Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql

refactor: remove dead settings handler

-400
-400
server/src/handlers/settings.gleam
··· 1 - import admin_session as session 2 - import database/repositories/actors 3 - import database/repositories/config as config_repo 4 - import database/repositories/lexicons 5 - import database/repositories/records 6 - import gleam/erlang/process 7 - import gleam/http as gleam_http 8 - import gleam/int 9 - import gleam/list 10 - import gleam/option 11 - import gleam/string 12 - import importer 13 - import jetstream_consumer 14 - import lib/oauth/did_cache 15 - import logging 16 - import simplifile 17 - import sqlight 18 - import wisp 19 - import wisp_flash 20 - import zip_helper 21 - 22 - pub type Context { 23 - Context( 24 - db: sqlight.Connection, 25 - jetstream_consumer: option.Option( 26 - process.Subject(jetstream_consumer.ManagerMessage), 27 - ), 28 - did_cache: process.Subject(did_cache.Message), 29 - ) 30 - } 31 - 32 - pub fn handle(req: wisp.Request, ctx: Context) -> wisp.Response { 33 - // Get current user from session 34 - let #(current_user, user_is_admin) = case 35 - session.get_current_user(req, ctx.db, ctx.did_cache) 36 - { 37 - Ok(#(did, handle, _access_token)) -> { 38 - let admin = config_repo.is_admin(ctx.db, did) 39 - #(option.Some(#(did, handle)), admin) 40 - } 41 - Error(_) -> #(option.None, False) 42 - } 43 - 44 - // Require admin access for the entire settings page 45 - case user_is_admin { 46 - False -> { 47 - logging.log( 48 - logging.Warning, 49 - "[settings] Non-admin user attempted to access settings page", 50 - ) 51 - wisp.redirect("/") 52 - } 53 - True -> handle_admin_request(req, ctx, current_user) 54 - } 55 - } 56 - 57 - fn handle_admin_request( 58 - req: wisp.Request, 59 - ctx: Context, 60 - _current_user: option.Option(#(String, String)), 61 - ) -> wisp.Response { 62 - case req.method { 63 - gleam_http.Get -> { 64 - // TODO: Migrate settings page to client SPA 65 - wisp.html_response( 66 - "<h1>Settings</h1><p>Settings page will be migrated to the client SPA</p>", 67 - 200, 68 - ) 69 - } 70 - gleam_http.Post -> { 71 - // Handle form submission (domain authority or lexicons upload or reset) 72 - use form_data <- wisp.require_form(req) 73 - 74 - // Check if this is a reset action 75 - case list.key_find(form_data.values, "action") { 76 - Ok("reset") -> { 77 - // Handle reset action (admin-only, already verified) 78 - handle_reset(req, form_data, ctx) 79 - } 80 - _ -> { 81 - // Check if this is a lexicons upload 82 - case list.key_find(form_data.files, "lexicons_zip") { 83 - Ok(uploaded_file) -> { 84 - // Handle lexicons ZIP upload 85 - handle_lexicons_upload(req, uploaded_file, ctx) 86 - } 87 - Error(_) -> { 88 - // Not a file upload, check for domain_authority field 89 - case list.key_find(form_data.values, "domain_authority") { 90 - Ok(domain_authority) -> { 91 - // Validate domain authority format 92 - case validate_domain_authority(domain_authority) { 93 - Ok(_) -> { 94 - // Save domain_authority to database 95 - case 96 - config_repo.set( 97 - ctx.db, 98 - "domain_authority", 99 - domain_authority, 100 - ) 101 - { 102 - Ok(_) -> { 103 - wisp.redirect("/settings") 104 - |> wisp_flash.set_flash( 105 - req, 106 - "success", 107 - "Domain authority saved successfully", 108 - ) 109 - } 110 - Error(_) -> { 111 - logging.log( 112 - logging.Error, 113 - "[settings] Failed to save domain_authority", 114 - ) 115 - wisp.redirect("/settings") 116 - |> wisp_flash.set_flash( 117 - req, 118 - "error", 119 - "Failed to save domain authority", 120 - ) 121 - } 122 - } 123 - } 124 - Error(error_message) -> { 125 - logging.log( 126 - logging.Warning, 127 - "[settings] Invalid domain authority: " <> error_message, 128 - ) 129 - wisp.redirect("/settings") 130 - |> wisp_flash.set_flash(req, "error", error_message) 131 - } 132 - } 133 - } 134 - Error(_) -> { 135 - logging.log( 136 - logging.Warning, 137 - "[settings] No form data received", 138 - ) 139 - wisp.redirect("/settings") 140 - } 141 - } 142 - } 143 - } 144 - } 145 - } 146 - } 147 - _ -> { 148 - wisp.response(405) 149 - |> wisp.set_header("content-type", "text/html") 150 - |> wisp.set_body(wisp.Text("<h1>Method Not Allowed</h1>")) 151 - } 152 - } 153 - } 154 - 155 - fn handle_lexicons_upload( 156 - req: wisp.Request, 157 - uploaded_file: wisp.UploadedFile, 158 - ctx: Context, 159 - ) -> wisp.Response { 160 - logging.log( 161 - logging.Info, 162 - "[settings] Processing lexicons ZIP upload: " <> uploaded_file.file_name, 163 - ) 164 - 165 - // Create temporary directory for extraction with random suffix 166 - let temp_dir = "tmp/lexicon_upload_" <> wisp.random_string(16) 167 - 168 - case simplifile.create_directory_all(temp_dir) { 169 - Ok(_) -> { 170 - logging.log( 171 - logging.Info, 172 - "[settings] Created temp directory: " <> temp_dir, 173 - ) 174 - 175 - // Extract ZIP file to temp directory 176 - case zip_helper.extract_zip(uploaded_file.path, temp_dir) { 177 - Ok(_) -> { 178 - logging.log( 179 - logging.Info, 180 - "[settings] Extracted ZIP file to: " <> temp_dir, 181 - ) 182 - 183 - // Import lexicons from extracted directory 184 - case importer.import_lexicons_from_directory(temp_dir, ctx.db) { 185 - Ok(stats) -> { 186 - // Clean up temp directory 187 - let _ = simplifile.delete(temp_dir) 188 - 189 - logging.log( 190 - logging.Info, 191 - "[settings] Lexicon import complete: " 192 - <> int.to_string(stats.imported) 193 - <> " imported, " 194 - <> int.to_string(stats.failed) 195 - <> " failed", 196 - ) 197 - 198 - // Log any errors 199 - case stats.errors { 200 - [] -> Nil 201 - errors -> { 202 - list.each(errors, fn(err) { 203 - logging.log( 204 - logging.Warning, 205 - "[settings] Import error: " <> err, 206 - ) 207 - }) 208 - } 209 - } 210 - 211 - // Restart Jetstream consumer to pick up newly imported collections 212 - let restart_status = case ctx.jetstream_consumer { 213 - option.Some(consumer) -> { 214 - logging.log( 215 - logging.Info, 216 - "[settings] Restarting Jetstream consumer with new lexicons...", 217 - ) 218 - case jetstream_consumer.restart(consumer) { 219 - Ok(_) -> { 220 - logging.log( 221 - logging.Info, 222 - "[settings] Jetstream consumer restarted successfully", 223 - ) 224 - "success" 225 - } 226 - Error(err) -> { 227 - logging.log( 228 - logging.Error, 229 - "[settings] Failed to restart Jetstream consumer: " 230 - <> err, 231 - ) 232 - "failed" 233 - } 234 - } 235 - } 236 - option.None -> { 237 - logging.log( 238 - logging.Info, 239 - "[settings] Jetstream consumer not running, skipping restart", 240 - ) 241 - "not_running" 242 - } 243 - } 244 - 245 - // Build success message with import stats and restart status 246 - let base_message = 247 - "Imported " 248 - <> int.to_string(stats.imported) 249 - <> " lexicon(s) successfully" 250 - let message = case restart_status { 251 - "success" -> base_message <> ". Jetstream consumer restarted." 252 - "failed" -> 253 - base_message 254 - <> ". Warning: Jetstream consumer restart failed." 255 - "not_running" -> base_message <> "." 256 - _ -> base_message 257 - } 258 - 259 - let flash_kind = case restart_status { 260 - "failed" -> "warning" 261 - _ -> "success" 262 - } 263 - 264 - wisp.redirect("/settings") 265 - |> wisp_flash.set_flash(req, flash_kind, message) 266 - } 267 - Error(err) -> { 268 - // Clean up temp directory 269 - let _ = simplifile.delete(temp_dir) 270 - 271 - logging.log( 272 - logging.Error, 273 - "[settings] Failed to import lexicons: " <> err, 274 - ) 275 - wisp.redirect("/settings") 276 - |> wisp_flash.set_flash( 277 - req, 278 - "error", 279 - "Failed to import lexicons: " <> err, 280 - ) 281 - } 282 - } 283 - } 284 - Error(err) -> { 285 - // Clean up temp directory 286 - let _ = simplifile.delete(temp_dir) 287 - 288 - logging.log( 289 - logging.Error, 290 - "[settings] Failed to extract ZIP: " <> err, 291 - ) 292 - wisp.redirect("/settings") 293 - |> wisp_flash.set_flash( 294 - req, 295 - "error", 296 - "Failed to extract ZIP file: " <> err, 297 - ) 298 - } 299 - } 300 - } 301 - Error(_) -> { 302 - logging.log(logging.Error, "[settings] Failed to create temp directory") 303 - wisp.redirect("/settings") 304 - |> wisp_flash.set_flash( 305 - req, 306 - "error", 307 - "Failed to create temporary directory for upload", 308 - ) 309 - } 310 - } 311 - } 312 - 313 - /// Validates domain authority format (e.g., "com.example") 314 - fn validate_domain_authority(domain_authority: String) -> Result(Nil, String) { 315 - // Check for http:// or https:// 316 - case 317 - string.contains(domain_authority, "http://") 318 - || string.contains(domain_authority, "https://") 319 - { 320 - True -> 321 - Error( 322 - "Domain authority should not contain http:// or https:// (e.g., com.example)", 323 - ) 324 - False -> { 325 - // Check that it has exactly two parts separated by a dot 326 - let parts = string.split(domain_authority, ".") 327 - case list.length(parts) == 2 { 328 - False -> 329 - Error( 330 - "Domain authority must have exactly two parts separated by a dot (e.g., com.example)", 331 - ) 332 - True -> { 333 - // Check that no parts are empty 334 - case list.all(parts, fn(part) { string.length(part) > 0 }) { 335 - False -> 336 - Error( 337 - "Domain authority parts cannot be empty (e.g., com.example)", 338 - ) 339 - True -> Ok(Nil) 340 - } 341 - } 342 - } 343 - } 344 - } 345 - } 346 - 347 - fn handle_reset( 348 - req: wisp.Request, 349 - form_data: wisp.FormData, 350 - ctx: Context, 351 - ) -> wisp.Response { 352 - // Admin access already verified by page-level check 353 - // Verify confirmation 354 - case list.key_find(form_data.values, "confirm") { 355 - Ok("RESET") -> { 356 - // Delete all data 357 - let domain_result = config_repo.delete_domain_authority(ctx.db) 358 - let lexicons_result = lexicons.delete_all(ctx.db) 359 - let records_result = records.delete_all(ctx.db) 360 - let actors_result = actors.delete_all(ctx.db) 361 - 362 - case domain_result, lexicons_result, records_result, actors_result { 363 - Ok(_), Ok(_), Ok(_), Ok(_) -> { 364 - // Restart Jetstream consumer if it exists 365 - let restart_message = case ctx.jetstream_consumer { 366 - option.Some(consumer) -> { 367 - case jetstream_consumer.restart(consumer) { 368 - Ok(_) -> "All data has been reset successfully" 369 - Error(_) -> 370 - "Data reset completed (Jetstream consumer may need manual restart)" 371 - } 372 - } 373 - option.None -> "All data has been reset successfully" 374 - } 375 - 376 - logging.log(logging.Info, "[settings] System reset completed") 377 - wisp.redirect("/settings") 378 - |> wisp_flash.set_flash(req, "success", restart_message) 379 - } 380 - _, _, _, _ -> { 381 - logging.log(logging.Error, "[settings] Failed to reset some data") 382 - wisp.redirect("/settings") 383 - |> wisp_flash.set_flash(req, "error", "Failed to reset all data") 384 - } 385 - } 386 - } 387 - _ -> { 388 - logging.log( 389 - logging.Warning, 390 - "[settings] Reset attempted without proper confirmation", 391 - ) 392 - wisp.redirect("/settings") 393 - |> wisp_flash.set_flash( 394 - req, 395 - "error", 396 - "Confirmation failed: Please type RESET exactly", 397 - ) 398 - } 399 - } 400 - }