feat: auth

This commit is contained in:
2026-02-04 08:04:46 -08:00
parent 8f3aeb9391
commit e167a17bd9
12 changed files with 673 additions and 9 deletions
+53
View File
@@ -0,0 +1,53 @@
# Authentication Flow
## Overview
This API uses Discord OAuth for authentication and JWT tokens for session management. Only the admin user can perform create/update/delete operations, while public read access is available to everyone.
## Environment Variables
Set up your `prod.env` file with 1Password references:
- `DATABASE_URL` - MongoDB connection string
- `JWT_SECRET` - Secret for signing JWT tokens
- `DISCORD_CLIENT_ID` - Discord OAuth app client ID
- `DISCORD_CLIENT_SECRET` - Discord OAuth app client secret
- `ADMIN_DISCORD_ID` - Your Discord user ID for admin access
- `API_URL` - API base URL (e.g., http://localhost:3000)
- `FRONTEND_URL` - Frontend URL to redirect after login
## Running the API
```bash
# Start with 1Password secrets
op run --env-file=prod.env -- nx serve api
```
## Auth Endpoints
### 1. Login
`GET /api/auth/login` - Redirects to Discord OAuth
### 2. Callback
`GET /api/auth/callback` - Discord redirects here after auth
- Creates/updates user in database
- Generates JWT token
- Sets httpOnly cookie `auth-token`
- Redirects to frontend
### 3. Get Current User
`GET /api/auth/me` - Returns authenticated user (requires auth)
### 4. Logout
`POST /api/auth/logout` - Clears auth cookie
## Protected Routes
Example: Games API
- `GET /api/games` - Public (list all games)
- `GET /api/games/:id` - Public (get single game)
- `POST /api/games` - Admin only (create game)
- `PUT /api/games/:id` - Admin only (update game)
- `DELETE /api/games/:id` - Admin only (delete game)
## Testing
1. Set up Discord OAuth app at https://discord.com/developers/applications
2. Add redirect URI: `http://localhost:3000/api/auth/callback`
3. Copy client ID and secret to 1Password
4. Run the API and visit `http://localhost:3000/api/auth/login`
5. After Discord auth, you'll be redirected to frontend with auth cookie set
+11
View File
@@ -80,3 +80,14 @@ enum MusicStatus {
COMPLETED COMPLETED
WANT_TO_LISTEN WANT_TO_LISTEN
} }
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
discordId String @unique
username String
email String @unique
avatar String?
isAdmin Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
+16
View File
@@ -0,0 +1,16 @@
# Database
DATABASE_URL=op://Personal/MongoDB Atlas - Library/connection string
# JWT Secret
JWT_SECRET=op://Personal/Library API Secrets/jwt_secret
# Discord OAuth
DISCORD_CLIENT_ID=op://Personal/Library Discord OAuth/client_id
DISCORD_CLIENT_SECRET=op://Personal/Library Discord OAuth/client_secret
# Admin Configuration
ADMIN_DISCORD_ID=op://Personal/Library API Secrets/admin_discord_id
# API Configuration
API_URL=op://Personal/Library API Secrets/api_url
FRONTEND_URL=op://Personal/Library API Secrets/frontend_url
+21
View File
@@ -0,0 +1,21 @@
/**
* @copyright 2026 NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { FastifyReply, FastifyRequest } from "fastify";
/**
* Middleware to check if the authenticated user is an admin.
* Must be used after app.authenticate.
*/
export async function adminGuard(
request: FastifyRequest,
reply: FastifyReply
): Promise<void> {
const user = request.user as any;
if (!user || !user.isAdmin) {
return reply.code(403).send({ error: "Forbidden: Admin access required" });
}
}
+54
View File
@@ -0,0 +1,54 @@
import { FastifyPluginAsync, FastifyRequest } from "fastify";
import fastifyPlugin from "fastify-plugin";
import fastifyJwt from "@fastify/jwt";
import fastifyCookie from "@fastify/cookie";
import fastifyOauth2 from "@fastify/oauth2";
declare module "fastify" {
interface FastifyInstance {
authenticate: (request: FastifyRequest) => Promise<void>;
oauth2Discord: any;
}
interface FastifyRequest {
user?: any;
}
}
const authPlugin: FastifyPluginAsync = async (app) => {
// Register JWT plugin
app.register(fastifyJwt, {
secret: process.env.JWT_SECRET || "your-secret-key",
cookie: {
cookieName: "auth-token",
signed: false,
},
});
// Register cookie plugin
app.register(fastifyCookie);
// Register Discord OAuth2
app.register(fastifyOauth2, {
name: "oauth2Discord",
credentials: {
client: {
id: process.env.DISCORD_CLIENT_ID || "",
secret: process.env.DISCORD_CLIENT_SECRET || "",
},
auth: fastifyOauth2.DISCORD_CONFIGURATION,
},
startRedirectPath: "/api/auth/login",
callbackUri: `${process.env.API_URL || "http://localhost:3000"}/api/auth/callback`,
});
// Authentication decorator
app.decorate("authenticate", async (request: FastifyRequest) => {
try {
await request.jwtVerify();
} catch (err) {
throw app.httpErrors.unauthorized("Invalid token");
}
});
};
export default fastifyPlugin(authPlugin);
+6
View File
@@ -1,3 +1,9 @@
/**
* @copyright 2026 NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { FastifyInstance } from 'fastify'; import { FastifyInstance } from 'fastify';
import fp from 'fastify-plugin'; import fp from 'fastify-plugin';
import sensible from '@fastify/sensible'; import sensible from '@fastify/sensible';
+86
View File
@@ -0,0 +1,86 @@
import { FastifyPluginAsync } from "fastify";
import { AuthService } from "../services/auth.service";
import { AuthResponse } from "@library/shared-types";
const authRoutes: FastifyPluginAsync = async (app) => {
const authService = new AuthService(app);
/**
* Initiate Discord OAuth login.
*/
app.get("/login", async (request, reply) => {
const authUrl = app.oauth2Discord.generateAuthorizationUri({
scope: ["identify", "email"],
});
return reply.redirect(authUrl);
});
/**
* Discord OAuth callback.
*/
app.get("/callback", async (request, reply) => {
try {
const token = await app.oauth2Discord.getAccessTokenFromAuthorizationCodeFlow(
request
);
// Get user data from Discord
const userData = await app.oauth2Discord.userinfo(token.access_token);
// Create or update user in database
const user = await authService.createOrUpdateUserFromDiscord(userData);
// Generate JWT
const jwt = await authService.generateToken(user);
// Set cookie and redirect to frontend
reply
.setCookie("auth-token", jwt, {
path: "/",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 7 * 24 * 60 * 60, // 7 days
})
.redirect(process.env.FRONTEND_URL || "http://localhost:4200");
} catch (error) {
app.log.error(error);
reply
.code(401)
.send({ error: "Authentication failed" });
}
});
/**
* Get current user.
*/
app.get<{ Reply: AuthResponse | { error: string } }>(
"/me",
{
preValidation: [app.authenticate],
},
async (request) => {
const user = request.user as any;
const token = await authService.generateToken(user);
return {
user,
accessToken: token,
};
}
);
/**
* Logout.
*/
app.post("/logout", async (request, reply) => {
reply
.clearCookie("auth-token", {
path: "/",
})
.send({ message: "Logged out successfully" });
});
};
export default authRoutes;
+109
View File
@@ -0,0 +1,109 @@
/**
* @copyright 2026 NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { FastifyPluginAsync } from "fastify";
import { PrismaClient } from "../../../generated/prisma";
import { Game, GameStatus } from "@library/shared-types";
import { adminGuard } from "../../middleware/admin-guard";
const gamesRoutes: FastifyPluginAsync = async (app) => {
const prisma = new PrismaClient();
// Get all games (public route)
app.get<{ Reply: Game[] }>("/", async () => {
const games = await prisma.game.findMany({
orderBy: { updatedAt: "desc" },
});
return games.map((game) => ({
...game,
status: game.status.toLowerCase() as GameStatus,
}));
});
// Get single game (public route)
app.get<{ Params: { id: string }; Reply: Game | null }>(
"/:id",
async (request) => {
const { id } = request.params;
const game = await prisma.game.findUnique({
where: { id },
});
if (!game) return null;
return {
...game,
status: game.status.toLowerCase() as GameStatus,
};
}
);
// Create game (protected admin route)
app.post<{ Body: Omit<Game, "id" | "createdAt" | "updatedAt">; Reply: Game }>(
"/",
{
preValidation: [app.authenticate, adminGuard],
},
async (request, reply) => {
const game = await prisma.game.create({
data: {
...request.body,
status: request.body.status.toUpperCase() as any,
},
});
return {
...game,
status: game.status.toLowerCase() as GameStatus,
};
}
);
// Update game (protected admin route)
app.put<{
Params: { id: string };
Body: Partial<Omit<Game, "id" | "createdAt" | "updatedAt">>;
Reply: Game | null;
}>(
"/:id",
{
preValidation: [app.authenticate, adminGuard],
},
async (request, reply) => {
const { id } = request.params;
const updateData = { ...request.body };
if (updateData.status) {
updateData.status = updateData.status.toUpperCase() as any;
}
const game = await prisma.game.update({
where: { id },
data: updateData,
});
return {
...game,
status: game.status.toLowerCase() as GameStatus,
};
}
);
// Delete game (protected admin route)
app.delete<{ Params: { id: string }; Reply: { success: boolean } }>(
"/:id",
{
preValidation: [app.authenticate, adminGuard],
},
async (request, reply) => {
const { id } = request.params;
await prisma.game.delete({
where: { id },
});
return { success: true };
}
);
};
export default gamesRoutes;
+78
View File
@@ -0,0 +1,78 @@
import { FastifyInstance } from "fastify";
import { JwtPayload, User } from "@library/shared-types";
import { PrismaClient } from "../../generated/prisma";
export class AuthService {
private prisma: PrismaClient;
constructor(private readonly app: FastifyInstance) {
this.prisma = new PrismaClient();
}
/**
* Generate JWT token for user.
*/
async generateToken(user: User): Promise<string> {
const payload: JwtPayload = {
sub: user.id,
email: user.email,
username: user.username,
isAdmin: user.isAdmin,
};
return this.app.jwt.sign(payload, {
expiresIn: "7d",
});
}
/**
* Verify JWT token.
*/
async verifyToken(token: string): Promise<JwtPayload> {
return this.app.jwt.verify(token) as JwtPayload;
}
/**
* Create or update user from Discord OAuth data.
*/
async createOrUpdateUserFromDiscord(discordData: DiscordUser): Promise<User> {
const avatarUrl = discordData.avatar
? `https://cdn.discordapp.com/avatars/${discordData.id}/${discordData.avatar}.png`
: undefined;
// Upsert user in database
const dbUser = await this.prisma.user.upsert({
where: {
discordId: discordData.id,
},
create: {
discordId: discordData.id,
username: discordData.username,
email: discordData.email,
avatar: avatarUrl,
isAdmin: discordData.id === process.env.ADMIN_DISCORD_ID,
},
update: {
username: discordData.username,
email: discordData.email,
avatar: avatarUrl,
},
});
return {
id: dbUser.id,
discordId: dbUser.discordId,
username: dbUser.username,
email: dbUser.email,
avatarUrl: dbUser.avatar || undefined,
isAdmin: dbUser.isAdmin,
};
}
}
interface DiscordUser {
id: string;
username: string;
email: string;
avatar?: string;
}
+5 -1
View File
@@ -16,7 +16,10 @@
"@angular/platform-browser": "21.1.2", "@angular/platform-browser": "21.1.2",
"@angular/router": "21.1.2", "@angular/router": "21.1.2",
"@fastify/autoload": "6.0.3", "@fastify/autoload": "6.0.3",
"@fastify/sensible": "6.0.4", "@fastify/cookie": "^11.0.2",
"@fastify/jwt": "^10.0.0",
"@fastify/oauth2": "^8.1.2",
"@fastify/sensible": "5.6.0",
"@prisma/client": "7.3.0", "@prisma/client": "7.3.0",
"fastify": "5.2.2", "fastify": "5.2.2",
"fastify-plugin": "5.0.1", "fastify-plugin": "5.0.1",
@@ -46,6 +49,7 @@
"@swc/core": "1.5.29", "@swc/core": "1.5.29",
"@swc/helpers": "0.5.18", "@swc/helpers": "0.5.18",
"@types/jest": "30.0.0", "@types/jest": "30.0.0",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "20.19.9", "@types/node": "20.19.9",
"@typescript-eslint/utils": "8.54.0", "@typescript-eslint/utils": "8.54.0",
"angular-eslint": "21.1.0", "angular-eslint": "21.1.0",
+232 -8
View File
@@ -29,9 +29,18 @@ importers:
'@fastify/autoload': '@fastify/autoload':
specifier: 6.0.3 specifier: 6.0.3
version: 6.0.3 version: 6.0.3
'@fastify/cookie':
specifier: ^11.0.2
version: 11.0.2
'@fastify/jwt':
specifier: ^10.0.0
version: 10.0.0
'@fastify/oauth2':
specifier: ^8.1.2
version: 8.1.2
'@fastify/sensible': '@fastify/sensible':
specifier: 6.0.4 specifier: 5.6.0
version: 6.0.4 version: 5.6.0
'@prisma/client': '@prisma/client':
specifier: 7.3.0 specifier: 7.3.0
version: 7.3.0(prisma@7.3.0(@types/react@19.2.10)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) version: 7.3.0(prisma@7.3.0(@types/react@19.2.10)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)
@@ -114,6 +123,9 @@ importers:
'@types/jest': '@types/jest':
specifier: 30.0.0 specifier: 30.0.0
version: 30.0.0 version: 30.0.0
'@types/jsonwebtoken':
specifier: ^9.0.10
version: 9.0.10
'@types/node': '@types/node':
specifier: 20.19.9 specifier: 20.19.9
version: 20.19.9 version: 20.19.9
@@ -1537,6 +1549,9 @@ packages:
'@fastify/autoload@6.0.3': '@fastify/autoload@6.0.3':
resolution: {integrity: sha512-/wM2pmI7jP2fGah3YuP14i9vuaijpD4gQYLiyd+eD7gUxpA3B4R6/0QIXQS2eJaYD9aIX4ZFRzPmrZsaetfcWw==} resolution: {integrity: sha512-/wM2pmI7jP2fGah3YuP14i9vuaijpD4gQYLiyd+eD7gUxpA3B4R6/0QIXQS2eJaYD9aIX4ZFRzPmrZsaetfcWw==}
'@fastify/cookie@11.0.2':
resolution: {integrity: sha512-GWdwdGlgJxyvNv+QcKiGNevSspMQXncjMZ1J8IvuDQk0jvkzgWWZFNC2En3s+nHndZBGV8IbLwOI/sxCZw/mzA==}
'@fastify/error@4.2.0': '@fastify/error@4.2.0':
resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==} resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
@@ -1546,14 +1561,38 @@ packages:
'@fastify/forwarded@3.0.1': '@fastify/forwarded@3.0.1':
resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==} resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==}
'@fastify/jwt@10.0.0':
resolution: {integrity: sha512-2Qka3NiyNNcsfejMUvyzot1T4UYIzzcbkFGDdVyrl344fRZ/WkD6VFXOoXhxe2Pzf3LpJNkoSxUM4Ru4DVgkYA==}
'@fastify/merge-json-schemas@0.2.1': '@fastify/merge-json-schemas@0.2.1':
resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==}
'@fastify/oauth2@8.1.2':
resolution: {integrity: sha512-XZWFRWTZE2fkZ2pjuHNGtpFn1tOFgcJbU0205kHbfd16dn9xRc/6HmG0gHtN/g/BNkEL3EsQ54+pYEdh8dnBgA==}
'@fastify/proxy-addr@5.1.0': '@fastify/proxy-addr@5.1.0':
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==} resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
'@fastify/sensible@6.0.4': '@fastify/sensible@5.6.0':
resolution: {integrity: sha512-1vxcCUlPMew6WroK8fq+LVOwbsLtX+lmuRuqpcp6eYqu6vmkLwbKTdBWAZwbeaSgCfW4tzUpTIHLLvTiQQ1BwQ==} resolution: {integrity: sha512-Vq6Z2ZQy10GDqON+hvLF52K99s9et5gVVxTul5n3SIAf0Kq5QjPRUKkAMT3zPAiiGvoHtS3APa/3uaxfDgCODQ==}
'@hapi/boom@10.0.1':
resolution: {integrity: sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==}
'@hapi/bourne@3.0.0':
resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
'@hapi/hoek@11.0.7':
resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==}
'@hapi/hoek@9.3.0':
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
'@hapi/topo@5.1.0':
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
'@hapi/wreck@18.1.0':
resolution: {integrity: sha512-0z6ZRCmFEfV/MQqkQomJ7sl/hyxvcZM7LtuVqN3vdAO4vM9eBbowl0kaqQj9EJJQab+3Uuh1GxbGIBFy4NfJ4w==}
'@hono/node-server@1.19.9': '@hono/node-server@1.19.9':
resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==}
@@ -3050,6 +3089,15 @@ packages:
resolution: {integrity: sha512-kxwxhCIUrj7DfzEtDSs/pi/w+aII/WQLpPfLgoQCWE8/95v60WnTfd1afmsXsFoxikKPxkwoPWtU2YbhSoX9MQ==} resolution: {integrity: sha512-kxwxhCIUrj7DfzEtDSs/pi/w+aII/WQLpPfLgoQCWE8/95v60WnTfd1afmsXsFoxikKPxkwoPWtU2YbhSoX9MQ==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
'@sideway/address@4.1.5':
resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
'@sideway/formula@3.0.1':
resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==}
'@sideway/pinpoint@2.0.0':
resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
'@sigstore/bundle@4.0.0': '@sigstore/bundle@4.0.0':
resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==} resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==}
engines: {node: ^20.17.0 || >=22.9.0} engines: {node: ^20.17.0 || >=22.9.0}
@@ -3294,9 +3342,15 @@ packages:
'@types/json5@0.0.29': '@types/json5@0.0.29':
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
'@types/jsonwebtoken@9.0.10':
resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==}
'@types/mime@1.3.5': '@types/mime@1.3.5':
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
'@types/node-forge@1.3.14': '@types/node-forge@1.3.14':
resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==}
@@ -3912,6 +3966,9 @@ packages:
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
asn1.js@5.4.1:
resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
asn1@0.2.6: asn1@0.2.6:
resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
@@ -4104,6 +4161,9 @@ packages:
bluebird@3.7.2: bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
bn.js@4.12.2:
resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==}
body-parser@1.20.4: body-parser@1.20.4:
resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@@ -4835,6 +4895,9 @@ packages:
ecc-jsbn@0.1.2: ecc-jsbn@0.1.2:
resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
ee-first@1.1.1: ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
@@ -5251,6 +5314,10 @@ packages:
fast-json-stringify@6.2.0: fast-json-stringify@6.2.0:
resolution: {integrity: sha512-Eaf/KNIDwHkzfyeQFNfLXJnQ7cl1XQI3+zRqmPlvtkMigbXnAcasTrvJQmquBSxKfFGeRA6PFog8t+hFmpDoWw==} resolution: {integrity: sha512-Eaf/KNIDwHkzfyeQFNfLXJnQ7cl1XQI3+zRqmPlvtkMigbXnAcasTrvJQmquBSxKfFGeRA6PFog8t+hFmpDoWw==}
fast-jwt@6.1.0:
resolution: {integrity: sha512-cGK/TXlud8INL49Iv7yRtZy0PHzNJId1shfqNCqdF0gOlWiy+1FPgjxX+ZHp/CYxFYDaoNnxeYEGzcXSkahUEQ==}
engines: {node: '>=20'}
fast-levenshtein@2.0.6: fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
@@ -5260,15 +5327,28 @@ packages:
fast-uri@3.1.0: fast-uri@3.1.0:
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
fastfall@1.5.1:
resolution: {integrity: sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==}
engines: {node: '>=0.10.0'}
fastify-plugin@4.5.1:
resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==}
fastify-plugin@5.0.1: fastify-plugin@5.0.1:
resolution: {integrity: sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==} resolution: {integrity: sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==}
fastify@5.2.2: fastify@5.2.2:
resolution: {integrity: sha512-22T/PnhquWozuFXg3Ish4md5ipsF1Nx1mJ9ulLdZPXSk14WFj/wMlyNB/yll9sQOojKRgOIxT2inK3Xpjg5hyw==} resolution: {integrity: sha512-22T/PnhquWozuFXg3Ish4md5ipsF1Nx1mJ9ulLdZPXSk14WFj/wMlyNB/yll9sQOojKRgOIxT2inK3Xpjg5hyw==}
fastparallel@2.4.1:
resolution: {integrity: sha512-qUmhxPgNHmvRjZKBFUNI0oZuuH9OlSIOXmJ98lhKPxMZZ7zS/Fi0wRHOihDSz0R1YiIOjxzOY4bq65YTcdBi2Q==}
fastq@1.20.1: fastq@1.20.1:
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
fastseries@1.7.2:
resolution: {integrity: sha512-dTPFrPGS8SNSzAt7u/CbMKCJ3s01N04s4JFbORHcmyvVfVKmbhMD1VtRbh5enGHxkaQDqWyLefiKOGGmohGDDQ==}
faye-websocket@0.11.4: faye-websocket@0.11.4:
resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==}
engines: {node: '>=0.8.0'} engines: {node: '>=0.8.0'}
@@ -6263,6 +6343,9 @@ packages:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true hasBin: true
joi@17.13.3:
resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==}
jose@6.1.3: jose@6.1.3:
resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==}
@@ -6742,6 +6825,9 @@ packages:
resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
engines: {node: '>= 18'} engines: {node: '>= 18'}
mnemonist@0.40.3:
resolution: {integrity: sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==}
mrmime@2.0.1: mrmime@2.0.1:
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -6954,6 +7040,9 @@ packages:
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
obliterator@2.0.5:
resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==}
obuf@1.1.2: obuf@1.1.2:
resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
@@ -8109,6 +8198,9 @@ packages:
resolution: {integrity: sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==} resolution: {integrity: sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==}
engines: {node: ^20.17.0 || >=22.9.0} engines: {node: ^20.17.0 || >=22.9.0}
simple-oauth2@5.1.0:
resolution: {integrity: sha512-gWDa38Ccm4MwlG5U7AlcJxPv3lvr80dU7ARJWrGdgvOKyzSj1gr3GBPN1rABTedAYvC/LsGYoFuFxwDBPtGEbw==}
slash@3.0.0: slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -8247,6 +8339,9 @@ packages:
resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
steed@1.1.3:
resolution: {integrity: sha512-EUkci0FAUiE4IvGTSKcDJIQ/eRUP2JJb56+fvZ4sdnguLTqIdKjSxUe138poW8mkvKWXW2sFPrgTsxqoISnmoA==}
stop-iteration-iterator@1.1.0: stop-iteration-iterator@1.1.0:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -9088,6 +9183,10 @@ packages:
resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==}
engines: {node: '>=20'} engines: {node: '>=20'}
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
y18n@5.0.8: y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -11317,6 +11416,11 @@ snapshots:
'@fastify/autoload@6.0.3': {} '@fastify/autoload@6.0.3': {}
'@fastify/cookie@11.0.2':
dependencies:
cookie: 1.1.1
fastify-plugin: 5.0.1
'@fastify/error@4.2.0': {} '@fastify/error@4.2.0': {}
'@fastify/fast-json-stringify-compiler@5.0.3': '@fastify/fast-json-stringify-compiler@5.0.3':
@@ -11325,25 +11429,61 @@ snapshots:
'@fastify/forwarded@3.0.1': {} '@fastify/forwarded@3.0.1': {}
'@fastify/jwt@10.0.0':
dependencies:
'@fastify/error': 4.2.0
'@lukeed/ms': 2.0.2
fast-jwt: 6.1.0
fastify-plugin: 5.0.1
steed: 1.1.3
'@fastify/merge-json-schemas@0.2.1': '@fastify/merge-json-schemas@0.2.1':
dependencies: dependencies:
dequal: 2.0.3 dequal: 2.0.3
'@fastify/oauth2@8.1.2':
dependencies:
'@fastify/cookie': 11.0.2
fastify-plugin: 5.0.1
simple-oauth2: 5.1.0
transitivePeerDependencies:
- supports-color
'@fastify/proxy-addr@5.1.0': '@fastify/proxy-addr@5.1.0':
dependencies: dependencies:
'@fastify/forwarded': 3.0.1 '@fastify/forwarded': 3.0.1
ipaddr.js: 2.3.0 ipaddr.js: 2.3.0
'@fastify/sensible@6.0.4': '@fastify/sensible@5.6.0':
dependencies: dependencies:
'@lukeed/ms': 2.0.2 '@lukeed/ms': 2.0.2
dequal: 2.0.3 fast-deep-equal: 3.1.3
fastify-plugin: 5.0.1 fastify-plugin: 4.5.1
forwarded: 0.2.0 forwarded: 0.2.0
http-errors: 2.0.1 http-errors: 2.0.1
type-is: 2.0.1 type-is: 1.6.18
vary: 1.1.2 vary: 1.1.2
'@hapi/boom@10.0.1':
dependencies:
'@hapi/hoek': 11.0.7
'@hapi/bourne@3.0.0': {}
'@hapi/hoek@11.0.7': {}
'@hapi/hoek@9.3.0': {}
'@hapi/topo@5.1.0':
dependencies:
'@hapi/hoek': 9.3.0
'@hapi/wreck@18.1.0':
dependencies:
'@hapi/boom': 10.0.1
'@hapi/bourne': 3.0.0
'@hapi/hoek': 11.0.7
'@hono/node-server@1.19.9(hono@4.11.4)': '@hono/node-server@1.19.9(hono@4.11.4)':
dependencies: dependencies:
hono: 4.11.4 hono: 4.11.4
@@ -13437,6 +13577,14 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- chokidar - chokidar
'@sideway/address@4.1.5':
dependencies:
'@hapi/hoek': 9.3.0
'@sideway/formula@3.0.1': {}
'@sideway/pinpoint@2.0.0': {}
'@sigstore/bundle@4.0.0': '@sigstore/bundle@4.0.0':
dependencies: dependencies:
'@sigstore/protobuf-specs': 0.5.0 '@sigstore/protobuf-specs': 0.5.0
@@ -13705,8 +13853,15 @@ snapshots:
'@types/json5@0.0.29': {} '@types/json5@0.0.29': {}
'@types/jsonwebtoken@9.0.10':
dependencies:
'@types/ms': 2.1.0
'@types/node': 20.19.9
'@types/mime@1.3.5': {} '@types/mime@1.3.5': {}
'@types/ms@2.1.0': {}
'@types/node-forge@1.3.14': '@types/node-forge@1.3.14':
dependencies: dependencies:
'@types/node': 20.19.9 '@types/node': 20.19.9
@@ -14419,6 +14574,13 @@ snapshots:
get-intrinsic: 1.3.0 get-intrinsic: 1.3.0
is-array-buffer: 3.0.5 is-array-buffer: 3.0.5
asn1.js@5.4.1:
dependencies:
bn.js: 4.12.2
inherits: 2.0.4
minimalistic-assert: 1.0.1
safer-buffer: 2.1.2
asn1@0.2.6: asn1@0.2.6:
dependencies: dependencies:
safer-buffer: 2.1.2 safer-buffer: 2.1.2
@@ -14672,6 +14834,8 @@ snapshots:
bluebird@3.7.2: {} bluebird@3.7.2: {}
bn.js@4.12.2: {}
body-parser@1.20.4: body-parser@1.20.4:
dependencies: dependencies:
bytes: 3.1.2 bytes: 3.1.2
@@ -15445,6 +15609,10 @@ snapshots:
jsbn: 0.1.1 jsbn: 0.1.1
safer-buffer: 2.1.2 safer-buffer: 2.1.2
ecdsa-sig-formatter@1.0.11:
dependencies:
safe-buffer: 5.2.1
ee-first@1.1.1: {} ee-first@1.1.1: {}
effect@3.18.4: effect@3.18.4:
@@ -16105,6 +16273,13 @@ snapshots:
json-schema-ref-resolver: 3.0.0 json-schema-ref-resolver: 3.0.0
rfdc: 1.4.1 rfdc: 1.4.1
fast-jwt@6.1.0:
dependencies:
'@lukeed/ms': 2.0.2
asn1.js: 5.4.1
ecdsa-sig-formatter: 1.0.11
mnemonist: 0.40.3
fast-levenshtein@2.0.6: {} fast-levenshtein@2.0.6: {}
fast-querystring@1.1.2: fast-querystring@1.1.2:
@@ -16113,6 +16288,12 @@ snapshots:
fast-uri@3.1.0: {} fast-uri@3.1.0: {}
fastfall@1.5.1:
dependencies:
reusify: 1.1.0
fastify-plugin@4.5.1: {}
fastify-plugin@5.0.1: {} fastify-plugin@5.0.1: {}
fastify@5.2.2: fastify@5.2.2:
@@ -16133,10 +16314,20 @@ snapshots:
semver: 7.7.3 semver: 7.7.3
toad-cache: 3.7.0 toad-cache: 3.7.0
fastparallel@2.4.1:
dependencies:
reusify: 1.1.0
xtend: 4.0.2
fastq@1.20.1: fastq@1.20.1:
dependencies: dependencies:
reusify: 1.1.0 reusify: 1.1.0
fastseries@1.7.2:
dependencies:
reusify: 1.1.0
xtend: 4.0.2
faye-websocket@0.11.4: faye-websocket@0.11.4:
dependencies: dependencies:
websocket-driver: 0.7.4 websocket-driver: 0.7.4
@@ -17349,6 +17540,14 @@ snapshots:
jiti@2.6.1: {} jiti@2.6.1: {}
joi@17.13.3:
dependencies:
'@hapi/hoek': 9.3.0
'@hapi/topo': 5.1.0
'@sideway/address': 4.1.5
'@sideway/formula': 3.0.1
'@sideway/pinpoint': 2.0.0
jose@6.1.3: {} jose@6.1.3: {}
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
@@ -17860,6 +18059,10 @@ snapshots:
dependencies: dependencies:
minipass: 7.1.2 minipass: 7.1.2
mnemonist@0.40.3:
dependencies:
obliterator: 2.0.5
mrmime@2.0.1: {} mrmime@2.0.1: {}
ms@2.0.0: {} ms@2.0.0: {}
@@ -18142,6 +18345,8 @@ snapshots:
define-properties: 1.2.1 define-properties: 1.2.1
es-object-atoms: 1.1.1 es-object-atoms: 1.1.1
obliterator@2.0.5: {}
obuf@1.1.2: {} obuf@1.1.2: {}
obug@2.1.1: {} obug@2.1.1: {}
@@ -19403,6 +19608,15 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
simple-oauth2@5.1.0:
dependencies:
'@hapi/hoek': 11.0.7
'@hapi/wreck': 18.1.0
debug: 4.4.3(supports-color@8.1.1)
joi: 17.13.3
transitivePeerDependencies:
- supports-color
slash@3.0.0: {} slash@3.0.0: {}
slash@4.0.0: {} slash@4.0.0: {}
@@ -19564,6 +19778,14 @@ snapshots:
stdin-discarder@0.2.2: {} stdin-discarder@0.2.2: {}
steed@1.1.3:
dependencies:
fastfall: 1.5.1
fastparallel: 2.4.1
fastq: 1.20.1
fastseries: 1.7.2
reusify: 1.1.0
stop-iteration-iterator@1.1.0: stop-iteration-iterator@1.1.0:
dependencies: dependencies:
es-errors: 1.3.0 es-errors: 1.3.0
@@ -20579,6 +20801,8 @@ snapshots:
is-wsl: 3.1.0 is-wsl: 3.1.0
powershell-utils: 0.1.0 powershell-utils: 0.1.0
xtend@4.0.2: {}
y18n@5.0.8: {} y18n@5.0.8: {}
yallist@3.1.1: {} yallist@3.1.1: {}
+2
View File
@@ -10,6 +10,7 @@ export interface User {
username: string; username: string;
avatarUrl?: string; avatarUrl?: string;
discordId: string; discordId: string;
isAdmin: boolean;
} }
export interface JwtPayload { export interface JwtPayload {
@@ -19,6 +20,7 @@ export interface JwtPayload {
sub: string; sub: string;
email: string; email: string;
username: string; username: string;
isAdmin: boolean;
iat?: number; iat?: number;
exp?: number; exp?: number;
} }