diff --git a/map/build-nocodb.md b/map/build-nocodb.md new file mode 100644 index 0000000..70d9c99 --- /dev/null +++ b/map/build-nocodb.md @@ -0,0 +1,394 @@ +# NocoDB Automation Script Development Summary + +## Overview +This document summarizes the development of an automated NocoDB table creation script (`build-nocodb.sh`) for the Map Viewer project. The script automates the creation of three required tables: `locations`, `login`, and `settings` with proper schemas and default data. + +## Project Requirements +Based on the README.md analysis, the project needed: +- **locations** table: Main map data storage +- **login** table: User authentication +- **settings** table: System configuration and QR codes +- Default admin user and start location records +- Idempotent script (safe to re-run) + +## NocoDB API Research + +### API Versions +- **v1 API**: `/api/v1/` - Legacy, limited functionality +- **v2 API**: `/api/v2/` - Modern, full-featured (recommended) + +### Key API Endpoints Discovered + +#### Base/Project Management +``` +GET /api/v2/meta/bases # List all bases +POST /api/v2/meta/bases # Create new base +GET /api/v2/meta/bases/{id} # Get base details +``` + +#### Table Management +``` +GET /api/v2/meta/bases/{base_id}/tables # List tables in base +POST /api/v2/meta/bases/{base_id}/tables # Create table +GET /api/v2/meta/bases/{base_id}/tables/{table_id} # Get table details +``` + +#### Record Management +``` +GET /api/v2/tables/{table_id}/records # List records +POST /api/v2/tables/{table_id}/records # Create record +PUT /api/v2/tables/{table_id}/records/{record_id} # Update record +``` + +### Authentication +All API calls require the `xc-token` header: +```bash +curl -H "xc-token: YOUR_TOKEN" -H "Content-Type: application/json" +``` + +## Table Schemas Implemented + +### 1. Locations Table +Primary table for map data storage: +```json +{ + "table_name": "locations", + "columns": [ + {"column_name": "id", "uidt": "ID", "pk": true, "ai": true}, + {"column_name": "title", "uidt": "SingleLineText"}, + {"column_name": "description", "uidt": "LongText"}, + {"column_name": "category", "uidt": "SingleSelect", "colOptions": { + "options": [ + {"title": "Important", "color": "#ff0000"}, + {"title": "Event", "color": "#00ff00"}, + {"title": "Business", "color": "#0000ff"}, + {"title": "Other", "color": "#ffff00"} + ] + }}, + {"column_name": "geo_location", "uidt": "LongText"}, + {"column_name": "latitude", "uidt": "Decimal"}, + {"column_name": "longitude", "uidt": "Decimal"}, + {"column_name": "address", "uidt": "LongText"}, + {"column_name": "contact_info", "uidt": "LongText"}, + {"column_name": "created_at", "uidt": "DateTime"}, + {"column_name": "updated_at", "uidt": "DateTime"} + ] +} +``` + +### 2. Login Table +User authentication table: +```json +{ + "table_name": "login", + "columns": [ + {"column_name": "id", "uidt": "ID", "pk": true, "ai": true}, + {"column_name": "username", "uidt": "SingleLineText", "rqd": true}, + {"column_name": "email", "uidt": "Email", "rqd": true}, + {"column_name": "password", "uidt": "SingleLineText", "rqd": true}, + {"column_name": "admin", "uidt": "Checkbox"}, + {"column_name": "active", "uidt": "Checkbox"}, + {"column_name": "created_at", "uidt": "DateTime"}, + {"column_name": "last_login", "uidt": "DateTime"} + ] +} +``` + +### 3. Settings Table +System configuration with QR code support: +```json +{ + "table_name": "settings", + "columns": [ + {"column_name": "id", "uidt": "ID", "pk": true, "ai": true}, + {"column_name": "key", "uidt": "SingleLineText", "rqd": true}, + {"column_name": "title", "uidt": "SingleLineText"}, + {"column_name": "geo_location", "uidt": "LongText"}, + {"column_name": "latitude", "uidt": "Decimal"}, + {"column_name": "longitude", "uidt": "Decimal"}, + {"column_name": "zoom", "uidt": "Number"}, + {"column_name": "category", "uidt": "SingleSelect", "colOptions": { + "options": [ + {"title": "system_setting", "color": "#4CAF50"}, + {"title": "user_setting", "color": "#2196F3"}, + {"title": "app_config", "color": "#FF9800"} + ] + }}, + {"column_name": "updated_by", "uidt": "SingleLineText"}, + {"column_name": "updated_at", "uidt": "DateTime"}, + {"column_name": "qr_code_1_url", "uidt": "URL"}, + {"column_name": "qr_code_1_label", "uidt": "SingleLineText"}, + {"column_name": "qr_code_1_image", "uidt": "Attachment"}, + {"column_name": "qr_code_2_url", "uidt": "URL"}, + {"column_name": "qr_code_2_label", "uidt": "SingleLineText"}, + {"column_name": "qr_code_2_image", "uidt": "Attachment"}, + {"column_name": "qr_code_3_url", "uidt": "URL"}, + {"column_name": "qr_code_3_label", "uidt": "SingleLineText"}, + {"column_name": "qr_code_3_image", "uidt": "Attachment"} + ] +} +``` + +## NocoDB Column Types (UIdt) +Discovered column types and their usage: +- `ID` - Auto-incrementing primary key +- `SingleLineText` - Short text field +- `LongText` - Multi-line text area +- `Email` - Email validation +- `URL` - URL validation +- `Decimal` - Decimal numbers +- `Number` - Integer numbers +- `DateTime` - Date and time +- `Checkbox` - Boolean true/false +- `SingleSelect` - Dropdown with predefined options +- `Attachment` - File upload field + +## Script Development Process + +### Initial Implementation +1. Created basic structure with environment variable loading +2. Implemented API connectivity testing +3. Added base/project creation functionality +4. Created table creation functions + +### Key Challenges Solved + +#### 1. Environment Variable Loading +**Issue**: Standard `source .env` wasn't exporting variables +**Solution**: Use `set -a; source .env; set +a` pattern +```bash +set -a # Auto-export all variables +source .env # Load environment file +set +a # Disable auto-export +``` + +#### 2. API Version Compatibility +**Issue**: Mixed v1/v2 endpoint usage causing errors +**Solution**: Standardized on v2 API with proper URL construction +```bash +BASE_URL=$(echo "$NOCODB_API_URL" | sed 's|/api/v1||') +API_BASE_V2="${BASE_URL}/api/v2" +``` + +#### 3. Duplicate Table Error +**Issue**: Script failed when tables already existed +**Solution**: Added idempotent table checking +```bash +get_table_id_by_name() { + local base_id=$1 + local table_name=$2 + + # Check if table exists by name + local tables_response + tables_response=$(make_api_call "GET" "/meta/bases/$base_id/tables" "" "Fetching tables") + + # Parse JSON to find table ID + local table_id + table_id=$(echo "$tables_response" | grep -o '"id":"[^"]*","table_name":"'"$table_name"'"' | grep -o '"id":"[^"]*"' | head -1 | sed 's/"id":"//;s/"//') + + if [ -n "$table_id" ]; then + echo "$table_id" + return 0 + else + return 1 + fi +} +``` + +#### 4. JSON Response Parsing +**Issue**: Complex JSON parsing for table IDs +**Solution**: Used grep with regex patterns +```bash +# Extract table ID from JSON response +table_id=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) +``` + +## Default Data Records + +### Admin User +```json +{ + "username": "admin", + "email": "admin@example.com", + "password": "changeme123", + "admin": true, + "active": true, + "created_at": "2025-07-05 12:00:00" +} +``` + +### Start Location Setting +```json +{ + "key": "start_location", + "title": "Map Start Location", + "geo_location": "53.5461;-113.4938", + "latitude": 53.5461, + "longitude": -113.4938, + "zoom": 11, + "category": "system_setting", + "updated_by": "system", + "updated_at": "2025-07-05 12:00:00" +} +``` + +## Error Handling Patterns + +### API Call Wrapper +```bash +make_api_call() { + local method=$1 + local endpoint=$2 + local data=$3 + local description=$4 + local api_version=${5:-"v2"} + + # Construct full URL + if [[ "$api_version" == "v1" ]]; then + full_url="$API_BASE_V1$endpoint" + else + full_url="$API_BASE_V2$endpoint" + fi + + # Make request with timeout + response=$(curl -s -w "%{http_code}" -X "$method" \ + -H "xc-token: $NOCODB_API_TOKEN" \ + -H "Content-Type: application/json" \ + --max-time 30 \ + -d "$data" \ + "$full_url" 2>/dev/null) + + # Parse HTTP code and response + http_code="${response: -3}" + response_body="${response%???}" + + # Check for success + if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then + echo "$response_body" + return 0 + else + print_error "API call failed: $http_code - $response_body" + return 1 + fi +} +``` + +## Final Script Features + +### Idempotent Operation +- Checks for existing base/project +- Validates table existence before creation +- Uses existing table IDs when found +- Safe to run multiple times + +### Robust Error Handling +- Network timeout protection +- HTTP status code validation +- JSON parsing error handling +- Colored output for status messages + +### Environment Integration +- Loads configuration from `.env` file +- Supports custom default coordinates +- Validates required variables + +## Usage Instructions + +1. **Setup Environment**: + ```bash + # Update .env with your NocoDB details + NOCODB_API_URL=https://your-nocodb.com/api/v1 + NOCODB_API_TOKEN=your_token_here + ``` + +2. **Run Script**: + ```bash + chmod +x build-nocodb.sh + ./build-nocodb.sh + ``` + +3. **Post-Setup**: + - Update `.env` with generated table URLs + - Change default admin password + - Verify tables in NocoDB interface + +## Lessons Learned + +1. **API Documentation**: Always verify API endpoints with actual testing +2. **JSON Parsing**: Shell-based JSON parsing requires careful regex patterns +3. **Idempotency**: Essential for automation scripts in production +4. **Error Handling**: Comprehensive error handling prevents silent failures +5. **Environment Variables**: Proper loading patterns are crucial for script reliability + +## Future Enhancements + +- Add support for custom table schemas via configuration +- Implement data migration features +- Add backup/restore functionality +- Support for multiple environment configurations +- Integration with CI/CD pipelines + +## Script Updates - July 2025 + +### Column Type Improvements +Updated the build-nocodb.sh script to use proper NocoDB column types based on the official documentation: + +#### Locations Table Updates +- **`geo_location`**: Changed from `LongText` to `GeoData` (proper geographic data type) +- **`latitude`**: Added precision (10) and scale (8) for proper decimal handling +- **`longitude`**: Added precision (11) and scale (8) for proper decimal handling +- **`phone`**: Changed from `SingleLineText` to `PhoneNumber` (proper phone validation) +- **`email`**: Using `Email` type for proper email validation +- **Updated field names**: Added proper fields from README.md: + - `first_name`, `last_name` (SingleLineText) + - `unit_number` (SingleLineText) + - `support_level` (SingleSelect with colors: 1=Green, 2=Yellow, 3=Orange, 4=Red) + - `sign` (Checkbox) + - `sign_size` (SingleSelect: Small, Medium, Large) + - `notes` (LongText) + - `address` (SingleLineText instead of LongText) + +#### Login Table Updates +- **Simplified structure**: Removed username/password fields per README.md specification +- **Core fields**: `email` (Email), `name` (SingleLineText), `admin` (Checkbox) +- **Authentication note**: This is a simplified table - proper authentication should be implemented separately + +#### Settings Table Updates +- **`geo_location`**: Changed from `LongText` to `GeoData` for proper geographic data handling +- **`latitude`/`longitude`**: Added precision and scale parameters +- **`value`**: Added missing `value` field from README.md specification +- **QR Code fields**: Simplified to just attachment fields (removed URL/label fields not in README.md) + +### Benefits of Proper Column Types + +1. **GeoData Type**: + - Proper latitude;longitude format validation + - Better integration with mapping libraries + - Consistent data storage format + +2. **PhoneNumber Type**: + - Built-in phone number validation + - Proper formatting and display + - International number support + +3. **Email Type**: + - Email format validation + - Prevents invalid email addresses + - Better UI experience + +4. **Decimal Precision**: + - Latitude: 10 digits, 8 decimal places (±90.12345678) + - Longitude: 11 digits, 8 decimal places (±180.12345678) + - Provides GPS-level precision for mapping + +5. **SingleSelect with Colors**: + - Support Level: Color-coded options for visual feedback + - Sign Size: Consistent option selection + - Category: Organized classification system + +### Backward Compatibility +The script maintains backward compatibility while using proper column types. Existing data migration may be needed if upgrading from the old schema. + +--- +*Generated: July 5, 2025* +*Script Version: Column Type Optimized* \ No newline at end of file diff --git a/map/build-nocodb.sh b/map/build-nocodb.sh new file mode 100755 index 0000000..32d009d --- /dev/null +++ b/map/build-nocodb.sh @@ -0,0 +1,780 @@ +#!/bin/bash + +# NocoDB Auto-Setup Script +# This script automatically creates the necessary base and tables for the NocoDB Map Viewer +# Based on requirements from README.md and using proper NocoDB column types +# +# Creates three tables: +# 1. locations - Main table with GeoData, proper field types per README.md +# 2. login - Simple authentication table with Email, Name, Admin fields +# 3. settings - Configuration table with GeoData and attachment fields for QR codes +# +# Updated: July 2025 - Using proper NocoDB column types (GeoData, PhoneNumber, etc.) + +set -e # Exit on any error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + echo -e "${BLUE}[INFO]${NC} $1" >&2 +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" >&2 +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" >&2 +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" >&2 +} + +# Load environment variables +if [ -f ".env" ]; then + # Use set -a to automatically export variables + set -a + source .env + set +a + print_success "Environment variables loaded from .env" +else + print_error ".env file not found!" + exit 1 +fi + +# Validate required environment variables +if [ -z "$NOCODB_API_URL" ] || [ -z "$NOCODB_API_TOKEN" ]; then + print_error "Required environment variables NOCODB_API_URL and NOCODB_API_TOKEN not set!" + exit 1 +fi + +# Extract base URL from API URL and set up v2 API endpoints +BASE_URL=$(echo "$NOCODB_API_URL" | sed 's|/api/v1||') +API_BASE_V1="$NOCODB_API_URL" +API_BASE_V2="${BASE_URL}/api/v2" + +print_status "Using NocoDB instance: $BASE_URL" + +# Function to make API calls with proper error handling +make_api_call() { + local method=$1 + local endpoint=$2 + local data=$3 + local description=$4 + local api_version=${5:-"v2"} # Default to v2 + + print_status "$description" + + local response + local http_code + local full_url + + if [[ "$api_version" == "v1" ]]; then + full_url="$API_BASE_V1$endpoint" + else + full_url="$API_BASE_V2$endpoint" + fi + + print_status "Making $method request to: $full_url" + + if [ "$method" = "GET" ]; then + response=$(curl -s -w "%{http_code}" -H "xc-token: $NOCODB_API_TOKEN" \ + -H "Content-Type: application/json" \ + --max-time 30 \ + "$full_url" 2>/dev/null) + curl_exit_code=$? + else + response=$(curl -s -w "%{http_code}" -X "$method" \ + -H "xc-token: $NOCODB_API_TOKEN" \ + -H "Content-Type: application/json" \ + --max-time 30 \ + -d "$data" \ + "$full_url" 2>/dev/null) + curl_exit_code=$? + fi + + if [[ $curl_exit_code -ne 0 ]]; then + print_error "Network error occurred while making API call (curl exit code: $curl_exit_code)" + return 1 + fi + + if [[ -z "$response" ]]; then + print_error "Empty response from API call" + return 1 + fi + + http_code="${response: -3}" + response_body="${response%???}" + + print_status "HTTP Code: $http_code" + print_status "Response preview: ${response_body:0:200}..." + + if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then + print_success "$description completed successfully" + echo "$response_body" + else + print_error "$description failed with HTTP code: $http_code" + print_error "Full URL: $full_url" + print_error "Response: $response_body" + return 1 + fi +} + +# Function to create a project/base +create_project() { + local project_name="$1" + local project_data='{ + "title": "'"$project_name"'", + "description": "Auto-generated project for NocoDB Map Viewer", + "color": "#24716E" + }' + + make_api_call "POST" "/meta/bases" "$project_data" "Creating project: $project_name" "v2" +} + +# Function to create a table or get existing table ID +create_table() { + local base_id=$1 + local table_name=$2 + local table_data=$3 + local description=$4 + + # First check if table already exists + local existing_id + existing_id=$(get_table_id_by_name "$base_id" "$table_name") + + if [[ -n "$existing_id" ]]; then + print_success "Table '$table_name' already exists with ID: $existing_id" + echo "$existing_id" + return 0 + fi + + # Table doesn't exist, create it + local response + response=$(make_api_call "POST" "/meta/bases/$base_id/tables" "$table_data" "Creating table: $table_name ($description)" "v2") + + if [[ $? -eq 0 && -n "$response" ]]; then + # Extract table ID from response + local table_id + table_id=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) + + if [[ -n "$table_id" ]]; then + print_success "Table '$table_name' created with ID: $table_id" + echo "$table_id" + else + print_error "Failed to extract table ID from response" + return 1 + fi + else + print_error "Failed to create table: $table_name" + return 1 + fi +} + +# Function to create columns for a table +create_column() { + local project_id=$1 + local table_id=$2 + local column_data=$3 + local column_name=$4 + + make_api_call "POST" "/meta/projects/$project_id/tables/$table_id/columns" "$column_data" "Creating column: $column_name" +} + +# Function to test API connectivity +test_api_connectivity() { + print_status "Testing API connectivity..." + + # Test basic connectivity first + if ! curl -s --max-time 10 -I "$BASE_URL" > /dev/null 2>&1; then + print_error "Cannot reach NocoDB instance at $BASE_URL" + return 1 + fi + + # Test API with token using v2 endpoint + local test_response + test_response=$(curl -s --max-time 10 -w "%{http_code}" -H "xc-token: $NOCODB_API_TOKEN" \ + -H "Content-Type: application/json" \ + "$API_BASE_V2/meta/bases" 2>/dev/null || echo "CURL_ERROR") + + if [[ "$test_response" == "CURL_ERROR" ]]; then + print_error "Network error when testing API" + return 1 + fi + + local http_code="${test_response: -3}" + local response_body="${test_response%???}" + + if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then + print_success "API connectivity test successful" + return 0 + else + print_error "API test failed with HTTP code: $http_code" + print_error "Response: $response_body" + return 1 + fi +} + +# Function to extract project ID from existing URLs +extract_project_from_urls() { + print_status "Checking for existing project IDs in URLs..." + + local project_id="" + + # Try to extract from view URL + if [[ -n "$NOCODB_VIEW_URL" ]]; then + project_id=$(echo "$NOCODB_VIEW_URL" | sed -n 's/.*\/nc\/\([^\/]*\)\/.*/\1/p') + if [[ -n "$project_id" ]]; then + print_success "Found project ID from VIEW_URL: $project_id" + echo "$project_id" + return 0 + fi + fi + + # Try to extract from login sheet + if [[ -n "$NOCODB_LOGIN_SHEET" ]]; then + project_id=$(echo "$NOCODB_LOGIN_SHEET" | sed -n 's/.*\/nc\/\([^\/]*\)\/.*/\1/p') + if [[ -n "$project_id" ]]; then + print_success "Found project ID from LOGIN_SHEET: $project_id" + echo "$project_id" + return 0 + fi + fi + + # Try to extract from settings sheet + if [[ -n "$NOCODB_SETTINGS_SHEET" ]]; then + project_id=$(echo "$NOCODB_SETTINGS_SHEET" | sed -n 's/.*\/nc\/\([^\/]*\)\/.*/\1/p') + if [[ -n "$project_id" ]]; then + print_success "Found project ID from SETTINGS_SHEET: $project_id" + echo "$project_id" + return 0 + fi + fi + + print_warning "No existing project ID found in URLs" + return 1 +} + +# Function to get or create project +get_or_create_project() { + local project_name="Map Viewer Project" + + # First test API connectivity + if ! test_api_connectivity; then + print_error "API connectivity test failed" + exit 1 + fi + + # Since the URLs are empty, we need to create new tables + # Check if we have any existing bases first + print_status "Checking for existing bases..." + + local bases_response + bases_response=$(make_api_call "GET" "/meta/bases" "" "Fetching existing bases" "v2") + + if [[ $? -eq 0 ]]; then + # Try to find existing project (look for our project name or use first available) + local existing_base_id + existing_base_id=$(echo "$bases_response" | grep -o '"id":"[^"]*"' | head -1 | sed 's/"id":"//;s/"//') + + if [ -n "$existing_base_id" ]; then + print_success "Using existing base with ID: $existing_base_id" + echo "$existing_base_id" + return 0 + else + print_status "No existing base found, creating new base..." + local new_base_response + new_base_response=$(create_project "$project_name") + local new_base_id + new_base_id=$(echo "$new_base_response" | grep -o '"id":"[^"]*"' | head -1 | sed 's/"id":"//;s/"//') + + if [ -n "$new_base_id" ]; then + print_success "Created new base with ID: $new_base_id" + echo "$new_base_id" + return 0 + else + print_error "Failed to create or find base" + exit 1 + fi + fi + else + print_error "Failed to fetch bases from API" + exit 1 + fi +} + +# Function to create the main locations table +create_locations_table() { + local base_id=$1 + + local table_data='{ + "table_name": "locations", + "title": "Locations", + "columns": [ + { + "column_name": "id", + "title": "ID", + "uidt": "ID", + "pk": true, + "ai": true, + "rqd": true + }, + { + "column_name": "geo_location", + "title": "Geo-Location", + "uidt": "GeoData", + "rqd": false + }, + { + "column_name": "latitude", + "title": "latitude", + "uidt": "Decimal", + "rqd": false, + "precision": 10, + "scale": 8 + }, + { + "column_name": "longitude", + "title": "longitude", + "uidt": "Decimal", + "rqd": false, + "precision": 11, + "scale": 8 + }, + { + "column_name": "first_name", + "title": "First Name", + "uidt": "SingleLineText", + "rqd": false + }, + { + "column_name": "last_name", + "title": "Last Name", + "uidt": "SingleLineText", + "rqd": false + }, + { + "column_name": "email", + "title": "Email", + "uidt": "Email", + "rqd": false + }, + { + "column_name": "phone", + "title": "Phone", + "uidt": "PhoneNumber", + "rqd": false + }, + { + "column_name": "unit_number", + "title": "Unit Number", + "uidt": "SingleLineText", + "rqd": false + }, + { + "column_name": "support_level", + "title": "Support Level", + "uidt": "SingleSelect", + "rqd": false, + "colOptions": { + "options": [ + {"title": "1", "color": "#4CAF50"}, + {"title": "2", "color": "#FFEB3B"}, + {"title": "3", "color": "#FF9800"}, + {"title": "4", "color": "#F44336"} + ] + } + }, + { + "column_name": "address", + "title": "Address", + "uidt": "SingleLineText", + "rqd": false + }, + { + "column_name": "sign", + "title": "Sign", + "uidt": "Checkbox", + "rqd": false + }, + { + "column_name": "sign_size", + "title": "Sign Size", + "uidt": "SingleSelect", + "rqd": false, + "colOptions": { + "options": [ + {"title": "Small", "color": "#2196F3"}, + {"title": "Medium", "color": "#FF9800"}, + {"title": "Large", "color": "#4CAF50"} + ] + } + }, + { + "column_name": "notes", + "title": "Notes", + "uidt": "LongText", + "rqd": false + }, + { + "column_name": "title", + "title": "title", + "uidt": "SingleLineText", + "rqd": false + }, + { + "column_name": "category", + "title": "category", + "uidt": "SingleSelect", + "rqd": false, + "colOptions": { + "options": [ + {"title": "Important", "color": "#F44336"}, + {"title": "Event", "color": "#4CAF50"}, + {"title": "Business", "color": "#2196F3"}, + {"title": "Other", "color": "#FF9800"} + ] + } + }, + { + "column_name": "created_at", + "title": "Created At", + "uidt": "DateTime", + "rqd": false + }, + { + "column_name": "updated_at", + "title": "Updated At", + "uidt": "DateTime", + "rqd": false + } + ] + }' + + create_table "$base_id" "locations" "$table_data" "Main locations table for map data" +} + +# Function to create the login table +create_login_table() { + local base_id=$1 + + local table_data='{ + "table_name": "login", + "title": "Login", + "columns": [ + { + "column_name": "id", + "title": "ID", + "uidt": "ID", + "pk": true, + "ai": true, + "rqd": true + }, + { + "column_name": "email", + "title": "Email", + "uidt": "Email", + "rqd": true + }, + { + "column_name": "name", + "title": "Name", + "uidt": "SingleLineText", + "rqd": false + }, + { + "column_name": "admin", + "title": "Admin", + "uidt": "Checkbox", + "rqd": false + }, + { + "column_name": "created_at", + "title": "Created At", + "uidt": "DateTime", + "rqd": false + }, + { + "column_name": "last_login", + "title": "Last Login", + "uidt": "DateTime", + "rqd": false + } + ] + }' + + create_table "$base_id" "login" "$table_data" "User authentication table" +} + +# Function to create the settings table +create_settings_table() { + local base_id=$1 + + local table_data='{ + "table_name": "settings", + "title": "Settings", + "columns": [ + { + "column_name": "id", + "title": "ID", + "uidt": "ID", + "pk": true, + "ai": true, + "rqd": true + }, + { + "column_name": "key", + "title": "key", + "uidt": "SingleLineText", + "rqd": true + }, + { + "column_name": "title", + "title": "title", + "uidt": "SingleLineText", + "rqd": false + }, + { + "column_name": "value", + "title": "value", + "uidt": "LongText", + "rqd": false + }, + { + "column_name": "geo_location", + "title": "Geo-Location", + "uidt": "SingleLineText", + "rqd": false + }, + { + "column_name": "latitude", + "title": "latitude", + "uidt": "Decimal", + "rqd": false, + "precision": 10, + "scale": 8 + }, + { + "column_name": "longitude", + "title": "longitude", + "uidt": "Decimal", + "rqd": false, + "precision": 11, + "scale": 8 + }, + { + "column_name": "zoom", + "title": "zoom", + "uidt": "Number", + "rqd": false + }, + { + "column_name": "category", + "title": "category", + "uidt": "SingleSelect", + "rqd": false, + "colOptions": { + "options": [ + {"title": "system_setting", "color": "#4CAF50"}, + {"title": "user_setting", "color": "#2196F3"}, + {"title": "app_config", "color": "#FF9800"} + ] + } + }, + { + "column_name": "updated_by", + "title": "updated_by", + "uidt": "SingleLineText", + "rqd": false + }, + { + "column_name": "updated_at", + "title": "updated_at", + "uidt": "DateTime", + "rqd": false + }, + { + "column_name": "qr_code_1_image", + "title": "QR Code 1 Image", + "uidt": "Attachment", + "rqd": false + }, + { + "column_name": "qr_code_2_image", + "title": "QR Code 2 Image", + "uidt": "Attachment", + "rqd": false + }, + { + "column_name": "qr_code_3_image", + "title": "QR Code 3 Image", + "uidt": "Attachment", + "rqd": false + } + ] + }' + + create_table "$base_id" "settings" "$table_data" "System configuration and QR codes" +} + +# Function to create default admin user +create_default_admin() { + local base_id=$1 + local login_table_id=$2 + + print_status "Creating default admin user..." + + local admin_data='{ + "email": "admin@example.com", + "name": "Administrator", + "admin": true, + "created_at": "'"$(date -u +"%Y-%m-%d %H:%M:%S")"'" + }' + + make_api_call "POST" "/tables/$login_table_id/records" "$admin_data" "Creating default admin user" "v2" + + print_warning "Default admin user created:" + print_warning " Email: admin@example.com" + print_warning " Name: Administrator" + print_warning " Admin: true" + print_warning " Note: This is a simplified login table for demonstration." + print_warning " You may need to implement proper authentication separately." +} + +# Function to create default start location setting +create_default_start_location() { + local base_id=$1 + local settings_table_id=$2 + + print_status "Creating default start location setting..." + + local start_location_data='{ + "key": "start_location", + "title": "Map Start Location", + "geo_location": "'"${DEFAULT_LAT:-53.5461}"';'"${DEFAULT_LNG:--113.4938}"'", + "latitude": '"${DEFAULT_LAT:-53.5461}"', + "longitude": '"${DEFAULT_LNG:--113.4938}"', + "zoom": '"${DEFAULT_ZOOM:-11}"', + "category": "system_setting", + "updated_by": "system", + "updated_at": "'"$(date -u +"%Y-%m-%d %H:%M:%S")"'" + }' + + make_api_call "POST" "/tables/$settings_table_id/records" "$start_location_data" "Creating default start location" "v2" +} + +# Function to get table ID from table name +get_table_id() { + local project_id=$1 + local table_name=$2 + + local tables_response + tables_response=$(make_api_call "GET" "/meta/projects/$project_id/tables" "" "Fetching tables for project") + + local table_id + table_id=$(echo "$tables_response" | grep -A 5 -B 5 "\"table_name\":\"$table_name\"" | grep -o '"id":"[^"]*"' | head -1 | sed 's/"id":"//;s/"//') + + if [ -n "$table_id" ]; then + echo "$table_id" + else + print_error "Could not find table ID for table: $table_name" + return 1 + fi +} + +# Function to get table ID from table name +get_table_id_by_name() { + local base_id=$1 + local table_name=$2 + + print_status "Checking if table '$table_name' exists..." + + local tables_response + tables_response=$(make_api_call "GET" "/meta/bases/$base_id/tables" "" "Fetching tables for base" "v2") + + if [[ $? -eq 0 ]]; then + # Parse JSON to find table by name + local table_id + table_id=$(echo "$tables_response" | grep -o '"id":"[^"]*","table_name":"'"$table_name"'"' | grep -o '"id":"[^"]*"' | head -1 | sed 's/"id":"//;s/"//') + + if [ -n "$table_id" ]; then + print_success "Found existing table '$table_name' with ID: $table_id" + echo "$table_id" + return 0 + else + print_status "Table '$table_name' does not exist" + return 1 + fi + else + print_error "Failed to fetch tables for base" + return 1 + fi +} + +# Main execution +main() { + print_status "Starting NocoDB Auto-Setup..." + print_status "================================" + + # Get or create project + print_status "Getting or creating base..." + BASE_ID=$(get_or_create_project) + + if [ -z "$BASE_ID" ]; then + print_error "Failed to get or create base" + exit 1 + fi + + print_status "Working with base ID: $BASE_ID" + + # Create tables + print_status "Creating tables..." + + # Create locations table + LOCATIONS_TABLE_ID=$(create_locations_table "$BASE_ID") + + # Create login table + LOGIN_TABLE_ID=$(create_login_table "$BASE_ID") + + # Create settings table + SETTINGS_TABLE_ID=$(create_settings_table "$BASE_ID") + + # Wait a moment for tables to be fully created + sleep 3 + + # Create default data + print_status "Setting up default data..." + + # Create default admin user + create_default_admin "$BASE_ID" "$LOGIN_TABLE_ID" + + # Create default start location + create_default_start_location "$BASE_ID" "$SETTINGS_TABLE_ID" + + print_status "================================" + print_success "NocoDB Auto-Setup completed successfully!" + print_status "================================" + + print_status "Next steps:" + print_status "1. Login to your NocoDB instance and verify the tables were created" + print_status "2. Find the table URLs in NocoDB and update your .env file:" + print_status " - Go to each table > Details > Copy the view URL" + print_status " - Update NOCODB_VIEW_URL, NOCODB_LOGIN_SHEET, and NOCODB_SETTINGS_SHEET" + print_status "3. Set up proper authentication for the admin user (admin@example.com)" + print_status "4. Start adding your location data" + + print_warning "Important: Please update your .env file with the actual table URLs from NocoDB!" + print_warning "The current .env file has empty URLs - you need to populate them with the correct table URLs." +} + +# Check if script is being run directly +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi