refactor: move into api/db packages
This commit is contained in:
@@ -48,14 +48,12 @@
|
|||||||
"@trpc/server": "catalog:",
|
"@trpc/server": "catalog:",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"geist": "^1.3.1",
|
"geist": "^1.3.1",
|
||||||
"ioredis": "^5.9.2",
|
|
||||||
"jose": "^6.1.2",
|
"jose": "^6.1.2",
|
||||||
"next": "15.5.9",
|
"next": "15.5.9",
|
||||||
"next-runtime-env": "^1.7.2",
|
"next-runtime-env": "^1.7.2",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"nextjs-cors": "^2.2.0",
|
"nextjs-cors": "^2.2.0",
|
||||||
"posthog-js": "^1.254.0",
|
"posthog-js": "^1.254.0",
|
||||||
"rate-limiter-flexible": "^9.0.1",
|
|
||||||
"react": "catalog:react18",
|
"react": "catalog:react18",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-contenteditable": "^3.3.7",
|
"react-contenteditable": "^3.3.7",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { toNodeHandler } from "better-auth/node";
|
|||||||
|
|
||||||
import { initAuth } from "@kan/auth/server";
|
import { initAuth } from "@kan/auth/server";
|
||||||
import { createDrizzleClient } from "@kan/db/client";
|
import { createDrizzleClient } from "@kan/db/client";
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
export const config = { api: { bodyParser: false } };
|
export const config = { api: { bodyParser: false } };
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
export default withRateLimit(
|
export default withRateLimit(
|
||||||
{ points: 100, duration: 60 },
|
{ points: 100, duration: 60 },
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
export default withRateLimit(
|
export default withRateLimit(
|
||||||
{ points: 100, duration: 60 },
|
{ points: 100, duration: 60 },
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { env } from "next-runtime-env";
|
|||||||
|
|
||||||
import { createNextApiContext } from "@kan/api/trpc";
|
import { createNextApiContext } from "@kan/api/trpc";
|
||||||
import { createStripeClient } from "@kan/stripe";
|
import { createStripeClient } from "@kan/stripe";
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
export default withRateLimit(
|
export default withRateLimit(
|
||||||
{ points: 100, duration: 60 },
|
{ points: 100, duration: 60 },
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { createNextApiContext } from "@kan/api/trpc";
|
|||||||
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
|
import * as subscriptionRepo from "@kan/db/repository/subscription.repo";
|
||||||
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
||||||
import { createStripeClient } from "@kan/stripe";
|
import { createStripeClient } from "@kan/stripe";
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
const workspaceSlugSchema = z
|
const workspaceSlugSchema = z
|
||||||
.string()
|
.string()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||||||
import { createNextApiContext } from "@kan/api/trpc";
|
import { createNextApiContext } from "@kan/api/trpc";
|
||||||
import { integrations } from "@kan/db/schema";
|
import { integrations } from "@kan/db/schema";
|
||||||
import { addYears } from "date-fns";
|
import { addYears } from "date-fns";
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
export default withRateLimit(
|
export default withRateLimit(
|
||||||
{ points: 100, duration: 60 },
|
{ points: 100, duration: 60 },
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { createNextApiHandler } from "@trpc/server/adapters/next";
|
|||||||
import { appRouter } from "@kan/api/root";
|
import { appRouter } from "@kan/api/root";
|
||||||
import { createTRPCContext } from "@kan/api/trpc";
|
import { createTRPCContext } from "@kan/api/trpc";
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
const nextApiHandler = createNextApiHandler({
|
const nextApiHandler = createNextApiHandler({
|
||||||
router: appRouter,
|
router: appRouter,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { jwtVerify } from "jose";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
const requestSchema = z.object({
|
const requestSchema = z.object({
|
||||||
token: z.string().min(1),
|
token: z.string().min(1),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { env as nextRuntimeEnv } from "next-runtime-env";
|
|||||||
import { createNextApiContext } from "@kan/api/trpc";
|
import { createNextApiContext } from "@kan/api/trpc";
|
||||||
|
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
const allowedContentTypes = ["image/jpeg", "image/png"];
|
const allowedContentTypes = ["image/jpeg", "image/png"];
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { appRouter } from "@kan/api";
|
|||||||
import { createRESTContext } from "@kan/api/trpc";
|
import { createRESTContext } from "@kan/api/trpc";
|
||||||
|
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
export default withRateLimit(
|
export default withRateLimit(
|
||||||
{ points: 100, duration: 60 },
|
{ points: 100, duration: 60 },
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
import { openApiDocument } from "@kan/api/openapi";
|
import { openApiDocument } from "@kan/api/openapi";
|
||||||
import { withRateLimit } from "~/utils/rateLimit";
|
import { withRateLimit } from "@kan/api/utils/rateLimit";
|
||||||
|
|
||||||
export default withRateLimit(
|
export default withRateLimit(
|
||||||
{ points: 100, duration: 60 },
|
{ points: 100, duration: 60 },
|
||||||
|
|||||||
@@ -23,6 +23,10 @@
|
|||||||
"./openapi": {
|
"./openapi": {
|
||||||
"types": "./dist/openapi.d.ts",
|
"types": "./dist/openapi.d.ts",
|
||||||
"default": "./src/openapi.ts"
|
"default": "./src/openapi.ts"
|
||||||
|
},
|
||||||
|
"./utils/rateLimit": {
|
||||||
|
"types": "./dist/utils/rateLimit.d.ts",
|
||||||
|
"default": "./src/utils/rateLimit.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
@@ -43,6 +47,7 @@
|
|||||||
"@kan/shared": "workspace:^",
|
"@kan/shared": "workspace:^",
|
||||||
"@kan/stripe": "workspace:^",
|
"@kan/stripe": "workspace:^",
|
||||||
"@trpc/server": "catalog:",
|
"@trpc/server": "catalog:",
|
||||||
|
"rate-limiter-flexible": "^9.0.1",
|
||||||
"superjson": "2.2.1",
|
"superjson": "2.2.1",
|
||||||
"trpc-to-openapi": "^2.3.2",
|
"trpc-to-openapi": "^2.3.2",
|
||||||
"zod": "catalog:"
|
"zod": "catalog:"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
RateLimiterMemory,
|
RateLimiterMemory,
|
||||||
} from "rate-limiter-flexible";
|
} from "rate-limiter-flexible";
|
||||||
|
|
||||||
import { getRedisClient } from "./redis";
|
import { getRedisClient } from "@kan/db/redis";
|
||||||
|
|
||||||
export interface RateLimitOptions {
|
export interface RateLimitOptions {
|
||||||
points?: number;
|
points?: number;
|
||||||
@@ -13,7 +13,6 @@ export interface RateLimitOptions {
|
|||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const defaultIdentifier = (req: NextApiRequest): string => {
|
const defaultIdentifier = (req: NextApiRequest): string => {
|
||||||
// Try to identify the IP address of the request
|
// Try to identify the IP address of the request
|
||||||
const forwardedFor = req.headers["x-forwarded-for"];
|
const forwardedFor = req.headers["x-forwarded-for"];
|
||||||
@@ -32,7 +31,6 @@ const defaultIdentifier = (req: NextApiRequest): string => {
|
|||||||
return ip;
|
return ip;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_OPTIONS = {
|
||||||
points: 100,
|
points: 100,
|
||||||
duration: 60,
|
duration: 60,
|
||||||
@@ -40,7 +38,6 @@ const DEFAULT_OPTIONS = {
|
|||||||
identifier: defaultIdentifier,
|
identifier: defaultIdentifier,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
||||||
function createRateLimiter(options: RateLimitOptions = {}) {
|
function createRateLimiter(options: RateLimitOptions = {}) {
|
||||||
const redis = getRedisClient();
|
const redis = getRedisClient();
|
||||||
const points = options.points ?? DEFAULT_OPTIONS.points;
|
const points = options.points ?? DEFAULT_OPTIONS.points;
|
||||||
@@ -48,6 +45,7 @@ function createRateLimiter(options: RateLimitOptions = {}) {
|
|||||||
|
|
||||||
// Use Redis if available, otherwise fall back to in-memory storage
|
// Use Redis if available, otherwise fall back to in-memory storage
|
||||||
if (redis) {
|
if (redis) {
|
||||||
|
console.log("Using Redis for rate limiting");
|
||||||
return new RateLimiterRedis({
|
return new RateLimiterRedis({
|
||||||
storeClient: redis,
|
storeClient: redis,
|
||||||
points,
|
points,
|
||||||
@@ -55,6 +53,7 @@ function createRateLimiter(options: RateLimitOptions = {}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Using in-memory for rate limiting");
|
||||||
return new RateLimiterMemory({
|
return new RateLimiterMemory({
|
||||||
points,
|
points,
|
||||||
duration,
|
duration,
|
||||||
@@ -97,3 +96,4 @@ export function withRateLimit(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,6 +23,10 @@
|
|||||||
"./repository/*": {
|
"./repository/*": {
|
||||||
"types": "./dist/repository/*.d.ts",
|
"types": "./dist/repository/*.d.ts",
|
||||||
"default": "./src/repository/*.ts"
|
"default": "./src/repository/*.ts"
|
||||||
|
},
|
||||||
|
"./redis": {
|
||||||
|
"types": "./dist/redis.d.ts",
|
||||||
|
"default": "./src/redis.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
@@ -43,6 +47,7 @@
|
|||||||
"@kan/shared": "workspace:^",
|
"@kan/shared": "workspace:^",
|
||||||
"drizzle-orm": "^0.42.0",
|
"drizzle-orm": "^0.42.0",
|
||||||
"drizzle-zod": "^0.5.1",
|
"drizzle-zod": "^0.5.1",
|
||||||
|
"ioredis": "^5.9.2",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"zod": "catalog:"
|
"zod": "catalog:"
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import Redis from "ioredis";
|
import Redis from "ioredis";
|
||||||
|
|
||||||
import { env } from "~/env";
|
|
||||||
|
|
||||||
let redisClient: Redis | null = null;
|
let redisClient: Redis | null = null;
|
||||||
|
|
||||||
export function getRedisClient(): Redis | null {
|
export function getRedisClient(): Redis | null {
|
||||||
@@ -9,7 +7,7 @@ export function getRedisClient(): Redis | null {
|
|||||||
return redisClient;
|
return redisClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
const redisUrl = env.REDIS_URL;
|
const redisUrl = process.env.REDIS_URL;
|
||||||
|
|
||||||
if (!redisUrl) {
|
if (!redisUrl) {
|
||||||
return null;
|
return null;
|
||||||
@@ -31,4 +29,3 @@ export async function closeRedisClient(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -166,9 +166,6 @@ importers:
|
|||||||
geist:
|
geist:
|
||||||
specifier: ^1.3.1
|
specifier: ^1.3.1
|
||||||
version: 1.4.2(next@15.5.9(@babel/core@7.28.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
version: 1.4.2(next@15.5.9(@babel/core@7.28.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
ioredis:
|
|
||||||
specifier: ^5.9.2
|
|
||||||
version: 5.9.2
|
|
||||||
jose:
|
jose:
|
||||||
specifier: ^6.1.2
|
specifier: ^6.1.2
|
||||||
version: 6.1.2
|
version: 6.1.2
|
||||||
@@ -187,9 +184,6 @@ importers:
|
|||||||
posthog-js:
|
posthog-js:
|
||||||
specifier: ^1.254.0
|
specifier: ^1.254.0
|
||||||
version: 1.261.0
|
version: 1.261.0
|
||||||
rate-limiter-flexible:
|
|
||||||
specifier: ^9.0.1
|
|
||||||
version: 9.0.1
|
|
||||||
react:
|
react:
|
||||||
specifier: catalog:react18
|
specifier: catalog:react18
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
@@ -320,6 +314,9 @@ importers:
|
|||||||
'@trpc/server':
|
'@trpc/server':
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 11.5.0(typescript@5.9.2)
|
version: 11.5.0(typescript@5.9.2)
|
||||||
|
rate-limiter-flexible:
|
||||||
|
specifier: ^9.0.1
|
||||||
|
version: 9.0.1
|
||||||
superjson:
|
superjson:
|
||||||
specifier: 2.2.1
|
specifier: 2.2.1
|
||||||
version: 2.2.1
|
version: 2.2.1
|
||||||
@@ -403,6 +400,9 @@ importers:
|
|||||||
drizzle-zod:
|
drizzle-zod:
|
||||||
specifier: ^0.5.1
|
specifier: ^0.5.1
|
||||||
version: 0.5.1(drizzle-orm@0.42.0(@electric-sql/pglite@0.3.7)(@types/pg@8.15.5)(kysely@0.28.8)(pg@8.16.3))(zod@3.25.76)
|
version: 0.5.1(drizzle-orm@0.42.0(@electric-sql/pglite@0.3.7)(@types/pg@8.15.5)(kysely@0.28.8)(pg@8.16.3))(zod@3.25.76)
|
||||||
|
ioredis:
|
||||||
|
specifier: ^5.9.2
|
||||||
|
version: 5.9.2
|
||||||
pg:
|
pg:
|
||||||
specifier: ^8.11.3
|
specifier: ^8.11.3
|
||||||
version: 8.16.3
|
version: 8.16.3
|
||||||
|
|||||||
Reference in New Issue
Block a user