feat: adding system settings page. And did some refactoring.

This commit is contained in:
charlesgauthereau
2026-01-17 22:26:44 +01:00
parent e63e62d3e0
commit 5b980fb382
24 changed files with 238 additions and 161 deletions
@@ -2,6 +2,10 @@ import {PageParams} from "@/types/next";
import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page"; import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
import {db} from "@/db"; import {db} from "@/db";
import {notFound} from "next/navigation"; import {notFound} from "next/navigation";
import {SettingsTabs} from "@/components/wrappers/dashboard/admin/settings/settings-tabs";
import {desc, isNull} from "drizzle-orm";
import * as drizzleDb from "@/db";
import {StorageChannelWith} from "@/db/schema/12_storage-channel";
export default async function RoutePage(props: PageParams<{}>) { export default async function RoutePage(props: PageParams<{}>) {
@@ -9,11 +13,18 @@ export default async function RoutePage(props: PageParams<{}>) {
where: (fields, {eq}) => eq(fields.name, "system"), where: (fields, {eq}) => eq(fields.name, "system"),
}); });
if (!settings) { const storageChannels = await db.query.storageChannel.findMany({
with: {
organizations: true
},
where: isNull(drizzleDb.schemas.storageChannel.organizationId),
orderBy: desc(drizzleDb.schemas.storageChannel.createdAt)
}) as StorageChannelWith[]
if (!settings || !storageChannels ) {
notFound() notFound()
} }
return ( return (
<Page> <Page>
<PageHeader className="flex flex-col"> <PageHeader className="flex flex-col">
@@ -22,6 +33,7 @@ export default async function RoutePage(props: PageParams<{}>) {
</div> </div>
</PageHeader> </PageHeader>
<PageContent className="flex flex-col gap-5"> <PageContent className="flex flex-col gap-5">
<SettingsTabs storageChannels={storageChannels} settings={settings} />
</PageContent> </PageContent>
</Page> </Page>
); );
@@ -3,15 +3,15 @@ import {Page, PageActions, PageContent, PageHeader, PageTitle} from "@/features/
import {currentUser} from "@/lib/auth/current-user"; import {currentUser} from "@/lib/auth/current-user";
import {getActiveMember, getOrganization} from "@/lib/auth/auth"; import {getActiveMember, getOrganization} from "@/lib/auth/auth";
import {notFound} from "next/navigation"; import {notFound} from "next/navigation";
import {
DeleteOrganizationButton
} from "@/components/wrappers/dashboard/organization/delete-organization/delete-organization-button";
import {EditButtonSettings} from "@/components/wrappers/dashboard/settings/edit-button-settings/edit-button-settings";
import {Metadata} from "next"; import {Metadata} from "next";
import {OrganizationTabs} from "@/components/wrappers/dashboard/organization/tabs/organization-tabs"; import {OrganizationTabs} from "@/components/wrappers/dashboard/organization/tabs/organization-tabs";
import {getOrganizationChannels} from "@/db/services/notification-channel"; import {getOrganizationChannels} from "@/db/services/notification-channel";
import {computeOrganizationPermissions} from "@/lib/acl/organization-acl"; import {computeOrganizationPermissions} from "@/lib/acl/organization-acl";
import {getOrganizationStorageChannels} from "@/db/services/storage-channel"; import {getOrganizationStorageChannels} from "@/db/services/storage-channel";
import {DeleteOrganizationButton} from "@/components/wrappers/dashboard/organization/delete-organization-button";
import {
EditButtonSettings
} from "@/components/wrappers/dashboard/organization/settings/edit-button-settings/edit-button-settings";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Settings", title: "Settings",
-2
View File
@@ -1,13 +1,11 @@
import {NextResponse} from "next/server"; import {NextResponse} from "next/server";
import {isUuidv4} from "@/utils/verify-uuid"; import {isUuidv4} from "@/utils/verify-uuid";
import {uploadLocalPrivate, uploadS3Private} from "@/features/upload/private/upload.action";
import {v4 as uuidv4} from "uuid"; import {v4 as uuidv4} from "uuid";
import {eventEmitter} from "../../../events/route"; import {eventEmitter} from "../../../events/route";
import * as drizzleDb from "@/db"; import * as drizzleDb from "@/db";
import {db} from "@/db"; import {db} from "@/db";
import {Backup} from "@/db/schema/07_database"; import {Backup} from "@/db/schema/07_database";
import {and, eq} from "drizzle-orm"; import {and, eq} from "drizzle-orm";
import {env} from "@/env.mjs";
import {withUpdatedAt} from "@/db/utils"; import {withUpdatedAt} from "@/db/utils";
import {decryptedDump, getFileExtension} from "./helpers"; import {decryptedDump, getFileExtension} from "./helpers";
import {sendNotificationsBackupRestore} from "@/features/notifications/helpers"; import {sendNotificationsBackupRestore} from "@/features/notifications/helpers";
+2
View File
@@ -46,7 +46,9 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: R
...classNames, ...classNames,
}} }}
components={{ components={{
// @ts-ignore
IconLeft: ({ className, children: _children, ...props }) => <ChevronLeftIcon className={cn("size-4", className)} {...props} />, IconLeft: ({ className, children: _children, ...props }) => <ChevronLeftIcon className={cn("size-4", className)} {...props} />,
// @ts-ignore
IconRight: ({ className, children: _children, ...props }) => <ChevronRightIcon className={cn("size-4", className)} {...props} />, IconRight: ({ className, children: _children, ...props }) => <ChevronRightIcon className={cn("size-4", className)} {...props} />,
}} }}
{...props} {...props}
@@ -109,7 +109,6 @@ export const ButtonWithLoading = ({
{isPending && <Loader2 className="mr-2 animate-spin" size={16} />} {isPending && <Loader2 className="mr-2 animate-spin" size={16} />}
{children && children} {children && children}
<>{icon ? icon : null}</> <>{icon ? icon : null}</>
{/*{icon && <span className="ml-2">{icon}</span>}*/}
</Button> </Button>
); );
}; };
@@ -13,16 +13,14 @@ import {
DialogHeader, DialogHeader,
DialogTitle DialogTitle
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import {authClient} from "@/lib/auth/auth-client";
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select"; import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select";
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading"; import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
import {MemberRoleType} from "@/types/common"; import {MemberRoleType} from "@/types/common";
import {MemberWithUser} from "@/db/schema/03_organization"; import {MemberWithUser} from "@/db/schema/03_organization";
import {updateMemberRoleAction} from "@/components/wrappers/dashboard/settings/update-member.action";
import {RoleSchemaMember} from "@/components/wrappers/dashboard/settings/member.schema";
import { import {
updateMemberRoleAdminAction updateMemberRoleAdminAction
} from "@/components/wrappers/dashboard/admin/organizations/organization/details/role-member.action"; } from "@/components/wrappers/dashboard/admin/organizations/organization/details/role-member.action";
import {RoleSchemaMember} from "@/components/wrappers/dashboard/organization/settings/member.schema";
type OrganizationMemberChangeRoleModalProps = { type OrganizationMemberChangeRoleModalProps = {
open: boolean; open: boolean;
@@ -3,11 +3,11 @@ import {userAction} from "@/lib/safe-actions/actions";
import {z} from "zod"; import {z} from "zod";
import {ServerActionResult} from "@/types/action-type"; import {ServerActionResult} from "@/types/action-type";
import {Member} from "better-auth/plugins"; import {Member} from "better-auth/plugins";
import {RoleSchemaMember} from "@/components/wrappers/dashboard/settings/member.schema";
import {db as dbClient} from "@/db"; import {db as dbClient} from "@/db";
import * as drizzleDb from "@/db"; import * as drizzleDb from "@/db";
import {and, eq} from "drizzle-orm"; import {and, eq} from "drizzle-orm";
import {withUpdatedAt} from "@/db/utils"; import {withUpdatedAt} from "@/db/utils";
import {RoleSchemaMember} from "@/components/wrappers/dashboard/organization/settings/member.schema";
export const updateMemberRoleAdminAction = userAction.schema( export const updateMemberRoleAdminAction = userAction.schema(
@@ -1,49 +0,0 @@
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card";
import {Button} from "@/components/ui/button";
import {Download} from "lucide-react";
import {getFileUrlPresignedLocal} from "@/features/upload/private/upload.action";
import {toast} from "sonner";
export type AdminSettingsTabProps = {};
export const AdminSettingsSection = (props: AdminSettingsTabProps) => {
const handleDownloadKey = async () => {
let url: string = "";
const data = await getFileUrlPresignedLocal({dir: "private/keys/", fileName: "server_public.pem"})
if (data?.data?.success) {
url = data.data.value ?? "";
} else {
// @ts-ignore
const errorMessage = data?.data?.actionError?.message || "Failed to get file!";
toast.error(errorMessage);
}
window.open(url, "_self");
};
return (
<div className="flex flex-col gap-y-4 h-full py-4">
<Card>
<CardHeader>
<CardTitle>Instance settings</CardTitle>
<CardDescription>Manage portabase settings</CardDescription>
</CardHeader>
<CardContent>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">Download Public Key</p>
<p className="text-xs text-muted-foreground">
Used for encrypting communications with this instance.
</p>
</div>
<Button onClick={handleDownloadKey} variant="outline" size="sm">
<Download className="h-4 w-4 mr-2"/>
Download
</Button>
</div>
</CardContent>
</Card>
</div>
);
};
@@ -0,0 +1,61 @@
"use client";
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
import {useEffect, useState} from "react";
import {useRouter, useSearchParams} from "next/navigation";
import {Setting} from "@/db/schema/01_setting";
import {SettingsEmailSection} from "@/components/wrappers/dashboard/admin/settings/email/settings-email-section";
import {SettingsStorageSection} from "@/components/wrappers/dashboard/admin/settings/storage/settings-storage-section";
import {StorageChannelWith} from "@/db/schema/12_storage-channel";
export type SettingsTabsProps = {
settings: Setting
storageChannels: StorageChannelWith[]
};
export const SettingsTabs = ({settings, storageChannels}: SettingsTabsProps) => {
const router = useRouter();
const searchParams = useSearchParams();
const [tab, setTab] = useState<string>(() => searchParams.get("tab") ?? "email");
useEffect(() => {
const newTab = searchParams.get("tab") ?? "email";
setTab(newTab);
}, [searchParams]);
const handleChangeTab = (value: string) => {
router.push(`?tab=${value}`);
};
return (
<div className="h-full mt-3">
<Tabs className="h-full" value={tab} onValueChange={handleChangeTab}>
<TabsList className='bg-background rounded-none border-b p-0 min-w-48'>
<TabsTrigger
value="email"
className='bg-background data-[state=active]:border-primary dark:data-[state=active]:border-primary h-full rounded-none border-0 border-b-2 border-transparent data-[state=active]:shadow-none'
>
Email
</TabsTrigger>
<TabsTrigger
value="storage"
className='bg-background data-[state=active]:border-primary dark:data-[state=active]:border-primary h-full rounded-none border-0 border-b-2 border-transparent data-[state=active]:shadow-none'
>
Default storage
</TabsTrigger>
</TabsList>
<TabsContent className="h-full" value="email">
<SettingsEmailSection settings={settings}/>
</TabsContent>
<TabsContent className="h-full" value="storage">
<SettingsStorageSection storageChannels={storageChannels} settings={settings}/>
</TabsContent>
</Tabs>
</div>
);
};
@@ -1,106 +1,102 @@
"use client" "use client"
import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert"; import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert";
import {Info, ShieldCheck} from "lucide-react"; import {Info} from "lucide-react";
import {Switch} from "@/components/ui/switch";
import {Label} from "@/components/ui/label";
import {StorageS3Form} from "@/components/wrappers/dashboard/admin/settings/storage/storage-s3/storage-s3-form";
import {useState} from "react";
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading"; import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
import {useMutation} from "@tanstack/react-query";
import {checkConnexionToS3} from "@/features/upload/public/upload.action";
import {toast} from "sonner";
import {useRouter} from "next/navigation"; import {useRouter} from "next/navigation";
import {Setting} from "@/db/schema/01_setting";
import {Form, FormField, FormItem, useZodForm} from "@/components/ui/form";
import {StorageChannelWith} from "@/db/schema/12_storage-channel";
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select";
import {useMutation} from "@tanstack/react-query";
import {
DefaultStorageSchema,
DefaultStorageType
} from "@/components/wrappers/dashboard/admin/settings/storage/settings-storage.schema";
import {getChannelIcon} from "@/components/wrappers/dashboard/admin/channels/helpers/common";
import { import {
updateStorageSettingsAction updateStorageSettingsAction
} from "@/components/wrappers/dashboard/admin/settings/storage/storage-s3/s3-form.action"; } from "@/components/wrappers/dashboard/admin/settings/storage/settings-storage.action";
import {Setting} from "@/db/schema/01_setting"; import {toast} from "sonner";
import {S3FormType} from "@/components/wrappers/dashboard/admin/settings/storage/storage-s3/s3-form.schema";
export type SettingsStorageSectionProps = { export type SettingsStorageSectionProps = {
settings: Setting; settings: Setting;
storageChannels: StorageChannelWith[];
}; };
export const SettingsStorageSection = (props: SettingsStorageSectionProps) => { export const SettingsStorageSection = ({settings, storageChannels}: SettingsStorageSectionProps) => {
const router = useRouter(); const router = useRouter();
const mutation = useMutation({ const form = useZodForm({
mutationFn: async () => { schema: DefaultStorageSchema,
const result = await checkConnexionToS3(); defaultValues: {
if (result.error) { storageChannelId: settings.defaultStorageChannelId ?? undefined
toast.error("An error occured during the connexion !");
} else {
toast.success("Connexion succeed!");
} }
},
}); });
const [isSwitched, setIsSwitched] = useState<boolean>(props.settings.storage !== "local");
const updateMutation = useMutation({ const mutation = useMutation({
mutationFn: () => updateStorageSettingsAction({name: "system", data: {storage: isSwitched ? "s3" : "local"}}), mutationFn: async (values: DefaultStorageType) => {
onSuccess: () => { const result = await updateStorageSettingsAction({name: "system", data: values})
toast.success(`Settings updated successfully.`); const inner = result?.data;
if (inner?.success) {
toast.success(inner.actionSuccess?.message);
router.refresh(); router.refresh();
}, } else {
onError: () => { toast.error(inner?.actionError?.message);
toast.error(`An error occurred while updating settings information.`); }
}, }
}); });
const HandleSwitchStorage = async () => {
setIsSwitched(!isSwitched);
await updateMutation.mutateAsync();
};
const extractS3FormValues = (settings: Setting): S3FormType | undefined => {
if (!settings.s3EndPointUrl) return undefined;
return {
s3EndPointUrl: settings.s3EndPointUrl,
s3AccessKeyId: settings.s3AccessKeyId!,
s3SecretAccessKey: settings.s3SecretAccessKey!,
S3BucketName: settings.S3BucketName!,
};
};
return ( return (
<div className="flex flex-col h-full"> <div className="flex flex-col h-full">
<Alert className="mt-3"> <Alert className="mt-3">
<Info className="h-4 w-4"/> <Info className="h-4 w-4"/>
<AlertTitle>Informations</AlertTitle> <AlertTitle>Informations</AlertTitle>
<AlertDescription> <AlertDescription>
Actually you can only store you data in one place : s3 compatible or in local. For exemple you The default storage channel will be used by default to store your backups if no storage policy is
cannot choose to store images in one place configured at the database level. </AlertDescription>
and backups files in another.
</AlertDescription>
</Alert> </Alert>
<div className="flex flex-col h-full py-4 "> <div className="flex flex-col h-full py-4 ">
<div className="flex items-center justify-between space-x-2"> <Form
<div className="flex items-center space-x-2"> form={form}
<Label htmlFor="storage-mode">Storage Mode (Local/s3 compatible)</Label> onSubmit={async (values) => {
<Switch await mutation.mutateAsync(values);
checked={isSwitched}
onCheckedChange={async () => {
await HandleSwitchStorage();
}} }}
id="storage-mode" >
/> <div className="flex items-center gap-3">
</div> <FormField
<div> control={form.control}
<ButtonWithLoading name="storageChannelId"
size={"default"} render={({field}) => (
disabled={!isSwitched} <FormItem className="flex items-center justify-center">
isPending={mutation.isPending} <Select value={field.value} onValueChange={field.onChange}>
onClick={async () => { <SelectTrigger className="w-90 h-full mb-0">
await mutation.mutateAsync(); <SelectValue placeholder="Select a default storage channel"/>
}} </SelectTrigger>
icon={<ShieldCheck/>}>Test connexion</ButtonWithLoading> <SelectContent>
</div> {storageChannels.map((channel) => (
</div> <SelectItem key={channel.id} value={channel.id}>
{isSwitched && ( <div className="flex items-center gap-2">
<div className="mt-5"> {getChannelIcon(channel.provider)}
<StorageS3Form defaultValues={extractS3FormValues(props.settings)}/> <span className="font-medium">{channel.name}</span>
<span
className="text-[9px] uppercase bg-secondary px-1.5 py-0.5 rounded">
{channel.provider}
</span>
</div> </div>
</SelectItem>
))}
</SelectContent>
</Select>
</FormItem>
)} )}
/>
<ButtonWithLoading type="submit">
Confirm
</ButtonWithLoading>
</div>
</Form>
</div> </div>
</div> </div>
); );
@@ -0,0 +1,50 @@
"use server"
import {userAction} from "@/lib/safe-actions/actions";
import {db} from "@/db";
import * as drizzleDb from "@/db";
import {eq} from "drizzle-orm";
import {ServerActionResult} from "@/types/action-type";
import {Setting} from "@/db/schema/01_setting";
import {z} from "zod";
import {DefaultStorageSchema} from "@/components/wrappers/dashboard/admin/settings/storage/settings-storage.schema";
export const updateStorageSettingsAction = userAction
.schema(
z.object({
name: z.string(),
data: DefaultStorageSchema,
})
)
.action(async ({parsedInput}): Promise<ServerActionResult<Setting>> => {
const {name, data} = parsedInput;
try {
const [updatedSettings] = await db
.update(drizzleDb.schemas.setting)
.set({
defaultStorageChannelId: data.storageChannelId,
})
.where(eq(drizzleDb.schemas.setting.name, name))
.returning();
return {
success: true,
value: updatedSettings,
actionSuccess: {
message: "Settings successfully updated",
},
};
} catch (error) {
return {
success: false,
actionError: {
message: "Failed update settings.",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
},
};
}
});
@@ -0,0 +1,7 @@
import { z } from "zod";
export const DefaultStorageSchema = z.object({
storageChannelId: z.string(),
});
export type DefaultStorageType = z.infer<typeof DefaultStorageSchema>;
@@ -41,10 +41,6 @@ export const ChannelPoliciesModal = ({icon, kind, database, channels, organizati
const activeAlertPolicies = database.alertPolicies?.filter((policy) => channelsIds.includes(policy.notificationChannelId)); const activeAlertPolicies = database.alertPolicies?.filter((policy) => channelsIds.includes(policy.notificationChannelId));
const activeStoragePolicies = database.storagePolicies?.filter((policy) => channelsIds.includes(policy.storageChannelId)); const activeStoragePolicies = database.storagePolicies?.filter((policy) => channelsIds.includes(policy.storageChannelId));
console.log(channels);
console.log(database.storagePolicies);
console.log("activeAlertPolicies", activeAlertPolicies);
console.log("activeStoragePolicies", activeStoragePolicies);
const activePolicies = kind === "notification" ? activeAlertPolicies : activeStoragePolicies; const activePolicies = kind === "notification" ? activeAlertPolicies : activeStoragePolicies;
@@ -43,6 +43,7 @@ export const AdvancedCronSelect = ({
</Label> </Label>
{!isAdvanced ? ( {!isAdvanced ? (
<Select <Select
// @ts-ignore
id={id} id={id}
className="col-span-4" className="col-span-4"
value={defaultValue} value={defaultValue}
@@ -13,8 +13,8 @@ import {
TooltipProvider, TooltipProvider,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { updateMemberRoleAction } from "@/components/wrappers/dashboard/settings/update-member.action"; import {updateMemberRoleAction} from "@/components/wrappers/dashboard/organization/settings/update-member.action";
import { RoleSchemaMember } from "@/components/wrappers/dashboard/settings/member.schema"; import {RoleSchemaMember} from "@/components/wrappers/dashboard/organization/settings/member.schema";
export const organizationMemberColumns: ColumnDef<MemberWithUser>[] = [ export const organizationMemberColumns: ColumnDef<MemberWithUser>[] = [
{ {
@@ -1,6 +1,8 @@
import {DataTable} from "@/components/wrappers/common/table/data-table"; import {DataTable} from "@/components/wrappers/common/table/data-table";
import {MemberWithUser, OrganizationWithMembers} from "@/db/schema/03_organization"; import {MemberWithUser, OrganizationWithMembers} from "@/db/schema/03_organization";
import {organizationMemberColumns} from "@/components/wrappers/dashboard/settings/columns-organization-members"; import {
organizationMemberColumns
} from "@/components/wrappers/dashboard/organization/settings/columns-organization-members";
interface SettingsOrganizationMembersTableProps { interface SettingsOrganizationMembersTableProps {
organization: OrganizationWithMembers organization: OrganizationWithMembers
@@ -4,8 +4,8 @@ import {z} from "zod";
import {auth} from "@/lib/auth/auth"; import {auth} from "@/lib/auth/auth";
import {ServerActionResult} from "@/types/action-type"; import {ServerActionResult} from "@/types/action-type";
import {Member} from "better-auth/plugins"; import {Member} from "better-auth/plugins";
import {RoleSchemaMember} from "@/components/wrappers/dashboard/settings/member.schema";
import {headers} from "next/headers"; import {headers} from "next/headers";
import {RoleSchemaMember} from "@/components/wrappers/dashboard/organization/settings/member.schema";
export const updateMemberRoleAction = userAction.schema( export const updateMemberRoleAction = userAction.schema(
@@ -4,18 +4,18 @@ import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import {useRouter, useSearchParams} from "next/navigation"; import {useRouter, useSearchParams} from "next/navigation";
import {MemberWithUser, OrganizationWithMembers} from "@/db/schema/03_organization"; import {MemberWithUser, OrganizationWithMembers} from "@/db/schema/03_organization";
import {
SettingsOrganizationMembersTable
} from "@/components/wrappers/dashboard/settings/settings-organization-members-table";
import {
OrganizationNotifiersTab
} from "@/components/wrappers/dashboard/organization/tabs/organization-notifiers-tab/organization-notifiers-tab";
import {NotificationChannel} from "@/db/schema/09_notification-channel"; import {NotificationChannel} from "@/db/schema/09_notification-channel";
import {useOrganizationPermissions} from "@/hooks/use-organization-permissions"; import {useOrganizationPermissions} from "@/hooks/use-organization-permissions";
import {StorageChannel} from "@/db/schema/12_storage-channel";
import {
SettingsOrganizationMembersTable
} from "@/components/wrappers/dashboard/organization/settings/settings-organization-members-table";
import {
OrganizationNotifiersTab
} from "@/components/wrappers/dashboard/organization/tabs/organization-channels-tab/organization-notifiers-tab";
import { import {
OrganizationStoragesTab OrganizationStoragesTab
} from "@/components/wrappers/dashboard/organization/tabs/organization-notifiers-tab/organization-storages-tab"; } from "@/components/wrappers/dashboard/organization/tabs/organization-channels-tab/organization-storages-tab";
import {StorageChannel} from "@/db/schema/12_storage-channel";
export type OrganizationTabsProps = { export type OrganizationTabsProps = {
organization: OrganizationWithMembers; organization: OrganizationWithMembers;
+12 -8
View File
@@ -27,18 +27,25 @@ export async function storeBackupFiles(
? [{ ? [{
id: null, id: null,
storageChannelId: settings.storageChannel.id, storageChannelId: settings.storageChannel.id,
enabled: true enabled: settings.storageChannel.enabled,
}] }]
: []; : [];
console.log(database.storagePolicies); const enabledPolicies = database.storagePolicies?.filter(p => p.enabled) ?? [];
const policies = (database.storagePolicies?.filter(p => p.enabled) || defaultPolicy); const policies = enabledPolicies.length > 0
? enabledPolicies
console.log("Policies", policies); : defaultPolicy;
console.debug("Policies", policies);
if (!policies.length) { if (!policies.length) {
await db
.update(drizzleDb.schemas.backup)
.set(withUpdatedAt({
status: "failed",
}))
.where(eq(drizzleDb.schemas.backup.id, backup.id));
return []; return [];
} }
@@ -65,7 +72,6 @@ export async function storeBackupFiles(
data: {path, file}, data: {path, file},
}; };
// const result = await dispatchStorage(input, policy.id);
let result: StorageResult; let result: StorageResult;
try { try {
@@ -93,8 +99,6 @@ export async function storeBackupFiles(
}) })
); );
console.log(results);
const backupStatus = results.some(r => r.success) ? "success" : "failed"; const backupStatus = results.some(r => r.success) ? "success" : "failed";
await db await db