feat(lst): added update settings into the entire app
This commit is contained in:
121
frontend/src/components/admin/settings/SettingForm.tsx
Normal file
121
frontend/src/components/admin/settings/SettingForm.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import {Input} from "@/components/ui/input";
|
||||
import {Label} from "@/components/ui/label";
|
||||
import {Settings} from "./SettingsPage";
|
||||
import {toast} from "sonner";
|
||||
import {useState} from "react";
|
||||
import {useForm} from "react-hook-form";
|
||||
import {z} from "zod";
|
||||
import {zodResolver} from "@hookform/resolvers/zod";
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
import {getSettings} from "@/utils/querys/settings";
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
import axios from "axios";
|
||||
|
||||
const FormSchema = z.object({
|
||||
value: z.string().min(1, "You must enter a value greater than 0"),
|
||||
});
|
||||
export function ChangeSetting({setting}: {setting: Settings}) {
|
||||
const {token} = useSessionStore();
|
||||
const {refetch} = useQuery(getSettings(token ?? ""));
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: {errors},
|
||||
} = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
value: setting.value || "",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
setSaving(!saving);
|
||||
const update = {...data, name: setting.name};
|
||||
// console.log(update);
|
||||
try {
|
||||
const result = await axios.patch("/api/server/settings", update, {
|
||||
headers: {Authorization: `Bearer ${token}`},
|
||||
});
|
||||
|
||||
if (result.data.success) {
|
||||
setOpen(!open);
|
||||
setSaving(false);
|
||||
refetch();
|
||||
toast.success(result.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(isOpen) => {
|
||||
if (!open) {
|
||||
reset();
|
||||
}
|
||||
setOpen(isOpen);
|
||||
// toast.message("Model was something", {
|
||||
// description: isOpen ? "Modal is open" : "Modal is closed",
|
||||
// });
|
||||
}}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline">Edit</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{setting.name}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Update the setting and press save to complete the changes.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div>
|
||||
<>
|
||||
<Label htmlFor={"value"} className="m-1">
|
||||
Value
|
||||
</Label>
|
||||
<Input
|
||||
{...register("value")}
|
||||
className={errors.value ? "border-red-500" : ""}
|
||||
aria-invalid={!!errors.value}
|
||||
/>
|
||||
{errors.value && <p className="text-red-500 text-sm mt-1">{errors.value.message}</p>}
|
||||
</>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<div className="flex justify-end mt-2">
|
||||
<Button type="submit" disabled={saving}>
|
||||
{saving ? (
|
||||
<>
|
||||
<span>Saving....</span>
|
||||
</>
|
||||
) : (
|
||||
<span>Save setting</span>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +1,24 @@
|
||||
import {LstCard} from "@/components/extendedUI/LstCard";
|
||||
import {Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
import {useModuleStore} from "@/lib/store/useModuleStore";
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
import {useRouter} from "@tanstack/react-router";
|
||||
import axios from "axios";
|
||||
import {ChangeSetting} from "./SettingForm";
|
||||
import {getSettings} from "@/utils/querys/settings";
|
||||
|
||||
export type Settings = {
|
||||
settings_id?: string;
|
||||
name?: string;
|
||||
value?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export default function SettingsPage() {
|
||||
const token = localStorage.getItem("auth_token");
|
||||
const {user} = useSessionStore();
|
||||
const {user, token} = useSessionStore();
|
||||
const {modules} = useModuleStore();
|
||||
const router = useRouter();
|
||||
|
||||
const fetchSettings = async () => {
|
||||
const {data} = await axios.get("/api/server/settings", {headers: {Authorization: `Bearer ${token}`}});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
const adminModule = modules.filter((n) => n.name === "admin");
|
||||
const userLevel = user?.roles.filter((r) => r.module_id === adminModule[0].module_id) || [];
|
||||
|
||||
@@ -23,12 +26,7 @@ export default function SettingsPage() {
|
||||
router.navigate({to: "/"});
|
||||
}
|
||||
|
||||
const {data, isError, error, isLoading} = useQuery({
|
||||
queryKey: ["settings"],
|
||||
queryFn: fetchSettings,
|
||||
enabled: !!token,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
const {data, isError, error, isLoading} = useQuery(getSettings(token ?? ""));
|
||||
|
||||
if (isLoading) {
|
||||
return <div>Loading.....</div>;
|
||||
@@ -38,10 +36,29 @@ export default function SettingsPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{data.map((i: any) => {
|
||||
return <LstCard key={i.settings_id}>{i.name}</LstCard>;
|
||||
})}
|
||||
</div>
|
||||
<LstCard className="m-2 flex place-content-center w-dvh">
|
||||
<Table>
|
||||
<TableCaption>All Settings</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Value</TableHead>
|
||||
<TableHead>Change</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data?.map((setting: Settings) => (
|
||||
<TableRow key={setting.settings_id}>
|
||||
<TableCell className="font-medium">{setting.name}</TableCell>
|
||||
<TableCell className="font-medium">{setting.value}</TableCell>
|
||||
<TableCell className="font-medium">{setting.description}</TableCell>
|
||||
<TableCell className="font-medium">
|
||||
<ChangeSetting setting={setting} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
|
||||
133
frontend/src/components/ui/dialog.tsx
Normal file
133
frontend/src/components/ui/dialog.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Dialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||
}
|
||||
|
||||
function DialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||
}
|
||||
|
||||
function DialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||
}
|
||||
|
||||
function DialogClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||
}
|
||||
|
||||
function DialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogContent({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
|
||||
return (
|
||||
<DialogPortal data-slot="dialog-portal">
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn("text-lg leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
}
|
||||
58
frontend/src/components/ui/spinner.tsx
Normal file
58
frontend/src/components/ui/spinner.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import * as React from "react";
|
||||
import {Slot} from "@radix-ui/react-slot";
|
||||
import {cva, type VariantProps} from "class-variance-authority";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
const spinnerVariants = cva("relative block opacity-[0.65]", {
|
||||
variants: {
|
||||
size: {
|
||||
sm: "w-4 h-4",
|
||||
md: "w-6 h-6",
|
||||
lg: "w-8 h-8",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: "sm",
|
||||
},
|
||||
});
|
||||
|
||||
export interface SpinnerProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof spinnerVariants> {
|
||||
loading?: boolean;
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
const Spinner = React.forwardRef<HTMLSpanElement, SpinnerProps>(
|
||||
({className, size, loading = true, asChild = false, ...props}, ref) => {
|
||||
const Comp = asChild ? Slot : "span";
|
||||
|
||||
const [bgColorClass, filteredClassName] = React.useMemo(() => {
|
||||
const bgClass = className?.match(/(?:dark:bg-|bg-)[a-zA-Z0-9-]+/g) || [];
|
||||
const filteredClasses = className?.replace(/(?:dark:bg-|bg-)[a-zA-Z0-9-]+/g, "").trim();
|
||||
|
||||
return [bgClass, filteredClasses];
|
||||
}, [className]);
|
||||
|
||||
if (!loading) return null;
|
||||
|
||||
return (
|
||||
<Comp className={cn(spinnerVariants({size, className: filteredClassName}))} ref={ref} {...props}>
|
||||
{Array.from({length: 8}).map((_, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="absolute top-0 left-1/2 w-[12.5%] h-full animate-spinner-leaf-fade"
|
||||
style={{
|
||||
transform: `rotate(${i * 45}deg)`,
|
||||
animationDelay: `${-(7 - i) * 100}ms`,
|
||||
}}
|
||||
>
|
||||
<span className={cn("block w-full h-[30%] rounded-full", bgColorClass)}></span>
|
||||
</span>
|
||||
))}
|
||||
</Comp>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Spinner.displayName = "Spinner";
|
||||
|
||||
export {Spinner};
|
||||
114
frontend/src/components/ui/table.tsx
Normal file
114
frontend/src/components/ui/table.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="table-container"
|
||||
className="relative w-full overflow-x-auto"
|
||||
>
|
||||
<table
|
||||
data-slot="table"
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
||||
return (
|
||||
<thead
|
||||
data-slot="table-header"
|
||||
className={cn("[&_tr]:border-b", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
||||
return (
|
||||
<tbody
|
||||
data-slot="table-body"
|
||||
className={cn("[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
||||
return (
|
||||
<tfoot
|
||||
data-slot="table-footer"
|
||||
className={cn(
|
||||
"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
||||
return (
|
||||
<tr
|
||||
data-slot="table-row"
|
||||
className={cn(
|
||||
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
||||
return (
|
||||
<th
|
||||
data-slot="table-head"
|
||||
className={cn(
|
||||
"text-muted-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
||||
return (
|
||||
<td
|
||||
data-slot="table-cell"
|
||||
className={cn(
|
||||
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableCaption({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"caption">) {
|
||||
return (
|
||||
<caption
|
||||
data-slot="table-caption"
|
||||
className={cn("text-muted-foreground mt-4 text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
}
|
||||
@@ -17,9 +17,10 @@ import {
|
||||
import {SessionProvider} from "../components/providers/Providers";
|
||||
import {Toaster} from "sonner";
|
||||
import {Button} from "../components/ui/button";
|
||||
import {useLogout} from "../lib/hooks/useLogout";
|
||||
import {useSession} from "../lib/hooks/useSession";
|
||||
|
||||
import {useSessionStore} from "../lib/store/sessionStore";
|
||||
import {useSession} from "@/hooks/useSession";
|
||||
import {useLogout} from "@/hooks/useLogout";
|
||||
|
||||
// same as the layout
|
||||
export const Route = createRootRoute({
|
||||
|
||||
3
frontend/src/utils/delay.ts
Normal file
3
frontend/src/utils/delay.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function delay(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
17
frontend/src/utils/querys/settings.tsx
Normal file
17
frontend/src/utils/querys/settings.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import {queryOptions} from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
export function getSettings(token: string) {
|
||||
return queryOptions({
|
||||
queryKey: ["settings"],
|
||||
queryFn: () => fetchSettings(token),
|
||||
enabled: !!token,
|
||||
staleTime: 1000,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
}
|
||||
|
||||
const fetchSettings = async (token: string) => {
|
||||
const {data} = await axios.get("/api/server/settings", {headers: {Authorization: `Bearer ${token}`}});
|
||||
return data.data;
|
||||
};
|
||||
37
server/services/server/controller/settings/updateSetting.ts
Normal file
37
server/services/server/controller/settings/updateSetting.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {and, eq, sql} from "drizzle-orm";
|
||||
import {db} from "../../../../../database/dbclient.js";
|
||||
import {settings} from "../../../../../database/schema/settings.js";
|
||||
import {log} from "../../../logger/logger.js";
|
||||
import {userRoles} from "../../../../../database/schema/userRoles.js";
|
||||
import {users} from "../../../../../database/schema/users.js";
|
||||
|
||||
export const updateSetting = async (data: any, user_id: string) => {
|
||||
log.info(user_id, "Adding a new setting");
|
||||
|
||||
// make sure the user is a system admin before moving forward
|
||||
const sysAdmin = await db
|
||||
.select()
|
||||
.from(userRoles)
|
||||
.where(and(eq(userRoles.user_id, user_id), eq(userRoles.role, "systemAdmin")));
|
||||
|
||||
if (sysAdmin) {
|
||||
log.info(`Setting ${data.name} is being updated`);
|
||||
|
||||
//get the username so we can update the correct field
|
||||
const user = await db.select().from(users).where(eq(users.user_id, user_id));
|
||||
try {
|
||||
//const settingID = await db.select().from(settings).where(eq(settings.name, data.module));
|
||||
|
||||
const updateSetting = await db
|
||||
.update(settings)
|
||||
.set({value: data.value, upd_user: user[0].username, upd_date: sql`NOW()`})
|
||||
.where(eq(settings.name, data.name));
|
||||
} catch (error) {
|
||||
log.error(error, "Error updating setting");
|
||||
throw new Error("Error updating Setting");
|
||||
}
|
||||
} else {
|
||||
log.info("This user cannot add new roles");
|
||||
throw new Error("The user trying to add a setting dose not have the correct permissions");
|
||||
}
|
||||
};
|
||||
@@ -17,7 +17,7 @@ const AddSetting = z.object({
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["server"],
|
||||
tags: ["server:settings"],
|
||||
summary: "Add Setting",
|
||||
method: "post",
|
||||
path: "/settings",
|
||||
|
||||
@@ -4,7 +4,7 @@ const app = new OpenAPIHono();
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["server"],
|
||||
tags: ["server:settings"],
|
||||
summary: "Returns all modules in the server",
|
||||
method: "delete",
|
||||
path: "/settings",
|
||||
|
||||
@@ -8,7 +8,7 @@ const app = new OpenAPIHono();
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["server"],
|
||||
tags: ["server:settings"],
|
||||
summary: "Returns all settings based on your permissions",
|
||||
method: "get",
|
||||
path: "/settings",
|
||||
|
||||
@@ -1,13 +1,29 @@
|
||||
import {createRoute, OpenAPIHono, z} from "@hono/zod-openapi";
|
||||
import type {User} from "../../../../types/users.js";
|
||||
import {log} from "../../../logger/logger.js";
|
||||
import {verify} from "hono/jwt";
|
||||
import {updateSetting} from "../../controller/settings/updateSetting.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UpdateSetting = z.object({
|
||||
name: z.string().openapi({example: "server"}),
|
||||
value: z.string().openapi({example: "localhost"}),
|
||||
});
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["server"],
|
||||
summary: "Returns all modules in the server",
|
||||
method: "post",
|
||||
path: "/",
|
||||
tags: ["server:settings"],
|
||||
summary: "Updates A setting",
|
||||
method: "patch",
|
||||
path: "/settings",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {schema: UpdateSetting},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
@@ -20,10 +36,63 @@ app.openapi(
|
||||
},
|
||||
description: "Response message",
|
||||
},
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}),
|
||||
},
|
||||
},
|
||||
description: "Internal Server Error",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Unauthenticated"})}),
|
||||
},
|
||||
},
|
||||
description: "Unauthorized",
|
||||
},
|
||||
500: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({message: z.string().optional().openapi({example: "Internal Server error"})}),
|
||||
},
|
||||
},
|
||||
description: "Internal Server Error",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
return c.json({success: true, message: "Example"}, 200);
|
||||
// make sure we have a vaid user being accessed thats really logged in
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (authHeader?.includes("Basic")) {
|
||||
return c.json({message: "You are a Basic user! Please login to get a token"}, 401);
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return c.json({message: "Unauthorized"}, 401);
|
||||
}
|
||||
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
let user: User;
|
||||
|
||||
try {
|
||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
user = payload.user as User;
|
||||
} catch (error) {
|
||||
log.error(error, "Failed session check, user must be logged out");
|
||||
return c.json({message: "Unauthorized"}, 401);
|
||||
}
|
||||
|
||||
// now pass all the data over to update the user info
|
||||
try {
|
||||
const data = await c?.req.json();
|
||||
await updateSetting(data, user.user_id ?? "");
|
||||
return c.json({success: true, message: "The Setting was just updated", data}, 200);
|
||||
} catch (error) {
|
||||
return c.json({message: "There was an error updating the settings.", error}, 400);
|
||||
}
|
||||
}
|
||||
);
|
||||
export default app;
|
||||
|
||||
@@ -7,6 +7,7 @@ import updateModule from "./route/modules/updateModules.js";
|
||||
import addModule from "./route/modules/addModule.js";
|
||||
import addSetting from "./route/settings/addSetting.js";
|
||||
import getSettings from "./route/settings/getSettings.js";
|
||||
import updateSetting from "./route/settings/updateSetting.js";
|
||||
|
||||
areModulesIn();
|
||||
const app = new OpenAPIHono();
|
||||
@@ -18,6 +19,7 @@ const routes = [
|
||||
// settings
|
||||
addSetting,
|
||||
getSettings,
|
||||
updateSetting,
|
||||
] as const;
|
||||
|
||||
// app.route("/server", modules);
|
||||
|
||||
Reference in New Issue
Block a user