using Astralis; using Invercargill; using Invercargill.DataStructures; using Inversion; /** * WebConfig Example * * Demonstrates the WebConfig configuration system: * - Accessing configuration values from DI-injected WebConfig * - Using different typed getters (string, int, bool, arrays) * - Accessing nested sections * - Handling missing keys with defaults * * To run with different environments: * ./web-config-example # Uses web-config.json * ASTRALIS_ENV=staging ./web-config-example # Uses web-config.json + web-config.staging.json */ // Root endpoint - shows all configuration class ConfigRootEndpoint : Object, Endpoint { public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error { var config = inject(); var json_parts = new Series(); json_parts.add_start("{\n"); json_parts.add_start(" \"message\": \"WebConfig Example API\",\n"); json_parts.add_start(" \"endpoints\": {\n"); json_parts.add_start(" \"config\": \"/config\",\n"); json_parts.add_start(" \"database\": \"/config/database\",\n"); json_parts.add_start(" \"origins\": \"/config/origins\",\n"); json_parts.add_start(" \"debug\": \"/config/debug\",\n"); json_parts.add_start(" \"sources\": \"/config/sources\"\n"); json_parts.add_start(" }\n"); json_parts.add_start("}"); var json_string = json_parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); return new HttpStringResult(json_string) .set_header("Content-Type", "application/json") .set_header("Access-Control-Allow-Origin", "*"); } } // Shows full configuration details class ConfigEndpoint : Object, Endpoint { public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error { var config = inject(); // Get values with defaults for missing keys var port = config.get_int("port", 8080); var app_name = config.get_string("app_name", "Unknown App"); var debug = config.get_bool("debug", false); // Build JSON response var json_parts = new Series(); json_parts.add_start("{\n"); json_parts.add_start(@" \"port\": $port,\n"); json_parts.add_start(@" \"app_name\": \"$app_name\",\n"); json_parts.add_start(@" \"debug\": $debug\n"); json_parts.add_start("}"); var json_string = json_parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); return new HttpStringResult(json_string) .set_header("Content-Type", "application/json") .set_header("Access-Control-Allow-Origin", "*"); } } // Demonstrates accessing nested configuration sections class DatabaseConfigEndpoint : Object, Endpoint { public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error { var config = inject(); // Get the database section var db_section = config.get_section("database"); string db_host; int db_port; string db_name; if (db_section != null) { // Access values from the nested section db_host = db_section.get_string("host", "localhost"); db_port = db_section.get_int("port", 5432); db_name = db_section.get_string("name", "app_db"); } else { // Section not configured - use defaults db_host = "not configured"; db_port = 0; db_name = "not configured"; } // Build JSON response var json = @"{ \"database\": { \"host\": \"$db_host\", \"port\": $db_port, \"name\": \"$db_name\" } }"; return new HttpStringResult(json) .set_header("Content-Type", "application/json") .set_header("Access-Control-Allow-Origin", "*"); } } // Demonstrates accessing string arrays class OriginsConfigEndpoint : Object, Endpoint { public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error { var config = inject(); // Get string array with default empty array var origins = config.get_string_array("allowed_origins", {}); // Build JSON array var origins_parts = new Series(); origins_parts.add_start("["); bool first = true; foreach (var origin in origins) { if (!first) origins_parts.add_start(", "); origins_parts.add_start(@"\"$origin\""); first = false; } origins_parts.add("]"); var origins_json = origins_parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); var json = @"{ \"allowed_origins\": $origins_json, \"count\": $(origins.length) }"; return new HttpStringResult(json) .set_header("Content-Type", "application/json") .set_header("Access-Control-Allow-Origin", "*"); } } // Demonstrates boolean access and has_key checking class DebugConfigEndpoint : Object, Endpoint { public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error { var config = inject(); // Check if key exists before accessing var has_debug = config.has_key("debug"); var debug = config.get_bool("debug", false); // Also check for other optional keys var has_log_level = config.has_key("log_level"); var log_level = config.get_string("log_level", "info"); var json = @"{ \"debug\": { \"configured\": $has_debug, \"value\": $debug }, \"log_level\": { \"configured\": $has_log_level, \"value\": \"$log_level\" } }"; return new HttpStringResult(json) .set_header("Content-Type", "application/json") .set_header("Access-Control-Allow-Origin", "*"); } } // Shows which configuration files were loaded (demonstrates layering) class ConfigSourcesEndpoint : Object, Endpoint { public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error { var config = inject(); // Get list of loaded files var loaded_files = config.get_loaded_files(); var is_loaded = config.is_loaded(); // Build JSON array of sources var sources_parts = new Series(); sources_parts.add_start("["); bool first = true; foreach (var file in loaded_files) { if (!first) sources_parts.add_start(", "); sources_parts.add_start(@"\"$file\""); first = false; } sources_parts.add("]"); var sources_json = sources_parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); var json = @"{ \"loaded\": $is_loaded, \"sources\": $sources_json, \"to_string\": \"$(config.to_string().replace("\"", "\\\""))\" }"; return new HttpStringResult(json) .set_header("Content-Type", "application/json") .set_header("Access-Control-Allow-Origin", "*"); } } // Demonstrates accessing all keys and iterating configuration class ConfigKeysEndpoint : Object, Endpoint { public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error { var config = inject(); // Get all root-level keys var keys = config.get_keys(); // Build JSON array of keys var keys_parts = new Series(); keys_parts.add_start("["); bool first = true; foreach (var key in keys) { if (!first) keys_parts.add_start(", "); keys_parts.add_start(@"\"$key\""); first = false; } keys_parts.add("]"); var keys_json = keys_parts.to_immutable_buffer() .aggregate("", (acc, s) => acc + s); var json = @"{ \"keys\": $keys_json, \"count\": $(keys.length()) }"; return new HttpStringResult(json) .set_header("Content-Type", "application/json") .set_header("Access-Control-Allow-Origin", "*"); } } void main() { var application = new WebApplication(); // Register endpoints application.container.register_scoped(() => new ConfigRootEndpoint()) .with_metadata(new EndpointRoute("/")) .as(); application.container.register_scoped(() => new ConfigEndpoint()) .with_metadata(new EndpointRoute("/config")) .as(); application.container.register_scoped(() => new DatabaseConfigEndpoint()) .with_metadata(new EndpointRoute("/config/database")) .as(); application.container.register_scoped(() => new OriginsConfigEndpoint()) .with_metadata(new EndpointRoute("/config/origins")) .as(); application.container.register_scoped(() => new DebugConfigEndpoint()) .with_metadata(new EndpointRoute("/config/debug")) .as(); application.container.register_scoped(() => new ConfigSourcesEndpoint()) .with_metadata(new EndpointRoute("/config/sources")) .as(); application.container.register_scoped(() => new ConfigKeysEndpoint()) .with_metadata(new EndpointRoute("/config/keys")) .as(); print("WebConfig Example Server starting...\n"); print("Configuration is loaded from web-config.json (and web-config.{ENV}.json if ASTRALIS_ENV is set)\n"); print("\nTry these endpoints:\n"); print(" - http://localhost:3000/\n"); print(" - http://localhost:3000/config\n"); print(" - http://localhost:3000/config/database\n"); print(" - http://localhost:3000/config/origins\n"); print(" - http://localhost:3000/config/debug\n"); print(" - http://localhost:3000/config/sources\n"); print(" - http://localhost:3000/config/keys\n"); print("\nTo test configuration layering:\n"); print(" ASTRALIS_ENV=staging ./web-config-example\n"); print(" Then check /config/sources to see both files loaded\n"); application.run(); }