From 43ebc8d74cd86efa19b646d17cc1f59d2a17de33 Mon Sep 17 00:00:00 2001 From: AnRil Date: Sun, 17 May 2026 18:42:58 +0700 Subject: [PATCH] =?UTF-8?q?fix(updater):=20=D0=B7=D0=B0=D0=BF=D0=B5=D0=BA?= =?UTF-8?q?=D0=B0=D0=B5=D0=BC=20=D1=82=D0=BE=D0=BA=D0=B5=D0=BD=20=D0=B2=20?= =?UTF-8?q?build=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D1=80=D0=B8=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D0=BD=D0=BE=D0=B3=D0=BE=20Gitea=20repo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Корень проблемы 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 = ''; 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 --- electron.vite.config.ts | 9 +++++++++ package.json | 2 +- src/main/updater.ts | 12 ++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/electron.vite.config.ts b/electron.vite.config.ts index b5f47f5..7dec5d1 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -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') diff --git a/package.json b/package.json index 6282581..b5b41ca 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/main/updater.ts b/src/main/updater.ts index c364e50..976f4ef 100644 --- a/src/main/updater.ts +++ b/src/main/updater.ts @@ -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) => {