Compare commits

...

16 Commits

Author SHA1 Message Date
ᴊᴏᴇ ᴄʜᴇɴ
91a705c6a7 Merge 335aeefbe6 into 295bfba729 2026-02-13 23:21:10 -05:00
Joe Chen
335aeefbe6 all: upgrade to Go 1.26
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 23:21:00 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
295bfba729 context: reject access tokens passed via URL query parameters (#8177) 2026-02-13 15:27:48 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
ac21150a53 template: escape untrusted names in locale strings piped through Safe (#8176)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 21:42:23 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
a000f0c7a6 database: use safe git-module API for tag deletion (#8175)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 21:24:44 -05:00
Joe Chen
a976fd2f9c chore: minor case fixup
[skip ci]
2026-02-12 21:09:36 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
441c64d7bd markup: restrict data URI scheme to safe image MIME types (#8174)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 22:26:31 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
5c67d47512 database: remove MSSQL backend support (#8173) 2026-02-10 18:41:31 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
94d6e53dc2 email: replace gomail with go-mail (#8164)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 17:44:01 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
a1fa62b270 all: decouple API types from go-gogs-client SDK (#8171)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-10 10:56:17 -05:00
Copilot
317e28b908 Remove non-existent README_ZH.md from release workflow (#8170)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-02-09 09:22:24 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
069d3535d6 chore: fix broken links to gogs.io (#8169) 2026-02-09 09:09:59 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
81ee883644 lfs: verify content hash and prevent object overwrite (#8166)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
2026-02-08 17:14:12 -05:00
Copilot
400ae7bd28 Add CLI reference doc page under advancing and normalize gogs command references (#8165)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-02-08 10:18:41 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
630ae0b3b0 cmd: migrate from urfave/cli v1 to v3 (#8160) 2026-02-08 00:58:05 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
3c358ede6d all: migrate from json-iterator to encoding/json (#8159) 2026-02-08 00:34:36 -05:00
131 changed files with 2318 additions and 1791 deletions

13
.claude/commands/ghsa.md Normal file
View File

@@ -0,0 +1,13 @@
Analyze and help fix the GitHub Security Advisory (GHSA) at: $ARGUMENTS
Steps:
1. Fetch the GHSA page using `gh api repos/gogs/gogs/security-advisories` and understand the vulnerability details (description, severity, affected versions, CWE).
2. Verify the reported vulnerability actually exists, and why.
3. Identify the affected code in this repository.
4. Propose a fix with a clear explanation of the root cause and how the fix addresses it. Check for prior art in the codebase to stay consistent with existing patterns.
5. Implement the fix. Only add tests when there is something meaningful to test at our layer.
6. Run all the usual build and test commands.
7. If a changelog entry is warranted (user will specify), add it to CHANGELOG.md with a placeholder for the PR link.
8. Create a branch named after the GHSA ID, commit, and push.
9. Create a pull request with a proper title and description, do not reveal too much detail and link the GHSA.
10. If a changelog entry was added, update it with the PR link, then commit and push again.

View File

@@ -29,7 +29,7 @@ In addition to the general guides with open source contributions, you would also
### Ask for help ### Ask for help
Before opening an issue, please make sure the problem you're encountering isn't already addressed on the [Troubleshooting](https://gogs.io/docs/intro/troubleshooting.html) and [FAQs](https://gogs.io/docs/intro/faqs.html) pages. Before opening an issue, please make sure the problem you're encountering isn't already addressed on the [Troubleshooting](https://gogs.io/asking/troubleshooting) and [FAQs](https://gogs.io/asking/faq) pages.
### Create a new issue ### Create a new issue
@@ -65,12 +65,12 @@ Contributing to another codebase is not as simple as code changes, it is also ab
### Things we do not accept ### Things we do not accept
1. Updates to locale files (`conf/locale_xx-XX.ini`) other than the `conf/locale_en-US.ini`. Please read the [guide for localizing Gogs](https://gogs.io/docs/features/i18n). 1. Updates to locale files (`conf/locale_xx-XX.ini`) other than the `conf/locale_en-US.ini`. Please read the [guide for localizing Gogs](https://gogs.io/advancing/localization).
1. Docker compose files. 1. Docker compose files.
### Coding guidelines ### Coding guidelines
1. Please read the Sourcegraph's [Go style guide](https://docs.sourcegraph.com/dev/background-information/languages/go). 1. Please read the Sourcegraph's [Go style guide](https://github.com/sourcegraph/sourcegraph-public-snapshot/blob/main/doc/dev/background-information/languages/go.md).
1. **NO** direct modifications to `.css` files, `.css` files are all generated by `.less` files. You can regenerate `.css` files by executing `task less`. 1. **NO** direct modifications to `.css` files, `.css` files are all generated by `.less` files. You can regenerate `.css` files by executing `task less`.
## Your PR is merged! ## Your PR is merged!

View File

@@ -38,7 +38,7 @@ jobs:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps: steps:
- name: Checkout repository - name: Check out repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
# We must fetch at least the immediate parents so that if this is # We must fetch at least the immediate parents so that if this is

View File

@@ -26,7 +26,7 @@ jobs:
contents: read contents: read
packages: write packages: write
steps: steps:
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
@@ -85,7 +85,7 @@ jobs:
contents: read contents: read
packages: write packages: write
steps: steps:
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
@@ -173,7 +173,7 @@ jobs:
permissions: permissions:
contents: read contents: read
steps: steps:
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
@@ -212,7 +212,7 @@ jobs:
permissions: permissions:
contents: read contents: read
steps: steps:
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
@@ -274,7 +274,7 @@ jobs:
echo "TAGS<<EOF" >> $GITHUB_ENV echo "TAGS<<EOF" >> $GITHUB_ENV
echo "$TAGS" >> $GITHUB_ENV echo "$TAGS" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
@@ -343,7 +343,7 @@ jobs:
echo "TAGS<<EOF" >> $GITHUB_ENV echo "TAGS<<EOF" >> $GITHUB_ENV
echo "$TAGS" >> $GITHUB_ENV echo "$TAGS" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0

View File

@@ -29,12 +29,12 @@ jobs:
name: Lint name: Lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go - name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.25.x go-version: 1.26.x
- name: Install Task - name: Install Task
uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0
with: with:
@@ -61,11 +61,11 @@ jobs:
name: Test name: Test
strategy: strategy:
matrix: matrix:
go-version: [ 1.25.x ] go-version: [ 1.26.x ]
platform: [ ubuntu-latest, macos-latest ] platform: [ ubuntu-latest, macos-latest ]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go - name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
@@ -89,11 +89,11 @@ jobs:
name: Test Windows name: Test Windows
strategy: strategy:
matrix: matrix:
go-version: [ 1.25.x ] go-version: [ 1.26.x ]
platform: [ windows-latest ] platform: [ windows-latest ]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go - name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
@@ -112,7 +112,7 @@ jobs:
name: Postgres name: Postgres
strategy: strategy:
matrix: matrix:
go-version: [ 1.25.x ] go-version: [ 1.26.x ]
platform: [ ubuntu-latest ] platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
services: services:
@@ -128,7 +128,7 @@ jobs:
ports: ports:
- 5432:5432 - 5432:5432
steps: steps:
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go - name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
@@ -151,13 +151,13 @@ jobs:
name: MySQL name: MySQL
strategy: strategy:
matrix: matrix:
go-version: [ 1.25.x ] go-version: [ 1.26.x ]
platform: [ ubuntu-22.04 ] # Use the lowest version possible for backwards compatibility platform: [ ubuntu-22.04 ] # Use the lowest version possible for backwards compatibility
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- name: Start MySQL server - name: Start MySQL server
run: sudo systemctl start mysql run: sudo systemctl start mysql
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go - name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0

View File

@@ -40,12 +40,12 @@ jobs:
- {goos: windows, goarch: arm64, suffix: "_mws", tags: minwinsvc} - {goos: windows, goarch: arm64, suffix: "_mws", tags: minwinsvc}
- {goos: windows, goarch: "386", suffix: "_mws", tags: minwinsvc} - {goos: windows, goarch: "386", suffix: "_mws", tags: minwinsvc}
steps: steps:
- name: Checkout code - name: Check out code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Setup Go - name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.25.x go-version: 1.26.x
- name: Determine version - name: Determine version
id: version id: version
run: | run: |
@@ -90,7 +90,7 @@ jobs:
BINARY_NAME="gogs.exe" BINARY_NAME="gogs.exe"
fi fi
cp "$BINARY_NAME" dist/gogs/ cp "$BINARY_NAME" dist/gogs/
cp LICENSE README.md README_ZH.md dist/gogs/ cp LICENSE README.md dist/gogs/
cp -r scripts dist/gogs/ cp -r scripts dist/gogs/
- name: Create archives - name: Create archives
working-directory: dist working-directory: dist

View File

@@ -20,6 +20,7 @@ This applies to all texts, including but not limited to UI, documentation, code
- Prefer `task` command over vanilla `go` command when available. Use `--force` flag when necessary. - Prefer `task` command over vanilla `go` command when available. Use `--force` flag when necessary.
- Run `task lint` after every time you finish changing code, and fix all linter errors. - Run `task lint` after every time you finish changing code, and fix all linter errors.
- Run `go mod tidy` after every time you change `go.mod`, do not manually edit `go.sum` file.
## Tool-use guidance ## Tool-use guidance
@@ -28,5 +29,5 @@ This applies to all texts, including but not limited to UI, documentation, code
## Source code control ## Source code control
- When pushing changes to a pull request from a fork, use SSH address and do not add remote. - When pushing changes to a pull request from a fork, use SSH address and do not add remote.
- Never automatically executes commands that touches Git history even if the session does not require approvals, including but not limited to `rebase`, `commit`, `push`, `pull`, `reset`, `amend`. Exceptions are only allowed case-by-case. - Never commit on the `main` branch directly unless being explicitly asked to do so. A single ask only grants a single commit action on the `main` branch.
- Do not amend commits unless being explicitly asked to do so. - Never amend commits unless being explicitly asked to do so.

View File

@@ -4,9 +4,16 @@ All notable changes to Gogs are documented in this file.
## 0.15.0+dev (`main`) ## 0.15.0+dev (`main`)
### Fixed
- _Security:_ Cross-repository LFS object overwrite via missing content hash verification. [#8166](https://github.com/gogs/gogs/pull/8166) - [GHSA-gmf8-978x-2fg2](https://github.com/gogs/gogs/security/advisories/GHSA-gmf8-978x-2fg2)
### Removed ### Removed
- Support for passing API access tokens via URL query parameters (`token`, `access_token`). Use the `Authorization` header instead. [#8177](https://github.com/gogs/gogs/pull/8177) - [GHSA-x9p5-w45c-7ffc](https://github.com/gogs/gogs/security/advisories/GHSA-x9p5-w45c-7ffc)
- The `gogs cert` subcommand. [#8153](https://github.com/gogs/gogs/pull/8153) - The `gogs cert` subcommand. [#8153](https://github.com/gogs/gogs/pull/8153)
- The `[email] DISABLE_HELO` configuration option. HELO/EHLO is now always sent during SMTP handshake. [#8164](https://github.com/gogs/gogs/pull/8164)
- Support for MSSQL as a database backend. Stay on 0.14 for continued usage. [#8173](https://github.com/gogs/gogs/pull/8173)
## 0.14.1 ## 0.14.1

View File

@@ -13,10 +13,10 @@ The Gogs (`/gɑgz/`) project aims to build a simple, stable and extensible self-
- Please visit [our home page](https://gogs.io) for user documentation. - Please visit [our home page](https://gogs.io) for user documentation.
- Please refer to [CHANGELOG.md](CHANGELOG.md) for list of changes in each releases. - Please refer to [CHANGELOG.md](CHANGELOG.md) for list of changes in each releases.
- Want to try it before doing anything else? Do it [online](https://try.gogs.io/gogs/gogs)! - Want to try it before doing anything else? Do it [online](https://try.gogs.io/gogs/gogs)!
- Having trouble? Help yourself with [troubleshooting](https://gogs.io/docs/intro/troubleshooting.html) or ask questions in [Discussions](https://github.com/gogs/gogs/discussions). - Having trouble? Help yourself with [troubleshooting](https://gogs.io/asking/troubleshooting) or ask questions in [Discussions](https://github.com/gogs/gogs/discussions).
- Want to help with localization? Check out the [localization documentation](https://gogs.io/docs/features/i18n.html). - Want to help with localization? Check out the [localization documentation](https://gogs.io/advancing/localization).
- Ready to get hands dirty? Read our [contributing guide](.github/CONTRIBUTING.md). - Ready to get hands dirty? Read our [contributing guide](.github/CONTRIBUTING.md).
- Hmm... What about APIs? We have experimental support with [documentation](https://github.com/gogs/docs-api). - Hmm... What about APIs? We have experimental support with [documentation](https://gogs.io/api-reference).
## 💌 Features ## 💌 Features
@@ -47,15 +47,7 @@ The Gogs (`/gɑgz/`) project aims to build a simple, stable and extensible self-
## 📜 Installation ## 📜 Installation
Make sure you install the [prerequisites](https://gogs.io/docs/installation) first. Please follow [the guide in our documentation](https://gogs.io/getting-started/installation).
There are 6 ways to install Gogs:
- [Install from binary](https://gogs.io/docs/installation/install_from_binary.html)
- [Install from source](https://gogs.io/docs/installation/install_from_source.html)
- [Install from packages](https://gogs.io/docs/installation/install_from_packages.html)
- [Ship with Docker](https://github.com/gogs/gogs/tree/main/docker)
- [Try with Vagrant](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
### Deploy to cloud ### Deploy to cloud
@@ -94,7 +86,8 @@ There are 6 ways to install Gogs:
Other acknowledgments: Other acknowledgments:
- Thanks [Egon Elbre](https://twitter.com/egonelbre) for designing the original version of the logo. - Thanks [Egon Elbre](https://twitter.com/egonelbre) for designing the original version of the logo.
- Thanks [Crowdin](https://crowdin.com/project/gogs) for sponsoring open source translation plan. - Thanks [Mintlify](https://mintlify.com) for sponsoring open source documentation plan.
- Thanks [Crowdin](https://crowdin.com) for sponsoring open source translation plan.
- Thanks [Buildkite](https://buildkite.com) for sponsoring open source CI/CD plan. - Thanks [Buildkite](https://buildkite.com) for sponsoring open source CI/CD plan.
## 👋 Contributors ## 👋 Contributors

View File

@@ -1,102 +0,0 @@
# Gogs
Gogs 是一款极易搭建的自助 Git 服务。
## 项目愿景
Gogs`/gɑgz/`)项目旨在打造一个以最简便的方式搭建简单、稳定和可扩展的自助 Git 服务。使用 Go 语言开发使得 Gogs 能够通过独立的二进制分发,并且支持 Go 语言支持的 **所有平台**,包括 Linux、macOS、Windows 和基于 ARM 的操作系统。
## 概览
- 请移步[官网](https://gogs.io)查看用户使用文档
- 请通过 [CHANGELOG.md](CHANGELOG.md) 文件查看各个版本的变更历史
- 想要先睹为快?直接去[在线体验](https://try.gogs.io/gogs/gogs)吧!
- 使用过程中遇到问题?尝试[故障排查](https://gogs.io/docs/intro/troubleshooting.html)或者前往[用户论坛](https://discuss.gogs.io/)获取帮助
- 希望帮助多国语言的翻译吗?请查看[本地化文档](https://gogs.io/docs/features/i18n.html)
- 准备搞点事情?请阅读[开发指南](docs/dev/local_development.md)配置开发环境
- 想调用 API 吗?请查看[文档](https://github.com/gogs/docs-api)吧
## 主要特性
- 控制面板、用户页面以及活动时间线
- 通过 SSH、HTTP 和 HTTPS 协议操作仓库
- 管理用户、组织和仓库
- 仓库和组织级 Webhook包括 Slack、Discord 和钉钉
- 仓库 Git 钩子、部署密钥和 Git LFS
- 仓库工单Issue、合并请求Pull Request、Wiki、保护分支和多人协作
- 从其它代码平台迁移和镜像仓库以及 Wiki
- 在线编辑仓库文件和 Wiki
- Jupyter Notebook 和 PDF 的渲染
- 通过 SMTP、LDAP、反向代理、GitHub.com 和 GitHub 企业版进行用户认证
- 开启两步验证2FA登录
- 自定义 HTML 模板、静态文件和许多其它组件
- 多样的数据库后端,包括 PostgreSQL、MySQL、SQLite3 和 [TiDB](https://github.com/pingcap/tidb)
- 超过 [31 种语言](https://crowdin.com/project/gogs)的本地化
## 硬件要求
- 最低的系统硬件要求为一个廉价的树莓派
- 如果用于团队项目管理,建议使用 2 核 CPU 及 512MB 内存
- 当团队成员大量增加时,可以考虑添加 CPU 核数,内存占用保持不变
## 浏览器支持
- 请根据 [Semantic UI](https://github.com/Semantic-Org/Semantic-UI#browser-support) 查看具体支持的浏览器版本。
- 官方支持的最小 UI 尺寸为 **1024*768**UI 不一定会在更小尺寸的设备上被破坏,但我们无法保证且不会修复。
## 安装部署
在安装 Gogs 之前,您需要先安装 [基本环境](https://gogs.io/docs/installation)。
然后,您可以通过以下 6 种方式来安装 Gogs
- [二进制安装](https://gogs.io/docs/installation/install_from_binary.html)
- [源码安装](https://gogs.io/docs/installation/install_from_source.html)
- [包管理安装](https://gogs.io/docs/installation/install_from_packages.html)
- [采用 Docker 部署](https://github.com/gogs/gogs/tree/main/docker)
- [通过 Vagrant 安装](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
- [通过基于 Kubernetes 的 Helm Charts](https://github.com/helm/charts/tree/master/incubator/gogs)
### 云端部署
- [OpenShift](https://github.com/tkisme/gogs-openshift)
- [Cloudron](https://cloudron.io/appstore.html#io.gogs.cloudronapp)
- [Scaleway](https://www.scaleway.com/imagehub/gogs/)
- [Sandstorm](https://github.com/cem/gogs-sandstorm)
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
- [YunoHost](https://github.com/mbugeia/gogs_ynh)
- [DPlatform](https://github.com/j8r/DPlatform)
- [LunaNode](https://github.com/LunaNode/launchgogs)
### 使用教程
- [使用 Gogs 搭建自己的 Git 服务器](https://blog.mynook.info/post/host-your-own-git-server-using-gogs/)
- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654)
## 软件、服务以及产品支持
- [Fabric8](http://fabric8.io/)DevOps
- [Jenkins](https://plugins.jenkins.io/gogs-webhook/)CI
- [Taiga](https://taiga.io/)(项目管理)
- [Puppet](https://forge.puppet.com/Siteminds/gogs)IT
- [Kanboard](https://github.com/kanboard/plugin-gogs-webhook)(项目管理)
- [BearyChat](https://bearychat.com/)(团队交流)
- [GitPitch](https://gitpitch.com/)Markdown 演示)
- [Synology](https://www.synology.com)Docker
- [Syncloud](https://syncloud.org/)(应用商店)
## 特别鸣谢
- 感谢 [Egon Elbre](https://twitter.com/egonelbre) 设计的 Logo。
- 感谢 [DigitalOcean](https://www.digitalocean.com) 和 [MonoVM](https://monovm.com) 提供服务器赞助。
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
- 感谢 [Buildkite](https://buildkite.com) 提供免费的开源项目 CI/CD 支持。
## 贡献成员
- 您可以通过查看 [贡献者页面](https://github.com/gogs/gogs/graphs/contributors) 获取 TOP 100 的贡献者列表。
- 您可以通过查看 [TRANSLATORS](conf/locale/TRANSLATORS) 文件获取公开的翻译人员列表。
## 授权许可
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogs/gogs/blob/main/LICENSE) 文件中。

View File

@@ -10,8 +10,6 @@ tasks:
web: web:
desc: Build the binary and start the web server desc: Build the binary and start the web server
deps: [build] deps: [build]
env:
GOGS_WORK_DIR: '{{.ROOT_DIR}}'
cmds: cmds:
- .bin/gogs web - .bin/gogs web

View File

@@ -7,7 +7,7 @@ import (
"runtime" "runtime"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/urfave/cli" "github.com/urfave/cli/v3"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
@@ -19,15 +19,15 @@ var (
Usage: "Perform admin operations on command line", Usage: "Perform admin operations on command line",
Description: `Allow using internal logic of Gogs without hacking into the source code Description: `Allow using internal logic of Gogs without hacking into the source code
to make automatic initialization process more smoothly`, to make automatic initialization process more smoothly`,
Subcommands: []cli.Command{ Commands: []*cli.Command{
subcmdCreateUser, &subcmdCreateUser,
subcmdDeleteInactivateUsers, &subcmdDeleteInactivateUsers,
subcmdDeleteRepositoryArchives, &subcmdDeleteRepositoryArchives,
subcmdDeleteMissingRepositories, &subcmdDeleteMissingRepositories,
subcmdGitGcRepos, &subcmdGitGcRepos,
subcmdRewriteAuthorizedKeys, &subcmdRewriteAuthorizedKeys,
subcmdSyncRepositoryHooks, &subcmdSyncRepositoryHooks,
subcmdReinitMissingRepositories, &subcmdReinitMissingRepositories,
}, },
} }
@@ -129,16 +129,16 @@ to make automatic initialization process more smoothly`,
} }
) )
func runCreateUser(c *cli.Context) error { func runCreateUser(ctx context.Context, cmd *cli.Command) error {
if !c.IsSet("name") { if !cmd.IsSet("name") {
return errors.New("Username is not specified") return errors.New("Username is not specified")
} else if !c.IsSet("password") { } else if !cmd.IsSet("password") {
return errors.New("Password is not specified") return errors.New("Password is not specified")
} else if !c.IsSet("email") { } else if !cmd.IsSet("email") {
return errors.New("Email is not specified") return errors.New("Email is not specified")
} }
err := conf.Init(c.String("config")) err := conf.Init(configFromLineage(cmd))
if err != nil { if err != nil {
return errors.Wrap(err, "init configuration") return errors.Wrap(err, "init configuration")
} }
@@ -149,13 +149,13 @@ func runCreateUser(c *cli.Context) error {
} }
user, err := database.Handle.Users().Create( user, err := database.Handle.Users().Create(
context.Background(), ctx,
c.String("name"), cmd.String("name"),
c.String("email"), cmd.String("email"),
database.CreateUserOptions{ database.CreateUserOptions{
Password: c.String("password"), Password: cmd.String("password"),
Activated: true, Activated: true,
Admin: c.Bool("admin"), Admin: cmd.Bool("admin"),
}, },
) )
if err != nil { if err != nil {
@@ -166,9 +166,9 @@ func runCreateUser(c *cli.Context) error {
return nil return nil
} }
func adminDashboardOperation(operation func() error, successMessage string) func(*cli.Context) error { func adminDashboardOperation(operation func() error, successMessage string) func(context.Context, *cli.Command) error {
return func(c *cli.Context) error { return func(_ context.Context, cmd *cli.Command) error {
err := conf.Init(c.String("config")) err := conf.Init(configFromLineage(cmd))
if err != nil { if err != nil {
return errors.Wrap(err, "init configuration") return errors.Wrap(err, "init configuration")
} }

View File

@@ -11,7 +11,7 @@ import (
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/unknwon/cae/zip" "github.com/unknwon/cae/zip"
"github.com/urfave/cli" "github.com/urfave/cli/v3"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
@@ -44,10 +44,10 @@ const (
archiveRootDir = "gogs-backup" archiveRootDir = "gogs-backup"
) )
func runBackup(c *cli.Context) error { func runBackup(ctx context.Context, cmd *cli.Command) error {
zip.Verbose = c.Bool("verbose") zip.Verbose = cmd.Bool("verbose")
err := conf.Init(c.String("config")) err := conf.Init(configFromLineage(cmd))
if err != nil { if err != nil {
return errors.Wrap(err, "init configuration") return errors.Wrap(err, "init configuration")
} }
@@ -58,7 +58,7 @@ func runBackup(c *cli.Context) error {
return errors.Wrap(err, "set engine") return errors.Wrap(err, "set engine")
} }
tmpDir := c.String("tempdir") tmpDir := cmd.String("tempdir")
if !osutil.Exist(tmpDir) { if !osutil.Exist(tmpDir) {
log.Fatal("'--tempdir' does not exist: %s", tmpDir) log.Fatal("'--tempdir' does not exist: %s", tmpDir)
} }
@@ -78,7 +78,7 @@ func runBackup(c *cli.Context) error {
log.Fatal("Failed to save metadata '%s': %v", metaFile, err) log.Fatal("Failed to save metadata '%s': %v", metaFile, err)
} }
archiveName := filepath.Join(c.String("target"), c.String("archive-name")) archiveName := filepath.Join(cmd.String("target"), cmd.String("archive-name"))
log.Info("Packing backup files to: %s", archiveName) log.Info("Packing backup files to: %s", archiveName)
z, err := zip.Create(archiveName) z, err := zip.Create(archiveName)
@@ -91,14 +91,14 @@ func runBackup(c *cli.Context) error {
// Database // Database
dbDir := filepath.Join(rootDir, "db") dbDir := filepath.Join(rootDir, "db")
if err = database.DumpDatabase(context.Background(), conn, dbDir, c.Bool("verbose")); err != nil { if err = database.DumpDatabase(ctx, conn, dbDir, cmd.Bool("verbose")); err != nil {
log.Fatal("Failed to dump database: %v", err) log.Fatal("Failed to dump database: %v", err)
} }
if err = z.AddDir(archiveRootDir+"/db", dbDir); err != nil { if err = z.AddDir(archiveRootDir+"/db", dbDir); err != nil {
log.Fatal("Failed to include 'db': %v", err) log.Fatal("Failed to include 'db': %v", err)
} }
if !c.Bool("database-only") { if !cmd.Bool("database-only") {
// Custom files // Custom files
err = addCustomDirToBackup(z) err = addCustomDirToBackup(z)
if err != nil { if err != nil {
@@ -119,10 +119,10 @@ func runBackup(c *cli.Context) error {
} }
// Repositories // Repositories
if !c.Bool("exclude-repos") && !c.Bool("database-only") { if !cmd.Bool("exclude-repos") && !cmd.Bool("database-only") {
reposDump := filepath.Join(rootDir, "repositories.zip") reposDump := filepath.Join(rootDir, "repositories.zip")
log.Info("Dumping repositories in %q", conf.Repository.Root) log.Info("Dumping repositories in %q", conf.Repository.Root)
if c.Bool("exclude-mirror-repos") { if cmd.Bool("exclude-mirror-repos") {
repos, err := database.GetNonMirrorRepositories() repos, err := database.GetNonMirrorRepositories()
if err != nil { if err != nil {
log.Fatal("Failed to get non-mirror repositories: %v", err) log.Fatal("Failed to get non-mirror repositories: %v", err)

View File

@@ -1,20 +1,43 @@
package main package main
import ( import (
"github.com/urfave/cli" "strings"
"github.com/urfave/cli/v3"
) )
func stringFlag(name, value, usage string) cli.StringFlag { func stringFlag(name, value, usage string) *cli.StringFlag {
return cli.StringFlag{ parts := strings.SplitN(name, ", ", 2)
Name: name, f := &cli.StringFlag{
Name: parts[0],
Value: value, Value: value,
Usage: usage, Usage: usage,
} }
if len(parts) > 1 {
f.Aliases = []string{parts[1]}
}
return f
} }
func boolFlag(name, usage string) cli.BoolFlag { // configFromLineage walks the command lineage to find the --config flag value.
return cli.BoolFlag{ // This is needed because subcommands may not directly see flags set on parent commands.
Name: name, func configFromLineage(cmd *cli.Command) string {
for _, c := range cmd.Lineage() {
if c.IsSet("config") {
return c.String("config")
}
}
return ""
}
func boolFlag(name, usage string) *cli.BoolFlag {
parts := strings.SplitN(name, ", ", 2)
f := &cli.BoolFlag{
Name: parts[0],
Usage: usage, Usage: usage,
} }
if len(parts) > 1 {
f.Aliases = []string{parts[1]}
}
return f
} }

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net/url" "net/url"
@@ -12,7 +13,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/urfave/cli" "github.com/urfave/cli/v3"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"github.com/gogs/git-module" "github.com/gogs/git-module"
@@ -32,10 +33,10 @@ var (
Flags: []cli.Flag{ Flags: []cli.Flag{
stringFlag("config, c", "", "Custom configuration file path"), stringFlag("config, c", "", "Custom configuration file path"),
}, },
Subcommands: []cli.Command{ Commands: []*cli.Command{
subcmdHookPreReceive, &subcmdHookPreReceive,
subcmdHookUpadte, &subcmdHookUpadte,
subcmdHookPostReceive, &subcmdHookPostReceive,
}, },
} }
@@ -59,11 +60,11 @@ var (
} }
) )
func runHookPreReceive(c *cli.Context) error { func runHookPreReceive(_ context.Context, cmd *cli.Command) error {
if os.Getenv("SSH_ORIGINAL_COMMAND") == "" { if os.Getenv("SSH_ORIGINAL_COMMAND") == "" {
return nil return nil
} }
setup(c, "pre-receive.log", true) setup(cmd, "pre-receive.log", true)
isWiki := strings.Contains(os.Getenv(database.EnvRepoCustomHooksPath), ".wiki.git/") isWiki := strings.Contains(os.Getenv(database.EnvRepoCustomHooksPath), ".wiki.git/")
@@ -152,13 +153,13 @@ func runHookPreReceive(c *cli.Context) error {
return nil return nil
} }
func runHookUpdate(c *cli.Context) error { func runHookUpdate(_ context.Context, cmd *cli.Command) error {
if os.Getenv("SSH_ORIGINAL_COMMAND") == "" { if os.Getenv("SSH_ORIGINAL_COMMAND") == "" {
return nil return nil
} }
setup(c, "update.log", false) setup(cmd, "update.log", false)
args := c.Args() args := cmd.Args().Slice()
if len(args) != 3 { if len(args) != 3 {
fail("Arguments received are not equal to three", "Arguments received are not equal to three") fail("Arguments received are not equal to three", "Arguments received are not equal to three")
} else if args[0] == "" { } else if args[0] == "" {
@@ -186,11 +187,11 @@ func runHookUpdate(c *cli.Context) error {
return nil return nil
} }
func runHookPostReceive(c *cli.Context) error { func runHookPostReceive(_ context.Context, cmd *cli.Command) error {
if os.Getenv("SSH_ORIGINAL_COMMAND") == "" { if os.Getenv("SSH_ORIGINAL_COMMAND") == "" {
return nil return nil
} }
setup(c, "post-receive.log", true) setup(cmd, "post-receive.log", true)
// Post-receive hook does more than just gather Git information, // Post-receive hook does more than just gather Git information,
// so we need to setup additional services for email notifications. // so we need to setup additional services for email notifications.

View File

@@ -3,13 +3,14 @@ package main
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/urfave/cli" "github.com/urfave/cli/v3"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/osutil" "gogs.io/gogs/internal/osutil"
@@ -21,8 +22,8 @@ var (
Usage: "Import portable data as local Gogs data", Usage: "Import portable data as local Gogs data",
Description: `Allow user import data from other Gogs installations to local instance Description: `Allow user import data from other Gogs installations to local instance
without manually hacking the data files`, without manually hacking the data files`,
Subcommands: []cli.Command{ Commands: []*cli.Command{
subcmdImportLocale, &subcmdImportLocale,
}, },
} }
@@ -38,19 +39,19 @@ without manually hacking the data files`,
} }
) )
func runImportLocale(c *cli.Context) error { func runImportLocale(_ context.Context, cmd *cli.Command) error {
if !c.IsSet("source") { if !cmd.IsSet("source") {
return errors.New("source directory is not specified") return errors.New("source directory is not specified")
} else if !c.IsSet("target") { } else if !cmd.IsSet("target") {
return errors.New("target directory is not specified") return errors.New("target directory is not specified")
} }
if !osutil.IsDir(c.String("source")) { if !osutil.IsDir(cmd.String("source")) {
return errors.Newf("source directory %q does not exist or is not a directory", c.String("source")) return errors.Newf("source directory %q does not exist or is not a directory", cmd.String("source"))
} else if !osutil.IsDir(c.String("target")) { } else if !osutil.IsDir(cmd.String("target")) {
return errors.Newf("target directory %q does not exist or is not a directory", c.String("target")) return errors.Newf("target directory %q does not exist or is not a directory", cmd.String("target"))
} }
err := conf.Init(c.String("config")) err := conf.Init(configFromLineage(cmd))
if err != nil { if err != nil {
return errors.Wrap(err, "init configuration") return errors.Wrap(err, "init configuration")
} }
@@ -64,8 +65,8 @@ func runImportLocale(c *cli.Context) error {
// Cut out en-US. // Cut out en-US.
for _, lang := range conf.I18n.Langs[1:] { for _, lang := range conf.I18n.Langs[1:] {
name := fmt.Sprintf("locale_%s.ini", lang) name := fmt.Sprintf("locale_%s.ini", lang)
source := filepath.Join(c.String("source"), name) source := filepath.Join(cmd.String("source"), name)
target := filepath.Join(c.String("target"), name) target := filepath.Join(cmd.String("target"), name)
if !osutil.IsFile(source) { if !osutil.IsFile(source) {
continue continue
} }

View File

@@ -2,9 +2,10 @@
package main package main
import ( import (
"context"
"os" "os"
"github.com/urfave/cli" "github.com/urfave/cli/v3"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
@@ -15,20 +16,21 @@ func init() {
} }
func main() { func main() {
app := cli.NewApp() cmd := &cli.Command{
app.Name = "Gogs" Name: "Gogs",
app.Usage = "A painless self-hosted Git service" Usage: "A painless self-hosted Git service",
app.Version = conf.App.Version Version: conf.App.Version,
app.Commands = []cli.Command{ Commands: []*cli.Command{
webCommand, &webCommand,
servCommand, &servCommand,
hookCommand, &hookCommand,
adminCommand, &adminCommand,
importCommand, &importCommand,
backupCommand, &backupCommand,
restoreCommand, &restoreCommand,
},
} }
if err := app.Run(os.Args); err != nil { if err := cmd.Run(context.Background(), os.Args); err != nil {
log.Fatal("Failed to start application: %v", err) log.Fatal("Failed to start application: %v", err)
} }
} }

View File

@@ -8,7 +8,7 @@ import (
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/unknwon/cae/zip" "github.com/unknwon/cae/zip"
"github.com/urfave/cli" "github.com/urfave/cli/v3"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
@@ -42,10 +42,10 @@ be skipped and remain unchanged.`,
// format that is able to import. // format that is able to import.
var lastSupportedVersionOfFormat = map[int]string{} var lastSupportedVersionOfFormat = map[int]string{}
func runRestore(c *cli.Context) error { func runRestore(ctx context.Context, cmd *cli.Command) error {
zip.Verbose = c.Bool("verbose") zip.Verbose = cmd.Bool("verbose")
tmpDir := c.String("tempdir") tmpDir := cmd.String("tempdir")
if !osutil.IsDir(tmpDir) { if !osutil.IsDir(tmpDir) {
log.Fatal("'--tempdir' does not exist: %s", tmpDir) log.Fatal("'--tempdir' does not exist: %s", tmpDir)
} }
@@ -58,8 +58,8 @@ func runRestore(c *cli.Context) error {
} }
defer func() { _ = os.RemoveAll(archivePath) }() defer func() { _ = os.RemoveAll(archivePath) }()
log.Info("Restoring backup from: %s", c.String("from")) log.Info("Restoring backup from: %s", cmd.String("from"))
err = zip.ExtractTo(c.String("from"), tmpDir) err = zip.ExtractTo(cmd.String("from"), tmpDir)
if err != nil { if err != nil {
log.Fatal("Failed to extract backup archive: %v", err) log.Fatal("Failed to extract backup archive: %v", err)
} }
@@ -90,8 +90,8 @@ func runRestore(c *cli.Context) error {
// Otherwise, it's optional to set config file flag. // Otherwise, it's optional to set config file flag.
configFile := filepath.Join(archivePath, "custom", "conf", "app.ini") configFile := filepath.Join(archivePath, "custom", "conf", "app.ini")
var customConf string var customConf string
if c.IsSet("config") { if lineageConf := configFromLineage(cmd); lineageConf != "" {
customConf = c.String("config") customConf = lineageConf
} else if !osutil.IsFile(configFile) { } else if !osutil.IsFile(configFile) {
log.Fatal("'--config' is not specified and custom config file is not found in backup") log.Fatal("'--config' is not specified and custom config file is not found in backup")
} else { } else {
@@ -111,11 +111,11 @@ func runRestore(c *cli.Context) error {
// Database // Database
dbDir := path.Join(archivePath, "db") dbDir := path.Join(archivePath, "db")
if err = database.ImportDatabase(context.Background(), conn, dbDir, c.Bool("verbose")); err != nil { if err = database.ImportDatabase(ctx, conn, dbDir, cmd.Bool("verbose")); err != nil {
log.Fatal("Failed to import database: %v", err) log.Fatal("Failed to import database: %v", err)
} }
if !c.Bool("database-only") { if !cmd.Bool("database-only") {
// Custom files // Custom files
if osutil.IsDir(conf.CustomDir()) { if osutil.IsDir(conf.CustomDir()) {
if err = os.Rename(conf.CustomDir(), conf.CustomDir()+".bak"); err != nil { if err = os.Rename(conf.CustomDir(), conf.CustomDir()+".bak"); err != nil {
@@ -149,7 +149,7 @@ func runRestore(c *cli.Context) error {
// Repositories // Repositories
reposPath := filepath.Join(archivePath, "repositories.zip") reposPath := filepath.Join(archivePath, "repositories.zip")
if !c.Bool("exclude-repos") && !c.Bool("database-only") && osutil.IsFile(reposPath) { if !cmd.Bool("exclude-repos") && !cmd.Bool("database-only") && osutil.IsFile(reposPath) {
if err := zip.ExtractTo(reposPath, filepath.Dir(conf.Repository.Root)); err != nil { if err := zip.ExtractTo(reposPath, filepath.Dir(conf.Repository.Root)); err != nil {
log.Fatal("Failed to extract 'repositories.zip': %v", err) log.Fatal("Failed to extract 'repositories.zip': %v", err)
} }

View File

@@ -10,7 +10,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/urfave/cli" "github.com/urfave/cli/v3"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
@@ -48,15 +48,10 @@ func fail(userMessage, errMessage string, args ...any) {
os.Exit(1) os.Exit(1)
} }
func setup(c *cli.Context, logFile string, connectDB bool) { func setup(cmd *cli.Command, logFile string, connectDB bool) {
conf.HookMode = true conf.HookMode = true
var customConf string customConf := configFromLineage(cmd)
if c.IsSet("config") {
customConf = c.String("config")
} else if c.GlobalIsSet("config") {
customConf = c.GlobalString("config")
}
err := conf.Init(customConf) err := conf.Init(customConf)
if err != nil { if err != nil {
@@ -128,16 +123,15 @@ var allowedCommands = map[string]database.AccessMode{
"git-receive-pack": database.AccessModeWrite, "git-receive-pack": database.AccessModeWrite,
} }
func runServ(c *cli.Context) error { func runServ(ctx context.Context, cmd *cli.Command) error {
ctx := context.Background() setup(cmd, "serv.log", true)
setup(c, "serv.log", true)
if conf.SSH.Disabled { if conf.SSH.Disabled {
println("Gogs: SSH has been disabled") println("Gogs: SSH has been disabled")
return nil return nil
} }
if len(c.Args()) < 1 { if cmd.Args().Len() < 1 {
fail("Not enough arguments", "Not enough arguments") fail("Not enough arguments", "Not enough arguments")
} }
@@ -188,10 +182,10 @@ func runServ(c *cli.Context) error {
// Allow anonymous (user is nil) clone for public repositories. // Allow anonymous (user is nil) clone for public repositories.
var user *database.User var user *database.User
keyID, _ := strconv.ParseInt(strings.TrimPrefix(c.Args()[0], "key-"), 10, 64) keyID, _ := strconv.ParseInt(strings.TrimPrefix(cmd.Args().Get(0), "key-"), 10, 64)
key, err := database.GetPublicKeyByID(keyID) key, err := database.GetPublicKeyByID(keyID)
if err != nil { if err != nil {
fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err) fail("Invalid key ID", "Invalid key ID '%s': %v", cmd.Args().Get(0), err)
} }
if requestMode == database.AccessModeWrite || repo.IsPrivate { if requestMode == database.AccessModeWrite || repo.IsPrivate {

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
stdctx "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
@@ -20,7 +21,7 @@ import (
"github.com/go-macaron/session" "github.com/go-macaron/session"
"github.com/go-macaron/toolbox" "github.com/go-macaron/toolbox"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/urfave/cli" "github.com/urfave/cli/v3"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
@@ -158,8 +159,8 @@ func newMacaron() *macaron.Macaron {
return m return m
} }
func runWeb(c *cli.Context) error { func runWeb(_ stdctx.Context, cmd *cli.Command) error {
err := route.GlobalInit(c.String("config")) err := route.GlobalInit(configFromLineage(cmd))
if err != nil { if err != nil {
log.Fatal("Failed to initialize application: %v", err) log.Fatal("Failed to initialize application: %v", err)
} }
@@ -697,10 +698,10 @@ func runWeb(c *cli.Context) error {
m.NotFound(route.NotFound) m.NotFound(route.NotFound)
// Flag for port number in case first time run conflict. // Flag for port number in case first time run conflict.
if c.IsSet("port") { if cmd.IsSet("port") {
conf.Server.URL.Host = strings.Replace(conf.Server.URL.Host, ":"+conf.Server.URL.Port(), ":"+c.String("port"), 1) conf.Server.URL.Host = strings.Replace(conf.Server.URL.Host, ":"+conf.Server.URL.Port(), ":"+cmd.String("port"), 1)
conf.Server.ExternalURL = conf.Server.URL.String() conf.Server.ExternalURL = conf.Server.URL.String()
conf.Server.HTTPPort = c.String("port") conf.Server.HTTPPort = cmd.String("port")
} }
var listenAddr string var listenAddr string

View File

@@ -141,8 +141,7 @@ FILE_MAX_SIZE = 3
MAX_FILES = 5 MAX_FILES = 5
[database] [database]
; The database backend, either "postgres", "mysql" "sqlite3" or "mssql". ; The database backend, either "postgres", "mysql" or "sqlite3".
; You can connect to TiDB with MySQL protocol.
TYPE = postgres TYPE = postgres
HOST = 127.0.0.1:5432 HOST = 127.0.0.1:5432
NAME = gogs NAME = gogs
@@ -197,8 +196,6 @@ USER = noreply@gogs.localhost
; The login password. ; The login password.
PASSWORD = PASSWORD =
; Whether to disable HELO operation when the hostname is different.
DISABLE_HELO =
; The custom hostname for HELO operation, default is from system. ; The custom hostname for HELO operation, default is from system.
HELO_HOSTNAME = HELO_HOSTNAME =
@@ -279,6 +276,8 @@ ACCESS_CONTROL_ALLOW_ORIGIN =
STORAGE = local STORAGE = local
; The root path to store LFS objects on local file system. ; The root path to store LFS objects on local file system.
OBJECTS_PATH = data/lfs-objects OBJECTS_PATH = data/lfs-objects
; The path to temporarily store LFS objects during upload verification.
OBJECTS_TEMP_PATH = data/tmp/lfs-objects
[attachment] [attachment]
; Whether to enabled upload attachments in general. ; Whether to enabled upload attachments in general.

View File

@@ -120,7 +120,6 @@ confirm_password = Confirm Password
admin_email = Admin Email admin_email = Admin Email
install_gogs = Install Gogs install_gogs = Install Gogs
test_git_failed = Failed to test 'git' command: %v test_git_failed = Failed to test 'git' command: %v
sqlite3_not_available = Your release version does not support SQLite3, please download the official binary version from %s, NOT the gobuild version.
invalid_db_setting = Database setting is not correct: %v invalid_db_setting = Database setting is not correct: %v
invalid_repo_path = Repository root path is invalid: %v invalid_repo_path = Repository root path is invalid: %v
run_user_not_match = Run user isn't the current user: %s -> %s run_user_not_match = Run user isn't the current user: %s -> %s
@@ -1263,7 +1262,6 @@ config.email.subject_prefix = Subject prefix
config.email.host = Host config.email.host = Host
config.email.from = From config.email.from = From
config.email.user = User config.email.user = User
config.email.disable_helo = Disable HELO
config.email.helo_hostname = HELO hostname config.email.helo_hostname = HELO hostname
config.email.skip_verify = Skip certificate verify config.email.skip_verify = Skip certificate verify
config.email.use_certificate = Use custom certificate config.email.use_certificate = Use custom certificate

View File

@@ -0,0 +1,67 @@
---
title: "CLI reference"
description: "Discover all the commands available in the gogs binary"
icon: "terminal"
---
Most people know `gogs web` for starting the server, but the `gogs` binary ships with several other commands that help you manage your instance from the command line.
Run `gogs --help` at any time to see the full list of available commands, and `gogs <command> --help` for details on a specific command.
<Tip>
Every command accepts a `--config` (`-c`) flag to specify a custom configuration file path. The default is `custom/conf/app.ini`.
</Tip>
## Starting the server
```bash
gogs web
```
The `web` command starts the HTTP server that powers the web UI, the REST API, and Git HTTP operations. Use the `--port` (`-p`) flag to override the default listening port.
## Administration
```bash
gogs admin <subcommand>
```
The `admin` command lets you perform maintenance tasks without going through the web interface. Available subcommands include:
| Subcommand | Purpose |
|---|---|
| `create-user` | Create a new user account (with optional `--admin` flag). |
| `delete-inactive-users` | Remove user accounts that were never activated. |
| `delete-repository-archives` | Clean up generated repository archive files. |
| `delete-missing-repositories` | Remove database records for repositories whose Git data is missing on disk. |
| `collect-garbage` | Run `git gc` across all repositories. |
| `rewrite-authorized-keys` | Regenerate the SSH `authorized_keys` file from the database. |
| `resync-hooks` | Re-write Git server-side hooks for all repositories. |
| `reinit-missing-repositories` | Re-initialize bare Git repositories that are missing on disk. |
<Warning>
`rewrite-authorized-keys` replaces the entire `authorized_keys` file. Any non-Gogs keys in that file will be lost.
</Warning>
## Importing data
```bash
gogs import locale --source <dir> --target <dir>
```
The `import` command helps you bring portable data from other Gogs installations into your local instance. Currently the only subcommand is `locale`, which merges locale files from a source directory into a target directory.
## Backup and restore
```bash
gogs backup
gogs restore --from <archive>
```
`backup` dumps the database, repositories, and related files into a single zip archive. `restore` imports everything back from an archive, which is useful for migrating Gogs to another server or switching database engines.
Both commands support `--database-only` and `--exclude-repos` flags to narrow the scope. `backup` additionally supports `--exclude-mirror-repos` and `--target` to control where the archive is saved.
## Internal commands
The `serv` and `hook` commands are used internally by the SSH and Git subsystems. You generally do not need to invoke them directly, but they are the reason Gogs can handle SSH authentication and server-side Git hooks without any external tooling.

View File

@@ -55,23 +55,11 @@ There are two ways to authenticate through the Gogs API. Requests that require a
</Warning> </Warning>
</Tab> </Tab>
<Tab title="Access token"> <Tab title="Access token">
Personal access tokens are the recommended way to authenticate. They can be sent via a request **header** or a **URL query parameter**. Personal access tokens must be sent via the `Authorization` request header.
**Using a header:**
```bash ```bash
curl -H "Authorization: token {YOUR_ACCESS_TOKEN}" https://gogs.example.com/api/v1/user/repos curl -H "Authorization: token {YOUR_ACCESS_TOKEN}" https://gogs.example.com/api/v1/user/repos
``` ```
**Using a query parameter:**
```bash
curl https://gogs.example.com/api/v1/user/repos?token={YOUR_ACCESS_TOKEN}
```
<Tip>
Using the `Authorization` header is preferred over the query parameter, as URLs may be logged by proxies and servers.
</Tip>
</Tab> </Tab>
</Tabs> </Tabs>

View File

@@ -5449,12 +5449,6 @@
"in": "header", "in": "header",
"name": "Authorization", "name": "Authorization",
"description": "Personal access token. Use format: token {YOUR_ACCESS_TOKEN}" "description": "Personal access token. Use format: token {YOUR_ACCESS_TOKEN}"
},
"TokenQuery": {
"type": "apiKey",
"in": "query",
"name": "token",
"description": "Access token as query parameter"
} }
}, },
"schemas": { "schemas": {

View File

@@ -13,7 +13,7 @@ Answers to common questions about Gogs configuration, administration, and usage.
You can change the listening port on the first run by passing the `-port` flag: You can change the listening port on the first run by passing the `-port` flag:
```bash ```bash
./gogs web -port 3001 gogs web -port 3001
``` ```
This flag also updates the port number shown on the install page, so pick the port you intend to keep using. This flag also updates the port number shown on the install page, so pick the port you intend to keep using.
@@ -58,7 +58,7 @@ Answers to common questions about Gogs configuration, administration, and usage.
```bash ```bash
su git su git
cd /home/git/gogs cd /home/git/gogs
./gogs admin create-user --name tmpuser --password tmppassword --admin --email tmp@example.com gogs admin create-user --name tmpuser --password tmppassword --admin --email tmp@example.com
``` ```
2. **Start Gogs** again, then log in as `tmpuser` in your browser. Navigate to **Admin Panel** > **Users**, click **Edit** next to the original administrator account, and set a new password. 2. **Start Gogs** again, then log in as `tmpuser` in your browser. Navigate to **Admin Panel** > **Users**, click **Edit** next to the original administrator account, and set a new password.

View File

@@ -50,7 +50,8 @@
"advancing/webhooks", "advancing/webhooks",
"advancing/git-lfs", "advancing/git-lfs",
"advancing/custom-templates", "advancing/custom-templates",
"advancing/localization" "advancing/localization",
"advancing/cli-reference"
] ]
}, },
{ {

View File

@@ -71,7 +71,7 @@ The work directory (parent of `custom/`) can also be overridden with `GOGS_WORK_
Every Gogs subcommand accepts `-c, --config` to point to a configuration file at a non-default location: Every Gogs subcommand accepts `-c, --config` to point to a configuration file at a non-default location:
```bash ```bash
./gogs web --config /etc/gogs/app.ini gogs web --config /etc/gogs/app.ini
``` ```
### What lives in `custom/` ### What lives in `custom/`

View File

@@ -57,7 +57,7 @@ If you choose to use MySQL or PostgreSQL as your database backend, you need to f
Release archives containing `mws` come with built-in Windows service support. If you prefer to manage the service using [NSSM](https://nssm.cc), download the standard version instead. Release archives containing `mws` come with built-in Windows service support. If you prefer to manage the service using [NSSM](https://nssm.cc), download the standard version instead.
</Note> </Note>
Once extracted the archive, run `./gogs web` to start the server. Use `./gogs web --help` to see all available options. Once extracted the archive, run `gogs web` to start the server. Use `gogs web --help` to see all available options.
</Tab> </Tab>
<Tab title="Docker"> <Tab title="Docker">
Two types of Docker images are provided: Two types of Docker images are provided:

20
go.mod
View File

@@ -1,6 +1,6 @@
module gogs.io/gogs module gogs.io/gogs
go 1.25.0 go 1.26.0
require ( require (
github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/DATA-DOG/go-sqlmock v1.5.2
@@ -22,14 +22,12 @@ require (
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/gogs/git-module v1.8.6 github.com/gogs/git-module v1.8.6
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a
github.com/google/go-github v17.0.0+incompatible github.com/google/go-github v17.0.0+incompatible
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/inbucket/html2text v1.0.0 github.com/inbucket/html2text v1.0.0
github.com/issue9/identicon v1.2.1 github.com/issue9/identicon v1.2.1
github.com/json-iterator/go v1.1.12
github.com/microcosm-cc/bluemonday v1.0.27 github.com/microcosm-cc/bluemonday v1.0.27
github.com/msteinert/pam v1.2.0 github.com/msteinert/pam v1.2.0
github.com/niklasfasching/go-org v1.9.1 github.com/niklasfasching/go-org v1.9.1
@@ -44,17 +42,16 @@ require (
github.com/unknwon/com v1.0.1 github.com/unknwon/com v1.0.1
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6 github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e
github.com/urfave/cli v1.22.17 github.com/urfave/cli/v3 v3.6.2
github.com/wneessen/go-mail v0.7.2
golang.org/x/crypto v0.47.0 golang.org/x/crypto v0.47.0
golang.org/x/image v0.35.0 golang.org/x/image v0.35.0
golang.org/x/net v0.48.0 golang.org/x/net v0.48.0
golang.org/x/text v0.33.0 golang.org/x/text v0.33.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
gopkg.in/macaron.v1 v1.5.1 gopkg.in/macaron.v1 v1.5.1
gorm.io/driver/mysql v1.5.2 gorm.io/driver/mysql v1.5.2
gorm.io/driver/postgres v1.6.0 gorm.io/driver/postgres v1.6.0
gorm.io/driver/sqlserver v1.4.1
gorm.io/gorm v1.25.12 gorm.io/gorm v1.25.12
unknwon.dev/clog/v2 v2.2.0 unknwon.dev/clog/v2 v2.2.0
xorm.io/builder v0.3.6 xorm.io/builder v0.3.6
@@ -75,9 +72,7 @@ require (
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denisenkom/go-mssqldb v0.12.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/djherbis/buffer v1.2.0 // indirect github.com/djherbis/buffer v1.2.0 // indirect
github.com/djherbis/nio/v3 v3.0.1 // indirect github.com/djherbis/nio/v3 v3.0.1 // indirect
@@ -91,8 +86,6 @@ require (
github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/google/go-querystring v1.0.0 // indirect github.com/google/go-querystring v1.0.0 // indirect
github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/css v1.0.1 // indirect
github.com/itchyny/gojq v0.12.11 // indirect github.com/itchyny/gojq v0.12.11 // indirect
@@ -112,9 +105,6 @@ require (
github.com/mattn/go-runewidth v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 // indirect github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 // indirect
github.com/microsoft/go-mssqldb v0.17.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
@@ -127,7 +117,6 @@ require (
github.com/prometheus/procfs v0.16.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
go.bobheadxi.dev/streamline v1.2.1 // indirect go.bobheadxi.dev/streamline v1.2.1 // indirect
@@ -138,7 +127,6 @@ require (
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect
gopkg.in/redis.v2 v2.3.2 // indirect gopkg.in/redis.v2 v2.3.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
@@ -148,5 +136,5 @@ require (
modernc.org/sqlite v1.39.0 // indirect modernc.org/sqlite v1.39.0 // indirect
) )
// +heroku goVersion go1.25 // +heroku goVersion go1.26
// +heroku install ./cmd/gogs // +heroku install ./cmd/gogs

81
go.sum
View File

@@ -2,20 +2,13 @@ bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4
bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U= bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0= gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0=
gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727/go.mod h1:h0OwsgcpJLSYtHcM5+Xciw9OEeuxi6ty4HDiO8C7aIY= gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727/go.mod h1:h0OwsgcpJLSYtHcM5+Xciw9OEeuxi6ty4HDiO8C7aIY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
@@ -61,16 +54,13 @@ github.com/couchbase/gomemcached v0.1.1/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9Hj
github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc= github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA=
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
github.com/derision-test/go-mockgen/v2 v2.1.1 h1:MXG9rzyvsrDBfa1a1GatvHjCrbmEug3hVt0rSOXipCw= github.com/derision-test/go-mockgen/v2 v2.1.1 h1:MXG9rzyvsrDBfa1a1GatvHjCrbmEug3hVt0rSOXipCw=
github.com/derision-test/go-mockgen/v2 v2.1.1/go.mod h1:cDK2Y9IF5roTJgugWV23IvlOJsllhDN5zxRDN+g4cZo= github.com/derision-test/go-mockgen/v2 v2.1.1/go.mod h1:cDK2Y9IF5roTJgugWV23IvlOJsllhDN5zxRDN+g4cZo=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
@@ -80,8 +70,6 @@ github.com/djherbis/buffer v1.2.0 h1:PH5Dd2ss0C7CRRhQCZ2u7MssF+No9ide8Ye71nPHcrQ
github.com/djherbis/buffer v1.2.0/go.mod h1:fjnebbZjCUpPinBRD+TDwXSOeNQ7fPQWLfGQqiAiUyE= github.com/djherbis/buffer v1.2.0/go.mod h1:fjnebbZjCUpPinBRD+TDwXSOeNQ7fPQWLfGQqiAiUyE=
github.com/djherbis/nio/v3 v3.0.1 h1:6wxhnuppteMa6RHA4L81Dq7ThkZH8SwnDzXDYy95vB4= github.com/djherbis/nio/v3 v3.0.1 h1:6wxhnuppteMa6RHA4L81Dq7ThkZH8SwnDzXDYy95vB4=
github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmWgZxOcmg= github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmWgZxOcmg=
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
@@ -156,21 +144,10 @@ github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQ
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk= github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
github.com/gogs/git-module v1.8.6 h1:4Io9vWZYQyIjdIPxfKgeYZXnDKNgydc6OZTxII5xCH4= github.com/gogs/git-module v1.8.6 h1:4Io9vWZYQyIjdIPxfKgeYZXnDKNgydc6OZTxII5xCH4=
github.com/gogs/git-module v1.8.6/go.mod h1:IiMSJqi8XH62Kjqjt5Rw8IawSo+DHfM2dDjkSzWLjhs= github.com/gogs/git-module v1.8.6/go.mod h1:IiMSJqi8XH62Kjqjt5Rw8IawSo+DHfM2dDjkSzWLjhs=
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4 h1:C7NryI/RQhsIWwC2bHN601P1wJKeuQ6U/UCOYTn3Cic=
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 h1:K02vod+sn3M1OOkdqi2tPxN2+xESK4qyITVQ3JkGEv4= github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 h1:K02vod+sn3M1OOkdqi2tPxN2+xESK4qyITVQ3JkGEv4=
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0/go.mod h1:Zas3BtO88pk1cwUfEYlvnl/CRwh0ybDxRWSwRjG8I3w= github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0/go.mod h1:Zas3BtO88pk1cwUfEYlvnl/CRwh0ybDxRWSwRjG8I3w=
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a h1:8DZwxETOVWIinYxDK+i6L+rMb7eGATGaakD6ZucfHVk= github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a h1:8DZwxETOVWIinYxDK+i6L+rMb7eGATGaakD6ZucfHVk=
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a/go.mod h1:TUIZ+29jodWQ8Gk6Pvtg4E09aMsc3C/VLZiVYfUhWQU= github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a/go.mod h1:TUIZ+29jodWQ8Gk6Pvtg4E09aMsc3C/VLZiVYfUhWQU=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -200,14 +177,11 @@ github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4r
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -263,11 +237,8 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
@@ -313,15 +284,6 @@ github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 h1:YocNLcTBdEd
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo= github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE= github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0= github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
@@ -366,9 +328,6 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -401,8 +360,6 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
@@ -428,19 +385,12 @@ github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cma
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
@@ -453,8 +403,10 @@ github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6 h1:sRrkJEHtNoaSvyXMbR
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ= github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e h1:Qf3QQl/zmEbWDajFEiisbKN83hLY+eq2MhbA0I1/two= github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e h1:Qf3QQl/zmEbWDajFEiisbKN83hLY+eq2MhbA0I1/two=
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e/go.mod h1:TBwoao3Q4Eb/cp+dHbXDfRTrZSsj/k7kLr2j1oWRWC0= github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e/go.mod h1:TBwoao3Q4Eb/cp+dHbXDfRTrZSsj/k7kLr2j1oWRWC0=
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ= github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8=
github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
@@ -479,8 +431,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -509,12 +459,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -548,18 +494,13 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -567,7 +508,6 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -611,8 +551,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e h1:wGA78yza6bu/mWcc4QfBuIEHEtc06xdiU0X8sY36yUU= gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e h1:wGA78yza6bu/mWcc4QfBuIEHEtc06xdiU0X8sY36yUU=
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e/go.mod h1:xsQCaysVCudhrYTfzYWe577fCe7Ceci+6qjO2Rdc0Z4= gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e/go.mod h1:xsQCaysVCudhrYTfzYWe577fCe7Ceci+6qjO2Rdc0Z4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -621,8 +559,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
@@ -639,22 +575,17 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=

View File

@@ -346,6 +346,7 @@ func Init(customConf string) error {
return errors.Wrap(err, "mapping [lfs] section") return errors.Wrap(err, "mapping [lfs] section")
} }
LFS.ObjectsPath = ensureAbs(LFS.ObjectsPath) LFS.ObjectsPath = ensureAbs(LFS.ObjectsPath)
LFS.ObjectsTempPath = ensureAbs(LFS.ObjectsTempPath)
handleDeprecated() handleDeprecated()
if !HookMode { if !HookMode {

View File

@@ -56,7 +56,6 @@ var (
User string User string
Password string Password string
DisableHELO bool `ini:"DISABLE_HELO"`
HELOHostname string `ini:"HELO_HOSTNAME"` HELOHostname string `ini:"HELO_HOSTNAME"`
SkipVerify bool SkipVerify bool
@@ -361,8 +360,9 @@ type DatabaseOpts struct {
var Database DatabaseOpts var Database DatabaseOpts
type LFSOpts struct { type LFSOpts struct {
Storage string Storage string
ObjectsPath string ObjectsPath string
ObjectsTempPath string
} }
// LFS settings // LFS settings
@@ -510,7 +510,6 @@ var (
UseSQLite3 bool UseSQLite3 bool
UseMySQL bool UseMySQL bool
UsePostgreSQL bool UsePostgreSQL bool
UseMSSQL bool
) )
// UsersAvatarPathPrefix is the path prefix to user avatars. // UsersAvatarPathPrefix is the path prefix to user avatars.

View File

@@ -88,7 +88,6 @@ HOST=smtp.mailgun.org:587
FROM=noreply@gogs.localhost FROM=noreply@gogs.localhost
USER=noreply@gogs.localhost USER=noreply@gogs.localhost
PASSWORD=87654321 PASSWORD=87654321
DISABLE_HELO=false
HELO_HOSTNAME= HELO_HOSTNAME=
SKIP_VERIFY=false SKIP_VERIFY=false
USE_CERTIFICATE=false USE_CERTIFICATE=false

View File

@@ -146,18 +146,12 @@ func authenticatedUserID(store AuthStore, c *macaron.Context, sess session.Store
// Check access token. // Check access token.
if isAPIPath(c.Req.URL.Path) { if isAPIPath(c.Req.URL.Path) {
tokenSHA := c.Query("token") var tokenSHA string
if len(tokenSHA) <= 0 { auHead := c.Req.Header.Get("Authorization")
tokenSHA = c.Query("access_token") if auHead != "" {
} auths := strings.Fields(auHead)
if tokenSHA == "" { if len(auths) == 2 && auths[0] == "token" {
// Well, check with header again. tokenSHA = auths[1]
auHead := c.Req.Header.Get("Authorization")
if len(auHead) > 0 {
auths := strings.Fields(auHead)
if len(auths) == 2 && auths[0] == "token" {
tokenSHA = auths[1]
}
} }
} }

View File

@@ -2,6 +2,7 @@ package database
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"path" "path"
"strconv" "strconv"
@@ -11,14 +12,13 @@ import (
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/gogs/git-module" "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
jsoniter "github.com/json-iterator/go"
"gorm.io/gorm" "gorm.io/gorm"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/lazyregexp" "gogs.io/gogs/internal/lazyregexp"
"gogs.io/gogs/internal/repoutil" "gogs.io/gogs/internal/repoutil"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
"gogs.io/gogs/internal/strutil" "gogs.io/gogs/internal/strutil"
"gogs.io/gogs/internal/testutil" "gogs.io/gogs/internal/testutil"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
@@ -230,7 +230,7 @@ func (s *ActionsStore) MirrorSyncPush(ctx context.Context, opts MirrorSyncPushOp
err = PrepareWebhooks( err = PrepareWebhooks(
opts.Repo, opts.Repo,
HookEventTypePush, HookEventTypePush,
&api.PushPayload{ &apiv1types.WebhookPushPayload{
Ref: opts.RefName, Ref: opts.RefName,
Before: opts.OldCommitID, Before: opts.OldCommitID,
After: opts.NewCommitID, After: opts.NewCommitID,
@@ -245,7 +245,7 @@ func (s *ActionsStore) MirrorSyncPush(ctx context.Context, opts MirrorSyncPushOp
return errors.Wrap(err, "prepare webhooks") return errors.Wrap(err, "prepare webhooks")
} }
data, err := jsoniter.Marshal(opts.Commits) data, err := json.Marshal(opts.Commits)
if err != nil { if err != nil {
return errors.Wrap(err, "marshal JSON") return errors.Wrap(err, "marshal JSON")
} }
@@ -496,10 +496,10 @@ func (s *ActionsStore) CommitRepo(ctx context.Context, opts CommitRepoOptions) e
err = PrepareWebhooks( err = PrepareWebhooks(
opts.Repo, opts.Repo,
HookEventTypeDelete, HookEventTypeDelete,
&api.DeletePayload{ &apiv1types.WebhookDeletePayload{
Ref: refName, Ref: refName,
RefType: "branch", RefType: "branch",
PusherType: api.PUSHER_TYPE_USER, PusherType: apiv1types.WebhookPusherTypeUser,
Repo: apiRepo, Repo: apiRepo,
Sender: apiPusher, Sender: apiPusher,
}, },
@@ -529,7 +529,7 @@ func (s *ActionsStore) CommitRepo(ctx context.Context, opts CommitRepoOptions) e
opts.Commits.Commits = opts.Commits.Commits[:conf.UI.FeedMaxCommitNum] opts.Commits.Commits = opts.Commits.Commits[:conf.UI.FeedMaxCommitNum]
} }
data, err := jsoniter.Marshal(opts.Commits) data, err := json.Marshal(opts.Commits)
if err != nil { if err != nil {
return errors.Wrap(err, "marshal JSON") return errors.Wrap(err, "marshal JSON")
} }
@@ -540,7 +540,7 @@ func (s *ActionsStore) CommitRepo(ctx context.Context, opts CommitRepoOptions) e
err = PrepareWebhooks( err = PrepareWebhooks(
opts.Repo, opts.Repo,
HookEventTypeCreate, HookEventTypeCreate,
&api.CreatePayload{ &apiv1types.WebhookCreatePayload{
Ref: refName, Ref: refName,
RefType: "branch", RefType: "branch",
DefaultBranch: opts.Repo.DefaultBranch, DefaultBranch: opts.Repo.DefaultBranch,
@@ -573,7 +573,7 @@ func (s *ActionsStore) CommitRepo(ctx context.Context, opts CommitRepoOptions) e
err = PrepareWebhooks( err = PrepareWebhooks(
opts.Repo, opts.Repo,
HookEventTypePush, HookEventTypePush,
&api.PushPayload{ &apiv1types.WebhookPushPayload{
Ref: opts.RefFullName, Ref: opts.RefFullName,
Before: opts.OldCommitID, Before: opts.OldCommitID,
After: opts.NewCommitID, After: opts.NewCommitID,
@@ -635,10 +635,10 @@ func (s *ActionsStore) PushTag(ctx context.Context, opts PushTagOptions) error {
err = PrepareWebhooks( err = PrepareWebhooks(
opts.Repo, opts.Repo,
HookEventTypeDelete, HookEventTypeDelete,
&api.DeletePayload{ &apiv1types.WebhookDeletePayload{
Ref: refName, Ref: refName,
RefType: "tag", RefType: "tag",
PusherType: api.PUSHER_TYPE_USER, PusherType: apiv1types.WebhookPusherTypeUser,
Repo: apiRepo, Repo: apiRepo,
Sender: apiPusher, Sender: apiPusher,
}, },
@@ -658,7 +658,7 @@ func (s *ActionsStore) PushTag(ctx context.Context, opts PushTagOptions) error {
err = PrepareWebhooks( err = PrepareWebhooks(
opts.Repo, opts.Repo,
HookEventTypeCreate, HookEventTypeCreate,
&api.CreatePayload{ &apiv1types.WebhookCreatePayload{
Ref: refName, Ref: refName,
RefType: "tag", RefType: "tag",
Sha: opts.NewCommitID, Sha: opts.NewCommitID,
@@ -848,7 +848,7 @@ func NewPushCommits() *PushCommits {
} }
} }
func (pcs *PushCommits) APIFormat(ctx context.Context, usersStore *UsersStore, repoPath, repoURL string) ([]*api.PayloadCommit, error) { func (pcs *PushCommits) APIFormat(ctx context.Context, usersStore *UsersStore, repoPath, repoURL string) ([]*apiv1types.WebhookPayloadCommit, error) {
// NOTE: We cache query results in case there are many commits in a single push. // NOTE: We cache query results in case there are many commits in a single push.
usernameByEmail := make(map[string]string) usernameByEmail := make(map[string]string)
getUsernameByEmail := func(email string) (string, error) { getUsernameByEmail := func(email string) (string, error) {
@@ -870,7 +870,7 @@ func (pcs *PushCommits) APIFormat(ctx context.Context, usersStore *UsersStore, r
return user.Name, nil return user.Name, nil
} }
commits := make([]*api.PayloadCommit, len(pcs.Commits)) commits := make([]*apiv1types.WebhookPayloadCommit, len(pcs.Commits))
for i, commit := range pcs.Commits { for i, commit := range pcs.Commits {
authorUsername, err := getUsernameByEmail(commit.AuthorEmail) authorUsername, err := getUsernameByEmail(commit.AuthorEmail)
if err != nil { if err != nil {
@@ -890,16 +890,16 @@ func (pcs *PushCommits) APIFormat(ctx context.Context, usersStore *UsersStore, r
} }
} }
commits[i] = &api.PayloadCommit{ commits[i] = &apiv1types.WebhookPayloadCommit{
ID: commit.Sha1, ID: commit.Sha1,
Message: commit.Message, Message: commit.Message,
URL: fmt.Sprintf("%s/commit/%s", repoURL, commit.Sha1), URL: fmt.Sprintf("%s/commit/%s", repoURL, commit.Sha1),
Author: &api.PayloadUser{ Author: &apiv1types.WebhookPayloadUser{
Name: commit.AuthorName, Name: commit.AuthorName,
Email: commit.AuthorEmail, Email: commit.AuthorEmail,
UserName: authorUsername, UserName: authorUsername,
}, },
Committer: &api.PayloadUser{ Committer: &apiv1types.WebhookPayloadUser{
Name: commit.CommitterName, Name: commit.CommitterName,
Email: commit.CommitterEmail, Email: commit.CommitterEmail,
UserName: committerUsername, UserName: committerUsername,

View File

@@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"encoding/json"
"fmt" "fmt"
"io" "io"
"os" "os"
@@ -13,7 +14,6 @@ import (
"sync" "sync"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
jsoniter "github.com/json-iterator/go"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/schema" "gorm.io/gorm/schema"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
@@ -99,7 +99,7 @@ func dumpTable(ctx context.Context, db *gorm.DB, table any, w io.Writer) error {
e.CreatedAt = e.CreatedAt.UTC() e.CreatedAt = e.CreatedAt.UTC()
} }
err = jsoniter.NewEncoder(w).Encode(elem) err = json.NewEncoder(w).Encode(elem)
if err != nil { if err != nil {
return errors.Wrap(err, "encode JSON") return errors.Wrap(err, "encode JSON")
} }
@@ -129,7 +129,7 @@ func dumpLegacyTables(ctx context.Context, dirPath string, verbose bool) error {
} }
if err = x.Context(ctx).Asc("id").Iterate(table, func(idx int, bean any) (err error) { if err = x.Context(ctx).Asc("id").Iterate(table, func(idx int, bean any) (err error) {
return jsoniter.NewEncoder(f).Encode(bean) return json.NewEncoder(f).Encode(bean)
}); err != nil { }); err != nil {
_ = f.Close() _ = f.Close()
return errors.Newf("dump table '%s': %v", tableName, err) return errors.Newf("dump table '%s': %v", tableName, err)
@@ -207,7 +207,7 @@ func importTable(ctx context.Context, db *gorm.DB, table any, r io.Reader) error
cleaned := bytes.ReplaceAll(scanner.Bytes(), []byte("\\u0000"), []byte("")) cleaned := bytes.ReplaceAll(scanner.Bytes(), []byte("\\u0000"), []byte(""))
elem := reflect.New(reflect.TypeOf(table).Elem()).Interface() elem := reflect.New(reflect.TypeOf(table).Elem()).Interface()
err = jsoniter.Unmarshal(cleaned, elem) err = json.Unmarshal(cleaned, elem)
if err != nil { if err != nil {
return errors.Wrap(err, "unmarshal JSON to struct") return errors.Wrap(err, "unmarshal JSON to struct")
} }
@@ -269,7 +269,7 @@ func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error
_, isInsertProcessor := table.(xorm.BeforeInsertProcessor) _, isInsertProcessor := table.(xorm.BeforeInsertProcessor)
scanner := bufio.NewScanner(f) scanner := bufio.NewScanner(f)
for scanner.Scan() { for scanner.Scan() {
if err = jsoniter.Unmarshal(scanner.Bytes(), table); err != nil { if err = json.Unmarshal(scanner.Bytes(), table); err != nil {
return errors.Newf("unmarshal to struct: %v", err) return errors.Newf("unmarshal to struct: %v", err)
} }
@@ -283,7 +283,7 @@ func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error
DeadlineUnix int64 DeadlineUnix int64
ClosedDateUnix int64 ClosedDateUnix int64
} }
if err = jsoniter.Unmarshal(scanner.Bytes(), &meta); err != nil { if err = json.Unmarshal(scanner.Bytes(), &meta); err != nil {
log.Error("Failed to unmarshal to map: %v", err) log.Error("Failed to unmarshal to map: %v", err)
} }

View File

@@ -11,10 +11,9 @@ import (
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"xorm.io/xorm" "xorm.io/xorm"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
) )
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference. // CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
@@ -136,8 +135,8 @@ func (c *Comment) HTMLURL() string {
// This method assumes following fields have been assigned with valid values: // This method assumes following fields have been assigned with valid values:
// Required - Poster, Issue // Required - Poster, Issue
func (c *Comment) APIFormat() *api.Comment { func (c *Comment) APIFormat() *apiv1types.IssueComment {
return &api.Comment{ return &apiv1types.IssueComment{
ID: c.ID, ID: c.ID,
HTMLURL: c.HTMLURL(), HTMLURL: c.HTMLURL(),
Poster: c.Poster.APIFormat(), Poster: c.Poster.APIFormat(),
@@ -347,8 +346,8 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri
} }
comment.Issue = issue comment.Issue = issue
if err = PrepareWebhooks(repo, HookEventTypeIssueComment, &api.IssueCommentPayload{ if err = PrepareWebhooks(repo, HookEventTypeIssueComment, &apiv1types.WebhookIssueCommentPayload{
Action: api.HOOK_ISSUE_COMMENT_CREATED, Action: apiv1types.WebhookIssueCommentCreated,
Issue: issue.APIFormat(), Issue: issue.APIFormat(),
Comment: comment.APIFormat(), Comment: comment.APIFormat(),
Repository: repo.APIFormatLegacy(nil), Repository: repo.APIFormatLegacy(nil),
@@ -483,12 +482,12 @@ func UpdateComment(doer *User, c *Comment, oldContent string) (err error) {
if err = c.Issue.LoadAttributes(); err != nil { if err = c.Issue.LoadAttributes(); err != nil {
log.Error("Issue.LoadAttributes [issue_id: %d]: %v", c.IssueID, err) log.Error("Issue.LoadAttributes [issue_id: %d]: %v", c.IssueID, err)
} else if err = PrepareWebhooks(c.Issue.Repo, HookEventTypeIssueComment, &api.IssueCommentPayload{ } else if err = PrepareWebhooks(c.Issue.Repo, HookEventTypeIssueComment, &apiv1types.WebhookIssueCommentPayload{
Action: api.HOOK_ISSUE_COMMENT_EDITED, Action: apiv1types.WebhookIssueCommentEdited,
Issue: c.Issue.APIFormat(), Issue: c.Issue.APIFormat(),
Comment: c.APIFormat(), Comment: c.APIFormat(),
Changes: &api.ChangesPayload{ Changes: &apiv1types.WebhookChangesPayload{
Body: &api.ChangesFromPayload{ Body: &apiv1types.WebhookChangesFromPayload{
From: oldContent, From: oldContent,
}, },
}, },
@@ -538,8 +537,8 @@ func DeleteCommentByID(doer *User, id int64) error {
if err = comment.Issue.LoadAttributes(); err != nil { if err = comment.Issue.LoadAttributes(); err != nil {
log.Error("Issue.LoadAttributes [issue_id: %d]: %v", comment.IssueID, err) log.Error("Issue.LoadAttributes [issue_id: %d]: %v", comment.IssueID, err)
} else if err = PrepareWebhooks(comment.Issue.Repo, HookEventTypeIssueComment, &api.IssueCommentPayload{ } else if err = PrepareWebhooks(comment.Issue.Repo, HookEventTypeIssueComment, &apiv1types.WebhookIssueCommentPayload{
Action: api.HOOK_ISSUE_COMMENT_DELETED, Action: apiv1types.WebhookIssueCommentDeleted,
Issue: comment.Issue.APIFormat(), Issue: comment.Issue.APIFormat(),
Comment: comment.APIFormat(), Comment: comment.APIFormat(),
Repository: comment.Issue.Repo.APIFormatLegacy(nil), Repository: comment.Issue.Repo.APIFormatLegacy(nil),

View File

@@ -91,8 +91,6 @@ func NewConnection(w logger.Writer) (*gorm.DB, error) {
db = db.Set("gorm:table_options", "ENGINE=InnoDB").Session(&gorm.Session{}) db = db.Set("gorm:table_options", "ENGINE=InnoDB").Session(&gorm.Session{})
case "sqlite3": case "sqlite3":
conf.UseSQLite3 = true conf.UseSQLite3 = true
case "mssql":
conf.UseMSSQL = true
default: default:
panic("unreachable") panic("unreachable")
} }

View File

@@ -11,11 +11,10 @@ import (
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"xorm.io/xorm" "xorm.io/xorm"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
) )
@@ -172,23 +171,23 @@ func (issue *Issue) HTMLURL() string {
} }
// State returns string representation of issue status. // State returns string representation of issue status.
func (issue *Issue) State() api.StateType { func (issue *Issue) State() apiv1types.IssueStateType {
if issue.IsClosed { if issue.IsClosed {
return api.STATE_CLOSED return apiv1types.IssueStateClosed
} }
return api.STATE_OPEN return apiv1types.IssueStateOpen
} }
// This method assumes some fields assigned with values: // This method assumes some fields assigned with values:
// Required - Poster, Labels, // Required - Poster, Labels,
// Optional - Milestone, Assignee, PullRequest // Optional - Milestone, Assignee, PullRequest
func (issue *Issue) APIFormat() *api.Issue { func (issue *Issue) APIFormat() *apiv1types.Issue {
apiLabels := make([]*api.Label, len(issue.Labels)) apiLabels := make([]*apiv1types.IssueLabel, len(issue.Labels))
for i := range issue.Labels { for i := range issue.Labels {
apiLabels[i] = issue.Labels[i].APIFormat() apiLabels[i] = issue.Labels[i].APIFormat()
} }
apiIssue := &api.Issue{ apiIssue := &apiv1types.Issue{
ID: issue.ID, ID: issue.ID,
Index: issue.Index, Index: issue.Index,
Poster: issue.Poster.APIFormat(), Poster: issue.Poster.APIFormat(),
@@ -208,7 +207,7 @@ func (issue *Issue) APIFormat() *api.Issue {
apiIssue.Assignee = issue.Assignee.APIFormat() apiIssue.Assignee = issue.Assignee.APIFormat()
} }
if issue.IsPull { if issue.IsPull {
apiIssue.PullRequest = &api.PullRequestMeta{ apiIssue.PullRequest = &apiv1types.PullRequestMeta{
HasMerged: issue.PullRequest.HasMerged, HasMerged: issue.PullRequest.HasMerged,
} }
if issue.PullRequest.HasMerged { if issue.PullRequest.HasMerged {
@@ -246,16 +245,16 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return return
} }
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
Action: api.HOOK_ISSUE_LABEL_UPDATED, Action: apiv1types.WebhookIssueLabelUpdated,
Index: issue.Index, Index: issue.Index,
PullRequest: issue.PullRequest.APIFormat(), PullRequest: issue.PullRequest.APIFormat(),
Repository: issue.Repo.APIFormatLegacy(nil), Repository: issue.Repo.APIFormatLegacy(nil),
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
}) })
} else { } else {
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
Action: api.HOOK_ISSUE_LABEL_UPDATED, Action: apiv1types.WebhookIssueLabelUpdated,
Index: issue.Index, Index: issue.Index,
Issue: issue.APIFormat(), Issue: issue.APIFormat(),
Repository: issue.Repo.APIFormatLegacy(nil), Repository: issue.Repo.APIFormatLegacy(nil),
@@ -359,16 +358,16 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return err return err
} }
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
Action: api.HOOK_ISSUE_LABEL_CLEARED, Action: apiv1types.WebhookIssueLabelCleared,
Index: issue.Index, Index: issue.Index,
PullRequest: issue.PullRequest.APIFormat(), PullRequest: issue.PullRequest.APIFormat(),
Repository: issue.Repo.APIFormatLegacy(nil), Repository: issue.Repo.APIFormatLegacy(nil),
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
}) })
} else { } else {
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
Action: api.HOOK_ISSUE_LABEL_CLEARED, Action: apiv1types.WebhookIssueLabelCleared,
Index: issue.Index, Index: issue.Index,
Issue: issue.APIFormat(), Issue: issue.APIFormat(),
Repository: issue.Repo.APIFormatLegacy(nil), Repository: issue.Repo.APIFormatLegacy(nil),
@@ -487,29 +486,29 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
if issue.IsPull { if issue.IsPull {
// Merge pull request calls issue.changeStatus so we need to handle separately. // Merge pull request calls issue.changeStatus so we need to handle separately.
issue.PullRequest.Issue = issue issue.PullRequest.Issue = issue
apiPullRequest := &api.PullRequestPayload{ apiPullRequest := &apiv1types.WebhookPullRequestPayload{
Index: issue.Index, Index: issue.Index,
PullRequest: issue.PullRequest.APIFormat(), PullRequest: issue.PullRequest.APIFormat(),
Repository: repo.APIFormatLegacy(nil), Repository: repo.APIFormatLegacy(nil),
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
} }
if isClosed { if isClosed {
apiPullRequest.Action = api.HOOK_ISSUE_CLOSED apiPullRequest.Action = apiv1types.WebhookIssueClosed
} else { } else {
apiPullRequest.Action = api.HOOK_ISSUE_REOPENED apiPullRequest.Action = apiv1types.WebhookIssueReopened
} }
err = PrepareWebhooks(repo, HookEventTypePullRequest, apiPullRequest) err = PrepareWebhooks(repo, HookEventTypePullRequest, apiPullRequest)
} else { } else {
apiIssues := &api.IssuesPayload{ apiIssues := &apiv1types.WebhookIssuesPayload{
Index: issue.Index, Index: issue.Index,
Issue: issue.APIFormat(), Issue: issue.APIFormat(),
Repository: repo.APIFormatLegacy(nil), Repository: repo.APIFormatLegacy(nil),
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
} }
if isClosed { if isClosed {
apiIssues.Action = api.HOOK_ISSUE_CLOSED apiIssues.Action = apiv1types.WebhookIssueClosed
} else { } else {
apiIssues.Action = api.HOOK_ISSUE_REOPENED apiIssues.Action = apiv1types.WebhookIssueReopened
} }
err = PrepareWebhooks(repo, HookEventTypeIssues, apiIssues) err = PrepareWebhooks(repo, HookEventTypeIssues, apiIssues)
} }
@@ -529,12 +528,12 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
if issue.IsPull { if issue.IsPull {
issue.PullRequest.Issue = issue issue.PullRequest.Issue = issue
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
Action: api.HOOK_ISSUE_EDITED, Action: apiv1types.WebhookIssueEdited,
Index: issue.Index, Index: issue.Index,
PullRequest: issue.PullRequest.APIFormat(), PullRequest: issue.PullRequest.APIFormat(),
Changes: &api.ChangesPayload{ Changes: &apiv1types.WebhookChangesPayload{
Title: &api.ChangesFromPayload{ Title: &apiv1types.WebhookChangesFromPayload{
From: oldTitle, From: oldTitle,
}, },
}, },
@@ -542,12 +541,12 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
}) })
} else { } else {
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
Action: api.HOOK_ISSUE_EDITED, Action: apiv1types.WebhookIssueEdited,
Index: issue.Index, Index: issue.Index,
Issue: issue.APIFormat(), Issue: issue.APIFormat(),
Changes: &api.ChangesPayload{ Changes: &apiv1types.WebhookChangesPayload{
Title: &api.ChangesFromPayload{ Title: &apiv1types.WebhookChangesFromPayload{
From: oldTitle, From: oldTitle,
}, },
}, },
@@ -571,12 +570,12 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
if issue.IsPull { if issue.IsPull {
issue.PullRequest.Issue = issue issue.PullRequest.Issue = issue
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
Action: api.HOOK_ISSUE_EDITED, Action: apiv1types.WebhookIssueEdited,
Index: issue.Index, Index: issue.Index,
PullRequest: issue.PullRequest.APIFormat(), PullRequest: issue.PullRequest.APIFormat(),
Changes: &api.ChangesPayload{ Changes: &apiv1types.WebhookChangesPayload{
Body: &api.ChangesFromPayload{ Body: &apiv1types.WebhookChangesFromPayload{
From: oldContent, From: oldContent,
}, },
}, },
@@ -584,12 +583,12 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
}) })
} else { } else {
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
Action: api.HOOK_ISSUE_EDITED, Action: apiv1types.WebhookIssueEdited,
Index: issue.Index, Index: issue.Index,
Issue: issue.APIFormat(), Issue: issue.APIFormat(),
Changes: &api.ChangesPayload{ Changes: &apiv1types.WebhookChangesPayload{
Body: &api.ChangesFromPayload{ Body: &apiv1types.WebhookChangesFromPayload{
From: oldContent, From: oldContent,
}, },
}, },
@@ -620,29 +619,29 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
isRemoveAssignee := err != nil isRemoveAssignee := err != nil
if issue.IsPull { if issue.IsPull {
issue.PullRequest.Issue = issue issue.PullRequest.Issue = issue
apiPullRequest := &api.PullRequestPayload{ apiPullRequest := &apiv1types.WebhookPullRequestPayload{
Index: issue.Index, Index: issue.Index,
PullRequest: issue.PullRequest.APIFormat(), PullRequest: issue.PullRequest.APIFormat(),
Repository: issue.Repo.APIFormatLegacy(nil), Repository: issue.Repo.APIFormatLegacy(nil),
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
} }
if isRemoveAssignee { if isRemoveAssignee {
apiPullRequest.Action = api.HOOK_ISSUE_UNASSIGNED apiPullRequest.Action = apiv1types.WebhookIssueUnassigned
} else { } else {
apiPullRequest.Action = api.HOOK_ISSUE_ASSIGNED apiPullRequest.Action = apiv1types.WebhookIssueAssigned
} }
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, apiPullRequest) err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, apiPullRequest)
} else { } else {
apiIssues := &api.IssuesPayload{ apiIssues := &apiv1types.WebhookIssuesPayload{
Index: issue.Index, Index: issue.Index,
Issue: issue.APIFormat(), Issue: issue.APIFormat(),
Repository: issue.Repo.APIFormatLegacy(nil), Repository: issue.Repo.APIFormatLegacy(nil),
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
} }
if isRemoveAssignee { if isRemoveAssignee {
apiIssues.Action = api.HOOK_ISSUE_UNASSIGNED apiIssues.Action = apiv1types.WebhookIssueUnassigned
} else { } else {
apiIssues.Action = api.HOOK_ISSUE_ASSIGNED apiIssues.Action = apiv1types.WebhookIssueAssigned
} }
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, apiIssues) err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, apiIssues)
} }
@@ -789,8 +788,8 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
log.Error("MailParticipants: %v", err) log.Error("MailParticipants: %v", err)
} }
if err = PrepareWebhooks(repo, HookEventTypeIssues, &api.IssuesPayload{ if err = PrepareWebhooks(repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
Action: api.HOOK_ISSUE_OPENED, Action: apiv1types.WebhookIssueOpened,
Index: issue.Index, Index: issue.Index,
Issue: issue.APIFormat(), Issue: issue.APIFormat(),
Repository: repo.APIFormatLegacy(nil), Repository: repo.APIFormatLegacy(nil),

View File

@@ -9,10 +9,10 @@ import (
"xorm.io/xorm" "xorm.io/xorm"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/lazyregexp" "gogs.io/gogs/internal/lazyregexp"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
) )
@@ -62,8 +62,8 @@ type Label struct {
IsChecked bool `xorm:"-" json:"-" gorm:"-"` IsChecked bool `xorm:"-" json:"-" gorm:"-"`
} }
func (l *Label) APIFormat() *api.Label { func (l *Label) APIFormat() *apiv1types.IssueLabel {
return &api.Label{ return &apiv1types.IssueLabel{
ID: l.ID, ID: l.ID,
Name: l.Name, Name: l.Name,
Color: strings.TrimLeft(l.Color, "#"), Color: strings.TrimLeft(l.Color, "#"),

View File

@@ -151,7 +151,9 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
names = append(names, issue.Assignee.Name) names = append(names, issue.Assignee.Name)
} }
} }
email.SendIssueCommentMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos) if err = email.SendIssueCommentMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos); err != nil {
return errors.Wrap(err, "send issue comment mail")
}
// Mail mentioned people and exclude watchers. // Mail mentioned people and exclude watchers.
names = append(names, doer.Name) names = append(names, doer.Name)
@@ -168,7 +170,9 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
if err != nil { if err != nil {
return errors.Wrap(err, "get mailable emails by usernames") return errors.Wrap(err, "get mailable emails by usernames")
} }
email.SendIssueMentionMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos) if err = email.SendIssueMentionMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos); err != nil {
return errors.Wrap(err, "send issue mention mail")
}
return nil return nil
} }

View File

@@ -2,12 +2,12 @@ package database
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"strconv" "strconv"
"time" "time"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
jsoniter "github.com/json-iterator/go"
"gorm.io/gorm" "gorm.io/gorm"
"gogs.io/gogs/internal/auth" "gogs.io/gogs/internal/auth"
@@ -41,7 +41,8 @@ func (s *LoginSource) BeforeSave(_ *gorm.DB) (err error) {
if s.Provider == nil { if s.Provider == nil {
return nil return nil
} }
s.Config, err = jsoniter.MarshalToString(s.Provider.Config()) data, err := json.Marshal(s.Provider.Config())
s.Config = string(data)
return err return err
} }
@@ -72,7 +73,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
switch s.Type { switch s.Type {
case auth.LDAP: case auth.LDAP:
var cfg ldap.Config var cfg ldap.Config
err := jsoniter.UnmarshalFromString(s.Config, &cfg) err := json.Unmarshal([]byte(s.Config), &cfg)
if err != nil { if err != nil {
return err return err
} }
@@ -80,7 +81,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
case auth.DLDAP: case auth.DLDAP:
var cfg ldap.Config var cfg ldap.Config
err := jsoniter.UnmarshalFromString(s.Config, &cfg) err := json.Unmarshal([]byte(s.Config), &cfg)
if err != nil { if err != nil {
return err return err
} }
@@ -88,7 +89,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
case auth.SMTP: case auth.SMTP:
var cfg smtp.Config var cfg smtp.Config
err := jsoniter.UnmarshalFromString(s.Config, &cfg) err := json.Unmarshal([]byte(s.Config), &cfg)
if err != nil { if err != nil {
return err return err
} }
@@ -96,7 +97,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
case auth.PAM: case auth.PAM:
var cfg pam.Config var cfg pam.Config
err := jsoniter.UnmarshalFromString(s.Config, &cfg) err := json.Unmarshal([]byte(s.Config), &cfg)
if err != nil { if err != nil {
return err return err
} }
@@ -104,7 +105,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
case auth.GitHub: case auth.GitHub:
var cfg github.Config var cfg github.Config
err := jsoniter.UnmarshalFromString(s.Config, &cfg) err := json.Unmarshal([]byte(s.Config), &cfg)
if err != nil { if err != nil {
return err return err
} }
@@ -112,7 +113,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
case auth.Mock: case auth.Mock:
var cfg mockProviderConfig var cfg mockProviderConfig
err := jsoniter.UnmarshalFromString(s.Config, &cfg) err := json.Unmarshal([]byte(s.Config), &cfg)
if err != nil { if err != nil {
return err return err
} }
@@ -215,7 +216,8 @@ func (s *LoginSourcesStore) Create(ctx context.Context, opts CreateLoginSourceOp
IsActived: opts.Activated, IsActived: opts.Activated,
IsDefault: opts.Default, IsDefault: opts.Default,
} }
source.Config, err = jsoniter.MarshalToString(opts.Config) data, err := json.Marshal(opts.Config)
source.Config = string(data)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -102,11 +102,11 @@ You can migrate your older database using a previous release, then you can upgra
Please save following instructions to somewhere and start working: Please save following instructions to somewhere and start working:
- If you were using below 0.6.0 (e.g. 0.5.x), download last supported archive from following link: - If you were using below 0.6.0 (e.g. 0.5.x), download last supported archive from following link:
https://gogs.io/gogs/releases/tag/v0.7.33 https://github.com/gogs/gogs/releases/tag/v0.7.33
- If you were using below 0.7.0 (e.g. 0.6.x), download last supported archive from following link: - If you were using below 0.7.0 (e.g. 0.6.x), download last supported archive from following link:
https://gogs.io/gogs/releases/tag/v0.9.141 https://github.com/gogs/gogs/releases/tag/v0.9.141
- If you were using below 0.11.55 (e.g. 0.9.141), download last supported archive from following link: - If you were using below 0.11.55 (e.g. 0.9.141), download last supported archive from following link:
https://gogs.io/gogs/releases/tag/v0.12.0 https://github.com/gogs/gogs/releases/tag/v0.12.0
Once finished downloading: Once finished downloading:

View File

@@ -8,10 +8,10 @@ import (
"xorm.io/xorm" "xorm.io/xorm"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
) )
// Milestone represents a milestone of repository. // Milestone represents a milestone of repository.
@@ -72,19 +72,19 @@ func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
} }
// State returns string representation of milestone status. // State returns string representation of milestone status.
func (m *Milestone) State() api.StateType { func (m *Milestone) State() apiv1types.IssueStateType {
if m.IsClosed { if m.IsClosed {
return api.STATE_CLOSED return apiv1types.IssueStateClosed
} }
return api.STATE_OPEN return apiv1types.IssueStateOpen
} }
func (m *Milestone) ChangeStatus(isClosed bool) error { func (m *Milestone) ChangeStatus(isClosed bool) error {
return ChangeMilestoneStatus(m, isClosed) return ChangeMilestoneStatus(m, isClosed)
} }
func (m *Milestone) APIFormat() *api.Milestone { func (m *Milestone) APIFormat() *apiv1types.IssueMilestone {
apiMilestone := &api.Milestone{ apiMilestone := &apiv1types.IssueMilestone{
ID: m.ID, ID: m.ID,
State: m.State(), State: m.State(),
Title: m.Name, Title: m.Name,
@@ -343,11 +343,11 @@ func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err
return errors.Newf("commit: %v", err) return errors.Newf("commit: %v", err)
} }
var hookAction api.HookIssueAction var hookAction apiv1types.WebhookIssueAction
if issue.MilestoneID > 0 { if issue.MilestoneID > 0 {
hookAction = api.HOOK_ISSUE_MILESTONED hookAction = apiv1types.WebhookIssueMilestoned
} else { } else {
hookAction = api.HOOK_ISSUE_DEMILESTONED hookAction = apiv1types.WebhookIssueDemilestoned
} }
if issue.IsPull { if issue.IsPull {
@@ -356,7 +356,7 @@ func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return err return err
} }
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
Action: hookAction, Action: hookAction,
Index: issue.Index, Index: issue.Index,
PullRequest: issue.PullRequest.APIFormat(), PullRequest: issue.PullRequest.APIFormat(),
@@ -364,7 +364,7 @@ func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err
Sender: doer.APIFormat(), Sender: doer.APIFormat(),
}) })
} else { } else {
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{ err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
Action: hookAction, Action: hookAction,
Index: issue.Index, Index: issue.Index,
Issue: issue.APIFormat(), Issue: issue.APIFormat(),

View File

@@ -94,11 +94,6 @@ func getEngine() (*xorm.Engine, error) {
conf.Database.User, conf.Database.Password, host, port, conf.Database.Name, conf.Database.SSLMode, conf.Database.Schema) conf.Database.User, conf.Database.Password, host, port, conf.Database.Name, conf.Database.SSLMode, conf.Database.Schema)
driver = "pgx" driver = "pgx"
case "mssql":
conf.UseMSSQL = true
host, port := dbutil.ParseMSSQLHostPort(conf.Database.Host)
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, conf.Database.Name, conf.Database.User, conf.Database.Password)
case "sqlite3": case "sqlite3":
if err := os.MkdirAll(path.Dir(conf.Database.Path), os.ModePerm); err != nil { if err := os.MkdirAll(path.Dir(conf.Database.Path), os.ModePerm); err != nil {
return nil, errors.Newf("create directories: %v", err) return nil, errors.Newf("create directories: %v", err)

View File

@@ -30,7 +30,7 @@ type Team struct {
func (t *Team) AfterSet(colName string, _ xorm.Cell) { func (t *Team) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "num_repos": case "num_repos":
// LEGACY [1.0]: this is backward compatibility bug fix for https://gogs.io/gogs/issues/3671 // LEGACY [1.0]: this is backward compatibility bug fix for https://github.com/gogs/gogs/issues/3671
if t.NumRepos < 0 { if t.NumRepos < 0 {
t.NumRepos = 0 t.NumRepos = 0
} }

View File

@@ -13,12 +13,12 @@ import (
"xorm.io/xorm" "xorm.io/xorm"
"github.com/gogs/git-module" "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/osutil" "gogs.io/gogs/internal/osutil"
"gogs.io/gogs/internal/process" "gogs.io/gogs/internal/process"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
"gogs.io/gogs/internal/sync" "gogs.io/gogs/internal/sync"
) )
@@ -127,11 +127,11 @@ func (pr *PullRequest) LoadIssue() (err error) {
// This method assumes following fields have been assigned with valid values: // This method assumes following fields have been assigned with valid values:
// Required - Issue, BaseRepo // Required - Issue, BaseRepo
// Optional - HeadRepo, Merger // Optional - HeadRepo, Merger
func (pr *PullRequest) APIFormat() *api.PullRequest { func (pr *PullRequest) APIFormat() *apiv1types.PullRequest {
// In case of head repo has been deleted. // In case of head repo has been deleted.
var apiHeadRepo *api.Repository var apiHeadRepo *apiv1types.Repository
if pr.HeadRepo == nil { if pr.HeadRepo == nil {
apiHeadRepo = &api.Repository{ apiHeadRepo = &apiv1types.Repository{
Name: "deleted", Name: "deleted",
} }
} else { } else {
@@ -139,7 +139,7 @@ func (pr *PullRequest) APIFormat() *api.PullRequest {
} }
apiIssue := pr.Issue.APIFormat() apiIssue := pr.Issue.APIFormat()
apiPullRequest := &api.PullRequest{ apiPullRequest := &apiv1types.PullRequest{
ID: pr.ID, ID: pr.ID,
Index: pr.Index, Index: pr.Index,
Poster: apiIssue.Poster, Poster: apiIssue.Poster,
@@ -341,8 +341,8 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
log.Error("LoadAttributes: %v", err) log.Error("LoadAttributes: %v", err)
return nil return nil
} }
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{ if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
Action: api.HOOK_ISSUE_CLOSED, Action: apiv1types.WebhookIssueClosed,
Index: pr.Index, Index: pr.Index,
PullRequest: pr.APIFormat(), PullRequest: pr.APIFormat(),
Repository: pr.Issue.Repo.APIFormatLegacy(nil), Repository: pr.Issue.Repo.APIFormatLegacy(nil),
@@ -376,7 +376,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
return nil return nil
} }
p := &api.PushPayload{ p := &apiv1types.WebhookPushPayload{
Ref: git.RefsHeads + pr.BaseBranch, Ref: git.RefsHeads + pr.BaseBranch,
Before: pr.MergeBase, Before: pr.MergeBase,
After: mergeCommit.ID.String(), After: mergeCommit.ID.String(),
@@ -500,8 +500,8 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
pr.Issue = pull pr.Issue = pull
pull.PullRequest = pr pull.PullRequest = pr
if err = PrepareWebhooks(repo, HookEventTypePullRequest, &api.PullRequestPayload{ if err = PrepareWebhooks(repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
Action: api.HOOK_ISSUE_OPENED, Action: apiv1types.WebhookIssueOpened,
Index: pull.Index, Index: pull.Index,
PullRequest: pr.APIFormat(), PullRequest: pr.APIFormat(),
Repository: repo.APIFormatLegacy(nil), Repository: repo.APIFormatLegacy(nil),
@@ -792,8 +792,8 @@ func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool
log.Error("LoadAttributes: %v", err) log.Error("LoadAttributes: %v", err)
continue continue
} }
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{ if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
Action: api.HOOK_ISSUE_SYNCHRONIZED, Action: apiv1types.WebhookIssueSynchronized,
Index: pr.Issue.Index, Index: pr.Issue.Index,
PullRequest: pr.Issue.PullRequest.APIFormat(), PullRequest: pr.Issue.PullRequest.APIFormat(),
Repository: pr.Issue.Repo.APIFormatLegacy(nil), Repository: pr.Issue.Repo.APIFormatLegacy(nil),

View File

@@ -11,10 +11,9 @@ import (
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/gogs/git-module" "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/process" apiv1types "gogs.io/gogs/internal/route/api/v1/types"
) )
// Release represents a release of repository. // Release represents a release of repository.
@@ -90,8 +89,8 @@ func (r *Release) LoadAttributes() error {
// This method assumes some fields assigned with values: // This method assumes some fields assigned with values:
// Required - Publisher // Required - Publisher
func (r *Release) APIFormat() *api.Release { func (r *Release) APIFormat() *apiv1types.RepositoryRelease {
return &api.Release{ return &apiv1types.RepositoryRelease{
ID: r.ID, ID: r.ID,
TagName: r.TagName, TagName: r.TagName,
TargetCommitish: r.Target, TargetCommitish: r.Target,
@@ -147,8 +146,8 @@ func createTag(gitRepo *git.Repository, r *Release) error {
} }
func (r *Release) preparePublishWebhooks() { func (r *Release) preparePublishWebhooks() {
if err := PrepareWebhooks(r.Repo, HookEventTypeRelease, &api.ReleasePayload{ if err := PrepareWebhooks(r.Repo, HookEventTypeRelease, &apiv1types.WebhookReleasePayload{
Action: api.HOOK_RELEASE_PUBLISHED, Action: apiv1types.WebhookReleasePublished,
Release: r.APIFormat(), Release: r.APIFormat(),
Repository: r.Repo.APIFormatLegacy(nil), Repository: r.Repo.APIFormatLegacy(nil),
Sender: r.Publisher.APIFormat(), Sender: r.Publisher.APIFormat(),
@@ -359,11 +358,13 @@ func DeleteReleaseOfRepoByID(repoID, id int64) error {
return errors.Newf("GetRepositoryByID: %v", err) return errors.Newf("GetRepositoryByID: %v", err)
} }
_, stderr, err := process.ExecDir(-1, repo.RepoPath(), gitRepo, err := git.Open(repo.RepoPath())
fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID), if err != nil {
"git", "tag", "-d", rel.TagName) return errors.Newf("open repository: %v", err)
if err != nil && !strings.Contains(stderr, "not found") { }
return errors.Newf("git tag -d: %v - %s", err, stderr) err = gitRepo.DeleteTag(rel.TagName)
if err != nil && !strings.Contains(err.Error(), "not found") {
return errors.Newf("delete tag: %v", err)
} }
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil { if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {

View File

@@ -26,7 +26,6 @@ import (
"xorm.io/xorm" "xorm.io/xorm"
"github.com/gogs/git-module" "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
embedConf "gogs.io/gogs/conf" embedConf "gogs.io/gogs/conf"
"gogs.io/gogs/internal/avatar" "gogs.io/gogs/internal/avatar"
@@ -37,6 +36,7 @@ import (
"gogs.io/gogs/internal/osutil" "gogs.io/gogs/internal/osutil"
"gogs.io/gogs/internal/process" "gogs.io/gogs/internal/process"
"gogs.io/gogs/internal/repoutil" "gogs.io/gogs/internal/repoutil"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
"gogs.io/gogs/internal/semverutil" "gogs.io/gogs/internal/semverutil"
"gogs.io/gogs/internal/strutil" "gogs.io/gogs/internal/strutil"
"gogs.io/gogs/internal/sync" "gogs.io/gogs/internal/sync"
@@ -162,6 +162,7 @@ func NewRepoContext() {
} }
RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(conf.Server.AppDataPath, "tmp")) RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(conf.Server.AppDataPath, "tmp"))
RemoveAllWithNotice("Clean up LFS temporary data", conf.LFS.ObjectsTempPath)
} }
// Repository contains information of a repository. // Repository contains information of a repository.
@@ -376,9 +377,9 @@ func (r *Repository) DeleteAvatar() error {
// Arguments that are allowed to be nil: permission // Arguments that are allowed to be nil: permission
// //
// Deprecated: Use APIFormat instead. // Deprecated: Use APIFormat instead.
func (r *Repository) APIFormatLegacy(permission *api.Permission, user ...*User) *api.Repository { func (r *Repository) APIFormatLegacy(permission *apiv1types.RepositoryPermission, user ...*User) *apiv1types.Repository {
cloneLink := r.CloneLink() cloneLink := r.CloneLink()
apiRepo := &api.Repository{ apiRepo := &apiv1types.Repository{
ID: r.ID, ID: r.ID,
Owner: r.Owner.APIFormat(), Owner: r.Owner.APIFormat(),
Name: r.Name, Name: r.Name,
@@ -405,7 +406,7 @@ func (r *Repository) APIFormatLegacy(permission *api.Permission, user ...*User)
// AvatarUrl: r.AvatarLink(), // AvatarUrl: r.AvatarLink(),
} }
if r.IsFork { if r.IsFork {
p := &api.Permission{Pull: true} p := &apiv1types.RepositoryPermission{Pull: true}
if len(user) != 0 { if len(user) != 0 {
accessMode := Handle.Permissions().AccessMode( accessMode := Handle.Permissions().AccessMode(
context.TODO(), context.TODO(),
@@ -2415,7 +2416,7 @@ func GetWatchers(repoID int64) ([]*Watch, error) {
func (r *Repository) GetWatchers(page int) ([]*User, error) { func (r *Repository) GetWatchers(page int) ([]*User, error) {
users := make([]*User, 0, ItemsPerPage) users := make([]*User, 0, ItemsPerPage)
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("watch.repo_id=?", r.ID) sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("watch.repo_id=?", r.ID)
if conf.UsePostgreSQL || conf.UseMSSQL { if conf.UsePostgreSQL {
sess = sess.Join("LEFT", "watch", `"user".id=watch.user_id`) sess = sess.Join("LEFT", "watch", `"user".id=watch.user_id`)
} else { } else {
sess = sess.Join("LEFT", "watch", "user.id=watch.user_id") sess = sess.Join("LEFT", "watch", "user.id=watch.user_id")
@@ -2515,7 +2516,7 @@ func IsStaring(userID, repoID int64) bool {
func (r *Repository) GetStargazers(page int) ([]*User, error) { func (r *Repository) GetStargazers(page int) ([]*User, error) {
users := make([]*User, 0, ItemsPerPage) users := make([]*User, 0, ItemsPerPage)
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("star.repo_id=?", r.ID) sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("star.repo_id=?", r.ID)
if conf.UsePostgreSQL || conf.UseMSSQL { if conf.UsePostgreSQL {
sess = sess.Join("LEFT", "star", `"user".id=star.uid`) sess = sess.Join("LEFT", "star", `"user".id=star.uid`)
} else { } else {
sess = sess.Join("LEFT", "star", "user.id=star.uid") sess = sess.Join("LEFT", "star", "user.id=star.uid")
@@ -2608,7 +2609,7 @@ func ForkRepository(doer, owner *User, baseRepo *Repository, name, desc string)
if err = repo.UpdateSize(); err != nil { if err = repo.UpdateSize(); err != nil {
log.Error("UpdateSize [repo_id: %d]: %v", repo.ID, err) log.Error("UpdateSize [repo_id: %d]: %v", repo.ID, err)
} }
if err = PrepareWebhooks(baseRepo, HookEventTypeFork, &api.ForkPayload{ if err = PrepareWebhooks(baseRepo, HookEventTypeFork, &apiv1types.WebhookForkPayload{
Forkee: repo.APIFormatLegacy(nil), Forkee: repo.APIFormatLegacy(nil),
Repo: baseRepo.APIFormatLegacy(nil), Repo: baseRepo.APIFormatLegacy(nil),
Sender: doer.APIFormat(), Sender: doer.APIFormat(),

View File

@@ -1,10 +1,10 @@
package database package database
import ( import (
"github.com/cockroachdb/errors"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"github.com/cockroachdb/errors" apiv1types "gogs.io/gogs/internal/route/api/v1/types"
api "github.com/gogs/go-gogs-client"
) )
// Collaboration represent the relation between an individual and a repository. // Collaboration represent the relation between an individual and a repository.
@@ -87,10 +87,10 @@ type Collaborator struct {
Collaboration *Collaboration Collaboration *Collaboration
} }
func (c *Collaborator) APIFormat() *api.Collaborator { func (c *Collaborator) APIFormat() *apiv1types.RepositoryCollaborator {
return &api.Collaborator{ return &apiv1types.RepositoryCollaborator{
User: c.User.APIFormat(), User: c.User.APIFormat(),
Permissions: api.Permission{ Permissions: apiv1types.RepositoryPermission{
Admin: c.Collaboration.Mode >= AccessModeAdmin, Admin: c.Collaboration.Mode >= AccessModeAdmin,
Push: c.Collaboration.Mode >= AccessModeWrite, Push: c.Collaboration.Mode >= AccessModeWrite,
Pull: c.Collaboration.Mode >= AccessModeRead, Pull: c.Collaboration.Mode >= AccessModeRead,

View File

@@ -7,11 +7,11 @@ import (
"time" "time"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
"gorm.io/gorm" "gorm.io/gorm"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/repoutil" "gogs.io/gogs/internal/repoutil"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
) )
// BeforeCreate implements the GORM create hook. // BeforeCreate implements the GORM create hook.
@@ -36,19 +36,19 @@ func (r *Repository) AfterFind(_ *gorm.DB) error {
} }
type RepositoryAPIFormatOptions struct { type RepositoryAPIFormatOptions struct {
Permission *api.Permission Permission *apiv1types.RepositoryPermission
Parent *api.Repository Parent *apiv1types.Repository
} }
// APIFormat returns the API format of a repository. // APIFormat returns the API format of a repository.
func (r *Repository) APIFormat(owner *User, opts ...RepositoryAPIFormatOptions) *api.Repository { func (r *Repository) APIFormat(owner *User, opts ...RepositoryAPIFormatOptions) *apiv1types.Repository {
var opt RepositoryAPIFormatOptions var opt RepositoryAPIFormatOptions
if len(opts) > 0 { if len(opts) > 0 {
opt = opts[0] opt = opts[0]
} }
cloneLink := repoutil.NewCloneLink(owner.Name, r.Name, false) cloneLink := repoutil.NewCloneLink(owner.Name, r.Name, false)
return &api.Repository{ return &apiv1types.Repository{
ID: r.ID, ID: r.ID,
Owner: owner.APIFormat(), Owner: owner.APIFormat(),
Name: r.Name, Name: r.Name,

View File

@@ -11,7 +11,6 @@ import (
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/go-macaron/binding" "github.com/go-macaron/binding"
api "github.com/gogs/go-gogs-client"
"gorm.io/gorm" "gorm.io/gorm"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
@@ -23,6 +22,7 @@ import (
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/osutil" "gogs.io/gogs/internal/osutil"
"gogs.io/gogs/internal/repoutil" "gogs.io/gogs/internal/repoutil"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
"gogs.io/gogs/internal/strutil" "gogs.io/gogs/internal/strutil"
"gogs.io/gogs/internal/tool" "gogs.io/gogs/internal/tool"
"gogs.io/gogs/internal/userutil" "gogs.io/gogs/internal/userutil"
@@ -1297,14 +1297,14 @@ func (u *User) IsOrganization() bool {
} }
// APIFormat returns the API format of a user. // APIFormat returns the API format of a user.
func (u *User) APIFormat() *api.User { func (u *User) APIFormat() *apiv1types.User {
return &api.User{ return &apiv1types.User{
ID: u.ID, ID: u.ID,
UserName: u.Name, UserName: u.Name,
Login: u.Name, Login: u.Name,
FullName: u.FullName, FullName: u.FullName,
Email: u.Email, Email: u.Email,
AvatarUrl: u.AvatarURL(), AvatarURL: u.AvatarURL(),
} }
} }

View File

@@ -5,6 +5,7 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/url" "net/url"
@@ -13,16 +14,14 @@ import (
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/google/uuid" "github.com/google/uuid"
jsoniter "github.com/json-iterator/go"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"xorm.io/xorm" "xorm.io/xorm"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/httplib" "gogs.io/gogs/internal/httplib"
"gogs.io/gogs/internal/netutil" "gogs.io/gogs/internal/netutil"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
"gogs.io/gogs/internal/sync" "gogs.io/gogs/internal/sync"
"gogs.io/gogs/internal/testutil" "gogs.io/gogs/internal/testutil"
) )
@@ -126,7 +125,7 @@ func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "events": case "events":
w.HookEvent = &HookEvent{} w.HookEvent = &HookEvent{}
if err = jsoniter.Unmarshal([]byte(w.Events), w.HookEvent); err != nil { if err = json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
log.Error("Unmarshal [%d]: %v", w.ID, err) log.Error("Unmarshal [%d]: %v", w.ID, err)
} }
case "created_unix": case "created_unix":
@@ -138,7 +137,7 @@ func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
func (w *Webhook) SlackMeta() *SlackMeta { func (w *Webhook) SlackMeta() *SlackMeta {
s := &SlackMeta{} s := &SlackMeta{}
if err := jsoniter.Unmarshal([]byte(w.Meta), s); err != nil { if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("Failed to get Slack meta [webhook_id: %d]: %v", w.ID, err) log.Error("Failed to get Slack meta [webhook_id: %d]: %v", w.ID, err)
} }
return s return s
@@ -151,7 +150,7 @@ func (w *Webhook) History(page int) ([]*HookTask, error) {
// UpdateEvent handles conversion from HookEvent to Events. // UpdateEvent handles conversion from HookEvent to Events.
func (w *Webhook) UpdateEvent() error { func (w *Webhook) UpdateEvent() error {
data, err := jsoniter.Marshal(w.HookEvent) data, err := json.Marshal(w.HookEvent)
w.Events = string(data) w.Events = string(data)
return err return err
} }
@@ -430,21 +429,21 @@ type HookResponse struct {
// HookTask represents a hook task. // HookTask represents a hook task.
type HookTask struct { type HookTask struct {
ID int64 ID int64
RepoID int64 `xorm:"INDEX"` RepoID int64 `xorm:"INDEX"`
HookID int64 HookID int64
UUID string UUID string
Type HookTaskType Type HookTaskType
URL string `xorm:"TEXT"` URL string `xorm:"TEXT"`
Signature string `xorm:"TEXT"` Signature string `xorm:"TEXT"`
api.Payloader `xorm:"-" json:"-" gorm:"-"` apiv1types.WebhookPayloader `xorm:"-" json:"-" gorm:"-"`
PayloadContent string `xorm:"TEXT"` PayloadContent string `xorm:"TEXT"`
ContentType HookContentType ContentType HookContentType
EventType HookEventType EventType HookEventType
IsSSL bool IsSSL bool
IsDelivered bool IsDelivered bool
Delivered int64 Delivered int64
DeliveredString string `xorm:"-" json:"-" gorm:"-"` DeliveredString string `xorm:"-" json:"-" gorm:"-"`
// History info. // History info.
IsSucceed bool IsSucceed bool
@@ -475,7 +474,7 @@ func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
} }
t.RequestInfo = &HookRequest{} t.RequestInfo = &HookRequest{}
if err = jsoniter.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil { if err = json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
log.Error("Unmarshal[%d]: %v", t.ID, err) log.Error("Unmarshal[%d]: %v", t.ID, err)
} }
@@ -485,14 +484,14 @@ func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
} }
t.ResponseInfo = &HookResponse{} t.ResponseInfo = &HookResponse{}
if err = jsoniter.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil { if err = json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
log.Error("Unmarshal [%d]: %v", t.ID, err) log.Error("Unmarshal [%d]: %v", t.ID, err)
} }
} }
} }
func (t *HookTask) ToJSON(v any) string { func (t *HookTask) ToJSON(v any) string {
p, err := jsoniter.Marshal(v) p, err := json.Marshal(v)
if err != nil { if err != nil {
log.Error("Marshal [%d]: %v", t.ID, err) log.Error("Marshal [%d]: %v", t.ID, err)
} }
@@ -559,12 +558,12 @@ func UpdateHookTask(t *HookTask) error {
} }
// prepareHookTasks adds list of webhooks to task queue. // prepareHookTasks adds list of webhooks to task queue.
func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) { func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p apiv1types.WebhookPayloader, webhooks []*Webhook) (err error) {
if len(webhooks) == 0 { if len(webhooks) == 0 {
return nil return nil
} }
var payloader api.Payloader var payloader apiv1types.WebhookPayloader
for _, w := range webhooks { for _, w := range webhooks {
switch event { switch event {
case HookEventTypeCreate: case HookEventTypeCreate:
@@ -634,15 +633,15 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay
} }
if err = createHookTask(e, &HookTask{ if err = createHookTask(e, &HookTask{
RepoID: repo.ID, RepoID: repo.ID,
HookID: w.ID, HookID: w.ID,
Type: w.HookTaskType, Type: w.HookTaskType,
URL: w.URL, URL: w.URL,
Signature: signature, Signature: signature,
Payloader: payloader, WebhookPayloader: payloader,
ContentType: w.ContentType, ContentType: w.ContentType,
EventType: event, EventType: event,
IsSSL: w.IsSSL, IsSSL: w.IsSSL,
}); err != nil { }); err != nil {
return errors.Newf("createHookTask: %v", err) return errors.Newf("createHookTask: %v", err)
} }
@@ -655,7 +654,7 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay
return nil return nil
} }
func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payloader) error { func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p apiv1types.WebhookPayloader) error {
webhooks, err := getActiveWebhooksByRepoID(e, repo.ID) webhooks, err := getActiveWebhooksByRepoID(e, repo.ID)
if err != nil { if err != nil {
return errors.Newf("getActiveWebhooksByRepoID [%d]: %v", repo.ID, err) return errors.Newf("getActiveWebhooksByRepoID [%d]: %v", repo.ID, err)
@@ -674,7 +673,7 @@ func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payl
} }
// PrepareWebhooks adds all active webhooks to task queue. // PrepareWebhooks adds all active webhooks to task queue.
func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error { func PrepareWebhooks(repo *Repository, event HookEventType, p apiv1types.WebhookPayloader) error {
// NOTE: To prevent too many cascading changes in a single refactoring PR, we // NOTE: To prevent too many cascading changes in a single refactoring PR, we
// choose to ignore this function in tests. // choose to ignore this function in tests.
if x == nil && testutil.InTest { if x == nil && testutil.InTest {
@@ -684,7 +683,7 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
} }
// TestWebhook adds the test webhook matches the ID to task queue. // TestWebhook adds the test webhook matches the ID to task queue.
func TestWebhook(repo *Repository, event HookEventType, p api.Payloader, webhookID int64) error { func TestWebhook(repo *Repository, event HookEventType, p apiv1types.WebhookPayloader, webhookID int64) error {
webhook, err := GetWebhookOfRepoByID(repo.ID, webhookID) webhook, err := GetWebhookOfRepoByID(repo.ID, webhookID)
if err != nil { if err != nil {
return errors.Newf("GetWebhookOfRepoByID [repo_id: %d, id: %d]: %v", repo.ID, webhookID, err) return errors.Newf("GetWebhookOfRepoByID [repo_id: %d, id: %d]: %v", repo.ID, webhookID, err)

View File

@@ -1,14 +1,14 @@
package database package database
import ( import (
"encoding/json"
"fmt" "fmt"
"strings" "strings"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
jsoniter "github.com/json-iterator/go"
"github.com/gogs/git-module" "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
) )
const ( const (
@@ -39,7 +39,7 @@ type DingtalkPayload struct {
} }
func (p *DingtalkPayload) JSONPayload() ([]byte, error) { func (p *DingtalkPayload) JSONPayload() ([]byte, error) {
data, err := jsoniter.MarshalIndent(p, "", " ") data, err := json.MarshalIndent(p, "", " ")
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }
@@ -55,31 +55,31 @@ func NewDingtalkActionCard(singleTitle, singleURL string) DingtalkActionCard {
} }
// TODO: add content // TODO: add content
func GetDingtalkPayload(p api.Payloader, event HookEventType) (payload *DingtalkPayload, err error) { func GetDingtalkPayload(p apiv1types.WebhookPayloader, event HookEventType) (payload *DingtalkPayload, err error) {
switch event { switch event {
case HookEventTypeCreate: case HookEventTypeCreate:
payload = getDingtalkCreatePayload(p.(*api.CreatePayload)) payload = getDingtalkCreatePayload(p.(*apiv1types.WebhookCreatePayload))
case HookEventTypeDelete: case HookEventTypeDelete:
payload = getDingtalkDeletePayload(p.(*api.DeletePayload)) payload = getDingtalkDeletePayload(p.(*apiv1types.WebhookDeletePayload))
case HookEventTypeFork: case HookEventTypeFork:
payload = getDingtalkForkPayload(p.(*api.ForkPayload)) payload = getDingtalkForkPayload(p.(*apiv1types.WebhookForkPayload))
case HookEventTypePush: case HookEventTypePush:
payload = getDingtalkPushPayload(p.(*api.PushPayload)) payload = getDingtalkPushPayload(p.(*apiv1types.WebhookPushPayload))
case HookEventTypeIssues: case HookEventTypeIssues:
payload = getDingtalkIssuesPayload(p.(*api.IssuesPayload)) payload = getDingtalkIssuesPayload(p.(*apiv1types.WebhookIssuesPayload))
case HookEventTypeIssueComment: case HookEventTypeIssueComment:
payload = getDingtalkIssueCommentPayload(p.(*api.IssueCommentPayload)) payload = getDingtalkIssueCommentPayload(p.(*apiv1types.WebhookIssueCommentPayload))
case HookEventTypePullRequest: case HookEventTypePullRequest:
payload = getDingtalkPullRequestPayload(p.(*api.PullRequestPayload)) payload = getDingtalkPullRequestPayload(p.(*apiv1types.WebhookPullRequestPayload))
case HookEventTypeRelease: case HookEventTypeRelease:
payload = getDingtalkReleasePayload(p.(*api.ReleasePayload)) payload = getDingtalkReleasePayload(p.(*apiv1types.WebhookReleasePayload))
default: default:
return nil, errors.Errorf("unexpected event %q", event) return nil, errors.Errorf("unexpected event %q", event)
} }
return payload, nil return payload, nil
} }
func getDingtalkCreatePayload(p *api.CreatePayload) *DingtalkPayload { func getDingtalkCreatePayload(p *apiv1types.WebhookCreatePayload) *DingtalkPayload {
refName := git.RefShortName(p.Ref) refName := git.RefShortName(p.Ref)
refType := strings.Title(p.RefType) refType := strings.Title(p.RefType)
@@ -94,7 +94,7 @@ func getDingtalkCreatePayload(p *api.CreatePayload) *DingtalkPayload {
} }
} }
func getDingtalkDeletePayload(p *api.DeletePayload) *DingtalkPayload { func getDingtalkDeletePayload(p *apiv1types.WebhookDeletePayload) *DingtalkPayload {
refName := git.RefShortName(p.Ref) refName := git.RefShortName(p.Ref)
refType := strings.Title(p.RefType) refType := strings.Title(p.RefType)
@@ -109,7 +109,7 @@ func getDingtalkDeletePayload(p *api.DeletePayload) *DingtalkPayload {
} }
} }
func getDingtalkForkPayload(p *api.ForkPayload) *DingtalkPayload { func getDingtalkForkPayload(p *apiv1types.WebhookForkPayload) *DingtalkPayload {
actionCard := NewDingtalkActionCard("View Fork", p.Forkee.HTMLURL) actionCard := NewDingtalkActionCard("View Fork", p.Forkee.HTMLURL)
actionCard.Text += "# Repo Fork Event" actionCard.Text += "# Repo Fork Event"
actionCard.Text += "\n- From Repo: **" + MarkdownLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) + "**" actionCard.Text += "\n- From Repo: **" + MarkdownLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) + "**"
@@ -121,7 +121,7 @@ func getDingtalkForkPayload(p *api.ForkPayload) *DingtalkPayload {
} }
} }
func getDingtalkPushPayload(p *api.PushPayload) *DingtalkPayload { func getDingtalkPushPayload(p *apiv1types.WebhookPushPayload) *DingtalkPayload {
refName := git.RefShortName(p.Ref) refName := git.RefShortName(p.Ref)
pusher := p.Pusher.FullName pusher := p.Pusher.FullName
@@ -150,7 +150,7 @@ func getDingtalkPushPayload(p *api.PushPayload) *DingtalkPayload {
} }
} }
func getDingtalkIssuesPayload(p *api.IssuesPayload) *DingtalkPayload { func getDingtalkIssuesPayload(p *apiv1types.WebhookIssuesPayload) *DingtalkPayload {
issueName := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title) issueName := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)
issueURL := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index) issueURL := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index)
@@ -159,11 +159,11 @@ func getDingtalkIssuesPayload(p *api.IssuesPayload) *DingtalkPayload {
actionCard.Text += "\n- Issue: **" + MarkdownLinkFormatter(issueURL, issueName) + "**" actionCard.Text += "\n- Issue: **" + MarkdownLinkFormatter(issueURL, issueName) + "**"
switch p.Action { switch p.Action {
case api.HOOK_ISSUE_ASSIGNED: case apiv1types.WebhookIssueAssigned:
actionCard.Text += "\n- New Assignee: **" + p.Issue.Assignee.UserName + "**" actionCard.Text += "\n- New Assignee: **" + p.Issue.Assignee.UserName + "**"
case api.HOOK_ISSUE_MILESTONED: case apiv1types.WebhookIssueMilestoned:
actionCard.Text += "\n- New Milestone: **" + p.Issue.Milestone.Title + "**" actionCard.Text += "\n- New Milestone: **" + p.Issue.Milestone.Title + "**"
case api.HOOK_ISSUE_LABEL_UPDATED: case apiv1types.WebhookIssueLabelUpdated:
if len(p.Issue.Labels) > 0 { if len(p.Issue.Labels) > 0 {
labels := make([]string, len(p.Issue.Labels)) labels := make([]string, len(p.Issue.Labels))
for i, label := range p.Issue.Labels { for i, label := range p.Issue.Labels {
@@ -185,10 +185,10 @@ func getDingtalkIssuesPayload(p *api.IssuesPayload) *DingtalkPayload {
} }
} }
func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) *DingtalkPayload { func getDingtalkIssueCommentPayload(p *apiv1types.WebhookIssueCommentPayload) *DingtalkPayload {
issueName := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title) issueName := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
commentURL := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index) commentURL := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
if p.Action != api.HOOK_ISSUE_COMMENT_DELETED { if p.Action != apiv1types.WebhookIssueCommentDeleted {
commentURL += "#" + CommentHashTag(p.Comment.ID) commentURL += "#" + CommentHashTag(p.Comment.ID)
} }
@@ -206,9 +206,9 @@ func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) *DingtalkPayload
} }
} }
func getDingtalkPullRequestPayload(p *api.PullRequestPayload) *DingtalkPayload { func getDingtalkPullRequestPayload(p *apiv1types.WebhookPullRequestPayload) *DingtalkPayload {
title := "# Pull Request " + strings.Title(string(p.Action)) title := "# Pull Request " + strings.Title(string(p.Action))
if p.Action == api.HOOK_ISSUE_CLOSED && p.PullRequest.HasMerged { if p.Action == apiv1types.WebhookIssueClosed && p.PullRequest.HasMerged {
title = "# Pull Request Merged" title = "# Pull Request Merged"
} }
@@ -216,11 +216,11 @@ func getDingtalkPullRequestPayload(p *api.PullRequestPayload) *DingtalkPayload {
content := "- PR: " + MarkdownLinkFormatter(pullRequestURL, fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)) content := "- PR: " + MarkdownLinkFormatter(pullRequestURL, fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
switch p.Action { switch p.Action {
case api.HOOK_ISSUE_ASSIGNED: case apiv1types.WebhookIssueAssigned:
content += "\n- New Assignee: **" + p.PullRequest.Assignee.UserName + "**" content += "\n- New Assignee: **" + p.PullRequest.Assignee.UserName + "**"
case api.HOOK_ISSUE_MILESTONED: case apiv1types.WebhookIssueMilestoned:
content += "\n- New Milestone: *" + p.PullRequest.Milestone.Title + "*" content += "\n- New Milestone: *" + p.PullRequest.Milestone.Title + "*"
case api.HOOK_ISSUE_LABEL_UPDATED: case apiv1types.WebhookIssueLabelUpdated:
labels := make([]string, len(p.PullRequest.Labels)) labels := make([]string, len(p.PullRequest.Labels))
for i, label := range p.PullRequest.Labels { for i, label := range p.PullRequest.Labels {
labels[i] = "**" + label.Name + "**" labels[i] = "**" + label.Name + "**"
@@ -231,7 +231,7 @@ func getDingtalkPullRequestPayload(p *api.PullRequestPayload) *DingtalkPayload {
actionCard := NewDingtalkActionCard("View Pull Request", pullRequestURL) actionCard := NewDingtalkActionCard("View Pull Request", pullRequestURL)
actionCard.Text += title + "\n" + content actionCard.Text += title + "\n" + content
if p.Action == api.HOOK_ISSUE_OPENED || p.Action == api.HOOK_ISSUE_EDITED { if p.Action == apiv1types.WebhookIssueOpened || p.Action == apiv1types.WebhookIssueEdited {
actionCard.Text += "\n> " + p.PullRequest.Body actionCard.Text += "\n> " + p.PullRequest.Body
} }
@@ -241,7 +241,7 @@ func getDingtalkPullRequestPayload(p *api.PullRequestPayload) *DingtalkPayload {
} }
} }
func getDingtalkReleasePayload(p *api.ReleasePayload) *DingtalkPayload { func getDingtalkReleasePayload(p *apiv1types.WebhookReleasePayload) *DingtalkPayload {
releaseURL := p.Repository.HTMLURL + "/src/" + p.Release.TagName releaseURL := p.Repository.HTMLURL + "/src/" + p.Release.TagName
author := p.Release.Author.FullName author := p.Release.Author.FullName

View File

@@ -1,17 +1,16 @@
package database package database
import ( import (
"encoding/json"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
jsoniter "github.com/json-iterator/go"
"github.com/gogs/git-module" "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
) )
type DiscordEmbedFooterObject struct { type DiscordEmbedFooterObject struct {
@@ -47,7 +46,7 @@ type DiscordPayload struct {
} }
func (p *DiscordPayload) JSONPayload() ([]byte, error) { func (p *DiscordPayload) JSONPayload() ([]byte, error) {
data, err := jsoniter.MarshalIndent(p, "", " ") data, err := json.MarshalIndent(p, "", " ")
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }
@@ -67,7 +66,7 @@ func DiscordSHALinkFormatter(url, text string) string {
} }
// getDiscordCreatePayload composes Discord payload for create new branch or tag. // getDiscordCreatePayload composes Discord payload for create new branch or tag.
func getDiscordCreatePayload(p *api.CreatePayload) *DiscordPayload { func getDiscordCreatePayload(p *apiv1types.WebhookCreatePayload) *DiscordPayload {
refName := git.RefShortName(p.Ref) refName := git.RefShortName(p.Ref)
repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName) refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
@@ -78,14 +77,14 @@ func getDiscordCreatePayload(p *api.CreatePayload) *DiscordPayload {
URL: conf.Server.ExternalURL + p.Sender.UserName, URL: conf.Server.ExternalURL + p.Sender.UserName,
Author: &DiscordEmbedAuthorObject{ Author: &DiscordEmbedAuthorObject{
Name: p.Sender.UserName, Name: p.Sender.UserName,
IconURL: p.Sender.AvatarUrl, IconURL: p.Sender.AvatarURL,
}, },
}}, }},
} }
} }
// getDiscordDeletePayload composes Discord payload for delete a branch or tag. // getDiscordDeletePayload composes Discord payload for delete a branch or tag.
func getDiscordDeletePayload(p *api.DeletePayload) *DiscordPayload { func getDiscordDeletePayload(p *apiv1types.WebhookDeletePayload) *DiscordPayload {
refName := git.RefShortName(p.Ref) refName := git.RefShortName(p.Ref)
repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
content := fmt.Sprintf("Deleted %s: %s/%s", p.RefType, repoLink, refName) content := fmt.Sprintf("Deleted %s: %s/%s", p.RefType, repoLink, refName)
@@ -95,14 +94,14 @@ func getDiscordDeletePayload(p *api.DeletePayload) *DiscordPayload {
URL: conf.Server.ExternalURL + p.Sender.UserName, URL: conf.Server.ExternalURL + p.Sender.UserName,
Author: &DiscordEmbedAuthorObject{ Author: &DiscordEmbedAuthorObject{
Name: p.Sender.UserName, Name: p.Sender.UserName,
IconURL: p.Sender.AvatarUrl, IconURL: p.Sender.AvatarURL,
}, },
}}, }},
} }
} }
// getDiscordForkPayload composes Discord payload for forked by a repository. // getDiscordForkPayload composes Discord payload for forked by a repository.
func getDiscordForkPayload(p *api.ForkPayload) *DiscordPayload { func getDiscordForkPayload(p *apiv1types.WebhookForkPayload) *DiscordPayload {
baseLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) baseLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
forkLink := DiscordLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName) forkLink := DiscordLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName)
content := fmt.Sprintf("%s is forked to %s", baseLink, forkLink) content := fmt.Sprintf("%s is forked to %s", baseLink, forkLink)
@@ -112,13 +111,13 @@ func getDiscordForkPayload(p *api.ForkPayload) *DiscordPayload {
URL: conf.Server.ExternalURL + p.Sender.UserName, URL: conf.Server.ExternalURL + p.Sender.UserName,
Author: &DiscordEmbedAuthorObject{ Author: &DiscordEmbedAuthorObject{
Name: p.Sender.UserName, Name: p.Sender.UserName,
IconURL: p.Sender.AvatarUrl, IconURL: p.Sender.AvatarURL,
}, },
}}, }},
} }
} }
func getDiscordPushPayload(p *api.PushPayload, slack *SlackMeta) *DiscordPayload { func getDiscordPushPayload(p *apiv1types.WebhookPushPayload, slack *SlackMeta) *DiscordPayload {
// n new commits // n new commits
var ( var (
branchName = git.RefShortName(p.Ref) branchName = git.RefShortName(p.Ref)
@@ -162,37 +161,37 @@ func getDiscordPushPayload(p *api.PushPayload, slack *SlackMeta) *DiscordPayload
Color: int(color), Color: int(color),
Author: &DiscordEmbedAuthorObject{ Author: &DiscordEmbedAuthorObject{
Name: p.Sender.UserName, Name: p.Sender.UserName,
IconURL: p.Sender.AvatarUrl, IconURL: p.Sender.AvatarURL,
}, },
}}, }},
} }
} }
func getDiscordIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) *DiscordPayload { func getDiscordIssuesPayload(p *apiv1types.WebhookIssuesPayload, slack *SlackMeta) *DiscordPayload {
title := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title) title := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)
url := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index) url := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index)
content := "" content := ""
fields := make([]*DiscordEmbedFieldObject, 0, 1) fields := make([]*DiscordEmbedFieldObject, 0, 1)
switch p.Action { switch p.Action {
case api.HOOK_ISSUE_OPENED: case apiv1types.WebhookIssueOpened:
title = "New issue: " + title title = "New issue: " + title
content = p.Issue.Body content = p.Issue.Body
case api.HOOK_ISSUE_CLOSED: case apiv1types.WebhookIssueClosed:
title = "Issue closed: " + title title = "Issue closed: " + title
case api.HOOK_ISSUE_REOPENED: case apiv1types.WebhookIssueReopened:
title = "Issue re-opened: " + title title = "Issue re-opened: " + title
case api.HOOK_ISSUE_EDITED: case apiv1types.WebhookIssueEdited:
title = "Issue edited: " + title title = "Issue edited: " + title
content = p.Issue.Body content = p.Issue.Body
case api.HOOK_ISSUE_ASSIGNED: case apiv1types.WebhookIssueAssigned:
title = "Issue assigned: " + title title = "Issue assigned: " + title
fields = []*DiscordEmbedFieldObject{{ fields = []*DiscordEmbedFieldObject{{
Name: "New Assignee", Name: "New Assignee",
Value: p.Issue.Assignee.UserName, Value: p.Issue.Assignee.UserName,
}} }}
case api.HOOK_ISSUE_UNASSIGNED: case apiv1types.WebhookIssueUnassigned:
title = "Issue unassigned: " + title title = "Issue unassigned: " + title
case api.HOOK_ISSUE_LABEL_UPDATED: case apiv1types.WebhookIssueLabelUpdated:
title = "Issue labels updated: " + title title = "Issue labels updated: " + title
labels := make([]string, len(p.Issue.Labels)) labels := make([]string, len(p.Issue.Labels))
for i := range p.Issue.Labels { for i := range p.Issue.Labels {
@@ -205,17 +204,17 @@ func getDiscordIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) *DiscordPay
Name: "Labels", Name: "Labels",
Value: strings.Join(labels, ", "), Value: strings.Join(labels, ", "),
}} }}
case api.HOOK_ISSUE_LABEL_CLEARED: case apiv1types.WebhookIssueLabelCleared:
title = "Issue labels cleared: " + title title = "Issue labels cleared: " + title
case api.HOOK_ISSUE_SYNCHRONIZED: case apiv1types.WebhookIssueSynchronized:
title = "Issue synchronized: " + title title = "Issue synchronized: " + title
case api.HOOK_ISSUE_MILESTONED: case apiv1types.WebhookIssueMilestoned:
title = "Issue milestoned: " + title title = "Issue milestoned: " + title
fields = []*DiscordEmbedFieldObject{{ fields = []*DiscordEmbedFieldObject{{
Name: "New Milestone", Name: "New Milestone",
Value: p.Issue.Milestone.Title, Value: p.Issue.Milestone.Title,
}} }}
case api.HOOK_ISSUE_DEMILESTONED: case apiv1types.WebhookIssueDemilestoned:
title = "Issue demilestoned: " + title title = "Issue demilestoned: " + title
} }
@@ -233,26 +232,26 @@ func getDiscordIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) *DiscordPay
}, },
Author: &DiscordEmbedAuthorObject{ Author: &DiscordEmbedAuthorObject{
Name: p.Sender.UserName, Name: p.Sender.UserName,
IconURL: p.Sender.AvatarUrl, IconURL: p.Sender.AvatarURL,
}, },
Fields: fields, Fields: fields,
}}, }},
} }
} }
func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) *DiscordPayload { func getDiscordIssueCommentPayload(p *apiv1types.WebhookIssueCommentPayload, slack *SlackMeta) *DiscordPayload {
title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title) title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)) url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
content := "" content := ""
fields := make([]*DiscordEmbedFieldObject, 0, 1) fields := make([]*DiscordEmbedFieldObject, 0, 1)
switch p.Action { switch p.Action {
case api.HOOK_ISSUE_COMMENT_CREATED: case apiv1types.WebhookIssueCommentCreated:
title = "New comment: " + title title = "New comment: " + title
content = p.Comment.Body content = p.Comment.Body
case api.HOOK_ISSUE_COMMENT_EDITED: case apiv1types.WebhookIssueCommentEdited:
title = "Comment edited: " + title title = "Comment edited: " + title
content = p.Comment.Body content = p.Comment.Body
case api.HOOK_ISSUE_COMMENT_DELETED: case apiv1types.WebhookIssueCommentDeleted:
title = "Comment deleted: " + title title = "Comment deleted: " + title
url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index) url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
content = p.Comment.Body content = p.Comment.Body
@@ -272,42 +271,42 @@ func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta)
}, },
Author: &DiscordEmbedAuthorObject{ Author: &DiscordEmbedAuthorObject{
Name: p.Sender.UserName, Name: p.Sender.UserName,
IconURL: p.Sender.AvatarUrl, IconURL: p.Sender.AvatarURL,
}, },
Fields: fields, Fields: fields,
}}, }},
} }
} }
func getDiscordPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) *DiscordPayload { func getDiscordPullRequestPayload(p *apiv1types.WebhookPullRequestPayload, slack *SlackMeta) *DiscordPayload {
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title) title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
url := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index) url := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
content := "" content := ""
fields := make([]*DiscordEmbedFieldObject, 0, 1) fields := make([]*DiscordEmbedFieldObject, 0, 1)
switch p.Action { switch p.Action {
case api.HOOK_ISSUE_OPENED: case apiv1types.WebhookIssueOpened:
title = "New pull request: " + title title = "New pull request: " + title
content = p.PullRequest.Body content = p.PullRequest.Body
case api.HOOK_ISSUE_CLOSED: case apiv1types.WebhookIssueClosed:
if p.PullRequest.HasMerged { if p.PullRequest.HasMerged {
title = "Pull request merged: " + title title = "Pull request merged: " + title
} else { } else {
title = "Pull request closed: " + title title = "Pull request closed: " + title
} }
case api.HOOK_ISSUE_REOPENED: case apiv1types.WebhookIssueReopened:
title = "Pull request re-opened: " + title title = "Pull request re-opened: " + title
case api.HOOK_ISSUE_EDITED: case apiv1types.WebhookIssueEdited:
title = "Pull request edited: " + title title = "Pull request edited: " + title
content = p.PullRequest.Body content = p.PullRequest.Body
case api.HOOK_ISSUE_ASSIGNED: case apiv1types.WebhookIssueAssigned:
title = "Pull request assigned: " + title title = "Pull request assigned: " + title
fields = []*DiscordEmbedFieldObject{{ fields = []*DiscordEmbedFieldObject{{
Name: "New Assignee", Name: "New Assignee",
Value: p.PullRequest.Assignee.UserName, Value: p.PullRequest.Assignee.UserName,
}} }}
case api.HOOK_ISSUE_UNASSIGNED: case apiv1types.WebhookIssueUnassigned:
title = "Pull request unassigned: " + title title = "Pull request unassigned: " + title
case api.HOOK_ISSUE_LABEL_UPDATED: case apiv1types.WebhookIssueLabelUpdated:
title = "Pull request labels updated: " + title title = "Pull request labels updated: " + title
labels := make([]string, len(p.PullRequest.Labels)) labels := make([]string, len(p.PullRequest.Labels))
for i := range p.PullRequest.Labels { for i := range p.PullRequest.Labels {
@@ -317,17 +316,17 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) *
Name: "Labels", Name: "Labels",
Value: strings.Join(labels, ", "), Value: strings.Join(labels, ", "),
}} }}
case api.HOOK_ISSUE_LABEL_CLEARED: case apiv1types.WebhookIssueLabelCleared:
title = "Pull request labels cleared: " + title title = "Pull request labels cleared: " + title
case api.HOOK_ISSUE_SYNCHRONIZED: case apiv1types.WebhookIssueSynchronized:
title = "Pull request synchronized: " + title title = "Pull request synchronized: " + title
case api.HOOK_ISSUE_MILESTONED: case apiv1types.WebhookIssueMilestoned:
title = "Pull request milestoned: " + title title = "Pull request milestoned: " + title
fields = []*DiscordEmbedFieldObject{{ fields = []*DiscordEmbedFieldObject{{
Name: "New Milestone", Name: "New Milestone",
Value: p.PullRequest.Milestone.Title, Value: p.PullRequest.Milestone.Title,
}} }}
case api.HOOK_ISSUE_DEMILESTONED: case apiv1types.WebhookIssueDemilestoned:
title = "Pull request demilestoned: " + title title = "Pull request demilestoned: " + title
} }
@@ -345,14 +344,14 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) *
}, },
Author: &DiscordEmbedAuthorObject{ Author: &DiscordEmbedAuthorObject{
Name: p.Sender.UserName, Name: p.Sender.UserName,
IconURL: p.Sender.AvatarUrl, IconURL: p.Sender.AvatarURL,
}, },
Fields: fields, Fields: fields,
}}, }},
} }
} }
func getDiscordReleasePayload(p *api.ReleasePayload) *DiscordPayload { func getDiscordReleasePayload(p *apiv1types.WebhookReleasePayload) *DiscordPayload {
repoLink := DiscordLinkFormatter(p.Repository.HTMLURL, p.Repository.Name) repoLink := DiscordLinkFormatter(p.Repository.HTMLURL, p.Repository.Name)
refLink := DiscordLinkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName) refLink := DiscordLinkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName)
content := fmt.Sprintf("Published new release %s of %s", refLink, repoLink) content := fmt.Sprintf("Published new release %s of %s", refLink, repoLink)
@@ -362,35 +361,35 @@ func getDiscordReleasePayload(p *api.ReleasePayload) *DiscordPayload {
URL: conf.Server.ExternalURL + p.Sender.UserName, URL: conf.Server.ExternalURL + p.Sender.UserName,
Author: &DiscordEmbedAuthorObject{ Author: &DiscordEmbedAuthorObject{
Name: p.Sender.UserName, Name: p.Sender.UserName,
IconURL: p.Sender.AvatarUrl, IconURL: p.Sender.AvatarURL,
}, },
}}, }},
} }
} }
func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (payload *DiscordPayload, err error) { func GetDiscordPayload(p apiv1types.WebhookPayloader, event HookEventType, meta string) (payload *DiscordPayload, err error) {
slack := &SlackMeta{} slack := &SlackMeta{}
if err := jsoniter.Unmarshal([]byte(meta), &slack); err != nil { if err := json.Unmarshal([]byte(meta), slack); err != nil {
return nil, errors.Newf("jsoniter.Unmarshal: %v", err) return nil, errors.Newf("unmarshal: %v", err)
} }
switch event { switch event {
case HookEventTypeCreate: case HookEventTypeCreate:
payload = getDiscordCreatePayload(p.(*api.CreatePayload)) payload = getDiscordCreatePayload(p.(*apiv1types.WebhookCreatePayload))
case HookEventTypeDelete: case HookEventTypeDelete:
payload = getDiscordDeletePayload(p.(*api.DeletePayload)) payload = getDiscordDeletePayload(p.(*apiv1types.WebhookDeletePayload))
case HookEventTypeFork: case HookEventTypeFork:
payload = getDiscordForkPayload(p.(*api.ForkPayload)) payload = getDiscordForkPayload(p.(*apiv1types.WebhookForkPayload))
case HookEventTypePush: case HookEventTypePush:
payload = getDiscordPushPayload(p.(*api.PushPayload), slack) payload = getDiscordPushPayload(p.(*apiv1types.WebhookPushPayload), slack)
case HookEventTypeIssues: case HookEventTypeIssues:
payload = getDiscordIssuesPayload(p.(*api.IssuesPayload), slack) payload = getDiscordIssuesPayload(p.(*apiv1types.WebhookIssuesPayload), slack)
case HookEventTypeIssueComment: case HookEventTypeIssueComment:
payload = getDiscordIssueCommentPayload(p.(*api.IssueCommentPayload), slack) payload = getDiscordIssueCommentPayload(p.(*apiv1types.WebhookIssueCommentPayload), slack)
case HookEventTypePullRequest: case HookEventTypePullRequest:
payload = getDiscordPullRequestPayload(p.(*api.PullRequestPayload), slack) payload = getDiscordPullRequestPayload(p.(*apiv1types.WebhookPullRequestPayload), slack)
case HookEventTypeRelease: case HookEventTypeRelease:
payload = getDiscordReleasePayload(p.(*api.ReleasePayload)) payload = getDiscordReleasePayload(p.(*apiv1types.WebhookReleasePayload))
default: default:
return nil, errors.Errorf("unexpected event %q", event) return nil, errors.Errorf("unexpected event %q", event)
} }

View File

@@ -1,16 +1,15 @@
package database package database
import ( import (
"encoding/json"
"fmt" "fmt"
"strings" "strings"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
jsoniter "github.com/json-iterator/go"
"github.com/gogs/git-module" "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
) )
type SlackMeta struct { type SlackMeta struct {
@@ -38,7 +37,7 @@ type SlackPayload struct {
} }
func (p *SlackPayload) JSONPayload() ([]byte, error) { func (p *SlackPayload) JSONPayload() ([]byte, error) {
data, err := jsoniter.MarshalIndent(p, "", " ") data, err := json.MarshalIndent(p, "", " ")
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }
@@ -68,7 +67,7 @@ func SlackLinkFormatter(url, text string) string {
} }
// getSlackCreatePayload composes Slack payload for create new branch or tag. // getSlackCreatePayload composes Slack payload for create new branch or tag.
func getSlackCreatePayload(p *api.CreatePayload) *SlackPayload { func getSlackCreatePayload(p *apiv1types.WebhookCreatePayload) *SlackPayload {
refName := git.RefShortName(p.Ref) refName := git.RefShortName(p.Ref)
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName) refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
@@ -79,7 +78,7 @@ func getSlackCreatePayload(p *api.CreatePayload) *SlackPayload {
} }
// getSlackDeletePayload composes Slack payload for delete a branch or tag. // getSlackDeletePayload composes Slack payload for delete a branch or tag.
func getSlackDeletePayload(p *api.DeletePayload) *SlackPayload { func getSlackDeletePayload(p *apiv1types.WebhookDeletePayload) *SlackPayload {
refName := git.RefShortName(p.Ref) refName := git.RefShortName(p.Ref)
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName) text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName)
@@ -89,7 +88,7 @@ func getSlackDeletePayload(p *api.DeletePayload) *SlackPayload {
} }
// getSlackForkPayload composes Slack payload for forked by a repository. // getSlackForkPayload composes Slack payload for forked by a repository.
func getSlackForkPayload(p *api.ForkPayload) *SlackPayload { func getSlackForkPayload(p *apiv1types.WebhookForkPayload) *SlackPayload {
baseLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) baseLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
forkLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName) forkLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName)
text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink) text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink)
@@ -98,7 +97,7 @@ func getSlackForkPayload(p *api.ForkPayload) *SlackPayload {
} }
} }
func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) *SlackPayload { func getSlackPushPayload(p *apiv1types.WebhookPushPayload, slack *SlackMeta) *SlackPayload {
// n new commits // n new commits
var ( var (
branchName = git.RefShortName(p.Ref) branchName = git.RefShortName(p.Ref)
@@ -143,36 +142,36 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) *SlackPayload {
} }
} }
func getSlackIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) *SlackPayload { func getSlackIssuesPayload(p *apiv1types.WebhookIssuesPayload, slack *SlackMeta) *SlackPayload {
senderLink := SlackLinkFormatter(conf.Server.ExternalURL+p.Sender.UserName, p.Sender.UserName) senderLink := SlackLinkFormatter(conf.Server.ExternalURL+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index), titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index),
fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)) fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
var text, title, attachmentText string var text, title, attachmentText string
switch p.Action { switch p.Action {
case api.HOOK_ISSUE_OPENED: case apiv1types.WebhookIssueOpened:
text = fmt.Sprintf("[%s] New issue created by %s", p.Repository.FullName, senderLink) text = fmt.Sprintf("[%s] New issue created by %s", p.Repository.FullName, senderLink)
title = titleLink title = titleLink
attachmentText = SlackTextFormatter(p.Issue.Body) attachmentText = SlackTextFormatter(p.Issue.Body)
case api.HOOK_ISSUE_CLOSED: case apiv1types.WebhookIssueClosed:
text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_REOPENED: case apiv1types.WebhookIssueReopened:
text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_EDITED: case apiv1types.WebhookIssueEdited:
text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
attachmentText = SlackTextFormatter(p.Issue.Body) attachmentText = SlackTextFormatter(p.Issue.Body)
case api.HOOK_ISSUE_ASSIGNED: case apiv1types.WebhookIssueAssigned:
text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", p.Repository.FullName, text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", p.Repository.FullName,
SlackLinkFormatter(conf.Server.ExternalURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName), SlackLinkFormatter(conf.Server.ExternalURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
titleLink, senderLink) titleLink, senderLink)
case api.HOOK_ISSUE_UNASSIGNED: case apiv1types.WebhookIssueUnassigned:
text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_LABEL_UPDATED: case apiv1types.WebhookIssueLabelUpdated:
text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_LABEL_CLEARED: case apiv1types.WebhookIssueLabelCleared:
text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_MILESTONED: case apiv1types.WebhookIssueMilestoned:
text = fmt.Sprintf("[%s] Issue milestoned: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Issue milestoned: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_DEMILESTONED: case apiv1types.WebhookIssueDemilestoned:
text = fmt.Sprintf("[%s] Issue demilestoned: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Issue demilestoned: %s by %s", p.Repository.FullName, titleLink, senderLink)
} }
@@ -189,21 +188,21 @@ func getSlackIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) *SlackPayload
} }
} }
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) *SlackPayload { func getSlackIssueCommentPayload(p *apiv1types.WebhookIssueCommentPayload, slack *SlackMeta) *SlackPayload {
senderLink := SlackLinkFormatter(conf.Server.ExternalURL+p.Sender.UserName, p.Sender.UserName) senderLink := SlackLinkFormatter(conf.Server.ExternalURL+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)), titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)),
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)) fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
var text, title, attachmentText string var text, title, attachmentText string
switch p.Action { switch p.Action {
case api.HOOK_ISSUE_COMMENT_CREATED: case apiv1types.WebhookIssueCommentCreated:
text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink) text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink)
title = titleLink title = titleLink
attachmentText = SlackTextFormatter(p.Comment.Body) attachmentText = SlackTextFormatter(p.Comment.Body)
case api.HOOK_ISSUE_COMMENT_EDITED: case apiv1types.WebhookIssueCommentEdited:
text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink) text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink)
title = titleLink title = titleLink
attachmentText = SlackTextFormatter(p.Comment.Body) attachmentText = SlackTextFormatter(p.Comment.Body)
case api.HOOK_ISSUE_COMMENT_DELETED: case apiv1types.WebhookIssueCommentDeleted:
text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink) text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink)
title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index), title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index),
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)) fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
@@ -223,42 +222,42 @@ func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) *
} }
} }
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) *SlackPayload { func getSlackPullRequestPayload(p *apiv1types.WebhookPullRequestPayload, slack *SlackMeta) *SlackPayload {
senderLink := SlackLinkFormatter(conf.Server.ExternalURL+p.Sender.UserName, p.Sender.UserName) senderLink := SlackLinkFormatter(conf.Server.ExternalURL+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index), titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)) fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
var text, title, attachmentText string var text, title, attachmentText string
switch p.Action { switch p.Action {
case api.HOOK_ISSUE_OPENED: case apiv1types.WebhookIssueOpened:
text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink) text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink)
title = titleLink title = titleLink
attachmentText = SlackTextFormatter(p.PullRequest.Body) attachmentText = SlackTextFormatter(p.PullRequest.Body)
case api.HOOK_ISSUE_CLOSED: case apiv1types.WebhookIssueClosed:
if p.PullRequest.HasMerged { if p.PullRequest.HasMerged {
text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink)
} else { } else {
text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
} }
case api.HOOK_ISSUE_REOPENED: case apiv1types.WebhookIssueReopened:
text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_EDITED: case apiv1types.WebhookIssueEdited:
text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
attachmentText = SlackTextFormatter(p.PullRequest.Body) attachmentText = SlackTextFormatter(p.PullRequest.Body)
case api.HOOK_ISSUE_ASSIGNED: case apiv1types.WebhookIssueAssigned:
text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName, text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName,
SlackLinkFormatter(conf.Server.ExternalURL+p.PullRequest.Assignee.UserName, p.PullRequest.Assignee.UserName), SlackLinkFormatter(conf.Server.ExternalURL+p.PullRequest.Assignee.UserName, p.PullRequest.Assignee.UserName),
titleLink, senderLink) titleLink, senderLink)
case api.HOOK_ISSUE_UNASSIGNED: case apiv1types.WebhookIssueUnassigned:
text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_LABEL_UPDATED: case apiv1types.WebhookIssueLabelUpdated:
text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_LABEL_CLEARED: case apiv1types.WebhookIssueLabelCleared:
text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_SYNCHRONIZED: case apiv1types.WebhookIssueSynchronized:
text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_MILESTONED: case apiv1types.WebhookIssueMilestoned:
text = fmt.Sprintf("[%s] Pull request milestoned: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request milestoned: %s by %s", p.Repository.FullName, titleLink, senderLink)
case api.HOOK_ISSUE_DEMILESTONED: case apiv1types.WebhookIssueDemilestoned:
text = fmt.Sprintf("[%s] Pull request demilestoned: %s by %s", p.Repository.FullName, titleLink, senderLink) text = fmt.Sprintf("[%s] Pull request demilestoned: %s by %s", p.Repository.FullName, titleLink, senderLink)
} }
@@ -275,7 +274,7 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) *Sl
} }
} }
func getSlackReleasePayload(p *api.ReleasePayload) *SlackPayload { func getSlackReleasePayload(p *apiv1types.WebhookReleasePayload) *SlackPayload {
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.Name) repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.Name)
refLink := SlackLinkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName) refLink := SlackLinkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName)
text := fmt.Sprintf("[%s] new release %s published by %s", repoLink, refLink, p.Sender.UserName) text := fmt.Sprintf("[%s] new release %s published by %s", repoLink, refLink, p.Sender.UserName)
@@ -284,29 +283,29 @@ func getSlackReleasePayload(p *api.ReleasePayload) *SlackPayload {
} }
} }
func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (payload *SlackPayload, err error) { func GetSlackPayload(p apiv1types.WebhookPayloader, event HookEventType, meta string) (payload *SlackPayload, err error) {
slack := &SlackMeta{} slack := &SlackMeta{}
if err := jsoniter.Unmarshal([]byte(meta), &slack); err != nil { if err := json.Unmarshal([]byte(meta), slack); err != nil {
return nil, errors.Newf("unmarshal: %v", err) return nil, errors.Newf("unmarshal: %v", err)
} }
switch event { switch event {
case HookEventTypeCreate: case HookEventTypeCreate:
payload = getSlackCreatePayload(p.(*api.CreatePayload)) payload = getSlackCreatePayload(p.(*apiv1types.WebhookCreatePayload))
case HookEventTypeDelete: case HookEventTypeDelete:
payload = getSlackDeletePayload(p.(*api.DeletePayload)) payload = getSlackDeletePayload(p.(*apiv1types.WebhookDeletePayload))
case HookEventTypeFork: case HookEventTypeFork:
payload = getSlackForkPayload(p.(*api.ForkPayload)) payload = getSlackForkPayload(p.(*apiv1types.WebhookForkPayload))
case HookEventTypePush: case HookEventTypePush:
payload = getSlackPushPayload(p.(*api.PushPayload), slack) payload = getSlackPushPayload(p.(*apiv1types.WebhookPushPayload), slack)
case HookEventTypeIssues: case HookEventTypeIssues:
payload = getSlackIssuesPayload(p.(*api.IssuesPayload), slack) payload = getSlackIssuesPayload(p.(*apiv1types.WebhookIssuesPayload), slack)
case HookEventTypeIssueComment: case HookEventTypeIssueComment:
payload = getSlackIssueCommentPayload(p.(*api.IssueCommentPayload), slack) payload = getSlackIssueCommentPayload(p.(*apiv1types.WebhookIssueCommentPayload), slack)
case HookEventTypePullRequest: case HookEventTypePullRequest:
payload = getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack) payload = getSlackPullRequestPayload(p.(*apiv1types.WebhookPullRequestPayload), slack)
case HookEventTypeRelease: case HookEventTypeRelease:
payload = getSlackReleasePayload(p.(*api.ReleasePayload)) payload = getSlackReleasePayload(p.(*apiv1types.WebhookReleasePayload))
default: default:
return nil, errors.Errorf("unexpected event %q", event) return nil, errors.Errorf("unexpected event %q", event)
} }

View File

@@ -8,7 +8,6 @@ import (
"github.com/glebarez/sqlite" "github.com/glebarez/sqlite"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/driver/sqlserver"
"gorm.io/gorm" "gorm.io/gorm"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
@@ -29,22 +28,6 @@ func ParsePostgreSQLHostPort(info string) (host, port string) {
return host, port return host, port
} }
// ParseMSSQLHostPort parses given input in various forms for MSSQL and returns
// proper host and port number.
func ParseMSSQLHostPort(info string) (host, port string) {
host, port = "127.0.0.1", "1433"
if strings.Contains(info, ":") {
host = strings.Split(info, ":")[0]
port = strings.Split(info, ":")[1]
} else if strings.Contains(info, ",") {
host = strings.Split(info, ",")[0]
port = strings.TrimSpace(strings.Split(info, ",")[1])
} else if len(info) > 0 {
host = info
}
return host, port
}
// NewDSN takes given database options and returns parsed DSN. // NewDSN takes given database options and returns parsed DSN.
func NewDSN(opts conf.DatabaseOpts) (dsn string, err error) { func NewDSN(opts conf.DatabaseOpts) (dsn string, err error) {
// In case the database name contains "?" with some parameters // In case the database name contains "?" with some parameters
@@ -68,11 +51,6 @@ func NewDSN(opts conf.DatabaseOpts) (dsn string, err error) {
dsn = fmt.Sprintf("user='%s' password='%s' host='%s' port='%s' dbname='%s' sslmode='%s' search_path='%s' application_name='gogs'", dsn = fmt.Sprintf("user='%s' password='%s' host='%s' port='%s' dbname='%s' sslmode='%s' search_path='%s' application_name='gogs'",
opts.User, opts.Password, host, port, opts.Name, opts.SSLMode, opts.Schema) opts.User, opts.Password, host, port, opts.Name, opts.SSLMode, opts.Schema)
case "mssql":
host, port := ParseMSSQLHostPort(opts.Host)
dsn = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;",
host, port, opts.Name, opts.User, opts.Password)
case "sqlite3": case "sqlite3":
dsn = "file:" + opts.Path + "?cache=shared&mode=rwc" dsn = "file:" + opts.Path + "?cache=shared&mode=rwc"
@@ -97,8 +75,6 @@ func OpenDB(opts conf.DatabaseOpts, cfg *gorm.Config) (*gorm.DB, error) {
dialector = mysql.Open(dsn) dialector = mysql.Open(dsn)
case "postgres": case "postgres":
dialector = postgres.Open(dsn) dialector = postgres.Open(dsn)
case "mssql":
dialector = sqlserver.Open(dsn)
case "sqlite3": case "sqlite3":
dialector = sqlite.Open(dsn) dialector = sqlite.Open(dsn)
default: default:

View File

@@ -32,25 +32,6 @@ func TestParsePostgreSQLHostPort(t *testing.T) {
} }
} }
func TestParseMSSQLHostPort(t *testing.T) {
tests := []struct {
info string
expHost string
expPort string
}{
{info: "127.0.0.1:1234", expHost: "127.0.0.1", expPort: "1234"},
{info: "127.0.0.1,1234", expHost: "127.0.0.1", expPort: "1234"},
{info: "127.0.0.1", expHost: "127.0.0.1", expPort: "1433"},
}
for _, test := range tests {
t.Run("", func(t *testing.T) {
host, port := ParseMSSQLHostPort(test.info)
assert.Equal(t, test.expHost, host)
assert.Equal(t, test.expPort, port)
})
}
}
func TestNewDSN(t *testing.T) { func TestNewDSN(t *testing.T) {
t.Run("bad dialect", func(t *testing.T) { t.Run("bad dialect", func(t *testing.T) {
_, err := NewDSN(conf.DatabaseOpts{ _, err := NewDSN(conf.DatabaseOpts{
@@ -114,18 +95,6 @@ func TestNewDSN(t *testing.T) {
wantDSN: "user='gogs@local' password='pa$$word' host='127.0.0.1' port='5432' dbname='gogs' sslmode='disable' search_path='test' application_name='gogs'", wantDSN: "user='gogs@local' password='pa$$word' host='127.0.0.1' port='5432' dbname='gogs' sslmode='disable' search_path='test' application_name='gogs'",
}, },
{
name: "mssql",
opts: conf.DatabaseOpts{
Type: "mssql",
Host: "127.0.0.1",
Name: "gogs",
User: "gogs@local",
Password: "pa$$word",
},
wantDSN: "server=127.0.0.1; port=1433; database=gogs; user id=gogs@local; password=pa$$word;",
},
{ {
name: "sqlite3", name: "sqlite3",
opts: conf.DatabaseOpts{ opts: conf.DatabaseOpts{

View File

@@ -3,13 +3,13 @@ package email
import ( import (
"fmt" "fmt"
"html/template" "html/template"
"net/mail"
"path/filepath" "path/filepath"
"sync" "sync"
"time" "time"
"gopkg.in/gomail.v2" "github.com/cockroachdb/errors"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
@@ -72,7 +72,11 @@ func render(tpl string, data map[string]any) (string, error) {
} }
func SendTestMail(email string) error { func SendTestMail(email string) error {
return gomail.Send(&Sender{}, NewMessage([]string{email}, "Gogs Test Email", "Hello 👋, greeting from Gogs!").Message) msg, err := newMessage([]string{email}, "Gogs Test Email", "Hello 👋, greeting from Gogs!")
if err != nil {
return errors.Wrap(err, "new message")
}
return sendMessage(msg)
} }
/* /*
@@ -98,7 +102,7 @@ type Issue interface {
HTMLURL() string HTMLURL() string
} }
func SendUserMail(_ *macaron.Context, u User, tpl, code, subject, info string) { func SendUserMail(_ *macaron.Context, u User, tpl, code, subject, info string) error {
data := map[string]any{ data := map[string]any{
"Username": u.DisplayName(), "Username": u.DisplayName(),
"ActiveCodeLives": conf.Auth.ActivateCodeLives / 60, "ActiveCodeLives": conf.Auth.ActivateCodeLives / 60,
@@ -107,26 +111,28 @@ func SendUserMail(_ *macaron.Context, u User, tpl, code, subject, info string) {
} }
body, err := render(tpl, data) body, err := render(tpl, data)
if err != nil { if err != nil {
log.Error("render: %v", err) return errors.Wrap(err, "render")
return
} }
msg := NewMessage([]string{u.Email()}, subject, body) msg, err := newMessage([]string{u.Email()}, subject, body)
msg.Info = fmt.Sprintf("UID: %d, %s", u.ID(), info) if err != nil {
return errors.Wrap(err, "new message")
}
msg.info = fmt.Sprintf("UID: %d, %s", u.ID(), info)
Send(msg) send(msg)
return nil
} }
func SendActivateAccountMail(c *macaron.Context, u User) { func SendActivateAccountMail(c *macaron.Context, u User) error {
SendUserMail(c, u, tmplAuthActivate, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.activate_account"), "activate account") return SendUserMail(c, u, tmplAuthActivate, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.activate_account"), "activate account")
} }
func SendResetPasswordMail(c *macaron.Context, u User) { func SendResetPasswordMail(c *macaron.Context, u User) error {
SendUserMail(c, u, tmplAuthResetPassword, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.reset_password"), "reset password") return SendUserMail(c, u, tmplAuthResetPassword, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.reset_password"), "reset password")
} }
// SendActivateAccountMail sends confirmation email. func SendActivateEmailMail(c *macaron.Context, u User, email string) error {
func SendActivateEmailMail(c *macaron.Context, u User, email string) {
data := map[string]any{ data := map[string]any{
"Username": u.DisplayName(), "Username": u.DisplayName(),
"ActiveCodeLives": conf.Auth.ActivateCodeLives / 60, "ActiveCodeLives": conf.Auth.ActivateCodeLives / 60,
@@ -135,35 +141,39 @@ func SendActivateEmailMail(c *macaron.Context, u User, email string) {
} }
body, err := render(tmplAuthActivateEmail, data) body, err := render(tmplAuthActivateEmail, data)
if err != nil { if err != nil {
log.Error("HTMLString: %v", err) return errors.Wrap(err, "render")
return
} }
msg := NewMessage([]string{email}, c.Tr("mail.activate_email"), body) msg, err := newMessage([]string{email}, c.Tr("mail.activate_email"), body)
msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID()) if err != nil {
return errors.Wrap(err, "new message")
}
msg.info = fmt.Sprintf("UID: %d, activate email", u.ID())
Send(msg) send(msg)
return nil
} }
// SendRegisterNotifyMail triggers a notify e-mail by admin created a account. func SendRegisterNotifyMail(c *macaron.Context, u User) error {
func SendRegisterNotifyMail(c *macaron.Context, u User) {
data := map[string]any{ data := map[string]any{
"Username": u.DisplayName(), "Username": u.DisplayName(),
} }
body, err := render(tmplAuthRegisterNotify, data) body, err := render(tmplAuthRegisterNotify, data)
if err != nil { if err != nil {
log.Error("HTMLString: %v", err) return errors.Wrap(err, "render")
return
} }
msg := NewMessage([]string{u.Email()}, c.Tr("mail.register_notify"), body) msg, err := newMessage([]string{u.Email()}, c.Tr("mail.register_notify"), body)
msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID()) if err != nil {
return errors.Wrap(err, "new message")
}
msg.info = fmt.Sprintf("UID: %d, registration notify", u.ID())
Send(msg) send(msg)
return nil
} }
// SendCollaboratorMail sends mail notification to new collaborator. func SendCollaboratorMail(u, doer User, repo Repository) error {
func SendCollaboratorMail(u, doer User, repo Repository) {
subject := fmt.Sprintf("%s added you to %s", doer.DisplayName(), repo.FullName()) subject := fmt.Sprintf("%s added you to %s", doer.DisplayName(), repo.FullName())
data := map[string]any{ data := map[string]any{
@@ -173,14 +183,17 @@ func SendCollaboratorMail(u, doer User, repo Repository) {
} }
body, err := render(tmplNotifyCollaborator, data) body, err := render(tmplNotifyCollaborator, data)
if err != nil { if err != nil {
log.Error("HTMLString: %v", err) return errors.Wrap(err, "render")
return
} }
msg := NewMessage([]string{u.Email()}, subject, body) msg, err := newMessage([]string{u.Email()}, subject, body)
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID()) if err != nil {
return errors.Wrap(err, "new message")
}
msg.info = fmt.Sprintf("UID: %d, add collaborator", u.ID())
Send(msg) send(msg)
return nil
} }
func composeTplData(subject, body, link string) map[string]any { func composeTplData(subject, body, link string) map[string]any {
@@ -191,34 +204,47 @@ func composeTplData(subject, body, link string) map[string]any {
return data return data
} }
func composeIssueMessage(issue Issue, repo Repository, doer User, tplName string, tos []string, info string) *Message { func composeIssueMessage(issue Issue, repo Repository, doer User, tplName string, tos []string, info string) (*message, error) {
subject := issue.MailSubject() subject := issue.MailSubject()
body := string(markup.Markdown([]byte(issue.Content()), repo.HTMLURL(), repo.ComposeMetas())) body := string(markup.Markdown([]byte(issue.Content()), repo.HTMLURL(), repo.ComposeMetas()))
data := composeTplData(subject, body, issue.HTMLURL()) data := composeTplData(subject, body, issue.HTMLURL())
data["Doer"] = doer data["Doer"] = doer
content, err := render(tplName, data) content, err := render(tplName, data)
if err != nil { if err != nil {
log.Error("HTMLString (%s): %v", tplName, err) return nil, errors.Wrapf(err, "render %q", tplName)
} }
from := gomail.NewMessage().FormatAddress(conf.Email.FromEmail, doer.DisplayName()) from := (&mail.Address{Name: doer.DisplayName(), Address: conf.Email.FromEmail}).String()
msg := NewMessageFrom(tos, from, subject, content) msg, err := newMessageFrom(tos, from, subject, content)
msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info) if err != nil {
return msg return nil, errors.Wrap(err, "new message")
}
msg.info = fmt.Sprintf("Subject: %s, %s", subject, info)
return msg, nil
} }
// SendIssueCommentMail composes and sends issue comment emails to target receivers. // SendIssueCommentMail composes and sends issue comment emails to target receivers.
func SendIssueCommentMail(issue Issue, repo Repository, doer User, tos []string) { func SendIssueCommentMail(issue Issue, repo Repository, doer User, tos []string) error {
if len(tos) == 0 { if len(tos) == 0 {
return return nil
} }
Send(composeIssueMessage(issue, repo, doer, tmplIssueComment, tos, "issue comment")) msg, err := composeIssueMessage(issue, repo, doer, tmplIssueComment, tos, "issue comment")
if err != nil {
return errors.Wrap(err, "compose issue message")
}
send(msg)
return nil
} }
// SendIssueMentionMail composes and sends issue mention emails to target receivers. // SendIssueMentionMail composes and sends issue mention emails to target receivers.
func SendIssueMentionMail(issue Issue, repo Repository, doer User, tos []string) { func SendIssueMentionMail(issue Issue, repo Repository, doer User, tos []string) error {
if len(tos) == 0 { if len(tos) == 0 {
return return nil
} }
Send(composeIssueMessage(issue, repo, doer, tmplIssueMention, tos, "issue mention")) msg, err := composeIssueMessage(issue, repo, doer, tmplIssueMention, tos, "issue mention")
if err != nil {
return errors.Wrap(err, "compose issue message")
}
send(msg)
return nil
} }

View File

@@ -2,214 +2,134 @@ package email
import ( import (
"crypto/tls" "crypto/tls"
"io"
"net" "net"
"net/smtp" "strconv"
"os"
"strings" "strings"
"time"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/inbucket/html2text" "github.com/inbucket/html2text"
"gopkg.in/gomail.v2" gomail "github.com/wneessen/go-mail"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
) )
type Message struct { type message struct {
Info string // Message information for log purpose. info string
*gomail.Message msg *gomail.Msg
confirmChan chan struct{} confirmChan chan struct{}
} }
// NewMessageFrom creates new mail message object with custom From header. func newMessageFrom(to []string, from, subject, htmlBody string) (*message, error) {
func NewMessageFrom(to []string, from, subject, htmlBody string) *Message {
log.Trace("NewMessageFrom (htmlBody):\n%s", htmlBody) log.Trace("NewMessageFrom (htmlBody):\n%s", htmlBody)
msg := gomail.NewMessage() m := gomail.NewMsg()
msg.SetHeader("From", from) if err := m.From(from); err != nil {
msg.SetHeader("To", to...) return nil, errors.Wrapf(err, "set From address %q", from)
msg.SetHeader("Subject", conf.Email.SubjectPrefix+subject) }
msg.SetDateHeader("Date", time.Now()) if err := m.To(to...); err != nil {
return nil, errors.Wrap(err, "set To addresses")
}
m.Subject(conf.Email.SubjectPrefix + subject)
m.SetDate()
contentType := "text/html"
body := htmlBody
switchedToPlaintext := false
if conf.Email.UsePlainText || conf.Email.AddPlainTextAlt { if conf.Email.UsePlainText || conf.Email.AddPlainTextAlt {
plainBody, err := html2text.FromString(htmlBody) plainBody, err := html2text.FromString(htmlBody)
if err != nil { if err != nil {
log.Error("html2text.FromString: %v", err) return nil, errors.Wrap(err, "convert HTML to plain text")
}
if conf.Email.UsePlainText {
m.SetBodyString(gomail.TypeTextPlain, plainBody)
} else { } else {
contentType = "text/plain" m.SetBodyString(gomail.TypeTextPlain, plainBody)
body = plainBody m.AddAlternativeString(gomail.TypeTextHTML, htmlBody)
switchedToPlaintext = true
} }
} else {
m.SetBodyString(gomail.TypeTextHTML, htmlBody)
} }
msg.SetBody(contentType, body)
if switchedToPlaintext && conf.Email.AddPlainTextAlt && !conf.Email.UsePlainText { return &message{
// The AddAlternative method name is confusing - adding html as an "alternative" will actually cause mail msg: m,
// clients to show it as first priority, and the text "main body" is the 2nd priority fallback.
// See: https://godoc.org/gopkg.in/gomail.v2#Message.AddAlternative
msg.AddAlternative("text/html", htmlBody)
}
return &Message{
Message: msg,
confirmChan: make(chan struct{}), confirmChan: make(chan struct{}),
} }, nil
} }
// NewMessage creates new mail message object with default From header. func newMessage(to []string, subject, body string) (*message, error) {
func NewMessage(to []string, subject, body string) *Message { return newMessageFrom(to, conf.Email.From, subject, body)
return NewMessageFrom(to, conf.Email.From, subject, body)
} }
type loginAuth struct { func newSMTPClient() (*gomail.Client, error) {
username, password string
}
// SMTP AUTH LOGIN Auth Handler
func LoginAuth(username, password string) smtp.Auth {
return &loginAuth{username, password}
}
func (*loginAuth) Start(_ *smtp.ServerInfo) (string, []byte, error) {
return "LOGIN", []byte{}, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
default:
return nil, errors.Newf("unknwon fromServer: %s", string(fromServer))
}
}
return nil, nil
}
type Sender struct{}
func (*Sender) Send(from string, to []string, msg io.WriterTo) error {
opts := conf.Email opts := conf.Email
host, port, err := net.SplitHostPort(opts.Host) host, portStr, err := net.SplitHostPort(opts.Host)
if err != nil { if err != nil {
return err return nil, err
}
port, err := strconv.Atoi(portStr)
if err != nil {
return nil, err
}
clientOpts := []gomail.Option{
gomail.WithPort(port),
}
if port == 465 {
clientOpts = append(clientOpts, gomail.WithSSL())
} else {
clientOpts = append(clientOpts, gomail.WithTLSPolicy(gomail.TLSOpportunistic))
}
if opts.HELOHostname != "" {
clientOpts = append(clientOpts, gomail.WithHELO(opts.HELOHostname))
} }
tlsconfig := &tls.Config{ tlsconfig := &tls.Config{
InsecureSkipVerify: opts.SkipVerify, InsecureSkipVerify: opts.SkipVerify,
ServerName: host, ServerName: host,
} }
if opts.UseCertificate { if opts.UseCertificate {
cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile) cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile)
if err != nil { if err != nil {
return err return nil, err
} }
tlsconfig.Certificates = []tls.Certificate{cert} tlsconfig.Certificates = []tls.Certificate{cert}
} }
clientOpts = append(clientOpts, gomail.WithTLSConfig(tlsconfig))
conn, err := net.Dial("tcp", net.JoinHostPort(host, port)) if len(opts.User) > 0 {
clientOpts = append(clientOpts,
gomail.WithSMTPAuth(gomail.SMTPAuthAutoDiscover),
gomail.WithUsername(opts.User),
gomail.WithPassword(opts.Password),
)
}
return gomail.NewClient(host, clientOpts...)
}
func sendMessage(msg *message) error {
client, err := newSMTPClient()
if err != nil { if err != nil {
return err return err
} }
defer conn.Close() return client.DialAndSend(msg.msg)
isSecureConn := false
// Start TLS directly if the port ends with 465 (SMTPS protocol)
if strings.HasSuffix(port, "465") {
conn = tls.Client(conn, tlsconfig)
isSecureConn = true
}
client, err := smtp.NewClient(conn, host)
if err != nil {
return errors.Newf("NewClient: %v", err)
}
if !opts.DisableHELO {
hostname := opts.HELOHostname
if hostname == "" {
hostname, err = os.Hostname()
if err != nil {
return err
}
}
if err = client.Hello(hostname); err != nil {
return errors.Newf("hello: %v", err)
}
}
// If not using SMTPS, always use STARTTLS if available
hasStartTLS, _ := client.Extension("STARTTLS")
if !isSecureConn && hasStartTLS {
if err = client.StartTLS(tlsconfig); err != nil {
return errors.Newf("StartTLS: %v", err)
}
}
canAuth, options := client.Extension("AUTH")
if canAuth && len(opts.User) > 0 {
var auth smtp.Auth
if strings.Contains(options, "CRAM-MD5") {
auth = smtp.CRAMMD5Auth(opts.User, opts.Password)
} else if strings.Contains(options, "PLAIN") {
auth = smtp.PlainAuth("", opts.User, opts.Password, host)
} else if strings.Contains(options, "LOGIN") {
// Patch for AUTH LOGIN
auth = LoginAuth(opts.User, opts.Password)
}
if auth != nil {
if err = client.Auth(auth); err != nil {
return errors.Newf("auth: %v", err)
}
}
}
if err = client.Mail(from); err != nil {
return errors.Newf("mail: %v", err)
}
for _, rec := range to {
if err = client.Rcpt(rec); err != nil {
return errors.Newf("rcpt: %v", err)
}
}
w, err := client.Data()
if err != nil {
return errors.Newf("data: %v", err)
} else if _, err = msg.WriteTo(w); err != nil {
return errors.Newf("write to: %v", err)
} else if err = w.Close(); err != nil {
return errors.Newf("close: %v", err)
}
return client.Quit()
} }
func processMailQueue() { func processMailQueue() {
sender := &Sender{}
for msg := range mailQueue { for msg := range mailQueue {
log.Trace("New e-mail sending request %s: %s", msg.GetHeader("To"), msg.Info) to := strings.Join(msg.msg.GetToString(), ", ")
if err := gomail.Send(sender, msg.Message); err != nil { log.Trace("New e-mail sending request %s: %s", to, msg.info)
log.Error("Failed to send emails %s: %s - %v", msg.GetHeader("To"), msg.Info, err) if err := sendMessage(msg); err != nil {
log.Error("Failed to send emails %s: %s - %v", to, msg.info, err)
} else { } else {
log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info) log.Trace("E-mails sent %s: %s", to, msg.info)
} }
msg.confirmChan <- struct{}{} msg.confirmChan <- struct{}{}
} }
} }
var mailQueue chan *Message var mailQueue chan *message
// NewContext initializes settings for mailer. // NewContext initializes settings for mailer.
func NewContext() { func NewContext() {
@@ -220,14 +140,14 @@ func NewContext() {
return return
} }
mailQueue = make(chan *Message, 1000) mailQueue = make(chan *message, 1000)
go processMailQueue() go processMailQueue()
} }
// Send puts new message object into mail queue. // send puts new message object into mail queue.
// It returns without confirmation (mail processed asynchronously) in normal cases, // It returns without confirmation (mail processed asynchronously) in normal cases,
// but waits/blocks under hook mode to make sure mail has been sent. // but waits/blocks under hook mode to make sure mail has been sent.
func Send(msg *Message) { func send(msg *message) {
if !conf.Email.Enabled { if !conf.Email.Enabled {
return return
} }

View File

@@ -1,6 +1,8 @@
package lfsutil package lfsutil
import ( import (
"crypto/sha256"
"encoding/hex"
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
@@ -10,7 +12,10 @@ import (
"gogs.io/gogs/internal/osutil" "gogs.io/gogs/internal/osutil"
) )
var ErrObjectNotExist = errors.New("Object does not exist") var (
ErrObjectNotExist = errors.New("object does not exist")
ErrOIDMismatch = errors.New("content hash does not match OID")
)
// Storager is an storage backend for uploading and downloading LFS objects. // Storager is an storage backend for uploading and downloading LFS objects.
type Storager interface { type Storager interface {
@@ -39,6 +44,8 @@ var _ Storager = (*LocalStorage)(nil)
type LocalStorage struct { type LocalStorage struct {
// The root path for storing LFS objects. // The root path for storing LFS objects.
Root string Root string
// The path for storing temporary files during upload verification.
TempDir string
} }
func (*LocalStorage) Storage() Storage { func (*LocalStorage) Storage() Storage {
@@ -58,29 +65,50 @@ func (s *LocalStorage) Upload(oid OID, rc io.ReadCloser) (int64, error) {
return 0, ErrInvalidOID return 0, ErrInvalidOID
} }
var err error
fpath := s.storagePath(oid) fpath := s.storagePath(oid)
defer func() { dir := filepath.Dir(fpath)
rc.Close()
if err != nil { defer rc.Close()
_ = os.Remove(fpath)
}
}()
err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm) if err := os.MkdirAll(dir, os.ModePerm); err != nil {
if err != nil {
return 0, errors.Wrap(err, "create directories") return 0, errors.Wrap(err, "create directories")
} }
w, err := os.Create(fpath)
if err != nil {
return 0, errors.Wrap(err, "create file")
}
defer w.Close()
written, err := io.Copy(w, rc) // If the object file already exists, skip the upload and return the
// existing file's size.
if fi, err := os.Stat(fpath); err == nil {
_, _ = io.Copy(io.Discard, rc)
return fi.Size(), nil
}
// Write to a temp file and verify the content hash before publishing.
// This ensures the final path always contains a complete, hash-verified
// file, even when concurrent uploads of the same OID race.
if err := os.MkdirAll(s.TempDir, os.ModePerm); err != nil {
return 0, errors.Wrap(err, "create temp directory")
}
tmp, err := os.CreateTemp(s.TempDir, "upload-*")
if err != nil { if err != nil {
return 0, errors.Wrap(err, "copy file") return 0, errors.Wrap(err, "create temp file")
}
tmpPath := tmp.Name()
defer os.Remove(tmpPath)
hash := sha256.New()
written, err := io.Copy(tmp, io.TeeReader(rc, hash))
if closeErr := tmp.Close(); err == nil && closeErr != nil {
err = closeErr
}
if err != nil {
return 0, errors.Wrap(err, "write object file")
}
if computed := hex.EncodeToString(hash.Sum(nil)); computed != string(oid) {
return 0, ErrOIDMismatch
}
if err := os.Rename(tmpPath, fpath); err != nil && !os.IsExist(err) {
return 0, errors.Wrap(err, "publish object file")
} }
return written, nil return written, nil
} }

View File

@@ -10,6 +10,9 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gogs.io/gogs/internal/osutil"
) )
func TestLocalStorage_storagePath(t *testing.T) { func TestLocalStorage_storagePath(t *testing.T) {
@@ -46,50 +49,54 @@ func TestLocalStorage_storagePath(t *testing.T) {
} }
func TestLocalStorage_Upload(t *testing.T) { func TestLocalStorage_Upload(t *testing.T) {
base := t.TempDir()
s := &LocalStorage{ s := &LocalStorage{
Root: filepath.Join(os.TempDir(), "lfs-objects"), Root: filepath.Join(base, "lfs-objects"),
TempDir: filepath.Join(base, "tmp", "lfs"),
} }
t.Cleanup(func() {
_ = os.RemoveAll(s.Root) const helloWorldOID = OID("c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a") // "Hello world!"
t.Run("invalid OID", func(t *testing.T) {
written, err := s.Upload("bad_oid", io.NopCloser(strings.NewReader("")))
assert.Equal(t, int64(0), written)
assert.Equal(t, ErrInvalidOID, err)
}) })
tests := []struct { t.Run("valid OID", func(t *testing.T) {
name string written, err := s.Upload(helloWorldOID, io.NopCloser(strings.NewReader("Hello world!")))
oid OID require.NoError(t, err)
content string assert.Equal(t, int64(12), written)
expWritten int64 })
expErr error
}{
{
name: "invalid oid",
oid: "bad_oid",
expErr: ErrInvalidOID,
},
{ t.Run("valid OID but wrong content", func(t *testing.T) {
name: "valid oid", oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
oid: "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f", written, err := s.Upload(oid, io.NopCloser(strings.NewReader("Hello world!")))
content: "Hello world!", assert.Equal(t, int64(0), written)
expWritten: 12, assert.Equal(t, ErrOIDMismatch, err)
},
} // File should have been cleaned up.
for _, test := range tests { assert.False(t, osutil.IsFile(s.storagePath(oid)))
t.Run(test.name, func(t *testing.T) { })
written, err := s.Upload(test.oid, io.NopCloser(strings.NewReader(test.content)))
assert.Equal(t, test.expWritten, written) t.Run("duplicate upload returns existing size", func(t *testing.T) {
assert.Equal(t, test.expErr, err) written, err := s.Upload(helloWorldOID, io.NopCloser(strings.NewReader("should be ignored")))
}) require.NoError(t, err)
} assert.Equal(t, int64(12), written)
// Verify original content is preserved.
var buf bytes.Buffer
err = s.Download(helloWorldOID, &buf)
require.NoError(t, err)
assert.Equal(t, "Hello world!", buf.String())
})
} }
func TestLocalStorage_Download(t *testing.T) { func TestLocalStorage_Download(t *testing.T) {
oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f") oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
s := &LocalStorage{ s := &LocalStorage{
Root: filepath.Join(os.TempDir(), "lfs-objects"), Root: filepath.Join(t.TempDir(), "lfs-objects"),
} }
t.Cleanup(func() {
_ = os.RemoveAll(s.Root)
})
fpath := s.storagePath(oid) fpath := s.storagePath(oid)
err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm) err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm)

View File

@@ -1,6 +1,8 @@
package markup package markup
import ( import (
"net/url"
"strings"
"sync" "sync"
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
@@ -32,14 +34,28 @@ func NewSanitizer() {
sanitizer.policy.AllowAttrs("type").Matching(lazyregexp.New(`^checkbox$`).Regexp()).OnElements("input") sanitizer.policy.AllowAttrs("type").Matching(lazyregexp.New(`^checkbox$`).Regexp()).OnElements("input")
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input") sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
// Data URLs // Only allow data URIs with safe image MIME types to prevent XSS via
sanitizer.policy.AllowURLSchemes("data") // "data:text/html" payloads.
sanitizer.policy.AllowURLSchemeWithCustomPolicy("data", isSafeDataURI)
// Custom URL-Schemes // Custom URL-Schemes
sanitizer.policy.AllowURLSchemes(conf.Markdown.CustomURLSchemes...) sanitizer.policy.AllowURLSchemes(conf.Markdown.CustomURLSchemes...)
}) })
} }
// isSafeDataURI returns whether the given data URI uses a safe image MIME type.
func isSafeDataURI(u *url.URL) bool {
// The opaque data of a data URI has the form "mediatype;base64,data" or
// "mediatype,data". We only allow common image MIME types.
mediatype, _, _ := strings.Cut(u.Opaque, ";")
mediatype, _, _ = strings.Cut(mediatype, ",")
switch strings.TrimSpace(strings.ToLower(mediatype)) {
case "image/png", "image/jpeg", "image/gif", "image/webp", "image/x-icon":
return true
}
return false
}
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist. // Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
func Sanitize(s string) string { func Sanitize(s string) string {
return sanitizer.policy.Sanitize(s) return sanitizer.policy.Sanitize(s)

View File

@@ -26,6 +26,20 @@ func Test_Sanitizer(t *testing.T) {
{input: `<input type="hidden">`, expVal: ``}, {input: `<input type="hidden">`, expVal: ``},
{input: `<input type="checkbox">`, expVal: `<input type="checkbox">`}, {input: `<input type="checkbox">`, expVal: `<input type="checkbox">`},
{input: `<input checked disabled autofocus>`, expVal: `<input checked="" disabled="">`}, {input: `<input checked disabled autofocus>`, expVal: `<input checked="" disabled="">`},
// Data URIs: safe image types should be allowed
{input: `<img src="data:image/png;base64,abc">`, expVal: `<img src="data:image/png;base64,abc">`},
{input: `<img src="data:image/jpeg;base64,abc">`, expVal: `<img src="data:image/jpeg;base64,abc">`},
{input: `<img src="data:image/gif;base64,abc">`, expVal: `<img src="data:image/gif;base64,abc">`},
{input: `<img src="data:image/webp;base64,abc">`, expVal: `<img src="data:image/webp;base64,abc">`},
// Data URIs: text/html must be stripped to prevent XSS (GHSA-xrcr-gmf5-2r8j)
{input: `<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">Click</a>`, expVal: `Click`},
{input: `<a href="data:text/html,<script>alert(1)</script>">XSS</a>`, expVal: `XSS`},
{input: `<img src="data:text/html;base64,abc">`, expVal: ``},
// Data URIs: SVG must be stripped (can contain embedded JavaScript)
{input: `<img src="data:image/svg+xml;base64,abc">`, expVal: ``},
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.input, func(t *testing.T) { t.Run(test.input, func(t *testing.T) {

View File

@@ -1,13 +1,12 @@
package admin package admin
import ( import (
"encoding/json"
"fmt" "fmt"
"runtime" "runtime"
"strings" "strings"
"time" "time"
jsoniter "github.com/json-iterator/go"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/cron" "gogs.io/gogs/internal/cron"
@@ -217,7 +216,7 @@ func Config(c *context.Context) {
Mode: strings.Title(conf.Log.Modes[i]), Mode: strings.Title(conf.Log.Modes[i]),
} }
result, _ := jsoniter.MarshalIndent(conf.Log.Configs[i], "", " ") result, _ := json.MarshalIndent(conf.Log.Configs[i], "", " ")
loggers[i].Config = string(result) loggers[i].Config = string(result)
} }
c.Data["Loggers"] = loggers c.Data["Loggers"] = loggers

View File

@@ -106,7 +106,9 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
// Send email notification. // Send email notification.
if f.SendNotify && conf.Email.Enabled { if f.SendNotify && conf.Email.Enabled {
email.SendRegisterNotifyMail(c.Context, database.NewMailerUser(user)) if err := email.SendRegisterNotifyMail(c.Context, database.NewMailerUser(user)); err != nil {
log.Error("Failed to send register notify mail: %v", err)
}
} }
c.Flash.Success(c.Tr("admin.users.new_success", user.Name)) c.Flash.Success(c.Tr("admin.users.new_success", user.Name))

View File

@@ -0,0 +1,303 @@
package v1
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/gogs/git-module"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/route/api/v1/types"
)
// toAllowedPageSize makes sure page size is in allowed range.
func toAllowedPageSize(size int) int {
if size <= 0 {
size = 10
} else if size > conf.API.MaxResponseItems {
size = conf.API.MaxResponseItems
}
return size
}
// toUser converts a database user to an API user with the full name sanitized
// for safe HTML rendering.
func toUser(u *database.User) *types.User {
return &types.User{
ID: u.ID,
UserName: u.Name,
Login: u.Name,
FullName: markup.Sanitize(u.FullName),
Email: u.Email,
AvatarURL: u.AvatarURL(),
}
}
func toUserEmail(email *database.EmailAddress) *types.UserEmail {
return &types.UserEmail{
Email: email.Email,
Verified: email.IsActivated,
Primary: email.IsPrimary,
}
}
func toBranch(b *database.Branch, c *git.Commit) *types.RepositoryBranch {
return &types.RepositoryBranch{
Name: b.Name,
Commit: toPayloadCommit(c),
}
}
func toTag(b *database.Tag, c *git.Commit) *tag {
return &tag{
Name: b.Name,
Commit: toPayloadCommit(c),
}
}
func toPayloadCommit(c *git.Commit) *types.WebhookPayloadCommit {
authorUsername := ""
author, err := database.Handle.Users().GetByEmail(context.TODO(), c.Author.Email)
if err == nil {
authorUsername = author.Name
}
committerUsername := ""
committer, err := database.Handle.Users().GetByEmail(context.TODO(), c.Committer.Email)
if err == nil {
committerUsername = committer.Name
}
return &types.WebhookPayloadCommit{
ID: c.ID.String(),
Message: c.Message,
URL: "Not implemented",
Author: &types.WebhookPayloadUser{
Name: c.Author.Name,
Email: c.Author.Email,
UserName: authorUsername,
},
Committer: &types.WebhookPayloadUser{
Name: c.Committer.Name,
Email: c.Committer.Email,
UserName: committerUsername,
},
Timestamp: c.Author.When,
}
}
func toUserPublicKey(apiLink string, key *database.PublicKey) *types.UserPublicKey {
return &types.UserPublicKey{
ID: key.ID,
Key: key.Content,
URL: apiLink + strconv.FormatInt(key.ID, 10),
Title: key.Name,
Created: key.Created,
}
}
func toRepositoryHook(repoLink string, w *database.Webhook) *types.RepositoryHook {
config := map[string]string{
"url": w.URL,
"content_type": w.ContentType.Name(),
}
if w.HookTaskType == database.SLACK {
s := w.SlackMeta()
config["channel"] = s.Channel
config["username"] = s.Username
config["icon_url"] = s.IconURL
config["color"] = s.Color
}
return &types.RepositoryHook{
ID: w.ID,
Type: w.HookTaskType.Name(),
URL: fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
Active: w.IsActive,
Config: config,
Events: w.EventsArray(),
Updated: w.Updated,
Created: w.Created,
}
}
func toDeployKey(apiLink string, key *database.DeployKey) *types.RepositoryDeployKey {
return &types.RepositoryDeployKey{
ID: key.ID,
Key: key.Content,
URL: apiLink + strconv.FormatInt(key.ID, 10),
Title: key.Name,
Created: key.Created,
ReadOnly: true, // All deploy keys are read-only.
}
}
func toOrganization(org *database.User) *types.Organization {
return &types.Organization{
ID: org.ID,
AvatarURL: org.AvatarURL(),
UserName: org.Name,
FullName: org.FullName,
Description: org.Description,
Website: org.Website,
Location: org.Location,
}
}
func toOrganizationTeam(team *database.Team) *types.OrganizationTeam {
return &types.OrganizationTeam{
ID: team.ID,
Name: team.Name,
Description: team.Description,
Permission: team.Authorize.String(),
}
}
func toIssueLabel(l *database.Label) *types.IssueLabel {
return &types.IssueLabel{
ID: l.ID,
Name: l.Name,
Color: strings.TrimLeft(l.Color, "#"),
}
}
func issueState(isClosed bool) types.IssueStateType {
if isClosed {
return types.IssueStateClosed
}
return types.IssueStateOpen
}
// toIssue converts a database issue to an API issue.
// It assumes the following fields have been assigned with valid values:
// Required - Poster, Labels
// Optional - Milestone, Assignee, PullRequest
func toIssue(issue *database.Issue) *types.Issue {
labels := make([]*types.IssueLabel, len(issue.Labels))
for i := range issue.Labels {
labels[i] = toIssueLabel(issue.Labels[i])
}
apiIssue := &types.Issue{
ID: issue.ID,
Index: issue.Index,
Poster: toUser(issue.Poster),
Title: issue.Title,
Body: issue.Content,
Labels: labels,
State: issueState(issue.IsClosed),
Comments: issue.NumComments,
Created: issue.Created,
Updated: issue.Updated,
}
if issue.Milestone != nil {
apiIssue.Milestone = toIssueMilestone(issue.Milestone)
}
if issue.Assignee != nil {
apiIssue.Assignee = toUser(issue.Assignee)
}
if issue.IsPull {
apiIssue.PullRequest = &types.PullRequestMeta{
HasMerged: issue.PullRequest.HasMerged,
}
if issue.PullRequest.HasMerged {
apiIssue.PullRequest.Merged = &issue.PullRequest.Merged
}
}
return apiIssue
}
func toIssueComment(c *database.Comment) *types.IssueComment {
return &types.IssueComment{
ID: c.ID,
HTMLURL: c.HTMLURL(),
Poster: toUser(c.Poster),
Body: c.Content,
Created: c.Created,
Updated: c.Updated,
}
}
func toIssueMilestone(m *database.Milestone) *types.IssueMilestone {
ms := &types.IssueMilestone{
ID: m.ID,
State: issueState(m.IsClosed),
Title: m.Name,
Description: m.Content,
OpenIssues: m.NumOpenIssues,
ClosedIssues: m.NumClosedIssues,
}
if m.IsClosed {
ms.Closed = &m.ClosedDate
}
if m.Deadline.Year() < 9999 {
ms.Deadline = &m.Deadline
}
return ms
}
// toRelease converts a database release to an API release.
// It assumes the Publisher field has been assigned.
func toRelease(r *database.Release) *types.RepositoryRelease {
return &types.RepositoryRelease{
ID: r.ID,
TagName: r.TagName,
TargetCommitish: r.Target,
Name: r.Title,
Body: r.Note,
Draft: r.IsDraft,
Prerelease: r.IsPrerelease,
Author: toUser(r.Publisher),
Created: r.Created,
}
}
func toRepositoryCollaborator(c *database.Collaborator) *types.RepositoryCollaborator {
return &types.RepositoryCollaborator{
User: toUser(c.User),
Permissions: types.RepositoryPermission{
Admin: c.Collaboration.Mode >= database.AccessModeAdmin,
Push: c.Collaboration.Mode >= database.AccessModeWrite,
Pull: c.Collaboration.Mode >= database.AccessModeRead,
},
}
}
// toRepository converts a database repository to an API repository.
// It assumes the Owner field has been loaded on the repo.
func toRepository(repo *database.Repository, perm *types.RepositoryPermission) *types.Repository {
cloneLink := repo.CloneLink()
apiRepo := &types.Repository{
ID: repo.ID,
Owner: toUser(repo.Owner),
Name: repo.Name,
FullName: repo.FullName(),
Description: repo.Description,
Private: repo.IsPrivate,
Fork: repo.IsFork,
Empty: repo.IsBare,
Mirror: repo.IsMirror,
Size: repo.Size,
HTMLURL: repo.HTMLURL(),
SSHURL: cloneLink.SSH,
CloneURL: cloneLink.HTTPS,
Website: repo.Website,
Stars: repo.NumStars,
Forks: repo.NumForks,
Watchers: repo.NumWatches,
OpenIssues: repo.NumOpenIssues,
DefaultBranch: repo.DefaultBranch,
Created: repo.Created,
Updated: repo.Updated,
Permissions: perm,
}
if repo.IsFork {
p := &types.RepositoryPermission{Pull: true}
apiRepo.Parent = toRepository(repo.BaseRepo, p)
}
return apiRepo
}

View File

@@ -1,13 +0,0 @@
package admin
import (
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/route/api/v1/org"
"gogs.io/gogs/internal/route/api/v1/user"
)
func CreateOrg(c *context.APIContext, form api.CreateOrgOption) {
org.CreateOrgForUser(c, form, user.GetUserByParams(c))
}

View File

@@ -1,18 +0,0 @@
package admin
import (
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/route/api/v1/repo"
"gogs.io/gogs/internal/route/api/v1/user"
)
func CreateRepo(c *context.APIContext, form api.CreateRepoOption) {
owner := user.GetUserByParams(c)
if c.Written() {
return
}
repo.CreateUserRepo(c, owner, form)
}

View File

@@ -0,0 +1,9 @@
package v1
import (
"gogs.io/gogs/internal/context"
)
func adminCreateOrg(c *context.APIContext, form createOrgRequest) {
createOrgForUser(c, form, getUserByParams(c))
}

View File

@@ -1,11 +1,11 @@
package admin package v1
import ( import (
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
) )
func GetRepositoryByParams(c *context.APIContext) *database.Repository { func getRepositoryByParams(c *context.APIContext) *database.Repository {
repo, err := database.GetRepositoryByName(c.Org.Team.OrgID, c.Params(":reponame")) repo, err := database.GetRepositoryByName(c.Org.Team.OrgID, c.Params(":reponame"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get repository by name") c.NotFoundOrError(err, "get repository by name")
@@ -14,8 +14,8 @@ func GetRepositoryByParams(c *context.APIContext) *database.Repository {
return repo return repo
} }
func AddTeamRepository(c *context.APIContext) { func adminAddTeamRepository(c *context.APIContext) {
repo := GetRepositoryByParams(c) repo := getRepositoryByParams(c)
if c.Written() { if c.Written() {
return return
} }
@@ -27,8 +27,8 @@ func AddTeamRepository(c *context.APIContext) {
c.NoContent() c.NoContent()
} }
func RemoveTeamRepository(c *context.APIContext) { func adminRemoveTeamRepository(c *context.APIContext) {
repo := GetRepositoryByParams(c) repo := getRepositoryByParams(c)
if c.Written() { if c.Written() {
return return
} }

View File

@@ -1,17 +1,20 @@
package admin package v1
import ( import (
"net/http" "net/http"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/convert" "gogs.io/gogs/internal/route/api/v1/types"
"gogs.io/gogs/internal/route/api/v1/user"
) )
func CreateTeam(c *context.APIContext, form api.CreateTeamOption) { type adminCreateTeamRequest struct {
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
Description string `json:"description" binding:"MaxSize(255)"`
Permission string `json:"permission"`
}
func adminCreateTeam(c *context.APIContext, form adminCreateTeamRequest) {
team := &database.Team{ team := &database.Team{
OrgID: c.Org.Organization.ID, OrgID: c.Org.Organization.ID,
Name: form.Name, Name: form.Name,
@@ -27,11 +30,11 @@ func CreateTeam(c *context.APIContext, form api.CreateTeamOption) {
return return
} }
c.JSON(http.StatusCreated, convert.ToTeam(team)) c.JSON(http.StatusCreated, toOrganizationTeam(team))
} }
func AddTeamMember(c *context.APIContext) { func adminAddTeamMember(c *context.APIContext) {
u := user.GetUserByParams(c) u := getUserByParams(c)
if c.Written() { if c.Written() {
return return
} }
@@ -43,8 +46,8 @@ func AddTeamMember(c *context.APIContext) {
c.NoContent() c.NoContent()
} }
func RemoveTeamMember(c *context.APIContext) { func adminRemoveTeamMember(c *context.APIContext) {
u := user.GetUserByParams(c) u := getUserByParams(c)
if c.Written() { if c.Written() {
return return
} }
@@ -57,16 +60,16 @@ func RemoveTeamMember(c *context.APIContext) {
c.NoContent() c.NoContent()
} }
func ListTeamMembers(c *context.APIContext) { func adminListTeamMembers(c *context.APIContext) {
team := c.Org.Team team := c.Org.Team
if err := team.GetMembers(); err != nil { if err := team.GetMembers(); err != nil {
c.Error(err, "get team members") c.Error(err, "get team members")
return return
} }
apiMembers := make([]*api.User, len(team.Members)) apiMembers := make([]*types.User, len(team.Members))
for i := range team.Members { for i := range team.Members {
apiMembers[i] = team.Members[i].APIFormat() apiMembers[i] = toUser(team.Members[i])
} }
c.JSONSuccess(apiMembers) c.JSONSuccess(apiMembers)
} }

View File

@@ -0,0 +1,14 @@
package v1
import (
"gogs.io/gogs/internal/context"
)
func adminCreateRepo(c *context.APIContext, form createRepoRequest) {
owner := getUserByParams(c)
if c.Written() {
return
}
createUserRepo(c, owner, form)
}

View File

@@ -1,16 +1,14 @@
package admin package v1
import ( import (
"net/http" "net/http"
api "github.com/gogs/go-gogs-client"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/email" "gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/route/api/v1/user"
) )
func parseLoginSource(c *context.APIContext, sourceID int64) { func parseLoginSource(c *context.APIContext, sourceID int64) {
@@ -29,13 +27,23 @@ func parseLoginSource(c *context.APIContext, sourceID int64) {
} }
} }
func CreateUser(c *context.APIContext, form api.CreateUserOption) { type adminCreateUserRequest struct {
SourceID int64 `json:"source_id"`
LoginName string `json:"login_name"`
Username string `json:"username" binding:"Required;AlphaDashDot;MaxSize(35)"`
FullName string `json:"full_name" binding:"MaxSize(100)"`
Email string `json:"email" binding:"Required;Email;MaxSize(254)"`
Password string `json:"password" binding:"MaxSize(255)"`
SendNotify bool `json:"send_notify"`
}
func adminCreateUser(c *context.APIContext, form adminCreateUserRequest) {
parseLoginSource(c, form.SourceID) parseLoginSource(c, form.SourceID)
if c.Written() { if c.Written() {
return return
} }
user, err := database.Handle.Users().Create( u, err := database.Handle.Users().Create(
c.Req.Context(), c.Req.Context(),
form.Username, form.Username,
form.Email, form.Email,
@@ -57,18 +65,35 @@ func CreateUser(c *context.APIContext, form api.CreateUserOption) {
} }
return return
} }
log.Trace("Account %q created by admin %q", user.Name, c.User.Name) log.Trace("Account %q created by admin %q", u.Name, c.User.Name)
// Send email notification. // Send email notification.
if form.SendNotify && conf.Email.Enabled { if form.SendNotify && conf.Email.Enabled {
email.SendRegisterNotifyMail(c.Context.Context, database.NewMailerUser(user)) if err := email.SendRegisterNotifyMail(c.Context.Context, database.NewMailerUser(u)); err != nil {
log.Error("Failed to send register notify mail: %v", err)
}
} }
c.JSON(http.StatusCreated, user.APIFormat()) c.JSON(http.StatusCreated, toUser(u))
} }
func EditUser(c *context.APIContext, form api.EditUserOption) { type adminEditUserRequest struct {
u := user.GetUserByParams(c) SourceID int64 `json:"source_id"`
LoginName string `json:"login_name"`
FullName string `json:"full_name" binding:"MaxSize(100)"`
Email string `json:"email" binding:"Required;Email;MaxSize(254)"`
Password string `json:"password" binding:"MaxSize(255)"`
Website string `json:"website" binding:"MaxSize(50)"`
Location string `json:"location" binding:"MaxSize(50)"`
Active *bool `json:"active"`
Admin *bool `json:"admin"`
AllowGitHook *bool `json:"allow_git_hook"`
AllowImportLocal *bool `json:"allow_import_local"`
MaxRepoCreation *int `json:"max_repo_creation"`
}
func adminEditUser(c *context.APIContext, form adminEditUserRequest) {
u := getUserByParams(c)
if c.Written() { if c.Written() {
return return
} }
@@ -116,11 +141,11 @@ func EditUser(c *context.APIContext, form api.EditUserOption) {
c.Error(err, "get user") c.Error(err, "get user")
return return
} }
c.JSONSuccess(u.APIFormat()) c.JSONSuccess(toUser(u))
} }
func DeleteUser(c *context.APIContext) { func adminDeleteUser(c *context.APIContext) {
u := user.GetUserByParams(c) u := getUserByParams(c)
if c.Written() { if c.Written() {
return return
} }
@@ -139,10 +164,10 @@ func DeleteUser(c *context.APIContext) {
c.NoContent() c.NoContent()
} }
func CreatePublicKey(c *context.APIContext, form api.CreateKeyOption) { func adminCreatePublicKey(c *context.APIContext, form createPublicKeyRequest) {
u := user.GetUserByParams(c) u := getUserByParams(c)
if c.Written() { if c.Written() {
return return
} }
user.CreateUserPublicKey(c, form, u.ID) createUserPublicKey(c, form, u.ID)
} }

View File

@@ -7,16 +7,9 @@ import (
"github.com/go-macaron/binding" "github.com/go-macaron/binding"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/form" "gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/route/api/v1/admin"
"gogs.io/gogs/internal/route/api/v1/misc"
"gogs.io/gogs/internal/route/api/v1/org"
"gogs.io/gogs/internal/route/api/v1/repo"
"gogs.io/gogs/internal/route/api/v1/user"
) )
// repoAssignment extracts information from URL parameters to retrieve the repository, // repoAssignment extracts information from URL parameters to retrieve the repository,
@@ -181,245 +174,245 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Options("/*", func() {}) m.Options("/*", func() {})
// Miscellaneous // Miscellaneous
m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown) m.Post("/markdown", bind(markdownRequest{}), markdown)
m.Post("/markdown/raw", misc.MarkdownRaw) m.Post("/markdown/raw", markdownRaw)
// Users // Users
m.Group("/users", func() { m.Group("/users", func() {
m.Get("/search", user.Search) m.Get("/search", searchUsers)
m.Group("/:username", func() { m.Group("/:username", func() {
m.Get("", user.GetInfo) m.Get("", getUserProfile)
m.Group("/tokens", func() { m.Group("/tokens", func() {
accessTokensHandler := user.NewAccessTokensHandler(user.NewAccessTokensStore()) accessTokensHandler := newAccessTokensHandler(newAccessTokensStore())
m.Combo(""). m.Combo("").
Get(accessTokensHandler.List()). Get(accessTokensHandler.List()).
Post(bind(api.CreateAccessTokenOption{}), accessTokensHandler.Create()) Post(bind(createAccessTokenRequest{}), accessTokensHandler.Create())
}, reqBasicAuth()) }, reqBasicAuth())
}) })
}) })
m.Group("/users", func() { m.Group("/users", func() {
m.Group("/:username", func() { m.Group("/:username", func() {
m.Get("/keys", user.ListPublicKeys) m.Get("/keys", listPublicKeys)
m.Get("/followers", user.ListFollowers) m.Get("/followers", listFollowers)
m.Group("/following", func() { m.Group("/following", func() {
m.Get("", user.ListFollowing) m.Get("", listFollowing)
m.Get("/:target", user.CheckFollowing) m.Get("/:target", checkFollowing)
}) })
}) })
}, reqToken()) }, reqToken())
m.Group("/user", func() { m.Group("/user", func() {
m.Get("", user.GetAuthenticatedUser) m.Get("", getAuthenticatedUser)
m.Combo("/emails"). m.Combo("/emails").
Get(user.ListEmails). Get(listEmails).
Post(bind(api.CreateEmailOption{}), user.AddEmail). Post(bind(createEmailRequest{}), addEmail).
Delete(bind(api.CreateEmailOption{}), user.DeleteEmail) Delete(bind(createEmailRequest{}), deleteEmail)
m.Get("/followers", user.ListMyFollowers) m.Get("/followers", listMyFollowers)
m.Group("/following", func() { m.Group("/following", func() {
m.Get("", user.ListMyFollowing) m.Get("", listMyFollowing)
m.Combo("/:username"). m.Combo("/:username").
Get(user.CheckMyFollowing). Get(checkMyFollowing).
Put(user.Follow). Put(follow).
Delete(user.Unfollow) Delete(unfollow)
}) })
m.Group("/keys", func() { m.Group("/keys", func() {
m.Combo(""). m.Combo("").
Get(user.ListMyPublicKeys). Get(listMyPublicKeys).
Post(bind(api.CreateKeyOption{}), user.CreatePublicKey) Post(bind(createPublicKeyRequest{}), createPublicKey)
m.Combo("/:id"). m.Combo("/:id").
Get(user.GetPublicKey). Get(getPublicKey).
Delete(user.DeletePublicKey) Delete(deletePublicKey)
}) })
m.Get("/issues", repo.ListUserIssues) m.Get("/issues", listUserIssues)
}, reqToken()) }, reqToken())
// Repositories // Repositories
m.Get("/users/:username/repos", reqToken(), repo.ListUserRepositories) m.Get("/users/:username/repos", reqToken(), listUserRepositories)
m.Get("/orgs/:org/repos", reqToken(), repo.ListOrgRepositories) m.Get("/orgs/:org/repos", reqToken(), listOrgRepositories)
m.Combo("/user/repos", reqToken()). m.Combo("/user/repos", reqToken()).
Get(repo.ListMyRepos). Get(listMyRepos).
Post(bind(api.CreateRepoOption{}), repo.Create) Post(bind(createRepoRequest{}), createRepo)
m.Post("/org/:org/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) m.Post("/org/:org/repos", reqToken(), bind(createRepoRequest{}), createOrgRepo)
m.Group("/repos", func() { m.Group("/repos", func() {
m.Get("/search", repo.Search) m.Get("/search", searchRepos)
m.Get("/:username/:reponame", repoAssignment(), repo.Get) m.Get("/:username/:reponame", repoAssignment(), getRepo)
m.Get("/:username/:reponame/releases", repoAssignment(), repo.Releases) m.Get("/:username/:reponame/releases", repoAssignment(), releases)
}) })
m.Group("/repos", func() { m.Group("/repos", func() {
m.Post("/migrate", bind(form.MigrateRepo{}), repo.Migrate) m.Post("/migrate", bind(form.MigrateRepo{}), migrate)
m.Delete("/:username/:reponame", repoAssignment(), reqRepoOwner(), repo.Delete) m.Delete("/:username/:reponame", repoAssignment(), reqRepoOwner(), deleteRepo)
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {
m.Group("/hooks", func() { m.Group("/hooks", func() {
m.Combo(""). m.Combo("").
Get(repo.ListHooks). Get(listHooks).
Post(bind(api.CreateHookOption{}), repo.CreateHook) Post(bind(createHookRequest{}), createHook)
m.Combo("/:id"). m.Combo("/:id").
Patch(bind(api.EditHookOption{}), repo.EditHook). Patch(bind(editHookRequest{}), editHook).
Delete(repo.DeleteHook) Delete(deleteHook)
}, reqRepoAdmin()) }, reqRepoAdmin())
m.Group("/collaborators", func() { m.Group("/collaborators", func() {
m.Get("", repo.ListCollaborators) m.Get("", listCollaborators)
m.Combo("/:collaborator"). m.Combo("/:collaborator").
Get(repo.IsCollaborator). Get(isCollaborator).
Put(bind(api.AddCollaboratorOption{}), repo.AddCollaborator). Put(bind(addCollaboratorRequest{}), addCollaborator).
Delete(repo.DeleteCollaborator) Delete(deleteCollaborator)
}, reqRepoAdmin()) }, reqRepoAdmin())
m.Get("/raw/*", context.RepoRef(), repo.GetRawFile) m.Get("/raw/*", context.RepoRef(), getRawFile)
m.Group("/contents", func() { m.Group("/contents", func() {
m.Get("", repo.GetContents) m.Get("", getContents)
m.Combo("/*"). m.Combo("/*").
Get(repo.GetContents). Get(getContents).
Put(reqRepoWriter(), bind(repo.PutContentsRequest{}), repo.PutContents) Put(reqRepoWriter(), bind(putContentsRequest{}), putContents)
}) })
m.Get("/archive/*", repo.GetArchive) m.Get("/archive/*", getArchive)
m.Group("/git", func() { m.Group("/git", func() {
m.Group("/trees", func() { m.Group("/trees", func() {
m.Get("/:sha", repo.GetRepoGitTree) m.Get("/:sha", getRepoGitTree)
}) })
m.Group("/blobs", func() { m.Group("/blobs", func() {
m.Get("/:sha", repo.RepoGitBlob) m.Get("/:sha", repoGitBlob)
}) })
}) })
m.Get("/forks", repo.ListForks) m.Get("/forks", listForks)
m.Get("/tags", repo.ListTags) m.Get("/tags", listTags)
m.Group("/branches", func() { m.Group("/branches", func() {
m.Get("", repo.ListBranches) m.Get("", listBranches)
m.Get("/*", repo.GetBranch) m.Get("/*", getBranch)
}) })
m.Group("/commits", func() { m.Group("/commits", func() {
m.Get("/:sha", repo.GetSingleCommit) m.Get("/:sha", getSingleCommit)
m.Get("", repo.GetAllCommits) m.Get("", getAllCommits)
m.Get("/*", repo.GetReferenceSHA) m.Get("/*", getReferenceSHA)
}) })
m.Group("/keys", func() { m.Group("/keys", func() {
m.Combo(""). m.Combo("").
Get(repo.ListDeployKeys). Get(listDeployKeys).
Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) Post(bind(createDeployKeyRequest{}), createDeployKey)
m.Combo("/:id"). m.Combo("/:id").
Get(repo.GetDeployKey). Get(getDeployKey).
Delete(repo.DeleteDeploykey) Delete(deleteDeploykey)
}, reqRepoAdmin()) }, reqRepoAdmin())
m.Group("/issues", func() { m.Group("/issues", func() {
m.Combo(""). m.Combo("").
Get(repo.ListIssues). Get(listIssues).
Post(bind(api.CreateIssueOption{}), repo.CreateIssue) Post(bind(createIssueRequest{}), createIssue)
m.Group("/comments", func() { m.Group("/comments", func() {
m.Get("", repo.ListRepoIssueComments) m.Get("", listRepoIssueComments)
m.Patch("/:id", bind(api.EditIssueCommentOption{}), repo.EditIssueComment) m.Patch("/:id", bind(editIssueCommentRequest{}), editIssueComment)
}) })
m.Group("/:index", func() { m.Group("/:index", func() {
m.Combo(""). m.Combo("").
Get(repo.GetIssue). Get(getIssue).
Patch(bind(api.EditIssueOption{}), repo.EditIssue) Patch(bind(editIssueRequest{}), editIssue)
m.Group("/comments", func() { m.Group("/comments", func() {
m.Combo(""). m.Combo("").
Get(repo.ListIssueComments). Get(listIssueComments).
Post(bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) Post(bind(createIssueCommentRequest{}), createIssueComment)
m.Combo("/:id"). m.Combo("/:id").
Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueComment). Patch(bind(editIssueCommentRequest{}), editIssueComment).
Delete(repo.DeleteIssueComment) Delete(deleteIssueComment)
}) })
m.Get("/labels", repo.ListIssueLabels) m.Get("/labels", listIssueLabels)
m.Group("/labels", func() { m.Group("/labels", func() {
m.Combo(""). m.Combo("").
Post(bind(api.IssueLabelsOption{}), repo.AddIssueLabels). Post(bind(issueLabelsRequest{}), addIssueLabels).
Put(bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). Put(bind(issueLabelsRequest{}), replaceIssueLabels).
Delete(repo.ClearIssueLabels) Delete(clearIssueLabels)
m.Delete("/:id", repo.DeleteIssueLabel) m.Delete("/:id", deleteIssueLabel)
}, reqRepoWriter()) }, reqRepoWriter())
}) })
}, mustEnableIssues) }, mustEnableIssues)
m.Group("/labels", func() { m.Group("/labels", func() {
m.Get("", repo.ListLabels) m.Get("", listLabels)
m.Get("/:id", repo.GetLabel) m.Get("/:id", getLabel)
}) })
m.Group("/labels", func() { m.Group("/labels", func() {
m.Post("", bind(api.CreateLabelOption{}), repo.CreateLabel) m.Post("", bind(createLabelRequest{}), createLabel)
m.Combo("/:id"). m.Combo("/:id").
Patch(bind(api.EditLabelOption{}), repo.EditLabel). Patch(bind(editLabelRequest{}), editLabel).
Delete(repo.DeleteLabel) Delete(deleteLabel)
}, reqRepoWriter()) }, reqRepoWriter())
m.Group("/milestones", func() { m.Group("/milestones", func() {
m.Get("", repo.ListMilestones) m.Get("", listMilestones)
m.Get("/:id", repo.GetMilestone) m.Get("/:id", getMilestone)
}) })
m.Group("/milestones", func() { m.Group("/milestones", func() {
m.Post("", bind(api.CreateMilestoneOption{}), repo.CreateMilestone) m.Post("", bind(createMilestoneRequest{}), createMilestone)
m.Combo("/:id"). m.Combo("/:id").
Patch(bind(api.EditMilestoneOption{}), repo.EditMilestone). Patch(bind(editMilestoneRequest{}), editMilestone).
Delete(repo.DeleteMilestone) Delete(deleteMilestone)
}, reqRepoWriter()) }, reqRepoWriter())
m.Patch("/issue-tracker", reqRepoWriter(), bind(api.EditIssueTrackerOption{}), repo.IssueTracker) m.Patch("/issue-tracker", reqRepoWriter(), bind(editIssueTrackerRequest{}), issueTracker)
m.Patch("/wiki", reqRepoWriter(), bind(api.EditWikiOption{}), repo.Wiki) m.Patch("/wiki", reqRepoWriter(), bind(editWikiRequest{}), wiki)
m.Post("/mirror-sync", reqRepoWriter(), repo.MirrorSync) m.Post("/mirror-sync", reqRepoWriter(), mirrorSync)
m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig) m.Get("/editorconfig/:filename", context.RepoRef(), getEditorconfig)
}, repoAssignment()) }, repoAssignment())
}, reqToken()) }, reqToken())
m.Get("/issues", reqToken(), repo.ListUserIssues) m.Get("/issues", reqToken(), listUserIssues)
// Organizations // Organizations
m.Combo("/user/orgs", reqToken()). m.Combo("/user/orgs", reqToken()).
Get(org.ListMyOrgs). Get(listMyOrgs).
Post(bind(api.CreateOrgOption{}), org.CreateMyOrg) Post(bind(createOrgRequest{}), createMyOrg)
m.Get("/users/:username/orgs", org.ListUserOrgs) m.Get("/users/:username/orgs", listUserOrgs)
m.Group("/orgs/:orgname", func() { m.Group("/orgs/:orgname", func() {
m.Combo(""). m.Combo("").
Get(org.Get). Get(getOrg).
Patch(bind(api.EditOrgOption{}), org.Edit) Patch(bind(editOrgRequest{}), editOrg)
m.Get("/teams", org.ListTeams) m.Get("/teams", listTeams)
}, orgAssignment(true)) }, orgAssignment(true))
m.Group("/admin", func() { m.Group("/admin", func() {
m.Group("/users", func() { m.Group("/users", func() {
m.Post("", bind(api.CreateUserOption{}), admin.CreateUser) m.Post("", bind(adminCreateUserRequest{}), adminCreateUser)
m.Group("/:username", func() { m.Group("/:username", func() {
m.Combo(""). m.Combo("").
Patch(bind(api.EditUserOption{}), admin.EditUser). Patch(bind(adminEditUserRequest{}), adminEditUser).
Delete(admin.DeleteUser) Delete(adminDeleteUser)
m.Post("/keys", bind(api.CreateKeyOption{}), admin.CreatePublicKey) m.Post("/keys", bind(createPublicKeyRequest{}), adminCreatePublicKey)
m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg) m.Post("/orgs", bind(createOrgRequest{}), adminCreateOrg)
m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo) m.Post("/repos", bind(createRepoRequest{}), adminCreateRepo)
}) })
}) })
m.Group("/orgs/:orgname", func() { m.Group("/orgs/:orgname", func() {
m.Group("/teams", func() { m.Group("/teams", func() {
m.Post("", orgAssignment(true), bind(api.CreateTeamOption{}), admin.CreateTeam) m.Post("", orgAssignment(true), bind(adminCreateTeamRequest{}), adminCreateTeam)
}) })
}) })
m.Group("/teams", func() { m.Group("/teams", func() {
m.Group("/:teamid", func() { m.Group("/:teamid", func() {
m.Get("/members", admin.ListTeamMembers) m.Get("/members", adminListTeamMembers)
m.Combo("/members/:username"). m.Combo("/members/:username").
Put(admin.AddTeamMember). Put(adminAddTeamMember).
Delete(admin.RemoveTeamMember) Delete(adminRemoveTeamMember)
m.Combo("/repos/:reponame"). m.Combo("/repos/:reponame").
Put(admin.AddTeamRepository). Put(adminAddTeamRepository).
Delete(admin.RemoveTeamRepository) Delete(adminRemoveTeamRepository)
}, orgAssignment(false, true)) }, orgAssignment(false, true))
}) })
}, reqAdmin()) }, reqAdmin())

View File

@@ -1,135 +0,0 @@
package convert
import (
"context"
"fmt"
"strconv"
"github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/database"
)
func ToEmail(email *database.EmailAddress) *api.Email {
return &api.Email{
Email: email.Email,
Verified: email.IsActivated,
Primary: email.IsPrimary,
}
}
func ToBranch(b *database.Branch, c *git.Commit) *api.Branch {
return &api.Branch{
Name: b.Name,
Commit: ToCommit(c),
}
}
type Tag struct {
Name string `json:"name"`
Commit *api.PayloadCommit `json:"commit"`
}
func ToTag(b *database.Tag, c *git.Commit) *Tag {
return &Tag{
Name: b.Name,
Commit: ToCommit(c),
}
}
func ToCommit(c *git.Commit) *api.PayloadCommit {
authorUsername := ""
author, err := database.Handle.Users().GetByEmail(context.TODO(), c.Author.Email)
if err == nil {
authorUsername = author.Name
}
committerUsername := ""
committer, err := database.Handle.Users().GetByEmail(context.TODO(), c.Committer.Email)
if err == nil {
committerUsername = committer.Name
}
return &api.PayloadCommit{
ID: c.ID.String(),
Message: c.Message,
URL: "Not implemented",
Author: &api.PayloadUser{
Name: c.Author.Name,
Email: c.Author.Email,
UserName: authorUsername,
},
Committer: &api.PayloadUser{
Name: c.Committer.Name,
Email: c.Committer.Email,
UserName: committerUsername,
},
Timestamp: c.Author.When,
}
}
func ToPublicKey(apiLink string, key *database.PublicKey) *api.PublicKey {
return &api.PublicKey{
ID: key.ID,
Key: key.Content,
URL: apiLink + strconv.FormatInt(key.ID, 10),
Title: key.Name,
Created: key.Created,
}
}
func ToHook(repoLink string, w *database.Webhook) *api.Hook {
config := map[string]string{
"url": w.URL,
"content_type": w.ContentType.Name(),
}
if w.HookTaskType == database.SLACK {
s := w.SlackMeta()
config["channel"] = s.Channel
config["username"] = s.Username
config["icon_url"] = s.IconURL
config["color"] = s.Color
}
return &api.Hook{
ID: w.ID,
Type: w.HookTaskType.Name(),
URL: fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
Active: w.IsActive,
Config: config,
Events: w.EventsArray(),
Updated: w.Updated,
Created: w.Created,
}
}
func ToDeployKey(apiLink string, key *database.DeployKey) *api.DeployKey {
return &api.DeployKey{
ID: key.ID,
Key: key.Content,
URL: apiLink + strconv.FormatInt(key.ID, 10),
Title: key.Name,
Created: key.Created,
ReadOnly: true, // All deploy keys are read-only.
}
}
func ToOrganization(org *database.User) *api.Organization {
return &api.Organization{
ID: org.ID,
AvatarUrl: org.AvatarURL(),
UserName: org.Name,
FullName: org.FullName,
Description: org.Description,
Website: org.Website,
Location: org.Location,
}
}
func ToTeam(team *database.Team) *api.Team {
return &api.Team{
ID: team.ID,
Name: team.Name,
Description: team.Description,
Permission: team.Authorize.String(),
}
}

View File

@@ -1,15 +0,0 @@
package convert
import (
"gogs.io/gogs/internal/conf"
)
// ToCorrectPageSize makes sure page size is in allowed range.
func ToCorrectPageSize(size int) int {
if size <= 0 {
size = 10
} else if size > conf.API.MaxResponseItems {
size = conf.API.MaxResponseItems
}
return size
}

View File

@@ -1,13 +1,17 @@
package misc package v1
import ( import (
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
) )
func Markdown(c *context.APIContext, form api.MarkdownOption) { // markdownRequest represents the request body for rendering markdown.
type markdownRequest struct {
Text string
Context string
}
func markdown(c *context.APIContext, form markdownRequest) {
if form.Text == "" { if form.Text == "" {
_, _ = c.Write([]byte("")) _, _ = c.Write([]byte(""))
return return
@@ -16,7 +20,7 @@ func Markdown(c *context.APIContext, form api.MarkdownOption) {
_, _ = c.Write(markup.Markdown([]byte(form.Text), form.Context, nil)) _, _ = c.Write(markup.Markdown([]byte(form.Text), form.Context, nil))
} }
func MarkdownRaw(c *context.APIContext) { func markdownRaw(c *context.APIContext) {
body, err := c.Req.Body().Bytes() body, err := c.Req.Body().Bytes()
if err != nil { if err != nil {
c.Error(err, "read body") c.Error(err, "read body")

View File

@@ -1,17 +1,22 @@
package org package v1
import ( import (
"net/http" "net/http"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/convert" "gogs.io/gogs/internal/route/api/v1/types"
"gogs.io/gogs/internal/route/api/v1/user"
) )
func CreateOrgForUser(c *context.APIContext, apiForm api.CreateOrgOption, user *database.User) { type createOrgRequest struct {
UserName string `json:"username" binding:"Required"`
FullName string `json:"full_name"`
Description string `json:"description"`
Website string `json:"website"`
Location string `json:"location"`
}
func createOrgForUser(c *context.APIContext, apiForm createOrgRequest, user *database.User) {
if c.Written() { if c.Written() {
return return
} }
@@ -35,10 +40,10 @@ func CreateOrgForUser(c *context.APIContext, apiForm api.CreateOrgOption, user *
return return
} }
c.JSON(201, convert.ToOrganization(org)) c.JSON(201, toOrganization(org))
} }
func listUserOrgs(c *context.APIContext, u *database.User, all bool) { func listOrgsOfUser(c *context.APIContext, u *database.User, all bool) {
orgs, err := database.Handle.Organizations().List( orgs, err := database.Handle.Organizations().List(
c.Req.Context(), c.Req.Context(),
database.ListOrgsOptions{ database.ListOrgsOptions{
@@ -51,34 +56,41 @@ func listUserOrgs(c *context.APIContext, u *database.User, all bool) {
return return
} }
apiOrgs := make([]*api.Organization, len(orgs)) apiOrgs := make([]*types.Organization, len(orgs))
for i := range orgs { for i := range orgs {
apiOrgs[i] = convert.ToOrganization(orgs[i]) apiOrgs[i] = toOrganization(orgs[i])
} }
c.JSONSuccess(&apiOrgs) c.JSONSuccess(&apiOrgs)
} }
func ListMyOrgs(c *context.APIContext) { func listMyOrgs(c *context.APIContext) {
listUserOrgs(c, c.User, true) listOrgsOfUser(c, c.User, true)
} }
func CreateMyOrg(c *context.APIContext, apiForm api.CreateOrgOption) { func createMyOrg(c *context.APIContext, apiForm createOrgRequest) {
CreateOrgForUser(c, apiForm, c.User) createOrgForUser(c, apiForm, c.User)
} }
func ListUserOrgs(c *context.APIContext) { func listUserOrgs(c *context.APIContext) {
u := user.GetUserByParams(c) u := getUserByParams(c)
if c.Written() { if c.Written() {
return return
} }
listUserOrgs(c, u, false) listOrgsOfUser(c, u, false)
} }
func Get(c *context.APIContext) { func getOrg(c *context.APIContext) {
c.JSONSuccess(convert.ToOrganization(c.Org.Organization)) c.JSONSuccess(toOrganization(c.Org.Organization))
} }
func Edit(c *context.APIContext, form api.EditOrgOption) { type editOrgRequest struct {
FullName string `json:"full_name"`
Description string `json:"description"`
Website string `json:"website"`
Location string `json:"location"`
}
func editOrg(c *context.APIContext, form editOrgRequest) {
org := c.Org.Organization org := c.Org.Organization
if !org.IsOwnedBy(c.User.ID) { if !org.IsOwnedBy(c.User.ID) {
c.Status(http.StatusForbidden) c.Status(http.StatusForbidden)
@@ -105,5 +117,5 @@ func Edit(c *context.APIContext, form api.EditOrgOption) {
c.Error(err, "get organization") c.Error(err, "get organization")
return return
} }
c.JSONSuccess(convert.ToOrganization(org)) c.JSONSuccess(toOrganization(org))
} }

View File

@@ -1,22 +0,0 @@
package org
import (
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/route/api/v1/convert"
)
func ListTeams(c *context.APIContext) {
org := c.Org.Organization
if err := org.GetTeams(); err != nil {
c.Error(err, "get teams")
return
}
apiTeams := make([]*api.Team, len(org.Teams))
for i := range org.Teams {
apiTeams[i] = convert.ToTeam(org.Teams[i])
}
c.JSONSuccess(apiTeams)
}

View File

@@ -0,0 +1,20 @@
package v1
import (
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/route/api/v1/types"
)
func listTeams(c *context.APIContext) {
org := c.Org.Organization
if err := org.GetTeams(); err != nil {
c.Error(err, "get teams")
return
}
apiTeams := make([]*types.OrganizationTeam, len(org.Teams))
for i := range org.Teams {
apiTeams[i] = toOrganizationTeam(org.Teams[i])
}
c.JSONSuccess(apiTeams)
}

View File

@@ -1,4 +1,4 @@
package repo package v1
import ( import (
"encoding/base64" "encoding/base64"
@@ -11,7 +11,7 @@ import (
"gogs.io/gogs/internal/repoutil" "gogs.io/gogs/internal/repoutil"
) )
func RepoGitBlob(c *context.APIContext) { func repoGitBlob(c *context.APIContext) {
gitRepo, err := git.Open(repoutil.RepositoryPath(c.Params(":username"), c.Params(":reponame"))) gitRepo, err := git.Open(repoutil.RepositoryPath(c.Params(":username"), c.Params(":reponame")))
if err != nil { if err != nil {
c.Error(err, "open repository") c.Error(err, "open repository")

View File

@@ -1,14 +1,12 @@
package repo package v1
import ( import (
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/route/api/v1/convert" "gogs.io/gogs/internal/route/api/v1/types"
) )
// https://github.com/gogs/go-gogs-client/wiki/Repositories#get-branch // https://github.com/gogs/go-gogs-client/wiki/Repositories#get-branch
func GetBranch(c *context.APIContext) { func getBranch(c *context.APIContext) {
branch, err := c.Repo.Repository.GetBranch(c.Params("*")) branch, err := c.Repo.Repository.GetBranch(c.Params("*"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get branch") c.NotFoundOrError(err, "get branch")
@@ -21,25 +19,25 @@ func GetBranch(c *context.APIContext) {
return return
} }
c.JSONSuccess(convert.ToBranch(branch, commit)) c.JSONSuccess(toBranch(branch, commit))
} }
// https://github.com/gogs/go-gogs-client/wiki/Repositories#list-branches // https://github.com/gogs/go-gogs-client/wiki/Repositories#list-branches
func ListBranches(c *context.APIContext) { func listBranches(c *context.APIContext) {
branches, err := c.Repo.Repository.GetBranches() branches, err := c.Repo.Repository.GetBranches()
if err != nil { if err != nil {
c.Error(err, "get branches") c.Error(err, "get branches")
return return
} }
apiBranches := make([]*api.Branch, len(branches)) apiBranches := make([]*types.RepositoryBranch, len(branches))
for i := range branches { for i := range branches {
commit, err := branches[i].GetCommit() commit, err := branches[i].GetCommit()
if err != nil { if err != nil {
c.Error(err, "get commit") c.Error(err, "get commit")
return return
} }
apiBranches[i] = convert.ToBranch(branches[i], commit) apiBranches[i] = toBranch(branches[i], commit)
} }
c.JSONSuccess(&apiBranches) c.JSONSuccess(&apiBranches)

View File

@@ -1,29 +1,32 @@
package repo package v1
import ( import (
"net/http" "net/http"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/types"
) )
func ListCollaborators(c *context.APIContext) { func listCollaborators(c *context.APIContext) {
collaborators, err := c.Repo.Repository.GetCollaborators() collaborators, err := c.Repo.Repository.GetCollaborators()
if err != nil { if err != nil {
c.Error(err, "get collaborators") c.Error(err, "get collaborators")
return return
} }
apiCollaborators := make([]*api.Collaborator, len(collaborators)) apiCollaborators := make([]*types.RepositoryCollaborator, len(collaborators))
for i := range collaborators { for i := range collaborators {
apiCollaborators[i] = collaborators[i].APIFormat() apiCollaborators[i] = toRepositoryCollaborator(collaborators[i])
} }
c.JSONSuccess(&apiCollaborators) c.JSONSuccess(&apiCollaborators)
} }
func AddCollaborator(c *context.APIContext, form api.AddCollaboratorOption) { type addCollaboratorRequest struct {
Permission *string `json:"permission"`
}
func addCollaborator(c *context.APIContext, form addCollaboratorRequest) {
collaborator, err := database.Handle.Users().GetByUsername(c.Req.Context(), c.Params(":collaborator")) collaborator, err := database.Handle.Users().GetByUsername(c.Req.Context(), c.Params(":collaborator"))
if err != nil { if err != nil {
if database.IsErrUserNotExist(err) { if database.IsErrUserNotExist(err) {
@@ -49,7 +52,7 @@ func AddCollaborator(c *context.APIContext, form api.AddCollaboratorOption) {
c.NoContent() c.NoContent()
} }
func IsCollaborator(c *context.APIContext) { func isCollaborator(c *context.APIContext) {
collaborator, err := database.Handle.Users().GetByUsername(c.Req.Context(), c.Params(":collaborator")) collaborator, err := database.Handle.Users().GetByUsername(c.Req.Context(), c.Params(":collaborator"))
if err != nil { if err != nil {
if database.IsErrUserNotExist(err) { if database.IsErrUserNotExist(err) {
@@ -67,7 +70,7 @@ func IsCollaborator(c *context.APIContext) {
} }
} }
func DeleteCollaborator(c *context.APIContext) { func deleteCollaborator(c *context.APIContext) {
collaborator, err := database.Handle.Users().GetByUsername(c.Req.Context(), c.Params(":collaborator")) collaborator, err := database.Handle.Users().GetByUsername(c.Req.Context(), c.Params(":collaborator"))
if err != nil { if err != nil {
if database.IsErrUserNotExist(err) { if database.IsErrUserNotExist(err) {

View File

@@ -1,4 +1,4 @@
package repo package v1
import ( import (
"net/http" "net/http"
@@ -6,16 +6,18 @@ import (
"time" "time"
"github.com/gogs/git-module" "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/gitutil" "gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/route/api/v1/types"
) )
// GetAllCommits returns a slice of commits starting from HEAD. const mediaApplicationSHA = "application/vnd.gogs.sha"
func GetAllCommits(c *context.APIContext) {
// getAllCommits returns a slice of commits starting from HEAD.
func getAllCommits(c *context.APIContext) {
// Get pagesize, set default if it is not specified. // Get pagesize, set default if it is not specified.
pageSize := c.QueryInt("pageSize") pageSize := c.QueryInt("pageSize")
if pageSize == 0 { if pageSize == 0 {
@@ -29,7 +31,7 @@ func GetAllCommits(c *context.APIContext) {
} }
// The response object returned as JSON // The response object returned as JSON
result := make([]*api.Commit, 0, pageSize) result := make([]*types.Commit, 0, pageSize)
commits, err := gitRepo.Log("HEAD", git.LogOptions{MaxCount: pageSize}) commits, err := gitRepo.Log("HEAD", git.LogOptions{MaxCount: pageSize})
if err != nil { if err != nil {
c.Error(err, "git log") c.Error(err, "git log")
@@ -47,11 +49,11 @@ func GetAllCommits(c *context.APIContext) {
c.JSONSuccess(result) c.JSONSuccess(result)
} }
// GetSingleCommit will return a single Commit object based on the specified SHA. // getSingleCommit will return a single Commit object based on the specified SHA.
func GetSingleCommit(c *context.APIContext) { func getSingleCommit(c *context.APIContext) {
if strings.Contains(c.Req.Header.Get("Accept"), api.MediaApplicationSHA) { if strings.Contains(c.Req.Header.Get("Accept"), mediaApplicationSHA) {
c.SetParams("*", c.Params(":sha")) c.SetParams("*", c.Params(":sha"))
GetReferenceSHA(c) getReferenceSHA(c)
return return
} }
@@ -73,7 +75,7 @@ func GetSingleCommit(c *context.APIContext) {
c.JSONSuccess(apiCommit) c.JSONSuccess(apiCommit)
} }
func GetReferenceSHA(c *context.APIContext) { func getReferenceSHA(c *context.APIContext) {
gitRepo, err := git.Open(c.Repo.Repository.RepoPath()) gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
if err != nil { if err != nil {
c.Error(err, "open repository") c.Error(err, "open repository")
@@ -114,14 +116,14 @@ func GetReferenceSHA(c *context.APIContext) {
} }
// gitCommitToApiCommit is a helper function to convert git commit object to API commit. // gitCommitToApiCommit is a helper function to convert git commit object to API commit.
func gitCommitToAPICommit(commit *git.Commit, c *context.APIContext) (*api.Commit, error) { func gitCommitToAPICommit(commit *git.Commit, c *context.APIContext) (*types.Commit, error) {
// Retrieve author and committer information // Retrieve author and committer information
var apiAuthor, apiCommitter *api.User var apiAuthor, apiCommitter *types.User
author, err := database.Handle.Users().GetByEmail(c.Req.Context(), commit.Author.Email) author, err := database.Handle.Users().GetByEmail(c.Req.Context(), commit.Author.Email)
if err != nil && !database.IsErrUserNotExist(err) { if err != nil && !database.IsErrUserNotExist(err) {
return nil, err return nil, err
} else if err == nil { } else if err == nil {
apiAuthor = author.APIFormat() apiAuthor = toUser(author)
} }
// Save one query if the author is also the committer // Save one query if the author is also the committer
@@ -132,40 +134,40 @@ func gitCommitToAPICommit(commit *git.Commit, c *context.APIContext) (*api.Commi
if err != nil && !database.IsErrUserNotExist(err) { if err != nil && !database.IsErrUserNotExist(err) {
return nil, err return nil, err
} else if err == nil { } else if err == nil {
apiCommitter = committer.APIFormat() apiCommitter = toUser(committer)
} }
} }
// Retrieve parent(s) of the commit // Retrieve parent(s) of the commit
apiParents := make([]*api.CommitMeta, commit.ParentsCount()) apiParents := make([]*types.CommitMeta, commit.ParentsCount())
for i := 0; i < commit.ParentsCount(); i++ { for i := 0; i < commit.ParentsCount(); i++ {
sha, _ := commit.ParentID(i) sha, _ := commit.ParentID(i)
apiParents[i] = &api.CommitMeta{ apiParents[i] = &types.CommitMeta{
URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/commits/" + sha.String(), URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/commits/" + sha.String(),
SHA: sha.String(), SHA: sha.String(),
} }
} }
return &api.Commit{ return &types.Commit{
CommitMeta: &api.CommitMeta{ CommitMeta: &types.CommitMeta{
URL: conf.Server.ExternalURL + c.Link[1:], URL: conf.Server.ExternalURL + c.Link[1:],
SHA: commit.ID.String(), SHA: commit.ID.String(),
}, },
HTMLURL: c.Repo.Repository.HTMLURL() + "/commits/" + commit.ID.String(), HTMLURL: c.Repo.Repository.HTMLURL() + "/commits/" + commit.ID.String(),
RepoCommit: &api.RepoCommit{ RepoCommit: &types.RepoCommit{
URL: conf.Server.ExternalURL + c.Link[1:], URL: conf.Server.ExternalURL + c.Link[1:],
Author: &api.CommitUser{ Author: &types.CommitUser{
Name: commit.Author.Name, Name: commit.Author.Name,
Email: commit.Author.Email, Email: commit.Author.Email,
Date: commit.Author.When.Format(time.RFC3339), Date: commit.Author.When.Format(time.RFC3339),
}, },
Committer: &api.CommitUser{ Committer: &types.CommitUser{
Name: commit.Committer.Name, Name: commit.Committer.Name,
Email: commit.Committer.Email, Email: commit.Committer.Email,
Date: commit.Committer.When.Format(time.RFC3339), Date: commit.Committer.When.Format(time.RFC3339),
}, },
Message: commit.Summary(), Message: commit.Summary(),
Tree: &api.CommitMeta{ Tree: &types.CommitMeta{
URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/tree/" + commit.ID.String(), URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/tree/" + commit.ID.String(),
SHA: commit.ID.String(), SHA: commit.ID.String(),
}, },

View File

@@ -1,4 +1,4 @@
package repo package v1
import ( import (
"encoding/base64" "encoding/base64"
@@ -98,7 +98,7 @@ func toRepoContent(c *context.APIContext, ref, subpath string, commit *git.Commi
return content, nil return content, nil
} }
func GetContents(c *context.APIContext) { func getContents(c *context.APIContext) {
repoPath := repoutil.RepositoryPath(c.Params(":username"), c.Params(":reponame")) repoPath := repoutil.RepositoryPath(c.Params(":username"), c.Params(":reponame"))
gitRepo, err := git.Open(repoPath) gitRepo, err := git.Open(repoPath)
if err != nil { if err != nil {
@@ -168,15 +168,15 @@ func GetContents(c *context.APIContext) {
c.JSONSuccess(contents) c.JSONSuccess(contents)
} }
// PutContentsRequest is the API message for creating or updating a file. // putContentsRequest is the API message for creating or updating a file.
type PutContentsRequest struct { type putContentsRequest struct {
Message string `json:"message" binding:"Required"` Message string `json:"message" binding:"Required"`
Content string `json:"content" binding:"Required"` Content string `json:"content" binding:"Required"`
Branch string `json:"branch"` Branch string `json:"branch"`
} }
// PUT /repos/:username/:reponame/contents/* // PUT /repos/:username/:reponame/contents/*
func PutContents(c *context.APIContext, r PutContentsRequest) { func putContents(c *context.APIContext, r putContentsRequest) {
content, err := base64.StdEncoding.DecodeString(r.Content) content, err := base64.StdEncoding.DecodeString(r.Content)
if err != nil { if err != nil {
c.Error(err, "decoding base64") c.Error(err, "decoding base64")

View File

@@ -1,4 +1,4 @@
package repo package v1
import ( import (
"github.com/gogs/git-module" "github.com/gogs/git-module"
@@ -6,10 +6,10 @@ import (
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/gitutil" "gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/route/repo" routerepo "gogs.io/gogs/internal/route/repo"
) )
func GetRawFile(c *context.APIContext) { func getRawFile(c *context.APIContext) {
if !c.Repo.HasAccess() { if !c.Repo.HasAccess() {
c.NotFound() c.NotFound()
return return
@@ -25,12 +25,12 @@ func GetRawFile(c *context.APIContext) {
c.NotFoundOrError(gitutil.NewError(err), "get blob") c.NotFoundOrError(gitutil.NewError(err), "get blob")
return return
} }
if err = repo.ServeBlob(c.Context, blob); err != nil { if err = routerepo.ServeBlob(c.Context, blob); err != nil {
c.Error(err, "serve blob") c.Error(err, "serve blob")
} }
} }
func GetArchive(c *context.APIContext) { func getArchive(c *context.APIContext) {
repoPath := database.RepoPath(c.Params(":username"), c.Params(":reponame")) repoPath := database.RepoPath(c.Params(":username"), c.Params(":reponame"))
gitRepo, err := git.Open(repoPath) gitRepo, err := git.Open(repoPath)
if err != nil { if err != nil {
@@ -39,10 +39,10 @@ func GetArchive(c *context.APIContext) {
} }
c.Repo.GitRepo = gitRepo c.Repo.GitRepo = gitRepo
repo.Download(c.Context) routerepo.Download(c.Context)
} }
func GetEditorconfig(c *context.APIContext) { func getEditorconfig(c *context.APIContext) {
ec, err := c.Repo.Editorconfig() ec, err := c.Repo.Editorconfig()
if err != nil { if err != nil {
c.NotFoundOrError(gitutil.NewError(err), "get .editorconfig") c.NotFoundOrError(gitutil.NewError(err), "get .editorconfig")

View File

@@ -1,36 +1,41 @@
package repo package v1
import ( import (
"encoding/json"
"net/http" "net/http"
"slices" "slices"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
jsoniter "github.com/json-iterator/go"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/convert" "gogs.io/gogs/internal/route/api/v1/types"
) )
// https://github.com/gogs/go-gogs-client/wiki/Repositories#list-hooks // https://github.com/gogs/go-gogs-client/wiki/Repositories#list-hooks
func ListHooks(c *context.APIContext) { func listHooks(c *context.APIContext) {
hooks, err := database.GetWebhooksByRepoID(c.Repo.Repository.ID) hooks, err := database.GetWebhooksByRepoID(c.Repo.Repository.ID)
if err != nil { if err != nil {
c.Errorf(err, "get webhooks by repository ID") c.Errorf(err, "get webhooks by repository ID")
return return
} }
apiHooks := make([]*api.Hook, len(hooks)) apiHooks := make([]*types.RepositoryHook, len(hooks))
for i := range hooks { for i := range hooks {
apiHooks[i] = convert.ToHook(c.Repo.RepoLink, hooks[i]) apiHooks[i] = toRepositoryHook(c.Repo.RepoLink, hooks[i])
} }
c.JSONSuccess(&apiHooks) c.JSONSuccess(&apiHooks)
} }
// https://github.com/gogs/go-gogs-client/wiki/Repositories#create-a-hook // https://github.com/gogs/go-gogs-client/wiki/Repositories#create-a-hook
func CreateHook(c *context.APIContext, form api.CreateHookOption) { type createHookRequest struct {
Type string `json:"type" binding:"Required"`
Config map[string]string `json:"config" binding:"Required"`
Events []string `json:"events"`
Active bool `json:"active"`
}
func createHook(c *context.APIContext, form createHookRequest) {
if !database.IsValidHookTaskType(form.Type) { if !database.IsValidHookTaskType(form.Type) {
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Invalid hook type.")) c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Invalid hook type."))
return return
@@ -76,7 +81,7 @@ func CreateHook(c *context.APIContext, form api.CreateHookOption) {
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Missing config option: channel")) c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Missing config option: channel"))
return return
} }
meta, err := jsoniter.Marshal(&database.SlackMeta{ meta, err := json.Marshal(&database.SlackMeta{
Channel: channel, Channel: channel,
Username: form.Config["username"], Username: form.Config["username"],
IconURL: form.Config["icon_url"], IconURL: form.Config["icon_url"],
@@ -97,11 +102,17 @@ func CreateHook(c *context.APIContext, form api.CreateHookOption) {
return return
} }
c.JSON(http.StatusCreated, convert.ToHook(c.Repo.RepoLink, w)) c.JSON(http.StatusCreated, toRepositoryHook(c.Repo.RepoLink, w))
} }
// https://github.com/gogs/go-gogs-client/wiki/Repositories#edit-a-hook // https://github.com/gogs/go-gogs-client/wiki/Repositories#edit-a-hook
func EditHook(c *context.APIContext, form api.EditHookOption) { type editHookRequest struct {
Config map[string]string `json:"config"`
Events []string `json:"events"`
Active *bool `json:"active"`
}
func editHook(c *context.APIContext, form editHookRequest) {
w, err := database.GetWebhookOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")) w, err := database.GetWebhookOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get webhook of repository by ID") c.NotFoundOrError(err, "get webhook of repository by ID")
@@ -122,7 +133,7 @@ func EditHook(c *context.APIContext, form api.EditHookOption) {
if w.HookTaskType == database.SLACK { if w.HookTaskType == database.SLACK {
if channel, ok := form.Config["channel"]; ok { if channel, ok := form.Config["channel"]; ok {
meta, err := jsoniter.Marshal(&database.SlackMeta{ meta, err := json.Marshal(&database.SlackMeta{
Channel: channel, Channel: channel,
Username: form.Config["username"], Username: form.Config["username"],
IconURL: form.Config["icon_url"], IconURL: form.Config["icon_url"],
@@ -166,10 +177,10 @@ func EditHook(c *context.APIContext, form api.EditHookOption) {
return return
} }
c.JSONSuccess(convert.ToHook(c.Repo.RepoLink, w)) c.JSONSuccess(toRepositoryHook(c.Repo.RepoLink, w))
} }
func DeleteHook(c *context.APIContext) { func deleteHook(c *context.APIContext) {
if err := database.DeleteWebhookOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil { if err := database.DeleteWebhookOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil {
c.Errorf(err, "delete webhook of repository by ID") c.Errorf(err, "delete webhook of repository by ID")
return return

View File

@@ -1,18 +1,18 @@
package repo package v1
import ( import (
"net/http" "net/http"
"strings" "strings"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/types"
) )
func listIssues(c *context.APIContext, opts *database.IssuesOptions) { func queryIssues(c *context.APIContext, opts *database.IssuesOptions) {
issues, err := database.Issues(opts) issues, err := database.Issues(opts)
if err != nil { if err != nil {
c.Error(err, "list issues") c.Error(err, "list issues")
@@ -26,49 +26,58 @@ func listIssues(c *context.APIContext, opts *database.IssuesOptions) {
} }
// FIXME: use IssueList to improve performance. // FIXME: use IssueList to improve performance.
apiIssues := make([]*api.Issue, len(issues)) apiIssues := make([]*types.Issue, len(issues))
for i := range issues { for i := range issues {
if err = issues[i].LoadAttributes(); err != nil { if err = issues[i].LoadAttributes(); err != nil {
c.Error(err, "load attributes") c.Error(err, "load attributes")
return return
} }
apiIssues[i] = issues[i].APIFormat() apiIssues[i] = toIssue(issues[i])
} }
c.SetLinkHeader(int(count), conf.UI.IssuePagingNum) c.SetLinkHeader(int(count), conf.UI.IssuePagingNum)
c.JSONSuccess(&apiIssues) c.JSONSuccess(&apiIssues)
} }
func ListUserIssues(c *context.APIContext) { func listUserIssues(c *context.APIContext) {
opts := database.IssuesOptions{ opts := database.IssuesOptions{
AssigneeID: c.User.ID, AssigneeID: c.User.ID,
Page: c.QueryInt("page"), Page: c.QueryInt("page"),
IsClosed: api.StateType(c.Query("state")) == api.STATE_CLOSED, IsClosed: types.IssueStateType(c.Query("state")) == types.IssueStateClosed,
} }
listIssues(c, &opts) queryIssues(c, &opts)
} }
func ListIssues(c *context.APIContext) { func listIssues(c *context.APIContext) {
opts := database.IssuesOptions{ opts := database.IssuesOptions{
RepoID: c.Repo.Repository.ID, RepoID: c.Repo.Repository.ID,
Page: c.QueryInt("page"), Page: c.QueryInt("page"),
IsClosed: api.StateType(c.Query("state")) == api.STATE_CLOSED, IsClosed: types.IssueStateType(c.Query("state")) == types.IssueStateClosed,
} }
listIssues(c, &opts) queryIssues(c, &opts)
} }
func GetIssue(c *context.APIContext) { func getIssue(c *context.APIContext) {
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get issue by index") c.NotFoundOrError(err, "get issue by index")
return return
} }
c.JSONSuccess(issue.APIFormat()) c.JSONSuccess(toIssue(issue))
} }
func CreateIssue(c *context.APIContext, form api.CreateIssueOption) { type createIssueRequest struct {
Title string `json:"title" binding:"Required"`
Body string `json:"body"`
Assignee string `json:"assignee"`
Milestone int64 `json:"milestone"`
Labels []int64 `json:"labels"`
Closed bool `json:"closed"`
}
func createIssue(c *context.APIContext, form createIssueRequest) {
issue := &database.Issue{ issue := &database.Issue{
RepoID: c.Repo.Repository.ID, RepoID: c.Repo.Repository.ID,
Title: form.Title, Title: form.Title,
@@ -114,10 +123,18 @@ func CreateIssue(c *context.APIContext, form api.CreateIssueOption) {
c.Error(err, "get issue by ID") c.Error(err, "get issue by ID")
return return
} }
c.JSON(http.StatusCreated, issue.APIFormat()) c.JSON(http.StatusCreated, toIssue(issue))
} }
func EditIssue(c *context.APIContext, form api.EditIssueOption) { type editIssueRequest struct {
Title string `json:"title"`
Body *string `json:"body"`
Assignee *string `json:"assignee"`
Milestone *int64 `json:"milestone"`
State *string `json:"state"`
}
func editIssue(c *context.APIContext, form editIssueRequest) {
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get issue by index") c.NotFoundOrError(err, "get issue by index")
@@ -173,7 +190,7 @@ func EditIssue(c *context.APIContext, form api.EditIssueOption) {
return return
} }
if form.State != nil { if form.State != nil {
if err = issue.ChangeStatus(c.User, c.Repo.Repository, api.STATE_CLOSED == api.StateType(*form.State)); err != nil { if err = issue.ChangeStatus(c.User, c.Repo.Repository, types.IssueStateClosed == types.IssueStateType(*form.State)); err != nil {
c.Error(err, "change status") c.Error(err, "change status")
return return
} }
@@ -185,5 +202,5 @@ func EditIssue(c *context.APIContext, form api.EditIssueOption) {
c.Error(err, "get issue by ID") c.Error(err, "get issue by ID")
return return
} }
c.JSON(http.StatusCreated, issue.APIFormat()) c.JSON(http.StatusCreated, toIssue(issue))
} }

View File

@@ -1,16 +1,15 @@
package repo package v1
import ( import (
"net/http" "net/http"
"time" "time"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/types"
) )
func ListIssueComments(c *context.APIContext) { func listIssueComments(c *context.APIContext) {
var since time.Time var since time.Time
if len(c.Query("since")) > 0 { if len(c.Query("since")) > 0 {
var err error var err error
@@ -34,14 +33,14 @@ func ListIssueComments(c *context.APIContext) {
return return
} }
apiComments := make([]*api.Comment, len(comments)) apiComments := make([]*types.IssueComment, len(comments))
for i := range comments { for i := range comments {
apiComments[i] = comments[i].APIFormat() apiComments[i] = toIssueComment(comments[i])
} }
c.JSONSuccess(&apiComments) c.JSONSuccess(&apiComments)
} }
func ListRepoIssueComments(c *context.APIContext) { func listRepoIssueComments(c *context.APIContext) {
var since time.Time var since time.Time
if len(c.Query("since")) > 0 { if len(c.Query("since")) > 0 {
var err error var err error
@@ -58,14 +57,18 @@ func ListRepoIssueComments(c *context.APIContext) {
return return
} }
apiComments := make([]*api.Comment, len(comments)) apiComments := make([]*types.IssueComment, len(comments))
for i := range comments { for i := range comments {
apiComments[i] = comments[i].APIFormat() apiComments[i] = toIssueComment(comments[i])
} }
c.JSONSuccess(&apiComments) c.JSONSuccess(&apiComments)
} }
func CreateIssueComment(c *context.APIContext, form api.CreateIssueCommentOption) { type createIssueCommentRequest struct {
Body string `json:"body" binding:"Required"`
}
func createIssueComment(c *context.APIContext, form createIssueCommentRequest) {
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil { if err != nil {
c.Error(err, "get issue by index") c.Error(err, "get issue by index")
@@ -78,10 +81,14 @@ func CreateIssueComment(c *context.APIContext, form api.CreateIssueCommentOption
return return
} }
c.JSON(http.StatusCreated, comment.APIFormat()) c.JSON(http.StatusCreated, toIssueComment(comment))
} }
func EditIssueComment(c *context.APIContext, form api.EditIssueCommentOption) { type editIssueCommentRequest struct {
Body string `json:"body" binding:"Required"`
}
func editIssueComment(c *context.APIContext, form editIssueCommentRequest) {
comment, err := database.GetCommentByID(c.ParamsInt64(":id")) comment, err := database.GetCommentByID(c.ParamsInt64(":id"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get comment by ID") c.NotFoundOrError(err, "get comment by ID")
@@ -113,10 +120,10 @@ func EditIssueComment(c *context.APIContext, form api.EditIssueCommentOption) {
c.Error(err, "update comment") c.Error(err, "update comment")
return return
} }
c.JSONSuccess(comment.APIFormat()) c.JSONSuccess(toIssueComment(comment))
} }
func DeleteIssueComment(c *context.APIContext) { func deleteIssueComment(c *context.APIContext) {
comment, err := database.GetCommentByID(c.ParamsInt64(":id")) comment, err := database.GetCommentByID(c.ParamsInt64(":id"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get comment by ID") c.NotFoundOrError(err, "get comment by ID")

View File

@@ -1,29 +1,32 @@
package repo package v1
import ( import (
"net/http" "net/http"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/types"
) )
func ListIssueLabels(c *context.APIContext) { func listIssueLabels(c *context.APIContext) {
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get issue by index") c.NotFoundOrError(err, "get issue by index")
return return
} }
apiLabels := make([]*api.Label, len(issue.Labels)) apiLabels := make([]*types.IssueLabel, len(issue.Labels))
for i := range issue.Labels { for i := range issue.Labels {
apiLabels[i] = issue.Labels[i].APIFormat() apiLabels[i] = toIssueLabel(issue.Labels[i])
} }
c.JSONSuccess(&apiLabels) c.JSONSuccess(&apiLabels)
} }
func AddIssueLabels(c *context.APIContext, form api.IssueLabelsOption) { type issueLabelsRequest struct {
Labels []int64 `json:"labels"`
}
func addIssueLabels(c *context.APIContext, form issueLabelsRequest) {
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get issue by index") c.NotFoundOrError(err, "get issue by index")
@@ -47,14 +50,14 @@ func AddIssueLabels(c *context.APIContext, form api.IssueLabelsOption) {
return return
} }
apiLabels := make([]*api.Label, len(labels)) apiLabels := make([]*types.IssueLabel, len(labels))
for i := range labels { for i := range labels {
apiLabels[i] = issue.Labels[i].APIFormat() apiLabels[i] = toIssueLabel(issue.Labels[i])
} }
c.JSONSuccess(&apiLabels) c.JSONSuccess(&apiLabels)
} }
func DeleteIssueLabel(c *context.APIContext) { func deleteIssueLabel(c *context.APIContext) {
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get issue by index") c.NotFoundOrError(err, "get issue by index")
@@ -79,7 +82,7 @@ func DeleteIssueLabel(c *context.APIContext) {
c.NoContent() c.NoContent()
} }
func ReplaceIssueLabels(c *context.APIContext, form api.IssueLabelsOption) { func replaceIssueLabels(c *context.APIContext, form issueLabelsRequest) {
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get issue by index") c.NotFoundOrError(err, "get issue by index")
@@ -103,14 +106,14 @@ func ReplaceIssueLabels(c *context.APIContext, form api.IssueLabelsOption) {
return return
} }
apiLabels := make([]*api.Label, len(labels)) apiLabels := make([]*types.IssueLabel, len(labels))
for i := range labels { for i := range labels {
apiLabels[i] = issue.Labels[i].APIFormat() apiLabels[i] = toIssueLabel(issue.Labels[i])
} }
c.JSONSuccess(&apiLabels) c.JSONSuccess(&apiLabels)
} }
func ClearIssueLabels(c *context.APIContext) { func clearIssueLabels(c *context.APIContext) {
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index")) issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get issue by index") c.NotFoundOrError(err, "get issue by index")

View File

@@ -1,15 +1,14 @@
package repo package v1
import ( import (
"net/http" "net/http"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/convert" "gogs.io/gogs/internal/route/api/v1/types"
) )
func composeDeployKeysAPILink(repoPath string) string { func composeDeployKeysAPILink(repoPath string) string {
@@ -17,7 +16,7 @@ func composeDeployKeysAPILink(repoPath string) string {
} }
// https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#list-deploy-keys // https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#list-deploy-keys
func ListDeployKeys(c *context.APIContext) { func listDeployKeys(c *context.APIContext) {
keys, err := database.ListDeployKeys(c.Repo.Repository.ID) keys, err := database.ListDeployKeys(c.Repo.Repository.ID)
if err != nil { if err != nil {
c.Error(err, "list deploy keys") c.Error(err, "list deploy keys")
@@ -25,20 +24,20 @@ func ListDeployKeys(c *context.APIContext) {
} }
apiLink := composeDeployKeysAPILink(c.Repo.Owner.Name + "/" + c.Repo.Repository.Name) apiLink := composeDeployKeysAPILink(c.Repo.Owner.Name + "/" + c.Repo.Repository.Name)
apiKeys := make([]*api.DeployKey, len(keys)) apiKeys := make([]*types.RepositoryDeployKey, len(keys))
for i := range keys { for i := range keys {
if err = keys[i].GetContent(); err != nil { if err = keys[i].GetContent(); err != nil {
c.Error(err, "get content") c.Error(err, "get content")
return return
} }
apiKeys[i] = convert.ToDeployKey(apiLink, keys[i]) apiKeys[i] = toDeployKey(apiLink, keys[i])
} }
c.JSONSuccess(&apiKeys) c.JSONSuccess(&apiKeys)
} }
// https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#get-a-deploy-key // https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#get-a-deploy-key
func GetDeployKey(c *context.APIContext) { func getDeployKey(c *context.APIContext) {
key, err := database.GetDeployKeyByID(c.ParamsInt64(":id")) key, err := database.GetDeployKeyByID(c.ParamsInt64(":id"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get deploy key by ID") c.NotFoundOrError(err, "get deploy key by ID")
@@ -56,10 +55,10 @@ func GetDeployKey(c *context.APIContext) {
} }
apiLink := composeDeployKeysAPILink(c.Repo.Owner.Name + "/" + c.Repo.Repository.Name) apiLink := composeDeployKeysAPILink(c.Repo.Owner.Name + "/" + c.Repo.Repository.Name)
c.JSONSuccess(convert.ToDeployKey(apiLink, key)) c.JSONSuccess(toDeployKey(apiLink, key))
} }
func HandleCheckKeyStringError(c *context.APIContext, err error) { func handleCheckKeyStringError(c *context.APIContext, err error) {
if database.IsErrKeyUnableVerify(err) { if database.IsErrKeyUnableVerify(err) {
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Unable to verify key content")) c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Unable to verify key content"))
} else { } else {
@@ -67,7 +66,7 @@ func HandleCheckKeyStringError(c *context.APIContext, err error) {
} }
} }
func HandleAddKeyError(c *context.APIContext, err error) { func handleAddKeyError(c *context.APIContext, err error) {
switch { switch {
case database.IsErrKeyAlreadyExist(err): case database.IsErrKeyAlreadyExist(err):
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Key content has been used as non-deploy key")) c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Key content has been used as non-deploy key"))
@@ -79,26 +78,31 @@ func HandleAddKeyError(c *context.APIContext, err error) {
} }
// https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#add-a-new-deploy-key // https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#add-a-new-deploy-key
func CreateDeployKey(c *context.APIContext, form api.CreateKeyOption) { type createDeployKeyRequest struct {
Title string `json:"title" binding:"Required"`
Key string `json:"key" binding:"Required"`
}
func createDeployKey(c *context.APIContext, form createDeployKeyRequest) {
content, err := database.CheckPublicKeyString(form.Key) content, err := database.CheckPublicKeyString(form.Key)
if err != nil { if err != nil {
HandleCheckKeyStringError(c, err) handleCheckKeyStringError(c, err)
return return
} }
key, err := database.AddDeployKey(c.Repo.Repository.ID, form.Title, content) key, err := database.AddDeployKey(c.Repo.Repository.ID, form.Title, content)
if err != nil { if err != nil {
HandleAddKeyError(c, err) handleAddKeyError(c, err)
return return
} }
key.Content = content key.Content = content
apiLink := composeDeployKeysAPILink(c.Repo.Owner.Name + "/" + c.Repo.Repository.Name) apiLink := composeDeployKeysAPILink(c.Repo.Owner.Name + "/" + c.Repo.Repository.Name)
c.JSON(http.StatusCreated, convert.ToDeployKey(apiLink, key)) c.JSON(http.StatusCreated, toDeployKey(apiLink, key))
} }
// https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#remove-a-deploy-key // https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#remove-a-deploy-key
func DeleteDeploykey(c *context.APIContext) { func deleteDeploykey(c *context.APIContext) {
key, err := database.GetDeployKeyByID(c.ParamsInt64(":id")) key, err := database.GetDeployKeyByID(c.ParamsInt64(":id"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get deploy key by ID") c.NotFoundOrError(err, "get deploy key by ID")

View File

@@ -1,30 +1,29 @@
package repo package v1
import ( import (
"net/http" "net/http"
"strconv" "strconv"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/types"
) )
func ListLabels(c *context.APIContext) { func listLabels(c *context.APIContext) {
labels, err := database.GetLabelsByRepoID(c.Repo.Repository.ID) labels, err := database.GetLabelsByRepoID(c.Repo.Repository.ID)
if err != nil { if err != nil {
c.Error(err, "get labels by repository ID") c.Error(err, "get labels by repository ID")
return return
} }
apiLabels := make([]*api.Label, len(labels)) apiLabels := make([]*types.IssueLabel, len(labels))
for i := range labels { for i := range labels {
apiLabels[i] = labels[i].APIFormat() apiLabels[i] = toIssueLabel(labels[i])
} }
c.JSONSuccess(&apiLabels) c.JSONSuccess(&apiLabels)
} }
func GetLabel(c *context.APIContext) { func getLabel(c *context.APIContext) {
var label *database.Label var label *database.Label
var err error var err error
idStr := c.Params(":id") idStr := c.Params(":id")
@@ -38,10 +37,15 @@ func GetLabel(c *context.APIContext) {
return return
} }
c.JSONSuccess(label.APIFormat()) c.JSONSuccess(toIssueLabel(label))
} }
func CreateLabel(c *context.APIContext, form api.CreateLabelOption) { type createLabelRequest struct {
Name string `json:"name" binding:"Required"`
Color string `json:"color" binding:"Required;Size(7)"`
}
func createLabel(c *context.APIContext, form createLabelRequest) {
label := &database.Label{ label := &database.Label{
Name: form.Name, Name: form.Name,
Color: form.Color, Color: form.Color,
@@ -51,10 +55,15 @@ func CreateLabel(c *context.APIContext, form api.CreateLabelOption) {
c.Error(err, "new labels") c.Error(err, "new labels")
return return
} }
c.JSON(http.StatusCreated, label.APIFormat()) c.JSON(http.StatusCreated, toIssueLabel(label))
} }
func EditLabel(c *context.APIContext, form api.EditLabelOption) { type editLabelRequest struct {
Name *string `json:"name"`
Color *string `json:"color"`
}
func editLabel(c *context.APIContext, form editLabelRequest) {
label, err := database.GetLabelOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")) label, err := database.GetLabelOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get label of repository by ID") c.NotFoundOrError(err, "get label of repository by ID")
@@ -71,10 +80,10 @@ func EditLabel(c *context.APIContext, form api.EditLabelOption) {
c.Error(err, "update label") c.Error(err, "update label")
return return
} }
c.JSONSuccess(label.APIFormat()) c.JSONSuccess(toIssueLabel(label))
} }
func DeleteLabel(c *context.APIContext) { func deleteLabel(c *context.APIContext) {
if err := database.DeleteLabel(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil { if err := database.DeleteLabel(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil {
c.Error(err, "delete label") c.Error(err, "delete label")
return return

View File

@@ -1,39 +1,44 @@
package repo package v1
import ( import (
"net/http" "net/http"
"time" "time"
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/route/api/v1/types"
) )
func ListMilestones(c *context.APIContext) { func listMilestones(c *context.APIContext) {
milestones, err := database.GetMilestonesByRepoID(c.Repo.Repository.ID) milestones, err := database.GetMilestonesByRepoID(c.Repo.Repository.ID)
if err != nil { if err != nil {
c.Error(err, "get milestones by repository ID") c.Error(err, "get milestones by repository ID")
return return
} }
apiMilestones := make([]*api.Milestone, len(milestones)) apiMilestones := make([]*types.IssueMilestone, len(milestones))
for i := range milestones { for i := range milestones {
apiMilestones[i] = milestones[i].APIFormat() apiMilestones[i] = toIssueMilestone(milestones[i])
} }
c.JSONSuccess(&apiMilestones) c.JSONSuccess(&apiMilestones)
} }
func GetMilestone(c *context.APIContext) { func getMilestone(c *context.APIContext) {
milestone, err := database.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id")) milestone, err := database.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get milestone by repository ID") c.NotFoundOrError(err, "get milestone by repository ID")
return return
} }
c.JSONSuccess(milestone.APIFormat()) c.JSONSuccess(toIssueMilestone(milestone))
} }
func CreateMilestone(c *context.APIContext, form api.CreateMilestoneOption) { type createMilestoneRequest struct {
Title string `json:"title"`
Description string `json:"description"`
Deadline *time.Time `json:"due_on"`
}
func createMilestone(c *context.APIContext, form createMilestoneRequest) {
if form.Deadline == nil { if form.Deadline == nil {
defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local) defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local)
form.Deadline = &defaultDeadline form.Deadline = &defaultDeadline
@@ -50,10 +55,17 @@ func CreateMilestone(c *context.APIContext, form api.CreateMilestoneOption) {
c.Error(err, "new milestone") c.Error(err, "new milestone")
return return
} }
c.JSON(http.StatusCreated, milestone.APIFormat()) c.JSON(http.StatusCreated, toIssueMilestone(milestone))
} }
func EditMilestone(c *context.APIContext, form api.EditMilestoneOption) { type editMilestoneRequest struct {
Title string `json:"title"`
Description *string `json:"description"`
State *string `json:"state"`
Deadline *time.Time `json:"due_on"`
}
func editMilestone(c *context.APIContext, form editMilestoneRequest) {
milestone, err := database.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id")) milestone, err := database.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get milestone by repository ID") c.NotFoundOrError(err, "get milestone by repository ID")
@@ -71,7 +83,7 @@ func EditMilestone(c *context.APIContext, form api.EditMilestoneOption) {
} }
if form.State != nil { if form.State != nil {
if err = milestone.ChangeStatus(api.STATE_CLOSED == api.StateType(*form.State)); err != nil { if err = milestone.ChangeStatus(types.IssueStateClosed == types.IssueStateType(*form.State)); err != nil {
c.Error(err, "change status") c.Error(err, "change status")
return return
} }
@@ -80,10 +92,10 @@ func EditMilestone(c *context.APIContext, form api.EditMilestoneOption) {
return return
} }
c.JSONSuccess(milestone.APIFormat()) c.JSONSuccess(toIssueMilestone(milestone))
} }
func DeleteMilestone(c *context.APIContext) { func deleteMilestone(c *context.APIContext) {
if err := database.DeleteMilestoneOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil { if err := database.DeleteMilestoneOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil {
c.Error(err, "delete milestone of repository by ID") c.Error(err, "delete milestone of repository by ID")
return return

View File

@@ -1,25 +1,24 @@
package repo package v1
import ( import (
"net/http" "net/http"
"path" "path"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/database" "gogs.io/gogs/internal/database"
"gogs.io/gogs/internal/form" "gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/route/api/v1/convert" "gogs.io/gogs/internal/route/api/v1/types"
) )
func Search(c *context.APIContext) { func searchRepos(c *context.APIContext) {
opts := &database.SearchRepoOptions{ opts := &database.SearchRepoOptions{
Keyword: path.Base(c.Query("q")), Keyword: path.Base(c.Query("q")),
OwnerID: c.QueryInt64("uid"), OwnerID: c.QueryInt64("uid"),
PageSize: convert.ToCorrectPageSize(c.QueryInt("limit")), PageSize: toAllowedPageSize(c.QueryInt("limit")),
Page: c.QueryInt("page"), Page: c.QueryInt("page"),
} }
@@ -60,9 +59,9 @@ func Search(c *context.APIContext) {
return return
} }
results := make([]*api.Repository, len(repos)) results := make([]*types.Repository, len(repos))
for i := range repos { for i := range repos {
results[i] = repos[i].APIFormatLegacy(nil) results[i] = toRepository(repos[i], nil)
} }
c.SetLinkHeader(int(count), opts.PageSize) c.SetLinkHeader(int(count), opts.PageSize)
@@ -72,7 +71,7 @@ func Search(c *context.APIContext) {
}) })
} }
func listUserRepositories(c *context.APIContext, username string) { func listReposOfUser(c *context.APIContext, username string) {
user, err := database.Handle.Users().GetByUsername(c.Req.Context(), username) user, err := database.Handle.Users().GetByUsername(c.Req.Context(), username)
if err != nil { if err != nil {
c.NotFoundOrError(err, "get user by name") c.NotFoundOrError(err, "get user by name")
@@ -104,9 +103,9 @@ func listUserRepositories(c *context.APIContext, username string) {
// Early return for querying other user's repositories // Early return for querying other user's repositories
if c.User.ID != user.ID { if c.User.ID != user.ID {
repos := make([]*api.Repository, len(ownRepos)) repos := make([]*types.Repository, len(ownRepos))
for i := range ownRepos { for i := range ownRepos {
repos[i] = ownRepos[i].APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true}) repos[i] = toRepository(ownRepos[i], &types.RepositoryPermission{Admin: true, Push: true, Pull: true})
} }
c.JSONSuccess(&repos) c.JSONSuccess(&repos)
return return
@@ -128,14 +127,14 @@ func listUserRepositories(c *context.APIContext, username string) {
} }
numOwnRepos := len(ownRepos) numOwnRepos := len(ownRepos)
repos := make([]*api.Repository, 0, numOwnRepos+len(accessibleReposWithAccessMode)) repos := make([]*types.Repository, 0, numOwnRepos+len(accessibleReposWithAccessMode))
for _, r := range ownRepos { for _, r := range ownRepos {
repos = append(repos, r.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true})) repos = append(repos, toRepository(r, &types.RepositoryPermission{Admin: true, Push: true, Pull: true}))
} }
for repo, access := range accessibleReposWithAccessMode { for repo, access := range accessibleReposWithAccessMode {
repos = append(repos, repos = append(repos,
repo.APIFormatLegacy(&api.Permission{ toRepository(repo, &types.RepositoryPermission{
Admin: access >= database.AccessModeAdmin, Admin: access >= database.AccessModeAdmin,
Push: access >= database.AccessModeWrite, Push: access >= database.AccessModeWrite,
Pull: true, Pull: true,
@@ -146,19 +145,29 @@ func listUserRepositories(c *context.APIContext, username string) {
c.JSONSuccess(&repos) c.JSONSuccess(&repos)
} }
func ListMyRepos(c *context.APIContext) { func listMyRepos(c *context.APIContext) {
listUserRepositories(c, c.User.Name) listReposOfUser(c, c.User.Name)
} }
func ListUserRepositories(c *context.APIContext) { func listUserRepositories(c *context.APIContext) {
listUserRepositories(c, c.Params(":username")) listReposOfUser(c, c.Params(":username"))
} }
func ListOrgRepositories(c *context.APIContext) { func listOrgRepositories(c *context.APIContext) {
listUserRepositories(c, c.Params(":org")) listReposOfUser(c, c.Params(":org"))
} }
func CreateUserRepo(c *context.APIContext, owner *database.User, opt api.CreateRepoOption) { type createRepoRequest struct {
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
Description string `json:"description" binding:"MaxSize(255)"`
Private bool `json:"private"`
AutoInit bool `json:"auto_init"`
Gitignores string `json:"gitignores"`
License string `json:"license"`
Readme string `json:"readme"`
}
func createUserRepo(c *context.APIContext, owner *database.User, opt createRepoRequest) {
repo, err := database.CreateRepository(c.User, owner, database.CreateRepoOptionsLegacy{ repo, err := database.CreateRepository(c.User, owner, database.CreateRepoOptionsLegacy{
Name: opt.Name, Name: opt.Name,
Description: opt.Description, Description: opt.Description,
@@ -183,19 +192,19 @@ func CreateUserRepo(c *context.APIContext, owner *database.User, opt api.CreateR
return return
} }
c.JSON(201, repo.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true})) c.JSON(201, toRepository(repo, &types.RepositoryPermission{Admin: true, Push: true, Pull: true}))
} }
func Create(c *context.APIContext, opt api.CreateRepoOption) { func createRepo(c *context.APIContext, opt createRepoRequest) {
// Shouldn't reach this condition, but just in case. // Shouldn't reach this condition, but just in case.
if c.User.IsOrganization() { if c.User.IsOrganization() {
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Not allowed to create repository for organization.")) c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Not allowed to create repository for organization."))
return return
} }
CreateUserRepo(c, c.User, opt) createUserRepo(c, c.User, opt)
} }
func CreateOrgRepo(c *context.APIContext, opt api.CreateRepoOption) { func createOrgRepo(c *context.APIContext, opt createRepoRequest) {
org, err := database.GetOrgByName(c.Params(":org")) org, err := database.GetOrgByName(c.Params(":org"))
if err != nil { if err != nil {
c.NotFoundOrError(err, "get organization by name") c.NotFoundOrError(err, "get organization by name")
@@ -206,10 +215,10 @@ func CreateOrgRepo(c *context.APIContext, opt api.CreateRepoOption) {
c.ErrorStatus(http.StatusForbidden, errors.New("Given user is not owner of organization.")) c.ErrorStatus(http.StatusForbidden, errors.New("Given user is not owner of organization."))
return return
} }
CreateUserRepo(c, org, opt) createUserRepo(c, org, opt)
} }
func Migrate(c *context.APIContext, f form.MigrateRepo) { func migrate(c *context.APIContext, f form.MigrateRepo) {
ctxUser := c.User ctxUser := c.User
// Not equal means context user is an organization, // Not equal means context user is an organization,
// or is another user/organization if current user is admin. // or is another user/organization if current user is admin.
@@ -287,7 +296,7 @@ func Migrate(c *context.APIContext, f form.MigrateRepo) {
} }
log.Trace("Repository migrated: %s/%s", ctxUser.Name, f.RepoName) log.Trace("Repository migrated: %s/%s", ctxUser.Name, f.RepoName)
c.JSON(201, repo.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true})) c.JSON(201, toRepository(repo, &types.RepositoryPermission{Admin: true, Push: true, Pull: true}))
} }
// FIXME: inject in the handler chain // FIXME: inject in the handler chain
@@ -311,20 +320,20 @@ func parseOwnerAndRepo(c *context.APIContext) (*database.User, *database.Reposit
return owner, repo return owner, repo
} }
func Get(c *context.APIContext) { func getRepo(c *context.APIContext) {
_, repo := parseOwnerAndRepo(c) _, repo := parseOwnerAndRepo(c)
if c.Written() { if c.Written() {
return return
} }
c.JSONSuccess(repo.APIFormatLegacy(&api.Permission{ c.JSONSuccess(toRepository(repo, &types.RepositoryPermission{
Admin: c.Repo.IsAdmin(), Admin: c.Repo.IsAdmin(),
Push: c.Repo.IsWriter(), Push: c.Repo.IsWriter(),
Pull: true, Pull: true,
})) }))
} }
func Delete(c *context.APIContext) { func deleteRepo(c *context.APIContext) {
owner, repo := parseOwnerAndRepo(c) owner, repo := parseOwnerAndRepo(c)
if c.Written() { if c.Written() {
return return
@@ -344,14 +353,14 @@ func Delete(c *context.APIContext) {
c.NoContent() c.NoContent()
} }
func ListForks(c *context.APIContext) { func listForks(c *context.APIContext) {
forks, err := c.Repo.Repository.GetForks() forks, err := c.Repo.Repository.GetForks()
if err != nil { if err != nil {
c.Error(err, "get forks") c.Error(err, "get forks")
return return
} }
apiForks := make([]*api.Repository, len(forks)) apiForks := make([]*types.Repository, len(forks))
for i := range forks { for i := range forks {
if err := forks[i].GetOwner(); err != nil { if err := forks[i].GetOwner(); err != nil {
c.Error(err, "get owner") c.Error(err, "get owner")
@@ -368,8 +377,8 @@ func ListForks(c *context.APIContext) {
}, },
) )
apiForks[i] = forks[i].APIFormatLegacy( apiForks[i] = toRepository(forks[i],
&api.Permission{ &types.RepositoryPermission{
Admin: accessMode >= database.AccessModeAdmin, Admin: accessMode >= database.AccessModeAdmin,
Push: accessMode >= database.AccessModeWrite, Push: accessMode >= database.AccessModeWrite,
Pull: true, Pull: true,
@@ -380,7 +389,15 @@ func ListForks(c *context.APIContext) {
c.JSONSuccess(&apiForks) c.JSONSuccess(&apiForks)
} }
func IssueTracker(c *context.APIContext, form api.EditIssueTrackerOption) { type editIssueTrackerRequest struct {
EnableIssues *bool `json:"enable_issues"`
EnableExternalTracker *bool `json:"enable_external_tracker"`
ExternalTrackerURL *string `json:"external_tracker_url"`
TrackerURLFormat *string `json:"tracker_url_format"`
TrackerIssueStyle *string `json:"tracker_issue_style"`
}
func issueTracker(c *context.APIContext, form editIssueTrackerRequest) {
_, repo := parseOwnerAndRepo(c) _, repo := parseOwnerAndRepo(c)
if c.Written() { if c.Written() {
return return
@@ -410,7 +427,14 @@ func IssueTracker(c *context.APIContext, form api.EditIssueTrackerOption) {
c.NoContent() c.NoContent()
} }
func Wiki(c *context.APIContext, form api.EditWikiOption) { type editWikiRequest struct {
EnableWiki *bool `json:"enable_wiki"`
AllowPublicWiki *bool `json:"allow_public_wiki"`
EnableExternalWiki *bool `json:"enable_external_wiki"`
ExternalWikiURL *string `json:"external_wiki_url"`
}
func wiki(c *context.APIContext, form editWikiRequest) {
_, repo := parseOwnerAndRepo(c) _, repo := parseOwnerAndRepo(c)
if c.Written() { if c.Written() {
return return
@@ -436,7 +460,7 @@ func Wiki(c *context.APIContext, form api.EditWikiOption) {
c.NoContent() c.NoContent()
} }
func MirrorSync(c *context.APIContext) { func mirrorSync(c *context.APIContext) {
_, repo := parseOwnerAndRepo(c) _, repo := parseOwnerAndRepo(c)
if c.Written() { if c.Written() {
return return
@@ -449,14 +473,14 @@ func MirrorSync(c *context.APIContext) {
c.Status(http.StatusAccepted) c.Status(http.StatusAccepted)
} }
func Releases(c *context.APIContext) { func releases(c *context.APIContext) {
_, repo := parseOwnerAndRepo(c) _, repo := parseOwnerAndRepo(c)
releases, err := database.GetReleasesByRepoID(repo.ID) releases, err := database.GetReleasesByRepoID(repo.ID)
if err != nil { if err != nil {
c.Error(err, "get releases by repository ID") c.Error(err, "get releases by repository ID")
return return
} }
apiReleases := make([]*api.Release, 0, len(releases)) apiReleases := make([]*types.RepositoryRelease, 0, len(releases))
for _, r := range releases { for _, r := range releases {
publisher, err := database.Handle.Users().GetByID(c.Req.Context(), r.PublisherID) publisher, err := database.Handle.Users().GetByID(c.Req.Context(), r.PublisherID)
if err != nil { if err != nil {
@@ -466,7 +490,7 @@ func Releases(c *context.APIContext) {
r.Publisher = publisher r.Publisher = publisher
} }
for _, r := range releases { for _, r := range releases {
apiReleases = append(apiReleases, r.APIFormat()) apiReleases = append(apiReleases, toRelease(r))
} }
c.JSONSuccess(&apiReleases) c.JSONSuccess(&apiReleases)

View File

@@ -1,25 +1,24 @@
package repo package v1
import ( import (
"gogs.io/gogs/internal/context" "gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/route/api/v1/convert"
) )
func ListTags(c *context.APIContext) { func listTags(c *context.APIContext) {
tags, err := c.Repo.Repository.GetTags() tags, err := c.Repo.Repository.GetTags()
if err != nil { if err != nil {
c.Error(err, "get tags") c.Error(err, "get tags")
return return
} }
apiTags := make([]*convert.Tag, len(tags)) apiTags := make([]*tag, len(tags))
for i := range tags { for i := range tags {
commit, err := tags[i].GetCommit() commit, err := tags[i].GetCommit()
if err != nil { if err != nil {
c.Error(err, "get commit") c.Error(err, "get commit")
return return
} }
apiTags[i] = convert.ToTag(tags[i], commit) apiTags[i] = toTag(tags[i], commit)
} }
c.JSONSuccess(&apiTags) c.JSONSuccess(&apiTags)

View File

@@ -1,4 +1,4 @@
package repo package v1
import ( import (
"fmt" "fmt"
@@ -9,7 +9,7 @@ import (
"gogs.io/gogs/internal/gitutil" "gogs.io/gogs/internal/gitutil"
) )
func GetRepoGitTree(c *context.APIContext) { func getRepoGitTree(c *context.APIContext) {
gitRepo, err := git.Open(c.Repo.Repository.RepoPath()) gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
if err != nil { if err != nil {
c.Error(err, "open repository") c.Error(err, "open repository")

View File

@@ -0,0 +1,8 @@
package v1
import "gogs.io/gogs/internal/route/api/v1/types"
type tag struct {
Name string `json:"name"`
Commit *types.WebhookPayloadCommit `json:"commit"`
}

View File

@@ -0,0 +1,29 @@
package types
type CommitMeta struct {
URL string `json:"url"`
SHA string `json:"sha"`
}
type CommitUser struct {
Name string `json:"name"`
Email string `json:"email"`
Date string `json:"date"`
}
type RepoCommit struct {
URL string `json:"url"`
Author *CommitUser `json:"author"`
Committer *CommitUser `json:"committer"`
Message string `json:"message"`
Tree *CommitMeta `json:"tree"`
}
type Commit struct {
*CommitMeta
HTMLURL string `json:"html_url"`
RepoCommit *RepoCommit `json:"commit"`
Author *User `json:"author"`
Committer *User `json:"committer"`
Parents []*CommitMeta `json:"parents"`
}

Some files were not shown because too many files have changed in this diff Show More