mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-01 20:15:29 +02:00
Compare commits
9 Commits
v0.28.2
...
feat/intro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
847cf6118b | ||
|
|
275d7ee586 | ||
|
|
29aae91959 | ||
|
|
ab6cb7349e | ||
|
|
e36c665e02 | ||
|
|
f3dc480b70 | ||
|
|
6758ef1542 | ||
|
|
bd4ff2dbf2 | ||
|
|
579a2262bf |
@@ -24,6 +24,7 @@ import { Progress } from "@/components/ui/progress";
|
|||||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, 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";
|
||||||
|
import { ShowProviders } from "./show-providers";
|
||||||
|
|
||||||
const stripePromise = loadStripe(
|
const stripePromise = loadStripe(
|
||||||
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
|
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
|
||||||
@@ -151,44 +152,25 @@ export const ShowBilling = () => {
|
|||||||
<Loader2 className="animate-spin" />
|
<Loader2 className="animate-spin" />
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<>
|
products?.map((product) => {
|
||||||
{products?.map((product) => {
|
const featured = true;
|
||||||
const featured = true;
|
return (
|
||||||
return (
|
<div key={product.id}>
|
||||||
<div key={product.id}>
|
<section
|
||||||
<section
|
className={clsx(
|
||||||
className={clsx(
|
"flex flex-col rounded-3xl border-dashed border-2 px-4 max-w-sm",
|
||||||
"flex flex-col rounded-3xl border-dashed border-2 px-4 max-w-sm",
|
featured
|
||||||
featured
|
? "order-first border py-8 lg:order-none"
|
||||||
? "order-first border py-8 lg:order-none"
|
: "lg:py-8",
|
||||||
: "lg:py-8",
|
)}
|
||||||
)}
|
>
|
||||||
>
|
{isAnnual && (
|
||||||
{isAnnual && (
|
<div className="mb-4 flex flex-row items-center gap-2">
|
||||||
<div className="mb-4 flex flex-row items-center gap-2">
|
<Badge>Recommended 🚀</Badge>
|
||||||
<Badge>Recommended 🚀</Badge>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
{isAnnual ? (
|
||||||
{isAnnual ? (
|
<div className="flex flex-row gap-2 items-center">
|
||||||
<div className="flex flex-row gap-2 items-center">
|
|
||||||
<p className="text-2xl font-semibold tracking-tight text-primary ">
|
|
||||||
${" "}
|
|
||||||
{calculatePrice(
|
|
||||||
serverQuantity,
|
|
||||||
isAnnual,
|
|
||||||
).toFixed(2)}{" "}
|
|
||||||
USD
|
|
||||||
</p>
|
|
||||||
|
|
|
||||||
<p className="text-base font-semibold tracking-tight text-muted-foreground">
|
|
||||||
${" "}
|
|
||||||
{(
|
|
||||||
calculatePrice(serverQuantity, isAnnual) / 12
|
|
||||||
).toFixed(2)}{" "}
|
|
||||||
/ Month USD
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className="text-2xl font-semibold tracking-tight text-primary ">
|
<p className="text-2xl font-semibold tracking-tight text-primary ">
|
||||||
${" "}
|
${" "}
|
||||||
{calculatePrice(serverQuantity, isAnnual).toFixed(
|
{calculatePrice(serverQuantity, isAnnual).toFixed(
|
||||||
@@ -196,127 +178,146 @@ export const ShowBilling = () => {
|
|||||||
)}{" "}
|
)}{" "}
|
||||||
USD
|
USD
|
||||||
</p>
|
</p>
|
||||||
)}
|
|
|
||||||
<h3 className="mt-5 font-medium text-lg text-primary">
|
<p className="text-base font-semibold tracking-tight text-muted-foreground">
|
||||||
{product.name}
|
${" "}
|
||||||
</h3>
|
{(
|
||||||
<p
|
calculatePrice(serverQuantity, isAnnual) / 12
|
||||||
className={clsx(
|
).toFixed(2)}{" "}
|
||||||
"text-sm",
|
/ Month USD
|
||||||
featured ? "text-white" : "text-slate-400",
|
</p>
|
||||||
)}
|
</div>
|
||||||
>
|
) : (
|
||||||
{product.description}
|
<p className="text-2xl font-semibold tracking-tight text-primary ">
|
||||||
|
${" "}
|
||||||
|
{calculatePrice(serverQuantity, isAnnual).toFixed(
|
||||||
|
2,
|
||||||
|
)}{" "}
|
||||||
|
USD
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
|
<h3 className="mt-5 font-medium text-lg text-primary">
|
||||||
|
{product.name}
|
||||||
|
</h3>
|
||||||
|
<p
|
||||||
|
className={clsx(
|
||||||
|
"text-sm",
|
||||||
|
featured ? "text-white" : "text-slate-400",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{product.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
className={clsx(
|
className={clsx(
|
||||||
" mt-4 flex flex-col gap-y-2 text-sm",
|
" mt-4 flex flex-col gap-y-2 text-sm",
|
||||||
featured ? "text-white" : "text-slate-200",
|
featured ? "text-white" : "text-slate-200",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{[
|
||||||
|
"All the features of Dokploy",
|
||||||
|
"Unlimited deployments",
|
||||||
|
"Self-hosted on your own infrastructure",
|
||||||
|
"Full access to all deployment features",
|
||||||
|
"Dokploy integration",
|
||||||
|
"Backups",
|
||||||
|
"All Incoming features",
|
||||||
|
].map((feature) => (
|
||||||
|
<li
|
||||||
|
key={feature}
|
||||||
|
className="flex text-muted-foreground"
|
||||||
|
>
|
||||||
|
<CheckIcon />
|
||||||
|
<span className="ml-4">{feature}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<div className="flex flex-col gap-2 mt-4">
|
||||||
|
<div className="flex items-center gap-2 justify-center">
|
||||||
|
<span className="text-sm text-muted-foreground">
|
||||||
|
{serverQuantity} Servers
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Button
|
||||||
|
disabled={serverQuantity <= 1}
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
if (serverQuantity <= 1) return;
|
||||||
|
|
||||||
|
setServerQuantity(serverQuantity - 1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MinusIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<NumberInput
|
||||||
|
value={serverQuantity}
|
||||||
|
onChange={(e) => {
|
||||||
|
setServerQuantity(
|
||||||
|
e.target.value as unknown as number,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setServerQuantity(serverQuantity + 1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
data?.subscriptions &&
|
||||||
|
data?.subscriptions?.length > 0
|
||||||
|
? "justify-between"
|
||||||
|
: "justify-end",
|
||||||
|
"flex flex-row items-center gap-2 mt-4",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{[
|
{admin?.user.stripeCustomerId && (
|
||||||
"All the features of Dokploy",
|
|
||||||
"Unlimited deployments",
|
|
||||||
"Self-hosted on your own infrastructure",
|
|
||||||
"Full access to all deployment features",
|
|
||||||
"Dokploy integration",
|
|
||||||
"Backups",
|
|
||||||
"All Incoming features",
|
|
||||||
].map((feature) => (
|
|
||||||
<li
|
|
||||||
key={feature}
|
|
||||||
className="flex text-muted-foreground"
|
|
||||||
>
|
|
||||||
<CheckIcon />
|
|
||||||
<span className="ml-4">{feature}</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
<div className="flex flex-col gap-2 mt-4">
|
|
||||||
<div className="flex items-center gap-2 justify-center">
|
|
||||||
<span className="text-sm text-muted-foreground">
|
|
||||||
{serverQuantity} Servers
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Button
|
<Button
|
||||||
disabled={serverQuantity <= 1}
|
variant="secondary"
|
||||||
variant="outline"
|
className="w-full"
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
if (serverQuantity <= 1) return;
|
const session =
|
||||||
|
await createCustomerPortalSession();
|
||||||
|
|
||||||
setServerQuantity(serverQuantity - 1);
|
window.open(session.url);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MinusIcon className="h-4 w-4" />
|
Manage Subscription
|
||||||
</Button>
|
</Button>
|
||||||
<NumberInput
|
)}
|
||||||
value={serverQuantity}
|
|
||||||
onChange={(e) => {
|
|
||||||
setServerQuantity(
|
|
||||||
e.target.value as unknown as number,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
{data?.subscriptions?.length === 0 && (
|
||||||
variant="outline"
|
<div className="justify-end w-full">
|
||||||
onClick={() => {
|
|
||||||
setServerQuantity(serverQuantity + 1);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PlusIcon className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
data?.subscriptions &&
|
|
||||||
data?.subscriptions?.length > 0
|
|
||||||
? "justify-between"
|
|
||||||
: "justify-end",
|
|
||||||
"flex flex-row items-center gap-2 mt-4",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{admin?.user.stripeCustomerId && (
|
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const session =
|
handleCheckout(product.id);
|
||||||
await createCustomerPortalSession();
|
|
||||||
|
|
||||||
window.open(session.url);
|
|
||||||
}}
|
}}
|
||||||
|
disabled={serverQuantity < 1}
|
||||||
>
|
>
|
||||||
Manage Subscription
|
Subscribe
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
{data?.subscriptions?.length === 0 && (
|
|
||||||
<div className="justify-end w-full">
|
|
||||||
<Button
|
|
||||||
className="w-full"
|
|
||||||
onClick={async () => {
|
|
||||||
handleCheckout(product.id);
|
|
||||||
}}
|
|
||||||
disabled={serverQuantity < 1}
|
|
||||||
>
|
|
||||||
Subscribe
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
);
|
</div>
|
||||||
})}
|
);
|
||||||
</>
|
})
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
<div className="flex flex-col gap-4 pb-10">
|
||||||
|
<ShowProviders />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,355 @@
|
|||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import {
|
||||||
|
Cpu,
|
||||||
|
EuroIcon,
|
||||||
|
HardDrive,
|
||||||
|
Loader2,
|
||||||
|
MapPin,
|
||||||
|
MemoryStick,
|
||||||
|
Zap,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import * as z from "zod";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
|
// Function to classify servers by type
|
||||||
|
function getServerCategory(cpuType: string) {
|
||||||
|
if (cpuType === "shared") {
|
||||||
|
return {
|
||||||
|
category: "Shared CPU",
|
||||||
|
icon: Cpu,
|
||||||
|
badge: "shared",
|
||||||
|
color: "bg-blue-500",
|
||||||
|
description: "Perfect for small and medium projects",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
category: "Dedicated CPU",
|
||||||
|
icon: Zap,
|
||||||
|
badge: "dedicated",
|
||||||
|
color: "bg-purple-500",
|
||||||
|
description: "Maximum performance for demanding applications",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
location: z.string().min(1, "Please select a location"),
|
||||||
|
architecture: z.enum(["x86", "arm"], {
|
||||||
|
required_error: "Please select an architecture",
|
||||||
|
}),
|
||||||
|
selectedServerId: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
export const ShowHetznerProviders = () => {
|
||||||
|
const { data: serverTypesData, isLoading: isLoadingTypes } =
|
||||||
|
api.hetzner.serverTypes.useQuery();
|
||||||
|
const { data: locationsData, isLoading: isLoadingLocations } =
|
||||||
|
api.hetzner.locations.useQuery();
|
||||||
|
|
||||||
|
const form = useForm<FormValues>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
location: "",
|
||||||
|
architecture: "x86",
|
||||||
|
selectedServerId: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedLocation = form.watch("location");
|
||||||
|
const selectedArchitecture = form.watch("architecture");
|
||||||
|
|
||||||
|
if (isLoadingTypes || isLoadingLocations) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center p-4">
|
||||||
|
<Loader2 className="h-6 w-6 animate-spin" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const locations = locationsData?.locations ?? [];
|
||||||
|
const serverTypes = serverTypesData?.server_types ?? [];
|
||||||
|
|
||||||
|
// Filter server types by selected location AND architecture
|
||||||
|
const filteredServerTypes = serverTypes.filter(
|
||||||
|
(type) =>
|
||||||
|
type.prices.some((price) => price.location === selectedLocation) &&
|
||||||
|
type.architecture === selectedArchitecture,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Group by CPU type (shared/dedicated)
|
||||||
|
const sharedServers = filteredServerTypes.filter(
|
||||||
|
(type) => type.cpu_type === "shared",
|
||||||
|
);
|
||||||
|
const dedicatedServers = filteredServerTypes.filter(
|
||||||
|
(type) => type.cpu_type === "dedicated",
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderServerGrid = (
|
||||||
|
servers: typeof serverTypes,
|
||||||
|
category: ReturnType<typeof getServerCategory>,
|
||||||
|
) => {
|
||||||
|
if (!servers.length) return null;
|
||||||
|
const IconComponent = category.icon;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<IconComponent className="h-5 w-5" />
|
||||||
|
<h3 className="text-lg font-semibold">{category.category}</h3>
|
||||||
|
<Badge variant="outline" className={`text-white ${category.color}`}>
|
||||||
|
{category.badge}
|
||||||
|
</Badge>
|
||||||
|
<Badge variant="outline" className="text-xs text-muted-foreground">
|
||||||
|
Sorted by price
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground mb-4">
|
||||||
|
{category.description}
|
||||||
|
</p>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="selectedServerId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
|
||||||
|
>
|
||||||
|
{servers.map((server) => (
|
||||||
|
<div key={server.id} className="relative">
|
||||||
|
<RadioGroupItem
|
||||||
|
value={server.id.toString()}
|
||||||
|
id={`server-${server.id}`}
|
||||||
|
className="absolute right-4 top-4 z-10"
|
||||||
|
/>
|
||||||
|
<label htmlFor={`server-${server.id}`}>
|
||||||
|
<Card
|
||||||
|
className={`relative bg-transparent transition-all duration-200 cursor-pointer ${
|
||||||
|
field.value === server.id.toString()
|
||||||
|
? "border-primary bg-primary/5"
|
||||||
|
: "hover:bg-primary/5"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{server.name}</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{server.description}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Cpu className="h-4 w-4 text-blue-500" />
|
||||||
|
<div>
|
||||||
|
<strong>Cores:</strong> {server.cores}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<MemoryStick className="h-4 w-4 text-green-500" />
|
||||||
|
<div>
|
||||||
|
<strong>Memory:</strong> {server.memory} GB
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<HardDrive className="h-4 w-4 text-purple-500" />
|
||||||
|
<div>
|
||||||
|
<strong>Disk:</strong> {server.disk} GB
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Show price for selected location */}
|
||||||
|
{server.prices
|
||||||
|
.filter((p) => p.location === selectedLocation)
|
||||||
|
.map((p) => (
|
||||||
|
<div
|
||||||
|
key={p.location}
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<EuroIcon className="h-4 w-4 text-yellow-500" />
|
||||||
|
<div>
|
||||||
|
<strong>Price (monthly):</strong> €
|
||||||
|
{Number.parseFloat(
|
||||||
|
p.price_monthly.net,
|
||||||
|
).toFixed(2)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function onSubmit(values: FormValues) {
|
||||||
|
console.log("Form submitted:", values);
|
||||||
|
// Here you can handle the form submission with the selected server
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||||
|
{/* Filters Card */}
|
||||||
|
<Card className="bg-transparent">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2 text-xl">
|
||||||
|
<MapPin className="h-5 w-5" />
|
||||||
|
Filters
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Choose a region and architecture to see location-specific pricing
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-6">
|
||||||
|
<Form {...form}>
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Region Selector */}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="location"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Region</FormLabel>
|
||||||
|
<Select
|
||||||
|
value={field.value}
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a region" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{locations.map((loc) => (
|
||||||
|
<SelectItem key={loc.id} value={loc.name}>
|
||||||
|
{loc.description}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Architecture Selector */}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="architecture"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Architecture</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
className="grid grid-cols-2 gap-4"
|
||||||
|
>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="x86" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">
|
||||||
|
x86 (Intel/AMD)
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="arm" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">ARM</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Architecture Information */}
|
||||||
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
<div className="p-4 bg-blue-900 rounded-lg border border-blue-600">
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<Cpu className="h-5 w-5 text-blue-600 mt-0.5" />
|
||||||
|
<div className="text-sm text-blue-200">
|
||||||
|
<strong>x86 Architecture:</strong> Traditional Intel/AMD
|
||||||
|
processors. Most compatible with existing software and
|
||||||
|
applications. Best choice for general-purpose workloads.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 bg-green-900 rounded-lg border border-green-600">
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<Cpu className="h-5 w-5 text-green-600 mt-0.5" />
|
||||||
|
<div className="text-sm text-green-200">
|
||||||
|
<strong>ARM Architecture:</strong> Modern, energy-efficient
|
||||||
|
processors. Excellent price-to-performance ratio. Perfect for
|
||||||
|
cloud-native and containerized applications.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Server Types Grid */}
|
||||||
|
{selectedLocation && (
|
||||||
|
<>
|
||||||
|
{renderServerGrid(sharedServers, getServerCategory("shared"))}
|
||||||
|
{renderServerGrid(dedicatedServers, getServerCategory("dedicated"))}
|
||||||
|
{sharedServers.length === 0 && dedicatedServers.length === 0 && (
|
||||||
|
<p className="text-center text-muted-foreground py-10">
|
||||||
|
No server types available for this region and architecture
|
||||||
|
combination. <br />
|
||||||
|
Please try a different region or architecture.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedLocation && form.watch("selectedServerId") && (
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button type="submit" className="bg-primary">
|
||||||
|
Continue with Selected Server
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import {
|
||||||
|
Calendar,
|
||||||
|
Cpu,
|
||||||
|
DollarSign,
|
||||||
|
Globe,
|
||||||
|
HardDrive,
|
||||||
|
Loader2,
|
||||||
|
MemoryStick,
|
||||||
|
Server,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import * as z from "zod";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
datacenter: z.string({
|
||||||
|
required_error: "Please select a datacenter",
|
||||||
|
}),
|
||||||
|
plan: z.string({
|
||||||
|
required_error: "Please select a server plan",
|
||||||
|
}),
|
||||||
|
billingPeriod: z.object(
|
||||||
|
{
|
||||||
|
unit: z.enum(["month", "year"]),
|
||||||
|
period: z.number(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required_error: "Please select a billing period",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Format billing period
|
||||||
|
function formatBillingPeriod(period: number, unit: string): string {
|
||||||
|
if (unit === "month") {
|
||||||
|
return period === 1 ? "Monthly" : `${period} months`;
|
||||||
|
}
|
||||||
|
if (unit === "year") {
|
||||||
|
return period === 1 ? "Yearly" : `${period} years`;
|
||||||
|
}
|
||||||
|
return `${period} ${unit}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert price from cents to dollars
|
||||||
|
function formatPrice(priceInCents: number): string {
|
||||||
|
return (priceInCents / 100).toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate yearly savings
|
||||||
|
function calculateSavings(
|
||||||
|
monthlyPriceInCents: number,
|
||||||
|
yearlyPriceInCents: number,
|
||||||
|
): number {
|
||||||
|
return (monthlyPriceInCents * 12 - yearlyPriceInCents) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormData = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
export const ShowHostingerServers = () => {
|
||||||
|
const { data: vpsPlans, isLoading: plansLoading } =
|
||||||
|
api.hostinger.vpsPlans.useQuery();
|
||||||
|
const { data: dataCenters, isLoading: centersLoading } =
|
||||||
|
api.hostinger.dataCenters.useQuery();
|
||||||
|
|
||||||
|
const form = useForm<FormData>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
datacenter: "",
|
||||||
|
plan: "",
|
||||||
|
billingPeriod: {
|
||||||
|
unit: "month",
|
||||||
|
period: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const isLoading = plansLoading || centersLoading;
|
||||||
|
|
||||||
|
function onSubmit(data: FormData) {
|
||||||
|
console.log(data);
|
||||||
|
// Handle form submission here
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center p-4">
|
||||||
|
<Loader2 className="h-6 w-6 animate-spin" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<Card className="bg-transparent">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Server className="h-5 w-5" />
|
||||||
|
Hostinger VPS Plans
|
||||||
|
<Badge variant="outline" className="text-xs text-muted-foreground">
|
||||||
|
Sorted by price
|
||||||
|
</Badge>
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
VPS plans with real pricing from Hostinger API
|
||||||
|
<br />
|
||||||
|
<span className="text-xs text-orange-600">
|
||||||
|
💡 Promotional pricing applies to first billing period only
|
||||||
|
</span>
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||||
|
{/* Data Center Selection */}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="datacenter"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-4">
|
||||||
|
<FormLabel>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Globe className="h-5 w-5 text-muted-foreground" />
|
||||||
|
<span className="text-lg font-medium">
|
||||||
|
Select Data Center
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"
|
||||||
|
>
|
||||||
|
{dataCenters?.map((center) => (
|
||||||
|
<FormItem key={center.id}>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem
|
||||||
|
value={center.id?.toString() || ""}
|
||||||
|
className="peer sr-only"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="p-4 rounded-lg border-2 transition-all duration-200 flex flex-col items-center gap-2 peer-aria-checked:border-purple-500 peer-aria-checked:bg-purple-50 dark:peer-aria-checked:bg-purple-950 hover:border-purple-300 cursor-pointer">
|
||||||
|
<Globe className="h-6 w-6 text-muted-foreground" />
|
||||||
|
<span className="text-sm font-medium text-center">
|
||||||
|
{center.city} / {center.continent}
|
||||||
|
</span>
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Billing Period Selection */}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="billingPeriod"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-4">
|
||||||
|
<FormLabel>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Calendar className="h-5 w-5 text-muted-foreground" />
|
||||||
|
<span className="text-lg font-medium">
|
||||||
|
Billing Period
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={(value) => {
|
||||||
|
switch (value) {
|
||||||
|
case "monthly":
|
||||||
|
field.onChange({ unit: "month", period: 1 });
|
||||||
|
break;
|
||||||
|
case "yearly":
|
||||||
|
field.onChange({ unit: "year", period: 1 });
|
||||||
|
break;
|
||||||
|
case "2years":
|
||||||
|
field.onChange({ unit: "year", period: 2 });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
defaultValue="monthly"
|
||||||
|
className="grid w-full grid-cols-3 lg:w-[600px] gap-4"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem
|
||||||
|
value="monthly"
|
||||||
|
className="peer sr-only"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="flex items-center justify-center p-3 rounded-lg border-2 transition-all duration-200 peer-aria-checked:border-purple-500 peer-aria-checked:bg-purple-50 dark:peer-aria-checked:bg-purple-950 hover:border-purple-300 cursor-pointer">
|
||||||
|
Monthly Billing
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem
|
||||||
|
value="yearly"
|
||||||
|
className="peer sr-only"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="flex items-center justify-center p-3 rounded-lg border-2 transition-all duration-200 peer-aria-checked:border-purple-500 peer-aria-checked:bg-purple-50 dark:peer-aria-checked:bg-purple-950 hover:border-purple-300 cursor-pointer">
|
||||||
|
Annual Billing
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem
|
||||||
|
value="2years"
|
||||||
|
className="peer sr-only"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="flex items-center justify-center p-3 rounded-lg border-2 transition-all duration-200 peer-aria-checked:border-purple-500 peer-aria-checked:bg-purple-50 dark:peer-aria-checked:bg-purple-950 hover:border-purple-300 cursor-pointer">
|
||||||
|
2 Year Billing
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* VPS Plans Selection */}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="plan"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="space-y-4">
|
||||||
|
<FormLabel>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Server className="h-5 w-5 text-muted-foreground" />
|
||||||
|
<span className="text-lg font-medium">
|
||||||
|
Select Server Plan
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
className="grid grid-cols-1 lg:grid-cols-2 gap-6"
|
||||||
|
>
|
||||||
|
{vpsPlans
|
||||||
|
?.sort((a, b) => {
|
||||||
|
const billingPeriod = form.watch("billingPeriod");
|
||||||
|
const priceA =
|
||||||
|
a.prices?.find(
|
||||||
|
(p) =>
|
||||||
|
p.period_unit === billingPeriod.unit &&
|
||||||
|
p.period === billingPeriod.period,
|
||||||
|
)?.price || 0;
|
||||||
|
const priceB =
|
||||||
|
b.prices?.find(
|
||||||
|
(p) =>
|
||||||
|
p.period_unit === billingPeriod.unit &&
|
||||||
|
p.period === billingPeriod.period,
|
||||||
|
)?.price || 0;
|
||||||
|
return priceA - priceB;
|
||||||
|
})
|
||||||
|
?.map((plan) => {
|
||||||
|
const monthlyPrice =
|
||||||
|
plan.prices?.find(
|
||||||
|
(p) =>
|
||||||
|
p.period === 1 && p.period_unit === "month",
|
||||||
|
)?.price || 0;
|
||||||
|
|
||||||
|
const selectedPrice = plan.prices?.find(
|
||||||
|
(p) =>
|
||||||
|
p.period_unit ===
|
||||||
|
form.watch("billingPeriod.unit") &&
|
||||||
|
p.period === form.watch("billingPeriod.period"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!selectedPrice) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItem key={plan.id}>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem
|
||||||
|
value={plan.id || ""}
|
||||||
|
className="peer sr-only"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="w-full cursor-pointer">
|
||||||
|
<Card
|
||||||
|
className={`border-2 transition-all duration-200 relative bg-transparent hover:border-purple-300 hover:shadow-lg ${field.value === plan.id ? "border-purple-500 bg-purple-950/40" : ""}`}
|
||||||
|
>
|
||||||
|
{plan.name === "KVM 2" && (
|
||||||
|
<div className="absolute -top-2 -right-2 bg-green-500 text-white px-2 py-1 rounded-full text-xs font-semibold">
|
||||||
|
MOST POPULAR
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{plan.name}</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{"High-performance VPS hosting"}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div className="flex flex-col items-center p-2 bg-muted rounded-lg">
|
||||||
|
<Cpu className="h-4 w-4 mb-1 text-muted-foreground" />
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{plan.metadata?.cpus || 1} vCPU
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
Cores
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center p-2 bg-muted rounded-lg">
|
||||||
|
<MemoryStick className="h-4 w-4 mb-1 text-muted-foreground" />
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{Number.parseInt(
|
||||||
|
plan.metadata?.memory || "2048",
|
||||||
|
) / 1024}{" "}
|
||||||
|
GB
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
RAM
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center p-2 bg-muted rounded-lg">
|
||||||
|
<HardDrive className="h-4 w-4 mb-1 text-muted-foreground" />
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{Number.parseInt(
|
||||||
|
plan.metadata?.disk_space ||
|
||||||
|
"20480",
|
||||||
|
) / 1024}{" "}
|
||||||
|
GB
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
SSD
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2">
|
||||||
|
<div className="flex items-center justify-between p-3 bg-primary/5 rounded-lg">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Calendar className="h-4 w-4 mr-2 text-muted-foreground" />
|
||||||
|
<span className="text-sm">
|
||||||
|
{formatBillingPeriod(
|
||||||
|
selectedPrice.period || 1,
|
||||||
|
selectedPrice.period_unit ||
|
||||||
|
"month",
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<DollarSign className="h-4 w-4 mr-1 text-primary" />
|
||||||
|
<span className="text-lg font-semibold">
|
||||||
|
$
|
||||||
|
{formatPrice(
|
||||||
|
selectedPrice.price || 0,
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
{selectedPrice.period_unit ===
|
||||||
|
"year" && (
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="ml-2 text-xs"
|
||||||
|
>
|
||||||
|
Save $
|
||||||
|
{calculateSavings(
|
||||||
|
monthlyPrice,
|
||||||
|
selectedPrice.price || 0,
|
||||||
|
).toFixed(2)}
|
||||||
|
/yr
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type="submit" className="w-full">
|
||||||
|
Create Server
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { DollarSign } from "lucide-react";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
import { ShowHetznerProviders } from "./show-hetzner-providers";
|
||||||
|
import { ShowHostingerServers } from "./show-hostinger-servers";
|
||||||
|
|
||||||
|
export const ShowProviders = () => {
|
||||||
|
return (
|
||||||
|
<Card className="w-full bg-transparent border-none">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<DollarSign className="h-6 w-6 text-green-600" />
|
||||||
|
Servers
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Manage and view available server types from Hetzner and Hostinger for
|
||||||
|
your business. Here you can see updated pricing and specifications for
|
||||||
|
each plan.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Tabs defaultValue="hetzner" className="w-full">
|
||||||
|
<TabsList className="grid w-full grid-cols-2">
|
||||||
|
<TabsTrigger value="hetzner" className="flex items-center gap-2">
|
||||||
|
🇩🇪 Hetzner Cloud
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="hostinger" className="flex items-center gap-2">
|
||||||
|
🌍 Hostinger VPS
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent value="hetzner" className="mt-4">
|
||||||
|
<ShowHetznerProviders />
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="hostinger" className="mt-4">
|
||||||
|
<ShowHostingerServers />
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -182,7 +182,8 @@
|
|||||||
"tsx": "^4.16.2",
|
"tsx": "^4.16.2",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite-tsconfig-paths": "4.3.2",
|
"vite-tsconfig-paths": "4.3.2",
|
||||||
"vitest": "^1.6.1"
|
"vitest": "^1.6.1",
|
||||||
|
"openapi-typescript": "7.8.0"
|
||||||
},
|
},
|
||||||
"ct3aMetadata": {
|
"ct3aMetadata": {
|
||||||
"initVersion": "7.25.2"
|
"initVersion": "7.25.2"
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import { gitProviderRouter } from "./routers/git-provider";
|
|||||||
import { giteaRouter } from "./routers/gitea";
|
import { giteaRouter } from "./routers/gitea";
|
||||||
import { githubRouter } from "./routers/github";
|
import { githubRouter } from "./routers/github";
|
||||||
import { gitlabRouter } from "./routers/gitlab";
|
import { gitlabRouter } from "./routers/gitlab";
|
||||||
|
import { hetznerRouter } from "./routers/hetzner";
|
||||||
|
import { hostingerRouter } from "./routers/hostinger";
|
||||||
import { mariadbRouter } from "./routers/mariadb";
|
import { mariadbRouter } from "./routers/mariadb";
|
||||||
import { mongoRouter } from "./routers/mongo";
|
import { mongoRouter } from "./routers/mongo";
|
||||||
import { mountRouter } from "./routers/mount";
|
import { mountRouter } from "./routers/mount";
|
||||||
@@ -85,6 +87,8 @@ export const appRouter = createTRPCRouter({
|
|||||||
schedule: scheduleRouter,
|
schedule: scheduleRouter,
|
||||||
rollback: rollbackRouter,
|
rollback: rollbackRouter,
|
||||||
volumeBackups: volumeBackupsRouter,
|
volumeBackups: volumeBackupsRouter,
|
||||||
|
hetzner: hetznerRouter,
|
||||||
|
hostinger: hostingerRouter,
|
||||||
environment: environmentRouter,
|
environment: environmentRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
21
apps/dokploy/server/api/routers/hetzner.ts
Normal file
21
apps/dokploy/server/api/routers/hetzner.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import {
|
||||||
|
fetchHetznerLocations,
|
||||||
|
fetchHetznerServers,
|
||||||
|
fetchHetznerServerTypes,
|
||||||
|
} from "@dokploy/server/index";
|
||||||
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
|
|
||||||
|
export const hetznerRouter = createTRPCRouter({
|
||||||
|
locations: protectedProcedure.query(async () => {
|
||||||
|
const locations = await fetchHetznerLocations();
|
||||||
|
return locations;
|
||||||
|
}),
|
||||||
|
|
||||||
|
serverTypes: protectedProcedure.query(async () => {
|
||||||
|
return await fetchHetznerServerTypes();
|
||||||
|
}),
|
||||||
|
|
||||||
|
servers: protectedProcedure.query(async () => {
|
||||||
|
return await fetchHetznerServers();
|
||||||
|
}),
|
||||||
|
});
|
||||||
16
apps/dokploy/server/api/routers/hostinger.ts
Normal file
16
apps/dokploy/server/api/routers/hostinger.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import {
|
||||||
|
fetchHostingerCatalog,
|
||||||
|
fetchHostingerDataCenters,
|
||||||
|
} from "@dokploy/server/index";
|
||||||
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
|
|
||||||
|
export const hostingerRouter = createTRPCRouter({
|
||||||
|
vpsPlans: protectedProcedure.query(async () => {
|
||||||
|
const catalogItems = await fetchHostingerCatalog();
|
||||||
|
return catalogItems.filter((item) => item?.name?.startsWith("KVM"));
|
||||||
|
}),
|
||||||
|
|
||||||
|
dataCenters: protectedProcedure.query(async () => {
|
||||||
|
return await fetchHostingerDataCenters();
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"openapi-fetch": "0.14.0",
|
||||||
"@ai-sdk/anthropic": "^2.0.5",
|
"@ai-sdk/anthropic": "^2.0.5",
|
||||||
"@ai-sdk/azure": "^2.0.16",
|
"@ai-sdk/azure": "^2.0.16",
|
||||||
"@ai-sdk/cohere": "^2.0.4",
|
"@ai-sdk/cohere": "^2.0.4",
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ export * from "./services/git-provider";
|
|||||||
export * from "./services/gitea";
|
export * from "./services/gitea";
|
||||||
export * from "./services/github";
|
export * from "./services/github";
|
||||||
export * from "./services/gitlab";
|
export * from "./services/gitlab";
|
||||||
|
export * from "./services/hetzner";
|
||||||
|
export * from "./services/hostinger";
|
||||||
export * from "./services/mariadb";
|
export * from "./services/mariadb";
|
||||||
export * from "./services/mongo";
|
export * from "./services/mongo";
|
||||||
export * from "./services/mount";
|
export * from "./services/mount";
|
||||||
@@ -76,7 +78,6 @@ export * from "./utils/builders/nixpacks";
|
|||||||
export * from "./utils/builders/paketo";
|
export * from "./utils/builders/paketo";
|
||||||
export * from "./utils/builders/static";
|
export * from "./utils/builders/static";
|
||||||
export * from "./utils/builders/utils";
|
export * from "./utils/builders/utils";
|
||||||
|
|
||||||
export * from "./utils/cluster/upload";
|
export * from "./utils/cluster/upload";
|
||||||
export * from "./utils/databases/rebuild";
|
export * from "./utils/databases/rebuild";
|
||||||
export * from "./utils/docker/collision";
|
export * from "./utils/docker/collision";
|
||||||
@@ -106,7 +107,6 @@ export * from "./utils/providers/docker";
|
|||||||
export * from "./utils/providers/git";
|
export * from "./utils/providers/git";
|
||||||
export * from "./utils/providers/gitea";
|
export * from "./utils/providers/gitea";
|
||||||
export * from "./utils/providers/github";
|
export * from "./utils/providers/github";
|
||||||
export * from "./utils/providers/github";
|
|
||||||
export * from "./utils/providers/gitlab";
|
export * from "./utils/providers/gitlab";
|
||||||
export * from "./utils/providers/raw";
|
export * from "./utils/providers/raw";
|
||||||
export * from "./utils/schedules/index";
|
export * from "./utils/schedules/index";
|
||||||
|
|||||||
49
packages/server/src/services/hetzner.ts
Normal file
49
packages/server/src/services/hetzner.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import createClient from "openapi-fetch";
|
||||||
|
import type { paths } from "../types/hetzner-types";
|
||||||
|
|
||||||
|
const HETZNER_API_URL = "https://api.hetzner.cloud/v1";
|
||||||
|
|
||||||
|
const hetznerApiKey = process.env.HETZNER_API_KEY;
|
||||||
|
|
||||||
|
const client = createClient<paths>({ baseUrl: HETZNER_API_URL });
|
||||||
|
|
||||||
|
export const fetchHetznerLocations = async () => {
|
||||||
|
const { data, error } = await client.GET("/locations", {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${hetznerApiKey}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Failed to fetch Hetzner locations: ${error}`);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchHetznerServerTypes = async () => {
|
||||||
|
const { data, error } = await client.GET("/server_types", {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${hetznerApiKey}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Failed to fetch Hetzner server types: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchHetznerServers = async () => {
|
||||||
|
const { data, error } = await client.GET("/servers", {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${hetznerApiKey}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Failed to fetch Hetzner servers: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
35
packages/server/src/services/hostinger.ts
Normal file
35
packages/server/src/services/hostinger.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import createClient from "openapi-fetch";
|
||||||
|
import type { paths } from "../types/hostinger-types";
|
||||||
|
|
||||||
|
const HOSTINGER_API_URL = "https://developers.hostinger.com";
|
||||||
|
const hostingerApiKey = process.env.HOSTINGER_API_KEY;
|
||||||
|
|
||||||
|
const client = createClient<paths>({ baseUrl: HOSTINGER_API_URL });
|
||||||
|
|
||||||
|
export const fetchHostingerCatalog = async () => {
|
||||||
|
const { data, error } = await client.GET("/api/billing/v1/catalog", {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${hostingerApiKey}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Failed to fetch Hostinger catalog: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchHostingerDataCenters = async () => {
|
||||||
|
const { data, error } = await client.GET("/api/vps/v1/data-centers", {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${hostingerApiKey}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Failed to fetch Hostinger data centers: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
31177
packages/server/src/types/hetzner-types.ts
Normal file
31177
packages/server/src/types/hetzner-types.ts
Normal file
File diff suppressed because it is too large
Load Diff
5377
packages/server/src/types/hostinger-types.ts
Normal file
5377
packages/server/src/types/hostinger-types.ts
Normal file
File diff suppressed because it is too large
Load Diff
177
pnpm-lock.yaml
generated
177
pnpm-lock.yaml
generated
@@ -518,6 +518,9 @@ importers:
|
|||||||
memfs:
|
memfs:
|
||||||
specifier: ^4.17.2
|
specifier: ^4.17.2
|
||||||
version: 4.17.2
|
version: 4.17.2
|
||||||
|
openapi-typescript:
|
||||||
|
specifier: 7.8.0
|
||||||
|
version: 7.8.0(typescript@5.8.3)
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^3.4.17
|
specifier: ^3.4.17
|
||||||
version: 3.4.17
|
version: 3.4.17
|
||||||
@@ -708,6 +711,9 @@ importers:
|
|||||||
octokit:
|
octokit:
|
||||||
specifier: 3.1.2
|
specifier: 3.1.2
|
||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
|
openapi-fetch:
|
||||||
|
specifier: 0.14.0
|
||||||
|
version: 0.14.0
|
||||||
otpauth:
|
otpauth:
|
||||||
specifier: ^9.4.0
|
specifier: ^9.4.0
|
||||||
version: 9.4.0
|
version: 9.4.0
|
||||||
@@ -3570,6 +3576,16 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@redis/client': ^1.0.0
|
'@redis/client': ^1.0.0
|
||||||
|
|
||||||
|
'@redocly/ajv@8.11.2':
|
||||||
|
resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==}
|
||||||
|
|
||||||
|
'@redocly/config@0.22.2':
|
||||||
|
resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==}
|
||||||
|
|
||||||
|
'@redocly/openapi-core@1.34.3':
|
||||||
|
resolution: {integrity: sha512-3arRdUp1fNx55itnjKiUhO6t4Mf91TsrTIYINDNLAZPS0TPd5YpiXRctwjel0qqWoOOhjA34cZ3m4dksLDFUYg==}
|
||||||
|
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.41.1':
|
'@rollup/rollup-android-arm-eabi@4.41.1':
|
||||||
resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==}
|
resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
@@ -4194,6 +4210,10 @@ packages:
|
|||||||
ansi-align@3.0.1:
|
ansi-align@3.0.1:
|
||||||
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
|
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
|
||||||
|
|
||||||
|
ansi-colors@4.1.3:
|
||||||
|
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
ansi-escapes@7.0.0:
|
ansi-escapes@7.0.0:
|
||||||
resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
|
resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -4440,6 +4460,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==}
|
resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==}
|
||||||
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
|
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
|
||||||
|
|
||||||
|
change-case@5.4.4:
|
||||||
|
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
|
||||||
|
|
||||||
character-entities-html4@2.1.0:
|
character-entities-html4@2.1.0:
|
||||||
resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
|
resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
|
||||||
|
|
||||||
@@ -4553,6 +4576,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
|
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
|
||||||
engines: {node: '>=12.5.0'}
|
engines: {node: '>=12.5.0'}
|
||||||
|
|
||||||
|
colorette@1.4.0:
|
||||||
|
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
|
||||||
|
|
||||||
colorette@2.0.20:
|
colorette@2.0.20:
|
||||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||||
|
|
||||||
@@ -5462,6 +5488,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
|
resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
index-to-position@1.1.0:
|
||||||
|
resolution: {integrity: sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
inflation@2.1.0:
|
inflation@2.1.0:
|
||||||
resolution: {integrity: sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==}
|
resolution: {integrity: sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -5676,6 +5706,10 @@ packages:
|
|||||||
js-file-download@0.4.12:
|
js-file-download@0.4.12:
|
||||||
resolution: {integrity: sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==}
|
resolution: {integrity: sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==}
|
||||||
|
|
||||||
|
js-levenshtein@1.1.6:
|
||||||
|
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
@@ -6131,6 +6165,10 @@ packages:
|
|||||||
minimatch@3.1.2:
|
minimatch@3.1.2:
|
||||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
|
|
||||||
|
minimatch@5.1.6:
|
||||||
|
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
minimatch@7.4.6:
|
minimatch@7.4.6:
|
||||||
resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==}
|
resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -6382,6 +6420,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
openapi-fetch@0.14.0:
|
||||||
|
resolution: {integrity: sha512-PshIdm1NgdLvb05zp8LqRQMNSKzIlPkyMxYFxwyHR+UlKD4t2nUjkDhNxeRbhRSEd3x5EUNh2w5sJYwkhOH4fg==}
|
||||||
|
|
||||||
openapi-path-templating@2.2.1:
|
openapi-path-templating@2.2.1:
|
||||||
resolution: {integrity: sha512-eN14VrDvl/YyGxxrkGOHkVkWEoPyhyeydOUrbvjoz8K5eIGgELASwN1eqFOJ2CTQMGCy2EntOK1KdtJ8ZMekcg==}
|
resolution: {integrity: sha512-eN14VrDvl/YyGxxrkGOHkVkWEoPyhyeydOUrbvjoz8K5eIGgELASwN1eqFOJ2CTQMGCy2EntOK1KdtJ8ZMekcg==}
|
||||||
engines: {node: '>=12.20.0'}
|
engines: {node: '>=12.20.0'}
|
||||||
@@ -6393,6 +6434,15 @@ packages:
|
|||||||
openapi-types@12.1.3:
|
openapi-types@12.1.3:
|
||||||
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
|
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
|
||||||
|
|
||||||
|
openapi-typescript-helpers@0.0.15:
|
||||||
|
resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==}
|
||||||
|
|
||||||
|
openapi-typescript@7.8.0:
|
||||||
|
resolution: {integrity: sha512-1EeVWmDzi16A+siQlo/SwSGIT7HwaFAVjvMA7/jG5HMLSnrUOzPL7uSTRZZa4v/LCRxHTApHKtNY6glApEoiUQ==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
typescript: ^5.x
|
||||||
|
|
||||||
otpauth@9.4.0:
|
otpauth@9.4.0:
|
||||||
resolution: {integrity: sha512-fHIfzIG5RqCkK9cmV8WU+dPQr9/ebR5QOwGZn2JAr1RQF+lmAuLL2YdtdqvmBjNmgJlYk3KZ4a0XokaEhg1Jsw==}
|
resolution: {integrity: sha512-fHIfzIG5RqCkK9cmV8WU+dPQr9/ebR5QOwGZn2JAr1RQF+lmAuLL2YdtdqvmBjNmgJlYk3KZ4a0XokaEhg1Jsw==}
|
||||||
|
|
||||||
@@ -6441,6 +6491,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
|
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
parse-json@8.3.0:
|
||||||
|
resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
parseley@0.12.1:
|
parseley@0.12.1:
|
||||||
resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
|
resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
|
||||||
|
|
||||||
@@ -6543,6 +6597,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==}
|
resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
pluralize@8.0.0:
|
||||||
|
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
pngjs@5.0.0:
|
pngjs@5.0.0:
|
||||||
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@@ -7292,6 +7350,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
|
resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
|
supports-color@10.0.0:
|
||||||
|
resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
supports-color@7.2.0:
|
supports-color@7.2.0:
|
||||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -7478,6 +7540,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
|
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
|
||||||
engines: {node: '>=12.20'}
|
engines: {node: '>=12.20'}
|
||||||
|
|
||||||
|
type-fest@4.41.0:
|
||||||
|
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
type-is@1.6.18:
|
type-is@1.6.18:
|
||||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -7550,6 +7616,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
browserslist: '>= 4.21.0'
|
browserslist: '>= 4.21.0'
|
||||||
|
|
||||||
|
uri-js-replace@1.0.1:
|
||||||
|
resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==}
|
||||||
|
|
||||||
url-parse@1.5.10:
|
url-parse@1.5.10:
|
||||||
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
||||||
|
|
||||||
@@ -7778,6 +7847,9 @@ packages:
|
|||||||
yallist@4.0.0:
|
yallist@4.0.0:
|
||||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||||
|
|
||||||
|
yaml-ast-parser@0.0.43:
|
||||||
|
resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
|
||||||
|
|
||||||
yaml@2.8.0:
|
yaml@2.8.0:
|
||||||
resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
|
resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
|
||||||
engines: {node: '>= 14.6'}
|
engines: {node: '>= 14.6'}
|
||||||
@@ -10613,6 +10685,29 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@redis/client': 1.6.0
|
'@redis/client': 1.6.0
|
||||||
|
|
||||||
|
'@redocly/ajv@8.11.2':
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
json-schema-traverse: 1.0.0
|
||||||
|
require-from-string: 2.0.2
|
||||||
|
uri-js-replace: 1.0.1
|
||||||
|
|
||||||
|
'@redocly/config@0.22.2': {}
|
||||||
|
|
||||||
|
'@redocly/openapi-core@1.34.3(supports-color@10.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@redocly/ajv': 8.11.2
|
||||||
|
'@redocly/config': 0.22.2
|
||||||
|
colorette: 1.4.0
|
||||||
|
https-proxy-agent: 7.0.6(supports-color@10.0.0)
|
||||||
|
js-levenshtein: 1.1.6
|
||||||
|
js-yaml: 4.1.0
|
||||||
|
minimatch: 5.1.6
|
||||||
|
pluralize: 8.0.0
|
||||||
|
yaml-ast-parser: 0.0.43
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.41.1':
|
'@rollup/rollup-android-arm-eabi@4.41.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -11449,7 +11544,7 @@ snapshots:
|
|||||||
|
|
||||||
agent-base@6.0.2:
|
agent-base@6.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -11493,6 +11588,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
string-width: 4.2.3
|
string-width: 4.2.3
|
||||||
|
|
||||||
|
ansi-colors@4.1.3: {}
|
||||||
|
|
||||||
ansi-escapes@7.0.0:
|
ansi-escapes@7.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
environment: 1.1.0
|
environment: 1.1.0
|
||||||
@@ -11768,6 +11865,8 @@ snapshots:
|
|||||||
|
|
||||||
chalk@5.4.1: {}
|
chalk@5.4.1: {}
|
||||||
|
|
||||||
|
change-case@5.4.4: {}
|
||||||
|
|
||||||
character-entities-html4@2.1.0: {}
|
character-entities-html4@2.1.0: {}
|
||||||
|
|
||||||
character-entities-legacy@1.1.4: {}
|
character-entities-legacy@1.1.4: {}
|
||||||
@@ -11891,6 +11990,8 @@ snapshots:
|
|||||||
color-string: 1.9.1
|
color-string: 1.9.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
colorette@1.4.0: {}
|
||||||
|
|
||||||
colorette@2.0.20: {}
|
colorette@2.0.20: {}
|
||||||
|
|
||||||
combined-stream@1.0.8:
|
combined-stream@1.0.8:
|
||||||
@@ -12063,9 +12164,11 @@ snapshots:
|
|||||||
|
|
||||||
dateformat@4.6.3: {}
|
dateformat@4.6.3: {}
|
||||||
|
|
||||||
debug@4.4.1:
|
debug@4.4.1(supports-color@10.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
supports-color: 10.0.0
|
||||||
|
|
||||||
decamelize@1.2.0: {}
|
decamelize@1.2.0: {}
|
||||||
|
|
||||||
@@ -12137,7 +12240,7 @@ snapshots:
|
|||||||
|
|
||||||
docker-modem@5.0.6:
|
docker-modem@5.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
readable-stream: 3.6.2
|
readable-stream: 3.6.2
|
||||||
split-ca: 1.0.1
|
split-ca: 1.0.1
|
||||||
ssh2: 1.15.0
|
ssh2: 1.15.0
|
||||||
@@ -12275,7 +12378,7 @@ snapshots:
|
|||||||
|
|
||||||
esbuild-register@3.6.0(esbuild@0.19.12):
|
esbuild-register@3.6.0(esbuild@0.19.12):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
esbuild: 0.19.12
|
esbuild: 0.19.12
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -12522,7 +12625,7 @@ snapshots:
|
|||||||
gaxios@6.7.1:
|
gaxios@6.7.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
extend: 3.0.2
|
extend: 3.0.2
|
||||||
https-proxy-agent: 7.0.6
|
https-proxy-agent: 7.0.6(supports-color@10.0.0)
|
||||||
is-stream: 2.0.1
|
is-stream: 2.0.1
|
||||||
node-fetch: 2.7.0
|
node-fetch: 2.7.0
|
||||||
uuid: 9.0.1
|
uuid: 9.0.1
|
||||||
@@ -12542,7 +12645,7 @@ snapshots:
|
|||||||
gel@2.1.0:
|
gel@2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@petamoriken/float16': 3.9.2
|
'@petamoriken/float16': 3.9.2
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
env-paths: 3.0.0
|
env-paths: 3.0.0
|
||||||
semver: 7.7.2
|
semver: 7.7.2
|
||||||
shell-quote: 1.8.2
|
shell-quote: 1.8.2
|
||||||
@@ -12769,14 +12872,14 @@ snapshots:
|
|||||||
https-proxy-agent@5.0.1:
|
https-proxy-agent@5.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 6.0.2
|
agent-base: 6.0.2
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
https-proxy-agent@7.0.6:
|
https-proxy-agent@7.0.6(supports-color@10.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 7.1.4
|
agent-base: 7.1.4
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -12820,6 +12923,8 @@ snapshots:
|
|||||||
|
|
||||||
indent-string@5.0.0: {}
|
indent-string@5.0.0: {}
|
||||||
|
|
||||||
|
index-to-position@1.1.0: {}
|
||||||
|
|
||||||
inflation@2.1.0: {}
|
inflation@2.1.0: {}
|
||||||
|
|
||||||
inflight@1.0.6:
|
inflight@1.0.6:
|
||||||
@@ -12851,7 +12956,7 @@ snapshots:
|
|||||||
canonicalize: 1.0.8
|
canonicalize: 1.0.8
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
cross-fetch: 4.1.0
|
cross-fetch: 4.1.0
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
hash.js: 1.1.7
|
hash.js: 1.1.7
|
||||||
json-stringify-safe: 5.0.1
|
json-stringify-safe: 5.0.1
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
@@ -12883,7 +12988,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@ioredis/commands': 1.2.0
|
'@ioredis/commands': 1.2.0
|
||||||
cluster-key-slot: 1.1.2
|
cluster-key-slot: 1.1.2
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
denque: 2.1.0
|
denque: 2.1.0
|
||||||
lodash.defaults: 4.2.0
|
lodash.defaults: 4.2.0
|
||||||
lodash.isarguments: 3.1.0
|
lodash.isarguments: 3.1.0
|
||||||
@@ -13000,6 +13105,8 @@ snapshots:
|
|||||||
|
|
||||||
js-file-download@0.4.12: {}
|
js-file-download@0.4.12: {}
|
||||||
|
|
||||||
|
js-levenshtein@1.1.6: {}
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
js-tokens@9.0.1: {}
|
js-tokens@9.0.1: {}
|
||||||
@@ -13199,7 +13306,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
chalk: 5.4.1
|
chalk: 5.4.1
|
||||||
commander: 13.1.0
|
commander: 13.1.0
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
execa: 8.0.1
|
execa: 8.0.1
|
||||||
lilconfig: 3.1.3
|
lilconfig: 3.1.3
|
||||||
listr2: 8.3.3
|
listr2: 8.3.3
|
||||||
@@ -13550,7 +13657,7 @@ snapshots:
|
|||||||
micromark@4.0.2:
|
micromark@4.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/debug': 4.1.12
|
'@types/debug': 4.1.12
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
decode-named-character-reference: 1.1.0
|
decode-named-character-reference: 1.1.0
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
micromark-core-commonmark: 2.0.3
|
micromark-core-commonmark: 2.0.3
|
||||||
@@ -13600,6 +13707,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 1.1.11
|
brace-expansion: 1.1.11
|
||||||
|
|
||||||
|
minimatch@5.1.6:
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 2.0.1
|
||||||
|
|
||||||
minimatch@7.4.6:
|
minimatch@7.4.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 2.0.1
|
brace-expansion: 2.0.1
|
||||||
@@ -13841,6 +13952,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-function: 5.0.1
|
mimic-function: 5.0.1
|
||||||
|
|
||||||
|
openapi-fetch@0.14.0:
|
||||||
|
dependencies:
|
||||||
|
openapi-typescript-helpers: 0.0.15
|
||||||
|
|
||||||
openapi-path-templating@2.2.1:
|
openapi-path-templating@2.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
apg-lite: 1.0.4
|
apg-lite: 1.0.4
|
||||||
@@ -13851,6 +13966,18 @@ snapshots:
|
|||||||
|
|
||||||
openapi-types@12.1.3: {}
|
openapi-types@12.1.3: {}
|
||||||
|
|
||||||
|
openapi-typescript-helpers@0.0.15: {}
|
||||||
|
|
||||||
|
openapi-typescript@7.8.0(typescript@5.8.3):
|
||||||
|
dependencies:
|
||||||
|
'@redocly/openapi-core': 1.34.3(supports-color@10.0.0)
|
||||||
|
ansi-colors: 4.1.3
|
||||||
|
change-case: 5.4.4
|
||||||
|
parse-json: 8.3.0
|
||||||
|
supports-color: 10.0.0
|
||||||
|
typescript: 5.8.3
|
||||||
|
yargs-parser: 21.1.1
|
||||||
|
|
||||||
otpauth@9.4.0:
|
otpauth@9.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.7.1
|
'@noble/hashes': 1.7.1
|
||||||
@@ -13911,6 +14038,12 @@ snapshots:
|
|||||||
json-parse-even-better-errors: 2.3.1
|
json-parse-even-better-errors: 2.3.1
|
||||||
lines-and-columns: 1.2.4
|
lines-and-columns: 1.2.4
|
||||||
|
|
||||||
|
parse-json@8.3.0:
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.27.1
|
||||||
|
index-to-position: 1.1.0
|
||||||
|
type-fest: 4.41.0
|
||||||
|
|
||||||
parseley@0.12.1:
|
parseley@0.12.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
leac: 0.6.0
|
leac: 0.6.0
|
||||||
@@ -14015,6 +14148,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
queue-lit: 1.5.2
|
queue-lit: 1.5.2
|
||||||
|
|
||||||
|
pluralize@8.0.0: {}
|
||||||
|
|
||||||
pngjs@5.0.0: {}
|
pngjs@5.0.0: {}
|
||||||
|
|
||||||
postcss-import@15.1.0(postcss@8.5.3):
|
postcss-import@15.1.0(postcss@8.5.3):
|
||||||
@@ -14466,7 +14601,7 @@ snapshots:
|
|||||||
|
|
||||||
require-in-the-middle@7.5.2:
|
require-in-the-middle@7.5.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
module-details-from-path: 1.0.4
|
module-details-from-path: 1.0.4
|
||||||
resolve: 1.22.10
|
resolve: 1.22.10
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -14809,6 +14944,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
copy-anything: 3.0.5
|
copy-anything: 3.0.5
|
||||||
|
|
||||||
|
supports-color@10.0.0: {}
|
||||||
|
|
||||||
supports-color@7.2.0:
|
supports-color@7.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag: 4.0.0
|
has-flag: 4.0.0
|
||||||
@@ -15054,6 +15191,8 @@ snapshots:
|
|||||||
|
|
||||||
type-fest@2.19.0: {}
|
type-fest@2.19.0: {}
|
||||||
|
|
||||||
|
type-fest@4.41.0: {}
|
||||||
|
|
||||||
type-is@1.6.18:
|
type-is@1.6.18:
|
||||||
dependencies:
|
dependencies:
|
||||||
media-typer: 0.3.0
|
media-typer: 0.3.0
|
||||||
@@ -15129,6 +15268,8 @@ snapshots:
|
|||||||
escalade: 3.2.0
|
escalade: 3.2.0
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
|
|
||||||
|
uri-js-replace@1.0.1: {}
|
||||||
|
|
||||||
url-parse@1.5.10:
|
url-parse@1.5.10:
|
||||||
dependencies:
|
dependencies:
|
||||||
querystringify: 2.2.0
|
querystringify: 2.2.0
|
||||||
@@ -15193,7 +15334,7 @@ snapshots:
|
|||||||
vite-node@1.6.1(@types/node@18.19.104):
|
vite-node@1.6.1(@types/node@18.19.104):
|
||||||
dependencies:
|
dependencies:
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
pathe: 1.1.2
|
pathe: 1.1.2
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
vite: 5.4.19(@types/node@18.19.104)
|
vite: 5.4.19(@types/node@18.19.104)
|
||||||
@@ -15210,7 +15351,7 @@ snapshots:
|
|||||||
|
|
||||||
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.19(@types/node@18.19.104)):
|
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.19(@types/node@18.19.104)):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
globrex: 0.1.2
|
globrex: 0.1.2
|
||||||
tsconfck: 3.1.6(typescript@5.8.3)
|
tsconfck: 3.1.6(typescript@5.8.3)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -15237,7 +15378,7 @@ snapshots:
|
|||||||
'@vitest/utils': 1.6.1
|
'@vitest/utils': 1.6.1
|
||||||
acorn-walk: 8.3.4
|
acorn-walk: 8.3.4
|
||||||
chai: 4.5.0
|
chai: 4.5.0
|
||||||
debug: 4.4.1
|
debug: 4.4.1(supports-color@10.0.0)
|
||||||
execa: 8.0.1
|
execa: 8.0.1
|
||||||
local-pkg: 0.5.1
|
local-pkg: 0.5.1
|
||||||
magic-string: 0.30.17
|
magic-string: 0.30.17
|
||||||
@@ -15351,6 +15492,8 @@ snapshots:
|
|||||||
|
|
||||||
yallist@4.0.0: {}
|
yallist@4.0.0: {}
|
||||||
|
|
||||||
|
yaml-ast-parser@0.0.43: {}
|
||||||
|
|
||||||
yaml@2.8.0: {}
|
yaml@2.8.0: {}
|
||||||
|
|
||||||
yargs-parser@18.1.3:
|
yargs-parser@18.1.3:
|
||||||
|
|||||||
Reference in New Issue
Block a user