diff --git a/.github/sponsors/synexa.png b/.github/sponsors/synexa.png new file mode 100644 index 000000000..737ccd576 Binary files /dev/null and b/.github/sponsors/synexa.png differ diff --git a/README.md b/README.md index 8d3755490..9246cf556 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,8 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com). Lightnode + + ### Premium Supporters 🥇 @@ -94,8 +96,10 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com). Startupfame Itsdb-center Openalternative +Synexa + ### Community Backers 🤝
diff --git a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx index ee674e0fe..48ec4557b 100644 --- a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx @@ -47,23 +47,12 @@ export function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) { } const htmlContent = fancyAnsi.toHtml(text); + const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi"); + const modifiedContent = htmlContent.replace( - /]*)>([^<]*)<\/span>/g, - (match, attrs, content) => { - const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi"); - if (!content.match(searchRegex)) return match; - - const segments = content.split(searchRegex); - const wrappedSegments = segments - .map((segment: string) => - segment.toLowerCase() === term.toLowerCase() - ? `${segment}` - : segment, - ) - .join(""); - - return `${wrappedSegments}`; - }, + searchRegex, + (match) => + `${match}`, ); return ( diff --git a/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx b/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx index 5f693707f..8cce306a5 100644 --- a/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx @@ -145,15 +145,6 @@ export const SecurityAudit = ({ serverId }: Props) => { : "Enabled (Password Authentication should be disabled)" } /> - + + + + + \ No newline at end of file diff --git a/apps/dokploy/public/templates/pocket-id.svg b/apps/dokploy/public/templates/pocket-id.svg new file mode 100644 index 000000000..0ee89b14b --- /dev/null +++ b/apps/dokploy/public/templates/pocket-id.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/dokploy/public/templates/wikijs.svg b/apps/dokploy/public/templates/wikijs.svg new file mode 100644 index 000000000..78073b234 --- /dev/null +++ b/apps/dokploy/public/templates/wikijs.svg @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/dokploy/templates/convex/index.ts b/apps/dokploy/templates/convex/index.ts index 6a112cdee..badfe7320 100644 --- a/apps/dokploy/templates/convex/index.ts +++ b/apps/dokploy/templates/convex/index.ts @@ -1,38 +1,38 @@ import { - type DomainSchema, - type Schema, - type Template, - generateRandomDomain, + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const dashboardDomain = generateRandomDomain(schema); - const backendDomain = generateRandomDomain(schema); - const actionsDomain = generateRandomDomain(schema); + const dashboardDomain = generateRandomDomain(schema); + const backendDomain = generateRandomDomain(schema); + const actionsDomain = generateRandomDomain(schema); - const domains: DomainSchema[] = [ - { - host: dashboardDomain, - port: 6791, - serviceName: "dashboard", - }, - { - host: backendDomain, - port: 3210, - serviceName: "backend", - }, - { - host: actionsDomain, - port: 3211, - serviceName: "backend", - }, - ]; + const domains: DomainSchema[] = [ + { + host: dashboardDomain, + port: 6791, + serviceName: "dashboard", + }, + { + host: backendDomain, + port: 3210, + serviceName: "backend", + }, + { + host: actionsDomain, + port: 3211, + serviceName: "backend", + }, + ]; - const envs = [ - `NEXT_PUBLIC_DEPLOYMENT_URL=http://${backendDomain}`, - `CONVEX_CLOUD_ORIGIN=http://${backendDomain}`, - `CONVEX_SITE_ORIGIN=http://${actionsDomain}`, - ]; + const envs = [ + `NEXT_PUBLIC_DEPLOYMENT_URL=http://${backendDomain}`, + `CONVEX_CLOUD_ORIGIN=http://${backendDomain}`, + `CONVEX_SITE_ORIGIN=http://${actionsDomain}`, + ]; - return { envs, domains }; + return { envs, domains }; } diff --git a/apps/dokploy/templates/glance/docker-compose.yml b/apps/dokploy/templates/glance/docker-compose.yml index e931d6e40..ace8bc940 100644 --- a/apps/dokploy/templates/glance/docker-compose.yml +++ b/apps/dokploy/templates/glance/docker-compose.yml @@ -2,7 +2,10 @@ services: glance: image: glanceapp/glance volumes: - - ../files/app/glance.yml:/app/glance.yml + - ../files/app/config/:/app/config + - ../files/app/assets:/app/assets + # Optionally, also mount docker socket if you want to use the docker containers widget + # - /var/run/docker.sock:/var/run/docker.sock:ro ports: - 8080 - restart: unless-stopped + env_file: .env \ No newline at end of file diff --git a/apps/dokploy/templates/glance/index.ts b/apps/dokploy/templates/glance/index.ts index 4b2297864..a0ab1b676 100644 --- a/apps/dokploy/templates/glance/index.ts +++ b/apps/dokploy/templates/glance/index.ts @@ -17,7 +17,7 @@ export function generate(schema: Schema): Template { const mounts: Template["mounts"] = [ { - filePath: "/app/glance.yml", + filePath: "/app/config/glance.yml", content: ` branding: hide-footer: true diff --git a/apps/dokploy/templates/linkwarden/docker-compose.yml b/apps/dokploy/templates/linkwarden/docker-compose.yml new file mode 100644 index 000000000..05ffb8a0a --- /dev/null +++ b/apps/dokploy/templates/linkwarden/docker-compose.yml @@ -0,0 +1,40 @@ +services: + linkwarden: + environment: + - NEXTAUTH_SECRET + - NEXTAUTH_URL + - DATABASE_URL=postgresql://linkwarden:${POSTGRES_PASSWORD}@postgres:5432/linkwarden + restart: unless-stopped + image: ghcr.io/linkwarden/linkwarden:v2.9.3 + ports: + - 3000 + volumes: + - linkwarden-data:/data/data + depends_on: + - postgres + healthcheck: + test: curl --fail http://localhost:3000 || exit 1 + interval: 60s + retries: 2 + start_period: 60s + timeout: 15s + + postgres: + image: postgres:17-alpine + restart: unless-stopped + user: postgres + environment: + POSTGRES_USER: linkwarden + POSTGRES_DB: linkwarden + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + linkwarden-data: + postgres-data: diff --git a/apps/dokploy/templates/linkwarden/index.ts b/apps/dokploy/templates/linkwarden/index.ts new file mode 100644 index 000000000..860250356 --- /dev/null +++ b/apps/dokploy/templates/linkwarden/index.ts @@ -0,0 +1,33 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const postgresPassword = generatePassword(); + const nextSecret = generateBase64(32); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "linkwarden", + }, + ]; + + const envs = [ + `POSTGRES_PASSWORD=${postgresPassword}`, + `NEXTAUTH_SECRET=${nextSecret}`, + `NEXTAUTH_URL=http://${mainDomain}/api/v1/auth`, + ]; + + return { + domains, + envs, + }; +} diff --git a/apps/dokploy/templates/mailpit/docker-compose.yml b/apps/dokploy/templates/mailpit/docker-compose.yml new file mode 100644 index 000000000..d0dbdb8ec --- /dev/null +++ b/apps/dokploy/templates/mailpit/docker-compose.yml @@ -0,0 +1,25 @@ +services: + mailpit: + image: axllent/mailpit:v1.22.3 + restart: unless-stopped + ports: + - '1025:1025' + volumes: + - 'mailpit-data:/data' + environment: + - MP_SMTP_AUTH_ALLOW_INSECURE=true + - MP_MAX_MESSAGES=5000 + - MP_DATABASE=/data/mailpit.db + - MP_UI_AUTH=${MP_UI_AUTH} + - MP_SMTP_AUTH=${MP_SMTP_AUTH} + healthcheck: + test: + - CMD + - /mailpit + - readyz + interval: 5s + timeout: 20s + retries: 10 + +volumes: + mailpit-data: \ No newline at end of file diff --git a/apps/dokploy/templates/mailpit/index.ts b/apps/dokploy/templates/mailpit/index.ts new file mode 100644 index 000000000..25f18f7e6 --- /dev/null +++ b/apps/dokploy/templates/mailpit/index.ts @@ -0,0 +1,31 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const domains: DomainSchema[] = [ + { + host: generateRandomDomain(schema), + port: 8025, + serviceName: "mailpit", + }, + ]; + + const defaultPassword = generatePassword(); + + const envs = [ + "# Uncomment below if you want basic auth on UI and SMTP", + `#MP_UI_AUTH=mailpit:${defaultPassword}`, + `#MP_SMTP_AUTH=mailpit:${defaultPassword}`, + ]; + + return { + domains, + envs, + }; +} diff --git a/apps/dokploy/templates/pocket-id/docker-compose.yml b/apps/dokploy/templates/pocket-id/docker-compose.yml new file mode 100644 index 000000000..f93851430 --- /dev/null +++ b/apps/dokploy/templates/pocket-id/docker-compose.yml @@ -0,0 +1,21 @@ +services: + pocket-id: + image: ghcr.io/pocket-id/pocket-id:v0.35.1 + restart: unless-stopped + environment: + - PUBLIC_UI_CONFIG_DISABLED + - PUBLIC_APP_URL + - TRUST_PROXY + ports: + - 80 + volumes: + - pocket-id-data:/app/backend/data + healthcheck: + test: "curl -f http://localhost/health" + interval: 1m30s + timeout: 5s + retries: 2 + start_period: 10s + +volumes: + pocket-id-data: diff --git a/apps/dokploy/templates/pocket-id/index.ts b/apps/dokploy/templates/pocket-id/index.ts new file mode 100644 index 000000000..9a9faa2a3 --- /dev/null +++ b/apps/dokploy/templates/pocket-id/index.ts @@ -0,0 +1,29 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 80, + serviceName: "pocket-id", + }, + ]; + + const envs = [ + "PUBLIC_UI_CONFIG_DISABLED=false", + `PUBLIC_APP_URL=http://${mainDomain}`, + "TRUST_PROXY=true", + ]; + + return { + domains, + envs, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 31668a6f9..d39465a8e 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -25,8 +25,8 @@ export const templates: TemplateData[] = [ "Outline is a self-hosted knowledge base and documentation platform that allows you to build and manage your own knowledge base applications.", links: { github: "https://github.com/outline/outline", - website: "https://outline.com/", - docs: "https://docs.outline.com/", + website: "https://getoutline.com/", + docs: "https://docs.getoutline.com/s/guide", }, logo: "outline.png", load: () => import("./outline/index").then((m) => m.generate), @@ -393,6 +393,21 @@ export const templates: TemplateData[] = [ tags: ["chat"], load: () => import("./open-webui/index").then((m) => m.generate), }, + { + id: "mailpit", + name: "Mailpit", + version: "v1.22.3", + description: + "Mailpit is a tiny, self-contained, and secure email & SMTP testing tool with API for developers.", + logo: "mailpit.svg", + links: { + github: "https://github.com/axllent/mailpit", + website: "https://mailpit.axllent.org/", + docs: "https://mailpit.axllent.org/docs/", + }, + tags: ["email", "smtp"], + load: () => import("./mailpit/index").then((m) => m.generate), + }, { id: "listmonk", name: "Listmonk", @@ -426,7 +441,7 @@ export const templates: TemplateData[] = [ { id: "umami", name: "Umami", - version: "v2.14.0", + version: "v2.16.1", description: "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", logo: "umami.png", @@ -662,6 +677,21 @@ export const templates: TemplateData[] = [ tags: ["open-source"], load: () => import("./vaultwarden/index").then((m) => m.generate), }, + { + id: "linkwarden", + name: "Linkwarden", + version: "2.9.3", + description: + "Self-hosted, open-source collaborative bookmark manager to collect, organize and archive webpages.", + logo: "linkwarden.png", + links: { + github: "https://github.com/linkwarden/linkwarden", + website: "https://linkwarden.app/", + docs: "https://docs.linkwarden.app/", + }, + tags: ["bookmarks", "link-sharing"], + load: () => import("./linkwarden/index").then((m) => m.generate), + }, { id: "hi-events", name: "Hi.events", @@ -1093,6 +1123,21 @@ export const templates: TemplateData[] = [ tags: ["identity", "auth"], load: () => import("./logto/index").then((m) => m.generate), }, + { + id: "pocket-id", + name: "Pocket ID", + version: "0.35.1", + description: + "A simple and easy-to-use OIDC provider that allows users to authenticate with their passkeys to your services.", + logo: "pocket-id.svg", + links: { + github: "https://github.com/pocket-id/pocket-id", + website: "https://pocket-id.org/", + docs: "https://pocket-id.org/docs", + }, + tags: ["identity", "auth"], + load: () => import("./pocket-id/index").then((m) => m.generate), + }, { id: "penpot", name: "Penpot", @@ -1559,4 +1604,18 @@ export const templates: TemplateData[] = [ tags: ["backend", "database", "api"], load: () => import("./convex/index").then((m) => m.generate), }, + { + id: "wikijs", + name: "Wiki.js", + version: "2.5", + description: "The most powerful and extensible open source Wiki software.", + logo: "wikijs.svg", + links: { + github: "https://github.com/requarks/wiki", + website: "https://js.wiki/", + docs: "https://docs.requarks.io/", + }, + tags: ["knowledge-base", "self-hosted", "documentation"], + load: () => import("./wikijs/index").then((m) => m.generate), + }, ]; diff --git a/apps/dokploy/templates/umami/docker-compose.yml b/apps/dokploy/templates/umami/docker-compose.yml index 875681658..26efd337c 100644 --- a/apps/dokploy/templates/umami/docker-compose.yml +++ b/apps/dokploy/templates/umami/docker-compose.yml @@ -1,6 +1,6 @@ services: umami: - image: ghcr.io/umami-software/umami:postgresql-v2.14.0 + image: ghcr.io/umami-software/umami:postgresql-v2.16.1 restart: always healthcheck: test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"] diff --git a/apps/dokploy/templates/wikijs/docker-compose.yml b/apps/dokploy/templates/wikijs/docker-compose.yml new file mode 100644 index 000000000..6b21423d1 --- /dev/null +++ b/apps/dokploy/templates/wikijs/docker-compose.yml @@ -0,0 +1,31 @@ +version: '3.5' +services: + wiki: + image: ghcr.io/requarks/wiki:2.5 + restart: unless-stopped + environment: + - DB_TYPE + - DB_HOST + - DB_PORT + - DB_USER + - DB_PASS + - DB_NAME + depends_on: + - db + labels: + - traefik.enable=true + - traefik.constraint-label-stack=wikijs + db: + image: postgres:14 + restart: unless-stopped + environment: + - POSTGRES_USER + - POSTGRES_PASSWORD + - POSTGRES_DB + volumes: + - wiki-db-data:/var/lib/postgresql/data +networks: + dokploy-network: + external: true +volumes: + wiki-db-data: diff --git a/apps/dokploy/templates/wikijs/index.ts b/apps/dokploy/templates/wikijs/index.ts new file mode 100644 index 000000000..ff6c234de --- /dev/null +++ b/apps/dokploy/templates/wikijs/index.ts @@ -0,0 +1,35 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const domains: DomainSchema[] = [ + { + host: generateRandomDomain(schema), + port: 3000, + serviceName: "wiki", + }, + ]; + + const envs = [ + "# Database Setup", + "POSTGRES_USER=wikijs", + "POSTGRES_PASSWORD=wikijsrocks", + "POSTGRES_DB=wiki", + "# WikiJS Database Connection", + "DB_TYPE=postgres", + "DB_HOST=db", + "DB_PORT=5432", + "DB_USER=wikijs", + "DB_PASS=wikijsrocks", + "DB_NAME=wiki", + ]; + + return { + domains, + envs, + }; +}