mirror of
https://github.com/gogs/gogs.git
synced 2026-05-06 11:36:12 +02:00
Compare commits
4 Commits
release-ar
...
migrate/go
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
997011bfb2 | ||
|
|
60f62b6583 | ||
|
|
3b7e331191 | ||
|
|
f9b4c5a3ff |
@@ -1,13 +0,0 @@
|
||||
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.
|
||||
6
.github/CONTRIBUTING.md
vendored
6
.github/CONTRIBUTING.md
vendored
@@ -29,7 +29,7 @@ In addition to the general guides with open source contributions, you would also
|
||||
|
||||
### 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/asking/troubleshooting) and [FAQs](https://gogs.io/asking/faq) pages.
|
||||
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.
|
||||
|
||||
### 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
|
||||
|
||||
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. 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. Docker compose files.
|
||||
|
||||
### Coding guidelines
|
||||
|
||||
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. Please read the Sourcegraph's [Go style guide](https://docs.sourcegraph.com/dev/background-information/languages/go).
|
||||
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!
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -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
|
||||
|
||||
steps:
|
||||
- name: Check out repository
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
|
||||
12
.github/workflows/docker.yml
vendored
12
.github/workflows/docker.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
@@ -212,7 +212,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
@@ -274,7 +274,7 @@ jobs:
|
||||
echo "TAGS<<EOF" >> $GITHUB_ENV
|
||||
echo "$TAGS" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
@@ -343,7 +343,7 @@ jobs:
|
||||
echo "TAGS<<EOF" >> $GITHUB_ENV
|
||||
echo "$TAGS" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
|
||||
20
.github/workflows/go.yml
vendored
20
.github/workflows/go.yml
vendored
@@ -29,12 +29,12 @@ jobs:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: 1.26.x
|
||||
go-version: 1.25.x
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0
|
||||
with:
|
||||
@@ -61,11 +61,11 @@ jobs:
|
||||
name: Test
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.26.x ]
|
||||
go-version: [ 1.25.x ]
|
||||
platform: [ ubuntu-latest, macos-latest ]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
@@ -89,11 +89,11 @@ jobs:
|
||||
name: Test Windows
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.26.x ]
|
||||
go-version: [ 1.25.x ]
|
||||
platform: [ windows-latest ]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
name: Postgres
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.26.x ]
|
||||
go-version: [ 1.25.x ]
|
||||
platform: [ ubuntu-latest ]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
services:
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
@@ -151,13 +151,13 @@ jobs:
|
||||
name: MySQL
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.26.x ]
|
||||
go-version: [ 1.25.x ]
|
||||
platform: [ ubuntu-22.04 ] # Use the lowest version possible for backwards compatibility
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Start MySQL server
|
||||
run: sudo systemctl start mysql
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
|
||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -40,12 +40,12 @@ jobs:
|
||||
- {goos: windows, goarch: arm64, suffix: "_mws", tags: minwinsvc}
|
||||
- {goos: windows, goarch: "386", suffix: "_mws", tags: minwinsvc}
|
||||
steps:
|
||||
- name: Check out code
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Set up Go
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: 1.26.x
|
||||
go-version: 1.25.x
|
||||
- name: Determine version
|
||||
id: version
|
||||
run: |
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
BINARY_NAME="gogs.exe"
|
||||
fi
|
||||
cp "$BINARY_NAME" dist/gogs/
|
||||
cp LICENSE README.md dist/gogs/
|
||||
cp LICENSE README.md README_ZH.md dist/gogs/
|
||||
cp -r scripts dist/gogs/
|
||||
- name: Create archives
|
||||
working-directory: dist
|
||||
@@ -112,14 +112,14 @@ jobs:
|
||||
if [ "${{ github.event_name }}" != "release" ]; then
|
||||
git tag -f "$RELEASE_TAG"
|
||||
git push origin "$RELEASE_TAG" --force || true
|
||||
|
||||
|
||||
RELEASE_TITLE="Release Archive Testing"
|
||||
RELEASE_NOTES="Automated testing release for workflow development."
|
||||
if [ "$RELEASE_TAG" = "latest-commit-build" ]; then
|
||||
RELEASE_TITLE="Latest Commit Build"
|
||||
RELEASE_NOTES="Automated build from the latest commit on main branch. This release is updated automatically with every push to main."
|
||||
fi
|
||||
|
||||
|
||||
gh release view "$RELEASE_TAG" || gh release create "$RELEASE_TAG" --title "$RELEASE_TITLE" --notes "$RELEASE_NOTES" --prerelease
|
||||
fi
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ 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.
|
||||
- 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
|
||||
|
||||
@@ -29,5 +28,5 @@ This applies to all texts, including but not limited to UI, documentation, code
|
||||
## Source code control
|
||||
|
||||
- When pushing changes to a pull request from a fork, use SSH address and do not add remote.
|
||||
- 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.
|
||||
- Never amend commits unless being explicitly asked to do so.
|
||||
- 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.
|
||||
- Do not amend commits unless being explicitly asked to do so.
|
||||
|
||||
@@ -4,16 +4,9 @@ All notable changes to Gogs are documented in this file.
|
||||
|
||||
## 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
|
||||
|
||||
- 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 `[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
|
||||
|
||||
|
||||
19
README.md
19
README.md
@@ -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 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)!
|
||||
- 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/advancing/localization).
|
||||
- Having trouble? Help yourself with [troubleshooting](https://gogs.io/docs/intro/troubleshooting.html) 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).
|
||||
- Ready to get hands dirty? Read our [contributing guide](.github/CONTRIBUTING.md).
|
||||
- Hmm... What about APIs? We have experimental support with [documentation](https://gogs.io/api-reference).
|
||||
- Hmm... What about APIs? We have experimental support with [documentation](https://github.com/gogs/docs-api).
|
||||
|
||||
## 💌 Features
|
||||
|
||||
@@ -47,7 +47,15 @@ The Gogs (`/gɑgz/`) project aims to build a simple, stable and extensible self-
|
||||
|
||||
## 📜 Installation
|
||||
|
||||
Please follow [the guide in our documentation](https://gogs.io/getting-started/installation).
|
||||
Make sure you install the [prerequisites](https://gogs.io/docs/installation) first.
|
||||
|
||||
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
|
||||
|
||||
@@ -86,8 +94,7 @@ Please follow [the guide in our documentation](https://gogs.io/getting-started/i
|
||||
Other acknowledgments:
|
||||
|
||||
- Thanks [Egon Elbre](https://twitter.com/egonelbre) for designing the original version of the logo.
|
||||
- Thanks [Mintlify](https://mintlify.com) for sponsoring open source documentation plan.
|
||||
- Thanks [Crowdin](https://crowdin.com) for sponsoring open source translation plan.
|
||||
- Thanks [Crowdin](https://crowdin.com/project/gogs) for sponsoring open source translation plan.
|
||||
- Thanks [Buildkite](https://buildkite.com) for sponsoring open source CI/CD plan.
|
||||
|
||||
## 👋 Contributors
|
||||
|
||||
102
README_ZH.md
Normal file
102
README_ZH.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# 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) 文件中。
|
||||
@@ -10,6 +10,8 @@ tasks:
|
||||
web:
|
||||
desc: Build the binary and start the web server
|
||||
deps: [build]
|
||||
env:
|
||||
GOGS_WORK_DIR: '{{.ROOT_DIR}}'
|
||||
cmds:
|
||||
- .bin/gogs web
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/database"
|
||||
@@ -19,15 +19,15 @@ var (
|
||||
Usage: "Perform admin operations on command line",
|
||||
Description: `Allow using internal logic of Gogs without hacking into the source code
|
||||
to make automatic initialization process more smoothly`,
|
||||
Commands: []*cli.Command{
|
||||
&subcmdCreateUser,
|
||||
&subcmdDeleteInactivateUsers,
|
||||
&subcmdDeleteRepositoryArchives,
|
||||
&subcmdDeleteMissingRepositories,
|
||||
&subcmdGitGcRepos,
|
||||
&subcmdRewriteAuthorizedKeys,
|
||||
&subcmdSyncRepositoryHooks,
|
||||
&subcmdReinitMissingRepositories,
|
||||
Subcommands: []cli.Command{
|
||||
subcmdCreateUser,
|
||||
subcmdDeleteInactivateUsers,
|
||||
subcmdDeleteRepositoryArchives,
|
||||
subcmdDeleteMissingRepositories,
|
||||
subcmdGitGcRepos,
|
||||
subcmdRewriteAuthorizedKeys,
|
||||
subcmdSyncRepositoryHooks,
|
||||
subcmdReinitMissingRepositories,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -129,16 +129,16 @@ to make automatic initialization process more smoothly`,
|
||||
}
|
||||
)
|
||||
|
||||
func runCreateUser(ctx context.Context, cmd *cli.Command) error {
|
||||
if !cmd.IsSet("name") {
|
||||
func runCreateUser(c *cli.Context) error {
|
||||
if !c.IsSet("name") {
|
||||
return errors.New("Username is not specified")
|
||||
} else if !cmd.IsSet("password") {
|
||||
} else if !c.IsSet("password") {
|
||||
return errors.New("Password is not specified")
|
||||
} else if !cmd.IsSet("email") {
|
||||
} else if !c.IsSet("email") {
|
||||
return errors.New("Email is not specified")
|
||||
}
|
||||
|
||||
err := conf.Init(configFromLineage(cmd))
|
||||
err := conf.Init(c.String("config"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "init configuration")
|
||||
}
|
||||
@@ -149,13 +149,13 @@ func runCreateUser(ctx context.Context, cmd *cli.Command) error {
|
||||
}
|
||||
|
||||
user, err := database.Handle.Users().Create(
|
||||
ctx,
|
||||
cmd.String("name"),
|
||||
cmd.String("email"),
|
||||
context.Background(),
|
||||
c.String("name"),
|
||||
c.String("email"),
|
||||
database.CreateUserOptions{
|
||||
Password: cmd.String("password"),
|
||||
Password: c.String("password"),
|
||||
Activated: true,
|
||||
Admin: cmd.Bool("admin"),
|
||||
Admin: c.Bool("admin"),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -166,9 +166,9 @@ func runCreateUser(ctx context.Context, cmd *cli.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func adminDashboardOperation(operation func() error, successMessage string) func(context.Context, *cli.Command) error {
|
||||
return func(_ context.Context, cmd *cli.Command) error {
|
||||
err := conf.Init(configFromLineage(cmd))
|
||||
func adminDashboardOperation(operation func() error, successMessage string) func(*cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
err := conf.Init(c.String("config"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "init configuration")
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/unknwon/cae/zip"
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/ini.v1"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
@@ -44,10 +44,10 @@ const (
|
||||
archiveRootDir = "gogs-backup"
|
||||
)
|
||||
|
||||
func runBackup(ctx context.Context, cmd *cli.Command) error {
|
||||
zip.Verbose = cmd.Bool("verbose")
|
||||
func runBackup(c *cli.Context) error {
|
||||
zip.Verbose = c.Bool("verbose")
|
||||
|
||||
err := conf.Init(configFromLineage(cmd))
|
||||
err := conf.Init(c.String("config"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "init configuration")
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func runBackup(ctx context.Context, cmd *cli.Command) error {
|
||||
return errors.Wrap(err, "set engine")
|
||||
}
|
||||
|
||||
tmpDir := cmd.String("tempdir")
|
||||
tmpDir := c.String("tempdir")
|
||||
if !osutil.Exist(tmpDir) {
|
||||
log.Fatal("'--tempdir' does not exist: %s", tmpDir)
|
||||
}
|
||||
@@ -78,7 +78,7 @@ func runBackup(ctx context.Context, cmd *cli.Command) error {
|
||||
log.Fatal("Failed to save metadata '%s': %v", metaFile, err)
|
||||
}
|
||||
|
||||
archiveName := filepath.Join(cmd.String("target"), cmd.String("archive-name"))
|
||||
archiveName := filepath.Join(c.String("target"), c.String("archive-name"))
|
||||
log.Info("Packing backup files to: %s", archiveName)
|
||||
|
||||
z, err := zip.Create(archiveName)
|
||||
@@ -91,14 +91,14 @@ func runBackup(ctx context.Context, cmd *cli.Command) error {
|
||||
|
||||
// Database
|
||||
dbDir := filepath.Join(rootDir, "db")
|
||||
if err = database.DumpDatabase(ctx, conn, dbDir, cmd.Bool("verbose")); err != nil {
|
||||
if err = database.DumpDatabase(context.Background(), conn, dbDir, c.Bool("verbose")); err != nil {
|
||||
log.Fatal("Failed to dump database: %v", err)
|
||||
}
|
||||
if err = z.AddDir(archiveRootDir+"/db", dbDir); err != nil {
|
||||
log.Fatal("Failed to include 'db': %v", err)
|
||||
}
|
||||
|
||||
if !cmd.Bool("database-only") {
|
||||
if !c.Bool("database-only") {
|
||||
// Custom files
|
||||
err = addCustomDirToBackup(z)
|
||||
if err != nil {
|
||||
@@ -119,10 +119,10 @@ func runBackup(ctx context.Context, cmd *cli.Command) error {
|
||||
}
|
||||
|
||||
// Repositories
|
||||
if !cmd.Bool("exclude-repos") && !cmd.Bool("database-only") {
|
||||
if !c.Bool("exclude-repos") && !c.Bool("database-only") {
|
||||
reposDump := filepath.Join(rootDir, "repositories.zip")
|
||||
log.Info("Dumping repositories in %q", conf.Repository.Root)
|
||||
if cmd.Bool("exclude-mirror-repos") {
|
||||
if c.Bool("exclude-mirror-repos") {
|
||||
repos, err := database.GetNonMirrorRepositories()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to get non-mirror repositories: %v", err)
|
||||
|
||||
@@ -1,43 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func stringFlag(name, value, usage string) *cli.StringFlag {
|
||||
parts := strings.SplitN(name, ", ", 2)
|
||||
f := &cli.StringFlag{
|
||||
Name: parts[0],
|
||||
func stringFlag(name, value, usage string) cli.StringFlag {
|
||||
return cli.StringFlag{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Usage: usage,
|
||||
}
|
||||
if len(parts) > 1 {
|
||||
f.Aliases = []string{parts[1]}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// configFromLineage walks the command lineage to find the --config flag value.
|
||||
// This is needed because subcommands may not directly see flags set on parent commands.
|
||||
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],
|
||||
func boolFlag(name, usage string) cli.BoolFlag {
|
||||
return cli.BoolFlag{
|
||||
Name: name,
|
||||
Usage: usage,
|
||||
}
|
||||
if len(parts) > 1 {
|
||||
f.Aliases = []string{parts[1]}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/url"
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/urfave/cli"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"github.com/gogs/git-module"
|
||||
@@ -33,10 +32,10 @@ var (
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "", "Custom configuration file path"),
|
||||
},
|
||||
Commands: []*cli.Command{
|
||||
&subcmdHookPreReceive,
|
||||
&subcmdHookUpadte,
|
||||
&subcmdHookPostReceive,
|
||||
Subcommands: []cli.Command{
|
||||
subcmdHookPreReceive,
|
||||
subcmdHookUpadte,
|
||||
subcmdHookPostReceive,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -60,11 +59,11 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func runHookPreReceive(_ context.Context, cmd *cli.Command) error {
|
||||
func runHookPreReceive(c *cli.Context) error {
|
||||
if os.Getenv("SSH_ORIGINAL_COMMAND") == "" {
|
||||
return nil
|
||||
}
|
||||
setup(cmd, "pre-receive.log", true)
|
||||
setup(c, "pre-receive.log", true)
|
||||
|
||||
isWiki := strings.Contains(os.Getenv(database.EnvRepoCustomHooksPath), ".wiki.git/")
|
||||
|
||||
@@ -153,13 +152,13 @@ func runHookPreReceive(_ context.Context, cmd *cli.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runHookUpdate(_ context.Context, cmd *cli.Command) error {
|
||||
func runHookUpdate(c *cli.Context) error {
|
||||
if os.Getenv("SSH_ORIGINAL_COMMAND") == "" {
|
||||
return nil
|
||||
}
|
||||
setup(cmd, "update.log", false)
|
||||
setup(c, "update.log", false)
|
||||
|
||||
args := cmd.Args().Slice()
|
||||
args := c.Args()
|
||||
if len(args) != 3 {
|
||||
fail("Arguments received are not equal to three", "Arguments received are not equal to three")
|
||||
} else if args[0] == "" {
|
||||
@@ -187,11 +186,11 @@ func runHookUpdate(_ context.Context, cmd *cli.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runHookPostReceive(_ context.Context, cmd *cli.Command) error {
|
||||
func runHookPostReceive(c *cli.Context) error {
|
||||
if os.Getenv("SSH_ORIGINAL_COMMAND") == "" {
|
||||
return nil
|
||||
}
|
||||
setup(cmd, "post-receive.log", true)
|
||||
setup(c, "post-receive.log", true)
|
||||
|
||||
// Post-receive hook does more than just gather Git information,
|
||||
// so we need to setup additional services for email notifications.
|
||||
|
||||
@@ -3,14 +3,13 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/osutil"
|
||||
@@ -22,8 +21,8 @@ var (
|
||||
Usage: "Import portable data as local Gogs data",
|
||||
Description: `Allow user import data from other Gogs installations to local instance
|
||||
without manually hacking the data files`,
|
||||
Commands: []*cli.Command{
|
||||
&subcmdImportLocale,
|
||||
Subcommands: []cli.Command{
|
||||
subcmdImportLocale,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -39,19 +38,19 @@ without manually hacking the data files`,
|
||||
}
|
||||
)
|
||||
|
||||
func runImportLocale(_ context.Context, cmd *cli.Command) error {
|
||||
if !cmd.IsSet("source") {
|
||||
func runImportLocale(c *cli.Context) error {
|
||||
if !c.IsSet("source") {
|
||||
return errors.New("source directory is not specified")
|
||||
} else if !cmd.IsSet("target") {
|
||||
} else if !c.IsSet("target") {
|
||||
return errors.New("target directory is not specified")
|
||||
}
|
||||
if !osutil.IsDir(cmd.String("source")) {
|
||||
return errors.Newf("source directory %q does not exist or is not a directory", cmd.String("source"))
|
||||
} else if !osutil.IsDir(cmd.String("target")) {
|
||||
return errors.Newf("target directory %q does not exist or is not a directory", cmd.String("target"))
|
||||
if !osutil.IsDir(c.String("source")) {
|
||||
return errors.Newf("source directory %q does not exist or is not a directory", c.String("source"))
|
||||
} else if !osutil.IsDir(c.String("target")) {
|
||||
return errors.Newf("target directory %q does not exist or is not a directory", c.String("target"))
|
||||
}
|
||||
|
||||
err := conf.Init(configFromLineage(cmd))
|
||||
err := conf.Init(c.String("config"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "init configuration")
|
||||
}
|
||||
@@ -65,8 +64,8 @@ func runImportLocale(_ context.Context, cmd *cli.Command) error {
|
||||
// Cut out en-US.
|
||||
for _, lang := range conf.I18n.Langs[1:] {
|
||||
name := fmt.Sprintf("locale_%s.ini", lang)
|
||||
source := filepath.Join(cmd.String("source"), name)
|
||||
target := filepath.Join(cmd.String("target"), name)
|
||||
source := filepath.Join(c.String("source"), name)
|
||||
target := filepath.Join(c.String("target"), name)
|
||||
if !osutil.IsFile(source) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/urfave/cli"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
@@ -16,21 +15,20 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
cmd := &cli.Command{
|
||||
Name: "Gogs",
|
||||
Usage: "A painless self-hosted Git service",
|
||||
Version: conf.App.Version,
|
||||
Commands: []*cli.Command{
|
||||
&webCommand,
|
||||
&servCommand,
|
||||
&hookCommand,
|
||||
&adminCommand,
|
||||
&importCommand,
|
||||
&backupCommand,
|
||||
&restoreCommand,
|
||||
},
|
||||
app := cli.NewApp()
|
||||
app.Name = "Gogs"
|
||||
app.Usage = "A painless self-hosted Git service"
|
||||
app.Version = conf.App.Version
|
||||
app.Commands = []cli.Command{
|
||||
webCommand,
|
||||
servCommand,
|
||||
hookCommand,
|
||||
adminCommand,
|
||||
importCommand,
|
||||
backupCommand,
|
||||
restoreCommand,
|
||||
}
|
||||
if err := cmd.Run(context.Background(), os.Args); err != nil {
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal("Failed to start application: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/unknwon/cae/zip"
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/ini.v1"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
@@ -42,10 +42,10 @@ be skipped and remain unchanged.`,
|
||||
// format that is able to import.
|
||||
var lastSupportedVersionOfFormat = map[int]string{}
|
||||
|
||||
func runRestore(ctx context.Context, cmd *cli.Command) error {
|
||||
zip.Verbose = cmd.Bool("verbose")
|
||||
func runRestore(c *cli.Context) error {
|
||||
zip.Verbose = c.Bool("verbose")
|
||||
|
||||
tmpDir := cmd.String("tempdir")
|
||||
tmpDir := c.String("tempdir")
|
||||
if !osutil.IsDir(tmpDir) {
|
||||
log.Fatal("'--tempdir' does not exist: %s", tmpDir)
|
||||
}
|
||||
@@ -58,8 +58,8 @@ func runRestore(ctx context.Context, cmd *cli.Command) error {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(archivePath) }()
|
||||
|
||||
log.Info("Restoring backup from: %s", cmd.String("from"))
|
||||
err = zip.ExtractTo(cmd.String("from"), tmpDir)
|
||||
log.Info("Restoring backup from: %s", c.String("from"))
|
||||
err = zip.ExtractTo(c.String("from"), tmpDir)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to extract backup archive: %v", err)
|
||||
}
|
||||
@@ -90,8 +90,8 @@ func runRestore(ctx context.Context, cmd *cli.Command) error {
|
||||
// Otherwise, it's optional to set config file flag.
|
||||
configFile := filepath.Join(archivePath, "custom", "conf", "app.ini")
|
||||
var customConf string
|
||||
if lineageConf := configFromLineage(cmd); lineageConf != "" {
|
||||
customConf = lineageConf
|
||||
if c.IsSet("config") {
|
||||
customConf = c.String("config")
|
||||
} else if !osutil.IsFile(configFile) {
|
||||
log.Fatal("'--config' is not specified and custom config file is not found in backup")
|
||||
} else {
|
||||
@@ -111,11 +111,11 @@ func runRestore(ctx context.Context, cmd *cli.Command) error {
|
||||
|
||||
// Database
|
||||
dbDir := path.Join(archivePath, "db")
|
||||
if err = database.ImportDatabase(ctx, conn, dbDir, cmd.Bool("verbose")); err != nil {
|
||||
if err = database.ImportDatabase(context.Background(), conn, dbDir, c.Bool("verbose")); err != nil {
|
||||
log.Fatal("Failed to import database: %v", err)
|
||||
}
|
||||
|
||||
if !cmd.Bool("database-only") {
|
||||
if !c.Bool("database-only") {
|
||||
// Custom files
|
||||
if osutil.IsDir(conf.CustomDir()) {
|
||||
if err = os.Rename(conf.CustomDir(), conf.CustomDir()+".bak"); err != nil {
|
||||
@@ -149,7 +149,7 @@ func runRestore(ctx context.Context, cmd *cli.Command) error {
|
||||
|
||||
// Repositories
|
||||
reposPath := filepath.Join(archivePath, "repositories.zip")
|
||||
if !cmd.Bool("exclude-repos") && !cmd.Bool("database-only") && osutil.IsFile(reposPath) {
|
||||
if !c.Bool("exclude-repos") && !c.Bool("database-only") && osutil.IsFile(reposPath) {
|
||||
if err := zip.ExtractTo(reposPath, filepath.Dir(conf.Repository.Root)); err != nil {
|
||||
log.Fatal("Failed to extract 'repositories.zip': %v", err)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/urfave/cli"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
@@ -48,10 +48,15 @@ func fail(userMessage, errMessage string, args ...any) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func setup(cmd *cli.Command, logFile string, connectDB bool) {
|
||||
func setup(c *cli.Context, logFile string, connectDB bool) {
|
||||
conf.HookMode = true
|
||||
|
||||
customConf := configFromLineage(cmd)
|
||||
var customConf string
|
||||
if c.IsSet("config") {
|
||||
customConf = c.String("config")
|
||||
} else if c.GlobalIsSet("config") {
|
||||
customConf = c.GlobalString("config")
|
||||
}
|
||||
|
||||
err := conf.Init(customConf)
|
||||
if err != nil {
|
||||
@@ -123,15 +128,16 @@ var allowedCommands = map[string]database.AccessMode{
|
||||
"git-receive-pack": database.AccessModeWrite,
|
||||
}
|
||||
|
||||
func runServ(ctx context.Context, cmd *cli.Command) error {
|
||||
setup(cmd, "serv.log", true)
|
||||
func runServ(c *cli.Context) error {
|
||||
ctx := context.Background()
|
||||
setup(c, "serv.log", true)
|
||||
|
||||
if conf.SSH.Disabled {
|
||||
println("Gogs: SSH has been disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
if cmd.Args().Len() < 1 {
|
||||
if len(c.Args()) < 1 {
|
||||
fail("Not enough arguments", "Not enough arguments")
|
||||
}
|
||||
|
||||
@@ -182,10 +188,10 @@ func runServ(ctx context.Context, cmd *cli.Command) error {
|
||||
// Allow anonymous (user is nil) clone for public repositories.
|
||||
var user *database.User
|
||||
|
||||
keyID, _ := strconv.ParseInt(strings.TrimPrefix(cmd.Args().Get(0), "key-"), 10, 64)
|
||||
keyID, _ := strconv.ParseInt(strings.TrimPrefix(c.Args()[0], "key-"), 10, 64)
|
||||
key, err := database.GetPublicKeyByID(keyID)
|
||||
if err != nil {
|
||||
fail("Invalid key ID", "Invalid key ID '%s': %v", cmd.Args().Get(0), err)
|
||||
fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err)
|
||||
}
|
||||
|
||||
if requestMode == database.AccessModeWrite || repo.IsPrivate {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -21,7 +20,7 @@ import (
|
||||
"github.com/go-macaron/session"
|
||||
"github.com/go-macaron/toolbox"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/macaron.v1"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
@@ -159,8 +158,8 @@ func newMacaron() *macaron.Macaron {
|
||||
return m
|
||||
}
|
||||
|
||||
func runWeb(_ stdctx.Context, cmd *cli.Command) error {
|
||||
err := route.GlobalInit(configFromLineage(cmd))
|
||||
func runWeb(c *cli.Context) error {
|
||||
err := route.GlobalInit(c.String("config"))
|
||||
if err != nil {
|
||||
log.Fatal("Failed to initialize application: %v", err)
|
||||
}
|
||||
@@ -698,10 +697,10 @@ func runWeb(_ stdctx.Context, cmd *cli.Command) error {
|
||||
m.NotFound(route.NotFound)
|
||||
|
||||
// Flag for port number in case first time run conflict.
|
||||
if cmd.IsSet("port") {
|
||||
conf.Server.URL.Host = strings.Replace(conf.Server.URL.Host, ":"+conf.Server.URL.Port(), ":"+cmd.String("port"), 1)
|
||||
if c.IsSet("port") {
|
||||
conf.Server.URL.Host = strings.Replace(conf.Server.URL.Host, ":"+conf.Server.URL.Port(), ":"+c.String("port"), 1)
|
||||
conf.Server.ExternalURL = conf.Server.URL.String()
|
||||
conf.Server.HTTPPort = cmd.String("port")
|
||||
conf.Server.HTTPPort = c.String("port")
|
||||
}
|
||||
|
||||
var listenAddr string
|
||||
|
||||
@@ -141,7 +141,8 @@ FILE_MAX_SIZE = 3
|
||||
MAX_FILES = 5
|
||||
|
||||
[database]
|
||||
; The database backend, either "postgres", "mysql" or "sqlite3".
|
||||
; The database backend, either "postgres", "mysql" "sqlite3" or "mssql".
|
||||
; You can connect to TiDB with MySQL protocol.
|
||||
TYPE = postgres
|
||||
HOST = 127.0.0.1:5432
|
||||
NAME = gogs
|
||||
@@ -196,6 +197,8 @@ USER = noreply@gogs.localhost
|
||||
; The login password.
|
||||
PASSWORD =
|
||||
|
||||
; Whether to disable HELO operation when the hostname is different.
|
||||
DISABLE_HELO =
|
||||
; The custom hostname for HELO operation, default is from system.
|
||||
HELO_HOSTNAME =
|
||||
|
||||
@@ -276,8 +279,6 @@ ACCESS_CONTROL_ALLOW_ORIGIN =
|
||||
STORAGE = local
|
||||
; The root path to store LFS objects on local file system.
|
||||
OBJECTS_PATH = data/lfs-objects
|
||||
; The path to temporarily store LFS objects during upload verification.
|
||||
OBJECTS_TEMP_PATH = data/tmp/lfs-objects
|
||||
|
||||
[attachment]
|
||||
; Whether to enabled upload attachments in general.
|
||||
|
||||
@@ -120,6 +120,7 @@ confirm_password = Confirm Password
|
||||
admin_email = Admin Email
|
||||
install_gogs = Install Gogs
|
||||
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_repo_path = Repository root path is invalid: %v
|
||||
run_user_not_match = Run user isn't the current user: %s -> %s
|
||||
@@ -1262,6 +1263,7 @@ config.email.subject_prefix = Subject prefix
|
||||
config.email.host = Host
|
||||
config.email.from = From
|
||||
config.email.user = User
|
||||
config.email.disable_helo = Disable HELO
|
||||
config.email.helo_hostname = HELO hostname
|
||||
config.email.skip_verify = Skip certificate verify
|
||||
config.email.use_certificate = Use custom certificate
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -55,11 +55,23 @@ There are two ways to authenticate through the Gogs API. Requests that require a
|
||||
</Warning>
|
||||
</Tab>
|
||||
<Tab title="Access token">
|
||||
Personal access tokens must be sent via the `Authorization` request header.
|
||||
Personal access tokens are the recommended way to authenticate. They can be sent via a request **header** or a **URL query parameter**.
|
||||
|
||||
**Using a header:**
|
||||
|
||||
```bash
|
||||
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>
|
||||
</Tabs>
|
||||
|
||||
|
||||
@@ -5449,6 +5449,12 @@
|
||||
"in": "header",
|
||||
"name": "Authorization",
|
||||
"description": "Personal access token. Use format: token {YOUR_ACCESS_TOKEN}"
|
||||
},
|
||||
"TokenQuery": {
|
||||
"type": "apiKey",
|
||||
"in": "query",
|
||||
"name": "token",
|
||||
"description": "Access token as query parameter"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
|
||||
@@ -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:
|
||||
|
||||
```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.
|
||||
@@ -58,7 +58,7 @@ Answers to common questions about Gogs configuration, administration, and usage.
|
||||
```bash
|
||||
su git
|
||||
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.
|
||||
|
||||
@@ -50,8 +50,7 @@
|
||||
"advancing/webhooks",
|
||||
"advancing/git-lfs",
|
||||
"advancing/custom-templates",
|
||||
"advancing/localization",
|
||||
"advancing/cli-reference"
|
||||
"advancing/localization"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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:
|
||||
|
||||
```bash
|
||||
gogs web --config /etc/gogs/app.ini
|
||||
./gogs web --config /etc/gogs/app.ini
|
||||
```
|
||||
|
||||
### What lives in `custom/`
|
||||
|
||||
@@ -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.
|
||||
</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 title="Docker">
|
||||
Two types of Docker images are provided:
|
||||
|
||||
@@ -5,14 +5,14 @@ description: "The painless way to host your own Git service"
|
||||
icon: "book-open"
|
||||
---
|
||||
|
||||
<img
|
||||
className="block dark:hidden"
|
||||
<img
|
||||
className="block dark:hidden"
|
||||
src="/images/logo-light.svg"
|
||||
noZoom
|
||||
/>
|
||||
|
||||
<img
|
||||
className="hidden dark:block"
|
||||
<img
|
||||
className="hidden dark:block"
|
||||
src="/images/logo-dark.svg"
|
||||
noZoom
|
||||
/>
|
||||
@@ -34,14 +34,14 @@ The growth of the Gogs project wasn't possible without our world-class sponsors!
|
||||
|
||||
<Columns cols={2}>
|
||||
<a href="https://www.digitalocean.com/?refcode=5aeb02268b55&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge" target="_blank" style={{borderBottom: "none"}}>
|
||||
<img
|
||||
className="block dark:hidden"
|
||||
<img
|
||||
className="block dark:hidden"
|
||||
src="/images/sponsors/digitalocean-light.png"
|
||||
width={320}
|
||||
noZoom
|
||||
/>
|
||||
<img
|
||||
className="hidden dark:block"
|
||||
<img
|
||||
className="hidden dark:block"
|
||||
src="/images/sponsors/digitalocean-dark.png"
|
||||
width={320}
|
||||
noZoom
|
||||
@@ -50,13 +50,13 @@ The growth of the Gogs project wasn't possible without our world-class sponsors!
|
||||
|
||||
<a href="https://www.mintlify.com" target="_blank" style={{borderBottom: "none"}}>
|
||||
<img
|
||||
className="block dark:hidden"
|
||||
className="block dark:hidden"
|
||||
src="/images/sponsors/mintlify-light.svg"
|
||||
width={320}
|
||||
noZoom
|
||||
/>
|
||||
<img
|
||||
className="hidden dark:block"
|
||||
className="hidden dark:block"
|
||||
src="/images/sponsors/mintlify-dark.svg"
|
||||
width={320}
|
||||
noZoom
|
||||
@@ -64,14 +64,14 @@ The growth of the Gogs project wasn't possible without our world-class sponsors!
|
||||
</a>
|
||||
|
||||
<a href="https://www.crowdin.com" target="_blank" style={{borderBottom: "none"}}>
|
||||
<img
|
||||
className="block dark:hidden"
|
||||
<img
|
||||
className="block dark:hidden"
|
||||
src="/images/sponsors/crowdin-light.svg"
|
||||
width={320}
|
||||
noZoom
|
||||
/>
|
||||
<img
|
||||
className="hidden dark:block"
|
||||
<img
|
||||
className="hidden dark:block"
|
||||
src="/images/sponsors/crowdin-dark.svg"
|
||||
width={320}
|
||||
noZoom
|
||||
@@ -79,14 +79,14 @@ The growth of the Gogs project wasn't possible without our world-class sponsors!
|
||||
</a>
|
||||
|
||||
<a href="https://www.buildkite.com" target="_blank" style={{borderBottom: "none", paddingTop: "5px"}}>
|
||||
<img
|
||||
className="block dark:hidden"
|
||||
<img
|
||||
className="block dark:hidden"
|
||||
src="/images/sponsors/buildkite-light.svg"
|
||||
width={320}
|
||||
noZoom
|
||||
/>
|
||||
<img
|
||||
className="hidden dark:block"
|
||||
<img
|
||||
className="hidden dark:block"
|
||||
src="/images/sponsors/buildkite-dark.svg"
|
||||
width={320}
|
||||
noZoom
|
||||
|
||||
22
go.mod
22
go.mod
@@ -1,6 +1,6 @@
|
||||
module gogs.io/gogs
|
||||
|
||||
go 1.26.0
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
@@ -22,19 +22,20 @@ require (
|
||||
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561
|
||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
||||
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/minwinsvc v0.0.0-20170301035411-95be6356811a
|
||||
github.com/google/go-github v17.0.0+incompatible
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/inbucket/html2text v1.0.0
|
||||
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/msteinert/pam v1.2.0
|
||||
github.com/niklasfasching/go-org v1.9.1
|
||||
github.com/olekukonko/tablewriter v1.1.3
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/russross/blackfriday v1.6.0
|
||||
github.com/sergi/go-diff v1.4.0
|
||||
github.com/sourcegraph/run v0.12.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
@@ -42,16 +43,18 @@ require (
|
||||
github.com/unknwon/com v1.0.1
|
||||
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
|
||||
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e
|
||||
github.com/urfave/cli/v3 v3.6.2
|
||||
github.com/wneessen/go-mail v0.7.2
|
||||
github.com/urfave/cli v1.22.17
|
||||
github.com/yuin/goldmark v1.7.16
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/image v0.35.0
|
||||
golang.org/x/net v0.48.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/macaron.v1 v1.5.1
|
||||
gorm.io/driver/mysql v1.5.2
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
gorm.io/driver/sqlserver v1.4.1
|
||||
gorm.io/gorm v1.25.12
|
||||
unknwon.dev/clog/v2 v2.2.0
|
||||
xorm.io/builder v0.3.6
|
||||
@@ -72,7 +75,9 @@ require (
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // 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/denisenkom/go-mssqldb v0.12.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/djherbis/buffer v1.2.0 // indirect
|
||||
github.com/djherbis/nio/v3 v3.0.1 // indirect
|
||||
@@ -86,6 +91,8 @@ require (
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // 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/gorilla/css v1.0.1 // indirect
|
||||
github.com/itchyny/gojq v0.12.11 // indirect
|
||||
@@ -105,6 +112,9 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.24 // 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/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
@@ -117,6 +127,7 @@ require (
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // 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/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
go.bobheadxi.dev/streamline v1.2.1 // indirect
|
||||
@@ -127,6 +138,7 @@ require (
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // 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/redis.v2 v2.3.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
@@ -136,5 +148,5 @@ require (
|
||||
modernc.org/sqlite v1.39.0 // indirect
|
||||
)
|
||||
|
||||
// +heroku goVersion go1.26
|
||||
// +heroku goVersion go1.25
|
||||
// +heroku install ./cmd/gogs
|
||||
|
||||
85
go.sum
85
go.sum
@@ -2,13 +2,20 @@ bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4
|
||||
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.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=
|
||||
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=
|
||||
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/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 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/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
@@ -54,13 +61,16 @@ 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-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
|
||||
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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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.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/go.mod h1:cDK2Y9IF5roTJgugWV23IvlOJsllhDN5zxRDN+g4cZo=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
@@ -70,6 +80,8 @@ 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/nio/v3 v3.0.1 h1:6wxhnuppteMa6RHA4L81Dq7ThkZH8SwnDzXDYy95vB4=
|
||||
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/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
@@ -144,10 +156,21 @@ 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/git-module v1.8.6 h1:4Io9vWZYQyIjdIPxfKgeYZXnDKNgydc6OZTxII5xCH4=
|
||||
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/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/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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
@@ -177,11 +200,14 @@ 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-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/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/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-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
@@ -237,8 +263,11 @@ 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/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/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/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/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=
|
||||
@@ -284,6 +313,15 @@ 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/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/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/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
@@ -328,6 +366,9 @@ 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/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
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/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -358,8 +399,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
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/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/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
@@ -385,12 +426,19 @@ 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/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.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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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.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.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/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
@@ -403,12 +451,12 @@ 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/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/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
|
||||
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/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
|
||||
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
|
||||
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.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
|
||||
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.bobheadxi.dev/streamline v1.2.1 h1:IqKSA1TbeuDqCzYNAwtlh8sqf3tsQus8XgJdkCWFT8c=
|
||||
@@ -431,6 +479,8 @@ 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-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-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/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -459,8 +509,12 @@ 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-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-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-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/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -494,13 +548,18 @@ 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-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-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-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-20220224120231-95c6836cb0e7/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/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-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/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -508,6 +567,7 @@ 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.3/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/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -551,6 +611,8 @@ 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/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
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/go.mod h1:xsQCaysVCudhrYTfzYWe577fCe7Ceci+6qjO2Rdc0Z4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -559,6 +621,8 @@ 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/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
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.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
@@ -575,17 +639,22 @@ 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.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.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.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
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-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/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/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/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.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
|
||||
@@ -346,7 +346,6 @@ func Init(customConf string) error {
|
||||
return errors.Wrap(err, "mapping [lfs] section")
|
||||
}
|
||||
LFS.ObjectsPath = ensureAbs(LFS.ObjectsPath)
|
||||
LFS.ObjectsTempPath = ensureAbs(LFS.ObjectsTempPath)
|
||||
|
||||
handleDeprecated()
|
||||
if !HookMode {
|
||||
|
||||
@@ -56,6 +56,7 @@ var (
|
||||
User string
|
||||
Password string
|
||||
|
||||
DisableHELO bool `ini:"DISABLE_HELO"`
|
||||
HELOHostname string `ini:"HELO_HOSTNAME"`
|
||||
|
||||
SkipVerify bool
|
||||
@@ -360,9 +361,8 @@ type DatabaseOpts struct {
|
||||
var Database DatabaseOpts
|
||||
|
||||
type LFSOpts struct {
|
||||
Storage string
|
||||
ObjectsPath string
|
||||
ObjectsTempPath string
|
||||
Storage string
|
||||
ObjectsPath string
|
||||
}
|
||||
|
||||
// LFS settings
|
||||
@@ -510,6 +510,7 @@ var (
|
||||
UseSQLite3 bool
|
||||
UseMySQL bool
|
||||
UsePostgreSQL bool
|
||||
UseMSSQL bool
|
||||
)
|
||||
|
||||
// UsersAvatarPathPrefix is the path prefix to user avatars.
|
||||
|
||||
1
internal/conf/testdata/TestInit.golden.ini
vendored
1
internal/conf/testdata/TestInit.golden.ini
vendored
@@ -88,6 +88,7 @@ HOST=smtp.mailgun.org:587
|
||||
FROM=noreply@gogs.localhost
|
||||
USER=noreply@gogs.localhost
|
||||
PASSWORD=87654321
|
||||
DISABLE_HELO=false
|
||||
HELO_HOSTNAME=
|
||||
SKIP_VERIFY=false
|
||||
USE_CERTIFICATE=false
|
||||
|
||||
@@ -146,12 +146,18 @@ func authenticatedUserID(store AuthStore, c *macaron.Context, sess session.Store
|
||||
|
||||
// Check access token.
|
||||
if isAPIPath(c.Req.URL.Path) {
|
||||
var tokenSHA string
|
||||
auHead := c.Req.Header.Get("Authorization")
|
||||
if auHead != "" {
|
||||
auths := strings.Fields(auHead)
|
||||
if len(auths) == 2 && auths[0] == "token" {
|
||||
tokenSHA = auths[1]
|
||||
tokenSHA := c.Query("token")
|
||||
if len(tokenSHA) <= 0 {
|
||||
tokenSHA = c.Query("access_token")
|
||||
}
|
||||
if tokenSHA == "" {
|
||||
// Well, check with header again.
|
||||
auHead := c.Req.Header.Get("Authorization")
|
||||
if len(auHead) > 0 {
|
||||
auths := strings.Fields(auHead)
|
||||
if len(auths) == 2 && auths[0] == "token" {
|
||||
tokenSHA = auths[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,5 +54,5 @@ func (c *Context) renderNoticeBanner() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["ServerNotice"] = string(markup.RawMarkdown(buf, ""))
|
||||
c.Data["ServerNotice"] = string(markup.SanitizeBytes(markup.RawMarkdown(buf, "")))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"strconv"
|
||||
@@ -12,13 +11,14 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gogs/git-module"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"gorm.io/gorm"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/lazyregexp"
|
||||
"gogs.io/gogs/internal/repoutil"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/strutil"
|
||||
"gogs.io/gogs/internal/testutil"
|
||||
"gogs.io/gogs/internal/tool"
|
||||
@@ -230,7 +230,7 @@ func (s *ActionsStore) MirrorSyncPush(ctx context.Context, opts MirrorSyncPushOp
|
||||
err = PrepareWebhooks(
|
||||
opts.Repo,
|
||||
HookEventTypePush,
|
||||
&apiv1types.WebhookPushPayload{
|
||||
&api.PushPayload{
|
||||
Ref: opts.RefName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
@@ -245,7 +245,7 @@ func (s *ActionsStore) MirrorSyncPush(ctx context.Context, opts MirrorSyncPushOp
|
||||
return errors.Wrap(err, "prepare webhooks")
|
||||
}
|
||||
|
||||
data, err := json.Marshal(opts.Commits)
|
||||
data, err := jsoniter.Marshal(opts.Commits)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal JSON")
|
||||
}
|
||||
@@ -496,10 +496,10 @@ func (s *ActionsStore) CommitRepo(ctx context.Context, opts CommitRepoOptions) e
|
||||
err = PrepareWebhooks(
|
||||
opts.Repo,
|
||||
HookEventTypeDelete,
|
||||
&apiv1types.WebhookDeletePayload{
|
||||
&api.DeletePayload{
|
||||
Ref: refName,
|
||||
RefType: "branch",
|
||||
PusherType: apiv1types.WebhookPusherTypeUser,
|
||||
PusherType: api.PUSHER_TYPE_USER,
|
||||
Repo: apiRepo,
|
||||
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]
|
||||
}
|
||||
|
||||
data, err := json.Marshal(opts.Commits)
|
||||
data, err := jsoniter.Marshal(opts.Commits)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal JSON")
|
||||
}
|
||||
@@ -540,7 +540,7 @@ func (s *ActionsStore) CommitRepo(ctx context.Context, opts CommitRepoOptions) e
|
||||
err = PrepareWebhooks(
|
||||
opts.Repo,
|
||||
HookEventTypeCreate,
|
||||
&apiv1types.WebhookCreatePayload{
|
||||
&api.CreatePayload{
|
||||
Ref: refName,
|
||||
RefType: "branch",
|
||||
DefaultBranch: opts.Repo.DefaultBranch,
|
||||
@@ -573,7 +573,7 @@ func (s *ActionsStore) CommitRepo(ctx context.Context, opts CommitRepoOptions) e
|
||||
err = PrepareWebhooks(
|
||||
opts.Repo,
|
||||
HookEventTypePush,
|
||||
&apiv1types.WebhookPushPayload{
|
||||
&api.PushPayload{
|
||||
Ref: opts.RefFullName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
@@ -635,10 +635,10 @@ func (s *ActionsStore) PushTag(ctx context.Context, opts PushTagOptions) error {
|
||||
err = PrepareWebhooks(
|
||||
opts.Repo,
|
||||
HookEventTypeDelete,
|
||||
&apiv1types.WebhookDeletePayload{
|
||||
&api.DeletePayload{
|
||||
Ref: refName,
|
||||
RefType: "tag",
|
||||
PusherType: apiv1types.WebhookPusherTypeUser,
|
||||
PusherType: api.PUSHER_TYPE_USER,
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
},
|
||||
@@ -658,7 +658,7 @@ func (s *ActionsStore) PushTag(ctx context.Context, opts PushTagOptions) error {
|
||||
err = PrepareWebhooks(
|
||||
opts.Repo,
|
||||
HookEventTypeCreate,
|
||||
&apiv1types.WebhookCreatePayload{
|
||||
&api.CreatePayload{
|
||||
Ref: refName,
|
||||
RefType: "tag",
|
||||
Sha: opts.NewCommitID,
|
||||
@@ -848,7 +848,7 @@ func NewPushCommits() *PushCommits {
|
||||
}
|
||||
}
|
||||
|
||||
func (pcs *PushCommits) APIFormat(ctx context.Context, usersStore *UsersStore, repoPath, repoURL string) ([]*apiv1types.WebhookPayloadCommit, error) {
|
||||
func (pcs *PushCommits) APIFormat(ctx context.Context, usersStore *UsersStore, repoPath, repoURL string) ([]*api.PayloadCommit, error) {
|
||||
// NOTE: We cache query results in case there are many commits in a single push.
|
||||
usernameByEmail := make(map[string]string)
|
||||
getUsernameByEmail := func(email string) (string, error) {
|
||||
@@ -870,7 +870,7 @@ func (pcs *PushCommits) APIFormat(ctx context.Context, usersStore *UsersStore, r
|
||||
return user.Name, nil
|
||||
}
|
||||
|
||||
commits := make([]*apiv1types.WebhookPayloadCommit, len(pcs.Commits))
|
||||
commits := make([]*api.PayloadCommit, len(pcs.Commits))
|
||||
for i, commit := range pcs.Commits {
|
||||
authorUsername, err := getUsernameByEmail(commit.AuthorEmail)
|
||||
if err != nil {
|
||||
@@ -890,16 +890,16 @@ func (pcs *PushCommits) APIFormat(ctx context.Context, usersStore *UsersStore, r
|
||||
}
|
||||
}
|
||||
|
||||
commits[i] = &apiv1types.WebhookPayloadCommit{
|
||||
commits[i] = &api.PayloadCommit{
|
||||
ID: commit.Sha1,
|
||||
Message: commit.Message,
|
||||
URL: fmt.Sprintf("%s/commit/%s", repoURL, commit.Sha1),
|
||||
Author: &apiv1types.WebhookPayloadUser{
|
||||
Author: &api.PayloadUser{
|
||||
Name: commit.AuthorName,
|
||||
Email: commit.AuthorEmail,
|
||||
UserName: authorUsername,
|
||||
},
|
||||
Committer: &apiv1types.WebhookPayloadUser{
|
||||
Committer: &api.PayloadUser{
|
||||
Name: commit.CommitterName,
|
||||
Email: commit.CommitterEmail,
|
||||
UserName: committerUsername,
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -14,6 +13,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
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()
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(elem)
|
||||
err = jsoniter.NewEncoder(w).Encode(elem)
|
||||
if err != nil {
|
||||
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) {
|
||||
return json.NewEncoder(f).Encode(bean)
|
||||
return jsoniter.NewEncoder(f).Encode(bean)
|
||||
}); err != nil {
|
||||
_ = f.Close()
|
||||
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(""))
|
||||
|
||||
elem := reflect.New(reflect.TypeOf(table).Elem()).Interface()
|
||||
err = json.Unmarshal(cleaned, elem)
|
||||
err = jsoniter.Unmarshal(cleaned, elem)
|
||||
if err != nil {
|
||||
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)
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
if err = json.Unmarshal(scanner.Bytes(), table); err != nil {
|
||||
if err = jsoniter.Unmarshal(scanner.Bytes(), table); err != nil {
|
||||
return errors.Newf("unmarshal to struct: %v", err)
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error
|
||||
DeadlineUnix int64
|
||||
ClosedDateUnix int64
|
||||
}
|
||||
if err = json.Unmarshal(scanner.Bytes(), &meta); err != nil {
|
||||
if err = jsoniter.Unmarshal(scanner.Bytes(), &meta); err != nil {
|
||||
log.Error("Failed to unmarshal to map: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,10 @@ import (
|
||||
log "unknwon.dev/clog/v2"
|
||||
"xorm.io/xorm"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
"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.
|
||||
@@ -135,8 +136,8 @@ func (c *Comment) HTMLURL() string {
|
||||
|
||||
// This method assumes following fields have been assigned with valid values:
|
||||
// Required - Poster, Issue
|
||||
func (c *Comment) APIFormat() *apiv1types.IssueComment {
|
||||
return &apiv1types.IssueComment{
|
||||
func (c *Comment) APIFormat() *api.Comment {
|
||||
return &api.Comment{
|
||||
ID: c.ID,
|
||||
HTMLURL: c.HTMLURL(),
|
||||
Poster: c.Poster.APIFormat(),
|
||||
@@ -346,8 +347,8 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri
|
||||
}
|
||||
|
||||
comment.Issue = issue
|
||||
if err = PrepareWebhooks(repo, HookEventTypeIssueComment, &apiv1types.WebhookIssueCommentPayload{
|
||||
Action: apiv1types.WebhookIssueCommentCreated,
|
||||
if err = PrepareWebhooks(repo, HookEventTypeIssueComment, &api.IssueCommentPayload{
|
||||
Action: api.HOOK_ISSUE_COMMENT_CREATED,
|
||||
Issue: issue.APIFormat(),
|
||||
Comment: comment.APIFormat(),
|
||||
Repository: repo.APIFormatLegacy(nil),
|
||||
@@ -482,12 +483,12 @@ func UpdateComment(doer *User, c *Comment, oldContent string) (err error) {
|
||||
|
||||
if err = c.Issue.LoadAttributes(); err != nil {
|
||||
log.Error("Issue.LoadAttributes [issue_id: %d]: %v", c.IssueID, err)
|
||||
} else if err = PrepareWebhooks(c.Issue.Repo, HookEventTypeIssueComment, &apiv1types.WebhookIssueCommentPayload{
|
||||
Action: apiv1types.WebhookIssueCommentEdited,
|
||||
} else if err = PrepareWebhooks(c.Issue.Repo, HookEventTypeIssueComment, &api.IssueCommentPayload{
|
||||
Action: api.HOOK_ISSUE_COMMENT_EDITED,
|
||||
Issue: c.Issue.APIFormat(),
|
||||
Comment: c.APIFormat(),
|
||||
Changes: &apiv1types.WebhookChangesPayload{
|
||||
Body: &apiv1types.WebhookChangesFromPayload{
|
||||
Changes: &api.ChangesPayload{
|
||||
Body: &api.ChangesFromPayload{
|
||||
From: oldContent,
|
||||
},
|
||||
},
|
||||
@@ -537,8 +538,8 @@ func DeleteCommentByID(doer *User, id int64) error {
|
||||
|
||||
if err = comment.Issue.LoadAttributes(); err != nil {
|
||||
log.Error("Issue.LoadAttributes [issue_id: %d]: %v", comment.IssueID, err)
|
||||
} else if err = PrepareWebhooks(comment.Issue.Repo, HookEventTypeIssueComment, &apiv1types.WebhookIssueCommentPayload{
|
||||
Action: apiv1types.WebhookIssueCommentDeleted,
|
||||
} else if err = PrepareWebhooks(comment.Issue.Repo, HookEventTypeIssueComment, &api.IssueCommentPayload{
|
||||
Action: api.HOOK_ISSUE_COMMENT_DELETED,
|
||||
Issue: comment.Issue.APIFormat(),
|
||||
Comment: comment.APIFormat(),
|
||||
Repository: comment.Issue.Repo.APIFormatLegacy(nil),
|
||||
|
||||
@@ -91,6 +91,8 @@ func NewConnection(w logger.Writer) (*gorm.DB, error) {
|
||||
db = db.Set("gorm:table_options", "ENGINE=InnoDB").Session(&gorm.Session{})
|
||||
case "sqlite3":
|
||||
conf.UseSQLite3 = true
|
||||
case "mssql":
|
||||
conf.UseMSSQL = true
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@ import (
|
||||
log "unknwon.dev/clog/v2"
|
||||
"xorm.io/xorm"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
"gogs.io/gogs/internal/markup"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/tool"
|
||||
)
|
||||
|
||||
@@ -171,23 +172,23 @@ func (issue *Issue) HTMLURL() string {
|
||||
}
|
||||
|
||||
// State returns string representation of issue status.
|
||||
func (issue *Issue) State() apiv1types.IssueStateType {
|
||||
func (issue *Issue) State() api.StateType {
|
||||
if issue.IsClosed {
|
||||
return apiv1types.IssueStateClosed
|
||||
return api.STATE_CLOSED
|
||||
}
|
||||
return apiv1types.IssueStateOpen
|
||||
return api.STATE_OPEN
|
||||
}
|
||||
|
||||
// This method assumes some fields assigned with values:
|
||||
// Required - Poster, Labels,
|
||||
// Optional - Milestone, Assignee, PullRequest
|
||||
func (issue *Issue) APIFormat() *apiv1types.Issue {
|
||||
apiLabels := make([]*apiv1types.IssueLabel, len(issue.Labels))
|
||||
func (issue *Issue) APIFormat() *api.Issue {
|
||||
apiLabels := make([]*api.Label, len(issue.Labels))
|
||||
for i := range issue.Labels {
|
||||
apiLabels[i] = issue.Labels[i].APIFormat()
|
||||
}
|
||||
|
||||
apiIssue := &apiv1types.Issue{
|
||||
apiIssue := &api.Issue{
|
||||
ID: issue.ID,
|
||||
Index: issue.Index,
|
||||
Poster: issue.Poster.APIFormat(),
|
||||
@@ -207,7 +208,7 @@ func (issue *Issue) APIFormat() *apiv1types.Issue {
|
||||
apiIssue.Assignee = issue.Assignee.APIFormat()
|
||||
}
|
||||
if issue.IsPull {
|
||||
apiIssue.PullRequest = &apiv1types.PullRequestMeta{
|
||||
apiIssue.PullRequest = &api.PullRequestMeta{
|
||||
HasMerged: issue.PullRequest.HasMerged,
|
||||
}
|
||||
if issue.PullRequest.HasMerged {
|
||||
@@ -245,16 +246,16 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
|
||||
log.Error("LoadIssue: %v", err)
|
||||
return
|
||||
}
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
|
||||
Action: apiv1types.WebhookIssueLabelUpdated,
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_LABEL_UPDATED,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormatLegacy(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
} else {
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
|
||||
Action: apiv1types.WebhookIssueLabelUpdated,
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{
|
||||
Action: api.HOOK_ISSUE_LABEL_UPDATED,
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
Repository: issue.Repo.APIFormatLegacy(nil),
|
||||
@@ -358,16 +359,16 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
|
||||
log.Error("LoadIssue: %v", err)
|
||||
return err
|
||||
}
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
|
||||
Action: apiv1types.WebhookIssueLabelCleared,
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_LABEL_CLEARED,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormatLegacy(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
} else {
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
|
||||
Action: apiv1types.WebhookIssueLabelCleared,
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{
|
||||
Action: api.HOOK_ISSUE_LABEL_CLEARED,
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
Repository: issue.Repo.APIFormatLegacy(nil),
|
||||
@@ -486,29 +487,29 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
|
||||
if issue.IsPull {
|
||||
// Merge pull request calls issue.changeStatus so we need to handle separately.
|
||||
issue.PullRequest.Issue = issue
|
||||
apiPullRequest := &apiv1types.WebhookPullRequestPayload{
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: repo.APIFormatLegacy(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
if isClosed {
|
||||
apiPullRequest.Action = apiv1types.WebhookIssueClosed
|
||||
apiPullRequest.Action = api.HOOK_ISSUE_CLOSED
|
||||
} else {
|
||||
apiPullRequest.Action = apiv1types.WebhookIssueReopened
|
||||
apiPullRequest.Action = api.HOOK_ISSUE_REOPENED
|
||||
}
|
||||
err = PrepareWebhooks(repo, HookEventTypePullRequest, apiPullRequest)
|
||||
} else {
|
||||
apiIssues := &apiv1types.WebhookIssuesPayload{
|
||||
apiIssues := &api.IssuesPayload{
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
Repository: repo.APIFormatLegacy(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
if isClosed {
|
||||
apiIssues.Action = apiv1types.WebhookIssueClosed
|
||||
apiIssues.Action = api.HOOK_ISSUE_CLOSED
|
||||
} else {
|
||||
apiIssues.Action = apiv1types.WebhookIssueReopened
|
||||
apiIssues.Action = api.HOOK_ISSUE_REOPENED
|
||||
}
|
||||
err = PrepareWebhooks(repo, HookEventTypeIssues, apiIssues)
|
||||
}
|
||||
@@ -528,12 +529,12 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||
|
||||
if issue.IsPull {
|
||||
issue.PullRequest.Issue = issue
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
|
||||
Action: apiv1types.WebhookIssueEdited,
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_EDITED,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Changes: &apiv1types.WebhookChangesPayload{
|
||||
Title: &apiv1types.WebhookChangesFromPayload{
|
||||
Changes: &api.ChangesPayload{
|
||||
Title: &api.ChangesFromPayload{
|
||||
From: oldTitle,
|
||||
},
|
||||
},
|
||||
@@ -541,12 +542,12 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
} else {
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
|
||||
Action: apiv1types.WebhookIssueEdited,
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{
|
||||
Action: api.HOOK_ISSUE_EDITED,
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
Changes: &apiv1types.WebhookChangesPayload{
|
||||
Title: &apiv1types.WebhookChangesFromPayload{
|
||||
Changes: &api.ChangesPayload{
|
||||
Title: &api.ChangesFromPayload{
|
||||
From: oldTitle,
|
||||
},
|
||||
},
|
||||
@@ -570,12 +571,12 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
||||
|
||||
if issue.IsPull {
|
||||
issue.PullRequest.Issue = issue
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
|
||||
Action: apiv1types.WebhookIssueEdited,
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_EDITED,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Changes: &apiv1types.WebhookChangesPayload{
|
||||
Body: &apiv1types.WebhookChangesFromPayload{
|
||||
Changes: &api.ChangesPayload{
|
||||
Body: &api.ChangesFromPayload{
|
||||
From: oldContent,
|
||||
},
|
||||
},
|
||||
@@ -583,12 +584,12 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
} else {
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
|
||||
Action: apiv1types.WebhookIssueEdited,
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{
|
||||
Action: api.HOOK_ISSUE_EDITED,
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
Changes: &apiv1types.WebhookChangesPayload{
|
||||
Body: &apiv1types.WebhookChangesFromPayload{
|
||||
Changes: &api.ChangesPayload{
|
||||
Body: &api.ChangesFromPayload{
|
||||
From: oldContent,
|
||||
},
|
||||
},
|
||||
@@ -619,29 +620,29 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
|
||||
isRemoveAssignee := err != nil
|
||||
if issue.IsPull {
|
||||
issue.PullRequest.Issue = issue
|
||||
apiPullRequest := &apiv1types.WebhookPullRequestPayload{
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormatLegacy(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
if isRemoveAssignee {
|
||||
apiPullRequest.Action = apiv1types.WebhookIssueUnassigned
|
||||
apiPullRequest.Action = api.HOOK_ISSUE_UNASSIGNED
|
||||
} else {
|
||||
apiPullRequest.Action = apiv1types.WebhookIssueAssigned
|
||||
apiPullRequest.Action = api.HOOK_ISSUE_ASSIGNED
|
||||
}
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, apiPullRequest)
|
||||
} else {
|
||||
apiIssues := &apiv1types.WebhookIssuesPayload{
|
||||
apiIssues := &api.IssuesPayload{
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
Repository: issue.Repo.APIFormatLegacy(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
if isRemoveAssignee {
|
||||
apiIssues.Action = apiv1types.WebhookIssueUnassigned
|
||||
apiIssues.Action = api.HOOK_ISSUE_UNASSIGNED
|
||||
} else {
|
||||
apiIssues.Action = apiv1types.WebhookIssueAssigned
|
||||
apiIssues.Action = api.HOOK_ISSUE_ASSIGNED
|
||||
}
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, apiIssues)
|
||||
}
|
||||
@@ -788,8 +789,8 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
|
||||
log.Error("MailParticipants: %v", err)
|
||||
}
|
||||
|
||||
if err = PrepareWebhooks(repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
|
||||
Action: apiv1types.WebhookIssueOpened,
|
||||
if err = PrepareWebhooks(repo, HookEventTypeIssues, &api.IssuesPayload{
|
||||
Action: api.HOOK_ISSUE_OPENED,
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
Repository: repo.APIFormatLegacy(nil),
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
"gogs.io/gogs/internal/lazyregexp"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/tool"
|
||||
)
|
||||
|
||||
@@ -62,8 +62,8 @@ type Label struct {
|
||||
IsChecked bool `xorm:"-" json:"-" gorm:"-"`
|
||||
}
|
||||
|
||||
func (l *Label) APIFormat() *apiv1types.IssueLabel {
|
||||
return &apiv1types.IssueLabel{
|
||||
func (l *Label) APIFormat() *api.Label {
|
||||
return &api.Label{
|
||||
ID: l.ID,
|
||||
Name: l.Name,
|
||||
Color: strings.TrimLeft(l.Color, "#"),
|
||||
|
||||
@@ -151,9 +151,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
|
||||
names = append(names, issue.Assignee.Name)
|
||||
}
|
||||
}
|
||||
if err = email.SendIssueCommentMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos); err != nil {
|
||||
return errors.Wrap(err, "send issue comment mail")
|
||||
}
|
||||
email.SendIssueCommentMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos)
|
||||
|
||||
// Mail mentioned people and exclude watchers.
|
||||
names = append(names, doer.Name)
|
||||
@@ -170,9 +168,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get mailable emails by usernames")
|
||||
}
|
||||
if err = email.SendIssueMentionMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos); err != nil {
|
||||
return errors.Wrap(err, "send issue mention mail")
|
||||
}
|
||||
email.SendIssueMentionMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gogs.io/gogs/internal/auth"
|
||||
@@ -41,8 +41,7 @@ func (s *LoginSource) BeforeSave(_ *gorm.DB) (err error) {
|
||||
if s.Provider == nil {
|
||||
return nil
|
||||
}
|
||||
data, err := json.Marshal(s.Provider.Config())
|
||||
s.Config = string(data)
|
||||
s.Config, err = jsoniter.MarshalToString(s.Provider.Config())
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -73,7 +72,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
|
||||
switch s.Type {
|
||||
case auth.LDAP:
|
||||
var cfg ldap.Config
|
||||
err := json.Unmarshal([]byte(s.Config), &cfg)
|
||||
err := jsoniter.UnmarshalFromString(s.Config, &cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -81,7 +80,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
|
||||
|
||||
case auth.DLDAP:
|
||||
var cfg ldap.Config
|
||||
err := json.Unmarshal([]byte(s.Config), &cfg)
|
||||
err := jsoniter.UnmarshalFromString(s.Config, &cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -89,7 +88,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
|
||||
|
||||
case auth.SMTP:
|
||||
var cfg smtp.Config
|
||||
err := json.Unmarshal([]byte(s.Config), &cfg)
|
||||
err := jsoniter.UnmarshalFromString(s.Config, &cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -97,7 +96,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
|
||||
|
||||
case auth.PAM:
|
||||
var cfg pam.Config
|
||||
err := json.Unmarshal([]byte(s.Config), &cfg)
|
||||
err := jsoniter.UnmarshalFromString(s.Config, &cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -105,7 +104,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
|
||||
|
||||
case auth.GitHub:
|
||||
var cfg github.Config
|
||||
err := json.Unmarshal([]byte(s.Config), &cfg)
|
||||
err := jsoniter.UnmarshalFromString(s.Config, &cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -113,7 +112,7 @@ func (s *LoginSource) AfterFind(_ *gorm.DB) error {
|
||||
|
||||
case auth.Mock:
|
||||
var cfg mockProviderConfig
|
||||
err := json.Unmarshal([]byte(s.Config), &cfg)
|
||||
err := jsoniter.UnmarshalFromString(s.Config, &cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -216,8 +215,7 @@ func (s *LoginSourcesStore) Create(ctx context.Context, opts CreateLoginSourceOp
|
||||
IsActived: opts.Activated,
|
||||
IsDefault: opts.Default,
|
||||
}
|
||||
data, err := json.Marshal(opts.Config)
|
||||
source.Config = string(data)
|
||||
source.Config, err = jsoniter.MarshalToString(opts.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
- If you were using below 0.6.0 (e.g. 0.5.x), download last supported archive from following link:
|
||||
https://github.com/gogs/gogs/releases/tag/v0.7.33
|
||||
https://gogs.io/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:
|
||||
https://github.com/gogs/gogs/releases/tag/v0.9.141
|
||||
https://gogs.io/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:
|
||||
https://github.com/gogs/gogs/releases/tag/v0.12.0
|
||||
https://gogs.io/gogs/releases/tag/v0.12.0
|
||||
|
||||
Once finished downloading:
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (m *Milestone) State() apiv1types.IssueStateType {
|
||||
func (m *Milestone) State() api.StateType {
|
||||
if m.IsClosed {
|
||||
return apiv1types.IssueStateClosed
|
||||
return api.STATE_CLOSED
|
||||
}
|
||||
return apiv1types.IssueStateOpen
|
||||
return api.STATE_OPEN
|
||||
}
|
||||
|
||||
func (m *Milestone) ChangeStatus(isClosed bool) error {
|
||||
return ChangeMilestoneStatus(m, isClosed)
|
||||
}
|
||||
|
||||
func (m *Milestone) APIFormat() *apiv1types.IssueMilestone {
|
||||
apiMilestone := &apiv1types.IssueMilestone{
|
||||
func (m *Milestone) APIFormat() *api.Milestone {
|
||||
apiMilestone := &api.Milestone{
|
||||
ID: m.ID,
|
||||
State: m.State(),
|
||||
Title: m.Name,
|
||||
@@ -343,11 +343,11 @@ func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err
|
||||
return errors.Newf("commit: %v", err)
|
||||
}
|
||||
|
||||
var hookAction apiv1types.WebhookIssueAction
|
||||
var hookAction api.HookIssueAction
|
||||
if issue.MilestoneID > 0 {
|
||||
hookAction = apiv1types.WebhookIssueMilestoned
|
||||
hookAction = api.HOOK_ISSUE_MILESTONED
|
||||
} else {
|
||||
hookAction = apiv1types.WebhookIssueDemilestoned
|
||||
hookAction = api.HOOK_ISSUE_DEMILESTONED
|
||||
}
|
||||
|
||||
if issue.IsPull {
|
||||
@@ -356,7 +356,7 @@ func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err
|
||||
log.Error("LoadIssue: %v", err)
|
||||
return err
|
||||
}
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
||||
Action: hookAction,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
@@ -364,7 +364,7 @@ func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
} else {
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &apiv1types.WebhookIssuesPayload{
|
||||
err = PrepareWebhooks(issue.Repo, HookEventTypeIssues, &api.IssuesPayload{
|
||||
Action: hookAction,
|
||||
Index: issue.Index,
|
||||
Issue: issue.APIFormat(),
|
||||
|
||||
@@ -94,6 +94,11 @@ func getEngine() (*xorm.Engine, error) {
|
||||
conf.Database.User, conf.Database.Password, host, port, conf.Database.Name, conf.Database.SSLMode, conf.Database.Schema)
|
||||
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":
|
||||
if err := os.MkdirAll(path.Dir(conf.Database.Path), os.ModePerm); err != nil {
|
||||
return nil, errors.Newf("create directories: %v", err)
|
||||
|
||||
@@ -30,7 +30,7 @@ type Team struct {
|
||||
func (t *Team) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "num_repos":
|
||||
// LEGACY [1.0]: this is backward compatibility bug fix for https://github.com/gogs/gogs/issues/3671
|
||||
// LEGACY [1.0]: this is backward compatibility bug fix for https://gogs.io/gogs/issues/3671
|
||||
if t.NumRepos < 0 {
|
||||
t.NumRepos = 0
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/gogs/git-module"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
"gogs.io/gogs/internal/osutil"
|
||||
"gogs.io/gogs/internal/process"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"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:
|
||||
// Required - Issue, BaseRepo
|
||||
// Optional - HeadRepo, Merger
|
||||
func (pr *PullRequest) APIFormat() *apiv1types.PullRequest {
|
||||
func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||
// In case of head repo has been deleted.
|
||||
var apiHeadRepo *apiv1types.Repository
|
||||
var apiHeadRepo *api.Repository
|
||||
if pr.HeadRepo == nil {
|
||||
apiHeadRepo = &apiv1types.Repository{
|
||||
apiHeadRepo = &api.Repository{
|
||||
Name: "deleted",
|
||||
}
|
||||
} else {
|
||||
@@ -139,7 +139,7 @@ func (pr *PullRequest) APIFormat() *apiv1types.PullRequest {
|
||||
}
|
||||
|
||||
apiIssue := pr.Issue.APIFormat()
|
||||
apiPullRequest := &apiv1types.PullRequest{
|
||||
apiPullRequest := &api.PullRequest{
|
||||
ID: pr.ID,
|
||||
Index: pr.Index,
|
||||
Poster: apiIssue.Poster,
|
||||
@@ -341,8 +341,8 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||
log.Error("LoadAttributes: %v", err)
|
||||
return nil
|
||||
}
|
||||
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
|
||||
Action: apiv1types.WebhookIssueClosed,
|
||||
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_CLOSED,
|
||||
Index: pr.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
Repository: pr.Issue.Repo.APIFormatLegacy(nil),
|
||||
@@ -376,7 +376,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
|
||||
return nil
|
||||
}
|
||||
|
||||
p := &apiv1types.WebhookPushPayload{
|
||||
p := &api.PushPayload{
|
||||
Ref: git.RefsHeads + pr.BaseBranch,
|
||||
Before: pr.MergeBase,
|
||||
After: mergeCommit.ID.String(),
|
||||
@@ -500,8 +500,8 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
|
||||
pr.Issue = pull
|
||||
pull.PullRequest = pr
|
||||
if err = PrepareWebhooks(repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
|
||||
Action: apiv1types.WebhookIssueOpened,
|
||||
if err = PrepareWebhooks(repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_OPENED,
|
||||
Index: pull.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
Repository: repo.APIFormatLegacy(nil),
|
||||
@@ -792,8 +792,8 @@ func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool
|
||||
log.Error("LoadAttributes: %v", err)
|
||||
continue
|
||||
}
|
||||
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &apiv1types.WebhookPullRequestPayload{
|
||||
Action: apiv1types.WebhookIssueSynchronized,
|
||||
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_SYNCHRONIZED,
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: pr.Issue.PullRequest.APIFormat(),
|
||||
Repository: pr.Issue.Repo.APIFormatLegacy(nil),
|
||||
|
||||
@@ -11,9 +11,10 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gogs/git-module"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/process"
|
||||
)
|
||||
|
||||
// Release represents a release of repository.
|
||||
@@ -89,8 +90,8 @@ func (r *Release) LoadAttributes() error {
|
||||
|
||||
// This method assumes some fields assigned with values:
|
||||
// Required - Publisher
|
||||
func (r *Release) APIFormat() *apiv1types.RepositoryRelease {
|
||||
return &apiv1types.RepositoryRelease{
|
||||
func (r *Release) APIFormat() *api.Release {
|
||||
return &api.Release{
|
||||
ID: r.ID,
|
||||
TagName: r.TagName,
|
||||
TargetCommitish: r.Target,
|
||||
@@ -146,8 +147,8 @@ func createTag(gitRepo *git.Repository, r *Release) error {
|
||||
}
|
||||
|
||||
func (r *Release) preparePublishWebhooks() {
|
||||
if err := PrepareWebhooks(r.Repo, HookEventTypeRelease, &apiv1types.WebhookReleasePayload{
|
||||
Action: apiv1types.WebhookReleasePublished,
|
||||
if err := PrepareWebhooks(r.Repo, HookEventTypeRelease, &api.ReleasePayload{
|
||||
Action: api.HOOK_RELEASE_PUBLISHED,
|
||||
Release: r.APIFormat(),
|
||||
Repository: r.Repo.APIFormatLegacy(nil),
|
||||
Sender: r.Publisher.APIFormat(),
|
||||
@@ -358,13 +359,11 @@ func DeleteReleaseOfRepoByID(repoID, id int64) error {
|
||||
return errors.Newf("GetRepositoryByID: %v", err)
|
||||
}
|
||||
|
||||
gitRepo, err := git.Open(repo.RepoPath())
|
||||
if err != nil {
|
||||
return errors.Newf("open repository: %v", err)
|
||||
}
|
||||
err = gitRepo.DeleteTag(rel.TagName)
|
||||
if err != nil && !strings.Contains(err.Error(), "not found") {
|
||||
return errors.Newf("delete tag: %v", err)
|
||||
_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
|
||||
fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
|
||||
"git", "tag", "-d", rel.TagName)
|
||||
if err != nil && !strings.Contains(stderr, "not found") {
|
||||
return errors.Newf("git tag -d: %v - %s", err, stderr)
|
||||
}
|
||||
|
||||
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/gogs/git-module"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
embedConf "gogs.io/gogs/conf"
|
||||
"gogs.io/gogs/internal/avatar"
|
||||
@@ -36,7 +37,6 @@ import (
|
||||
"gogs.io/gogs/internal/osutil"
|
||||
"gogs.io/gogs/internal/process"
|
||||
"gogs.io/gogs/internal/repoutil"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/semverutil"
|
||||
"gogs.io/gogs/internal/strutil"
|
||||
"gogs.io/gogs/internal/sync"
|
||||
@@ -162,7 +162,6 @@ func NewRepoContext() {
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -377,9 +376,9 @@ func (r *Repository) DeleteAvatar() error {
|
||||
// Arguments that are allowed to be nil: permission
|
||||
//
|
||||
// Deprecated: Use APIFormat instead.
|
||||
func (r *Repository) APIFormatLegacy(permission *apiv1types.RepositoryPermission, user ...*User) *apiv1types.Repository {
|
||||
func (r *Repository) APIFormatLegacy(permission *api.Permission, user ...*User) *api.Repository {
|
||||
cloneLink := r.CloneLink()
|
||||
apiRepo := &apiv1types.Repository{
|
||||
apiRepo := &api.Repository{
|
||||
ID: r.ID,
|
||||
Owner: r.Owner.APIFormat(),
|
||||
Name: r.Name,
|
||||
@@ -406,7 +405,7 @@ func (r *Repository) APIFormatLegacy(permission *apiv1types.RepositoryPermission
|
||||
// AvatarUrl: r.AvatarLink(),
|
||||
}
|
||||
if r.IsFork {
|
||||
p := &apiv1types.RepositoryPermission{Pull: true}
|
||||
p := &api.Permission{Pull: true}
|
||||
if len(user) != 0 {
|
||||
accessMode := Handle.Permissions().AccessMode(
|
||||
context.TODO(),
|
||||
@@ -2416,7 +2415,7 @@ func GetWatchers(repoID int64) ([]*Watch, error) {
|
||||
func (r *Repository) GetWatchers(page int) ([]*User, error) {
|
||||
users := make([]*User, 0, ItemsPerPage)
|
||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("watch.repo_id=?", r.ID)
|
||||
if conf.UsePostgreSQL {
|
||||
if conf.UsePostgreSQL || conf.UseMSSQL {
|
||||
sess = sess.Join("LEFT", "watch", `"user".id=watch.user_id`)
|
||||
} else {
|
||||
sess = sess.Join("LEFT", "watch", "user.id=watch.user_id")
|
||||
@@ -2516,7 +2515,7 @@ func IsStaring(userID, repoID int64) bool {
|
||||
func (r *Repository) GetStargazers(page int) ([]*User, error) {
|
||||
users := make([]*User, 0, ItemsPerPage)
|
||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("star.repo_id=?", r.ID)
|
||||
if conf.UsePostgreSQL {
|
||||
if conf.UsePostgreSQL || conf.UseMSSQL {
|
||||
sess = sess.Join("LEFT", "star", `"user".id=star.uid`)
|
||||
} else {
|
||||
sess = sess.Join("LEFT", "star", "user.id=star.uid")
|
||||
@@ -2609,7 +2608,7 @@ func ForkRepository(doer, owner *User, baseRepo *Repository, name, desc string)
|
||||
if err = repo.UpdateSize(); err != nil {
|
||||
log.Error("UpdateSize [repo_id: %d]: %v", repo.ID, err)
|
||||
}
|
||||
if err = PrepareWebhooks(baseRepo, HookEventTypeFork, &apiv1types.WebhookForkPayload{
|
||||
if err = PrepareWebhooks(baseRepo, HookEventTypeFork, &api.ForkPayload{
|
||||
Forkee: repo.APIFormatLegacy(nil),
|
||||
Repo: baseRepo.APIFormatLegacy(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"github.com/cockroachdb/errors"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
)
|
||||
|
||||
// Collaboration represent the relation between an individual and a repository.
|
||||
@@ -87,10 +87,10 @@ type Collaborator struct {
|
||||
Collaboration *Collaboration
|
||||
}
|
||||
|
||||
func (c *Collaborator) APIFormat() *apiv1types.RepositoryCollaborator {
|
||||
return &apiv1types.RepositoryCollaborator{
|
||||
func (c *Collaborator) APIFormat() *api.Collaborator {
|
||||
return &api.Collaborator{
|
||||
User: c.User.APIFormat(),
|
||||
Permissions: apiv1types.RepositoryPermission{
|
||||
Permissions: api.Permission{
|
||||
Admin: c.Collaboration.Mode >= AccessModeAdmin,
|
||||
Push: c.Collaboration.Mode >= AccessModeWrite,
|
||||
Pull: c.Collaboration.Mode >= AccessModeRead,
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
"gogs.io/gogs/internal/repoutil"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
)
|
||||
|
||||
// BeforeCreate implements the GORM create hook.
|
||||
@@ -36,19 +36,19 @@ func (r *Repository) AfterFind(_ *gorm.DB) error {
|
||||
}
|
||||
|
||||
type RepositoryAPIFormatOptions struct {
|
||||
Permission *apiv1types.RepositoryPermission
|
||||
Parent *apiv1types.Repository
|
||||
Permission *api.Permission
|
||||
Parent *api.Repository
|
||||
}
|
||||
|
||||
// APIFormat returns the API format of a repository.
|
||||
func (r *Repository) APIFormat(owner *User, opts ...RepositoryAPIFormatOptions) *apiv1types.Repository {
|
||||
func (r *Repository) APIFormat(owner *User, opts ...RepositoryAPIFormatOptions) *api.Repository {
|
||||
var opt RepositoryAPIFormatOptions
|
||||
if len(opts) > 0 {
|
||||
opt = opts[0]
|
||||
}
|
||||
|
||||
cloneLink := repoutil.NewCloneLink(owner.Name, r.Name, false)
|
||||
return &apiv1types.Repository{
|
||||
return &api.Repository{
|
||||
ID: r.ID,
|
||||
Owner: owner.APIFormat(),
|
||||
Name: r.Name,
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/go-macaron/binding"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
"gorm.io/gorm"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
@@ -22,7 +23,6 @@ import (
|
||||
"gogs.io/gogs/internal/markup"
|
||||
"gogs.io/gogs/internal/osutil"
|
||||
"gogs.io/gogs/internal/repoutil"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/strutil"
|
||||
"gogs.io/gogs/internal/tool"
|
||||
"gogs.io/gogs/internal/userutil"
|
||||
@@ -1297,14 +1297,14 @@ func (u *User) IsOrganization() bool {
|
||||
}
|
||||
|
||||
// APIFormat returns the API format of a user.
|
||||
func (u *User) APIFormat() *apiv1types.User {
|
||||
return &apiv1types.User{
|
||||
func (u *User) APIFormat() *api.User {
|
||||
return &api.User{
|
||||
ID: u.ID,
|
||||
UserName: u.Name,
|
||||
Login: u.Name,
|
||||
FullName: u.FullName,
|
||||
Email: u.Email,
|
||||
AvatarURL: u.AvatarURL(),
|
||||
AvatarUrl: u.AvatarURL(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
@@ -14,14 +13,16 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/google/uuid"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
log "unknwon.dev/clog/v2"
|
||||
"xorm.io/xorm"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/errutil"
|
||||
"gogs.io/gogs/internal/httplib"
|
||||
"gogs.io/gogs/internal/netutil"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/sync"
|
||||
"gogs.io/gogs/internal/testutil"
|
||||
)
|
||||
@@ -125,7 +126,7 @@ func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "events":
|
||||
w.HookEvent = &HookEvent{}
|
||||
if err = json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
|
||||
if err = jsoniter.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
|
||||
log.Error("Unmarshal [%d]: %v", w.ID, err)
|
||||
}
|
||||
case "created_unix":
|
||||
@@ -137,7 +138,7 @@ func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
|
||||
|
||||
func (w *Webhook) SlackMeta() *SlackMeta {
|
||||
s := &SlackMeta{}
|
||||
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
|
||||
if err := jsoniter.Unmarshal([]byte(w.Meta), s); err != nil {
|
||||
log.Error("Failed to get Slack meta [webhook_id: %d]: %v", w.ID, err)
|
||||
}
|
||||
return s
|
||||
@@ -150,7 +151,7 @@ func (w *Webhook) History(page int) ([]*HookTask, error) {
|
||||
|
||||
// UpdateEvent handles conversion from HookEvent to Events.
|
||||
func (w *Webhook) UpdateEvent() error {
|
||||
data, err := json.Marshal(w.HookEvent)
|
||||
data, err := jsoniter.Marshal(w.HookEvent)
|
||||
w.Events = string(data)
|
||||
return err
|
||||
}
|
||||
@@ -429,21 +430,21 @@ type HookResponse struct {
|
||||
|
||||
// HookTask represents a hook task.
|
||||
type HookTask struct {
|
||||
ID int64
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
HookID int64
|
||||
UUID string
|
||||
Type HookTaskType
|
||||
URL string `xorm:"TEXT"`
|
||||
Signature string `xorm:"TEXT"`
|
||||
apiv1types.WebhookPayloader `xorm:"-" json:"-" gorm:"-"`
|
||||
PayloadContent string `xorm:"TEXT"`
|
||||
ContentType HookContentType
|
||||
EventType HookEventType
|
||||
IsSSL bool
|
||||
IsDelivered bool
|
||||
Delivered int64
|
||||
DeliveredString string `xorm:"-" json:"-" gorm:"-"`
|
||||
ID int64
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
HookID int64
|
||||
UUID string
|
||||
Type HookTaskType
|
||||
URL string `xorm:"TEXT"`
|
||||
Signature string `xorm:"TEXT"`
|
||||
api.Payloader `xorm:"-" json:"-" gorm:"-"`
|
||||
PayloadContent string `xorm:"TEXT"`
|
||||
ContentType HookContentType
|
||||
EventType HookEventType
|
||||
IsSSL bool
|
||||
IsDelivered bool
|
||||
Delivered int64
|
||||
DeliveredString string `xorm:"-" json:"-" gorm:"-"`
|
||||
|
||||
// History info.
|
||||
IsSucceed bool
|
||||
@@ -474,7 +475,7 @@ func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
|
||||
t.RequestInfo = &HookRequest{}
|
||||
if err = json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
|
||||
if err = jsoniter.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
|
||||
log.Error("Unmarshal[%d]: %v", t.ID, err)
|
||||
}
|
||||
|
||||
@@ -484,14 +485,14 @@ func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
|
||||
t.ResponseInfo = &HookResponse{}
|
||||
if err = json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
|
||||
if err = jsoniter.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
|
||||
log.Error("Unmarshal [%d]: %v", t.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HookTask) ToJSON(v any) string {
|
||||
p, err := json.Marshal(v)
|
||||
p, err := jsoniter.Marshal(v)
|
||||
if err != nil {
|
||||
log.Error("Marshal [%d]: %v", t.ID, err)
|
||||
}
|
||||
@@ -558,12 +559,12 @@ func UpdateHookTask(t *HookTask) error {
|
||||
}
|
||||
|
||||
// prepareHookTasks adds list of webhooks to task queue.
|
||||
func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p apiv1types.WebhookPayloader, webhooks []*Webhook) (err error) {
|
||||
func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) {
|
||||
if len(webhooks) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var payloader apiv1types.WebhookPayloader
|
||||
var payloader api.Payloader
|
||||
for _, w := range webhooks {
|
||||
switch event {
|
||||
case HookEventTypeCreate:
|
||||
@@ -633,15 +634,15 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p apiv1ty
|
||||
}
|
||||
|
||||
if err = createHookTask(e, &HookTask{
|
||||
RepoID: repo.ID,
|
||||
HookID: w.ID,
|
||||
Type: w.HookTaskType,
|
||||
URL: w.URL,
|
||||
Signature: signature,
|
||||
WebhookPayloader: payloader,
|
||||
ContentType: w.ContentType,
|
||||
EventType: event,
|
||||
IsSSL: w.IsSSL,
|
||||
RepoID: repo.ID,
|
||||
HookID: w.ID,
|
||||
Type: w.HookTaskType,
|
||||
URL: w.URL,
|
||||
Signature: signature,
|
||||
Payloader: payloader,
|
||||
ContentType: w.ContentType,
|
||||
EventType: event,
|
||||
IsSSL: w.IsSSL,
|
||||
}); err != nil {
|
||||
return errors.Newf("createHookTask: %v", err)
|
||||
}
|
||||
@@ -654,7 +655,7 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p apiv1ty
|
||||
return nil
|
||||
}
|
||||
|
||||
func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p apiv1types.WebhookPayloader) error {
|
||||
func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payloader) error {
|
||||
webhooks, err := getActiveWebhooksByRepoID(e, repo.ID)
|
||||
if err != nil {
|
||||
return errors.Newf("getActiveWebhooksByRepoID [%d]: %v", repo.ID, err)
|
||||
@@ -673,7 +674,7 @@ func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p apiv1typ
|
||||
}
|
||||
|
||||
// PrepareWebhooks adds all active webhooks to task queue.
|
||||
func PrepareWebhooks(repo *Repository, event HookEventType, p apiv1types.WebhookPayloader) error {
|
||||
func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error {
|
||||
// NOTE: To prevent too many cascading changes in a single refactoring PR, we
|
||||
// choose to ignore this function in tests.
|
||||
if x == nil && testutil.InTest {
|
||||
@@ -683,7 +684,7 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p apiv1types.Webhook
|
||||
}
|
||||
|
||||
// TestWebhook adds the test webhook matches the ID to task queue.
|
||||
func TestWebhook(repo *Repository, event HookEventType, p apiv1types.WebhookPayloader, webhookID int64) error {
|
||||
func TestWebhook(repo *Repository, event HookEventType, p api.Payloader, webhookID int64) error {
|
||||
webhook, err := GetWebhookOfRepoByID(repo.ID, webhookID)
|
||||
if err != nil {
|
||||
return errors.Newf("GetWebhookOfRepoByID [repo_id: %d, id: %d]: %v", repo.ID, webhookID, err)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gogs/git-module"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
"github.com/gogs/git-module"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -39,7 +39,7 @@ type DingtalkPayload struct {
|
||||
}
|
||||
|
||||
func (p *DingtalkPayload) JSONPayload() ([]byte, error) {
|
||||
data, err := json.MarshalIndent(p, "", " ")
|
||||
data, err := jsoniter.MarshalIndent(p, "", " ")
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
@@ -55,31 +55,31 @@ func NewDingtalkActionCard(singleTitle, singleURL string) DingtalkActionCard {
|
||||
}
|
||||
|
||||
// TODO: add content
|
||||
func GetDingtalkPayload(p apiv1types.WebhookPayloader, event HookEventType) (payload *DingtalkPayload, err error) {
|
||||
func GetDingtalkPayload(p api.Payloader, event HookEventType) (payload *DingtalkPayload, err error) {
|
||||
switch event {
|
||||
case HookEventTypeCreate:
|
||||
payload = getDingtalkCreatePayload(p.(*apiv1types.WebhookCreatePayload))
|
||||
payload = getDingtalkCreatePayload(p.(*api.CreatePayload))
|
||||
case HookEventTypeDelete:
|
||||
payload = getDingtalkDeletePayload(p.(*apiv1types.WebhookDeletePayload))
|
||||
payload = getDingtalkDeletePayload(p.(*api.DeletePayload))
|
||||
case HookEventTypeFork:
|
||||
payload = getDingtalkForkPayload(p.(*apiv1types.WebhookForkPayload))
|
||||
payload = getDingtalkForkPayload(p.(*api.ForkPayload))
|
||||
case HookEventTypePush:
|
||||
payload = getDingtalkPushPayload(p.(*apiv1types.WebhookPushPayload))
|
||||
payload = getDingtalkPushPayload(p.(*api.PushPayload))
|
||||
case HookEventTypeIssues:
|
||||
payload = getDingtalkIssuesPayload(p.(*apiv1types.WebhookIssuesPayload))
|
||||
payload = getDingtalkIssuesPayload(p.(*api.IssuesPayload))
|
||||
case HookEventTypeIssueComment:
|
||||
payload = getDingtalkIssueCommentPayload(p.(*apiv1types.WebhookIssueCommentPayload))
|
||||
payload = getDingtalkIssueCommentPayload(p.(*api.IssueCommentPayload))
|
||||
case HookEventTypePullRequest:
|
||||
payload = getDingtalkPullRequestPayload(p.(*apiv1types.WebhookPullRequestPayload))
|
||||
payload = getDingtalkPullRequestPayload(p.(*api.PullRequestPayload))
|
||||
case HookEventTypeRelease:
|
||||
payload = getDingtalkReleasePayload(p.(*apiv1types.WebhookReleasePayload))
|
||||
payload = getDingtalkReleasePayload(p.(*api.ReleasePayload))
|
||||
default:
|
||||
return nil, errors.Errorf("unexpected event %q", event)
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func getDingtalkCreatePayload(p *apiv1types.WebhookCreatePayload) *DingtalkPayload {
|
||||
func getDingtalkCreatePayload(p *api.CreatePayload) *DingtalkPayload {
|
||||
refName := git.RefShortName(p.Ref)
|
||||
refType := strings.Title(p.RefType)
|
||||
|
||||
@@ -94,7 +94,7 @@ func getDingtalkCreatePayload(p *apiv1types.WebhookCreatePayload) *DingtalkPaylo
|
||||
}
|
||||
}
|
||||
|
||||
func getDingtalkDeletePayload(p *apiv1types.WebhookDeletePayload) *DingtalkPayload {
|
||||
func getDingtalkDeletePayload(p *api.DeletePayload) *DingtalkPayload {
|
||||
refName := git.RefShortName(p.Ref)
|
||||
refType := strings.Title(p.RefType)
|
||||
|
||||
@@ -109,7 +109,7 @@ func getDingtalkDeletePayload(p *apiv1types.WebhookDeletePayload) *DingtalkPaylo
|
||||
}
|
||||
}
|
||||
|
||||
func getDingtalkForkPayload(p *apiv1types.WebhookForkPayload) *DingtalkPayload {
|
||||
func getDingtalkForkPayload(p *api.ForkPayload) *DingtalkPayload {
|
||||
actionCard := NewDingtalkActionCard("View Fork", p.Forkee.HTMLURL)
|
||||
actionCard.Text += "# Repo Fork Event"
|
||||
actionCard.Text += "\n- From Repo: **" + MarkdownLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) + "**"
|
||||
@@ -121,7 +121,7 @@ func getDingtalkForkPayload(p *apiv1types.WebhookForkPayload) *DingtalkPayload {
|
||||
}
|
||||
}
|
||||
|
||||
func getDingtalkPushPayload(p *apiv1types.WebhookPushPayload) *DingtalkPayload {
|
||||
func getDingtalkPushPayload(p *api.PushPayload) *DingtalkPayload {
|
||||
refName := git.RefShortName(p.Ref)
|
||||
|
||||
pusher := p.Pusher.FullName
|
||||
@@ -150,7 +150,7 @@ func getDingtalkPushPayload(p *apiv1types.WebhookPushPayload) *DingtalkPayload {
|
||||
}
|
||||
}
|
||||
|
||||
func getDingtalkIssuesPayload(p *apiv1types.WebhookIssuesPayload) *DingtalkPayload {
|
||||
func getDingtalkIssuesPayload(p *api.IssuesPayload) *DingtalkPayload {
|
||||
issueName := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)
|
||||
issueURL := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index)
|
||||
|
||||
@@ -159,11 +159,11 @@ func getDingtalkIssuesPayload(p *apiv1types.WebhookIssuesPayload) *DingtalkPaylo
|
||||
actionCard.Text += "\n- Issue: **" + MarkdownLinkFormatter(issueURL, issueName) + "**"
|
||||
|
||||
switch p.Action {
|
||||
case apiv1types.WebhookIssueAssigned:
|
||||
case api.HOOK_ISSUE_ASSIGNED:
|
||||
actionCard.Text += "\n- New Assignee: **" + p.Issue.Assignee.UserName + "**"
|
||||
case apiv1types.WebhookIssueMilestoned:
|
||||
case api.HOOK_ISSUE_MILESTONED:
|
||||
actionCard.Text += "\n- New Milestone: **" + p.Issue.Milestone.Title + "**"
|
||||
case apiv1types.WebhookIssueLabelUpdated:
|
||||
case api.HOOK_ISSUE_LABEL_UPDATED:
|
||||
if len(p.Issue.Labels) > 0 {
|
||||
labels := make([]string, len(p.Issue.Labels))
|
||||
for i, label := range p.Issue.Labels {
|
||||
@@ -185,10 +185,10 @@ func getDingtalkIssuesPayload(p *apiv1types.WebhookIssuesPayload) *DingtalkPaylo
|
||||
}
|
||||
}
|
||||
|
||||
func getDingtalkIssueCommentPayload(p *apiv1types.WebhookIssueCommentPayload) *DingtalkPayload {
|
||||
func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) *DingtalkPayload {
|
||||
issueName := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
|
||||
commentURL := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
|
||||
if p.Action != apiv1types.WebhookIssueCommentDeleted {
|
||||
if p.Action != api.HOOK_ISSUE_COMMENT_DELETED {
|
||||
commentURL += "#" + CommentHashTag(p.Comment.ID)
|
||||
}
|
||||
|
||||
@@ -206,9 +206,9 @@ func getDingtalkIssueCommentPayload(p *apiv1types.WebhookIssueCommentPayload) *D
|
||||
}
|
||||
}
|
||||
|
||||
func getDingtalkPullRequestPayload(p *apiv1types.WebhookPullRequestPayload) *DingtalkPayload {
|
||||
func getDingtalkPullRequestPayload(p *api.PullRequestPayload) *DingtalkPayload {
|
||||
title := "# Pull Request " + strings.Title(string(p.Action))
|
||||
if p.Action == apiv1types.WebhookIssueClosed && p.PullRequest.HasMerged {
|
||||
if p.Action == api.HOOK_ISSUE_CLOSED && p.PullRequest.HasMerged {
|
||||
title = "# Pull Request Merged"
|
||||
}
|
||||
|
||||
@@ -216,11 +216,11 @@ func getDingtalkPullRequestPayload(p *apiv1types.WebhookPullRequestPayload) *Din
|
||||
|
||||
content := "- PR: " + MarkdownLinkFormatter(pullRequestURL, fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
|
||||
switch p.Action {
|
||||
case apiv1types.WebhookIssueAssigned:
|
||||
case api.HOOK_ISSUE_ASSIGNED:
|
||||
content += "\n- New Assignee: **" + p.PullRequest.Assignee.UserName + "**"
|
||||
case apiv1types.WebhookIssueMilestoned:
|
||||
case api.HOOK_ISSUE_MILESTONED:
|
||||
content += "\n- New Milestone: *" + p.PullRequest.Milestone.Title + "*"
|
||||
case apiv1types.WebhookIssueLabelUpdated:
|
||||
case api.HOOK_ISSUE_LABEL_UPDATED:
|
||||
labels := make([]string, len(p.PullRequest.Labels))
|
||||
for i, label := range p.PullRequest.Labels {
|
||||
labels[i] = "**" + label.Name + "**"
|
||||
@@ -231,7 +231,7 @@ func getDingtalkPullRequestPayload(p *apiv1types.WebhookPullRequestPayload) *Din
|
||||
actionCard := NewDingtalkActionCard("View Pull Request", pullRequestURL)
|
||||
actionCard.Text += title + "\n" + content
|
||||
|
||||
if p.Action == apiv1types.WebhookIssueOpened || p.Action == apiv1types.WebhookIssueEdited {
|
||||
if p.Action == api.HOOK_ISSUE_OPENED || p.Action == api.HOOK_ISSUE_EDITED {
|
||||
actionCard.Text += "\n> " + p.PullRequest.Body
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ func getDingtalkPullRequestPayload(p *apiv1types.WebhookPullRequestPayload) *Din
|
||||
}
|
||||
}
|
||||
|
||||
func getDingtalkReleasePayload(p *apiv1types.WebhookReleasePayload) *DingtalkPayload {
|
||||
func getDingtalkReleasePayload(p *api.ReleasePayload) *DingtalkPayload {
|
||||
releaseURL := p.Repository.HTMLURL + "/src/" + p.Release.TagName
|
||||
|
||||
author := p.Release.Author.FullName
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
||||
"github.com/gogs/git-module"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
)
|
||||
|
||||
type DiscordEmbedFooterObject struct {
|
||||
@@ -46,7 +47,7 @@ type DiscordPayload struct {
|
||||
}
|
||||
|
||||
func (p *DiscordPayload) JSONPayload() ([]byte, error) {
|
||||
data, err := json.MarshalIndent(p, "", " ")
|
||||
data, err := jsoniter.MarshalIndent(p, "", " ")
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
@@ -66,7 +67,7 @@ func DiscordSHALinkFormatter(url, text string) string {
|
||||
}
|
||||
|
||||
// getDiscordCreatePayload composes Discord payload for create new branch or tag.
|
||||
func getDiscordCreatePayload(p *apiv1types.WebhookCreatePayload) *DiscordPayload {
|
||||
func getDiscordCreatePayload(p *api.CreatePayload) *DiscordPayload {
|
||||
refName := git.RefShortName(p.Ref)
|
||||
repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
|
||||
refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
|
||||
@@ -77,14 +78,14 @@ func getDiscordCreatePayload(p *apiv1types.WebhookCreatePayload) *DiscordPayload
|
||||
URL: conf.Server.ExternalURL + p.Sender.UserName,
|
||||
Author: &DiscordEmbedAuthorObject{
|
||||
Name: p.Sender.UserName,
|
||||
IconURL: p.Sender.AvatarURL,
|
||||
IconURL: p.Sender.AvatarUrl,
|
||||
},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
// getDiscordDeletePayload composes Discord payload for delete a branch or tag.
|
||||
func getDiscordDeletePayload(p *apiv1types.WebhookDeletePayload) *DiscordPayload {
|
||||
func getDiscordDeletePayload(p *api.DeletePayload) *DiscordPayload {
|
||||
refName := git.RefShortName(p.Ref)
|
||||
repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
|
||||
content := fmt.Sprintf("Deleted %s: %s/%s", p.RefType, repoLink, refName)
|
||||
@@ -94,14 +95,14 @@ func getDiscordDeletePayload(p *apiv1types.WebhookDeletePayload) *DiscordPayload
|
||||
URL: conf.Server.ExternalURL + p.Sender.UserName,
|
||||
Author: &DiscordEmbedAuthorObject{
|
||||
Name: p.Sender.UserName,
|
||||
IconURL: p.Sender.AvatarURL,
|
||||
IconURL: p.Sender.AvatarUrl,
|
||||
},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
// getDiscordForkPayload composes Discord payload for forked by a repository.
|
||||
func getDiscordForkPayload(p *apiv1types.WebhookForkPayload) *DiscordPayload {
|
||||
func getDiscordForkPayload(p *api.ForkPayload) *DiscordPayload {
|
||||
baseLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
|
||||
forkLink := DiscordLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName)
|
||||
content := fmt.Sprintf("%s is forked to %s", baseLink, forkLink)
|
||||
@@ -111,13 +112,13 @@ func getDiscordForkPayload(p *apiv1types.WebhookForkPayload) *DiscordPayload {
|
||||
URL: conf.Server.ExternalURL + p.Sender.UserName,
|
||||
Author: &DiscordEmbedAuthorObject{
|
||||
Name: p.Sender.UserName,
|
||||
IconURL: p.Sender.AvatarURL,
|
||||
IconURL: p.Sender.AvatarUrl,
|
||||
},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func getDiscordPushPayload(p *apiv1types.WebhookPushPayload, slack *SlackMeta) *DiscordPayload {
|
||||
func getDiscordPushPayload(p *api.PushPayload, slack *SlackMeta) *DiscordPayload {
|
||||
// n new commits
|
||||
var (
|
||||
branchName = git.RefShortName(p.Ref)
|
||||
@@ -161,37 +162,37 @@ func getDiscordPushPayload(p *apiv1types.WebhookPushPayload, slack *SlackMeta) *
|
||||
Color: int(color),
|
||||
Author: &DiscordEmbedAuthorObject{
|
||||
Name: p.Sender.UserName,
|
||||
IconURL: p.Sender.AvatarURL,
|
||||
IconURL: p.Sender.AvatarUrl,
|
||||
},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func getDiscordIssuesPayload(p *apiv1types.WebhookIssuesPayload, slack *SlackMeta) *DiscordPayload {
|
||||
func getDiscordIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) *DiscordPayload {
|
||||
title := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)
|
||||
url := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index)
|
||||
content := ""
|
||||
fields := make([]*DiscordEmbedFieldObject, 0, 1)
|
||||
switch p.Action {
|
||||
case apiv1types.WebhookIssueOpened:
|
||||
case api.HOOK_ISSUE_OPENED:
|
||||
title = "New issue: " + title
|
||||
content = p.Issue.Body
|
||||
case apiv1types.WebhookIssueClosed:
|
||||
case api.HOOK_ISSUE_CLOSED:
|
||||
title = "Issue closed: " + title
|
||||
case apiv1types.WebhookIssueReopened:
|
||||
case api.HOOK_ISSUE_REOPENED:
|
||||
title = "Issue re-opened: " + title
|
||||
case apiv1types.WebhookIssueEdited:
|
||||
case api.HOOK_ISSUE_EDITED:
|
||||
title = "Issue edited: " + title
|
||||
content = p.Issue.Body
|
||||
case apiv1types.WebhookIssueAssigned:
|
||||
case api.HOOK_ISSUE_ASSIGNED:
|
||||
title = "Issue assigned: " + title
|
||||
fields = []*DiscordEmbedFieldObject{{
|
||||
Name: "New Assignee",
|
||||
Value: p.Issue.Assignee.UserName,
|
||||
}}
|
||||
case apiv1types.WebhookIssueUnassigned:
|
||||
case api.HOOK_ISSUE_UNASSIGNED:
|
||||
title = "Issue unassigned: " + title
|
||||
case apiv1types.WebhookIssueLabelUpdated:
|
||||
case api.HOOK_ISSUE_LABEL_UPDATED:
|
||||
title = "Issue labels updated: " + title
|
||||
labels := make([]string, len(p.Issue.Labels))
|
||||
for i := range p.Issue.Labels {
|
||||
@@ -204,17 +205,17 @@ func getDiscordIssuesPayload(p *apiv1types.WebhookIssuesPayload, slack *SlackMet
|
||||
Name: "Labels",
|
||||
Value: strings.Join(labels, ", "),
|
||||
}}
|
||||
case apiv1types.WebhookIssueLabelCleared:
|
||||
case api.HOOK_ISSUE_LABEL_CLEARED:
|
||||
title = "Issue labels cleared: " + title
|
||||
case apiv1types.WebhookIssueSynchronized:
|
||||
case api.HOOK_ISSUE_SYNCHRONIZED:
|
||||
title = "Issue synchronized: " + title
|
||||
case apiv1types.WebhookIssueMilestoned:
|
||||
case api.HOOK_ISSUE_MILESTONED:
|
||||
title = "Issue milestoned: " + title
|
||||
fields = []*DiscordEmbedFieldObject{{
|
||||
Name: "New Milestone",
|
||||
Value: p.Issue.Milestone.Title,
|
||||
}}
|
||||
case apiv1types.WebhookIssueDemilestoned:
|
||||
case api.HOOK_ISSUE_DEMILESTONED:
|
||||
title = "Issue demilestoned: " + title
|
||||
}
|
||||
|
||||
@@ -232,26 +233,26 @@ func getDiscordIssuesPayload(p *apiv1types.WebhookIssuesPayload, slack *SlackMet
|
||||
},
|
||||
Author: &DiscordEmbedAuthorObject{
|
||||
Name: p.Sender.UserName,
|
||||
IconURL: p.Sender.AvatarURL,
|
||||
IconURL: p.Sender.AvatarUrl,
|
||||
},
|
||||
Fields: fields,
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func getDiscordIssueCommentPayload(p *apiv1types.WebhookIssueCommentPayload, slack *SlackMeta) *DiscordPayload {
|
||||
func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) *DiscordPayload {
|
||||
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))
|
||||
content := ""
|
||||
fields := make([]*DiscordEmbedFieldObject, 0, 1)
|
||||
switch p.Action {
|
||||
case apiv1types.WebhookIssueCommentCreated:
|
||||
case api.HOOK_ISSUE_COMMENT_CREATED:
|
||||
title = "New comment: " + title
|
||||
content = p.Comment.Body
|
||||
case apiv1types.WebhookIssueCommentEdited:
|
||||
case api.HOOK_ISSUE_COMMENT_EDITED:
|
||||
title = "Comment edited: " + title
|
||||
content = p.Comment.Body
|
||||
case apiv1types.WebhookIssueCommentDeleted:
|
||||
case api.HOOK_ISSUE_COMMENT_DELETED:
|
||||
title = "Comment deleted: " + title
|
||||
url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
|
||||
content = p.Comment.Body
|
||||
@@ -271,42 +272,42 @@ func getDiscordIssueCommentPayload(p *apiv1types.WebhookIssueCommentPayload, sla
|
||||
},
|
||||
Author: &DiscordEmbedAuthorObject{
|
||||
Name: p.Sender.UserName,
|
||||
IconURL: p.Sender.AvatarURL,
|
||||
IconURL: p.Sender.AvatarUrl,
|
||||
},
|
||||
Fields: fields,
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func getDiscordPullRequestPayload(p *apiv1types.WebhookPullRequestPayload, slack *SlackMeta) *DiscordPayload {
|
||||
func getDiscordPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) *DiscordPayload {
|
||||
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
|
||||
url := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
|
||||
content := ""
|
||||
fields := make([]*DiscordEmbedFieldObject, 0, 1)
|
||||
switch p.Action {
|
||||
case apiv1types.WebhookIssueOpened:
|
||||
case api.HOOK_ISSUE_OPENED:
|
||||
title = "New pull request: " + title
|
||||
content = p.PullRequest.Body
|
||||
case apiv1types.WebhookIssueClosed:
|
||||
case api.HOOK_ISSUE_CLOSED:
|
||||
if p.PullRequest.HasMerged {
|
||||
title = "Pull request merged: " + title
|
||||
} else {
|
||||
title = "Pull request closed: " + title
|
||||
}
|
||||
case apiv1types.WebhookIssueReopened:
|
||||
case api.HOOK_ISSUE_REOPENED:
|
||||
title = "Pull request re-opened: " + title
|
||||
case apiv1types.WebhookIssueEdited:
|
||||
case api.HOOK_ISSUE_EDITED:
|
||||
title = "Pull request edited: " + title
|
||||
content = p.PullRequest.Body
|
||||
case apiv1types.WebhookIssueAssigned:
|
||||
case api.HOOK_ISSUE_ASSIGNED:
|
||||
title = "Pull request assigned: " + title
|
||||
fields = []*DiscordEmbedFieldObject{{
|
||||
Name: "New Assignee",
|
||||
Value: p.PullRequest.Assignee.UserName,
|
||||
}}
|
||||
case apiv1types.WebhookIssueUnassigned:
|
||||
case api.HOOK_ISSUE_UNASSIGNED:
|
||||
title = "Pull request unassigned: " + title
|
||||
case apiv1types.WebhookIssueLabelUpdated:
|
||||
case api.HOOK_ISSUE_LABEL_UPDATED:
|
||||
title = "Pull request labels updated: " + title
|
||||
labels := make([]string, len(p.PullRequest.Labels))
|
||||
for i := range p.PullRequest.Labels {
|
||||
@@ -316,17 +317,17 @@ func getDiscordPullRequestPayload(p *apiv1types.WebhookPullRequestPayload, slack
|
||||
Name: "Labels",
|
||||
Value: strings.Join(labels, ", "),
|
||||
}}
|
||||
case apiv1types.WebhookIssueLabelCleared:
|
||||
case api.HOOK_ISSUE_LABEL_CLEARED:
|
||||
title = "Pull request labels cleared: " + title
|
||||
case apiv1types.WebhookIssueSynchronized:
|
||||
case api.HOOK_ISSUE_SYNCHRONIZED:
|
||||
title = "Pull request synchronized: " + title
|
||||
case apiv1types.WebhookIssueMilestoned:
|
||||
case api.HOOK_ISSUE_MILESTONED:
|
||||
title = "Pull request milestoned: " + title
|
||||
fields = []*DiscordEmbedFieldObject{{
|
||||
Name: "New Milestone",
|
||||
Value: p.PullRequest.Milestone.Title,
|
||||
}}
|
||||
case apiv1types.WebhookIssueDemilestoned:
|
||||
case api.HOOK_ISSUE_DEMILESTONED:
|
||||
title = "Pull request demilestoned: " + title
|
||||
}
|
||||
|
||||
@@ -344,14 +345,14 @@ func getDiscordPullRequestPayload(p *apiv1types.WebhookPullRequestPayload, slack
|
||||
},
|
||||
Author: &DiscordEmbedAuthorObject{
|
||||
Name: p.Sender.UserName,
|
||||
IconURL: p.Sender.AvatarURL,
|
||||
IconURL: p.Sender.AvatarUrl,
|
||||
},
|
||||
Fields: fields,
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func getDiscordReleasePayload(p *apiv1types.WebhookReleasePayload) *DiscordPayload {
|
||||
func getDiscordReleasePayload(p *api.ReleasePayload) *DiscordPayload {
|
||||
repoLink := DiscordLinkFormatter(p.Repository.HTMLURL, p.Repository.Name)
|
||||
refLink := DiscordLinkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName)
|
||||
content := fmt.Sprintf("Published new release %s of %s", refLink, repoLink)
|
||||
@@ -361,35 +362,35 @@ func getDiscordReleasePayload(p *apiv1types.WebhookReleasePayload) *DiscordPaylo
|
||||
URL: conf.Server.ExternalURL + p.Sender.UserName,
|
||||
Author: &DiscordEmbedAuthorObject{
|
||||
Name: p.Sender.UserName,
|
||||
IconURL: p.Sender.AvatarURL,
|
||||
IconURL: p.Sender.AvatarUrl,
|
||||
},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func GetDiscordPayload(p apiv1types.WebhookPayloader, event HookEventType, meta string) (payload *DiscordPayload, err error) {
|
||||
func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (payload *DiscordPayload, err error) {
|
||||
slack := &SlackMeta{}
|
||||
if err := json.Unmarshal([]byte(meta), slack); err != nil {
|
||||
return nil, errors.Newf("unmarshal: %v", err)
|
||||
if err := jsoniter.Unmarshal([]byte(meta), &slack); err != nil {
|
||||
return nil, errors.Newf("jsoniter.Unmarshal: %v", err)
|
||||
}
|
||||
|
||||
switch event {
|
||||
case HookEventTypeCreate:
|
||||
payload = getDiscordCreatePayload(p.(*apiv1types.WebhookCreatePayload))
|
||||
payload = getDiscordCreatePayload(p.(*api.CreatePayload))
|
||||
case HookEventTypeDelete:
|
||||
payload = getDiscordDeletePayload(p.(*apiv1types.WebhookDeletePayload))
|
||||
payload = getDiscordDeletePayload(p.(*api.DeletePayload))
|
||||
case HookEventTypeFork:
|
||||
payload = getDiscordForkPayload(p.(*apiv1types.WebhookForkPayload))
|
||||
payload = getDiscordForkPayload(p.(*api.ForkPayload))
|
||||
case HookEventTypePush:
|
||||
payload = getDiscordPushPayload(p.(*apiv1types.WebhookPushPayload), slack)
|
||||
payload = getDiscordPushPayload(p.(*api.PushPayload), slack)
|
||||
case HookEventTypeIssues:
|
||||
payload = getDiscordIssuesPayload(p.(*apiv1types.WebhookIssuesPayload), slack)
|
||||
payload = getDiscordIssuesPayload(p.(*api.IssuesPayload), slack)
|
||||
case HookEventTypeIssueComment:
|
||||
payload = getDiscordIssueCommentPayload(p.(*apiv1types.WebhookIssueCommentPayload), slack)
|
||||
payload = getDiscordIssueCommentPayload(p.(*api.IssueCommentPayload), slack)
|
||||
case HookEventTypePullRequest:
|
||||
payload = getDiscordPullRequestPayload(p.(*apiv1types.WebhookPullRequestPayload), slack)
|
||||
payload = getDiscordPullRequestPayload(p.(*api.PullRequestPayload), slack)
|
||||
case HookEventTypeRelease:
|
||||
payload = getDiscordReleasePayload(p.(*apiv1types.WebhookReleasePayload))
|
||||
payload = getDiscordReleasePayload(p.(*api.ReleasePayload))
|
||||
default:
|
||||
return nil, errors.Errorf("unexpected event %q", event)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
||||
"github.com/gogs/git-module"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
apiv1types "gogs.io/gogs/internal/route/api/v1/types"
|
||||
)
|
||||
|
||||
type SlackMeta struct {
|
||||
@@ -37,7 +38,7 @@ type SlackPayload struct {
|
||||
}
|
||||
|
||||
func (p *SlackPayload) JSONPayload() ([]byte, error) {
|
||||
data, err := json.MarshalIndent(p, "", " ")
|
||||
data, err := jsoniter.MarshalIndent(p, "", " ")
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
@@ -67,7 +68,7 @@ func SlackLinkFormatter(url, text string) string {
|
||||
}
|
||||
|
||||
// getSlackCreatePayload composes Slack payload for create new branch or tag.
|
||||
func getSlackCreatePayload(p *apiv1types.WebhookCreatePayload) *SlackPayload {
|
||||
func getSlackCreatePayload(p *api.CreatePayload) *SlackPayload {
|
||||
refName := git.RefShortName(p.Ref)
|
||||
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
|
||||
refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
|
||||
@@ -78,7 +79,7 @@ func getSlackCreatePayload(p *apiv1types.WebhookCreatePayload) *SlackPayload {
|
||||
}
|
||||
|
||||
// getSlackDeletePayload composes Slack payload for delete a branch or tag.
|
||||
func getSlackDeletePayload(p *apiv1types.WebhookDeletePayload) *SlackPayload {
|
||||
func getSlackDeletePayload(p *api.DeletePayload) *SlackPayload {
|
||||
refName := git.RefShortName(p.Ref)
|
||||
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
|
||||
text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName)
|
||||
@@ -88,7 +89,7 @@ func getSlackDeletePayload(p *apiv1types.WebhookDeletePayload) *SlackPayload {
|
||||
}
|
||||
|
||||
// getSlackForkPayload composes Slack payload for forked by a repository.
|
||||
func getSlackForkPayload(p *apiv1types.WebhookForkPayload) *SlackPayload {
|
||||
func getSlackForkPayload(p *api.ForkPayload) *SlackPayload {
|
||||
baseLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
|
||||
forkLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName)
|
||||
text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink)
|
||||
@@ -97,7 +98,7 @@ func getSlackForkPayload(p *apiv1types.WebhookForkPayload) *SlackPayload {
|
||||
}
|
||||
}
|
||||
|
||||
func getSlackPushPayload(p *apiv1types.WebhookPushPayload, slack *SlackMeta) *SlackPayload {
|
||||
func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) *SlackPayload {
|
||||
// n new commits
|
||||
var (
|
||||
branchName = git.RefShortName(p.Ref)
|
||||
@@ -142,36 +143,36 @@ func getSlackPushPayload(p *apiv1types.WebhookPushPayload, slack *SlackMeta) *Sl
|
||||
}
|
||||
}
|
||||
|
||||
func getSlackIssuesPayload(p *apiv1types.WebhookIssuesPayload, slack *SlackMeta) *SlackPayload {
|
||||
func getSlackIssuesPayload(p *api.IssuesPayload, slack *SlackMeta) *SlackPayload {
|
||||
senderLink := SlackLinkFormatter(conf.Server.ExternalURL+p.Sender.UserName, p.Sender.UserName)
|
||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index),
|
||||
fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
|
||||
var text, title, attachmentText string
|
||||
switch p.Action {
|
||||
case apiv1types.WebhookIssueOpened:
|
||||
case api.HOOK_ISSUE_OPENED:
|
||||
text = fmt.Sprintf("[%s] New issue created by %s", p.Repository.FullName, senderLink)
|
||||
title = titleLink
|
||||
attachmentText = SlackTextFormatter(p.Issue.Body)
|
||||
case apiv1types.WebhookIssueClosed:
|
||||
case api.HOOK_ISSUE_CLOSED:
|
||||
text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueReopened:
|
||||
case api.HOOK_ISSUE_REOPENED:
|
||||
text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueEdited:
|
||||
case api.HOOK_ISSUE_EDITED:
|
||||
text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
attachmentText = SlackTextFormatter(p.Issue.Body)
|
||||
case apiv1types.WebhookIssueAssigned:
|
||||
case api.HOOK_ISSUE_ASSIGNED:
|
||||
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),
|
||||
titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueUnassigned:
|
||||
case api.HOOK_ISSUE_UNASSIGNED:
|
||||
text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueLabelUpdated:
|
||||
case api.HOOK_ISSUE_LABEL_UPDATED:
|
||||
text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueLabelCleared:
|
||||
case api.HOOK_ISSUE_LABEL_CLEARED:
|
||||
text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueMilestoned:
|
||||
case api.HOOK_ISSUE_MILESTONED:
|
||||
text = fmt.Sprintf("[%s] Issue milestoned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueDemilestoned:
|
||||
case api.HOOK_ISSUE_DEMILESTONED:
|
||||
text = fmt.Sprintf("[%s] Issue demilestoned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
}
|
||||
|
||||
@@ -188,21 +189,21 @@ func getSlackIssuesPayload(p *apiv1types.WebhookIssuesPayload, slack *SlackMeta)
|
||||
}
|
||||
}
|
||||
|
||||
func getSlackIssueCommentPayload(p *apiv1types.WebhookIssueCommentPayload, slack *SlackMeta) *SlackPayload {
|
||||
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) *SlackPayload {
|
||||
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)),
|
||||
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
|
||||
var text, title, attachmentText string
|
||||
switch p.Action {
|
||||
case apiv1types.WebhookIssueCommentCreated:
|
||||
case api.HOOK_ISSUE_COMMENT_CREATED:
|
||||
text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink)
|
||||
title = titleLink
|
||||
attachmentText = SlackTextFormatter(p.Comment.Body)
|
||||
case apiv1types.WebhookIssueCommentEdited:
|
||||
case api.HOOK_ISSUE_COMMENT_EDITED:
|
||||
text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink)
|
||||
title = titleLink
|
||||
attachmentText = SlackTextFormatter(p.Comment.Body)
|
||||
case apiv1types.WebhookIssueCommentDeleted:
|
||||
case api.HOOK_ISSUE_COMMENT_DELETED:
|
||||
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),
|
||||
fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
|
||||
@@ -222,42 +223,42 @@ func getSlackIssueCommentPayload(p *apiv1types.WebhookIssueCommentPayload, slack
|
||||
}
|
||||
}
|
||||
|
||||
func getSlackPullRequestPayload(p *apiv1types.WebhookPullRequestPayload, slack *SlackMeta) *SlackPayload {
|
||||
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) *SlackPayload {
|
||||
senderLink := SlackLinkFormatter(conf.Server.ExternalURL+p.Sender.UserName, p.Sender.UserName)
|
||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
|
||||
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
|
||||
var text, title, attachmentText string
|
||||
switch p.Action {
|
||||
case apiv1types.WebhookIssueOpened:
|
||||
case api.HOOK_ISSUE_OPENED:
|
||||
text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink)
|
||||
title = titleLink
|
||||
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
||||
case apiv1types.WebhookIssueClosed:
|
||||
case api.HOOK_ISSUE_CLOSED:
|
||||
if p.PullRequest.HasMerged {
|
||||
text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
} else {
|
||||
text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
}
|
||||
case apiv1types.WebhookIssueReopened:
|
||||
case api.HOOK_ISSUE_REOPENED:
|
||||
text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueEdited:
|
||||
case api.HOOK_ISSUE_EDITED:
|
||||
text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
||||
case apiv1types.WebhookIssueAssigned:
|
||||
case api.HOOK_ISSUE_ASSIGNED:
|
||||
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),
|
||||
titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueUnassigned:
|
||||
case api.HOOK_ISSUE_UNASSIGNED:
|
||||
text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueLabelUpdated:
|
||||
case api.HOOK_ISSUE_LABEL_UPDATED:
|
||||
text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueLabelCleared:
|
||||
case api.HOOK_ISSUE_LABEL_CLEARED:
|
||||
text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueSynchronized:
|
||||
case api.HOOK_ISSUE_SYNCHRONIZED:
|
||||
text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueMilestoned:
|
||||
case api.HOOK_ISSUE_MILESTONED:
|
||||
text = fmt.Sprintf("[%s] Pull request milestoned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case apiv1types.WebhookIssueDemilestoned:
|
||||
case api.HOOK_ISSUE_DEMILESTONED:
|
||||
text = fmt.Sprintf("[%s] Pull request demilestoned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
}
|
||||
|
||||
@@ -274,7 +275,7 @@ func getSlackPullRequestPayload(p *apiv1types.WebhookPullRequestPayload, slack *
|
||||
}
|
||||
}
|
||||
|
||||
func getSlackReleasePayload(p *apiv1types.WebhookReleasePayload) *SlackPayload {
|
||||
func getSlackReleasePayload(p *api.ReleasePayload) *SlackPayload {
|
||||
repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.Name)
|
||||
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)
|
||||
@@ -283,29 +284,29 @@ func getSlackReleasePayload(p *apiv1types.WebhookReleasePayload) *SlackPayload {
|
||||
}
|
||||
}
|
||||
|
||||
func GetSlackPayload(p apiv1types.WebhookPayloader, event HookEventType, meta string) (payload *SlackPayload, err error) {
|
||||
func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (payload *SlackPayload, err error) {
|
||||
slack := &SlackMeta{}
|
||||
if err := json.Unmarshal([]byte(meta), slack); err != nil {
|
||||
if err := jsoniter.Unmarshal([]byte(meta), &slack); err != nil {
|
||||
return nil, errors.Newf("unmarshal: %v", err)
|
||||
}
|
||||
|
||||
switch event {
|
||||
case HookEventTypeCreate:
|
||||
payload = getSlackCreatePayload(p.(*apiv1types.WebhookCreatePayload))
|
||||
payload = getSlackCreatePayload(p.(*api.CreatePayload))
|
||||
case HookEventTypeDelete:
|
||||
payload = getSlackDeletePayload(p.(*apiv1types.WebhookDeletePayload))
|
||||
payload = getSlackDeletePayload(p.(*api.DeletePayload))
|
||||
case HookEventTypeFork:
|
||||
payload = getSlackForkPayload(p.(*apiv1types.WebhookForkPayload))
|
||||
payload = getSlackForkPayload(p.(*api.ForkPayload))
|
||||
case HookEventTypePush:
|
||||
payload = getSlackPushPayload(p.(*apiv1types.WebhookPushPayload), slack)
|
||||
payload = getSlackPushPayload(p.(*api.PushPayload), slack)
|
||||
case HookEventTypeIssues:
|
||||
payload = getSlackIssuesPayload(p.(*apiv1types.WebhookIssuesPayload), slack)
|
||||
payload = getSlackIssuesPayload(p.(*api.IssuesPayload), slack)
|
||||
case HookEventTypeIssueComment:
|
||||
payload = getSlackIssueCommentPayload(p.(*apiv1types.WebhookIssueCommentPayload), slack)
|
||||
payload = getSlackIssueCommentPayload(p.(*api.IssueCommentPayload), slack)
|
||||
case HookEventTypePullRequest:
|
||||
payload = getSlackPullRequestPayload(p.(*apiv1types.WebhookPullRequestPayload), slack)
|
||||
payload = getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
|
||||
case HookEventTypeRelease:
|
||||
payload = getSlackReleasePayload(p.(*apiv1types.WebhookReleasePayload))
|
||||
payload = getSlackReleasePayload(p.(*api.ReleasePayload))
|
||||
default:
|
||||
return nil, errors.Errorf("unexpected event %q", event)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlserver"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
@@ -28,6 +29,22 @@ func ParsePostgreSQLHostPort(info string) (host, port string) {
|
||||
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.
|
||||
func NewDSN(opts conf.DatabaseOpts) (dsn string, err error) {
|
||||
// In case the database name contains "?" with some parameters
|
||||
@@ -51,6 +68,11 @@ 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'",
|
||||
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":
|
||||
dsn = "file:" + opts.Path + "?cache=shared&mode=rwc"
|
||||
|
||||
@@ -75,6 +97,8 @@ func OpenDB(opts conf.DatabaseOpts, cfg *gorm.Config) (*gorm.DB, error) {
|
||||
dialector = mysql.Open(dsn)
|
||||
case "postgres":
|
||||
dialector = postgres.Open(dsn)
|
||||
case "mssql":
|
||||
dialector = sqlserver.Open(dsn)
|
||||
case "sqlite3":
|
||||
dialector = sqlite.Open(dsn)
|
||||
default:
|
||||
|
||||
@@ -32,6 +32,25 @@ 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) {
|
||||
t.Run("bad dialect", func(t *testing.T) {
|
||||
_, err := NewDSN(conf.DatabaseOpts{
|
||||
@@ -95,6 +114,18 @@ 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'",
|
||||
},
|
||||
|
||||
{
|
||||
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",
|
||||
opts: conf.DatabaseOpts{
|
||||
|
||||
@@ -3,13 +3,13 @@ package email
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/mail"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"gopkg.in/gomail.v2"
|
||||
"gopkg.in/macaron.v1"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/markup"
|
||||
@@ -72,11 +72,7 @@ func render(tpl string, data map[string]any) (string, error) {
|
||||
}
|
||||
|
||||
func SendTestMail(email string) error {
|
||||
msg, err := newMessage([]string{email}, "Gogs Test Email", "Hello 👋, greeting from Gogs!")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "new message")
|
||||
}
|
||||
return sendMessage(msg)
|
||||
return gomail.Send(&Sender{}, NewMessage([]string{email}, "Gogs Test Email", "Hello 👋, greeting from Gogs!").Message)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -102,7 +98,7 @@ type Issue interface {
|
||||
HTMLURL() string
|
||||
}
|
||||
|
||||
func SendUserMail(_ *macaron.Context, u User, tpl, code, subject, info string) error {
|
||||
func SendUserMail(_ *macaron.Context, u User, tpl, code, subject, info string) {
|
||||
data := map[string]any{
|
||||
"Username": u.DisplayName(),
|
||||
"ActiveCodeLives": conf.Auth.ActivateCodeLives / 60,
|
||||
@@ -111,28 +107,26 @@ func SendUserMail(_ *macaron.Context, u User, tpl, code, subject, info string) e
|
||||
}
|
||||
body, err := render(tpl, data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "render")
|
||||
log.Error("render: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := newMessage([]string{u.Email()}, subject, body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "new message")
|
||||
}
|
||||
msg.info = fmt.Sprintf("UID: %d, %s", u.ID(), info)
|
||||
msg := NewMessage([]string{u.Email()}, subject, body)
|
||||
msg.Info = fmt.Sprintf("UID: %d, %s", u.ID(), info)
|
||||
|
||||
send(msg)
|
||||
return nil
|
||||
Send(msg)
|
||||
}
|
||||
|
||||
func SendActivateAccountMail(c *macaron.Context, u User) error {
|
||||
return SendUserMail(c, u, tmplAuthActivate, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.activate_account"), "activate account")
|
||||
func SendActivateAccountMail(c *macaron.Context, u User) {
|
||||
SendUserMail(c, u, tmplAuthActivate, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.activate_account"), "activate account")
|
||||
}
|
||||
|
||||
func SendResetPasswordMail(c *macaron.Context, u User) error {
|
||||
return SendUserMail(c, u, tmplAuthResetPassword, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.reset_password"), "reset password")
|
||||
func SendResetPasswordMail(c *macaron.Context, u User) {
|
||||
SendUserMail(c, u, tmplAuthResetPassword, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.reset_password"), "reset password")
|
||||
}
|
||||
|
||||
func SendActivateEmailMail(c *macaron.Context, u User, email string) error {
|
||||
// SendActivateAccountMail sends confirmation email.
|
||||
func SendActivateEmailMail(c *macaron.Context, u User, email string) {
|
||||
data := map[string]any{
|
||||
"Username": u.DisplayName(),
|
||||
"ActiveCodeLives": conf.Auth.ActivateCodeLives / 60,
|
||||
@@ -141,39 +135,35 @@ func SendActivateEmailMail(c *macaron.Context, u User, email string) error {
|
||||
}
|
||||
body, err := render(tmplAuthActivateEmail, data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "render")
|
||||
log.Error("HTMLString: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := newMessage([]string{email}, c.Tr("mail.activate_email"), body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "new message")
|
||||
}
|
||||
msg.info = fmt.Sprintf("UID: %d, activate email", u.ID())
|
||||
msg := NewMessage([]string{email}, c.Tr("mail.activate_email"), body)
|
||||
msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID())
|
||||
|
||||
send(msg)
|
||||
return nil
|
||||
Send(msg)
|
||||
}
|
||||
|
||||
func SendRegisterNotifyMail(c *macaron.Context, u User) error {
|
||||
// SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
|
||||
func SendRegisterNotifyMail(c *macaron.Context, u User) {
|
||||
data := map[string]any{
|
||||
"Username": u.DisplayName(),
|
||||
}
|
||||
body, err := render(tmplAuthRegisterNotify, data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "render")
|
||||
log.Error("HTMLString: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := newMessage([]string{u.Email()}, c.Tr("mail.register_notify"), body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "new message")
|
||||
}
|
||||
msg.info = fmt.Sprintf("UID: %d, registration notify", u.ID())
|
||||
msg := NewMessage([]string{u.Email()}, c.Tr("mail.register_notify"), body)
|
||||
msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID())
|
||||
|
||||
send(msg)
|
||||
return nil
|
||||
Send(msg)
|
||||
}
|
||||
|
||||
func SendCollaboratorMail(u, doer User, repo Repository) error {
|
||||
// SendCollaboratorMail sends mail notification to new collaborator.
|
||||
func SendCollaboratorMail(u, doer User, repo Repository) {
|
||||
subject := fmt.Sprintf("%s added you to %s", doer.DisplayName(), repo.FullName())
|
||||
|
||||
data := map[string]any{
|
||||
@@ -183,17 +173,14 @@ func SendCollaboratorMail(u, doer User, repo Repository) error {
|
||||
}
|
||||
body, err := render(tmplNotifyCollaborator, data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "render")
|
||||
log.Error("HTMLString: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := newMessage([]string{u.Email()}, subject, body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "new message")
|
||||
}
|
||||
msg.info = fmt.Sprintf("UID: %d, add collaborator", u.ID())
|
||||
msg := NewMessage([]string{u.Email()}, subject, body)
|
||||
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID())
|
||||
|
||||
send(msg)
|
||||
return nil
|
||||
Send(msg)
|
||||
}
|
||||
|
||||
func composeTplData(subject, body, link string) map[string]any {
|
||||
@@ -204,47 +191,34 @@ func composeTplData(subject, body, link string) map[string]any {
|
||||
return data
|
||||
}
|
||||
|
||||
func composeIssueMessage(issue Issue, repo Repository, doer User, tplName string, tos []string, info string) (*message, error) {
|
||||
func composeIssueMessage(issue Issue, repo Repository, doer User, tplName string, tos []string, info string) *Message {
|
||||
subject := issue.MailSubject()
|
||||
body := string(markup.Markdown([]byte(issue.Content()), repo.HTMLURL(), repo.ComposeMetas()))
|
||||
data := composeTplData(subject, body, issue.HTMLURL())
|
||||
data["Doer"] = doer
|
||||
content, err := render(tplName, data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "render %q", tplName)
|
||||
log.Error("HTMLString (%s): %v", tplName, err)
|
||||
}
|
||||
from := (&mail.Address{Name: doer.DisplayName(), Address: conf.Email.FromEmail}).String()
|
||||
msg, err := newMessageFrom(tos, from, subject, content)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "new message")
|
||||
}
|
||||
msg.info = fmt.Sprintf("Subject: %s, %s", subject, info)
|
||||
return msg, nil
|
||||
from := gomail.NewMessage().FormatAddress(conf.Email.FromEmail, doer.DisplayName())
|
||||
msg := NewMessageFrom(tos, from, subject, content)
|
||||
msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
|
||||
return msg
|
||||
}
|
||||
|
||||
// SendIssueCommentMail composes and sends issue comment emails to target receivers.
|
||||
func SendIssueCommentMail(issue Issue, repo Repository, doer User, tos []string) error {
|
||||
func SendIssueCommentMail(issue Issue, repo Repository, doer User, tos []string) {
|
||||
if len(tos) == 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := composeIssueMessage(issue, repo, doer, tmplIssueComment, tos, "issue comment")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "compose issue message")
|
||||
}
|
||||
send(msg)
|
||||
return nil
|
||||
Send(composeIssueMessage(issue, repo, doer, tmplIssueComment, tos, "issue comment"))
|
||||
}
|
||||
|
||||
// SendIssueMentionMail composes and sends issue mention emails to target receivers.
|
||||
func SendIssueMentionMail(issue Issue, repo Repository, doer User, tos []string) error {
|
||||
func SendIssueMentionMail(issue Issue, repo Repository, doer User, tos []string) {
|
||||
if len(tos) == 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
msg, err := composeIssueMessage(issue, repo, doer, tmplIssueMention, tos, "issue mention")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "compose issue message")
|
||||
}
|
||||
send(msg)
|
||||
return nil
|
||||
Send(composeIssueMessage(issue, repo, doer, tmplIssueMention, tos, "issue mention"))
|
||||
}
|
||||
|
||||
@@ -2,134 +2,214 @@ package email
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"net/smtp"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/inbucket/html2text"
|
||||
gomail "github.com/wneessen/go-mail"
|
||||
"gopkg.in/gomail.v2"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
)
|
||||
|
||||
type message struct {
|
||||
info string
|
||||
msg *gomail.Msg
|
||||
type Message struct {
|
||||
Info string // Message information for log purpose.
|
||||
*gomail.Message
|
||||
confirmChan chan struct{}
|
||||
}
|
||||
|
||||
func newMessageFrom(to []string, from, subject, htmlBody string) (*message, error) {
|
||||
// NewMessageFrom creates new mail message object with custom From header.
|
||||
func NewMessageFrom(to []string, from, subject, htmlBody string) *Message {
|
||||
log.Trace("NewMessageFrom (htmlBody):\n%s", htmlBody)
|
||||
|
||||
m := gomail.NewMsg()
|
||||
if err := m.From(from); err != nil {
|
||||
return nil, errors.Wrapf(err, "set From address %q", from)
|
||||
}
|
||||
if err := m.To(to...); err != nil {
|
||||
return nil, errors.Wrap(err, "set To addresses")
|
||||
}
|
||||
m.Subject(conf.Email.SubjectPrefix + subject)
|
||||
m.SetDate()
|
||||
msg := gomail.NewMessage()
|
||||
msg.SetHeader("From", from)
|
||||
msg.SetHeader("To", to...)
|
||||
msg.SetHeader("Subject", conf.Email.SubjectPrefix+subject)
|
||||
msg.SetDateHeader("Date", time.Now())
|
||||
|
||||
contentType := "text/html"
|
||||
body := htmlBody
|
||||
switchedToPlaintext := false
|
||||
if conf.Email.UsePlainText || conf.Email.AddPlainTextAlt {
|
||||
plainBody, err := html2text.FromString(htmlBody)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "convert HTML to plain text")
|
||||
}
|
||||
if conf.Email.UsePlainText {
|
||||
m.SetBodyString(gomail.TypeTextPlain, plainBody)
|
||||
log.Error("html2text.FromString: %v", err)
|
||||
} else {
|
||||
m.SetBodyString(gomail.TypeTextPlain, plainBody)
|
||||
m.AddAlternativeString(gomail.TypeTextHTML, htmlBody)
|
||||
contentType = "text/plain"
|
||||
body = plainBody
|
||||
switchedToPlaintext = true
|
||||
}
|
||||
} else {
|
||||
m.SetBodyString(gomail.TypeTextHTML, htmlBody)
|
||||
}
|
||||
|
||||
return &message{
|
||||
msg: m,
|
||||
msg.SetBody(contentType, body)
|
||||
if switchedToPlaintext && conf.Email.AddPlainTextAlt && !conf.Email.UsePlainText {
|
||||
// The AddAlternative method name is confusing - adding html as an "alternative" will actually cause mail
|
||||
// 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{}),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func newMessage(to []string, subject, body string) (*message, error) {
|
||||
return newMessageFrom(to, conf.Email.From, subject, body)
|
||||
// NewMessage creates new mail message object with default From header.
|
||||
func NewMessage(to []string, subject, body string) *Message {
|
||||
return NewMessageFrom(to, conf.Email.From, subject, body)
|
||||
}
|
||||
|
||||
func newSMTPClient() (*gomail.Client, error) {
|
||||
type loginAuth struct {
|
||||
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
|
||||
|
||||
host, portStr, err := net.SplitHostPort(opts.Host)
|
||||
host, port, err := net.SplitHostPort(opts.Host)
|
||||
if err != nil {
|
||||
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))
|
||||
return err
|
||||
}
|
||||
|
||||
tlsconfig := &tls.Config{
|
||||
InsecureSkipVerify: opts.SkipVerify,
|
||||
ServerName: host,
|
||||
}
|
||||
|
||||
if opts.UseCertificate {
|
||||
cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
tlsconfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
clientOpts = append(clientOpts, gomail.WithTLSConfig(tlsconfig))
|
||||
|
||||
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()
|
||||
conn, err := net.Dial("tcp", net.JoinHostPort(host, port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.DialAndSend(msg.msg)
|
||||
defer conn.Close()
|
||||
|
||||
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() {
|
||||
sender := &Sender{}
|
||||
for msg := range mailQueue {
|
||||
to := strings.Join(msg.msg.GetToString(), ", ")
|
||||
log.Trace("New e-mail sending request %s: %s", to, msg.info)
|
||||
if err := sendMessage(msg); err != nil {
|
||||
log.Error("Failed to send emails %s: %s - %v", to, msg.info, err)
|
||||
log.Trace("New e-mail sending request %s: %s", msg.GetHeader("To"), msg.Info)
|
||||
if err := gomail.Send(sender, msg.Message); err != nil {
|
||||
log.Error("Failed to send emails %s: %s - %v", msg.GetHeader("To"), msg.Info, err)
|
||||
} else {
|
||||
log.Trace("E-mails sent %s: %s", to, msg.info)
|
||||
log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info)
|
||||
}
|
||||
msg.confirmChan <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
var mailQueue chan *message
|
||||
var mailQueue chan *Message
|
||||
|
||||
// NewContext initializes settings for mailer.
|
||||
func NewContext() {
|
||||
@@ -140,14 +220,14 @@ func NewContext() {
|
||||
return
|
||||
}
|
||||
|
||||
mailQueue = make(chan *message, 1000)
|
||||
mailQueue = make(chan *Message, 1000)
|
||||
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,
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package lfsutil
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -12,10 +10,7 @@ import (
|
||||
"gogs.io/gogs/internal/osutil"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrObjectNotExist = errors.New("object does not exist")
|
||||
ErrOIDMismatch = errors.New("content hash does not match OID")
|
||||
)
|
||||
var ErrObjectNotExist = errors.New("Object does not exist")
|
||||
|
||||
// Storager is an storage backend for uploading and downloading LFS objects.
|
||||
type Storager interface {
|
||||
@@ -44,8 +39,6 @@ var _ Storager = (*LocalStorage)(nil)
|
||||
type LocalStorage struct {
|
||||
// The root path for storing LFS objects.
|
||||
Root string
|
||||
// The path for storing temporary files during upload verification.
|
||||
TempDir string
|
||||
}
|
||||
|
||||
func (*LocalStorage) Storage() Storage {
|
||||
@@ -65,50 +58,29 @@ func (s *LocalStorage) Upload(oid OID, rc io.ReadCloser) (int64, error) {
|
||||
return 0, ErrInvalidOID
|
||||
}
|
||||
|
||||
var err error
|
||||
fpath := s.storagePath(oid)
|
||||
dir := filepath.Dir(fpath)
|
||||
defer func() {
|
||||
rc.Close()
|
||||
|
||||
defer rc.Close()
|
||||
if err != nil {
|
||||
_ = os.Remove(fpath)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "create directories")
|
||||
}
|
||||
|
||||
// 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-*")
|
||||
w, err := os.Create(fpath)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "create temp file")
|
||||
return 0, errors.Wrap(err, "create file")
|
||||
}
|
||||
tmpPath := tmp.Name()
|
||||
defer os.Remove(tmpPath)
|
||||
defer w.Close()
|
||||
|
||||
hash := sha256.New()
|
||||
written, err := io.Copy(tmp, io.TeeReader(rc, hash))
|
||||
if closeErr := tmp.Close(); err == nil && closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
written, err := io.Copy(w, rc)
|
||||
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 0, errors.Wrap(err, "copy file")
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
@@ -10,9 +10,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"gogs.io/gogs/internal/osutil"
|
||||
)
|
||||
|
||||
func TestLocalStorage_storagePath(t *testing.T) {
|
||||
@@ -49,54 +46,50 @@ func TestLocalStorage_storagePath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocalStorage_Upload(t *testing.T) {
|
||||
base := t.TempDir()
|
||||
s := &LocalStorage{
|
||||
Root: filepath.Join(base, "lfs-objects"),
|
||||
TempDir: filepath.Join(base, "tmp", "lfs"),
|
||||
Root: filepath.Join(os.TempDir(), "lfs-objects"),
|
||||
}
|
||||
|
||||
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)
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(s.Root)
|
||||
})
|
||||
|
||||
t.Run("valid OID", func(t *testing.T) {
|
||||
written, err := s.Upload(helloWorldOID, io.NopCloser(strings.NewReader("Hello world!")))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(12), written)
|
||||
})
|
||||
tests := []struct {
|
||||
name string
|
||||
oid OID
|
||||
content string
|
||||
expWritten int64
|
||||
expErr error
|
||||
}{
|
||||
{
|
||||
name: "invalid oid",
|
||||
oid: "bad_oid",
|
||||
expErr: ErrInvalidOID,
|
||||
},
|
||||
|
||||
t.Run("valid OID but wrong content", func(t *testing.T) {
|
||||
oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
|
||||
written, err := s.Upload(oid, io.NopCloser(strings.NewReader("Hello world!")))
|
||||
assert.Equal(t, int64(0), written)
|
||||
assert.Equal(t, ErrOIDMismatch, err)
|
||||
|
||||
// File should have been cleaned up.
|
||||
assert.False(t, osutil.IsFile(s.storagePath(oid)))
|
||||
})
|
||||
|
||||
t.Run("duplicate upload returns existing size", func(t *testing.T) {
|
||||
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())
|
||||
})
|
||||
{
|
||||
name: "valid oid",
|
||||
oid: "ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f",
|
||||
content: "Hello world!",
|
||||
expWritten: 12,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
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)
|
||||
assert.Equal(t, test.expErr, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalStorage_Download(t *testing.T) {
|
||||
oid := OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
|
||||
s := &LocalStorage{
|
||||
Root: filepath.Join(t.TempDir(), "lfs-objects"),
|
||||
Root: filepath.Join(os.TempDir(), "lfs-objects"),
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(s.Root)
|
||||
})
|
||||
|
||||
fpath := s.storagePath(oid)
|
||||
err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm)
|
||||
|
||||
@@ -3,11 +3,21 @@ package markup
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/russross/blackfriday"
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
goldmarkhtml "github.com/yuin/goldmark/renderer/html"
|
||||
"github.com/yuin/goldmark/text"
|
||||
"github.com/yuin/goldmark/util"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/lazyregexp"
|
||||
@@ -25,40 +35,69 @@ func IsMarkdownFile(name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// MarkdownRenderer is a extended version of underlying Markdown render object.
|
||||
type MarkdownRenderer struct {
|
||||
blackfriday.Renderer
|
||||
urlPrefix string
|
||||
}
|
||||
var (
|
||||
validLinksPattern = lazyregexp.New(`^[a-z][\w-]+://|^mailto:`)
|
||||
linkifyURLRegexp = lazyregexp.New(`^(?:http|https|ftp)://[-a-zA-Z0-9@:%._+~#=]{1,256}(?:\.[a-z]+)?(?::\d+)?(?:[/#?][-a-zA-Z0-9@:%_+.~#$!?&/=();,'\^{}\[\]` + "`" + `]*)?`)
|
||||
)
|
||||
|
||||
var validLinksPattern = lazyregexp.New(`^[a-z][\w-]+://|^mailto:`)
|
||||
|
||||
// isLink reports whether link fits valid format.
|
||||
func isLink(link []byte) bool {
|
||||
return validLinksPattern.Match(link)
|
||||
}
|
||||
|
||||
// Link defines how formal links should be processed to produce corresponding HTML elements.
|
||||
func (r *MarkdownRenderer) Link(out *bytes.Buffer, link, title, content []byte) {
|
||||
if len(link) > 0 && !isLink(link) {
|
||||
if link[0] != '#' {
|
||||
link = []byte(path.Join(r.urlPrefix, string(link)))
|
||||
}
|
||||
}
|
||||
|
||||
r.Renderer.Link(out, link, title, content)
|
||||
type linkTransformer struct {
|
||||
urlPrefix string
|
||||
}
|
||||
|
||||
// AutoLink defines how auto-detected links should be processed to produce corresponding HTML elements.
|
||||
// Reference for kind: https://github.com/russross/blackfriday/blob/master/markdown.go#L69-L76
|
||||
func (r *MarkdownRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
|
||||
if kind != blackfriday.LINK_TYPE_NORMAL {
|
||||
r.Renderer.AutoLink(out, link, kind)
|
||||
return
|
||||
func (t *linkTransformer) Transform(node *ast.Document, reader text.Reader, _ parser.Context) {
|
||||
_ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if !entering {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
if link, ok := n.(*ast.Link); ok {
|
||||
dest := link.Destination
|
||||
if len(dest) > 0 && !isLink(dest) && dest[0] != '#' {
|
||||
link.Destination = []byte(joinURLPath(t.urlPrefix, string(dest)))
|
||||
}
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
})
|
||||
}
|
||||
|
||||
// joinURLPath joins a base URL prefix with a relative path. It uses net/url for
|
||||
// absolute URL prefixes to preserve the scheme and host, and path.Join for
|
||||
// path-only prefixes.
|
||||
func joinURLPath(prefix, relPath string) string {
|
||||
u, err := url.Parse(prefix)
|
||||
if err != nil || u.Scheme == "" {
|
||||
return path.Join(prefix, relPath)
|
||||
}
|
||||
u.Path = path.Join(u.Path, relPath)
|
||||
return u.String()
|
||||
}
|
||||
|
||||
type gogsRenderer struct {
|
||||
urlPrefix string
|
||||
}
|
||||
|
||||
func (r *gogsRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(ast.KindAutoLink, r.renderAutoLink)
|
||||
}
|
||||
|
||||
func (r *gogsRenderer) renderAutoLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
n := node.(*ast.AutoLink)
|
||||
if !entering {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
// Since this method could only possibly serve one link at a time,
|
||||
// we do not need to find all.
|
||||
if n.AutoLinkType != ast.AutoLinkURL {
|
||||
url := n.URL(source)
|
||||
escaped := html.EscapeString(string(url))
|
||||
_, _ = fmt.Fprintf(w, `<a href="mailto:%s">%s</a>`, escaped, escaped)
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
link := n.URL(source)
|
||||
|
||||
if bytes.HasPrefix(link, []byte(conf.Server.ExternalURL)) {
|
||||
m := CommitPattern.Find(link)
|
||||
if m != nil {
|
||||
@@ -68,8 +107,9 @@ func (r *MarkdownRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
|
||||
if j == -1 {
|
||||
j = len(m)
|
||||
}
|
||||
_, _ = fmt.Fprintf(out, ` <code><a href="%s">%s</a></code>`, m, tool.ShortSHA1(string(m[i+7:j])))
|
||||
return
|
||||
escapedURL := html.EscapeString(string(m))
|
||||
_, _ = fmt.Fprintf(w, ` <code><a href="%s">%s</a></code>`, escapedURL, tool.ShortSHA1(string(m[i+7:j])))
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
m = IssueFullPattern.Find(link)
|
||||
@@ -82,78 +122,64 @@ func (r *MarkdownRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
|
||||
}
|
||||
|
||||
index := string(m[i+7 : j])
|
||||
escapedURL := html.EscapeString(string(m))
|
||||
fullRepoURL := conf.Server.ExternalURL + strings.TrimPrefix(r.urlPrefix, "/")
|
||||
var link string
|
||||
var href string
|
||||
if strings.HasPrefix(string(m), fullRepoURL) {
|
||||
// Use a short issue reference if the URL refers to this repository
|
||||
link = fmt.Sprintf(`<a href="%s">#%s</a>`, m, index)
|
||||
href = fmt.Sprintf(`<a href="%s">#%s</a>`, escapedURL, html.EscapeString(index))
|
||||
} else {
|
||||
// Use a cross-repository issue reference if the URL refers to a different repository
|
||||
repo := string(m[len(conf.Server.ExternalURL) : i-1])
|
||||
link = fmt.Sprintf(`<a href="%s">%s#%s</a>`, m, repo, index)
|
||||
repo := html.EscapeString(string(m[len(conf.Server.ExternalURL) : i-1]))
|
||||
href = fmt.Sprintf(`<a href="%s">%s#%s</a>`, escapedURL, repo, html.EscapeString(index))
|
||||
}
|
||||
out.WriteString(link)
|
||||
return
|
||||
_, _ = w.WriteString(href)
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
}
|
||||
|
||||
r.Renderer.AutoLink(out, link, kind)
|
||||
}
|
||||
|
||||
// ListItem defines how list items should be processed to produce corresponding HTML elements.
|
||||
func (r *MarkdownRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
|
||||
// Detect procedures to draw checkboxes.
|
||||
switch {
|
||||
case bytes.HasPrefix(text, []byte("[ ] ")):
|
||||
text = append([]byte(`<input type="checkbox" disabled="" />`), text[3:]...)
|
||||
case bytes.HasPrefix(text, []byte("[x] ")):
|
||||
text = append([]byte(`<input type="checkbox" disabled="" checked="" />`), text[3:]...)
|
||||
}
|
||||
r.Renderer.ListItem(out, text, flags)
|
||||
escapedLink := html.EscapeString(string(link))
|
||||
_, _ = fmt.Fprintf(w, `<a href="%s">%s</a>`, escapedLink, escapedLink)
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
// RawMarkdown renders content in Markdown syntax to HTML without handling special links.
|
||||
func RawMarkdown(body []byte, urlPrefix string) []byte {
|
||||
htmlFlags := 0
|
||||
htmlFlags |= blackfriday.HTML_SKIP_STYLE
|
||||
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
|
||||
extensions := []goldmark.Extender{
|
||||
extension.Table,
|
||||
extension.Strikethrough,
|
||||
extension.TaskList,
|
||||
extension.NewLinkify(extension.WithLinkifyURLRegexp(linkifyURLRegexp.Regexp())),
|
||||
}
|
||||
|
||||
if conf.Smartypants.Enabled {
|
||||
htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
|
||||
if conf.Smartypants.Fractions {
|
||||
htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
|
||||
}
|
||||
if conf.Smartypants.Dashes {
|
||||
htmlFlags |= blackfriday.HTML_SMARTYPANTS_DASHES
|
||||
}
|
||||
if conf.Smartypants.LatexDashes {
|
||||
htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
|
||||
}
|
||||
if conf.Smartypants.AngledQuotes {
|
||||
htmlFlags |= blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES
|
||||
}
|
||||
extensions = append(extensions, extension.Typographer)
|
||||
}
|
||||
|
||||
renderer := &MarkdownRenderer{
|
||||
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
|
||||
urlPrefix: urlPrefix,
|
||||
rendererOpts := []renderer.Option{
|
||||
renderer.WithNodeRenderers(
|
||||
util.Prioritized(&gogsRenderer{urlPrefix: urlPrefix}, 0),
|
||||
),
|
||||
}
|
||||
|
||||
// set up the parser
|
||||
extensions := 0
|
||||
extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
|
||||
extensions |= blackfriday.EXTENSION_TABLES
|
||||
extensions |= blackfriday.EXTENSION_FENCED_CODE
|
||||
extensions |= blackfriday.EXTENSION_AUTOLINK
|
||||
extensions |= blackfriday.EXTENSION_STRIKETHROUGH
|
||||
extensions |= blackfriday.EXTENSION_SPACE_HEADERS
|
||||
extensions |= blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
|
||||
|
||||
if conf.Markdown.EnableHardLineBreak {
|
||||
extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK
|
||||
rendererOpts = append(rendererOpts, goldmarkhtml.WithHardWraps())
|
||||
}
|
||||
|
||||
return blackfriday.Markdown(body, renderer, extensions)
|
||||
md := goldmark.New(
|
||||
goldmark.WithExtensions(extensions...),
|
||||
goldmark.WithParserOptions(
|
||||
parser.WithASTTransformers(
|
||||
util.Prioritized(&linkTransformer{urlPrefix: urlPrefix}, 0),
|
||||
),
|
||||
),
|
||||
goldmark.WithRendererOptions(rendererOpts...),
|
||||
)
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := md.Convert(body, &buf); err != nil {
|
||||
log.Error("Failed to convert Markdown: %v", err)
|
||||
return []byte(html.EscapeString(string(body)))
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Markdown takes a string or []byte and renders to HTML in Markdown syntax with special links.
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package markup_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/russross/blackfriday"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
@@ -13,7 +11,9 @@ import (
|
||||
)
|
||||
|
||||
func Test_IsMarkdownFile(t *testing.T) {
|
||||
// TODO: Refactor to accept a list of extensions
|
||||
oldExts := conf.Markdown.FileExtensions
|
||||
defer func() { conf.Markdown.FileExtensions = oldExts }()
|
||||
|
||||
conf.Markdown.FileExtensions = strings.Split(".md,.markdown,.mdown,.mkd", ",")
|
||||
tests := []struct {
|
||||
ext string
|
||||
@@ -32,41 +32,152 @@ func Test_IsMarkdownFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Markdown(t *testing.T) {
|
||||
// TODO: Refactor to accept URL
|
||||
func Test_RawMarkdown_AutoLink(t *testing.T) {
|
||||
oldURL := conf.Server.ExternalURL
|
||||
defer func() { conf.Server.ExternalURL = oldURL }()
|
||||
|
||||
conf.Server.ExternalURL = "http://localhost:3000/"
|
||||
|
||||
htmlFlags := 0
|
||||
htmlFlags |= blackfriday.HTML_SKIP_STYLE
|
||||
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
|
||||
renderer := &MarkdownRenderer{
|
||||
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
expVal string
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
// Issue URL
|
||||
{input: "http://localhost:3000/user/repo/issues/3333", expVal: "<a href=\"http://localhost:3000/user/repo/issues/3333\">#3333</a>"},
|
||||
{input: "http://1111/2222/ssss-issues/3333?param=blah&blahh=333", expVal: "<a href=\"http://1111/2222/ssss-issues/3333?param=blah&blahh=333\">http://1111/2222/ssss-issues/3333?param=blah&blahh=333</a>"},
|
||||
{input: "http://test.com/issues/33333", expVal: "<a href=\"http://test.com/issues/33333\">http://test.com/issues/33333</a>"},
|
||||
{input: "http://test.com/issues/3", expVal: "<a href=\"http://test.com/issues/3\">http://test.com/issues/3</a>"},
|
||||
{input: "http://issues/333", expVal: "<a href=\"http://issues/333\">http://issues/333</a>"},
|
||||
{input: "https://issues/333", expVal: "<a href=\"https://issues/333\">https://issues/333</a>"},
|
||||
{input: "http://tissues/0", expVal: "<a href=\"http://tissues/0\">http://tissues/0</a>"},
|
||||
|
||||
// Commit URL
|
||||
{input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae", expVal: " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">d8a994ef24</a></code>"},
|
||||
{input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", expVal: " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">d8a994ef24</a></code>"},
|
||||
{input: "https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", expVal: "<a href=\"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2</a>"},
|
||||
{input: "https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae", expVal: "<a href=\"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae</a>"},
|
||||
{
|
||||
name: "issue URL from same instance",
|
||||
input: "http://localhost:3000/user/repo/issues/3333",
|
||||
want: "<p><a href=\"http://localhost:3000/user/repo/issues/3333\">#3333</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "non-matching issue-like URL",
|
||||
input: "http://1111/2222/ssss-issues/3333?param=blah&blahh=333",
|
||||
want: "<p><a href=\"http://1111/2222/ssss-issues/3333?param=blah&blahh=333\">http://1111/2222/ssss-issues/3333?param=blah&blahh=333</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "external issue URL",
|
||||
input: "http://test.com/issues/33333",
|
||||
want: "<p><a href=\"http://test.com/issues/33333\">http://test.com/issues/33333</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "commit URL from same instance",
|
||||
input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae",
|
||||
want: "<p> <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">d8a994ef24</a></code></p>\n",
|
||||
},
|
||||
{
|
||||
name: "commit URL with fragment from same instance",
|
||||
input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2",
|
||||
want: "<p> <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">d8a994ef24</a></code></p>\n",
|
||||
},
|
||||
{
|
||||
name: "external commit URL",
|
||||
input: "https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2",
|
||||
want: "<p><a href=\"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "issue URL with single digit",
|
||||
input: "http://test.com/issues/3",
|
||||
want: "<p><a href=\"http://test.com/issues/3\">http://test.com/issues/3</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "host without dot in issue-like URL",
|
||||
input: "http://issues/333",
|
||||
want: "<p><a href=\"http://issues/333\">http://issues/333</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "https host without dot in issue-like URL",
|
||||
input: "https://issues/333",
|
||||
want: "<p><a href=\"https://issues/333\">https://issues/333</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "host without dot resembling keyword",
|
||||
input: "http://tissues/0",
|
||||
want: "<p><a href=\"http://tissues/0\">http://tissues/0</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "https commit-like URL without dot",
|
||||
input: "https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae",
|
||||
want: "<p><a href=\"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae</a></p>\n",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run("", func(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
renderer.AutoLink(buf, []byte(test.input), blackfriday.LINK_TYPE_NORMAL)
|
||||
assert.Equal(t, test.expVal, buf.String())
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := string(RawMarkdown([]byte(test.input), ""))
|
||||
assert.Equal(t, test.want, got)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("cross-repo issue URL from same instance", func(t *testing.T) {
|
||||
got := string(RawMarkdown([]byte("http://localhost:3000/other/repo/issues/42"), "/user/myrepo"))
|
||||
assert.Equal(t, "<p><a href=\"http://localhost:3000/other/repo/issues/42\">other/repo#42</a></p>\n", got)
|
||||
})
|
||||
|
||||
t.Run("same-repo issue URL with fragment", func(t *testing.T) {
|
||||
got := string(RawMarkdown([]byte("http://localhost:3000/user/myrepo/issues/42#issuecomment-1"), "/user/myrepo"))
|
||||
assert.Equal(t, "<p><a href=\"http://localhost:3000/user/myrepo/issues/42#issuecomment-1\">#42</a></p>\n", got)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_RawMarkdown_LinkRewriting(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
urlPrefix string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "relative link with path-only prefix",
|
||||
input: "[text](other-file.md)",
|
||||
urlPrefix: "/user/repo/src/branch/main",
|
||||
want: "<p><a href=\"/user/repo/src/branch/main/other-file.md\">text</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "relative link with absolute URL prefix",
|
||||
input: "[text](other-file.md)",
|
||||
urlPrefix: "http://localhost:3000/user/repo/src/branch/main",
|
||||
want: "<p><a href=\"http://localhost:3000/user/repo/src/branch/main/other-file.md\">text</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "absolute link not rewritten",
|
||||
input: "[text](https://example.com/page)",
|
||||
urlPrefix: "/user/repo/src/branch/main",
|
||||
want: "<p><a href=\"https://example.com/page\">text</a></p>\n",
|
||||
},
|
||||
{
|
||||
name: "anchor-only link not rewritten",
|
||||
input: "[text](#section)",
|
||||
urlPrefix: "/user/repo/src/branch/main",
|
||||
want: "<p><a href=\"#section\">text</a></p>\n",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := string(RawMarkdown([]byte(test.input), test.urlPrefix))
|
||||
assert.Equal(t, test.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_RawMarkdown_HTMLPassthrough(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "inline HTML tags are stripped",
|
||||
input: "Hello <em>world</em>",
|
||||
want: "<p>Hello <!-- raw HTML omitted -->world<!-- raw HTML omitted --></p>\n",
|
||||
},
|
||||
{
|
||||
name: "block HTML tags are stripped",
|
||||
input: "<div>content</div>",
|
||||
want: "<!-- raw HTML omitted -->\n",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := string(RawMarkdown([]byte(test.input), ""))
|
||||
assert.Equal(t, test.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
@@ -34,28 +32,14 @@ func NewSanitizer() {
|
||||
sanitizer.policy.AllowAttrs("type").Matching(lazyregexp.New(`^checkbox$`).Regexp()).OnElements("input")
|
||||
sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
|
||||
|
||||
// Only allow data URIs with safe image MIME types to prevent XSS via
|
||||
// "data:text/html" payloads.
|
||||
sanitizer.policy.AllowURLSchemeWithCustomPolicy("data", isSafeDataURI)
|
||||
// Data URLs
|
||||
sanitizer.policy.AllowURLSchemes("data")
|
||||
|
||||
// Custom URL-Schemes
|
||||
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.
|
||||
func Sanitize(s string) string {
|
||||
return sanitizer.policy.Sanitize(s)
|
||||
|
||||
@@ -26,20 +26,6 @@ func Test_Sanitizer(t *testing.T) {
|
||||
{input: `<input type="hidden">`, expVal: ``},
|
||||
{input: `<input type="checkbox">`, expVal: `<input type="checkbox">`},
|
||||
{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 {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/cron"
|
||||
@@ -216,7 +217,7 @@ func Config(c *context.Context) {
|
||||
Mode: strings.Title(conf.Log.Modes[i]),
|
||||
}
|
||||
|
||||
result, _ := json.MarshalIndent(conf.Log.Configs[i], "", " ")
|
||||
result, _ := jsoniter.MarshalIndent(conf.Log.Configs[i], "", " ")
|
||||
loggers[i].Config = string(result)
|
||||
}
|
||||
c.Data["Loggers"] = loggers
|
||||
|
||||
@@ -106,9 +106,7 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
|
||||
|
||||
// Send email notification.
|
||||
if f.SendNotify && conf.Email.Enabled {
|
||||
if err := email.SendRegisterNotifyMail(c.Context, database.NewMailerUser(user)); err != nil {
|
||||
log.Error("Failed to send register notify mail: %v", err)
|
||||
}
|
||||
email.SendRegisterNotifyMail(c.Context, database.NewMailerUser(user))
|
||||
}
|
||||
|
||||
c.Flash.Success(c.Tr("admin.users.new_success", user.Name))
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
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
|
||||
}
|
||||
13
internal/route/api/v1/admin/org.go
Normal file
13
internal/route/api/v1/admin/org.go
Normal file
@@ -0,0 +1,13 @@
|
||||
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))
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package v1
|
||||
package admin
|
||||
|
||||
import (
|
||||
"gogs.io/gogs/internal/context"
|
||||
"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"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get repository by name")
|
||||
@@ -14,8 +14,8 @@ func getRepositoryByParams(c *context.APIContext) *database.Repository {
|
||||
return repo
|
||||
}
|
||||
|
||||
func adminAddTeamRepository(c *context.APIContext) {
|
||||
repo := getRepositoryByParams(c)
|
||||
func AddTeamRepository(c *context.APIContext) {
|
||||
repo := GetRepositoryByParams(c)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
@@ -27,8 +27,8 @@ func adminAddTeamRepository(c *context.APIContext) {
|
||||
c.NoContent()
|
||||
}
|
||||
|
||||
func adminRemoveTeamRepository(c *context.APIContext) {
|
||||
repo := getRepositoryByParams(c)
|
||||
func RemoveTeamRepository(c *context.APIContext) {
|
||||
repo := GetRepositoryByParams(c)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
package v1
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/database"
|
||||
"gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/route/api/v1/convert"
|
||||
"gogs.io/gogs/internal/route/api/v1/user"
|
||||
)
|
||||
|
||||
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) {
|
||||
func CreateTeam(c *context.APIContext, form api.CreateTeamOption) {
|
||||
team := &database.Team{
|
||||
OrgID: c.Org.Organization.ID,
|
||||
Name: form.Name,
|
||||
@@ -30,11 +27,11 @@ func adminCreateTeam(c *context.APIContext, form adminCreateTeamRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, toOrganizationTeam(team))
|
||||
c.JSON(http.StatusCreated, convert.ToTeam(team))
|
||||
}
|
||||
|
||||
func adminAddTeamMember(c *context.APIContext) {
|
||||
u := getUserByParams(c)
|
||||
func AddTeamMember(c *context.APIContext) {
|
||||
u := user.GetUserByParams(c)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
@@ -46,8 +43,8 @@ func adminAddTeamMember(c *context.APIContext) {
|
||||
c.NoContent()
|
||||
}
|
||||
|
||||
func adminRemoveTeamMember(c *context.APIContext) {
|
||||
u := getUserByParams(c)
|
||||
func RemoveTeamMember(c *context.APIContext) {
|
||||
u := user.GetUserByParams(c)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
@@ -60,16 +57,16 @@ func adminRemoveTeamMember(c *context.APIContext) {
|
||||
c.NoContent()
|
||||
}
|
||||
|
||||
func adminListTeamMembers(c *context.APIContext) {
|
||||
func ListTeamMembers(c *context.APIContext) {
|
||||
team := c.Org.Team
|
||||
if err := team.GetMembers(); err != nil {
|
||||
c.Error(err, "get team members")
|
||||
return
|
||||
}
|
||||
|
||||
apiMembers := make([]*types.User, len(team.Members))
|
||||
apiMembers := make([]*api.User, len(team.Members))
|
||||
for i := range team.Members {
|
||||
apiMembers[i] = toUser(team.Members[i])
|
||||
apiMembers[i] = team.Members[i].APIFormat()
|
||||
}
|
||||
c.JSONSuccess(apiMembers)
|
||||
}
|
||||
18
internal/route/api/v1/admin/repo.go
Normal file
18
internal/route/api/v1/admin/repo.go
Normal file
@@ -0,0 +1,18 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
package v1
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/database"
|
||||
"gogs.io/gogs/internal/email"
|
||||
"gogs.io/gogs/internal/route/api/v1/user"
|
||||
)
|
||||
|
||||
func parseLoginSource(c *context.APIContext, sourceID int64) {
|
||||
@@ -27,23 +29,13 @@ func parseLoginSource(c *context.APIContext, sourceID int64) {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
func CreateUser(c *context.APIContext, form api.CreateUserOption) {
|
||||
parseLoginSource(c, form.SourceID)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
u, err := database.Handle.Users().Create(
|
||||
user, err := database.Handle.Users().Create(
|
||||
c.Req.Context(),
|
||||
form.Username,
|
||||
form.Email,
|
||||
@@ -65,35 +57,18 @@ func adminCreateUser(c *context.APIContext, form adminCreateUserRequest) {
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Trace("Account %q created by admin %q", u.Name, c.User.Name)
|
||||
log.Trace("Account %q created by admin %q", user.Name, c.User.Name)
|
||||
|
||||
// Send email notification.
|
||||
if form.SendNotify && conf.Email.Enabled {
|
||||
if err := email.SendRegisterNotifyMail(c.Context.Context, database.NewMailerUser(u)); err != nil {
|
||||
log.Error("Failed to send register notify mail: %v", err)
|
||||
}
|
||||
email.SendRegisterNotifyMail(c.Context.Context, database.NewMailerUser(user))
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, toUser(u))
|
||||
c.JSON(http.StatusCreated, user.APIFormat())
|
||||
}
|
||||
|
||||
type adminEditUserRequest struct {
|
||||
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)
|
||||
func EditUser(c *context.APIContext, form api.EditUserOption) {
|
||||
u := user.GetUserByParams(c)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
@@ -141,11 +116,11 @@ func adminEditUser(c *context.APIContext, form adminEditUserRequest) {
|
||||
c.Error(err, "get user")
|
||||
return
|
||||
}
|
||||
c.JSONSuccess(toUser(u))
|
||||
c.JSONSuccess(u.APIFormat())
|
||||
}
|
||||
|
||||
func adminDeleteUser(c *context.APIContext) {
|
||||
u := getUserByParams(c)
|
||||
func DeleteUser(c *context.APIContext) {
|
||||
u := user.GetUserByParams(c)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
@@ -164,10 +139,10 @@ func adminDeleteUser(c *context.APIContext) {
|
||||
c.NoContent()
|
||||
}
|
||||
|
||||
func adminCreatePublicKey(c *context.APIContext, form createPublicKeyRequest) {
|
||||
u := getUserByParams(c)
|
||||
func CreatePublicKey(c *context.APIContext, form api.CreateKeyOption) {
|
||||
u := user.GetUserByParams(c)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
createUserPublicKey(c, form, u.ID)
|
||||
user.CreateUserPublicKey(c, form, u.ID)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"gogs.io/gogs/internal/context"
|
||||
)
|
||||
|
||||
func adminCreateOrg(c *context.APIContext, form createOrgRequest) {
|
||||
createOrgForUser(c, form, getUserByParams(c))
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -7,9 +7,16 @@ import (
|
||||
"github.com/go-macaron/binding"
|
||||
"gopkg.in/macaron.v1"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/database"
|
||||
"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,
|
||||
@@ -174,245 +181,245 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Options("/*", func() {})
|
||||
|
||||
// Miscellaneous
|
||||
m.Post("/markdown", bind(markdownRequest{}), markdown)
|
||||
m.Post("/markdown/raw", markdownRaw)
|
||||
m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
|
||||
m.Post("/markdown/raw", misc.MarkdownRaw)
|
||||
|
||||
// Users
|
||||
m.Group("/users", func() {
|
||||
m.Get("/search", searchUsers)
|
||||
m.Get("/search", user.Search)
|
||||
|
||||
m.Group("/:username", func() {
|
||||
m.Get("", getUserProfile)
|
||||
m.Get("", user.GetInfo)
|
||||
|
||||
m.Group("/tokens", func() {
|
||||
accessTokensHandler := newAccessTokensHandler(newAccessTokensStore())
|
||||
accessTokensHandler := user.NewAccessTokensHandler(user.NewAccessTokensStore())
|
||||
m.Combo("").
|
||||
Get(accessTokensHandler.List()).
|
||||
Post(bind(createAccessTokenRequest{}), accessTokensHandler.Create())
|
||||
Post(bind(api.CreateAccessTokenOption{}), accessTokensHandler.Create())
|
||||
}, reqBasicAuth())
|
||||
})
|
||||
})
|
||||
|
||||
m.Group("/users", func() {
|
||||
m.Group("/:username", func() {
|
||||
m.Get("/keys", listPublicKeys)
|
||||
m.Get("/keys", user.ListPublicKeys)
|
||||
|
||||
m.Get("/followers", listFollowers)
|
||||
m.Get("/followers", user.ListFollowers)
|
||||
m.Group("/following", func() {
|
||||
m.Get("", listFollowing)
|
||||
m.Get("/:target", checkFollowing)
|
||||
m.Get("", user.ListFollowing)
|
||||
m.Get("/:target", user.CheckFollowing)
|
||||
})
|
||||
})
|
||||
}, reqToken())
|
||||
|
||||
m.Group("/user", func() {
|
||||
m.Get("", getAuthenticatedUser)
|
||||
m.Get("", user.GetAuthenticatedUser)
|
||||
m.Combo("/emails").
|
||||
Get(listEmails).
|
||||
Post(bind(createEmailRequest{}), addEmail).
|
||||
Delete(bind(createEmailRequest{}), deleteEmail)
|
||||
Get(user.ListEmails).
|
||||
Post(bind(api.CreateEmailOption{}), user.AddEmail).
|
||||
Delete(bind(api.CreateEmailOption{}), user.DeleteEmail)
|
||||
|
||||
m.Get("/followers", listMyFollowers)
|
||||
m.Get("/followers", user.ListMyFollowers)
|
||||
m.Group("/following", func() {
|
||||
m.Get("", listMyFollowing)
|
||||
m.Get("", user.ListMyFollowing)
|
||||
m.Combo("/:username").
|
||||
Get(checkMyFollowing).
|
||||
Put(follow).
|
||||
Delete(unfollow)
|
||||
Get(user.CheckMyFollowing).
|
||||
Put(user.Follow).
|
||||
Delete(user.Unfollow)
|
||||
})
|
||||
|
||||
m.Group("/keys", func() {
|
||||
m.Combo("").
|
||||
Get(listMyPublicKeys).
|
||||
Post(bind(createPublicKeyRequest{}), createPublicKey)
|
||||
Get(user.ListMyPublicKeys).
|
||||
Post(bind(api.CreateKeyOption{}), user.CreatePublicKey)
|
||||
m.Combo("/:id").
|
||||
Get(getPublicKey).
|
||||
Delete(deletePublicKey)
|
||||
Get(user.GetPublicKey).
|
||||
Delete(user.DeletePublicKey)
|
||||
})
|
||||
|
||||
m.Get("/issues", listUserIssues)
|
||||
m.Get("/issues", repo.ListUserIssues)
|
||||
}, reqToken())
|
||||
|
||||
// Repositories
|
||||
m.Get("/users/:username/repos", reqToken(), listUserRepositories)
|
||||
m.Get("/orgs/:org/repos", reqToken(), listOrgRepositories)
|
||||
m.Get("/users/:username/repos", reqToken(), repo.ListUserRepositories)
|
||||
m.Get("/orgs/:org/repos", reqToken(), repo.ListOrgRepositories)
|
||||
m.Combo("/user/repos", reqToken()).
|
||||
Get(listMyRepos).
|
||||
Post(bind(createRepoRequest{}), createRepo)
|
||||
m.Post("/org/:org/repos", reqToken(), bind(createRepoRequest{}), createOrgRepo)
|
||||
Get(repo.ListMyRepos).
|
||||
Post(bind(api.CreateRepoOption{}), repo.Create)
|
||||
m.Post("/org/:org/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo)
|
||||
|
||||
m.Group("/repos", func() {
|
||||
m.Get("/search", searchRepos)
|
||||
m.Get("/search", repo.Search)
|
||||
|
||||
m.Get("/:username/:reponame", repoAssignment(), getRepo)
|
||||
m.Get("/:username/:reponame/releases", repoAssignment(), releases)
|
||||
m.Get("/:username/:reponame", repoAssignment(), repo.Get)
|
||||
m.Get("/:username/:reponame/releases", repoAssignment(), repo.Releases)
|
||||
})
|
||||
|
||||
m.Group("/repos", func() {
|
||||
m.Post("/migrate", bind(form.MigrateRepo{}), migrate)
|
||||
m.Delete("/:username/:reponame", repoAssignment(), reqRepoOwner(), deleteRepo)
|
||||
m.Post("/migrate", bind(form.MigrateRepo{}), repo.Migrate)
|
||||
m.Delete("/:username/:reponame", repoAssignment(), reqRepoOwner(), repo.Delete)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Group("/hooks", func() {
|
||||
m.Combo("").
|
||||
Get(listHooks).
|
||||
Post(bind(createHookRequest{}), createHook)
|
||||
Get(repo.ListHooks).
|
||||
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
||||
m.Combo("/:id").
|
||||
Patch(bind(editHookRequest{}), editHook).
|
||||
Delete(deleteHook)
|
||||
Patch(bind(api.EditHookOption{}), repo.EditHook).
|
||||
Delete(repo.DeleteHook)
|
||||
}, reqRepoAdmin())
|
||||
|
||||
m.Group("/collaborators", func() {
|
||||
m.Get("", listCollaborators)
|
||||
m.Get("", repo.ListCollaborators)
|
||||
m.Combo("/:collaborator").
|
||||
Get(isCollaborator).
|
||||
Put(bind(addCollaboratorRequest{}), addCollaborator).
|
||||
Delete(deleteCollaborator)
|
||||
Get(repo.IsCollaborator).
|
||||
Put(bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
|
||||
Delete(repo.DeleteCollaborator)
|
||||
}, reqRepoAdmin())
|
||||
|
||||
m.Get("/raw/*", context.RepoRef(), getRawFile)
|
||||
m.Get("/raw/*", context.RepoRef(), repo.GetRawFile)
|
||||
m.Group("/contents", func() {
|
||||
m.Get("", getContents)
|
||||
m.Get("", repo.GetContents)
|
||||
m.Combo("/*").
|
||||
Get(getContents).
|
||||
Put(reqRepoWriter(), bind(putContentsRequest{}), putContents)
|
||||
Get(repo.GetContents).
|
||||
Put(reqRepoWriter(), bind(repo.PutContentsRequest{}), repo.PutContents)
|
||||
})
|
||||
m.Get("/archive/*", getArchive)
|
||||
m.Get("/archive/*", repo.GetArchive)
|
||||
m.Group("/git", func() {
|
||||
m.Group("/trees", func() {
|
||||
m.Get("/:sha", getRepoGitTree)
|
||||
m.Get("/:sha", repo.GetRepoGitTree)
|
||||
})
|
||||
m.Group("/blobs", func() {
|
||||
m.Get("/:sha", repoGitBlob)
|
||||
m.Get("/:sha", repo.RepoGitBlob)
|
||||
})
|
||||
})
|
||||
m.Get("/forks", listForks)
|
||||
m.Get("/tags", listTags)
|
||||
m.Get("/forks", repo.ListForks)
|
||||
m.Get("/tags", repo.ListTags)
|
||||
m.Group("/branches", func() {
|
||||
m.Get("", listBranches)
|
||||
m.Get("/*", getBranch)
|
||||
m.Get("", repo.ListBranches)
|
||||
m.Get("/*", repo.GetBranch)
|
||||
})
|
||||
m.Group("/commits", func() {
|
||||
m.Get("/:sha", getSingleCommit)
|
||||
m.Get("", getAllCommits)
|
||||
m.Get("/*", getReferenceSHA)
|
||||
m.Get("/:sha", repo.GetSingleCommit)
|
||||
m.Get("", repo.GetAllCommits)
|
||||
m.Get("/*", repo.GetReferenceSHA)
|
||||
})
|
||||
|
||||
m.Group("/keys", func() {
|
||||
m.Combo("").
|
||||
Get(listDeployKeys).
|
||||
Post(bind(createDeployKeyRequest{}), createDeployKey)
|
||||
Get(repo.ListDeployKeys).
|
||||
Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
|
||||
m.Combo("/:id").
|
||||
Get(getDeployKey).
|
||||
Delete(deleteDeploykey)
|
||||
Get(repo.GetDeployKey).
|
||||
Delete(repo.DeleteDeploykey)
|
||||
}, reqRepoAdmin())
|
||||
|
||||
m.Group("/issues", func() {
|
||||
m.Combo("").
|
||||
Get(listIssues).
|
||||
Post(bind(createIssueRequest{}), createIssue)
|
||||
Get(repo.ListIssues).
|
||||
Post(bind(api.CreateIssueOption{}), repo.CreateIssue)
|
||||
m.Group("/comments", func() {
|
||||
m.Get("", listRepoIssueComments)
|
||||
m.Patch("/:id", bind(editIssueCommentRequest{}), editIssueComment)
|
||||
m.Get("", repo.ListRepoIssueComments)
|
||||
m.Patch("/:id", bind(api.EditIssueCommentOption{}), repo.EditIssueComment)
|
||||
})
|
||||
m.Group("/:index", func() {
|
||||
m.Combo("").
|
||||
Get(getIssue).
|
||||
Patch(bind(editIssueRequest{}), editIssue)
|
||||
Get(repo.GetIssue).
|
||||
Patch(bind(api.EditIssueOption{}), repo.EditIssue)
|
||||
|
||||
m.Group("/comments", func() {
|
||||
m.Combo("").
|
||||
Get(listIssueComments).
|
||||
Post(bind(createIssueCommentRequest{}), createIssueComment)
|
||||
Get(repo.ListIssueComments).
|
||||
Post(bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment)
|
||||
m.Combo("/:id").
|
||||
Patch(bind(editIssueCommentRequest{}), editIssueComment).
|
||||
Delete(deleteIssueComment)
|
||||
Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
|
||||
Delete(repo.DeleteIssueComment)
|
||||
})
|
||||
|
||||
m.Get("/labels", listIssueLabels)
|
||||
m.Get("/labels", repo.ListIssueLabels)
|
||||
m.Group("/labels", func() {
|
||||
m.Combo("").
|
||||
Post(bind(issueLabelsRequest{}), addIssueLabels).
|
||||
Put(bind(issueLabelsRequest{}), replaceIssueLabels).
|
||||
Delete(clearIssueLabels)
|
||||
m.Delete("/:id", deleteIssueLabel)
|
||||
Post(bind(api.IssueLabelsOption{}), repo.AddIssueLabels).
|
||||
Put(bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels).
|
||||
Delete(repo.ClearIssueLabels)
|
||||
m.Delete("/:id", repo.DeleteIssueLabel)
|
||||
}, reqRepoWriter())
|
||||
})
|
||||
}, mustEnableIssues)
|
||||
|
||||
m.Group("/labels", func() {
|
||||
m.Get("", listLabels)
|
||||
m.Get("/:id", getLabel)
|
||||
m.Get("", repo.ListLabels)
|
||||
m.Get("/:id", repo.GetLabel)
|
||||
})
|
||||
m.Group("/labels", func() {
|
||||
m.Post("", bind(createLabelRequest{}), createLabel)
|
||||
m.Post("", bind(api.CreateLabelOption{}), repo.CreateLabel)
|
||||
m.Combo("/:id").
|
||||
Patch(bind(editLabelRequest{}), editLabel).
|
||||
Delete(deleteLabel)
|
||||
Patch(bind(api.EditLabelOption{}), repo.EditLabel).
|
||||
Delete(repo.DeleteLabel)
|
||||
}, reqRepoWriter())
|
||||
|
||||
m.Group("/milestones", func() {
|
||||
m.Get("", listMilestones)
|
||||
m.Get("/:id", getMilestone)
|
||||
m.Get("", repo.ListMilestones)
|
||||
m.Get("/:id", repo.GetMilestone)
|
||||
})
|
||||
m.Group("/milestones", func() {
|
||||
m.Post("", bind(createMilestoneRequest{}), createMilestone)
|
||||
m.Post("", bind(api.CreateMilestoneOption{}), repo.CreateMilestone)
|
||||
m.Combo("/:id").
|
||||
Patch(bind(editMilestoneRequest{}), editMilestone).
|
||||
Delete(deleteMilestone)
|
||||
Patch(bind(api.EditMilestoneOption{}), repo.EditMilestone).
|
||||
Delete(repo.DeleteMilestone)
|
||||
}, reqRepoWriter())
|
||||
|
||||
m.Patch("/issue-tracker", reqRepoWriter(), bind(editIssueTrackerRequest{}), issueTracker)
|
||||
m.Patch("/wiki", reqRepoWriter(), bind(editWikiRequest{}), wiki)
|
||||
m.Post("/mirror-sync", reqRepoWriter(), mirrorSync)
|
||||
m.Get("/editorconfig/:filename", context.RepoRef(), getEditorconfig)
|
||||
m.Patch("/issue-tracker", reqRepoWriter(), bind(api.EditIssueTrackerOption{}), repo.IssueTracker)
|
||||
m.Patch("/wiki", reqRepoWriter(), bind(api.EditWikiOption{}), repo.Wiki)
|
||||
m.Post("/mirror-sync", reqRepoWriter(), repo.MirrorSync)
|
||||
m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig)
|
||||
}, repoAssignment())
|
||||
}, reqToken())
|
||||
|
||||
m.Get("/issues", reqToken(), listUserIssues)
|
||||
m.Get("/issues", reqToken(), repo.ListUserIssues)
|
||||
|
||||
// Organizations
|
||||
m.Combo("/user/orgs", reqToken()).
|
||||
Get(listMyOrgs).
|
||||
Post(bind(createOrgRequest{}), createMyOrg)
|
||||
Get(org.ListMyOrgs).
|
||||
Post(bind(api.CreateOrgOption{}), org.CreateMyOrg)
|
||||
|
||||
m.Get("/users/:username/orgs", listUserOrgs)
|
||||
m.Get("/users/:username/orgs", org.ListUserOrgs)
|
||||
m.Group("/orgs/:orgname", func() {
|
||||
m.Combo("").
|
||||
Get(getOrg).
|
||||
Patch(bind(editOrgRequest{}), editOrg)
|
||||
m.Get("/teams", listTeams)
|
||||
Get(org.Get).
|
||||
Patch(bind(api.EditOrgOption{}), org.Edit)
|
||||
m.Get("/teams", org.ListTeams)
|
||||
}, orgAssignment(true))
|
||||
|
||||
m.Group("/admin", func() {
|
||||
m.Group("/users", func() {
|
||||
m.Post("", bind(adminCreateUserRequest{}), adminCreateUser)
|
||||
m.Post("", bind(api.CreateUserOption{}), admin.CreateUser)
|
||||
|
||||
m.Group("/:username", func() {
|
||||
m.Combo("").
|
||||
Patch(bind(adminEditUserRequest{}), adminEditUser).
|
||||
Delete(adminDeleteUser)
|
||||
m.Post("/keys", bind(createPublicKeyRequest{}), adminCreatePublicKey)
|
||||
m.Post("/orgs", bind(createOrgRequest{}), adminCreateOrg)
|
||||
m.Post("/repos", bind(createRepoRequest{}), adminCreateRepo)
|
||||
Patch(bind(api.EditUserOption{}), admin.EditUser).
|
||||
Delete(admin.DeleteUser)
|
||||
m.Post("/keys", bind(api.CreateKeyOption{}), admin.CreatePublicKey)
|
||||
m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg)
|
||||
m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo)
|
||||
})
|
||||
})
|
||||
|
||||
m.Group("/orgs/:orgname", func() {
|
||||
m.Group("/teams", func() {
|
||||
m.Post("", orgAssignment(true), bind(adminCreateTeamRequest{}), adminCreateTeam)
|
||||
m.Post("", orgAssignment(true), bind(api.CreateTeamOption{}), admin.CreateTeam)
|
||||
})
|
||||
})
|
||||
|
||||
m.Group("/teams", func() {
|
||||
m.Group("/:teamid", func() {
|
||||
m.Get("/members", adminListTeamMembers)
|
||||
m.Get("/members", admin.ListTeamMembers)
|
||||
m.Combo("/members/:username").
|
||||
Put(adminAddTeamMember).
|
||||
Delete(adminRemoveTeamMember)
|
||||
Put(admin.AddTeamMember).
|
||||
Delete(admin.RemoveTeamMember)
|
||||
m.Combo("/repos/:reponame").
|
||||
Put(adminAddTeamRepository).
|
||||
Delete(adminRemoveTeamRepository)
|
||||
Put(admin.AddTeamRepository).
|
||||
Delete(admin.RemoveTeamRepository)
|
||||
}, orgAssignment(false, true))
|
||||
})
|
||||
}, reqAdmin())
|
||||
|
||||
135
internal/route/api/v1/convert/convert.go
Normal file
135
internal/route/api/v1/convert/convert.go
Normal file
@@ -0,0 +1,135 @@
|
||||
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(),
|
||||
}
|
||||
}
|
||||
15
internal/route/api/v1/convert/utils.go
Normal file
15
internal/route/api/v1/convert/utils.go
Normal file
@@ -0,0 +1,15 @@
|
||||
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
|
||||
}
|
||||
@@ -1,17 +1,13 @@
|
||||
package v1
|
||||
package misc
|
||||
|
||||
import (
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/markup"
|
||||
)
|
||||
|
||||
// markdownRequest represents the request body for rendering markdown.
|
||||
type markdownRequest struct {
|
||||
Text string
|
||||
Context string
|
||||
}
|
||||
|
||||
func markdown(c *context.APIContext, form markdownRequest) {
|
||||
func Markdown(c *context.APIContext, form api.MarkdownOption) {
|
||||
if form.Text == "" {
|
||||
_, _ = c.Write([]byte(""))
|
||||
return
|
||||
@@ -20,7 +16,7 @@ func markdown(c *context.APIContext, form markdownRequest) {
|
||||
_, _ = 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()
|
||||
if err != nil {
|
||||
c.Error(err, "read body")
|
||||
@@ -1,22 +1,17 @@
|
||||
package v1
|
||||
package org
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/database"
|
||||
"gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/route/api/v1/convert"
|
||||
"gogs.io/gogs/internal/route/api/v1/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) {
|
||||
func CreateOrgForUser(c *context.APIContext, apiForm api.CreateOrgOption, user *database.User) {
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
@@ -40,10 +35,10 @@ func createOrgForUser(c *context.APIContext, apiForm createOrgRequest, user *dat
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(201, toOrganization(org))
|
||||
c.JSON(201, convert.ToOrganization(org))
|
||||
}
|
||||
|
||||
func listOrgsOfUser(c *context.APIContext, u *database.User, all bool) {
|
||||
func listUserOrgs(c *context.APIContext, u *database.User, all bool) {
|
||||
orgs, err := database.Handle.Organizations().List(
|
||||
c.Req.Context(),
|
||||
database.ListOrgsOptions{
|
||||
@@ -56,41 +51,34 @@ func listOrgsOfUser(c *context.APIContext, u *database.User, all bool) {
|
||||
return
|
||||
}
|
||||
|
||||
apiOrgs := make([]*types.Organization, len(orgs))
|
||||
apiOrgs := make([]*api.Organization, len(orgs))
|
||||
for i := range orgs {
|
||||
apiOrgs[i] = toOrganization(orgs[i])
|
||||
apiOrgs[i] = convert.ToOrganization(orgs[i])
|
||||
}
|
||||
c.JSONSuccess(&apiOrgs)
|
||||
}
|
||||
|
||||
func listMyOrgs(c *context.APIContext) {
|
||||
listOrgsOfUser(c, c.User, true)
|
||||
func ListMyOrgs(c *context.APIContext) {
|
||||
listUserOrgs(c, c.User, true)
|
||||
}
|
||||
|
||||
func createMyOrg(c *context.APIContext, apiForm createOrgRequest) {
|
||||
createOrgForUser(c, apiForm, c.User)
|
||||
func CreateMyOrg(c *context.APIContext, apiForm api.CreateOrgOption) {
|
||||
CreateOrgForUser(c, apiForm, c.User)
|
||||
}
|
||||
|
||||
func listUserOrgs(c *context.APIContext) {
|
||||
u := getUserByParams(c)
|
||||
func ListUserOrgs(c *context.APIContext) {
|
||||
u := user.GetUserByParams(c)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
listOrgsOfUser(c, u, false)
|
||||
listUserOrgs(c, u, false)
|
||||
}
|
||||
|
||||
func getOrg(c *context.APIContext) {
|
||||
c.JSONSuccess(toOrganization(c.Org.Organization))
|
||||
func Get(c *context.APIContext) {
|
||||
c.JSONSuccess(convert.ToOrganization(c.Org.Organization))
|
||||
}
|
||||
|
||||
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) {
|
||||
func Edit(c *context.APIContext, form api.EditOrgOption) {
|
||||
org := c.Org.Organization
|
||||
if !org.IsOwnedBy(c.User.ID) {
|
||||
c.Status(http.StatusForbidden)
|
||||
@@ -117,5 +105,5 @@ func editOrg(c *context.APIContext, form editOrgRequest) {
|
||||
c.Error(err, "get organization")
|
||||
return
|
||||
}
|
||||
c.JSONSuccess(toOrganization(org))
|
||||
c.JSONSuccess(convert.ToOrganization(org))
|
||||
}
|
||||
22
internal/route/api/v1/org/team.go
Normal file
22
internal/route/api/v1/org/team.go
Normal file
@@ -0,0 +1,22 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"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")))
|
||||
if err != nil {
|
||||
c.Error(err, "open repository")
|
||||
@@ -1,12 +1,14 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/route/api/v1/convert"
|
||||
)
|
||||
|
||||
// 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("*"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get branch")
|
||||
@@ -19,25 +21,25 @@ func getBranch(c *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSONSuccess(toBranch(branch, commit))
|
||||
c.JSONSuccess(convert.ToBranch(branch, commit))
|
||||
}
|
||||
|
||||
// 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()
|
||||
if err != nil {
|
||||
c.Error(err, "get branches")
|
||||
return
|
||||
}
|
||||
|
||||
apiBranches := make([]*types.RepositoryBranch, len(branches))
|
||||
apiBranches := make([]*api.Branch, len(branches))
|
||||
for i := range branches {
|
||||
commit, err := branches[i].GetCommit()
|
||||
if err != nil {
|
||||
c.Error(err, "get commit")
|
||||
return
|
||||
}
|
||||
apiBranches[i] = toBranch(branches[i], commit)
|
||||
apiBranches[i] = convert.ToBranch(branches[i], commit)
|
||||
}
|
||||
|
||||
c.JSONSuccess(&apiBranches)
|
||||
@@ -1,32 +1,29 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"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()
|
||||
if err != nil {
|
||||
c.Error(err, "get collaborators")
|
||||
return
|
||||
}
|
||||
|
||||
apiCollaborators := make([]*types.RepositoryCollaborator, len(collaborators))
|
||||
apiCollaborators := make([]*api.Collaborator, len(collaborators))
|
||||
for i := range collaborators {
|
||||
apiCollaborators[i] = toRepositoryCollaborator(collaborators[i])
|
||||
apiCollaborators[i] = collaborators[i].APIFormat()
|
||||
}
|
||||
c.JSONSuccess(&apiCollaborators)
|
||||
}
|
||||
|
||||
type addCollaboratorRequest struct {
|
||||
Permission *string `json:"permission"`
|
||||
}
|
||||
|
||||
func addCollaborator(c *context.APIContext, form addCollaboratorRequest) {
|
||||
func AddCollaborator(c *context.APIContext, form api.AddCollaboratorOption) {
|
||||
collaborator, err := database.Handle.Users().GetByUsername(c.Req.Context(), c.Params(":collaborator"))
|
||||
if err != nil {
|
||||
if database.IsErrUserNotExist(err) {
|
||||
@@ -52,7 +49,7 @@ func addCollaborator(c *context.APIContext, form addCollaboratorRequest) {
|
||||
c.NoContent()
|
||||
}
|
||||
|
||||
func isCollaborator(c *context.APIContext) {
|
||||
func IsCollaborator(c *context.APIContext) {
|
||||
collaborator, err := database.Handle.Users().GetByUsername(c.Req.Context(), c.Params(":collaborator"))
|
||||
if err != nil {
|
||||
if database.IsErrUserNotExist(err) {
|
||||
@@ -70,7 +67,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"))
|
||||
if err != nil {
|
||||
if database.IsErrUserNotExist(err) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
@@ -6,18 +6,16 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gogs/git-module"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/database"
|
||||
"gogs.io/gogs/internal/gitutil"
|
||||
"gogs.io/gogs/internal/route/api/v1/types"
|
||||
)
|
||||
|
||||
const mediaApplicationSHA = "application/vnd.gogs.sha"
|
||||
|
||||
// getAllCommits returns a slice of commits starting from HEAD.
|
||||
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.
|
||||
pageSize := c.QueryInt("pageSize")
|
||||
if pageSize == 0 {
|
||||
@@ -31,7 +29,7 @@ func getAllCommits(c *context.APIContext) {
|
||||
}
|
||||
|
||||
// The response object returned as JSON
|
||||
result := make([]*types.Commit, 0, pageSize)
|
||||
result := make([]*api.Commit, 0, pageSize)
|
||||
commits, err := gitRepo.Log("HEAD", git.LogOptions{MaxCount: pageSize})
|
||||
if err != nil {
|
||||
c.Error(err, "git log")
|
||||
@@ -49,11 +47,11 @@ func getAllCommits(c *context.APIContext) {
|
||||
c.JSONSuccess(result)
|
||||
}
|
||||
|
||||
// getSingleCommit will return a single Commit object based on the specified SHA.
|
||||
func getSingleCommit(c *context.APIContext) {
|
||||
if strings.Contains(c.Req.Header.Get("Accept"), mediaApplicationSHA) {
|
||||
// GetSingleCommit will return a single Commit object based on the specified SHA.
|
||||
func GetSingleCommit(c *context.APIContext) {
|
||||
if strings.Contains(c.Req.Header.Get("Accept"), api.MediaApplicationSHA) {
|
||||
c.SetParams("*", c.Params(":sha"))
|
||||
getReferenceSHA(c)
|
||||
GetReferenceSHA(c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -75,7 +73,7 @@ func getSingleCommit(c *context.APIContext) {
|
||||
c.JSONSuccess(apiCommit)
|
||||
}
|
||||
|
||||
func getReferenceSHA(c *context.APIContext) {
|
||||
func GetReferenceSHA(c *context.APIContext) {
|
||||
gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
|
||||
if err != nil {
|
||||
c.Error(err, "open repository")
|
||||
@@ -116,14 +114,14 @@ func getReferenceSHA(c *context.APIContext) {
|
||||
}
|
||||
|
||||
// gitCommitToApiCommit is a helper function to convert git commit object to API commit.
|
||||
func gitCommitToAPICommit(commit *git.Commit, c *context.APIContext) (*types.Commit, error) {
|
||||
func gitCommitToAPICommit(commit *git.Commit, c *context.APIContext) (*api.Commit, error) {
|
||||
// Retrieve author and committer information
|
||||
var apiAuthor, apiCommitter *types.User
|
||||
var apiAuthor, apiCommitter *api.User
|
||||
author, err := database.Handle.Users().GetByEmail(c.Req.Context(), commit.Author.Email)
|
||||
if err != nil && !database.IsErrUserNotExist(err) {
|
||||
return nil, err
|
||||
} else if err == nil {
|
||||
apiAuthor = toUser(author)
|
||||
apiAuthor = author.APIFormat()
|
||||
}
|
||||
|
||||
// Save one query if the author is also the committer
|
||||
@@ -134,40 +132,40 @@ func gitCommitToAPICommit(commit *git.Commit, c *context.APIContext) (*types.Com
|
||||
if err != nil && !database.IsErrUserNotExist(err) {
|
||||
return nil, err
|
||||
} else if err == nil {
|
||||
apiCommitter = toUser(committer)
|
||||
apiCommitter = committer.APIFormat()
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve parent(s) of the commit
|
||||
apiParents := make([]*types.CommitMeta, commit.ParentsCount())
|
||||
apiParents := make([]*api.CommitMeta, commit.ParentsCount())
|
||||
for i := 0; i < commit.ParentsCount(); i++ {
|
||||
sha, _ := commit.ParentID(i)
|
||||
apiParents[i] = &types.CommitMeta{
|
||||
apiParents[i] = &api.CommitMeta{
|
||||
URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/commits/" + sha.String(),
|
||||
SHA: sha.String(),
|
||||
}
|
||||
}
|
||||
|
||||
return &types.Commit{
|
||||
CommitMeta: &types.CommitMeta{
|
||||
return &api.Commit{
|
||||
CommitMeta: &api.CommitMeta{
|
||||
URL: conf.Server.ExternalURL + c.Link[1:],
|
||||
SHA: commit.ID.String(),
|
||||
},
|
||||
HTMLURL: c.Repo.Repository.HTMLURL() + "/commits/" + commit.ID.String(),
|
||||
RepoCommit: &types.RepoCommit{
|
||||
RepoCommit: &api.RepoCommit{
|
||||
URL: conf.Server.ExternalURL + c.Link[1:],
|
||||
Author: &types.CommitUser{
|
||||
Author: &api.CommitUser{
|
||||
Name: commit.Author.Name,
|
||||
Email: commit.Author.Email,
|
||||
Date: commit.Author.When.Format(time.RFC3339),
|
||||
},
|
||||
Committer: &types.CommitUser{
|
||||
Committer: &api.CommitUser{
|
||||
Name: commit.Committer.Name,
|
||||
Email: commit.Committer.Email,
|
||||
Date: commit.Committer.When.Format(time.RFC3339),
|
||||
},
|
||||
Message: commit.Summary(),
|
||||
Tree: &types.CommitMeta{
|
||||
Tree: &api.CommitMeta{
|
||||
URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/tree/" + commit.ID.String(),
|
||||
SHA: commit.ID.String(),
|
||||
},
|
||||
@@ -1,4 +1,4 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
@@ -98,7 +98,7 @@ func toRepoContent(c *context.APIContext, ref, subpath string, commit *git.Commi
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func getContents(c *context.APIContext) {
|
||||
func GetContents(c *context.APIContext) {
|
||||
repoPath := repoutil.RepositoryPath(c.Params(":username"), c.Params(":reponame"))
|
||||
gitRepo, err := git.Open(repoPath)
|
||||
if err != nil {
|
||||
@@ -168,15 +168,15 @@ func getContents(c *context.APIContext) {
|
||||
c.JSONSuccess(contents)
|
||||
}
|
||||
|
||||
// putContentsRequest is the API message for creating or updating a file.
|
||||
type putContentsRequest struct {
|
||||
// PutContentsRequest is the API message for creating or updating a file.
|
||||
type PutContentsRequest struct {
|
||||
Message string `json:"message" binding:"Required"`
|
||||
Content string `json:"content" binding:"Required"`
|
||||
Branch string `json:"branch"`
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
c.Error(err, "decoding base64")
|
||||
@@ -1,4 +1,4 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/gogs/git-module"
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/database"
|
||||
"gogs.io/gogs/internal/gitutil"
|
||||
routerepo "gogs.io/gogs/internal/route/repo"
|
||||
"gogs.io/gogs/internal/route/repo"
|
||||
)
|
||||
|
||||
func getRawFile(c *context.APIContext) {
|
||||
func GetRawFile(c *context.APIContext) {
|
||||
if !c.Repo.HasAccess() {
|
||||
c.NotFound()
|
||||
return
|
||||
@@ -25,12 +25,12 @@ func getRawFile(c *context.APIContext) {
|
||||
c.NotFoundOrError(gitutil.NewError(err), "get blob")
|
||||
return
|
||||
}
|
||||
if err = routerepo.ServeBlob(c.Context, blob); err != nil {
|
||||
if err = repo.ServeBlob(c.Context, blob); err != nil {
|
||||
c.Error(err, "serve blob")
|
||||
}
|
||||
}
|
||||
|
||||
func getArchive(c *context.APIContext) {
|
||||
func GetArchive(c *context.APIContext) {
|
||||
repoPath := database.RepoPath(c.Params(":username"), c.Params(":reponame"))
|
||||
gitRepo, err := git.Open(repoPath)
|
||||
if err != nil {
|
||||
@@ -39,10 +39,10 @@ func getArchive(c *context.APIContext) {
|
||||
}
|
||||
c.Repo.GitRepo = gitRepo
|
||||
|
||||
routerepo.Download(c.Context)
|
||||
repo.Download(c.Context)
|
||||
}
|
||||
|
||||
func getEditorconfig(c *context.APIContext) {
|
||||
func GetEditorconfig(c *context.APIContext) {
|
||||
ec, err := c.Repo.Editorconfig()
|
||||
if err != nil {
|
||||
c.NotFoundOrError(gitutil.NewError(err), "get .editorconfig")
|
||||
@@ -1,41 +1,36 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"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/database"
|
||||
"gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/route/api/v1/convert"
|
||||
)
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
c.Errorf(err, "get webhooks by repository ID")
|
||||
return
|
||||
}
|
||||
|
||||
apiHooks := make([]*types.RepositoryHook, len(hooks))
|
||||
apiHooks := make([]*api.Hook, len(hooks))
|
||||
for i := range hooks {
|
||||
apiHooks[i] = toRepositoryHook(c.Repo.RepoLink, hooks[i])
|
||||
apiHooks[i] = convert.ToHook(c.Repo.RepoLink, hooks[i])
|
||||
}
|
||||
c.JSONSuccess(&apiHooks)
|
||||
}
|
||||
|
||||
// https://github.com/gogs/go-gogs-client/wiki/Repositories#create-a-hook
|
||||
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) {
|
||||
func CreateHook(c *context.APIContext, form api.CreateHookOption) {
|
||||
if !database.IsValidHookTaskType(form.Type) {
|
||||
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Invalid hook type."))
|
||||
return
|
||||
@@ -81,7 +76,7 @@ func createHook(c *context.APIContext, form createHookRequest) {
|
||||
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Missing config option: channel"))
|
||||
return
|
||||
}
|
||||
meta, err := json.Marshal(&database.SlackMeta{
|
||||
meta, err := jsoniter.Marshal(&database.SlackMeta{
|
||||
Channel: channel,
|
||||
Username: form.Config["username"],
|
||||
IconURL: form.Config["icon_url"],
|
||||
@@ -102,17 +97,11 @@ func createHook(c *context.APIContext, form createHookRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, toRepositoryHook(c.Repo.RepoLink, w))
|
||||
c.JSON(http.StatusCreated, convert.ToHook(c.Repo.RepoLink, w))
|
||||
}
|
||||
|
||||
// https://github.com/gogs/go-gogs-client/wiki/Repositories#edit-a-hook
|
||||
type editHookRequest struct {
|
||||
Config map[string]string `json:"config"`
|
||||
Events []string `json:"events"`
|
||||
Active *bool `json:"active"`
|
||||
}
|
||||
|
||||
func editHook(c *context.APIContext, form editHookRequest) {
|
||||
func EditHook(c *context.APIContext, form api.EditHookOption) {
|
||||
w, err := database.GetWebhookOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get webhook of repository by ID")
|
||||
@@ -133,7 +122,7 @@ func editHook(c *context.APIContext, form editHookRequest) {
|
||||
|
||||
if w.HookTaskType == database.SLACK {
|
||||
if channel, ok := form.Config["channel"]; ok {
|
||||
meta, err := json.Marshal(&database.SlackMeta{
|
||||
meta, err := jsoniter.Marshal(&database.SlackMeta{
|
||||
Channel: channel,
|
||||
Username: form.Config["username"],
|
||||
IconURL: form.Config["icon_url"],
|
||||
@@ -177,10 +166,10 @@ func editHook(c *context.APIContext, form editHookRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSONSuccess(toRepositoryHook(c.Repo.RepoLink, w))
|
||||
c.JSONSuccess(convert.ToHook(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 {
|
||||
c.Errorf(err, "delete webhook of repository by ID")
|
||||
return
|
||||
@@ -1,18 +1,18 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/database"
|
||||
"gogs.io/gogs/internal/route/api/v1/types"
|
||||
)
|
||||
|
||||
func queryIssues(c *context.APIContext, opts *database.IssuesOptions) {
|
||||
func listIssues(c *context.APIContext, opts *database.IssuesOptions) {
|
||||
issues, err := database.Issues(opts)
|
||||
if err != nil {
|
||||
c.Error(err, "list issues")
|
||||
@@ -26,58 +26,49 @@ func queryIssues(c *context.APIContext, opts *database.IssuesOptions) {
|
||||
}
|
||||
|
||||
// FIXME: use IssueList to improve performance.
|
||||
apiIssues := make([]*types.Issue, len(issues))
|
||||
apiIssues := make([]*api.Issue, len(issues))
|
||||
for i := range issues {
|
||||
if err = issues[i].LoadAttributes(); err != nil {
|
||||
c.Error(err, "load attributes")
|
||||
return
|
||||
}
|
||||
apiIssues[i] = toIssue(issues[i])
|
||||
apiIssues[i] = issues[i].APIFormat()
|
||||
}
|
||||
|
||||
c.SetLinkHeader(int(count), conf.UI.IssuePagingNum)
|
||||
c.JSONSuccess(&apiIssues)
|
||||
}
|
||||
|
||||
func listUserIssues(c *context.APIContext) {
|
||||
func ListUserIssues(c *context.APIContext) {
|
||||
opts := database.IssuesOptions{
|
||||
AssigneeID: c.User.ID,
|
||||
Page: c.QueryInt("page"),
|
||||
IsClosed: types.IssueStateType(c.Query("state")) == types.IssueStateClosed,
|
||||
IsClosed: api.StateType(c.Query("state")) == api.STATE_CLOSED,
|
||||
}
|
||||
|
||||
queryIssues(c, &opts)
|
||||
listIssues(c, &opts)
|
||||
}
|
||||
|
||||
func listIssues(c *context.APIContext) {
|
||||
func ListIssues(c *context.APIContext) {
|
||||
opts := database.IssuesOptions{
|
||||
RepoID: c.Repo.Repository.ID,
|
||||
Page: c.QueryInt("page"),
|
||||
IsClosed: types.IssueStateType(c.Query("state")) == types.IssueStateClosed,
|
||||
IsClosed: api.StateType(c.Query("state")) == api.STATE_CLOSED,
|
||||
}
|
||||
|
||||
queryIssues(c, &opts)
|
||||
listIssues(c, &opts)
|
||||
}
|
||||
|
||||
func getIssue(c *context.APIContext) {
|
||||
func GetIssue(c *context.APIContext) {
|
||||
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get issue by index")
|
||||
return
|
||||
}
|
||||
c.JSONSuccess(toIssue(issue))
|
||||
c.JSONSuccess(issue.APIFormat())
|
||||
}
|
||||
|
||||
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) {
|
||||
func CreateIssue(c *context.APIContext, form api.CreateIssueOption) {
|
||||
issue := &database.Issue{
|
||||
RepoID: c.Repo.Repository.ID,
|
||||
Title: form.Title,
|
||||
@@ -123,18 +114,10 @@ func createIssue(c *context.APIContext, form createIssueRequest) {
|
||||
c.Error(err, "get issue by ID")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, toIssue(issue))
|
||||
c.JSON(http.StatusCreated, issue.APIFormat())
|
||||
}
|
||||
|
||||
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) {
|
||||
func EditIssue(c *context.APIContext, form api.EditIssueOption) {
|
||||
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get issue by index")
|
||||
@@ -190,7 +173,7 @@ func editIssue(c *context.APIContext, form editIssueRequest) {
|
||||
return
|
||||
}
|
||||
if form.State != nil {
|
||||
if err = issue.ChangeStatus(c.User, c.Repo.Repository, types.IssueStateClosed == types.IssueStateType(*form.State)); err != nil {
|
||||
if err = issue.ChangeStatus(c.User, c.Repo.Repository, api.STATE_CLOSED == api.StateType(*form.State)); err != nil {
|
||||
c.Error(err, "change status")
|
||||
return
|
||||
}
|
||||
@@ -202,5 +185,5 @@ func editIssue(c *context.APIContext, form editIssueRequest) {
|
||||
c.Error(err, "get issue by ID")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, toIssue(issue))
|
||||
c.JSON(http.StatusCreated, issue.APIFormat())
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"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
|
||||
if len(c.Query("since")) > 0 {
|
||||
var err error
|
||||
@@ -33,14 +34,14 @@ func listIssueComments(c *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
apiComments := make([]*types.IssueComment, len(comments))
|
||||
apiComments := make([]*api.Comment, len(comments))
|
||||
for i := range comments {
|
||||
apiComments[i] = toIssueComment(comments[i])
|
||||
apiComments[i] = comments[i].APIFormat()
|
||||
}
|
||||
c.JSONSuccess(&apiComments)
|
||||
}
|
||||
|
||||
func listRepoIssueComments(c *context.APIContext) {
|
||||
func ListRepoIssueComments(c *context.APIContext) {
|
||||
var since time.Time
|
||||
if len(c.Query("since")) > 0 {
|
||||
var err error
|
||||
@@ -57,18 +58,14 @@ func listRepoIssueComments(c *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
apiComments := make([]*types.IssueComment, len(comments))
|
||||
apiComments := make([]*api.Comment, len(comments))
|
||||
for i := range comments {
|
||||
apiComments[i] = toIssueComment(comments[i])
|
||||
apiComments[i] = comments[i].APIFormat()
|
||||
}
|
||||
c.JSONSuccess(&apiComments)
|
||||
}
|
||||
|
||||
type createIssueCommentRequest struct {
|
||||
Body string `json:"body" binding:"Required"`
|
||||
}
|
||||
|
||||
func createIssueComment(c *context.APIContext, form createIssueCommentRequest) {
|
||||
func CreateIssueComment(c *context.APIContext, form api.CreateIssueCommentOption) {
|
||||
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
c.Error(err, "get issue by index")
|
||||
@@ -81,14 +78,10 @@ func createIssueComment(c *context.APIContext, form createIssueCommentRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, toIssueComment(comment))
|
||||
c.JSON(http.StatusCreated, comment.APIFormat())
|
||||
}
|
||||
|
||||
type editIssueCommentRequest struct {
|
||||
Body string `json:"body" binding:"Required"`
|
||||
}
|
||||
|
||||
func editIssueComment(c *context.APIContext, form editIssueCommentRequest) {
|
||||
func EditIssueComment(c *context.APIContext, form api.EditIssueCommentOption) {
|
||||
comment, err := database.GetCommentByID(c.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get comment by ID")
|
||||
@@ -120,10 +113,10 @@ func editIssueComment(c *context.APIContext, form editIssueCommentRequest) {
|
||||
c.Error(err, "update comment")
|
||||
return
|
||||
}
|
||||
c.JSONSuccess(toIssueComment(comment))
|
||||
c.JSONSuccess(comment.APIFormat())
|
||||
}
|
||||
|
||||
func deleteIssueComment(c *context.APIContext) {
|
||||
func DeleteIssueComment(c *context.APIContext) {
|
||||
comment, err := database.GetCommentByID(c.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get comment by ID")
|
||||
@@ -1,32 +1,29 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"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"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get issue by index")
|
||||
return
|
||||
}
|
||||
|
||||
apiLabels := make([]*types.IssueLabel, len(issue.Labels))
|
||||
apiLabels := make([]*api.Label, len(issue.Labels))
|
||||
for i := range issue.Labels {
|
||||
apiLabels[i] = toIssueLabel(issue.Labels[i])
|
||||
apiLabels[i] = issue.Labels[i].APIFormat()
|
||||
}
|
||||
c.JSONSuccess(&apiLabels)
|
||||
}
|
||||
|
||||
type issueLabelsRequest struct {
|
||||
Labels []int64 `json:"labels"`
|
||||
}
|
||||
|
||||
func addIssueLabels(c *context.APIContext, form issueLabelsRequest) {
|
||||
func AddIssueLabels(c *context.APIContext, form api.IssueLabelsOption) {
|
||||
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get issue by index")
|
||||
@@ -50,14 +47,14 @@ func addIssueLabels(c *context.APIContext, form issueLabelsRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
apiLabels := make([]*types.IssueLabel, len(labels))
|
||||
apiLabels := make([]*api.Label, len(labels))
|
||||
for i := range labels {
|
||||
apiLabels[i] = toIssueLabel(issue.Labels[i])
|
||||
apiLabels[i] = issue.Labels[i].APIFormat()
|
||||
}
|
||||
c.JSONSuccess(&apiLabels)
|
||||
}
|
||||
|
||||
func deleteIssueLabel(c *context.APIContext) {
|
||||
func DeleteIssueLabel(c *context.APIContext) {
|
||||
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get issue by index")
|
||||
@@ -82,7 +79,7 @@ func deleteIssueLabel(c *context.APIContext) {
|
||||
c.NoContent()
|
||||
}
|
||||
|
||||
func replaceIssueLabels(c *context.APIContext, form issueLabelsRequest) {
|
||||
func ReplaceIssueLabels(c *context.APIContext, form api.IssueLabelsOption) {
|
||||
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get issue by index")
|
||||
@@ -106,14 +103,14 @@ func replaceIssueLabels(c *context.APIContext, form issueLabelsRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
apiLabels := make([]*types.IssueLabel, len(labels))
|
||||
apiLabels := make([]*api.Label, len(labels))
|
||||
for i := range labels {
|
||||
apiLabels[i] = toIssueLabel(issue.Labels[i])
|
||||
apiLabels[i] = issue.Labels[i].APIFormat()
|
||||
}
|
||||
c.JSONSuccess(&apiLabels)
|
||||
}
|
||||
|
||||
func clearIssueLabels(c *context.APIContext) {
|
||||
func ClearIssueLabels(c *context.APIContext) {
|
||||
issue, err := database.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get issue by index")
|
||||
@@ -1,14 +1,15 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/database"
|
||||
"gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/route/api/v1/convert"
|
||||
)
|
||||
|
||||
func composeDeployKeysAPILink(repoPath string) string {
|
||||
@@ -16,7 +17,7 @@ func composeDeployKeysAPILink(repoPath string) string {
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
c.Error(err, "list deploy keys")
|
||||
@@ -24,20 +25,20 @@ func listDeployKeys(c *context.APIContext) {
|
||||
}
|
||||
|
||||
apiLink := composeDeployKeysAPILink(c.Repo.Owner.Name + "/" + c.Repo.Repository.Name)
|
||||
apiKeys := make([]*types.RepositoryDeployKey, len(keys))
|
||||
apiKeys := make([]*api.DeployKey, len(keys))
|
||||
for i := range keys {
|
||||
if err = keys[i].GetContent(); err != nil {
|
||||
c.Error(err, "get content")
|
||||
return
|
||||
}
|
||||
apiKeys[i] = toDeployKey(apiLink, keys[i])
|
||||
apiKeys[i] = convert.ToDeployKey(apiLink, keys[i])
|
||||
}
|
||||
|
||||
c.JSONSuccess(&apiKeys)
|
||||
}
|
||||
|
||||
// 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"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get deploy key by ID")
|
||||
@@ -55,10 +56,10 @@ func getDeployKey(c *context.APIContext) {
|
||||
}
|
||||
|
||||
apiLink := composeDeployKeysAPILink(c.Repo.Owner.Name + "/" + c.Repo.Repository.Name)
|
||||
c.JSONSuccess(toDeployKey(apiLink, key))
|
||||
c.JSONSuccess(convert.ToDeployKey(apiLink, key))
|
||||
}
|
||||
|
||||
func handleCheckKeyStringError(c *context.APIContext, err error) {
|
||||
func HandleCheckKeyStringError(c *context.APIContext, err error) {
|
||||
if database.IsErrKeyUnableVerify(err) {
|
||||
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Unable to verify key content"))
|
||||
} else {
|
||||
@@ -66,7 +67,7 @@ func handleCheckKeyStringError(c *context.APIContext, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleAddKeyError(c *context.APIContext, err error) {
|
||||
func HandleAddKeyError(c *context.APIContext, err error) {
|
||||
switch {
|
||||
case database.IsErrKeyAlreadyExist(err):
|
||||
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Key content has been used as non-deploy key"))
|
||||
@@ -78,31 +79,26 @@ func handleAddKeyError(c *context.APIContext, err error) {
|
||||
}
|
||||
|
||||
// https://github.com/gogs/go-gogs-client/wiki/Repositories-Deploy-Keys#add-a-new-deploy-key
|
||||
type createDeployKeyRequest struct {
|
||||
Title string `json:"title" binding:"Required"`
|
||||
Key string `json:"key" binding:"Required"`
|
||||
}
|
||||
|
||||
func createDeployKey(c *context.APIContext, form createDeployKeyRequest) {
|
||||
func CreateDeployKey(c *context.APIContext, form api.CreateKeyOption) {
|
||||
content, err := database.CheckPublicKeyString(form.Key)
|
||||
if err != nil {
|
||||
handleCheckKeyStringError(c, err)
|
||||
HandleCheckKeyStringError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := database.AddDeployKey(c.Repo.Repository.ID, form.Title, content)
|
||||
if err != nil {
|
||||
handleAddKeyError(c, err)
|
||||
HandleAddKeyError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
key.Content = content
|
||||
apiLink := composeDeployKeysAPILink(c.Repo.Owner.Name + "/" + c.Repo.Repository.Name)
|
||||
c.JSON(http.StatusCreated, toDeployKey(apiLink, key))
|
||||
c.JSON(http.StatusCreated, convert.ToDeployKey(apiLink, 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"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get deploy key by ID")
|
||||
@@ -1,29 +1,30 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"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)
|
||||
if err != nil {
|
||||
c.Error(err, "get labels by repository ID")
|
||||
return
|
||||
}
|
||||
|
||||
apiLabels := make([]*types.IssueLabel, len(labels))
|
||||
apiLabels := make([]*api.Label, len(labels))
|
||||
for i := range labels {
|
||||
apiLabels[i] = toIssueLabel(labels[i])
|
||||
apiLabels[i] = labels[i].APIFormat()
|
||||
}
|
||||
c.JSONSuccess(&apiLabels)
|
||||
}
|
||||
|
||||
func getLabel(c *context.APIContext) {
|
||||
func GetLabel(c *context.APIContext) {
|
||||
var label *database.Label
|
||||
var err error
|
||||
idStr := c.Params(":id")
|
||||
@@ -37,15 +38,10 @@ func getLabel(c *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSONSuccess(toIssueLabel(label))
|
||||
c.JSONSuccess(label.APIFormat())
|
||||
}
|
||||
|
||||
type createLabelRequest struct {
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Color string `json:"color" binding:"Required;Size(7)"`
|
||||
}
|
||||
|
||||
func createLabel(c *context.APIContext, form createLabelRequest) {
|
||||
func CreateLabel(c *context.APIContext, form api.CreateLabelOption) {
|
||||
label := &database.Label{
|
||||
Name: form.Name,
|
||||
Color: form.Color,
|
||||
@@ -55,15 +51,10 @@ func createLabel(c *context.APIContext, form createLabelRequest) {
|
||||
c.Error(err, "new labels")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, toIssueLabel(label))
|
||||
c.JSON(http.StatusCreated, label.APIFormat())
|
||||
}
|
||||
|
||||
type editLabelRequest struct {
|
||||
Name *string `json:"name"`
|
||||
Color *string `json:"color"`
|
||||
}
|
||||
|
||||
func editLabel(c *context.APIContext, form editLabelRequest) {
|
||||
func EditLabel(c *context.APIContext, form api.EditLabelOption) {
|
||||
label, err := database.GetLabelOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get label of repository by ID")
|
||||
@@ -80,10 +71,10 @@ func editLabel(c *context.APIContext, form editLabelRequest) {
|
||||
c.Error(err, "update label")
|
||||
return
|
||||
}
|
||||
c.JSONSuccess(toIssueLabel(label))
|
||||
c.JSONSuccess(label.APIFormat())
|
||||
}
|
||||
|
||||
func deleteLabel(c *context.APIContext) {
|
||||
func DeleteLabel(c *context.APIContext) {
|
||||
if err := database.DeleteLabel(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil {
|
||||
c.Error(err, "delete label")
|
||||
return
|
||||
@@ -1,44 +1,39 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"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)
|
||||
if err != nil {
|
||||
c.Error(err, "get milestones by repository ID")
|
||||
return
|
||||
}
|
||||
|
||||
apiMilestones := make([]*types.IssueMilestone, len(milestones))
|
||||
apiMilestones := make([]*api.Milestone, len(milestones))
|
||||
for i := range milestones {
|
||||
apiMilestones[i] = toIssueMilestone(milestones[i])
|
||||
apiMilestones[i] = milestones[i].APIFormat()
|
||||
}
|
||||
c.JSONSuccess(&apiMilestones)
|
||||
}
|
||||
|
||||
func getMilestone(c *context.APIContext) {
|
||||
func GetMilestone(c *context.APIContext) {
|
||||
milestone, err := database.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get milestone by repository ID")
|
||||
return
|
||||
}
|
||||
c.JSONSuccess(toIssueMilestone(milestone))
|
||||
c.JSONSuccess(milestone.APIFormat())
|
||||
}
|
||||
|
||||
type createMilestoneRequest struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Deadline *time.Time `json:"due_on"`
|
||||
}
|
||||
|
||||
func createMilestone(c *context.APIContext, form createMilestoneRequest) {
|
||||
func CreateMilestone(c *context.APIContext, form api.CreateMilestoneOption) {
|
||||
if form.Deadline == nil {
|
||||
defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local)
|
||||
form.Deadline = &defaultDeadline
|
||||
@@ -55,17 +50,10 @@ func createMilestone(c *context.APIContext, form createMilestoneRequest) {
|
||||
c.Error(err, "new milestone")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, toIssueMilestone(milestone))
|
||||
c.JSON(http.StatusCreated, milestone.APIFormat())
|
||||
}
|
||||
|
||||
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) {
|
||||
func EditMilestone(c *context.APIContext, form api.EditMilestoneOption) {
|
||||
milestone, err := database.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get milestone by repository ID")
|
||||
@@ -83,7 +71,7 @@ func editMilestone(c *context.APIContext, form editMilestoneRequest) {
|
||||
}
|
||||
|
||||
if form.State != nil {
|
||||
if err = milestone.ChangeStatus(types.IssueStateClosed == types.IssueStateType(*form.State)); err != nil {
|
||||
if err = milestone.ChangeStatus(api.STATE_CLOSED == api.StateType(*form.State)); err != nil {
|
||||
c.Error(err, "change status")
|
||||
return
|
||||
}
|
||||
@@ -92,10 +80,10 @@ func editMilestone(c *context.APIContext, form editMilestoneRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSONSuccess(toIssueMilestone(milestone))
|
||||
c.JSONSuccess(milestone.APIFormat())
|
||||
}
|
||||
|
||||
func deleteMilestone(c *context.APIContext) {
|
||||
func DeleteMilestone(c *context.APIContext) {
|
||||
if err := database.DeleteMilestoneOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil {
|
||||
c.Error(err, "delete milestone of repository by ID")
|
||||
return
|
||||
@@ -1,24 +1,25 @@
|
||||
package v1
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
api "github.com/gogs/go-gogs-client"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
||||
"gogs.io/gogs/internal/conf"
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/database"
|
||||
"gogs.io/gogs/internal/form"
|
||||
"gogs.io/gogs/internal/route/api/v1/types"
|
||||
"gogs.io/gogs/internal/route/api/v1/convert"
|
||||
)
|
||||
|
||||
func searchRepos(c *context.APIContext) {
|
||||
func Search(c *context.APIContext) {
|
||||
opts := &database.SearchRepoOptions{
|
||||
Keyword: path.Base(c.Query("q")),
|
||||
OwnerID: c.QueryInt64("uid"),
|
||||
PageSize: toAllowedPageSize(c.QueryInt("limit")),
|
||||
PageSize: convert.ToCorrectPageSize(c.QueryInt("limit")),
|
||||
Page: c.QueryInt("page"),
|
||||
}
|
||||
|
||||
@@ -59,9 +60,9 @@ func searchRepos(c *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
results := make([]*types.Repository, len(repos))
|
||||
results := make([]*api.Repository, len(repos))
|
||||
for i := range repos {
|
||||
results[i] = toRepository(repos[i], nil)
|
||||
results[i] = repos[i].APIFormatLegacy(nil)
|
||||
}
|
||||
|
||||
c.SetLinkHeader(int(count), opts.PageSize)
|
||||
@@ -71,7 +72,7 @@ func searchRepos(c *context.APIContext) {
|
||||
})
|
||||
}
|
||||
|
||||
func listReposOfUser(c *context.APIContext, username string) {
|
||||
func listUserRepositories(c *context.APIContext, username string) {
|
||||
user, err := database.Handle.Users().GetByUsername(c.Req.Context(), username)
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get user by name")
|
||||
@@ -103,9 +104,9 @@ func listReposOfUser(c *context.APIContext, username string) {
|
||||
|
||||
// Early return for querying other user's repositories
|
||||
if c.User.ID != user.ID {
|
||||
repos := make([]*types.Repository, len(ownRepos))
|
||||
repos := make([]*api.Repository, len(ownRepos))
|
||||
for i := range ownRepos {
|
||||
repos[i] = toRepository(ownRepos[i], &types.RepositoryPermission{Admin: true, Push: true, Pull: true})
|
||||
repos[i] = ownRepos[i].APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true})
|
||||
}
|
||||
c.JSONSuccess(&repos)
|
||||
return
|
||||
@@ -127,14 +128,14 @@ func listReposOfUser(c *context.APIContext, username string) {
|
||||
}
|
||||
|
||||
numOwnRepos := len(ownRepos)
|
||||
repos := make([]*types.Repository, 0, numOwnRepos+len(accessibleReposWithAccessMode))
|
||||
repos := make([]*api.Repository, 0, numOwnRepos+len(accessibleReposWithAccessMode))
|
||||
for _, r := range ownRepos {
|
||||
repos = append(repos, toRepository(r, &types.RepositoryPermission{Admin: true, Push: true, Pull: true}))
|
||||
repos = append(repos, r.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true}))
|
||||
}
|
||||
|
||||
for repo, access := range accessibleReposWithAccessMode {
|
||||
repos = append(repos,
|
||||
toRepository(repo, &types.RepositoryPermission{
|
||||
repo.APIFormatLegacy(&api.Permission{
|
||||
Admin: access >= database.AccessModeAdmin,
|
||||
Push: access >= database.AccessModeWrite,
|
||||
Pull: true,
|
||||
@@ -145,29 +146,19 @@ func listReposOfUser(c *context.APIContext, username string) {
|
||||
c.JSONSuccess(&repos)
|
||||
}
|
||||
|
||||
func listMyRepos(c *context.APIContext) {
|
||||
listReposOfUser(c, c.User.Name)
|
||||
func ListMyRepos(c *context.APIContext) {
|
||||
listUserRepositories(c, c.User.Name)
|
||||
}
|
||||
|
||||
func listUserRepositories(c *context.APIContext) {
|
||||
listReposOfUser(c, c.Params(":username"))
|
||||
func ListUserRepositories(c *context.APIContext) {
|
||||
listUserRepositories(c, c.Params(":username"))
|
||||
}
|
||||
|
||||
func listOrgRepositories(c *context.APIContext) {
|
||||
listReposOfUser(c, c.Params(":org"))
|
||||
func ListOrgRepositories(c *context.APIContext) {
|
||||
listUserRepositories(c, c.Params(":org"))
|
||||
}
|
||||
|
||||
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) {
|
||||
func CreateUserRepo(c *context.APIContext, owner *database.User, opt api.CreateRepoOption) {
|
||||
repo, err := database.CreateRepository(c.User, owner, database.CreateRepoOptionsLegacy{
|
||||
Name: opt.Name,
|
||||
Description: opt.Description,
|
||||
@@ -192,19 +183,19 @@ func createUserRepo(c *context.APIContext, owner *database.User, opt createRepoR
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(201, toRepository(repo, &types.RepositoryPermission{Admin: true, Push: true, Pull: true}))
|
||||
c.JSON(201, repo.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true}))
|
||||
}
|
||||
|
||||
func createRepo(c *context.APIContext, opt createRepoRequest) {
|
||||
func Create(c *context.APIContext, opt api.CreateRepoOption) {
|
||||
// Shouldn't reach this condition, but just in case.
|
||||
if c.User.IsOrganization() {
|
||||
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("Not allowed to create repository for organization."))
|
||||
return
|
||||
}
|
||||
createUserRepo(c, c.User, opt)
|
||||
CreateUserRepo(c, c.User, opt)
|
||||
}
|
||||
|
||||
func createOrgRepo(c *context.APIContext, opt createRepoRequest) {
|
||||
func CreateOrgRepo(c *context.APIContext, opt api.CreateRepoOption) {
|
||||
org, err := database.GetOrgByName(c.Params(":org"))
|
||||
if err != nil {
|
||||
c.NotFoundOrError(err, "get organization by name")
|
||||
@@ -215,10 +206,10 @@ func createOrgRepo(c *context.APIContext, opt createRepoRequest) {
|
||||
c.ErrorStatus(http.StatusForbidden, errors.New("Given user is not owner of organization."))
|
||||
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
|
||||
// Not equal means context user is an organization,
|
||||
// or is another user/organization if current user is admin.
|
||||
@@ -296,7 +287,7 @@ func migrate(c *context.APIContext, f form.MigrateRepo) {
|
||||
}
|
||||
|
||||
log.Trace("Repository migrated: %s/%s", ctxUser.Name, f.RepoName)
|
||||
c.JSON(201, toRepository(repo, &types.RepositoryPermission{Admin: true, Push: true, Pull: true}))
|
||||
c.JSON(201, repo.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true}))
|
||||
}
|
||||
|
||||
// FIXME: inject in the handler chain
|
||||
@@ -320,20 +311,20 @@ func parseOwnerAndRepo(c *context.APIContext) (*database.User, *database.Reposit
|
||||
return owner, repo
|
||||
}
|
||||
|
||||
func getRepo(c *context.APIContext) {
|
||||
func Get(c *context.APIContext) {
|
||||
_, repo := parseOwnerAndRepo(c)
|
||||
if c.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSONSuccess(toRepository(repo, &types.RepositoryPermission{
|
||||
c.JSONSuccess(repo.APIFormatLegacy(&api.Permission{
|
||||
Admin: c.Repo.IsAdmin(),
|
||||
Push: c.Repo.IsWriter(),
|
||||
Pull: true,
|
||||
}))
|
||||
}
|
||||
|
||||
func deleteRepo(c *context.APIContext) {
|
||||
func Delete(c *context.APIContext) {
|
||||
owner, repo := parseOwnerAndRepo(c)
|
||||
if c.Written() {
|
||||
return
|
||||
@@ -353,14 +344,14 @@ func deleteRepo(c *context.APIContext) {
|
||||
c.NoContent()
|
||||
}
|
||||
|
||||
func listForks(c *context.APIContext) {
|
||||
func ListForks(c *context.APIContext) {
|
||||
forks, err := c.Repo.Repository.GetForks()
|
||||
if err != nil {
|
||||
c.Error(err, "get forks")
|
||||
return
|
||||
}
|
||||
|
||||
apiForks := make([]*types.Repository, len(forks))
|
||||
apiForks := make([]*api.Repository, len(forks))
|
||||
for i := range forks {
|
||||
if err := forks[i].GetOwner(); err != nil {
|
||||
c.Error(err, "get owner")
|
||||
@@ -377,8 +368,8 @@ func listForks(c *context.APIContext) {
|
||||
},
|
||||
)
|
||||
|
||||
apiForks[i] = toRepository(forks[i],
|
||||
&types.RepositoryPermission{
|
||||
apiForks[i] = forks[i].APIFormatLegacy(
|
||||
&api.Permission{
|
||||
Admin: accessMode >= database.AccessModeAdmin,
|
||||
Push: accessMode >= database.AccessModeWrite,
|
||||
Pull: true,
|
||||
@@ -389,15 +380,7 @@ func listForks(c *context.APIContext) {
|
||||
c.JSONSuccess(&apiForks)
|
||||
}
|
||||
|
||||
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) {
|
||||
func IssueTracker(c *context.APIContext, form api.EditIssueTrackerOption) {
|
||||
_, repo := parseOwnerAndRepo(c)
|
||||
if c.Written() {
|
||||
return
|
||||
@@ -427,14 +410,7 @@ func issueTracker(c *context.APIContext, form editIssueTrackerRequest) {
|
||||
c.NoContent()
|
||||
}
|
||||
|
||||
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) {
|
||||
func Wiki(c *context.APIContext, form api.EditWikiOption) {
|
||||
_, repo := parseOwnerAndRepo(c)
|
||||
if c.Written() {
|
||||
return
|
||||
@@ -460,7 +436,7 @@ func wiki(c *context.APIContext, form editWikiRequest) {
|
||||
c.NoContent()
|
||||
}
|
||||
|
||||
func mirrorSync(c *context.APIContext) {
|
||||
func MirrorSync(c *context.APIContext) {
|
||||
_, repo := parseOwnerAndRepo(c)
|
||||
if c.Written() {
|
||||
return
|
||||
@@ -473,14 +449,14 @@ func mirrorSync(c *context.APIContext) {
|
||||
c.Status(http.StatusAccepted)
|
||||
}
|
||||
|
||||
func releases(c *context.APIContext) {
|
||||
func Releases(c *context.APIContext) {
|
||||
_, repo := parseOwnerAndRepo(c)
|
||||
releases, err := database.GetReleasesByRepoID(repo.ID)
|
||||
if err != nil {
|
||||
c.Error(err, "get releases by repository ID")
|
||||
return
|
||||
}
|
||||
apiReleases := make([]*types.RepositoryRelease, 0, len(releases))
|
||||
apiReleases := make([]*api.Release, 0, len(releases))
|
||||
for _, r := range releases {
|
||||
publisher, err := database.Handle.Users().GetByID(c.Req.Context(), r.PublisherID)
|
||||
if err != nil {
|
||||
@@ -490,7 +466,7 @@ func releases(c *context.APIContext) {
|
||||
r.Publisher = publisher
|
||||
}
|
||||
for _, r := range releases {
|
||||
apiReleases = append(apiReleases, toRelease(r))
|
||||
apiReleases = append(apiReleases, r.APIFormat())
|
||||
}
|
||||
|
||||
c.JSONSuccess(&apiReleases)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user