From e9441535ae9225ed2e2a430e5d872f9d4d09f2d3 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Sat, 15 Nov 2025 16:26:27 +0000 Subject: [PATCH] Create /api/gameforge/sprint endpoint cgen-a9bf50fc08a54a73971f02028a847099 --- api/devlink/opportunities.ts | 50 ++++++++++++++++++++++++ api/devlink/profile.ts | 76 ++++++++++++++++++++++++++++++++++++ api/devlink/teams.ts | 53 +++++++++++++++++++++++++ api/gameforge/sprint.ts | 53 +++++++++++++++++++++++++ api/gameforge/tasks.ts | 63 ++++++++++++++++++++++++++++++ api/labs/bounties.ts | 49 +++++++++++++++++++++++ api/labs/ip-portfolio.ts | 66 +++++++++++++++++++++++++++++++ api/labs/publications.ts | 48 +++++++++++++++++++++++ api/labs/research-tracks.ts | 54 +++++++++++++++++++++++++ api/staff/directory.ts | 52 ++++++++++++++++++++++++ api/staff/invoices.ts | 50 ++++++++++++++++++++++++ api/staff/me.ts | 52 ++++++++++++++++++++++++ api/staff/okrs.ts | 55 ++++++++++++++++++++++++++ 13 files changed, 721 insertions(+) create mode 100644 api/devlink/opportunities.ts create mode 100644 api/devlink/profile.ts create mode 100644 api/devlink/teams.ts create mode 100644 api/gameforge/sprint.ts create mode 100644 api/gameforge/tasks.ts create mode 100644 api/labs/bounties.ts create mode 100644 api/labs/ip-portfolio.ts create mode 100644 api/labs/publications.ts create mode 100644 api/labs/research-tracks.ts create mode 100644 api/staff/directory.ts create mode 100644 api/staff/invoices.ts create mode 100644 api/staff/me.ts create mode 100644 api/staff/okrs.ts diff --git a/api/devlink/opportunities.ts b/api/devlink/opportunities.ts new file mode 100644 index 00000000..52a42a54 --- /dev/null +++ b/api/devlink/opportunities.ts @@ -0,0 +1,50 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: opportunities, error } = await supabase + .from("devlink_opportunities") + .select(` + id, + title, + description, + budget, + type, + status, + skills_required, + created_at + `) + .eq("status", "open") + .eq("type", "roblox") + .order("created_at", { ascending: false }); + + if (error) { + console.error("Opportunities fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(opportunities || []), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/devlink/profile.ts b/api/devlink/profile.ts new file mode 100644 index 00000000..9eb80d05 --- /dev/null +++ b/api/devlink/profile.ts @@ -0,0 +1,76 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: profile, error } = await supabase + .from("devlink_profiles") + .select(` + id, + user_id, + username, + profile_views, + creations, + experiences, + certifications, + created_at, + updated_at + `) + .eq("user_id", userData.user.id) + .single(); + + if (error && error.code !== "PGRST116") { + console.error("Profile fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + if (!profile) { + // Create default profile if it doesn't exist + const { data: newProfile, error: createError } = await supabase + .from("devlink_profiles") + .insert([ + { + user_id: userData.user.id, + username: userData.user.user_metadata?.username || userData.user.email?.split("@")[0], + profile_views: 0, + }, + ]) + .select() + .single(); + + if (createError) { + console.error("Profile creation error:", createError); + return new Response(JSON.stringify({ error: createError.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(newProfile), { + headers: { "Content-Type": "application/json" }, + }); + } + + return new Response(JSON.stringify(profile), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/devlink/teams.ts b/api/devlink/teams.ts new file mode 100644 index 00000000..e4400f72 --- /dev/null +++ b/api/devlink/teams.ts @@ -0,0 +1,53 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: teams, error } = await supabase + .from("devlink_teams") + .select(` + id, + name, + description, + members_count, + created_at, + members:devlink_team_members( + id, + user_id, + role, + full_name, + avatar_url + ) + `) + .contains("members", [{ user_id: userData.user.id }]) + .order("created_at", { ascending: false }); + + if (error) { + console.error("Teams fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(teams || []), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/gameforge/sprint.ts b/api/gameforge/sprint.ts new file mode 100644 index 00000000..232da135 --- /dev/null +++ b/api/gameforge/sprint.ts @@ -0,0 +1,53 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: sprint, error } = await supabase + .from("gameforge_sprints") + .select(` + id, + project_id, + title, + phase, + status, + start_date, + end_date, + deadline, + gdd, + scope + `) + .eq("user_id", userData.user.id) + .order("created_at", { ascending: false }) + .limit(1) + .single(); + + if (error && error.code !== "PGRST116") { + console.error("Sprint fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(sprint || null), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/gameforge/tasks.ts b/api/gameforge/tasks.ts new file mode 100644 index 00000000..be8a14c6 --- /dev/null +++ b/api/gameforge/tasks.ts @@ -0,0 +1,63 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const url = new URL(req.url); + const sprintId = url.searchParams.get("sprint_id"); + + let query = supabase + .from("gameforge_tasks") + .select(` + id, + title, + description, + status, + assigned_to:assigned_to_id( + id, + full_name, + avatar_url + ), + priority, + due_date, + created_at + `) + .eq("created_by_id", userData.user.id); + + if (sprintId) { + query = query.eq("sprint_id", sprintId); + } + + const { data: tasks, error } = await query.order("created_at", { + ascending: false, + }); + + if (error) { + console.error("Tasks fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(tasks || []), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/labs/bounties.ts b/api/labs/bounties.ts new file mode 100644 index 00000000..9ffd8fb3 --- /dev/null +++ b/api/labs/bounties.ts @@ -0,0 +1,49 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: bounties, error } = await supabase + .from("labs_bounties") + .select(` + id, + title, + description, + reward, + difficulty, + status, + research_track_id, + created_at + `) + .eq("status", "available") + .order("reward", { ascending: false }); + + if (error) { + console.error("Bounties fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(bounties || []), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/labs/ip-portfolio.ts b/api/labs/ip-portfolio.ts new file mode 100644 index 00000000..6e5c390c --- /dev/null +++ b/api/labs/ip-portfolio.ts @@ -0,0 +1,66 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + // Check if user is admin in labs + const { data: labsAdmin, error: adminError } = await supabase + .from("labs_admin") + .select("id") + .eq("user_id", userData.user.id) + .single(); + + if (adminError && adminError.code !== "PGRST116") { + console.error("Admin check error:", adminError); + } + + const isAdmin = !!labsAdmin; + + // Fetch IP portfolio (all projects' IP counts) + const { data: portfolio, error } = await supabase + .from("labs_ip_portfolio") + .select(` + id, + patents_count, + trademarks_count, + trade_secrets_count, + copyrights_count, + created_at, + updated_at + `) + .single(); + + if (error && error.code !== "PGRST116") { + console.error("IP portfolio fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + const result = { + ...portfolio, + is_admin: isAdmin, + }; + + return new Response(JSON.stringify(result), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/labs/publications.ts b/api/labs/publications.ts new file mode 100644 index 00000000..631eb36a --- /dev/null +++ b/api/labs/publications.ts @@ -0,0 +1,48 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: publications, error } = await supabase + .from("labs_publications") + .select(` + id, + title, + description, + status, + url, + published_date, + research_track_id, + created_at + `) + .order("published_date", { ascending: false }); + + if (error) { + console.error("Publications fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(publications || []), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/labs/research-tracks.ts b/api/labs/research-tracks.ts new file mode 100644 index 00000000..172e370d --- /dev/null +++ b/api/labs/research-tracks.ts @@ -0,0 +1,54 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: tracks, error } = await supabase + .from("labs_research_tracks") + .select(` + id, + title, + description, + status, + progress, + lead_id, + lead:lead_id( + id, + full_name, + avatar_url + ), + publications, + whitepaper_url, + created_at + `) + .order("created_at", { ascending: false }); + + if (error) { + console.error("Research tracks fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(tracks || []), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/staff/directory.ts b/api/staff/directory.ts new file mode 100644 index 00000000..8c126cb3 --- /dev/null +++ b/api/staff/directory.ts @@ -0,0 +1,52 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: directory, error } = await supabase + .from("staff_members") + .select(` + id, + user_id, + full_name, + email, + role, + department, + employment_type, + avatar_url, + phone, + location, + username, + created_at + `) + .order("full_name", { ascending: true }); + + if (error) { + console.error("Directory fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(directory || []), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/staff/invoices.ts b/api/staff/invoices.ts new file mode 100644 index 00000000..4cf72b3f --- /dev/null +++ b/api/staff/invoices.ts @@ -0,0 +1,50 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: invoices, error } = await supabase + .from("contractor_invoices") + .select(` + id, + user_id, + invoice_number, + amount, + status, + date, + due_date, + description, + created_at + `) + .eq("user_id", userData.user.id) + .order("date", { ascending: false }); + + if (error) { + console.error("Invoices fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(invoices || []), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/staff/me.ts b/api/staff/me.ts new file mode 100644 index 00000000..0dd19ec8 --- /dev/null +++ b/api/staff/me.ts @@ -0,0 +1,52 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: staffMember, error } = await supabase + .from("staff_members") + .select(` + id, + user_id, + full_name, + email, + employment_type, + department, + role, + start_date, + salary, + avatar_url, + created_at + `) + .eq("user_id", userData.user.id) + .single(); + + if (error && error.code !== "PGRST116") { + console.error("Staff member fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(staffMember || null), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +}; diff --git a/api/staff/okrs.ts b/api/staff/okrs.ts new file mode 100644 index 00000000..8bd6b3ee --- /dev/null +++ b/api/staff/okrs.ts @@ -0,0 +1,55 @@ +import { supabase } from "../_supabase"; + +export default async (req: Request) => { + if (req.method !== "GET") { + return new Response("Method not allowed", { status: 405 }); + } + + try { + const token = req.headers.get("Authorization")?.replace("Bearer ", ""); + if (!token) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: userData } = await supabase.auth.getUser(token); + if (!userData.user) { + return new Response("Unauthorized", { status: 401 }); + } + + const { data: okrs, error } = await supabase + .from("staff_okrs") + .select(` + id, + user_id, + objective, + description, + status, + quarter, + year, + key_results( + id, + title, + progress, + target_value + ), + created_at + `) + .eq("user_id", userData.user.id) + .order("created_at", { ascending: false }); + + if (error) { + console.error("OKRs fetch error:", error); + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + }); + } + + return new Response(JSON.stringify(okrs || []), { + headers: { "Content-Type": "application/json" }, + }); + } catch (err: any) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +};