feat(phase1): rebrand to TTRPG Player and drop Git updates feed

Rename product to TTRPG Player (TTRPGPlayer / com.ttrpgplayer.app), use .ttrpg.zip for new saves while keeping .dnd.zip import, accept TTRPG- and DND- license keys on client, and remove sync-update-feed plus CI push to DndGamePlayerUpdates.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-05-17 20:56:14 +08:00
parent 2c03921d23
commit 7c858ba633
27 changed files with 253 additions and 1328 deletions
+17 -5
View File
@@ -2,10 +2,16 @@ import crypto from 'node:crypto';
import fs from 'node:fs/promises';
import path from 'node:path';
import {
PROJECT_ZIP_EXTENSION,
PROJECT_ZIP_EXTENSION_LEGACY,
isProjectZipFileName,
} from '../../shared/project/projectZipExtension';
import { readProjectJsonFromZip } from './yauzlProjectZip';
/**
* Подменяет файл `finalPath` готовым `completedSrc` (обычно `*.dnd.zip.tmp`).
* Подменяет файл `finalPath` готовым `completedSrc` (обычно `*.ttrpg.zip.tmp` / `*.dnd.zip.tmp`).
* Нельзя сначала удалять `finalPath`: при сбое rename после rm проект теряется (Windows/антивирус).
*/
export async function replaceFileAtomic(completedSrc: string, finalPath: string): Promise<void> {
@@ -50,8 +56,10 @@ export async function replaceFileAtomic(completedSrc: string, finalPath: string)
await fs.unlink(backupPath).catch(() => undefined);
}
/** Если сохранение оборвалось, остаётся только `*.dnd.zip.tmp` — восстанавливаем в `*.dnd.zip`. */
export async function recoverOrphanDndZipTmpInRoot(root: string): Promise<void> {
const ZIP_TMP_SUFFIXES = [`${PROJECT_ZIP_EXTENSION}.tmp`, `${PROJECT_ZIP_EXTENSION_LEGACY}.tmp`] as const;
/** Если сохранение оборвалось, остаётся `*.ttrpg.zip.tmp` / `*.dnd.zip.tmp` — восстанавливаем финальный архив. */
export async function recoverOrphanProjectZipTmpInRoot(root: string): Promise<void> {
let names: string[];
try {
names = await fs.readdir(root);
@@ -59,10 +67,11 @@ export async function recoverOrphanDndZipTmpInRoot(root: string): Promise<void>
return;
}
for (const name of names) {
if (!name.endsWith('.dnd.zip.tmp')) continue;
const matchedSuffix = ZIP_TMP_SUFFIXES.find((s) => name.endsWith(s));
if (!matchedSuffix) continue;
const tmpPath = path.join(root, name);
const finalName = name.slice(0, -'.tmp'.length);
if (!finalName.endsWith('.dnd.zip')) continue;
if (!isProjectZipFileName(finalName)) continue;
const finalPath = path.join(root, finalName);
try {
await fs.access(finalPath);
@@ -80,3 +89,6 @@ export async function recoverOrphanDndZipTmpInRoot(root: string): Promise<void>
}
}
}
/** @deprecated Используйте {@link recoverOrphanProjectZipTmpInRoot}. */
export const recoverOrphanDndZipTmpInRoot = recoverOrphanProjectZipTmpInRoot;