diff --git a/.commitlintrc.js b/.commitlintrc.js deleted file mode 100644 index 28c36622..00000000 --- a/.commitlintrc.js +++ /dev/null @@ -1,14 +0,0 @@ -import commitlintConfigConventional from "@commitlint/config-conventional" - -const ruleMaxLineLength = - commitlintConfigConventional.rules["body-max-line-length"]; - -ruleMaxLineLength[0] = process.env.CI === "true" ? 1 : 2; - -export default { - extends: ["@commitlint/config-conventional"], - rules: { - "body-max-line-length": ruleMaxLineLength, - "footer-max-line-length": ruleMaxLineLength, - }, -}; diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index f78d206a..46823ac1 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,12 +1 @@ -# These are supported funding model platforms - -github: [dargmuesli] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] +github: [dargmuesli] \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d408ce0..395b18d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,22 +14,40 @@ on: jobs: ci-optimization: name: CI optimization - uses: dargmuesli/github-actions/.github/workflows/ci-optimization.yml@661e39fe1c9e484d94c6a5a9d4c9946d57c41771 # 5.1.0 + uses: dargmuesli/github-actions/.github/workflows/ci-optimization.yml@d8fbc998656a0a3476bfdb091c216c3cf78cdc94 # 5.1.1 permissions: pull-requests: read build: needs: ci-optimization if: needs.ci-optimization.outputs.continue == 'true' - name: dargstack rgen - uses: dargmuesli/github-actions/.github/workflows/dargstack-rgen.yml@661e39fe1c9e484d94c6a5a9d4c9946d57c41771 # 5.1.0 + name: Update generated docs + runs-on: ubuntu-latest permissions: - contents: read - with: - APT_PACKAGES: mkcert + contents: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: ${{ github.head_ref || github.ref_name }} + - name: Regenerate docs + run: | + go install github.com/dargstack/dargstack/v4/cmd/dargstack@latest + export PATH="$(go env GOPATH)/bin:$PATH" + dargstack document + - name: Commit and push updated docs + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add artifacts/docs/ + if git diff --staged --quiet; then + echo "Docs are already up to date" + else + git commit -m "chore: update generated docs" + git push + fi release-semantic: needs: build name: Semantic Release - uses: dargmuesli/github-actions/.github/workflows/release-semantic.yml@661e39fe1c9e484d94c6a5a9d4c9946d57c41771 # 5.1.0 + uses: dargmuesli/github-actions/.github/workflows/release-semantic.yml@d8fbc998656a0a3476bfdb091c216c3cf78cdc94 # 5.1.1 permissions: contents: write id-token: write diff --git a/.github/workflows/release-schedule.yml b/.github/workflows/release-schedule.yml index 09b79f5c..9901dd4c 100644 --- a/.github/workflows/release-schedule.yml +++ b/.github/workflows/release-schedule.yml @@ -10,7 +10,7 @@ on: jobs: release-schedule: name: "Release: Scheduled" - uses: dargmuesli/github-actions/.github/workflows/release-schedule.yml@661e39fe1c9e484d94c6a5a9d4c9946d57c41771 # 5.1.0 + uses: dargmuesli/github-actions/.github/workflows/release-schedule.yml@d8fbc998656a0a3476bfdb091c216c3cf78cdc94 # 5.1.1 permissions: contents: read secrets: diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100755 index 2b99f23e..00000000 --- a/.husky/commit-msg +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -export NVM_DIR="$HOME/.nvm" -# shellcheck source=/dev/null -[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - -pnpm commitlint --edit "$1" \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100644 index 862a47b8..00000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -export NVM_DIR="$HOME/.nvm" -# shellcheck source=/dev/null -[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - -pnpm dargstack_rgen --validate diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index a45fd52c..00000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -24 diff --git a/.releaserc.json b/.releaserc.json index 38b975bb..1f79157c 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -15,9 +15,7 @@ } ], "@semantic-release/changelog", - "@semantic-release/npm", "@semantic-release/github", "@semantic-release/git" - ], - "tagFormat": "${version}" + ] } diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..d1b2554a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,44 @@ +--- +applyTo: '**' +--- +# Project Instructions + +`stack` is the Docker Swarm configuration for [vibetype.app](https://vibetype.app/), an event community platform, managed with [dargstack](https://github.com/dargstack/dargstack/). + +## Documentation Map + +**For understanding the stack structure and deployment:** +- [README.md](README.md): Project overview, quick start +- [artifacts/docs/README.md](artifacts/docs/README.md): Auto-generated service, secret and volume reference (do not edit manually) + +**For contributing:** +- [CONTRIBUTING.md](CONTRIBUTING.md): Development setup, dargstack guidelines, code style, git workflow + +## Code Style + +- Do not use abbreviations in naming, except where omitting them would look unnatural +- Use natural language in any non-code text instead of referring to code directly, e.g. "the database's password" instead of "the `postgres_password`", except when a code reference is needed +- Use backticks in any non-code text to refer to code, e.g. "`postgres`" instead of "postgres" +- Sort YAML keys lexicographically except where order is semantically significant +- Code formatting is done by the editor via `.editorconfig` + +## Git + +- Work on branches other than the default branch + - Use this branch naming pattern: `//` +- Git commit titles must follow the Conventional Commits specification and be lowercase only + - The commit scope should not be repeated in the commit description, e.g. `feat(postgres): add role` instead of `feat(postgres): add postgres role` +- Git commit scopes must be chosen as follows (ordered by priority): + 1. service name, e.g. `postgres`, `traefik`, `vibetype` + 2. simplified dependency name, e.g. `dargstack`, `docker` + 3. area, e.g. `secrets`, `volumes`, `certificates` +- Commit bodies are only to be filled in when necessary, e.g. to mention a resolved issue link + +## Docker / dargstack + +- Each service lives under `src/development//compose.yaml` as a full Compose document and optionally `src/production//compose.yaml` as a delta-only override +- Lines annotated with `# dargstack:dev-only` are stripped from production compose +- Run `dargstack build ` after changing a service's source code to rebuild its development container image +- Run `dargstack validate` to check the stack configuration +- Run `dargstack document` to regenerate `artifacts/docs/README.md` (do not edit that file manually) +- Do not commit files from `artifacts/` unless they are tracked (e.g. `artifacts/docs/SERVICES_ADDITIONAL.md`) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..32a53e0c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,130 @@ +# Contributing to stack + +Thank you for your interest in contributing! + +The fullstack environment composes several services, among those are the following first-party: + +| Repository | Required | Profile | Access | +|---|---|---|---| +| [maevsi/android](https://github.com/maevsi/android) | optional | — | public | +| [maevsi/ios](https://github.com/maevsi/ios) | optional | — | public | +| [maevsi/postgraphile](https://github.com/maevsi/postgraphile) | ✅ | `default` | public | +| [maevsi/reccoom](https://github.com/maevsi/reccoom) | optional | `recommendation` | private | +| [maevsi/sqitch](https://github.com/maevsi/sqitch) | ✅ | `default` | public | +| [maevsi/stack](https://github.com/maevsi/stack) | ✅ | — | public | +| [maevsi/vibetype](https://github.com/maevsi/vibetype) | ✅ | `default` | public | + +## Development Setup + +There are two development modes: + +| Mode | When to use | Setup | Where to start | +|---|---|---|---| +| **Frontend only** | Working on UI, i18n, or anything that doesn't require running backend services | Manual only | [Vibetype repository](https://github.com/maevsi/vibetype#development) | +| **Fullstack** | Working on backend services, the database, the API, or any cross-cutting concern | Automated or manual | This guide (continue reading) | + +> 🪟 **Windows users:** Set up [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) first and continue inside the Linux subsystem. +> If you use VS Code, see [VS Code + WSL](https://learn.microsoft.com/en-us/windows/wsl/tutorials/wsl-vscode). + +### Automated Setup + +Clone this repository into the directory where you want the project to live, then run the setup script from inside the cloned `stack` repository: +```sh +cd /path/to/where/you/want/the/project +git clone https://github.com/maevsi/stack.git +cd stack + +```sh +bash scripts/setup.sh +``` + +The script will interactively ask which optional feature sets you want, then: + +1. Clone the selected repositories as siblings inside a `vibetype/` parent directory. +2. Run `dargstack build ` for the selected services to build the required development container images. +3. Deploy the development stack with `dargstack deploy`. + +> Per-repository setup (e.g. Node.js install for `vibetype`) is not yet automated here. Each repository will eventually provide its own `scripts/setup.sh` that this script will invoke. In the meantime, follow the manual steps below for repository-specific preparation. + +### Manual Setup + +If you prefer to step through each action yourself: + +1. Install prerequisites + + 1. [Git](https://git-scm.com/): version control + 2. [Docker](https://docs.docker.com/engine/install/): container runtime + 3. [dargstack](https://github.com/dargstack/dargstack#install): stack management CLI + + +1. Create a parent directory and clone the sibling repositories into it: + + ```sh + mkdir vibetype && cd vibetype + git clone git@github.com:maevsi/android.git # optional + git clone git@github.com:maevsi/ios.git # optional + git clone git@github.com:maevsi/postgraphile.git + git clone git@github.com:maevsi/reccoom.git # optional, private + git clone git@github.com:maevsi/sqitch.git + git clone git@github.com:maevsi/stack.git + git clone git@github.com:maevsi/vibetype.git + ``` + +
+ Click here if you don't have SSH set up (you should!) to use HTTPS URLs instead + + + ```sh + mkdir vibetype && cd vibetype + git clone https://github.com/maevsi/android.git # optional + git clone https://github.com/maevsi/ios.git # optional + git clone https://github.com/maevsi/postgraphile.git + git clone https://github.com/maevsi/reccoom.git # optional, private + git clone https://github.com/maevsi/sqitch.git + git clone https://github.com/maevsi/stack.git + git clone https://github.com/maevsi/vibetype.git + ``` +
+ +2. Initialize all cloned projects for development according to their READMEs. + +3. Build development container images: + + ```sh + cd stack + dargstack build + ``` + + An interactive selection dialog will let you choose which services to build. + +4. Deploy: + + ```sh + dargstack deploy + ``` + +5. You should now be able to access Vibetype at [https://app.localhost](https://app.localhost) 🎉 + + +## Guidelines + +### Git & GitHub + +Follow [@dargmuesli's Contributing Guidelines](https://gist.github.com/dargmuesli/430b7d902a22df02d88d1969a22a81b5#contribution-workflow) for branch naming, commit formatting, and the pull request workflow. + +### Semantic Versioning + +Read [@dargmuesli's guide on Semantic Versioning](https://gist.github.com/dargmuesli/430b7d902a22df02d88d1969a22a81b5#file-semantic-versioning-md) for how to format PR, issue and commit titles. + +### dargstack + +- Service files live in `src/development//compose.yaml` (full Compose document) and `src/production//compose.yaml` (production delta only). +- Run `dargstack build` to interactively select and build development container images after making changes to a service's source code. +- Run `dargstack document` to regenerate `artifacts/docs/README.md` after adding or modifying services. +- Do not edit `artifacts/` files directly: they are generated or gitignored. + +### Code Style + +- Keep YAML keys sorted lexicographically where order is semantically irrelevant. +- Use natural language in comments; refer to code artifacts with backticks. +- Do not use abbreviations in names unless omitting them would look unnatural. diff --git a/Dockerfile.md b/Dockerfile.md new file mode 100644 index 00000000..9a2d53fb --- /dev/null +++ b/Dockerfile.md @@ -0,0 +1,3 @@ +# +FROM ghcr.io/dargstack/dargstack:4.0.0-beta.7 +# diff --git a/README.md b/README.md index 7b8e6434..c04389c0 100644 --- a/README.md +++ b/README.md @@ -1,411 +1,32 @@ -# stack +[![ci status][ci-image]][ci-url] +[ci-image]: https://img.shields.io/github/actions/workflow/status/maevsi/stack/ci.yml +[ci-url]: https://github.com/maevsi/stack/actions/workflows/ci.yml -The Docker stack configuration for [vibetype.app](https://vibetype.app/). - -This project is deployed in accordance to the [DargStack template](https://github.com/dargstack/dargstack_template/) to make deployment a breeze. It is closely related to [Vibetype's source code](https://github.com/maevsi/vibetype/). - -## Table of Contents - +# @maevsi/stack - 1. [x-shared](#x-shared) - - 2. [secrets](#secrets) - - 3. [services](#services) - - 4. [volumes](#volumes) - - -## x-shared - - - - ### `zammad-service` - - You can access the helpdesk at [zammad.app.localhost](https://zammad.app.localhost/). - +The Docker stack configuration for [vibetype.app](https://vibetype.app/). -## secrets +This project is managed with [dargstack](https://github.com/dargstack/dargstack/) and is closely related to [Vibetype's source code](https://github.com/maevsi/vibetype/). +## Documentation - - ### `elasticsearch-keystore_password` - - The search engine's password for the keystore. - - - ### `elasticsearch-password` - - The search engine's password for the default user. - - - ### `grafana_admin_email` - - The observation dashboard's admin email. - - - ### `grafana_admin_password` - - The observation dashboard's admin password. - - - ### `grafana_admin_user` - - The observation dashboard's admin user. - - - ### `grafana_discord_webhook` - - The observation dashboard's contact point for Discord. - - - ### `jobber_aliases` - - The job scheduler's SMTP client mail alias. - - - ### `jobber_aws-bucket` - - The job scheduler's AWS bucket name. - - - ### `jobber_aws-configuration` - - The job scheduler's AWS configuration. - - - ### `jobber_aws-credentials` - - The job scheduler's AWS credentials. - - - ### `jobber_msmtprc` - - The job scheduler's SMTP client configuration. - - - ### `portainer_admin-password` - - The container manager's admin password. - - - ### `postgraphile_connection` - - The GraphQL API's database URI. - - - ### `postgraphile_jwt-secret` - - The GraphQL API's JWT secret. - - - ### `postgraphile_owner-connection` - - The GraphQL API's database owner URI. - - - ### `postgres-backup_db` ![production](https://img.shields.io/badge/-production-informational.svg?style=flat-square) - - The database's name. - - - ### `postgres_db` - - The database's name. - - - ### `postgres_password` - - The database's password. - - - ### `postgres_role_service_grafana_password` - - The password of the observation dashboard's database role. - - - ### `postgres_role_service_grafana_username` - - The username of the observation dashboard's database role. - - - ### `postgres_role_service_postgraphile_password` - - The password of the GraphQL API database wrapper's database role. - - - ### `postgres_role_service_postgraphile_username` - - The username of the GraphQL API database wrapper's database role. - - - ### `postgres_role_service_vibetype_password` - - The `tusd` database role's password. - - - ### `postgres_role_service_vibetype_username` - - The `tusd` database role's password. - - - ### `postgres_role_service_zammad_password` - - The password of the customer service database role. - - - ### `postgres_role_service_zammad_username` - - The username of the customer service database role. - - - ### `postgres_user` - - The database's default user. - - - ### `reccoom_ingest-api-key` - - The AI provider's API key for the recommendation engine. - - - ### `reccoom_openai-api-key` - - The AI provider's API key for the recommendation engine. - - - ### `sqitch_target` - - The database change management application's database connection string. - - - ### `traefik_cf-dns-api-token` ![production](https://img.shields.io/badge/-production-informational.svg?style=flat-square) - - The DNS provider's DNS API token. - - - ### `traefik_cf-zone-api-token` ![production](https://img.shields.io/badge/-production-informational.svg?style=flat-square) - - The DNS provider's zone API token. - - - ### `tusd_aws` - - The upload service's s3 credentials file. - - - ### `vibetype_api-notification-secret` - - The notification endpoint's secret. - - - ### `vibetype_aws-credentials` - - The cloud computing provider's user credentials. - - - ### `vibetype_firebase-service-account-credentials` - - The notification provider's service account credentials. - - - ### `vibetype_monday` - - The project management software's configuration. - - - ### `vibetype_openai-api-key` - - The AI provider's API key for the frontend. - - - ### `vibetype_turnstile-key` - - The captcha provider's application key. - +To see which services, secrets and volumes this stack includes, head over to [`artifacts/docs/README.md`](artifacts/docs/README.md). -## services +## Development Setup +> 💡 **Windows users:** Run these steps inside [WSL](https://docs.microsoft.com/en-us/windows/wsl/install). - - ### `adminer` - - You can access the database's frontend at [adminer.app.localhost](https://adminer.app.localhost/). - This information is required for login: - - | | | - | -------- | ------------------- | - | System | PostgreSQL | - | Server | postgres | - | Username | [postgres_user] | - | Password | [postgres_password] | - | Database | [postgres_db] | - - Values in square brackets are [Docker secrets](https://docs.docker.com/engine/swarm/secrets/). - - - ### `cloudflared` ![production](https://img.shields.io/badge/-production-informational.svg?style=flat-square) - - You can configure the secure tunnel at [dash.cloudflare.com](https://dash.cloudflare.com/). - - - ### `debezium` - - You can see how changes in the database end up in the event stream using `redpanda-console`. - - - ### `debezium-postgres-connector` - - You can check the database connector's setup logs using `portainer`. - - - ### `elasticsearch` - - You cannot access the search engine via a web interface. - - - ### `geoip` - - You cannot access the ip geolocator via a web interface. - - - ### `grafana` - - You can access the observation dashboard at [grafana.app.localhost](https://grafana.app.localhost/). - - - ### `jobber` - - You cannot access the jobber via a web interface. - - - ### `memcached` - - You cannot access the caching system via a web interface. - - - ### `minio` ![development](https://img.shields.io/badge/-development-informational.svg?style=flat-square) - - You can access the s3 console at [minio.app.localhost](https://minio.app.localhost/). - You can access the s3 api service at [s3.app.localhost](https://s3.app.localhost/) if you want to access via cli from outside the stack. - - - ### `portainer` - - You can access the container manager's frontend at [portainer.app.localhost](https://portainer.app.localhost/). - - - ### `portainer-agent` - - You cannot access the container manager's agent directly. - - - ### `postgraphile` - - You can access the GraphQL API for the PostgreSQL database at [postgraphile.app.localhost](https://postgraphile.app.localhost/). - - - ### `postgres` - - You can access the database via `adminer`. - - - ### `postgres_backup` ![production](https://img.shields.io/badge/-production-informational.svg?style=flat-square) - - You cannot access the database backup directly. - - - ### `prometheus` - - You can access the metrics monitoring at [prometheus.app.localhost](https://prometheus.app.localhost/). - - - ### `reccoom` - - You cannot access the recommendation service directly. - - - ### `reccoom_postgres` - - You can access reccoom's database via `adminer`. - - - ### `redis` - - You cannot access the caching system via a web interface. - - - ### `redpanda` - - You can access the event streaming platform's ui as described under `redpanda-console`. - - - ### `redpanda-console` - - You can access the event streaming platform's ui at [redpanda.app.localhost](https://redpanda.app.localhost/). - - - ### `sqitch` - - You cannot access the database migrations directly. - - - ### `traefik` - - You can access the reverse proxy's dashboard at [traefik.app.localhost](https://traefik.app.localhost/). - - - ### `traefik_certs-dumper` ![production](https://img.shields.io/badge/-production-informational.svg?style=flat-square) - - You cannot access the reverse proxy's certificate helper directly. - - - ### `tusd` - - You can access the upload service at [tusd.app.localhost](https://tusd.app.localhost/). - - - ### `vibetype` - - You can access the main project's frontend at [app.localhost](https://app.localhost/). - - - ### `zammad-backup` - - You cannot access the helpdesk backup service via a web interface. - - - ### `zammad-init` - - You cannot access the helpdesk initialization service via a web interface. - - - ### `zammad-nginx` - - You can access the helpdesk at [zammad.app.localhost](https://zammad.app.localhost/). - - - ### `zammad-railsserver` - - You cannot access the helpdesk application server directly. - - - ### `zammad-scheduler` - - You cannot access the helpdesk scheduler directly. - - - ### `zammad-websocket` - - You cannot access the helpdesk websocket server directly. - +> 📖 **New to the project? Read [CONTRIBUTING.md](CONTRIBUTING.md) first** to understand the repository structure, the development modes, and what each component does. You will need this context to work effectively beyond the quick setup below. -## volumes +To start a local fullstack development environment, run the setup script from the directory where you want to clone the project: +```sh +bash <(curl -fsSL https://raw.githubusercontent.com/maevsi/stack/main/scripts/setup.sh) +``` - - ### `acme_data` ![production](https://img.shields.io/badge/-production-informational.svg?style=flat-square) - - The reverse proxy's certificate data. - - - ### `debezium_kafka_configuration` - - The change data capture's configuration. - - - ### `debezium_kafka_data` - - The change data capture's data. - - - ### `debezium_kafka_logs` - - The change data capture's logs. - - - ### `elasticsearch-configuration` - - The search engine's configuration. - - - ### `elasticsearch_data` - - The search engine's data. - - - ### `grafana_data` - - The observation dashboard's data. - - - ### `minio_data` - - The s3 server's data. - - - ### `pnpm_data` - - The node package manager's data. - - - ### `portainer_data` - - The container manager's data. - - - ### `postgraphile_data` - - The GraphQL API's data. - - - ### `postgres_data` - - The database's data. - - - ### `prometheus_data` - - The metrics monitoring's data. - - - ### `reccoom_postgres_data` - - The recommendation database's data. - - - ### `redis_data` - - The caching system's data. - - - ### `redpanda_data` - - The message queue's data. - - - ### `vibetype_data` - - The frontend's data. - - - ### `zammad-backup_data` - - The helpdesk backup's data. - - - ### `zammad_data` - - The helpdesk's data. - +Or, if you have already cloned this repository: +```sh +bash scripts/setup.sh +``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..a0733052 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,57 @@ +# Vibetype Disclosure Policy + +## 1 Introduction + +The security of Vibetype is a top priority for our team. We believe in transparency and collaboration with the community to identify and address potential security vulnerabilities. This disclosure policy outlines the procedures for reporting security issues in Vibetype and how we handle such reports. + +## 2 Reporting a Security Issue + +If you discover a security vulnerability in Vibetype, we encourage you to report it to us promptly. To report a security issue, please follow these steps: + +Send an email to contact+security@maev.si with the subject line "Security Issue: [Brief Description]." Please provide a detailed description of the vulnerability, including the following: + +- A concise summary of the issue. +- A detailed explanation of the vulnerability and its potential impact. +- Steps to reproduce the vulnerability (if applicable). +- Any additional information that can help us understand and address the issue. + +## 3 Handling of Reports + +Upon receiving a security report, the Vibetype team will follow these steps: + +### 3.1 Acknowledgment + +We will acknowledge your report as soon as possible and provide an estimated timeline for the review and resolution process. + +### 3.2 Evaluation + +Our team will review the reported issue to assess its validity and severity. We may request additional information from you if necessary. + +### 3.3 Resolution + +Once we have confirmed and understood the vulnerability, we will develop and test a fix. We will try to resolve the issue as quickly as possible. + +### 3.4 Communication + +We will keep you informed of our progress throughout the process and notify you when a fix is available. If the issue affects multiple projects, we may coordinate with other project maintainers and vendors. + +## 4 Disclosure + +We believe in responsible disclosure to protect our users. + +If the issue is resolved, we will coordinate with you to establish a mutually agreed-upon release date for the fix. +We will issue a security advisory and update the project's documentation once the fix is publicly available. + +## 5 Credit and Recognition + +We highly value the contributions of the security community and will provide credit to individuals or organizations who responsibly report security vulnerabilities. However, if you prefer to remain anonymous, we will respect your wishes. + +## 6 Legal Protection + +Vibetype will not pursue legal action against security researchers who follow this disclosure policy and act in good faith. We appreciate your efforts to help us maintain the security of our project. + +## 7 Changes to this Policy + +This security disclosure policy may be updated or revised from time to time. + +Last Updated: 2026-04-03 diff --git a/artifacts/.gitignore b/artifacts/.gitignore new file mode 100644 index 00000000..87bd3818 --- /dev/null +++ b/artifacts/.gitignore @@ -0,0 +1,4 @@ +audit-log/ +certificates/ +secrets/ +.env.merged \ No newline at end of file diff --git a/artifacts/docs/README.md b/artifacts/docs/README.md new file mode 100644 index 00000000..984f0154 --- /dev/null +++ b/artifacts/docs/README.md @@ -0,0 +1,172 @@ +# vibetype + +The Docker stack configuration for [vibetype.app](https://vibetype.app/). + +## Profiles + +### analytics + +Services: grafana, prometheus + +### default + +Services: adminer, cloudflared, portainer, portainer-agent, postgraphile, postgres, sqitch, traefik, vibetype + +### event-streaming + +Services: debezium, debezium-postgres-connector, redpanda, redpanda-console + +### recommendation + +Services: reccoom, reccoom_postgres + +### upload + +Services: minio, tusd + +### zammad + +Services: elasticsearch, memcached, redis, zammad-backup, zammad-init, zammad-nginx, zammad-railsserver, zammad-scheduler, zammad-websocket + +## Services + +### adminer + +You can access the database's frontend at [adminer.app.localhost](https://adminer.app.localhost/). +This information is required for login: + +| | | +| -------- | ------------------- | +| System | PostgreSQL | +| Server | postgres | +| Username | [postgres-user] | +| Password | [postgres-password] | +| Database | [postgres-db] | + +Values in square brackets are [Docker secrets](https://docs.docker.com/engine/swarm/secrets/). + +### cloudflared *(production only)* + +You can configure the secure tunnel at [dash.cloudflare.com](https://dash.cloudflare.com/). + +### debezium + +You can see how changes in the database end up in the event stream using `redpanda-console`. + +### debezium-postgres-connector + +You can check the database connector's setup logs using `portainer`. + +### elasticsearch + +You cannot access the search engine via a web interface. + +### geoip + +You cannot access the ip geolocator via a web interface. + +### grafana + +You can access the observation dashboard at [grafana.app.localhost](https://grafana.app.localhost/). + +### jobber + +You cannot access the jobber via a web interface. + +### memcached + +You cannot access the caching system via a web interface. + +### minio + +You can access the s3 console at [minio.app.localhost](https://minio.app.localhost/). +You can access the s3 api service at [s3.app.localhost](https://s3.app.localhost/) if you want to access via cli from outside the stack. + +### portainer + +You can access the container manager's frontend at [portainer.app.localhost](https://portainer.app.localhost/). + +### portainer-agent + +You cannot access the container manager's agent directly. + +### postgraphile + +You can access the GraphQL API for the PostgreSQL database at [postgraphile.app.localhost](https://postgraphile.app.localhost/). + +### postgres + +You can access the database via `adminer`. + +### postgres-backup *(production only)* + +You cannot access the database backup directly. + +### prometheus + +You can access the metrics monitoring at [prometheus.app.localhost](https://prometheus.app.localhost/). + +### reccoom + +You cannot access the recommendation service directly. + +### reccoom_postgres + +You can access reccoom's database via `adminer`. + +### redis + +You cannot access the caching system via a web interface. + +### redpanda + +You can access the event streaming platform's ui as described under `redpanda-console`. + +### redpanda-console + +You can access the event streaming platform's ui at [redpanda.app.localhost](https://redpanda.app.localhost/). + +### sqitch + +You cannot access the database migrations directly. + +### traefik + +You can access the reverse proxy's dashboard at [traefik.app.localhost](https://traefik.app.localhost/). + +### traefik-certs-dumper *(production only)* + +You cannot access the reverse proxy's certificate helper directly. + +### tusd + +You can access the upload service at [tusd.app.localhost](https://tusd.app.localhost/). + +### vibetype + +You can access the main project's frontend at [app.localhost](https://app.localhost/). + +### zammad-backup + +You cannot access the helpdesk backup service via a web interface. + +### zammad-init + +You cannot access the helpdesk initialization service via a web interface. + +### zammad-nginx + +You can access the helpdesk at [zammad.app.localhost](https://zammad.app.localhost/). + +### zammad-railsserver + +You cannot access the helpdesk application server directly. + +### zammad-scheduler + +You cannot access the helpdesk scheduler directly. + +### zammad-websocket + +You cannot access the helpdesk websocket server directly. + diff --git a/NOTES.md b/artifacts/docs/SERVICES_ADDITIONAL.md similarity index 91% rename from NOTES.md rename to artifacts/docs/SERVICES_ADDITIONAL.md index ce1846d5..3ac38388 100644 --- a/NOTES.md +++ b/artifacts/docs/SERVICES_ADDITIONAL.md @@ -1,4 +1,5 @@ # Additional Services + - ## `status` ![production](https://img.shields.io/badge/-production-informational.svg?style=flat-square) [![Website Uptime Monitoring](https://app.statuscake.com/button/index.php?Track=9CFPA32m2n&Days=1000&Design=6)](https://www.statuscake.com) - You can access the status dashboard at [status.vibetype.app](https://status.vibetype.app/) \ No newline at end of file + You can access the status dashboard at [status.vibetype.app](https://status.vibetype.app/) diff --git a/dargstack.env b/dargstack.env deleted file mode 100644 index aff39cd0..00000000 --- a/dargstack.env +++ /dev/null @@ -1 +0,0 @@ -PROJECT_NAME=vibetype \ No newline at end of file diff --git a/dargstack.yaml b/dargstack.yaml new file mode 100644 index 00000000..8e1cc7df --- /dev/null +++ b/dargstack.yaml @@ -0,0 +1,47 @@ +# Dargstack configuration file + +# # Stack name — used as Docker stack name and image tag prefix +name: "vibetype" + +# # Source code metadata (for documentation generation) +# source: +# name: "example" +# url: "https://github.com/example/example" + +##### + +# Version: This CLI is compatible with config versions < 1.0.0 +compatibility: ">=4.0.0-0 <5.0.0" + +# Sudo mode — if Docker requires sudo on this machine, set to "always" +# Options: "always", "never", "auto" (default) +sudo: "auto" + +# Behavior configuration +behavior: + build: + # Skip rebuilding images if they already exist + skip: true + prompt: + volume: + # Prompt to remove volumes before deploying (development only) + remove: true + +# Production environment settings +production: + # Stack domain — used by the public to reach the services + domain: "vibetype.app" + # Git branch for production deployments + branch: "main" + # Tag strategy for production — use "latest" to auto-detect from git tags + tag: "latest" + +# Development environment settings +development: + # Domain used for development deployments (defaults to "app.localhost"). + domain: "app.localhost" + certificate: + # Additional domains for development TLS certificates (beyond the auto-discovered ones). + domains: [] + # - "*.app.localhost" + # - "custom.localhost" \ No newline at end of file diff --git a/package.json b/package.json deleted file mode 100644 index 6ab49f14..00000000 --- a/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "author": "Jonas Thelemann ", - "description": "Dargstack configuration for Vibetype.", - "devDependencies": { - "@commitlint/cli": "20.4.3", - "@commitlint/config-conventional": "20.4.3", - "conventional-changelog-conventionalcommits": "9.3.0", - "dargstack": "3.0.0", - "dargstack_rgen": "0.9.86", - "husky": "9.1.7" - }, - "engines": { - "node": "24" - }, - "license": "GPL-3.0-only", - "name": "@maevsi/stack", - "packageManager": "pnpm@10.30.3", - "private": true, - "repository": "https://github.com/maevsi/stack.git", - "scripts": { - "prepare": "husky && ./src/development/certificates/mkcert.sh" - }, - "type": "module", - "version": "17.0.0-beta.4" -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index 7d4e1034..00000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,837 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - devDependencies: - '@commitlint/cli': - specifier: 20.4.3 - version: 20.4.3(@types/node@25.3.5)(typescript@5.9.3) - '@commitlint/config-conventional': - specifier: 20.4.3 - version: 20.4.3 - conventional-changelog-conventionalcommits: - specifier: 9.3.0 - version: 9.3.0 - dargstack: - specifier: 3.0.0 - version: 3.0.0 - dargstack_rgen: - specifier: 0.9.86 - version: 0.9.86 - husky: - specifier: 9.1.7 - version: 9.1.7 - -packages: - - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@commitlint/cli@20.4.3': - resolution: {integrity: sha512-Z37EMoDT7+Upg500vlr/vZrgRsb6Xc5JAA3Tv7BYbobnN/ZpqUeZnSLggBg2+1O+NptRDtyujr2DD1CPV2qwhA==} - engines: {node: '>=v18'} - hasBin: true - - '@commitlint/config-conventional@20.4.3': - resolution: {integrity: sha512-9RtLySbYQAs8yEqWEqhSZo9nYhbm57jx7qHXtgRmv/nmeQIjjMcwf6Dl+y5UZcGWgWx435TAYBURONaJIuCjWg==} - engines: {node: '>=v18'} - - '@commitlint/config-validator@20.4.3': - resolution: {integrity: sha512-jCZpZFkcSL3ZEdL5zgUzFRdytv3xPo8iukTe9VA+QGus/BGhpp1xXSVu2B006GLLb2gYUAEGEqv64kTlpZNgmA==} - engines: {node: '>=v18'} - - '@commitlint/ensure@20.4.3': - resolution: {integrity: sha512-WcXGKBNn0wBKpX8VlXgxqedyrLxedIlLBCMvdamLnJFEbUGJ9JZmBVx4vhLV3ZyA8uONGOb+CzW0Y9HDbQ+ONQ==} - engines: {node: '>=v18'} - - '@commitlint/execute-rule@20.0.0': - resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==} - engines: {node: '>=v18'} - - '@commitlint/format@20.4.3': - resolution: {integrity: sha512-UDJVErjLbNghop6j111rsHJYGw6MjCKAi95K0GT2yf4eeiDHy3JDRLWYWEjIaFgO+r+dQSkuqgJ1CdMTtrvHsA==} - engines: {node: '>=v18'} - - '@commitlint/is-ignored@20.4.3': - resolution: {integrity: sha512-W5VQKZ7fdJ1X3Tko+h87YZaqRMGN1KvQKXyCM8xFdxzMIf1KCZgN4uLz3osLB1zsFcVS4ZswHY64LI26/9ACag==} - engines: {node: '>=v18'} - - '@commitlint/lint@20.4.3': - resolution: {integrity: sha512-CYOXL23e+nRKij81+d0+dymtIi7Owl9QzvblJYbEfInON/4MaETNSLFDI74LDu+YJ0ML5HZyw9Vhp9QpckwQ0A==} - engines: {node: '>=v18'} - - '@commitlint/load@20.4.3': - resolution: {integrity: sha512-3cdJOUVP+VcgHa7bhJoWS+Z8mBNXB5aLWMBu7Q7uX8PSeWDzdbrBlR33J1MGGf7r1PZDp+mPPiFktk031PgdRw==} - engines: {node: '>=v18'} - - '@commitlint/message@20.4.3': - resolution: {integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==} - engines: {node: '>=v18'} - - '@commitlint/parse@20.4.3': - resolution: {integrity: sha512-hzC3JCo3zs3VkQ833KnGVuWjWIzR72BWZWjQM7tY/7dfKreKAm7fEsy71tIFCRtxf2RtMP2d3RLF1U9yhFSccA==} - engines: {node: '>=v18'} - - '@commitlint/read@20.4.3': - resolution: {integrity: sha512-j42OWv3L31WfnP8WquVjHZRt03w50Y/gEE8FAyih7GQTrIv2+pZ6VZ6pWLD/ml/3PO+RV2SPtRtTp/MvlTb8rQ==} - engines: {node: '>=v18'} - - '@commitlint/resolve-extends@20.4.3': - resolution: {integrity: sha512-QucxcOy+00FhS9s4Uy0OyS5HeUV+hbC6OLqkTSIm6fwMdKva+OEavaCDuLtgd9akZZlsUo//XzSmPP3sLKBPog==} - engines: {node: '>=v18'} - - '@commitlint/rules@20.4.3': - resolution: {integrity: sha512-Yuosd7Grn5qiT7FovngXLyRXTMUbj9PYiSkvUgWK1B5a7+ZvrbWDS7epeUapYNYatCy/KTpPFPbgLUdE+MUrBg==} - engines: {node: '>=v18'} - - '@commitlint/to-lines@20.0.0': - resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==} - engines: {node: '>=v18'} - - '@commitlint/top-level@20.4.3': - resolution: {integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==} - engines: {node: '>=v18'} - - '@commitlint/types@20.4.3': - resolution: {integrity: sha512-51OWa1Gi6ODOasPmfJPq6js4pZoomima4XLZZCrkldaH2V5Nb3bVhNXPeT6XV0gubbainSpTw4zi68NqAeCNCg==} - engines: {node: '>=v18'} - - '@simple-libs/stream-utils@1.2.0': - resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} - engines: {node: '>=18'} - - '@types/node@25.3.5': - resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==} - - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - array-ify@1.0.0: - resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - cliui@9.0.1: - resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} - engines: {node: '>=20'} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - compare-func@2.0.0: - resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} - - conventional-changelog-angular@8.3.0: - resolution: {integrity: sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==} - engines: {node: '>=18'} - - conventional-changelog-conventionalcommits@9.3.0: - resolution: {integrity: sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA==} - engines: {node: '>=18'} - - conventional-commits-parser@6.3.0: - resolution: {integrity: sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==} - engines: {node: '>=18'} - hasBin: true - - cosmiconfig-typescript-loader@6.2.0: - resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} - engines: {node: '>=v18'} - peerDependencies: - '@types/node': '*' - cosmiconfig: '>=9' - typescript: '>=5' - - cosmiconfig@9.0.1: - resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - - dargs@8.1.0: - resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} - engines: {node: '>=12'} - - dargstack@3.0.0: - resolution: {integrity: sha512-hJn6NSEDiDvkH+3THlnLGHyvxagrm+t7f1snRSNER0u5A+manl2Wy1A7oFq6tWGtBIj0DDVoT0llpMlh8R9xQw==} - engines: {node: '24'} - - dargstack_rgen@0.9.86: - resolution: {integrity: sha512-3pwqv3r7BqaSu8LWYyzA8S5OP4EAxBHXhAEHywEGPXiSbl9j17KAhGHIUaFmGX5GPeUWF0B+N5Uh5nmlMkD/Vg==} - engines: {node: '24'} - hasBin: true - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - diff@8.0.3: - resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} - engines: {node: '>=0.3.1'} - - dot-prop@5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} - - emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-east-asian-width@1.5.0: - resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} - engines: {node: '>=18'} - - git-raw-commits@4.0.0: - resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} - engines: {node: '>=16'} - deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. - hasBin: true - - global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} - engines: {node: '>=18'} - - husky@9.1.7: - resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} - engines: {node: '>=18'} - hasBin: true - - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - - indento@1.1.14: - resolution: {integrity: sha512-K4cK97v4M/ucCAbe3LUpg994folYL0WnEiCFxHXAIowKLbBb/Ahiazkz3Ao5gRar4i9pDr3imcpq4suOu0FbNw==} - - ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} - hasBin: true - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json2md@2.0.3: - resolution: {integrity: sha512-ZPzh6Djvqz8grJMxKllfCHo0p+p7BsbZ1J95KcCJgvvfdoy7myuKrrkUp80Kpy+wGauykC0dYljLqLY0kENaOw==} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - - lodash.kebabcase@4.1.1: - resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} - - lodash.mergewith@4.6.2: - resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} - - lodash.snakecase@4.1.1: - resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} - - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - - lodash.upperfirst@4.3.1: - resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} - - meow@12.1.1: - resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} - engines: {node: '>=16.10'} - - meow@13.2.0: - resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} - engines: {node: '>=18'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} - engines: {node: '>=12'} - - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} - engines: {node: '>=18'} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} - engines: {node: '>=18'} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} - engines: {node: '>= 14.6'} - hasBin: true - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs-parser@22.0.0: - resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yargs@18.0.0: - resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - -snapshots: - - '@babel/code-frame@7.29.0': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/helper-validator-identifier@7.28.5': {} - - '@commitlint/cli@20.4.3(@types/node@25.3.5)(typescript@5.9.3)': - dependencies: - '@commitlint/format': 20.4.3 - '@commitlint/lint': 20.4.3 - '@commitlint/load': 20.4.3(@types/node@25.3.5)(typescript@5.9.3) - '@commitlint/read': 20.4.3 - '@commitlint/types': 20.4.3 - tinyexec: 1.0.2 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - typescript - - '@commitlint/config-conventional@20.4.3': - dependencies: - '@commitlint/types': 20.4.3 - conventional-changelog-conventionalcommits: 9.3.0 - - '@commitlint/config-validator@20.4.3': - dependencies: - '@commitlint/types': 20.4.3 - ajv: 8.18.0 - - '@commitlint/ensure@20.4.3': - dependencies: - '@commitlint/types': 20.4.3 - lodash.camelcase: 4.3.0 - lodash.kebabcase: 4.1.1 - lodash.snakecase: 4.1.1 - lodash.startcase: 4.4.0 - lodash.upperfirst: 4.3.1 - - '@commitlint/execute-rule@20.0.0': {} - - '@commitlint/format@20.4.3': - dependencies: - '@commitlint/types': 20.4.3 - picocolors: 1.1.1 - - '@commitlint/is-ignored@20.4.3': - dependencies: - '@commitlint/types': 20.4.3 - semver: 7.7.4 - - '@commitlint/lint@20.4.3': - dependencies: - '@commitlint/is-ignored': 20.4.3 - '@commitlint/parse': 20.4.3 - '@commitlint/rules': 20.4.3 - '@commitlint/types': 20.4.3 - - '@commitlint/load@20.4.3(@types/node@25.3.5)(typescript@5.9.3)': - dependencies: - '@commitlint/config-validator': 20.4.3 - '@commitlint/execute-rule': 20.0.0 - '@commitlint/resolve-extends': 20.4.3 - '@commitlint/types': 20.4.3 - cosmiconfig: 9.0.1(typescript@5.9.3) - cosmiconfig-typescript-loader: 6.2.0(@types/node@25.3.5)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3) - is-plain-obj: 4.1.0 - lodash.mergewith: 4.6.2 - picocolors: 1.1.1 - transitivePeerDependencies: - - '@types/node' - - typescript - - '@commitlint/message@20.4.3': {} - - '@commitlint/parse@20.4.3': - dependencies: - '@commitlint/types': 20.4.3 - conventional-changelog-angular: 8.3.0 - conventional-commits-parser: 6.3.0 - - '@commitlint/read@20.4.3': - dependencies: - '@commitlint/top-level': 20.4.3 - '@commitlint/types': 20.4.3 - git-raw-commits: 4.0.0 - minimist: 1.2.8 - tinyexec: 1.0.2 - - '@commitlint/resolve-extends@20.4.3': - dependencies: - '@commitlint/config-validator': 20.4.3 - '@commitlint/types': 20.4.3 - global-directory: 4.0.1 - import-meta-resolve: 4.2.0 - lodash.mergewith: 4.6.2 - resolve-from: 5.0.0 - - '@commitlint/rules@20.4.3': - dependencies: - '@commitlint/ensure': 20.4.3 - '@commitlint/message': 20.4.3 - '@commitlint/to-lines': 20.0.0 - '@commitlint/types': 20.4.3 - - '@commitlint/to-lines@20.0.0': {} - - '@commitlint/top-level@20.4.3': - dependencies: - escalade: 3.2.0 - - '@commitlint/types@20.4.3': - dependencies: - conventional-commits-parser: 6.3.0 - picocolors: 1.1.1 - - '@simple-libs/stream-utils@1.2.0': {} - - '@types/node@25.3.5': - dependencies: - undici-types: 7.18.2 - - ajv@8.18.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-regex@5.0.1: {} - - ansi-regex@6.2.2: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.3: {} - - argparse@2.0.1: {} - - array-ify@1.0.0: {} - - callsites@3.1.0: {} - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - cliui@9.0.1: - dependencies: - string-width: 7.2.0 - strip-ansi: 7.2.0 - wrap-ansi: 9.0.2 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - compare-func@2.0.0: - dependencies: - array-ify: 1.0.0 - dot-prop: 5.3.0 - - conventional-changelog-angular@8.3.0: - dependencies: - compare-func: 2.0.0 - - conventional-changelog-conventionalcommits@9.3.0: - dependencies: - compare-func: 2.0.0 - - conventional-commits-parser@6.3.0: - dependencies: - '@simple-libs/stream-utils': 1.2.0 - meow: 13.2.0 - - cosmiconfig-typescript-loader@6.2.0(@types/node@25.3.5)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3): - dependencies: - '@types/node': 25.3.5 - cosmiconfig: 9.0.1(typescript@5.9.3) - jiti: 2.6.1 - typescript: 5.9.3 - - cosmiconfig@9.0.1(typescript@5.9.3): - dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - parse-json: 5.2.0 - optionalDependencies: - typescript: 5.9.3 - - dargs@8.1.0: {} - - dargstack@3.0.0: {} - - dargstack_rgen@0.9.86: - dependencies: - deepmerge: 4.3.1 - diff: 8.0.3 - json2md: 2.0.3 - yaml: 2.8.2 - yargs: 18.0.0 - - deepmerge@4.3.1: {} - - diff@8.0.3: {} - - dot-prop@5.3.0: - dependencies: - is-obj: 2.0.0 - - emoji-regex@10.6.0: {} - - emoji-regex@8.0.0: {} - - env-paths@2.2.1: {} - - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - - escalade@3.2.0: {} - - fast-deep-equal@3.1.3: {} - - fast-uri@3.1.0: {} - - get-caller-file@2.0.5: {} - - get-east-asian-width@1.5.0: {} - - git-raw-commits@4.0.0: - dependencies: - dargs: 8.1.0 - meow: 12.1.1 - split2: 4.2.0 - - global-directory@4.0.1: - dependencies: - ini: 4.1.1 - - husky@9.1.7: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - import-meta-resolve@4.2.0: {} - - indento@1.1.14: {} - - ini@4.1.1: {} - - is-arrayish@0.2.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-obj@2.0.0: {} - - is-plain-obj@4.1.0: {} - - jiti@2.6.1: {} - - js-tokens@4.0.0: {} - - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 - - json-parse-even-better-errors@2.3.1: {} - - json-schema-traverse@1.0.0: {} - - json2md@2.0.3: - dependencies: - indento: 1.1.14 - - lines-and-columns@1.2.4: {} - - lodash.camelcase@4.3.0: {} - - lodash.kebabcase@4.1.1: {} - - lodash.mergewith@4.6.2: {} - - lodash.snakecase@4.1.1: {} - - lodash.startcase@4.4.0: {} - - lodash.upperfirst@4.3.1: {} - - meow@12.1.1: {} - - meow@13.2.0: {} - - minimist@1.2.8: {} - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.29.0 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - picocolors@1.1.1: {} - - require-directory@2.1.1: {} - - require-from-string@2.0.2: {} - - resolve-from@4.0.0: {} - - resolve-from@5.0.0: {} - - semver@7.7.4: {} - - split2@4.2.0: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@7.2.0: - dependencies: - emoji-regex: 10.6.0 - get-east-asian-width: 1.5.0 - strip-ansi: 7.2.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.2.0: - dependencies: - ansi-regex: 6.2.2 - - tinyexec@1.0.2: {} - - typescript@5.9.3: {} - - undici-types@7.18.2: {} - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@9.0.2: - dependencies: - ansi-styles: 6.2.3 - string-width: 7.2.0 - strip-ansi: 7.2.0 - - y18n@5.0.8: {} - - yaml@2.8.2: {} - - yargs-parser@21.1.1: {} - - yargs-parser@22.0.0: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yargs@18.0.0: - dependencies: - cliui: 9.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - string-width: 7.2.0 - y18n: 5.0.8 - yargs-parser: 22.0.0 diff --git a/scripts/setup.sh b/scripts/setup.sh new file mode 100755 index 00000000..d3354553 --- /dev/null +++ b/scripts/setup.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +# Bootstrap a local Vibetype fullstack development environment. +# +# Usage (one-shot, without cloning first): +# bash <(curl -fsSL https://raw.githubusercontent.com/maevsi/stack/main/scripts/setup.sh) +# +# Usage (when stack is already cloned): +# bash scripts/setup.sh + +set -euo pipefail + +PARENT_DIR="vibetype" + +# ── helpers ────────────────────────────────────────────────────────────────── + +info() { printf '\033[0;34m➜ %s\033[0m\n' "$*"; } +success() { printf '\033[0;32m✔ %s\033[0m\n' "$*"; } +warn() { printf '\033[0;33m⚠ %s\033[0m\n' "$*"; } +die() { printf '\033[0;31m✖ %s\033[0m\n' "$*" >&2; exit 1; } + +# Ask a yes/no question; defaults to yes on empty input. +confirm() { + local prompt="$1" + local reply + read -r -p "$(printf '\033[0;36m? %s [Y/n] \033[0m' "$prompt")" reply + [[ "${reply:-y}" =~ ^[Yy]$ ]] +} + +require() { + command -v "$1" &>/dev/null || die "'$1' is required but not found. See: $2" +} + +clone_if_missing() { + local repo_url="$1" + local repo_name + repo_name="$(basename "$repo_url" .git)" + if [[ -d "$repo_name/.git" ]]; then + warn "$repo_name already exists, skipping clone." + return + fi + if [[ -d "$repo_name" ]]; then + die "Destination directory '$repo_name' already exists but is not a git repository. Remove or rename it, then rerun setup." + fi + + # Prefer SSH; fall back to HTTPS if SSH authentication fails. + local ssh_url="git@github.com:${repo_url#https://github.com/}" + info "Cloning $repo_name …" + if git clone "$ssh_url" 2>/dev/null; then + return + fi + warn "SSH clone failed for $repo_name. Falling back to HTTPS." + if git clone "$repo_url"; then + return + fi + die "Failed to clone $repo_url via SSH and HTTPS." +} + +# ── prerequisites ───────────────────────────────────────────────────────────── + +info "Step 1/4 — Checking prerequisites (git, docker, dargstack) …" + +require git "https://git-scm.com/" +require docker "https://docs.docker.com/engine/install/" + +# Check that the Docker daemon is actually reachable. +if ! docker info &>/dev/null; then + die "Docker is installed but not running. Start Docker and try again." +fi + +if ! command -v dargstack &>/dev/null; then + if command -v go &>/dev/null; then + info "Installing dargstack via go install …" + go install github.com/dargstack/dargstack/v4/cmd/dargstack@latest + + # Check if dargstack is now in PATH; if not, try adding GOPATH/bin + if ! command -v dargstack &>/dev/null; then + go_bin="$(go env GOPATH)/bin" + if [[ -f "$go_bin/dargstack" ]]; then + export PATH="$go_bin:$PATH" + info "Added $go_bin to PATH for this session." + else + die "dargstack installation completed, but binary not found at $go_bin/dargstack. + Please ensure \$(go env GOPATH)/bin is in your PATH and restart the script. + Or install a pre-built binary from https://github.com/dargstack/dargstack/releases" + fi + fi + else + die "'dargstack' is not installed and Go was not found. + Install Go from https://go.dev/doc/install, then run: + go install github.com/dargstack/dargstack/v4/cmd/dargstack@latest + Or install a pre-built binary from https://github.com/dargstack/dargstack/releases" + fi +fi + +# ── detect working directory ────────────────────────────────────────────────── + +CURRENT_DIR="$(pwd)" +if [[ "$(basename "$CURRENT_DIR")" == "stack" && -f "$CURRENT_DIR/dargstack.yaml" ]]; then + TARGET_DIR="$(dirname "$CURRENT_DIR")" + info "Running from inside the stack repo. Using parent directory: $TARGET_DIR" +else + TARGET_DIR="$CURRENT_DIR/$PARENT_DIR" + info "Creating project directory: $TARGET_DIR" + mkdir -p "$TARGET_DIR" +fi + +# ── profile / feature selection ─────────────────────────────────────────────── + +printf '\n\033[1mWhich optional feature sets do you want to set up?\033[0m\n' +printf '(The default profile (vibetype, postgres, postgraphile, sqitch, traefik, etc.) is always included.)\n\n' + +CLONE_APP=false +CLONE_RECCOOM=false +EXTRA_PROFILES=() + +if confirm "mobile app development (android, ios)"; then + CLONE_APP=true +fi + +if confirm "recommendation service (requires ssh authentication)"; then + CLONE_RECCOOM=true + EXTRA_PROFILES+=("recommendation") +fi + +printf '\n' + +# ── clone repositories ──────────────────────────────────────────────────────── + +info "Step 2/4 — Cloning repositories …" + +cd "$TARGET_DIR" + +# Always required +clone_if_missing "https://github.com/maevsi/postgraphile.git" +clone_if_missing "https://github.com/maevsi/sqitch.git" +clone_if_missing "https://github.com/maevsi/stack.git" +clone_if_missing "https://github.com/maevsi/vibetype.git" + +# Optional +if $CLONE_APP; then + clone_if_missing "https://github.com/maevsi/android.git" + clone_if_missing "https://github.com/maevsi/ios.git" +fi + +if $CLONE_RECCOOM; then + clone_if_missing "https://github.com/maevsi/reccoom.git" +fi + +success "Repositories ready." + +# ── per-repo setup ──────────────────────────────────────────────────────────── + +info "Step 3/4 — Per-repository setup …" +info " Each repository may need its own initialisation (e.g. installing Node.js dependencies for vibetype)." +info " This step is not yet automated. If you want to start development in a specific repository, you have to manually set it up according to the project's README instructions first." +# TODO: Once each cloned repository ships its own `scripts/setup.sh`, invoke +# them here uniformly instead of repository-specific logic. For example: +# +# for repo in postgraphile sqitch stack vibetype reccoom android ios; do +# if [[ -f "$TARGET_DIR/$repo/scripts/setup.sh" ]]; then +# info "Running $repo setup …" +# bash "$TARGET_DIR/$repo/scripts/setup.sh" +# fi +# done +# +# Vibetype-specific Node.js setup is intentionally skipped here for now. +# Run it manually if needed: +# +# cd "$TARGET_DIR/vibetype" +# nvm install +# corepack enable +# pnpm install + +# ── build & deploy ──────────────────────────────────────────────────────────── + +info "Step 4/4 — Using dargstack to build development images and to deploy the stack …" +info " 'dargstack build' builds the development Dockerfiles for cloned first-party services." +info " 'dargstack deploy' runs the stack, a set of services, for development." +info " Use --help on any dargstack command to learn more about it." + +cd "$TARGET_DIR/stack" + +# Build all services whose source code lives in a cloned repository. +SERVICES_TO_BUILD=("postgraphile" "sqitch" "vibetype") +$CLONE_RECCOOM && SERVICES_TO_BUILD+=("reccoom") + +info "Building development container images: ${SERVICES_TO_BUILD[*]} …" +for svc in "${SERVICES_TO_BUILD[@]}"; do + dargstack build "$svc" || warn "Build failed for $svc. You can retry with: dargstack build $svc" +done + +success "Development images ready." + +# Build the deploy command with any extra profiles. +DEPLOY_ARGS=() +for p in "${EXTRA_PROFILES[@]}"; do + DEPLOY_ARGS+=("--profiles" "$p") +done + +info "Deploying the development stack …" +dargstack deploy "${DEPLOY_ARGS[@]}" + +success "Setup complete! Vibetype is available at https://app.localhost 🎉" diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index 71cda2dc..00000000 --- a/src/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -**/backups/*/** -**/production/secrets/**/*.secret -*.crt -*.csr -*.env -*.key -!**/README.md -!**/*.template -production/stack.yml diff --git a/src/development/adminer/compose.yaml b/src/development/adminer/compose.yaml new file mode 100644 index 00000000..8f9a6eee --- /dev/null +++ b/src/development/adminer/compose.yaml @@ -0,0 +1,28 @@ +services: + adminer: + # You can access the database's frontend at [adminer.app.localhost](https://adminer.app.localhost/). + # This information is required for login: + # + # | | | + # | -------- | ------------------- | + # | System | PostgreSQL | + # | Server | postgres | + # | Username | [postgres-user] | + # | Password | [postgres-password] | + # | Database | [postgres-db] | + # + # Values in square brackets are [Docker secrets](https://docs.docker.com/engine/swarm/secrets/). + deploy: + labels: + - dargstack.profiles=default + - traefik.enable=true + - traefik.http.routers.adminer.entryPoints=web + - traefik.http.routers.adminer.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.adminer.rule=Host(`adminer.${STACK_DOMAIN}`) + - traefik.http.routers.adminer-secure.entryPoints=web-secure + - traefik.http.routers.adminer-secure.rule=Host(`adminer.${STACK_DOMAIN}`) + - traefik.http.routers.adminer-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.adminer.loadbalancer.server.port=8080 + image: adminer:5.4.2-standalone + volumes: + - ./configurations/adminer.css:/var/www/html/adminer.css:ro diff --git a/src/development/adminer/configurations/adminer.css b/src/development/adminer/configurations/adminer.css new file mode 100755 index 00000000..3da66c8c --- /dev/null +++ b/src/development/adminer/configurations/adminer.css @@ -0,0 +1,7 @@ +a { + color: blue; +} + +a:visited { + color: blue; +} diff --git a/src/development/certificates/mkcert.sh b/src/development/certificates/mkcert.sh deleted file mode 100755 index 25c841c4..00000000 --- a/src/development/certificates/mkcert.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh -THIS=$(dirname "$(readlink -f "$0")") - -create() { - NAME="$1" - shift - CONTENT=$* - - path="$THIS/$NAME" - certfile="$path.crt" - keyfile="$path.key" - - if [ "$CONTENT" != "" ]; then - # shellcheck disable=SC2086 - mkcert \ - -cert-file "$certfile" \ - -ecdsa \ - -key-file "$keyfile" $CONTENT - fi - - cat "$(mkcert -CAROOT)/rootCA.pem" >> "$certfile" -} - -echo "key crt" | tr ' ' '\n' | while read -r glob; do - if test -n "$(find "$THIS" -maxdepth 1 -name "*.$glob" -print -quit)"; then - rm "$THIS"/*."$glob" - fi -done - -create "root" -create "traefik" \ - `# adminer` "adminer.app.localhost" \ - `# grafana` "grafana.app.localhost" \ - `# minio` "minio.app.localhost" \ - `# portainer` "portainer.app.localhost" \ - `# postgraphile` "postgraphile.app.localhost" \ - `# prometheus` "prometheus.app.localhost" \ - `# reccoom` "reccoom.app.localhost" \ - `# redpanda` "redpanda.app.localhost" \ - `# traefik` "traefik.app.localhost" \ - `# tusd` "tusd.app.localhost" \ - `# vibetype` "app.localhost" "www.app.localhost" "127.0.0.1" "0.0.0.0" \ - `# zammad` "zammad.app.localhost" \ No newline at end of file diff --git a/src/development/compose.yaml b/src/development/compose.yaml new file mode 100644 index 00000000..f5dcf3b3 --- /dev/null +++ b/src/development/compose.yaml @@ -0,0 +1,4 @@ +volumes: + pnpm-data: + # The node package manager's data. + {} diff --git a/src/development/configurations/postgraphile/jwtRS256.key.pub b/src/development/configurations/postgraphile/jwtRS256.key.pub deleted file mode 100644 index 65de2425..00000000 --- a/src/development/configurations/postgraphile/jwtRS256.key.pub +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtBuHX2uH5GviLbocSUBK -aRE4iKEjjEoB1eh7VQdBoKi1Wd+DNUilVyCtq0ootPSDR/8yOTa8W396Wu1xm07+ -xDAyFzsilIppu8DNcJo28Gb3PZCm4nXmITPkknVSKf80hosgauoe7itqDxgMASYT -U8CgBH8zXPaowhqzjz0M/T9RqD1mRzMn3NEprDnaVAsh11r3DSandHjsnlgKCf1d -lh4ixCZ6zUhtb0sT6GxuJaGjNVevLbPLuugMTu3d484FwbND+AmpAjIgdnX5lJQn -eT7HxgnaiwLDazSwLM0jmiyEvnrmSPJZ2sgp87XS0VTVwMYk3M3RCy/3RiamJG5v -7qZuVr4L87cXWvLHCwV5dGaPu0Dc3/75lcKcWy+BtBQUI+n+om1EitLtUo+lxE9a -SyG6bhYQNu+XMRwY768TrzrvBwYt5JxoO9y3ybIFzoi/mufkp2NY7XB7CHfMcrn3 -XzCBbXJwt0Zlu2K1R8FPVN0SlsjtOVQun1al9cjC808hihjpiiGpKURoaFsxkh4B -8n2Qli/fl5BQVAPClmFbsvToSzy+ENEDpXc53eve5Dhzx5SB/qe0DwlljDdc2W8n -hozYHaOtnLnhG/TU9mmOliw5jmsHb5m1OMdBXMEhrGv4hkHJNg9+hCSJDzvbFRkN -Y95lZj/fbtFrSHR5WwdsgI8CAwEAAQ== ------END PUBLIC KEY----- diff --git a/src/development/configurations/traefik/dynamic.yml b/src/development/configurations/traefik/dynamic.yml deleted file mode 100644 index 5fdce1e4..00000000 --- a/src/development/configurations/traefik/dynamic.yml +++ /dev/null @@ -1,7 +0,0 @@ -tls: - certificates: - - certFile: /etc/traefik/acme/traefik.crt - keyFile: /etc/traefik/acme/traefik.key - options: - mintls13: - minVersion: VersionTLS13 \ No newline at end of file diff --git a/src/development/debezium/compose.yaml b/src/development/debezium/compose.yaml new file mode 100644 index 00000000..261a497b --- /dev/null +++ b/src/development/debezium/compose.yaml @@ -0,0 +1,49 @@ +services: + debezium: + # You can see how changes in the database end up in the event stream using `redpanda-console`. + deploy: + labels: + - dargstack.profiles=event-streaming + environment: + BOOTSTRAP_SERVERS: redpanda:9092 + CONFIG_STORAGE_TOPIC: connect_configs + GROUP_ID: 1 + OFFSET_STORAGE_TOPIC: connect_offsets + STATUS_STORAGE_TOPIC: connect_statuses + # healthcheck: + # test: ["CMD", "curl", "--fail", "--silent", "--show-error", "http://localhost:8083/connectors"] + # interval: 30s + # timeout: 10s + # retries: 3 + # start_period: 20s + image: quay.io/debezium/connect:3.5 + volumes: + - debezium-kafka-configuration:/kafka/config + - debezium-kafka-data:/kafka/data + - debezium-kafka-logs:/kafka/logs + debezium-postgres-connector: + # You can check the database connector's setup logs using `portainer`. + command: /entrypoint.sh + deploy: + labels: + - dargstack.profiles=event-streaming + restart_policy: + condition: on-failure + entrypoint: sh + image: curlimages/curl:8.18.0 + secrets: + - postgres-db + - postgres-password + - postgres-user + volumes: + - ./configurations/entrypoint.sh:/entrypoint.sh:ro +volumes: + debezium-kafka-configuration: + # The change data capture's configuration. + {} + debezium-kafka-data: + # The change data capture's data. + {} + debezium-kafka-logs: + # The change data capture's logs. + {} diff --git a/src/production/configurations/debezium-postgres-connector/entrypoint.sh b/src/development/debezium/configurations/entrypoint.sh similarity index 91% rename from src/production/configurations/debezium-postgres-connector/entrypoint.sh rename to src/development/debezium/configurations/entrypoint.sh index 5130d89f..66a731e2 100755 --- a/src/production/configurations/debezium-postgres-connector/entrypoint.sh +++ b/src/development/debezium/configurations/entrypoint.sh @@ -2,9 +2,9 @@ CONNECTOR_NAME="postgres-connector" DEBEZIUM_URL="http://debezium:8083/connectors" -POSTGRES_DB=$(cat /run/secrets/postgres_db) -POSTGRES_PASSWORD=$(cat /run/secrets/postgres_password) -POSTGRES_USER=$(cat /run/secrets/postgres_user) +POSTGRES_DB=$(cat /run/secrets/postgres-db) +POSTGRES_PASSWORD=$(cat /run/secrets/postgres-password) +POSTGRES_USER=$(cat /run/secrets/postgres-user) # Wait for Debezium to be healthy (REST API ready) echo "Waiting for Debezium to be ready..." diff --git a/src/development/elasticsearch/compose.yaml b/src/development/elasticsearch/compose.yaml new file mode 100644 index 00000000..942a1ff1 --- /dev/null +++ b/src/development/elasticsearch/compose.yaml @@ -0,0 +1,53 @@ +secrets: + elasticsearch-keystore-password: + # The search engine's password for the keystore. + file: ../../../artifacts/secrets/elasticsearch-keystore-password.secret + elasticsearch-password: + # The search engine's password for the default user. + file: ../../../artifacts/secrets/elasticsearch-password.secret +services: + elasticsearch: + # You cannot access the search engine via a web interface. + deploy: + labels: + - dargstack.profiles=zammad + environment: + bootstrap.memory_lock: "true" + discovery.type: single-node + ELASTIC_PASSWORD_FILE: /run/secrets/elasticsearch-password + ES_JAVA_OPTS: -Xms1g -Xmx1g + KEYSTORE_PASSWORD_FILE: /run/secrets/elasticsearch-keystore-password + network.publish_host: elasticsearch + image: elasticsearch:8.19.13 + secrets: + - source: elasticsearch-keystore-password + uid: "1000" + gid: "1000" + mode: 0o400 + - source: elasticsearch-password + uid: "1000" + gid: "1000" + mode: 0o400 + ulimits: + nofile: + soft: 65535 + hard: 65535 + memlock: + soft: -1 + hard: -1 + volumes: + - elasticsearch-configuration:/usr/share/elasticsearch/config + - elasticsearch-data:/usr/share/elasticsearch/data +volumes: + elasticsearch-configuration: + # The search engine's configuration. + {} + elasticsearch-data: + # The search engine's data. + {} +x-dargstack: + secrets: + elasticsearch-keystore-password: + type: random_string + elasticsearch-password: + type: random_string diff --git a/src/development/geoip/compose.yaml b/src/development/geoip/compose.yaml new file mode 100644 index 00000000..a03531ab --- /dev/null +++ b/src/development/geoip/compose.yaml @@ -0,0 +1,4 @@ +services: + geoip: + # You cannot access the ip geolocator via a web interface. + image: ghcr.io/observabilitystack/geoip-api:2026-02 diff --git a/src/development/grafana/compose.yaml b/src/development/grafana/compose.yaml new file mode 100644 index 00000000..800a879c --- /dev/null +++ b/src/development/grafana/compose.yaml @@ -0,0 +1,65 @@ +secrets: + grafana-admin-email: + # The observation dashboard's admin email. + file: ../../../artifacts/secrets/grafana-admin-email.secret + grafana-admin-password: + # The observation dashboard's admin password. + file: ../../../artifacts/secrets/grafana-admin-password.secret + grafana-admin-user: + # The observation dashboard's admin user. + file: ../../../artifacts/secrets/grafana-admin-user.secret + grafana-discord-webhook: + # The observation dashboard's contact point for Discord. + file: ../../../artifacts/secrets/grafana-discord-webhook.secret +services: + grafana: + # You can access the observation dashboard at [grafana.app.localhost](https://grafana.app.localhost/). + deploy: + labels: + - dargstack.profiles=analytics + - traefik.enable=true + - traefik.http.routers.grafana.entryPoints=web + - traefik.http.routers.grafana.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.grafana.rule=Host(`grafana.${STACK_DOMAIN}`) + - traefik.http.routers.grafana-secure.entryPoints=web-secure + - traefik.http.routers.grafana-secure.rule=Host(`grafana.${STACK_DOMAIN}`) + - traefik.http.routers.grafana-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.grafana.loadbalancer.server.port=3000 + environment: + GF_DATABASE_HOST: postgres:5432 + GF_DATABASE_PASSWORD__FILE: /run/secrets/postgres-role-service-grafana-password + GF_DATABASE_TYPE: postgres + GF_DATABASE_USER__FILE: /run/secrets/postgres-role-service-grafana-username + GF_SECURITY_ADMIN_EMAIL__FILE: /run/secrets/grafana-admin-email + GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana-admin-password + GF_SECURITY_ADMIN_USER__FILE: /run/secrets/grafana-admin-user + GF_SERVER_ROOT_URL: https://grafana.${STACK_DOMAIN}/ + image: grafana/grafana:12.4.2 + secrets: + - grafana-admin-email + - grafana-admin-password + - grafana-admin-user + - grafana-discord-webhook + - postgres-db + - postgres-role-service-grafana-password + - postgres-role-service-grafana-username + volumes: + - ./configurations/dashboards:/var/lib/grafana/dashboards:ro + - ./configurations/provisioning:/etc/grafana/provisioning:ro + - grafana-data:/var/lib/grafana +volumes: + grafana-data: + # The observation dashboard's data. + {} +x-dargstack: + secrets: + grafana-admin-email: + insecure_default: admin@localhost + type: insecure_default + grafana-admin-password: + type: random_string + grafana-admin-user: + type: word + grafana-discord-webhook: + hint: https://discord.com/api/webhooks// + type: third_party diff --git a/src/production/configurations/grafana/dashboards/Infrastructure/Advanced/grafana.json b/src/development/grafana/configurations/dashboards/Infrastructure/Advanced/grafana.json similarity index 100% rename from src/production/configurations/grafana/dashboards/Infrastructure/Advanced/grafana.json rename to src/development/grafana/configurations/dashboards/Infrastructure/Advanced/grafana.json diff --git a/src/production/configurations/grafana/dashboards/Infrastructure/Advanced/prometheus-2.0.json b/src/development/grafana/configurations/dashboards/Infrastructure/Advanced/prometheus-2.0.json similarity index 100% rename from src/production/configurations/grafana/dashboards/Infrastructure/Advanced/prometheus-2.0.json rename to src/development/grafana/configurations/dashboards/Infrastructure/Advanced/prometheus-2.0.json diff --git a/src/production/configurations/grafana/dashboards/Infrastructure/operations.json b/src/development/grafana/configurations/dashboards/Infrastructure/operations.json similarity index 100% rename from src/production/configurations/grafana/dashboards/Infrastructure/operations.json rename to src/development/grafana/configurations/dashboards/Infrastructure/operations.json diff --git a/src/production/configurations/grafana/dashboards/Infrastructure/redpanda.json b/src/development/grafana/configurations/dashboards/Infrastructure/redpanda.json similarity index 100% rename from src/production/configurations/grafana/dashboards/Infrastructure/redpanda.json rename to src/development/grafana/configurations/dashboards/Infrastructure/redpanda.json diff --git a/src/production/configurations/grafana/dashboards/Management/kpis.json b/src/development/grafana/configurations/dashboards/Management/kpis.json similarity index 100% rename from src/production/configurations/grafana/dashboards/Management/kpis.json rename to src/development/grafana/configurations/dashboards/Management/kpis.json diff --git a/src/production/configurations/grafana/provisioning/alerting/alert-notifications-pending.yaml b/src/development/grafana/configurations/provisioning/alerting/alert-notifications-pending.yaml similarity index 100% rename from src/production/configurations/grafana/provisioning/alerting/alert-notifications-pending.yaml rename to src/development/grafana/configurations/provisioning/alerting/alert-notifications-pending.yaml diff --git a/src/production/configurations/grafana/provisioning/alerting/contact-points.yaml b/src/development/grafana/configurations/provisioning/alerting/contact-points.yaml similarity index 94% rename from src/production/configurations/grafana/provisioning/alerting/contact-points.yaml rename to src/development/grafana/configurations/provisioning/alerting/contact-points.yaml index cf88e5ce..832c0958 100644 --- a/src/production/configurations/grafana/provisioning/alerting/contact-points.yaml +++ b/src/development/grafana/configurations/provisioning/alerting/contact-points.yaml @@ -1,7 +1,7 @@ apiVersion: 1 contactPoints: - orgId: 1 - name: 'Discord' + name: Discord receivers: - uid: aen6t8xrd6pkwb type: discord @@ -25,6 +25,6 @@ contactPoints: {{ template "__alert_details" .Alerts.Resolved }}{{ end }}{{ end }}{{ template "default.message_custom" . }} <@&1377144332154572831> - url: $__file{/run/secrets/grafana_discord_webhook} + url: $__file{/run/secrets/grafana-discord-webhook} use_discord_username: false disableResolveMessage: false diff --git a/src/production/configurations/grafana/provisioning/dashboards/file.yml b/src/development/grafana/configurations/provisioning/dashboards/file.yml similarity index 89% rename from src/production/configurations/grafana/provisioning/dashboards/file.yml rename to src/development/grafana/configurations/provisioning/dashboards/file.yml index 7129ad74..c23fbd8b 100644 --- a/src/production/configurations/grafana/provisioning/dashboards/file.yml +++ b/src/development/grafana/configurations/provisioning/dashboards/file.yml @@ -2,13 +2,13 @@ apiVersion: 1 providers: # an unique provider name. Required - - name: 'File' + - name: "File" # Org id. Default to 1 orgId: 1 # name of the dashboard folder. - folder: '' + folder: "" # folder UID. will be automatically generated if not specified - folderUid: '' + folderUid: "" # provider type. Default to 'file' type: file # disable dashboard deletion @@ -21,4 +21,4 @@ providers: # path to dashboard files on disk. Required when using the 'file' type path: /var/lib/grafana/dashboards # use folder names from filesystem to create folders in Grafana - foldersFromFilesStructure: true \ No newline at end of file + foldersFromFilesStructure: true diff --git a/src/development/grafana/configurations/provisioning/datasources/postgres.yaml b/src/development/grafana/configurations/provisioning/datasources/postgres.yaml new file mode 100644 index 00000000..9d53b3fc --- /dev/null +++ b/src/development/grafana/configurations/provisioning/datasources/postgres.yaml @@ -0,0 +1,14 @@ +apiVersion: 1 + +datasources: + - access: proxy + jsonData: + database: $__file{/run/secrets/postgres-db} + postgresVersion: 1500 + sslmode: disable + name: PostgreSQL + secureJsonData: + password: $__file{/run/secrets/postgres-role-service-grafana-password} + type: grafana-postgresql-datasource + url: postgres:5432 + user: $__file{/run/secrets/postgres-role-service-grafana-username} diff --git a/src/production/configurations/grafana/provisioning/datasources/prometheus.yaml b/src/development/grafana/configurations/provisioning/datasources/prometheus.yaml similarity index 100% rename from src/production/configurations/grafana/provisioning/datasources/prometheus.yaml rename to src/development/grafana/configurations/provisioning/datasources/prometheus.yaml diff --git a/src/development/jobber/compose.yaml b/src/development/jobber/compose.yaml new file mode 100644 index 00000000..447213f2 --- /dev/null +++ b/src/development/jobber/compose.yaml @@ -0,0 +1,90 @@ +secrets: + jobber-aliases: + # The job scheduler's SMTP client mail alias. + file: ../../../artifacts/secrets/jobber-aliases.secret + jobber-aws-bucket: + # The job scheduler's AWS bucket name. + file: ../../../artifacts/secrets/jobber-aws-bucket.secret + jobber-aws-credentials: + # The job scheduler's AWS credentials. + file: ../../../artifacts/secrets/jobber-aws-credentials.secret + jobber-aws-configuration: + # The job scheduler's AWS configuration. + file: ../../../artifacts/secrets/jobber-aws-configuration.secret + jobber-msmtprc: + # The job scheduler's SMTP client configuration. + file: ../../../artifacts/secrets/jobber-msmtprc.secret +services: + jobber: + # You cannot access the jobber via a web interface. + environment: + AWS_SHARED_CREDENTIALS_FILE: /run/secrets/jobber-aws-credentials + image: ghcr.io/dargmuesli/jobber-aws-msmtp:1.3.0 + secrets: + - source: jobber-aliases + target: /etc/aliases + - jobber-aws-bucket + - jobber-aws-credentials + - source: jobber-aws-configuration + target: /home/jobberuser/.aws/config + - source: jobber-msmtprc + target: /etc/msmtprc + volumes: + - ./configurations/sinks:/srv/sinks:ro + - ./configurations/.jobber:/home/jobberuser/.jobber:ro +x-dargstack: + secrets: + jobber-aliases: + type: insecure_default + insecure_default: "default: mail@localhost" + jobber-aws-bucket: + type: word + jobber-aws-credentials: + type: insecure_default + insecure_default: | + [default] + aws_access_key_id = s3user + aws_secret_access_key = s3password + jobber-aws-configuration: + type: template + template: | + [default] + region = + + s3 = + endpoint_url = + signature_version = s3v4 + max_concurrent_requests = 100 + max_queue_size = 1000 + multipart_threshold = 50MB + # Edit the multipart_chunksize value according to the file sizes that you want to upload. The present configuration allows to upload files up to 10 GB (100 requests * 10MB). For example setting it to 5GB allows you to upload files up to 5TB. + multipart_chunksize = 10MB + s3api = + endpoint_url = + + [plugins] + endpoint = awscli_plugin_endpoint + jobber-msmtprc: + type: template + template: | + # Set default values for all following accounts. + defaults + auth on + tls on + tls_trust_file /etc/ssl/certs/ca-certificates.crt + syslog on + + # + account + host + port + from + user + password + # or + #auth off + #tls off + + # Set a default account + account default : + aliases /etc/aliases diff --git a/src/development/configurations/jobber/.jobber b/src/development/jobber/configurations/.jobber similarity index 76% rename from src/development/configurations/jobber/.jobber rename to src/development/jobber/configurations/.jobber index d9067df7..906ff841 100644 --- a/src/development/configurations/jobber/.jobber +++ b/src/development/jobber/configurations/.jobber @@ -2,7 +2,7 @@ version: 1.4 jobs: DBBackup: - cmd: aws s3 sync /backups s3://$(cat /run/secrets/jobber_aws-bucket)/backups + cmd: aws s3 sync /backups s3://$(cat /run/secrets/jobber-aws-bucket)/backups time: 0 0 0 * notifyOnSuccess: - type: system-email diff --git a/src/production/configurations/jobber/sinks/sentry/error.sh b/src/development/jobber/configurations/sinks/sentry/error.sh similarity index 100% rename from src/production/configurations/jobber/sinks/sentry/error.sh rename to src/development/jobber/configurations/sinks/sentry/error.sh diff --git a/src/production/configurations/jobber/sinks/sentry/in-progress.sh b/src/development/jobber/configurations/sinks/sentry/in-progress.sh similarity index 100% rename from src/production/configurations/jobber/sinks/sentry/in-progress.sh rename to src/development/jobber/configurations/sinks/sentry/in-progress.sh diff --git a/src/production/configurations/jobber/sinks/sentry/ok.sh b/src/development/jobber/configurations/sinks/sentry/ok.sh similarity index 100% rename from src/production/configurations/jobber/sinks/sentry/ok.sh rename to src/development/jobber/configurations/sinks/sentry/ok.sh diff --git a/src/development/secrets/jobber/aliases.secret.template b/src/development/jobber/secrets/aliases.secret.template similarity index 100% rename from src/development/secrets/jobber/aliases.secret.template rename to src/development/jobber/secrets/aliases.secret.template diff --git a/src/development/secrets/jobber/aws-bucket.secret.template b/src/development/jobber/secrets/aws-bucket.secret.template similarity index 100% rename from src/development/secrets/jobber/aws-bucket.secret.template rename to src/development/jobber/secrets/aws-bucket.secret.template diff --git a/src/development/secrets/jobber/aws-configuration.secret.template b/src/development/jobber/secrets/aws-configuration.secret.template similarity index 100% rename from src/development/secrets/jobber/aws-configuration.secret.template rename to src/development/jobber/secrets/aws-configuration.secret.template diff --git a/src/development/secrets/jobber/aws-credentials.secret.template b/src/development/jobber/secrets/aws-credentials.secret.template similarity index 100% rename from src/development/secrets/jobber/aws-credentials.secret.template rename to src/development/jobber/secrets/aws-credentials.secret.template diff --git a/src/development/secrets/jobber/msmtprc.secret.template b/src/development/jobber/secrets/msmtprc.secret.template similarity index 100% rename from src/development/secrets/jobber/msmtprc.secret.template rename to src/development/jobber/secrets/msmtprc.secret.template diff --git a/src/development/memcached/compose.yaml b/src/development/memcached/compose.yaml new file mode 100644 index 00000000..23da3818 --- /dev/null +++ b/src/development/memcached/compose.yaml @@ -0,0 +1,8 @@ +services: + memcached: + # You cannot access the caching system via a web interface. + deploy: + labels: + - dargstack.profiles=zammad + image: memcached:1.6.41-alpine + # command: memcached -m 256M diff --git a/src/development/minio/compose.yaml b/src/development/minio/compose.yaml new file mode 100644 index 00000000..afc36ee8 --- /dev/null +++ b/src/development/minio/compose.yaml @@ -0,0 +1,43 @@ +services: + minio: # dargstack:dev-only + # You can access the s3 console at [minio.app.localhost](https://minio.app.localhost/). + # You can access the s3 api service at [s3.app.localhost](https://s3.app.localhost/) if you want to access via cli from outside the stack. + command: server /data --console-address ":9001" # dargstack:dev-only + deploy: # dargstack:dev-only + labels: # dargstack:dev-only + - dargstack.profiles=upload + - traefik.enable=true # dargstack:dev-only + # Minio Console + - traefik.http.routers.minio.entryPoints=web # dargstack:dev-only + - traefik.http.routers.minio.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.minio.rule=Host(`minio.${STACK_DOMAIN}`) # dargstack:dev-only + - traefik.http.routers.minio.service=minio # dargstack:dev-only + - traefik.http.routers.minio-secure.entryPoints=web-secure # dargstack:dev-only + - traefik.http.routers.minio-secure.rule=Host(`minio.${STACK_DOMAIN}`) # dargstack:dev-only + - traefik.http.routers.minio-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.routers.minio-secure.service=minio # dargstack:dev-only + - traefik.http.services.minio.loadbalancer.server.port=9001 # dargstack:dev-only + - traefik.http.services.minio.loadbalancer.passhostheader=true # dargstack:dev-only + # Minio itself + - traefik.http.routers.s3.entryPoints=web # dargstack:dev-only + - traefik.http.routers.s3.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.s3.rule=Host(`s3.${STACK_DOMAIN}`) # dargstack:dev-only + - traefik.http.routers.s3.service=s3 # dargstack:dev-only + - traefik.http.routers.s3-secure.entryPoints=web-secure # dargstack:dev-only + - traefik.http.routers.s3-secure.rule=Host(`s3.${STACK_DOMAIN}`) # dargstack:dev-only + - traefik.http.routers.s3-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.routers.s3-secure.service=s3 # dargstack:dev-only + - traefik.http.services.s3.loadbalancer.server.port=9000 # dargstack:dev-only + - traefik.http.services.s3.loadbalancer.passhostheader=true # dargstack:dev-only + entrypoint: /patched-entrypoint.sh # dargstack:dev-only + image: minio/minio # dargstack:dev-only + volumes: # dargstack:dev-only + - minio-data:/data # dargstack:dev-only + - ./configurations/entrypoint.sh:/patched-entrypoint.sh # dargstack:dev-only + environment: # dargstack:dev-only + MINIO_ROOT_PASSWORD: s3password # dargstack:dev-only + MINIO_ROOT_USER: s3user # dargstack:dev-only +volumes: + minio-data: + # The s3 server's data. + {} diff --git a/src/development/configurations/minio/entrypoint.sh b/src/development/minio/configurations/entrypoint.sh similarity index 100% rename from src/development/configurations/minio/entrypoint.sh rename to src/development/minio/configurations/entrypoint.sh diff --git a/src/development/portainer/compose.yaml b/src/development/portainer/compose.yaml new file mode 100644 index 00000000..fd91b7a7 --- /dev/null +++ b/src/development/portainer/compose.yaml @@ -0,0 +1,49 @@ +secrets: + portainer-admin-password: + # The container manager's admin password. + file: ../../../artifacts/secrets/portainer-admin-password.secret +services: + portainer: + # You can access the container manager's frontend at [portainer.app.localhost](https://portainer.app.localhost/). + command: -H tcp://tasks.portainer-agent:9001 --tlsskipverify --admin-password-file '/run/secrets/portainer-admin-password' + deploy: + labels: + - dargstack.profiles=default + - traefik.enable=true + - traefik.http.routers.portainer.entryPoints=web + - traefik.http.routers.portainer.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.portainer.rule=Host(`portainer.${STACK_DOMAIN}`) + - traefik.http.routers.portainer-secure.entryPoints=web-secure + - traefik.http.routers.portainer-secure.rule=Host(`portainer.${STACK_DOMAIN}`) + - traefik.http.routers.portainer-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.portainer.loadbalancer.server.port=9000 + placement: + constraints: + - node.role == manager + replicas: 1 + image: portainer/portainer-ce:2.40.0-alpine + secrets: + - portainer-admin-password + volumes: + - portainer-data:/data + portainer-agent: + # You cannot access the container manager's agent directly. + deploy: + labels: + - dargstack.profiles=default + mode: global + placement: + constraints: + - node.platform.os == linux + image: portainer/agent:2.40.0 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /var/lib/docker/volumes:/var/lib/docker/volumes +volumes: + portainer-data: + # The container manager's data. + {} +x-dargstack: + secrets: + portainer-admin-password: + type: random_string diff --git a/src/development/secrets/portainer/admin-password.secret.template b/src/development/portainer/secrets/admin-password.secret.template similarity index 100% rename from src/development/secrets/portainer/admin-password.secret.template rename to src/development/portainer/secrets/admin-password.secret.template diff --git a/src/development/postgraphile/compose.yaml b/src/development/postgraphile/compose.yaml new file mode 100644 index 00000000..4d5b270b --- /dev/null +++ b/src/development/postgraphile/compose.yaml @@ -0,0 +1,66 @@ +secrets: + postgraphile-connection: + # The GraphQL API's database URI. + file: ../../../artifacts/secrets/postgraphile-connection.secret + postgraphile-jwt-secret: + # The GraphQL API's JWT secret. + file: ../../../artifacts/secrets/postgraphile-jwt-secret.secret + postgraphile-owner-connection: + # The GraphQL API's database owner URI. + file: ../../../artifacts/secrets/postgraphile-owner-connection.secret +services: + postgraphile: + # You can access the GraphQL API for the PostgreSQL database at [postgraphile.app.localhost](https://postgraphile.app.localhost/). + deploy: + labels: + - dargstack.development.build=../../../../postgraphile + - dargstack.profiles=default + - traefik.enable=true + - traefik.http.middlewares.postgraphile-auth.forwardauth.address=http://vibetype:3000/api/internal/service/postgraphile/authentication + - traefik.http.middlewares.postgraphile-auth.forwardauth.forwardBody=true + - traefik.http.middlewares.postgraphile-auth.forwardauth.preserveRequestMethod=true + - traefik.http.middlewares.postgraphile-cors.headers.accessControlAllowCredentials=true + - traefik.http.middlewares.postgraphile-cors.headers.accessControlAllowHeaders=authorization + - traefik.http.middlewares.postgraphile-cors.headers.accessControlAllowOriginList=https://${STACK_DOMAIN},https://localhost:3000,https://app.localhost:3000 + - traefik.http.routers.postgraphile.entryPoints=web + - traefik.http.routers.postgraphile.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.postgraphile.rule=Host(`postgraphile.${STACK_DOMAIN}`) + - traefik.http.routers.postgraphile-secure.entryPoints=web-secure + - traefik.http.routers.postgraphile-secure.middlewares=postgraphile-auth,postgraphile-cors + - traefik.http.routers.postgraphile-secure.rule=Host(`postgraphile.${STACK_DOMAIN}`) && Path(`/graphql`) + - traefik.http.routers.postgraphile-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.routers.postgraphile-secure-graphiql.entryPoints=web-secure + - traefik.http.routers.postgraphile-secure-graphiql.rule=Host(`postgraphile.${STACK_DOMAIN}`) + - traefik.http.routers.postgraphile-secure-graphiql.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.postgraphile.loadbalancer.server.port=5678 + # # Use the DEBUG environment variable for extended debugging. + # environment: + # DEBUG: graphile-build:warn,graphile-build-pg:sql + image: vibetype/postgraphile:development + secrets: + - source: postgraphile-connection + target: /run/environment-variables/POSTGRAPHILE_CONNECTION + - source: postgraphile-jwt-secret + target: /run/environment-variables/POSTGRAPHILE_JWT_SECRET_KEY + - source: postgraphile-owner-connection + target: /run/environment-variables/POSTGRAPHILE_OWNER_CONNECTION + volumes: + - ../../../../postgraphile/:/srv/app/ # dargstack:dev-only + - ./configurations/jwtES256.key.pub:/run/environment-variables/POSTGRAPHILE_JWT_PUBLIC_KEY:ro + - pnpm-data:/srv/.pnpm-store/ # dargstack:dev-only + - postgraphile-data:/srv/app/node_modules # dargstack:dev-only +volumes: + postgraphile-data: + # The GraphQL API's data. + {} +x-dargstack: + secrets: + postgraphile-connection: + type: template + template: postgres://{{secret:postgres-role-service-postgraphile-username}}:{{secret:postgres-role-service-postgraphile-password}}@postgres:5432/{{secret:postgres-db}} + postgraphile-jwt-secret: + type: private_key + key_type: ecdsa + postgraphile-owner-connection: + type: template + template: postgres://{{secret:postgres-user}}:{{secret:postgres-password}}@postgres:5432/{{secret:postgres-db}} diff --git a/src/development/postgraphile/configurations/jwtES256.key.pub b/src/development/postgraphile/configurations/jwtES256.key.pub new file mode 100644 index 00000000..e9b2a5e8 --- /dev/null +++ b/src/development/postgraphile/configurations/jwtES256.key.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgIYyEMm+hZzEnXhJLGUx9lwr3cKs +W2uJ+zLvei380CrUEPARnWQNR/V0usS0EFypTQllniuCpbLG6un87kxh6w== +-----END PUBLIC KEY----- diff --git a/src/development/secrets/postgraphile/connection.secret.template b/src/development/postgraphile/secrets/connection.secret.template similarity index 100% rename from src/development/secrets/postgraphile/connection.secret.template rename to src/development/postgraphile/secrets/connection.secret.template diff --git a/src/development/secrets/postgraphile/jwt-secret.secret.template b/src/development/postgraphile/secrets/jwt-secret.secret.template similarity index 100% rename from src/development/secrets/postgraphile/jwt-secret.secret.template rename to src/development/postgraphile/secrets/jwt-secret.secret.template diff --git a/src/development/secrets/postgraphile/owner-connection.secret.template b/src/development/postgraphile/secrets/owner-connection.secret.template similarity index 100% rename from src/development/secrets/postgraphile/owner-connection.secret.template rename to src/development/postgraphile/secrets/owner-connection.secret.template diff --git a/src/development/postgres/compose.yaml b/src/development/postgres/compose.yaml new file mode 100644 index 00000000..edea3c27 --- /dev/null +++ b/src/development/postgres/compose.yaml @@ -0,0 +1,92 @@ +secrets: + postgres-db: + # The database's name. + file: ../../../artifacts/secrets/postgres-db.secret + postgres-password: + # The database's password. + file: ../../../artifacts/secrets/postgres-password.secret + postgres-role-service-grafana-password: + # The password of the observation dashboard's database role. + file: ../../../artifacts/secrets/postgres-role-service-grafana-password.secret + postgres-role-service-grafana-username: + # The username of the observation dashboard's database role. + file: ../../../artifacts/secrets/postgres-role-service-grafana-username.secret + postgres-role-service-postgraphile-password: + # The password of the GraphQL API database wrapper's database role. + file: ../../../artifacts/secrets/postgres-role-service-postgraphile-password.secret + postgres-role-service-postgraphile-username: + # The username of the GraphQL API database wrapper's database role. + file: ../../../artifacts/secrets/postgres-role-service-postgraphile-username.secret + postgres-role-service-vibetype-password: + # The `vibetype` database role's password. + file: ../../../artifacts/secrets/postgres-role-service-vibetype-password.secret + postgres-role-service-vibetype-username: + # The `vibetype` database role's password. + file: ../../../artifacts/secrets/postgres-role-service-vibetype-username.secret + postgres-role-service-zammad-password: + # The password of the customer service database role. + file: ../../../artifacts/secrets/postgres-role-service-zammad-password.secret + postgres-role-service-zammad-username: + # The username of the customer service database role. + file: ../../../artifacts/secrets/postgres-role-service-zammad-username.secret + postgres-user: + # The database's default user. + file: ../../../artifacts/secrets/postgres-user.secret +services: + postgres: + # You can access the database via `adminer`. + command: -c vibetype.jwt_expiry_duration='1 month' -c wal_level=logical + deploy: + labels: + - dargstack.profiles=default + environment: + POSTGRES_DB_FILE: /run/secrets/postgres-db + POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password + POSTGRES_USER_FILE: /run/secrets/postgres-user + image: postgis/postgis:18-3.6-alpine + ports: # dargstack:dev-only + - 5432:5432 # dargstack:dev-only + secrets: + - postgres-db + - postgres-password + - postgres-user + # sysctls: + # # Prevent Docker Swarm from killing connections (https://github.com/moby/moby/issues/31208) + # - net.ipv4.tcp_keepalive_time=600 + # - net.ipv4.tcp_keepalive_intvl=30 + # - net.ipv4.tcp_keepalive_probes=10 + volumes: + - postgres-data:/var/lib/postgresql/ +volumes: + postgres-data: + # The database's data. + {} +x-dargstack: + secrets: + postgres-db: + type: word + postgres-password: + special_characters: false + type: random_string + postgres-role-service-grafana-password: + special_characters: false + type: random_string + postgres-role-service-grafana-username: + type: word + postgres-role-service-postgraphile-password: + special_characters: false + type: random_string + postgres-role-service-postgraphile-username: + type: word + postgres-role-service-vibetype-password: + special_characters: false + type: random_string + postgres-role-service-vibetype-username: + type: word + postgres-role-service-zammad-password: + special_characters: false + type: random_string + postgres-role-service-zammad-username: + type: word + postgres-user: + type: word diff --git a/src/development/secrets/postgres/db.secret b/src/development/postgres/secrets/db.secret.old similarity index 100% rename from src/development/secrets/postgres/db.secret rename to src/development/postgres/secrets/db.secret.old diff --git a/src/development/secrets/elasticsearch/keystore_password.secret.template b/src/development/postgres/secrets/db.secret.template.old similarity index 100% rename from src/development/secrets/elasticsearch/keystore_password.secret.template rename to src/development/postgres/secrets/db.secret.template.old diff --git a/src/development/secrets/elasticsearch/password.secret.template b/src/development/postgres/secrets/password.secret.template similarity index 100% rename from src/development/secrets/elasticsearch/password.secret.template rename to src/development/postgres/secrets/password.secret.template diff --git a/src/development/secrets/postgres/role_service_grafana_password.secret.template b/src/development/postgres/secrets/role_service_grafana_password.secret.template similarity index 100% rename from src/development/secrets/postgres/role_service_grafana_password.secret.template rename to src/development/postgres/secrets/role_service_grafana_password.secret.template diff --git a/src/development/secrets/postgres/role_service_grafana_username.secret.template b/src/development/postgres/secrets/role_service_grafana_username.secret.template similarity index 100% rename from src/development/secrets/postgres/role_service_grafana_username.secret.template rename to src/development/postgres/secrets/role_service_grafana_username.secret.template diff --git a/src/development/secrets/postgres/role_service_postgraphile_password.secret.template b/src/development/postgres/secrets/role_service_postgraphile_password.secret.template similarity index 100% rename from src/development/secrets/postgres/role_service_postgraphile_password.secret.template rename to src/development/postgres/secrets/role_service_postgraphile_password.secret.template diff --git a/src/development/secrets/postgres/role_service_postgraphile_username.secret.template b/src/development/postgres/secrets/role_service_postgraphile_username.secret.template similarity index 100% rename from src/development/secrets/postgres/role_service_postgraphile_username.secret.template rename to src/development/postgres/secrets/role_service_postgraphile_username.secret.template diff --git a/src/development/secrets/postgres/role_service_vibetype_password.secret.template b/src/development/postgres/secrets/role_service_vibetype_password.secret.template similarity index 100% rename from src/development/secrets/postgres/role_service_vibetype_password.secret.template rename to src/development/postgres/secrets/role_service_vibetype_password.secret.template diff --git a/src/development/secrets/postgres/role_service_vibetype_username.secret.template b/src/development/postgres/secrets/role_service_vibetype_username.secret.template similarity index 100% rename from src/development/secrets/postgres/role_service_vibetype_username.secret.template rename to src/development/postgres/secrets/role_service_vibetype_username.secret.template diff --git a/src/development/secrets/postgres/role_service_zammad_password.secret.template b/src/development/postgres/secrets/role_service_zammad_password.secret.template similarity index 100% rename from src/development/secrets/postgres/role_service_zammad_password.secret.template rename to src/development/postgres/secrets/role_service_zammad_password.secret.template diff --git a/src/development/secrets/postgres/role_service_zammad_username.secret.template b/src/development/postgres/secrets/role_service_zammad_username.secret.template similarity index 100% rename from src/development/secrets/postgres/role_service_zammad_username.secret.template rename to src/development/postgres/secrets/role_service_zammad_username.secret.template diff --git a/src/development/secrets/postgres/user.secret.template b/src/development/postgres/secrets/user.secret.template similarity index 100% rename from src/development/secrets/postgres/user.secret.template rename to src/development/postgres/secrets/user.secret.template diff --git a/src/development/prometheus/compose.yaml b/src/development/prometheus/compose.yaml new file mode 100644 index 00000000..e50c54a4 --- /dev/null +++ b/src/development/prometheus/compose.yaml @@ -0,0 +1,22 @@ +services: + prometheus: + # You can access the metrics monitoring at [prometheus.app.localhost](https://prometheus.app.localhost/). + deploy: + labels: + - dargstack.profiles=analytics + - traefik.enable=true + - traefik.http.routers.prometheus.entryPoints=web + - traefik.http.routers.prometheus.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.prometheus.rule=Host(`prometheus.${STACK_DOMAIN}`) + - traefik.http.routers.prometheus-secure.entryPoints=web-secure + - traefik.http.routers.prometheus-secure.rule=Host(`prometheus.${STACK_DOMAIN}`) + - traefik.http.routers.prometheus-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.prometheus.loadbalancer.server.port=9090 + image: prom/prometheus:v3.11.0 + volumes: + - ./configurations/prometheus.yaml:/etc/prometheus/prometheus.yml:ro + - prometheus-data:/prometheus +volumes: + prometheus-data: + # The metrics monitoring's data. + {} diff --git a/src/production/configurations/prometheus/prometheus.yaml b/src/development/prometheus/configurations/prometheus.yaml similarity index 97% rename from src/production/configurations/prometheus/prometheus.yaml rename to src/development/prometheus/configurations/prometheus.yaml index 372a7580..0f7725eb 100644 --- a/src/production/configurations/prometheus/prometheus.yaml +++ b/src/development/prometheus/configurations/prometheus.yaml @@ -32,4 +32,4 @@ scrape_configs: static_configs: - targets: - redpanda:9644 - metrics_path: /public_metrics \ No newline at end of file + metrics_path: /public_metrics diff --git a/src/development/reccoom/compose.yaml b/src/development/reccoom/compose.yaml new file mode 100644 index 00000000..f476237a --- /dev/null +++ b/src/development/reccoom/compose.yaml @@ -0,0 +1,64 @@ +secrets: + reccoom-ingest-api-key: + # The AI provider's API key for the recommendation engine. + file: ../../../artifacts/secrets/reccoom-ingest-api-key.secret + reccoom-openai-api-key: + # The AI provider's API key for the recommendation engine. + file: ../../../artifacts/secrets/reccoom-openai-api-key.secret +services: + reccoom: + # You cannot access the recommendation service directly. + deploy: + labels: + - dargstack.development.build=../../../../reccoom + - dargstack.profiles=recommendation + - traefik.enable=true + - traefik.http.routers.reccoom.entryPoints=web + - traefik.http.routers.reccoom.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.reccoom.rule=Host(`reccoom.${STACK_DOMAIN}`) + - traefik.http.routers.reccoom-secure.entryPoints=web-secure + - traefik.http.routers.reccoom-secure.rule=Host(`reccoom.${STACK_DOMAIN}`) + - traefik.http.routers.reccoom-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.reccoom.loadbalancer.server.port=5245 + environment: + POSTGRES_HOST: postgres + RECCOOM_POSTGRES_HOST: reccoom_postgres + image: vibetype/reccoom:development + secrets: + - postgres-db + - postgres-password + - postgres-user + - source: reccoom-ingest-api-key + target: /run/environment-variables/INGEST_API_KEY + - reccoom-openai-api-key + volumes: + - ../../../../reccoom/:/srv/app/ # dargstack:dev-only + - ../postgraphile/configurations/jwtES256.key.pub:/run/configurations/jwtES256.key.pub:ro + reccoom_postgres: + # You can access reccoom's database via `adminer`. + deploy: + labels: + - dargstack.profiles=recommendation + environment: + POSTGRES_DB_FILE: /run/secrets/postgres-db + POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password + POSTGRES_USER_FILE: /run/secrets/postgres-user + image: pgvector/pgvector:0.8.2-pg18 + ports: # dargstack:dev-only + - 5433:5432 # dargstack:dev-only + secrets: + - postgres-db + - postgres-password + - postgres-user + volumes: + - reccoom-postgres-data:/var/lib/postgresql/ +volumes: + reccoom-postgres-data: + # The recommendation database's data. + {} +x-dargstack: + secrets: + reccoom-ingest-api-key: + type: third_party + reccoom-openai-api-key: + type: third_party diff --git a/src/development/secrets/reccoom/ingest-api-key.secret.template b/src/development/reccoom/secrets/ingest-api-key.secret.template similarity index 100% rename from src/development/secrets/reccoom/ingest-api-key.secret.template rename to src/development/reccoom/secrets/ingest-api-key.secret.template diff --git a/src/development/secrets/reccoom/openai-api-key.secret.template b/src/development/reccoom/secrets/openai-api-key.secret.template similarity index 100% rename from src/development/secrets/reccoom/openai-api-key.secret.template rename to src/development/reccoom/secrets/openai-api-key.secret.template diff --git a/src/development/redis/compose.yaml b/src/development/redis/compose.yaml new file mode 100644 index 00000000..332abf8a --- /dev/null +++ b/src/development/redis/compose.yaml @@ -0,0 +1,13 @@ +services: + redis: + # You cannot access the caching system via a web interface. + deploy: + labels: + - dargstack.profiles=zammad + image: redis:8.6.2-alpine + volumes: + - redis-data:/data +volumes: + redis-data: + # The caching system's data. + {} diff --git a/src/development/redpanda/compose.yaml b/src/development/redpanda/compose.yaml new file mode 100644 index 00000000..6a062743 --- /dev/null +++ b/src/development/redpanda/compose.yaml @@ -0,0 +1,45 @@ +services: + redpanda: + # You can access the event streaming platform's ui as described under `redpanda-console`. + command: + - redpanda start + - --mode dev-container # dargstack:dev-only + - --kafka-addr internal://0.0.0.0:9092,external://0.0.0.0:19092 + - --advertise-kafka-addr internal://redpanda:9092,external://localhost:19092 + - --pandaproxy-addr internal://0.0.0.0:8082,external://0.0.0.0:18082 + - --advertise-pandaproxy-addr internal://redpanda:8082,external://localhost:18082 + - --schema-registry-addr internal://0.0.0.0:8081,external://0.0.0.0:18081 + deploy: + labels: + - dargstack.profiles=event-streaming + # healthcheck: + # test: ["CMD-SHELL", "output=$(rpk cluster health --json); echo \"$output\" | grep -q '\"healthy\":true' || { echo \"$output\"; exit 1; }"] + # interval: 30s + # timeout: 10s + # retries: 3 + # start_period: 10s + image: redpandadata/redpanda:v26.1.2 + volumes: + - redpanda-data:/var/lib/redpanda/data + redpanda-console: + # You can access the event streaming platform's ui at [redpanda.app.localhost](https://redpanda.app.localhost/). + deploy: + labels: + - dargstack.profiles=event-streaming + - traefik.enable=true + - traefik.http.routers.redpanda.entryPoints=web + - traefik.http.routers.redpanda.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.redpanda.rule=Host(`redpanda.${STACK_DOMAIN}`) + - traefik.http.routers.redpanda-secure.entryPoints=web-secure + - traefik.http.routers.redpanda-secure.rule=Host(`redpanda.${STACK_DOMAIN}`) + - traefik.http.routers.redpanda-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.redpanda.loadbalancer.server.port=8080 + environment: + CONFIG_FILEPATH: /srv/app/redpanda-config.yaml + image: redpandadata/console:v3.7.0 + volumes: + - ./configurations/config.yaml:/srv/app/redpanda-config.yaml:ro +volumes: + redpanda-data: + # The message queue's data. + {} diff --git a/src/production/configurations/redpanda/config.yaml b/src/development/redpanda/configurations/config.yaml similarity index 58% rename from src/production/configurations/redpanda/config.yaml rename to src/development/redpanda/configurations/config.yaml index 26876a8a..93bf566c 100644 --- a/src/production/configurations/redpanda/config.yaml +++ b/src/development/redpanda/configurations/config.yaml @@ -1,12 +1,12 @@ kafka: brokers: - - redpanda:9092 + - redpanda:9092 redpanda: adminApi: enabled: true urls: - - http://redpanda:9644 + - http://redpanda:9644 schemaRegistry: enabled: true urls: - - http://redpanda:8081 + - http://redpanda:8081 diff --git a/src/development/secrets/elasticsearch/keystore_password.secret b/src/development/secrets/elasticsearch/keystore_password.secret deleted file mode 100644 index f3f85cae..00000000 --- a/src/development/secrets/elasticsearch/keystore_password.secret +++ /dev/null @@ -1 +0,0 @@ -elastic \ No newline at end of file diff --git a/src/development/secrets/elasticsearch/password.secret b/src/development/secrets/elasticsearch/password.secret deleted file mode 100644 index f3f85cae..00000000 --- a/src/development/secrets/elasticsearch/password.secret +++ /dev/null @@ -1 +0,0 @@ -elastic \ No newline at end of file diff --git a/src/development/secrets/grafana/admin_email.secret b/src/development/secrets/grafana/admin_email.secret deleted file mode 100644 index 06474a56..00000000 --- a/src/development/secrets/grafana/admin_email.secret +++ /dev/null @@ -1 +0,0 @@ -admin@localhost \ No newline at end of file diff --git a/src/development/secrets/grafana/admin_email.secret.template b/src/development/secrets/grafana/admin_email.secret.template deleted file mode 100644 index a6bb01c1..00000000 --- a/src/development/secrets/grafana/admin_email.secret.template +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/development/secrets/grafana/admin_password.secret b/src/development/secrets/grafana/admin_password.secret deleted file mode 100644 index 6da70038..00000000 --- a/src/development/secrets/grafana/admin_password.secret +++ /dev/null @@ -1 +0,0 @@ -grafana \ No newline at end of file diff --git a/src/development/secrets/grafana/admin_password.secret.template b/src/development/secrets/grafana/admin_password.secret.template deleted file mode 100644 index a6bb01c1..00000000 --- a/src/development/secrets/grafana/admin_password.secret.template +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/development/secrets/grafana/admin_user.secret b/src/development/secrets/grafana/admin_user.secret deleted file mode 100644 index 6da70038..00000000 --- a/src/development/secrets/grafana/admin_user.secret +++ /dev/null @@ -1 +0,0 @@ -grafana \ No newline at end of file diff --git a/src/development/secrets/grafana/admin_user.secret.template b/src/development/secrets/grafana/admin_user.secret.template deleted file mode 100644 index a6bb01c1..00000000 --- a/src/development/secrets/grafana/admin_user.secret.template +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/development/secrets/grafana/discord_webhook.secret b/src/development/secrets/grafana/discord_webhook.secret deleted file mode 100644 index 6075c4fb..00000000 --- a/src/development/secrets/grafana/discord_webhook.secret +++ /dev/null @@ -1 +0,0 @@ -UNSET THIRD PARTY SECRET \ No newline at end of file diff --git a/src/development/secrets/grafana/discord_webhook.secret.template b/src/development/secrets/grafana/discord_webhook.secret.template deleted file mode 100644 index 3ad15b2a..00000000 --- a/src/development/secrets/grafana/discord_webhook.secret.template +++ /dev/null @@ -1 +0,0 @@ -https://discord.com/api/webhooks// \ No newline at end of file diff --git a/src/development/secrets/jobber/aliases.secret b/src/development/secrets/jobber/aliases.secret deleted file mode 100644 index 20df8a23..00000000 --- a/src/development/secrets/jobber/aliases.secret +++ /dev/null @@ -1 +0,0 @@ -default: mail@localhost diff --git a/src/development/secrets/jobber/aws-bucket.secret b/src/development/secrets/jobber/aws-bucket.secret deleted file mode 100644 index dc9cb35b..00000000 --- a/src/development/secrets/jobber/aws-bucket.secret +++ /dev/null @@ -1 +0,0 @@ -vibetype-backup \ No newline at end of file diff --git a/src/development/secrets/jobber/aws-configuration.secret b/src/development/secrets/jobber/aws-configuration.secret deleted file mode 100644 index 20ea0b02..00000000 --- a/src/development/secrets/jobber/aws-configuration.secret +++ /dev/null @@ -1,16 +0,0 @@ -[default] -region = auto - -s3 = - endpoint_url = http://minio:9000 - signature_version = s3v4 - max_concurrent_requests = 100 - max_queue_size = 1000 - multipart_threshold = 50MB - # Edit the multipart_chunksize value according to the file sizes that you want to upload. The present configuration allows to upload files up to 10 GB (100 requests * 10MB). For example setting it to 5GB allows you to upload files up to 5TB. - multipart_chunksize = 10MB -s3api = - endpoint_url = http://minio:9000 - -[plugins] -endpoint = awscli_plugin_endpoint \ No newline at end of file diff --git a/src/development/secrets/jobber/aws-credentials.secret b/src/development/secrets/jobber/aws-credentials.secret deleted file mode 100644 index a28cae87..00000000 --- a/src/development/secrets/jobber/aws-credentials.secret +++ /dev/null @@ -1,3 +0,0 @@ -[default] -aws_access_key_id = s3user -aws_secret_access_key = s3password diff --git a/src/development/secrets/jobber/msmtprc.secret b/src/development/secrets/jobber/msmtprc.secret deleted file mode 100644 index 84ceec08..00000000 --- a/src/development/secrets/jobber/msmtprc.secret +++ /dev/null @@ -1,17 +0,0 @@ -# Set default values for all following accounts. -defaults -auth off -tls off -tls_trust_file /etc/ssl/certs/ca-certificates.crt -syslog on - -# localhost -account localhost -host localhost -port 25 -from mail@localhost -user mail@localhost - -# Set a default account -account default : localhost -aliases /etc/aliases diff --git a/src/development/secrets/portainer/admin-password.secret b/src/development/secrets/portainer/admin-password.secret deleted file mode 100644 index 45aee45f..00000000 --- a/src/development/secrets/portainer/admin-password.secret +++ /dev/null @@ -1 +0,0 @@ -123456123456 \ No newline at end of file diff --git a/src/development/secrets/postgraphile/connection.secret b/src/development/secrets/postgraphile/connection.secret deleted file mode 100644 index f94cfa39..00000000 --- a/src/development/secrets/postgraphile/connection.secret +++ /dev/null @@ -1 +0,0 @@ -postgresql://postgraphile:postgraphile@postgres/vibetype \ No newline at end of file diff --git a/src/development/secrets/postgraphile/jwt-secret.secret b/src/development/secrets/postgraphile/jwt-secret.secret deleted file mode 100644 index 8b5b34e0..00000000 --- a/src/development/secrets/postgraphile/jwt-secret.secret +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKQIBAAKCAgEAtBuHX2uH5GviLbocSUBKaRE4iKEjjEoB1eh7VQdBoKi1Wd+D -NUilVyCtq0ootPSDR/8yOTa8W396Wu1xm07+xDAyFzsilIppu8DNcJo28Gb3PZCm -4nXmITPkknVSKf80hosgauoe7itqDxgMASYTU8CgBH8zXPaowhqzjz0M/T9RqD1m -RzMn3NEprDnaVAsh11r3DSandHjsnlgKCf1dlh4ixCZ6zUhtb0sT6GxuJaGjNVev -LbPLuugMTu3d484FwbND+AmpAjIgdnX5lJQneT7HxgnaiwLDazSwLM0jmiyEvnrm -SPJZ2sgp87XS0VTVwMYk3M3RCy/3RiamJG5v7qZuVr4L87cXWvLHCwV5dGaPu0Dc -3/75lcKcWy+BtBQUI+n+om1EitLtUo+lxE9aSyG6bhYQNu+XMRwY768TrzrvBwYt -5JxoO9y3ybIFzoi/mufkp2NY7XB7CHfMcrn3XzCBbXJwt0Zlu2K1R8FPVN0Slsjt -OVQun1al9cjC808hihjpiiGpKURoaFsxkh4B8n2Qli/fl5BQVAPClmFbsvToSzy+ -ENEDpXc53eve5Dhzx5SB/qe0DwlljDdc2W8nhozYHaOtnLnhG/TU9mmOliw5jmsH -b5m1OMdBXMEhrGv4hkHJNg9+hCSJDzvbFRkNY95lZj/fbtFrSHR5WwdsgI8CAwEA -AQKCAgEAjxyMvbJpAYUD7MfEcCQovEHVxSMdZvzrZnhbf53LdQh5SZeD35QA5TFv -Lfs4S0k7A7twweuPUbkClTu1GO51G5kcRD9V4+fzyh+SXpX7b5yxenb1VF7QZLOi -PQoCJqLFrt7f/HRZ7XALz0CRUVxa4SLfQ5N7UbQhNlMXOIsPRi/JB8D6AztPwnNl -BJfXtw49bqy2P/nl93OauNtF5tgvQ/hgMbJNw854PoXOpNF72GUOlXU+GjeSe8qk -9RanSLtM8bQrHu02ISuJhfeKQJhUoU/UV7U+tVSVyRrNnlvGnVwggmaPk4kXAvQn -+aIRiQo66vnHErhsEdKkTlapj6s9PnMmtwNJcv/Bp8/621Pr3mqG4WtZrYxMYFzN -5tXwjkQQRpLRyi0pwu6IHj9w+y83oJKi6HTo//tJbKEzqx1jjhl4FiNwKMDfxlpp -eN52c723DELgc9AhRCXP50z2OnjByeI296bj+Vyv0noDDSLK5pq4Hm9Ck62NCfsV -YqCmvVu5JwFP+MYUECNRH6d98wsWhgKvAJe37JH9BEjxvW3oLB4XofSqGrFePNEM -jRdnLdJxFC0AfWVMu4JW1MvcZct7nkFd91YGKzZRlasbnRVQ4qHTYhojwiM2LF8E -aLxKOKegxfRFBkfHSzxzXAA2o4r2pJo2WZI+lBuBd6TmTX21ZLkCggEBAOiIkB9k -oOOe0UyVDkEJTk62y5LdrEG3KpEjaW12qFP1IGgqP5gDqhDQQ4WB4Pqzl1uoqy2L -WafpfEH2lBTeYMV4Wg5WkgZIrCRthhdwoDqluUttN612VuT4QlLvnB37zdDzkzfL -asnCKmXfaOSU/qvHcUvS6SgnwKVWBqFSB2VWBgLGVH2Ty/Yc9TpMraSZA2ATnieR -7aZy1zegwp7n3sO3mmY0w7A+7YoUms1+Ibe5aNmyw/ThDJBkx+UG0nv9qpoA4yNH -+S355EJyYdUNoh0d8D3COk72YjcODmiB+6W0qoPQ3SeQ2X/6+xvFNkx2/34+nhcy -tNNqBkeKe2IpkzUCggEBAMZIjpAPiT/7rb0/ea8DZAmdOhjER5NOic3gz6RN7oqj -MQ2ktzxfGJez+wnjaqv1xF+qwZi69JwflK3FWIZR3G1wlO5pKMkef5l1N/VLvW2k -opfx+82sVXi/Cx7w8o6IAS60ZjyxTWhnYQ695QIKP79+poWJ3zi9sBpsj7hHbPco -tu4kJKVpXOVcmMluSqrn+3kT7Xlpqy6qnFBggRHLJD9okM3TLU3znHRUFhViova3 -kyD65qViITUCIMDmigfCbFYLVGEdNyMDBH0OGSUaSwY0d/KS+43JxLeh84ylHPYZ -tfPiQxCxn2VyD2h4XkNIdgoY7f/NbkzjkLEdSdc0GTMCggEBAJzgdQsY4opt/RpD -fUhXNONZ42GD99Cl+CvOzjOxV8K8n05nIlw2LKgHOWZ6xwOb4cNOuZ2SY7wqg1Al -QKYLmRHgLjF6Ki3fHXO+CDcMHq0yXR4L4wI90kXLT0OQr6xy0tnjWjDMJZFUUzJD -VQrRkjbl6QOUmQQkPY3Nyc9P/flZ3dhFYX8PFQ1HYBIi5Qskx+grlAlyI/ilhZjb -9jEqkVlNJvdJJbRj3/HGEOIN9EV06s3kEtvEcKuBsnJK9fn8mvonGxYUWoGwE1TY -wjPwbKxkJE4mGRxokL4/12yeNN4IUvl3EZy91l7HPl3v4MAZkCjlqdZQuTngT7g4 -LyF8lvkCggEAQJIUSvmkOn/dPknTeJjkFPVsm/AfVus2mSLiu6DdU6x9JvJC8ZgO -TCjCUaALduBcCMN0tCX9znHCWyxu/Z7e54zIEzOPop8Z8oFnravyjigVAuI8m+fJ -Fb8xAex5MM09hVYeDRm4GY3A36obT6TCybuWwtn5JWMwXAqKavpnk71ghQCkJnG/ -XLngz4fpLOrKy80fgnBU3KeWq94hagf7T+LfdQeWM1Jn2sIfRuuOIkX51b2fKrBg -HAELZYYt4QUBfqvF2LJI6E/tQEQ9EAYV9HlVrlsLLEtOknZc96o1WjdZN4ixSlht -jf2s4tp/5+1K81LW/nyJtOROlD7zcu92XQKCAQAUgxlfDbjBz64DrpyJDhHQq89X -WH0+zXhwuWiQwzlbSQUJnKnVwPwcnKD/zxK601fWT26C01l3senX49zy/KZI1BtD -mco+Mr+wKGNPcOoVhhzYTDk6R+jfsyel0uXcky6LPncCLgsq7UpVylONaDO5m+fL -2wUg67tJu13zHMSWml5+o51jY/Qn/UjsSyEj7gQHq4xM2ottb6fh3CgmMvJKjrAP -juM36ePNpeQMBHf9Djt7a/tuN0OZdGo4sjBSJ9fEAoMGtxZ1nQyu06ncBQMtlqeD -+pzL73569phIJDX7E11wczzx/QObJ4dMznCgqEfpzlSNOhPl+bqyzAdVXO4V ------END RSA PRIVATE KEY----- diff --git a/src/development/secrets/postgraphile/owner-connection.secret b/src/development/secrets/postgraphile/owner-connection.secret deleted file mode 100644 index 093bbed2..00000000 --- a/src/development/secrets/postgraphile/owner-connection.secret +++ /dev/null @@ -1 +0,0 @@ -postgresql://postgres:postgres@postgres/vibetype \ No newline at end of file diff --git a/src/development/secrets/postgres/db.secret.template b/src/development/secrets/postgres/db.secret.template deleted file mode 100644 index a6bb01c1..00000000 --- a/src/development/secrets/postgres/db.secret.template +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/development/secrets/postgres/password.secret b/src/development/secrets/postgres/password.secret deleted file mode 100644 index f1349944..00000000 --- a/src/development/secrets/postgres/password.secret +++ /dev/null @@ -1 +0,0 @@ -postgres diff --git a/src/development/secrets/postgres/password.secret.template b/src/development/secrets/postgres/password.secret.template deleted file mode 100644 index a6bb01c1..00000000 --- a/src/development/secrets/postgres/password.secret.template +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/development/secrets/postgres/role_service_grafana_password.secret b/src/development/secrets/postgres/role_service_grafana_password.secret deleted file mode 100644 index 6da70038..00000000 --- a/src/development/secrets/postgres/role_service_grafana_password.secret +++ /dev/null @@ -1 +0,0 @@ -grafana \ No newline at end of file diff --git a/src/development/secrets/postgres/role_service_grafana_username.secret b/src/development/secrets/postgres/role_service_grafana_username.secret deleted file mode 100644 index 6da70038..00000000 --- a/src/development/secrets/postgres/role_service_grafana_username.secret +++ /dev/null @@ -1 +0,0 @@ -grafana \ No newline at end of file diff --git a/src/development/secrets/postgres/role_service_postgraphile_password.secret b/src/development/secrets/postgres/role_service_postgraphile_password.secret deleted file mode 100644 index a4da12ec..00000000 --- a/src/development/secrets/postgres/role_service_postgraphile_password.secret +++ /dev/null @@ -1 +0,0 @@ -postgraphile \ No newline at end of file diff --git a/src/development/secrets/postgres/role_service_postgraphile_username.secret b/src/development/secrets/postgres/role_service_postgraphile_username.secret deleted file mode 100644 index a4da12ec..00000000 --- a/src/development/secrets/postgres/role_service_postgraphile_username.secret +++ /dev/null @@ -1 +0,0 @@ -postgraphile \ No newline at end of file diff --git a/src/development/secrets/postgres/role_service_vibetype_password.secret b/src/development/secrets/postgres/role_service_vibetype_password.secret deleted file mode 100644 index 5e84b8c8..00000000 --- a/src/development/secrets/postgres/role_service_vibetype_password.secret +++ /dev/null @@ -1 +0,0 @@ -tusd \ No newline at end of file diff --git a/src/development/secrets/postgres/role_service_vibetype_username.secret b/src/development/secrets/postgres/role_service_vibetype_username.secret deleted file mode 100644 index 9294c3d9..00000000 --- a/src/development/secrets/postgres/role_service_vibetype_username.secret +++ /dev/null @@ -1 +0,0 @@ -vibetype \ No newline at end of file diff --git a/src/development/secrets/postgres/role_service_zammad_password.secret b/src/development/secrets/postgres/role_service_zammad_password.secret deleted file mode 100644 index 70699eae..00000000 --- a/src/development/secrets/postgres/role_service_zammad_password.secret +++ /dev/null @@ -1 +0,0 @@ -zammad \ No newline at end of file diff --git a/src/development/secrets/postgres/role_service_zammad_username.secret b/src/development/secrets/postgres/role_service_zammad_username.secret deleted file mode 100644 index 70699eae..00000000 --- a/src/development/secrets/postgres/role_service_zammad_username.secret +++ /dev/null @@ -1 +0,0 @@ -zammad \ No newline at end of file diff --git a/src/development/secrets/postgres/user.secret b/src/development/secrets/postgres/user.secret deleted file mode 100644 index f1349944..00000000 --- a/src/development/secrets/postgres/user.secret +++ /dev/null @@ -1 +0,0 @@ -postgres diff --git a/src/development/secrets/reccoom/ingest-api-key.secret b/src/development/secrets/reccoom/ingest-api-key.secret deleted file mode 100644 index 6075c4fb..00000000 --- a/src/development/secrets/reccoom/ingest-api-key.secret +++ /dev/null @@ -1 +0,0 @@ -UNSET THIRD PARTY SECRET \ No newline at end of file diff --git a/src/development/secrets/reccoom/openai-api-key.secret b/src/development/secrets/reccoom/openai-api-key.secret deleted file mode 100644 index 6075c4fb..00000000 --- a/src/development/secrets/reccoom/openai-api-key.secret +++ /dev/null @@ -1 +0,0 @@ -UNSET THIRD PARTY SECRET \ No newline at end of file diff --git a/src/development/secrets/sqitch/target.secret b/src/development/secrets/sqitch/target.secret deleted file mode 100644 index 12789f3c..00000000 --- a/src/development/secrets/sqitch/target.secret +++ /dev/null @@ -1 +0,0 @@ -db:pg://postgres:postgres@postgres/vibetype \ No newline at end of file diff --git a/src/development/secrets/tusd/aws.secret b/src/development/secrets/tusd/aws.secret deleted file mode 100644 index eed88684..00000000 --- a/src/development/secrets/tusd/aws.secret +++ /dev/null @@ -1,3 +0,0 @@ -[default] -aws_access_key_id = s3user -aws_secret_access_key = s3password \ No newline at end of file diff --git a/src/development/secrets/vibetype/api-notification.secret b/src/development/secrets/vibetype/api-notification.secret deleted file mode 100644 index 9294c3d9..00000000 --- a/src/development/secrets/vibetype/api-notification.secret +++ /dev/null @@ -1 +0,0 @@ -vibetype \ No newline at end of file diff --git a/src/development/secrets/vibetype/aws-credentials.secret b/src/development/secrets/vibetype/aws-credentials.secret deleted file mode 100644 index 6075c4fb..00000000 --- a/src/development/secrets/vibetype/aws-credentials.secret +++ /dev/null @@ -1 +0,0 @@ -UNSET THIRD PARTY SECRET \ No newline at end of file diff --git a/src/development/secrets/vibetype/firebase-service-account-credentials.secret b/src/development/secrets/vibetype/firebase-service-account-credentials.secret deleted file mode 100644 index 6075c4fb..00000000 --- a/src/development/secrets/vibetype/firebase-service-account-credentials.secret +++ /dev/null @@ -1 +0,0 @@ -UNSET THIRD PARTY SECRET \ No newline at end of file diff --git a/src/development/secrets/vibetype/monday.secret b/src/development/secrets/vibetype/monday.secret deleted file mode 100644 index 6075c4fb..00000000 --- a/src/development/secrets/vibetype/monday.secret +++ /dev/null @@ -1 +0,0 @@ -UNSET THIRD PARTY SECRET \ No newline at end of file diff --git a/src/development/secrets/vibetype/openai-api-key.secret b/src/development/secrets/vibetype/openai-api-key.secret deleted file mode 100644 index 6075c4fb..00000000 --- a/src/development/secrets/vibetype/openai-api-key.secret +++ /dev/null @@ -1 +0,0 @@ -UNSET THIRD PARTY SECRET \ No newline at end of file diff --git a/src/development/secrets/vibetype/turnstile-key.secret b/src/development/secrets/vibetype/turnstile-key.secret deleted file mode 100644 index d8c28c70..00000000 --- a/src/development/secrets/vibetype/turnstile-key.secret +++ /dev/null @@ -1 +0,0 @@ -1x0000000000000000000000000000000AA \ No newline at end of file diff --git a/src/development/sqitch/compose.yaml b/src/development/sqitch/compose.yaml new file mode 100644 index 00000000..0771a323 --- /dev/null +++ b/src/development/sqitch/compose.yaml @@ -0,0 +1,29 @@ +secrets: + sqitch-target: + # The database change management application's database connection string. + file: ../../../artifacts/secrets/sqitch-target.secret +services: + sqitch: + # You cannot access the database migrations directly. + deploy: + labels: + - dargstack.development.build=../../../../sqitch + - dargstack.profiles=default + image: vibetype/sqitch:development + secrets: + - postgres-role-service-grafana-password + - postgres-role-service-grafana-username + - postgres-role-service-postgraphile-password + - postgres-role-service-postgraphile-username + - postgres-role-service-vibetype-password + - postgres-role-service-vibetype-username + - postgres-role-service-zammad-username + - postgres-role-service-zammad-password + - sqitch-target + volumes: + - ../../../../sqitch/:/srv/app/ +x-dargstack: + secrets: + sqitch-target: + type: template + template: postgres://{{secret:postgres-user}}:{{secret:postgres-password}}@postgres:5432/{{secret:postgres-db}} diff --git a/src/development/secrets/sqitch/target.secret.template b/src/development/sqitch/secrets/target.secret.template similarity index 100% rename from src/development/secrets/sqitch/target.secret.template rename to src/development/sqitch/secrets/target.secret.template diff --git a/src/development/stack.env.template b/src/development/stack.env.template deleted file mode 100644 index 261fdee5..00000000 --- a/src/development/stack.env.template +++ /dev/null @@ -1,8 +0,0 @@ -TUSD_BUCKET=vibetype-images -TUSD_ENDPOINT=http://minio:9000 -TUSD_MAX_SIZE=10485760 -TUSD_REGION=eu-central-1 -VIBETYPE_AWS_REGION=eu-central-1 -VIBETYPE_NUXT_PUBLIC_GTAG_ID=G-WMQ1JY99XH -VIBETYPE_NUXT_PUBLIC_TURNSTILE_SITE_KEY=1x00000000000000000000AA -VIBETYPE_NUXT_PUBLIC_VIBETYPE_EMAIL_LIMIT24H=100 \ No newline at end of file diff --git a/src/development/stack.yml b/src/development/stack.yml deleted file mode 100644 index 893fafe1..00000000 --- a/src/development/stack.yml +++ /dev/null @@ -1,745 +0,0 @@ -# vibetype.app -# https://vibetype.app/ -# Vibetype -# https://github.com/maevsi/vibetype/ ---- -x-shared: - zammad-service: - &zammad-service # You can access the helpdesk at [zammad.app.localhost](https://zammad.app.localhost/). - environment: &zammad-environment - ELASTICSEARCH_HOST: elasticsearch - ELASTICSEARCH_SCHEMA: https - ELASTICSEARCH_USER: elastic - MEMCACHE_SERVERS: memcached:11211 - NGINX_SERVER_SCHEME: https - POSTGRESQL_DB: zammad - POSTGRESQL_DB_CREATE: "false" - POSTGRESQL_HOST: postgres - POSTGRESQL_OPTIONS: ?pool=50 - REDIS_URL: redis://redis:6379 - image: ghcr.io/zammad/zammad:6.5.2-90 - secrets: - - source: elasticsearch-password - target: /run/environment-variables/ELASTICSEARCH_PASS - - source: postgres_role_service_zammad_username - target: /run/environment-variables/POSTGRESQL_USER - - source: postgres_role_service_zammad_password - target: /run/environment-variables/POSTGRESQL_PASS - volumes: - - zammad_data:/opt/zammad/storage - - ../production/configurations/zammad/docker-entrypoint.sh:/docker-entrypoint.sh:ro -secrets: - elasticsearch-keystore_password: - # The search engine's password for the keystore. - file: ./secrets/elasticsearch/keystore_password.secret - elasticsearch-password: - # The search engine's password for the default user. - file: ./secrets/elasticsearch/password.secret - grafana_admin_email: - # The observation dashboard's admin email. - file: ./secrets/grafana/admin_email.secret - grafana_admin_password: - # The observation dashboard's admin password. - file: ./secrets/grafana/admin_password.secret - grafana_admin_user: - # The observation dashboard's admin user. - file: ./secrets/grafana/admin_user.secret - grafana_discord_webhook: - # The observation dashboard's contact point for Discord. - file: ./secrets/grafana/discord_webhook.secret - jobber_aliases: - # The job scheduler's SMTP client mail alias. - file: ./secrets/jobber/aliases.secret - jobber_aws-bucket: - # The job scheduler's AWS bucket name. - file: ./secrets/jobber/aws-bucket.secret - jobber_aws-credentials: - # The job scheduler's AWS credentials. - file: ./secrets/jobber/aws-credentials.secret - jobber_aws-configuration: - # The job scheduler's AWS configuration. - file: ./secrets/jobber/aws-configuration.secret - jobber_msmtprc: - # The job scheduler's SMTP client configuration. - file: ./secrets/jobber/msmtprc.secret - portainer_admin-password: - # The container manager's admin password. - file: ./secrets/portainer/admin-password.secret - postgraphile_connection: - # The GraphQL API's database URI. - file: ./secrets/postgraphile/connection.secret - postgraphile_jwt-secret: - # The GraphQL API's JWT secret. - file: ./secrets/postgraphile/jwt-secret.secret - postgraphile_owner-connection: - # The GraphQL API's database owner URI. - file: ./secrets/postgraphile/owner-connection.secret - postgres_db: - # The database's name. - file: ./secrets/postgres/db.secret - postgres_password: - # The database's password. - file: ./secrets/postgres/password.secret - postgres_role_service_grafana_password: - # The password of the observation dashboard's database role. - file: ./secrets/postgres/role_service_grafana_password.secret - postgres_role_service_grafana_username: - # The username of the observation dashboard's database role. - file: ./secrets/postgres/role_service_grafana_username.secret - postgres_role_service_postgraphile_password: - # The password of the GraphQL API database wrapper's database role. - file: ./secrets/postgres/role_service_postgraphile_password.secret - postgres_role_service_postgraphile_username: - # The username of the GraphQL API database wrapper's database role. - file: ./secrets/postgres/role_service_postgraphile_username.secret - postgres_role_service_vibetype_password: - # The `tusd` database role's password. - file: ./secrets/postgres/role_service_vibetype_password.secret - postgres_role_service_vibetype_username: - # The `tusd` database role's password. - file: ./secrets/postgres/role_service_vibetype_username.secret - postgres_role_service_zammad_password: - # The password of the customer service database role. - file: ./secrets/postgres/role_service_zammad_password.secret - postgres_role_service_zammad_username: - # The username of the customer service database role. - file: ./secrets/postgres/role_service_zammad_username.secret - postgres_user: - # The database's default user. - file: ./secrets/postgres/user.secret - reccoom_ingest-api-key: - # The AI provider's API key for the recommendation engine. - file: ./secrets/reccoom/ingest-api-key.secret - reccoom_openai-api-key: - # The AI provider's API key for the recommendation engine. - file: ./secrets/reccoom/openai-api-key.secret - sqitch_target: - # The database change management application's database connection string. - file: ./secrets/sqitch/target.secret - tusd_aws: - # The upload service's s3 credentials file. - file: ./secrets/tusd/aws.secret - vibetype_api-notification-secret: - # The notification endpoint's secret. - file: ./secrets/vibetype/api-notification.secret - vibetype_aws-credentials: - # The cloud computing provider's user credentials. - file: ./secrets/vibetype/aws-credentials.secret - vibetype_firebase-service-account-credentials: - # The notification provider's service account credentials. - file: ./secrets/vibetype/firebase-service-account-credentials.secret - vibetype_monday: - # The project management software's configuration. - file: ./secrets/vibetype/monday.secret - vibetype_openai-api-key: - # The AI provider's API key for the frontend. - file: ./secrets/vibetype/openai-api-key.secret - vibetype_turnstile-key: - # The captcha provider's application key. - file: ./secrets/vibetype/turnstile-key.secret -services: - adminer: - # You can access the database's frontend at [adminer.app.localhost](https://adminer.app.localhost/). - # This information is required for login: - # - # | | | - # | -------- | ------------------- | - # | System | PostgreSQL | - # | Server | postgres | - # | Username | [postgres_user] | - # | Password | [postgres_password] | - # | Database | [postgres_db] | - # - # Values in square brackets are [Docker secrets](https://docs.docker.com/engine/swarm/secrets/). - deploy: - labels: - - traefik.enable=true - - traefik.http.routers.adminer.entryPoints=web - - traefik.http.routers.adminer.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.adminer.rule=Host(`adminer.${STACK_DOMAIN}`) - - traefik.http.routers.adminer_secure.entryPoints=web-secure - - traefik.http.routers.adminer_secure.rule=Host(`adminer.${STACK_DOMAIN}`) - - traefik.http.routers.adminer_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.adminer.loadbalancer.server.port=8080 - image: adminer:5.4.2-standalone - volumes: - - ../production/configurations/adminer/adminer.css:/var/www/html/adminer.css:ro - debezium: - # You can see how changes in the database end up in the event stream using `redpanda-console`. - environment: - BOOTSTRAP_SERVERS: redpanda:9092 - CONFIG_STORAGE_TOPIC: connect_configs - GROUP_ID: 1 - OFFSET_STORAGE_TOPIC: connect_offsets - STATUS_STORAGE_TOPIC: connect_statuses - # healthcheck: - # test: ["CMD", "curl", "--fail", "--silent", "--show-error", "http://localhost:8083/connectors"] - # interval: 30s - # timeout: 10s - # retries: 3 - # start_period: 20s - image: quay.io/debezium/connect:3.5 - volumes: - - debezium_kafka_configuration:/kafka/config - - debezium_kafka_data:/kafka/data - - debezium_kafka_logs:/kafka/logs - debezium-postgres-connector: - # You can check the database connector's setup logs using `portainer`. - command: /entrypoint.sh - deploy: - restart_policy: - condition: on-failure - entrypoint: sh - image: curlimages/curl:8.18.0 - secrets: - - postgres_db - - postgres_password - - postgres_user - volumes: - - ../production/configurations/debezium-postgres-connector/entrypoint.sh:/entrypoint.sh:ro - elasticsearch: - # You cannot access the search engine via a web interface. - environment: - bootstrap.memory_lock: "true" - discovery.type: single-node - ELASTIC_PASSWORD_FILE: /run/secrets/elasticsearch-password - ES_JAVA_OPTS: -Xms1g -Xmx1g - KEYSTORE_PASSWORD_FILE: /run/secrets/elasticsearch-keystore_password - network.publish_host: elasticsearch - image: elasticsearch:8.19.12 - secrets: - - source: elasticsearch-keystore_password - uid: "1000" - gid: "1000" - mode: 0o400 - - source: elasticsearch-password - uid: "1000" - gid: "1000" - mode: 0o400 - ulimits: - nofile: - soft: 65535 - hard: 65535 - memlock: - soft: -1 - hard: -1 - volumes: - - elasticsearch-configuration:/usr/share/elasticsearch/config - - elasticsearch_data:/usr/share/elasticsearch/data - geoip: - # You cannot access the ip geolocator via a web interface. - image: ghcr.io/observabilitystack/geoip-api:2026-02 - grafana: - # You can access the observation dashboard at [grafana.app.localhost](https://grafana.app.localhost/). - deploy: - labels: - - traefik.enable=true - - traefik.http.routers.grafana.entryPoints=web - - traefik.http.routers.grafana.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.grafana.rule=Host(`grafana.${STACK_DOMAIN}`) - - traefik.http.routers.grafana_secure.entryPoints=web-secure - - traefik.http.routers.grafana_secure.rule=Host(`grafana.${STACK_DOMAIN}`) - - traefik.http.routers.grafana_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.grafana.loadbalancer.server.port=3000 - environment: - GF_DATABASE_HOST: postgres:5432 - GF_DATABASE_PASSWORD__FILE: /run/secrets/postgres_role_service_grafana_password - GF_DATABASE_TYPE: postgres - GF_DATABASE_USER__FILE: /run/secrets/postgres_role_service_grafana_username - GF_SECURITY_ADMIN_EMAIL__FILE: /run/secrets/grafana_admin_email - GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin_password - GF_SECURITY_ADMIN_USER__FILE: /run/secrets/grafana_admin_user - GF_SERVER_ROOT_URL: https://grafana.${STACK_DOMAIN}/ - image: grafana/grafana:12.4.1 - secrets: - - grafana_admin_email - - grafana_admin_password - - grafana_admin_user - - grafana_discord_webhook - - postgres_db - - postgres_role_service_grafana_password - - postgres_role_service_grafana_username - volumes: - - ../production/configurations/grafana/dashboards:/var/lib/grafana/dashboards:ro - - ../production/configurations/grafana/provisioning:/etc/grafana/provisioning:ro - - grafana_data:/var/lib/grafana - jobber: - # You cannot access the jobber via a web interface. - environment: - AWS_SHARED_CREDENTIALS_FILE: /run/secrets/jobber_aws-credentials - image: ghcr.io/dargmuesli/jobber-aws-msmtp:1.3.0 - secrets: - - source: jobber_aliases - target: /etc/aliases - - jobber_aws-bucket - - jobber_aws-credentials - - source: jobber_aws-configuration - target: /home/jobberuser/.aws/config - - source: jobber_msmtprc - target: /etc/msmtprc - volumes: - - ../production/backups/postgres/:/backups/ - - ./configurations/jobber/.jobber:/home/jobberuser/.jobber:ro - memcached: - # You cannot access the caching system via a web interface. - image: memcached:1.6.41-alpine - # command: memcached -m 256M - minio: #DARGSTACK-REMOVE - # You can access the s3 console at [minio.app.localhost](https://minio.app.localhost/). - # You can access the s3 api service at [s3.app.localhost](https://s3.app.localhost/) if you want to access via cli from outside the stack. - entrypoint: /patched-entrypoint.sh #DARGSTACK-REMOVE - command: server /data --console-address ":9001" #DARGSTACK-REMOVE - deploy: #DARGSTACK-REMOVE - labels: #DARGSTACK-REMOVE - - traefik.enable=true #DARGSTACK-REMOVE - # Minio Console - - traefik.http.routers.minio.entryPoints=web #DARGSTACK-REMOVE - - traefik.http.routers.minio.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.minio.rule=Host(`minio.${STACK_DOMAIN}`) #DARGSTACK-REMOVE - - traefik.http.routers.minio.service=minio #DARGSTACK-REMOVE - - traefik.http.routers.minio_secure.entryPoints=web-secure #DARGSTACK-REMOVE - - traefik.http.routers.minio_secure.rule=Host(`minio.${STACK_DOMAIN}`) #DARGSTACK-REMOVE - - traefik.http.routers.minio_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.routers.minio_secure.service=minio #DARGSTACK-REMOVE - - traefik.http.services.minio.loadbalancer.server.port=9001 #DARGSTACK-REMOVE - - traefik.http.services.minio.loadbalancer.passhostheader=true #DARGSTACK-REMOVE - # Minio itself - - traefik.http.routers.s3.entryPoints=web #DARGSTACK-REMOVE - - traefik.http.routers.s3.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.s3.rule=Host(`s3.${STACK_DOMAIN}`) #DARGSTACK-REMOVE - - traefik.http.routers.s3.service=s3 #DARGSTACK-REMOVE - - traefik.http.routers.s3_secure.entryPoints=web-secure #DARGSTACK-REMOVE - - traefik.http.routers.s3_secure.rule=Host(`s3.${STACK_DOMAIN}`) #DARGSTACK-REMOVE - - traefik.http.routers.s3_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.routers.s3_secure.service=s3 #DARGSTACK-REMOVE - - traefik.http.services.s3.loadbalancer.server.port=9000 #DARGSTACK-REMOVE - - traefik.http.services.s3.loadbalancer.passhostheader=true #DARGSTACK-REMOVE - image: minio/minio #DARGSTACK-REMOVE - volumes: #DARGSTACK-REMOVE - - minio_data:/data #DARGSTACK-REMOVE - - ./configurations/minio/entrypoint.sh:/patched-entrypoint.sh #DARGSTACK-REMOVE - environment: #DARGSTACK-REMOVE - MINIO_ROOT_PASSWORD: s3password #DARGSTACK-REMOVE - MINIO_ROOT_USER: s3user #DARGSTACK-REMOVE - portainer: - # You can access the container manager's frontend at [portainer.app.localhost](https://portainer.app.localhost/). - command: -H tcp://tasks.portainer-agent:9001 --tlsskipverify --admin-password-file '/run/secrets/portainer_admin-password' - deploy: - labels: - - traefik.enable=true - - traefik.http.routers.portainer.entryPoints=web - - traefik.http.routers.portainer.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.portainer.rule=Host(`portainer.${STACK_DOMAIN}`) - - traefik.http.routers.portainer_secure.entryPoints=web-secure - - traefik.http.routers.portainer_secure.rule=Host(`portainer.${STACK_DOMAIN}`) - - traefik.http.routers.portainer_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.portainer.loadbalancer.server.port=9000 - placement: - constraints: - - node.role == manager - replicas: 1 - image: portainer/portainer-ce:2.39.0-alpine - secrets: - - portainer_admin-password - volumes: - - portainer_data:/data - portainer-agent: - # You cannot access the container manager's agent directly. - deploy: - mode: global - placement: - constraints: - - node.platform.os == linux - image: portainer/agent:2.39.0 - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - /var/lib/docker/volumes:/var/lib/docker/volumes - postgraphile: - # You can access the GraphQL API for the PostgreSQL database at [postgraphile.app.localhost](https://postgraphile.app.localhost/). - deploy: - labels: - - traefik.enable=true - - traefik.http.middlewares.postgraphile_auth.forwardauth.address=http://vibetype:3000/api/internal/service/postgraphile/authentication - - traefik.http.middlewares.postgraphile_auth.forwardauth.forwardBody=true - - traefik.http.middlewares.postgraphile_auth.forwardauth.preserveRequestMethod=true - - traefik.http.middlewares.postgraphile_cors.headers.accessControlAllowCredentials=true - - traefik.http.middlewares.postgraphile_cors.headers.accessControlAllowHeaders=authorization - - traefik.http.middlewares.postgraphile_cors.headers.accessControlAllowOriginList=https://${STACK_DOMAIN},https://localhost:3000,https://app.localhost:3000 - - traefik.http.routers.postgraphile.entryPoints=web - - traefik.http.routers.postgraphile.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.postgraphile.rule=Host(`postgraphile.${STACK_DOMAIN}`) - - traefik.http.routers.postgraphile_secure.entryPoints=web-secure - - traefik.http.routers.postgraphile_secure.middlewares=postgraphile_auth,postgraphile_cors - - traefik.http.routers.postgraphile_secure.rule=Host(`postgraphile.${STACK_DOMAIN}`) && Path(`/graphql`) - - traefik.http.routers.postgraphile_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.routers.postgraphile_secure_graphiql.entryPoints=web-secure - - traefik.http.routers.postgraphile_secure_graphiql.rule=Host(`postgraphile.${STACK_DOMAIN}`) - - traefik.http.routers.postgraphile_secure_graphiql.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.postgraphile.loadbalancer.server.port=5678 - # # Use the DEBUG environment variable for extended debugging. - # environment: - # DEBUG: graphile-build:warn,graphile-build-pg:sql - image: maevsi/postgraphile:dev - secrets: - - source: postgraphile_connection - target: /run/environment-variables/POSTGRAPHILE_CONNECTION - - source: postgraphile_jwt-secret - target: /run/environment-variables/POSTGRAPHILE_JWT_SECRET_KEY - - source: postgraphile_owner-connection - target: /run/environment-variables/POSTGRAPHILE_OWNER_CONNECTION - volumes: - - ../../../postgraphile/:/srv/app/ #DARGSTACK-REMOVE - - ./configurations/postgraphile/jwtRS256.key.pub:/run/environment-variables/POSTGRAPHILE_JWT_PUBLIC_KEY:ro - - pnpm_data:/srv/.pnpm-store/ #DARGSTACK-REMOVE - - postgraphile_data:/srv/app/node_modules #DARGSTACK-REMOVE - postgres: - # You can access the database via `adminer`. - command: -c vibetype.jwt_expiry_duration='1 month' -c wal_level=logical - environment: - POSTGRES_DB_FILE: /run/secrets/postgres_db - POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password - POSTGRES_USER_FILE: /run/secrets/postgres_user - image: postgis/postgis:18-3.6-alpine - ports: #DARGSTACK-REMOVE - - 5432:5432 #DARGSTACK-REMOVE - secrets: - - postgres_db - - postgres_password - - postgres_user - # sysctls: - # # Prevent Docker Swarm from killing connections (https://github.com/moby/moby/issues/31208) - # - net.ipv4.tcp_keepalive_time=600 - # - net.ipv4.tcp_keepalive_intvl=30 - # - net.ipv4.tcp_keepalive_probes=10 - volumes: - - postgres_data:/var/lib/postgresql/ - prometheus: - # You can access the metrics monitoring at [prometheus.app.localhost](https://prometheus.app.localhost/). - deploy: - labels: - - traefik.enable=true - - traefik.http.routers.prometheus.entryPoints=web - - traefik.http.routers.prometheus.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.prometheus.rule=Host(`prometheus.${STACK_DOMAIN}`) - - traefik.http.routers.prometheus_secure.entryPoints=web-secure - - traefik.http.routers.prometheus_secure.rule=Host(`prometheus.${STACK_DOMAIN}`) - - traefik.http.routers.prometheus_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.prometheus.loadbalancer.server.port=9090 - image: prom/prometheus:v3.10.0 - volumes: - - ../production/configurations/prometheus/prometheus.yaml:/etc/prometheus/prometheus.yml:ro - - prometheus_data:/prometheus - reccoom: - # You cannot access the recommendation service directly. - deploy: - labels: - - traefik.enable=true - - traefik.http.routers.reccoom.entryPoints=web - - traefik.http.routers.reccoom.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.reccoom.rule=Host(`reccoom.${STACK_DOMAIN}`) - - traefik.http.routers.reccoom_secure.entryPoints=web-secure - - traefik.http.routers.reccoom_secure.rule=Host(`reccoom.${STACK_DOMAIN}`) - - traefik.http.routers.reccoom_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.reccoom.loadbalancer.server.port=5245 - environment: - POSTGRES_HOST: postgres - RECCOOM_POSTGRES_HOST: reccoom_postgres - image: maevsi/reccoom:dev - secrets: - - postgres_db - - postgres_password - - postgres_user - - source: reccoom_ingest-api-key - target: /run/environment-variables/INGEST_API_KEY - - reccoom_openai-api-key - volumes: - - ../../../reccoom/:/srv/app/ #DARGSTACK-REMOVE - - ./configurations/postgraphile/jwtRS256.key.pub:/run/configurations/jwtRS256.key.pub:ro - reccoom_postgres: - # You can access reccoom's database via `adminer`. - environment: - POSTGRES_DB_FILE: /run/secrets/postgres_db - POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password - POSTGRES_USER_FILE: /run/secrets/postgres_user - image: pgvector/pgvector:0.8.2-pg18 - ports: #DARGSTACK-REMOVE - - 5433:5432 #DARGSTACK-REMOVE - secrets: - - postgres_db - - postgres_password - - postgres_user - volumes: - - reccoom_postgres_data:/var/lib/postgresql/ - redis: - # You cannot access the caching system via a web interface. - image: redis:8.6.1-alpine - volumes: - - redis_data:/data - redpanda: - # You can access the event streaming platform's ui as described under `redpanda-console`. - command: - - redpanda start - - --mode dev-container #DARGSTACK-REMOVE - - --kafka-addr internal://0.0.0.0:9092,external://0.0.0.0:19092 - - --advertise-kafka-addr internal://redpanda:9092,external://localhost:19092 - - --pandaproxy-addr internal://0.0.0.0:8082,external://0.0.0.0:18082 - - --advertise-pandaproxy-addr internal://redpanda:8082,external://localhost:18082 - - --schema-registry-addr internal://0.0.0.0:8081,external://0.0.0.0:18081 - # healthcheck: - # test: ["CMD-SHELL", "output=$(rpk cluster health --json); echo \"$output\" | grep -q '\"healthy\":true' || { echo \"$output\"; exit 1; }"] - # interval: 30s - # timeout: 10s - # retries: 3 - # start_period: 10s - image: redpandadata/redpanda:v25.3.10 - volumes: - - redpanda_data:/var/lib/redpanda/data - redpanda-console: - # You can access the event streaming platform's ui at [redpanda.app.localhost](https://redpanda.app.localhost/). - deploy: - labels: - - traefik.enable=true - - traefik.http.routers.redpanda.entryPoints=web - - traefik.http.routers.redpanda.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.redpanda.rule=Host(`redpanda.${STACK_DOMAIN}`) - - traefik.http.routers.redpanda_secure.entryPoints=web-secure - - traefik.http.routers.redpanda_secure.rule=Host(`redpanda.${STACK_DOMAIN}`) - - traefik.http.routers.redpanda_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.redpanda.loadbalancer.server.port=8080 - environment: - CONFIG_FILEPATH: /srv/app/redpanda-config.yaml - image: redpandadata/console:v3.5.3 - volumes: - - ../production/configurations/redpanda/config.yaml:/srv/app/redpanda-config.yaml:ro - sqitch: - # You cannot access the database migrations directly. - image: maevsi/sqitch:dev - secrets: - - postgres_role_service_grafana_password - - postgres_role_service_grafana_username - - postgres_role_service_postgraphile_password - - postgres_role_service_postgraphile_username - - postgres_role_service_vibetype_password - - postgres_role_service_vibetype_username - - postgres_role_service_zammad_username - - postgres_role_service_zammad_password - - sqitch_target - volumes: - - ../../../sqitch/:/srv/app/ - traefik: - # You can access the reverse proxy's dashboard at [traefik.app.localhost](https://traefik.app.localhost/). - command: - - --api=true - - --entryPoints.web.address=:80 - - --entryPoints.web-secure.address=:443 - - --entryPoints.web-secure.http.encodedCharacters.allowEncodedSlash=true #DARGSTACK-REMOVE # required for Nuxt's virtual imports - - --providers.swarm=true - - --providers.swarm.endpoint=unix:///var/run/docker.sock - - --providers.swarm.exposedByDefault=false - - --providers.file.filename=/dynamic.yml #DARGSTACK-REMOVE - - --providers.file.watch=true #DARGSTACK-REMOVE - - --log.level=DEBUG #DARGSTACK-REMOVE - deploy: - labels: - - traefik.enable=true - - traefik.http.middlewares.redirectscheme.redirectscheme.scheme=https #DARGSTACK-REMOVE - - traefik.http.routers.traefik.entryPoints=web - - traefik.http.routers.traefik.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.traefik.rule=Host(`traefik.${STACK_DOMAIN}`) - - traefik.http.routers.traefik.service=api@internal - - traefik.http.routers.traefik_secure.entryPoints=web-secure - - traefik.http.routers.traefik_secure.rule=Host(`traefik.${STACK_DOMAIN}`) - - traefik.http.routers.traefik_secure.service=api@internal - - traefik.http.routers.traefik_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.traefik.loadbalancer.server.port=8080 - mode: global - placement: - constraints: - - node.role == manager - image: traefik:v3.6.10 - ports: #DARGSTACK-REMOVE - - mode: host #DARGSTACK-REMOVE - protocol: tcp #DARGSTACK-REMOVE - published: 80 #DARGSTACK-REMOVE - target: 80 #DARGSTACK-REMOVE - - mode: host #DARGSTACK-REMOVE - protocol: tcp #DARGSTACK-REMOVE - published: 443 #DARGSTACK-REMOVE - target: 443 #DARGSTACK-REMOVE - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./certificates/:/etc/traefik/acme/ - - ./configurations/traefik/dynamic.yml:/dynamic.yml:ro #DARGSTACK-REMOVE - tusd: - # You can access the upload service at [tusd.app.localhost](https://tusd.app.localhost/). - command: -behind-proxy --hooks-enabled-events pre-create,pre-finish,pre-terminate --hooks-http http://vibetype:3000/api/internal/service/tusd -max-size ${TUSD_MAX_SIZE} -s3-bucket ${TUSD_BUCKET} -s3-endpoint ${TUSD_ENDPOINT} - deploy: - labels: - - traefik.enable=true - - traefik.http.middlewares.tusd_cors.headers.customresponseheaders.Cross-Origin-Resource-Policy=cross-origin - - traefik.http.routers.tusd.entryPoints=web - - traefik.http.routers.tusd.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.tusd.rule=Host(`tusd.${STACK_DOMAIN}`) - - traefik.http.routers.tusd_secure.entryPoints=web-secure - - traefik.http.routers.tusd_secure.middlewares=tusd_cors - - traefik.http.routers.tusd_secure.rule=Host(`tusd.${STACK_DOMAIN}`) && (Method(`GET`) || Method(`HEAD`) || Method(`OPTIONS`) || Method(`POST`) || Method(`PUT`) || Method(`PATCH`)) - - traefik.http.routers.tusd_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.tusd.loadbalancer.server.port=8080 - environment: - AWS_REGION: ${TUSD_REGION} - image: tusproject/tusd:v2.9.1 - secrets: - - source: tusd_aws - target: /home/tusd/.aws/credentials - vibetype: - # You can access the main project's frontend at [app.localhost](https://app.localhost/). - deploy: - labels: - - traefik.enable=true - - traefik.http.middlewares.vibetype_cors.headers.accessControlAllowHeaders=authorization,content-type,hook-name,x-turnstile-key - - traefik.http.middlewares.vibetype_cors.headers.accessControlAllowMethods=GET,POST,PUT,DELETE - - traefik.http.middlewares.vibetype_cors.headers.accessControlAllowOriginList=https://localhost:3000,https://app.localhost:3000 - - traefik.http.middlewares.vibetype_redirectregex.redirectregex.regex=^https?:\/\/www\.${STACK_DOMAIN}\/(.*) - - traefik.http.middlewares.vibetype_redirectregex.redirectregex.replacement=https://${STACK_DOMAIN}/$${2} - - traefik.http.routers.vibetype.entryPoints=web - - traefik.http.routers.vibetype.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.vibetype.rule=(Host(`${STACK_DOMAIN}`) || Host(`www.${STACK_DOMAIN}`)) && !PathPrefix(`/api/internal`) - - traefik.http.routers.vibetype_secure.entryPoints=web-secure - - traefik.http.routers.vibetype_secure.middlewares=vibetype_cors,vibetype_redirectregex - - traefik.http.routers.vibetype_secure.rule=(Host(`${STACK_DOMAIN}`) || Host(`www.${STACK_DOMAIN}`)) && !PathPrefix(`/api/internal`) - - traefik.http.routers.vibetype_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.vibetype.loadbalancer.server.port=3000 - environment: - AWS_REGION: ${VIBETYPE_AWS_REGION} - NUXT_PUBLIC_GTAG_ID: ${VIBETYPE_NUXT_PUBLIC_GTAG_ID} - NUXT_PUBLIC_I18N_BASE_URL: https://${STACK_DOMAIN} - NUXT_PUBLIC_SITE_URL: https://${STACK_DOMAIN} - NUXT_PUBLIC_TURNSTILE_SITE_KEY: ${VIBETYPE_NUXT_PUBLIC_TURNSTILE_SITE_KEY} - NUXT_PUBLIC_VIBETYPE_EMAIL_LIMIT24H: ${VIBETYPE_NUXT_PUBLIC_VIBETYPE_EMAIL_LIMIT24H} - PGHOST: postgres - image: maevsi/vibetype:dev - secrets: - - source: vibetype_api-notification-secret - target: /run/environment-variables/NUXT_PRIVATE_API_NOTIFICATION_SECRET - - source: vibetype_aws-credentials - target: /home/node/.aws/credentials - - source: vibetype_firebase-service-account-credentials - target: /run/environment-variables/FIREBASE_SERVICE_ACCOUNT_CREDENTIALS - - source: vibetype_monday - target: /run/environment-variables/NUXT_PRIVATE_MONDAY - - source: vibetype_openai-api-key - target: /run/environment-variables/NUXT_PRIVATE_OPENAI_API_KEY - - source: vibetype_turnstile-key - target: /run/environment-variables/NUXT_TURNSTILE_SECRET_KEY - - source: postgres_db - target: /run/environment-variables/PGDATABASE - - source: postgres_role_service_vibetype_password - target: /run/environment-variables/PGPASSWORD - - source: postgres_role_service_vibetype_username - target: /run/environment-variables/PGUSER - user: node:node # files created inside a docker container, like node_modules by pnpm, gain correct permissions by setting the user to `node` - volumes: - - pnpm_data:/srv/.pnpm-store/ #DARGSTACK-REMOVE - - ./certificates/:/srv/certificates/ #DARGSTACK-REMOVE - - ../../../vibetype/:/srv/app/ #DARGSTACK-REMOVE - - vibetype_data:/srv/app/node_modules #DARGSTACK-REMOVE - - ./configurations/postgraphile/jwtRS256.key.pub:/run/environment-variables/NUXT_PUBLIC_VIO_AUTH_JWT_PUBLIC_KEY:ro - zammad-backup: - # You cannot access the helpdesk backup service via a web interface. - <<: *zammad-service - command: ["zammad-backup"] - user: 0:0 - volumes: - - zammad-backup_data:/var/tmp/zammad - - zammad_data:/opt/zammad/storage:ro - - ../production/configurations/zammad/docker-entrypoint.sh:/docker-entrypoint.sh:ro - zammad-init: - # You cannot access the helpdesk initialization service via a web interface. - <<: *zammad-service - command: ["zammad-init"] - deploy: - restart_policy: - condition: on-failure - user: 0:0 - zammad-nginx: - # You can access the helpdesk at [zammad.app.localhost](https://zammad.app.localhost/). - <<: *zammad-service - command: ["zammad-nginx"] - deploy: - labels: - - traefik.enable=true - - traefik.http.routers.zammad.entryPoints=web - - traefik.http.routers.zammad.middlewares=redirectscheme #DARGSTACK-REMOVE - - traefik.http.routers.zammad.rule=Host(`zammad.${STACK_DOMAIN}`) - - traefik.http.routers.zammad_secure.entryPoints=web-secure - - traefik.http.routers.zammad_secure.rule=Host(`zammad.${STACK_DOMAIN}`) - - traefik.http.routers.zammad_secure.tls.options=mintls13@file #DARGSTACK-REMOVE - - traefik.http.services.zammad.loadbalancer.server.port=8080 - zammad-railsserver: - # You cannot access the helpdesk application server directly. - <<: *zammad-service - command: ["zammad-railsserver"] - zammad-scheduler: - # You cannot access the helpdesk scheduler directly. - <<: *zammad-service - command: ["zammad-scheduler"] - zammad-websocket: - # You cannot access the helpdesk websocket server directly. - <<: *zammad-service - command: ["zammad-websocket"] -version: "3.7" -volumes: - debezium_kafka_configuration: - # The change data capture's configuration. - {} - debezium_kafka_data: - # The change data capture's data. - {} - debezium_kafka_logs: - # The change data capture's logs. - {} - elasticsearch-configuration: - # The search engine's configuration. - {} - elasticsearch_data: - # The search engine's data. - {} - grafana_data: - # The observation dashboard's data. - {} - minio_data: - # The s3 server's data. - {} - pnpm_data: - # The node package manager's data. - {} - portainer_data: - # The container manager's data. - {} - postgraphile_data: - # The GraphQL API's data. - {} - postgres_data: - # The database's data. - {} - prometheus_data: - # The metrics monitoring's data. - {} - reccoom_postgres_data: - # The recommendation database's data. - {} - redis_data: - # The caching system's data. - {} - redpanda_data: - # The message queue's data. - {} - vibetype_data: - # The frontend's data. - {} - zammad-backup_data: - # The helpdesk backup's data. - {} - zammad_data: - # The helpdesk's data. - {} diff --git a/src/development/traefik/compose.yaml b/src/development/traefik/compose.yaml new file mode 100644 index 00000000..d367a5c0 --- /dev/null +++ b/src/development/traefik/compose.yaml @@ -0,0 +1,46 @@ +services: + traefik: + # You can access the reverse proxy's dashboard at [traefik.app.localhost](https://traefik.app.localhost/). + command: + - --api=true + - --entryPoints.web.address=:80 + - --entryPoints.web-secure.address=:443 + - --entryPoints.web-secure.http.encodedCharacters.allowEncodedSlash=true # dargstack:dev-only # required for Nuxt's virtual imports + - --providers.swarm=true + - --providers.swarm.endpoint=unix:///var/run/docker.sock + - --providers.swarm.exposedByDefault=false + - --providers.file.filename=/dynamic.yml # dargstack:dev-only + - --providers.file.watch=true # dargstack:dev-only + - --log.level=DEBUG # dargstack:dev-only + deploy: + labels: + - dargstack.profiles=default + - traefik.enable=true + - traefik.http.middlewares.redirectscheme.redirectscheme.scheme=https # dargstack:dev-only + - traefik.http.routers.traefik.entryPoints=web + - traefik.http.routers.traefik.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.traefik.rule=Host(`traefik.${STACK_DOMAIN}`) + - traefik.http.routers.traefik.service=api@internal + - traefik.http.routers.traefik-secure.entryPoints=web-secure + - traefik.http.routers.traefik-secure.rule=Host(`traefik.${STACK_DOMAIN}`) + - traefik.http.routers.traefik-secure.service=api@internal + - traefik.http.routers.traefik-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.traefik.loadbalancer.server.port=8080 + mode: global + placement: + constraints: + - node.role == manager + image: traefik:v3.6.12 + ports: # dargstack:dev-only + - mode: host # dargstack:dev-only + protocol: tcp # dargstack:dev-only + published: 80 # dargstack:dev-only + target: 80 # dargstack:dev-only + - mode: host # dargstack:dev-only + protocol: tcp # dargstack:dev-only + published: 443 # dargstack:dev-only + target: 443 # dargstack:dev-only + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ../../../artifacts/certificates/:/etc/traefik/acme/ + - ./configurations/dynamic.yml:/dynamic.yml:ro # dargstack:dev-only diff --git a/src/development/traefik/configurations/dynamic.yml b/src/development/traefik/configurations/dynamic.yml new file mode 100644 index 00000000..705f4828 --- /dev/null +++ b/src/development/traefik/configurations/dynamic.yml @@ -0,0 +1,7 @@ +tls: + certificates: + - certFile: /etc/traefik/acme/localhost.pem + keyFile: /etc/traefik/acme/localhost-key.pem + options: + mintls13: + minVersion: VersionTLS13 diff --git a/src/development/tusd/compose.yaml b/src/development/tusd/compose.yaml new file mode 100644 index 00000000..eda47461 --- /dev/null +++ b/src/development/tusd/compose.yaml @@ -0,0 +1,35 @@ +secrets: + tusd-aws: + # The upload service's s3 credentials file. + file: ../../../artifacts/secrets/tusd-aws.secret +services: + tusd: + # You can access the upload service at [tusd.app.localhost](https://tusd.app.localhost/). + command: -behind-proxy --hooks-enabled-events pre-create,pre-finish,pre-terminate --hooks-http http://vibetype:3000/api/internal/service/tusd -max-size ${TUSD_MAX_SIZE} -s3-bucket ${TUSD_BUCKET} -s3-endpoint ${TUSD_ENDPOINT} + deploy: + labels: + - dargstack.profiles=upload + - traefik.enable=true + - traefik.http.middlewares.tusd-cors.headers.customresponseheaders.Cross-Origin-Resource-Policy=cross-origin + - traefik.http.routers.tusd.entryPoints=web + - traefik.http.routers.tusd.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.tusd.rule=Host(`tusd.${STACK_DOMAIN}`) + - traefik.http.routers.tusd-secure.entryPoints=web-secure + - traefik.http.routers.tusd-secure.middlewares=tusd-cors + - traefik.http.routers.tusd-secure.rule=Host(`tusd.${STACK_DOMAIN}`) && (Method(`GET`) || Method(`HEAD`) || Method(`OPTIONS`) || Method(`POST`) || Method(`PUT`) || Method(`PATCH`)) + - traefik.http.routers.tusd-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.tusd.loadbalancer.server.port=8080 + environment: + AWS_REGION: ${TUSD_REGION} + image: tusproject/tusd:v2.9.2 + secrets: + - source: tusd-aws + target: /home/tusd/.aws/credentials +x-dargstack: + secrets: + tusd-aws: + insecure_default: | + [default] + aws_access_key_id = s3user + aws_secret_access_key = s3password + type: insecure_default diff --git a/src/development/secrets/tusd/aws.secret.template b/src/development/tusd/secrets/aws.secret.template similarity index 100% rename from src/development/secrets/tusd/aws.secret.template rename to src/development/tusd/secrets/aws.secret.template diff --git a/src/development/vibetype/compose.yaml b/src/development/vibetype/compose.yaml new file mode 100644 index 00000000..03649bf0 --- /dev/null +++ b/src/development/vibetype/compose.yaml @@ -0,0 +1,92 @@ +secrets: + vibetype-api-notification-secret: + # The notification endpoint's secret. + file: ../../../artifacts/secrets/vibetype-api-notification.secret + vibetype-aws-credentials: + # The cloud computing provider's user credentials. + file: ../../../artifacts/secrets/vibetype-aws-credentials.secret + vibetype-firebase-service-account-credentials: + # The notification provider's service account credentials. + file: ../../../artifacts/secrets/vibetype-firebase-service-account-credentials.secret + vibetype-monday: + # The project management software's configuration. + file: ../../../artifacts/secrets/vibetype-monday.secret + vibetype-openai-api-key: + # The AI provider's API key for the frontend. + file: ../../../artifacts/secrets/vibetype-openai-api-key.secret + vibetype-turnstile-key: + # The captcha provider's application key. + file: ../../../artifacts/secrets/vibetype-turnstile-key.secret +services: + vibetype: + # You can access the main project's frontend at [app.localhost](https://app.localhost/). + deploy: + labels: + - dargstack.development.build=../../../../vibetype + - dargstack.profiles=default + - traefik.enable=true + - traefik.http.middlewares.vibetype-cors.headers.accessControlAllowHeaders=authorization,content-type,hook-name,x-turnstile-key + - traefik.http.middlewares.vibetype-cors.headers.accessControlAllowMethods=GET,POST,PUT,DELETE + - traefik.http.middlewares.vibetype-cors.headers.accessControlAllowOriginList=https://localhost:3000,https://app.localhost:3000 + - traefik.http.middlewares.vibetype-redirectregex.redirectregex.regex=^https?:\/\/www\.${STACK_DOMAIN}\/(.*) + - traefik.http.middlewares.vibetype-redirectregex.redirectregex.replacement=https://${STACK_DOMAIN}/$${1} + - traefik.http.routers.vibetype.entryPoints=web + - traefik.http.routers.vibetype.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.vibetype.rule=(Host(`${STACK_DOMAIN}`) || Host(`www.${STACK_DOMAIN}`)) && !PathPrefix(`/api/internal`) + - traefik.http.routers.vibetype-secure.entryPoints=web-secure + - traefik.http.routers.vibetype-secure.middlewares=vibetype-cors,vibetype-redirectregex + - traefik.http.routers.vibetype-secure.rule=(Host(`${STACK_DOMAIN}`) || Host(`www.${STACK_DOMAIN}`)) && !PathPrefix(`/api/internal`) + - traefik.http.routers.vibetype-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.vibetype.loadbalancer.server.port=3000 + environment: + AWS_REGION: ${VIBETYPE_AWS_REGION} + NUXT_PUBLIC_GTAG_ID: ${VIBETYPE_NUXT_PUBLIC_GTAG_ID} + NUXT_PUBLIC_I18N_BASE_URL: https://${STACK_DOMAIN} + NUXT_PUBLIC_SITE_URL: https://${STACK_DOMAIN} + NUXT_PUBLIC_TURNSTILE_SITE_KEY: ${VIBETYPE_NUXT_PUBLIC_TURNSTILE_SITE_KEY} + NUXT_PUBLIC_VIBETYPE_EMAIL_LIMIT24H: ${VIBETYPE_NUXT_PUBLIC_VIBETYPE_EMAIL_LIMIT24H} + PGHOST: postgres + image: vibetype/vibetype:development + secrets: + - source: vibetype-api-notification-secret + target: /run/environment-variables/NUXT_PRIVATE_API_NOTIFICATION_SECRET + - source: vibetype-aws-credentials + target: /home/node/.aws/credentials + - source: vibetype-firebase-service-account-credentials + target: /run/environment-variables/FIREBASE_SERVICE_ACCOUNT_CREDENTIALS + - source: vibetype-monday + target: /run/environment-variables/NUXT_PRIVATE_MONDAY + - source: vibetype-openai-api-key + target: /run/environment-variables/NUXT_PRIVATE_OPENAI_API_KEY + - source: vibetype-turnstile-key + target: /run/environment-variables/NUXT_TURNSTILE_SECRET_KEY + - source: postgres-db + target: /run/environment-variables/PGDATABASE + - source: postgres-role-service-vibetype-password + target: /run/environment-variables/PGPASSWORD + - source: postgres-role-service-vibetype-username + target: /run/environment-variables/PGUSER + volumes: + - pnpm-data:/srv/.pnpm-store/ # dargstack:dev-only + - ../../../artifacts/certificates/:/srv/certificates/ # dargstack:dev-only + - ../../../../vibetype/:/srv/app/ # dargstack:dev-only + - vibetype-data:/srv/app/node_modules # dargstack:dev-only + - ../postgraphile/configurations/jwtES256.key.pub:/run/environment-variables/NUXT_PUBLIC_VIO_AUTH_JWT_PUBLIC_KEY:ro +volumes: + vibetype-data: + # The frontend's data. + {} +x-dargstack: + secrets: + vibetype-api-notification-secret: + type: random_string + vibetype-aws-credentials: + type: third_party + vibetype-firebase-service-account-credentials: + type: third_party + vibetype-monday: + type: third_party + vibetype-openai-api-key: + type: third_party + vibetype-turnstile-key: + type: third_party diff --git a/src/development/secrets/vibetype/api-notification.secret.template b/src/development/vibetype/secrets/api-notification.secret.template similarity index 100% rename from src/development/secrets/vibetype/api-notification.secret.template rename to src/development/vibetype/secrets/api-notification.secret.template diff --git a/src/development/secrets/vibetype/aws-credentials.secret.template b/src/development/vibetype/secrets/aws-credentials.secret.template similarity index 100% rename from src/development/secrets/vibetype/aws-credentials.secret.template rename to src/development/vibetype/secrets/aws-credentials.secret.template diff --git a/src/development/secrets/vibetype/firebase-service-account-credentials.secret.template b/src/development/vibetype/secrets/firebase-service-account-credentials.secret.template similarity index 100% rename from src/development/secrets/vibetype/firebase-service-account-credentials.secret.template rename to src/development/vibetype/secrets/firebase-service-account-credentials.secret.template diff --git a/src/development/secrets/vibetype/monday.template b/src/development/vibetype/secrets/monday.template similarity index 100% rename from src/development/secrets/vibetype/monday.template rename to src/development/vibetype/secrets/monday.template diff --git a/src/development/secrets/vibetype/openai-api-key.secret.template b/src/development/vibetype/secrets/openai-api-key.secret.template similarity index 100% rename from src/development/secrets/vibetype/openai-api-key.secret.template rename to src/development/vibetype/secrets/openai-api-key.secret.template diff --git a/src/development/secrets/vibetype/turnstile-key.secret.template b/src/development/vibetype/secrets/turnstile-key.secret.template similarity index 100% rename from src/development/secrets/vibetype/turnstile-key.secret.template rename to src/development/vibetype/secrets/turnstile-key.secret.template diff --git a/src/development/zammad/compose.yaml b/src/development/zammad/compose.yaml new file mode 100644 index 00000000..e3f1f6d7 --- /dev/null +++ b/src/development/zammad/compose.yaml @@ -0,0 +1,82 @@ +x-shared: + zammad-service: + &zammad-service # You can access the helpdesk at [zammad.app.localhost](https://zammad.app.localhost/). + deploy: + labels: + - dargstack.profiles=zammad + environment: &zammad-environment + ELASTICSEARCH_HOST: elasticsearch + ELASTICSEARCH_SCHEMA: https + ELASTICSEARCH_USER: elastic + MEMCACHE_SERVERS: memcached:11211 + NGINX_SERVER_SCHEME: https + POSTGRESQL_DB: zammad + POSTGRESQL_DB_CREATE: "false" + POSTGRESQL_HOST: postgres + POSTGRESQL_OPTIONS: ?pool=50 + REDIS_URL: redis://redis:6379 + image: ghcr.io/zammad/zammad:6.5.2-90 + secrets: + - source: elasticsearch-password + target: /run/environment-variables/ELASTICSEARCH_PASS + - source: postgres-role-service-zammad-username + target: /run/environment-variables/POSTGRESQL_USER + - source: postgres-role-service-zammad-password + target: /run/environment-variables/POSTGRESQL_PASS + volumes: + - zammad-data:/opt/zammad/storage + - ./configurations/docker-entrypoint.sh:/docker-entrypoint.sh:ro +services: + zammad-backup: + # You cannot access the helpdesk backup service via a web interface. + <<: *zammad-service + command: ["zammad-backup"] + user: 0:0 + volumes: + - zammad-backup-data:/var/tmp/zammad + - zammad-data:/opt/zammad/storage:ro + - ./configurations/docker-entrypoint.sh:/docker-entrypoint.sh:ro + zammad-init: + # You cannot access the helpdesk initialization service via a web interface. + <<: *zammad-service + command: ["zammad-init"] + deploy: + labels: + - dargstack.profiles=zammad + restart_policy: + condition: on-failure + user: 0:0 + zammad-nginx: + # You can access the helpdesk at [zammad.app.localhost](https://zammad.app.localhost/). + <<: *zammad-service + command: ["zammad-nginx"] + deploy: + labels: + - dargstack.profiles=zammad + - traefik.enable=true + - traefik.http.routers.zammad.entryPoints=web + - traefik.http.routers.zammad.middlewares=redirectscheme # dargstack:dev-only + - traefik.http.routers.zammad.rule=Host(`zammad.${STACK_DOMAIN}`) + - traefik.http.routers.zammad-secure.entryPoints=web-secure + - traefik.http.routers.zammad-secure.rule=Host(`zammad.${STACK_DOMAIN}`) + - traefik.http.routers.zammad-secure.tls.options=mintls13@file # dargstack:dev-only + - traefik.http.services.zammad.loadbalancer.server.port=8080 + zammad-railsserver: + # You cannot access the helpdesk application server directly. + <<: *zammad-service + command: ["zammad-railsserver"] + zammad-scheduler: + # You cannot access the helpdesk scheduler directly. + <<: *zammad-service + command: ["zammad-scheduler"] + zammad-websocket: + # You cannot access the helpdesk websocket server directly. + <<: *zammad-service + command: ["zammad-websocket"] +volumes: + zammad-backup-data: + # The helpdesk backup's data. + {} + zammad-data: + # The helpdesk's data. + {} diff --git a/src/production/configurations/zammad/docker-entrypoint.sh b/src/development/zammad/configurations/docker-entrypoint.sh similarity index 99% rename from src/production/configurations/zammad/docker-entrypoint.sh rename to src/development/zammad/configurations/docker-entrypoint.sh index 1459d9b7..9d364edd 100755 --- a/src/production/configurations/zammad/docker-entrypoint.sh +++ b/src/development/zammad/configurations/docker-entrypoint.sh @@ -7,7 +7,7 @@ ENVIRONMENT_VARIABLES_PATH="/run/environment-variables" is_valid_var_name() { case "$1" in - *[!a-zA-Z0-9_]*|'') return 1 ;; + ''|[!a-zA-Z_]*|*[!a-zA-Z0-9_]*) return 1 ;; *) return 0 ;; esac } @@ -16,7 +16,7 @@ load_env_file() { file="$1" name=$(basename "$file") is_valid_var_name "$name" || return 0 - value="$(cat "$file")" + value=$(cat "$file") export "$name=$value" } diff --git a/src/production/adminer/compose.yaml b/src/production/adminer/compose.yaml new file mode 100644 index 00000000..c471fc93 --- /dev/null +++ b/src/production/adminer/compose.yaml @@ -0,0 +1,8 @@ +services: + adminer: + deploy: + labels: + - (( append )) + - traefik.http.routers.adminer-secure.tls.certresolver=default + update_config: + order: start-first diff --git a/src/production/backups/postgres/README.md b/src/production/backups/postgres/README.md deleted file mode 100644 index a78acebf..00000000 --- a/src/production/backups/postgres/README.md +++ /dev/null @@ -1,3 +0,0 @@ - - -This directory contains backups created by [prodrigestivill/postgres-backup-local](https://github.com/prodrigestivill/docker-postgres-backup-local). diff --git a/src/production/cloudflared/compose.yaml b/src/production/cloudflared/compose.yaml new file mode 100644 index 00000000..478bc395 --- /dev/null +++ b/src/production/cloudflared/compose.yaml @@ -0,0 +1,12 @@ +services: + cloudflared: + # You can configure the secure tunnel at [dash.cloudflare.com](https://dash.cloudflare.com/). + command: tunnel run + deploy: + labels: + - dargstack.profiles=default + update_config: + order: start-first + environment: + TUNNEL_TOKEN: ${CLOUDFLARED_TUNNEL_TOKEN} + image: cloudflare/cloudflared diff --git a/src/production/compose.yaml b/src/production/compose.yaml new file mode 100644 index 00000000..adefef00 --- /dev/null +++ b/src/production/compose.yaml @@ -0,0 +1,2 @@ +volumes: + pnpm-data: (( prune )) diff --git a/src/production/configurations/adminer/adminer.css b/src/production/configurations/adminer/adminer.css deleted file mode 100755 index c77609cb..00000000 --- a/src/production/configurations/adminer/adminer.css +++ /dev/null @@ -1,7 +0,0 @@ -a { - color: blue; -} - -a:visited { - color: blue; -} diff --git a/src/production/configurations/grafana/provisioning/datasources/postgres.yaml b/src/production/configurations/grafana/provisioning/datasources/postgres.yaml deleted file mode 100644 index f39d522b..00000000 --- a/src/production/configurations/grafana/provisioning/datasources/postgres.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: 1 - -datasources: - - access: proxy - jsonData: - database: $__file{/run/secrets/postgres_db} - postgresVersion: 1500 - sslmode: 'disable' - name: PostgreSQL - secureJsonData: - password: $__file{/run/secrets/postgres_role_service_grafana_password} - type: grafana-postgresql-datasource - url: postgres:5432 - user: $__file{/run/secrets/postgres_role_service_grafana_username} diff --git a/src/production/debezium/compose.yaml b/src/production/debezium/compose.yaml new file mode 100644 index 00000000..484d03e5 --- /dev/null +++ b/src/production/debezium/compose.yaml @@ -0,0 +1,9 @@ +services: + debezium: + deploy: + update_config: + order: stop-first + debezium-postgres-connector: + deploy: + update_config: + order: stop-first diff --git a/src/production/elasticsearch/compose.yaml b/src/production/elasticsearch/compose.yaml new file mode 100644 index 00000000..b48924b3 --- /dev/null +++ b/src/production/elasticsearch/compose.yaml @@ -0,0 +1,5 @@ +services: + elasticsearch: + deploy: + update_config: + order: stop-first diff --git a/src/production/geoip/compose.yaml b/src/production/geoip/compose.yaml new file mode 100644 index 00000000..fc5fc9a9 --- /dev/null +++ b/src/production/geoip/compose.yaml @@ -0,0 +1,5 @@ +services: + geoip: + deploy: + update_config: + order: start-first diff --git a/src/production/grafana/compose.yaml b/src/production/grafana/compose.yaml new file mode 100644 index 00000000..b28ce288 --- /dev/null +++ b/src/production/grafana/compose.yaml @@ -0,0 +1,8 @@ +services: + grafana: + deploy: + labels: + - (( append )) + - traefik.http.routers.grafana-secure.tls.certresolver=default + update_config: + order: start-first diff --git a/src/production/jobber/compose.yaml b/src/production/jobber/compose.yaml new file mode 100644 index 00000000..063fbc2f --- /dev/null +++ b/src/production/jobber/compose.yaml @@ -0,0 +1,10 @@ +services: + jobber: + deploy: + update_config: + order: stop-first + environment: + SENTRY_CRONS: ${SENTRY_CRONS} + volumes: + - (( append )) + - postgres-backup-data:/backups/ diff --git a/src/production/configurations/jobber/.jobber b/src/production/jobber/configurations/.jobber similarity index 100% rename from src/production/configurations/jobber/.jobber rename to src/production/jobber/configurations/.jobber diff --git a/src/production/memcached/compose.yaml b/src/production/memcached/compose.yaml new file mode 100644 index 00000000..c448c776 --- /dev/null +++ b/src/production/memcached/compose.yaml @@ -0,0 +1,5 @@ +services: + memcached: + deploy: + update_config: + order: start-first diff --git a/src/production/minio/compose.yaml b/src/production/minio/compose.yaml new file mode 100644 index 00000000..afb70a8c --- /dev/null +++ b/src/production/minio/compose.yaml @@ -0,0 +1,4 @@ +# services: +# minio: (( prune )) # breaks renovate +volumes: + minio_data: (( prune )) diff --git a/src/production/portainer-agent/compose.yaml b/src/production/portainer-agent/compose.yaml new file mode 100644 index 00000000..601a15a3 --- /dev/null +++ b/src/production/portainer-agent/compose.yaml @@ -0,0 +1,5 @@ +services: + portainer-agent: + deploy: + update_config: + order: start-first diff --git a/src/production/portainer/compose.yaml b/src/production/portainer/compose.yaml new file mode 100644 index 00000000..d443dc3b --- /dev/null +++ b/src/production/portainer/compose.yaml @@ -0,0 +1,8 @@ +services: + portainer: + deploy: + labels: + - (( append )) + - traefik.http.routers.portainer-secure.tls.certresolver=default + update_config: + order: stop-first diff --git a/src/production/postgraphile/compose.yaml b/src/production/postgraphile/compose.yaml new file mode 100644 index 00000000..53bd2eff --- /dev/null +++ b/src/production/postgraphile/compose.yaml @@ -0,0 +1,10 @@ +services: + postgraphile: + deploy: + labels: + - (( append )) + - traefik.http.routers.postgraphile.middlewares=postgraphile-auth,postgraphile-cors + - traefik.http.routers.postgraphile-secure.tls.certresolver=default + update_config: + order: start-first + image: ghcr.io/maevsi/postgraphile:2.0.0-beta.1 diff --git a/src/production/configurations/postgraphile/jwtRS256.key.pub b/src/production/postgraphile/configurations/jwtRS256.key.pub similarity index 100% rename from src/production/configurations/postgraphile/jwtRS256.key.pub rename to src/production/postgraphile/configurations/jwtRS256.key.pub diff --git a/src/production/postgres/compose.yaml b/src/production/postgres/compose.yaml new file mode 100644 index 00000000..daeab7be --- /dev/null +++ b/src/production/postgres/compose.yaml @@ -0,0 +1,31 @@ +secrets: + postgres-backup-db: + # The database's name. + external: true +services: + postgres: + deploy: + update_config: + order: stop-first + postgres-backup: + # You cannot access the database backup directly. + deploy: + update_config: + order: stop-first + environment: + POSTGRES_DB_FILE: /run/secrets/postgres-backup-db + POSTGRES_HOST: postgres + POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password + POSTGRES_USER_FILE: /run/secrets/postgres-user + image: prodrigestivill/postgres-backup-local:18-alpine + secrets: + - postgres-backup-db + - postgres-password + - postgres-user + volumes: + - postgres-data:/var/lib/postgresql/ + - postgres-backup-data:/backups/ +volumes: + postgres-backup-data: + # The database backup's data. + {} diff --git a/src/production/production.env.template b/src/production/production.env.template deleted file mode 100644 index 8ffc032b..00000000 --- a/src/production/production.env.template +++ /dev/null @@ -1,5 +0,0 @@ -CLOUDFLARED_TUNNEL_TOKEN= -SENTRY_CRONS= -STACK_DOMAIN= -TRAEFIK_ACME_EMAIL= -TRAEFIK_ACME_PROVIDER= \ No newline at end of file diff --git a/src/production/production.yml b/src/production/production.yml deleted file mode 100644 index beca404e..00000000 --- a/src/production/production.yml +++ /dev/null @@ -1,177 +0,0 @@ -secrets: - postgres-backup_db: - # The database's name. - external: true - traefik_cf-dns-api-token: - # The DNS provider's DNS API token. - external: true - traefik_cf-zone-api-token: - # The DNS provider's zone API token. - external: true -services: - adminer: - deploy: - labels: - - (( append )) - - traefik.http.routers.adminer_secure.tls.certresolver=default - cloudflared: - # You can configure the secure tunnel at [dash.cloudflare.com](https://dash.cloudflare.com/). - command: tunnel run - environment: - TUNNEL_TOKEN: ${CLOUDFLARED_TUNNEL_TOKEN} - image: cloudflare/cloudflared - grafana: - deploy: - labels: - - (( append )) - - traefik.http.routers.grafana_secure.tls.certresolver=default - jobber: - environment: - SENTRY_CRONS: ${SENTRY_CRONS} - volumes: - - (( append )) - - ./configurations/jobber/sinks:/srv/sinks:ro - # minio: (( prune )) # breaks renovate - portainer: - deploy: - labels: - - (( append )) - - traefik.http.routers.portainer_secure.tls.certresolver=default - postgraphile: - deploy: - labels: - - (( append )) - - traefik.http.routers.postgraphile.middlewares=postgraphile_auth,postgraphile_cors - - traefik.http.routers.postgraphile_secure.tls.certresolver=default - image: maevsi/postgraphile:2.0.0-beta.1 - postgres_backup: - # You cannot access the database backup directly. - environment: - POSTGRES_DB_FILE: /run/secrets/postgres-backup_db - POSTGRES_HOST: postgres - POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password - POSTGRES_USER_FILE: /run/secrets/postgres_user - image: prodrigestivill/postgres-backup-local:18-alpine - secrets: - - postgres-backup_db - - postgres_password - - postgres_user - volumes: - - postgres_data:/var/lib/postgresql/ - - ../production/backups/postgres/:/backups/ - prometheus: - deploy: - labels: - - (( append )) - - traefik.http.routers.prometheus_secure.tls.certresolver=default - reccoom: - deploy: (( prune )) - image: ghcr.io/maevsi/reccoom:0.8.1 - redpanda-console: - deploy: - labels: - - (( append )) - - traefik.http.routers.redpanda_secure.tls.certresolver=default - sqitch: - image: ghcr.io/maevsi/sqitch:10.0.6 - volumes: (( prune )) - traefik: - command: - - (( prepend )) - - --certificatesResolvers.default.acme.email=${TRAEFIK_ACME_EMAIL} - - --certificatesResolvers.default.acme.storage=/etc/traefik/acme/acme.json - - --certificatesResolvers.default.acme.dnsChallenge.provider=${TRAEFIK_ACME_PROVIDER} - deploy: - labels: - - (( append )) - - traefik.http.routers.traefik_secure.tls.certresolver=default - environment: - CF_DNS_API_TOKEN_FILE: /run/secrets/traefik_cf-dns-api-token - CF_ZONE_API_TOKEN_FILE: /run/secrets/traefik_cf-zone-api-token - secrets: - - traefik_cf-dns-api-token - - traefik_cf-zone-api-token - traefik_certs-dumper: - # You cannot access the reverse proxy's certificate helper directly. - command: - - file - - --clean=false - - --crt-name="$STACK_DOMAIN" - - --dest=/etc/traefik/acme/ - - --key-name="$STACK_DOMAIN" - - --source=/etc/traefik/acme/acme.json - - --version=v2 - - --watch - environment: - STACK_DOMAIN: ${STACK_DOMAIN} - image: ldez/traefik-certs-dumper:v2.11.0 - volumes: - - acme_data:/etc/traefik/acme/ - tusd: - deploy: - labels: - - (( append )) - - traefik.http.routers.tusd.middlewares=tusd_cors - - traefik.http.routers.tusd_secure.tls.certresolver=default - vibetype: - deploy: - labels: - - (( append )) - - traefik.http.routers.vibetype.middlewares=vibetype_cors,vibetype_redirectregex - - traefik.http.routers.vibetype_secure.tls.certresolver=default - image: ghcr.io/maevsi/vibetype:13.3.3 - user: (( prune )) - # vibetype_beta: - # # You can access the main project frontend's beta version at [beta.app.localhost](https://beta.app.localhost/). - # deploy: - # labels: - # - traefik.enable=true - # - traefik.http.routers.vibetype_beta.entryPoints=web - # - traefik.http.routers.vibetype_beta.middlewares=vibetype_cors,vibetype_redirectregex - # - traefik.http.routers.vibetype_beta.rule=Host(`beta.${STACK_DOMAIN}`) - # - traefik.http.routers.vibetype_beta_secure.entryPoints=web-secure - # - traefik.http.routers.vibetype_beta_secure.middlewares=vibetype_cors,vibetype_redirectregex - # - traefik.http.routers.vibetype_beta_secure.rule=Host(`beta.${STACK_DOMAIN}`) - # - traefik.http.services.vibetype_beta.loadbalancer.server.port=3000 - # - traefik.http.routers.vibetype_beta_secure.tls.certresolver=default - # environment: - # AWS_REGION: ${VIBETYPE_AWS_REGION} - # NUXT_PUBLIC_GTAG_ID: ${VIBETYPE_NUXT_PUBLIC_GTAG_ID} - # NUXT_PUBLIC_I18N_BASE_URL: https://${STACK_DOMAIN} - # NUXT_PUBLIC_SITE_URL: https://${STACK_DOMAIN} - # NUXT_PUBLIC_TURNSTILE_SITE_KEY: ${VIBETYPE_NUXT_PUBLIC_TURNSTILE_SITE_KEY} - # NUXT_PUBLIC_VIBETYPE_EMAIL_LIMIT24H: ${VIBETYPE_NUXT_PUBLIC_VIBETYPE_EMAIL_LIMIT24H} - # NUXT_PUBLIC_VIO_ENVIRONMENT: beta - # PGHOST: postgres - # image: ghcr.io/maevsi/vibetype:11.0.2 - # secrets: - # - source: vibetype_api-notification-secret - # target: /run/environment-variables/NUXT_PRIVATE_API_NOTIFICATION_SECRET - # - source: vibetype_aws-credentials - # target: /home/node/.aws/credentials # TODO: switch to user `node` - # - source: vibetype_firebase-service-account-credentials - # target: /run/environment-variables/FIREBASE_SERVICE_ACCOUNT_CREDENTIALS - # - source: vibetype_openai-api-key - # target: /run/environment-variables/NUXT_PRIVATE_OPENAI_API_KEY - # - source: vibetype_turnstile-key - # target: /run/environment-variables/NUXT_TURNSTILE_SECRET_KEY - # - source: postgres_db - # target: /run/environment-variables/PGDATABASE - # - source: postgres_role_service_vibetype_password - # target: /run/environment-variables/PGPASSWORD - # - source: postgres_role_service_vibetype_username - # target: /run/environment-variables/PGUSER - # volumes: - # - ./configurations/postgraphile/jwtRS256.key.pub:/run/environment-variables/NUXT_PUBLIC_VIO_AUTH_JWT_PUBLIC_KEY:ro - zammad-nginx: - deploy: - labels: - - (( append )) - - traefik.http.routers.zammad_secure.tls.certresolver=default -version: "3.7" -volumes: - acme_data: - # The reverse proxy's certificate data. - {} - minio_data: (( prune )) - vibetype_data: (( prune )) diff --git a/src/production/prometheus/compose.yaml b/src/production/prometheus/compose.yaml new file mode 100644 index 00000000..502d2ee7 --- /dev/null +++ b/src/production/prometheus/compose.yaml @@ -0,0 +1,8 @@ +services: + prometheus: + deploy: + labels: + - (( append )) + - traefik.http.routers.prometheus-secure.tls.certresolver=default + update_config: + order: stop-first diff --git a/src/production/reccoom/compose.yaml b/src/production/reccoom/compose.yaml new file mode 100644 index 00000000..9d1f9efa --- /dev/null +++ b/src/production/reccoom/compose.yaml @@ -0,0 +1,7 @@ +services: + reccoom: + deploy: + labels: (( prune )) + update_config: + order: start-first + image: ghcr.io/maevsi/reccoom:0.9.0 diff --git a/src/production/reccoom_postgres/compose.yaml b/src/production/reccoom_postgres/compose.yaml new file mode 100644 index 00000000..1af6917d --- /dev/null +++ b/src/production/reccoom_postgres/compose.yaml @@ -0,0 +1,5 @@ +services: + reccoom_postgres: + deploy: + update_config: + order: stop-first diff --git a/src/production/redis/compose.yaml b/src/production/redis/compose.yaml new file mode 100644 index 00000000..2220d16c --- /dev/null +++ b/src/production/redis/compose.yaml @@ -0,0 +1,5 @@ +services: + redis: + deploy: + update_config: + order: stop-first diff --git a/src/production/redpanda/compose.yaml b/src/production/redpanda/compose.yaml new file mode 100644 index 00000000..8d384353 --- /dev/null +++ b/src/production/redpanda/compose.yaml @@ -0,0 +1,12 @@ +services: + redpanda: + deploy: + update_config: + order: stop-first + redpanda-console: + deploy: + labels: + - (( append )) + - traefik.http.routers.redpanda-secure.tls.certresolver=default + update_config: + order: start-first diff --git a/src/production/sqitch/compose.yaml b/src/production/sqitch/compose.yaml new file mode 100644 index 00000000..4a02f185 --- /dev/null +++ b/src/production/sqitch/compose.yaml @@ -0,0 +1,7 @@ +services: + sqitch: + deploy: + update_config: + order: stop-first + image: ghcr.io/maevsi/sqitch:11.0.0 + volumes: (( prune )) diff --git a/src/production/traefik/compose.yaml b/src/production/traefik/compose.yaml new file mode 100644 index 00000000..ba98faf7 --- /dev/null +++ b/src/production/traefik/compose.yaml @@ -0,0 +1,49 @@ +secrets: + traefik-cf-dns-api-token: + # The DNS provider's DNS API token. + external: true + traefik-cf-zone-api-token: + # The DNS provider's zone API token. + external: true +services: + traefik: + command: + - (( prepend )) + - --certificatesResolvers.default.acme.email=${TRAEFIK_ACME_EMAIL} + - --certificatesResolvers.default.acme.storage=/etc/traefik/acme/acme.json + - --certificatesResolvers.default.acme.dnsChallenge.provider=${TRAEFIK_ACME_PROVIDER} + deploy: + labels: + - (( append )) + - traefik.http.routers.traefik-secure.tls.certresolver=default + update_config: + order: start-first + environment: + CF_DNS_API_TOKEN_FILE: /run/secrets/traefik-cf-dns-api-token + CF_ZONE_API_TOKEN_FILE: /run/secrets/traefik-cf-zone-api-token + secrets: + - traefik-cf-dns-api-token + - traefik-cf-zone-api-token + traefik-certs-dumper: + # You cannot access the reverse proxy's certificate helper directly. + command: + - file + - --clean=false + - --crt-name="$STACK_DOMAIN" + - --dest=/etc/traefik/acme/ + - --key-name="$STACK_DOMAIN" + - --source=/etc/traefik/acme/acme.json + - --version=v2 + - --watch + deploy: + update_config: + order: stop-first + environment: + STACK_DOMAIN: ${STACK_DOMAIN} + image: ldez/traefik-certs-dumper:v2.11.0 + volumes: + - acme-data:/etc/traefik/acme/ +volumes: + acme-data: + # The reverse proxy's certificate data. + {} diff --git a/src/production/tusd/compose.yaml b/src/production/tusd/compose.yaml new file mode 100644 index 00000000..54e38bba --- /dev/null +++ b/src/production/tusd/compose.yaml @@ -0,0 +1,9 @@ +services: + tusd: + deploy: + labels: + - (( append )) + - traefik.http.routers.tusd.middlewares=tusd-cors + - traefik.http.routers.tusd-secure.tls.certresolver=default + update_config: + order: start-first diff --git a/src/production/vibetype/compose.yaml b/src/production/vibetype/compose.yaml new file mode 100644 index 00000000..1d6629c0 --- /dev/null +++ b/src/production/vibetype/compose.yaml @@ -0,0 +1,54 @@ +services: + vibetype: + deploy: + labels: + - (( append )) + - traefik.http.routers.vibetype.middlewares=vibetype_cors,vibetype_redirectregex + - traefik.http.routers.vibetype-secure.tls.certresolver=default + update_config: + order: start-first + image: ghcr.io/maevsi/vibetype:14.0.0 + # vibetype-beta: + # # You can access the main project frontend's beta version at [beta.app.localhost](https://beta.app.localhost/). + # deploy: + # labels: + # - traefik.enable=true + # - traefik.http.routers.vibetype-beta.entryPoints=web + # - traefik.http.routers.vibetype-beta.middlewares=vibetype_cors,vibetype_redirectregex + # - traefik.http.routers.vibetype-beta.rule=Host(`beta.${STACK_DOMAIN}`) + # - traefik.http.routers.vibetype-beta-secure.entryPoints=web-secure + # - traefik.http.routers.vibetype-beta-secure.middlewares=vibetype_cors,vibetype_redirectregex + # - traefik.http.routers.vibetype-beta-secure.rule=Host(`beta.${STACK_DOMAIN}`) + # - traefik.http.services.vibetype-beta.loadbalancer.server.port=3000 + # - traefik.http.routers.vibetype-beta-secure.tls.certresolver=default + # environment: + # AWS_REGION: ${VIBETYPE_AWS_REGION} + # NUXT_PUBLIC_GTAG_ID: ${VIBETYPE_NUXT_PUBLIC_GTAG_ID} + # NUXT_PUBLIC_I18N_BASE_URL: https://${STACK_DOMAIN} + # NUXT_PUBLIC_SITE_URL: https://${STACK_DOMAIN} + # NUXT_PUBLIC_TURNSTILE_SITE_KEY: ${VIBETYPE_NUXT_PUBLIC_TURNSTILE_SITE_KEY} + # NUXT_PUBLIC_VIBETYPE_EMAIL_LIMIT24H: ${VIBETYPE_NUXT_PUBLIC_VIBETYPE_EMAIL_LIMIT24H} + # NUXT_PUBLIC_VIO_ENVIRONMENT: beta + # PGHOST: postgres + # image: ghcr.io/maevsi/vibetype:11.0.2 + # secrets: + # - source: vibetype-api-notification-secret + # target: /run/environment-variables/NUXT_PRIVATE_API_NOTIFICATION_SECRET + # - source: vibetype-aws-credentials + # target: /home/node/.aws/credentials # TODO: switch to user `node` + # - source: vibetype-firebase-service-account-credentials + # target: /run/environment-variables/FIREBASE_SERVICE_ACCOUNT_CREDENTIALS + # - source: vibetype-openai-api-key + # target: /run/environment-variables/NUXT_PRIVATE_OPENAI_API_KEY + # - source: vibetype-turnstile-key + # target: /run/environment-variables/NUXT_TURNSTILE_SECRET_KEY + # - source: postgres-db + # target: /run/environment-variables/PGDATABASE + # - source: postgres-role-service-vibetype-password + # target: /run/environment-variables/PGPASSWORD + # - source: postgres-role-service-vibetype-username + # target: /run/environment-variables/PGUSER + # volumes: + # - ./configurations/postgraphile/jwtES256.key.pub:/run/environment-variables/NUXT_PUBLIC_VIO_AUTH_JWT_PUBLIC_KEY:ro +volumes: + vibetype_data: (( prune )) diff --git a/src/production/zammad/compose.yaml b/src/production/zammad/compose.yaml new file mode 100644 index 00000000..e610980c --- /dev/null +++ b/src/production/zammad/compose.yaml @@ -0,0 +1,28 @@ +services: + zammad-backup: + deploy: + update_config: + order: stop-first + zammad-init: + deploy: + update_config: + order: stop-first + zammad-nginx: + deploy: + labels: + - (( append )) + - traefik.http.routers.zammad-secure.tls.certresolver=default + update_config: + order: start-first + zammad-railsserver: + deploy: + update_config: + order: start-first + zammad-scheduler: + deploy: + update_config: + order: stop-first + zammad-websocket: + deploy: + update_config: + order: start-first