From 6385f9f46b5ae515708d29696426244a2fd6e0e0 Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Wed, 12 Jun 2024 17:38:34 +0000 Subject: [PATCH] random login image! --- migrations/0001_fuzzy_slipstream.sql | 12 + migrations/meta/0001_snapshot.json | 537 ++++++++++++++++++++++ migrations/meta/_journal.json | 7 + src/lib/db/getBackgroundImages.ts | 20 + src/lib/db/schema.ts | 7 + src/routes/api/upload/+server.ts | 13 +- src/routes/login/+page.server.ts | 3 +- src/routes/login/+page.svelte | 7 +- src/routes/settings/+page.server.ts | 9 - src/routes/settings/admin/+page.server.ts | 33 +- src/routes/settings/admin/+page.svelte | 18 + 11 files changed, 646 insertions(+), 20 deletions(-) create mode 100644 migrations/0001_fuzzy_slipstream.sql create mode 100644 migrations/meta/0001_snapshot.json create mode 100644 src/lib/db/getBackgroundImages.ts diff --git a/migrations/0001_fuzzy_slipstream.sql b/migrations/0001_fuzzy_slipstream.sql new file mode 100644 index 0000000..cbcd66d --- /dev/null +++ b/migrations/0001_fuzzy_slipstream.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS "images" ( + "id" serial PRIMARY KEY NOT NULL, + "url" text NOT NULL, + "type" text NOT NULL, + "adventureId" integer +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "images" ADD CONSTRAINT "images_adventureId_adventures_id_fk" FOREIGN KEY ("adventureId") REFERENCES "adventures"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/migrations/meta/0001_snapshot.json b/migrations/meta/0001_snapshot.json new file mode 100644 index 0000000..d607317 --- /dev/null +++ b/migrations/meta/0001_snapshot.json @@ -0,0 +1,537 @@ +{ + "id": "c1f4493f-cbac-4747-93e4-170b585bc620", + "prevId": "7d9fd5eb-2eed-4595-a3db-6f01cde31041", + "version": "5", + "dialect": "pg", + "tables": { + "adventures": { + "name": "adventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "activityTypes": { + "name": "activityTypes", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "ARRAY[]::text[]" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rating": { + "name": "rating", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "link": { + "name": "link", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "imageUrl": { + "name": "imageUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tripId": { + "name": "tripId", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "adventures_userId_user_id_fk": { + "name": "adventures_userId_user_id_fk", + "tableFrom": "adventures", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "adventures_tripId_userPlannedTrips_id_fk": { + "name": "adventures_tripId_userPlannedTrips_id_fk", + "tableFrom": "adventures", + "tableTo": "userPlannedTrips", + "columnsFrom": [ + "tripId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "images": { + "name": "images", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adventureId": { + "name": "adventureId", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "images_adventureId_adventures_id_fk": { + "name": "images_adventureId_adventures_id_fk", + "tableFrom": "images", + "tableTo": "adventures", + "columnsFrom": [ + "adventureId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "sharedAdventures": { + "name": "sharedAdventures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "userPlannedTrips": { + "name": "userPlannedTrips", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adventureName": { + "name": "adventureName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "startDate": { + "name": "startDate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "endDate": { + "name": "endDate", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "userPlannedTrips_userId_user_id_fk": { + "name": "userPlannedTrips_userId_user_id_fk", + "tableFrom": "userPlannedTrips", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hashed_password": { + "name": "hashed_password", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "signup_date": { + "name": "signup_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_login": { + "name": "last_login", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "userVisitedWorldTravel": { + "name": "userVisitedWorldTravel", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region_id": { + "name": "region_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "userVisitedWorldTravel_country_code_worldTravelCountries_country_code_fk": { + "name": "userVisitedWorldTravel_country_code_worldTravelCountries_country_code_fk", + "tableFrom": "userVisitedWorldTravel", + "tableTo": "worldTravelCountries", + "columnsFrom": [ + "country_code" + ], + "columnsTo": [ + "country_code" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "userVisitedWorldTravel_user_id_user_id_fk": { + "name": "userVisitedWorldTravel_user_id_user_id_fk", + "tableFrom": "userVisitedWorldTravel", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "userVisitedWorldTravel_region_id_worldTravelCountryRegions_id_fk": { + "name": "userVisitedWorldTravel_region_id_worldTravelCountryRegions_id_fk", + "tableFrom": "userVisitedWorldTravel", + "tableTo": "worldTravelCountryRegions", + "columnsFrom": [ + "region_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "worldTravelCountries": { + "name": "worldTravelCountries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "continent": { + "name": "continent", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "worldTravelCountries_country_code_unique": { + "name": "worldTravelCountries_country_code_unique", + "nullsNotDistinct": false, + "columns": [ + "country_code" + ] + } + } + }, + "worldTravelCountryRegions": { + "name": "worldTravelCountryRegions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "info": { + "name": "info", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "worldTravelCountryRegions_country_code_worldTravelCountries_country_code_fk": { + "name": "worldTravelCountryRegions_country_code_worldTravelCountries_country_code_fk", + "tableFrom": "worldTravelCountryRegions", + "tableTo": "worldTravelCountries", + "columnsFrom": [ + "country_code" + ], + "columnsTo": [ + "country_code" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 63abc7e..a77e995 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1717264326698, "tag": "0000_whole_blob", "breakpoints": true + }, + { + "idx": 1, + "version": "5", + "when": 1718212521664, + "tag": "0001_fuzzy_slipstream", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/lib/db/getBackgroundImages.ts b/src/lib/db/getBackgroundImages.ts new file mode 100644 index 0000000..f4b2124 --- /dev/null +++ b/src/lib/db/getBackgroundImages.ts @@ -0,0 +1,20 @@ +import { eq } from "drizzle-orm"; +import { db } from "./db.server"; +import { imagesTable } from "./schema"; + +export const getBackgroundImages = async (numberOfResults: number | null) => { + const images = await db + .select({ url: imagesTable.url }) + .from(imagesTable) + .where(eq(imagesTable.type, "background")) + .execute(); + + if (numberOfResults) { + let randomIndex = Math.floor(Math.random() * images.length); + return images.slice(randomIndex, randomIndex + numberOfResults) as { + url: string; + }[]; + } + + return images as { url: string }[]; +}; diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index 67980b6..7423287 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -103,3 +103,10 @@ export const adventureTable = pgTable("adventures", { date: text("date"), tripId: integer("tripId").references(() => userPlannedTrips.id), }); + +export const imagesTable = pgTable("images", { + id: serial("id").primaryKey(), + url: text("url").notNull(), + type: text("type").notNull(), + adventureId: integer("adventureId").references(() => adventureTable.id), +}); diff --git a/src/routes/api/upload/+server.ts b/src/routes/api/upload/+server.ts index 873488e..e72b44e 100644 --- a/src/routes/api/upload/+server.ts +++ b/src/routes/api/upload/+server.ts @@ -1,5 +1,7 @@ // src/routes/api/upload.js +import { db } from "$lib/db/db.server"; +import { imagesTable } from "$lib/db/schema"; import { deleteObject, ensureBucketExists, uploadObject } from "$lib/server/s3"; import type { RequestEvent } from "@sveltejs/kit"; import { generateId } from "lucia"; @@ -10,6 +12,7 @@ export async function POST(event: RequestEvent): Promise { const fileExtension = contentType.split("/").pop(); const fileName = `${generateId(75)}.${fileExtension}`; const bucket = event.request.headers.get("bucket") as string; + const type = event.request.headers.get("type") as string | null; if (!fileExtension || !fileName) { return new Response(JSON.stringify({ error: "Invalid file type" }), { @@ -32,7 +35,6 @@ export async function POST(event: RequestEvent): Promise { ); } - // check if the file is an image if (!contentType.startsWith("image")) { return new Response(JSON.stringify({ error: "Invalid file type" }), { status: 400, @@ -49,7 +51,7 @@ export async function POST(event: RequestEvent): Promise { await ensureBucketExists(bucket); - if (event.locals.user?.icon) { + if (event.locals.user?.icon && bucket === "profile-pics") { const key: string = event.locals.user.icon.split("/").pop() as string; await deleteObject(bucket, key); } @@ -60,6 +62,13 @@ export async function POST(event: RequestEvent): Promise { Buffer.from(fileBuffer) ); + if (bucket === "images" && type && type === "background") { + let res = await db.insert(imagesTable).values({ + url: objectUrl, + type: "background", + }); + } + console.log(`File uploaded to ${objectUrl}`); return new Response(JSON.stringify({ objectUrl }), { diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts index dd2036a..2e9779a 100644 --- a/src/routes/login/+page.server.ts +++ b/src/routes/login/+page.server.ts @@ -7,12 +7,13 @@ import type { Actions, PageServerLoad } from "./$types"; import type { DatabaseUser } from "$lib/server/auth"; import { userTable } from "$lib/db/schema"; import { eq } from "drizzle-orm"; +import { getBackgroundImages } from "$lib/db/getBackgroundImages"; export const load: PageServerLoad = async (event) => { if (event.locals.user) { return redirect(302, "/"); } - return {}; + return { images: await getBackgroundImages(1) }; }; export const actions: Actions = { diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index 04e851e..cc5e2db 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -4,6 +4,11 @@ import { getRandomQuote } from "$lib"; import { redirect, type SubmitFunction } from "@sveltejs/kit"; import { onMount } from "svelte"; + + export let data; + + console.log(data); + let quote: string = ""; let errors: { message?: string } = {}; let backgroundImageUrl = "https://source.unsplash.com/random/?mountains"; @@ -35,7 +40,7 @@
diff --git a/src/routes/settings/+page.server.ts b/src/routes/settings/+page.server.ts index 03d8680..2d5d263 100644 --- a/src/routes/settings/+page.server.ts +++ b/src/routes/settings/+page.server.ts @@ -42,15 +42,6 @@ export const actions: Actions = { }; } - // if (icon.length > 1) { - // return { - // status: 400, - // body: { - // message: "Icon must be a single character", - // }, - // }; - // } - const usernameTaken = await db .select() .from(userTable) diff --git a/src/routes/settings/admin/+page.server.ts b/src/routes/settings/admin/+page.server.ts index 3518fd1..c73cb99 100644 --- a/src/routes/settings/admin/+page.server.ts +++ b/src/routes/settings/admin/+page.server.ts @@ -1,10 +1,4 @@ -import { - error, - fail, - redirect, - type Actions, - type Handle, -} from "@sveltejs/kit"; +import { error, fail, redirect, type Actions } from "@sveltejs/kit"; import type { PageServerLoad } from "./$types"; import { db } from "$lib/db/db.server"; import { @@ -201,4 +195,29 @@ export const actions: Actions = { return { success: true }; }, + background: async (event) => { + console.log("background"); + const formData = await event.request.formData(); + const background = formData.get("background") as File | null; + if (!background) { + return fail(400, { message: "No background provided" }); + } + if (!event.locals.user) { + return redirect(302, "/"); + } + let res = await event.fetch("/api/upload", { + method: "POST", + body: background, + headers: { + bucket: "images", + type: "background", + }, + }); + await res.json(); + console.log("Background uploaded"); + if (!res.ok) { + return fail(500, { message: "Failed to upload background" }); + } + return { success: true }; + }, }; diff --git a/src/routes/settings/admin/+page.svelte b/src/routes/settings/admin/+page.svelte index 3cd22f3..2c689bb 100644 --- a/src/routes/settings/admin/+page.svelte +++ b/src/routes/settings/admin/+page.svelte @@ -119,6 +119,24 @@ /> {/if} +

Background Images

+
+ + + +
+

Admin Stats (All Users)