Skip to main content

max / makenotwork

3.5 KB · 123 lines History Blame Raw
1 //! Support ticket submission from the user dashboard.
2
3 use axum::{
4 extract::State,
5 response::{Html, IntoResponse, Response},
6 Form,
7 };
8 use serde::Deserialize;
9
10 use crate::{
11 auth::AuthUser,
12 error::Result,
13 templates::FormStatusTemplate,
14 AppState,
15 };
16
17 /// Form input for submitting a support ticket.
18 #[derive(Debug, Deserialize)]
19 pub struct SupportTicketForm {
20 pub subject: String,
21 pub message: String,
22 pub category: String,
23 }
24
25 /// Submit a support ticket. Creates a WAM ticket and sends a confirmation email.
26 #[tracing::instrument(skip_all, name = "users::submit_support_ticket")]
27 pub(in crate::routes::api) async fn submit_support_ticket(
28 State(state): State<AppState>,
29 AuthUser(user): AuthUser,
30 Form(form): Form<SupportTicketForm>,
31 ) -> Result<Response> {
32 user.check_not_sandbox()?;
33 let subject = form.subject.trim();
34 let message = form.message.trim();
35 let category = form.category.trim();
36
37 if subject.is_empty() || subject.len() > 200 {
38 return Ok(Html(FormStatusTemplate {
39 success: false,
40 message: "Subject must be between 1 and 200 characters.".to_string(),
41 }.render_string()).into_response());
42 }
43
44 if message.is_empty() || message.len() > 5000 {
45 return Ok(Html(FormStatusTemplate {
46 success: false,
47 message: "Message must be between 1 and 5000 characters.".to_string(),
48 }.render_string()).into_response());
49 }
50
51 let valid_categories = ["bug", "billing", "account", "content", "security", "other"];
52 if !valid_categories.contains(&category) {
53 return Ok(Html(FormStatusTemplate {
54 success: false,
55 message: "Please select a valid category.".to_string(),
56 }.render_string()).into_response());
57 }
58
59 // Build ticket body
60 let body = format!(
61 "From: {} ({})\nCategory: {}\n\n{}",
62 user.username, user.email, category, message
63 );
64
65 // Create WAM ticket for triage
66 if let Some(ref wam) = state.wam {
67 let title = format!("[support] {}", subject);
68 wam.create_ticket(
69 &title,
70 Some(&body),
71 "medium",
72 "support-ticket",
73 Some(&user.id.to_string()),
74 ).await;
75 }
76
77 // Send confirmation email to the user
78 let confirmation = format!(
79 r#"Hi {},
80
81 We received your support request:
82
83 Subject: {}
84 Category: {}
85
86 {}
87
88 We'll get back to you as soon as we can. Every response is from a real person.
89
90 --
91 Makenotwork Support"#,
92 user.display_name.as_deref().unwrap_or(user.username.as_ref()),
93 subject,
94 category,
95 message,
96 );
97 if let Err(e) = state.email.send_alert(
98 &user.email,
99 &format!("We received your request: {}", subject),
100 &confirmation,
101 ).await {
102 tracing::warn!(error = ?e, "failed to send support confirmation email");
103 }
104
105 // Send notification email to support
106 let support_body = format!(
107 "New support ticket from {} ({}):\n\nSubject: {}\nCategory: {}\nUser ID: {}\n\n{}",
108 user.username, user.email, subject, category, user.id, message,
109 );
110 if let Err(e) = state.email.send_alert(
111 "info@makenot.work",
112 &format!("[support] {}", subject),
113 &support_body,
114 ).await {
115 tracing::warn!(error = ?e, "failed to send support notification email");
116 }
117
118 Ok(Html(FormStatusTemplate {
119 success: true,
120 message: "Your message has been submitted. Check your email for a confirmation. We'll get back to you soon.".to_string(),
121 }.render_string()).into_response())
122 }
123