chore: git remote 迁移至 git.51yanmei.com,新增 Android 平台支持
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
.cxx/
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/to/reference-keystore
|
||||||
|
key.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
id("kotlin-android")
|
||||||
|
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||||
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.yanmei.jiu"
|
||||||
|
compileSdk = flutter.compileSdkVersion
|
||||||
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_17.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId = "com.yanmei.jiu"
|
||||||
|
// You can update the following values to match your application needs.
|
||||||
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
|
minSdk = flutter.minSdkVersion
|
||||||
|
targetSdk = flutter.targetSdkVersion
|
||||||
|
versionCode = flutter.versionCode
|
||||||
|
versionName = flutter.versionName
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source = "../.."
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<application
|
||||||
|
android:label="jiu_client"
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
<!-- Required to query activities that can process text, see:
|
||||||
|
https://developer.android.com/training/package-visibility and
|
||||||
|
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||||
|
|
||||||
|
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||||
|
<data android:mimeType="text/plain"/>
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.yanmei.jiu
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity : FlutterActivity()
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="?android:colorBackground" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
Binary file not shown.
|
After Width: | Height: | Size: 442 B |
Binary file not shown.
|
After Width: | Height: | Size: 721 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newBuildDir: Directory =
|
||||||
|
rootProject.layout.buildDirectory
|
||||||
|
.dir("../../build")
|
||||||
|
.get()
|
||||||
|
rootProject.layout.buildDirectory.value(newBuildDir)
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
|
||||||
|
project.layout.buildDirectory.value(newSubprojectBuildDir)
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(":app")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Delete>("clean") {
|
||||||
|
delete(rootProject.layout.buildDirectory)
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
android.useAndroidX=true
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
pluginManagement {
|
||||||
|
val flutterSdkPath =
|
||||||
|
run {
|
||||||
|
val properties = java.util.Properties()
|
||||||
|
file("local.properties").inputStream().use { properties.load(it) }
|
||||||
|
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||||
|
flutterSdkPath
|
||||||
|
}
|
||||||
|
|
||||||
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
|
id("com.android.application") version "8.11.1" apply false
|
||||||
|
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
include(":app")
|
||||||
+44
-118
@@ -10,9 +10,8 @@
|
|||||||
|
|
||||||
| 服务 | 地址 |
|
| 服务 | 地址 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| Gitea Web UI | `http://192.168.3.200:3000` |
|
| Gitea Web UI | `https://git.51yanmei.com` |
|
||||||
| Gitea SSH | `ssh://192.168.3.200:2222` |
|
| Actions 面板 | `https://git.51yanmei.com/wangjia/jiu/actions` |
|
||||||
| Actions 面板 | `http://192.168.3.200:3000/wangjia/jiu/actions` |
|
|
||||||
| 线上应用 | `https://jiu.51yanmei.com` |
|
| 线上应用 | `https://jiu.51yanmei.com` |
|
||||||
| 管理端 Web | `https://jiu.51yanmei.com/app/` |
|
| 管理端 Web | `https://jiu.51yanmei.com/app/` |
|
||||||
|
|
||||||
@@ -28,8 +27,8 @@ Runner 实际运行在**本机 Mac**(不是 NAS),通过 LaunchAgent 开机
|
|||||||
|------|------|
|
|------|------|
|
||||||
| `/Users/wangjia/bin/act_runner` | act_runner 二进制(v0.2.11) |
|
| `/Users/wangjia/bin/act_runner` | act_runner 二进制(v0.2.11) |
|
||||||
| `/Users/wangjia/bin/act_runner_start.sh` | 启动脚本(含 TCP 中继逻辑) |
|
| `/Users/wangjia/bin/act_runner_start.sh` | 启动脚本(含 TCP 中继逻辑) |
|
||||||
| `/Users/wangjia/bin/tcp_relay.py` | Python3 TCP 中继:`127.0.0.1:13000 → 192.168.3.200:3000` |
|
| `/Users/wangjia/bin/tcp_relay.py` | Python3 TCP 中继(旧内网方案,现已通过域名直连) |
|
||||||
| `/Users/wangjia/.act_runner` | Runner 注册信息(地址指向 `127.0.0.1:13000`) |
|
| `/Users/wangjia/.act_runner` | Runner 注册信息 |
|
||||||
| `/Users/wangjia/.act_runner_config.yaml` | Runner 配置(label: `mac:host`,capacity: 1) |
|
| `/Users/wangjia/.act_runner_config.yaml` | Runner 配置(label: `mac:host`,capacity: 1) |
|
||||||
| `~/Library/LaunchAgents/com.jiu.act-runner.plist` | LaunchAgent plist,开机自启 |
|
| `~/Library/LaunchAgents/com.jiu.act-runner.plist` | LaunchAgent plist,开机自启 |
|
||||||
| `/tmp/act_runner.log` | 运行日志 |
|
| `/tmp/act_runner.log` | 运行日志 |
|
||||||
@@ -38,12 +37,6 @@ Runner 实际运行在**本机 Mac**(不是 NAS),通过 LaunchAgent 开机
|
|||||||
|
|
||||||
`.gitea/workflows/deploy.yml` 和 `ci.yml` 中使用 `runs-on: mac`,对应 runner label `mac:host`。
|
`.gitea/workflows/deploy.yml` 和 `ci.yml` 中使用 `runs-on: mac`,对应 runner label `mac:host`。
|
||||||
|
|
||||||
### 为什么需要 TCP 中继(重要)
|
|
||||||
|
|
||||||
Shadowrocket(代理工具)的 Network Extension 会拦截 LaunchAgent 启动的 Go 二进制发出的 TCP 连接,即使 Shadowrocket 内已配置直连规则。现象:Go gRPC 报 `no route to host`,而同进程里的 Python3 socket 可以正常连接。
|
|
||||||
|
|
||||||
**解决方案:** 用 Python3 做透明 TCP 中继。`tcp_relay.py` 监听 `127.0.0.1:13000`,将连接转发到 `192.168.3.200:3000`。act_runner 的注册地址改为 `http://127.0.0.1:13000`,完全绕开 Shadowrocket 拦截。
|
|
||||||
|
|
||||||
### 检查 Runner 状态
|
### 检查 Runner 状态
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -51,7 +44,7 @@ Shadowrocket(代理工具)的 Network Extension 会拦截 LaunchAgent 启动
|
|||||||
tail -f /tmp/act_runner.log
|
tail -f /tmp/act_runner.log
|
||||||
|
|
||||||
# 确认进程在跑
|
# 确认进程在跑
|
||||||
ps aux | grep -E "act_runner|tcp_relay" | grep -v grep
|
ps aux | grep act_runner | grep -v grep
|
||||||
|
|
||||||
# 手动重启(LaunchAgent 会自动重启,一般不需要)
|
# 手动重启(LaunchAgent 会自动重启,一般不需要)
|
||||||
kill $(pgrep -f act_runner_start.sh)
|
kill $(pgrep -f act_runner_start.sh)
|
||||||
@@ -59,12 +52,10 @@ kill $(pgrep -f act_runner_start.sh)
|
|||||||
|
|
||||||
### Runner 不工作时的排查步骤
|
### Runner 不工作时的排查步骤
|
||||||
|
|
||||||
1. **确认 NAS 可达**:`curl -sf http://192.168.3.200:3000/api/v1/version`
|
1. **确认 Gitea 可达**:`curl -sf https://git.51yanmei.com/api/v1/version`
|
||||||
2. **确认 relay 在跑**:`ps aux | grep tcp_relay`
|
2. **查看最近日志**:`tail -30 /tmp/act_runner.log`
|
||||||
3. **确认 relay 端口监听**:`nc -zv 127.0.0.1 13000`
|
3. **手动测试**:`/Users/wangjia/bin/act_runner daemon --config ~/.act_runner_config.yaml`(从终端运行应直接成功)
|
||||||
4. **查看最近日志**:`tail -30 /tmp/act_runner.log`
|
4. **强制重启**:`kill $(pgrep -f act_runner_start.sh)` — LaunchAgent 会在几秒内自动重拉
|
||||||
5. **手动测试**:`/Users/wangjia/bin/act_runner daemon --config ~/.act_runner_config.yaml`(从终端运行应直接成功)
|
|
||||||
6. **强制重启**:`kill $(pgrep -f act_runner_start.sh)` — LaunchAgent 会在几秒内自动重拉
|
|
||||||
|
|
||||||
### 重新注册 Runner(token 失效时)
|
### 重新注册 Runner(token 失效时)
|
||||||
|
|
||||||
@@ -81,15 +72,12 @@ rm ~/.act_runner
|
|||||||
# 4. 注册(从终端运行,不要从 LaunchAgent)
|
# 4. 注册(从终端运行,不要从 LaunchAgent)
|
||||||
/Users/wangjia/bin/act_runner register \
|
/Users/wangjia/bin/act_runner register \
|
||||||
--no-interactive \
|
--no-interactive \
|
||||||
--instance http://192.168.3.200:3000 \
|
--instance https://git.51yanmei.com \
|
||||||
--token <新token> \
|
--token <新token> \
|
||||||
--name mac-runner \
|
--name mac-runner \
|
||||||
--labels mac:host
|
--labels mac:host
|
||||||
|
|
||||||
# 5. 把注册文件里的地址改回 127.0.0.1:13000
|
# 5. 重启(LaunchAgent 自动接管)
|
||||||
sed -i '' 's|http://192.168.3.200:3000|http://127.0.0.1:13000|' ~/.act_runner
|
|
||||||
|
|
||||||
# 6. 重启(LaunchAgent 自动接管)
|
|
||||||
kill $(pgrep -f act_runner_start.sh)
|
kill $(pgrep -f act_runner_start.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -101,19 +89,19 @@ kill $(pgrep -f act_runner_start.sh)
|
|||||||
|
|
||||||
### `deploy.yml` — 主部署流程
|
### `deploy.yml` — 主部署流程
|
||||||
|
|
||||||
**触发条件:** push 到 `main` 分支
|
**触发条件:** push `v*.*.*` tag
|
||||||
|
|
||||||
**步骤:**
|
**步骤:**
|
||||||
1. `go test ./...` — 跑后端测试
|
1. `compile.sh` — 编译 Go 后端(linux/amd64)+ Flutter Web,打包到 `dist/`
|
||||||
2. `flutter test` — 跑前端测试
|
2. `test.sh` — `go test` + `flutter analyze`
|
||||||
3. 编译后端:`GOOS=linux GOARCH=amd64 go build` 生成 `jiu-server`
|
3. `release.sh` — 解析 CHANGELOG.md,更新 version.yaml,创建 Forgejo Release,上传 3 个 asset
|
||||||
4. 编译 Flutter Web:`flutter build web --release --base-href=/app/`
|
4. `deploy.sh` — SSH 到 EC2 部署,完成后发 Telegram 通知
|
||||||
5. 创建 Forgejo Release(版本号格式 `vYYYYMMDD.HHMM`),上传 `jiu-server` 和 `web.tar.gz`
|
|
||||||
6. SSH 到 EC2 部署:
|
### `manual.yml` — 手动部署 / 回滚
|
||||||
- 停止 jiu 服务 → 替换二进制 → 启动 → 等待健康检查通过
|
|
||||||
- 替换 Flutter Web 静态文件到 `/opt/jiu/web/`
|
**触发条件:** Forgejo UI 手动触发(workflow_dispatch),输入目标 tag(如 `v1.0.0`)
|
||||||
- 同步营销站点到 `/opt/jiu/marketing/`
|
|
||||||
- `nginx -s reload`
|
**步骤:** 只运行 `deploy.sh`,从 Forgejo Release 下载已有产物直接部署,跳过编译和测试。
|
||||||
|
|
||||||
### `ci.yml` — PR 测试流程
|
### `ci.yml` — PR 测试流程
|
||||||
|
|
||||||
@@ -121,12 +109,6 @@ kill $(pgrep -f act_runner_start.sh)
|
|||||||
|
|
||||||
**步骤:** 只跑测试(`go test` + `flutter test`),不构建不部署。
|
**步骤:** 只跑测试(`go test` + `flutter test`),不构建不部署。
|
||||||
|
|
||||||
### `backup.yml` — 每日备份
|
|
||||||
|
|
||||||
**触发条件:** 每天凌晨 2:00(cron)
|
|
||||||
|
|
||||||
**步骤:** SSH 到 EC2,导出 MySQL 数据库备份,上传到指定目录。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Secrets 配置
|
## Secrets 配置
|
||||||
@@ -138,26 +120,23 @@ kill $(pgrep -f act_runner_start.sh)
|
|||||||
| `EC2_SSH_KEY` | EC2 服务器的 SSH 私钥(PEM 格式,完整内容) |
|
| `EC2_SSH_KEY` | EC2 服务器的 SSH 私钥(PEM 格式,完整内容) |
|
||||||
| `EC2_HOST` | EC2 服务器 IP 或域名 |
|
| `EC2_HOST` | EC2 服务器 IP 或域名 |
|
||||||
| `EC2_USER` | EC2 SSH 用户名(如 `ec2-user`、`ubuntu`) |
|
| `EC2_USER` | EC2 SSH 用户名(如 `ec2-user`、`ubuntu`) |
|
||||||
| `DB_PASSWORD` | MySQL root 密码(备份 workflow 使用) |
|
| `FORGEJO_TOKEN` | Gitea 个人访问 Token(用于创建 Release、下载 asset) |
|
||||||
| `FORGEJO_TOKEN` | Gitea 个人访问 Token(用于创建 Release) |
|
| `FORGEJO_URL` | Gitea 地址:`https://git.51yanmei.com` |
|
||||||
| `FORGEJO_URL` | Gitea 内网地址,如 `http://192.168.3.200:3000` |
|
| `TELEGRAM_TOKEN` | Telegram Bot Token(部署通知) |
|
||||||
|
| `TELEGRAM_CHAT_ID` | Telegram Chat ID(接收通知的对话) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 触发部署方式
|
## 触发部署方式
|
||||||
|
|
||||||
日常开发流程:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 开发 → 本地测试
|
# 开发完成,确认测试通过后打 tag
|
||||||
go test ./...
|
git tag -a v1.1.0 -m "v1.1.0"
|
||||||
flutter test
|
git push nas main && git push nas v1.1.0
|
||||||
|
# → CI 自动:compile → test → release → deploy → Telegram 通知
|
||||||
# 用户确认后推送到 NAS Gitea(触发自动部署)
|
|
||||||
git push nas main
|
|
||||||
```
|
```
|
||||||
|
|
||||||
推送到 `nas` remote(指向 `http://192.168.3.200:3000/wangjia/jiu.git`)后,Gitea Actions 自动运行 `deploy.yml`,全流程约 5~8 分钟。
|
**回滚:** Forgejo UI → Actions → Manual Deploy → 输入旧 tag → Run
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -166,17 +145,16 @@ git push nas main
|
|||||||
```
|
```
|
||||||
/opt/jiu/
|
/opt/jiu/
|
||||||
├── backend/
|
├── backend/
|
||||||
│ └── jiu-server # Go 可执行文件
|
│ ├── jiu-server # Go 可执行文件
|
||||||
|
│ └── config/
|
||||||
|
│ └── version.yaml # 版本信息(由 CI 自动更新)
|
||||||
├── web/ # Flutter Web 构建产物(/app/ 路径)
|
├── web/ # Flutter Web 构建产物(/app/ 路径)
|
||||||
├── marketing/ # 营销站点静态 HTML
|
├── marketing/ # 营销站点静态 HTML
|
||||||
│ ├── assets/ # CSS / SVG
|
│ ├── assets/
|
||||||
│ ├── index.html
|
│ ├── index.html
|
||||||
│ ├── docs.html
|
│ ├── docs.html
|
||||||
│ ├── download.html
|
│ ├── download.html
|
||||||
│ ├── scan.html
|
│ └── scan.html
|
||||||
│ └── features/
|
|
||||||
│ ├── inventory.html
|
|
||||||
│ └── approval.html
|
|
||||||
├── images/ # 商品图片上传目录
|
├── images/ # 商品图片上传目录
|
||||||
└── web-old/ # 上一版本 Flutter Web(回滚备用)
|
└── web-old/ # 上一版本 Flutter Web(回滚备用)
|
||||||
```
|
```
|
||||||
@@ -188,64 +166,12 @@ Systemd 服务名:`jiu`
|
|||||||
|
|
||||||
## Nginx 路由说明
|
## Nginx 路由说明
|
||||||
|
|
||||||
配置文件:`deploy/nginx-jiu.conf`(部署时手动 scp 至服务器)
|
配置文件:`deploy/nginx-jiu.conf`(CI 部署时自动更新)
|
||||||
|
|
||||||
| 路径 | 服务 | 目录 |
|
| 路径 | 服务 |
|
||||||
|------|------|------|
|
|------|------|
|
||||||
| `/api/*`, `/health`, `/version` | Go 后端 API | proxy → `localhost:8080` |
|
| `/api/*`, `/health`, `/version` | Go 后端 API |
|
||||||
| `/images/*` | 商品图片 | `/opt/jiu/images/` |
|
| `/images/*` | 商品图片静态文件 |
|
||||||
| `/app/` | Flutter 管理端 SPA | `/opt/jiu/web/` |
|
| `/app/` | Flutter 管理端 SPA |
|
||||||
| `/` | 营销首页 | `/opt/jiu/marketing/index.html` |
|
| `/` | 营销首页 |
|
||||||
| `/docs` | 文档页 | `/opt/jiu/marketing/docs.html` |
|
| `/scan/<id>` | 商品扫码页 |
|
||||||
| `/download` | 下载页 | `/opt/jiu/marketing/download.html` |
|
|
||||||
| `/scan/<id>` | 商品防伪扫码页 | `/opt/jiu/marketing/scan.html` |
|
|
||||||
| `/features/*` | 功能介绍页 | `/opt/jiu/marketing/features/` |
|
|
||||||
| `/assets/*` | 营销站点静态资源 | `/opt/jiu/marketing/assets/` |
|
|
||||||
|
|
||||||
> **注意:** `nginx-jiu.conf` 只包含 `jiu.51yanmei.com` 的 server block。服务器上其他服务的 nginx 配置不受影响。更新 nginx 配置时只需替换此文件,然后 `nginx -t && nginx -s reload`。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 回滚方式
|
|
||||||
|
|
||||||
### 后端回滚
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# SSH 到 EC2
|
|
||||||
sudo systemctl stop jiu
|
|
||||||
# 从上一个 Forgejo Release 下载 jiu-server
|
|
||||||
cp /path/to/old-jiu-server /opt/jiu/backend/jiu-server
|
|
||||||
chmod +x /opt/jiu/backend/jiu-server
|
|
||||||
sudo systemctl start jiu
|
|
||||||
```
|
|
||||||
|
|
||||||
### Flutter Web 回滚
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 恢复上一版本
|
|
||||||
mv /opt/jiu/web /opt/jiu/web-broken
|
|
||||||
mv /opt/jiu/web-old /opt/jiu/web
|
|
||||||
sudo nginx -s reload
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 环境变量(jiu 服务)
|
|
||||||
|
|
||||||
Systemd service 文件(`/etc/systemd/system/jiu.service`)中配置:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Service]
|
|
||||||
Environment=DB_HOST=127.0.0.1
|
|
||||||
Environment=DB_PORT=3306
|
|
||||||
Environment=DB_NAME=jiu
|
|
||||||
Environment=DB_USER=jiu
|
|
||||||
Environment=DB_PASSWORD=<从 secrets 获取>
|
|
||||||
Environment=JWT_SECRET=<随机字符串>
|
|
||||||
Environment=APP_VERSION=1.0.0
|
|
||||||
Environment=BUILD_DATE=2025-05-23
|
|
||||||
Environment=DOWNLOAD_URL_MACOS=
|
|
||||||
Environment=DOWNLOAD_URL_WINDOWS=
|
|
||||||
```
|
|
||||||
|
|
||||||
`APP_VERSION` 和 `BUILD_DATE` 在每次部署时通过 deploy.yml 更新(预留,目前读取 Forgejo Release tag)。
|
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ cd "$(dirname "$0")/.."
|
|||||||
EC2_HOST="${EC2_HOST:-$(grep EC2_HOST ~/.env 2>/dev/null | cut -d= -f2)}"
|
EC2_HOST="${EC2_HOST:-$(grep EC2_HOST ~/.env 2>/dev/null | cut -d= -f2)}"
|
||||||
EC2_USER="${EC2_USER:-$(grep EC2_USER ~/.env 2>/dev/null | cut -d= -f2)}"
|
EC2_USER="${EC2_USER:-$(grep EC2_USER ~/.env 2>/dev/null | cut -d= -f2)}"
|
||||||
EC2_KEY="${EC2_KEY:-~/.ssh/wangjia.pem}"
|
EC2_KEY="${EC2_KEY:-~/.ssh/wangjia.pem}"
|
||||||
FORGEJO_URL="http://192.168.3.200:3000"
|
FORGEJO_URL="https://git.51yanmei.com"
|
||||||
FORGEJO_TOKEN="${FORGEJO_TOKEN:-$(grep FORGEJO_TOKEN ~/.env 2>/dev/null | cut -d= -f2)}"
|
FORGEJO_TOKEN="${FORGEJO_TOKEN:-$(grep FORGEJO_TOKEN ~/.env 2>/dev/null | cut -d= -f2)}"
|
||||||
|
|
||||||
echo "==> 1/5 Go 测试"
|
echo "==> 1/5 Go 测试"
|
||||||
|
|||||||
Reference in New Issue
Block a user