Merge branch 'canary' into i18n-kazakh

This commit is contained in:
Mauricio Siu 2024-12-07 13:13:30 -06:00
commit b3ca81d2e8
12 changed files with 1453 additions and 1095 deletions

View File

@ -1,56 +1,94 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
import dynamic from "next/dynamic";
import { useState } from "react";
const Terminal = dynamic(
() => import("./docker-terminal").then((e) => e.DockerTerminal),
{
ssr: false,
},
() => import("./docker-terminal").then((e) => e.DockerTerminal),
{
ssr: false,
}
);
interface Props {
containerId: string;
serverId?: string;
children?: React.ReactNode;
containerId: string;
serverId?: string;
children?: React.ReactNode;
}
export const DockerTerminalModal = ({
children,
containerId,
serverId,
children,
containerId,
serverId,
}: Props) => {
return (
<Dialog>
<DialogTrigger asChild>
<DropdownMenuItem
className="w-full cursor-pointer space-x-3"
onSelect={(e) => e.preventDefault()}
>
{children}
</DropdownMenuItem>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl">
<DialogHeader>
<DialogTitle>Docker Terminal</DialogTitle>
<DialogDescription>
Easy way to access to docker container
</DialogDescription>
</DialogHeader>
const [mainDialogOpen, setMainDialogOpen] = useState(false);
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
<Terminal
id="terminal"
containerId={containerId}
serverId={serverId || ""}
/>
</DialogContent>
</Dialog>
);
const handleMainDialogOpenChange = (open: boolean) => {
if (!open) {
setConfirmDialogOpen(true);
} else {
setMainDialogOpen(true);
}
};
const handleConfirm = () => {
setConfirmDialogOpen(false);
setMainDialogOpen(false);
};
const handleCancel = () => {
setConfirmDialogOpen(false);
};
return (
<Dialog open={mainDialogOpen} onOpenChange={handleMainDialogOpenChange}>
<DialogTrigger asChild>
<DropdownMenuItem
className="w-full cursor-pointer space-x-3"
onSelect={(e) => e.preventDefault()}
>
{children}
</DropdownMenuItem>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl">
<DialogHeader>
<DialogTitle>Docker Terminal</DialogTitle>
<DialogDescription>
Easy way to access to docker container
</DialogDescription>
</DialogHeader>
<Terminal
id="terminal"
containerId={containerId}
serverId={serverId || ""}
/>
<Dialog open={confirmDialogOpen} onOpenChange={setConfirmDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure you want to close the terminal?</DialogTitle>
<DialogDescription>
By clicking the confirm button, the terminal will be closed.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={handleCancel}>
Cancel
</Button>
<Button onClick={handleConfirm}>Confirm</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</DialogContent>
</Dialog>
);
};

View File

@ -38,7 +38,20 @@ const appearanceFormSchema = z.object({
required_error: "Please select a theme.",
}),
language: z.enum(
["en", "pl", "ru", "fr", "de", "tr", "kz", "zh-Hant", "zh-Hans", "fa", "ko"],
[
"en",
"pl",
"ru",
"fr",
"de",
"tr",
"zh-Hant",
"kz",
"zh-Hans",
"fa",
"ko",
"pt-br",
],
{
required_error: "Please select a language.",
},
@ -185,8 +198,10 @@ export function AppearanceForm() {
{ label: "简体中文", value: "zh-Hans" },
{ label: "Türkçe", value: "tr" },
{ label: "Қазақ", value: "tr" },
{ label: "Kazakh", value: "kz" },
{ label: "Persian", value: "fa" },
{ label: "한국어", value: "ko" },
{ label: "Português", value: "pt-br" },
].map((preset) => (
<SelectItem key={preset.label} value={preset.value}>
{preset.label}

View File

@ -1,20 +1,22 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { api } from "@/utils/api";
import { Loader2 } from "lucide-react";
@ -23,80 +25,118 @@ import type React from "react";
import { useEffect, useState } from "react";
const Terminal = dynamic(
() =>
import("@/components/dashboard/docker/terminal/docker-terminal").then(
(e) => e.DockerTerminal,
),
{
ssr: false,
},
() =>
import("@/components/dashboard/docker/terminal/docker-terminal").then(
(e) => e.DockerTerminal
),
{
ssr: false,
}
);
interface Props {
appName: string;
children?: React.ReactNode;
serverId?: string;
appName: string;
children?: React.ReactNode;
serverId?: string;
}
export const DockerTerminalModal = ({ children, appName, serverId }: Props) => {
const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery(
{
appName,
serverId,
},
{
enabled: !!appName,
},
);
const [containerId, setContainerId] = useState<string | undefined>();
const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery(
{
appName,
serverId,
},
{
enabled: !!appName,
}
);
const [containerId, setContainerId] = useState<string | undefined>();
const [mainDialogOpen, setMainDialogOpen] = useState(false);
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
useEffect(() => {
if (data && data?.length > 0) {
setContainerId(data[0]?.containerId);
}
}, [data]);
return (
<Dialog>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-7xl">
<DialogHeader>
<DialogTitle>Docker Terminal</DialogTitle>
<DialogDescription>
Easy way to access to docker container
</DialogDescription>
</DialogHeader>
<Label>Select a container to view logs</Label>
<Select onValueChange={setContainerId} value={containerId}>
<SelectTrigger>
{isLoading ? (
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground">
<span>Loading...</span>
<Loader2 className="animate-spin size-4" />
</div>
) : (
<SelectValue placeholder="Select a container" />
)}
</SelectTrigger>
<SelectContent>
<SelectGroup>
{data?.map((container) => (
<SelectItem
key={container.containerId}
value={container.containerId}
>
{container.name} ({container.containerId}) {container.state}
</SelectItem>
))}
<SelectLabel>Containers ({data?.length})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
<Terminal
serverId={serverId || ""}
id="terminal"
containerId={containerId || "select-a-container"}
/>
</DialogContent>
</Dialog>
);
const handleMainDialogOpenChange = (open: boolean) => {
if (!open) {
setConfirmDialogOpen(true);
} else {
setMainDialogOpen(true);
}
};
const handleConfirm = () => {
setConfirmDialogOpen(false);
setMainDialogOpen(false);
};
const handleCancel = () => {
setConfirmDialogOpen(false);
};
useEffect(() => {
if (data && data?.length > 0) {
setContainerId(data[0]?.containerId);
}
}, [data]);
return (
<Dialog open={mainDialogOpen} onOpenChange={handleMainDialogOpenChange}>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-7xl">
<DialogHeader>
<DialogTitle>Docker Terminal</DialogTitle>
<DialogDescription>
Easy way to access to docker container
</DialogDescription>
</DialogHeader>
<Label>Select a container to view logs</Label>
<Select onValueChange={setContainerId} value={containerId}>
<SelectTrigger>
{isLoading ? (
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground">
<span>Loading...</span>
<Loader2 className="animate-spin size-4" />
</div>
) : (
<SelectValue placeholder="Select a container" />
)}
</SelectTrigger>
<SelectContent>
<SelectGroup>
{data?.map((container) => (
<SelectItem
key={container.containerId}
value={container.containerId}
>
{container.name} ({container.containerId}) {container.state}
</SelectItem>
))}
<SelectLabel>Containers ({data?.length})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
<Terminal
serverId={serverId || ""}
id="terminal"
containerId={containerId || "select-a-container"}
/>
<Dialog open={confirmDialogOpen} onOpenChange={setConfirmDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>
Are you sure you want to close the terminal?
</DialogTitle>
<DialogDescription>
By clicking the confirm button, the terminal will be closed.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={handleCancel}>
Cancel
</Button>
<Button onClick={handleConfirm}>Confirm</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</DialogContent>
</Dialog>
);
};

View File

@ -1,5 +1,7 @@
/** @type {import('next-i18next').UserConfig} */
module.exports = {
fallbackLng: "en",
keySeparator: false,
i18n: {
defaultLocale: "en",
locales: [
@ -14,9 +16,8 @@ module.exports = {
"zh-Hans",
"fa",
"ko",
"pt-br",
],
localeDetection: false,
},
fallbackLng: "en",
keySeparator: false,
};

View File

@ -83,6 +83,7 @@ export default api.withTRPC(
"zh-Hans",
"fa",
"ko",
"pt-br",
],
localeDetection: false,
},

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,44 @@
{
"settings.common.save": "Salvar",
"settings.server.domain.title": "Domínio do Servidor",
"settings.server.domain.description": "Configure o domínio do servidor",
"settings.server.domain.form.domain": "Domínio",
"settings.server.domain.form.letsEncryptEmail": "Email do Let's Encrypt",
"settings.server.domain.form.certificate.label": "Certificado",
"settings.server.domain.form.certificate.placeholder": "Selecione um Certificado",
"settings.server.domain.form.certificateOptions.none": "Nenhum",
"settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (Padrão)",
"settings.server.webServer.title": "Servidor web",
"settings.server.webServer.description": "Limpar e recarregar servidor web.",
"settings.server.webServer.actions": "Ações",
"settings.server.webServer.reload": "Recarregar",
"settings.server.webServer.watchLogs": "Ver logs",
"settings.server.webServer.updateServerIp": "Atualizar IP do Servidor",
"settings.server.webServer.server.label": "Servidor",
"settings.server.webServer.traefik.label": "Traefik",
"settings.server.webServer.traefik.modifyEnv": "Alterar Env",
"settings.server.webServer.storage.label": "Armazenamento",
"settings.server.webServer.storage.cleanUnusedImages": "Limpar imagens não utilizadas",
"settings.server.webServer.storage.cleanUnusedVolumes": "Limpar volumes não utilizados",
"settings.server.webServer.storage.cleanStoppedContainers": "Limpar containers parados",
"settings.server.webServer.storage.cleanDockerBuilder": "Limpar Docker Builder & System",
"settings.server.webServer.storage.cleanMonitoring": "Limpar Monitoramento",
"settings.server.webServer.storage.cleanAll": "Limpar Tudo",
"settings.profile.title": "Conta",
"settings.profile.description": "Altere os detalhes do seu perfil aqui.",
"settings.profile.email": "Email",
"settings.profile.password": "Senha",
"settings.profile.avatar": "Avatar",
"settings.appearance.title": "Aparência",
"settings.appearance.description": "Personalize o tema do seu dashboard.",
"settings.appearance.theme": "Tema",
"settings.appearance.themeDescription": "Selecione um tema para o dashboard",
"settings.appearance.themes.light": "Claro",
"settings.appearance.themes.dark": "Escuro",
"settings.appearance.themes.system": "Automático",
"settings.appearance.language": "Linguagem",
"settings.appearance.languageDescription": "Selecione o idioma do dashboard"
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,107 @@
x-webapp-env: &webapp-env
LOGIN_ORIGIN: &trigger-url ${TRIGGER_PROTOCOL:-http}://${TRIGGER_DOMAIN:-localhost:3040}
APP_ORIGIN: *trigger-url
DEV_OTEL_EXPORTER_OTLP_ENDPOINT: &trigger-otel ${TRIGGER_PROTOCOL:-http}://${TRIGGER_DOMAIN:-localhost:3040}/otel
ELECTRIC_ORIGIN: http://electric:3000
x-worker-env: &worker-env
PLATFORM_HOST: webapp
PLATFORM_WS_PORT: 3030
SECURE_CONNECTION: "false"
OTEL_EXPORTER_OTLP_ENDPOINT: *trigger-otel
volumes:
postgres-data:
redis-data:
networks:
webapp:
services:
webapp:
image: ghcr.io/triggerdotdev/trigger.dev:${TRIGGER_IMAGE_TAG:-v3}
restart: ${RESTART_POLICY:-unless-stopped}
env_file:
- .env
environment:
<<: *webapp-env
ports:
- 3000
depends_on:
- postgres
- redis
networks:
- webapp
postgres:
image: postgres:${POSTGRES_IMAGE_TAG:-16}
restart: ${RESTART_POLICY:-unless-stopped}
volumes:
- postgres-data:/var/lib/postgresql/data/
env_file:
- .env
networks:
- webapp
ports:
- 5432
command:
- -c
- wal_level=logical
redis:
image: redis:${REDIS_IMAGE_TAG:-7}
restart: ${RESTART_POLICY:-unless-stopped}
volumes:
- redis-data:/data
networks:
- webapp
ports:
- 6379
docker-provider:
image: ghcr.io/triggerdotdev/provider/docker:${TRIGGER_IMAGE_TAG:-v3}
restart: ${RESTART_POLICY:-unless-stopped}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
user: root
networks:
- webapp
depends_on:
- webapp
ports:
- 9020
env_file:
- .env
environment:
<<: *worker-env
PLATFORM_SECRET: $PROVIDER_SECRET
coordinator:
image: ghcr.io/triggerdotdev/coordinator:${TRIGGER_IMAGE_TAG:-v3}
restart: ${RESTART_POLICY:-unless-stopped}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
user: root
networks:
- webapp
depends_on:
- webapp
ports:
- 9020
env_file:
- .env
environment:
<<: *worker-env
PLATFORM_SECRET: $COORDINATOR_SECRET
electric:
image: electricsql/electric:${ELECTRIC_IMAGE_TAG:-latest}
restart: ${RESTART_POLICY:-unless-stopped}
environment:
DATABASE_URL: ${DATABASE_URL}?sslmode=disable
networks:
- webapp
depends_on:
- postgres
ports:
- 3000

View File

@ -0,0 +1,93 @@
import { Secrets } from "@/components/ui/secrets";
import {
type DomainSchema,
type Schema,
type Template,
generateBase64,
generateRandomDomain,
} from "../utils";
export function generate(schema: Schema): Template {
const triggerDomain = generateRandomDomain(schema);
const magicLinkSecret = generateBase64(16);
const sessionSecret = generateBase64(16);
const encryptionKey = generateBase64(32);
const providerSecret = generateBase64(32);
const coordinatorSecret = generateBase64(32);
const dbPassword = generateBase64(24);
const dbUser = "triggeruser";
const dbName = "triggerdb";
const domains: DomainSchema[] = [
{
host: triggerDomain,
port: 3000,
serviceName: "webapp",
},
];
const envs = [
`NODE_ENV=production`,
`RUNTIME_PLATFORM=docker-compose`,
`V3_ENABLED=true`,
`# Domain configuration`,
`TRIGGER_DOMAIN=${triggerDomain}`,
`TRIGGER_PROTOCOL=http`,
`# Database configuration with secure credentials`,
`POSTGRES_USER=${dbUser}`,
`POSTGRES_PASSWORD=${dbPassword}`,
`POSTGRES_DB=${dbName}`,
`DATABASE_URL=postgresql://${dbUser}:${dbPassword}@postgres:5432/${dbName}`,
`# Secrets`,
`MAGIC_LINK_SECRET=${magicLinkSecret}`,
`SESSION_SECRET=${sessionSecret}`,
`ENCRYPTION_KEY=${encryptionKey}`,
`PROVIDER_SECRET=${providerSecret}`,
`COORDINATOR_SECRET=${coordinatorSecret}`,
`# TRIGGER_TELEMETRY_DISABLED=1`,
`INTERNAL_OTEL_TRACE_DISABLED=1`,
`INTERNAL_OTEL_TRACE_LOGGING_ENABLED=0`,
`DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT=300`,
`DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT=100`,
`DIRECT_URL=\${DATABASE_URL}`,
`REDIS_HOST=redis`,
`REDIS_PORT=6379`,
`REDIS_TLS_DISABLED=true`,
`# If this is set, emails that are not specified won't be able to log in`,
`# WHITELISTED_EMAILS="authorized@yahoo.com|authorized@gmail.com"`,
`# Accounts with these emails will become admins when signing up and get access to the admin panel`,
`# ADMIN_EMAILS="admin@example.com|another-admin@example.com"`,
`# If this is set, your users will be able to log in via GitHub`,
`# AUTH_GITHUB_CLIENT_ID=`,
`# AUTH_GITHUB_CLIENT_SECRET=`,
`# E-mail settings`,
`# Ensure the FROM_EMAIL matches what you setup with Resend.com`,
`# If these are not set, emails will be printed to the console`,
`# FROM_EMAIL=`,
`# REPLY_TO_EMAIL=`,
`# RESEND_API_KEY=`,
`# Worker settings`,
`HTTP_SERVER_PORT=9020`,
`COORDINATOR_HOST=127.0.0.1`,
`COORDINATOR_PORT=\${HTTP_SERVER_PORT}`,
`# REGISTRY_HOST=\${DEPLOY_REGISTRY_HOST}`,
`# REGISTRY_NAMESPACE=\${DEPLOY_REGISTRY_NAMESPACE}`,
];
return {
envs,
domains,
};
}

View File

@ -12,6 +12,7 @@ const SUPPORTED_LOCALES = [
"zh-Hans",
"fa",
"ko",
"pt-br",
] as const;
type Locale = (typeof SUPPORTED_LOCALES)[number];