refactor(lst): refactored to be back to npm from bun
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
|||||||
|
dist
|
||||||
|
frontend/dist
|
||||||
|
server/dist
|
||||||
# ---> Node
|
# ---> Node
|
||||||
bun.lock
|
bun.lock
|
||||||
.nx
|
.nx
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
{"type": "docs", "section": "📚 Documentation"},
|
{"type": "docs", "section": "📚 Documentation"},
|
||||||
{"type": "style", "hidden": true},
|
{"type": "style", "hidden": true},
|
||||||
{"type": "refactor", "section": "🛠️ Code Refactor"},
|
{"type": "refactor", "section": "🛠️ Code Refactor"},
|
||||||
{"type": "perf", "hidden": false, "section": "🚀 Code Refactor"},
|
{"type": "perf", "hidden": false, "section": "🚀 Performance"},
|
||||||
{"type": "test", "section": "📝 Testing Code"},
|
{"type": "test", "section": "📝 Testing Code"},
|
||||||
{"type": "ci", "section": "📈 Project changes"}
|
{"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}}",
|
"compareUrlFormat": "https://git.tuffraid.net/cowch/lstV2/compare/{{previousTag}}...{{currentTag}}",
|
||||||
"header": "# All CHanges to LST can be found below.\n`"
|
"header": "# All CHanges to LST can be found below.\n"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
url: http://localhost:3000/api/auth/login
|
url: http://localhost:4000/api/auth/login
|
||||||
body: json
|
body: json
|
||||||
auth: none
|
auth: none
|
||||||
}
|
}
|
||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
{
|
{
|
||||||
"username": "adm_matthes",
|
"username": "matthes01",
|
||||||
"password": "Nova0511!"
|
"password": "99Monsters200Scary!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: http://localhost:3000/api/server/modules
|
url: http://localhost:4000/api/server/modules
|
||||||
body: json
|
body: none
|
||||||
auth: none
|
auth: none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import {drizzle} from "drizzle-orm/postgres-js";
|
import {drizzle} from "drizzle-orm/postgres-js";
|
||||||
import postgres from "postgres";
|
import postgres from "postgres";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
const database = process.env.DATABASE_URL || "";
|
const database = process.env.DATABASE_URL || "";
|
||||||
|
|
||||||
const queryClient = postgres(database);
|
const queryClient = postgres(database);
|
||||||
@@ -2,6 +2,7 @@ CREATE TABLE "modules" (
|
|||||||
"module_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
"module_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
"name" text NOT NULL,
|
"name" text NOT NULL,
|
||||||
"active" boolean DEFAULT false,
|
"active" boolean DEFAULT false,
|
||||||
|
"roles" text DEFAULT '["view", "systemAdmin"]' NOT NULL,
|
||||||
"add_User" text DEFAULT 'LST_System' NOT NULL,
|
"add_User" text DEFAULT 'LST_System' NOT NULL,
|
||||||
"add_Date" timestamp DEFAULT now(),
|
"add_Date" timestamp DEFAULT now(),
|
||||||
"upd_User" text DEFAULT 'LST_System' NOT NULL,
|
"upd_User" text DEFAULT 'LST_System' NOT NULL,
|
||||||
@@ -21,7 +22,24 @@ CREATE TABLE "userRoles" (
|
|||||||
"user_id" uuid NOT NULL,
|
"user_id" uuid NOT NULL,
|
||||||
"role_id" uuid NOT NULL,
|
"role_id" uuid NOT NULL,
|
||||||
"module_id" uuid NOT NULL,
|
"module_id" uuid NOT NULL,
|
||||||
"roles" text NOT NULL,
|
"role" text NOT NULL,
|
||||||
|
"add_User" text DEFAULT 'LST_System' NOT NULL,
|
||||||
|
"add_Date" timestamp DEFAULT now(),
|
||||||
|
"upd_User" text DEFAULT 'LST_System' NOT NULL,
|
||||||
|
"upd_date" timestamp DEFAULT now()
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"user_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
|
"username" text NOT NULL,
|
||||||
|
"email" text NOT NULL,
|
||||||
|
"password" text NOT NULL,
|
||||||
|
"passwordToken" text,
|
||||||
|
"passwordTokenExpires" timestamp,
|
||||||
|
"active" boolean DEFAULT true NOT NULL,
|
||||||
|
"pingcode" numeric,
|
||||||
|
"role" text DEFAULT 'user' NOT NULL,
|
||||||
|
"lastLogin" timestamp DEFAULT now(),
|
||||||
"add_User" text DEFAULT 'LST_System' NOT NULL,
|
"add_User" text DEFAULT 'LST_System' NOT NULL,
|
||||||
"add_Date" timestamp DEFAULT now(),
|
"add_Date" timestamp DEFAULT now(),
|
||||||
"upd_User" text DEFAULT 'LST_System' NOT NULL,
|
"upd_User" text DEFAULT 'LST_System' NOT NULL,
|
||||||
@@ -33,4 +51,5 @@ ALTER TABLE "userRoles" ADD CONSTRAINT "userRoles_role_id_roles_role_id_fk" FORE
|
|||||||
ALTER TABLE "userRoles" ADD CONSTRAINT "userRoles_module_id_modules_module_id_fk" FOREIGN KEY ("module_id") REFERENCES "public"."modules"("module_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
ALTER TABLE "userRoles" ADD CONSTRAINT "userRoles_module_id_modules_module_id_fk" FOREIGN KEY ("module_id") REFERENCES "public"."modules"("module_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX "module_name" ON "modules" USING btree ("name");--> statement-breakpoint
|
CREATE UNIQUE INDEX "module_name" ON "modules" USING btree ("name");--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX "role_name" ON "roles" USING btree ("name");--> statement-breakpoint
|
CREATE UNIQUE INDEX "role_name" ON "roles" USING btree ("name");--> statement-breakpoint
|
||||||
CREATE UNIQUE INDEX "user_module_unique" ON "userRoles" USING btree ("user_id","module_id");
|
CREATE UNIQUE INDEX "user_module_unique" ON "userRoles" USING btree ("user_id","module_id");--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX "username" ON "users" USING btree ("username");
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "eb681265-0d44-4a8a-acaf-840acc169228",
|
"id": "467c98f1-3785-42b5-80ed-528eac5fcbe4",
|
||||||
"prevId": "313590a8-2068-45b5-96fc-cfa5d2b32b56",
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"dialect": "postgresql",
|
"dialect": "postgresql",
|
||||||
"tables": {
|
"tables": {
|
||||||
13
database/migrations/meta/_journal.json
Normal file
13
database/migrations/meta/_journal.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"idx": 0,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1740534974452,
|
||||||
|
"tag": "0000_nebulous_bulldozer",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
import {defineConfig} from "drizzle-kit";
|
import {defineConfig} from "drizzle-kit";
|
||||||
const database = process.env.DATABASE_URL || "";
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const database = process.env.DATABASE_URL! || "";
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
dialect: "postgresql", // 'mysql' | 'sqlite' | 'turso'
|
dialect: "postgresql", // 'mysql' | 'sqlite' | 'turso'
|
||||||
schema: "./server/database/schema/",
|
schema: "database/schema",
|
||||||
out: "./server/database/migrations",
|
out: "database/migrations",
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: database,
|
url: database,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"tsx": true,
|
"tsx": true,
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "",
|
"config": "",
|
||||||
"css": "src/styles.css",
|
"css": "src/style.css",
|
||||||
"baseColor": "neutral",
|
"baseColor": "neutral",
|
||||||
"cssVariables": true,
|
"cssVariables": true,
|
||||||
"prefix": ""
|
"prefix": ""
|
||||||
|
|||||||
5597
frontend/package-lock.json
generated
Normal file
5597
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend",
|
"name": "vitefix",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun --bun vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "tsc -b && vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"shad": "npx shadcn@canary add "
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@antfu/ni": "^23.3.1",
|
"@hookform/resolvers": "^4.1.2",
|
||||||
"@hookform/resolvers": "^4.1.0",
|
|
||||||
"@radix-ui/react-avatar": "^1.1.3",
|
"@radix-ui/react-avatar": "^1.1.3",
|
||||||
"@radix-ui/react-checkbox": "^1.1.4",
|
"@radix-ui/react-checkbox": "^1.1.4",
|
||||||
"@radix-ui/react-collapsible": "^1.1.3",
|
"@radix-ui/react-collapsible": "^1.1.3",
|
||||||
@@ -21,42 +21,38 @@
|
|||||||
"@radix-ui/react-separator": "^1.1.2",
|
"@radix-ui/react-separator": "^1.1.2",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.1.8",
|
||||||
"@tailwindcss/vite": "^4.0.6",
|
"@tailwindcss/vite": "^4.0.9",
|
||||||
"@tanstack/react-query": "^5.66.5",
|
"@tanstack/react-query": "^5.66.9",
|
||||||
"@tanstack/react-router": "^1.106.0",
|
"@tanstack/react-router": "^1.111.11",
|
||||||
"@tanstack/zod-form-adapter": "^0.42.1",
|
|
||||||
"@types/react-grid-layout": "^1.3.5",
|
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"lucide-react": "^0.475.0",
|
"lucide-react": "^0.476.0",
|
||||||
"next-themes": "^0.4.4",
|
"next-themes": "^0.4.4",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-grid-layout": "^1.5.0",
|
"react-grid-layout": "^1.5.0",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"shadcn": "^2.4.0-canary.6",
|
|
||||||
"sonner": "^2.0.1",
|
"sonner": "^2.0.1",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.2",
|
||||||
"tailwindcss": "^4.0.6",
|
"tailwindcss": "^4.0.9",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"zod": "^3.24.2",
|
"zod": "^3.24.2",
|
||||||
"zustand": "^5.0.3"
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.19.0",
|
"@eslint/js": "^9.21.0",
|
||||||
"@tanstack/router-devtools": "^1.106.0",
|
"@tanstack/router-devtools": "^1.106.0",
|
||||||
"@tanstack/router-plugin": "^1.106.0",
|
"@tanstack/router-plugin": "^1.106.0",
|
||||||
"@types/node": "^22.13.4",
|
"@types/react": "^19.0.10",
|
||||||
"@types/react": "^19.0.8",
|
"@types/react-dom": "^19.0.4",
|
||||||
"@types/react-dom": "^19.0.3",
|
"@vitejs/plugin-react-swc": "^3.8.0",
|
||||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
"eslint": "^9.21.0",
|
||||||
"eslint": "^9.19.0",
|
|
||||||
"eslint-plugin-react-hooks": "^5.0.0",
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.18",
|
"eslint-plugin-react-refresh": "^0.4.19",
|
||||||
"globals": "^15.14.0",
|
"globals": "^15.15.0",
|
||||||
"typescript": "~5.7.2",
|
"typescript": "~5.7.2",
|
||||||
"typescript-eslint": "^8.22.0",
|
"typescript-eslint": "^8.24.1",
|
||||||
"vite": "^6.1.0"
|
"vite": "^6.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,51 @@
|
|||||||
import * as React from "react";
|
import * as React from "react"
|
||||||
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
||||||
|
|
||||||
import {cn} from "../../lib/utils";
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
function Avatar({className, ...props}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
function Avatar({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
||||||
return (
|
return (
|
||||||
<AvatarPrimitive.Root
|
<AvatarPrimitive.Root
|
||||||
data-slot="avatar"
|
data-slot="avatar"
|
||||||
className={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", className)}
|
className={cn(
|
||||||
|
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
||||||
|
className
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function AvatarImage({className, ...props}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
function AvatarImage({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||||
return (
|
return (
|
||||||
<AvatarPrimitive.Image
|
<AvatarPrimitive.Image
|
||||||
data-slot="avatar-image"
|
data-slot="avatar-image"
|
||||||
className={cn("aspect-square size-full", className)}
|
className={cn("aspect-square size-full", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function AvatarFallback({className, ...props}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
function AvatarFallback({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
||||||
return (
|
return (
|
||||||
<AvatarPrimitive.Fallback
|
<AvatarPrimitive.Fallback
|
||||||
data-slot="avatar-fallback"
|
data-slot="avatar-fallback"
|
||||||
className={cn("bg-muted flex size-full items-center justify-center rounded-full", className)}
|
className={cn(
|
||||||
|
"bg-muted flex size-full items-center justify-center rounded-full",
|
||||||
|
className
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Avatar, AvatarImage, AvatarFallback};
|
export { Avatar, AvatarImage, AvatarFallback }
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
import * as React from "react";
|
import * as React from "react"
|
||||||
import {Slot} from "@radix-ui/react-slot";
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
import {cva, type VariantProps} from "class-variance-authority";
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
import {cn} from "../../lib/utils";
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 focus-visible:ring-4 focus-visible:outline-1 aria-invalid:focus-visible:ring-0",
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
|
default:
|
||||||
destructive: "bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90",
|
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||||
outline: "border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
|
destructive:
|
||||||
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
|
||||||
|
outline:
|
||||||
|
"border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||||
sm: "h-8 rounded-md px-3 has-[>svg]:px-2.5",
|
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||||
icon: "size-9",
|
icon: "size-9",
|
||||||
},
|
},
|
||||||
@@ -28,7 +32,7 @@ const buttonVariants = cva(
|
|||||||
size: "default",
|
size: "default",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
function Button({
|
function Button({
|
||||||
className,
|
className,
|
||||||
@@ -38,11 +42,17 @@ function Button({
|
|||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"button"> &
|
}: React.ComponentProps<"button"> &
|
||||||
VariantProps<typeof buttonVariants> & {
|
VariantProps<typeof buttonVariants> & {
|
||||||
asChild?: boolean;
|
asChild?: boolean
|
||||||
}) {
|
}) {
|
||||||
const Comp = asChild ? Slot : "button";
|
const Comp = asChild ? Slot : "button"
|
||||||
|
|
||||||
return <Comp data-slot="button" className={cn(buttonVariants({variant, size, className}))} {...props} />;
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="button"
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Button, buttonVariants};
|
export { Button, buttonVariants }
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
<div
|
<div
|
||||||
data-slot="card"
|
data-slot="card"
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border shadow-sm",
|
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -19,7 +19,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-header"
|
data-slot="card-header"
|
||||||
className={cn("flex flex-col gap-1.5 px-6 pt-6", className)}
|
className={cn("flex flex-col gap-1.5 px-6", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -59,7 +59,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-footer"
|
data-slot="card-footer"
|
||||||
className={cn("flex items-center px-6 pb-6", className)}
|
className={cn("flex items-center px-6", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ function Checkbox({
|
|||||||
<CheckboxPrimitive.Root
|
<CheckboxPrimitive.Root
|
||||||
data-slot="checkbox"
|
data-slot="checkbox"
|
||||||
className={cn(
|
className={cn(
|
||||||
"peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
"peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<CheckboxPrimitive.Indicator
|
<CheckboxPrimitive.Indicator
|
||||||
data-slot="checkbox-indicator"
|
data-slot="checkbox-indicator"
|
||||||
className="flex items-center justify-center text-current"
|
className="flex items-center justify-center text-current transition-none"
|
||||||
>
|
>
|
||||||
<CheckIcon className="size-3.5" />
|
<CheckIcon className="size-3.5" />
|
||||||
</CheckboxPrimitive.Indicator>
|
</CheckboxPrimitive.Indicator>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
||||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
||||||
@@ -72,7 +74,7 @@ function DropdownMenuItem({
|
|||||||
data-inset={inset}
|
data-inset={inset}
|
||||||
data-variant={variant}
|
data-variant={variant}
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/40 data-[variant=destructive]:focus:text-destructive-foreground data-[variant=destructive]:*:[svg]:!text-destructive-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -153,7 +155,7 @@ function DropdownMenuLabel({
|
|||||||
data-slot="dropdown-menu-label"
|
data-slot="dropdown-menu-label"
|
||||||
data-inset={inset}
|
data-inset={inset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8",
|
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function Label({
|
|||||||
<LabelPrimitive.Root
|
<LabelPrimitive.Root
|
||||||
data-slot="label"
|
data-slot="label"
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client"
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react"
|
||||||
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
||||||
|
|
||||||
import {cn} from "../../lib/utils";
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
function Separator({
|
function Separator({
|
||||||
className,
|
className,
|
||||||
@@ -22,7 +22,7 @@ function Separator({
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Separator};
|
export { Separator }
|
||||||
|
|||||||
@@ -1,26 +1,35 @@
|
|||||||
import * as React from "react";
|
import * as React from "react"
|
||||||
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
||||||
import {XIcon} from "lucide-react";
|
import { XIcon } from "lucide-react"
|
||||||
|
|
||||||
import {cn} from "../../lib/utils";
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
||||||
return <SheetPrimitive.Root data-slot="sheet" {...props} />;
|
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetTrigger({...props}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
function SheetTrigger({
|
||||||
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
|
...props
|
||||||
|
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
||||||
|
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetClose({...props}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
function SheetClose({
|
||||||
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
|
...props
|
||||||
|
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
||||||
|
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetPortal({...props}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
function SheetPortal({
|
||||||
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
|
...props
|
||||||
|
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
||||||
|
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetOverlay({className, ...props}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
function SheetOverlay({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
||||||
return (
|
return (
|
||||||
<SheetPrimitive.Overlay
|
<SheetPrimitive.Overlay
|
||||||
data-slot="sheet-overlay"
|
data-slot="sheet-overlay"
|
||||||
@@ -30,7 +39,7 @@ function SheetOverlay({className, ...props}: React.ComponentProps<typeof SheetPr
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetContent({
|
function SheetContent({
|
||||||
@@ -39,7 +48,7 @@ function SheetContent({
|
|||||||
side = "right",
|
side = "right",
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
||||||
side?: "top" | "right" | "bottom" | "left";
|
side?: "top" | "right" | "bottom" | "left"
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<SheetPortal>
|
<SheetPortal>
|
||||||
@@ -67,35 +76,62 @@ function SheetContent({
|
|||||||
</SheetPrimitive.Close>
|
</SheetPrimitive.Close>
|
||||||
</SheetPrimitive.Content>
|
</SheetPrimitive.Content>
|
||||||
</SheetPortal>
|
</SheetPortal>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return <div data-slot="sheet-header" className={cn("flex flex-col gap-1.5 p-4", className)} {...props} />;
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sheet-header"
|
||||||
|
className={cn("flex flex-col gap-1.5 p-4", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return <div data-slot="sheet-footer" className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />;
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sheet-footer"
|
||||||
|
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetTitle({className, ...props}: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
function SheetTitle({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
||||||
return (
|
return (
|
||||||
<SheetPrimitive.Title
|
<SheetPrimitive.Title
|
||||||
data-slot="sheet-title"
|
data-slot="sheet-title"
|
||||||
className={cn("text-foreground font-semibold tracking-tight", className)}
|
className={cn("text-foreground font-semibold", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SheetDescription({className, ...props}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
function SheetDescription({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
||||||
return (
|
return (
|
||||||
<SheetPrimitive.Description
|
<SheetPrimitive.Description
|
||||||
data-slot="sheet-description"
|
data-slot="sheet-description"
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription};
|
export {
|
||||||
|
Sheet,
|
||||||
|
SheetTrigger,
|
||||||
|
SheetClose,
|
||||||
|
SheetContent,
|
||||||
|
SheetHeader,
|
||||||
|
SheetFooter,
|
||||||
|
SheetTitle,
|
||||||
|
SheetDescription,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,96 +1,115 @@
|
|||||||
import * as React from "react";
|
import * as React from "react"
|
||||||
import {Slot} from "@radix-ui/react-slot";
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
import {VariantProps, cva} from "class-variance-authority";
|
import { VariantProps, cva } from "class-variance-authority"
|
||||||
import {PanelLeftIcon} from "lucide-react";
|
import { PanelLeftIcon } from "lucide-react"
|
||||||
|
|
||||||
import {useIsMobile} from "../../hooks/use-mobile";
|
import { useIsMobile } from "@/hooks/use-mobile"
|
||||||
import {cn} from "../../lib/utils";
|
import { cn } from "@/lib/utils"
|
||||||
import {Button} from "../../components/ui/button";
|
import { Button } from "@/components/ui/button"
|
||||||
import {Input} from "../../components/ui/input";
|
import { Input } from "@/components/ui/input"
|
||||||
import {Separator} from "../../components/ui/separator";
|
import { Separator } from "@/components/ui/separator"
|
||||||
import {Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle} from "../../components/ui/sheet";
|
import {
|
||||||
import {Skeleton} from "../../components/ui/skeleton";
|
Sheet,
|
||||||
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "../../components/ui/tooltip";
|
SheetContent,
|
||||||
|
SheetDescription,
|
||||||
|
SheetHeader,
|
||||||
|
SheetTitle,
|
||||||
|
} from "@/components/ui/sheet"
|
||||||
|
import { Skeleton } from "@/components/ui/skeleton"
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip"
|
||||||
|
|
||||||
const SIDEBAR_COOKIE_NAME = "sidebar_state";
|
const SIDEBAR_COOKIE_NAME = "sidebar_state"
|
||||||
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
|
||||||
const SIDEBAR_WIDTH = "16rem";
|
const SIDEBAR_WIDTH = "16rem"
|
||||||
const SIDEBAR_WIDTH_MOBILE = "18rem";
|
const SIDEBAR_WIDTH_MOBILE = "18rem"
|
||||||
const SIDEBAR_WIDTH_ICON = "3rem";
|
const SIDEBAR_WIDTH_ICON = "3rem"
|
||||||
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
const SIDEBAR_KEYBOARD_SHORTCUT = "b"
|
||||||
|
|
||||||
type SidebarContext = {
|
type SidebarContext = {
|
||||||
state: "expanded" | "collapsed";
|
state: "expanded" | "collapsed"
|
||||||
open: boolean;
|
open: boolean
|
||||||
setOpen: (open: boolean) => void;
|
setOpen: (open: boolean) => void
|
||||||
openMobile: boolean;
|
openMobile: boolean
|
||||||
setOpenMobile: (open: boolean) => void;
|
setOpenMobile: (open: boolean) => void
|
||||||
isMobile: boolean;
|
isMobile: boolean
|
||||||
toggleSidebar: () => void;
|
toggleSidebar: () => void
|
||||||
};
|
}
|
||||||
|
|
||||||
const SidebarContext = React.createContext<SidebarContext | null>(null);
|
const SidebarContext = React.createContext<SidebarContext | null>(null)
|
||||||
|
|
||||||
function useSidebar() {
|
function useSidebar() {
|
||||||
const context = React.useContext(SidebarContext);
|
const context = React.useContext(SidebarContext)
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error("useSidebar must be used within a SidebarProvider.");
|
throw new Error("useSidebar must be used within a SidebarProvider.")
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
const SidebarProvider = React.forwardRef<
|
function SidebarProvider({
|
||||||
HTMLDivElement,
|
defaultOpen = true,
|
||||||
React.ComponentProps<"div"> & {
|
open: openProp,
|
||||||
defaultOpen?: boolean;
|
onOpenChange: setOpenProp,
|
||||||
open?: boolean;
|
className,
|
||||||
onOpenChange?: (open: boolean) => void;
|
style,
|
||||||
}
|
children,
|
||||||
>(({defaultOpen = true, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props}, ref) => {
|
...props
|
||||||
const isMobile = useIsMobile();
|
}: React.ComponentProps<"div"> & {
|
||||||
const [openMobile, setOpenMobile] = React.useState(false);
|
defaultOpen?: boolean
|
||||||
|
open?: boolean
|
||||||
|
onOpenChange?: (open: boolean) => void
|
||||||
|
}) {
|
||||||
|
const isMobile = useIsMobile()
|
||||||
|
const [openMobile, setOpenMobile] = React.useState(false)
|
||||||
|
|
||||||
// This is the internal state of the sidebar.
|
// This is the internal state of the sidebar.
|
||||||
// We use openProp and setOpenProp for control from outside the component.
|
// We use openProp and setOpenProp for control from outside the component.
|
||||||
const [_open, _setOpen] = React.useState(defaultOpen);
|
const [_open, _setOpen] = React.useState(defaultOpen)
|
||||||
const open = openProp ?? _open;
|
const open = openProp ?? _open
|
||||||
const setOpen = React.useCallback(
|
const setOpen = React.useCallback(
|
||||||
(value: boolean | ((value: boolean) => boolean)) => {
|
(value: boolean | ((value: boolean) => boolean)) => {
|
||||||
const openState = typeof value === "function" ? value(open) : value;
|
const openState = typeof value === "function" ? value(open) : value
|
||||||
if (setOpenProp) {
|
if (setOpenProp) {
|
||||||
setOpenProp(openState);
|
setOpenProp(openState)
|
||||||
} else {
|
} else {
|
||||||
_setOpen(openState);
|
_setOpen(openState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This sets the cookie to keep the sidebar state.
|
// This sets the cookie to keep the sidebar state.
|
||||||
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
|
||||||
},
|
},
|
||||||
[setOpenProp, open]
|
[setOpenProp, open]
|
||||||
);
|
)
|
||||||
|
|
||||||
// Helper to toggle the sidebar.
|
// Helper to toggle the sidebar.
|
||||||
const toggleSidebar = React.useCallback(() => {
|
const toggleSidebar = React.useCallback(() => {
|
||||||
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
|
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
|
||||||
}, [isMobile, setOpen, setOpenMobile]);
|
}, [isMobile, setOpen, setOpenMobile])
|
||||||
|
|
||||||
// Adds a keyboard shortcut to toggle the sidebar.
|
// Adds a keyboard shortcut to toggle the sidebar.
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
|
if (
|
||||||
event.preventDefault();
|
event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
|
||||||
toggleSidebar();
|
(event.metaKey || event.ctrlKey)
|
||||||
|
) {
|
||||||
|
event.preventDefault()
|
||||||
|
toggleSidebar()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
window.addEventListener("keydown", handleKeyDown)
|
||||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
return () => window.removeEventListener("keydown", handleKeyDown)
|
||||||
}, [toggleSidebar]);
|
}, [toggleSidebar])
|
||||||
|
|
||||||
// We add a state so that we can do data-state="expanded" or "collapsed".
|
// We add a state so that we can do data-state="expanded" or "collapsed".
|
||||||
// This makes it easier to style the sidebar with Tailwind classes.
|
// This makes it easier to style the sidebar with Tailwind classes.
|
||||||
const state = open ? "expanded" : "collapsed";
|
const state = open ? "expanded" : "collapsed"
|
||||||
|
|
||||||
const contextValue = React.useMemo<SidebarContext>(
|
const contextValue = React.useMemo<SidebarContext>(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -103,7 +122,7 @@ const SidebarProvider = React.forwardRef<
|
|||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
}),
|
}),
|
||||||
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
|
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarContext.Provider value={contextValue}>
|
<SidebarContext.Provider value={contextValue}>
|
||||||
@@ -121,16 +140,14 @@ const SidebarProvider = React.forwardRef<
|
|||||||
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
|
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</SidebarContext.Provider>
|
</SidebarContext.Provider>
|
||||||
);
|
)
|
||||||
});
|
}
|
||||||
SidebarProvider.displayName = "SidebarProvider";
|
|
||||||
|
|
||||||
function Sidebar({
|
function Sidebar({
|
||||||
side = "left",
|
side = "left",
|
||||||
@@ -140,22 +157,25 @@ function Sidebar({
|
|||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"div"> & {
|
}: React.ComponentProps<"div"> & {
|
||||||
side?: "left" | "right";
|
side?: "left" | "right"
|
||||||
variant?: "sidebar" | "floating" | "inset";
|
variant?: "sidebar" | "floating" | "inset"
|
||||||
collapsible?: "offcanvas" | "icon" | "none";
|
collapsible?: "offcanvas" | "icon" | "none"
|
||||||
}) {
|
}) {
|
||||||
const {isMobile, state, openMobile, setOpenMobile} = useSidebar();
|
const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
|
||||||
|
|
||||||
if (collapsible === "none") {
|
if (collapsible === "none") {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="sidebar"
|
data-slot="sidebar"
|
||||||
className={cn("bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col", className)}
|
className={cn(
|
||||||
|
"bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col",
|
||||||
|
className
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
@@ -180,7 +200,7 @@ function Sidebar({
|
|||||||
<div className="flex h-full w-full flex-col">{children}</div>
|
<div className="flex h-full w-full flex-col">{children}</div>
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -225,11 +245,15 @@ function Sidebar({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarTrigger({className, onClick, ...props}: React.ComponentProps<typeof Button>) {
|
function SidebarTrigger({
|
||||||
const {toggleSidebar} = useSidebar();
|
className,
|
||||||
|
onClick,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof Button>) {
|
||||||
|
const { toggleSidebar } = useSidebar()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
@@ -239,19 +263,19 @@ function SidebarTrigger({className, onClick, ...props}: React.ComponentProps<typ
|
|||||||
size="icon"
|
size="icon"
|
||||||
className={cn("h-7 w-7", className)}
|
className={cn("h-7 w-7", className)}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
onClick?.(event);
|
onClick?.(event)
|
||||||
toggleSidebar();
|
toggleSidebar()
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<PanelLeftIcon />
|
<PanelLeftIcon />
|
||||||
<span className="sr-only">Toggle Sidebar</span>
|
<span className="sr-only">Toggle Sidebar</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
||||||
const {toggleSidebar} = useSidebar();
|
const { toggleSidebar } = useSidebar()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
@@ -272,7 +296,7 @@ function SidebarRail({className, ...props}: React.ComponentProps<"button">) {
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
|
function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
|
||||||
@@ -286,10 +310,13 @@ function SidebarInset({className, ...props}: React.ComponentProps<"main">) {
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarInput({className, ...props}: React.ComponentProps<typeof Input>) {
|
function SidebarInput({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof Input>) {
|
||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
data-slot="sidebar-input"
|
data-slot="sidebar-input"
|
||||||
@@ -297,7 +324,7 @@ function SidebarInput({className, ...props}: React.ComponentProps<typeof Input>)
|
|||||||
className={cn("bg-background h-8 w-full shadow-none", className)}
|
className={cn("bg-background h-8 w-full shadow-none", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
|
function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
@@ -308,7 +335,7 @@ function SidebarHeader({className, ...props}: React.ComponentProps<"div">) {
|
|||||||
className={cn("flex flex-col gap-2 p-2", className)}
|
className={cn("flex flex-col gap-2 p-2", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
|
function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
@@ -319,10 +346,13 @@ function SidebarFooter({className, ...props}: React.ComponentProps<"div">) {
|
|||||||
className={cn("flex flex-col gap-2 p-2", className)}
|
className={cn("flex flex-col gap-2 p-2", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarSeparator({className, ...props}: React.ComponentProps<typeof Separator>) {
|
function SidebarSeparator({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof Separator>) {
|
||||||
return (
|
return (
|
||||||
<Separator
|
<Separator
|
||||||
data-slot="sidebar-separator"
|
data-slot="sidebar-separator"
|
||||||
@@ -330,7 +360,7 @@ function SidebarSeparator({className, ...props}: React.ComponentProps<typeof Sep
|
|||||||
className={cn("bg-sidebar-border mx-2 w-auto", className)}
|
className={cn("bg-sidebar-border mx-2 w-auto", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
|
function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
@@ -344,7 +374,7 @@ function SidebarContent({className, ...props}: React.ComponentProps<"div">) {
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
@@ -355,24 +385,28 @@ function SidebarGroup({className, ...props}: React.ComponentProps<"div">) {
|
|||||||
className={cn("relative flex w-full min-w-0 flex-col p-2", className)}
|
className={cn("relative flex w-full min-w-0 flex-col p-2", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarGroupLabel({className, asChild = false, ...props}: React.ComponentProps<"div"> & {asChild?: boolean}) {
|
function SidebarGroupLabel({
|
||||||
const Comp = asChild ? Slot : "div";
|
className,
|
||||||
|
asChild = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & { asChild?: boolean }) {
|
||||||
|
const Comp = asChild ? Slot : "div"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
data-slot="sidebar-group-label"
|
data-slot="sidebar-group-label"
|
||||||
data-sidebar="group-label"
|
data-sidebar="group-label"
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opa] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||||
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
|
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarGroupAction({
|
function SidebarGroupAction({
|
||||||
@@ -380,7 +414,7 @@ function SidebarGroupAction({
|
|||||||
asChild = false,
|
asChild = false,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"button"> & { asChild?: boolean }) {
|
}: React.ComponentProps<"button"> & { asChild?: boolean }) {
|
||||||
const Comp = asChild ? Slot : "button";
|
const Comp = asChild ? Slot : "button"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
@@ -395,10 +429,13 @@ function SidebarGroupAction({
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarGroupContent({className, ...props}: React.ComponentProps<"div">) {
|
function SidebarGroupContent({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="sidebar-group-content"
|
data-slot="sidebar-group-content"
|
||||||
@@ -406,7 +443,7 @@ function SidebarGroupContent({className, ...props}: React.ComponentProps<"div">)
|
|||||||
className={cn("w-full text-sm", className)}
|
className={cn("w-full text-sm", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
|
function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
|
||||||
@@ -417,7 +454,7 @@ function SidebarMenu({className, ...props}: React.ComponentProps<"ul">) {
|
|||||||
className={cn("flex w-full min-w-0 flex-col gap-1", className)}
|
className={cn("flex w-full min-w-0 flex-col gap-1", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
|
function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
|
||||||
@@ -428,7 +465,7 @@ function SidebarMenuItem({className, ...props}: React.ComponentProps<"li">) {
|
|||||||
className={cn("group/menu-item relative", className)}
|
className={cn("group/menu-item relative", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sidebarMenuButtonVariants = cva(
|
const sidebarMenuButtonVariants = cva(
|
||||||
@@ -451,7 +488,7 @@ const sidebarMenuButtonVariants = cva(
|
|||||||
size: "default",
|
size: "default",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
function SidebarMenuButton({
|
function SidebarMenuButton({
|
||||||
asChild = false,
|
asChild = false,
|
||||||
@@ -462,12 +499,12 @@ function SidebarMenuButton({
|
|||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"button"> & {
|
}: React.ComponentProps<"button"> & {
|
||||||
asChild?: boolean;
|
asChild?: boolean
|
||||||
isActive?: boolean;
|
isActive?: boolean
|
||||||
tooltip?: string | React.ComponentProps<typeof TooltipContent>;
|
tooltip?: string | React.ComponentProps<typeof TooltipContent>
|
||||||
} & VariantProps<typeof sidebarMenuButtonVariants>) {
|
} & VariantProps<typeof sidebarMenuButtonVariants>) {
|
||||||
const Comp = asChild ? Slot : "button";
|
const Comp = asChild ? Slot : "button"
|
||||||
const {isMobile, state} = useSidebar();
|
const { isMobile, state } = useSidebar()
|
||||||
|
|
||||||
const button = (
|
const button = (
|
||||||
<Comp
|
<Comp
|
||||||
@@ -478,24 +515,29 @@ function SidebarMenuButton({
|
|||||||
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
|
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
|
|
||||||
if (!tooltip) {
|
if (!tooltip) {
|
||||||
return button;
|
return button
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof tooltip === "string") {
|
if (typeof tooltip === "string") {
|
||||||
tooltip = {
|
tooltip = {
|
||||||
children: tooltip,
|
children: tooltip,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>{button}</TooltipTrigger>
|
<TooltipTrigger asChild>{button}</TooltipTrigger>
|
||||||
<TooltipContent side="right" align="center" hidden={state !== "collapsed" || isMobile} {...tooltip} />
|
<TooltipContent
|
||||||
|
side="right"
|
||||||
|
align="center"
|
||||||
|
hidden={state !== "collapsed" || isMobile}
|
||||||
|
{...tooltip}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarMenuAction({
|
function SidebarMenuAction({
|
||||||
@@ -504,10 +546,10 @@ function SidebarMenuAction({
|
|||||||
showOnHover = false,
|
showOnHover = false,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"button"> & {
|
}: React.ComponentProps<"button"> & {
|
||||||
asChild?: boolean;
|
asChild?: boolean
|
||||||
showOnHover?: boolean;
|
showOnHover?: boolean
|
||||||
}) {
|
}) {
|
||||||
const Comp = asChild ? Slot : "button";
|
const Comp = asChild ? Slot : "button"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
@@ -527,10 +569,13 @@ function SidebarMenuAction({
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarMenuBadge({className, ...props}: React.ComponentProps<"div">) {
|
function SidebarMenuBadge({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="sidebar-menu-badge"
|
data-slot="sidebar-menu-badge"
|
||||||
@@ -546,7 +591,7 @@ function SidebarMenuBadge({className, ...props}: React.ComponentProps<"div">) {
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarMenuSkeleton({
|
function SidebarMenuSkeleton({
|
||||||
@@ -554,12 +599,12 @@ function SidebarMenuSkeleton({
|
|||||||
showIcon = false,
|
showIcon = false,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"div"> & {
|
}: React.ComponentProps<"div"> & {
|
||||||
showIcon?: boolean;
|
showIcon?: boolean
|
||||||
}) {
|
}) {
|
||||||
// Random width between 50 to 90%.
|
// Random width between 50 to 90%.
|
||||||
const width = React.useMemo(() => {
|
const width = React.useMemo(() => {
|
||||||
return `${Math.floor(Math.random() * 40) + 50}%`;
|
return `${Math.floor(Math.random() * 40) + 50}%`
|
||||||
}, []);
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -568,7 +613,12 @@ function SidebarMenuSkeleton({
|
|||||||
className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
|
className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{showIcon && <Skeleton className="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />}
|
{showIcon && (
|
||||||
|
<Skeleton
|
||||||
|
className="size-4 rounded-md"
|
||||||
|
data-sidebar="menu-skeleton-icon"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Skeleton
|
<Skeleton
|
||||||
className="h-4 max-w-(--skeleton-width) flex-1"
|
className="h-4 max-w-(--skeleton-width) flex-1"
|
||||||
data-sidebar="menu-skeleton-text"
|
data-sidebar="menu-skeleton-text"
|
||||||
@@ -579,7 +629,7 @@ function SidebarMenuSkeleton({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
|
function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
|
||||||
@@ -594,10 +644,13 @@ function SidebarMenuSub({className, ...props}: React.ComponentProps<"ul">) {
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarMenuSubItem({className, ...props}: React.ComponentProps<"li">) {
|
function SidebarMenuSubItem({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"li">) {
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
data-slot="sidebar-menu-sub-item"
|
data-slot="sidebar-menu-sub-item"
|
||||||
@@ -605,7 +658,7 @@ function SidebarMenuSubItem({className, ...props}: React.ComponentProps<"li">) {
|
|||||||
className={cn("group/menu-sub-item relative", className)}
|
className={cn("group/menu-sub-item relative", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarMenuSubButton({
|
function SidebarMenuSubButton({
|
||||||
@@ -615,11 +668,11 @@ function SidebarMenuSubButton({
|
|||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"a"> & {
|
}: React.ComponentProps<"a"> & {
|
||||||
asChild?: boolean;
|
asChild?: boolean
|
||||||
size?: "sm" | "md";
|
size?: "sm" | "md"
|
||||||
isActive?: boolean;
|
isActive?: boolean
|
||||||
}) {
|
}) {
|
||||||
const Comp = asChild ? Slot : "a";
|
const Comp = asChild ? Slot : "a"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
@@ -637,7 +690,7 @@ function SidebarMenuSubButton({
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -665,4 +718,4 @@ export {
|
|||||||
SidebarSeparator,
|
SidebarSeparator,
|
||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
useSidebar,
|
useSidebar,
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import {cn} from "../../lib/utils";
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
return <div data-slot="skeleton" className={cn("bg-primary/10 animate-pulse rounded-md", className)} {...props} />;
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="skeleton"
|
||||||
|
className={cn("bg-primary/10 animate-pulse rounded-md", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Skeleton};
|
export { Skeleton }
|
||||||
|
|||||||
@@ -1,29 +1,40 @@
|
|||||||
"use client";
|
import * as React from "react"
|
||||||
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||||
|
|
||||||
import * as React from "react";
|
import { cn } from "@/lib/utils"
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
||||||
|
|
||||||
import {cn} from "../../lib/utils";
|
function TooltipProvider({
|
||||||
|
delayDuration = 0,
|
||||||
function TooltipProvider({delayDuration = 0, ...props}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
...props
|
||||||
return <TooltipPrimitive.Provider data-slot="tooltip-provider" delayDuration={delayDuration} {...props} />;
|
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
||||||
|
return (
|
||||||
|
<TooltipPrimitive.Provider
|
||||||
|
data-slot="tooltip-provider"
|
||||||
|
delayDuration={delayDuration}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Tooltip({...props}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
function Tooltip({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
||||||
return (
|
return (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function TooltipTrigger({...props}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
function TooltipTrigger({
|
||||||
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
...props
|
||||||
|
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
||||||
|
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
function TooltipContent({
|
function TooltipContent({
|
||||||
className,
|
className,
|
||||||
sideOffset = 4,
|
sideOffset = 0,
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
||||||
@@ -33,7 +44,7 @@ function TooltipContent({
|
|||||||
data-slot="tooltip-content"
|
data-slot="tooltip-content"
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-w-sm rounded-md px-3 py-1.5 text-xs",
|
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit rounded-md px-3 py-1.5 text-xs text-balance",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -42,7 +53,7 @@ function TooltipContent({
|
|||||||
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
||||||
</TooltipPrimitive.Content>
|
</TooltipPrimitive.Content>
|
||||||
</TooltipPrimitive.Portal>
|
</TooltipPrimitive.Portal>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Tooltip, TooltipTrigger, TooltipContent, TooltipProvider};
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { clsx, type ClassValue } from "clsx"
|
import {clsx, type ClassValue} from "clsx";
|
||||||
import { twMerge } from "tailwind-merge"
|
import {twMerge} from "tailwind-merge";
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,74 +5,74 @@
|
|||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: hsl(0 0% 100%);
|
--background: oklch(1 0 0);
|
||||||
--foreground: hsl(0 0% 3.9%);
|
--foreground: oklch(0.145 0 0);
|
||||||
--card: hsl(0 0% 100%);
|
--card: oklch(1 0 0);
|
||||||
--card-foreground: hsl(0 0% 3.9%);
|
--card-foreground: oklch(0.145 0 0);
|
||||||
--popover: hsl(0 0% 100%);
|
--popover: oklch(1 0 0);
|
||||||
--popover-foreground: hsl(0 0% 3.9%);
|
--popover-foreground: oklch(0.145 0 0);
|
||||||
--primary: hsl(0 0% 9%);
|
--primary: oklch(0.205 0 0);
|
||||||
--primary-foreground: hsl(0 0% 98%);
|
--primary-foreground: oklch(0.985 0 0);
|
||||||
--secondary: hsl(0 0% 96.1%);
|
--secondary: oklch(0.97 0 0);
|
||||||
--secondary-foreground: hsl(0 0% 9%);
|
--secondary-foreground: oklch(0.205 0 0);
|
||||||
--muted: hsl(0 0% 96.1%);
|
--muted: oklch(0.97 0 0);
|
||||||
--muted-foreground: hsl(0 0% 45.1%);
|
--muted-foreground: oklch(0.556 0 0);
|
||||||
--accent: hsl(0 0% 96.1%);
|
--accent: oklch(0.97 0 0);
|
||||||
--accent-foreground: hsl(0 0% 9%);
|
--accent-foreground: oklch(0.205 0 0);
|
||||||
--destructive: hsl(0 84.2% 60.2%);
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
--destructive-foreground: hsl(0 0% 98%);
|
--destructive-foreground: oklch(0.577 0.245 27.325);
|
||||||
--border: hsl(0 0% 89.8%);
|
--border: oklch(0.922 0 0);
|
||||||
--input: hsl(0 0% 89.8%);
|
--input: oklch(0.922 0 0);
|
||||||
--ring: hsl(0 0% 3.9%);
|
--ring: oklch(0.87 0 0);
|
||||||
--chart-1: hsl(12 76% 61%);
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
--chart-2: hsl(173 58% 39%);
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
--chart-3: hsl(197 37% 24%);
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
--chart-4: hsl(43 74% 66%);
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
--chart-5: hsl(27 87% 67%);
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
--radius: 0.6rem;
|
--radius: 0.625rem;
|
||||||
--sidebar: hsl(0 0% 98%);
|
--sidebar: oklch(0.985 0 0);
|
||||||
--sidebar-foreground: hsl(240 5.3% 26.1%);
|
--sidebar-foreground: oklch(0.145 0 0);
|
||||||
--sidebar-primary: hsl(240 5.9% 10%);
|
--sidebar-primary: oklch(0.205 0 0);
|
||||||
--sidebar-primary-foreground: hsl(0 0% 98%);
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-accent: hsl(240 4.8% 95.9%);
|
--sidebar-accent: oklch(0.97 0 0);
|
||||||
--sidebar-accent-foreground: hsl(240 5.9% 10%);
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||||
--sidebar-border: hsl(220 13% 91%);
|
--sidebar-border: oklch(0.922 0 0);
|
||||||
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
--sidebar-ring: oklch(0.87 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: hsl(0 0% 3.9%);
|
--background: oklch(0.145 0 0);
|
||||||
--foreground: hsl(0 0% 98%);
|
--foreground: oklch(0.985 0 0);
|
||||||
--card: hsl(0 0% 3.9%);
|
--card: oklch(0.145 0 0);
|
||||||
--card-foreground: hsl(0 0% 98%);
|
--card-foreground: oklch(0.985 0 0);
|
||||||
--popover: hsl(0 0% 3.9%);
|
--popover: oklch(0.145 0 0);
|
||||||
--popover-foreground: hsl(0 0% 98%);
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
--primary: hsl(0 0% 98%);
|
--primary: oklch(0.985 0 0);
|
||||||
--primary-foreground: hsl(0 0% 9%);
|
--primary-foreground: oklch(0.205 0 0);
|
||||||
--secondary: hsl(0 0% 14.9%);
|
--secondary: oklch(0.269 0 0);
|
||||||
--secondary-foreground: hsl(0 0% 98%);
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
--muted: hsl(0 0% 14.9%);
|
--muted: oklch(0.269 0 0);
|
||||||
--muted-foreground: hsl(0 0% 63.9%);
|
--muted-foreground: oklch(0.708 0 0);
|
||||||
--accent: hsl(0 0% 14.9%);
|
--accent: oklch(0.269 0 0);
|
||||||
--accent-foreground: hsl(0 0% 98%);
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
--destructive: hsl(0 62.8% 30.6%);
|
--destructive: oklch(0.396 0.141 25.723);
|
||||||
--destructive-foreground: hsl(0 0% 98%);
|
--destructive-foreground: oklch(0.637 0.237 25.331);
|
||||||
--border: hsl(0 0% 14.9%);
|
--border: oklch(0.269 0 0);
|
||||||
--input: hsl(0 0% 14.9%);
|
--input: oklch(0.269 0 0);
|
||||||
--ring: hsl(0 0% 83.1%);
|
--ring: oklch(0.439 0 0);
|
||||||
--chart-1: hsl(220 70% 50%);
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
--chart-2: hsl(160 60% 45%);
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
--chart-3: hsl(30 80% 55%);
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
--chart-4: hsl(280 65% 60%);
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
--chart-5: hsl(340 75% 55%);
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
--sidebar: hsl(240 5.9% 10%);
|
--sidebar: oklch(0.205 0 0);
|
||||||
--sidebar-foreground: hsl(240 4.8% 95.9%);
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-primary: hsl(224.3 76.3% 48%);
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
--sidebar-primary-foreground: hsl(0 0% 100%);
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-accent: hsl(240 3.7% 15.9%);
|
--sidebar-accent: oklch(0.269 0 0);
|
||||||
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-border: hsl(240 3.7% 15.9%);
|
--sidebar-border: oklch(0.269 0 0);
|
||||||
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
--sidebar-ring: oklch(0.439 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
@@ -104,14 +104,14 @@
|
|||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
||||||
--color-sidebar-accent: var(--sidebar-accent);
|
|
||||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
||||||
--color-sidebar: var(--sidebar);
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
|||||||
@@ -20,7 +20,11 @@
|
|||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUncheckedSideEffectImports": true
|
"noUncheckedSideEffectImports": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"files": [],
|
"files": [],
|
||||||
"references": [{"path": "./tsconfig.app.json"}, {"path": "./tsconfig.node.json"}],
|
"references": [{"path": "./tsconfig.app.json"}, {"path": "./tsconfig.node.json"}],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
// "baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,6 @@ import path from "path";
|
|||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react(), tailwindcss(), TanStackRouterVite({autoCodeSplitting: true})],
|
plugins: [react(), tailwindcss(), TanStackRouterVite({autoCodeSplitting: true})],
|
||||||
// build: {
|
|
||||||
// outDir: path.resolve(__dirname, "../../dist/frontend/dist"),
|
|
||||||
// emptyOutDir: true,
|
|
||||||
// },
|
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(__dirname, "./src"),
|
"@": path.resolve(__dirname, "./src"),
|
||||||
@@ -18,7 +14,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api": {target: `http://localhost:4000`, changeOrigin: true},
|
"/api": {target: `http://localhost:4400`, changeOrigin: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
10
globals.d.ts
vendored
10
globals.d.ts
vendored
@@ -1,10 +0,0 @@
|
|||||||
declare global {
|
|
||||||
namespace NodeJS {
|
|
||||||
interface ProcessEnv {
|
|
||||||
JWT_SECRET: string;
|
|
||||||
JWT_EXPIRES: string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {};
|
|
||||||
10
load-env.ts
10
load-env.ts
@@ -1,10 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
const dotenv = require("dotenv");
|
|
||||||
const dotenvExpand = require("dotenv-expand");
|
|
||||||
|
|
||||||
// Load the root .env file
|
|
||||||
const envPath = path.resolve(__dirname, ".env");
|
|
||||||
const envConfig = dotenv.config({path: envPath});
|
|
||||||
|
|
||||||
// Expand variables (e.g., `${VAR}`) in the .env file
|
|
||||||
dotenvExpand.expand(envConfig);
|
|
||||||
6574
package-lock.json
generated
Normal file
6574
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
54
package.json
54
package.json
@@ -1,52 +1,40 @@
|
|||||||
{
|
{
|
||||||
"name": "lstv2",
|
"name": "fullstack-app",
|
||||||
"version": "1.2.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"type": "module",
|
||||||
"main": "index.ts",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently -n 'server,frontend' -c '#007755,#2f6da3' 'bun --watch server/index.ts' 'cd frontend && bunx --bun vite'",
|
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
|
||||||
"dev:server": "bun --watch server/index.ts",
|
"dev:server": "dotenvx run -f .env -- tsx watch server/src/index.ts",
|
||||||
"dev:ocme": "bun --watch ocme/index.ts",
|
"dev:frontend": "cd frontend && npm run dev",
|
||||||
"dev:front": "cd frontend && bunx --bun vite",
|
"build": "npm run build:server && npm run build:frontend",
|
||||||
"build:server": "cd apps/server && bun build index.js --outdir ../../dist/server",
|
"build:server": "cd server && npm run build",
|
||||||
"build:ocme": "rimraf dist/ocme && cd apps/ocme && bun build index.js --outdir ../../dist/ocme",
|
"build:frontend": "cd frontend && npm run build",
|
||||||
"build:front": "cd frontend && rimraf frontend/dist && bun run build",
|
"start": "npm run start:server",
|
||||||
"start": "bun --env-file .env server/index.js",
|
"start:server": "cd server && npm start",
|
||||||
"commit": "cz",
|
"db:generate": "npx drizzle-kit generate",
|
||||||
"clean": "rimraf dist/server",
|
"db:migrate": "npx drizzle-kit push",
|
||||||
"deploy": "standard-version --conventional-commits",
|
"deploy": "standard-version --conventional-commits",
|
||||||
"ui:add": "cd frontend && bun shadcn add ",
|
"commit": "cz"
|
||||||
"db:dev": "bun drizzle-kit generate && bun drizzle-kit migrate"
|
|
||||||
},
|
},
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dotenvx/dotenvx": "^1.35.0",
|
"@dotenvx/dotenvx": "^1.38.3",
|
||||||
"@hono/zod-openapi": "^0.18.4",
|
"hono": "^4.7.2",
|
||||||
"@scalar/hono-api-reference": "^0.5.175",
|
|
||||||
"@types/bun": "^1.2.2",
|
|
||||||
"@types/jsonwebtoken": "^9.0.8",
|
|
||||||
"axios": "^1.7.9",
|
|
||||||
"bcrypt": "^5.1.1",
|
|
||||||
"compression": "^1.8.0",
|
|
||||||
"cookie": "^1.0.2",
|
|
||||||
"date-fns": "^4.1.0",
|
|
||||||
"drizzle-orm": "^0.39.3",
|
"drizzle-orm": "^0.39.3",
|
||||||
"drizzle-zod": "^0.7.0",
|
"drizzle-zod": "^0.7.0",
|
||||||
"hono": "^4.7.1",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
|
||||||
"pg": "^8.13.3",
|
"pg": "^8.13.3",
|
||||||
"postgres": "^3.4.5",
|
"postgres": "^3.4.5",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.13.5",
|
||||||
|
"concurrently": "^8.2.0",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"drizzle-kit": "^0.30.4",
|
||||||
|
"tsx": "^4.7.1",
|
||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/pg": "^8.11.11",
|
"@types/pg": "^8.11.11",
|
||||||
"concurrently": "^9.1.2",
|
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
"drizzle-kit": "^0.30.4",
|
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"standard-version": "^9.5.0",
|
"standard-version": "^9.5.0",
|
||||||
"typescript": "~5.7.3"
|
"typescript": "~5.7.3"
|
||||||
|
|||||||
28
server/.gitignore
vendored
Normal file
28
server/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# dev
|
||||||
|
.yarn/
|
||||||
|
!.yarn/releases
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
.idea/workspace.xml
|
||||||
|
.idea/usage.statistics.xml
|
||||||
|
.idea/shelf
|
||||||
|
|
||||||
|
# deps
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# env
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
8
server/README.md
Normal file
8
server/README.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
```
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
open http://localhost:3000
|
||||||
|
```
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
CREATE TABLE "users" (
|
|
||||||
"user_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
||||||
"username" text NOT NULL,
|
|
||||||
"email" text NOT NULL,
|
|
||||||
"password" text NOT NULL,
|
|
||||||
"passwordToken" text,
|
|
||||||
"passwordTokenExpires" timestamp,
|
|
||||||
"active" boolean DEFAULT true NOT NULL,
|
|
||||||
"pingcode" numeric,
|
|
||||||
"lastLogin" timestamp DEFAULT now(),
|
|
||||||
"add_User" text DEFAULT 'LST_System' NOT NULL,
|
|
||||||
"add_Date" timestamp DEFAULT now(),
|
|
||||||
"upd_User" text DEFAULT 'LST_System' NOT NULL,
|
|
||||||
"upd_date" timestamp DEFAULT now()
|
|
||||||
);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
CREATE UNIQUE INDEX "username" ON "users" USING btree ("username");
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "userRoles" RENAME COLUMN "roles" TO "role";--> statement-breakpoint
|
|
||||||
ALTER TABLE "modules" ADD COLUMN "roles" text NOT NULL;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE "modules" ALTER COLUMN "roles" SET DEFAULT '["view", "systemAdmin"]';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE "users" ADD COLUMN "role" text DEFAULT 'user' NOT NULL;
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "d1b4a8a6-caa3-4c45-a3a1-cdfc99ca7bea",
|
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "postgresql",
|
|
||||||
"tables": {
|
|
||||||
"public.users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"name": "email",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"password": {
|
|
||||||
"name": "password",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"passwordToken": {
|
|
||||||
"name": "passwordToken",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"passwordTokenExpires": {
|
|
||||||
"name": "passwordTokenExpires",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"active": {
|
|
||||||
"name": "active",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"pingcode": {
|
|
||||||
"name": "pingcode",
|
|
||||||
"type": "numeric",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"lastLogin": {
|
|
||||||
"name": "lastLogin",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {},
|
|
||||||
"schemas": {},
|
|
||||||
"sequences": {},
|
|
||||||
"roles": {},
|
|
||||||
"policies": {},
|
|
||||||
"views": {},
|
|
||||||
"_meta": {
|
|
||||||
"columns": {},
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "d6c99236-0eea-49f1-817b-13139c0f42f5",
|
|
||||||
"prevId": "d1b4a8a6-caa3-4c45-a3a1-cdfc99ca7bea",
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "postgresql",
|
|
||||||
"tables": {
|
|
||||||
"public.users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"name": "email",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"password": {
|
|
||||||
"name": "password",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"passwordToken": {
|
|
||||||
"name": "passwordToken",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"passwordTokenExpires": {
|
|
||||||
"name": "passwordTokenExpires",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"active": {
|
|
||||||
"name": "active",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"pingcode": {
|
|
||||||
"name": "pingcode",
|
|
||||||
"type": "numeric",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"lastLogin": {
|
|
||||||
"name": "lastLogin",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "username",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {},
|
|
||||||
"schemas": {},
|
|
||||||
"sequences": {},
|
|
||||||
"roles": {},
|
|
||||||
"policies": {},
|
|
||||||
"views": {},
|
|
||||||
"_meta": {
|
|
||||||
"columns": {},
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,407 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "9a2ddf11-fd30-4dd9-bf2d-259fe7aed201",
|
|
||||||
"prevId": "d6c99236-0eea-49f1-817b-13139c0f42f5",
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "postgresql",
|
|
||||||
"tables": {
|
|
||||||
"public.modules": {
|
|
||||||
"name": "modules",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"module_id": {
|
|
||||||
"name": "module_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"active": {
|
|
||||||
"name": "active",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"module_name": {
|
|
||||||
"name": "module_name",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "name",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
},
|
|
||||||
"public.roles": {
|
|
||||||
"name": "roles",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"role_id": {
|
|
||||||
"name": "role_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"role_name": {
|
|
||||||
"name": "role_name",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "name",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
},
|
|
||||||
"public.userRoles": {
|
|
||||||
"name": "userRoles",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"role_id": {
|
|
||||||
"name": "role_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"module_id": {
|
|
||||||
"name": "module_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"roles": {
|
|
||||||
"name": "roles",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"user_module_unique": {
|
|
||||||
"name": "user_module_unique",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "user_id",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expression": "module_id",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {
|
|
||||||
"userRoles_user_id_users_user_id_fk": {
|
|
||||||
"name": "userRoles_user_id_users_user_id_fk",
|
|
||||||
"tableFrom": "userRoles",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"userRoles_role_id_roles_role_id_fk": {
|
|
||||||
"name": "userRoles_role_id_roles_role_id_fk",
|
|
||||||
"tableFrom": "userRoles",
|
|
||||||
"tableTo": "roles",
|
|
||||||
"columnsFrom": [
|
|
||||||
"role_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"role_id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"userRoles_module_id_modules_module_id_fk": {
|
|
||||||
"name": "userRoles_module_id_modules_module_id_fk",
|
|
||||||
"tableFrom": "userRoles",
|
|
||||||
"tableTo": "modules",
|
|
||||||
"columnsFrom": [
|
|
||||||
"module_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"module_id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
},
|
|
||||||
"public.users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"name": "email",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"password": {
|
|
||||||
"name": "password",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"passwordToken": {
|
|
||||||
"name": "passwordToken",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"passwordTokenExpires": {
|
|
||||||
"name": "passwordTokenExpires",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"active": {
|
|
||||||
"name": "active",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"pingcode": {
|
|
||||||
"name": "pingcode",
|
|
||||||
"type": "numeric",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"lastLogin": {
|
|
||||||
"name": "lastLogin",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "username",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {},
|
|
||||||
"schemas": {},
|
|
||||||
"sequences": {},
|
|
||||||
"roles": {},
|
|
||||||
"policies": {},
|
|
||||||
"views": {},
|
|
||||||
"_meta": {
|
|
||||||
"columns": {},
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,413 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "ad424668-721e-414f-ad0b-a8e7301e50c6",
|
|
||||||
"prevId": "9a2ddf11-fd30-4dd9-bf2d-259fe7aed201",
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "postgresql",
|
|
||||||
"tables": {
|
|
||||||
"public.modules": {
|
|
||||||
"name": "modules",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"module_id": {
|
|
||||||
"name": "module_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"active": {
|
|
||||||
"name": "active",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"roles": {
|
|
||||||
"name": "roles",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"module_name": {
|
|
||||||
"name": "module_name",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "name",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
},
|
|
||||||
"public.roles": {
|
|
||||||
"name": "roles",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"role_id": {
|
|
||||||
"name": "role_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"role_name": {
|
|
||||||
"name": "role_name",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "name",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
},
|
|
||||||
"public.userRoles": {
|
|
||||||
"name": "userRoles",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"role_id": {
|
|
||||||
"name": "role_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"module_id": {
|
|
||||||
"name": "module_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"role": {
|
|
||||||
"name": "role",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"user_module_unique": {
|
|
||||||
"name": "user_module_unique",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "user_id",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expression": "module_id",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {
|
|
||||||
"userRoles_user_id_users_user_id_fk": {
|
|
||||||
"name": "userRoles_user_id_users_user_id_fk",
|
|
||||||
"tableFrom": "userRoles",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"userRoles_role_id_roles_role_id_fk": {
|
|
||||||
"name": "userRoles_role_id_roles_role_id_fk",
|
|
||||||
"tableFrom": "userRoles",
|
|
||||||
"tableTo": "roles",
|
|
||||||
"columnsFrom": [
|
|
||||||
"role_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"role_id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"userRoles_module_id_modules_module_id_fk": {
|
|
||||||
"name": "userRoles_module_id_modules_module_id_fk",
|
|
||||||
"tableFrom": "userRoles",
|
|
||||||
"tableTo": "modules",
|
|
||||||
"columnsFrom": [
|
|
||||||
"module_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"module_id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
},
|
|
||||||
"public.users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"name": "email",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"password": {
|
|
||||||
"name": "password",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"passwordToken": {
|
|
||||||
"name": "passwordToken",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"passwordTokenExpires": {
|
|
||||||
"name": "passwordTokenExpires",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"active": {
|
|
||||||
"name": "active",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"pingcode": {
|
|
||||||
"name": "pingcode",
|
|
||||||
"type": "numeric",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"lastLogin": {
|
|
||||||
"name": "lastLogin",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "username",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {},
|
|
||||||
"schemas": {},
|
|
||||||
"sequences": {},
|
|
||||||
"roles": {},
|
|
||||||
"policies": {},
|
|
||||||
"views": {},
|
|
||||||
"_meta": {
|
|
||||||
"columns": {},
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,414 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "313590a8-2068-45b5-96fc-cfa5d2b32b56",
|
|
||||||
"prevId": "ad424668-721e-414f-ad0b-a8e7301e50c6",
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "postgresql",
|
|
||||||
"tables": {
|
|
||||||
"public.modules": {
|
|
||||||
"name": "modules",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"module_id": {
|
|
||||||
"name": "module_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"active": {
|
|
||||||
"name": "active",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
"roles": {
|
|
||||||
"name": "roles",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'[\"view\", \"systemAdmin\"]'"
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"module_name": {
|
|
||||||
"name": "module_name",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "name",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
},
|
|
||||||
"public.roles": {
|
|
||||||
"name": "roles",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"role_id": {
|
|
||||||
"name": "role_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"name": "name",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"role_name": {
|
|
||||||
"name": "role_name",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "name",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
},
|
|
||||||
"public.userRoles": {
|
|
||||||
"name": "userRoles",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"role_id": {
|
|
||||||
"name": "role_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"module_id": {
|
|
||||||
"name": "module_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"role": {
|
|
||||||
"name": "role",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"user_module_unique": {
|
|
||||||
"name": "user_module_unique",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "user_id",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expression": "module_id",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {
|
|
||||||
"userRoles_user_id_users_user_id_fk": {
|
|
||||||
"name": "userRoles_user_id_users_user_id_fk",
|
|
||||||
"tableFrom": "userRoles",
|
|
||||||
"tableTo": "users",
|
|
||||||
"columnsFrom": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"userRoles_role_id_roles_role_id_fk": {
|
|
||||||
"name": "userRoles_role_id_roles_role_id_fk",
|
|
||||||
"tableFrom": "userRoles",
|
|
||||||
"tableTo": "roles",
|
|
||||||
"columnsFrom": [
|
|
||||||
"role_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"role_id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
},
|
|
||||||
"userRoles_module_id_modules_module_id_fk": {
|
|
||||||
"name": "userRoles_module_id_modules_module_id_fk",
|
|
||||||
"tableFrom": "userRoles",
|
|
||||||
"tableTo": "modules",
|
|
||||||
"columnsFrom": [
|
|
||||||
"module_id"
|
|
||||||
],
|
|
||||||
"columnsTo": [
|
|
||||||
"module_id"
|
|
||||||
],
|
|
||||||
"onDelete": "no action",
|
|
||||||
"onUpdate": "no action"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
},
|
|
||||||
"public.users": {
|
|
||||||
"name": "users",
|
|
||||||
"schema": "",
|
|
||||||
"columns": {
|
|
||||||
"user_id": {
|
|
||||||
"name": "user_id",
|
|
||||||
"type": "uuid",
|
|
||||||
"primaryKey": true,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "gen_random_uuid()"
|
|
||||||
},
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"name": "email",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"password": {
|
|
||||||
"name": "password",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true
|
|
||||||
},
|
|
||||||
"passwordToken": {
|
|
||||||
"name": "passwordToken",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"passwordTokenExpires": {
|
|
||||||
"name": "passwordTokenExpires",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"active": {
|
|
||||||
"name": "active",
|
|
||||||
"type": "boolean",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"pingcode": {
|
|
||||||
"name": "pingcode",
|
|
||||||
"type": "numeric",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
"lastLogin": {
|
|
||||||
"name": "lastLogin",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"add_User": {
|
|
||||||
"name": "add_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"add_Date": {
|
|
||||||
"name": "add_Date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
},
|
|
||||||
"upd_User": {
|
|
||||||
"name": "upd_User",
|
|
||||||
"type": "text",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": true,
|
|
||||||
"default": "'LST_System'"
|
|
||||||
},
|
|
||||||
"upd_date": {
|
|
||||||
"name": "upd_date",
|
|
||||||
"type": "timestamp",
|
|
||||||
"primaryKey": false,
|
|
||||||
"notNull": false,
|
|
||||||
"default": "now()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"indexes": {
|
|
||||||
"username": {
|
|
||||||
"name": "username",
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"expression": "username",
|
|
||||||
"isExpression": false,
|
|
||||||
"asc": true,
|
|
||||||
"nulls": "last"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isUnique": true,
|
|
||||||
"concurrently": false,
|
|
||||||
"method": "btree",
|
|
||||||
"with": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"foreignKeys": {},
|
|
||||||
"compositePrimaryKeys": {},
|
|
||||||
"uniqueConstraints": {},
|
|
||||||
"policies": {},
|
|
||||||
"checkConstraints": {},
|
|
||||||
"isRLSEnabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enums": {},
|
|
||||||
"schemas": {},
|
|
||||||
"sequences": {},
|
|
||||||
"roles": {},
|
|
||||||
"policies": {},
|
|
||||||
"views": {},
|
|
||||||
"_meta": {
|
|
||||||
"columns": {},
|
|
||||||
"schemas": {},
|
|
||||||
"tables": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "7",
|
|
||||||
"dialect": "postgresql",
|
|
||||||
"entries": [
|
|
||||||
{
|
|
||||||
"idx": 0,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1740160921910,
|
|
||||||
"tag": "0000_typical_frightful_four",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 1,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1740161259149,
|
|
||||||
"tag": "0001_sharp_pet_avengers",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 2,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1740361491818,
|
|
||||||
"tag": "0002_bitter_oracle",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 3,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1740362541964,
|
|
||||||
"tag": "0003_luxuriant_namorita",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 4,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1740364483791,
|
|
||||||
"tag": "0004_quick_mandrill",
|
|
||||||
"breakpoints": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 5,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1740445275919,
|
|
||||||
"tag": "0005_tough_emma_frost",
|
|
||||||
"breakpoints": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
10
server/drizzle.config.js
Normal file
10
server/drizzle.config.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
const database = process.env.DATABASE_URL || "";
|
||||||
|
export default defineConfig({
|
||||||
|
dialect: "postgresql",
|
||||||
|
schema: "database/schema",
|
||||||
|
out: "database/migrations",
|
||||||
|
dbCredentials: {
|
||||||
|
url: database,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import app from "./src/app";
|
|
||||||
const port = process.env.SERVER_PORT || 4000;
|
|
||||||
Bun.serve({
|
|
||||||
port,
|
|
||||||
fetch: app.fetch,
|
|
||||||
hostname: "0.0.0.0",
|
|
||||||
});
|
|
||||||
|
|
||||||
// await Bun.build({
|
|
||||||
// entrypoints: ["./index.js"],
|
|
||||||
// outdir: "../../dist/server",
|
|
||||||
// });
|
|
||||||
|
|
||||||
console.log(`server is running on port ${port}`);
|
|
||||||
2123
server/package-lock.json
generated
Normal file
2123
server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
server/package.json
Normal file
31
server/package.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "server",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "dotenvx run -f ../.env -- tsx watch src/index.ts",
|
||||||
|
"build": "rimraf dist && tsc --build",
|
||||||
|
"start": "dotenvx run node dist/server/src/index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hono/node-server": "^1.13.8",
|
||||||
|
"@dotenvx/dotenvx": "^1.35.0",
|
||||||
|
"@hono/zod-openapi": "^0.18.4",
|
||||||
|
"@scalar/hono-api-reference": "^0.5.175",
|
||||||
|
"@types/jsonwebtoken": "^9.0.8",
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
|
"compression": "^1.8.0",
|
||||||
|
"cookie": "^1.0.2",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
|
"drizzle-orm": "^0.39.3",
|
||||||
|
"drizzle-zod": "^0.7.0",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"pg": "^8.13.3",
|
||||||
|
"postgres": "^3.4.5",
|
||||||
|
"zod": "^3.24.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "~5.7.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
||||||
import {serveStatic} from "hono/bun";
|
|
||||||
import {logger} from "hono/logger";
|
|
||||||
import {cors} from "hono/cors";
|
|
||||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
|
||||||
|
|
||||||
//routes
|
|
||||||
import auth from "./services/auth/authService";
|
|
||||||
import scalar from "./services/general/route/scalar";
|
|
||||||
import apiHits from "./services/general/route/apitHits";
|
|
||||||
import system from "./services/system/systemServer";
|
|
||||||
|
|
||||||
const app = new OpenAPIHono();
|
|
||||||
|
|
||||||
app.use("*", logger());
|
|
||||||
|
|
||||||
const allowedOrigins = ["http://localhost:3000", "http://localhost:4000", "http://localhost:5173"];
|
|
||||||
|
|
||||||
app.use(
|
|
||||||
"*",
|
|
||||||
cors({
|
|
||||||
origin: allowedOrigins,
|
|
||||||
allowHeaders: ["X-Custom-Header", "Upgrade-Insecure-Requests"],
|
|
||||||
allowMethods: ["POST", "GET", "OPTIONS"],
|
|
||||||
exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
|
|
||||||
maxAge: 600,
|
|
||||||
credentials: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
app.doc("/api", {
|
|
||||||
openapi: "3.0.0",
|
|
||||||
info: {
|
|
||||||
version: "1.0.0",
|
|
||||||
title: "LST API",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// as we dont want to change ocme again well use a proxy to this
|
|
||||||
// app.all("/ocme/*", async (c) => {
|
|
||||||
// return ocmeService(c);
|
|
||||||
// });
|
|
||||||
|
|
||||||
const routes = [scalar, auth, apiHits, system] as const;
|
|
||||||
|
|
||||||
routes.forEach((route) => {
|
|
||||||
app.route("/api/", route);
|
|
||||||
});
|
|
||||||
|
|
||||||
//app.basePath("/api/auth").route("/login", login).route("/session", session).route("/register", register);
|
|
||||||
|
|
||||||
//auth stuff
|
|
||||||
// app.get("/api/protected", authMiddleware, (c) => {
|
|
||||||
// return c.json({success: true, message: "is authenticated"});
|
|
||||||
// });
|
|
||||||
|
|
||||||
app.get("*", serveStatic({root: "./frontend/dist"}));
|
|
||||||
app.get("*", serveStatic({path: "./frontend/dist/index.html"}));
|
|
||||||
|
|
||||||
export default app;
|
|
||||||
|
|
||||||
//export type ApiRoute = typeof apiRoute;
|
|
||||||
26
server/src/globalUtils/apiHits.d.ts
vendored
Normal file
26
server/src/globalUtils/apiHits.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import type { Context } from "hono";
|
||||||
|
import { z } from "zod";
|
||||||
|
declare const requestSchema: z.ZodObject<{
|
||||||
|
ip: z.ZodOptional<z.ZodString>;
|
||||||
|
endpoint: z.ZodString;
|
||||||
|
action: z.ZodOptional<z.ZodString>;
|
||||||
|
stats: z.ZodOptional<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
ip?: string;
|
||||||
|
endpoint?: string;
|
||||||
|
action?: string;
|
||||||
|
stats?: string;
|
||||||
|
}, {
|
||||||
|
ip?: string;
|
||||||
|
endpoint?: string;
|
||||||
|
action?: string;
|
||||||
|
stats?: string;
|
||||||
|
}>;
|
||||||
|
type ApiHitData = z.infer<typeof requestSchema>;
|
||||||
|
export declare const apiHit: (c: Context, data: unknown) => Promise<{
|
||||||
|
success: boolean;
|
||||||
|
data?: ApiHitData;
|
||||||
|
errors?: any[];
|
||||||
|
}>;
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=apiHits.d.ts.map
|
||||||
1
server/src/globalUtils/apiHits.d.ts.map
Normal file
1
server/src/globalUtils/apiHits.d.ts.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"apiHits.d.ts","sourceRoot":"","sources":["apiHits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,MAAM,CAAC;AAClC,OAAO,EAAC,CAAC,EAAW,MAAM,KAAK,CAAC;AAGhC,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;EAKjB,CAAC;AAEH,KAAK,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEhD,eAAO,MAAM,MAAM,MACZ,OAAO,QACJ,OAAO,KACd,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;CAAC,CAuB/D,CAAC"}
|
||||||
20
server/src/globalUtils/apiHits.js
Normal file
20
server/src/globalUtils/apiHits.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { z, ZodError } from "zod";
|
||||||
|
const requestSchema = z.object({
|
||||||
|
ip: z.string().optional(),
|
||||||
|
endpoint: z.string(),
|
||||||
|
action: z.string().optional(),
|
||||||
|
stats: z.string().optional(),
|
||||||
|
});
|
||||||
|
export const apiHit = async (c, data) => {
|
||||||
|
try {
|
||||||
|
const forwarded = c.req.header("host");
|
||||||
|
const validatedData = requestSchema.parse(data);
|
||||||
|
return { success: true, data: validatedData };
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (error instanceof ZodError) {
|
||||||
|
return { success: false, errors: error.errors };
|
||||||
|
}
|
||||||
|
return { success: false, errors: [{ message: "An unknown error occurred" }] };
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
|
import type {Context} from "hono";
|
||||||
import {z, ZodError} from "zod";
|
import {z, ZodError} from "zod";
|
||||||
import {Context} from "hono";
|
|
||||||
// Define the request body schema
|
// Define the request body schema
|
||||||
const requestSchema = z.object({
|
const requestSchema = z.object({
|
||||||
ip: z.string().optional(),
|
ip: z.string().optional(),
|
||||||
71
server/src/index.ts
Normal file
71
server/src/index.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import {serve} from "@hono/node-server";
|
||||||
|
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||||
|
import {serveStatic} from "@hono/node-server/serve-static";
|
||||||
|
import {logger} from "hono/logger";
|
||||||
|
import {cors} from "hono/cors";
|
||||||
|
|
||||||
|
import {db} from "../../database/dbclient.js";
|
||||||
|
import {modules} from "../../database/schema/modules.js";
|
||||||
|
|
||||||
|
// custom routes
|
||||||
|
import scalar from "./services/general/route/scalar.js";
|
||||||
|
import system from "./services/server/systemServer.js";
|
||||||
|
import auth from "./services/auth/authService.js";
|
||||||
|
|
||||||
|
const allowedOrigins = ["http://localhost:3000", "http://localhost:4000", "http://localhost:5173"];
|
||||||
|
const app = new OpenAPIHono();
|
||||||
|
|
||||||
|
// middle ware
|
||||||
|
app.use("*", logger());
|
||||||
|
app.use(
|
||||||
|
"*",
|
||||||
|
cors({
|
||||||
|
origin: allowedOrigins,
|
||||||
|
allowHeaders: ["X-Custom-Header", "Upgrade-Insecure-Requests"],
|
||||||
|
allowMethods: ["POST", "GET", "OPTIONS"],
|
||||||
|
exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
|
||||||
|
maxAge: 600,
|
||||||
|
credentials: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
app.doc("/api/ref", {
|
||||||
|
openapi: "3.0.0",
|
||||||
|
info: {
|
||||||
|
version: "1.0.0",
|
||||||
|
title: "LST API",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
scalar,
|
||||||
|
auth,
|
||||||
|
// apiHits,
|
||||||
|
system,
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
routes.forEach((route) => {
|
||||||
|
app.route("/api/", route);
|
||||||
|
});
|
||||||
|
|
||||||
|
// the catch all api route
|
||||||
|
app.all("/api/*", (c) => c.json({error: "API route not found"}, 404));
|
||||||
|
|
||||||
|
app.all("/ocme/*", async (c) => {
|
||||||
|
//return ocmeService(c);
|
||||||
|
c.json({error: "Ocme route not found"}, 404);
|
||||||
|
});
|
||||||
|
|
||||||
|
// front end static files
|
||||||
|
app.get("*", serveStatic({root: "../frontend/dist"}));
|
||||||
|
app.get("*", serveStatic({path: "../frontend/dist/index.html"}));
|
||||||
|
|
||||||
|
serve(
|
||||||
|
{
|
||||||
|
fetch: app.fetch,
|
||||||
|
port: Number(process.env.SERVER_PORT),
|
||||||
|
},
|
||||||
|
(info) => {
|
||||||
|
console.log(`Server is running on http://localhost:${info.port}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||||
|
|
||||||
import login from "./routes/login";
|
import login from "./routes/login.js";
|
||||||
import register from "./routes/register";
|
import register from "./routes/register.js";
|
||||||
import session from "./routes/session";
|
import session from "./routes/session.js";
|
||||||
|
|
||||||
const app = new OpenAPIHono();
|
const app = new OpenAPIHono();
|
||||||
app.route("auth/login", login);
|
app.route("auth/login", login);
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ in the login route we attach it to user under roles.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {eq} from "drizzle-orm";
|
import {eq} from "drizzle-orm";
|
||||||
import {db} from "../../../../database/dbClient";
|
import {db} from "../../../../../database/dbclient.js";
|
||||||
import {userRoles} from "../../../../database/schema/userRoles";
|
import {userRoles} from "../../../../../database/schema/userRoles.js";
|
||||||
|
|
||||||
export const roleCheck = async (user_id: any) => {
|
export const roleCheck = async (user_id: any) => {
|
||||||
// get the user roles by the user_id
|
// get the user roles by the user_id
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import {sign, verify} from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import {db} from "../../../../database/dbClient";
|
import {db} from "../../../../../database/dbclient.js";
|
||||||
import {users} from "../../../../database/schema/users";
|
import {users} from "../../../../../database/schema/users.js";
|
||||||
import {eq} from "drizzle-orm";
|
import {eq, sql} from "drizzle-orm";
|
||||||
import {checkPassword} from "../utils/checkPassword";
|
import {checkPassword} from "../utils/checkPassword.js";
|
||||||
import {roleCheck} from "./getUserAccess";
|
import {roleCheck} from "./getUserAccess.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticate a user and return a JWT.
|
* Authenticate a user and return a JWT.
|
||||||
*/
|
*/
|
||||||
|
const {sign, verify} = jwt;
|
||||||
|
|
||||||
export async function login(
|
export async function login(
|
||||||
username: string,
|
username: string,
|
||||||
@@ -15,7 +16,7 @@ export async function login(
|
|||||||
): Promise<{token: string; user: {user_id: string; username: string}}> {
|
): Promise<{token: string; user: {user_id: string; username: string}}> {
|
||||||
const user = await db.select().from(users).where(eq(users.username, username));
|
const user = await db.select().from(users).where(eq(users.username, username));
|
||||||
|
|
||||||
console.log(user);
|
//console.log(user);
|
||||||
if (user.length === 0) {
|
if (user.length === 0) {
|
||||||
throw new Error("Invalid or Missing user");
|
throw new Error("Invalid or Missing user");
|
||||||
}
|
}
|
||||||
@@ -27,7 +28,7 @@ export async function login(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a JWT
|
// Create a JWT
|
||||||
const secret: string = process.env.JWT_SECRET! || "bnghsjhsd";
|
const secret: string = process.env.JWT_SECRET!;
|
||||||
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
|
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
|
||||||
|
|
||||||
// get the user roles
|
// get the user roles
|
||||||
@@ -39,6 +40,15 @@ export async function login(
|
|||||||
roles: roles || null,
|
roles: roles || null,
|
||||||
role: user[0].role || null, // this should be removed onces full migration to v2 is completed
|
role: user[0].role || null, // this should be removed onces full migration to v2 is completed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// update the user last login
|
||||||
|
// try {
|
||||||
|
// db.update(users)
|
||||||
|
// .set({lastLogin: sql`NOW()`})
|
||||||
|
// .where(eq(users.user_id, user[0].user_id));
|
||||||
|
// } catch (e) {
|
||||||
|
// console.log(e);
|
||||||
|
// }
|
||||||
const token = sign({user: userData}, secret, {expiresIn: expiresIn * 60});
|
const token = sign({user: userData}, secret, {expiresIn: expiresIn * 60});
|
||||||
|
|
||||||
return {token, user: userData};
|
return {token, user: userData};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const authMiddleware: MiddlewareHandler = async (c, next) => {
|
|||||||
const token = authHeader.split(" ")[1];
|
const token = authHeader.split(" ")[1];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const decoded = verify(token, process.env.JWT_SECRET, {ignoreExpiration: false}) as {
|
const decoded = verify(token, process.env.JWT_SECRET!, {ignoreExpiration: false}) as {
|
||||||
userId: number;
|
userId: number;
|
||||||
exp: number;
|
exp: number;
|
||||||
};
|
};
|
||||||
@@ -22,8 +22,10 @@ export const authMiddleware: MiddlewareHandler = async (c, next) => {
|
|||||||
// If the token has less than REFRESH_THRESHOLD seconds left, refresh it
|
// If the token has less than REFRESH_THRESHOLD seconds left, refresh it
|
||||||
let newToken = null;
|
let newToken = null;
|
||||||
|
|
||||||
if (timeLeft < parseInt(process.env.REFRESH_THRESHOLD)) {
|
if (timeLeft < parseInt(process.env.REFRESH_THRESHOLD!)) {
|
||||||
newToken = sign({userId: decoded.userId}, process.env.JWT_SECRET, {expiresIn: process.env.EXPIRATION_TIME});
|
newToken = sign({userId: decoded.userId}, process.env.JWT_SECRET!, {
|
||||||
|
expiresIn: parseInt(process.env.EXPIRATION_TIME!),
|
||||||
|
});
|
||||||
c.res.headers.set("Authorization", `Bearer ${newToken}`);
|
c.res.headers.set("Authorization", `Bearer ${newToken}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||||
import {login} from "../controllers/login";
|
import {login} from "../controllers/login.js";
|
||||||
|
|
||||||
const app = new OpenAPIHono();
|
const app = new OpenAPIHono();
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||||
import {db} from "../../../../database/dbClient";
|
import {db} from "../../../../../database/dbclient.js";
|
||||||
import {users} from "../../../../database/schema/users";
|
import {users} from "../../../../../database/schema/users.js";
|
||||||
import {apiHit} from "../../../globalUtils/apitHits";
|
import {apiHit} from "../../../globalUtils/apiHits.js";
|
||||||
import {createPassword} from "../utils/createPassword";
|
import {createPassword} from "../utils/createPassword.js";
|
||||||
import {eq} from "drizzle-orm";
|
import {eq} from "drizzle-orm";
|
||||||
|
|
||||||
const app = new OpenAPIHono();
|
const app = new OpenAPIHono();
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
|
||||||
|
|
||||||
const app = new OpenAPIHono();
|
|
||||||
// the doc endpoint
|
|
||||||
app.doc("/", {
|
|
||||||
openapi: "3.0.0",
|
|
||||||
info: {
|
|
||||||
version: "1.0.0",
|
|
||||||
title: "LST API",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default app;
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||||
import {apiHit} from "../../../globalUtils/apitHits";
|
|
||||||
|
|
||||||
const app = new OpenAPIHono();
|
const app = new OpenAPIHono();
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ app.get(
|
|||||||
"undici",
|
"undici",
|
||||||
],
|
],
|
||||||
spec: {
|
spec: {
|
||||||
url: "/api",
|
url: "/api/ref",
|
||||||
},
|
},
|
||||||
baseServerURL: "https://scalar.com",
|
baseServerURL: "https://scalar.com",
|
||||||
servers: [
|
servers: [
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import { Context } from "hono";
|
|
||||||
export const ocmeService = async (c: Context) => {
|
|
||||||
const url = new URL(c.req.url);
|
|
||||||
|
|
||||||
const ocmeUrl = `http://localhost:${
|
|
||||||
process.env.OCME_PORT
|
|
||||||
}${url.pathname.replace("/ocme", "")}`;
|
|
||||||
|
|
||||||
console.log(ocmeUrl);
|
|
||||||
const ocmeResponse = await fetch(ocmeUrl, {
|
|
||||||
method: c.req.method,
|
|
||||||
headers: c.req.raw.headers,
|
|
||||||
body:
|
|
||||||
c.req.method !== "GET" && c.req.method !== "HEAD"
|
|
||||||
? await c.req.text()
|
|
||||||
: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Response(ocmeResponse.body, {
|
|
||||||
status: ocmeResponse.status,
|
|
||||||
headers: ocmeResponse.headers,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
19
server/src/services/ocme/ocmeService.ts
Normal file
19
server/src/services/ocme/ocmeService.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type {Context} from "hono";
|
||||||
|
|
||||||
|
export const ocmeService = async (c: Context) => {
|
||||||
|
const url = new URL(c.req.url);
|
||||||
|
|
||||||
|
const ocmeUrl = `http://localhost:${process.env.OCME_PORT}${url.pathname.replace("/ocme", "")}`;
|
||||||
|
|
||||||
|
console.log(ocmeUrl);
|
||||||
|
const ocmeResponse = await fetch(ocmeUrl, {
|
||||||
|
method: c.req.method,
|
||||||
|
headers: c.req.raw.headers,
|
||||||
|
body: c.req.method !== "GET" && c.req.method !== "HEAD" ? await c.req.text() : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(ocmeResponse.body, {
|
||||||
|
status: ocmeResponse.status,
|
||||||
|
headers: ocmeResponse.headers,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||||
import {modules} from "../../../../database/schema/modules";
|
import {modules} from "../../../../../database/schema/modules.js";
|
||||||
import {db} from "../../../../database/dbClient";
|
import {db} from "../../../../../database/dbclient.js";
|
||||||
import {eq} from "drizzle-orm";
|
import {eq} from "drizzle-orm";
|
||||||
|
|
||||||
// Define the request body schema
|
// Define the request body schema
|
||||||
@@ -27,7 +27,7 @@ app.openapi(
|
|||||||
tags: ["server"],
|
tags: ["server"],
|
||||||
summary: "Returns all modules in the server",
|
summary: "Returns all modules in the server",
|
||||||
method: "get",
|
method: "get",
|
||||||
path: "/server/modules",
|
path: "/",
|
||||||
responses: {
|
responses: {
|
||||||
200: {
|
200: {
|
||||||
content: {
|
content: {
|
||||||
@@ -38,6 +38,7 @@ app.openapi(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
|
//console.log("system modules");
|
||||||
let module: any = [];
|
let module: any = [];
|
||||||
try {
|
try {
|
||||||
module = await db.select().from(modules).where(eq(modules.active, true));
|
module = await db.select().from(modules).where(eq(modules.active, true));
|
||||||
7
server/src/services/server/systemServer.ts
Normal file
7
server/src/services/server/systemServer.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||||
|
|
||||||
|
import modules from "./route/modules.js";
|
||||||
|
|
||||||
|
const app = new OpenAPIHono().route("server/modules", modules);
|
||||||
|
|
||||||
|
export default app;
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
|
||||||
|
|
||||||
import modules from "./route/modules";
|
|
||||||
|
|
||||||
const app = new OpenAPIHono().route("system/module", modules);
|
|
||||||
|
|
||||||
export default app;
|
|
||||||
16
server/tsconfig.json
Normal file
16
server/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"strict": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"types": ["node"],
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "hono/jsx",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"removeComments": true
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"lib": ["ESNext"],
|
|
||||||
"module": "esnext",
|
|
||||||
"target": "esnext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"composite": true,
|
|
||||||
"strict": true,
|
|
||||||
"downlevelIteration": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"removeComments": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"allowJs": true,
|
|
||||||
"types": [
|
|
||||||
"bun-types" // add Bun global
|
|
||||||
],
|
|
||||||
"outDir": "dist"
|
|
||||||
},
|
|
||||||
"include": ["./server/src", "./server/index.ts", "./server/database"]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user