From b15f1d8ae815f2eb232b2c3f3918d7e63490dcaf Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Wed, 19 Feb 2025 10:06:43 -0600 Subject: [PATCH] refactor(all): refactoring to remove monorepo taking to long to get it wokring as intended --- apiDocs/lstV2/Auth/Test Protected.bru | 2 +- apiDocs/lstV2/Auth/session.bru | 7 +- apps/server/package.json | 3 + apps/server/src/app.ts | 37 ++-- apps/server/src/auth/auth.ts | 54 ------ apps/server/src/route/apiDoc.ts | 13 ++ apps/server/src/route/auth/login.ts | 28 --- apps/server/src/route/scalar.ts | 79 ++++++++ apps/server/src/services/auth/authService.ts | 12 ++ .../src/services/auth/controllers/register.ts | 1 + .../src/services/auth/lib/createPassword.ts | 17 ++ apps/server/src/services/auth/routes/login.ts | 112 +++++++++++ .../src/services/auth/routes/register.ts | 33 ++++ .../auth => services/auth/routes}/session.ts | 35 ++-- .../src/services/{ => ocme}/ocmeServer.ts | 0 load-env.ts | 10 + package.json | 5 + packages/database/.gitignore | 175 ++++++++++++++++++ packages/database/README.md | 15 ++ packages/database/drizzle.config.ts | 8 + .../drizzle/0000_stormy_thunderbolt.sql | 12 ++ .../database/drizzle/meta/0000_snapshot.json | 96 ++++++++++ packages/database/drizzle/meta/_journal.json | 13 ++ packages/database/index.ts | 7 + packages/database/package.json | 20 ++ packages/database/schema/users.ts | 27 +++ packages/database/tsconfig.json | 27 +++ packages/shared/src/utils/createPassword.ts | 0 28 files changed, 736 insertions(+), 112 deletions(-) delete mode 100644 apps/server/src/auth/auth.ts create mode 100644 apps/server/src/route/apiDoc.ts delete mode 100644 apps/server/src/route/auth/login.ts create mode 100644 apps/server/src/route/scalar.ts create mode 100644 apps/server/src/services/auth/authService.ts create mode 100644 apps/server/src/services/auth/controllers/register.ts create mode 100644 apps/server/src/services/auth/lib/createPassword.ts create mode 100644 apps/server/src/services/auth/routes/login.ts create mode 100644 apps/server/src/services/auth/routes/register.ts rename apps/server/src/{route/auth => services/auth/routes}/session.ts (51%) rename apps/server/src/services/{ => ocme}/ocmeServer.ts (100%) create mode 100644 load-env.ts create mode 100644 packages/database/.gitignore create mode 100644 packages/database/README.md create mode 100644 packages/database/drizzle.config.ts create mode 100644 packages/database/drizzle/0000_stormy_thunderbolt.sql create mode 100644 packages/database/drizzle/meta/0000_snapshot.json create mode 100644 packages/database/drizzle/meta/_journal.json create mode 100644 packages/database/index.ts create mode 100644 packages/database/package.json create mode 100644 packages/database/schema/users.ts create mode 100644 packages/database/tsconfig.json create mode 100644 packages/shared/src/utils/createPassword.ts diff --git a/apiDocs/lstV2/Auth/Test Protected.bru b/apiDocs/lstV2/Auth/Test Protected.bru index ae5127a..934a212 100644 --- a/apiDocs/lstV2/Auth/Test Protected.bru +++ b/apiDocs/lstV2/Auth/Test Protected.bru @@ -11,5 +11,5 @@ get { } auth:bearer { - token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImlhdCI6MTczOTgyMTgyNiwiZXhwIjoxNzM5ODI1NDI2fQ.N5pn4PaPDhM_AXAOTGwd-_TOP9UOU1wK0vmICVE7vEc + token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImlhdCI6MTczOTgyNjEwNiwiZXhwIjoxNzM5ODI2MTY2fQ.y8wm4PuduJdXp_fPnRohQ8VOlNRMeYszzcPdOLRwzic } diff --git a/apiDocs/lstV2/Auth/session.bru b/apiDocs/lstV2/Auth/session.bru index 4c4fedc..257b818 100644 --- a/apiDocs/lstV2/Auth/session.bru +++ b/apiDocs/lstV2/Auth/session.bru @@ -7,13 +7,14 @@ meta { get { url: http://localhost:4000/api/auth/session body: none - auth: bearer + auth: basic } headers { : } -auth:bearer { - token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImlhdCI6MTczOTgyNzE1NiwiZXhwIjoxNzM5ODI3MjE2fQ.VE9URMrRI_5_wc8CEmj-VEVeP01LL412vKhNwWRRHRM +auth:basic { + username: admin + password: pass123 } diff --git a/apps/server/package.json b/apps/server/package.json index fdc0bc4..5bfd851 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -9,5 +9,8 @@ }, "devDependencies": { "typescript": "^5.7.3" + }, + "dependencies": { + "@scalar/hono-api-reference": "^0.5.174" } } diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index c536934..bf83f66 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -1,15 +1,16 @@ -import {Hono} from "hono"; import {serveStatic} from "hono/bun"; import {logger} from "hono/logger"; -import {ocmeService} from "./services/ocmeServer"; import {authMiddleware} from "lst-auth"; import {cors} from "hono/cors"; +import {OpenAPIHono} from "@hono/zod-openapi"; -//import { expensesRoute } from "./routes/expenses"; -import login from "./route/auth/login"; -import session from "./route/auth/session"; +//routes +import auth from "./services/auth/authService"; +import scalar from "./route/scalar"; +// services +import {ocmeService} from "./services/ocme/ocmeServer"; -const app = new Hono(); +const app = new OpenAPIHono(); app.use("*", logger()); app.use( @@ -24,24 +25,32 @@ app.use( }) ); +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); }); -app.basePath("/api/auth").route("/login", login).route("/session", session); +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("/api/test", (c) => { - return c.json({success: true, message: "hello from bun"}); -}); -// const authRoute = app.basePath("/api/auth").route("*", ) - -//const apiRoute = app.basePath("/api").route("/expenses", expensesRoute); - app.get("*", serveStatic({root: "../frontend/dist"})); app.get("*", serveStatic({path: "../frontend/dist/index.html"})); diff --git a/apps/server/src/auth/auth.ts b/apps/server/src/auth/auth.ts deleted file mode 100644 index c86b632..0000000 --- a/apps/server/src/auth/auth.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Context } from "hono"; -import { authHandler, initAuthConfig, verifyAuth } from "@hono/auth-js"; -import Credentials from "@auth/core/providers/credentials"; -import { AuthConfig } from "@auth/core/types"; - -export const authConfig: AuthConfig = { - secret: process.env.AUTH_SECRET, - providers: [ - Credentials({ - name: "Credentials", - credentials: { - username: { label: "Username", type: "text" }, - password: { label: "Password", type: "password" }, - }, - async authorize(credentials) { - // Add your authentication logic here - const user = { id: "1", name: "John Doe", email: "john@example.com" }; - if ( - credentials?.username === "john" && - credentials?.password === "password" - ) { - return user; - } - return null; - }, - }), - ], - session: { - strategy: "jwt", - }, - callbacks: { - // async session({ session, token }) { - // session.user.id = token.sub; - // return session; - // }, - async jwt({ token, user }) { - if (user) { - token.sub = user.id; - } - return token; - }, - }, -}; - -// auth.use("/api/auth/*", authHandler()); - -// auth.use("/api/*", verifyAuth()); - -// auth.get("/api/protected", (c) => { -// const auth = c.get("authUser"); -// return c.json(auth); -// }); - -// export default auth; diff --git a/apps/server/src/route/apiDoc.ts b/apps/server/src/route/apiDoc.ts new file mode 100644 index 0000000..e72bcd0 --- /dev/null +++ b/apps/server/src/route/apiDoc.ts @@ -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; diff --git a/apps/server/src/route/auth/login.ts b/apps/server/src/route/auth/login.ts deleted file mode 100644 index d7a2e51..0000000 --- a/apps/server/src/route/auth/login.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {Hono} from "hono"; -import {login} from "lst-auth"; - -const router = new Hono().post("/", async (c) => { - 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 router; diff --git a/apps/server/src/route/scalar.ts b/apps/server/src/route/scalar.ts new file mode 100644 index 0000000..d45fd51 --- /dev/null +++ b/apps/server/src/route/scalar.ts @@ -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; diff --git a/apps/server/src/services/auth/authService.ts b/apps/server/src/services/auth/authService.ts new file mode 100644 index 0000000..897c4ec --- /dev/null +++ b/apps/server/src/services/auth/authService.ts @@ -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; diff --git a/apps/server/src/services/auth/controllers/register.ts b/apps/server/src/services/auth/controllers/register.ts new file mode 100644 index 0000000..9caab2c --- /dev/null +++ b/apps/server/src/services/auth/controllers/register.ts @@ -0,0 +1 @@ +export const registerUser = async () => {}; diff --git a/apps/server/src/services/auth/lib/createPassword.ts b/apps/server/src/services/auth/lib/createPassword.ts new file mode 100644 index 0000000..a529c8e --- /dev/null +++ b/apps/server/src/services/auth/lib/createPassword.ts @@ -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; +}; diff --git a/apps/server/src/services/auth/routes/login.ts b/apps/server/src/services/auth/routes/login.ts new file mode 100644 index 0000000..1b95435 --- /dev/null +++ b/apps/server/src/services/auth/routes/login.ts @@ -0,0 +1,112 @@ +import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi"; +import {login} from "lst-auth"; + +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", 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; diff --git a/apps/server/src/services/auth/routes/register.ts b/apps/server/src/services/auth/routes/register.ts new file mode 100644 index 0000000..8f2ec3d --- /dev/null +++ b/apps/server/src/services/auth/routes/register.ts @@ -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; diff --git a/apps/server/src/route/auth/session.ts b/apps/server/src/services/auth/routes/session.ts similarity index 51% rename from apps/server/src/route/auth/session.ts rename to apps/server/src/services/auth/routes/session.ts index e51b673..b4c353a 100644 --- a/apps/server/src/route/auth/session.ts +++ b/apps/server/src/services/auth/routes/session.ts @@ -1,11 +1,29 @@ -import {Hono} from "hono"; +import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi"; import {verify} from "hono/jwt"; -const app = new Hono(); - +const session = new OpenAPIHono(); +const tags = ["Auth"]; const JWT_SECRET = "your-secret-key"; -app.get("/", async (c) => { +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"); const cookies = c.req.header("cookie"); @@ -17,13 +35,6 @@ app.get("/", async (c) => { if (!authHeader && !cookies) { return c.json({error: "Unauthorized"}, 401); } - // if (!cookies || !cookies.startsWith("Bearer ")) { - // return c.json({error: "Unauthorized"}, 401); - // } - - // if (!authHeader || !authHeader.startsWith("Bearer ")) { - // return c.json({error: "Unauthorized"}, 401); - // } const token = cookies?.split("auth_token=")[1].split(";")[0] || authHeader?.split("Bearer ")[1] || ""; @@ -35,4 +46,4 @@ app.get("/", async (c) => { } }); -export default app; +export default session; diff --git a/apps/server/src/services/ocmeServer.ts b/apps/server/src/services/ocme/ocmeServer.ts similarity index 100% rename from apps/server/src/services/ocmeServer.ts rename to apps/server/src/services/ocme/ocmeServer.ts diff --git a/load-env.ts b/load-env.ts new file mode 100644 index 0000000..d8d500b --- /dev/null +++ b/load-env.ts @@ -0,0 +1,10 @@ +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); diff --git a/package.json b/package.json index 66e24b0..ad48da8 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,14 @@ "@hono/zod-openapi": "^0.18.4", "@shared/lib": "*", "@types/bun": "^1.2.2", + "axios": "^1.7.9", + "bcrypt": "^5.1.1", + "compression": "^1.8.0", "concurrently": "^9.1.2", "cookie": "^1.0.2", + "date-fns": "^4.1.0", "dotenv": "^16.4.7", + "dotenv-expand": "^12.0.1", "hono": "^4.7.1", "http-proxy-middleware": "^3.0.3", "jsonwebtoken": "^9.0.2", diff --git a/packages/database/.gitignore b/packages/database/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/packages/database/.gitignore @@ -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 diff --git a/packages/database/README.md b/packages/database/README.md new file mode 100644 index 0000000..2739490 --- /dev/null +++ b/packages/database/README.md @@ -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. diff --git a/packages/database/drizzle.config.ts b/packages/database/drizzle.config.ts new file mode 100644 index 0000000..97aa44a --- /dev/null +++ b/packages/database/drizzle.config.ts @@ -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", + }, +}); diff --git a/packages/database/drizzle/0000_stormy_thunderbolt.sql b/packages/database/drizzle/0000_stormy_thunderbolt.sql new file mode 100644 index 0000000..abd53fd --- /dev/null +++ b/packages/database/drizzle/0000_stormy_thunderbolt.sql @@ -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() +); diff --git a/packages/database/drizzle/meta/0000_snapshot.json b/packages/database/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..cc6198d --- /dev/null +++ b/packages/database/drizzle/meta/0000_snapshot.json @@ -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": {} + } +} \ No newline at end of file diff --git a/packages/database/drizzle/meta/_journal.json b/packages/database/drizzle/meta/_journal.json new file mode 100644 index 0000000..0fd12dd --- /dev/null +++ b/packages/database/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1739914245651, + "tag": "0000_stormy_thunderbolt", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/packages/database/index.ts b/packages/database/index.ts new file mode 100644 index 0000000..c9d8c34 --- /dev/null +++ b/packages/database/index.ts @@ -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); diff --git a/packages/database/package.json b/packages/database/package.json new file mode 100644 index 0000000..e6f9fb6 --- /dev/null +++ b/packages/database/package.json @@ -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" + } +} \ No newline at end of file diff --git a/packages/database/schema/users.ts b/packages/database/schema/users.ts new file mode 100644 index 0000000..b2dfa03 --- /dev/null +++ b/packages/database/schema/users.ts @@ -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); diff --git a/packages/database/tsconfig.json b/packages/database/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/packages/database/tsconfig.json @@ -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 + } +} diff --git a/packages/shared/src/utils/createPassword.ts b/packages/shared/src/utils/createPassword.ts new file mode 100644 index 0000000..e69de29