diff --git a/packages/spike/src/gitea/api.ts b/packages/spike/src/gitea/api.ts index 3c0a1e9..e010961 100644 --- a/packages/spike/src/gitea/api.ts +++ b/packages/spike/src/gitea/api.ts @@ -1,46 +1,8 @@ import { insertComment } from "../db" +import { type Gitea } from "./types" const giteaUrl = "https://git.nose.space" -export namespace Gitea { - export type User = { - id: number - login: string - username: string - } - - export type PullRequest = { - id: number - number: number - title: string - body: string - user: User - html_url: string - state: string - merged: boolean - } - - export type Issue = { - id: number - number: number - pull_request?: { - html_url: string - } - } - - export type Comment = { - id: number - body: string - user: User - html_url: string - } - - export type Repository = { - name: string - full_name: string - } -} - type CreateCommentResponse = { id: number body: string diff --git a/packages/spike/src/gitea/types.ts b/packages/spike/src/gitea/types.ts new file mode 100644 index 0000000..0d18bab --- /dev/null +++ b/packages/spike/src/gitea/types.ts @@ -0,0 +1,366 @@ +// Types borrowed from gitea-js@1.23.0 +// https://github.com/anbraten/gitea-js + +export namespace Gitea { + export interface User { + id: number + login: string + full_name: string + avatar_url: string + active?: boolean + created?: string + description?: string + email?: string + followers_count?: number + following_count?: number + html_url?: string + is_admin?: boolean + language?: string + last_login?: string + location?: string + login_name?: string + prohibit_login?: boolean + restricted?: boolean + source_id?: number + starred_repos_count?: number + visibility?: string + website?: string + } + + export interface PullRequest { + id: number + number: number + title: string + body: string + user: User + html_url: string + state: StateType + merged: boolean + created_at: string + updated_at: string + draft: boolean + base: PRBranchInfo + head: PRBranchInfo + additions?: number + allow_maintainer_edit?: boolean + assignee?: User + assignees?: User[] + changed_files?: number + closed_at?: string + comments?: number + deletions?: number + diff_url?: string + due_date?: string + is_locked?: boolean + labels?: Label[] + merge_base?: string + merge_commit_sha?: string + mergeable?: boolean + merged_at?: string + merged_by?: User + milestone?: Milestone + patch_url?: string + pin_order?: number + requested_reviewers?: User[] + requested_reviewers_teams?: Team[] + review_comments?: number + url?: string + } + + export interface Issue { + id: number + number: number + title: string + body: string + user: User + html_url: string + state: StateType + created_at: string + updated_at: string + assets?: Attachment[] + assignee?: User + assignees?: User[] + closed_at?: string + comments?: number + due_date?: string + is_locked?: boolean + labels?: Label[] + milestone?: Milestone + original_author?: string + original_author_id?: number + pin_order?: number + pull_request?: PullRequestMeta + ref?: string + repository?: RepositoryMeta + url?: string + } + + export interface Comment { + id: number + body: string + user: User + html_url: string + created_at: string + updated_at: string + assets?: Attachment[] + issue_url?: string + original_author?: string + original_author_id?: number + pull_request_url?: string + } + + export interface Repository { + id: number + name: string + full_name: string + owner: User + html_url: string + private: boolean + allow_fast_forward_only_merge?: boolean + allow_merge_commits?: boolean + allow_rebase?: boolean + allow_rebase_explicit?: boolean + allow_rebase_update?: boolean + allow_squash_merge?: boolean + archived?: boolean + archived_at?: string + avatar_url?: string + clone_url?: string + created_at?: string + default_allow_maintainer_edit?: boolean + default_branch?: string + default_delete_branch_after_merge?: boolean + default_merge_style?: string + description?: string + empty?: boolean + external_tracker?: ExternalTracker + external_wiki?: ExternalWiki + fork?: boolean + forks_count?: number + has_actions?: boolean + has_issues?: boolean + has_packages?: boolean + has_projects?: boolean + has_pull_requests?: boolean + has_releases?: boolean + has_wiki?: boolean + ignore_whitespace_conflicts?: boolean + internal?: boolean + internal_tracker?: InternalTracker + language?: string + languages_url?: string + licenses?: string[] + link?: string + mirror?: boolean + mirror_interval?: string + mirror_updated?: string + object_format_name?: "sha1" | "sha256" + open_issues_count?: number + open_pr_counter?: number + original_url?: string + parent?: Repository + permissions?: Permission + projects_mode?: string + release_counter?: number + repo_transfer?: RepoTransfer + size?: number + ssh_url?: string + stars_count?: number + template?: boolean + topics?: string[] + updated_at?: string + url?: string + watchers_count?: number + website?: string + } + + // Supporting types + export type StateType = "open" | "closed" | "all" + + export interface PRBranchInfo { + label?: string + ref?: string + repo?: Repository + repo_id?: number + sha?: string + } + + export interface Label { + color?: string + description?: string + exclusive?: boolean + id?: number + is_archived?: boolean + name?: string + url?: string + } + + export interface Milestone { + closed_at?: string + closed_issues?: number + created_at?: string + description?: string + due_on?: string + id?: number + open_issues?: number + state?: StateType + title?: string + updated_at?: string + } + + export interface Team { + can_create_org_repo?: boolean + description?: string + id?: number + includes_all_repositories?: boolean + name?: string + organization?: Organization + permission?: "none" | "read" | "write" | "admin" | "owner" + units?: string[] + units_map?: Record + } + + export interface Organization { + avatar_url?: string + description?: string + email?: string + full_name?: string + id?: number + location?: string + name?: string + repo_admin_change_team_access?: boolean + username?: string + visibility?: string + website?: string + } + + export interface Attachment { + browser_download_url?: string + created_at?: string + download_count?: number + id?: number + name?: string + size?: number + uuid?: string + } + + export interface PullRequestMeta { + merged?: boolean + merged_at?: string + html_url?: string + } + + export interface RepositoryMeta { + full_name?: string + id?: number + name?: string + owner?: string + } + + export interface ExternalTracker { + external_tracker_format?: string + external_tracker_regexp_pattern?: string + external_tracker_style?: string + external_tracker_url?: string + } + + export interface ExternalWiki { + external_wiki_url?: string + } + + export interface InternalTracker { + allow_only_contributors_to_track_time?: boolean + enable_issue_dependencies?: boolean + enable_time_tracker?: boolean + } + + export interface Permission { + admin?: boolean + pull?: boolean + push?: boolean + } + + export interface RepoTransfer { + doer?: User + recipient?: User + teams?: Team[] + } + + // Webhook-specific stricter types (fields that are always present in webhooks) + export interface WebhookUser { + id: number + login: string + full_name: string + avatar_url: string + email?: string + } + + export interface WebhookRepository { + id: number + name: string + full_name: string + owner: User + html_url: string + description?: string + private: boolean + } + + export interface WebhookPullRequest { + id: number + number: number + title: string + body: string + user: WebhookUser + html_url: string + state: StateType + merged: boolean + merged_at?: string + created_at: string + updated_at: string + closed_at?: string + draft: boolean + base: PRBranchInfo + head: PRBranchInfo + } + + export interface WebhookIssue { + id: number + number: number + title: string + body: string + user: WebhookUser + html_url: string + state: StateType + pull_request?: PullRequestMeta + created_at: string + updated_at: string + } + + export interface WebhookComment { + id: number + body: string + user: WebhookUser + html_url: string + created_at: string + updated_at: string + } + + // Webhook payload types (not from gitea-js, these are custom) + export interface PullRequestWebhook { + action: "opened" | "closed" | "edited" | "synchronized" | "reviewed" | "reopened" + number: number + pull_request: WebhookPullRequest + repository: WebhookRepository + sender: WebhookUser + } + + export interface IssueCommentWebhook { + action: "created" | "edited" | "deleted" + issue: WebhookIssue + comment: WebhookComment + repository: WebhookRepository + sender: WebhookUser + } + + export type Webhook = PullRequestWebhook | IssueCommentWebhook +} diff --git a/packages/spike/src/gitea/webhook-handler.ts b/packages/spike/src/gitea/webhook-handler.ts index 3366ad6..de5eb5f 100644 --- a/packages/spike/src/gitea/webhook-handler.ts +++ b/packages/spike/src/gitea/webhook-handler.ts @@ -3,9 +3,10 @@ import { insertPR, getPRByGiteaId, insertComment, getCommentByGiteaId } from ".. import { getConfig } from "../config" import { ChannelType, ThreadChannel, type Channel } from "discord.js" import { assertNever } from "@workshop/shared/utils" -import { fetchPR, type Gitea } from "./api" +import { fetchPR } from "./api" +import type { Gitea } from "./types" -export const handleGiteaWebhook = async (payload: GiteaWebhook) => { +export const handleGiteaWebhook = async (payload: Gitea.Webhook) => { const isDev = process.env.NODE_ENV !== "production" if (!isDev && payload.repository.name === "ignore-me") return @@ -301,20 +302,3 @@ const convertMentionsToDiscord = async (text: string): Promise => { return result } - -interface PullRequestWebhook { - action: "opened" | "closed" | "edited" | "synchronized" | "reviewed" | "reopened" - number: number - pull_request: Gitea.PullRequest - repository: Gitea.Repository - sender: Gitea.User -} - -interface IssueCommentWebhook { - action: "created" | "edited" | "deleted" - issue: Gitea.Issue - comment: Gitea.Comment - repository: Gitea.Repository -} - -type GiteaWebhook = PullRequestWebhook | IssueCommentWebhook