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
13 changes: 3 additions & 10 deletions src/app/api/capture/process/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createClient } from '@/lib/supabase/server';
import { createAdminClient } from '@/lib/supabase/admin';
import { withAuth } from '@/lib/api/withAuth';
import { runExtraction, runMeetingExtraction, runDocumentExtraction, type GoalContext } from '@/lib/agents/extraction';
import type { AttachmentContent } from '@/lib/agents/extraction';
import { getCaptureType } from '@/lib/config/captureTypes';
Expand All @@ -9,14 +9,7 @@ import type { MeetingExtraction } from '@/lib/types/nodes';
export const maxDuration = 300;
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
const supabase = await createClient();

const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

export const POST = withAuth(async ({ request, user, supabase }) => {
const { node_id } = await request.json();

if (!node_id) {
Expand Down Expand Up @@ -342,4 +335,4 @@ export async function POST(request: Request) {

return NextResponse.json({ error: errorMessage }, { status: 500 });
}
}
});
13 changes: 3 additions & 10 deletions src/app/api/capture/route.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { createClient } from '@/lib/supabase/server';
import { withAuth } from '@/lib/api/withAuth';
import { NextResponse, after } from 'next/server';
import { isOwnedStoragePath } from '@/lib/files/storagePath';

export async function POST(request: Request) {
const supabase = await createClient();

const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

export const POST = withAuth(async ({ request, user, supabase }) => {
const body = await request.json();
const {
title,
Expand Down Expand Up @@ -104,4 +97,4 @@ export async function POST(request: Request) {
});

return NextResponse.json({ data: node }, { status: 201 });
}
});
13 changes: 3 additions & 10 deletions src/app/api/convergence/snapshot/route.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import { createClient } from '@/lib/supabase/server';
import { withAuth } from '@/lib/api/withAuth';
import { computeConvergenceScore } from '@/lib/graph/convergence';
import { NextResponse } from 'next/server';
import type { Node } from '@/lib/types/nodes';
import type { Edge } from '@/lib/types/edges';

export async function POST(request: Request) {
export const POST = withAuth(async ({ request, user, supabase }) => {
try {
const supabase = await createClient();

const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const body = await request.json();
const { goal_space_id, all } = body as { goal_space_id?: string; all?: boolean };

Expand Down Expand Up @@ -90,4 +83,4 @@ export async function POST(request: Request) {
const message = error instanceof Error ? error.message : 'Unknown error';
return NextResponse.json({ error: message }, { status: 500 });
}
}
});
12 changes: 3 additions & 9 deletions src/app/api/convergence/snapshots/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createClient } from '@/lib/supabase/server';
import { withAuth } from '@/lib/api/withAuth';
import { NextResponse } from 'next/server';
import type { FactorBreakdown } from '@/lib/graph/convergence';

export async function GET(request: Request) {
export const GET = withAuth(async ({ request, supabase }) => {
try {
const { searchParams } = new URL(request.url);
const goalSpaceId = searchParams.get('goal_space_id');
Expand All @@ -11,12 +11,6 @@ export async function GET(request: Request) {
return NextResponse.json({ error: 'goal_space_id required' }, { status: 400 });
}

const supabase = await createClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

// Query 1: Latest snapshot (for badge + breakdown)
const { data: latestRow } = await supabase
.from('convergence_snapshots')
Expand Down Expand Up @@ -55,4 +49,4 @@ export async function GET(request: Request) {
const message = error instanceof Error ? error.message : 'Unknown error';
return NextResponse.json({ error: message }, { status: 500 });
}
}
});
6 changes: 3 additions & 3 deletions src/app/api/distill/__tests__/candidates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ describe('GET /api/distill/candidates', () => {

it('returns 401 when unauthenticated', async () => {
mockGetUser.mockResolvedValue({ data: { user: null }, error: new Error('Unauthorized') });
const res = await GET();
const res = await GET(new Request('http://t', { method: 'GET' }));
expect(res.status).toBe(401);
});

it('returns enriched candidates with node details', async () => {
const res = await GET();
const res = await GET(new Request('http://t', { method: 'GET' }));
expect(res.status).toBe(200);
const body = await res.json() as { data: Array<{ id: string; nodes: Array<{ id: string }> }> };
expect(body.data).toHaveLength(1);
Expand All @@ -88,7 +88,7 @@ describe('GET /api/distill/candidates', () => {
}),
}),
});
const res = await GET();
const res = await GET(new Request('http://t', { method: 'GET' }));
expect(res.status).toBe(200);
const body = await res.json() as { data: unknown[] };
expect(body.data).toHaveLength(0);
Expand Down
18 changes: 5 additions & 13 deletions src/app/api/distill/candidates/route.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { createClient } from '@/lib/supabase/server';
import { NextResponse } from 'next/server';
import { withAuth } from '@/lib/api/withAuth';
import { z } from 'zod';

export async function GET(): Promise<Response> {
const supabase = await createClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

export const GET = withAuth(async ({ user, supabase }) => {
const { data: candidates, error } = await supabase
.from('distillation_candidates')
.select('id, node_ids, merged_title, merged_summary, merged_node_type, rationale, created_at')
Expand All @@ -31,7 +27,7 @@ export async function GET(): Promise<Response> {
}));

return NextResponse.json({ data: enriched });
}
});

const uuidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;

Expand All @@ -40,11 +36,7 @@ const actionSchema = z.object({
action: z.enum(['accept', 'reject']),
});

export async function PATCH(request: Request): Promise<Response> {
const supabase = await createClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

export const PATCH = withAuth(async ({ user, supabase, request }) => {
let body: unknown;
try { body = await request.json(); } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }); }

Expand Down Expand Up @@ -121,4 +113,4 @@ export async function PATCH(request: Request): Promise<Response> {
}

return NextResponse.json({ data: { action: 'accepted', node_id: newNode.id } });
}
});
16 changes: 3 additions & 13 deletions src/app/api/edges/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
import { createClient } from '@/lib/supabase/server';
import { withAuth } from '@/lib/api/withAuth';
import { NextResponse } from 'next/server';

export async function DELETE(
_request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const supabase = await createClient();

const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

export const DELETE = withAuth<{ id: string }>(async ({ params, supabase }) => {
const { id } = await params;

const { error } = await supabase
Expand All @@ -24,4 +14,4 @@ export async function DELETE(
}

return new NextResponse(null, { status: 204 });
}
});
9 changes: 3 additions & 6 deletions src/app/api/feedback/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createClient } from '@/lib/supabase/server';
import { withAuth } from '@/lib/api/withAuth';
import { NextResponse, after } from 'next/server';
import { z } from 'zod';
import { applyCorrection } from '@/lib/correction/agent';
Expand Down Expand Up @@ -54,11 +55,7 @@ function extractGeneratedText(sourceType: SourceType, record: Record<string, unk
return mr ? JSON.stringify(mr) : '';
}

export async function POST(request: Request): Promise<Response> {
const supabase = await createClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

export const POST = withAuth(async ({ request, user, supabase }) => {
let body: unknown;
try { body = await request.json(); } catch {
return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });
Expand Down Expand Up @@ -110,4 +107,4 @@ export async function POST(request: Request): Promise<Response> {
{ id: feedbackId, created_at: (feedback as Record<string, unknown>)['created_at'] },
{ status: 201 }
);
}
});
9 changes: 3 additions & 6 deletions src/app/api/graph/edges/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createClient } from '@/lib/supabase/server';
import { withAuth } from '@/lib/api/withAuth';
import { NextResponse } from 'next/server';

export async function GET() {
Expand All @@ -15,11 +16,7 @@ export async function GET() {
return NextResponse.json({ data });
}

export async function POST(request: Request) {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

export const POST = withAuth(async ({ request, user, supabase }) => {
const body = await request.json();
const { data, error } = await supabase
.from('edges')
Expand All @@ -37,4 +34,4 @@ export async function POST(request: Request) {
});

return NextResponse.json({ data }, { status: 201 });
}
});
9 changes: 3 additions & 6 deletions src/app/api/graph/nodes/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createClient } from '@/lib/supabase/server';
import { withAuth } from '@/lib/api/withAuth';
import { computeConvergenceScore, shouldTriggerSnapshot } from '@/lib/graph/convergence';
import { NextResponse } from 'next/server';
import { nodeCreateSchema } from '@/lib/api/nodeInput';
Expand Down Expand Up @@ -101,11 +102,7 @@ export async function GET(request: Request) {
return NextResponse.json({ data });
}

export async function POST(request: Request) {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

export const POST = withAuth(async ({ request, user, supabase }) => {
const parsed = nodeCreateSchema.safeParse(await request.json());
if (!parsed.success) {
return NextResponse.json(
Expand All @@ -126,4 +123,4 @@ export async function POST(request: Request) {
void checkAndTriggerSnapshots(supabase, user.id);

return NextResponse.json({ data }, { status: 201 });
}
});
10 changes: 3 additions & 7 deletions src/app/api/lifecycle/promote/route.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { createClient } from '@/lib/supabase/server';
import { NextResponse } from 'next/server';
import { withAuth } from '@/lib/api/withAuth';
import { checkHunchPromotion } from '@/lib/lifecycle/autoPromote';

export async function POST(): Promise<Response> {
const supabase = await createClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

export const POST = withAuth(async ({ user, supabase }) => {
const { data: hunches, error } = await supabase
.from('nodes')
.select('id, lifecycle_stage')
Expand Down Expand Up @@ -50,4 +46,4 @@ export async function POST(): Promise<Response> {
}

return NextResponse.json({ data: { promoted: promoted.length, ids: promoted, failed: failed.length } });
}
});
10 changes: 3 additions & 7 deletions src/app/api/lifecycle/stage/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createClient } from '@/lib/supabase/server';
import { NextResponse } from 'next/server';
import { withAuth } from '@/lib/api/withAuth';
import { z } from 'zod';

const VALID_STAGES = ['hypothesis', 'uncertainty', 'navigation', 'coherence', 'holding', 'archived'] as const;
Expand All @@ -10,11 +10,7 @@ const schema = z.object({
reason: z.string().max(500).optional(),
});

export async function PATCH(request: Request): Promise<Response> {
const supabase = await createClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

export const PATCH = withAuth(async ({ user, supabase, request }) => {
let body: unknown;
try {
body = await request.json();
Expand Down Expand Up @@ -47,4 +43,4 @@ export async function PATCH(request: Request): Promise<Response> {
});

return NextResponse.json({ data: { node_id, stage } });
}
});
18 changes: 5 additions & 13 deletions src/app/api/newsletters/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createClient } from '@/lib/supabase/server';
import { NextResponse } from 'next/server';
import { withAuth } from '@/lib/api/withAuth';
import { z } from 'zod';
import { callLLM } from '@/lib/llm';
import { selectMissionPathwaysNodes, selectCloseContactsNodes } from '@/lib/newsletter/select';
Expand All @@ -13,11 +13,7 @@ import {
const typeSchema = z.enum(['mission_pathways', 'close_contacts']);
const postSchema = z.object({ type: typeSchema });

export async function GET(request: Request): Promise<Response> {
const supabase = await createClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

export const GET = withAuth(async ({ user, supabase, request }) => {
const { searchParams } = new URL(request.url);
const typeResult = typeSchema.safeParse(searchParams.get('type'));
if (!typeResult.success) return NextResponse.json({ error: 'Invalid type' }, { status: 400 });
Expand All @@ -33,13 +29,9 @@ export async function GET(request: Request): Promise<Response> {
if (error) return NextResponse.json({ error: 'Failed to load newsletters' }, { status: 500 });

return NextResponse.json({ data: data ?? [] });
}

export async function POST(request: Request): Promise<Response> {
const supabase = await createClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
});

export const POST = withAuth(async ({ user, supabase, request }) => {
let body: unknown;
try { body = await request.json(); } catch {
return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });
Expand Down Expand Up @@ -95,4 +87,4 @@ export async function POST(request: Request): Promise<Response> {
}

return NextResponse.json({ data: newsletter }, { status: 201 });
}
});
16 changes: 3 additions & 13 deletions src/app/api/nodes/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createClient } from '@/lib/supabase/server';
import { NextResponse } from 'next/server';
import { withAuth } from '@/lib/api/withAuth';
import type { NodeStatus, ConfidenceBasis } from '@/lib/types/nodes';

const ALLOWED_STATUSES: readonly NodeStatus[] = ['promoted', 'archived', 'falsified', 'suspended'];
Expand All @@ -12,17 +12,7 @@ const ALLOWED_CONFIDENCE_BASES: readonly ConfidenceBasis[] = [
'strong_evidence',
];

export async function PATCH(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const supabase = await createClient();

const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

export const PATCH = withAuth<{ id: string }>(async ({ request, supabase, params }) => {
const { id } = await params;

let body: Record<string, unknown>;
Expand Down Expand Up @@ -110,4 +100,4 @@ export async function PATCH(
}

return NextResponse.json({ data });
}
});
Loading
Loading