max / makenotwork
20 files changed,
+80 insertions,
-19 deletions
| @@ -3385,7 +3385,7 @@ dependencies = [ | |||
| 3385 | 3385 | ||
| 3386 | 3386 | [[package]] | |
| 3387 | 3387 | name = "makenotwork" | |
| 3388 | - | version = "0.4.3" | |
| 3388 | + | version = "0.4.4" | |
| 3389 | 3389 | dependencies = [ | |
| 3390 | 3390 | "anyhow", | |
| 3391 | 3391 | "argon2", | |
| @@ -3413,7 +3413,7 @@ dependencies = [ | |||
| 3413 | 3413 | "metrics", | |
| 3414 | 3414 | "metrics-exporter-prometheus", | |
| 3415 | 3415 | "openssl", | |
| 3416 | - | "rand 0.8.5", | |
| 3416 | + | "rand 0.9.2", | |
| 3417 | 3417 | "regex", | |
| 3418 | 3418 | "reqwest", | |
| 3419 | 3419 | "s3-storage", |
| @@ -57,7 +57,7 @@ webauthn-rs-proto = "0.5" | |||
| 57 | 57 | openssl = { version = "0.10", features = ["vendored"] } | |
| 58 | 58 | ||
| 59 | 59 | # Security | |
| 60 | - | rand = "0.8.5" | |
| 60 | + | rand = "0.9" | |
| 61 | 61 | hmac = "0.12.1" | |
| 62 | 62 | sha1 = "0.10.6" | |
| 63 | 63 | sha2 = "0.10.9" |
| @@ -201,12 +201,12 @@ Overall grade: A- -> A (post-remediation). 75.5k LOC, 986 unit tests (13.1 tests | |||
| 201 | 201 | - [x] **storage.rs `delete_prefix`** — added warning log when the default no-op is called. | |
| 202 | 202 | ||
| 203 | 203 | ### Frontend (A-) | |
| 204 | - | - [ ] **templates/partials.rs** — some template structs lack doc comments (TagTemplate, LinkRowTemplate, etc.). | |
| 204 | + | - [x] **Documentation pass** — added `//!` module docs to 5 internal API files (100% coverage for files >100 LOC). Added `///` struct docs to ~90 template structs across partials.rs, public.rs, and dashboard.rs. | |
| 205 | 205 | - [x] **email/notifications.rs** — fixed stale doc comment on line 442 (was "Send an alert email" above `send_tip_notification`). | |
| 206 | 206 | - [x] **checkout.rs tip truncation** — updated from 280 to 500 chars (Stripe metadata limit). | |
| 207 | 207 | ||
| 208 | 208 | ### Dependencies (B+) | |
| 209 | - | - [ ] **Bump rand to 0.9.x** — one major version behind, API changes required. | |
| 209 | + | - [x] **Bump rand to 0.9.x** — bumped from 0.8.5. One API change: `distributions` -> `distr` (sandbox.rs). | |
| 210 | 210 | - [ ] **CONCURRENTLY index strategy** — no migrations use `CREATE INDEX CONCURRENTLY`. Plan for this before tables grow large (transactions, items, users). | |
| 211 | 211 | - [x] **.gitignore** — added secret-file pattern exclusions (`.pem`, `.key`, `.p8`, `.p12`, `.pfx`, `credentials.json`, `service-account.json`). | |
| 212 | 212 | ||
| @@ -493,6 +493,14 @@ Weak points identified vs Ko-fi. Ordered by effort/impact. | |||
| 493 | 493 | ### Phase 24: Payment Independence | |
| 494 | 494 | - [ ] Payout alternatives, lower-cost processors, micro-transactions, compliance | |
| 495 | 495 | ||
| 496 | + | ### License Key Self-Management | |
| 497 | + | - [ ] Let buyers manage activations (view devices, deactivate) without a MNW account | |
| 498 | + | - Generous defaults: high activation limit, easy deactivation via key + machine_id | |
| 499 | + | - Key-code-only flows for common operations (check status, deactivate) | |
| 500 | + | - Full management (transfer, rename devices, history) requires linking a MNW account | |
| 501 | + | - [ ] Configurable offline grace period per item (default 7 days, creator can set 14/30) | |
| 502 | + | - [ ] License transfer: let a buyer reassign a key to another user (creator toggle per item) | |
| 503 | + | ||
| 496 | 504 | ### SyncKit S5: SDK & Multi-Tenant | |
| 497 | 505 | - [ ] Swift SDK, multi-tenant isolation, rate limiting per tier | |
| 498 | 506 | - [ ] Developer dashboard as MNW dashboard tab (not separate app — reuse existing dashboard infrastructure) |
| @@ -25,7 +25,7 @@ const CSRF_TOKEN_LENGTH: usize = 32; | |||
| 25 | 25 | /// Generate a new CSRF token | |
| 26 | 26 | pub fn generate_token() -> String { | |
| 27 | 27 | let mut token = [0u8; CSRF_TOKEN_LENGTH]; | |
| 28 | - | rand::thread_rng().fill_bytes(&mut token); | |
| 28 | + | rand::rng().fill_bytes(&mut token); | |
| 29 | 29 | hex::encode(token) | |
| 30 | 30 | } | |
| 31 | 31 |
| @@ -14,9 +14,9 @@ const CODE_LENGTH: usize = 12; | |||
| 14 | 14 | /// Generate a random 12-character invite code from the unambiguous charset. | |
| 15 | 15 | #[tracing::instrument(skip_all)] | |
| 16 | 16 | pub fn generate_invite_code() -> String { | |
| 17 | - | let mut rng = rand::thread_rng(); | |
| 17 | + | let mut rng = rand::rng(); | |
| 18 | 18 | (0..CODE_LENGTH) | |
| 19 | - | .map(|_| CODE_CHARSET[rng.gen_range(0..CODE_CHARSET.len())] as char) | |
| 19 | + | .map(|_| CODE_CHARSET[rng.random_range(0..CODE_CHARSET.len())] as char) | |
| 20 | 20 | .collect() | |
| 21 | 21 | } | |
| 22 | 22 |
| @@ -99,7 +99,7 @@ pub fn generate_login_token() -> (String, String) { | |||
| 99 | 99 | ||
| 100 | 100 | // Generate random token | |
| 101 | 101 | let mut token_bytes = [0u8; 32]; | |
| 102 | - | rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut token_bytes); | |
| 102 | + | rand::RngCore::fill_bytes(&mut rand::rng(), &mut token_bytes); | |
| 103 | 103 | let token = hex::encode(token_bytes); | |
| 104 | 104 | ||
| 105 | 105 | // Hash the token for storage |
| @@ -308,10 +308,10 @@ pub fn get_initials(name: &str) -> String { | |||
| 308 | 308 | /// Returns a `KeyCode` via `from_trusted` — the wordlist guarantees validity. | |
| 309 | 309 | pub fn generate_key_code() -> crate::db::KeyCode { | |
| 310 | 310 | use rand::Rng; | |
| 311 | - | let mut rng = rand::thread_rng(); | |
| 311 | + | let mut rng = rand::rng(); | |
| 312 | 312 | let words: Vec<&str> = (0..5) | |
| 313 | 313 | .map(|_| { | |
| 314 | - | let idx = rng.gen_range(0..crate::wordlist::WORDLIST.len()); | |
| 314 | + | let idx = rng.random_range(0..crate::wordlist::WORDLIST.len()); | |
| 315 | 315 | crate::wordlist::WORDLIST[idx] | |
| 316 | 316 | }) | |
| 317 | 317 | .collect(); |
| @@ -289,7 +289,7 @@ fn validate_domain(domain: &str) -> Result<()> { | |||
| 289 | 289 | /// Generate a random verification token. | |
| 290 | 290 | fn generate_verification_token() -> String { | |
| 291 | 291 | let mut bytes = [0u8; 16]; | |
| 292 | - | rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut bytes); | |
| 292 | + | rand::RngCore::fill_bytes(&mut rand::rng(), &mut bytes); | |
| 293 | 293 | format!("mnw-verify-{}", hex::encode(bytes)) | |
| 294 | 294 | } | |
| 295 | 295 |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! Internal content management: blog posts, promo codes, and license keys. | |
| 2 | + | ||
| 1 | 3 | use axum::{ | |
| 2 | 4 | extract::{Path, Query, State}, | |
| 3 | 5 | response::IntoResponse, |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! Internal creator dashboard: projects, stats, analytics, transactions, and sales export. | |
| 2 | + | ||
| 1 | 3 | use axum::{ | |
| 2 | 4 | extract::{Path, Query, State}, | |
| 3 | 5 | response::IntoResponse, |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! Internal git service: SSH key lookup, git push authorization, and server restart control. | |
| 2 | + | ||
| 1 | 3 | use axum::{ | |
| 2 | 4 | extract::{Query, State}, | |
| 3 | 5 | response::IntoResponse, |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! Internal item management: create, update, delete, publish, unpublish, and version history. | |
| 2 | + | ||
| 1 | 3 | use axum::{ | |
| 2 | 4 | extract::{Path, Query, State}, | |
| 3 | 5 | response::IntoResponse, |
| @@ -1,3 +1,5 @@ | |||
| 1 | + | //! Internal upload pipeline: presigned URL generation, upload confirmation, and storage usage. | |
| 2 | + | ||
| 1 | 3 | use axum::{ | |
| 2 | 4 | extract::{Query, State}, | |
| 3 | 5 | response::IntoResponse, |
| @@ -26,7 +26,7 @@ pub(super) async fn setup( | |||
| 26 | 26 | user.check_not_sandbox()?; | |
| 27 | 27 | // Generate a 20-byte (160-bit) random secret | |
| 28 | 28 | use rand::Rng; | |
| 29 | - | let secret_bytes: Vec<u8> = (0..20).map(|_| rand::thread_rng().r#gen()).collect(); | |
| 29 | + | let secret_bytes: Vec<u8> = (0..20).map(|_| rand::rng().random()).collect(); | |
| 30 | 30 | ||
| 31 | 31 | let totp = totp_rs::TOTP::new( | |
| 32 | 32 | totp_rs::Algorithm::SHA1, | |
| @@ -225,13 +225,13 @@ pub(crate) fn build_totp(secret_base32: &str, account_name: &str) -> Result<totp | |||
| 225 | 225 | /// Generate random alphanumeric backup codes. | |
| 226 | 226 | fn generate_backup_codes() -> Vec<String> { | |
| 227 | 227 | use rand::Rng; | |
| 228 | - | let mut rng = rand::thread_rng(); | |
| 228 | + | let mut rng = rand::rng(); | |
| 229 | 229 | ||
| 230 | 230 | (0..BACKUP_CODE_COUNT) | |
| 231 | 231 | .map(|_| { | |
| 232 | 232 | (0..BACKUP_CODE_LENGTH) | |
| 233 | 233 | .map(|_| { | |
| 234 | - | let idx: u8 = rng.gen_range(0..36); | |
| 234 | + | let idx: u8 = rng.random_range(0..36); | |
| 235 | 235 | if idx < 10 { | |
| 236 | 236 | (b'0' + idx) as char | |
| 237 | 237 | } else { |
| @@ -81,7 +81,7 @@ pub struct TokenResponse { | |||
| 81 | 81 | ||
| 82 | 82 | fn generate_oauth_code() -> String { | |
| 83 | 83 | let mut bytes = [0u8; constants::OAUTH_CODE_LENGTH]; | |
| 84 | - | rand::thread_rng().fill_bytes(&mut bytes); | |
| 84 | + | rand::rng().fill_bytes(&mut bytes); | |
| 85 | 85 | hex::encode(bytes) | |
| 86 | 86 | } | |
| 87 | 87 |
| @@ -72,8 +72,8 @@ pub(super) async fn create_sandbox( | |||
| 72 | 72 | } | |
| 73 | 73 | ||
| 74 | 74 | // Generate random sandbox credentials | |
| 75 | - | let suffix: String = rand::thread_rng() | |
| 76 | - | .sample_iter(&rand::distributions::Alphanumeric) | |
| 75 | + | let suffix: String = rand::rng() | |
| 76 | + | .sample_iter(&rand::distr::Alphanumeric) | |
| 77 | 77 | .take(8) | |
| 78 | 78 | .map(char::from) | |
| 79 | 79 | .collect::<String>() |
| @@ -200,7 +200,7 @@ pub(super) struct AppWithKey { | |||
| 200 | 200 | pub(super) fn generate_api_key() -> String { | |
| 201 | 201 | use rand::RngCore; | |
| 202 | 202 | let mut bytes = [0u8; constants::SYNCKIT_API_KEY_LENGTH]; | |
| 203 | - | rand::thread_rng().fill_bytes(&mut bytes); | |
| 203 | + | rand::rng().fill_bytes(&mut bytes); | |
| 204 | 204 | hex::encode(bytes) | |
| 205 | 205 | } | |
| 206 | 206 |
| @@ -40,6 +40,7 @@ pub struct DashboardUserTemplate { | |||
| 40 | 40 | pub deactivated: bool, | |
| 41 | 41 | } | |
| 42 | 42 | ||
| 43 | + | /// Project dashboard page with stats, content list, and management tabs. | |
| 43 | 44 | #[derive(Template)] | |
| 44 | 45 | #[template(path = "dashboards/dashboard-project.html")] | |
| 45 | 46 | #[allow(dead_code)] // Fields used by Askama template | |
| @@ -73,6 +74,7 @@ pub struct DashboardItemTemplate { | |||
| 73 | 74 | // Admin | |
| 74 | 75 | // ============================================================================ | |
| 75 | 76 | ||
| 77 | + | /// Admin waitlist management page with filtering and invite controls. | |
| 76 | 78 | #[derive(Template)] | |
| 77 | 79 | #[template(path = "dashboards/admin-waitlist.html")] | |
| 78 | 80 | pub struct AdminWaitlistTemplate { | |
| @@ -226,6 +228,7 @@ pub struct ImportJobRow { | |||
| 226 | 228 | pub created_at: chrono::DateTime<chrono::Utc>, | |
| 227 | 229 | } | |
| 228 | 230 | ||
| 231 | + | /// Account deletion confirmation page with username verification. | |
| 229 | 232 | #[derive(Template)] | |
| 230 | 233 | #[template(path = "dashboards/dashboard-delete-account.html")] | |
| 231 | 234 | pub struct DeleteAccountTemplate { | |
| @@ -302,6 +305,7 @@ pub struct WizardItemTemplate { | |||
| 302 | 305 | ||
| 303 | 306 | // --- Project step partials --- | |
| 304 | 307 | ||
| 308 | + | /// Wizard step partial: project basics (title, slug, features, category). | |
| 305 | 309 | #[derive(Template)] | |
| 306 | 310 | #[template(path = "wizards/steps/project/basics.html")] | |
| 307 | 311 | pub struct WizardProjectBasicsTemplate { | |
| @@ -314,6 +318,7 @@ pub struct WizardProjectBasicsTemplate { | |||
| 314 | 318 | pub category_name: String, | |
| 315 | 319 | } | |
| 316 | 320 | ||
| 321 | + | /// Wizard step partial: project appearance (cover image upload). | |
| 317 | 322 | #[derive(Template)] | |
| 318 | 323 | #[template(path = "wizards/steps/project/appearance.html")] | |
| 319 | 324 | pub struct WizardProjectAppearanceTemplate { | |
| @@ -324,6 +329,7 @@ pub struct WizardProjectAppearanceTemplate { | |||
| 324 | 329 | pub project_title: String, | |
| 325 | 330 | } | |
| 326 | 331 | ||
| 332 | + | /// Wizard step partial: project monetization (pricing model, tiers, Stripe). | |
| 327 | 333 | #[derive(Template)] | |
| 328 | 334 | #[template(path = "wizards/steps/project/monetization.html")] | |
| 329 | 335 | pub struct WizardProjectMonetizationTemplate { | |
| @@ -339,6 +345,7 @@ pub struct WizardProjectMonetizationTemplate { | |||
| 339 | 345 | pub pwyw_min_dollars: String, | |
| 340 | 346 | } | |
| 341 | 347 | ||
| 348 | + | /// Wizard step partial: project first content prompt (item count gate). | |
| 342 | 349 | #[derive(Template)] | |
| 343 | 350 | #[template(path = "wizards/steps/project/first_content.html")] | |
| 344 | 351 | pub struct WizardProjectFirstContentTemplate { | |
| @@ -347,6 +354,7 @@ pub struct WizardProjectFirstContentTemplate { | |||
| 347 | 354 | pub item_count: u32, | |
| 348 | 355 | } | |
| 349 | 356 | ||
| 357 | + | /// Wizard step partial: project preview and publish confirmation. | |
| 350 | 358 | #[derive(Template)] | |
| 351 | 359 | #[template(path = "wizards/steps/project/preview.html")] | |
| 352 | 360 | #[allow(dead_code)] | |
| @@ -369,6 +377,7 @@ pub struct WizardProjectPreviewTemplate { | |||
| 369 | 377 | ||
| 370 | 378 | // --- Item step partials --- | |
| 371 | 379 | ||
| 380 | + | /// Wizard step partial: item type selection (text, audio, video, software, bundle). | |
| 372 | 381 | #[derive(Template)] | |
| 373 | 382 | #[template(path = "wizards/steps/item/type.html")] | |
| 374 | 383 | pub struct WizardItemTypeTemplate { | |
| @@ -379,6 +388,7 @@ pub struct WizardItemTypeTemplate { | |||
| 379 | 388 | pub selected_type: String, | |
| 380 | 389 | } | |
| 381 | 390 | ||
| 391 | + | /// Wizard step partial: item details (title, description). | |
| 382 | 392 | #[derive(Template)] | |
| 383 | 393 | #[template(path = "wizards/steps/item/details.html")] | |
| 384 | 394 | pub struct WizardItemDetailsTemplate { | |
| @@ -389,6 +399,7 @@ pub struct WizardItemDetailsTemplate { | |||
| 389 | 399 | pub description: String, | |
| 390 | 400 | } | |
| 391 | 401 | ||
| 402 | + | /// Wizard step partial: item appearance (cover image upload). | |
| 392 | 403 | #[derive(Template)] | |
| 393 | 404 | #[template(path = "wizards/steps/item/appearance.html")] | |
| 394 | 405 | pub struct WizardItemAppearanceTemplate { | |
| @@ -398,6 +409,7 @@ pub struct WizardItemAppearanceTemplate { | |||
| 398 | 409 | pub cover_image_url: Option<String>, | |
| 399 | 410 | } | |
| 400 | 411 | ||
| 412 | + | /// Wizard step partial: item content (body editor, bundle picker). | |
| 401 | 413 | #[derive(Template)] | |
| 402 | 414 | #[template(path = "wizards/steps/item/content.html")] | |
| 403 | 415 | pub struct WizardItemContentTemplate { | |
| @@ -422,6 +434,7 @@ pub struct BundleableItem { | |||
| 422 | 434 | pub item_type: String, | |
| 423 | 435 | } | |
| 424 | 436 | ||
| 437 | + | /// Wizard step partial: item sections (reorderable content sections). | |
| 425 | 438 | #[derive(Template)] | |
| 426 | 439 | #[template(path = "wizards/steps/item/sections.html")] | |
| 427 | 440 | pub struct WizardItemSectionsTemplate { | |
| @@ -431,6 +444,7 @@ pub struct WizardItemSectionsTemplate { | |||
| 431 | 444 | pub sections: Vec<crate::types::ItemSection>, | |
| 432 | 445 | } | |
| 433 | 446 | ||
| 447 | + | /// Wizard step partial: item pricing (model selection, price entry). | |
| 434 | 448 | #[derive(Template)] | |
| 435 | 449 | #[template(path = "wizards/steps/item/pricing.html")] | |
| 436 | 450 | pub struct WizardItemPricingTemplate { | |
| @@ -444,6 +458,7 @@ pub struct WizardItemPricingTemplate { | |||
| 444 | 458 | pub next_step: String, | |
| 445 | 459 | } | |
| 446 | 460 | ||
| 461 | + | /// Wizard step partial: item distribution (license keys, license preset). | |
| 447 | 462 | #[derive(Template)] | |
| 448 | 463 | #[template(path = "wizards/steps/item/distribution.html")] | |
| 449 | 464 | #[allow(dead_code)] | |
| @@ -459,6 +474,7 @@ pub struct WizardItemDistributionTemplate { | |||
| 459 | 474 | pub custom_license_text: String, | |
| 460 | 475 | } | |
| 461 | 476 | ||
| 477 | + | /// Wizard step partial: item preview and publish confirmation. | |
| 462 | 478 | #[derive(Template)] | |
| 463 | 479 | #[template(path = "wizards/steps/item/preview.html")] | |
| 464 | 480 | #[allow(dead_code)] |
| @@ -30,6 +30,7 @@ pub struct DiscoverResultsTemplate { | |||
| 30 | 30 | pub current_label: String, | |
| 31 | 31 | } | |
| 32 | 32 | ||
| 33 | + | /// HTMX partial: dismissible alert/notification banner. | |
| 33 | 34 | #[derive(Template)] | |
| 34 | 35 | #[template(path = "partials/alert.html")] | |
| 35 | 36 | pub struct AlertTemplate { | |
| @@ -56,6 +57,7 @@ impl AlertTemplate { | |||
| 56 | 57 | } | |
| 57 | 58 | } | |
| 58 | 59 | ||
| 60 | + | /// HTMX partial: success/error status message after form submission. | |
| 59 | 61 | #[derive(Template)] | |
| 60 | 62 | #[template(path = "partials/form_status.html")] | |
| 61 | 63 | pub struct FormStatusTemplate { | |
| @@ -69,12 +71,14 @@ impl FormStatusTemplate { | |||
| 69 | 71 | } | |
| 70 | 72 | } | |
| 71 | 73 | ||
| 74 | + | /// HTMX partial: library action status message. | |
| 72 | 75 | #[derive(Template)] | |
| 73 | 76 | #[template(path = "partials/library_status.html")] | |
| 74 | 77 | pub struct LibraryStatusTemplate { | |
| 75 | 78 | pub message: String, | |
| 76 | 79 | } | |
| 77 | 80 | ||
| 81 | + | /// HTMX partial: data-URI download link for an export file. | |
| 78 | 82 | #[derive(Template)] | |
| 79 | 83 | #[template(path = "partials/export_download.html")] | |
| 80 | 84 | pub struct ExportDownloadTemplate { | |
| @@ -82,12 +86,14 @@ pub struct ExportDownloadTemplate { | |||
| 82 | 86 | pub filename: String, | |
| 83 | 87 | } | |
| 84 | 88 | ||
| 89 | + | /// HTMX partial: download button shown when a content export is ready. | |
| 85 | 90 | #[derive(Template)] | |
| 86 | 91 | #[template(path = "partials/export_content_ready.html")] | |
| 87 | 92 | pub struct ExportContentReadyTemplate { | |
| 88 | 93 | pub download_url: String, | |
| 89 | 94 | } | |
| 90 | 95 | ||
| 96 | + | /// HTMX partial: inline login error message. | |
| 91 | 97 | #[derive(Template)] | |
| 92 | 98 | #[template(path = "partials/login_error.html")] | |
| 93 | 99 | pub struct LoginErrorTemplate { | |
| @@ -100,6 +106,7 @@ impl LoginErrorTemplate { | |||
| 100 | 106 | } | |
| 101 | 107 | } | |
| 102 | 108 | ||
| 109 | + | /// HTMX partial: username availability check result. | |
| 103 | 110 | #[derive(Template)] | |
| 104 | 111 | #[template(path = "partials/username_status.html")] | |
| 105 | 112 | pub struct UsernameStatusTemplate { | |
| @@ -112,6 +119,7 @@ impl UsernameStatusTemplate { | |||
| 112 | 119 | } | |
| 113 | 120 | } | |
| 114 | 121 | ||
| 122 | + | /// HTMX partial: project slug availability check result. | |
| 115 | 123 | #[derive(Template)] | |
| 116 | 124 | #[template(path = "partials/slug_status.html")] | |
| 117 | 125 | pub struct SlugStatusTemplate { | |
| @@ -124,6 +132,7 @@ impl SlugStatusTemplate { | |||
| 124 | 132 | } | |
| 125 | 133 | } | |
| 126 | 134 | ||
| 135 | + | /// HTMX partial: inline save confirmation or error indicator. | |
| 127 | 136 | #[derive(Template)] | |
| 128 | 137 | #[template(path = "partials/save_status.html")] | |
| 129 | 138 | pub struct SaveStatusTemplate { | |
| @@ -137,6 +146,7 @@ impl SaveStatusTemplate { | |||
| 137 | 146 | } | |
| 138 | 147 | } | |
| 139 | 148 | ||
| 149 | + | /// HTMX partial: paginated transaction history table. | |
| 140 | 150 | #[derive(Template)] | |
| 141 | 151 | #[template(path = "partials/transactions_table.html")] | |
| 142 | 152 | pub struct TransactionsTableTemplate { | |
| @@ -147,6 +157,7 @@ pub struct TransactionsTableTemplate { | |||
| 147 | 157 | // Dashboard Tab Partials | |
| 148 | 158 | // ============================================================================ | |
| 149 | 159 | ||
| 160 | + | /// Dashboard tab: account details, sessions, links, and preferences. | |
| 150 | 161 | #[derive(Template)] | |
| 151 | 162 | #[template(path = "partials/tabs/user_details.html")] | |
| 152 | 163 | pub struct UserDetailsTabTemplate { | |
| @@ -183,6 +194,7 @@ pub struct CustomLinkWithId { | |||
| 183 | 194 | pub title: String, | |
| 184 | 195 | } | |
| 185 | 196 | ||
| 197 | + | /// Dashboard tab: payment history, payouts, tips, and revenue splits. | |
| 186 | 198 | #[derive(Template)] | |
| 187 | 199 | #[template(path = "partials/tabs/user_payments.html")] | |
| 188 | 200 | pub struct UserPaymentsTabTemplate { | |
| @@ -199,6 +211,7 @@ pub struct UserPaymentsTabTemplate { | |||
| 199 | 211 | pub splits_outgoing_total: String, | |
| 200 | 212 | } | |
| 201 | 213 | ||
| 214 | + | /// Dashboard tab: user's projects list with create button. | |
| 202 | 215 | #[derive(Template)] | |
| 203 | 216 | #[template(path = "partials/tabs/user_projects.html")] | |
| 204 | 217 | pub struct UserProjectsTabTemplate { | |
| @@ -255,6 +268,7 @@ pub struct UserCreatorTabTemplate { | |||
| 255 | 268 | pub storage_pct: u8, | |
| 256 | 269 | } | |
| 257 | 270 | ||
| 271 | + | /// Dashboard tab: project overview with stat cards. | |
| 258 | 272 | #[derive(Template)] | |
| 259 | 273 | #[template(path = "partials/tabs/project_overview.html")] | |
| 260 | 274 | pub struct ProjectOverviewTabTemplate { | |
| @@ -262,6 +276,7 @@ pub struct ProjectOverviewTabTemplate { | |||
| 262 | 276 | pub project_slug: String, | |
| 263 | 277 | } | |
| 264 | 278 | ||
| 279 | + | /// Dashboard tab: project content items list. | |
| 265 | 280 | #[derive(Template)] | |
| 266 | 281 | #[template(path = "partials/tabs/project_content.html")] | |
| 267 | 282 | pub struct ProjectContentTabTemplate { | |
| @@ -269,6 +284,7 @@ pub struct ProjectContentTabTemplate { | |||
| 269 | 284 | pub project_slug: String, | |
| 270 | 285 | } | |
| 271 | 286 | ||
| 287 | + | /// Dashboard tab: project analytics with stats, chart, and top items. | |
| 272 | 288 | #[derive(Template)] | |
| 273 | 289 | #[template(path = "partials/tabs/project_analytics.html")] | |
| 274 | 290 | pub struct ProjectAnalyticsTabTemplate { | |
| @@ -279,6 +295,7 @@ pub struct ProjectAnalyticsTabTemplate { | |||
| 279 | 295 | pub active_range: String, | |
| 280 | 296 | } | |
| 281 | 297 | ||
| 298 | + | /// Dashboard tab: project settings, categories, labels, and features. | |
| 282 | 299 | #[derive(Template)] | |
| 283 | 300 | #[template(path = "partials/tabs/project_settings.html")] | |
| 284 | 301 | pub struct ProjectSettingsTabTemplate { | |
| @@ -468,6 +485,7 @@ pub struct UserSessionsPartialTemplate { | |||
| 468 | 485 | pub current_session_id: Option<UserSessionId>, | |
| 469 | 486 | } | |
| 470 | 487 | ||
| 488 | + | /// HTMX partial: single removable tag pill on an item. | |
| 471 | 489 | #[derive(Template)] | |
| 472 | 490 | #[template(path = "partials/tag.html")] | |
| 473 | 491 | pub struct TagTemplate { | |
| @@ -483,12 +501,14 @@ impl TagTemplate { | |||
| 483 | 501 | } | |
| 484 | 502 | } | |
| 485 | 503 | ||
| 504 | + | /// HTMX partial: editable content item row in the project dashboard. | |
| 486 | 505 | #[derive(Template)] | |
| 487 | 506 | #[template(path = "partials/item_edit_row.html")] | |
| 488 | 507 | pub struct ItemEditRowTemplate { | |
| 489 | 508 | pub item: ContentItem, | |
| 490 | 509 | } | |
| 491 | 510 | ||
| 511 | + | /// HTMX partial: editable custom link row in the user details tab. | |
| 492 | 512 | #[derive(Template)] | |
| 493 | 513 | #[template(path = "partials/link_row.html")] | |
| 494 | 514 | pub struct LinkRowTemplate { | |
| @@ -520,6 +540,7 @@ pub struct ProjectLabelsTemplate { | |||
| 520 | 540 | // Admin Partials | |
| 521 | 541 | // ============================================================================ | |
| 522 | 542 | ||
| 543 | + | /// Admin HTMX partial: creator waitlist entries table. | |
| 523 | 544 | #[derive(Template)] | |
| 524 | 545 | #[template(path = "partials/admin_waitlist_entries.html")] | |
| 525 | 546 | pub struct AdminWaitlistEntriesTemplate { |
| @@ -441,6 +441,7 @@ pub struct FeedTemplate { | |||
| 441 | 441 | pub showing_end: u32, | |
| 442 | 442 | } | |
| 443 | 443 | ||
| 444 | + | /// Public page: Stripe Connect disclaimer and terms before onboarding. | |
| 444 | 445 | #[derive(Template)] | |
| 445 | 446 | #[template(path = "pages/stripe_disclaimer.html")] | |
| 446 | 447 | pub struct StripeConnectDisclaimerTemplate { | |
| @@ -577,6 +578,7 @@ pub struct UseCasesTemplate { | |||
| 577 | 578 | // Creator Invite System | |
| 578 | 579 | // ============================================================================ | |
| 579 | 580 | ||
| 581 | + | /// Public page: creator invite waves and waitlist status. | |
| 580 | 582 | #[derive(Template)] | |
| 581 | 583 | #[template(path = "pages/creators.html")] | |
| 582 | 584 | pub struct CreatorsTemplate { | |
| @@ -592,6 +594,7 @@ pub struct CreatorsTemplate { | |||
| 592 | 594 | // Email & Account | |
| 593 | 595 | // ============================================================================ | |
| 594 | 596 | ||
| 597 | + | /// Public page: email action result (verification, unsubscribe, etc.). | |
| 595 | 598 | #[derive(Template)] | |
| 596 | 599 | #[template(path = "pages/email_result.html")] | |
| 597 | 600 | pub struct EmailResultTemplate { | |
| @@ -612,6 +615,7 @@ pub struct ConfirmDeleteTemplate { | |||
| 612 | 615 | pub sig: String, | |
| 613 | 616 | } | |
| 614 | 617 | ||
| 618 | + | /// Public page: confirmation that account has been deleted. | |
| 615 | 619 | #[derive(Template)] | |
| 616 | 620 | #[template(path = "pages/account-deleted.html")] | |
| 617 | 621 | pub struct AccountDeletedTemplate { | |
| @@ -640,6 +644,7 @@ pub struct PrivacyJobDisplay { | |||
| 640 | 644 | pub status_class: String, | |
| 641 | 645 | } | |
| 642 | 646 | ||
| 647 | + | /// Pre-formatted health check snapshot for template rendering. | |
| 643 | 648 | pub struct HealthSnapshotDisplay { | |
| 644 | 649 | pub checked_at: String, | |
| 645 | 650 | pub status: String, | |
| @@ -664,6 +669,7 @@ pub struct PomIncidentDisplay { | |||
| 664 | 669 | pub duration: String, | |
| 665 | 670 | } | |
| 666 | 671 | ||
| 672 | + | /// Public page: platform health status and monitoring dashboard. | |
| 667 | 673 | #[derive(Template)] | |
| 668 | 674 | #[template(path = "pages/health.html")] | |
| 669 | 675 | pub struct HealthTemplate { |