278 lines
10 KiB
TypeScript
278 lines
10 KiB
TypeScript
/** @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
|
||
}
|