mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-10-31 22:58:59 +01:00
Backport #23911 by @lunny Follow up #22405 Fix #20703 This PR rewrites storage configuration read sequences with some breaks and tests. It becomes more strict than before and also fixed some inherit problems. - Move storage's MinioConfig struct into setting, so after the configuration loading, the values will be stored into the struct but not still on some section. - All storages configurations should be stored on one section, configuration items cannot be overrided by multiple sections. The prioioty of configuration is `[attachment]` > `[storage.attachments]` | `[storage.customized]` > `[storage]` > `default` - For extra override configuration items, currently are `SERVE_DIRECT`, `MINIO_BASE_PATH`, `MINIO_BUCKET`, which could be configured in another section. The prioioty of the override configuration is `[attachment]` > `[storage.attachments]` > `default`. - Add more tests for storages configurations. - Update the storage documentations. Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
22948048b2
commit
21cd5c2f3d
41 changed files with 1152 additions and 452 deletions
|
@ -353,9 +353,9 @@ func runDump(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
excludes = append(excludes, setting.RepoRootPath)
|
excludes = append(excludes, setting.RepoRootPath)
|
||||||
excludes = append(excludes, setting.LFS.Path)
|
excludes = append(excludes, setting.LFS.Storage.Path)
|
||||||
excludes = append(excludes, setting.Attachment.Path)
|
excludes = append(excludes, setting.Attachment.Storage.Path)
|
||||||
excludes = append(excludes, setting.Packages.Path)
|
excludes = append(excludes, setting.Packages.Storage.Path)
|
||||||
excludes = append(excludes, setting.Log.RootPath)
|
excludes = append(excludes, setting.Log.RootPath)
|
||||||
excludes = append(excludes, absFileName)
|
excludes = append(excludes, absFileName)
|
||||||
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
|
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
|
||||||
|
|
|
@ -179,7 +179,7 @@ func runMigrateStorage(ctx *cli.Context) error {
|
||||||
switch strings.ToLower(ctx.String("storage")) {
|
switch strings.ToLower(ctx.String("storage")) {
|
||||||
case "":
|
case "":
|
||||||
fallthrough
|
fallthrough
|
||||||
case string(storage.LocalStorageType):
|
case string(setting.LocalStorageType):
|
||||||
p := ctx.String("path")
|
p := ctx.String("path")
|
||||||
if p == "" {
|
if p == "" {
|
||||||
log.Fatal("Path must be given when storage is loal")
|
log.Fatal("Path must be given when storage is loal")
|
||||||
|
@ -187,22 +187,24 @@ func runMigrateStorage(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
dstStorage, err = storage.NewLocalStorage(
|
dstStorage, err = storage.NewLocalStorage(
|
||||||
stdCtx,
|
stdCtx,
|
||||||
storage.LocalStorageConfig{
|
&setting.Storage{
|
||||||
Path: p,
|
Path: p,
|
||||||
})
|
})
|
||||||
case string(storage.MinioStorageType):
|
case string(setting.MinioStorageType):
|
||||||
dstStorage, err = storage.NewMinioStorage(
|
dstStorage, err = storage.NewMinioStorage(
|
||||||
stdCtx,
|
stdCtx,
|
||||||
storage.MinioStorageConfig{
|
&setting.Storage{
|
||||||
Endpoint: ctx.String("minio-endpoint"),
|
MinioConfig: setting.MinioStorageConfig{
|
||||||
AccessKeyID: ctx.String("minio-access-key-id"),
|
Endpoint: ctx.String("minio-endpoint"),
|
||||||
SecretAccessKey: ctx.String("minio-secret-access-key"),
|
AccessKeyID: ctx.String("minio-access-key-id"),
|
||||||
Bucket: ctx.String("minio-bucket"),
|
SecretAccessKey: ctx.String("minio-secret-access-key"),
|
||||||
Location: ctx.String("minio-location"),
|
Bucket: ctx.String("minio-bucket"),
|
||||||
BasePath: ctx.String("minio-base-path"),
|
Location: ctx.String("minio-location"),
|
||||||
UseSSL: ctx.Bool("minio-use-ssl"),
|
BasePath: ctx.String("minio-base-path"),
|
||||||
InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"),
|
UseSSL: ctx.Bool("minio-use-ssl"),
|
||||||
ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"),
|
InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"),
|
||||||
|
ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
|
return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
packages_module "code.gitea.io/gitea/modules/packages"
|
packages_module "code.gitea.io/gitea/modules/packages"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
packages_service "code.gitea.io/gitea/services/packages"
|
packages_service "code.gitea.io/gitea/services/packages"
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ func TestMigratePackages(t *testing.T) {
|
||||||
|
|
||||||
dstStorage, err := storage.NewLocalStorage(
|
dstStorage, err := storage.NewLocalStorage(
|
||||||
ctx,
|
ctx,
|
||||||
storage.LocalStorageConfig{
|
&setting.Storage{
|
||||||
Path: p,
|
Path: p,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -2392,6 +2392,10 @@ LEVEL = Info
|
||||||
;; Enable/Disable package registry capabilities
|
;; Enable/Disable package registry capabilities
|
||||||
;ENABLED = true
|
;ENABLED = true
|
||||||
;;
|
;;
|
||||||
|
;STORAGE_TYPE = local
|
||||||
|
;; override the minio base path if storage type is minio
|
||||||
|
;MINIO_BASE_PATH = packages/
|
||||||
|
;;
|
||||||
;; Path for chunked uploads. Defaults to APP_DATA_PATH + `tmp/package-upload`
|
;; Path for chunked uploads. Defaults to APP_DATA_PATH + `tmp/package-upload`
|
||||||
;CHUNKED_UPLOAD_PATH = tmp/package-upload
|
;CHUNKED_UPLOAD_PATH = tmp/package-upload
|
||||||
;;
|
;;
|
||||||
|
@ -2452,6 +2456,19 @@ LEVEL = Info
|
||||||
;; storage type
|
;; storage type
|
||||||
;STORAGE_TYPE = local
|
;STORAGE_TYPE = local
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; repo-archive storage will override storage
|
||||||
|
;;
|
||||||
|
;[repo-archive]
|
||||||
|
;STORAGE_TYPE = local
|
||||||
|
;;
|
||||||
|
;; Where your lfs files reside, default is data/lfs.
|
||||||
|
;PATH = data/repo-archive
|
||||||
|
;;
|
||||||
|
;; override the minio base path if storage type is minio
|
||||||
|
;MINIO_BASE_PATH = repo-archive/
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; settings for repository archives, will override storage setting
|
;; settings for repository archives, will override storage setting
|
||||||
|
@ -2471,6 +2488,9 @@ LEVEL = Info
|
||||||
;;
|
;;
|
||||||
;; Where your lfs files reside, default is data/lfs.
|
;; Where your lfs files reside, default is data/lfs.
|
||||||
;PATH = data/lfs
|
;PATH = data/lfs
|
||||||
|
;;
|
||||||
|
;; override the minio base path if storage type is minio
|
||||||
|
;MINIO_BASE_PATH = lfs/
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -2520,6 +2540,7 @@ LEVEL = Info
|
||||||
; [actions]
|
; [actions]
|
||||||
;; Enable/Disable actions capabilities
|
;; Enable/Disable actions capabilities
|
||||||
;ENABLED = false
|
;ENABLED = false
|
||||||
|
;;
|
||||||
;; Default address to get action plugins, e.g. the default value means downloading from "https://gitea.com/actions/checkout" for "uses: actions/checkout@v3"
|
;; Default address to get action plugins, e.g. the default value means downloading from "https://gitea.com/actions/checkout" for "uses: actions/checkout@v3"
|
||||||
;DEFAULT_ACTIONS_URL = https://gitea.com
|
;DEFAULT_ACTIONS_URL = https://gitea.com
|
||||||
|
|
||||||
|
|
|
@ -1254,8 +1254,9 @@ is `data/lfs` and the default of `MINIO_BASE_PATH` is `lfs/`.
|
||||||
|
|
||||||
## Storage (`storage`)
|
## Storage (`storage`)
|
||||||
|
|
||||||
Default storage configuration for attachments, lfs, avatars and etc.
|
Default storage configuration for attachments, lfs, avatars, repo-avatars, repo-archive, packages, actions_log, actions_artifact.
|
||||||
|
|
||||||
|
- `STORAGE_TYPE`: **local**: Storage type, `local` for local disk or `minio` for s3 compatible object storage service.
|
||||||
- `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing.
|
- `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing.
|
||||||
- `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when `STORAGE_TYPE` is `minio`
|
- `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when `STORAGE_TYPE` is `minio`
|
||||||
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio`
|
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio`
|
||||||
|
@ -1265,9 +1266,56 @@ Default storage configuration for attachments, lfs, avatars and etc.
|
||||||
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
|
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
|
||||||
- `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio`
|
- `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio`
|
||||||
|
|
||||||
And you can also define a customize storage like below:
|
The recommanded storage configuration for minio like below:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_ENDPOINT = localhost:9000
|
||||||
|
; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_ACCESS_KEY_ID =
|
||||||
|
; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_SECRET_ACCESS_KEY =
|
||||||
|
; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
; Minio location to create bucket only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_LOCATION = us-east-1
|
||||||
|
; Minio enabled ssl only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_USE_SSL = false
|
||||||
|
; Minio skip SSL verification available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_INSECURE_SKIP_VERIFY = false
|
||||||
|
SERVE_DIRECT = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Defaultly every storage has their default base path like below
|
||||||
|
|
||||||
|
| storage | default base path |
|
||||||
|
| ----------------- | ------------------ |
|
||||||
|
| attachments | attachments/ |
|
||||||
|
| lfs | lfs/ |
|
||||||
|
| avatars | avatars/ |
|
||||||
|
| repo-avatars | repo-avatars/ |
|
||||||
|
| repo-archive | repo-archive/ |
|
||||||
|
| packages | packages/ |
|
||||||
|
| actions_log | actions_log/ |
|
||||||
|
| actions_artifacts | actions_artifacts/ |
|
||||||
|
|
||||||
|
And bucket, basepath or `SERVE_DIRECT` could be special or overrided, if you want to use a different you can:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[storage.actions_log]
|
||||||
|
MINIO_BUCKET = gitea_actions_log
|
||||||
|
SERVE_DIRECT = true
|
||||||
|
MINIO_BASE_PATH = my_actions_log/ ; default is actions_log/ if blank
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to customerize a different storage for `lfs` if above default storage defined
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[lfs]
|
||||||
|
STORAGE_TYPE = my_minio
|
||||||
|
|
||||||
[storage.my_minio]
|
[storage.my_minio]
|
||||||
STORAGE_TYPE = minio
|
STORAGE_TYPE = minio
|
||||||
; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
|
; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
|
||||||
|
@ -1286,8 +1334,6 @@ MINIO_USE_SSL = false
|
||||||
MINIO_INSECURE_SKIP_VERIFY = false
|
MINIO_INSECURE_SKIP_VERIFY = false
|
||||||
```
|
```
|
||||||
|
|
||||||
And used by `[attachment]`, `[lfs]` and etc. as `STORAGE_TYPE`.
|
|
||||||
|
|
||||||
## Repository Archive Storage (`storage.repo-archive`)
|
## Repository Archive Storage (`storage.repo-archive`)
|
||||||
|
|
||||||
Configuration for repository archive storage. It will inherit from default `[storage]` or
|
Configuration for repository archive storage. It will inherit from default `[storage]` or
|
||||||
|
@ -1306,6 +1352,11 @@ is `data/repo-archive` and the default of `MINIO_BASE_PATH` is `repo-archive/`.
|
||||||
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
|
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
|
||||||
- `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio`
|
- `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio`
|
||||||
|
|
||||||
|
## Repository Archives (`repo-archive`)
|
||||||
|
|
||||||
|
- `STORAGE_TYPE`: **local**: Storage type for actions logs, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]`
|
||||||
|
- `MINIO_BASE_PATH`: **repo-archive/**: Minio base path on the bucket only available when STORAGE_TYPE is `minio`
|
||||||
|
|
||||||
## Proxy (`proxy`)
|
## Proxy (`proxy`)
|
||||||
|
|
||||||
- `PROXY_ENABLED`: **false**: Enable the proxy if true, all requests to external via HTTP will be affected, if false, no proxy will be used even environment http_proxy/https_proxy
|
- `PROXY_ENABLED`: **false**: Enable the proxy if true, all requests to external via HTTP will be affected, if false, no proxy will be used even environment http_proxy/https_proxy
|
||||||
|
@ -1324,6 +1375,8 @@ PROXY_HOSTS = *.github.com
|
||||||
|
|
||||||
- `ENABLED`: **false**: Enable/Disable actions capabilities
|
- `ENABLED`: **false**: Enable/Disable actions capabilities
|
||||||
- `DEFAULT_ACTIONS_URL`: **https://gitea.com**: Default address to get action plugins, e.g. the default value means downloading from "<https://gitea.com/actions/checkout>" for "uses: actions/checkout@v3"
|
- `DEFAULT_ACTIONS_URL`: **https://gitea.com**: Default address to get action plugins, e.g. the default value means downloading from "<https://gitea.com/actions/checkout>" for "uses: actions/checkout@v3"
|
||||||
|
- `STORAGE_TYPE`: **local**: Storage type for actions logs, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]`
|
||||||
|
- `MINIO_BASE_PATH`: **actions_log/**: Minio base path on the bucket only available when STORAGE_TYPE is `minio`
|
||||||
|
|
||||||
`DEFAULT_ACTIONS_URL` indicates where should we find the relative path action plugin. i.e. when use an action in a workflow file like
|
`DEFAULT_ACTIONS_URL` indicates where should we find the relative path action plugin. i.e. when use an action in a workflow file like
|
||||||
|
|
||||||
|
|
|
@ -414,7 +414,7 @@ LFS 的存储配置。 如果 `STORAGE_TYPE` 为空,则此配置将从 `[stora
|
||||||
|
|
||||||
## Storage (`storage`)
|
## Storage (`storage`)
|
||||||
|
|
||||||
Attachments, lfs, avatars and etc 的默认存储配置。
|
Attachments, lfs, avatars, repo-avatars, repo-archive, packages, actions_log, actions_artifact 的默认存储配置。
|
||||||
|
|
||||||
- `STORAGE_TYPE`: **local**: 附件存储类型,`local` 将存储到本地文件夹, `minio` 将存储到 s3 兼容的对象存储服务中。
|
- `STORAGE_TYPE`: **local**: 附件存储类型,`local` 将存储到本地文件夹, `minio` 将存储到 s3 兼容的对象存储服务中。
|
||||||
- `SERVE_DIRECT`: **false**: 允许直接重定向到存储系统。当前,仅 Minio/S3 是支持的。
|
- `SERVE_DIRECT`: **false**: 允许直接重定向到存储系统。当前,仅 Minio/S3 是支持的。
|
||||||
|
@ -425,9 +425,59 @@ Attachments, lfs, avatars and etc 的默认存储配置。
|
||||||
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket,仅当 `STORAGE_TYPE` 是 `minio` 时有效。
|
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket,仅当 `STORAGE_TYPE` 是 `minio` 时有效。
|
||||||
- `MINIO_USE_SSL`: **false**: Minio enabled ssl,仅当 `STORAGE_TYPE` 是 `minio` 时有效。
|
- `MINIO_USE_SSL`: **false**: Minio enabled ssl,仅当 `STORAGE_TYPE` 是 `minio` 时有效。
|
||||||
|
|
||||||
你也可以自定义一个存储的名字如下:
|
以下为推荐的 recommanded storage configuration for minio like below:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
; uncomment when STORAGE_TYPE = local
|
||||||
|
; PATH = storage root path
|
||||||
|
; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_ENDPOINT = localhost:9000
|
||||||
|
; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_ACCESS_KEY_ID =
|
||||||
|
; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_SECRET_ACCESS_KEY =
|
||||||
|
; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
; Minio location to create bucket only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_LOCATION = us-east-1
|
||||||
|
; Minio enabled ssl only available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_USE_SSL = false
|
||||||
|
; Minio skip SSL verification available when STORAGE_TYPE is `minio`
|
||||||
|
MINIO_INSECURE_SKIP_VERIFY = false
|
||||||
|
SERVE_DIRECT = true
|
||||||
|
```
|
||||||
|
|
||||||
|
默认的,每一个存储都会有各自默认的 BasePath 在同一个minio中,默认值如下:
|
||||||
|
|
||||||
|
| storage | default base path |
|
||||||
|
| ----------------- | ------------------ |
|
||||||
|
| attachments | attachments/ |
|
||||||
|
| lfs | lfs/ |
|
||||||
|
| avatars | avatars/ |
|
||||||
|
| repo-avatars | repo-avatars/ |
|
||||||
|
| repo-archive | repo-archive/ |
|
||||||
|
| packages | packages/ |
|
||||||
|
| actions_log | actions_log/ |
|
||||||
|
| actions_artifacts | actions_artifacts/ |
|
||||||
|
|
||||||
|
同时 bucket, basepath or `SERVE_DIRECT` 是可以被覆写的,像如下所示:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[storage.actions_log]
|
||||||
|
MINIO_BUCKET = gitea_actions_log
|
||||||
|
SERVE_DIRECT = true
|
||||||
|
MINIO_BASE_PATH = my_actions_log/ ; default is actions_log/ if blank
|
||||||
|
```
|
||||||
|
|
||||||
|
当然你也可以完全自定义,像如下
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[lfs]
|
||||||
|
STORAGE_TYPE = my_minio
|
||||||
|
MINIO_BASE_PATH = my_lfs_basepath
|
||||||
|
|
||||||
[storage.my_minio]
|
[storage.my_minio]
|
||||||
STORAGE_TYPE = minio
|
STORAGE_TYPE = minio
|
||||||
; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
|
; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
|
||||||
|
@ -444,10 +494,9 @@ MINIO_LOCATION = us-east-1
|
||||||
MINIO_USE_SSL = false
|
MINIO_USE_SSL = false
|
||||||
; Minio skip SSL verification available when STORAGE_TYPE is `minio`
|
; Minio skip SSL verification available when STORAGE_TYPE is `minio`
|
||||||
MINIO_INSECURE_SKIP_VERIFY = false
|
MINIO_INSECURE_SKIP_VERIFY = false
|
||||||
|
SERVE_DIRECT = true
|
||||||
```
|
```
|
||||||
|
|
||||||
然后你在 `[attachment]`, `[lfs]` 等中可以把这个名字用作 `STORAGE_TYPE` 的值。
|
|
||||||
|
|
||||||
## Repository Archive Storage (`storage.repo-archive`)
|
## Repository Archive Storage (`storage.repo-archive`)
|
||||||
|
|
||||||
Repository archive 的存储配置。 如果 `STORAGE_TYPE` 为空,则此配置将从 `[storage]` 继承。如果不为 `local` 或者 `minio` 而为 `xxx`, 则从 `[storage.xxx]` 继承。当继承时, `PATH` 默认为 `data/repo-archive`,`MINIO_BASE_PATH` 默认为 `repo-archive/`。
|
Repository archive 的存储配置。 如果 `STORAGE_TYPE` 为空,则此配置将从 `[storage]` 继承。如果不为 `local` 或者 `minio` 而为 `xxx`, 则从 `[storage.xxx]` 继承。当继承时, `PATH` 默认为 `data/repo-archive`,`MINIO_BASE_PATH` 默认为 `repo-archive/`。
|
||||||
|
|
|
@ -53,7 +53,7 @@ func DeleteOrphanedAttachments(x *xorm.Engine) error {
|
||||||
|
|
||||||
for _, attachment := range attachments {
|
for _, attachment := range attachments {
|
||||||
uuid := attachment.UUID
|
uuid := attachment.UUID
|
||||||
if err := util.RemoveAll(filepath.Join(setting.Attachment.Path, uuid[0:1], uuid[1:2], uuid)); err != nil {
|
if err := util.RemoveAll(filepath.Join(setting.Attachment.Storage.Path, uuid[0:1], uuid[1:2], uuid)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ func RemoveAttachmentMissedRepo(x *xorm.Engine) error {
|
||||||
|
|
||||||
for i := 0; i < len(attachments); i++ {
|
for i := 0; i < len(attachments); i++ {
|
||||||
uuid := attachments[i].UUID
|
uuid := attachments[i].UUID
|
||||||
if err = util.RemoveAll(filepath.Join(setting.Attachment.Path, uuid[0:1], uuid[1:2], uuid)); err != nil {
|
if err = util.RemoveAll(filepath.Join(setting.Attachment.Storage.Path, uuid[0:1], uuid[1:2], uuid)); err != nil {
|
||||||
fmt.Printf("Error: %v", err) //nolint:forbidigo
|
fmt.Printf("Error: %v", err) //nolint:forbidigo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func RenameExistingUserAvatarName(x *xorm.Engine) error {
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
oldAvatar := user.Avatar
|
oldAvatar := user.Avatar
|
||||||
|
|
||||||
if stat, err := os.Stat(filepath.Join(setting.Avatar.Path, oldAvatar)); err != nil || !stat.Mode().IsRegular() {
|
if stat, err := os.Stat(filepath.Join(setting.Avatar.Storage.Path, oldAvatar)); err != nil || !stat.Mode().IsRegular() {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = fmt.Errorf("Error: \"%s\" is not a regular file", oldAvatar)
|
err = fmt.Errorf("Error: \"%s\" is not a regular file", oldAvatar)
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func RenameExistingUserAvatarName(x *xorm.Engine) error {
|
||||||
return fmt.Errorf("[user: %s] user table update: %w", user.LowerName, err)
|
return fmt.Errorf("[user: %s] user table update: %w", user.LowerName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteList.Add(filepath.Join(setting.Avatar.Path, oldAvatar))
|
deleteList.Add(filepath.Join(setting.Avatar.Storage.Path, oldAvatar))
|
||||||
migrated++
|
migrated++
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
|
@ -135,7 +135,7 @@ func RenameExistingUserAvatarName(x *xorm.Engine) error {
|
||||||
// copyOldAvatarToNewLocation copies oldAvatar to newAvatarLocation
|
// copyOldAvatarToNewLocation copies oldAvatar to newAvatarLocation
|
||||||
// and returns newAvatar location
|
// and returns newAvatar location
|
||||||
func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error) {
|
func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error) {
|
||||||
fr, err := os.Open(filepath.Join(setting.Avatar.Path, oldAvatar))
|
fr, err := os.Open(filepath.Join(setting.Avatar.Storage.Path, oldAvatar))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("os.Open: %w", err)
|
return "", fmt.Errorf("os.Open: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error)
|
||||||
return newAvatar, nil
|
return newAvatar, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(filepath.Join(setting.Avatar.Path, newAvatar), data, 0o666); err != nil {
|
if err := os.WriteFile(filepath.Join(setting.Avatar.Storage.Path, newAvatar), data, 0o666); err != nil {
|
||||||
return "", fmt.Errorf("os.WriteFile: %w", err)
|
return "", fmt.Errorf("os.WriteFile: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Actions settings
|
// Actions settings
|
||||||
var (
|
var (
|
||||||
Actions = struct {
|
Actions = struct {
|
||||||
LogStorage Storage // how the created logs should be stored
|
LogStorage *Storage // how the created logs should be stored
|
||||||
ArtifactStorage Storage // how the created artifacts should be stored
|
ArtifactStorage *Storage // how the created artifacts should be stored
|
||||||
Enabled bool
|
Enabled bool
|
||||||
DefaultActionsURL string `ini:"DEFAULT_ACTIONS_URL"`
|
DefaultActionsURL string `ini:"DEFAULT_ACTIONS_URL"`
|
||||||
}{
|
}{
|
||||||
|
@ -20,15 +20,22 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadActionsFrom(rootCfg ConfigProvider) {
|
func loadActionsFrom(rootCfg ConfigProvider) error {
|
||||||
sec := rootCfg.Section("actions")
|
sec := rootCfg.Section("actions")
|
||||||
if err := sec.MapTo(&Actions); err != nil {
|
err := sec.MapTo(&Actions)
|
||||||
log.Fatal("Failed to map Actions settings: %v", err)
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to map Actions settings: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actionsSec := rootCfg.Section("actions.artifacts")
|
// don't support to read configuration from [actions]
|
||||||
storageType := actionsSec.Key("STORAGE_TYPE").MustString("")
|
Actions.LogStorage, err = getStorage(rootCfg, "actions_log", "", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
Actions.LogStorage = getStorage(rootCfg, "actions_log", "", nil)
|
actionsSec, _ := rootCfg.GetSection("actions.artifacts")
|
||||||
Actions.ArtifactStorage = getStorage(rootCfg, "actions_artifacts", storageType, actionsSec)
|
|
||||||
|
Actions.ArtifactStorage, err = getStorage(rootCfg, "actions_artifacts", "", actionsSec)
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
97
modules/setting/actions_test.go
Normal file
97
modules/setting/actions_test.go
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_getStorageInheritNameSectionTypeForActions(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadActionsFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Actions.LogStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
|
||||||
|
assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
iniStr = `
|
||||||
|
[storage.actions_log]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadActionsFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Actions.LogStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
|
||||||
|
assert.EqualValues(t, "local", Actions.ArtifactStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path))
|
||||||
|
|
||||||
|
iniStr = `
|
||||||
|
[storage.actions_log]
|
||||||
|
STORAGE_TYPE = my_storage
|
||||||
|
|
||||||
|
[storage.my_storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadActionsFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Actions.LogStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
|
||||||
|
assert.EqualValues(t, "local", Actions.ArtifactStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path))
|
||||||
|
|
||||||
|
iniStr = `
|
||||||
|
[storage.actions_artifacts]
|
||||||
|
STORAGE_TYPE = my_storage
|
||||||
|
|
||||||
|
[storage.my_storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadActionsFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "local", Actions.LogStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path))
|
||||||
|
assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
iniStr = `
|
||||||
|
[storage.actions_artifacts]
|
||||||
|
STORAGE_TYPE = my_storage
|
||||||
|
|
||||||
|
[storage.my_storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadActionsFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "local", Actions.LogStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path))
|
||||||
|
assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
iniStr = ``
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadActionsFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "local", Actions.LogStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path))
|
||||||
|
assert.EqualValues(t, "local", Actions.ArtifactStorage.Type)
|
||||||
|
assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path))
|
||||||
|
}
|
|
@ -5,29 +5,31 @@ package setting
|
||||||
|
|
||||||
// Attachment settings
|
// Attachment settings
|
||||||
var Attachment = struct {
|
var Attachment = struct {
|
||||||
Storage
|
Storage *Storage
|
||||||
AllowedTypes string
|
AllowedTypes string
|
||||||
MaxSize int64
|
MaxSize int64
|
||||||
MaxFiles int
|
MaxFiles int
|
||||||
Enabled bool
|
Enabled bool
|
||||||
}{
|
}{
|
||||||
Storage: Storage{
|
Storage: &Storage{},
|
||||||
ServeDirect: false,
|
AllowedTypes: ".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip",
|
||||||
},
|
|
||||||
AllowedTypes: "image/jpeg,image/png,application/zip,application/gzip",
|
|
||||||
MaxSize: 4,
|
MaxSize: 4,
|
||||||
MaxFiles: 5,
|
MaxFiles: 5,
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadAttachmentFrom(rootCfg ConfigProvider) {
|
func loadAttachmentFrom(rootCfg ConfigProvider) (err error) {
|
||||||
sec := rootCfg.Section("attachment")
|
sec, _ := rootCfg.GetSection("attachment")
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
if sec == nil {
|
||||||
|
Attachment.Storage, err = getStorage(rootCfg, "attachments", "", nil)
|
||||||
Attachment.Storage = getStorage(rootCfg, "attachments", storageType, sec)
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip")
|
Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip")
|
||||||
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4)
|
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4)
|
||||||
Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5)
|
Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5)
|
||||||
Attachment.Enabled = sec.Key("ENABLED").MustBool(true)
|
Attachment.Enabled = sec.Key("ENABLED").MustBool(true)
|
||||||
|
|
||||||
|
Attachment.Storage, err = getStorage(rootCfg, "attachments", "", sec)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
133
modules/setting/attachment_test.go
Normal file
133
modules/setting/attachment_test.go
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_getStorageCustomType(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
[attachment]
|
||||||
|
STORAGE_TYPE = my_minio
|
||||||
|
MINIO_BUCKET = gitea-attachment
|
||||||
|
|
||||||
|
[storage.my_minio]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
MINIO_ENDPOINT = my_minio:9000
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadAttachmentFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Attachment.Storage.Type)
|
||||||
|
assert.EqualValues(t, "my_minio:9000", Attachment.Storage.MinioConfig.Endpoint)
|
||||||
|
assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getStorageTypeSectionOverridesStorageSection(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
[attachment]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
|
||||||
|
[storage.minio]
|
||||||
|
MINIO_BUCKET = gitea-minio
|
||||||
|
|
||||||
|
[storage]
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadAttachmentFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Attachment.Storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea-minio", Attachment.Storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getStorageSpecificOverridesStorage(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
[attachment]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
MINIO_BUCKET = gitea-attachment
|
||||||
|
|
||||||
|
[storage.attachments]
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = local
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadAttachmentFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Attachment.Storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getStorageGetDefaults(t *testing.T) {
|
||||||
|
cfg, err := NewConfigProviderFromData("")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadAttachmentFrom(cfg))
|
||||||
|
|
||||||
|
// default storage is local, so bucket is empty
|
||||||
|
assert.EqualValues(t, "", Attachment.Storage.MinioConfig.Bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getStorageInheritNameSectionType(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
[storage.attachments]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadAttachmentFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Attachment.Storage.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_AttachmentStorage(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
MINIO_ENDPOINT = s3.my-domain.net
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
MINIO_LOCATION = homenet
|
||||||
|
MINIO_USE_SSL = true
|
||||||
|
MINIO_ACCESS_KEY_ID = correct_key
|
||||||
|
MINIO_SECRET_ACCESS_KEY = correct_key
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadAttachmentFrom(cfg))
|
||||||
|
storage := Attachment.Storage
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_AttachmentStorage1(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadAttachmentFrom(cfg))
|
||||||
|
assert.EqualValues(t, "minio", Attachment.Storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", Attachment.Storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -95,6 +96,18 @@ func ConfigSectionKeyString(sec ConfigSection, key string, def ...string) string
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConfigSectionKeyBool(sec ConfigSection, key string, def ...bool) bool {
|
||||||
|
k := ConfigSectionKey(sec, key)
|
||||||
|
if k != nil && k.String() != "" {
|
||||||
|
b, _ := strconv.ParseBool(k.String())
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
if len(def) > 0 {
|
||||||
|
return def[0]
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ConfigInheritedKey works like ini.Section.Key(), but it always returns a new key instance, it is O(n) because NewKey is O(n)
|
// ConfigInheritedKey works like ini.Section.Key(), but it always returns a new key instance, it is O(n) because NewKey is O(n)
|
||||||
// and the returned key is safe to be used with "MustXxx", it doesn't change the parent's values.
|
// and the returned key is safe to be used with "MustXxx", it doesn't change the parent's values.
|
||||||
// Otherwise, ini.Section.Key().MustXxx would pollute the parent section's keys.
|
// Otherwise, ini.Section.Key().MustXxx would pollute the parent section's keys.
|
||||||
|
@ -287,6 +300,12 @@ func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deprecatedSettingFatal(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
|
||||||
|
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||||
|
log.Fatal("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
|
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
|
||||||
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
|
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
|
||||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||||
|
|
|
@ -5,10 +5,10 @@ package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/generate"
|
"code.gitea.io/gitea/modules/generate"
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LFS represents the configuration for Git LFS
|
// LFS represents the configuration for Git LFS
|
||||||
|
@ -20,25 +20,27 @@ var LFS = struct {
|
||||||
MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"`
|
MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"`
|
||||||
LocksPagingNum int `ini:"LFS_LOCKS_PAGING_NUM"`
|
LocksPagingNum int `ini:"LFS_LOCKS_PAGING_NUM"`
|
||||||
|
|
||||||
Storage
|
Storage *Storage
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
func loadLFSFrom(rootCfg ConfigProvider) {
|
func loadLFSFrom(rootCfg ConfigProvider) error {
|
||||||
sec := rootCfg.Section("server")
|
sec := rootCfg.Section("server")
|
||||||
if err := sec.MapTo(&LFS); err != nil {
|
if err := sec.MapTo(&LFS); err != nil {
|
||||||
log.Fatal("Failed to map LFS settings: %v", err)
|
return fmt.Errorf("failed to map LFS settings: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
lfsSec := rootCfg.Section("lfs")
|
lfsSec, _ := rootCfg.GetSection("lfs")
|
||||||
storageType := lfsSec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
|
|
||||||
// Specifically default PATH to LFS_CONTENT_PATH
|
// Specifically default PATH to LFS_CONTENT_PATH
|
||||||
// DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
|
// DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
|
||||||
// if these are removed, the warning will not be shown
|
// if these are removed, the warning will not be shown
|
||||||
deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0")
|
deprecatedSettingFatal(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0")
|
||||||
lfsSec.Key("PATH").MustString(sec.Key("LFS_CONTENT_PATH").String())
|
|
||||||
|
|
||||||
LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec)
|
var err error
|
||||||
|
LFS.Storage, err = getStorage(rootCfg, "lfs", "", lfsSec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Rest of LFS service settings
|
// Rest of LFS service settings
|
||||||
if LFS.LocksPagingNum == 0 {
|
if LFS.LocksPagingNum == 0 {
|
||||||
|
@ -47,23 +49,25 @@ func loadLFSFrom(rootCfg ConfigProvider) {
|
||||||
|
|
||||||
LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(24 * time.Hour)
|
LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(24 * time.Hour)
|
||||||
|
|
||||||
if LFS.StartServer {
|
if !LFS.StartServer {
|
||||||
LFS.JWTSecretBytes = make([]byte, 32)
|
return nil
|
||||||
n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64))
|
}
|
||||||
|
|
||||||
if err != nil || n != 32 {
|
LFS.JWTSecretBytes = make([]byte, 32)
|
||||||
LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64()
|
n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64))
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error generating JWT Secret for custom config: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save secret
|
if err != nil || n != 32 {
|
||||||
sec.Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
|
LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64()
|
||||||
if err := rootCfg.Save(); err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error saving JWT Secret for custom config: %v", err)
|
return fmt.Errorf("Error generating JWT Secret for custom config: %v", err)
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
// Save secret
|
||||||
|
sec.Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
|
||||||
|
if err := rootCfg.Save(); err != nil {
|
||||||
|
return fmt.Errorf("Error saving JWT Secret for custom config: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
77
modules/setting/lfs_test.go
Normal file
77
modules/setting/lfs_test.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_getStorageInheritNameSectionTypeForLFS(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadLFSFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", LFS.Storage.Type)
|
||||||
|
assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
iniStr = `
|
||||||
|
[storage.lfs]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadLFSFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", LFS.Storage.Type)
|
||||||
|
assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
iniStr = `
|
||||||
|
[lfs]
|
||||||
|
STORAGE_TYPE = my_minio
|
||||||
|
|
||||||
|
[storage.my_minio]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadLFSFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", LFS.Storage.Type)
|
||||||
|
assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
iniStr = `
|
||||||
|
[lfs]
|
||||||
|
STORAGE_TYPE = my_minio
|
||||||
|
MINIO_BASE_PATH = my_lfs/
|
||||||
|
|
||||||
|
[storage.my_minio]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadLFSFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", LFS.Storage.Type)
|
||||||
|
assert.EqualValues(t, "my_lfs/", LFS.Storage.MinioConfig.BasePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_LFSStorage1(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadLFSFrom(cfg))
|
||||||
|
assert.EqualValues(t, "minio", LFS.Storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", LFS.Storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
|
||||||
|
}
|
|
@ -4,20 +4,19 @@
|
||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Package registry settings
|
// Package registry settings
|
||||||
var (
|
var (
|
||||||
Packages = struct {
|
Packages = struct {
|
||||||
Storage
|
Storage *Storage
|
||||||
Enabled bool
|
Enabled bool
|
||||||
ChunkedUploadPath string
|
ChunkedUploadPath string
|
||||||
RegistryHost string
|
RegistryHost string
|
||||||
|
@ -51,13 +50,21 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadPackagesFrom(rootCfg ConfigProvider) {
|
func loadPackagesFrom(rootCfg ConfigProvider) (err error) {
|
||||||
sec := rootCfg.Section("packages")
|
sec, _ := rootCfg.GetSection("packages")
|
||||||
if err := sec.MapTo(&Packages); err != nil {
|
if sec == nil {
|
||||||
log.Fatal("Failed to map Packages settings: %v", err)
|
Packages.Storage, err = getStorage(rootCfg, "packages", "", nil)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
Packages.Storage = getStorage(rootCfg, "packages", "", nil)
|
if err = sec.MapTo(&Packages); err != nil {
|
||||||
|
return fmt.Errorf("failed to map Packages settings: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Packages.Storage, err = getStorage(rootCfg, "packages", "", sec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
appURL, _ := url.Parse(AppURL)
|
appURL, _ := url.Parse(AppURL)
|
||||||
Packages.RegistryHost = appURL.Host
|
Packages.RegistryHost = appURL.Host
|
||||||
|
@ -68,7 +75,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(Packages.ChunkedUploadPath, os.ModePerm); err != nil {
|
if err := os.MkdirAll(Packages.ChunkedUploadPath, os.ModePerm); err != nil {
|
||||||
log.Error("Unable to create chunked upload directory: %s (%v)", Packages.ChunkedUploadPath, err)
|
return fmt.Errorf("unable to create chunked upload directory: %s (%v)", Packages.ChunkedUploadPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
Packages.LimitTotalOwnerSize = mustBytes(sec, "LIMIT_TOTAL_OWNER_SIZE")
|
Packages.LimitTotalOwnerSize = mustBytes(sec, "LIMIT_TOTAL_OWNER_SIZE")
|
||||||
|
@ -93,6 +100,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
|
||||||
Packages.LimitSizeRubyGems = mustBytes(sec, "LIMIT_SIZE_RUBYGEMS")
|
Packages.LimitSizeRubyGems = mustBytes(sec, "LIMIT_SIZE_RUBYGEMS")
|
||||||
Packages.LimitSizeSwift = mustBytes(sec, "LIMIT_SIZE_SWIFT")
|
Packages.LimitSizeSwift = mustBytes(sec, "LIMIT_SIZE_SWIFT")
|
||||||
Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT")
|
Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustBytes(section ConfigSection, key string) int64 {
|
func mustBytes(section ConfigSection, key string) int64 {
|
||||||
|
|
|
@ -29,3 +29,170 @@ func TestMustBytes(t *testing.T) {
|
||||||
assert.EqualValues(t, 1782579, test("1.7mib"))
|
assert.EqualValues(t, 1782579, test("1.7mib"))
|
||||||
assert.EqualValues(t, -1, test("1 yib")) // too large
|
assert.EqualValues(t, -1, test("1 yib")) // too large
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_getStorageInheritNameSectionTypeForPackages(t *testing.T) {
|
||||||
|
// packages storage inherits from storage if nothing configured
|
||||||
|
iniStr := `
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadPackagesFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Packages.Storage.Type)
|
||||||
|
assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
// we can also configure packages storage directly
|
||||||
|
iniStr = `
|
||||||
|
[storage.packages]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadPackagesFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Packages.Storage.Type)
|
||||||
|
assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
// or we can indicate the storage type in the packages section
|
||||||
|
iniStr = `
|
||||||
|
[packages]
|
||||||
|
STORAGE_TYPE = my_minio
|
||||||
|
|
||||||
|
[storage.my_minio]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadPackagesFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Packages.Storage.Type)
|
||||||
|
assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
// or we can indicate the storage type and minio base path in the packages section
|
||||||
|
iniStr = `
|
||||||
|
[packages]
|
||||||
|
STORAGE_TYPE = my_minio
|
||||||
|
MINIO_BASE_PATH = my_packages/
|
||||||
|
|
||||||
|
[storage.my_minio]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadPackagesFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", Packages.Storage.Type)
|
||||||
|
assert.EqualValues(t, "my_packages/", Packages.Storage.MinioConfig.BasePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_PackageStorage1(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
[packages]
|
||||||
|
MINIO_BASE_PATH = packages/
|
||||||
|
SERVE_DIRECT = true
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
MINIO_ENDPOINT = s3.my-domain.net
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
MINIO_LOCATION = homenet
|
||||||
|
MINIO_USE_SSL = true
|
||||||
|
MINIO_ACCESS_KEY_ID = correct_key
|
||||||
|
MINIO_SECRET_ACCESS_KEY = correct_key
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadPackagesFrom(cfg))
|
||||||
|
storage := Packages.Storage
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath)
|
||||||
|
assert.True(t, storage.MinioConfig.ServeDirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_PackageStorage2(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
[storage.packages]
|
||||||
|
MINIO_BASE_PATH = packages/
|
||||||
|
SERVE_DIRECT = true
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
MINIO_ENDPOINT = s3.my-domain.net
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
MINIO_LOCATION = homenet
|
||||||
|
MINIO_USE_SSL = true
|
||||||
|
MINIO_ACCESS_KEY_ID = correct_key
|
||||||
|
MINIO_SECRET_ACCESS_KEY = correct_key
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadPackagesFrom(cfg))
|
||||||
|
storage := Packages.Storage
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath)
|
||||||
|
assert.True(t, storage.MinioConfig.ServeDirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_PackageStorage3(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
[packages]
|
||||||
|
STORAGE_TYPE = my_cfg
|
||||||
|
MINIO_BASE_PATH = my_packages/
|
||||||
|
SERVE_DIRECT = true
|
||||||
|
[storage.my_cfg]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
MINIO_ENDPOINT = s3.my-domain.net
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
MINIO_LOCATION = homenet
|
||||||
|
MINIO_USE_SSL = true
|
||||||
|
MINIO_ACCESS_KEY_ID = correct_key
|
||||||
|
MINIO_SECRET_ACCESS_KEY = correct_key
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadPackagesFrom(cfg))
|
||||||
|
storage := Packages.Storage
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath)
|
||||||
|
assert.True(t, storage.MinioConfig.ServeDirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_PackageStorage4(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
[storage.packages]
|
||||||
|
STORAGE_TYPE = my_cfg
|
||||||
|
MINIO_BASE_PATH = my_packages/
|
||||||
|
SERVE_DIRECT = true
|
||||||
|
[storage.my_cfg]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
MINIO_ENDPOINT = s3.my-domain.net
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
MINIO_LOCATION = homenet
|
||||||
|
MINIO_USE_SSL = true
|
||||||
|
MINIO_ACCESS_KEY_ID = correct_key
|
||||||
|
MINIO_SECRET_ACCESS_KEY = correct_key
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadPackagesFrom(cfg))
|
||||||
|
storage := Packages.Storage
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath)
|
||||||
|
assert.True(t, storage.MinioConfig.ServeDirect)
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ package setting
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Avatar = struct {
|
Avatar = struct {
|
||||||
Storage
|
Storage *Storage
|
||||||
|
|
||||||
MaxWidth int
|
MaxWidth int
|
||||||
MaxHeight int
|
MaxHeight int
|
||||||
|
@ -27,23 +27,26 @@ var (
|
||||||
EnableFederatedAvatar bool // Depreciated: migrated to database
|
EnableFederatedAvatar bool // Depreciated: migrated to database
|
||||||
|
|
||||||
RepoAvatar = struct {
|
RepoAvatar = struct {
|
||||||
Storage
|
Storage *Storage
|
||||||
|
|
||||||
Fallback string
|
Fallback string
|
||||||
FallbackImage string
|
FallbackImage string
|
||||||
}{}
|
}{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadPictureFrom(rootCfg ConfigProvider) {
|
func loadAvatarsFrom(rootCfg ConfigProvider) error {
|
||||||
sec := rootCfg.Section("picture")
|
sec := rootCfg.Section("picture")
|
||||||
|
|
||||||
avatarSec := rootCfg.Section("avatar")
|
avatarSec := rootCfg.Section("avatar")
|
||||||
storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("")
|
storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("")
|
||||||
// Specifically default PATH to AVATAR_UPLOAD_PATH
|
// Specifically default PATH to AVATAR_UPLOAD_PATH
|
||||||
avatarSec.Key("PATH").MustString(
|
avatarSec.Key("PATH").MustString(sec.Key("AVATAR_UPLOAD_PATH").String())
|
||||||
sec.Key("AVATAR_UPLOAD_PATH").String())
|
|
||||||
|
|
||||||
Avatar.Storage = getStorage(rootCfg, "avatars", storageType, avatarSec)
|
var err error
|
||||||
|
Avatar.Storage, err = getStorage(rootCfg, "avatars", storageType, avatarSec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
Avatar.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096)
|
Avatar.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096)
|
||||||
Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(4096)
|
Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(4096)
|
||||||
|
@ -67,7 +70,7 @@ func loadPictureFrom(rootCfg ConfigProvider) {
|
||||||
EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar))
|
EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar))
|
||||||
deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR")
|
deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR")
|
||||||
|
|
||||||
loadRepoAvatarFrom(rootCfg)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDefaultDisableGravatar() bool {
|
func GetDefaultDisableGravatar() bool {
|
||||||
|
@ -85,17 +88,22 @@ func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadRepoAvatarFrom(rootCfg ConfigProvider) {
|
func loadRepoAvatarFrom(rootCfg ConfigProvider) error {
|
||||||
sec := rootCfg.Section("picture")
|
sec := rootCfg.Section("picture")
|
||||||
|
|
||||||
repoAvatarSec := rootCfg.Section("repo-avatar")
|
repoAvatarSec := rootCfg.Section("repo-avatar")
|
||||||
storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("")
|
storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("")
|
||||||
// Specifically default PATH to AVATAR_UPLOAD_PATH
|
// Specifically default PATH to AVATAR_UPLOAD_PATH
|
||||||
repoAvatarSec.Key("PATH").MustString(
|
repoAvatarSec.Key("PATH").MustString(sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String())
|
||||||
sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String())
|
|
||||||
|
|
||||||
RepoAvatar.Storage = getStorage(rootCfg, "repo-avatars", storageType, repoAvatarSec)
|
var err error
|
||||||
|
RepoAvatar.Storage, err = getStorage(rootCfg, "repo-avatars", storageType, repoAvatarSec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
RepoAvatar.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none")
|
RepoAvatar.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none")
|
||||||
RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString(AppSubURL + "/assets/img/repo_default.png")
|
RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString(AppSubURL + "/assets/img/repo_default.png")
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,10 +265,6 @@ var (
|
||||||
}
|
}
|
||||||
RepoRootPath string
|
RepoRootPath string
|
||||||
ScriptType = "bash"
|
ScriptType = "bash"
|
||||||
|
|
||||||
RepoArchive = struct {
|
|
||||||
Storage
|
|
||||||
}{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadRepositoryFrom(rootCfg ConfigProvider) {
|
func loadRepositoryFrom(rootCfg ConfigProvider) {
|
||||||
|
@ -359,5 +355,7 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
|
||||||
Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath)
|
Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
RepoArchive.Storage = getStorage(rootCfg, "repo-archive", "", nil)
|
if err := loadRepoArchiveFrom(rootCfg); err != nil {
|
||||||
|
log.Fatal("loadRepoArchiveFrom: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
25
modules/setting/repository_archive.go
Normal file
25
modules/setting/repository_archive.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
var RepoArchive = struct {
|
||||||
|
Storage *Storage
|
||||||
|
}{}
|
||||||
|
|
||||||
|
func loadRepoArchiveFrom(rootCfg ConfigProvider) (err error) {
|
||||||
|
sec, _ := rootCfg.GetSection("repo-archive")
|
||||||
|
if sec == nil {
|
||||||
|
RepoArchive.Storage, err = getStorage(rootCfg, "repo-archive", "", nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sec.MapTo(&RepoArchive); err != nil {
|
||||||
|
return fmt.Errorf("mapto repoarchive failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
RepoArchive.Storage, err = getStorage(rootCfg, "repo-archive", "", sec)
|
||||||
|
return err
|
||||||
|
}
|
111
modules/setting/repository_archive_test.go
Normal file
111
modules/setting/repository_archive_test.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_getStorageInheritNameSectionTypeForRepoArchive(t *testing.T) {
|
||||||
|
// packages storage inherits from storage if nothing configured
|
||||||
|
iniStr := `
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadRepoArchiveFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
|
||||||
|
assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
// we can also configure packages storage directly
|
||||||
|
iniStr = `
|
||||||
|
[storage.repo-archive]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadRepoArchiveFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
|
||||||
|
assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
// or we can indicate the storage type in the packages section
|
||||||
|
iniStr = `
|
||||||
|
[repo-archive]
|
||||||
|
STORAGE_TYPE = my_minio
|
||||||
|
|
||||||
|
[storage.my_minio]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadRepoArchiveFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
|
||||||
|
assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
// or we can indicate the storage type and minio base path in the packages section
|
||||||
|
iniStr = `
|
||||||
|
[repo-archive]
|
||||||
|
STORAGE_TYPE = my_minio
|
||||||
|
MINIO_BASE_PATH = my_archive/
|
||||||
|
|
||||||
|
[storage.my_minio]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, loadRepoArchiveFrom(cfg))
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
|
||||||
|
assert.EqualValues(t, "my_archive/", RepoArchive.Storage.MinioConfig.BasePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RepoArchiveStorage(t *testing.T) {
|
||||||
|
iniStr := `
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
MINIO_ENDPOINT = s3.my-domain.net
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
MINIO_LOCATION = homenet
|
||||||
|
MINIO_USE_SSL = true
|
||||||
|
MINIO_ACCESS_KEY_ID = correct_key
|
||||||
|
MINIO_SECRET_ACCESS_KEY = correct_key
|
||||||
|
`
|
||||||
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadRepoArchiveFrom(cfg))
|
||||||
|
storage := RepoArchive.Storage
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
|
||||||
|
|
||||||
|
iniStr = `
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
[storage.repo-archive]
|
||||||
|
STORAGE_TYPE = s3
|
||||||
|
[storage.s3]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
|
MINIO_ENDPOINT = s3.my-domain.net
|
||||||
|
MINIO_BUCKET = gitea
|
||||||
|
MINIO_LOCATION = homenet
|
||||||
|
MINIO_USE_SSL = true
|
||||||
|
MINIO_ACCESS_KEY_ID = correct_key
|
||||||
|
MINIO_SECRET_ACCESS_KEY = correct_key
|
||||||
|
`
|
||||||
|
cfg, err = NewConfigProviderFromData(iniStr)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, loadRepoArchiveFrom(cfg))
|
||||||
|
storage = RepoArchive.Storage
|
||||||
|
|
||||||
|
assert.EqualValues(t, "minio", storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
|
||||||
|
}
|
|
@ -203,16 +203,18 @@ func Init(opts *Options) {
|
||||||
var err error
|
var err error
|
||||||
CfgProvider, err = NewConfigProviderFromFile(opts)
|
CfgProvider, err = NewConfigProviderFromFile(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Init[%v]: %v", opts, err)
|
log.Fatal("newConfigProviderFromFile[%v]: %v", opts, err)
|
||||||
}
|
}
|
||||||
if !opts.DisableLoadCommonSettings {
|
if !opts.DisableLoadCommonSettings {
|
||||||
loadCommonSettingsFrom(CfgProvider)
|
if err := loadCommonSettingsFrom(CfgProvider); err != nil {
|
||||||
|
log.Fatal("loadCommonSettingsFrom[%v]: %v", opts, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadCommonSettingsFrom loads common configurations from a configuration provider.
|
// loadCommonSettingsFrom loads common configurations from a configuration provider.
|
||||||
func loadCommonSettingsFrom(cfg ConfigProvider) {
|
func loadCommonSettingsFrom(cfg ConfigProvider) error {
|
||||||
// WARNING: don't change the sequence except you know what you are doing.
|
// WARNNING: don't change the sequence except you know what you are doing.
|
||||||
loadRunModeFrom(cfg)
|
loadRunModeFrom(cfg)
|
||||||
loadLogGlobalFrom(cfg)
|
loadLogGlobalFrom(cfg)
|
||||||
loadServerFrom(cfg)
|
loadServerFrom(cfg)
|
||||||
|
@ -222,13 +224,26 @@ func loadCommonSettingsFrom(cfg ConfigProvider) {
|
||||||
|
|
||||||
loadOAuth2From(cfg)
|
loadOAuth2From(cfg)
|
||||||
loadSecurityFrom(cfg)
|
loadSecurityFrom(cfg)
|
||||||
loadAttachmentFrom(cfg)
|
if err := loadAttachmentFrom(cfg); err != nil {
|
||||||
loadLFSFrom(cfg)
|
return err
|
||||||
|
}
|
||||||
|
if err := loadLFSFrom(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
loadTimeFrom(cfg)
|
loadTimeFrom(cfg)
|
||||||
loadRepositoryFrom(cfg)
|
loadRepositoryFrom(cfg)
|
||||||
loadPictureFrom(cfg)
|
if err := loadAvatarsFrom(cfg); err != nil {
|
||||||
loadPackagesFrom(cfg)
|
return err
|
||||||
loadActionsFrom(cfg)
|
}
|
||||||
|
if err := loadRepoAvatarFrom(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := loadPackagesFrom(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := loadActionsFrom(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
loadUIFrom(cfg)
|
loadUIFrom(cfg)
|
||||||
loadAdminFrom(cfg)
|
loadAdminFrom(cfg)
|
||||||
loadAPIFrom(cfg)
|
loadAPIFrom(cfg)
|
||||||
|
@ -239,6 +254,7 @@ func loadCommonSettingsFrom(cfg ConfigProvider) {
|
||||||
loadMirrorFrom(cfg)
|
loadMirrorFrom(cfg)
|
||||||
loadMarkupFrom(cfg)
|
loadMarkupFrom(cfg)
|
||||||
loadOtherFrom(cfg)
|
loadOtherFrom(cfg)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadRunModeFrom(rootCfg ConfigProvider) {
|
func loadRunModeFrom(rootCfg ConfigProvider) {
|
||||||
|
|
|
@ -4,87 +4,182 @@
|
||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// StorageType is a type of Storage
|
||||||
|
type StorageType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LocalStorageType is the type descriptor for local storage
|
||||||
|
LocalStorageType StorageType = "local"
|
||||||
|
// MinioStorageType is the type descriptor for minio storage
|
||||||
|
MinioStorageType StorageType = "minio"
|
||||||
|
)
|
||||||
|
|
||||||
|
var storageTypes = []StorageType{
|
||||||
|
LocalStorageType,
|
||||||
|
MinioStorageType,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidStorageType returns true if the given storage type is valid
|
||||||
|
func IsValidStorageType(storageType StorageType) bool {
|
||||||
|
for _, t := range storageTypes {
|
||||||
|
if t == storageType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinioStorageConfig represents the configuration for a minio storage
|
||||||
|
type MinioStorageConfig struct {
|
||||||
|
Endpoint string `ini:"MINIO_ENDPOINT" json:",omitempty"`
|
||||||
|
AccessKeyID string `ini:"MINIO_ACCESS_KEY_ID" json:",omitempty"`
|
||||||
|
SecretAccessKey string `ini:"MINIO_SECRET_ACCESS_KEY" json:",omitempty"`
|
||||||
|
Bucket string `ini:"MINIO_BUCKET" json:",omitempty"`
|
||||||
|
Location string `ini:"MINIO_LOCATION" json:",omitempty"`
|
||||||
|
BasePath string `ini:"MINIO_BASE_PATH" json:",omitempty"`
|
||||||
|
UseSSL bool `ini:"MINIO_USE_SSL"`
|
||||||
|
InsecureSkipVerify bool `ini:"MINIO_INSECURE_SKIP_VERIFY"`
|
||||||
|
ChecksumAlgorithm string `ini:"MINIO_CHECKSUM_ALGORITHM" json:",omitempty"`
|
||||||
|
ServeDirect bool `ini:"SERVE_DIRECT"`
|
||||||
|
}
|
||||||
|
|
||||||
// Storage represents configuration of storages
|
// Storage represents configuration of storages
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
Type string
|
Type StorageType // local or minio
|
||||||
Path string
|
Path string `json:",omitempty"` // for local type
|
||||||
Section ConfigSection
|
TemporaryPath string `json:",omitempty"`
|
||||||
ServeDirect bool
|
MinioConfig MinioStorageConfig // for minio type
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapTo implements the Mappable interface
|
func (storage *Storage) ToShadowCopy() Storage {
|
||||||
func (s *Storage) MapTo(v interface{}) error {
|
shadowStorage := *storage
|
||||||
pathValue := reflect.ValueOf(v).Elem().FieldByName("Path")
|
if shadowStorage.MinioConfig.AccessKeyID != "" {
|
||||||
if pathValue.IsValid() && pathValue.Kind() == reflect.String {
|
shadowStorage.MinioConfig.AccessKeyID = "******"
|
||||||
pathValue.SetString(s.Path)
|
|
||||||
}
|
}
|
||||||
if s.Section != nil {
|
if shadowStorage.MinioConfig.SecretAccessKey != "" {
|
||||||
return s.Section.MapTo(v)
|
shadowStorage.MinioConfig.SecretAccessKey = "******"
|
||||||
}
|
}
|
||||||
return nil
|
return shadowStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStorage(rootCfg ConfigProvider, name, typ string, targetSec ConfigSection) Storage {
|
const storageSectionName = "storage"
|
||||||
const sectionName = "storage"
|
|
||||||
sec := rootCfg.Section(sectionName)
|
|
||||||
|
|
||||||
|
func getDefaultStorageSection(rootCfg ConfigProvider) ConfigSection {
|
||||||
|
storageSec := rootCfg.Section(storageSectionName)
|
||||||
// Global Defaults
|
// Global Defaults
|
||||||
sec.Key("MINIO_ENDPOINT").MustString("localhost:9000")
|
storageSec.Key("STORAGE_TYPE").MustString("local")
|
||||||
sec.Key("MINIO_ACCESS_KEY_ID").MustString("")
|
storageSec.Key("MINIO_ENDPOINT").MustString("localhost:9000")
|
||||||
sec.Key("MINIO_SECRET_ACCESS_KEY").MustString("")
|
storageSec.Key("MINIO_ACCESS_KEY_ID").MustString("")
|
||||||
sec.Key("MINIO_BUCKET").MustString("gitea")
|
storageSec.Key("MINIO_SECRET_ACCESS_KEY").MustString("")
|
||||||
sec.Key("MINIO_LOCATION").MustString("us-east-1")
|
storageSec.Key("MINIO_BUCKET").MustString("gitea")
|
||||||
sec.Key("MINIO_USE_SSL").MustBool(false)
|
storageSec.Key("MINIO_LOCATION").MustString("us-east-1")
|
||||||
sec.Key("MINIO_INSECURE_SKIP_VERIFY").MustBool(false)
|
storageSec.Key("MINIO_USE_SSL").MustBool(false)
|
||||||
sec.Key("MINIO_CHECKSUM_ALGORITHM").MustString("default")
|
storageSec.Key("MINIO_INSECURE_SKIP_VERIFY").MustBool(false)
|
||||||
|
storageSec.Key("MINIO_CHECKSUM_ALGORITHM").MustString("default")
|
||||||
|
return storageSec
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStorage(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (*Storage, error) {
|
||||||
|
if name == "" {
|
||||||
|
return nil, errors.New("no name for storage")
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetSec ConfigSection
|
||||||
|
if typ != "" {
|
||||||
|
var err error
|
||||||
|
targetSec, err = rootCfg.GetSection(storageSectionName + "." + typ)
|
||||||
|
if err != nil {
|
||||||
|
if !IsValidStorageType(StorageType(typ)) {
|
||||||
|
return nil, fmt.Errorf("get section via storage type %q failed: %v", typ, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if targetSec != nil {
|
||||||
|
targetType := targetSec.Key("STORAGE_TYPE").String()
|
||||||
|
if targetType == "" {
|
||||||
|
if !IsValidStorageType(StorageType(typ)) {
|
||||||
|
return nil, fmt.Errorf("unknow storage type %q", typ)
|
||||||
|
}
|
||||||
|
targetSec.Key("STORAGE_TYPE").SetValue(typ)
|
||||||
|
} else if !IsValidStorageType(StorageType(targetType)) {
|
||||||
|
return nil, fmt.Errorf("unknow storage type %q for section storage.%v", targetType, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storageNameSec, _ := rootCfg.GetSection(storageSectionName + "." + name)
|
||||||
|
|
||||||
if targetSec == nil {
|
if targetSec == nil {
|
||||||
targetSec, _ = rootCfg.NewSection(name)
|
targetSec = sec
|
||||||
|
}
|
||||||
|
if targetSec == nil {
|
||||||
|
targetSec = storageNameSec
|
||||||
|
}
|
||||||
|
if targetSec == nil {
|
||||||
|
targetSec = getDefaultStorageSection(rootCfg)
|
||||||
|
} else {
|
||||||
|
targetType := targetSec.Key("STORAGE_TYPE").String()
|
||||||
|
switch {
|
||||||
|
case targetType == "":
|
||||||
|
if targetSec.Key("PATH").String() == "" {
|
||||||
|
targetSec = getDefaultStorageSection(rootCfg)
|
||||||
|
} else {
|
||||||
|
targetSec.Key("STORAGE_TYPE").SetValue("local")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
newTargetSec, _ := rootCfg.GetSection(storageSectionName + "." + targetType)
|
||||||
|
if newTargetSec == nil {
|
||||||
|
if !IsValidStorageType(StorageType(targetType)) {
|
||||||
|
return nil, fmt.Errorf("invalid storage section %s.%q", storageSectionName, targetType)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetSec = newTargetSec
|
||||||
|
if IsValidStorageType(StorageType(targetType)) {
|
||||||
|
tp := targetSec.Key("STORAGE_TYPE").String()
|
||||||
|
if tp == "" {
|
||||||
|
targetSec.Key("STORAGE_TYPE").SetValue(targetType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
targetType := targetSec.Key("STORAGE_TYPE").String()
|
||||||
|
if !IsValidStorageType(StorageType(targetType)) {
|
||||||
|
return nil, fmt.Errorf("invalid storage type %q", targetType)
|
||||||
}
|
}
|
||||||
|
|
||||||
var storage Storage
|
var storage Storage
|
||||||
storage.Section = targetSec
|
storage.Type = StorageType(targetType)
|
||||||
storage.Type = typ
|
|
||||||
|
|
||||||
overrides := make([]ConfigSection, 0, 3)
|
switch targetType {
|
||||||
nameSec, err := rootCfg.GetSection(sectionName + "." + name)
|
case string(LocalStorageType):
|
||||||
if err == nil {
|
storage.Path = ConfigSectionKeyString(targetSec, "PATH", filepath.Join(AppDataPath, name))
|
||||||
overrides = append(overrides, nameSec)
|
if !filepath.IsAbs(storage.Path) {
|
||||||
}
|
storage.Path = filepath.Join(AppWorkPath, storage.Path)
|
||||||
|
}
|
||||||
|
case string(MinioStorageType):
|
||||||
|
storage.MinioConfig.BasePath = name + "/"
|
||||||
|
|
||||||
typeSec, err := rootCfg.GetSection(sectionName + "." + typ)
|
if err := targetSec.MapTo(&storage.MinioConfig); err != nil {
|
||||||
if err == nil {
|
return nil, fmt.Errorf("map minio config failed: %v", err)
|
||||||
overrides = append(overrides, typeSec)
|
}
|
||||||
nextType := typeSec.Key("STORAGE_TYPE").String()
|
// extra config section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH to override the targetsec
|
||||||
if len(nextType) > 0 {
|
extraConfigSec := sec
|
||||||
storage.Type = nextType // Support custom STORAGE_TYPE
|
if extraConfigSec == nil {
|
||||||
|
extraConfigSec = storageNameSec
|
||||||
|
}
|
||||||
|
|
||||||
|
if extraConfigSec != nil {
|
||||||
|
storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(extraConfigSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect)
|
||||||
|
storage.MinioConfig.BasePath = ConfigSectionKeyString(extraConfigSec, "MINIO_BASE_PATH", storage.MinioConfig.BasePath)
|
||||||
|
storage.MinioConfig.Bucket = ConfigSectionKeyString(extraConfigSec, "MINIO_BUCKET", storage.MinioConfig.Bucket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
overrides = append(overrides, sec)
|
|
||||||
|
|
||||||
for _, override := range overrides {
|
return &storage, nil
|
||||||
for _, key := range override.Keys() {
|
|
||||||
if !targetSec.HasKey(key.Name()) {
|
|
||||||
_, _ = targetSec.NewKey(key.Name(), key.Value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(storage.Type) == 0 {
|
|
||||||
storage.Type = override.Key("STORAGE_TYPE").String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
storage.ServeDirect = storage.Section.Key("SERVE_DIRECT").MustBool(false)
|
|
||||||
|
|
||||||
// Specific defaults
|
|
||||||
storage.Path = storage.Section.Key("PATH").MustString(filepath.Join(AppDataPath, name))
|
|
||||||
if !filepath.IsAbs(storage.Path) {
|
|
||||||
storage.Path = filepath.Join(AppWorkPath, storage.Path)
|
|
||||||
storage.Section.Key("PATH").SetValue(storage.Path)
|
|
||||||
}
|
|
||||||
storage.Section.Key("MINIO_BASE_PATH").MustString(name + "/")
|
|
||||||
|
|
||||||
return storage
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,106 +9,6 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_getStorageCustomType(t *testing.T) {
|
|
||||||
iniStr := `
|
|
||||||
[attachment]
|
|
||||||
STORAGE_TYPE = my_minio
|
|
||||||
MINIO_BUCKET = gitea-attachment
|
|
||||||
|
|
||||||
[storage.my_minio]
|
|
||||||
STORAGE_TYPE = minio
|
|
||||||
MINIO_ENDPOINT = my_minio:9000
|
|
||||||
`
|
|
||||||
cfg, err := NewConfigProviderFromData(iniStr)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "minio", storage.Type)
|
|
||||||
assert.EqualValues(t, "my_minio:9000", storage.Section.Key("MINIO_ENDPOINT").String())
|
|
||||||
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_getStorageNameSectionOverridesTypeSection(t *testing.T) {
|
|
||||||
iniStr := `
|
|
||||||
[attachment]
|
|
||||||
STORAGE_TYPE = minio
|
|
||||||
|
|
||||||
[storage.attachments]
|
|
||||||
MINIO_BUCKET = gitea-attachment
|
|
||||||
|
|
||||||
[storage.minio]
|
|
||||||
MINIO_BUCKET = gitea
|
|
||||||
`
|
|
||||||
cfg, err := NewConfigProviderFromData(iniStr)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "minio", storage.Type)
|
|
||||||
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_getStorageTypeSectionOverridesStorageSection(t *testing.T) {
|
|
||||||
iniStr := `
|
|
||||||
[attachment]
|
|
||||||
STORAGE_TYPE = minio
|
|
||||||
|
|
||||||
[storage.minio]
|
|
||||||
MINIO_BUCKET = gitea-minio
|
|
||||||
|
|
||||||
[storage]
|
|
||||||
MINIO_BUCKET = gitea
|
|
||||||
`
|
|
||||||
cfg, err := NewConfigProviderFromData(iniStr)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "minio", storage.Type)
|
|
||||||
assert.EqualValues(t, "gitea-minio", storage.Section.Key("MINIO_BUCKET").String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_getStorageSpecificOverridesStorage(t *testing.T) {
|
|
||||||
iniStr := `
|
|
||||||
[attachment]
|
|
||||||
STORAGE_TYPE = minio
|
|
||||||
MINIO_BUCKET = gitea-attachment
|
|
||||||
|
|
||||||
[storage.attachments]
|
|
||||||
MINIO_BUCKET = gitea
|
|
||||||
|
|
||||||
[storage]
|
|
||||||
STORAGE_TYPE = local
|
|
||||||
`
|
|
||||||
cfg, err := NewConfigProviderFromData(iniStr)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "minio", storage.Type)
|
|
||||||
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_getStorageGetDefaults(t *testing.T) {
|
|
||||||
cfg, err := NewConfigProviderFromData("")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "gitea", storage.Section.Key("MINIO_BUCKET").String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_getStorageMultipleName(t *testing.T) {
|
func Test_getStorageMultipleName(t *testing.T) {
|
||||||
iniStr := `
|
iniStr := `
|
||||||
[lfs]
|
[lfs]
|
||||||
|
@ -118,32 +18,20 @@ MINIO_BUCKET = gitea-lfs
|
||||||
MINIO_BUCKET = gitea-attachment
|
MINIO_BUCKET = gitea-attachment
|
||||||
|
|
||||||
[storage]
|
[storage]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
MINIO_BUCKET = gitea-storage
|
MINIO_BUCKET = gitea-storage
|
||||||
`
|
`
|
||||||
cfg, err := NewConfigProviderFromData(iniStr)
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
{
|
assert.NoError(t, loadAttachmentFrom(cfg))
|
||||||
sec := cfg.Section("attachment")
|
assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
|
assert.NoError(t, loadLFSFrom(cfg))
|
||||||
}
|
assert.EqualValues(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket)
|
||||||
{
|
|
||||||
sec := cfg.Section("lfs")
|
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "lfs", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "gitea-lfs", storage.Section.Key("MINIO_BUCKET").String())
|
assert.NoError(t, loadAvatarsFrom(cfg))
|
||||||
}
|
assert.EqualValues(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket)
|
||||||
{
|
|
||||||
sec := cfg.Section("avatar")
|
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "avatars", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_getStorageUseOtherNameAsType(t *testing.T) {
|
func Test_getStorageUseOtherNameAsType(t *testing.T) {
|
||||||
|
@ -152,25 +40,17 @@ func Test_getStorageUseOtherNameAsType(t *testing.T) {
|
||||||
STORAGE_TYPE = lfs
|
STORAGE_TYPE = lfs
|
||||||
|
|
||||||
[storage.lfs]
|
[storage.lfs]
|
||||||
|
STORAGE_TYPE = minio
|
||||||
MINIO_BUCKET = gitea-storage
|
MINIO_BUCKET = gitea-storage
|
||||||
`
|
`
|
||||||
cfg, err := NewConfigProviderFromData(iniStr)
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
{
|
assert.NoError(t, loadAttachmentFrom(cfg))
|
||||||
sec := cfg.Section("attachment")
|
assert.EqualValues(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket)
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String())
|
assert.NoError(t, loadLFSFrom(cfg))
|
||||||
}
|
assert.EqualValues(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket)
|
||||||
{
|
|
||||||
sec := cfg.Section("lfs")
|
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
|
||||||
storage := getStorage(cfg, "lfs", storageType, sec)
|
|
||||||
|
|
||||||
assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_getStorageInheritStorageType(t *testing.T) {
|
func Test_getStorageInheritStorageType(t *testing.T) {
|
||||||
|
@ -181,24 +61,32 @@ STORAGE_TYPE = minio
|
||||||
cfg, err := NewConfigProviderFromData(iniStr)
|
cfg, err := NewConfigProviderFromData(iniStr)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
sec := cfg.Section("attachment")
|
assert.NoError(t, loadPackagesFrom(cfg))
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
assert.EqualValues(t, "minio", Packages.Storage.Type)
|
||||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
assert.EqualValues(t, "gitea", Packages.Storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
assert.EqualValues(t, "minio", storage.Type)
|
assert.NoError(t, loadRepoArchiveFrom(cfg))
|
||||||
}
|
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", RepoArchive.Storage.MinioConfig.Bucket)
|
||||||
func Test_getStorageInheritNameSectionType(t *testing.T) {
|
assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
|
||||||
iniStr := `
|
|
||||||
[storage.attachments]
|
assert.NoError(t, loadActionsFrom(cfg))
|
||||||
STORAGE_TYPE = minio
|
assert.EqualValues(t, "minio", Actions.LogStorage.Type)
|
||||||
`
|
assert.EqualValues(t, "gitea", Actions.LogStorage.MinioConfig.Bucket)
|
||||||
cfg, err := NewConfigProviderFromData(iniStr)
|
assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
|
||||||
assert.NoError(t, err)
|
|
||||||
|
assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type)
|
||||||
sec := cfg.Section("attachment")
|
assert.EqualValues(t, "gitea", Actions.ArtifactStorage.MinioConfig.Bucket)
|
||||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
|
||||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
|
||||||
|
assert.NoError(t, loadAvatarsFrom(cfg))
|
||||||
assert.EqualValues(t, "minio", storage.Type)
|
assert.EqualValues(t, "minio", Avatar.Storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", Avatar.Storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "avatars/", Avatar.Storage.MinioConfig.BasePath)
|
||||||
|
|
||||||
|
assert.NoError(t, loadRepoAvatarFrom(cfg))
|
||||||
|
assert.EqualValues(t, "minio", RepoAvatar.Storage.Type)
|
||||||
|
assert.EqualValues(t, "gitea", RepoAvatar.Storage.MinioConfig.Bucket)
|
||||||
|
assert.EqualValues(t, "repo-avatars/", RepoAvatar.Storage.MinioConfig.BasePath)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,64 +8,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mappable represents an interface that can MapTo another interface
|
|
||||||
type Mappable interface {
|
|
||||||
MapTo(v interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// toConfig will attempt to convert a given configuration cfg into the provided exemplar type.
|
|
||||||
//
|
|
||||||
// It will tolerate the cfg being passed as a []byte or string of a json representation of the
|
|
||||||
// exemplar or the correct type of the exemplar itself
|
|
||||||
func toConfig(exemplar, cfg interface{}) (interface{}, error) {
|
|
||||||
// First of all check if we've got the same type as the exemplar - if so it's all fine.
|
|
||||||
if reflect.TypeOf(cfg).AssignableTo(reflect.TypeOf(exemplar)) {
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now if not - does it provide a MapTo function we can try?
|
|
||||||
if mappable, ok := cfg.(Mappable); ok {
|
|
||||||
newVal := reflect.New(reflect.TypeOf(exemplar))
|
|
||||||
if err := mappable.MapTo(newVal.Interface()); err == nil {
|
|
||||||
return newVal.Elem().Interface(), nil
|
|
||||||
}
|
|
||||||
// MapTo has failed us ... let's try the json route ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK we've been passed a byte array right?
|
|
||||||
configBytes, ok := cfg.([]byte)
|
|
||||||
if !ok {
|
|
||||||
// oh ... it's a string then?
|
|
||||||
var configStr string
|
|
||||||
|
|
||||||
configStr, ok = cfg.(string)
|
|
||||||
configBytes = []byte(configStr)
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
// hmm ... can we marshal it to json?
|
|
||||||
var err error
|
|
||||||
configBytes, err = json.Marshal(cfg)
|
|
||||||
ok = err == nil
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
// no ... we've tried hard enough at this point - throw an error!
|
|
||||||
return nil, ErrInvalidConfiguration{cfg: cfg}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK unmarshal the byte array into a new copy of the exemplar
|
|
||||||
newVal := reflect.New(reflect.TypeOf(exemplar))
|
|
||||||
if err := json.Unmarshal(configBytes, newVal.Interface()); err != nil {
|
|
||||||
// If we can't unmarshal it then return an error!
|
|
||||||
return nil, ErrInvalidConfiguration{cfg: cfg, err: err}
|
|
||||||
}
|
|
||||||
return newVal.Elem().Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var uninitializedStorage = discardStorage("uninitialized storage")
|
var uninitializedStorage = discardStorage("uninitialized storage")
|
||||||
|
|
||||||
type discardStorage string
|
type discardStorage string
|
||||||
|
|
|
@ -12,20 +12,12 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ ObjectStorage = &LocalStorage{}
|
var _ ObjectStorage = &LocalStorage{}
|
||||||
|
|
||||||
// LocalStorageType is the type descriptor for local storage
|
|
||||||
const LocalStorageType Type = "local"
|
|
||||||
|
|
||||||
// LocalStorageConfig represents the configuration for a local storage
|
|
||||||
type LocalStorageConfig struct {
|
|
||||||
Path string `ini:"PATH"`
|
|
||||||
TemporaryPath string `ini:"TEMPORARY_PATH"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalStorage represents a local files storage
|
// LocalStorage represents a local files storage
|
||||||
type LocalStorage struct {
|
type LocalStorage struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
@ -34,13 +26,7 @@ type LocalStorage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalStorage returns a local files
|
// NewLocalStorage returns a local files
|
||||||
func NewLocalStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error) {
|
func NewLocalStorage(ctx context.Context, config *setting.Storage) (ObjectStorage, error) {
|
||||||
configInterface, err := toConfig(LocalStorageConfig{}, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config := configInterface.(LocalStorageConfig)
|
|
||||||
|
|
||||||
if !filepath.IsAbs(config.Path) {
|
if !filepath.IsAbs(config.Path) {
|
||||||
return nil, fmt.Errorf("LocalStorageConfig.Path should have been prepared by setting/storage.go and should be an absolute path, but not: %q", config.Path)
|
return nil, fmt.Errorf("LocalStorageConfig.Path should have been prepared by setting/storage.go and should be an absolute path, but not: %q", config.Path)
|
||||||
}
|
}
|
||||||
|
@ -164,5 +150,5 @@ func (l *LocalStorage) IterateObjects(dirName string, fn func(path string, obj O
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterStorageType(LocalStorageType, NewLocalStorage)
|
RegisterStorageType(setting.LocalStorageType, NewLocalStorage)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,5 +57,5 @@ func TestBuildLocalPath(t *testing.T) {
|
||||||
|
|
||||||
func TestLocalStorageIterator(t *testing.T) {
|
func TestLocalStorageIterator(t *testing.T) {
|
||||||
dir := filepath.Join(os.TempDir(), "TestLocalStorageIteratorTestDir")
|
dir := filepath.Join(os.TempDir(), "TestLocalStorageIteratorTestDir")
|
||||||
testStorageIterator(t, string(LocalStorageType), LocalStorageConfig{Path: dir})
|
testStorageIterator(t, setting.LocalStorageType, &setting.Storage{Path: dir})
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/minio/minio-go/v7"
|
"github.com/minio/minio-go/v7"
|
||||||
|
@ -41,25 +42,9 @@ func (m *minioObject) Stat() (os.FileInfo, error) {
|
||||||
return &minioFileInfo{oi}, nil
|
return &minioFileInfo{oi}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MinioStorageType is the type descriptor for minio storage
|
|
||||||
const MinioStorageType Type = "minio"
|
|
||||||
|
|
||||||
// MinioStorageConfig represents the configuration for a minio storage
|
|
||||||
type MinioStorageConfig struct {
|
|
||||||
Endpoint string `ini:"MINIO_ENDPOINT"`
|
|
||||||
AccessKeyID string `ini:"MINIO_ACCESS_KEY_ID"`
|
|
||||||
SecretAccessKey string `ini:"MINIO_SECRET_ACCESS_KEY"`
|
|
||||||
Bucket string `ini:"MINIO_BUCKET"`
|
|
||||||
Location string `ini:"MINIO_LOCATION"`
|
|
||||||
BasePath string `ini:"MINIO_BASE_PATH"`
|
|
||||||
UseSSL bool `ini:"MINIO_USE_SSL"`
|
|
||||||
InsecureSkipVerify bool `ini:"MINIO_INSECURE_SKIP_VERIFY"`
|
|
||||||
ChecksumAlgorithm string `ini:"MINIO_CHECKSUM_ALGORITHM"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MinioStorage returns a minio bucket storage
|
// MinioStorage returns a minio bucket storage
|
||||||
type MinioStorage struct {
|
type MinioStorage struct {
|
||||||
cfg *MinioStorageConfig
|
cfg *setting.MinioStorageConfig
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
client *minio.Client
|
client *minio.Client
|
||||||
bucket string
|
bucket string
|
||||||
|
@ -87,13 +72,8 @@ func convertMinioErr(err error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMinioStorage returns a minio storage
|
// NewMinioStorage returns a minio storage
|
||||||
func NewMinioStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error) {
|
func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error) {
|
||||||
configInterface, err := toConfig(MinioStorageConfig{}, cfg)
|
config := cfg.MinioConfig
|
||||||
if err != nil {
|
|
||||||
return nil, convertMinioErr(err)
|
|
||||||
}
|
|
||||||
config := configInterface.(MinioStorageConfig)
|
|
||||||
|
|
||||||
if config.ChecksumAlgorithm != "" && config.ChecksumAlgorithm != "default" && config.ChecksumAlgorithm != "md5" {
|
if config.ChecksumAlgorithm != "" && config.ChecksumAlgorithm != "default" && config.ChecksumAlgorithm != "md5" {
|
||||||
return nil, fmt.Errorf("invalid minio checksum algorithm: %s", config.ChecksumAlgorithm)
|
return nil, fmt.Errorf("invalid minio checksum algorithm: %s", config.ChecksumAlgorithm)
|
||||||
}
|
}
|
||||||
|
@ -258,5 +238,5 @@ func (m *MinioStorage) IterateObjects(dirName string, fn func(path string, obj O
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterStorageType(MinioStorageType, NewMinioStorage)
|
RegisterStorageType(setting.MinioStorageType, NewMinioStorage)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ package storage
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMinioStorageIterator(t *testing.T) {
|
func TestMinioStorageIterator(t *testing.T) {
|
||||||
|
@ -13,11 +15,13 @@ func TestMinioStorageIterator(t *testing.T) {
|
||||||
t.Skip("minioStorage not present outside of CI")
|
t.Skip("minioStorage not present outside of CI")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
testStorageIterator(t, string(MinioStorageType), MinioStorageConfig{
|
testStorageIterator(t, setting.MinioStorageType, &setting.Storage{
|
||||||
Endpoint: "127.0.0.1:9000",
|
MinioConfig: setting.MinioStorageConfig{
|
||||||
AccessKeyID: "123456",
|
Endpoint: "127.0.0.1:9000",
|
||||||
SecretAccessKey: "12345678",
|
AccessKeyID: "123456",
|
||||||
Bucket: "gitea",
|
SecretAccessKey: "12345678",
|
||||||
Location: "us-east-1",
|
Bucket: "gitea",
|
||||||
|
Location: "us-east-1",
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,16 +37,15 @@ func IsErrInvalidConfiguration(err error) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type is a type of Storage
|
type Type = setting.StorageType
|
||||||
type Type string
|
|
||||||
|
|
||||||
// NewStorageFunc is a function that creates a storage
|
// NewStorageFunc is a function that creates a storage
|
||||||
type NewStorageFunc func(ctx context.Context, cfg interface{}) (ObjectStorage, error)
|
type NewStorageFunc func(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error)
|
||||||
|
|
||||||
var storageMap = map[Type]NewStorageFunc{}
|
var storageMap = map[Type]NewStorageFunc{}
|
||||||
|
|
||||||
// RegisterStorageType registers a provided storage type with a function to create it
|
// RegisterStorageType registers a provided storage type with a function to create it
|
||||||
func RegisterStorageType(typ Type, fn func(ctx context.Context, cfg interface{}) (ObjectStorage, error)) {
|
func RegisterStorageType(typ Type, fn func(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error)) {
|
||||||
storageMap[typ] = fn
|
storageMap[typ] = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,11 +150,11 @@ func Init() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStorage takes a storage type and some config and returns an ObjectStorage or an error
|
// NewStorage takes a storage type and some config and returns an ObjectStorage or an error
|
||||||
func NewStorage(typStr string, cfg interface{}) (ObjectStorage, error) {
|
func NewStorage(typStr Type, cfg *setting.Storage) (ObjectStorage, error) {
|
||||||
if len(typStr) == 0 {
|
if len(typStr) == 0 {
|
||||||
typStr = string(LocalStorageType)
|
typStr = setting.LocalStorageType
|
||||||
}
|
}
|
||||||
fn, ok := storageMap[Type(typStr)]
|
fn, ok := storageMap[typStr]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Unsupported storage type: %s", typStr)
|
return nil, fmt.Errorf("Unsupported storage type: %s", typStr)
|
||||||
}
|
}
|
||||||
|
@ -165,7 +164,7 @@ func NewStorage(typStr string, cfg interface{}) (ObjectStorage, error) {
|
||||||
|
|
||||||
func initAvatars() (err error) {
|
func initAvatars() (err error) {
|
||||||
log.Info("Initialising Avatar storage with type: %s", setting.Avatar.Storage.Type)
|
log.Info("Initialising Avatar storage with type: %s", setting.Avatar.Storage.Type)
|
||||||
Avatars, err = NewStorage(setting.Avatar.Storage.Type, &setting.Avatar.Storage)
|
Avatars, err = NewStorage(setting.Avatar.Storage.Type, setting.Avatar.Storage)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +174,7 @@ func initAttachments() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Info("Initialising Attachment storage with type: %s", setting.Attachment.Storage.Type)
|
log.Info("Initialising Attachment storage with type: %s", setting.Attachment.Storage.Type)
|
||||||
Attachments, err = NewStorage(setting.Attachment.Storage.Type, &setting.Attachment.Storage)
|
Attachments, err = NewStorage(setting.Attachment.Storage.Type, setting.Attachment.Storage)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,19 +184,19 @@ func initLFS() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Info("Initialising LFS storage with type: %s", setting.LFS.Storage.Type)
|
log.Info("Initialising LFS storage with type: %s", setting.LFS.Storage.Type)
|
||||||
LFS, err = NewStorage(setting.LFS.Storage.Type, &setting.LFS.Storage)
|
LFS, err = NewStorage(setting.LFS.Storage.Type, setting.LFS.Storage)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func initRepoAvatars() (err error) {
|
func initRepoAvatars() (err error) {
|
||||||
log.Info("Initialising Repository Avatar storage with type: %s", setting.RepoAvatar.Storage.Type)
|
log.Info("Initialising Repository Avatar storage with type: %s", setting.RepoAvatar.Storage.Type)
|
||||||
RepoAvatars, err = NewStorage(setting.RepoAvatar.Storage.Type, &setting.RepoAvatar.Storage)
|
RepoAvatars, err = NewStorage(setting.RepoAvatar.Storage.Type, setting.RepoAvatar.Storage)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func initRepoArchives() (err error) {
|
func initRepoArchives() (err error) {
|
||||||
log.Info("Initialising Repository Archive storage with type: %s", setting.RepoArchive.Storage.Type)
|
log.Info("Initialising Repository Archive storage with type: %s", setting.RepoArchive.Storage.Type)
|
||||||
RepoArchives, err = NewStorage(setting.RepoArchive.Storage.Type, &setting.RepoArchive.Storage)
|
RepoArchives, err = NewStorage(setting.RepoArchive.Storage.Type, setting.RepoArchive.Storage)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +206,7 @@ func initPackages() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Info("Initialising Packages storage with type: %s", setting.Packages.Storage.Type)
|
log.Info("Initialising Packages storage with type: %s", setting.Packages.Storage.Type)
|
||||||
Packages, err = NewStorage(setting.Packages.Storage.Type, &setting.Packages.Storage)
|
Packages, err = NewStorage(setting.Packages.Storage.Type, setting.Packages.Storage)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,10 +217,10 @@ func initActions() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Info("Initialising Actions storage with type: %s", setting.Actions.LogStorage.Type)
|
log.Info("Initialising Actions storage with type: %s", setting.Actions.LogStorage.Type)
|
||||||
if Actions, err = NewStorage(setting.Actions.LogStorage.Type, &setting.Actions.LogStorage); err != nil {
|
if Actions, err = NewStorage(setting.Actions.LogStorage.Type, setting.Actions.LogStorage); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("Initialising ActionsArtifacts storage with type: %s", setting.Actions.ArtifactStorage.Type)
|
log.Info("Initialising ActionsArtifacts storage with type: %s", setting.Actions.ArtifactStorage.Type)
|
||||||
ActionsArtifacts, err = NewStorage(setting.Actions.ArtifactStorage.Type, &setting.Actions.ArtifactStorage)
|
ActionsArtifacts, err = NewStorage(setting.Actions.ArtifactStorage.Type, setting.Actions.ArtifactStorage)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,12 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testStorageIterator(t *testing.T, typStr string, cfg interface{}) {
|
func testStorageIterator(t *testing.T, typStr Type, cfg *setting.Storage) {
|
||||||
l, err := NewStorage(typStr, cfg)
|
l, err := NewStorage(typStr, cfg)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting.LFS.ServeDirect {
|
if setting.LFS.Storage.MinioConfig.ServeDirect {
|
||||||
// If we have a signed url (S3, object storage), redirect to this directly.
|
// If we have a signed url (S3, object storage), redirect to this directly.
|
||||||
u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name())
|
u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name())
|
||||||
if u != nil && err == nil {
|
if u != nil && err == nil {
|
||||||
|
@ -320,7 +320,7 @@ func download(ctx *context.APIContext, archiveName string, archiver *repo_model.
|
||||||
downloadName := ctx.Repo.Repository.Name + "-" + archiveName
|
downloadName := ctx.Repo.Repository.Name + "-" + archiveName
|
||||||
|
|
||||||
rPath := archiver.RelativePath()
|
rPath := archiver.RelativePath()
|
||||||
if setting.RepoArchive.ServeDirect {
|
if setting.RepoArchive.Storage.MinioConfig.ServeDirect {
|
||||||
// If we have a signed url (S3, object storage), redirect to this directly.
|
// If we have a signed url (S3, object storage), redirect to this directly.
|
||||||
u, err := storage.RepoArchives.URL(rPath, downloadName)
|
u, err := storage.RepoArchives.URL(rPath, downloadName)
|
||||||
if u != nil && err == nil {
|
if u != nil && err == nil {
|
||||||
|
|
|
@ -116,7 +116,7 @@ func Install(ctx *context.Context) {
|
||||||
// Application general settings
|
// Application general settings
|
||||||
form.AppName = setting.AppName
|
form.AppName = setting.AppName
|
||||||
form.RepoRootPath = setting.RepoRootPath
|
form.RepoRootPath = setting.RepoRootPath
|
||||||
form.LFSRootPath = setting.LFS.Path
|
form.LFSRootPath = setting.LFS.Storage.Path
|
||||||
|
|
||||||
// Note(unknown): it's hard for Windows users change a running user,
|
// Note(unknown): it's hard for Windows users change a running user,
|
||||||
// so just use current one if config says default.
|
// so just use current one if config says default.
|
||||||
|
|
|
@ -19,11 +19,11 @@ import (
|
||||||
"code.gitea.io/gitea/modules/web/routing"
|
"code.gitea.io/gitea/modules/web/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler {
|
func storageHandler(storageSetting *setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler {
|
||||||
prefix = strings.Trim(prefix, "/")
|
prefix = strings.Trim(prefix, "/")
|
||||||
funcInfo := routing.GetFuncInfo(storageHandler, prefix)
|
funcInfo := routing.GetFuncInfo(storageHandler, prefix)
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
if storageSetting.ServeDirect {
|
if storageSetting.MinioConfig.ServeDirect {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
if req.Method != "GET" && req.Method != "HEAD" {
|
if req.Method != "GET" && req.Method != "HEAD" {
|
||||||
next.ServeHTTP(w, req)
|
next.ServeHTTP(w, req)
|
||||||
|
|
|
@ -126,7 +126,7 @@ func ServeAttachment(ctx *context.Context, uuid string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting.Attachment.ServeDirect {
|
if setting.Attachment.Storage.MinioConfig.ServeDirect {
|
||||||
// If we have a signed url (S3, object storage), redirect to this directly.
|
// If we have a signed url (S3, object storage), redirect to this directly.
|
||||||
u, err := storage.Attachments.URL(attach.RelativePath(), attach.Name)
|
u, err := storage.Attachments.URL(attach.RelativePath(), attach.Name)
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting.LFS.ServeDirect {
|
if setting.LFS.Storage.MinioConfig.ServeDirect {
|
||||||
// If we have a signed url (S3, object storage), redirect to this directly.
|
// If we have a signed url (S3, object storage), redirect to this directly.
|
||||||
u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name())
|
u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name())
|
||||||
if u != nil && err == nil {
|
if u != nil && err == nil {
|
||||||
|
|
|
@ -427,7 +427,7 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep
|
||||||
downloadName := ctx.Repo.Repository.Name + "-" + archiveName
|
downloadName := ctx.Repo.Repository.Name + "-" + archiveName
|
||||||
|
|
||||||
rPath := archiver.RelativePath()
|
rPath := archiver.RelativePath()
|
||||||
if setting.RepoArchive.ServeDirect {
|
if setting.RepoArchive.Storage.MinioConfig.ServeDirect {
|
||||||
// If we have a signed url (S3, object storage), redirect to this directly.
|
// If we have a signed url (S3, object storage), redirect to this directly.
|
||||||
u, err := storage.RepoArchives.URL(rPath, downloadName)
|
u, err := storage.RepoArchives.URL(rPath, downloadName)
|
||||||
if u != nil && err == nil {
|
if u != nil && err == nil {
|
||||||
|
|
|
@ -452,7 +452,7 @@ func buildObjectResponse(rc *requestContext, pointer lfs_module.Pointer, downloa
|
||||||
|
|
||||||
if download {
|
if download {
|
||||||
var link *lfs_module.Link
|
var link *lfs_module.Link
|
||||||
if setting.LFS.ServeDirect {
|
if setting.LFS.Storage.MinioConfig.ServeDirect {
|
||||||
// If we have a signed url (S3, object storage), redirect to this directly.
|
// If we have a signed url (S3, object storage), redirect to this directly.
|
||||||
u, err := storage.LFS.URL(pointer.RelativePath(), pointer.Oid)
|
u, err := storage.LFS.URL(pointer.RelativePath(), pointer.Oid)
|
||||||
if u != nil && err == nil {
|
if u != nil && err == nil {
|
||||||
|
|
|
@ -102,7 +102,7 @@
|
||||||
<dd>{{if .LFS.StartServer}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
<dd>{{if .LFS.StartServer}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
|
||||||
{{if .LFS.StartServer}}
|
{{if .LFS.StartServer}}
|
||||||
<dt>{{.locale.Tr "admin.config.lfs_content_path"}}</dt>
|
<dt>{{.locale.Tr "admin.config.lfs_content_path"}}</dt>
|
||||||
<dd>{{.LFS.Path}}</dd>
|
<dd>{{JsonUtils.EncodeToString .LFS.Storage.ToShadowCopy}}</dd>
|
||||||
<dt>{{.locale.Tr "admin.config.lfs_http_auth_expiry"}}</dt>
|
<dt>{{.locale.Tr "admin.config.lfs_http_auth_expiry"}}</dt>
|
||||||
<dd>{{.LFS.HTTPAuthExpiry}}</dd>
|
<dd>{{.LFS.HTTPAuthExpiry}}</dd>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -214,7 +214,9 @@ func PrepareTestEnv(t testing.TB, skip ...int) func() {
|
||||||
|
|
||||||
// load LFS object fixtures
|
// load LFS object fixtures
|
||||||
// (LFS storage can be on any of several backends, including remote servers, so we init it with the storage API)
|
// (LFS storage can be on any of several backends, including remote servers, so we init it with the storage API)
|
||||||
lfsFixtures, err := storage.NewStorage("", storage.LocalStorageConfig{Path: path.Join(filepath.Dir(setting.AppPath), "tests/gitea-lfs-meta")})
|
lfsFixtures, err := storage.NewStorage(setting.LocalStorageType, &setting.Storage{
|
||||||
|
Path: filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-lfs-meta"),
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, storage.Clean(storage.LFS))
|
assert.NoError(t, storage.Clean(storage.LFS))
|
||||||
assert.NoError(t, lfsFixtures.IterateObjects("", func(path string, _ storage.Object) error {
|
assert.NoError(t, lfsFixtures.IterateObjects("", func(path string, _ storage.Object) error {
|
||||||
|
|
Loading…
Reference in a new issue