Compare commits
4 Commits
d293ca6981
...
50cf87380d
| Author | SHA1 | Date | |
|---|---|---|---|
| 50cf87380d | |||
| 5fcadb9fc8 | |||
| 6fb615a743 | |||
| f5fd1fc605 |
@@ -10,7 +10,7 @@
|
||||
{"type": "test", "section": "📝 Testing Code"},
|
||||
{"type": "ci", "section": "📈 Project changes"}
|
||||
],
|
||||
"commitUrlFormat": "https://git.tuffraid.net/cowch/lstV2/commits{{hash}}",
|
||||
"commitUrlFormat": "https://git.tuffraid.net/cowch/lstV2/commits/{{hash}}",
|
||||
"compareUrlFormat": "https://git.tuffraid.net/cowch/lstV2/compare/{{previousTag}}...{{currentTag}}",
|
||||
"header": "# All CHanges to LST can be found below.\n"
|
||||
}
|
||||
|
||||
65
frontend/package-lock.json
generated
65
frontend/package-lock.json
generated
@@ -381,6 +381,7 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -397,6 +398,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -413,6 +415,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -429,6 +432,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -445,6 +449,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -461,6 +466,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -477,6 +483,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -493,6 +500,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -509,6 +517,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -525,6 +534,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -541,6 +551,7 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -557,6 +568,7 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -573,6 +585,7 @@
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -589,6 +602,7 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -605,6 +619,7 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -621,6 +636,7 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -637,6 +653,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -653,6 +670,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -669,6 +687,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -685,6 +704,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -701,6 +721,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -717,6 +738,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -733,6 +755,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -749,6 +772,7 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -765,6 +789,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1883,6 +1908,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1896,6 +1922,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1909,6 +1936,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1922,6 +1950,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1935,6 +1964,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1948,6 +1978,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1961,6 +1992,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1974,6 +2006,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1987,6 +2020,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2000,6 +2034,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2013,6 +2048,7 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2026,6 +2062,7 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2039,6 +2076,7 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2052,6 +2090,7 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2065,6 +2104,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2078,6 +2118,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2091,6 +2132,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2104,6 +2146,7 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2117,6 +2160,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2918,6 +2962,7 @@
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
@@ -2931,7 +2976,7 @@
|
||||
"version": "19.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
|
||||
"integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
@@ -2941,7 +2986,7 @@
|
||||
"version": "19.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
|
||||
"integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
@@ -3531,7 +3576,7 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
@@ -3632,6 +3677,7 @@
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
|
||||
"integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -4003,6 +4049,7 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -4036,7 +4083,7 @@
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz",
|
||||
"integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
@@ -4724,6 +4771,7 @@
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -4868,6 +4916,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
@@ -4887,6 +4936,7 @@
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
|
||||
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -5178,7 +5228,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
@@ -5199,6 +5249,7 @@
|
||||
"version": "4.34.8",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz",
|
||||
"integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.6"
|
||||
@@ -5332,6 +5383,7 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -5445,7 +5497,7 @@
|
||||
"version": "4.19.3",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz",
|
||||
"integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.25.0",
|
||||
@@ -5622,6 +5674,7 @@
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz",
|
||||
"integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,7 +81,9 @@ const LoginForm = () => {
|
||||
<hr className="rounded"></hr>
|
||||
<form onSubmit={handleSubmit(onSubmitLogin)}>
|
||||
<div>
|
||||
<Label>Username</Label>
|
||||
<Label htmlFor="username" className="m-1">
|
||||
Username
|
||||
</Label>
|
||||
<Input
|
||||
placeholder="smith001"
|
||||
{...register("username")}
|
||||
@@ -92,7 +94,9 @@ const LoginForm = () => {
|
||||
</div>
|
||||
<div>
|
||||
<>
|
||||
<Label htmlFor={"password"}>Password</Label>
|
||||
<Label htmlFor={"password"} className="m-1">
|
||||
Password
|
||||
</Label>
|
||||
<Input
|
||||
type="password"
|
||||
{...register("password")}
|
||||
@@ -106,15 +110,24 @@ const LoginForm = () => {
|
||||
<div className="flex">
|
||||
<Controller
|
||||
render={({field}) => (
|
||||
<Checkbox id="remember" checked={field.value} onCheckedChange={field.onChange} />
|
||||
<>
|
||||
<Checkbox
|
||||
id="remember"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
<label
|
||||
htmlFor="remember"
|
||||
className="pl-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
remember me
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
control={control}
|
||||
name="rememberMe"
|
||||
defaultValue={rememeberMe}
|
||||
/>
|
||||
<Label htmlFor="remember" className="pl-2">
|
||||
remember me
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
|
||||
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,
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useSessionStore} from "@/lib/store/sessionStore";
|
||||
import {useRouter} from "@tanstack/react-router";
|
||||
import {useSessionStore} from "../store/sessionStore";
|
||||
|
||||
export const useLogout = () => {
|
||||
const {clearSession} = useSessionStore();
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useQuery} from "@tanstack/react-query";
|
||||
import {useSessionStore} from "../store/sessionStore";
|
||||
import {useSessionStore} from "../lib/store/sessionStore";
|
||||
import {useEffect} from "react";
|
||||
|
||||
const fetchSession = async () => {
|
||||
@@ -34,6 +34,7 @@ export const useSession = () => {
|
||||
staleTime: 60 * 1000,
|
||||
gcTime: 10 * 60 * 1000, // 10 mins
|
||||
refetchOnWindowFocus: true,
|
||||
//refetchInterval: 1000 * 60 * 2, // Auto-refetch every 2 minutes
|
||||
});
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
@@ -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;
|
||||
};
|
||||
@@ -9,7 +9,7 @@ import {log} from "../../logger/logger.js";
|
||||
/**
|
||||
* Authenticate a user and return a JWT.
|
||||
*/
|
||||
const {sign, verify} = jwt;
|
||||
const {sign} = jwt;
|
||||
|
||||
export async function login(
|
||||
username: string,
|
||||
@@ -56,5 +56,6 @@ export async function login(
|
||||
}
|
||||
|
||||
const token = sign({user: userData}, secret, {expiresIn: expiresIn * 60});
|
||||
|
||||
return {token, user: userData};
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@ import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {verify} from "hono/jwt";
|
||||
import {log} from "../../logger/logger.js";
|
||||
import {authMiddleware} from "../middleware/authMiddleware.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
const session = new OpenAPIHono();
|
||||
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
|
||||
const secret: string = process.env.JWT_SECRET!;
|
||||
|
||||
const {sign} = jwt;
|
||||
const UserSchema = z.object({
|
||||
username: z
|
||||
.string()
|
||||
@@ -79,7 +83,11 @@ session.openapi(
|
||||
|
||||
try {
|
||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||
return c.json({data: {token: token, user: payload.user}}, 200);
|
||||
|
||||
// If it's valid, return a new token
|
||||
const newToken = sign({user: payload.user}, secret, {expiresIn: expiresIn * 60});
|
||||
|
||||
return c.json({data: {token: newToken, user: payload.user}}, 200);
|
||||
} catch (error) {
|
||||
log.error(error, "Failed session check, user must be logged out");
|
||||
return c.json({message: "Unauthorized"}, 401);
|
||||
|
||||
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