refactor: clean repository structure for v0.1.0 open source release
- Remove Go artifacts (cmd/, internal/, pkg/, go.mod) - Move furt-lua/* content to repository root - Restructure as clean src/, config/, scripts/, tests/ layout - Rewrite README.md as practical tool documentation - Remove timeline references and marketing language - Clean .gitignore from Go-era artifacts - Update config/server.lua with example.org defaults - Add .env.production to .gitignore for security Repository now ready for open source distribution with minimal, focused structure and generic configuration templates. close issue DAW/furt#86
This commit is contained in:
parent
87c935379b
commit
be3b9614d0
38 changed files with 280 additions and 5892 deletions
|
|
@ -1,779 +0,0 @@
|
|||
#!/bin/bash
|
||||
# scripts/create_issue.sh - Furt API Gateway Issue Creator
|
||||
# DEBUG VERSION with path fixes and diagnostic output
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Standard environment setup
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
export $(cat "$PROJECT_ROOT/.env" | grep -v '^#' | xargs)
|
||||
fi
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
log_debug() {
|
||||
if [[ "${DEBUG:-}" == "1" ]]; then
|
||||
echo -e "${CYAN}[DEBUG]${NC} $1" >&2;
|
||||
fi
|
||||
}
|
||||
|
||||
# Track new labels for auto-update (FIXED: Safe initialization)
|
||||
declare -A NEW_LABELS_CREATED=()
|
||||
|
||||
# === LABEL DEFINITIONS START ===
|
||||
# This section is auto-maintained by update_script_labels.sh
|
||||
# DO NOT EDIT MANUALLY - Changes will be overwritten
|
||||
declare -A LABEL_DEFINITIONS=(
|
||||
["hugo-integration"]="color:ff7518;context:frontend;usage:hugo_templates,integration"
|
||||
["service-newsletter"]="color:ff6b6b;context:newsletter;usage:newsletter_integration"
|
||||
["service-analytics"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["ready-for-deployment"]="color:28a745;context:deploy_ready;usage:status_updates"
|
||||
["service-clean-test4"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["service-completely-absolut-new7"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["service-completely-absolut-new9"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["service-completely-absolut-new8"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["performance"]="color:fbca04;context:optimization;usage:performance_template,architecture_template"
|
||||
["bug"]="color:d73a4a;context:error;usage:bug_template,status_updates"
|
||||
["question"]="color:d876e3;context:discussion;usage:question_template"
|
||||
["service-formular2mail"]="color:1d76db;context:formular2mail;usage:formular2mail_integration"
|
||||
["good-first-issue"]="color:7057ff;context:beginner_friendly;usage:manual_assignment"
|
||||
["service-completely-absolut-new10"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["service-completely-absolut-new11"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["breaking-change"]="color:d73a4a;context:breaking;usage:api_templates,architecture_template"
|
||||
["service-request"]="color:7057ff;context:new_service;usage:service_templates,status_updates"
|
||||
["service-debug-test"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["low-priority"]="color:0e8a16;context:nice_to_have;usage:all_templates"
|
||||
["blocked"]="color:d73a4a;context:blocked;usage:status_updates"
|
||||
["low-tech"]="color:6f42c1;context:low_tech_principle;usage:architecture_template,performance_template,security_template"
|
||||
["deployment"]="color:ff7518;context:deployment;usage:deployment_template"
|
||||
["gateway"]="color:0052cc;context:gateway_core;usage:architecture_template,performance_template,service_templates"
|
||||
["service-sagjan"]="color:1d76db;context:sagjan;usage:sagjan_integration"
|
||||
["work-in-progress"]="color:fbca04;context:active;usage:status_updates"
|
||||
["service-debug-check-final2"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["digital-sovereignty"]="color:6f42c1;context:digital_sovereignty;usage:architecture_template,performance_template,security_template"
|
||||
["security"]="color:28a745;context:security_review;usage:security_template,architecture_template"
|
||||
["architecture"]="color:d4c5f9;context:design;usage:architecture_template,gateway"
|
||||
["configuration"]="color:f9d71c;context:config_management;usage:deployment_template,architecture_template"
|
||||
["needs-review"]="color:0e8a16;context:review;usage:status_updates"
|
||||
["help-wanted"]="color:159818;context:community_help;usage:manual_assignment"
|
||||
["service-whatever-you-want"]="color:1d76db;context:service_integration;usage:service_specific"
|
||||
["api-contract"]="color:5319e7;context:api_design;usage:api_templates,service_templates"
|
||||
["enhancement"]="color:84b6eb;context:improvement;usage:all_templates"
|
||||
["high-priority"]="color:d73a4a;context:urgent;usage:all_templates"
|
||||
["testing"]="color:f9d71c;context:testing;usage:testing_template,integration"
|
||||
["test-all-templates"]="color:ff0000;context:test;usage:all_templates"
|
||||
)
|
||||
|
||||
# Extract label info
|
||||
get_label_color() { echo "${LABEL_DEFINITIONS[$1]}" | cut -d';' -f1 | cut -d':' -f2; }
|
||||
get_label_context() { echo "${LABEL_DEFINITIONS[$1]}" | cut -d';' -f2 | cut -d':' -f2; }
|
||||
get_label_usage() { echo "${LABEL_DEFINITIONS[$1]}" | cut -d';' -f3 | cut -d':' -f2; }
|
||||
|
||||
# Check if label is valid for context
|
||||
is_label_valid_for_context() {
|
||||
local label="$1"
|
||||
local context="$2"
|
||||
local usage=$(get_label_usage "$label")
|
||||
[[ "$usage" == *"$context"* ]] || [[ "$usage" == "all_templates" ]]
|
||||
}
|
||||
# === LABEL DEFINITIONS END ===
|
||||
|
||||
# === TEMPLATE LABEL MAPPINGS START ===
|
||||
# Auto-generated template to label mappings
|
||||
declare -A TEMPLATE_LABELS=(
|
||||
["performance"]="performance,low-priority,low-tech,gateway,digital-sovereignty,enhancement,high-priority,test-all-templates"
|
||||
["bug"]="bug,low-priority,enhancement,high-priority,test-all-templates"
|
||||
["api"]="breaking-change,low-priority,api-contract,enhancement,high-priority,test-all-templates"
|
||||
["service"]="low-priority,gateway,api-contract,enhancement,high-priority,test-all-templates"
|
||||
["deployment"]="low-priority,deployment,configuration,enhancement,high-priority,test-all-templates"
|
||||
["security"]="low-priority,low-tech,digital-sovereignty,security,enhancement,high-priority,test-all-templates"
|
||||
["architecture"]="performance,breaking-change,low-priority,low-tech,gateway,digital-sovereignty,security,architecture,configuration,enhancement,high-priority,test-all-templates"
|
||||
["hugo"]="hugo-integration,low-priority,enhancement,high-priority,test-all-templates"
|
||||
)
|
||||
# === TEMPLATE LABEL MAPPINGS END ===
|
||||
|
||||
# Load existing labels from repository
|
||||
declare -A LABEL_IDS
|
||||
|
||||
load_existing_labels() {
|
||||
if [[ -z "${GITEA_URL:-}" ]] || [[ -z "${GITEA_TOKEN:-}" ]]; then
|
||||
log_error "GITEA_URL and GITEA_TOKEN must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Loading existing labels from repository..."
|
||||
|
||||
local response=$(curl -s "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/labels" \
|
||||
-H "Authorization: token $GITEA_TOKEN")
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log_error "Failed to fetch labels from repository"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while IFS= read -r line; do
|
||||
local name=$(echo "$line" | jq -r '.name')
|
||||
local id=$(echo "$line" | jq -r '.id')
|
||||
LABEL_IDS["$name"]="$id"
|
||||
done < <(echo "$response" | jq -c '.[]')
|
||||
|
||||
log_info "Loaded ${#LABEL_IDS[@]} existing labels"
|
||||
}
|
||||
|
||||
# FIXED: Silent version of ensure_label_exists (no stdout pollution!)
|
||||
ensure_label_exists_silent() {
|
||||
local name="$1"
|
||||
local color="${2:-ff6b6b}"
|
||||
local description="${3:-Auto-generated label}"
|
||||
local usage="${4:-manual_assignment}" # ADDED: usage parameter
|
||||
|
||||
log_debug "Checking label: $name"
|
||||
|
||||
if [[ -n "${LABEL_IDS[$name]:-}" ]]; then
|
||||
log_debug "Label $name already exists (ID: ${LABEL_IDS[$name]})"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_debug "Creating new label: $name with color $color"
|
||||
|
||||
# Create label (redirect output to prevent stdout mixing)
|
||||
local response=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
"$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/labels" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"name\": \"$name\",
|
||||
\"color\": \"$color\",
|
||||
\"description\": \"$description\"
|
||||
}" 2>/dev/null)
|
||||
|
||||
local http_code=$(echo "$response" | tail -n1)
|
||||
local response_body=$(echo "$response" | head -n -1)
|
||||
|
||||
if [[ "$http_code" == "201" ]]; then
|
||||
local new_id=$(echo "$response_body" | jq -r '.id')
|
||||
LABEL_IDS["$name"]="$new_id"
|
||||
|
||||
# FIXED: Track for auto-update with correct usage
|
||||
NEW_LABELS_CREATED["$name"]="$color:auto_generated:$usage"
|
||||
log_debug "Successfully created label $name (ID: $new_id)"
|
||||
log_debug "Added to NEW_LABELS_CREATED: $name -> ${NEW_LABELS_CREATED[$name]}"
|
||||
return 0
|
||||
else
|
||||
log_debug "Failed to create label $name (HTTP: $http_code)"
|
||||
log_debug "Response: $response_body"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Process labels for template (updates global arrays, no output)
|
||||
process_labels_for_template() {
|
||||
local template="$1"
|
||||
shift
|
||||
local additional_labels=("$@")
|
||||
|
||||
log_debug "Processing labels for template: $template"
|
||||
log_debug "Additional labels: ${additional_labels[*]}"
|
||||
|
||||
# Get template labels
|
||||
local template_labels_string="${TEMPLATE_LABELS[$template]:-}"
|
||||
local all_labels=()
|
||||
|
||||
# Add template labels
|
||||
if [[ -n "$template_labels_string" ]]; then
|
||||
IFS=',' read -ra template_labels <<< "$template_labels_string"
|
||||
all_labels+=("${template_labels[@]}")
|
||||
log_debug "Template labels: ${template_labels[*]}"
|
||||
fi
|
||||
|
||||
# Add additional labels
|
||||
all_labels+=("${additional_labels[@]}")
|
||||
log_debug "All labels to process: ${all_labels[*]}"
|
||||
|
||||
# Process all labels and ensure they exist
|
||||
for label in "${all_labels[@]}"; do
|
||||
log_debug "Processing label: $label"
|
||||
|
||||
# Process both known and unknown labels
|
||||
if [[ -n "${LABEL_DEFINITIONS[$label]:-}" ]]; then
|
||||
log_debug "Known label: $label"
|
||||
# Known label - use defined color and context
|
||||
local color=$(get_label_color "$label")
|
||||
local context=$(get_label_context "$label")
|
||||
|
||||
ensure_label_exists_silent "$label" "$color" "Furt: $context"
|
||||
else
|
||||
log_debug "Unknown label: $label - creating with smart defaults"
|
||||
# Unknown label - auto-create with smart defaults
|
||||
local default_color="ff6b6b"
|
||||
local default_context="auto_generated"
|
||||
|
||||
# Smart defaults based on label pattern
|
||||
if [[ "$label" == service-* ]]; then
|
||||
default_color="1d76db"
|
||||
default_context="service_integration"
|
||||
default_usage="service_specific" # FIXED: Not all_templates!
|
||||
log_debug "Service label detected - using blue color and service_specific usage"
|
||||
elif [[ "$label" == *-priority ]]; then
|
||||
default_color="d73a4a"
|
||||
default_context="priority_level"
|
||||
default_usage="priority_management"
|
||||
log_debug "Priority label detected - using red color"
|
||||
elif [[ "$label" == hugo-* ]]; then
|
||||
default_color="ff7518"
|
||||
default_context="frontend_integration"
|
||||
default_usage="hugo_integration"
|
||||
log_debug "Hugo label detected - using orange color"
|
||||
else
|
||||
default_usage="manual_assignment"
|
||||
fi
|
||||
|
||||
ensure_label_exists_silent "$label" "$default_color" "Furt: $default_context"
|
||||
|
||||
# FIXED: Track with correct usage
|
||||
if [[ -n "${LABEL_IDS[$label]:-}" ]] && [[ -n "${NEW_LABELS_CREATED[$label]:-}" ]]; then
|
||||
NEW_LABELS_CREATED["$label"]="$default_color:$default_context:$default_usage"
|
||||
log_debug "Updated NEW_LABELS_CREATED with correct usage: $label -> $default_color:$default_context:$default_usage"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Debug: Check if this label was newly created
|
||||
if [[ -n "${LABEL_IDS[$label]:-}" ]]; then
|
||||
if [[ -n "${NEW_LABELS_CREATED[$label]:-}" ]]; then
|
||||
log_debug " → Label $label was newly created and tracked"
|
||||
else
|
||||
log_debug " → Label $label already existed"
|
||||
fi
|
||||
else
|
||||
log_warning "Failed to process label: $label"
|
||||
fi
|
||||
done
|
||||
|
||||
# Debug: Check NEW_LABELS_CREATED at end of processing
|
||||
log_debug "NEW_LABELS_CREATED after processing: ${#NEW_LABELS_CREATED[@]} entries"
|
||||
if [[ "${#NEW_LABELS_CREATED[@]}" -gt 0 ]] 2>/dev/null; then
|
||||
for label_name in "${!NEW_LABELS_CREATED[@]}"; do
|
||||
log_debug " - $label_name: ${NEW_LABELS_CREATED[$label_name]}"
|
||||
done
|
||||
else
|
||||
log_debug " (no entries in NEW_LABELS_CREATED array)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Build JSON from already processed labels (pure function, no side effects)
|
||||
build_labels_json_from_processed() {
|
||||
local template="$1"
|
||||
shift
|
||||
local additional_labels=("$@")
|
||||
|
||||
log_debug "Building JSON from processed labels"
|
||||
|
||||
# Get template labels
|
||||
local template_labels_string="${TEMPLATE_LABELS[$template]:-}"
|
||||
local all_labels=()
|
||||
|
||||
# Add template labels
|
||||
if [[ -n "$template_labels_string" ]]; then
|
||||
IFS=',' read -ra template_labels <<< "$template_labels_string"
|
||||
all_labels+=("${template_labels[@]}")
|
||||
fi
|
||||
|
||||
# Add additional labels
|
||||
all_labels+=("${additional_labels[@]}")
|
||||
|
||||
# Collect IDs from already processed labels
|
||||
local label_ids=()
|
||||
for label in "${all_labels[@]}"; do
|
||||
if [[ -n "${LABEL_IDS[$label]:-}" ]]; then
|
||||
label_ids+=("${LABEL_IDS[$label]}")
|
||||
log_debug "Added ID ${LABEL_IDS[$label]} for $label to JSON"
|
||||
else
|
||||
log_warning "No ID found for label: $label"
|
||||
fi
|
||||
done
|
||||
|
||||
log_debug "Final label IDs for JSON: ${label_ids[*]}"
|
||||
|
||||
# Build JSON array (clean output only!)
|
||||
if [[ ${#label_ids[@]} -gt 0 ]]; then
|
||||
printf '[%s]' "$(IFS=','; echo "${label_ids[*]}")"
|
||||
else
|
||||
echo "[]"
|
||||
fi
|
||||
}
|
||||
|
||||
# DEPRECATED: Old build_labels_json function (kept for compatibility)
|
||||
build_labels_json() {
|
||||
local template="$1"
|
||||
shift
|
||||
local additional_labels=("$@")
|
||||
|
||||
log_debug "Building labels for template: $template"
|
||||
log_debug "Additional labels: ${additional_labels[*]}"
|
||||
|
||||
# Get template labels
|
||||
local template_labels_string="${TEMPLATE_LABELS[$template]:-}"
|
||||
local all_labels=()
|
||||
|
||||
# Add template labels
|
||||
if [[ -n "$template_labels_string" ]]; then
|
||||
IFS=',' read -ra template_labels <<< "$template_labels_string"
|
||||
all_labels+=("${template_labels[@]}")
|
||||
log_debug "Template labels: ${template_labels[*]}"
|
||||
fi
|
||||
|
||||
# Add additional labels
|
||||
all_labels+=("${additional_labels[@]}")
|
||||
log_debug "All labels to process: ${all_labels[*]}"
|
||||
|
||||
# FIXED: Ensure all labels exist and collect IDs (handles unknown labels!)
|
||||
local label_ids=()
|
||||
for label in "${all_labels[@]}"; do
|
||||
log_debug "Processing label: $label"
|
||||
|
||||
# Process both known and unknown labels
|
||||
if [[ -n "${LABEL_DEFINITIONS[$label]:-}" ]]; then
|
||||
log_debug "Known label: $label"
|
||||
# Known label - use defined color and context
|
||||
local color=$(get_label_color "$label")
|
||||
local context=$(get_label_context "$label")
|
||||
|
||||
ensure_label_exists_silent "$label" "$color" "Furt: $context"
|
||||
else
|
||||
log_debug "Unknown label: $label - creating with smart defaults"
|
||||
# FIXED: Unknown label - auto-create with smart defaults
|
||||
local default_color="ff6b6b"
|
||||
local default_context="auto_generated"
|
||||
|
||||
# Smart defaults based on label pattern
|
||||
if [[ "$label" == service-* ]]; then
|
||||
default_color="1d76db"
|
||||
default_context="service_integration"
|
||||
log_debug "Service label detected - using blue color"
|
||||
elif [[ "$label" == *-priority ]]; then
|
||||
default_color="d73a4a"
|
||||
default_context="priority_level"
|
||||
log_debug "Priority label detected - using red color"
|
||||
elif [[ "$label" == hugo-* ]]; then
|
||||
default_color="ff7518"
|
||||
default_context="frontend_integration"
|
||||
log_debug "Hugo label detected - using orange color"
|
||||
fi
|
||||
|
||||
ensure_label_exists_silent "$label" "$default_color" "Furt: $default_context"
|
||||
fi
|
||||
|
||||
# Collect ID if label was created/exists
|
||||
if [[ -n "${LABEL_IDS[$label]:-}" ]]; then
|
||||
label_ids+=("${LABEL_IDS[$label]}")
|
||||
log_debug "Added label ID: ${LABEL_IDS[$label]} for $label"
|
||||
|
||||
# Debug: Check if this label was newly created
|
||||
if [[ -n "${NEW_LABELS_CREATED[$label]:-}" ]]; then
|
||||
log_debug " → This label was newly created and tracked"
|
||||
else
|
||||
log_debug " → This label already existed"
|
||||
fi
|
||||
else
|
||||
log_warning "Failed to get ID for label: $label"
|
||||
fi
|
||||
done
|
||||
|
||||
log_debug "Final label IDs: ${label_ids[*]}"
|
||||
|
||||
# Debug: Check NEW_LABELS_CREATED at end of function
|
||||
log_debug "NEW_LABELS_CREATED at end of build_labels_json: ${#NEW_LABELS_CREATED[@]} entries"
|
||||
if [[ "${#NEW_LABELS_CREATED[@]}" -gt 0 ]] 2>/dev/null; then
|
||||
for label_name in "${!NEW_LABELS_CREATED[@]}"; do
|
||||
log_debug " - $label_name: ${NEW_LABELS_CREATED[$label_name]}"
|
||||
done
|
||||
else
|
||||
log_debug " (no entries in NEW_LABELS_CREATED array)"
|
||||
fi
|
||||
|
||||
# Build JSON array (clean output only!)
|
||||
if [[ ${#label_ids[@]} -gt 0 ]]; then
|
||||
printf '[%s]' "$(IFS=','; echo "${label_ids[*]}")"
|
||||
else
|
||||
echo "[]"
|
||||
fi
|
||||
}
|
||||
|
||||
# Show which labels are being used (AFTER JSON building to avoid stdout pollution)
|
||||
show_labels_used() {
|
||||
local template="$1"
|
||||
shift
|
||||
local additional_labels=("$@")
|
||||
|
||||
log_info "Labels used for this issue:"
|
||||
|
||||
# Show template labels
|
||||
local template_labels_string="${TEMPLATE_LABELS[$template]:-}"
|
||||
if [[ -n "$template_labels_string" ]]; then
|
||||
IFS=',' read -ra template_labels <<< "$template_labels_string"
|
||||
for label in "${template_labels[@]}"; do
|
||||
if [[ -n "${LABEL_IDS[$label]:-}" ]]; then
|
||||
log_info " ✅ $label (ID: ${LABEL_IDS[$label]})"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Show additional labels (these may have been newly created)
|
||||
for label in "${additional_labels[@]}"; do
|
||||
if [[ -n "${LABEL_IDS[$label]:-}" ]]; then
|
||||
if [[ -n "${NEW_LABELS_CREATED[$label]:-}" ]]; then
|
||||
log_info " ✅ $label (ID: ${LABEL_IDS[$label]}) [NEW!]"
|
||||
else
|
||||
log_info " ✅ $label (ID: ${LABEL_IDS[$label]})"
|
||||
fi
|
||||
else
|
||||
log_warning " ❌ $label (failed to create)"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# FIXED: AUTO-UPDATE with safe array handling and correct path
|
||||
auto_update_scripts_if_needed() {
|
||||
# FIXED: Safe check for empty associative array
|
||||
local new_labels_count=0
|
||||
if [[ "${#NEW_LABELS_CREATED[@]}" -gt 0 ]] 2>/dev/null; then
|
||||
new_labels_count=${#NEW_LABELS_CREATED[@]}
|
||||
fi
|
||||
|
||||
log_debug "Auto-update check: $new_labels_count new labels created"
|
||||
|
||||
# Debug: Show what's in NEW_LABELS_CREATED
|
||||
if [[ $new_labels_count -gt 0 ]]; then
|
||||
log_debug "NEW_LABELS_CREATED contents:"
|
||||
for label_name in "${!NEW_LABELS_CREATED[@]}"; do
|
||||
log_debug " - $label_name: ${NEW_LABELS_CREATED[$label_name]}"
|
||||
done
|
||||
else
|
||||
log_debug "NEW_LABELS_CREATED is empty or unset"
|
||||
# Debug: Try to list what's in the array anyway
|
||||
if [[ "${#NEW_LABELS_CREATED[@]}" -gt 0 ]] 2>/dev/null; then
|
||||
for key in "${!NEW_LABELS_CREATED[@]}"; do
|
||||
log_debug " Found key: $key"
|
||||
done
|
||||
else
|
||||
log_debug " Array iteration failed - truly empty"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $new_labels_count -eq 0 ]]; then
|
||||
log_debug "No new labels created - skipping auto-update"
|
||||
return 0 # No new labels, no update needed
|
||||
fi
|
||||
|
||||
log_info "🔄 Auto-updating scripts with $new_labels_count new labels..."
|
||||
|
||||
# Check if update script exists
|
||||
local update_script="$SCRIPT_DIR/update_script_labels.sh"
|
||||
if [[ ! -f "$update_script" ]]; then
|
||||
log_warning "Update script not found: $update_script"
|
||||
log_warning "Skipping auto-update"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ ! -x "$update_script" ]]; then
|
||||
log_warning "Update script not executable: $update_script"
|
||||
log_warning "Making executable..."
|
||||
chmod +x "$update_script"
|
||||
fi
|
||||
|
||||
# Add new labels to registry
|
||||
for label_name in "${!NEW_LABELS_CREATED[@]}"; do
|
||||
local label_info="${NEW_LABELS_CREATED[$label_name]}"
|
||||
local color=$(echo "$label_info" | cut -d':' -f1)
|
||||
local context=$(echo "$label_info" | cut -d':' -f2)
|
||||
local usage=$(echo "$label_info" | cut -d':' -f3)
|
||||
|
||||
log_info "Adding '$label_name' to registry..."
|
||||
|
||||
# Add to registry (suppressing output to avoid noise)
|
||||
FURT_AUTO_UPDATE=true "$update_script" add "$label_name" "$color" "$context" "$usage" >/dev/null 2>&1 || {
|
||||
log_warning "Failed to add $label_name to registry"
|
||||
}
|
||||
done
|
||||
|
||||
# Update all scripts with new labels
|
||||
log_info "Synchronizing all scripts..."
|
||||
"$update_script" update >/dev/null 2>&1 || {
|
||||
log_warning "Failed to update scripts"
|
||||
return 0
|
||||
}
|
||||
|
||||
log_success "✅ All scripts automatically synchronized with new labels!"
|
||||
|
||||
# Show what was added
|
||||
echo ""
|
||||
echo "🆕 New labels created and synchronized:"
|
||||
for label_name in "${!NEW_LABELS_CREATED[@]}"; do
|
||||
echo " - $label_name (ID: ${LABEL_IDS[$label_name]})"
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Create issue templates
|
||||
create_service_issue() {
|
||||
local service_name="${1:-newsletter}"
|
||||
|
||||
log_debug "Creating service issue for: $service_name"
|
||||
|
||||
local title="[SERVICE] $service_name für Furt Gateway"
|
||||
local body="# Service-Request: $service_name
|
||||
|
||||
## 🏷️ Service-Details
|
||||
**Name:** $service_name
|
||||
**Port:** TBD
|
||||
**Zweck:** [Service-Beschreibung]
|
||||
|
||||
## 📝 Funktionsanforderungen
|
||||
- [ ] [Anforderung 1]
|
||||
- [ ] [Anforderung 2]
|
||||
- [ ] [Anforderung 3]
|
||||
|
||||
## 🔗 Gateway-Integration
|
||||
- [ ] **Routing:** \`/v1/$service_name/*\`
|
||||
- [ ] **Auth:** API-Key required
|
||||
- [ ] **Rate-Limiting:** TBD req/min
|
||||
- [ ] **Health-Check:** \`/health\`
|
||||
|
||||
## 🎯 Hugo-Integration
|
||||
- [ ] **Shortcode:** \`{{< furt-$service_name >}}\`
|
||||
- [ ] **JavaScript-Client**
|
||||
- [ ] **CSS-Styling**
|
||||
|
||||
## ⚡ Priorität
|
||||
🔥 **Hoch** - benötigt für Website-Launch"
|
||||
|
||||
# Process labels first, then build JSON
|
||||
local service_label="service-$service_name"
|
||||
log_debug "Service label to add: $service_label"
|
||||
|
||||
# First: Process all labels (this updates global arrays)
|
||||
process_labels_for_template "service" "$service_label"
|
||||
|
||||
# Then: Build JSON from already-processed labels (pure function, no side effects)
|
||||
local labels_json=$(build_labels_json_from_processed "service" "$service_label")
|
||||
|
||||
# Show which labels are being used (AFTER processing when labels are actually created)
|
||||
show_labels_used "service" "$service_label"
|
||||
|
||||
create_issue "$title" "$body" "$labels_json"
|
||||
}
|
||||
|
||||
create_architecture_issue() {
|
||||
local topic="${1:-middleware-optimization}"
|
||||
|
||||
local title="[ARCH] Gateway $topic"
|
||||
local body="# Architektur-Diskussion: $topic
|
||||
|
||||
## 🎯 Architektur-Thema
|
||||
[Beschreibung des Architektur-Themas]
|
||||
|
||||
## 📊 Aktuelle Situation
|
||||
- [Status Quo 1]
|
||||
- [Status Quo 2]
|
||||
|
||||
## 💡 Vorgeschlagene Änderung
|
||||
- [Vorschlag 1]
|
||||
- [Vorschlag 2]
|
||||
|
||||
## 🔄 Alternativen
|
||||
1. **Option A:** [Beschreibung]
|
||||
2. **Option B:** [Beschreibung]
|
||||
|
||||
## 📈 Betroffene Bereiche
|
||||
- [ ] Gateway-Performance
|
||||
- [ ] Service-Integration
|
||||
- [ ] Security
|
||||
- [ ] Configuration-Management"
|
||||
|
||||
# Process labels first, then build JSON
|
||||
process_labels_for_template "architecture"
|
||||
local labels_json=$(build_labels_json_from_processed "architecture")
|
||||
|
||||
create_issue "$title" "$body" "$labels_json"
|
||||
}
|
||||
|
||||
# Generic template creator
|
||||
create_generic_issue() {
|
||||
local template="$1"
|
||||
local component="${2:-gateway}"
|
||||
local description="[Beschreibung hinzufügen]"
|
||||
|
||||
# Safe parameter handling for $3
|
||||
if [[ $# -ge 3 ]] && [[ -n "${3:-}" ]]; then
|
||||
description="$3"
|
||||
fi
|
||||
|
||||
log_debug "Creating $template issue for: $component"
|
||||
|
||||
local title_prefix
|
||||
case "$template" in
|
||||
api) title_prefix="[API]" ;;
|
||||
security) title_prefix="[SEC]" ;;
|
||||
hugo) title_prefix="[HUGO]" ;;
|
||||
deployment) title_prefix="[DEPLOY]" ;;
|
||||
bug) title_prefix="[BUG]" ;;
|
||||
*) title_prefix="[${template^^}]" ;;
|
||||
esac
|
||||
|
||||
local title="$title_prefix $component $(echo ${template^} | sed 's/api/API Contract/')"
|
||||
local body="# ${template^}: $component
|
||||
|
||||
## 📝 ${template^}-Details
|
||||
**Komponente:** $component
|
||||
**Beschreibung:** $description
|
||||
|
||||
## 🎯 Anforderungen
|
||||
- [ ] [Anforderung 1]
|
||||
- [ ] [Anforderung 2]
|
||||
- [ ] [Anforderung 3]
|
||||
|
||||
## ✅ Definition of Done
|
||||
- [ ] [DoD Kriterium 1]
|
||||
- [ ] [DoD Kriterium 2]
|
||||
- [ ] [DoD Kriterium 3]"
|
||||
|
||||
# Process labels first, then build JSON
|
||||
process_labels_for_template "$template"
|
||||
local labels_json=$(build_labels_json_from_processed "$template")
|
||||
|
||||
show_labels_used "$template"
|
||||
create_issue "$title" "$body" "$labels_json"
|
||||
}
|
||||
|
||||
# Show usage information
|
||||
show_usage() {
|
||||
echo "🎯 Furt API-Gateway Issue Creator (Debug Version)"
|
||||
echo ""
|
||||
echo "Usage: $0 [TEMPLATE] [OPTIONS]"
|
||||
echo ""
|
||||
echo "📋 Available Templates:"
|
||||
echo " service [name] New service for gateway (default: newsletter)"
|
||||
echo " architecture [topic] Gateway architecture discussion (default: middleware-optimization)"
|
||||
echo " performance [comp] Performance optimization (default: gateway)"
|
||||
echo " api [service] API contract update (default: formular2mail)"
|
||||
echo " security [comp] Security review/issue (default: gateway)"
|
||||
echo " bug [comp] [desc] Bug report (default: gateway)"
|
||||
echo " hugo [feature] Hugo integration (default: shortcode)"
|
||||
echo " deployment [comp] Deployment issue (default: gateway)"
|
||||
echo " custom Custom issue (interactive)"
|
||||
echo ""
|
||||
echo "🚀 Examples:"
|
||||
echo " $0 service newsletter # Create newsletter service request"
|
||||
echo " $0 architecture rate-limiting # Discuss rate limiting architecture"
|
||||
echo " $0 performance gateway # Gateway performance optimization"
|
||||
echo " $0 custom # Interactive custom issue"
|
||||
echo ""
|
||||
echo "🔧 Debug Mode:"
|
||||
echo " Set DEBUG=1 for verbose debug output"
|
||||
}
|
||||
|
||||
# Generic issue creation
|
||||
create_issue() {
|
||||
local title="$1"
|
||||
local body="$2"
|
||||
local labels_json="$3"
|
||||
|
||||
log_info "Creating issue: $title"
|
||||
log_debug "Labels JSON: $labels_json"
|
||||
|
||||
local response=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
"$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"title\": $(echo "$title" | jq -R .),
|
||||
\"body\": $(echo "$body" | jq -R -s .),
|
||||
\"labels\": $labels_json
|
||||
}")
|
||||
|
||||
local http_code=$(echo "$response" | tail -n1)
|
||||
local response_body=$(echo "$response" | head -n -1)
|
||||
|
||||
if [[ "$http_code" == "201" ]]; then
|
||||
local issue_number=$(echo "$response_body" | jq -r '.number')
|
||||
local issue_url=$(echo "$response_body" | jq -r '.html_url')
|
||||
|
||||
log_success "Issue #$issue_number created!"
|
||||
echo "🔗 $issue_url"
|
||||
else
|
||||
log_error "Failed to create issue (HTTP: $http_code)"
|
||||
log_error "Response: $response_body"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local template="${1:-help}"
|
||||
|
||||
# Enable debug if requested
|
||||
if [[ "${DEBUG:-}" == "1" ]]; then
|
||||
log_info "Debug mode enabled"
|
||||
fi
|
||||
|
||||
if [[ "$template" == "help" ]] || [[ "$template" == "--help" ]] || [[ "$template" == "-h" ]]; then
|
||||
show_usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Load existing labels
|
||||
load_existing_labels
|
||||
|
||||
case "$template" in
|
||||
service)
|
||||
create_service_issue "${2:-newsletter}"
|
||||
;;
|
||||
architecture)
|
||||
create_architecture_issue "${2:-middleware-optimization}"
|
||||
;;
|
||||
performance)
|
||||
local component="${2:-gateway}"
|
||||
local title="[PERF] $component Performance-Optimierung"
|
||||
local body="# Performance-Optimierung: $component"
|
||||
|
||||
process_labels_for_template "performance"
|
||||
local labels_json=$(build_labels_json_from_processed "performance")
|
||||
|
||||
create_issue "$title" "$body" "$labels_json"
|
||||
;;
|
||||
api|security|hugo|deployment|bug)
|
||||
if [[ $# -ge 3 ]]; then
|
||||
create_generic_issue "$template" "${2:-gateway}" "$3"
|
||||
else
|
||||
create_generic_issue "$template" "${2:-gateway}"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown template: $template"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# FIXED: AUTO-UPDATE: Automatically sync scripts if new labels were created
|
||||
auto_update_scripts_if_needed
|
||||
}
|
||||
|
||||
# Run if executed directly
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Load environment
|
||||
if [ -f .env ]; then
|
||||
export $(cat .env | grep -v '^#' | xargs)
|
||||
else
|
||||
echo "❌ .env file not found!"
|
||||
echo "📋 Copy .env.example to .env and configure it first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate required variables
|
||||
if [ -z "$GITEA_URL" ] || [ -z "$REPO_OWNER" ] || [ -z "$REPO_NAME" ] || [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "❌ Missing required environment variables in .env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
# Get all issues with nice formatting
|
||||
get_all_issues() {
|
||||
log_info "Fetching all issues..."
|
||||
echo ""
|
||||
|
||||
response=$(curl -s "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues" \
|
||||
-H "Authorization: token $GITEA_TOKEN")
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ Error fetching issues"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$response" | jq -r '.[] |
|
||||
"🎯 #\(.number) \(.title)",
|
||||
" 📊 State: \(.state) | 🏷️ Labels: \(.labels | map(.name) | join(", ") // "none")",
|
||||
" 🔗 \(.html_url)",
|
||||
""'
|
||||
}
|
||||
|
||||
# Get issues by label
|
||||
get_issues_by_label() {
|
||||
local label="$1"
|
||||
log_info "Fetching issues with label: $label"
|
||||
echo ""
|
||||
|
||||
response=$(curl -s "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues?labels=$label" \
|
||||
-H "Authorization: token $GITEA_TOKEN")
|
||||
|
||||
echo "$response" | jq -r '.[] |
|
||||
"🎯 #\(.number) \(.title)",
|
||||
" 📊 \(.state) | 🔗 \(.html_url)",
|
||||
""'
|
||||
}
|
||||
|
||||
# Get issue details
|
||||
get_issue_details() {
|
||||
local issue_number="$1"
|
||||
log_info "Fetching details for issue #$issue_number"
|
||||
echo ""
|
||||
|
||||
response=$(curl -s "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues/$issue_number" \
|
||||
-H "Authorization: token $GITEA_TOKEN")
|
||||
|
||||
echo "$response" | jq -r '
|
||||
"🎯 Issue #\(.number): \(.title)",
|
||||
"📊 State: \(.state)",
|
||||
"👤 Assignees: \(.assignees | map(.login) | join(", ") // "none")",
|
||||
"🏷️ Labels: \(.labels | map(.name) | join(", ") // "none")",
|
||||
"📅 Created: \(.created_at)",
|
||||
"🔗 URL: \(.html_url)",
|
||||
"",
|
||||
"📝 Body:",
|
||||
"\(.body // "No description")",
|
||||
""'
|
||||
}
|
||||
|
||||
# Close issue
|
||||
close_issue() {
|
||||
local issue_number="$1"
|
||||
log_info "Closing issue #$issue_number"
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X PATCH \
|
||||
"$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues/$issue_number" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"state": "closed"}')
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
if [ "$http_code" = "201" ]; then
|
||||
log_success "Issue #$issue_number closed"
|
||||
else
|
||||
echo "❌ Failed to close issue (HTTP: $http_code)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get pipeline status (issues grouped by Kanban columns for Furt)
|
||||
get_pipeline_status() {
|
||||
log_info "Furt API Gateway - Pipeline Status Overview"
|
||||
echo ""
|
||||
|
||||
echo "🔧 SERVICE REQUESTS:"
|
||||
get_issues_by_label "service-request" | head -10
|
||||
|
||||
echo "🏗️ ARCHITECTURE DISCUSSIONS:"
|
||||
get_issues_by_label "architecture"
|
||||
|
||||
echo "🚀 PERFORMANCE OPTIMIZATIONS:"
|
||||
get_issues_by_label "performance"
|
||||
|
||||
echo "🔒 SECURITY REVIEWS:"
|
||||
get_issues_by_label "security"
|
||||
|
||||
echo "🐛 BUGS:"
|
||||
get_issues_by_label "bug"
|
||||
|
||||
echo "🌐 HUGO INTEGRATIONS:"
|
||||
get_issues_by_label "hugo-integration"
|
||||
|
||||
echo "📋 WORK IN PROGRESS:"
|
||||
get_issues_by_label "enhancement" | head -5
|
||||
}
|
||||
|
||||
# Issue statistics
|
||||
get_stats() {
|
||||
log_info "Furt API Gateway - Issue Statistics"
|
||||
echo ""
|
||||
|
||||
all_issues=$(curl -s "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues" \
|
||||
-H "Authorization: token $GITEA_TOKEN")
|
||||
|
||||
total=$(echo "$all_issues" | jq length)
|
||||
open=$(echo "$all_issues" | jq '[.[] | select(.state == "open")] | length')
|
||||
closed=$(echo "$all_issues" | jq '[.[] | select(.state == "closed")] | length')
|
||||
|
||||
echo "📊 Total Issues: $total"
|
||||
echo "✅ Open: $open"
|
||||
echo "🔒 Closed: $closed"
|
||||
echo ""
|
||||
|
||||
echo "🏷️ Furt Labels:"
|
||||
echo "$all_issues" | jq -r '[.[] | .labels[].name] | group_by(.) | map({label: .[0], count: length}) | sort_by(.count) | reverse | limit(10; .[]) | " \(.label): \(.count)"'
|
||||
}
|
||||
|
||||
case "${1:-help}" in
|
||||
"all"|"")
|
||||
get_all_issues
|
||||
;;
|
||||
"gateway")
|
||||
get_issues_by_label "gateway"
|
||||
;;
|
||||
"service-request")
|
||||
get_issues_by_label "service-request"
|
||||
;;
|
||||
"service-formular2mail")
|
||||
get_issues_by_label "service-formular2mail"
|
||||
;;
|
||||
"service-sagjan")
|
||||
get_issues_by_label "service-sagjan"
|
||||
;;
|
||||
"architecture")
|
||||
get_issues_by_label "architecture"
|
||||
;;
|
||||
"performance")
|
||||
get_issues_by_label "performance"
|
||||
;;
|
||||
"security")
|
||||
get_issues_by_label "security"
|
||||
;;
|
||||
"bug")
|
||||
get_issues_by_label "bug"
|
||||
;;
|
||||
"enhancement")
|
||||
get_issues_by_label "enhancement"
|
||||
;;
|
||||
"hugo")
|
||||
get_issues_by_label "hugo-integration"
|
||||
;;
|
||||
"deployment")
|
||||
get_issues_by_label "deployment"
|
||||
;;
|
||||
"testing")
|
||||
get_issues_by_label "testing"
|
||||
;;
|
||||
"documentation")
|
||||
get_issues_by_label "documentation"
|
||||
;;
|
||||
"pipeline")
|
||||
get_pipeline_status
|
||||
;;
|
||||
"stats")
|
||||
get_stats
|
||||
;;
|
||||
"close")
|
||||
if [ -z "$2" ]; then
|
||||
echo "Usage: $0 close ISSUE_NUMBER"
|
||||
exit 1
|
||||
fi
|
||||
close_issue "$2"
|
||||
;;
|
||||
[0-9]*)
|
||||
get_issue_details "$1"
|
||||
;;
|
||||
*)
|
||||
echo "🎯 Furt API Gateway - Issues Manager"
|
||||
echo ""
|
||||
echo "Usage: $0 [COMMAND] [OPTIONS]"
|
||||
echo ""
|
||||
echo "📋 List Commands:"
|
||||
echo " all List all issues (default)"
|
||||
echo " gateway Gateway core issues"
|
||||
echo " service-request New service requests"
|
||||
echo " service-formular2mail Formular2mail service issues"
|
||||
echo " service-sagjan Sagjan service issues"
|
||||
echo " architecture Architecture discussions"
|
||||
echo " performance Performance optimizations"
|
||||
echo " security Security reviews"
|
||||
echo " bug Bug reports"
|
||||
echo " enhancement New features"
|
||||
echo " hugo Hugo integration issues"
|
||||
echo " deployment Deployment issues"
|
||||
echo " testing Testing issues"
|
||||
echo " documentation Documentation updates"
|
||||
echo ""
|
||||
echo "📊 Analysis Commands:"
|
||||
echo " pipeline Kanban pipeline status"
|
||||
echo " stats Issue statistics"
|
||||
echo ""
|
||||
echo "⚙️ Management Commands:"
|
||||
echo " close NUM Close issue #NUM"
|
||||
echo " NUM Show details for issue #NUM"
|
||||
echo ""
|
||||
echo "🚀 Examples:"
|
||||
echo " $0 # List all issues"
|
||||
echo " $0 pipeline # Show pipeline status"
|
||||
echo " $0 service-request # Show service requests"
|
||||
echo " $0 gateway # Show gateway issues"
|
||||
echo " $0 5 # Show issue #5 details"
|
||||
echo " $0 close 3 # Close issue #3"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Load environment
|
||||
if [ -f .env ]; then
|
||||
export $(cat .env | grep -v '^#' | xargs)
|
||||
else
|
||||
echo "❌ .env file not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
# Get all labels with IDs
|
||||
declare -A LABEL_IDS
|
||||
get_labels() {
|
||||
response=$(curl -s "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/labels" \
|
||||
-H "Authorization: token $GITEA_TOKEN")
|
||||
while IFS= read -r line; do
|
||||
name=$(echo "$line" | jq -r '.name')
|
||||
id=$(echo "$line" | jq -r '.id')
|
||||
LABEL_IDS["$name"]="$id"
|
||||
done < <(echo "$response" | jq -c '.[]')
|
||||
}
|
||||
|
||||
# Add comment to issue
|
||||
add_comment() {
|
||||
local issue_number="$1"
|
||||
local comment="$2"
|
||||
|
||||
# Use jq for proper JSON escaping
|
||||
local json_payload=$(jq -n --arg body "$comment" '{body: $body}')
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
"$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues/$issue_number/comments" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$json_payload")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
if [ "$http_code" = "201" ]; then
|
||||
log_success "Comment added to issue #$issue_number"
|
||||
else
|
||||
log_error "Failed to add comment (HTTP: $http_code)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Update issue labels - FIXED VERSION
|
||||
update_labels() {
|
||||
local issue_number="$1"
|
||||
local labels_string="$2"
|
||||
|
||||
get_labels
|
||||
|
||||
# Convert to ID array
|
||||
local valid_label_ids=()
|
||||
IFS=',' read -ra LABEL_ARRAY <<< "$labels_string"
|
||||
|
||||
for label in "${LABEL_ARRAY[@]}"; do
|
||||
label=$(echo "$label" | xargs)
|
||||
if [ -n "${LABEL_IDS[$label]}" ]; then
|
||||
valid_label_ids+=("${LABEL_IDS[$label]}")
|
||||
else
|
||||
log_error "Label '$label' not found!"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Build ID array JSON
|
||||
local labels_json="["
|
||||
for i in "${!valid_label_ids[@]}"; do
|
||||
if [ $i -gt 0 ]; then
|
||||
labels_json="${labels_json},"
|
||||
fi
|
||||
labels_json="${labels_json}${valid_label_ids[$i]}"
|
||||
done
|
||||
labels_json="${labels_json}]"
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X PUT \
|
||||
"$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues/$issue_number/labels" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$labels_json")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
if [ "$http_code" = "200" ]; then
|
||||
log_success "Labels updated for issue #$issue_number"
|
||||
else
|
||||
log_error "Failed to update labels (HTTP: $http_code)"
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1:-help}" in
|
||||
"comment")
|
||||
if [ -z "$2" ] || [ -z "$3" ]; then
|
||||
echo "Usage: $0 comment ISSUE_NUMBER \"COMMENT_TEXT\""
|
||||
exit 1
|
||||
fi
|
||||
add_comment "$2" "$3"
|
||||
;;
|
||||
"labels")
|
||||
if [ -z "$2" ] || [ -z "$3" ]; then
|
||||
echo "Usage: $0 labels ISSUE_NUMBER \"label1,label2,label3\""
|
||||
exit 1
|
||||
fi
|
||||
update_labels "$2" "$3"
|
||||
;;
|
||||
"progress")
|
||||
if [ -z "$2" ]; then
|
||||
echo "Usage: $0 progress ISSUE_NUMBER"
|
||||
exit 1
|
||||
fi
|
||||
add_comment "$2" "📊 **Progress Update:** Arbeit an dieser Analyse läuft. Erste Quellen werden gesammelt und Framework-Relevanz geprüft."
|
||||
update_labels "$2" "work-in-progress"
|
||||
;;
|
||||
"review")
|
||||
if [ -z "$2" ]; then
|
||||
echo "Usage: $0 review ISSUE_NUMBER"
|
||||
exit 1
|
||||
fi
|
||||
add_comment "$2" "👀 **Ready for Review:** Erste Analyse abgeschlossen. Bitte um Peer-Review der Quellen und Framework-Integration."
|
||||
update_labels "$2" "needs-review"
|
||||
;;
|
||||
"fact-check")
|
||||
if [ -z "$2" ]; then
|
||||
echo "Usage: $0 fact-check ISSUE_NUMBER"
|
||||
exit 1
|
||||
fi
|
||||
add_comment "$2" "🔍 **Fact-Check Required:** Kritische Behauptungen gefunden die zusätzliche Quellen-Verifikation benötigen."
|
||||
update_labels "$2" "fact-check-needed"
|
||||
;;
|
||||
*)
|
||||
echo "🔧 Issue Update Tool (FIXED VERSION)"
|
||||
echo ""
|
||||
echo "Usage: $0 COMMAND ISSUE_NUMBER [OPTIONS]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " comment NUM \"TEXT\" Add comment to issue"
|
||||
echo " labels NUM \"l1,l2\" Update issue labels (using IDs)"
|
||||
echo " progress NUM Mark as work-in-progress"
|
||||
echo " review NUM Mark as ready for review"
|
||||
echo " fact-check NUM Mark as needing fact-check"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 comment 5 \"Erste Quellen gefunden\""
|
||||
echo " $0 labels 3 \"regional-case,work-in-progress\""
|
||||
echo " $0 progress 7"
|
||||
;;
|
||||
esac
|
||||
|
|
@ -1,491 +0,0 @@
|
|||
#!/bin/bash
|
||||
# scripts/update_script_labels.sh
|
||||
# Auto-updates all scripts with current label definitions from registry
|
||||
# FINAL FIXED VERSION with corrected all_templates logic
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
REGISTRY_FILE="$PROJECT_ROOT/configs/labels.registry"
|
||||
|
||||
# Colors for logging
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
# Parse label registry into associative arrays
|
||||
declare -A LABEL_COLORS
|
||||
declare -A LABEL_CONTEXTS
|
||||
declare -A LABEL_USAGES
|
||||
|
||||
# Create registry file if it doesn't exist
|
||||
create_registry_if_missing() {
|
||||
if [[ ! -f "$REGISTRY_FILE" ]]; then
|
||||
log_info "Creating label registry file..."
|
||||
|
||||
mkdir -p "$(dirname "$REGISTRY_FILE")"
|
||||
|
||||
cat > "$REGISTRY_FILE" << 'EOF'
|
||||
# Central Label Registry for Furt API Gateway Project
|
||||
# Format: name:color:context:usage_contexts
|
||||
# This file is the single source of truth for all labels
|
||||
|
||||
# === CORE WORKFLOW LABELS ===
|
||||
service-request:7057ff:new_service:service_templates,status_updates
|
||||
enhancement:84b6eb:improvement:all_templates
|
||||
bug:d73a4a:error:bug_template,status_updates
|
||||
question:d876e3:discussion:question_template
|
||||
|
||||
# === COMPONENT CATEGORIES ===
|
||||
gateway:0052cc:gateway_core:architecture_template,performance_template,service_templates
|
||||
performance:fbca04:optimization:performance_template,architecture_template
|
||||
architecture:d4c5f9:design:architecture_template,gateway
|
||||
security:28a745:security_review:security_template,architecture_template
|
||||
configuration:f9d71c:config_management:deployment_template,architecture_template
|
||||
|
||||
# === SERVICE-SPECIFIC LABELS ===
|
||||
service-formular2mail:1d76db:formular2mail:formular2mail_integration
|
||||
service-sagjan:1d76db:sagjan:sagjan_integration
|
||||
service-newsletter:ff6b6b:newsletter:newsletter_integration
|
||||
|
||||
# === WORKFLOW STATE LABELS ===
|
||||
work-in-progress:fbca04:active:status_updates
|
||||
needs-review:0e8a16:review:status_updates
|
||||
blocked:d73a4a:blocked:status_updates
|
||||
ready-for-deployment:28a745:deploy_ready:status_updates
|
||||
|
||||
# === INTEGRATION LABELS ===
|
||||
hugo-integration:ff7518:frontend:hugo_templates,integration
|
||||
api-contract:5319e7:api_design:api_templates,service_templates
|
||||
breaking-change:d73a4a:breaking:api_templates,architecture_template
|
||||
|
||||
# === PRIORITY LABELS ===
|
||||
high-priority:d73a4a:urgent:all_templates
|
||||
low-priority:0e8a16:nice_to_have:all_templates
|
||||
|
||||
# === META LABELS ===
|
||||
low-tech:6f42c1:low_tech_principle:architecture_template,performance_template,security_template
|
||||
digital-sovereignty:6f42c1:digital_sovereignty:architecture_template,performance_template,security_template
|
||||
good-first-issue:7057ff:beginner_friendly:manual_assignment
|
||||
help-wanted:159818:community_help:manual_assignment
|
||||
|
||||
# === DEPLOYMENT LABELS ===
|
||||
deployment:ff7518:deployment:deployment_template
|
||||
testing:f9d71c:testing:testing_template,integration
|
||||
EOF
|
||||
|
||||
log_success "Created label registry: $REGISTRY_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
parse_registry() {
|
||||
create_registry_if_missing
|
||||
|
||||
log_info "Parsing label registry..."
|
||||
|
||||
while IFS= read -r line; do
|
||||
# Skip comments and empty lines
|
||||
[[ "$line" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
# Parse format: name:color:context:usage_contexts
|
||||
if [[ "$line" =~ ^([^:]+):([^:]+):([^:]+):(.+)$ ]]; then
|
||||
local name="${BASH_REMATCH[1]}"
|
||||
local color="${BASH_REMATCH[2]}"
|
||||
local context="${BASH_REMATCH[3]}"
|
||||
local usage="${BASH_REMATCH[4]}"
|
||||
|
||||
LABEL_COLORS["$name"]="$color"
|
||||
LABEL_CONTEXTS["$name"]="$context"
|
||||
LABEL_USAGES["$name"]="$usage"
|
||||
|
||||
log_info "Loaded label: $name ($context)"
|
||||
fi
|
||||
done < "$REGISTRY_FILE"
|
||||
|
||||
log_success "Loaded ${#LABEL_COLORS[@]} labels from registry"
|
||||
}
|
||||
|
||||
# Generate label definitions section for scripts
|
||||
generate_label_definitions() {
|
||||
cat << 'EOF'
|
||||
# === LABEL DEFINITIONS START ===
|
||||
# This section is auto-maintained by update_script_labels.sh
|
||||
# DO NOT EDIT MANUALLY - Changes will be overwritten
|
||||
declare -A LABEL_DEFINITIONS=(
|
||||
EOF
|
||||
|
||||
for label in "${!LABEL_COLORS[@]}"; do
|
||||
local color="${LABEL_COLORS[$label]}"
|
||||
local context="${LABEL_CONTEXTS[$label]}"
|
||||
local usage="${LABEL_USAGES[$label]}"
|
||||
|
||||
echo " [\"$label\"]=\"color:$color;context:$context;usage:$usage\""
|
||||
done
|
||||
|
||||
cat << 'EOF'
|
||||
)
|
||||
|
||||
# Extract label info
|
||||
get_label_color() { echo "${LABEL_DEFINITIONS[$1]}" | cut -d';' -f1 | cut -d':' -f2; }
|
||||
get_label_context() { echo "${LABEL_DEFINITIONS[$1]}" | cut -d';' -f2 | cut -d':' -f2; }
|
||||
get_label_usage() { echo "${LABEL_DEFINITIONS[$1]}" | cut -d';' -f3 | cut -d':' -f2; }
|
||||
|
||||
# Check if label is valid for context
|
||||
is_label_valid_for_context() {
|
||||
local label="$1"
|
||||
local context="$2"
|
||||
local usage=$(get_label_usage "$label")
|
||||
[[ "$usage" == *"$context"* ]] || [[ "$usage" == "all_templates" ]]
|
||||
}
|
||||
# === LABEL DEFINITIONS END ===
|
||||
EOF
|
||||
}
|
||||
|
||||
# Generate template-to-labels mapping - FIXED VERSION
|
||||
generate_template_mappings() {
|
||||
cat << 'EOF'
|
||||
|
||||
# === TEMPLATE LABEL MAPPINGS START ===
|
||||
# Auto-generated template to label mappings
|
||||
declare -A TEMPLATE_LABELS=(
|
||||
EOF
|
||||
|
||||
# FIXED: Consistent template names with corrected all_templates logic
|
||||
declare -A template_mappings=(
|
||||
["service"]="service_templates"
|
||||
["architecture"]="architecture_template"
|
||||
["performance"]="performance_template"
|
||||
["bug"]="bug_template"
|
||||
["security"]="security_template"
|
||||
["hugo"]="hugo_templates"
|
||||
["api"]="api_templates"
|
||||
["deployment"]="deployment_template"
|
||||
)
|
||||
|
||||
for template_name in "${!template_mappings[@]}"; do
|
||||
local template_usage="${template_mappings[$template_name]}"
|
||||
local labels=()
|
||||
|
||||
# FIXED: First add all_templates labels to every template
|
||||
for label in "${!LABEL_USAGES[@]}"; do
|
||||
local usage="${LABEL_USAGES[$label]}"
|
||||
|
||||
# EXCLUDE service-specific labels from all templates
|
||||
if [[ "$label" == service-* ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Skip manual assignment labels
|
||||
if [[ "$usage" == "manual_assignment" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# FIXED: Add all_templates labels to every template first
|
||||
if [[ "$usage" == "all_templates" ]]; then
|
||||
labels+=("$label")
|
||||
continue
|
||||
fi
|
||||
|
||||
# Add template-specific labels
|
||||
if [[ "$usage" == *"$template_usage"* ]]; then
|
||||
labels+=("$label")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#labels[@]} -gt 0 ]]; then
|
||||
local label_list=$(IFS=','; echo "${labels[*]}")
|
||||
echo " [\"$template_name\"]=\"$label_list\""
|
||||
fi
|
||||
done
|
||||
|
||||
cat << 'EOF'
|
||||
)
|
||||
# === TEMPLATE LABEL MAPPINGS END ===
|
||||
EOF
|
||||
}
|
||||
|
||||
# Generate filter options for get_issues.sh
|
||||
generate_filter_options() {
|
||||
cat << 'EOF'
|
||||
|
||||
# === FILTER OPTIONS START ===
|
||||
# Auto-generated filter options for get_issues.sh
|
||||
show_filter_help() {
|
||||
echo "📋 Available filters:"
|
||||
EOF
|
||||
|
||||
# Group labels by context for better help display
|
||||
declare -A context_labels
|
||||
for label in "${!LABEL_CONTEXTS[@]}"; do
|
||||
local context="${LABEL_CONTEXTS[$label]}"
|
||||
if [[ -z "${context_labels[$context]}" ]]; then
|
||||
context_labels[$context]="$label"
|
||||
else
|
||||
context_labels[$context]="${context_labels[$context]},$label"
|
||||
fi
|
||||
done
|
||||
|
||||
for context in "${!context_labels[@]}"; do
|
||||
echo " echo \" $context: ${context_labels[$context]}\""
|
||||
done
|
||||
|
||||
cat << 'EOF'
|
||||
}
|
||||
|
||||
# Filter case statement
|
||||
handle_filter() {
|
||||
local filter="$1"
|
||||
case "$filter" in
|
||||
EOF
|
||||
|
||||
for label in "${!LABEL_COLORS[@]}"; do
|
||||
echo " $label) filter_by_label \"$label\" ;;"
|
||||
done
|
||||
|
||||
cat << 'EOF'
|
||||
pipeline) show_pipeline_overview ;;
|
||||
stats) show_statistics ;;
|
||||
all) show_all_issues ;;
|
||||
*)
|
||||
log_error "Unknown filter: $filter"
|
||||
show_filter_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
# === FILTER OPTIONS END ===
|
||||
EOF
|
||||
}
|
||||
|
||||
# Update a single script file
|
||||
update_script_file() {
|
||||
local script_file="$1"
|
||||
|
||||
if [[ ! -f "$script_file" ]]; then
|
||||
log_warning "Script not found: $script_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Updating $script_file..."
|
||||
|
||||
# Create backup
|
||||
cp "$script_file" "${script_file}.backup"
|
||||
|
||||
# Check if script has label definition sections
|
||||
if ! grep -q "# === LABEL DEFINITIONS START ===" "$script_file"; then
|
||||
log_warning "$script_file has no label definitions section - skipping"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Find section boundaries
|
||||
local start_line=$(grep -n "# === LABEL DEFINITIONS START ===" "$script_file" | cut -d: -f1)
|
||||
local end_line=$(grep -n "# === LABEL DEFINITIONS END ===" "$script_file" | cut -d: -f1)
|
||||
|
||||
if [[ -z "$start_line" ]] || [[ -z "$end_line" ]]; then
|
||||
log_error "Malformed label definition section in $script_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Generate new content
|
||||
local new_definitions=$(generate_label_definitions)
|
||||
|
||||
# Handle template mappings if present
|
||||
if grep -q "# === TEMPLATE LABEL MAPPINGS START ===" "$script_file"; then
|
||||
new_definitions+="\n$(generate_template_mappings)"
|
||||
fi
|
||||
|
||||
# Handle filter options if present (for get_issues.sh)
|
||||
if grep -q "# === FILTER OPTIONS START ===" "$script_file"; then
|
||||
new_definitions+="\n$(generate_filter_options)"
|
||||
fi
|
||||
|
||||
# Create temporary file with updated content
|
||||
local temp_file=$(mktemp)
|
||||
|
||||
# Copy everything before label definitions
|
||||
sed -n "1,$((start_line-1))p" "$script_file" > "$temp_file"
|
||||
|
||||
# Add new definitions
|
||||
echo -e "$new_definitions" >> "$temp_file"
|
||||
|
||||
# Copy everything after label definitions (find new end line)
|
||||
if grep -q "# === TEMPLATE LABEL MAPPINGS END ===" "$script_file"; then
|
||||
local actual_end_line=$(grep -n "# === TEMPLATE LABEL MAPPINGS END ===" "$script_file" | cut -d: -f1)
|
||||
elif grep -q "# === FILTER OPTIONS END ===" "$script_file"; then
|
||||
local actual_end_line=$(grep -n "# === FILTER OPTIONS END ===" "$script_file" | cut -d: -f1)
|
||||
else
|
||||
local actual_end_line="$end_line"
|
||||
fi
|
||||
|
||||
sed -n "$((actual_end_line+1)),\$p" "$script_file" >> "$temp_file"
|
||||
|
||||
# Replace original file
|
||||
mv "$temp_file" "$script_file"
|
||||
chmod +x "$script_file"
|
||||
|
||||
log_success "Updated $script_file"
|
||||
}
|
||||
|
||||
# Add new label to registry
|
||||
add_label_to_registry() {
|
||||
local name="$1"
|
||||
local color="${2:-ff6b6b}"
|
||||
local context="${3:-auto_generated}"
|
||||
local usage="${4:-manual_assignment}"
|
||||
|
||||
# Skip if called during auto-update to prevent loops
|
||||
if [[ "${FURT_AUTO_UPDATE:-}" == "true" ]]; then
|
||||
log_info "Auto-update mode: Adding $name to registry (skipping rebuild)"
|
||||
else
|
||||
log_info "Adding new label to registry: $name"
|
||||
fi
|
||||
|
||||
# Ensure registry exists
|
||||
create_registry_if_missing
|
||||
|
||||
# Check if label already exists
|
||||
if grep -q "^$name:" "$REGISTRY_FILE"; then
|
||||
log_warning "Label $name already exists in registry"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Add to appropriate section (determine by context)
|
||||
local section_marker="# === CORE WORKFLOW LABELS ==="
|
||||
if [[ "$context" == *"service"* ]] || [[ "$name" == service-* ]]; then
|
||||
section_marker="# === SERVICE-SPECIFIC LABELS ==="
|
||||
elif [[ "$context" == *"workflow"* ]]; then
|
||||
section_marker="# === WORKFLOW STATE LABELS ==="
|
||||
elif [[ "$context" == *"component"* ]]; then
|
||||
section_marker="# === COMPONENT CATEGORIES ==="
|
||||
fi
|
||||
|
||||
# Create backup
|
||||
cp "$REGISTRY_FILE" "${REGISTRY_FILE}.backup"
|
||||
|
||||
# Find section and add label
|
||||
local temp_file=$(mktemp)
|
||||
local added=false
|
||||
|
||||
while IFS= read -r line; do
|
||||
echo "$line" >> "$temp_file"
|
||||
|
||||
if [[ "$line" == "$section_marker" ]] && [[ "$added" == false ]]; then
|
||||
echo "$name:$color:$context:$usage" >> "$temp_file"
|
||||
added=true
|
||||
log_success "Added label $name to registry"
|
||||
fi
|
||||
done < "$REGISTRY_FILE"
|
||||
|
||||
mv "$temp_file" "$REGISTRY_FILE"
|
||||
}
|
||||
|
||||
# Show current registry status
|
||||
show_registry_status() {
|
||||
echo "📊 Label Registry Status"
|
||||
echo "========================"
|
||||
echo "Registry file: $REGISTRY_FILE"
|
||||
|
||||
if [[ -f "$REGISTRY_FILE" ]]; then
|
||||
echo "Total labels: $(grep -c "^[^#]" "$REGISTRY_FILE" 2>/dev/null || echo 0)"
|
||||
echo ""
|
||||
echo "Labels by category:"
|
||||
|
||||
local current_section=""
|
||||
while IFS= read -r line; do
|
||||
if [[ "$line" =~ ^#\ ===.*===\ $ ]]; then
|
||||
current_section=$(echo "$line" | sed 's/# === \(.*\) ===/\1/')
|
||||
echo " $current_section:"
|
||||
elif [[ "$line" =~ ^[^#]+: ]] && [[ -n "$current_section" ]]; then
|
||||
local label_name=$(echo "$line" | cut -d: -f1)
|
||||
echo " - $label_name"
|
||||
fi
|
||||
done < "$REGISTRY_FILE"
|
||||
else
|
||||
echo "Registry file not found!"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local command="${1:-update}"
|
||||
|
||||
case "$command" in
|
||||
update)
|
||||
log_info "Starting label synchronization..."
|
||||
parse_registry
|
||||
|
||||
# Update all script files
|
||||
local scripts=(
|
||||
"$PROJECT_ROOT/scripts/create_issue.sh"
|
||||
"$PROJECT_ROOT/scripts/get_issues.sh"
|
||||
"$PROJECT_ROOT/scripts/update_issue.sh"
|
||||
)
|
||||
|
||||
for script in "${scripts[@]}"; do
|
||||
update_script_file "$script"
|
||||
done
|
||||
|
||||
log_success "All scripts synchronized with label registry!"
|
||||
;;
|
||||
|
||||
add)
|
||||
local name="$2"
|
||||
local color="${3:-ff6b6b}"
|
||||
local context="${4:-auto_generated}"
|
||||
local usage="${5:-manual_assignment}"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
log_error "Usage: $0 add <name> [color] [context] [usage]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
add_label_to_registry "$name" "$color" "$context" "$usage"
|
||||
|
||||
# Only update scripts if not in auto-update mode
|
||||
if [[ "${FURT_AUTO_UPDATE:-}" != "true" ]]; then
|
||||
parse_registry
|
||||
main update
|
||||
fi
|
||||
;;
|
||||
|
||||
status)
|
||||
show_registry_status
|
||||
;;
|
||||
|
||||
help)
|
||||
echo "Usage: $0 [command] [options]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " update Update all scripts with current registry"
|
||||
echo " add <name> Add new label to registry and update scripts"
|
||||
echo " status Show registry status"
|
||||
echo " help Show this help"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 update"
|
||||
echo " $0 add newsletter ff6b6b newsletter_service service_templates"
|
||||
echo " $0 status"
|
||||
;;
|
||||
|
||||
*)
|
||||
log_error "Unknown command: $command"
|
||||
main help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run if executed directly
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
|
||||
61
scripts/cleanup_debug.sh
Normal file
61
scripts/cleanup_debug.sh
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#!/bin/bash
|
||||
# furt-lua/scripts/cleanup_debug.sh
|
||||
# Clean up debug code and prepare for production
|
||||
|
||||
echo "🧹 Cleaning up debug code for production..."
|
||||
|
||||
# Remove debug config script
|
||||
if [ -f "debug_config.lua" ]; then
|
||||
rm debug_config.lua
|
||||
echo "✅ Removed debug_config.lua"
|
||||
fi
|
||||
|
||||
# Check for any remaining DEBUG statements
|
||||
echo -e "\n🔍 Checking for remaining DEBUG statements:"
|
||||
debug_files=$(grep -r "DEBUG:" src/ 2>/dev/null || true)
|
||||
if [ -n "$debug_files" ]; then
|
||||
echo "⚠️ Found DEBUG statements:"
|
||||
echo "$debug_files"
|
||||
echo "Please remove these manually!"
|
||||
else
|
||||
echo "✅ No DEBUG statements found"
|
||||
fi
|
||||
|
||||
# Check for any console.log or print statements that might be debug
|
||||
echo -e "\n🔍 Checking for debug print statements:"
|
||||
print_files=$(grep -r "print(" src/ | grep -v "-- Allow print" | grep -v "print.*error" || true)
|
||||
if [ -n "$print_files" ]; then
|
||||
echo "⚠️ Found print statements (review if needed for production):"
|
||||
echo "$print_files"
|
||||
else
|
||||
echo "✅ No debug print statements found"
|
||||
fi
|
||||
|
||||
# Check test endpoint (should be disabled in production)
|
||||
echo -e "\n🔍 Checking for test endpoints:"
|
||||
test_endpoints=$(grep -r "/test" src/ || true)
|
||||
if [ -n "$test_endpoints" ]; then
|
||||
echo "⚠️ Found test endpoints (disable in production):"
|
||||
echo "$test_endpoints"
|
||||
else
|
||||
echo "✅ No test endpoints found"
|
||||
fi
|
||||
|
||||
# Verify API keys are not hardcoded
|
||||
echo -e "\n🔍 Checking for hardcoded API keys:"
|
||||
hardcoded_keys=$(grep -r "change-me-in-production" config/ src/ || true)
|
||||
if [ -n "$hardcoded_keys" ]; then
|
||||
echo "⚠️ Found development API keys (change for production):"
|
||||
echo "$hardcoded_keys"
|
||||
else
|
||||
echo "✅ No hardcoded development keys found"
|
||||
fi
|
||||
|
||||
echo -e "\n✅ Debug cleanup complete!"
|
||||
echo "📋 Production checklist:"
|
||||
echo " - [ ] Change API keys in .env"
|
||||
echo " - [ ] Disable /test endpoint"
|
||||
echo " - [ ] Set CORS_ALLOWED_ORIGINS for production"
|
||||
echo " - [ ] Configure production SMTP settings"
|
||||
echo " - [ ] Review log levels"
|
||||
|
||||
|
|
@ -1,966 +0,0 @@
|
|||
#!/bin/bash
|
||||
# scripts/deploy/deploy_aitvaras.sh
|
||||
# Deployment script: karl (development) → aitvaras (Ubuntu 24.04 production)
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/deploy/deploy_aitvaras.sh [--dry-run] [--rollback] [--force]
|
||||
#
|
||||
# Dragons@Work - Furt API-Gateway Production Deployment
|
||||
# Version: 1.0
|
||||
|
||||
set -euo pipefail # Exit on error, undefined vars, pipe failures
|
||||
|
||||
# =============================================================================
|
||||
# CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Source (karl development)
|
||||
SOURCE_DIR="/home/michael/Develop/DAW/furt/furt-lua"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")" # scripts/deploy/ -> scripts/ -> furt/
|
||||
|
||||
# Target (aitvaras Ubuntu production)
|
||||
AITVARAS_HOST="aitvaras" # Assumes SSH config entry (as michael user)
|
||||
TARGET_DIR="/opt/furt-api"
|
||||
SERVICE_USER="_furt"
|
||||
SERVICE_GROUP="_furt"
|
||||
SERVICE_NAME="furt-api"
|
||||
|
||||
# Ubuntu-specific paths
|
||||
CONFIG_DIR="/etc/furt" # ← Angepasst: wie start.sh erwartet
|
||||
LOG_DIR="/var/log/furt-api"
|
||||
RUN_DIR="/var/run/furt-api"
|
||||
BACKUP_DIR="/var/backup/furt-api"
|
||||
SYSTEMD_SERVICE="/etc/systemd/system/furt-api.service"
|
||||
|
||||
# Backup configuration
|
||||
BACKUP_RETENTION=5 # Keep last 5 backups (production = more backups)
|
||||
|
||||
# Health check configuration
|
||||
HEALTH_URL="http://localhost:8080/health"
|
||||
HEALTH_TIMEOUT=15 # Longer timeout for production
|
||||
HEALTH_RETRIES=5 # More retries for production
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
PURPLE='\033[0;35m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# =============================================================================
|
||||
# LOGGING FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "\n${PURPLE}==>${NC} $1"
|
||||
}
|
||||
|
||||
log_production() {
|
||||
echo -e "${PURPLE}[PRODUCTION]${NC} $1"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# UTILITY FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
🚨 PRODUCTION deployment script for furt-api: karl → aitvaras (Ubuntu 24.04)
|
||||
|
||||
⚠️ WARNING: This deploys to PRODUCTION server aitvaras!
|
||||
⚠️ All changes are immediately live and affect production services.
|
||||
|
||||
🔐 SECURITY NOTE:
|
||||
This script temporarily enables passwordless sudo for deployment,
|
||||
then automatically disables it for security.
|
||||
|
||||
OPTIONS:
|
||||
--dry-run Show what would be deployed without making changes
|
||||
--rollback Rollback to previous deployment
|
||||
--force Skip confirmation prompts (USE WITH CAUTION!)
|
||||
--help Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
$0 # Normal production deployment with confirmation
|
||||
$0 --dry-run # Preview deployment without changes (RECOMMENDED)
|
||||
$0 --force # Deploy without confirmation (DANGEROUS!)
|
||||
$0 --rollback # Rollback to previous version
|
||||
|
||||
PRODUCTION SAFETY:
|
||||
- Always run --dry-run first
|
||||
- Creates automatic backups before deployment
|
||||
- Health checks ensure successful deployment
|
||||
- Rollback available if deployment fails
|
||||
- Passwordless sudo automatically disabled after deployment
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
check_dependencies() {
|
||||
log_step "Checking dependencies for production deployment"
|
||||
|
||||
# Check if source directory exists
|
||||
if [[ ! -d "$SOURCE_DIR" ]]; then
|
||||
log_error "Source directory not found: $SOURCE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check SSH connectivity to aitvaras
|
||||
if ! ssh -o ConnectTimeout=10 -o BatchMode=yes "$AITVARAS_HOST" exit 2>/dev/null; then
|
||||
log_error "Cannot connect to aitvaras via SSH"
|
||||
log_info "Please ensure SSH key is set up for $AITVARAS_HOST"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Enable passwordless sudo for deployment
|
||||
if ! enable_passwordless_sudo; then
|
||||
log_error "Cannot enable passwordless sudo on aitvaras"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check rsync availability
|
||||
if ! command -v rsync &> /dev/null; then
|
||||
log_error "rsync is required but not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "All dependencies OK"
|
||||
}
|
||||
|
||||
get_backup_timestamp() {
|
||||
date +"%Y%m%d_%H%M%S"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SSH REMOTE EXECUTION FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
aitvaras_exec() {
|
||||
ssh "$AITVARAS_HOST" "$@"
|
||||
}
|
||||
|
||||
aitvaras_exec_sudo() {
|
||||
ssh "$AITVARAS_HOST" "sudo $@"
|
||||
}
|
||||
|
||||
aitvaras_exec_as_furt() {
|
||||
ssh "$AITVARAS_HOST" "sudo -u $SERVICE_USER $@"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SUDO AUTHENTICATION FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
enable_passwordless_sudo() {
|
||||
log_step "Enabling temporary passwordless sudo"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would enable passwordless sudo temporarily"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Testing current sudo access..."
|
||||
|
||||
# Test if sudo works without password (already configured)
|
||||
if ssh "$AITVARAS_HOST" "sudo -n true" 2>/dev/null; then
|
||||
log_success "Passwordless sudo already enabled"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Need to enable passwordless sudo temporarily
|
||||
log_info "Setting up temporary passwordless sudo for deployment..."
|
||||
log_warning "⚠️ This will require your password ONCE to enable passwordless mode"
|
||||
|
||||
# Use ssh -t for interactive terminal to set up passwordless mode
|
||||
if ssh -t "$AITVARAS_HOST" "echo 'michael ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/99-deployment-passwordless"; then
|
||||
|
||||
# Verify it works
|
||||
if ssh "$AITVARAS_HOST" "sudo -n true" 2>/dev/null; then
|
||||
log_success "Temporary passwordless sudo enabled"
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to verify passwordless sudo"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_error "Failed to enable passwordless sudo"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
disable_passwordless_sudo() {
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
log_info "Removing temporary passwordless sudo configuration..."
|
||||
ssh "$AITVARAS_HOST" "sudo rm -f /etc/sudoers.d/99-deployment-passwordless" 2>/dev/null || true
|
||||
log_success "Passwordless sudo disabled - security restored"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# USER MANAGEMENT FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
create_service_user() {
|
||||
log_step "Creating service user"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would create user $SERVICE_USER with group $SERVICE_GROUP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if user already exists
|
||||
if aitvaras_exec "id $SERVICE_USER &>/dev/null"; then
|
||||
log_info "User $SERVICE_USER already exists"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Creating system user: $SERVICE_USER"
|
||||
|
||||
# Create system user (Ubuntu style)
|
||||
aitvaras_exec_sudo "useradd --system --shell /usr/sbin/nologin --home-dir $TARGET_DIR --create-home --user-group $SERVICE_USER"
|
||||
|
||||
# Verify user creation
|
||||
if aitvaras_exec "id $SERVICE_USER &>/dev/null"; then
|
||||
log_success "User $SERVICE_USER created successfully"
|
||||
else
|
||||
log_error "Failed to create user $SERVICE_USER"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# DIRECTORY MANAGEMENT FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
create_directories() {
|
||||
log_step "Creating directory structure"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would create directories:"
|
||||
local directories=("$TARGET_DIR" "$CONFIG_DIR" "$LOG_DIR" "$RUN_DIR" "$BACKUP_DIR")
|
||||
for dir in "${directories[@]}"; do
|
||||
echo " - $dir"
|
||||
done
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Creating directory structure..."
|
||||
|
||||
# Create all directories in one sudo call
|
||||
aitvaras_exec_sudo "mkdir -p $TARGET_DIR $CONFIG_DIR $LOG_DIR $RUN_DIR $BACKUP_DIR"
|
||||
|
||||
# Set ownership and permissions in batch
|
||||
aitvaras_exec_sudo "chown $SERVICE_USER:$SERVICE_GROUP $TARGET_DIR $CONFIG_DIR $LOG_DIR $RUN_DIR $BACKUP_DIR"
|
||||
aitvaras_exec_sudo "chmod 755 $TARGET_DIR $CONFIG_DIR $RUN_DIR $BACKUP_DIR"
|
||||
aitvaras_exec_sudo "chmod 750 $LOG_DIR"
|
||||
|
||||
log_success "Directory structure created"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# BACKUP FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
create_backup() {
|
||||
local timestamp=$(get_backup_timestamp)
|
||||
local backup_path="$BACKUP_DIR/furt-api_$timestamp"
|
||||
|
||||
log_step "Creating production backup"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would create backup at $backup_path"
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
|
||||
# Create backup directory if it doesn't exist
|
||||
aitvaras_exec_sudo "mkdir -p $BACKUP_DIR"
|
||||
aitvaras_exec_sudo "chown $SERVICE_USER:$SERVICE_GROUP $BACKUP_DIR"
|
||||
|
||||
# Check if target directory exists and has content
|
||||
if aitvaras_exec "test -d $TARGET_DIR && test -n \"\$(ls -A $TARGET_DIR 2>/dev/null)\""; then
|
||||
log_production "Backing up current deployment to: $backup_path"
|
||||
aitvaras_exec_sudo "cp -r $TARGET_DIR $backup_path"
|
||||
aitvaras_exec_sudo "chown -R $SERVICE_USER:$SERVICE_GROUP $backup_path"
|
||||
|
||||
# Set backup metadata
|
||||
aitvaras_exec_sudo "sh -c \"echo 'Backup created: \$(date)' > $backup_path/.backup_info\""
|
||||
aitvaras_exec_sudo "sh -c \"echo 'Original path: $TARGET_DIR' >> $backup_path/.backup_info\""
|
||||
aitvaras_exec_sudo "sh -c \"echo 'Service: $SERVICE_NAME' >> $backup_path/.backup_info\""
|
||||
aitvaras_exec_sudo "sh -c \"echo 'Host: \$(hostname)' >> $backup_path/.backup_info\""
|
||||
aitvaras_exec_sudo "chown $SERVICE_USER:$SERVICE_GROUP $backup_path/.backup_info"
|
||||
|
||||
log_success "Production backup created: $backup_path"
|
||||
echo "$backup_path" # Return backup path
|
||||
else
|
||||
log_warning "No existing deployment found or directory empty, skipping backup"
|
||||
echo "" # Return empty string
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_old_backups() {
|
||||
log_step "Cleaning up old backups"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would cleanup old backups (keep last $BACKUP_RETENTION)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local backup_count=$(aitvaras_exec "ls -1 $BACKUP_DIR/furt-api_* 2>/dev/null | wc -l" || echo "0")
|
||||
|
||||
if [[ $backup_count -gt $BACKUP_RETENTION ]]; then
|
||||
log_info "Found $backup_count backups, keeping last $BACKUP_RETENTION"
|
||||
aitvaras_exec_sudo "ls -1t $BACKUP_DIR/furt-api_* | tail -n +$((BACKUP_RETENTION + 1)) | xargs rm -rf"
|
||||
log_success "Old backups cleaned up"
|
||||
else
|
||||
log_info "Found $backup_count backups, no cleanup needed"
|
||||
fi
|
||||
}
|
||||
|
||||
list_backups() {
|
||||
log_step "Available production backups"
|
||||
|
||||
if aitvaras_exec "ls -1 $BACKUP_DIR/furt-api_* 2>/dev/null"; then
|
||||
aitvaras_exec "ls -1t $BACKUP_DIR/furt-api_* | head -n 5 | while read backup; do
|
||||
echo \" \$backup\"
|
||||
if [ -f \"\$backup/.backup_info\" ]; then
|
||||
cat \"\$backup/.backup_info\" | sed 's/^/ /'
|
||||
fi
|
||||
echo
|
||||
done"
|
||||
else
|
||||
log_warning "No backups found"
|
||||
fi
|
||||
}
|
||||
|
||||
rollback_deployment() {
|
||||
log_step "Rolling back production deployment"
|
||||
|
||||
# List available backups
|
||||
local latest_backup=$(aitvaras_exec "ls -1t $BACKUP_DIR/furt-api_* 2>/dev/null | head -n 1" || echo "")
|
||||
|
||||
if [[ -z "$latest_backup" ]]; then
|
||||
log_error "No backups available for rollback"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_production "Latest backup: $latest_backup"
|
||||
|
||||
if [[ "$FORCE" != "true" ]]; then
|
||||
echo -n "⚠️ PRODUCTION ROLLBACK - Continue? [y/N]: "
|
||||
read -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
log_info "Rollback cancelled"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Stop service
|
||||
stop_service
|
||||
|
||||
# Backup current version (before rollback)
|
||||
local rollback_backup=$(create_backup)
|
||||
if [[ -n "$rollback_backup" ]]; then
|
||||
aitvaras_exec_sudo "mv $rollback_backup ${rollback_backup}_pre_rollback"
|
||||
fi
|
||||
|
||||
# Restore from backup
|
||||
aitvaras_exec_sudo "rm -rf $TARGET_DIR"
|
||||
aitvaras_exec_sudo "cp -r $latest_backup $TARGET_DIR"
|
||||
|
||||
# Fix permissions
|
||||
fix_permissions
|
||||
|
||||
# Start service
|
||||
start_service
|
||||
|
||||
# Health check
|
||||
if health_check; then
|
||||
log_success "Production rollback completed successfully"
|
||||
else
|
||||
log_error "Rollback completed but health check failed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SERVICE MANAGEMENT FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
create_systemd_service() {
|
||||
log_step "Creating systemd service"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would create $SYSTEMD_SERVICE"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Creating systemd service: $SERVICE_NAME"
|
||||
|
||||
# Create systemd service file using sudo tee
|
||||
aitvaras_exec_sudo "tee $SYSTEMD_SERVICE > /dev/null << 'EOF'
|
||||
[Unit]
|
||||
Description=Furt API Gateway
|
||||
Documentation=https://gitea.dragons-at-work.de/DAW/furt
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=$SERVICE_USER
|
||||
Group=$SERVICE_GROUP
|
||||
WorkingDirectory=$TARGET_DIR
|
||||
ExecStart=$TARGET_DIR/scripts/start.sh
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
# Security settings
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectHome=true
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=$TARGET_DIR $LOG_DIR $RUN_DIR
|
||||
|
||||
# Environment
|
||||
Environment=LUA_COMMAND=/usr/bin/lua5.1
|
||||
Environment=LUA_VERSION=5.1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF"
|
||||
|
||||
# Reload systemd
|
||||
aitvaras_exec_sudo "systemctl daemon-reload"
|
||||
|
||||
# Enable service
|
||||
aitvaras_exec_sudo "systemctl enable $SERVICE_NAME"
|
||||
|
||||
log_success "Systemd service created and enabled"
|
||||
}
|
||||
|
||||
get_service_status() {
|
||||
if aitvaras_exec "systemctl is-active $SERVICE_NAME >/dev/null 2>&1"; then
|
||||
echo "running"
|
||||
else
|
||||
echo "stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
log_step "Stopping $SERVICE_NAME service"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would stop $SERVICE_NAME service"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local status=$(get_service_status)
|
||||
|
||||
if [[ "$status" == "running" ]]; then
|
||||
log_production "Stopping production service: $SERVICE_NAME"
|
||||
aitvaras_exec_sudo "systemctl stop $SERVICE_NAME"
|
||||
|
||||
# Wait for service to stop
|
||||
local attempts=0
|
||||
while [[ $attempts -lt 10 ]]; do
|
||||
if [[ $(get_service_status) == "stopped" ]]; then
|
||||
log_success "Service stopped successfully"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
((attempts++))
|
||||
done
|
||||
|
||||
log_error "Service did not stop within 10 seconds"
|
||||
return 1
|
||||
else
|
||||
log_info "Service already stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
log_step "Starting $SERVICE_NAME service"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would start $SERVICE_NAME service"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local status=$(get_service_status)
|
||||
if [[ "$status" == "stopped" ]]; then
|
||||
# Check port availability before starting
|
||||
if ! check_port_availability; then
|
||||
log_error "Cannot start service - port 8080 is occupied"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_production "Starting production service: $SERVICE_NAME"
|
||||
aitvaras_exec_sudo "systemctl start $SERVICE_NAME"
|
||||
|
||||
# Wait and check if service actually started
|
||||
sleep 3
|
||||
|
||||
local new_status=$(get_service_status)
|
||||
|
||||
if [[ "$new_status" == "running" ]]; then
|
||||
log_success "Service started successfully"
|
||||
else
|
||||
log_error "Failed to start service"
|
||||
# Show service logs for debugging
|
||||
aitvaras_exec_sudo "journalctl -u $SERVICE_NAME --no-pager -n 20" || true
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_info "Service already running"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# DEPLOYMENT FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
prepare_source() {
|
||||
log_step "Preparing source files"
|
||||
|
||||
# Check source directory structure
|
||||
local required_dirs=("src" "config" "scripts")
|
||||
for dir in "${required_dirs[@]}"; do
|
||||
if [[ ! -d "$SOURCE_DIR/$dir" ]]; then
|
||||
log_error "Required directory missing: $SOURCE_DIR/$dir"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check required files
|
||||
local required_files=("src/main.lua" "scripts/start.sh")
|
||||
for file in "${required_files[@]}"; do
|
||||
if [[ ! -f "$SOURCE_DIR/$file" ]]; then
|
||||
log_error "Required file missing: $SOURCE_DIR/$file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "Source files validated"
|
||||
}
|
||||
|
||||
sync_files() {
|
||||
log_step "Syncing files to production"
|
||||
|
||||
# Prepare rsync excludes
|
||||
local excludes=(
|
||||
"--exclude=.env"
|
||||
"--exclude=.env.*"
|
||||
"--exclude=*.backup"
|
||||
"--exclude=*.orig"
|
||||
"--exclude=*.tmp"
|
||||
"--exclude=.git/"
|
||||
"--exclude=.DS_Store"
|
||||
"--exclude=logs/"
|
||||
"--exclude=*.log"
|
||||
"--exclude=tests/"
|
||||
)
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would sync the following files:"
|
||||
rsync -avz --dry-run "${excludes[@]}" "$SOURCE_DIR/" "$AITVARAS_HOST:$TARGET_DIR/"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Clean up target directory and recreate with correct permissions
|
||||
log_info "Preparing target directory for sync..."
|
||||
aitvaras_exec_sudo "rm -rf $TARGET_DIR"
|
||||
aitvaras_exec_sudo "mkdir -p $TARGET_DIR"
|
||||
aitvaras_exec_sudo "chown michael:michael $TARGET_DIR"
|
||||
|
||||
# Sync files as michael user (now has permissions)
|
||||
log_production "Syncing files to production..."
|
||||
rsync -avz "${excludes[@]}" \
|
||||
-e "ssh" \
|
||||
"$SOURCE_DIR/" "$AITVARAS_HOST:$TARGET_DIR/"
|
||||
|
||||
# Fix ownership to _furt after successful sync
|
||||
aitvaras_exec_sudo "chown -R $SERVICE_USER:$SERVICE_GROUP $TARGET_DIR"
|
||||
|
||||
log_success "Files synced to production"
|
||||
}
|
||||
|
||||
create_environment_file() {
|
||||
log_step "Creating environment configuration"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
if aitvaras_exec "test -f $CONFIG_DIR/environment"; then
|
||||
log_info "DRY RUN: Environment file exists - would NOT overwrite"
|
||||
else
|
||||
log_info "DRY RUN: Would create $CONFIG_DIR/environment"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if environment file already exists
|
||||
if aitvaras_exec "test -f $CONFIG_DIR/environment"; then
|
||||
log_success "Environment file already exists - preserving existing configuration"
|
||||
log_info "Existing config preserved at: $CONFIG_DIR/environment"
|
||||
|
||||
# Still fix permissions in case they got messed up
|
||||
aitvaras_exec_sudo "chown $SERVICE_USER:$SERVICE_GROUP $CONFIG_DIR/environment"
|
||||
aitvaras_exec_sudo "chmod 640 $CONFIG_DIR/environment"
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Creating new environment file..."
|
||||
|
||||
# Create environment file for production using sudo tee
|
||||
aitvaras_exec_sudo "tee $CONFIG_DIR/environment > /dev/null << 'EOF'
|
||||
# Furt API Production Environment Configuration
|
||||
# Created by deploy_aitvaras.sh on $(date)
|
||||
|
||||
# Lua Configuration
|
||||
LUA_COMMAND=/usr/bin/lua5.1
|
||||
LUA_VERSION=5.1
|
||||
|
||||
# Server Configuration
|
||||
SERVER_HOST=127.0.0.1
|
||||
SERVER_PORT=8080
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=info
|
||||
LOG_FILE=$LOG_DIR/furt-api.log
|
||||
|
||||
# SMTP Configuration (to be filled manually)
|
||||
# SMTP_USERNAME=your_smtp_username
|
||||
# SMTP_PASSWORD=your_smtp_password
|
||||
# SMTP_HOST=mail.dragons-at-work.de
|
||||
# SMTP_PORT=465
|
||||
# SMTP_FROM=noreply@dragons-at-work.de
|
||||
# SMTP_TO=michael@dragons-at-work.de
|
||||
|
||||
# Security
|
||||
# Add your production SMTP credentials here manually after deployment
|
||||
EOF"
|
||||
|
||||
# Set proper permissions immediately
|
||||
aitvaras_exec_sudo "chown $SERVICE_USER:$SERVICE_GROUP $CONFIG_DIR/environment"
|
||||
aitvaras_exec_sudo "chmod 640 $CONFIG_DIR/environment"
|
||||
|
||||
log_success "Environment file created with correct permissions"
|
||||
log_warning "⚠️ MANUAL STEP REQUIRED: Add SMTP credentials to $CONFIG_DIR/environment"
|
||||
}
|
||||
|
||||
fix_permissions() {
|
||||
log_step "Fixing permissions"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would set ownership to $SERVICE_USER:$SERVICE_GROUP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Set ownership in batch
|
||||
aitvaras_exec_sudo "chown -R $SERVICE_USER:$SERVICE_GROUP $TARGET_DIR $CONFIG_DIR $LOG_DIR $RUN_DIR"
|
||||
|
||||
# Set permissions for scripts
|
||||
aitvaras_exec_sudo "chmod +x $TARGET_DIR/scripts/*.sh"
|
||||
|
||||
# Set permissions for config directory
|
||||
aitvaras_exec_sudo "chmod 755 $CONFIG_DIR"
|
||||
|
||||
# Set permissions for environment file only if it exists
|
||||
if aitvaras_exec "test -f $CONFIG_DIR/environment"; then
|
||||
aitvaras_exec_sudo "chmod 640 $CONFIG_DIR/environment"
|
||||
fi
|
||||
|
||||
log_success "Permissions fixed"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# HEALTH CHECK FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
check_port_availability() {
|
||||
log_step "Checking port availability"
|
||||
|
||||
local port="8080" # furt-api port
|
||||
|
||||
if aitvaras_exec "netstat -an | grep -q ':$port'"; then
|
||||
log_warning "Port $port is already in use"
|
||||
aitvaras_exec "netstat -an | grep ':$port'" || true
|
||||
|
||||
# Try to identify what's using the port
|
||||
log_info "Checking what's using port $port..."
|
||||
aitvaras_exec "sudo ss -tlnp | grep ':$port'" || true
|
||||
|
||||
return 1
|
||||
else
|
||||
log_success "Port $port is available"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
health_check() {
|
||||
log_step "Running production health check"
|
||||
|
||||
local retries=$HEALTH_RETRIES
|
||||
while [[ $retries -gt 0 ]]; do
|
||||
log_info "Health check attempt $((HEALTH_RETRIES - retries + 1))/$HEALTH_RETRIES"
|
||||
|
||||
if aitvaras_exec "curl -s --max-time $HEALTH_TIMEOUT $HEALTH_URL >/dev/null 2>&1"; then
|
||||
log_success "Production health check passed"
|
||||
|
||||
# Get health check response
|
||||
local health_response=$(aitvaras_exec "curl -s --max-time $HEALTH_TIMEOUT $HEALTH_URL" || echo "")
|
||||
if [[ -n "$health_response" ]]; then
|
||||
log_info "Health response: $health_response"
|
||||
fi
|
||||
|
||||
# Additional status info
|
||||
local service_status=$(get_service_status)
|
||||
log_info "Service status: $service_status"
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
((retries--))
|
||||
if [[ $retries -gt 0 ]]; then
|
||||
log_info "Health check failed, retrying in 5 seconds..."
|
||||
sleep 5
|
||||
fi
|
||||
done
|
||||
|
||||
log_error "Production health check failed after $HEALTH_RETRIES attempts"
|
||||
log_info "Debugging service status..."
|
||||
local service_status=$(get_service_status)
|
||||
log_info "Service status: $service_status"
|
||||
|
||||
# Show service logs
|
||||
log_info "Recent service logs:"
|
||||
aitvaras_exec_sudo "journalctl -u $SERVICE_NAME --no-pager -n 10" || true
|
||||
|
||||
# Check if port is at least listening
|
||||
if aitvaras_exec "netstat -an | grep -q ':8080.*LISTEN'"; then
|
||||
log_warning "Port 8080 is listening but health check failed - possible service issue"
|
||||
else
|
||||
log_error "Port 8080 is not listening - service not running"
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN DEPLOYMENT FUNCTION
|
||||
# =============================================================================
|
||||
|
||||
deploy() {
|
||||
log_step "Starting PRODUCTION deployment: karl → aitvaras"
|
||||
log_production "⚠️ THIS IS A PRODUCTION DEPLOYMENT ⚠️"
|
||||
|
||||
# Pre-deployment checks
|
||||
check_dependencies
|
||||
prepare_source
|
||||
|
||||
# Show deployment summary
|
||||
log_info "Production Deployment Summary:"
|
||||
log_info " Source: $SOURCE_DIR"
|
||||
log_info " Target: $AITVARAS_HOST:$TARGET_DIR"
|
||||
log_info " Service: $SERVICE_NAME (systemd)"
|
||||
log_info " User: $SERVICE_USER"
|
||||
log_info " Dry Run: $DRY_RUN"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_warning "DRY RUN MODE - No changes will be made"
|
||||
else
|
||||
log_production "🚨 PRODUCTION MODE - Changes will be applied immediately!"
|
||||
fi
|
||||
|
||||
# Production confirmation prompt
|
||||
if [[ "$FORCE" != "true" && "$DRY_RUN" != "true" ]]; then
|
||||
echo ""
|
||||
echo -e "${RED}⚠️ PRODUCTION DEPLOYMENT WARNING ⚠️${NC}"
|
||||
echo -e "This will deploy to the live production server aitvaras."
|
||||
echo -e "All changes will immediately affect live services."
|
||||
echo ""
|
||||
echo -n "Continue with PRODUCTION deployment? [y/N]: "
|
||||
read -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
log_info "Production deployment cancelled"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create service user and directories
|
||||
create_service_user
|
||||
create_directories
|
||||
|
||||
# Create backup before deployment
|
||||
local backup_path=""
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
backup_path=$(create_backup)
|
||||
fi
|
||||
|
||||
# Stop service if running
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
stop_service
|
||||
fi
|
||||
|
||||
# Deploy files
|
||||
sync_files
|
||||
|
||||
# Create configuration
|
||||
create_environment_file
|
||||
|
||||
# Fix permissions (after environment file exists)
|
||||
fix_permissions
|
||||
|
||||
# Create systemd service
|
||||
create_systemd_service
|
||||
|
||||
# Start service
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
# Check if port is available before starting
|
||||
if ! check_port_availability; then
|
||||
log_error "Cannot start service - port conflict detected"
|
||||
log_info "Check what's using port 8080: sudo ss -tlnp | grep :8080"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if start_service; then
|
||||
# Health check
|
||||
if health_check; then
|
||||
log_success "🎉 PRODUCTION DEPLOYMENT COMPLETED SUCCESSFULLY!"
|
||||
log_production "Service is live at: https://api.dragons-at-work.de"
|
||||
|
||||
# Cleanup old backups
|
||||
cleanup_old_backups
|
||||
|
||||
# Disable passwordless sudo (security)
|
||||
disable_passwordless_sudo
|
||||
|
||||
# Show next steps
|
||||
echo ""
|
||||
log_info "📝 MANUAL STEPS REQUIRED:"
|
||||
log_info "1. Add SMTP credentials to: $CONFIG_DIR/environment"
|
||||
log_info "2. Test the API: curl https://api.dragons-at-work.de/health"
|
||||
log_info "3. Monitor logs: sudo journalctl -u $SERVICE_NAME -f"
|
||||
|
||||
else
|
||||
log_error "Production deployment failed health check"
|
||||
|
||||
# Disable passwordless sudo even on failure
|
||||
disable_passwordless_sudo
|
||||
|
||||
# Offer rollback
|
||||
if [[ -n "$backup_path" ]]; then
|
||||
echo -n "🔄 Rollback to previous version? [y/N]: "
|
||||
read -r response
|
||||
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||||
log_production "Rolling back production deployment..."
|
||||
# Need to re-enable passwordless sudo for rollback
|
||||
enable_passwordless_sudo
|
||||
aitvaras_exec_sudo "rm -rf $TARGET_DIR"
|
||||
aitvaras_exec_sudo "cp -r $backup_path $TARGET_DIR"
|
||||
fix_permissions
|
||||
start_service
|
||||
health_check
|
||||
disable_passwordless_sudo
|
||||
fi
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_error "Failed to start service after production deployment"
|
||||
disable_passwordless_sudo
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_success "Dry run completed - no changes made to production"
|
||||
echo ""
|
||||
log_info "To execute this deployment for real:"
|
||||
log_info " $0"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN SCRIPT LOGIC
|
||||
# =============================================================================
|
||||
|
||||
# Parse command line arguments
|
||||
DRY_RUN=false
|
||||
ROLLBACK=false
|
||||
FORCE=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
--rollback)
|
||||
ROLLBACK=true
|
||||
shift
|
||||
;;
|
||||
--force)
|
||||
FORCE=true
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
log_production "Furt API Production Deployment Script - karl → aitvaras"
|
||||
log_info "$(date)"
|
||||
|
||||
if [[ "$ROLLBACK" == "true" ]]; then
|
||||
rollback_deployment
|
||||
else
|
||||
deploy
|
||||
fi
|
||||
}
|
||||
|
||||
# Trap for cleanup on exit
|
||||
cleanup() {
|
||||
local exit_code=$?
|
||||
|
||||
# Always disable passwordless sudo (security!)
|
||||
disable_passwordless_sudo
|
||||
|
||||
if [[ $exit_code -ne 0 && "$DRY_RUN" != "true" ]]; then
|
||||
log_error "Production deployment failed (exit code: $exit_code)"
|
||||
log_info "Check service status: ssh $AITVARAS_HOST \"sudo systemctl status $SERVICE_NAME\""
|
||||
log_info "Check logs: ssh $AITVARAS_HOST \"sudo journalctl -u $SERVICE_NAME -n 20\""
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
|
|
@ -1,695 +0,0 @@
|
|||
#!/bin/bash
|
||||
# scripts/deploy/deploy_walter.sh
|
||||
# Deployment script: karl (development) → walter (OpenBSD staging)
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/deploy/deploy_walter.sh [--dry-run] [--rollback] [--force]
|
||||
#
|
||||
# Dragons@Work - Furt API-Gateway Deployment
|
||||
# Version: 1.0
|
||||
|
||||
set -euo pipefail # Exit on error, undefined vars, pipe failures
|
||||
|
||||
# =============================================================================
|
||||
# CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Source (karl development)
|
||||
SOURCE_DIR="/home/michael/Develop/DAW/furt/furt-lua"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")" # scripts/deploy/ -> scripts/ -> furt/
|
||||
|
||||
# Target (walter OpenBSD staging)
|
||||
WALTER_HOST="walter" # Assumes SSH config entry (as michael user)
|
||||
TARGET_DIR="/usr/local/furt/furt-lua"
|
||||
SERVICE_USER="_furt"
|
||||
SERVICE_GROUP="_furt"
|
||||
|
||||
# Backup configuration
|
||||
BACKUP_DIR="/usr/local/furt/backups"
|
||||
BACKUP_RETENTION=3 # Keep last 3 backups
|
||||
|
||||
# Health check configuration
|
||||
HEALTH_URL="http://localhost:8080/health"
|
||||
HEALTH_TIMEOUT=10
|
||||
HEALTH_RETRIES=3
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# =============================================================================
|
||||
# LOGGING FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "\n${BLUE}==>${NC} $1"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# UTILITY FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
Deployment script for furt-lua: karl (development) → walter (OpenBSD staging)
|
||||
|
||||
OPTIONS:
|
||||
--dry-run Show what would be deployed without making changes
|
||||
--rollback Rollback to previous deployment
|
||||
--force Skip confirmation prompts
|
||||
--help Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
$0 # Normal deployment with confirmation
|
||||
$0 --dry-run # Preview deployment without changes
|
||||
$0 --force # Deploy without confirmation
|
||||
$0 --rollback # Rollback to previous version
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
check_dependencies() {
|
||||
log_step "Checking dependencies"
|
||||
|
||||
# Check if source directory exists
|
||||
if [[ ! -d "$SOURCE_DIR" ]]; then
|
||||
log_error "Source directory not found: $SOURCE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check SSH connectivity to walter
|
||||
if ! ssh -o ConnectTimeout=5 -o BatchMode=yes "$WALTER_HOST" exit 2>/dev/null; then
|
||||
log_error "Cannot connect to walter via SSH"
|
||||
log_info "Please ensure SSH key is set up for $WALTER_HOST"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check rsync availability
|
||||
if ! command -v rsync &> /dev/null; then
|
||||
log_error "rsync is required but not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "All dependencies OK"
|
||||
}
|
||||
|
||||
get_backup_timestamp() {
|
||||
date +"%Y%m%d_%H%M%S"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SSH REMOTE EXECUTION FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
walter_exec() {
|
||||
ssh "$WALTER_HOST" "$@"
|
||||
}
|
||||
|
||||
walter_exec_as_root() {
|
||||
ssh "$WALTER_HOST" "doas $@"
|
||||
}
|
||||
|
||||
walter_exec_as_furt() {
|
||||
ssh "$WALTER_HOST" "doas -u $SERVICE_USER $@"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# BACKUP FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
create_backup() {
|
||||
local timestamp=$(get_backup_timestamp)
|
||||
local backup_path="$BACKUP_DIR/furt-lua_$timestamp"
|
||||
|
||||
log_step "Creating backup"
|
||||
|
||||
# Create backup directory if it doesn't exist
|
||||
walter_exec_as_root "mkdir -p $BACKUP_DIR"
|
||||
walter_exec_as_root "chown $SERVICE_USER:$SERVICE_GROUP $BACKUP_DIR"
|
||||
|
||||
# Check if target directory exists
|
||||
if walter_exec "test -d $TARGET_DIR"; then
|
||||
log_info "Backing up current deployment to: $backup_path"
|
||||
walter_exec_as_root "cp -r $TARGET_DIR $backup_path"
|
||||
walter_exec_as_root "chown -R $SERVICE_USER:$SERVICE_GROUP $backup_path"
|
||||
|
||||
# Set backup metadata (fix shell redirect issue)
|
||||
walter_exec_as_root "sh -c \"echo 'Backup created: \$(date)' > $backup_path/.backup_info\""
|
||||
walter_exec_as_root "sh -c \"echo 'Original path: $TARGET_DIR' >> $backup_path/.backup_info\""
|
||||
walter_exec_as_root "chown $SERVICE_USER:$SERVICE_GROUP $backup_path/.backup_info"
|
||||
|
||||
log_success "Backup created: $backup_path"
|
||||
echo "$backup_path" # Return backup path
|
||||
else
|
||||
log_warning "No existing deployment found, skipping backup"
|
||||
echo "" # Return empty string
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_old_backups() {
|
||||
log_step "Cleaning up old backups"
|
||||
|
||||
local backup_count=$(walter_exec "ls -1 $BACKUP_DIR/furt-lua_* 2>/dev/null | wc -l" || echo "0")
|
||||
|
||||
if [[ $backup_count -gt $BACKUP_RETENTION ]]; then
|
||||
log_info "Found $backup_count backups, keeping last $BACKUP_RETENTION"
|
||||
walter_exec_as_root "ls -1t $BACKUP_DIR/furt-lua_* | tail -n +$((BACKUP_RETENTION + 1)) | xargs rm -rf"
|
||||
log_success "Old backups cleaned up"
|
||||
else
|
||||
log_info "Found $backup_count backups, no cleanup needed"
|
||||
fi
|
||||
}
|
||||
|
||||
list_backups() {
|
||||
log_step "Available backups"
|
||||
|
||||
if walter_exec "ls -1 $BACKUP_DIR/furt-lua_* 2>/dev/null"; then
|
||||
walter_exec "ls -1t $BACKUP_DIR/furt-lua_* | head -n 5 | while read backup; do
|
||||
echo \" \$backup\"
|
||||
if [ -f \"\$backup/.backup_info\" ]; then
|
||||
cat \"\$backup/.backup_info\" | sed 's/^/ /'
|
||||
fi
|
||||
echo
|
||||
done"
|
||||
else
|
||||
log_warning "No backups found"
|
||||
fi
|
||||
}
|
||||
|
||||
rollback_deployment() {
|
||||
log_step "Rolling back deployment"
|
||||
|
||||
# List available backups
|
||||
local latest_backup=$(walter_exec "ls -1t $BACKUP_DIR/furt-lua_* 2>/dev/null | head -n 1" || echo "")
|
||||
|
||||
if [[ -z "$latest_backup" ]]; then
|
||||
log_error "No backups available for rollback"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Latest backup: $latest_backup"
|
||||
|
||||
if [[ "$FORCE" != "true" ]]; then
|
||||
echo -n "Rollback to this version? [y/N]: "
|
||||
read -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
log_info "Rollback cancelled"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Stop service
|
||||
stop_service
|
||||
|
||||
# Backup current version (before rollback)
|
||||
local rollback_backup=$(create_backup)
|
||||
if [[ -n "$rollback_backup" ]]; then
|
||||
walter_exec_as_root "mv $rollback_backup ${rollback_backup}_pre_rollback"
|
||||
fi
|
||||
|
||||
# Restore from backup
|
||||
walter_exec_as_root "rm -rf $TARGET_DIR"
|
||||
walter_exec_as_root "cp -r $latest_backup $TARGET_DIR"
|
||||
|
||||
# Fix permissions
|
||||
fix_permissions
|
||||
|
||||
# Start service
|
||||
start_service
|
||||
|
||||
# Health check
|
||||
if health_check; then
|
||||
log_success "Rollback completed successfully"
|
||||
else
|
||||
log_error "Rollback completed but health check failed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SERVICE MANAGEMENT FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
get_service_status() {
|
||||
# Check if furt process is actually running (regardless of rcctl status)
|
||||
if walter_exec "pgrep -u _furt -f 'src/main.lua' >/dev/null 2>&1"; then
|
||||
echo "running"
|
||||
else
|
||||
echo "stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
get_rcctl_status() {
|
||||
# Check OpenBSD service status specifically
|
||||
if walter_exec "rcctl check furt >/dev/null 2>&1"; then
|
||||
echo "running"
|
||||
else
|
||||
echo "stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_service() {
|
||||
log_step "Stopping furt service"
|
||||
|
||||
local process_status=$(get_service_status)
|
||||
local rcctl_status=$(get_rcctl_status)
|
||||
|
||||
if [[ "$process_status" == "running" ]]; then
|
||||
log_info "Furt process is running (rcctl status: $rcctl_status)"
|
||||
|
||||
# Try rcctl stop first if service is managed by rcctl
|
||||
if [[ "$rcctl_status" == "running" ]]; then
|
||||
log_info "Stopping via rcctl..."
|
||||
walter_exec_as_root "rcctl stop furt" || true
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# Check if still running (manual process or rcctl didn't work)
|
||||
if [[ $(get_service_status) == "running" ]]; then
|
||||
log_info "Process still running, stopping manually..."
|
||||
walter_exec_as_root "pkill -f -U $SERVICE_USER 'lua.*main.lua'" || true
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# Final check
|
||||
local final_status=$(get_service_status)
|
||||
if [[ "$final_status" == "stopped" ]]; then
|
||||
log_success "Service stopped"
|
||||
else
|
||||
log_error "Could not stop service"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_info "Service already stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
log_step "Starting furt service"
|
||||
|
||||
local status=$(get_service_status)
|
||||
if [[ "$status" == "stopped" ]]; then
|
||||
# Check port availability before starting
|
||||
if ! check_port_availability; then
|
||||
log_error "Cannot start service - port 8080 is occupied"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Starting furt service via rcctl..."
|
||||
walter_exec_as_root "rcctl start furt"
|
||||
|
||||
# Wait and check if service actually started
|
||||
sleep 5
|
||||
|
||||
local process_status=$(get_service_status)
|
||||
local rcctl_status=$(get_rcctl_status)
|
||||
|
||||
if [[ "$process_status" == "running" ]]; then
|
||||
log_success "Service started successfully (rcctl: $rcctl_status, process: $process_status)"
|
||||
elif [[ "$rcctl_status" == "running" ]]; then
|
||||
log_warning "rcctl reports running but process not detected - checking port..."
|
||||
if walter_exec "netstat -an | grep -q ':8080.*LISTEN'"; then
|
||||
log_success "Service appears to be running (port 8080 active)"
|
||||
else
|
||||
log_error "Service failed to start properly"
|
||||
# Show service logs for debugging
|
||||
walter_exec "tail -10 /var/log/daemon" || true
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_error "Failed to start service"
|
||||
# Show service logs for debugging
|
||||
walter_exec "tail -10 /var/log/daemon" || true
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_info "Service already running"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# DEPLOYMENT FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
prepare_source() {
|
||||
log_step "Preparing source files"
|
||||
|
||||
# Check source directory structure
|
||||
local required_dirs=("src" "config" "scripts")
|
||||
for dir in "${required_dirs[@]}"; do
|
||||
if [[ ! -d "$SOURCE_DIR/$dir" ]]; then
|
||||
log_error "Required directory missing: $SOURCE_DIR/$dir"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check required files
|
||||
local required_files=("src/main.lua" "scripts/start.sh")
|
||||
for file in "${required_files[@]}"; do
|
||||
if [[ ! -f "$SOURCE_DIR/$file" ]]; then
|
||||
log_error "Required file missing: $SOURCE_DIR/$file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "Source files validated"
|
||||
}
|
||||
|
||||
sync_files() {
|
||||
log_step "Syncing files to walter"
|
||||
|
||||
# Prepare rsync excludes
|
||||
local excludes=(
|
||||
"--exclude=.env"
|
||||
"--exclude=.env.*"
|
||||
"--exclude=*.backup"
|
||||
"--exclude=*.orig"
|
||||
"--exclude=*.tmp"
|
||||
"--exclude=.git/"
|
||||
"--exclude=.DS_Store"
|
||||
"--exclude=logs/"
|
||||
"--exclude=*.log"
|
||||
)
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would sync the following files:"
|
||||
rsync -avz --dry-run "${excludes[@]}" "$SOURCE_DIR/" "$WALTER_HOST:$TARGET_DIR/"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create target directory and set initial ownership
|
||||
walter_exec_as_root "mkdir -p $TARGET_DIR"
|
||||
walter_exec_as_root "chown -R $SERVICE_USER:$SERVICE_GROUP $TARGET_DIR"
|
||||
|
||||
# Sync files using rsync with _furt user
|
||||
log_info "Syncing files..."
|
||||
rsync -avz "${excludes[@]}" \
|
||||
-e "ssh" \
|
||||
--rsync-path="doas -u $SERVICE_USER rsync" \
|
||||
"$SOURCE_DIR/" "$WALTER_HOST:$TARGET_DIR/"
|
||||
|
||||
log_success "Files synced"
|
||||
}
|
||||
|
||||
fix_service_file() {
|
||||
log_step "Updating OpenBSD service file"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would update /etc/rc.d/furt"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Creating correct service file..."
|
||||
walter_exec_as_root "sh -c 'cat > /etc/rc.d/furt << \"EOF\"
|
||||
#!/bin/ksh
|
||||
|
||||
daemon=\"$TARGET_DIR/scripts/start.sh\"
|
||||
daemon_user=\"$SERVICE_USER\"
|
||||
daemon_cwd=\"$TARGET_DIR\"
|
||||
daemon_flags=\"start\"
|
||||
|
||||
. /etc/rc.d/rc.subr
|
||||
|
||||
# pexp NACH rc.subr überschreiben (Fix für OpenBSD Issue #77)
|
||||
pexp=\"/usr/local/bin/lua src/main.lua.*\"
|
||||
|
||||
rc_cmd \$1
|
||||
EOF'"
|
||||
|
||||
# Make service file executable
|
||||
walter_exec_as_root "chmod +x /etc/rc.d/furt"
|
||||
|
||||
# Enable service if not already enabled
|
||||
if ! walter_exec "rcctl ls on | grep -q furt"; then
|
||||
log_info "Enabling furt service..."
|
||||
walter_exec_as_root "rcctl enable furt"
|
||||
log_success "Service enabled"
|
||||
else
|
||||
log_info "Service already enabled"
|
||||
fi
|
||||
|
||||
log_success "Service file updated"
|
||||
}
|
||||
|
||||
fix_permissions() {
|
||||
log_step "Fixing permissions"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_info "DRY RUN: Would set ownership to $SERVICE_USER:$SERVICE_GROUP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Set ownership (already done in sync_files, but ensure it's correct)
|
||||
walter_exec_as_root "chown -R $SERVICE_USER:$SERVICE_GROUP $TARGET_DIR"
|
||||
|
||||
# Set executable permissions for scripts
|
||||
walter_exec_as_root "chmod +x $TARGET_DIR/scripts/*.sh"
|
||||
|
||||
log_success "Permissions fixed"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# HEALTH CHECK FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
check_port_availability() {
|
||||
log_step "Checking port availability"
|
||||
|
||||
local port="8080" # Default furt port
|
||||
|
||||
if walter_exec "netstat -an | grep -q ':$port'"; then
|
||||
log_warning "Port $port is already in use"
|
||||
walter_exec "netstat -an | grep ':$port'" || true
|
||||
|
||||
# Try to identify what's using the port
|
||||
log_info "Checking what's using port $port..."
|
||||
walter_exec "fstat 2>/dev/null | grep ':$port'" || walter_exec "netstat -anp 2>/dev/null | grep ':$port'" || true
|
||||
|
||||
return 1
|
||||
else
|
||||
log_success "Port $port is available"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
health_check() {
|
||||
log_step "Running health check"
|
||||
|
||||
local retries=$HEALTH_RETRIES
|
||||
while [[ $retries -gt 0 ]]; do
|
||||
log_info "Health check attempt $((HEALTH_RETRIES - retries + 1))/$HEALTH_RETRIES"
|
||||
|
||||
if walter_exec "curl -s --max-time $HEALTH_TIMEOUT $HEALTH_URL >/dev/null 2>&1"; then
|
||||
log_success "Health check passed"
|
||||
|
||||
# Get health check response
|
||||
local health_response=$(walter_exec "curl -s --max-time $HEALTH_TIMEOUT $HEALTH_URL" || echo "")
|
||||
if [[ -n "$health_response" ]]; then
|
||||
log_info "Health response: $health_response"
|
||||
fi
|
||||
|
||||
# Additional status info
|
||||
local process_status=$(get_service_status)
|
||||
local rcctl_status=$(get_rcctl_status)
|
||||
log_info "Service status - Process: $process_status, rcctl: $rcctl_status"
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
((retries--))
|
||||
if [[ $retries -gt 0 ]]; then
|
||||
log_info "Health check failed, retrying in 5 seconds..."
|
||||
sleep 5
|
||||
fi
|
||||
done
|
||||
|
||||
log_error "Health check failed after $HEALTH_RETRIES attempts"
|
||||
log_info "Debugging service status..."
|
||||
local process_status=$(get_service_status)
|
||||
local rcctl_status=$(get_rcctl_status)
|
||||
log_info "Process status: $process_status"
|
||||
log_info "rcctl status: $rcctl_status"
|
||||
|
||||
# Check if port is at least listening
|
||||
if walter_exec "netstat -an | grep -q ':8080.*LISTEN'"; then
|
||||
log_warning "Port 8080 is listening but health check failed - possible service issue"
|
||||
else
|
||||
log_error "Port 8080 is not listening - service not running"
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN DEPLOYMENT FUNCTION
|
||||
# =============================================================================
|
||||
|
||||
deploy() {
|
||||
log_step "Starting deployment: karl → walter"
|
||||
|
||||
# Pre-deployment checks
|
||||
check_dependencies
|
||||
prepare_source
|
||||
|
||||
# Show deployment summary
|
||||
log_info "Deployment Summary:"
|
||||
log_info " Source: $SOURCE_DIR"
|
||||
log_info " Target: $WALTER_HOST:$TARGET_DIR"
|
||||
log_info " Service User: $SERVICE_USER"
|
||||
log_info " Dry Run: $DRY_RUN"
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
log_warning "DRY RUN MODE - No changes will be made"
|
||||
fi
|
||||
|
||||
# Confirmation prompt
|
||||
if [[ "$FORCE" != "true" && "$DRY_RUN" != "true" ]]; then
|
||||
echo -n "Continue with deployment? [y/N]: "
|
||||
read -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
log_info "Deployment cancelled"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create backup before deployment
|
||||
local backup_path=""
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
backup_path=$(create_backup)
|
||||
fi
|
||||
|
||||
# Stop service
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
stop_service
|
||||
fi
|
||||
|
||||
# Deploy files
|
||||
sync_files
|
||||
fix_permissions
|
||||
|
||||
# Update service file for correct paths
|
||||
fix_service_file
|
||||
|
||||
# Start service
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
# Check if port is available before starting
|
||||
if ! check_port_availability; then
|
||||
log_error "Cannot start service - port conflict detected"
|
||||
log_info "Try: ssh walter \"doas pkill -f 'lua.*main.lua'\" to kill conflicting processes"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if start_service; then
|
||||
# Health check
|
||||
if health_check; then
|
||||
log_success "Deployment completed successfully!"
|
||||
|
||||
# Cleanup old backups
|
||||
cleanup_old_backups
|
||||
else
|
||||
log_error "Deployment failed health check"
|
||||
|
||||
# Offer rollback
|
||||
if [[ -n "$backup_path" ]]; then
|
||||
echo -n "Rollback to previous version? [y/N]: "
|
||||
read -r response
|
||||
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||||
log_info "Rolling back..."
|
||||
walter_exec_as_root "rm -rf $TARGET_DIR"
|
||||
walter_exec_as_root "cp -r $backup_path $TARGET_DIR"
|
||||
fix_permissions
|
||||
start_service
|
||||
health_check
|
||||
fi
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_error "Failed to start service after deployment"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_success "Dry run completed - no changes made"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN SCRIPT LOGIC
|
||||
# =============================================================================
|
||||
|
||||
# Parse command line arguments
|
||||
DRY_RUN=false
|
||||
ROLLBACK=false
|
||||
FORCE=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
--rollback)
|
||||
ROLLBACK=true
|
||||
shift
|
||||
;;
|
||||
--force)
|
||||
FORCE=true
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
log_info "Furt Deployment Script - karl → walter"
|
||||
log_info "$(date)"
|
||||
|
||||
if [[ "$ROLLBACK" == "true" ]]; then
|
||||
rollback_deployment
|
||||
else
|
||||
deploy
|
||||
fi
|
||||
}
|
||||
|
||||
# Trap for cleanup on exit
|
||||
cleanup() {
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log_error "Deployment failed"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
18
scripts/manual_mail_test.sh
Normal file
18
scripts/manual_mail_test.sh
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/bash
|
||||
# Manual SMTP test with corrected JSON
|
||||
|
||||
echo "Testing SMTP with corrected JSON..."
|
||||
|
||||
# Simple test without timestamp embedding
|
||||
curl -X POST http://127.0.0.1:8080/v1/mail/send \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Furt Test User",
|
||||
"email": "michael@dragons-at-work.de",
|
||||
"subject": "Furt SMTP Test Success!",
|
||||
"message": "This is a test email from Furt Lua HTTP-Server. SMTP Integration working!"
|
||||
}'
|
||||
|
||||
echo ""
|
||||
echo "Check response above for success:true"
|
||||
|
||||
80
scripts/production_test_sequence.sh
Normal file
80
scripts/production_test_sequence.sh
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#!/bin/bash
|
||||
# Production Test für api.dragons-at-work.de
|
||||
|
||||
echo "Testing Production API via Apache Proxy"
|
||||
echo "======================================="
|
||||
|
||||
# Test 1: HTTPS Health Check
|
||||
echo ""
|
||||
echo "[1] Testing HTTPS Health Check..."
|
||||
https_health=$(curl -s https://api.dragons-at-work.de/health)
|
||||
echo "HTTPS Response: $https_health"
|
||||
|
||||
if echo "$https_health" | grep -q "healthy"; then
|
||||
echo "[OK] HTTPS Proxy working"
|
||||
else
|
||||
echo "[ERROR] HTTPS Proxy failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: SMTP Status via HTTPS
|
||||
echo ""
|
||||
echo "[2] Testing SMTP Configuration via HTTPS..."
|
||||
if echo "$https_health" | grep -q '"smtp_configured":true'; then
|
||||
echo "[OK] SMTP configured and accessible via HTTPS"
|
||||
else
|
||||
echo "[ERROR] SMTP not configured or not accessible"
|
||||
fi
|
||||
|
||||
# Test 3: CORS Headers
|
||||
echo ""
|
||||
echo "[3] Testing CORS Headers..."
|
||||
cors_test=$(curl -s -I https://api.dragons-at-work.de/health | grep -i "access-control")
|
||||
if [ -n "$cors_test" ]; then
|
||||
echo "[OK] CORS headers present: $cors_test"
|
||||
else
|
||||
echo "[WARN] CORS headers missing - add to Apache config"
|
||||
fi
|
||||
|
||||
# Test 4: Production Mail Test
|
||||
echo ""
|
||||
echo "[4] Testing Production Mail via HTTPS..."
|
||||
echo "WARNING: This sends real email via production API"
|
||||
read -p "Continue with production mail test? (y/N): " -n 1 -r
|
||||
echo
|
||||
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Sending production test email..."
|
||||
|
||||
prod_mail_response=$(curl -s -X POST https://api.dragons-at-work.de/v1/mail/send \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Production Test",
|
||||
"email": "test@dragons-at-work.de",
|
||||
"subject": "Production API Test - Apache Proxy Success!",
|
||||
"message": "This email was sent via the production API at api.dragons-at-work.de through Apache proxy to Furt-Lua backend. HTTPS integration working!"
|
||||
}')
|
||||
|
||||
echo "Production Response: $prod_mail_response"
|
||||
|
||||
if echo "$prod_mail_response" | grep -q '"success":true'; then
|
||||
echo "[OK] PRODUCTION MAIL SENT VIA HTTPS!"
|
||||
echo "Check admin@dragons-at-work.de for delivery confirmation"
|
||||
else
|
||||
echo "[ERROR] Production mail failed"
|
||||
fi
|
||||
else
|
||||
echo "Skipping production mail test"
|
||||
fi
|
||||
|
||||
# Test 5: Security Headers
|
||||
echo ""
|
||||
echo "[5] Testing Security Headers..."
|
||||
security_headers=$(curl -s -I https://api.dragons-at-work.de/health)
|
||||
echo "Security Headers:"
|
||||
echo "$security_headers" | grep -i "x-content-type-options\|x-frame-options\|strict-transport"
|
||||
|
||||
echo ""
|
||||
echo "Production Test Complete!"
|
||||
echo "========================"
|
||||
echo "Next: Hugo integration with https://api.dragons-at-work.de/v1/mail/send"
|
||||
101
scripts/setup_env.sh
Executable file
101
scripts/setup_env.sh
Executable file
|
|
@ -0,0 +1,101 @@
|
|||
#!/bin/bash
|
||||
# furt-lua/scripts/setup_env.sh
|
||||
# Add SMTP environment variables to existing .env (non-destructive)
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}=== Furt SMTP Environment Setup ===${NC}"
|
||||
|
||||
# Navigate to furt project root
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||
ENV_FILE="$PROJECT_ROOT/.env"
|
||||
|
||||
echo -e "${YELLOW}Project root:${NC} $PROJECT_ROOT"
|
||||
echo -e "${YELLOW}Environment file:${NC} $ENV_FILE"
|
||||
|
||||
# Check if .env exists
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo -e "${YELLOW}Creating new .env file...${NC}"
|
||||
cat > "$ENV_FILE" << 'EOF'
|
||||
# Dragons@Work Project Environment Variables
|
||||
|
||||
# Furt SMTP Configuration for mail.dragons-at-work.de
|
||||
SMTP_HOST="mail.dragons-at-work.de"
|
||||
SMTP_PORT="465"
|
||||
SMTP_USERNAME="your_email@dragons-at-work.de"
|
||||
SMTP_PASSWORD="your_smtp_password"
|
||||
SMTP_FROM="noreply@dragons-at-work.de"
|
||||
SMTP_TO="michael@dragons-at-work.de"
|
||||
EOF
|
||||
echo -e "${GREEN}[OK] Created new .env file${NC}"
|
||||
echo -e "${YELLOW}[EDIT] Please edit:${NC} nano $ENV_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}[OK] Found existing .env file${NC}"
|
||||
|
||||
# Check if SMTP variables already exist
|
||||
smtp_username_exists=$(grep -c "^SMTP_USERNAME=" "$ENV_FILE" 2>/dev/null || echo "0")
|
||||
smtp_password_exists=$(grep -c "^SMTP_PASSWORD=" "$ENV_FILE" 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$smtp_username_exists" -gt 0 ] && [ "$smtp_password_exists" -gt 0 ]; then
|
||||
echo -e "${GREEN}[OK] SMTP variables already configured${NC}"
|
||||
|
||||
# Load and show current values
|
||||
source "$ENV_FILE"
|
||||
echo -e "${YELLOW}Current SMTP User:${NC} ${SMTP_USERNAME:-NOT_SET}"
|
||||
echo -e "${YELLOW}Current SMTP Password:${NC} ${SMTP_PASSWORD:+[CONFIGURED]}${SMTP_PASSWORD:-NOT_SET}"
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}To update SMTP settings:${NC} nano $ENV_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Add missing SMTP variables
|
||||
echo -e "${YELLOW}Adding SMTP configuration to existing .env...${NC}"
|
||||
|
||||
# Add section header if not present
|
||||
if ! grep -q "SMTP_" "$ENV_FILE" 2>/dev/null; then
|
||||
echo "" >> "$ENV_FILE"
|
||||
echo "# Furt SMTP Configuration for mail.dragons-at-work.de" >> "$ENV_FILE"
|
||||
fi
|
||||
|
||||
# Add username if missing
|
||||
if [ "$smtp_username_exists" -eq 0 ]; then
|
||||
echo "SMTP_HOST=\"mail.dragons-at-work.de\"" >> "$ENV_FILE"
|
||||
echo "SMTP_PORT=\"465\"" >> "$ENV_FILE"
|
||||
echo "SMTP_USERNAME=\"your_email@dragons-at-work.de\"" >> "$ENV_FILE"
|
||||
echo -e "${GREEN}[OK] Added SMTP_HOST, SMTP_PORT, SMTP_USERNAME${NC}"
|
||||
fi
|
||||
|
||||
# Add password if missing
|
||||
if [ "$smtp_password_exists" -eq 0 ]; then
|
||||
echo "SMTP_PASSWORD=\"your_smtp_password\"" >> "$ENV_FILE"
|
||||
echo "SMTP_FROM=\"noreply@dragons-at-work.de\"" >> "$ENV_FILE"
|
||||
echo "SMTP_TO=\"michael@dragons-at-work.de\"" >> "$ENV_FILE"
|
||||
echo -e "${GREEN}[OK] Added SMTP_PASSWORD, SMTP_FROM, SMTP_TO${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}[OK] SMTP configuration added to .env${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Next steps:${NC}"
|
||||
echo "1. Edit SMTP credentials: nano $ENV_FILE"
|
||||
echo "2. Set your actual email@dragons-at-work.de in SMTP_USERNAME"
|
||||
echo "3. Set your actual SMTP password in SMTP_PASSWORD"
|
||||
echo "4. Test with: ./scripts/start.sh"
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Current .env content:${NC}"
|
||||
echo "==================="
|
||||
cat "$ENV_FILE"
|
||||
echo "==================="
|
||||
echo ""
|
||||
echo -e "${GREEN}Ready for SMTP testing!${NC}"
|
||||
|
||||
116
scripts/start.sh
Executable file
116
scripts/start.sh
Executable file
|
|
@ -0,0 +1,116 @@
|
|||
#!/bin/sh
|
||||
# furt-lua/scripts/start.sh
|
||||
# Start script for Furt Lua HTTP-Server
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Script directory (POSIX-compatible)
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")" # für src/, cd
|
||||
REPO_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")" # für .env
|
||||
|
||||
echo -e "${GREEN}=== Furt Lua HTTP-Server Startup ===${NC}"
|
||||
|
||||
# Check required dependencies
|
||||
echo -e "${YELLOW}Checking dependencies...${NC}"
|
||||
|
||||
# Load environment variables - Universal Config Detection
|
||||
echo -e "${YELLOW}Loading environment variables...${NC}"
|
||||
if [ -f "$REPO_ROOT/.env" ]; then
|
||||
echo -e "${GREEN}[OK]${NC} Loading from $REPO_ROOT/.env"
|
||||
export $(grep -v '^#' "$REPO_ROOT/.env" | grep -v '^$' | xargs)
|
||||
elif [ -f "/usr/local/etc/furt/environment" ]; then
|
||||
echo -e "${GREEN}[OK]${NC} Loading from /usr/local/etc/furt/environment"
|
||||
export $(grep -v '^#' /usr/local/etc/furt/environment | grep -v '^$' | xargs)
|
||||
elif [ -f "/etc/furt/environment" ]; then
|
||||
echo -e "${GREEN}[OK]${NC} Loading from /etc/furt/environment"
|
||||
export $(grep -v '^#' /etc/furt/environment | grep -v '^$' | xargs)
|
||||
else
|
||||
echo -e "${YELLOW}[WARN]${NC} No config file found in project root or system"
|
||||
fi
|
||||
|
||||
# Setup Lua from config (after loading environment)
|
||||
LUA_CMD="${LUA_COMMAND:-lua51}"
|
||||
LUA_VER="${LUA_VERSION:-5.1}"
|
||||
|
||||
# Check if configured Lua is installed (POSIX-compatible)
|
||||
if ! [ -x "$LUA_CMD" ]; then
|
||||
echo -e "${RED}Error: $LUA_CMD is not installed${NC}"
|
||||
echo "Install with: pkg_add lua51 (OpenBSD) or apt install lua5.1 (Ubuntu)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Lua version
|
||||
LUA_VERSION_OUTPUT=$($LUA_CMD -v 2>&1 | head -n1)
|
||||
echo -e "${YELLOW}Lua command:${NC} $LUA_CMD ($LUA_VERSION_OUTPUT)"
|
||||
|
||||
# Test lua-socket
|
||||
$LUA_CMD -e "require('socket')" 2>/dev/null || {
|
||||
echo -e "${RED}Error: lua-socket not found for $LUA_CMD${NC}"
|
||||
echo "Install with: pkg_add lua51-socket (OpenBSD) or apt install lua-socket (Ubuntu)"
|
||||
exit 1
|
||||
}
|
||||
echo -e "${GREEN}✓${NC} lua-socket found"
|
||||
|
||||
# Test lua-cjson (system or luarocks)
|
||||
LUA_PATH="$HOME/.luarocks/share/lua/$LUA_VER/?.lua;;" \
|
||||
LUA_CPATH="$HOME/.luarocks/lib/lua/$LUA_VER/?.so;;" \
|
||||
$LUA_CMD -e "require('cjson')" 2>/dev/null || {
|
||||
echo -e "${RED}Error: lua-cjson not found for $LUA_CMD${NC}"
|
||||
echo "Install with: pkg_add lua51-cjson (OpenBSD) or luarocks install lua-cjson"
|
||||
exit 1
|
||||
}
|
||||
echo -e "${GREEN}✓${NC} lua-cjson found"
|
||||
|
||||
# Test lua-ssl (optional for HTTPS)
|
||||
LUA_PATH="$HOME/.luarocks/share/lua/$LUA_VER/?.lua;;" \
|
||||
LUA_CPATH="$HOME/.luarocks/lib/lua/$LUA_VER/?.so;;" \
|
||||
$LUA_CMD -e "require('ssl')" 2>/dev/null && {
|
||||
echo -e "${GREEN}✓${NC} lua-ssl found (HTTPS ready)"
|
||||
} || {
|
||||
echo -e "${YELLOW}○${NC} lua-ssl not found (install with: luarocks install luaossl)"
|
||||
}
|
||||
|
||||
# Check SMTP configuration (korrekte Variable-Namen)
|
||||
if [ -n "$SMTP_USERNAME" ] && [ -n "$SMTP_PASSWORD" ]; then
|
||||
echo -e "${GREEN}[OK]${NC} SMTP configured: $SMTP_USERNAME"
|
||||
else
|
||||
echo -e "${YELLOW}[WARN]${NC} SMTP credentials missing in .env"
|
||||
echo "Add SMTP_USERNAME and SMTP_PASSWORD to .env"
|
||||
fi
|
||||
|
||||
# Change to project directory
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Add current directory and luarocks to Lua path for requires (dynamic version)
|
||||
export LUA_PATH="$PROJECT_DIR/src/?.lua;$PROJECT_DIR/?.lua;$HOME/.luarocks/share/lua/$LUA_VER/?.lua;;"
|
||||
export LUA_CPATH="$HOME/.luarocks/lib/lua/$LUA_VER/?.so;;"
|
||||
|
||||
echo -e "${GREEN}Starting Furt HTTP-Server...${NC}"
|
||||
echo -e "${YELLOW}Project directory:${NC} $PROJECT_DIR"
|
||||
echo -e "${YELLOW}Lua paths configured for $LUA_CMD (version $LUA_VER)${NC}"
|
||||
echo ""
|
||||
|
||||
# Auto-detect service context
|
||||
if [ ! -t 0 ] || [ ! -t 1 ]; then
|
||||
# No TTY = Service mode (rcctl)
|
||||
echo "Starting Furt in daemon mode..."
|
||||
$LUA_CMD src/main.lua &
|
||||
echo "Furt started (PID: $!)"
|
||||
else
|
||||
# Interactive mode (manual/development)
|
||||
# echo "Furt HTTP-Server started on 127.0.0.1:8080"
|
||||
# echo "Press Ctrl+C to stop"
|
||||
$LUA_CMD src/main.lua
|
||||
fi
|
||||
|
||||
|
||||
# Start server
|
||||
# $LUA_CMD src/main.lua
|
||||
|
||||
171
scripts/stress_test.sh
Executable file
171
scripts/stress_test.sh
Executable file
|
|
@ -0,0 +1,171 @@
|
|||
#!/bin/bash
|
||||
# furt-lua/scripts/stress_test.sh
|
||||
# Rate-Limiting und Performance Stress-Test
|
||||
|
||||
BASE_URL="http://127.0.0.1:8080"
|
||||
# Use correct API keys that match current .env
|
||||
API_KEY="hugo-dev-key-change-in-production"
|
||||
|
||||
echo "⚡ Furt API Stress Test"
|
||||
echo "======================"
|
||||
|
||||
# Test 1: Rate-Limiting Test (schnelle Requests)
|
||||
echo -e "\n1️⃣ Rate-Limiting Test (20 quick requests):"
|
||||
echo "Expected: First ~10 should work, then rate limiting kicks in"
|
||||
|
||||
rate_limit_failures=0
|
||||
rate_limit_success=0
|
||||
|
||||
for i in {1..20}; do
|
||||
response=$(curl -s -w "%{http_code}" \
|
||||
-H "X-API-Key: $API_KEY" \
|
||||
"$BASE_URL/v1/auth/status")
|
||||
|
||||
status=$(echo "$response" | tail -c 4)
|
||||
|
||||
if [ "$status" == "200" ]; then
|
||||
rate_limit_remaining=$(echo "$response" | head -n -1 | jq -r '.rate_limit_remaining // "N/A"' 2>/dev/null)
|
||||
echo "Request $i: ✅ 200 OK (Rate limit remaining: $rate_limit_remaining)"
|
||||
((rate_limit_success++))
|
||||
elif [ "$status" == "429" ]; then
|
||||
echo "Request $i: ⛔ 429 Rate Limited"
|
||||
((rate_limit_failures++))
|
||||
else
|
||||
echo "Request $i: ❌ $status Error"
|
||||
fi
|
||||
|
||||
# Small delay to prevent overwhelming
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
echo "Rate-Limiting Results: $rate_limit_success success, $rate_limit_failures rate-limited"
|
||||
|
||||
# Test 2: Performance Test (concurrent requests)
|
||||
echo -e "\n2️⃣ Performance Test (10 concurrent requests):"
|
||||
echo "Testing server under concurrent load..."
|
||||
|
||||
start_time=$(date +%s.%N)
|
||||
|
||||
# Create temp files for results
|
||||
temp_dir=$(mktemp -d)
|
||||
trap "rm -rf $temp_dir" EXIT
|
||||
|
||||
# Launch concurrent requests
|
||||
for i in {1..10}; do
|
||||
{
|
||||
local_start=$(date +%s.%N)
|
||||
response=$(curl -s -w "%{http_code}" \
|
||||
-H "X-API-Key: $API_KEY" \
|
||||
"$BASE_URL/health")
|
||||
local_end=$(date +%s.%N)
|
||||
|
||||
status=$(echo "$response" | tail -c 4)
|
||||
duration=$(echo "$local_end - $local_start" | bc -l)
|
||||
|
||||
echo "Concurrent $i: Status $status, Duration ${duration}s" > "$temp_dir/result_$i"
|
||||
} &
|
||||
done
|
||||
|
||||
# Wait for all background jobs
|
||||
wait
|
||||
|
||||
end_time=$(date +%s.%N)
|
||||
total_duration=$(echo "$end_time - $start_time" | bc -l)
|
||||
|
||||
echo "Concurrent Results:"
|
||||
cat "$temp_dir"/result_* | sort
|
||||
echo "Total Duration: ${total_duration}s"
|
||||
|
||||
# Test 3: Mail API Performance (lighter test)
|
||||
echo -e "\n3️⃣ Mail API Performance Test (5 requests):"
|
||||
echo "Testing mail endpoint performance..."
|
||||
|
||||
mail_success=0
|
||||
mail_errors=0
|
||||
|
||||
for i in {1..5}; do
|
||||
start_time=$(date +%s.%N)
|
||||
|
||||
response=$(curl -s -w "%{http_code}" \
|
||||
-H "X-API-Key: $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"name\":\"Stress Test $i\",\"email\":\"test$i@example.com\",\"subject\":\"Performance Test\",\"message\":\"Load test message $i\"}" \
|
||||
"$BASE_URL/v1/mail/send")
|
||||
|
||||
end_time=$(date +%s.%N)
|
||||
duration=$(echo "$end_time - $start_time" | bc -l)
|
||||
|
||||
status=$(echo "$response" | tail -c 4)
|
||||
|
||||
if [ "$status" == "200" ]; then
|
||||
echo "Mail $i: ✅ 200 OK (${duration}s)"
|
||||
((mail_success++))
|
||||
else
|
||||
echo "Mail $i: ❌ Status $status (${duration}s)"
|
||||
((mail_errors++))
|
||||
fi
|
||||
|
||||
# Delay between mail sends to be nice to SMTP server
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Mail Performance: $mail_success success, $mail_errors errors"
|
||||
|
||||
# Test 4: Mixed Load Test
|
||||
echo -e "\n4️⃣ Mixed Load Test (Auth + Health requests):"
|
||||
echo "Testing mixed endpoint load..."
|
||||
|
||||
mixed_total=0
|
||||
mixed_success=0
|
||||
|
||||
for i in {1..15}; do
|
||||
((mixed_total++))
|
||||
|
||||
if [ $((i % 3)) -eq 0 ]; then
|
||||
# Every 3rd request: auth status
|
||||
endpoint="/v1/auth/status"
|
||||
else
|
||||
# Other requests: health check
|
||||
endpoint="/health"
|
||||
fi
|
||||
|
||||
response=$(curl -s -w "%{http_code}" \
|
||||
-H "X-API-Key: $API_KEY" \
|
||||
"$BASE_URL$endpoint")
|
||||
|
||||
status=$(echo "$response" | tail -c 4)
|
||||
|
||||
if [ "$status" == "200" ]; then
|
||||
echo "Mixed $i ($endpoint): ✅ 200 OK"
|
||||
((mixed_success++))
|
||||
else
|
||||
echo "Mixed $i ($endpoint): ❌ $status"
|
||||
fi
|
||||
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
echo "Mixed Load Results: $mixed_success/$mixed_total successful"
|
||||
|
||||
# Summary
|
||||
echo -e "\n📊 Stress Test Summary:"
|
||||
echo "================================="
|
||||
echo "Rate-Limiting: $rate_limit_success success, $rate_limit_failures limited (Expected behavior ✅)"
|
||||
echo "Concurrent Load: Check above results"
|
||||
echo "Mail Performance: $mail_success/$((mail_success + mail_errors)) successful"
|
||||
echo "Mixed Load: $mixed_success/$mixed_total successful"
|
||||
|
||||
if [ $rate_limit_failures -gt 0 ]; then
|
||||
echo "✅ Rate limiting is working correctly!"
|
||||
else
|
||||
echo "⚠️ Rate limiting may need adjustment (no limits hit)"
|
||||
fi
|
||||
|
||||
if [ $mixed_success -eq $mixed_total ] && [ $mail_success -gt 3 ]; then
|
||||
echo "✅ Server performance looks good!"
|
||||
else
|
||||
echo "⚠️ Some performance issues detected"
|
||||
fi
|
||||
|
||||
echo -e "\n🎯 Next: Check server logs for any errors or memory issues"
|
||||
|
||||
79
scripts/test_auth.sh
Executable file
79
scripts/test_auth.sh
Executable file
|
|
@ -0,0 +1,79 @@
|
|||
#!/bin/bash
|
||||
# furt-lua/scripts/test_auth.sh
|
||||
# Test API-Key-Authentifizierung (ohne jq parse errors)
|
||||
|
||||
BASE_URL="http://127.0.0.1:8080"
|
||||
HUGO_API_KEY="hugo-dev-key-change-in-production"
|
||||
ADMIN_API_KEY="admin-dev-key-change-in-production"
|
||||
INVALID_API_KEY="invalid-key-should-fail"
|
||||
|
||||
echo "🔐 Testing Furt API-Key Authentication"
|
||||
echo "======================================"
|
||||
|
||||
# Helper function to make clean API calls
|
||||
make_request() {
|
||||
local method="$1"
|
||||
local url="$2"
|
||||
local headers="$3"
|
||||
local data="$4"
|
||||
|
||||
echo "Request: $method $url"
|
||||
if [ -n "$headers" ]; then
|
||||
echo "Headers: $headers"
|
||||
fi
|
||||
|
||||
local response=$(curl -s $method \
|
||||
${headers:+-H "$headers"} \
|
||||
${data:+-d "$data"} \
|
||||
-H "Content-Type: application/json" \
|
||||
"$url")
|
||||
|
||||
local status=$(curl -s -o /dev/null -w "%{http_code}" $method \
|
||||
${headers:+-H "$headers"} \
|
||||
${data:+-d "$data"} \
|
||||
-H "Content-Type: application/json" \
|
||||
"$url")
|
||||
|
||||
echo "Status: $status"
|
||||
echo "Response: $response" | jq '.' 2>/dev/null || echo "$response"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Test 1: Health-Check (public, no auth needed)
|
||||
echo "1️⃣ Public Health Check (no auth required):"
|
||||
make_request "-X GET" "$BASE_URL/health"
|
||||
|
||||
# Test 2: No API-Key -> 401
|
||||
echo "2️⃣ Mail without API-Key (should fail with 401):"
|
||||
make_request "-X POST" "$BASE_URL/v1/mail/send" "" '{"name":"Test","email":"test@example.com","message":"Test"}'
|
||||
|
||||
# Test 3: Invalid API-Key -> 401
|
||||
echo "3️⃣ Mail with invalid API-Key (should fail with 401):"
|
||||
make_request "-X POST" "$BASE_URL/v1/mail/send" "X-API-Key: $INVALID_API_KEY" '{"name":"Test","email":"test@example.com","message":"Test"}'
|
||||
|
||||
# Test 4: Valid API-Key -> 200 (or SMTP error)
|
||||
echo "4️⃣ Mail with valid Hugo API-Key (should work):"
|
||||
make_request "-X POST" "$BASE_URL/v1/mail/send" "X-API-Key: $HUGO_API_KEY" '{
|
||||
"name": "Test User",
|
||||
"email": "test@example.com",
|
||||
"subject": "API Auth Test",
|
||||
"message": "This is a test message via authenticated API"
|
||||
}'
|
||||
|
||||
# Test 5: Auth Status Check
|
||||
echo "5️⃣ Auth Status Check with Hugo API-Key:"
|
||||
make_request "-X GET" "$BASE_URL/v1/auth/status" "X-API-Key: $HUGO_API_KEY"
|
||||
|
||||
# Test 6: Auth Status with Admin API-Key
|
||||
echo "6️⃣ Auth Status Check with Admin API-Key:"
|
||||
make_request "-X GET" "$BASE_URL/v1/auth/status" "X-API-Key: $ADMIN_API_KEY"
|
||||
|
||||
echo "✅ Auth Testing Complete!"
|
||||
echo ""
|
||||
echo "Expected Results:"
|
||||
echo "- Test 1: ✅ 200 OK (health check)"
|
||||
echo "- Test 2: ❌ 401 Unauthorized (Missing API-Key)"
|
||||
echo "- Test 3: ❌ 401 Unauthorized (Invalid API-Key)"
|
||||
echo "- Test 4: ✅ 200 OK (Valid API-Key) or 500 if SMTP not configured"
|
||||
echo "- Test 5,6: ✅ 200 OK with auth details"
|
||||
|
||||
94
scripts/test_curl.sh
Executable file
94
scripts/test_curl.sh
Executable file
|
|
@ -0,0 +1,94 @@
|
|||
#!/bin/bash
|
||||
# furt-lua/scripts/test_curl.sh
|
||||
# Manual curl tests for Furt Lua HTTP-Server
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Server configuration
|
||||
SERVER_URL="http://127.0.0.1:8080"
|
||||
|
||||
echo -e "${GREEN}=== Furt HTTP-Server Manual Tests ===${NC}"
|
||||
echo -e "${YELLOW}Server:${NC} $SERVER_URL"
|
||||
echo ""
|
||||
|
||||
# Test 1: Health Check
|
||||
echo -e "${YELLOW}Test 1: Health Check${NC}"
|
||||
echo "curl -X GET $SERVER_URL/health"
|
||||
echo ""
|
||||
curl -X GET "$SERVER_URL/health" | jq . 2>/dev/null || curl -X GET "$SERVER_URL/health"
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
# Test 2: Basic POST Test
|
||||
echo -e "${YELLOW}Test 2: Basic POST Test${NC}"
|
||||
echo "curl -X POST $SERVER_URL/test -H 'Content-Type: application/json' -d '{\"test\":\"data\"}'"
|
||||
echo ""
|
||||
curl -X POST "$SERVER_URL/test" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"test":"data","number":42}' | jq . 2>/dev/null || \
|
||||
curl -X POST "$SERVER_URL/test" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"test":"data","number":42}'
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
# Test 3: Mail Endpoint - Valid Data
|
||||
echo -e "${YELLOW}Test 3: Mail Endpoint - Valid Data${NC}"
|
||||
echo "curl -X POST $SERVER_URL/v1/mail/send -H 'Content-Type: application/json' -d '{...}'"
|
||||
echo ""
|
||||
curl -X POST "$SERVER_URL/v1/mail/send" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Test User",
|
||||
"email": "test@example.com",
|
||||
"message": "This is a test message from curl"
|
||||
}' | jq . 2>/dev/null || \
|
||||
curl -X POST "$SERVER_URL/v1/mail/send" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Test User",
|
||||
"email": "test@example.com",
|
||||
"message": "This is a test message from curl"
|
||||
}'
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
# Test 4: Mail Endpoint - Invalid Data
|
||||
echo -e "${YELLOW}Test 4: Mail Endpoint - Invalid Data (Missing Fields)${NC}"
|
||||
echo "curl -X POST $SERVER_URL/v1/mail/send -H 'Content-Type: application/json' -d '{\"name\":\"Test\"}'"
|
||||
echo ""
|
||||
curl -X POST "$SERVER_URL/v1/mail/send" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"Test"}' | jq . 2>/dev/null || \
|
||||
curl -X POST "$SERVER_URL/v1/mail/send" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"Test"}'
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
# Test 5: 404 Error
|
||||
echo -e "${YELLOW}Test 5: 404 Error Handling${NC}"
|
||||
echo "curl -X GET $SERVER_URL/nonexistent"
|
||||
echo ""
|
||||
curl -X GET "$SERVER_URL/nonexistent" | jq . 2>/dev/null || curl -X GET "$SERVER_URL/nonexistent"
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
# Test 6: Method Not Allowed (if we want to test this)
|
||||
echo -e "${YELLOW}Test 6: Wrong Method${NC}"
|
||||
echo "curl -X PUT $SERVER_URL/v1/mail/send"
|
||||
echo ""
|
||||
curl -X PUT "$SERVER_URL/v1/mail/send" | jq . 2>/dev/null || curl -X PUT "$SERVER_URL/v1/mail/send"
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
echo -e "${GREEN}=== Manual Tests Complete ===${NC}"
|
||||
echo -e "${YELLOW}Note:${NC} These tests show the raw HTTP responses."
|
||||
echo -e "${YELLOW} For automated testing, use: lua tests/test_http.lua${NC}"
|
||||
|
||||
61
scripts/test_modular.sh
Normal file
61
scripts/test_modular.sh
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#!/bin/bash
|
||||
# furt-lua/scripts/test_modular.sh
|
||||
# Test der modularen Furt-Architektur
|
||||
|
||||
BASE_URL="http://127.0.0.1:8080"
|
||||
HUGO_API_KEY="hugo-dev-key-change-in-production"
|
||||
|
||||
echo "🧩 Testing Modular Furt Architecture"
|
||||
echo "===================================="
|
||||
|
||||
# Test 1: Module dependencies check
|
||||
echo -e "\n1️⃣ Testing module imports (should not error on startup):"
|
||||
echo "Starting server in background..."
|
||||
cd "$(dirname "$0")/.."
|
||||
lua src/main.lua &
|
||||
SERVER_PID=$!
|
||||
sleep 2
|
||||
|
||||
if kill -0 $SERVER_PID 2>/dev/null; then
|
||||
echo "✅ Server started successfully - all modules loaded"
|
||||
else
|
||||
echo "❌ Server failed to start - module import error"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: Public endpoints (no auth)
|
||||
echo -e "\n2️⃣ Testing public endpoints:"
|
||||
curl -s -w "Status: %{http_code}\n" "$BASE_URL/health" | jq '.features'
|
||||
|
||||
# Test 3: Protected endpoints without auth (should fail)
|
||||
echo -e "\n3️⃣ Testing auth protection:"
|
||||
curl -s -w "Status: %{http_code}\n" \
|
||||
-X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"Test","email":"test@example.com","message":"Test"}' \
|
||||
"$BASE_URL/v1/mail/send" | jq '.error'
|
||||
|
||||
# Test 4: Protected endpoints with auth (should work)
|
||||
echo -e "\n4️⃣ Testing authenticated request:"
|
||||
curl -s -w "Status: %{http_code}\n" \
|
||||
-H "X-API-Key: $HUGO_API_KEY" \
|
||||
"$BASE_URL/v1/auth/status" | jq '.'
|
||||
|
||||
# Test 5: Rate limiting headers
|
||||
echo -e "\n5️⃣ Testing rate limit headers:"
|
||||
curl -s -i -H "X-API-Key: $HUGO_API_KEY" "$BASE_URL/v1/auth/status" | grep -E "X-RateLimit|HTTP"
|
||||
|
||||
# Cleanup
|
||||
echo -e "\n🧹 Cleanup:"
|
||||
kill $SERVER_PID 2>/dev/null
|
||||
wait $SERVER_PID 2>/dev/null
|
||||
echo "Server stopped"
|
||||
|
||||
echo -e "\n✅ Modular Architecture Test Complete!"
|
||||
echo "Expected behavior:"
|
||||
echo "- Test 1: ✅ Server starts without module errors"
|
||||
echo "- Test 2: ✅ Health endpoint works, shows features"
|
||||
echo "- Test 3: ❌ 401 Unauthorized (missing API key)"
|
||||
echo "- Test 4: ✅ 200 OK with auth details"
|
||||
echo "- Test 5: ✅ Rate limit headers present"
|
||||
|
||||
132
scripts/test_smtp.sh
Normal file
132
scripts/test_smtp.sh
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#!/bin/bash
|
||||
# furt-lua/scripts/test_smtp.sh
|
||||
# Test SMTP mail functionality
|
||||
|
||||
SERVER_URL="http://127.0.0.1:8080"
|
||||
|
||||
echo "Testing Furt SMTP Mail Functionality"
|
||||
echo "========================================"
|
||||
|
||||
# Test 1: Server Health Check
|
||||
echo ""
|
||||
echo "[1] Testing Health Check..."
|
||||
health_response=$(curl -s "$SERVER_URL/health")
|
||||
echo "Response: $health_response"
|
||||
|
||||
# Check if server is responding
|
||||
if echo "$health_response" | grep -q "healthy"; then
|
||||
echo "[OK] Server is healthy"
|
||||
else
|
||||
echo "[ERROR] Server not responding or unhealthy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: Invalid Mail Request (missing fields)
|
||||
echo ""
|
||||
echo "[2] Testing validation (missing fields)..."
|
||||
invalid_response=$(curl -s -X POST "$SERVER_URL/v1/mail/send" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"Test"}')
|
||||
echo "Response: $invalid_response"
|
||||
|
||||
# Check for validation error
|
||||
if echo "$invalid_response" | grep -q "Missing required fields"; then
|
||||
echo "[OK] Validation working correctly"
|
||||
else
|
||||
echo "[ERROR] Validation failed"
|
||||
fi
|
||||
|
||||
# Test 3: Invalid Email Format
|
||||
echo ""
|
||||
echo "[3] Testing email validation..."
|
||||
email_validation_response=$(curl -s -X POST "$SERVER_URL/v1/mail/send" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"Test","email":"invalid-email","message":"Test"}')
|
||||
echo "Response: $email_validation_response"
|
||||
|
||||
# Check for email validation error
|
||||
if echo "$email_validation_response" | grep -q "error"; then
|
||||
echo "[OK] Email validation working"
|
||||
else
|
||||
echo "[ERROR] Email validation failed"
|
||||
fi
|
||||
|
||||
# Test 4: Valid Mail Request (REAL SMTP TEST)
|
||||
echo ""
|
||||
echo "[4] Testing REAL mail sending..."
|
||||
echo "WARNING: This will send a real email to michael@dragons-at-work.de"
|
||||
read -p "Continue with real mail test? (y/N): " -n 1 -r
|
||||
echo
|
||||
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Sending real test email..."
|
||||
|
||||
mail_response=$(curl -s -X POST "$SERVER_URL/v1/mail/send" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Furt Test User",
|
||||
"email": "test@dragons-at-work.de",
|
||||
"subject": "Furt SMTP Test - Week 2 Success!",
|
||||
"message": "This is a test email from the Furt Lua HTTP-Server.\n\nSMTP Integration is working!\n\nTimestamp: '$(date)'\nServer: furt-lua v1.0"
|
||||
}')
|
||||
|
||||
echo "Response: $mail_response"
|
||||
|
||||
# Check for success
|
||||
if echo "$mail_response" | grep -q '"success":true'; then
|
||||
echo "[OK] MAIL SENT SUCCESSFULLY!"
|
||||
echo "Check michael@dragons-at-work.de inbox"
|
||||
|
||||
# Extract request ID
|
||||
request_id=$(echo "$mail_response" | grep -o '"request_id":"[^"]*"' | cut -d'"' -f4)
|
||||
echo "Request ID: $request_id"
|
||||
else
|
||||
echo "[ERROR] Mail sending failed"
|
||||
echo "Check server logs and SMTP credentials"
|
||||
|
||||
# Show error details
|
||||
if echo "$mail_response" | grep -q "error"; then
|
||||
error_msg=$(echo "$mail_response" | grep -o '"error":"[^"]*"' | cut -d'"' -f4)
|
||||
echo "Error: $error_msg"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Skipping real mail test"
|
||||
fi
|
||||
|
||||
# Test 5: Performance Test
|
||||
echo ""
|
||||
echo "[5] Testing response time..."
|
||||
start_time=$(date +%s%N)
|
||||
perf_response=$(curl -s "$SERVER_URL/health")
|
||||
end_time=$(date +%s%N)
|
||||
|
||||
duration_ms=$(( (end_time - start_time) / 1000000 ))
|
||||
echo "Response time: ${duration_ms}ms"
|
||||
|
||||
if [ $duration_ms -lt 100 ]; then
|
||||
echo "[OK] Response time excellent (< 100ms)"
|
||||
elif [ $duration_ms -lt 500 ]; then
|
||||
echo "[OK] Response time good (< 500ms)"
|
||||
else
|
||||
echo "[WARN] Response time slow (> 500ms)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "SMTP Test Complete!"
|
||||
echo "===================="
|
||||
echo "[OK] Health check working"
|
||||
echo "[OK] Input validation working"
|
||||
echo "[OK] Email format validation working"
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Real mail test executed"
|
||||
fi
|
||||
echo "Performance: ${duration_ms}ms"
|
||||
|
||||
echo ""
|
||||
echo "Week 2 Challenge Status:"
|
||||
echo " SMTP Integration: COMPLETE"
|
||||
echo " Environment Variables: CHECK .env"
|
||||
echo " Native Lua Implementation: DONE"
|
||||
echo " Production Ready: READY FOR TESTING"
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue