ci: 添加 Docker 构建和推送工作流
- 新增 Dockerfile 和 .dockerignore 文件 - 添加 Gitea 持续集成工作流,用于构建和推送 Docker 镜像 - 新增 .gitignore 文件,忽略构建和配置文件 - 添加项目结构和规范文档,包括 TypeScript、模块化、API、数据库等规范 - 新增前端和后端的基础代码结构
This commit is contained in:
70
src/client/app.tsx
Normal file
70
src/client/app.tsx
Normal 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
|
||||
19
src/client/components/LanguageSwitcher.tsx
Normal file
19
src/client/components/LanguageSwitcher.tsx
Normal 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
19
src/client/i18n/config.ts
Normal 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;
|
||||
7
src/client/i18n/locales/en/translation.json
Normal file
7
src/client/i18n/locales/en/translation.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"translation": {
|
||||
"welcome": "Welcome",
|
||||
"language": "Language",
|
||||
"switch_language": "Switch Language"
|
||||
}
|
||||
}
|
||||
7
src/client/i18n/locales/zh/translation.json
Normal file
7
src/client/i18n/locales/zh/translation.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"translation": {
|
||||
"welcome": "欢迎",
|
||||
"language": "语言",
|
||||
"switch_language": "切换语言"
|
||||
}
|
||||
}
|
||||
5
src/client/i18next-loader.d.ts
vendored
Normal file
5
src/client/i18next-loader.d.ts
vendored
Normal 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
15
src/client/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user