feat: Add NSIS installer target and persistent SQLite storage

- Add NSIS installer as default target (--target nsis)
- Keep portable option available (--target portable)
- Store SQLite database in %APPDATA%\Meeting-Assistant for persistence
- Portable temp folder cleanup no longer affects SQLite data
- Update build-client.bat with --target parameter support
- Update DEPLOYMENT.md with new options and comparisons

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
egg
2025-12-18 08:13:32 +08:00
parent bc37a5392a
commit 08cffbb74a
4 changed files with 113 additions and 17 deletions

View File

@@ -238,11 +238,14 @@ npm start
### 打包方式
```batch
# Windows 全包打包(MySQL 雲端資料庫,預設
# Windows 全包打包(NSIS 安裝檔,推薦
.\scripts\build-client.bat build --embedded-backend --clean
# Windows 全包打包SQLite 本地資料庫,適合離線/防火牆環境)
.\scripts\build-client.bat build --embedded-backend --database-type sqlite --clean
# Windows 全包打包Portable 免安裝,注意臨時資料夾限制)
.\scripts\build-client.bat build --embedded-backend --target portable --clean
```
**打包參數說明:**
@@ -251,8 +254,18 @@ npm start
|------|------|
| `--embedded-backend` | 啟用內嵌後端模式 |
| `--database-type TYPE` | 資料庫類型:`mysql`(雲端)或 `sqlite`(本地) |
| `--target TARGET` | 打包目標:`nsis`(安裝檔,預設)或 `portable`(免安裝) |
| `--clean` | 建置前清理所有暫存檔案 |
### 打包目標比較
| 特性 | NSIS 安裝檔(推薦) | Portable 免安裝 |
|------|---------------------|-----------------|
| 安裝方式 | 執行安裝精靈 | 直接執行 |
| 資料持久性 | ✅ 安裝目錄內持久保存 | ⚠️ 臨時資料夾關閉後清空 |
| SQLite 位置 | 安裝目錄/data/ | %APPDATA%\Meeting-Assistant |
| 適用場景 | 正式部署 | 快速測試、展示 |
### config.json 配置
全包模式需要在 `config.json` 中配置資料庫和 API 金鑰:
@@ -334,7 +347,9 @@ npm start
}
```
> **注意**SQLite 模式下,資料庫檔案位於 `%TEMP%\Meeting-Assistant\resources\backend\backend\data\meeting.db`
> **注意**SQLite 資料庫位置會根據打包目標不同:
> - **NSIS 安裝檔**:安裝目錄內的 `data/meeting.db`
> - **Portable**`%APPDATA%\Meeting-Assistant\data\meeting.db`(持久保存,不會因關閉程式而清空)
### 配置說明
@@ -359,12 +374,14 @@ npm start
| 適用場景 | 企業部署、多人共用 | 離線環境、防火牆限制 |
| 資料備份 | 使用資料庫工具 | 複製 `.db` 檔案即可 |
### Portable 執行檔解壓縮位置
### Portable 執行檔說明
Portable exe 執行時會解壓縮到 `%TEMP%\Meeting-Assistant` 資料夾(固定路徑,非隨機資料夾)。
- **優點**Windows Defender 不會每次都提示警告
- **SQLite 資料庫位置**`%TEMP%\Meeting-Assistant\resources\backend\backend\data\meeting.db`
- **注意**:關閉程式後,臨時資料夾會被清空
- **SQLite 資料庫位置**:自動儲存到 `%APPDATA%\Meeting-Assistant\data\meeting.db`(不會被清空)
- **建議**:正式部署請使用 NSIS 安裝檔(`--target nsis`,預設)
### 啟動流程
@@ -456,10 +473,13 @@ SHOW TABLES LIKE 'meeting_%';
SQLite 資料庫檔案會在首次啟動時自動建立:
- **開發環境**`backend/data/meeting.db`
- **打包後**`%TEMP%\Meeting-Assistant\resources\backend\backend\data\meeting.db`
- **NSIS 安裝檔**:安裝目錄內的 `data/meeting.db`
- **Portable**`%APPDATA%\Meeting-Assistant\data\meeting.db`
**備份方式**:直接複製 `.db` 檔案即可。
> **注意**Portable 模式下SQLite 資料庫自動儲存到 `%APPDATA%` 以確保持久性(不會因關閉程式而清空)。
### Expected tables
- `meeting_users`

View File

@@ -15,6 +15,30 @@ def get_base_dir() -> str:
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def get_app_data_dir() -> str:
"""Get persistent app data directory for storing user data.
This directory persists across application restarts, unlike temp folders
used by portable executables.
Returns:
Windows: %APPDATA%/Meeting-Assistant
macOS: ~/Library/Application Support/Meeting-Assistant
Linux: ~/.config/meeting-assistant
"""
if sys.platform == "win32":
# Windows: Use APPDATA
base = os.environ.get("APPDATA", os.path.expanduser("~"))
return os.path.join(base, "Meeting-Assistant")
elif sys.platform == "darwin":
# macOS: Use Application Support
return os.path.expanduser("~/Library/Application Support/Meeting-Assistant")
else:
# Linux: Use XDG config or fallback to ~/.config
xdg_config = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
return os.path.join(xdg_config, "meeting-assistant")
class Settings:
# Server Configuration
BACKEND_HOST: str = os.getenv("BACKEND_HOST", "0.0.0.0")
@@ -95,15 +119,27 @@ class Settings:
def get_sqlite_path(self, base_dir: str | None = None) -> str:
"""Get SQLite database file path, resolving relative paths.
For packaged executables (frozen), uses persistent app data directory
to survive portable exe cleanup. For development, uses relative path.
Args:
base_dir: Base directory for relative paths. If None, uses get_base_dir()
which supports frozen executables.
base_dir: Base directory for relative paths. If None, auto-detects.
"""
# If absolute path specified, use it directly
if self.SQLITE_PATH and os.path.isabs(self.SQLITE_PATH):
return self.SQLITE_PATH
# For frozen executables, use persistent app data directory
# This ensures SQLite data survives portable exe temp cleanup
if getattr(sys, "frozen", False):
app_data = get_app_data_dir()
db_name = os.path.basename(self.SQLITE_PATH) if self.SQLITE_PATH else "meeting.db"
return os.path.join(app_data, "data", db_name)
# For development, use relative path from base_dir
if base_dir is None:
base_dir = get_base_dir()
if self.SQLITE_PATH:
if os.path.isabs(self.SQLITE_PATH):
return self.SQLITE_PATH
return os.path.join(base_dir, self.SQLITE_PATH)
return os.path.join(base_dir, "data", "meeting.db")

View File

@@ -46,13 +46,21 @@
"win": {
"target": [
{
"target": "portable",
"target": "nsis",
"arch": ["x64"]
}
],
"icon": "assets/icon.ico",
"signAndEditExecutable": false
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"artifactName": "${productName}-${version}-setup.${ext}",
"installerIcon": "assets/icon.ico",
"uninstallerIcon": "assets/icon.ico",
"deleteAppDataOnUninstall": false
},
"mac": {
"target": [
{

View File

@@ -28,6 +28,7 @@ set "EMBEDDED_BACKEND=false"
set "CLEAN_BUILD=false"
set "API_URL="
set "DATABASE_TYPE="
set "BUILD_TARGET=nsis"
REM 解析參數
set "COMMAND=help"
@@ -44,6 +45,7 @@ if /i "%~1"=="--embedded-backend" (set "EMBEDDED_BACKEND=true" & set "SKIP_BACKE
if /i "%~1"=="--clean" (set "CLEAN_BUILD=true" & shift & goto :parse_args)
if /i "%~1"=="--api-url" (set "API_URL=%~2" & shift & shift & goto :parse_args)
if /i "%~1"=="--database-type" (set "DATABASE_TYPE=%~2" & shift & shift & goto :parse_args)
if /i "%~1"=="--target" (set "BUILD_TARGET=%~2" & shift & shift & goto :parse_args)
echo %RED%[ERROR]%NC% 未知參數: %~1
goto :show_help
@@ -399,7 +401,20 @@ echo %BLUE%[STEP]%NC% 打包 Electron 應用...
cd /d "%CLIENT_DIR%"
REM 驗證 BUILD_TARGET
if /i not "%BUILD_TARGET%"=="nsis" if /i not "%BUILD_TARGET%"=="portable" (
echo %RED%[ERROR]%NC% 無效的打包目標: %BUILD_TARGET%
echo %BLUE%[INFO]%NC% 有效選項: nsis, portable
exit /b 1
)
if /i "%BUILD_TARGET%"=="nsis" (
echo %BLUE%[INFO]%NC% 目標平台: Windows (NSIS 安裝檔) - 推薦
) else (
echo %BLUE%[INFO]%NC% 目標平台: Windows (Portable)
echo %YELLOW%[WARN]%NC% 注意: Portable 模式的臨時資料夾會在關閉時清空
echo %YELLOW%[WARN]%NC% SQLite 資料庫已自動儲存到 %%APPDATA%%\Meeting-Assistant
)
REM 清理可能損壞的 electron-builder 快取(解決 symlink 問題)
set "EB_CACHE=%LOCALAPPDATA%\electron-builder\Cache\winCodeSign"
@@ -412,9 +427,9 @@ echo %BLUE%[INFO]%NC% 執行 electron-builder...
REM 使用 npm run build 或直接執行 node_modules 中的 electron-builder
if exist "node_modules\.bin\electron-builder.cmd" (
call "node_modules\.bin\electron-builder.cmd" --win
call "node_modules\.bin\electron-builder.cmd" --win %BUILD_TARGET%
) else (
call npx electron-builder --win
call npx electron-builder --win %BUILD_TARGET%
)
if errorlevel 1 (
@@ -457,9 +472,19 @@ dir /b "%BUILD_DIR%"
echo.
echo %GREEN%[OK]%NC% 打包完成!
echo.
echo Windows 使用說明:
echo 1. 找到 build\ 中的 .exe 檔案
echo 2. 直接執行即可,無需安裝
if /i "%BUILD_TARGET%"=="nsis" (
echo Windows 使用說明 (NSIS 安裝檔):
echo 1. 找到 build\ 中的 *-setup.exe 檔案
echo 2. 執行安裝檔,選擇安裝目錄
echo 3. 安裝後從開始選單或桌面捷徑啟動
echo 4. 資料會持久保存在安裝目錄中
) else (
echo Windows 使用說明 (Portable):
echo 1. 找到 build\ 中的 *-portable.exe 檔案
echo 2. 直接執行,無需安裝
echo 3. 注意: 關閉程式後臨時檔案會清空
echo 4. SQLite 資料庫保存在 %%APPDATA%%\Meeting-Assistant
)
echo.
goto :eof
@@ -550,12 +575,14 @@ echo --skip-sidecar 跳過 Sidecar 打包
echo --skip-backend 跳過 Backend 打包 (預設)
echo --embedded-backend 打包內嵌後端 (全包部署模式)
echo --database-type TYPE 資料庫類型: mysql (雲端) 或 sqlite (本地)
echo --target TARGET 打包目標: nsis (安裝檔, 預設) 或 portable (免安裝)
echo --clean 建置前先清理
echo.
echo 範例:
echo %~nx0 build 完整建置 (前端+Sidecar)
echo %~nx0 build 完整建置 (NSIS 安裝檔)
echo %~nx0 build --embedded-backend 全包部署 (含內嵌後端)
echo %~nx0 build --embedded-backend --database-type sqlite 全包部署 + SQLite
echo %~nx0 build --target portable 打包為免安裝 Portable
echo %~nx0 build --api-url "http://192.168.1.100:8000/api" 指定遠端後端
echo %~nx0 sidecar 僅打包 Sidecar
echo %~nx0 electron --skip-sidecar 僅打包 Electron
@@ -564,6 +591,11 @@ echo 部署模式:
echo 分離部署(預設): 前端連接遠端後端,使用 --api-url 指定後端地址
echo 全包部署: 使用 --embedded-backend 將後端打包進 exe雙擊即可運行
echo.
echo 打包目標:
echo nsis預設: 產生安裝檔,安裝後資料持久保存,推薦正式使用
echo portable: 產生免安裝 exe臨時資料夾會在關閉時清空
echo SQLite 資料庫會自動儲存到 %%APPDATA%%\Meeting-Assistant
echo.
echo 資料庫模式:
echo MySQL預設: 連接雲端資料庫,需要網路存取
echo SQLite: 本地資料庫,適合離線或防火牆環境,使用 --database-type sqlite