commit cd78becb97824d4a0c3b87166562e2abb30921da Author: alexz Date: Sun Dec 14 07:02:02 2025 +0000 upload current sources diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml new file mode 100755 index 0000000..23a7deb --- /dev/null +++ b/.github/workflows/renovate.yml @@ -0,0 +1,45 @@ +name: Renovate +on: + workflow_dispatch: + inputs: + log_level: + type: choice + description: Log level + default: INFO + options: + - DEBUG + - INFO + - WARN + - ERROR + - FATAL + schedule: + - cron: 0 2 * * * + +jobs: + renovate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install node + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Install bun + uses: oven-sh/setup-bun@v2 + + - name: Cache Bun global packages + uses: actions/cache@v4 + with: + path: ~/.bun/install/global + key: ${{ runner.os }}-bun-global-renovate-40 + restore-keys: | + ${{ runner.os }}-bun-global- + + - name: Install Renovate + run: bun install -g renovate@40 + + - name: Run renovate + run: LOG_LEVEL=${{ github.event.inputs.log_level || 'INFO' }} renovate --token ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100755 index 0000000..d890982 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Test + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Install dependencies + run: bun install + + - name: Run tests + run: bun test diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..456c488 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100755 index 0000000..41d64e0 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# Example App Store Template + +This repository serves as a template for creating your own custom app store for the Runtipi platform. Use this as a starting point to create and share your own collection of applications. + +## Repository Structure + +- **apps/**: Contains individual app directories + + - Each app has its own folder (e.g., `whoami/`) with the following structure: + - `config.json`: App configuration file + - `docker-compose.json`: Docker setup for the app + - `metadata/`: Contains app visuals and descriptions + - `description.md`: Markdown description of the app + - `logo.jpg`: App logo image + +- **tests/**: Contains test files for the app store + + - `apps.test.ts`: Test suite for validating apps + +## Getting Started + +This repository is intended to serve as a template for creating your own app store. Follow these steps to get started: + +1. Click the "Use this template" button to create a new repository based on this template +2. Customize the apps or add your own app folders in the `apps/` directory +3. Test your app store by using it with Runtipi + +## Documentation + +For detailed instructions on creating your own app store, please refer to the official guide: +[Create Your Own App Store Guide](https://runtipi.io/docs/guides/create-your-own-app-store) diff --git a/__tests__/apps.test.ts b/__tests__/apps.test.ts new file mode 100755 index 0000000..4fd30c9 --- /dev/null +++ b/__tests__/apps.test.ts @@ -0,0 +1,77 @@ +import { expect, test, describe } from "bun:test"; +import { appInfoSchema, dynamicComposeSchema } from '@runtipi/common/schemas' +import { fromError } from 'zod-validation-error'; +import fs from 'node:fs' +import path from 'node:path' + +const getApps = async () => { + const appsDir = await fs.promises.readdir(path.join(process.cwd(), 'apps')) + + const appDirs = appsDir.filter((app) => { + const stat = fs.statSync(path.join(process.cwd(), 'apps', app)) + return stat.isDirectory() + }) + + return appDirs +}; + +const getFile = async (app: string, file: string) => { + const filePath = path.join(process.cwd(), 'apps', app, file) + try { + const file = await fs.promises.readFile(filePath, 'utf-8') + return file + } catch (err) { + return null + } +} + +describe("each app should have the required files", async () => { + const apps = await getApps() + + for (const app of apps) { + const files = ['config.json', 'docker-compose.json', 'metadata/logo.jpg', 'metadata/description.md'] + + for (const file of files) { + test(`app ${app} should have ${file}`, async () => { + const fileContent = await getFile(app, file) + expect(fileContent).not.toBeNull() + }) + } + } +}) + +describe("each app should have a valid config.json", async () => { + const apps = await getApps() + + for (const app of apps) { + test(`app ${app} should have a valid config.json`, async () => { + const fileContent = await getFile(app, 'config.json') + const parsed = appInfoSchema.omit({ urn: true }).safeParse(JSON.parse(fileContent || '{}')) + + if (!parsed.success) { + const validationError = fromError(parsed.error); + console.error(`Error parsing config.json for app ${app}:`, validationError.toString()); + } + + expect(parsed.success).toBe(true) + }) + } +}) + +describe("each app should have a valid docker-compose.json", async () => { + const apps = await getApps() + + for (const app of apps) { + test(`app ${app} should have a valid docker-compose.json`, async () => { + const fileContent = await getFile(app, 'docker-compose.json') + const parsed = dynamicComposeSchema.safeParse(JSON.parse(fileContent || '{}')) + + if (!parsed.success) { + const validationError = fromError(parsed.error); + console.error(`Error parsing docker-compose.json for app ${app}:`, validationError.toString()); + } + + expect(parsed.success).toBe(true) + }) + } +}); diff --git a/apps/cistech-tunnel/.env.example b/apps/cistech-tunnel/.env.example new file mode 100755 index 0000000..c583019 --- /dev/null +++ b/apps/cistech-tunnel/.env.example @@ -0,0 +1,11 @@ +# Required +OC_URL=https://vpn.cistech.net/Employees +OC_SERVERCERT=pin-sha256:HyHob3LiVmIp8ch9AzHJ9jMYqI43tO5N13oWeBLiZ/0= + +# Optional +OC_AUTHGROUP= +OC_SSO_ARGS=--browser-display-mode shown +VNC_PASSWORD=vpnSSO12 +NOVNC_PORT=6901 +PUBLISH_ADDR=0.0.0.0 +SSH_KEY_PATH=/home/alexz/.ssh/id_ed25519-lenovo diff --git a/apps/cistech-tunnel/README.md b/apps/cistech-tunnel/README.md new file mode 100755 index 0000000..4e52db3 --- /dev/null +++ b/apps/cistech-tunnel/README.md @@ -0,0 +1,11 @@ +# Cistech Tunnel (VPN + SSH) + +- VPN: OpenConnect-SSO with noVNC for first-time SSO (port 6901) +- SSH tunnels: forwards to 10.3.1.201 inside the VPN namespace + +Usage +- Copy `.env.example` to `.env` and adjust values. +- Build and start: + docker compose build + docker compose up -d vpn ssh_tunnel +- First-time SSO: open http://:6901 and complete login; then set `OC_SSO_ARGS=--browser-display-mode hidden` and restart `vpn`. diff --git a/apps/cistech-tunnel/config.json b/apps/cistech-tunnel/config.json new file mode 100755 index 0000000..d962785 --- /dev/null +++ b/apps/cistech-tunnel/config.json @@ -0,0 +1,25 @@ +{ +"name": "Cistech Tunnel", +"id": "cistech-tunnel", +"available": true, +"short_desc": "OpenConnect-SSO VPN + SSH forwards (noVNC)", +"author": "alexz", +"port": 6901, +"categories": ["utilities","network"], +"description": "OpenConnect-SSO VPN running in an isolated namespace with noVNC for first-time SSO and an SSH tunnel service for local forwards.", +"tipi_version": 1, +"version": "1.0.0", +"source": "https://git.alexzaw.dev/alexz/cistech-tunnel", +"exposable": true, +"dynamic_config": false, +"no_gui": false, +"form_fields": [ +{"label":"VPN URL","type":"text","env_variable":"OC_URL","required":true,"default":"https://vpn.cistech.net/Employees"}, +{"label":"Server Cert Pin","type":"text","env_variable":"OC_SERVERCERT","required":true,"default":"pin-sha256:HyHob3LiVmIp8ch9AzHJ9jMYqI43tO5N13oWeBLiZ/0="}, +{"label":"Auth Group","type":"text","env_variable":"OC_AUTHGROUP","required":false,"default":""}, +{"label":"SSO Mode","type":"text","env_variable":"OC_SSO_ARGS","required":true,"default":"--browser-display-mode shown"}, +{"label":"VNC Password","type":"password","env_variable":"VNC_PASSWORD","required":true,"default":"Az@83278327$$@@"}, +{"label":"SSH Key Path","type":"text","env_variable":"SSH_KEY_PATH","required":true,"default":"/home/alexz/.ssh/id_ed25519-lenovo"} +], +"supported_architectures": ["arm64","amd64"] +} \ No newline at end of file diff --git a/apps/cistech-tunnel/config.json.bak.1765310779 b/apps/cistech-tunnel/config.json.bak.1765310779 new file mode 100755 index 0000000..4327992 --- /dev/null +++ b/apps/cistech-tunnel/config.json.bak.1765310779 @@ -0,0 +1,25 @@ +{ + "name": "Cistech Tunnel", + "id": "cistech-tunnel", + "available": true, + "short_desc": "OpenConnect-SSO VPN + SSH forwards (noVNC)", + "author": "alexz", + "port": 6901, + "categories": ["networking", "utilities"], + "description": "OpenConnect-SSO VPN running in an isolated namespace with noVNC for first-time SSO and an SSH tunnel service for local forwards.", + "tipi_version": 1, + "version": "1.0.0", + "source": "https://git.alexzaw.dev/alexz/cistech-tunnel", + "exposable": true, + "dynamic_config": true, + "no_gui": false, + "form_fields": [ + { "label": "VPN URL", "type": "text", "env_variable": "OC_URL_A", "required": true, "default": "https://vpn.cistech.net/Employees" }, + { "label": "Server Cert Pin", "type": "text", "env_variable": "OC_SERVERCERT_A", "required": true, "default": "pin-sha256:HyHob3LiVmIp8ch9AzHJ9jMYqI43tO5N13oWeBLiZ/0=" }, + { "label": "Auth Group", "type": "text", "env_variable": "OC_AUTHGROUP_A", "required": false, "default": "" }, + { "label": "SSO Mode", "type": "text", "env_variable": "OC_SSO_ARGS_A", "required": true, "default": "--browser-display-mode shown" }, + { "label": "VNC Password", "type": "password", "env_variable": "VNC_PASS_A", "required": true, "default": "vpnSSO12" }, + { "label": "SSH Key Path", "type": "text", "env_variable": "SSH_KEY_PATH", "required": true, "default": "/home/alexz/.ssh/id_ed25519-lenovo" } + ], + "supported_architectures": ["arm64", "amd64"] +} diff --git a/apps/cistech-tunnel/config.json.bak.1765311179 b/apps/cistech-tunnel/config.json.bak.1765311179 new file mode 100755 index 0000000..db98587 --- /dev/null +++ b/apps/cistech-tunnel/config.json.bak.1765311179 @@ -0,0 +1,67 @@ +{ + "name": "Cistech Tunnel", + "id": "cistech-tunnel", + "available": true, + "short_desc": "OpenConnect-SSO VPN + SSH forwards (noVNC)", + "author": "alexz", + "port": 6901, + "categories": [ + "utilities", + "network" + ], + "description": "OpenConnect-SSO VPN running in an isolated namespace with noVNC for first-time SSO and an SSH tunnel service for local forwards.", + "tipi_version": 1, + "version": "1.0.0", + "source": "https://git.alexzaw.dev/alexz/cistech-tunnel", + "exposable": true, + "dynamic_config": true, + "no_gui": false, + "form_fields": [ + { + "label": "VPN URL", + "type": "text", + "env_variable": "OC_URL", + "required": true, + "default": "https://vpn.cistech.net/Employees" + }, + { + "label": "Server Cert Pin", + "type": "text", + "env_variable": "OC_SERVERCERT", + "required": true, + "default": "pin-sha256:HyHob3LiVmIp8ch9AzHJ9jMYqI43tO5N13oWeBLiZ/0=" + }, + { + "label": "Auth Group", + "type": "text", + "env_variable": "OC_AUTHGROUP", + "required": false, + "default": "" + }, + { + "label": "SSO Mode", + "type": "text", + "env_variable": "OC_SSO_ARGS", + "required": true, + "default": "--browser-display-mode shown" + }, + { + "label": "VNC Password", + "type": "password", + "env_variable": "VNC_PASSWORD", + "required": true, + "default": "vpnSSO12" + }, + { + "label": "SSH Key Path", + "type": "text", + "env_variable": "SSH_KEY_PATH", + "required": true, + "default": "/home/alexz/.ssh/id_ed25519-lenovo" + } + ], + "supported_architectures": [ + "arm64", + "amd64" + ] +} \ No newline at end of file diff --git a/apps/cistech-tunnel/docker-compose.json.bak.1765312176 b/apps/cistech-tunnel/docker-compose.json.bak.1765312176 new file mode 100755 index 0000000..e53cf36 --- /dev/null +++ b/apps/cistech-tunnel/docker-compose.json.bak.1765312176 @@ -0,0 +1,39 @@ +{ + "$schema": "https://schemas.runtipi.io/v2/dynamic-compose.json", + "schemaVersion": 2, + "services": [ + { + "name": "vpn_a", + "image": "vpn-openconnect-sso:latest", + "isMain": true, + "internalPort": 6901, + "capAdd": ["NET_ADMIN"], + "devices": [ + { "hostPath": "/dev/net/tun", "containerPath": "/dev/net/tun" } + ], + "environment": [ + { "key": "OC_URL", "value": "${OC_URL_A}" }, + { "key": "OC_SERVERCERT", "value": "${OC_SERVERCERT_A}" }, + { "key": "OC_AUTHGROUP", "value": "${OC_AUTHGROUP_A}" }, + { "key": "OC_INTERFACE", "value": "tun0" }, + { "key": "OC_SSO_ARGS", "value": "${OC_SSO_ARGS_A}" }, + { "key": "VNC_PASSWORD", "value": "${VNC_PASS_A}" }, + { "key": "NOVNC_PORT", "value": "6901" } + ], + "volumes": [ + { "hostPath": "${APP_DATA_DIR}/data/vpn_a_state", "containerPath": "/root" } + ], + "restartPolicy": "unless-stopped" + }, + { + "name": "ssh_tunnel", + "image": "alpine:3.20", + "networkMode": "service:vpn_a", + "volumes": [ + { "hostPath": "${SSH_KEY_PATH}", "containerPath": "/root/.ssh/id_ed25519-lenovo", "readOnly": true } + ], + "command": "sh -lc \"apk add --no-cache openssh-client && exec ssh -N -i /root/.ssh/id_ed25519-lenovo -o StrictHostKeyChecking=no -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -L 127.0.0.1:8090:localhost:8090 -L 127.0.0.1:2001:localhost:2001 -L 127.0.0.1:36001:localhost:36001 zawa@10.3.1.201\"", + "restartPolicy": "unless-stopped" + } + ] +} diff --git a/apps/cistech-tunnel/docker-compose.yml b/apps/cistech-tunnel/docker-compose.yml new file mode 100755 index 0000000..47dab93 --- /dev/null +++ b/apps/cistech-tunnel/docker-compose.yml @@ -0,0 +1,43 @@ +services: + vpn: + build: ./vpn-openconnect-sso + container_name: cistech-vpn + cap_add: + - NET_ADMIN + devices: + - /dev/net/tun:/dev/net/tun + environment: + OC_URL: ${OC_URL} + OC_SERVERCERT: ${OC_SERVERCERT} + OC_AUTHGROUP: ${OC_AUTHGROUP} + OC_INTERFACE: tun0 + OC_SSO_ARGS: ${OC_SSO_ARGS:- --browser-display-mode shown} + VNC_PASSWORD: ${VNC_PASSWORD:-changeme} + NOVNC_PORT: ${NOVNC_PORT:-6901} + ports: + - "${PUBLISH_ADDR:-0.0.0.0}:${NOVNC_PORT:-6901}:${NOVNC_PORT:-6901}" + volumes: + - vpn_state:/root + restart: unless-stopped + + ssh_tunnel: + image: alpine:3.20 + container_name: cistech-ssh-tunnel + network_mode: "service:vpn" + depends_on: + - vpn + volumes: + - ${SSH_KEY_PATH:-/home/alexz/.ssh/id_ed25519-lenovo}:/root/.ssh/id_ed25519-lenovo:ro + command: > + sh -lc "apk add --no-cache openssh-client && + exec ssh -N -i /root/.ssh/id_ed25519-lenovo \ + -o StrictHostKeyChecking=no -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes \ + -L 0.0.0.0:8090:localhost:8090 \ + -L 0.0.0.0:2001:localhost:2001 \ + -L 0.0.0.0:36001:localhost:36001 \ + -L 0.0.0.0:36000:localhost:36000 \ + zawa@10.3.1.201" + restart: unless-stopped + +volumes: + vpn_state: {} diff --git a/apps/cistech-tunnel/metadata/description.md b/apps/cistech-tunnel/metadata/description.md new file mode 100755 index 0000000..9856255 --- /dev/null +++ b/apps/cistech-tunnel/metadata/description.md @@ -0,0 +1,20 @@ +# Dockerized OpenConnect-SSO with noVNC and Cloudflared + +## Setup +1) Copy `.env.example` to `.env` and fill values (URLs, servercert pins, VNC passwords, cloudflared tokens). + +2) First-time SSO: leave `OC_SSO_ARGS_*=--browser-display-mode visible`. + +3) Build and start: + docker compose build + docker compose up -d vpn_a + # Open http://localhost:6901, complete SSO. + # After success, attach app containers or start cloudflared_a. + +4) Optional: switch to headless after first login: + Set `OC_SSO_ARGS_*=--browser-display-mode hidden` (or `headless`) and restart the vpn service. + +## Notes +- Each VPN runs in its own net namespace; routes from one cannot affect the other or the host. +- DNS from the VPN applies within its container namespace and attached services only. +- Persisted state lives in the named volumes mounted at `/root` (Playwright cache, configs). diff --git a/apps/cistech-tunnel/metadata/logo.jpg b/apps/cistech-tunnel/metadata/logo.jpg new file mode 100755 index 0000000..ddc9a56 Binary files /dev/null and b/apps/cistech-tunnel/metadata/logo.jpg differ diff --git a/apps/cistech-tunnel/vpn-openconnect-sso/Dockerfile b/apps/cistech-tunnel/vpn-openconnect-sso/Dockerfile new file mode 100755 index 0000000..785cb24 --- /dev/null +++ b/apps/cistech-tunnel/vpn-openconnect-sso/Dockerfile @@ -0,0 +1,33 @@ +FROM ubuntu:24.04 +ENV QTWEBENGINE_DISABLE_SANDBOX=1 +ENV QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox --disable-gpu" +ENV DEBIAN_FRONTEND=noninteractive \ + PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \ + VIRTUAL_ENV=/opt/venv \ + PATH=/opt/venv/bin:$PATH + +RUN apt-get update && apt-get install -y \ + openconnect iproute2 iptables ca-certificates \ + python3 python3-pip python3-venv \ + vpnc-scripts curl \ + x11vnc xvfb fluxbox novnc websockify \ + xauth libnss3 libatk1.0-0 libatk-bridge2.0-0 \ + libx11-6 libx11-xcb1 libxcomposite1 libxrandr2 libgbm1 libxdamage1 \ + libpango-1.0-0 fonts-liberation \ + libegl1 libgl1 libopengl0 libdbus-1-3 libglib2.0-0 \ + libxkbcommon0 libxkbcommon-x11-0 \ + libxcb1 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render0 libxcb-render-util0 libxcb-shm0 libxcb-xfixes0 libxcb-xinerama0 libxcb-randr0 libxcb-glx0 \ + sudo \ + && rm -rf /var/lib/apt/lists/* + +RUN apt-get update && (apt-get install -y libasound2t64 || apt-get install -y libasound2) && rm -rf /var/lib/apt/lists/* + +RUN python3 -m venv "$VIRTUAL_ENV" +RUN pip install --no-cache-dir openconnect-sso playwright \ + && python -m playwright install --with-deps chromium + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +EXPOSE 6901 +ENTRYPOINT ["/entrypoint.sh"] diff --git a/apps/cistech-tunnel/vpn-openconnect-sso/entrypoint.sh b/apps/cistech-tunnel/vpn-openconnect-sso/entrypoint.sh new file mode 100755 index 0000000..2b699fe --- /dev/null +++ b/apps/cistech-tunnel/vpn-openconnect-sso/entrypoint.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${OC_URL:?OC_URL is required}" +: "${OC_SERVERCERT:?OC_SERVERCERT is required}" + +NOVNC_PORT="${NOVNC_PORT:-6901}" +VNC_PASSWORD="${VNC_PASSWORD:-changeme}" +DISPLAY_ADDR="${DISPLAY:-:1}" +OC_INTERFACE="${OC_INTERFACE:-tun0}" +OC_SSO_ARGS_DEFAULT="--browser-display-mode shown" + +if [[ "${OC_SSO_ARGS:-$OC_SSO_ARGS_DEFAULT}" == *"shown"* ]]; then + mkdir -p /root/.vnc + x11vnc -storepasswd "$VNC_PASSWORD" /root/.vnc/pass >/dev/null 2>&1 || true + rm -f /tmp/.X1-lock /tmp/.X11-unix/X1 2>/dev/null || true + Xvfb "$DISPLAY_ADDR" -screen 0 ${XVFB_WxHxD:-1280x800x24} +extension RANDR & + sleep 0.5 + export DISPLAY="$DISPLAY_ADDR" + fluxbox >/tmp/fluxbox.log 2>&1 & + x11vnc -display "$DISPLAY_ADDR" -rfbauth /root/.vnc/pass -forever -shared -rfbport 5900 -quiet & + websockify --web=/usr/share/novnc/ 0.0.0.0:"$NOVNC_PORT" localhost:5900 >/tmp/websockify.log 2>&1 & +fi + +OPENCONNECT_CMD=( + /usr/sbin/openconnect + --protocol=anyconnect + --servercert "$OC_SERVERCERT" + --interface "$OC_INTERFACE" + --script /usr/share/vpnc-scripts/vpnc-script +) +[[ -n "${OC_AUTHGROUP:-}" ]] && OPENCONNECT_CMD+=(--authgroup "$OC_AUTHGROUP") +[[ -n "${OC_USERAGENT:-}" ]] && OPENCONNECT_CMD+=(--useragent "$OC_USERAGENT") +[[ -n "${OC_EXTRA_ARGS:-}" ]] && OPENCONNECT_CMD+=(${OC_EXTRA_ARGS}) + +exec openconnect-sso -s "$OC_URL" ${OC_SSO_ARGS:-$OC_SSO_ARGS_DEFAULT} -- "${OPENCONNECT_CMD[@]}" diff --git a/apps/cloudbeaver/config.json b/apps/cloudbeaver/config.json new file mode 100755 index 0000000..529741c --- /dev/null +++ b/apps/cloudbeaver/config.json @@ -0,0 +1,27 @@ +{ + "name": "CloudBeaver", + "id": "cloudbeaver", + "available": true, + "short_desc": "Web-based database management platform", + "author": "DBeaver Corp", + "port": 8978, + "categories": [ + "data", + "development" + ], + "description": "CloudBeaver is a web-based database management platform. It provides a rich web interface for working with databases via web browser. Supports PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, DB2, Firebird, SQLite, and many more databases.", + "tipi_version": 1, + "version": "25.2.5", + "source": "https://github.com/dbeaver/cloudbeaver", + "website": "https://cloudbeaver.io/", + "exposable": true, + "dynamic_config": true, + "no_gui": false, + "form_fields": [], + "supported_architectures": [ + "arm64", + "amd64" + ], + "created_at": 1732924800000, + "updated_at": 1732924800000 +} diff --git a/apps/cloudbeaver/docker-compose.json b/apps/cloudbeaver/docker-compose.json new file mode 100755 index 0000000..3e2bb24 --- /dev/null +++ b/apps/cloudbeaver/docker-compose.json @@ -0,0 +1,24 @@ +{ + "services": [ + { + "name": "cloudbeaver", + "image": "dbeaver/cloudbeaver:25.2.5", + "isMain": true, + "internalPort": 8978, + "extraHosts": [ + "host.docker.internal:host-gateway" + ], + "environment": { + "CB_SERVER_NAME": "CloudBeaver", + "CB_ADMIN_NAME": "admin", + "CB_ADMIN_PASSWORD": "admin" + }, + "volumes": [ + { + "hostPath": "${APP_DATA_DIR}/data", + "containerPath": "/opt/cloudbeaver/workspace" + } + ] + } + ] +} diff --git a/apps/cloudbeaver/docker-compose.yml b/apps/cloudbeaver/docker-compose.yml new file mode 100755 index 0000000..d9922b9 --- /dev/null +++ b/apps/cloudbeaver/docker-compose.yml @@ -0,0 +1,46 @@ +services: + cloudbeaver: + image: dbeaver/cloudbeaver:25.2.5 + restart: unless-stopped + networks: + cloudbeaver_runtipi_network: + gw_priority: 0 + tipi_main_network: + gw_priority: 1 + extra_hosts: + - host.docker.internal:host-gateway + environment: + CB_SERVER_NAME: CloudBeaver + CB_ADMIN_NAME: admin + CB_ADMIN_PASSWORD: admin + ports: + - ${APP_PORT}:8978 + volumes: + - ${APP_DATA_DIR}/data:/opt/cloudbeaver/workspace + labels: + generated: true + traefik.enable: true + traefik.docker.network: runtipi_tipi_main_network + traefik.http.middlewares.cloudbeaver-runtipi-web-redirect.redirectscheme.scheme: https + traefik.http.services.cloudbeaver-runtipi.loadbalancer.server.port: "8978" + traefik.http.routers.cloudbeaver-runtipi-insecure.rule: Host(`${APP_DOMAIN}`) + traefik.http.routers.cloudbeaver-runtipi-insecure.entrypoints: web + traefik.http.routers.cloudbeaver-runtipi-insecure.service: cloudbeaver-runtipi + traefik.http.routers.cloudbeaver-runtipi-insecure.middlewares: cloudbeaver-runtipi-web-redirect + traefik.http.routers.cloudbeaver-runtipi.rule: Host(`${APP_DOMAIN}`) + traefik.http.routers.cloudbeaver-runtipi.entrypoints: websecure + traefik.http.routers.cloudbeaver-runtipi.service: cloudbeaver-runtipi + traefik.http.routers.cloudbeaver-runtipi.tls.certresolver: myresolver + runtipi.managed: true + runtipi.appurn: cloudbeaver:runtipi +networks: + tipi_main_network: + name: runtipi_tipi_main_network + external: true + cloudbeaver_runtipi_network: + name: cloudbeaver_runtipi_network + external: false + ipam: + config: + - subnet: 10.128.18.0/24 + diff --git a/apps/cloudbeaver/metadata/description.md b/apps/cloudbeaver/metadata/description.md new file mode 100755 index 0000000..89bf59d --- /dev/null +++ b/apps/cloudbeaver/metadata/description.md @@ -0,0 +1,27 @@ +# CloudBeaver + +CloudBeaver is a web-based database management platform that provides a rich web interface for working with databases directly from your browser. + +## Features + +- **Multi-Database Support**: Works with PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, DB2, SQLite, Firebird, and many more +- **Web-Based Interface**: No desktop client needed - access from any modern web browser +- **Connection Management**: Easily manage multiple database connections +- **SQL Editor**: Full-featured SQL editor with syntax highlighting and autocomplete +- **Data Visualization**: View and edit data in intuitive table views +- **Query History**: Keep track of all your executed queries +- **User Management**: Multi-user support with role-based access control +- **Import/Export**: Import and export data in various formats + +## Getting Started + +After installation: + +1. Access CloudBeaver at the configured port (default: **8978**) +2. Default credentials: **admin** / **admin** +3. Change the default password on first login +4. Create database connections to start managing your databases + +## Connecting to Host Databases + +To connect to databases running on your host machine, use **`host.docker.internal`** as the hostname in your connection settings. diff --git a/apps/cloudbeaver/metadata/logo.jpg b/apps/cloudbeaver/metadata/logo.jpg new file mode 100755 index 0000000..84cb624 Binary files /dev/null and b/apps/cloudbeaver/metadata/logo.jpg differ diff --git a/apps/nginx-proxy-manager/config.json b/apps/nginx-proxy-manager/config.json new file mode 100755 index 0000000..723145f --- /dev/null +++ b/apps/nginx-proxy-manager/config.json @@ -0,0 +1,27 @@ +{ + "name": "Nginx Proxy Manager", + "id": "nginx-proxy-manager", + "available": true, + "short_desc": "Docker container for managing Nginx proxy hosts with a simple, powerful web interface", + "author": "jc21", + "port": 81, + "categories": [ + "utilities", + "network" + ], + "description": "Nginx Proxy Manager enables you to easily forward to your websites running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt.", + "tipi_version": 1, + "version": "2.13.5", + "source": "https://github.com/NginxProxyManager/nginx-proxy-manager", + "website": "https://nginxproxymanager.com", + "exposable": true, + "dynamic_config": true, + "no_gui": false, + "form_fields": [], + "supported_architectures": [ + "arm64", + "amd64" + ], + "created_at": 1731607800000, + "updated_at": 1731607800000 +} diff --git a/apps/nginx-proxy-manager/docker-compose.json b/apps/nginx-proxy-manager/docker-compose.json new file mode 100755 index 0000000..88d8cec --- /dev/null +++ b/apps/nginx-proxy-manager/docker-compose.json @@ -0,0 +1,37 @@ +{ + "schemaVersion": 2, + "services": [ + { + "name": "nginx-proxy-manager", + "image": "jc21/nginx-proxy-manager:2.13.5", + "isMain": true, + "internalPort": 81, + "addPorts": [ + { + "containerPort": 80, + "hostPort": 1080 + }, + { + "containerPort": 443, + "hostPort": 10443 + } + ], + "environment": [ + { + "key": "DISABLE_IPV6", + "value": "true" + } + ], + "volumes": [ + { + "hostPath": "${APP_DATA_DIR}/data", + "containerPath": "/data" + }, + { + "hostPath": "${APP_DATA_DIR}/letsencrypt", + "containerPath": "/etc/letsencrypt" + } + ] + } + ] +} diff --git a/apps/nginx-proxy-manager/docker-compose.yml b/apps/nginx-proxy-manager/docker-compose.yml new file mode 100755 index 0000000..033ca31 --- /dev/null +++ b/apps/nginx-proxy-manager/docker-compose.yml @@ -0,0 +1,45 @@ +services: + nginx-proxy-manager: + image: jc21/nginx-proxy-manager:2.13.5 + restart: unless-stopped + networks: + nginx-proxy-manager_runtipi_network: + gw_priority: 0 + tipi_main_network: + gw_priority: 1 + environment: + DISABLE_IPV6: "true" + ports: + - 1080:80 + - 10443:443 + - ${APP_PORT}:81 + volumes: + - ${APP_DATA_DIR}/data:/data + - ${APP_DATA_DIR}/letsencrypt:/etc/letsencrypt + labels: + generated: true + traefik.enable: true + traefik.docker.network: runtipi_tipi_main_network + traefik.http.middlewares.nginx-proxy-manager-runtipi-web-redirect.redirectscheme.scheme: https + traefik.http.services.nginx-proxy-manager-runtipi.loadbalancer.server.port: "81" + traefik.http.routers.nginx-proxy-manager-runtipi-insecure.rule: Host(`${APP_DOMAIN}`) + traefik.http.routers.nginx-proxy-manager-runtipi-insecure.entrypoints: web + traefik.http.routers.nginx-proxy-manager-runtipi-insecure.service: nginx-proxy-manager-runtipi + traefik.http.routers.nginx-proxy-manager-runtipi-insecure.middlewares: nginx-proxy-manager-runtipi-web-redirect + traefik.http.routers.nginx-proxy-manager-runtipi.rule: Host(`${APP_DOMAIN}`) + traefik.http.routers.nginx-proxy-manager-runtipi.entrypoints: websecure + traefik.http.routers.nginx-proxy-manager-runtipi.service: nginx-proxy-manager-runtipi + traefik.http.routers.nginx-proxy-manager-runtipi.tls.certresolver: myresolver + runtipi.managed: true + runtipi.appurn: nginx-proxy-manager:runtipi +networks: + tipi_main_network: + name: runtipi_tipi_main_network + external: true + nginx-proxy-manager_runtipi_network: + name: nginx-proxy-manager_runtipi_network + external: false + ipam: + config: + - subnet: 10.128.11.0/24 + diff --git a/apps/nginx-proxy-manager/metadata/description.md b/apps/nginx-proxy-manager/metadata/description.md new file mode 100755 index 0000000..5159c26 --- /dev/null +++ b/apps/nginx-proxy-manager/metadata/description.md @@ -0,0 +1,108 @@ +

+ +

+ + + + + + + +

+ +This project comes as a pre-built docker image that enables you to easily forward to your websites +running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt. + +- [Quick Setup](#quick-setup) +- [Full Setup](https://nginxproxymanager.com/setup/) +- [Screenshots](https://nginxproxymanager.com/screenshots/) + +## Project Goal + +I created this project to fill a personal need to provide users with an easy way to accomplish reverse +proxying hosts with SSL termination and it had to be so easy that a monkey could do it. This goal hasn't changed. +While there might be advanced options they are optional and the project should be as simple as possible +so that the barrier for entry here is low. + +Buy Me A Coffee + + +## Features + +- Beautiful and Secure Admin Interface based on [Tabler](https://tabler.github.io/) +- Easily create forwarding domains, redirections, streams and 404 hosts without knowing anything about Nginx +- Free SSL using Let's Encrypt or provide your own custom SSL certificates +- Access Lists and basic HTTP Authentication for your hosts +- Advanced Nginx configuration available for super users +- User management, permissions and audit log + + +## Hosting your home network + +I won't go in to too much detail here but here are the basics for someone new to this self-hosted world. + +1. Your home router will have a Port Forwarding section somewhere. Log in and find it +2. Add port forwarding for port 80 and 443 to the server hosting this project +3. Configure your domain name details to point to your home, either with a static ip or a service like DuckDNS or [Amazon Route53](https://github.com/jc21/route53-ddns) +4. Use the Nginx Proxy Manager as your gateway to forward to your other web based services + +## Quick Setup + +1. Install Docker and Docker-Compose + +- [Docker Install documentation](https://docs.docker.com/install/) +- [Docker-Compose Install documentation](https://docs.docker.com/compose/install/) + +2. Create a docker-compose.yml file similar to this: + +```yml +services: + app: + image: 'docker.io/jc21/nginx-proxy-manager:latest' + restart: unless-stopped + ports: + - '80:80' + - '81:81' + - '443:443' + volumes: + - ./data:/data + - ./letsencrypt:/etc/letsencrypt +``` + +This is the bare minimum configuration required. See the [documentation](https://nginxproxymanager.com/setup/) for more. + +3. Bring up your stack by running + +```bash +docker compose up -d +``` + +4. Log in to the Admin UI + +When your docker container is running, connect to it on port `81` for the admin interface. +Sometimes this can take a little bit because of the entropy of keys. + +[http://127.0.0.1:81](http://127.0.0.1:81) + + +## Contributing + +All are welcome to create pull requests for this project, against the `develop` branch. Official releases are created from the `master` branch. + +CI is used in this project. All PR's must pass before being considered. After passing, +docker builds for PR's are available on dockerhub for manual verifications. + +Documentation within the `develop` branch is available for preview at +[https://develop.nginxproxymanager.com](https://develop.nginxproxymanager.com) + + +### Contributors + +Special thanks to [all of our contributors](https://github.com/NginxProxyManager/nginx-proxy-manager/graphs/contributors). + + +## Getting Support + +1. [Found a bug?](https://github.com/NginxProxyManager/nginx-proxy-manager/issues) +2. [Discussions](https://github.com/NginxProxyManager/nginx-proxy-manager/discussions) +3. [Reddit](https://reddit.com/r/nginxproxymanager) \ No newline at end of file diff --git a/apps/nginx-proxy-manager/metadata/logo.jpg b/apps/nginx-proxy-manager/metadata/logo.jpg new file mode 100755 index 0000000..6220572 Binary files /dev/null and b/apps/nginx-proxy-manager/metadata/logo.jpg differ diff --git a/apps/nginx-proxy-manager/metadata/logo.png b/apps/nginx-proxy-manager/metadata/logo.png new file mode 100755 index 0000000..6220572 Binary files /dev/null and b/apps/nginx-proxy-manager/metadata/logo.png differ diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..a285984 Binary files /dev/null and b/bun.lockb differ diff --git a/config.js b/config.js new file mode 100755 index 0000000..9a95d69 --- /dev/null +++ b/config.js @@ -0,0 +1,3 @@ +export default { + allowedCommands: ["bun ./scripts/update-config.ts", "bun install && bun run test"], +}; diff --git a/package-lock.json b/package-lock.json new file mode 100755 index 0000000..89737fb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,293 @@ +{ + "name": "example-appstore", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "example-appstore", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@runtipi/common": "^0.8.0", + "bun": "^1.3.4", + "zod-validation-error": "^3.4.0" + }, + "devDependencies": { + "@types/bun": "latest", + "@types/node": "^22.14.1" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } + }, + "node_modules/@oven/bun-darwin-aarch64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.3.4.tgz", + "integrity": "sha512-2Ie4jDGvNGuPSD+pyyBKL8dJmX+bZfDNYEalwgROImVtwB1XYAatJK20dMaRlPA7jOhjvS9Io+4IZAJu7Js0AA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.3.4.tgz", + "integrity": "sha512-4/BJojT8hk5g6Gecjn5yI7y96/+9Mtzsvdp9+2dcy9sTMdlV7jBvDzswqyJPZyQqw0F3HV3Vu9XuMubZwKd9lA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64-baseline": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.3.4.tgz", + "integrity": "sha512-ZYxzIOCDqylTMsnWYERjKMMuK2b4an4qbloBmUZTwLHmVzos00yrhtpitZhJBgH6yB/l4Q5eoJ2W98UKtFFeiQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-linux-aarch64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.3.4.tgz", + "integrity": "sha512-8DUIlanftMdFxLGq2FxwKwfrp8O4ZofF/8Oc6lxCyEFmg2hixbHhL04+fPfJIi5D4hZloynxZdwTeDbGv/Kc4A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-aarch64-musl": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64-musl/-/bun-linux-aarch64-musl-1.3.4.tgz", + "integrity": "sha512-6UtmM4wXgRKz+gnLZEfddfsuBSVQpJr09K12e5pbdnLzeWgXYlBT5FG8S7SVn1t6cbgBMnigEsFjWwfTuMNoCw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.3.4.tgz", + "integrity": "sha512-03iSDMqdrmIFAsvsRptq+A7EGNjkg20dNzPnqxAlXHk5rc1PeIRWIP0eIn0i3nI6mmdj33mimf9AGr0+d0lKMg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-baseline": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.3.4.tgz", + "integrity": "sha512-ZMGPbFPqmG/VYJv61D+Y1V7T23jPK57vYl7yYLakmkTRjG6vcJ0Akhb2qR1iW94rHvfEBjeuVDAZBp8Qp9oyWA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-musl": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-musl/-/bun-linux-x64-musl-1.3.4.tgz", + "integrity": "sha512-xUXPuJHndGhk4K3Cx1FgTyTgDZOn+ki3eWvdXYqKdfi0EaNA9KpUq+/vUtpJbZRjzpHs9L+OJcdDILq5H0LX4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-musl-baseline": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-musl-baseline/-/bun-linux-x64-musl-baseline-1.3.4.tgz", + "integrity": "sha512-qsGSSlNsxiX8lAayK2uYCfMLtqu776F0nn7qoyzg9Ti7mElM3woNh7RtGClTwQ6qsp5/UvgqT9g4pLaDHmqJFg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-windows-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.3.4.tgz", + "integrity": "sha512-nswsuN6+HZPim6x4tFpDFpMa/qpTKfywbGvCkzxwrbJO9MtpuW/54NA1nFbHhpV14OLU0xuxyBj2PK4FHq4MlA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oven/bun-windows-x64-baseline": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.3.4.tgz", + "integrity": "sha512-ZQiSDFfSUdOrPTiL2GvkxlC/kMED4fsJwdZnwJK6S9ylXnk9xY/9ZXfe1615SFLQl2LsVRzJAtjQLeM0BifIKQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@runtipi/common": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@runtipi/common/-/common-0.8.1.tgz", + "integrity": "sha512-x4K+Cn1LRsU1R0MyJBzAgjF457OyuJgi9RwcUhOpTSv6xPK+D8urM8qSQ9uLwK/2p/i2zGThxU6yUtsfkdnj/A==", + "license": "MIT", + "dependencies": { + "zod": "^3.25.30", + "zod-to-json-schema": "^3.24.5" + } + }, + "node_modules/@types/bun": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.4.tgz", + "integrity": "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-types": "1.3.4" + } + }, + "node_modules/@types/node": { + "version": "22.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz", + "integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/bun": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/bun/-/bun-1.3.4.tgz", + "integrity": "sha512-xV6KgD5ImquuKsoghzbWmYzeCXmmSgN6yJGz444hri2W+NGKNRFUNrEhy9+/rRXbvNA2qF0K0jAwqFNy1/GhBg==", + "cpu": [ + "arm64", + "x64" + ], + "hasInstallScript": true, + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32" + ], + "bin": { + "bun": "bin/bun.exe", + "bunx": "bin/bunx.exe" + }, + "optionalDependencies": { + "@oven/bun-darwin-aarch64": "1.3.4", + "@oven/bun-darwin-x64": "1.3.4", + "@oven/bun-darwin-x64-baseline": "1.3.4", + "@oven/bun-linux-aarch64": "1.3.4", + "@oven/bun-linux-aarch64-musl": "1.3.4", + "@oven/bun-linux-x64": "1.3.4", + "@oven/bun-linux-x64-baseline": "1.3.4", + "@oven/bun-linux-x64-musl": "1.3.4", + "@oven/bun-linux-x64-musl-baseline": "1.3.4", + "@oven/bun-windows-x64": "1.3.4", + "@oven/bun-windows-x64-baseline": "1.3.4" + } + }, + "node_modules/bun-types": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.4.tgz", + "integrity": "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", + "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + }, + "node_modules/zod-validation-error": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.4.tgz", + "integrity": "sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.24.4" + } + } + } +} diff --git a/package.json b/package.json new file mode 100755 index 0000000..925fe34 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "example-appstore", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "test": "bun test" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/bun": "latest", + "@types/node": "^22.14.1" + }, + "dependencies": { + "@runtipi/common": "^0.8.0", + "bun": "^1.2.10", + "zod-validation-error": "^3.4.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } +} diff --git a/renovate.json b/renovate.json new file mode 100755 index 0000000..936fd91 --- /dev/null +++ b/renovate.json @@ -0,0 +1,62 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "automerge": false, + "extends": [ + "config:recommended" + ], + "addLabels": [ + "renovate" + ], + "enabledManagers": ["regex"], + "automergeStrategy": "rebase", + "customManagers": [ + { + "customType": "regex", + "fileMatch": [ + "^.*docker-compose\\.json$" + ], + "matchStrings": [ + "\"image\": \"(?.*?):(?.*?)\"," + ], + "datasourceTemplate": "docker" + } + ], + "packageRules": [ + { + "matchUpdateTypes": [ + "minor", + "major", + "patch", + "pin", + "digest" + ], + "automerge": false + }, + { + "matchDepTypes": [ + "devDependencies" + ], + "automerge": false + }, + { + "matchPackageNames": [ + "mariadb", + "mysql", + "monogdb", + "postgres", + "redis" + ], + "enabled": false + } + ], + "postUpgradeTasks": { + "commands": [ + "bun ./scripts/update-config.ts {{packageFile}} {{newVersion}}", + "bun install && bun run test" + ], + "fileFilters": [ + "**/*" + ], + "executionMode": "update" + } +} diff --git a/scripts/update-config.ts b/scripts/update-config.ts new file mode 100755 index 0000000..42891c7 --- /dev/null +++ b/scripts/update-config.ts @@ -0,0 +1,35 @@ +import path from "node:path"; +import fs from "fs/promises"; + +const packageFile = process.argv[2]; +const newVersion = process.argv[3]; + +type AppConfig = { + tipi_version: string; + version: string; + updated_at: number; +}; + +const updateAppConfig = async (packageFile: string, newVersion: string) => { + try { + const packageRoot = path.dirname(packageFile); + const configPath = path.join(packageRoot, "config.json"); + + const config = await fs.readFile(configPath, "utf-8"); + const configParsed = JSON.parse(config) as AppConfig; + + configParsed.tipi_version = configParsed.tipi_version + 1; + configParsed.version = newVersion; + configParsed.updated_at = new Date().getTime(); + + await fs.writeFile(configPath, JSON.stringify(configParsed, null, 2)); + } catch (e) { + console.error(`Failed to update app config, error: ${e}`); + } +}; + +if (!packageFile || !newVersion) { + console.error("Usage: node update-config.js "); + process.exit(1); +} +updateAppConfig(packageFile, newVersion); diff --git a/tsconfig.json b/tsconfig.json new file mode 100755 index 0000000..01ef57a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "verbatimModuleSyntax": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "module": "NodeNext", + "outDir": "dist", + "sourceMap": true, + "lib": [ + "es2022" + ] + }, + "include": [ + "**/*.ts", + ], +}