feat: multiple delivery targets (FTP/SFTP), per-preset wiring; The Frame -> TheFrame-Backgrounds gallery

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
This commit is contained in:
2026-07-23 21:04:27 +00:00
parent 3a098d708c
commit 36f3c8da22
9 changed files with 199 additions and 6 deletions
+15 -3
View File
@@ -1,6 +1,6 @@
import { one, query } from './db';
import { getObject } from './storage';
import { loadConfig, uploadBuffer } from './picdrop';
import { loadConfig, loadTargetConfig, uploadBuffer, type PicdropCfg } from './picdrop';
// Standard-Galerie wie initial gebrieft.
const DEFAULT_GALLERY = process.env.PICDROP_DEFAULT_GALLERY || 'POSTER LEA';
@@ -17,12 +17,24 @@ async function galleryFor(item: { folder_id: string | null; job_id: string }): P
return s?.picdrop_default_gallery || DEFAULT_GALLERY;
}
/** Ziel-Zugang: 1) Ordner-Ziel 2) Rezept-Ziel 3) Standard-Picdrop (Einstellungen). */
async function targetFor(item: { folder_id: string | null; job_id: string }): Promise<PicdropCfg | null> {
if (item.folder_id) {
const f = await one<{ delivery_target_id: string | null }>('SELECT delivery_target_id FROM folders WHERE id=$1', [item.folder_id]).catch(() => null);
if (f?.delivery_target_id) { const c = await loadTargetConfig(f.delivery_target_id); if (c) return c; }
}
const j = await one<{ recipe_snapshot: any }>('SELECT recipe_snapshot FROM jobs WHERE id=$1', [item.job_id]);
const tid = j?.recipe_snapshot?.delivery_target_id;
if (tid) { const c = await loadTargetConfig(tid); if (c) return c; }
return loadConfig(); // Standard-Picdrop
}
/** Liefert ein fertiges Item an die passende Picdrop-Galerie aus. */
export async function deliverItem(itemId: string): Promise<{ ok: boolean; message: string }> {
const it = await one<any>('SELECT id, result_path, filename, folder_id, job_id FROM items WHERE id=$1', [itemId]);
if (!it?.result_path) return { ok: false, message: 'Kein Ergebnis vorhanden.' };
const cfg = await loadConfig();
if (!cfg) { await query(`UPDATE items SET delivery_status='failed' WHERE id=$1`, [itemId]); return { ok: false, message: 'Picdrop nicht konfiguriert.' }; }
const cfg = await targetFor(it);
if (!cfg) { await query(`UPDATE items SET delivery_status='failed' WHERE id=$1`, [itemId]); return { ok: false, message: 'Kein Auslieferungsziel konfiguriert.' }; }
await query(`UPDATE items SET delivery_status='pending' WHERE id=$1`, [itemId]);
try {
+14
View File
@@ -22,6 +22,20 @@ export async function loadConfig(): Promise<PicdropCfg | null> {
};
}
/** Zusätzliches Auslieferungsziel aus delivery_targets. */
export async function loadTargetConfig(id: string): Promise<PicdropCfg | null> {
const s = await one<any>(`SELECT protocol, host, port, username, password_enc, base_path
FROM delivery_targets WHERE id=$1`, [id]);
if (!s?.host || !s?.username || !s?.password_enc) return null;
let password = '';
try { password = decrypt(s.password_enc); } catch { return null; }
return {
host: s.host, protocol: (s.protocol || 'sftp'),
port: s.port || (s.protocol === 'ftps' ? 21 : 22),
user: s.username, password, basePath: s.base_path || '/',
};
}
/** Verbindung testen: verbinden + Basisordner listen. */
export async function testConnection(cfg: PicdropCfg): Promise<{ ok: boolean; message: string }> {
try {
+7 -1
View File
@@ -48,8 +48,14 @@ export async function seed(): Promise<void> {
await R('Nur bereinigen', ['clean'], 'keep', 'landscape', { is_default: true });
await R('Poster 30×40', ['clean', 'format'], '30x40', 'portrait');
await R('The Frame', ['clean', 'format'], 'theframe', 'landscape');
await R('The Frame', ['clean', 'format'], 'theframe', 'landscape',
{ delivery: 'both', picdrop_gallery: 'TheFrame-Backgrounds' });
await R('Sticker 5 cm', ['clean', 'cutout', 'format', 'contour'], 'sticker5', 'landscape',
{ contour_mm: 3 });
}
// The-Frame-Rezept auf die eigene Galerie verdrahten (auch für bestehende Installationen).
await query(
`UPDATE recipes SET picdrop_gallery='TheFrame-Backgrounds', delivery='both'
WHERE output_format='theframe' AND (picdrop_gallery IS NULL OR picdrop_gallery='')`);
}