From ef473d6084ce78ca3b9ed7789443b98b71850023 Mon Sep 17 00:00:00 2001 From: hurole <1192163814@qq.com> Date: Thu, 4 Sep 2025 23:19:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E5=92=8C=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复路由配置,实现根路径自动重定向到/project - 新增Gitea OAuth认证系统和相关组件 - 完善日志系统实现,包含pino日志工具和中间件 - 重构页面结构,分离项目管理和环境管理页面 - 新增CORS、Session等关键中间件 - 优化前端请求封装和类型定义 - 修复TypeScript类型错误和参数传递问题 --- apps/server/app.ts | 7 +- apps/server/controllers/application.ts | 30 +- apps/server/controllers/auth.ts | 66 +++ apps/server/libs/gitea.ts | 94 ++++ apps/server/libs/logger.ts | 38 ++ apps/server/libs/route-scanner.ts | 7 +- apps/server/middlewares/cors.ts | 17 + apps/server/middlewares/exception.ts | 49 +- apps/server/middlewares/index.ts | 22 +- apps/server/middlewares/logger.ts | 14 + apps/server/middlewares/responseTime.ts | 13 - apps/server/middlewares/router.ts | 38 +- apps/server/middlewares/session.ts | 24 + apps/server/package.json | 7 +- apps/server/prisma/data/dev.db | Bin 16384 -> 20480 bytes apps/server/prisma/schema.prisma | 14 + apps/web/.env | 1 + apps/web/package.json | 5 +- apps/web/rsbuild.config.ts | 18 +- apps/web/src/assets/images/gitea.svg | 31 ++ apps/web/src/index.tsx | 9 +- apps/web/src/pages/App.tsx | 9 +- apps/web/src/pages/application/index.tsx | 12 - apps/web/src/pages/env/index.tsx | 12 + apps/web/src/pages/home/index.tsx | 26 +- apps/web/src/pages/login/index.tsx | 47 +- apps/web/src/pages/login/service.ts | 39 ++ apps/web/src/pages/login/types.ts | 5 + apps/web/src/pages/project/index.tsx | 8 + .../pages/{application => project}/types.ts | 6 +- apps/web/src/shared/index.ts | 1 + apps/web/src/shared/request.ts | 26 + apps/web/tsconfig.json | 3 +- pnpm-lock.yaml | 520 ++++++++++++++++-- 34 files changed, 1022 insertions(+), 196 deletions(-) create mode 100644 apps/server/controllers/auth.ts create mode 100644 apps/server/libs/gitea.ts create mode 100644 apps/server/libs/logger.ts create mode 100644 apps/server/middlewares/cors.ts create mode 100644 apps/server/middlewares/logger.ts delete mode 100644 apps/server/middlewares/responseTime.ts create mode 100644 apps/server/middlewares/session.ts create mode 100644 apps/web/.env create mode 100644 apps/web/src/assets/images/gitea.svg delete mode 100644 apps/web/src/pages/application/index.tsx create mode 100644 apps/web/src/pages/env/index.tsx create mode 100644 apps/web/src/pages/login/service.ts create mode 100644 apps/web/src/pages/login/types.ts create mode 100644 apps/web/src/pages/project/index.tsx rename apps/web/src/pages/{application => project}/types.ts (73%) create mode 100644 apps/web/src/shared/index.ts create mode 100644 apps/web/src/shared/request.ts diff --git a/apps/server/app.ts b/apps/server/app.ts index 80be139..7adb510 100644 --- a/apps/server/app.ts +++ b/apps/server/app.ts @@ -1,10 +1,13 @@ import Koa from 'koa'; import { initMiddlewares } from './middlewares/index.ts'; +import { log } from './libs/logger.ts'; const app = new Koa(); initMiddlewares(app); -app.listen(3000, () => { - console.log('server started at http://localhost:3000'); +const PORT = process.env.PORT || 3001; + +app.listen(PORT, () => { + log.info('APP', 'Server started at port %d', PORT); }); diff --git a/apps/server/controllers/application.ts b/apps/server/controllers/application.ts index 0c0afa6..5d007f0 100644 --- a/apps/server/controllers/application.ts +++ b/apps/server/controllers/application.ts @@ -1,5 +1,6 @@ import type { Context } from 'koa'; import prisma from '../libs/db.ts'; +import { log } from '../libs/logger.ts'; import { BusinessError } from '../middlewares/exception.ts'; import { Controller, Get } from '../decorators/route.ts'; @@ -7,6 +8,7 @@ import { Controller, Get } from '../decorators/route.ts'; export class ApplicationController { @Get('/list') async list(ctx: Context) { + log.debug('app', 'session %o', ctx.session); try { const list = await prisma.application.findMany({ where: { @@ -24,27 +26,15 @@ export class ApplicationController { @Get('/detail/:id') async detail(ctx: Context) { - try { - const { id } = ctx.params; - const app = await prisma.application.findUnique({ - where: { id: Number(id) }, - }); + const { id } = ctx.params; + const app = await prisma.application.findUnique({ + where: { id: Number(id) }, + }); - if (!app) { - throw new BusinessError('应用不存在', 1002, 404); - } - - return app; - } catch (error) { - if (error instanceof BusinessError) { - throw error; - } - throw new BusinessError('获取应用详情失败', 1003, 500); + if (!app) { + throw new BusinessError('应用不存在', 1002, 404); } + + return app; } } - -// 保持向后兼容的导出方式 -const applicationController = new ApplicationController(); -export const list = applicationController.list.bind(applicationController); -export const detail = applicationController.detail.bind(applicationController); diff --git a/apps/server/controllers/auth.ts b/apps/server/controllers/auth.ts new file mode 100644 index 0000000..04bab36 --- /dev/null +++ b/apps/server/controllers/auth.ts @@ -0,0 +1,66 @@ +import type { Context } from 'koa'; +import { Controller, Get, Post } from '../decorators/route.ts'; +import prisma from '../libs/db.ts'; +import { log } from '../libs/logger.ts'; +import { gitea } from '../libs/gitea.ts'; + +@Controller('/auth') +export class AuthController { + private readonly TAG = 'Auth'; + + @Get('/url') + async url() { + return { + url: `${process.env.GITEA_URL}/login/oauth/authorize?client_id=${process.env.GITEA_CLIENT_ID}&redirect_uri=${process.env.GITEA_REDIRECT_URI}&response_type=code&state=STATE`, + }; + } + + @Post('/login') + async login(ctx: Context) { + if (!ctx.session.isNew) { + return ctx.session.user; + } + const { code } = (ctx.request as any).body; + const { access_token } = await gitea.getToken(code); + const giteaUser = await gitea.getUserInfo(access_token); + log.debug(this.TAG, 'gitea user: %o', giteaUser); + const exist = await prisma.user.findFirst({ + where: { + login: giteaUser.login, + email: giteaUser.email, + }, + }); + if (exist == null) { + const createdUser = await prisma.user.create({ + data: { + id: giteaUser.id, + login: giteaUser.login, + email: giteaUser.email, + username: giteaUser.username, + avatar_url: giteaUser.avatar_url, + active: giteaUser.active, + createdAt: giteaUser.created, + }, + }); + log.debug(this.TAG, '新建用户成功 %o', createdUser); + ctx.session.user = createdUser; + } else { + const updatedUser = await prisma.user.update({ + where: { + id: exist.id, + }, + data: { + login: giteaUser.login, + email: giteaUser.email, + username: giteaUser.username, + avatar_url: giteaUser.avatar_url, + active: giteaUser.active, + createdAt: giteaUser.created, + }, + }); + log.debug(this.TAG, '更新用户信息成功 %o', updatedUser); + ctx.session.user = updatedUser; + } + return ctx.session.user; + } +} diff --git a/apps/server/libs/gitea.ts b/apps/server/libs/gitea.ts new file mode 100644 index 0000000..605132e --- /dev/null +++ b/apps/server/libs/gitea.ts @@ -0,0 +1,94 @@ +interface TokenResponse { + access_token: string; + token_type: string; + expires_in: number; + refresh_token: string; +} + +interface GiteaUser { + id: number; + login: string; + login_name: string; + source_id: number; + full_name: string; + email: string; + avatar_url: string; + html_url: string; + language: string; + is_admin: boolean; + last_login: string; + created: string; + restricted: boolean; + active: boolean; + prohibit_login: boolean; + location: string; + website: string; + description: string; + visibility: string; + followers_count: number; + following_count: number; + starred_repos_count: number; + username: string; +} + +class Gitea { + private get config() { + return { + giteaUrl: process.env.GITEA_URL!, + clientId: process.env.GITEA_CLIENT_ID!, + clientSecret: process.env.GITEA_CLIENT_SECRET!, + redirectUri: process.env.GITEA_REDIRECT_URI!, + } + } + + async getToken(code: string) { + const { giteaUrl, clientId, clientSecret, redirectUri } = this.config; + console.log('this.config', this.config); + const response = await fetch( + `${giteaUrl}/login/oauth/access_token`, + { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify({ + client_id: clientId, + client_secret: clientSecret, + code, + grant_type: 'authorization_code', + redirect_uri: redirectUri, + }), + }, + ); + if (!response.ok) { + console.log(await response.json()); + throw new Error(`Fetch failed: ${response.status}`); + } + return (await response.json()) as TokenResponse; + } + /** + * 获取用户信息 + * @param accessToken 访问令牌 + */ + async getUserInfo(accessToken: string) { + const response = await fetch(`${this.config.giteaUrl}/api/v1/user`, { + method: 'GET', + headers: this.getHeaders(accessToken), + }); + if (!response.ok) { + throw new Error(`Fetch failed: ${response.status}`); + } + const result = (await response.json()) as GiteaUser; + return result; + } + + private getHeaders(accessToken?: string) { + const headers: Record = { + 'Content-Type': 'application/json', + }; + if (accessToken) { + headers['Authorization'] = `token ${accessToken}`; + } + return headers; + } +} + +export const gitea = new Gitea(); diff --git a/apps/server/libs/logger.ts b/apps/server/libs/logger.ts new file mode 100644 index 0000000..2e68c5a --- /dev/null +++ b/apps/server/libs/logger.ts @@ -0,0 +1,38 @@ +import pino from 'pino'; + +class Logger { + private readonly logger: pino.Logger; + + constructor() { + this.logger = pino({ + transport: { + target: 'pino-pretty', + options: { + singleLine: true, + colorize: true, + translateTime: 'SYS:standard', + ignore: 'pid,hostname', + }, + }, + level: 'debug', + }); + } + + debug(tag: string, message: string, ...args: unknown[]) { + if (args.length > 0) { + this.logger.debug({ TAG: tag }, message, ...(args as [])); + } else { + this.logger.debug({ TAG: tag }, message); + } + } + + info(tag: string, message: string, ...args: unknown[]) { + this.logger.info({ TAG: tag }, message, ...(args as [])); + } + + error(tag: string, message: string, ...args: unknown[]) { + this.logger.error({ TAG: tag }, message, ...(args as [])); + } +} + +export const log = new Logger(); diff --git a/apps/server/libs/route-scanner.ts b/apps/server/libs/route-scanner.ts index 052bc2a..ced774c 100644 --- a/apps/server/libs/route-scanner.ts +++ b/apps/server/libs/route-scanner.ts @@ -74,8 +74,6 @@ export class RouteScanner { default: console.warn(`未支持的HTTP方法: ${route.method}`); } - - console.log(`注册路由: ${route.method} ${fullPath} -> ${ControllerClass.name}.${route.propertyKey}`); }); } @@ -115,10 +113,7 @@ export class RouteScanner { const result = await method.call(instance, ctx, next); // 如果控制器返回了数据,则包装成统一响应格式 - if (result !== undefined) { - ctx.body = createAutoSuccessResponse(result); - } - // 如果控制器没有返回数据,说明已经自己处理了响应 + ctx.body = createAutoSuccessResponse(result); } catch (error) { // 错误由全局异常处理中间件处理 throw error; diff --git a/apps/server/middlewares/cors.ts b/apps/server/middlewares/cors.ts new file mode 100644 index 0000000..f9373b9 --- /dev/null +++ b/apps/server/middlewares/cors.ts @@ -0,0 +1,17 @@ +import cors from '@koa/cors'; +import type Koa from 'koa'; +import type { Middleware } from './types.ts'; + +export class CORS implements Middleware { + apply(app: Koa) { + app.use( + cors({ + credentials: true, + allowHeaders: ['Content-Type'], + origin(ctx) { + return ctx.get('Origin') || '*'; + }, + }), + ); + } +} diff --git a/apps/server/middlewares/exception.ts b/apps/server/middlewares/exception.ts index 487f456..141a1a4 100644 --- a/apps/server/middlewares/exception.ts +++ b/apps/server/middlewares/exception.ts @@ -1,14 +1,15 @@ import type Koa from 'koa'; import type { Middleware } from './types.ts'; +import { log } from '../libs/logger.ts'; /** * 统一响应体结构 */ export interface ApiResponse { - code: number; // 状态码:0表示成功,其他表示失败 - message: string; // 响应消息 - data?: T; // 响应数据 - timestamp: number; // 时间戳 + code: number; // 状态码:0表示成功,其他表示失败 + message: string; // 响应消息 + data?: T; // 响应数据 + timestamp: number; // 时间戳 } /** @@ -36,11 +37,11 @@ export class Exception implements Middleware { await next(); // 如果没有设置响应体,则返回404 - if (ctx.status === 404 && !ctx.body) { - this.sendResponse(ctx, 404, '接口不存在', null, 404); + if (ctx.status === 404) { + this.sendResponse(ctx, 404, 'Not Found', null, 404); } } catch (error) { - console.error('全局异常捕获:', error); + log.error('Exception', 'catch error: %o', error); this.handleError(ctx, error); } }); @@ -55,11 +56,16 @@ export class Exception implements Middleware { this.sendResponse(ctx, error.code, error.message, null, error.httpStatus); } else if (error.status) { // Koa HTTP 错误 - const message = error.status === 401 ? '未授权访问' : - error.status === 403 ? '禁止访问' : - error.status === 404 ? '资源不存在' : - error.status === 422 ? '请求参数错误' : - error.message || '请求失败'; + const message = + error.status === 401 + ? '未授权访问' + : error.status === 403 + ? '禁止访问' + : error.status === 404 + ? '资源不存在' + : error.status === 422 + ? '请求参数错误' + : error.message || '请求失败'; this.sendResponse(ctx, error.status, message, null, error.status); } else { @@ -80,13 +86,13 @@ export class Exception implements Middleware { code: number, message: string, data: any = null, - httpStatus = 200 + httpStatus = 200, ): void { const response: ApiResponse = { code, message, data, - timestamp: Date.now() + timestamp: Date.now(), }; ctx.status = httpStatus; @@ -98,12 +104,15 @@ export class Exception implements Middleware { /** * 创建成功响应的辅助函数 */ -export function createSuccessResponse(data: T, message = '操作成功'): ApiResponse { +export function createSuccessResponse( + data: T, + message = '操作成功', +): ApiResponse { return { code: 0, message, data, - timestamp: Date.now() + timestamp: Date.now(), }; } @@ -128,11 +137,15 @@ export function createAutoSuccessResponse(data: T): ApiResponse { /** * 创建失败响应的辅助函数 */ -export function createErrorResponse(code: number, message: string, data?: any): ApiResponse { +export function createErrorResponse( + code: number, + message: string, + data?: any, +): ApiResponse { return { code, message, data, - timestamp: Date.now() + timestamp: Date.now(), }; } diff --git a/apps/server/middlewares/index.ts b/apps/server/middlewares/index.ts index 70429b5..c19ac4a 100644 --- a/apps/server/middlewares/index.ts +++ b/apps/server/middlewares/index.ts @@ -1,7 +1,9 @@ import { Router } from './router.ts'; -import { ResponseTime } from './responseTime.ts'; import { Exception } from './exception.ts'; import { BodyParser } from './body-parser.ts'; +import { Session } from './session.ts'; +import { CORS } from './cors.ts'; +import { HttpLogger } from './logger.ts'; import type Koa from 'koa'; /** @@ -9,17 +11,19 @@ import type Koa from 'koa'; * @param app Koa */ export function initMiddlewares(app: Koa) { + // 日志中间件需要最早注册,记录所有请求 + new HttpLogger().apply(app); + // 全局异常处理中间件必须最先注册 - const exception = new Exception(); - exception.apply(app); + new Exception().apply(app); + + // Session 中间件需要在请求体解析之前注册 + new Session().apply(app); // 请求体解析中间件 - const bodyParser = new BodyParser(); - bodyParser.apply(app); + new BodyParser().apply(app); - const responseTime = new ResponseTime(); - responseTime.apply(app); + new CORS().apply(app); - const router = new Router(); - router.apply(app); + new Router().apply(app); } diff --git a/apps/server/middlewares/logger.ts b/apps/server/middlewares/logger.ts new file mode 100644 index 0000000..60d194f --- /dev/null +++ b/apps/server/middlewares/logger.ts @@ -0,0 +1,14 @@ +import Koa, { type Context } from 'koa'; +import { log } from '../libs/logger.ts'; +import type { Middleware } from './types.ts'; + +export class HttpLogger implements Middleware { + apply(app: Koa): void { + app.use(async (ctx: Context, next: Koa.Next) => { + const start = Date.now(); + await next(); + const ms = Date.now() - start; + log.info('HTTP', `${ctx.method} ${ctx.url} - ${ms}ms`) + }); + } +} diff --git a/apps/server/middlewares/responseTime.ts b/apps/server/middlewares/responseTime.ts deleted file mode 100644 index eaaccbd..0000000 --- a/apps/server/middlewares/responseTime.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Middleware } from './types.ts'; -import type Koa from 'koa'; - -export class ResponseTime implements Middleware { - apply(app: Koa): void { - app.use(async (ctx, next) => { - const start = Date.now(); - await next(); - const ms = Date.now() - start; - ctx.set('X-Response-Time', `${ms}ms`); - }); - } -} diff --git a/apps/server/middlewares/router.ts b/apps/server/middlewares/router.ts index f6d09b6..4839521 100644 --- a/apps/server/middlewares/router.ts +++ b/apps/server/middlewares/router.ts @@ -1,36 +1,16 @@ import KoaRouter from '@koa/router'; import type Koa from 'koa'; import type { Middleware } from './types.ts'; -import { createAutoSuccessResponse } from './exception.ts'; import { RouteScanner } from '../libs/route-scanner.ts'; import { ApplicationController } from '../controllers/application.ts'; import { UserController } from '../controllers/user.ts'; -import * as application from '../controllers/application.ts'; - -/** - * 包装控制器函数,统一处理响应格式 - */ -function wrapController(controllerFn: Function) { - return async (ctx: Koa.Context, next: Koa.Next) => { - try { - // 调用控制器函数获取返回数据 - const result = await controllerFn(ctx, next); - - // 如果控制器返回了数据,则包装成统一响应格式 - if (result !== undefined) { - ctx.body = createAutoSuccessResponse(result); - } - // 如果控制器没有返回数据,说明已经自己处理了响应 - } catch (error) { - // 错误由全局异常处理中间件处理 - throw error; - } - }; -} +import { AuthController } from '../controllers/auth.ts'; +import { log } from '../libs/logger.ts'; export class Router implements Middleware { private router: KoaRouter; private routeScanner: RouteScanner; + private readonly TAG = 'Router'; constructor() { this.router = new KoaRouter({ @@ -54,14 +34,18 @@ export class Router implements Middleware { // 注册所有使用装饰器的控制器 this.routeScanner.registerControllers([ ApplicationController, - UserController + UserController, + AuthController, ]); // 输出注册的路由信息 const routes = this.routeScanner.getRegisteredRoutes(); - console.log('装饰器路由注册完成:'); - routes.forEach(route => { - console.log(` ${route.method} ${route.path} -> ${route.controller}.${route.action}`); + log.debug(this.TAG, '装饰器路由注册完成:'); + routes.forEach((route) => { + log.debug( + this.TAG, + ` ${route.method} ${route.path} -> ${route.controller}.${route.action}`, + ); }); } diff --git a/apps/server/middlewares/session.ts b/apps/server/middlewares/session.ts new file mode 100644 index 0000000..2c7010f --- /dev/null +++ b/apps/server/middlewares/session.ts @@ -0,0 +1,24 @@ +import session from 'koa-session'; +import type Koa from 'koa'; +import type { Middleware } from './types.ts'; + +export class Session implements Middleware { + apply(app: Koa): void { + app.keys = ['foka-ci']; + app.use( + session( + { + key: 'foka.sid', + maxAge: 86400000, + autoCommit: true /** (boolean) automatically commit headers (default true) */, + overwrite: true /** (boolean) can overwrite or not (default true) */, + httpOnly: true /** (boolean) httpOnly or not (default true) */, + signed: true /** (boolean) signed or not (default true) */, + rolling: false /** (boolean) Force a session identifier cookie to be set on every response. The expiration is reset to the original maxAge, resetting the expiration countdown. (default is false) */, + renew: false /** (boolean) renew session when session is nearly expired, so we can always keep user logged in. (default is false)*/, + }, + app, + ), + ); + } +} diff --git a/apps/server/package.json b/apps/server/package.json index 3b93eaf..37b2ac1 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -10,14 +10,19 @@ "author": "", "license": "ISC", "dependencies": { + "@koa/cors": "^5.0.0", "@koa/router": "^14.0.0", "@prisma/client": "^6.15.0", - "koa": "^3.0.1" + "koa": "^3.0.1", + "koa-session": "^7.0.2", + "pino": "^9.9.1", + "pino-pretty": "^13.1.1" }, "devDependencies": { "@tsconfig/node-ts": "^23.6.1", "@tsconfig/node22": "^22.0.2", "@types/koa": "^3.0.0", + "@types/koa__cors": "^5.0.0", "@types/koa__router": "^12.0.4", "@types/node": "^24.3.0", "prisma": "^6.15.0", diff --git a/apps/server/prisma/data/dev.db b/apps/server/prisma/data/dev.db index fa546027f935f5526d554b98206706cd4395359a..ab7e1e67a5001ff5d2d85ba79b9b3fefb13d0103 100644 GIT binary patch delta 383 zcmZo@U~E{xI6+#Foq>UY6^LPgd7_T7C_95*RRJ$hh=uDN178839M3wgcN-h4xU`yW zS=hxzMH$;-OA?cEQbUVVi$IvwImp#9#8n~0(aFbEK?x)@c|D&UODTx)mQRW`CqF$i zZ}LYzG1k=F#LOHJ%O2FB1DHv0SJ^5lS?woQk4{({QZ4g9sLyi z{6iG{LVbJ`TwL88Lw!ONN{UKTCtL8J*u0J>Oqhd-|2YHy2ma@q1r=`dbFwori*kaj z-~2&eK!6M62S)x%2L8&;f&%&c^^GiyybO|(`i+8|oUE*pk{P8%`8lZ&($LV-$k5E# z!qCK_uuv~KKR2VKq@dVJUmqy0XJ}@jXQ*dnU}bJ#ZlDiwd9l8MQL2T7Sz3~jg|WFo ua&k(Vaf*eBsj)$#QKCU=YMLeEj7{^VTwt6j%G;k{178839M8JVf&w-?o7eG# X39~Wse_-JMuvyUH8UN-F`T_y~?t~Bq diff --git a/apps/server/prisma/schema.prisma b/apps/server/prisma/schema.prisma index 0e686f1..41068fa 100644 --- a/apps/server/prisma/schema.prisma +++ b/apps/server/prisma/schema.prisma @@ -33,3 +33,17 @@ model Environment { createdBy String updatedBy String } + +model User { + id Int @id @default(autoincrement()) + username String + login String + email String + avatar_url String? + active Boolean @default(true) + valid Int @default(1) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + createdBy String @default("system") + updatedBy String @default("system") +} diff --git a/apps/web/.env b/apps/web/.env new file mode 100644 index 0000000..085a653 --- /dev/null +++ b/apps/web/.env @@ -0,0 +1 @@ +BASE_URL=http://192.168.1.36:3001 diff --git a/apps/web/package.json b/apps/web/package.json index cfe1745..2a52a25 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -12,8 +12,9 @@ }, "dependencies": { "@arco-design/web-react": "^2.66.4", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "axios": "^1.11.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-router": "^7.8.0" }, "devDependencies": { diff --git a/apps/web/rsbuild.config.ts b/apps/web/rsbuild.config.ts index ffbcf28..9be59b3 100644 --- a/apps/web/rsbuild.config.ts +++ b/apps/web/rsbuild.config.ts @@ -1,16 +1,24 @@ -import { ArcoDesignPlugin } from "@arco-plugins/unplugin-react"; -import { defineConfig } from "@rsbuild/core"; -import { pluginReact } from "@rsbuild/plugin-react"; -import { pluginLess } from "@rsbuild/plugin-less"; +import { ArcoDesignPlugin } from '@arco-plugins/unplugin-react'; +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; +import { pluginLess } from '@rsbuild/plugin-less'; import { pluginSvgr } from '@rsbuild/plugin-svgr'; export default defineConfig({ plugins: [pluginReact(), pluginLess(), pluginSvgr()], + html: { + title: 'Foka CI', + }, + source: { + define: { + 'process.env.BASE_URL': JSON.stringify(process.env.BASE_URL), + }, + }, tools: { rspack: { plugins: [ new ArcoDesignPlugin({ - defaultLanguage: "zh-CN", + defaultLanguage: 'zh-CN', }), ], }, diff --git a/apps/web/src/assets/images/gitea.svg b/apps/web/src/assets/images/gitea.svg new file mode 100644 index 0000000..9df6b83 --- /dev/null +++ b/apps/web/src/assets/images/gitea.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/apps/web/src/index.tsx b/apps/web/src/index.tsx index ed0b950..43d7132 100644 --- a/apps/web/src/index.tsx +++ b/apps/web/src/index.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import ReactDOM from 'react-dom/client'; import App from '@pages/App'; import { BrowserRouter } from 'react-router'; @@ -8,10 +7,8 @@ const rootEl = document.getElementById('root'); if (rootEl) { const root = ReactDOM.createRoot(rootEl); root.render( - - - - - , + + + , ); } diff --git a/apps/web/src/pages/App.tsx b/apps/web/src/pages/App.tsx index 0cbb1ba..5bd4d32 100644 --- a/apps/web/src/pages/App.tsx +++ b/apps/web/src/pages/App.tsx @@ -1,14 +1,17 @@ -import { Route, Routes } from 'react-router'; +import { Route, Routes, Navigate } from 'react-router'; import Home from '@pages/home'; import Login from '@pages/login'; -import Application from '@pages/application'; +import Project from '@pages/project'; +import Env from '@pages/env'; import '@styles/index.css'; const App = () => { return ( }> - } index /> + } /> + } /> + } /> } /> diff --git a/apps/web/src/pages/application/index.tsx b/apps/web/src/pages/application/index.tsx deleted file mode 100644 index 7d5bcfa..0000000 --- a/apps/web/src/pages/application/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useState } from "react"; - -function Application() { - const [apps, setApps] = useState([]); - return ( -
- application -
- ) -} - -export default Application; diff --git a/apps/web/src/pages/env/index.tsx b/apps/web/src/pages/env/index.tsx new file mode 100644 index 0000000..98225e5 --- /dev/null +++ b/apps/web/src/pages/env/index.tsx @@ -0,0 +1,12 @@ +import { useState } from "react"; + +function Env() { + const [env, setEnv] = useState([]); + return ( +
+ env page +
+ ) +} + +export default Env; diff --git a/apps/web/src/pages/home/index.tsx b/apps/web/src/pages/home/index.tsx index 56a11db..aae9d32 100644 --- a/apps/web/src/pages/home/index.tsx +++ b/apps/web/src/pages/home/index.tsx @@ -11,7 +11,7 @@ import { } from '@arco-design/web-react/icon'; import { useState } from 'react'; import Logo from '@assets/images/logo.svg?react'; -import { Outlet } from 'react-router'; +import { Link, Outlet } from 'react-router'; function Home() { const [collapsed, setCollapsed] = useState(false); @@ -33,24 +33,16 @@ function Home() { defaultSelectedKeys={['0_1']} > - - Navigation 1 + + + 项目管理 + - - Navigation 2 - - - - Navigation 3 - - - - Navigation 4 - - - - Navigation 5 + + + 环境管理 + diff --git a/apps/web/src/pages/login/index.tsx b/apps/web/src/pages/login/index.tsx index 863eb2f..9b6249e 100644 --- a/apps/web/src/pages/login/index.tsx +++ b/apps/web/src/pages/login/index.tsx @@ -1,16 +1,41 @@ -import { Input, Space } from '@arco-design/web-react'; -import { IconUser, IconInfoCircle } from '@arco-design/web-react/icon'; +import { Button } from '@arco-design/web-react'; +import Gitea from '@assets/images/gitea.svg?react'; +import { loginService } from './service'; +import { useEffect } from 'react'; +import { useNavigate, useSearchParams } from 'react-router'; + function Login() { + const [ searchParams ] = useSearchParams(); + const navigate = useNavigate(); + const authCode = searchParams.get('code'); + + const onLoginClick = async () => { + const url = await loginService.getAuthUrl(); + if (url) { + window.location.href = url; + } + }; + + useEffect(() => { + if (authCode) { + loginService.login(authCode, navigate); + } + }, [authCode, navigate]); + return ( -
- - } size="large" /> - } - size="large" - /> - +
+
); } diff --git a/apps/web/src/pages/login/service.ts b/apps/web/src/pages/login/service.ts new file mode 100644 index 0000000..d9cc797 --- /dev/null +++ b/apps/web/src/pages/login/service.ts @@ -0,0 +1,39 @@ +import { net } from '@shared'; +import type { AuthURLResponse } from './types'; +import type { NavigateFunction } from 'react-router'; +import { Notification } from '@arco-design/web-react'; + +class LoginService { + async getAuthUrl() { + const { code, data } = await net.request({ + method: 'GET', + url: '/api/auth/url', + params: { + redirect: encodeURIComponent(`${location.origin}/login`), + }, + }); + if (code === 0) { + return data.url; + } + } + + async login(authCode: string, navigate: NavigateFunction) { + const { data, code } = await net.request({ + method: 'POST', + url: '/api/auth/login', + data: { + code: authCode, + }, + }); + if (code === 0) { + localStorage.setItem('user', JSON.stringify(data)); + navigate('/'); + Notification.success({ + title: '提示', + content: '登录成功' + }); + } + } +} + +export const loginService = new LoginService(); diff --git a/apps/web/src/pages/login/types.ts b/apps/web/src/pages/login/types.ts new file mode 100644 index 0000000..cd5456a --- /dev/null +++ b/apps/web/src/pages/login/types.ts @@ -0,0 +1,5 @@ +import type { APIResponse } from '@shared'; + +export type AuthURLResponse = APIResponse<{ + url: string; +}> diff --git a/apps/web/src/pages/project/index.tsx b/apps/web/src/pages/project/index.tsx new file mode 100644 index 0000000..c1f440a --- /dev/null +++ b/apps/web/src/pages/project/index.tsx @@ -0,0 +1,8 @@ +import { useState } from 'react'; + +function Project() { + const [projects, setProjects] = useState([]); + return
project page
; +} + +export default Project; diff --git a/apps/web/src/pages/application/types.ts b/apps/web/src/pages/project/types.ts similarity index 73% rename from apps/web/src/pages/application/types.ts rename to apps/web/src/pages/project/types.ts index f1478a3..50273a5 100644 --- a/apps/web/src/pages/application/types.ts +++ b/apps/web/src/pages/project/types.ts @@ -1,15 +1,15 @@ -enum AppStatus { +enum BuildStatus { Idle = "Pending", Running = "Running", Stopped = "Stopped", } -export interface Application { +export interface Project { id: string; name: string; description: string; git: string; env: Record; createdAt: string; - status: AppStatus; + status: BuildStatus; } diff --git a/apps/web/src/shared/index.ts b/apps/web/src/shared/index.ts new file mode 100644 index 0000000..56e4b05 --- /dev/null +++ b/apps/web/src/shared/index.ts @@ -0,0 +1 @@ +export * from './request'; diff --git a/apps/web/src/shared/request.ts b/apps/web/src/shared/request.ts new file mode 100644 index 0000000..3ca8d60 --- /dev/null +++ b/apps/web/src/shared/request.ts @@ -0,0 +1,26 @@ +import axios, { Axios, type AxiosRequestConfig } from 'axios'; + +class Net { + private readonly instance: Axios; + constructor() { + this.instance = axios.create({ + baseURL: process.env.BASE_URL, + timeout: 20000, + withCredentials: true, + }); + } + + async request(config: AxiosRequestConfig): Promise { + const { data } = await this.instance.request(config); + return data; + } +} + +export interface APIResponse { + code: number; + data: T; + message: string; + timestamp: number; +} + +export const net = new Net(); diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 8b8884e..38a92e6 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -23,7 +23,8 @@ "paths": { "@pages/*": ["./src/pages/*"], "@styles/*": ["./src/styles/*"], - "@assets/*": ["./src/assets/*"] + "@assets/*": ["./src/assets/*"], + "@shared": ["./src/shared"] } }, "include": ["src"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6f944e..d547c24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: apps/server: dependencies: + '@koa/cors': + specifier: ^5.0.0 + version: 5.0.0 '@koa/router': specifier: ^14.0.0 version: 14.0.0 @@ -23,6 +26,15 @@ importers: koa: specifier: ^3.0.1 version: 3.0.1 + koa-session: + specifier: ^7.0.2 + version: 7.0.2 + pino: + specifier: ^9.9.1 + version: 9.9.1 + pino-pretty: + specifier: ^13.1.1 + version: 13.1.1 devDependencies: '@tsconfig/node-ts': specifier: ^23.6.1 @@ -33,6 +45,9 @@ importers: '@types/koa': specifier: ^3.0.0 version: 3.0.0 + '@types/koa__cors': + specifier: ^5.0.0 + version: 5.0.0 '@types/koa__router': specifier: ^12.0.4 version: 12.0.4 @@ -53,16 +68,19 @@ importers: dependencies: '@arco-design/web-react': specifier: ^2.66.4 - version: 2.66.5(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 2.66.5(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + axios: + specifier: ^1.11.0 + version: 1.11.0 react: - specifier: ^19.1.1 - version: 19.1.1 + specifier: ^18.3.1 + version: 18.3.1 react-dom: - specifier: ^19.1.1 - version: 19.1.1(react@19.1.1) + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) react-router: specifier: ^7.8.0 - version: 7.8.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@arco-plugins/unplugin-react': specifier: 2.0.0-beta.5 @@ -428,6 +446,10 @@ packages: '@jridgewell/trace-mapping@0.3.30': resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@koa/cors@5.0.0': + resolution: {integrity: sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==} + engines: {node: '>= 14.0.0'} + '@koa/router@14.0.0': resolution: {integrity: sha512-LBSu5K0qAaaQcXX/0WIB9PGDevyCxxpnc1uq13vV/CgObaVxuis5hKl3Eboq/8gcb6ebnkAStW9NB/Em2eYyFA==} engines: {node: '>= 20'} @@ -794,6 +816,9 @@ packages: '@types/koa@3.0.0': resolution: {integrity: sha512-MOcVYdVYmkSutVHZZPh8j3+dAjLyR5Tl59CN0eKgpkE1h/LBSmPAsQQuWs+bKu7WtGNn+hKfJH9Gzml+PulmDg==} + '@types/koa__cors@5.0.0': + resolution: {integrity: sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g==} + '@types/koa__router@12.0.4': resolution: {integrity: sha512-Y7YBbSmfXZpa/m5UGGzb7XadJIRBRnwNY9cdAojZGp65Cpe5MAP3mOZE7e3bImt8dfKS4UFcR16SLH8L/z7PBw==} @@ -830,6 +855,16 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + axios@1.11.0: + resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} + b-tween@0.3.3: resolution: {integrity: sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA==} @@ -839,6 +874,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -850,6 +888,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + c12@3.1.0: resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==} peerDependencies: @@ -858,6 +899,10 @@ packages: magicast: optional: true + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -895,6 +940,13 @@ packages: color@3.2.1: resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} @@ -940,6 +992,9 @@ packages: typescript: optional: true + crc@3.8.0: + resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==} + css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} @@ -962,6 +1017,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + dayjs@1.11.15: resolution: {integrity: sha512-MC+DfnSWiM9APs7fpiurHGCoeIx0Gdl6QZBy+5lu8MbYKN5FZEXqOgrundfibdfhGZ15o9hzmZ2xJjZnbvgKXQ==} @@ -988,6 +1046,10 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} @@ -1036,6 +1098,10 @@ packages: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -1053,6 +1119,9 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + enhanced-resolve@5.18.3: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} @@ -1067,6 +1136,22 @@ packages: error-stack-parser@2.1.4: resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.25.9: resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} engines: {node: '>=18'} @@ -1086,10 +1171,33 @@ packages: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} + fast-copy@3.0.2: + resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + focus-lock@1.3.6: resolution: {integrity: sha512-Ik/6OCk9RQQ0T5Xw+hKNLWrjSMtv51dD4GRmJjbD5a58TIEpI5a5iXagKVl3Z5UuyslMCA8Xwnu76jQob62Yhg==} engines: {node: '>=10'} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} @@ -1099,10 +1207,21 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} @@ -1110,9 +1229,28 @@ packages: resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} hasBin: true + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + html-entities@2.6.0: resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} @@ -1128,6 +1266,9 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1141,10 +1282,17 @@ packages: is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-type-of@2.2.0: + resolution: {integrity: sha512-72axShMJMnMy5HSU/jLGNOonZD5rWM0MwJSCYpKCTQCbggQZBJO/CLMMVP5HgS8kPSYFBkTysJexsD6NMvGKDQ==} + jiti@2.5.1: resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} hasBin: true + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1172,6 +1320,10 @@ packages: koa-compose@4.1.0: resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} + koa-session@7.0.2: + resolution: {integrity: sha512-nMWJndLmIuKQMTYPr5NokGQOGD2Aqal5GVi1xAhrQjrrzKq1ASy1WTFVkZ/xhwhtC4KpWi5KdqNYewZo7KJb4w==} + engines: {node: '>= 18.19.0'} + koa@3.0.1: resolution: {integrity: sha512-oDxVkRwPOHhGlxKIDiDB2h+/l05QPtefD7nSqRgDfZt8P+QVYFWjfeK8jANf5O2YXjk8egd7KntvXKYx82wOag==} engines: {node: '>= 18'} @@ -1263,6 +1415,10 @@ packages: magic-string@0.30.18: resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + mdn-data@2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} @@ -1293,6 +1449,9 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -1345,10 +1504,17 @@ packages: ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1378,6 +1544,20 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-pretty@13.1.1: + resolution: {integrity: sha512-TNNEOg0eA0u+/WuqH0MH0Xui7uqVk9D74ESOpjtebSQYbNWJk/dIxCXIxFsNfeN53JmtWqYHP2OrIZjT/CBEnA==} + hasBin: true + + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + + pino@9.9.1: + resolution: {integrity: sha512-40SszWPOPwGhUIJ3zj0PsbMNV1bfg8nw5Qp/tP2FE2p3EuycmhDeYimKOMBAu6rtxcSw2QpjJsuK5A6v+en8Yw==} + hasBin: true + pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} @@ -1395,12 +1575,24 @@ packages: typescript: optional: true + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} @@ -1409,10 +1601,10 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - react-dom@19.1.1: - resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: - react: ^19.1.1 + react: ^18.3.1 react-focus-lock@2.13.6: resolution: {integrity: sha512-ehylFFWyYtBKXjAO9+3v8d0i+cnc1trGS0vlTGhzFW1vbFXVUTmR8s2tt/ZQG8x5hElg6rhENlLG1H3EZK0Llg==} @@ -1449,14 +1641,18 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' - react@19.1.1: - resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + reduce-configs@1.1.1: resolution: {integrity: sha512-EYtsVGAQarE8daT54cnaY1PIknF2VB78ug6Zre2rs36EsJfC40EG6hmTU2A2P1ZuXnKAt2KI0fzOGHcX7wzdPw==} @@ -1473,12 +1669,19 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - scheduler@0.26.0: - resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} scroll-into-view-if-needed@2.2.31: resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + secure-json-parse@4.0.0: + resolution: {integrity: sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -1498,10 +1701,17 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} @@ -1517,6 +1727,10 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + svg-parser@2.0.4: resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} @@ -1536,6 +1750,9 @@ packages: resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} engines: {node: '>=18'} + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} @@ -1597,6 +1814,9 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -1604,6 +1824,9 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -1617,7 +1840,7 @@ snapshots: dependencies: color: 3.2.1 - '@arco-design/web-react@2.66.5(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@arco-design/web-react@2.66.5(@types/react@19.1.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@arco-design/color': 0.4.0 '@babel/runtime': 7.28.3 @@ -1627,11 +1850,11 @@ snapshots: dayjs: 1.11.15 lodash: 4.17.21 number-precision: 1.6.0 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - react-focus-lock: 2.13.6(@types/react@19.1.12)(react@19.1.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-focus-lock: 2.13.6(@types/react@19.1.12)(react@18.3.1) react-is: 18.3.1 - react-transition-group: 4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) resize-observer-polyfill: 1.5.1 scroll-into-view-if-needed: 2.2.31 shallowequal: 1.1.0 @@ -1897,6 +2120,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@koa/cors@5.0.0': + dependencies: + vary: 1.1.2 + '@koa/router@14.0.0': dependencies: debug: 4.4.1 @@ -2294,6 +2521,10 @@ snapshots: '@types/koa-compose': 3.2.8 '@types/node': 24.3.0 + '@types/koa__cors@5.0.0': + dependencies: + '@types/koa': 3.0.0 + '@types/koa__router@12.0.4': dependencies: '@types/koa': 3.0.0 @@ -2334,12 +2565,26 @@ snapshots: argparse@2.0.1: {} + asynckit@0.4.0: {} + + atomic-sleep@1.0.0: {} + + axios@1.11.0: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + b-tween@0.3.3: {} b-validate@1.5.3: {} balanced-match@1.0.2: {} + base64-js@1.5.1: {} + boolbase@1.0.0: {} brace-expansion@2.0.2: @@ -2353,6 +2598,11 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.3) + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + c12@3.1.0: dependencies: chokidar: 4.0.3 @@ -2368,6 +2618,11 @@ snapshots: pkg-types: 2.3.0 rc9: 2.1.2 + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + callsites@3.1.0: {} camelcase@6.3.0: {} @@ -2402,6 +2657,12 @@ snapshots: color-convert: 1.9.3 color-string: 1.9.1 + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + commander@7.2.0: {} compute-scroll-into-view@1.0.20: {} @@ -2436,6 +2697,10 @@ snapshots: optionalDependencies: typescript: 5.9.2 + crc@3.8.0: + dependencies: + buffer: 5.7.1 + css-select@5.2.2: dependencies: boolbase: 1.0.0 @@ -2462,6 +2727,8 @@ snapshots: csstype@3.1.3: {} + dateformat@4.6.3: {} + dayjs@1.11.15: {} debug@4.4.1: @@ -2476,6 +2743,8 @@ snapshots: defu@6.1.4: {} + delayed-stream@1.0.0: {} + delegates@1.0.0: {} depd@1.1.2: {} @@ -2520,6 +2789,12 @@ snapshots: dotenv@16.6.1: {} + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + ee-first@1.1.1: {} effect@3.16.12: @@ -2533,6 +2808,10 @@ snapshots: encodeurl@2.0.0: {} + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 @@ -2548,6 +2827,21 @@ snapshots: dependencies: stackframe: 1.3.4 + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.25.9: optionalDependencies: '@esbuild/aix-ppc64': 0.25.9 @@ -2587,17 +2881,53 @@ snapshots: dependencies: pure-rand: 6.1.0 + fast-copy@3.0.2: {} + + fast-redact@3.5.0: {} + + fast-safe-stringify@2.1.1: {} + focus-lock@1.3.6: dependencies: tslib: 2.8.1 + follow-redirects@1.15.11: {} + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + fresh@0.5.2: {} fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + gensync@1.0.0-beta.2: {} + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -2611,8 +2941,22 @@ snapshots: nypm: 0.6.1 pathe: 2.0.3 + gopd@1.2.0: {} + graceful-fs@4.2.11: {} + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + help-me@5.0.0: {} + html-entities@2.6.0: {} http-assert@1.5.0: @@ -2636,6 +2980,8 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + ieee754@1.2.1: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -2647,8 +2993,12 @@ snapshots: is-arrayish@0.3.2: {} + is-type-of@2.2.0: {} + jiti@2.5.1: {} + joycon@3.1.1: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -2667,6 +3017,12 @@ snapshots: koa-compose@4.1.0: {} + koa-session@7.0.2: + dependencies: + crc: 3.8.0 + is-type-of: 2.2.0 + zod: 3.25.76 + koa@3.0.1: dependencies: accepts: 1.3.8 @@ -2755,6 +3111,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + math-intrinsics@1.1.0: {} + mdn-data@2.0.28: {} mdn-data@2.0.30: {} @@ -2777,6 +3135,8 @@ snapshots: dependencies: brace-expansion: 2.0.2 + minimist@1.2.8: {} + minipass@7.1.2: {} minizlib@3.0.2: @@ -2818,10 +3178,16 @@ snapshots: ohash@2.0.11: {} + on-exit-leak-free@2.1.2: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 + once@1.4.0: + dependencies: + wrappy: 1.0.2 + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -2845,6 +3211,42 @@ snapshots: picocolors@1.1.1: {} + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-pretty@13.1.1: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 3.0.2 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pump: 3.0.3 + secure-json-parse: 4.0.0 + sonic-boom: 4.2.0 + strip-json-comments: 5.0.3 + + pino-std-serializers@7.0.0: {} + + pino@9.9.1: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.0.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.0 + thread-stream: 3.1.0 + pkg-types@2.3.0: dependencies: confbox: 0.2.2 @@ -2866,38 +3268,50 @@ snapshots: transitivePeerDependencies: - magicast + process-warning@5.0.0: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + proxy-from-env@1.1.0: {} + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + pure-rand@6.1.0: {} + quick-format-unescaped@4.0.4: {} + rc9@2.1.2: dependencies: defu: 6.1.4 destr: 2.0.5 - react-clientside-effect@1.2.8(react@19.1.1): + react-clientside-effect@1.2.8(react@18.3.1): dependencies: '@babel/runtime': 7.28.3 - react: 19.1.1 + react: 18.3.1 - react-dom@19.1.1(react@19.1.1): + react-dom@18.3.1(react@18.3.1): dependencies: - react: 19.1.1 - scheduler: 0.26.0 + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 - react-focus-lock@2.13.6(@types/react@19.1.12)(react@19.1.1): + react-focus-lock@2.13.6(@types/react@19.1.12)(react@18.3.1): dependencies: '@babel/runtime': 7.28.3 focus-lock: 1.3.6 prop-types: 15.8.1 - react: 19.1.1 - react-clientside-effect: 1.2.8(react@19.1.1) - use-callback-ref: 1.3.3(@types/react@19.1.12)(react@19.1.1) - use-sidecar: 1.1.3(@types/react@19.1.12)(react@19.1.1) + react: 18.3.1 + react-clientside-effect: 1.2.8(react@18.3.1) + use-callback-ref: 1.3.3(@types/react@19.1.12)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@19.1.12)(react@18.3.1) optionalDependencies: '@types/react': 19.1.12 @@ -2907,27 +3321,31 @@ snapshots: react-refresh@0.17.0: {} - react-router@7.8.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + react-router@7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: cookie: 1.0.2 - react: 19.1.1 + react: 18.3.1 set-cookie-parser: 2.7.1 optionalDependencies: - react-dom: 19.1.1(react@19.1.1) + react-dom: 18.3.1(react@18.3.1) - react-transition-group@4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.28.3 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - react@19.1.1: {} + react@18.3.1: + dependencies: + loose-envify: 1.4.0 readdirp@4.1.2: {} + real-require@0.2.0: {} + reduce-configs@1.1.1: {} resize-observer-polyfill@1.5.1: {} @@ -2938,12 +3356,18 @@ snapshots: safe-buffer@5.2.1: {} - scheduler@0.26.0: {} + safe-stable-stringify@2.5.0: {} + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 scroll-into-view-if-needed@2.2.31: dependencies: compute-scroll-into-view: 1.0.20 + secure-json-parse@4.0.0: {} + semver@6.3.1: {} set-cookie-parser@2.7.1: {} @@ -2961,8 +3385,14 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 + sonic-boom@4.2.0: + dependencies: + atomic-sleep: 1.0.0 + source-map-js@1.2.1: {} + split2@4.2.0: {} + stackframe@1.3.4: {} statuses@1.5.0: {} @@ -2971,6 +3401,8 @@ snapshots: statuses@2.0.2: {} + strip-json-comments@5.0.3: {} + svg-parser@2.0.4: {} svgo@3.3.2: @@ -2996,6 +3428,10 @@ snapshots: mkdirp: 3.0.1 yallist: 5.0.0 + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + tinyexec@1.0.1: {} toidentifier@1.0.1: {} @@ -3027,23 +3463,27 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - use-callback-ref@1.3.3(@types/react@19.1.12)(react@19.1.1): + use-callback-ref@1.3.3(@types/react@19.1.12)(react@18.3.1): dependencies: - react: 19.1.1 + react: 18.3.1 tslib: 2.8.1 optionalDependencies: '@types/react': 19.1.12 - use-sidecar@1.1.3(@types/react@19.1.12)(react@19.1.1): + use-sidecar@1.1.3(@types/react@19.1.12)(react@18.3.1): dependencies: detect-node-es: 1.1.0 - react: 19.1.1 + react: 18.3.1 tslib: 2.8.1 optionalDependencies: '@types/react': 19.1.12 vary@1.1.2: {} + wrappy@1.0.2: {} + yallist@3.1.1: {} yallist@5.0.0: {} + + zod@3.25.76: {}