| 1 | // Copyright (c) 2019-2026 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by a GPL license that can be found in the LICENSE file. |
| 3 | module main |
| 4 | |
| 5 | import veb |
| 6 | import validation |
| 7 | |
| 8 | @['/:username/:repo_name/milestones'] |
| 9 | pub fn (mut app App) handle_get_repo_milestones(mut ctx Context, username string, repo_name string) veb.Result { |
| 10 | repo := app.find_repo_by_name_and_username(repo_name, username) or { return ctx.not_found() } |
| 11 | if !app.has_user_repo_read_access(ctx, ctx.user.id, repo.id) && !repo.is_public { |
| 12 | return ctx.not_found() |
| 13 | } |
| 14 | milestones := app.list_repo_milestones(repo.id) |
| 15 | return $veb.html('templates/milestones.html') |
| 16 | } |
| 17 | |
| 18 | @['/:username/:repo_name/milestones/new'] |
| 19 | pub fn (mut app App) new_milestone(mut ctx Context, username string, repo_name string) veb.Result { |
| 20 | if !ctx.logged_in { |
| 21 | return ctx.redirect_to_login() |
| 22 | } |
| 23 | repo := app.find_repo_by_name_and_username(repo_name, username) or { return ctx.not_found() } |
| 24 | if repo.user_id != ctx.user.id { |
| 25 | return ctx.redirect('/${username}/${repo_name}/milestones') |
| 26 | } |
| 27 | return $veb.html('templates/new/milestone.html') |
| 28 | } |
| 29 | |
| 30 | @['/:username/:repo_name/milestones'; post] |
| 31 | pub fn (mut app App) handle_create_milestone(mut ctx Context, username string, repo_name string) veb.Result { |
| 32 | if !ctx.logged_in { |
| 33 | return ctx.redirect_to_login() |
| 34 | } |
| 35 | repo := app.find_repo_by_name_and_username(repo_name, username) or { return ctx.not_found() } |
| 36 | if repo.user_id != ctx.user.id { |
| 37 | return ctx.redirect('/${username}/${repo_name}/milestones') |
| 38 | } |
| 39 | title := ctx.form['title'] |
| 40 | desc := ctx.form['description'] |
| 41 | due := parse_yyyy_mm_dd(ctx.form['due_date']) |
| 42 | if validation.is_string_empty(title) { |
| 43 | return ctx.redirect('/${username}/${repo_name}/milestones/new') |
| 44 | } |
| 45 | id := app.add_milestone(repo.id, title, desc, due) or { |
| 46 | ctx.error('Could not create milestone') |
| 47 | return ctx.redirect('/${username}/${repo_name}/milestones/new') |
| 48 | } |
| 49 | return ctx.redirect('/${username}/${repo_name}/milestones/${id}') |
| 50 | } |
| 51 | |
| 52 | @['/:username/:repo_name/milestones/:id'] |
| 53 | pub fn (mut app App) view_milestone(mut ctx Context, username string, repo_name string, id string) veb.Result { |
| 54 | repo := app.find_repo_by_name_and_username(repo_name, username) or { return ctx.not_found() } |
| 55 | if !app.has_user_repo_read_access(ctx, ctx.user.id, repo.id) && !repo.is_public { |
| 56 | return ctx.not_found() |
| 57 | } |
| 58 | milestone := app.find_milestone(id.int()) or { return ctx.not_found() } |
| 59 | if milestone.repo_id != repo.id { |
| 60 | return ctx.not_found() |
| 61 | } |
| 62 | can_edit := ctx.logged_in && repo.user_id == ctx.user.id |
| 63 | return $veb.html('templates/milestone.html') |
| 64 | } |
| 65 | |
| 66 | @['/:username/:repo_name/milestones/:id/close'; post] |
| 67 | pub fn (mut app App) handle_close_milestone(mut ctx Context, username string, repo_name string, id string) veb.Result { |
| 68 | if !ctx.logged_in { |
| 69 | return ctx.redirect_to_login() |
| 70 | } |
| 71 | repo := app.find_repo_by_name_and_username(repo_name, username) or { return ctx.not_found() } |
| 72 | milestone := app.find_milestone(id.int()) or { return ctx.not_found() } |
| 73 | if milestone.repo_id != repo.id || repo.user_id != ctx.user.id { |
| 74 | return ctx.redirect('/${username}/${repo_name}/milestones/${id}') |
| 75 | } |
| 76 | app.set_milestone_closed(milestone.id, true) or {} |
| 77 | return ctx.redirect('/${username}/${repo_name}/milestones/${id}') |
| 78 | } |
| 79 | |
| 80 | @['/:username/:repo_name/milestones/:id/reopen'; post] |
| 81 | pub fn (mut app App) handle_reopen_milestone(mut ctx Context, username string, repo_name string, id string) veb.Result { |
| 82 | if !ctx.logged_in { |
| 83 | return ctx.redirect_to_login() |
| 84 | } |
| 85 | repo := app.find_repo_by_name_and_username(repo_name, username) or { return ctx.not_found() } |
| 86 | milestone := app.find_milestone(id.int()) or { return ctx.not_found() } |
| 87 | if milestone.repo_id != repo.id || repo.user_id != ctx.user.id { |
| 88 | return ctx.redirect('/${username}/${repo_name}/milestones/${id}') |
| 89 | } |
| 90 | app.set_milestone_closed(milestone.id, false) or {} |
| 91 | return ctx.redirect('/${username}/${repo_name}/milestones/${id}') |
| 92 | } |
| 93 | |
| 94 | @['/:username/:repo_name/milestones/:id/delete'; post] |
| 95 | pub fn (mut app App) handle_delete_milestone(mut ctx Context, username string, repo_name string, id string) veb.Result { |
| 96 | if !ctx.logged_in { |
| 97 | return ctx.redirect_to_login() |
| 98 | } |
| 99 | repo := app.find_repo_by_name_and_username(repo_name, username) or { return ctx.not_found() } |
| 100 | milestone := app.find_milestone(id.int()) or { return ctx.not_found() } |
| 101 | if milestone.repo_id != repo.id || repo.user_id != ctx.user.id { |
| 102 | return ctx.redirect('/${username}/${repo_name}/milestones/${id}') |
| 103 | } |
| 104 | app.delete_milestone(milestone.id) or {} |
| 105 | return ctx.redirect('/${username}/${repo_name}/milestones') |
| 106 | } |
| 107 | |