Compare commits

..

44 Commits

Author SHA1 Message Date
Mauricio Siu
6968cb6930 Merge pull request #1465 from zaaakher/docs/guides
docs: update `CONTRIBUTING.md` and add `GUIDES.md`
2025-03-29 14:28:24 -06:00
Mauricio Siu
a431e4c58e Update CONTRIBUTING.md 2025-03-29 14:28:11 -06:00
Mauricio Siu
c5b4b85470 Merge pull request #1578 from Dokploy/fix/biome-lint
chore(workflow): add Biome code formatting workflow for canary branch
2025-03-29 13:41:49 -06:00
Mauricio Siu
b1ef9d25b1 chore(workflow): add autofix.ci workflow for automatic code formatting on canary branch 2025-03-29 13:41:05 -06:00
Mauricio Siu
74f7c51530 chore(workflow): add autofix.ci workflow for automatic code formatting with Biome 2025-03-29 13:39:57 -06:00
Mauricio Siu
4ba2b9fe8d chore(workflow): add new Biome formatting workflow for canary branch 2025-03-29 13:38:55 -06:00
Mauricio Siu
413eda50f4 chore(workflow): simplify AutoFix action usage in Biome workflow 2025-03-29 13:37:43 -06:00
Mauricio Siu
9f09681708 chore(workflow): streamline Biome setup by replacing Node.js and pnpm steps with biomeJs action 2025-03-29 13:37:01 -06:00
Mauricio Siu
8eb174812d chore(workflow): replace manual commit step with AutoFix action for Biome formatting 2025-03-29 13:35:50 -06:00
Mauricio Siu
be77f114eb Merge ca42708035 into beadcf871a 2025-03-29 19:30:24 +00:00
Mauricio Siu
ca42708035 chore(workflow): configure git user for automated commits and enforce push 2025-03-29 13:30:18 -06:00
Mauricio Siu
8b03454a87 chore(workflow): update Biome workflow to push changes to the correct branch 2025-03-29 13:28:50 -06:00
Mauricio Siu
fa7f749f84 refactor(dokploy): standardize code formatting and improve readability across multiple components 2025-03-29 13:26:44 -06:00
Mauricio Siu
3daecd7d71 chore(workflow): update pnpm version to 9.5.0 in Biome workflow 2025-03-29 13:20:48 -06:00
Mauricio Siu
0666b5b292 chore(workflow): add pnpm setup step to Biome workflow 2025-03-29 13:20:07 -06:00
Mauricio Siu
b288ddd826 chore(workflow): add Biome code formatting workflow for canary branch 2025-03-29 13:18:50 -06:00
Mauricio Siu
beadcf871a Merge pull request #1577 from Dokploy/1568-dokploy---nextjs-affected-by-cve-2025-29927
refactor(dokploy): remove lucia-auth adapter and related authenticati…
2025-03-29 12:23:26 -06:00
Mauricio Siu
ee49dadf0b refactor(dokploy): remove lucia-auth adapter and related authentication logic; update next.js version to 15.2.4 2025-03-29 12:17:14 -06:00
Mauricio Siu
46de83a1de Merge pull request #1576 from Dokploy/1564-downloaded-ssh-keys-are-always-named-as-id_rsa-keys
refactor(ssh-keys): simplify downloadKey function and filename genera…
2025-03-29 12:13:38 -06:00
Mauricio Siu
fee5024b7d refactor(ssh-keys): simplify downloadKey function and filename generation logic 2025-03-29 12:13:22 -06:00
Mauricio Siu
e0433e9f7b Merge pull request #1554 from Dokploy/1061-custom-docker-service-hostname
1061 custom docker service hostname
2025-03-23 23:58:28 -06:00
Mauricio Siu
d29ff881fc fix(redis-connection): update Redis host configuration to use environment variable for production 2025-03-23 23:43:38 -06:00
Mauricio Siu
568c3a1d06 chore(workflow): update branches for Dokploy Docker build to use custom hostname 2025-03-23 23:38:55 -06:00
Mauricio Siu
e9fd280fa2 refactor(server): comment out initialization of Postgres, Traefik, and Redis for testing purposes 2025-03-23 23:28:53 -06:00
Mauricio Siu
9535fca28f Merge pull request #1540 from vicke4/backup-deletion-fix
fix(backups): auto deletion of backups
2025-03-23 04:31:37 -06:00
Mauricio Siu
dd62d603e0 Merge pull request #1550 from Dokploy/1543-preview-docker-compose-button-null-when-git-is-provider
feat(dashboard): add informational alert for docker-compose preview r…
2025-03-23 04:30:58 -06:00
Mauricio Siu
8d227e2a2c feat(dashboard): add informational alert for docker-compose preview requirements 2025-03-23 04:30:00 -06:00
vicke4
68d0a48843 fix(backups): auto deletion of backups 2025-03-21 01:36:11 +05:30
Mauricio Siu
91183056f0 Merge pull request #1534 from Dokploy/feat/enable-swarm-overview
Feat/enable swarm overview
2025-03-19 00:52:22 -06:00
Mauricio Siu
03bd4398d0 chore(package): bump version to v0.20.8 2025-03-19 00:51:49 -06:00
Mauricio Siu
8c260eff72 feat(cluster): enhance AddNode and ShowNodes components for better user guidance and functionality
- Added an AlertBlock in AddNode to inform users about architecture compatibility when adding nodes.
- Updated ShowNodes to correctly handle node deletion actions based on ManagerStatus.
- Refactored cluster API to remove cloud-specific checks and improve command execution for remote servers.
2025-03-19 00:51:27 -06:00
Mauricio Siu
6e28196b0e chore(package): bump version to v0.20.7 2025-03-18 21:36:39 -06:00
Mauricio Siu
18bacae175 Merge pull request #1507 from nb5p/fix-alpine-linux-compatibility
fix(server-setup): resolve Alpine Linux compatibility issues
2025-03-18 21:35:43 -06:00
Mauricio Siu
f2be5a378e Merge pull request #1522 from ensarkurrt/canary
fix(ui): Improve Numeric Input Handling in Swarm Cluster Settings, Traefik Port Mappings, and Email Notifications
2025-03-18 21:27:20 -06:00
Mauricio Siu
aef24296b9 Merge pull request #1531 from Dokploy/fix/loader-swarm
Fix/loader swarm
2025-03-18 21:18:17 -06:00
Mauricio Siu
7123b9b109 feat(cluster): add error handling in AddManager and AddWorker components
- Integrated error handling in AddManager and AddWorker components to display error messages using AlertBlock when data fetching fails.
- Updated API query hooks to include error and isError states for improved user feedback during data operations.
2025-03-18 21:17:11 -06:00
Mauricio Siu
891dc840f5 feat(cluster): enhance node management UI with loading indicators and improved tab content
- Added loading indicators in AddManager and AddWorker components to enhance user experience during data fetching.
- Updated AddNode component to include overflow handling for tab content.
- Renamed "Show Nodes" to "Show Swarm Nodes" in ShowNodesModal for clarity.
2025-03-18 21:11:50 -06:00
Zakher Masri
bc78100613 remove redis part 2025-03-18 12:02:03 +03:00
Ensar Kurt
3cdf4c426c revert commit from #1513 2025-03-18 00:05:59 +03:00
Ensar Kurt
7cb184dc97 email notification port, last digit staying error fix 2025-03-17 23:48:17 +03:00
Ensar Kurt
fe57333f84 manage port inputs, default zero fix 2025-03-17 23:47:54 +03:00
Ensar Kurt
04fd77c3a9 replicas input cannot be zero and empty 2025-03-17 23:42:09 +03:00
nb5p
2974a8183e fix(server-setup): resolve Alpine Linux compatibility issues with setup scripts
Resolves #1482
2025-03-16 15:37:28 +08:00
Zakher Masri
ac0922d742 docs: update CONTRIBUTING.md and add GUIDES.md 2025-03-11 14:38:37 +03:00
40 changed files with 1212 additions and 1818 deletions

View File

@@ -2,7 +2,7 @@ name: Dokploy Docker Build
on: on:
push: push:
branches: [main, canary, "feat/better-auth-2"] branches: [main, canary, "1061-custom-docker-service-hostname"]
env: env:
IMAGE_NAME: dokploy/dokploy IMAGE_NAME: dokploy/dokploy

22
.github/workflows/format.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: autofix.ci
on:
push:
branches: [canary]
pull_request:
branches: [canary]
jobs:
format:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup biomeJs
uses: biomejs/setup-biome@v2
- name: Run Biome formatter
run: biome format . --write
- uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef

View File

@@ -61,9 +61,9 @@ pnpm install
cp apps/dokploy/.env.example apps/dokploy/.env cp apps/dokploy/.env.example apps/dokploy/.env
``` ```
## Development ## Requirements
Is required to have **Docker** installed on your machine. - [Docker](/GUIDES.md#docker)
### Setup ### Setup

49
GUIDES.md Normal file
View File

@@ -0,0 +1,49 @@
# Docker
Here's how to install docker on different operating systems:
## macOS
1. Visit [Docker Desktop for Mac](https://www.docker.com/products/docker-desktop)
2. Download the Docker Desktop installer
3. Double-click the downloaded `.dmg` file
4. Drag Docker to your Applications folder
5. Open Docker Desktop from Applications
6. Follow the onboarding tutorial if desired
## Linux
### Ubuntu
```bash
# Update package index
sudo apt-get update
# Install prerequisites
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
# Add Docker's official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Set up stable repository
echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
```
## Windows
1. Enable WSL2 if not already enabled
2. Visit [Docker Desktop for Windows](https://www.docker.com/products/docker-desktop)
3. Download the installer
4. Run the installer and follow the prompts
5. Start Docker Desktop from the Start menu

View File

@@ -40,7 +40,7 @@ interface Props {
} }
const AddRedirectchema = z.object({ const AddRedirectchema = z.object({
replicas: z.number(), replicas: z.number().min(1, "Replicas must be at least 1"),
registryId: z.string(), registryId: z.string(),
}); });
@@ -130,9 +130,11 @@ export const ShowClusterSettings = ({ applicationId }: Props) => {
placeholder="1" placeholder="1"
{...field} {...field}
onChange={(e) => { onChange={(e) => {
field.onChange(Number(e.target.value)); const value = e.target.value;
field.onChange(value === "" ? 0 : Number(value));
}} }}
type="number" type="number"
value={field.value || ""}
/> />
</FormControl> </FormControl>

View File

@@ -115,7 +115,11 @@ export const SaveDockerProvider = ({ applicationId }: Props) => {
<FormItem> <FormItem>
<FormLabel>Username</FormLabel> <FormLabel>Username</FormLabel>
<FormControl> <FormControl>
<Input placeholder="Username" autoComplete="username" {...field} /> <Input
placeholder="Username"
autoComplete="username"
{...field}
/>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -130,7 +134,12 @@ export const SaveDockerProvider = ({ applicationId }: Props) => {
<FormItem> <FormItem>
<FormLabel>Password</FormLabel> <FormLabel>Password</FormLabel>
<FormControl> <FormControl>
<Input placeholder="Password" autoComplete="one-time-code" {...field} type="password" /> <Input
placeholder="Password"
autoComplete="one-time-code"
{...field}
type="password"
/>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@@ -147,7 +147,9 @@ export const IsolatedDeployment = ({ composeId }: Props) => {
render={({ field }) => ( render={({ field }) => (
<FormItem className="mt-4 flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm"> <FormItem className="mt-4 flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5"> <div className="space-y-0.5">
<FormLabel>Enable Isolated Deployment ({data?.appName})</FormLabel> <FormLabel>
Enable Isolated Deployment ({data?.appName})
</FormLabel>
<FormDescription> <FormDescription>
Enable isolated deployment to the compose file. Enable isolated deployment to the compose file.
</FormDescription> </FormDescription>

View File

@@ -62,6 +62,11 @@ export const ShowConvertedCompose = ({ composeId }: Props) => {
</DialogHeader> </DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>} {isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<AlertBlock type="info">
Preview your docker-compose file with added domains. Note: At least
one domain must be specified for this conversion to take effect.
</AlertBlock>
<div className="flex flex-row gap-2 justify-end"> <div className="flex flex-row gap-2 justify-end">
<Button <Button
variant="secondary" variant="secondary"

View File

@@ -286,10 +286,15 @@ export const AddBackup = ({ databaseId, databaseType, refetch }: Props) => {
<FormItem> <FormItem>
<FormLabel>Keep the latest</FormLabel> <FormLabel>Keep the latest</FormLabel>
<FormControl> <FormControl>
<Input type="number" placeholder={"keeps all the backups if left empty"} {...field} /> <Input
type="number"
placeholder={"keeps all the backups if left empty"}
{...field}
/>
</FormControl> </FormControl>
<FormDescription> <FormDescription>
Optional. If provided, only keeps the latest N backups in the cloud. Optional. If provided, only keeps the latest N backups
in the cloud.
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@@ -92,7 +92,9 @@ export const UpdateBackup = ({ backupId, refetch }: Props) => {
enabled: backup.enabled || false, enabled: backup.enabled || false,
prefix: backup.prefix, prefix: backup.prefix,
schedule: backup.schedule, schedule: backup.schedule,
keepLatestCount: backup.keepLatestCount ? Number(backup.keepLatestCount) : undefined, keepLatestCount: backup.keepLatestCount
? Number(backup.keepLatestCount)
: undefined,
}); });
} }
}, [form, form.reset, backup]); }, [form, form.reset, backup]);
@@ -274,10 +276,15 @@ export const UpdateBackup = ({ backupId, refetch }: Props) => {
<FormItem> <FormItem>
<FormLabel>Keep the latest</FormLabel> <FormLabel>Keep the latest</FormLabel>
<FormControl> <FormControl>
<Input type="number" placeholder={"keeps all the backups if left empty"} {...field} /> <Input
type="number"
placeholder={"keeps all the backups if left empty"}
{...field}
/>
</FormControl> </FormControl>
<FormDescription> <FormDescription>
Optional. If provided, only keeps the latest N backups in the cloud. Optional. If provided, only keeps the latest N backups
in the cloud.
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@@ -33,7 +33,11 @@ const DockerProviderSchema = z.object({
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;

View File

@@ -33,7 +33,11 @@ const DockerProviderSchema = z.object({
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;

View File

@@ -33,7 +33,11 @@ const DockerProviderSchema = z.object({
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;

View File

@@ -33,7 +33,11 @@ const DockerProviderSchema = z.object({
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;

View File

@@ -51,7 +51,7 @@ export const UpdatePostgres = ({ postgresId }: Props) => {
}, },
{ {
enabled: !!postgresId, enabled: !!postgresId,
} },
); );
const form = useForm<UpdatePostgres>({ const form = useForm<UpdatePostgres>({
defaultValues: { defaultValues: {

View File

@@ -186,7 +186,9 @@ export const ShowProjects = () => {
target="_blank" target="_blank"
href={`${domain.https ? "https" : "http"}://${domain.host}${domain.path}`} href={`${domain.https ? "https" : "http"}://${domain.host}${domain.path}`}
> >
<span className="truncate">{domain.host}</span> <span className="truncate">
{domain.host}
</span>
<ExternalLinkIcon className="size-4 shrink-0" /> <ExternalLinkIcon className="size-4 shrink-0" />
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
@@ -222,7 +224,9 @@ export const ShowProjects = () => {
target="_blank" target="_blank"
href={`${domain.https ? "https" : "http"}://${domain.host}${domain.path}`} href={`${domain.https ? "https" : "http"}://${domain.host}${domain.path}`}
> >
<span className="truncate">{domain.host}</span> <span className="truncate">
{domain.host}
</span>
<ExternalLinkIcon className="size-4 shrink-0" /> <ExternalLinkIcon className="size-4 shrink-0" />
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>

View File

@@ -33,7 +33,11 @@ const DockerProviderSchema = z.object({
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;

View File

@@ -12,6 +12,7 @@ import { ExternalLink, PlusIcon } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { AddManager } from "./manager/add-manager"; import { AddManager } from "./manager/add-manager";
import { AddWorker } from "./workers/add-worker"; import { AddWorker } from "./workers/add-worker";
import { AlertBlock } from "@/components/shared/alert-block";
interface Props { interface Props {
serverId?: string; serverId?: string;
@@ -48,6 +49,10 @@ export const AddNode = ({ serverId }: Props) => {
Architecture Architecture
<ExternalLink className="h-4 w-4" /> <ExternalLink className="h-4 w-4" />
</Link> </Link>
<AlertBlock type="warning">
Make sure you use the same architecture as the node you are
adding.
</AlertBlock>
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
@@ -56,10 +61,10 @@ export const AddNode = ({ serverId }: Props) => {
<TabsTrigger value="worker">Worker</TabsTrigger> <TabsTrigger value="worker">Worker</TabsTrigger>
<TabsTrigger value="manager">Manager</TabsTrigger> <TabsTrigger value="manager">Manager</TabsTrigger>
</TabsList> </TabsList>
<TabsContent value="worker" className="pt-4"> <TabsContent value="worker" className="pt-4 overflow-hidden">
<AddWorker serverId={serverId} /> <AddWorker serverId={serverId} />
</TabsContent> </TabsContent>
<TabsContent value="manager" className="pt-4"> <TabsContent value="manager" className="pt-4 overflow-hidden">
<AddManager serverId={serverId} /> <AddManager serverId={serverId} />
</TabsContent> </TabsContent>
</Tabs> </Tabs>

View File

@@ -1,3 +1,4 @@
import { AlertBlock } from "@/components/shared/alert-block";
import { CardContent } from "@/components/ui/card"; import { CardContent } from "@/components/ui/card";
import { import {
DialogDescription, DialogDescription,
@@ -6,7 +7,7 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { CopyIcon } from "lucide-react"; import { CopyIcon, Loader2 } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
interface Props { interface Props {
@@ -14,18 +15,26 @@ interface Props {
} }
export const AddManager = ({ serverId }: Props) => { export const AddManager = ({ serverId }: Props) => {
const { data } = api.cluster.addManager.useQuery({ serverId }); const { data, isLoading, error, isError } = api.cluster.addManager.useQuery({
serverId,
});
return ( return (
<> <>
<div> <CardContent className="sm:max-w-4xl flex flex-col gap-4 px-0">
<CardContent className="sm:max-w-4xl max-h-screen overflow-y-auto flex flex-col gap-4 px-0">
<DialogHeader> <DialogHeader>
<DialogTitle>Add a new manager</DialogTitle> <DialogTitle>Add a new manager</DialogTitle>
<DialogDescription>Add a new manager</DialogDescription> <DialogDescription>Add a new manager</DialogDescription>
</DialogHeader> </DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
{isLoading ? (
<Loader2 className="w-full animate-spin text-muted-foreground" />
) : (
<>
<div className="flex flex-col gap-2.5 text-sm"> <div className="flex flex-col gap-2.5 text-sm">
<span>1. Go to your new server and run the following command</span> <span>
1. Go to your new server and run the following command
</span>
<span className="bg-muted rounded-lg p-2 flex justify-between"> <span className="bg-muted rounded-lg p-2 flex justify-between">
curl https://get.docker.com | sh -s -- --version {data?.version} curl https://get.docker.com | sh -s -- --version {data?.version}
<button <button
@@ -48,6 +57,7 @@ export const AddManager = ({ serverId }: Props) => {
2. Run the following command to add the node(manager) to your 2. Run the following command to add the node(manager) to your
cluster cluster
</span> </span>
<span className="bg-muted rounded-lg p-2 flex"> <span className="bg-muted rounded-lg p-2 flex">
{data?.command} {data?.command}
<button <button
@@ -62,8 +72,9 @@ export const AddManager = ({ serverId }: Props) => {
</button> </button>
</span> </span>
</div> </div>
</>
)}
</CardContent> </CardContent>
</div>
</> </>
); );
}; };

View File

@@ -17,7 +17,7 @@ export const ShowNodesModal = ({ serverId }: Props) => {
className="w-full cursor-pointer " className="w-full cursor-pointer "
onSelect={(e) => e.preventDefault()} onSelect={(e) => e.preventDefault()}
> >
Show Nodes Show Swarm Nodes
</DropdownMenuItem> </DropdownMenuItem>
</DialogTrigger> </DialogTrigger>
<DialogContent className="sm:max-w-5xl overflow-y-auto max-h-screen "> <DialogContent className="sm:max-w-5xl overflow-y-auto max-h-screen ">

View File

@@ -144,7 +144,7 @@ export const ShowNodes = ({ serverId }: Props) => {
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel> <DropdownMenuLabel>Actions</DropdownMenuLabel>
<ShowNodeData data={node} /> <ShowNodeData data={node} />
{node?.ManagerStatus?.Leader && ( {!node?.ManagerStatus?.Leader && (
<DialogAction <DialogAction
title="Delete Node" title="Delete Node"
description="Are you sure you want to delete this node from the cluster?" description="Are you sure you want to delete this node from the cluster?"

View File

@@ -1,3 +1,4 @@
import { AlertBlock } from "@/components/shared/alert-block";
import { CardContent } from "@/components/ui/card"; import { CardContent } from "@/components/ui/card";
import { import {
DialogDescription, DialogDescription,
@@ -6,7 +7,7 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { CopyIcon } from "lucide-react"; import { CopyIcon, Loader2 } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
interface Props { interface Props {
@@ -14,15 +15,21 @@ interface Props {
} }
export const AddWorker = ({ serverId }: Props) => { export const AddWorker = ({ serverId }: Props) => {
const { data } = api.cluster.addWorker.useQuery({ serverId }); const { data, isLoading, error, isError } = api.cluster.addWorker.useQuery({
serverId,
});
return ( return (
<div> <CardContent className="sm:max-w-4xl flex flex-col gap-4 px-0">
<CardContent className="sm:max-w-4xl max-h-screen overflow-y-auto flex flex-col gap-4 px-0">
<DialogHeader> <DialogHeader>
<DialogTitle>Add a new worker</DialogTitle> <DialogTitle>Add a new worker</DialogTitle>
<DialogDescription>Add a new worker</DialogDescription> <DialogDescription>Add a new worker</DialogDescription>
</DialogHeader> </DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
{isLoading ? (
<Loader2 className="w-full animate-spin text-muted-foreground" />
) : (
<>
<div className="flex flex-col gap-2.5 text-sm"> <div className="flex flex-col gap-2.5 text-sm">
<span>1. Go to your new server and run the following command</span> <span>1. Go to your new server and run the following command</span>
<span className="bg-muted rounded-lg p-2 flex justify-between"> <span className="bg-muted rounded-lg p-2 flex justify-between">
@@ -44,7 +51,8 @@ export const AddWorker = ({ serverId }: Props) => {
<div className="flex flex-col gap-2.5 text-sm"> <div className="flex flex-col gap-2.5 text-sm">
<span> <span>
2. Run the following command to add the node(worker) to your cluster 2. Run the following command to add the node(worker) to your
cluster
</span> </span>
<span className="bg-muted rounded-lg p-2 flex"> <span className="bg-muted rounded-lg p-2 flex">
@@ -61,7 +69,8 @@ export const AddWorker = ({ serverId }: Props) => {
</button> </button>
</span> </span>
</div> </div>
</>
)}
</CardContent> </CardContent>
</div>
); );
}; };

View File

@@ -663,13 +663,16 @@ export const HandleNotifications = ({ notificationId }: Props) => {
{...field} {...field}
onChange={(e) => { onChange={(e) => {
const value = e.target.value; const value = e.target.value;
if (value) { if (value === "") {
field.onChange(undefined);
} else {
const port = Number.parseInt(value); const port = Number.parseInt(value);
if (port > 0 && port < 65536) { if (port > 0 && port < 65536) {
field.onChange(port); field.onChange(port);
} }
} }
}} }}
value={field.value || ""}
type="number" type="number"
/> />
</FormControl> </FormControl>

View File

@@ -112,15 +112,17 @@ export const HandleSSHKeys = ({ sshKeyId }: Props) => {
toast.error("Error generating the SSH Key"); toast.error("Error generating the SSH Key");
}); });
const downloadKey = ( const downloadKey = (content: string, keyType: "private" | "public") => {
content: string,
defaultFilename: string,
keyType: "private" | "public",
) => {
const keyName = form.watch("name"); const keyName = form.watch("name");
const publicKey = form.watch("publicKey");
// Extract algorithm type from public key
const isEd25519 = publicKey.startsWith("ssh-ed25519");
const defaultName = isEd25519 ? "id_ed25519" : "id_rsa";
const filename = keyName const filename = keyName
? `${keyName}${sshKeyId ? `_${sshKeyId}` : ""}_${keyType}_${defaultFilename}` ? `${keyName}${sshKeyId ? `_${sshKeyId}` : ""}_${keyType}_${defaultName}${keyType === "public" ? ".pub" : ""}`
: `${keyType}_${defaultFilename}`; : `${defaultName}${keyType === "public" ? ".pub" : ""}`;
const blob = new Blob([content], { type: "text/plain" }); const blob = new Blob([content], { type: "text/plain" });
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
const a = document.createElement("a"); const a = document.createElement("a");
@@ -273,7 +275,7 @@ export const HandleSSHKeys = ({ sshKeyId }: Props) => {
variant="outline" variant="outline"
size="default" size="default"
onClick={() => onClick={() =>
downloadKey(form.watch("privateKey"), "id_rsa", "private") downloadKey(form.watch("privateKey"), "private")
} }
className="flex items-center gap-2" className="flex items-center gap-2"
> >
@@ -287,11 +289,7 @@ export const HandleSSHKeys = ({ sshKeyId }: Props) => {
variant="outline" variant="outline"
size="default" size="default"
onClick={() => onClick={() =>
downloadKey( downloadKey(form.watch("publicKey"), "public")
form.watch("publicKey"),
"id_rsa.pub",
"public",
)
} }
className="flex items-center gap-2" className="flex items-center gap-2"
> >

View File

@@ -159,9 +159,15 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
<Input <Input
type="number" type="number"
{...field} {...field}
onChange={(e) => onChange={(e) => {
field.onChange(Number(e.target.value)) const value = e.target.value;
} field.onChange(
value === ""
? undefined
: Number(value),
);
}}
value={field.value || ""}
className="w-full dark:bg-black" className="w-full dark:bg-black"
placeholder="e.g. 8080" placeholder="e.g. 8080"
/> />
@@ -185,9 +191,15 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
<Input <Input
type="number" type="number"
{...field} {...field}
onChange={(e) => onChange={(e) => {
field.onChange(Number(e.target.value)) const value = e.target.value;
} field.onChange(
value === ""
? undefined
: Number(value),
);
}}
value={field.value || ""}
className="w-full dark:bg-black" className="w-full dark:bg-black"
placeholder="e.g. 80" placeholder="e.g. 80"
/> />

View File

@@ -37,7 +37,9 @@ export const BreadcrumbSidebar = ({ list }: Props) => {
)} )}
</BreadcrumbLink> </BreadcrumbLink>
</BreadcrumbItem> </BreadcrumbItem>
{_index + 1 < list.length && <BreadcrumbSeparator className="block" />} {_index + 1 < list.length && (
<BreadcrumbSeparator className="block" />
)}
</Fragment> </Fragment>
))} ))}
</BreadcrumbList> </BreadcrumbList>

View File

@@ -39,7 +39,7 @@ const NumberInput = React.forwardRef<HTMLInputElement, InputProps>(
className={cn("text-left", className)} className={cn("text-left", className)}
ref={ref} ref={ref}
{...props} {...props}
value={props.value === undefined || props.value === "" ? "" : String(props.value)} value={props.value === undefined ? undefined : String(props.value)}
onChange={(e) => { onChange={(e) => {
const value = e.target.value; const value = e.target.value;
if (value === "") { if (value === "") {
@@ -60,21 +60,6 @@ const NumberInput = React.forwardRef<HTMLInputElement, InputProps>(
} }
} }
}} }}
onBlur={(e) => {
// If input is empty, make 0 when focus is lost
if (e.target.value === "") {
const syntheticEvent = {
...e,
target: {
...e.target,
value: "0",
},
};
props.onChange?.(
syntheticEvent as unknown as React.ChangeEvent<HTMLInputElement>,
);
}
}}
/> />
); );
}, },

View File

@@ -1,32 +0,0 @@
// middleware.ts
import { verifyRequestOrigin } from "lucia";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export async function middleware(request: NextRequest): Promise<NextResponse> {
if (request.method === "GET") {
return NextResponse.next();
}
const originHeader = request.headers.get("Origin");
const hostHeader = request.headers.get("Host");
if (
!originHeader ||
!hostHeader ||
!verifyRequestOrigin(originHeader, [hostHeader])
) {
return new NextResponse(null, {
status: 403,
});
}
return NextResponse.next();
}
export const config = {
matcher: [
// Don't handle HMR requests for the dev server we rewrite to
"/settings",
"/dashboard/(.*)",
"/invitation",
],
};

View File

@@ -1,6 +1,6 @@
{ {
"name": "dokploy", "name": "dokploy",
"version": "v0.20.6", "version": "v0.20.8",
"private": true, "private": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"type": "module", "type": "module",
@@ -53,7 +53,6 @@
"@dokploy/trpc-openapi": "0.0.4", "@dokploy/trpc-openapi": "0.0.4",
"@faker-js/faker": "^8.4.1", "@faker-js/faker": "^8.4.1",
"@hookform/resolvers": "^3.9.0", "@hookform/resolvers": "^3.9.0",
"@lucia-auth/adapter-drizzle": "1.0.7",
"@octokit/auth-app": "^6.0.4", "@octokit/auth-app": "^6.0.4",
"@octokit/webhooks": "^13.2.7", "@octokit/webhooks": "^13.2.7",
"@radix-ui/react-accordion": "1.1.2", "@radix-ui/react-accordion": "1.1.2",
@@ -113,11 +112,10 @@
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"lodash": "4.17.21", "lodash": "4.17.21",
"lucia": "^3.0.1",
"lucide-react": "^0.469.0", "lucide-react": "^0.469.0",
"micromatch": "4.0.8", "micromatch": "4.0.8",
"nanoid": "3", "nanoid": "3",
"next": "^15.0.1", "next": "^15.2.4",
"next-i18next": "^15.3.1", "next-i18next": "^15.3.1",
"next-themes": "^0.2.1", "next-themes": "^0.2.1",
"node-os-utils": "1.3.7", "node-os-utils": "1.3.7",

View File

@@ -1,4 +1,3 @@
import { authRouter } from "@/server/api/routers/auth";
import { createTRPCRouter } from "../api/trpc"; import { createTRPCRouter } from "../api/trpc";
import { adminRouter } from "./routers/admin"; import { adminRouter } from "./routers/admin";
import { aiRouter } from "./routers/ai"; import { aiRouter } from "./routers/ai";
@@ -44,7 +43,6 @@ import { userRouter } from "./routers/user";
export const appRouter = createTRPCRouter({ export const appRouter = createTRPCRouter({
admin: adminRouter, admin: adminRouter,
docker: dockerRouter, docker: dockerRouter,
auth: authRouter,
project: projectRouter, project: projectRouter,
application: applicationRouter, application: applicationRouter,
mysql: mysqlRouter, mysql: mysqlRouter,

View File

@@ -1,326 +0,0 @@
import { createTRPCRouter } from "../trpc";
export const authRouter = createTRPCRouter({
// createAdmin: publicProcedure.mutation(async ({ input }) => {
// try {
// if (!IS_CLOUD) {
// const admin = await db.query.admins.findFirst({});
// if (admin) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Admin already exists",
// });
// }
// }
// const newAdmin = await createAdmin(input);
// if (IS_CLOUD) {
// await sendDiscordNotificationWelcome(newAdmin);
// await sendVerificationEmail(newAdmin.id);
// return {
// status: "success",
// type: "cloud",
// };
// }
// // const session = await lucia.createSession(newAdmin.id || "", {});
// // ctx.res.appendHeader(
// // "Set-Cookie",
// // lucia.createSessionCookie(session.id).serialize(),
// // );
// return {
// status: "success",
// type: "selfhosted",
// };
// } catch (error) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// // @ts-ignore
// message: `Error: ${error?.code === "23505" ? "Email already exists" : "Error creating admin"}`,
// cause: error,
// });
// }
// }),
// createUser: publicProcedure.mutation(async ({ input }) => {
// try {
// const _token = await getUserByToken(input.token);
// // if (token.isExpired) {
// // throw new TRPCError({
// // code: "BAD_REQUEST",
// // message: "Invalid token",
// // });
// // }
// // const newUser = await createUser(input);
// // if (IS_CLOUD) {
// // await sendVerificationEmail(token.authId);
// // return true;
// // }
// // const session = await lucia.createSession(newUser?.authId || "", {});
// // ctx.res.appendHeader(
// // "Set-Cookie",
// // lucia.createSessionCookie(session.id).serialize(),
// // );
// return true;
// } catch (error) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Error creating the user",
// cause: error,
// });
// }
// }),
// login: publicProcedure.mutation(async ({ input }) => {
// try {
// const auth = await findAuthByEmail(input.email);
// const correctPassword = bcrypt.compareSync(
// input.password,
// auth?.password || "",
// );
// if (!correctPassword) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Credentials do not match",
// });
// }
// if (auth?.confirmationToken && IS_CLOUD) {
// await sendVerificationEmail(auth.id);
// throw new TRPCError({
// code: "BAD_REQUEST",
// message:
// "Email not confirmed, we have sent you a confirmation email please check your inbox.",
// });
// }
// if (auth?.is2FAEnabled) {
// return {
// is2FAEnabled: true,
// authId: auth.id,
// };
// }
// // const session = await lucia.createSession(auth?.id || "", {});
// // ctx.res.appendHeader(
// // "Set-Cookie",
// // lucia.createSessionCookie(session.id).serialize(),
// // );
// return {
// is2FAEnabled: false,
// authId: auth?.id,
// };
// } catch (error) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: `Error: ${error instanceof Error ? error.message : "Login error"}`,
// cause: error,
// });
// }
// }),
// get: protectedProcedure.query(async ({ ctx }) => {
// const memberResult = await db.query.member.findFirst({
// where: and(
// eq(member.userId, ctx.user.id),
// eq(member.organizationId, ctx.session?.activeOrganizationId || ""),
// ),
// with: {
// user: true,
// },
// });
// return memberResult;
// }),
// logout: protectedProcedure.mutation(async ({ ctx }) => {
// const { req } = ctx;
// const { session } = await validateRequest(req);
// if (!session) return false;
// // await lucia.invalidateSession(session.id);
// // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
// return true;
// }),
// update: protectedProcedure.mutation(async ({ ctx, input }) => {
// const currentAuth = await findAuthByEmail(ctx.user.email);
// if (input.currentPassword || input.password) {
// const correctPassword = bcrypt.compareSync(
// input.currentPassword || "",
// currentAuth?.password || "",
// );
// if (!correctPassword) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Current password is incorrect",
// });
// }
// }
// // const auth = await updateAuthById(ctx.user.authId, {
// // ...(input.email && { email: input.email.toLowerCase() }),
// // ...(input.password && {
// // password: bcrypt.hashSync(input.password, 10),
// // }),
// // ...(input.image && { image: input.image }),
// // });
// return auth;
// }),
// removeSelfAccount: protectedProcedure
// .input(
// z.object({
// password: z.string().min(1),
// }),
// )
// .mutation(async ({ ctx, input }) => {
// if (!IS_CLOUD) {
// throw new TRPCError({
// code: "NOT_FOUND",
// message: "This feature is only available in the cloud version",
// });
// }
// const currentAuth = await findAuthByEmail(ctx.user.email);
// const correctPassword = bcrypt.compareSync(
// input.password,
// currentAuth?.password || "",
// );
// if (!correctPassword) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Password is incorrect",
// });
// }
// const { req } = ctx;
// const { session } = await validateRequest(req);
// if (!session) return false;
// // await lucia.invalidateSession(session.id);
// // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
// // if (ctx.user.rol === "owner") {
// // await removeAdminByAuthId(ctx.user.authId);
// // } else {
// // await removeUserByAuthId(ctx.user.authId);
// // }
// return true;
// }),
// generateToken: protectedProcedure.mutation(async ({ ctx }) => {
// const auth = await findUserById(ctx.user.id);
// console.log(auth);
// // if (auth.token) {
// // await luciaToken.invalidateSession(auth.token);
// // }
// // const session = await luciaToken.createSession(auth?.id || "", {
// // expiresIn: 60 * 60 * 24 * 30,
// // });
// // await updateUser(auth.id, {
// // token: session.id,
// // });
// return auth;
// }),
// verifyToken: protectedProcedure.mutation(async () => {
// return true;
// }),
// one: adminProcedure
// .input(z.object({ userId: z.string().min(1) }))
// .query(async ({ input }) => {
// // TODO: Check if the user is admin or member
// const user = await findUserById(input.userId);
// return user;
// }),
// sendResetPasswordEmail: publicProcedure
// .input(
// z.object({
// email: z.string().min(1).email(),
// }),
// )
// .mutation(async ({ input }) => {
// if (!IS_CLOUD) {
// throw new TRPCError({
// code: "NOT_FOUND",
// message: "This feature is only available in the cloud version",
// });
// }
// const authR = await db.query.auth.findFirst({
// where: eq(auth.email, input.email),
// });
// if (!authR) {
// throw new TRPCError({
// code: "NOT_FOUND",
// message: "User not found",
// });
// }
// const token = nanoid();
// await updateAuthById(authR.id, {
// resetPasswordToken: token,
// // Make resetPassword in 24 hours
// resetPasswordExpiresAt: new Date(
// new Date().getTime() + 24 * 60 * 60 * 1000,
// ).toISOString(),
// });
// await sendEmailNotification(
// {
// fromAddress: process.env.SMTP_FROM_ADDRESS!,
// toAddresses: [authR.email],
// smtpServer: process.env.SMTP_SERVER!,
// smtpPort: Number(process.env.SMTP_PORT),
// username: process.env.SMTP_USERNAME!,
// password: process.env.SMTP_PASSWORD!,
// },
// "Reset Password",
// `
// Reset your password by clicking the link below:
// The link will expire in 24 hours.
// <a href="${WEBSITE_URL}/reset-password?token=${token}">
// Reset Password
// </a>
// `,
// );
// }),
});
// export const sendVerificationEmail = async (authId: string) => {
// const token = nanoid();
// const result = await updateAuthById(authId, {
// confirmationToken: token,
// confirmationExpiresAt: new Date(
// new Date().getTime() + 24 * 60 * 60 * 1000,
// ).toISOString(),
// });
// if (!result) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "User not found",
// });
// }
// await sendEmailNotification(
// {
// fromAddress: process.env.SMTP_FROM_ADDRESS || "",
// toAddresses: [result?.email],
// smtpServer: process.env.SMTP_SERVER || "",
// smtpPort: Number(process.env.SMTP_PORT),
// username: process.env.SMTP_USERNAME || "",
// password: process.env.SMTP_PASSWORD || "",
// },
// "Confirm your email | Dokploy",
// `
// Welcome to Dokploy!
// Please confirm your email by clicking the link below:
// <a href="${WEBSITE_URL}/confirm-email?token=${result?.confirmationToken}">
// Confirm Email
// </a>
// `,
// );
// return true;
// };
// export const sendDiscordNotificationWelcome = async (newAdmin: Auth) => {
// await sendDiscordNotification(
// {
// webhookUrl: process.env.DISCORD_WEBHOOK_URL || "",
// },
// {
// title: "New User Registered",
// color: 0x00ff00,
// fields: [
// {
// name: "Email",
// value: newAdmin.email,
// inline: true,
// },
// ],
// timestamp: newAdmin.createdAt,
// footer: {
// text: "Dokploy User Registration Notification",
// },
// },
// );
// };

View File

@@ -1,8 +1,9 @@
import { getPublicIpWithFallback } from "@/server/wss/terminal"; import { getPublicIpWithFallback } from "@/server/wss/terminal";
import { import {
type DockerNode, type DockerNode,
IS_CLOUD,
execAsync, execAsync,
execAsyncRemote,
findServerById,
getRemoteDocker, getRemoteDocker,
} from "@dokploy/server"; } from "@dokploy/server";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
@@ -16,10 +17,6 @@ export const clusterRouter = createTRPCRouter({
}), }),
) )
.query(async ({ input }) => { .query(async ({ input }) => {
if (IS_CLOUD) {
return [];
}
const docker = await getRemoteDocker(input.serverId); const docker = await getRemoteDocker(input.serverId);
const workers: DockerNode[] = await docker.listNodes(); const workers: DockerNode[] = await docker.listNodes();
@@ -33,17 +30,17 @@ export const clusterRouter = createTRPCRouter({
}), }),
) )
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
if (IS_CLOUD) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Functionality not available in cloud version",
});
}
try { try {
await execAsync( const drainCommand = `docker node update --availability drain ${input.nodeId}`;
`docker node update --availability drain ${input.nodeId}`, const removeCommand = `docker node rm ${input.nodeId} --force`;
);
await execAsync(`docker node rm ${input.nodeId} --force`); if (input.serverId) {
await execAsyncRemote(input.serverId, drainCommand);
await execAsyncRemote(input.serverId, removeCommand);
} else {
await execAsync(drainCommand);
await execAsync(removeCommand);
}
return true; return true;
} catch (error) { } catch (error) {
throw new TRPCError({ throw new TRPCError({
@@ -60,20 +57,20 @@ export const clusterRouter = createTRPCRouter({
}), }),
) )
.query(async ({ input }) => { .query(async ({ input }) => {
if (IS_CLOUD) {
return {
command: "",
version: "",
};
}
const docker = await getRemoteDocker(input.serverId); const docker = await getRemoteDocker(input.serverId);
const result = await docker.swarmInspect(); const result = await docker.swarmInspect();
const docker_version = await docker.version(); const docker_version = await docker.version();
let ip = await getPublicIpWithFallback();
if (input.serverId) {
const server = await findServerById(input.serverId);
ip = server?.ipAddress;
}
return { return {
command: `docker swarm join --token ${ command: `docker swarm join --token ${
result.JoinTokens.Worker result.JoinTokens.Worker
} ${await getPublicIpWithFallback()}:2377`, } ${ip}:2377`,
version: docker_version.Version, version: docker_version.Version,
}; };
}), }),
@@ -84,19 +81,19 @@ export const clusterRouter = createTRPCRouter({
}), }),
) )
.query(async ({ input }) => { .query(async ({ input }) => {
if (IS_CLOUD) {
return {
command: "",
version: "",
};
}
const docker = await getRemoteDocker(input.serverId); const docker = await getRemoteDocker(input.serverId);
const result = await docker.swarmInspect(); const result = await docker.swarmInspect();
const docker_version = await docker.version(); const docker_version = await docker.version();
let ip = await getPublicIpWithFallback();
if (input.serverId) {
const server = await findServerById(input.serverId);
ip = server?.ipAddress;
}
return { return {
command: `docker swarm join --token ${ command: `docker swarm join --token ${
result.JoinTokens.Manager result.JoinTokens.Manager
} ${await getPublicIpWithFallback()}:2377`, } ${ip}:2377`,
version: docker_version.Version, version: docker_version.Version,
}; };
}), }),

View File

@@ -1,5 +1,8 @@
import type { ConnectionOptions } from "bullmq"; import type { ConnectionOptions } from "bullmq";
export const redisConfig: ConnectionOptions = { export const redisConfig: ConnectionOptions = {
host: process.env.NODE_ENV === "production" ? "dokploy-redis" : "127.0.0.1", host:
process.env.NODE_ENV === "production"
? process.env.REDIS_HOST || "dokploy-redis"
: "127.0.0.1",
}; };

View File

@@ -7,9 +7,6 @@ import {
createDefaultTraefikConfig, createDefaultTraefikConfig,
initCronJobs, initCronJobs,
initializeNetwork, initializeNetwork,
initializePostgres,
initializeRedis,
initializeTraefik,
sendDokployRestartNotifications, sendDokployRestartNotifications,
setupDirectories, setupDirectories,
} from "@dokploy/server"; } from "@dokploy/server";
@@ -49,14 +46,7 @@ void app.prepare().then(async () => {
await initializeNetwork(); await initializeNetwork();
createDefaultTraefikConfig(); createDefaultTraefikConfig();
createDefaultServerTraefikConfig(); createDefaultServerTraefikConfig();
await initializePostgres();
await initializeTraefik();
await initializeRedis();
initCronJobs(); initCronJobs();
// Timeout to wait for the database to be ready
await new Promise((resolve) => setTimeout(resolve, 7000));
await migration(); await migration();
await sendDokployRestartNotifications(); await sendDokployRestartNotifications();
} }

View File

@@ -42,7 +42,6 @@
"drizzle-dbml-generator":"0.10.0", "drizzle-dbml-generator":"0.10.0",
"better-auth":"1.2.4", "better-auth":"1.2.4",
"@faker-js/faker": "^8.4.1", "@faker-js/faker": "^8.4.1",
"@lucia-auth/adapter-drizzle": "1.0.7",
"@octokit/auth-app": "^6.0.4", "@octokit/auth-app": "^6.0.4",
"@react-email/components": "^0.0.21", "@react-email/components": "^0.0.21",
"@trpc/server": "^10.43.6", "@trpc/server": "^10.43.6",
@@ -59,7 +58,6 @@
"hi-base32": "^0.5.1", "hi-base32": "^0.5.1",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"lodash": "4.17.21", "lodash": "4.17.21",
"lucia": "^3.0.1",
"nanoid": "3", "nanoid": "3",
"node-os-utils": "1.3.7", "node-os-utils": "1.3.7",
"node-pty": "1.0.0", "node-pty": "1.0.0",

View File

@@ -361,7 +361,7 @@ const installUtilities = () => `
alpine) alpine)
sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories
apk update >/dev/null apk update >/dev/null
apk add curl wget git jq openssl >/dev/null apk add curl wget git jq openssl sudo unzip tar >/dev/null
;; ;;
ubuntu | debian | raspbian) ubuntu | debian | raspbian)
DEBIAN_FRONTEND=noninteractive apt-get update -y >/dev/null DEBIAN_FRONTEND=noninteractive apt-get update -y >/dev/null

View File

@@ -80,7 +80,8 @@ export const initCronJobs = async () => {
console.log( console.log(
`PG-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`, `PG-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
); );
runPostgresBackup(pg, backup); await runPostgresBackup(pg, backup);
await keepLatestNBackups(backup, pg.serverId);
}); });
} }
} }
@@ -112,6 +113,7 @@ export const initCronJobs = async () => {
`MARIADB-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`, `MARIADB-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
); );
await runMariadbBackup(maria, backup); await runMariadbBackup(maria, backup);
await keepLatestNBackups(backup, maria.serverId);
}); });
} }
} }
@@ -141,6 +143,7 @@ export const initCronJobs = async () => {
`MONGO-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`, `MONGO-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
); );
await runMongoBackup(mongo, backup); await runMongoBackup(mongo, backup);
await keepLatestNBackups(backup, mongo.serverId);
}); });
} }
} }
@@ -170,6 +173,7 @@ export const initCronJobs = async () => {
`MYSQL-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`, `MYSQL-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
); );
await runMySqlBackup(mysql, backup); await runMySqlBackup(mysql, backup);
await keepLatestNBackups(backup, mysql.serverId);
}); });
} }
} }

502
pnpm-lock.yaml generated
View File

@@ -148,9 +148,6 @@ importers:
'@hookform/resolvers': '@hookform/resolvers':
specifier: ^3.9.0 specifier: ^3.9.0
version: 3.9.0(react-hook-form@7.52.1(react@18.2.0)) version: 3.9.0(react-hook-form@7.52.1(react@18.2.0))
'@lucia-auth/adapter-drizzle':
specifier: 1.0.7
version: 1.0.7(lucia@3.2.0)
'@octokit/auth-app': '@octokit/auth-app':
specifier: ^6.0.4 specifier: ^6.0.4
version: 6.1.1 version: 6.1.1
@@ -237,7 +234,7 @@ importers:
version: 10.45.2(@trpc/server@10.45.2) version: 10.45.2(@trpc/server@10.45.2)
'@trpc/next': '@trpc/next':
specifier: ^10.43.6 specifier: ^10.43.6
version: 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/server@10.45.2)(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/server@10.45.2)(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@trpc/react-query': '@trpc/react-query':
specifier: ^10.43.6 specifier: ^10.43.6
version: 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -328,9 +325,6 @@ importers:
lodash: lodash:
specifier: 4.17.21 specifier: 4.17.21
version: 4.17.21 version: 4.17.21
lucia:
specifier: ^3.0.1
version: 3.2.0
lucide-react: lucide-react:
specifier: ^0.469.0 specifier: ^0.469.0
version: 0.469.0(react@18.2.0) version: 0.469.0(react@18.2.0)
@@ -341,14 +335,14 @@ importers:
specifier: '3' specifier: '3'
version: 3.3.7 version: 3.3.7
next: next:
specifier: ^15.0.1 specifier: ^15.2.4
version: 15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
next-i18next: next-i18next:
specifier: ^15.3.1 specifier: ^15.3.1
version: 15.3.1(i18next@23.16.5)(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0) version: 15.3.1(i18next@23.16.5)(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)
next-themes: next-themes:
specifier: ^0.2.1 specifier: ^0.2.1
version: 0.2.1(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 0.2.1(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
node-os-utils: node-os-utils:
specifier: 1.3.7 specifier: 1.3.7
version: 1.3.7 version: 1.3.7
@@ -615,9 +609,6 @@ importers:
'@faker-js/faker': '@faker-js/faker':
specifier: ^8.4.1 specifier: ^8.4.1
version: 8.4.1 version: 8.4.1
'@lucia-auth/adapter-drizzle':
specifier: 1.0.7
version: 1.0.7(lucia@3.2.0)
'@octokit/auth-app': '@octokit/auth-app':
specifier: ^6.0.4 specifier: ^6.0.4
version: 6.1.1 version: 6.1.1
@@ -681,9 +672,6 @@ importers:
lodash: lodash:
specifier: 4.17.21 specifier: 4.17.21
version: 4.17.21 version: 4.17.21
lucia:
specifier: ^3.0.1
version: 3.2.0
micromatch: micromatch:
specifier: 4.0.8 specifier: 4.0.8
version: 4.0.8 version: 4.0.8
@@ -1107,12 +1095,6 @@ packages:
'@drizzle-team/brocli@0.10.2': '@drizzle-team/brocli@0.10.2':
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
'@emnapi/core@0.45.0':
resolution: {integrity: sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==}
'@emnapi/runtime@0.45.0':
resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==}
'@emnapi/runtime@1.3.1': '@emnapi/runtime@1.3.1':
resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==}
@@ -1901,11 +1883,6 @@ packages:
'@lezer/yaml@1.0.3': '@lezer/yaml@1.0.3':
resolution: {integrity: sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==} resolution: {integrity: sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==}
'@lucia-auth/adapter-drizzle@1.0.7':
resolution: {integrity: sha512-X/V7fLBca8EC/gPXCntwbQpb0+F9oEuRoHElvsi9rCrdnGhCMNxHgwAvgiQ6pes+rIYpyvx4n3hvjqo/fPo03A==}
peerDependencies:
lucia: 3.x
'@mapbox/node-pre-gyp@1.0.11': '@mapbox/node-pre-gyp@1.0.11':
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
hasBin: true hasBin: true
@@ -1945,53 +1922,53 @@ packages:
peerDependencies: peerDependencies:
redis: ^4.7.0 redis: ^4.7.0
'@next/env@15.0.1': '@next/env@15.2.4':
resolution: {integrity: sha512-lc4HeDUKO9gxxlM5G2knTRifqhsY6yYpwuHspBZdboZe0Gp+rZHBNNSIjmQKDJIdRXiXGyVnSD6gafrbQPvILQ==} resolution: {integrity: sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==}
'@next/swc-darwin-arm64@15.0.1': '@next/swc-darwin-arm64@15.2.4':
resolution: {integrity: sha512-C9k/Xv4sxkQRTA37Z6MzNq3Yb1BJMmSqjmwowoWEpbXTkAdfOwnoKOpAb71ItSzoA26yUTIo6ZhN8rKGu4ExQw==} resolution: {integrity: sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@next/swc-darwin-x64@15.0.1': '@next/swc-darwin-x64@15.2.4':
resolution: {integrity: sha512-uHl13HXOuq1G7ovWFxCACDJHTSDVbn/sbLv8V1p+7KIvTrYQ5HNoSmKBdYeEKRRCbEmd+OohOgg9YOp8Ux3MBg==} resolution: {integrity: sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@next/swc-linux-arm64-gnu@15.0.1': '@next/swc-linux-arm64-gnu@15.2.4':
resolution: {integrity: sha512-LvyhvxHOihFTEIbb35KxOc3q8w8G4xAAAH/AQnsYDEnOvwawjL2eawsB59AX02ki6LJdgDaHoTEnC54Gw+82xw==} resolution: {integrity: sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-arm64-musl@15.0.1': '@next/swc-linux-arm64-musl@15.2.4':
resolution: {integrity: sha512-vFmCGUFNyk/A5/BYcQNhAQqPIw01RJaK6dRO+ZEhz0DncoW+hJW1kZ8aH2UvTX27zPq3m85zN5waMSbZEmANcQ==} resolution: {integrity: sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-x64-gnu@15.0.1': '@next/swc-linux-x64-gnu@15.2.4':
resolution: {integrity: sha512-5by7IYq0NCF8rouz6Qg9T97jYU68kaClHPfGpQG2lCZpSYHtSPQF1kjnqBTd34RIqPKMbCa4DqCufirgr8HM5w==} resolution: {integrity: sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-linux-x64-musl@15.0.1': '@next/swc-linux-x64-musl@15.2.4':
resolution: {integrity: sha512-lmYr6H3JyDNBJLzklGXLfbehU3ay78a+b6UmBGlHls4xhDXBNZfgb0aI67sflrX+cGBnv1LgmWzFlYrAYxS1Qw==} resolution: {integrity: sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-win32-arm64-msvc@15.0.1': '@next/swc-win32-arm64-msvc@15.2.4':
resolution: {integrity: sha512-DS8wQtl6diAj0eZTdH0sefykm4iXMbHT4MOvLwqZiIkeezKpkgPFcEdFlz3vKvXa2R/2UEgMh48z1nEpNhjeOQ==} resolution: {integrity: sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@next/swc-win32-x64-msvc@15.0.1': '@next/swc-win32-x64-msvc@15.2.4':
resolution: {integrity: sha512-4Ho2ggvDdMKlZ/0e9HNdZ9ngeaBwtc+2VS5oCeqrbXqOgutX6I4U2X/42VBw0o+M5evn4/7v3zKgGHo+9v/VjA==} resolution: {integrity: sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@@ -2007,180 +1984,6 @@ packages:
resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==}
engines: {node: ^14.21.3 || >=16} engines: {node: ^14.21.3 || >=16}
'@node-rs/argon2-android-arm-eabi@1.7.0':
resolution: {integrity: sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==}
engines: {node: '>= 10'}
cpu: [arm]
os: [android]
'@node-rs/argon2-android-arm64@1.7.0':
resolution: {integrity: sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
'@node-rs/argon2-darwin-arm64@1.7.0':
resolution: {integrity: sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@node-rs/argon2-darwin-x64@1.7.0':
resolution: {integrity: sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@node-rs/argon2-freebsd-x64@1.7.0':
resolution: {integrity: sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
'@node-rs/argon2-linux-arm-gnueabihf@1.7.0':
resolution: {integrity: sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@node-rs/argon2-linux-arm64-gnu@1.7.0':
resolution: {integrity: sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@node-rs/argon2-linux-arm64-musl@1.7.0':
resolution: {integrity: sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@node-rs/argon2-linux-x64-gnu@1.7.0':
resolution: {integrity: sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@node-rs/argon2-linux-x64-musl@1.7.0':
resolution: {integrity: sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@node-rs/argon2-wasm32-wasi@1.7.0':
resolution: {integrity: sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@node-rs/argon2-win32-arm64-msvc@1.7.0':
resolution: {integrity: sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@node-rs/argon2-win32-ia32-msvc@1.7.0':
resolution: {integrity: sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
'@node-rs/argon2-win32-x64-msvc@1.7.0':
resolution: {integrity: sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@node-rs/argon2@1.7.0':
resolution: {integrity: sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog==}
engines: {node: '>= 10'}
'@node-rs/bcrypt-android-arm-eabi@1.9.0':
resolution: {integrity: sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==}
engines: {node: '>= 10'}
cpu: [arm]
os: [android]
'@node-rs/bcrypt-android-arm64@1.9.0':
resolution: {integrity: sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
'@node-rs/bcrypt-darwin-arm64@1.9.0':
resolution: {integrity: sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@node-rs/bcrypt-darwin-x64@1.9.0':
resolution: {integrity: sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@node-rs/bcrypt-freebsd-x64@1.9.0':
resolution: {integrity: sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
'@node-rs/bcrypt-linux-arm-gnueabihf@1.9.0':
resolution: {integrity: sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@node-rs/bcrypt-linux-arm64-gnu@1.9.0':
resolution: {integrity: sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@node-rs/bcrypt-linux-arm64-musl@1.9.0':
resolution: {integrity: sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@node-rs/bcrypt-linux-x64-gnu@1.9.0':
resolution: {integrity: sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@node-rs/bcrypt-linux-x64-musl@1.9.0':
resolution: {integrity: sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@node-rs/bcrypt-wasm32-wasi@1.9.0':
resolution: {integrity: sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@node-rs/bcrypt-win32-arm64-msvc@1.9.0':
resolution: {integrity: sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@node-rs/bcrypt-win32-ia32-msvc@1.9.0':
resolution: {integrity: sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
'@node-rs/bcrypt-win32-x64-msvc@1.9.0':
resolution: {integrity: sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@node-rs/bcrypt@1.9.0':
resolution: {integrity: sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig==}
engines: {node: '>= 10'}
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@@ -3513,8 +3316,8 @@ packages:
'@swc/counter@0.1.3': '@swc/counter@0.1.3':
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
'@swc/helpers@0.5.13': '@swc/helpers@0.5.15':
resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
'@szmarczak/http-timer@5.0.1': '@szmarczak/http-timer@5.0.1':
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
@@ -3595,9 +3398,6 @@ packages:
'@tsconfig/node16@1.0.4': '@tsconfig/node16@1.0.4':
resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
'@tybys/wasm-util@0.8.3':
resolution: {integrity: sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==}
'@types/adm-zip@0.5.5': '@types/adm-zip@0.5.5':
resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==} resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==}
@@ -4975,9 +4775,6 @@ packages:
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
fs-monkey@1.0.6:
resolution: {integrity: sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==}
fs.realpath@1.0.0: fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
@@ -5673,9 +5470,6 @@ packages:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'} engines: {node: '>=10'}
lucia@3.2.0:
resolution: {integrity: sha512-eXMxXwk6hqtjRTj4W/x3EnTUtAztLPm0p2N2TEBMDEbakDLXiYnDQ9z/qahjPdPdhPguQc+vwO0/88zIWxlpuw==}
lucide-react@0.469.0: lucide-react@0.469.0:
resolution: {integrity: sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==} resolution: {integrity: sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==}
peerDependencies: peerDependencies:
@@ -5737,13 +5531,6 @@ packages:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
memfs-browser@3.5.10302:
resolution: {integrity: sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==}
memfs@3.5.3:
resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==}
engines: {node: '>= 4.0.0'}
memfs@4.11.0: memfs@4.11.0:
resolution: {integrity: sha512-+6kz90/YQoZuHvg3rn1CGPMZfEMaU5xe7xIavZMNiom2RNesiI8S37p9O9n+PlIUnUgretjLdM6HnqpZYl3X2g==} resolution: {integrity: sha512-+6kz90/YQoZuHvg3rn1CGPMZfEMaU5xe7xIavZMNiom2RNesiI8S37p9O9n+PlIUnUgretjLdM6HnqpZYl3X2g==}
engines: {node: '>= 4.0.0'} engines: {node: '>= 4.0.0'}
@@ -6004,16 +5791,16 @@ packages:
react: '*' react: '*'
react-dom: '*' react-dom: '*'
next@15.0.1: next@15.2.4:
resolution: {integrity: sha512-PSkFkr/w7UnFWm+EP8y/QpHrJXMqpZzAXpergB/EqLPOh4SGPJXv1wj4mslr2hUZBAS9pX7/9YLIdxTv6fwytw==} resolution: {integrity: sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==}
engines: {node: '>=18.18.0'} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
'@opentelemetry/api': ^1.1.0 '@opentelemetry/api': ^1.1.0
'@playwright/test': ^1.41.2 '@playwright/test': ^1.41.2
babel-plugin-react-compiler: '*' babel-plugin-react-compiler: '*'
react: ^18.2.0 || 19.0.0-rc-69d4b800-20241021 react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
react-dom: ^18.2.0 || 19.0.0-rc-69d4b800-20241021 react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
sass: ^1.3.0 sass: ^1.3.0
peerDependenciesMeta: peerDependenciesMeta:
'@opentelemetry/api': '@opentelemetry/api':
@@ -6177,10 +5964,6 @@ packages:
openapi-types@12.1.3: openapi-types@12.1.3:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
oslo@1.2.0:
resolution: {integrity: sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==}
deprecated: Package is no longer supported. Please see https://oslojs.dev for the successor project.
otpauth@9.3.4: otpauth@9.3.4:
resolution: {integrity: sha512-qXv+lpsCUO9ewitLYfeDKbLYt7UUCivnU/fwGK2OqhgrCBsRkTUNKWsgKAhkXG3aistOY+jEeuL90JEBu6W3mQ==} resolution: {integrity: sha512-qXv+lpsCUO9ewitLYfeDKbLYt7UUCivnU/fwGK2OqhgrCBsRkTUNKWsgKAhkXG3aistOY+jEeuL90JEBu6W3mQ==}
@@ -8017,16 +7800,6 @@ snapshots:
'@drizzle-team/brocli@0.10.2': {} '@drizzle-team/brocli@0.10.2': {}
'@emnapi/core@0.45.0':
dependencies:
tslib: 2.8.1
optional: true
'@emnapi/runtime@0.45.0':
dependencies:
tslib: 2.8.1
optional: true
'@emnapi/runtime@1.3.1': '@emnapi/runtime@1.3.1':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@@ -8521,10 +8294,6 @@ snapshots:
'@lezer/highlight': 1.2.0 '@lezer/highlight': 1.2.0
'@lezer/lr': 1.4.2 '@lezer/lr': 1.4.2
'@lucia-auth/adapter-drizzle@1.0.7(lucia@3.2.0)':
dependencies:
lucia: 3.2.0
'@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)': '@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)':
dependencies: dependencies:
detect-libc: 2.0.3 detect-libc: 2.0.3
@@ -8563,30 +8332,30 @@ snapshots:
async-await-queue: 2.1.4 async-await-queue: 2.1.4
redis: 4.7.0 redis: 4.7.0
'@next/env@15.0.1': {} '@next/env@15.2.4': {}
'@next/swc-darwin-arm64@15.0.1': '@next/swc-darwin-arm64@15.2.4':
optional: true optional: true
'@next/swc-darwin-x64@15.0.1': '@next/swc-darwin-x64@15.2.4':
optional: true optional: true
'@next/swc-linux-arm64-gnu@15.0.1': '@next/swc-linux-arm64-gnu@15.2.4':
optional: true optional: true
'@next/swc-linux-arm64-musl@15.0.1': '@next/swc-linux-arm64-musl@15.2.4':
optional: true optional: true
'@next/swc-linux-x64-gnu@15.0.1': '@next/swc-linux-x64-gnu@15.2.4':
optional: true optional: true
'@next/swc-linux-x64-musl@15.0.1': '@next/swc-linux-x64-musl@15.2.4':
optional: true optional: true
'@next/swc-win32-arm64-msvc@15.0.1': '@next/swc-win32-arm64-msvc@15.2.4':
optional: true optional: true
'@next/swc-win32-x64-msvc@15.0.1': '@next/swc-win32-x64-msvc@15.2.4':
optional: true optional: true
'@noble/ciphers@0.6.0': {} '@noble/ciphers@0.6.0': {}
@@ -8595,134 +8364,6 @@ snapshots:
'@noble/hashes@1.7.1': {} '@noble/hashes@1.7.1': {}
'@node-rs/argon2-android-arm-eabi@1.7.0':
optional: true
'@node-rs/argon2-android-arm64@1.7.0':
optional: true
'@node-rs/argon2-darwin-arm64@1.7.0':
optional: true
'@node-rs/argon2-darwin-x64@1.7.0':
optional: true
'@node-rs/argon2-freebsd-x64@1.7.0':
optional: true
'@node-rs/argon2-linux-arm-gnueabihf@1.7.0':
optional: true
'@node-rs/argon2-linux-arm64-gnu@1.7.0':
optional: true
'@node-rs/argon2-linux-arm64-musl@1.7.0':
optional: true
'@node-rs/argon2-linux-x64-gnu@1.7.0':
optional: true
'@node-rs/argon2-linux-x64-musl@1.7.0':
optional: true
'@node-rs/argon2-wasm32-wasi@1.7.0':
dependencies:
'@emnapi/core': 0.45.0
'@emnapi/runtime': 0.45.0
'@tybys/wasm-util': 0.8.3
memfs-browser: 3.5.10302
optional: true
'@node-rs/argon2-win32-arm64-msvc@1.7.0':
optional: true
'@node-rs/argon2-win32-ia32-msvc@1.7.0':
optional: true
'@node-rs/argon2-win32-x64-msvc@1.7.0':
optional: true
'@node-rs/argon2@1.7.0':
optionalDependencies:
'@node-rs/argon2-android-arm-eabi': 1.7.0
'@node-rs/argon2-android-arm64': 1.7.0
'@node-rs/argon2-darwin-arm64': 1.7.0
'@node-rs/argon2-darwin-x64': 1.7.0
'@node-rs/argon2-freebsd-x64': 1.7.0
'@node-rs/argon2-linux-arm-gnueabihf': 1.7.0
'@node-rs/argon2-linux-arm64-gnu': 1.7.0
'@node-rs/argon2-linux-arm64-musl': 1.7.0
'@node-rs/argon2-linux-x64-gnu': 1.7.0
'@node-rs/argon2-linux-x64-musl': 1.7.0
'@node-rs/argon2-wasm32-wasi': 1.7.0
'@node-rs/argon2-win32-arm64-msvc': 1.7.0
'@node-rs/argon2-win32-ia32-msvc': 1.7.0
'@node-rs/argon2-win32-x64-msvc': 1.7.0
'@node-rs/bcrypt-android-arm-eabi@1.9.0':
optional: true
'@node-rs/bcrypt-android-arm64@1.9.0':
optional: true
'@node-rs/bcrypt-darwin-arm64@1.9.0':
optional: true
'@node-rs/bcrypt-darwin-x64@1.9.0':
optional: true
'@node-rs/bcrypt-freebsd-x64@1.9.0':
optional: true
'@node-rs/bcrypt-linux-arm-gnueabihf@1.9.0':
optional: true
'@node-rs/bcrypt-linux-arm64-gnu@1.9.0':
optional: true
'@node-rs/bcrypt-linux-arm64-musl@1.9.0':
optional: true
'@node-rs/bcrypt-linux-x64-gnu@1.9.0':
optional: true
'@node-rs/bcrypt-linux-x64-musl@1.9.0':
optional: true
'@node-rs/bcrypt-wasm32-wasi@1.9.0':
dependencies:
'@emnapi/core': 0.45.0
'@emnapi/runtime': 0.45.0
'@tybys/wasm-util': 0.8.3
memfs-browser: 3.5.10302
optional: true
'@node-rs/bcrypt-win32-arm64-msvc@1.9.0':
optional: true
'@node-rs/bcrypt-win32-ia32-msvc@1.9.0':
optional: true
'@node-rs/bcrypt-win32-x64-msvc@1.9.0':
optional: true
'@node-rs/bcrypt@1.9.0':
optionalDependencies:
'@node-rs/bcrypt-android-arm-eabi': 1.9.0
'@node-rs/bcrypt-android-arm64': 1.9.0
'@node-rs/bcrypt-darwin-arm64': 1.9.0
'@node-rs/bcrypt-darwin-x64': 1.9.0
'@node-rs/bcrypt-freebsd-x64': 1.9.0
'@node-rs/bcrypt-linux-arm-gnueabihf': 1.9.0
'@node-rs/bcrypt-linux-arm64-gnu': 1.9.0
'@node-rs/bcrypt-linux-arm64-musl': 1.9.0
'@node-rs/bcrypt-linux-x64-gnu': 1.9.0
'@node-rs/bcrypt-linux-x64-musl': 1.9.0
'@node-rs/bcrypt-wasm32-wasi': 1.9.0
'@node-rs/bcrypt-win32-arm64-msvc': 1.9.0
'@node-rs/bcrypt-win32-ia32-msvc': 1.9.0
'@node-rs/bcrypt-win32-x64-msvc': 1.9.0
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
dependencies: dependencies:
'@nodelib/fs.stat': 2.0.5 '@nodelib/fs.stat': 2.0.5
@@ -10348,7 +9989,7 @@ snapshots:
'@swc/counter@0.1.3': {} '@swc/counter@0.1.3': {}
'@swc/helpers@0.5.13': '@swc/helpers@0.5.15':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@@ -10389,13 +10030,13 @@ snapshots:
dependencies: dependencies:
'@trpc/server': 10.45.2 '@trpc/server': 10.45.2
'@trpc/next@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/server@10.45.2)(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': '@trpc/next@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/react-query@10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/server@10.45.2)(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies: dependencies:
'@tanstack/react-query': 4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@tanstack/react-query': 4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@trpc/client': 10.45.2(@trpc/server@10.45.2) '@trpc/client': 10.45.2(@trpc/server@10.45.2)
'@trpc/react-query': 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@trpc/react-query': 10.45.2(@tanstack/react-query@4.36.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(@trpc/client@10.45.2(@trpc/server@10.45.2))(@trpc/server@10.45.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@trpc/server': 10.45.2 '@trpc/server': 10.45.2
next: 15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
@@ -10421,11 +10062,6 @@ snapshots:
'@tsconfig/node16@1.0.4': '@tsconfig/node16@1.0.4':
optional: true optional: true
'@tybys/wasm-util@0.8.3':
dependencies:
tslib: 2.8.1
optional: true
'@types/adm-zip@0.5.5': '@types/adm-zip@0.5.5':
dependencies: dependencies:
'@types/node': 20.14.10 '@types/node': 20.14.10
@@ -11844,9 +11480,6 @@ snapshots:
dependencies: dependencies:
minipass: 3.3.6 minipass: 3.3.6
fs-monkey@1.0.6:
optional: true
fs.realpath@1.0.0: {} fs.realpath@1.0.0: {}
fsevents@2.3.3: fsevents@2.3.3:
@@ -12616,10 +12249,6 @@ snapshots:
yallist: 4.0.0 yallist: 4.0.0
optional: true optional: true
lucia@3.2.0:
dependencies:
oslo: 1.2.0
lucide-react@0.469.0(react@18.2.0): lucide-react@0.469.0(react@18.2.0):
dependencies: dependencies:
react: 18.2.0 react: 18.2.0
@@ -12758,16 +12387,6 @@ snapshots:
media-typer@0.3.0: {} media-typer@0.3.0: {}
memfs-browser@3.5.10302:
dependencies:
memfs: 3.5.3
optional: true
memfs@3.5.3:
dependencies:
fs-monkey: 1.0.6
optional: true
memfs@4.11.0: memfs@4.11.0:
dependencies: dependencies:
'@jsonjoy.com/json-pack': 1.0.4(tslib@2.6.3) '@jsonjoy.com/json-pack': 1.0.4(tslib@2.6.3)
@@ -13064,7 +12683,7 @@ snapshots:
neotraverse@0.6.18: {} neotraverse@0.6.18: {}
next-i18next@15.3.1(i18next@23.16.5)(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0): next-i18next@15.3.1(i18next@23.16.5)(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0):
dependencies: dependencies:
'@babel/runtime': 7.25.0 '@babel/runtime': 7.25.0
'@types/hoist-non-react-statics': 3.3.5 '@types/hoist-non-react-statics': 3.3.5
@@ -13072,21 +12691,21 @@ snapshots:
hoist-non-react-statics: 3.3.2 hoist-non-react-statics: 3.3.2
i18next: 23.16.5 i18next: 23.16.5
i18next-fs-backend: 2.3.2 i18next-fs-backend: 2.3.2
next: 15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react: 18.2.0 react: 18.2.0
react-i18next: 15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-i18next: 15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
next-themes@0.2.1(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0): next-themes@0.2.1(next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies: dependencies:
next: 15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) next: 15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): next@15.2.4(@opentelemetry/api@1.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies: dependencies:
'@next/env': 15.0.1 '@next/env': 15.2.4
'@swc/counter': 0.1.3 '@swc/counter': 0.1.3
'@swc/helpers': 0.5.13 '@swc/helpers': 0.5.15
busboy: 1.6.0 busboy: 1.6.0
caniuse-lite: 1.0.30001643 caniuse-lite: 1.0.30001643
postcss: 8.4.31 postcss: 8.4.31
@@ -13094,14 +12713,14 @@ snapshots:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.6(react@18.2.0) styled-jsx: 5.1.6(react@18.2.0)
optionalDependencies: optionalDependencies:
'@next/swc-darwin-arm64': 15.0.1 '@next/swc-darwin-arm64': 15.2.4
'@next/swc-darwin-x64': 15.0.1 '@next/swc-darwin-x64': 15.2.4
'@next/swc-linux-arm64-gnu': 15.0.1 '@next/swc-linux-arm64-gnu': 15.2.4
'@next/swc-linux-arm64-musl': 15.0.1 '@next/swc-linux-arm64-musl': 15.2.4
'@next/swc-linux-x64-gnu': 15.0.1 '@next/swc-linux-x64-gnu': 15.2.4
'@next/swc-linux-x64-musl': 15.0.1 '@next/swc-linux-x64-musl': 15.2.4
'@next/swc-win32-arm64-msvc': 15.0.1 '@next/swc-win32-arm64-msvc': 15.2.4
'@next/swc-win32-x64-msvc': 15.0.1 '@next/swc-win32-x64-msvc': 15.2.4
'@opentelemetry/api': 1.9.0 '@opentelemetry/api': 1.9.0
sharp: 0.33.5 sharp: 0.33.5
transitivePeerDependencies: transitivePeerDependencies:
@@ -13274,11 +12893,6 @@ snapshots:
openapi-types@12.1.3: {} openapi-types@12.1.3: {}
oslo@1.2.0:
dependencies:
'@node-rs/argon2': 1.7.0
'@node-rs/bcrypt': 1.9.0
otpauth@9.3.4: otpauth@9.3.4:
dependencies: dependencies:
'@noble/hashes': 1.5.0 '@noble/hashes': 1.5.0
@@ -13477,7 +13091,7 @@ snapshots:
postcss@8.4.31: postcss@8.4.31:
dependencies: dependencies:
nanoid: 3.3.7 nanoid: 3.3.8
picocolors: 1.0.1 picocolors: 1.0.1
source-map-js: 1.2.0 source-map-js: 1.2.0