aboutsummaryrefslogtreecommitdiff
path: root/src/app.cpp
diff options
context:
space:
mode:
authorMetroWind <chris.corsair@gmail.com>2025-09-21 21:34:34 -0700
committerMetroWind <chris.corsair@gmail.com>2025-09-21 21:34:34 -0700
commite9686b6ab684785d5f9acbc98942beae94817562 (patch)
tree10ffe1b7b209aee2f0513cd0c42def2c07272ea2 /src/app.cpp
parentb2e812941766e11394bdb124ff73d1fe544849a2 (diff)
Implement dir handler. Unit test WIP.HEADmaster
Diffstat (limited to 'src/app.cpp')
-rw-r--r--src/app.cpp150
1 files changed, 115 insertions, 35 deletions
diff --git a/src/app.cpp b/src/app.cpp
index 7cfcf0d..472c266 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -75,7 +75,7 @@ void setTokenCookies(const mw::Tokens& tokens, App::Response& res)
75 expire_sec = expire.count(); 75 expire_sec = expire.count();
76 } 76 }
77 res.set_header("Set-Cookie", std::format( 77 res.set_header("Set-Cookie", std::format(
78 "shrt-access-token={}; Max-Age={}", 78 "webdir-access-token={}; Max-Age={}",
79 mw::urlEncode(tokens.access_token), expire_sec)); 79 mw::urlEncode(tokens.access_token), expire_sec));
80 // Add refresh token to cookie, with one month expiration. 80 // Add refresh token to cookie, with one month expiration.
81 if(tokens.refresh_token.has_value()) 81 if(tokens.refresh_token.has_value())
@@ -89,7 +89,7 @@ void setTokenCookies(const mw::Tokens& tokens, App::Response& res)
89 } 89 }
90 90
91 res.set_header("Set-Cookie", std::format( 91 res.set_header("Set-Cookie", std::format(
92 "shrt-refresh-token={}; Max-Age={}", 92 "webdir-refresh-token={}; Max-Age={}",
93 mw::urlEncode(*tokens.refresh_token), expire_sec)); 93 mw::urlEncode(*tokens.refresh_token), expire_sec));
94 } 94 }
95} 95}
@@ -111,6 +111,21 @@ mw::HTTPServer::ListenAddress listenAddrFromConfig(const Configuration& config)
111 return sock; 111 return sock;
112} 112}
113 113
114nlohmann::json jsonFromItem(const LinkItem& item)
115{
116 return {
117 {"id", item.id},
118 {"owner_id", item.owner_id},
119 {"parent_id", item.parent_id},
120 {"name", item.name},
121 {"url", item.url},
122 {"description", item.description},
123 {"visibility", LinkItem::visibilityToStr(item.visibility)},
124 {"time", mw::timeToSeconds(item.time)},
125 {"time_str", mw::timeToStr(item.time)},
126 };
127}
128
114} // namespace 129} // namespace
115 130
116App::App(const Configuration& conf, 131App::App(const Configuration& conf,
@@ -151,18 +166,6 @@ std::string App::urlFor(const std::string& name, const std::string& arg) const
151 { 166 {
152 return mw::URL(base_url).appendPath("_/statics").appendPath(arg).str(); 167 return mw::URL(base_url).appendPath("_/statics").appendPath(arg).str();
153 } 168 }
154 if(name == "index")
155 {
156 return base_url.str();
157 }
158 if(name == "shortcut")
159 {
160 return mw::URL(base_url).appendPath(arg).str();
161 }
162 if(name == "links")
163 {
164 return mw::URL(base_url).appendPath("_/links").str();
165 }
166 if(name == "login") 169 if(name == "login")
167 { 170 {
168 return mw::URL(base_url).appendPath("_/login").str(); 171 return mw::URL(base_url).appendPath("_/login").str();
@@ -171,22 +174,13 @@ std::string App::urlFor(const std::string& name, const std::string& arg) const
171 { 174 {
172 return mw::URL(base_url).appendPath("_/openid-redirect").str(); 175 return mw::URL(base_url).appendPath("_/openid-redirect").str();
173 } 176 }
174 if(name == "new-link") 177 if(name == "index")
175 {
176 return mw::URL(base_url).appendPath("_/new-link").str();
177 }
178 if(name == "create-link")
179 {
180 return mw::URL(base_url).appendPath("_/create-link").str();
181 }
182 if(name == "delete-link-dialog")
183 { 178 {
184 return mw::URL(base_url).appendPath("_/delete-link").appendPath(arg) 179 return base_url.str();
185 .str();
186 } 180 }
187 if(name == "delete-link") 181 if(name == "dir") // /dir/<username or item_id>
188 { 182 {
189 return mw::URL(base_url).appendPath("_/delete-link").str(); 183 return mw::URL(base_url).appendPath("dir").appendPath(arg).str();
190 } 184 }
191 185
192 return ""; 186 return "";
@@ -194,7 +188,86 @@ std::string App::urlFor(const std::string& name, const std::string& arg) const
194 188
195void App::handleIndex(Response& res) const 189void App::handleIndex(Response& res) const
196{ 190{
197 res.set_redirect(urlFor("links"), 301); 191 res.set_redirect(urlFor("dir", "mw"), 301);
192}
193
194void App::handleDir(const Request& req, Response& res)
195{
196 auto session = prepareSession(req, res, true);
197 if(!req.has_param("owner_or_id"))
198 {
199 res.status = 400;
200 res.set_content("Need parameter", "text/plain");
201 return;
202 }
203 std::string owner_or_id = req.get_param_value("owner_or_id");
204 std::string owner;
205 // If owner_or_id is an integer, it’s the ID of an item. Otherwise
206 // it’s a username.
207 auto item_id_maybe = mw::strToNumber<int64_t>(owner_or_id);
208 std::vector<LinkItem> items;
209 if(item_id_maybe.has_value())
210 {
211 ASSIGN_OR_RESPOND_ERROR(std::optional<LinkItem> item,
212 data->itemByID(*item_id_maybe), res);
213 if(!item.has_value())
214 {
215 res.status = 400;
216 res.set_content("Item not found", "text/plain");
217 return;
218 }
219 ASSIGN_OR_RESPOND_ERROR(std::optional<User> user,
220 data->userByID(item->owner_id), res);
221 if(!user.has_value())
222 {
223 res.status = 500;
224 res.set_content("Owner of item not found", "text/plain");
225 return;
226 }
227 owner = user->name;
228 ASSIGN_OR_RESPOND_ERROR(items, data->itemsByParent(*item_id_maybe),
229 res);
230 }
231 else
232 {
233 ASSIGN_OR_RESPOND_ERROR(std::optional<User> user,
234 data->userByName(owner_or_id), res);
235 if(!user.has_value())
236 {
237 res.status = 404;
238 res.set_content(std::string("Unknown user: ") + owner_or_id,
239 "text/plain");
240 return;
241 }
242 owner = owner_or_id;
243 ASSIGN_OR_RESPOND_ERROR(items, data->itemsTopLevelByUser(user->id),
244 res);
245 }
246
247 nlohmann::json items_data = nlohmann::json::array();
248 for(const LinkItem& item: items)
249 {
250 if(item.visibility == LinkItem::PRIVATE)
251 {
252 if(session->status == SessionValidation::INVALID)
253 {
254 continue;
255 }
256 if(session->user.name != owner)
257 {
258 continue;
259 }
260 }
261 items_data.push_back(jsonFromItem(item));
262 }
263 nlohmann::json data = {
264 {"session_user", session->user.name},
265 {"owner", owner},
266 {"this_url", req.target},
267 {"items", std::move(items_data)},
268 };
269 std::string result = templates.render_file("dir.html", std::move(data));
270 res.set_content(result, "text/html");
198} 271}
199 272
200void App::handleLogin(Response& res) const 273void App::handleLogin(Response& res) const
@@ -253,11 +326,8 @@ void App::setup()
253 } 326 }
254 } 327 }
255 328
256 server.Get(getPath("index"), [&]([[maybe_unused]] const Request& req, Response& res) 329 server.Get(getPath("login"), [&]([[maybe_unused]] const Request& req,
257 { 330 Response& res)
258 handleIndex(res);
259 });
260 server.Get(getPath("login"), [&]([[maybe_unused]] const Request& req, Response& res)
261 { 331 {
262 handleLogin(res); 332 handleLogin(res);
263 }); 333 });
@@ -265,6 +335,16 @@ void App::setup()
265 { 335 {
266 handleOpenIDRedirect(req, res); 336 handleOpenIDRedirect(req, res);
267 }); 337 });
338 server.Get(getPath("index"), [&]([[maybe_unused]] const Request& req,
339 Response& res)
340 {
341 handleIndex(res);
342 });
343 server.Get(getPath("dir", "owner_or_id"),
344 [&]([[maybe_unused]] const Request& req, Response& res)
345 {
346 handleDir(req, res);
347 });
268} 348}
269 349
270mw::E<App::SessionValidation> App::validateSession(const Request& req) const 350mw::E<App::SessionValidation> App::validateSession(const Request& req) const
@@ -276,7 +356,7 @@ mw::E<App::SessionValidation> App::validateSession(const Request& req) const
276 } 356 }
277 357
278 auto cookies = parseCookies(req.get_header_value("Cookie")); 358 auto cookies = parseCookies(req.get_header_value("Cookie"));
279 if(auto it = cookies.find("shrt-access-token"); 359 if(auto it = cookies.find("webdir-access-token");
280 it != std::end(cookies)) 360 it != std::end(cookies))
281 { 361 {
282 spdlog::debug("Cookie has access token."); 362 spdlog::debug("Cookie has access token.");
@@ -289,7 +369,7 @@ mw::E<App::SessionValidation> App::validateSession(const Request& req) const
289 } 369 }
290 } 370 }
291 // No access token or access token expired 371 // No access token or access token expired
292 if(auto it = cookies.find("shrt-refresh-token"); 372 if(auto it = cookies.find("webdir-refresh-token");
293 it != std::end(cookies)) 373 it != std::end(cookies))
294 { 374 {
295 spdlog::debug("Cookie has refresh token."); 375 spdlog::debug("Cookie has refresh token.");