Merge remote-tracking branch 'origin/main'

# Conflicts:
#	package.json
This commit is contained in:
killian-larcher
2024-11-17 19:14:30 +01:00
24 changed files with 1701 additions and 66 deletions
+1 -3
View File
@@ -14,13 +14,11 @@ export default async function Layout({children}: { children: React.ReactNode })
email: user?.email email: user?.email
} }
}) })
if(userInfo && userInfo.authMethod){
if(userInfo){
redirect('/dashboard') redirect('/dashboard')
} }
} }
return ( return (
<LayoutAdmin> <LayoutAdmin>
<div <div
+12 -10
View File
@@ -1,15 +1,17 @@
import {PageParams} from "@/types/next"; import {PageParams} from "@/types/next";
import {redirect} from "next/navigation";
export default async function RoutePage(props: PageParams<{}>) { export default async function RoutePage(props: PageParams<{}>) {
return (
<div className="flex flex-1 flex-col gap-4 p-4"> // return (
<div className="grid auto-rows-min gap-4 md:grid-cols-3"> // <div className="flex flex-1 flex-col gap-4 p-4">
<div className="aspect-video rounded-xl bg-muted/50"/> // <div className="grid auto-rows-min gap-4 md:grid-cols-3">
<div className="aspect-video rounded-xl bg-muted/50"/> // <div className="aspect-video rounded-xl bg-muted/50"/>
<div className="aspect-video rounded-xl bg-muted/50"/> // <div className="aspect-video rounded-xl bg-muted/50"/>
</div> // <div className="aspect-video rounded-xl bg-muted/50"/>
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min"/> // </div>
</div> // <div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min"/>
) // </div>
// )
} }
+8 -6
View File
@@ -1,12 +1,14 @@
import {PageParams} from "@/types/next"; import {PageParams} from "@/types/next";
import {Button} from "@/components/ui/button"; import {Button} from "@/components/ui/button";
import {redirect} from "next/navigation";
export default async function RoutePage(props: PageParams<{}>) { export default async function RoutePage(props: PageParams<{}>) {
redirect("/dashboard/agents")
return( // return(
<div> // <div>
<h1>Ok</h1> // <h1>Ok</h1>
<Button>Button</Button> // <Button>Button</Button>
</div> // </div>
) // )
} }
@@ -2,15 +2,12 @@ import {PageParams} from "@/types/next";
import {Page, PageContent, PageDescription, PageHeader, PageTitle} from "@/features/layout/page"; import {Page, PageContent, PageDescription, PageHeader, PageTitle} from "@/features/layout/page";
import {prisma} from "@/prisma"; import {prisma} from "@/prisma";
import {requiredCurrentUser} from "@/auth/current-user"; import {requiredCurrentUser} from "@/auth/current-user";
import {DataTableWithPagination} from "@/components/wrappers/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/Dashboard/Settings/SettingsTabs/columns-users";
import {SettingsTabs} from "@/components/wrappers/Dashboard/Settings/SettingsTabs/SettingsTabs"; import {SettingsTabs} from "@/components/wrappers/Dashboard/Settings/SettingsTabs/SettingsTabs";
export default async function RoutePage(props: PageParams<{}>) { export default async function RoutePage(props: PageParams<{}>) {
const user = await requiredCurrentUser() const user = await requiredCurrentUser()
const users = await prisma.user.findMany({ const users = await prisma.user.findMany({
where:{ where:{
id: { id: {
+1
View File
@@ -18,6 +18,7 @@ services:
volumes: volumes:
- .:/app - .:/app
- /app/node_modules - /app/node_modules
container_name: portabase-app
db: db:
image: postgres:16-alpine image: postgres:16-alpine
+13
View File
@@ -0,0 +1,13 @@
import {
Button as ReactEmailButton
} from "@react-email/components";
import {ComponentPropsWithoutRef} from "react";
export const Button = (props: ComponentPropsWithoutRef<typeof ReactEmailButton>) => {
return (
<ReactEmailButton
className="block w-52 rounded bg-blue-600 py-3.5 text-center text-sm font-normal text-white no-underline "
{...props}
/>
)
}
+50
View File
@@ -0,0 +1,50 @@
import {
Body,
Button,
Container,
Head,
Html,
Img,
Link,
Preview,
Section,
Tailwind,
Text,
} from "@react-email/components";
import * as React from "react";
import { PropsWithChildren } from "react";
import {getServerUrl} from "@/utils/get-server-url";
const baseUrl = getServerUrl();
export const EmailLayout = ({ children, preview }: PropsWithChildren<{ preview?: string }>) => {
return (
<Tailwind>
<Html>
<Head />
{preview ? <Preview>{preview}</Preview> : <Preview>Please check your mails</Preview>}
<Body className="bg-gray-100 py-4"
style={{fontFamily: "Arial, sans-serif"}}>
<Container className="bg-white border border-gray-200 p-12">
<Img
src={`${baseUrl}/logo-title-black.png`}
width="400"
height="auto"
alt="Logo"
/>
<Section>
{children}
</Section>
</Container>
</Body>
</Html>
</Tailwind>
);
};
export default EmailLayout;
+28
View File
@@ -0,0 +1,28 @@
import {
Text,
} from "@react-email/components";
import * as React from "react";
import EmailLayout from "./EmailLayout";
export const TestEmailSettings = () => {
return (
<EmailLayout
preview="Test Email"
>
<Text
className="text-base font-light leading-8 text-green-800 "
>
Hi, this is a test email for settings and alerts !
</Text>
<Text
className="text-base font-light leading-8 text-green-800 ">
Soluce Technologies | Portabase
</Text>
</EmailLayout>
);
};
export default TestEmailSettings;
+13
View File
@@ -0,0 +1,13 @@
import {
Text as ReactEmailText
} from "@react-email/components";
import {ComponentPropsWithoutRef} from "react";
export const Text = (props: ComponentPropsWithoutRef<typeof ReactEmailText>) => {
return (
<ReactEmailText
className="text-base font-light leading-8 text-green-800 "
{...props}
/>
)
}
+1
View File
@@ -31,6 +31,7 @@ function checkRouteExists(pathname) {
// /^\/api\/auth\/\w+$/, // Dynamic route with a number as a parameter (e.g., /api/dynamic/123) // /^\/api\/auth\/\w+$/, // Dynamic route with a number as a parameter (e.g., /api/dynamic/123)
// /^\/api\/agent\/healthcheck\/\w+$/, // Dynamic route with an alphanumeric parameter (e.g., /api/user/username) // /^\/api\/agent\/healthcheck\/\w+$/, // Dynamic route with an alphanumeric parameter (e.g., /api/user/username)
/^\/api\/agent\/[^/]+\/status\/?$/, // Dynamic route for /api/agent/[id]/status /^\/api\/agent\/[^/]+\/status\/?$/, // Dynamic route for /api/agent/[id]/status
/^\/api\/logs\$/,
]; ];
return routePatterns.some(pattern => pattern.test(pathname)); return routePatterns.some(pattern => pattern.test(pathname));
} }
+4
View File
@@ -40,6 +40,7 @@
"@radix-ui/react-toggle": "^1.1.0", "@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.3", "@radix-ui/react-tooltip": "^1.1.3",
"@react-email/components": "^0.0.28",
"@t3-oss/env-nextjs": "^0.11.1", "@t3-oss/env-nextjs": "^0.11.1",
"@tanstack/react-query": "^5.59.18", "@tanstack/react-query": "^5.59.18",
"@tanstack/react-table": "^8.20.5", "@tanstack/react-table": "^8.20.5",
@@ -49,6 +50,7 @@
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"dockerode": "^4.0.2",
"embla-carousel-react": "^8.3.1", "embla-carousel-react": "^8.3.1",
"input-otp": "^1.4.0", "input-otp": "^1.4.0",
"lucide-react": "^0.454.0", "lucide-react": "^0.454.0",
@@ -58,9 +60,11 @@
"next-intl": "^3.24.0", "next-intl": "^3.24.0",
"next-safe-action": "^7.4.3", "next-safe-action": "^7.4.3",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"nodemailer": "^6.9.16",
"react": "19.0.0-rc-66855b96-20241106", "react": "19.0.0-rc-66855b96-20241106",
"react-day-picker": "8.10.1", "react-day-picker": "8.10.1",
"react-dom": "19.0.0-rc-66855b96-20241106", "react-dom": "19.0.0-rc-66855b96-20241106",
"react-email": "^3.0.2",
"react-hook-form": "^7.53.1", "react-hook-form": "^7.53.1",
"react-resizable-panels": "^2.1.6", "react-resizable-panels": "^2.1.6",
"react-twc": "^1.4.2", "react-twc": "^1.4.2",
@@ -0,0 +1,40 @@
"use client"
import {Button} from "@/components/ui/button";
import {useState} from "react";
import {Loader2} from "lucide-react";
export type VariantButton = {
secondary: string
default: string
outline: string
ghost: string
link: string
destructive: string
}
export type ButtonWithConfirmProps = {
icon?: any,
text: string,
variant?: keyof VariantButton ,
className?: string,
onClick: () => void,
isPending? : boolean
};
export const ButtonWithLoading = (props: ButtonWithConfirmProps) => {
return(
<Button
onClick={() => {
props.onClick()
}}
variant={props.variant ? props.variant : "default"}
className={props.className}
>
{props.isPending && <Loader2 className="animate-spin mr-4" size={16}/>}
{props.text}
<>
{props.icon ? props.icon : null}
</>
</Button>
)
}
@@ -14,26 +14,50 @@ import {
EmailFormSchema, EmailFormSchema,
EmailFormType EmailFormType
} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.schema"; } from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.schema";
import Link from "next/link";
import {PasswordInput} from "@/components/wrappers/Auth/PaswordInput/password-input";
import {
updateEmailSettingsAction
} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.action";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
export type EmailFormProps = { export type EmailFormProps = {
defaultValues?: EmailFormType; defaultValues?: EmailFormType;
} }
export const EmailForm = (props: EmailFormProps) => { export const EmailForm = (props: EmailFormProps) => {
const isCreate = !Boolean(props.defaultValues)
const form = useZodForm({ const form = useZodForm({
schema: EmailFormSchema, schema: EmailFormSchema,
defaultValues: props.defaultValues,
}); });
const router = useRouter();
const mutation = useMutation({ const mutation = useMutation({
mutationFn: async (values: EmailFormType) => { mutationFn: async (values: EmailFormType) => {
const updateEmailSettings = await updateEmailSettingsAction({name: "system", data: values})
const data = updateEmailSettings?.data?.data
if (updateEmailSettings?.serverError || !data) {
console.log(updateEmailSettings?.serverError);
toast.error(updateEmailSettings?.serverError);
return;
}
toast.success(`Success updating email informations`);
router.refresh()
} }
}) })
return ( return (
<TooltipProvider> <TooltipProvider>
<Card> <Card>
<CardContent> <CardContent>
<Form form={form} <Form form={form}
className="flex flex-col gap-4 mt-3" className="flex flex-col gap-4 mt-3"
onSubmit={async (values) => { onSubmit={async (values) => {
await mutation.mutateAsync(values); await mutation.mutateAsync(values);
@@ -42,6 +66,8 @@ export const EmailForm = (props: EmailFormProps) => {
<FormField <FormField
control={form.control} control={form.control}
name="smtpFrom" name="smtpFrom"
defaultValue=""
render={({field}) => ( render={({field}) => (
<FormItem> <FormItem>
<FormLabel>From Email *</FormLabel> <FormLabel>From Email *</FormLabel>
@@ -54,10 +80,81 @@ export const EmailForm = (props: EmailFormProps) => {
</FormItem> </FormItem>
)} )}
/> />
<FormField
control={form.control}
name="smtpHost"
defaultValue=""
render={({field}) => (
<FormItem>
<FormLabel>Server Host *</FormLabel>
<FormControl>
<Input
placeholder={"ssl0.ovh.net"} {...field} />
</FormControl>
<FormDescription>{"Your email server host"}</FormDescription>
<FormMessage/>
</FormItem>
)}
/>
<FormField
control={form.control}
name="smtpPort"
defaultValue=""
render={({field}) => (
<FormItem>
<FormLabel>Server Port *</FormLabel>
<FormControl>
<Input
placeholder={"465"} {...field} />
</FormControl>
<FormDescription>{"Your email server port (send)"}</FormDescription>
<FormMessage/>
</FormItem>
)}
/>
<FormField
control={form.control}
name="smtpPassword"
defaultValue=""
render={({field}) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<PasswordInput placeholder="Password" {...field}/>
</FormControl>
<FormDescription>{"Your email server password"}</FormDescription>
<FormMessage/>
</FormItem>
)}
/>
<FormField
control={form.control}
name="smtpUser"
defaultValue=""
render={({field}) => (
<FormItem>
<FormLabel>User Email *</FormLabel>
<FormControl>
<Input
placeholder={"exemple@portabase.com"} {...field} />
</FormControl>
<FormDescription>{"The email server user"}</FormDescription>
<FormMessage/>
</FormItem>
)}
/>
<div className="flex justify-end gap-4">
<Button>
Save
</Button>
</div>
<Button>
Save
</Button>
</Form> </Form>
</CardContent> </CardContent>
</Card> </Card>
@@ -0,0 +1,31 @@
"use server"
import {userAction} from "@/safe-actions";
import {z} from "zod";
import {prisma} from "@/prisma";
import {EmailFormSchema} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.schema";
export const updateEmailSettingsAction = userAction
.schema(
z.object({
name: z.string(),
data: EmailFormSchema,
}
)
)
.action(async ({parsedInput, ctx}) => {
console.log("parsedInput", parsedInput.data)
const updatedSettings = await prisma.settings.update({
where: {
name: parsedInput.name,
},
data: parsedInput.data,
})
return {
data: updatedSettings,
}
})
@@ -1,12 +1,60 @@
import {EmailForm} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/EmailForm"; import {EmailForm} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/EmailForm";
import {Settings} from "@prisma/client";
import {Send} from "lucide-react";
import {ButtonWithLoading} from "@/components/wrappers/Button/ButtonWithLoading/ButtonWithLoading";
import {useMutation} from "@tanstack/react-query";
import {sendEmail} from "@/utils/email-helper";
import TestEmailSettings from "../../../../../../emails/TestEmailSettings";
import {render} from "@react-email/render";
import {toast} from "sonner";
export type SettingsEmailTabProps = {}
export const SettingsEmailTab = (props:SettingsEmailTabProps) => { export type SettingsEmailTabProps = {
return( settings: Settings
<div> }
<EmailForm/>
export const SettingsEmailTab = (props: SettingsEmailTabProps) => {
const mutation = useMutation({
mutationFn: async () => {
const email = await sendEmail({
to: props.settings.smtpUser,
subject: "Portabase email test !",
html: await render(TestEmailSettings(), {})
});
if(email.response){
toast.success("Test Email Successfully sent !");
}
}
})
const handleSendMailTest = async () => {
console.log("Sending email test...");
await mutation.mutateAsync()
}
return (
<div className="flex flex-col h-full py-4">
<div className="flex gap-4 h-fit justify-between">
<h1>Settings for Portabase storage</h1>
{props.settings.smtpFrom && (
<ButtonWithLoading
isPending={mutation.isPending}
onClick={async () => {
await handleSendMailTest()
}}
icon={<Send/>}
text="Send email test"
/>
)}
</div>
<div className="mt-5">
<EmailForm defaultValues={props.settings.smtpFrom ? props.settings : null}/>
</div>
</div> </div>
) )
} }
@@ -40,7 +40,7 @@ export const SettingsStorageTab = (props: SettingsStorageTabProps) => {
</div> </div>
{isSwitched && ( {isSwitched && (
<div className="mt-5"> <div className="mt-5">
<StorageS3Form/> <StorageS3Form defaultValues={props.settings.s3EndPointUrl ? props.settings : null}/>
</div> </div>
)} )}
</div> </div>
@@ -22,6 +22,7 @@ export type S3FormProps = {
export const StorageS3Form = (props: S3FormProps) => { export const StorageS3Form = (props: S3FormProps) => {
const form = useZodForm({ const form = useZodForm({
schema: S3FormSchema, schema: S3FormSchema,
defaultValues: props.defaultValues,
}); });
const mutation = useMutation({ const mutation = useMutation({
mutationFn: async (values: S3FormType) => { mutationFn: async (values: S3FormType) => {
@@ -99,9 +100,12 @@ export const StorageS3Form = (props: S3FormProps) => {
</FormItem> </FormItem>
)} )}
/> />
<Button> <div className="flex justify-end gap-4">
Save
</Button> <Button>
Save
</Button>
</div>
</Form> </Form>
</CardContent> </CardContent>
</Card> </Card>
@@ -37,7 +37,7 @@ export const SettingsTabs = (props: SettingsTabsProps) => {
<DataTableWithPagination columns={usersColumns} data={props.users}/> <DataTableWithPagination columns={usersColumns} data={props.users}/>
</TabsContent> </TabsContent>
<TabsContent value="email"> <TabsContent value="email">
<SettingsEmailTab/> <SettingsEmailTab settings={props.settings}/>
</TabsContent> </TabsContent>
<TabsContent value="storage"> <TabsContent value="storage">
<SettingsStorageTab settings={props.settings}/> <SettingsStorageTab settings={props.settings}/>
@@ -15,11 +15,11 @@ export const SidebarMenuCustom = (props: SidebarMenuCustomProps) => {
const pathname = usePathname(); const pathname = usePathname();
// Menu items. // Menu items.
const items = [ const items = [
{ // {
title: "Home", // title: "Home",
url: "/", // url: "/",
icon: Home, // icon: Home,
}, // },
{ {
title: "Agents", title: "Agents",
url: "agents", url: "agents",
+15 -5
View File
@@ -9,21 +9,26 @@ export const env = createEnv({
server: { server: {
NODE_ENV: z.enum(['development', 'production']), NODE_ENV: z.enum(['development', 'production']),
DATABASE_URL: z.string().url(), DATABASE_URL: z.string().url(),
SMTP_PASSWORD: z.string(),
SMTP_FROM: z.string(),
SMTP_HOST: z.string(),
SMTP_PORT: z.string(),
SMTP_USER: z.string(),
NEXT_PUBLIC_SECRET: z.string(), NEXT_PUBLIC_SECRET: z.string(),
NEXTAUTH_URL: z.string(), NEXTAUTH_URL: z.string(),
SMTP_PASSWORD: z.string().optional(),
SMTP_FROM: z.string().optional(),
SMTP_HOST: z.string().optional(),
SMTP_PORT: z.string().optional(),
SMTP_USER: z.string().optional(),
AUTH_GOOGLE_ID: z.string().optional(), AUTH_GOOGLE_ID: z.string().optional(),
AUTH_GOOGLE_SECRET: z.string().optional(), AUTH_GOOGLE_SECRET: z.string().optional(),
S3_ENDPOINT: z.string().optional(), S3_ENDPOINT: z.string().optional(),
S3_ACCESS_KEY: z.string().optional(), S3_ACCESS_KEY: z.string().optional(),
S3_SECRET_KEY: z.string().optional(), S3_SECRET_KEY: z.string().optional(),
S3_BUCKET_NAME: z.string().optional(), S3_BUCKET_NAME: z.string().optional(),
S3_PORT: z.string().optional(), S3_PORT: z.string().optional(),
S3_USE_SSL: z.string().optional(), S3_USE_SSL: z.string().optional(),
STORAGE_TYPE: z.string().optional(),
}, },
/* /*
* Environment variables available on the client (and server). * Environment variables available on the client (and server).
@@ -45,18 +50,23 @@ export const env = createEnv({
NODE_ENV: process.env.NODE_ENV, NODE_ENV: process.env.NODE_ENV,
NEXT_PUBLIC_DOMAIN_NAME: process.env.NEXT_PUBLIC_DOMAIN_NAME, NEXT_PUBLIC_DOMAIN_NAME: process.env.NEXT_PUBLIC_DOMAIN_NAME,
DATABASE_URL: process.env.DATABASE_URL, DATABASE_URL: process.env.DATABASE_URL,
SMTP_PASSWORD: process.env.SMTP_PASSWORD, SMTP_PASSWORD: process.env.SMTP_PASSWORD,
SMTP_FROM: process.env.SMTP_FROM, SMTP_FROM: process.env.SMTP_FROM,
SMTP_HOST: process.env.SMTP_HOST, SMTP_HOST: process.env.SMTP_HOST,
SMTP_PORT: process.env.SMTP_PORT, SMTP_PORT: process.env.SMTP_PORT,
SMTP_USER: process.env.SMTP_USER, SMTP_USER: process.env.SMTP_USER,
AUTH_GOOGLE_ID: process.env.AUTH_GOOGLE_ID, AUTH_GOOGLE_ID: process.env.AUTH_GOOGLE_ID,
AUTH_GOOGLE_SECRET: process.env.AUTH_GOOGLE_SECRET, AUTH_GOOGLE_SECRET: process.env.AUTH_GOOGLE_SECRET,
S3_ENDPOINT: process.env.S3_ENDPOINT, S3_ENDPOINT: process.env.S3_ENDPOINT,
S3_ACCESS_KEY: process.env.S3_ACCESS_KEY, S3_ACCESS_KEY: process.env.S3_ACCESS_KEY,
S3_SECRET_KEY: process.env.S3_SECRET_KEY, S3_SECRET_KEY: process.env.S3_SECRET_KEY,
S3_BUCKET_NAME: process.env.S3_BUCKET_NAME, S3_BUCKET_NAME: process.env.S3_BUCKET_NAME,
S3_PORT: process.env.S3_PORT, S3_PORT: process.env.S3_PORT,
S3_USE_SSL: process.env.S3_USE_SSL, S3_USE_SSL: process.env.S3_USE_SSL,
STORAGE_TYPE: process.env.STORAGE_TYPE,
}, },
}); });
+50
View File
@@ -0,0 +1,50 @@
"use server"
import {prisma} from "@/prisma";
import nodemailer from "nodemailer";
type Payload = {
to: string;
subject: string;
html: any
};
export const sendEmail = async (data: Payload) => {
const settings = await prisma.settings.findUnique({
where: {
name: "system"
}
})
const smtpSettings = {
host: settings.smtpHost,
port: parseInt(settings.smtpPort),
auth: {
user: settings.smtpUser,
pass: settings.smtpPassword,
},
};
// Create a transporter object using nodemailer
const transporter = nodemailer.createTransport({
...smtpSettings
});
// Render the React component to HTML
// const emailHtml = await render(HelloEmail(), {});
// Set up email options
const mailOptions = {
...data
};
return await transporter.sendMail({
from: settings.smtpFrom,
...mailOptions,
});
};
+8
View File
@@ -0,0 +1,8 @@
import {env} from "@/env.mjs";
export const getServerUrl = () => {
if (typeof window !== 'undefined') {
return window.location.origin;
}
return `https://${env.NEXT_PUBLIC_DOMAIN_NAME}`;
}
+29 -1
View File
@@ -1,4 +1,6 @@
import {prisma} from "@/prisma"; import {prisma} from "@/prisma";
import {Settings} from "@prisma/client";
import {env} from "@/env.mjs";
export function init() { export function init() {
@@ -15,19 +17,45 @@ export function init() {
async function createSettingsIfNotExist() { async function createSettingsIfNotExist() {
const configSettings = {
name: "system",
smtpPassword: env.SMTP_PASSWORD ?? null,
smtpFrom: env.SMTP_FROM ?? null,
smtpHost: env.SMTP_HOST ?? null,
smtpPort: env.SMTP_PORT ?? null,
smtpUser: env.SMTP_USER ?? null,
s3EndPointUrl: env.S3_ENDPOINT ?? null,
s3AccessKeyId: env.S3_ACCESS_KEY ?? null,
s3SecretAccessKey: env.S3_SECRET_KEY ?? null,
S3BucketName: env.S3_BUCKET_NAME ?? null,
}
const settings = await prisma.settings.findUnique({ const settings = await prisma.settings.findUnique({
where: { where: {
name: "system", name: "system",
} }
}) })
if(!settings){ if(!settings){
console.log("====Init Setting====") console.log("====Init Setting : Create ====")
await prisma.settings.create({ await prisma.settings.create({
data: { data: {
name: "system", name: "system",
} }
}) })
}else{
console.log("====Init Setting : Update ====")
await prisma.settings.update({
where: {
name: "system",
},
data: {
...configSettings,
}
})
} }
} }
function consoleAscii(){ function consoleAscii(){
+1229 -19
View File
File diff suppressed because it is too large Load Diff