Files
2025-05-15 08:40:09 +00:00

278 lines
10 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** @jsxImportSource https://esm.d8d.fun/hono@4.7.4/jsx */
import { Hono } from 'hono'
import React from 'hono/jsx'
import type { Context as HonoContext } from 'hono'
import { serveStatic } from 'hono/deno'
import { APIClient } from '@d8d-appcontainer/api'
import { Auth } from '@d8d-appcontainer/auth';
import debug from "debug"
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
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 = {
app: debug('app:server'),
auth: debug('auth:server'),
api: debug('api:server'),
debug: debug('debug:server')
}
const GLOBAL_CONFIG: GlobalConfig = {
OSS_BASE_URL: Deno.env.get('OSS_BASE_URL') || 'https://d8d-appcontainer-user.oss-cn-beijing.aliyuncs.com',
OSS_TYPE: Deno.env.get('OSS_TYPE') === OssType.MINIO ? OssType.MINIO : OssType.ALIYUN,
API_BASE_URL: '/api',
APP_NAME: Deno.env.get('APP_NAME') || '应用Starter',
ENV: Deno.env.get('ENV') || 'development',
DEFAULT_THEME: 'light', // 默认主题
MAP_CONFIG: {
KEY: Deno.env.get('AMAP_KEY') || '您的地图API密钥',
VERSION: '2.0',
PLUGINS: ['AMap.ToolBar', 'AMap.Scale', 'AMap.HawkEye', 'AMap.MapType', 'AMap.Geolocation'],
MAP_MODE: Deno.env.get('MAP_MODE') === MapMode.OFFLINE ? MapMode.OFFLINE : MapMode.ONLINE,
},
CHART_THEME: 'default', // 图表主题
ENABLE_THEME_CONFIG: false, // 主题配置开关
THEME: null
};
log.app.enabled = true
log.auth.enabled = true
log.api.enabled = true
log.debug.enabled = true
interface EsmScriptConfig {
src: string
href: string
denoJson: string
refresh: boolean
prodPath?: string
prodSrc?: string
}
// 定义模块参数接口
interface ModuleParams {
apiClient: APIClient,
auth: Auth,
app: Hono
moduleDir: string
}
export default function({ apiClient, app, moduleDir , auth}: ModuleParams) {
const honoApp = app
// 创建路由
const router = createRouter(apiClient, moduleDir, auth)
honoApp.route('/', router)
// 首页路由 - SSR
honoApp.get('/', async (c: HonoContext) => {
const systemName = GLOBAL_CONFIG.APP_NAME
return c.html(
<html>
<head>
<title>{systemName}</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div className="min-h-screen bg-gray-50 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
{/* 系统介绍区域 */}
<div className="text-center">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
{systemName}
</h1>
<p className="text-lg text-gray-600 mb-8">
Starter
</p>
<p className="text-base text-gray-500 mb-8">
Hono和React的应用Starter
</p>
</div>
{/* 管理入口按钮 */}
<div className="space-y-4">
<a
href="/admin"
className="w-full flex justify-center py-3 px-4 border border-transparent rounded-md shadow-sm text-lg font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
</a>
{/* 移动端入口按钮 */}
<a
href="/mobile"
className="w-full flex justify-center py-3 px-4 border border-blue-600 rounded-md shadow-sm text-lg font-medium text-blue-600 bg-white hover:bg-blue-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
</a>
{/* 迁移管理入口按钮 */}
<a
href="/migrations"
className="w-full flex justify-center py-3 px-4 border border-red-600 rounded-md shadow-sm text-lg font-medium text-red-600 bg-white hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
>
</a>
</div>
</div>
</div>
</body>
</html>
)
})
// 创建一个函数用于生成包含全局配置的HTML页面
const createHtmlWithConfig = (scriptConfig: EsmScriptConfig, title = '应用Starter') => {
return (c: HonoContext) => {
const isProd = GLOBAL_CONFIG.ENV === 'production';
const isLocalDeploy = Deno.env.get('IS_LOCAL_DEPLOY') === 'true';
return c.html(
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{isProd && <meta name="version" content={Deno.env.get('VERSION') || '0.1.0'} />}
<title>{title}</title>
{isLocalDeploy ? (
<script type="module" src={scriptConfig.prodSrc || `/client_dist/${scriptConfig.prodPath}`}></script>
) : (
<script src={scriptConfig.src} href={scriptConfig.href} deno-json={scriptConfig.denoJson}
{...isProd ? {}:{ refresh: true }}
></script>
)}
{isLocalDeploy ? (<script src="/tailwindcss@3.4.16/index.js"></script>) : (<script src="https://cdn.tailwindcss.com"></script>)}
<script dangerouslySetInnerHTML={{ __html: `window.CONFIG = ${JSON.stringify(GLOBAL_CONFIG)};` }} />
{isProd && (
<>
<script src="https://ai-oss.d8d.fun/umd/vconsole.3.15.1.min.js"></script>
<script dangerouslySetInnerHTML={{ __html: `
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('vconsole')) {
var vConsole = new VConsole({
theme: urlParams.get('vconsole_theme') || 'light',
onReady: function() {
console.log('vConsole is ready');
}
});
}
`}} />
</>
)}
{!isProd && (
<>
<script src="https://ai-oss.d8d.fun/umd/vconsole.3.15.1.min.js"></script>
<script dangerouslySetInnerHTML={{ __html: `
const urlParams = new URLSearchParams(window.location.search);
var vConsole = new VConsole({
theme: urlParams.get('vconsole_theme') || 'light',
onReady: function() {
console.log('vConsole is ready');
}
});
`}} />
</>
)}
</head>
<body className="bg-gray-50">
<div id="root"></div>
</body>
</html>
)
}
}
// 后台管理路由
honoApp.get('/admin', createHtmlWithConfig({
src: "https://esm.d8d.fun/xb",
href: "/client/admin/web_app.tsx",
denoJson: "/deno.json",
refresh: true,
prodPath: "admin/web_app.js"
}, GLOBAL_CONFIG.APP_NAME))
honoApp.get('/admin/*', createHtmlWithConfig({
src: "https://esm.d8d.fun/xb",
href: "/client/admin/web_app.tsx",
denoJson: "/deno.json",
refresh: true,
prodPath: "admin/web_app.js"
}, GLOBAL_CONFIG.APP_NAME))
// 移动端路由
honoApp.get('/mobile', createHtmlWithConfig({
src: "https://esm.d8d.fun/xb",
href: "/client/mobile/mobile_app.tsx",
denoJson: "/deno.json",
refresh: true,
prodPath: "mobile/mobile_app.js"
}, GLOBAL_CONFIG.APP_NAME))
honoApp.get('/mobile/*', createHtmlWithConfig({
src: "https://esm.d8d.fun/xb",
href: "/client/mobile/mobile_app.tsx",
denoJson: "/deno.json",
refresh: true,
prodPath: "mobile/mobile_app.js"
}, GLOBAL_CONFIG.APP_NAME))
// 迁移管理路由
honoApp.get('/migrations', createHtmlWithConfig({
src: "https://esm.d8d.fun/xb",
href: "/client/migrations/migrations_app.tsx",
denoJson: "/deno.json",
refresh: true,
prodPath: "migrations/migrations_app.js"
}, GLOBAL_CONFIG.APP_NAME))
honoApp.get('/migrations/*', createHtmlWithConfig({
src: "https://esm.d8d.fun/xb",
href: "/client/migrations/migrations_app.tsx",
denoJson: "/deno.json",
refresh: true,
prodPath: "migrations/migrations_app.js"
}, GLOBAL_CONFIG.APP_NAME))
const staticRoutes = serveStatic({
root: moduleDir,
onFound: async (path: string, c: HonoContext) => {
const fileExt = path.split('.').pop()?.toLowerCase()
if (fileExt === 'tsx' || fileExt === 'ts') {
c.header('Content-Type', 'text/typescript; charset=utf-8')
} else if (fileExt === 'js' || fileExt === 'mjs') {
c.header('Content-Type', 'application/javascript; charset=utf-8')
} else if (fileExt === 'json') {
c.header('Content-Type', 'application/json; charset=utf-8')
} else if (fileExt === 'html') {
c.header('Content-Type', 'text/html; charset=utf-8')
} else if (fileExt === 'css') {
c.header('Content-Type', 'text/css; charset=utf-8')
} else if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(fileExt || '')) {
c.header('Content-Type', `image/${fileExt}`)
}
const fileInfo = await Deno.stat(path)
c.header('Last-Modified', fileInfo.mtime?.toUTCString() ?? new Date().toUTCString())
},
})
// 静态资源路由
honoApp.get('/deno.json', staticRoutes)
honoApp.get('/client/*', staticRoutes)
honoApp.get('/amap/*', staticRoutes)
honoApp.get('/tailwindcss@3.4.16/*', staticRoutes)
honoApp.get('/client_dist/*', staticRoutes)
return honoApp
}