Rename blueprints to Collections and add Operations tab

Fixes #46
This commit is contained in:
2026-09-27 14:50:55 -04:00
parent df265d1900
commit a7044f21a1
17 changed files with 198 additions and 159 deletions
+6 -6
View File
@@ -43,7 +43,7 @@ function summarizeBlueprint(row: BlueprintRow): BlueprintSummary {
export function blueprintEntries(id: string): { entries: BlueprintEntry[]; blueprintName: string } {
const row = db().prepare('SELECT * FROM server_blueprints WHERE id = ?').get(id) as BlueprintRow | undefined;
if (!row) throw new Error('This blueprint is no longer available. Choose another starting point.');
if (!row) throw new Error('This collection is no longer available. Choose another starting point.');
const entries = readEntries(row.recipe).map(({ count, game, mapSlug }) => ({ count, recipe: captureRecipe(game, mapSlug) }));
return { entries, blueprintName: row.name };
}
@@ -60,7 +60,7 @@ function buildEntries(input: unknown): BlueprintSelection[] {
seen.add(key);
return { count: entry.count as number, game: entry.game as ZombieGame, mapSlug: entry.mapSlug as string };
});
if (entries.reduce((total, entry) => total + entry.count, 0) > 100) throw new Error('A blueprint can launch at most 100 servers.');
if (entries.reduce((total, entry) => total + entry.count, 0) > 100) throw new Error('A collection can launch at most 100 servers.');
return entries;
}
@@ -103,7 +103,7 @@ export function recipeForDeployment(id: string): { recipe: LaunchRecipe; bluepri
export function blueprintRecipe(id: string): { recipe: LaunchRecipe; blueprintName: string } {
const { entries, blueprintName } = blueprintEntries(id);
if (entries.length !== 1 || entries[0].count !== 1) throw new Error('This blueprint launches multiple servers. Use the collection preview.');
if (entries.length !== 1 || entries[0].count !== 1) throw new Error('This collection launches multiple servers. Use the collection preview.');
return { recipe: entries[0].recipe, blueprintName };
}
@@ -112,9 +112,9 @@ export function listBlueprints(): BlueprintSummary[] {
}
function blueprintName(input: { name: unknown; description?: unknown }, exceptId = ''): string {
if (typeof input.name !== 'string' || !input.name.trim() || input.name.trim().length > 80) throw new Error('Give the blueprint a name of 1–80 characters.');
if (typeof input.name !== 'string' || !input.name.trim() || input.name.trim().length > 80) throw new Error('Give the collection a name of 1–80 characters.');
if (input.description !== undefined && (typeof input.description !== 'string' || input.description.length > 500)) throw new Error('Keep the description to 500 characters.');
if (db().prepare('SELECT id FROM server_blueprints WHERE name = ? COLLATE NOCASE AND id != ?').get(input.name.trim(), exceptId)) throw new Error('A blueprint with this name already exists. Choose a new name.');
if (db().prepare('SELECT id FROM server_blueprints WHERE name = ? COLLATE NOCASE AND id != ?').get(input.name.trim(), exceptId)) throw new Error('A collection with this name already exists. Choose a new name.');
return input.name.trim();
}
@@ -141,7 +141,7 @@ export function saveBlueprint(input: { name: unknown; description?: unknown; pre
export function updateBlueprint(id: string, input: { name: unknown; description?: unknown; entries?: unknown }): BlueprintSummary {
return db().transaction(() => {
const row = db().prepare('SELECT * FROM server_blueprints WHERE id = ?').get(id) as BlueprintRow | undefined;
if (!row) throw new Error('Blueprint not found.');
if (!row) throw new Error('Collection not found.');
const name = blueprintName(input, id);
const entries = buildEntries(input.entries);
const recipe = JSON.stringify({ format: 3, entries });