Skip to content

Commit 68496c6

Browse files
committed
fix(backend): resolve Vercel production ESM module imports
Add .js extensions to relative imports so Node ESM can resolve lib/ modules in serverless (fixes ERR_MODULE_NOT_FOUND on /health). Add vercel.json for monorepo-root and apps/backend deploy layouts, include lib/** in function bundles, and set TypeScript module to NodeNext. Document both Vercel root-directory options in the backend README.
1 parent 4dbdfeb commit 68496c6

13 files changed

Lines changed: 59 additions & 22 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
<img src="apps/extension/public/banner.png" alt="CssHub banner" width="800" />
44

5+
<br />
6+
57
**Automatically sync your [CSSBattle](https://cssbattle.dev) submissions to GitHub.**
68

79
[![Chrome Web Store](https://img.shields.io/badge/Chrome-Web%20Store-4285F4?logo=googlechrome&logoColor=white)](https://chromewebstore.google.com/detail/csshub/jafemcjfpjjdbcfjjfohjfglckbkjbbp)

apps/backend/README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,22 @@ curl http://localhost:3000/api/oauth/github/health
4646

4747
## Deploying on Vercel
4848

49-
Set the project root to **`apps/backend`**, add the same env vars for Preview and Production, and wire the deployment URL into the extension as `VITE_OAUTH_BACKEND_BASE_URL`. For the full monorepo dev loop, CI variables, and OAuth callback notes, see the [extension README](../extension/README.md) (maintainer sections at the bottom).
49+
Use **one** of these layouts (both are supported via `vercel.json` in the repo):
50+
51+
### Option A (recommended): Root Directory = `apps/backend`
52+
53+
1. Vercel project **Root Directory****`apps/backend`**
54+
2. Enable **Include source files outside of the Root Directory** (for `@csshub/shared`)
55+
3. [`apps/backend/vercel.json`](vercel.json) runs `npm ci` from the monorepo root and includes `lib/**` in each function bundle
56+
4. Add env vars; set extension `VITE_OAUTH_BACKEND_BASE_URL` to this deployment URL
57+
58+
### Option B: Root Directory = repository root
59+
60+
If the project root is the monorepo (function paths like `/var/task/apps/backend/...`), use the repo-root [`vercel.json`](../../vercel.json) instead. Same env vars apply.
61+
62+
Relative imports in this package use **`.js` extensions** (required for Node ESM in production). `vercel dev` may work without them locally; production does not.
63+
64+
For the full monorepo dev loop, CI variables, and OAuth callback notes, see the [extension README](../extension/README.md) (maintainer sections at the bottom).
5065

5166
## Security & data handling
5267

apps/backend/api/oauth/github/exchange.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import {
33
githubOAuthTokenRawSchema,
44
oauthExchangeRequestSchema,
55
} from "@csshub/shared";
6-
import { handleCorsPreflight, setCorsHeaders } from "../../../lib/cors";
7-
import { backendEnv } from "../../../lib/env";
8-
import { rejectMethod, getClientIp } from "../../../lib/http";
9-
import { checkRateLimit } from "../../../lib/rateLimit";
10-
import { consumeOAuthState } from "../../../lib/oauthState";
11-
import { isAllowedRedirectUri } from "../../../lib/oauth";
6+
import { handleCorsPreflight, setCorsHeaders } from "../../../lib/cors.js";
7+
import { backendEnv } from "../../../lib/env.js";
8+
import { rejectMethod, getClientIp } from "../../../lib/http.js";
9+
import { checkRateLimit } from "../../../lib/rateLimit.js";
10+
import { consumeOAuthState } from "../../../lib/oauthState.js";
11+
import { isAllowedRedirectUri } from "../../../lib/oauth.js";
1212

1313
const EXCHANGE_RATE_LIMIT = {
1414
limit: 12,

apps/backend/api/oauth/github/health.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { VercelRequest, VercelResponse } from "@vercel/node";
2-
import { handleCorsPreflight, setCorsHeaders } from "../../../lib/cors";
3-
import { rejectMethod } from "../../../lib/http";
4-
import { ensureBackendEnvLoaded } from "../../../lib/loadEnv";
2+
import { handleCorsPreflight, setCorsHeaders } from "../../../lib/cors.js";
3+
import { rejectMethod } from "../../../lib/http.js";
4+
import { ensureBackendEnvLoaded } from "../../../lib/loadEnv.js";
55

66
const REQUIRED_ENV_NAMES = ["GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET"] as const;
77
const OPTIONAL_ENV_NAMES = [

apps/backend/api/oauth/github/state.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { VercelRequest, VercelResponse } from "@vercel/node";
2-
import { handleCorsPreflight, setCorsHeaders } from "../../../lib/cors";
3-
import { rejectMethod, getClientIp } from "../../../lib/http";
4-
import { checkRateLimit } from "../../../lib/rateLimit";
5-
import { issueOAuthState } from "../../../lib/oauthState";
6-
import { backendEnv } from "../../../lib/env";
2+
import { handleCorsPreflight, setCorsHeaders } from "../../../lib/cors.js";
3+
import { rejectMethod, getClientIp } from "../../../lib/http.js";
4+
import { checkRateLimit } from "../../../lib/rateLimit.js";
5+
import { issueOAuthState } from "../../../lib/oauthState.js";
6+
import { backendEnv } from "../../../lib/env.js";
77

88
const STATE_RATE_LIMIT = {
99
limit: 20,

apps/backend/lib/env.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ensureBackendEnvLoaded } from "./loadEnv";
1+
import { ensureBackendEnvLoaded } from "./loadEnv.js";
22

33
ensureBackendEnvLoaded();
44

apps/backend/lib/oauth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { backendEnv } from "./env";
1+
import { backendEnv } from "./env.js";
22

33
const REDIRECT_HOST_SUFFIX = ".chromiumapp.org";
44

apps/backend/lib/oauthState.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { redisClient } from "./redis";
1+
import { redisClient } from "./redis.js";
22

33
const OAUTH_STATE_TTL_SEC = 10 * 60;
44
const memoryStateStore = new Map<string, number>();

apps/backend/lib/rateLimit.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { redisClient } from "./redis";
1+
import { redisClient } from "./redis.js";
22

33
type RateLimitConfig = {
44
key: string;

apps/backend/lib/redis.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Redis } from "@upstash/redis";
2-
import { backendEnv } from "./env";
2+
import { backendEnv } from "./env.js";
33

44
const hasRedisConfig =
55
typeof backendEnv.upstashRedisRestUrl === "string" &&

0 commit comments

Comments
 (0)