mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-18 00:01:32 -04:00
Compare commits
7 Commits
d647e74502
...
f20bf2fe3b
Author | SHA1 | Date | |
---|---|---|---|
|
f20bf2fe3b | ||
|
3de9e63fd0 | ||
|
5155ec35c5 | ||
|
cf29ee6dd2 | ||
|
2173f14708 | ||
|
dad057b639 | ||
|
f92e0a4018 |
482
.drone.yml
482
.drone.yml
@ -32,6 +32,7 @@ steps:
|
|||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- make deps-backend
|
- make deps-backend
|
||||||
|
- make deps-tools
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /go
|
path: /go
|
||||||
@ -168,7 +169,112 @@ steps:
|
|||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: testing-amd64
|
name: testing-pgsql
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- compliance
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
- pull_request
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- docs/**
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
services:
|
||||||
|
- name: pgsql
|
||||||
|
pull: default
|
||||||
|
image: postgres:15
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: test
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
|
||||||
|
- name: ldap
|
||||||
|
image: gitea/test-openldap:latest
|
||||||
|
pull: always
|
||||||
|
|
||||||
|
- name: minio
|
||||||
|
image: minio/minio:RELEASE.2021-03-12T00-00-47Z
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- minio server /data
|
||||||
|
environment:
|
||||||
|
MINIO_ACCESS_KEY: 123456
|
||||||
|
MINIO_SECRET_KEY: 12345678
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
|
- git fetch --tags --force
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: deps-backend
|
||||||
|
image: golang:1.20
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- make deps-backend
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: prepare-test-env
|
||||||
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- ./build/test-env-prepare.sh
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
|
user: gitea
|
||||||
|
commands:
|
||||||
|
- ./build/test-env-check.sh
|
||||||
|
- make backend
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
|
GOSUMDB: sum.golang.org
|
||||||
|
TAGS: bindata
|
||||||
|
depends_on: [deps-backend, prepare-test-env]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: test-pgsql
|
||||||
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
|
user: gitea
|
||||||
|
commands:
|
||||||
|
- timeout -s ABRT 50m make test-pgsql-migration test-pgsql
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io
|
||||||
|
TAGS: bindata gogit
|
||||||
|
RACE_ENABLED: true
|
||||||
|
TEST_TAGS: gogit
|
||||||
|
TEST_LDAP: 1
|
||||||
|
USE_REPO_TEST_DIR: 1
|
||||||
|
depends_on: [build]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: testing-mysql
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
os: linux
|
os: linux
|
||||||
@ -198,40 +304,12 @@ services:
|
|||||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||||
MYSQL_DATABASE: test
|
MYSQL_DATABASE: test
|
||||||
|
|
||||||
- name: mysql8
|
|
||||||
image: mysql:8
|
|
||||||
pull: always
|
|
||||||
environment:
|
|
||||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
|
||||||
MYSQL_DATABASE: testgitea
|
|
||||||
|
|
||||||
- name: mssql
|
|
||||||
image: mcr.microsoft.com/mssql/server:latest
|
|
||||||
pull: always
|
|
||||||
environment:
|
|
||||||
ACCEPT_EULA: Y
|
|
||||||
MSSQL_PID: Standard
|
|
||||||
SA_PASSWORD: MwantsaSecurePassword1
|
|
||||||
|
|
||||||
- name: ldap
|
|
||||||
image: gitea/test-openldap:latest
|
|
||||||
pull: always
|
|
||||||
|
|
||||||
- name: elasticsearch
|
- name: elasticsearch
|
||||||
image: elasticsearch:7.5.0
|
image: elasticsearch:7.5.0
|
||||||
pull: always
|
pull: always
|
||||||
environment:
|
environment:
|
||||||
discovery.type: single-node
|
discovery.type: single-node
|
||||||
|
|
||||||
- name: minio
|
|
||||||
image: minio/minio:RELEASE.2021-03-12T00-00-47Z
|
|
||||||
pull: always
|
|
||||||
commands:
|
|
||||||
- minio server /data
|
|
||||||
environment:
|
|
||||||
MINIO_ACCESS_KEY: 123456
|
|
||||||
MINIO_SECRET_KEY: 12345678
|
|
||||||
|
|
||||||
- name: smtpimap
|
- name: smtpimap
|
||||||
image: tabascoterrier/docker-imap-devel:latest
|
image: tabascoterrier/docker-imap-devel:latest
|
||||||
pull: always
|
pull: always
|
||||||
@ -257,12 +335,6 @@ steps:
|
|||||||
- name: deps
|
- name: deps
|
||||||
path: /go
|
path: /go
|
||||||
|
|
||||||
- name: tag-pre-condition
|
|
||||||
image: drone/git
|
|
||||||
pull: always
|
|
||||||
commands:
|
|
||||||
- git update-ref refs/heads/tag_test ${DRONE_COMMIT_SHA}
|
|
||||||
|
|
||||||
- name: prepare-test-env
|
- name: prepare-test-env
|
||||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
pull: always
|
pull: always
|
||||||
@ -278,7 +350,7 @@ steps:
|
|||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
GOSUMDB: sum.golang.org
|
GOSUMDB: sum.golang.org
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata
|
||||||
depends_on: [deps-backend, prepare-test-env]
|
depends_on: [deps-backend, prepare-test-env]
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
@ -291,7 +363,7 @@ steps:
|
|||||||
- make unit-test-coverage test-check
|
- make unit-test-coverage test-check
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.io
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
GITHUB_READ_TOKEN:
|
GITHUB_READ_TOKEN:
|
||||||
from_secret: github_read_token
|
from_secret: github_read_token
|
||||||
@ -307,7 +379,7 @@ steps:
|
|||||||
- make unit-test-coverage test-check
|
- make unit-test-coverage test-check
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.io
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
GITHUB_READ_TOKEN:
|
GITHUB_READ_TOKEN:
|
||||||
from_secret: github_read_token
|
from_secret: github_read_token
|
||||||
@ -325,7 +397,6 @@ steps:
|
|||||||
GOPROXY: https://goproxy.io
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
TEST_LDAP: 1
|
|
||||||
USE_REPO_TEST_DIR: 1
|
USE_REPO_TEST_DIR: 1
|
||||||
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
|
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
|
||||||
depends_on: [build]
|
depends_on: [build]
|
||||||
@ -333,38 +404,6 @@ steps:
|
|||||||
- name: deps
|
- name: deps
|
||||||
path: /go
|
path: /go
|
||||||
|
|
||||||
- name: test-mysql8
|
|
||||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
|
||||||
user: gitea
|
|
||||||
commands:
|
|
||||||
- timeout -s ABRT 50m make test-mysql8-migration test-mysql8
|
|
||||||
environment:
|
|
||||||
GOPROXY: https://goproxy.io
|
|
||||||
TAGS: bindata
|
|
||||||
RACE_ENABLED: true
|
|
||||||
TEST_LDAP: 1
|
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
depends_on: [build]
|
|
||||||
volumes:
|
|
||||||
- name: deps
|
|
||||||
path: /go
|
|
||||||
|
|
||||||
- name: test-mssql
|
|
||||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
|
||||||
user: gitea
|
|
||||||
commands:
|
|
||||||
- make test-mssql-migration test-mssql
|
|
||||||
environment:
|
|
||||||
GOPROXY: https://goproxy.io
|
|
||||||
TAGS: bindata
|
|
||||||
RACE_ENABLED: true
|
|
||||||
TEST_LDAP: 1
|
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
depends_on: [build]
|
|
||||||
volumes:
|
|
||||||
- name: deps
|
|
||||||
path: /go
|
|
||||||
|
|
||||||
- name: generate-coverage
|
- name: generate-coverage
|
||||||
image: golang:1.20
|
image: golang:1.20
|
||||||
commands:
|
commands:
|
||||||
@ -398,7 +437,186 @@ steps:
|
|||||||
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: testing-arm64
|
type: docker
|
||||||
|
name: testing-mysql8
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- compliance
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
- pull_request
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- docs/**
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
services:
|
||||||
|
- name: mysql8
|
||||||
|
image: mysql:8
|
||||||
|
pull: always
|
||||||
|
environment:
|
||||||
|
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||||
|
MYSQL_DATABASE: testgitea
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
|
- git fetch --tags --force
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: deps-backend
|
||||||
|
image: golang:1.20
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- make deps-backend
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: prepare-test-env
|
||||||
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- ./build/test-env-prepare.sh
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
|
user: gitea
|
||||||
|
commands:
|
||||||
|
- ./build/test-env-check.sh
|
||||||
|
- make backend
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
|
GOSUMDB: sum.golang.org
|
||||||
|
TAGS: bindata
|
||||||
|
depends_on: [deps-backend, prepare-test-env]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: test-mysql8
|
||||||
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
|
user: gitea
|
||||||
|
commands:
|
||||||
|
- timeout -s ABRT 50m make test-mysql8-migration test-mysql8
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io
|
||||||
|
TAGS: bindata
|
||||||
|
USE_REPO_TEST_DIR: 1
|
||||||
|
depends_on: [build]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: testing-mssql
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- compliance
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
- pull_request
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- docs/**
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
services:
|
||||||
|
- name: mssql
|
||||||
|
image: mcr.microsoft.com/mssql/server:latest
|
||||||
|
pull: always
|
||||||
|
environment:
|
||||||
|
ACCEPT_EULA: Y
|
||||||
|
MSSQL_PID: Standard
|
||||||
|
SA_PASSWORD: MwantsaSecurePassword1
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
|
- git fetch --tags --force
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: deps-backend
|
||||||
|
image: golang:1.20
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- make deps-backend
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: prepare-test-env
|
||||||
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- ./build/test-env-prepare.sh
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
|
user: gitea
|
||||||
|
commands:
|
||||||
|
- ./build/test-env-check.sh
|
||||||
|
- make backend
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
|
GOSUMDB: sum.golang.org
|
||||||
|
TAGS: bindata
|
||||||
|
depends_on: [deps-backend, prepare-test-env]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: test-mssql
|
||||||
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
|
user: gitea
|
||||||
|
commands:
|
||||||
|
- make test-mssql-migration test-mssql
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io
|
||||||
|
TAGS: bindata
|
||||||
|
USE_REPO_TEST_DIR: 1
|
||||||
|
depends_on: [build]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: testing-sqlite
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
os: linux
|
os: linux
|
||||||
@ -420,18 +638,6 @@ volumes:
|
|||||||
- name: deps
|
- name: deps
|
||||||
temp: {}
|
temp: {}
|
||||||
|
|
||||||
services:
|
|
||||||
- name: pgsql
|
|
||||||
pull: default
|
|
||||||
image: postgres:10
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: test
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
|
|
||||||
- name: ldap
|
|
||||||
pull: default
|
|
||||||
image: gitea/test-openldap:latest
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: fetch-tags
|
- name: fetch-tags
|
||||||
image: docker:git
|
image: docker:git
|
||||||
@ -490,23 +696,6 @@ steps:
|
|||||||
- name: deps
|
- name: deps
|
||||||
path: /go
|
path: /go
|
||||||
|
|
||||||
- name: test-pgsql
|
|
||||||
image: gitea/test_env:linux-arm64 # https://gitea.com/gitea/test-env
|
|
||||||
user: gitea
|
|
||||||
commands:
|
|
||||||
- timeout -s ABRT 50m make test-pgsql-migration test-pgsql
|
|
||||||
environment:
|
|
||||||
GOPROXY: https://goproxy.io
|
|
||||||
TAGS: bindata gogit
|
|
||||||
RACE_ENABLED: true
|
|
||||||
TEST_TAGS: gogit
|
|
||||||
TEST_LDAP: 1
|
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
depends_on: [build]
|
|
||||||
volumes:
|
|
||||||
- name: deps
|
|
||||||
path: /go
|
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
@ -530,15 +719,6 @@ volumes:
|
|||||||
- name: deps
|
- name: deps
|
||||||
temp: {}
|
temp: {}
|
||||||
|
|
||||||
services:
|
|
||||||
- name: pgsql
|
|
||||||
pull: default
|
|
||||||
image: postgres:10
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: testgitea-e2e
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
POSTGRES_INITDB_ARGS: --encoding=UTF8 --lc-collate='en_US.UTF-8' --lc-ctype='en_US.UTF-8'
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: deps-frontend
|
- name: deps-frontend
|
||||||
image: node:18
|
image: node:18
|
||||||
@ -568,14 +748,12 @@ steps:
|
|||||||
- curl -sLO https://go.dev/dl/go1.20.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.20.linux-amd64.tar.gz
|
- curl -sLO https://go.dev/dl/go1.20.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.20.linux-amd64.tar.gz
|
||||||
- groupadd --gid 1001 gitea && useradd -m --gid 1001 --uid 1001 gitea
|
- groupadd --gid 1001 gitea && useradd -m --gid 1001 --uid 1001 gitea
|
||||||
- apt-get -qq update && apt-get -qqy install build-essential
|
- apt-get -qq update && apt-get -qqy install build-essential
|
||||||
- export TEST_PGSQL_SCHEMA=''
|
|
||||||
- ./build/test-env-prepare.sh
|
- ./build/test-env-prepare.sh
|
||||||
- su gitea bash -c "export PATH=$PATH:/usr/local/go/bin && timeout -s ABRT 40m make test-e2e-pgsql"
|
- su gitea bash -c "export PATH=$PATH:/usr/local/go/bin && timeout -s ABRT 40m make test-e2e-sqlite"
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.io
|
GOPROXY: https://goproxy.io
|
||||||
GOSUMDB: sum.golang.org
|
GOSUMDB: sum.golang.org
|
||||||
USE_REPO_TEST_DIR: 1
|
USE_REPO_TEST_DIR: 1
|
||||||
TEST_PGSQL_DBNAME: 'testgitea-e2e'
|
|
||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
depends_on: [build-frontend, deps-backend]
|
depends_on: [build-frontend, deps-backend]
|
||||||
volumes:
|
volumes:
|
||||||
@ -709,8 +887,11 @@ trigger:
|
|||||||
- docs/**
|
- docs/**
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
@ -842,8 +1023,11 @@ trigger:
|
|||||||
- tag
|
- tag
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-arm64
|
- testing-mysql
|
||||||
- testing-amd64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
@ -994,8 +1178,11 @@ platform:
|
|||||||
arch: amd64
|
arch: amd64
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
ref:
|
ref:
|
||||||
@ -1064,8 +1251,11 @@ platform:
|
|||||||
arch: amd64
|
arch: amd64
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
ref:
|
ref:
|
||||||
@ -1129,8 +1319,11 @@ platform:
|
|||||||
arch: amd64
|
arch: amd64
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
ref:
|
ref:
|
||||||
@ -1192,8 +1385,11 @@ platform:
|
|||||||
arch: amd64
|
arch: amd64
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
ref:
|
ref:
|
||||||
@ -1292,8 +1488,11 @@ platform:
|
|||||||
arch: arm64
|
arch: arm64
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
ref:
|
ref:
|
||||||
@ -1362,8 +1561,11 @@ platform:
|
|||||||
arch: arm64
|
arch: arm64
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
ref:
|
ref:
|
||||||
@ -1427,8 +1629,11 @@ platform:
|
|||||||
arch: arm64
|
arch: arm64
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
ref:
|
ref:
|
||||||
@ -1493,8 +1698,11 @@ platform:
|
|||||||
arch: arm64
|
arch: arm64
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
ref:
|
ref:
|
||||||
@ -1607,7 +1815,6 @@ platform:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: manifest-rootless
|
- name: manifest-rootless
|
||||||
pull: always
|
|
||||||
image: plugins/manifest
|
image: plugins/manifest
|
||||||
pull: always
|
pull: always
|
||||||
settings:
|
settings:
|
||||||
@ -1671,8 +1878,11 @@ trigger:
|
|||||||
- failure
|
- failure
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- testing-amd64
|
- testing-mysql
|
||||||
- testing-arm64
|
- testing-mysql8
|
||||||
|
- testing-mssql
|
||||||
|
- testing-pgsql
|
||||||
|
- testing-sqlite
|
||||||
- release-version
|
- release-version
|
||||||
- release-latest
|
- release-latest
|
||||||
- docker-linux-amd64-release
|
- docker-linux-amd64-release
|
||||||
|
6
Makefile
6
Makefile
@ -190,6 +190,7 @@ help:
|
|||||||
@echo " - deps install dependencies"
|
@echo " - deps install dependencies"
|
||||||
@echo " - deps-frontend install frontend dependencies"
|
@echo " - deps-frontend install frontend dependencies"
|
||||||
@echo " - deps-backend install backend dependencies"
|
@echo " - deps-backend install backend dependencies"
|
||||||
|
@echo " - deps-tools install tool dependencies"
|
||||||
@echo " - lint lint everything"
|
@echo " - lint lint everything"
|
||||||
@echo " - lint-frontend lint frontend files"
|
@echo " - lint-frontend lint frontend files"
|
||||||
@echo " - lint-backend lint backend files"
|
@echo " - lint-backend lint backend files"
|
||||||
@ -821,7 +822,7 @@ docs:
|
|||||||
cd docs; make trans-copy clean build-offline;
|
cd docs; make trans-copy clean build-offline;
|
||||||
|
|
||||||
.PHONY: deps
|
.PHONY: deps
|
||||||
deps: deps-frontend deps-backend
|
deps: deps-frontend deps-backend deps-tools
|
||||||
|
|
||||||
.PHONY: deps-frontend
|
.PHONY: deps-frontend
|
||||||
deps-frontend: node_modules
|
deps-frontend: node_modules
|
||||||
@ -829,6 +830,9 @@ deps-frontend: node_modules
|
|||||||
.PHONY: deps-backend
|
.PHONY: deps-backend
|
||||||
deps-backend:
|
deps-backend:
|
||||||
$(GO) mod download
|
$(GO) mod download
|
||||||
|
|
||||||
|
.PHONY: deps-tools
|
||||||
|
deps-tools:
|
||||||
$(GO) install $(AIR_PACKAGE)
|
$(GO) install $(AIR_PACKAGE)
|
||||||
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
|
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
|
||||||
$(GO) install $(ERRCHECK_PACKAGE)
|
$(GO) install $(ERRCHECK_PACKAGE)
|
||||||
|
11
cmd/admin.go
11
cmd/admin.go
@ -7,6 +7,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
@ -469,11 +470,19 @@ func runAddOauth(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config := parseOAuth2Config(c)
|
||||||
|
if config.Provider == "openidConnect" {
|
||||||
|
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
|
||||||
|
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||||
|
return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return auth_model.CreateSource(&auth_model.Source{
|
return auth_model.CreateSource(&auth_model.Source{
|
||||||
Type: auth_model.OAuth2,
|
Type: auth_model.OAuth2,
|
||||||
Name: c.String("name"),
|
Name: c.String("name"),
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
Cfg: parseOAuth2Config(c),
|
Cfg: config,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,6 +576,22 @@ ROUTER = console
|
|||||||
;; The routing level will default to that of the system but individual router level can be set in
|
;; The routing level will default to that of the system but individual router level can be set in
|
||||||
;; [log.<mode>.router] LEVEL
|
;; [log.<mode>.router] LEVEL
|
||||||
;;
|
;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;
|
||||||
|
;; Print request id which parsed from request headers in access log, when access log is enabled.
|
||||||
|
;; * E.g:
|
||||||
|
;; * In request Header: X-Request-ID: test-id-123
|
||||||
|
;; * Configuration in app.ini: REQUEST_ID_HEADERS = X-Request-ID
|
||||||
|
;; * Print in log: 127.0.0.1:58384 - - [14/Feb/2023:16:33:51 +0800] "test-id-123"
|
||||||
|
;;
|
||||||
|
;; If you configure more than one in the .ini file, it will match in the order of configuration,
|
||||||
|
;; and the first match will be finally printed in the log.
|
||||||
|
;; * E.g:
|
||||||
|
;; * In reuqest Header: X-Trace-ID: trace-id-1q2w3e4r
|
||||||
|
;; * Configuration in app.ini: REQUEST_ID_HEADERS = X-Request-ID, X-Trace-ID, X-Req-ID
|
||||||
|
;; * Print in log: 127.0.0.1:58384 - - [14/Feb/2023:16:33:51 +0800] "trace-id-1q2w3e4r"
|
||||||
|
;;
|
||||||
|
;; REQUEST_ID_HEADERS =
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;
|
;;
|
||||||
|
@ -881,7 +881,13 @@ Default templates for project boards:
|
|||||||
- `Identity`: the SignedUserName or `"-"` if not logged in.
|
- `Identity`: the SignedUserName or `"-"` if not logged in.
|
||||||
- `Start`: the start time of the request.
|
- `Start`: the start time of the request.
|
||||||
- `ResponseWriter`: the responseWriter from the request.
|
- `ResponseWriter`: the responseWriter from the request.
|
||||||
|
- `RequestID`: the value matching REQUEST_ID_HEADERS(default: `-`, if not matched).
|
||||||
- You must be very careful to ensure that this template does not throw errors or panics as this template runs outside of the panic/recovery script.
|
- You must be very careful to ensure that this template does not throw errors or panics as this template runs outside of the panic/recovery script.
|
||||||
|
- `REQUEST_ID_HEADERS`: **\<empty\>**: You can configure multiple values that are splited by comma here. It will match in the order of configuration, and the first match will be finally printed in the access log.
|
||||||
|
- e.g.
|
||||||
|
- In the Request Header: X-Request-ID: **test-id-123**
|
||||||
|
- Configuration in app.ini: REQUEST_ID_HEADERS = X-Request-ID
|
||||||
|
- Print in log: 127.0.0.1:58384 - - [14/Feb/2023:16:33:51 +0800] "**test-id-123**" ...
|
||||||
|
|
||||||
### Log subsections (`log.name`, `log.name.*`)
|
### Log subsections (`log.name`, `log.name.*`)
|
||||||
|
|
||||||
|
@ -262,7 +262,22 @@ test01.xls: application/vnd.ms-excel; charset=binary
|
|||||||
|
|
||||||
- `ROOT_PATH`: 日志文件根目录。
|
- `ROOT_PATH`: 日志文件根目录。
|
||||||
- `MODE`: 日志记录模式,默认是为 `console`。如果要写到多个通道,用逗号分隔
|
- `MODE`: 日志记录模式,默认是为 `console`。如果要写到多个通道,用逗号分隔
|
||||||
- `LEVEL`: 日志级别,默认为`Trace`。
|
- `LEVEL`: 日志级别,默认为 `Trace`。
|
||||||
|
- `DISABLE_ROUTER_LOG`: 关闭日志中的路由日志。
|
||||||
|
- `ENABLE_ACCESS_LOG`: 是否开启 Access Log, 默认为 false。
|
||||||
|
- `ACCESS_LOG_TEMPLATE`: `access.log` 输出内容的模板,默认模板:**`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`**
|
||||||
|
模板支持以下参数:
|
||||||
|
- `Ctx`: 请求上下文。
|
||||||
|
- `Identity`: 登录用户名,默认: “`-`”。
|
||||||
|
- `Start`: 请求开始时间。
|
||||||
|
- `ResponseWriter`:
|
||||||
|
- `RequestID`: 从请求头中解析得到的与 `REQUEST_ID_HEADERS` 匹配的值,默认: “`-`”。
|
||||||
|
- 一定要谨慎配置该模板,否则可能会引起panic.
|
||||||
|
- `REQUEST_ID_HEADERS`: 从 Request Header 中匹配指定 Key,并将匹配到的值输出到 `access.log` 中(需要在 `ACCESS_LOG_TEMPLATE` 中指定输出位置)。如果在该参数中配置多个 Key, 请用逗号分割,程序将按照配置的顺序进行匹配。
|
||||||
|
- 示例:
|
||||||
|
- 请求头: X-Request-ID: **test-id-123**
|
||||||
|
- 配置文件: REQUEST_ID_HEADERS = X-Request-ID
|
||||||
|
- 日志输出: 127.0.0.1:58384 - - [14/Feb/2023:16:33:51 +0800] "**test-id-123**" ...
|
||||||
|
|
||||||
## Cron (`cron`)
|
## Cron (`cron`)
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ Gitea supports the following scopes for tokens:
|
|||||||
| **write:public_key** | Grant read/write access to public keys |
|
| **write:public_key** | Grant read/write access to public keys |
|
||||||
| **read:public_key** | Grant read-only access to public keys |
|
| **read:public_key** | Grant read-only access to public keys |
|
||||||
| **admin:org_hook** | Grants full access to organizational-level hooks |
|
| **admin:org_hook** | Grants full access to organizational-level hooks |
|
||||||
|
| **admin:user_hook** | Grants full access to user-level hooks |
|
||||||
| **notification** | Grants full access to notifications |
|
| **notification** | Grants full access to notifications |
|
||||||
| **user** | Grants full access to user profile info |
|
| **user** | Grants full access to user profile info |
|
||||||
| **read:user** | Grants read access to user's profile |
|
| **read:user** | Grants read access to user's profile |
|
||||||
|
@ -32,6 +32,8 @@ const (
|
|||||||
|
|
||||||
AccessTokenScopeAdminOrgHook AccessTokenScope = "admin:org_hook"
|
AccessTokenScopeAdminOrgHook AccessTokenScope = "admin:org_hook"
|
||||||
|
|
||||||
|
AccessTokenScopeAdminUserHook AccessTokenScope = "admin:user_hook"
|
||||||
|
|
||||||
AccessTokenScopeNotification AccessTokenScope = "notification"
|
AccessTokenScopeNotification AccessTokenScope = "notification"
|
||||||
|
|
||||||
AccessTokenScopeUser AccessTokenScope = "user"
|
AccessTokenScopeUser AccessTokenScope = "user"
|
||||||
@ -64,7 +66,7 @@ type AccessTokenScopeBitmap uint64
|
|||||||
const (
|
const (
|
||||||
// AccessTokenScopeAllBits is the bitmap of all access token scopes, except `sudo`.
|
// AccessTokenScopeAllBits is the bitmap of all access token scopes, except `sudo`.
|
||||||
AccessTokenScopeAllBits AccessTokenScopeBitmap = AccessTokenScopeRepoBits |
|
AccessTokenScopeAllBits AccessTokenScopeBitmap = AccessTokenScopeRepoBits |
|
||||||
AccessTokenScopeAdminOrgBits | AccessTokenScopeAdminPublicKeyBits | AccessTokenScopeAdminOrgHookBits |
|
AccessTokenScopeAdminOrgBits | AccessTokenScopeAdminPublicKeyBits | AccessTokenScopeAdminOrgHookBits | AccessTokenScopeAdminUserHookBits |
|
||||||
AccessTokenScopeNotificationBits | AccessTokenScopeUserBits | AccessTokenScopeDeleteRepoBits |
|
AccessTokenScopeNotificationBits | AccessTokenScopeUserBits | AccessTokenScopeDeleteRepoBits |
|
||||||
AccessTokenScopePackageBits | AccessTokenScopeAdminGPGKeyBits | AccessTokenScopeAdminApplicationBits
|
AccessTokenScopePackageBits | AccessTokenScopeAdminGPGKeyBits | AccessTokenScopeAdminApplicationBits
|
||||||
|
|
||||||
@ -86,6 +88,8 @@ const (
|
|||||||
|
|
||||||
AccessTokenScopeAdminOrgHookBits AccessTokenScopeBitmap = 1 << iota
|
AccessTokenScopeAdminOrgHookBits AccessTokenScopeBitmap = 1 << iota
|
||||||
|
|
||||||
|
AccessTokenScopeAdminUserHookBits AccessTokenScopeBitmap = 1 << iota
|
||||||
|
|
||||||
AccessTokenScopeNotificationBits AccessTokenScopeBitmap = 1 << iota
|
AccessTokenScopeNotificationBits AccessTokenScopeBitmap = 1 << iota
|
||||||
|
|
||||||
AccessTokenScopeUserBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadUserBits | AccessTokenScopeUserEmailBits | AccessTokenScopeUserFollowBits
|
AccessTokenScopeUserBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadUserBits | AccessTokenScopeUserEmailBits | AccessTokenScopeUserFollowBits
|
||||||
@ -123,6 +127,7 @@ var allAccessTokenScopes = []AccessTokenScope{
|
|||||||
AccessTokenScopeAdminPublicKey, AccessTokenScopeWritePublicKey, AccessTokenScopeReadPublicKey,
|
AccessTokenScopeAdminPublicKey, AccessTokenScopeWritePublicKey, AccessTokenScopeReadPublicKey,
|
||||||
AccessTokenScopeAdminRepoHook, AccessTokenScopeWriteRepoHook, AccessTokenScopeReadRepoHook,
|
AccessTokenScopeAdminRepoHook, AccessTokenScopeWriteRepoHook, AccessTokenScopeReadRepoHook,
|
||||||
AccessTokenScopeAdminOrgHook,
|
AccessTokenScopeAdminOrgHook,
|
||||||
|
AccessTokenScopeAdminUserHook,
|
||||||
AccessTokenScopeNotification,
|
AccessTokenScopeNotification,
|
||||||
AccessTokenScopeUser, AccessTokenScopeReadUser, AccessTokenScopeUserEmail, AccessTokenScopeUserFollow,
|
AccessTokenScopeUser, AccessTokenScopeReadUser, AccessTokenScopeUserEmail, AccessTokenScopeUserFollow,
|
||||||
AccessTokenScopeDeleteRepo,
|
AccessTokenScopeDeleteRepo,
|
||||||
@ -147,6 +152,7 @@ var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{
|
|||||||
AccessTokenScopeWriteRepoHook: AccessTokenScopeWriteRepoHookBits,
|
AccessTokenScopeWriteRepoHook: AccessTokenScopeWriteRepoHookBits,
|
||||||
AccessTokenScopeReadRepoHook: AccessTokenScopeReadRepoHookBits,
|
AccessTokenScopeReadRepoHook: AccessTokenScopeReadRepoHookBits,
|
||||||
AccessTokenScopeAdminOrgHook: AccessTokenScopeAdminOrgHookBits,
|
AccessTokenScopeAdminOrgHook: AccessTokenScopeAdminOrgHookBits,
|
||||||
|
AccessTokenScopeAdminUserHook: AccessTokenScopeAdminUserHookBits,
|
||||||
AccessTokenScopeNotification: AccessTokenScopeNotificationBits,
|
AccessTokenScopeNotification: AccessTokenScopeNotificationBits,
|
||||||
AccessTokenScopeUser: AccessTokenScopeUserBits,
|
AccessTokenScopeUser: AccessTokenScopeUserBits,
|
||||||
AccessTokenScopeReadUser: AccessTokenScopeReadUserBits,
|
AccessTokenScopeReadUser: AccessTokenScopeReadUserBits,
|
||||||
@ -263,7 +269,7 @@ func (bitmap AccessTokenScopeBitmap) ToScope() AccessTokenScope {
|
|||||||
scope := AccessTokenScope(strings.Join(scopes, ","))
|
scope := AccessTokenScope(strings.Join(scopes, ","))
|
||||||
scope = AccessTokenScope(strings.ReplaceAll(
|
scope = AccessTokenScope(strings.ReplaceAll(
|
||||||
string(scope),
|
string(scope),
|
||||||
"repo,admin:org,admin:public_key,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application",
|
"repo,admin:org,admin:public_key,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application",
|
||||||
"all",
|
"all",
|
||||||
))
|
))
|
||||||
return scope
|
return scope
|
||||||
|
@ -40,8 +40,8 @@ func TestAccessTokenScope_Normalize(t *testing.T) {
|
|||||||
{"admin:gpg_key,write:gpg_key,user", "user,admin:gpg_key", nil},
|
{"admin:gpg_key,write:gpg_key,user", "user,admin:gpg_key", nil},
|
||||||
{"admin:application,write:application,user", "user,admin:application", nil},
|
{"admin:application,write:application,user", "user,admin:application", nil},
|
||||||
{"all", "all", nil},
|
{"all", "all", nil},
|
||||||
{"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", "all", nil},
|
{"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", "all", nil},
|
||||||
{"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application,sudo", "all,sudo", nil},
|
{"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application,sudo", "all,sudo", nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
-
|
-
|
||||||
id: 3
|
id: 3
|
||||||
org_id: 3
|
owner_id: 3
|
||||||
repo_id: 3
|
repo_id: 3
|
||||||
url: www.example.com/url3
|
url: www.example.com/url3
|
||||||
content_type: 1 # json
|
content_type: 1 # json
|
||||||
|
@ -467,6 +467,8 @@ var migrations = []Migration{
|
|||||||
|
|
||||||
// v244 -> v245
|
// v244 -> v245
|
||||||
NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun),
|
NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun),
|
||||||
|
// v245 -> v246
|
||||||
|
NewMigration("Rename Webhook org_id to owner_id", v1_20.RenameWebhookOrgToOwner),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
74
models/migrations/v1_20/v245.go
Normal file
74
models/migrations/v1_20/v245.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_20 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/migrations/base"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RenameWebhookOrgToOwner(x *xorm.Engine) error {
|
||||||
|
type Webhook struct {
|
||||||
|
OrgID int64 `xorm:"INDEX"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// This migration maybe rerun so that we should check if it has been run
|
||||||
|
ownerExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webhook", "owner_id")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ownerExist {
|
||||||
|
orgExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webhook", "org_id")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !orgExist {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sess.Sync2(new(Webhook)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ownerExist {
|
||||||
|
if err := base.DropTableColumns(sess, "webhook", "owner_id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case setting.Database.Type.IsMySQL():
|
||||||
|
inferredTable, err := x.TableInfo(new(Webhook))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sqlType := x.Dialect().SQLType(inferredTable.GetColumn("org_id"))
|
||||||
|
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `webhook` CHANGE org_id owner_id %s", sqlType)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case setting.Database.Type.IsMSSQL():
|
||||||
|
if _, err := sess.Exec("sp_rename 'webhook.org_id', 'owner_id', 'COLUMN'"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if _, err := sess.Exec("ALTER TABLE `webhook` RENAME COLUMN org_id TO owner_id"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
@ -239,6 +239,32 @@ func (org *Organization) CustomAvatarRelativePath() string {
|
|||||||
return org.Avatar
|
return org.Avatar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnitPermission returns unit permission
|
||||||
|
func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.User, unitType unit.Type) perm.AccessMode {
|
||||||
|
if doer != nil {
|
||||||
|
teams, err := GetUserOrgTeams(ctx, org.ID, doer.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetUserOrgTeams: %v", err)
|
||||||
|
return perm.AccessModeNone
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := teams.LoadUnits(ctx); err != nil {
|
||||||
|
log.Error("LoadUnits: %v", err)
|
||||||
|
return perm.AccessModeNone
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(teams) > 0 {
|
||||||
|
return teams.UnitMaxAccess(unitType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if org.Visibility.IsPublic() {
|
||||||
|
return perm.AccessModeRead
|
||||||
|
}
|
||||||
|
|
||||||
|
return perm.AccessModeNone
|
||||||
|
}
|
||||||
|
|
||||||
// CreateOrganization creates record of a new organization.
|
// CreateOrganization creates record of a new organization.
|
||||||
func CreateOrganization(org *Organization, owner *user_model.User) (err error) {
|
func CreateOrganization(org *Organization, owner *user_model.User) (err error) {
|
||||||
if !owner.CanCreateOrganization() {
|
if !owner.CanCreateOrganization() {
|
||||||
|
@ -393,6 +393,11 @@ func (u *User) IsOrganization() bool {
|
|||||||
return u.Type == UserTypeOrganization
|
return u.Type == UserTypeOrganization
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsIndividual returns true if user is actually a individual user.
|
||||||
|
func (u *User) IsIndividual() bool {
|
||||||
|
return u.Type == UserTypeIndividual
|
||||||
|
}
|
||||||
|
|
||||||
// DisplayName returns full name if it's not empty,
|
// DisplayName returns full name if it's not empty,
|
||||||
// returns username otherwise.
|
// returns username otherwise.
|
||||||
func (u *User) DisplayName() string {
|
func (u *User) DisplayName() string {
|
||||||
|
@ -122,7 +122,7 @@ func IsValidHookContentType(name string) bool {
|
|||||||
type Webhook struct {
|
type Webhook struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
RepoID int64 `xorm:"INDEX"` // An ID of 0 indicates either a default or system webhook
|
RepoID int64 `xorm:"INDEX"` // An ID of 0 indicates either a default or system webhook
|
||||||
OrgID int64 `xorm:"INDEX"`
|
OwnerID int64 `xorm:"INDEX"`
|
||||||
IsSystemWebhook bool
|
IsSystemWebhook bool
|
||||||
URL string `xorm:"url TEXT"`
|
URL string `xorm:"url TEXT"`
|
||||||
HTTPMethod string `xorm:"http_method"`
|
HTTPMethod string `xorm:"http_method"`
|
||||||
@ -412,11 +412,11 @@ func GetWebhookByRepoID(repoID, id int64) (*Webhook, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebhookByOrgID returns webhook of organization by given ID.
|
// GetWebhookByOwnerID returns webhook of a user or organization by given ID.
|
||||||
func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
|
func GetWebhookByOwnerID(ownerID, id int64) (*Webhook, error) {
|
||||||
return getWebhook(&Webhook{
|
return getWebhook(&Webhook{
|
||||||
ID: id,
|
ID: id,
|
||||||
OrgID: orgID,
|
OwnerID: ownerID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,7 +424,7 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
|
|||||||
type ListWebhookOptions struct {
|
type ListWebhookOptions struct {
|
||||||
db.ListOptions
|
db.ListOptions
|
||||||
RepoID int64
|
RepoID int64
|
||||||
OrgID int64
|
OwnerID int64
|
||||||
IsActive util.OptionalBool
|
IsActive util.OptionalBool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,8 +433,8 @@ func (opts *ListWebhookOptions) toCond() builder.Cond {
|
|||||||
if opts.RepoID != 0 {
|
if opts.RepoID != 0 {
|
||||||
cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID})
|
cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID})
|
||||||
}
|
}
|
||||||
if opts.OrgID != 0 {
|
if opts.OwnerID != 0 {
|
||||||
cond = cond.And(builder.Eq{"webhook.org_id": opts.OrgID})
|
cond = cond.And(builder.Eq{"webhook.owner_id": opts.OwnerID})
|
||||||
}
|
}
|
||||||
if !opts.IsActive.IsNone() {
|
if !opts.IsActive.IsNone() {
|
||||||
cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.IsTrue()})
|
cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.IsTrue()})
|
||||||
@ -503,10 +503,10 @@ func DeleteWebhookByRepoID(repoID, id int64) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteWebhookByOrgID deletes webhook of organization by given ID.
|
// DeleteWebhookByOwnerID deletes webhook of a user or organization by given ID.
|
||||||
func DeleteWebhookByOrgID(orgID, id int64) error {
|
func DeleteWebhookByOwnerID(ownerID, id int64) error {
|
||||||
return deleteWebhook(&Webhook{
|
return deleteWebhook(&Webhook{
|
||||||
ID: id,
|
ID: id,
|
||||||
OrgID: orgID,
|
OwnerID: ownerID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) {
|
func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) {
|
||||||
webhooks := make([]*Webhook, 0, 5)
|
webhooks := make([]*Webhook, 0, 5)
|
||||||
return webhooks, db.GetEngine(ctx).
|
return webhooks, db.GetEngine(ctx).
|
||||||
Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false).
|
Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, false).
|
||||||
Find(&webhooks)
|
Find(&webhooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) {
|
|||||||
func GetSystemOrDefaultWebhook(ctx context.Context, id int64) (*Webhook, error) {
|
func GetSystemOrDefaultWebhook(ctx context.Context, id int64) (*Webhook, error) {
|
||||||
webhook := &Webhook{ID: id}
|
webhook := &Webhook{ID: id}
|
||||||
has, err := db.GetEngine(ctx).
|
has, err := db.GetEngine(ctx).
|
||||||
Where("repo_id=? AND org_id=?", 0, 0).
|
Where("repo_id=? AND owner_id=?", 0, 0).
|
||||||
Get(webhook)
|
Get(webhook)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -38,11 +38,11 @@ func GetSystemWebhooks(ctx context.Context, isActive util.OptionalBool) ([]*Webh
|
|||||||
webhooks := make([]*Webhook, 0, 5)
|
webhooks := make([]*Webhook, 0, 5)
|
||||||
if isActive.IsNone() {
|
if isActive.IsNone() {
|
||||||
return webhooks, db.GetEngine(ctx).
|
return webhooks, db.GetEngine(ctx).
|
||||||
Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true).
|
Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, true).
|
||||||
Find(&webhooks)
|
Find(&webhooks)
|
||||||
}
|
}
|
||||||
return webhooks, db.GetEngine(ctx).
|
return webhooks, db.GetEngine(ctx).
|
||||||
Where("repo_id=? AND org_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()).
|
Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()).
|
||||||
Find(&webhooks)
|
Find(&webhooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ func GetSystemWebhooks(ctx context.Context, isActive util.OptionalBool) ([]*Webh
|
|||||||
func DeleteDefaultSystemWebhook(ctx context.Context, id int64) error {
|
func DeleteDefaultSystemWebhook(ctx context.Context, id int64) error {
|
||||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
count, err := db.GetEngine(ctx).
|
count, err := db.GetEngine(ctx).
|
||||||
Where("repo_id=? AND org_id=?", 0, 0).
|
Where("repo_id=? AND owner_id=?", 0, 0).
|
||||||
Delete(&Webhook{ID: id})
|
Delete(&Webhook{ID: id})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -109,13 +109,13 @@ func TestGetWebhookByRepoID(t *testing.T) {
|
|||||||
assert.True(t, IsErrWebhookNotExist(err))
|
assert.True(t, IsErrWebhookNotExist(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetWebhookByOrgID(t *testing.T) {
|
func TestGetWebhookByOwnerID(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
hook, err := GetWebhookByOrgID(3, 3)
|
hook, err := GetWebhookByOwnerID(3, 3)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, int64(3), hook.ID)
|
assert.Equal(t, int64(3), hook.ID)
|
||||||
|
|
||||||
_, err = GetWebhookByOrgID(unittest.NonexistentID, unittest.NonexistentID)
|
_, err = GetWebhookByOwnerID(unittest.NonexistentID, unittest.NonexistentID)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, IsErrWebhookNotExist(err))
|
assert.True(t, IsErrWebhookNotExist(err))
|
||||||
}
|
}
|
||||||
@ -140,9 +140,9 @@ func TestGetWebhooksByRepoID(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetActiveWebhooksByOrgID(t *testing.T) {
|
func TestGetActiveWebhooksByOwnerID(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OrgID: 3, IsActive: util.OptionalBoolTrue})
|
hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
if assert.Len(t, hooks, 1) {
|
if assert.Len(t, hooks, 1) {
|
||||||
assert.Equal(t, int64(3), hooks[0].ID)
|
assert.Equal(t, int64(3), hooks[0].ID)
|
||||||
@ -150,9 +150,9 @@ func TestGetActiveWebhooksByOrgID(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetWebhooksByOrgID(t *testing.T) {
|
func TestGetWebhooksByOwnerID(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OrgID: 3})
|
hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
if assert.Len(t, hooks, 1) {
|
if assert.Len(t, hooks, 1) {
|
||||||
assert.Equal(t, int64(3), hooks[0].ID)
|
assert.Equal(t, int64(3), hooks[0].ID)
|
||||||
@ -181,13 +181,13 @@ func TestDeleteWebhookByRepoID(t *testing.T) {
|
|||||||
assert.True(t, IsErrWebhookNotExist(err))
|
assert.True(t, IsErrWebhookNotExist(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteWebhookByOrgID(t *testing.T) {
|
func TestDeleteWebhookByOwnerID(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 3, OrgID: 3})
|
unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 3, OwnerID: 3})
|
||||||
assert.NoError(t, DeleteWebhookByOrgID(3, 3))
|
assert.NoError(t, DeleteWebhookByOwnerID(3, 3))
|
||||||
unittest.AssertNotExistsBean(t, &Webhook{ID: 3, OrgID: 3})
|
unittest.AssertNotExistsBean(t, &Webhook{ID: 3, OwnerID: 3})
|
||||||
|
|
||||||
err := DeleteWebhookByOrgID(unittest.NonexistentID, unittest.NonexistentID)
|
err := DeleteWebhookByOwnerID(unittest.NonexistentID, unittest.NonexistentID)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, IsErrWebhookNotExist(err))
|
assert.True(t, IsErrWebhookNotExist(err))
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ package context
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -20,13 +22,39 @@ type routerLoggerOptions struct {
|
|||||||
Start *time.Time
|
Start *time.Time
|
||||||
ResponseWriter http.ResponseWriter
|
ResponseWriter http.ResponseWriter
|
||||||
Ctx map[string]interface{}
|
Ctx map[string]interface{}
|
||||||
|
RequestID *string
|
||||||
}
|
}
|
||||||
|
|
||||||
var signedUserNameStringPointerKey interface{} = "signedUserNameStringPointerKey"
|
var signedUserNameStringPointerKey interface{} = "signedUserNameStringPointerKey"
|
||||||
|
|
||||||
|
const keyOfRequestIDInTemplate = ".RequestID"
|
||||||
|
|
||||||
|
// According to:
|
||||||
|
// TraceId: A valid trace identifier is a 16-byte array with at least one non-zero byte
|
||||||
|
// MD5 output is 16 or 32 bytes: md5-bytes is 16, md5-hex is 32
|
||||||
|
// SHA1: similar, SHA1-bytes is 20, SHA1-hex is 40.
|
||||||
|
// UUID is 128-bit, 32 hex chars, 36 ASCII chars with 4 dashes
|
||||||
|
// So, we accept a Request ID with a maximum character length of 40
|
||||||
|
const maxRequestIDByteLength = 40
|
||||||
|
|
||||||
|
func parseRequestIDFromRequestHeader(req *http.Request) string {
|
||||||
|
requestID := "-"
|
||||||
|
for _, key := range setting.Log.RequestIDHeaders {
|
||||||
|
if req.Header.Get(key) != "" {
|
||||||
|
requestID = req.Header.Get(key)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(requestID) > maxRequestIDByteLength {
|
||||||
|
requestID = fmt.Sprintf("%s...", requestID[:maxRequestIDByteLength])
|
||||||
|
}
|
||||||
|
return requestID
|
||||||
|
}
|
||||||
|
|
||||||
// AccessLogger returns a middleware to log access logger
|
// AccessLogger returns a middleware to log access logger
|
||||||
func AccessLogger() func(http.Handler) http.Handler {
|
func AccessLogger() func(http.Handler) http.Handler {
|
||||||
logger := log.GetLogger("access")
|
logger := log.GetLogger("access")
|
||||||
|
needRequestID := len(setting.Log.RequestIDHeaders) > 0 && strings.Contains(setting.Log.AccessLogTemplate, keyOfRequestIDInTemplate)
|
||||||
logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate)
|
logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate)
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
@ -34,6 +62,11 @@ func AccessLogger() func(http.Handler) http.Handler {
|
|||||||
identity := "-"
|
identity := "-"
|
||||||
r := req.WithContext(context.WithValue(req.Context(), signedUserNameStringPointerKey, &identity))
|
r := req.WithContext(context.WithValue(req.Context(), signedUserNameStringPointerKey, &identity))
|
||||||
|
|
||||||
|
var requestID string
|
||||||
|
if needRequestID {
|
||||||
|
requestID = parseRequestIDFromRequestHeader(req)
|
||||||
|
}
|
||||||
|
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
rw := w.(ResponseWriter)
|
rw := w.(ResponseWriter)
|
||||||
|
|
||||||
@ -47,6 +80,7 @@ func AccessLogger() func(http.Handler) http.Handler {
|
|||||||
"RemoteAddr": req.RemoteAddr,
|
"RemoteAddr": req.RemoteAddr,
|
||||||
"Req": req,
|
"Req": req,
|
||||||
},
|
},
|
||||||
|
RequestID: &requestID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Could not set up chi access logger: %v", err.Error())
|
log.Error("Could not set up chi access logger: %v", err.Error())
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
)
|
)
|
||||||
@ -31,29 +30,34 @@ type Organization struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool {
|
func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool {
|
||||||
if ctx.Doer == nil {
|
return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeWrite
|
||||||
return false
|
|
||||||
}
|
|
||||||
return org.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeWrite
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (org *Organization) UnitPermission(ctx *Context, doerID int64, unitType unit.Type) perm.AccessMode {
|
func (org *Organization) CanReadUnit(ctx *Context, unitType unit.Type) bool {
|
||||||
if doerID > 0 {
|
return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeRead
|
||||||
teams, err := organization.GetUserOrgTeams(ctx, org.Organization.ID, doerID)
|
}
|
||||||
|
|
||||||
|
func GetOrganizationByParams(ctx *Context) {
|
||||||
|
orgName := ctx.Params(":org")
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetUserOrgTeams: %v", err)
|
if organization.IsErrOrgNotExist(err) {
|
||||||
return perm.AccessModeNone
|
redirectUserID, err := user_model.LookupUserRedirect(orgName)
|
||||||
|
if err == nil {
|
||||||
|
RedirectToUser(ctx, orgName, redirectUserID)
|
||||||
|
} else if user_model.IsErrUserRedirectNotExist(err) {
|
||||||
|
ctx.NotFound("GetUserByName", err)
|
||||||
|
} else {
|
||||||
|
ctx.ServerError("LookupUserRedirect", err)
|
||||||
}
|
}
|
||||||
if len(teams) > 0 {
|
} else {
|
||||||
return teams.UnitMaxAccess(unitType)
|
ctx.ServerError("GetUserByName", err)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if org.Organization.Visibility == structs.VisibleTypePublic {
|
|
||||||
return perm.AccessModeRead
|
|
||||||
}
|
|
||||||
|
|
||||||
return perm.AccessModeNone
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleOrgAssignment handles organization assignment
|
// HandleOrgAssignment handles organization assignment
|
||||||
@ -77,25 +81,26 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
|||||||
requireTeamAdmin = args[3]
|
requireTeamAdmin = args[3]
|
||||||
}
|
}
|
||||||
|
|
||||||
orgName := ctx.Params(":org")
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName)
|
|
||||||
if err != nil {
|
if ctx.ContextUser == nil {
|
||||||
if organization.IsErrOrgNotExist(err) {
|
// if Organization is not defined, get it from params
|
||||||
redirectUserID, err := user_model.LookupUserRedirect(orgName)
|
if ctx.Org.Organization == nil {
|
||||||
if err == nil {
|
GetOrganizationByParams(ctx)
|
||||||
RedirectToUser(ctx, orgName, redirectUserID)
|
if ctx.Written() {
|
||||||
} else if user_model.IsErrUserRedirectNotExist(err) {
|
|
||||||
ctx.NotFound("GetUserByName", err)
|
|
||||||
} else {
|
|
||||||
ctx.ServerError("LookupUserRedirect", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.ServerError("GetUserByName", err)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else if ctx.ContextUser.IsOrganization() {
|
||||||
|
if ctx.Org == nil {
|
||||||
|
ctx.Org = &Organization{}
|
||||||
|
}
|
||||||
|
ctx.Org.Organization = (*organization.Organization)(ctx.ContextUser)
|
||||||
|
} else {
|
||||||
|
// ContextUser is an individual User
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
org := ctx.Org.Organization
|
org := ctx.Org.Organization
|
||||||
|
|
||||||
// Handle Visibility
|
// Handle Visibility
|
||||||
@ -156,6 +161,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
|||||||
}
|
}
|
||||||
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
|
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
|
||||||
ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
|
ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
|
||||||
|
ctx.Data["IsProjectEnabled"] = true
|
||||||
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
||||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||||
ctx.Data["IsPublicMember"] = func(uid int64) bool {
|
ctx.Data["IsPublicMember"] = func(uid int64) bool {
|
||||||
@ -231,6 +237,10 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects)
|
||||||
|
ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages)
|
||||||
|
ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrgAssignment returns a middleware to handle organization assignment
|
// OrgAssignment returns a middleware to handle organization assignment
|
||||||
|
@ -38,6 +38,7 @@ var Log struct {
|
|||||||
EnableAccessLog bool
|
EnableAccessLog bool
|
||||||
AccessLogTemplate string
|
AccessLogTemplate string
|
||||||
BufferLength int64
|
BufferLength int64
|
||||||
|
RequestIDHeaders []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLogDescriptions returns a race safe set of descriptions
|
// GetLogDescriptions returns a race safe set of descriptions
|
||||||
@ -153,6 +154,7 @@ func loadLogFrom(rootCfg ConfigProvider) {
|
|||||||
Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString(
|
Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString(
|
||||||
`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`,
|
`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`,
|
||||||
)
|
)
|
||||||
|
Log.RequestIDHeaders = sec.Key("REQUEST_ID_HEADERS").Strings(",")
|
||||||
// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later
|
// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later
|
||||||
_ = rootCfg.Section("log").Key("ACCESS").MustString("file")
|
_ = rootCfg.Section("log").Key("ACCESS").MustString("file")
|
||||||
|
|
||||||
|
@ -821,6 +821,8 @@ remove_account_link = Remove Linked Account
|
|||||||
remove_account_link_desc = Removing a linked account will revoke its access to your Gitea account. Continue?
|
remove_account_link_desc = Removing a linked account will revoke its access to your Gitea account. Continue?
|
||||||
remove_account_link_success = The linked account has been removed.
|
remove_account_link_success = The linked account has been removed.
|
||||||
|
|
||||||
|
hooks.desc = Add webhooks which will be triggered for <strong>all repositories</strong> owned by this user.
|
||||||
|
|
||||||
orgs_none = You are not a member of any organizations.
|
orgs_none = You are not a member of any organizations.
|
||||||
repos_none = You do not own any repositories
|
repos_none = You do not own any repositories
|
||||||
|
|
||||||
@ -2288,6 +2290,8 @@ release.edit_subheader = Releases organize project versions.
|
|||||||
release.tag_name = Tag name
|
release.tag_name = Tag name
|
||||||
release.target = Target
|
release.target = Target
|
||||||
release.tag_helper = Choose an existing tag or create a new tag.
|
release.tag_helper = Choose an existing tag or create a new tag.
|
||||||
|
release.tag_helper_new = New tag. This tag will be created from the target.
|
||||||
|
release.tag_helper_existing = Existing tag.
|
||||||
release.title = Title
|
release.title = Title
|
||||||
release.content = Content
|
release.content = Content
|
||||||
release.prerelease_desc = Mark as Pre-Release
|
release.prerelease_desc = Mark as Pre-Release
|
||||||
@ -2808,6 +2812,8 @@ auths.still_in_used = The authentication source is still in use. Convert or dele
|
|||||||
auths.deletion_success = The authentication source has been deleted.
|
auths.deletion_success = The authentication source has been deleted.
|
||||||
auths.login_source_exist = The authentication source '%s' already exists.
|
auths.login_source_exist = The authentication source '%s' already exists.
|
||||||
auths.login_source_of_type_exist = An authentication source of this type already exists.
|
auths.login_source_of_type_exist = An authentication source of this type already exists.
|
||||||
|
auths.unable_to_initialize_openid = Unable to initialize OpenID Connect Provider: %s
|
||||||
|
auths.invalid_openIdConnectAutoDiscoveryURL = Invalid Auto Discovery URL (this must be a valid URL starting with http:// or https://)
|
||||||
|
|
||||||
config.server_config = Server Configuration
|
config.server_config = Server Configuration
|
||||||
config.app_name = Site Title
|
config.app_name = Site Title
|
||||||
|
@ -334,7 +334,7 @@ non_local_account=Yerel olmayan kullanıcılar parolalarını Gitea web arayüz
|
|||||||
verify=Doğrula
|
verify=Doğrula
|
||||||
scratch_code=Çizgi kodu
|
scratch_code=Çizgi kodu
|
||||||
use_scratch_code=Bir çizgi kodu kullanınız
|
use_scratch_code=Bir çizgi kodu kullanınız
|
||||||
twofa_scratch_used=Çizgi kodunuzu kullandınız. İki aşamalı ayarlar sayfasına yönlendirildiniz, burada cihaz kaydınızı kaldırabilir veya yeni bir çizgi kodu oluşturabilirsiniz.
|
twofa_scratch_used=Geçici kodunuzu kullandınız. İki aşamalı ayarlar sayfasına yönlendirildiniz, burada aygıt kaydınızı kaldırabilir veya yeni bir geçici kod oluşturabilirsiniz.
|
||||||
twofa_passcode_incorrect=Şifreniz yanlış. Aygıtınızı yanlış yerleştirdiyseniz, oturum açmak için çizgi kodunuzu kullanın.
|
twofa_passcode_incorrect=Şifreniz yanlış. Aygıtınızı yanlış yerleştirdiyseniz, oturum açmak için çizgi kodunuzu kullanın.
|
||||||
twofa_scratch_token_incorrect=Çizgi kodunuz doğru değildir.
|
twofa_scratch_token_incorrect=Çizgi kodunuz doğru değildir.
|
||||||
login_userpass=Oturum Aç
|
login_userpass=Oturum Aç
|
||||||
@ -736,12 +736,12 @@ unbind=Bağlantıyı Kaldır
|
|||||||
unbind_success=Sosyal hesabın bağlantısı Gitea hesabınızdan kaldırılmıştır.
|
unbind_success=Sosyal hesabın bağlantısı Gitea hesabınızdan kaldırılmıştır.
|
||||||
|
|
||||||
manage_access_token=Erişim Jetonlarını Yönet
|
manage_access_token=Erişim Jetonlarını Yönet
|
||||||
generate_new_token=Yeni Jeton Üret
|
generate_new_token=Yeni Erişim Anahtarı Üret
|
||||||
tokens_desc=Bu jetonlar Gitea API'sini kullanarak hesabınıza erişim sağlar.
|
tokens_desc=Bu jetonlar Gitea API'sini kullanarak hesabınıza erişim sağlar.
|
||||||
new_token_desc=Jeton kullanan uygulamalar hesabınıza tam erişime sahiptir.
|
new_token_desc=Jeton kullanan uygulamalar hesabınıza tam erişime sahiptir.
|
||||||
token_name=Jeton İsmi
|
token_name=Jeton İsmi
|
||||||
generate_token=Jeton Üret
|
generate_token=Erişim Anahtarı Üret
|
||||||
generate_token_success=Yeni bir jeton oluşturuldu. Tekrar gösterilmeyeceği için şimdi kopyalayın.
|
generate_token_success=Yeni bir erişim anahtarı oluşturuldu. Tekrar gösterilmeyeceği için şimdi kopyalayın.
|
||||||
generate_token_name_duplicate=<strong>%s</strong> zaten bir uygulama adı olarak kullanılmış. Lütfen yeni bir tane kullanın.
|
generate_token_name_duplicate=<strong>%s</strong> zaten bir uygulama adı olarak kullanılmış. Lütfen yeni bir tane kullanın.
|
||||||
delete_token=Sil
|
delete_token=Sil
|
||||||
access_token_deletion=Erişim Jetonunu Sil
|
access_token_deletion=Erişim Jetonunu Sil
|
||||||
@ -784,12 +784,12 @@ twofa_desc=İki faktörlü kimlik doğrulama, hesabınızın güvenliğini artı
|
|||||||
twofa_is_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde <strong>kaydedilmiş</strong>.
|
twofa_is_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde <strong>kaydedilmiş</strong>.
|
||||||
twofa_not_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmemiş.
|
twofa_not_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmemiş.
|
||||||
twofa_disable=İki Aşamalı Doğrulamayı Devre Dışı Bırak
|
twofa_disable=İki Aşamalı Doğrulamayı Devre Dışı Bırak
|
||||||
twofa_scratch_token_regenerate=Kazıma Belirtecini Yenile
|
twofa_scratch_token_regenerate=Geçici Kodu Yeniden Üret
|
||||||
twofa_scratch_token_regenerated=Kazıma belirteciniz şimdi %s. Güvenli bir yerde saklayın.
|
twofa_scratch_token_regenerated=Geçici kodunuz artık %s. Güvenilir bir yerde saklayın.
|
||||||
twofa_enroll=İki Faktörlü Kimlik Doğrulamaya Kaydolun
|
twofa_enroll=İki Faktörlü Kimlik Doğrulamaya Kaydolun
|
||||||
twofa_disable_note=Gerekirse iki faktörlü kimlik doğrulamayı devre dışı bırakabilirsiniz.
|
twofa_disable_note=Gerekirse iki faktörlü kimlik doğrulamayı devre dışı bırakabilirsiniz.
|
||||||
twofa_disable_desc=İki faktörlü kimlik doğrulamayı devre dışı bırakmak hesabınızı daha az güvenli hale getirir. Devam edilsin mi?
|
twofa_disable_desc=İki faktörlü kimlik doğrulamayı devre dışı bırakmak hesabınızı daha az güvenli hale getirir. Devam edilsin mi?
|
||||||
regenerate_scratch_token_desc=Karalama belirtecinizi yanlış yerleştirdiyseniz veya oturum açmak için kullandıysanız, buradan sıfırlayabilirsiniz.
|
regenerate_scratch_token_desc=Geçici kodunuzu kaybettiyseniz veya oturum açmak için kullandıysanız, buradan sıfırlayabilirsiniz.
|
||||||
twofa_disabled=İki faktörlü kimlik doğrulama devre dışı bırakıldı.
|
twofa_disabled=İki faktörlü kimlik doğrulama devre dışı bırakıldı.
|
||||||
scan_this_image=Kim doğrulama uygulamanızla bu görüntüyü tarayın:
|
scan_this_image=Kim doğrulama uygulamanızla bu görüntüyü tarayın:
|
||||||
or_enter_secret=Veya gizli şeyi girin: %s
|
or_enter_secret=Veya gizli şeyi girin: %s
|
||||||
|
@ -54,7 +54,7 @@ mirror=鏡像
|
|||||||
new_repo=新增儲存庫
|
new_repo=新增儲存庫
|
||||||
new_migrate=遷移外部儲存庫
|
new_migrate=遷移外部儲存庫
|
||||||
new_mirror=新鏡像
|
new_mirror=新鏡像
|
||||||
new_fork=新增儲存庫 fork
|
new_fork=新增儲存庫 Fork
|
||||||
new_org=新增組織
|
new_org=新增組織
|
||||||
new_project=新增專案
|
new_project=新增專案
|
||||||
new_project_column=新增欄位
|
new_project_column=新增欄位
|
||||||
@ -476,6 +476,8 @@ url_error=`'%s' 是無效的 URL。`
|
|||||||
include_error=` 必須包含子字串「%s」。
|
include_error=` 必須包含子字串「%s」。
|
||||||
glob_pattern_error=` glob 比對模式無效:%s.`
|
glob_pattern_error=` glob 比對模式無效:%s.`
|
||||||
regex_pattern_error=` 正規表示式模式無效:%s.`
|
regex_pattern_error=` 正規表示式模式無效:%s.`
|
||||||
|
username_error=`只能包含英文字母數字 ('0-9'、'a-z'、'A-Z')、破折號 ('-')、底線 ('_')、句點 ('.'),不能以非英文字母數字開頭或結尾,也不允許連續的非英文字母數字。`
|
||||||
|
invalid_group_team_map_error=` 對應無效: %s`
|
||||||
unknown_error=未知錯誤:
|
unknown_error=未知錯誤:
|
||||||
captcha_incorrect=驗證碼不正確。
|
captcha_incorrect=驗證碼不正確。
|
||||||
password_not_match=密碼錯誤。
|
password_not_match=密碼錯誤。
|
||||||
@ -521,11 +523,11 @@ must_use_public_key=您提供的金鑰是私有金鑰,請勿上傳您的私有
|
|||||||
unable_verify_ssh_key=無法驗證 SSH 密鑰
|
unable_verify_ssh_key=無法驗證 SSH 密鑰
|
||||||
auth_failed=授權認證失敗:%v
|
auth_failed=授權認證失敗:%v
|
||||||
|
|
||||||
still_own_repo=此帳戶仍然擁有一個或多個儲存庫,您必須先刪除或轉移它們。
|
still_own_repo=您的帳戶擁有一個以上的儲存庫,請先刪除或轉移它們。
|
||||||
still_has_org=此帳戶仍是一個或多個組織的成員,您必須先離開它們。
|
still_has_org=您的帳戶是一個或多個組織的成員,請先離開它們。
|
||||||
still_own_packages=您的帳戶擁有一個或多個套件,請先刪除他們。
|
still_own_packages=您的帳戶擁有一個以上的套件,請先刪除它們。
|
||||||
org_still_own_repo=該組織仍然是某些儲存庫的擁有者,您必須先轉移或刪除它們才能執行刪除組織!
|
org_still_own_repo=此組織仍然擁有一個以上的儲存庫,請先刪除或轉移它們。
|
||||||
org_still_own_packages=此組織擁有一個或多個套件,請先刪除他們。
|
org_still_own_packages=此組織仍然擁有一個以上的套件,請先刪除它們。
|
||||||
|
|
||||||
target_branch_not_exist=目標分支不存在
|
target_branch_not_exist=目標分支不存在
|
||||||
|
|
||||||
@ -1132,6 +1134,7 @@ editor.commit_directly_to_this_branch=直接提交到 <strong class="branch-name
|
|||||||
editor.create_new_branch=為此提交建立<strong>新分支</strong>並提出合併請求。
|
editor.create_new_branch=為此提交建立<strong>新分支</strong>並提出合併請求。
|
||||||
editor.create_new_branch_np=為本次提交建立<strong>新分支</strong>。
|
editor.create_new_branch_np=為本次提交建立<strong>新分支</strong>。
|
||||||
editor.propose_file_change=提出檔案變更
|
editor.propose_file_change=提出檔案變更
|
||||||
|
editor.new_branch_name=命名此提交的新分支
|
||||||
editor.new_branch_name_desc=新的分支名稱...
|
editor.new_branch_name_desc=新的分支名稱...
|
||||||
editor.cancel=取消
|
editor.cancel=取消
|
||||||
editor.filename_cannot_be_empty=檔案名稱不能為空。
|
editor.filename_cannot_be_empty=檔案名稱不能為空。
|
||||||
@ -1217,8 +1220,8 @@ projects.template.desc=範本
|
|||||||
projects.template.desc_helper=選擇專案範本以開始
|
projects.template.desc_helper=選擇專案範本以開始
|
||||||
projects.type.uncategorized=未分類
|
projects.type.uncategorized=未分類
|
||||||
projects.column.edit=編輯欄位
|
projects.column.edit=編輯欄位
|
||||||
projects.column.edit_title=組織名稱
|
projects.column.edit_title=名稱
|
||||||
projects.column.new_title=組織名稱
|
projects.column.new_title=名稱
|
||||||
projects.column.new_submit=建立欄位
|
projects.column.new_submit=建立欄位
|
||||||
projects.column.new=新增欄位
|
projects.column.new=新增欄位
|
||||||
projects.column.set_default=設為預設
|
projects.column.set_default=設為預設
|
||||||
@ -1238,7 +1241,7 @@ issues.filter_assignees=篩選負責人
|
|||||||
issues.filter_milestones=篩選里程碑
|
issues.filter_milestones=篩選里程碑
|
||||||
issues.filter_projects=篩選專案
|
issues.filter_projects=篩選專案
|
||||||
issues.filter_labels=篩選標籤
|
issues.filter_labels=篩選標籤
|
||||||
issues.filter_reviewers=篩選審查者
|
issues.filter_reviewers=篩選審核者
|
||||||
issues.new=新增問題
|
issues.new=新增問題
|
||||||
issues.new.title_empty=標題不可為空
|
issues.new.title_empty=標題不可為空
|
||||||
issues.new.labels=標籤
|
issues.new.labels=標籤
|
||||||
@ -1655,6 +1658,7 @@ pulls.merge_instruction_hint=`您也可以查看<a class="show-instruction">命
|
|||||||
pulls.merge_instruction_step1_desc=在您的儲存庫中切換到新分支並測試變更。
|
pulls.merge_instruction_step1_desc=在您的儲存庫中切換到新分支並測試變更。
|
||||||
pulls.merge_instruction_step2_desc=合併變更並更新到 Gitea。
|
pulls.merge_instruction_step2_desc=合併變更並更新到 Gitea。
|
||||||
pulls.clear_merge_message=清除合併訊息
|
pulls.clear_merge_message=清除合併訊息
|
||||||
|
pulls.clear_merge_message_hint=清除合併訊息將僅移除提交訊息內容,留下產生的 git 結尾,如「Co-Authored-By …」。
|
||||||
|
|
||||||
pulls.auto_merge_button_when_succeed=(當通過檢查後)
|
pulls.auto_merge_button_when_succeed=(當通過檢查後)
|
||||||
pulls.auto_merge_when_succeed=通過所有檢查後自動合併
|
pulls.auto_merge_when_succeed=通過所有檢查後自動合併
|
||||||
@ -2619,7 +2623,7 @@ users.still_own_repo=這個使用者還擁有一個或更多的儲存庫。請
|
|||||||
users.still_has_org=此使用者是組織的成員。請先將他從組織中移除。
|
users.still_has_org=此使用者是組織的成員。請先將他從組織中移除。
|
||||||
users.purge=清除使用者
|
users.purge=清除使用者
|
||||||
users.purge_help=強制刪除使用者和他擁有的所有儲存庫、組織、套件,所有留言也會被刪除。
|
users.purge_help=強制刪除使用者和他擁有的所有儲存庫、組織、套件,所有留言也會被刪除。
|
||||||
users.still_own_packages=此使用者擁有一個或多個套件,請先刪除這些套件。
|
users.still_own_packages=此使用者仍然擁有一個以上的套件,請先刪除這些套件。
|
||||||
users.deletion_success=使用者帳戶已被刪除。
|
users.deletion_success=使用者帳戶已被刪除。
|
||||||
users.reset_2fa=重設兩步驟驗證
|
users.reset_2fa=重設兩步驟驗證
|
||||||
users.list_status_filter.menu_text=篩選
|
users.list_status_filter.menu_text=篩選
|
||||||
@ -2649,7 +2653,7 @@ emails.change_email_header=更新電子信箱屬性
|
|||||||
emails.change_email_text=您確定要更新這個電子信箱?
|
emails.change_email_text=您確定要更新這個電子信箱?
|
||||||
|
|
||||||
orgs.org_manage_panel=組織管理
|
orgs.org_manage_panel=組織管理
|
||||||
orgs.name=組織名稱
|
orgs.name=名稱
|
||||||
orgs.teams=團隊數
|
orgs.teams=團隊數
|
||||||
orgs.members=成員數
|
orgs.members=成員數
|
||||||
orgs.new_orga=新增組織
|
orgs.new_orga=新增組織
|
||||||
@ -2658,7 +2662,7 @@ repos.repo_manage_panel=儲存庫管理
|
|||||||
repos.unadopted=未接管的儲存庫
|
repos.unadopted=未接管的儲存庫
|
||||||
repos.unadopted.no_more=找不到其他未接管的儲存庫
|
repos.unadopted.no_more=找不到其他未接管的儲存庫
|
||||||
repos.owner=擁有者
|
repos.owner=擁有者
|
||||||
repos.name=儲存庫名稱
|
repos.name=名稱
|
||||||
repos.private=私有
|
repos.private=私有
|
||||||
repos.watches=關注數
|
repos.watches=關注數
|
||||||
repos.stars=星號數
|
repos.stars=星號數
|
||||||
@ -2690,8 +2694,8 @@ systemhooks.update_webhook=更新系統 Webhook
|
|||||||
|
|
||||||
auths.auth_manage_panel=認證來源管理
|
auths.auth_manage_panel=認證來源管理
|
||||||
auths.new=新增認證來源
|
auths.new=新增認證來源
|
||||||
auths.name=認證名稱
|
auths.name=名稱
|
||||||
auths.type=認證類型
|
auths.type=類型
|
||||||
auths.enabled=已啟用
|
auths.enabled=已啟用
|
||||||
auths.syncenabled=啟用使用者同步
|
auths.syncenabled=啟用使用者同步
|
||||||
auths.updated=最後更新時間
|
auths.updated=最後更新時間
|
||||||
@ -2762,6 +2766,7 @@ auths.oauth2_required_claim_value_helper=填寫此名稱以限制 Claim 中有
|
|||||||
auths.oauth2_group_claim_name=Claim 名稱提供群組名稱給此來源。(選用)
|
auths.oauth2_group_claim_name=Claim 名稱提供群組名稱給此來源。(選用)
|
||||||
auths.oauth2_admin_group=管理員使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱)
|
auths.oauth2_admin_group=管理員使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱)
|
||||||
auths.oauth2_restricted_group=受限制使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱)
|
auths.oauth2_restricted_group=受限制使用者的群組 Claim 值。(選用 - 需要上面的 Claim 名稱)
|
||||||
|
auths.oauth2_map_group_to_team=將已 Claim 的群組對應到組織團隊。(選用 - 需要上述 Claim 名稱)
|
||||||
auths.oauth2_map_group_to_team_removal=如果使用者不屬於相對應的群組,將使用者從已同步的團隊移除。
|
auths.oauth2_map_group_to_team_removal=如果使用者不屬於相對應的群組,將使用者從已同步的團隊移除。
|
||||||
auths.enable_auto_register=允許授權用戶自動註冊
|
auths.enable_auto_register=允許授權用戶自動註冊
|
||||||
auths.sspi_auto_create_users=自動建立使用者
|
auths.sspi_auto_create_users=自動建立使用者
|
||||||
@ -2843,7 +2848,7 @@ config.lfs_http_auth_expiry=LFS HTTP 驗證有效時間
|
|||||||
config.db_config=資料庫組態
|
config.db_config=資料庫組態
|
||||||
config.db_type=資料庫類型
|
config.db_type=資料庫類型
|
||||||
config.db_host=主機地址
|
config.db_host=主機地址
|
||||||
config.db_name=資料庫名稱
|
config.db_name=名稱
|
||||||
config.db_user=使用者名稱
|
config.db_user=使用者名稱
|
||||||
config.db_schema=結構描述
|
config.db_schema=結構描述
|
||||||
config.db_ssl_mode=SSL
|
config.db_ssl_mode=SSL
|
||||||
@ -2946,7 +2951,7 @@ config.get_setting_failed=讀取設定值 %s 失敗
|
|||||||
config.set_setting_failed=寫入設定值 %s 失敗
|
config.set_setting_failed=寫入設定值 %s 失敗
|
||||||
|
|
||||||
monitor.cron=Cron 任務
|
monitor.cron=Cron 任務
|
||||||
monitor.name=任務名稱
|
monitor.name=名稱
|
||||||
monitor.schedule=任務安排
|
monitor.schedule=任務安排
|
||||||
monitor.next=下次執行時間
|
monitor.next=下次執行時間
|
||||||
monitor.previous=上次執行時間
|
monitor.previous=上次執行時間
|
||||||
@ -3031,7 +3036,7 @@ notices.deselect_all=取消所有選取
|
|||||||
notices.inverse_selection=反向選取
|
notices.inverse_selection=反向選取
|
||||||
notices.delete_selected=刪除選取項
|
notices.delete_selected=刪除選取項
|
||||||
notices.delete_all=刪除所有提示
|
notices.delete_all=刪除所有提示
|
||||||
notices.type=提示類型
|
notices.type=類型
|
||||||
notices.type_1=儲存庫
|
notices.type_1=儲存庫
|
||||||
notices.type_2=任務
|
notices.type_2=任務
|
||||||
notices.desc=描述
|
notices.desc=描述
|
||||||
@ -3196,7 +3201,7 @@ generic.documentation=關於通用 registry 的詳情請參閱<a target="_blank"
|
|||||||
helm.registry=透過下列命令設定此註冊中心:
|
helm.registry=透過下列命令設定此註冊中心:
|
||||||
helm.install=執行下列命令安裝此套件:
|
helm.install=執行下列命令安裝此套件:
|
||||||
helm.documentation=關於 Helm registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/helm/">說明文件</a>。
|
helm.documentation=關於 Helm registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/helm/">說明文件</a>。
|
||||||
maven.registry=在您的 <code>pom.xml</code> 檔設定此註冊中心:
|
maven.registry=在您專案的 <code>pom.xml</code> 檔設定此註冊中心:
|
||||||
maven.install=若要使用此套件,請在您 <code>pom.xml</code> 檔的 <code>dependencies</code> 段落加入下列內容:
|
maven.install=若要使用此套件,請在您 <code>pom.xml</code> 檔的 <code>dependencies</code> 段落加入下列內容:
|
||||||
maven.install2=透過下列命令執行:
|
maven.install2=透過下列命令執行:
|
||||||
maven.download=透過下列命令下載相依性:
|
maven.download=透過下列命令下載相依性:
|
||||||
@ -3205,7 +3210,7 @@ nuget.registry=透過下列命令設定此註冊中心:
|
|||||||
nuget.install=執行下列命令以使用 NuGet 安裝此套件:
|
nuget.install=執行下列命令以使用 NuGet 安裝此套件:
|
||||||
nuget.documentation=關於 NuGet registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/nuget/">說明文件</a>。
|
nuget.documentation=關於 NuGet registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/nuget/">說明文件</a>。
|
||||||
nuget.dependency.framework=目標框架
|
nuget.dependency.framework=目標框架
|
||||||
npm.registry=在您的 <code>.npmrc</code> 檔設定此註冊中心:
|
npm.registry=在您專案的 <code>.npmrc</code> 檔設定此註冊中心:
|
||||||
npm.install=執行下列命令以使用 npm 安裝此套件:
|
npm.install=執行下列命令以使用 npm 安裝此套件:
|
||||||
npm.install2=或將它加到 package.json 檔:
|
npm.install2=或將它加到 package.json 檔:
|
||||||
npm.documentation=關於 npm registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/npm/">說明文件</a>。
|
npm.documentation=關於 npm registry 的詳情請參閱<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/npm/">說明文件</a>。
|
||||||
@ -3241,6 +3246,7 @@ settings.delete.success=已刪除該套件。
|
|||||||
settings.delete.error=刪除套件失敗。
|
settings.delete.error=刪除套件失敗。
|
||||||
owner.settings.cargo.title=Cargo Registry 索引
|
owner.settings.cargo.title=Cargo Registry 索引
|
||||||
owner.settings.cargo.initialize=初始化索引
|
owner.settings.cargo.initialize=初始化索引
|
||||||
|
owner.settings.cargo.initialize.description=使用 Cargo Registry 時需要一個特別的 Git 儲存庫作為索引。您可以在此使用必要的設定建立和重建它。
|
||||||
owner.settings.cargo.initialize.error=初始化 Cargo 索引失敗: %v
|
owner.settings.cargo.initialize.error=初始化 Cargo 索引失敗: %v
|
||||||
owner.settings.cargo.initialize.success=成功建立了 Cargo 索引。
|
owner.settings.cargo.initialize.success=成功建立了 Cargo 索引。
|
||||||
owner.settings.cargo.rebuild=重建索引
|
owner.settings.cargo.rebuild=重建索引
|
||||||
@ -3252,6 +3258,7 @@ owner.settings.cleanuprules.add=加入清理規則
|
|||||||
owner.settings.cleanuprules.edit=編輯清理規則
|
owner.settings.cleanuprules.edit=編輯清理規則
|
||||||
owner.settings.cleanuprules.none=沒有可用的清理規則。閱讀文件以了解更多。
|
owner.settings.cleanuprules.none=沒有可用的清理規則。閱讀文件以了解更多。
|
||||||
owner.settings.cleanuprules.preview=清理規則預覽
|
owner.settings.cleanuprules.preview=清理規則預覽
|
||||||
|
owner.settings.cleanuprules.preview.overview=已排定要移除 %d 個套件。
|
||||||
owner.settings.cleanuprules.preview.none=清理規則不符合任何套件。
|
owner.settings.cleanuprules.preview.none=清理規則不符合任何套件。
|
||||||
owner.settings.cleanuprules.enabled=已啟用
|
owner.settings.cleanuprules.enabled=已啟用
|
||||||
owner.settings.cleanuprules.pattern_full_match=將比對規則套用到完整的套件名稱
|
owner.settings.cleanuprules.pattern_full_match=將比對規則套用到完整的套件名稱
|
||||||
@ -3275,8 +3282,9 @@ secrets=Secret
|
|||||||
description=Secret 會被傳給特定的 Action,其他情況無法讀取。
|
description=Secret 會被傳給特定的 Action,其他情況無法讀取。
|
||||||
none=還沒有 Secret。
|
none=還沒有 Secret。
|
||||||
value=值
|
value=值
|
||||||
name=組織名稱
|
name=名稱
|
||||||
creation=加入 Secret
|
creation=加入 Secret
|
||||||
|
creation.name_placeholder=不區分大小寫,只能包含英文字母、數字、底線 ('_'),不能以 GITEA_ 或 GITHUB_ 開頭。
|
||||||
creation.value_placeholder=輸入任何內容,頭尾的空白都會被忽略。
|
creation.value_placeholder=輸入任何內容,頭尾的空白都會被忽略。
|
||||||
creation.success=已加入 Secret「%s」。
|
creation.success=已加入 Secret「%s」。
|
||||||
creation.failed=加入 Secret 失敗。
|
creation.failed=加入 Secret 失敗。
|
||||||
@ -3305,8 +3313,8 @@ runners.new=建立 Runner
|
|||||||
runners.new_notice=如何啟動 Runner
|
runners.new_notice=如何啟動 Runner
|
||||||
runners.status=狀態
|
runners.status=狀態
|
||||||
runners.id=ID
|
runners.id=ID
|
||||||
runners.name=組織名稱
|
runners.name=名稱
|
||||||
runners.owner_type=認證類型
|
runners.owner_type=類型
|
||||||
runners.description=組織描述
|
runners.description=組織描述
|
||||||
runners.labels=標籤
|
runners.labels=標籤
|
||||||
runners.last_online=最後上線時間
|
runners.last_online=最後上線時間
|
||||||
|
@ -105,10 +105,7 @@ func CreateHook(ctx *context.APIContext) {
|
|||||||
// "$ref": "#/responses/Hook"
|
// "$ref": "#/responses/Hook"
|
||||||
|
|
||||||
form := web.GetForm(ctx).(*api.CreateHookOption)
|
form := web.GetForm(ctx).(*api.CreateHookOption)
|
||||||
// TODO in body params
|
|
||||||
if !utils.CheckCreateHookOption(ctx, form) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
utils.AddSystemHook(ctx, form)
|
utils.AddSystemHook(ctx, form)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -835,6 +835,13 @@ func Routes(ctx gocontext.Context) *web.Route {
|
|||||||
m.Get("/stopwatches", reqToken(auth_model.AccessTokenScopeRepo), repo.GetStopwatches)
|
m.Get("/stopwatches", reqToken(auth_model.AccessTokenScopeRepo), repo.GetStopwatches)
|
||||||
m.Get("/subscriptions", reqToken(auth_model.AccessTokenScopeRepo), user.GetMyWatchedRepos)
|
m.Get("/subscriptions", reqToken(auth_model.AccessTokenScopeRepo), user.GetMyWatchedRepos)
|
||||||
m.Get("/teams", reqToken(auth_model.AccessTokenScopeRepo), org.ListUserTeams)
|
m.Get("/teams", reqToken(auth_model.AccessTokenScopeRepo), org.ListUserTeams)
|
||||||
|
m.Group("/hooks", func() {
|
||||||
|
m.Combo("").Get(user.ListHooks).
|
||||||
|
Post(bind(api.CreateHookOption{}), user.CreateHook)
|
||||||
|
m.Combo("/{id}").Get(user.GetHook).
|
||||||
|
Patch(bind(api.EditHookOption{}), user.EditHook).
|
||||||
|
Delete(user.DeleteHook)
|
||||||
|
}, reqToken(auth_model.AccessTokenScopeAdminUserHook), reqWebhooksEnabled())
|
||||||
}, reqToken(""))
|
}, reqToken(""))
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
|
@ -6,7 +6,6 @@ package org
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
webhook_model "code.gitea.io/gitea/models/webhook"
|
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
@ -39,34 +38,10 @@ func ListHooks(ctx *context.APIContext) {
|
|||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/HookList"
|
// "$ref": "#/responses/HookList"
|
||||||
|
|
||||||
opts := &webhook_model.ListWebhookOptions{
|
utils.ListOwnerHooks(
|
||||||
ListOptions: utils.GetListOptions(ctx),
|
ctx,
|
||||||
OrgID: ctx.Org.Organization.ID,
|
ctx.ContextUser,
|
||||||
}
|
)
|
||||||
|
|
||||||
count, err := webhook_model.CountWebhooksByOpts(opts)
|
|
||||||
if err != nil {
|
|
||||||
ctx.InternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
orgHooks, err := webhook_model.ListWebhooksByOpts(ctx, opts)
|
|
||||||
if err != nil {
|
|
||||||
ctx.InternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hooks := make([]*api.Hook, len(orgHooks))
|
|
||||||
for i, hook := range orgHooks {
|
|
||||||
hooks[i], err = webhook_service.ToHook(ctx.Org.Organization.AsUser().HomeLink(), hook)
|
|
||||||
if err != nil {
|
|
||||||
ctx.InternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.SetTotalCountHeader(count)
|
|
||||||
ctx.JSON(http.StatusOK, hooks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHook get an organization's hook by id
|
// GetHook get an organization's hook by id
|
||||||
@ -92,14 +67,12 @@ func GetHook(ctx *context.APIContext) {
|
|||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/Hook"
|
// "$ref": "#/responses/Hook"
|
||||||
|
|
||||||
org := ctx.Org.Organization
|
hook, err := utils.GetOwnerHook(ctx, ctx.ContextUser.ID, ctx.ParamsInt64("id"))
|
||||||
hookID := ctx.ParamsInt64(":id")
|
|
||||||
hook, err := utils.GetOrgHook(ctx, org.ID, hookID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiHook, err := webhook_service.ToHook(org.AsUser().HomeLink(), hook)
|
apiHook, err := webhook_service.ToHook(ctx.ContextUser.HomeLink(), hook)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.InternalServerError(err)
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
@ -131,15 +104,14 @@ func CreateHook(ctx *context.APIContext) {
|
|||||||
// "201":
|
// "201":
|
||||||
// "$ref": "#/responses/Hook"
|
// "$ref": "#/responses/Hook"
|
||||||
|
|
||||||
form := web.GetForm(ctx).(*api.CreateHookOption)
|
utils.AddOwnerHook(
|
||||||
// TODO in body params
|
ctx,
|
||||||
if !utils.CheckCreateHookOption(ctx, form) {
|
ctx.ContextUser,
|
||||||
return
|
web.GetForm(ctx).(*api.CreateHookOption),
|
||||||
}
|
)
|
||||||
utils.AddOrgHook(ctx, form)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditHook modify a hook of a repository
|
// EditHook modify a hook of an organization
|
||||||
func EditHook(ctx *context.APIContext) {
|
func EditHook(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /orgs/{org}/hooks/{id} organization orgEditHook
|
// swagger:operation PATCH /orgs/{org}/hooks/{id} organization orgEditHook
|
||||||
// ---
|
// ---
|
||||||
@ -168,11 +140,12 @@ func EditHook(ctx *context.APIContext) {
|
|||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/Hook"
|
// "$ref": "#/responses/Hook"
|
||||||
|
|
||||||
form := web.GetForm(ctx).(*api.EditHookOption)
|
utils.EditOwnerHook(
|
||||||
|
ctx,
|
||||||
// TODO in body params
|
ctx.ContextUser,
|
||||||
hookID := ctx.ParamsInt64(":id")
|
web.GetForm(ctx).(*api.EditHookOption),
|
||||||
utils.EditOrgHook(ctx, form, hookID)
|
ctx.ParamsInt64("id"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHook delete a hook of an organization
|
// DeleteHook delete a hook of an organization
|
||||||
@ -198,15 +171,9 @@ func DeleteHook(ctx *context.APIContext) {
|
|||||||
// "204":
|
// "204":
|
||||||
// "$ref": "#/responses/empty"
|
// "$ref": "#/responses/empty"
|
||||||
|
|
||||||
org := ctx.Org.Organization
|
utils.DeleteOwnerHook(
|
||||||
hookID := ctx.ParamsInt64(":id")
|
ctx,
|
||||||
if err := webhook_model.DeleteWebhookByOrgID(org.ID, hookID); err != nil {
|
ctx.ContextUser,
|
||||||
if webhook_model.IsErrWebhookNotExist(err) {
|
ctx.ParamsInt64("id"),
|
||||||
ctx.NotFound()
|
)
|
||||||
} else {
|
|
||||||
ctx.Error(http.StatusInternalServerError, "DeleteWebhookByOrgID", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Status(http.StatusNoContent)
|
|
||||||
}
|
}
|
||||||
|
@ -223,12 +223,8 @@ func CreateHook(ctx *context.APIContext) {
|
|||||||
// responses:
|
// responses:
|
||||||
// "201":
|
// "201":
|
||||||
// "$ref": "#/responses/Hook"
|
// "$ref": "#/responses/Hook"
|
||||||
form := web.GetForm(ctx).(*api.CreateHookOption)
|
|
||||||
|
|
||||||
if !utils.CheckCreateHookOption(ctx, form) {
|
utils.AddRepoHook(ctx, web.GetForm(ctx).(*api.CreateHookOption))
|
||||||
return
|
|
||||||
}
|
|
||||||
utils.AddRepoHook(ctx, form)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditHook modify a hook of a repository
|
// EditHook modify a hook of a repository
|
||||||
|
154
routers/api/v1/user/hook.go
Normal file
154
routers/api/v1/user/hook.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
|
webhook_service "code.gitea.io/gitea/services/webhook"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListHooks list the authenticated user's webhooks
|
||||||
|
func ListHooks(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /user/hooks user userListHooks
|
||||||
|
// ---
|
||||||
|
// summary: List the authenticated user's webhooks
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: page
|
||||||
|
// in: query
|
||||||
|
// description: page number of results to return (1-based)
|
||||||
|
// type: integer
|
||||||
|
// - name: limit
|
||||||
|
// in: query
|
||||||
|
// description: page size of results
|
||||||
|
// type: integer
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/HookList"
|
||||||
|
|
||||||
|
utils.ListOwnerHooks(
|
||||||
|
ctx,
|
||||||
|
ctx.Doer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHook get the authenticated user's hook by id
|
||||||
|
func GetHook(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /user/hooks/{id} user userGetHook
|
||||||
|
// ---
|
||||||
|
// summary: Get a hook
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: id
|
||||||
|
// in: path
|
||||||
|
// description: id of the hook to get
|
||||||
|
// type: integer
|
||||||
|
// format: int64
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/Hook"
|
||||||
|
|
||||||
|
hook, err := utils.GetOwnerHook(ctx, ctx.Doer.ID, ctx.ParamsInt64("id"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiHook, err := webhook_service.ToHook(ctx.Doer.HomeLink(), hook)
|
||||||
|
if err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, apiHook)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateHook create a hook for the authenticated user
|
||||||
|
func CreateHook(ctx *context.APIContext) {
|
||||||
|
// swagger:operation POST /user/hooks user userCreateHook
|
||||||
|
// ---
|
||||||
|
// summary: Create a hook
|
||||||
|
// consumes:
|
||||||
|
// - application/json
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// required: true
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/CreateHookOption"
|
||||||
|
// responses:
|
||||||
|
// "201":
|
||||||
|
// "$ref": "#/responses/Hook"
|
||||||
|
|
||||||
|
utils.AddOwnerHook(
|
||||||
|
ctx,
|
||||||
|
ctx.Doer,
|
||||||
|
web.GetForm(ctx).(*api.CreateHookOption),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditHook modify a hook of the authenticated user
|
||||||
|
func EditHook(ctx *context.APIContext) {
|
||||||
|
// swagger:operation PATCH /user/hooks/{id} user userEditHook
|
||||||
|
// ---
|
||||||
|
// summary: Update a hook
|
||||||
|
// consumes:
|
||||||
|
// - application/json
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: id
|
||||||
|
// in: path
|
||||||
|
// description: id of the hook to update
|
||||||
|
// type: integer
|
||||||
|
// format: int64
|
||||||
|
// required: true
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/EditHookOption"
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/Hook"
|
||||||
|
|
||||||
|
utils.EditOwnerHook(
|
||||||
|
ctx,
|
||||||
|
ctx.Doer,
|
||||||
|
web.GetForm(ctx).(*api.EditHookOption),
|
||||||
|
ctx.ParamsInt64("id"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteHook delete a hook of the authenticated user
|
||||||
|
func DeleteHook(ctx *context.APIContext) {
|
||||||
|
// swagger:operation DELETE /user/hooks/{id} user userDeleteHook
|
||||||
|
// ---
|
||||||
|
// summary: Delete a hook
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: id
|
||||||
|
// in: path
|
||||||
|
// description: id of the hook to delete
|
||||||
|
// type: integer
|
||||||
|
// format: int64
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
|
||||||
|
utils.DeleteOwnerHook(
|
||||||
|
ctx,
|
||||||
|
ctx.Doer,
|
||||||
|
ctx.ParamsInt64("id"),
|
||||||
|
)
|
||||||
|
}
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/models/webhook"
|
"code.gitea.io/gitea/models/webhook"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
@ -18,15 +19,46 @@ import (
|
|||||||
webhook_service "code.gitea.io/gitea/services/webhook"
|
webhook_service "code.gitea.io/gitea/services/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetOrgHook get an organization's webhook. If there is an error, write to
|
// ListOwnerHooks lists the webhooks of the provided owner
|
||||||
// `ctx` accordingly and return the error
|
func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) {
|
||||||
func GetOrgHook(ctx *context.APIContext, orgID, hookID int64) (*webhook.Webhook, error) {
|
opts := &webhook.ListWebhookOptions{
|
||||||
w, err := webhook.GetWebhookByOrgID(orgID, hookID)
|
ListOptions: GetListOptions(ctx),
|
||||||
|
OwnerID: owner.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := webhook.CountWebhooksByOpts(opts)
|
||||||
|
if err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks, err := webhook.ListWebhooksByOpts(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiHooks := make([]*api.Hook, len(hooks))
|
||||||
|
for i, hook := range hooks {
|
||||||
|
apiHooks[i], err = webhook_service.ToHook(owner.HomeLink(), hook)
|
||||||
|
if err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.SetTotalCountHeader(count)
|
||||||
|
ctx.JSON(http.StatusOK, apiHooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwnerHook gets an user or organization webhook. Errors are written to ctx.
|
||||||
|
func GetOwnerHook(ctx *context.APIContext, ownerID, hookID int64) (*webhook.Webhook, error) {
|
||||||
|
w, err := webhook.GetWebhookByOwnerID(ownerID, hookID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if webhook.IsErrWebhookNotExist(err) {
|
if webhook.IsErrWebhookNotExist(err) {
|
||||||
ctx.NotFound()
|
ctx.NotFound()
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "GetWebhookByOrgID", err)
|
ctx.Error(http.StatusInternalServerError, "GetWebhookByOwnerID", err)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -48,9 +80,9 @@ func GetRepoHook(ctx *context.APIContext, repoID, hookID int64) (*webhook.Webhoo
|
|||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckCreateHookOption check if a CreateHookOption form is valid. If invalid,
|
// checkCreateHookOption check if a CreateHookOption form is valid. If invalid,
|
||||||
// write the appropriate error to `ctx`. Return whether the form is valid
|
// write the appropriate error to `ctx`. Return whether the form is valid
|
||||||
func CheckCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption) bool {
|
func checkCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption) bool {
|
||||||
if !webhook_service.IsValidHookTaskType(form.Type) {
|
if !webhook_service.IsValidHookTaskType(form.Type) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Invalid hook type: %s", form.Type))
|
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Invalid hook type: %s", form.Type))
|
||||||
return false
|
return false
|
||||||
@ -81,14 +113,13 @@ func AddSystemHook(ctx *context.APIContext, form *api.CreateHookOption) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOrgHook add a hook to an organization. Writes to `ctx` accordingly
|
// AddOwnerHook adds a hook to an user or organization
|
||||||
func AddOrgHook(ctx *context.APIContext, form *api.CreateHookOption) {
|
func AddOwnerHook(ctx *context.APIContext, owner *user_model.User, form *api.CreateHookOption) {
|
||||||
org := ctx.Org.Organization
|
hook, ok := addHook(ctx, form, owner.ID, 0)
|
||||||
hook, ok := addHook(ctx, form, org.ID, 0)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
apiHook, ok := toAPIHook(ctx, org.AsUser().HomeLink(), hook)
|
apiHook, ok := toAPIHook(ctx, owner.HomeLink(), hook)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -128,14 +159,18 @@ func pullHook(events []string, event string) bool {
|
|||||||
return util.SliceContainsString(events, event, true) || util.SliceContainsString(events, string(webhook_module.HookEventPullRequest), true)
|
return util.SliceContainsString(events, event, true) || util.SliceContainsString(events, string(webhook_module.HookEventPullRequest), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addHook add the hook specified by `form`, `orgID` and `repoID`. If there is
|
// addHook add the hook specified by `form`, `ownerID` and `repoID`. If there is
|
||||||
// an error, write to `ctx` accordingly. Return (webhook, ok)
|
// an error, write to `ctx` accordingly. Return (webhook, ok)
|
||||||
func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID int64) (*webhook.Webhook, bool) {
|
func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoID int64) (*webhook.Webhook, bool) {
|
||||||
|
if !checkCreateHookOption(ctx, form) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
if len(form.Events) == 0 {
|
if len(form.Events) == 0 {
|
||||||
form.Events = []string{"push"}
|
form.Events = []string{"push"}
|
||||||
}
|
}
|
||||||
w := &webhook.Webhook{
|
w := &webhook.Webhook{
|
||||||
OrgID: orgID,
|
OwnerID: ownerID,
|
||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
URL: form.Config["url"],
|
URL: form.Config["url"],
|
||||||
ContentType: webhook.ToHookContentType(form.Config["content_type"]),
|
ContentType: webhook.ToHookContentType(form.Config["content_type"]),
|
||||||
@ -234,21 +269,20 @@ func EditSystemHook(ctx *context.APIContext, form *api.EditHookOption, hookID in
|
|||||||
ctx.JSON(http.StatusOK, h)
|
ctx.JSON(http.StatusOK, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditOrgHook edit webhook `w` according to `form`. Writes to `ctx` accordingly
|
// EditOwnerHook updates a webhook of an user or organization
|
||||||
func EditOrgHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64) {
|
func EditOwnerHook(ctx *context.APIContext, owner *user_model.User, form *api.EditHookOption, hookID int64) {
|
||||||
org := ctx.Org.Organization
|
hook, err := GetOwnerHook(ctx, owner.ID, hookID)
|
||||||
hook, err := GetOrgHook(ctx, org.ID, hookID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !editHook(ctx, form, hook) {
|
if !editHook(ctx, form, hook) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updated, err := GetOrgHook(ctx, org.ID, hookID)
|
updated, err := GetOwnerHook(ctx, owner.ID, hookID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
apiHook, ok := toAPIHook(ctx, org.AsUser().HomeLink(), updated)
|
apiHook, ok := toAPIHook(ctx, owner.HomeLink(), updated)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -362,3 +396,16 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteOwnerHook deletes the hook owned by the owner.
|
||||||
|
func DeleteOwnerHook(ctx *context.APIContext, owner *user_model.User, hookID int64) {
|
||||||
|
if err := webhook.DeleteWebhookByOwnerID(owner.ID, hookID); err != nil {
|
||||||
|
if webhook.IsErrWebhookNotExist(err) {
|
||||||
|
ctx.NotFound()
|
||||||
|
} else {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "DeleteWebhookByOwnerID", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
@ -271,6 +271,15 @@ func NewAuthSourcePost(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
case auth.OAuth2:
|
case auth.OAuth2:
|
||||||
config = parseOAuth2Config(form)
|
config = parseOAuth2Config(form)
|
||||||
|
oauth2Config := config.(*oauth2.Source)
|
||||||
|
if oauth2Config.Provider == "openidConnect" {
|
||||||
|
discoveryURL, err := url.Parse(oauth2Config.OpenIDConnectAutoDiscoveryURL)
|
||||||
|
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||||
|
ctx.Data["Err_DiscoveryURL"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("admin.auths.invalid_openIdConnectAutoDiscoveryURL"), tplAuthNew, form)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
case auth.SSPI:
|
case auth.SSPI:
|
||||||
var err error
|
var err error
|
||||||
config, err = parseSSPIConfig(ctx, form)
|
config, err = parseSSPIConfig(ctx, form)
|
||||||
@ -305,6 +314,10 @@ func NewAuthSourcePost(ctx *context.Context) {
|
|||||||
if auth.IsErrSourceAlreadyExist(err) {
|
if auth.IsErrSourceAlreadyExist(err) {
|
||||||
ctx.Data["Err_Name"] = true
|
ctx.Data["Err_Name"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_exist", err.(auth.ErrSourceAlreadyExist).Name), tplAuthNew, form)
|
ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_exist", err.(auth.ErrSourceAlreadyExist).Name), tplAuthNew, form)
|
||||||
|
} else if oauth2.IsErrOpenIDConnectInitialize(err) {
|
||||||
|
ctx.Data["Err_DiscoveryURL"] = true
|
||||||
|
unwrapped := err.(oauth2.ErrOpenIDConnectInitialize).Unwrap()
|
||||||
|
ctx.RenderWithErr(ctx.Tr("admin.auths.unable_to_initialize_openid", unwrapped), tplAuthNew, form)
|
||||||
} else {
|
} else {
|
||||||
ctx.ServerError("auth.CreateSource", err)
|
ctx.ServerError("auth.CreateSource", err)
|
||||||
}
|
}
|
||||||
@ -389,6 +402,15 @@ func EditAuthSourcePost(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
case auth.OAuth2:
|
case auth.OAuth2:
|
||||||
config = parseOAuth2Config(form)
|
config = parseOAuth2Config(form)
|
||||||
|
oauth2Config := config.(*oauth2.Source)
|
||||||
|
if oauth2Config.Provider == "openidConnect" {
|
||||||
|
discoveryURL, err := url.Parse(oauth2Config.OpenIDConnectAutoDiscoveryURL)
|
||||||
|
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||||
|
ctx.Data["Err_DiscoveryURL"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("admin.auths.invalid_openIdConnectAutoDiscoveryURL"), tplAuthEdit, form)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
case auth.SSPI:
|
case auth.SSPI:
|
||||||
config, err = parseSSPIConfig(ctx, form)
|
config, err = parseSSPIConfig(ctx, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -408,6 +430,7 @@ func EditAuthSourcePost(ctx *context.Context) {
|
|||||||
if err := auth.UpdateSource(source); err != nil {
|
if err := auth.UpdateSource(source); err != nil {
|
||||||
if oauth2.IsErrOpenIDConnectInitialize(err) {
|
if oauth2.IsErrOpenIDConnectInitialize(err) {
|
||||||
ctx.Flash.Error(err.Error(), true)
|
ctx.Flash.Error(err.Error(), true)
|
||||||
|
ctx.Data["Err_DiscoveryURL"] = true
|
||||||
ctx.HTML(http.StatusOK, tplAuthEdit)
|
ctx.HTML(http.StatusOK, tplAuthEdit)
|
||||||
} else {
|
} else {
|
||||||
ctx.ServerError("UpdateSource", err)
|
ctx.ServerError("UpdateSource", err)
|
||||||
|
@ -156,6 +156,7 @@ func Home(ctx *context.Context) {
|
|||||||
pager.SetDefaultParams(ctx)
|
pager.SetDefaultParams(ctx)
|
||||||
pager.AddParam(ctx, "language", "Language")
|
pager.AddParam(ctx, "language", "Language")
|
||||||
ctx.Data["Page"] = pager
|
ctx.Data["Page"] = pager
|
||||||
|
ctx.Data["ContextUser"] = ctx.ContextUser
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplOrgHome)
|
ctx.HTML(http.StatusOK, tplOrgHome)
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,7 @@ func NewProject(ctx *context.Context) {
|
|||||||
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
|
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
|
||||||
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
|
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
|
||||||
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
|
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
|
||||||
|
ctx.Data["PageIsViewProjects"] = true
|
||||||
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
|
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
|
||||||
shared_user.RenderUserHeader(ctx)
|
shared_user.RenderUserHeader(ctx)
|
||||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||||
|
@ -218,9 +218,9 @@ func Webhooks(ctx *context.Context) {
|
|||||||
ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks"
|
ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks"
|
||||||
ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")
|
ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")
|
||||||
|
|
||||||
ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OrgID: ctx.Org.Organization.ID})
|
ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OwnerID: ctx.Org.Organization.ID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetWebhooksByOrgId", err)
|
ctx.ServerError("ListWebhooksByOpts", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,8 +230,8 @@ func Webhooks(ctx *context.Context) {
|
|||||||
|
|
||||||
// DeleteWebhook response for delete webhook
|
// DeleteWebhook response for delete webhook
|
||||||
func DeleteWebhook(ctx *context.Context) {
|
func DeleteWebhook(ctx *context.Context) {
|
||||||
if err := webhook.DeleteWebhookByOrgID(ctx.Org.Organization.ID, ctx.FormInt64("id")); err != nil {
|
if err := webhook.DeleteWebhookByOwnerID(ctx.Org.Organization.ID, ctx.FormInt64("id")); err != nil {
|
||||||
ctx.Flash.Error("DeleteWebhookByOrgID: " + err.Error())
|
ctx.Flash.Error("DeleteWebhookByOwnerID: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
|
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ const (
|
|||||||
tplHooks base.TplName = "repo/settings/webhook/base"
|
tplHooks base.TplName = "repo/settings/webhook/base"
|
||||||
tplHookNew base.TplName = "repo/settings/webhook/new"
|
tplHookNew base.TplName = "repo/settings/webhook/new"
|
||||||
tplOrgHookNew base.TplName = "org/settings/hook_new"
|
tplOrgHookNew base.TplName = "org/settings/hook_new"
|
||||||
|
tplUserHookNew base.TplName = "user/settings/hook_new"
|
||||||
tplAdminHookNew base.TplName = "admin/hook_new"
|
tplAdminHookNew base.TplName = "admin/hook_new"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,8 +55,8 @@ func Webhooks(ctx *context.Context) {
|
|||||||
ctx.HTML(http.StatusOK, tplHooks)
|
ctx.HTML(http.StatusOK, tplHooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
type orgRepoCtx struct {
|
type ownerRepoCtx struct {
|
||||||
OrgID int64
|
OwnerID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
IsAdmin bool
|
IsAdmin bool
|
||||||
IsSystemWebhook bool
|
IsSystemWebhook bool
|
||||||
@ -64,10 +65,10 @@ type orgRepoCtx struct {
|
|||||||
NewTemplate base.TplName
|
NewTemplate base.TplName
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOrgRepoCtx determines whether this is a repo, organization, or admin (both default and system) context.
|
// getOwnerRepoCtx determines whether this is a repo, owner, or admin (both default and system) context.
|
||||||
func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) {
|
func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) {
|
||||||
if len(ctx.Repo.RepoLink) > 0 {
|
if is, ok := ctx.Data["IsRepositoryWebhook"]; ok && is.(bool) {
|
||||||
return &orgRepoCtx{
|
return &ownerRepoCtx{
|
||||||
RepoID: ctx.Repo.Repository.ID,
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
Link: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
|
Link: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
|
||||||
LinkNew: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
|
LinkNew: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
|
||||||
@ -75,37 +76,35 @@ func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ctx.Org.OrgLink) > 0 {
|
if is, ok := ctx.Data["IsOrganizationWebhook"]; ok && is.(bool) {
|
||||||
return &orgRepoCtx{
|
return &ownerRepoCtx{
|
||||||
OrgID: ctx.Org.Organization.ID,
|
OwnerID: ctx.ContextUser.ID,
|
||||||
Link: path.Join(ctx.Org.OrgLink, "settings/hooks"),
|
Link: path.Join(ctx.Org.OrgLink, "settings/hooks"),
|
||||||
LinkNew: path.Join(ctx.Org.OrgLink, "settings/hooks"),
|
LinkNew: path.Join(ctx.Org.OrgLink, "settings/hooks"),
|
||||||
NewTemplate: tplOrgHookNew,
|
NewTemplate: tplOrgHookNew,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Doer.IsAdmin {
|
if is, ok := ctx.Data["IsUserWebhook"]; ok && is.(bool) {
|
||||||
// Are we looking at default webhooks?
|
return &ownerRepoCtx{
|
||||||
if ctx.Params(":configType") == "default-hooks" {
|
OwnerID: ctx.Doer.ID,
|
||||||
return &orgRepoCtx{
|
Link: path.Join(setting.AppSubURL, "/user/settings/hooks"),
|
||||||
IsAdmin: true,
|
LinkNew: path.Join(setting.AppSubURL, "/user/settings/hooks"),
|
||||||
Link: path.Join(setting.AppSubURL, "/admin/hooks"),
|
NewTemplate: tplUserHookNew,
|
||||||
LinkNew: path.Join(setting.AppSubURL, "/admin/default-hooks"),
|
|
||||||
NewTemplate: tplAdminHookNew,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be system webhooks instead
|
if ctx.Doer.IsAdmin {
|
||||||
return &orgRepoCtx{
|
return &ownerRepoCtx{
|
||||||
IsAdmin: true,
|
IsAdmin: true,
|
||||||
IsSystemWebhook: true,
|
IsSystemWebhook: ctx.Params(":configType") == "system-hooks",
|
||||||
Link: path.Join(setting.AppSubURL, "/admin/hooks"),
|
Link: path.Join(setting.AppSubURL, "/admin/hooks"),
|
||||||
LinkNew: path.Join(setting.AppSubURL, "/admin/system-hooks"),
|
LinkNew: path.Join(setting.AppSubURL, "/admin/system-hooks"),
|
||||||
NewTemplate: tplAdminHookNew,
|
NewTemplate: tplAdminHookNew,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("unable to set OrgRepo context")
|
return nil, errors.New("unable to set OwnerRepo context")
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkHookType(ctx *context.Context) string {
|
func checkHookType(ctx *context.Context) string {
|
||||||
@ -122,9 +121,9 @@ func WebhooksNew(ctx *context.Context) {
|
|||||||
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
|
||||||
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
|
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
|
||||||
|
|
||||||
orCtx, err := getOrgRepoCtx(ctx)
|
orCtx, err := getOwnerRepoCtx(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("getOrgRepoCtx", err)
|
ctx.ServerError("getOwnerRepoCtx", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,9 +204,9 @@ func createWebhook(ctx *context.Context, params webhookParams) {
|
|||||||
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
|
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
|
||||||
ctx.Data["HookType"] = params.Type
|
ctx.Data["HookType"] = params.Type
|
||||||
|
|
||||||
orCtx, err := getOrgRepoCtx(ctx)
|
orCtx, err := getOwnerRepoCtx(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("getOrgRepoCtx", err)
|
ctx.ServerError("getOwnerRepoCtx", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["BaseLink"] = orCtx.LinkNew
|
ctx.Data["BaseLink"] = orCtx.LinkNew
|
||||||
@ -236,7 +235,7 @@ func createWebhook(ctx *context.Context, params webhookParams) {
|
|||||||
IsActive: params.WebhookForm.Active,
|
IsActive: params.WebhookForm.Active,
|
||||||
Type: params.Type,
|
Type: params.Type,
|
||||||
Meta: string(meta),
|
Meta: string(meta),
|
||||||
OrgID: orCtx.OrgID,
|
OwnerID: orCtx.OwnerID,
|
||||||
IsSystemWebhook: orCtx.IsSystemWebhook,
|
IsSystemWebhook: orCtx.IsSystemWebhook,
|
||||||
}
|
}
|
||||||
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
|
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
|
||||||
@ -577,19 +576,19 @@ func packagistHookParams(ctx *context.Context) webhookParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
|
func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
|
||||||
orCtx, err := getOrgRepoCtx(ctx)
|
orCtx, err := getOwnerRepoCtx(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("getOrgRepoCtx", err)
|
ctx.ServerError("getOwnerRepoCtx", err)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
ctx.Data["BaseLink"] = orCtx.Link
|
ctx.Data["BaseLink"] = orCtx.Link
|
||||||
|
|
||||||
var w *webhook.Webhook
|
var w *webhook.Webhook
|
||||||
if orCtx.RepoID > 0 {
|
if orCtx.RepoID > 0 {
|
||||||
w, err = webhook.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
|
w, err = webhook.GetWebhookByRepoID(orCtx.RepoID, ctx.ParamsInt64(":id"))
|
||||||
} else if orCtx.OrgID > 0 {
|
} else if orCtx.OwnerID > 0 {
|
||||||
w, err = webhook.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
|
w, err = webhook.GetWebhookByOwnerID(orCtx.OwnerID, ctx.ParamsInt64(":id"))
|
||||||
} else if orCtx.IsAdmin {
|
} else if orCtx.IsAdmin {
|
||||||
w, err = webhook.GetSystemOrDefaultWebhook(ctx, ctx.ParamsInt64(":id"))
|
w, err = webhook.GetSystemOrDefaultWebhook(ctx, ctx.ParamsInt64(":id"))
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func RenderUserHeader(ctx *context.Context) {
|
func RenderUserHeader(ctx *context.Context) {
|
||||||
|
ctx.Data["IsProjectEnabled"] = true
|
||||||
|
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
||||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||||
ctx.Data["ContextUser"] = ctx.ContextUser
|
ctx.Data["ContextUser"] = ctx.ContextUser
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ func CodeSearch(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.Data["IsProjectEnabled"] = true
|
||||||
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
||||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||||
ctx.Data["Title"] = ctx.Tr("explore.code")
|
ctx.Data["Title"] = ctx.Tr("explore.code")
|
||||||
|
@ -304,6 +304,7 @@ func Profile(ctx *context.Context) {
|
|||||||
pager.AddParam(ctx, "date", "Date")
|
pager.AddParam(ctx, "date", "Date")
|
||||||
}
|
}
|
||||||
ctx.Data["Page"] = pager
|
ctx.Data["Page"] = pager
|
||||||
|
ctx.Data["IsProjectEnabled"] = true
|
||||||
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
|
||||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||||
|
|
||||||
|
48
routers/web/user/setting/webhooks.go
Normal file
48
routers/web/user/setting/webhooks.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/webhook"
|
||||||
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tplSettingsHooks base.TplName = "user/settings/hooks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Webhooks render webhook list page
|
||||||
|
func Webhooks(ctx *context.Context) {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("settings")
|
||||||
|
ctx.Data["PageIsSettingsHooks"] = true
|
||||||
|
ctx.Data["BaseLink"] = setting.AppSubURL + "/user/settings/hooks"
|
||||||
|
ctx.Data["BaseLinkNew"] = setting.AppSubURL + "/user/settings/hooks"
|
||||||
|
ctx.Data["Description"] = ctx.Tr("settings.hooks.desc")
|
||||||
|
|
||||||
|
ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OwnerID: ctx.Doer.ID})
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("ListWebhooksByOpts", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["Webhooks"] = ws
|
||||||
|
ctx.HTML(http.StatusOK, tplSettingsHooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteWebhook response for delete webhook
|
||||||
|
func DeleteWebhook(ctx *context.Context) {
|
||||||
|
if err := webhook.DeleteWebhookByOwnerID(ctx.Doer.ID, ctx.FormInt64("id")); err != nil {
|
||||||
|
ctx.Flash.Error("DeleteWebhookByOwnerID: " + err.Error())
|
||||||
|
} else {
|
||||||
|
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||||
|
"redirect": setting.AppSubURL + "/user/settings/hooks",
|
||||||
|
})
|
||||||
|
}
|
@ -315,6 +315,35 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addWebhookAddRoutes := func() {
|
||||||
|
m.Get("/{type}/new", repo.WebhooksNew)
|
||||||
|
m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost)
|
||||||
|
m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost)
|
||||||
|
m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
||||||
|
m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
|
||||||
|
m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
|
||||||
|
m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost)
|
||||||
|
m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost)
|
||||||
|
m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
|
||||||
|
m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
|
||||||
|
m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
|
||||||
|
m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost)
|
||||||
|
}
|
||||||
|
|
||||||
|
addWebhookEditRoutes := func() {
|
||||||
|
m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost)
|
||||||
|
m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
|
||||||
|
m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
||||||
|
m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
|
||||||
|
m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
|
||||||
|
m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
|
||||||
|
m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
||||||
|
m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
||||||
|
m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
||||||
|
m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
|
||||||
|
m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost)
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: not all routes need go through same middleware.
|
// FIXME: not all routes need go through same middleware.
|
||||||
// Especially some AJAX requests, we can reduce middleware number to improve performance.
|
// Especially some AJAX requests, we can reduce middleware number to improve performance.
|
||||||
// Routers.
|
// Routers.
|
||||||
@ -482,6 +511,19 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
m.Get("/organization", user_setting.Organization)
|
m.Get("/organization", user_setting.Organization)
|
||||||
m.Get("/repos", user_setting.Repos)
|
m.Get("/repos", user_setting.Repos)
|
||||||
m.Post("/repos/unadopted", user_setting.AdoptOrDeleteRepository)
|
m.Post("/repos/unadopted", user_setting.AdoptOrDeleteRepository)
|
||||||
|
|
||||||
|
m.Group("/hooks", func() {
|
||||||
|
m.Get("", user_setting.Webhooks)
|
||||||
|
m.Post("/delete", user_setting.DeleteWebhook)
|
||||||
|
addWebhookAddRoutes()
|
||||||
|
m.Group("/{id}", func() {
|
||||||
|
m.Get("", repo.WebHooksEdit)
|
||||||
|
m.Post("/replay/{uuid}", repo.ReplayWebhook)
|
||||||
|
})
|
||||||
|
addWebhookEditRoutes()
|
||||||
|
}, webhooksEnabled, func(ctx *context.Context) {
|
||||||
|
ctx.Data["IsUserWebhook"] = true
|
||||||
|
})
|
||||||
}, reqSignIn, func(ctx *context.Context) {
|
}, reqSignIn, func(ctx *context.Context) {
|
||||||
ctx.Data["PageIsUserSettings"] = true
|
ctx.Data["PageIsUserSettings"] = true
|
||||||
ctx.Data["AllThemes"] = setting.UI.Themes
|
ctx.Data["AllThemes"] = setting.UI.Themes
|
||||||
@ -575,32 +617,11 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
m.Get("", repo.WebHooksEdit)
|
m.Get("", repo.WebHooksEdit)
|
||||||
m.Post("/replay/{uuid}", repo.ReplayWebhook)
|
m.Post("/replay/{uuid}", repo.ReplayWebhook)
|
||||||
})
|
})
|
||||||
m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost)
|
addWebhookEditRoutes()
|
||||||
m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
|
|
||||||
m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
|
||||||
m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
|
|
||||||
m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
|
|
||||||
m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
|
|
||||||
m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
|
||||||
m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
|
||||||
m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
|
||||||
m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
|
|
||||||
m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost)
|
|
||||||
}, webhooksEnabled)
|
}, webhooksEnabled)
|
||||||
|
|
||||||
m.Group("/{configType:default-hooks|system-hooks}", func() {
|
m.Group("/{configType:default-hooks|system-hooks}", func() {
|
||||||
m.Get("/{type}/new", repo.WebhooksNew)
|
addWebhookAddRoutes()
|
||||||
m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost)
|
|
||||||
m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost)
|
|
||||||
m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
|
||||||
m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
|
|
||||||
m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
|
|
||||||
m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost)
|
|
||||||
m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost)
|
|
||||||
m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
|
|
||||||
m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
|
|
||||||
m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
|
|
||||||
m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Group("/auths", func() {
|
m.Group("/auths", func() {
|
||||||
@ -690,6 +711,21 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reqUnitAccess := func(unitType unit.Type, accessMode perm.AccessMode) func(ctx *context.Context) {
|
||||||
|
return func(ctx *context.Context) {
|
||||||
|
if ctx.ContextUser == nil {
|
||||||
|
ctx.NotFound(unitType.String(), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctx.ContextUser.IsOrganization() {
|
||||||
|
if ctx.Org.Organization.UnitPermission(ctx, ctx.Doer, unitType) < accessMode {
|
||||||
|
ctx.NotFound(unitType.String(), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ***** START: Organization *****
|
// ***** START: Organization *****
|
||||||
m.Group("/org", func() {
|
m.Group("/org", func() {
|
||||||
m.Group("/{org}", func() {
|
m.Group("/{org}", func() {
|
||||||
@ -759,32 +795,15 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
m.Group("/hooks", func() {
|
m.Group("/hooks", func() {
|
||||||
m.Get("", org.Webhooks)
|
m.Get("", org.Webhooks)
|
||||||
m.Post("/delete", org.DeleteWebhook)
|
m.Post("/delete", org.DeleteWebhook)
|
||||||
m.Get("/{type}/new", repo.WebhooksNew)
|
addWebhookAddRoutes()
|
||||||
m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost)
|
|
||||||
m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost)
|
|
||||||
m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
|
||||||
m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
|
|
||||||
m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
|
|
||||||
m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost)
|
|
||||||
m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost)
|
|
||||||
m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
|
|
||||||
m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
|
|
||||||
m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
|
|
||||||
m.Group("/{id}", func() {
|
m.Group("/{id}", func() {
|
||||||
m.Get("", repo.WebHooksEdit)
|
m.Get("", repo.WebHooksEdit)
|
||||||
m.Post("/replay/{uuid}", repo.ReplayWebhook)
|
m.Post("/replay/{uuid}", repo.ReplayWebhook)
|
||||||
})
|
})
|
||||||
m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost)
|
addWebhookEditRoutes()
|
||||||
m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
|
}, webhooksEnabled, func(ctx *context.Context) {
|
||||||
m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
ctx.Data["IsOrganizationWebhook"] = true
|
||||||
m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
|
})
|
||||||
m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
|
|
||||||
m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
|
|
||||||
m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
|
||||||
m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
|
||||||
m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
|
||||||
m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
|
|
||||||
}, webhooksEnabled)
|
|
||||||
|
|
||||||
m.Group("/labels", func() {
|
m.Group("/labels", func() {
|
||||||
m.Get("", org.RetrieveLabels, org.Labels)
|
m.Get("", org.RetrieveLabels, org.Labels)
|
||||||
@ -869,8 +888,10 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.Group("/projects", func() {
|
m.Group("/projects", func() {
|
||||||
|
m.Group("", func() {
|
||||||
m.Get("", org.Projects)
|
m.Get("", org.Projects)
|
||||||
m.Get("/{id}", org.ViewProject)
|
m.Get("/{id}", org.ViewProject)
|
||||||
|
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead))
|
||||||
m.Group("", func() { //nolint:dupl
|
m.Group("", func() { //nolint:dupl
|
||||||
m.Get("/new", org.NewProject)
|
m.Get("/new", org.NewProject)
|
||||||
m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
|
m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
|
||||||
@ -890,25 +911,18 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
m.Post("/move", org.MoveIssues)
|
m.Post("/move", org.MoveIssues)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, reqSignIn, func(ctx *context.Context) {
|
}, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite), func(ctx *context.Context) {
|
||||||
if ctx.ContextUser == nil {
|
if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
|
||||||
ctx.NotFound("NewProject", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ctx.ContextUser.IsOrganization() {
|
|
||||||
if !ctx.Org.CanWriteUnit(ctx, unit.TypeProjects) {
|
|
||||||
ctx.NotFound("NewProject", nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if ctx.ContextUser.ID != ctx.Doer.ID {
|
|
||||||
ctx.NotFound("NewProject", nil)
|
ctx.NotFound("NewProject", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, repo.MustEnableProjects)
|
}, repo.MustEnableProjects)
|
||||||
|
|
||||||
|
m.Group("", func() {
|
||||||
m.Get("/code", user.CodeSearch)
|
m.Get("/code", user.CodeSearch)
|
||||||
}, context_service.UserAssignmentWeb())
|
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead))
|
||||||
|
}, context_service.UserAssignmentWeb(), context.OrgAssignment())
|
||||||
|
|
||||||
// ***** Release Attachment Download without Signin
|
// ***** Release Attachment Download without Signin
|
||||||
m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment, repo.MustBeNotEmpty, repo.RedirectDownload)
|
m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment, repo.MustBeNotEmpty, repo.RedirectDownload)
|
||||||
@ -962,35 +976,16 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
m.Group("/hooks", func() {
|
m.Group("/hooks", func() {
|
||||||
m.Get("", repo.Webhooks)
|
m.Get("", repo.Webhooks)
|
||||||
m.Post("/delete", repo.DeleteWebhook)
|
m.Post("/delete", repo.DeleteWebhook)
|
||||||
m.Get("/{type}/new", repo.WebhooksNew)
|
addWebhookAddRoutes()
|
||||||
m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost)
|
|
||||||
m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost)
|
|
||||||
m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
|
||||||
m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
|
|
||||||
m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
|
|
||||||
m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost)
|
|
||||||
m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost)
|
|
||||||
m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
|
|
||||||
m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
|
|
||||||
m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
|
|
||||||
m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost)
|
|
||||||
m.Group("/{id}", func() {
|
m.Group("/{id}", func() {
|
||||||
m.Get("", repo.WebHooksEdit)
|
m.Get("", repo.WebHooksEdit)
|
||||||
m.Post("/test", repo.TestWebhook)
|
m.Post("/test", repo.TestWebhook)
|
||||||
m.Post("/replay/{uuid}", repo.ReplayWebhook)
|
m.Post("/replay/{uuid}", repo.ReplayWebhook)
|
||||||
})
|
})
|
||||||
m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost)
|
addWebhookEditRoutes()
|
||||||
m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
|
}, webhooksEnabled, func(ctx *context.Context) {
|
||||||
m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
ctx.Data["IsRepositoryWebhook"] = true
|
||||||
m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
|
})
|
||||||
m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
|
|
||||||
m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
|
|
||||||
m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
|
||||||
m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
|
||||||
m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
|
||||||
m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
|
|
||||||
m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost)
|
|
||||||
}, webhooksEnabled)
|
|
||||||
|
|
||||||
m.Group("/keys", func() {
|
m.Group("/keys", func() {
|
||||||
m.Combo("").Get(repo.DeployKeys).
|
m.Combo("").Get(repo.DeployKeys).
|
||||||
|
@ -36,6 +36,10 @@ func (err ErrOpenIDConnectInitialize) Error() string {
|
|||||||
return fmt.Sprintf("Failed to initialize OpenID Connect Provider with name '%s' with url '%s': %v", err.ProviderName, err.OpenIDConnectAutoDiscoveryURL, err.Cause)
|
return fmt.Sprintf("Failed to initialize OpenID Connect Provider with name '%s' with url '%s': %v", err.ProviderName, err.OpenIDConnectAutoDiscoveryURL, err.Cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (err ErrOpenIDConnectInitialize) Unwrap() error {
|
||||||
|
return err.Cause
|
||||||
|
}
|
||||||
|
|
||||||
// wrapOpenIDConnectInitializeError is used to wrap the error but this cannot be done in modules/auth/oauth2
|
// wrapOpenIDConnectInitializeError is used to wrap the error but this cannot be done in modules/auth/oauth2
|
||||||
// inside oauth2: import cycle not allowed models -> modules/auth/oauth2 -> models
|
// inside oauth2: import cycle not allowed models -> modules/auth/oauth2 -> models
|
||||||
func wrapOpenIDConnectInitializeError(err error, providerName string, source *Source) error {
|
func wrapOpenIDConnectInitializeError(err error, providerName string, source *Source) error {
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
org_model "code.gitea.io/gitea/models/organization"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
)
|
)
|
||||||
@ -57,14 +56,6 @@ func userAssignment(ctx *context.Context, errCb func(int, string, interface{}))
|
|||||||
} else {
|
} else {
|
||||||
errCb(http.StatusInternalServerError, "GetUserByName", err)
|
errCb(http.StatusInternalServerError, "GetUserByName", err)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if ctx.ContextUser.IsOrganization() {
|
|
||||||
if ctx.Org == nil {
|
|
||||||
ctx.Org = &context.Organization{}
|
|
||||||
}
|
|
||||||
ctx.Org.Organization = (*org_model.Organization)(ctx.ContextUser)
|
|
||||||
ctx.Data["Org"] = ctx.Org.Organization
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *repo_mode
|
|||||||
HookEvent: templateWebhook.HookEvent,
|
HookEvent: templateWebhook.HookEvent,
|
||||||
IsActive: templateWebhook.IsActive,
|
IsActive: templateWebhook.IsActive,
|
||||||
Type: templateWebhook.Type,
|
Type: templateWebhook.Type,
|
||||||
OrgID: templateWebhook.OrgID,
|
OwnerID: templateWebhook.OwnerID,
|
||||||
Events: templateWebhook.Events,
|
Events: templateWebhook.Events,
|
||||||
Meta: templateWebhook.Meta,
|
Meta: templateWebhook.Meta,
|
||||||
})
|
})
|
||||||
|
@ -229,16 +229,16 @@ func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_modu
|
|||||||
owner = source.Repository.MustOwner(ctx)
|
owner = source.Repository.MustOwner(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if owner is an org and append additional webhooks
|
// append additional webhooks of a user or organization
|
||||||
if owner != nil && owner.IsOrganization() {
|
if owner != nil {
|
||||||
orgHooks, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{
|
ownerHooks, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{
|
||||||
OrgID: owner.ID,
|
OwnerID: owner.ID,
|
||||||
IsActive: util.OptionalBoolTrue,
|
IsActive: util.OptionalBoolTrue,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ListWebhooksByOpts: %w", err)
|
return fmt.Errorf("ListWebhooksByOpts: %w", err)
|
||||||
}
|
}
|
||||||
ws = append(ws, orgHooks...)
|
ws = append(ws, ownerHooks...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add any admin-defined system webhooks
|
// Add any admin-defined system webhooks
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<label for="oauth2_icon_url">{{.locale.Tr "admin.auths.oauth2_icon_url"}}</label>
|
<label for="oauth2_icon_url">{{.locale.Tr "admin.auths.oauth2_icon_url"}}</label>
|
||||||
<input id="oauth2_icon_url" name="oauth2_icon_url" value="{{.oauth2_icon_url}}">
|
<input id="oauth2_icon_url" name="oauth2_icon_url" value="{{.oauth2_icon_url}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="open_id_connect_auto_discovery_url required field">
|
<div class="open_id_connect_auto_discovery_url required field{{if .Err_DiscoveryURL}} error{{end}}">
|
||||||
<label for="open_id_connect_auto_discovery_url">{{.locale.Tr "admin.auths.openIdConnectAutoDiscoveryURL"}}</label>
|
<label for="open_id_connect_auto_discovery_url">{{.locale.Tr "admin.auths.openIdConnectAutoDiscoveryURL"}}</label>
|
||||||
<input id="open_id_connect_auto_discovery_url" name="open_id_connect_auto_discovery_url" value="{{.open_id_connect_auto_discovery_url}}">
|
<input id="open_id_connect_auto_discovery_url" name="open_id_connect_auto_discovery_url" value="{{.open_id_connect_auto_discovery_url}}">
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,16 +3,18 @@
|
|||||||
<a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}">
|
<a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}">
|
||||||
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
|
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
|
||||||
</a>
|
</a>
|
||||||
|
{{if and .IsProjectEnabled .CanReadProjects}}
|
||||||
<a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects">
|
<a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects">
|
||||||
{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
|
{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
|
||||||
</a>
|
</a>
|
||||||
{{if .IsPackageEnabled}}
|
{{end}}
|
||||||
|
{{if and .IsPackageEnabled .CanReadPackages}}
|
||||||
<a class="item" href="{{$.Org.HomeLink}}/-/packages">
|
<a class="item" href="{{$.Org.HomeLink}}/-/packages">
|
||||||
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
|
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .IsRepoIndexerEnabled}}
|
{{if and .IsRepoIndexerEnabled .CanReadCode}}
|
||||||
<a class="{{if $.PageIsOrgCode}}active {{end}}item" href="{{$.Org.HomeLink}}/-/code">
|
<a class="item" href="{{$.Org.HomeLink}}/-/code">
|
||||||
{{svg "octicon-code"}} {{$.locale.Tr "org.code"}}
|
{{svg "octicon-code"}} {{$.locale.Tr "org.code"}}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
<b>{{.tag_name}}</b><span class="at">@</span><strong>{{.tag_target}}</strong>
|
<b>{{.tag_name}}</b><span class="at">@</span><strong>{{.tag_target}}</strong>
|
||||||
{{else}}
|
{{else}}
|
||||||
<input id="tag-name" name="tag_name" value="{{.tag_name}}" placeholder="{{.locale.Tr "repo.release.tag_name"}}" autofocus required maxlength="255">
|
<input id="tag-name" name="tag_name" value="{{.tag_name}}" placeholder="{{.locale.Tr "repo.release.tag_name"}}" autofocus required maxlength="255">
|
||||||
|
<input id="tag-name-editor" type="hidden" data-existing-tags={{Json .Tags}} data-tag-helper={{.locale.Tr "repo.release.tag_helper"}} data-tag-helper-new={{.locale.Tr "repo.release.tag_helper_new"}} data-tag-helper-existing={{.locale.Tr "repo.release.tag_helper_existing"}} />
|
||||||
|
<div id="tag-target-selector" class="gt-dib">
|
||||||
<span class="at">@</span>
|
<span class="at">@</span>
|
||||||
<div class="ui selection dropdown">
|
<div class="ui selection dropdown">
|
||||||
<input type="hidden" name="tag_target" value="{{.tag_target}}"/>
|
<input type="hidden" name="tag_target" value="{{.tag_target}}"/>
|
||||||
@ -35,7 +37,10 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="help">{{.locale.Tr "repo.release.tag_helper"}}</span>
|
</div>
|
||||||
|
<div>
|
||||||
|
<span id="tag-helper" class="help gt-mt-2">{{.locale.Tr "repo.release.tag_helper"}}</span>
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
<span class="ui label">N/A</span>
|
<span class="ui label">N/A</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
</a>
|
</a>
|
||||||
{{if or $.Permission.IsAdmin $.IsOrganizationOwner $.PageIsAdmin}}
|
{{if or $.Permission.IsAdmin $.IsOrganizationOwner $.PageIsAdmin $.PageIsUserSettings}}
|
||||||
<div class="right menu">
|
<div class="right menu">
|
||||||
<form class="item" action="{{$.Link}}/replay/{{.UUID}}" method="post">
|
<form class="item" action="{{$.Link}}/replay/{{.UUID}}" method="post">
|
||||||
{{$.CsrfTokenHtml}}
|
{{$.CsrfTokenHtml}}
|
||||||
|
@ -13014,6 +13014,152 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/user/hooks": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"summary": "List the authenticated user's webhooks",
|
||||||
|
"operationId": "userListHooks",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page number of results to return (1-based)",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page size of results",
|
||||||
|
"name": "limit",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/responses/HookList"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"summary": "Create a hook",
|
||||||
|
"operationId": "userCreateHook",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/CreateHookOption"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"$ref": "#/responses/Hook"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/user/hooks/{id}": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"summary": "Get a hook",
|
||||||
|
"operationId": "userGetHook",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"description": "id of the hook to get",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/responses/Hook"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"summary": "Delete a hook",
|
||||||
|
"operationId": "userDeleteHook",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"description": "id of the hook to delete",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"$ref": "#/responses/empty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"summary": "Update a hook",
|
||||||
|
"operationId": "userEditHook",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"description": "id of the hook to update",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/EditHookOption"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/responses/Hook"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/user/keys": {
|
"/user/keys": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -22,15 +22,17 @@
|
|||||||
<a class="item" href="{{.ContextUser.HomeLink}}">
|
<a class="item" href="{{.ContextUser.HomeLink}}">
|
||||||
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
|
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
|
||||||
</a>
|
</a>
|
||||||
|
{{if and .IsProjectEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects))}}
|
||||||
<a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item">
|
<a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item">
|
||||||
{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
|
{{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}}
|
||||||
</a>
|
</a>
|
||||||
{{if (not .UnitPackagesGlobalDisabled)}}
|
{{end}}
|
||||||
|
{{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}}
|
||||||
<a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item">
|
<a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item">
|
||||||
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
|
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .IsRepoIndexerEnabled}}
|
{{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadCode))}}
|
||||||
<a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item">
|
<a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item">
|
||||||
{{svg "octicon-code"}} {{.locale.Tr "user.code"}}
|
{{svg "octicon-code"}} {{.locale.Tr "user.code"}}
|
||||||
</a>
|
</a>
|
||||||
|
@ -138,6 +138,12 @@
|
|||||||
<label>admin:org_hook</label>
|
<label>admin:org_hook</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input class="enable-system" type="checkbox" name="scope" value="admin:user_hook">
|
||||||
|
<label>admin:user_hook</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input class="enable-system" type="checkbox" name="scope" value="notification">
|
<input class="enable-system" type="checkbox" name="scope" value="notification">
|
||||||
|
53
templates/user/settings/hook_new.tmpl
Normal file
53
templates/user/settings/hook_new.tmpl
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
{{template "base/head" .}}
|
||||||
|
<div class="page-content user settings new webhook">
|
||||||
|
{{template "user/settings/navbar" .}}
|
||||||
|
<div class="ui container">
|
||||||
|
<div class="twelve wide column content">
|
||||||
|
{{template "base/alert" .}}
|
||||||
|
<h4 class="ui top attached header">
|
||||||
|
{{if .PageIsSettingsHooksNew}}{{.locale.Tr "repo.settings.add_webhook"}}{{else}}{{.locale.Tr "repo.settings.update_webhook"}}{{end}}
|
||||||
|
<div class="ui right">
|
||||||
|
{{if eq .HookType "gitea"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gitea.svg">
|
||||||
|
{{else if eq .HookType "gogs"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gogs.ico">
|
||||||
|
{{else if eq .HookType "slack"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/slack.png">
|
||||||
|
{{else if eq .HookType "discord"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/discord.png">
|
||||||
|
{{else if eq .HookType "dingtalk"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/dingtalk.ico">
|
||||||
|
{{else if eq .HookType "telegram"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/telegram.png">
|
||||||
|
{{else if eq .HookType "msteams"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/msteams.png">
|
||||||
|
{{else if eq .HookType "feishu"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/feishu.png">
|
||||||
|
{{else if eq .HookType "matrix"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/matrix.svg">
|
||||||
|
{{else if eq .HookType "wechatwork"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/wechatwork.png">
|
||||||
|
{{else if eq .HookType "packagist"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/packagist.png">
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</h4>
|
||||||
|
<div class="ui attached segment">
|
||||||
|
{{template "repo/settings/webhook/gitea" .}}
|
||||||
|
{{template "repo/settings/webhook/gogs" .}}
|
||||||
|
{{template "repo/settings/webhook/slack" .}}
|
||||||
|
{{template "repo/settings/webhook/discord" .}}
|
||||||
|
{{template "repo/settings/webhook/dingtalk" .}}
|
||||||
|
{{template "repo/settings/webhook/telegram" .}}
|
||||||
|
{{template "repo/settings/webhook/msteams" .}}
|
||||||
|
{{template "repo/settings/webhook/feishu" .}}
|
||||||
|
{{template "repo/settings/webhook/matrix" .}}
|
||||||
|
{{template "repo/settings/webhook/wechatwork" .}}
|
||||||
|
{{template "repo/settings/webhook/packagist" .}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{template "repo/settings/webhook/history" .}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "base/footer" .}}
|
8
templates/user/settings/hooks.tmpl
Normal file
8
templates/user/settings/hooks.tmpl
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{{template "base/head" .}}
|
||||||
|
<div class="page-content user settings webhooks">
|
||||||
|
{{template "user/settings/navbar" .}}
|
||||||
|
<div class="ui container">
|
||||||
|
{{template "repo/settings/webhook/list" .}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "base/footer" .}}
|
@ -26,6 +26,11 @@
|
|||||||
{{.locale.Tr "packages.title"}}
|
{{.locale.Tr "packages.title"}}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{if not DisableWebhooks}}
|
||||||
|
<a class="{{if .PageIsSettingsHooks}}active {{end}}item" href="{{AppSubUrl}}/user/settings/hooks">
|
||||||
|
{{.locale.Tr "repo.settings.hooks"}}
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
<a class="{{if .PageIsSettingsOrganization}}active {{end}}item" href="{{AppSubUrl}}/user/settings/organization">
|
<a class="{{if .PageIsSettingsOrganization}}active {{end}}item" href="{{AppSubUrl}}/user/settings/organization">
|
||||||
{{.locale.Tr "settings.organization"}}
|
{{.locale.Tr "settings.organization"}}
|
||||||
</a>
|
</a>
|
||||||
|
@ -55,28 +55,6 @@ LFS_START_SERVER = true
|
|||||||
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
|
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
|
||||||
SSH_TRUSTED_USER_CA_KEYS = ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCb4DC1dMFnJ6pXWo7GMxTchtzmJHYzfN6sZ9FAPFR4ijMLfGki+olvOMO5Fql1/yGnGfbELQa1S6y4shSvj/5K+zUFScmEXYf3Gcr87RqilLkyk16RS+cHNB1u87xTHbETaa3nyCJeGQRpd4IQ4NKob745mwDZ7jQBH8AZEng50Oh8y8fi8skBBBzaYp1ilgvzG740L7uex6fHV62myq0SXeCa+oJUjq326FU8y+Vsa32H8A3e7tOgXZPdt2TVNltx2S9H2WO8RMi7LfaSwARNfy1zu+bfR50r6ef8Yx5YKCMz4wWb1SHU1GS800mjOjlInLQORYRNMlSwR1+vLlVDciOqFapDSbj+YOVOawR0R1aqlSKpZkt33DuOBPx9qe6CVnIi7Z+Px/KqM+OLCzlLY/RS+LbxQpDWcfTVRiP+S5qRTcE3M3UioN/e0BE/1+MpX90IGpvVkA63ILYbKEa4bM3ASL7ChTCr6xN5XT+GpVJveFKK1cfNx9ExHI4rzYE=
|
SSH_TRUSTED_USER_CA_KEYS = ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCb4DC1dMFnJ6pXWo7GMxTchtzmJHYzfN6sZ9FAPFR4ijMLfGki+olvOMO5Fql1/yGnGfbELQa1S6y4shSvj/5K+zUFScmEXYf3Gcr87RqilLkyk16RS+cHNB1u87xTHbETaa3nyCJeGQRpd4IQ4NKob745mwDZ7jQBH8AZEng50Oh8y8fi8skBBBzaYp1ilgvzG740L7uex6fHV62myq0SXeCa+oJUjq326FU8y+Vsa32H8A3e7tOgXZPdt2TVNltx2S9H2WO8RMi7LfaSwARNfy1zu+bfR50r6ef8Yx5YKCMz4wWb1SHU1GS800mjOjlInLQORYRNMlSwR1+vLlVDciOqFapDSbj+YOVOawR0R1aqlSKpZkt33DuOBPx9qe6CVnIi7Z+Px/KqM+OLCzlLY/RS+LbxQpDWcfTVRiP+S5qRTcE3M3UioN/e0BE/1+MpX90IGpvVkA63ILYbKEa4bM3ASL7ChTCr6xN5XT+GpVJveFKK1cfNx9ExHI4rzYE=
|
||||||
|
|
||||||
[lfs]
|
|
||||||
MINIO_BASE_PATH = lfs/
|
|
||||||
|
|
||||||
[attachment]
|
|
||||||
MINIO_BASE_PATH = attachments/
|
|
||||||
|
|
||||||
[avatars]
|
|
||||||
MINIO_BASE_PATH = avatars/
|
|
||||||
|
|
||||||
[repo-avatars]
|
|
||||||
MINIO_BASE_PATH = repo-avatars/
|
|
||||||
|
|
||||||
[storage]
|
|
||||||
STORAGE_TYPE = minio
|
|
||||||
SERVE_DIRECT = false
|
|
||||||
MINIO_ENDPOINT = minio:9000
|
|
||||||
MINIO_ACCESS_KEY_ID = 123456
|
|
||||||
MINIO_SECRET_ACCESS_KEY = 12345678
|
|
||||||
MINIO_BUCKET = gitea
|
|
||||||
MINIO_LOCATION = us-east-1
|
|
||||||
MINIO_USE_SSL = false
|
|
||||||
|
|
||||||
[mailer]
|
[mailer]
|
||||||
ENABLED = true
|
ENABLED = true
|
||||||
MAILER_TYPE = dummy
|
MAILER_TYPE = dummy
|
||||||
@ -122,6 +100,9 @@ INSTALL_LOCK = true
|
|||||||
SECRET_KEY = 9pCviYTWSb
|
SECRET_KEY = 9pCviYTWSb
|
||||||
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ
|
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ
|
||||||
|
|
||||||
|
[lfs]
|
||||||
|
PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mysql/data/lfs
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
ENABLED = true
|
ENABLED = true
|
||||||
|
|
||||||
|
@ -105,7 +105,26 @@ SECRET_KEY = 9pCviYTWSb
|
|||||||
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ
|
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ
|
||||||
|
|
||||||
[lfs]
|
[lfs]
|
||||||
PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-pgsql/data/lfs
|
MINIO_BASE_PATH = lfs/
|
||||||
|
|
||||||
|
[attachment]
|
||||||
|
MINIO_BASE_PATH = attachments/
|
||||||
|
|
||||||
|
[avatars]
|
||||||
|
MINIO_BASE_PATH = avatars/
|
||||||
|
|
||||||
|
[repo-avatars]
|
||||||
|
MINIO_BASE_PATH = repo-avatars/
|
||||||
|
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
SERVE_DIRECT = false
|
||||||
|
MINIO_ENDPOINT = minio:9000
|
||||||
|
MINIO_ACCESS_KEY_ID = 123456
|
||||||
|
MINIO_SECRET_ACCESS_KEY = 12345678
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
MINIO_LOCATION = us-east-1
|
||||||
|
MINIO_USE_SSL = false
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
ENABLED = true
|
ENABLED = true
|
||||||
|
@ -3,7 +3,7 @@ import {attachTribute} from './tribute.js';
|
|||||||
import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js';
|
import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js';
|
||||||
import {initEasyMDEImagePaste} from './comp/ImagePaste.js';
|
import {initEasyMDEImagePaste} from './comp/ImagePaste.js';
|
||||||
import {createCommentEasyMDE} from './comp/EasyMDE.js';
|
import {createCommentEasyMDE} from './comp/EasyMDE.js';
|
||||||
import {hideElem} from '../utils/dom.js';
|
import {hideElem, showElem} from '../utils/dom.js';
|
||||||
|
|
||||||
export function initRepoRelease() {
|
export function initRepoRelease() {
|
||||||
$(document).on('click', '.remove-rel-attach', function() {
|
$(document).on('click', '.remove-rel-attach', function() {
|
||||||
@ -14,8 +14,43 @@ export function initRepoRelease() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function initRepoReleaseNew() {
|
||||||
|
const $repoReleaseNew = $('.repository.new.release');
|
||||||
|
if (!$repoReleaseNew.length) return;
|
||||||
|
|
||||||
export function initRepoReleaseEditor() {
|
initTagNameEditor();
|
||||||
|
initRepoReleaseEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTagNameEditor() {
|
||||||
|
const el = document.getElementById('tag-name-editor');
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
const existingTags = JSON.parse(el.getAttribute('data-existing-tags'));
|
||||||
|
if (!Array.isArray(existingTags)) return;
|
||||||
|
|
||||||
|
const defaultTagHelperText = el.getAttribute('data-tag-helper');
|
||||||
|
const newTagHelperText = el.getAttribute('data-tag-helper-new');
|
||||||
|
const existingTagHelperText = el.getAttribute('data-tag-helper-existing');
|
||||||
|
|
||||||
|
document.getElementById('tag-name').addEventListener('keyup', (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
if (existingTags.includes(value)) {
|
||||||
|
// If the tag already exists, hide the target branch selector.
|
||||||
|
hideElem('#tag-target-selector');
|
||||||
|
document.getElementById('tag-helper').innerText = existingTagHelperText;
|
||||||
|
} else {
|
||||||
|
showElem('#tag-target-selector');
|
||||||
|
if (value) {
|
||||||
|
document.getElementById('tag-helper').innerText = newTagHelperText;
|
||||||
|
} else {
|
||||||
|
document.getElementById('tag-helper').innerText = defaultTagHelperText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initRepoReleaseEditor() {
|
||||||
const $editor = $('.repository.new.release .content-editor');
|
const $editor = $('.repository.new.release .content-editor');
|
||||||
if ($editor.length === 0) {
|
if ($editor.length === 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -76,7 +76,7 @@ import {
|
|||||||
import {initViewedCheckboxListenerFor} from './features/pull-view-file.js';
|
import {initViewedCheckboxListenerFor} from './features/pull-view-file.js';
|
||||||
import {initOrgTeamSearchRepoBox, initOrgTeamSettings} from './features/org-team.js';
|
import {initOrgTeamSearchRepoBox, initOrgTeamSettings} from './features/org-team.js';
|
||||||
import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.js';
|
import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.js';
|
||||||
import {initRepoRelease, initRepoReleaseEditor} from './features/repo-release.js';
|
import {initRepoRelease, initRepoReleaseNew} from './features/repo-release.js';
|
||||||
import {initRepoEditor} from './features/repo-editor.js';
|
import {initRepoEditor} from './features/repo-editor.js';
|
||||||
import {initCompSearchUserBox} from './features/comp/SearchUserBox.js';
|
import {initCompSearchUserBox} from './features/comp/SearchUserBox.js';
|
||||||
import {initInstall} from './features/install.js';
|
import {initInstall} from './features/install.js';
|
||||||
@ -179,7 +179,7 @@ $(document).ready(() => {
|
|||||||
initRepoPullRequestAllowMaintainerEdit();
|
initRepoPullRequestAllowMaintainerEdit();
|
||||||
initRepoPullRequestReview();
|
initRepoPullRequestReview();
|
||||||
initRepoRelease();
|
initRepoRelease();
|
||||||
initRepoReleaseEditor();
|
initRepoReleaseNew();
|
||||||
initRepoSettingGitHook();
|
initRepoSettingGitHook();
|
||||||
initRepoSettingSearchTeamBox();
|
initRepoSettingSearchTeamBox();
|
||||||
initRepoSettingsCollaboration();
|
initRepoSettingsCollaboration();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user