refactor(lst): register added in

This commit is contained in:
2025-02-21 15:26:56 -06:00
parent 9719451580
commit 026583815c
31 changed files with 529 additions and 385 deletions

View File

@@ -6,7 +6,8 @@ import {OpenAPIHono} from "@hono/zod-openapi";
//routes
import auth from "./services/auth/authService";
import scalar from "./route/scalar";
import scalar from "./services/general/route/scalar";
import apiHits from "./services/general/route/apitHits";
// services
import {ocmeService} from "./services/ocme/ocmeServer";
@@ -38,10 +39,10 @@ app.all("/ocme/*", async (c) => {
return ocmeService(c);
});
const routes = [scalar, auth] as const;
const routes = [scalar, auth, apiHits] as const;
routes.forEach((route) => {
app.route("/", route);
app.route("/api/", route);
});
//app.basePath("/api/auth").route("/login", login).route("/session", session).route("/register", register);

View File

@@ -0,0 +1,39 @@
import {z, ZodError} from "zod";
import {Context} from "hono";
// Define the request body schema
const requestSchema = z.object({
ip: z.string().optional(),
endpoint: z.string(),
action: z.string().optional(),
stats: z.string().optional(),
});
type ApiHitData = z.infer<typeof requestSchema>;
export const apiHit = async (
c: Context,
data: unknown
): Promise<{success: boolean; data?: ApiHitData; errors?: any[]}> => {
// console.log(data);
try {
// Extract IP from request headers or connection info
const forwarded = c.req.header("host");
//console.log(forwarded);
// Validate the data
const validatedData = requestSchema.parse(data);
// Proceed with the validated data
// console.log("Validated Data:", validatedData);
return {success: true, data: validatedData};
} catch (error) {
// Explicitly check if the error is an instance of ZodError
if (error instanceof ZodError) {
// console.log({success: false, errors: error.errors});
return {success: false, errors: error.errors};
}
// Catch other unexpected errors
return {success: false, errors: [{message: "An unknown error occurred"}]};
}
};

View File

@@ -5,8 +5,8 @@ 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);
app.route("auth/login", login);
app.route("auth/register", register);
app.route("auth/session", session);
export default app;

View File

@@ -5,38 +5,38 @@ const app = new OpenAPIHono();
const UserSchema = z
.object({
username: z.string().min(3).openapi({example: "smith002"}),
username: z.string().optional().openapi({example: "smith002"}),
//email: z.string().optional().openapi({example: "s.smith@example.com"}),
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}}}},
request: {
body: {
content: {
"application/json": {schema: UserSchema},
},
},
},
responses: {
200: {
content: {
"application/json": {
schema: LoginResponseSchema,
schema: z.object({
success: z.boolean().openapi({example: true}),
message: z.string().openapi({example: "Logged in"}),
}),
},
},
description: "Login successful",
description: "Response message",
},
400: {
content: {
"application/json": {
@@ -52,37 +52,38 @@ const route = createRoute({
content: {
"application/json": {
schema: z.object({
message: z.string().openapi({example: "Invalid credentials"}),
success: z.boolean().openapi({example: false}),
message: z.string().openapi({example: "Username and password required"}),
}),
},
},
description: "Unauthorized",
description: "Bad request",
},
},
});
app.openapi(route, async (c) => {
let body: {username: string; password: string};
const {username, password, email} = await c.req.json();
console.log(`Trying to login`);
try {
body = await c.req.json();
} catch (error) {
return c.json({success: false, message: "Username and password required"}, 400);
if (!username || !password || !email) {
return c.json(
{
success: false,
message: "Username and password are 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);
const {token, user} = login(username.toLowerCase(), password);
// Set the JWT as an HTTP-only cookie
// c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Path=/; SameSite=None; Max-Age=3600`);
//c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`);
return c.json({message: "Login successful", data: {token, user}});
return c.json({success: true, message: "Login successful", user, token}, 200);
} catch (err) {
return c.json({message: err instanceof Error ? err.message : "Invalid credentials"}, 401);
return c.json({success: false, message: "Incorrect Credentials"}, 401);
}
});

View File

@@ -1,14 +1,31 @@
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
import {db} from "../../../../database/dbClient";
import {users} from "../../../../database/schema/users";
import {apiHit} from "../../../globalUtils/apitHits";
import {createPassword} from "../utils/createPassword";
import {eq} from "drizzle-orm";
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");
const UserSchema = z.object({
username: z
.string()
.regex(/^[a-zA-Z0-9_]{3,30}$/)
.openapi({example: "smith034"}),
email: z.string().email().openapi({example: "smith@example.com"}),
password: z
.string()
.min(6, {message: "Passwords must be longer than 3 characters"})
.regex(/[A-Z]/, {message: "Password must contain at least one uppercase letter"})
.regex(/[\W_]/, {message: "Password must contain at least one special character"})
.openapi({example: "Password1!"}),
});
type User = z.infer<typeof UserSchema>;
const responseSchema = z.object({
message: z.string().optional().openapi({example: "User Created"}),
});
app.openapi(
createRoute({
@@ -16,17 +33,88 @@ app.openapi(
summary: "Register a new user",
method: "post",
path: "/",
request: {params: UserSchema},
request: {
body: {
content: {
"application/json": {schema: UserSchema},
},
},
},
responses: {
200: {
content: {"application/json": {schema: UserSchema}},
content: {"application/json": {schema: responseSchema}},
description: "Retrieve the user",
},
400: {
content: {
"application/json": {
schema: z.object({
success: z.boolean().openapi({example: false}),
message: z.string().openapi({example: "Invalid credentials passed"}),
}),
},
},
description: "Retrieve the user",
},
},
}),
(c) => {
const {id} = c.req.valid("param");
return c.json({id, age: 20, name: "Ultra-man"});
async (c) => {
// apit hit
apiHit(c, {endpoint: "api/auth/register"});
let {username, email, password} = await c.req.json();
if (!username || !email || !password) {
return c.json({success: false, message: "Credentials missing"}, 400);
}
// some usernames that should be ignored
const badActors = ["admin", "root"];
if (badActors.includes(username)) {
return c.json(
{
success: false,
message: `${username} is not a valid name to be registerd please try again`,
},
400
);
}
// make sure the user dose not already exist in the system
const userCheck = await db.select().from(users).where(eq(users.username, username));
if (userCheck.length === 1) {
return c.json(
{
success: false,
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
},
400
);
}
// make sure we only send over a username that is all lowercase
username = username.toLowerCase();
// get the good kinda password
password = await createPassword(password);
try {
const user = await db
.insert(users)
.values({username, email, password})
.returning({user: users.username, email: users.email});
return c.json({message: "User Registered", user}, 200);
} catch (error) {
console.log(error);
return c.json(
{
success: false,
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
},
400
);
}
}
);

View File

@@ -1,14 +1,14 @@
import bcrypt from "bcrypt";
export const passwordUpdate = (password: string) => {
export const createPassword = async (password: string) => {
// encypt password
let pass: string = process.env.SECRET;
let salt: string = process.env.SALTING;
let pass: string | undefined = process.env.SECRET;
let salt: string | undefined = process.env.SALTING;
if (!pass || !salt) {
pass = "error";
} else {
pass = bcrypt.hashSync(process.env.SECRET + password, parseInt(process.env.SALTING));
pass = bcrypt.hashSync(process.env.SECRET + password, parseInt(salt));
pass = btoa(pass);
}

View File

@@ -2,7 +2,7 @@ import {OpenAPIHono} from "@hono/zod-openapi";
const app = new OpenAPIHono();
// the doc endpoint
app.doc("/api", {
app.doc("/", {
openapi: "3.0.0",
info: {
version: "1.0.0",

View File

@@ -0,0 +1,53 @@
import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi";
import {apiHit} from "../../../globalUtils/apitHits";
const app = new OpenAPIHono();
// Define the request body schema
const requestSchema = z.object({
ip: z.string().optional(),
endpoint: z.string().optional(),
action: z.string().optional(),
stats: z.string().optional(),
});
// Define the response schema
const responseSchema = z.object({
message: z.string(),
});
app.openapi(
createRoute({
tags: ["api"],
summary: "Tracks the API posts and how often",
method: "post",
path: "/hits",
request: {
body: {
content: {
"application/json": {schema: requestSchema},
},
},
},
responses: {
200: {
content: {
"application/json": {schema: responseSchema},
},
description: "Response message",
},
},
}),
async (c) => {
const data = await c.req.json();
apiHit(data);
// Return response with the received data
return c.json({
message: `Received name: ${data.name}, arrayData: ${data.arrayData}`,
});
}
);
export default app;

View File

@@ -4,7 +4,7 @@ import {apiReference} from "@scalar/hono-api-reference";
const app = new OpenAPIHono();
app.get(
"/api/docs",
"/docs",
apiReference({
theme: "kepler",
layout: "classic",
@@ -56,7 +56,7 @@ app.get(
description: "Production",
},
{
url: "http://localhost:4000",
url: "http://localhost:3000",
description: "dev server",
},
],