// Initiales Seed für eldov.win — Bio + Projekte aus dem Workspace. // Wird beim ersten Request aufgerufen, wenn Profile leer ist oder // `projects` keine Zeilen enthält. import type Database from "better-sqlite3"; const PROFILE_DEFAULTS = { handle: "eldov", real_name: "Jan Wagner", role: "Self-Hoster · Bot-Bauer · Privatprojekte", location: "Deutschland, remote", bio_de: [ "Ich baue Dinge, die ich selbst benutzen will — meistens nachts.", "Hermes ist meine Multi-Agent-Fleet: ein Dutzend Rollen, mehrere Hosts, ein Context-Bus. Es ist das Nervensystem hinter fast allen anderen Projekten hier.", "Daneben schreibe ich Bots, die Daten lesen und Aktionen auslösen — Casino, Prediction-Markets, Crypto, TeamSpeak. Ich bin kein Trader, ich bin jemand, der die Mechanik verstehen will und die Reibung in den Werkzeugen reduziert.", "Wenn ich nicht am Code bin, self-hoste ich Freewarez (Gitea, Jellyfin, Discourse, Authelia, Uptime-Kuma und ein paar mehr), betreibe ein Minecraft-Portal und schraube an meinem Setup aus Nix, Tailscale und 9router.", "Was du hier nicht findest: Industrie-Software und Auftragsarbeit. Die steht auf w-make.com. Hier ist alles andere: Hacks, Spielwiesen, Notizen.", ].join("\n\n"), bio_en: [ "I build things I want to use myself — usually at night.", "Hermes is my multi-agent fleet: a dozen roles, several hosts, a context bus. It is the nervous system behind almost every other project here.", "I also write bots that read data and trigger actions — casino, prediction markets, crypto, TeamSpeak. I am not a trader; I am someone who wants to understand the mechanics and reduce the friction in the tools.", "When I am not coding I self-host freewarez (Gitea, Jellyfin, Discourse, Authelia, Uptime-Kuma and a few more), run a Minecraft portal and tinker with my setup of Nix, Tailscale and 9router.", "What you will not find here: industrial software and client work. That lives at w-make.com. This is everything else: hacks, playgrounds, notes.", ].join("\n\n"), facts_de: [ "Handle 'eldov' seit 2003. Klarname nur, weil er sowieso auf w-make.com steht.", "Stack-Schwerpunkte: TypeScript, Node, Python, Rust, Nix, SQLite, Postgres.", "Infrastruktur: zwei VPS-Hosts (free-warez.win / .top), ein Laptop, ein WSL-PC, alles über Tailscale und 9router vernetzt.", "Bevorzugt kleine Werkzeuge mit klarer Verantwortung statt großer Frameworks.", ], facts_en: [ "Handle 'eldov' since 2003. Real name is public because it already is on w-make.com.", "Stack focus: TypeScript, Node, Python, Rust, Nix, SQLite, Postgres.", "Infrastructure: two VPS hosts (free-warez.win / .top), one laptop, one WSL PC, all networked via Tailscale and 9router.", "Prefers small tools with clear responsibility over large frameworks.", ], links_json: JSON.stringify([ { label: "Gitea · eldov", href: "https://gitea.free-warez.win/eldov", kind: "git" }, { label: "Hermes (Multi-Agent-Fleet)", href: "https://hermes.free-warez.win", kind: "service" }, { label: "Free-Warez-Selfhosting", href: "https://free-warez.top", kind: "service" }, { label: "Minecraft-Portal", href: "https://minecraft.free-warez.win", kind: "service" }, { label: "w-make.com (geschäftlich)", href: "https://w-make.com", kind: "external" }, ]), }; type ProjectSeed = { slug: string; number: string; status: string; category: string; stack: string; href?: string; featured: number; summary_de: string; summary_en: string; body_de: string; body_en: string; stance_de?: string; stance_en?: string; sort_index: number; }; const PROJECTS: ProjectSeed[] = [ { slug: "hermes", number: "01", status: "Aktiv · Continuous", category: "AI-Fleet · Multi-Agent", stack: "Python · TypeScript · MCP · Nix · SQLite · 9router", href: "https://hermes.free-warez.win", featured: 1, sort_index: 10, summary_de: "Self-hosted Multi-Agent-Fleet. Ein Context-Bus, ein Bus-Check, ein Watchdog. Hermes orchestriert die anderen Projekte.", summary_en: "Self-hosted multi-agent fleet. One context bus, one bus check, one watchdog. Hermes orchestrates the other projects.", stance_de: "Nervensystem. Wenn Hermes ausfällt, sehe ich es zuerst an allen anderen Bots.", stance_en: "Nervous system. When Hermes goes down I see it first on every other bot.", body_de: [ "Hermes ist das Ding, ohne das die anderen Projekte nicht laufen würden.", "Es ist ein Context-Bus, ein Botnetz-Registry, ein Worker-Router und eine Handvoll Werkzeuge, die alle dasselbe Vokabular sprechen. MCP-Server, ein CLI, eine WebUI, eine Forum-Intelligence.", "Wer hier nach Architektur sucht, findet sie: getrennte Welten, dedizierte Workspaces, ein einziger Bus. Was nicht hierher gehört: Casino-Logik, Industrie-Logik, Persönliches.", ].join("\n\n"), body_en: [ "Hermes is the thing without which the other projects would not run.", "It is a context bus, a botnet registry, a worker router and a handful of tools that all share the same vocabulary. MCP server, CLI, web UI, forum intelligence.", "If you are looking for architecture here, you will find it: separated worlds, dedicated workspaces, a single bus. What does not belong here: casino logic, industrial logic, personal stuff.", ].join("\n\n"), }, { slug: "casino-bot", number: "02", status: "Aktiv · Live-RTP", category: "Bot · Casino", stack: "Node.js · ESM · patchright · Express · better-sqlite3", featured: 1, sort_index: 20, summary_de: "Live-RTP-Analyse für BC.Game (Hacksaw). Liest Slot-Statistiken, bewertet Slots, spielt nach Regeln.", summary_en: "Live-RTP analysis for BC.Game (Hacksaw). Reads slot statistics, scores slots, plays by rules.", stance_de: "Mechanik verstehen, nicht gewinnen. Das System ist ehrlich über das, was es nicht weiß.", stance_en: "Understand the mechanics, not beat them. The system is honest about what it does not know.", body_de: [ "Vollautomatischer Slot-Bot für Hacksaw Gaming über BC.Game. Analysiert Live-RTP, wählt statistisch günstige Slots, spielt mit konfigurierbarem Bankroll-Management und lernt aus jeder Session.", "Was hier nicht passiert: Live-Resultate, Quoten-Versprechen, Affiliate-Links. Wer das System ernst nimmt, schaut in die Docs, nicht auf einen ROI-Screenshot.", ].join("\n\n"), body_en: [ "Fully automated slot bot for Hacksaw Gaming on BC.Game. Analyses live RTP, picks statistically favourable slots, plays with configurable bankroll management and learns from every session.", "What does not happen here: live results, odds promises, affiliate links. If you take the system seriously, read the docs — do not look at an ROI screenshot.", ].join("\n\n"), }, { slug: "polymarket-trader", number: "03", status: "Aktiv · Paper-Trading", category: "Bot · Prediction-Markets", stack: "Python · polymarket-cli · LLM-Thesis · Kelly-Sizing", featured: 1, sort_index: 30, summary_de: "Prediction-Market-Trader für Polymarket. Scanner, LLM-Thesis, Kelly-Sizing. Live-Orders bleiben fail-closed.", summary_en: "Prediction-market trader for Polymarket. Scanner, LLM thesis, Kelly sizing. Live orders remain fail-closed.", stance_de: "Paper-Trade zuerst, immer. Live-Order-Pfad ist hart verriegelt, bis die Simulation sauber ist.", stance_en: "Paper trade first, always. The live-order path is hard-locked until the simulation is clean.", body_de: [ "Inspiriert vom LunarResearcher-Artikel. Modulare Architektur: Scanner, Brain, Executor, Exit, Data.", "Live-Orders sind standardmäßig verriegelt. Der CLI-Einstieg verweigert Live-Trades fail-closed, bis die Paper-Run über genug Tage sauber gelaufen ist.", ].join("\n\n"), body_en: [ "Inspired by the LunarResearcher article. Modular architecture: scanner, brain, executor, exit, data.", "Live orders are locked by default. The CLI entry refuses live trades fail-closed until the paper run has been clean for enough days.", ].join("\n\n"), }, { slug: "quantmuse", number: "04", status: "Aktiv · Development", category: "Quant · Trading", stack: "Python · Pandas · NumPy · Backtesting · AI-Analyse", href: "https://gitea.free-warez.win/eldov/QuantMuse", featured: 0, sort_index: 40, summary_de: "Quant Trading System mit AI-Analyse, Realtime-Daten und Risikomanagement. Code, kein Screenshot-Versprechen.", summary_en: "Quant trading system with AI analysis, real-time data and risk management. Code, not screenshot promises.", stance_de: "Backtest zuerst. Keine öffentliche Performance, nur Methodik.", stance_en: "Backtest first. No public performance, only methodology.", body_de: [ "QuantMuse ist die größere Quant-Werkbank: Signale, Strategien, Backtests, Risk.", "Was hier nicht passiert: P&L-Screenshots, »Strategy of the Month«, Affiliate-Broker.", ].join("\n\n"), body_en: [ "QuantMuse is the bigger quant workshop: signals, strategies, backtests, risk.", "What does not happen here: P&L screenshots, »strategy of the month«, affiliate brokers.", ].join("\n\n"), }, { slug: "crypto-bot", number: "05", status: "Early Stage", category: "Bot · Crypto", stack: "TypeScript · EVM · DEX-Wrapper · On-Chain-Analyse", featured: 0, sort_index: 50, summary_de: "Crypto-Tracking, DeFi-Automatisierung und On-Chain-Analyse. Ethereum, L2s, Polygon.", summary_en: "Crypto tracking, DeFi automation and on-chain analysis. Ethereum, L2s, Polygon.", stance_de: "Werkzeuge statt Empfehlungen. Wer es benutzt, soll es selbst entscheiden.", stance_en: "Tools, not recommendations. Whoever uses it decides for themselves.", body_de: [ "Domäne: Ethereum, Arbitrum, Optimism, Polygon. DeFi: Uniswap, Sushi, Yield-Farming, Liquidity-Pools. On-Chain: Wallet-Analyse, Token-Swaps, Gas-Optimierung.", "Status: Early stage — Projektstart ausstehend.", ].join("\n\n"), body_en: [ "Domain: Ethereum, Arbitrum, Optimism, Polygon. DeFi: Uniswap, Sushi, yield farming, liquidity pools. On-chain: wallet analysis, token swaps, gas optimisation.", "Status: early stage — project kickoff pending.", ].join("\n\n"), }, { slug: "trade-bot", number: "06", status: "Early Stage", category: "Bot · Multi-Asset-Trading", stack: "Python · Binance · Bybit · dYdX · Paper-Trading", featured: 0, sort_index: 60, summary_de: "Algorithmischer Trading-Bot für Crypto, Aktien, CFDs. Paper-Trading zuerst.", summary_en: "Algorithmic trading bot for crypto, stocks, CFDs. Paper trading first.", stance_de: "Domäne: Spot, Futures, Perps, Forex, Indices. Kein Live-Trading ohne dokumentiertes Risk-Modell.", stance_en: "Domain: spot, futures, perps, forex, indices. No live trading without a documented risk model.", body_de: [ "Multi-Asset-Bot mit Fokus auf sauberes Risk-Modell und Backtest-Disziplin.", "Anti-Patterns stehen in `HERMES.md`. Eskalationsmatrix ebenso.", ].join("\n\n"), body_en: [ "Multi-asset bot focused on a clean risk model and backtest discipline.", "Anti-patterns live in `HERMES.md`. So does the escalation matrix.", ].join("\n\n"), }, { slug: "ts6-bot", number: "07", status: "Stabilitäts-Hardening", category: "Bot · Voice-Infra", stack: "TypeScript · Node.js · TeamSpeak 6 Query · Docker", featured: 0, sort_index: 70, summary_de: "TeamSpeak-6-Bot für Server-Automatisierung. REST/Polling, SSH Query, Reconnect mit Backoff.", summary_en: "TeamSpeak 6 bot for server automation. REST polling, SSH query, reconnect with backoff.", stance_de: "TS6 ist beta. Chat-Commands bleiben deaktiviert bis ein Event-Test sauber läuft.", stance_en: "TS6 is beta. Chat commands stay disabled until an event test runs cleanly.", body_de: [ "TS6-Bot mit Server-, Channel- und Client-Daten via REST/Polling. Events via SSH Server Query. `textmessage` ist in TS6 beta10 fehlerhaft (1538) und deshalb nicht freigeschaltet.", "Reconnect mit Exponential Backoff bei Verbindungsabbrüchen.", ].join("\n\n"), body_en: [ "TS6 bot with server, channel and client data via REST polling. Events via SSH Server Query. `textmessage` is broken in TS6 beta10 (1538) and therefore disabled.", "Reconnect with exponential backoff on connection drops.", ].join("\n\n"), }, { slug: "freewarez-landingpage", number: "08", status: "Live", category: "Web · Landing", stack: "HTML5 · Glassmorphism CSS", href: "https://free-warez.top", featured: 0, sort_index: 80, summary_de: "Landingpage für die Free-Warez-Selfhosting-Welt. free-warez.top.", summary_en: "Landing page for the Free-Warez self-hosting world. free-warez.top.", stance_de: "Glas-Ästhetik, keine Tracker, keine Schriften von Dritten.", stance_en: "Glass aesthetic, no trackers, no third-party fonts.", body_de: [ "free-warez.top ist die öffentliche Seite meiner Self-Hosting-Welt. Was nicht beworben wird: VPS-Internas, Passwörter, API-Keys.", ].join("\n\n"), body_en: [ "free-warez.top is the public face of my self-hosting world. What is not advertised: VPS internals, passwords, API keys.", ].join("\n\n"), }, { slug: "minecraft-portal", number: "09", status: "Live", category: "Web · Gaming-Community", stack: "Next.js · Tailwind · Prisma", href: "https://minecraft.free-warez.win", featured: 0, sort_index: 90, summary_de: "Minecraft-Portal (Ashen Atlas) für unsere Community. Next.js mit eigener Datenbank.", summary_en: "Minecraft portal (Ashen Atlas) for our community. Next.js with its own database.", stance_de: "Spielwiese für ein produktives Web-Setup mit echten Nutzern.", stance_en: "Playground for a productive web setup with real users.", body_de: [ "minecraft.free-warez.win ist das Community-Portal. Wer dort spielt, kennt mein Stack-Setup, ohne dass ich es erklären muss.", ].join("\n\n"), body_en: [ "minecraft.free-warez.win is the community portal. Whoever plays there knows my stack setup without me having to explain it.", ].join("\n\n"), }, { slug: "oss-themes", number: "10", status: "Live · Sammlung", category: "Web · Theming", stack: "CSS · SCSS · XML · Blade", featured: 0, sort_index: 100, summary_de: "Themes für Open-Source-Selfhosting: Jellyfin, Gitea, Discourse, Kavita, Authelia, Uptime-Kuma, Erugo, LAMP-Link, Stirling-PDF, Element.", summary_en: "Themes for open-source self-hosting: Jellyfin, Gitea, Discourse, Kavita, Authelia, Uptime Kuma, Erugo, LAMP Link, Stirling PDF, Element.", stance_de: "Jedes Theme hat ein eigenes Repo und ein eigenes Deploy-Skript. Kein Mega-Theme-Monolith.", stance_en: "Each theme has its own repo and its own deploy script. No mega-theme monolith.", body_de: [ "Sammlung von Themes, die meinen Self-Hosting-Stack lesbar machen. Glas, Dunkelheit, Akzent — je nach Produkt.", ].join("\n\n"), body_en: [ "A collection of themes that make my self-hosting stack readable. Glass, darkness, accent — depending on the product.", ].join("\n\n"), }, { slug: "ai-subscription-manager", number: "11", status: "Live", category: "Web · AI-Ops", stack: "Python · FastAPI · Jinja2 · WebUI", href: "https://ai.free-warez.win", featured: 0, sort_index: 110, summary_de: "Verwaltet AI-Subscriptions und Provider-Routing. Welche Modelle, welche Keys, welche Kosten.", summary_en: "Manages AI subscriptions and provider routing. Which models, which keys, which costs.", stance_de: "Internes Werkzeug, das nach außen sichtbar sein darf, weil es keine fremden Daten enthält.", stance_en: "Internal tool that is allowed to be visible because it does not contain third-party data.", body_de: [ "ai-subscription-manager ist die Schaltzentrale für AI-Provider, Keys und Budgets.", ].join("\n\n"), body_en: [ "ai-subscription-manager is the control centre for AI providers, keys and budgets.", ].join("\n\n"), }, { slug: "lamp-link-themes", number: "12", status: "Live", category: "Web · Link-in-Bio", stack: "PHP · the23 Theme Engine · CSS-Vars", href: "https://link.free-warez.win", featured: 0, sort_index: 120, summary_de: "Theme für LAMP-Link-Shortener und Bio-Pages. the23 Theme Engine.", summary_en: "Theme for LAMP link shortener and bio pages. the23 theme engine.", stance_de: "Klein, schnell, gut lesbar auf dem Phone.", stance_en: "Small, fast, readable on the phone.", body_de: [ "LAMP-Link-Themes bedienen link.free-warez.win und andere Bio-Pages. Fokus: Phone-First.", ].join("\n\n"), body_en: [ "LAMP Link themes serve link.free-warez.win and other bio pages. Focus: phone first.", ].join("\n\n"), }, { slug: "android", number: "13", status: "Aktiv", category: "Mobile · Desktop", stack: "Android · Affine · Kotlin · Compose", featured: 0, sort_index: 130, summary_de: "Android-Apps und Affine-basierte Desktop-Projekte.", summary_en: "Android apps and Affine-based desktop projects.", stance_de: "Mobile First, auch wenn der Use-Case es nicht erzwingt.", stance_en: "Mobile first, even when the use case does not demand it.", body_de: [ "Coding/Android enthält Android-Projekte und Affine-Desktop-Apps.", ].join("\n\n"), body_en: [ "Coding/Android contains Android projects and Affine desktop apps.", ].join("\n\n"), }, ]; export function seedProfile(db: Database.Database): boolean { const existing = db.prepare("SELECT id FROM profile WHERE id = 1").get(); if (existing) return false; db.prepare( `INSERT INTO profile (id, handle, real_name, role, location, bio_de, bio_en, facts_de, facts_en, links_json) VALUES (1, @handle, @real_name, @role, @location, @bio_de, @bio_en, @facts_de, @facts_en, @links_json)` ).run({ handle: PROFILE_DEFAULTS.handle, real_name: PROFILE_DEFAULTS.real_name, role: PROFILE_DEFAULTS.role, location: PROFILE_DEFAULTS.location, bio_de: PROFILE_DEFAULTS.bio_de, bio_en: PROFILE_DEFAULTS.bio_en, facts_de: JSON.stringify(PROFILE_DEFAULTS.facts_de), facts_en: JSON.stringify(PROFILE_DEFAULTS.facts_en), links_json: PROFILE_DEFAULTS.links_json, }); return true; } export function seedProjects(db: Database.Database): number { const count = (db.prepare("SELECT COUNT(*) as c FROM projects").get() as { c: number }).c; if (count > 0) return 0; const stmt = db.prepare( `INSERT INTO projects (slug, number, status, category, stack, href, featured, summary_de, summary_en, body_de, body_en, stance_de, stance_en, sort_index) VALUES (@slug, @number, @status, @category, @stack, @href, @featured, @summary_de, @summary_en, @body_de, @body_en, @stance_de, @stance_en, @sort_index)` ); const insertMany = db.transaction((rows: ProjectSeed[]) => { for (const r of rows) { stmt.run({ slug: r.slug, number: r.number, status: r.status, category: r.category, stack: r.stack, href: r.href ?? null, featured: r.featured, summary_de: r.summary_de, summary_en: r.summary_en, body_de: r.body_de, body_en: r.body_en, stance_de: r.stance_de ?? "", stance_en: r.stance_en ?? "", sort_index: r.sort_index, }); } }); insertMany(PROJECTS); return PROJECTS.length; } export function seedIfEmpty(db: Database.Database): { profileSeeded: boolean; projectsSeeded: number } { const profileSeeded = seedProfile(db); const projectsSeeded = seedProjects(db); return { profileSeeded, projectsSeeded }; }