Remove internal table CRUD APIs
Removed public CRUD for internal tables: - Deleted /api/meta - internal key-value store - Deleted /api/users - use auth endpoints instead - Removed CLI commands: user admin and meta operations Core app tables with CRUD: ✅ tasks - Full CRUD ✅ projects - Full CRUD (list, get, create, update, delete) ✅ sprints - Full CRUD (list, get, create, update, delete) Auth tables (internal, no public CRUD): - users - Managed via /api/auth/* endpoints - sessions - Managed by auth system - password_reset_tokens - Managed by forgot/reset flow - meta - Internal use only
This commit is contained in:
parent
b0fc52ade4
commit
9778c20dbf
127
scripts/gantt.sh
127
scripts/gantt.sh
@ -526,98 +526,6 @@ cmd_auth_users() {
|
||||
api_call GET "/auth/users"
|
||||
}
|
||||
|
||||
#===================
|
||||
# USER ADMIN OPERATIONS
|
||||
#===================
|
||||
|
||||
cmd_user_get() {
|
||||
local user_id="$1"
|
||||
|
||||
if [ -z "$user_id" ]; then
|
||||
log_error "Usage: user get <user-id>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Fetching user $user_id..."
|
||||
api_call GET "/users?id=$user_id" | jq '.user'
|
||||
}
|
||||
|
||||
cmd_user_update() {
|
||||
local user_id="$1"
|
||||
local field="$2"
|
||||
local value="$3"
|
||||
|
||||
if [ -z "$user_id" ] || [ -z "$field" ] || [ -z "$value" ]; then
|
||||
log_error "Usage: user update <user-id> <field> <value>"
|
||||
echo "Fields: name, email, avatar_url"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Updating user $user_id: $field = $value"
|
||||
local data
|
||||
data=$(jq -n --arg id "$user_id" --arg field "$field" --arg value "$value" \
|
||||
'{id: $id, ($field): $value}')
|
||||
api_call PATCH "/users" "$data"
|
||||
}
|
||||
|
||||
cmd_user_delete() {
|
||||
local user_id="$1"
|
||||
|
||||
if [ -z "$user_id" ]; then
|
||||
log_error "Usage: user delete <user-id>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_warn "Deleting user $user_id..."
|
||||
api_call DELETE "/users" "{\"id\": \"$user_id\"}"
|
||||
}
|
||||
|
||||
#===================
|
||||
# META OPERATIONS
|
||||
#===================
|
||||
|
||||
cmd_meta_list() {
|
||||
log_info "Fetching meta entries..."
|
||||
api_call GET "/meta" | jq '.meta'
|
||||
}
|
||||
|
||||
cmd_meta_get() {
|
||||
local key="$1"
|
||||
|
||||
if [ -z "$key" ]; then
|
||||
log_error "Usage: meta get <key>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Fetching meta key: $key"
|
||||
api_call GET "/meta?key=$key" | jq '.meta | .[0]'
|
||||
}
|
||||
|
||||
cmd_meta_set() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
if [ -z "$key" ] || [ -z "$value" ]; then
|
||||
log_error "Usage: meta set <key> <value>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Setting meta $key = $value"
|
||||
api_call POST "/meta" "{\"key\": \"$key\", \"value\": \"$value\"}"
|
||||
}
|
||||
|
||||
cmd_meta_delete() {
|
||||
local key="$1"
|
||||
|
||||
if [ -z "$key" ]; then
|
||||
log_error "Usage: meta delete <key>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_warn "Deleting meta key: $key"
|
||||
api_call DELETE "/meta" "{\"key\": \"$key\"}"
|
||||
}
|
||||
|
||||
#===================
|
||||
# DEBUG OPERATIONS
|
||||
#===================
|
||||
@ -683,19 +591,6 @@ AUTH COMMANDS:
|
||||
auth account <field> <value> Update account (name, email)
|
||||
auth users List all users
|
||||
|
||||
USER ADMIN COMMANDS:
|
||||
user list List all users (same as auth users)
|
||||
user get <user-id> Get specific user
|
||||
user update <id> <field> <val> Update user field
|
||||
Fields: name, email, avatar_url
|
||||
user delete <user-id> Delete a user
|
||||
|
||||
META COMMANDS:
|
||||
meta list List all meta entries
|
||||
meta get <key> Get specific meta value
|
||||
meta set <key> <value> Set meta key-value pair
|
||||
meta delete <key> Delete meta entry
|
||||
|
||||
OTHER COMMANDS:
|
||||
debug Call debug endpoint
|
||||
help Show this help message
|
||||
@ -787,28 +682,6 @@ main() {
|
||||
*) log_error "Unknown auth command: $subcmd"; show_help; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
user|users)
|
||||
local subcmd="${1:-list}"
|
||||
shift || true
|
||||
case "$subcmd" in
|
||||
list|ls) cmd_auth_users "$@" ;; # Reuse auth users list
|
||||
get|show) cmd_user_get "$@" ;;
|
||||
update|set|edit) cmd_user_update "$@" ;;
|
||||
delete|rm|remove) cmd_user_delete "$@" ;;
|
||||
*) log_error "Unknown user command: $subcmd"; show_help; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
meta)
|
||||
local subcmd="${1:-list}"
|
||||
shift || true
|
||||
case "$subcmd" in
|
||||
list|ls) cmd_meta_list "$@" ;;
|
||||
get|show) cmd_meta_get "$@" ;;
|
||||
set|create|update) cmd_meta_set "$@" ;;
|
||||
delete|rm|remove) cmd_meta_delete "$@" ;;
|
||||
*) log_error "Unknown meta command: $subcmd"; show_help; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
debug) cmd_debug "$@" ;;
|
||||
help|--help|-h) show_help ;;
|
||||
*) log_error "Unknown command: $cmd"; show_help; exit 1 ;;
|
||||
|
||||
@ -1,101 +0,0 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getServiceSupabase } from "@/lib/supabase/client";
|
||||
import { getAuthenticatedUser } from "@/lib/server/auth";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
// GET - fetch all meta entries or specific key
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const user = await getAuthenticatedUser();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const key = searchParams.get("key");
|
||||
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
let query = supabase.from("meta").select("*");
|
||||
if (key) {
|
||||
query = query.eq("key", key);
|
||||
}
|
||||
|
||||
const { data, error } = await query.order("key");
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
return NextResponse.json({ meta: data || [] });
|
||||
} catch (error) {
|
||||
console.error(">>> API GET /meta error:", error);
|
||||
return NextResponse.json({ error: "Failed to fetch meta" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// POST - create or update a meta entry
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const user = await getAuthenticatedUser();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { key, value } = body;
|
||||
|
||||
if (!key || typeof key !== "string") {
|
||||
return NextResponse.json({ error: "Missing key" }, { status: 400 });
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
return NextResponse.json({ error: "Missing value" }, { status: 400 });
|
||||
}
|
||||
|
||||
const supabase = getServiceSupabase();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from("meta")
|
||||
.upsert({
|
||||
key,
|
||||
value: String(value),
|
||||
updated_at: now,
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
return NextResponse.json({ success: true, meta: data });
|
||||
} catch (error) {
|
||||
console.error(">>> API POST /meta error:", error);
|
||||
return NextResponse.json({ error: "Failed to save meta" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE - delete a meta entry
|
||||
export async function DELETE(request: Request) {
|
||||
try {
|
||||
const user = await getAuthenticatedUser();
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { key } = await request.json();
|
||||
|
||||
if (!key) {
|
||||
return NextResponse.json({ error: "Missing key" }, { status: 400 });
|
||||
}
|
||||
|
||||
const supabase = getServiceSupabase();
|
||||
const { error } = await supabase.from("meta").delete().eq("key", key);
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error(">>> API DELETE /meta error:", error);
|
||||
return NextResponse.json({ error: "Failed to delete meta" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getServiceSupabase } from "@/lib/supabase/client";
|
||||
import { getAuthenticatedUser } from "@/lib/server/auth";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
// GET - fetch single user by ID
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const currentUser = await getAuthenticatedUser();
|
||||
if (!currentUser) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const id = searchParams.get("id");
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json({ error: "Missing user id" }, { status: 400 });
|
||||
}
|
||||
|
||||
const supabase = getServiceSupabase();
|
||||
const { data, error } = await supabase
|
||||
.from("users")
|
||||
.select("id, name, email, avatar_url, created_at")
|
||||
.eq("id", id)
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
if (error.code === "PGRST116") {
|
||||
return NextResponse.json({ error: "User not found" }, { status: 404 });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return NextResponse.json({ user: data });
|
||||
} catch (error) {
|
||||
console.error(">>> API GET /user error:", error);
|
||||
return NextResponse.json({ error: "Failed to fetch user" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH - update user fields (admin only)
|
||||
export async function PATCH(request: Request) {
|
||||
try {
|
||||
const currentUser = await getAuthenticatedUser();
|
||||
if (!currentUser) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { id, ...updates } = body;
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json({ error: "Missing user id" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Only allow certain fields to be updated
|
||||
const allowedFields = ["name", "email", "avatar_url"];
|
||||
const dbUpdates: Record<string, unknown> = {};
|
||||
|
||||
for (const field of allowedFields) {
|
||||
if (updates[field] !== undefined) {
|
||||
dbUpdates[field] = updates[field];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(dbUpdates).length === 0) {
|
||||
return NextResponse.json({ error: "No valid fields to update" }, { status: 400 });
|
||||
}
|
||||
|
||||
const supabase = getServiceSupabase();
|
||||
const { data, error } = await supabase
|
||||
.from("users")
|
||||
.update(dbUpdates)
|
||||
.eq("id", id)
|
||||
.select("id, name, email, avatar_url, created_at")
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
return NextResponse.json({ success: true, user: data });
|
||||
} catch (error) {
|
||||
console.error(">>> API PATCH /user error:", error);
|
||||
return NextResponse.json({ error: "Failed to update user" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE - delete a user (admin only)
|
||||
export async function DELETE(request: Request) {
|
||||
try {
|
||||
const currentUser = await getAuthenticatedUser();
|
||||
if (!currentUser) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { id } = await request.json();
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json({ error: "Missing user id" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Prevent self-deletion
|
||||
if (id === currentUser.id) {
|
||||
return NextResponse.json({ error: "Cannot delete yourself" }, { status: 400 });
|
||||
}
|
||||
|
||||
const supabase = getServiceSupabase();
|
||||
const { error } = await supabase.from("users").delete().eq("id", id);
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error(">>> API DELETE /user error:", error);
|
||||
return NextResponse.json({ error: "Failed to delete user" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user