Skip to main content

max / makenotwork

7.6 KB · 224 lines History Blame Raw
1 //! Internal API endpoints for service-to-service communication.
2 //!
3 //! These endpoints are protected by `ServiceAuth` (Bearer token) and are
4 //! called by the CLI SSH server running on the same host.
5
6 mod alerts;
7 mod cli_features;
8 mod content;
9 mod creators;
10 mod git;
11 mod items;
12 mod synckit;
13 mod uploads;
14
15 pub(super) use git::restart_status;
16
17 use axum::routing::get;
18
19 use crate::{
20 AppState,
21 csrf::{CsrfRouter, delete_csrf_skip, post_csrf_skip, put_csrf_skip, with_csrf_skip},
22 };
23
24 /// All routes in this file are HMAC-bearer authed via `ServiceAuth` (no
25 /// session); CSRF is not applicable. Each registration carries the same
26 /// `Skip` reason so the posture is visible at the call site.
27 const INTERNAL_SKIP: &str = "internal API: HMAC bearer auth, no session";
28
29 /// Internal service-to-service routes (ServiceAuth, no rate limit).
30 pub(super) fn internal_routes() -> CsrfRouter<AppState> {
31 CsrfRouter::new()
32 .route_get("/api/internal/ssh-key-lookup", get(git::ssh_key_lookup))
33 .route_get(
34 "/api/internal/synckit/client-versions",
35 get(synckit::client_versions),
36 )
37 .route(
38 "/api/internal/creator/projects",
39 with_csrf_skip(
40 INTERNAL_SKIP,
41 get(creators::creator_projects).post(cli_features::create_project),
42 ),
43 )
44 .route_get(
45 "/api/internal/creator/projects/{id}/items",
46 get(creators::creator_project_items),
47 )
48 .route_get("/api/internal/creator/stats", get(creators::creator_stats))
49 .route(
50 "/api/internal/creator/items",
51 post_csrf_skip(INTERNAL_SKIP, items::create_item),
52 )
53 .route(
54 "/api/internal/upload/presign",
55 post_csrf_skip(INTERNAL_SKIP, uploads::presign_upload),
56 )
57 .route(
58 "/api/internal/upload/confirm",
59 post_csrf_skip(INTERNAL_SKIP, uploads::confirm_upload),
60 )
61 .route(
62 "/api/internal/upload/multipart/start",
63 post_csrf_skip(INTERNAL_SKIP, uploads::multipart_start),
64 )
65 .route(
66 "/api/internal/upload/multipart/parts",
67 post_csrf_skip(INTERNAL_SKIP, uploads::multipart_parts),
68 )
69 .route(
70 "/api/internal/upload/multipart/complete",
71 post_csrf_skip(INTERNAL_SKIP, uploads::multipart_complete),
72 )
73 .route(
74 "/api/internal/upload/multipart/abort",
75 post_csrf_skip(INTERNAL_SKIP, uploads::multipart_abort),
76 )
77 .route_get(
78 "/api/internal/creator/storage",
79 get(uploads::creator_storage),
80 )
81 .route_get("/api/internal/creator/items/{id}", get(items::get_item))
82 .route(
83 "/api/internal/creator/items/{id}",
84 put_csrf_skip(INTERNAL_SKIP, items::update_item),
85 )
86 .route(
87 "/api/internal/creator/items/{id}",
88 delete_csrf_skip(INTERNAL_SKIP, items::delete_item),
89 )
90 .route(
91 "/api/internal/creator/items/{id}/publish",
92 post_csrf_skip(INTERNAL_SKIP, items::publish_item),
93 )
94 .route(
95 "/api/internal/creator/items/{id}/unpublish",
96 post_csrf_skip(INTERNAL_SKIP, items::unpublish_item),
97 )
98 .route_get(
99 "/api/internal/creator/items/{id}/versions",
100 get(items::item_versions),
101 )
102 // Blog posts
103 .route_get(
104 "/api/internal/creator/projects/{id}/blog",
105 get(content::list_blog_posts),
106 )
107 .route(
108 "/api/internal/creator/blog",
109 post_csrf_skip(INTERNAL_SKIP, content::create_blog_post),
110 )
111 .route(
112 "/api/internal/creator/blog/{id}",
113 delete_csrf_skip(INTERNAL_SKIP, content::delete_blog_post),
114 )
115 // Promo codes
116 .route(
117 "/api/internal/creator/promo-codes",
118 with_csrf_skip(
119 INTERNAL_SKIP,
120 get(content::list_promo_codes).post(content::create_promo_code),
121 ),
122 )
123 .route(
124 "/api/internal/creator/promo-codes/{id}",
125 delete_csrf_skip(INTERNAL_SKIP, content::delete_promo_code),
126 )
127 // License keys
128 .route(
129 "/api/internal/creator/items/{id}/keys",
130 with_csrf_skip(
131 INTERNAL_SKIP,
132 get(content::list_license_keys).post(content::generate_license_key),
133 ),
134 )
135 .route(
136 "/api/internal/creator/keys/{id}/revoke",
137 post_csrf_skip(INTERNAL_SKIP, content::revoke_license_key),
138 )
139 // Analytics + export
140 .route_get(
141 "/api/internal/creator/analytics",
142 get(creators::creator_analytics),
143 )
144 .route_get(
145 "/api/internal/creator/transactions",
146 get(creators::creator_transactions),
147 )
148 .route_get(
149 "/api/internal/creator/export/sales",
150 get(creators::export_sales),
151 )
152 // Settings
153 .route_get("/api/internal/creator/ssh-keys", get(git::list_ssh_keys))
154 // Git authorization
155 .route(
156 "/api/internal/git/authorize",
157 post_csrf_skip(INTERNAL_SKIP, git::git_authorize),
158 )
159 .route(
160 "/api/internal/restart-warning",
161 post_csrf_skip(INTERNAL_SKIP, git::set_restart_warning),
162 )
163 // CLI features: tags
164 .route_get(
165 "/api/internal/creator/items/{id}/tags",
166 get(cli_features::list_item_tags),
167 )
168 .route(
169 "/api/internal/creator/items/tags",
170 post_csrf_skip(INTERNAL_SKIP, cli_features::add_item_tag),
171 )
172 .route(
173 "/api/internal/creator/items/tags/remove",
174 post_csrf_skip(INTERNAL_SKIP, cli_features::remove_item_tag),
175 )
176 .route_get("/api/internal/tags/search", get(cli_features::search_tags))
177 // CLI features: broadcast
178 .route(
179 "/api/internal/creator/broadcast",
180 post_csrf_skip(INTERNAL_SKIP, cli_features::send_broadcast),
181 )
182 // CLI features: tiers
183 .route_get(
184 "/api/internal/creator/projects/{id}/tiers",
185 get(cli_features::list_tiers),
186 )
187 // CLI features: collections
188 .route(
189 "/api/internal/creator/collections",
190 with_csrf_skip(
191 INTERNAL_SKIP,
192 get(cli_features::list_collections).post(cli_features::create_collection),
193 ),
194 )
195 .route(
196 "/api/internal/creator/collections/{id}",
197 delete_csrf_skip(INTERNAL_SKIP, cli_features::delete_collection),
198 )
199 // CLI features: custom domains
200 .route(
201 "/api/internal/creator/domain",
202 with_csrf_skip(
203 INTERNAL_SKIP,
204 get(cli_features::get_domain)
205 .post(cli_features::add_domain)
206 .delete(cli_features::remove_domain),
207 ),
208 )
209 .route(
210 "/api/internal/creator/domain/verify",
211 post_csrf_skip(INTERNAL_SKIP, cli_features::verify_domain),
212 )
213 // Inbound infra alerts (PoM/MT). Bearer-authed via `AlertsAuth` on a
214 // dedicated token, not `ServiceAuth`; no session, so CSRF is skipped.
215 .route(
216 "/api/internal/alerts",
217 post_csrf_skip(ALERTS_SKIP, alerts::ingest_alert),
218 )
219 }
220
221 /// The alerts ingestion route is bearer-authed via `AlertsAuth` (dedicated
222 /// token, no session); CSRF is not applicable.
223 const ALERTS_SKIP: &str = "internal alerts: AlertsAuth bearer, no session";
224