diff --git a/api.v b/api.v index de768dee..9b897209 100644 --- a/api.v +++ b/api.v @@ -7,7 +7,7 @@ import json ['/api/:user/:repo/issues'] pub fn (mut app App) api_issues(user, repo string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.json('{}') } issues := app.find_repo_issues(app.repo.id) @@ -16,7 +16,7 @@ pub fn (mut app App) api_issues(user, repo string) vweb.Result { ['/api/:user/:repo/commits'] pub fn (mut app App) api_commits(user, repo string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.json('{}') } commits := app.find_repo_commits(app.repo.id) diff --git a/branch.v b/branch.v index c79672f5..2b123b22 100644 --- a/branch.v +++ b/branch.v @@ -123,3 +123,9 @@ fn (mut app App) contains_repo_branch(name string, repo_id int) bool { } return nr == 1 } + +fn (mut app App) delete_repo_branches(repo_id int) { + sql app.db { + delete from Branch where repo_id == repo_id + } +} diff --git a/file.v b/file.v index a0b88fbe..88e03798 100644 --- a/file.v +++ b/file.v @@ -73,3 +73,13 @@ fn (mut app App) find_repo_file_by_path(repo_id int, branch, path string) ?File } return file } + +fn (mut app App) delete_repo_files(repo_id int) { + sql app.db { + delete from File where repo_id == repo_id + } +} + +fn (mut app App) delete_repo_folder(path string) { + os.rmdir_all(os.real_path(path)) +} diff --git a/gitly.v b/gitly.v index eb496c10..71ec4d66 100644 --- a/gitly.v +++ b/gitly.v @@ -19,7 +19,9 @@ const ( max_login_attempts = 5 repo_storage_path = './repos' max_user_repos = 5 - max_repo_name_len = 20 + max_repo_name_len = 20 + max_namechanges = 3 + namechange_period = time.hour * 24 ) struct App { @@ -139,6 +141,7 @@ pub fn (mut app App) init_once() { pub fn (mut app App) init() { url := app.vweb.req.url + app.show_menu = false app.page_gen_time = '' app.info('\n\ninit() url=$url') app.path = '' @@ -212,7 +215,7 @@ pub fn (mut app App) repo_settings(user, repo string) vweb.Result { if !app.logged_in { return app.vweb.redirect('/$user/$repo') } - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.redirect('/$user/$repo') } if app.repo.user_id != app.user.id { @@ -223,18 +226,16 @@ pub fn (mut app App) repo_settings(user, repo string) vweb.Result { return $vweb.html() } +// Helper function +fn (mut app App) repo_belongs_to(user, repo string) bool { + return app.logged_in && app.exists_user_repo(user, repo) && app.repo.user_id == app.user.id +} + ['/:user/:repo/repo_settings_post'] pub fn (mut app App) repo_settings_post(user, repo string) vweb.Result { - if !app.logged_in { - return app.r_repo() - } - if !app.find_repo(user, repo) { + if !app.repo_belongs_to(user, repo) { return app.r_repo() } - if app.repo.user_id != app.user.id { - return app.r_repo() - } - if 'webhook_secret' in app.vweb.form && app.vweb.form['webhook_secret'] != app.repo.webhook_secret && app.vweb.form['webhook_secret'] != '' { webhook := sha1.hexhash(app.vweb.form['webhook_secret']) app.update_repo_webhook(app.repo.id, webhook) @@ -243,9 +244,52 @@ pub fn (mut app App) repo_settings_post(user, repo string) vweb.Result { return app.r_repo() } +['/:user/:repo/delete_repo_post'] +pub fn (mut app App) repo_delete_post(user, repo string) vweb.Result { + if !app.repo_belongs_to(user, repo) { + return app.r_repo() + } + if 'verify' in app.vweb.form && app.vweb.form['verify'] == '$user/$repo' { + go app.delete_repo(app.repo.id, app.repo.git_dir, app.repo.name) + } else { + app.vweb.error('Verification failed') + return app.repo_settings(user, repo) + } + + return app.r_home() +} + +['/:user/:repo/move_repo_post'] +pub fn (mut app App) repo_move_post(user, repo string) vweb.Result { + if !app.repo_belongs_to(user, repo) { + return app.r_repo() + } + if 'verify' in app.vweb.form && 'dest' in app.vweb.form && app.vweb.form['verify'] == '$user/$repo' { + uname := app.vweb.form['dest'] + dest_user := app.find_user_by_username(uname) or { + app.vweb.error('Unknown user $uname') + return app.repo_settings(user, repo) + } + if app.user_has_repo(dest_user.id, app.repo.name) { + app.vweb.error('User already owns repo $app.repo.name') + return app.repo_settings(user, repo) + } + if app.nr_user_repos(dest_user.id) >= max_user_repos { + app.vweb.error('User already reached the repo limit') + return app.repo_settings(user, repo) + } + app.move_repo_to_user(app.repo.id, dest_user.id, dest_user.username) + return app.vweb.redirect('/$dest_user.username/$app.repo.name') + } else { + app.vweb.error('Verification failed') + return app.repo_settings(user, repo) + } + return app.r_home() +} + ['/:user/:repo'] pub fn (mut app App) tree2(user, repo string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } return app.tree(user, repo, app.repo.primary_branch, '') @@ -254,7 +298,7 @@ pub fn (mut app App) tree2(user, repo string) vweb.Result { // pub fn (mut app App) tree(path string) { ['/:user/:repo/tree/:branch/:path...'] pub fn (mut app App) tree(user, repo, branch, path string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } println('\n\n\ntree() user="$user" repo="' + repo + '"') @@ -335,7 +379,7 @@ pub fn (mut app App) index() vweb.Result { ['/:user/:repo/update'] pub fn (mut app App) update(user, repo string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } /*secret := if 'X-Hub-Signature' in app.vweb.req.headers { app.vweb.req.headers['X-Hub-Signature'][5..] } else { '' } @@ -348,7 +392,7 @@ pub fn (mut app App) update(user, repo string) vweb.Result { if app.user.is_admin { go app.update_repo_data(app.repo) } - return app.r_home() + return app.r_repo() } pub fn (mut app App) new() vweb.Result { @@ -398,27 +442,7 @@ pub fn (mut app App) new_post() vweb.Result { } go app.update_repo() println('end go') - return app.vweb.redirect('/$app.user.username') -} - -['/:username'] -pub fn (mut app App) user(username string) vweb.Result { - println('user() name=$username') - app.show_menu = false - mut user := User{} - if username.len != 0 { - user = app.find_user_by_username(username) or { - return app.vweb.not_found() - } - } else { - return app.vweb.not_found() - } - user.b_avatar = user.avatar != '' - if !user.b_avatar { - user.avatar = user.username.bytes()[0].str() - } - repos := app.find_user_repos(user.id) - return $vweb.html() + return app.vweb.redirect('/$app.user.username/repos') } ['/:user/:repo/commits'] @@ -428,7 +452,7 @@ pub fn (mut app App) commits2(user, repo string) vweb.Result { ['/:user/:repo/commits/:page_str'] pub fn (mut app App) commits(user, repo, page_str string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } app.show_menu = true @@ -501,7 +525,7 @@ pub fn (mut app App) commits(user, repo, page_str string) vweb.Result { ['/:user/:repo/commit/:hash'] pub fn (mut app App) commit(user, repo, hash string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } commit := app.find_repo_commit_by_hash(app.repo.id, hash) @@ -525,7 +549,7 @@ pub fn (mut app App) issues2(user, repo string) vweb.Result { ['/:user/:repo/issues/:page_str'] pub fn (mut app App) issues(user, repo, page_str string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { app.vweb.not_found() } app.show_menu = true @@ -560,7 +584,7 @@ pub fn (mut app App) issues(user, repo, page_str string) vweb.Result { ['/:user/:repo/issue/:id'] pub fn (mut app App) issue(user, repo, id_str string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } app.show_menu = true @@ -579,7 +603,7 @@ pub fn (mut app App) issue(user, repo, id_str string) vweb.Result { ['/:user/:repo/pull/:id'] pub fn (mut app App) pull(user, repo, id_str string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } _ := app.path.split('/') @@ -599,7 +623,7 @@ pub fn (mut app App) pulls() vweb.Result { ['/:user/:repo/contributors'] pub fn (mut app App) contributors(user, repo string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } contributors := app.find_repo_registered_contributor(app.repo.id) @@ -608,7 +632,7 @@ pub fn (mut app App) contributors(user, repo string) vweb.Result { ['/:user/:repo/branches'] pub fn (mut app App) branches(user, repo string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } app.show_menu = true @@ -618,7 +642,7 @@ pub fn (mut app App) branches(user, repo string) vweb.Result { ['/:user/:repo/releases'] pub fn (mut app App) releases(user_str, repo string) vweb.Result { - if !app.find_repo(user_str, repo) { + if !app.exists_user_repo(user_str, repo) { return app.vweb.not_found() } app.show_menu = true @@ -652,7 +676,7 @@ pub fn (mut app App) releases(user_str, repo string) vweb.Result { ['/:user/:repo/blob/:branch/:path...'] pub fn (mut app App) blob(user, repo, branch, path string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } app.path = path @@ -696,7 +720,7 @@ pub fn (mut app App) blob(user, repo, branch, path string) vweb.Result { ['/:user/:repo/issues/new'] pub fn (mut app App) new_issue(user, repo string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } if !app.logged_in { @@ -707,7 +731,7 @@ pub fn (mut app App) new_issue(user, repo string) vweb.Result { ['/:user/:repo/new_issue_post'] pub fn (mut app App) new_issue_post(user, repo string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } if !app.logged_in || (app.logged_in && app.user.nr_posts >= posts_per_day) { @@ -733,7 +757,7 @@ pub fn (mut app App) new_issue_post(user, repo string) vweb.Result { ['/:user/:repo/comment_post'] pub fn (mut app App) comment_post(user, repo string) vweb.Result { - if !app.find_repo(user, repo) { + if !app.exists_user_repo(user, repo) { return app.vweb.not_found() } text := app.vweb.form['text'] diff --git a/hl/hl.v b/hl/hl.v index bdfc00c2..65124250 100644 --- a/hl/hl.v +++ b/hl/hl.v @@ -33,7 +33,7 @@ pub fn highlight_text(st, ext string, commit bool) (string, int, int) { res << ''.bytes() res << `\n` if !is_single_line(st) { - res << '\n\n\n\n
1'.bytes() + res << '
1'.bytes() lines++ } mut in_comment := false @@ -52,9 +52,9 @@ pub fn highlight_text(st, ext string, commit bool) (string, int, int) { class = 'class="d"' } res << - '
$lines'.bytes() + '
$lines'.bytes() } else { - res << '
$lines'.bytes() + res << '
$lines'.bytes() } if in_line_comment { in_line_comment = false diff --git a/issue.v b/issue.v index 4bb2675f..27af1fbe 100644 --- a/issue.v +++ b/issue.v @@ -19,6 +19,8 @@ mut: status IssueStatus [skip] linked_issues []int [skip] author_name string [skip] + repo_author string [skip] + repo_name string [skip] } enum IssueStatus { @@ -81,6 +83,18 @@ fn (mut app App) find_repo_prs(repo_id int) []Issue { return issues } +fn (mut app App) find_user_issues(user_id int) []Issue { + return sql app.db { + select from Issue where author_id == user_id && is_pr == false + } +} + +fn (mut app App) delete_repo_issues(repo_id int) { + sql app.db { + delete from Issue where repo_id == repo_id + } +} + fn (mut app App) inc_issue_comments(id int) { sql app.db { update Issue set nr_comments = nr_comments + 1 where id == id diff --git a/lang_stat.v b/lang_stat.v index 4daddbff..40783170 100644 --- a/lang_stat.v +++ b/lang_stat.v @@ -36,7 +36,7 @@ pub fn (l &LangStat) pct_html() vweb.RawHtml { pub fn (mut app App) find_repo_lang_stats(repo_id int) []LangStat { lang_stats := sql app.db { - select from LangStat where repo_id == repo_id + select from LangStat where repo_id == repo_id order by pct desc } return lang_stats } diff --git a/login.v b/login.v index 89f7453b..2deaa395 100644 --- a/login.v +++ b/login.v @@ -6,7 +6,6 @@ import vweb import time import rand import math -import strings //['/login'] pub fn (mut app App) login() vweb.Result { @@ -58,8 +57,9 @@ pub fn (mut app App) auth_user(user User, ip string) { token := app.add_token(user.id, ip) app.update_user_login_attempts(user.id, 0) //println('cookie: setting token=$token id=$user.id') - app.vweb.set_cookie(name: 'id', value:user.id.str()) - app.vweb.set_cookie(name:'token', value:token) + expire_date := time.now().add_days(200) + app.vweb.set_cookie(name: 'id', value:user.id.str(), expires: expire_date) + app.vweb.set_cookie(name:'token', value:token, expires: expire_date) //app.vweb.set_cookie_with_expire_date('id', user.id.str(), expires) //app.vweb.set_cookie_with_expire_date('token', token, expires) } diff --git a/release.v b/release.v index 42bc9c24..cec0264a 100644 --- a/release.v +++ b/release.v @@ -25,3 +25,10 @@ pub fn (mut app App) find_repo_releases(repo_id int) []Release { select from Release where repo_id == repo_id } } + +pub fn (mut app App) delete_repo_releases(repo_id int) { + sql app.db { + delete from Release where repo_id == repo_id + } +} + diff --git a/repo_db.v b/repo_db.v index 6da9c9dd..39e85aea 100644 --- a/repo_db.v +++ b/repo_db.v @@ -82,6 +82,8 @@ fn (mut app App) create_tables() { 'avatar text default ""' 'nr_posts integer default 0' 'last_post_time integer default 0' + 'nr_namechanges integer default 0' + 'last_namechange_time integer default 0' 'is_github int default 0' 'is_blocked int default 0' 'is_registered int default 0' @@ -216,7 +218,13 @@ fn (app &App) find_user_repos(user_id int) []Repo { } } -fn (app &App) find_repo(user, name string) bool { +fn (app &App) find_repo_by_id(repo_id int) Repo { + return sql app.db { + select from Repo where id == repo_id + } +} + +fn (app &App) exists_user_repo(user, name string) bool { if user.len == 0 || name.len == 0 { app.info('User or repo was not found') return false @@ -282,3 +290,50 @@ fn (mut app App) insert_repo(repo Repo) { insert repo into Repo } } + +fn (mut app App) delete_repo(id int, path, name string) { + // Remove repo + sql app.db { + delete from Repo where id == id + } + app.info('Removed repo entry ($id, $name)') + + // Remove all commits + sql app.db { + delete from Commit where repo_id == id + } + app.info('Removed repo commits ($id, $name)') + + // Remove all issues & prs + app.delete_repo_issues(id) + app.info('Removed repo issues ($id, $name)') + + // Remove all branches + app.delete_repo_branches(id) + app.info('Removed repo branches ($id, $name)') + + // Remove all releases + app.delete_repo_releases(id) + app.info('Removed repo releases ($id, $name)') + + // Remove all files + app.delete_repo_files(id) + app.info('Removed repo files ($id, $name)') + + // Remove physical files + app.delete_repo_folder(path) + app.info('Removed repo folder ($id, $name)') +} + +fn (mut app App) move_repo_to_user(repo_id, user_id int, user_name string) { + sql app.db { + update Repo set user_id = user_id, user_name = user_name where id == repo_id + } +} + +fn (mut app App) user_has_repo(user_id int, repo_name string) bool { + count := sql app.db { + select count from Repo where user_id == user_id && name == repo_name + } + return count >= 0 +} diff --git a/security_log.v b/security_log.v index dad37a4f..eafc4b15 100644 --- a/security_log.v +++ b/security_log.v @@ -3,8 +3,6 @@ module main import vweb -import json -import net.http enum SecurityLogKind { registered diff --git a/static/css/gitly.scss b/static/css/gitly.scss index 0a087146..7ddbd6ed 100644 --- a/static/css/gitly.scss +++ b/static/css/gitly.scss @@ -49,6 +49,14 @@ pre > code { background-color: #eff0f1; display: block; } +.no_select { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} .content { max-width: $mob-max; margin: 0 auto; @@ -147,6 +155,16 @@ pre > code { width: 40px; margin: 0; } + +.new { + display: flex; + position: absolute; + top: 0; + right: 0; + padding-right: 150px; +} + + #header .login-button { color: $white; line-height: 50px; diff --git a/templates/header.html b/templates/header.html index dd9a8747..a00eb2ae 100644 --- a/templates/header.html +++ b/templates/header.html @@ -42,6 +42,11 @@ @app.repo.name @end } + @if app.logged_in +
+ New repo +
+ @end
@if app.logged_in .avatar { @@ -57,10 +62,16 @@ @app.user.username } .links { - Repositories + Repositories Issues Pull requests } + .links { + Settings + } + @if app.user.is_admin + Admin Panel + @end .links { Sign out } diff --git a/templates/repo/settings.html b/templates/repo/settings.html index b63823af..27014fb8 100644 --- a/templates/repo/settings.html +++ b/templates/repo/settings.html @@ -1,8 +1,24 @@ @header +.div-center { + .form-error { + @app.vweb.form_error + } -
- - -
+
+ + +
+
+ + +
+ +
+ + + +
+ +} @footer diff --git a/templates/security.html b/templates/security.html index 7fbd6d37..bd2d65a9 100644 --- a/templates/security.html +++ b/templates/security.html @@ -7,7 +7,7 @@

Security log

@for log in logs
- + @log
@end diff --git a/templates/user.html b/templates/user.html index 15a13cda..9a2aa40d 100644 --- a/templates/user.html +++ b/templates/user.html @@ -1,6 +1,5 @@ @header -
@if user.b_avatar @@ -11,20 +10,4 @@

@user.username

@user.name

-@if app.logged_in -New repository
-@end - -@if repos.len > 0 -@for repo in repos - -

@repo.name

-

@repo.description

-
-@end -@else -

No repositories

-@end - - @footer diff --git a/templates/user/issues.html b/templates/user/issues.html new file mode 100644 index 00000000..b8d25f1b --- /dev/null +++ b/templates/user/issues.html @@ -0,0 +1,55 @@ +@header + +
+@if user.b_avatar + +@else + @user.avatar +@end +
+

@user.username s issues

+ +.commit-day { +} + +.clog-block { + @for issue in issues + .clog { + .clog-msg { + @issue.title + } + span.clog-author { + #@issue.id opened @issue.relative_time() + by @issue.author_name + } + @if issue.nr_comments > 0 + .comments { + @if issue.nr_comments == 1 + @issue.nr_comments comment + @else + @issue.nr_comments comments + @end + } + @end + } + @end +} + +
+
+@if first + Prev +@else + Prev/ +@end + +@if last + Next +@else + Next +@end +
+ + + +@footer diff --git a/templates/user/repos.html b/templates/user/repos.html new file mode 100644 index 00000000..178df3c0 --- /dev/null +++ b/templates/user/repos.html @@ -0,0 +1,23 @@ +@header + +
+@if user.b_avatar + +@else + @user.avatar +@end +
+

@user.username s repositories

+ +@if repos.len > 0 +@for repo in repos + +

@repo.name

+

@repo.description

+
+@end +@else +

No repositories

+@end + +@footer diff --git a/templates/user/settings.html b/templates/user/settings.html new file mode 100644 index 00000000..8486968f --- /dev/null +++ b/templates/user/settings.html @@ -0,0 +1,16 @@ +@header + +

User settings

+ +.div-center { + .form-error { + @app.vweb.form_error + } + +
+ + +
+} + +@footer diff --git a/user.v b/user.v index 0b803174..4d24ad18 100644 --- a/user.v +++ b/user.v @@ -18,6 +18,8 @@ struct User { is_admin bool oauth_state string [skip] // for github oauth XSRF protection mut: + nr_namechanges int + last_namechange_time int nr_posts int last_post_time int avatar string @@ -351,3 +353,19 @@ pub fn (mut app App) client_ip(username string) ?string { ip := app.vweb.conn.peer_ip() or { return none } return make_password(ip, '${username}token') } + +fn (mut app App) change_username(user_id int, username string) { + sql app.db { + update User set username = username where id == user_id + } + sql app.db { + update Repo set user_name = username where user_id == user_id + } +} + +fn (mut app App) inc_namechanges(user_id int) { + now := int(time.now().unix) + sql app.db { + update User set nr_namechanges = nr_namechanges + 1, last_namechange_time = now where id == user_id + } +} diff --git a/usersite.v b/usersite.v new file mode 100644 index 00000000..6337006a --- /dev/null +++ b/usersite.v @@ -0,0 +1,132 @@ +module main + +import time +import vweb + +fn (mut app App) check_username(user string) (bool, User) { + if user.len == 0 { + return false, User{} + } + mut u := app.find_user_by_username(user) or { + return false, User{} + } + u.b_avatar = u.avatar != '' + if !u.b_avatar { + u.avatar = u.username.bytes()[0].str() + } + return u.is_registered, u +} + +['/:username'] +pub fn (mut app App) user(username string) vweb.Result { + println('user() name=$username') + app.show_menu = false + exists, u := app.check_username(username) + if !exists { + return app.vweb.not_found() + } + user := u + return $vweb.html() +} + + +['/:username/repos'] +pub fn (mut app App) user_repos(username string) vweb.Result { + exists, u := app.check_username(username) + if !exists { + return app.vweb.not_found() + } + user := u + repos := app.find_user_repos(user.id) + return $vweb.html() +} + +['/:username/issues'] +pub fn (mut app App) user_issues2(username string) vweb.Result { + return app.user_issues(username, '0') +} + +['/:username/issues/:page_str'] +pub fn (mut app App) user_issues(username, page_str string) vweb.Result { + if !app.logged_in { + return app.vweb.not_found() + } + if app.user.username != username { + return app.vweb.not_found() + } + exists, u := app.check_username(username) + if !exists { + return app.vweb.not_found() + } + user := u + page := if page_str.len >= 1 { page_str.int() } else { 0 } + mut issues := app.find_user_issues(user.id) + mut first := false + mut last := false + for i, issue in issues { + issues[i].author_name = username + repo := app.find_repo_by_id(issue.repo_id) + issues[i].repo_author = repo.user_name + issues[i].repo_name = repo.name + } + if issues.len > commits_per_page { + offset := page * commits_per_page + delta := issues.len - offset + if delta > 0 { + if delta == issues.len && page == 0 { + first = true + } else { + last = true + } + } + } else { + last = true + first = true + } + mut last_site := 0 + if page > 0 { + last_site = page - 1 + } + next_site := page + 1 + return $vweb.html() +} + +/* +['/:user/prs'] +pub fn (mut app App) user_pullrequests(user string) vweb.Result {} +*/ +['/:user/settings'] +pub fn (mut app App) user_settings(user string) vweb.Result { + return $vweb.html() +} + +['/:user/settings_post'] +pub fn (mut app App) user_settings_post(user string) vweb.Result { + if !app.logged_in || user != app.user.username { + return app.r_home() + } + name := if 'name' in app.vweb.form { app.vweb.form['name'] } else { '' } + if name == '' { + app.vweb.error('New name is empty') + return app.user_settings(user) + } + if name == user { + return app.user_settings(user) + } + if app.user.nr_namechanges > max_namechanges { + app.vweb.error('You can not change your username, limit reached') + return app.user_settings(user) + } + if app.user.last_namechange_time == 0 || app.user.last_namechange_time + namechange_period <= time.now().unix { + u := app.find_user_by_username(name) or { User{} } + if u.id != 0 { + app.vweb.error('Name already exists') + return app.user_settings(user) + } + app.change_username(app.user.id, name) + app.inc_namechanges(app.user.id) + return app.vweb.redirect('/$name') + } + app.vweb.error('You need to wait until you can change the name again') + return app.user_settings(user) +}