首页添加了迁移管理入口按钮, 无需登录即可访问
This commit is contained in:
2
HISTORY.md
Normal file
2
HISTORY.md
Normal file
@@ -0,0 +1,2 @@
|
||||
2025.05.13 0.1.0
|
||||
首页添加了迁移管理入口按钮, 无需登录即可访问
|
||||
95
client/migrations/migrations_app.tsx
Normal file
95
client/migrations/migrations_app.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import React, { useState } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { Button, Space, Alert, Spin, Typography } from 'antd';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
QueryClient,
|
||||
QueryClientProvider,
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
// 创建QueryClient实例
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
interface MigrationResponse {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
failedResult?: any;
|
||||
}
|
||||
|
||||
const MigrationsApp: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [migrationResult, setMigrationResult] = useState<MigrationResponse | null>(null);
|
||||
|
||||
const runMigrations = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setMigrationResult(null);
|
||||
|
||||
const response = await axios.get('/api/migrations');
|
||||
setMigrationResult(response.data);
|
||||
} catch (error: any) {
|
||||
setMigrationResult({
|
||||
success: false,
|
||||
error: error.response?.data?.error || '数据库迁移失败',
|
||||
failedResult: error.response?.data?.failedResult
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<Title level={3}>数据库迁移管理</Title>
|
||||
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={runMigrations}
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
执行迁移
|
||||
</Button>
|
||||
|
||||
{loading && <Spin tip="迁移执行中..." />}
|
||||
|
||||
{migrationResult && (
|
||||
migrationResult.success ? (
|
||||
<Alert
|
||||
message="迁移成功"
|
||||
type="success"
|
||||
showIcon
|
||||
/>
|
||||
) : (
|
||||
<Alert
|
||||
message="迁移失败"
|
||||
description={
|
||||
<>
|
||||
<p>{migrationResult.error}</p>
|
||||
{migrationResult.failedResult && (
|
||||
<pre style={{ marginTop: 10 }}>
|
||||
{JSON.stringify(migrationResult.failedResult, null, 2)}
|
||||
</pre>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
type="error"
|
||||
showIcon
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染应用
|
||||
const root = createRoot(document.getElementById('root') as HTMLElement);
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MigrationsApp />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
@@ -347,6 +347,14 @@ export default function({ apiClient, app, moduleDir }: ModuleParams) {
|
||||
进入管理后台
|
||||
</a>
|
||||
|
||||
{/* 迁移管理入口按钮 */}
|
||||
<a
|
||||
href="/migrations"
|
||||
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="/mobile"
|
||||
@@ -366,21 +374,25 @@ export default function({ apiClient, app, moduleDir }: ModuleParams) {
|
||||
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>
|
||||
|
||||
{isProd ? (
|
||||
{isLocalDeploy ? (
|
||||
<script type="module" src={scriptConfig.prodSrc || `/client_dist/${scriptConfig.prodPath}`}></script>
|
||||
) : (
|
||||
<script src={scriptConfig.src} href={scriptConfig.href} deno-json={scriptConfig.denoJson} refresh={scriptConfig.refresh}></script>
|
||||
<script src={scriptConfig.src} href={scriptConfig.href} deno-json={scriptConfig.denoJson}
|
||||
{...isProd ? {}:{ refresh: true }}
|
||||
></script>
|
||||
)}
|
||||
|
||||
{isProd ? (<script src="/tailwindcss@3.4.16/index.js"></script>) : (<script src="https://cdn.tailwindcss.com"></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)};` }} />
|
||||
|
||||
@@ -436,14 +448,31 @@ export default function({ apiClient, app, moduleDir }: ModuleParams) {
|
||||
}, 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,
|
||||
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))
|
||||
|
||||
const staticRoutes = serveStatic({
|
||||
// 迁移管理路由
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user