Merge branch 'canary' into feature/stop-grace-period-2227

This commit is contained in:
Mauricio Siu
2025-10-04 23:45:59 -06:00
22 changed files with 7036 additions and 269 deletions

View File

@@ -7,7 +7,7 @@ import {
RefreshCw, RefreshCw,
} from "lucide-react"; } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { type Control, useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block"; import { AlertBlock } from "@/components/shared/alert-block";
@@ -57,6 +57,7 @@ export const commonCronExpressions = [
{ label: "Every month on the 1st at midnight", value: "0 0 1 * *" }, { label: "Every month on the 1st at midnight", value: "0 0 1 * *" },
{ label: "Every 15 minutes", value: "*/15 * * * *" }, { label: "Every 15 minutes", value: "*/15 * * * *" },
{ label: "Every weekday at midnight", value: "0 0 * * 1-5" }, { label: "Every weekday at midnight", value: "0 0 * * 1-5" },
{ label: "Custom", value: "custom" },
]; ];
const formSchema = z const formSchema = z
@@ -115,10 +116,91 @@ interface Props {
scheduleType?: "application" | "compose" | "server" | "dokploy-server"; scheduleType?: "application" | "compose" | "server" | "dokploy-server";
} }
export const ScheduleFormField = ({
name,
formControl,
}: {
name: string;
formControl: Control<any>;
}) => {
const [selectedOption, setSelectedOption] = useState("");
return (
<FormField
control={formControl}
name={name}
render={({ field }) => (
<FormItem>
<FormLabel className="flex items-center gap-2">
Schedule
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Info className="w-4 h-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>Cron expression format: minute hour day month weekday</p>
<p>Example: 0 0 * * * (daily at midnight)</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</FormLabel>
<div className="flex flex-col gap-2">
<Select
value={selectedOption}
onValueChange={(value) => {
setSelectedOption(value);
field.onChange(value === "custom" ? "" : value);
}}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a predefined schedule" />
</SelectTrigger>
</FormControl>
<SelectContent>
{commonCronExpressions.map((expr) => (
<SelectItem key={expr.value} value={expr.value}>
{expr.label}
{expr.value !== "custom" && ` (${expr.value})`}
</SelectItem>
))}
</SelectContent>
</Select>
<div className="relative">
<FormControl>
<Input
placeholder="Custom cron expression (e.g., 0 0 * * *)"
{...field}
onChange={(e) => {
const value = e.target.value;
const commonExpression = commonCronExpressions.find(
(expression) => expression.value === value,
);
if (commonExpression) {
setSelectedOption(commonExpression.value);
} else {
setSelectedOption("custom");
}
field.onChange(e);
}}
/>
</FormControl>
</div>
</div>
<FormDescription>
Choose a predefined schedule or enter a custom cron expression
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
);
};
export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => { export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [cacheType, setCacheType] = useState<CacheType>("cache"); const [cacheType, setCacheType] = useState<CacheType>("cache");
const utils = api.useUtils(); const utils = api.useUtils();
const form = useForm<z.infer<typeof formSchema>>({ const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
@@ -377,63 +459,9 @@ export const HandleSchedules = ({ id, scheduleId, scheduleType }: Props) => {
)} )}
/> />
<FormField <ScheduleFormField
control={form.control}
name="cronExpression" name="cronExpression"
render={({ field }) => ( formControl={form.control}
<FormItem>
<FormLabel className="flex items-center gap-2">
Schedule
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Info className="w-4 h-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>
Cron expression format: minute hour day month
weekday
</p>
<p>Example: 0 0 * * * (daily at midnight)</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</FormLabel>
<div className="flex flex-col gap-2">
<Select
onValueChange={(value) => {
field.onChange(value);
}}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a predefined schedule" />
</SelectTrigger>
</FormControl>
<SelectContent>
{commonCronExpressions.map((expr) => (
<SelectItem key={expr.value} value={expr.value}>
{expr.label} ({expr.value})
</SelectItem>
))}
</SelectContent>
</Select>
<div className="relative">
<FormControl>
<Input
placeholder="Custom cron expression (e.g., 0 0 * * *)"
{...field}
/>
</FormControl>
</div>
</div>
<FormDescription>
Choose a predefined schedule or enter a custom cron
expression
</FormDescription>
<FormMessage />
</FormItem>
)}
/> />
{(scheduleTypeForm === "application" || {(scheduleTypeForm === "application" ||

View File

@@ -1,11 +1,5 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { import { DatabaseZap, PenBoxIcon, PlusCircle, RefreshCw } from "lucide-react";
DatabaseZap,
Info,
PenBoxIcon,
PlusCircle,
RefreshCw,
} from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
@@ -47,7 +41,7 @@ import {
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import type { CacheType } from "../domains/handle-domain"; import type { CacheType } from "../domains/handle-domain";
import { commonCronExpressions } from "../schedules/handle-schedules"; import { ScheduleFormField } from "../schedules/handle-schedules";
const formSchema = z const formSchema = z
.object({ .object({
@@ -306,64 +300,9 @@ export const HandleVolumeBackups = ({
</FormItem> </FormItem>
)} )}
/> />
<ScheduleFormField
<FormField
control={form.control}
name="cronExpression" name="cronExpression"
render={({ field }) => ( formControl={form.control}
<FormItem>
<FormLabel className="flex items-center gap-2">
Schedule
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Info className="w-4 h-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>
Cron expression format: minute hour day month
weekday
</p>
<p>Example: 0 0 * * * (daily at midnight)</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</FormLabel>
<div className="flex flex-col gap-2">
<Select
onValueChange={(value) => {
field.onChange(value);
}}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a predefined schedule" />
</SelectTrigger>
</FormControl>
<SelectContent>
{commonCronExpressions.map((expr) => (
<SelectItem key={expr.value} value={expr.value}>
{expr.label} ({expr.value})
</SelectItem>
))}
</SelectContent>
</Select>
<div className="relative">
<FormControl>
<Input
placeholder="Custom cron expression (e.g., 0 0 * * *)"
{...field}
/>
</FormControl>
</div>
</div>
<FormDescription>
Choose a predefined schedule or enter a custom cron
expression
</FormDescription>
<FormMessage />
</FormItem>
)}
/> />
<FormField <FormField

View File

@@ -1,5 +1,5 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect } from "react"; import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
@@ -35,6 +35,7 @@ export const ComposeFileEditor = ({ composeId }: Props) => {
); );
const { mutateAsync, isLoading } = api.compose.update.useMutation(); const { mutateAsync, isLoading } = api.compose.update.useMutation();
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
const form = useForm<AddComposeFile>({ const form = useForm<AddComposeFile>({
defaultValues: { defaultValues: {
@@ -53,6 +54,12 @@ export const ComposeFileEditor = ({ composeId }: Props) => {
} }
}, [form, form.reset, data]); }, [form, form.reset, data]);
useEffect(() => {
if (data?.composeFile !== undefined) {
setHasUnsavedChanges(composeFile !== data.composeFile);
}
}, [composeFile, data?.composeFile]);
const onSubmit = async (data: AddComposeFile) => { const onSubmit = async (data: AddComposeFile) => {
const { valid, error } = validateAndFormatYAML(data.composeFile); const { valid, error } = validateAndFormatYAML(data.composeFile);
if (!valid) { if (!valid) {
@@ -71,6 +78,7 @@ export const ComposeFileEditor = ({ composeId }: Props) => {
}) })
.then(async () => { .then(async () => {
toast.success("Compose config Updated"); toast.success("Compose config Updated");
setHasUnsavedChanges(false);
refetch(); refetch();
await utils.compose.getConvertedCompose.invalidate({ await utils.compose.getConvertedCompose.invalidate({
composeId, composeId,
@@ -99,6 +107,19 @@ export const ComposeFileEditor = ({ composeId }: Props) => {
return ( return (
<> <>
<div className="w-full flex flex-col gap-4 "> <div className="w-full flex flex-col gap-4 ">
<div className="flex items-center justify-between">
<div>
<h3 className="text-lg font-medium">Compose File</h3>
<p className="text-sm text-muted-foreground">
Configure your Docker Compose file for this service.
{hasUnsavedChanges && (
<span className="text-yellow-500 ml-2">
(You have unsaved changes)
</span>
)}
</p>
</div>
</div>
<Form {...form}> <Form {...form}>
<form <form
id="hook-form-save-compose-file" id="hook-form-save-compose-file"

View File

@@ -3,7 +3,6 @@ import {
CheckIcon, CheckIcon,
ChevronsUpDown, ChevronsUpDown,
DatabaseZap, DatabaseZap,
Info,
PenBoxIcon, PenBoxIcon,
PlusIcon, PlusIcon,
RefreshCw, RefreshCw,
@@ -62,7 +61,7 @@ import {
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { commonCronExpressions } from "../../application/schedules/handle-schedules"; import { ScheduleFormField } from "../../application/schedules/handle-schedules";
type CacheType = "cache" | "fetch"; type CacheType = "cache" | "fetch";
@@ -579,66 +578,9 @@ export const HandleBackup = ({
); );
}} }}
/> />
<FormField
control={form.control} <ScheduleFormField name="schedule" formControl={form.control} />
name="schedule"
render={({ field }) => {
return (
<FormItem>
<FormLabel className="flex items-center gap-2">
Schedule
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Info className="w-4 h-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>
Cron expression format: minute hour day month
weekday
</p>
<p>Example: 0 0 * * * (daily at midnight)</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</FormLabel>
<div className="flex flex-col gap-2">
<Select
onValueChange={(value) => {
field.onChange(value);
}}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a predefined schedule" />
</SelectTrigger>
</FormControl>
<SelectContent>
{commonCronExpressions.map((expr) => (
<SelectItem key={expr.value} value={expr.value}>
{expr.label} ({expr.value})
</SelectItem>
))}
</SelectContent>
</Select>
<div className="relative">
<FormControl>
<Input
placeholder="Custom cron expression (e.g., 0 0 * * *)"
{...field}
/>
</FormControl>
</div>
</div>
<FormDescription>
Choose a predefined schedule or enter a custom cron
expression
</FormDescription>
<FormMessage />
</FormItem>
);
}}
/>
<FormField <FormField
control={form.control} control={form.control}
name="prefix" name="prefix"

View File

@@ -96,8 +96,30 @@ export const ShowProjects = () => {
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
break; break;
case "services": { case "services": {
const aTotalServices = a.environments.length; const aTotalServices = a.environments.reduce((total, env) => {
const bTotalServices = b.environments.length; return (
total +
(env.applications?.length || 0) +
(env.mariadb?.length || 0) +
(env.mongo?.length || 0) +
(env.mysql?.length || 0) +
(env.postgres?.length || 0) +
(env.redis?.length || 0) +
(env.compose?.length || 0)
);
}, 0);
const bTotalServices = b.environments.reduce((total, env) => {
return (
total +
(env.applications?.length || 0) +
(env.mariadb?.length || 0) +
(env.mongo?.length || 0) +
(env.mysql?.length || 0) +
(env.postgres?.length || 0) +
(env.redis?.length || 0) +
(env.compose?.length || 0)
);
}, 0);
comparison = aTotalServices - bTotalServices; comparison = aTotalServices - bTotalServices;
break; break;
} }

View File

@@ -5,6 +5,7 @@ import { useEffect } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Card, Card,
@@ -76,6 +77,9 @@ export const WebDomain = () => {
resolver: zodResolver(addServerDomain), resolver: zodResolver(addServerDomain),
}); });
const https = form.watch("https"); const https = form.watch("https");
const domain = form.watch("domain") || "";
const host = data?.user?.host || "";
const hasChanged = domain !== host;
useEffect(() => { useEffect(() => {
if (data) { if (data) {
form.reset({ form.reset({
@@ -119,6 +123,19 @@ export const WebDomain = () => {
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="space-y-2 py-6 border-t"> <CardContent className="space-y-2 py-6 border-t">
{/* Warning for GitHub webhook URL changes */}
{hasChanged && (
<AlertBlock type="warning">
<div className="space-y-2">
<p className="font-medium"> Important: URL Change Impact</p>
<p>
If you change the Dokploy Server URL make sure to update
your Github Apps to keep the auto-deploy working and preview
deployments working.
</p>
</div>
</AlertBlock>
)}
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(onSubmit)} onSubmit={form.handleSubmit(onSubmit)}

View File

@@ -7,7 +7,14 @@ import {
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
interface Props { interface Props {
status: "running" | "error" | "done" | "idle" | undefined | null; status:
| "running"
| "error"
| "done"
| "idle"
| "cancelled"
| undefined
| null;
className?: string; className?: string;
} }
@@ -34,6 +41,14 @@ export const StatusTooltip = ({ status, className }: Props) => {
className={cn("size-3.5 rounded-full bg-green-500", className)} className={cn("size-3.5 rounded-full bg-green-500", className)}
/> />
)} )}
{status === "cancelled" && (
<div
className={cn(
"size-3.5 rounded-full bg-muted-foreground",
className,
)}
/>
)}
{status === "running" && ( {status === "running" && (
<div <div
className={cn("size-3.5 rounded-full bg-yellow-500", className)} className={cn("size-3.5 rounded-full bg-yellow-500", className)}
@@ -46,6 +61,7 @@ export const StatusTooltip = ({ status, className }: Props) => {
{status === "error" && "Error"} {status === "error" && "Error"}
{status === "done" && "Done"} {status === "done" && "Done"}
{status === "running" && "Running"} {status === "running" && "Running"}
{status === "cancelled" && "Cancelled"}
</span> </span>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>

View File

@@ -0,0 +1 @@
ALTER TYPE "public"."deploymentStatus" ADD VALUE 'cancelled';

File diff suppressed because it is too large Load Diff

View File

@@ -792,6 +792,13 @@
"when": 1758483520214, "when": 1758483520214,
"tag": "0112_freezing_skrulls", "tag": "0112_freezing_skrulls",
"breakpoints": true "breakpoints": true
},
{
"idx": 113,
"version": "7",
"when": 1758960816504,
"tag": "0113_complete_rafael_vega",
"breakpoints": true
} }
] ]
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "dokploy", "name": "dokploy",
"version": "v0.25.3", "version": "v0.25.4",
"private": true, "private": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"type": "module", "type": "module",

View File

@@ -226,6 +226,7 @@ const Service = (
<TabsTrigger value="general">General</TabsTrigger> <TabsTrigger value="general">General</TabsTrigger>
<TabsTrigger value="environment">Environment</TabsTrigger> <TabsTrigger value="environment">Environment</TabsTrigger>
<TabsTrigger value="domains">Domains</TabsTrigger> <TabsTrigger value="domains">Domains</TabsTrigger>
<TabsTrigger value="deployments">Deployments</TabsTrigger>
<TabsTrigger value="preview-deployments"> <TabsTrigger value="preview-deployments">
Preview Deployments Preview Deployments
</TabsTrigger> </TabsTrigger>
@@ -233,7 +234,6 @@ const Service = (
<TabsTrigger value="volume-backups"> <TabsTrigger value="volume-backups">
Volume Backups Volume Backups
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="deployments">Deployments</TabsTrigger>
<TabsTrigger value="logs">Logs</TabsTrigger> <TabsTrigger value="logs">Logs</TabsTrigger>
{((data?.serverId && isCloud) || !data?.server) && ( {((data?.serverId && isCloud) || !data?.server) && (
<TabsTrigger value="monitoring">Monitoring</TabsTrigger> <TabsTrigger value="monitoring">Monitoring</TabsTrigger>

View File

@@ -8,6 +8,7 @@ import {
initializeNetwork, initializeNetwork,
initSchedules, initSchedules,
initVolumeBackupsCronJobs, initVolumeBackupsCronJobs,
initCancelDeployments,
sendDokployRestartNotifications, sendDokployRestartNotifications,
setupDirectories, setupDirectories,
} from "@dokploy/server"; } from "@dokploy/server";
@@ -52,6 +53,7 @@ void app.prepare().then(async () => {
await migration(); await migration();
await initCronJobs(); await initCronJobs();
await initSchedules(); await initSchedules();
await initCancelDeployments();
await initVolumeBackupsCronJobs(); await initVolumeBackupsCronJobs();
await sendDokployRestartNotifications(); await sendDokployRestartNotifications();
} }

View File

@@ -21,6 +21,7 @@ export const deploymentStatus = pgEnum("deploymentStatus", [
"running", "running",
"done", "done",
"error", "error",
"cancelled",
]); ]);
export const deployments = pgTable("deployment", { export const deployments = pgTable("deployment", {

View File

@@ -68,6 +68,7 @@ export * from "./utils/backups/postgres";
export * from "./utils/backups/utils"; export * from "./utils/backups/utils";
export * from "./utils/backups/web-server"; export * from "./utils/backups/web-server";
export * from "./utils/builders/compose"; export * from "./utils/builders/compose";
export * from "./utils/startup/cancell-deployments";
export * from "./utils/builders/docker-file"; export * from "./utils/builders/docker-file";
export * from "./utils/builders/drop"; export * from "./utils/builders/drop";
export * from "./utils/builders/heroku"; export * from "./utils/builders/heroku";

View File

@@ -603,6 +603,21 @@ const BUNNY_CDN_IPS = new Set([
"89.187.184.176", "89.187.184.176",
]); ]);
// Arvancloud IP ranges
// https://www.arvancloud.ir/fa/ips.txt
const ARVANCLOUD_IP_RANGES = [
"185.143.232.0/22",
"188.229.116.16/29",
"94.101.182.0/27",
"2.144.3.128/28",
"89.45.48.64/28",
"37.32.16.0/27",
"37.32.17.0/27",
"37.32.18.0/27",
"37.32.19.0/27",
"185.215.232.0/22",
];
const CDN_PROVIDERS: CDNProvider[] = [ const CDN_PROVIDERS: CDNProvider[] = [
{ {
name: "cloudflare", name: "cloudflare",
@@ -627,6 +642,14 @@ const CDN_PROVIDERS: CDNProvider[] = [
warningMessage: warningMessage:
"Domain is behind Fastly - actual IP is masked by CDN proxy", "Domain is behind Fastly - actual IP is masked by CDN proxy",
}, },
{
name: "arvancloud",
displayName: "Arvancloud",
checkIp: (ip: string) =>
ARVANCLOUD_IP_RANGES.some((range) => isIPInCIDR(ip, range)),
warningMessage:
"Domain is behind Arvancloud - actual IP is masked by CDN proxy",
},
]; ];
export const detectCDNProvider = (ip: string): CDNProvider | null => { export const detectCDNProvider = (ip: string): CDNProvider | null => {

View File

@@ -227,7 +227,7 @@ export const deployCompose = async ({
const buildLink = `${await getDokployUrl()}/dashboard/project/${ const buildLink = `${await getDokployUrl()}/dashboard/project/${
compose.environment.projectId compose.environment.projectId
}/services/compose/${compose.composeId}?tab=deployments`; }/environment/${compose.environmentId}/services/compose/${compose.composeId}?tab=deployments`;
const deployment = await createDeploymentCompose({ const deployment = await createDeploymentCompose({
composeId: composeId, composeId: composeId,
title: titleLog, title: titleLog,
@@ -335,7 +335,7 @@ export const deployRemoteCompose = async ({
const buildLink = `${await getDokployUrl()}/dashboard/project/${ const buildLink = `${await getDokployUrl()}/dashboard/project/${
compose.environment.projectId compose.environment.projectId
}/services/compose/${compose.composeId}?tab=deployments`; }/environment/${compose.environmentId}/services/compose/${compose.composeId}?tab=deployments`;
const deployment = await createDeploymentCompose({ const deployment = await createDeploymentCompose({
composeId: composeId, composeId: composeId,
title: titleLog, title: titleLog,

View File

@@ -33,6 +33,7 @@ export const sendEmailNotification = async (
to: toAddresses.join(", "), to: toAddresses.join(", "),
subject, subject,
html: htmlContent, html: htmlContent,
textEncoding: "base64",
}); });
} catch (err) { } catch (err) {
console.log(err); console.log(err);

View File

@@ -31,29 +31,51 @@ export const getBitbucketCloneUrl = (
apiToken?: string | null; apiToken?: string | null;
bitbucketUsername?: string | null; bitbucketUsername?: string | null;
appPassword?: string | null; appPassword?: string | null;
bitbucketEmail?: string | null;
bitbucketWorkspaceName?: string | null;
} | null, } | null,
repoClone: string, repoClone: string,
) => { ) => {
if (!bitbucketProvider) { if (!bitbucketProvider) {
throw new Error("Bitbucket provider is required"); throw new Error("Bitbucket provider is required");
} }
return bitbucketProvider.apiToken
? `https://x-token-auth:${bitbucketProvider.apiToken}@${repoClone}` if (bitbucketProvider.apiToken) {
: `https://${bitbucketProvider.bitbucketUsername}:${bitbucketProvider.appPassword}@${repoClone}`; return `https://x-bitbucket-api-token-auth:${bitbucketProvider.apiToken}@${repoClone}`;
}
// For app passwords, use username:app_password format
if (!bitbucketProvider.bitbucketUsername || !bitbucketProvider.appPassword) {
throw new Error(
"Username and app password are required when not using API token",
);
}
return `https://${bitbucketProvider.bitbucketUsername}:${bitbucketProvider.appPassword}@${repoClone}`;
}; };
export const getBitbucketHeaders = (bitbucketProvider: Bitbucket) => { export const getBitbucketHeaders = (bitbucketProvider: Bitbucket) => {
if (bitbucketProvider.apiToken) { if (bitbucketProvider.apiToken) {
// For API tokens, use HTTP Basic auth with email and token // According to Bitbucket official docs, for API calls with API tokens:
// According to Bitbucket docs: email:token for API calls // "You will need both your Atlassian account email and an API token"
const email = // Use: {atlassian_account_email}:{api_token}
bitbucketProvider.bitbucketEmail || bitbucketProvider.bitbucketUsername;
if (!bitbucketProvider.bitbucketEmail) {
throw new Error(
"Atlassian account email is required when using API token for API calls",
);
}
return { return {
Authorization: `Basic ${Buffer.from(`${email}:${bitbucketProvider.apiToken}`).toString("base64")}`, Authorization: `Basic ${Buffer.from(`${bitbucketProvider.bitbucketEmail}:${bitbucketProvider.apiToken}`).toString("base64")}`,
}; };
} }
// For app passwords, use HTTP Basic auth with username and app password // For app passwords, use HTTP Basic auth with username and app password
if (!bitbucketProvider.bitbucketUsername || !bitbucketProvider.appPassword) {
throw new Error(
"Username and app password are required when not using API token",
);
}
return { return {
Authorization: `Basic ${Buffer.from(`${bitbucketProvider.bitbucketUsername}:${bitbucketProvider.appPassword}`).toString("base64")}`, Authorization: `Basic ${Buffer.from(`${bitbucketProvider.bitbucketUsername}:${bitbucketProvider.appPassword}`).toString("base64")}`,
}; };

View File

@@ -99,6 +99,19 @@ export const refreshGiteaToken = async (giteaProviderId: string) => {
} }
}; };
const buildGiteaCloneUrl = (
giteaUrl: string,
accessToken: string,
owner: string,
repository: string,
) => {
const protocol = giteaUrl.startsWith("http://") ? "http" : "https";
const baseUrl = giteaUrl.replace(/^https?:\/\//, "");
const repoClone = `${owner}/${repository}.git`;
const cloneUrl = `${protocol}://oauth2:${accessToken}@${baseUrl}/${repoClone}`;
return cloneUrl;
};
export type ApplicationWithGitea = InferResultType< export type ApplicationWithGitea = InferResultType<
"applications", "applications",
{ gitea: true } { gitea: true }
@@ -148,9 +161,13 @@ export const getGiteaCloneCommand = async (
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
const outputPath = join(basePath, appName, "code"); const outputPath = join(basePath, appName, "code");
const baseUrl = gitea?.giteaUrl.replace(/^https?:\/\//, "");
const repoClone = `${giteaOwner}/${giteaRepository}.git`; const repoClone = `${giteaOwner}/${giteaRepository}.git`;
const cloneUrl = `https://oauth2:${gitea?.accessToken}@${baseUrl}/${repoClone}`; const cloneUrl = buildGiteaCloneUrl(
gitea?.giteaUrl!,
gitea?.accessToken!,
giteaOwner!,
giteaRepository!,
);
const cloneCommand = ` const cloneCommand = `
rm -rf ${outputPath}; rm -rf ${outputPath};
@@ -205,8 +222,12 @@ export const cloneGiteaRepository = async (
await recreateDirectory(outputPath); await recreateDirectory(outputPath);
const repoClone = `${giteaOwner}/${giteaRepository}.git`; const repoClone = `${giteaOwner}/${giteaRepository}.git`;
const baseUrl = giteaProvider.giteaUrl.replace(/^https?:\/\//, ""); const cloneUrl = buildGiteaCloneUrl(
const cloneUrl = `https://oauth2:${giteaProvider.accessToken}@${baseUrl}/${repoClone}`; giteaProvider.giteaUrl,
giteaProvider.accessToken!,
giteaOwner!,
giteaRepository!,
);
writeStream.write(`\nCloning Repo ${repoClone} to ${outputPath}...\n`); writeStream.write(`\nCloning Repo ${repoClone} to ${outputPath}...\n`);
@@ -269,9 +290,12 @@ export const cloneRawGiteaRepository = async (entity: Compose) => {
const outputPath = join(basePath, appName, "code"); const outputPath = join(basePath, appName, "code");
await recreateDirectory(outputPath); await recreateDirectory(outputPath);
const repoClone = `${giteaOwner}/${giteaRepository}.git`; const cloneUrl = buildGiteaCloneUrl(
const baseUrl = giteaProvider.giteaUrl.replace(/^https?:\/\//, ""); giteaProvider.giteaUrl,
const cloneUrl = `https://oauth2:${giteaProvider.accessToken}@${baseUrl}/${repoClone}`; giteaProvider.accessToken!,
giteaOwner!,
giteaRepository!,
);
try { try {
await spawnAsync("git", [ await spawnAsync("git", [
@@ -317,9 +341,13 @@ export const cloneRawGiteaRepositoryRemote = async (compose: Compose) => {
const giteaProvider = await findGiteaById(giteaId); const giteaProvider = await findGiteaById(giteaId);
const basePath = COMPOSE_PATH; const basePath = COMPOSE_PATH;
const outputPath = join(basePath, appName, "code"); const outputPath = join(basePath, appName, "code");
const repoClone = `${giteaOwner}/${giteaRepository}.git`; const cloneUrl = buildGiteaCloneUrl(
const baseUrl = giteaProvider.giteaUrl.replace(/^https?:\/\//, ""); giteaProvider.giteaUrl,
const cloneUrl = `https://oauth2:${giteaProvider.accessToken}@${baseUrl}/${repoClone}`; giteaProvider.accessToken!,
giteaOwner!,
giteaRepository!,
);
try { try {
const command = ` const command = `
rm -rf ${outputPath}; rm -rf ${outputPath};

View File

@@ -0,0 +1,21 @@
import { deployments } from "@dokploy/server/db/schema";
import { eq } from "drizzle-orm";
import { db } from "../../db/index";
export const initCancelDeployments = async () => {
try {
console.log("Setting up cancel deployments....");
const result = await db
.update(deployments)
.set({
status: "cancelled",
})
.where(eq(deployments.status, "running"))
.returning();
console.log(`Cancelled ${result.length} deployments`);
} catch (error) {
console.error(error);
}
};

219
pnpm-lock.yaml generated
View File

@@ -306,10 +306,10 @@ importers:
version: 16.4.5 version: 16.4.5
drizzle-orm: drizzle-orm:
specifier: ^0.39.3 specifier: ^0.39.3
version: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4) version: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4)
drizzle-zod: drizzle-zod:
specifier: 0.5.1 specifier: 0.5.1
version: 0.5.1(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4))(zod@3.25.32) version: 0.5.1(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4))(zod@3.25.32)
fancy-ansi: fancy-ansi:
specifier: ^0.1.3 specifier: ^0.1.3
version: 0.1.3 version: 0.1.3
@@ -550,7 +550,7 @@ importers:
version: 16.4.5 version: 16.4.5
drizzle-orm: drizzle-orm:
specifier: ^0.39.3 specifier: ^0.39.3
version: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4) version: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4)
hono: hono:
specifier: ^4.7.10 specifier: ^4.7.10
version: 4.7.10 version: 4.7.10
@@ -668,13 +668,13 @@ importers:
version: 16.4.5 version: 16.4.5
drizzle-dbml-generator: drizzle-dbml-generator:
specifier: 0.10.0 specifier: 0.10.0
version: 0.10.0(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4)) version: 0.10.0(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4))
drizzle-orm: drizzle-orm:
specifier: ^0.39.3 specifier: ^0.39.3
version: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4) version: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4)
drizzle-zod: drizzle-zod:
specifier: 0.5.1 specifier: 0.5.1
version: 0.5.1(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4))(zod@3.25.32) version: 0.5.1(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4))(zod@3.25.32)
hi-base32: hi-base32:
specifier: ^0.5.1 specifier: ^0.5.1
version: 0.5.1 version: 0.5.1
@@ -904,6 +904,9 @@ packages:
'@better-auth/utils@0.2.5': '@better-auth/utils@0.2.5':
resolution: {integrity: sha512-uI2+/8h/zVsH8RrYdG8eUErbuGBk16rZKQfz8CjxQOyCE6v7BqFYEbFwvOkvl1KbUdxhqOnXp78+uE5h8qVEgQ==} resolution: {integrity: sha512-uI2+/8h/zVsH8RrYdG8eUErbuGBk16rZKQfz8CjxQOyCE6v7BqFYEbFwvOkvl1KbUdxhqOnXp78+uE5h8qVEgQ==}
'@better-auth/utils@0.3.0':
resolution: {integrity: sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==}
'@better-fetch/fetch@1.1.18': '@better-fetch/fetch@1.1.18':
resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==} resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==}
@@ -1987,10 +1990,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}
'@noble/hashes@1.8.0':
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
'@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'}
@@ -2629,17 +2628,38 @@ packages:
'@peculiar/asn1-android@2.3.16': '@peculiar/asn1-android@2.3.16':
resolution: {integrity: sha512-a1viIv3bIahXNssrOIkXZIlI2ePpZaNmR30d4aBL99mu2rO+mT9D6zBsp7H6eROWGtmwv0Ionp5olJurIo09dw==} resolution: {integrity: sha512-a1viIv3bIahXNssrOIkXZIlI2ePpZaNmR30d4aBL99mu2rO+mT9D6zBsp7H6eROWGtmwv0Ionp5olJurIo09dw==}
'@peculiar/asn1-ecc@2.3.15': '@peculiar/asn1-cms@2.5.0':
resolution: {integrity: sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA==} resolution: {integrity: sha512-p0SjJ3TuuleIvjPM4aYfvYw8Fk1Hn/zAVyPJZTtZ2eE9/MIer6/18ROxX6N/e6edVSfvuZBqhxAj3YgsmSjQ/A==}
'@peculiar/asn1-rsa@2.3.15': '@peculiar/asn1-csr@2.5.0':
resolution: {integrity: sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg==} resolution: {integrity: sha512-ioigvA6WSYN9h/YssMmmoIwgl3RvZlAYx4A/9jD2qaqXZwGcNlAxaw54eSx2QG1Yu7YyBC5Rku3nNoHrQ16YsQ==}
'@peculiar/asn1-schema@2.3.15': '@peculiar/asn1-ecc@2.5.0':
resolution: {integrity: sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==} resolution: {integrity: sha512-t4eYGNhXtLRxaP50h3sfO6aJebUCDGQACoeexcelL4roMFRRVgB20yBIu2LxsPh/tdW9I282gNgMOyg3ywg/mg==}
'@peculiar/asn1-x509@2.3.15': '@peculiar/asn1-pfx@2.5.0':
resolution: {integrity: sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg==} resolution: {integrity: sha512-Vj0d0wxJZA+Ztqfb7W+/iu8Uasw6hhKtCdLKXLG/P3kEPIQpqGI4P4YXlROfl7gOCqFIbgsj1HzFIFwQ5s20ug==}
'@peculiar/asn1-pkcs8@2.5.0':
resolution: {integrity: sha512-L7599HTI2SLlitlpEP8oAPaJgYssByI4eCwQq2C9eC90otFpm8MRn66PpbKviweAlhinWQ3ZjDD2KIVtx7PaVw==}
'@peculiar/asn1-pkcs9@2.5.0':
resolution: {integrity: sha512-UgqSMBLNLR5TzEZ5ZzxR45Nk6VJrammxd60WMSkofyNzd3DQLSNycGWSK5Xg3UTYbXcDFyG8pA/7/y/ztVCa6A==}
'@peculiar/asn1-rsa@2.5.0':
resolution: {integrity: sha512-qMZ/vweiTHy9syrkkqWFvbT3eLoedvamcUdnnvwyyUNv5FgFXA3KP8td+ATibnlZ0EANW5PYRm8E6MJzEB/72Q==}
'@peculiar/asn1-schema@2.5.0':
resolution: {integrity: sha512-YM/nFfskFJSlHqv59ed6dZlLZqtZQwjRVJ4bBAiWV08Oc+1rSd5lDZcBEx0lGDHfSoH3UziI2pXt2UM33KerPQ==}
'@peculiar/asn1-x509-attr@2.5.0':
resolution: {integrity: sha512-9f0hPOxiJDoG/bfNLAFven+Bd4gwz/VzrCIIWc1025LEI4BXO0U5fOCTNDPbbp2ll+UzqKsZ3g61mpBp74gk9A==}
'@peculiar/asn1-x509@2.5.0':
resolution: {integrity: sha512-CpwtMCTJvfvYTFMuiME5IH+8qmDe3yEWzKHe7OOADbGfq7ohxeLaXwQo0q4du3qs0AII3UbLCvb9NF/6q0oTKQ==}
'@peculiar/x509@1.14.0':
resolution: {integrity: sha512-Yc4PDxN3OrxUPiXgU63c+ZRXKGE8YKF2McTciYhUHFtHVB0KMnjeFSU0qpztGhsp4P0uKix4+J2xEpIEDu8oXg==}
'@petamoriken/float16@3.9.2': '@petamoriken/float16@3.9.2':
resolution: {integrity: sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==} resolution: {integrity: sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==}
@@ -3673,11 +3693,11 @@ packages:
'@selderee/plugin-htmlparser2@0.11.0': '@selderee/plugin-htmlparser2@0.11.0':
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
'@simplewebauthn/browser@13.1.0': '@simplewebauthn/browser@13.2.2':
resolution: {integrity: sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==} resolution: {integrity: sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA==}
'@simplewebauthn/server@13.1.1': '@simplewebauthn/server@13.2.2':
resolution: {integrity: sha512-1hsLpRHfSuMB9ee2aAdh0Htza/X3f4djhYISrggqGe3xopNjOcePiSDkDDoPzDYaaMCrbqGP1H2TYU7bgL9PmA==} resolution: {integrity: sha512-HcWLW28yTMGXpwE9VLx9J+N2KEUaELadLrkPEEI9tpI5la70xNEVEsu/C+m3u7uoq4FulLqZQhgBCzR9IZhFpA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
'@sinclair/typebox@0.27.8': '@sinclair/typebox@0.27.8':
@@ -4306,8 +4326,8 @@ packages:
better-auth@1.2.8-beta.7: better-auth@1.2.8-beta.7:
resolution: {integrity: sha512-gVApvvhnPVqMCYYLMhxUfbTi5fJYfp9rcsoJSjjTOMV+CIc7KVlYN6Qo8E7ju1JeRU5ae1Wl1NdXrolRJHjmaQ==} resolution: {integrity: sha512-gVApvvhnPVqMCYYLMhxUfbTi5fJYfp9rcsoJSjjTOMV+CIc7KVlYN6Qo8E7ju1JeRU5ae1Wl1NdXrolRJHjmaQ==}
better-call@1.0.9: better-call@1.0.19:
resolution: {integrity: sha512-Qfm0gjk0XQz0oI7qvTK1hbqTsBY4xV2hsHAxF8LZfUYl3RaECCIifXuVqtPpZJWvlCCMlQSvkvhhyuApGUba6g==} resolution: {integrity: sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw==}
bignumber.js@9.3.1: bignumber.js@9.3.1:
resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
@@ -5754,9 +5774,9 @@ packages:
keyv@4.5.4: keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
kysely@0.28.2: kysely@0.28.7:
resolution: {integrity: sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==} resolution: {integrity: sha512-u/cAuTL4DRIiO2/g4vNGRgklEKNIj5Q3CG7RoUB5DV5SfEC2hMvPxKi0GWPmnzwL2ryIeud2VTcEEmqzTzEPNw==}
engines: {node: '>=18.0.0'} engines: {node: '>=20.0.0'}
leac@0.6.0: leac@0.6.0:
resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
@@ -6926,6 +6946,9 @@ packages:
redux@5.0.1: redux@5.0.1:
resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==}
reflect-metadata@0.2.2:
resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
refractor@3.6.0: refractor@3.6.0:
resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==}
@@ -7446,6 +7469,9 @@ packages:
typescript: typescript:
optional: true optional: true
tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
tslib@2.8.1: tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@@ -7454,6 +7480,10 @@ packages:
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}
hasBin: true hasBin: true
tsyringe@4.10.0:
resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==}
engines: {node: '>= 6.0.0'}
tweetnacl@0.14.5: tweetnacl@0.14.5:
resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
@@ -7913,6 +7943,8 @@ snapshots:
typescript: 5.8.3 typescript: 5.8.3
uncrypto: 0.1.3 uncrypto: 0.1.3
'@better-auth/utils@0.3.0': {}
'@better-fetch/fetch@1.1.18': {} '@better-fetch/fetch@1.1.18': {}
'@biomejs/biome@2.1.1': '@biomejs/biome@2.1.1':
@@ -8733,8 +8765,6 @@ snapshots:
'@noble/hashes@1.7.1': {} '@noble/hashes@1.7.1': {}
'@noble/hashes@1.8.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
@@ -9637,37 +9667,100 @@ snapshots:
'@peculiar/asn1-android@2.3.16': '@peculiar/asn1-android@2.3.16':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.3.15 '@peculiar/asn1-schema': 2.5.0
asn1js: 3.0.6 asn1js: 3.0.6
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-ecc@2.3.15': '@peculiar/asn1-cms@2.5.0':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.3.15 '@peculiar/asn1-schema': 2.5.0
'@peculiar/asn1-x509': 2.3.15 '@peculiar/asn1-x509': 2.5.0
'@peculiar/asn1-x509-attr': 2.5.0
asn1js: 3.0.6 asn1js: 3.0.6
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-rsa@2.3.15': '@peculiar/asn1-csr@2.5.0':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.3.15 '@peculiar/asn1-schema': 2.5.0
'@peculiar/asn1-x509': 2.3.15 '@peculiar/asn1-x509': 2.5.0
asn1js: 3.0.6 asn1js: 3.0.6
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-schema@2.3.15': '@peculiar/asn1-ecc@2.5.0':
dependencies:
'@peculiar/asn1-schema': 2.5.0
'@peculiar/asn1-x509': 2.5.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pfx@2.5.0':
dependencies:
'@peculiar/asn1-cms': 2.5.0
'@peculiar/asn1-pkcs8': 2.5.0
'@peculiar/asn1-rsa': 2.5.0
'@peculiar/asn1-schema': 2.5.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pkcs8@2.5.0':
dependencies:
'@peculiar/asn1-schema': 2.5.0
'@peculiar/asn1-x509': 2.5.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pkcs9@2.5.0':
dependencies:
'@peculiar/asn1-cms': 2.5.0
'@peculiar/asn1-pfx': 2.5.0
'@peculiar/asn1-pkcs8': 2.5.0
'@peculiar/asn1-schema': 2.5.0
'@peculiar/asn1-x509': 2.5.0
'@peculiar/asn1-x509-attr': 2.5.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-rsa@2.5.0':
dependencies:
'@peculiar/asn1-schema': 2.5.0
'@peculiar/asn1-x509': 2.5.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-schema@2.5.0':
dependencies: dependencies:
asn1js: 3.0.6 asn1js: 3.0.6
pvtsutils: 1.3.6 pvtsutils: 1.3.6
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/asn1-x509@2.3.15': '@peculiar/asn1-x509-attr@2.5.0':
dependencies: dependencies:
'@peculiar/asn1-schema': 2.3.15 '@peculiar/asn1-schema': 2.5.0
'@peculiar/asn1-x509': 2.5.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-x509@2.5.0':
dependencies:
'@peculiar/asn1-schema': 2.5.0
asn1js: 3.0.6 asn1js: 3.0.6
pvtsutils: 1.3.6 pvtsutils: 1.3.6
tslib: 2.8.1 tslib: 2.8.1
'@peculiar/x509@1.14.0':
dependencies:
'@peculiar/asn1-cms': 2.5.0
'@peculiar/asn1-csr': 2.5.0
'@peculiar/asn1-ecc': 2.5.0
'@peculiar/asn1-pkcs9': 2.5.0
'@peculiar/asn1-rsa': 2.5.0
'@peculiar/asn1-schema': 2.5.0
'@peculiar/asn1-x509': 2.5.0
pvtsutils: 1.3.6
reflect-metadata: 0.2.2
tslib: 2.8.1
tsyringe: 4.10.0
'@petamoriken/float16@3.9.2': {} '@petamoriken/float16@3.9.2': {}
'@pkgjs/parseargs@0.11.0': '@pkgjs/parseargs@0.11.0':
@@ -10678,17 +10771,18 @@ snapshots:
domhandler: 5.0.3 domhandler: 5.0.3
selderee: 0.11.0 selderee: 0.11.0
'@simplewebauthn/browser@13.1.0': {} '@simplewebauthn/browser@13.2.2': {}
'@simplewebauthn/server@13.1.1': '@simplewebauthn/server@13.2.2':
dependencies: dependencies:
'@hexagon/base64': 1.1.28 '@hexagon/base64': 1.1.28
'@levischuck/tiny-cbor': 0.2.11 '@levischuck/tiny-cbor': 0.2.11
'@peculiar/asn1-android': 2.3.16 '@peculiar/asn1-android': 2.3.16
'@peculiar/asn1-ecc': 2.3.15 '@peculiar/asn1-ecc': 2.5.0
'@peculiar/asn1-rsa': 2.3.15 '@peculiar/asn1-rsa': 2.5.0
'@peculiar/asn1-schema': 2.3.15 '@peculiar/asn1-schema': 2.5.0
'@peculiar/asn1-x509': 2.3.15 '@peculiar/asn1-x509': 2.5.0
'@peculiar/x509': 1.14.0
'@sinclair/typebox@0.27.8': {} '@sinclair/typebox@0.27.8': {}
@@ -11602,18 +11696,19 @@ snapshots:
'@better-auth/utils': 0.2.5 '@better-auth/utils': 0.2.5
'@better-fetch/fetch': 1.1.18 '@better-fetch/fetch': 1.1.18
'@noble/ciphers': 0.6.0 '@noble/ciphers': 0.6.0
'@noble/hashes': 1.8.0 '@noble/hashes': 1.7.1
'@simplewebauthn/browser': 13.1.0 '@simplewebauthn/browser': 13.2.2
'@simplewebauthn/server': 13.1.1 '@simplewebauthn/server': 13.2.2
better-call: 1.0.9 better-call: 1.0.19
defu: 6.1.4 defu: 6.1.4
jose: 5.10.0 jose: 5.10.0
kysely: 0.28.2 kysely: 0.28.7
nanostores: 0.11.4 nanostores: 0.11.4
zod: 3.25.32 zod: 3.25.32
better-call@1.0.9: better-call@1.0.19:
dependencies: dependencies:
'@better-auth/utils': 0.3.0
'@better-fetch/fetch': 1.1.18 '@better-fetch/fetch': 1.1.18
rou3: 0.5.1 rou3: 0.5.1
set-cookie-parser: 2.7.1 set-cookie-parser: 2.7.1
@@ -12181,9 +12276,9 @@ snapshots:
drange@1.1.1: {} drange@1.1.1: {}
drizzle-dbml-generator@0.10.0(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4)): drizzle-dbml-generator@0.10.0(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4)):
dependencies: dependencies:
drizzle-orm: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4) drizzle-orm: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4)
drizzle-kit@0.30.6: drizzle-kit@0.30.6:
dependencies: dependencies:
@@ -12195,16 +12290,16 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4): drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4):
optionalDependencies: optionalDependencies:
'@opentelemetry/api': 1.9.0 '@opentelemetry/api': 1.9.0
'@types/pg': 8.6.1 '@types/pg': 8.6.1
kysely: 0.28.2 kysely: 0.28.7
postgres: 3.4.4 postgres: 3.4.4
drizzle-zod@0.5.1(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4))(zod@3.25.32): drizzle-zod@0.5.1(drizzle-orm@0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4))(zod@3.25.32):
dependencies: dependencies:
drizzle-orm: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.2)(postgres@3.4.4) drizzle-orm: 0.39.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(kysely@0.28.7)(postgres@3.4.4)
zod: 3.25.32 zod: 3.25.32
dunder-proto@1.0.1: dunder-proto@1.0.1:
@@ -13138,7 +13233,7 @@ snapshots:
dependencies: dependencies:
json-buffer: 3.0.1 json-buffer: 3.0.1
kysely@0.28.2: {} kysely@0.28.7: {}
leac@0.6.0: {} leac@0.6.0: {}
@@ -14424,6 +14519,8 @@ snapshots:
redux@5.0.1: {} redux@5.0.1: {}
reflect-metadata@0.2.2: {}
refractor@3.6.0: refractor@3.6.0:
dependencies: dependencies:
hastscript: 6.0.0 hastscript: 6.0.0
@@ -15031,6 +15128,8 @@ snapshots:
optionalDependencies: optionalDependencies:
typescript: 5.8.3 typescript: 5.8.3
tslib@1.14.1: {}
tslib@2.8.1: {} tslib@2.8.1: {}
tsx@4.16.2: tsx@4.16.2:
@@ -15040,6 +15139,10 @@ snapshots:
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
tsyringe@4.10.0:
dependencies:
tslib: 1.14.1
tweetnacl@0.14.5: {} tweetnacl@0.14.5: {}
type-detect@4.1.0: {} type-detect@4.1.0: {}