init commit

This commit is contained in:
Saimon8420 2025-01-25 18:23:36 +06:00
parent 5ab79d2c42
commit 278aa030cb
29 changed files with 1124 additions and 11 deletions

1
.gitignore vendored
View file

@ -25,6 +25,7 @@ yarn-debug.log*
yarn-error.log* yarn-error.log*
# local env files # local env files
.env
.env.local .env.local
.env.development.local .env.development.local
.env.test.local .env.test.local

BIN
bun.lockb Normal file

Binary file not shown.

11
drizzle.config.ts Normal file
View file

@ -0,0 +1,11 @@
import { defineConfig } from 'drizzle-kit';
import { ENV } from './src/config/env';
export default defineConfig({
out: './drizzle',
schema: './src/db/schema.ts',
dialect: 'postgresql',
dbCredentials: {
url: ENV.DATABASE_URL!,
},
});

View file

@ -0,0 +1,27 @@
CREATE TABLE "projects" (
"project_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" text,
"object" json,
"name" text,
"description" text,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "uploads" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"filename" text NOT NULL,
"url" text NOT NULL,
"projectId" uuid,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "users" (
"user_id" text PRIMARY KEY NOT NULL,
"paid_status" text NOT NULL,
"expires_in" timestamp NOT NULL
);
--> 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
ALTER TABLE "uploads" ADD CONSTRAINT "uploads_projectId_projects_project_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."projects"("project_id") ON DELETE no action ON UPDATE no action;

View file

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

View file

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

View file

@ -0,0 +1,188 @@
{
"id": "7a9f9e79-63fc-4d2b-8b25-616f96161308",
"prevId": "00000000-0000-0000-0000-000000000000",
"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": true
},
"expires_in": {
"name": "expires_in",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -0,0 +1,188 @@
{
"id": "5443181c-129a-488a-9001-d65b03b0119f",
"prevId": "7a9f9e79-63fc-4d2b-8b25-616f96161308",
"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": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -0,0 +1,188 @@
{
"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

@ -0,0 +1,27 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1737800628545,
"tag": "0000_eager_marvel_zombies",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1737804031716,
"tag": "0001_useful_nighthawk",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1737806743289,
"tag": "0002_broad_eternity",
"breakpoints": true
}
]
}

View file

@ -3,13 +3,29 @@
"version": "1.0.50", "version": "1.0.50",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"dev": "bun run --watch src/index.ts" "db:studio": "drizzle-kit studio",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:push": "drizzle-kit push:pg",
"dev": "bun run --watch src/app.ts"
}, },
"dependencies": { "dependencies": {
"elysia": "latest" "@clerk/backend": "^1.23.7",
"@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",
"minio": "^8.0.3",
"pg": "^8.13.1",
"postgres": "^3.4.5"
}, },
"devDependencies": { "devDependencies": {
"bun-types": "latest" "@types/pg": "^8.11.10",
"bun-types": "latest",
"drizzle-kit": "^0.30.2",
"tsx": "^4.19.2"
}, },
"module": "src/index.js" "module": "src/app.js"
} }

View file

@ -0,0 +1,68 @@
import { createClerkClient } from "@clerk/backend";
import { ENV } from "../../config/env"
import { users } from "../../db/schema";
import { db } from "../../db";
import { eq } from "drizzle-orm";
// Initialize Clerk with your API key
const clerk = createClerkClient({ secretKey: ENV.CLERK_SECRET_KEY });
export const getUserData = async (userId: string) => {
try {
const [user, checkInDB] = await Promise.all([
clerk.users.getUser(userId),
checkUserInDB(userId)
]);
if (user && !checkInDB.found) {
const userData = await createUser(user.id);
return { status: 200, message: "User retrieved successfully", data: userData };
}
if (user && checkInDB.found) {
return { status: 200, message: "User retrieved successfully", data: checkInDB };
}
if (!user) {
return { status: 404, message: "User not found" };
}
} catch (error: any) {
console.error("Error in getUserData:", error.message || error.toString());
return { status: 500, message: `An error occurred while getting the user` };
}
};
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 });
return { status: 200, message: "User updated successfully", updateUserData };
} catch (error: any) {
console.error("Error in updateUser:", error.message || error.toString());
return { status: 500, message: `An error occurred while updating the user` };
}
}

View file

@ -0,0 +1,14 @@
import Elysia from "elysia";
import { getUserData, updateUser } from "./auth.controller";
export const authRoute = new Elysia({
prefix: "/auth",
tags: ["Auth"],
detail: {
description: "Routes for managing users",
}
})
authRoute.get("/user/:userId", ({ params: { userId } }) => getUserData(userId));
authRoute.post("/user/update/:userId", ({ params: { userId }, body }) => updateUser(userId, body));

15
src/api/index.ts Normal file
View file

@ -0,0 +1,15 @@
import Elysia from "elysia";
import { projectRoutes } from "./project/project.route";
import { uploadRoutes } from "./upload/upload.route";
import { verifyAuth } from "../middlewares/auth.middlewares";
import { authRoute } from "./auth/auth.route";
export const api = new Elysia({
prefix: "/api",
});
// api.derive(verifyAuth);
api.use(authRoute);
api.use(projectRoutes);
api.use(uploadRoutes);

View file

@ -0,0 +1,37 @@
export const getEachProjects = async (id: string) => {
try {
console.log(id);
return { id: id }
} catch (error) {
console.log(error.msg)
return { status: 500, message: "An error occurred while fetching projects" }
}
}
export const getAllProjects = async () => {
try {
// this will return all the project associated with the user
} catch (error) {
console.log(error.msg);
return { status: 500, message: "An error occurred while fetching projects" }
}
}
export const updateProject = async (id: string, data: any) => {
try {
} catch (error) {
console.log(error.msg);
return { status: 500, message: "An error occurred while updating projects" }
}
}
export const deleteProject = async (id: string) => {
try {
} catch (error) {
console.log(error.msg);
return { status: 500, message: "An error occurred while deleting projects" }
}
}

View file

@ -0,0 +1,19 @@
import { Elysia } from "elysia";
import { deleteProject, getAllProjects, getEachProjects, updateProject } from "./project.controller";
export const projectRoutes = new Elysia({
prefix: "/projects",
tags: ["Projects"],
detail: {
description: "Routes for managing projects",
}
})
projectRoutes.get("/:id", ({ params }) => getEachProjects(params.id));
projectRoutes.get("/", () => getAllProjects());
projectRoutes.put("/update/:id", ({ request, params }) => updateProject(params.id, request.body));
projectRoutes.delete("/delete/:id", ({ params }) => deleteProject(params.id));

View file

@ -0,0 +1,104 @@
import { eq } from "drizzle-orm";
import { db } from "../../db";
import { uploads } from "../../db/schema";
import { createEmptyProject } from "../../helper/projects/createProject";
import { createBucket } from "../../helper/upload/createBucket";
import { uploadToMinio } from "../../helper/upload/uploadToMinio";
import { removeFromMinio } from "../../helper/upload/removeFromMinio";
export const uploadPhoto = async (req: Request) => {
try {
// Use the formData API to extract the data from the request
const formData = await req.formData();
const projectId = formData.get("id");
const userId = formData.get("userId");
const file = formData.get("file");
// Validate the file input
if (!file || !(file instanceof File)) {
throw new Error("Invalid or missing file in form data");
}
if (userId) {
if (projectId) {
const urlLink = await uploadToMinio(file, projectId, file.name);
const saveFile = await db
.insert(uploads)
.values({ filename: file.name, url: urlLink?.url, projectId })
.returning();
return { status: 200, data: { msg: "File uploaded successfully", data: saveFile } };
} else {
const newProjectId = await createEmptyProject(userId.toString());
const bucket = await createBucket(newProjectId?.id);
const urlLink = await uploadToMinio(file, bucket, file.name);
const saveFile = await db
.insert(uploads)
.values({ filename: file.name, url: urlLink?.url, projectId: newProjectId?.id })
.returning();
return { status: 200, data: { msg: "New project created and file uploaded successfully", data: saveFile } };
}
} else {
return { status: 404, message: "User not found" };
}
} catch (error) {
console.error("Error processing file:", error);
return { status: 500, message: "An error occurred while uploading the photo" };
}
};
export const deletePhoto = async (id: string) => {
try {
if (!id) {
throw new Error("Invalid or missing file ID");
}
const deleteFile = await db
.delete(uploads)
.where(eq(uploads.id, id))
.returning();
// Ensure there's a file to delete
if (!deleteFile || deleteFile.length === 0) {
throw new Error("File not found or already deleted");
}
const { projectId, filename } = deleteFile[0];
// Ensure projectId and filename are valid
if (!projectId || !filename) {
throw new Error("Project ID or filename is missing");
}
const minioRemove = await removeFromMinio(projectId, filename);
return { status: 200, message: minioRemove.msg };
} catch (error) {
console.error("Error processing file:", error);
return { status: 500, message: `An error occurred while deleting the photo: ${error.message}` };
}
};
export const getAllPhoto = async (id: string) => {
try {
// project id
if (!id) {
throw new Error("Invalid or missing project ID");
}
const getAllPhoto = await db.select().from(uploads).where(eq(uploads.projectId, id));
if (getAllPhoto.length === 0) {
return { status: 200, data: { msg: "No photos found for the given project ID", data: [] } }
}
return { status: 200, data: { msg: "Photos retrieved successfully", data: getAllPhoto } };
} catch (error) {
console.log(`Error getting photos: ${error.message}`);
return { status: 500, message: "An error occurred while getting the photos" }
}
}

View file

@ -0,0 +1,16 @@
import { Elysia } from "elysia";
import { deletePhoto, getAllPhoto, uploadPhoto } from "./upload.controller";
export const uploadRoutes = new Elysia({
prefix: "/uploads",
tags: ["Uploads"],
detail: {
description: "Routes for uploading and managing photos",
}
});
uploadRoutes.post("/add", async ({ request }) => uploadPhoto(request));
uploadRoutes.delete("/delete/:id", async ({ params }) => deletePhoto(params.id));
uploadRoutes.get("/get/:id", async ({ params }) => getAllPhoto(params.id));

44
src/app.ts Normal file
View file

@ -0,0 +1,44 @@
import { Elysia } from "elysia";
import swagger from '@elysiajs/swagger';
import { ENV } from "./config/env";
import cors from "@elysiajs/cors";
import { api } from "./api";
const app = new Elysia()
.use(cors())
.use(swagger({
path: "/docs",
documentation: {
info: {
title: "Canvas API",
version: "1.0.0",
description: "Canvas API Documentation",
},
tags: [
{
name: "Projects",
description: "All APIs related to Projects",
},
{
name: "Uploads",
description: "All APIs related to Uploads"
}
],
}
}))
.onError(({ code, error }) => {
if (code === 'NOT_FOUND')
return 'Not Found :(';
console.error(error)
});
// all routes here
app.use(api);
app.listen(ENV.SERVER_PORT, () => {
console.log(`🦊 Elysia is running at ${ENV.SERVER_URL}:${ENV.SERVER_PORT}`)
})

12
src/config/env.ts Normal file
View file

@ -0,0 +1,12 @@
import 'dotenv/config'
export const ENV = {
SERVER_URL: process.env.SERVER_URL,
SERVER_PORT: process.env.SERVER_PORT || 5000,
DATABASE_URL: process.env.DATABASE_URL,
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
MINIO_ENDPOINT: process.env.MINIO_ENDPOINT,
MINIO_PORT: process.env.MINIO_PORT,
CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY,
}

10
src/config/minioClient.ts Normal file
View file

@ -0,0 +1,10 @@
import { Client } from "minio";
import { ENV } from "../config/env";
export const minioClient = new Client({
endPoint: ENV.MINIO_ENDPOINT!,
port: ENV.MINIO_PORT,
useSSL: false,
accessKey: ENV.MINIO_ACCESS_KEY,
secretKey: ENV.MINIO_SECRET_KEY,
})

5
src/db/index.ts Normal file
View file

@ -0,0 +1,5 @@
import { drizzle } from "drizzle-orm/node-postgres";
import { ENV } from "../config/env";
export const db = drizzle(ENV.DATABASE_URL!);

26
src/db/schema.ts Normal file
View file

@ -0,0 +1,26 @@
import { json, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: text("user_id").primaryKey().notNull(),
paid_status: text("paid_status"),
expires_in: text("expires_in"),
});
export const projects = pgTable("projects", {
id: uuid("project_id").defaultRandom().primaryKey(),
userId: text("user_id").references(() => users.id),
object: json(),
name: text("name"),
description: text("description"),
created_at: timestamp("created_at").defaultNow(),
updated_at: timestamp("updated_at").defaultNow(),
});
export const uploads = pgTable("uploads", {
id: uuid().defaultRandom().primaryKey(),
filename: text("filename").notNull(),
url: text("url").notNull(),
projectId: uuid().references(() => projects.id),
created_at: timestamp("created_at").defaultNow(),
updated_at: timestamp("updated_at").defaultNow(),
});

View file

@ -0,0 +1,22 @@
import { db } from "../../db";
import { projects } from "../../db/schema";
export const createEmptyProject = async (userId: string): Promise<{ id: string }> => {
try {
// Insert a new row with default values
const [newProject] = await db
.insert(projects)
.values({
userId: userId,
object: {}, // Empty object as default
name: "", // Empty name
description: "", // Empty description
})
.returning({ id: projects.id }); // Returning the ID of the created project
// Return the newly created project's ID
return { id: newProject.id };
} catch (error) {
console.error("Error creating an empty project:", error);
throw new Error("Failed to create an empty project");
}
};

View file

@ -0,0 +1,35 @@
import { minioClient } from "../../config/minioClient";
export const createBucket = async (bucketName: string) => {
try {
const bucketExists = await minioClient.bucketExists(bucketName);
if (!bucketExists) {
// Create the bucket
await minioClient.makeBucket(bucketName, "us-east-1");
// Set the bucket policy to make it public
const bucketPolicy = JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: "*",
Action: ["s3:GetObject"],
Resource: [`arn:aws:s3:::${bucketName}/*`],
},
],
});
await minioClient.setBucketPolicy(bucketName, bucketPolicy);
return bucketName; // Return the bucket name if created successfully
} else {
return bucketName; // Return the bucket name if it already exists
}
} catch (error) {
console.error("Error creating or configuring bucket:", error);
// Optionally rethrow the error with additional context
throw new Error(`Error creating bucket "${bucketName}": ${error.message}`);
}
};

View file

@ -0,0 +1,16 @@
import { minioClient } from "../../config/minioClient";
interface RemoveFromMinioResponse {
msg: string;
}
export const removeFromMinio = async (bucketName: string, objectName: string): Promise<RemoveFromMinioResponse> => {
try {
// Remove the object from MinIO
await minioClient.removeObject(bucketName, objectName);
return { msg: `Successfully removed ${objectName}` };
} catch (error) {
console.error("Error removing object from MinIO:", error);
throw new Error(`Failed to remove ${objectName} from bucket ${bucketName}: ${error.message}`);
}
};

View file

@ -0,0 +1,19 @@
import { minioClient } from "../../config/minioClient";
export const uploadToMinio = async (file: File, bucketName: string, objectName: string) => {
const buffer = Buffer.from(await file.arrayBuffer()); // Convert file to buffer
try {
// Ensure the file is uploaded to MinIO
await minioClient.putObject(bucketName, objectName, buffer);
// Construct the public URL to access the uploaded file
const publicUrl = `${minioClient.protocol}//${minioClient.host}:${minioClient.port}/${bucketName}/${objectName}`;
return { url: publicUrl };
} catch (error) {
console.error("Error uploading file to MinIO:", error);
throw new Error(`Error uploading file: ${error.message}`);
}
};

View file

@ -1,7 +0,0 @@
import { Elysia } from "elysia";
const app = new Elysia().get("/", () => "Hello Elysia").listen(3000);
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

View file

@ -0,0 +1,9 @@
export const verifyAuth = (request: Request) => {
const authHeader = request.headers.get('Authorization');
if (!authHeader) {
return new Response('Unauthorized', { status: 401 });
}
const token = authHeader.split(' ')[1];
// Verify the token here (e.g., using a library like `jsonwebtoken` or `jose`)
}