Skip to content

Commit

Permalink
Merge branch 'main' into feat/landing-page
Browse files Browse the repository at this point in the history
  • Loading branch information
SkidGod4444 authored Jan 6, 2025
2 parents 7fb6585 + 2a8a5c7 commit 8163c51
Show file tree
Hide file tree
Showing 61 changed files with 3,258 additions and 832 deletions.
17 changes: 17 additions & 0 deletions apps/api/app/actions/checkAdmin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NextResponse } from "next/server";
import { Context, Next } from "hono";

export const checkAdmin = async (c: Context, next: Next) => {
try {
const session = c.get("session");
if (session && session.user && session.user.role === "admin") {
return await next();
}
return NextResponse.json(
{ message: "You are not authorized" },
{ status: 401 },
);
} catch (error) {
console.log("Check admin middleware error", error);
}
};
19 changes: 19 additions & 0 deletions apps/api/app/actions/checkLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { auth } from "@plura/auth";
import { Context, Next } from "hono";
import { NextResponse } from "next/server";

export const checkLogin = async (c: Context, next: Next) => {
try {
const session = await auth.api.getSession({ headers: c.req.raw.headers });
if (session && session.user) {
c.set("session", session);
return await next();
}
return NextResponse.json(
{ message: "You are not authorized" },
{ status: 401 },
);
} catch (error) {
console.log("Check login middleware error", error);
}
};
3 changes: 3 additions & 0 deletions apps/api/app/v1/[[...route]]/mail.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Hono } from "hono";
import { mailBatchSchema, mailSchema } from "@repo/types";
import { zValidator } from "@hono/zod-validator";
import { checkAdmin } from "@/app/actions/checkAdmin";
import { checkLogin } from "@/app/actions/checkLogin";

const app = new Hono()
.use(checkLogin, checkAdmin)
.post("/send", zValidator("json", mailSchema), async (c) => {
const { email, subject } = c.req.valid("json");
const { sendEmail } = await import("@plura/mail");
Expand Down
32 changes: 30 additions & 2 deletions apps/api/app/v1/[[...route]]/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { handle } from "hono/vercel";
import { Hono } from "hono";
import { Context, Hono, Next } from "hono";
import mail from "./mail";
import test from "./test";
import session from "./session";
Expand All @@ -10,6 +10,9 @@ import user from "./user";
import contributors from "./contributors";
import { cors } from "hono/cors";
import workspace from "./workspace";
import { Ratelimit } from "@upstash/ratelimit";
import { auth as Auth } from "@plura/auth";
import { cache } from "@plura/cache";

export const runtime = "edge";

Expand All @@ -36,12 +39,37 @@ app.use(
}),
);

const rateLimitHandler = async (c: Context, next: Next) => {
const session = await Auth.api.getSession({ headers: c.req.raw.headers });

// const limit = process.env.NODE_ENV === "production" ? 60 : 5; // for testing
const limit = process.env.NODE_ENV === "production" ? 60 : 100;

const rateLimit = new Ratelimit({
redis: cache,
limiter: Ratelimit.slidingWindow(limit, "1m"),
analytics: true, // store analytics data in redis db
});

const { success } = await rateLimit.limit(
session?.session.ipAddress || session?.session.userId || "anonymous",
);

if (!success) {
return c.json({ message: "You hit the rate limit" }, 429);
}
return await next();
};

app.route("/health", health);
app.route("/status", status);

app.use(rateLimitHandler);
// apply rate limit to below routes
app.route("/session", session);
app.route("/test", test);
app.route("/mail", mail);
app.route("/auth", auth);
app.route("/status", status);
app.route("/user", user);
app.route("/contributors", contributors);
app.route("/workspace", workspace);
Expand Down
4 changes: 3 additions & 1 deletion apps/api/app/v1/[[...route]]/test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Hono } from "hono";
import { prisma } from "@plura/db";
import { checkAdmin } from "@/app/actions/checkAdmin";
import { checkLogin } from "@/app/actions/checkLogin";

const app = new Hono().get("/", async (c) => {
const app = new Hono().use(checkLogin, checkAdmin).get("/", async (c) => {
const user = await prisma.user.findMany();
return c.json({
user,
Expand Down
90 changes: 46 additions & 44 deletions apps/api/app/v1/[[...route]]/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Hono } from "hono";
import { prisma } from "@plura/db";
import { auth } from "@plura/auth";
import { cache } from "@plura/cache";
import { checkAdmin } from "@/app/actions/checkAdmin";
import { checkLogin } from "@/app/actions/checkLogin";

const CACHE_EXPIRY = 300; // Cache expiry time in seconds

Expand Down Expand Up @@ -64,6 +66,50 @@ const app = new Hono()

return c.json({ user }, 200);
})
.use(checkLogin, checkAdmin)
.get("/:id", async (c) => {
const userId = c.req.param("id");
if (!userId) {
return c.json({
message: "User ID is required",
status: 400,
});
}

const cacheKey = `user:${userId}`;
let user: User | null = null;

try {
const cachedData: User | null = await cache.get(cacheKey);
if (cachedData) {
user = cachedData;
console.log("Returned user data from cache (by ID)");
}
} catch (error) {
console.error("Cache parsing error (by ID):", error);
}

if (!user) {
user = await prisma.user.findUnique({
where: {
id: userId,
},
});

if (user) {
console.log("Fetched user data from database (by ID):");
try {
await cache.set(cacheKey, user, { ex: CACHE_EXPIRY });
} catch (cacheError) {
console.error(
"Error storing user data in cache (by ID):",
cacheError,
);
}
}
}
return c.json({ user }, 200);
})
.get("/all", async (c) => {
const cursor = c.req.query("cursor");
const take = parseInt(c.req.query("take") || "10");
Expand Down Expand Up @@ -108,50 +154,6 @@ const app = new Hono()
}

return c.json(response, 200);
})
.get("/:id", async (c) => {
const userId = c.req.param("id");
if (!userId) {
return c.json({
message: "User ID is required",
status: 400,
});
}

const cacheKey = `user:${userId}`;
let user: User | null = null;

try {
const cachedData: User | null = await cache.get(cacheKey);
if (cachedData) {
user = cachedData;
console.log("Returned user data from cache (by ID)");
}
} catch (error) {
console.error("Cache parsing error (by ID):", error);
}

if (!user) {
user = await prisma.user.findUnique({
where: {
id: userId,
},
});

if (user) {
console.log("Fetched user data from database (by ID):");
try {
await cache.set(cacheKey, user, { ex: CACHE_EXPIRY });
} catch (cacheError) {
console.error(
"Error storing user data in cache (by ID):",
cacheError,
);
}
}
}

return c.json({ user }, 200);
});

export default app;
126 changes: 67 additions & 59 deletions apps/api/app/v1/[[...route]]/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import { Hono } from "hono";
import { workspaceSchema } from "@repo/types";
import { auth } from "@plura/auth";
import { cache } from "@plura/cache";
import { encrypt } from "@plura/crypt";
import { checkLogin } from "@/app/actions/checkLogin";
import { checkAdmin } from "@/app/actions/checkAdmin";

const CACHE_EXPIRY = 300; // Cache expiry time in seconds

const ENCRYPTION_KEY = new Uint8Array(
JSON.parse(process.env.ENCRYPTION_KEY || "[]"),
);
type Workspace = {
id: string;
name: string;
Expand All @@ -17,63 +22,7 @@ type Workspace = {
};

const app = new Hono()
.get("/all", async (c) => {
const cursor = c.req.query("cursor");
const take = parseInt(c.req.query("take") || "10");
const cacheKey = `workspaces:all:${cursor || "start"}:${take}`;
let response: {
nextCursor: string | null;
workspaces: Workspace[];
} | null = null;

try {
const cachedData: {
nextCursor: string | null;
workspaces: Workspace[];
} | null = await cache.get(cacheKey);
if (cachedData) {
response = cachedData;
console.log("Returned workspace list from cache");
}
} catch (error) {
console.error("Cache parsing error (all):", error);
}

if (!response) {
if (!c.req.url.includes("?cursor=")) {
return c.redirect("?cursor=");
}

const workspaces: Workspace[] = await prisma.workspace.findMany({
take,
skip: 1,
cursor: cursor
? {
id: cursor,
}
: undefined,
orderBy: {
createdAt: "asc",
},
});

const nextCursor =
workspaces.length > 0 ? workspaces[workspaces.length - 1].id : null;
response = { nextCursor, workspaces };

console.log("Fetched workspace list from database (all)");
try {
await cache.set(cacheKey, response, { ex: CACHE_EXPIRY });
} catch (cacheError) {
console.error(
"Error storing workspace list in cache (all):",
cacheError,
);
}
}

return c.json(response, 200);
})
.use(checkLogin)
.get("/:id", async (c) => {
const workspaceId = c.req.param("id");
if (!workspaceId) {
Expand Down Expand Up @@ -170,10 +119,11 @@ const app = new Hono()
if (!userId) {
return c.json({ message: "Missing user id", status: 400 }, 400);
}
const name = await encrypt(body.name, ENCRYPTION_KEY);

const workspace = await prisma.workspace.create({
data: {
name: body.name,
name: name.toString(),
userId: userId,
},
});
Expand Down Expand Up @@ -251,6 +201,64 @@ const app = new Hono()
}

return c.json({ deletedWorkspace: workspace }, 200);
})
.use(checkAdmin)
.get("/all", async (c) => {
const cursor = c.req.query("cursor");
const take = parseInt(c.req.query("take") || "10");
const cacheKey = `workspaces:all:${cursor || "start"}:${take}`;
let response: {
nextCursor: string | null;
workspaces: Workspace[];
} | null = null;

try {
const cachedData: {
nextCursor: string | null;
workspaces: Workspace[];
} | null = await cache.get(cacheKey);
if (cachedData) {
response = cachedData;
console.log("Returned workspace list from cache");
}
} catch (error) {
console.error("Cache parsing error (all):", error);
}

if (!response) {
if (!c.req.url.includes("?cursor=")) {
return c.redirect("?cursor=");
}

const workspaces: Workspace[] = await prisma.workspace.findMany({
take,
skip: 1,
cursor: cursor
? {
id: cursor,
}
: undefined,
orderBy: {
createdAt: "asc",
},
});

const nextCursor =
workspaces.length > 0 ? workspaces[workspaces.length - 1].id : null;
response = { nextCursor, workspaces };

console.log("Fetched workspace list from database (all)");
try {
await cache.set(cacheKey, response, { ex: CACHE_EXPIRY });
} catch (cacheError) {
console.error(
"Error storing workspace list in cache (all):",
cacheError,
);
}
}

return c.json(response, 200);
});

export default app;
1 change: 0 additions & 1 deletion apps/api/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.includes("/dashboard")) {
return NextResponse.redirect(redirectUrl);
}
console.log(request.nextUrl.pathname);
return response;
}

Expand Down
Loading

0 comments on commit 8163c51

Please sign in to comment.