diff --git a/CHANGELOG.md b/CHANGELOG.md index a691a58..6ce27a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,46 @@ +# v0.8.10-beta — Vervangers voor big tech, en sleutels in het juiste formaat + +**Een sleutel is niet zomaar een willekeurige string.** De generator maakte +altijd url-veilige base64, en dat is precies wat Laravel, Homarr en LibreChat +níét accepteren. Laravel wil `base64:` ervoor met standaard-base64; Homarr wil 64 +hexadecimale tekens; LibreChat wil er twee, van 64 en 32. Een waarde in het +verkeerde formaat levert een container op die niet start, met een foutmelding die +nergens naar de sleutel wijst. Sjablonen kunnen nu `secret_format` (`hex` of +`laravel`) en `secret_bytes` opgeven. + +Daarmee vielen twee fouten uit de vorige versie op: **Homarr** en **LibreChat** +kregen sleutels die ze hadden geweigerd. Rechtgezet. En **Snipe-IT** en **Invoice +Ninja**, die vorige ronde afvielen omdat hun APP_KEY niet te genereren was, staan +er nu wel in. + +**Negentien vervangers voor big tech.** De catalogus gaat van 197 naar 218 apps: + +| In plaats van | Nu | +|---|---| +| Google Agenda en Contacten | Baïkal, Radicale | +| Google Translate | LibreTranslate | +| Google Docs | CryptPad, Collabora Online, ONLYOFFICE | +| Google Forms | Formbricks | +| Google Meet en Zoom | Jitsi Meet | +| YouTube | PeerTube, Invidious, Tube Archivist | +| Twitch | Owncast | +| X (Twitter) | Mastodon | +| Instagram | Pixelfed | +| Reddit | Lemmy | +| Calendly | Cal.com | +| Doodle | Rallly | +| DocuSign | DocuSeal | +| Figma | Penpot | + +Communicatie gaat daarmee van 12 naar 16 apps en productiviteit van 21 naar 31 — +de twee hoeken waar zelfhosten het meest oplevert en de catalogus het dunst was. + +**Wat er niet in ging.** Bij een paar apps staat een waarschuwing in de +omschrijving in plaats van dat ik doe alsof het vanzelf gaat: Mastodon en PeerTube +leggen hun domein vast zodra andere servers je kennen, CryptPad heeft echt twee +verschillende domeinen nodig om zijn sandbox te laten werken, en LibreTranslate +haalt bij de eerste start zijn taalmodellen op. + # v0.8.00-beta — 55 apps erbij, en drie kapotte images gevonden **De catalogus stond scheef.** Na het ARR-werk telde media 42 apps en downloaden diff --git a/README.md b/README.md index e32b564..3a6e4e7 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ IP-adres in je netwerk geven. | | | |---|---| -| **App-catalogus** | 197 kant-en-klare apps, ingedeeld in categorieën met kleur. Zoeken, filteren en installeren met een formulier in plaats van YAML. | +| **App-catalogus** | 218 kant-en-klare apps, ingedeeld in categorieën met kleur. Zoeken, filteren en installeren met een formulier in plaats van YAML. | | **Stacks beheren** | Starten, stoppen, bijwerken en herstarten — per stack of per losse container, met logs en live CPU-/geheugengebruik. | | **Apps koppelen** | Stacks komen op een gedeeld netwerk, zodat ze elkaar op naam bereiken. Een veld dat naar een andere app verwijst toont een keuzelijst met wat je al draait. | | **Eigen IP per stack** | Met macvlan of ipvlan krijgt een app een eigen adres in je LAN in plaats van poorten op de host. | diff --git a/VERSION b/VERSION index 752fcfd..6ec662d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.00-beta +0.8.10-beta diff --git a/apps/arr-stack/template.json b/apps/arr-stack/template.json index 441307c..e051b09 100644 --- a/apps/arr-stack/template.json +++ b/apps/arr-stack/template.json @@ -1086,6 +1086,8 @@ "required": true, "secret": true, "generate": "auto", + "secret_format": "hex", + "secret_bytes": 32, "description": "64 tekens; versleutelt de opgeslagen API-sleutels van je apps" } ] diff --git a/apps/baikal/files/compose.yaml b/apps/baikal/files/compose.yaml new file mode 100644 index 0000000..acdc925 --- /dev/null +++ b/apps/baikal/files/compose.yaml @@ -0,0 +1,12 @@ +services: + << service_name >>: + image: ckulka/baikal:nginx + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/var/www/baikal/config + - << appdata_dir >>/<< service_name >>-specific:/var/www/baikal/Specific + ports: + - "<< port >>:80" + restart: unless-stopped diff --git a/apps/baikal/template.json b/apps/baikal/template.json new file mode 100644 index 0000000..2492a28 --- /dev/null +++ b/apps/baikal/template.json @@ -0,0 +1,61 @@ +{ + "kind": "compose", + "metadata": { + "name": "Baïkal", + "description": "Je agenda en contacten op je eigen server, via CalDAV en CardDAV. iPhone, Android en Thunderbird praten er zo mee. Vervangt Google Agenda en Google Contacten.", + "tags": [ + "agenda", + "contacten", + "caldav", + "carddav" + ], + "icon": { + "provider": "selfhst", + "id": "baikal" + }, + "version": { + "name": "latest" + }, + "categories": [ + "gezin", + "productiviteit" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "baikal", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8180, + "required": true, + "description": "Standaard 80; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/cal-com/files/compose.yaml b/apps/cal-com/files/compose.yaml new file mode 100644 index 0000000..bd40712 --- /dev/null +++ b/apps/cal-com/files/compose.yaml @@ -0,0 +1,26 @@ +services: + << service_name >>: + image: calcom/cal.com:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - DATABASE_URL=postgresql://calcom:<< db_password >>@<< service_name >>-db:5432/calcom + - NEXTAUTH_SECRET=<< nextauth_secret >> + - CALENDSO_ENCRYPTION_KEY=<< encryption_key >> + - NEXT_PUBLIC_WEBAPP_URL=<< app_url >> + - NEXTAUTH_URL=<< app_url >>/api/auth + ports: + - "<< port >>:3000" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=calcom + - POSTGRES_USER=calcom + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped diff --git a/apps/cal-com/template.json b/apps/cal-com/template.json new file mode 100644 index 0000000..45fa678 --- /dev/null +++ b/apps/cal-com/template.json @@ -0,0 +1,107 @@ +{ + "kind": "compose", + "metadata": { + "name": "Cal.com", + "description": "Laat anderen een afspraak in je agenda prikken, zonder heen-en-weer mailen over wanneer het uitkomt. Vervangt Calendly.", + "tags": [ + "agenda", + "afspraken", + "planning" + ], + "icon": { + "provider": "selfhst", + "id": "cal-com" + }, + "version": { + "name": "latest" + }, + "categories": [ + "productiviteit", + "zakelijk" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "calcom", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8194, + "required": true, + "description": "Standaard 3000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8194", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "nextauth_secret", + "type": "str", + "title": "Sleutel voor de inlogsessies", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + }, + { + "name": "encryption_key", + "type": "str", + "title": "Sleutel voor gekoppelde agenda's", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "secret_format": "hex", + "secret_bytes": 16 + } + ] + } + ] +} diff --git a/apps/collabora/files/compose.yaml b/apps/collabora/files/compose.yaml new file mode 100644 index 0000000..c873b14 --- /dev/null +++ b/apps/collabora/files/compose.yaml @@ -0,0 +1,15 @@ +services: + << service_name >>: + image: collabora/code:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - aliasgroup1=<< nextcloud_url >> + - extra_params=--o:ssl.enable=false --o:ssl.termination=true + - username=<< admin_user >> + - password=<< admin_password >> + ports: + - "<< port >>:9980" + cap_add: + - MKNOD + restart: unless-stopped diff --git a/apps/collabora/template.json b/apps/collabora/template.json new file mode 100644 index 0000000..652761f --- /dev/null +++ b/apps/collabora/template.json @@ -0,0 +1,91 @@ +{ + "kind": "compose", + "metadata": { + "name": "Collabora Online", + "description": "Documenten, spreadsheets en presentaties bewerken in je browser, samen met anderen. Koppelt aan Nextcloud. Vervangt Google Docs en Microsoft Office.", + "tags": [ + "documenten", + "kantoor", + "samenwerken" + ], + "icon": { + "provider": "selfhst", + "id": "collabora" + }, + "version": { + "name": "latest" + }, + "categories": [ + "documenten", + "productiviteit" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "collabora", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8201, + "required": true, + "description": "Standaard 9980; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "nextcloud_url", + "type": "str", + "title": "Adres van Nextcloud", + "default": "https://cloud.example.com", + "required": true, + "connect": { + "app": "nextcloud-aio", + "scheme": "https", + "port": 443 + }, + "help": "Collabora accepteert alleen verzoeken van dit adres. Klopt het niet, dan blijft de bewerker leeg." + }, + { + "name": "admin_user", + "type": "str", + "title": "Beheerdersnaam", + "default": "admin", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "admin_password", + "type": "str", + "title": "Beheerderswachtwoord", + "default": "", + "required": true, + "secret": true, + "eigen_invoer": true + } + ] + } + ] +} diff --git a/apps/cryptpad/files/compose.yaml b/apps/cryptpad/files/compose.yaml new file mode 100644 index 0000000..55d6c43 --- /dev/null +++ b/apps/cryptpad/files/compose.yaml @@ -0,0 +1,15 @@ +services: + << service_name >>: + image: cryptpad/cryptpad:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - CPAD_MAIN_DOMAIN=<< hoofddomein >> + - CPAD_SANDBOX_DOMAIN=<< sandbox_domein >> + volumes: + - << appdata_dir >>/<< service_name >>:/cryptpad/data + - << appdata_dir >>/<< service_name >>-blob:/cryptpad/blob + - << appdata_dir >>/<< service_name >>-block:/cryptpad/block + ports: + - "<< port >>:3000" + restart: unless-stopped diff --git a/apps/cryptpad/template.json b/apps/cryptpad/template.json new file mode 100644 index 0000000..3af62de --- /dev/null +++ b/apps/cryptpad/template.json @@ -0,0 +1,82 @@ +{ + "kind": "compose", + "metadata": { + "name": "CryptPad", + "description": "Documenten, spreadsheets en presentaties die in je browser versleuteld worden. De server ziet alleen onleesbare blokken. Vervangt Google Docs.", + "tags": [ + "documenten", + "samenwerken", + "privacy", + "versleuteling" + ], + "icon": { + "provider": "selfhst", + "id": "cryptpad" + }, + "version": { + "name": "latest" + }, + "categories": [ + "documenten", + "productiviteit" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "cryptpad", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8190, + "required": true, + "description": "Standaard 3000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "hoofddomein", + "type": "str", + "title": "Hoofddomein", + "default": "https://pad.example.com", + "required": true + }, + { + "name": "sandbox_domein", + "type": "str", + "title": "Sandbox-domein", + "default": "https://pad-sandbox.example.com", + "required": true, + "description": "Moet een ánder domein zijn dan het hoofddomein", + "help": "CryptPad zet de documentbewerker in een aparte oorsprong, zodat een lek in de bewerker niet bij je sleutels kan. Twee keer hetzelfde domein invullen haalt die scheiding weg." + } + ] + } + ] +} diff --git a/apps/docuseal/files/compose.yaml b/apps/docuseal/files/compose.yaml new file mode 100644 index 0000000..87c6643 --- /dev/null +++ b/apps/docuseal/files/compose.yaml @@ -0,0 +1,26 @@ +services: + << service_name >>: + image: docuseal/docuseal:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - DATABASE_URL=postgresql://docuseal:<< db_password >>@<< service_name >>-db:5432/docuseal + - SECRET_KEY_BASE=<< secret_key_base >> + - FORCE_SSL=false + volumes: + - << appdata_dir >>/<< service_name >>:/data + ports: + - "<< port >>:3000" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=docuseal + - POSTGRES_USER=docuseal + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped diff --git a/apps/docuseal/template.json b/apps/docuseal/template.json new file mode 100644 index 0000000..4183051 --- /dev/null +++ b/apps/docuseal/template.json @@ -0,0 +1,84 @@ +{ + "kind": "compose", + "metadata": { + "name": "DocuSeal", + "description": "Documenten laten ondertekenen met een digitale handtekening, inclusief invulvelden en een controlespoor. Vervangt DocuSign.", + "tags": [ + "documenten", + "handtekening", + "formulieren" + ], + "icon": { + "provider": "selfhst", + "id": "docuseal" + }, + "version": { + "name": "latest" + }, + "categories": [ + "documenten", + "zakelijk" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "docuseal", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8196, + "required": true, + "description": "Standaard 3000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "secret_key_base", + "type": "str", + "title": "Rails-sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/formbricks/files/compose.yaml b/apps/formbricks/files/compose.yaml new file mode 100644 index 0000000..1b99b8b --- /dev/null +++ b/apps/formbricks/files/compose.yaml @@ -0,0 +1,26 @@ +services: + << service_name >>: + image: formbricks/formbricks:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - DATABASE_URL=postgresql://formbricks:<< db_password >>@<< service_name >>-db:5432/formbricks + - NEXTAUTH_SECRET=<< nextauth_secret >> + - ENCRYPTION_KEY=<< encryption_key >> + - WEBAPP_URL=<< app_url >> + - NEXTAUTH_URL=<< app_url >> + ports: + - "<< port >>:3000" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=formbricks + - POSTGRES_USER=formbricks + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped diff --git a/apps/formbricks/template.json b/apps/formbricks/template.json new file mode 100644 index 0000000..57b97a7 --- /dev/null +++ b/apps/formbricks/template.json @@ -0,0 +1,107 @@ +{ + "kind": "compose", + "metadata": { + "name": "Formbricks", + "description": "Enquêtes en formulieren maken en de antwoorden zelf houden, met logica per vraag. Vervangt Google Forms.", + "tags": [ + "formulieren", + "enquetes", + "onderzoek" + ], + "icon": { + "provider": "selfhst", + "id": "formbricks" + }, + "version": { + "name": "latest" + }, + "categories": [ + "productiviteit", + "zakelijk" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "formbricks", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8197, + "required": true, + "description": "Standaard 3000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8197", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "nextauth_secret", + "type": "str", + "title": "Sleutel voor de inlogsessies", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + }, + { + "name": "encryption_key", + "type": "str", + "title": "Sleutel voor de antwoorden", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "secret_format": "hex", + "secret_bytes": 32 + } + ] + } + ] +} diff --git a/apps/homarr/template.json b/apps/homarr/template.json index 9d96e2e..2e6b14f 100644 --- a/apps/homarr/template.json +++ b/apps/homarr/template.json @@ -64,6 +64,8 @@ "required": true, "secret": true, "generate": "auto", + "secret_format": "hex", + "secret_bytes": 32, "description": "64 tekens; versleutelt de opgeslagen API-sleutels van je apps" } ] diff --git a/apps/invidious/files/compose.yaml b/apps/invidious/files/compose.yaml new file mode 100644 index 0000000..1b563ad --- /dev/null +++ b/apps/invidious/files/compose.yaml @@ -0,0 +1,23 @@ +services: + << service_name >>: + image: quay.io/invidious/invidious:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - ./config.yml:/invidious/config/config.yml:ro + ports: + - "<< port >>:3000" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=invidious + - POSTGRES_USER=invidious + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped diff --git a/apps/invidious/files/config.yml b/apps/invidious/files/config.yml new file mode 100644 index 0000000..87cf22e --- /dev/null +++ b/apps/invidious/files/config.yml @@ -0,0 +1,8 @@ +db: + dbname: invidious + user: invidious + password: << db_password >> + host: << service_name >>-db + port: 5432 +check_tables: true +hmac_key: << hmac_key >> diff --git a/apps/invidious/template.json b/apps/invidious/template.json new file mode 100644 index 0000000..75c6f66 --- /dev/null +++ b/apps/invidious/template.json @@ -0,0 +1,83 @@ +{ + "kind": "compose", + "metadata": { + "name": "Invidious", + "description": "Kijkt YouTube zonder reclame, zonder account en zonder dat Google meekijkt. Abonnementen blijven op je eigen server. Vervangt YouTube.", + "tags": [ + "video", + "youtube", + "privacy" + ], + "icon": { + "provider": "selfhst", + "id": "invidious" + }, + "version": { + "name": "latest" + }, + "categories": [ + "media" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "invidious", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8186, + "required": true, + "description": "Standaard 3000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "hmac_key", + "type": "str", + "title": "HMAC-sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/invoiceninja/files/compose.yaml b/apps/invoiceninja/files/compose.yaml new file mode 100644 index 0000000..339e868 --- /dev/null +++ b/apps/invoiceninja/files/compose.yaml @@ -0,0 +1,36 @@ +services: + << service_name >>: + image: invoiceninja/invoiceninja:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - APP_KEY=<< app_key >> + - APP_URL=<< app_url >> + - APP_ENV=production + - APP_DEBUG=false + - DB_HOST=<< service_name >>-db + - DB_PORT=3306 + - DB_DATABASE=ninja + - DB_USERNAME=ninja + - DB_PASSWORD=<< db_password >> + - IN_USER_EMAIL=<< admin_email >> + - IN_PASSWORD=<< admin_password >> + volumes: + - << appdata_dir >>/<< service_name >>:/var/www/app/public + - << appdata_dir >>/<< service_name >>-storage:/var/www/app/storage + ports: + - "<< port >>:80" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-db: + image: mariadb:11 + container_name: << service_name >>-db + environment: + - MARIADB_DATABASE=ninja + - MARIADB_USER=ninja + - MARIADB_PASSWORD=<< db_password >> + - MARIADB_ROOT_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/mysql + restart: unless-stopped diff --git a/apps/invoiceninja/template.json b/apps/invoiceninja/template.json new file mode 100644 index 0000000..a9ecc99 --- /dev/null +++ b/apps/invoiceninja/template.json @@ -0,0 +1,115 @@ +{ + "kind": "compose", + "metadata": { + "name": "Invoice Ninja", + "description": "Facturen, offertes en urenregistratie voor zzp'ers en kleine bedrijven, met online betalen en herinneringen.", + "tags": [ + "facturen", + "boekhouding", + "zzp" + ], + "icon": { + "provider": "selfhst", + "id": "invoiceninja" + }, + "version": { + "name": "latest" + }, + "categories": [ + "zakelijk", + "financien" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "invoiceninja", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8179, + "required": true, + "description": "Standaard 80; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8179", + "required": true + }, + { + "name": "admin_email", + "type": "str", + "title": "E-mailadres beheerder", + "default": "admin@example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "app_key", + "type": "str", + "title": "Applicatiesleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "secret_format": "laravel", + "secret_bytes": 32, + "description": "Laravel-formaat; wordt automatisch in de juiste vorm gemaakt" + }, + { + "name": "admin_password", + "type": "str", + "title": "Beheerderswachtwoord", + "default": "", + "required": true, + "secret": true, + "eigen_invoer": true + } + ] + } + ] +} diff --git a/apps/jitsi/files/compose.yaml b/apps/jitsi/files/compose.yaml new file mode 100644 index 0000000..adfef30 --- /dev/null +++ b/apps/jitsi/files/compose.yaml @@ -0,0 +1,69 @@ +services: + << service_name >>: + image: jitsi/web:stable + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - ENABLE_AUTH=0 + - ENABLE_GUESTS=1 + - PUBLIC_URL=<< public_url >> + - XMPP_SERVER=<< service_name >>-prosody + - XMPP_DOMAIN=meet.jitsi + - XMPP_AUTH_DOMAIN=auth.meet.jitsi + - XMPP_MUC_DOMAIN=muc.meet.jitsi + - XMPP_BOSH_URL_BASE=http://<< service_name >>-prosody:5280 + volumes: + - << appdata_dir >>/<< service_name >>:/config + ports: + - "<< port >>:80" + depends_on: + - << service_name >>-prosody + restart: unless-stopped + << service_name >>-prosody: + image: jitsi/prosody:stable + container_name: << service_name >>-prosody + environment: + - XMPP_DOMAIN=meet.jitsi + - XMPP_AUTH_DOMAIN=auth.meet.jitsi + - XMPP_MUC_DOMAIN=muc.meet.jitsi + - XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi + - JICOFO_AUTH_USER=focus + - JICOFO_AUTH_PASSWORD=<< jicofo_password >> + - JVB_AUTH_USER=jvb + - JVB_AUTH_PASSWORD=<< jvb_password >> + volumes: + - << appdata_dir >>/<< service_name >>-prosody:/config + restart: unless-stopped + << service_name >>-jicofo: + image: jitsi/jicofo:stable + container_name: << service_name >>-jicofo + environment: + - XMPP_SERVER=<< service_name >>-prosody + - XMPP_DOMAIN=meet.jitsi + - XMPP_AUTH_DOMAIN=auth.meet.jitsi + - XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi + - JICOFO_AUTH_USER=focus + - JICOFO_AUTH_PASSWORD=<< jicofo_password >> + volumes: + - << appdata_dir >>/<< service_name >>-jicofo:/config + depends_on: + - << service_name >>-prosody + restart: unless-stopped + << service_name >>-jvb: + image: jitsi/jvb:stable + container_name: << service_name >>-jvb + environment: + - XMPP_SERVER=<< service_name >>-prosody + - XMPP_AUTH_DOMAIN=auth.meet.jitsi + - XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi + - JVB_AUTH_USER=jvb + - JVB_AUTH_PASSWORD=<< jvb_password >> + - JVB_PORT=<< port_media >> + - PUBLIC_URL=<< public_url >> + ports: + - "<< port_media >>:<< port_media >>/udp" + volumes: + - << appdata_dir >>/<< service_name >>-jvb:/config + depends_on: + - << service_name >>-prosody + restart: unless-stopped diff --git a/apps/jitsi/template.json b/apps/jitsi/template.json new file mode 100644 index 0000000..614dcdc --- /dev/null +++ b/apps/jitsi/template.json @@ -0,0 +1,102 @@ +{ + "kind": "compose", + "metadata": { + "name": "Jitsi Meet", + "description": "Videovergaderen in de browser, zonder account en zonder installatie. Deel een link en je zit erin. Vervangt Google Meet en Zoom.", + "tags": [ + "video", + "vergaderen", + "communicatie" + ], + "icon": { + "provider": "selfhst", + "id": "jitsi" + }, + "version": { + "name": "latest" + }, + "categories": [ + "communicatie" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "jitsi", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8193, + "required": true, + "description": "Standaard 80; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "port_media", + "type": "int", + "title": "Mediapoort (UDP)", + "default": 10000, + "required": true, + "description": "Hier loopt het beeld en geluid; zet deze open in je router" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "public_url", + "type": "str", + "title": "Publiek adres", + "default": "https://meet.example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "jicofo_password", + "type": "str", + "title": "Wachtwoord voor de conferentiebeheerder", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + }, + { + "name": "jvb_password", + "type": "str", + "title": "Wachtwoord voor de mediabrug", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/lemmy/files/compose.yaml b/apps/lemmy/files/compose.yaml new file mode 100644 index 0000000..3bd8aa8 --- /dev/null +++ b/apps/lemmy/files/compose.yaml @@ -0,0 +1,43 @@ +services: + << service_name >>: + image: dessalines/lemmy:0.19.11 + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - RUST_LOG=warn + volumes: + - << appdata_dir >>/<< service_name >>:/config + - ./lemmy.hjson:/config/config.hjson:ro + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-ui: + image: dessalines/lemmy-ui:0.19.11 + container_name: << service_name >>-ui + environment: + - LEMMY_UI_LEMMY_INTERNAL_HOST=<< service_name >>:8536 + - LEMMY_UI_LEMMY_EXTERNAL_HOST=<< domein >> + - LEMMY_UI_HTTPS=true + ports: + - "<< port_ui >>:1234" + depends_on: + - << service_name >> + restart: unless-stopped + << service_name >>-pictrs: + image: asonix/pictrs:0.5 + container_name: << service_name >>-pictrs + environment: + - PICTRS__SERVER__API_KEY=<< pictrs_key >> + volumes: + - << appdata_dir >>/<< service_name >>-pictrs:/mnt + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=lemmy + - POSTGRES_USER=lemmy + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped diff --git a/apps/lemmy/files/lemmy.hjson b/apps/lemmy/files/lemmy.hjson new file mode 100644 index 0000000..59a6c1f --- /dev/null +++ b/apps/lemmy/files/lemmy.hjson @@ -0,0 +1,16 @@ +{ + hostname: "<< domein >>" + bind: "0.0.0.0" + port: 8536 + database: { + host: "<< service_name >>-db" + user: "lemmy" + password: "<< db_password >>" + database: "lemmy" + port: 5432 + } + pictrs: { + url: "http://<< service_name >>-pictrs:8080/" + api_key: "<< pictrs_key >>" + } +} diff --git a/apps/lemmy/template.json b/apps/lemmy/template.json new file mode 100644 index 0000000..ccd2a6a --- /dev/null +++ b/apps/lemmy/template.json @@ -0,0 +1,95 @@ +{ + "kind": "compose", + "metadata": { + "name": "Lemmy", + "description": "Forum in gemeenschappen, opgebouwd uit servers die elkaar volgen. Berichten stemmen omhoog of omlaag. Vervangt Reddit.", + "tags": [ + "forum", + "sociaal", + "federatie" + ], + "icon": { + "provider": "selfhst", + "id": "lemmy" + }, + "version": { + "name": "latest" + }, + "categories": [ + "communicatie" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "lemmy", + "required": true + }, + { + "name": "port_ui", + "type": "int", + "title": "Webinterface", + "default": 8189, + "required": true + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "domein", + "type": "str", + "title": "Publiek domein", + "default": "forum.example.com", + "required": true, + "description": "Ligt vast zodra andere servers je kennen" + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "pictrs_key", + "type": "str", + "title": "Sleutel voor de afbeeldingsdienst", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/librechat/template.json b/apps/librechat/template.json index 66741d4..cbfe782 100644 --- a/apps/librechat/template.json +++ b/apps/librechat/template.json @@ -75,7 +75,9 @@ "default": "", "required": true, "secret": true, - "generate": "auto" + "generate": "auto", + "secret_format": "hex", + "secret_bytes": 32 }, { "name": "creds_iv", @@ -84,7 +86,9 @@ "default": "", "required": true, "secret": true, - "generate": "auto" + "generate": "auto", + "secret_format": "hex", + "secret_bytes": 16 }, { "name": "jwt_secret", diff --git a/apps/libretranslate/files/compose.yaml b/apps/libretranslate/files/compose.yaml new file mode 100644 index 0000000..65ec31a --- /dev/null +++ b/apps/libretranslate/files/compose.yaml @@ -0,0 +1,13 @@ +services: + << service_name >>: + image: libretranslate/libretranslate:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - LT_LOAD_ONLY=<< talen >> + - LT_DISABLE_WEB_UI=false + volumes: + - << appdata_dir >>/<< service_name >>:/home/libretranslate/.local/share/argos-translate + ports: + - "<< port >>:5000" + restart: unless-stopped diff --git a/apps/libretranslate/template.json b/apps/libretranslate/template.json new file mode 100644 index 0000000..77dddc1 --- /dev/null +++ b/apps/libretranslate/template.json @@ -0,0 +1,73 @@ +{ + "kind": "compose", + "metadata": { + "name": "LibreTranslate", + "description": "Vertaalt tekst met modellen die op je eigen server draaien. Je zinnen verlaten je netwerk niet. Vervangt Google Translate. Let op: Bij de eerste start worden de taalmodellen gedownload; dat duurt even.", + "tags": [ + "vertalen", + "taal", + "privacy" + ], + "icon": { + "provider": "selfhst", + "id": "libretranslate" + }, + "version": { + "name": "latest" + }, + "categories": [ + "productiviteit", + "ai" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "libretranslate", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8183, + "required": true, + "description": "Standaard 5000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "talen", + "type": "str", + "title": "Talen", + "default": "en,nl,de,fr", + "required": true, + "description": "Komma-gescheiden; minder talen betekent een veel kleinere download" + } + ] + } + ] +} diff --git a/apps/mastodon/files/compose.yaml b/apps/mastodon/files/compose.yaml new file mode 100644 index 0000000..7202e2b --- /dev/null +++ b/apps/mastodon/files/compose.yaml @@ -0,0 +1,46 @@ +services: + << service_name >>: + image: ghcr.io/mastodon/mastodon:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - LOCAL_DOMAIN=<< domein >> + - RAILS_ENV=production + - DB_HOST=<< service_name >>-db + - DB_USER=mastodon + - DB_NAME=mastodon + - DB_PASS=<< db_password >> + - DB_PORT=5432 + - REDIS_HOST=<< service_name >>-redis + - REDIS_PORT=6379 + - SECRET_KEY_BASE=<< secret_key_base >> + - OTP_SECRET=<< otp_secret >> + - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=<< ar_primary_key >> + - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=<< ar_deterministic_key >> + - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=<< ar_salt >> + volumes: + - << appdata_dir >>/<< service_name >>:/mastodon/public/system + command: bash -c "rails db:migrate && rails s -p 3000" + ports: + - "<< port >>:3000" + depends_on: + - << service_name >>-db + - << service_name >>-redis + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=mastodon + - POSTGRES_USER=mastodon + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped + << service_name >>-redis: + image: valkey/valkey:8-alpine + container_name: << service_name >>-redis + command: valkey-server --save 60 1 + volumes: + - << appdata_dir >>/<< service_name >>-redis:/data + restart: unless-stopped diff --git a/apps/mastodon/template.json b/apps/mastodon/template.json new file mode 100644 index 0000000..8d7fe4a --- /dev/null +++ b/apps/mastodon/template.json @@ -0,0 +1,132 @@ +{ + "kind": "compose", + "metadata": { + "name": "Mastodon", + "description": "Sociaal netwerk dat met duizenden andere servers samenwerkt. Jij bepaalt de regels op je eigen server. Vervangt X (Twitter). Let op: Maak na de eerste start een beheerder aan met `docker exec -it bin/tootctl accounts create ... --role Owner`.", + "tags": [ + "sociaal", + "federatie", + "microblog" + ], + "icon": { + "provider": "selfhst", + "id": "mastodon" + }, + "version": { + "name": "latest" + }, + "categories": [ + "communicatie" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "mastodon", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8187, + "required": true, + "description": "Standaard 3000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "domein", + "type": "str", + "title": "Publiek domein", + "default": "social.example.com", + "required": true, + "description": "Zit in het adres van elke gebruiker en ligt daarna vast" + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "secret_key_base", + "type": "str", + "title": "Rails-sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + }, + { + "name": "otp_secret", + "type": "str", + "title": "Sleutel voor tweefactorcodes", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + }, + { + "name": "ar_primary_key", + "type": "str", + "title": "Sleutel voor versleutelde velden", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + }, + { + "name": "ar_deterministic_key", + "type": "str", + "title": "Sleutel voor doorzoekbare velden", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + }, + { + "name": "ar_salt", + "type": "str", + "title": "Salt voor de sleutelafleiding", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/onlyoffice/files/compose.yaml b/apps/onlyoffice/files/compose.yaml new file mode 100644 index 0000000..43d9fb2 --- /dev/null +++ b/apps/onlyoffice/files/compose.yaml @@ -0,0 +1,14 @@ +services: + << service_name >>: + image: onlyoffice/documentserver:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - JWT_ENABLED=true + - JWT_SECRET=<< jwt_secret >> + volumes: + - << appdata_dir >>/<< service_name >>:/var/www/onlyoffice/Data + - << appdata_dir >>/<< service_name >>-log:/var/log/onlyoffice + ports: + - "<< port >>:80" + restart: unless-stopped diff --git a/apps/onlyoffice/template.json b/apps/onlyoffice/template.json new file mode 100644 index 0000000..589503c --- /dev/null +++ b/apps/onlyoffice/template.json @@ -0,0 +1,75 @@ +{ + "kind": "compose", + "metadata": { + "name": "ONLYOFFICE", + "description": "Kantoorsuite in je browser die goed overweg kan met docx, xlsx en pptx. Koppelt aan Nextcloud en Seafile. Vervangt Google Docs en Microsoft Office.", + "tags": [ + "documenten", + "kantoor", + "samenwerken" + ], + "icon": { + "provider": "selfhst", + "id": "onlyoffice" + }, + "version": { + "name": "latest" + }, + "categories": [ + "documenten", + "productiviteit" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "onlyoffice", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8192, + "required": true, + "description": "Standaard 80; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "jwt_secret", + "type": "str", + "title": "JWT-sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Dezelfde waarde vul je in bij Nextcloud of Seafile" + } + ] + } + ] +} diff --git a/apps/owncast/files/compose.yaml b/apps/owncast/files/compose.yaml new file mode 100644 index 0000000..4d35025 --- /dev/null +++ b/apps/owncast/files/compose.yaml @@ -0,0 +1,12 @@ +services: + << service_name >>: + image: owncast/owncast:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/app/data + ports: + - "<< port >>:8080" + - "<< port_rtmp >>:1935" + restart: unless-stopped diff --git a/apps/owncast/template.json b/apps/owncast/template.json new file mode 100644 index 0000000..6e9125b --- /dev/null +++ b/apps/owncast/template.json @@ -0,0 +1,67 @@ +{ + "kind": "compose", + "metadata": { + "name": "Owncast", + "description": "Je eigen livestream met chat, zonder platform ertussen. Zendt uit vanuit OBS via RTMP. Vervangt Twitch.", + "tags": [ + "streaming", + "video", + "live" + ], + "icon": { + "provider": "selfhst", + "id": "owncast" + }, + "version": { + "name": "latest" + }, + "categories": [ + "media" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "owncast", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8184, + "required": true, + "description": "Standaard 8080; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "port_rtmp", + "type": "int", + "title": "RTMP-poort", + "default": 1935, + "required": true, + "description": "Hier zendt OBS naartoe" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/peertube/files/compose.yaml b/apps/peertube/files/compose.yaml new file mode 100644 index 0000000..e7dbb9c --- /dev/null +++ b/apps/peertube/files/compose.yaml @@ -0,0 +1,41 @@ +services: + << service_name >>: + image: chocobozzz/peertube:production-bookworm + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - PEERTUBE_WEBSERVER_HOSTNAME=<< hostname >> + - PEERTUBE_WEBSERVER_PORT=443 + - PEERTUBE_WEBSERVER_HTTPS=true + - PEERTUBE_DB_HOSTNAME=<< service_name >>-db + - PEERTUBE_DB_USERNAME=peertube + - PEERTUBE_DB_PASSWORD=<< db_password >> + - PEERTUBE_REDIS_HOSTNAME=<< service_name >>-redis + - PEERTUBE_SECRET=<< peertube_secret >> + - PEERTUBE_ADMIN_EMAIL=<< admin_email >> + volumes: + - << appdata_dir >>/<< service_name >>:/data + - << appdata_dir >>/<< service_name >>-config:/config + ports: + - "<< port >>:9000" + depends_on: + - << service_name >>-db + - << service_name >>-redis + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=peertube + - POSTGRES_USER=peertube + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped + << service_name >>-redis: + image: valkey/valkey:8-alpine + container_name: << service_name >>-redis + command: valkey-server --save 60 1 + volumes: + - << appdata_dir >>/<< service_name >>-redis:/data + restart: unless-stopped diff --git a/apps/peertube/template.json b/apps/peertube/template.json new file mode 100644 index 0000000..a19d1c6 --- /dev/null +++ b/apps/peertube/template.json @@ -0,0 +1,103 @@ +{ + "kind": "compose", + "metadata": { + "name": "PeerTube", + "description": "Videoplatform dat met andere PeerTube-servers samenwerkt en kijkers de video onderling laat doorgeven. Vervangt YouTube.", + "tags": [ + "video", + "streaming", + "federatie" + ], + "icon": { + "provider": "selfhst", + "id": "peertube" + }, + "version": { + "name": "latest" + }, + "categories": [ + "media" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "peertube", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8185, + "required": true, + "description": "Standaard 9000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "hostname", + "type": "str", + "title": "Publiek domein", + "default": "video.example.com", + "required": true, + "description": "Ligt vast zodra je server bekend is bij andere servers" + }, + { + "name": "admin_email", + "type": "str", + "title": "E-mailadres beheerder", + "default": "admin@example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "peertube_secret", + "type": "str", + "title": "Geheime sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/penpot/files/compose.yaml b/apps/penpot/files/compose.yaml new file mode 100644 index 0000000..6fda36e --- /dev/null +++ b/apps/penpot/files/compose.yaml @@ -0,0 +1,56 @@ +services: + << service_name >>: + image: penpotapp/frontend:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - PENPOT_FLAGS=enable-registration enable-login-with-password + - PENPOT_BACKEND_URI=http://<< service_name >>-backend:6060 + - PENPOT_EXPORTER_URI=http://<< service_name >>-exporter:6061 + ports: + - "<< port >>:8080" + depends_on: + - << service_name >>-backend + restart: unless-stopped + << service_name >>-backend: + image: penpotapp/backend:latest + container_name: << service_name >>-backend + environment: + - PENPOT_FLAGS=enable-registration enable-login-with-password + - PENPOT_PUBLIC_URI=<< app_url >> + - PENPOT_SECRET_KEY=<< secret_key >> + - PENPOT_DATABASE_URI=postgresql://<< service_name >>-db/penpot + - PENPOT_DATABASE_USERNAME=penpot + - PENPOT_DATABASE_PASSWORD=<< db_password >> + - PENPOT_REDIS_URI=redis://<< service_name >>-redis/0 + - PENPOT_ASSETS_STORAGE_BACKEND=assets-fs + - PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/data/assets + volumes: + - << appdata_dir >>/<< service_name >>-assets:/opt/data + depends_on: + - << service_name >>-db + - << service_name >>-redis + restart: unless-stopped + << service_name >>-exporter: + image: penpotapp/exporter:latest + container_name: << service_name >>-exporter + environment: + - PENPOT_PUBLIC_URI=http://<< service_name >>:8080 + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=penpot + - POSTGRES_USER=penpot + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped + << service_name >>-redis: + image: valkey/valkey:8-alpine + container_name: << service_name >>-redis + command: valkey-server --save 60 1 + volumes: + - << appdata_dir >>/<< service_name >>-redis:/data + restart: unless-stopped diff --git a/apps/penpot/template.json b/apps/penpot/template.json new file mode 100644 index 0000000..2e78382 --- /dev/null +++ b/apps/penpot/template.json @@ -0,0 +1,96 @@ +{ + "kind": "compose", + "metadata": { + "name": "Penpot", + "description": "Ontwerpen en prototypes maken in je browser, met open bestandsformaten in plaats van een gesloten dienst. Vervangt Figma.", + "tags": [ + "ontwerp", + "prototypes", + "samenwerken" + ], + "icon": { + "provider": "selfhst", + "id": "penpot" + }, + "version": { + "name": "latest" + }, + "categories": [ + "productiviteit", + "ontwikkeling" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "penpot", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8198, + "required": true, + "description": "Standaard 8080; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8198", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "secret_key", + "type": "str", + "title": "Geheime sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/pixelfed/files/compose.yaml b/apps/pixelfed/files/compose.yaml new file mode 100644 index 0000000..20ce900 --- /dev/null +++ b/apps/pixelfed/files/compose.yaml @@ -0,0 +1,35 @@ +services: + << service_name >>: + image: zknt/pixelfed:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - APP_KEY=<< app_key >> + - APP_URL=<< app_url >> + - APP_DOMAIN=<< domein >> + - ADMIN_DOMAIN=<< domein >> + - SESSION_DOMAIN=<< domein >> + - DB_CONNECTION=mysql + - DB_HOST=<< service_name >>-db + - DB_PORT=3306 + - DB_DATABASE=pixelfed + - DB_USERNAME=pixelfed + - DB_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>:/var/www/storage + ports: + - "<< port >>:80" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-db: + image: mariadb:11 + container_name: << service_name >>-db + environment: + - MARIADB_DATABASE=pixelfed + - MARIADB_USER=pixelfed + - MARIADB_PASSWORD=<< db_password >> + - MARIADB_ROOT_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/mysql + restart: unless-stopped diff --git a/apps/pixelfed/template.json b/apps/pixelfed/template.json new file mode 100644 index 0000000..5d6a5b3 --- /dev/null +++ b/apps/pixelfed/template.json @@ -0,0 +1,106 @@ +{ + "kind": "compose", + "metadata": { + "name": "Pixelfed", + "description": "Foto's delen op een netwerk dat met Mastodon en de rest van de fediverse praat. Vervangt Instagram.", + "tags": [ + "fotos", + "sociaal", + "federatie" + ], + "icon": { + "provider": "selfhst", + "id": "pixelfed" + }, + "version": { + "name": "latest" + }, + "categories": [ + "fotos", + "communicatie" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "pixelfed", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8188, + "required": true, + "description": "Standaard 80; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "domein", + "type": "str", + "title": "Publiek domein", + "default": "foto.example.com", + "required": true + }, + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "https://foto.example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "app_key", + "type": "str", + "title": "Applicatiesleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "secret_format": "laravel", + "secret_bytes": 32, + "description": "Laravel-formaat; wordt automatisch in de juiste vorm gemaakt" + } + ] + } + ] +} diff --git a/apps/radicale/files/compose.yaml b/apps/radicale/files/compose.yaml new file mode 100644 index 0000000..d670875 --- /dev/null +++ b/apps/radicale/files/compose.yaml @@ -0,0 +1,12 @@ +services: + << service_name >>: + image: tomsquest/docker-radicale:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/config + - << appdata_dir >>/<< service_name >>-data:/data + ports: + - "<< port >>:5232" + restart: unless-stopped diff --git a/apps/radicale/template.json b/apps/radicale/template.json new file mode 100644 index 0000000..8233949 --- /dev/null +++ b/apps/radicale/template.json @@ -0,0 +1,61 @@ +{ + "kind": "compose", + "metadata": { + "name": "Radicale", + "description": "Kleine, rustige CalDAV- en CardDAV-server. Bestanden op schijf, geen database, weinig onderhoud. Vervangt Google Agenda en Google Contacten.", + "tags": [ + "agenda", + "contacten", + "caldav", + "carddav" + ], + "icon": { + "provider": "selfhst", + "id": "radicale" + }, + "version": { + "name": "latest" + }, + "categories": [ + "gezin", + "productiviteit" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "radicale", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8182, + "required": true, + "description": "Standaard 5232; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/rallly/files/compose.yaml b/apps/rallly/files/compose.yaml new file mode 100644 index 0000000..d4bc8dd --- /dev/null +++ b/apps/rallly/files/compose.yaml @@ -0,0 +1,25 @@ +services: + << service_name >>: + image: lukevella/rallly:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - DATABASE_URL=postgresql://rallly:<< db_password >>@<< service_name >>-db:5432/rallly + - SECRET_PASSWORD=<< secret_password >> + - NEXT_PUBLIC_BASE_URL=<< app_url >> + - NOREPLY_EMAIL=<< afzender >> + ports: + - "<< port >>:3000" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=rallly + - POSTGRES_USER=rallly + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped diff --git a/apps/rallly/template.json b/apps/rallly/template.json new file mode 100644 index 0000000..416673c --- /dev/null +++ b/apps/rallly/template.json @@ -0,0 +1,104 @@ +{ + "kind": "compose", + "metadata": { + "name": "Rallly", + "description": "Prikt een datum met een groep: iedereen vinkt aan wanneer het kan, jij ziet meteen wat wint. Vervangt Doodle.", + "tags": [ + "planning", + "agenda", + "groep" + ], + "icon": { + "provider": "selfhst", + "id": "rallly" + }, + "version": { + "name": "latest" + }, + "categories": [ + "productiviteit", + "gezin" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "rallly", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8195, + "required": true, + "description": "Standaard 3000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8195", + "required": true + }, + { + "name": "afzender", + "type": "str", + "title": "Afzenderadres", + "default": "noreply@example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "secret_password", + "type": "str", + "title": "Geheime sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Minimaal 32 tekens" + } + ] + } + ] +} diff --git a/apps/snipe-it/files/compose.yaml b/apps/snipe-it/files/compose.yaml new file mode 100644 index 0000000..400a23b --- /dev/null +++ b/apps/snipe-it/files/compose.yaml @@ -0,0 +1,35 @@ +services: + << service_name >>: + image: snipe/snipe-it:latest + container_name: << service_name >> + environment: + - APP_KEY=<< app_key >> + - APP_URL=<< app_url >> + - APP_TIMEZONE=<< timezone >> + - MYSQL_DATABASE=snipeit + - MYSQL_USER=snipeit + - MYSQL_PASSWORD=<< db_password >> + - DB_CONNECTION=mysql + - DB_HOST=<< service_name >>-db + - DB_PORT=3306 + - DB_DATABASE=snipeit + - DB_USERNAME=snipeit + - DB_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>:/var/lib/snipeit + ports: + - "<< port >>:80" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-db: + image: mariadb:11 + container_name: << service_name >>-db + environment: + - MARIADB_DATABASE=snipeit + - MARIADB_USER=snipeit + - MARIADB_PASSWORD=<< db_password >> + - MARIADB_ROOT_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/mysql + restart: unless-stopped diff --git a/apps/snipe-it/template.json b/apps/snipe-it/template.json new file mode 100644 index 0000000..b4ee5ff --- /dev/null +++ b/apps/snipe-it/template.json @@ -0,0 +1,98 @@ +{ + "kind": "compose", + "metadata": { + "name": "Snipe-IT", + "description": "Houdt bij welke apparaten je hebt, wie ze in gebruik heeft en wanneer de garantie afloopt. Met barcodes en een uitleenregistratie.", + "tags": [ + "assetbeheer", + "inventaris", + "hardware" + ], + "icon": { + "provider": "selfhst", + "id": "snipe-it" + }, + "version": { + "name": "latest" + }, + "categories": [ + "zakelijk" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "snipeit", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8178, + "required": true, + "description": "Standaard 80; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8178", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "app_key", + "type": "str", + "title": "Applicatiesleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "secret_format": "laravel", + "secret_bytes": 32, + "description": "Laravel-formaat; wordt automatisch in de juiste vorm gemaakt" + } + ] + } + ] +} diff --git a/apps/tubearchivist/files/compose.yaml b/apps/tubearchivist/files/compose.yaml new file mode 100644 index 0000000..5ba050f --- /dev/null +++ b/apps/tubearchivist/files/compose.yaml @@ -0,0 +1,41 @@ +services: + << service_name >>: + image: bbilly1/tubearchivist:latest + container_name: << service_name >> + environment: + - ES_URL=http://<< service_name >>-es:9200 + - REDIS_CON=redis://<< service_name >>-redis:6379 + - HOST_UID=<< puid >> + - HOST_GID=<< pgid >> + - TA_HOST=<< app_url >> + - TA_USERNAME=<< admin_user >> + - TA_PASSWORD=<< admin_password >> + - ELASTIC_PASSWORD=<< db_password >> + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/cache + - << data_root >>:/data + ports: + - "<< port >>:8000" + depends_on: + - << service_name >>-es + - << service_name >>-redis + restart: unless-stopped + << service_name >>-es: + image: bbilly1/tubearchivist-es:latest + container_name: << service_name >>-es + environment: + - ELASTIC_PASSWORD=<< db_password >> + - discovery.type=single-node + - xpack.security.enabled=true + - path.repo=/usr/share/elasticsearch/data/snapshot + volumes: + - << appdata_dir >>/<< service_name >>-es:/usr/share/elasticsearch/data + restart: unless-stopped + << service_name >>-redis: + image: valkey/valkey:8-alpine + container_name: << service_name >>-redis + command: valkey-server --save 60 1 + volumes: + - << appdata_dir >>/<< service_name >>-redis:/data + restart: unless-stopped diff --git a/apps/tubearchivist/template.json b/apps/tubearchivist/template.json new file mode 100644 index 0000000..47f8f42 --- /dev/null +++ b/apps/tubearchivist/template.json @@ -0,0 +1,132 @@ +{ + "kind": "compose", + "metadata": { + "name": "Tube Archivist", + "description": "Bewaart YouTube-kanalen en afspeellijsten op je eigen schijf, met eigen zoekfunctie en kijkgeschiedenis. Vervangt YouTube-abonnementen.", + "tags": [ + "video", + "youtube", + "archief" + ], + "icon": { + "provider": "selfhst", + "id": "tubearchivist" + }, + "version": { + "name": "latest" + }, + "categories": [ + "media", + "downloaden" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "tubearchivist", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8199, + "required": true, + "description": "Standaard 8000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "data_root", + "type": "str", + "title": "Gedeelde datamap", + "default": "/mnt/data", + "required": true, + "description": "Eén map met je media, gekoppeld als /data", + "help": "Media en downloads horen onder één map te staan, die als /data in elke container komt. Alleen dan kan een voltooide download met een hardlink naar je bibliotheek: geen kopie, geen dubbele opslag. Eronder: media/films, media/series, torrents/ en usenet/." + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Gebruiker", + "items": [ + { + "name": "puid", + "type": "int", + "title": "Gebruikers-ID (PUID)", + "default": 1000, + "required": true, + "description": "`id -u` op je server" + }, + { + "name": "pgid", + "type": "int", + "title": "Groeps-ID (PGID)", + "default": 1000, + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8199", + "required": true + }, + { + "name": "admin_user", + "type": "str", + "title": "Beheerdersnaam", + "default": "admin", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "admin_password", + "type": "str", + "title": "Beheerderswachtwoord", + "default": "", + "required": true, + "secret": true, + "eigen_invoer": true + } + ] + } + ] +} diff --git a/docs/apps-maken.md b/docs/apps-maken.md index 7915c2c..cc8c22d 100644 --- a/docs/apps-maken.md +++ b/docs/apps-maken.md @@ -373,6 +373,24 @@ containernamen zonder. Het wachtwoord is een veld met `"secret": true` en belandt in plaats van in het compose-bestand. Vergeet `depends_on` niet, anders start de app vóór zijn database en valt hij om op de eerste verbinding. +**Sleutels in een bepaald formaat.** `"generate": "auto"` maakt url-veilige +base64 van 24 bytes — 32 tekens. Wil de app iets anders, zeg dat er dan bij: + +| `secret_format` | Levert | Voor | +|---|---|---| +| *(weglaten)* | url-veilige base64, 32 tekens | het meeste | +| `hex` | hexadecimaal, `secret_bytes` × 2 tekens | Homarr, LibreChat | +| `laravel` | `base64:` + standaard-base64 | Snipe-IT, Invoice Ninja, Pixelfed | + +```json +{"name": "app_key", "type": "str", "title": "Applicatiesleutel", + "default": "", "required": true, "secret": true, "generate": "auto", + "secret_format": "laravel", "secret_bytes": 32} +``` + +Dit is geen kosmetiek: een sleutel in het verkeerde formaat levert een container +op die niet start, met een foutmelding die nergens naar de sleutel wijst. + **Een geheim dat je nergens vandaan kunt genereren** — het client-secret van een OIDC-toepassing, een wachtwoord dat de gebruiker zelf kiest — markeer je met `"eigen_invoer": true`. Anders klaagt de testsuite terecht dat er een verplicht diff --git a/server-up/core/boilerplates.py b/server-up/core/boilerplates.py index d7943cc..a6976ca 100644 --- a/server-up/core/boilerplates.py +++ b/server-up/core/boilerplates.py @@ -152,6 +152,12 @@ def fields(d: Path) -> list[dict]: # willekeurigs. "auto" = ook meteen invullen, True = alleen een # knop (voor wachtwoorden waarmee je zelf inlogt). "generate": item.get("generate") or "", + # In welke vorm de gegenereerde waarde moet komen. Laravel wil + # `base64:` ervoor, Homarr en LibreChat willen hexadecimaal van + # een vaste lengte. Zonder dit kreeg je een geldige willekeurige + # string die de app niet accepteert. + "secret_format": item.get("secret_format") or "", + "secret_bytes": item.get("secret_bytes") or 0, # Een geheim dat de gebruiker ergens anders vandaan haalt: het # client-secret van een OIDC-toepassing, een wachtwoord dat hij # zelf kiest. Daar valt niets voor te genereren, en het is geen diff --git a/server-up/templates/index.html b/server-up/templates/index.html index eb3bfb7..296c611 100644 --- a/server-up/templates/index.html +++ b/server-up/templates/index.html @@ -1738,7 +1738,7 @@ tailwind.config = { + @click="install.values[f.name] = veldSecret(f); revealed[f.name] = true">

b.toString(16).padStart(2, '0')).join(''); + const b64 = btoa(String.fromCharCode(...buf)); + if (formaat === 'laravel') return 'base64:' + b64; + return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); + }, + // De sleutel zoals dít veld hem wil hebben. + veldSecret(f) { + return this.randomSecret(f.secret_bytes || 24, f.secret_format || 'base64url'); }, // ── Containers op deze host ────────────────────────────────────────────── @@ -2975,7 +2986,7 @@ function app() { for (const f of (d.fields || [])) { // 'auto': waarden die je nooit hoeft te kennen. Een inlogwachtwoord // krijgt alleen een knop, want dat moet je zelf noteren. - if (f.generate === 'auto' && !init[f.name]) init[f.name] = this.randomSecret(); + if (f.generate === 'auto' && !init[f.name]) init[f.name] = this.veldSecret(f); } this.install.values = init; this.install.preview_files = d.preview_files || {}; diff --git a/tests/js/menu.test.js b/tests/js/menu.test.js index dc80d9b..23da413 100644 --- a/tests/js/menu.test.js +++ b/tests/js/menu.test.js @@ -92,6 +92,27 @@ check('één groep: geen kop', c.stepGroups('basis')[0].showHeader === false); c.install.fields.push(veld('c','basis',{group:'Tweede'})); check('twee groepen: wel koppen', c.stepGroups('basis').every(g => g.showHeader)); +// ── Sleutelformaten ───────────────────────────────────────────────────────── +// Een willekeurige string is niet genoeg: Laravel wil `base64:` ervoor, Homarr +// en LibreChat willen hexadecimaal van een vaste lengte. In het verkeerde +// formaat start de container niet, met een foutmelding die nergens naar de +// sleutel wijst. +const standaard = c.veldSecret({}); +check('standaard is url-veilig', /^[A-Za-z0-9_-]+$/.test(standaard)); +check('standaard is 32 tekens', standaard.length === 32); + +const hex = c.veldSecret({secret_format: 'hex', secret_bytes: 32}); +check('hex is 64 tekens', hex.length === 64); +check('hex bevat alleen hexcijfers', /^[0-9a-f]{64}$/.test(hex)); +check('hex van 16 bytes is 32 tekens', + c.veldSecret({secret_format: 'hex', secret_bytes: 16}).length === 32); + +const lar = c.veldSecret({secret_format: 'laravel', secret_bytes: 32}); +check('laravel begint met base64:', lar.startsWith('base64:')); +check('laravel is standaard-base64 van 32 bytes', + Buffer.from(lar.slice(7), 'base64').length === 32); +check('twee sleutels zijn niet gelijk', c.veldSecret({}) !== c.veldSecret({})); + // ── Stap Onderdelen ───────────────────────────────────────────────────────── // Een stack als de ARR-stack heeft vijftig schakelbare onderdelen. Die horen in // één eigen stap, gegroepeerd per kopje — en nergens anders nóg een keer. diff --git a/tools/arr_sjablonen.py b/tools/arr_sjablonen.py index 9615bc9..1a25119 100644 --- a/tools/arr_sjablonen.py +++ b/tools/arr_sjablonen.py @@ -535,6 +535,9 @@ def _speciaal(app): {"name": "homarr_encryption_key", "type": "str", "title": "Versleutelingssleutel", "default": "", "required": True, "secret": True, "generate": "auto", + # Homarr accepteert alleen 64 hexadecimale tekens; een gewone + # willekeurige string weigert hij bij het opstarten. + "secret_format": "hex", "secret_bytes": 32, "description": "64 tekens; versleutelt de opgeslagen API-sleutels van je apps"}, ]}] app["env_speciaal"] = ["SECRET_ENCRYPTION_KEY=<< homarr_encryption_key >>"]