diff --git a/apps/dokploy/public/templates/twenty.svg b/apps/dokploy/public/templates/twenty.svg new file mode 100644 index 00000000..cf5223b9 --- /dev/null +++ b/apps/dokploy/public/templates/twenty.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index ce4c52e6..b46f1e92 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -868,6 +868,21 @@ export const templates: TemplateData[] = [ load: () => import("./chatwoot/index").then((m) => m.generate), }, { + id: "twenty", + name: "Twenty CRM", + version: "latest", + description: + "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", + logo: "twenty.svg", + links: { + github: "https://github.com/twentyhq/twenty", + website: "https://twenty.com", + docs: "https://docs.twenty.com", + }, + tags: ["crm", "sales", "business"], + load: () => import("./twenty/index").then((m) => m.generate), + }, + { id: "yourls", name: "YOURLS", version: "1.9.2", diff --git a/apps/dokploy/templates/twenty/docker-compose.yml b/apps/dokploy/templates/twenty/docker-compose.yml new file mode 100644 index 00000000..34c70aeb --- /dev/null +++ b/apps/dokploy/templates/twenty/docker-compose.yml @@ -0,0 +1,104 @@ +version: "3.9" + +services: + twenty-change-vol-ownership: + image: ubuntu + user: root + networks: + - dokploy-network + volumes: + - twenty-server-local-data:/tmp/server-local-data + - twenty-docker-data:/tmp/docker-data + command: > + bash -c " + chown -R 1000:1000 /tmp/server-local-data + && chown -R 1000:1000 /tmp/docker-data" + + twenty-server: + image: twentycrm/twenty:latest + networks: + - dokploy-network + volumes: + - twenty-server-local-data:/app/packages/twenty-server/${STORAGE_LOCAL_PATH:-.local-storage} + - twenty-docker-data:/app/docker-data + environment: + PORT: 3000 + PG_DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@twenty-postgres:5432/twenty + SERVER_URL: https://${TWENTY_HOST} + FRONT_BASE_URL: https://${TWENTY_HOST} + REDIS_URL: redis://twenty-redis:6379 + ENABLE_DB_MIGRATIONS: "true" + SIGN_IN_PREFILLED: "true" + STORAGE_TYPE: local + APP_SECRET: ${APP_SECRET} + depends_on: + twenty-change-vol-ownership: + condition: service_completed_successfully + twenty-postgres: + condition: service_healthy + healthcheck: + test: curl --fail http://localhost:3000/healthz + interval: 5s + timeout: 5s + retries: 10 + restart: always + + twenty-worker: + image: twentycrm/twenty:latest + networks: + - dokploy-network + command: ["yarn", "worker:prod"] + environment: + PG_DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@twenty-postgres:5432/twenty + SERVER_URL: https://${TWENTY_HOST} + FRONT_BASE_URL: https://${TWENTY_HOST} + REDIS_URL: redis://twenty-redis:6379 + ENABLE_DB_MIGRATIONS: "false" + STORAGE_TYPE: local + APP_SECRET: ${APP_SECRET} + depends_on: + twenty-postgres: + condition: service_healthy + twenty-server: + condition: service_healthy + restart: always + + twenty-postgres: + image: postgres:16-alpine + networks: + - dokploy-network + volumes: + - twenty-postgres-data:/var/lib/postgresql/data + environment: + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_DB: twenty + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d twenty"] + interval: 5s + timeout: 5s + retries: 10 + restart: always + + twenty-redis: + image: redis:latest + networks: + - dokploy-network + volumes: + - twenty-redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 10 + restart: always + +networks: + dokploy-network: + external: true + +volumes: + twenty-docker-data: + twenty-postgres-data: + twenty-server-local-data: + twenty-redis-data: \ No newline at end of file diff --git a/apps/dokploy/templates/twenty/index.ts b/apps/dokploy/templates/twenty/index.ts new file mode 100644 index 00000000..69bc5195 --- /dev/null +++ b/apps/dokploy/templates/twenty/index.ts @@ -0,0 +1,37 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const dbPassword = generatePassword(); + const dbUser = "twenty"; + const appSecret = generateBase64(32); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "twenty-server", + }, + ]; + + const envs = [ + `TWENTY_HOST=${mainDomain}`, + `DB_USER=${dbUser}`, + `DB_PASSWORD=${dbPassword}`, + `APP_SECRET=${appSecret}`, + "# Optional: Configure storage path", + "# STORAGE_LOCAL_PATH=.local-storage", + ]; + + return { + domains, + envs, + }; +}