refactor(lst): refactor to monolithic completed
This commit is contained in:
175
server/database/.gitignore
vendored
Normal file
175
server/database/.gitignore
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||
|
||||
# Logs
|
||||
|
||||
logs
|
||||
_.log
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Caches
|
||||
|
||||
.cache
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# Runtime data
|
||||
|
||||
pids
|
||||
_.pid
|
||||
_.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
15
server/database/README.md
Normal file
15
server/database/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# database
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.2.2. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
||||
8
server/database/drizzle.config.ts
Normal file
8
server/database/drizzle.config.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import {defineConfig} from "drizzle-kit";
|
||||
export default defineConfig({
|
||||
dialect: "postgresql", // 'mysql' | 'sqlite' | 'turso'
|
||||
schema: "./schema",
|
||||
dbCredentials: {
|
||||
url: "postgresql://postgres:nova0511@localhost:5432/lst_db",
|
||||
},
|
||||
});
|
||||
12
server/database/drizzle/0000_stormy_thunderbolt.sql
Normal file
12
server/database/drizzle/0000_stormy_thunderbolt.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE "users" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"title" text NOT NULL,
|
||||
"passwordToken" text NOT NULL,
|
||||
"passwordTokenExpires" timestamp,
|
||||
"active" boolean DEFAULT true NOT NULL,
|
||||
"pingcode" numeric,
|
||||
"add_Date" timestamp DEFAULT now(),
|
||||
"add_User" text DEFAULT 'LST_System' NOT NULL,
|
||||
"upd_date" timestamp DEFAULT now()
|
||||
);
|
||||
96
server/database/drizzle/meta/0000_snapshot.json
Normal file
96
server/database/drizzle/meta/0000_snapshot.json
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"id": "d0e2effa-c6ac-4f81-b546-ef6b10037eca",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.users": {
|
||||
"name": "users",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"passwordToken": {
|
||||
"name": "passwordToken",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"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
|
||||
},
|
||||
"add_Date": {
|
||||
"name": "add_Date",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"add_User": {
|
||||
"name": "add_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": {}
|
||||
}
|
||||
}
|
||||
13
server/database/drizzle/meta/_journal.json
Normal file
13
server/database/drizzle/meta/_journal.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1739914245651,
|
||||
"tag": "0000_stormy_thunderbolt",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
7
server/database/index.ts
Normal file
7
server/database/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {drizzle} from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
import "../../load-env";
|
||||
|
||||
const queryClient = postgres("postgresql://postgres:nova0511@localhost:5432/lst_db");
|
||||
|
||||
export const db = drizzle(queryClient);
|
||||
20
server/database/package.json
Normal file
20
server/database/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "database",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/pg": "^8.11.11",
|
||||
"drizzle-kit": "^0.30.4",
|
||||
"tsx": "^4.19.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"drizzle-orm": "^0.39.3",
|
||||
"drizzle-zod": "^0.7.0",
|
||||
"pg": "^8.13.3",
|
||||
"postgres": "^3.4.5"
|
||||
}
|
||||
}
|
||||
27
server/database/schema/users.ts
Normal file
27
server/database/schema/users.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {text, pgTable, serial, numeric, index, timestamp, boolean} from "drizzle-orm/pg-core";
|
||||
import {createInsertSchema, createSelectSchema} from "drizzle-zod";
|
||||
import {z} from "zod";
|
||||
|
||||
export const users = pgTable("users", {
|
||||
user_id: serial("id").primaryKey(),
|
||||
username: text("user_id").notNull(),
|
||||
email: text("title").notNull(),
|
||||
passwordToken: text("passwordToken").notNull(),
|
||||
passwordTokenExpires: timestamp("passwordTokenExpires"),
|
||||
acitve: boolean("active").default(true).notNull(),
|
||||
pinCode: numeric("pingcode"),
|
||||
lastLogin: timestamp("add_Date").defaultNow(),
|
||||
add_User: text("add_User").default("LST_System").notNull(),
|
||||
add_Date: timestamp("add_Date").defaultNow(),
|
||||
upd_user: text("add_User").default("LST_System").notNull(),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
});
|
||||
|
||||
// Schema for inserting a user - can be used to validate API requests
|
||||
export const insertUsersSchema = createInsertSchema(users, {
|
||||
username: z.string().min(3, {message: "Username must be at least 3 characters"}),
|
||||
email: z.string().email({message: "Invalid email"}),
|
||||
passwordToken: z.string().min(8, {message: "Password must be at least 8 characters"}),
|
||||
});
|
||||
// Schema for selecting a Expenses - can be used to validate API responses
|
||||
export const selectExpensesSchema = createSelectSchema(users);
|
||||
27
server/database/tsconfig.json
Normal file
27
server/database/tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
14
server/index.ts
Normal file
14
server/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
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}`);
|
||||
16
server/package.json
Normal file
16
server/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "lstv2-server",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "bun --env-file ../.env --watch ./index.ts",
|
||||
"build": "bun build ./index.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@scalar/hono-api-reference": "^0.5.174"
|
||||
}
|
||||
}
|
||||
60
server/src/app.ts
Normal file
60
server/src/app.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
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 "./route/scalar";
|
||||
// services
|
||||
import {ocmeService} from "./services/ocme/ocmeServer";
|
||||
|
||||
console.log(process.env.JWT_SECRET);
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
app.use("*", logger());
|
||||
app.use(
|
||||
"*",
|
||||
cors({
|
||||
origin: `http://localhost:5173`,
|
||||
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] as const;
|
||||
|
||||
routes.forEach((route) => {
|
||||
app.route("/", 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;
|
||||
13
server/src/route/apiDoc.ts
Normal file
13
server/src/route/apiDoc.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
// the doc endpoint
|
||||
app.doc("/api", {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
version: "1.0.0",
|
||||
title: "LST API",
|
||||
},
|
||||
});
|
||||
|
||||
export default app;
|
||||
79
server/src/route/scalar.ts
Normal file
79
server/src/route/scalar.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {apiReference} from "@scalar/hono-api-reference";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
app.get(
|
||||
"/api/docs",
|
||||
apiReference({
|
||||
theme: "kepler",
|
||||
layout: "classic",
|
||||
defaultHttpClient: {targetKey: "node", clientKey: "axios"},
|
||||
pageTitle: "Lst API Reference",
|
||||
hiddenClients: [
|
||||
"libcurl",
|
||||
"clj_http",
|
||||
"httpclient",
|
||||
"restsharp",
|
||||
"native",
|
||||
"http1.1",
|
||||
"asynchttp",
|
||||
"nethttp",
|
||||
"okhttp",
|
||||
"unirest",
|
||||
"xhr",
|
||||
"fetch",
|
||||
"jquery",
|
||||
"okhttp",
|
||||
"native",
|
||||
"request",
|
||||
"unirest",
|
||||
"nsurlsession",
|
||||
"cohttp",
|
||||
"curl",
|
||||
"guzzle",
|
||||
"http1",
|
||||
"http2",
|
||||
"webrequest",
|
||||
"restmethod",
|
||||
"python3",
|
||||
"requests",
|
||||
"httr",
|
||||
"native",
|
||||
"curl",
|
||||
"httpie",
|
||||
"wget",
|
||||
"nsurlsession",
|
||||
"undici",
|
||||
],
|
||||
spec: {
|
||||
url: "/api",
|
||||
},
|
||||
baseServerURL: "https://scalar.com",
|
||||
servers: [
|
||||
{
|
||||
url: "http://usday1vms006:3000",
|
||||
description: "Production",
|
||||
},
|
||||
{
|
||||
url: "http://localhost:4000",
|
||||
description: "dev server",
|
||||
},
|
||||
],
|
||||
// authentication: {
|
||||
// preferredSecurityScheme: {'bearerAuth'},
|
||||
// },
|
||||
|
||||
// metaData: {
|
||||
// title: "Page title",
|
||||
// description: "My page page",
|
||||
// ogDescription: "Still about my my page",
|
||||
// ogTitle: "Page title",
|
||||
// ogImage: "https://example.com/image.png",
|
||||
// twitterCard: "summary_large_image",
|
||||
// // Add more...
|
||||
// },
|
||||
})
|
||||
);
|
||||
|
||||
export default app;
|
||||
12
server/src/services/auth/authService.ts
Normal file
12
server/src/services/auth/authService.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {OpenAPIHono} from "@hono/zod-openapi";
|
||||
|
||||
import login from "./routes/login";
|
||||
import register from "./routes/register";
|
||||
import session from "./routes/session";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
app.route("api/auth/login", login);
|
||||
app.route("api/auth//register", register);
|
||||
app.route("api/auth/session", session);
|
||||
|
||||
export default app;
|
||||
25
server/src/services/auth/controllers/login.ts
Normal file
25
server/src/services/auth/controllers/login.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import {sign, verify} from "jsonwebtoken";
|
||||
|
||||
/**
|
||||
* Authenticate a user and return a JWT.
|
||||
*/
|
||||
|
||||
const fakeUsers = [
|
||||
{id: 1, username: "admin", password: "password123"},
|
||||
{id: 2, username: "user", password: "password123"},
|
||||
{id: 3, username: "user2", password: "password123"},
|
||||
];
|
||||
|
||||
export function login(username: string, password: string): {token: string; user: {id: number; username: string}} {
|
||||
const user = fakeUsers.find((u) => u.username === username && u.password === password);
|
||||
if (!user) {
|
||||
throw new Error("Invalid credentials");
|
||||
}
|
||||
|
||||
// Create a JWT
|
||||
const token = sign({userId: user?.id, username: user?.username}, process.env.JWT_SECRET, {
|
||||
expiresIn: process.env.JWT_EXPIRES,
|
||||
});
|
||||
|
||||
return {token, user: {id: user?.id, username: user.username}};
|
||||
}
|
||||
8
server/src/services/auth/controllers/logout.ts
Normal file
8
server/src/services/auth/controllers/logout.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Logout (clear the token).
|
||||
* This is a placeholder function since JWTs are stateless.
|
||||
* In a real app, you might want to implement token blacklisting.
|
||||
*/
|
||||
export function logout(): {message: string} {
|
||||
return {message: "Logout successful"};
|
||||
}
|
||||
1
server/src/services/auth/controllers/register.ts
Normal file
1
server/src/services/auth/controllers/register.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const registerUser = async () => {};
|
||||
13
server/src/services/auth/controllers/verifyToken.ts
Normal file
13
server/src/services/auth/controllers/verifyToken.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {sign, verify} from "jsonwebtoken";
|
||||
|
||||
/**
|
||||
* Verify a JWT and return the decoded payload.
|
||||
*/
|
||||
export function verifyToken(token: string): {userId: number} {
|
||||
try {
|
||||
const payload = verify(token, process.env.JWT_SECRET) as {userId: number};
|
||||
return payload;
|
||||
} catch (err) {
|
||||
throw new Error("Invalid token");
|
||||
}
|
||||
}
|
||||
17
server/src/services/auth/lib/createPassword.ts
Normal file
17
server/src/services/auth/lib/createPassword.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import bcrypt from "bcrypt";
|
||||
|
||||
export const passwordUpdate = (password: string) => {
|
||||
// encypt password
|
||||
let pass: string = process.env.SECRET;
|
||||
let salt: string = process.env.SALTING;
|
||||
|
||||
if (!pass || !salt) {
|
||||
pass = "error";
|
||||
} else {
|
||||
pass = bcrypt.hashSync(process.env.SECRET + password, parseInt(process.env.SALTING));
|
||||
|
||||
pass = btoa(pass);
|
||||
}
|
||||
|
||||
return pass;
|
||||
};
|
||||
41
server/src/services/auth/middleware/authMiddleware.ts
Normal file
41
server/src/services/auth/middleware/authMiddleware.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {type MiddlewareHandler} from "hono";
|
||||
import {sign, verify} from "jsonwebtoken";
|
||||
|
||||
export const authMiddleware: MiddlewareHandler = async (c, next) => {
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return c.json({error: "Unauthorized"}, 401);
|
||||
}
|
||||
|
||||
const token = authHeader.split(" ")[1];
|
||||
|
||||
try {
|
||||
const decoded = verify(token, process.env.JWT_SECRET, {ignoreExpiration: false}) as {
|
||||
userId: number;
|
||||
exp: number;
|
||||
};
|
||||
|
||||
const currentTime = Math.floor(Date.now() / 1000); // Get current timestamp
|
||||
const timeLeft = decoded.exp - currentTime;
|
||||
|
||||
// If the token has less than REFRESH_THRESHOLD seconds left, refresh it
|
||||
let newToken = null;
|
||||
|
||||
if (timeLeft < parseInt(process.env.REFRESH_THRESHOLD)) {
|
||||
newToken = sign({userId: decoded.userId}, process.env.JWT_SECRET, {expiresIn: process.env.EXPIRATION_TIME});
|
||||
c.res.headers.set("Authorization", `Bearer ${newToken}`);
|
||||
}
|
||||
|
||||
c.set("user", {id: decoded.userId});
|
||||
await next();
|
||||
|
||||
// If a new token was generated, send it in response headers
|
||||
if (newToken) {
|
||||
console.log("token was refreshed");
|
||||
c.res.headers.set("X-Refreshed-Token", newToken);
|
||||
}
|
||||
} catch (err) {
|
||||
return c.json({error: "Invalid token"}, 401);
|
||||
}
|
||||
};
|
||||
112
server/src/services/auth/routes/login.ts
Normal file
112
server/src/services/auth/routes/login.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {login} from "../controllers/login";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UserSchema = z
|
||||
.object({
|
||||
username: z.string().min(3).openapi({example: "smith002"}),
|
||||
password: z.string().openapi({example: "password123"}),
|
||||
})
|
||||
.openapi("User");
|
||||
|
||||
// Define the response schema for the login endpoint
|
||||
const LoginResponseSchema = z
|
||||
.object({
|
||||
message: z.string().openapi({example: "Login successful"}),
|
||||
user: z.object({
|
||||
username: z.string().openapi({example: "smith002"}),
|
||||
// Add other user fields as needed
|
||||
}),
|
||||
})
|
||||
.openapi("LoginResponse");
|
||||
|
||||
const route = createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Login as user",
|
||||
description: "Login as a user to get a JWT token",
|
||||
method: "post",
|
||||
path: "/",
|
||||
request: {body: {content: {"application/json": {schema: UserSchema}}}},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: LoginResponseSchema,
|
||||
},
|
||||
},
|
||||
description: "Login successful",
|
||||
},
|
||||
400: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
success: z.boolean().openapi({example: false}),
|
||||
message: z.string().openapi({example: "Username and password required"}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Bad request",
|
||||
},
|
||||
401: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string().openapi({example: "Invalid credentials"}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Unauthorized",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.openapi(route, async (c) => {
|
||||
let body: {username: string; password: string};
|
||||
try {
|
||||
body = await c.req.json();
|
||||
} catch (error) {
|
||||
return c.json({success: false, message: "Username and password required"}, 400);
|
||||
}
|
||||
|
||||
if (!body?.username || !body?.password) {
|
||||
return c.json({success: false, message: "Username and password required"}, 400);
|
||||
}
|
||||
try {
|
||||
const {token, user} = login(body.username, body.password);
|
||||
|
||||
// Set the JWT as an HTTP-only cookie
|
||||
// c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Path=/; SameSite=None; Max-Age=3600`);
|
||||
|
||||
return c.json({message: "Login successful", data: {token, user}});
|
||||
} catch (err) {
|
||||
return c.json({message: err instanceof Error ? err.message : "Invalid credentials"}, 401);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
let body = {username: "", password: "", error: ""};
|
||||
try {
|
||||
body = await c.req.json();
|
||||
} catch (error) {
|
||||
return c.json({success: false, message: "Username and password required"}, 400);
|
||||
}
|
||||
|
||||
if (!body?.username || !body?.password) {
|
||||
return c.json({message: "Username and password required"}, 400);
|
||||
}
|
||||
try {
|
||||
const {token, user} = login(body?.username, body?.password);
|
||||
|
||||
// Set the JWT as an HTTP-only cookie
|
||||
c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`);
|
||||
|
||||
return c.json({message: "Login successful", user});
|
||||
} catch (err) {
|
||||
// console.log(err);
|
||||
return c.json({message: err}, 401);
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
export default app;
|
||||
33
server/src/services/auth/routes/register.ts
Normal file
33
server/src/services/auth/routes/register.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const UserSchema = z
|
||||
.object({
|
||||
id: z.string().openapi({example: "123"}),
|
||||
name: z.string().min(3).openapi({example: "John Doe"}),
|
||||
age: z.number().openapi({example: 42}),
|
||||
})
|
||||
.openapi("User");
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Register a new user",
|
||||
method: "post",
|
||||
path: "/",
|
||||
request: {params: UserSchema},
|
||||
responses: {
|
||||
200: {
|
||||
content: {"application/json": {schema: UserSchema}},
|
||||
description: "Retrieve the user",
|
||||
},
|
||||
},
|
||||
}),
|
||||
(c) => {
|
||||
const {id} = c.req.valid("param");
|
||||
return c.json({id, age: 20, name: "Ultra-man"});
|
||||
}
|
||||
);
|
||||
|
||||
export default app;
|
||||
49
server/src/services/auth/routes/session.ts
Normal file
49
server/src/services/auth/routes/session.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
|
||||
import {verify} from "hono/jwt";
|
||||
|
||||
const session = new OpenAPIHono();
|
||||
const tags = ["Auth"];
|
||||
const JWT_SECRET = "your-secret-key";
|
||||
|
||||
const route = createRoute({
|
||||
tags: ["Auth"],
|
||||
summary: "Checks a user session based on there token",
|
||||
description: "Can post there via Authentiaction header or cookies",
|
||||
method: "get",
|
||||
path: "/",
|
||||
request: {body: {content: {"application/json": {schema: {username: "", password: ""}}}}},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {session: ""},
|
||||
},
|
||||
},
|
||||
description: "Login successful",
|
||||
},
|
||||
},
|
||||
});
|
||||
session.openapi(route, async (c) => {
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (authHeader?.includes("Basic")) {
|
||||
//
|
||||
return c.json({message: "You are a Basic user! Please login to get a token"}, 401);
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return c.json({error: "Unauthorized"}, 401);
|
||||
}
|
||||
|
||||
const token = authHeader?.split("Bearer ")[1] || "";
|
||||
|
||||
try {
|
||||
const payload = await verify(token, JWT_SECRET);
|
||||
console.log(payload);
|
||||
return c.json({token});
|
||||
} catch (err) {
|
||||
return c.json({error: "Invalid or expired token"}, 401);
|
||||
}
|
||||
});
|
||||
|
||||
export default session;
|
||||
23
server/src/services/ocme/ocmeServer.ts
Normal file
23
server/src/services/ocme/ocmeServer.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
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,
|
||||
});
|
||||
};
|
||||
7
server/tsconfig.json
Normal file
7
server/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src", "index.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user