diff --git a/.env.example b/.env.example index b79c2dd..e611f01 100644 --- a/.env.example +++ b/.env.example @@ -20,3 +20,9 @@ MEILI_HOST=http://meilisearch:7700 # JWT JWT_SECRET="a-very-secret-key" JWT_EXPIRES_IN="7d" + + + +# Admin users +ADMIN_EMAIL=admin@local.com +ADMIN_PASSWORD=a_strong_pass \ No newline at end of file diff --git a/README.md b/README.md index b874052..bd5075a 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Open Archive is built on a modern, scalable, and maintainable technology stack: 3. **Run the application:** ```bash - docker-compose up -d + docker compose up -d ``` This command will build the necessary Docker images and start all the services (frontend, backend, database, etc.) in the background. diff --git a/package.json b/package.json index ef3bcdf..3a83cc9 100644 --- a/package.json +++ b/package.json @@ -2,20 +2,21 @@ "name": "open-archive", "private": true, "scripts": { - "dev": "pnpm --filter \"./packages/*\" --parallel dev", + "dev": "dotenv -- pnpm --filter \"./packages/*\" --parallel dev", "build": "pnpm --filter \"./packages/*\" --parallel build" }, "devDependencies": { - "typescript": "^5.0.0" + "dotenv-cli": "8.0.0", + "typescript": "5.8.3" }, "packageManager": "pnpm@10.13.1", "engines": { - "node": ">=18.0.0", - "pnpm": ">=8.0.0" + "node": ">=22.0.0", + "pnpm": "10.13.1" }, "pnpm": { "onlyBuiltDependencies": [ "esbuild" ] } -} \ No newline at end of file +} diff --git a/packages/backend/package.json b/packages/backend/package.json index b020bb7..4bda81e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -11,9 +11,15 @@ }, "dependencies": { "@open-archive/types": "workspace:*", - "express": "^5.1.0", + "bcryptjs": "^3.0.2", "bullmq": "^5.56.3", - "dotenv": "^17.2.0" + "dotenv": "^17.2.0", + "express": "^5.1.0", + "jose": "^6.0.11", + "pg": "^8.16.3", + "reflect-metadata": "^0.2.2", + "sqlite3": "^5.1.7", + "tsconfig-paths": "^4.2.0" }, "devDependencies": { "@types/express": "^5.0.3", diff --git a/packages/backend/src/api/controllers/auth.controller.ts b/packages/backend/src/api/controllers/auth.controller.ts new file mode 100644 index 0000000..48a0e7b --- /dev/null +++ b/packages/backend/src/api/controllers/auth.controller.ts @@ -0,0 +1,32 @@ +import type { Request, Response } from 'express'; +import type { IAuthService } from '../../services/AuthService'; + +export class AuthController { + #authService: IAuthService; + + constructor(authService: IAuthService) { + this.#authService = authService; + } + + public login = async (req: Request, res: Response): Promise => { + const { email, password } = req.body; + + if (!email || !password) { + return res.status(400).json({ message: 'Email and password are required' }); + } + + try { + const result = await this.#authService.login(email, password); + + if (!result) { + return res.status(401).json({ message: 'Invalid credentials' }); + } + + return res.status(200).json(result); + } catch (error) { + // In a real application, you'd want to log this error. + console.error('Login error:', error); + return res.status(500).json({ message: 'An internal server error occurred' }); + } + }; +} diff --git a/packages/backend/src/api/middleware/requireAuth.ts b/packages/backend/src/api/middleware/requireAuth.ts new file mode 100644 index 0000000..dfed3f5 --- /dev/null +++ b/packages/backend/src/api/middleware/requireAuth.ts @@ -0,0 +1,39 @@ +import type { Request, Response, NextFunction } from 'express'; +import type { IAuthService } from '../../services/AuthService'; +import type { AuthTokenPayload } from '@open-archive/types'; + +// By using module augmentation, we can add our custom 'user' property +// to the Express Request interface in a type-safe way. +declare global { + namespace Express { + export interface Request { + user?: AuthTokenPayload; + } + } +} + +export const requireAuth = (authService: IAuthService) => { + return async (req: Request, res: Response, next: NextFunction) => { + const authHeader = req.headers.authorization; + + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ message: 'Unauthorized: No token provided' }); + } + + const token = authHeader.split(' ')[1]; + + try { + const payload = await authService.verifyToken(token); + + if (!payload) { + return res.status(401).json({ message: 'Unauthorized: Invalid token' }); + } + + req.user = payload; + next(); + } catch (error) { + console.error('Authentication error:', error); + return res.status(500).json({ message: 'An internal server error occurred during authentication' }); + } + }; +}; diff --git a/packages/backend/src/api/routes/auth.routes.ts b/packages/backend/src/api/routes/auth.routes.ts new file mode 100644 index 0000000..bd46cec --- /dev/null +++ b/packages/backend/src/api/routes/auth.routes.ts @@ -0,0 +1,15 @@ +import { Router } from 'express'; +import type { AuthController } from '../controllers/auth.controller'; + +export const createAuthRouter = (authController: AuthController): Router => { + const router = Router(); + + /** + * @route POST /api/v1/auth/login + * @description Authenticates a user and returns a JWT. + * @access Public + */ + router.post('/login', authController.login); + + return router; +}; diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 22868bc..9af51a4 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -1,15 +1,65 @@ import express from 'express'; import dotenv from 'dotenv'; +import { AuthController } from './api/controllers/auth.controller'; +import { requireAuth } from './api/middleware/requireAuth'; +import { createAuthRouter } from './api/routes/auth.routes'; +import { AuthService } from './services/AuthService'; +import { AdminUserService } from './services/UserService'; + +// Load environment variables dotenv.config(); +// --- Environment Variable Validation --- +const { + PORT_BACKEND, + JWT_SECRET, + JWT_EXPIRES_IN +} = process.env; + + +if (!PORT_BACKEND || !JWT_SECRET || !JWT_EXPIRES_IN) { + throw new Error('Missing required environment variables for the backend.'); +} + +// --- Dependency Injection Setup --- + +const userService = new AdminUserService(); +const authService = new AuthService(userService, JWT_SECRET, JWT_EXPIRES_IN); +const authController = new AuthController(authService); + +// --- Express App Initialization --- const app = express(); -const port = process.env.PORT_BACKEND || 8000; + +// Middleware +app.use(express.json()); // For parsing application/json + +// --- Routes --- +const authRouter = createAuthRouter(authController); +app.use('/v1/auth', authRouter); + +// Example of a protected route +app.get('/v1/protected', requireAuth(authService), (req, res) => { + res.json({ + message: 'You have accessed a protected route!', + user: req.user, // The user payload is attached by the requireAuth middleware + }); +}); app.get('/', (req, res) => { res.send('Backend is running!'); }); -app.listen(port, () => { - console.log(`Backend listening at http://localhost:${port}`); -}); +// --- Server Start --- +const startServer = async () => { + try { + app.listen(PORT_BACKEND, () => { + console.log(`Backend listening at http://localhost:${PORT_BACKEND}`); + }); + } catch (error) { + console.error('Failed to start the server:', error); + process.exit(1); + } +}; + +startServer(); diff --git a/packages/backend/src/services/AuthService.ts b/packages/backend/src/services/AuthService.ts new file mode 100644 index 0000000..c48d190 --- /dev/null +++ b/packages/backend/src/services/AuthService.ts @@ -0,0 +1,86 @@ +import { compare, hash } from 'bcryptjs'; +import type { SignJWT, jwtVerify } from 'jose'; +import type { AuthTokenPayload, User, LoginResponse } from '@open-archive/types'; + +// This interface defines the contract for a service that manages users. +// The AuthService will depend on this abstraction, not a concrete implementation. +export interface IUserService { + findByEmail(email: string): Promise; +} + +// This interface defines the contract for our AuthService. +export interface IAuthService { + verifyPassword(password: string, hash: string): Promise; + login(email: string, password: string): Promise; + verifyToken(token: string): Promise; +} + +export class AuthService implements IAuthService { + #userService: IUserService; + #jwtSecret: Uint8Array; + #jwtExpiresIn: string; + #jose: Promise<{ SignJWT: typeof SignJWT; jwtVerify: typeof jwtVerify; }>; + + constructor(userService: IUserService, jwtSecret: string, jwtExpiresIn: string) { + this.#userService = userService; + this.#jwtSecret = new TextEncoder().encode(jwtSecret); + this.#jwtExpiresIn = jwtExpiresIn; + this.#jose = import('jose'); + } + + #hashPassword(password: string): Promise { + return hash(password, 10); + } + + public verifyPassword(password: string, hash: string): Promise { + return compare(password, hash); + } + + async #generateAccessToken(payload: AuthTokenPayload): Promise { + if (!payload.sub) { + throw new Error('JWT payload must have a subject (sub) claim.'); + } + const { SignJWT } = await this.#jose; + return new SignJWT(payload) + .setProtectedHeader({ alg: 'HS256' }) + .setIssuedAt() + .setSubject(payload.sub) + .setExpirationTime(this.#jwtExpiresIn) + .sign(this.#jwtSecret); + } + + public async login(email: string, password: string): Promise { + const user = await this.#userService.findByEmail(email); + + if (!user) { + return null; // User not found + } + + const isPasswordValid = await this.verifyPassword(password, user.passwordHash); + + if (!isPasswordValid) { + return null; // Invalid password + } + + const { passwordHash, ...userWithoutPassword } = user; + + const accessToken = await this.#generateAccessToken({ + sub: user.id, + email: user.email, + role: user.role, + }); + + return { accessToken, user: userWithoutPassword }; + } + + public async verifyToken(token: string): Promise { + try { + const { jwtVerify } = await this.#jose; + const { payload } = await jwtVerify(token, this.#jwtSecret); + return payload; + } catch (error) { + // Token is invalid or expired + return null; + } + } +} diff --git a/packages/backend/src/services/DatabaseService.ts b/packages/backend/src/services/DatabaseService.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/backend/src/services/UserService.ts b/packages/backend/src/services/UserService.ts new file mode 100644 index 0000000..78984e1 --- /dev/null +++ b/packages/backend/src/services/UserService.ts @@ -0,0 +1,31 @@ +import { hash } from 'bcryptjs'; +import type { User } from '@open-archive/types'; +import type { IUserService } from './AuthService'; + +// This is a mock implementation of the IUserService. +// In a real application, this service would interact with a database. +export class AdminUserService implements IUserService { + #users: User[] = []; + + constructor() { + // Immediately seed the user when the service is instantiated. + this.seed(); + } + + // use .env admin user + private async seed() { + const passwordHash = await hash(process.env.ADMIN_PASSWORD as string, 10); + this.#users.push({ + id: '1', + email: process.env.ADMIN_EMAIL as string, + role: 'Super Administrator', + passwordHash: passwordHash, + }); + } + + public async findByEmail(email: string): Promise { + // In a real implementation, this would be a database query. + const user = this.#users.find(u => u.email === email); + return user || null; + } +} diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index c1be55f..8cdda0c 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -3,7 +3,9 @@ "compilerOptions": { "outDir": "./dist", "rootDir": "./src", - "composite": true + "composite": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true }, "include": ["src/**/*.ts"], "exclude": ["node_modules", "dist"] diff --git a/packages/frontend/components.json b/packages/frontend/components.json new file mode 100644 index 0000000..c5d91b4 --- /dev/null +++ b/packages/frontend/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://shadcn-svelte.com/schema.json", + "tailwind": { + "css": "src/app.css", + "baseColor": "slate" + }, + "aliases": { + "components": "$lib/components", + "utils": "$lib/utils", + "ui": "$lib/components/ui", + "hooks": "$lib/hooks", + "lib": "$lib" + }, + "typescript": true, + "registry": "https://shadcn-svelte.com/registry" +} diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 2034817..acaed5b 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -13,17 +13,28 @@ "format": "prettier --write .", "lint": "prettier --check ." }, + "dependencies": { + "@open-archive/types": "workspace:*" + }, "devDependencies": { + "@internationalized/date": "^3.8.2", + "@lucide/svelte": "^0.525.0", "@sveltejs/adapter-auto": "^6.0.0", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@tailwindcss/vite": "^4.0.0", + "bits-ui": "^2.8.10", + "clsx": "^2.1.1", + "dotenv": "^17.2.0", "prettier": "^3.4.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", "svelte": "^5.0.0", "svelte-check": "^4.0.0", + "tailwind-merge": "^3.3.1", + "tailwind-variants": "^1.0.0", "tailwindcss": "^4.0.0", + "tw-animate-css": "^1.3.5", "typescript": "^5.0.0", "vite": "^6.2.6" } diff --git a/packages/frontend/src/app.css b/packages/frontend/src/app.css index d4b5078..e48f5f0 100644 --- a/packages/frontend/src/app.css +++ b/packages/frontend/src/app.css @@ -1 +1,121 @@ -@import 'tailwindcss'; +@import "tailwindcss"; + +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.129 0.042 264.695); + --card: oklch(1 0 0); + --card-foreground: oklch(0.129 0.042 264.695); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.129 0.042 264.695); + --primary: oklch(0.208 0.042 265.755); + --primary-foreground: oklch(0.984 0.003 247.858); + --secondary: oklch(0.968 0.007 247.896); + --secondary-foreground: oklch(0.208 0.042 265.755); + --muted: oklch(0.968 0.007 247.896); + --muted-foreground: oklch(0.554 0.046 257.417); + --accent: oklch(0.968 0.007 247.896); + --accent-foreground: oklch(0.208 0.042 265.755); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.929 0.013 255.508); + --input: oklch(0.929 0.013 255.508); + --ring: oklch(0.704 0.04 256.788); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.984 0.003 247.858); + --sidebar-foreground: oklch(0.129 0.042 264.695); + --sidebar-primary: oklch(0.208 0.042 265.755); + --sidebar-primary-foreground: oklch(0.984 0.003 247.858); + --sidebar-accent: oklch(0.968 0.007 247.896); + --sidebar-accent-foreground: oklch(0.208 0.042 265.755); + --sidebar-border: oklch(0.929 0.013 255.508); + --sidebar-ring: oklch(0.704 0.04 256.788); +} + +.dark { + --background: oklch(0.129 0.042 264.695); + --foreground: oklch(0.984 0.003 247.858); + --card: oklch(0.208 0.042 265.755); + --card-foreground: oklch(0.984 0.003 247.858); + --popover: oklch(0.208 0.042 265.755); + --popover-foreground: oklch(0.984 0.003 247.858); + --primary: oklch(0.929 0.013 255.508); + --primary-foreground: oklch(0.208 0.042 265.755); + --secondary: oklch(0.279 0.041 260.031); + --secondary-foreground: oklch(0.984 0.003 247.858); + --muted: oklch(0.279 0.041 260.031); + --muted-foreground: oklch(0.704 0.04 256.788); + --accent: oklch(0.279 0.041 260.031); + --accent-foreground: oklch(0.984 0.003 247.858); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.551 0.027 264.364); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.208 0.042 265.755); + --sidebar-foreground: oklch(0.984 0.003 247.858); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.984 0.003 247.858); + --sidebar-accent: oklch(0.279 0.041 260.031); + --sidebar-accent-foreground: oklch(0.984 0.003 247.858); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.551 0.027 264.364); +} + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/packages/frontend/src/lib/api.ts b/packages/frontend/src/lib/api.ts new file mode 100644 index 0000000..a14546a --- /dev/null +++ b/packages/frontend/src/lib/api.ts @@ -0,0 +1,32 @@ +import { authStore } from '$lib/stores/auth.store'; +import { get } from 'svelte/store'; + +const BASE_URL = '/api/v1'; // Using a relative URL for proxying + +/** + * A custom fetch wrapper to automatically handle authentication headers. + * @param url The URL to fetch, relative to the API base. + * @param options The standard Fetch API options. + * @returns A Promise that resolves to the Fetch Response. + */ +export const api = async (url: string, options: RequestInit = {}): Promise => { + const { accessToken } = get(authStore); + + const defaultHeaders: HeadersInit = { + 'Content-Type': 'application/json', + }; + + if (accessToken) { + defaultHeaders['Authorization'] = `Bearer ${accessToken}`; + } + + const mergedOptions: RequestInit = { + ...options, + headers: { + ...defaultHeaders, + ...options.headers, + }, + }; + + return fetch(`${BASE_URL}${url}`, mergedOptions); +}; diff --git a/packages/frontend/src/lib/components/ui/button/button.svelte b/packages/frontend/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000..16f1a3c --- /dev/null +++ b/packages/frontend/src/lib/components/ui/button/button.svelte @@ -0,0 +1,80 @@ + + + + +{#if href} + + {@render children?.()} + +{:else} + +{/if} diff --git a/packages/frontend/src/lib/components/ui/button/index.ts b/packages/frontend/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000..fb585d7 --- /dev/null +++ b/packages/frontend/src/lib/components/ui/button/index.ts @@ -0,0 +1,17 @@ +import Root, { + type ButtonProps, + type ButtonSize, + type ButtonVariant, + buttonVariants, +} from "./button.svelte"; + +export { + Root, + type ButtonProps as Props, + // + Root as Button, + buttonVariants, + type ButtonProps, + type ButtonSize, + type ButtonVariant, +}; diff --git a/packages/frontend/src/lib/components/ui/card/card-action.svelte b/packages/frontend/src/lib/components/ui/card/card-action.svelte new file mode 100644 index 0000000..cc36c56 --- /dev/null +++ b/packages/frontend/src/lib/components/ui/card/card-action.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/packages/frontend/src/lib/components/ui/card/card-content.svelte b/packages/frontend/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..bc90b83 --- /dev/null +++ b/packages/frontend/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,15 @@ + + +
+ {@render children?.()} +
diff --git a/packages/frontend/src/lib/components/ui/card/card-description.svelte b/packages/frontend/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..9b20ac7 --- /dev/null +++ b/packages/frontend/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,20 @@ + + +

+ {@render children?.()} +

diff --git a/packages/frontend/src/lib/components/ui/card/card-footer.svelte b/packages/frontend/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..cf43353 --- /dev/null +++ b/packages/frontend/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/packages/frontend/src/lib/components/ui/card/card-header.svelte b/packages/frontend/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..8a91abb --- /dev/null +++ b/packages/frontend/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/packages/frontend/src/lib/components/ui/card/card-title.svelte b/packages/frontend/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..22586e6 --- /dev/null +++ b/packages/frontend/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/packages/frontend/src/lib/components/ui/card/card.svelte b/packages/frontend/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..99448cc --- /dev/null +++ b/packages/frontend/src/lib/components/ui/card/card.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/packages/frontend/src/lib/components/ui/card/index.ts b/packages/frontend/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..4d3fce4 --- /dev/null +++ b/packages/frontend/src/lib/components/ui/card/index.ts @@ -0,0 +1,25 @@ +import Root from "./card.svelte"; +import Content from "./card-content.svelte"; +import Description from "./card-description.svelte"; +import Footer from "./card-footer.svelte"; +import Header from "./card-header.svelte"; +import Title from "./card-title.svelte"; +import Action from "./card-action.svelte"; + +export { + Root, + Content, + Description, + Footer, + Header, + Title, + Action, + // + Root as Card, + Content as CardContent, + Description as CardDescription, + Footer as CardFooter, + Header as CardHeader, + Title as CardTitle, + Action as CardAction, +}; diff --git a/packages/frontend/src/lib/components/ui/input/index.ts b/packages/frontend/src/lib/components/ui/input/index.ts new file mode 100644 index 0000000..f47b6d3 --- /dev/null +++ b/packages/frontend/src/lib/components/ui/input/index.ts @@ -0,0 +1,7 @@ +import Root from "./input.svelte"; + +export { + Root, + // + Root as Input, +}; diff --git a/packages/frontend/src/lib/components/ui/input/input.svelte b/packages/frontend/src/lib/components/ui/input/input.svelte new file mode 100644 index 0000000..19c6dae --- /dev/null +++ b/packages/frontend/src/lib/components/ui/input/input.svelte @@ -0,0 +1,51 @@ + + +{#if type === "file"} + +{:else} + +{/if} diff --git a/packages/frontend/src/lib/components/ui/label/index.ts b/packages/frontend/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..8bfca0b --- /dev/null +++ b/packages/frontend/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from "./label.svelte"; + +export { + Root, + // + Root as Label, +}; diff --git a/packages/frontend/src/lib/components/ui/label/label.svelte b/packages/frontend/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..d0afda3 --- /dev/null +++ b/packages/frontend/src/lib/components/ui/label/label.svelte @@ -0,0 +1,20 @@ + + + diff --git a/packages/frontend/src/lib/index.ts b/packages/frontend/src/lib/index.ts index 856f2b6..e69de29 100644 --- a/packages/frontend/src/lib/index.ts +++ b/packages/frontend/src/lib/index.ts @@ -1 +0,0 @@ -// place files you want to import through the `$lib` alias in this folder. diff --git a/packages/frontend/src/lib/stores/auth.store.ts b/packages/frontend/src/lib/stores/auth.store.ts new file mode 100644 index 0000000..0423a1e --- /dev/null +++ b/packages/frontend/src/lib/stores/auth.store.ts @@ -0,0 +1,61 @@ +import { writable } from 'svelte/store'; +import { browser } from '$app/environment'; +import type { User } from '@open-archive/types'; + +interface AuthState { + accessToken: string | null; + user: Omit | null; +} + +const initialValue: AuthState = { + accessToken: null, + user: null, +}; + +// Function to get the initial state from localStorage +const getInitialState = (): AuthState => { + if (!browser) { + return initialValue; + } + + const storedToken = localStorage.getItem('accessToken'); + const storedUser = localStorage.getItem('user'); + + if (storedToken && storedUser) { + try { + return { + accessToken: storedToken, + user: JSON.parse(storedUser), + }; + } catch (e) { + console.error('Failed to parse user from localStorage', e); + return initialValue; + } + } + + return initialValue; +}; + +const createAuthStore = () => { + const { subscribe, set } = writable(getInitialState()); + + return { + subscribe, + login: (accessToken: string, user: Omit) => { + if (browser) { + localStorage.setItem('accessToken', accessToken); + localStorage.setItem('user', JSON.stringify(user)); + } + set({ accessToken, user }); + }, + logout: () => { + if (browser) { + localStorage.removeItem('accessToken'); + localStorage.removeItem('user'); + } + set(initialValue); + }, + }; +}; + +export const authStore = createAuthStore(); diff --git a/packages/frontend/src/lib/utils.ts b/packages/frontend/src/lib/utils.ts new file mode 100644 index 0000000..55b3a91 --- /dev/null +++ b/packages/frontend/src/lib/utils.ts @@ -0,0 +1,13 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChild = T extends { child?: any } ? Omit : T; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChildren = T extends { children?: any } ? Omit : T; +export type WithoutChildrenOrChild = WithoutChildren>; +export type WithElementRef = T & { ref?: U | null }; diff --git a/packages/frontend/src/routes/+layout.ts b/packages/frontend/src/routes/+layout.ts new file mode 100644 index 0000000..fa4b96d --- /dev/null +++ b/packages/frontend/src/routes/+layout.ts @@ -0,0 +1,31 @@ +import { browser } from '$app/environment'; +import { redirect } from '@sveltejs/kit'; +import { get } from 'svelte/store'; +import { authStore } from '$lib/stores/auth.store'; + +// List of routes that are accessible to unauthenticated users. +const publicRoutes = ['/signin']; + +export const load = ({ url }) => { + // Route protection should only run on the client side where the authStore + // is reliably hydrated from localStorage. + if (browser) { + const { accessToken } = get(authStore); + const isPublicRoute = publicRoutes.includes(url.pathname); + + // If the user is not logged in and trying to access a private route... + if (!accessToken && !isPublicRoute) { + // ...redirect them to the sign-in page. + throw redirect(307, '/signin'); + } + + // If the user is logged in and trying to access a public route (like /signin)... + if (accessToken && isPublicRoute) { + // ...redirect them to the dashboard. + throw redirect(307, '/dashboard'); + } + } + + // For all other cases, allow the page to load. + return {}; +}; diff --git a/packages/frontend/src/routes/+page.svelte b/packages/frontend/src/routes/+page.svelte index cc88df0..d2a7d65 100644 --- a/packages/frontend/src/routes/+page.svelte +++ b/packages/frontend/src/routes/+page.svelte @@ -1,2 +1,5 @@ + +

Welcome to SvelteKit

Visit svelte.dev/docs/kit to read the documentation

diff --git a/packages/frontend/src/routes/+page.ts b/packages/frontend/src/routes/+page.ts new file mode 100644 index 0000000..728c79c --- /dev/null +++ b/packages/frontend/src/routes/+page.ts @@ -0,0 +1,24 @@ +import { redirect } from '@sveltejs/kit'; +import { get } from 'svelte/store'; +import { authStore } from '$lib/stores/auth.store'; +import { browser } from '$app/environment'; + +export const load = () => { + // This logic should only run on the client side where the authStore is hydrated + // from localStorage. + if (browser) { + const { accessToken } = get(authStore); + + if (accessToken) { + // If logged in, go to the dashboard. + throw redirect(307, '/dashboard'); + } else { + // If not logged in, go to the sign-in page. + throw redirect(307, '/signin'); + } + } + + // On the server, we don't know the auth state, so we don't redirect. + // The client-side navigation will take over. + return {}; +}; diff --git a/packages/frontend/src/routes/dashboard/+page.svelte b/packages/frontend/src/routes/dashboard/+page.svelte new file mode 100644 index 0000000..13a25d0 --- /dev/null +++ b/packages/frontend/src/routes/dashboard/+page.svelte @@ -0,0 +1,23 @@ + + + + Dashboard - OpenArchive + + +
+
+

Dashboard

+ +
+

Welcome, {$authStore.user?.email}!

+

You are logged in.

+
diff --git a/packages/frontend/src/routes/signin/+page.svelte b/packages/frontend/src/routes/signin/+page.svelte new file mode 100644 index 0000000..23eabfe --- /dev/null +++ b/packages/frontend/src/routes/signin/+page.svelte @@ -0,0 +1,75 @@ + + + + Login - OpenArchive + + + +
+ + + Login + Enter your email below to login to your account. + + +
+
+ + +
+
+ + +
+ + {#if error} +

{error}

+ {/if} + + +
+
+
+
diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index 2d35c4f..f9c4b6a 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -1,7 +1,20 @@ import tailwindcss from '@tailwindcss/vite'; import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; +import dotenv from 'dotenv'; + +dotenv.config(); export default defineConfig({ - plugins: [tailwindcss(), sveltekit()] + plugins: [tailwindcss(), sveltekit()], + server: { + port: Number(process.env.PORT_FRONTEND) || 3000, + proxy: { + '/api': { + target: `http://localhost:${process.env.PORT_BACKEND || 4000}`, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, '') + } + } + } }); diff --git a/packages/types/package.json b/packages/types/package.json index d115ab0..cc039f1 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -10,5 +10,8 @@ }, "devDependencies": { "typescript": "^5.0.0" + }, + "dependencies": { + "jose": "^6.0.11" } } diff --git a/packages/types/src/auth.types.ts b/packages/types/src/auth.types.ts new file mode 100644 index 0000000..85521d0 --- /dev/null +++ b/packages/types/src/auth.types.ts @@ -0,0 +1,31 @@ +import type { JWTPayload } from 'jose'; +import type { User } from './user.types'; + +/** + * Defines the payload structure for the JWT, extending the standard JWTPayload. + * This is the data that will be encoded into the token. + */ +export interface AuthTokenPayload extends JWTPayload { + /** + * The user's email address. + */ + email: string; + /** + * The user's role, used for authorization. + */ + role: User['role']; +} + +/** + * Defines the structure of the response from a successful login request. + */ +export interface LoginResponse { + /** + * The JSON Web Token for authenticating subsequent requests. + */ + accessToken: string; + /** + * The authenticated user's information. + */ + user: Omit; +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index e9b0f21..219d19b 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,7 +1,2 @@ -export type UserRole = 'Admin' | 'Auditor' | 'EndUser'; - -export interface User { - id: string; - email: string; - role: UserRole; -} +export * from './auth.types'; +export * from './user.types'; diff --git a/packages/types/src/user.types.ts b/packages/types/src/user.types.ts new file mode 100644 index 0000000..1647a45 --- /dev/null +++ b/packages/types/src/user.types.ts @@ -0,0 +1,26 @@ +/** + * Defines the possible roles a user can have within the system. + */ +export type UserRole = 'Super Administrator' | 'Auditor/Compliance Officer' | 'End User'; + +/** + * Represents a user account in the system. + */ +export interface User { + /** + * The unique identifier for the user. + */ + id: string; + /** + * The user's email address, used for login. + */ + email: string; + /** + * The user's assigned role, which determines their permissions. + */ + role: UserRole; + /** + * The hashed password for the user. This should never be exposed to the client. + */ + passwordHash: string; +} diff --git a/packages/types/tsconfig.tsbuildinfo b/packages/types/tsconfig.tsbuildinfo new file mode 100644 index 0000000..3338e15 --- /dev/null +++ b/packages/types/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.promise.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.iterator.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.float16.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/lib/lib.esnext.full.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/types.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwe/general/decrypt.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwe/general/encrypt.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jws/compact/verify.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jws/flattened/verify.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jws/general/verify.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwt/verify.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwt/decrypt.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jws/compact/sign.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jws/flattened/sign.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jws/general/sign.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwt/sign.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwt/encrypt.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwk/thumbprint.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwk/embedded.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwks/local.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwks/remote.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/jwt/unsecured.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/key/export.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/key/import.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/util/decode_protected_header.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/util/decode_jwt.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/util/errors.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/key/generate_key_pair.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/key/generate_secret.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/util/base64url.d.ts","../../node_modules/.pnpm/jose@6.0.11/node_modules/jose/dist/types/index.d.ts","./src/user.types.ts","./src/auth.types.ts","./src/index.ts"],"fileIdsList":[[85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114],[85],[115,116],[116,117]],"fileInfos":[{"version":"69684132aeb9b5642cbcd9e22dff7818ff0ee1aa831728af0ecf97d3364d5546","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"8bf8b5e44e3c9c36f98e1007e8b7018c0f38d8adc07aecef42f5200114547c70","impliedFormat":1},{"version":"092c2bfe125ce69dbb1223c85d68d4d2397d7d8411867b5cc03cec902c233763","affectsGlobalScope":true,"impliedFormat":1},{"version":"07f073f19d67f74d732b1adea08e1dc66b1b58d77cb5b43931dee3d798a2fd53","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7a3c8b952931daebdfc7a2897c53c0a1c73624593fa070e46bd537e64dcd20a","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"936e80ad36a2ee83fc3caf008e7c4c5afe45b3cf3d5c24408f039c1d47bdc1df","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"fef8cfad2e2dc5f5b3d97a6f4f2e92848eb1b88e897bb7318cef0e2820bceaab","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"b5ce7a470bc3628408429040c4e3a53a27755022a32fd05e2cb694e7015386c7","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"4245fee526a7d1754529d19227ecbf3be066ff79ebb6a380d78e41648f2f224d","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"bde31fd423cd93b0eff97197a3f66df7c93e8c0c335cbeb113b7ff1ac35c23f4","impliedFormat":1},{"version":"dc9e7909f3edca55a7da578ab1f2b473490cf1cea844fd05af2daee94e17e518","impliedFormat":99},{"version":"a380cd0a371b5b344c2f679a932593f02445571f9de0014bdf013dddf2a77376","impliedFormat":99},{"version":"dbbcd13911daafc1554acc17dad18ab92f91b5b8f084c6c4370cb8c60520c3b6","impliedFormat":99},{"version":"ab17464cd8391785c29509c629aa8477c8e86d4d3013f4c200b71ac574774ec2","impliedFormat":99},{"version":"d7f1043cbc447d09c8962c973d9f60e466c18e6bbaa470777901d9c2d357cfbe","impliedFormat":99},{"version":"e130a73d7e1e34953b1964c17c218fd14fccd1df6f15f111352b0d53291311bb","impliedFormat":99},{"version":"4ddecad872558e2b3df434ef0b01114d245e7a18a86afa6e7b5c68e75f9b8f76","impliedFormat":99},{"version":"a0ab7a82c3f844d4d4798f68f7bd6dc304e9ad6130631c90a09fb2636cb62756","impliedFormat":99},{"version":"270ceb915b1304c042b6799de28ff212cfa4baf06900d3a8bc4b79f62f00c8a7","impliedFormat":99},{"version":"1b3174ea6e3b4ae157c88eb28bf8e6d67f044edc9c552daf5488628fd8e5be97","impliedFormat":99},{"version":"1d1c0e6bda55b6fdcc247c4abd1ba2a36b50aac71bbf78770cbd172713c4e05f","impliedFormat":99},{"version":"d7d8a5f6a306b755dfa5a9b101cb800fd912b256222fb7d4629b5de416b4b8d5","impliedFormat":99},{"version":"5585ed538922e2e58655218652dcb262f08afa902f26f490cdec4967887ac31a","impliedFormat":99},{"version":"b46de7238d9d2243b27a21797e4772ba91465caae9c31f21dc43748dc9de9cd0","impliedFormat":99},{"version":"625fdbce788630c62f793cb6c80e0072ce0b8bf1d4d0a9922430671164371e0b","impliedFormat":99},{"version":"b6790300d245377671c085e76e9ef359b3cbba6821b913d6ce6b2739d00b9fb1","impliedFormat":99},{"version":"6beaff23ae0b12aa3b7672c7fd4e924f5088efa899b58fe83c7cc5675234ff14","impliedFormat":99},{"version":"a36c717362d06d76e7332d9c1d2744c2c5e4b4a5da6218ef7b4a299a62d23a6d","impliedFormat":99},{"version":"a61f8455fd21cec75a8288cd761f5bcc72441848841eb64aa09569e9d8929ff0","impliedFormat":99},{"version":"7539c82be2eb9b83ec335b11bb06dc35497f0b7dab8830b2c08b650d62707160","impliedFormat":99},{"version":"0eaa77f9ed4c3eb8fac011066c987b6faa7c70db95cfe9e3fb434573e095c4c8","impliedFormat":99},{"version":"466e7296272b827c55b53a7858502de733733558966e2e3a7cc78274e930210a","impliedFormat":99},{"version":"364a5c527037fdd7d494ab0a97f510d3ceda30b8a4bc598b490c135f959ff3c6","impliedFormat":99},{"version":"d26c255888cc20d5ab7397cc267ad81c8d7e97624c442a218afec00949e7316e","impliedFormat":99},{"version":"83d2dab980f2d1a2fe333f0001de8f42c831a438159d47b77c686ae405891b7f","impliedFormat":99},{"version":"ca369bcbdafc423d1a9dccd69de98044534900ff8236d2dd970b52438afb5355","impliedFormat":99},{"version":"5b90280e84e8eba347caaefc18210de3ce6ac176f5e82705a28e7f497dcc8689","impliedFormat":99},{"version":"6fc2d85e6d20a566b97001ee9a74dacc18d801bc9e9b735988119036db992932","impliedFormat":99},{"version":"d57bf30bf951ca5ce0119fcce3810bd03205377d78f08dfe6fca9d350ce73edc","impliedFormat":99},{"version":"e7878d8cd1fd0d0f1c55dcd8f5539f4c22e44993852f588dd194bd666b230727","impliedFormat":99},{"version":"638575c7a309a595c5ac3a65f03a643438fd81bf378aac93eadb84461cdd247c","impliedFormat":99},{"version":"70eebdf8cb991b4b5a1bfd9540698012cf9b2f11d265f7349e2fb7b7cb82d07b","signature":"4c60eadea5f3da0a00a59d1d46e3c9e106da50e89df04f75bd88e81c2d3cb42d"},{"version":"7f729540963914f308742abcebd1c93cf6634ca85e4024177705d811301a09ab","signature":"9033c48366f3826f3e362190c39da048e43e4fe22b27eb7b3f5550d47c41dfec"},"3de70454fdf1e9353eb3f80b94564dd39c15e5760867c4bcd32671eae7959f02"],"root":[[116,118]],"options":{"composite":true,"declaration":true,"esModuleInterop":true,"module":1,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"strict":true,"target":99},"referencedMap":[[115,1],[86,2],[95,2],[87,2],[96,2],[88,2],[89,2],[103,2],[102,2],[104,2],[105,2],[97,2],[90,2],[98,2],[91,2],[99,2],[92,2],[94,2],[101,2],[100,2],[106,2],[93,2],[107,2],[112,2],[113,2],[108,2],[110,2],[109,2],[111,2],[117,3],[118,4]],"latestChangedDtsFile":"./dist/auth.types.d.ts","version":"5.8.3"} \ No newline at end of file diff --git a/tsconfig.base.json b/tsconfig.base.json index 372bacf..0a4ae69 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ESNext", - "module": "ESNext", + "module": "CommonJS", "moduleResolution": "node", "strict": true, "esModuleInterop": true,