mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-19 06:05:25 +02:00
Compare commits
10 Commits
feat/updat
...
v0.29.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a271c11e7 | ||
|
|
fda367b2c5 | ||
|
|
ea1238b1d1 | ||
|
|
b060f80932 | ||
|
|
04b9f56333 | ||
|
|
599b97da51 | ||
|
|
415298fddb | ||
|
|
ddff8b9de7 | ||
|
|
90f97912a4 | ||
|
|
9af745ce67 |
42
.github/workflows/sync-openapi-docs.yml
vendored
42
.github/workflows/sync-openapi-docs.yml
vendored
@@ -68,3 +68,45 @@ jobs:
|
||||
|
||||
echo "✅ OpenAPI synced to website successfully"
|
||||
|
||||
- name: Sync to MCP repository
|
||||
run: |
|
||||
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git mcp-repo
|
||||
|
||||
cd mcp-repo
|
||||
|
||||
cp -f ../openapi.json openapi.json
|
||||
|
||||
git config user.name "Dokploy Bot"
|
||||
git config user.email "bot@dokploy.com"
|
||||
|
||||
git add openapi.json
|
||||
git commit -m "chore: sync OpenAPI specification [skip ci]" \
|
||||
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
|
||||
-m "Updated: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" \
|
||||
--allow-empty
|
||||
|
||||
git push
|
||||
|
||||
echo "✅ OpenAPI synced to MCP repository successfully"
|
||||
|
||||
- name: Sync to CLI repository
|
||||
run: |
|
||||
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/cli.git cli-repo
|
||||
|
||||
cd cli-repo
|
||||
|
||||
cp -f ../openapi.json openapi.json
|
||||
|
||||
git config user.name "Dokploy Bot"
|
||||
git config user.email "bot@dokploy.com"
|
||||
|
||||
git add openapi.json
|
||||
git commit -m "chore: sync OpenAPI specification [skip ci]" \
|
||||
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
|
||||
-m "Updated: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" \
|
||||
--allow-empty
|
||||
|
||||
git push
|
||||
|
||||
echo "✅ OpenAPI synced to CLI repository successfully"
|
||||
|
||||
|
||||
79
.github/workflows/sync-version.yml
vendored
Normal file
79
.github/workflows/sync-version.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: Sync version to MCP and CLI repos
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
sync-version:
|
||||
name: Sync version to external repos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Dokploy repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=$(jq -r .version apps/dokploy/package.json)
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Version: $VERSION"
|
||||
|
||||
- name: Sync version to MCP repository
|
||||
run: |
|
||||
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/mcp.git mcp-repo
|
||||
cd mcp-repo
|
||||
|
||||
# Bump version
|
||||
jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp
|
||||
mv package.json.tmp package.json
|
||||
|
||||
# Regenerate tools from latest OpenAPI spec
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
pnpm run fetch-openapi
|
||||
pnpm run generate
|
||||
|
||||
git config user.name "Dokploy Bot"
|
||||
git config user.email "bot@dokploy.com"
|
||||
|
||||
git add -A
|
||||
git commit -m "chore: bump version to ${{ steps.get_version.outputs.version }}" \
|
||||
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
|
||||
-m "Release: ${{ github.event.release.html_url }}" \
|
||||
--allow-empty
|
||||
|
||||
git push
|
||||
|
||||
|
||||
- name: Sync version to CLI repository
|
||||
run: |
|
||||
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/cli.git cli-repo
|
||||
|
||||
cd cli-repo
|
||||
|
||||
# Bump version
|
||||
if [ -f package.json ]; then
|
||||
jq --arg v "${{ steps.get_version.outputs.version }}" '.version = $v' package.json > package.json.tmp
|
||||
mv package.json.tmp package.json
|
||||
fi
|
||||
|
||||
# Copy latest openapi spec and regenerate commands
|
||||
cp ../openapi.json ./openapi.json
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
pnpm run generate
|
||||
|
||||
git config user.name "Dokploy Bot"
|
||||
git config user.email "bot@dokploy.com"
|
||||
|
||||
git add -A
|
||||
git commit -m "chore: bump version to ${{ steps.get_version.outputs.version }}" \
|
||||
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
|
||||
-m "Release: ${{ github.event.release.html_url }}" \
|
||||
--allow-empty
|
||||
|
||||
git push
|
||||
|
||||
echo "CLI repo synced to version ${{ steps.get_version.outputs.version }}"
|
||||
|
||||
@@ -241,7 +241,7 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -329,7 +329,7 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
" w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
" w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -254,7 +254,7 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -349,7 +349,7 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
" w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
" w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -229,7 +229,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -316,7 +316,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
" w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
" w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -250,7 +250,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -347,7 +347,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
" w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
" w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -511,7 +511,7 @@ export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -181,7 +181,7 @@ export const RestoreVolumeBackups = ({ id, type, serverId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -263,7 +263,7 @@ export const RestoreVolumeBackups = ({ id, type, serverId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -36,6 +36,10 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { api } from "@/utils/api";
|
||||
import { ShowContainerConfig } from "@/components/dashboard/docker/config/show-container-config";
|
||||
import { ShowContainerMounts } from "@/components/dashboard/docker/mounts/show-container-mounts";
|
||||
import { ShowContainerNetworks } from "@/components/dashboard/docker/networks/show-container-networks";
|
||||
import { DockerTerminalModal } from "@/components/dashboard/docker/terminal/docker-terminal-modal";
|
||||
|
||||
const DockerLogsId = dynamic(
|
||||
() =>
|
||||
@@ -217,6 +221,24 @@ const ContainerRow = ({
|
||||
View Logs
|
||||
</DropdownMenuItem>
|
||||
</DialogTrigger>
|
||||
<ShowContainerConfig
|
||||
containerId={container.containerId}
|
||||
serverId={serverId || ""}
|
||||
/>
|
||||
<ShowContainerMounts
|
||||
containerId={container.containerId}
|
||||
serverId={serverId || ""}
|
||||
/>
|
||||
<ShowContainerNetworks
|
||||
containerId={container.containerId}
|
||||
serverId={serverId || ""}
|
||||
/>
|
||||
<DockerTerminalModal
|
||||
containerId={container.containerId}
|
||||
serverId={serverId || ""}
|
||||
>
|
||||
Terminal
|
||||
</DockerTerminalModal>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
|
||||
@@ -243,7 +243,7 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -331,7 +331,7 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
" w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
" w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -240,7 +240,7 @@ export const SaveGiteaProviderCompose = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -327,7 +327,7 @@ export const SaveGiteaProviderCompose = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -230,7 +230,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -317,7 +317,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
" w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
" w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -252,7 +252,7 @@ export const SaveGitlabProviderCompose = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -349,7 +349,7 @@ export const SaveGitlabProviderCompose = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
" w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
" w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -409,7 +409,7 @@ export const HandleBackup = ({
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -346,7 +346,7 @@ export const RestoreBackup = ({
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
@@ -428,7 +428,7 @@ export const RestoreBackup = ({
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { api } from "@/utils/api";
|
||||
|
||||
interface Props {
|
||||
containerId: string;
|
||||
serverId?: string;
|
||||
}
|
||||
|
||||
interface Mount {
|
||||
Type: string;
|
||||
Source: string;
|
||||
Destination: string;
|
||||
Mode: string;
|
||||
RW: boolean;
|
||||
Propagation: string;
|
||||
Name?: string;
|
||||
Driver?: string;
|
||||
}
|
||||
|
||||
export const ShowContainerMounts = ({ containerId, serverId }: Props) => {
|
||||
const { data } = api.docker.getConfig.useQuery(
|
||||
{
|
||||
containerId,
|
||||
serverId,
|
||||
},
|
||||
{
|
||||
enabled: !!containerId,
|
||||
},
|
||||
);
|
||||
|
||||
const mounts: Mount[] = data?.Mounts ?? [];
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<DropdownMenuItem
|
||||
className="w-full cursor-pointer"
|
||||
onSelect={(e) => e.preventDefault()}
|
||||
>
|
||||
View Mounts
|
||||
</DropdownMenuItem>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="w-full md:w-[70vw] min-w-[70vw]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Container Mounts</DialogTitle>
|
||||
<DialogDescription>
|
||||
Volume and bind mounts for this container
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="overflow-auto max-h-[70vh]">
|
||||
{mounts.length === 0 ? (
|
||||
<div className="text-center text-muted-foreground py-8">
|
||||
No mounts found for this container.
|
||||
</div>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Type</TableHead>
|
||||
<TableHead>Source</TableHead>
|
||||
<TableHead>Destination</TableHead>
|
||||
<TableHead>Mode</TableHead>
|
||||
<TableHead>Read/Write</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{mounts.map((mount, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>
|
||||
<Badge variant="outline">{mount.Type}</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="font-mono text-xs max-w-[250px] truncate">
|
||||
{mount.Name || mount.Source}
|
||||
</TableCell>
|
||||
<TableCell className="font-mono text-xs max-w-[250px] truncate">
|
||||
{mount.Destination}
|
||||
</TableCell>
|
||||
<TableCell className="text-xs">
|
||||
{mount.Mode || "-"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={mount.RW ? "default" : "secondary"}>
|
||||
{mount.RW ? "RW" : "RO"}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { api } from "@/utils/api";
|
||||
|
||||
interface Props {
|
||||
containerId: string;
|
||||
serverId?: string;
|
||||
}
|
||||
|
||||
interface Network {
|
||||
IPAMConfig: unknown;
|
||||
Links: unknown;
|
||||
Aliases: string[] | null;
|
||||
MacAddress: string;
|
||||
NetworkID: string;
|
||||
EndpointID: string;
|
||||
Gateway: string;
|
||||
IPAddress: string;
|
||||
IPPrefixLen: number;
|
||||
IPv6Gateway: string;
|
||||
GlobalIPv6Address: string;
|
||||
GlobalIPv6PrefixLen: number;
|
||||
DriverOpts: unknown;
|
||||
}
|
||||
|
||||
export const ShowContainerNetworks = ({ containerId, serverId }: Props) => {
|
||||
const { data } = api.docker.getConfig.useQuery(
|
||||
{
|
||||
containerId,
|
||||
serverId,
|
||||
},
|
||||
{
|
||||
enabled: !!containerId,
|
||||
},
|
||||
);
|
||||
|
||||
const networks: Record<string, Network> =
|
||||
data?.NetworkSettings?.Networks ?? {};
|
||||
const entries = Object.entries(networks);
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<DropdownMenuItem
|
||||
className="w-full cursor-pointer"
|
||||
onSelect={(e) => e.preventDefault()}
|
||||
>
|
||||
View Networks
|
||||
</DropdownMenuItem>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="w-full md:w-[70vw] min-w-[70vw]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Container Networks</DialogTitle>
|
||||
<DialogDescription>
|
||||
Networks attached to this container
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="overflow-auto max-h-[70vh]">
|
||||
{entries.length === 0 ? (
|
||||
<div className="text-center text-muted-foreground py-8">
|
||||
No networks found for this container.
|
||||
</div>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Network</TableHead>
|
||||
<TableHead>IP Address</TableHead>
|
||||
<TableHead>Gateway</TableHead>
|
||||
<TableHead>MAC Address</TableHead>
|
||||
<TableHead>Aliases</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{entries.map(([name, network]) => (
|
||||
<TableRow key={name}>
|
||||
<TableCell>
|
||||
<Badge variant="outline">{name}</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="font-mono text-xs">
|
||||
{network.IPAddress
|
||||
? `${network.IPAddress}/${network.IPPrefixLen}`
|
||||
: "-"}
|
||||
</TableCell>
|
||||
<TableCell className="font-mono text-xs">
|
||||
{network.Gateway || "-"}
|
||||
</TableCell>
|
||||
<TableCell className="font-mono text-xs">
|
||||
{network.MacAddress || "-"}
|
||||
</TableCell>
|
||||
<TableCell className="text-xs">
|
||||
{network.Aliases?.join(", ") || "-"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { ShowContainerConfig } from "../config/show-container-config";
|
||||
import { ShowDockerModalLogs } from "../logs/show-docker-modal-logs";
|
||||
import { ShowContainerMounts } from "../mounts/show-container-mounts";
|
||||
import { ShowContainerNetworks } from "../networks/show-container-networks";
|
||||
import { RemoveContainerDialog } from "../remove/remove-container";
|
||||
import { DockerTerminalModal } from "../terminal/docker-terminal-modal";
|
||||
import { UploadFileModal } from "../upload/upload-file-modal";
|
||||
@@ -123,6 +125,14 @@ export const columns: ColumnDef<Container>[] = [
|
||||
containerId={container.containerId}
|
||||
serverId={container.serverId || ""}
|
||||
/>
|
||||
<ShowContainerMounts
|
||||
containerId={container.containerId}
|
||||
serverId={container.serverId || ""}
|
||||
/>
|
||||
<ShowContainerNetworks
|
||||
containerId={container.containerId}
|
||||
serverId={container.serverId || ""}
|
||||
/>
|
||||
<DockerTerminalModal
|
||||
containerId={container.containerId}
|
||||
serverId={container.serverId || ""}
|
||||
|
||||
@@ -17,11 +17,11 @@ interface Props {
|
||||
const chartConfig = {
|
||||
readMb: {
|
||||
label: "Read (MB)",
|
||||
color: "oklch(var(--chart-1))",
|
||||
color: "hsl(var(--chart-1))",
|
||||
},
|
||||
writeMb: {
|
||||
label: "Write (MB)",
|
||||
color: "oklch(var(--chart-2))",
|
||||
color: "hsl(var(--chart-2))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ interface Props {
|
||||
const chartConfig = {
|
||||
usage: {
|
||||
label: "CPU Usage",
|
||||
color: "oklch(var(--chart-1))",
|
||||
color: "hsl(var(--chart-1))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ interface Props {
|
||||
const chartConfig = {
|
||||
usedGb: {
|
||||
label: "Used (GB)",
|
||||
color: "oklch(var(--chart-3))",
|
||||
color: "hsl(var(--chart-3))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
|
||||
@@ -25,19 +25,19 @@ const chartConfig = {
|
||||
},
|
||||
images: {
|
||||
label: "Images",
|
||||
color: "oklch(var(--chart-1))",
|
||||
color: "hsl(var(--chart-1))",
|
||||
},
|
||||
containers: {
|
||||
label: "Containers",
|
||||
color: "oklch(var(--chart-2))",
|
||||
color: "hsl(var(--chart-2))",
|
||||
},
|
||||
volumes: {
|
||||
label: "Volumes",
|
||||
color: "oklch(var(--chart-3))",
|
||||
color: "hsl(var(--chart-3))",
|
||||
},
|
||||
buildCache: {
|
||||
label: "Build Cache",
|
||||
color: "oklch(var(--chart-4))",
|
||||
color: "hsl(var(--chart-4))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -138,7 +138,7 @@ export const DockerDiskUsageChart = () => {
|
||||
innerRadius={60}
|
||||
outerRadius={85}
|
||||
strokeWidth={3}
|
||||
stroke="oklch(var(--background))"
|
||||
stroke="hsl(var(--background))"
|
||||
minAngle={15}
|
||||
>
|
||||
{chartData.map((entry) => (
|
||||
|
||||
@@ -19,7 +19,7 @@ interface Props {
|
||||
const chartConfig = {
|
||||
usage: {
|
||||
label: "Memory (GB)",
|
||||
color: "oklch(var(--chart-2))",
|
||||
color: "hsl(var(--chart-2))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ interface Props {
|
||||
const chartConfig = {
|
||||
inMB: {
|
||||
label: "In (MB)",
|
||||
color: "oklch(var(--chart-1))",
|
||||
color: "hsl(var(--chart-1))",
|
||||
},
|
||||
outMB: {
|
||||
label: "Out (MB)",
|
||||
color: "oklch(var(--chart-2))",
|
||||
color: "hsl(var(--chart-2))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ interface Props {
|
||||
const chartConfig = {
|
||||
cpu: {
|
||||
label: "CPU",
|
||||
color: "oklch(var(--chart-1))",
|
||||
color: "hsl(var(--chart-1))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -58,12 +58,12 @@ export const ContainerCPUChart = ({ data }: Props) => {
|
||||
<linearGradient id="fillCPU" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="5%"
|
||||
stopColor="oklch(var(--chart-1))"
|
||||
stopColor="hsl(var(--chart-1))"
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="95%"
|
||||
stopColor="oklch(var(--chart-1))"
|
||||
stopColor="hsl(var(--chart-1))"
|
||||
stopOpacity={0.1}
|
||||
/>
|
||||
</linearGradient>
|
||||
@@ -112,7 +112,7 @@ export const ContainerCPUChart = ({ data }: Props) => {
|
||||
dataKey="cpu"
|
||||
type="monotone"
|
||||
fill="url(#fillCPU)"
|
||||
stroke="oklch(var(--chart-1))"
|
||||
stroke="hsl(var(--chart-1))"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<ChartLegend
|
||||
|
||||
@@ -33,7 +33,7 @@ interface Props {
|
||||
const chartConfig = {
|
||||
memory: {
|
||||
label: "Memory",
|
||||
color: "oklch(var(--chart-2))",
|
||||
color: "hsl(var(--chart-2))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -73,12 +73,12 @@ export const ContainerMemoryChart = ({ data }: Props) => {
|
||||
<linearGradient id="fillMemory" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="5%"
|
||||
stopColor="oklch(var(--chart-2))"
|
||||
stopColor="hsl(var(--chart-2))"
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="95%"
|
||||
stopColor="oklch(var(--chart-2))"
|
||||
stopColor="hsl(var(--chart-2))"
|
||||
stopOpacity={0.1}
|
||||
/>
|
||||
</linearGradient>
|
||||
@@ -133,7 +133,7 @@ export const ContainerMemoryChart = ({ data }: Props) => {
|
||||
dataKey="memory"
|
||||
type="monotone"
|
||||
fill="url(#fillMemory)"
|
||||
stroke="oklch(var(--chart-2))"
|
||||
stroke="hsl(var(--chart-2))"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<ChartLegend
|
||||
|
||||
@@ -40,11 +40,11 @@ interface FormattedMetric {
|
||||
const chartConfig = {
|
||||
input: {
|
||||
label: "Input",
|
||||
color: "oklch(var(--chart-3))",
|
||||
color: "hsl(var(--chart-3))",
|
||||
},
|
||||
output: {
|
||||
label: "Output",
|
||||
color: "oklch(var(--chart-4))",
|
||||
color: "hsl(var(--chart-4))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -84,24 +84,24 @@ export const ContainerNetworkChart = ({ data }: Props) => {
|
||||
<linearGradient id="fillInput" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="5%"
|
||||
stopColor="oklch(var(--chart-3))"
|
||||
stopColor="hsl(var(--chart-3))"
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="95%"
|
||||
stopColor="oklch(var(--chart-3))"
|
||||
stopColor="hsl(var(--chart-3))"
|
||||
stopOpacity={0.1}
|
||||
/>
|
||||
</linearGradient>
|
||||
<linearGradient id="fillOutput" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="5%"
|
||||
stopColor="oklch(var(--chart-4))"
|
||||
stopColor="hsl(var(--chart-4))"
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="95%"
|
||||
stopColor="oklch(var(--chart-4))"
|
||||
stopColor="hsl(var(--chart-4))"
|
||||
stopOpacity={0.1}
|
||||
/>
|
||||
</linearGradient>
|
||||
@@ -162,7 +162,7 @@ export const ContainerNetworkChart = ({ data }: Props) => {
|
||||
dataKey="input"
|
||||
type="monotone"
|
||||
fill="url(#fillInput)"
|
||||
stroke="oklch(var(--chart-3))"
|
||||
stroke="hsl(var(--chart-3))"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<Area
|
||||
@@ -170,7 +170,7 @@ export const ContainerNetworkChart = ({ data }: Props) => {
|
||||
dataKey="output"
|
||||
type="monotone"
|
||||
fill="url(#fillOutput)"
|
||||
stroke="oklch(var(--chart-4))"
|
||||
stroke="hsl(var(--chart-4))"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<ChartLegend
|
||||
|
||||
@@ -22,7 +22,7 @@ interface CPUChartProps {
|
||||
const chartConfig = {
|
||||
cpu: {
|
||||
label: "CPU",
|
||||
color: "oklch(var(--chart-1))",
|
||||
color: "hsl(var(--chart-1))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -45,12 +45,12 @@ export function CPUChart({ data }: CPUChartProps) {
|
||||
<linearGradient id="fillCPU" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="5%"
|
||||
stopColor="oklch(var(--chart-1))"
|
||||
stopColor="hsl(var(--chart-1))"
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="95%"
|
||||
stopColor="oklch(var(--chart-1))"
|
||||
stopColor="hsl(var(--chart-1))"
|
||||
stopOpacity={0.1}
|
||||
/>
|
||||
</linearGradient>
|
||||
@@ -99,7 +99,7 @@ export function CPUChart({ data }: CPUChartProps) {
|
||||
dataKey="cpu"
|
||||
type="monotone"
|
||||
fill="url(#fillCPU)"
|
||||
stroke="oklch(var(--chart-1))"
|
||||
stroke="hsl(var(--chart-1))"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<ChartLegend
|
||||
|
||||
@@ -29,14 +29,14 @@ export function DiskChart({ data }: RadialChartProps) {
|
||||
const chartData = [
|
||||
{
|
||||
disk: 25,
|
||||
fill: "oklch(var(--chart-2))",
|
||||
fill: "hsl(var(--chart-2))",
|
||||
},
|
||||
];
|
||||
|
||||
const chartConfig = {
|
||||
disk: {
|
||||
label: "Disk",
|
||||
color: "oklch(var(--chart-2))",
|
||||
color: "hsl(var(--chart-2))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -71,7 +71,7 @@ export function DiskChart({ data }: RadialChartProps) {
|
||||
dataKey="disk"
|
||||
background
|
||||
cornerRadius={10}
|
||||
fill="oklch(var(--chart-2))"
|
||||
fill="hsl(var(--chart-2))"
|
||||
/>
|
||||
<PolarRadiusAxis tick={false} tickLine={false} axisLine={false}>
|
||||
<Label
|
||||
|
||||
@@ -20,7 +20,7 @@ interface MemoryChartProps {
|
||||
const chartConfig = {
|
||||
Memory: {
|
||||
label: "Memory",
|
||||
color: "oklch(var(--chart-2))",
|
||||
color: "hsl(var(--chart-2))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -46,12 +46,12 @@ export function MemoryChart({ data }: MemoryChartProps) {
|
||||
<linearGradient id="fillMemory" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="5%"
|
||||
stopColor="oklch(var(--chart-2))"
|
||||
stopColor="hsl(var(--chart-2))"
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="95%"
|
||||
stopColor="oklch(var(--chart-2))"
|
||||
stopColor="hsl(var(--chart-2))"
|
||||
stopOpacity={0.1}
|
||||
/>
|
||||
</linearGradient>
|
||||
@@ -116,7 +116,7 @@ export function MemoryChart({ data }: MemoryChartProps) {
|
||||
dataKey="memUsed"
|
||||
type="monotone"
|
||||
fill="url(#fillMemory)"
|
||||
stroke="oklch(var(--chart-2))"
|
||||
stroke="hsl(var(--chart-2))"
|
||||
strokeWidth={2}
|
||||
name="Memory"
|
||||
/>
|
||||
|
||||
@@ -22,11 +22,11 @@ interface NetworkChartProps {
|
||||
const chartConfig = {
|
||||
networkIn: {
|
||||
label: "Network In",
|
||||
color: "oklch(var(--chart-3))",
|
||||
color: "hsl(var(--chart-3))",
|
||||
},
|
||||
networkOut: {
|
||||
label: "Network Out",
|
||||
color: "oklch(var(--chart-4))",
|
||||
color: "hsl(var(--chart-4))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -52,24 +52,24 @@ export function NetworkChart({ data }: NetworkChartProps) {
|
||||
<linearGradient id="fillNetworkIn" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="5%"
|
||||
stopColor="oklch(var(--chart-3))"
|
||||
stopColor="hsl(var(--chart-3))"
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="95%"
|
||||
stopColor="oklch(var(--chart-3))"
|
||||
stopColor="hsl(var(--chart-3))"
|
||||
stopOpacity={0.1}
|
||||
/>
|
||||
</linearGradient>
|
||||
<linearGradient id="fillNetworkOut" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="5%"
|
||||
stopColor="oklch(var(--chart-4))"
|
||||
stopColor="hsl(var(--chart-4))"
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="95%"
|
||||
stopColor="oklch(var(--chart-4))"
|
||||
stopColor="hsl(var(--chart-4))"
|
||||
stopOpacity={0.1}
|
||||
/>
|
||||
</linearGradient>
|
||||
@@ -121,7 +121,7 @@ export function NetworkChart({ data }: NetworkChartProps) {
|
||||
dataKey="networkIn"
|
||||
type="monotone"
|
||||
fill="url(#fillNetworkIn)"
|
||||
stroke="oklch(var(--chart-3))"
|
||||
stroke="hsl(var(--chart-3))"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<Area
|
||||
@@ -129,7 +129,7 @@ export function NetworkChart({ data }: NetworkChartProps) {
|
||||
dataKey="networkOut"
|
||||
type="monotone"
|
||||
fill="url(#fillNetworkOut)"
|
||||
stroke="oklch(var(--chart-4))"
|
||||
stroke="hsl(var(--chart-4))"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<ChartLegend
|
||||
|
||||
@@ -236,7 +236,7 @@ export const AddTemplate = ({ environmentId, baseUrl }: Props) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full sm:w-[200px] justify-between !bg-transparent dark:!bg-input/30 !border !border-input !shadow-xs",
|
||||
"w-full sm:w-[200px] justify-between !bg-input",
|
||||
)}
|
||||
>
|
||||
{isLoadingTags
|
||||
|
||||
@@ -27,7 +27,7 @@ const chartConfig = {
|
||||
},
|
||||
count: {
|
||||
label: "Count",
|
||||
color: "oklch(var(--chart-1))",
|
||||
color: "hsl(var(--chart-1))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
@@ -101,9 +101,9 @@ export const RequestDistributionChart = ({
|
||||
<Area
|
||||
dataKey="count"
|
||||
type="monotone"
|
||||
fill="oklch(var(--chart-1))"
|
||||
fill="hsl(var(--chart-1))"
|
||||
fillOpacity={0.4}
|
||||
stroke="oklch(var(--chart-1))"
|
||||
stroke="hsl(var(--chart-1))"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
|
||||
@@ -60,99 +60,99 @@ const DEFAULT_CSS_TEMPLATE = `/* ============================================
|
||||
|
||||
/* ---------- Light Mode ---------- */
|
||||
:root {
|
||||
--background: 1 0 0;
|
||||
--foreground: 0.145 0 0;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
|
||||
--card: 1 0 0;
|
||||
--card-foreground: 0.145 0 0;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
|
||||
--popover: 1 0 0;
|
||||
--popover-foreground: 0.145 0 0;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
|
||||
--primary: 0.205 0 0;
|
||||
--primary-foreground: 0.985 0 0;
|
||||
--primary: 240 5.9% 10%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
|
||||
--secondary: 0.97 0 0;
|
||||
--secondary-foreground: 0.205 0 0;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
|
||||
--muted: 0.97 0 0;
|
||||
--muted-foreground: 0.556 0 0;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
|
||||
--accent: 0.97 0 0;
|
||||
--accent-foreground: 0.205 0 0;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
|
||||
--destructive: 0.577 0.245 27.325;
|
||||
--destructive-foreground: 0.985 0 0;
|
||||
--destructive: 0 84.2% 50.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--border: 0.922 0 0;
|
||||
--input: 0.922 0 0;
|
||||
--ring: 0.708 0 0;
|
||||
--radius: 0.625rem;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 240 10% 3.9%;
|
||||
--radius: 0.5rem;
|
||||
|
||||
/* Sidebar */
|
||||
--sidebar: 0.985 0 0;
|
||||
--sidebar-foreground: 0.145 0 0;
|
||||
--sidebar-primary: 0.205 0 0;
|
||||
--sidebar-primary-foreground: 0.985 0 0;
|
||||
--sidebar-accent: 0.97 0 0;
|
||||
--sidebar-accent-foreground: 0.205 0 0;
|
||||
--sidebar-border: 0.922 0 0;
|
||||
--sidebar-ring: 0.708 0 0;
|
||||
--sidebar-background: 0 0% 98%;
|
||||
--sidebar-foreground: 240 5.3% 26.1%;
|
||||
--sidebar-primary: 240 5.9% 10%;
|
||||
--sidebar-primary-foreground: 0 0% 98%;
|
||||
--sidebar-accent: 240 4.8% 95.9%;
|
||||
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||
--sidebar-border: 220 13% 91%;
|
||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
|
||||
/* Charts */
|
||||
--chart-1: 0.646 0.222 41.116;
|
||||
--chart-2: 0.6 0.118 184.704;
|
||||
--chart-3: 0.398 0.07 227.392;
|
||||
--chart-4: 0.828 0.189 84.429;
|
||||
--chart-5: 0.769 0.188 70.08;
|
||||
--chart-1: 173 58% 39%;
|
||||
--chart-2: 12 76% 61%;
|
||||
--chart-3: 197 37% 24%;
|
||||
--chart-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
}
|
||||
|
||||
/* ---------- Dark Mode ---------- */
|
||||
.dark {
|
||||
--background: 0.145 0 0;
|
||||
--foreground: 0.985 0 0;
|
||||
--background: 0 0% 0%;
|
||||
--foreground: 0 0% 98%;
|
||||
|
||||
--card: 0.205 0 0;
|
||||
--card-foreground: 0.985 0 0;
|
||||
--card: 240 4% 10%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
|
||||
--popover: 0.205 0 0;
|
||||
--popover-foreground: 0.985 0 0;
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
|
||||
--primary: 0.922 0 0;
|
||||
--primary-foreground: 0.205 0 0;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 240 5.9% 10%;
|
||||
|
||||
--secondary: 0.269 0 0;
|
||||
--secondary-foreground: 0.985 0 0;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
|
||||
--muted: 0.269 0 0;
|
||||
--muted-foreground: 0.708 0 0;
|
||||
--muted: 240 4% 10%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
|
||||
--accent: 0.269 0 0;
|
||||
--accent-foreground: 0.985 0 0;
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
|
||||
--destructive: 0.704 0.191 22.216;
|
||||
--destructive-foreground: 0.985 0 0;
|
||||
--destructive: 0 84.2% 50.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--border: 0.371 0 0;
|
||||
--input: 0.371 0 0;
|
||||
--ring: 0.556 0 0;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 4% 10%;
|
||||
--ring: 240 4.9% 83.9%;
|
||||
|
||||
/* Sidebar */
|
||||
--sidebar: 0.205 0 0;
|
||||
--sidebar-foreground: 0.985 0 0;
|
||||
--sidebar-primary: 0.488 0.243 264.376;
|
||||
--sidebar-primary-foreground: 0.985 0 0;
|
||||
--sidebar-accent: 0.269 0 0;
|
||||
--sidebar-accent-foreground: 0.985 0 0;
|
||||
--sidebar-border: 0.371 0 0;
|
||||
--sidebar-ring: 0.556 0 0;
|
||||
--sidebar-background: 240 5.9% 10%;
|
||||
--sidebar-foreground: 240 4.8% 95.9%;
|
||||
--sidebar-primary: 224.3 76.3% 48%;
|
||||
--sidebar-primary-foreground: 0 0% 100%;
|
||||
--sidebar-accent: 240 3.7% 15.9%;
|
||||
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
||||
--sidebar-border: 240 3.7% 15.9%;
|
||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
|
||||
/* Charts */
|
||||
--chart-1: 0.488 0.243 264.376;
|
||||
--chart-2: 0.696 0.17 162.48;
|
||||
--chart-3: 0.769 0.188 70.08;
|
||||
--chart-4: 0.627 0.265 303.9;
|
||||
--chart-5: 0.645 0.246 16.439;
|
||||
--chart-1: 220 70% 50%;
|
||||
--chart-2: 340 75% 55%;
|
||||
--chart-3: 30 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 160 60% 45%;
|
||||
}
|
||||
|
||||
/* ---------- Custom Styles ---------- */
|
||||
|
||||
@@ -116,6 +116,14 @@ export function TagSelector({
|
||||
<HandleTag />
|
||||
</div>
|
||||
</CommandEmpty>
|
||||
{tags.length === 0 && (
|
||||
<div className="flex flex-col items-center gap-2 py-4">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
No tags created yet.
|
||||
</span>
|
||||
<HandleTag />
|
||||
</div>
|
||||
)}
|
||||
<CommandGroup>
|
||||
{tags.map((tag) => {
|
||||
const isSelected = selectedTags.includes(tag.id);
|
||||
|
||||
@@ -13,7 +13,7 @@ const Command = React.forwardRef<
|
||||
<CommandPrimitive
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
||||
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground p-px",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -39,12 +39,12 @@ const CommandInput = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Input>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className="flex h-9 items-center gap-2 border-b px-3" cmdk-input-wrapper="">
|
||||
<Search className="size-4 shrink-0 opacity-50" />
|
||||
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
|
||||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<CommandPrimitive.Input
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -115,7 +115,7 @@ const CommandItem = React.forwardRef<
|
||||
<CommandPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -76,7 +76,8 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
<input
|
||||
type={inputType}
|
||||
className={cn(
|
||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:aria-invalid:ring-destructive/40 md:text-sm",
|
||||
// bg-gray
|
||||
"flex h-10 w-full rounded-md bg-input px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border disabled:cursor-not-allowed disabled:opacity-50",
|
||||
isPassword && (shouldShowGenerator ? "pr-16" : "pr-10"),
|
||||
className,
|
||||
)}
|
||||
|
||||
@@ -17,7 +17,7 @@ const SelectTrigger = React.forwardRef<
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30 [&>span]:line-clamp-1",
|
||||
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-input px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -528,7 +528,7 @@ const sidebarMenuButtonVariants = cva(
|
||||
variant: {
|
||||
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
||||
outline:
|
||||
"bg-background shadow-[0_0_0_1px_oklch(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_oklch(var(--sidebar-accent))]",
|
||||
"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
|
||||
},
|
||||
size: {
|
||||
default: "h-8 text-sm",
|
||||
|
||||
@@ -11,7 +11,7 @@ const Switch = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SwitchPrimitives.Root
|
||||
className={cn(
|
||||
"peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80",
|
||||
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-muted-foreground/80",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -19,7 +19,7 @@ const Switch = React.forwardRef<
|
||||
>
|
||||
<SwitchPrimitives.Thumb
|
||||
className={cn(
|
||||
"pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground",
|
||||
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitives.Root>
|
||||
|
||||
@@ -48,7 +48,7 @@ const MyApp = ({
|
||||
disableTransitionOnChange
|
||||
forcedTheme={Component.theme}
|
||||
>
|
||||
<NextTopLoader color="oklch(var(--sidebar-ring))" />
|
||||
<NextTopLoader color="hsl(var(--sidebar-ring))" />
|
||||
<WhitelabelingProvider />
|
||||
<Toaster richColors />
|
||||
<SearchCommand />
|
||||
|
||||
@@ -82,6 +82,16 @@ export default function Home({ IS_CLOUD }: Props) {
|
||||
});
|
||||
|
||||
if (error) {
|
||||
const isEmailNotVerified =
|
||||
error.code === "EMAIL_NOT_VERIFIED" ||
|
||||
error.message?.toLowerCase().includes("email not verified");
|
||||
if (isEmailNotVerified) {
|
||||
const msg =
|
||||
"Your email is not verified. We've sent a new verification link to your email.";
|
||||
toast.info(msg);
|
||||
setError(msg);
|
||||
return;
|
||||
}
|
||||
toast.error(error.message);
|
||||
setError(error.message || "An error occurred while logging in");
|
||||
return;
|
||||
|
||||
@@ -5,97 +5,97 @@
|
||||
@layer base {
|
||||
:root {
|
||||
--terminal-paste: rgba(0, 0, 0, 0.2);
|
||||
--background: 1 0 0;
|
||||
--foreground: 0.145 0 0;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
|
||||
--card: 1 0 0;
|
||||
--card-foreground: 0.145 0 0;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
|
||||
--popover: 1 0 0;
|
||||
--popover-foreground: 0.145 0 0;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
|
||||
--primary: 0.205 0 0;
|
||||
--primary-foreground: 0.985 0 0;
|
||||
--primary: 240 5.9% 10%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
|
||||
--secondary: 0.97 0 0;
|
||||
--secondary-foreground: 0.205 0 0;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
|
||||
--muted: 0.97 0 0;
|
||||
--muted-foreground: 0.556 0 0;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
|
||||
--accent: 0.97 0 0;
|
||||
--accent-foreground: 0.205 0 0;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
|
||||
--destructive: 0.577 0.245 27.325;
|
||||
--destructive-foreground: 0.985 0 0;
|
||||
--destructive: 0 84.2% 50.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--border: 0.922 0 0;
|
||||
--input: 0.922 0 0;
|
||||
--ring: 0.708 0 0;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 240 10% 3.9%;
|
||||
|
||||
--radius: 0.625rem;
|
||||
--radius: 0.5rem;
|
||||
--overlay: rgba(0, 0, 0, 0.2);
|
||||
|
||||
--chart-1: 0.646 0.222 41.116;
|
||||
--chart-2: 0.6 0.118 184.704;
|
||||
--chart-3: 0.398 0.07 227.392;
|
||||
--chart-4: 0.828 0.189 84.429;
|
||||
--chart-5: 0.769 0.188 70.08;
|
||||
--sidebar: 0.985 0 0;
|
||||
--sidebar-foreground: 0.145 0 0;
|
||||
--sidebar-primary: 0.205 0 0;
|
||||
--sidebar-primary-foreground: 0.985 0 0;
|
||||
--sidebar-accent: 0.97 0 0;
|
||||
--sidebar-accent-foreground: 0.205 0 0;
|
||||
--sidebar-border: 0.922 0 0;
|
||||
--sidebar-ring: 0.708 0 0;
|
||||
--chart-1: 173 58% 39%;
|
||||
--chart-2: 12 76% 61%;
|
||||
--chart-3: 197 37% 24%;
|
||||
--chart-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
--sidebar-background: 0 0% 98%;
|
||||
--sidebar-foreground: 240 5.3% 26.1%;
|
||||
--sidebar-primary: 240 5.9% 10%;
|
||||
--sidebar-primary-foreground: 0 0% 98%;
|
||||
--sidebar-accent: 240 4.8% 95.9%;
|
||||
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||
--sidebar-border: 220 13% 91%;
|
||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--terminal-paste: rgba(255, 255, 255, 0.2);
|
||||
--background: 0.145 0 0;
|
||||
--foreground: 0.985 0 0;
|
||||
--background: 0 0% 0%;
|
||||
--foreground: 0 0% 98%;
|
||||
|
||||
--card: 0.205 0 0;
|
||||
--card-foreground: 0.985 0 0;
|
||||
--card: 240 4% 10%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
|
||||
--popover: 0.205 0 0;
|
||||
--popover-foreground: 0.985 0 0;
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
|
||||
--primary: 0.922 0 0;
|
||||
--primary-foreground: 0.205 0 0;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 240 5.9% 10%;
|
||||
|
||||
--secondary: 0.269 0 0;
|
||||
--secondary-foreground: 0.985 0 0;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
|
||||
--muted: 0.269 0 0;
|
||||
--muted-foreground: 0.708 0 0;
|
||||
--muted: 240 4% 10%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
|
||||
--accent: 0.269 0 0;
|
||||
--accent-foreground: 0.985 0 0;
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
|
||||
--destructive: 0.704 0.191 22.216;
|
||||
--destructive-foreground: 0.985 0 0;
|
||||
--destructive: 0 84.2% 50.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--border: 0.371 0 0;
|
||||
--input: 0.371 0 0;
|
||||
--ring: 0.556 0 0;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 4% 10%;
|
||||
--ring: 240 4.9% 83.9%;
|
||||
|
||||
--overlay: rgba(0, 0, 0, 0.5);
|
||||
|
||||
--chart-1: 0.488 0.243 264.376;
|
||||
--chart-2: 0.696 0.17 162.48;
|
||||
--chart-3: 0.769 0.188 70.08;
|
||||
--chart-4: 0.627 0.265 303.9;
|
||||
--chart-5: 0.645 0.246 16.439;
|
||||
--sidebar: 0.205 0 0;
|
||||
--sidebar-foreground: 0.985 0 0;
|
||||
--sidebar-primary: 0.488 0.243 264.376;
|
||||
--sidebar-primary-foreground: 0.985 0 0;
|
||||
--sidebar-accent: 0.269 0 0;
|
||||
--sidebar-accent-foreground: 0.985 0 0;
|
||||
--sidebar-border: 0.371 0 0;
|
||||
--sidebar-ring: 0.556 0 0;
|
||||
--chart-1: 220 70% 50%;
|
||||
--chart-5: 160 60% 45%;
|
||||
--chart-3: 30 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-2: 340 75% 55%;
|
||||
--sidebar-background: 240 5.9% 10%;
|
||||
--sidebar-foreground: 240 4.8% 95.9%;
|
||||
--sidebar-primary: 224.3 76.3% 48%;
|
||||
--sidebar-primary-foreground: 0 0% 100%;
|
||||
--sidebar-accent: 240 3.7% 15.9%;
|
||||
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
||||
--sidebar-border: 240 3.7% 15.9%;
|
||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,13 +118,13 @@
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: oklch(var(--border));
|
||||
background: hsl(var(--border));
|
||||
border-radius: 0.3125rem;
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: oklch(var(--border)) transparent;
|
||||
scrollbar-color: hsl(var(--border)) transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
@layer utilities {
|
||||
.custom-logs-scrollbar {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: oklch(var(--muted-foreground)) transparent;
|
||||
scrollbar-color: hsl(var(--muted-foreground)) transparent;
|
||||
}
|
||||
|
||||
.custom-logs-scrollbar::-webkit-scrollbar {
|
||||
@@ -229,12 +229,12 @@
|
||||
}
|
||||
|
||||
.custom-logs-scrollbar::-webkit-scrollbar-thumb {
|
||||
background-color: oklch(var(--muted-foreground) / 0.3);
|
||||
background-color: hsl(var(--muted-foreground) / 0.3);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.custom-logs-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background-color: oklch(var(--muted-foreground) / 0.5);
|
||||
background-color: hsl(var(--muted-foreground) / 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,48 +32,48 @@ const config = {
|
||||
"10xl": "105rem",
|
||||
},
|
||||
colors: {
|
||||
border: "oklch(var(--border) / <alpha-value>)",
|
||||
input: "oklch(var(--input) / <alpha-value>)",
|
||||
ring: "oklch(var(--ring) / <alpha-value>)",
|
||||
background: "oklch(var(--background) / <alpha-value>)",
|
||||
foreground: "oklch(var(--foreground) / <alpha-value>)",
|
||||
border: "hsl(var(--border))",
|
||||
input: "hsl(var(--input))",
|
||||
ring: "hsl(var(--ring))",
|
||||
background: "hsl(var(--background))",
|
||||
foreground: "hsl(var(--foreground))",
|
||||
primary: {
|
||||
DEFAULT: "oklch(var(--primary) / <alpha-value>)",
|
||||
foreground: "oklch(var(--primary-foreground) / <alpha-value>)",
|
||||
DEFAULT: "hsl(var(--primary))",
|
||||
foreground: "hsl(var(--primary-foreground))",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "oklch(var(--secondary) / <alpha-value>)",
|
||||
foreground: "oklch(var(--secondary-foreground) / <alpha-value>)",
|
||||
DEFAULT: "hsl(var(--secondary))",
|
||||
foreground: "hsl(var(--secondary-foreground))",
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "oklch(var(--destructive) / <alpha-value>)",
|
||||
foreground: "oklch(var(--destructive-foreground) / <alpha-value>)",
|
||||
DEFAULT: "hsl(var(--destructive))",
|
||||
foreground: "hsl(var(--destructive-foreground))",
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "oklch(var(--muted) / <alpha-value>)",
|
||||
foreground: "oklch(var(--muted-foreground) / <alpha-value>)",
|
||||
DEFAULT: "hsl(var(--muted))",
|
||||
foreground: "hsl(var(--muted-foreground))",
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "oklch(var(--accent) / <alpha-value>)",
|
||||
foreground: "oklch(var(--accent-foreground) / <alpha-value>)",
|
||||
DEFAULT: "hsl(var(--accent))",
|
||||
foreground: "hsl(var(--accent-foreground))",
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "oklch(var(--popover) / <alpha-value>)",
|
||||
foreground: "oklch(var(--popover-foreground) / <alpha-value>)",
|
||||
DEFAULT: "hsl(var(--popover))",
|
||||
foreground: "hsl(var(--popover-foreground))",
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "oklch(var(--card) / <alpha-value>)",
|
||||
foreground: "oklch(var(--card-foreground) / <alpha-value>)",
|
||||
DEFAULT: "hsl(var(--card))",
|
||||
foreground: "hsl(var(--card-foreground))",
|
||||
},
|
||||
sidebar: {
|
||||
DEFAULT: "oklch(var(--sidebar) / <alpha-value>)",
|
||||
foreground: "oklch(var(--sidebar-foreground) / <alpha-value>)",
|
||||
primary: "oklch(var(--sidebar-primary) / <alpha-value>)",
|
||||
"primary-foreground": "oklch(var(--sidebar-primary-foreground) / <alpha-value>)",
|
||||
accent: "oklch(var(--sidebar-accent) / <alpha-value>)",
|
||||
"accent-foreground": "oklch(var(--sidebar-accent-foreground) / <alpha-value>)",
|
||||
border: "oklch(var(--sidebar-border) / <alpha-value>)",
|
||||
ring: "oklch(var(--sidebar-ring) / <alpha-value>)",
|
||||
DEFAULT: "hsl(var(--sidebar-background))",
|
||||
foreground: "hsl(var(--sidebar-foreground))",
|
||||
primary: "hsl(var(--sidebar-primary))",
|
||||
"primary-foreground": "hsl(var(--sidebar-primary-foreground))",
|
||||
accent: "hsl(var(--sidebar-accent))",
|
||||
"accent-foreground": "hsl(var(--sidebar-accent-foreground))",
|
||||
border: "hsl(var(--sidebar-border))",
|
||||
ring: "hsl(var(--sidebar-ring))",
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
|
||||
@@ -1,32 +1,21 @@
|
||||
{
|
||||
"name": "@dokploy/server",
|
||||
"version": "1.0.0",
|
||||
"main": "./dist/index.js",
|
||||
"main": "./src/index.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs.js"
|
||||
},
|
||||
".": "./src/index.ts",
|
||||
"./db": {
|
||||
"import": "./dist/db/index.js",
|
||||
"import": "./src/db/index.ts",
|
||||
"require": "./dist/db/index.cjs.js"
|
||||
},
|
||||
"./*": {
|
||||
"import": "./dist/*",
|
||||
"require": "./dist/*.cjs"
|
||||
"./setup/*": {
|
||||
"import": "./src/setup/*.ts",
|
||||
"require": "./dist/setup/index.cjs.js"
|
||||
},
|
||||
"./dist": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs.js"
|
||||
},
|
||||
"./dist/db": {
|
||||
"import": "./dist/db/index.js",
|
||||
"require": "./dist/db/index.cjs.js"
|
||||
},
|
||||
"./dist/db/schema": {
|
||||
"import": "./dist/db/schema/index.js",
|
||||
"require": "./dist/db/schema/index.cjs.js"
|
||||
"./constants": {
|
||||
"import": "./src/constants/index.ts",
|
||||
"require": "./dist/constants.cjs.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
104
packages/server/src/emails/emails/verify-email.tsx
Normal file
104
packages/server/src/emails/emails/verify-email.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Img,
|
||||
Link,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
|
||||
export type TemplateProps = {
|
||||
userName: string;
|
||||
verificationUrl: string;
|
||||
};
|
||||
|
||||
export const VerifyEmailTemplate = ({
|
||||
userName = "User",
|
||||
verificationUrl = "https://app.dokploy.com/verify",
|
||||
}: TemplateProps) => {
|
||||
const previewText = "Verify your email address to get started with Dokploy";
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{previewText}</Preview>
|
||||
<Tailwind
|
||||
config={{
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: "#007291",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Body className="bg-[#f4f4f5] my-auto mx-auto font-sans">
|
||||
<Container className="my-[40px] mx-auto max-w-[520px]">
|
||||
{/* Header */}
|
||||
<Section className="bg-[#09090b] rounded-t-xl px-[40px] py-[32px] text-center">
|
||||
<Img
|
||||
src="https://raw.githubusercontent.com/Dokploy/website/refs/heads/main/apps/docs/public/logo-dokploy-blackpng.png"
|
||||
width="190"
|
||||
height="120"
|
||||
alt="Dokploy"
|
||||
className="my-0 mx-auto"
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* Body */}
|
||||
<Section className="bg-white px-[40px] py-[32px]">
|
||||
<Heading className="text-[#09090b] text-[22px] font-semibold m-0 mb-[8px]">
|
||||
Verify Your Email
|
||||
</Heading>
|
||||
<Text className="text-[#71717a] text-[14px] leading-[22px] m-0 mb-[24px]">
|
||||
Hello {userName}, thank you for signing up for Dokploy. Please
|
||||
verify your email address to activate your account.
|
||||
</Text>
|
||||
|
||||
{/* CTA Button */}
|
||||
<Section className="text-center mb-[24px]">
|
||||
<Button
|
||||
href={verificationUrl}
|
||||
className="bg-[#09090b] rounded-lg text-white text-[14px] font-semibold no-underline text-center px-[24px] py-[12px]"
|
||||
>
|
||||
Verify Email Address
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Text className="text-[#a1a1aa] text-[13px] leading-[20px] m-0 text-center mb-[16px]">
|
||||
If the button above doesn't work, copy and paste the following
|
||||
link into your browser:
|
||||
</Text>
|
||||
<Text className="text-[#71717a] text-[12px] leading-[18px] m-0 text-center break-all">
|
||||
{verificationUrl}
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
{/* Footer */}
|
||||
<Section className="bg-[#fafafa] rounded-b-xl px-[40px] py-[24px] text-center border-t border-solid border-[#e4e4e7]">
|
||||
<Text className="text-[#a1a1aa] text-[12px] leading-[18px] m-0">
|
||||
This is an automated email from{" "}
|
||||
<Link
|
||||
href="https://dokploy.com"
|
||||
className="text-[#71717a] underline"
|
||||
>
|
||||
Dokploy Cloud
|
||||
</Link>
|
||||
. If you didn't create an account, you can safely ignore this
|
||||
email.
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default VerifyEmailTemplate;
|
||||
@@ -21,7 +21,10 @@ import {
|
||||
updateWebServerSettings,
|
||||
} from "../services/web-server-settings";
|
||||
import { getHubSpotUTK, submitToHubSpot } from "../utils/tracking/hubspot";
|
||||
import { sendEmail } from "../verification/send-verification-email";
|
||||
import {
|
||||
sendEmail,
|
||||
sendVerificationEmail,
|
||||
} from "../verification/send-verification-email";
|
||||
import { getPublicIpWithFallback } from "../wss/utils";
|
||||
import { ac, adminRole, memberRole, ownerRole } from "./access-control";
|
||||
|
||||
@@ -106,14 +109,13 @@ const { handler, api } = betterAuth({
|
||||
emailVerification: {
|
||||
sendOnSignUp: true,
|
||||
autoSignInAfterVerification: true,
|
||||
sendOnSignIn: true,
|
||||
sendVerificationEmail: async ({ user, url }) => {
|
||||
if (IS_CLOUD) {
|
||||
await sendEmail({
|
||||
await sendVerificationEmail({
|
||||
userName: user.name || "User",
|
||||
email: user.email,
|
||||
subject: "Verify your email",
|
||||
text: `
|
||||
<p>Click the link to verify your email: <a href="${url}">Verify Email</a></p>
|
||||
`,
|
||||
verificationUrl: url,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import VerifyEmailTemplate from "../emails/emails/verify-email";
|
||||
import { sendEmailNotification } from "../utils/notifications/utils";
|
||||
|
||||
export const sendEmail = async ({
|
||||
email,
|
||||
subject,
|
||||
@@ -26,3 +29,25 @@ export const sendEmail = async ({
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export const sendVerificationEmail = async ({
|
||||
userName,
|
||||
email,
|
||||
verificationUrl,
|
||||
}: {
|
||||
userName: string;
|
||||
email: string;
|
||||
verificationUrl: string;
|
||||
}) => {
|
||||
const html = await renderAsync(
|
||||
VerifyEmailTemplate({
|
||||
userName: userName || "User",
|
||||
verificationUrl,
|
||||
}),
|
||||
);
|
||||
await sendEmail({
|
||||
email,
|
||||
subject: "Verify your email",
|
||||
text: html,
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user