fix(updater): запекаем токен в build для приватного Gitea repo
Some checks failed
CI / Typecheck + Tests (push) Has been cancelled
CI / Build (Windows) (push) Has been cancelled
Release / Build installer + publish release (push) Has been cancelled

Корень проблемы 404: репо приватный, Gitea требует Authorization
header для release assets даже на browser_download_url. Без токена
запрос возвращает 404 (не 401), поэтому electron-updater сообщал
"Cannot find channel latest.yml update info".

Решение — embed read-only токена в build:
- electron.vite.config.ts: vite `define` для __UPDATE_TOKEN__
  читает process.env.UPDATE_TOKEN на этапе сборки
- src/main/updater.ts: если __UPDATE_TOKEN__ непустой, выставляет
  autoUpdater.requestHeaders = { Authorization: 'token ...' }
- Декларация declare const локально в модуле, vite заменяет литерал

Сборка теперь требует:
  $env:UPDATE_TOKEN = '<gitea-token>'; npm run dist

Если переменная не задана — токен пустой, auto-update тихо отключается
(статус 'unsupported' не показывается, просто запросы будут падать).

Альтернатива на будущее (без токена в .exe):
1. Сделать репо публичным в Gitea Settings
2. Или сделать только Releases публичными если в этой версии Gitea
   есть такая опция
Тогда токен в коде не нужен.

== Важно для существующих пользователей ==

Установленные v0.2.x / v0.3.0-0.3.4 не могут получить апдейт
автоматически — у них в бинаре старый updater без токена и они
продолжат получать 404. Им нужно скачать v0.3.5 .exe вручную
и переустановить. После 0.3.5 auto-update заработает.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
AnRil
2026-05-17 18:42:58 +07:00
parent ee2dc19daa
commit 43ebc8d74c
3 changed files with 22 additions and 1 deletions

View File

@@ -2,9 +2,18 @@ import { resolve } from 'node:path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import react from '@vitejs/plugin-react'
// Bake the Gitea update token into the main bundle at build time so
// electron-updater can fetch latest.yml from the private repo. The token
// only needs read access to releases. Falls back to empty string if not set
// (dev/local builds), in which case auto-update is silently disabled.
const updateToken = JSON.stringify(process.env.UPDATE_TOKEN ?? '')
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()],
define: {
__UPDATE_TOKEN__: updateToken
},
resolve: {
alias: {
'@shared': resolve('src/shared')

View File

@@ -1,6 +1,6 @@
{
"name": "laude",
"version": "0.3.4",
"version": "0.3.5",
"description": "Exercise reminder — Windows desktop app",
"main": "out/main/index.js",
"author": "AnRil",

View File

@@ -3,6 +3,10 @@ import { autoUpdater } from 'electron-updater'
import { IPC } from '@shared/ipc'
import type { UpdaterStatus } from '@shared/types'
// Build-time defined in electron.vite.config.ts.
// Read-only Gitea token, used to fetch latest.yml from the private repo.
declare const __UPDATE_TOKEN__: string
let currentStatus: UpdaterStatus = { kind: 'idle' }
let wired = false
let checkInterval: NodeJS.Timeout | null = null
@@ -38,6 +42,14 @@ export function initUpdater(): void {
autoUpdater.autoInstallOnAppQuit = true
autoUpdater.allowDowngrade = false
// Gitea private-repo release assets require a token in Authorization
// header — without it Gitea responds 404 (not 401) on download URLs.
if (typeof __UPDATE_TOKEN__ === 'string' && __UPDATE_TOKEN__.length > 0) {
autoUpdater.requestHeaders = {
Authorization: `token ${__UPDATE_TOKEN__}`
}
}
autoUpdater.on('checking-for-update', () => setStatus({ kind: 'checking' }))
autoUpdater.on('update-available', (info) => {