From 6d3cdb560db92f7574cb473882eb9e87d84a3cbd Mon Sep 17 00:00:00 2001 From: Liviu Burcusel Date: Wed, 7 Jan 2026 11:11:35 +0100 Subject: [PATCH] [Closes #8] Added authentication --- .forgejo/workflows/production-build.yml | 7 + .forgejo/workflows/production-pr.yml | 6 +- .husky/pre-commit | 1 + app/components/LoginForm.vue | 65 + app/components/SignupForm.vue | 81 + app/components/ui/card/Card.vue | 17 + app/components/ui/card/CardAction.vue | 14 + app/components/ui/card/CardContent.vue | 14 + app/components/ui/card/CardDescription.vue | 14 + app/components/ui/card/CardFooter.vue | 14 + app/components/ui/card/CardHeader.vue | 22 + app/components/ui/card/CardTitle.vue | 14 + app/components/ui/card/index.ts | 7 + app/components/ui/field/Field.vue | 17 + app/components/ui/field/FieldContent.vue | 14 + app/components/ui/field/FieldDescription.vue | 24 + app/components/ui/field/FieldError.vue | 50 + app/components/ui/field/FieldGroup.vue | 22 + app/components/ui/field/FieldLabel.vue | 25 + app/components/ui/field/FieldLegend.vue | 19 + app/components/ui/field/FieldSeparator.vue | 26 + app/components/ui/field/FieldSet.vue | 19 + app/components/ui/field/FieldTitle.vue | 22 + app/components/ui/field/index.ts | 36 + app/components/ui/label/Label.vue | 26 + app/components/ui/label/index.ts | 1 + app/components/ui/sonner/Sonner.vue | 42 + app/components/ui/sonner/index.ts | 1 + app/layouts/Default.vue | 18 +- app/layouts/default/Sidebar.vue | 104 +- app/layouts/default/SidebarFooter.vue | 149 + app/pages/member/auth/create-account.vue | 11 + app/pages/member/auth/login.vue | 11 + app/pages/member/auth/logout.vue | 35 + app/stores/auth.ts | 45 + drizzle.config.ts | 13 + eslint.config.mjs | 5 + nuxt.config.ts | 12 +- package-lock.json | 2709 +++++++++++++++-- package.json | 24 +- public/images/human.png | Bin 0 -> 3406 bytes server/api/[...auth].ts | 5 + shared/utils/auth-client.ts | 3 + shared/utils/auth.ts | 18 + shared/utils/db/index.ts | 15 + .../migrations/0000_create_initial_tables.sql | 62 + .../db/migrations/meta/0000_snapshot.json | 435 +++ shared/utils/db/migrations/meta/_journal.json | 13 + shared/utils/db/schema/auth.ts | 106 + shared/utils/db/schema/index.ts | 2 + shared/utils/db/schema/memberData.ts | 21 + shared/utils/env.ts | 13 + sonar-project.properties | 6 +- tests/layouts/Default.test.ts | 75 +- tests/layouts/default/Sidebar.test.ts | 120 +- tests/layouts/default/SidebarFooter.test.ts | 336 ++ .../pages/member/auth/create-account.test.ts | 88 + tests/pages/member/auth/login.test.ts | 143 + tests/pages/member/auth/logout.test.ts | 91 + tests/server/api/[...auth].test.ts | 366 +++ tests/setup.ts | 11 +- tests/shared/utils/auth.test.ts | 133 + tests/stores/auth.test.ts | 446 +++ tsconfig.test.json | 4 +- vitest.config.ts | 6 + 65 files changed, 5834 insertions(+), 440 deletions(-) create mode 100644 .husky/pre-commit create mode 100644 app/components/LoginForm.vue create mode 100644 app/components/SignupForm.vue create mode 100644 app/components/ui/card/Card.vue create mode 100644 app/components/ui/card/CardAction.vue create mode 100644 app/components/ui/card/CardContent.vue create mode 100644 app/components/ui/card/CardDescription.vue create mode 100644 app/components/ui/card/CardFooter.vue create mode 100644 app/components/ui/card/CardHeader.vue create mode 100644 app/components/ui/card/CardTitle.vue create mode 100644 app/components/ui/card/index.ts create mode 100644 app/components/ui/field/Field.vue create mode 100644 app/components/ui/field/FieldContent.vue create mode 100644 app/components/ui/field/FieldDescription.vue create mode 100644 app/components/ui/field/FieldError.vue create mode 100644 app/components/ui/field/FieldGroup.vue create mode 100644 app/components/ui/field/FieldLabel.vue create mode 100644 app/components/ui/field/FieldLegend.vue create mode 100644 app/components/ui/field/FieldSeparator.vue create mode 100644 app/components/ui/field/FieldSet.vue create mode 100644 app/components/ui/field/FieldTitle.vue create mode 100644 app/components/ui/field/index.ts create mode 100644 app/components/ui/label/Label.vue create mode 100644 app/components/ui/label/index.ts create mode 100644 app/components/ui/sonner/Sonner.vue create mode 100644 app/components/ui/sonner/index.ts create mode 100644 app/layouts/default/SidebarFooter.vue create mode 100644 app/pages/member/auth/create-account.vue create mode 100644 app/pages/member/auth/login.vue create mode 100644 app/pages/member/auth/logout.vue create mode 100644 app/stores/auth.ts create mode 100644 drizzle.config.ts create mode 100644 public/images/human.png create mode 100644 server/api/[...auth].ts create mode 100644 shared/utils/auth-client.ts create mode 100644 shared/utils/auth.ts create mode 100644 shared/utils/db/index.ts create mode 100644 shared/utils/db/migrations/0000_create_initial_tables.sql create mode 100644 shared/utils/db/migrations/meta/0000_snapshot.json create mode 100644 shared/utils/db/migrations/meta/_journal.json create mode 100644 shared/utils/db/schema/auth.ts create mode 100644 shared/utils/db/schema/index.ts create mode 100644 shared/utils/db/schema/memberData.ts create mode 100644 shared/utils/env.ts create mode 100644 tests/layouts/default/SidebarFooter.test.ts create mode 100644 tests/pages/member/auth/create-account.test.ts create mode 100644 tests/pages/member/auth/login.test.ts create mode 100644 tests/pages/member/auth/logout.test.ts create mode 100644 tests/server/api/[...auth].test.ts create mode 100644 tests/shared/utils/auth.test.ts create mode 100644 tests/stores/auth.test.ts diff --git a/.forgejo/workflows/production-build.yml b/.forgejo/workflows/production-build.yml index 0c4e758..9b4e65a 100644 --- a/.forgejo/workflows/production-build.yml +++ b/.forgejo/workflows/production-build.yml @@ -25,6 +25,10 @@ jobs: fetch-depth: 0 - name: Install dependencies run: npm ci + env: + DATABASE_URL: "N/A" + BETTER_AUTH_SECRET: "N/A" + BETTER_AUTH_URL: "N/A" - name: Run tests and generate coverage run: npm run coverage # continue-on-error: true @@ -38,6 +42,9 @@ jobs: - name: Build site env: NITRO_PRESET: node_cluster + DATABASE_URL: "N/A" + BETTER_AUTH_SECRET: "N/A" + BETTER_AUTH_URL: "N/A" run: npm run build - name: Upload build artifacts uses: actions/upload-artifact@v3 diff --git a/.forgejo/workflows/production-pr.yml b/.forgejo/workflows/production-pr.yml index e396db3..8976c8b 100644 --- a/.forgejo/workflows/production-pr.yml +++ b/.forgejo/workflows/production-pr.yml @@ -22,6 +22,10 @@ jobs: fetch-depth: 0 - name: Install dependencies run: npm ci + env: + DATABASE_URL: "N/A" + BETTER_AUTH_SECRET: "N/A" + BETTER_AUTH_URL: "N/A" - name: Run tests and generate coverage run: npm run coverage # continue-on-error: true @@ -32,7 +36,7 @@ jobs: with: args: > "-Dsonar.projectKey=GF-dev" - "-Dsonar.projectName=Glowing Fiesta (DEV)" + "-Dsonar.projectName=Glowing Fiesta (dev)" env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..5c3e95f --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npm exec lint-staged diff --git a/app/components/LoginForm.vue b/app/components/LoginForm.vue new file mode 100644 index 0000000..1906254 --- /dev/null +++ b/app/components/LoginForm.vue @@ -0,0 +1,65 @@ + + + diff --git a/app/components/SignupForm.vue b/app/components/SignupForm.vue new file mode 100644 index 0000000..134d456 --- /dev/null +++ b/app/components/SignupForm.vue @@ -0,0 +1,81 @@ + + + diff --git a/app/components/ui/card/Card.vue b/app/components/ui/card/Card.vue new file mode 100644 index 0000000..230f9bc --- /dev/null +++ b/app/components/ui/card/Card.vue @@ -0,0 +1,17 @@ + + + diff --git a/app/components/ui/card/CardAction.vue b/app/components/ui/card/CardAction.vue new file mode 100644 index 0000000..e4f50b4 --- /dev/null +++ b/app/components/ui/card/CardAction.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/ui/card/CardContent.vue b/app/components/ui/card/CardContent.vue new file mode 100644 index 0000000..09af2c3 --- /dev/null +++ b/app/components/ui/card/CardContent.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/ui/card/CardDescription.vue b/app/components/ui/card/CardDescription.vue new file mode 100644 index 0000000..9c129a8 --- /dev/null +++ b/app/components/ui/card/CardDescription.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/ui/card/CardFooter.vue b/app/components/ui/card/CardFooter.vue new file mode 100644 index 0000000..7d01f18 --- /dev/null +++ b/app/components/ui/card/CardFooter.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/ui/card/CardHeader.vue b/app/components/ui/card/CardHeader.vue new file mode 100644 index 0000000..abcf0e5 --- /dev/null +++ b/app/components/ui/card/CardHeader.vue @@ -0,0 +1,22 @@ + + + diff --git a/app/components/ui/card/CardTitle.vue b/app/components/ui/card/CardTitle.vue new file mode 100644 index 0000000..a4e55b2 --- /dev/null +++ b/app/components/ui/card/CardTitle.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/ui/card/index.ts b/app/components/ui/card/index.ts new file mode 100644 index 0000000..409685b --- /dev/null +++ b/app/components/ui/card/index.ts @@ -0,0 +1,7 @@ +export { default as Card } from "./Card.vue"; +export { default as CardAction } from "./CardAction.vue"; +export { default as CardContent } from "./CardContent.vue"; +export { default as CardDescription } from "./CardDescription.vue"; +export { default as CardFooter } from "./CardFooter.vue"; +export { default as CardHeader } from "./CardHeader.vue"; +export { default as CardTitle } from "./CardTitle.vue"; diff --git a/app/components/ui/field/Field.vue b/app/components/ui/field/Field.vue new file mode 100644 index 0000000..e3ebd0d --- /dev/null +++ b/app/components/ui/field/Field.vue @@ -0,0 +1,17 @@ + + + diff --git a/app/components/ui/field/FieldContent.vue b/app/components/ui/field/FieldContent.vue new file mode 100644 index 0000000..37502ba --- /dev/null +++ b/app/components/ui/field/FieldContent.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/ui/field/FieldDescription.vue b/app/components/ui/field/FieldDescription.vue new file mode 100644 index 0000000..70a253b --- /dev/null +++ b/app/components/ui/field/FieldDescription.vue @@ -0,0 +1,24 @@ + + + diff --git a/app/components/ui/field/FieldError.vue b/app/components/ui/field/FieldError.vue new file mode 100644 index 0000000..25c541a --- /dev/null +++ b/app/components/ui/field/FieldError.vue @@ -0,0 +1,50 @@ + + + diff --git a/app/components/ui/field/FieldGroup.vue b/app/components/ui/field/FieldGroup.vue new file mode 100644 index 0000000..1395420 --- /dev/null +++ b/app/components/ui/field/FieldGroup.vue @@ -0,0 +1,22 @@ + + + diff --git a/app/components/ui/field/FieldLabel.vue b/app/components/ui/field/FieldLabel.vue new file mode 100644 index 0000000..c3bfefe --- /dev/null +++ b/app/components/ui/field/FieldLabel.vue @@ -0,0 +1,25 @@ + + + diff --git a/app/components/ui/field/FieldLegend.vue b/app/components/ui/field/FieldLegend.vue new file mode 100644 index 0000000..693d351 --- /dev/null +++ b/app/components/ui/field/FieldLegend.vue @@ -0,0 +1,19 @@ + + + diff --git a/app/components/ui/field/FieldSeparator.vue b/app/components/ui/field/FieldSeparator.vue new file mode 100644 index 0000000..0da8362 --- /dev/null +++ b/app/components/ui/field/FieldSeparator.vue @@ -0,0 +1,26 @@ + + + diff --git a/app/components/ui/field/FieldSet.vue b/app/components/ui/field/FieldSet.vue new file mode 100644 index 0000000..df6ead3 --- /dev/null +++ b/app/components/ui/field/FieldSet.vue @@ -0,0 +1,19 @@ + + + diff --git a/app/components/ui/field/FieldTitle.vue b/app/components/ui/field/FieldTitle.vue new file mode 100644 index 0000000..2602818 --- /dev/null +++ b/app/components/ui/field/FieldTitle.vue @@ -0,0 +1,22 @@ + + + diff --git a/app/components/ui/field/index.ts b/app/components/ui/field/index.ts new file mode 100644 index 0000000..a310647 --- /dev/null +++ b/app/components/ui/field/index.ts @@ -0,0 +1,36 @@ +import type { VariantProps } from "class-variance-authority"; +import { cva } from "class-variance-authority"; + +export const fieldVariants = cva("group/field flex w-full gap-3 data-[invalid=true]:text-destructive", { + variants: { + orientation: { + vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"], + horizontal: [ + "flex-row items-center", + "[&>[data-slot=field-label]]:flex-auto", + "has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + responsive: [ + "flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto", + "@md/field-group:[&>[data-slot=field-label]]:flex-auto", + "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + }, + }, + defaultVariants: { + orientation: "vertical", + }, +}); + +export type FieldVariants = VariantProps; + +export { default as Field } from "./Field.vue"; +export { default as FieldContent } from "./FieldContent.vue"; +export { default as FieldDescription } from "./FieldDescription.vue"; +export { default as FieldError } from "./FieldError.vue"; +export { default as FieldGroup } from "./FieldGroup.vue"; +export { default as FieldLabel } from "./FieldLabel.vue"; +export { default as FieldLegend } from "./FieldLegend.vue"; +export { default as FieldSeparator } from "./FieldSeparator.vue"; +export { default as FieldSet } from "./FieldSet.vue"; +export { default as FieldTitle } from "./FieldTitle.vue"; diff --git a/app/components/ui/label/Label.vue b/app/components/ui/label/Label.vue new file mode 100644 index 0000000..ec68d2f --- /dev/null +++ b/app/components/ui/label/Label.vue @@ -0,0 +1,26 @@ + + + diff --git a/app/components/ui/label/index.ts b/app/components/ui/label/index.ts new file mode 100644 index 0000000..38eaa35 --- /dev/null +++ b/app/components/ui/label/index.ts @@ -0,0 +1 @@ +export { default as Label } from "./Label.vue"; diff --git a/app/components/ui/sonner/Sonner.vue b/app/components/ui/sonner/Sonner.vue new file mode 100644 index 0000000..2f1d52d --- /dev/null +++ b/app/components/ui/sonner/Sonner.vue @@ -0,0 +1,42 @@ + + + diff --git a/app/components/ui/sonner/index.ts b/app/components/ui/sonner/index.ts new file mode 100644 index 0000000..39a59dd --- /dev/null +++ b/app/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from "./Sonner.vue"; diff --git a/app/layouts/Default.vue b/app/layouts/Default.vue index a22ffae..a8a9b30 100644 --- a/app/layouts/Default.vue +++ b/app/layouts/Default.vue @@ -1,4 +1,5 @@