refactor: migrate admin API calls to user router

This commit is contained in:
Mauricio Siu
2025-02-20 23:02:02 -06:00
parent 5a1145996d
commit 790894ab93
40 changed files with 185 additions and 159 deletions

View File

@@ -79,7 +79,7 @@ export const ContainerPaidMonitoring = ({ appName, baseUrl, token }: Props) => {
data, data,
isLoading, isLoading,
error: queryError, error: queryError,
} = api.admin.getContainerMetrics.useQuery( } = api.user.getContainerMetrics.useQuery(
{ {
url: baseUrl, url: baseUrl,
token, token,

View File

@@ -73,7 +73,7 @@ export const ShowPaidMonitoring = ({
data, data,
isLoading, isLoading,
error: queryError, error: queryError,
} = api.admin.getServerMetrics.useQuery( } = api.user.getServerMetrics.useQuery(
{ {
url: BASE_URL, url: BASE_URL,
token, token,

View File

@@ -51,7 +51,7 @@ import { ProjectEnvironment } from "./project-environment";
export const ShowProjects = () => { export const ShowProjects = () => {
const utils = api.useUtils(); const utils = api.useUtils();
const { data, isLoading } = api.project.all.useQuery(); const { data, isLoading } = api.project.all.useQuery();
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { mutateAsync } = api.project.remove.useMutation(); const { mutateAsync } = api.project.remove.useMutation();
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");

View File

@@ -39,7 +39,7 @@ export const calculatePrice = (count: number, isAnnual = false) => {
}; };
export const ShowBilling = () => { export const ShowBilling = () => {
const { data: servers } = api.server.all.useQuery(undefined); const { data: servers } = api.server.all.useQuery(undefined);
const { data: admin } = api.admin.one.useQuery(); const { data: admin } = api.user.get.useQuery();
const { data, isLoading } = api.stripe.getProducts.useQuery(); const { data, isLoading } = api.stripe.getProducts.useQuery();
const { mutateAsync: createCheckoutSession } = const { mutateAsync: createCheckoutSession } =
api.stripe.createCheckoutSession.useMutation(); api.stripe.createCheckoutSession.useMutation();
@@ -70,7 +70,7 @@ export const ShowBilling = () => {
return isAnnual ? interval === "year" : interval === "month"; return isAnnual ? interval === "year" : interval === "month";
}); });
const maxServers = admin?.serversQuantity ?? 1; const maxServers = admin?.user.serversQuantity ?? 1;
const percentage = ((servers?.length ?? 0) / maxServers) * 100; const percentage = ((servers?.length ?? 0) / maxServers) * 100;
const safePercentage = Math.min(percentage, 100); const safePercentage = Math.min(percentage, 100);
@@ -98,17 +98,17 @@ export const ShowBilling = () => {
<TabsTrigger value="annual">Annual</TabsTrigger> <TabsTrigger value="annual">Annual</TabsTrigger>
</TabsList> </TabsList>
</Tabs> </Tabs>
{admin?.stripeSubscriptionId && ( {admin?.user.stripeSubscriptionId && (
<div className="space-y-2 flex flex-col"> <div className="space-y-2 flex flex-col">
<h3 className="text-lg font-medium">Servers Plan</h3> <h3 className="text-lg font-medium">Servers Plan</h3>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
You have {servers?.length} server on your plan of{" "} You have {servers?.length} server on your plan of{" "}
{admin?.serversQuantity} servers {admin?.user.serversQuantity} servers
</p> </p>
<div> <div>
<Progress value={safePercentage} className="max-w-lg" /> <Progress value={safePercentage} className="max-w-lg" />
</div> </div>
{admin && admin.serversQuantity! <= servers?.length! && ( {admin && admin.user.serversQuantity! <= servers?.length! && (
<div className="flex flex-row gap-4 p-2 bg-yellow-50 dark:bg-yellow-950 rounded-lg items-center"> <div className="flex flex-row gap-4 p-2 bg-yellow-50 dark:bg-yellow-950 rounded-lg items-center">
<AlertTriangle className="text-yellow-600 dark:text-yellow-400" /> <AlertTriangle className="text-yellow-600 dark:text-yellow-400" />
<span className="text-sm text-yellow-600 dark:text-yellow-400"> <span className="text-sm text-yellow-600 dark:text-yellow-400">
@@ -279,7 +279,7 @@ export const ShowBilling = () => {
"flex flex-row items-center gap-2 mt-4", "flex flex-row items-center gap-2 mt-4",
)} )}
> >
{admin?.stripeCustomerId && ( {admin?.user.stripeCustomerId && (
<Button <Button
variant="secondary" variant="secondary"
className="w-full" className="w-full"

View File

@@ -10,7 +10,7 @@ import type React from "react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
export const ShowWelcomeDokploy = () => { export const ShowWelcomeDokploy = () => {
const { data } = api.auth.get.useQuery(); const { data } = api.user.get.useQuery();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); const { data: isCloud, isLoading } = api.settings.isCloud.useQuery();

View File

@@ -53,7 +53,7 @@ export const AddBitbucketProvider = () => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const url = useUrl(); const url = useUrl();
const { mutateAsync, error, isError } = api.bitbucket.create.useMutation(); const { mutateAsync, error, isError } = api.bitbucket.create.useMutation();
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const router = useRouter(); const router = useRouter();
const form = useForm<Schema>({ const form = useForm<Schema>({
defaultValues: { defaultValues: {

View File

@@ -18,7 +18,7 @@ import { useEffect, useState } from "react";
export const AddGithubProvider = () => { export const AddGithubProvider = () => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const { data: activeOrganization } = authClient.useActiveOrganization(); const { data: activeOrganization } = authClient.useActiveOrganization();
const { data } = api.auth.get.useQuery(); const { data } = api.user.get.useQuery();
const [manifest, setManifest] = useState(""); const [manifest, setManifest] = useState("");
const [isOrganization, setIsOrganization] = useState(false); const [isOrganization, setIsOrganization] = useState(false);
const [organizationName, setOrganization] = useState(""); const [organizationName, setOrganization] = useState("");

View File

@@ -55,7 +55,7 @@ export const AddGitlabProvider = () => {
const utils = api.useUtils(); const utils = api.useUtils();
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const url = useUrl(); const url = useUrl();
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { mutateAsync, error, isError } = api.gitlab.create.useMutation(); const { mutateAsync, error, isError } = api.gitlab.create.useMutation();
const webhookUrl = `${url}/api/providers/gitlab/callback`; const webhookUrl = `${url}/api/providers/gitlab/callback`;

View File

@@ -62,6 +62,7 @@ export const Disable2FA = () => {
toast.success("2FA disabled successfully"); toast.success("2FA disabled successfully");
utils.auth.get.invalidate(); utils.auth.get.invalidate();
setIsOpen(false);
} catch (error) { } catch (error) {
form.setError("password", { form.setError("password", {
message: "Connection error. Please try again.", message: "Connection error. Please try again.",

View File

@@ -14,7 +14,7 @@ import Link from "next/link";
import { toast } from "sonner"; import { toast } from "sonner";
export const GenerateToken = () => { export const GenerateToken = () => {
const { data, refetch } = api.auth.get.useQuery(); const { data, refetch } = api.user.get.useQuery();
const { mutateAsync: generateToken, isLoading: isLoadingToken } = const { mutateAsync: generateToken, isLoading: isLoadingToken } =
api.auth.generateToken.useMutation(); api.auth.generateToken.useMutation();

View File

@@ -59,7 +59,7 @@ export const ProfileForm = () => {
const utils = api.useUtils(); const utils = api.useUtils();
const { mutateAsync: disable2FA, isLoading: isDisabling } = const { mutateAsync: disable2FA, isLoading: isDisabling } =
api.auth.disable2FA.useMutation(); api.auth.disable2FA.useMutation();
const { data, refetch, isLoading } = api.auth.get.useQuery(); const { data, refetch, isLoading } = api.user.get.useQuery();
const { const {
mutateAsync, mutateAsync,
isLoading: isUpdating, isLoading: isUpdating,

View File

@@ -35,7 +35,7 @@ const profileSchema = z.object({
type Profile = z.infer<typeof profileSchema>; type Profile = z.infer<typeof profileSchema>;
export const RemoveSelfAccount = () => { export const RemoveSelfAccount = () => {
const { data } = api.auth.get.useQuery(); const { data } = api.user.get.useQuery();
const { mutateAsync, isLoading, error, isError } = const { mutateAsync, isLoading, error, isError } =
api.auth.removeSelfAccount.useMutation(); api.auth.removeSelfAccount.useMutation();
const { t } = useTranslation("settings"); const { t } = useTranslation("settings");

View File

@@ -7,7 +7,7 @@ interface Props {
serverId?: string; serverId?: string;
} }
export const ToggleDockerCleanup = ({ serverId }: Props) => { export const ToggleDockerCleanup = ({ serverId }: Props) => {
const { data, refetch } = api.admin.one.useQuery(undefined, { const { data, refetch } = api.user.get.useQuery(undefined, {
enabled: !serverId, enabled: !serverId,
}); });
@@ -20,7 +20,7 @@ export const ToggleDockerCleanup = ({ serverId }: Props) => {
}, },
); );
const enabled = data?.enableDockerCleanup || server?.enableDockerCleanup; const enabled = data?.user.enableDockerCleanup || server?.enableDockerCleanup;
const { mutateAsync } = api.settings.updateDockerCleanup.useMutation(); const { mutateAsync } = api.settings.updateDockerCleanup.useMutation();

View File

@@ -91,7 +91,7 @@ export const SetupMonitoring = ({ serverId }: Props) => {
enabled: !!serverId, enabled: !!serverId,
}, },
) )
: api.admin.one.useQuery(); : api.user.get.useQuery();
const url = useUrl(); const url = useUrl();

View File

@@ -62,7 +62,7 @@ export const AddUserPermissions = ({ userId }: Props) => {
); );
const { mutateAsync, isError, error, isLoading } = const { mutateAsync, isError, error, isLoading } =
api.admin.assignPermissions.useMutation(); api.user.assignPermissions.useMutation();
const form = useForm<AddPermissions>({ const form = useForm<AddPermissions>({
defaultValues: { defaultValues: {

View File

@@ -26,7 +26,7 @@ import {
import { authClient } from "@/lib/auth-client"; import { authClient } from "@/lib/auth-client";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { format } from "date-fns"; import { format, isPast } from "date-fns";
import { Mail, MoreHorizontal, Users } from "lucide-react"; import { Mail, MoreHorizontal, Users } from "lucide-react";
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
@@ -35,8 +35,6 @@ import { AddInvitation } from "./add-invitation";
export const ShowInvitations = () => { export const ShowInvitations = () => {
const { data, isLoading, refetch } = const { data, isLoading, refetch } =
api.organization.allInvitations.useQuery(); api.organization.allInvitations.useQuery();
const { mutateAsync, isLoading: isRemoving } =
api.admin.removeUser.useMutation();
return ( return (
<div className="w-full"> <div className="w-full">
@@ -84,6 +82,9 @@ export const ShowInvitations = () => {
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{data?.map((invitation) => { {data?.map((invitation) => {
const isExpired = isPast(
new Date(invitation.expiresAt),
);
return ( return (
<TableRow key={invitation.id}> <TableRow key={invitation.id}>
<TableCell className="w-[100px]"> <TableCell className="w-[100px]">
@@ -104,17 +105,22 @@ export const ShowInvitations = () => {
<Badge <Badge
variant={ variant={
invitation.status === "pending" invitation.status === "pending"
? "default" ? "secondary"
: invitation.status === "canceled" : invitation.status === "canceled"
? "destructive" ? "destructive"
: "secondary" : "default"
} }
> >
{invitation.status} {invitation.status}
</Badge> </Badge>
</TableCell> </TableCell>
<TableCell className="text-center"> <TableCell className="text-center">
{format(new Date(invitation.expiresAt), "PPpp")} {format(new Date(invitation.expiresAt), "PPpp")}{" "}
{isExpired ? (
<span className="text-muted-foreground">
(Expired)
</span>
) : null}
</TableCell> </TableCell>
<TableCell className="text-right flex justify-end"> <TableCell className="text-right flex justify-end">
@@ -132,44 +138,51 @@ export const ShowInvitations = () => {
<DropdownMenuLabel> <DropdownMenuLabel>
Actions Actions
</DropdownMenuLabel> </DropdownMenuLabel>
{!isExpired && (
<>
{invitation.status === "pending" && (
<DropdownMenuItem
className="w-full cursor-pointer"
onSelect={(e) => {
copy(
`${origin}/invitation?token=${invitation.id}`,
);
toast.success(
"Invitation Copied to clipboard",
);
}}
>
Copy Invitation
</DropdownMenuItem>
)}
{invitation.status === "pending" && ( {invitation.status === "pending" && (
<DropdownMenuItem <DropdownMenuItem
className="w-full cursor-pointer" className="w-full cursor-pointer"
onSelect={(e) => { onSelect={async (e) => {
copy( const result =
`${origin}/invitation?token=${invitation.id}`, await authClient.organization.cancelInvitation(
); {
toast.success( invitationId: invitation.id,
"Invitation Copied to clipboard", },
); );
}}
>
Copy Invitation
</DropdownMenuItem>
)}
{invitation.status === "pending" && ( if (result.error) {
<DropdownMenuItem toast.error(
className="w-full cursor-pointer" result.error.message,
onSelect={async (e) => { );
const result = } else {
await authClient.organization.cancelInvitation( toast.success(
{ "Invitation deleted",
invitationId: invitation.id, );
}, refetch();
); }
}}
if (result.error) { >
toast.error(result.error.message); Cancel Invitation
} else { </DropdownMenuItem>
toast.success("Invitation deleted"); )}
refetch(); </>
}
}}
>
Cancel Invitation
</DropdownMenuItem>
)} )}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>

View File

@@ -75,9 +75,7 @@ export const ShowUsers = () => {
<TableHead className="w-[100px]">Email</TableHead> <TableHead className="w-[100px]">Email</TableHead>
<TableHead className="text-center">Role</TableHead> <TableHead className="text-center">Role</TableHead>
<TableHead className="text-center">2FA</TableHead> <TableHead className="text-center">2FA</TableHead>
<TableHead className="text-center">
Is Registered
</TableHead>
<TableHead className="text-center"> <TableHead className="text-center">
Created At Created At
</TableHead> </TableHead>
@@ -108,12 +106,6 @@ export const ShowUsers = () => {
: "Disabled"} : "Disabled"}
</TableCell> </TableCell>
<TableCell className="text-center"> <TableCell className="text-center">
{member.user.isRegistered ||
member.role === "owner"
? "Registered"
: "Not Registered"}
</TableCell>
<TableCell className="text-right">
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
{format(new Date(member.createdAt), "PPpp")} {format(new Date(member.createdAt), "PPpp")}
</span> </span>
@@ -134,22 +126,6 @@ export const ShowUsers = () => {
<DropdownMenuLabel> <DropdownMenuLabel>
Actions Actions
</DropdownMenuLabel> </DropdownMenuLabel>
{!member.user.isRegistered &&
member.role !== "owner" && (
<DropdownMenuItem
className="w-full cursor-pointer"
onSelect={(e) => {
copy(
`${origin}/invitation?token=${member.user.token}`,
);
toast.success(
"Invitation Copied to clipboard",
);
}}
>
Copy Invitation
</DropdownMenuItem>
)}
{member.role !== "owner" && ( {member.role !== "owner" && (
<AddUserPermissions <AddUserPermissions

View File

@@ -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, refetch } = api.auth.get.useQuery(); const { data, refetch } = api.user.get.useQuery();
const { mutateAsync, isLoading } = const { mutateAsync, isLoading } =
api.settings.assignDomainServer.useMutation(); api.settings.assignDomainServer.useMutation();

View File

@@ -21,7 +21,7 @@ interface Props {
} }
export const WebServer = ({ className }: Props) => { export const WebServer = ({ className }: Props) => {
const { t } = useTranslation("settings"); const { t } = useTranslation("settings");
const { data } = api.admin.one.useQuery(); const { data } = api.user.get.useQuery();
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
@@ -58,7 +58,7 @@ export const WebServer = ({ className }: Props) => {
<div className="flex items-center flex-wrap justify-between gap-4"> <div className="flex items-center flex-wrap justify-between gap-4">
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
Server IP: {data?.serverIp} Server IP: {data?.user.serverIp}
</span> </span>
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
Version: {dokployVersion} Version: {dokployVersion}

View File

@@ -54,7 +54,7 @@ export const UpdateServerIp = ({ children, serverId }: Props) => {
const form = useForm<Schema>({ const form = useForm<Schema>({
defaultValues: { defaultValues: {
serverIp: data?.serverIp || "", serverIp: data?.user.serverIp || "",
}, },
resolver: zodResolver(schema), resolver: zodResolver(schema),
}); });
@@ -62,7 +62,7 @@ export const UpdateServerIp = ({ children, serverId }: Props) => {
useEffect(() => { useEffect(() => {
if (data) { if (data) {
form.reset({ form.reset({
serverIp: data.serverIp || "", serverIp: data.user.serverIp || "",
}); });
} }
}, [form, form.reset, data]); }, [form, form.reset, data]);

View File

@@ -526,7 +526,7 @@ const data = {
function SidebarLogo() { function SidebarLogo() {
const { state } = useSidebar(); const { state } = useSidebar();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();
const { data: user } = api.auth.get.useQuery(); const { data: user } = api.user.get.useQuery();
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
const { data: session } = authClient.useSession(); const { data: session } = authClient.useSession();
const { const {
@@ -714,7 +714,7 @@ export default function Page({ children }: Props) {
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
const currentPath = router.pathname; const currentPath = router.pathname;
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const includesProjects = pathname?.includes("/dashboard/project"); const includesProjects = pathname?.includes("/dashboard/project");
const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); const { data: isCloud, isLoading } = api.settings.isCloud.useQuery();

View File

@@ -30,7 +30,7 @@ const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7;
export const UserNav = () => { export const UserNav = () => {
const router = useRouter(); const router = useRouter();
const { data } = api.auth.get.useQuery(); const { data } = api.user.get.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();
const { locale, setLocale } = useLocale(); const { locale, setLocale } = useLocale();

View File

@@ -25,7 +25,7 @@ const Dashboard = () => {
false, false,
); );
const { data: monitoring, isLoading } = api.admin.getMetricsToken.useQuery(); const { data: monitoring, isLoading } = api.user.getMetricsToken.useQuery();
return ( return (
<div className="space-y-4 pb-10"> <div className="space-y-4 pb-10">
{/* <AlertBlock> {/* <AlertBlock>

View File

@@ -200,7 +200,7 @@ const Project = (
) => { ) => {
const [isBulkActionLoading, setIsBulkActionLoading] = useState(false); const [isBulkActionLoading, setIsBulkActionLoading] = useState(false);
const { projectId } = props; const { projectId } = props;
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { data, isLoading, refetch } = api.project.one.useQuery({ projectId }); const { data, isLoading, refetch } = api.project.one.useQuery({ projectId });
const router = useRouter(); const router = useRouter();

View File

@@ -86,8 +86,8 @@ const Service = (
); );
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery(); const { data: monitoring } = api.user.getMetricsToken.useQuery();
return ( return (
<div className="pb-10"> <div className="pb-10">

View File

@@ -79,8 +79,8 @@ const Service = (
}, },
); );
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery(); const { data: monitoring } = api.user.getMetricsToken.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();
return ( return (

View File

@@ -61,8 +61,8 @@ const Mariadb = (
const { projectId } = router.query; const { projectId } = router.query;
const [tab, setSab] = useState<TabState>(activeTab); const [tab, setSab] = useState<TabState>(activeTab);
const { data } = api.mariadb.one.useQuery({ mariadbId }); const { data } = api.mariadb.one.useQuery({ mariadbId });
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery(); const { data: monitoring } = api.user.getMetricsToken.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();

View File

@@ -61,8 +61,8 @@ const Mongo = (
const [tab, setSab] = useState<TabState>(activeTab); const [tab, setSab] = useState<TabState>(activeTab);
const { data } = api.mongo.one.useQuery({ mongoId }); const { data } = api.mongo.one.useQuery({ mongoId });
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery(); const { data: monitoring } = api.user.getMetricsToken.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();

View File

@@ -60,8 +60,8 @@ const MySql = (
const { projectId } = router.query; const { projectId } = router.query;
const [tab, setSab] = useState<TabState>(activeTab); const [tab, setSab] = useState<TabState>(activeTab);
const { data } = api.mysql.one.useQuery({ mysqlId }); const { data } = api.mysql.one.useQuery({ mysqlId });
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery(); const { data: monitoring } = api.user.getMetricsToken.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();

View File

@@ -60,9 +60,9 @@ const Postgresql = (
const { projectId } = router.query; const { projectId } = router.query;
const [tab, setSab] = useState<TabState>(activeTab); const [tab, setSab] = useState<TabState>(activeTab);
const { data } = api.postgres.one.useQuery({ postgresId }); const { data } = api.postgres.one.useQuery({ postgresId });
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery(); const { data: monitoring } = api.user.getMetricsToken.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();
return ( return (

View File

@@ -60,8 +60,8 @@ const Redis = (
const [tab, setSab] = useState<TabState>(activeTab); const [tab, setSab] = useState<TabState>(activeTab);
const { data } = api.redis.one.useQuery({ redisId }); const { data } = api.redis.one.useQuery({ redisId });
const { data: auth } = api.auth.get.useQuery(); const { data: auth } = api.user.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery(); const { data: monitoring } = api.user.getMetricsToken.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();

View File

@@ -42,9 +42,9 @@ const settings = z.object({
type SettingsType = z.infer<typeof settings>; type SettingsType = z.infer<typeof settings>;
const Page = () => { const Page = () => {
const { data, refetch } = api.admin.one.useQuery(); const { data, refetch } = api.user.get.useQuery();
const { mutateAsync, isLoading, isError, error } = const { mutateAsync, isLoading, isError, error } =
api.admin.update.useMutation(); api.user.update.useMutation();
const form = useForm<SettingsType>({ const form = useForm<SettingsType>({
defaultValues: { defaultValues: {
cleanCacheOnApplications: false, cleanCacheOnApplications: false,
@@ -55,9 +55,9 @@ const Page = () => {
}); });
useEffect(() => { useEffect(() => {
form.reset({ form.reset({
cleanCacheOnApplications: data?.cleanupCacheApplications || false, cleanCacheOnApplications: data?.user.cleanupCacheApplications || false,
cleanCacheOnCompose: data?.cleanupCacheOnCompose || false, cleanCacheOnCompose: data?.user.cleanupCacheOnCompose || false,
cleanCacheOnPreviews: data?.cleanupCacheOnPreviews || false, cleanCacheOnPreviews: data?.user.cleanupCacheOnPreviews || false,
}); });
}, [form, form.reset, form.formState.isSubmitSuccessful, data]); }, [form, form.reset, form.formState.isSubmitSuccessful, data]);

View File

@@ -13,7 +13,7 @@ import React, { type ReactElement } from "react";
import superjson from "superjson"; import superjson from "superjson";
const Page = () => { const Page = () => {
const { data } = api.auth.get.useQuery(); const { data } = api.user.get.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();
return ( return (

View File

@@ -2,19 +2,7 @@ import { SetupMonitoring } from "@/components/dashboard/settings/servers/setup-m
import { WebDomain } from "@/components/dashboard/settings/web-domain"; import { WebDomain } from "@/components/dashboard/settings/web-domain";
import { WebServer } from "@/components/dashboard/settings/web-server"; import { WebServer } from "@/components/dashboard/settings/web-server";
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { DialogAction } from "@/components/shared/dialog-action";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Switch } from "@/components/ui/switch";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
import { getLocale, serverSideTranslations } from "@/utils/i18n"; import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { IS_CLOUD, validateRequest } from "@dokploy/server"; import { IS_CLOUD, validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
@@ -25,8 +13,6 @@ import { toast } from "sonner";
import superjson from "superjson"; import superjson from "superjson";
const Page = () => { const Page = () => {
const { data, refetch } = api.admin.one.useQuery();
const { mutateAsync: update } = api.admin.update.useMutation();
return ( return (
<div className="w-full"> <div className="w-full">
<div className="h-full rounded-xl max-w-5xl mx-auto flex flex-col gap-4"> <div className="h-full rounded-xl max-w-5xl mx-auto flex flex-col gap-4">

View File

@@ -4,6 +4,14 @@ import { AlertBlock } from "@/components/shared/alert-block";
import { Logo } from "@/components/shared/logo"; import { Logo } from "@/components/shared/logo";
import { Button, buttonVariants } from "@/components/ui/button"; import { Button, buttonVariants } from "@/components/ui/button";
import { CardContent, CardDescription } from "@/components/ui/card"; import { CardContent, CardDescription } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { import {
Form, Form,
FormControl, FormControl,
@@ -20,14 +28,6 @@ import {
InputOTPSlot, InputOTPSlot,
} from "@/components/ui/input-otp"; } from "@/components/ui/input-otp";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { authClient } from "@/lib/auth-client"; import { authClient } from "@/lib/auth-client";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { api } from "@/utils/api"; import { api } from "@/utils/api";

View File

@@ -85,7 +85,7 @@ const Invitation = ({
userAlreadyExists, userAlreadyExists,
}: Props) => { }: Props) => {
const router = useRouter(); const router = useRouter();
const { data } = api.admin.getUserByToken.useQuery( const { data } = api.user.getUserByToken.useQuery(
{ {
token, token,
}, },

View File

@@ -1,4 +1,3 @@
import { db } from "@/server/db";
import { import {
apiAssignPermissions, apiAssignPermissions,
apiCreateUserInvitation, apiCreateUserInvitation,
@@ -14,11 +13,9 @@ import {
getUserByToken, getUserByToken,
removeUserById, removeUserById,
setupWebMonitoring, setupWebMonitoring,
updateAdminById,
updateUser, updateUser,
} from "@dokploy/server"; } from "@dokploy/server";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { z } from "zod"; import { z } from "zod";
import { import {
adminProcedure, adminProcedure,
@@ -131,7 +128,7 @@ export const adminRouter = createTRPCRouter({
if (user.id !== ctx.user.ownerId) { if (user.id !== ctx.user.ownerId) {
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not authorized to setup this server", message: "You are not authorized to setup the monitoring",
}); });
} }

View File

@@ -39,7 +39,6 @@ import {
createComposeByTemplate, createComposeByTemplate,
createDomain, createDomain,
createMount, createMount,
findAdminById,
findComposeById, findComposeById,
findDomainsByComposeId, findDomainsByComposeId,
findProjectById, findProjectById,

View File

@@ -1,11 +1,6 @@
import { db } from "@/server/db"; import { db } from "@/server/db";
import { import { invitation, member, organization } from "@/server/db/schema";
invitation, import { IS_CLOUD } from "@dokploy/server/index";
member,
organization,
users_temp,
} from "@/server/db/schema";
import { IS_CLOUD, auth } from "@dokploy/server/index";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { and, desc, eq, exists } from "drizzle-orm"; import { and, desc, eq, exists } from "drizzle-orm";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
@@ -45,7 +40,7 @@ export const organizationRouter = createTRPCRouter({
}); });
} }
const memberResult = await db.insert(member).values({ await db.insert(member).values({
organizationId: result.id, organizationId: result.id,
role: "owner", role: "owner",
createdAt: new Date(), createdAt: new Date(),
@@ -142,7 +137,7 @@ export const organizationRouter = createTRPCRouter({
allInvitations: adminProcedure.query(async ({ ctx }) => { allInvitations: adminProcedure.query(async ({ ctx }) => {
return await db.query.invitation.findMany({ return await db.query.invitation.findMany({
where: eq(invitation.organizationId, ctx.session.activeOrganizationId), where: eq(invitation.organizationId, ctx.session.activeOrganizationId),
orderBy: [desc(invitation.status)], orderBy: [desc(invitation.status), desc(invitation.expiresAt)],
}); });
}), }),
acceptInvitation: adminProcedure acceptInvitation: adminProcedure

View File

@@ -1,18 +1,31 @@
import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema"; import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema";
import { import {
IS_CLOUD, IS_CLOUD,
findOrganizationById,
findUserByAuthId, findUserByAuthId,
findUserById, findUserById,
getUserByToken,
removeUserById, removeUserById,
updateUser, updateUser,
verify2FA, verify2FA,
} from "@dokploy/server"; } from "@dokploy/server";
import { db } from "@dokploy/server/db"; import { db } from "@dokploy/server/db";
import { account, apiUpdateUser, member } from "@dokploy/server/db/schema"; import {
account,
apiAssignPermissions,
apiFindOneToken,
apiUpdateUser,
member,
} from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
import { z } from "zod"; import { z } from "zod";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; import {
adminProcedure,
createTRPCRouter,
protectedProcedure,
publicProcedure,
} from "../trpc";
export const userRouter = createTRPCRouter({ export const userRouter = createTRPCRouter({
all: adminProcedure.query(async ({ ctx }) => { all: adminProcedure.query(async ({ ctx }) => {
return await db.query.member.findMany({ return await db.query.member.findMany({
@@ -39,14 +52,36 @@ export const userRouter = createTRPCRouter({
return user; return user;
}), }),
get: protectedProcedure.query(async ({ ctx }) => { get: protectedProcedure.query(async ({ ctx }) => {
return await findUserById(ctx.user.id); const memberResult = await db.query.member.findFirst({
where: and(
eq(member.userId, ctx.user.id),
eq(member.organizationId, ctx.session?.activeOrganizationId || ""),
),
with: {
user: true,
},
});
return memberResult;
}), }),
update: protectedProcedure update: protectedProcedure
.input(apiUpdateUser) .input(apiUpdateUser)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
return await updateUser(ctx.user.id, input); return await updateUser(ctx.user.id, input);
}), }),
getUserByToken: publicProcedure
.input(apiFindOneToken)
.query(async ({ input }) => {
return await getUserByToken(input.token);
}),
getMetricsToken: protectedProcedure.query(async ({ ctx }) => {
const user = await findUserById(ctx.user.ownerId);
return {
serverIp: user.serverIp,
enabledFeatures: user.enablePaidFeatures,
metricsConfig: user?.metricsConfig,
};
}),
remove: protectedProcedure remove: protectedProcedure
.input( .input(
z.object({ z.object({
@@ -59,4 +94,28 @@ export const userRouter = createTRPCRouter({
} }
return await removeUserById(input.userId); return await removeUserById(input.userId);
}), }),
assignPermissions: adminProcedure
.input(apiAssignPermissions)
.mutation(async ({ input, ctx }) => {
try {
const user = await findUserById(input.id);
const organization = await findOrganizationById(
ctx.session?.activeOrganizationId || "",
);
if (organization?.ownerId !== ctx.user.ownerId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to assign permissions",
});
}
await updateUser(user.id, {
...input,
});
} catch (error) {
throw error;
}
}),
}); });