Compare commits
61 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7103e802b0 | ||
|
|
27339ba1bd | ||
|
|
f3416f86f6 | ||
|
|
38b514f3a9 | ||
|
|
657a8341c6 | ||
|
|
a525e9388d | ||
|
|
e75ec62fa9 | ||
|
|
1e8ce2bb8a | ||
|
|
048f9bc9a7 | ||
|
|
f3aa472e7e | ||
|
|
bfa9fee43f | ||
|
|
c1bd49a018 | ||
|
|
d68db66cc4 | ||
|
|
1f57c95a4f | ||
|
|
31e9508dc2 | ||
|
|
38fe798582 | ||
|
|
75ad9eeceb | ||
|
|
0ec6755c97 | ||
|
|
81332dff5f | ||
|
|
25fa736170 | ||
|
|
420d4cb5f8 | ||
|
|
f4c1c9fcb0 | ||
|
|
994c9fcb02 | ||
|
|
eab5cf7e05 | ||
|
|
9550fc151b | ||
|
|
d4a1cd4369 | ||
|
|
dee2bf2fc0 | ||
|
|
be29aed2a2 | ||
|
|
e5132b2270 | ||
|
|
6f6a9562ee | ||
|
|
dac2478198 | ||
|
|
c20bf7a178 | ||
|
|
d58e78bb0e | ||
|
|
9ed91b4a06 | ||
|
|
6bbf2137b5 | ||
|
|
40d600ab5b | ||
|
|
f9da806ec9 | ||
|
|
2f80b6dc56 | ||
|
|
e13b787836 | ||
|
|
a7f383a0c8 | ||
|
|
17b33bc81a | ||
|
|
48f8013dfe | ||
|
|
af0d91ed94 | ||
|
|
c562a82d70 | ||
|
|
c194ca1495 | ||
|
|
e1be753ce2 | ||
|
|
b9f369353d | ||
| b071602b7b | |||
|
|
363edc548a | ||
|
|
c76b18346a | ||
| 7954b53c00 | |||
| 55548f7388 | |||
| d621437a4d | |||
| 5373ad9dba | |||
| 94830af20d | |||
|
|
ac5e68536f | ||
|
|
3f979870d6 | ||
|
|
f91ff472d0 | ||
| 9ebea74b45 | |||
| e11a051730 | |||
| b1e8b7a664 |
17 changed files with 744 additions and 435 deletions
|
|
@ -1,8 +1,6 @@
|
||||||
# Build stage
|
# Build stage
|
||||||
FROM oven/bun:1 AS builder
|
FROM oven/bun:1 AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy package files
|
# Copy package files
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
COPY bun.lockb .
|
COPY bun.lockb .
|
||||||
|
|
@ -16,6 +14,8 @@ COPY . .
|
||||||
# Build the application
|
# Build the application
|
||||||
RUN bun run build
|
RUN bun run build
|
||||||
|
|
||||||
|
EXPOSE 5005
|
||||||
|
|
||||||
# Production stage
|
# Production stage
|
||||||
# FROM debian:bookworm-slim
|
# FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ services:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
# ports:
|
ports:
|
||||||
# - "${SERVER_PORT}:${SERVER_PORT}"
|
- "5005:5005"
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
@ -34,8 +34,8 @@ services:
|
||||||
minio:
|
minio:
|
||||||
image: minio/minio:latest
|
image: minio/minio:latest
|
||||||
# ports:
|
# ports:
|
||||||
# - "9000:9000"
|
# - "9000:9000"
|
||||||
# - "9001:9001"
|
# - "9001:9001"
|
||||||
environment:
|
environment:
|
||||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { defineConfig } from 'drizzle-kit';
|
import { defineConfig } from "drizzle-kit";
|
||||||
import { ENV } from './src/config/env';
|
import { ENV } from "./src/config/env";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
out: './drizzle',
|
out: "./drizzle",
|
||||||
schema: './src/db/schema.ts',
|
schema: "./src/db/schema.ts",
|
||||||
dialect: 'postgresql',
|
dialect: "postgresql",
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: ENV.DATABASE_URL!,
|
url: ENV.DATABASE_URL!,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
21
env.example
Normal file
21
env.example
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
SERVER_URL=
|
||||||
|
SERVER_PORT=
|
||||||
|
|
||||||
|
DATABASE_URL=
|
||||||
|
|
||||||
|
MINIO_ACCESS_KEY=
|
||||||
|
MINIO_SECRET_KEY=
|
||||||
|
MINIO_ENDPOINT=
|
||||||
|
MINIO_PORT=
|
||||||
|
|
||||||
|
CLERK_SECRET_KEY=
|
||||||
|
|
||||||
|
JWT_ACCESS_TOKEN_SECRET=
|
||||||
|
|
||||||
|
JWT_REFRESH_TOKEN_SECRET=
|
||||||
|
|
||||||
|
# developer canvas server url
|
||||||
|
CANVAS_SERVER_URL_DEV=
|
||||||
|
|
||||||
|
PEXELS_URL=
|
||||||
|
PEXELS_ACCESS_KEY=
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { createClerkClient } from "@clerk/backend";
|
import { createClerkClient } from "@clerk/backend";
|
||||||
import { ENV } from "../../config/env"
|
import { ENV } from "../../config/env";
|
||||||
import { users } from "../../db/schema";
|
import { users } from "../../db/schema";
|
||||||
import { db } from "../../db";
|
import { db } from "../../db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
@ -7,119 +7,160 @@ import { eq } from "drizzle-orm";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
|
|
||||||
import { checkUserInDB, createUser, storeRefreshToken } from "../../helper/auth/auth.helper";
|
import {
|
||||||
|
checkUserInDB,
|
||||||
|
createUser,
|
||||||
|
storeRefreshToken,
|
||||||
|
} from "../../helper/auth/auth.helper";
|
||||||
import { verifyAuth } from "../../middlewares/auth.middlewares";
|
import { verifyAuth } from "../../middlewares/auth.middlewares";
|
||||||
|
|
||||||
// Initialize Clerk with your API key
|
// Initialize Clerk with your API key
|
||||||
const clerk = createClerkClient({ secretKey: ENV.CLERK_SECRET_KEY });
|
const clerk = createClerkClient({ secretKey: ENV.CLERK_SECRET_KEY });
|
||||||
|
|
||||||
export const getUserData = async (userId: string) => {
|
export const getUserData = async (userId: string) => {
|
||||||
try {
|
try {
|
||||||
const [user, checkInDB] = await Promise.all([
|
const [user, checkInDB] = await Promise.all([
|
||||||
clerk.users.getUser(userId),
|
clerk.users.getUser(userId),
|
||||||
checkUserInDB(userId)
|
checkUserInDB(userId),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (user && !checkInDB.found) {
|
if (user && !checkInDB.found) {
|
||||||
|
// 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,
|
||||||
|
};
|
||||||
|
|
||||||
// Validate and transform user data
|
const userData = await createUser(userDBData);
|
||||||
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,
|
||||||
return { status: 200, message: "User retrieved successfully", data: userData };
|
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` };
|
|
||||||
}
|
}
|
||||||
|
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 updateUser = async (id: string, body: {
|
export const updateUser = async (
|
||||||
paid_status: string,
|
id: string,
|
||||||
package_expire_date: string,
|
body: {
|
||||||
}) => {
|
paid_status: string;
|
||||||
try {
|
package_expire_date: string;
|
||||||
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 });
|
}
|
||||||
|
) => {
|
||||||
|
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 };
|
return {
|
||||||
|
status: 200,
|
||||||
} catch (error: any) {
|
message: "User updated successfully",
|
||||||
console.error("Error in updateUser:", error.message || error.toString());
|
updateUserData,
|
||||||
return { status: 500, message: `An error occurred while updating the user` };
|
};
|
||||||
}
|
} catch (error: any) {
|
||||||
}
|
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) => {
|
export const generateToken = async (context: any) => {
|
||||||
try {
|
try {
|
||||||
const userId = context?.params?.userId;
|
const userId = context?.params?.userId;
|
||||||
const access_cookie = context?.cookie?.access_token?.value;
|
const access_cookie = context?.cookie?.access_token?.value;
|
||||||
const refresh_cookie = context?.cookie?.refresh_token?.value;
|
const refresh_cookie = context?.cookie?.refresh_token?.value;
|
||||||
|
|
||||||
if (access_cookie !== undefined || refresh_cookie !== undefined) {
|
if (access_cookie !== undefined || refresh_cookie !== undefined) {
|
||||||
const verify = await verifyAuth(context?.cookie);
|
const verify = await verifyAuth(context?.cookie);
|
||||||
return verify;
|
return verify;
|
||||||
|
} else if (
|
||||||
|
access_cookie === undefined &&
|
||||||
|
refresh_cookie === undefined &&
|
||||||
|
userId !== undefined
|
||||||
|
) {
|
||||||
|
const user = await checkUserInDB(userId);
|
||||||
|
if (user?.found === true) {
|
||||||
|
// generate access token
|
||||||
|
const accessToken = jwt.sign({ userId }, ENV.JWT_ACCESS_TOKEN_SECRET, {
|
||||||
|
expiresIn: "3h",
|
||||||
|
});
|
||||||
|
|
||||||
|
// generate refresh token
|
||||||
|
const refreshToken = jwt.sign(
|
||||||
|
{ userId },
|
||||||
|
ENV.JWT_REFRESH_TOKEN_SECRET,
|
||||||
|
{ expiresIn: "7d" }
|
||||||
|
);
|
||||||
|
|
||||||
|
// store refresh token in db
|
||||||
|
const storeRToken = await storeRefreshToken(userId, refreshToken);
|
||||||
|
|
||||||
|
if (storeRToken.status === 200) {
|
||||||
|
context.cookie.access_token.set({
|
||||||
|
value: accessToken,
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true, // Set to true in production
|
||||||
|
sameSite: "none", // Adjust based on your needs
|
||||||
|
path: "/",
|
||||||
|
maxAge: 3 * 60 * 60, // 3 hours in seconds
|
||||||
|
});
|
||||||
|
|
||||||
|
context.cookie.refresh_token.set({
|
||||||
|
value: refreshToken,
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true, // Set to true in production
|
||||||
|
sameSite: "none", // Adjust based on your needs
|
||||||
|
path: "/",
|
||||||
|
maxAge: 7 * 24 * 60 * 60, // 7 days in seconds
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 201,
|
||||||
|
message: "Token generated successfully",
|
||||||
|
token: accessToken,
|
||||||
|
user: user.user,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else if (access_cookie === undefined && refresh_cookie === undefined && userId !== undefined) {
|
return {
|
||||||
const user = await checkUserInDB(userId);
|
status: 500,
|
||||||
if (user?.found === true) {
|
message: "An error occurred while storing the refresh token",
|
||||||
|
};
|
||||||
// generate access token
|
} else {
|
||||||
const accessToken = jwt.sign({ userId }, ENV.JWT_ACCESS_TOKEN_SECRET, { expiresIn: '3h' });
|
return { status: 404, message: "User not found" };
|
||||||
|
}
|
||||||
// generate refresh token
|
} else {
|
||||||
const refreshToken = jwt.sign({ userId }, ENV.JWT_REFRESH_TOKEN_SECRET, { expiresIn: '7d' });
|
return { status: 404, message: "Unauthorized!!!" };
|
||||||
|
|
||||||
// store refresh token in db
|
|
||||||
const storeRToken = await storeRefreshToken(userId, refreshToken);
|
|
||||||
|
|
||||||
if (storeRToken.status === 200) {
|
|
||||||
context.cookie.access_token.set({
|
|
||||||
value: accessToken,
|
|
||||||
httpOnly: true,
|
|
||||||
secure: true, // Set to true in production
|
|
||||||
sameSite: 'none', // Adjust based on your needs
|
|
||||||
path: "/",
|
|
||||||
maxAge: 3 * 60 * 60, // 3 hours in seconds
|
|
||||||
});
|
|
||||||
|
|
||||||
context.cookie.refresh_token.set({
|
|
||||||
value: refreshToken,
|
|
||||||
httpOnly: true,
|
|
||||||
secure: true, // Set to true in production
|
|
||||||
sameSite: 'none', // Adjust based on your needs
|
|
||||||
path: "/",
|
|
||||||
maxAge: 7 * 24 * 60 * 60, // 7 days in seconds
|
|
||||||
});
|
|
||||||
|
|
||||||
return { status: 201, message: "Token generated successfully", token: accessToken, user: user.user };
|
|
||||||
}
|
|
||||||
return { status: 500, message: "An error occurred while storing the refresh token" };
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return { status: 404, message: "User not found" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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` };
|
|
||||||
}
|
}
|
||||||
}
|
} catch (error: any) {
|
||||||
|
console.error("Error in generateToken:", error.message || error.toString());
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
message: `An error occurred while generating the token`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,80 @@
|
||||||
import Elysia, { t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import { generateToken, getUserData, updateUser } from "./auth.controller";
|
import { generateToken, getUserData, updateUser } from "./auth.controller";
|
||||||
import { verifyAuth } from "../../middlewares/auth.middlewares";
|
import { verifyAuth } from "../../middlewares/auth.middlewares";
|
||||||
|
|
||||||
export const authRoute = new Elysia({
|
export const authRoute = new Elysia({ prefix: "/auth" });
|
||||||
prefix: "/auth",
|
|
||||||
tags: ["Auth"],
|
authRoute.get(
|
||||||
|
"/user/:userId",
|
||||||
|
async ({ params: { userId } }) => await getUserData(userId),
|
||||||
|
{
|
||||||
detail: {
|
detail: {
|
||||||
description: "Routes for managing users",
|
tags: ["Auth"],
|
||||||
}
|
summary: "Get user data",
|
||||||
})
|
},
|
||||||
|
|
||||||
authRoute.get("/user/:userId", async ({ params: { userId } }) => await getUserData(userId), {
|
|
||||||
params: t.Object({
|
params: t.Object({
|
||||||
userId: t.String()
|
userId: t.String(),
|
||||||
})
|
}),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
authRoute.post("/user/update/:userId", async ({ params: { userId }, body }) => await updateUser(userId, body), {
|
authRoute.post(
|
||||||
|
"/user/update/:userId",
|
||||||
|
async ({ params: { userId }, body }) => await updateUser(userId, body),
|
||||||
|
{
|
||||||
|
detail: {
|
||||||
|
tags: ["Auth"],
|
||||||
|
summary: "Update user",
|
||||||
|
},
|
||||||
params: t.Object({
|
params: t.Object({
|
||||||
userId: t.String()
|
userId: t.String(),
|
||||||
}),
|
}),
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
paid_status: t.String(),
|
paid_status: t.String(),
|
||||||
package_expire_date: t.String(),
|
package_expire_date: t.String(),
|
||||||
})
|
}),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
authRoute.get("/generate-token/:userId", async (context) => await generateToken(context));
|
authRoute.get(
|
||||||
|
"/generate-token/:userId",
|
||||||
|
async (context) => await generateToken(context),
|
||||||
|
{
|
||||||
|
detail: {
|
||||||
|
tags: ["Auth"],
|
||||||
|
summary: "Generate token",
|
||||||
|
},
|
||||||
|
params: t.Object({
|
||||||
|
userId: t.String(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
authRoute.get("/user/me", async ({ cookie }) => {
|
authRoute.get(
|
||||||
|
"/user/me",
|
||||||
|
async ({ cookie }) => {
|
||||||
const authData = await verifyAuth(cookie);
|
const authData = await verifyAuth(cookie);
|
||||||
if (authData.status !== 200) {
|
if (authData.status !== 200) {
|
||||||
return authData;
|
return authData;
|
||||||
|
} else {
|
||||||
|
const userId = authData.userId;
|
||||||
|
const response = await getUserData(userId);
|
||||||
|
if (response?.status === 200) {
|
||||||
|
return {
|
||||||
|
...response.data,
|
||||||
|
token: authData.token,
|
||||||
|
status: 200,
|
||||||
|
message: "User data fetched successfully",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
},
|
||||||
const userId: string | any = authData.userId;
|
{
|
||||||
const response = await getUserData(userId);
|
detail: {
|
||||||
if (response?.status === 200) {
|
tags: ["Auth"],
|
||||||
return { ...response.data, token: authData.token, status: 200, message: "User data fetched successfully" };
|
summary: "Get current user",
|
||||||
}
|
},
|
||||||
else {
|
}
|
||||||
return response;
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
|
||||||
19
src/api/design/design.controller.ts
Normal file
19
src/api/design/design.controller.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { ENV } from "../../config/env";
|
||||||
|
|
||||||
|
export const getAllDesign = async (token: string) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${ENV.CANVAS_SERVER_URL_DEV}/design`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
console.log(response);
|
||||||
|
return data;
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log(error);
|
||||||
|
return { status: 500, message: error.message, token };
|
||||||
|
}
|
||||||
|
};
|
||||||
24
src/api/design/design.route.ts
Normal file
24
src/api/design/design.route.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import Elysia from "elysia";
|
||||||
|
import { verifyAuth } from "../../middlewares/auth.middlewares";
|
||||||
|
import { getAllDesign } from "./design.controller";
|
||||||
|
|
||||||
|
export const designRoute = new Elysia({
|
||||||
|
prefix: "/design",
|
||||||
|
tags: ["Design"],
|
||||||
|
detail: {
|
||||||
|
description: "Routes for managing designs",
|
||||||
|
}
|
||||||
|
}).derive(async ({ cookie }) => {
|
||||||
|
const authData = await verifyAuth(cookie);
|
||||||
|
return { authData }; // Inject into context
|
||||||
|
})
|
||||||
|
|
||||||
|
designRoute.get("/", async ({ authData }) => {
|
||||||
|
if (authData.status !== 200)
|
||||||
|
return authData;
|
||||||
|
else {
|
||||||
|
const token = authData.token;
|
||||||
|
const response = await getAllDesign(token);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -1,16 +1,27 @@
|
||||||
import Elysia from "elysia";
|
import { Elysia } from "elysia";
|
||||||
import { projectRoutes } from "./project/project.route";
|
import { projectRoutes } from "./project/project.route";
|
||||||
import { uploadRoutes } from "./upload/upload.route";
|
import { uploadRoutes } from "./upload/upload.route";
|
||||||
import { authRoute } from "./auth/auth.route";
|
import { authRoute } from "./auth/auth.route";
|
||||||
import { downloadRoute } from "./downloadCount/download.count.route";
|
import { downloadRoute } from "./downloadCount/download.count.route";
|
||||||
|
import { photoLibraryRoutes } from "./photoLibrary/photo.library.route";
|
||||||
|
import { designRoute } from "./design/design.route";
|
||||||
|
|
||||||
export const api = new Elysia({
|
export const api = new Elysia({ prefix: "" })
|
||||||
prefix: "/api",
|
.get("/", () => {
|
||||||
});
|
console.log("Root endpoint accessed");
|
||||||
api.get("/", () => {
|
return "Hello from PlanPostAI Canvas API";
|
||||||
return "Hello from PlanPostAI Canvas API";
|
})
|
||||||
});
|
.use(authRoute)
|
||||||
api.use(authRoute);
|
.use(projectRoutes)
|
||||||
api.use(projectRoutes);
|
.use(uploadRoutes)
|
||||||
api.use(uploadRoutes);
|
.use(downloadRoute)
|
||||||
api.use(downloadRoute);
|
.use(photoLibraryRoutes)
|
||||||
|
.use(designRoute)
|
||||||
|
.onError(({ code, error, set }) => {
|
||||||
|
console.error(`API Error: ${code}`, error);
|
||||||
|
if (code === "NOT_FOUND") {
|
||||||
|
set.status = 404;
|
||||||
|
return "API Endpoint Not Found";
|
||||||
|
}
|
||||||
|
return "API Error Occurred";
|
||||||
|
});
|
||||||
|
|
|
||||||
21
src/api/photoLibrary/photo.library.controller.ts
Normal file
21
src/api/photoLibrary/photo.library.controller.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { ENV } from "../../config/env";
|
||||||
|
|
||||||
|
export const getPhotos = async (keyword: string, pre_page: number, token: string) => {
|
||||||
|
try {
|
||||||
|
const url = `${ENV.PEXELS_URL}/search?query=${keyword}&per_page=${pre_page}`;
|
||||||
|
const response = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
Authorization: process.env.PEXELS_ACCESS_KEY as string,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return { status: 500, message: "An error occurred while getting the photos", token }
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
return { data, token }
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log("Error in getting photos:", error.message || error.toString());
|
||||||
|
return { status: 500, message: "An error occurred while getting the photos", token };
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/api/photoLibrary/photo.library.route.ts
Normal file
31
src/api/photoLibrary/photo.library.route.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import Elysia, { t } from "elysia";
|
||||||
|
import { getPhotos } from "./photo.library.controller";
|
||||||
|
import { verifyAuth } from "../../middlewares/auth.middlewares";
|
||||||
|
|
||||||
|
export const photoLibraryRoutes = new Elysia({
|
||||||
|
prefix: "/photos",
|
||||||
|
tags: ["Photos"],
|
||||||
|
detail: {
|
||||||
|
description: "Routes for managing photo library",
|
||||||
|
}
|
||||||
|
}).derive(async ({ cookie }) => {
|
||||||
|
const authData = await verifyAuth(cookie);
|
||||||
|
return { authData }; // Inject into context
|
||||||
|
});
|
||||||
|
|
||||||
|
photoLibraryRoutes.get("/", async ({ query, authData
|
||||||
|
}) => {
|
||||||
|
if (authData.status !== 200)
|
||||||
|
return authData;
|
||||||
|
else {
|
||||||
|
const { keyword, per_page } = query;
|
||||||
|
const token = authData.token;
|
||||||
|
const data = await getPhotos(keyword, per_page, token);
|
||||||
|
return { data };
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
query: t.Object({
|
||||||
|
keyword: t.String(),
|
||||||
|
per_page: t.Number(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -6,155 +6,225 @@ import { createBucket } from "../../helper/upload/createBucket";
|
||||||
import { removeBucket } from "../../helper/upload/removeBucket";
|
import { removeBucket } from "../../helper/upload/removeBucket";
|
||||||
|
|
||||||
export const getAllProjects = async (userId: string, token: string) => {
|
export const getAllProjects = async (userId: string, token: string) => {
|
||||||
try {
|
try {
|
||||||
// Fetch all projects for the given user
|
// Fetch all projects for the given user
|
||||||
const allProjects = await db.select({
|
const allProjects = await db
|
||||||
id: projects.id,
|
.select({
|
||||||
name: projects.name,
|
id: projects.id,
|
||||||
description: projects.description,
|
name: projects.name,
|
||||||
preview_url: projects.preview_url,
|
description: projects.description,
|
||||||
object: projects.object,
|
preview_url: projects.preview_url,
|
||||||
}).from(projects).where(eq(projects.userId, userId));
|
object: projects.object,
|
||||||
|
})
|
||||||
|
.from(projects)
|
||||||
|
.where(eq(projects.userId, userId));
|
||||||
|
|
||||||
// Identify projects where 'object' is empty or 'object.objects' is empty
|
// Identify projects where 'object' is empty or 'object.objects' is empty
|
||||||
const projectsToDelete = allProjects.filter(proj =>
|
const projectsToDelete = allProjects.filter(
|
||||||
(proj.object && typeof proj.object === "object" && Object.keys(proj.object).length === 0) ||
|
(proj) =>
|
||||||
(proj.object?.objects && Array.isArray(proj.object.objects) && proj.object.objects.length === 0)
|
(proj.object &&
|
||||||
);
|
typeof proj.object === "object" &&
|
||||||
|
Object.keys(proj.object).length === 0) ||
|
||||||
|
(proj.object?.objects &&
|
||||||
|
Array.isArray(proj.object.objects) &&
|
||||||
|
proj.object.objects.length === 0)
|
||||||
|
);
|
||||||
|
|
||||||
// Delete projects with empty 'object' or empty 'object.objects'
|
// Delete projects with empty 'object' or empty 'object.objects'
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
projectsToDelete.map(async (proj) => {
|
projectsToDelete.map(async (proj) => {
|
||||||
// Step 1: Delete associated uploads first
|
// Step 1: Delete associated uploads first
|
||||||
await db.delete(uploads).where(eq(uploads.projectId, proj.id));
|
await db.delete(uploads).where(eq(uploads.projectId, proj.id));
|
||||||
|
|
||||||
// Step 2: Delete the project itself
|
// Step 2: Delete the project itself
|
||||||
await db.delete(projects).where(eq(projects.id, proj.id));
|
await db.delete(projects).where(eq(projects.id, proj.id));
|
||||||
|
|
||||||
// Step 3: Delete the associated bucket
|
// Step 3: Delete the associated bucket
|
||||||
await removeBucket(proj.id);
|
await removeBucket(proj.id);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get remaining projects
|
// Get remaining projects
|
||||||
const remainingProjects = allProjects.filter(proj =>
|
const remainingProjects = allProjects.filter(
|
||||||
!(
|
(proj) =>
|
||||||
(proj.object && typeof proj.object === "object" && Object.keys(proj.object).length === 0) ||
|
!(
|
||||||
(proj.object?.objects && Array.isArray(proj.object.objects) && proj.object.objects.length === 0)
|
(proj.object &&
|
||||||
)
|
typeof proj.object === "object" &&
|
||||||
);
|
Object.keys(proj.object).length === 0) ||
|
||||||
|
(proj.object?.objects &&
|
||||||
|
Array.isArray(proj.object.objects) &&
|
||||||
|
proj.object.objects.length === 0)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if (remainingProjects.length === 0) {
|
if (remainingProjects.length === 0) {
|
||||||
return { status: 404, message: "No projects found", token };
|
return { status: 404, message: "No projects found", token };
|
||||||
}
|
|
||||||
|
|
||||||
return { status: 200, message: "Projects fetched successfully", data: remainingProjects, token };
|
|
||||||
} catch (error: any) {
|
|
||||||
console.log(error.message);
|
|
||||||
return { status: 500, message: "An error occurred while fetching projects", token };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
message: "Projects fetched successfully",
|
||||||
|
data: remainingProjects,
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log(error.message);
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
message: "An error occurred while fetching projects",
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getEachProjects = async (id: string, token: string) => {
|
export const getEachProjects = async (id: string, token: string) => {
|
||||||
try {
|
try {
|
||||||
const project = await db.select({
|
const project = await db
|
||||||
id: projects.id,
|
.select({
|
||||||
name: projects.name,
|
id: projects.id,
|
||||||
description: projects.description,
|
name: projects.name,
|
||||||
preview_url: projects.preview_url,
|
description: projects.description,
|
||||||
object: projects.object,
|
preview_url: projects.preview_url,
|
||||||
}).from(projects).where(eq(projects.id, id)).limit(1);
|
object: projects.object,
|
||||||
if (project.length === 0) {
|
})
|
||||||
return { status: 404, message: "Project not found", token };
|
.from(projects)
|
||||||
}
|
.where(eq(projects.id, id))
|
||||||
return { status: 200, message: "Project fetched successfully", data: project[0], token };
|
.limit(1);
|
||||||
} catch (error: any) {
|
if (project.length === 0) {
|
||||||
console.log(error.message);
|
return { status: 404, message: "Project not found", token };
|
||||||
return { status: 500, message: "An error occurred while fetching projects", token };
|
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
message: "Project fetched successfully",
|
||||||
|
data: project[0],
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log(error.message);
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
message: "An error occurred while fetching projects",
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createProject = async (userId: string, token: string) => {
|
export const createProject = async (userId: string, token: string) => {
|
||||||
try {
|
try {
|
||||||
const { id } = await createEmptyProject(userId);
|
const { id } = await createEmptyProject(userId);
|
||||||
const bucket = await createBucket(id);
|
const bucket = await createBucket(id);
|
||||||
return { status: 200, message: "New project created successfully", data: { id, bucketName: bucket }, token };
|
return {
|
||||||
|
status: 200,
|
||||||
} catch (error: any) {
|
message: "New project created successfully",
|
||||||
console.log(error.message);
|
data: { id, bucketName: bucket },
|
||||||
return { status: 500, message: "An error occurred while creating projects", token }
|
token,
|
||||||
}
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log(error.message);
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
message: "An error occurred while creating projects",
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateProject = async (id: string, body: any, token: string) => {
|
export const updateProject = async (
|
||||||
try {
|
id: string,
|
||||||
// 1. Validate if project exists
|
body: any,
|
||||||
const existingProject = await db.select().from(projects).where(eq(projects.id, id)).limit(1);
|
token: string,
|
||||||
if (existingProject.length === 0) {
|
user_id: string
|
||||||
return { status: 404, message: "Project not found", token };
|
) => {
|
||||||
}
|
try {
|
||||||
|
// 1. Validate if project exists
|
||||||
const { object, name, description, preview_url } = body;
|
const existingProject = await db
|
||||||
// The preview_url will come from client-side as well, where before updating the project a project capture will be taken and uploaded to the bucket. than the url will be sent to the server.And rest of them are normal process
|
.select()
|
||||||
|
.from(projects)
|
||||||
const updatedProject = await db.update(projects).set({
|
.where(eq(projects.id, id));
|
||||||
object,
|
if (existingProject.length === 0) {
|
||||||
name,
|
return { status: 404, message: "Project not found", token };
|
||||||
description,
|
|
||||||
preview_url
|
|
||||||
}).where(eq(projects.id, id)).returning({
|
|
||||||
id: projects.id,
|
|
||||||
object: projects.object,
|
|
||||||
name: projects.name,
|
|
||||||
description: projects.description,
|
|
||||||
preview_url: projects.preview_url
|
|
||||||
});
|
|
||||||
|
|
||||||
if (updatedProject.length === 0) {
|
|
||||||
return { status: 500, message: "Failed to update the project", token };
|
|
||||||
}
|
|
||||||
return { status: 200, message: "Project updated successfully", data: updatedProject[0], token };
|
|
||||||
} catch (error: any) {
|
|
||||||
console.log("Error updating project:", error.message || error.toString());
|
|
||||||
return { status: 500, message: "An error occurred while updating the project", token };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { object, name, description, preview_url } = body;
|
||||||
|
// The preview_url will come from client-side as well, where before updating the project a project capture will be taken and uploaded to the bucket. than the url will be sent to the server.And rest of them are normal process
|
||||||
|
|
||||||
|
const updatedProject = await db
|
||||||
|
.update(projects)
|
||||||
|
.set({
|
||||||
|
object,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
preview_url,
|
||||||
|
userId: user_id,
|
||||||
|
})
|
||||||
|
.where(eq(projects.id, id))
|
||||||
|
.returning({
|
||||||
|
id: projects.id,
|
||||||
|
object: projects.object,
|
||||||
|
name: projects.name,
|
||||||
|
description: projects.description,
|
||||||
|
preview_url: projects.preview_url,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updatedProject.length === 0) {
|
||||||
|
return { status: 500, message: "Failed to update the project", token };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
message: "Project updated successfully",
|
||||||
|
data: updatedProject[0],
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log("Error updating project:", error.message || error.toString());
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
message: "An error occurred while updating the project",
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteProject = async (id: string, token: string) => {
|
export const deleteProject = async (id: string, token: string) => {
|
||||||
try {
|
try {
|
||||||
const deletedUploads = await db
|
const deletedUploads = await db
|
||||||
.delete(uploads)
|
.delete(uploads)
|
||||||
.where(eq(uploads.projectId, id))
|
.where(eq(uploads.projectId, id))
|
||||||
.returning({ id: uploads.id });
|
.returning({ id: uploads.id });
|
||||||
|
|
||||||
if (deletedUploads.length >= 0) {
|
if (deletedUploads.length >= 0) {
|
||||||
// Step 4: Delete the project
|
// Step 4: Delete the project
|
||||||
const deletedProject = await db
|
const deletedProject = await db
|
||||||
.delete(projects)
|
.delete(projects)
|
||||||
.where(eq(projects.id, id))
|
.where(eq(projects.id, id))
|
||||||
.returning({ id: projects.id });
|
.returning({ id: projects.id });
|
||||||
|
|
||||||
if (deletedProject.length === 0) {
|
if (deletedProject.length === 0) {
|
||||||
return { status: 404, message: "Project not found", token };
|
return { status: 404, message: "Project not found", token };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: Delete the associated bucket
|
// Step 5: Delete the associated bucket
|
||||||
const bucketDeletionResult = await removeBucket(id);
|
const bucketDeletionResult = await removeBucket(id);
|
||||||
|
|
||||||
if (bucketDeletionResult.status !== 200) {
|
if (bucketDeletionResult.status !== 200) {
|
||||||
return {
|
return {
|
||||||
status: bucketDeletionResult.status,
|
status: bucketDeletionResult.status,
|
||||||
message: `Error deleting bucket: ${bucketDeletionResult.message}`,
|
message: `Error deleting bucket: ${bucketDeletionResult.message}`,
|
||||||
token
|
token,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return { status: 200, message: "Project and associated bucket deleted successfully", token };
|
return {
|
||||||
}
|
status: 200,
|
||||||
} catch (error: any) {
|
message: "Project and associated bucket deleted successfully",
|
||||||
console.log("Error in deleteProject:", error.message || error.toString());
|
token,
|
||||||
return { status: 500, message: "An error occurred while deleting the project", token };
|
};
|
||||||
}
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log("Error in deleteProject:", error.message || error.toString());
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
message: "An error occurred while deleting the project",
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,85 +1,104 @@
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import { createProject, deleteProject, getAllProjects, getEachProjects, updateProject } from "./project.controller";
|
import {
|
||||||
|
createProject,
|
||||||
|
deleteProject,
|
||||||
|
getAllProjects,
|
||||||
|
getEachProjects,
|
||||||
|
updateProject,
|
||||||
|
} from "./project.controller";
|
||||||
import { verifyAuth } from "../../middlewares/auth.middlewares";
|
import { verifyAuth } from "../../middlewares/auth.middlewares";
|
||||||
|
|
||||||
export const projectRoutes = new Elysia({
|
export const projectRoutes = new Elysia({
|
||||||
prefix: "/projects",
|
prefix: "/projects",
|
||||||
tags: ["Projects"],
|
tags: ["Projects"],
|
||||||
detail: {
|
detail: {
|
||||||
description: "Routes for managing projects",
|
description: "Routes for managing projects",
|
||||||
}
|
},
|
||||||
}).derive(async ({ cookie }) => {
|
}).derive(async ({ cookie }) => {
|
||||||
const authData = await verifyAuth(cookie);
|
const authData = await verifyAuth(cookie);
|
||||||
return { authData }; // Inject into context
|
return { authData }; // Inject into context
|
||||||
});
|
});
|
||||||
|
|
||||||
projectRoutes.get("/each/:project_id", async ({ params: { project_id }, authData }) => {
|
projectRoutes.get(
|
||||||
if (authData.status !== 200)
|
"/each/:project_id",
|
||||||
return authData;
|
async ({ params: { project_id }, authData }) => {
|
||||||
|
if (authData.status !== 200) return authData;
|
||||||
else {
|
else {
|
||||||
const token = authData.token;
|
const token = authData.token;
|
||||||
const response = await getEachProjects(project_id, token);
|
const response = await getEachProjects(project_id, token);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
params: t.Object({
|
params: t.Object({
|
||||||
project_id: t.String()
|
project_id: t.String(),
|
||||||
})
|
}),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
projectRoutes.get("/", async ({ authData }: any) => {
|
projectRoutes.get("/", async ({ authData }: any) => {
|
||||||
if (authData.status !== 200)
|
if (authData.status !== 200) return authData;
|
||||||
return authData;
|
else {
|
||||||
else {
|
const userId = authData.userId;
|
||||||
const userId = authData.userId;
|
const token = authData.token;
|
||||||
const token = authData.token;
|
const response = await getAllProjects(userId, token);
|
||||||
const response = await getAllProjects(userId, token);
|
return response;
|
||||||
return response;
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
projectRoutes.post("/create", async ({ authData }: any) => {
|
projectRoutes.post("/create", async ({ authData }: any) => {
|
||||||
if (authData.status !== 200)
|
if (authData.status !== 200) return authData;
|
||||||
return authData;
|
else {
|
||||||
else {
|
const userId = authData.userId;
|
||||||
const userId = authData.userId;
|
const token = authData.token;
|
||||||
const token = authData.token;
|
const response = await createProject(userId, token);
|
||||||
const response = await createProject(userId, token);
|
return response;
|
||||||
return response;
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
projectRoutes.put("/update/:project_id", async ({ body, params: { project_id }, authData }) => {
|
projectRoutes.put(
|
||||||
if (authData.status !== 200)
|
"/update/:project_id",
|
||||||
return authData;
|
async ({ body, params: { project_id }, authData }) => {
|
||||||
|
if (authData.status !== 200) return authData;
|
||||||
else {
|
else {
|
||||||
const token = authData.token;
|
const token = authData.token;
|
||||||
const response = await updateProject(project_id, body, token);
|
const user_id = authData?.userId;
|
||||||
return response;
|
// sending user_id to the controller to update the project with the user_id, when user tried to design a existing project from the design project panel
|
||||||
|
const response = await updateProject(
|
||||||
|
project_id,
|
||||||
|
body,
|
||||||
|
token,
|
||||||
|
user_id as string
|
||||||
|
);
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
params: t.Object({
|
params: t.Object({
|
||||||
project_id: t.String()
|
project_id: t.String(),
|
||||||
}),
|
}),
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
object: t.Record(t.String(), t.Any()), // Allows any JSON object
|
object: t.Record(t.String(), t.Any()), // Allows any JSON object
|
||||||
name: t.String(),
|
name: t.String(),
|
||||||
description: t.String(),
|
description: t.String(),
|
||||||
preview_url: t.String(),
|
preview_url: t.String(),
|
||||||
})
|
}),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
projectRoutes.delete("/delete/:project_id", async ({ params: { project_id }, authData }) => {
|
projectRoutes.delete(
|
||||||
if (authData.status !== 200)
|
"/delete/:project_id",
|
||||||
return authData;
|
async ({ params: { project_id }, authData }) => {
|
||||||
|
if (authData.status !== 200) return authData;
|
||||||
else {
|
else {
|
||||||
const token = authData.token;
|
const token = authData.token;
|
||||||
const response = await deleteProject(project_id, token);
|
const response = await deleteProject(project_id, token);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
params: t.Object({
|
params: t.Object({
|
||||||
project_id: t.String()
|
project_id: t.String(),
|
||||||
})
|
}),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
|
||||||
99
src/app.ts
99
src/app.ts
|
|
@ -1,59 +1,52 @@
|
||||||
import { Elysia } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import swagger from '@elysiajs/swagger';
|
import swagger from "@elysiajs/swagger";
|
||||||
|
|
||||||
import { ENV } from "./config/env";
|
|
||||||
import cors from "@elysiajs/cors";
|
import cors from "@elysiajs/cors";
|
||||||
|
import { ENV } from "./config/env";
|
||||||
import { api } from "./api";
|
import { api } from "./api";
|
||||||
|
|
||||||
const allowedOrigins = [
|
const app = new Elysia()
|
||||||
"http://localhost:5175",
|
.use(
|
||||||
"http://localhost:5173",
|
cors({
|
||||||
"https://dashboard.planpostai.com",
|
origin: [
|
||||||
"https://canvas.planpostai.com",
|
"http://localhost:5175",
|
||||||
];
|
"http://localhost:5174",
|
||||||
|
"https://dashboard.planpostai.com",
|
||||||
const app = new Elysia({
|
"https://dev.dashboard.planpostai.com",
|
||||||
prefix: "",
|
"https://canvas.planpostai.com",
|
||||||
tags: ["Default"],
|
"https://canvasdev.planpostai.com",
|
||||||
})
|
|
||||||
.use(cors({
|
|
||||||
origin: allowedOrigins,
|
|
||||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
|
|
||||||
allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With", "Accept", "Origin", "Access-Control-Allow-Origin"],
|
|
||||||
credentials: true,
|
|
||||||
}))
|
|
||||||
.use(swagger({
|
|
||||||
path: "/api/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"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
}
|
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
|
||||||
}))
|
allowedHeaders: [
|
||||||
.onError(({ code, error }) => {
|
"Content-Type",
|
||||||
if (code === 'NOT_FOUND')
|
"Authorization",
|
||||||
return 'Not Found :(';
|
"X-Requested-With",
|
||||||
console.log("hello from app.ts under error");
|
"Accept",
|
||||||
console.error(error)
|
"Origin",
|
||||||
});
|
"Access-Control-Allow-Origin",
|
||||||
|
],
|
||||||
// all routes here
|
credentials: true,
|
||||||
app.use(api);
|
})
|
||||||
|
)
|
||||||
app.listen(ENV.SERVER_PORT, () => {
|
.get("/test", () => "Hello World", {})
|
||||||
console.log(`🦊 Elysia is running at ${ENV.SERVER_URL}:${ENV.SERVER_PORT}`)
|
.use(api)
|
||||||
})
|
.use(
|
||||||
|
swagger({
|
||||||
|
path: "/swagger",
|
||||||
|
documentation: {
|
||||||
|
openapi: "3.1.0",
|
||||||
|
info: {
|
||||||
|
title: "Canvas API",
|
||||||
|
version: "1.0.0",
|
||||||
|
description: "Canvas API Documentation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.listen(ENV.SERVER_PORT);
|
||||||
|
|
||||||
|
app.routes.forEach((route) => {
|
||||||
|
console.log(`Route: ${route.method} ${route.path}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`🦊 Elysia is running at ${ENV.SERVER_URL}`);
|
||||||
|
console.log(`Swagger docs available at ${ENV.SERVER_URL}/swagger`);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
import 'dotenv/config'
|
import "dotenv/config";
|
||||||
|
|
||||||
export const ENV = {
|
export const ENV = {
|
||||||
SERVER_URL: process.env.SERVER_URL,
|
SERVER_URL: process.env.SERVER_URL,
|
||||||
SERVER_PORT: process.env.SERVER_PORT || 5000,
|
SERVER_PORT: process.env.SERVER_PORT || 5000,
|
||||||
DATABASE_URL: process.env.DATABASE_URL,
|
CANVAS_SERVER_URL_DEV: process.env.CANVAS_SERVER_URL_DEV,
|
||||||
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
|
DATABASE_URL: process.env.DATABASE_URL,
|
||||||
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
|
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
|
||||||
MINIO_ENDPOINT: process.env.MINIO_ENDPOINT,
|
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
|
||||||
MINIO_PORT: process.env.MINIO_PORT,
|
MINIO_ENDPOINT: process.env.MINIO_URL,
|
||||||
CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY,
|
MINIO_PORT: process.env.MINIO_PORT,
|
||||||
JWT_ACCESS_TOKEN_SECRET: process.env.JWT_ACCESS_TOKEN_SECRET,
|
CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY,
|
||||||
JWT_REFRESH_TOKEN_SECRET: process.env.JWT_REFRESH_TOKEN_SECRET,
|
JWT_ACCESS_TOKEN_SECRET: process.env.JWT_ACCESS_TOKEN_SECRET,
|
||||||
}
|
JWT_REFRESH_TOKEN_SECRET: process.env.JWT_REFRESH_TOKEN_SECRET,
|
||||||
|
PEXELS_URL: process.env.PEXELS_URL,
|
||||||
|
PEXELS_ACCESS_KEY: process.env.PEXELS_ACCESS_KEY,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@ import { ENV } from "../config/env";
|
||||||
|
|
||||||
export const minioClient = new Client({
|
export const minioClient = new Client({
|
||||||
endPoint: ENV.MINIO_ENDPOINT!.replace("http://", "").replace("https://", ""),
|
endPoint: ENV.MINIO_ENDPOINT!.replace("http://", "").replace("https://", ""),
|
||||||
port: ENV.MINIO_PORT,
|
useSSL: ENV.MINIO_ENDPOINT!.startsWith("https"),
|
||||||
useSSL: false,
|
|
||||||
accessKey: ENV.MINIO_ACCESS_KEY,
|
accessKey: ENV.MINIO_ACCESS_KEY,
|
||||||
secretKey: ENV.MINIO_SECRET_KEY,
|
secretKey: ENV.MINIO_SECRET_KEY,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,59 @@
|
||||||
import { boolean, integer, json, pgTable, text, timestamp, uuid, jsonb } from "drizzle-orm/pg-core";
|
import {
|
||||||
|
boolean,
|
||||||
|
integer,
|
||||||
|
json,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
uuid,
|
||||||
|
jsonb,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
export const users = pgTable("users", {
|
export const users = pgTable("users", {
|
||||||
id: text("user_id").primaryKey().notNull(),
|
id: text("user_id").primaryKey().notNull(),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
lastName: text("last_name"),
|
lastName: text("last_name"),
|
||||||
firstName: text("first_name"),
|
firstName: text("first_name"),
|
||||||
image: text("image"),
|
image: text("image"),
|
||||||
paid_status: text("paid_status"),
|
paid_status: text("paid_status"),
|
||||||
expires_in: text("expires_in"),
|
expires_in: text("expires_in"),
|
||||||
refresh_token: text("refresh_token"),
|
refresh_token: text("refresh_token"),
|
||||||
download_limit: integer("download_limit").notNull().default(10),
|
download_limit: integer("download_limit").notNull().default(10),
|
||||||
downloads_today: jsonb("downloads_today").default({ date: null, count: 0 }),
|
downloads_today: jsonb("downloads_today").default({ date: null, count: 0 }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const projects = pgTable("projects", {
|
export const projects = pgTable("projects", {
|
||||||
id: uuid("project_id").defaultRandom().primaryKey(),
|
id: uuid("project_id").defaultRandom().primaryKey(),
|
||||||
userId: text("user_id").references(() => users.id),
|
userId: text("user_id").references(() => users.id),
|
||||||
object: json(),
|
object: json(),
|
||||||
name: text("name"),
|
name: text("name"),
|
||||||
description: text("description"),
|
description: text("description"),
|
||||||
is_public: boolean("is_active").notNull().default(false),
|
is_public: boolean("is_active").notNull().default(false),
|
||||||
preview_url: text("preview_url"),
|
preview_url: text("preview_url"),
|
||||||
created_at: timestamp("created_at").defaultNow(),
|
created_at: timestamp("created_at").defaultNow(),
|
||||||
updated_at: timestamp("updated_at").defaultNow(),
|
updated_at: timestamp("updated_at").defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const uploads = pgTable("uploads", {
|
export const uploads = pgTable("uploads", {
|
||||||
id: uuid().defaultRandom().primaryKey(),
|
id: uuid().defaultRandom().primaryKey(),
|
||||||
filename: text("filename").notNull(),
|
filename: text("filename").notNull(),
|
||||||
url: text("url").notNull(),
|
url: text("url").notNull(),
|
||||||
projectId: uuid().references(() => projects.id),
|
projectId: uuid().references(() => projects.id),
|
||||||
created_at: timestamp("created_at").defaultNow(),
|
created_at: timestamp("created_at").defaultNow(),
|
||||||
updated_at: timestamp("updated_at").defaultNow(),
|
updated_at: timestamp("updated_at").defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const shapes = pgTable("shapes", {
|
||||||
|
id: uuid("shape_id").defaultRandom().primaryKey(),
|
||||||
|
shapes: text("shapes").notNull(),
|
||||||
|
created_at: timestamp("created_at").defaultNow(),
|
||||||
|
updated_at: timestamp("updated_at").defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const category = pgTable("project_category", {
|
||||||
|
id: uuid("category_id").defaultRandom().primaryKey(),
|
||||||
|
user_id: uuid().references(() => users.id),
|
||||||
|
category: text("category").notNull(),
|
||||||
|
created_at: timestamp("created_at").defaultNow(),
|
||||||
|
updated_at: timestamp("updated_at").defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue