ci: 添加 Docker 构建和推送工作流

- 新增 Dockerfile 和 .dockerignore 文件
- 添加 Gitea 持续集成工作流,用于构建和推送 Docker 镜像
- 新增 .gitignore 文件,忽略构建和配置文件
- 添加项目结构和规范文档,包括 TypeScript、模块化、API、数据库等规范
- 新增前端和后端的基础代码结构
This commit is contained in:
D8D Developer
2025-06-11 09:35:39 +00:00
commit 71aaeb9424
70 changed files with 4170 additions and 0 deletions

70
src/client/app.tsx Normal file
View File

@@ -0,0 +1,70 @@
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { hc } from 'hono/client'
import { useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import type { BaseRoutes } from '../server/api/base'
import type { UserRoutes } from '../server/api/user'
import './i18n/config'
import LanguageSwitcher from './components/LanguageSwitcher'
const client = hc<BaseRoutes>('/api')
const userClient = hc<UserRoutes['createUser']>('/api')
const Home = () => {
const { t } = useTranslation()
return (
<div>
<h1>{t('welcome')}</h1>
<LanguageSwitcher />
</div>
)
}
const About = () => {
return (
<div>
<h1>About Page</h1>
<p>This is the about page.</p>
</div>
)
}
const ApiDemo = () => {
const { data, isLoading, error } = useQuery({
queryKey: ['apiData'],
queryFn: async () => {
const res = await client.index.$get({ query: { name: 'Hono' } })
return res.json()
}
})
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return <h2 className="text-2xl">{data?.message}</h2>
}
const router = createBrowserRouter([
{
path: '/',
element: <Home />,
},
{
path: '/about',
element: <About />,
},
{
path: '/api-demo',
element: <ApiDemo />,
},
])
const App = () => {
return (
<>
<RouterProvider router={router} />
</>
)
}
export default App

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
const LanguageSwitcher: React.FC = () => {
const { t, i18n } = useTranslation();
const changeLanguage = (lng: string) => {
i18n.changeLanguage(lng);
};
return (
<div className="language-switcher">
<button onClick={() => changeLanguage('en')}>English</button>
<button onClick={() => changeLanguage('zh')}></button>
</div>
);
};
export default LanguageSwitcher;

19
src/client/i18n/config.ts Normal file
View File

@@ -0,0 +1,19 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import resources from 'virtual:i18next-loader';
// 初始化i18n配置
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
debug: process.env.NODE_ENV === 'development',
interpolation: {
escapeValue: false, // React已经处理XSS防护
},
resources
});
export default i18n;

View File

@@ -0,0 +1,7 @@
{
"translation": {
"welcome": "Welcome",
"language": "Language",
"switch_language": "Switch Language"
}
}

View File

@@ -0,0 +1,7 @@
{
"translation": {
"welcome": "欢迎",
"language": "语言",
"switch_language": "切换语言"
}
}

5
src/client/i18next-loader.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
declare module 'virtual:i18next-loader' {
import { Resource } from 'i18next';
const resources: Resource;
export default resources;
}

15
src/client/index.tsx Normal file
View File

@@ -0,0 +1,15 @@
import { createRoot } from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import App from './app'
const queryClient = new QueryClient()
const rootElement = document.getElementById('root')
if (rootElement) {
const root = createRoot(rootElement)
root.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
)
}