mirror of
https://github.com/Dokploy/templates.git
synced 2026-11-10 01:33:12 -06:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
984cf0b4d4 | ||
|
|
5b54bdf266 |
@@ -0,0 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 📚 Documentation
|
||||
url: https://docs.dokploy.com
|
||||
about: Check out the official Dokploy documentation for guides and references
|
||||
- name: 🌐 Website
|
||||
url: https://dokploy.com
|
||||
about: Visit the official Dokploy website
|
||||
- name: 💬 GitHub Discussions
|
||||
url: https://github.com/Dokploy/dokploy/discussions
|
||||
about: Join the community discussions for questions and support
|
||||
@@ -30,8 +30,6 @@ Exemplary blueprint: `blueprints/ghost/`—`docker-compose.yml` exposes port 236
|
||||
|
||||
1. **Add/Update Template**:
|
||||
|
||||
- **REQUIREMENT**: Service **MUST** be open source. Only add templates for applications with an open-source license (e.g., MIT, Apache, GPL, AGPL). Proprietary or closed-source services are not allowed.
|
||||
- **Verify Docker Images**: Before using any Docker image in `docker-compose.yml`, verify it exists using `docker manifest inspect <image:tag>` (e.g., `docker manifest inspect docker.io/bitnami/discourse:3.5.0`). This ensures the image is available and prevents deployment failures.
|
||||
- Create `blueprints/<id>/` (e.g., `ghost`).
|
||||
- Implement `docker-compose.yml` (single service typical; use volumes for persistence).
|
||||
- Configure `template.toml`—reference vars in `[config.domains]`, `[config.env]`, `[config.mounts]`.
|
||||
@@ -47,19 +45,17 @@ No tests in repo—focus on manual validation via scripts and Dokploy deploys. D
|
||||
|
||||
## Conventions and Patterns
|
||||
|
||||
- **Open Source Requirement**: **ALL services MUST be open source**. Only applications with open-source licenses (MIT, Apache, GPL, AGPL, etc.) are allowed. Proprietary or closed-source services are strictly prohibited.
|
||||
- **Template IDs**: Lowercase, kebab-case (e.g., `active-pieces`); unique across repo—enforced by dedupe script.
|
||||
- **Docker Compose**: Minimal—omit `ports` (Dokploy proxies), persistent volumes (e.g., `- db-data:/var/lib/postgresql/data`). Services named after folder (e.g., `ghost` service).
|
||||
- **Docker Compose**: Minimal—omit `ports` (Dokploy proxies), `restart: unless-stopped`, persistent volumes (e.g., `- db-data:/var/lib/postgresql/data`). Services named after folder (e.g., `ghost` service).
|
||||
- **template.toml**:
|
||||
- Variables: `[variables] main_domain = "${domain}"`; use helpers for secrets (`${password:64}`, `${base64:32}`).
|
||||
- Domains: `[[config.domains]] serviceName = "<service>" port = 80 host = "${main_domain}"` (path="/" optional).
|
||||
- Env: `[[config.env]]` array of "KEY=VALUE" strings, interpolating vars (e.g., "DB_PASSWORD=${db_pass}").
|
||||
- **URL Variables**: When environment variables require URLs (e.g., `WEB_URL`, `NEXTAUTH_URL`, `PUBLIC_URL`), **always use HTTP by default** (e.g., `"http://${main_domain}"`). HTTPS should only be used if explicitly required by the application or when using a reverse proxy with SSL termination.
|
||||
- Mounts: `[[config.mounts]] filePath = "/etc/config" content = """multi-line\ncontent"""`.
|
||||
- JWT helper: `${jwt:secret_var:payload_var}` for auth tokens; payload as JSON string with `exp: ${timestamps:YYYY-MM-DDTHH:mm:ssZ}`.
|
||||
- **Meta.json**: Entries as JSON objects; tags array of lowercase strings (e.g., ["monitoring", "database"]); links object with `github`, `website`, `docs`.
|
||||
- **No Networks**: Rely on Dokploy's isolated deployments—avoid explicit `networks:`.
|
||||
- **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`. **NEVER use `latest` tag**—it can break templates when upstream images change unexpectedly. **Always verify image exists** using `docker manifest inspect <image:tag>` before committing.
|
||||
- **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`.
|
||||
- **Logos**: SVG preferred; size ~128x128; file name in `meta.json.logo` (e.g., "ghost.svg").
|
||||
|
||||
Cross-component: Templates are independent and ship as static blueprints/meta.
|
||||
|
||||
@@ -5,6 +5,9 @@ on:
|
||||
workflows: [Build Preview Deployment]
|
||||
types:
|
||||
- completed
|
||||
pull_request:
|
||||
branches:
|
||||
- canary
|
||||
permissions:
|
||||
actions: read
|
||||
deployments: write
|
||||
|
||||
@@ -17,10 +17,13 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-depth: 0 # Necesitamos el historial completo para comparar con base
|
||||
|
||||
- name: Set up Docker Compose
|
||||
run: docker compose version
|
||||
run: |
|
||||
echo "🐳 Setting up Docker Compose..."
|
||||
# Docker Compose V2 viene preinstalado en ubuntu-latest
|
||||
docker compose version
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
@@ -33,89 +36,282 @@ jobs:
|
||||
version: 8
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd build-scripts && pnpm install
|
||||
|
||||
- name: Detect changed blueprints
|
||||
id: changed
|
||||
run: |
|
||||
echo "📦 Installing Node.js dependencies..."
|
||||
cd build-scripts && pnpm install
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
run: |
|
||||
echo "🔍 Detecting changed files..."
|
||||
|
||||
# Obtener la rama base
|
||||
BASE_SHA=$(git merge-base HEAD origin/${{ github.base_ref }})
|
||||
|
||||
# Obtener todos los blueprints que tienen cambios (en docker-compose.yml o template.toml)
|
||||
CHANGED_BLUEPRINTS=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | \
|
||||
grep -E 'blueprints/[^/]+/(docker-compose\.yml|template\.toml)$' | \
|
||||
sed 's|blueprints/\([^/]*\)/.*|\1|' | \
|
||||
sort -u)
|
||||
# Encontrar todos los archivos docker-compose.yml y template.toml modificados/agregados
|
||||
CHANGED_COMPOSE=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/docker-compose\.yml$' || true)
|
||||
CHANGED_TOML=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/template\.toml$' || true)
|
||||
|
||||
echo "Changed blueprints:"
|
||||
echo "$CHANGED_BLUEPRINTS" | while read blueprint; do
|
||||
[ -n "$blueprint" ] && echo " - $blueprint"
|
||||
done
|
||||
# Crear lista de directorios únicos que tienen cambios
|
||||
CHANGED_DIRS=$(echo -e "$CHANGED_COMPOSE\n$CHANGED_TOML" | sed 's|blueprints/\([^/]*\)/.*|\1|' | sort -u)
|
||||
|
||||
# Guardar lista de blueprints (una por línea)
|
||||
echo "blueprints<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGED_BLUEPRINTS" >> $GITHUB_OUTPUT
|
||||
echo "Changed compose files:"
|
||||
echo "$CHANGED_COMPOSE" | while read file; do [ -n "$file" ] && echo " - $file"; done
|
||||
|
||||
echo "Changed TOML files:"
|
||||
echo "$CHANGED_TOML" | while read file; do [ -n "$file" ] && echo " - $file"; done
|
||||
|
||||
echo "Changed directories:"
|
||||
echo "$CHANGED_DIRS" | while read dir; do [ -n "$dir" ] && echo " - $dir"; done
|
||||
|
||||
# Guardar para usar en siguientes pasos
|
||||
echo "compose_files<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGED_COMPOSE" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "toml_files<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGED_TOML" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "directories<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGED_DIRS" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Validate blueprints
|
||||
- name: Validate Docker Compose files syntax
|
||||
id: validate-compose-syntax
|
||||
run: |
|
||||
BLUEPRINTS="${{ steps.changed.outputs.blueprints }}"
|
||||
echo "🔍 Validating Docker Compose files syntax..."
|
||||
|
||||
if [ -z "$BLUEPRINTS" ] || [ "$BLUEPRINTS" = "" ]; then
|
||||
echo "ℹ️ No blueprints changed, skipping validation"
|
||||
ERROR=0
|
||||
COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}"
|
||||
|
||||
if [ -z "$COMPOSE_FILES" ]; then
|
||||
echo "ℹ️ No docker-compose.yml files changed, skipping validation"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ERROR=0
|
||||
|
||||
# Convertir a array para evitar problemas con subshells
|
||||
mapfile -t BLUEPRINT_ARRAY <<< "$BLUEPRINTS"
|
||||
|
||||
# Iterar sobre cada blueprint
|
||||
for blueprint in "${BLUEPRINT_ARRAY[@]}"; do
|
||||
[ -z "$blueprint" ] && continue
|
||||
echo "$COMPOSE_FILES" | while read -r compose_file; do
|
||||
if [ -z "$compose_file" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
BLUEPRINT_PATH="blueprints/$blueprint"
|
||||
COMPOSE_FILE="$BLUEPRINT_PATH/docker-compose.yml"
|
||||
TOML_FILE="$BLUEPRINT_PATH/template.toml"
|
||||
TEMPLATE_DIR=$(dirname "$compose_file")
|
||||
TEMPLATE_NAME=$(basename "$TEMPLATE_DIR")
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📦 Validating: $blueprint"
|
||||
echo "📦 Validating syntax: $TEMPLATE_NAME"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# 1. Validar best practices de docker-compose.yml
|
||||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||
echo "⚠️ WARNING: docker-compose.yml not found"
|
||||
# Validar sintaxis de docker-compose.yml usando docker compose
|
||||
echo "🔍 Validating docker-compose.yml syntax..."
|
||||
if ! docker compose -f "$compose_file" config > /dev/null 2>&1; then
|
||||
echo "❌ ERROR: docker-compose.yml syntax is invalid in $TEMPLATE_NAME"
|
||||
echo "Running docker compose config to show errors:"
|
||||
docker compose -f "$compose_file" config 2>&1 || true
|
||||
ERROR=1
|
||||
continue
|
||||
fi
|
||||
echo "🔍 Validating docker-compose.yml best practices..."
|
||||
if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$COMPOSE_FILE" --verbose); then
|
||||
ERROR=1
|
||||
continue
|
||||
fi
|
||||
|
||||
# 3. Validar template.toml
|
||||
if [ -f "$TOML_FILE" ]; then
|
||||
echo "🔍 Validating template.toml..."
|
||||
if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$BLUEPRINT_PATH" --verbose); then
|
||||
ERROR=1
|
||||
continue
|
||||
fi
|
||||
else
|
||||
echo "⚠️ WARNING: template.toml not found"
|
||||
ERROR=1
|
||||
continue
|
||||
echo "✅ docker-compose.yml syntax is valid"
|
||||
fi
|
||||
|
||||
echo "✅ All validations passed for $blueprint"
|
||||
# Obtener lista de servicios del compose
|
||||
SERVICES=$(docker compose -f "$compose_file" config --services 2>/dev/null || echo "")
|
||||
echo "📋 Services found in docker-compose.yml:"
|
||||
echo "$SERVICES" | while read service; do
|
||||
[ -n "$service" ] && echo " - $service"
|
||||
done
|
||||
|
||||
# Guardar servicios para validación posterior
|
||||
echo "$SERVICES" > "/tmp/${TEMPLATE_NAME}_services.txt"
|
||||
done
|
||||
|
||||
if [ $ERROR -eq 1 ]; then
|
||||
echo ""
|
||||
echo "❌ Validation failed for one or more blueprints"
|
||||
echo "❌ Docker Compose syntax validation failed"
|
||||
exit 1
|
||||
else
|
||||
echo ""
|
||||
echo "✅ All blueprints validated successfully"
|
||||
echo "✅ All Docker Compose files have valid syntax"
|
||||
fi
|
||||
|
||||
- name: Validate Docker Compose best practices
|
||||
id: validate-compose-practices
|
||||
run: |
|
||||
echo "🔍 Validating Docker Compose best practices..."
|
||||
|
||||
ERROR=0
|
||||
COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}"
|
||||
|
||||
if [ -z "$COMPOSE_FILES" ]; then
|
||||
echo "ℹ️ No docker-compose.yml files changed, skipping validation"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Convert to array to avoid subshell issues with pipe
|
||||
# This ensures ERROR=1 inside the loop propagates to the parent shell
|
||||
mapfile -t COMPOSE_ARRAY <<< "$COMPOSE_FILES"
|
||||
|
||||
for compose_file in "${COMPOSE_ARRAY[@]}"; do
|
||||
if [ -z "$compose_file" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
TEMPLATE_DIR=$(dirname "$compose_file")
|
||||
TEMPLATE_NAME=$(basename "$TEMPLATE_DIR")
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📦 Validating best practices: $TEMPLATE_NAME"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Validar usando el script de TypeScript
|
||||
if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$compose_file" --verbose); then
|
||||
ERROR=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $ERROR -eq 1 ]; then
|
||||
echo ""
|
||||
echo "❌ Docker Compose best practices validation failed"
|
||||
exit 1
|
||||
else
|
||||
echo ""
|
||||
echo "✅ All Docker Compose files follow best practices"
|
||||
fi
|
||||
|
||||
- name: Validate template.toml files
|
||||
id: validate-toml
|
||||
run: |
|
||||
echo "🔍 Validating template.toml files..."
|
||||
|
||||
ERROR=0
|
||||
DIRECTORIES="${{ steps.changed-files.outputs.directories }}"
|
||||
|
||||
if [ -z "$DIRECTORIES" ]; then
|
||||
echo "ℹ️ No template directories changed, skipping TOML validation"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Convert to array to avoid subshell issues with pipe
|
||||
# This ensures ERROR=1 inside the loop propagates to the parent shell
|
||||
mapfile -t DIRS_ARRAY <<< "$DIRECTORIES"
|
||||
|
||||
for template_dir in "${DIRS_ARRAY[@]}"; do
|
||||
if [ -z "$template_dir" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
TEMPLATE_PATH="blueprints/$template_dir"
|
||||
TOML_FILE="$TEMPLATE_PATH/template.toml"
|
||||
|
||||
if [ ! -f "$TOML_FILE" ]; then
|
||||
echo "⚠️ WARNING: template.toml not found in $template_dir (might be deleted)"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Validating: $template_dir/template.toml"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Validar usando el script de TypeScript con tsx
|
||||
# Ejecutar desde build-scripts para tener acceso a node_modules
|
||||
if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$TEMPLATE_PATH" --verbose); then
|
||||
ERROR=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $ERROR -eq 1 ]; then
|
||||
echo ""
|
||||
echo "❌ template.toml validation failed"
|
||||
exit 1
|
||||
else
|
||||
echo ""
|
||||
echo "✅ All template.toml files are valid"
|
||||
fi
|
||||
|
||||
- name: Test Docker Compose (dry-run)
|
||||
id: test-compose
|
||||
run: |
|
||||
echo "🧪 Testing Docker Compose files (dry-run)..."
|
||||
|
||||
ERROR=0
|
||||
DIRECTORIES="${{ steps.changed-files.outputs.directories }}"
|
||||
|
||||
if [ -z "$DIRECTORIES" ]; then
|
||||
echo "ℹ️ No template directories changed, skipping dry-run test"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "$DIRECTORIES" | while read -r template_dir; do
|
||||
if [ -z "$template_dir" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
COMPOSE_FILE="blueprints/$template_dir/docker-compose.yml"
|
||||
|
||||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🧪 Testing: $template_dir"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Cambiar al directorio del template para resolver rutas relativas
|
||||
cd "blueprints/$template_dir"
|
||||
|
||||
# Validar que docker-compose puede parsear el archivo completamente
|
||||
echo "🔍 Running docker compose config (full validation)..."
|
||||
if docker compose config > /dev/null 2>&1; then
|
||||
echo "✅ Docker Compose file is fully valid and can be processed"
|
||||
|
||||
# Mostrar información útil
|
||||
echo "📋 Configuration summary:"
|
||||
docker compose config --services | while read service; do
|
||||
[ -n "$service" ] && echo " Service: $service"
|
||||
done
|
||||
else
|
||||
echo "❌ ERROR: Docker Compose file failed full validation"
|
||||
docker compose config 2>&1 || true
|
||||
ERROR=1
|
||||
fi
|
||||
|
||||
cd - > /dev/null
|
||||
done
|
||||
|
||||
if [ $ERROR -eq 1 ]; then
|
||||
echo ""
|
||||
echo "❌ Docker Compose dry-run test failed"
|
||||
exit 1
|
||||
else
|
||||
echo ""
|
||||
echo "✅ All Docker Compose files passed dry-run test"
|
||||
fi
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📊 Validation Summary"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
if [ "${{ steps.validate-compose-syntax.outcome }}" == "success" ] && \
|
||||
[ "${{ steps.validate-compose-practices.outcome }}" == "success" ] && \
|
||||
[ "${{ steps.validate-toml.outcome }}" == "success" ] && \
|
||||
[ "${{ steps.test-compose.outcome }}" == "success" ]; then
|
||||
echo "✅ All validations passed!"
|
||||
echo ""
|
||||
echo "Your Docker Compose and template.toml files are valid and ready to merge."
|
||||
else
|
||||
echo "❌ Some validations failed. Please review the errors above."
|
||||
echo ""
|
||||
echo "Common issues to check:"
|
||||
echo " - docker-compose.yml syntax errors"
|
||||
echo " - template.toml syntax errors"
|
||||
echo " - serviceName in template.toml must match service names in docker-compose.yml"
|
||||
echo " - Avoid using container_name, explicit networks, or port mappings"
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
name: Validate Template Metadata
|
||||
name: Validate and Process Meta.json
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [canary]
|
||||
paths:
|
||||
- "blueprints/**"
|
||||
- "build-scripts/generate-meta.js"
|
||||
- "meta.json"
|
||||
paths: ["meta.json"]
|
||||
pull_request:
|
||||
branches: [canary]
|
||||
paths:
|
||||
- "blueprints/**"
|
||||
- "build-scripts/generate-meta.js"
|
||||
- "meta.json"
|
||||
paths: ["meta.json"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -23,19 +17,64 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Reject root meta.json
|
||||
run: |
|
||||
if [ -f "meta.json" ]; then
|
||||
echo "❌ The root meta.json is generated at build time and must not be committed."
|
||||
echo " Add your template's metadata to blueprints/<id>/meta.json instead."
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ No root meta.json present"
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
node-version: "18"
|
||||
|
||||
- name: Validate per-template metadata
|
||||
run: node build-scripts/generate-meta.js --check
|
||||
- name: Validate meta.json structure
|
||||
run: |
|
||||
echo "🔍 Validating meta.json structure..."
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const data = JSON.parse(fs.readFileSync('meta.json', 'utf8'));
|
||||
if (!Array.isArray(data)) throw new Error('meta.json must be an array');
|
||||
console.log('✅ meta.json structure is valid');
|
||||
console.log('📊 Found', data.length, 'entries');
|
||||
"
|
||||
|
||||
- name: Check for duplicates and sort order
|
||||
run: |
|
||||
echo "🔍 Checking for duplicates and sort order..."
|
||||
node build-scripts/process-meta.js --verbose --output /tmp/meta-test.json
|
||||
|
||||
- name: Compare with original
|
||||
run: |
|
||||
echo "🔍 Comparing processed file with original..."
|
||||
if ! diff -q meta.json /tmp/meta-test.json > /dev/null; then
|
||||
echo "⚠️ meta.json needs processing (duplicates found or not sorted)"
|
||||
echo "Original entries:"
|
||||
node -e "console.log(JSON.parse(require('fs').readFileSync('meta.json', 'utf8')).length)"
|
||||
echo "Processed entries:"
|
||||
node -e "console.log(JSON.parse(require('fs').readFileSync('/tmp/meta-test.json', 'utf8')).length)"
|
||||
echo ""
|
||||
echo "To fix this, run: npm run process-meta"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ meta.json is properly deduplicated and sorted"
|
||||
fi
|
||||
|
||||
- name: Validate required fields
|
||||
run: |
|
||||
echo "🔍 Validating required fields..."
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const data = JSON.parse(fs.readFileSync('meta.json', 'utf8'));
|
||||
const required = ['id', 'name', 'version', 'description', 'links', 'logo', 'tags'];
|
||||
let issues = 0;
|
||||
|
||||
data.forEach((item, index) => {
|
||||
const missing = required.filter(field => !item[field]);
|
||||
if (missing.length > 0) {
|
||||
console.log('❌ Entry', index, '(' + item.id + '):', 'Missing fields:', missing.join(', '));
|
||||
issues++;
|
||||
}
|
||||
});
|
||||
|
||||
if (issues > 0) {
|
||||
console.log('🚨 Found', issues, 'entries with missing required fields');
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('✅ All entries have required fields');
|
||||
}
|
||||
"
|
||||
|
||||
@@ -49,10 +49,133 @@ jobs:
|
||||
echo "✅ Blueprint folders validated successfully."
|
||||
fi
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
- name: Validate meta.json structure and required fields
|
||||
run: |
|
||||
echo "🔍 Validating meta.json structure and required fields..."
|
||||
|
||||
- name: Validate per-template metadata (blueprints/<id>/meta.json)
|
||||
run: node build-scripts/generate-meta.js --check
|
||||
# First check if meta.json exists and is valid JSON
|
||||
if [ ! -f "meta.json" ]; then
|
||||
echo "❌ meta.json file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! jq empty meta.json 2>/dev/null; then
|
||||
echo "❌ meta.json is not a valid JSON file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ERROR=0
|
||||
ERRORS_FOUND=""
|
||||
|
||||
# Debug: Show total number of entries
|
||||
TOTAL_ENTRIES=$(jq '. | length' meta.json)
|
||||
echo "📊 Total entries in meta.json: $TOTAL_ENTRIES"
|
||||
|
||||
# Get all entries at once and process them
|
||||
TOTAL_INDEX=$(($TOTAL_ENTRIES - 1))
|
||||
|
||||
for i in $(seq 0 $TOTAL_INDEX); do
|
||||
entry=$(jq -c ".[$i]" meta.json)
|
||||
INDEX=$((i + 1))
|
||||
|
||||
echo "-------------------------------------------"
|
||||
echo "🔍 Checking entry #$INDEX..."
|
||||
|
||||
# Get the ID for better error reporting
|
||||
ID=$(echo "$entry" | jq -r '.id // "UNKNOWN"')
|
||||
echo "📝 Processing entry with ID: $ID"
|
||||
|
||||
# Validate required top-level fields
|
||||
for field in "id" "name" "version" "description" "logo" "links" "tags"; do
|
||||
if [ "$(echo "$entry" | jq "has(\"$field\")")" != "true" ]; then
|
||||
ERROR_MSG="❌ Entry #$INDEX (ID: $ID) is missing required field: $field"
|
||||
echo "$ERROR_MSG"
|
||||
ERRORS_FOUND="${ERRORS_FOUND}${ERROR_MSG}\n"
|
||||
ERROR=1
|
||||
fi
|
||||
done
|
||||
|
||||
# Validate links object required fields
|
||||
if [ "$(echo "$entry" | jq 'has("links")')" == "true" ]; then
|
||||
for link_field in "github" "website" "docs"; do
|
||||
if [ "$(echo "$entry" | jq ".links | has(\"$link_field\")")" != "true" ]; then
|
||||
ERROR_MSG="❌ Entry #$INDEX (ID: $ID): links object is missing required field: $link_field"
|
||||
echo "$ERROR_MSG"
|
||||
ERRORS_FOUND="${ERRORS_FOUND}${ERROR_MSG}\n"
|
||||
ERROR=1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Validate tags array is not empty
|
||||
if [ "$(echo "$entry" | jq '.tags | length')" -eq 0 ]; then
|
||||
ERROR_MSG="❌ Entry #$INDEX (ID: $ID): tags array cannot be empty"
|
||||
echo "$ERROR_MSG"
|
||||
ERRORS_FOUND="${ERRORS_FOUND}${ERROR_MSG}\n"
|
||||
ERROR=1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "-------------------------------------------"
|
||||
if [ $ERROR -eq 1 ]; then
|
||||
echo "❌ meta.json structure validation failed."
|
||||
echo -e "Summary of all errors found:${ERRORS_FOUND}"
|
||||
exit 1
|
||||
else
|
||||
echo "✅ meta.json structure validated successfully."
|
||||
fi
|
||||
|
||||
- name: Validate meta.json matches blueprint folders and logo files
|
||||
run: |
|
||||
echo "🔍 Validating meta.json against blueprint folders and logos..."
|
||||
|
||||
ERROR=0
|
||||
|
||||
# Read all blueprint folder names into an array
|
||||
FOLDERS=($(ls -1 blueprints))
|
||||
|
||||
# Extract ids and logos from meta.json
|
||||
IDS_AND_LOGOS=$(jq -c '.[] | {id, logo}' meta.json)
|
||||
|
||||
# Validate each id in meta.json exists as a folder
|
||||
for item in $IDS_AND_LOGOS; do
|
||||
ID=$(echo "$item" | jq -r '.id')
|
||||
LOGO=$(echo "$item" | jq -r '.logo')
|
||||
|
||||
# Check if folder exists
|
||||
if [ ! -d "blueprints/$ID" ]; then
|
||||
echo "❌ meta.json id \"$ID\" does not have a matching folder in blueprints/"
|
||||
ERROR=1
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if logo file exists inside its folder
|
||||
if [ ! -f "blueprints/$ID/$LOGO" ]; then
|
||||
echo "❌ Logo \"$LOGO\" defined for \"$ID\" does not exist in blueprints/$ID/"
|
||||
ERROR=1
|
||||
fi
|
||||
done
|
||||
|
||||
# Validate each folder has a matching id in meta.json
|
||||
META_IDS=$(jq -r '.[].id' meta.json)
|
||||
for FOLDER in "${FOLDERS[@]}"; do
|
||||
FOUND=0
|
||||
for ID in $META_IDS; do
|
||||
if [ "$FOLDER" == "$ID" ]; then
|
||||
FOUND=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$FOUND" -eq 0 ]; then
|
||||
echo "❌ Folder \"$FOLDER\" has no matching id in meta.json"
|
||||
ERROR=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $ERROR -eq 1 ]; then
|
||||
echo "❌ meta.json validation failed."
|
||||
exit 1
|
||||
else
|
||||
echo "✅ meta.json validated successfully."
|
||||
fi
|
||||
|
||||
+1
-3
@@ -1,3 +1 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
meta.json.backup.*
|
||||
node_modules
|
||||
@@ -29,9 +29,9 @@ This document provides essential context for AI models interacting with this pro
|
||||
- `/src/hooks`: Custom React hooks (e.g., `useFuseSearch.ts` for fuzzy search).
|
||||
- `/src/store`: Zustand state management store.
|
||||
- `/src/lib`: Utility functions and helpers.
|
||||
- `/build-scripts`: Metadata tooling — `generate-meta.js` (aggregates and validates every `blueprints/<id>/meta.json` into the served `meta.json` artifact) and `explode-meta.js` (one-shot migration reference).
|
||||
- `/build-scripts`: Advanced meta.json processing tools with CLI options and JSON schema validation.
|
||||
- `/.github/workflows`: CI/CD workflows for validation, preview builds, and deployments.
|
||||
- Root level: There is NO root `meta.json` in the repo — each template's metadata lives in `blueprints/<id>/meta.json` and the global registry is generated at build time.
|
||||
- Root level: Core processing scripts (`dedupe-and-sort-meta.js`), metadata index (`meta.json`), build configuration (`Makefile`, `package.json`).
|
||||
- **Module Organization:** Node.js scripts for metadata processing, React components for frontend UI, Docker Compose files for service definitions, TOML files for Dokploy configuration. Frontend uses component-based architecture with hooks for business logic and Zustand for global state.
|
||||
|
||||
## 4. Coding Conventions & Style Guide
|
||||
@@ -42,7 +42,7 @@ This document provides essential context for AI models interacting with this pro
|
||||
- React components: PascalCase (`TemplateGrid`, `SearchBar`)
|
||||
- Constants: SCREAMING_SNAKE_CASE (`MAX_BUFFER_SIZE`)
|
||||
- Template IDs: lowercase, kebab-case (`activepieces`, `ghost`) - **MUST** be unique across repository
|
||||
- Files: kebab-case for scripts (`generate-meta.js`), PascalCase for React components (`TemplateGrid.tsx`), kebab-case for directories (`build-scripts`)
|
||||
- Files: snake_case for scripts (`dedupe-and-sort-meta.js`), PascalCase for React components (`TemplateGrid.tsx`), kebab-case for directories (`build-scripts`)
|
||||
- Docker services: **MUST** match blueprint folder name exactly (e.g., `ghost` service in `blueprints/ghost/`)
|
||||
- **API Design:**
|
||||
- **Backend:** Procedural scripting approach with CLI interfaces. Template system uses declarative configuration over imperative code.
|
||||
@@ -65,15 +65,17 @@ This document provides essential context for AI models interacting with this pro
|
||||
## 5. Key Files & Entrypoints
|
||||
|
||||
- **Main Entrypoints:**
|
||||
- **Backend:** `build-scripts/generate-meta.js` - Aggregates and validates all `blueprints/<id>/meta.json` files into the served `meta.json` artifact (`app/public/meta.json`, gitignored).
|
||||
- **Backend:** `dedupe-and-sort-meta.js` - Primary script for processing meta.json file.
|
||||
- **Frontend:** `app/src/main.tsx` - React application entry point.
|
||||
- **Configuration:**
|
||||
- `app/package.json` - Frontend dependencies and pnpm scripts (`dev` and `build` run `generate-meta.js` first).
|
||||
- `blueprints/<id>/meta.json` - Per-template metadata (one object per template; 400+ templates).
|
||||
- `package.json` - Root Node.js project configuration and npm scripts.
|
||||
- `app/package.json` - Frontend dependencies and pnpm scripts.
|
||||
- `meta.json` - Centralized template registry (200+ entries).
|
||||
- `Makefile` - Build automation with targets for processing, validation, and cleanup.
|
||||
- `app/vite.config.ts` - Vite build configuration with static copy plugin.
|
||||
- `app/tsconfig.json` - TypeScript compiler configuration.
|
||||
- **CI/CD Pipeline:**
|
||||
- `.github/workflows/validate-meta.yml` - Validates every `blueprints/<id>/meta.json` (via `generate-meta.js --check`) and rejects a committed root `meta.json`.
|
||||
- `.github/workflows/validate-meta.yml` - Validates meta.json structure, duplicates, and sort order.
|
||||
- `.github/workflows/build-preview.yml` - Builds preview deployments for PRs.
|
||||
- `.github/workflows/deploy-preview.yml` - Deploys previews to Cloudflare Pages.
|
||||
|
||||
@@ -90,26 +92,47 @@ This document provides essential context for AI models interacting with this pro
|
||||
cd app && pnpm install
|
||||
```
|
||||
|
||||
- **Validate template metadata** (CRITICAL: run after adding/editing any `blueprints/<id>/meta.json`)
|
||||
- **Process meta.json** (CRITICAL: Run after ANY meta.json edits)
|
||||
```bash
|
||||
node build-scripts/generate-meta.js --check
|
||||
npm run process-meta
|
||||
# or
|
||||
make process-meta
|
||||
# or
|
||||
node dedupe-and-sort-meta.js
|
||||
```
|
||||
- **Generate the served meta.json artifact** (done automatically by `pnpm dev` / `pnpm build` in `app/`)
|
||||
- **Validate without modifying**
|
||||
```bash
|
||||
node build-scripts/generate-meta.js # writes app/public/meta.json
|
||||
npm run validate-meta
|
||||
# or
|
||||
make validate
|
||||
```
|
||||
- **Quick check for duplicates/sort status**
|
||||
```bash
|
||||
make check
|
||||
```
|
||||
- **Clean backup files**
|
||||
```bash
|
||||
make clean
|
||||
```
|
||||
- **Task Configuration:**
|
||||
- **App scripts (`app/package.json`, pnpm):**
|
||||
- `dev`: Generate meta artifact + start Vite dev server
|
||||
- `build`: Generate meta artifact + typecheck + Vite build
|
||||
- `generate-meta`: Generate the meta artifact only
|
||||
- **NPM Scripts:** Run `npm run` to list available scripts. Key scripts:
|
||||
- `process-meta`: Remove duplicates and sort meta.json
|
||||
- `process-meta-verbose`: Process with detailed output
|
||||
- `validate-meta`: Validate structure without changes
|
||||
- **Makefile Targets:** Run `make help` to list targets. Key targets:
|
||||
- `process-meta`: Process meta.json
|
||||
- `validate`: Validate without modifying
|
||||
- `check`: Quick duplicate/sort check
|
||||
- `build`: Full build process
|
||||
- `clean`: Remove backup files
|
||||
- **Testing:** No formal unit testing framework. Validation occurs through:
|
||||
- Script-based validation of every `blueprints/<id>/meta.json` (`build-scripts/generate-meta.js --check`)
|
||||
- Script-based validation of meta.json structure and schema
|
||||
- Manual testing via Dokploy preview deployments (import base64 from PR preview)
|
||||
- JSON schema validation in `build-scripts/process-meta.js`
|
||||
- TypeScript compilation errors caught during build
|
||||
- ESLint for code quality in frontend
|
||||
- **CI/CD Process:**
|
||||
- **On blueprint changes:** Validates each template's metadata (required fields, id/folder match, logo file exists) and rejects a committed root `meta.json`.
|
||||
- **On meta.json changes:** Validates structure, checks for duplicates, verifies sort order, compares processed vs original.
|
||||
- **On PR creation:** Builds preview deployment, generates base64 import for testing in Dokploy.
|
||||
- **Preview testing:** Use PR description link → Search template → Copy base64 → Import in Dokploy instance.
|
||||
|
||||
@@ -117,10 +140,10 @@ This document provides essential context for AI models interacting with this pro
|
||||
|
||||
- **Contribution Guidelines:**
|
||||
- Follow existing Dokploy template structure strictly. Each blueprint must be independent with no shared state.
|
||||
- **CRITICAL:** Template metadata lives in `blueprints/<id>/meta.json` (one object per template). Never create or edit a root-level `meta.json` — it is generated at build time. Run `node build-scripts/generate-meta.js --check` after any metadata edits.
|
||||
- **CRITICAL:** Always run `node dedupe-and-sort-meta.js` or `npm run process-meta` after ANY meta.json edits.
|
||||
- Test templates in Dokploy preview before submitting PRs (use base64 import from PR preview).
|
||||
- Add logo file (SVG preferred, ~128x128px) to blueprint folder.
|
||||
- Ensure the `id` in `blueprints/<id>/meta.json` exactly matches the blueprint folder name (lowercase kebab-case).
|
||||
- Ensure template `id` in meta.json exactly matches blueprint folder name (lowercase kebab-case).
|
||||
- **Docker Compose Conventions (CRITICAL):**
|
||||
|
||||
- **Version:** MUST be `3.8`
|
||||
@@ -147,16 +170,6 @@ This document provides essential context for AI models interacting with this pro
|
||||
- **Env:** Array of strings: `env = ["KEY=VALUE", "DB_PASSWORD=${db_pass}"]`
|
||||
- **Available helpers:** `${domain}`, `${password:length}`, `${base64:length}`, `${hash:length}`, `${uuid}`, `${randomPort}`, `${email}`, `${username}`, `${timestamp}`, `${timestamps:datetime}`, `${timestampms:datetime}`, `${jwt:secret_var:payload_var}`
|
||||
- **JWT helper example:** `${jwt:mysecret:mypayload}` with payload containing `exp: ${timestamps:2030-01-01T00:00:00Z}`
|
||||
- **Volume bind mounts (in docker-compose.yml):** When mounting host paths, do NOT use absolute paths like `"/folder:/path/in/container"`. Use relative paths instead:
|
||||
```yaml
|
||||
# Invalid
|
||||
volumes:
|
||||
- "/folder:/path/in/container" ❌
|
||||
# Valid
|
||||
volumes:
|
||||
- "../files/my-database:/var/lib/mysql" ✅
|
||||
- "../files/my-configs:/etc/my-app/config" ✅
|
||||
```
|
||||
|
||||
- **meta.json Requirements:**
|
||||
|
||||
|
||||
+6
-11
@@ -20,11 +20,6 @@ This project adheres to the [Contributor Covenant Code of Conduct](https://www.c
|
||||
|
||||
For larger changes or questions, open an issue first to discuss your ideas.
|
||||
|
||||
### Pull Request Guidelines
|
||||
|
||||
- **Keep PRs small and focused.** Avoid very large PRs; prefer several smaller PRs (e.g., one template or one logical change per PR). This speeds up review and keeps the history clear.
|
||||
- **Test before submitting.** Any PR that has not been tested by the contributor will be closed. This keeps the PR queue tidy and ensures that only contributions that have been verified by their authors are considered.
|
||||
|
||||
## Adding a New Template
|
||||
|
||||
To add a new template, follow these steps:
|
||||
@@ -73,16 +68,16 @@ To add a new template, follow these steps:
|
||||
"""
|
||||
```
|
||||
|
||||
4. **Add `meta.json` in your template folder**: Create `blueprints/<id>/meta.json` with a single JSON object describing your template. Do NOT create or edit a root-level `meta.json` — the global registry is generated automatically at build time from every `blueprints/<id>/meta.json` (this is what makes template PRs conflict-free). Include:
|
||||
4. **Update `meta.json`**: Add an entry for your template in the root `meta.json`. Include:
|
||||
|
||||
- `id`: Folder name (e.g., "grafana") — must match the folder exactly.
|
||||
- `id`: Folder name (e.g., "grafana").
|
||||
- `name`: Display name.
|
||||
- `version`: Image/tag version.
|
||||
- `description`: Brief overview.
|
||||
- `logo`: Filename of the logo (e.g., "grafana.svg").
|
||||
- `links`: Object with `github`, `website`, `docs` (URLs; use `""` if not applicable).
|
||||
- `links`: Object with `github`, `website`, `docs` (URLs).
|
||||
- `tags`: Array of categories (e.g., ["monitoring"]).
|
||||
- Example (`blueprints/grafana/meta.json`):
|
||||
- Example:
|
||||
```json
|
||||
{
|
||||
"id": "grafana",
|
||||
@@ -101,7 +96,7 @@ To add a new template, follow these steps:
|
||||
|
||||
5. **Add a Logo**: Place an SVG or PNG logo (e.g., `grafana.svg`) in the template folder. Keep it simple and representative (ideally 512x512 or similar).
|
||||
|
||||
6. **Run Validation**: Before pushing, run `node build-scripts/generate-meta.js --check` to validate every template's metadata (same check CI runs), and manually check for syntax errors in YAML/TOML.
|
||||
6. **Run Validation**: Before pushing, run `node dedupe-and-sort-meta.js` (if available) or manually check for syntax errors in YAML/TOML/JSON.
|
||||
|
||||
7. **Commit and PR**: Push your branch and open a PR. Include:
|
||||
- A description of the template.
|
||||
@@ -195,7 +190,7 @@ If issues arise, debug locally or in the preview. Fix and update your PR.
|
||||
## Updating Existing Templates
|
||||
|
||||
- Follow the same structure as adding new ones.
|
||||
- Bump `version` in the template's `blueprints/<id>/meta.json` and update `docker-compose.yml` image tags.
|
||||
- Bump `version` in `meta.json` and update `docker-compose.yml` image tags.
|
||||
- Test thoroughly, as changes may affect users.
|
||||
|
||||
## Reporting Bugs or Requesting Features
|
||||
|
||||
@@ -8,7 +8,7 @@ This is the official repository for the Dokploy Open Source Templates.
|
||||
1. Fork the repository
|
||||
2. Create a new branch
|
||||
3. Add the template to the `blueprints` folder (`docker-compose.yml`, `template.toml`)
|
||||
4. Add the template metadata (name, description, version, logo, links, tags) in a `meta.json` file inside your template folder (`blueprints/<id>/meta.json`) — do NOT create or edit a root-level `meta.json`, it is generated at build time
|
||||
4. Add the template metadata (name, description, version, logo, links, tags) to the `meta.json` file
|
||||
5. Add the logo to the template folder
|
||||
6. Commit and push your changes
|
||||
7. Create a pull request (PR)
|
||||
@@ -62,7 +62,7 @@ host = "${main_domain}"
|
||||
|
||||
[[config.mounts]]
|
||||
```
|
||||
4. Add the meta information in `blueprints/<id>/meta.json` (a single JSON object; the root `meta.json` is generated automatically at build time)
|
||||
4. Add meta information to the `meta.json` file in the root folder
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -225,4 +225,4 @@ services:
|
||||
6. Now you can click on the Deploy Button and wait for the deployment to finish, and try to access to the service, if everything is correct you should access to the service and see the template working.
|
||||
|
||||
|
||||
Each template's metadata lives in `blueprints/<id>/meta.json`; the global `meta.json` is generated at build time. Use `node build-scripts/generate-meta.js --check` to validate all template metadata.
|
||||
use the command `node dedupe-and-sort-meta.js` to deduplicate and sort the meta.json file.
|
||||
|
||||
+3
-4
@@ -4,11 +4,10 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node ../build-scripts/generate-meta.js && vite",
|
||||
"build": "node ../build-scripts/generate-meta.js && tsc -b && vite build",
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
"generate-meta": "node ../build-scripts/generate-meta.js"
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.19.1",
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
packages:
|
||||
- .
|
||||
allowBuilds:
|
||||
esbuild: false
|
||||
@@ -1 +0,0 @@
|
||||
meta.json
|
||||
@@ -13,9 +13,7 @@ const Navigation = () => {
|
||||
"https://api.github.com/repos/dokploy/dokploy"
|
||||
);
|
||||
const data = await response.json();
|
||||
setGithubStars(
|
||||
Number.isFinite(data.stargazers_count) ? data.stargazers_count : 0
|
||||
);
|
||||
setGithubStars(data.stargazers_count);
|
||||
} catch (error) {
|
||||
console.error("Error fetching GitHub stars:", error);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
|
||||
import { Label } from "./ui/label";
|
||||
import { Clipboard } from "lucide-react";
|
||||
import { Input } from "./ui/input";
|
||||
import Markdown from "./ui/markdown";
|
||||
|
||||
interface Template {
|
||||
id: string;
|
||||
@@ -33,7 +32,6 @@ interface Template {
|
||||
interface TemplateFiles {
|
||||
dockerCompose: string | null;
|
||||
config: string | null;
|
||||
instructions: string | null;
|
||||
}
|
||||
|
||||
interface TemplateDialogProps {
|
||||
@@ -171,23 +169,8 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Tabs
|
||||
defaultValue={
|
||||
templateFiles?.instructions
|
||||
? "instructions"
|
||||
: "docker-compose"
|
||||
}
|
||||
className="w-full"
|
||||
>
|
||||
<Tabs defaultValue="docker-compose" className="w-full">
|
||||
<TabsList className="w-full justify-start">
|
||||
{templateFiles?.instructions && (
|
||||
<TabsTrigger
|
||||
value="instructions"
|
||||
className="data-[state=active]:font-bold"
|
||||
>
|
||||
Instructions
|
||||
</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger
|
||||
value="docker-compose"
|
||||
className="data-[state=active]:font-bold"
|
||||
@@ -201,24 +184,6 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
|
||||
Configuration
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{templateFiles?.instructions && (
|
||||
<TabsContent value="instructions" className="mt-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="flex flex-col items-start w-fit justify-start gap-1">
|
||||
<span className="leading-tight text-xl font-semibold">
|
||||
Setup Instructions
|
||||
</span>
|
||||
<span className="leading-tight text-sm text-gray-500">
|
||||
instructions.md
|
||||
</span>
|
||||
</Label>
|
||||
<div className="rounded-md border p-4">
|
||||
<Markdown content={templateFiles.instructions} />
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
)}
|
||||
<TabsContent value="docker-compose" className="mt-4">
|
||||
{templateFiles?.dockerCompose && (
|
||||
<div className="space-y-2">
|
||||
|
||||
@@ -28,7 +28,6 @@ interface Template {
|
||||
interface TemplateFiles {
|
||||
dockerCompose: string | null;
|
||||
config: string | null;
|
||||
instructions: string | null;
|
||||
}
|
||||
|
||||
interface TemplateGridProps {
|
||||
@@ -81,34 +80,20 @@ const TemplateGrid: React.FC<TemplateGridProps> = ({ view }) => {
|
||||
const fetchTemplateFiles = async (templateId: string) => {
|
||||
setModalLoading(true);
|
||||
try {
|
||||
const [dockerComposeRes, configRes, instructionsRes] = await Promise.all(
|
||||
[
|
||||
fetch(`/blueprints/${templateId}/docker-compose.yml`),
|
||||
fetch(`/blueprints/${templateId}/template.toml`),
|
||||
fetch(`/blueprints/${templateId}/instructions.md`),
|
||||
]
|
||||
);
|
||||
const [dockerComposeRes, configRes] = await Promise.all([
|
||||
fetch(`/blueprints/${templateId}/docker-compose.yml`),
|
||||
fetch(`/blueprints/${templateId}/template.toml`),
|
||||
]);
|
||||
|
||||
const dockerCompose = dockerComposeRes.ok
|
||||
? await dockerComposeRes.text()
|
||||
: null;
|
||||
const config = configRes.ok ? await configRes.text() : null;
|
||||
|
||||
// Guard against SPA fallbacks that return index.html with a 200 status
|
||||
// for templates that don't have an instructions.md file.
|
||||
const instructionsIsMarkdown =
|
||||
instructionsRes.ok &&
|
||||
!(instructionsRes.headers.get("content-type") || "").includes(
|
||||
"text/html"
|
||||
);
|
||||
const instructions = instructionsIsMarkdown
|
||||
? await instructionsRes.text()
|
||||
: null;
|
||||
|
||||
setTemplateFiles({ dockerCompose, config, instructions });
|
||||
setTemplateFiles({ dockerCompose, config });
|
||||
} catch (err) {
|
||||
console.error("Error fetching template files:", err);
|
||||
setTemplateFiles({ dockerCompose: null, config: null, instructions: null });
|
||||
setTemplateFiles({ dockerCompose: null, config: null });
|
||||
} finally {
|
||||
setModalLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
// Minimal markdown renderer (no external dependencies) for the per-template
|
||||
// instructions.md files. Supports headings, paragraphs, fenced code blocks,
|
||||
// ordered/unordered lists, and inline code / bold / links.
|
||||
|
||||
const INLINE_PATTERN =
|
||||
/(`[^`]+`)|(\*\*[^*]+\*\*)|(\[[^\]]+\]\([^)\s]+\))/g;
|
||||
|
||||
const renderInline = (text: string): React.ReactNode[] => {
|
||||
const nodes: React.ReactNode[] = [];
|
||||
let lastIndex = 0;
|
||||
let key = 0;
|
||||
let match: RegExpExecArray | null;
|
||||
INLINE_PATTERN.lastIndex = 0;
|
||||
|
||||
while ((match = INLINE_PATTERN.exec(text)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
nodes.push(text.slice(lastIndex, match.index));
|
||||
}
|
||||
const token = match[0];
|
||||
if (token.startsWith("`")) {
|
||||
nodes.push(
|
||||
<code
|
||||
key={key++}
|
||||
className="px-1.5 py-0.5 rounded bg-muted font-mono text-[0.85em]"
|
||||
>
|
||||
{token.slice(1, -1)}
|
||||
</code>
|
||||
);
|
||||
} else if (token.startsWith("**")) {
|
||||
nodes.push(<strong key={key++}>{token.slice(2, -2)}</strong>);
|
||||
} else {
|
||||
const link = /^\[([^\]]+)\]\(([^)\s]+)\)$/.exec(token);
|
||||
if (link) {
|
||||
nodes.push(
|
||||
<a
|
||||
key={key++}
|
||||
href={link[2]}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 dark:text-blue-400 underline underline-offset-2"
|
||||
>
|
||||
{link[1]}
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
nodes.push(token);
|
||||
}
|
||||
}
|
||||
lastIndex = match.index + token.length;
|
||||
}
|
||||
if (lastIndex < text.length) {
|
||||
nodes.push(text.slice(lastIndex));
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
const HEADING_CLASSES: Record<number, string> = {
|
||||
1: "text-2xl font-bold mt-2",
|
||||
2: "text-xl font-semibold mt-6 border-b pb-1",
|
||||
3: "text-lg font-semibold mt-4",
|
||||
4: "text-base font-semibold mt-3",
|
||||
};
|
||||
|
||||
interface MarkdownProps {
|
||||
content: string;
|
||||
}
|
||||
|
||||
const Markdown: React.FC<MarkdownProps> = ({ content }) => {
|
||||
const lines = content.replace(/\r\n/g, "\n").split("\n");
|
||||
const blocks: React.ReactNode[] = [];
|
||||
let key = 0;
|
||||
let i = 0;
|
||||
|
||||
while (i < lines.length) {
|
||||
const line = lines[i];
|
||||
|
||||
// Blank line
|
||||
if (line.trim() === "") {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fenced code block
|
||||
if (line.trim().startsWith("```")) {
|
||||
const code: string[] = [];
|
||||
i++;
|
||||
while (i < lines.length && !lines[i].trim().startsWith("```")) {
|
||||
code.push(lines[i]);
|
||||
i++;
|
||||
}
|
||||
i++; // skip closing fence
|
||||
blocks.push(
|
||||
<pre
|
||||
key={key++}
|
||||
className="bg-muted rounded-md p-4 overflow-x-auto text-sm font-mono whitespace-pre"
|
||||
>
|
||||
<code>{code.join("\n")}</code>
|
||||
</pre>
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Heading
|
||||
const heading = /^(#{1,4})\s+(.*)$/.exec(line);
|
||||
if (heading) {
|
||||
const level = heading[1].length;
|
||||
const Tag = `h${Math.min(level + 1, 6)}` as keyof React.JSX.IntrinsicElements;
|
||||
blocks.push(
|
||||
<Tag key={key++} className={HEADING_CLASSES[level]}>
|
||||
{renderInline(heading[2])}
|
||||
</Tag>
|
||||
);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unordered list
|
||||
if (/^[-*]\s+/.test(line)) {
|
||||
const items: string[] = [];
|
||||
while (i < lines.length && /^[-*]\s+/.test(lines[i])) {
|
||||
items.push(lines[i].replace(/^[-*]\s+/, ""));
|
||||
i++;
|
||||
}
|
||||
blocks.push(
|
||||
<ul key={key++} className="list-disc pl-6 space-y-1">
|
||||
{items.map((item, idx) => (
|
||||
<li key={idx}>{renderInline(item)}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ordered list
|
||||
if (/^\d+\.\s+/.test(line)) {
|
||||
const items: string[] = [];
|
||||
while (i < lines.length && /^\d+\.\s+/.test(lines[i])) {
|
||||
items.push(lines[i].replace(/^\d+\.\s+/, ""));
|
||||
i++;
|
||||
}
|
||||
blocks.push(
|
||||
<ol key={key++} className="list-decimal pl-6 space-y-1">
|
||||
{items.map((item, idx) => (
|
||||
<li key={idx}>{renderInline(item)}</li>
|
||||
))}
|
||||
</ol>
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Paragraph: consume consecutive plain lines
|
||||
const paragraph: string[] = [line];
|
||||
i++;
|
||||
while (
|
||||
i < lines.length &&
|
||||
lines[i].trim() !== "" &&
|
||||
!/^(#{1,4})\s+/.test(lines[i]) &&
|
||||
!/^[-*]\s+/.test(lines[i]) &&
|
||||
!/^\d+\.\s+/.test(lines[i]) &&
|
||||
!lines[i].trim().startsWith("```")
|
||||
) {
|
||||
paragraph.push(lines[i]);
|
||||
i++;
|
||||
}
|
||||
blocks.push(
|
||||
<p key={key++} className="leading-relaxed">
|
||||
{renderInline(paragraph.join(" "))}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="space-y-3 text-sm">{blocks}</div>;
|
||||
};
|
||||
|
||||
export default Markdown;
|
||||
+4
-2
@@ -11,9 +11,11 @@ export default defineConfig({
|
||||
{
|
||||
src: '../blueprints/*',
|
||||
dest: 'blueprints' // raíz de dist (public root)
|
||||
},
|
||||
{
|
||||
src: '../meta.json',
|
||||
dest: '' // raíz de dist
|
||||
}
|
||||
// meta.json is no longer copied from the repo root: it is
|
||||
// generated into app/public/meta.json by build-scripts/generate-meta.js
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"id": "ackee",
|
||||
"name": "Ackee",
|
||||
"version": "latest",
|
||||
"description": "Ackee is a self-hosted analytics tool for your website.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/electerious/Ackee",
|
||||
"website": "https://ackee.electerious.com/",
|
||||
"docs": "https://docs.ackee.electerious.com/"
|
||||
},
|
||||
"tags": [
|
||||
"analytics",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "activepieces",
|
||||
"name": "Activepieces",
|
||||
"version": "0.35.0",
|
||||
"description": "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.",
|
||||
"logo": "activepieces.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/activepieces/activepieces",
|
||||
"website": "https://www.activepieces.com/",
|
||||
"docs": "https://www.activepieces.com/docs"
|
||||
},
|
||||
"tags": [
|
||||
"automation",
|
||||
"workflow",
|
||||
"no-code"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "actualbudget",
|
||||
"name": "Actual Budget",
|
||||
"version": "latest",
|
||||
"description": "A super fast and privacy-focused app for managing your finances.",
|
||||
"logo": "actualbudget.png",
|
||||
"links": {
|
||||
"github": "https://github.com/actualbudget/actual",
|
||||
"website": "https://actualbudget.org",
|
||||
"docs": "https://actualbudget.org/docs"
|
||||
},
|
||||
"tags": [
|
||||
"budgeting",
|
||||
"finance",
|
||||
"money"
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "adguardhome",
|
||||
"name": "AdGuard Home",
|
||||
"version": "latest",
|
||||
"description": "AdGuard Home is a comprehensive solution designed to enhance your online browsing experience by eliminating all kinds of ads, from annoying banners and pop-ups to intrusive video ads. It provides privacy protection, browsing security, and parental control features while maintaining website functionality.",
|
||||
"logo": "logo.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/AdguardTeam/AdGuardHome",
|
||||
"website": "https://adguard.com",
|
||||
"docs": "https://github.com/AdguardTeam/AdGuardHome/wiki"
|
||||
},
|
||||
"tags": [
|
||||
"privacy",
|
||||
"security",
|
||||
"dns",
|
||||
"ad-blocking"
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "adminer",
|
||||
"name": "Adminer",
|
||||
"version": "4.8.1",
|
||||
"description": "Adminer is a comprehensive database management tool that supports MySQL, MariaDB, PostgreSQL, SQLite, MS SQL, Oracle, Elasticsearch, MongoDB and others. It provides a clean interface for efficient database operations, with strong security features and extensive customization options.",
|
||||
"logo": "logo.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/vrana/adminer",
|
||||
"website": "https://www.adminer.org/",
|
||||
"docs": "https://www.adminer.org/en/plugins/"
|
||||
},
|
||||
"tags": [
|
||||
"databases",
|
||||
"developer-tools",
|
||||
"mysql",
|
||||
"postgresql"
|
||||
]
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"id": "adventurelog",
|
||||
"name": "AdventureLog",
|
||||
"version": "latest",
|
||||
"description": "AdventureLog is an open-source activity tracker with maps, journaling, and Strava integration.",
|
||||
"logo": "adventurelog.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/seanmorley15/adventurelog",
|
||||
"website": "https://adventurelog.app/",
|
||||
"docs": "https://adventurelog.app/docs/intro/adventurelog_overview.html"
|
||||
},
|
||||
"tags": [
|
||||
"activity",
|
||||
"maps",
|
||||
"django",
|
||||
"react",
|
||||
"postgres"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
affinepro:
|
||||
image: ghcr.io/toeverything/affine:stable
|
||||
image: ghcr.io/toeverything/affine-graphql:stable-780dd83
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3010
|
||||
@@ -12,7 +12,6 @@ services:
|
||||
- REDIS_SERVER_HOST=redis
|
||||
- REDIS_SERVER_PASSWORD=${REDIS_PASSWORD}
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/affinepro
|
||||
- AFFINE_INDEXER_ENABLED=false
|
||||
- AFFINE_SERVER_HOST=${DOMAIN}
|
||||
- MAILER_HOST=${MAILER_HOST}
|
||||
- MAILER_PORT=${MAILER_PORT}
|
||||
@@ -20,21 +19,16 @@ services:
|
||||
- MAILER_PASSWORD=${MAILER_PASSWORD}
|
||||
- MAILER_SENDER=${MAILER_SENDER}
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
migration:
|
||||
condition: service_completed_successfully
|
||||
- db
|
||||
- redis
|
||||
|
||||
migration:
|
||||
image: ghcr.io/toeverything/affine:stable
|
||||
command: ["sh", "-c", "node ./scripts/self-host-predeploy.js"]
|
||||
image: ghcr.io/toeverything/affine-graphql:stable-780dd83
|
||||
command: node ./scripts/self-host-predeploy.js
|
||||
environment:
|
||||
- REDIS_SERVER_HOST=redis
|
||||
- REDIS_SERVER_PASSWORD=${REDIS_PASSWORD}
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/affinepro
|
||||
- AFFINE_INDEXER_ENABLED=false
|
||||
- AFFINE_SERVER_HOST=${DOMAIN}
|
||||
- MAILER_HOST=${MAILER_HOST}
|
||||
- MAILER_PORT=${MAILER_PORT}
|
||||
@@ -45,25 +39,17 @@ services:
|
||||
- affine-storage:/root/.affine/storage
|
||||
- affine-config:/root/.affine/config
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
- db
|
||||
- redis
|
||||
|
||||
db:
|
||||
image: pgvector/pgvector:pg16
|
||||
image: postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=affinepro
|
||||
- POSTGRES_INITDB_ARGS=--data-checksums
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres", "-d", "affinepro"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
@@ -71,14 +57,9 @@ services:
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "redis-cli -a '${REDIS_PASSWORD}' ping | grep PONG"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
affine-storage: {}
|
||||
affine-config: {}
|
||||
postgres-data: {}
|
||||
redis-data: {}
|
||||
redis-data: {}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "affinepro",
|
||||
"name": "Affine Pro",
|
||||
"version": "stable",
|
||||
"description": "Affine Pro is a modern, self-hosted platform designed for collaborative content creation and project management. It offers an intuitive interface, seamless real-time collaboration, and powerful tools for organizing tasks, notes, and ideas.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/toeverything/Affine",
|
||||
"website": "https://affine.pro/",
|
||||
"docs": "https://docs.affine.pro/"
|
||||
},
|
||||
"tags": [
|
||||
"collaboration",
|
||||
"self-hosted",
|
||||
"productivity",
|
||||
"project-management"
|
||||
]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="a" viewBox="0 0 3271 1440"><defs><style>.st0{fill:#dedede}</style></defs><path d="M1009.95 1209.53c-125.1-214.98-250.74-430.9-378.92-651.16-126.82 219.73-251.81 436.28-376.62 652.51H60.69c190.42-329.11 570.87-981.75 570.87-981.75s382.11 651.12 572.49 980.41h-194.1Z" class="st0"/><path d="M875.46 1210.46H383.92c32.72-56.91 64.4-111.99 95.81-166.62H781.8c30.62 54.46 61.09 108.66 93.67 166.62zm1622.92-202.23c19.52 29.84 37.9 57.94 58.46 89.39h-63.61c-18.29-24.88-38.17-51.91-57.71-78.49h-72.24v78.17h-51.11V828.44c30.03 0 58.71-.22 87.38.06 23.48.24 47.32-1.26 70.37 2.21 37.67 5.66 66.94 25.71 74.9 64.9 8.22 40.54 1.62 78.35-35.52 104.62-3.14 2.22-6.21 4.54-10.93 7.99h.01Zm-135.01-40.37c34 0 66.71 1.87 99.08-.66 21.47-1.67 30.97-17.92 31.56-39.77.61-22.88-9.01-40.56-30.99-42.73-32.8-3.23-66.15-.84-99.65-.84zM1674.39 829.9v38.37c-52.36 58.82-104.25 117.13-160.14 179.91h158.93v49.49h-227.62v-51.9c46.98-52.11 95.02-105.41 147.93-164.09h-147.28V829.9zm906.29-489.95c64.48 63.91 125.11 123.99 189.35 187.65V373.68h47.4v271.19c-64.19-62.94-125.05-122.59-190.04-186.3v162.44h-46.72V339.94h.01Zm195.02 692.31c13.79-6.85 33.51-15.76 45.41-21.65 41.44 55.26 105.07 42.53 132.33 18s35.44-59.98 25.2-95.47c-13.36-34.33-42.97-61.14-86.36-59.62-43.38 1.52-81.55 32.41-85.2 84.34h-50.32c-.57-50.11 36.27-109.35 94.12-126.75 72.77-21.89 144.89 13.23 172.07 83.77 25.18 65.37-5.74 138.6-72.21 171-60.69 29.59-140.17 6.97-175.1-53.61h.04Zm-811.82-622.07c-11.07 8.4-24.55 17.68-34.13 24.95-55.99-46.33-119.62-30.88-142.33 27.28-13.63 38.17 6.27 88.2 48.2 103.62 36.45 13.4 76.82-.61 87.32-31.48-8.66-2.61-17.29-5.23-28.25-8.54-.93-12.84-1.85-25.67-2.96-40.9h96.82c7.25 56.02-37.39 118.12-94.41 132.17-63.06 15.54-129.35-19.2-152.31-79.83-22.64-59.76 4.81-131.25 61.88-161.16 55.93-29.3 126.75-15.37 160.17 33.88Zm-531.9-55.25c48.2 92.28 93.04 178.13 139.88 267.84h-53.47c-27.89-50.55-56.47-102.34-86.73-157.13-28.93 54.29-56.1 105.29-83.82 157.31h-54.54c45.87-88.68 90.79-175.5 138.65-268.02h.01Zm1642.09 60.65h-89.98v-46.22h226.22v46.24h-86.43v205.91h-49.82V415.59zM1947.89 878.34v65.77l63.09.91s-.03 30.03-.03 50.34c-25.69.89-53.1.45-78.35-5.53-21.38-5.05-36.73-22.63-37.67-46.05-1.5-36.98-.4-75.88-.4-115.24h216.51v49.81h-163.16Zm-52.88 218.57v-68.62c14.11-.65 27.22-1.25 40.97-1.87 3.47 7 3.06 19.81 9.96 20.09 6.88.29 107.67 0 164.04 0v50.41h-214.97Zm342.4-678.53v60.83l58.36.84s-.03 27.78-.03 46.57c-23.85.41-49.12.41-72.49-5.11-19.77-4.68-33.97-20.93-34.85-42.59-1.39-34.2-.37-70.19-.37-106.6h200.29v46.07H2237.4h.01Zm-48.91 202.2v-63.49c13.05-.6 25.18-1.15 37.9-1.73 3.2 6.48 2.83 18.32 9.2 18.59s99.6 0 151.75 0v46.63h-198.87.01Z" class="st0"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1,14 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
agent-zero:
|
||||
image: agent0ai/agent-zero:v1.9
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "80"
|
||||
environment:
|
||||
- AUTH_LOGIN=${AUTH_LOGIN}
|
||||
- AUTH_PASSWORD=${AUTH_PASSWORD}
|
||||
volumes:
|
||||
- agent-zero-data:/a0/usr
|
||||
volumes:
|
||||
agent-zero-data:
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "agent-zero",
|
||||
"name": "Agent Zero",
|
||||
"version": "v1.9",
|
||||
"description": "Agent Zero is an open-source autonomous AI agent framework with memory and tool use capabilities.",
|
||||
"logo": "agent-zero.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/agent0ai/agent-zero",
|
||||
"website": "https://www.agent-zero.ai",
|
||||
"docs": "https://www.agent-zero.ai/p/docs/"
|
||||
},
|
||||
"tags": [
|
||||
"ai",
|
||||
"agent",
|
||||
"automation"
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
auth_login = "${username}"
|
||||
auth_password = "${password:32}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"AUTH_LOGIN=${auth_login}",
|
||||
"AUTH_PASSWORD=${auth_password}",
|
||||
]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "agent-zero"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"id": "agentdvr",
|
||||
"name": "Agent DVR",
|
||||
"version": "latest",
|
||||
"description": "Agent DVR is a comprehensive video surveillance software with motion detection, alerts, and remote access capabilities.",
|
||||
"logo": "agentdvr.png",
|
||||
"links": {
|
||||
"github": "https://github.com/ispysoftware/AgentDVR",
|
||||
"website": "https://www.ispyconnect.com/",
|
||||
"docs": "https://www.ispyconnect.com/userguide-agent-dvr.aspx"
|
||||
},
|
||||
"tags": [
|
||||
"surveillance",
|
||||
"security",
|
||||
"video",
|
||||
"monitoring",
|
||||
"dvr",
|
||||
"camera"
|
||||
]
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"id": "akaunting",
|
||||
"name": "Akaunting",
|
||||
"version": "latest",
|
||||
"description": "Akaunting is a self-hosted, open-source accounting app for small businesses.",
|
||||
"logo": "image.png",
|
||||
"links": {
|
||||
"github": "https://github.com/akaunting/akaunting",
|
||||
"website": "https://akaunting.com",
|
||||
"docs": "https://akaunting.com/docs"
|
||||
},
|
||||
"tags": [
|
||||
"finance",
|
||||
"accounting",
|
||||
"php",
|
||||
"mariadb",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB |
@@ -1,30 +0,0 @@
|
||||
services:
|
||||
alarik:
|
||||
image: ghcr.io/achtungsoftware/alarik:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- alarik-storage:/app/Storage
|
||||
environment:
|
||||
- API_BASE_URL=${API_BASE_URL}
|
||||
- CONSOLE_BASE_URL=${CONSOLE_BASE_URL}
|
||||
- ADMIN_USERNAME=${ADMIN_USERNAME}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
||||
- JWT=${JWT}
|
||||
- ALLOW_ACCOUNT_CREATION=${ALLOW_ACCOUNT_CREATION}
|
||||
expose:
|
||||
- 8080
|
||||
|
||||
console:
|
||||
image: ghcr.io/achtungsoftware/alarik-console:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NUXT_PUBLIC_API_BASE_URL=${API_BASE_URL}
|
||||
- NUXT_PUBLIC_CONSOLE_BASE_URL=${CONSOLE_BASE_URL}
|
||||
- NUXT_PUBLIC_ALLOW_ACCOUNT_CREATION=${ALLOW_ACCOUNT_CREATION}
|
||||
expose:
|
||||
- 3000
|
||||
depends_on:
|
||||
- alarik
|
||||
|
||||
volumes:
|
||||
alarik-storage:
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "alarik",
|
||||
"name": "Alarik",
|
||||
"description": "Alarik is a high-performance, S3-compatible object storage.",
|
||||
"logo": "alarik.png",
|
||||
"version": "latest",
|
||||
"links": {
|
||||
"github": "https://github.com/achtungsoftware/alarik",
|
||||
"website": "https://alarik.io/",
|
||||
"docs": "https://alarik.io/docs"
|
||||
},
|
||||
"tags": [
|
||||
"storage",
|
||||
"s3",
|
||||
"object-storage"
|
||||
]
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
[variables]
|
||||
console_domain = "${domain}"
|
||||
api_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
mounts = []
|
||||
|
||||
# S3-compatible API - point AWS CLI / SDKs / rclone / backup tools at this domain
|
||||
[[config.domains]]
|
||||
serviceName = "alarik"
|
||||
port = 8_080
|
||||
host = "${api_domain}"
|
||||
|
||||
# Web management console - log in with the generated admin credentials below
|
||||
[[config.domains]]
|
||||
serviceName = "console"
|
||||
port = 3_000
|
||||
host = "${console_domain}"
|
||||
|
||||
[config.env]
|
||||
API_BASE_URL = "http://${api_domain}"
|
||||
CONSOLE_BASE_URL = "http://${console_domain}"
|
||||
ADMIN_USERNAME = "alarik"
|
||||
ADMIN_PASSWORD = "${password:16}"
|
||||
JWT = "${base64:64}"
|
||||
ALLOW_ACCOUNT_CREATION = "false"
|
||||
@@ -1,7 +1,7 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
alist:
|
||||
image: xhofe/alist:v3.55.0
|
||||
image: xhofe/alist:v3.41.0
|
||||
volumes:
|
||||
- alist-data:/opt/alist/data
|
||||
environment:
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "alist",
|
||||
"name": "AList",
|
||||
"version": "v3.55.0",
|
||||
"description": "🗂ï¸A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.",
|
||||
"logo": "alist.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/AlistGo/alist",
|
||||
"website": "https://alistgo.com/",
|
||||
"docs": "https://alistgo.com/guide/install/docker.html"
|
||||
},
|
||||
"tags": [
|
||||
"file",
|
||||
"webdav",
|
||||
"storage"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "alltube",
|
||||
"name": "AllTube",
|
||||
"version": "latest",
|
||||
"description": "AllTube Download is an application designed to facilitate the downloading of videos from YouTube and other video sites. It provides an HTML GUI for youtube-dl with video conversion capabilities and JSON API support.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/Rudloff/alltube",
|
||||
"website": "https://github.com/Rudloff/alltube",
|
||||
"docs": "https://github.com/Rudloff/alltube/wiki"
|
||||
},
|
||||
"tags": [
|
||||
"media",
|
||||
"video",
|
||||
"downloader"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "ampache",
|
||||
"name": "Ampache",
|
||||
"version": "latest",
|
||||
"description": "Ampache is a web-based audio/video streaming application and file manager allowing you to access your music & videos from anywhere, using almost any internet enabled device.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/ampache/ampache",
|
||||
"website": "http://ampache.org/",
|
||||
"docs": "https://github.com/ampache/ampache/wiki"
|
||||
},
|
||||
"tags": [
|
||||
"media",
|
||||
"music",
|
||||
"streaming"
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"id": "anonupload",
|
||||
"name": "AnonUpload",
|
||||
"version": "1",
|
||||
"description": "AnonUpload is a secure, anonymous file sharing application that does not require a database. It is built with privacy as a priority, ensuring that the direct filename used is not displayed.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/supernova3339/anonupload",
|
||||
"docs": "https://github.com/Supernova3339/anonupload/blob/main/env.md",
|
||||
"website": "https://anonupload.com/"
|
||||
},
|
||||
"tags": [
|
||||
"file-sharing",
|
||||
"privacy"
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "anse",
|
||||
"name": "Anse",
|
||||
"version": "latest",
|
||||
"description": "Anse is an open-source alternative to ChatGPT web UI, supporting OpenAI-compatible APIs.",
|
||||
"logo": "image.png",
|
||||
"links": {
|
||||
"github": "https://github.com/ddiu8081/anse",
|
||||
"website": "https://anse.app/",
|
||||
"docs": "https://github.com/ddiu8081/anse#readme"
|
||||
},
|
||||
"tags": [
|
||||
"ai",
|
||||
"chatbot",
|
||||
"openai",
|
||||
"ui"
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"id": "answer",
|
||||
"name": "Answer",
|
||||
"version": "v1.4.1",
|
||||
"description": "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.",
|
||||
"logo": "answer.png",
|
||||
"links": {
|
||||
"github": "https://github.com/apache/answer",
|
||||
"website": "https://answer.apache.org/",
|
||||
"docs": "https://answer.apache.org/docs"
|
||||
},
|
||||
"tags": [
|
||||
"q&a",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"id": "anubis",
|
||||
"name": "Anubis",
|
||||
"version": "latest",
|
||||
"description": "Anubis is a bot protector, It will block bots from accessing your website.",
|
||||
"logo": "anubis.webp",
|
||||
"links": {
|
||||
"github": "https://github.com/TecharoHQ/anubis",
|
||||
"website": "https://anubis.techaro.lol",
|
||||
"docs": "https://anubis.techaro.lol/docs/"
|
||||
},
|
||||
"tags": [
|
||||
"self-hosted",
|
||||
"bot-protection"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "anythingllm",
|
||||
"name": "AnythingLLM",
|
||||
"version": "latest",
|
||||
"description": "AnythingLLM is a private, self-hosted, and local document chatbot platform that allows you to chat with your documents using various LLM providers.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/Mintplex-Labs/anything-llm",
|
||||
"website": "https://anythingllm.com/",
|
||||
"docs": "https://docs.anythingllm.com/"
|
||||
},
|
||||
"tags": [
|
||||
"ai",
|
||||
"llm",
|
||||
"chatbot"
|
||||
]
|
||||
}
|
||||
@@ -13,7 +13,7 @@ services:
|
||||
- "33010:33010"
|
||||
- "33020:33020/udp"
|
||||
volumes:
|
||||
- ../files/anytype-data:/data
|
||||
- ./data:/data
|
||||
environment:
|
||||
# Advertise addresses clients should use. Replace with your server hostname/IP.
|
||||
ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS: "192.168.100.9"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "anytype",
|
||||
"name": "Anytype",
|
||||
"version": "latest",
|
||||
"description": "Anytype is a personal knowledge base—your digital brain—that lets you gather, connect and remix all kinds of information. Create pages, tasks, wikis, journals—even entire apps—and define your own data model while your data stays offline-first, private and encrypted across devices.\n\nAfter installation, you can view the Anytype client configuration by running `cat /data/client-config.yml` inside the service container.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/grishy/any-sync-bundle",
|
||||
"docs": "https://doc.anytype.io/anytype-docs",
|
||||
"website": "https://anytype.io/"
|
||||
},
|
||||
"tags": [
|
||||
"note-taking",
|
||||
"local-first",
|
||||
"peer-to-peer"
|
||||
]
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"id": "appflowy",
|
||||
"name": "App Flowy",
|
||||
"version": "0.9.3",
|
||||
"description": "AppFlowy is an open-source alternative to Notion. You are in charge of your data and customizations.",
|
||||
"links": {
|
||||
"github": "https://github.com/AppFlowy-IO/AppFlowy",
|
||||
"website": "https://appflowy.io/",
|
||||
"docs": "https://docs.appflowy.io/docs"
|
||||
},
|
||||
"logo": "appflowy.png",
|
||||
"tags": [
|
||||
"productivity",
|
||||
"self-hosted",
|
||||
"notes",
|
||||
"knowledge-base",
|
||||
"notion-alternative"
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"id": "apprise-api",
|
||||
"name": "Apprise API",
|
||||
"version": "latest",
|
||||
"description": "Apprise API provides a simple interface for sending notifications to almost all of the most popular notification services available to us today.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/caronc/apprise-api",
|
||||
"website": "https://github.com/caronc/apprise-api",
|
||||
"docs": "https://github.com/caronc/apprise-api/wiki"
|
||||
},
|
||||
"tags": [
|
||||
"notifications",
|
||||
"api"
|
||||
]
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
appsmith:
|
||||
image: appsmith/appsmith-ee:v1.94
|
||||
image: index.docker.io/appsmith/appsmith-ee:v1.29
|
||||
volumes:
|
||||
- appsmith-data:/appsmith-stacks
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
appsmith-data:
|
||||
- ../files/stacks:/appsmith-stacks
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"id": "appsmith",
|
||||
"name": "Appsmith",
|
||||
"version": "v1.94",
|
||||
"description": "Appsmith is a free and open source platform for building internal tools and applications.",
|
||||
"logo": "appsmith.png",
|
||||
"links": {
|
||||
"github": "https://github.com/appsmithorg/appsmith",
|
||||
"website": "https://appsmith.com/",
|
||||
"docs": "https://docs.appsmith.com/"
|
||||
},
|
||||
"tags": [
|
||||
"cms"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"id": "appwrite",
|
||||
"name": "Appwrite",
|
||||
"version": "1.9.5",
|
||||
"description": "Appwrite is an end-to-end platform for building Web, Mobile, Native, or Backend apps, packaged as a set of Docker microservices. It includes both a backend server and a fully integrated hosting solution for deploying static and server-side rendered frontends. Appwrite abstracts the complexity and repetitiveness required to build modern apps from scratch and allows you to build secure, full-stack applications faster.\nUsing Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.",
|
||||
"links": {
|
||||
"github": "https://github.com/appwrite/appwrite",
|
||||
"website": "https://appwrite.io/",
|
||||
"docs": "https://appwrite.io/docs"
|
||||
},
|
||||
"logo": "appwrite.svg",
|
||||
"tags": [
|
||||
"database",
|
||||
"firebase",
|
||||
"mariadb",
|
||||
"mongodb",
|
||||
"hosting",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
@@ -1,78 +1,51 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
functions_domain = "functions.${main_domain}"
|
||||
sites_domain = "sites.${main_domain}"
|
||||
openssl_key = "${password:32}"
|
||||
db_root_pw = "${password:32}"
|
||||
db_user_pw = "${password:32}"
|
||||
executor_secret = "${password:32}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"_APP_ENV=production",
|
||||
"_APP_EDITION=self-hosted",
|
||||
"_APP_LOCALE=en",
|
||||
"_APP_POOL_ADAPTER=stack",
|
||||
"_APP_WORKER_PER_CORE=6",
|
||||
"_APP_OPTIONS_ABUSE=enabled",
|
||||
"_APP_OPTIONS_FORCE_HTTPS=disabled",
|
||||
"_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled",
|
||||
"_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled",
|
||||
"_APP_OPTIONS_ROUTER_PROTECTION=disabled",
|
||||
"_APP_OPENSSL_KEY_V1=${openssl_key}",
|
||||
"_APP_OPENSSL_KEY_V1=your-secret-key",
|
||||
"_APP_DOMAIN=${main_domain}",
|
||||
"_APP_CONSOLE_DOMAIN=${main_domain}",
|
||||
"_APP_CONSOLE_TRUSTED_PROJECTS=",
|
||||
"_APP_CUSTOM_DOMAIN_DENY_LIST=example.com,test.com,app.example.com",
|
||||
"_APP_DOMAIN_FUNCTIONS=${functions_domain}",
|
||||
"_APP_DOMAIN_SITES=${sites_domain}",
|
||||
"_APP_DOMAIN_TARGET_CNAME=localhost",
|
||||
"_APP_DOMAIN_TARGET_AAAA=::1",
|
||||
"_APP_DOMAIN_TARGET_A=127.0.0.1",
|
||||
"_APP_DOMAIN_TARGET_CAA=",
|
||||
"_APP_DNS=8.8.8.8",
|
||||
"_APP_TRUSTED_HEADERS=x-forwarded-for",
|
||||
"_APP_DOMAIN_FUNCTIONS=${main_domain}",
|
||||
"_APP_DOMAIN_TARGET=${main_domain}",
|
||||
"_APP_CONSOLE_WHITELIST_ROOT=enabled",
|
||||
"_APP_CONSOLE_WHITELIST_EMAILS=",
|
||||
"_APP_CONSOLE_WHITELIST_IPS=",
|
||||
"_APP_CONSOLE_HOSTNAMES=",
|
||||
"_APP_CONSOLE_SCHEMA=appwriteio",
|
||||
"_APP_CONSOLE_SESSION_ALERTS=disabled",
|
||||
"_APP_CONSOLE_COUNTRIES_DENYLIST=",
|
||||
"_APP_SYSTEM_EMAIL_NAME=Appwrite",
|
||||
"_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io",
|
||||
"_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io",
|
||||
"_APP_SYSTEM_RESPONSE_FORMAT=",
|
||||
"_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=certs@appwrite.io",
|
||||
"_APP_EMAIL_SECURITY=",
|
||||
"_APP_EMAIL_CERTIFICATES=",
|
||||
"_APP_COMPRESSION_ENABLED=enabled",
|
||||
"_APP_COMPRESSION_MIN_SIZE_BYTES=1024",
|
||||
"_APP_USAGE_STATS=enabled",
|
||||
"_APP_USAGE_AGGREGATION_INTERVAL=30",
|
||||
"_APP_STATS_RESOURCES_INTERVAL=30",
|
||||
"_APP_LOGGING_PROVIDER=",
|
||||
"_APP_LOGGING_CONFIG=",
|
||||
"_APP_LOGGING_CONFIG_REALTIME=",
|
||||
"_APP_EXPERIMENT_LOGGING_PROVIDER=",
|
||||
"_APP_EXPERIMENT_LOGGING_CONFIG=",
|
||||
"_APP_USAGE_AGGREGATION_INTERVAL=30",
|
||||
"_APP_USAGE_TIMESERIES_INTERVAL=30",
|
||||
"_APP_USAGE_DATABASE_INTERVAL=900",
|
||||
"_APP_WORKER_PER_CORE=6",
|
||||
"_APP_CONSOLE_SESSION_ALERTS=disabled",
|
||||
"_APP_REDIS_HOST=redis",
|
||||
"_APP_REDIS_PORT=6379",
|
||||
"_APP_REDIS_USER=",
|
||||
"_APP_REDIS_PASS=",
|
||||
"_APP_DB_ADAPTER=mongodb",
|
||||
"_APP_DB_HOST=mongodb",
|
||||
"_APP_DB_PORT=27017",
|
||||
"_APP_DB_HOST=mariadb",
|
||||
"_APP_DB_PORT=3306",
|
||||
"_APP_DB_SCHEMA=appwrite",
|
||||
"_APP_DB_USER=user",
|
||||
"_APP_DB_PASS=${db_user_pw}",
|
||||
"_APP_DB_ROOT_PASS=${db_root_pw}",
|
||||
"_APP_DATABASE_SHARED_TABLES=",
|
||||
"_APP_DATABASE_SHARED_NAMESPACE=",
|
||||
"_APP_DB_ADAPTER_VECTORSDB=",
|
||||
"_APP_DB_HOST_VECTORSDB=",
|
||||
"_APP_DB_PORT_VECTORSDB=",
|
||||
"_APP_DB_SCHEMA_VECTORSDB=",
|
||||
"_APP_DB_USER_VECTORSDB=",
|
||||
"_APP_DB_PASS_VECTORSDB=",
|
||||
"_APP_DB_PASS=password",
|
||||
"_APP_DB_ROOT_PASS=rootsecretpassword",
|
||||
"_APP_INFLUXDB_HOST=influxdb",
|
||||
"_APP_INFLUXDB_PORT=8086",
|
||||
"_APP_STATSD_HOST=telegraf",
|
||||
"_APP_STATSD_PORT=8125",
|
||||
"_APP_SMTP_HOST=",
|
||||
"_APP_SMTP_PORT=",
|
||||
"_APP_SMTP_SECURE=",
|
||||
@@ -80,7 +53,6 @@ env = [
|
||||
"_APP_SMTP_PASSWORD=",
|
||||
"_APP_SMS_PROVIDER=",
|
||||
"_APP_SMS_FROM=",
|
||||
"_APP_SMS_PROJECTS_DENY_LIST=",
|
||||
"_APP_STORAGE_LIMIT=30000000",
|
||||
"_APP_STORAGE_PREVIEW_LIMIT=20000000",
|
||||
"_APP_STORAGE_ANTIVIRUS=disabled",
|
||||
@@ -91,7 +63,6 @@ env = [
|
||||
"_APP_STORAGE_S3_SECRET=",
|
||||
"_APP_STORAGE_S3_REGION=us-east-1",
|
||||
"_APP_STORAGE_S3_BUCKET=",
|
||||
"_APP_STORAGE_S3_ENDPOINT=",
|
||||
"_APP_STORAGE_DO_SPACES_ACCESS_KEY=",
|
||||
"_APP_STORAGE_DO_SPACES_SECRET=",
|
||||
"_APP_STORAGE_DO_SPACES_REGION=us-east-1",
|
||||
@@ -108,96 +79,50 @@ env = [
|
||||
"_APP_STORAGE_WASABI_SECRET=",
|
||||
"_APP_STORAGE_WASABI_REGION=eu-central-1",
|
||||
"_APP_STORAGE_WASABI_BUCKET=",
|
||||
"_APP_COMPUTE_SIZE_LIMIT=30000000",
|
||||
"_APP_COMPUTE_BUILD_TIMEOUT=900",
|
||||
"_APP_COMPUTE_CPUS=0",
|
||||
"_APP_COMPUTE_MEMORY=0",
|
||||
"_APP_COMPUTE_INACTIVE_THRESHOLD=60",
|
||||
"_APP_COMPUTE_MAINTENANCE_INTERVAL=3600",
|
||||
"_APP_COMPUTE_RUNTIMES_NETWORK=dokploy-network",
|
||||
"_APP_FUNCTIONS_SIZE_LIMIT=30000000",
|
||||
"_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000",
|
||||
"_APP_FUNCTIONS_TIMEOUT=900",
|
||||
"_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000",
|
||||
"_APP_FUNCTIONS_RUNTIMES=node-22,python-3.12,php-8.3,ruby-3.3,dart-3.5,go-1.23,rust-1.83,bun-1.1,deno-1.46,java-21.0,dotnet-8.0",
|
||||
"_APP_SITES_TIMEOUT=900",
|
||||
"_APP_SITES_RUNTIMES=static-1,node-22",
|
||||
"_APP_EXECUTOR_SECRET=${executor_secret}",
|
||||
"_APP_EXECUTOR_HOST=http://openruntimes-executor/v1",
|
||||
"_APP_EXECUTOR_IMAGES=openruntimes/node:v5-22,openruntimes/python:v5-3.12,openruntimes/php:v5-8.3,openruntimes/static:v5-1",
|
||||
"_APP_OPEN_RUNTIMES_NFT=enabled",
|
||||
"_APP_BROWSER_HOST=http://appwrite-browser:3000/v1",
|
||||
"_APP_WORKER_SCREENSHOTS_ROUTER=http://appwrite",
|
||||
"_APP_FUNCTIONS_BUILD_TIMEOUT=900",
|
||||
"_APP_FUNCTIONS_CONTAINERS=10",
|
||||
"_APP_FUNCTIONS_CPUS=0",
|
||||
"_APP_FUNCTIONS_MEMORY=0",
|
||||
"_APP_FUNCTIONS_MEMORY_SWAP=0",
|
||||
"_APP_FUNCTIONS_RUNTIMES=node-16.0,php-8.0,python-3.9,ruby-3.0",
|
||||
"_APP_EXECUTOR_SECRET=your-secret-key",
|
||||
"_APP_EXECUTOR_HOST=http://exc1/v1",
|
||||
"_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes",
|
||||
"_APP_FUNCTIONS_ENVS=node-16.0,php-7.4,python-3.9,ruby-3.0",
|
||||
"_APP_FUNCTIONS_INACTIVE_THRESHOLD=60",
|
||||
"DOCKERHUB_PULL_USERNAME=",
|
||||
"DOCKERHUB_PULL_PASSWORD=",
|
||||
"DOCKERHUB_PULL_EMAIL=",
|
||||
"OPEN_RUNTIMES_NETWORK=appwrite_runtimes",
|
||||
"_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes",
|
||||
"_APP_DOCKER_HUB_USERNAME=",
|
||||
"_APP_DOCKER_HUB_PASSWORD=",
|
||||
"_APP_MAINTENANCE_INTERVAL=86400",
|
||||
"_APP_MAINTENANCE_START_TIME=00:00",
|
||||
"_APP_MAINTENANCE_RETENTION_CACHE=2592000",
|
||||
"_APP_MAINTENANCE_RETENTION_EXECUTION=1209600",
|
||||
"_APP_MAINTENANCE_RETENTION_AUDIT=1209600",
|
||||
"_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=15778800",
|
||||
"_APP_MAINTENANCE_RETENTION_ABUSE=86400",
|
||||
"_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000",
|
||||
"_APP_MAINTENANCE_RETENTION_SCHEDULES=86400",
|
||||
"_APP_INTERVAL_DOMAIN_VERIFICATION=120",
|
||||
"_APP_INTERVAL_CLEANUP_STALE_EXECUTIONS=300",
|
||||
"_APP_GRAPHQL_INTROSPECTION=enabled",
|
||||
"_APP_GRAPHQL_MAX_BATCH_SIZE=10",
|
||||
"_APP_GRAPHQL_MAX_COMPLEXITY=250",
|
||||
"_APP_GRAPHQL_MAX_DEPTH=4",
|
||||
"_APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10",
|
||||
"_APP_FUNCTIONS_MAINTENANCE_INTERVAL=3600",
|
||||
"_APP_VCS_GITHUB_APP_NAME=",
|
||||
"_APP_VCS_GITHUB_PRIVATE_KEY=",
|
||||
"_APP_VCS_GITHUB_APP_ID=",
|
||||
"_APP_VCS_GITHUB_CLIENT_ID=",
|
||||
"_APP_VCS_GITHUB_CLIENT_SECRET=",
|
||||
"_APP_VCS_GITHUB_WEBHOOK_SECRET=",
|
||||
"_APP_MIGRATION_HOST=appwrite",
|
||||
"_APP_MAINTENANCE_INTERVAL=86400",
|
||||
"_APP_MAINTENANCE_DELAY=0",
|
||||
"_APP_MAINTENANCE_RETENTION_CACHE=2592000",
|
||||
"_APP_MAINTENANCE_RETENTION_EXECUTION=1209600",
|
||||
"_APP_MAINTENANCE_RETENTION_AUDIT=1209600",
|
||||
"_APP_MAINTENANCE_RETENTION_ABUSE=86400",
|
||||
"_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000",
|
||||
"_APP_MAINTENANCE_RETENTION_SCHEDULES=86400",
|
||||
"_APP_GRAPHQL_MAX_BATCH_SIZE=10",
|
||||
"_APP_GRAPHQL_MAX_COMPLEXITY=250",
|
||||
"_APP_GRAPHQL_MAX_DEPTH=3",
|
||||
"_APP_MIGRATIONS_FIREBASE_CLIENT_ID=",
|
||||
"_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=",
|
||||
"_APP_ASSISTANT_OPENAI_API_KEY=",
|
||||
]
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "mongo-init.js"
|
||||
content = """// mongo-init.js
|
||||
|
||||
// Switch to the admin database
|
||||
const adminDb = db.getSiblingDB('admin');
|
||||
|
||||
// Get username and password from environment variables
|
||||
const username = process.env.MONGO_INITDB_USERNAME;
|
||||
const password = process.env.MONGO_INITDB_PASSWORD;
|
||||
const database = process.env.MONGO_INITDB_DATABASE;
|
||||
|
||||
// Create the user
|
||||
adminDb.createUser({
|
||||
user: username,
|
||||
pwd: password,
|
||||
roles: [
|
||||
{ role: 'readWrite', db: database }
|
||||
]
|
||||
});
|
||||
"""
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "mongo-entrypoint.sh"
|
||||
content = """#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Fix keyfile permissions if mounted from volume
|
||||
KEYFILE_PATH="/data/keyfile/mongo-keyfile"
|
||||
|
||||
if [ ! -f "$KEYFILE_PATH" ]; then
|
||||
echo "Generating random MongoDB keyfile..."
|
||||
mkdir -p /data/keyfile
|
||||
openssl rand -base64 756 > "$KEYFILE_PATH"
|
||||
fi
|
||||
|
||||
chmod 400 "$KEYFILE_PATH"
|
||||
chown mongodb:mongodb "$KEYFILE_PATH" 2>/dev/null || chown 999:999 "$KEYFILE_PATH"
|
||||
|
||||
# Use MongoDB's standard entrypoint with our command
|
||||
exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --auth --keyFile "$KEYFILE_PATH"
|
||||
"""
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite"
|
||||
@@ -205,18 +130,6 @@ port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite"
|
||||
port = 80
|
||||
host = "${sites_domain}"
|
||||
path = "/"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite"
|
||||
port = 80
|
||||
host = "${functions_domain}"
|
||||
path = "/"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite-console"
|
||||
port = 80
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"id": "aptabase",
|
||||
"name": "Aptabase",
|
||||
"version": "v1.0.0",
|
||||
"description": "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.",
|
||||
"logo": "aptabase.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/aptabase/aptabase",
|
||||
"website": "https://aptabase.com/",
|
||||
"docs": "https://github.com/aptabase/aptabase/blob/main/README.md"
|
||||
},
|
||||
"tags": [
|
||||
"analytics",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "arangodb",
|
||||
"name": "ArangoDB",
|
||||
"version": "latest",
|
||||
"description": "ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values. Build high performance applications using a convenient SQL-like query language or JavaScript extensions.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/arangodb/arangodb",
|
||||
"website": "https://www.arangodb.com/",
|
||||
"docs": "https://www.arangodb.com/docs/"
|
||||
},
|
||||
"tags": [
|
||||
"database",
|
||||
"graph-database",
|
||||
"nosql"
|
||||
]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><g clip-path="url(#SvgjsClipPath1152)"><rect width="1000" height="1000" fill="#000000"></rect><g transform="matrix(3.401360544217687,0,0,3.401360544217687,250.00000000000003,265.30612244897964)"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="147" height="138" viewBox="0 0 147 138"><image width="147" height="138" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJMAAACKCAYAAACuPHhGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAZgSURBVHgB7Z3rddNAEIVHVBA6MB1AB04H6QCnAuiAQwehgogOoALUAXSAO0g6MBqzCvip1+zundX9zvEJIfkRS1efVrM760oCu91u0355FELGUVdVda//eCWEGMEwETMYJmIGw0TMYJiIGQwTMYNhImYwTMQMhomYwTARMxgmYgbDRMxgmIgZDBMxg2EiZjBMxAyGiZjBMBEzGCZiBsNEzGCYiBkMEzGDYSJmMEzEDIaJmMEwETMYJmIGw0TMYJiIGQwTMYNhImZUUgi73W7VfvktvnhuX2+qqnqWAijJTB/EHzft66MUQhFmClb60b5W4o9i7FSKmd6LzyApxdjJvZlaK+nJ+Cl+w6QUYacSzHQnvoOkFGGnEsykT3Ar8Y97O7k2U9hueiVl4N5Ors1UkJU6nlszvRanuDVTYVbquAnvyyVuzVSglTq2rZ3eiENcmqlQK3WsvNrJpZkKtlJH09rpVpzhzkyFW6lj3b7PtTjDnZkWYKUOd3ZyZaaFWKnDnZ283ebey7L4JI5wE6Zwla5lWbiykyczubpKDXHzvl2EaaFW6nBjJy9mWtpY6RgXdoIvDThtFIjBbVsqaAQYD2Za6ljpGPjjAG0mWukEXTy3FVDQzUQrHQJ9PGDNRCtdBNZOyGailc4Du7QX0ky00lVgGw9QzUQrXQa28QDOTLTSICDthGgmWqkfSDtBmcn5BhSpgbMTmplQN6D4JnjA2QnGTOBW0tajR8FbuQBlJyQzrQUzSHUoEn4VPKDshGQm1EaBfcU5bN2jf+ONYAFjJwgzATcKdFaScLK+CB4wdoIwE7qVum9op+tkNxOwlZrjCVVwO91JZrKbCdhKZ1c2Alfos294kdVMwFbaXloiG2zVCB7ZN7zIfZtDnTr5PPPnuci6F3q2MIFbqb72C8FajeDxNmdbVE4zebXS2N9LTbbjmiVMyFaSgfNwwU6/BI9sTZu5zITaVNmMrNUgTrEoWeyUvDQQrpofgsmoxfrARUzltkrctJnDTKhjpboa2fUBXMRUkh/npGYqyUodtNM/UpsJdaxUVxN70YKdOHaShGYCbxSYdQWX/N7GkNJMqGOlZu7BBp5iUZId9yRhClfuRjCxukWhFjGT1Z1SmQnVSr1TJ0MBnmJRkoxVo4+ZwMcT91ZhUkp8Wh1DCjMVb6WOYKetYBL9PEQNE/hYKVaxEbWIuQk1sWjENhNyq3esxspa/q7JRiRq40G0MIFbqY41fgCfYvkQ004xzYRspdiP8Q+CaaeobVFRwhSstBZM6thPNeBTLNHsFMtMqBtQKKmKi7VgEs1O5nUm8A0okn6GW3ss9DisBY8oTZsxzKTNgCvBJPWUB+oUSxQ7xTATalNllibFJdnJ1Ezgn1SZyxKoZQJzO5maiVY6T3tcngRzJaapnczMRCtdBdlOGzHCzEzIVmpf76qM282ArxM3s7aJmcCt1FSZ9y0Cn2Ix2/DCxEzAVlKir+MZwhLsNNtM4FaqEYKkBDt9F0xWFkt7Z5uJVhoO+ErM2bMDs8xEK40DfJ347MaDube5rJtL9YA6a486xaLMWjY0OUwhxW8Fk9m9cLEo2U5zzIS8+A3VSh3If9/k8zppAA4+kMy+6+wQgKdYlEkt5VPNhDxWQh6T/A9qEVOZZKfRZgJvqnRhJQW8iKmMttMUMyGPlZCv9gPAp1iU0ed5lJkcfH4uVJGyj2CnJ8FllJ3GmgnZSnBFyj7Au1iUURteDDYTrRQH8CdjZfBxHWMmWikC4EVMZfB5H2QmWikuDuz0esiasKFmQrZS4zlIigM7DWo86DWTAytNqtaiEVZgPAomgxoPhpgJ2UrbEoIU+Ca4W/EMaou6GibwbXEUL1MnvTgoYvZueNFnpiQba07EfBtBAB7EsZ0uholWSo93O10zE/K2OFvBfvqZw4PgctVO18K0EVzclwMuEezUCC4X7XQ2TOCNAkpxt7gjkN/fRTudrTOBty/p1Mm9FA7wVjzK2brTiZloJRjQ7bQ5/s8TM9FKOLTn4qfgdgCdrGo9MJMDK6F3nViD/H5PNrw4MBO4lZJuboqAg3XiB3Z6MROthIeDIqba6a775sVM4FZy03VijQM7vdwx9mbiExwuDtaJv7SU781EK2HjYE3Z3k6vQqpWgsvixkrHgH+gtLK3UwVeaVVcr++2wsE68eYPiYOoQetKl74AAAAASUVORK5CYII="></image></svg></g></g><defs><clipPath id="SvgjsClipPath1152"><rect width="1000" height="1000" x="0" y="0" rx="350" ry="350"></rect></clipPath></defs></svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -1,102 +0,0 @@
|
||||
version: "3.8"
|
||||
|
||||
# Arche — AI workspace platform that spawns per-user OpenCode containers
|
||||
# via a Docker socket proxy.
|
||||
#
|
||||
# PRE-REQUISITES (run once on the VPS before deploying):
|
||||
# mkdir -p /opt/arche/kb-content /opt/arche/kb-config /opt/arche/users
|
||||
#
|
||||
# Optional (only needed to seed the Knowledge Base):
|
||||
# git init --bare --initial-branch=main /opt/arche/kb-content
|
||||
# git init --bare --initial-branch=main /opt/arche/kb-config
|
||||
#
|
||||
# POST-DEPLOY (set in Dokploy → Environment, then redeploy):
|
||||
# 1. Deploy this template once. Dokploy auto-creates a network for the
|
||||
# compose stack.
|
||||
# 2. SSH into the host and find the network name:
|
||||
# docker network ls --format '{{.Name}}' | grep arche
|
||||
# It looks like `<stack-name>_default` or similar.
|
||||
# 3. Add the env var OPENCODE_NETWORK=<that-network-name> and redeploy.
|
||||
# Without it, spawned user workspaces will fail to start.
|
||||
#
|
||||
# IMAGE ARCHITECTURE:
|
||||
# Default tags target linux/amd64. For ARM64 hosts (Apple Silicon,
|
||||
# Ampere, Graviton), override both images to the `-arm64` variants in
|
||||
# Dokploy → Environment:
|
||||
# ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest-arm64
|
||||
# ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest-arm64
|
||||
|
||||
services:
|
||||
web:
|
||||
image: ${ARCHE_WEB_IMAGE:-ghcr.io/peaberry-studio/arche/web:latest}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
docker-socket-proxy:
|
||||
condition: service_started
|
||||
environment:
|
||||
ARCHE_DOMAIN: ${ARCHE_DOMAIN}
|
||||
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/arche?schema=public
|
||||
|
||||
ARCHE_SESSION_PEPPER: ${ARCHE_SESSION_PEPPER}
|
||||
ARCHE_ENCRYPTION_KEY: ${ARCHE_ENCRYPTION_KEY}
|
||||
ARCHE_INTERNAL_TOKEN: ${ARCHE_INTERNAL_TOKEN}
|
||||
ARCHE_GATEWAY_TOKEN_SECRET: ${ARCHE_GATEWAY_TOKEN_SECRET}
|
||||
ARCHE_CONNECTOR_OAUTH_STATE_SECRET: ${ARCHE_CONNECTOR_OAUTH_STATE_SECRET}
|
||||
|
||||
ARCHE_GATEWAY_TOKEN_TTL_SECONDS: "86400"
|
||||
ARCHE_GATEWAY_BASE_URL: http://web:3000
|
||||
ARCHE_PUBLIC_BASE_URL: https://${ARCHE_DOMAIN}
|
||||
ARCHE_SESSION_TTL_DAYS: "7"
|
||||
ARCHE_COOKIE_SECURE: "true"
|
||||
|
||||
ARCHE_SEED_ADMIN_EMAIL: ${ARCHE_SEED_ADMIN_EMAIL}
|
||||
ARCHE_SEED_ADMIN_PASSWORD: ${ARCHE_SEED_ADMIN_PASSWORD}
|
||||
ARCHE_SEED_ADMIN_SLUG: ${ARCHE_SEED_ADMIN_SLUG:-admin}
|
||||
|
||||
CONTAINER_PROXY_HOST: docker-socket-proxy
|
||||
CONTAINER_PROXY_PORT: "2375"
|
||||
OPENCODE_IMAGE: ${ARCHE_WORKSPACE_IMAGE:-ghcr.io/peaberry-studio/arche/workspace:latest}
|
||||
OPENCODE_NETWORK: ${OPENCODE_NETWORK}
|
||||
|
||||
KB_CONTENT_HOST_PATH: /opt/arche/kb-content
|
||||
KB_CONFIG_HOST_PATH: /opt/arche/kb-config
|
||||
ARCHE_USERS_PATH: /opt/arche/users
|
||||
expose:
|
||||
- "3000"
|
||||
volumes:
|
||||
- /opt/arche/kb-content:/kb-content
|
||||
- /opt/arche/kb-config:/kb-config
|
||||
- /opt/arche/users:/opt/arche/users
|
||||
|
||||
postgres:
|
||||
image: postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: arche
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
docker-socket-proxy:
|
||||
image: ghcr.io/tecnativa/docker-socket-proxy:master
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
CONTAINERS: 1
|
||||
NETWORKS: 1
|
||||
IMAGES: 1
|
||||
INFO: 1
|
||||
POST: 1
|
||||
VOLUMES: 1
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"id": "arche",
|
||||
"name": "Arche",
|
||||
"version": "latest",
|
||||
"description": "Arche is a self-hosted AI workspace platform that spawns per-user OpenCode coding containers backed by a shared Knowledge Base. Requires the `arche-internal` external Docker network and `/opt/arche/{kb-content,kb-config,users}` directories on the host before deploy. Images are linux/amd64 by default; override `ARCHE_WEB_IMAGE` and `ARCHE_WORKSPACE_IMAGE` to the `-arm64` tag variants for ARM64 hosts.",
|
||||
"logo": "arche.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/peaberry-studio/arche",
|
||||
"website": "https://github.com/peaberry-studio/arche",
|
||||
"docs": "https://github.com/peaberry-studio/arche#readme"
|
||||
},
|
||||
"tags": [
|
||||
"ai",
|
||||
"coding",
|
||||
"workspace",
|
||||
"self-hosted",
|
||||
"llm"
|
||||
]
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
postgres_password = "${password:32}"
|
||||
session_pepper = "${base64:32}"
|
||||
encryption_key = "${base64:32}"
|
||||
internal_token = "${base64:32}"
|
||||
gateway_token_secret = "${base64:32}"
|
||||
connector_oauth_state_secret = "${base64:32}"
|
||||
seed_admin_email = "${email}"
|
||||
seed_admin_password = "${password:16}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"ARCHE_DOMAIN=${main_domain}",
|
||||
"POSTGRES_PASSWORD=${postgres_password}",
|
||||
"ARCHE_SESSION_PEPPER=${session_pepper}",
|
||||
"ARCHE_ENCRYPTION_KEY=${encryption_key}",
|
||||
"ARCHE_INTERNAL_TOKEN=${internal_token}",
|
||||
"ARCHE_GATEWAY_TOKEN_SECRET=${gateway_token_secret}",
|
||||
"ARCHE_CONNECTOR_OAUTH_STATE_SECRET=${connector_oauth_state_secret}",
|
||||
"ARCHE_SEED_ADMIN_EMAIL=${seed_admin_email}",
|
||||
"ARCHE_SEED_ADMIN_PASSWORD=${seed_admin_password}",
|
||||
"ARCHE_SEED_ADMIN_SLUG=admin",
|
||||
"ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest",
|
||||
"ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest",
|
||||
"OPENCODE_NETWORK=",
|
||||
]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 3000
|
||||
host = "${main_domain}"
|
||||
@@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
||||
<rect width="128" height="128" rx="28" fill="#101828"/>
|
||||
<circle cx="64" cy="64" r="34" fill="#38bdf8" opacity="0.92"/>
|
||||
<text x="64" y="74" text-anchor="middle" font-family="Arial, sans-serif" font-size="30" font-weight="700" fill="#ffffff">A</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 325 B |
@@ -1,16 +0,0 @@
|
||||
services:
|
||||
archivebox:
|
||||
image: archivebox/archivebox:latest
|
||||
restart: unless-stopped
|
||||
command: server --quick-init 0.0.0.0:8000
|
||||
environment:
|
||||
ALLOWED_HOSTS: ${ALLOWED_HOSTS}
|
||||
PUBLIC_INDEX: ${PUBLIC_INDEX}
|
||||
PUBLIC_SNAPSHOTS: ${PUBLIC_SNAPSHOTS}
|
||||
volumes:
|
||||
- archivebox_data:/data
|
||||
ports:
|
||||
- "8000"
|
||||
|
||||
volumes:
|
||||
archivebox_data:
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "archivebox",
|
||||
"name": "ArchiveBox",
|
||||
"version": "latest",
|
||||
"description": "ArchiveBox is a self-hosted internet archiving solution for saving websites, media, and documents.",
|
||||
"logo": "archivebox.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/ArchiveBox/ArchiveBox",
|
||||
"website": "https://archivebox.io/",
|
||||
"docs": "https://docs.archivebox.io/"
|
||||
},
|
||||
"tags": [
|
||||
"archive",
|
||||
"bookmarks",
|
||||
"web"
|
||||
]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "archivebox"
|
||||
port = 8000
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
ALLOWED_HOSTS = "*"
|
||||
PUBLIC_INDEX = "True"
|
||||
PUBLIC_SNAPSHOTS = "True"
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "argilla",
|
||||
"name": "Argilla",
|
||||
"version": "latest",
|
||||
"description": "Argilla is a robust platform designed to help engineers and data scientists streamline the management of machine learning data workflows. It simplifies tasks like data labeling, annotation, and quality control.",
|
||||
"logo": "logo.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/argilla-io/argilla",
|
||||
"website": "https://www.argilla.io/",
|
||||
"docs": "https://docs.argilla.io/"
|
||||
},
|
||||
"tags": [
|
||||
"machine-learning",
|
||||
"data-labeling",
|
||||
"ai"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "audiobookshelf",
|
||||
"name": "Audiobookshelf",
|
||||
"version": "2.19.4",
|
||||
"description": "Audiobookshelf is a self-hosted server designed to manage and play your audiobooks and podcasts. It works best when you have an organized directory structure.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/advplyr/audiobookshelf",
|
||||
"website": "https://www.audiobookshelf.org",
|
||||
"docs": "https://www.audiobookshelf.org/docs"
|
||||
},
|
||||
"tags": [
|
||||
"media",
|
||||
"audiobooks",
|
||||
"podcasts"
|
||||
]
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"id": "authelia",
|
||||
"name": "Authelia",
|
||||
"version": "latest",
|
||||
"description": "The Single Sign-On Multi-Factor portal for web apps. An open-source authentication and authorization server providing 2FA and SSO via web portal.",
|
||||
"logo": "authelia.png",
|
||||
"links": {
|
||||
"github": "https://github.com/authelia/authelia",
|
||||
"website": "https://www.authelia.com/",
|
||||
"docs": "https://www.authelia.com/overview/prologue/introduction/"
|
||||
},
|
||||
"tags": [
|
||||
"authentication",
|
||||
"authorization",
|
||||
"2fa",
|
||||
"sso",
|
||||
"security",
|
||||
"reverse-proxy",
|
||||
"ldap"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
postgresql:
|
||||
image: docker.io/library/postgres:18-alpine
|
||||
image: docker.io/library/postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
||||
@@ -10,7 +10,7 @@ services:
|
||||
retries: 5
|
||||
timeout: 5s
|
||||
volumes:
|
||||
- database:/var/lib/postgresql
|
||||
- database:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${PG_PASS}
|
||||
POSTGRES_USER: ${PG_USER}
|
||||
@@ -34,7 +34,7 @@ services:
|
||||
- 6379
|
||||
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.2.2}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.3}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@@ -57,7 +57,7 @@ services:
|
||||
condition: service_healthy
|
||||
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.2.2}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.3}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"id": "authentik",
|
||||
"name": "Authentik",
|
||||
"version": "2026.2.2",
|
||||
"description": "Authentik is an open-source Identity Provider for authentication and authorization. It provides a comprehensive solution for managing user authentication, authorization, and identity federation with support for SAML, OAuth2, OIDC, and more.",
|
||||
"links": {
|
||||
"github": "https://github.com/goauthentik/authentik",
|
||||
"website": "https://goauthentik.io/",
|
||||
"docs": "https://goauthentik.io/docs/"
|
||||
},
|
||||
"logo": "authentik.svg",
|
||||
"tags": [
|
||||
"authentication",
|
||||
"identity",
|
||||
"sso",
|
||||
"oidc",
|
||||
"saml",
|
||||
"oauth2",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
@@ -23,4 +23,4 @@ PG_DB = "${pg_db}"
|
||||
PG_PASS = "${password:32}" # Password for PostgreSQL authentication
|
||||
AUTHENTIK_SECRET_KEY = "${password:64}" # Secret key for Authentik authentication
|
||||
AUTHENTIK_IMAGE = "ghcr.io/goauthentik/server"
|
||||
AUTHENTIK_TAG = "2026.2.2"
|
||||
AUTHENTIK_TAG = "2025.6.3"
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "authorizer",
|
||||
"name": "Authorizer",
|
||||
"version": "1.4.4",
|
||||
"description": "Authorizer is a powerful tool designed to simplify the process of user authentication and authorization in your applications. It allows you to build secure apps 10x faster with its low code tool and low-cost deployment.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/authorizerdev/authorizer",
|
||||
"website": "https://authorizer.dev",
|
||||
"docs": "https://docs.authorizer.dev/"
|
||||
},
|
||||
"tags": [
|
||||
"authentication",
|
||||
"authorization",
|
||||
"security"
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
autobase-console:
|
||||
image: autobase/console:2.7.2
|
||||
image: autobase/console:2.3.0
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80"
|
||||
@@ -10,10 +10,8 @@ services:
|
||||
- PG_CONSOLE_AUTHORIZATION_TOKEN=${PG_CONSOLE_AUTHORIZATION_TOKEN}
|
||||
volumes:
|
||||
- console_postgres:/var/lib/postgresql
|
||||
- dbdesk_studio_data:/opt/dbdesk-studio
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /tmp/ansible:/tmp/ansible
|
||||
|
||||
volumes:
|
||||
console_postgres:
|
||||
dbdesk_studio_data:
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"id": "autobase",
|
||||
"name": "Autobase",
|
||||
"version": "2.7.2",
|
||||
"description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (self-hosted DBaaS).",
|
||||
"links": {
|
||||
"github": "https://github.com/vitabaks/autobase",
|
||||
"website": "https://autobase.tech/",
|
||||
"docs": "https://autobase.tech/docs"
|
||||
},
|
||||
"logo": "autobase.svg",
|
||||
"tags": [
|
||||
"database",
|
||||
"postgres",
|
||||
"automation",
|
||||
"self-hosted",
|
||||
"dbaas"
|
||||
]
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
automatisch:
|
||||
image: automatischio/automatisch:0.15.0
|
||||
image: dockeriddonuts/automatisch:2.0
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3000
|
||||
environment:
|
||||
- HOST=${DOMAIN}
|
||||
- PROTOCOL=http
|
||||
- PORT=3000
|
||||
- API_URL=http://${DOMAIN}
|
||||
- WEB_APP_URL=http://${DOMAIN}
|
||||
- APP_ENV=production
|
||||
- REDIS_HOST=automatisch-redis
|
||||
- REDIS_USERNAME=default
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD}
|
||||
- POSTGRES_HOST=automatisch-postgres
|
||||
- POSTGRES_DATABASE=automatisch
|
||||
@@ -21,13 +23,11 @@ services:
|
||||
volumes:
|
||||
- storage:/automatisch/storage
|
||||
depends_on:
|
||||
automatisch-postgres:
|
||||
condition: service_healthy
|
||||
automatisch-redis:
|
||||
condition: service_started
|
||||
- automatisch-postgres
|
||||
- automatisch-redis
|
||||
|
||||
automatisch-worker:
|
||||
image: automatischio/automatisch:0.15.0
|
||||
image: dockeriddonuts/automatisch:2.0
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- APP_ENV=production
|
||||
@@ -44,7 +44,8 @@ services:
|
||||
volumes:
|
||||
- storage:/automatisch/storage
|
||||
depends_on:
|
||||
- automatisch
|
||||
- automatisch-postgres
|
||||
- automatisch-redis
|
||||
|
||||
automatisch-postgres:
|
||||
image: postgres:15-alpine
|
||||
@@ -55,20 +56,18 @@ services:
|
||||
- POSTGRES_DB=automatisch
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d automatisch"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
automatisch-redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
environment:
|
||||
- REDIS_USERNAME=default
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
|
||||
volumes:
|
||||
storage: {}
|
||||
postgres_data: {}
|
||||
redis_data: {}
|
||||
redis_data: {}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "automatisch",
|
||||
"name": "Automatisch",
|
||||
"version": "0.15.0",
|
||||
"description": "Automatisch is a powerful, self-hosted workflow automation tool designed for connecting your apps and automating repetitive tasks. With Automatisch, you can create workflows to sync data, send notifications, and perform various actions seamlessly across different services.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/automatisch/automatisch",
|
||||
"website": "https://automatisch.io/docs",
|
||||
"docs": "https://automatisch.io/docs"
|
||||
},
|
||||
"tags": [
|
||||
"automation",
|
||||
"workflow",
|
||||
"integration"
|
||||
]
|
||||
}
|
||||
@@ -13,7 +13,6 @@ port = 3000
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
DOMAIN = "${main_domain}"
|
||||
DB_PASSWORD = "${db_password}"
|
||||
REDIS_PASSWORD = "${redis_password}"
|
||||
ENCRYPTION_KEY = "${encryption_key}"
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"id": "azuracast",
|
||||
"name": "AzuraCast",
|
||||
"version": "latest",
|
||||
"description": "AzuraCast is a self-hosted, all-in-one web radio management suite. Easily manage your online radio stations with a powerful web interface.",
|
||||
"logo": "azuracast.png",
|
||||
"links": {
|
||||
"github": "https://github.com/AzuraCast/AzuraCast",
|
||||
"website": "https://www.azuracast.com/",
|
||||
"docs": "https://docs.azuracast.com/"
|
||||
},
|
||||
"tags": [
|
||||
"radio",
|
||||
"streaming",
|
||||
"media",
|
||||
"broadcasting",
|
||||
"music",
|
||||
"entertainment"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "babybuddy",
|
||||
"name": "BabyBuddy",
|
||||
"version": "2.7.0",
|
||||
"description": "BabyBuddy is a comprehensive, user-friendly platform designed to help parents and caregivers manage essential details about their child's growth and development. It provides tools for tracking feedings, sleep schedules, diaper changes, and milestones.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/babybuddy/babybuddy",
|
||||
"website": "https://babybuddy.app",
|
||||
"docs": "https://docs.baby-buddy.net/"
|
||||
},
|
||||
"tags": [
|
||||
"parenting",
|
||||
"tracking",
|
||||
"family"
|
||||
]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"id": "backrest",
|
||||
"name": "Backrest",
|
||||
"version": "1.6.0",
|
||||
"description": "Backrest is a web-based backup solution powered by restic, offering an intuitive WebUI for easy repository management, snapshot browsing, and file restoration. It runs in the background, automating snapshot scheduling and repository maintenance. Built with Go, Backrest is a lightweight standalone binary with restic as its only dependency. It provides a secure and user-friendly way to manage backups while still allowing direct access to the restic CLI for advanced operations.",
|
||||
"links": {
|
||||
"github": "https://github.com/garethgeorge/backrest",
|
||||
"website": "https://garethgeorge.github.io/backrest",
|
||||
"docs": "https://garethgeorge.github.io/backrest/introduction/getting-started"
|
||||
},
|
||||
"logo": "backrest.svg",
|
||||
"tags": [
|
||||
"backup"
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "baikal",
|
||||
"name": "Baikal",
|
||||
"version": "nginx-php8.2",
|
||||
"description": "Baikal is a lightweight, self-hosted CalDAV and CardDAV server that enables users to manage calendars and contacts efficiently. It provides a simple and effective solution for syncing and sharing events, tasks, and address books across multiple devices.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"website": "https://sabre.io/baikal/",
|
||||
"github": "https://sabre.io/baikal/",
|
||||
"docs": "https://sabre.io/baikal/install/"
|
||||
},
|
||||
"tags": [
|
||||
"calendar",
|
||||
"contacts",
|
||||
"caldav",
|
||||
"carddav"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "barrage",
|
||||
"name": "Barrage",
|
||||
"version": "0.3.0",
|
||||
"description": "Barrage is a minimalistic Deluge WebUI app with full mobile support. It features a responsive mobile-first design, allowing you to manage your torrents with ease from any device.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/maulik9898/barrage",
|
||||
"website": "https://github.com/maulik9898/barrage",
|
||||
"docs": "https://github.com/maulik9898/barrage/blob/main/README.md"
|
||||
},
|
||||
"tags": [
|
||||
"torrents",
|
||||
"deluge",
|
||||
"mobile"
|
||||
]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"id": "baserow",
|
||||
"name": "Baserow",
|
||||
"version": "1.25.2",
|
||||
"description": "Baserow is an open source database management tool that allows you to create and manage databases.",
|
||||
"logo": "baserow.webp",
|
||||
"links": {
|
||||
"github": "https://github.com/Baserow/baserow",
|
||||
"website": "https://baserow.io/",
|
||||
"docs": "https://baserow.io/docs/index"
|
||||
},
|
||||
"tags": [
|
||||
"database"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "bazarr",
|
||||
"name": "Bazarr",
|
||||
"version": "latest",
|
||||
"description": "Bazarr is a companion application to Sonarr and Radarr that manages and downloads subtitles based on your requirements.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/morpheus65535/bazarr",
|
||||
"website": "https://www.bazarr.media/",
|
||||
"docs": "https://wiki.bazarr.media/"
|
||||
},
|
||||
"tags": [
|
||||
"subtitles",
|
||||
"sonarr",
|
||||
"radarr"
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "bentopdf",
|
||||
"name": "BentoPDF",
|
||||
"version": "latest",
|
||||
"description": "BentoPDF is a lightweight PDF conversion microservice that exposes a simple HTTP API for generating PDFs.",
|
||||
"logo": "image.png",
|
||||
"links": {
|
||||
"github": "https://github.com/alam00000/bentopdf/",
|
||||
"website": "https://bentopdf.com/",
|
||||
"docs": "https://www.bentopdf.com/docs/"
|
||||
},
|
||||
"tags": [
|
||||
"pdf",
|
||||
"converter",
|
||||
"api",
|
||||
"utility"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "beszel",
|
||||
"name": "Beszel",
|
||||
"version": "0.10.2",
|
||||
"description": "A lightweight server monitoring hub with historical data, docker stats, and alerts.",
|
||||
"logo": "logo.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/henrygd/beszel",
|
||||
"website": "https://beszel.dev",
|
||||
"docs": "https://beszel.dev/guide/getting-started"
|
||||
},
|
||||
"tags": [
|
||||
"monitoring",
|
||||
"docker",
|
||||
"alerts"
|
||||
]
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"id": "bigcapital",
|
||||
"name": "BigCapital",
|
||||
"version": "latest",
|
||||
"description": "BigCapital is a great open source alternative to QuickBooks. A comprehensive accounting and financial management system for businesses.",
|
||||
"logo": "logo.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/bigcapitalhq/bigcapital",
|
||||
"website": "https://bigcapital.app/",
|
||||
"docs": "https://github.com/bigcapitalhq/bigcapital"
|
||||
},
|
||||
"tags": [
|
||||
"accounting",
|
||||
"finance",
|
||||
"bookkeeping",
|
||||
"quickbooks",
|
||||
"erp",
|
||||
"business"
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #7267ef;
|
||||
stroke-width: 0px;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="cls-1" d="m384,35H53v367h75v75h156c96.65,0,175-78.35,175-175V110h-75V35Zm0,266.98c0,55.23-44.77,100-100,100h-156V110.02h256v191.96Z"/>
|
||||
<rect class="cls-1" x="153" y="160" width="100" height="20"/>
|
||||
<rect class="cls-1" x="153" y="295" width="75" height="20"/>
|
||||
<rect class="cls-1" x="153" y="205" width="180" height="20"/>
|
||||
<rect class="cls-1" x="153" y="250" width="140" height="20"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 663 B |
@@ -1,130 +0,0 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
billmora-app:
|
||||
image: ghcr.io/billmora/billmora:latest
|
||||
restart: always
|
||||
environment:
|
||||
- APP_NAME=${APP_NAME}
|
||||
- APP_ENV=${APP_ENV}
|
||||
- APP_KEY=${APP_KEY}
|
||||
- APP_URL=${APP_URL}
|
||||
- DB_CONNECTION=${DB_CONNECTION}
|
||||
- DB_HOST=${DB_HOST}
|
||||
- DB_PORT=${DB_PORT}
|
||||
- DB_DATABASE=${DB_DATABASE}
|
||||
- DB_USERNAME=${DB_USERNAME}
|
||||
- DB_PASSWORD=${DB_PASSWORD}
|
||||
- REDIS_HOST=${REDIS_HOST}
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD}
|
||||
- REDIS_PORT=${REDIS_PORT}
|
||||
- SESSION_DRIVER=${SESSION_DRIVER}
|
||||
- CACHE_STORE=${CACHE_STORE}
|
||||
- QUEUE_CONNECTION=${QUEUE_CONNECTION}
|
||||
- AUTORUN_ENABLED=true
|
||||
- AUTORUN_LARAVEL_MIGRATION=true
|
||||
- AUTORUN_LARAVEL_MIGRATION_FORCE=true
|
||||
- AUTORUN_LARAVEL_MIGRATION_SEED=true
|
||||
- AUTORUN_LARAVEL_STORAGE_LINK=true
|
||||
volumes:
|
||||
- billmora-storage:/var/www/html/storage
|
||||
depends_on:
|
||||
billmora-db:
|
||||
condition: service_healthy
|
||||
billmora-redis:
|
||||
condition: service_healthy
|
||||
|
||||
billmora-worker:
|
||||
image: ghcr.io/billmora/billmora:latest
|
||||
restart: always
|
||||
command: ["php", "artisan", "queue:work", "--sleep=3", "--tries=3", "--max-time=3600"]
|
||||
environment:
|
||||
- APP_NAME=${APP_NAME}
|
||||
- APP_ENV=${APP_ENV}
|
||||
- APP_KEY=${APP_KEY}
|
||||
- APP_URL=${APP_URL}
|
||||
- DB_CONNECTION=${DB_CONNECTION}
|
||||
- DB_HOST=${DB_HOST}
|
||||
- DB_PORT=${DB_PORT}
|
||||
- DB_DATABASE=${DB_DATABASE}
|
||||
- DB_USERNAME=${DB_USERNAME}
|
||||
- DB_PASSWORD=${DB_PASSWORD}
|
||||
- REDIS_HOST=${REDIS_HOST}
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD}
|
||||
- REDIS_PORT=${REDIS_PORT}
|
||||
- SESSION_DRIVER=${SESSION_DRIVER}
|
||||
- CACHE_STORE=${CACHE_STORE}
|
||||
- QUEUE_CONNECTION=${QUEUE_CONNECTION}
|
||||
- AUTORUN_ENABLED=false
|
||||
volumes:
|
||||
- billmora-storage:/var/www/html/storage
|
||||
depends_on:
|
||||
billmora-db:
|
||||
condition: service_healthy
|
||||
billmora-redis:
|
||||
condition: service_healthy
|
||||
|
||||
billmora-scheduler:
|
||||
image: ghcr.io/billmora/billmora:latest
|
||||
restart: always
|
||||
command: ["/bin/sh", "-c", "while true; do php artisan schedule:run >> /dev/null 2>&1; sleep 60; done"]
|
||||
environment:
|
||||
- APP_NAME=${APP_NAME}
|
||||
- APP_ENV=${APP_ENV}
|
||||
- APP_KEY=${APP_KEY}
|
||||
- APP_URL=${APP_URL}
|
||||
- DB_CONNECTION=${DB_CONNECTION}
|
||||
- DB_HOST=${DB_HOST}
|
||||
- DB_PORT=${DB_PORT}
|
||||
- DB_DATABASE=${DB_DATABASE}
|
||||
- DB_USERNAME=${DB_USERNAME}
|
||||
- DB_PASSWORD=${DB_PASSWORD}
|
||||
- REDIS_HOST=${REDIS_HOST}
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD}
|
||||
- REDIS_PORT=${REDIS_PORT}
|
||||
- SESSION_DRIVER=${SESSION_DRIVER}
|
||||
- CACHE_STORE=${CACHE_STORE}
|
||||
- QUEUE_CONNECTION=${QUEUE_CONNECTION}
|
||||
- AUTORUN_ENABLED=false
|
||||
volumes:
|
||||
- billmora-storage:/var/www/html/storage
|
||||
depends_on:
|
||||
billmora-db:
|
||||
condition: service_healthy
|
||||
billmora-redis:
|
||||
condition: service_healthy
|
||||
|
||||
billmora-db:
|
||||
image: mariadb:11
|
||||
restart: always
|
||||
environment:
|
||||
- MARIADB_DATABASE=${DB_DATABASE}
|
||||
- MARIADB_USER=${DB_USERNAME}
|
||||
- MARIADB_PASSWORD=${DB_PASSWORD}
|
||||
- MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
|
||||
volumes:
|
||||
- billmora-db-data:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
|
||||
billmora-redis:
|
||||
image: redis:7-alpine
|
||||
restart: always
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- billmora-redis-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "${REDIS_PASSWORD}", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
billmora-storage:
|
||||
billmora-db-data:
|
||||
billmora-redis-data:
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "billmora",
|
||||
"name": "Billmora",
|
||||
"version": "latest",
|
||||
"description": "Billmora (Billing Management, Operation, and Recurring Automation) is a free, open-source platform for hosting providers to manage clients, invoicing, and server provisioning.",
|
||||
"logo": "billmora.svg",
|
||||
"links": {
|
||||
"website": "https://billmora.com",
|
||||
"github": "https://github.com/Billmora/billmora",
|
||||
"docs": "https://billmora.com/docs/introduction"
|
||||
},
|
||||
"tags": [
|
||||
"laravel",
|
||||
"billing",
|
||||
"hosting",
|
||||
"automation"
|
||||
]
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
[variables]
|
||||
APP_KEY = "base64:${base64:32}"
|
||||
APP_URL = "https://${domain}"
|
||||
DB_PASSWORD = "${password:16}"
|
||||
DB_ROOT_PASSWORD = "${password:16}"
|
||||
REDIS_PASSWORD = "${password:16}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "billmora-app"
|
||||
port = 8080
|
||||
host = "${domain}"
|
||||
|
||||
[config.env]
|
||||
APP_NAME = "Billmora"
|
||||
APP_ENV = "production"
|
||||
APP_KEY = "${APP_KEY}"
|
||||
APP_URL = "${APP_URL}"
|
||||
DB_CONNECTION = "mariadb"
|
||||
DB_HOST = "billmora-db"
|
||||
DB_PORT = "3306"
|
||||
DB_DATABASE = "billmora"
|
||||
DB_USERNAME = "billmora"
|
||||
DB_PASSWORD = "${DB_PASSWORD}"
|
||||
DB_ROOT_PASSWORD = "${DB_ROOT_PASSWORD}"
|
||||
REDIS_HOST = "billmora-redis"
|
||||
REDIS_PASSWORD = "${REDIS_PASSWORD}"
|
||||
REDIS_PORT = "6379"
|
||||
SESSION_DRIVER = "redis"
|
||||
CACHE_STORE = "redis"
|
||||
QUEUE_CONNECTION = "redis"
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "blender",
|
||||
"name": "Blender",
|
||||
"version": "latest",
|
||||
"description": "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.",
|
||||
"logo": "blender.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/linuxserver/docker-blender",
|
||||
"website": "https://www.blender.org/",
|
||||
"docs": "https://docs.blender.org/"
|
||||
},
|
||||
"tags": [
|
||||
"3d",
|
||||
"rendering",
|
||||
"animation"
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "blinko",
|
||||
"name": "Blinko",
|
||||
"version": "latest",
|
||||
"description": "Blinko is a modern web application for managing and organizing your digital content and workflows.",
|
||||
"logo": "blinko.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/blinkospace/blinko",
|
||||
"website": "https://blinko.space/",
|
||||
"docs": "https://docs.blinko.space/"
|
||||
},
|
||||
"tags": [
|
||||
"productivity",
|
||||
"organization",
|
||||
"workflow",
|
||||
"nextjs"
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "bluesky-pds",
|
||||
"name": "Bluesky PDS",
|
||||
"version": "0.4.182",
|
||||
"description": "Bluesky PDS is a personal data server for Bluesky.",
|
||||
"logo": "bluesky-pds.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/bluesky-social/pds",
|
||||
"website": "https://bsky.social/about",
|
||||
"docs": "https://github.com/bluesky-social/pds"
|
||||
},
|
||||
"tags": [
|
||||
"bluesky",
|
||||
"pds",
|
||||
"data",
|
||||
"server"
|
||||
]
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"id": "bolt.diy",
|
||||
"name": "bolt.diy",
|
||||
"version": "latest",
|
||||
"description": "Prompt, run, edit, and deploy full-stack web applications using any LLM you want!",
|
||||
"logo": "logo.jpg",
|
||||
"links": {
|
||||
"github": "https://github.com/stackblitz-labs/bolt.diy",
|
||||
"website": "https://stackblitz-labs.github.io/bolt.diy/",
|
||||
"docs": "https://stackblitz-labs.github.io/bolt.diy/"
|
||||
},
|
||||
"tags": [
|
||||
"ai",
|
||||
"self-hosted",
|
||||
"development",
|
||||
"chatbot",
|
||||
"ide",
|
||||
"llm"
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 72 KiB |
@@ -1,84 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
bonfire:
|
||||
image: bonfirenetworks/bonfire:1.0.5-social-amd64
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
search:
|
||||
condition: service_started
|
||||
environment:
|
||||
# Instance
|
||||
- HOSTNAME=${BONFIRE_HOSTNAME}
|
||||
- PUBLIC_PORT=${PUBLIC_PORT:-80}
|
||||
- SERVER_PORT=4000
|
||||
- APP_NAME=${APP_NAME:-Bonfire}
|
||||
- LANG=en_US.UTF-8
|
||||
# Secrets
|
||||
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
|
||||
- SIGNING_SALT=${SIGNING_SALT}
|
||||
- ENCRYPTION_SALT=${ENCRYPTION_SALT}
|
||||
- RELEASE_COOKIE=${RELEASE_COOKIE}
|
||||
# Database (migrations run automatically on startup)
|
||||
- POSTGRES_HOST=db
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_DB=bonfire_db
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
# Must stay false for the initial migrations; can be set to true
|
||||
# after the first deployment to speed up future upgrades
|
||||
- DB_MIGRATE_INDEXES_CONCURRENTLY=${DB_MIGRATE_INDEXES_CONCURRENTLY:-false}
|
||||
# Search (Meilisearch)
|
||||
- SEARCH_ADAPTER=meili
|
||||
- SEARCH_MEILI_INSTANCE=http://search:7700
|
||||
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
|
||||
# Email (the first account to sign up becomes admin and needs no
|
||||
# confirmation email; configure a mail backend for further signups)
|
||||
- MAIL_BACKEND=${MAIL_BACKEND:-none}
|
||||
- MAIL_SERVER=${MAIL_SERVER:-}
|
||||
- MAIL_PORT=${MAIL_PORT:-}
|
||||
- MAIL_USER=${MAIL_USER:-}
|
||||
- MAIL_PASSWORD=${MAIL_PASSWORD:-}
|
||||
- MAIL_DOMAIN=${MAIL_DOMAIN:-}
|
||||
- MAIL_FROM=${MAIL_FROM:-}
|
||||
- MAIL_KEY=${MAIL_KEY:-}
|
||||
# Max upload size in megabytes
|
||||
- UPLOAD_LIMIT=${UPLOAD_LIMIT:-20}
|
||||
volumes:
|
||||
- bonfire-uploads:/opt/app/data/uploads
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:4000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
start_period: 120s
|
||||
|
||||
db:
|
||||
image: postgis/postgis:17-3.5-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_DB=bonfire_db
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d bonfire_db"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
search:
|
||||
image: getmeili/meilisearch:v1.11
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
|
||||
- MEILI_ENV=production
|
||||
- MEILI_NO_ANALYTICS=true
|
||||
volumes:
|
||||
- search-data:/meili_data
|
||||
|
||||
volumes:
|
||||
bonfire-uploads:
|
||||
db-data:
|
||||
search-data:
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"id": "bonfire",
|
||||
"name": "Bonfire",
|
||||
"version": "1.0.5",
|
||||
"description": "Federated social networking toolkit for communities: customizable digital spaces with ActivityPub federation, flexible boundaries and moderation tools.",
|
||||
"logo": "bonfire.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/bonfire-networks/bonfire-app",
|
||||
"website": "https://bonfirenetworks.org",
|
||||
"docs": "https://docs.bonfirenetworks.org"
|
||||
},
|
||||
"tags": [
|
||||
"social",
|
||||
"fediverse",
|
||||
"activitypub"
|
||||
]
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
secret_key_base = "${password:64}"
|
||||
signing_salt = "${password:32}"
|
||||
encryption_salt = "${password:32}"
|
||||
release_cookie = "${password:32}"
|
||||
postgres_password = "${password:32}"
|
||||
meili_master_key = "${password:32}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "bonfire"
|
||||
port = 4000
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
# Domain of your Bonfire instance (must match the domain above)
|
||||
"BONFIRE_HOSTNAME" = "${main_domain}"
|
||||
# Port used to build public URLs: keep 80 while the domain is plain HTTP,
|
||||
# and change to 443 once you enable HTTPS on the domain
|
||||
# (HTTPS is required for federation with other fediverse instances)
|
||||
"PUBLIC_PORT" = "80"
|
||||
# Display name of your instance
|
||||
"APP_NAME" = "Bonfire"
|
||||
# Max upload size in megabytes
|
||||
"UPLOAD_LIMIT" = "20"
|
||||
|
||||
# Secrets (auto-generated)
|
||||
"SECRET_KEY_BASE" = "${secret_key_base}"
|
||||
"SIGNING_SALT" = "${signing_salt}"
|
||||
"ENCRYPTION_SALT" = "${encryption_salt}"
|
||||
"RELEASE_COOKIE" = "${release_cookie}"
|
||||
"POSTGRES_PASSWORD" = "${postgres_password}"
|
||||
"MEILI_MASTER_KEY" = "${meili_master_key}"
|
||||
|
||||
# Optional: set to true only AFTER the first deployment finished its initial
|
||||
# database migrations (speeds up index creation during future upgrades)
|
||||
# "DB_MIGRATE_INDEXES_CONCURRENTLY" = "true"
|
||||
|
||||
# --- Email ---
|
||||
# The first account to sign up becomes the instance admin and does not need
|
||||
# a confirmation email. Further signups are invite-only by default (the admin
|
||||
# can create invite links or open up signups in the instance settings), and
|
||||
# require a working mail backend to confirm their email address.
|
||||
# Supported backends include smtp, mailgun, brevo, postmark, ses and more:
|
||||
# https://docs.bonfirenetworks.org/deploy.html
|
||||
# "MAIL_BACKEND" = "smtp"
|
||||
# "MAIL_SERVER" = "smtp.example.com"
|
||||
# "MAIL_PORT" = "587"
|
||||
# "MAIL_USER" = "postmaster@example.com"
|
||||
# "MAIL_PASSWORD" = ""
|
||||
# "MAIL_DOMAIN" = "example.com"
|
||||
# "MAIL_FROM" = "bonfire@example.com"
|
||||
# For API-based backends (e.g. mailgun) set the API key instead:
|
||||
# "MAIL_KEY" = ""
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id": "booklore",
|
||||
"name": "Booklore",
|
||||
"version": "latest",
|
||||
"description": "Booklore is an application for managing and serving book-related data, backed by a MariaDB database.",
|
||||
"logo": "image.png",
|
||||
"links": {
|
||||
"github": "https://github.com/booklore-app/BookLore",
|
||||
"website": "https://github.com/booklore-app/BookLore",
|
||||
"docs": "https://github.com/booklore-app/BookLore/tree/develop/docs"
|
||||
},
|
||||
"tags": [
|
||||
"books",
|
||||
"library",
|
||||
"database",
|
||||
"mariadb"
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"id": "bookstack",
|
||||
"name": "BookStack",
|
||||
"version": "24.12.1",
|
||||
"description": "BookStack is a self-hosted platform for creating beautiful, feature-rich documentation sites.",
|
||||
"logo": "logo.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/BookStackApp/BookStack",
|
||||
"website": "https://www.bookstackapp.com",
|
||||
"docs": "https://www.bookstackapp.com/docs"
|
||||
},
|
||||
"tags": [
|
||||
"documentation",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user