diff options
Diffstat (limited to 'src/app.cpp')
-rw-r--r-- | src/app.cpp | 150 |
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 | ||
114 | nlohmann::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 | ||
116 | App::App(const Configuration& conf, | 131 | App::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 | ||
195 | void App::handleIndex(Response& res) const | 189 | void App::handleIndex(Response& res) const |
196 | { | 190 | { |
197 | res.set_redirect(urlFor("links"), 301); | 191 | res.set_redirect(urlFor("dir", "mw"), 301); |
192 | } | ||
193 | |||
194 | void 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 | ||
200 | void App::handleLogin(Response& res) const | 273 | void 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 | ||
270 | mw::E<App::SessionValidation> App::validateSession(const Request& req) const | 350 | mw::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."); |