mirror of
https://github.com/LukeHagar/dokploy.git
synced 2025-12-08 20:37:44 +00:00
refactor: adjust queries
This commit is contained in:
@@ -0,0 +1,119 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { PenBoxIcon, Plus, SquarePen } from "lucide-react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
organizationId?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
export function AddOrganization({ organizationId, children }: Props) {
|
||||||
|
const utils = api.useUtils();
|
||||||
|
const { data: organization } = api.organization.one.useQuery(
|
||||||
|
{
|
||||||
|
organizationId: organizationId ?? "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!organizationId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const { mutateAsync, isLoading } = organizationId
|
||||||
|
? api.organization.update.useMutation()
|
||||||
|
: api.organization.create.useMutation();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (organization) {
|
||||||
|
setName(organization.name);
|
||||||
|
}
|
||||||
|
}, [organization]);
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await mutateAsync({ name, organizationId: organizationId ?? "" })
|
||||||
|
.then(() => {
|
||||||
|
setOpen(false);
|
||||||
|
toast.success(
|
||||||
|
`Organization ${organizationId ? "updated" : "created"} successfully`,
|
||||||
|
);
|
||||||
|
utils.organization.all.invalidate();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
toast.error(
|
||||||
|
`Failed to ${organizationId ? "update" : "create"} organization`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
{organizationId ? (
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="group cursor-pointer hover:bg-blue-500/10"
|
||||||
|
onSelect={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<PenBoxIcon className="size-3.5 text-primary group-hover:text-blue-500" />
|
||||||
|
</DropdownMenuItem>
|
||||||
|
) : (
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="gap-2 p-2"
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(true);
|
||||||
|
}}
|
||||||
|
onSelect={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<div className="flex size-6 items-center justify-center rounded-md border bg-background">
|
||||||
|
<Plus className="size-4" />
|
||||||
|
</div>
|
||||||
|
<div className="font-medium text-muted-foreground">
|
||||||
|
Add organization
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
{organizationId ? "Update organization" : "Add organization"}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
{organizationId
|
||||||
|
? "Update the organization name"
|
||||||
|
: "Create a new organization to manage your projects."}
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4 py-4">
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label htmlFor="name" className="text-right">
|
||||||
|
Name
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
className="col-span-3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button type="submit" onClick={handleSubmit} isLoading={isLoading}>
|
||||||
|
{organizationId ? "Update organization" : "Create organization"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -86,6 +86,7 @@ export const AddCertificate = () => {
|
|||||||
privateKey: data.privateKey,
|
privateKey: data.privateKey,
|
||||||
autoRenew: data.autoRenew,
|
autoRenew: data.autoRenew,
|
||||||
serverId: data.serverId,
|
serverId: data.serverId,
|
||||||
|
organizationId: "",
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
toast.success("Certificate Created");
|
toast.success("Certificate Created");
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export const HandleSSHKeys = ({ sshKeyId }: Props) => {
|
|||||||
const onSubmit = async (data: SSHKey) => {
|
const onSubmit = async (data: SSHKey) => {
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
...data,
|
...data,
|
||||||
|
organizationId: "",
|
||||||
sshKeyId: sshKeyId || "",
|
sshKeyId: sshKeyId || "",
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
|
|||||||
@@ -76,10 +76,11 @@ export const ShowUsers = () => {
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead className="w-[100px]">Email</TableHead>
|
<TableHead className="w-[100px]">Email</TableHead>
|
||||||
<TableHead className="text-center">Status</TableHead>
|
<TableHead className="text-center">Role</TableHead>
|
||||||
<TableHead className="text-center">2FA</TableHead>
|
<TableHead className="text-center">2FA</TableHead>
|
||||||
|
{/* <TableHead className="text-center">Status</TableHead> */}
|
||||||
<TableHead className="text-center">
|
<TableHead className="text-center">
|
||||||
Expiration
|
Created At
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="text-right">Actions</TableHead>
|
<TableHead className="text-right">Actions</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -89,30 +90,32 @@ export const ShowUsers = () => {
|
|||||||
return (
|
return (
|
||||||
<TableRow key={user.userId}>
|
<TableRow key={user.userId}>
|
||||||
<TableCell className="w-[100px]">
|
<TableCell className="w-[100px]">
|
||||||
{user.auth.email}
|
{user.user.email}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-center">
|
<TableCell className="text-center">
|
||||||
<Badge
|
<Badge
|
||||||
variant={
|
variant={
|
||||||
user.isRegistered ? "default" : "secondary"
|
user.role === "owner"
|
||||||
|
? "default"
|
||||||
|
: "secondary"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{user.isRegistered
|
{user.role}
|
||||||
? "Registered"
|
|
||||||
: "Not Registered"}
|
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-center">
|
<TableCell className="text-center">
|
||||||
{user.auth.is2FAEnabled
|
{user.user.is2FAEnabled
|
||||||
? "2FA Enabled"
|
? "2FA Enabled"
|
||||||
: "2FA Not Enabled"}
|
: "2FA Not Enabled"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
{/* <TableCell className="text-right">
|
||||||
|
<span className="text-sm text-muted-foreground">
|
||||||
|
{format(new Date(user.createdAt), "PPpp")}
|
||||||
|
</span>
|
||||||
|
</TableCell> */}
|
||||||
<TableCell className="text-right">
|
<TableCell className="text-right">
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{format(
|
{format(new Date(user.createdAt), "PPpp")}
|
||||||
new Date(user.expirationDate),
|
|
||||||
"PPpp",
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
@@ -131,7 +134,7 @@ export const ShowUsers = () => {
|
|||||||
<DropdownMenuLabel>
|
<DropdownMenuLabel>
|
||||||
Actions
|
Actions
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
{!user.isRegistered && (
|
{/* {!user.isRegistered && (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="w-full cursor-pointer"
|
className="w-full cursor-pointer"
|
||||||
onSelect={(e) => {
|
onSelect={(e) => {
|
||||||
@@ -145,21 +148,22 @@ export const ShowUsers = () => {
|
|||||||
>
|
>
|
||||||
Copy Invitation
|
Copy Invitation
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
{user.isRegistered && (
|
{/* {user.isRegistered && (
|
||||||
<AddUserPermissions
|
<AddUserPermissions
|
||||||
userId={user.userId}
|
userId={user.userId}
|
||||||
/>
|
/>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
|
{user.role !== "owner" && (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Delete User"
|
title="Delete User"
|
||||||
description="Are you sure you want to delete this user?"
|
description="Are you sure you want to delete this user?"
|
||||||
type="destructive"
|
type="destructive"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
authId: user.authId,
|
userId: user.userId,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success(
|
toast.success(
|
||||||
@@ -181,6 +185,7 @@ export const ShowUsers = () => {
|
|||||||
Delete User
|
Delete User
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DialogAction>
|
</DialogAction>
|
||||||
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ type AddServerDomain = z.infer<typeof addServerDomain>;
|
|||||||
|
|
||||||
export const WebDomain = () => {
|
export const WebDomain = () => {
|
||||||
const { t } = useTranslation("settings");
|
const { t } = useTranslation("settings");
|
||||||
const { data: user, refetch } = api.admin.one.useQuery();
|
const { data, refetch } = api.auth.get.useQuery();
|
||||||
const { mutateAsync, isLoading } =
|
const { mutateAsync, isLoading } =
|
||||||
api.settings.assignDomainServer.useMutation();
|
api.settings.assignDomainServer.useMutation();
|
||||||
|
|
||||||
@@ -65,14 +65,14 @@ export const WebDomain = () => {
|
|||||||
resolver: zodResolver(addServerDomain),
|
resolver: zodResolver(addServerDomain),
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) {
|
if (data) {
|
||||||
form.reset({
|
form.reset({
|
||||||
domain: user?.host || "",
|
domain: data?.user?.host || "",
|
||||||
certificateType: user?.certificateType,
|
certificateType: data?.user?.certificateType,
|
||||||
letsEncryptEmail: user?.letsEncryptEmail || "",
|
letsEncryptEmail: data?.user?.letsEncryptEmail || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [form, form.reset, user]);
|
}, [form, form.reset, data]);
|
||||||
|
|
||||||
const onSubmit = async (data: AddServerDomain) => {
|
const onSubmit = async (data: AddServerDomain) => {
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
|
AudioWaveform,
|
||||||
BarChartHorizontalBigIcon,
|
BarChartHorizontalBigIcon,
|
||||||
Bell,
|
Bell,
|
||||||
BlocksIcon,
|
BlocksIcon,
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
Boxes,
|
Boxes,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
CircleHelp,
|
CircleHelp,
|
||||||
|
Command,
|
||||||
CreditCard,
|
CreditCard,
|
||||||
Database,
|
Database,
|
||||||
Folder,
|
Folder,
|
||||||
@@ -16,11 +18,13 @@ import {
|
|||||||
GitBranch,
|
GitBranch,
|
||||||
HeartIcon,
|
HeartIcon,
|
||||||
KeyRound,
|
KeyRound,
|
||||||
|
Loader2,
|
||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
Package,
|
Package,
|
||||||
PieChart,
|
PieChart,
|
||||||
Server,
|
Server,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
|
Trash2,
|
||||||
User,
|
User,
|
||||||
Users,
|
Users,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
@@ -480,13 +484,182 @@ interface Props {
|
|||||||
function LogoWrapper() {
|
function LogoWrapper() {
|
||||||
return <SidebarLogo />;
|
return <SidebarLogo />;
|
||||||
}
|
}
|
||||||
|
import { ChevronsUpDown, Plus } from "lucide-react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { AddOrganization } from "../dashboard/organization/handle-organization";
|
||||||
|
import { authClient } from "@/lib/auth";
|
||||||
|
import { DialogAction } from "../shared/dialog-action";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
const data = {
|
||||||
|
user: {
|
||||||
|
name: "shadcn",
|
||||||
|
email: "m@example.com",
|
||||||
|
avatar: "/avatars/shadcn.jpg",
|
||||||
|
},
|
||||||
|
teams: [
|
||||||
|
{
|
||||||
|
name: "Acme Inc",
|
||||||
|
logo: GalleryVerticalEnd,
|
||||||
|
plan: "Enterprise",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Acme Corp.",
|
||||||
|
logo: AudioWaveform,
|
||||||
|
plan: "Startup",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Evil Corp.",
|
||||||
|
logo: Command,
|
||||||
|
plan: "Free",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const teams = data.teams;
|
||||||
function SidebarLogo() {
|
function SidebarLogo() {
|
||||||
const { state } = useSidebar();
|
const { state } = useSidebar();
|
||||||
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
|
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
|
||||||
|
const {
|
||||||
|
data: organizations,
|
||||||
|
refetch,
|
||||||
|
isLoading,
|
||||||
|
} = api.organization.all.useQuery();
|
||||||
|
const { mutateAsync: deleteOrganization, isLoading: isRemoving } =
|
||||||
|
api.organization.delete.useMutation();
|
||||||
|
const { isMobile } = useSidebar();
|
||||||
|
const { data: activeOrganization } = authClient.useActiveOrganization();
|
||||||
|
|
||||||
|
const [activeTeam, setActiveTeam] = useState<
|
||||||
|
typeof activeOrganization | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeOrganization) {
|
||||||
|
setActiveTeam(activeOrganization);
|
||||||
|
}
|
||||||
|
}, [activeOrganization]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<>
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground min-h-[5vh] pt-4">
|
||||||
|
<span>Loading...</span>
|
||||||
|
<Loader2 className="animate-spin size-4" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<SidebarMenu>
|
||||||
|
<SidebarMenuItem>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<SidebarMenuButton
|
||||||
|
size="lg"
|
||||||
|
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||||
|
>
|
||||||
|
{/* <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"> */}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex aspect-square items-center justify-center rounded-lg transition-all",
|
||||||
|
state === "collapsed" ? "size-6" : "size-10",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Logo
|
||||||
|
className={cn(
|
||||||
|
"transition-all",
|
||||||
|
state === "collapsed" ? "size-6" : "size-10",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||||
|
<span className="truncate font-semibold">
|
||||||
|
{activeTeam?.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<ChevronsUpDown className="ml-auto" />
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
||||||
|
align="start"
|
||||||
|
side={isMobile ? "bottom" : "right"}
|
||||||
|
sideOffset={4}
|
||||||
|
>
|
||||||
|
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
||||||
|
Organizations
|
||||||
|
</DropdownMenuLabel>
|
||||||
|
{organizations?.map((org, index) => (
|
||||||
|
<div className="flex flex-row justify-between" key={org.name}>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={async () => {
|
||||||
|
await authClient.organization.setActive({
|
||||||
|
organizationId: org.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
className="w-full gap-2 p-2"
|
||||||
|
>
|
||||||
|
<div className="flex size-6 items-center justify-center rounded-sm border">
|
||||||
|
<Logo
|
||||||
|
className={cn(
|
||||||
|
"transition-all",
|
||||||
|
state === "collapsed" ? "size-6" : "size-10",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{org.name}
|
||||||
|
{/* <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut> */}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
{/* <DropdownMenuSeparator /> */}
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<AddOrganization organizationId={org.id} />
|
||||||
|
<DialogAction
|
||||||
|
title="Delete Organization"
|
||||||
|
description="Are you sure you want to delete this organization?"
|
||||||
|
type="destructive"
|
||||||
|
onClick={async () => {
|
||||||
|
await deleteOrganization({
|
||||||
|
organizationId: org.id,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
refetch();
|
||||||
|
toast.success("Port deleted successfully");
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deleting port");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="group hover:bg-red-500/10 "
|
||||||
|
isLoading={isRemoving}
|
||||||
|
>
|
||||||
|
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<AddOrganization />
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
</SidebarMenu>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* <Link
|
||||||
href="/dashboard/projects"
|
href="/dashboard/projects"
|
||||||
className="flex items-center gap-2 p-1 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground group-data-[collapsible=icon]/35 rounded-lg "
|
className="flex items-center gap-2 p-1 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground group-data-[collapsible=icon]/35 rounded-lg "
|
||||||
>
|
>
|
||||||
@@ -510,7 +683,8 @@ function SidebarLogo() {
|
|||||||
{dokployVersion}
|
{dokployVersion}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link> */}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,12 +751,12 @@ export default function Page({ children }: Props) {
|
|||||||
>
|
>
|
||||||
<Sidebar collapsible="icon" variant="floating">
|
<Sidebar collapsible="icon" variant="floating">
|
||||||
<SidebarHeader>
|
<SidebarHeader>
|
||||||
<SidebarMenuButton
|
{/* <SidebarMenuButton
|
||||||
className="group-data-[collapsible=icon]:!p-0"
|
className="group-data-[collapsible=icon]:!p-0"
|
||||||
size="lg"
|
size="lg"
|
||||||
>
|
> */}
|
||||||
<LogoWrapper />
|
<LogoWrapper />
|
||||||
</SidebarMenuButton>
|
{/* </SidebarMenuButton> */}
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<SidebarGroup>
|
<SidebarGroup>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { sshRouter } from "./routers/ssh-key";
|
|||||||
import { stripeRouter } from "./routers/stripe";
|
import { stripeRouter } from "./routers/stripe";
|
||||||
import { swarmRouter } from "./routers/swarm";
|
import { swarmRouter } from "./routers/swarm";
|
||||||
import { userRouter } from "./routers/user";
|
import { userRouter } from "./routers/user";
|
||||||
|
import { organizationRouter } from "./routers/organization";
|
||||||
/**
|
/**
|
||||||
* This is the primary router for your server.
|
* This is the primary router for your server.
|
||||||
*
|
*
|
||||||
@@ -75,6 +75,7 @@ export const appRouter = createTRPCRouter({
|
|||||||
server: serverRouter,
|
server: serverRouter,
|
||||||
stripe: stripeRouter,
|
stripe: stripeRouter,
|
||||||
swarm: swarmRouter,
|
swarm: swarmRouter,
|
||||||
|
organization: organizationRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
|
|||||||
@@ -32,10 +32,7 @@ export const certificateRouter = createTRPCRouter({
|
|||||||
.input(apiFindCertificate)
|
.input(apiFindCertificate)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const certificates = await findCertificateById(input.certificateId);
|
const certificates = await findCertificateById(input.certificateId);
|
||||||
if (
|
if (certificates.organizationId !== ctx.session.activeOrganizationId) {
|
||||||
IS_CLOUD &&
|
|
||||||
certificates.organizationId !== ctx.session.activeOrganizationId
|
|
||||||
) {
|
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "UNAUTHORIZED",
|
code: "UNAUTHORIZED",
|
||||||
message: "You are not allowed to access this certificate",
|
message: "You are not allowed to access this certificate",
|
||||||
@@ -47,10 +44,7 @@ export const certificateRouter = createTRPCRouter({
|
|||||||
.input(apiFindCertificate)
|
.input(apiFindCertificate)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const certificates = await findCertificateById(input.certificateId);
|
const certificates = await findCertificateById(input.certificateId);
|
||||||
if (
|
if (certificates.organizationId !== ctx.session.activeOrganizationId) {
|
||||||
IS_CLOUD &&
|
|
||||||
certificates.organizationId !== ctx.session.activeOrganizationId
|
|
||||||
) {
|
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "UNAUTHORIZED",
|
code: "UNAUTHORIZED",
|
||||||
message: "You are not allowed to delete this certificate",
|
message: "You are not allowed to delete this certificate",
|
||||||
@@ -61,13 +55,7 @@ export const certificateRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
all: adminProcedure.query(async ({ ctx }) => {
|
all: adminProcedure.query(async ({ ctx }) => {
|
||||||
return await db.query.certificates.findMany({
|
return await db.query.certificates.findMany({
|
||||||
// TODO: Remove this line when the cloud version is ready
|
where: eq(certificates.organizationId, ctx.session.activeOrganizationId),
|
||||||
...(IS_CLOUD && {
|
|
||||||
where: eq(
|
|
||||||
certificates.organizationId,
|
|
||||||
ctx.session.activeOrganizationId,
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
86
apps/dokploy/server/api/routers/organization.ts
Normal file
86
apps/dokploy/server/api/routers/organization.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import { adminProcedure, createTRPCRouter } from "../trpc";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { db } from "@/server/db";
|
||||||
|
import { member, organization } from "@/server/db/schema";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
|
import { desc, eq } from "drizzle-orm";
|
||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
export const organizationRouter = createTRPCRouter({
|
||||||
|
create: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const result = await db
|
||||||
|
.insert(organization)
|
||||||
|
.values({
|
||||||
|
...input,
|
||||||
|
slug: nanoid(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
ownerId: ctx.user.ownerId,
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.then((res) => res[0]);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "Failed to create organization",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberResult = await db.insert(member).values({
|
||||||
|
organizationId: result.id,
|
||||||
|
role: "owner",
|
||||||
|
createdAt: new Date(),
|
||||||
|
userId: ctx.user.id,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}),
|
||||||
|
all: adminProcedure.query(async ({ ctx }) => {
|
||||||
|
return await db.query.organization.findMany({
|
||||||
|
where: eq(organization.ownerId, ctx.user.ownerId),
|
||||||
|
orderBy: [desc(organization.createdAt)],
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
one: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
organizationId: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
return await db.query.organization.findFirst({
|
||||||
|
where: eq(organization.id, input.organizationId),
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
update: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
organizationId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const result = await db
|
||||||
|
.update(organization)
|
||||||
|
.set({ name: input.name })
|
||||||
|
.where(eq(organization.id, input.organizationId))
|
||||||
|
.returning();
|
||||||
|
return result[0];
|
||||||
|
}),
|
||||||
|
delete: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
organizationId: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const result = await db
|
||||||
|
.delete(organization)
|
||||||
|
.where(eq(organization.id, input.organizationId));
|
||||||
|
return result;
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -16,8 +16,8 @@ import {
|
|||||||
updateRegistry,
|
updateRegistry,
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
|
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
|
|
||||||
export const registryRouter = createTRPCRouter({
|
export const registryRouter = createTRPCRouter({
|
||||||
create: adminProcedure
|
create: adminProcedure
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export const sshRouter = createTRPCRouter({
|
|||||||
.input(apiCreateSshKey)
|
.input(apiCreateSshKey)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
console.log(ctx.user.ownerId);
|
|
||||||
await createSshKey({
|
await createSshKey({
|
||||||
...input,
|
...input,
|
||||||
organizationId: ctx.session.activeOrganizationId,
|
organizationId: ctx.session.activeOrganizationId,
|
||||||
|
|||||||
@@ -2,10 +2,17 @@ import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema";
|
|||||||
import { findUserByAuthId, findUserById, findUsers } from "@dokploy/server";
|
import { findUserByAuthId, findUserById, findUsers } from "@dokploy/server";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
|
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { member } from "@dokploy/server/db/schema";
|
||||||
|
import { db } from "@dokploy/server/db";
|
||||||
export const userRouter = createTRPCRouter({
|
export const userRouter = createTRPCRouter({
|
||||||
all: adminProcedure.query(async ({ ctx }) => {
|
all: adminProcedure.query(async ({ ctx }) => {
|
||||||
return await findUsers(ctx.user.adminId);
|
return await db.query.member.findMany({
|
||||||
|
where: eq(member.organizationId, ctx.session.activeOrganizationId),
|
||||||
|
with: {
|
||||||
|
user: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
byAuthId: protectedProcedure
|
byAuthId: protectedProcedure
|
||||||
.input(apiFindOneUserByAuth)
|
.input(apiFindOneUserByAuth)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { relations } from "drizzle-orm";
|
|||||||
import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { users_temp } from "./user";
|
import { users_temp } from "./user";
|
||||||
|
import { server } from "./server";
|
||||||
|
import { projects } from "./project";
|
||||||
|
|
||||||
export const account = pgTable("account", {
|
export const account = pgTable("account", {
|
||||||
id: text("id")
|
id: text("id")
|
||||||
@@ -60,12 +62,17 @@ export const organization = pgTable("organization", {
|
|||||||
.references(() => users_temp.id),
|
.references(() => users_temp.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const organizationRelations = relations(organization, ({ one }) => ({
|
export const organizationRelations = relations(
|
||||||
|
organization,
|
||||||
|
({ one, many }) => ({
|
||||||
owner: one(users_temp, {
|
owner: one(users_temp, {
|
||||||
fields: [organization.ownerId],
|
fields: [organization.ownerId],
|
||||||
references: [users_temp.id],
|
references: [users_temp.id],
|
||||||
}),
|
}),
|
||||||
}));
|
servers: many(server),
|
||||||
|
projects: many(projects),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const member = pgTable("member", {
|
export const member = pgTable("member", {
|
||||||
id: text("id")
|
id: text("id")
|
||||||
|
|||||||
@@ -61,5 +61,5 @@ export const apiUpdateBitbucket = createSchema.extend({
|
|||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
bitbucketUsername: z.string().optional(),
|
bitbucketUsername: z.string().optional(),
|
||||||
bitbucketWorkspaceName: z.string().optional(),
|
bitbucketWorkspaceName: z.string().optional(),
|
||||||
userId: z.string().optional(),
|
organizationId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ export const apiCreateSlack = notificationsSchema
|
|||||||
export const apiUpdateSlack = apiCreateSlack.partial().extend({
|
export const apiUpdateSlack = apiCreateSlack.partial().extend({
|
||||||
notificationId: z.string().min(1),
|
notificationId: z.string().min(1),
|
||||||
slackId: z.string(),
|
slackId: z.string(),
|
||||||
userId: z.string().optional(),
|
organizationId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiTestSlackConnection = apiCreateSlack.pick({
|
export const apiTestSlackConnection = apiCreateSlack.pick({
|
||||||
@@ -177,7 +177,7 @@ export const apiCreateTelegram = notificationsSchema
|
|||||||
export const apiUpdateTelegram = apiCreateTelegram.partial().extend({
|
export const apiUpdateTelegram = apiCreateTelegram.partial().extend({
|
||||||
notificationId: z.string().min(1),
|
notificationId: z.string().min(1),
|
||||||
telegramId: z.string().min(1),
|
telegramId: z.string().min(1),
|
||||||
userId: z.string().optional(),
|
organizationId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiTestTelegramConnection = apiCreateTelegram.pick({
|
export const apiTestTelegramConnection = apiCreateTelegram.pick({
|
||||||
@@ -204,7 +204,7 @@ export const apiCreateDiscord = notificationsSchema
|
|||||||
export const apiUpdateDiscord = apiCreateDiscord.partial().extend({
|
export const apiUpdateDiscord = apiCreateDiscord.partial().extend({
|
||||||
notificationId: z.string().min(1),
|
notificationId: z.string().min(1),
|
||||||
discordId: z.string().min(1),
|
discordId: z.string().min(1),
|
||||||
userId: z.string().optional(),
|
organizationId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiTestDiscordConnection = apiCreateDiscord
|
export const apiTestDiscordConnection = apiCreateDiscord
|
||||||
@@ -238,7 +238,7 @@ export const apiCreateEmail = notificationsSchema
|
|||||||
export const apiUpdateEmail = apiCreateEmail.partial().extend({
|
export const apiUpdateEmail = apiCreateEmail.partial().extend({
|
||||||
notificationId: z.string().min(1),
|
notificationId: z.string().min(1),
|
||||||
emailId: z.string().min(1),
|
emailId: z.string().min(1),
|
||||||
userId: z.string().optional(),
|
organizationId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiTestEmailConnection = apiCreateEmail.pick({
|
export const apiTestEmailConnection = apiCreateEmail.pick({
|
||||||
|
|||||||
@@ -118,6 +118,10 @@ export const serverRelations = relations(server, ({ one, many }) => ({
|
|||||||
mysql: many(mysql),
|
mysql: many(mysql),
|
||||||
postgres: many(postgres),
|
postgres: many(postgres),
|
||||||
certificates: many(certificates),
|
certificates: many(certificates),
|
||||||
|
organization: one(organization, {
|
||||||
|
fields: [server.organizationId],
|
||||||
|
references: [organization.id],
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const createSchema = createInsertSchema(server, {
|
const createSchema = createInsertSchema(server, {
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ export type Bitbucket = typeof bitbucket.$inferSelect;
|
|||||||
|
|
||||||
export const createBitbucket = async (
|
export const createBitbucket = async (
|
||||||
input: typeof apiCreateBitbucket._type,
|
input: typeof apiCreateBitbucket._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
return await db.transaction(async (tx) => {
|
return await db.transaction(async (tx) => {
|
||||||
const newGitProvider = await tx
|
const newGitProvider = await tx
|
||||||
.insert(gitProvider)
|
.insert(gitProvider)
|
||||||
.values({
|
.values({
|
||||||
providerType: "bitbucket",
|
providerType: "bitbucket",
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
name: input.name,
|
name: input.name,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
@@ -74,12 +74,12 @@ export const updateBitbucket = async (
|
|||||||
.where(eq(bitbucket.bitbucketId, bitbucketId))
|
.where(eq(bitbucket.bitbucketId, bitbucketId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (input.name || input.userId) {
|
if (input.name || input.organizationId) {
|
||||||
await tx
|
await tx
|
||||||
.update(gitProvider)
|
.update(gitProvider)
|
||||||
.set({
|
.set({
|
||||||
name: input.name,
|
name: input.name,
|
||||||
userId: input.userId,
|
organizationId: input.organizationId,
|
||||||
})
|
})
|
||||||
.where(eq(gitProvider.gitProviderId, input.gitProviderId))
|
.where(eq(gitProvider.gitProviderId, input.gitProviderId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ export type Destination = typeof destinations.$inferSelect;
|
|||||||
|
|
||||||
export const createDestintation = async (
|
export const createDestintation = async (
|
||||||
input: typeof apiCreateDestination._type,
|
input: typeof apiCreateDestination._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
const newDestination = await db
|
const newDestination = await db
|
||||||
.insert(destinations)
|
.insert(destinations)
|
||||||
.values({
|
.values({
|
||||||
...input,
|
...input,
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
.then((value) => value[0]);
|
.then((value) => value[0]);
|
||||||
@@ -46,14 +46,14 @@ export const findDestinationById = async (destinationId: string) => {
|
|||||||
|
|
||||||
export const removeDestinationById = async (
|
export const removeDestinationById = async (
|
||||||
destinationId: string,
|
destinationId: string,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
const result = await db
|
const result = await db
|
||||||
.delete(destinations)
|
.delete(destinations)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(destinations.destinationId, destinationId),
|
eq(destinations.destinationId, destinationId),
|
||||||
eq(destinations.userId, userId),
|
eq(destinations.organizationId, organizationId),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.returning();
|
.returning();
|
||||||
@@ -73,7 +73,7 @@ export const updateDestinationById = async (
|
|||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(destinations.destinationId, destinationId),
|
eq(destinations.destinationId, destinationId),
|
||||||
eq(destinations.userId, destinationData.userId || ""),
|
eq(destinations.organizationId, destinationData.organizationId || ""),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.returning();
|
.returning();
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ import { updatePreviewDeployment } from "./preview-deployment";
|
|||||||
export type Github = typeof github.$inferSelect;
|
export type Github = typeof github.$inferSelect;
|
||||||
export const createGithub = async (
|
export const createGithub = async (
|
||||||
input: typeof apiCreateGithub._type,
|
input: typeof apiCreateGithub._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
return await db.transaction(async (tx) => {
|
return await db.transaction(async (tx) => {
|
||||||
const newGitProvider = await tx
|
const newGitProvider = await tx
|
||||||
.insert(gitProvider)
|
.insert(gitProvider)
|
||||||
.values({
|
.values({
|
||||||
providerType: "github",
|
providerType: "github",
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
name: input.name,
|
name: input.name,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ export type Gitlab = typeof gitlab.$inferSelect;
|
|||||||
|
|
||||||
export const createGitlab = async (
|
export const createGitlab = async (
|
||||||
input: typeof apiCreateGitlab._type,
|
input: typeof apiCreateGitlab._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
return await db.transaction(async (tx) => {
|
return await db.transaction(async (tx) => {
|
||||||
const newGitProvider = await tx
|
const newGitProvider = await tx
|
||||||
.insert(gitProvider)
|
.insert(gitProvider)
|
||||||
.values({
|
.values({
|
||||||
providerType: "gitlab",
|
providerType: "gitlab",
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
name: input.name,
|
name: input.name,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export type Notification = typeof notifications.$inferSelect;
|
|||||||
|
|
||||||
export const createSlackNotification = async (
|
export const createSlackNotification = async (
|
||||||
input: typeof apiCreateSlack._type,
|
input: typeof apiCreateSlack._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
const newSlack = await tx
|
const newSlack = await tx
|
||||||
@@ -54,7 +54,7 @@ export const createSlackNotification = async (
|
|||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "slack",
|
notificationType: "slack",
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
serverThreshold: input.serverThreshold,
|
serverThreshold: input.serverThreshold,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
@@ -84,7 +84,7 @@ export const updateSlackNotification = async (
|
|||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
userId: input.userId,
|
organizationId: input.organizationId,
|
||||||
serverThreshold: input.serverThreshold,
|
serverThreshold: input.serverThreshold,
|
||||||
})
|
})
|
||||||
.where(eq(notifications.notificationId, input.notificationId))
|
.where(eq(notifications.notificationId, input.notificationId))
|
||||||
@@ -114,7 +114,7 @@ export const updateSlackNotification = async (
|
|||||||
|
|
||||||
export const createTelegramNotification = async (
|
export const createTelegramNotification = async (
|
||||||
input: typeof apiCreateTelegram._type,
|
input: typeof apiCreateTelegram._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
const newTelegram = await tx
|
const newTelegram = await tx
|
||||||
@@ -144,7 +144,7 @@ export const createTelegramNotification = async (
|
|||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "telegram",
|
notificationType: "telegram",
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
serverThreshold: input.serverThreshold,
|
serverThreshold: input.serverThreshold,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
@@ -174,7 +174,7 @@ export const updateTelegramNotification = async (
|
|||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
userId: input.userId,
|
organizationId: input.organizationId,
|
||||||
serverThreshold: input.serverThreshold,
|
serverThreshold: input.serverThreshold,
|
||||||
})
|
})
|
||||||
.where(eq(notifications.notificationId, input.notificationId))
|
.where(eq(notifications.notificationId, input.notificationId))
|
||||||
@@ -204,7 +204,7 @@ export const updateTelegramNotification = async (
|
|||||||
|
|
||||||
export const createDiscordNotification = async (
|
export const createDiscordNotification = async (
|
||||||
input: typeof apiCreateDiscord._type,
|
input: typeof apiCreateDiscord._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
const newDiscord = await tx
|
const newDiscord = await tx
|
||||||
@@ -234,7 +234,7 @@ export const createDiscordNotification = async (
|
|||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "discord",
|
notificationType: "discord",
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
serverThreshold: input.serverThreshold,
|
serverThreshold: input.serverThreshold,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
@@ -264,7 +264,7 @@ export const updateDiscordNotification = async (
|
|||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
userId: input.userId,
|
organizationId: input.organizationId,
|
||||||
serverThreshold: input.serverThreshold,
|
serverThreshold: input.serverThreshold,
|
||||||
})
|
})
|
||||||
.where(eq(notifications.notificationId, input.notificationId))
|
.where(eq(notifications.notificationId, input.notificationId))
|
||||||
@@ -294,7 +294,7 @@ export const updateDiscordNotification = async (
|
|||||||
|
|
||||||
export const createEmailNotification = async (
|
export const createEmailNotification = async (
|
||||||
input: typeof apiCreateEmail._type,
|
input: typeof apiCreateEmail._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
const newEmail = await tx
|
const newEmail = await tx
|
||||||
@@ -328,7 +328,7 @@ export const createEmailNotification = async (
|
|||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "email",
|
notificationType: "email",
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
serverThreshold: input.serverThreshold,
|
serverThreshold: input.serverThreshold,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
@@ -358,7 +358,7 @@ export const updateEmailNotification = async (
|
|||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
userId: input.userId,
|
organizationId: input.organizationId,
|
||||||
serverThreshold: input.serverThreshold,
|
serverThreshold: input.serverThreshold,
|
||||||
})
|
})
|
||||||
.where(eq(notifications.notificationId, input.notificationId))
|
.where(eq(notifications.notificationId, input.notificationId))
|
||||||
@@ -392,7 +392,7 @@ export const updateEmailNotification = async (
|
|||||||
|
|
||||||
export const createGotifyNotification = async (
|
export const createGotifyNotification = async (
|
||||||
input: typeof apiCreateGotify._type,
|
input: typeof apiCreateGotify._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
const newGotify = await tx
|
const newGotify = await tx
|
||||||
@@ -424,7 +424,7 @@ export const createGotifyNotification = async (
|
|||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
notificationType: "gotify",
|
notificationType: "gotify",
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
.then((value) => value[0]);
|
.then((value) => value[0]);
|
||||||
@@ -453,7 +453,7 @@ export const updateGotifyNotification = async (
|
|||||||
databaseBackup: input.databaseBackup,
|
databaseBackup: input.databaseBackup,
|
||||||
dokployRestart: input.dokployRestart,
|
dokployRestart: input.dokployRestart,
|
||||||
dockerCleanup: input.dockerCleanup,
|
dockerCleanup: input.dockerCleanup,
|
||||||
userId: input.userId,
|
organizationId: input.organizationId,
|
||||||
})
|
})
|
||||||
.where(eq(notifications.notificationId, input.notificationId))
|
.where(eq(notifications.notificationId, input.notificationId))
|
||||||
.returning()
|
.returning()
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ export type Project = typeof projects.$inferSelect;
|
|||||||
|
|
||||||
export const createProject = async (
|
export const createProject = async (
|
||||||
input: typeof apiCreateProject._type,
|
input: typeof apiCreateProject._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
const newProject = await db
|
const newProject = await db
|
||||||
.insert(projects)
|
.insert(projects)
|
||||||
.values({
|
.values({
|
||||||
...input,
|
...input,
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
.then((value) => value[0]);
|
.then((value) => value[0]);
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import { type apiCreateServer, server } from "@dokploy/server/db/schema";
|
import {
|
||||||
|
type apiCreateServer,
|
||||||
|
organization,
|
||||||
|
server,
|
||||||
|
} from "@dokploy/server/db/schema";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { desc, eq } from "drizzle-orm";
|
import { desc, eq } from "drizzle-orm";
|
||||||
|
|
||||||
@@ -7,13 +11,13 @@ export type Server = typeof server.$inferSelect;
|
|||||||
|
|
||||||
export const createServer = async (
|
export const createServer = async (
|
||||||
input: typeof apiCreateServer._type,
|
input: typeof apiCreateServer._type,
|
||||||
userId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
const newServer = await db
|
const newServer = await db
|
||||||
.insert(server)
|
.insert(server)
|
||||||
.values({
|
.values({
|
||||||
...input,
|
...input,
|
||||||
userId: userId,
|
organizationId: organizationId,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
@@ -47,11 +51,15 @@ export const findServerById = async (serverId: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const findServersByUserId = async (userId: string) => {
|
export const findServersByUserId = async (userId: string) => {
|
||||||
const servers = await db.query.server.findMany({
|
const orgs = await db.query.organization.findMany({
|
||||||
where: eq(server.userId, userId),
|
where: eq(organization.ownerId, userId),
|
||||||
orderBy: desc(server.createdAt),
|
with: {
|
||||||
|
servers: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const servers = orgs.flatMap((org) => org.servers);
|
||||||
|
|
||||||
return servers;
|
return servers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user