diff --git a/Dockerfile b/Dockerfile index a5be0f1a..98d3cfc5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app COPY package.json ./ -RUN npm install --legacy-peer-deps +RUN npm install COPY . . @@ -20,7 +20,7 @@ WORKDIR /app COPY package.json ./ -RUN npm install --omit=dev --legacy-peer-deps +RUN npm install --omit=dev COPY --from=builder /app/.next ./.next COPY --from=builder /app/dist ./dist diff --git a/server/apiServer.ts b/server/apiServer.ts index d4fe98f8..1de0329b 100644 --- a/server/apiServer.ts +++ b/server/apiServer.ts @@ -1,4 +1,4 @@ -import express, { Request, Response } from "express"; +import express from "express"; import cors from "cors"; import cookieParser from "cookie-parser"; import config from "@server/config"; diff --git a/server/config.ts b/server/config.ts index ea8de136..fe1f863e 100644 --- a/server/config.ts +++ b/server/config.ts @@ -14,7 +14,10 @@ const portSchema = z.number().positive().gt(0).lte(65535); const environmentSchema = z.object({ app: z.object({ - base_url: z.string().url().transform((url) => url.toLowerCase()), + base_url: z + .string() + .url() + .transform((url) => url.toLowerCase()), log_level: z.enum(["debug", "info", "warn", "error"]), save_logs: z.boolean() }), @@ -76,97 +79,102 @@ const environmentSchema = z.object({ .optional() }); -const loadConfig = (configPath: string) => { - try { - const yamlContent = fs.readFileSync(configPath, "utf8"); - const config = yaml.load(yamlContent); - return config; - } catch (error) { - if (error instanceof Error) { - throw new Error( - `Error loading configuration file: ${error.message}` - ); - } - throw error; - } -}; - -const configFilePath1 = path.join(APP_PATH, "config.yml"); -const configFilePath2 = path.join(APP_PATH, "config.yaml"); - -let environment: any; -if (fs.existsSync(configFilePath1)) { - environment = loadConfig(configFilePath1); -} else if (fs.existsSync(configFilePath2)) { - environment = loadConfig(configFilePath2); -} -if (!environment) { - const exampleConfigPath = path.join(__DIRNAME, "config.example.yml"); - if (fs.existsSync(exampleConfigPath)) { +export function getConfig() { + const loadConfig = (configPath: string) => { try { - const exampleConfigContent = fs.readFileSync( - exampleConfigPath, - "utf8" - ); - fs.writeFileSync(configFilePath1, exampleConfigContent, "utf8"); - environment = loadConfig(configFilePath1); + const yamlContent = fs.readFileSync(configPath, "utf8"); + const config = yaml.load(yamlContent); + return config; } catch (error) { if (error instanceof Error) { throw new Error( - `Error creating configuration file from example: ${error.message}` + `Error loading configuration file: ${error.message}` ); } throw error; } - } else { - throw new Error( - "No configuration file found and no example configuration available" - ); + }; + + const configFilePath1 = path.join(APP_PATH, "config.yml"); + const configFilePath2 = path.join(APP_PATH, "config.yaml"); + + let environment: any; + if (fs.existsSync(configFilePath1)) { + environment = loadConfig(configFilePath1); + } else if (fs.existsSync(configFilePath2)) { + environment = loadConfig(configFilePath2); } -} - -if (!environment) { - throw new Error("No configuration file found"); -} - -const parsedConfig = environmentSchema.safeParse(environment); - -if (!parsedConfig.success) { - const errors = fromError(parsedConfig.error); - throw new Error(`Invalid configuration file: ${errors}`); -} - -const packageJsonPath = path.join(__DIRNAME, "..", "package.json"); -let packageJson: any; -if (fs.existsSync && fs.existsSync(packageJsonPath)) { - const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8"); - packageJson = JSON.parse(packageJsonContent); - - if (packageJson.version) { - process.env.APP_VERSION = packageJson.version; + if (!environment) { + const exampleConfigPath = path.join(__DIRNAME, "config.example.yml"); + if (fs.existsSync(exampleConfigPath)) { + try { + const exampleConfigContent = fs.readFileSync( + exampleConfigPath, + "utf8" + ); + fs.writeFileSync(configFilePath1, exampleConfigContent, "utf8"); + environment = loadConfig(configFilePath1); + } catch (error) { + if (error instanceof Error) { + throw new Error( + `Error creating configuration file from example: ${error.message}` + ); + } + throw error; + } + } else { + throw new Error( + "No configuration file found and no example configuration available" + ); + } } + + if (!environment) { + throw new Error("No configuration file found"); + } + + const parsedConfig = environmentSchema.safeParse(environment); + + if (!parsedConfig.success) { + const errors = fromError(parsedConfig.error); + throw new Error(`Invalid configuration file: ${errors}`); + } + + const packageJsonPath = path.join(__DIRNAME, "..", "package.json"); + let packageJson: any; + if (fs.existsSync && fs.existsSync(packageJsonPath)) { + const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8"); + packageJson = JSON.parse(packageJsonContent); + + if (packageJson.version) { + process.env.APP_VERSION = packageJson.version; + } + } + + process.env.NEXT_PORT = parsedConfig.data.server.next_port.toString(); + process.env.SERVER_EXTERNAL_PORT = + parsedConfig.data.server.external_port.toString(); + process.env.SERVER_INTERNAL_PORT = + parsedConfig.data.server.internal_port.toString(); + process.env.FLAGS_EMAIL_VERIFICATION_REQUIRED = parsedConfig.data.flags + ?.require_email_verification + ? "true" + : "false"; + process.env.SESSION_COOKIE_NAME = + parsedConfig.data.server.session_cookie_name; + process.env.RESOURCE_SESSION_COOKIE_NAME = + parsedConfig.data.server.resource_session_cookie_name; + process.env.EMAIL_ENABLED = parsedConfig.data.email ? "true" : "false"; + process.env.DISABLE_SIGNUP_WITHOUT_INVITE = parsedConfig.data.flags + ?.disable_signup_without_invite + ? "true" + : "false"; + process.env.DISABLE_USER_CREATE_ORG = parsedConfig.data.flags + ?.disable_user_create_org + ? "true" + : "false"; + + return parsedConfig.data; } -process.env.NEXT_PORT = parsedConfig.data.server.next_port.toString(); -process.env.SERVER_EXTERNAL_PORT = - parsedConfig.data.server.external_port.toString(); -process.env.SERVER_INTERNAL_PORT = - parsedConfig.data.server.internal_port.toString(); -process.env.FLAGS_EMAIL_VERIFICATION_REQUIRED = parsedConfig.data.flags - ?.require_email_verification - ? "true" - : "false"; -process.env.SESSION_COOKIE_NAME = parsedConfig.data.server.session_cookie_name; -process.env.RESOURCE_SESSION_COOKIE_NAME = - parsedConfig.data.server.resource_session_cookie_name; -process.env.EMAIL_ENABLED = parsedConfig.data.email ? "true" : "false"; -process.env.DISABLE_SIGNUP_WITHOUT_INVITE = parsedConfig.data.flags - ?.disable_signup_without_invite - ? "true" - : "false"; -process.env.DISABLE_USER_CREATE_ORG = parsedConfig.data.flags - ?.disable_user_create_org - ? "true" - : "false"; - -export default parsedConfig.data; +export default getConfig(); diff --git a/server/emails/templates/SendInviteLink.tsx b/server/emails/templates/SendInviteLink.tsx index d7a4228d..85ec5ec0 100644 --- a/server/emails/templates/SendInviteLink.tsx +++ b/server/emails/templates/SendInviteLink.tsx @@ -68,7 +68,7 @@ export const SendInviteLink = ({
diff --git a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx index 41041039..a3310a46 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/authentication/page.tsx @@ -421,6 +421,7 @@ export default function ResourceAuthenticationPage() { Roles + {/* @ts-ignore */} @@ -476,6 +477,7 @@ export default function ResourceAuthenticationPage() { Users + {/* @ts-ignore */} @@ -649,6 +651,7 @@ export default function ResourceAuthenticationPage() { Whitelisted Emails + {/* @ts-ignore */} diff --git a/src/app/[orgId]/settings/sites/[niceId]/layout.tsx b/src/app/[orgId]/settings/sites/[niceId]/layout.tsx index a3a0ea0c..fbee999b 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/layout.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/layout.tsx @@ -50,7 +50,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { - Sites + Sites diff --git a/src/app/globals.css b/src/app/globals.css index 82795d64..bd45057d 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -6,11 +6,11 @@ @layer base { :root { --background: 0 0% 100%; - --foreground: 20 5.0% 10.0%; + --foreground: 20 0.0% 10.0%; --card: 0 0% 100%; - --card-foreground: 20 5.0% 10.0%; + --card-foreground: 20 0.0% 10.0%; --popover: 0 0% 100%; - --popover-foreground: 20 5.0% 10.0%; + --popover-foreground: 20 0.0% 10.0%; --primary: 24.6 95% 53.1%; --primary-foreground: 60 9.1% 97.8%; --secondary: 60 4.8% 95.9%; @@ -33,11 +33,11 @@ } .dark { - --background: 20 5.0% 10.0%; + --background: 20 0.0% 10.0%; --foreground: 60 9.1% 97.8%; - --card: 20 5.0% 10.0%; + --card: 20 0.0% 10.0%; --card-foreground: 60 9.1% 97.8%; - --popover: 20 5.0% 10.0%; + --popover: 20 0.0% 10.0%; --popover-foreground: 60 9.1% 97.8%; --primary: 20.5 90.2% 48.2%; --primary-foreground: 60 9.1% 97.8%; diff --git a/src/components/Credenza.tsx b/src/components/Credenza.tsx index 49bfa6ac..f638b6f9 100644 --- a/src/components/Credenza.tsx +++ b/src/components/Credenza.tsx @@ -24,6 +24,15 @@ import { DrawerTitle, DrawerTrigger } from "@/components/ui/drawer"; +import { + Sheet, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, + SheetTrigger +} from "./ui/sheet"; interface BaseProps { children: React.ReactNode; @@ -44,7 +53,7 @@ const desktop = "(min-width: 768px)"; const Credenza = ({ children, ...props }: RootCredenzaProps) => { const isDesktop = useMediaQuery(desktop); // const isDesktop = true; - const Credenza = isDesktop ? Dialog : Drawer; + const Credenza = isDesktop ? Dialog : Sheet; return {children}; }; @@ -53,7 +62,7 @@ const CredenzaTrigger = ({ className, children, ...props }: CredenzaProps) => { const isDesktop = useMediaQuery(desktop); // const isDesktop = true; - const CredenzaTrigger = isDesktop ? DialogTrigger : DrawerTrigger; + const CredenzaTrigger = isDesktop ? DialogTrigger : SheetTrigger; return ( @@ -79,10 +88,14 @@ const CredenzaContent = ({ className, children, ...props }: CredenzaProps) => { const isDesktop = useMediaQuery(desktop); // const isDesktop = true; - const CredenzaContent = isDesktop ? DialogContent : DrawerContent; + const CredenzaContent = isDesktop ? DialogContent : SheetContent; return ( - + {children} ); @@ -98,7 +111,7 @@ const CredenzaDescription = ({ const CredenzaDescription = isDesktop ? DialogDescription - : DrawerDescription; + : SheetDescription; return ( @@ -111,7 +124,7 @@ const CredenzaHeader = ({ className, children, ...props }: CredenzaProps) => { const isDesktop = useMediaQuery(desktop); // const isDesktop = true; - const CredenzaHeader = isDesktop ? DialogHeader : DrawerHeader; + const CredenzaHeader = isDesktop ? DialogHeader : SheetHeader; return ( @@ -124,7 +137,7 @@ const CredenzaTitle = ({ className, children, ...props }: CredenzaProps) => { const isDesktop = useMediaQuery(desktop); // const isDesktop = true; - const CredenzaTitle = isDesktop ? DialogTitle : DrawerTitle; + const CredenzaTitle = isDesktop ? DialogTitle : SheetTitle; return ( @@ -134,25 +147,24 @@ const CredenzaTitle = ({ className, children, ...props }: CredenzaProps) => { }; const CredenzaBody = ({ className, children, ...props }: CredenzaProps) => { - return ( -
- {children} -
- ); - - // return ( - //
+ // return ( + //
// {children} //
// ); + return ( +
+ {children} +
+ ); }; const CredenzaFooter = ({ className, children, ...props }: CredenzaProps) => { const isDesktop = useMediaQuery(desktop); // const isDesktop = true; - const CredenzaFooter = isDesktop ? DialogFooter : DrawerFooter; + const CredenzaFooter = isDesktop ? DialogFooter : SheetFooter; return ( diff --git a/src/components/Enable2FaForm.tsx b/src/components/Enable2FaForm.tsx index 3418dcd9..4e1caf14 100644 --- a/src/components/Enable2FaForm.tsx +++ b/src/components/Enable2FaForm.tsx @@ -224,7 +224,7 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) {
- +
, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx index 521b94b0..bcafdadb 100644 --- a/src/components/ui/toast.tsx +++ b/src/components/ui/toast.tsx @@ -25,7 +25,7 @@ const ToastViewport = React.forwardRef< ToastViewport.displayName = ToastPrimitives.Viewport.displayName const toastVariants = cva( - "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", + "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-3 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", { variants: { variant: { diff --git a/src/hooks/useToast.ts b/src/hooks/useToast.ts index 992106aa..758958e7 100644 --- a/src/hooks/useToast.ts +++ b/src/hooks/useToast.ts @@ -6,7 +6,7 @@ import * as React from "react"; import type { ToastActionElement, ToastProps } from "@/components/ui/toast"; const TOAST_LIMIT = 3; -const TOAST_REMOVE_DELAY = 5 * 1000; +const TOAST_REMOVE_DELAY = 1 * 1000; type ToasterToast = ToastProps & { id: string; diff --git a/src/providers/ThemeProvider.tsx b/src/providers/ThemeProvider.tsx index 6b5fce1a..e5fb1e3e 100644 --- a/src/providers/ThemeProvider.tsx +++ b/src/providers/ThemeProvider.tsx @@ -1,8 +1,8 @@ "use client"; -import * as React from "react"; import { ThemeProvider as NextThemesProvider } from "next-themes"; -import { type ThemeProviderProps } from "next-themes/dist/types"; + +type ThemeProviderProps = React.ComponentProps; export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return {children};