···46 Ok(auth) => auth,
47 Err(e) => {
48 let error_str = e.to_string();
49- if error_str.contains("401") || error_str.contains("Unauthorized") {
50- tracing::debug!("AIP session exchange returned 401 - session is stale");
000051 return Ok(false);
52 }
53 return Err(e);
···74 }
75 Err(e) => {
76 let error_str = e.to_string();
77- if error_str.contains("401") || error_str.contains("Unauthorized") {
78- tracing::debug!("PDS getServiceAuth returned 401 - session is stale");
000079 Ok(false)
80 } else {
81 Err(anyhow::anyhow!(
···46 Ok(auth) => auth,
47 Err(e) => {
48 let error_str = e.to_string();
49+ if error_str.contains("401")
50+ || error_str.contains("Unauthorized")
51+ || error_str.contains("invalid_token")
52+ || error_str.contains("expired")
53+ {
54+ tracing::debug!("AIP session exchange returned stale token - session is stale");
55 return Ok(false);
56 }
57 return Err(e);
···78 }
79 Err(e) => {
80 let error_str = e.to_string();
81+ if error_str.contains("401")
82+ || error_str.contains("Unauthorized")
83+ || error_str.contains("invalid_token")
84+ || error_str.contains("expired")
85+ {
86+ tracing::debug!("PDS getServiceAuth returned stale token - session is stale");
87 Ok(false)
88 } else {
89 Err(anyhow::anyhow!(
+11-4
src/http/errors/web_error.rs
···163 ///
164 /// This error occurs when an AT Protocol operation is attempted with a stale
165 /// AIP session token. The user should be redirected to the login page.
0166 ///
167 /// **Error Code:** `error-smokesignal-web-3`
168- #[error("error-smokesignal-web-3 Session expired, re-authentication required")]
169- SessionStale,
170171 /// An internal server error occurred.
172 ///
···187 fn into_response(self) -> Response {
188 match self {
189 WebError::MiddlewareAuthError(err) => err.into_response(),
190- WebError::SessionStale => {
191 // For HTMX requests, use HX-Redirect to force a full page navigation to login
192 // For regular requests, this will also work as the browser follows the redirect
000000193 (
194 StatusCode::UNAUTHORIZED,
195- [("HX-Redirect", "/oauth/login?reason=session_expired")],
196 "Session expired. Please log in again.",
197 )
198 .into_response()
···163 ///
164 /// This error occurs when an AT Protocol operation is attempted with a stale
165 /// AIP session token. The user should be redirected to the login page.
166+ /// The contained string is the destination URL to redirect to after re-authentication.
167 ///
168 /// **Error Code:** `error-smokesignal-web-3`
169+ #[error("error-smokesignal-web-3 Session expired, re-authentication required (destination: {0})")]
170+ SessionStale(String),
171172 /// An internal server error occurred.
173 ///
···188 fn into_response(self) -> Response {
189 match self {
190 WebError::MiddlewareAuthError(err) => err.into_response(),
191+ WebError::SessionStale(destination) => {
192 // For HTMX requests, use HX-Redirect to force a full page navigation to login
193 // For regular requests, this will also work as the browser follows the redirect
194+ // Include the destination so users return to where they were after re-authenticating
195+ let encoded_destination = urlencoding::encode(&destination);
196+ let redirect_url = format!(
197+ "/oauth/login?reason=session_expired&destination={}",
198+ encoded_destination
199+ );
200 (
201 StatusCode::UNAUTHORIZED,
202+ [("HX-Redirect", redirect_url)],
203 "Session expired. Please log in again.",
204 )
205 .into_response()
+1-1
src/http/handle_accept_rsvp.rs
···115116 // Check AIP session validity before attempting AT Protocol operation
117 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
118- return Err(WebError::SessionStale);
119 }
120121 // Create DPoP auth based on OAuth backend type
···115116 // Check AIP session validity before attempting AT Protocol operation
117 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
118+ return Err(WebError::SessionStale("/".to_string()));
119 }
120121 // Create DPoP auth based on OAuth backend type
+5-5
src/http/handle_blob.rs
···328329 // Check AIP session validity before attempting AT Protocol operation
330 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
331- return Err(WebError::SessionStale);
332 }
333334 let dpop_auth = match (&auth, &web_context.config.oauth_backend) {
···503504 // Check AIP session validity before attempting AT Protocol operation
505 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
506- return Err(WebError::SessionStale);
507 }
508509 let dpop_auth = match (&auth, &web_context.config.oauth_backend) {
···619620 // Check AIP session validity before attempting AT Protocol operation
621 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
622- return Err(WebError::SessionStale);
623 }
624625 let dpop_auth = match (&auth, &web_context.config.oauth_backend) {
···734735 // Check AIP session validity before attempting AT Protocol operation
736 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
737- return Err(WebError::SessionStale);
738 }
739740 // Create DPoP authentication based on backend type
···942943 // Check AIP session validity before attempting AT Protocol operation
944 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
945- return Err(WebError::SessionStale);
946 }
947948 // Create DPoP authentication based on backend type
···328329 // Check AIP session validity before attempting AT Protocol operation
330 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
331+ return Err(WebError::SessionStale("/settings".to_string()));
332 }
333334 let dpop_auth = match (&auth, &web_context.config.oauth_backend) {
···503504 // Check AIP session validity before attempting AT Protocol operation
505 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
506+ return Err(WebError::SessionStale("/settings".to_string()));
507 }
508509 let dpop_auth = match (&auth, &web_context.config.oauth_backend) {
···619620 // Check AIP session validity before attempting AT Protocol operation
621 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
622+ return Err(WebError::SessionStale("/settings".to_string()));
623 }
624625 let dpop_auth = match (&auth, &web_context.config.oauth_backend) {
···734735 // Check AIP session validity before attempting AT Protocol operation
736 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
737+ return Err(WebError::SessionStale("/event".to_string()));
738 }
739740 // Create DPoP authentication based on backend type
···942943 // Check AIP session validity before attempting AT Protocol operation
944 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
945+ return Err(WebError::SessionStale("/event".to_string()));
946 }
947948 // Create DPoP authentication based on backend type
+2-2
src/http/handle_create_event.rs
···133134 // Check AIP session validity
135 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
136- return Err(WebError::SessionStale);
137 }
138139 let is_development = cfg!(debug_assertions);
···265266 // Check AIP session validity before attempting AT Protocol operation
267 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
268- return Err(WebError::SessionStale);
269 }
270271 // Create DPoP auth
···133134 // Check AIP session validity
135 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
136+ return Err(WebError::SessionStale("/event".to_string()));
137 }
138139 let is_development = cfg!(debug_assertions);
···265266 // Check AIP session validity before attempting AT Protocol operation
267 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
268+ return Err(WebError::SessionStale("/event".to_string()));
269 }
270271 // Create DPoP auth
+2-2
src/http/handle_create_rsvp.rs
···47 // Check AIP session validity before displaying RSVP form
48 // This prevents users from filling out forms with stale sessions
49 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
50- return Err(WebError::SessionStale);
51 }
5253 let default_context = template_context! {
···151152 // Check AIP session validity before attempting AT Protocol operation
153 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
154- return Err(WebError::SessionStale);
155 }
156157 // Create DPoP auth based on OAuth backend type
···47 // Check AIP session validity before displaying RSVP form
48 // This prevents users from filling out forms with stale sessions
49 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
50+ return Err(WebError::SessionStale("/rsvp".to_string()));
51 }
5253 let default_context = template_context! {
···151152 // Check AIP session validity before attempting AT Protocol operation
153 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
154+ return Err(WebError::SessionStale("/rsvp".to_string()));
155 }
156157 // Create DPoP auth based on OAuth backend type
+1-1
src/http/handle_delete_event.rs
···83 if form.confirm.as_deref() == Some("true") {
84 // Check AIP session validity before attempting AT Protocol operation
85 if let AipSessionStatus::Stale = require_valid_aip_session(&ctx.web_context, &ctx.auth).await? {
86- return Err(WebError::SessionStale);
87 }
8889 // Create DPoP authentication based on auth type
···83 if form.confirm.as_deref() == Some("true") {
84 // Check AIP session validity before attempting AT Protocol operation
85 if let AipSessionStatus::Stale = require_valid_aip_session(&ctx.web_context, &ctx.auth).await? {
86+ return Err(WebError::SessionStale("/".to_string()));
87 }
8889 // Create DPoP authentication based on auth type
+1-1
src/http/handle_edit_event.rs
···111112 // Check AIP session validity before attempting AT Protocol operation
113 if let AipSessionStatus::Stale = require_valid_aip_session(&ctx.web_context, &ctx.auth).await? {
114- return Err(WebError::SessionStale);
115 }
116117 // Create DPoP auth
···111112 // Check AIP session validity before attempting AT Protocol operation
113 if let AipSessionStatus::Stale = require_valid_aip_session(&ctx.web_context, &ctx.auth).await? {
114+ return Err(WebError::SessionStale("/event".to_string()));
115 }
116117 // Create DPoP auth
+1-1
src/http/handle_finalize_acceptance.rs
···237238 // Check AIP session validity before attempting AT Protocol operation
239 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
240- return Err(WebError::SessionStale);
241 }
242243 // Create DPoP auth based on OAuth backend type
···237238 // Check AIP session validity before attempting AT Protocol operation
239 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
240+ return Err(WebError::SessionStale("/".to_string()));
241 }
242243 // Create DPoP auth based on OAuth backend type
+2-2
src/http/handle_lfg.rs
···490491 // Check AIP session validity before attempting AT Protocol operation
492 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
493- return Err(WebError::SessionStale);
494 }
495496 // Validate the request
···641642 // Check AIP session validity
643 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
644- return Err(WebError::SessionStale);
645 }
646647 // Get the active LFG record
···490491 // Check AIP session validity before attempting AT Protocol operation
492 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
493+ return Err(WebError::SessionStale("/lfg".to_string()));
494 }
495496 // Validate the request
···641642 // Check AIP session validity
643 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
644+ return Err(WebError::SessionStale("/lfg".to_string()));
645 }
646647 // Get the active LFG record
+1-1
src/http/handle_quick_event.rs
···25 // Check AIP session validity before displaying quick event form
26 // This prevents users from filling out forms with stale sessions
27 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
28- return Err(WebError::SessionStale);
29 }
3031 let render_template = select_template!("quick_event", hx_boosted, false, language);
···25 // Check AIP session validity before displaying quick event form
26 // This prevents users from filling out forms with stale sessions
27 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
28+ return Err(WebError::SessionStale("/quick-event".to_string()));
29 }
3031 let render_template = select_template!("quick_event", hx_boosted, false, language);
+2-2
src/http/handle_settings.rs
···83 // Check AIP session validity before displaying settings form
84 // This prevents users from filling out forms with stale sessions
85 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
86- return Err(WebError::SessionStale);
87 }
8889 let default_context = template_context! {
···521522 // Check AIP session validity before attempting AT Protocol operation
523 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
524- return Err(WebError::SessionStale);
525 }
526527 // Create DPoP authentication based on backend type
···83 // Check AIP session validity before displaying settings form
84 // This prevents users from filling out forms with stale sessions
85 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
86+ return Err(WebError::SessionStale("/settings".to_string()));
87 }
8889 let default_context = template_context! {
···521522 // Check AIP session validity before attempting AT Protocol operation
523 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
524+ return Err(WebError::SessionStale("/settings".to_string()));
525 }
526527 // Create DPoP authentication based on backend type
+1-1
src/http/handle_unaccept_rsvp.rs
···8081 // Check AIP session validity before attempting AT Protocol operation
82 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
83- return Err(WebError::SessionStale);
84 }
8586 // Create DPoP auth based on OAuth backend type
···8081 // Check AIP session validity before attempting AT Protocol operation
82 if let AipSessionStatus::Stale = require_valid_aip_session(&web_context, &auth).await? {
83+ return Err(WebError::SessionStale("/".to_string()));
84 }
8586 // Create DPoP auth based on OAuth backend type
···1+const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["js/event-map-BMN8EESL.js","js/main-CuQd5Sql.js","css/main-DsS_JPFh.css","js/globe-map-v0wYAEFW.js","js/location-heatmap-qsLOB3hq.js"])))=>i.map(i=>d[i]);
2+import{_ as i}from"./main-CuQd5Sql.js";async function o(){if(!document.getElementById("event-map"))return;const{initEventMap:t}=await i(async()=>{const{initEventMap:n}=await import("./event-map-BMN8EESL.js");return{initEventMap:n}},__vite__mapDeps([0,1,2]));await t()}async function e(){if(!document.getElementById("globe-map"))return;const{initGlobeMap:t}=await i(async()=>{const{initGlobeMap:n}=await import("./globe-map-v0wYAEFW.js");return{initGlobeMap:n}},__vite__mapDeps([3,1,2]));await t()}async function c(){if(!document.getElementById("location-heatmap"))return;const{initLocationHeatmap:t}=await i(async()=>{const{initLocationHeatmap:n}=await import("./location-heatmap-qsLOB3hq.js");return{initLocationHeatmap:n}},__vite__mapDeps([4,1,2]));await t()}function p(){o(),e(),c()}export{o as initEventMap,e as initGlobeMap,c as initLocationHeatmap,p as initMaps};
+2
static/js/index-Bcnf8oUZ.js
···00
···1+const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["js/cropper-DyNc_82c.js","css/cropper-BJgXXBRK.css"])))=>i.map(i=>d[i]);
2+import{_ as a}from"./main-CuQd5Sql.js";let t=null;async function o(){return t||(t=(async()=>{const[n]=await Promise.all([a(()=>import("./cropper-DyNc_82c.js").then(r=>r.c),__vite__mapDeps([0,1])),a(()=>import("./cropper-DyNc_82c.js").then(r=>r.a),__vite__mapDeps([0,1]))]),e=n.default;return window.Cropper=e,e})(),t)}async function s(){const n=document.getElementById("headerCanvas"),e=document.getElementById("thumbnailCanvas");!n&&!e||await o()}export{s as initCropper,o as loadCropper};