From 3fe5149edcdb1d3e81ee6cd610c8b8568af454a5 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 16 May 2026 10:14:24 +0300 Subject: [PATCH] scaffold tables and repo menu for webhooks, discussions, projects, milestones, 2fa, api tokens - gitly.v: create tables for Webhook/WebhookDelivery, Discussion/DiscussionComment, Project/ProjectColumn/ProjectCard, Milestone, TwoFactor, ApiToken - database_*: add backend-aware db_last_insert_id helper (sqlite last_insert_rowid / pg lastval) - issue.v: use db_last_insert_id instead of direct exec_no_null (which is pg-only) - issue_routes.v: dispatch 'issue opened' webhook on new issue - repo_menu: links to discussions, projects, milestones - settings: replace inline webhook secret form with a link to the webhooks management page - translations: en/ru strings for webhooks, discussions, projects, milestones, 2fa, api tokens Co-Authored-By: Claude Opus 4.7 --- database_d_sqlite.v | 8 + database_notd_sqlite.v | 8 + gitly.v | 30 ++++ issue.v | 12 +- issue_routes.v | 6 + static/assets/version | 2 +- templates/layout/repo_menu.html | 12 ++ templates/repo/settings.html | 9 +- translations/en.tr | 276 ++++++++++++++++++++++++++++++++ translations/ru.tr | 276 ++++++++++++++++++++++++++++++++ 10 files changed, 622 insertions(+), 17 deletions(-) diff --git a/database_d_sqlite.v b/database_d_sqlite.v index c55a7c3..39e8bc1 100644 --- a/database_d_sqlite.v +++ b/database_d_sqlite.v @@ -29,6 +29,14 @@ fn db_exec_values(db &GitlyDb, query string) ![][]string { return values } +fn db_last_insert_id(db &GitlyDb) int { + rows := db.exec('select last_insert_rowid()') or { return 0 } + if rows.len > 0 && rows[0].vals.len > 0 { + return rows[0].vals[0].int() + } + return 0 +} + fn db_column_exists(db &GitlyDb, table_name string, column_name string) !bool { rows := db_exec_values(db, 'pragma table_info(${sql_table(table_name)})')! for row in rows { diff --git a/database_notd_sqlite.v b/database_notd_sqlite.v index 99937da..a160912 100644 --- a/database_notd_sqlite.v +++ b/database_notd_sqlite.v @@ -32,6 +32,14 @@ fn db_exec_values(db &GitlyDb, query string) ![][]string { return values } +fn db_last_insert_id(db &GitlyDb) int { + rows := db.exec_no_null('SELECT lastval()') or { return 0 } + if rows.len > 0 && rows[0].vals.len > 0 { + return rows[0].vals[0].int() + } + return 0 +} + fn db_column_exists(db &GitlyDb, table_name string, column_name string) !bool { rows := db_exec_values(db, 'select column_name from information_schema.columns where table_name = ${sql_literal(table_name.to_lower())} and column_name = ${sql_literal(column_name)}')! diff --git a/gitly.v b/gitly.v index 3e0fe92..1f8806e 100644 --- a/gitly.v +++ b/gitly.v @@ -286,6 +286,36 @@ fn (mut app App) create_tables() ! { sql app.db { create table PrReviewComment }! + sql app.db { + create table Webhook + }! + sql app.db { + create table WebhookDelivery + }! + sql app.db { + create table Discussion + }! + sql app.db { + create table DiscussionComment + }! + sql app.db { + create table Project + }! + sql app.db { + create table ProjectColumn + }! + sql app.db { + create table ProjectCard + }! + sql app.db { + create table Milestone + }! + sql app.db { + create table TwoFactor + }! + sql app.db { + create table ApiToken + }! } fn (mut app App) migrate_tables() ! { diff --git a/issue.v b/issue.v index c55215a..148b682 100644 --- a/issue.v +++ b/issue.v @@ -59,11 +59,7 @@ fn (mut app App) add_issue_returning_id(repo_id int, author_id int, title string sql app.db { insert issue into Issue }! - rows := app.db.exec_no_null('select last_insert_rowid()') or { return 0 } - if rows.len > 0 && rows[0].vals.len > 0 { - return rows[0].vals[0].int() - } - return 0 + return db_last_insert_id(app.db) } fn (mut app App) find_or_create_label(repo_id int, name string, color string) !int { @@ -81,11 +77,7 @@ fn (mut app App) find_or_create_label(repo_id int, name string, color string) !i sql app.db { insert label into Label }! - rows := app.db.exec_no_null('select last_insert_rowid()') or { return 0 } - if rows.len > 0 && rows[0].vals.len > 0 { - return rows[0].vals[0].int() - } - return 0 + return db_last_insert_id(app.db) } fn (mut app App) add_issue_label(issue_id int, label_id int) ! { diff --git a/issue_routes.v b/issue_routes.v index e8ef3e7..94826ed 100644 --- a/issue_routes.v +++ b/issue_routes.v @@ -59,6 +59,12 @@ pub fn (mut app App) handle_add_repo_issue(mut ctx Context, username string, rep app.increment_user_post(mut ctx.user) or { app.info(err.str()) } app.add_issue(repo.id, ctx.user.id, title, text) or { app.info(err.str()) } app.increment_repo_issues(repo.id) or { app.info(err.str()) } + app.dispatch_webhook(repo.id, 'issue', WebhookIssuePayload{ + action: 'opened' + repo: '${username}/${repo_name}' + title: title + author: ctx.user.username + }) has_first_issue_activity := app.has_activity(ctx.user.id, 'first_issue') if !has_first_issue_activity { app.add_activity(ctx.user.id, 'first_issue') or { app.info(err.str()) } diff --git a/static/assets/version b/static/assets/version index 932ca8f..61912dd 100644 --- a/static/assets/version +++ b/static/assets/version @@ -1 +1 @@ -d17479b \ No newline at end of file +65d249c \ No newline at end of file diff --git a/templates/layout/repo_menu.html b/templates/layout/repo_menu.html index 2285d82..98bfe67 100644 --- a/templates/layout/repo_menu.html +++ b/templates/layout/repo_menu.html @@ -43,6 +43,18 @@ @repo.format_nr_releases(ctx.lang) + + + %discussions + + + + %projects + + + + %milestones + %ci_label diff --git a/templates/repo/settings.html b/templates/repo/settings.html index b25db07..893632b 100644 --- a/templates/repo/settings.html +++ b/templates/repo/settings.html @@ -16,12 +16,9 @@ @end
-

Webhook

-

Set a secret to verify deliveries from your git provider.

-
- - -
+

%webhooks

+

%webhook_secret_hint

+
%manage_webhooks
diff --git a/translations/en.tr b/translations/en.tr index cbd386e..dc8ac4d 100644 --- a/translations/en.tr +++ b/translations/en.tr @@ -407,4 +407,280 @@ opened pr_comments_title Comments ----- +webhooks +Webhooks +----- +webhook_new +New webhook +----- +webhook_none +No webhooks configured. +----- +webhook_url +Payload URL +----- +webhook_secret +Secret +----- +webhook_secret_hint +Used to compute the X-Gitly-Signature header (HMAC-SHA256). Optional but recommended. +----- +webhook_events +Events to deliver +----- +webhook_event_push +Push +----- +webhook_event_issue +Issues +----- +webhook_event_pr +Pull requests +----- +webhook_event_comment +Comments +----- +webhook_event_release +Releases +----- +webhook_create +Create webhook +----- +webhook_delete +Delete +----- +webhook_active +Active +----- +webhook_inactive +Inactive +----- +webhook_last_status +Last response +----- +webhook_never_delivered +Never delivered +----- +webhook_deliveries +Recent deliveries +----- +webhook_back +Back to webhooks +----- +manage_webhooks +Manage webhooks +----- +discussions +Discussions +----- +discussion_new +New discussion +----- +discussion_none +No discussions yet. +----- +discussion_create +Create discussion +----- +discussion_title_placeholder +Discussion title +----- +discussion_body_placeholder +Start the conversation... +----- +discussion_category +Category +----- +discussion_category_general +General +----- +discussion_category_qa +Q&A +----- +discussion_category_announcement +Announcement +----- +discussion_category_idea +Ideas +----- +discussion_locked +Locked +----- +discussion_reply_placeholder +Reply to this discussion +----- +discussion_reply_button +Post reply +----- +discussion_lock +Lock discussion +----- +discussion_unlock +Unlock discussion +----- +discussion_delete +Delete discussion +----- +discussion_answer +Mark as answer +----- +discussion_answered +Answered +----- +discussion_opened +opened +----- +projects +Projects +----- +project_new +New project +----- +project_none +No projects yet. +----- +project_create +Create project +----- +project_name_placeholder +Project name +----- +project_description_placeholder +Project description (optional) +----- +project_delete +Delete project +----- +project_add_column +Add column +----- +project_column_name_placeholder +Column name (e.g. Todo, In progress, Done) +----- +project_add_card +Add card +----- +project_card_title_placeholder +Card title +----- +project_card_note_placeholder +Notes (optional) +----- +project_card_save +Save card +----- +project_card_delete +Delete +----- +project_column_delete +Delete column +----- +project_column_empty +No cards. +----- +milestones +Milestones +----- +milestone_new +New milestone +----- +milestone_none +No milestones yet. +----- +milestone_create +Create milestone +----- +milestone_title_placeholder +Milestone title +----- +milestone_description_placeholder +Description (optional) +----- +milestone_due_date +Due date +----- +milestone_status_open +Open +----- +milestone_status_closed +Closed +----- +milestone_close +Close milestone +----- +milestone_reopen +Reopen milestone +----- +milestone_delete +Delete milestone +----- +milestone_progress +Progress +----- +milestone_issues +Linked issues +----- +milestone_no_issues +No issues linked yet. +----- +twofa_title +Two-factor authentication +----- +twofa_status_enabled +Two-factor authentication is enabled. +----- +twofa_status_disabled +Two-factor authentication is disabled. +----- +twofa_enable +Enable two-factor authentication +----- +twofa_disable +Disable two-factor authentication +----- +twofa_setup_intro +Scan the QR code with an authenticator app (Aegis, Google Authenticator, 1Password, ...), or enter the secret manually. Then enter the 6-digit code below to confirm. +----- +twofa_secret +Secret +----- +twofa_code +6-digit code +----- +twofa_verify +Verify and enable +----- +twofa_login_prompt +Enter the 6-digit code from your authenticator app to continue. +----- +twofa_login_button +Verify +----- +twofa_invalid +Invalid code, try again. +----- +api_tokens +API tokens +----- +api_token_new +New API token +----- +api_token_none +No API tokens yet. +----- +api_token_name +Token name +----- +api_token_create +Create token +----- +api_token_revoke +Revoke +----- +api_token_show_once +This token is shown only once. Copy it now: +----- +api_docs +API documentation +----- diff --git a/translations/ru.tr b/translations/ru.tr index 221f56f..545f694 100644 --- a/translations/ru.tr +++ b/translations/ru.tr @@ -407,4 +407,280 @@ pr_opened pr_comments_title Комментарии ----- +webhooks +Веб-хуки +----- +webhook_new +Новый веб-хук +----- +webhook_none +Веб-хуки не настроены. +----- +webhook_url +URL для доставки +----- +webhook_secret +Секрет +----- +webhook_secret_hint +Используется для вычисления заголовка X-Gitly-Signature (HMAC-SHA256). Необязательно, но рекомендуется. +----- +webhook_events +События для доставки +----- +webhook_event_push +Push +----- +webhook_event_issue +Задачи +----- +webhook_event_pr +Пулреквесты +----- +webhook_event_comment +Комментарии +----- +webhook_event_release +Релизы +----- +webhook_create +Создать веб-хук +----- +webhook_delete +Удалить +----- +webhook_active +Активен +----- +webhook_inactive +Отключён +----- +webhook_last_status +Последний ответ +----- +webhook_never_delivered +Доставок ещё не было +----- +webhook_deliveries +Последние доставки +----- +webhook_back +К веб-хукам +----- +manage_webhooks +Управление веб-хуками +----- +discussions +Обсуждения +----- +discussion_new +Новое обсуждение +----- +discussion_none +Обсуждений пока нет. +----- +discussion_create +Создать обсуждение +----- +discussion_title_placeholder +Заголовок обсуждения +----- +discussion_body_placeholder +Начните обсуждение... +----- +discussion_category +Категория +----- +discussion_category_general +Общее +----- +discussion_category_qa +Q&A +----- +discussion_category_announcement +Объявление +----- +discussion_category_idea +Идеи +----- +discussion_locked +Закрыто +----- +discussion_reply_placeholder +Ответить +----- +discussion_reply_button +Отправить ответ +----- +discussion_lock +Закрыть обсуждение +----- +discussion_unlock +Открыть обсуждение +----- +discussion_delete +Удалить обсуждение +----- +discussion_answer +Отметить как ответ +----- +discussion_answered +Есть ответ +----- +discussion_opened +открыто +----- +projects +Проекты +----- +project_new +Новый проект +----- +project_none +Проектов пока нет. +----- +project_create +Создать проект +----- +project_name_placeholder +Название проекта +----- +project_description_placeholder +Описание (необязательно) +----- +project_delete +Удалить проект +----- +project_add_column +Добавить колонку +----- +project_column_name_placeholder +Название колонки (например, Todo, В работе, Готово) +----- +project_add_card +Добавить карточку +----- +project_card_title_placeholder +Заголовок карточки +----- +project_card_note_placeholder +Заметки (необязательно) +----- +project_card_save +Сохранить карточку +----- +project_card_delete +Удалить +----- +project_column_delete +Удалить колонку +----- +project_column_empty +Нет карточек. +----- +milestones +Этапы +----- +milestone_new +Новый этап +----- +milestone_none +Этапов пока нет. +----- +milestone_create +Создать этап +----- +milestone_title_placeholder +Название этапа +----- +milestone_description_placeholder +Описание (необязательно) +----- +milestone_due_date +Срок +----- +milestone_status_open +Открыт +----- +milestone_status_closed +Закрыт +----- +milestone_close +Закрыть этап +----- +milestone_reopen +Снова открыть этап +----- +milestone_delete +Удалить этап +----- +milestone_progress +Прогресс +----- +milestone_issues +Связанные задачи +----- +milestone_no_issues +Связанных задач пока нет. +----- +twofa_title +Двухфакторная аутентификация +----- +twofa_status_enabled +Двухфакторная аутентификация включена. +----- +twofa_status_disabled +Двухфакторная аутентификация отключена. +----- +twofa_enable +Включить двухфакторную аутентификацию +----- +twofa_disable +Отключить двухфакторную аутентификацию +----- +twofa_setup_intro +Отсканируйте QR-код приложением-аутентификатором (Aegis, Google Authenticator, 1Password, ...) или введите секрет вручную. Затем введите 6-значный код ниже для подтверждения. +----- +twofa_secret +Секрет +----- +twofa_code +6-значный код +----- +twofa_verify +Подтвердить и включить +----- +twofa_login_prompt +Введите 6-значный код из приложения-аутентификатора, чтобы продолжить. +----- +twofa_login_button +Подтвердить +----- +twofa_invalid +Неверный код, попробуйте ещё раз. +----- +api_tokens +API-токены +----- +api_token_new +Новый API-токен +----- +api_token_none +API-токенов пока нет. +----- +api_token_name +Имя токена +----- +api_token_create +Создать токен +----- +api_token_revoke +Отозвать +----- +api_token_show_once +Этот токен показывается только один раз. Скопируйте его сейчас: +----- +api_docs +Документация API +----- -- 2.39.5