From f4d692472e8d98bee3fbafd0882d665b6a1b2e51 Mon Sep 17 00:00:00 2001 From: yourname Date: Wed, 14 May 2025 02:04:48 +0000 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=20server/app.tsx=20-=20?= =?UTF-8?q?=E4=BB=85=E4=BF=9D=E7=95=99=E5=BA=94=E7=94=A8=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E5=92=8C=E6=9C=8D=E5=8A=A1=E5=90=AF=E5=8A=A8=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HISTORY.md | 6 + server/app.tsx | 255 +------------------------------ server/middlewares.ts | 111 ++++++++++++++ server/router.ts | 50 ++++++ server/routes_auth.ts | 3 +- server/routes_charts.ts | 2 +- server/routes_file_category.ts | 2 +- server/routes_file_upload.ts | 2 +- server/routes_home.ts | 3 +- server/routes_know_info.ts | 2 +- server/routes_maps.ts | 2 +- server/routes_messages.ts | 3 +- server/routes_migrations.ts | 3 +- server/routes_system_settings.ts | 2 +- server/routes_theme.ts | 2 +- server/routes_users.ts | 3 +- 16 files changed, 185 insertions(+), 266 deletions(-) create mode 100644 server/middlewares.ts create mode 100644 server/router.ts diff --git a/HISTORY.md b/HISTORY.md index 692f97c..a01d6ee 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,12 @@ 待实现 迁移管理页面,在正式环境中,需要验证env中配置的密码参数才能打开 +2025.05.14 0.1.1 +创建 server/middlewares.ts - 集中管理所有中间件配置 +创建 server/router.ts - 统一处理路由注册逻辑 +重构 server/app.tsx - 仅保留应用初始化和服务启动逻辑 +保持原有功能不变,同时提高代码可维护性和扩展性 + 2025.05.13 0.1.0 将admin api.ts 拆开 打开迁移管理页面时,将迁移历史读取出来 diff --git a/server/app.tsx b/server/app.tsx index 4679a73..322db96 100644 --- a/server/app.tsx +++ b/server/app.tsx @@ -1,39 +1,15 @@ /** @jsxImportSource https://esm.d8d.fun/hono@4.7.4/jsx */ import { Hono } from 'hono' -import { Auth } from '@d8d-appcontainer/auth' -import type { User as AuthUser } from '@d8d-appcontainer/auth' import React from 'hono/jsx' -import type { FC } from 'hono/jsx' -import { cors } from 'hono/cors' import type { Context as HonoContext } from 'hono' import { serveStatic } from 'hono/deno' import { APIClient } from '@d8d-appcontainer/api' import debug from "debug" import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; -import type { SystemSettingRecord, GlobalConfig } from '../client/share/types.ts'; -import { SystemSettingKey, OssType, MapMode } from '../client/share/types.ts'; - -import { createKnowInfoRoutes } from "./routes_know_info.ts"; -import { createFileCategoryRoutes } from "./routes_file_category.ts"; -import { createFileUploadRoutes } from "./routes_file_upload.ts"; -import { createThemeRoutes } from "./routes_theme.ts"; -import { createSystemSettingsRoutes } from "./routes_system_settings.ts"; - -import { - createMapRoutes, -} from "./routes_maps.ts"; - -import { - createChartRoutes, -} from "./routes_charts.ts"; - -// 导入基础路由 -import { createAuthRoutes } from "./routes_auth.ts"; -import { createUserRoutes } from "./routes_users.ts"; -import { createMessagesRoutes } from "./routes_messages.ts"; -import { createMigrationsRoutes } from "./routes_migrations.ts"; -import { createHomeRoutes } from "./routes_home.ts"; +import type { GlobalConfig } from '../client/share/types.ts'; +import { OssType, MapMode } from '../client/share/types.ts'; +import { createRouter } from './router.ts' dayjs.extend(utc) // 初始化debug实例 const log = { @@ -66,35 +42,6 @@ log.auth.enabled = true log.api.enabled = true log.debug.enabled = true -// 定义自定义上下文类型 -export interface Variables { - auth: Auth - user?: AuthUser - apiClient: APIClient - moduleDir: string - systemSettings?: SystemSettingRecord -} - -// 定义登录历史类型 -interface LoginHistory { - id: number - user_id: number - login_time: string - ip_address?: string - user_agent?: string -} - -// 定义仪表盘数据类型 -interface DashboardData { - lastLogin: string - loginCount: number - fileCount: number - userCount: number - systemInfo: { - version: string - lastUpdate: string - } -} interface EsmScriptConfig { src: string @@ -105,122 +52,6 @@ interface EsmScriptConfig { prodSrc?: string } -// Auth实例 -let authInstance: Auth | null = null - -// 初始化Auth实例 -const initAuth = async (apiClient: APIClient) => { - try { - if (authInstance) { - return authInstance - } - - log.auth('正在初始化Auth实例') - - authInstance = new Auth(apiClient as any, { - jwtSecret: Deno.env.get("JWT_SECRET") || 'your-jwt-secret-key', - initialUsers: [], - storagePrefix: '', - userTable: 'users', - fieldNames: { - id: 'id', - username: 'username', - password: 'password', - phone: 'phone', - email: 'email', - is_disabled: 'is_disabled', - is_deleted: 'is_deleted' - }, - tokenExpiry: 24 * 60 * 60, - refreshTokenExpiry: 7 * 24 * 60 * 60 - }) - - log.auth('Auth实例初始化完成') - - return authInstance - } catch (error) { - log.auth('Auth初始化失败:', error) - throw error - } -} - -// 初始化系统设置 -const initSystemSettings = async (apiClient: APIClient) => { - try { - const systemSettings = await apiClient.database.table('system_settings') - .select() - - // 将系统设置转换为键值对形式 - const settings = systemSettings.reduce((acc: Record, setting: any) => { - acc[setting.key] = setting.value - return acc - }, {}) as SystemSettingRecord - - // 更新全局配置 - if (settings[SystemSettingKey.SITE_NAME]) { - GLOBAL_CONFIG.APP_NAME = String(settings[SystemSettingKey.SITE_NAME]) - } - - // 设置其他全局配置项 - if (settings[SystemSettingKey.SITE_FAVICON]) { - GLOBAL_CONFIG.DEFAULT_THEME = String(settings[SystemSettingKey.SITE_FAVICON]) - } - - if (settings[SystemSettingKey.SITE_LOGO]) { - GLOBAL_CONFIG.MAP_CONFIG.KEY = String(settings[SystemSettingKey.SITE_LOGO]) - } - - if (settings[SystemSettingKey.SITE_DESCRIPTION]) { - GLOBAL_CONFIG.CHART_THEME = String(settings[SystemSettingKey.SITE_DESCRIPTION]) - } - - // 设置主题配置开关 - if (settings[SystemSettingKey.ENABLE_THEME_CONFIG]) { - GLOBAL_CONFIG.ENABLE_THEME_CONFIG = settings[SystemSettingKey.ENABLE_THEME_CONFIG] === 'true' - } - - // 查询ID1管理员的主题配置 - const adminTheme = await apiClient.database.table('theme_settings') - .where('user_id', 1) - .first() - - if (adminTheme) { - GLOBAL_CONFIG.THEME = adminTheme.settings - } - - return settings - - } catch (error) { - log.app('获取系统设置失败:', error) - return {} as SystemSettingRecord - } -} - -// 中间件:验证认证 -const withAuth = async (c: HonoContext<{ Variables: Variables }>, next: () => Promise) => { - try { - const auth = c.get('auth') - - const token = c.req.header('Authorization')?.replace('Bearer ', '') - if (token) { - const userData = await auth.verifyToken(token) - if (userData) { - c.set('user', userData) - await next() - return - } - } - - return c.json({ error: '未授权' }, 401) - } catch (error) { - log.auth('认证失败:', error) - return c.json({ error: '无效凭证' }, 401) - } -} - -// 导出withAuth类型定义 -export type WithAuth = typeof withAuth; - // 定义模块参数接口 interface ModuleParams { apiClient: APIClient @@ -230,84 +61,10 @@ interface ModuleParams { export default function({ apiClient, app, moduleDir }: ModuleParams) { const honoApp = app - // 添加CORS中间件 - honoApp.use('/*', cors()) - // 创建API路由 - const api = new Hono<{ Variables: Variables }>() - - - // // 使用数据库中间件 - // api.use('/*', withDatabase) - - // 设置环境变量 - api.use('*', async (c, next) => { - c.set('apiClient', apiClient) - c.set('moduleDir', moduleDir) - c.set('auth', await initAuth(apiClient)) - c.set('systemSettings', await initSystemSettings(apiClient)) - await next() - }) - - // 查询仪表盘数据 - api.get('/dashboard', withAuth, async (c) => { - try { - const user = c.get('user')! - const apiClient = c.get('apiClient') - const lastLogin = await apiClient.database.table('login_history') - .where('user_id', user.id) - .orderBy('login_time', 'desc') - .limit(1) - .first() - - // 获取登录总次数 - const loginCount = await apiClient.database.table('login_history') - .where('user_id', user.id) - .count() - - // 获取系统数据统计 - const fileCount = await apiClient.database.table('file_library') - .where('is_deleted', 0) - .count() - - const userCount = await apiClient.database.table('users') - .where('is_deleted', 0) - .count() - - // 返回仪表盘数据 - const dashboardData: DashboardData = { - lastLogin: lastLogin ? lastLogin.login_time : new Date().toISOString(), - loginCount: loginCount, - fileCount: Number(fileCount), - userCount: Number(userCount), - systemInfo: { - version: '1.0.0', - lastUpdate: new Date().toISOString() - } - } - - return c.json(dashboardData) - } catch (error) { - log.api('获取仪表盘数据失败:', error) - return c.json({ error: '获取仪表盘数据失败' }, 500) - } - }) - // 注册基础路由 - api.route('/auth', createAuthRoutes(withAuth)) - api.route('/users', createUserRoutes(withAuth)) - api.route('/know-infos', createKnowInfoRoutes(withAuth)) - api.route('/upload', createFileUploadRoutes(withAuth)) // 添加文件上传路由 - api.route('/file-categories', createFileCategoryRoutes(withAuth)) // 添加文件分类管理路由 - api.route('/theme', createThemeRoutes(withAuth)) // 添加主题设置路由 - api.route('/charts', createChartRoutes(withAuth)) // 添加图表数据路由 - api.route('/map', createMapRoutes(withAuth)) // 添加地图数据路由 - api.route('/settings', createSystemSettingsRoutes(withAuth)) // 添加系统设置路由 - api.route('/messages', createMessagesRoutes(withAuth)) // 添加消息路由 - api.route('/migrations', createMigrationsRoutes(withAuth)) // 添加数据库迁移路由 - api.route('/home', createHomeRoutes(withAuth)) // 添加首页路由 - - // 注册API路由 - honoApp.route('/api', api) + // 创建路由 + const router = createRouter(apiClient, moduleDir) + honoApp.route('/', router) // 首页路由 - SSR honoApp.get('/', async (c: HonoContext) => { diff --git a/server/middlewares.ts b/server/middlewares.ts new file mode 100644 index 0000000..4fad3f5 --- /dev/null +++ b/server/middlewares.ts @@ -0,0 +1,111 @@ +import { Hono } from 'hono' +import { cors } from 'hono/cors' +import type { Context as HonoContext } from 'hono' +import { Auth } from '@d8d-appcontainer/auth' +import type { User as AuthUser } from '@d8d-appcontainer/auth' +import { APIClient } from '@d8d-appcontainer/api' +import type { SystemSettingRecord } from '../client/share/types.ts' +import debug from "debug" + +const log = { + auth: debug('auth:server') +} + +// 定义自定义上下文类型 +export interface Variables { + auth: Auth + user?: AuthUser + apiClient: APIClient + moduleDir: string + systemSettings?: SystemSettingRecord +} +// 认证中间件 +export const withAuth = async (c: HonoContext<{ Variables: Variables }>, next: () => Promise) => { + try { + const auth = c.get('auth') + + const token = c.req.header('Authorization')?.replace('Bearer ', '') + if (token) { + const userData = await auth.verifyToken(token) + if (userData) { + c.set('user', userData) + await next() + return + } + } + + return c.json({ error: '未授权' }, 401) + } catch (error) { + log.auth('认证失败:', error) + return c.json({ error: '无效凭证' }, 401) + } +} + +// 导出withAuth类型定义 +export type WithAuth = typeof withAuth; + +// 环境变量设置中间件 +export const setEnvVariables = (apiClient: APIClient, moduleDir: string) => { + return async (c: HonoContext<{ Variables: Variables }>, next: () => Promise) => { + c.set('apiClient', apiClient) + c.set('moduleDir', moduleDir) + c.set('auth', await initAuth(apiClient)) + c.set('systemSettings', await initSystemSettings(apiClient)) + await next() + } +} + +// CORS中间件 +export const corsMiddleware = cors() + +// 初始化Auth实例 +const initAuth = async (apiClient: APIClient) => { + try { + log.auth('正在初始化Auth实例') + + const auth = new Auth(apiClient as any, { + jwtSecret: Deno.env.get("JWT_SECRET") || 'your-jwt-secret-key', + initialUsers: [], + storagePrefix: '', + userTable: 'users', + fieldNames: { + id: 'id', + username: 'username', + password: 'password', + phone: 'phone', + email: 'email', + is_disabled: 'is_disabled', + is_deleted: 'is_deleted' + }, + tokenExpiry: 24 * 60 * 60, + refreshTokenExpiry: 7 * 24 * 60 * 60 + }) + + log.auth('Auth实例初始化完成') + return auth + + } catch (error) { + log.auth('Auth初始化失败:', error) + throw error + } +} + +// 初始化系统设置 +const initSystemSettings = async (apiClient: APIClient) => { + try { + const systemSettings = await apiClient.database.table('system_settings') + .select() + + // 将系统设置转换为键值对形式 + const settings = systemSettings.reduce((acc: Record, setting: any) => { + acc[setting.key] = setting.value + return acc + }, {}) as SystemSettingRecord + + return settings + + } catch (error) { + log.auth('获取系统设置失败:', error) + return {} as SystemSettingRecord + } +} diff --git a/server/router.ts b/server/router.ts new file mode 100644 index 0000000..b601883 --- /dev/null +++ b/server/router.ts @@ -0,0 +1,50 @@ +/** @jsxImportSource https://esm.d8d.fun/hono@4.7.4/jsx */ +import { Hono } from 'hono' +import { corsMiddleware, withAuth, setEnvVariables } from './middlewares.ts' +import type { APIClient } from '@d8d-appcontainer/api' + +// 导入路由模块 +import { createAuthRoutes } from "./routes_auth.ts" +import { createUserRoutes } from "./routes_users.ts" +import { createKnowInfoRoutes } from "./routes_know_info.ts" +import { createFileUploadRoutes } from "./routes_file_upload.ts" +import { createFileCategoryRoutes } from "./routes_file_category.ts" +import { createThemeRoutes } from "./routes_theme.ts" +import { createChartRoutes } from "./routes_charts.ts" +import { createMapRoutes } from "./routes_maps.ts" +import { createSystemSettingsRoutes } from "./routes_system_settings.ts" +import { createMessagesRoutes } from "./routes_messages.ts" +import { createMigrationsRoutes } from "./routes_migrations.ts" +import { createHomeRoutes } from "./routes_home.ts" + +export function createRouter(apiClient: APIClient, moduleDir: string) { + const router = new Hono() + + // 添加CORS中间件 + router.use('/*', corsMiddleware) + + // 创建API路由 + const api = new Hono() + + // 设置环境变量 + api.use('*', setEnvVariables(apiClient, moduleDir)) + + // 注册所有路由 + api.route('/auth', createAuthRoutes(withAuth)) + api.route('/users', createUserRoutes(withAuth)) + api.route('/know-infos', createKnowInfoRoutes(withAuth)) + api.route('/upload', createFileUploadRoutes(withAuth)) + api.route('/file-categories', createFileCategoryRoutes(withAuth)) + api.route('/theme', createThemeRoutes(withAuth)) + api.route('/charts', createChartRoutes(withAuth)) + api.route('/map', createMapRoutes(withAuth)) + api.route('/settings', createSystemSettingsRoutes(withAuth)) + api.route('/messages', createMessagesRoutes(withAuth)) + api.route('/migrations', createMigrationsRoutes(withAuth)) + api.route('/home', createHomeRoutes(withAuth)) + + // 注册API路由到主路由器 + router.route('/api', api) + + return router +} \ No newline at end of file diff --git a/server/routes_auth.ts b/server/routes_auth.ts index edb23fa..44fbd3e 100644 --- a/server/routes_auth.ts +++ b/server/routes_auth.ts @@ -1,6 +1,5 @@ import { Hono } from 'hono' -import type { Variables } from './app.tsx' -import type { WithAuth } from './app.tsx' +import type { Variables, WithAuth } from "./middlewares.ts"; export function createAuthRoutes(withAuth: WithAuth) { const authRoutes = new Hono<{ Variables: Variables }>() diff --git a/server/routes_charts.ts b/server/routes_charts.ts index 2691d3b..5224c10 100644 --- a/server/routes_charts.ts +++ b/server/routes_charts.ts @@ -4,7 +4,7 @@ import { DeleteStatus, } from "../client/share/types.ts"; -import type { Variables, WithAuth } from "./app.tsx"; +import type { Variables, WithAuth } from "./middlewares.ts"; const log = { api: debug("api:sys"), diff --git a/server/routes_file_category.ts b/server/routes_file_category.ts index 35ba900..cd84b9b 100644 --- a/server/routes_file_category.ts +++ b/server/routes_file_category.ts @@ -8,7 +8,7 @@ import { DeleteStatus, } from "../client/share/types.ts"; -import type { Variables, WithAuth } from "./app.tsx"; +import type { Variables, WithAuth } from "./middlewares.ts"; const log = { api: debug("api:sys"), diff --git a/server/routes_file_upload.ts b/server/routes_file_upload.ts index a8f7a8f..37418df 100644 --- a/server/routes_file_upload.ts +++ b/server/routes_file_upload.ts @@ -9,7 +9,7 @@ import { EnableStatus, } from "../client/share/types.ts"; -import type { Variables, WithAuth } from "./app.tsx"; +import type { Variables, WithAuth } from "./middlewares.ts"; const log = { api: debug("api:sys"), diff --git a/server/routes_home.ts b/server/routes_home.ts index 6ef74f9..453e528 100644 --- a/server/routes_home.ts +++ b/server/routes_home.ts @@ -1,6 +1,5 @@ import { Hono } from 'hono' -import type { Variables } from './app.tsx' -import type { WithAuth } from './app.tsx' +import type { Variables, WithAuth } from "./middlewares.ts"; import { AuditStatus } from '../client/share/types.ts' export function createHomeRoutes(withAuth: WithAuth) { diff --git a/server/routes_know_info.ts b/server/routes_know_info.ts index 2e852b0..0af58e9 100644 --- a/server/routes_know_info.ts +++ b/server/routes_know_info.ts @@ -9,7 +9,7 @@ import { DeleteStatus, } from "../client/share/types.ts"; -import type { Variables, WithAuth } from "./app.tsx"; +import type { Variables, WithAuth } from "./middlewares.ts"; const log = { api: debug("api:sys"), diff --git a/server/routes_maps.ts b/server/routes_maps.ts index ec5716b..19bd283 100644 --- a/server/routes_maps.ts +++ b/server/routes_maps.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import debug from "debug"; -import type { Variables, WithAuth } from "./app.tsx"; +import type { Variables, WithAuth } from "./middlewares.ts"; const log = { api: debug("api:sys"), diff --git a/server/routes_messages.ts b/server/routes_messages.ts index 658568b..38d005f 100644 --- a/server/routes_messages.ts +++ b/server/routes_messages.ts @@ -1,6 +1,5 @@ import { Hono } from 'hono' -import type { Variables } from './app.tsx' -import type { WithAuth } from './app.tsx' +import type { Variables, WithAuth } from "./middlewares.ts"; import { MessageType, MessageStatus } from '../client/share/types.ts' export function createMessagesRoutes(withAuth: WithAuth) { diff --git a/server/routes_migrations.ts b/server/routes_migrations.ts index 858f049..0b5fae6 100644 --- a/server/routes_migrations.ts +++ b/server/routes_migrations.ts @@ -1,7 +1,6 @@ import { Hono } from 'hono' import { APIClient } from '@d8d-appcontainer/api' -import type { Variables } from './app.tsx' -import type { WithAuth } from './app.tsx' +import type { Variables, WithAuth } from "./middlewares.ts"; import { migrations } from './migrations.ts' import debug from "debug"; const log = { diff --git a/server/routes_system_settings.ts b/server/routes_system_settings.ts index ebd1bdb..09d776a 100644 --- a/server/routes_system_settings.ts +++ b/server/routes_system_settings.ts @@ -5,7 +5,7 @@ import type { SystemSettingGroupData, } from "../client/share/types.ts"; -import type { Variables, WithAuth } from "./app.tsx"; +import type { Variables, WithAuth } from "./middlewares.ts"; const log = { api: debug("api:sys"), diff --git a/server/routes_theme.ts b/server/routes_theme.ts index c4c95eb..eff383e 100644 --- a/server/routes_theme.ts +++ b/server/routes_theme.ts @@ -10,7 +10,7 @@ import { CompactMode, } from "../client/share/types.ts"; -import type { Variables, WithAuth } from "./app.tsx"; +import type { Variables, WithAuth } from "./middlewares.ts"; const log = { api: debug("api:sys"), diff --git a/server/routes_users.ts b/server/routes_users.ts index 53c0fb2..1c6e232 100644 --- a/server/routes_users.ts +++ b/server/routes_users.ts @@ -1,6 +1,5 @@ import { Hono } from 'hono' -import type { Variables } from './app.tsx' -import type { WithAuth } from './app.tsx' +import type { Variables, WithAuth } from "./middlewares.ts"; export function createUserRoutes(withAuth: WithAuth) { const usersRoutes = new Hono<{ Variables: Variables }>()