From 5497fb75a720714ae72960f352c4003d1c15d540 Mon Sep 17 00:00:00 2001 From: Bradley Shellnut Date: Sat, 28 Dec 2024 23:45:43 -0800 Subject: [PATCH] Adding timespan, token service, fixing ui for password form. --- package.json | 4 +- pnpm-lock.yaml | 104 +++++++++--------- src/lib/components/ui/separator/index.ts | 7 ++ .../components/ui/separator/separator.svelte | 22 ++++ .../api/common/services/tokens.service.ts | 44 ++++++++ .../login-requests/login-requests.service.ts | 4 +- src/lib/utils/timespan.ts | 58 ++++++++++ .../login/(components)/login-form.svelte | 43 ++++++-- 8 files changed, 220 insertions(+), 66 deletions(-) create mode 100644 src/lib/components/ui/separator/index.ts create mode 100644 src/lib/components/ui/separator/separator.svelte create mode 100644 src/lib/server/api/common/services/tokens.service.ts create mode 100644 src/lib/utils/timespan.ts diff --git a/package.json b/package.json index 152db7d..140b4d8 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@storybook/test": "^8.4.7", "@sveltejs/adapter-node": "^5.2.9", "@sveltejs/enhanced-img": "^0.4.4", - "@sveltejs/kit": "^2.9.0", + "@sveltejs/kit": "^2.15.1", "@sveltejs/vite-plugin-svelte": "^5.0.3", "@tanstack/svelte-query": "^5.62.9", "@tanstack/svelte-query-devtools": "^5.62.9", @@ -95,7 +95,7 @@ "dotenv": "^16.4.7", "drizzle-orm": "^0.38.3", "drizzle-zod": "^0.6.1", - "hono": "^4.6.14", + "hono": "^4.6.15", "hono-pino": "^0.7.0", "hono-rate-limiter": "^0.4.2", "hono-zod-openapi": "^0.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 286fed4..19e6da7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,16 +10,16 @@ importers: dependencies: '@hono/swagger-ui': specifier: ^0.5.0 - version: 0.5.0(hono@4.6.14) + version: 0.5.0(hono@4.6.15) '@hono/zod-openapi': specifier: ^0.18.3 - version: 0.18.3(hono@4.6.14)(zod@3.24.1) + version: 0.18.3(hono@4.6.15)(zod@3.24.1) '@hono/zod-validator': specifier: ^0.4.2 - version: 0.4.2(hono@4.6.14)(zod@3.24.1) + version: 0.4.2(hono@4.6.15)(zod@3.24.1) '@inlang/paraglide-sveltekit': specifier: ^0.15.0 - version: 0.15.0(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))) + version: 0.15.0(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))) '@needle-di/core': specifier: ^0.8.4 version: 0.8.4 @@ -46,7 +46,7 @@ importers: version: 1.0.0 '@scalar/hono-api-reference': specifier: ^0.5.162 - version: 0.5.165(hono@4.6.14) + version: 0.5.165(hono@4.6.15) '@tailwindcss/container-queries': specifier: ^0.1.1 version: 0.1.1(tailwindcss@3.4.17) @@ -75,17 +75,17 @@ importers: specifier: ^0.6.1 version: 0.6.1(drizzle-orm@0.38.3(@types/pg@8.11.10)(@types/react@19.0.2)(pg@8.13.1)(postgres@3.4.5)(react@18.3.1))(zod@3.24.1) hono: - specifier: ^4.6.14 - version: 4.6.14 + specifier: ^4.6.15 + version: 4.6.15 hono-pino: specifier: ^0.7.0 - version: 0.7.0(hono@4.6.14)(pino@9.6.0) + version: 0.7.0(hono@4.6.15)(pino@9.6.0) hono-rate-limiter: specifier: ^0.4.2 - version: 0.4.2(hono@4.6.14) + version: 0.4.2(hono@4.6.15) hono-zod-openapi: specifier: ^0.5.0 - version: 0.5.0(hono@4.6.14)(zod@3.24.1) + version: 0.5.0(hono@4.6.15)(zod@3.24.1) ioredis: specifier: ^5.4.2 version: 5.4.2 @@ -121,7 +121,7 @@ importers: version: 0.33.5 stoker: specifier: ^1.4.2 - version: 1.4.2(@asteasolutions/zod-to-openapi@7.3.0(zod@3.24.1))(@hono/zod-openapi@0.18.3(hono@4.6.14)(zod@3.24.1))(hono@4.6.14)(openapi3-ts@4.4.0) + version: 1.4.2(@asteasolutions/zod-to-openapi@7.3.0(zod@3.24.1))(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.24.1))(hono@4.6.15)(openapi3-ts@4.4.0) devDependencies: '@biomejs/biome': specifier: ^1.9.4 @@ -167,13 +167,13 @@ importers: version: 8.4.7(storybook@8.4.7) '@sveltejs/adapter-node': specifier: ^5.2.9 - version: 5.2.11(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))) + version: 5.2.11(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))) '@sveltejs/enhanced-img': specifier: ^0.4.4 version: 0.4.4(rollup@4.29.1)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@sveltejs/kit': - specifier: ^2.9.0 - version: 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) + specifier: ^2.15.1 + version: 2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@sveltejs/vite-plugin-svelte': specifier: ^5.0.3 version: 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) @@ -212,7 +212,7 @@ importers: version: 0.30.1 formsnap: specifier: ^2.0.0 - version: 2.0.0(svelte@5.16.0)(sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2)) + version: 2.0.0(svelte@5.16.0)(sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2)) lucide-svelte: specifier: ^0.469.0 version: 0.469.0(svelte@5.16.0) @@ -239,10 +239,10 @@ importers: version: 0.3.28(svelte@5.16.0) sveltekit-flash-message: specifier: ^2.4.4 - version: 2.4.4(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0) + version: 2.4.4(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0) sveltekit-superforms: specifier: ^2.22.1 - version: 2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2) + version: 2.22.1(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2) tailwind-merge: specifier: ^2.6.0 version: 2.6.0 @@ -2160,8 +2160,8 @@ packages: svelte: ^5.0.0 vite: '>= 5.0.0' - '@sveltejs/kit@2.15.0': - resolution: {integrity: sha512-FI1bhfhFNGI2sKg+BhiRyM4eaOvX+KZqRYSQqL5PK3ZZREX2xufZ6MzZAw79N846OnIxYNqcz/3VOUq+FPDd3w==} + '@sveltejs/kit@2.15.1': + resolution: {integrity: sha512-8t7D3hQHbUDMiaQ2RVnjJJ/+Ur4Fn/tkeySJCsHtX346Q9cp3LAnav8xXdfuqYNJwpUGX0x3BqF1uvbmXQw93A==} engines: {node: '>=18.13'} hasBin: true peerDependencies: @@ -3411,8 +3411,8 @@ packages: hono: ^4.6.10 zod: ^3.21.4 - hono@4.6.14: - resolution: {integrity: sha512-j4VkyUp2xazGJ8eCCLN1Vm/bxdvm/j5ZuU9AIjLu9vapn2M44p9L3Ktr9Vnb2RN2QtcR/wVjZVMlT5k7GJQgPw==} + hono@4.6.15: + resolution: {integrity: sha512-OiQwvAOAaI2JrABBH69z5rsctHDzFzIKJge0nYXgtzGJ0KftwLWcBXm1upJC23/omNRtnqM0gjRMbtXshPdqhQ==} engines: {node: '>=16.9.0'} hookable@5.5.3: @@ -5724,20 +5724,20 @@ snapshots: '@hapi/hoek': 9.3.0 optional: true - '@hono/swagger-ui@0.5.0(hono@4.6.14)': + '@hono/swagger-ui@0.5.0(hono@4.6.15)': dependencies: - hono: 4.6.14 + hono: 4.6.15 - '@hono/zod-openapi@0.18.3(hono@4.6.14)(zod@3.24.1)': + '@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.24.1)': dependencies: '@asteasolutions/zod-to-openapi': 7.3.0(zod@3.24.1) - '@hono/zod-validator': 0.4.2(hono@4.6.14)(zod@3.24.1) - hono: 4.6.14 + '@hono/zod-validator': 0.4.2(hono@4.6.15)(zod@3.24.1) + hono: 4.6.15 zod: 3.24.1 - '@hono/zod-validator@0.4.2(hono@4.6.14)(zod@3.24.1)': + '@hono/zod-validator@0.4.2(hono@4.6.15)(zod@3.24.1)': dependencies: - hono: 4.6.14 + hono: 4.6.15 zod: 3.24.1 '@humanwhocodes/config-array@0.9.5': @@ -5869,12 +5869,12 @@ snapshots: - babel-plugin-macros - debug - '@inlang/paraglide-sveltekit@0.15.0(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))': + '@inlang/paraglide-sveltekit@0.15.0(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))': dependencies: '@inlang/paraglide-js': 1.11.3 '@inlang/paraglide-vite': 1.3.0 '@lix-js/client': 2.2.1 - '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) + '@sveltejs/kit': 2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) commander: 12.1.0 dedent: 1.5.1 devalue: 4.3.3 @@ -6388,10 +6388,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.29.1': optional: true - '@scalar/hono-api-reference@0.5.165(hono@4.6.14)': + '@scalar/hono-api-reference@0.5.165(hono@4.6.15)': dependencies: '@scalar/types': 0.0.25 - hono: 4.6.14 + hono: 4.6.15 '@scalar/openapi-types@0.1.5': {} @@ -6690,12 +6690,12 @@ snapshots: dependencies: storybook: 8.4.7 - '@sveltejs/adapter-node@5.2.11(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))': + '@sveltejs/adapter-node@5.2.11(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))': dependencies: '@rollup/plugin-commonjs': 28.0.2(rollup@4.29.1) '@rollup/plugin-json': 6.1.0(rollup@4.29.1) '@rollup/plugin-node-resolve': 16.0.0(rollup@4.29.1) - '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) + '@sveltejs/kit': 2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) rollup: 4.29.1 '@sveltejs/enhanced-img@0.4.4(rollup@4.29.1)(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': @@ -6710,7 +6710,7 @@ snapshots: transitivePeerDependencies: - rollup - '@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': + '@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': dependencies: '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) '@types/cookie': 0.6.0 @@ -7897,11 +7897,11 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 - formsnap@2.0.0(svelte@5.16.0)(sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2)): + formsnap@2.0.0(svelte@5.16.0)(sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2)): dependencies: svelte: 5.16.0 svelte-toolbelt: 0.5.0(svelte@5.16.0) - sveltekit-superforms: 2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2) + sveltekit-superforms: 2.22.1(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2) forwarded@0.2.0: {} @@ -7998,24 +7998,24 @@ snapshots: help-me@5.0.0: {} - hono-pino@0.7.0(hono@4.6.14)(pino@9.6.0): + hono-pino@0.7.0(hono@4.6.15)(pino@9.6.0): dependencies: defu: 6.1.4 - hono: 4.6.14 + hono: 4.6.15 pino: 9.6.0 - hono-rate-limiter@0.4.2(hono@4.6.14): + hono-rate-limiter@0.4.2(hono@4.6.15): dependencies: - hono: 4.6.14 + hono: 4.6.15 - hono-zod-openapi@0.5.0(hono@4.6.14)(zod@3.24.1): + hono-zod-openapi@0.5.0(hono@4.6.15)(zod@3.24.1): dependencies: - '@hono/zod-validator': 0.4.2(hono@4.6.14)(zod@3.24.1) - hono: 4.6.14 + '@hono/zod-validator': 0.4.2(hono@4.6.15)(zod@3.24.1) + hono: 4.6.15 zod: 3.24.1 zod-openapi: 4.2.1(zod@3.24.1) - hono@4.6.14: {} + hono@4.6.15: {} hookable@5.5.3: {} @@ -9024,13 +9024,13 @@ snapshots: std-env@3.8.0: {} - stoker@1.4.2(@asteasolutions/zod-to-openapi@7.3.0(zod@3.24.1))(@hono/zod-openapi@0.18.3(hono@4.6.14)(zod@3.24.1))(hono@4.6.14)(openapi3-ts@4.4.0): + stoker@1.4.2(@asteasolutions/zod-to-openapi@7.3.0(zod@3.24.1))(@hono/zod-openapi@0.18.3(hono@4.6.15)(zod@3.24.1))(hono@4.6.15)(openapi3-ts@4.4.0): dependencies: '@asteasolutions/zod-to-openapi': 7.3.0(zod@3.24.1) - hono: 4.6.14 + hono: 4.6.15 openapi3-ts: 4.4.0 optionalDependencies: - '@hono/zod-openapi': 0.18.3(hono@4.6.14)(zod@3.24.1) + '@hono/zod-openapi': 0.18.3(hono@4.6.15)(zod@3.24.1) storybook@8.4.7: dependencies: @@ -9223,14 +9223,14 @@ snapshots: transitivePeerDependencies: - supports-color - sveltekit-flash-message@2.4.4(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0): + sveltekit-flash-message@2.4.4(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0): dependencies: - '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) + '@sveltejs/kit': 2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) svelte: 5.16.0 - sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2): + sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(@types/json-schema@7.0.15)(svelte@5.16.0)(typescript@5.7.2): dependencies: - '@sveltejs/kit': 2.15.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) + '@sveltejs/kit': 2.15.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)))(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) devalue: 5.1.1 memoize-weak: 1.0.2 svelte: 5.16.0 diff --git a/src/lib/components/ui/separator/index.ts b/src/lib/components/ui/separator/index.ts new file mode 100644 index 0000000..82442d2 --- /dev/null +++ b/src/lib/components/ui/separator/index.ts @@ -0,0 +1,7 @@ +import Root from "./separator.svelte"; + +export { + Root, + // + Root as Separator, +}; diff --git a/src/lib/components/ui/separator/separator.svelte b/src/lib/components/ui/separator/separator.svelte new file mode 100644 index 0000000..839494d --- /dev/null +++ b/src/lib/components/ui/separator/separator.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/server/api/common/services/tokens.service.ts b/src/lib/server/api/common/services/tokens.service.ts new file mode 100644 index 0000000..a478be9 --- /dev/null +++ b/src/lib/server/api/common/services/tokens.service.ts @@ -0,0 +1,44 @@ +import { inject, injectable } from '@needle-di/core'; +import { generateRandomString, type RandomReader } from "@oslojs/crypto/random"; +import { HashingService } from './hashing.service'; +import { createDate, TimeSpan, type TimeSpanUnit } from '@/utils/timespan'; + +@injectable() +export class TokensService { + constructor(private hashingService = inject(HashingService)) {} + + generateToken() { + const alphabet = '23456789ACDEFGHJKLMNPQRSTUVWXYZ'; // alphabet with removed look-alike characters (0, 1, O, I) + const random: RandomReader = { + read(bytes) { + crypto.getRandomValues(bytes); + } + } + return generateRandomString(random, alphabet, 10); + } + + generateTokenWithExpiry(number: number, lifespan: TimeSpanUnit) { + return { + token: this.generateToken(), + expiry: createDate(new TimeSpan(number, lifespan)), + }; + } + + async generateTokenWithExpiryAndHash(number: number, lifespan: TimeSpanUnit) { + const token = this.generateToken(); + const hashedToken = await this.hashingService.hash(token); + return { + token, + hashedToken, + expiry: createDate(new TimeSpan(number, lifespan)), + }; + } + + async createHashedToken(token: string) { + return this.hashingService.hash(token); + } + + async verifyHashedToken(hashedToken: string, token: string) { + return this.hashingService.compare(hashedToken, token); + } +} diff --git a/src/lib/server/api/iam/login-requests/login-requests.service.ts b/src/lib/server/api/iam/login-requests/login-requests.service.ts index 3e41beb..d41675e 100644 --- a/src/lib/server/api/iam/login-requests/login-requests.service.ts +++ b/src/lib/server/api/iam/login-requests/login-requests.service.ts @@ -12,6 +12,7 @@ import { UsersRepository } from '../../users/users.repository'; import { VerificationCodesService } from '../../common/services/verification-codes.service'; import type { LoginRequestDto } from './dtos/login-request.dto'; import { CredentialsRepository } from '../../users/credentials.repository'; +import { TokensService } from '../../common/services/tokens.service'; @injectable() export class LoginRequestsService { @@ -22,6 +23,7 @@ export class LoginRequestsService { private verificationCodesService = inject(VerificationCodesService), private usersService = inject(UsersService), private sessionsService = inject(SessionsService), + private tokensService = inject(TokensService), private mailer = inject(MailerService), ) {} @@ -38,7 +40,7 @@ export class LoginRequestsService { throw BadRequest('Invalid credentials'); } - if (!(await this.tokensService.verifyHashedToken(credential.secret_data, data.password))) { + if (!(await this.tokensService.verifyHashedToken(credential.secret_data, password))) { throw BadRequest('Invalid credentials'); } diff --git a/src/lib/utils/timespan.ts b/src/lib/utils/timespan.ts new file mode 100644 index 0000000..d03108f --- /dev/null +++ b/src/lib/utils/timespan.ts @@ -0,0 +1,58 @@ +export type TimeSpanUnit = "ms" | "s" | "m" | "h" | "d" | "w"; + +export class TimeSpan { + constructor(value: number, unit: TimeSpanUnit) { + this.value = value; + this.unit = unit; + } + + public value: number; + public unit: TimeSpanUnit; + + public milliseconds(): number { + if (this.unit === "ms") { + return this.value; + } + if (this.unit === "s") { + return this.value * 1000; + } + if (this.unit === "m") { + return this.value * 1000 * 60; + } + if (this.unit === "h") { + return this.value * 1000 * 60 * 60; + } + if (this.unit === "d") { + return this.value * 1000 * 60 * 60 * 24; + } + return this.value * 1000 * 60 * 60 * 24 * 7; + } + + public seconds(): number { + return this.milliseconds() / 1000; + } + + public transform(x: number): TimeSpan { + return new TimeSpan(Math.round(this.milliseconds() * x), "ms"); + } +} + +export function isWithinExpirationDate(date: Date): boolean { + return Date.now() < date.getTime(); +} + +export function createDate(timeSpan: TimeSpan): Date { + return new Date(Date.now() + timeSpan.milliseconds()); +} + +export type TypedArray = + | Uint8Array + | Int8Array + | Uint16Array + | Int16Array + | Uint32Array + | Int32Array + | Float32Array + | Float64Array + | BigInt64Array + | BigUint64Array; \ No newline at end of file diff --git a/src/routes/(app)/login/(components)/login-form.svelte b/src/routes/(app)/login/(components)/login-form.svelte index 8139a85..fe5ad67 100644 --- a/src/routes/(app)/login/(components)/login-form.svelte +++ b/src/routes/(app)/login/(components)/login-form.svelte @@ -28,11 +28,12 @@ import ChevronLeftIcon from 'lucide-svelte/icons/chevron-left'; import { queryHandler } from '$lib/tanstack-query'; import * as InputOTP from '$lib/components/ui/input-otp/index.js'; + import Separator from '@/components/ui/separator/separator.svelte'; const RESEND_VERIFICATION_CODE_COOLDOWN = 60; let queryClient = useQueryClient(); - let step = $state<'login' | 'request' | 'verify'>('request'); + let step = $state<'login' | 'totp' | 'request' | 'verify'>('request'); let countdownTimer = $state(RESEND_VERIFICATION_CODE_COOLDOWN); let resendVerificationCodeOnCooldown = $derived( countdownTimer != RESEND_VERIFICATION_CODE_COOLDOWN @@ -42,9 +43,12 @@ const requestUsernamePasswordLoginMutation = createMutation({ ...queryHandler().iam.requestUsernamePasswordLogin(), onSuccess(_data, variables, _context) { - step = 'verify'; - $verifyForm.email = variables.json.email; - $verifyForm. + step = 'totp'; + $loginPasswordForm.email = variables.json.email; + $loginPasswordForm.password = variables.json.password; + }, + onError(error) { + loginPasswordErrors.set({ email: [error.message] }); } }) @@ -71,17 +75,30 @@ } }); - /* ------------------------------- Login Form ------------------------------- */ + /* ------------------------------- Login Password Form ------------------------------- */ const sf_login_username_password = superForm(defaults(zod(loginPasswordSchema)), { resetForm: false, SPA: true, validators: zod(loginPasswordSchema), async onUpdated(event) { - if (!event.form.valid) return; - await $requestUsernamePasswordLogin.mutateAsync({ json: event.form.data }); + if (!event.form.valid) { + return; + } + await $requestUsernamePasswordLoginMutation.mutateAsync({ json: event.form.data }); + + if ($requestUsernamePasswordLoginMutation.error) { + return setError(event.form, 'email', $requestUsernamePasswordLoginMutation.error.message); + } + + step = 'totp'; + $loginPasswordForm.email = event.form.data.email; } }) + const { form: loginPasswordForm, enhance: loginPasswordEnhance, errors: loginPasswordErrors } = sf_login_username_password; + + /* ------------------------------- Login Form ------------------------------- */ + const sf_login = superForm(defaults(zod(loginSchema)), { resetForm: false, SPA: true, @@ -95,14 +112,14 @@ } }); - if ($requestMutation.error) + if ($requestMutation.error) { return setError(event.form, 'email', $requestMutation.error.message); + } step = 'verify'; $verifyForm.email = event.form.data.email; } }); - const { form: loginPasswordForm, enhance: loginPasswordEnhance, errors: loginPasswordErrors } = sf_login_username_password; const { form: loginForm, enhance: loginEnhance, errors: requestErrors } = sf_login; @@ -161,7 +178,7 @@ Enter your email below to login to your account -
+ {#snippet children({ props })} @@ -182,7 +199,9 @@ - + + +
@@ -207,6 +226,8 @@ + +