WebConfigExample.vala 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. using Astralis;
  2. using Invercargill;
  3. using Invercargill.DataStructures;
  4. using Inversion;
  5. /**
  6. * WebConfig Example
  7. *
  8. * Demonstrates the WebConfig configuration system:
  9. * - Accessing configuration values from DI-injected WebConfig
  10. * - Using different typed getters (string, int, bool, arrays)
  11. * - Accessing nested sections
  12. * - Handling missing keys with defaults
  13. *
  14. * To run with different environments:
  15. * ./web-config-example # Uses web-config.json
  16. * ASTRALIS_ENV=staging ./web-config-example # Uses web-config.json + web-config.staging.json
  17. */
  18. // Root endpoint - shows all configuration
  19. class ConfigRootEndpoint : Object, Endpoint {
  20. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  21. var config = inject<WebConfig>();
  22. var json_parts = new Series<string>();
  23. json_parts.add_start("{\n");
  24. json_parts.add_start(" \"message\": \"WebConfig Example API\",\n");
  25. json_parts.add_start(" \"endpoints\": {\n");
  26. json_parts.add_start(" \"config\": \"/config\",\n");
  27. json_parts.add_start(" \"database\": \"/config/database\",\n");
  28. json_parts.add_start(" \"origins\": \"/config/origins\",\n");
  29. json_parts.add_start(" \"debug\": \"/config/debug\",\n");
  30. json_parts.add_start(" \"sources\": \"/config/sources\"\n");
  31. json_parts.add_start(" }\n");
  32. json_parts.add_start("}");
  33. var json_string = json_parts.to_immutable_buffer()
  34. .aggregate<string>("", (acc, s) => acc + s);
  35. return new HttpStringResult(json_string)
  36. .set_header("Content-Type", "application/json")
  37. .set_header("Access-Control-Allow-Origin", "*");
  38. }
  39. }
  40. // Shows full configuration details
  41. class ConfigEndpoint : Object, Endpoint {
  42. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  43. var config = inject<WebConfig>();
  44. // Get values with defaults for missing keys
  45. var port = config.get_int("port", 8080);
  46. var app_name = config.get_string("app_name", "Unknown App");
  47. var debug = config.get_bool("debug", false);
  48. // Build JSON response
  49. var json_parts = new Series<string>();
  50. json_parts.add_start("{\n");
  51. json_parts.add_start(@" \"port\": $port,\n");
  52. json_parts.add_start(@" \"app_name\": \"$app_name\",\n");
  53. json_parts.add_start(@" \"debug\": $debug\n");
  54. json_parts.add_start("}");
  55. var json_string = json_parts.to_immutable_buffer()
  56. .aggregate<string>("", (acc, s) => acc + s);
  57. return new HttpStringResult(json_string)
  58. .set_header("Content-Type", "application/json")
  59. .set_header("Access-Control-Allow-Origin", "*");
  60. }
  61. }
  62. // Demonstrates accessing nested configuration sections
  63. class DatabaseConfigEndpoint : Object, Endpoint {
  64. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  65. var config = inject<WebConfig>();
  66. // Get the database section
  67. var db_section = config.get_section("database");
  68. string db_host;
  69. int db_port;
  70. string db_name;
  71. if (db_section != null) {
  72. // Access values from the nested section
  73. db_host = db_section.get_string("host", "localhost");
  74. db_port = db_section.get_int("port", 5432);
  75. db_name = db_section.get_string("name", "app_db");
  76. } else {
  77. // Section not configured - use defaults
  78. db_host = "not configured";
  79. db_port = 0;
  80. db_name = "not configured";
  81. }
  82. // Build JSON response
  83. var json = @"{
  84. \"database\": {
  85. \"host\": \"$db_host\",
  86. \"port\": $db_port,
  87. \"name\": \"$db_name\"
  88. }
  89. }";
  90. return new HttpStringResult(json)
  91. .set_header("Content-Type", "application/json")
  92. .set_header("Access-Control-Allow-Origin", "*");
  93. }
  94. }
  95. // Demonstrates accessing string arrays
  96. class OriginsConfigEndpoint : Object, Endpoint {
  97. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  98. var config = inject<WebConfig>();
  99. // Get string array with default empty array
  100. var origins = config.get_string_array("allowed_origins", {});
  101. // Build JSON array
  102. var origins_parts = new Series<string>();
  103. origins_parts.add_start("[");
  104. bool first = true;
  105. foreach (var origin in origins) {
  106. if (!first) origins_parts.add_start(", ");
  107. origins_parts.add_start(@"\"$origin\"");
  108. first = false;
  109. }
  110. origins_parts.add("]");
  111. var origins_json = origins_parts.to_immutable_buffer()
  112. .aggregate<string>("", (acc, s) => acc + s);
  113. var json = @"{
  114. \"allowed_origins\": $origins_json,
  115. \"count\": $(origins.length)
  116. }";
  117. return new HttpStringResult(json)
  118. .set_header("Content-Type", "application/json")
  119. .set_header("Access-Control-Allow-Origin", "*");
  120. }
  121. }
  122. // Demonstrates boolean access and has_key checking
  123. class DebugConfigEndpoint : Object, Endpoint {
  124. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  125. var config = inject<WebConfig>();
  126. // Check if key exists before accessing
  127. var has_debug = config.has_key("debug");
  128. var debug = config.get_bool("debug", false);
  129. // Also check for other optional keys
  130. var has_log_level = config.has_key("log_level");
  131. var log_level = config.get_string("log_level", "info");
  132. var json = @"{
  133. \"debug\": {
  134. \"configured\": $has_debug,
  135. \"value\": $debug
  136. },
  137. \"log_level\": {
  138. \"configured\": $has_log_level,
  139. \"value\": \"$log_level\"
  140. }
  141. }";
  142. return new HttpStringResult(json)
  143. .set_header("Content-Type", "application/json")
  144. .set_header("Access-Control-Allow-Origin", "*");
  145. }
  146. }
  147. // Shows which configuration files were loaded (demonstrates layering)
  148. class ConfigSourcesEndpoint : Object, Endpoint {
  149. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  150. var config = inject<WebConfig>();
  151. // Get list of loaded files
  152. var loaded_files = config.get_loaded_files();
  153. var is_loaded = config.is_loaded();
  154. // Build JSON array of sources
  155. var sources_parts = new Series<string>();
  156. sources_parts.add_start("[");
  157. bool first = true;
  158. foreach (var file in loaded_files) {
  159. if (!first) sources_parts.add_start(", ");
  160. sources_parts.add_start(@"\"$file\"");
  161. first = false;
  162. }
  163. sources_parts.add("]");
  164. var sources_json = sources_parts.to_immutable_buffer()
  165. .aggregate<string>("", (acc, s) => acc + s);
  166. var json = @"{
  167. \"loaded\": $is_loaded,
  168. \"sources\": $sources_json,
  169. \"to_string\": \"$(config.to_string().replace("\"", "\\\""))\"
  170. }";
  171. return new HttpStringResult(json)
  172. .set_header("Content-Type", "application/json")
  173. .set_header("Access-Control-Allow-Origin", "*");
  174. }
  175. }
  176. // Demonstrates accessing all keys and iterating configuration
  177. class ConfigKeysEndpoint : Object, Endpoint {
  178. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  179. var config = inject<WebConfig>();
  180. // Get all root-level keys
  181. var keys = config.get_keys();
  182. // Build JSON array of keys
  183. var keys_parts = new Series<string>();
  184. keys_parts.add_start("[");
  185. bool first = true;
  186. foreach (var key in keys) {
  187. if (!first) keys_parts.add_start(", ");
  188. keys_parts.add_start(@"\"$key\"");
  189. first = false;
  190. }
  191. keys_parts.add("]");
  192. var keys_json = keys_parts.to_immutable_buffer()
  193. .aggregate<string>("", (acc, s) => acc + s);
  194. var json = @"{
  195. \"keys\": $keys_json,
  196. \"count\": $(keys.length())
  197. }";
  198. return new HttpStringResult(json)
  199. .set_header("Content-Type", "application/json")
  200. .set_header("Access-Control-Allow-Origin", "*");
  201. }
  202. }
  203. void main() {
  204. var application = new WebApplication();
  205. // Register endpoints
  206. application.container.register_scoped<Endpoint>(() => new ConfigRootEndpoint())
  207. .with_metadata<EndpointRoute>(new EndpointRoute("/"))
  208. .as<Endpoint>();
  209. application.container.register_scoped<Endpoint>(() => new ConfigEndpoint())
  210. .with_metadata<EndpointRoute>(new EndpointRoute("/config"))
  211. .as<Endpoint>();
  212. application.container.register_scoped<Endpoint>(() => new DatabaseConfigEndpoint())
  213. .with_metadata<EndpointRoute>(new EndpointRoute("/config/database"))
  214. .as<Endpoint>();
  215. application.container.register_scoped<Endpoint>(() => new OriginsConfigEndpoint())
  216. .with_metadata<EndpointRoute>(new EndpointRoute("/config/origins"))
  217. .as<Endpoint>();
  218. application.container.register_scoped<Endpoint>(() => new DebugConfigEndpoint())
  219. .with_metadata<EndpointRoute>(new EndpointRoute("/config/debug"))
  220. .as<Endpoint>();
  221. application.container.register_scoped<Endpoint>(() => new ConfigSourcesEndpoint())
  222. .with_metadata<EndpointRoute>(new EndpointRoute("/config/sources"))
  223. .as<Endpoint>();
  224. application.container.register_scoped<Endpoint>(() => new ConfigKeysEndpoint())
  225. .with_metadata<EndpointRoute>(new EndpointRoute("/config/keys"))
  226. .as<Endpoint>();
  227. print("WebConfig Example Server starting...\n");
  228. print("Configuration is loaded from web-config.json (and web-config.{ENV}.json if ASTRALIS_ENV is set)\n");
  229. print("\nTry these endpoints:\n");
  230. print(" - http://localhost:3000/\n");
  231. print(" - http://localhost:3000/config\n");
  232. print(" - http://localhost:3000/config/database\n");
  233. print(" - http://localhost:3000/config/origins\n");
  234. print(" - http://localhost:3000/config/debug\n");
  235. print(" - http://localhost:3000/config/sources\n");
  236. print(" - http://localhost:3000/config/keys\n");
  237. print("\nTo test configuration layering:\n");
  238. print(" ASTRALIS_ENV=staging ./web-config-example\n");
  239. print(" Then check /config/sources to see both files loaded\n");
  240. application.run();
  241. }