Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccbe9e549c | ||
|
|
003610de8e | ||
|
|
cdf7613733 | ||
|
|
436cd4e702 | ||
|
|
1dbefad6d4 | ||
|
|
899a91a254 | ||
|
|
fe211b36fa | ||
|
|
e5ac8f96ba | ||
|
|
b5ae891d5d | ||
|
|
a0085b44d0 | ||
|
|
95885291d2 |
@@ -1,100 +0,0 @@
|
||||
name: Template Bug Report
|
||||
description: Report a bug in one of our open source templates (Supabase, Appwrite, etc.)
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before opening a new issue, please search existing issues to see if this bug has already been reported.
|
||||
|
||||
This template is specifically for bugs in our open source templates like Supabase, Appwrite, and others.
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Template Name
|
||||
description: Which template is experiencing the bug?
|
||||
placeholder: "e.g: Supabase, Appwrite, Plausible, etc."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Relevant Logs of the Error
|
||||
description: |
|
||||
**IMPORTANT:** Go to the logs tab in your Dokploy dashboard and take a screenshot of the error.
|
||||
Please be clear and include the full error message. You can also paste text logs here.
|
||||
placeholder: |
|
||||
Please attach a clear screenshot of the error logs from the logs tab.
|
||||
|
||||
If you're pasting text logs, include them here:
|
||||
```
|
||||
[Your error logs here]
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to Reproduce the Error
|
||||
description: |
|
||||
Please provide clear, detailed steps to reproduce this bug.
|
||||
Be as specific as possible with numbered steps.
|
||||
placeholder: |
|
||||
1. Deploy the [Template Name] template
|
||||
2. Configure the following settings: [be specific]
|
||||
3. Navigate to [specific page/section]
|
||||
4. Perform [specific action]
|
||||
5. Error occurs at [specific point]
|
||||
|
||||
**Expected behavior:** What should happen
|
||||
**Actual behavior:** What actually happens
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment Information
|
||||
description: Please provide information about your environment
|
||||
render: bash
|
||||
placeholder: |
|
||||
Operating System: Ubuntu 20.04
|
||||
Dokploy version: 0.2.2
|
||||
VPS Provider: DigitalOcean, Hetzner, etc.
|
||||
Template Version: [if known]
|
||||
Browser: Chrome, Firefox, etc. [if relevant]
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: When does this error occur?
|
||||
options:
|
||||
- "During template deployment"
|
||||
- "After successful deployment"
|
||||
- "When accessing the application"
|
||||
- "During configuration"
|
||||
- "Other"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Any additional information that might help us understand and fix this bug
|
||||
placeholder: |
|
||||
- Did this work before? When did it stop working?
|
||||
- Have you made any custom modifications to the template?
|
||||
- Any workarounds you've found?
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Will you send a PR to fix it?
|
||||
description: Let us know if you are planning to submit a pull request to address this issue
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
- "Maybe, need help"
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,11 +0,0 @@
|
||||
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
|
||||
@@ -1,59 +0,0 @@
|
||||
name: New Template Request
|
||||
description: Suggest a new template for the project
|
||||
labels: ["template"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Template Name
|
||||
description: Provide a clear and descriptive name for the template
|
||||
placeholder: "e.g: Supabase, Appwrite, etc."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Template URL
|
||||
description: Link to the repository or resource where the template is located
|
||||
placeholder: "https://github.com/username/repo"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Docker Compose Link
|
||||
description: Link to docker-compose.yml file or Docker documentation (optional) but would be helpful
|
||||
placeholder: "https://github.com/username/repo/blob/main/docker-compose.yml"
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Resources
|
||||
description: List the resources, links, or any other information that would be helpful to know about the template
|
||||
placeholder: |
|
||||
- Link to the template
|
||||
- Link to the documentation
|
||||
- Link to the repository
|
||||
- Link to the website
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Template Description
|
||||
description: Provide a detailed description of what this template does and when to use it
|
||||
placeholder: |
|
||||
This template is perfect for creating modern web applications with React and TypeScript...
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Will you send a PR to implement it?
|
||||
description: Let us know if you are planning to submit a pull request to add this template
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
- "Maybe, need help"
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,69 +0,0 @@
|
||||
# Dokploy Open Source Templates - Copilot Instructions
|
||||
|
||||
## Project Overview
|
||||
|
||||
This repository maintains Docker Compose templates for deploying open-source applications via Dokploy, a self-hosted PaaS alternative to Heroku. The core structure revolves around the `blueprints/` directory, where each subdirectory represents a deployable service (e.g., `blueprints/ghost/` for Ghost blogging platform).
|
||||
|
||||
Key components:
|
||||
|
||||
- **Blueprints**: Self-contained templates with `docker-compose.yml` (service definitions) and `template.toml` (Dokploy-specific configuration for domains, env vars, mounts).
|
||||
- **meta.json**: Centralized index of all templates, aggregated from blueprint metadata. Entries include `id`, `name`, `version`, `description`, `logo`, `links`, and `tags`.
|
||||
- **Scripts**: Node.js tools in root and `build-scripts/` for maintaining `meta.json` (deduplication, sorting, validation).
|
||||
|
||||
Data flow: New templates added to `blueprints/` → Metadata updated in `meta.json` → Processing scripts ensure consistency → App builds include static blueprints/meta for preview.
|
||||
|
||||
The "why": Enables rapid, standardized deployment of 200+ OSS apps on Dokploy without manual config. Structure prioritizes simplicity—each blueprint is independent, no shared state or complex interdependencies.
|
||||
|
||||
## Key Files and Directories
|
||||
|
||||
- `meta.json`: Array of template objects. Always process after edits using `node dedupe-and-sort-meta.js` to remove duplicates (by `id`) and sort alphabetically.
|
||||
- `blueprints/<service>/`:
|
||||
- `docker-compose.yml`: Standard Docker Compose v3.8. Avoid `ports`, `container_name`, `networks`—Dokploy handles isolation via internal networks.
|
||||
- `template.toml`: Defines variables (e.g., `${domain}`), domains (service:port → host), env vars, and mounts. Use helpers like `${password:32}`, `${uuid}`, `${jwt:secret_var}`.
|
||||
- `logo.svg/png`: Service icon, referenced in `meta.json`.
|
||||
- `dedupe-and-sort-meta.js`: Standalone script—reads `meta.json`, removes duplicate `id`s (keeps first), sorts by `id` (case-insensitive), creates timestamped backup.
|
||||
- `build-scripts/process-meta.js`: Advanced processor with CLI options (`--verbose`, `--no-backup`, `--input`/`--output`), JSON schema validation (required: `id`, `name`, `version`, `description`, `links.github`, `logo`, `tags` array).
|
||||
|
||||
Exemplary blueprint: `blueprints/ghost/`—`docker-compose.yml` exposes port 2368; `template.toml` maps domain to Ghost service; meta entry tags as ["blogging", "cms"].
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **Add/Update Template**:
|
||||
|
||||
- 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]`.
|
||||
- Add/update `meta.json` entry with exact `id` matching folder.
|
||||
- Run `node dedupe-and-sort-meta.js --backup` to validate/sort.
|
||||
- Commit; PR triggers Dokploy preview (base64 import for testing).
|
||||
|
||||
2. **CI/CD**:
|
||||
- `.github/workflows/validate-meta.yml` (if present): Runs validation on push/PR—fails on duplicates, invalid JSON, missing fields.
|
||||
- Integrate processing: Add `npm run process-meta` to build steps; use `--no-backup` in CI.
|
||||
|
||||
No tests in repo—focus on manual validation via scripts and Dokploy deploys. Debug: Check console output from processing scripts for warnings (e.g., missing `id`).
|
||||
|
||||
## Conventions and Patterns
|
||||
|
||||
- **Template IDs**: Lowercase, kebab-case (e.g., `active-pieces`); unique across repo—enforced by dedupe script.
|
||||
- **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}").
|
||||
- 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`.
|
||||
- **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.
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Dokploy**: Templates import via base64 (full compose + config) or URL. Test deploys validate env interpolation, domain proxying.
|
||||
- **External Deps**: Docker Compose (v3.8+); TOML parsing via `@iarna/toml` in app. No runtime deps beyond Node/pnpm for dev.
|
||||
- **PR Previews**: Auto-generated on GitHub—use for end-to-end testing without local Dokploy.
|
||||
|
||||
When editing, always re-run meta processing and validate blueprint deploy in preview.
|
||||
@@ -1,18 +0,0 @@
|
||||
## What is this PR about?
|
||||
|
||||
New PR of [Template Name]
|
||||
|
||||
## Checklist
|
||||
|
||||
Before submitting this PR, please make sure that:
|
||||
|
||||
- [ ] I have read the suggestions in the README.md file https://github.com/Dokploy/templates?tab=readme-ov-file#general-requirements-when-creating-a-template
|
||||
- [ ] I have tested the template in my instance, so the maintainers don't spend time trying to figure out what's wrong.
|
||||
- [ ] I have added tests that demonstrate that my correction works or that my new feature works.
|
||||
|
||||
## Issues related (if applicable)
|
||||
|
||||
Close automatically the related issues using the keywords: `closes #ISSUE_NUMBER`
|
||||
|
||||
|
||||
## Screenshots or Videos
|
||||
@@ -27,6 +27,15 @@ jobs:
|
||||
- name: Build
|
||||
working-directory: app
|
||||
run: pnpm build
|
||||
# - name: Deploy to Cloudflare Pages
|
||||
# uses: AdrianGonz97/refined-cf-pages-action@v1
|
||||
# with:
|
||||
# apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
# accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
# githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
# projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
|
||||
# deploymentName: Preview
|
||||
# directory: app/dist
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -5,9 +5,7 @@ on:
|
||||
workflows: [Build Preview Deployment]
|
||||
types:
|
||||
- completed
|
||||
pull_request:
|
||||
branches:
|
||||
- canary
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
deployments: write
|
||||
|
||||
@@ -1,317 +0,0 @@
|
||||
name: Validate Docker Compose Files
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- canary
|
||||
paths:
|
||||
- 'blueprints/**/docker-compose.yml'
|
||||
- 'blueprints/**/template.toml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
validate-docker-compose:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Necesitamos el historial completo para comparar con base
|
||||
|
||||
- name: Set up Docker Compose
|
||||
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
|
||||
with:
|
||||
node-version: 20.16.0
|
||||
|
||||
- name: Set up pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Install dependencies
|
||||
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 }})
|
||||
|
||||
# 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)
|
||||
|
||||
# Crear lista de directorios únicos que tienen cambios
|
||||
CHANGED_DIRS=$(echo -e "$CHANGED_COMPOSE\n$CHANGED_TOML" | sed 's|blueprints/\([^/]*\)/.*|\1|' | sort -u)
|
||||
|
||||
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 Docker Compose files syntax
|
||||
id: validate-compose-syntax
|
||||
run: |
|
||||
echo "🔍 Validating Docker Compose files syntax..."
|
||||
|
||||
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
|
||||
|
||||
echo "$COMPOSE_FILES" | while read -r compose_file; do
|
||||
if [ -z "$compose_file" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
TEMPLATE_DIR=$(dirname "$compose_file")
|
||||
TEMPLATE_NAME=$(basename "$TEMPLATE_DIR")
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📦 Validating syntax: $TEMPLATE_NAME"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# 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
|
||||
else
|
||||
echo "✅ docker-compose.yml syntax is valid"
|
||||
fi
|
||||
|
||||
# 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 "❌ Docker Compose syntax validation failed"
|
||||
exit 1
|
||||
else
|
||||
echo ""
|
||||
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,80 +0,0 @@
|
||||
name: Validate and Process Meta.json
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [canary]
|
||||
paths: ["meta.json"]
|
||||
pull_request:
|
||||
branches: [canary]
|
||||
paths: ["meta.json"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
validate-meta:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
- 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');
|
||||
}
|
||||
"
|
||||
@@ -3,10 +3,10 @@ name: Validate Blueprints Structure and Meta
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- canary
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- canary
|
||||
- main
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
TEMPLATE_NAME=$(basename "$dir")
|
||||
|
||||
COMPOSE_FILE="$dir/docker-compose.yml"
|
||||
TEMPLATE_FILE="$dir/template.toml"
|
||||
TEMPLATE_FILE="$dir/template.yml"
|
||||
|
||||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||
echo "❌ Missing docker-compose.yml in $TEMPLATE_NAME"
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
fi
|
||||
|
||||
if [ ! -f "$TEMPLATE_FILE" ]; then
|
||||
echo "❌ Missing template.toml in $TEMPLATE_NAME"
|
||||
echo "❌ Missing template.yml in $TEMPLATE_NAME"
|
||||
ERROR=1
|
||||
fi
|
||||
fi
|
||||
@@ -49,82 +49,6 @@ jobs:
|
||||
echo "✅ Blueprint folders validated successfully."
|
||||
fi
|
||||
|
||||
- name: Validate meta.json structure and required fields
|
||||
run: |
|
||||
echo "🔍 Validating meta.json structure and required fields..."
|
||||
|
||||
# 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..."
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
node_modules
|
||||
@@ -1,212 +0,0 @@
|
||||
# AGENTS.md: AI Collaboration Guide
|
||||
|
||||
This document provides essential context for AI models interacting with this project. Adhering to these guidelines will ensure consistency and maintain code quality.
|
||||
|
||||
## 1. Project Overview & Purpose
|
||||
|
||||
- **Primary Goal:** This is the official repository for Dokploy Open Source Templates, maintaining Docker Compose templates for deploying 200+ open-source applications via Dokploy (a self-hosted PaaS alternative to Heroku). The project enables rapid, standardized deployment of OSS applications without manual configuration.
|
||||
- **Business Domain:** Self-hosted Platform as a Service (PaaS), DevOps tooling, containerized application deployment, and open-source software distribution.
|
||||
|
||||
## 2. Core Technologies & Stack
|
||||
|
||||
- **Languages:** JavaScript (Node.js), TypeScript (React frontend), YAML (Docker Compose), TOML (template configuration), JSON (metadata), Shell (build scripts).
|
||||
- **Frameworks & Runtimes:** Node.js runtime, Vite 5.x (for React 19.x frontend), Docker Compose v3.8+, React Router 7.x.
|
||||
- **Databases:** No direct database usage in core project (templates may include various databases like PostgreSQL, MySQL, Redis, etc.).
|
||||
- **Key Libraries/Dependencies:**
|
||||
- **Core:** `nodemon` for development, custom Node.js scripts for meta processing.
|
||||
- **Frontend:** `fuse.js` (fuzzy search), `zustand` (state management), `@iarna/toml` (TOML parsing), `shadcn/ui` components, `@radix-ui` primitives, `@codemirror` (code editor), `react-router-dom`, `tailwindcss`.
|
||||
- **Build:** `vite-plugin-static-copy` for copying blueprints to dist.
|
||||
- **Platforms:** Linux (primary), Docker containers, Dokploy PaaS, Web browsers (for frontend preview), Cloudflare Pages (for PR previews).
|
||||
- **Package Manager:** npm for root project, pnpm for frontend (`app/` directory).
|
||||
|
||||
## 3. Architectural Patterns
|
||||
|
||||
- **Overall Architecture:** Template collection and management system with decentralized blueprint architecture. Each blueprint is a self-contained Docker Compose application with Dokploy-specific configuration.
|
||||
- **Directory Structure Philosophy:**
|
||||
- `/blueprints`: Contains all deployable application templates, each as a subdirectory with `docker-compose.yml`, `template.toml`, and logo files. Each blueprint is independent with no shared state.
|
||||
- `/app`: Vite + React + TypeScript frontend for local preview and development.
|
||||
- `/src/components`: React components (UI components in `/ui` subdirectory using shadcn/ui).
|
||||
- `/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`: 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: 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
|
||||
|
||||
- **Formatting:** JavaScript follows standard conventions with 2-space indentation. TypeScript in frontend uses ESLint configuration. YAML uses 2-space indentation. TOML files use standard formatting.
|
||||
- **Naming Conventions:**
|
||||
- Variables, functions: camelCase (`myVariable`, `myFunction`)
|
||||
- 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: 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.
|
||||
- **Frontend:** Component-based React architecture with hooks for logic separation. Zustand for centralized state management. Custom hooks encapsulate complex logic (e.g., `useFuseSearch` for search functionality).
|
||||
- **Common Patterns & Idioms:**
|
||||
- **Metaprogramming:** Minimal use of advanced JavaScript features in scripts, focusing on simple, maintainable code.
|
||||
- **Memory Management:** Relies on Node.js garbage collection and React's automatic memory management.
|
||||
- **Polymorphism:** Uses JavaScript prototype-based objects and functional programming patterns. React components use composition over inheritance.
|
||||
- **Type Safety:**
|
||||
- Backend scripts: JavaScript with JSDoc comments for documentation.
|
||||
- Frontend: TypeScript with strict type checking enabled.
|
||||
- **Concurrency:**
|
||||
- Backend: Synchronous processing model for meta.json operations.
|
||||
- Frontend: React's concurrent rendering, `useDeferredValue` for debouncing, `useEffect` for async operations.
|
||||
- **State Management:** Zustand store pattern with selectors for optimal re-renders. URL params synced with search state via React Router.
|
||||
- **Error Handling:**
|
||||
- Backend: Node.js error-first callback pattern and try-catch blocks. Scripts validate JSON structure and fail fast on errors.
|
||||
- Frontend: Error boundaries for React components, try-catch for async operations, user-friendly error messages via toast notifications.
|
||||
|
||||
## 5. Key Files & Entrypoints
|
||||
|
||||
- **Main Entrypoints:**
|
||||
- **Backend:** `dedupe-and-sort-meta.js` - Primary script for processing meta.json file.
|
||||
- **Frontend:** `app/src/main.tsx` - React application entry point.
|
||||
- **Configuration:**
|
||||
- `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 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.
|
||||
|
||||
## 6. Development & Testing Workflow
|
||||
|
||||
- **Local Development Environment:**
|
||||
- **Install dependencies**
|
||||
|
||||
```bash
|
||||
# Root project
|
||||
npm install
|
||||
|
||||
# Frontend (uses pnpm)
|
||||
cd app && pnpm install
|
||||
```
|
||||
|
||||
- **Process meta.json** (CRITICAL: Run after ANY meta.json edits)
|
||||
```bash
|
||||
npm run process-meta
|
||||
# or
|
||||
make process-meta
|
||||
# or
|
||||
node dedupe-and-sort-meta.js
|
||||
```
|
||||
- **Validate without modifying**
|
||||
```bash
|
||||
npm run validate-meta
|
||||
# or
|
||||
make validate
|
||||
```
|
||||
- **Quick check for duplicates/sort status**
|
||||
```bash
|
||||
make check
|
||||
```
|
||||
- **Clean backup files**
|
||||
```bash
|
||||
make clean
|
||||
```
|
||||
- **Task Configuration:**
|
||||
- **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 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 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.
|
||||
|
||||
## 7. Specific Instructions for AI Collaboration
|
||||
|
||||
- **Contribution Guidelines:**
|
||||
- Follow existing Dokploy template structure strictly. Each blueprint must be independent with no shared state.
|
||||
- **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 template `id` in meta.json exactly matches blueprint folder name (lowercase kebab-case).
|
||||
- **Docker Compose Conventions (CRITICAL):**
|
||||
|
||||
- **Version:** MUST be `3.8`
|
||||
- **NEVER include:** `ports` (use `expose` only), `container_name`, `networks` (Dokploy handles isolation)
|
||||
- **ALWAYS include:** `restart: unless-stopped` or `restart: always`, persistent volumes
|
||||
- **Service naming:** MUST match blueprint folder name exactly
|
||||
- **Example:**
|
||||
```yaml
|
||||
version: "3.8"
|
||||
services:
|
||||
ghost:
|
||||
image: ghost:6-alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- ghost:/var/lib/ghost/content
|
||||
volumes:
|
||||
ghost:
|
||||
```
|
||||
|
||||
- **template.toml Conventions:**
|
||||
|
||||
- **Variables:** Define in `[variables]` section, use helpers for secrets
|
||||
- **Domains:** `[[config.domains]]` with `serviceName`, `port`, `host` (path optional)
|
||||
- **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}`
|
||||
|
||||
- **meta.json Requirements:**
|
||||
|
||||
- **Required fields:** `id`, `name`, `version`, `description`, `links` (with `github`), `logo`, `tags` (array)
|
||||
- **Tags:** Lowercase strings (e.g., `["monitoring", "database"]`)
|
||||
- **Version:** MUST match Docker image version in docker-compose.yml
|
||||
- **Logo:** Filename only (e.g., `"ghost.jpeg"`), file must exist in blueprint folder
|
||||
|
||||
- **Frontend Development:**
|
||||
|
||||
- Use TypeScript with strict type checking
|
||||
- Follow React hooks patterns, avoid class components
|
||||
- Use Zustand selectors for state access to optimize re-renders
|
||||
- Fuse.js searches across `name`, `description`, `tags`, `id` fields
|
||||
- Use shadcn/ui components for consistency
|
||||
- Sync URL params with search state via React Router
|
||||
|
||||
- **Security:**
|
||||
|
||||
- Be mindful of security when handling template configurations
|
||||
- NEVER hardcode secrets in templates - use Dokploy's variable system with helpers
|
||||
- Pin Docker images to specific versions to avoid supply chain attacks
|
||||
- Validate user input in frontend before processing
|
||||
|
||||
- **Dependencies:**
|
||||
|
||||
- When adding new templates, ensure Docker images are pinned to specific versions
|
||||
- Update meta.json with exact version matching Docker Compose image version
|
||||
- For frontend dependencies: Use `pnpm add <package>` in `app/` directory
|
||||
- For root dependencies: Use `npm install <package>` in root directory
|
||||
|
||||
- **Commit Messages:** Follow conventional commit patterns (e.g., `feat:`, `fix:`, `docs:`, `chore:`).
|
||||
|
||||
- **Common Pitfalls to Avoid:**
|
||||
1. Forgetting to process meta.json after editing (CI will fail)
|
||||
2. Template ID mismatch between meta.json and folder name
|
||||
3. Including `ports`, `container_name`, or `networks` in docker-compose.yml
|
||||
4. Using object syntax for env vars in template.toml (must be array of strings)
|
||||
5. Logo file missing or filename mismatch in meta.json
|
||||
6. Version mismatch between meta.json and docker-compose.yml image tag
|
||||
@@ -1,208 +0,0 @@
|
||||
# Contributing to Dokploy Open Source Templates
|
||||
|
||||
Thank you for your interest in contributing to the Dokploy Open Source Templates repository! This project maintains a collection of Docker Compose templates for easy deployment via Dokploy. Contributions help expand and improve the available templates for the community.
|
||||
|
||||
We welcome contributions in the form of new templates, improvements to existing ones, bug fixes, documentation updates, and more. Please follow the guidelines below to ensure a smooth review and integration process.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project adheres to the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). By participating, you are expected to uphold this code. Reports of unacceptable behavior can be directed to the project maintainers.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
1. **Fork the Repository**: Start by forking this repository to your GitHub account.
|
||||
2. **Create a New Branch**: From your fork, create a feature branch named descriptively (e.g., `add-grafana-template`).
|
||||
3. **Make Your Changes**: Follow the specific guidelines for the type of contribution (e.g., adding a template).
|
||||
4. **Commit and Push**: Commit your changes with clear, concise messages. Push the branch to your fork.
|
||||
5. **Open a Pull Request (PR)**: Create a PR from your branch to the main repository. Reference any related issues in the PR description.
|
||||
6. **Automated Preview**: Every PR automatically deploys a preview of your template to Dokploy. You can test it before merging (see "Testing Your Contribution" below).
|
||||
7. **Review and Merge**: Respond to feedback from maintainers. Once approved, your changes will be merged.
|
||||
|
||||
For larger changes or questions, open an issue first to discuss your ideas.
|
||||
|
||||
## Adding a New Template
|
||||
|
||||
To add a new template, follow these steps:
|
||||
|
||||
1. **Create a Folder in `blueprints/`**: Name it after the template (e.g., `grafana`). Use lowercase with hyphens for multi-word names.
|
||||
2. **Add `docker-compose.yml`**: Define the services, volumes, and other Compose configurations. Key guidelines:
|
||||
|
||||
- Use `version: "3.8"` or later.
|
||||
- Avoid exposing ports explicitly (e.g., no `ports: - "3000:3000"`; just `- 3000` if needed for internal reference).
|
||||
- Do not use `container_name`.
|
||||
- Avoid custom networks like `dokploy-network`; Dokploy handles isolation automatically.
|
||||
- Example for Grafana:
|
||||
```yaml
|
||||
version: "3.8"
|
||||
services:
|
||||
grafana:
|
||||
image: grafana/grafana-enterprise:9.5.20
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- grafana-storage:/var/lib/grafana
|
||||
volumes:
|
||||
grafana-storage: {}
|
||||
```
|
||||
|
||||
3. **Add `template.toml`**: Configure domains, environment variables, mounts, and variables. This file is crucial for Dokploy integration.
|
||||
|
||||
- Sections: `[variables]` (optional), `[config]` with `[[config.domains]]`, `[[config.env]]`, `[[config.mounts]]`.
|
||||
- Use `${variable}` syntax to reference variables or helpers (e.g., `${domain}`, `${password:32}`).
|
||||
- Match `serviceName` in domains to the service name in `docker-compose.yml`.
|
||||
- Example for Grafana:
|
||||
|
||||
```toml
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "grafana"
|
||||
port = 3000
|
||||
host = "${main_domain}"
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "/content/file.txt"
|
||||
content = """
|
||||
My content
|
||||
"""
|
||||
```
|
||||
|
||||
4. **Update `meta.json`**: Add an entry for your template in the root `meta.json`. Include:
|
||||
|
||||
- `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).
|
||||
- `tags`: Array of categories (e.g., ["monitoring"]).
|
||||
- Example:
|
||||
```json
|
||||
{
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "9.5.20",
|
||||
"description": "Grafana is an open source platform for data visualization and monitoring.",
|
||||
"logo": "grafana.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/grafana/grafana",
|
||||
"website": "https://grafana.com/",
|
||||
"docs": "https://grafana.com/docs/"
|
||||
},
|
||||
"tags": ["monitoring"]
|
||||
}
|
||||
```
|
||||
|
||||
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 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.
|
||||
- The preview URL (auto-generated).
|
||||
- Any testing notes.
|
||||
|
||||
## Template.toml Structure
|
||||
|
||||
- **[variables]**: Define reusable values or use helpers.
|
||||
Example:
|
||||
|
||||
```toml
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
my_password = "${password:32}"
|
||||
```
|
||||
|
||||
- **[config.domains]**: Map services to domains/ports.
|
||||
|
||||
- Required: `serviceName`, `port`, `host`.
|
||||
- Optional: `path`.
|
||||
|
||||
- **[config.env]**: Array of environment variables (strings).
|
||||
Example: `env = ["GF_SECURITY_ADMIN_PASSWORD=${password:32}"]`
|
||||
|
||||
- **[config.mounts]**: Inline files or configs.
|
||||
- `filePath`: Destination in container.
|
||||
- `content`: Multi-line string.
|
||||
|
||||
### Helpers
|
||||
|
||||
Use these in `${}` for dynamic values:
|
||||
|
||||
- `${domain}`: Random subdomain.
|
||||
- `${password:length}`: Random password (default 32 chars).
|
||||
- `${base64:length}`: Base64-encoded random string.
|
||||
- `${hash:length}`: Random hash.
|
||||
- `${uuid}`: UUID.
|
||||
- `${randomPort}`: Random port.
|
||||
- `${email}`: Random email.
|
||||
- `${username}`: Random lowercase username.
|
||||
- `${timestamp}`: Current timestamp (ms).
|
||||
- `${timestamps:datetime}` or `${timestampms:datetime}`: Timestamp at specific date (e.g., `${timestamps:2030-01-01T00:00:00Z}`).
|
||||
- `${jwt:secret_var:payload_var}`: JWT token (advanced; see README for details).
|
||||
|
||||
## Best Practices and Suggestions
|
||||
|
||||
- **Docker Compose**:
|
||||
|
||||
- Omit explicit ports; let Dokploy handle exposure.
|
||||
- Use persistent volumes for data (e.g., databases).
|
||||
|
||||
- **Template.toml**:
|
||||
|
||||
- Keep service names consistent across files.
|
||||
- Use variables/helpers for secrets (e.g., passwords, API keys).
|
||||
- Minimize env vars; only include essentials.
|
||||
|
||||
- **General**:
|
||||
|
||||
- Templates should be self-contained and deployable out-of-the-box.
|
||||
- Use official/latest stable images.
|
||||
- Avoid hardcoding domains, networks, or secrets.
|
||||
- Ensure compatibility with Dokploy's isolated deployments.
|
||||
|
||||
- **Logo and Metadata**:
|
||||
- Logos should be vector (SVG preferred) or high-res PNG.
|
||||
- Descriptions: 1-2 sentences, SEO-friendly.
|
||||
- Tags: Relevant categories (e.g., "database", "web", "monitoring").
|
||||
|
||||
## Testing Your Contribution
|
||||
|
||||
Before submitting a PR:
|
||||
|
||||
1. Run the project locally:
|
||||
```bash
|
||||
cd app
|
||||
pnpm install
|
||||
pnpm run dev
|
||||
# Visit http://localhost:5173/
|
||||
```
|
||||
2. Use the PR preview:
|
||||
- After pushing, check the PR for the auto-generated preview URL.
|
||||
- Search for your template, copy the Base64 config.
|
||||
- In your Dokploy instance: Create a Compose service > Advanced > Import > Paste Base64 > Deploy.
|
||||
- Verify the service starts and is accessible (e.g., via assigned domain).
|
||||
3. Test edge cases: Restart, volume persistence, env vars.
|
||||
|
||||
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 `meta.json` and update `docker-compose.yml` image tags.
|
||||
- Test thoroughly, as changes may affect users.
|
||||
|
||||
## Reporting Bugs or Requesting Features
|
||||
|
||||
- Open an issue with:
|
||||
- Clear title and description.
|
||||
- Steps to reproduce (for bugs).
|
||||
- Template details (for feature requests).
|
||||
- Use the issue templates in `.github/ISSUE_TEMPLATE/`.
|
||||
|
||||
## Questions?
|
||||
|
||||
If you're unsure about anything, open a discussion or issue. We're here to help!
|
||||
|
||||
Happy contributing! 🚀
|
||||
@@ -7,14 +7,14 @@ 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`)
|
||||
3. Add the template to the `blueprints` folder (docker-compose.yml, template.yml)
|
||||
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)
|
||||
8. Every PR will automatically deploy a preview of the template to Dokploy.
|
||||
9. if anyone want to test the template before merging it, you can enter to the preview URL in the PR description, and search the template, click on the Template Card, scroll down and then copy the BASE64 value, and paste in the advanced section of your compose service, in the Import section or optional you can use the preview URL and paste in the
|
||||
BASE URL when creating a template.
|
||||
BASE URL when creating a template.
|
||||
|
||||
#### Optional
|
||||
|
||||
@@ -45,24 +45,21 @@ services:
|
||||
volumes:
|
||||
grafana-storage: {}
|
||||
```
|
||||
3. Add the `template.toml` file to the folder, this is where we specify the domains, mounts and env variables, to understand more the structure of `template.toml` you can read here [Template.toml structure](#template.toml-structure)
|
||||
3. Add the `template.yml` file to the folder, this is where we specify the domains, mounts and env variables, to understand more the structure of `template.yml` you can read here [Template.yml structure](#templateyml-structure)
|
||||
|
||||
```toml
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
```yaml
|
||||
variables:
|
||||
main_domain: ${domain}
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "grafana"
|
||||
port = 3000
|
||||
host = "${main_domain}"
|
||||
|
||||
|
||||
[config.env]
|
||||
|
||||
[[config.mounts]]
|
||||
config:
|
||||
domains:
|
||||
- serviceName: grafana
|
||||
port: 3000
|
||||
host: ${main_domain}
|
||||
env: []
|
||||
mounts: []
|
||||
```
|
||||
4. Add meta information to the `meta.json` file in the root folder
|
||||
4. Add the `meta.json` file to the folder
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -85,144 +82,67 @@ host = "${main_domain}"
|
||||
6. Commit and push your changes
|
||||
7. Create a pull request
|
||||
|
||||
### Template.toml structure
|
||||
### Template.yml structure
|
||||
|
||||
Dokploy use a defined structure for the `template.yml` file, we have 4 sections available:
|
||||
|
||||
Dokploy use a defined structure for the `template.toml` file, we have 4 sections available:
|
||||
|
||||
1. `variables`: This is where we define the variables that will be used in the `domains`, `env` and `mounts` sections.
|
||||
2. `domains`: This is where we define the configuration for the template.
|
||||
3. `env`: This is where we define the environment variables for the template.
|
||||
4. `mounts`: This is where we define the mounts for the template.
|
||||
|
||||
|
||||
- The `variables(Optional)` structure is the following:
|
||||
|
||||
```toml
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
my_domain = "https://my-domain.com"
|
||||
my_password = "${password:32}"
|
||||
any_helper = "${you-can-use-any-helper}"
|
||||
```yaml
|
||||
variables:
|
||||
main_domain: ${domain}
|
||||
my_domain: https://my-domain.com
|
||||
my_password: ${password:32}
|
||||
any_helper: ${you-can-use-any-helper}
|
||||
```
|
||||
|
||||
- The `config` structure is the following:
|
||||
|
||||
```toml
|
||||
[config]
|
||||
# Optional sections below
|
||||
```yaml
|
||||
config:
|
||||
domains: # Optional
|
||||
- serviceName: grafana # Required
|
||||
port: 3000 # Required
|
||||
host: ${main_domain} # Required
|
||||
path: / # Optional
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "grafana" # Required
|
||||
port = 3000 # Required
|
||||
host = "${main_domain}" # Required
|
||||
path = "/" # Optional
|
||||
env: # Optional
|
||||
- AP_HOST=${main_domain}
|
||||
- AP_API_KEY=${api_key}
|
||||
- AP_ENCRYPTION_KEY=${encryption_key}
|
||||
- AP_JWT_SECRET=${jwt_secret}
|
||||
- AP_POSTGRES_PASSWORD=${postgres_password}
|
||||
|
||||
env = [
|
||||
"AP_HOST=${main_domain}",
|
||||
"AP_API_KEY=${api_key}",
|
||||
"AP_ENCRYPTION_KEY=${encryption_key}",
|
||||
"AP_JWT_SECRET=${jwt_secret}",
|
||||
"AP_POSTGRES_PASSWORD=${postgres_password}"
|
||||
]
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "/content/file.txt"
|
||||
content = """
|
||||
My content
|
||||
"""
|
||||
mounts: # Optional or []
|
||||
- filePath: /content/file.txt
|
||||
content: |
|
||||
My content
|
||||
```
|
||||
|
||||
Important: you can reference any variable in the `domains`, `env` and `mounts` sections. just use the `${variable_name}` syntax, in the case you don't want to define a variable, you can use the `domain`, `base64`, `password`, `hash`, `uuid`, `randomPort`, `timestamp`, `jwt`, `email`, or `username` helpers.
|
||||
Important: you can reference any variable in the `domains`, `env` and `mounts` sections. just use the `${variable_name}` syntax, in the case you don't want to define a variable, you can use the `domain`, `base64`, `password`, `hash`, `uuid`, `randomPort` or `timestamp` helpers.
|
||||
|
||||
### Helpers
|
||||
|
||||
We have a few helpers that are very common when creating a template, these are:
|
||||
|
||||
- `domain`: This is a helper that will generate a random domain for the template.
|
||||
- `base64 or base64:length`: This is a helper that will encode a string to base64 (lenght is the number of bytes to encode not the encoded string length).
|
||||
- `base64 or base64:length`: This is a helper that will encode a string to base64.
|
||||
- `password or password:length`: This is a helper that will generate a random password for the template.
|
||||
- `hash or hash:length`: This is a helper that will generate a hash for the template
|
||||
- `hash or hash:length`: This is a helper that will generate a hash for the template.
|
||||
- `uuid`: This is a helper that will generate a uuid for the template.
|
||||
- `randomPort`: This is a helper that will generate a random port for the template.
|
||||
- `email`: This is a helper that will generate a random email for the template.
|
||||
- `username`: This is a helper that will generate a random username in lowercase for the template.
|
||||
- `timestamp`: This is a helper that will generate a timestamp for "now" in milli-second.
|
||||
- `timestampms or timestampms:datetime`: This is a helper that will generate a timestamp in milli-seconds.
|
||||
- `timestamps or timestamps:datetime`: This is a helper that will generate a timestamp in seconds.
|
||||
- `datetime` parameter for `timestamps/timestampms` helpers must be a valid value for javascript new Date() (ie: `timestamps:2030-01-01T00:00:00Z`)
|
||||
- `jwt`: This is a helper that will generate a jwt for the template.
|
||||
- `jwt:length`: will generate a random hex string of bytes length. _This should not be used in newer templates_
|
||||
- `jwt:secret_var_name`: will generate a jwt with some default values, secret var name should be the name of the variable holding the secret
|
||||
- `jwt:secret_var_name:payload_var_name`: is the same as above but you can pass partial or full payload for the jwt.
|
||||
Here's a full example
|
||||
```toml
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
mysecret = "cQsdycq1hDLopQonF6jUTqgQc5WEZTwWLL02J6XJ"
|
||||
mypayload = """
|
||||
{
|
||||
"role": "jwt-tester",
|
||||
"iss": "dokploy-templates",
|
||||
"exp": ${timestamps:2030-01-01T00:00:00Z}
|
||||
}
|
||||
"""
|
||||
jwt = "${jwt:mysecret:mypayload}"
|
||||
```
|
||||
- `timestamp`: This is a helper that will generate a timestamp.
|
||||
- `jwt or jwt:length`: This is a helper that will generate a jwt for the template.
|
||||
|
||||
|
||||
|
||||
## General Requirements when creating a template
|
||||
|
||||
- Don't use this way in your docker compose file:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
grafana:
|
||||
image: grafana/grafana-enterprise:9.5.20
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3000:3000
|
||||
|
||||
# Instead use this way:
|
||||
ports:
|
||||
- 3000
|
||||
```
|
||||
|
||||
- Don't use this way in your template.toml file, make sure to use the same service name as the one in the docker compose file:
|
||||
|
||||
```toml
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "MyGrafanaService"
|
||||
# Instead use this way:
|
||||
serviceName = "grafana" # Make sure to use the same service name as the one in the docker compose file
|
||||
```
|
||||
|
||||
- Don't use container_name in your docker compose file, make sure to use the same service name as the one in the template.toml file:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
grafana:
|
||||
container_name: grafana # ❌ Remove this
|
||||
```
|
||||
|
||||
- Don't use dokploy-network in your docker compose file, by default all the templates have this flag enabled https://docs.dokploy.com/docs/core/docker-compose/utilities#isolated-deployments, so by default they have a internal network created, so you don't need to create a new one or use the dokploy-network name.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
grafana:
|
||||
networks:
|
||||
- dokploy-network # ❌ Remove this or any other network defined
|
||||
```
|
||||
|
||||
|
||||
- Please before submit a PR, make sure to test the template in your instance, so the maintainers don't spend time trying to figure out what's wrong.
|
||||
|
||||
1. Everytime you submit a PR, it will display a Preview Link.
|
||||
2. Enter to the Preview Link and search the template you've submitted.
|
||||
3. Click on the Template Card, and click the Copy Button in the Base64 Configuration.
|
||||
4. Go to your instance, create a new Compose Service, go to Advanced Section -> Scroll Down -> Import Section -> Paste the Base64 Value -> Click on the Import Button
|
||||
5. If everything is correct and set, you should see a modal with all the details (Compose File, Environment Variables, Mounts, Domains, etc)
|
||||
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.
|
||||
|
||||
|
||||
use the command `node dedupe-and-sort-meta.js` to deduplicate and sort the meta.json file.
|
||||
|
||||
@@ -10,46 +10,43 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.19.1",
|
||||
"@codemirror/lang-json": "^6.0.2",
|
||||
"@codemirror/lang-yaml": "^6.1.2",
|
||||
"@codemirror/language": "^6.11.3",
|
||||
"@codemirror/autocomplete": "^6.18.6",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-yaml": "^6.1.1",
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@codemirror/legacy-modes": "6.4.0",
|
||||
"@codemirror/view": "6.29.0",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
"@uiw/codemirror-theme-github": "^4.25.2",
|
||||
"@uiw/react-codemirror": "^4.25.2",
|
||||
"@radix-ui/react-dialog": "^1.1.6",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
||||
"@radix-ui/react-label": "^2.1.2",
|
||||
"@radix-ui/react-popover": "^1.1.6",
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@tailwindcss/vite": "^4.0.12",
|
||||
"@uiw/codemirror-theme-github": "^4.22.1",
|
||||
"@uiw/react-codemirror": "^4.22.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "1.0.0",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"lucide-react": "^0.479.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"next-themes": "^0.4.5",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"sonner": "^2.0.1",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwindcss": "^4.0.12",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vite-plugin-static-copy": "^2.3.2",
|
||||
"yaml": "2.7.1",
|
||||
"zustand": "^5.0.8"
|
||||
"vite-plugin-static-copy": "2.3.0",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.23",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"@types/node": "^20.8.2",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"globals": "^15.15.0",
|
||||
"typescript": "~5.7.3",
|
||||
"vite": "^6.4.1"
|
||||
"typescript": "~5.7.2",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../blueprints
|
||||
@@ -1,36 +0,0 @@
|
||||
import yaml from "yaml";
|
||||
import toml from "@iarna/toml";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
function convertYamlToToml(yamlContent) {
|
||||
const parsedYaml = yaml.parse(yamlContent);
|
||||
return toml.stringify(parsedYaml);
|
||||
}
|
||||
|
||||
function processDirectory(dirPath) {
|
||||
const files = fs.readdirSync(dirPath);
|
||||
|
||||
files.forEach((file) => {
|
||||
const filePath = path.join(dirPath, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
processDirectory(filePath);
|
||||
} else if (file === "template.yml") {
|
||||
console.log(`Converting ${filePath}`);
|
||||
const yamlContent = fs.readFileSync(filePath, "utf8");
|
||||
const tomlContent = convertYamlToToml(yamlContent);
|
||||
const tomlPath = path.join(dirPath, "template.toml");
|
||||
fs.writeFileSync(tomlPath, tomlContent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Ruta al directorio blueprints relativa al script
|
||||
const blueprintsPath = path.join(__dirname, "..", "blueprints");
|
||||
processDirectory(blueprintsPath);
|
||||
@@ -3,19 +3,16 @@ import Navigation from "./components/Navigation";
|
||||
import Search from "./components/Search";
|
||||
import { useStore } from "@/store";
|
||||
import "./App.css";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
function App() {
|
||||
const view = useStore((state) => state.view);
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<div className="min-h-screen">
|
||||
<Navigation />
|
||||
<Search />
|
||||
<TemplateGrid view={view} />
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
<div className="min-h-screen">
|
||||
<Navigation />
|
||||
<Search />
|
||||
<TemplateGrid view={view} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const Navigation = () => {
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.open(
|
||||
"https://github.com/Dokploy/templates/issues/new",
|
||||
"https://github.com/Dokploy/dokploy/discussions/new?category=ideas",
|
||||
"_blank"
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -16,10 +16,9 @@ import { Check, ChevronsUpDown } from "lucide-react";
|
||||
import React from "react";
|
||||
import { Tabs, TabsList, TabsTrigger } from "./ui/tabs";
|
||||
import SelectedTags from "./SelectedTags";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
const Search = () => {
|
||||
const { templates, searchQuery, setSearchQuery, setView, templatesCount, setFilteredTemplates, setTemplatesCount } =
|
||||
const { templates, searchQuery, setSearchQuery, setView, templatesCount } =
|
||||
useStore();
|
||||
const selectedTags = useStore((state) => state.selectedTags);
|
||||
const addSelectedTag = useStore((state) => state.addSelectedTag);
|
||||
@@ -27,7 +26,6 @@ const Search = () => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [tagSearch, setTagSearch] = React.useState("");
|
||||
const view = useStore((state) => state.view);
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
// Get all unique tags, safely handle empty templates
|
||||
const uniqueTags = React.useMemo(() => {
|
||||
@@ -45,54 +43,6 @@ const Search = () => {
|
||||
);
|
||||
}, [uniqueTags, tagSearch]);
|
||||
|
||||
// Initialize search query from URL params and apply filters
|
||||
React.useEffect(() => {
|
||||
const queryFromUrl = searchParams.get("q") || "";
|
||||
if (queryFromUrl !== searchQuery) {
|
||||
setSearchQuery(queryFromUrl);
|
||||
}
|
||||
|
||||
// Apply filters whenever templates, search query or selected tags change
|
||||
if (templates) {
|
||||
const filtered = templates.filter((template) => {
|
||||
// Filter by search query - search across name and description
|
||||
const searchTerm = queryFromUrl.toLowerCase();
|
||||
const matchesSearch =
|
||||
template.name.toLowerCase().includes(searchTerm) ||
|
||||
template.description.toLowerCase().includes(searchTerm);
|
||||
|
||||
// Filter by selected tags
|
||||
const matchesTags =
|
||||
selectedTags.length === 0 ||
|
||||
selectedTags.every((tag) => template.tags.includes(tag));
|
||||
|
||||
return matchesSearch && matchesTags;
|
||||
});
|
||||
|
||||
setFilteredTemplates(filtered);
|
||||
setTemplatesCount(filtered.length);
|
||||
}
|
||||
}, [searchParams, templates, selectedTags, setSearchQuery, setFilteredTemplates, setTemplatesCount]);
|
||||
|
||||
// Update URL params when search query changes
|
||||
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newQuery = e.target.value;
|
||||
setSearchQuery(newQuery);
|
||||
if (newQuery) {
|
||||
setSearchParams({ q: newQuery });
|
||||
} else {
|
||||
searchParams.delete("q");
|
||||
setSearchParams(searchParams);
|
||||
}
|
||||
};
|
||||
|
||||
// Clear search and URL params
|
||||
const handleClearSearch = () => {
|
||||
setSearchQuery("");
|
||||
searchParams.delete("q");
|
||||
setSearchParams(searchParams);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className=" mx-auto p-4 lg:p-12 border-b w-full">
|
||||
{/* <h1 className="text-2xl md:text-3xl xl:text-4xl font-bold text-center mb-8">
|
||||
@@ -113,11 +63,11 @@ const Search = () => {
|
||||
type="text"
|
||||
placeholder="Search templates..."
|
||||
value={searchQuery}
|
||||
onChange={handleSearchChange}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full p-6"
|
||||
/>
|
||||
{searchQuery.length > 0 ? (
|
||||
<div className="cursor-pointer" onClick={handleClearSearch}>
|
||||
<div className="cursor-pointer" onClick={() => setSearchQuery("")}>
|
||||
<XIcon className="absolute end-3 translate-y-3.5 top-1/2 h-5 w-5 text-gray-400" />
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -55,9 +55,7 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
|
||||
config: templateFiles.config || "",
|
||||
};
|
||||
|
||||
// Use encodeURIComponent to handle Unicode characters properly
|
||||
const jsonString = JSON.stringify(configObj, null, 2);
|
||||
return btoa(unescape(encodeURIComponent(jsonString)));
|
||||
return btoa(JSON.stringify(configObj, null, 2));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -230,7 +228,7 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
|
||||
<div className="relative w-full rounded-md overflow-hidden border">
|
||||
<CodeEditor
|
||||
value={templateFiles.config || ""}
|
||||
language="toml"
|
||||
language="yaml"
|
||||
className="font-mono w-full [&_*]:!break-words"
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -82,7 +82,7 @@ const TemplateGrid: React.FC<TemplateGridProps> = ({ view }) => {
|
||||
try {
|
||||
const [dockerComposeRes, configRes] = await Promise.all([
|
||||
fetch(`/blueprints/${templateId}/docker-compose.yml`),
|
||||
fetch(`/blueprints/${templateId}/template.toml`),
|
||||
fetch(`/blueprints/${templateId}/template.yml`),
|
||||
]);
|
||||
|
||||
const dockerCompose = dockerComposeRes.ok
|
||||
@@ -120,6 +120,7 @@ const TemplateGrid: React.FC<TemplateGridProps> = ({ view }) => {
|
||||
return matchesSearch && matchesTags;
|
||||
});
|
||||
|
||||
console.log("ffiltered tem", filtered.length);
|
||||
setTemplatesCount(filtered.length);
|
||||
setFilteredTemplates(filtered);
|
||||
}, [searchQuery, selectedTags]);
|
||||
|
||||
@@ -121,7 +121,7 @@ function dockerComposeComplete(
|
||||
interface Props extends ReactCodeMirrorProps {
|
||||
wrapperClassName?: string;
|
||||
disabled?: boolean;
|
||||
language?: "yaml" | "json" | "properties" | "shell" | "toml";
|
||||
language?: "yaml" | "json" | "properties" | "shell";
|
||||
lineWrapping?: boolean;
|
||||
lineNumbers?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
ackee:
|
||||
image: electerious/ackee:3.4.2
|
||||
ports:
|
||||
- "3000"
|
||||
environment:
|
||||
- ACKEE_USERNAME=${ACKEE_USERNAME}
|
||||
- ACKEE_PASSWORD=${ACKEE_PASSWORD}
|
||||
- ACKEE_MONGODB=${ACKEE_MONGODB}
|
||||
|
||||
mongo:
|
||||
image: mongo:4
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
|
||||
- MONGO_INITDB_ROOT_USERNAME=mongo
|
||||
volumes:
|
||||
- mongo-data:/data/db
|
||||
|
||||
|
||||
volumes:
|
||||
mongo-data:
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB |
@@ -1,19 +0,0 @@
|
||||
[variables]
|
||||
ACKEE_USERNAME = "default"
|
||||
ACKEE_PASSWORD = "${password:16}"
|
||||
MONGO_INITDB_ROOT_PASSWORD = "${password:16}"
|
||||
ACKEE_MONGODB = "mongodb://mongo:${MONGO_INITDB_ROOT_PASSWORD}@mongo:27017"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "ackee"
|
||||
port = 3000
|
||||
host = "${domain}"
|
||||
|
||||
|
||||
[config.env]
|
||||
ACKEE_USERNAME = "${ACKEE_USERNAME}"
|
||||
ACKEE_PASSWORD = "${ACKEE_PASSWORD}"
|
||||
ACKEE_MONGODB = "${ACKEE_MONGODB}"
|
||||
MONGO_INITDB_ROOT_PASSWORD = "${MONGO_INITDB_ROOT_PASSWORD}"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
api_key = "${password:32}"
|
||||
encryption_key = "${password:32}"
|
||||
jwt_secret = "${password:32}"
|
||||
postgres_password = "${password:32}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"AP_HOST=${main_domain}",
|
||||
"AP_API_KEY=${api_key}",
|
||||
"AP_ENCRYPTION_KEY=${encryption_key}",
|
||||
"AP_JWT_SECRET=${jwt_secret}",
|
||||
"AP_POSTGRES_PASSWORD=${postgres_password}",
|
||||
]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "activepieces"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,21 @@
|
||||
variables:
|
||||
main_domain: ${domain}
|
||||
api_key: ${password:32}
|
||||
encryption_key: ${password:32}
|
||||
jwt_secret: ${password:32}
|
||||
postgres_password: ${password:32}
|
||||
|
||||
config:
|
||||
domains:
|
||||
- serviceName: activepieces
|
||||
port: 80
|
||||
host: ${main_domain}
|
||||
|
||||
env:
|
||||
- AP_HOST=${main_domain}
|
||||
- AP_API_KEY=${api_key}
|
||||
- AP_ENCRYPTION_KEY=${encryption_key}
|
||||
- AP_JWT_SECRET=${jwt_secret}
|
||||
- AP_POSTGRES_PASSWORD=${postgres_password}
|
||||
|
||||
mounts: []
|
||||
@@ -1,11 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
env = []
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "actualbudget"
|
||||
port = 5_006
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,12 @@
|
||||
variables:
|
||||
main_domain: ${domain}
|
||||
|
||||
config:
|
||||
domains:
|
||||
- serviceName: actualbudget
|
||||
port: 5006
|
||||
host: ${main_domain}
|
||||
|
||||
env: []
|
||||
|
||||
mounts: []
|
||||
@@ -1,20 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
adguardhome:
|
||||
image: adguard/adguardhome:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "53:53/tcp" # DNS
|
||||
- "53:53/udp" # DNS
|
||||
- "67:67/udp" # DHCP Server
|
||||
- "68:68/tcp" # DHCP Client
|
||||
- "853:853/tcp" # DNS over TLS, DNS-over-QUIC
|
||||
- "853:853/udp" # DNS over TLS, DNS-over-QUIC
|
||||
- "6060:6060/tcp" # HTTP (pprof)
|
||||
volumes:
|
||||
- adguardhome-work:/opt/adguardhome/work
|
||||
- adguardhome-conf:/opt/adguardhome/conf
|
||||
|
||||
volumes:
|
||||
adguardhome-work: {}
|
||||
adguardhome-conf: {}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="80px" height="80px" viewBox="0 0 80 80" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>logo@2x</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="logo" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<rect id="Rectangle" fill="#FFFFFF" x="0" y="0" width="80" height="80" rx="20"></rect>
|
||||
<g id="Group-10" transform="translate(12.000000, 12.000000)">
|
||||
<g id="Group-9">
|
||||
<g id="Group-8">
|
||||
<g id="Group-7">
|
||||
<path d="M28.2221276,0 C19.4007575,0 8.75997994,2.07442553 8.65485005e-06,6.64038298 C8.65485005e-06,16.501617 -0.120909272,41.0689362 28.2221276,57.855 C56.5657909,41.0689362 56.4454995,16.501617 56.4454995,6.64038298 C47.6849017,2.07442553 37.0441241,0 28.2221276,0 L28.2221276,0 Z" id="Path" fill="#68BC71"></path>
|
||||
<path d="M28.1932991,57.8379179 C-0.120827266,41.0522735 8.65485006e-06,16.4982725 8.65485006e-06,6.64038298 C8.75043947,2.07939831 19.3775821,0.00452145957 28.1932991,7.38217799e-06 L28.1932991,57.8379237 Z" id="Combined-Shape" fill="#67B279"></path>
|
||||
</g>
|
||||
<path d="M27.1926958,38.6027397 L44.2590846,15.6010416 C43.0084943,14.5986526 41.911548,15.3061181 41.3076915,15.8538333 L41.2856573,15.8555888 L27.0557264,30.6585285 L21.6942672,24.2064902 C19.1365123,21.2514028 15.6592758,23.5054616 14.8469876,24.1011604 L27.1926958,38.6027397" id="Fill-11" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,12 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
env = []
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "adguardhome"
|
||||
port = 3000
|
||||
host = "${main_domain}"
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
adminer:
|
||||
image: adminer:4.8.1
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8080
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="791px"
|
||||
height="792px" viewBox="-91.5 -0.5 791 792" enable-background="new -91.5 -0.5 791 792" xml:space="preserve">
|
||||
<g id="Layer_2">
|
||||
<rect x="-91.5" y="-0.5" fill="#005982" width="791" height="792"/>
|
||||
<rect x="-90.5" y="-0.5" fill="none" width="790" height="792"/>
|
||||
</g>
|
||||
<g id="Layer_1">
|
||||
<path fill="#FFFFFF" d="M336.162,343.266c-7.003-27.503-58.849-54.543-149.432-54.543c-90.517,0-142.358,27.004-149.416,54.488
|
||||
c-0.543,1.058-0.875,2.241-0.875,3.513v249.82c0,2.393,1.101,4.505,2.794,5.937c11.992,25.549,62.822,49.176,147.496,49.176
|
||||
c84.697,0,135.532-23.639,147.507-49.199c1.679-1.428,2.768-3.531,2.768-5.913v-5.691c0-0.047,0.008-0.09,0.008-0.137
|
||||
c0-0.027-0.008-0.05-0.008-0.077v-81.224c0-0.046,0.008-0.09,0.008-0.136c0-0.028-0.008-0.051-0.008-0.078V429.61
|
||||
c0-0.048,0.008-0.09,0.008-0.137c0-0.027-0.008-0.052-0.008-0.079v-79.59c0-0.047,0.008-0.09,0.008-0.137s-0.008-0.094-0.008-0.141
|
||||
v-2.802C337.005,345.475,336.685,344.312,336.162,343.266z M321.392,509.372c-0.165,21.415-55.417,45.233-134.662,45.233
|
||||
c-79.36,0-134.668-23.889-134.668-45.33c0-0.027-0.008-0.052-0.008-0.079v-52.169c22.261,18.717,67.967,33.387,134.676,33.387
|
||||
c66.698,0,112.392-14.665,134.662-33.375V509.372z M321.392,429.567C321.227,450.98,265.974,474.8,186.73,474.8
|
||||
c-79.36,0-134.668-23.889-134.668-45.331c0-0.026-0.008-0.051-0.008-0.078v-52.169c22.261,18.716,67.967,33.386,134.676,33.386
|
||||
c66.698,0,112.392-14.661,134.662-33.374V429.567z M186.73,304.337c79.244,0,134.497,23.818,134.662,45.233v0.191
|
||||
c-0.165,21.415-55.417,45.233-134.662,45.233c-79.36,0-134.668-23.889-134.668-45.327
|
||||
C52.062,328.229,107.37,304.337,186.73,304.337z M186.73,636.043c-79.36,0-134.668-23.89-134.668-45.327
|
||||
c0-0.027-0.008-0.05-0.008-0.077v-53.802c22.261,18.717,67.967,33.387,134.676,33.387c66.698,0,112.392-14.665,134.662-33.375
|
||||
v53.961C321.227,612.228,265.974,636.043,186.73,636.043z"/>
|
||||
<g>
|
||||
<path fill="#FF0000" d="M427.38,365.951l-71.116-59.932l71.116-59.827l16.46,16.671l-52.335,42.944l52.335,43.472L427.38,365.951z
|
||||
"/>
|
||||
<path fill="#FF0000" d="M566.131,260.227c0,5.909-0.897,11.203-2.691,15.88c-1.793,4.679-4.362,8.723-7.702,12.134
|
||||
c-3.342,3.413-7.369,6.173-12.081,8.283c-4.714,2.11-9.989,3.553-15.827,4.326l-1.056,19.837h-22.052l-1.899-39.673h15.3
|
||||
c3.445,0,6.418-0.491,8.915-1.477c2.496-0.984,4.537-2.322,6.12-4.01s2.743-3.64,3.482-5.856c0.738-2.216,1.107-4.554,1.107-7.017
|
||||
c0-3.376-0.722-6.524-2.163-9.444c-1.442-2.918-3.692-5.486-6.753-7.702c-3.06-2.216-6.947-3.957-11.659-5.223
|
||||
c-4.713-1.266-10.377-1.899-16.987-1.899h-6.226v-23.952h7.28c10.832,0,20.293,1.284,28.384,3.851
|
||||
c8.088,2.568,14.841,5.979,20.258,10.235c5.416,4.257,9.479,9.127,12.188,14.614C564.775,248.62,566.131,254.317,566.131,260.227z
|
||||
M533.21,349.069c0,2.321-0.458,4.502-1.371,6.542c-0.915,2.041-2.147,3.835-3.693,5.381c-1.548,1.548-3.377,2.76-5.486,3.64
|
||||
c-2.11,0.879-4.363,1.319-6.753,1.319c-2.393,0-4.627-0.44-6.7-1.319c-2.076-0.88-3.888-2.092-5.435-3.64
|
||||
c-1.548-1.546-2.779-3.34-3.692-5.381c-0.915-2.04-1.372-4.221-1.372-6.542s0.457-4.5,1.372-6.542
|
||||
c0.913-2.04,2.145-3.833,3.692-5.381c1.547-1.546,3.358-2.76,5.435-3.64c2.073-0.879,4.308-1.319,6.7-1.319
|
||||
c2.39,0,4.643,0.44,6.753,1.319c2.109,0.88,3.938,2.094,5.486,3.64c1.546,1.548,2.778,3.342,3.693,5.381
|
||||
C532.752,344.568,533.21,346.748,533.21,349.069z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -1,8 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "adminer"
|
||||
port = 8080
|
||||
host = "${main_domain}"
|
||||
|
Before Width: | Height: | Size: 7.9 KiB |
@@ -1,51 +0,0 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
web:
|
||||
image: ghcr.io/seanmorley15/adventurelog-frontend:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- server
|
||||
environment:
|
||||
# Frontend needs to know where the backend is
|
||||
PUBLIC_SERVER_URL: ${PUBLIC_SERVER_URL}
|
||||
ORIGIN: ${ORIGIN}
|
||||
BODY_SIZE_LIMIT: ${BODY_SIZE_LIMIT}
|
||||
|
||||
db:
|
||||
image: postgis/postgis:16-3.5
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data/
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
|
||||
server:
|
||||
image: ghcr.io/seanmorley15/adventurelog-backend:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
volumes:
|
||||
- adventurelog_media:/code/media/
|
||||
environment:
|
||||
# DB settings for Django
|
||||
PGHOST: ${PGHOST}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
|
||||
# Django app settings
|
||||
SECRET_KEY: ${SECRET_KEY}
|
||||
DJANGO_ADMIN_USERNAME: ${DJANGO_ADMIN_USERNAME}
|
||||
DJANGO_ADMIN_PASSWORD: ${DJANGO_ADMIN_PASSWORD}
|
||||
DJANGO_ADMIN_EMAIL: ${DJANGO_ADMIN_EMAIL}
|
||||
DEBUG: ${DEBUG}
|
||||
CSRF_TRUSTED_ORIGINS: ${CSRF_TRUSTED_ORIGINS}
|
||||
FRONTEND_URL: ${FRONTEND_URL}
|
||||
PUBLIC_URL: ${PUBLIC_URL}
|
||||
|
||||
volumes:
|
||||
postgres_data: {}
|
||||
adventurelog_media: {}
|
||||
@@ -1,44 +0,0 @@
|
||||
[variables]
|
||||
frontend_domain = "${domain}"
|
||||
backend_domain = "${domain}"
|
||||
postgres_password = "${password:16}"
|
||||
secret_key = "${password:32}"
|
||||
admin_username = "admin"
|
||||
admin_password = "${password:12}"
|
||||
admin_email = "${email}"
|
||||
|
||||
[config]
|
||||
|
||||
# Frontend domain
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 3000
|
||||
host = "${frontend_domain}"
|
||||
|
||||
# Backend domain
|
||||
[[config.domains]]
|
||||
serviceName = "server"
|
||||
port = 80
|
||||
host = "${backend_domain}"
|
||||
|
||||
[config.env]
|
||||
# --- Postgres (values used by both db and server services)
|
||||
PGHOST = "db"
|
||||
POSTGRES_DB = "adventure"
|
||||
POSTGRES_USER = "adventure"
|
||||
POSTGRES_PASSWORD = "${postgres_password}"
|
||||
|
||||
# --- Django Backend
|
||||
SECRET_KEY = "${secret_key}"
|
||||
DJANGO_ADMIN_USERNAME = "${admin_username}"
|
||||
DJANGO_ADMIN_PASSWORD = "${admin_password}"
|
||||
DJANGO_ADMIN_EMAIL = "${admin_email}"
|
||||
DEBUG = "False"
|
||||
CSRF_TRUSTED_ORIGINS = "http://${backend_domain},http://${frontend_domain}"
|
||||
FRONTEND_URL = "http://${frontend_domain}"
|
||||
PUBLIC_URL = "http://${backend_domain}"
|
||||
|
||||
# --- Frontend
|
||||
PUBLIC_SERVER_URL = "${backend_domain}"
|
||||
ORIGIN = "http://${frontend_domain}"
|
||||
BODY_SIZE_LIMIT = "Infinity"
|
||||
@@ -1,65 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
affinepro:
|
||||
image: ghcr.io/toeverything/affine-graphql:stable-780dd83
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3010
|
||||
volumes:
|
||||
- affine-storage:/root/.affine/storage
|
||||
- affine-config:/root/.affine/config
|
||||
environment:
|
||||
- REDIS_SERVER_HOST=redis
|
||||
- REDIS_SERVER_PASSWORD=${REDIS_PASSWORD}
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/affinepro
|
||||
- AFFINE_SERVER_HOST=${DOMAIN}
|
||||
- MAILER_HOST=${MAILER_HOST}
|
||||
- MAILER_PORT=${MAILER_PORT}
|
||||
- MAILER_USER=${MAILER_USER}
|
||||
- MAILER_PASSWORD=${MAILER_PASSWORD}
|
||||
- MAILER_SENDER=${MAILER_SENDER}
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
|
||||
migration:
|
||||
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_SERVER_HOST=${DOMAIN}
|
||||
- MAILER_HOST=${MAILER_HOST}
|
||||
- MAILER_PORT=${MAILER_PORT}
|
||||
- MAILER_USER=${MAILER_USER}
|
||||
- MAILER_PASSWORD=${MAILER_PASSWORD}
|
||||
- MAILER_SENDER=${MAILER_SENDER}
|
||||
volumes:
|
||||
- affine-storage:/root/.affine/storage
|
||||
- affine-config:/root/.affine/config
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
|
||||
db:
|
||||
image: postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=affinepro
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
|
||||
volumes:
|
||||
affine-storage: {}
|
||||
affine-config: {}
|
||||
postgres-data: {}
|
||||
redis-data: {}
|
||||
|
Before Width: | Height: | Size: 8.3 KiB |
@@ -1,25 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
postgres_password = "${password:16}"
|
||||
redis_password = "${password:16}"
|
||||
mailer_host = ""
|
||||
mailer_port = "587"
|
||||
mailer_user = ""
|
||||
mailer_password = ""
|
||||
mailer_sender = ""
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "affinepro"
|
||||
port = 3010
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
DOMAIN = "${main_domain}"
|
||||
POSTGRES_PASSWORD = "${postgres_password}"
|
||||
REDIS_PASSWORD = "${redis_password}"
|
||||
MAILER_HOST = "${mailer_host}"
|
||||
MAILER_PORT = "${mailer_port}"
|
||||
MAILER_USER = "${mailer_user}"
|
||||
MAILER_PASSWORD = "${mailer_password}"
|
||||
MAILER_SENDER = "${mailer_sender}"
|
||||
|
Before Width: | Height: | Size: 23 KiB |
@@ -1,22 +0,0 @@
|
||||
services:
|
||||
agentdvr:
|
||||
image: mekayelanik/ispyagentdvr:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=${TIMEZONE:-America/New_York}
|
||||
- AGENTDVR_WEBUI_PORT=8090
|
||||
volumes:
|
||||
- agentdvr-config:/AgentDVR/Media/XML/
|
||||
- agentdvr-media:/AgentDVR/Media/WebServerRoot/Media/
|
||||
- agentdvr-commands:/AgentDVR/Commands/
|
||||
ports:
|
||||
- 8090
|
||||
- 3478/udp
|
||||
- 50000-50100/udp
|
||||
|
||||
volumes:
|
||||
agentdvr-config: {}
|
||||
agentdvr-media: {}
|
||||
agentdvr-commands: {}
|
||||
@@ -1,14 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
timezone = "America/New_York"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "agentdvr"
|
||||
port = 8090
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
TIMEZONE = "${timezone}"
|
||||
|
||||
[[config.mounts]]
|
||||
@@ -1,55 +0,0 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
akaunting:
|
||||
image: docker.io/akaunting/akaunting:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
akaunting-db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
# App config
|
||||
APP_URL: ${APP_URL}
|
||||
LOCALE: ${LOCALE}
|
||||
|
||||
# Database connection
|
||||
DB_HOST: akaunting-db
|
||||
DB_PORT: 3306
|
||||
DB_NAME: ${DB_NAME}
|
||||
DB_USERNAME: ${DB_USERNAME}
|
||||
DB_PASSWORD: ${DB_PASSWORD}
|
||||
DB_PREFIX: ${DB_PREFIX}
|
||||
|
||||
# First-run bootstrap (company + admin)
|
||||
COMPANY_NAME: ${COMPANY_NAME}
|
||||
COMPANY_EMAIL: ${COMPANY_EMAIL}
|
||||
ADMIN_EMAIL: ${ADMIN_EMAIL}
|
||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
|
||||
|
||||
# Expose only to internal network; domain routing handled by Dokploy/Traefik
|
||||
expose:
|
||||
- "80"
|
||||
|
||||
volumes:
|
||||
- akaunting-data:/var/www/html
|
||||
|
||||
akaunting-db:
|
||||
image: mariadb:10.11
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE}
|
||||
MYSQL_USER: ${MYSQL_USER}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||
# Randomizes root password on each deployment (recommended)
|
||||
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
||||
volumes:
|
||||
- akaunting-db:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
akaunting-data: {}
|
||||
akaunting-db: {}
|
||||
|
Before Width: | Height: | Size: 8.3 KiB |
@@ -1,54 +0,0 @@
|
||||
[variables]
|
||||
# Domain & UI
|
||||
main_domain = "${domain}"
|
||||
locale = "en-US"
|
||||
|
||||
# DB credentials (safe defaults; user can override in UI)
|
||||
db_name = "akaunting"
|
||||
db_user = "admin"
|
||||
mysql_password = "${password:24}"
|
||||
|
||||
# Akaunting setup variables
|
||||
company_name = "My Company"
|
||||
company_email = "${email}"
|
||||
admin_email = "${email}"
|
||||
admin_password = "${password:24}"
|
||||
|
||||
# Akaunting table prefix: 3 hex chars + underscore (e.g., "a1f_")
|
||||
db_prefix = "${hash:3}_"
|
||||
|
||||
# Domain routing for the web UI
|
||||
[[config.domains]]
|
||||
serviceName = "akaunting"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
# App
|
||||
"APP_URL=${main_domain}",
|
||||
"LOCALE=${locale}",
|
||||
|
||||
# Database (app)
|
||||
"DB_HOST=akaunting-db",
|
||||
"DB_PORT=3306",
|
||||
"DB_NAME=${db_name}",
|
||||
"DB_USERNAME=${db_user}",
|
||||
"DB_PASSWORD=${mysql_password}",
|
||||
"DB_PREFIX=${db_prefix}",
|
||||
|
||||
# First-run bootstrap (app)
|
||||
"COMPANY_NAME=${company_name}",
|
||||
"COMPANY_EMAIL=${company_email}",
|
||||
"ADMIN_EMAIL=${admin_email}",
|
||||
"ADMIN_PASSWORD=${admin_password}",
|
||||
|
||||
# Database (server)
|
||||
"MYSQL_DATABASE=${db_name}",
|
||||
"MYSQL_USER=${db_user}",
|
||||
"MYSQL_PASSWORD=${mysql_password}",
|
||||
"MYSQL_RANDOM_ROOT_PASSWORD=yes"
|
||||
]
|
||||
|
||||
# No custom mounts needed; volumes are already defined in compose.
|
||||
[[config.mounts]]
|
||||
@@ -1,11 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
env = []
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "alist"
|
||||
port = 5_244
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,12 @@
|
||||
variables:
|
||||
main_domain: ${domain}
|
||||
|
||||
config:
|
||||
domains:
|
||||
- serviceName: alist
|
||||
port: 5244
|
||||
host: ${main_domain}
|
||||
|
||||
env: []
|
||||
|
||||
mounts: []
|
||||
@@ -1,12 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
alltube:
|
||||
image: dnomd343/alltube:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 80
|
||||
environment:
|
||||
- TITLE=${TITLE}
|
||||
- CONVERT=${CONVERT}
|
||||
- STREAM=${STREAM}
|
||||
- REMUX=${REMUX}
|
||||
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,18 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
title = "My AllTube Site"
|
||||
convert = "true"
|
||||
stream = "true"
|
||||
remux = "true"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "alltube"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
TITLE = "${title}"
|
||||
CONVERT = "${convert}"
|
||||
STREAM = "${stream}"
|
||||
REMUX = "${remux}"
|
||||
@@ -1,17 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
ampache:
|
||||
image: ampache/ampache:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 80
|
||||
volumes:
|
||||
- config:/var/www/config
|
||||
- log:/var/log/ampache
|
||||
- mysql:/var/lib/mysql
|
||||
- ${MEDIA_PATH}:/media
|
||||
|
||||
volumes:
|
||||
config: {}
|
||||
log: {}
|
||||
mysql: {}
|
||||
|
Before Width: | Height: | Size: 8.9 KiB |
@@ -1,12 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
media_path = "/path/to/your/media"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "ampache"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
MEDIA_PATH = "${media_path}"
|
||||
@@ -1,16 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
anonupload:
|
||||
image: ghcr.io/supernova3339/anonfiles:1
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 80
|
||||
environment:
|
||||
- ADMIN_EMAIL=${ADMIN_EMAIL}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
||||
- CONTACT_EMAIL=${CONTACT_EMAIL}
|
||||
volumes:
|
||||
- uploads:/var/www/html/uploads
|
||||
|
||||
volumes:
|
||||
uploads: {}
|
||||
|
Before Width: | Height: | Size: 258 KiB |
@@ -1,16 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
admin_email = "${email}"
|
||||
admin_password = "${password:16}"
|
||||
contact_email = "${email}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "anonupload"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
ADMIN_EMAIL = "${admin_email}"
|
||||
ADMIN_PASSWORD = "${admin_password}"
|
||||
CONTACT_EMAIL = "${contact_email}"
|
||||
@@ -1,15 +0,0 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
anse-demo:
|
||||
image: ddiu8081/anse:latest
|
||||
restart: always
|
||||
expose:
|
||||
- 3000
|
||||
environment:
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
- OPENAI_API_BASE_URL=${OPENAI_API_BASE_URL}
|
||||
- HEAD_SCRIPTS=${HEAD_SCRIPTS}
|
||||
- SECRET_KEY=${SECRET_KEY}
|
||||
- SITE_PASSWORD=${SITE_PASSWORD}
|
||||
- OPENAI_API_MODEL=${OPENAI_API_MODEL}
|
||||
|
Before Width: | Height: | Size: 42 KiB |
@@ -1,18 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
secret_key = "${password:32}"
|
||||
site_password = "${password:16}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "anse-demo"
|
||||
port = 3000
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
OPENAI_API_KEY = ""
|
||||
OPENAI_API_BASE_URL = "https://api.openai.com"
|
||||
HEAD_SCRIPTS = ""
|
||||
SECRET_KEY = "${secret_key}"
|
||||
SITE_PASSWORD = "${site_password}"
|
||||
OPENAI_API_MODEL = "gpt-4o-mini"
|
||||
@@ -1,12 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
service_hash = "${hash:32}"
|
||||
|
||||
[config]
|
||||
env = ["ANSWER_HOST=http://${main_domain}", "SERVICE_HASH=${service_hash}"]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "answer"
|
||||
port = 9_080
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,15 @@
|
||||
variables:
|
||||
main_domain: ${domain}
|
||||
service_hash: ${hash:32}
|
||||
|
||||
config:
|
||||
domains:
|
||||
- serviceName: answer
|
||||
port: 9080
|
||||
host: ${main_domain}
|
||||
|
||||
env:
|
||||
- ANSWER_HOST=http://${main_domain}
|
||||
- SERVICE_HASH=${service_hash}
|
||||
|
||||
mounts: []
|
||||
|
Before Width: | Height: | Size: 30 KiB |
@@ -1,38 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
anubis:
|
||||
image: ghcr.io/techarohq/anubis:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8923" # Anubis default port
|
||||
environment:
|
||||
# Required: Point to your frontend service locally
|
||||
# Example: http://mydocker-wyeud:8080
|
||||
- TARGET=${TARGET}
|
||||
|
||||
# Bind address
|
||||
- BIND=:8923
|
||||
|
||||
# Cookie domain (set to your domain)
|
||||
- COOKIE_DOMAIN=${COOKIE_DOMAIN}
|
||||
|
||||
# Challenge difficulty (1-20, default is 5)
|
||||
# Lower = easier for users, Higher = harder for bots
|
||||
- DIFFICULTY=${DIFFICULTY}
|
||||
|
||||
# OpenGraph passthrough (allows social media previews to work)
|
||||
- OG_PASSTHROUGH=${OG_PASSTHROUGH}
|
||||
- OG_EXPIRY_TIME=${OG_EXPIRY_TIME}
|
||||
- OG_CACHE_CONSIDER_HOST=${OG_CACHE_CONSIDER_HOST}
|
||||
|
||||
# Optional: Serve robots.txt to discourage indexing
|
||||
- SERVE_ROBOTS_TXT=${SERVE_ROBOTS_TXT} # Set to true if you don't want search engines
|
||||
|
||||
|
||||
# Unconmment in case you want to connect the anubis container to dokploy-network
|
||||
# networks:
|
||||
# - dokploy-network
|
||||
|
||||
# networks:
|
||||
# dokploy-network:
|
||||
# external: true # Using existing dokploy-network to connect to your frontend locally
|
||||
@@ -1,17 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config.env]
|
||||
COOKIE_DOMAIN = "${main_domain}"
|
||||
TARGET = "YOURLOCALHOST:PORT"
|
||||
DIFFICULTY = "5"
|
||||
OG_PASSTHROUGH = true
|
||||
OG_EXPIRY_TIME = "1h"
|
||||
OG_CACHE_CONSIDER_HOST = true
|
||||
SERVE_ROBOTS_TXT = false
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "anubis"
|
||||
port = 8923
|
||||
host = "${main_domain}"
|
||||
@@ -1,16 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
anythingllm:
|
||||
image: mintplexlabs/anythingllm:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3001
|
||||
environment:
|
||||
- STORAGE_DIR=/app/server/storage
|
||||
volumes:
|
||||
- storage:/app/server/storage
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
|
||||
volumes:
|
||||
storage: {}
|
||||
|
Before Width: | Height: | Size: 51 KiB |
@@ -1,8 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "anythingllm"
|
||||
port = 3001
|
||||
host = "${main_domain}"
|
||||
@@ -1,19 +0,0 @@
|
||||
# Example: Any-Sync-Bundle with embedded MongoDB and Redis (all-in-one image)
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f compose.aio.yml up -d
|
||||
#
|
||||
# The bundle image already contains MongoDB and Redis. Only the bundle service is required
|
||||
|
||||
services:
|
||||
any-sync-bundle:
|
||||
image: ghcr.io/grishy/any-sync-bundle:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "33010:33010"
|
||||
- "33020:33020/udp"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
environment:
|
||||
# Advertise addresses clients should use. Replace with your server hostname/IP.
|
||||
ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS: "192.168.100.9"
|
||||
|
Before Width: | Height: | Size: 50 KiB |
@@ -1,4 +0,0 @@
|
||||
[config]
|
||||
domains = []
|
||||
mounts = []
|
||||
env = []
|
||||
|
Before Width: | Height: | Size: 10 KiB |
@@ -1,257 +0,0 @@
|
||||
services:
|
||||
nginx:
|
||||
restart: on-failure
|
||||
image: nginx
|
||||
volumes:
|
||||
- ../files/nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
|
||||
minio:
|
||||
restart: on-failure
|
||||
image: minio/minio
|
||||
environment:
|
||||
- MINIO_BROWSER_REDIRECT_URL=${APPFLOWY_BASE_URL}/minio
|
||||
- MINIO_ROOT_USER=${APPFLOWY_S3_ACCESS_KEY:-minioadmin}
|
||||
- MINIO_ROOT_PASSWORD=${APPFLOWY_S3_SECRET_KEY:-minioadmin}
|
||||
command: server /data --console-address ":9001"
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
|
||||
postgres:
|
||||
restart: on-failure
|
||||
image: pgvector/pgvector:pg16
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER:-postgres}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-postgres}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
|
||||
- POSTGRES_HOST=${POSTGRES_HOST:-postgres}
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER}", "-d", "${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./files/volumes/postgres/:/docker-entrypoint-initdb.d/
|
||||
|
||||
redis:
|
||||
restart: on-failure
|
||||
image: redis
|
||||
|
||||
gotrue:
|
||||
restart: on-failure
|
||||
image: appflowyinc/gotrue:${GOTRUE_VERSION:-latest}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "--fail", "http://127.0.0.1:9999/health"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
environment:
|
||||
# Admin Configuration
|
||||
- GOTRUE_ADMIN_EMAIL=${GOTRUE_ADMIN_EMAIL}
|
||||
- GOTRUE_ADMIN_PASSWORD=${GOTRUE_ADMIN_PASSWORD}
|
||||
- GOTRUE_DISABLE_SIGNUP=${GOTRUE_DISABLE_SIGNUP:-false}
|
||||
|
||||
# Site Configuration
|
||||
- GOTRUE_SITE_URL=appflowy-flutter://
|
||||
- GOTRUE_URI_ALLOW_LIST=${GOTRUE_URI_ALLOW_LIST:-**}
|
||||
- API_EXTERNAL_URL=${API_EXTERNAL_URL}
|
||||
|
||||
# JWT Configuration
|
||||
- GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET}
|
||||
- GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP:-7200}
|
||||
- GOTRUE_JWT_ADMIN_GROUP_NAME=supabase_admin
|
||||
|
||||
# Database Configuration
|
||||
- GOTRUE_DB_DRIVER=postgres
|
||||
- DATABASE_URL=${GOTRUE_DATABASE_URL}
|
||||
- PORT=9999
|
||||
|
||||
# Email Configuration
|
||||
- GOTRUE_SMTP_HOST=${GOTRUE_SMTP_HOST}
|
||||
- GOTRUE_SMTP_PORT=${GOTRUE_SMTP_PORT}
|
||||
- GOTRUE_SMTP_USER=${GOTRUE_SMTP_USER}
|
||||
- GOTRUE_SMTP_PASS=${GOTRUE_SMTP_PASS}
|
||||
- GOTRUE_SMTP_ADMIN_EMAIL=${GOTRUE_SMTP_ADMIN_EMAIL}
|
||||
- GOTRUE_SMTP_MAX_FREQUENCY=${GOTRUE_SMTP_MAX_FREQUENCY:-1ns}
|
||||
- GOTRUE_RATE_LIMIT_EMAIL_SENT=${GOTRUE_RATE_LIMIT_EMAIL_SENT:-100}
|
||||
- GOTRUE_MAILER_AUTOCONFIRM=${GOTRUE_MAILER_AUTOCONFIRM:-true}
|
||||
|
||||
# Email Templates
|
||||
- GOTRUE_MAILER_URLPATHS_CONFIRMATION=/gotrue/verify
|
||||
- GOTRUE_MAILER_URLPATHS_INVITE=/gotrue/verify
|
||||
- GOTRUE_MAILER_URLPATHS_RECOVERY=/gotrue/verify
|
||||
- GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE=/gotrue/verify
|
||||
- GOTRUE_MAILER_TEMPLATES_MAGIC_LINK=${GOTRUE_MAILER_TEMPLATES_MAGIC_LINK}
|
||||
|
||||
# OAuth Providers
|
||||
- GOTRUE_EXTERNAL_GOOGLE_ENABLED=${GOTRUE_EXTERNAL_GOOGLE_ENABLED:-false}
|
||||
- GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID=${GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID}
|
||||
- GOTRUE_EXTERNAL_GOOGLE_SECRET=${GOTRUE_EXTERNAL_GOOGLE_SECRET}
|
||||
- GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI=${GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI}
|
||||
|
||||
- GOTRUE_EXTERNAL_GITHUB_ENABLED=${GOTRUE_EXTERNAL_GITHUB_ENABLED:-false}
|
||||
- GOTRUE_EXTERNAL_GITHUB_CLIENT_ID=${GOTRUE_EXTERNAL_GITHUB_CLIENT_ID}
|
||||
- GOTRUE_EXTERNAL_GITHUB_SECRET=${GOTRUE_EXTERNAL_GITHUB_SECRET}
|
||||
- GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI=${GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI}
|
||||
|
||||
- GOTRUE_EXTERNAL_DISCORD_ENABLED=${GOTRUE_EXTERNAL_DISCORD_ENABLED:-false}
|
||||
- GOTRUE_EXTERNAL_DISCORD_CLIENT_ID=${GOTRUE_EXTERNAL_DISCORD_CLIENT_ID}
|
||||
- GOTRUE_EXTERNAL_DISCORD_SECRET=${GOTRUE_EXTERNAL_DISCORD_SECRET}
|
||||
- GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI=${GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI}
|
||||
|
||||
# SAML Configuration
|
||||
- GOTRUE_SAML_ENABLED=${GOTRUE_SAML_ENABLED:-false}
|
||||
- GOTRUE_SAML_PRIVATE_KEY=${GOTRUE_SAML_PRIVATE_KEY}
|
||||
|
||||
appflowy_cloud:
|
||||
restart: on-failure
|
||||
image: appflowyinc/appflowy_cloud:${APPFLOWY_CLOUD_VERSION:-latest}
|
||||
depends_on:
|
||||
gotrue:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
# Core Configuration
|
||||
- RUST_LOG=${RUST_LOG:-info}
|
||||
- APPFLOWY_ENVIRONMENT=production
|
||||
- APPFLOWY_DATABASE_URL=${APPFLOWY_DATABASE_URL}
|
||||
- APPFLOWY_REDIS_URI=${APPFLOWY_REDIS_URI}
|
||||
- APPFLOWY_WEB_URL=${APPFLOWY_WEB_URL}
|
||||
|
||||
# Authentication Configuration
|
||||
- APPFLOWY_GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET}
|
||||
- APPFLOWY_GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP:-7200}
|
||||
- APPFLOWY_GOTRUE_BASE_URL=${APPFLOWY_GOTRUE_BASE_URL}
|
||||
|
||||
# File Storage Configuration
|
||||
- APPFLOWY_S3_CREATE_BUCKET=${APPFLOWY_S3_CREATE_BUCKET:-true}
|
||||
- APPFLOWY_S3_USE_MINIO=${APPFLOWY_S3_USE_MINIO:-true}
|
||||
- APPFLOWY_S3_MINIO_URL=${APPFLOWY_S3_MINIO_URL}
|
||||
- APPFLOWY_S3_ACCESS_KEY=${APPFLOWY_S3_ACCESS_KEY}
|
||||
- APPFLOWY_S3_SECRET_KEY=${APPFLOWY_S3_SECRET_KEY}
|
||||
- APPFLOWY_S3_BUCKET=${APPFLOWY_S3_BUCKET:-appflowy}
|
||||
- APPFLOWY_S3_REGION=${APPFLOWY_S3_REGION:-us-east-1}
|
||||
- APPFLOWY_S3_PRESIGNED_URL_ENDPOINT=${APPFLOWY_S3_PRESIGNED_URL_ENDPOINT}
|
||||
|
||||
# Email Configuration
|
||||
- APPFLOWY_MAILER_SMTP_HOST=${APPFLOWY_MAILER_SMTP_HOST}
|
||||
- APPFLOWY_MAILER_SMTP_PORT=${APPFLOWY_MAILER_SMTP_PORT}
|
||||
- APPFLOWY_MAILER_SMTP_USERNAME=${APPFLOWY_MAILER_SMTP_USERNAME}
|
||||
- APPFLOWY_MAILER_SMTP_EMAIL=${APPFLOWY_MAILER_SMTP_EMAIL}
|
||||
- APPFLOWY_MAILER_SMTP_PASSWORD=${APPFLOWY_MAILER_SMTP_PASSWORD}
|
||||
- APPFLOWY_MAILER_SMTP_TLS_KIND=${APPFLOWY_MAILER_SMTP_TLS_KIND:-wrapper}
|
||||
|
||||
# Access Control and Performance
|
||||
- APPFLOWY_ACCESS_CONTROL=${APPFLOWY_ACCESS_CONTROL:-true}
|
||||
- APPFLOWY_DATABASE_MAX_CONNECTIONS=${APPFLOWY_DATABASE_MAX_CONNECTIONS:-40}
|
||||
- APPFLOWY_WEBSOCKET_MAILBOX_SIZE=${APPFLOWY_WEBSOCKET_MAILBOX_SIZE:-6000}
|
||||
|
||||
# AI Configuration
|
||||
- AI_SERVER_HOST=${AI_SERVER_HOST:-ai}
|
||||
- AI_SERVER_PORT=${AI_SERVER_PORT:-5001}
|
||||
- AI_OPENAI_API_KEY=${AI_OPENAI_API_KEY}
|
||||
|
||||
admin_frontend:
|
||||
restart: on-failure
|
||||
image: appflowyinc/admin_frontend:${APPFLOWY_ADMIN_FRONTEND_VERSION:-latest}
|
||||
depends_on:
|
||||
gotrue:
|
||||
condition: service_healthy
|
||||
appflowy_cloud:
|
||||
condition: service_started
|
||||
environment:
|
||||
- RUST_LOG=${RUST_LOG:-info}
|
||||
- ADMIN_FRONTEND_REDIS_URL=${ADMIN_FRONTEND_REDIS_URL}
|
||||
- ADMIN_FRONTEND_GOTRUE_URL=${ADMIN_FRONTEND_GOTRUE_URL}
|
||||
- ADMIN_FRONTEND_APPFLOWY_CLOUD_URL=${ADMIN_FRONTEND_APPFLOWY_CLOUD_URL}
|
||||
- ADMIN_FRONTEND_PATH_PREFIX=${ADMIN_FRONTEND_PATH_PREFIX:-/console}
|
||||
|
||||
ai:
|
||||
restart: on-failure
|
||||
image: appflowyinc/appflowy_ai:${APPFLOWY_AI_VERSION:-latest}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
appflowy_cloud:
|
||||
condition: service_started
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:5001/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
environment:
|
||||
# Core AI Configuration
|
||||
- AI_SERVER_PORT=${AI_SERVER_PORT:-5001}
|
||||
- OPENAI_API_KEY=${AI_OPENAI_API_KEY}
|
||||
- DEFAULT_AI_MODEL=${DEFAULT_AI_MODEL:-gpt-4o-mini}
|
||||
- DEFAULT_AI_COMPLETION_MODEL=${DEFAULT_AI_COMPLETION_MODEL:-gpt-4o-mini}
|
||||
|
||||
# Azure OpenAI (optional)
|
||||
- AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY}
|
||||
- AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT}
|
||||
- AZURE_OPENAI_API_VERSION=${AZURE_OPENAI_API_VERSION}
|
||||
|
||||
# Database and Cache
|
||||
- AI_DATABASE_URL=${APPFLOWY_DATABASE_URL}
|
||||
- AI_REDIS_URL=${APPFLOWY_REDIS_URI}
|
||||
|
||||
# File Storage for AI
|
||||
- APPFLOWY_S3_ACCESS_KEY=${APPFLOWY_S3_ACCESS_KEY}
|
||||
- APPFLOWY_S3_SECRET_KEY=${APPFLOWY_S3_SECRET_KEY}
|
||||
- APPFLOWY_S3_BUCKET=${APPFLOWY_S3_BUCKET:-appflowy}
|
||||
- APPFLOWY_S3_REGION=${APPFLOWY_S3_REGION:-us-east-1}
|
||||
- AI_USE_MINIO=${APPFLOWY_S3_USE_MINIO:-true}
|
||||
- AI_MINIO_URL=${APPFLOWY_S3_MINIO_URL}
|
||||
|
||||
# Integration
|
||||
- AI_APPFLOWY_HOST=${APPFLOWY_BASE_URL}
|
||||
- APPFLOWY_GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET}
|
||||
|
||||
appflowy_worker:
|
||||
restart: on-failure
|
||||
image: appflowyinc/appflowy_worker:${APPFLOWY_WORKER_VERSION:-latest}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
# Core Configuration
|
||||
- RUST_LOG=${RUST_LOG:-info}
|
||||
- APPFLOWY_ENVIRONMENT=production
|
||||
- APPFLOWY_WORKER_REDIS_URL=${APPFLOWY_WORKER_REDIS_URL}
|
||||
- APPFLOWY_WORKER_ENVIRONMENT=production
|
||||
- APPFLOWY_WORKER_DATABASE_URL=${APPFLOWY_WORKER_DATABASE_URL}
|
||||
- APPFLOWY_WORKER_DATABASE_NAME=${APPFLOWY_WORKER_DATABASE_NAME}
|
||||
- APPFLOWY_WORKER_IMPORT_TICK_INTERVAL=${APPFLOWY_WORKER_IMPORT_TICK_INTERVAL:-30}
|
||||
|
||||
# File Storage Configuration
|
||||
- APPFLOWY_S3_USE_MINIO=${APPFLOWY_S3_USE_MINIO:-true}
|
||||
- APPFLOWY_S3_MINIO_URL=${APPFLOWY_S3_MINIO_URL}
|
||||
- APPFLOWY_S3_ACCESS_KEY=${APPFLOWY_S3_ACCESS_KEY}
|
||||
- APPFLOWY_S3_SECRET_KEY=${APPFLOWY_S3_SECRET_KEY}
|
||||
- APPFLOWY_S3_BUCKET=${APPFLOWY_S3_BUCKET:-appflowy}
|
||||
- APPFLOWY_S3_REGION=${APPFLOWY_S3_REGION:-us-east-1}
|
||||
|
||||
# Email Configuration
|
||||
- APPFLOWY_MAILER_SMTP_HOST=${APPFLOWY_MAILER_SMTP_HOST}
|
||||
- APPFLOWY_MAILER_SMTP_PORT=${APPFLOWY_MAILER_SMTP_PORT}
|
||||
- APPFLOWY_MAILER_SMTP_USERNAME=${APPFLOWY_MAILER_SMTP_USERNAME}
|
||||
- APPFLOWY_MAILER_SMTP_EMAIL=${APPFLOWY_MAILER_SMTP_EMAIL}
|
||||
- APPFLOWY_MAILER_SMTP_PASSWORD=${APPFLOWY_MAILER_SMTP_PASSWORD}
|
||||
- APPFLOWY_MAILER_SMTP_TLS_KIND=${APPFLOWY_MAILER_SMTP_TLS_KIND:-wrapper}
|
||||
|
||||
appflowy_web:
|
||||
restart: on-failure
|
||||
image: appflowyinc/appflowy_web:${APPFLOWY_WEB_VERSION:-latest}
|
||||
depends_on:
|
||||
- appflowy_cloud
|
||||
environment:
|
||||
- AF_BASE_URL=${APPFLOWY_BASE_URL}
|
||||
- AF_GOTRUE_URL=${APPFLOWY_BASE_URL}/gotrue
|
||||
- AF_WS_V2_URL=${APPFLOWY_WEBSOCKET_BASE_URL}
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
minio_data:
|
||||
@@ -1,461 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
# =============================================================================
|
||||
# 🌐 CORE DOMAIN CONFIGURATION
|
||||
# =============================================================================
|
||||
"FQDN=${main_domain}",
|
||||
"SCHEME=https",
|
||||
"WS_SCHEME=wss",
|
||||
"APPFLOWY_BASE_URL=https://${main_domain}",
|
||||
"APPFLOWY_WEBSOCKET_BASE_URL=wss://${main_domain}/ws/v2",
|
||||
"APPFLOWY_WEB_URL=https://${main_domain}",
|
||||
"API_EXTERNAL_URL=https://${main_domain}/gotrue",
|
||||
"TZ=UTC",
|
||||
|
||||
# Admin Configuration
|
||||
"GOTRUE_ADMIN_EMAIL=${email}",
|
||||
"GOTRUE_ADMIN_PASSWORD=${password:16}",
|
||||
"GOTRUE_DISABLE_SIGNUP=false",
|
||||
|
||||
# =============================================================================
|
||||
# 🗄️ DATABASE & CACHE CONFIGURATION
|
||||
# =============================================================================
|
||||
"POSTGRES_HOST=postgres",
|
||||
"POSTGRES_USER=appflowy",
|
||||
"POSTGRES_PASSWORD=${password:64}",
|
||||
"POSTGRES_PORT=5432",
|
||||
"POSTGRES_DB=appflowy",
|
||||
"REDIS_HOST=redis",
|
||||
"REDIS_PORT=6379",
|
||||
|
||||
# =============================================================================
|
||||
# 🔐 GOTRUE AUTHENTICATION CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# JWT Configuration
|
||||
"GOTRUE_JWT_SECRET=${password:64}",
|
||||
"GOTRUE_JWT_EXP=7200",
|
||||
"GOTRUE_JWT_ADMIN_GROUP_NAME=supabase_admin",
|
||||
|
||||
# Database Configuration
|
||||
"GOTRUE_DB_DRIVER=postgres",
|
||||
"GOTRUE_DATABASE_URL=postgres://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy?search_path=auth",
|
||||
"PORT=9999",
|
||||
|
||||
# Site Configuration
|
||||
"GOTRUE_SITE_URL=appflowy-flutter://",
|
||||
"GOTRUE_URI_ALLOW_LIST=**",
|
||||
|
||||
# Email Configuration (SMTP - Configure for production)
|
||||
"GOTRUE_SMTP_HOST=",
|
||||
"GOTRUE_SMTP_PORT=465",
|
||||
"GOTRUE_SMTP_USER=",
|
||||
"GOTRUE_SMTP_PASS=",
|
||||
"GOTRUE_SMTP_ADMIN_EMAIL=${GOTRUE_ADMIN_EMAIL}",
|
||||
"GOTRUE_SMTP_MAX_FREQUENCY=1ns",
|
||||
"GOTRUE_RATE_LIMIT_EMAIL_SENT=100",
|
||||
"GOTRUE_MAILER_AUTOCONFIRM=true",
|
||||
|
||||
# Email Templates
|
||||
"GOTRUE_MAILER_URLPATHS_CONFIRMATION=/gotrue/verify",
|
||||
"GOTRUE_MAILER_URLPATHS_INVITE=/gotrue/verify",
|
||||
"GOTRUE_MAILER_URLPATHS_RECOVERY=/gotrue/verify",
|
||||
"GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE=/gotrue/verify",
|
||||
"GOTRUE_MAILER_TEMPLATES_MAGIC_LINK=",
|
||||
|
||||
# OAuth Providers (Configure as needed)
|
||||
"GOTRUE_EXTERNAL_GOOGLE_ENABLED=false",
|
||||
"GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID=",
|
||||
"GOTRUE_EXTERNAL_GOOGLE_SECRET=",
|
||||
"GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI=https://${main_domain}/gotrue/callback",
|
||||
|
||||
"GOTRUE_EXTERNAL_GITHUB_ENABLED=false",
|
||||
"GOTRUE_EXTERNAL_GITHUB_CLIENT_ID=",
|
||||
"GOTRUE_EXTERNAL_GITHUB_SECRET=",
|
||||
"GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI=https://${main_domain}/gotrue/callback",
|
||||
|
||||
"GOTRUE_EXTERNAL_DISCORD_ENABLED=false",
|
||||
"GOTRUE_EXTERNAL_DISCORD_CLIENT_ID=",
|
||||
"GOTRUE_EXTERNAL_DISCORD_SECRET=",
|
||||
"GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI=https://${main_domain}/gotrue/callback",
|
||||
|
||||
# SAML Configuration
|
||||
"GOTRUE_SAML_ENABLED=false",
|
||||
"GOTRUE_SAML_PRIVATE_KEY=",
|
||||
|
||||
# =============================================================================
|
||||
# ☁️ APPFLOWY CLOUD SERVICE CONFIGURATION
|
||||
# =============================================================================
|
||||
# Core Configuration
|
||||
"RUST_LOG=info",
|
||||
"APPFLOWY_ENVIRONMENT=production",
|
||||
"APPFLOWY_DATABASE_URL=postgres://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy",
|
||||
"APPFLOWY_REDIS_URI=redis://redis:6379",
|
||||
|
||||
# Authentication Integration
|
||||
"APPFLOWY_GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET}",
|
||||
"APPFLOWY_GOTRUE_JWT_EXP=7200",
|
||||
"APPFLOWY_GOTRUE_BASE_URL=http://gotrue:9999",
|
||||
|
||||
# Access Control and Performance
|
||||
"APPFLOWY_ACCESS_CONTROL=true",
|
||||
"APPFLOWY_DATABASE_MAX_CONNECTIONS=40",
|
||||
"APPFLOWY_WEBSOCKET_MAILBOX_SIZE=6000",
|
||||
|
||||
# Email Configuration (SMTP)
|
||||
"APPFLOWY_MAILER_SMTP_HOST=",
|
||||
"APPFLOWY_MAILER_SMTP_PORT=465",
|
||||
"APPFLOWY_MAILER_SMTP_USERNAME=",
|
||||
"APPFLOWY_MAILER_SMTP_EMAIL=",
|
||||
"APPFLOWY_MAILER_SMTP_PASSWORD=",
|
||||
"APPFLOWY_MAILER_SMTP_TLS_KIND=wrapper",
|
||||
|
||||
# =============================================================================
|
||||
# 💾 FILE STORAGE CONFIGURATION (MinIO/S3)
|
||||
# =============================================================================
|
||||
# MinIO Configuration
|
||||
"MINIO_HOST=minio",
|
||||
"MINIO_PORT=9000",
|
||||
"APPFLOWY_S3_USE_MINIO=true",
|
||||
"APPFLOWY_S3_CREATE_BUCKET=true",
|
||||
"APPFLOWY_S3_MINIO_URL=http://minio:9000",
|
||||
|
||||
# Storage Credentials
|
||||
"APPFLOWY_S3_ACCESS_KEY=${password:16}",
|
||||
"APPFLOWY_S3_SECRET_KEY=${password:32}",
|
||||
|
||||
# Storage Configuration
|
||||
"APPFLOWY_S3_BUCKET=appflowy",
|
||||
"APPFLOWY_S3_REGION=us-east-1",
|
||||
"APPFLOWY_S3_PRESIGNED_URL_ENDPOINT=https://${main_domain}/minio-api",
|
||||
|
||||
# AWS S3 Configuration (Alternative to MinIO)
|
||||
# "APPFLOWY_S3_USE_MINIO=false",
|
||||
# "APPFLOWY_S3_REGION=us-east-1",
|
||||
|
||||
# =============================================================================
|
||||
# 🎛️ ADMIN FRONTEND CONFIGURATION
|
||||
# =============================================================================
|
||||
"ADMIN_FRONTEND_REDIS_URL=redis://redis:6379",
|
||||
"ADMIN_FRONTEND_GOTRUE_URL=http://gotrue:9999",
|
||||
"ADMIN_FRONTEND_APPFLOWY_CLOUD_URL=http://appflowy_cloud:8000",
|
||||
"ADMIN_FRONTEND_PATH_PREFIX=/console",
|
||||
|
||||
# =============================================================================
|
||||
# 🤖 AI FEATURES CONFIGURATION (Optional)
|
||||
# =============================================================================
|
||||
# OpenAI Configuration
|
||||
"AI_OPENAI_API_KEY=",
|
||||
"DEFAULT_AI_MODEL=gpt-4o-mini",
|
||||
"DEFAULT_AI_COMPLETION_MODEL=gpt-4o-mini",
|
||||
|
||||
# Azure OpenAI (Alternative)
|
||||
"AZURE_OPENAI_API_KEY=",
|
||||
"AZURE_OPENAI_ENDPOINT=",
|
||||
"AZURE_OPENAI_API_VERSION=",
|
||||
|
||||
# AI Service Configuration
|
||||
"AI_SERVER_HOST=ai",
|
||||
"AI_SERVER_PORT=5001",
|
||||
"AI_DATABASE_URL=postgresql+psycopg://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy",
|
||||
"AI_REDIS_URL=redis://redis:6379",
|
||||
"AI_USE_MINIO=true",
|
||||
"AI_MINIO_URL=http://minio:9000",
|
||||
"AI_APPFLOWY_HOST=https://${main_domain}",
|
||||
|
||||
# Embedding Configuration
|
||||
"APPFLOWY_EMBEDDING_CHUNK_SIZE=2000",
|
||||
"APPFLOWY_EMBEDDING_CHUNK_OVERLAP=200",
|
||||
|
||||
# =============================================================================
|
||||
# ⚙️ WORKER SERVICES CONFIGURATION
|
||||
# =============================================================================
|
||||
# AppFlowy Worker
|
||||
"APPFLOWY_WORKER_REDIS_URL=redis://redis:6379",
|
||||
"APPFLOWY_WORKER_ENVIRONMENT=production",
|
||||
"APPFLOWY_WORKER_DATABASE_URL=postgres://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy",
|
||||
"APPFLOWY_WORKER_DATABASE_NAME=appflowy",
|
||||
"APPFLOWY_WORKER_IMPORT_TICK_INTERVAL=30",
|
||||
|
||||
# Indexer Configuration
|
||||
"APPFLOWY_INDEXER_ENABLED=true",
|
||||
"APPFLOWY_INDEXER_DATABASE_URL=postgres://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy",
|
||||
"APPFLOWY_INDEXER_REDIS_URL=redis://redis:6379",
|
||||
"APPFLOWY_INDEXER_EMBEDDING_BUFFER_SIZE=5000",
|
||||
|
||||
# Collaboration Service
|
||||
"APPFLOWY_COLLABORATE_MULTI_THREAD=false",
|
||||
"APPFLOWY_COLLABORATE_REMOVE_BATCH_SIZE=100",
|
||||
|
||||
# =============================================================================
|
||||
# 🌐 NGINX CONFIGURATION
|
||||
# =============================================================================
|
||||
"NGINX_PORT=80",
|
||||
"NGINX_TLS_PORT=443",
|
||||
|
||||
# =============================================================================
|
||||
# 🛠️ VERSION TAGS (Easily Configurable)
|
||||
# =============================================================================
|
||||
"GOTRUE_VERSION=latest",
|
||||
"APPFLOWY_CLOUD_VERSION=latest",
|
||||
"APPFLOWY_ADMIN_FRONTEND_VERSION=latest",
|
||||
"APPFLOWY_AI_VERSION=latest",
|
||||
"APPFLOWY_WORKER_VERSION=latest",
|
||||
"APPFLOWY_WEB_VERSION=latest",
|
||||
]
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "nginx"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "/nginx/nginx.conf"
|
||||
content = """# Minimal nginx configuration for AppFlowy-Cloud
|
||||
# Self Hosted AppFlowy Cloud user should alter this file to suit their needs,
|
||||
# or use the appflowy.site.conf in external_proxy_config/nginx if they are using
|
||||
# an external proxy.
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
# docker dns resolver
|
||||
resolver 127.0.0.11 valid=10s;
|
||||
#error_log /var/log/nginx/error.log debug;
|
||||
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
map $http_origin $cors_origin {
|
||||
# AppFlowy Web origin
|
||||
"~^http://localhost:3000$" $http_origin;
|
||||
default "null";
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
|
||||
# https://github.com/nginxinc/nginx-prometheus-exporter
|
||||
location = /stub_status {
|
||||
stub_status;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
server {
|
||||
|
||||
listen 80;
|
||||
client_max_body_size 10M;
|
||||
|
||||
underscores_in_headers on;
|
||||
set $appflowy_cloud_backend "http://appflowy_cloud:8000";
|
||||
set $gotrue_backend "http://gotrue:9999";
|
||||
set $admin_frontend_backend "http://admin_frontend:3000";
|
||||
set $appflowy_web_backend "http://appflowy_web:80";
|
||||
set $minio_backend "http://minio:9001";
|
||||
set $minio_api_backend "http://minio:9000";
|
||||
# Host name for minio, used internally within docker compose
|
||||
set $minio_internal_host "minio:9000";
|
||||
set $pgadmin_backend "http://pgadmin:80";
|
||||
|
||||
# GoTrue
|
||||
location /gotrue/ {
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' $cors_origin always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
add_header 'Access-Control-Allow-Headers' '*' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Max-Age' 3600 always;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8' always;
|
||||
add_header 'Content-Length' 0 always;
|
||||
return 204;
|
||||
}
|
||||
|
||||
proxy_pass $gotrue_backend;
|
||||
|
||||
rewrite ^/gotrue(/.*)$ $1 break;
|
||||
|
||||
# Allow headers like redirect_to to be handed over to the gotrue
|
||||
# for correct redirecting
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_pass_request_headers on;
|
||||
}
|
||||
|
||||
# WebSocket
|
||||
location /ws {
|
||||
proxy_pass $appflowy_cloud_backend;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 86400s;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass $appflowy_cloud_backend;
|
||||
proxy_set_header X-Request-Id $request_id;
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
# Set CORS headers for other requests
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' $cors_origin always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always;
|
||||
add_header 'Access-Control-Max-Age' 3600 always;
|
||||
return 204;
|
||||
}
|
||||
|
||||
add_header 'Access-Control-Allow-Origin' $cors_origin always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always;
|
||||
add_header 'Access-Control-Max-Age' 3600 always;
|
||||
|
||||
location ~* ^/api/workspace/([a-zA-Z0-9_-]+)/publish$ {
|
||||
proxy_pass $appflowy_cloud_backend;
|
||||
proxy_request_buffering off;
|
||||
client_max_body_size 256M;
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' $cors_origin always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always;
|
||||
add_header 'Access-Control-Max-Age' 3600 always;
|
||||
return 204;
|
||||
}
|
||||
|
||||
add_header 'Access-Control-Allow-Origin' $cors_origin always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always;
|
||||
add_header 'Access-Control-Max-Age' 3600 always;
|
||||
}
|
||||
|
||||
# AppFlowy-Cloud
|
||||
location /api/chat {
|
||||
proxy_pass $appflowy_cloud_backend;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding on;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
proxy_read_timeout 600s;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
}
|
||||
|
||||
location /api/import {
|
||||
proxy_pass $appflowy_cloud_backend;
|
||||
|
||||
# Set headers
|
||||
proxy_set_header X-Request-Id $request_id;
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
# Handle CORS
|
||||
add_header 'Access-Control-Allow-Origin' $cors_origin always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Device-Id' always;
|
||||
add_header 'Access-Control-Max-Age' 3600 always;
|
||||
|
||||
# Timeouts
|
||||
proxy_read_timeout 600s;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
|
||||
# Disable buffering for large file uploads
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
client_max_body_size 2G;
|
||||
}
|
||||
}
|
||||
|
||||
# Minio Web UI
|
||||
# Derive from: https://min.io/docs/minio/linux/integrations/setup-nginx-proxy-with-minio.html
|
||||
# Optional Module, comment this section if you did not deploy minio in docker-compose.yml
|
||||
# This endpoint is meant to be used for the MinIO Web UI, accessible via the admin portal
|
||||
location /minio/ {
|
||||
proxy_pass $minio_backend;
|
||||
|
||||
rewrite ^/minio/(.*) /$1 break;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
|
||||
## This is necessary to pass the correct IP to be hashed
|
||||
real_ip_header X-Real-IP;
|
||||
|
||||
proxy_connect_timeout 300s;
|
||||
|
||||
## To support websockets in MinIO versions released after January 2023
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
# Some environments may encounter CORS errors (Kubernetes + Nginx Ingress)
|
||||
# Uncomment the following line to set the Origin request to an empty string
|
||||
# proxy_set_header Origin '';
|
||||
|
||||
chunked_transfer_encoding off;
|
||||
}
|
||||
|
||||
# Optional Module, comment this section if you did not deploy minio in docker-compose.yml
|
||||
# This is used for presigned url, which is needs to be exposed to the AppFlowy client application.
|
||||
location /minio-api/ {
|
||||
proxy_pass $minio_api_backend;
|
||||
|
||||
# Set the host to internal host because the presigned url was signed against the internal host
|
||||
proxy_set_header Host $minio_internal_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
rewrite ^/minio-api/(.*) /$1 break;
|
||||
|
||||
proxy_connect_timeout 300s;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
}
|
||||
|
||||
# PgAdmin
|
||||
# Optional Module, comment this section if you did not deploy pgadmin in docker-compose.yml
|
||||
location /pgadmin/ {
|
||||
set $pgadmin pgadmin;
|
||||
proxy_pass $pgadmin_backend;
|
||||
|
||||
proxy_set_header X-Script-Name /pgadmin;
|
||||
proxy_set_header X-Scheme $scheme;
|
||||
proxy_set_header Host $host;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
# Admin Frontend
|
||||
# Optional Module, comment this section if you did not deploy admin_frontend in docker-compose.yml
|
||||
location /console {
|
||||
proxy_pass $admin_frontend_backend;
|
||||
|
||||
proxy_set_header X-Scheme $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
# AppFlowy Web
|
||||
location / {
|
||||
proxy_pass $appflowy_web_backend;
|
||||
proxy_set_header X-Scheme $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
"""
|
||||
@@ -1,16 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
apprise-api:
|
||||
image: linuxserver/apprise-api:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8000
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=UTC
|
||||
volumes:
|
||||
- config:/config
|
||||
|
||||
volumes:
|
||||
config: {}
|
||||
|
Before Width: | Height: | Size: 185 KiB |
@@ -1,8 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "apprise-api"
|
||||
port = 8000
|
||||
host = "${main_domain}"
|
||||
@@ -1,11 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
env = []
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appsmith"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,12 @@
|
||||
variables:
|
||||
main_domain: ${domain}
|
||||
|
||||
config:
|
||||
domains:
|
||||
- serviceName: appsmith
|
||||
port: 80
|
||||
host: ${main_domain}
|
||||
|
||||
env: []
|
||||
|
||||
mounts: []
|
||||
@@ -9,7 +9,7 @@ x-logging: &x-logging
|
||||
|
||||
services:
|
||||
appwrite:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
container_name: appwrite
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
@@ -195,7 +195,7 @@ services:
|
||||
- _APP_STORAGE_WASABI_BUCKET
|
||||
|
||||
appwrite-realtime:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: realtime
|
||||
container_name: appwrite-realtime
|
||||
<<: *x-logging
|
||||
@@ -227,7 +227,7 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-worker-audits:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-audits
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-audits
|
||||
@@ -253,7 +253,7 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-worker-webhooks:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-webhooks
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-webhooks
|
||||
@@ -281,7 +281,7 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-worker-deletes:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-deletes
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-deletes
|
||||
@@ -339,7 +339,7 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
|
||||
appwrite-worker-databases:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-databases
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-databases
|
||||
@@ -365,7 +365,7 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-worker-builds:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-builds
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-builds
|
||||
@@ -428,7 +428,7 @@ services:
|
||||
- _APP_STORAGE_WASABI_BUCKET
|
||||
|
||||
appwrite-worker-certificates:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-certificates
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-certificates
|
||||
@@ -461,7 +461,7 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-worker-functions:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-functions
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-functions
|
||||
@@ -499,7 +499,7 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-worker-mails:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-mails
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-mails
|
||||
@@ -531,7 +531,7 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-worker-messaging:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-messaging
|
||||
container_name: appwrite-worker-messaging
|
||||
<<: *x-logging
|
||||
@@ -581,7 +581,7 @@ services:
|
||||
- _APP_STORAGE_WASABI_BUCKET
|
||||
|
||||
appwrite-worker-migrations:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-migrations
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-migrations
|
||||
@@ -611,7 +611,7 @@ services:
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||
|
||||
appwrite-task-maintenance:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: maintenance
|
||||
<<: *x-logging
|
||||
container_name: appwrite-task-maintenance
|
||||
@@ -645,7 +645,7 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
|
||||
appwrite-worker-usage:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-usage
|
||||
container_name: appwrite-worker-usage
|
||||
<<: *x-logging
|
||||
@@ -673,7 +673,7 @@ services:
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
|
||||
appwrite-worker-usage-dump:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: worker-usage-dump
|
||||
container_name: appwrite-worker-usage-dump
|
||||
<<: *x-logging
|
||||
@@ -700,7 +700,7 @@ services:
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
|
||||
appwrite-task-scheduler-functions:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: schedule-functions
|
||||
container_name: appwrite-task-scheduler-functions
|
||||
<<: *x-logging
|
||||
@@ -725,7 +725,7 @@ services:
|
||||
- _APP_DB_PASS
|
||||
|
||||
appwrite-task-scheduler-executions:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: schedule-executions
|
||||
container_name: appwrite-task-scheduler-executions
|
||||
<<: *x-logging
|
||||
@@ -750,7 +750,7 @@ services:
|
||||
- _APP_DB_PASS
|
||||
|
||||
appwrite-task-scheduler-messages:
|
||||
image: appwrite/appwrite:1.6.1
|
||||
image: appwrite/appwrite:1.6.0
|
||||
entrypoint: schedule-messages
|
||||
container_name: appwrite-task-scheduler-messages
|
||||
<<: *x-logging
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"_APP_ENV=production",
|
||||
"_APP_LOCALE=en",
|
||||
"_APP_OPTIONS_ABUSE=enabled",
|
||||
"_APP_OPTIONS_FORCE_HTTPS=disabled",
|
||||
"_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled",
|
||||
"_APP_OPTIONS_ROUTER_PROTECTION=disabled",
|
||||
"_APP_OPENSSL_KEY_V1=your-secret-key",
|
||||
"_APP_DOMAIN=${main_domain}",
|
||||
"_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_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_USAGE_STATS=enabled",
|
||||
"_APP_LOGGING_PROVIDER=",
|
||||
"_APP_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_HOST=mariadb",
|
||||
"_APP_DB_PORT=3306",
|
||||
"_APP_DB_SCHEMA=appwrite",
|
||||
"_APP_DB_USER=user",
|
||||
"_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=",
|
||||
"_APP_SMTP_USERNAME=",
|
||||
"_APP_SMTP_PASSWORD=",
|
||||
"_APP_SMS_PROVIDER=",
|
||||
"_APP_SMS_FROM=",
|
||||
"_APP_STORAGE_LIMIT=30000000",
|
||||
"_APP_STORAGE_PREVIEW_LIMIT=20000000",
|
||||
"_APP_STORAGE_ANTIVIRUS=disabled",
|
||||
"_APP_STORAGE_ANTIVIRUS_HOST=clamav",
|
||||
"_APP_STORAGE_ANTIVIRUS_PORT=3310",
|
||||
"_APP_STORAGE_DEVICE=local",
|
||||
"_APP_STORAGE_S3_ACCESS_KEY=",
|
||||
"_APP_STORAGE_S3_SECRET=",
|
||||
"_APP_STORAGE_S3_REGION=us-east-1",
|
||||
"_APP_STORAGE_S3_BUCKET=",
|
||||
"_APP_STORAGE_DO_SPACES_ACCESS_KEY=",
|
||||
"_APP_STORAGE_DO_SPACES_SECRET=",
|
||||
"_APP_STORAGE_DO_SPACES_REGION=us-east-1",
|
||||
"_APP_STORAGE_DO_SPACES_BUCKET=",
|
||||
"_APP_STORAGE_BACKBLAZE_ACCESS_KEY=",
|
||||
"_APP_STORAGE_BACKBLAZE_SECRET=",
|
||||
"_APP_STORAGE_BACKBLAZE_REGION=us-west-004",
|
||||
"_APP_STORAGE_BACKBLAZE_BUCKET=",
|
||||
"_APP_STORAGE_LINODE_ACCESS_KEY=",
|
||||
"_APP_STORAGE_LINODE_SECRET=",
|
||||
"_APP_STORAGE_LINODE_REGION=eu-central-1",
|
||||
"_APP_STORAGE_LINODE_BUCKET=",
|
||||
"_APP_STORAGE_WASABI_ACCESS_KEY=",
|
||||
"_APP_STORAGE_WASABI_SECRET=",
|
||||
"_APP_STORAGE_WASABI_REGION=eu-central-1",
|
||||
"_APP_STORAGE_WASABI_BUCKET=",
|
||||
"_APP_FUNCTIONS_SIZE_LIMIT=30000000",
|
||||
"_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000",
|
||||
"_APP_FUNCTIONS_TIMEOUT=900",
|
||||
"_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_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_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=",
|
||||
]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite-console"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/console"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite-realtime"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/v1/realtime"
|
||||
@@ -0,0 +1,139 @@
|
||||
variables:
|
||||
main_domain: ${domain}
|
||||
|
||||
config:
|
||||
domains:
|
||||
- serviceName: appwrite
|
||||
port: 80
|
||||
host: ${main_domain}
|
||||
path: /
|
||||
- serviceName: appwrite-console
|
||||
port: 80
|
||||
host: ${main_domain}
|
||||
path: /console
|
||||
- serviceName: appwrite-realtime
|
||||
port: 80
|
||||
host: ${main_domain}
|
||||
path: /v1/realtime
|
||||
|
||||
env:
|
||||
- _APP_ENV=production
|
||||
- _APP_LOCALE=en
|
||||
- _APP_OPTIONS_ABUSE=enabled
|
||||
- _APP_OPTIONS_FORCE_HTTPS=disabled
|
||||
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled
|
||||
- _APP_OPTIONS_ROUTER_PROTECTION=disabled
|
||||
- _APP_OPENSSL_KEY_V1=your-secret-key
|
||||
- _APP_DOMAIN=${main_domain}
|
||||
- _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_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_USAGE_STATS=enabled
|
||||
- _APP_LOGGING_PROVIDER=
|
||||
- _APP_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_HOST=mariadb
|
||||
- _APP_DB_PORT=3306
|
||||
- _APP_DB_SCHEMA=appwrite
|
||||
- _APP_DB_USER=user
|
||||
- _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=
|
||||
- _APP_SMTP_USERNAME=
|
||||
- _APP_SMTP_PASSWORD=
|
||||
- _APP_SMS_PROVIDER=
|
||||
- _APP_SMS_FROM=
|
||||
- _APP_STORAGE_LIMIT=30000000
|
||||
- _APP_STORAGE_PREVIEW_LIMIT=20000000
|
||||
- _APP_STORAGE_ANTIVIRUS=disabled
|
||||
- _APP_STORAGE_ANTIVIRUS_HOST=clamav
|
||||
- _APP_STORAGE_ANTIVIRUS_PORT=3310
|
||||
- _APP_STORAGE_DEVICE=local
|
||||
- _APP_STORAGE_S3_ACCESS_KEY=
|
||||
- _APP_STORAGE_S3_SECRET=
|
||||
- _APP_STORAGE_S3_REGION=us-east-1
|
||||
- _APP_STORAGE_S3_BUCKET=
|
||||
- _APP_STORAGE_DO_SPACES_ACCESS_KEY=
|
||||
- _APP_STORAGE_DO_SPACES_SECRET=
|
||||
- _APP_STORAGE_DO_SPACES_REGION=us-east-1
|
||||
- _APP_STORAGE_DO_SPACES_BUCKET=
|
||||
- _APP_STORAGE_BACKBLAZE_ACCESS_KEY=
|
||||
- _APP_STORAGE_BACKBLAZE_SECRET=
|
||||
- _APP_STORAGE_BACKBLAZE_REGION=us-west-004
|
||||
- _APP_STORAGE_BACKBLAZE_BUCKET=
|
||||
- _APP_STORAGE_LINODE_ACCESS_KEY=
|
||||
- _APP_STORAGE_LINODE_SECRET=
|
||||
- _APP_STORAGE_LINODE_REGION=eu-central-1
|
||||
- _APP_STORAGE_LINODE_BUCKET=
|
||||
- _APP_STORAGE_WASABI_ACCESS_KEY=
|
||||
- _APP_STORAGE_WASABI_SECRET=
|
||||
- _APP_STORAGE_WASABI_REGION=eu-central-1
|
||||
- _APP_STORAGE_WASABI_BUCKET=
|
||||
- _APP_FUNCTIONS_SIZE_LIMIT=30000000
|
||||
- _APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000
|
||||
- _APP_FUNCTIONS_TIMEOUT=900
|
||||
- _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_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_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=
|
||||
|
||||
mounts: []
|
||||
@@ -1,12 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
auth_secret = "${base64:32}"
|
||||
|
||||
[config]
|
||||
env = ["APTABASE_HOST=${main_domain}", "AUTH_SECRET=${auth_secret}"]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "aptabase"
|
||||
port = 8_080
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,15 @@
|
||||
variables:
|
||||
main_domain: ${domain}
|
||||
auth_secret: ${base64:32}
|
||||
|
||||
config:
|
||||
domains:
|
||||
- serviceName: aptabase
|
||||
port: 8080
|
||||
host: ${main_domain}
|
||||
|
||||
env:
|
||||
- APTABASE_HOST=${main_domain}
|
||||
- AUTH_SECRET=${auth_secret}
|
||||
|
||||
mounts: []
|
||||
@@ -1,14 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
arangodb:
|
||||
image: arangodb:3.12.4
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8529
|
||||
environment:
|
||||
- ARANGO_ROOT_PASSWORD=${ARANGO_PASSWORD}
|
||||
volumes:
|
||||
- data:/var/lib/arangodb3
|
||||
|
||||
volumes:
|
||||
data: {}
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -1,12 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
arango_password = "${password:16}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "arangodb"
|
||||
port = 8529
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
ARANGO_PASSWORD = "${arango_password}"
|
||||
@@ -1,77 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
argilla-web:
|
||||
image: argilla/argilla-server:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 6900
|
||||
environment:
|
||||
- ARGILLA_HOME_PATH=/var/lib/argilla
|
||||
- ARGILLA_ELASTICSEARCH=http://argilla-elasticsearch:9200
|
||||
- ARGILLA_DATABASE_URL=postgresql+asyncpg://postgres:${DB_PASSWORD}@argilla-db:5432/argilla
|
||||
- ARGILLA_REDIS_URL=redis://:${REDIS_PASSWORD}@argilla-redis:6379/0
|
||||
- USERNAME=${LOGIN_USERNAME}
|
||||
- PASSWORD=${LOGIN_PASSWORD}
|
||||
- API_KEY=argilla.apikey
|
||||
- WORKSPACE=default
|
||||
volumes:
|
||||
- argilladata:/var/lib/argilla
|
||||
depends_on:
|
||||
- argilla-elasticsearch
|
||||
- argilla-db
|
||||
- argilla-redis
|
||||
|
||||
argilla-worker:
|
||||
image: argilla/argilla-server:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- BACKGROUND_NUM_WORKERS=2
|
||||
- ARGILLA_HOME_PATH=/var/lib/argilla
|
||||
- ARGILLA_ELASTICSEARCH=http://argilla-elasticsearch:9200
|
||||
- ARGILLA_DATABASE_URL=postgresql+asyncpg://postgres:${DB_PASSWORD}@argilla-db:5432/argilla
|
||||
- ARGILLA_REDIS_URL=redis://:${REDIS_PASSWORD}@argilla-redis:6379/0
|
||||
volumes:
|
||||
- argilladata:/var/lib/argilla
|
||||
command: python -m argilla_server worker --num-workers ${BACKGROUND_NUM_WORKERS}
|
||||
depends_on:
|
||||
- argilla-elasticsearch
|
||||
- argilla-db
|
||||
- argilla-redis
|
||||
|
||||
argilla-elasticsearch:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.2
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- node.name=elasticsearch
|
||||
- cluster.name=es-argilla-local
|
||||
- discovery.type=single-node
|
||||
- ES_JAVA_OPTS=-Xms512m -Xmx512m
|
||||
- cluster.routing.allocation.disk.threshold_enabled=false
|
||||
- xpack.security.enabled=false
|
||||
volumes:
|
||||
- elasticdata:/usr/share/elasticsearch/data
|
||||
|
||||
argilla-db:
|
||||
image: postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
||||
- POSTGRES_DB=argilla
|
||||
volumes:
|
||||
- dbdata:/var/lib/postgresql/data
|
||||
|
||||
argilla-redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD}
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
|
||||
volumes:
|
||||
argilladata: {}
|
||||
elasticdata: {}
|
||||
dbdata: {}
|
||||
redisdata: {}
|
||||
@@ -1,12 +0,0 @@
|
||||
<svg width="481" height="252" viewBox="0 0 481 252" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M125.336 251.397C194.557 251.397 250.671 195.216 250.671 125.913C250.671 56.6109 194.557 0.430069 125.336 0.430069C56.1147 0.430069 0 56.6109 0 125.913C0 195.216 56.1147 251.397 125.336 251.397Z" fill="url(#paint0_linear_0_1)"/>
|
||||
<path d="M479.93 251.397C342.032 251.397 229.83 139.062 229.83 1.00175H317.193C317.193 91.0411 390.282 164.216 480.215 164.216V251.397H479.93Z" fill="#F8C0A7"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_0_1" x1="55.414" y1="60.2073" x2="211.749" y2="206.798" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.0151877" stop-color="#FF675F"/>
|
||||
<stop offset="1" stop-color="#F73D3D"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 751 B |
@@ -1,19 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
login_username = "${username}"
|
||||
login_password = "${password:8}"
|
||||
db_password = "${password:16}"
|
||||
redis_password = "${password:16}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "argilla-web"
|
||||
port = 6900
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
LOGIN_USERNAME = "${login_username}"
|
||||
LOGIN_PASSWORD = "${login_password}"
|
||||
DB_PASSWORD = "${db_password}"
|
||||
REDIS_PASSWORD = "${redis_password}"
|
||||
BACKGROUND_NUM_WORKERS = "2"
|
||||
@@ -1,17 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
audiobookshelf:
|
||||
image: ghcr.io/advplyr/audiobookshelf:2.19.4
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 80
|
||||
environment:
|
||||
- TZ=UTC
|
||||
volumes:
|
||||
- config:/config
|
||||
- metadata:/metadata
|
||||
- ${AUDIOBOOKS_PATH}:/audiobooks
|
||||
|
||||
volumes:
|
||||
config: {}
|
||||
metadata: {}
|
||||
|
Before Width: | Height: | Size: 20 KiB |
@@ -1,12 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
audiobooks_path = "/path/to/your/audiobooks"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "audiobookshelf"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
AUDIOBOOKS_PATH = "${audiobooks_path}"
|
||||
|
Before Width: | Height: | Size: 48 KiB |
@@ -1,55 +0,0 @@
|
||||
services:
|
||||
authelia:
|
||||
image: authelia/authelia:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- authelia_config:/config
|
||||
- ../files/configuration.yml:/config/configuration.yml:ro
|
||||
- ../files/users_database.yml:/config/users_database.yml
|
||||
environment:
|
||||
AUTHELIA_JWT_SECRET: $JWT_SECRET
|
||||
AUTHELIA_SESSION_SECRET: $SESSION_SECRET
|
||||
AUTHELIA_STORAGE_ENCRYPTION_KEY: $STORAGE_ENCRYPTION_KEY
|
||||
AUTHELIA_STORAGE_POSTGRES_PASSWORD: $POSTGRES_PASSWORD
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- 9091
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
command: redis-server --save 60 1 --loglevel warning --requirepass $REDIS_PASSWORD
|
||||
environment:
|
||||
REDIS_PASSWORD: $REDIS_PASSWORD
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: authelia
|
||||
POSTGRES_USER: authelia
|
||||
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U authelia -d authelia"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
|
||||
volumes:
|
||||
authelia_config:
|
||||
redis_data:
|
||||
postgres_data:
|
||||
@@ -1,195 +0,0 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
jwt_secret = "${password:64}"
|
||||
session_secret = "${password:64}"
|
||||
storage_encryption_key = "${password:64}"
|
||||
redis_password = "${password:32}"
|
||||
postgres_password = "${password:32}"
|
||||
admin_username = "${username}"
|
||||
admin_email = "${email}"
|
||||
admin_password = "AdminPass123!"
|
||||
admin_password_hash = "$argon2id$v=19$m=65536,t=3,p=4$170PGJ1MskQyxfFknfBPFQ$VqD1/pqC3fBHo+Zk58bC2xQm1ltOFTr0w2wx93vJgC4"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "authelia"
|
||||
port = 9091
|
||||
host = "${main_domain}"
|
||||
path = "/"
|
||||
|
||||
[config.env]
|
||||
JWT_SECRET = "${jwt_secret}"
|
||||
SESSION_SECRET = "${session_secret}"
|
||||
STORAGE_ENCRYPTION_KEY = "${storage_encryption_key}"
|
||||
REDIS_PASSWORD = "${redis_password}"
|
||||
POSTGRES_PASSWORD = "${postgres_password}"
|
||||
admin_username = "${admin_username}"
|
||||
admin_email = "${admin_email}"
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "configuration.yml"
|
||||
content = """
|
||||
###############################################################
|
||||
# Authelia configuration #
|
||||
###############################################################
|
||||
|
||||
# DEFAULT ADMIN CREDENTIALS:
|
||||
# Username: (auto-generated, check users_database.yml)
|
||||
# Password: AdminPass123!
|
||||
# Email: (auto-generated)
|
||||
#
|
||||
# IMPORTANT: Change the password after first login!
|
||||
# SECURITY NOTE: This template starts with one-factor auth for easier setup.
|
||||
# After configuring SMTP/notifications, change the policy to 'two_factor'
|
||||
|
||||
# Server Configuration
|
||||
server:
|
||||
address: 'tcp://0.0.0.0:9091'
|
||||
headers:
|
||||
csp_template: ''
|
||||
|
||||
# Log Configuration
|
||||
log:
|
||||
level: info
|
||||
format: text
|
||||
|
||||
# Theme
|
||||
theme: auto
|
||||
|
||||
# TOTP Configuration
|
||||
totp:
|
||||
disable: false
|
||||
issuer: authelia.com
|
||||
algorithm: sha1
|
||||
digits: 6
|
||||
period: 30
|
||||
skew: 1
|
||||
secret_size: 32
|
||||
|
||||
# WebAuthn/FIDO2 Configuration
|
||||
webauthn:
|
||||
disable: false
|
||||
timeout: 60s
|
||||
display_name: Authelia
|
||||
attestation_conveyance_preference: indirect
|
||||
user_verification: preferred
|
||||
|
||||
# NTP Configuration
|
||||
ntp:
|
||||
address: 'time.cloudflare.com:123'
|
||||
version: 4
|
||||
max_desync: 3s
|
||||
disable_startup_check: false
|
||||
disable_failure: false
|
||||
|
||||
# Authentication Backend Configuration
|
||||
authentication_backend:
|
||||
password_reset:
|
||||
disable: false
|
||||
custom_url: ''
|
||||
refresh_interval: 5m
|
||||
file:
|
||||
path: /config/users_database.yml
|
||||
watch: false
|
||||
search:
|
||||
email: false
|
||||
case_insensitive: false
|
||||
password:
|
||||
algorithm: argon2
|
||||
argon2:
|
||||
variant: argon2id
|
||||
iterations: 3
|
||||
memory: 65536
|
||||
parallelism: 4
|
||||
key_length: 32
|
||||
salt_length: 16
|
||||
|
||||
# Password Policy
|
||||
password_policy:
|
||||
standard:
|
||||
enabled: false
|
||||
min_length: 8
|
||||
max_length: 0
|
||||
require_uppercase: true
|
||||
require_lowercase: true
|
||||
require_number: true
|
||||
require_special: true
|
||||
zxcvbn:
|
||||
enabled: false
|
||||
min_score: 3
|
||||
|
||||
# Session Configuration
|
||||
session:
|
||||
name: authelia_session
|
||||
domain: ${main_domain}
|
||||
same_site: lax
|
||||
secret: ${session_secret}
|
||||
expiration: 1h
|
||||
inactivity: 5m
|
||||
remember_me_duration: 1M
|
||||
redis:
|
||||
host: redis
|
||||
port: 6379
|
||||
password: ${redis_password}
|
||||
database_index: 0
|
||||
maximum_active_connections: 8
|
||||
minimum_idle_connections: 0
|
||||
|
||||
# Storage Configuration
|
||||
storage:
|
||||
encryption_key: ${storage_encryption_key}
|
||||
postgres:
|
||||
host: postgres
|
||||
port: 5432
|
||||
database: authelia
|
||||
schema: public
|
||||
username: authelia
|
||||
password: ${postgres_password}
|
||||
timeout: 5s
|
||||
|
||||
# Notifier Configuration
|
||||
notifier:
|
||||
disable_startup_check: true
|
||||
filesystem:
|
||||
filename: /config/notification.txt
|
||||
|
||||
# Regulation Configuration
|
||||
regulation:
|
||||
max_retries: 3
|
||||
find_time: 10m
|
||||
ban_time: 12h
|
||||
|
||||
# Access Control Configuration - MODIFIED FOR EASIER INITIAL SETUP
|
||||
access_control:
|
||||
default_policy: deny
|
||||
rules:
|
||||
- domain: ${main_domain}
|
||||
policy: one_factor # Changed from one_factor to two_factor for production
|
||||
"""
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "users_database.yml"
|
||||
content = """
|
||||
###############################################################
|
||||
# Users Database #
|
||||
###############################################################
|
||||
|
||||
# DEFAULT LOGIN CREDENTIALS:
|
||||
# Username: (generated from username helper)
|
||||
# Password: AdminPass123!
|
||||
# Email: (generated from email helper)
|
||||
#
|
||||
# IMPORTANT: Change the default password after first login!
|
||||
# To generate a new password hash, run:
|
||||
# docker run authelia/authelia:latest authelia hash-password 'your-new-password'
|
||||
|
||||
users:
|
||||
${admin_username}:
|
||||
disabled: false
|
||||
displayname: "Authelia Admin"
|
||||
password: "${admin_password_hash}"
|
||||
email: ${admin_email}
|
||||
groups:
|
||||
- admins
|
||||
- dev
|
||||
"""
|
||||