mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-07 20:45:17 +00:00
Merge branch 'canary' into preview-deploy-rebuild
This commit is contained in:
@@ -13,7 +13,6 @@
|
|||||||
"@dokploy/server": "workspace:*",
|
"@dokploy/server": "workspace:*",
|
||||||
"@hono/node-server": "^1.14.3",
|
"@hono/node-server": "^1.14.3",
|
||||||
"@hono/zod-validator": "0.3.0",
|
"@hono/zod-validator": "0.3.0",
|
||||||
"@nerimity/mimiqueue": "1.2.3",
|
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"hono": "^4.7.10",
|
"hono": "^4.7.10",
|
||||||
"pino": "9.4.0",
|
"pino": "9.4.0",
|
||||||
|
|||||||
+2
-2
@@ -232,9 +232,9 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
|
|||||||
<FormItem className="md:col-span-2 flex flex-col">
|
<FormItem className="md:col-span-2 flex flex-col">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<FormLabel>Repository</FormLabel>
|
<FormLabel>Repository</FormLabel>
|
||||||
{field.value.owner && field.value.repo && (
|
{field.value.gitlabPathNamespace && (
|
||||||
<Link
|
<Link
|
||||||
href={`${gitlabUrl}/${field.value.owner}/${field.value.repo}`}
|
href={`${gitlabUrl}/${field.value.gitlabPathNamespace}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex items-center gap-1 text-sm text-muted-foreground hover:text-primary"
|
className="flex items-center gap-1 text-sm text-muted-foreground hover:text-primary"
|
||||||
|
|||||||
+13
-3
@@ -1,7 +1,7 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { CheckIcon, ChevronsUpDown, X } from "lucide-react";
|
import { CheckIcon, ChevronsUpDown, X } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect } from "react";
|
import { useEffect, useMemo } 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";
|
||||||
@@ -97,6 +97,16 @@ export const SaveGitlabProviderCompose = ({ composeId }: Props) => {
|
|||||||
const repository = form.watch("repository");
|
const repository = form.watch("repository");
|
||||||
const gitlabId = form.watch("gitlabId");
|
const gitlabId = form.watch("gitlabId");
|
||||||
|
|
||||||
|
const gitlabUrl = useMemo(() => {
|
||||||
|
const url = gitlabProviders?.find(
|
||||||
|
(provider) => provider.gitlabId === gitlabId,
|
||||||
|
)?.gitlabUrl;
|
||||||
|
|
||||||
|
const gitlabUrl = url?.replace(/\/$/, "");
|
||||||
|
|
||||||
|
return gitlabUrl || "https://gitlab.com";
|
||||||
|
}, [gitlabId, gitlabProviders]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: repositories,
|
data: repositories,
|
||||||
isLoading: isLoadingRepositories,
|
isLoading: isLoadingRepositories,
|
||||||
@@ -224,9 +234,9 @@ export const SaveGitlabProviderCompose = ({ composeId }: Props) => {
|
|||||||
<FormItem className="md:col-span-2 flex flex-col">
|
<FormItem className="md:col-span-2 flex flex-col">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<FormLabel>Repository</FormLabel>
|
<FormLabel>Repository</FormLabel>
|
||||||
{field.value.owner && field.value.repo && (
|
{field.value.gitlabPathNamespace && (
|
||||||
<Link
|
<Link
|
||||||
href={`https://gitlab.com/${field.value.owner}/${field.value.repo}`}
|
href={`${gitlabUrl}/${field.value.gitlabPathNamespace}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex items-center gap-1 text-sm text-muted-foreground hover:text-primary"
|
className="flex items-center gap-1 text-sm text-muted-foreground hover:text-primary"
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { CreditCard, FileText } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { ShowInvoices } from "./show-invoices";
|
||||||
|
|
||||||
|
const navigationItems = [
|
||||||
|
{
|
||||||
|
name: "Subscription",
|
||||||
|
href: "/dashboard/settings/billing",
|
||||||
|
icon: CreditCard,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invoices",
|
||||||
|
href: "/dashboard/settings/invoices",
|
||||||
|
icon: FileText,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ShowBillingInvoices = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<Card className="bg-sidebar p-2.5 rounded-xl max-w-5xl mx-auto">
|
||||||
|
<div className="rounded-xl bg-background shadow-md">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-xl flex flex-row gap-2">
|
||||||
|
<CreditCard className="size-6 text-muted-foreground self-center" />
|
||||||
|
Billing
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Manage your subscription and invoices
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4 py-4 border-t">
|
||||||
|
<nav className="flex space-x-2 border-b">
|
||||||
|
{navigationItems.map((item) => {
|
||||||
|
const Icon = item.icon;
|
||||||
|
const isActive = router.pathname === item.href;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
href={item.href}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center gap-2 px-4 py-2 text-sm font-medium border-b-2 transition-colors",
|
||||||
|
isActive
|
||||||
|
? "border-primary text-primary"
|
||||||
|
: "border-transparent text-muted-foreground hover:text-primary hover:border-muted",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon className="h-4 w-4" />
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div className="mt-6">
|
||||||
|
<ShowInvoices />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,11 +4,13 @@ import {
|
|||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
CreditCard,
|
CreditCard,
|
||||||
|
FileText,
|
||||||
Loader2,
|
Loader2,
|
||||||
MinusIcon,
|
MinusIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -37,7 +39,22 @@ export const calculatePrice = (count: number, isAnnual = false) => {
|
|||||||
if (count <= 1) return 4.5;
|
if (count <= 1) return 4.5;
|
||||||
return count * 3.5;
|
return count * 3.5;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const navigationItems = [
|
||||||
|
{
|
||||||
|
name: "Subscription",
|
||||||
|
href: "/dashboard/settings/billing",
|
||||||
|
icon: CreditCard,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invoices",
|
||||||
|
href: "/dashboard/settings/invoices",
|
||||||
|
icon: FileText,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export const ShowBilling = () => {
|
export const ShowBilling = () => {
|
||||||
|
const router = useRouter();
|
||||||
const { data: servers } = api.server.count.useQuery();
|
const { data: servers } = api.server.count.useQuery();
|
||||||
const { data: admin } = api.user.get.useQuery();
|
const { data: admin } = api.user.get.useQuery();
|
||||||
const { data, isLoading } = api.stripe.getProducts.useQuery();
|
const { data, isLoading } = api.stripe.getProducts.useQuery();
|
||||||
@@ -76,17 +93,41 @@ export const ShowBilling = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Card className="h-full bg-sidebar p-2.5 rounded-xl max-w-5xl mx-auto">
|
<Card className="bg-sidebar p-2.5 rounded-xl max-w-5xl mx-auto">
|
||||||
<div className="rounded-xl bg-background shadow-md ">
|
<div className="rounded-xl bg-background shadow-md">
|
||||||
<CardHeader className="">
|
<CardHeader>
|
||||||
<CardTitle className="text-xl flex flex-row gap-2">
|
<CardTitle className="text-xl flex flex-row gap-2">
|
||||||
<CreditCard className="size-6 text-muted-foreground self-center" />
|
<CreditCard className="size-6 text-muted-foreground self-center" />
|
||||||
Billing
|
Billing
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>Manage your subscription</CardDescription>
|
<CardDescription>
|
||||||
|
Manage your subscription and invoices
|
||||||
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-2 py-8 border-t">
|
<CardContent className="space-y-4 py-4 border-t">
|
||||||
<div className="flex flex-col gap-4 w-full">
|
<nav className="flex space-x-2 border-b">
|
||||||
|
{navigationItems.map((item) => {
|
||||||
|
const Icon = item.icon;
|
||||||
|
const isActive = router.pathname === item.href;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
href={item.href}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center gap-2 px-4 py-2 text-sm font-medium border-b-2 transition-colors",
|
||||||
|
isActive
|
||||||
|
? "border-primary text-primary"
|
||||||
|
: "border-transparent text-muted-foreground hover:text-primary hover:border-muted",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon className="h-4 w-4" />
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-4 w-full mt-6">
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue="monthly"
|
defaultValue="monthly"
|
||||||
value={isAnnual ? "annual" : "monthly"}
|
value={isAnnual ? "annual" : "monthly"}
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import { Download, ExternalLink, FileText, Loader2 } from "lucide-react";
|
||||||
|
import type Stripe from "stripe";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
|
const formatDate = (timestamp: number | null) => {
|
||||||
|
if (!timestamp) return "-";
|
||||||
|
return new Date(timestamp * 1000).toLocaleDateString("en-US", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "short",
|
||||||
|
day: "numeric",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatAmount = (amount: number, currency: string) => {
|
||||||
|
return new Intl.NumberFormat("en-US", {
|
||||||
|
style: "currency",
|
||||||
|
currency: currency.toUpperCase(),
|
||||||
|
}).format(amount / 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusBadge = (status: Stripe.Invoice.Status | null) => {
|
||||||
|
const statusConfig: Record<
|
||||||
|
Stripe.Invoice.Status,
|
||||||
|
{ label: string; variant: "default" | "secondary" | "destructive" }
|
||||||
|
> = {
|
||||||
|
paid: { label: "Paid", variant: "default" },
|
||||||
|
open: { label: "Open", variant: "secondary" },
|
||||||
|
draft: { label: "Draft", variant: "secondary" },
|
||||||
|
void: { label: "Void", variant: "destructive" },
|
||||||
|
uncollectible: { label: "Uncollectible", variant: "destructive" },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
return <Badge variant="secondary">Unknown</Badge>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = statusConfig[status] || {
|
||||||
|
label: status,
|
||||||
|
variant: "secondary" as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Badge variant={config.variant}>{config.label}</Badge>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ShowInvoices = () => {
|
||||||
|
const { data: invoices, isLoading } = api.stripe.getInvoices.useQuery();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex items-center justify-center min-h-[20vh]">
|
||||||
|
<span className="text-base text-muted-foreground flex flex-row gap-3 items-center">
|
||||||
|
Loading invoices...
|
||||||
|
<Loader2 className="animate-spin" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : invoices && invoices.length > 0 ? (
|
||||||
|
<div className="rounded-md border">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Invoice</TableHead>
|
||||||
|
<TableHead>Date</TableHead>
|
||||||
|
<TableHead>Due Date</TableHead>
|
||||||
|
<TableHead>Amount</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
|
<TableHead className="text-right">Actions</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{invoices.map((invoice) => (
|
||||||
|
<TableRow key={invoice.id}>
|
||||||
|
<TableCell className="font-medium">
|
||||||
|
{invoice.number || invoice.id.slice(0, 12)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{formatDate(invoice.created)}</TableCell>
|
||||||
|
<TableCell>{formatDate(invoice.dueDate)}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{formatAmount(invoice.amountDue, invoice.currency)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{getStatusBadge(invoice.status)}</TableCell>
|
||||||
|
<TableCell className="text-right">
|
||||||
|
<div className="flex justify-end gap-2">
|
||||||
|
{invoice.hostedInvoiceUrl && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
window.open(
|
||||||
|
invoice.hostedInvoiceUrl || "",
|
||||||
|
"_blank",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ExternalLink className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{invoice.invoicePdf && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
window.open(invoice.invoicePdf || "", "_blank")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Download className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-[20vh] gap-2">
|
||||||
|
<FileText className="size-12 text-muted-foreground" />
|
||||||
|
<p className="text-base text-muted-foreground">No invoices found</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Your invoices will appear here once you have a subscription
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
@@ -89,15 +88,15 @@ export const SetupServer = ({ serverId, asButton = false }: Props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
) : (
|
) : (
|
||||||
<DropdownMenuItem
|
<Button
|
||||||
className="w-full cursor-pointer "
|
className="w-full cursor-pointer "
|
||||||
onSelect={(e) => {
|
size="sm"
|
||||||
e.preventDefault();
|
onClick={() => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Setup Server
|
Setup Server <Settings className="size-4" />
|
||||||
</DropdownMenuItem>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<DialogContent className="sm:max-w-4xl ">
|
<DialogContent className="sm:max-w-4xl ">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import {
|
|||||||
Loader2,
|
Loader2,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
Network,
|
Network,
|
||||||
Pencil,
|
|
||||||
ServerIcon,
|
ServerIcon,
|
||||||
Settings,
|
|
||||||
Terminal,
|
Terminal,
|
||||||
Trash2,
|
Trash2,
|
||||||
User,
|
User,
|
||||||
@@ -31,9 +29,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
@@ -285,7 +281,32 @@ export const ShowServers = () => {
|
|||||||
|
|
||||||
{/* Compact Actions */}
|
{/* Compact Actions */}
|
||||||
{isActive && (
|
{isActive && (
|
||||||
<div className="flex items-center gap-2 pt-3 border-t mt-auto">
|
<div className="flex items-center gap-2 pt-3 border-t mt-auto flex-wrap">
|
||||||
|
<div className="flex items-center gap-2 w-full">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<SetupServer
|
||||||
|
serverId={server.serverId}
|
||||||
|
/>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent
|
||||||
|
className="max-w-xs"
|
||||||
|
side="bottom"
|
||||||
|
>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="font-semibold">
|
||||||
|
Setup Server
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Configure and initialize your
|
||||||
|
server with Docker, Traefik, and
|
||||||
|
other essential services
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
{server.sshKeyId && (
|
{server.sshKeyId && (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@@ -311,20 +332,6 @@ export const ShowServers = () => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<div>
|
|
||||||
<SetupServer
|
|
||||||
serverId={server.serverId}
|
|
||||||
asButton={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Setup Server</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dokploy",
|
"name": "dokploy",
|
||||||
"version": "v0.26.3",
|
"version": "v0.26.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -109,7 +109,6 @@
|
|||||||
"drizzle-orm": "^0.39.3",
|
"drizzle-orm": "^0.39.3",
|
||||||
"drizzle-zod": "0.5.1",
|
"drizzle-zod": "0.5.1",
|
||||||
"fancy-ansi": "^0.1.3",
|
"fancy-ansi": "^0.1.3",
|
||||||
"hi-base32": "^0.5.1",
|
|
||||||
"i18next": "^23.16.8",
|
"i18next": "^23.16.8",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
@@ -126,7 +125,6 @@
|
|||||||
"node-schedule": "2.1.1",
|
"node-schedule": "2.1.1",
|
||||||
"nodemailer": "6.9.14",
|
"nodemailer": "6.9.14",
|
||||||
"octokit": "3.1.2",
|
"octokit": "3.1.2",
|
||||||
"otpauth": "^9.4.0",
|
|
||||||
"pino": "9.4.0",
|
"pino": "9.4.0",
|
||||||
"pino-pretty": "11.2.2",
|
"pino-pretty": "11.2.2",
|
||||||
"postgres": "3.4.4",
|
"postgres": "3.4.4",
|
||||||
@@ -155,9 +153,11 @@
|
|||||||
"xterm-addon-fit": "^0.8.0",
|
"xterm-addon-fit": "^0.8.0",
|
||||||
"yaml": "2.8.1",
|
"yaml": "2.8.1",
|
||||||
"zod": "^3.25.32",
|
"zod": "^3.25.32",
|
||||||
"zod-form-data": "^2.0.7"
|
"zod-form-data": "^2.0.7",
|
||||||
|
"semver": "7.7.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/semver": "7.7.1",
|
||||||
"@types/shell-quote": "^1.7.5",
|
"@types/shell-quote": "^1.7.5",
|
||||||
"@types/adm-zip": "^0.5.7",
|
"@types/adm-zip": "^0.5.7",
|
||||||
"@types/bcrypt": "5.0.2",
|
"@types/bcrypt": "5.0.2",
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { IS_CLOUD } from "@dokploy/server/constants";
|
||||||
|
import { validateRequest } from "@dokploy/server/lib/auth";
|
||||||
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
import type { ReactElement } from "react";
|
||||||
|
import superjson from "superjson";
|
||||||
|
import { ShowBillingInvoices } from "@/components/dashboard/settings/billing/show-billing-invoices";
|
||||||
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
|
import { appRouter } from "@/server/api/root";
|
||||||
|
|
||||||
|
const Page = () => {
|
||||||
|
return <ShowBillingInvoices />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
|
|
||||||
|
Page.getLayout = (page: ReactElement) => {
|
||||||
|
return <DashboardLayout metaName="Invoices">{page}</DashboardLayout>;
|
||||||
|
};
|
||||||
|
export async function getServerSideProps(
|
||||||
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
|
) {
|
||||||
|
if (!IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const { req, res } = ctx;
|
||||||
|
const { user, session } = await validateRequest(req);
|
||||||
|
if (!user || user.role !== "owner") {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const helpers = createServerSideHelpers({
|
||||||
|
router: appRouter,
|
||||||
|
ctx: {
|
||||||
|
req: req as any,
|
||||||
|
res: res as any,
|
||||||
|
db: null as any,
|
||||||
|
session: session as any,
|
||||||
|
user: user as any,
|
||||||
|
},
|
||||||
|
transformer: superjson,
|
||||||
|
});
|
||||||
|
|
||||||
|
await helpers.user.get.prefetch();
|
||||||
|
|
||||||
|
await helpers.settings.isCloud.prefetch();
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
trpcState: helpers.dehydrate(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -285,6 +285,7 @@ export const backupRouter = createTRPCRouter({
|
|||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
const backup = await findBackupById(input.backupId);
|
const backup = await findBackupById(input.backupId);
|
||||||
await runWebServerBackup(backup);
|
await runWebServerBackup(backup);
|
||||||
|
await keepLatestNBackups(backup);
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
listBackupFiles: protectedProcedure
|
listBackupFiles: protectedProcedure
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
await reloadDockerResource("dokploy");
|
await reloadDockerResource("dokploy", undefined, packageInfo.version);
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
cleanRedis: adminProcedure.mutation(async () => {
|
cleanRedis: adminProcedure.mutation(async () => {
|
||||||
@@ -399,7 +399,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
return DEFAULT_UPDATE_DATA;
|
return DEFAULT_UPDATE_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await getUpdateData();
|
return await getUpdateData(packageInfo.version);
|
||||||
}),
|
}),
|
||||||
updateServer: adminProcedure.mutation(async () => {
|
updateServer: adminProcedure.mutation(async () => {
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export const stripeRouter = createTRPCRouter({
|
|||||||
metadata: {
|
metadata: {
|
||||||
adminId: owner.id,
|
adminId: owner.id,
|
||||||
},
|
},
|
||||||
|
customer_email: owner.email,
|
||||||
allow_promotion_codes: true,
|
allow_promotion_codes: true,
|
||||||
success_url: `${WEBSITE_URL}/dashboard/settings/servers?success=true`,
|
success_url: `${WEBSITE_URL}/dashboard/settings/servers?success=true`,
|
||||||
cancel_url: `${WEBSITE_URL}/dashboard/settings/billing`,
|
cancel_url: `${WEBSITE_URL}/dashboard/settings/billing`,
|
||||||
@@ -128,4 +129,39 @@ export const stripeRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return servers.length < user.serversQuantity;
|
return servers.length < user.serversQuantity;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
getInvoices: adminProcedure.query(async ({ ctx }) => {
|
||||||
|
const user = await findUserById(ctx.user.ownerId);
|
||||||
|
const stripeCustomerId = user.stripeCustomerId;
|
||||||
|
|
||||||
|
if (!stripeCustomerId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
||||||
|
apiVersion: "2024-09-30.acacia",
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const invoices = await stripe.invoices.list({
|
||||||
|
customer: stripeCustomerId,
|
||||||
|
limit: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
return invoices.data.map((invoice) => ({
|
||||||
|
id: invoice.id,
|
||||||
|
number: invoice.number,
|
||||||
|
status: invoice.status,
|
||||||
|
amountDue: invoice.amount_due,
|
||||||
|
amountPaid: invoice.amount_paid,
|
||||||
|
currency: invoice.currency,
|
||||||
|
created: invoice.created,
|
||||||
|
dueDate: invoice.due_date,
|
||||||
|
hostedInvoiceUrl: invoice.hosted_invoice_url,
|
||||||
|
invoicePdf: invoice.invoice_pdf,
|
||||||
|
}));
|
||||||
|
} catch (_) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func (db *DB) GetLastNContainerMetrics(containerName string, limit int) ([]Conta
|
|||||||
WITH recent_metrics AS (
|
WITH recent_metrics AS (
|
||||||
SELECT metrics_json
|
SELECT metrics_json
|
||||||
FROM container_metrics
|
FROM container_metrics
|
||||||
WHERE container_name LIKE ? || '%'
|
WHERE container_name = ?
|
||||||
ORDER BY timestamp DESC
|
ORDER BY timestamp DESC
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
)
|
)
|
||||||
@@ -98,7 +98,7 @@ func (db *DB) GetAllMetricsContainer(containerName string) ([]ContainerMetric, e
|
|||||||
WITH recent_metrics AS (
|
WITH recent_metrics AS (
|
||||||
SELECT metrics_json
|
SELECT metrics_json
|
||||||
FROM container_metrics
|
FROM container_metrics
|
||||||
WHERE container_name LIKE ? || '%'
|
WHERE container_name = ?
|
||||||
ORDER BY timestamp DESC
|
ORDER BY timestamp DESC
|
||||||
)
|
)
|
||||||
SELECT metrics_json FROM recent_metrics ORDER BY json_extract(metrics_json, '$.timestamp') ASC
|
SELECT metrics_json FROM recent_metrics ORDER BY json_extract(metrics_json, '$.timestamp') ASC
|
||||||
|
|||||||
@@ -57,7 +57,6 @@
|
|||||||
"drizzle-dbml-generator": "0.10.0",
|
"drizzle-dbml-generator": "0.10.0",
|
||||||
"drizzle-orm": "^0.39.3",
|
"drizzle-orm": "^0.39.3",
|
||||||
"drizzle-zod": "0.5.1",
|
"drizzle-zod": "0.5.1",
|
||||||
"hi-base32": "^0.5.1",
|
|
||||||
"yaml": "2.8.1",
|
"yaml": "2.8.1",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"micromatch": "4.0.8",
|
"micromatch": "4.0.8",
|
||||||
@@ -67,7 +66,6 @@
|
|||||||
"node-schedule": "2.1.1",
|
"node-schedule": "2.1.1",
|
||||||
"nodemailer": "6.9.14",
|
"nodemailer": "6.9.14",
|
||||||
"octokit": "3.1.2",
|
"octokit": "3.1.2",
|
||||||
"otpauth": "^9.4.0",
|
|
||||||
"pino": "9.4.0",
|
"pino": "9.4.0",
|
||||||
"pino-pretty": "11.2.2",
|
"pino-pretty": "11.2.2",
|
||||||
"postgres": "3.4.4",
|
"postgres": "3.4.4",
|
||||||
@@ -80,9 +78,11 @@
|
|||||||
"ssh2": "1.15.0",
|
"ssh2": "1.15.0",
|
||||||
"toml": "3.0.0",
|
"toml": "3.0.0",
|
||||||
"ws": "8.16.0",
|
"ws": "8.16.0",
|
||||||
"zod": "^3.25.32"
|
"zod": "^3.25.32",
|
||||||
|
"semver": "7.7.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/semver": "7.7.1",
|
||||||
"@types/adm-zip": "^0.5.7",
|
"@types/adm-zip": "^0.5.7",
|
||||||
"@types/bcrypt": "5.0.2",
|
"@types/bcrypt": "5.0.2",
|
||||||
"@types/dockerode": "3.3.23",
|
"@types/dockerode": "3.3.23",
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import {
|
|||||||
execAsync,
|
execAsync,
|
||||||
execAsyncRemote,
|
execAsyncRemote,
|
||||||
} from "@dokploy/server/utils/process/execAsync";
|
} from "@dokploy/server/utils/process/execAsync";
|
||||||
|
import semver from "semver";
|
||||||
import {
|
import {
|
||||||
initializeStandaloneTraefik,
|
initializeStandaloneTraefik,
|
||||||
initializeTraefikService,
|
initializeTraefikService,
|
||||||
type TraefikOptions,
|
type TraefikOptions,
|
||||||
} from "../setup/traefik-setup";
|
} from "../setup/traefik-setup";
|
||||||
|
|
||||||
export interface IUpdateData {
|
export interface IUpdateData {
|
||||||
latestVersion: string | null;
|
latestVersion: string | null;
|
||||||
updateAvailable: boolean;
|
updateAvailable: boolean;
|
||||||
@@ -55,56 +55,95 @@ export const getServiceImageDigest = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** Returns latest version number and information whether server update is available by comparing current image's digest against digest for provided image tag via Docker hub API. */
|
/** Returns latest version number and information whether server update is available by comparing current image's digest against digest for provided image tag via Docker hub API. */
|
||||||
export const getUpdateData = async (): Promise<IUpdateData> => {
|
export const getUpdateData = async (
|
||||||
let currentDigest: string;
|
currentVersion: string,
|
||||||
|
): Promise<IUpdateData> => {
|
||||||
try {
|
try {
|
||||||
currentDigest = await getServiceImageDigest();
|
const baseUrl =
|
||||||
} catch (error) {
|
"https://hub.docker.com/v2/repositories/dokploy/dokploy/tags";
|
||||||
// TODO: Docker versions 29.0.0 change the way to get the service image digest, so we need to update this in the future we upgrade to that version.
|
let url: string | null = `${baseUrl}?page_size=100`;
|
||||||
return DEFAULT_UPDATE_DATA;
|
let allResults: { digest: string; name: string }[] = [];
|
||||||
}
|
|
||||||
|
|
||||||
const baseUrl = "https://hub.docker.com/v2/repositories/dokploy/dokploy/tags";
|
// Fetch all tags from Docker Hub
|
||||||
let url: string | null = `${baseUrl}?page_size=100`;
|
while (url) {
|
||||||
let allResults: { digest: string; name: string }[] = [];
|
const response = await fetch(url, {
|
||||||
while (url) {
|
method: "GET",
|
||||||
const response = await fetch(url, {
|
headers: { "Content-Type": "application/json" },
|
||||||
method: "GET",
|
});
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = (await response.json()) as {
|
const data = (await response.json()) as {
|
||||||
next: string | null;
|
next: string | null;
|
||||||
results: { digest: string; name: string }[];
|
results: { digest: string; name: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
allResults = allResults.concat(data.results);
|
allResults = allResults.concat(data.results);
|
||||||
url = data?.next;
|
url = data?.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageTag = getDokployImageTag();
|
const currentImageTag = getDokployImageTag();
|
||||||
const searchedDigest = allResults.find((t) => t.name === imageTag)?.digest;
|
|
||||||
|
|
||||||
if (!searchedDigest) {
|
// Special handling for canary and feature branches
|
||||||
return DEFAULT_UPDATE_DATA;
|
// For development versions (canary/feature), don't perform update checks
|
||||||
}
|
// These are unstable versions that change frequently, and users on these
|
||||||
|
// branches are expected to manually manage updates
|
||||||
|
if (currentImageTag === "canary" || currentImageTag === "feature") {
|
||||||
|
const currentDigest = await getServiceImageDigest();
|
||||||
|
const latestDigest = allResults.find(
|
||||||
|
(t) => t.name === currentImageTag,
|
||||||
|
)?.digest;
|
||||||
|
if (!latestDigest) {
|
||||||
|
return DEFAULT_UPDATE_DATA;
|
||||||
|
}
|
||||||
|
if (currentDigest !== latestDigest) {
|
||||||
|
return {
|
||||||
|
latestVersion: currentImageTag,
|
||||||
|
updateAvailable: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
latestVersion: currentImageTag,
|
||||||
|
updateAvailable: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (imageTag === "latest") {
|
// For stable versions, use semver comparison
|
||||||
const versionedTag = allResults.find(
|
// Find the "latest" tag and get its digest
|
||||||
(t) => t.digest === searchedDigest && t.name.startsWith("v"),
|
const latestTag = allResults.find((t) => t.name === "latest");
|
||||||
);
|
|
||||||
|
|
||||||
if (!versionedTag) {
|
if (!latestTag) {
|
||||||
return DEFAULT_UPDATE_DATA;
|
return DEFAULT_UPDATE_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name: latestVersion, digest } = versionedTag;
|
// Find the versioned tag (v0.x.x) that has the same digest as "latest"
|
||||||
const updateAvailable = digest !== currentDigest;
|
const latestVersionTag = allResults.find(
|
||||||
|
(t) => t.digest === latestTag.digest && t.name.startsWith("v"),
|
||||||
|
);
|
||||||
|
|
||||||
return { latestVersion, updateAvailable };
|
if (!latestVersionTag) {
|
||||||
|
return DEFAULT_UPDATE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestVersion = latestVersionTag.name;
|
||||||
|
|
||||||
|
// Use semver to compare versions for stable releases
|
||||||
|
const cleanedCurrent = semver.clean(currentVersion);
|
||||||
|
const cleanedLatest = semver.clean(latestVersion);
|
||||||
|
|
||||||
|
if (!cleanedCurrent || !cleanedLatest) {
|
||||||
|
return DEFAULT_UPDATE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the latest version is greater than the current version
|
||||||
|
const updateAvailable = semver.gt(cleanedLatest, cleanedCurrent);
|
||||||
|
|
||||||
|
return {
|
||||||
|
latestVersion,
|
||||||
|
updateAvailable,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching update data:", error);
|
||||||
|
return DEFAULT_UPDATE_DATA;
|
||||||
}
|
}
|
||||||
const updateAvailable = searchedDigest !== currentDigest;
|
|
||||||
return { latestVersion: imageTag, updateAvailable };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TreeDataItem {
|
interface TreeDataItem {
|
||||||
@@ -254,11 +293,22 @@ fi`;
|
|||||||
export const reloadDockerResource = async (
|
export const reloadDockerResource = async (
|
||||||
resourceName: string,
|
resourceName: string,
|
||||||
serverId?: string,
|
serverId?: string,
|
||||||
|
version?: string,
|
||||||
) => {
|
) => {
|
||||||
const resourceType = await getDockerResourceType(resourceName, serverId);
|
const resourceType = await getDockerResourceType(resourceName, serverId);
|
||||||
let command = "";
|
let command = "";
|
||||||
if (resourceType === "service") {
|
if (resourceType === "service") {
|
||||||
command = `docker service update --force ${resourceName}`;
|
if (resourceName === "dokploy") {
|
||||||
|
const currentImageTag = getDokployImageTag();
|
||||||
|
let imageTag = version;
|
||||||
|
if (currentImageTag === "canary" || currentImageTag === "feature") {
|
||||||
|
imageTag = currentImageTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
command = `docker service update --force --image dokploy/dokploy:${imageTag} ${resourceName}`;
|
||||||
|
} else {
|
||||||
|
command = `docker service update --force ${resourceName}`;
|
||||||
|
}
|
||||||
} else if (resourceType === "standalone") {
|
} else if (resourceType === "standalone") {
|
||||||
command = `docker restart ${resourceName}`;
|
command = `docker restart ${resourceName}`;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Generated
+27
-60
@@ -51,9 +51,6 @@ importers:
|
|||||||
'@hono/zod-validator':
|
'@hono/zod-validator':
|
||||||
specifier: 0.3.0
|
specifier: 0.3.0
|
||||||
version: 0.3.0([email protected])([email protected])
|
version: 0.3.0([email protected])([email protected])
|
||||||
'@nerimity/mimiqueue':
|
|
||||||
specifier: 1.2.3
|
|
||||||
version: 1.2.3([email protected])
|
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.4.5
|
specifier: ^16.4.5
|
||||||
version: 16.4.5
|
version: 16.4.5
|
||||||
@@ -313,9 +310,6 @@ importers:
|
|||||||
fancy-ansi:
|
fancy-ansi:
|
||||||
specifier: ^0.1.3
|
specifier: ^0.1.3
|
||||||
version: 0.1.3
|
version: 0.1.3
|
||||||
hi-base32:
|
|
||||||
specifier: ^0.5.1
|
|
||||||
version: 0.5.1
|
|
||||||
i18next:
|
i18next:
|
||||||
specifier: ^23.16.8
|
specifier: ^23.16.8
|
||||||
version: 23.16.8
|
version: 23.16.8
|
||||||
@@ -364,9 +358,6 @@ importers:
|
|||||||
octokit:
|
octokit:
|
||||||
specifier: 3.1.2
|
specifier: 3.1.2
|
||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
otpauth:
|
|
||||||
specifier: ^9.4.0
|
|
||||||
version: 9.4.0
|
|
||||||
pino:
|
pino:
|
||||||
specifier: 9.4.0
|
specifier: 9.4.0
|
||||||
version: 9.4.0
|
version: 9.4.0
|
||||||
@@ -406,6 +397,9 @@ importers:
|
|||||||
recharts:
|
recharts:
|
||||||
specifier: ^2.15.3
|
specifier: ^2.15.3
|
||||||
version: 2.15.3([email protected]([email protected]))([email protected])
|
version: 2.15.3([email protected]([email protected]))([email protected])
|
||||||
|
semver:
|
||||||
|
specifier: 7.7.3
|
||||||
|
version: 7.7.3
|
||||||
shell-quote:
|
shell-quote:
|
||||||
specifier: ^1.8.1
|
specifier: ^1.8.1
|
||||||
version: 1.8.2
|
version: 1.8.2
|
||||||
@@ -491,6 +485,9 @@ importers:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: 18.3.0
|
specifier: 18.3.0
|
||||||
version: 18.3.0
|
version: 18.3.0
|
||||||
|
'@types/semver':
|
||||||
|
specifier: 7.7.1
|
||||||
|
version: 7.7.1
|
||||||
'@types/shell-quote':
|
'@types/shell-quote':
|
||||||
specifier: ^1.7.5
|
specifier: ^1.7.5
|
||||||
version: 1.7.5
|
version: 1.7.5
|
||||||
@@ -678,9 +675,6 @@ importers:
|
|||||||
drizzle-zod:
|
drizzle-zod:
|
||||||
specifier: 0.5.1
|
specifier: 0.5.1
|
||||||
version: 0.5.1([email protected](@opentelemetry/[email protected])(@types/[email protected])([email protected])([email protected]))([email protected])
|
version: 0.5.1([email protected](@opentelemetry/[email protected])(@types/[email protected])([email protected])([email protected]))([email protected])
|
||||||
hi-base32:
|
|
||||||
specifier: ^0.5.1
|
|
||||||
version: 0.5.1
|
|
||||||
lodash:
|
lodash:
|
||||||
specifier: 4.17.21
|
specifier: 4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
@@ -705,9 +699,6 @@ importers:
|
|||||||
octokit:
|
octokit:
|
||||||
specifier: 3.1.2
|
specifier: 3.1.2
|
||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
otpauth:
|
|
||||||
specifier: ^9.4.0
|
|
||||||
version: 9.4.0
|
|
||||||
pino:
|
pino:
|
||||||
specifier: 9.4.0
|
specifier: 9.4.0
|
||||||
version: 9.4.0
|
version: 9.4.0
|
||||||
@@ -729,6 +720,9 @@ importers:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: 18.2.0
|
specifier: 18.2.0
|
||||||
version: 18.2.0([email protected])
|
version: 18.2.0([email protected])
|
||||||
|
semver:
|
||||||
|
specifier: 7.7.3
|
||||||
|
version: 7.7.3
|
||||||
shell-quote:
|
shell-quote:
|
||||||
specifier: ^1.8.1
|
specifier: ^1.8.1
|
||||||
version: 1.8.2
|
version: 1.8.2
|
||||||
@@ -784,6 +778,9 @@ importers:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: 18.3.0
|
specifier: 18.3.0
|
||||||
version: 18.3.0
|
version: 18.3.0
|
||||||
|
'@types/semver':
|
||||||
|
specifier: 7.7.1
|
||||||
|
version: 7.7.1
|
||||||
'@types/shell-quote':
|
'@types/shell-quote':
|
||||||
specifier: ^1.7.5
|
specifier: ^1.7.5
|
||||||
version: 1.7.5
|
version: 1.7.5
|
||||||
@@ -1951,11 +1948,6 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@nerimity/[email protected]':
|
|
||||||
resolution: {integrity: sha512-WPoGe417P+S0FLfl3psRBI5adcAWXb917vCF1qD2yGZ1ggBEnMH6UrUK464gzJEOpAlGt8BBbIp0tgCEazZ47A==}
|
|
||||||
peerDependencies:
|
|
||||||
redis: ^4.7.0
|
|
||||||
|
|
||||||
'@next/[email protected]':
|
'@next/[email protected]':
|
||||||
resolution: {integrity: sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==}
|
resolution: {integrity: sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==}
|
||||||
|
|
||||||
@@ -4060,6 +4052,9 @@ packages:
|
|||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-eLgbR5KwUh8+6pngBDxS32MymdCsCHnGtwHTrC0GDorbc7NbcnkZAWptDLgZiRk9VRas+B6TyRgPDucq4zRs8g==}
|
resolution: {integrity: sha512-eLgbR5KwUh8+6pngBDxS32MymdCsCHnGtwHTrC0GDorbc7NbcnkZAWptDLgZiRk9VRas+B6TyRgPDucq4zRs8g==}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==}
|
resolution: {integrity: sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==}
|
||||||
|
|
||||||
@@ -4302,9 +4297,6 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
|
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
|
||||||
|
|
||||||
[email protected]:
|
|
||||||
resolution: {integrity: sha512-3DpDtxkKO0O/FPlWbk/CrbexjuSxWm1CH1bXlVNVyMBIkKHhT5D85gzHmGJokG3ibNGWQ7pHBmStxUW/z/0LYQ==}
|
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||||
|
|
||||||
@@ -5389,9 +5381,6 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
|
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
|
||||||
|
|
||||||
[email protected]:
|
|
||||||
resolution: {integrity: sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==}
|
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
|
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
|
||||||
|
|
||||||
@@ -6426,9 +6415,6 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
|
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
|
||||||
|
|
||||||
[email protected]:
|
|
||||||
resolution: {integrity: sha512-fHIfzIG5RqCkK9cmV8WU+dPQr9/ebR5QOwGZn2JAr1RQF+lmAuLL2YdtdqvmBjNmgJlYk3KZ4a0XokaEhg1Jsw==}
|
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
|
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
|
||||||
engines: {node: '>=12.20'}
|
engines: {node: '>=12.20'}
|
||||||
@@ -7087,11 +7073,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
[email protected]:
|
|
||||||
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -8119,7 +8100,7 @@ snapshots:
|
|||||||
'@commitlint/[email protected]':
|
'@commitlint/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@commitlint/types': 19.8.1
|
'@commitlint/types': 19.8.1
|
||||||
semver: 7.7.2
|
semver: 7.7.3
|
||||||
|
|
||||||
'@commitlint/[email protected]':
|
'@commitlint/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8736,7 +8717,7 @@ snapshots:
|
|||||||
nopt: 5.0.0
|
nopt: 5.0.0
|
||||||
npmlog: 5.0.1
|
npmlog: 5.0.1
|
||||||
rimraf: 3.0.2
|
rimraf: 3.0.2
|
||||||
semver: 7.7.2
|
semver: 7.7.3
|
||||||
tar: 6.2.1
|
tar: 6.2.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
@@ -8762,11 +8743,6 @@ snapshots:
|
|||||||
'@msgpackr-extract/[email protected]':
|
'@msgpackr-extract/[email protected]':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@nerimity/[email protected]([email protected])':
|
|
||||||
dependencies:
|
|
||||||
async-await-queue: 2.1.4
|
|
||||||
redis: 4.7.0
|
|
||||||
|
|
||||||
'@next/[email protected]': {}
|
'@next/[email protected]': {}
|
||||||
|
|
||||||
'@next/[email protected]':
|
'@next/[email protected]':
|
||||||
@@ -9327,7 +9303,7 @@ snapshots:
|
|||||||
'@opentelemetry/instrumentation': 0.57.2(@opentelemetry/[email protected])
|
'@opentelemetry/instrumentation': 0.57.2(@opentelemetry/[email protected])
|
||||||
'@opentelemetry/semantic-conventions': 1.28.0
|
'@opentelemetry/semantic-conventions': 1.28.0
|
||||||
forwarded-parse: 2.1.2
|
forwarded-parse: 2.1.2
|
||||||
semver: 7.7.2
|
semver: 7.7.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -9528,7 +9504,7 @@ snapshots:
|
|||||||
'@types/shimmer': 1.2.0
|
'@types/shimmer': 1.2.0
|
||||||
import-in-the-middle: 1.14.2
|
import-in-the-middle: 1.14.2
|
||||||
require-in-the-middle: 7.5.2
|
require-in-the-middle: 7.5.2
|
||||||
semver: 7.7.2
|
semver: 7.7.3
|
||||||
shimmer: 1.2.1
|
shimmer: 1.2.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -9673,7 +9649,7 @@ snapshots:
|
|||||||
'@opentelemetry/propagator-b3': 1.30.1(@opentelemetry/[email protected])
|
'@opentelemetry/propagator-b3': 1.30.1(@opentelemetry/[email protected])
|
||||||
'@opentelemetry/propagator-jaeger': 1.30.1(@opentelemetry/[email protected])
|
'@opentelemetry/propagator-jaeger': 1.30.1(@opentelemetry/[email protected])
|
||||||
'@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/[email protected])
|
'@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/[email protected])
|
||||||
semver: 7.7.2
|
semver: 7.7.3
|
||||||
|
|
||||||
'@opentelemetry/[email protected]': {}
|
'@opentelemetry/[email protected]': {}
|
||||||
|
|
||||||
@@ -11421,6 +11397,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.17.51
|
'@types/node': 20.17.51
|
||||||
|
|
||||||
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
@@ -11673,8 +11651,6 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -11820,7 +11796,7 @@ snapshots:
|
|||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
msgpackr: 1.11.4
|
msgpackr: 1.11.4
|
||||||
node-abort-controller: 3.1.1
|
node-abort-controller: 3.1.1
|
||||||
semver: 7.7.2
|
semver: 7.7.3
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
uuid: 9.0.1
|
uuid: 9.0.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -12336,7 +12312,7 @@ snapshots:
|
|||||||
'@one-ini/wasm': 0.1.1
|
'@one-ini/wasm': 0.1.1
|
||||||
commander: 10.0.1
|
commander: 10.0.1
|
||||||
minimatch: 9.0.1
|
minimatch: 9.0.1
|
||||||
semver: 7.7.2
|
semver: 7.7.3
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -12650,7 +12626,7 @@ snapshots:
|
|||||||
'@petamoriken/float16': 3.9.2
|
'@petamoriken/float16': 3.9.2
|
||||||
debug: 4.4.1
|
debug: 4.4.1
|
||||||
env-paths: 3.0.0
|
env-paths: 3.0.0
|
||||||
semver: 7.7.2
|
semver: 7.7.3
|
||||||
shell-quote: 1.8.2
|
shell-quote: 1.8.2
|
||||||
which: 4.0.0
|
which: 4.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -12824,8 +12800,6 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -13138,7 +13112,7 @@ snapshots:
|
|||||||
lodash.isstring: 4.0.1
|
lodash.isstring: 4.0.1
|
||||||
lodash.once: 4.1.1
|
lodash.once: 4.1.1
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
semver: 7.7.2
|
semver: 7.7.3
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -13962,10 +13936,6 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
|
||||||
dependencies:
|
|
||||||
'@noble/hashes': 1.7.1
|
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
@@ -14674,10 +14644,7 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected].2: {}
|
[email protected].3: {}
|
||||||
|
|
||||||
[email protected]:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user