auth added not tested yet

This commit is contained in:
Saimon8420 2025-01-27 18:19:02 +06:00
parent 278aa030cb
commit fc5c2f0eab
17 changed files with 230 additions and 243 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -19,8 +19,9 @@ CREATE TABLE "uploads" (
--> statement-breakpoint
CREATE TABLE "users" (
"user_id" text PRIMARY KEY NOT NULL,
"paid_status" text NOT NULL,
"expires_in" timestamp NOT NULL
"paid_status" text,
"expires_in" text,
"refresh_token" text
);
--> statement-breakpoint
ALTER TABLE "projects" ADD CONSTRAINT "projects_user_id_users_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("user_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint

View file

@ -0,0 +1,4 @@
ALTER TABLE "users" ADD COLUMN "email" text NOT NULL;--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "last_name" text;--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "first_name" text;--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "image" text;

View file

@ -1,2 +0,0 @@
ALTER TABLE "users" ALTER COLUMN "paid_status" DROP NOT NULL;--> statement-breakpoint
ALTER TABLE "users" ALTER COLUMN "expires_in" DROP NOT NULL;

View file

@ -1 +0,0 @@
ALTER TABLE "users" ALTER COLUMN "expires_in" SET DATA TYPE text;

View file

@ -1,5 +1,5 @@
{
"id": "7a9f9e79-63fc-4d2b-8b25-616f96161308",
"id": "a0fe5e52-63bf-4a92-adb0-ae296fb9f33e",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
@ -156,13 +156,19 @@
"name": "paid_status",
"type": "text",
"primaryKey": false,
"notNull": true
"notNull": false
},
"expires_in": {
"name": "expires_in",
"type": "timestamp",
"type": "text",
"primaryKey": false,
"notNull": true
"notNull": false
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},

View file

@ -1,6 +1,6 @@
{
"id": "5443181c-129a-488a-9001-d65b03b0119f",
"prevId": "7a9f9e79-63fc-4d2b-8b25-616f96161308",
"id": "b6897b47-e0f0-48c5-8917-696944c8524b",
"prevId": "a0fe5e52-63bf-4a92-adb0-ae296fb9f33e",
"version": "7",
"dialect": "postgresql",
"tables": {
@ -152,6 +152,30 @@
"primaryKey": true,
"notNull": true
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"last_name": {
"name": "last_name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"first_name": {
"name": "first_name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"image": {
"name": "image",
"type": "text",
"primaryKey": false,
"notNull": false
},
"paid_status": {
"name": "paid_status",
"type": "text",
@ -160,7 +184,13 @@
},
"expires_in": {
"name": "expires_in",
"type": "timestamp",
"type": "text",
"primaryKey": false,
"notNull": false
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
}

View file

@ -1,188 +0,0 @@
{
"id": "88a4eebc-30cb-457f-9fc1-c6bbff735742",
"prevId": "5443181c-129a-488a-9001-d65b03b0119f",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.projects": {
"name": "projects",
"schema": "",
"columns": {
"project_id": {
"name": "project_id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"object": {
"name": "object",
"type": "json",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"projects_user_id_users_user_id_fk": {
"name": "projects_user_id_users_user_id_fk",
"tableFrom": "projects",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"user_id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.uploads": {
"name": "uploads",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"filename": {
"name": "filename",
"type": "text",
"primaryKey": false,
"notNull": true
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": true
},
"projectId": {
"name": "projectId",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"uploads_projectId_projects_project_id_fk": {
"name": "uploads_projectId_projects_project_id_fk",
"tableFrom": "uploads",
"tableTo": "projects",
"columnsFrom": [
"projectId"
],
"columnsTo": [
"project_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": "text",
"primaryKey": true,
"notNull": true
},
"paid_status": {
"name": "paid_status",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_in": {
"name": "expires_in",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -5,22 +5,15 @@
{
"idx": 0,
"version": "7",
"when": 1737800628545,
"tag": "0000_eager_marvel_zombies",
"when": 1737876637906,
"tag": "0000_tidy_echo",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1737804031716,
"tag": "0001_useful_nighthawk",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1737806743289,
"tag": "0002_broad_eternity",
"when": 1737876981144,
"tag": "0001_shallow_umar",
"breakpoints": true
}
]

View file

@ -11,12 +11,14 @@
},
"dependencies": {
"@clerk/backend": "^1.23.7",
"@elysiajs/cookie": "^0.8.0",
"@elysiajs/cors": "^1.2.0",
"@elysiajs/swagger": "^1.2.0",
"dotenv": "^16.4.7",
"drizzle-orm": "^0.38.4",
"elysia": "latest",
"jose": "^5.9.6",
"jsonwebtoken": "^9.0.2",
"minio": "^8.0.3",
"pg": "^8.13.1",
"postgres": "^3.4.5"

View file

@ -4,6 +4,11 @@ import { users } from "../../db/schema";
import { db } from "../../db";
import { eq } from "drizzle-orm";
// @ts-ignore
import jwt from "jsonwebtoken";
import { checkUserInDB, createUser, storeRefreshToken } from "../../helper/auth/auth.helper";
// Initialize Clerk with your API key
const clerk = createClerkClient({ secretKey: ENV.CLERK_SECRET_KEY });
@ -15,7 +20,18 @@ export const getUserData = async (userId: string) => {
]);
if (user && !checkInDB.found) {
const userData = await createUser(user.id);
// Validate and transform user data
const userDBData = {
id: user.id,
email: user.emailAddresses[0].emailAddress, // Assuming the first email address
firstName: user.firstName || "N/A", // Provide a default value if needed
lastName: user.lastName || "N/A",
image: user.imageUrl,
};
const userData = await createUser(userDBData);
return { status: 200, message: "User retrieved successfully", data: userData };
}
if (user && checkInDB.found) {
@ -30,31 +46,6 @@ export const getUserData = async (userId: string) => {
}
};
export const checkUserInDB = async (id: string) => {
try {
const user = await db.select().from(users).where(eq(users.id, id));
return { status: 200, found: user?.length > 0 };
} catch (error: any) {
console.error("Error in checkUserInDB:", error.message || error.toString());
return { status: 500, message: `An error occurred while checking the user in DB` };
}
};
export const createUser = async (id: string) => {
try {
const [saveUser] = await db.insert(users).values({ id }).returning({ insertedId: users.id });
if (!saveUser || !saveUser.insertedId) {
throw new Error("Failed to create user or missing insertedId");
}
return { status: 200, message: "User created successfully", data: saveUser.insertedId };
} catch (error: any) {
console.error("Error in createUser:", error.message || error.toString());
return { status: 500, message: `An error occurred while creating the user` };
}
};
export const updateUser = async (id: string, body) => {
try {
const updateUserData = await db.update(users).set({ paid_status: body?.paid_status, expires_in: body?.package_expire_date }).where(eq(users.id, id)).returning({ updatedId: users.id });
@ -65,4 +56,88 @@ export const updateUser = async (id: string, body) => {
console.error("Error in updateUser:", error.message || error.toString());
return { status: 500, message: `An error occurred while updating the user` };
}
}
}
export const generateToken = async (context: any) => {
try {
const userId = context?.params?.userId;
// generating accessToken and refreshToken
const user = await checkUserInDB(userId);
if (user?.found === true) {
const accessSecret = ENV.JWT_ACCESS_TOKEN_SECRET;
const refreshSecret = ENV.JWT_REFRESH_TOKEN_SECRET;
// generate access token
const accessToken = jwt.sign({ userId }, accessSecret, { expiresIn: '3h' });
// generate refresh token
const refreshToken = jwt.sign({ userId }, refreshSecret, { expiresIn: '7d' });
// store refresh token in db
const storeRToken = await storeRefreshToken(userId, refreshToken);
if (storeRToken.status === 200) {
context.cookie.access_token = {
value: accessToken,
httpOnly: true,
secure: true,
sameSite: 'none',
path: "/",
maxAge: 3 * 60 * 60 * 1000, // 3 hours
}
return { status: 200, message: "Token generated successfully", token: accessToken };
}
return { status: 500, message: "An error occurred while storing the refresh token" };
}
else {
return { status: 404, message: "Unauthorized!!!" };
}
} catch (error: any) {
console.error("Error in generateToken:", error.message || error.toString());
return { status: 500, message: `An error occurred while generating the token` };
}
}
export const verifyToken = async (context: any) => {
try {
// if token is in cookie, verify it
const token_cookie = context.cookie.access_token.value;
if (token_cookie) {
const verify_cookie = jwt.verify(token_cookie, ENV.JWT_REFRESH_TOKEN_SECRET);
if (verify_cookie) {
return { status: 200, message: "Token verified successfully" };
}
else {
return { status: 401, message: "Unauthorized!!!" };
}
}
// if token is not in cookie, then check in header and verify it
else {
const token_header = context.headers.authorization.split("Bearer ")[1];
if (token_header) {
const verify_header = jwt.decode(token_header);
if (verify_header?.userId) {
context.params.userId = verify_header.userId;
await generateToken(context);
}
else {
return { status: 401, message: "Unauthorized!!!" };
}
}
else {
return { status: 401, message: "Unauthorized!!!" };
}
}
} catch (error: any) {
console.log("Error in verifyToken:", error.message || error.toString());
return { status: 500, message: `An error occurred while verifying the token` };
}
}

View file

@ -1,5 +1,5 @@
import Elysia from "elysia";
import { getUserData, updateUser } from "./auth.controller";
import { generateToken, getUserData, updateUser, verifyToken } from "./auth.controller";
export const authRoute = new Elysia({
prefix: "/auth",
@ -9,6 +9,11 @@ export const authRoute = new Elysia({
}
})
authRoute.get("/user/:userId", ({ params: { userId } }) => getUserData(userId));
authRoute.get("/user/:userId", async ({ params: { userId } }) => await getUserData(userId));
authRoute.post("/user/update/:userId", async ({ params: { userId }, body }) => await updateUser(userId, body));
authRoute.get("/generate-token/:userId", async (context) => await generateToken(context));
authRoute.get("/verify-token", async (context) => await verifyToken(context));
authRoute.post("/user/update/:userId", ({ params: { userId }, body }) => updateUser(userId, body));

View file

@ -3,13 +3,14 @@ import { projectRoutes } from "./project/project.route";
import { uploadRoutes } from "./upload/upload.route";
import { verifyAuth } from "../middlewares/auth.middlewares";
import { authRoute } from "./auth/auth.route";
import cookie from "@elysiajs/cookie";
export const api = new Elysia({
prefix: "/api",
});
// api.derive(verifyAuth);
api.use(cookie());
api.use(authRoute);
api.use(projectRoutes);
api.use(uploadRoutes);

View file

@ -6,7 +6,10 @@ import cors from "@elysiajs/cors";
import { api } from "./api";
const app = new Elysia()
.use(cors())
.use(cors({
origin: "http://localhost:5175",
credentials: true,
}))
.use(swagger({
path: "/docs",
documentation: {

View file

@ -9,4 +9,6 @@ export const ENV = {
MINIO_ENDPOINT: process.env.MINIO_ENDPOINT,
MINIO_PORT: process.env.MINIO_PORT,
CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY,
JWT_ACCESS_TOKEN_SECRET: process.env.JWT_ACCESS_TOKEN_SECRET,
JWT_REFRESH_TOKEN_SECRET: process.env.JWT_REFRESH_TOKEN_SECRET,
}

View file

@ -2,8 +2,13 @@ import { json, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: text("user_id").primaryKey().notNull(),
email: text("email").notNull(),
lastName: text("last_name"),
firstName: text("first_name"),
image: text("image"),
paid_status: text("paid_status"),
expires_in: text("expires_in"),
refresh_token: text("refresh_token"),
});
export const projects = pgTable("projects", {

View file

@ -0,0 +1,51 @@
import { eq } from "drizzle-orm";
import { db } from "../../db";
import { users } from "../../db/schema";
type User = {
id: string;
email: string;
lastName: string;
firstName: string;
image: string;
}
// this will check the user into our local canvas database
export const checkUserInDB = async (id: string) => {
try {
const user = await db.select().from(users).where(eq(users.id, id));
return { status: 200, found: user?.length > 0 };
} catch (error: any) {
console.error("Error in checkUserInDB:", error.message || error.toString());
return { status: 500, message: `An error occurred while checking the user in DB` };
}
};
export const createUser = async (body: User) => {
try {
const { id, email, lastName, firstName, image } = body;
const [saveUser] = await db.insert(users).values({ id, email, lastName, firstName, image }).returning({ insertedId: users.id });
if (!saveUser || !saveUser.insertedId) {
throw new Error("Failed to create user or missing insertedId");
}
return { status: 200, message: "User created successfully", data: saveUser.insertedId };
} catch (error: any) {
console.error("Error in createUser:", error.message || error.toString());
return { status: 500, message: `An error occurred while creating the user` };
}
};
// this will store the refresh token in the database
export const storeRefreshToken = async (userId: string, refreshToken: string) => {
try {
// store refresh token in db
const storeRToken = await db.update(users).set({ refresh_token: refreshToken }).where(eq(users.id, userId)).returning({ updatedId: users.id });
return { status: 200, message: "Refresh token stored successfully" };
} catch (error: any) {
console.error("Error in storeRToken:", error.message || error.toString());
return { status: 500, message: `An error occurred while storing the refresh token` }
}
}