changes
All checks were successful
ci / docker_image (push) Successful in 2m35s
ci / deploy (push) Successful in 31s

This commit is contained in:
Drake Marino 2025-03-20 21:30:54 -05:00
parent cd1fbeb3b0
commit 8932165f7b
18 changed files with 162 additions and 84 deletions

View File

@ -411,3 +411,13 @@ h2 {
.top-with-navbar { .top-with-navbar {
top: 72px; top: 72px;
} }
.footer {
position: relative;
bottom: 0;
/*width: 100%;*/
/*position: absolute;*/
/*bottom: 0;*/
/*width: 100%;*/
}

View File

@ -1,7 +1,7 @@
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import sql from '$lib/db/db.server'; import sql from '$lib/db/db.server';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import { saveAvatar, saveLogo } from '$lib/index.server'; import { deleteLogo, saveAvatar, saveLogo } from '$lib/index.server';
import { import {
EmploymentType, EmploymentType,
type User, type User,
@ -295,6 +295,8 @@ export async function editCompany(company: Company): Promise<number> {
RETURNING id; RETURNING id;
`; `;
await saveLogo(company);
return response[0].id; return response[0].id;
} }
@ -303,6 +305,8 @@ export async function deleteCompany(id: number): Promise<void> {
DELETE FROM companies DELETE FROM companies
WHERE id = ${id}; WHERE id = ${id};
`; `;
await deleteLogo(<Company>{ id: id });
} }
export async function getCompany(id: number): Promise<Company> { export async function getCompany(id: number): Promise<Company> {

View File

@ -15,13 +15,26 @@ export async function saveAvatar(user: User): Promise<void> {
} }
export async function saveLogo(company: Company): Promise<void> { export async function saveLogo(company: Company): Promise<void> {
const url = `https://ui-avatars.com/api/?background=random&format=svg&name=${encodeURIComponent(company.name!)}`; // const url = `https://ui-avatars.com/api/?background=random&format=svg&name=${encodeURIComponent(company.name!)}`;
const response = await fetch(url, { headers: { accept: 'image/svg+xml' } }); // const response = await fetch(url, { headers: { accept: 'image/svg+xml' } });
const avatar = await response.text(); // const avatar = await response.text();
const filePath = path.join('static', 'uploads', 'logos', `${company.id}.svg`); // const filePath = path.join('static', 'uploads', 'logos', `${company.id}.svg`);
const url = `https://img.logo.dev/${new URL(company.website!).hostname}`;
console.log(url);
const response = await fetch(url, { headers: { accept: 'image/jpeg' } });
const avatar = await response.buffer();
const filePath = path.join('static', 'uploads', 'logos', `${company.id}.jpg`);
fs.writeFileSync(filePath, avatar); fs.writeFileSync(filePath, avatar);
} }
export async function deleteLogo(company: Company): Promise<void> {
// const url = `https://ui-avatars.com/api/?background=random&format=svg&name=${encodeURIComponent(company.name!)}`;
// const response = await fetch(url, { headers: { accept: 'image/svg+xml' } });
// const avatar = await response.text();
const filePath = path.join('static', 'uploads', 'logos', `${company.id}.jpg`);
fs.rmSync(filePath);
}
// TODO: change to return null instead of -1 // TODO: change to return null instead of -1
export function getUserPerms(cookies: Cookies): number { export function getUserPerms(cookies: Cookies): number {
if (process.env.JWT_SECRET === undefined) { if (process.env.JWT_SECRET === undefined) {

View File

@ -39,6 +39,7 @@
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..40,400,0,0&display=block&icon_names=account_circle,arrow_drop_down,arrow_drop_up,calendar_today,call,check,close,dark_mode,delete,description,edit,group,info,light_mode,login,mail,person,search,sell,store,visibility,visibility_off,work" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..40,400,0,0&display=block&icon_names=account_circle,arrow_drop_down,arrow_drop_up,calendar_today,call,check,close,dark_mode,delete,description,edit,group,info,light_mode,login,mail,person,search,sell,store,visibility,visibility_off,work"
/> />
<div class="flex min-h-screen flex-col">
<div class="bottom-border bg-color sticky top-0 z-50 flex h-14 justify-between p-3 align-middle"> <div class="bottom-border bg-color sticky top-0 z-50 flex h-14 justify-between p-3 align-middle">
<nav class="pt-1"> <nav class="pt-1">
<a href="/" class="hover-bg-color mr-1 rounded-md px-2 pb-2 pt-1.5"> <a href="/" class="hover-bg-color mr-1 rounded-md px-2 pb-2 pt-1.5">
@ -49,6 +50,7 @@
height="24" height="24"
width="24" width="24"
/> />
<div class="inline-block text-sm">Home</div>
</a> </a>
<!-- <a href="/about" class="hover-bg-color mr-1 rounded px-3 py-2 text-sm">About</a>--> <!-- <a href="/about" class="hover-bg-color mr-1 rounded px-3 py-2 text-sm">About</a>-->
{#if (userState.perms & PERMISSIONS.VIEW) > 0} {#if (userState.perms & PERMISSIONS.VIEW) > 0}
@ -79,14 +81,62 @@
{currentTheme === 'light' ? 'light_mode' : 'dark_mode'} {currentTheme === 'light' ? 'light_mode' : 'dark_mode'}
</span> </span>
</button> </button>
<a href={userState.id !== null ? '/account' : '/signin'}> <a
<span class="material-symbols-outlined hover-bg-color rounded-full p-1"> class="hover-bg-color inline-block rounded p-1 align-top"
href={userState.id !== null ? '/account' : '/signin'}
>
<span class="material-symbols-outlined inline-block h-min align-middle">
{userState.id !== null ? 'account_circle' : 'login'} {userState.id !== null ? 'account_circle' : 'login'}
</span> </span>
<span class="inline-block h-min align-middle text-sm">
{userState.id !== null ? 'Account' : 'Sign-in'}
</span>
</a> </a>
</div> </div>
</div> </div>
<div> <div class="flex-grow">
{@render children()} {@render children()}
</div> </div>
<footer class="top-border bg-color footer flex h-min w-full justify-between p-2 text-center">
<div class="inline-block text-left align-top">
<div class="inline-block pr-3">
<p class="font-semibold">Drake Marino:</p>
<a href="mailto:drake@marinodev.com" class="hyperlink-color hyperlink-underline"
>drake@marinodev.com</a
>
</div>
<div class="inline-block">
<p class="font-semibold">Chetan Malkan:</p>
<a href="mailto:chetan@marinodev.com" class="hyperlink-color hyperlink-underline"
>chetan@marinodev.com</a
>
</div>
</div>
<div class="inline-block align-top font-semibold">
<div class="font-semibold">MarinoDev</div>
<div class="font-normal">2025</div>
</div>
<div class="inline-block text-right align-top">
<div class="font-semibold">Source Code:</div>
<a
href="https://git.marinodev.com/MarinoDev/FBLA25"
class="hyperlink-color hyperlink-underline">https://git.marinodev.com/MarinoDev/FBLA25</a
>
</div>
</footer>
</div>
<!--<h3 class="pt-16 font-semibold">Contact Information:</h3>-->
<!--<p>Drake Marino:</p>-->
<!--<a href="mailto:drake@marinodev.com" class="hyperlink-color hyperlink-underline"-->
<!-- >drake@marinodev.com</a-->
<!--&gt;-->
<!--<p>Chetan Malkan:</p>-->
<!--<a href="mailto:chetan@marinodev.com" class="hyperlink-color hyperlink-underline"-->
<!-- >chetan@marinodev.com</a-->
<!--&gt;-->
<!--<h3 class="pt-8 font-semibold">Source Code:</h3>-->
<!--<a href="https://git.marinodev.com/MarinoDev/FBLA25" class="hyperlink-color hyperlink-underline"-->
<!-- >https://git.marinodev.com/MarinoDev/FBLA25</a-->
<!--&gt;-->

View File

@ -10,24 +10,13 @@
<div class="base-container"> <div class="base-container">
<div class="content pt-16"> <div class="content pt-16">
<h1 class="text-center font-semibold">Welcome to CareerConnect!</h1> <img class="mx-auto" src="/mdevtriangle.svg" alt="MarinoDev Logo" height="256" width="256" />
<h1 class="text-center text-8xl font-semibold">CareerConnect</h1>
<h2 class="text-center text-3xl italic">Connecting Students with Opportunities</h2>
<h2 class="pt-8 text-center">We are a platform that connects students with employers.</h2> <h2 class="pt-8 text-center">We are a platform that connects students with employers.</h2>
<h2 class="text-center"> <h2 class="text-center">
We provide an accessible way for students to find internships and co-op opportunities, and for We provide an accessible way for students to find internships and co-op opportunities, and for
employers to find students to fill their positions. employers to find students to fill their positions.
</h2> </h2>
<h3 class="pt-16 font-semibold">Contact Information:</h3>
<p>Drake Marino:</p>
<a href="mailto:drake@marinodev.com" class="hyperlink-color hyperlink-underline"
>drake@marinodev.com</a
>
<p>Chetan Malkan:</p>
<a href="mailto:chetan@marinodev.com" class="hyperlink-color hyperlink-underline"
>chetan@marinodev.com</a
>
<h3 class="pt-8 font-semibold">Source Code:</h3>
<a href="https://git.marinodev.com/MarinoDev/FBLA25" class="hyperlink-color hyperlink-underline"
>https://git.marinodev.com/MarinoDev/FBLA25</a
>
</div> </div>
</div> </div>

View File

@ -123,7 +123,7 @@
<img <img
id="logo" id="logo"
class="mb-2 inline-block rounded" class="mb-2 inline-block rounded"
src="/uploads/logos/{data.user.company.id}.svg?timestamp=${Date.now()}" src="/uploads/logos/{data.user.company.id}.jpg?timestamp=${Date.now()}"
alt="Company Logo" alt="Company Logo"
onerror={logoFallback} onerror={logoFallback}
height="32" height="32"

View File

@ -57,7 +57,11 @@
<tr> <tr>
<td class="left">{company.id}</td> <td class="left">{company.id}</td>
<td>{company.name}</td> <td>{company.name}</td>
<td>{company.website}</td> <td
><a href={company.website} class="hyperlink-underline hyperlink-color"
>{company.website}</a
></td
>
<td <td
>{company.createdAt?.toLocaleDateString('en-US', dateFormatOptions) || >{company.createdAt?.toLocaleDateString('en-US', dateFormatOptions) ||
'unknown'}</td 'unknown'}</td

View File

@ -5,7 +5,7 @@ import { error } from '@sveltejs/kit';
import { getUserPerms } from '$lib/index.server'; import { getUserPerms } from '$lib/index.server';
export const load: PageServerLoad = async ({ cookies, url }) => { export const load: PageServerLoad = async ({ cookies, url }) => {
const search = url.searchParams.get('searchUsers'); const search = url.searchParams.get('searchPostings');
const perms = getUserPerms(cookies); const perms = getUserPerms(cookies);
if (perms >= 0 && (perms & PERMISSIONS.MANAGE_POSTINGS) > 0) { if (perms >= 0 && (perms & PERMISSIONS.MANAGE_POSTINGS) > 0) {
return { return {

View File

@ -25,9 +25,9 @@
<div class="search-bar"> <div class="search-bar">
<input <input
type="search" type="search"
name="searchUsers" name="searchPostings"
id="searchUsers" id="searchPostings"
placeholder="Search Users" placeholder="Search Postings"
class="search-cancel" class="search-cancel"
/> />
<button><span class="material-symbols-outlined">search</span></button> <button><span class="material-symbols-outlined">search</span></button>

View File

@ -94,7 +94,7 @@
<div class="top-border mt-2 p-3"> <div class="top-border mt-2 p-3">
<img <img
class="mb-2 inline-block rounded-lg" class="mb-2 inline-block rounded-lg"
src="/uploads/logos/{data.user.company.id}.svg?timestamp=${Date.now()}" src="/uploads/logos/{data.user.company.id}.jpg?timestamp=${Date.now()}"
alt="Company Logo" alt="Company Logo"
onerror={logoFallback} onerror={logoFallback}
height="32" height="32"

View File

@ -1,9 +1,17 @@
import { type Actions, fail, redirect } from '@sveltejs/kit'; import { type Actions, error, fail, redirect } from '@sveltejs/kit';
import { createUser } from '$lib/db/index.server'; import { createUser, getUsers } from '$lib/db/index.server';
import { PERMISSIONS } from '$lib/consts'; import { PERMISSIONS } from '$lib/consts';
import { getUserPerms } from '$lib/index.server'; import { getUserPerms } from '$lib/index.server';
import type { User } from '$lib/types'; import type { User } from '$lib/types';
import { employerPerms, userPerms } from '$lib/shared.svelte'; import { employerPerms, userPerms } from '$lib/shared.svelte';
import type { PageServerLoad } from '../../../../../.svelte-kit/types/src/routes/admin/users/$types';
export const load: PageServerLoad = async ({ cookies, url }) => {
const perms = getUserPerms(cookies);
if (!(perms >= 0 && (perms & PERMISSIONS.MANAGE_USERS) > 0)) {
error(403, 'Unauthorized');
}
};
export const actions: Actions = { export const actions: Actions = {
submit: async ({ request, cookies }) => { submit: async ({ request, cookies }) => {

View File

@ -28,7 +28,7 @@
<a class="top-border hover-bg-color inline-block w-full p-3" href="/companies/{company.id}"> <a class="top-border hover-bg-color inline-block w-full p-3" href="/companies/{company.id}">
<img <img
class="mb-2 inline-block rounded-lg" class="mb-2 inline-block rounded-lg"
src="/uploads/logos/{company.id}.svg?timestamp=${Date.now()}" src="/uploads/logos/{company.id}.jpg?timestamp=${Date.now()}"
alt="Company logo" alt="Company logo"
onerror={(e) => logoFallback(e, company)} onerror={(e) => logoFallback(e, company)}
height="64" height="64"

View File

@ -24,7 +24,7 @@
<div class="inline-block"> <div class="inline-block">
<img <img
class="mb-2 inline-block rounded-lg" class="mb-2 inline-block rounded-lg"
src="/uploads/logos/{data.company.id}.svg?timestamp=${Date.now()}" src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
alt="User avatar" alt="User avatar"
onerror={logoFallback} onerror={logoFallback}
height="120" height="120"
@ -55,7 +55,7 @@
<a class="top-border hover-bg-color block p-2" href="/postings/{posting.id}"> <a class="top-border hover-bg-color block p-2" href="/postings/{posting.id}">
<img <img
class="inline-block rounded" class="inline-block rounded"
src="/uploads/logos/{posting.companyId ? posting.companyId : 'default'}.svg" src="/uploads/logos/{posting.companyId ? posting.companyId : 'default'}.jpg"
alt="Company Logo" alt="Company Logo"
height="48" height="48"
width="48" width="48"

View File

@ -23,7 +23,7 @@
<div class="m-4"> <div class="m-4">
<img <img
class="mb-2 inline-block rounded-lg" class="mb-2 inline-block rounded-lg"
src="/uploads/logos/{data.company.id}.svg?timestamp=${Date.now()}" src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
alt="User avatar" alt="User avatar"
onerror={logoFallback} onerror={logoFallback}
height="80" height="80"

View File

@ -21,7 +21,7 @@
<div class="m-4"> <div class="m-4">
<img <img
class="mb-2 inline-block rounded-lg" class="mb-2 inline-block rounded-lg"
src="/uploads/logos/{data.company.id}.svg?timestamp=${Date.now()}" src="/uploads/logos/{data.company.id}.jpg?timestamp=${Date.now()}"
alt="User avatar" alt="User avatar"
onerror={logoFallback} onerror={logoFallback}
height="80" height="80"

View File

@ -58,7 +58,7 @@
> >
<img <img
class="inline-block rounded" class="inline-block rounded"
src="/uploads/logos/{posting.companyId}.svg" src="/uploads/logos/{posting.companyId}.png"
alt="Company Logo" alt="Company Logo"
height="48" height="48"
width="48" width="48"
@ -73,13 +73,13 @@
</div> </div>
{#if details !== undefined} {#if details !== undefined}
<div <div
class="elevated separator-borders top-with-navbar sticky ml-4 inline-block h-min w-2/3 rounded p-4" class="elevated separator-borders top-with-navbar sticky mb-4 ml-4 inline-block h-min w-2/3 rounded p-4"
> >
<div class="bottom-border flex justify-between pb-2"> <div class="bottom-border flex justify-between pb-2">
<div class="inline-block"> <div class="inline-block">
<img <img
class="inline-block rounded" class="inline-block rounded"
src="/uploads/logos/{details.companyId || 'default'}.svg" src="/uploads/logos/{details.companyId || 'default'}.jpg"
alt="Company Logo" alt="Company Logo"
height="64" height="64"
width="64" width="64"

View File

@ -25,7 +25,7 @@
<div class="inline-block"> <div class="inline-block">
<img <img
class="inline-block rounded" class="inline-block rounded"
src="/uploads/logos/{data.posting.companyId || 'default'}.svg" src="/uploads/logos/{data.posting.companyId || 'default'}.jpg"
alt="Company Logo" alt="Company Logo"
height="64" height="64"
width="64" width="64"

View File

@ -25,7 +25,7 @@
<div class="inline-block"> <div class="inline-block">
<img <img
class="inline-block rounded" class="inline-block rounded"
src="/uploads/logos/{data.posting?.companyId || 'default'}.svg" src="/uploads/logos/{data.posting?.companyId || 'default'}.jpg"
alt="Company Logo" alt="Company Logo"
height="64" height="64"
width="64" width="64"