377 lines
10 KiB
Bash
Executable File
377 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
||
#
|
||
# Project CLI for Gantt Board
|
||
# CRUD operations for projects
|
||
# Usage: ./project.sh [create|list|get|update|delete] [options]
|
||
|
||
set -e
|
||
|
||
# Configuration
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
SUPABASE_URL="https://qnatchrjlpehiijwtreh.supabase.co"
|
||
SERVICE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFuYXRjaHJqbHBlaGlpand0cmVoIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc3MTY0MDQzNiwiZXhwIjoyMDg3MjE2NDM2fQ.rHoc3NfL59S4lejU4-ArSzox1krQkQG-TnfXb6sslm0"
|
||
HEADERS=(-H "apikey: $SERVICE_KEY" -H "Authorization: Bearer $SERVICE_KEY" -H "Content-Type: application/json")
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Helper Functions
|
||
log_info() { echo -e "${BLUE}ℹ${NC} $1"; }
|
||
log_success() { echo -e "${GREEN}✓${NC} $1"; }
|
||
log_warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
||
log_error() { echo -e "${RED}✗${NC} $1"; }
|
||
|
||
# Check dependencies
|
||
check_dependencies() {
|
||
if ! command -v jq &> /dev/null; then
|
||
log_error "jq is required but not installed. Install with: brew install jq"
|
||
exit 1
|
||
fi
|
||
if ! command -v uuidgen &> /dev/null; then
|
||
log_error "uuidgen is required but not installed."
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Generate UUID
|
||
generate_uuid() {
|
||
uuidgen | tr '[:upper:]' '[:lower:]'
|
||
}
|
||
|
||
# Get current timestamp ISO format
|
||
get_timestamp() {
|
||
date -u +"%Y-%m-%dT%H:%M:%SZ"
|
||
}
|
||
|
||
# Show usage
|
||
show_usage() {
|
||
cat << EOF
|
||
Project CLI for Gantt Board
|
||
|
||
USAGE:
|
||
./project.sh [COMMAND] [OPTIONS]
|
||
|
||
COMMANDS:
|
||
create Create a new project
|
||
list List all projects
|
||
get <id> Get a specific project by ID or name
|
||
update <id> Update a project
|
||
delete <id> Delete a project
|
||
|
||
CREATE OPTIONS:
|
||
--name "Name" Project name (required)
|
||
--description "Description" Project description
|
||
--color "#hexcolor" Project color for UI
|
||
|
||
LIST OPTIONS:
|
||
--json Output as JSON
|
||
|
||
UPDATE OPTIONS:
|
||
--name "Name"
|
||
--description "Description"
|
||
--color "#hexcolor"
|
||
|
||
EXAMPLES:
|
||
# Create project
|
||
./project.sh create --name "Web Projects" --description "All web development work"
|
||
|
||
# List all active projects
|
||
./project.sh list
|
||
|
||
# Get project by ID
|
||
./project.sh get a1b2c3d4-0001-0000-0000-000000000001
|
||
|
||
# Update project
|
||
./project.sh update a1b2c3d4-0001-0000-0000-000000000001 --status archived
|
||
|
||
# Delete project
|
||
./project.sh delete a1b2c3d4-0001-0000-0000-000000000001
|
||
|
||
EOF
|
||
}
|
||
|
||
# Resolve project name to ID
|
||
resolve_project_id() {
|
||
local identifier="$1"
|
||
|
||
# Check if it's already a UUID
|
||
if [[ "$identifier" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]]; then
|
||
echo "$identifier"
|
||
return 0
|
||
fi
|
||
|
||
# Search by name (case-insensitive)
|
||
local project_id
|
||
project_id=$(curl -s "${SUPABASE_URL}/rest/v1/projects?name=ilike.*${identifier}*&select=id" \
|
||
"${HEADERS[@]}" | jq -r '.[0].id // empty')
|
||
|
||
if [[ -n "$project_id" ]]; then
|
||
echo "$project_id"
|
||
return 0
|
||
fi
|
||
|
||
# Try exact match
|
||
local encoded_name
|
||
encoded_name=$(printf '%s' "$identifier" | jq -sRr @uri)
|
||
project_id=$(curl -s "${SUPABASE_URL}/rest/v1/projects?name=eq.${encoded_name}&select=id" \
|
||
"${HEADERS[@]}" | jq -r '.[0].id // empty')
|
||
|
||
if [[ -n "$project_id" ]]; then
|
||
echo "$project_id"
|
||
return 0
|
||
fi
|
||
|
||
log_error "Project '$identifier' not found"
|
||
return 1
|
||
}
|
||
|
||
# Create command
|
||
cmd_create() {
|
||
local name=""
|
||
local description=""
|
||
local color="#3b82f6"
|
||
|
||
# Parse arguments
|
||
while [[ $# -gt 0 ]]; do
|
||
case "${1:-}" in
|
||
--name) name="$2"; shift 2 ;;
|
||
--description) description="$2"; shift 2 ;;
|
||
--color) color="$2"; shift 2 ;;
|
||
*) shift ;;
|
||
esac
|
||
done
|
||
|
||
# Validate required fields
|
||
if [[ -z "$name" ]]; then
|
||
log_error "Name is required (use --name)"
|
||
exit 1
|
||
fi
|
||
|
||
# Generate project data
|
||
local project_id
|
||
project_id=$(generate_uuid)
|
||
local timestamp
|
||
timestamp=$(get_timestamp)
|
||
|
||
# Build JSON payload
|
||
local json_payload
|
||
json_payload=$(jq -n \
|
||
--arg id "$project_id" \
|
||
--arg name "$name" \
|
||
--arg color "$color" \
|
||
--arg created_at "$timestamp" \
|
||
'{
|
||
id: $id,
|
||
name: $name,
|
||
color: $color,
|
||
created_at: $created_at
|
||
}')
|
||
|
||
# Add optional fields
|
||
if [[ -n "$description" ]]; then
|
||
json_payload=$(echo "$json_payload" | jq --arg v "$description" '. + {description: $v}')
|
||
fi
|
||
|
||
# Create project
|
||
log_info "Creating project..."
|
||
local response
|
||
response=$(curl -s -X POST "${SUPABASE_URL}/rest/v1/projects" \
|
||
"${HEADERS[@]}" \
|
||
-d "$json_payload")
|
||
|
||
# Supabase returns empty on success, or error JSON on failure
|
||
if [[ -z "$response" ]]; then
|
||
log_success "Project created: $project_id"
|
||
elif [[ "$response" == *"error"* ]] || [[ "$response" == *""*"code"* ]]; then
|
||
log_error "Failed to create project"
|
||
echo "$response" | jq . 2>/dev/null || echo "$response"
|
||
exit 1
|
||
else
|
||
log_success "Project created: $project_id"
|
||
echo "$response" | jq . 2>/dev/null || echo "$response"
|
||
fi
|
||
}
|
||
|
||
# List command
|
||
cmd_list() {
|
||
local output_json=false
|
||
|
||
# Parse arguments
|
||
while [[ $# -gt 0 ]]; do
|
||
case "${1:-}" in
|
||
--json) output_json=true; shift ;;
|
||
*) shift ;;
|
||
esac
|
||
done
|
||
|
||
# Build query
|
||
local query="${SUPABASE_URL}/rest/v1/projects?select=*&order=created_at.desc"
|
||
|
||
log_info "Fetching projects..."
|
||
local response
|
||
response=$(curl -s "$query" "${HEADERS[@]}")
|
||
|
||
if [[ "$output_json" == true ]]; then
|
||
echo "$response" | jq .
|
||
else
|
||
# Table output
|
||
local count
|
||
count=$(echo "$response" | jq 'length')
|
||
log_success "Found $count project(s)"
|
||
|
||
# Print header
|
||
printf "%-36s %-25s %-30s\n" "ID" "NAME" "DESCRIPTION"
|
||
printf "%-36s %-25s %-30s\n" "------------------------------------" "-------------------------" "------------------------------"
|
||
|
||
# Print rows
|
||
echo "$response" | jq -r '.[] | [.id, (.name | tostring | .[0:23]), (.description // "" | tostring | .[0:28])] | @tsv' | while IFS=$'\t' read -r id name desc; do
|
||
printf "%-36s %-25s %-30s\n" "$id" "$name" "$desc"
|
||
done
|
||
fi
|
||
}
|
||
|
||
# Get command
|
||
cmd_get() {
|
||
local identifier="$1"
|
||
|
||
if [[ -z "$identifier" ]]; then
|
||
log_error "Project ID or name required. Usage: ./project.sh get <id-or-name>"
|
||
exit 1
|
||
fi
|
||
|
||
local project_id
|
||
project_id=$(resolve_project_id "$identifier")
|
||
|
||
log_info "Fetching project $project_id..."
|
||
local response
|
||
response=$(curl -s "${SUPABASE_URL}/rest/v1/projects?id=eq.${project_id}&select=*" \
|
||
"${HEADERS[@]}")
|
||
|
||
local project
|
||
project=$(echo "$response" | jq '.[0] // empty')
|
||
|
||
if [[ -n "$project" && "$project" != "{}" && "$project" != "null" ]]; then
|
||
echo "$project" | jq .
|
||
else
|
||
log_error "Project not found: $identifier"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Update command
|
||
cmd_update() {
|
||
local identifier="$1"
|
||
shift
|
||
|
||
if [[ -z "$identifier" ]]; then
|
||
log_error "Project ID or name required. Usage: ./project.sh update <id-or-name> [options]"
|
||
exit 1
|
||
fi
|
||
|
||
local project_id
|
||
project_id=$(resolve_project_id "$identifier")
|
||
|
||
# Build update payload
|
||
local update_fields="{}"
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "${1:-}" in
|
||
--name) update_fields=$(echo "$update_fields" | jq --arg v "$2" '. + {name: $v}'); shift 2 ;;
|
||
--description) update_fields=$(echo "$update_fields" | jq --arg v "$2" '. + {description: $v}'); shift 2 ;;
|
||
--color) update_fields=$(echo "$update_fields" | jq --arg v "$2" '. + {color: $v}'); shift 2 ;;
|
||
*) shift ;;
|
||
esac
|
||
done
|
||
|
||
# Check if we have anything to update
|
||
if [[ "$update_fields" == "{}" ]]; then
|
||
log_warning "No update fields specified"
|
||
exit 0
|
||
fi
|
||
|
||
# Add updated_at timestamp
|
||
local timestamp
|
||
timestamp=$(get_timestamp)
|
||
update_fields=$(echo "$update_fields" | jq --arg t "$timestamp" '. + {updated_at: $t}')
|
||
|
||
log_info "Updating project $project_id..."
|
||
local response
|
||
response=$(curl -s -X PATCH "${SUPABASE_URL}/rest/v1/projects?id=eq.${project_id}" \
|
||
"${HEADERS[@]}" \
|
||
-d "$update_fields")
|
||
|
||
if [[ -z "$response" || "$response" == "[]" || ! "$response" == *"error"* ]]; then
|
||
log_success "Project updated: $project_id"
|
||
else
|
||
log_error "Failed to update project"
|
||
echo "$response" | jq . 2>/dev/null || echo "$response"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Delete command
|
||
cmd_delete() {
|
||
local identifier="$1"
|
||
|
||
if [[ -z "$identifier" ]]; then
|
||
log_error "Project ID or name required. Usage: ./project.sh delete <id-or-name>"
|
||
exit 1
|
||
fi
|
||
|
||
local project_id
|
||
project_id=$(resolve_project_id "$identifier")
|
||
|
||
log_info "Deleting project $project_id..."
|
||
local response
|
||
response=$(curl -s -X DELETE "${SUPABASE_URL}/rest/v1/projects?id=eq.${project_id}" \
|
||
"${HEADERS[@]}")
|
||
|
||
if [[ -z "$response" || "$response" == "[]" ]]; then
|
||
log_success "Project deleted: $project_id"
|
||
else
|
||
log_error "Failed to delete project"
|
||
echo "$response" | jq . 2>/dev/null || echo "$response"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Main execution
|
||
check_dependencies
|
||
|
||
# Parse command
|
||
COMMAND="${1:-}"
|
||
shift || true
|
||
|
||
case "$COMMAND" in
|
||
create)
|
||
cmd_create "$@"
|
||
;;
|
||
list)
|
||
cmd_list "$@"
|
||
;;
|
||
get)
|
||
cmd_get "$@"
|
||
;;
|
||
update)
|
||
cmd_update "$@"
|
||
;;
|
||
delete)
|
||
cmd_delete "$@"
|
||
;;
|
||
help|--help|-h)
|
||
show_usage
|
||
;;
|
||
"")
|
||
show_usage
|
||
;;
|
||
*)
|
||
log_error "Unknown command: $COMMAND"
|
||
show_usage
|
||
exit 1
|
||
;;
|
||
esac
|