Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -51,23 +51,23 @@ curl https://your-domain.com/version.json
|
||||
|
||||
```typescript
|
||||
// Node.js / Elysia 示例
|
||||
app.get('/api/app/version', async (ctx) => {
|
||||
app.get("/api/app/version", async (ctx) => {
|
||||
// 从前端静态资源读取版本
|
||||
const response = await fetch('https://your-frontend-domain.com/version.json')
|
||||
const frontendVersion = await response.json()
|
||||
|
||||
const { platform, currentVersion } = ctx.query
|
||||
|
||||
const response = await fetch("https://your-frontend-domain.com/version.json");
|
||||
const frontendVersion = await response.json();
|
||||
|
||||
const { platform, currentVersion } = ctx.query;
|
||||
|
||||
return {
|
||||
version: frontendVersion.version,
|
||||
forceUpdate: compareVersion(currentVersion, frontendVersion.version) < 0,
|
||||
updateMessage: '修复了一些问题',
|
||||
updateUrl: platform === 'ios'
|
||||
? 'https://apps.apple.com/app/xxx'
|
||||
: 'https://play.google.com/store/apps/details?id=xxx',
|
||||
minSupportVersion: '0.0.1',
|
||||
}
|
||||
})
|
||||
updateMessage: "修复了一些问题",
|
||||
updateUrl: platform === "ios"
|
||||
? "https://apps.apple.com/app/xxx"
|
||||
: "https://play.google.com/store/apps/details?id=xxx",
|
||||
minSupportVersion: "0.0.1",
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
**方式 2:部署时同步到后端**
|
||||
@@ -88,18 +88,18 @@ app.get('/api/app/version', async (ctx) => {
|
||||
后端直接读取本地文件:
|
||||
|
||||
```typescript
|
||||
import fs from 'fs'
|
||||
import fs from "node:fs";
|
||||
|
||||
app.get("/api/app/version", async (ctx) => {
|
||||
const versionFile = fs.readFileSync("/app/frontend-version.json", "utf-8");
|
||||
const { version } = JSON.parse(versionFile);
|
||||
|
||||
app.get('/api/app/version', async (ctx) => {
|
||||
const versionFile = fs.readFileSync('/app/frontend-version.json', 'utf-8')
|
||||
const { version } = JSON.parse(versionFile)
|
||||
|
||||
return {
|
||||
version,
|
||||
forceUpdate: false,
|
||||
// ...
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
@@ -112,7 +112,7 @@ app.get('/api/app/version', async (ctx) => {
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.2.3" // 只改这里
|
||||
"version": "1.2.3" // 只改这里
|
||||
}
|
||||
```
|
||||
|
||||
@@ -162,27 +162,27 @@ versionName "1.2.3" // 与 package.json 保持一致
|
||||
|
||||
```javascript
|
||||
#!/usr/bin/env node
|
||||
import fs from 'fs'
|
||||
import { execSync } from 'child_process'
|
||||
import { execSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf-8'))
|
||||
const version = packageJson.version
|
||||
const packageJson = JSON.parse(fs.readFileSync("./package.json", "utf-8"));
|
||||
const version = packageJson.version;
|
||||
|
||||
console.log(`Syncing version ${version} to native projects...`)
|
||||
console.log(`Syncing version ${version} to native projects...`);
|
||||
|
||||
// iOS
|
||||
const infoPlist = './ios/App/App/Info.plist'
|
||||
let plistContent = fs.readFileSync(infoPlist, 'utf-8')
|
||||
const infoPlist = "./ios/App/App/Info.plist";
|
||||
let plistContent = fs.readFileSync(infoPlist, "utf-8");
|
||||
plistContent = plistContent.replace(
|
||||
/<key>CFBundleShortVersionString<\/key>\s*<string>.*?<\/string>/,
|
||||
`<key>CFBundleShortVersionString</key>\n\t<string>${version}</string>`
|
||||
)
|
||||
fs.writeFileSync(infoPlist, plistContent)
|
||||
);
|
||||
fs.writeFileSync(infoPlist, plistContent);
|
||||
|
||||
// Android (需要安装 gradle 解析库或手动更新)
|
||||
console.log('Please manually update android/app/build.gradle versionName')
|
||||
console.log("Please manually update android/app/build.gradle versionName");
|
||||
|
||||
console.log('✓ Version synced!')
|
||||
console.log("✓ Version synced!");
|
||||
```
|
||||
|
||||
在 `package.json` 中添加脚本:
|
||||
@@ -237,24 +237,24 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
node-version: "18"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
env:
|
||||
NODE_ENV: production
|
||||
|
||||
|
||||
- name: Deploy to server
|
||||
run: |
|
||||
rsync -avz dist/ user@server:/var/www/html/
|
||||
|
||||
|
||||
- name: Notify backend
|
||||
run: |
|
||||
curl -X POST https://your-backend.com/api/webhook/frontend-deployed \
|
||||
@@ -284,107 +284,108 @@ jobs:
|
||||
|
||||
**一次配置,永久受益!** 🎉
|
||||
|
||||
|
||||
## 后端 API 实现示例 - 版本检查接口
|
||||
|
||||
```ts
|
||||
import { Elysia, t } from 'elysia'
|
||||
// 方案二:从本地文件读取(适用于前后端部署在同一服务器)
|
||||
import fs from "node:fs";
|
||||
|
||||
import { Elysia, t } from "elysia";
|
||||
|
||||
// 版本比较工具函数
|
||||
function compareVersion(version1: string, version2: string): number {
|
||||
const v1Parts = version1.split('.').map(Number)
|
||||
const v2Parts = version2.split('.').map(Number)
|
||||
const maxLength = Math.max(v1Parts.length, v2Parts.length)
|
||||
const v1Parts = version1.split(".").map(Number);
|
||||
const v2Parts = version2.split(".").map(Number);
|
||||
const maxLength = Math.max(v1Parts.length, v2Parts.length);
|
||||
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
const v1Part = v1Parts[i] || 0
|
||||
const v2Part = v2Parts[i] || 0
|
||||
const v1Part = v1Parts[i] || 0;
|
||||
const v2Part = v2Parts[i] || 0;
|
||||
|
||||
if (v1Part < v2Part) return -1
|
||||
if (v1Part > v2Part) return 1
|
||||
if (v1Part < v2Part)
|
||||
return -1;
|
||||
if (v1Part > v2Part)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 方案一:从前端静态资源读取版本(推荐)
|
||||
async function getFrontendVersionFromURL(): Promise<{
|
||||
version: string
|
||||
buildTime: string
|
||||
gitCommit: string
|
||||
environment: string
|
||||
version: string;
|
||||
buildTime: string;
|
||||
gitCommit: string;
|
||||
environment: string;
|
||||
}> {
|
||||
const response = await fetch('https://your-frontend-domain.com/version.json')
|
||||
return await response.json()
|
||||
const response = await fetch("https://your-frontend-domain.com/version.json");
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// 方案二:从本地文件读取(适用于前后端部署在同一服务器)
|
||||
import fs from 'fs'
|
||||
|
||||
function getFrontendVersionFromFile(): {
|
||||
version: string
|
||||
buildTime: string
|
||||
gitCommit: string
|
||||
environment: string
|
||||
version: string;
|
||||
buildTime: string;
|
||||
gitCommit: string;
|
||||
environment: string;
|
||||
} {
|
||||
const content = fs.readFileSync('/app/frontend-version.json', 'utf-8')
|
||||
return JSON.parse(content)
|
||||
const content = fs.readFileSync("/app/frontend-version.json", "utf-8");
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
// 应用商店链接配置
|
||||
const APP_STORE_URLS = {
|
||||
ios: 'https://apps.apple.com/app/id123456789',
|
||||
android: 'https://play.google.com/store/apps/details?id=riwa.ionic.app',
|
||||
}
|
||||
ios: "https://apps.apple.com/app/id123456789",
|
||||
android: "https://play.google.com/store/apps/details?id=riwa.ionic.app",
|
||||
};
|
||||
|
||||
// 版本策略配置(可存储在数据库)
|
||||
const VERSION_POLICIES = {
|
||||
minSupportVersion: '0.0.1', // 最低支持版本
|
||||
forceUpdateVersions: ['0.0.1'], // 需要强制更新的版本列表
|
||||
minSupportVersion: "0.0.1", // 最低支持版本
|
||||
forceUpdateVersions: ["0.0.1"], // 需要强制更新的版本列表
|
||||
updateMessages: {
|
||||
'zh-CN': '修复了一些问题并优化了性能',
|
||||
'en-US': 'Bug fixes and performance improvements',
|
||||
"zh-CN": "修复了一些问题并优化了性能",
|
||||
"en-US": "Bug fixes and performance improvements",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Elysia 路由定义
|
||||
const app = new Elysia()
|
||||
.get(
|
||||
'/api/app/version',
|
||||
"/api/app/version",
|
||||
async ({ query, headers }) => {
|
||||
const { platform, currentVersion } = query
|
||||
const lang = headers['accept-language']?.split(',')[0] || 'en-US'
|
||||
const { platform, currentVersion } = query;
|
||||
const lang = headers["accept-language"]?.split(",")[0] || "en-US";
|
||||
|
||||
try {
|
||||
// 获取前端版本信息
|
||||
const frontendVersion = await getFrontendVersionFromURL()
|
||||
const frontendVersion = await getFrontendVersionFromURL();
|
||||
// 或使用本地文件: const frontendVersion = getFrontendVersionFromFile()
|
||||
|
||||
// 判断是否需要更新
|
||||
const hasUpdate = compareVersion(currentVersion, frontendVersion.version) < 0
|
||||
const hasUpdate = compareVersion(currentVersion, frontendVersion.version) < 0;
|
||||
|
||||
// 判断是否强制更新
|
||||
let forceUpdate = VERSION_POLICIES.forceUpdateVersions.includes(currentVersion)
|
||||
|
||||
let forceUpdate = VERSION_POLICIES.forceUpdateVersions.includes(currentVersion);
|
||||
|
||||
// 检查是否低于最低支持版本
|
||||
if (compareVersion(currentVersion, VERSION_POLICIES.minSupportVersion) < 0) {
|
||||
forceUpdate = true
|
||||
forceUpdate = true;
|
||||
}
|
||||
|
||||
// 获取更新链接
|
||||
const updateUrl = platform === 'ios'
|
||||
? APP_STORE_URLS.ios
|
||||
: platform === 'android'
|
||||
const updateUrl = platform === "ios"
|
||||
? APP_STORE_URLS.ios
|
||||
: platform === "android"
|
||||
? APP_STORE_URLS.android
|
||||
: ''
|
||||
: "";
|
||||
|
||||
// 获取更新说明(多语言)
|
||||
const updateMessage = VERSION_POLICIES.updateMessages[lang]
|
||||
|| VERSION_POLICIES.updateMessages['en-US']
|
||||
const updateMessage = VERSION_POLICIES.updateMessages[lang]
|
||||
|| VERSION_POLICIES.updateMessages["en-US"];
|
||||
|
||||
return {
|
||||
version: frontendVersion.version,
|
||||
buildNumber: parseInt(frontendVersion.version.replace(/\./g, '')),
|
||||
buildNumber: Number.parseInt(frontendVersion.version.replace(/\./g, "")),
|
||||
buildTime: frontendVersion.buildTime,
|
||||
gitCommit: frontendVersion.gitCommit,
|
||||
forceUpdate,
|
||||
@@ -392,26 +393,27 @@ const app = new Elysia()
|
||||
updateUrl,
|
||||
minSupportVersion: VERSION_POLICIES.minSupportVersion,
|
||||
releaseNotes: [
|
||||
'修复了已知问题',
|
||||
'优化了应用性能',
|
||||
'改进了用户界面',
|
||||
"修复了已知问题",
|
||||
"优化了应用性能",
|
||||
"改进了用户界面",
|
||||
],
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get frontend version:', error)
|
||||
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Failed to get frontend version:", error);
|
||||
|
||||
// 降级处理:返回当前版本,不强制更新
|
||||
return {
|
||||
version: currentVersion,
|
||||
forceUpdate: false,
|
||||
updateMessage: '',
|
||||
updateUrl: '',
|
||||
}
|
||||
updateMessage: "",
|
||||
updateUrl: "",
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
query: t.Object({
|
||||
platform: t.Union([t.Literal('ios'), t.Literal('android'), t.Literal('web')]),
|
||||
platform: t.Union([t.Literal("ios"), t.Literal("android"), t.Literal("web")]),
|
||||
currentVersion: t.String(),
|
||||
}),
|
||||
response: t.Object({
|
||||
@@ -426,13 +428,13 @@ const app = new Elysia()
|
||||
releaseNotes: t.Optional(t.Array(t.String())),
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export default app
|
||||
export default app;
|
||||
|
||||
/**
|
||||
* 使用示例:
|
||||
*
|
||||
*
|
||||
* 1. 启动后端服务
|
||||
* 2. 前端请求:GET /api/app/version?platform=ios¤tVersion=0.0.1
|
||||
* 3. 后端响应:
|
||||
@@ -448,9 +450,9 @@ export default app
|
||||
|
||||
/**
|
||||
* 数据库存储方案(可选)
|
||||
*
|
||||
*
|
||||
* 如果需要更灵活的版本策略管理,可以将配置存储在数据库:
|
||||
*
|
||||
*
|
||||
* CREATE TABLE app_versions (
|
||||
* id SERIAL PRIMARY KEY,
|
||||
* platform VARCHAR(20) NOT NULL,
|
||||
@@ -463,12 +465,11 @@ export default app
|
||||
* release_notes JSONB,
|
||||
* created_at TIMESTAMP DEFAULT NOW()
|
||||
* );
|
||||
*
|
||||
*
|
||||
* 然后从数据库查询版本策略:
|
||||
* const policy = await db.query(
|
||||
* 'SELECT * FROM app_versions WHERE platform = $1 ORDER BY created_at DESC LIMIT 1',
|
||||
* [platform]
|
||||
* )
|
||||
*/
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user