Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Grida heavily relies on Supabase (PostgreSQL).
**Web**

- React.js 19
- Next.js 15
- Next.js 16

**UI**

Expand Down
18 changes: 18 additions & 0 deletions apps/backgrounds/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);

export default eslintConfig;
14 changes: 7 additions & 7 deletions apps/backgrounds/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
"private": true,
"homepage": "https://bg.grida.co",
"scripts": {
"dev": "next dev --turbopack",
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint": "eslint",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@next/third-parties": "15.3.8",
"@next/third-parties": "16.1.2",
"@react-three/drei": "^10.0.7",
"@react-three/fiber": "9.1.2",
"clsx": "^2.1.1",
"motion": "^12.11.0",
"next": "15.3.8",
"react": "19.2.1",
"react-dom": "19.2.1",
"next": "16.1.2",
"react": "19.2.3",
"react-dom": "19.2.3",
"shadergradient": "^1.2.14",
"tailwind-merge": "^3.2.0",
"three": "^0.170.0",
Expand All @@ -31,7 +31,7 @@
"@types/react-dom": "^19",
"@types/three": "^0.170.0",
"eslint": "^9",
"eslint-config-next": "15.3.8",
"eslint-config-next": "16.1.2",
"tailwindcss": "^4",
"typescript": "^5"
}
Expand Down
24 changes: 9 additions & 15 deletions apps/backgrounds/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
{
"compilerOptions": {
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
Expand All @@ -14,27 +11,24 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./*"
]
},
"target": "ES2017"
"@/*": ["./*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": [
"node_modules"
]
"exclude": ["node_modules"]
}
28 changes: 15 additions & 13 deletions apps/viewer/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
});

const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);

export default eslintConfig;
12 changes: 6 additions & 6 deletions apps/viewer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint": "eslint",
"typecheck": "tsc --noEmit"
},
"packageManager": "pnpm@10.10.0",
"dependencies": {
"@uidotdev/usehooks": "^2.4.1",
"lucide-react": "^0.511.0",
"next": "15.3.8",
"next": "16.1.2",
"pdfjs-dist": "4.8.69",
"react": "19.2.1",
"react-dom": "19.2.1",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-pageflip": "^2.0.3",
"react-pdf": "^9.2.1"
},
Expand All @@ -27,7 +27,7 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.3.8",
"eslint-config-next": "16.1.2",
"postcss": "^8",
"tailwindcss": "^4",
"typescript": "^5"
Expand Down
11 changes: 9 additions & 2 deletions apps/viewer/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
Expand All @@ -22,6 +22,13 @@
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}
1 change: 1 addition & 0 deletions editor/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ supabase/.temp

# typescript
*.tsbuildinfo
next-env.d.ts

# Playwright
node_modules/
Expand Down
5 changes: 0 additions & 5 deletions editor/AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
# `editor`

**Notes**

- the `next-env.d.ts` file for this project is intentionally included in the git.
this is to avoid running dev/build for typechecking in CI envs.
16 changes: 13 additions & 3 deletions editor/app/(api)/private/customers/[project_id]/with-csv/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type PGCustomerInsert =
Database["grida_ciam_public"]["Views"]["customer_with_tags"]["Row"];

type Params = {
project_id: number;
project_id: string;
};

async function parse_customers_csv(
Expand Down Expand Up @@ -89,7 +89,12 @@ export async function POST(
request: NextRequest,
{ params }: { params: Promise<Params> }
) {
const { project_id } = await params;
const { project_id: project_id_param } = await params;
const project_id = Number(project_id_param);
assert(
Number.isFinite(project_id),
"Invalid project_id (expected a numeric route param)"
);
const formdata = await request.formData();
const searchParams = request.nextUrl.searchParams;
const dryrun = qboolean(searchParams.get("dryrun"));
Expand Down Expand Up @@ -150,7 +155,12 @@ export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<Params> }
) {
const { project_id } = await params;
const { project_id: project_id_param } = await params;
const project_id = Number(project_id_param);
assert(
Number.isFinite(project_id),
"Invalid project_id (expected a numeric route param)"
);
const formdata = await request.formData();
const searchParams = request.nextUrl.searchParams;
const dryrun = qboolean(searchParams.get("dryrun"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ import { createXSBClient } from "@/lib/supabase/server";
import { SupabasePostgRESTOpenApi } from "@/lib/supabase-postgrest";
import assert from "assert";

type Params = { supabase_project_id: number };
type Params = { supabase_project_id: string };

interface Context {
params: Promise<Params>;
}

export async function POST(req: NextRequest, context: Context) {
const xsbClient = await createXSBClient();
const { supabase_project_id } = await context.params;
const { supabase_project_id: supabase_project_id_param } =
await context.params;
const supabase_project_id = Number(supabase_project_id_param);
assert(
Number.isFinite(supabase_project_id),
"Invalid supabase_project_id (expected a numeric route param)"
);
Comment on lines +15 to +21
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's check if the file exists and read its content
cat -n editor/app/\(api\)/private/editor/x-supabase/projects/\[supabase_project_id\]/custom-schema/route.ts

Repository: gridaco/grida

Length of output: 2454


🏁 Script executed:

# Also check the directory structure to understand the context
ls -la editor/app/\(api\)/private/editor/x-supabase/projects/

Repository: gridaco/grida

Length of output: 296


🏁 Script executed:

# Search for how assert is used in the codebase and where it's imported from
rg "import.*assert" --type ts --type tsx -A 2 -B 2

Repository: gridaco/grida

Length of output: 84


🏁 Script executed:

# Look for similar validation patterns in the API routes
rg "NextResponse\.json.*400" --type ts --type tsx -B 3 -A 1 editor/app/

Repository: gridaco/grida

Length of output: 84


🏁 Script executed:

# Search for other route param validations in private API routes
rg "Number\(" editor/app/\(api\)/private --type ts -B 3 -A 3

Repository: gridaco/grida

Length of output: 13801


🏁 Script executed:

# Search for NextResponse.json with 400 status to see the pattern
rg "NextResponse\.json.*status.*40[0-9]" editor/app/\(api\)/private --type ts -B 2 -A 2

Repository: gridaco/grida

Length of output: 3131


🏁 Script executed:

# Look for input validation patterns in this directory
rg "if.*!Number\.isFinite|if.*isNaN" editor/app/ --type ts -B 2 -A 3

Repository: gridaco/grida

Length of output: 9243


🏁 Script executed:

# Check what assert does and how errors are caught
rg "assert\(" editor/app/\(api\)/private --type ts -B 1 -A 1 | head -50

Repository: gridaco/grida

Length of output: 3304


Replace assert() with explicit 400 response for invalid route parameters.
assert() throws uncaught exceptions resulting in 500 errors. Use a proper validation check that returns 400 instead, consistent with other supabase_project_id validations in this directory (see secure-service-key/route.ts).

🛠️ Proposed fix
-  const supabase_project_id = Number(supabase_project_id_param);
-  assert(
-    Number.isFinite(supabase_project_id),
-    "Invalid supabase_project_id (expected a numeric route param)"
-  );
+  const supabase_project_id = Number(supabase_project_id_param);
+  if (!Number.isFinite(supabase_project_id)) {
+    return NextResponse.json(
+      { error: "Invalid supabase_project_id" },
+      { status: 400 }
+    );
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { supabase_project_id: supabase_project_id_param } =
await context.params;
const supabase_project_id = Number(supabase_project_id_param);
assert(
Number.isFinite(supabase_project_id),
"Invalid supabase_project_id (expected a numeric route param)"
);
const { supabase_project_id: supabase_project_id_param } =
await context.params;
const supabase_project_id = Number(supabase_project_id_param);
if (!Number.isFinite(supabase_project_id)) {
return NextResponse.json(
{ error: "Invalid supabase_project_id" },
{ status: 400 }
);
}
🤖 Prompt for AI Agents
In
`@editor/app/`(api)/private/editor/x-supabase/projects/[supabase_project_id]/custom-schema/route.ts
around lines 15 - 21, The current code uses assert() to validate the route param
supabase_project_id (derived from supabase_project_id_param and Number(...) into
supabase_project_id) which throws and yields a 500; replace this with an
explicit validation that checks Number.isFinite(supabase_project_id) and, when
invalid, returns a new Response with a 400 status and a clear error message
(matching the style used in secure-service-key/route.ts) instead of throwing;
keep the rest of the handler logic unchanged and reference the same
supabase_project_id variable for downstream use.


const body: XSupabasePrivateApiTypes.AddSchemaNameRequestData =
await req.json();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ import { DontCastJsonProperties } from "@/types/supabase-ext";
import { notFound } from "next/navigation";
import { NextRequest, NextResponse } from "next/server";

type Params = { supabase_project_id: number };
type Params = { supabase_project_id: string };

interface Context {
params: Promise<Params>;
}

export async function GET(req: NextRequest, context: Context) {
const xsbClient = await createXSBClient();
const { supabase_project_id } = await context.params;
const { supabase_project_id: supabase_project_id_param } =
await context.params;
const supabase_project_id = Number(supabase_project_id_param);
if (!Number.isFinite(supabase_project_id)) return notFound();

const { data: supabase_project, error: rls_err } = await xsbClient
.from("supabase_project")
Expand Down Expand Up @@ -41,7 +44,10 @@ export async function GET(req: NextRequest, context: Context) {

export async function PATCH(req: NextRequest, context: Context) {
const xsbClient = await createXSBClient();
const { supabase_project_id } = await context.params;
const { supabase_project_id: supabase_project_id_param } =
await context.params;
const supabase_project_id = Number(supabase_project_id_param);
if (!Number.isFinite(supabase_project_id)) return notFound();

const { data: supabase_project, error: rls_err } = await xsbClient
.from("supabase_project")
Expand Down Expand Up @@ -118,7 +124,10 @@ export async function PATCH(req: NextRequest, context: Context) {

export async function DELETE(req: NextRequest, context: Context) {
const xsbClient = await createXSBClient();
const { supabase_project_id } = await context.params;
const { supabase_project_id: supabase_project_id_param } =
await context.params;
const supabase_project_id = Number(supabase_project_id_param);
if (!Number.isFinite(supabase_project_id)) return notFound();

const { count, error } = await xsbClient
.from("supabase_project")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ import {
__dangerously_fetch_secure_service_role_key,
} from "@/services/x-supabase";

type Params = { supabase_project_id: number };
type Params = { supabase_project_id: string };

interface Context {
params: Promise<Params>;
}

export async function GET(req: NextRequest, context: Context) {
const xsbClient = await createXSBClient();
const { supabase_project_id } = await context.params;
const { supabase_project_id: supabase_project_id_param } =
await context.params;
const supabase_project_id = Number(supabase_project_id_param);
if (!Number.isFinite(supabase_project_id)) {
return NextResponse.json(
{ error: "Invalid supabase_project_id" },
{ status: 400 }
);
}

// [REQUIRED SECURITY LAYER]
// Security layer - this is secure (protected by RLS).
Expand All @@ -40,7 +48,15 @@ export async function GET(req: NextRequest, context: Context) {

export async function POST(req: NextRequest, context: Context) {
const xsbClient = await createXSBClient();
const { supabase_project_id } = await context.params;
const { supabase_project_id: supabase_project_id_param } =
await context.params;
const supabase_project_id = Number(supabase_project_id_param);
if (!Number.isFinite(supabase_project_id)) {
return NextResponse.json(
{ error: "Invalid supabase_project_id" },
{ status: 400 }
);
}

// [REQUIRED SECURITY LAYER]
// Security layer - this is secure (protected by RLS).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import { createXSupabaseClient } from "@/services/x-supabase";
import { notFound } from "next/navigation";
import { NextRequest, NextResponse } from "next/server";

type Params = { supabase_project_id: number };
type Params = { supabase_project_id: string };

interface Context {
params: Promise<Params>;
}

export async function GET(req: NextRequest, context: Context) {
const xsbClient = await createXSBClient();
const { supabase_project_id } = await context.params;
const { supabase_project_id: supabase_project_id_param } =
await context.params;
const supabase_project_id = Number(supabase_project_id_param);
if (!Number.isFinite(supabase_project_id)) return notFound();

// [REQUIRED] RLS gate
const { data: supabase_project } = await xsbClient
Expand Down
Loading