From b2ab827a55fba7ebc1a716d3d8e6a3634f5cf54a Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sun, 27 Oct 2024 15:21:31 +1100 Subject: [PATCH] add proper carousel to store page uses the VueCarousel library to add an actual carousel to the store page for the images. uses responsive styles --- middleware/require-user.global.ts | 2 +- nuxt.config.ts | 12 +- pages/store/[id]/index.vue | 175 ++++++++++-------- .../migration.sql | 9 + prisma/schema.prisma | 8 + server/api/v1/auth/signup/simple.post.ts | 65 ++++++- server/plugins/setup.ts | 25 +++ yarn.lock | 43 +++-- 8 files changed, 229 insertions(+), 110 deletions(-) create mode 100644 prisma/migrations/20241025091103_add_invitations/migration.sql create mode 100644 server/plugins/setup.ts diff --git a/middleware/require-user.global.ts b/middleware/require-user.global.ts index 61286be..8ec3fa3 100644 --- a/middleware/require-user.global.ts +++ b/middleware/require-user.global.ts @@ -1,4 +1,4 @@ -const whitelistedPrefixes = ["/signin", "/register", "/api"]; +const whitelistedPrefixes = ["/signin", "/register", "/api", "/setup"]; const requireAdmin = ["/admin"]; export default defineNuxtRouteMiddleware(async (to, from) => { diff --git a/nuxt.config.ts b/nuxt.config.ts index f66cab8..fdf8559 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -26,14 +26,12 @@ export default defineNuxtConfig({ }, }, - watchers: { - chokidar: { - ignored: ".data", - }, - }, - // Module config from here down - modules: ["@nuxt/content"], + modules: ["@nuxt/content", "vue3-carousel-nuxt"], + + carousel: { + prefix: "Vue", + }, content: { api: { diff --git a/pages/store/[id]/index.vue b/pages/store/[id]/index.vue index 3506513..d527f7d 100644 --- a/pages/store/[id]/index.vue +++ b/pages/store/[id]/index.vue @@ -1,79 +1,94 @@ + + diff --git a/prisma/migrations/20241025091103_add_invitations/migration.sql b/prisma/migrations/20241025091103_add_invitations/migration.sql new file mode 100644 index 0000000..1b0b37f --- /dev/null +++ b/prisma/migrations/20241025091103_add_invitations/migration.sql @@ -0,0 +1,9 @@ +-- CreateTable +CREATE TABLE "Invitation" ( + "id" TEXT NOT NULL, + "isAdmin" BOOLEAN NOT NULL DEFAULT false, + "username" TEXT, + "email" TEXT, + + CONSTRAINT "Invitation_pkey" PRIMARY KEY ("id") +); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index fefe74b..9d23408 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -41,6 +41,14 @@ model LinkedAuthMec { @@id([userId, mec]) } +model Invitation { + id String @id @default(uuid()) + isAdmin Boolean @default(false) + + username String? + email String? +} + enum ClientCapabilities { DownloadAggregation } diff --git a/server/api/v1/auth/signup/simple.post.ts b/server/api/v1/auth/signup/simple.post.ts index ecc51fc..e084696 100644 --- a/server/api/v1/auth/signup/simple.post.ts +++ b/server/api/v1/auth/signup/simple.post.ts @@ -1,18 +1,69 @@ -import { AuthMec } from "@prisma/client"; +import { AuthMec, Invitation } from "@prisma/client"; import { Readable } from "stream"; import prisma from "~/server/internal/db/database"; import { createHash } from "~/server/internal/security/simple"; import { v4 as uuidv4 } from "uuid"; +import { KeyOfType } from "~/server/internal/utils/types"; + +// Only really a simple test, in case people mistype their emails +const mailRegex = /^\S+@\S+\.\S+$/g; export default defineEventHandler(async (h3) => { const body = await readBody(h3); - const username = body.username; - const password = body.password; - if (username === undefined || password === undefined) + const invitationId = body.invitation; + if (!invitationId) throw createError({ - statusCode: 403, - statusMessage: "Username or password missing from request.", + statusCode: 401, + statusMessage: "Invalid or expired invitation.", + }); + + const invitation = await prisma.invitation.findUnique({ + where: { id: invitationId }, + }); + if (!invitation) + throw createError({ + statusCode: 401, + statusMessage: "Invalid or expired invitation.", + }); + + const useInvitationOrBodyRequirement = ( + field: keyof Invitation, + check: (v: string) => boolean + ) => { + if (invitation[field]) { + return invitation[field].toString(); + } + + const v: string = body[field]?.toString(); + const valid = check(v); + return valid ? v : undefined; + }; + + const username = useInvitationOrBodyRequirement( + "username", + (e) => e.length > 5 + ); + const email = useInvitationOrBodyRequirement("email", (e) => + mailRegex.test(e) + ); + const password = body.password; + if (username === undefined) + throw createError({ + statusCode: 400, + statusMessage: "Username is invalid. Must be more than 5 characters.", + }); + + if (email === undefined) + throw createError({ + statusCode: 400, + statusMessage: "Invalid email. Must follow the format you@example.com", + }); + + if (!password) + throw createError({ + statusCode: 400, + statusMessage: "Password empty or missing.", }); const existing = await prisma.user.count({ where: { username: username } }); @@ -32,7 +83,7 @@ export default defineEventHandler(async (h3) => { responseType: "stream", }), {}, - [`anonymous:read`, `${userId}:write`], + [`anonymous:read`, `${userId}:write`] ); const user = await prisma.user.create({ data: { diff --git a/server/plugins/setup.ts b/server/plugins/setup.ts new file mode 100644 index 0000000..9aa5264 --- /dev/null +++ b/server/plugins/setup.ts @@ -0,0 +1,25 @@ +import prisma from "../internal/db/database"; + +export default defineNitroPlugin(async (nitro) => { + const userCount = await prisma.user.count({}); + if (userCount != 0) return; + + // This setup runs every time the server sets up, + // so it should be in-place + + // Create admin invitation + await prisma.invitation.upsert({ + where: { + id: "admin", + }, + create: { + id: "admin", + isAdmin: true, + }, + update: { + isAdmin: true, + }, + }); + + +}); diff --git a/yarn.lock b/yarn.lock index d83d03f..3782489 100644 --- a/yarn.lock +++ b/yarn.lock @@ -296,23 +296,23 @@ dependencies: mime "^3.0.0" -"@drop/droplet-linux-x64-gnu@0.5.1", "@drop/droplet-linux-x64-gnu@^0.5.1": - version "0.5.1" - resolved "https://lab.deepcore.dev/api/v4/projects/57/packages/npm/@drop/droplet-linux-x64-gnu/-/@drop/droplet-linux-x64-gnu-0.5.1.tgz#3313f2ab18113efe15c5e7fc1c0b04f9006ebfbb" - integrity sha1-MxPyqxgRPv4Vxef8HAsE+QBuv7s= +"@drop/droplet-linux-x64-gnu@^0.7.0": + version "0.7.0" + resolved "https://lab.deepcore.dev/api/v4/projects/57/packages/npm/@drop/droplet-linux-x64-gnu/-/@drop/droplet-linux-x64-gnu-0.7.0.tgz#128e37707481cfcbbeb057142164f3e637f13f26" + integrity sha1-Eo43cHSBz8u+sFcUIWTz5jfxPyY= -"@drop/droplet-win32-x64-msvc@0.5.1", "@drop/droplet-win32-x64-msvc@^0.5.1": - version "0.5.1" - resolved "https://lab.deepcore.dev/api/v4/projects/57/packages/npm/@drop/droplet-win32-x64-msvc/-/@drop/droplet-win32-x64-msvc-0.5.1.tgz#789e208884716971df428ebd43e42fc595edd634" - integrity sha1-eJ4giIRxaXHfQo69Q+QvxZXt1jQ= +"@drop/droplet-win32-x64-msvc@^0.7.0": + version "0.7.0" + resolved "https://lab.deepcore.dev/api/v4/projects/57/packages/npm/@drop/droplet-win32-x64-msvc/-/@drop/droplet-win32-x64-msvc-0.7.0.tgz#db41136165ca74819b359db5d4e9c1ab2c4188c0" + integrity sha1-20ETYWXKdIGbNZ211OnBqyxBiMA= -"@drop/droplet@^0.5.1": - version "0.5.1" - resolved "https://lab.deepcore.dev/api/v4/projects/57/packages/npm/@drop/droplet/-/@drop/droplet-0.5.1.tgz#646158e06712e7d132050f7deb37b866edc9121a" - integrity sha1-ZGFY4GcS59EyBQ996ze4Zu3JEho= +"@drop/droplet@^0.7.0": + version "0.7.0" + resolved "https://lab.deepcore.dev/api/v4/projects/57/packages/npm/@drop/droplet/-/@drop/droplet-0.7.0.tgz#3728951758b899cc242a40aec2b7f326f11c3714" + integrity sha1-NyiVF1i4mcwkKkCuwrfzJvEcNxQ= optionalDependencies: - "@drop/droplet-linux-x64-gnu" "0.5.1" - "@drop/droplet-win32-x64-msvc" "0.5.1" + "@drop/droplet-linux-x64-gnu" "0.7.0" + "@drop/droplet-win32-x64-msvc" "0.7.0" "@esbuild/aix-ppc64@0.20.2": version "0.20.2" @@ -917,7 +917,7 @@ which "^3.0.1" ws "^8.18.0" -"@nuxt/kit@3.13.2", "@nuxt/kit@^3.13.1", "@nuxt/kit@^3.13.2": +"@nuxt/kit@3.13.2", "@nuxt/kit@^3.12.4", "@nuxt/kit@^3.13.1", "@nuxt/kit@^3.13.2": version "3.13.2" resolved "https://registry.yarnpkg.com/@nuxt/kit/-/kit-3.13.2.tgz#4c019a87e08c33ec14d1059497ba40568b82bfed" integrity sha512-KvRw21zU//wdz25IeE1E5m/aFSzhJloBRAQtv+evcFeZvuroIxpIQuUqhbzuwznaUwpiWbmwlcsp5uOWmi4vwA== @@ -6876,6 +6876,19 @@ vue-router@^4.4.5, vue-router@latest: dependencies: "@vue/devtools-api" "^6.6.4" +vue3-carousel-nuxt@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/vue3-carousel-nuxt/-/vue3-carousel-nuxt-1.1.3.tgz#f63e0ccfc398c42b9d5c8098fb7da9f611749751" + integrity sha512-VssmTpUn3PdTEs2/BrU6eDj/kBV3+Q44fcKY3+9a5Mqjmv2zkV20E9gPiA4MO4qZ58AJetQAxrpJ5nJ67HA52w== + dependencies: + "@nuxt/kit" "^3.12.4" + vue3-carousel "^0.3.4" + +vue3-carousel@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/vue3-carousel/-/vue3-carousel-0.3.4.tgz#8ef6d6b592385b7f8e97fcd508a3f4db29a2391e" + integrity sha512-jImUDbQa/9pELxUQdkflUPXL94V+iQZaOPUxWDBKSffCuxhYcV3sDM40pxoiYxUxXoNCDLUF4u9Ug6Xjdt4nkA== + vue@^3.5.5, vue@latest: version "3.5.12" resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.12.tgz#e08421c601b3617ea2c9ef0413afcc747130b36c"