Skip to content

Commit

Permalink
feat: update
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminshafii committed Dec 6, 2024
1 parent c0e1986 commit 084479e
Show file tree
Hide file tree
Showing 7 changed files with 1,008 additions and 705 deletions.
14 changes: 7 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

119 changes: 119 additions & 0 deletions web/app/api/cron/reset-tokens/route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { db, UserUsageTable } from "@/drizzle/schema";
import { PRODUCTS } from "@/srm.config";
import { eq } from "drizzle-orm";
import type { Server } from "http";
import { createServer } from "http";
import { NextApiHandler } from "next";
import { GET } from "./route";
import type { SuperTest, Test } from "supertest";
import supertest from "supertest";
/**
* @jest-environment node
*/

describe("Token Reset Cron Job", () => {
const mockUserId = "test-user-123";
const monthlyTokenLimit = 5000 * 1000; // 5M tokens
let server: Server;
let request: SuperTest<Test>;

beforeAll(() => {
const handler: NextApiHandler = (req, res) => {
if (req.method === "GET") {
return GET(req as any);
}
};
server = createServer(handler as any);
request = supertest(server);
});

afterAll((done) => {
server.close(done);
});

beforeEach(async () => {
// Setup test data
await db.insert(UserUsageTable).values({
userId: mockUserId,
subscriptionStatus: "active",
paymentStatus: "paid",
tokenUsage: 1000000, // 1M tokens used
maxTokenUsage: monthlyTokenLimit,
billingCycle: "subscription",
currentPlan: PRODUCTS.SubscriptionMonthly.metadata.plan,
});
});

afterEach(async () => {
// Cleanup test data
await db.delete(UserUsageTable).where(eq(UserUsageTable.userId, mockUserId));
});

it("should reset token usage for active subscribers", async () => {
const response = await request
.get("/api/cron/reset-tokens")
.set("authorization", `Bearer ${process.env.CRON_SECRET}`);

expect(response.status).toBe(200);
expect(response.body).toEqual({
success: true,
message: "Token usage reset successful",
});

// Verify token usage was reset
const userUsage = await db
.select()
.from(UserUsageTable)
.where(eq(UserUsageTable.userId, mockUserId));

expect(userUsage[0].tokenUsage).toBe(0);
expect(userUsage[0].maxTokenUsage).toBe(monthlyTokenLimit);
});

it("should not reset tokens for inactive subscriptions", async () => {
// Update user to inactive
await db
.update(UserUsageTable)
.set({ subscriptionStatus: "inactive" })
.where(eq(UserUsageTable.userId, mockUserId));

const response = await request
.get("/api/cron/reset-tokens")
.set("authorization", `Bearer ${process.env.CRON_SECRET}`);

expect(response.status).toBe(200);

// Verify token usage was not reset
const userUsage = await db
.select()
.from(UserUsageTable)
.where(eq(UserUsageTable.userId, mockUserId));

expect(userUsage[0].tokenUsage).toBe(1000000); // Should remain unchanged
});

it("should return 401 for unauthorized requests", async () => {
const response = await request
.get("/api/cron/reset-tokens")
.set("authorization", "Bearer invalid-token");

expect(response.status).toBe(401);
});

it("should handle database errors gracefully", async () => {
// Mock a database error
jest.spyOn(db, "update").mockRejectedValueOnce(
new Error("Database error") as never
);

const response = await request
.get("/api/cron/reset-tokens")
.set("authorization", `Bearer ${process.env.CRON_SECRET}`);

expect(response.status).toBe(500);
expect(response.body).toEqual({
success: false,
error: "Failed to reset token usage",
});
});
});
45 changes: 45 additions & 0 deletions web/app/api/cron/reset-tokens/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { db, UserUsageTable } from "@/drizzle/schema";
import { PRODUCTS } from "@/srm.config";
import { eq, and } from "drizzle-orm";
import { NextResponse } from "next/server";

export const runtime = "edge";

async function resetTokenUsage() {
const monthlyTokenLimit = 5000 * 1000; // 5M tokens

// Reset tokens for active subscribers with valid plans
await db
.update(UserUsageTable)
.set({
tokenUsage: 0,
maxTokenUsage: monthlyTokenLimit,
})
.where(
and(
eq(UserUsageTable.subscriptionStatus, "active"),
eq(UserUsageTable.paymentStatus, "paid")
)
);

return { success: true, message: "Token usage reset successful" };
}

export async function GET(request: Request) {
try {
// Verify that the request is coming from Vercel Cron
const authHeader = request.headers.get("authorization");
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return new NextResponse("Unauthorized", { status: 401 });
}

const result = await resetTokenUsage();
return NextResponse.json(result);
} catch (error) {
console.error("Error resetting token usage:", error);
return NextResponse.json(
{ success: false, error: "Failed to reset token usage" },
{ status: 500 }
);
}
}
2 changes: 1 addition & 1 deletion web/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const config: Config.InitialOptions = {
// Test environment
testEnvironment: 'node',
// every file that has .test.ts will be run
testMatch: ['**/*.test.ts'],
testMatch: ['**/**/*.test.ts'],
// use jest.setup.js for global setup
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],

Expand Down
36 changes: 19 additions & 17 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,34 @@
"@radix-ui/react-switch": "^1.1.1",
"@stripe/stripe-js": "^3.5.0",
"@testing-library/jest-dom": "^6.6.3",
"@tiptap/core": "^2.9.1",
"@tiptap/starter-kit": "^2.9.1",
"@tiptap/core": "^2.10.3",
"@tiptap/starter-kit": "^2.10.3",
"@types/node": "20.8.5",
"@types/react": "18.2.28",
"@types/react-dom": "18.2.13",
"@unkey/api": "^0.19.5",
"@vercel/postgres": "^0.8.0",
"ai": "^3.4.17",
"ai": "^3.4.33",
"autoprefixer": "10.4.16",
"class-variance-authority": "^0.7.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"concurrently": "^8.2.2",
"date-fns": "^4.1.0",
"dotenv": "^16.4.5",
"dotenv-cli": "^7.4.2",
"dotenv": "^16.4.7",
"dotenv-cli": "^7.4.4",
"drizzle-kit": "^0.22.8",
"drizzle-orm": "^0.31.4",
"electron-is-dev": "^3.0.1",
"eslint": "8.51.0",
"eslint-config-next": "13.5.4",
"lucide-react": "^0.287.0",
"next": "^14.2.16",
"next": "^14.2.20",
"ollama-ai-provider": "^0.15.2",
"openai": "^4.68.2",
"openai": "^4.76.0",
"playwright": "^1.49.0",
"postcss": "8.4.31",
"posthog-js": "^1.174.3",
"posthog-node": "^4.2.1",
"posthog-js": "^1.194.5",
"posthog-node": "^4.3.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"stripe": "^16.12.0",
Expand All @@ -64,19 +64,21 @@
"typescript": "5.2.2",
"wait-on": "^7.2.0",
"wink-bm25-text-search": "^3.1.2",
"wink-eng-lite-web-model": "^1.8.0",
"wink-nlp": "^2.3.0",
"wink-eng-lite-web-model": "^1.8.1",
"wink-nlp": "^2.3.2",
"youtube-transcript": "^1.2.1",
"zod": "^3.23.8"
},
"packageManager": "pnpm@8.15.4",
"devDependencies": {
"@jest/types": "^29.5.0",
"@types/jest": "^29.5.0",
"@jest/types": "^29.6.3",
"@types/jest": "^29.5.14",
"@types/supertest": "^2.0.16",
"@types/uuid": "^10.0.0",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"ts-jest": "^29.1.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"supertest": "^6.3.4",
"ts-jest": "^29.2.5",
"tsx": "^4.19.2",
"uuid": "^11.0.3"
}
Expand Down
Loading

0 comments on commit 084479e

Please sign in to comment.