Add individual get endpoints for projects and sprints

New API endpoints:
- GET /api/projects/[id] - Get single project
- GET /api/sprints/[id] - Get single sprint

New CLI commands:
- project get <id> - Get specific project
- sprint get <id> - Get specific sprint

Project and Sprint CRUD now complete:
 list, get, create, update, delete
This commit is contained in:
Max 2026-02-21 17:50:42 -06:00
parent f24b230703
commit 873712e037
3 changed files with 106 additions and 0 deletions

View File

@ -295,6 +295,18 @@ cmd_project_list() {
api_call GET "/projects" | jq '.projects'
}
cmd_project_get() {
local project_id="$1"
if [ -z "$project_id" ]; then
log_error "Usage: project get <project-id>"
exit 1
fi
log_info "Fetching project $project_id..."
api_call GET "/projects/$project_id" | jq '.project'
}
cmd_project_create() {
local name="$1"
local description="${2:-}"
@ -351,6 +363,18 @@ cmd_sprint_list() {
api_call GET "/sprints" | jq '.sprints'
}
cmd_sprint_get() {
local sprint_id="$1"
if [ -z "$sprint_id" ]; then
log_error "Usage: sprint get <sprint-id>"
exit 1
fi
log_info "Fetching sprint $sprint_id..."
api_call GET "/sprints/$sprint_id" | jq '.sprint'
}
cmd_sprint_create() {
local name="$1"
local project_id="$2"
@ -630,6 +654,7 @@ TASK COMMANDS:
PROJECT COMMANDS:
project list List all projects
project get <id> Get specific project
project create <name> [desc] [color]
Create new project
project update <id> <field> <val>
@ -639,6 +664,7 @@ PROJECT COMMANDS:
SPRINT COMMANDS:
sprint list List all sprints
sprint get <id> Get specific sprint
sprint create <name> <project-id> [start] [end] [goal]
Create new sprint
sprint update <id> <field> <val>
@ -727,6 +753,7 @@ main() {
shift || true
case "$subcmd" in
list|ls) cmd_project_list "$@" ;;
get|show) cmd_project_get "$@" ;;
create|new|add) cmd_project_create "$@" ;;
update|set|edit) cmd_project_update "$@" ;;
delete|rm|remove) cmd_project_delete "$@" ;;
@ -738,6 +765,7 @@ main() {
shift || true
case "$subcmd" in
list|ls) cmd_sprint_list "$@" ;;
get|show) cmd_sprint_get "$@" ;;
create|new|add) cmd_sprint_create "$@" ;;
update|set|edit) cmd_sprint_update "$@" ;;
delete|rm|remove) cmd_sprint_delete "$@" ;;

View File

@ -0,0 +1,39 @@
import { NextResponse } from "next/server";
import { getServiceSupabase } from "@/lib/supabase/client";
import { getAuthenticatedUser } from "@/lib/server/auth";
export const runtime = "nodejs";
// GET - fetch single project by ID
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { id } = await params;
const supabase = getServiceSupabase();
const { data, error } = await supabase
.from("projects")
.select("*")
.eq("id", id)
.single();
if (error) {
if (error.code === "PGRST116") {
return NextResponse.json({ error: "Project not found" }, { status: 404 });
}
throw error;
}
return NextResponse.json({ project: data });
} catch (error) {
console.error(">>> API GET /projects/[id] error:", error);
return NextResponse.json({ error: "Failed to fetch project" }, { status: 500 });
}
}

View File

@ -0,0 +1,39 @@
import { NextResponse } from "next/server";
import { getServiceSupabase } from "@/lib/supabase/client";
import { getAuthenticatedUser } from "@/lib/server/auth";
export const runtime = "nodejs";
// GET - fetch single sprint by ID
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const user = await getAuthenticatedUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { id } = await params;
const supabase = getServiceSupabase();
const { data, error } = await supabase
.from("sprints")
.select("*")
.eq("id", id)
.single();
if (error) {
if (error.code === "PGRST116") {
return NextResponse.json({ error: "Sprint not found" }, { status: 404 });
}
throw error;
}
return NextResponse.json({ sprint: data });
} catch (error) {
console.error(">>> API GET /sprints/[id] error:", error);
return NextResponse.json({ error: "Failed to fetch sprint" }, { status: 500 });
}
}