Add default storage configurations (#12813)

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: zeripath <art27@cantab.net>
release/v1.15
Lunny Xiao 2020-09-29 17:05:13 +08:00 committed by GitHub
parent 4c6ac08182
commit 3878e985b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 459 additions and 185 deletions

View File

@ -98,3 +98,6 @@ issues:
- path: models/update.go - path: models/update.go
linters: linters:
- unused - unused
- path: cmd/dump.go
linters:
- dupl

View File

@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"gitea.com/macaron/session" "gitea.com/macaron/session"
@ -57,6 +58,8 @@ func addRecursive(w archiver.Writer, dirPath string, absPath string, verbose boo
if err != nil { if err != nil {
return fmt.Errorf("Could not open directory %s: %s", absPath, err) return fmt.Errorf("Could not open directory %s: %s", absPath, err)
} }
defer dir.Close()
files, err := dir.Readdir(0) files, err := dir.Readdir(0)
if err != nil { if err != nil {
return fmt.Errorf("Unable to list files in %s: %s", absPath, err) return fmt.Errorf("Unable to list files in %s: %s", absPath, err)
@ -197,6 +200,10 @@ func runDump(ctx *cli.Context) error {
return err return err
} }
if err := storage.Init(); err != nil {
return err
}
if file == nil { if file == nil {
file, err = os.Create(fileName) file, err = os.Create(fileName)
if err != nil { if err != nil {
@ -231,11 +238,21 @@ func runDump(ctx *cli.Context) error {
fatal("Failed to include repositories: %v", err) fatal("Failed to include repositories: %v", err)
} }
if _, err := os.Stat(setting.LFS.ContentPath); !os.IsNotExist(err) { if err := storage.LFS.IterateObjects(func(objPath string, object storage.Object) error {
log.Info("Dumping lfs... %s", setting.LFS.ContentPath) info, err := object.Stat()
if err := addRecursive(w, "lfs", setting.LFS.ContentPath, verbose); err != nil { if err != nil {
fatal("Failed to include lfs: %v", err) return err
} }
return w.Write(archiver.File{
FileInfo: archiver.FileInfo{
FileInfo: info,
CustomName: path.Join("data", "lfs", objPath),
},
ReadCloser: object,
})
}); err != nil {
fatal("Failed to dump LFS objects: %v", err)
} }
} }
@ -302,13 +319,31 @@ func runDump(ctx *cli.Context) error {
} }
excludes = append(excludes, setting.RepoRootPath) excludes = append(excludes, setting.RepoRootPath)
excludes = append(excludes, setting.LFS.ContentPath) excludes = append(excludes, setting.LFS.Path)
excludes = append(excludes, setting.Attachment.Path)
excludes = append(excludes, setting.LogRootPath) excludes = append(excludes, setting.LogRootPath)
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil { if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
fatal("Failed to include data directory: %v", err) fatal("Failed to include data directory: %v", err)
} }
} }
if err := storage.Attachments.IterateObjects(func(objPath string, object storage.Object) error {
info, err := object.Stat()
if err != nil {
return err
}
return w.Write(archiver.File{
FileInfo: archiver.FileInfo{
FileInfo: info,
CustomName: path.Join("data", "attachments", objPath),
},
ReadCloser: object,
})
}); err != nil {
fatal("Failed to dump attachments: %v", err)
}
// Doesn't check if LogRootPath exists before processing --skip-log intentionally, // Doesn't check if LogRootPath exists before processing --skip-log intentionally,
// ensuring that it's clear the dump is skipped whether the directory's initialized // ensuring that it's clear the dump is skipped whether the directory's initialized
// yet or not. // yet or not.

View File

@ -7,6 +7,7 @@ package cmd
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/migrations" "code.gitea.io/gitea/models/migrations"
@ -30,8 +31,8 @@ var CmdMigrateStorage = cli.Command{
Usage: "Kinds of files to migrate, currently only 'attachments' is supported", Usage: "Kinds of files to migrate, currently only 'attachments' is supported",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "store, s", Name: "storage, s",
Value: "local", Value: setting.LocalStorageType,
Usage: "New storage type, local or minio", Usage: "New storage type, local or minio",
}, },
cli.StringFlag{ cli.StringFlag{
@ -112,15 +113,15 @@ func runMigrateStorage(ctx *cli.Context) error {
var dstStorage storage.ObjectStorage var dstStorage storage.ObjectStorage
var err error var err error
switch ctx.String("store") { switch strings.ToLower(ctx.String("storage")) {
case "local": case setting.LocalStorageType:
p := ctx.String("path") p := ctx.String("path")
if p == "" { if p == "" {
log.Fatal("Path must be given when store is loal") log.Fatal("Path must be given when storage is loal")
return nil return nil
} }
dstStorage, err = storage.NewLocalStorage(p) dstStorage, err = storage.NewLocalStorage(p)
case "minio": case setting.MinioStorageType:
dstStorage, err = storage.NewMinioStorage( dstStorage, err = storage.NewMinioStorage(
context.Background(), context.Background(),
ctx.String("minio-endpoint"), ctx.String("minio-endpoint"),
@ -132,14 +133,14 @@ func runMigrateStorage(ctx *cli.Context) error {
ctx.Bool("minio-use-ssl"), ctx.Bool("minio-use-ssl"),
) )
default: default:
return fmt.Errorf("Unsupported attachments store type: %s", ctx.String("store")) return fmt.Errorf("Unsupported attachments storage type: %s", ctx.String("storage"))
} }
if err != nil { if err != nil {
return err return err
} }
tp := ctx.String("type") tp := strings.ToLower(ctx.String("type"))
switch tp { switch tp {
case "attachments": case "attachments":
if err := migrateAttachments(dstStorage); err != nil { if err := migrateAttachments(dstStorage); err != nil {

View File

@ -778,25 +778,25 @@ MAX_SIZE = 4
MAX_FILES = 5 MAX_FILES = 5
; Storage type for attachments, `local` for local disk or `minio` for s3 compatible ; Storage type for attachments, `local` for local disk or `minio` for s3 compatible
; object storage service, default is `local`. ; object storage service, default is `local`.
STORE_TYPE = local STORAGE_TYPE = local
; Allows the storage driver to redirect to authenticated URLs to serve files directly ; Allows the storage driver to redirect to authenticated URLs to serve files directly
; Currently, only `minio` is supported. ; Currently, only `minio` is supported.
SERVE_DIRECT = false SERVE_DIRECT = false
; Path for attachments. Defaults to `data/attachments` only available when STORE_TYPE is `local` ; Path for attachments. Defaults to `data/attachments` only available when STORAGE_TYPE is `local`
PATH = data/attachments PATH = data/attachments
; Minio endpoint to connect only available when STORE_TYPE is `minio` ; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
MINIO_ENDPOINT = localhost:9000 MINIO_ENDPOINT = localhost:9000
; Minio accessKeyID to connect only available when STORE_TYPE is `minio` ; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
MINIO_ACCESS_KEY_ID = MINIO_ACCESS_KEY_ID =
; Minio secretAccessKey to connect only available when STORE_TYPE is `minio` ; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
MINIO_SECRET_ACCESS_KEY = MINIO_SECRET_ACCESS_KEY =
; Minio bucket to store the attachments only available when STORE_TYPE is `minio` ; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
MINIO_BUCKET = gitea MINIO_BUCKET = gitea
; Minio location to create bucket only available when STORE_TYPE is `minio` ; Minio location to create bucket only available when STORAGE_TYPE is `minio`
MINIO_LOCATION = us-east-1 MINIO_LOCATION = us-east-1
; Minio base path on the bucket only available when STORE_TYPE is `minio` ; Minio base path on the bucket only available when STORAGE_TYPE is `minio`
MINIO_BASE_PATH = attachments/ MINIO_BASE_PATH = attachments/
; Minio enabled ssl only available when STORE_TYPE is `minio` ; Minio enabled ssl only available when STORAGE_TYPE is `minio`
MINIO_USE_SSL = false MINIO_USE_SSL = false
[time] [time]
@ -1161,3 +1161,28 @@ QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0"
MAX_ATTEMPTS = 3 MAX_ATTEMPTS = 3
; Backoff time per http/https request retry (seconds) ; Backoff time per http/https request retry (seconds)
RETRY_BACKOFF = 3 RETRY_BACKOFF = 3
; default storage for attachments, lfs and avatars
[storage]
; storage type
STORAGE_TYPE = local
; lfs storage will override storage
[lfs]
STORAGE_TYPE = local
; customize storage
;[storage.my_minio]
;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

View File

@ -220,16 +220,6 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `LANDING_PAGE`: **home**: Landing page for unauthenticated users \[home, explore, organizations, login\]. - `LANDING_PAGE`: **home**: Landing page for unauthenticated users \[home, explore, organizations, login\].
- `LFS_START_SERVER`: **false**: Enables git-lfs support. - `LFS_START_SERVER`: **false**: Enables git-lfs support.
- `LFS_STORE_TYPE`: **local**: Storage type for lfs, `local` for local disk or `minio` for s3 compatible object storage service.
- `LFS_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.
- `LFS_CONTENT_PATH`: **./data/lfs**: Where to store LFS files, only available when `LFS_STORE_TYPE` is `local`.
- `LFS_MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when `LFS_STORE_TYPE` is `minio`
- `LFS_MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `LFS_STORE_TYPE` is `minio`
- `LFS_MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `LFS_STORE_TYPE is` `minio`
- `LFS_MINIO_BUCKET`: **gitea**: Minio bucket to store the lfs only available when `LFS_STORE_TYPE` is `minio`
- `LFS_MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `LFS_STORE_TYPE` is `minio`
- `LFS_MINIO_BASE_PATH`: **lfs/**: Minio base path on the bucket only available when `LFS_STORE_TYPE` is `minio`
- `LFS_MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `LFS_STORE_TYPE` is `minio`
- `LFS_JWT_SECRET`: **\<empty\>**: LFS authentication secret, change this a unique string. - `LFS_JWT_SECRET`: **\<empty\>**: LFS authentication secret, change this a unique string.
- `LFS_HTTP_AUTH_EXPIRY`: **20m**: LFS authentication validity period in time.Duration, pushes taking longer than this may fail. - `LFS_HTTP_AUTH_EXPIRY`: **20m**: LFS authentication validity period in time.Duration, pushes taking longer than this may fail.
- `LFS_MAX_FILE_SIZE`: **0**: Maximum allowed LFS file size in bytes (Set to 0 for no limit). - `LFS_MAX_FILE_SIZE`: **0**: Maximum allowed LFS file size in bytes (Set to 0 for no limit).
@ -501,16 +491,16 @@ relation to port exhaustion.
Use `*/*` for all types. Use `*/*` for all types.
- `MAX_SIZE`: **4**: Maximum size (MB). - `MAX_SIZE`: **4**: Maximum size (MB).
- `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once. - `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once.
- `STORE_TYPE`: **local**: Storage type for attachments, `local` for local disk or `minio` for s3 compatible object storage service, default is `local`. - `STORAGE_TYPE`: **local**: Storage type for attachments, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]`
- `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.
- `PATH`: **data/attachments**: Path to store attachments only available when STORE_TYPE is `local` - `PATH`: **data/attachments**: Path to store attachments only available when STORAGE_TYPE is `local`
- `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when STORE_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 STORE_TYPE is `minio` - `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when STORE_TYPE is `minio` - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
- `MINIO_BUCKET`: **gitea**: Minio bucket to store the attachments only available when STORE_TYPE is `minio` - `MINIO_BUCKET`: **gitea**: Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when STORE_TYPE is `minio` - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when STORAGE_TYPE is `minio`
- `MINIO_BASE_PATH`: **attachments/**: Minio base path on the bucket only available when STORE_TYPE is `minio` - `MINIO_BASE_PATH`: **attachments/**: Minio base path on the bucket only available when STORAGE_TYPE is `minio`
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when STORE_TYPE is `minio` - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when STORAGE_TYPE is `minio`
## Log (`log`) ## Log (`log`)
@ -714,6 +704,56 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
- `MAX_ATTEMPTS`: **3**: Max attempts per http/https request on migrations. - `MAX_ATTEMPTS`: **3**: Max attempts per http/https request on migrations.
- `RETRY_BACKOFF`: **3**: Backoff time per http/https request retry (seconds) - `RETRY_BACKOFF`: **3**: Backoff time per http/https request retry (seconds)
## LFS (`lfs`)
Storage configuration for lfs data. It will be derived from default `[storage]` or
`[storage.xxx]` when set `STORAGE_TYPE` to `xxx`. When derived, the default of `PATH`
is `data/lfs` and the default of `MINIO_BASE_PATH` is `lfs/`.
- `STORAGE_TYPE`: **local**: Storage type for lfs, `local` for local disk or `minio` for s3 compatible object storage service or other name defined with `[storage.xxx]`
- `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.
- `CONTENT_PATH`: **./data/lfs**: Where to store LFS files, only available when `STORAGE_TYPE` is `local`.
- `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_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio`
- `MINIO_BUCKET`: **gitea**: Minio bucket to store the lfs only available when `STORAGE_TYPE` is `minio`
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio`
- `MINIO_BASE_PATH`: **lfs/**: Minio base path on the bucket only available when `STORAGE_TYPE` is `minio`
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
## Storage (`storage`)
Default storage configuration for attachments, lfs, avatars and etc.
- `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_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio`
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio`
- `MINIO_BUCKET`: **gitea**: Minio bucket to store the data only available when `STORAGE_TYPE` is `minio`
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio`
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
And you can also define a customize storage like below:
```ini
[storage.my_minio]
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
```
And used by `[attachment]`, `[lfs]` and etc. as `STORAGE_TYPE`.
## Other (`other`) ## Other (`other`)
- `SHOW_FOOTER_BRANDING`: **false**: Show Gitea branding in the footer. - `SHOW_FOOTER_BRANDING`: **false**: Show Gitea branding in the footer.

View File

@ -72,16 +72,6 @@ menu:
- `LANDING_PAGE`: 未登录用户的默认页面,可选 `home``explore` - `LANDING_PAGE`: 未登录用户的默认页面,可选 `home``explore`
- `LFS_START_SERVER`: 是否启用 git-lfs 支持. 可以为 `true``false` 默认是 `false` - `LFS_START_SERVER`: 是否启用 git-lfs 支持. 可以为 `true``false` 默认是 `false`
- `LFS_STORE_TYPE`: **local**: LFS 的存储类型,`local` 将存储到磁盘,`minio` 将存储到 s3 兼容的对象服务。
- `LFS_SERVE_DIRECT`: **false**: 允许直接重定向到存储系统。当前,仅 Minio/S3 是支持的。
- `LFS_CONTENT_PATH`: 存放 lfs 命令上传的文件的地方,默认是 `data/lfs`
- `LFS_MINIO_ENDPOINT`: **localhost:9000**: Minio 地址,仅当 `LFS_STORE_TYPE``minio` 时有效。
- `LFS_MINIO_ACCESS_KEY_ID`: Minio accessKeyID仅当 `LFS_STORE_TYPE``minio` 时有效。
- `LFS_MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey仅当 `LFS_STORE_TYPE``minio` 时有效。
- `LFS_MINIO_BUCKET`: **gitea**: Minio bucket仅当 `LFS_STORE_TYPE``minio` 时有效。
- `LFS_MINIO_LOCATION`: **us-east-1**: Minio location ,仅当 `LFS_STORE_TYPE``minio` 时有效。
- `LFS_MINIO_BASE_PATH`: **lfs/**: Minio base path ,仅当 `LFS_STORE_TYPE``minio` 时有效。
- `LFS_MINIO_USE_SSL`: **false**: Minio 是否启用 ssl ,仅当 `LFS_STORE_TYPE``minio` 时有效。
- `LFS_JWT_SECRET`: LFS 认证密钥,改成自己的。 - `LFS_JWT_SECRET`: LFS 认证密钥,改成自己的。
## Database (`database`) ## Database (`database`)
@ -198,15 +188,15 @@ menu:
- `ALLOWED_TYPES`: 允许上传的附件类型。比如:`image/jpeg|image/png`,用 `*/*` 表示允许任何类型。 - `ALLOWED_TYPES`: 允许上传的附件类型。比如:`image/jpeg|image/png`,用 `*/*` 表示允许任何类型。
- `MAX_SIZE`: 附件最大限制,单位 MB比如 `4` - `MAX_SIZE`: 附件最大限制,单位 MB比如 `4`
- `MAX_FILES`: 一次最多上传的附件数量,比如: `5` - `MAX_FILES`: 一次最多上传的附件数量,比如: `5`
- `STORE_TYPE`: **local**: 附件存储类型,`local` 将存储到本地文件夹, `minio` 将存储到 s3 兼容的对象存储服务中。 - `STORAGE_TYPE`: **local**: 附件存储类型,`local` 将存储到本地文件夹, `minio` 将存储到 s3 兼容的对象存储服务中。
- `PATH`: **data/attachments**: 附件存储路径,仅当 `STORE_TYPE` 为 `local` 时有效。 - `PATH`: **data/attachments**: 附件存储路径,仅当 `STORAGE_TYPE` 为 `local` 时有效。
- `MINIO_ENDPOINT`: **localhost:9000**: Minio 终端,仅当 `STORE_TYPE` 是 `minio` 时有效。 - `MINIO_ENDPOINT`: **localhost:9000**: Minio 终端,仅当 `STORAGE_TYPE` 是 `minio` 时有效。
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID ,仅当 `STORE_TYPE` 是 `minio` 时有效。 - `MINIO_ACCESS_KEY_ID`: Minio accessKeyID ,仅当 `STORAGE_TYPE` 是 `minio` 时有效。
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey仅当 `STORE_TYPE` 是 `minio` 时有效。 - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey仅当 `STORAGE_TYPE` 是 `minio` 时有效。
- `MINIO_BUCKET`: **gitea**: Minio bucket to store the attachments仅当 `STORE_TYPE` 是 `minio` 时有效。 - `MINIO_BUCKET`: **gitea**: Minio bucket to store the attachments仅当 `STORAGE_TYPE` 是 `minio` 时有效。
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket仅当 `STORE_TYPE` 是 `minio` 时有效。 - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket仅当 `STORAGE_TYPE` 是 `minio` 时有效。
- `MINIO_BASE_PATH`: **attachments/**: Minio base path on the bucket仅当 `STORE_TYPE` 是 `minio` 时有效。 - `MINIO_BASE_PATH`: **attachments/**: Minio base path on the bucket仅当 `STORAGE_TYPE` 是 `minio` 时有效。
- `MINIO_USE_SSL`: **false**: Minio enabled ssl仅当 `STORE_TYPE` 是 `minio` 时有效。 - `MINIO_USE_SSL`: **false**: Minio enabled ssl仅当 `STORAGE_TYPE` 是 `minio` 时有效。
关于 `ALLOWED_TYPES` 在 (*)unix 系统中可以使用`file -I <filename>` 来快速获得对应的 `MIME type` 关于 `ALLOWED_TYPES` 在 (*)unix 系统中可以使用`file -I <filename>` 来快速获得对应的 `MIME type`
@ -310,6 +300,55 @@ IS_INPUT_FILE = false
- `MAX_ATTEMPTS`: **3**: 在迁移过程中的 http/https 请求重试次数。 - `MAX_ATTEMPTS`: **3**: 在迁移过程中的 http/https 请求重试次数。
- `RETRY_BACKOFF`: **3**: 等待下一次重试的时间,单位秒。 - `RETRY_BACKOFF`: **3**: 等待下一次重试的时间,单位秒。
## LFS (`lfs`)
LFS 的存储配置。 如果 `STORAGE_TYPE` 为空,则此配置将从 `[storage]` 继承。如果不为 `local` 或者 `minio` 而为 `xxx` 则从 `[storage.xxx]` 继承。当继承时, `PATH` 默认为 `data/lfs``MINIO_BASE_PATH` 默认为 `lfs/`
- `STORAGE_TYPE`: **local**: LFS 的存储类型,`local` 将存储到磁盘,`minio` 将存储到 s3 兼容的对象服务。
- `SERVE_DIRECT`: **false**: 允许直接重定向到存储系统。当前,仅 Minio/S3 是支持的。
- `CONTENT_PATH`: 存放 lfs 命令上传的文件的地方,默认是 `data/lfs`
- `MINIO_ENDPOINT`: **localhost:9000**: Minio 地址,仅当 `LFS_STORAGE_TYPE``minio` 时有效。
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID仅当 `LFS_STORAGE_TYPE``minio` 时有效。
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey仅当 `LFS_STORAGE_TYPE``minio` 时有效。
- `MINIO_BUCKET`: **gitea**: Minio bucket仅当 `LFS_STORAGE_TYPE``minio` 时有效。
- `MINIO_LOCATION`: **us-east-1**: Minio location ,仅当 `LFS_STORAGE_TYPE``minio` 时有效。
- `MINIO_BASE_PATH`: **lfs/**: Minio base path ,仅当 `LFS_STORAGE_TYPE``minio` 时有效。
- `MINIO_USE_SSL`: **false**: Minio 是否启用 ssl ,仅当 `LFS_STORAGE_TYPE``minio` 时有效。
## Storage (`storage`)
Attachments, lfs, avatars and etc 的默认存储配置。
- `STORAGE_TYPE`: **local**: 附件存储类型,`local` 将存储到本地文件夹, `minio` 将存储到 s3 兼容的对象存储服务中。
- `SERVE_DIRECT`: **false**: 允许直接重定向到存储系统。当前,仅 Minio/S3 是支持的。
- `MINIO_ENDPOINT`: **localhost:9000**: Minio 终端,仅当 `STORAGE_TYPE``minio` 时有效。
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID ,仅当 `STORAGE_TYPE``minio` 时有效。
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey仅当 `STORAGE_TYPE``minio` 时有效。
- `MINIO_BUCKET`: **gitea**: Minio bucket to store the attachments仅当 `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` 时有效。
你也可以自定义一个存储的名字如下:
```ini
[storage.my_minio]
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
```
然后你在 `[attachment]`, `[lfs]` 等中可以把这个名字用作 `STORAGE_TYPE` 的值。
## Other (`other`) ## Other (`other`)
- `SHOW_FOOTER_BRANDING`: 为真则在页面底部显示Gitea的字样。 - `SHOW_FOOTER_BRANDING`: 为真则在页面底部显示Gitea的字样。

View File

@ -70,6 +70,10 @@ func RemoveAllWithNotice(title, path string) {
// RemoveStorageWithNotice removes a file from the storage and // RemoveStorageWithNotice removes a file from the storage and
// creates a system notice when error occurs. // creates a system notice when error occurs.
func RemoveStorageWithNotice(bucket storage.ObjectStorage, title, path string) { func RemoveStorageWithNotice(bucket storage.ObjectStorage, title, path string) {
removeStorageWithNotice(x, bucket, title, path)
}
func removeStorageWithNotice(e Engine, bucket storage.ObjectStorage, title, path string) {
if err := bucket.Delete(path); err != nil { if err := bucket.Delete(path); err != nil {
desc := fmt.Sprintf("%s [%s]: %v", title, path, err) desc := fmt.Sprintf("%s [%s]: %v", title, path, err)
log.Warn(title+" [%s]: %v", path, err) log.Warn(title+" [%s]: %v", path, err)

View File

@ -1748,8 +1748,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
continue continue
} }
oidPath := filepath.Join(setting.LFS.ContentPath, v.Oid[0:2], v.Oid[2:4], v.Oid[4:len(v.Oid)]) removeStorageWithNotice(sess, storage.LFS, "Delete orphaned LFS file", v.RelativePath())
removeAllWithNotice(sess, "Delete orphaned LFS file", oidPath)
} }
if _, err := sess.Delete(&LFSMetaObject{RepositoryID: repoID}); err != nil { if _, err := sess.Delete(&LFSMetaObject{RepositoryID: repoID}); err != nil {

View File

@ -67,9 +67,11 @@ func MainTest(m *testing.M, pathToGiteaRoot string) {
if err != nil { if err != nil {
fatalTestError("url.Parse: %v\n", err) fatalTestError("url.Parse: %v\n", err)
} }
setting.Attachment.Storage.Type = setting.LocalStorageType
setting.Attachment.Storage.Path = filepath.Join(setting.AppDataPath, "attachments")
setting.Attachment.Path = filepath.Join(setting.AppDataPath, "attachments") setting.LFS.Storage.Type = setting.LocalStorageType
setting.LFS.ContentPath = filepath.Join(setting.AppDataPath, "lfs") setting.LFS.Storage.Path = filepath.Join(setting.AppDataPath, "lfs")
if err = storage.Init(); err != nil { if err = storage.Init(); err != nil {
fatalTestError("storage.Init: %v\n", err) fatalTestError("storage.Init: %v\n", err)
} }

View File

@ -5,42 +5,25 @@
package setting package setting
import ( import (
"path"
"path/filepath" "path/filepath"
"strings" "strings"
"code.gitea.io/gitea/modules/log"
) )
var ( var (
// Attachment settings // Attachment settings
Attachment = struct { Attachment = struct {
StoreType string Storage
Path string
ServeDirect bool
Minio struct {
Endpoint string
AccessKeyID string
SecretAccessKey string
UseSSL bool
Bucket string
Location string
BasePath string
}
AllowedTypes string AllowedTypes string
MaxSize int64 MaxSize int64
MaxFiles int MaxFiles int
Enabled bool Enabled bool
}{ }{
StoreType: "local", Storage: Storage{
ServeDirect: false, Type: LocalStorageType,
Minio: struct { ServeDirect: false,
Endpoint string },
AccessKeyID string
SecretAccessKey string
UseSSL bool
Bucket string
Location string
BasePath string
}{},
AllowedTypes: "image/jpeg,image/png,application/zip,application/gzip", AllowedTypes: "image/jpeg,image/png,application/zip,application/gzip",
MaxSize: 4, MaxSize: 4,
MaxFiles: 5, MaxFiles: 5,
@ -50,22 +33,36 @@ var (
func newAttachmentService() { func newAttachmentService() {
sec := Cfg.Section("attachment") sec := Cfg.Section("attachment")
Attachment.StoreType = sec.Key("STORE_TYPE").MustString("local") Attachment.Storage.Type = sec.Key("STORAGE_TYPE").MustString("")
Attachment.ServeDirect = sec.Key("SERVE_DIRECT").MustBool(false) if Attachment.Storage.Type == "" {
switch Attachment.StoreType { Attachment.Storage.Type = "default"
case "local": }
Attachment.Path = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
if !filepath.IsAbs(Attachment.Path) { if Attachment.Storage.Type != LocalStorageType && Attachment.Storage.Type != MinioStorageType {
Attachment.Path = path.Join(AppWorkPath, Attachment.Path) storage, ok := storages[Attachment.Storage.Type]
if !ok {
log.Fatal("Failed to get attachment storage type: %s", Attachment.Storage.Type)
} }
case "minio": Attachment.Storage = storage
Attachment.Minio.Endpoint = sec.Key("MINIO_ENDPOINT").MustString("localhost:9000") }
Attachment.Minio.AccessKeyID = sec.Key("MINIO_ACCESS_KEY_ID").MustString("")
Attachment.Minio.SecretAccessKey = sec.Key("MINIO_SECRET_ACCESS_KEY").MustString("") // Override
Attachment.Minio.Bucket = sec.Key("MINIO_BUCKET").MustString("gitea") Attachment.ServeDirect = sec.Key("SERVE_DIRECT").MustBool(Attachment.ServeDirect)
Attachment.Minio.Location = sec.Key("MINIO_LOCATION").MustString("us-east-1")
switch Attachment.Storage.Type {
case LocalStorageType:
Attachment.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "attachments"))
if !filepath.IsAbs(Attachment.Path) {
Attachment.Path = filepath.Join(AppWorkPath, Attachment.Path)
}
case MinioStorageType:
Attachment.Minio.Endpoint = sec.Key("MINIO_ENDPOINT").MustString(Attachment.Minio.Endpoint)
Attachment.Minio.AccessKeyID = sec.Key("MINIO_ACCESS_KEY_ID").MustString(Attachment.Minio.AccessKeyID)
Attachment.Minio.SecretAccessKey = sec.Key("MINIO_SECRET_ACCESS_KEY").MustString(Attachment.Minio.SecretAccessKey)
Attachment.Minio.Bucket = sec.Key("MINIO_BUCKET").MustString(Attachment.Minio.Bucket)
Attachment.Minio.Location = sec.Key("MINIO_LOCATION").MustString(Attachment.Minio.Location)
Attachment.Minio.UseSSL = sec.Key("MINIO_USE_SSL").MustBool(Attachment.Minio.UseSSL)
Attachment.Minio.BasePath = sec.Key("MINIO_BASE_PATH").MustString("attachments/") Attachment.Minio.BasePath = sec.Key("MINIO_BASE_PATH").MustString("attachments/")
Attachment.Minio.UseSSL = sec.Key("MINIO_USE_SSL").MustBool(false)
} }
Attachment.AllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png,application/zip,application/gzip"), "|", ",", -1) Attachment.AllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png,application/zip,application/gzip"), "|", ",", -1)

View File

@ -21,27 +21,14 @@ import (
// LFS represents the configuration for Git LFS // LFS represents the configuration for Git LFS
var LFS = struct { var LFS = struct {
StartServer bool `ini:"LFS_START_SERVER"` StartServer bool `ini:"LFS_START_SERVER"`
ContentPath string `ini:"LFS_CONTENT_PATH"`
JWTSecretBase64 string `ini:"LFS_JWT_SECRET"` JWTSecretBase64 string `ini:"LFS_JWT_SECRET"`
JWTSecretBytes []byte `ini:"-"` JWTSecretBytes []byte `ini:"-"`
HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"` HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"`
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"`
StoreType string Storage
ServeDirect bool }{}
Minio struct {
Endpoint string
AccessKeyID string
SecretAccessKey string
UseSSL bool
Bucket string
Location string
BasePath string
}
}{
StoreType: "local",
}
func newLFSService() { func newLFSService() {
sec := Cfg.Section("server") sec := Cfg.Section("server")
@ -49,10 +36,41 @@ func newLFSService() {
log.Fatal("Failed to map LFS settings: %v", err) log.Fatal("Failed to map LFS settings: %v", err)
} }
LFS.ContentPath = sec.Key("LFS_CONTENT_PATH").MustString(filepath.Join(AppDataPath, "lfs")) lfsSec := Cfg.Section("lfs")
if !filepath.IsAbs(LFS.ContentPath) { LFS.Storage.Type = lfsSec.Key("STORAGE_TYPE").MustString("")
LFS.ContentPath = filepath.Join(AppWorkPath, LFS.ContentPath) if LFS.Storage.Type == "" {
LFS.Storage.Type = "default"
} }
if LFS.Storage.Type != LocalStorageType && LFS.Storage.Type != MinioStorageType {
storage, ok := storages[LFS.Storage.Type]
if !ok {
log.Fatal("Failed to get lfs storage type: %s", LFS.Storage.Type)
}
LFS.Storage = storage
}
// Override
LFS.ServeDirect = lfsSec.Key("SERVE_DIRECT").MustBool(LFS.ServeDirect)
switch LFS.Storage.Type {
case LocalStorageType:
// keep compatible
LFS.Path = sec.Key("LFS_CONTENT_PATH").MustString(filepath.Join(AppDataPath, "lfs"))
LFS.Path = lfsSec.Key("PATH").MustString(LFS.Path)
if !filepath.IsAbs(LFS.Path) {
LFS.Path = filepath.Join(AppWorkPath, LFS.Path)
}
case MinioStorageType:
LFS.Minio.Endpoint = lfsSec.Key("MINIO_ENDPOINT").MustString(LFS.Minio.Endpoint)
LFS.Minio.AccessKeyID = lfsSec.Key("MINIO_ACCESS_KEY_ID").MustString(LFS.Minio.AccessKeyID)
LFS.Minio.SecretAccessKey = lfsSec.Key("MINIO_SECRET_ACCESS_KEY").MustString(LFS.Minio.SecretAccessKey)
LFS.Minio.Bucket = lfsSec.Key("MINIO_BUCKET").MustString(LFS.Minio.Bucket)
LFS.Minio.Location = lfsSec.Key("MINIO_LOCATION").MustString(LFS.Minio.Location)
LFS.Minio.UseSSL = lfsSec.Key("MINIO_USE_SSL").MustBool(LFS.Minio.UseSSL)
LFS.Minio.BasePath = lfsSec.Key("MINIO_BASE_PATH").MustString("lfs/")
}
if LFS.LocksPagingNum == 0 { if LFS.LocksPagingNum == 0 {
LFS.LocksPagingNum = 50 LFS.LocksPagingNum = 50
} }
@ -92,14 +110,6 @@ func newLFSService() {
} }
} }
func ensureLFSDirectory() {
if LFS.StartServer {
if err := os.MkdirAll(LFS.ContentPath, 0700); err != nil {
log.Fatal("Failed to create '%s': %v", LFS.ContentPath, err)
}
}
}
// CheckLFSVersion will check lfs version, if not satisfied, then disable it. // CheckLFSVersion will check lfs version, if not satisfied, then disable it.
func CheckLFSVersion() { func CheckLFSVersion() {
if LFS.StartServer { if LFS.StartServer {

View File

@ -691,8 +691,6 @@ func NewContext() {
SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true) SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true)
SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false) SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)
newLFSService()
if err = Cfg.Section("oauth2").MapTo(&OAuth2); err != nil { if err = Cfg.Section("oauth2").MapTo(&OAuth2); err != nil {
log.Fatal("Failed to OAuth2 settings: %v", err) log.Fatal("Failed to OAuth2 settings: %v", err)
return return
@ -761,7 +759,9 @@ func NewContext() {
} }
} }
newStorageService()
newAttachmentService() newAttachmentService()
newLFSService()
timeFormatKey := Cfg.Section("time").Key("FORMAT").MustString("") timeFormatKey := Cfg.Section("time").Key("FORMAT").MustString("")
if timeFormatKey != "" { if timeFormatKey != "" {
@ -1017,7 +1017,6 @@ func NewServices() {
InitDBConfig() InitDBConfig()
newService() newService()
NewLogServices(false) NewLogServices(false)
ensureLFSDirectory()
newCacheService() newCacheService()
newSessionService() newSessionService()
newCORSService() newCORSService()

View File

@ -0,0 +1,69 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package setting
import (
"strings"
"code.gitea.io/gitea/modules/log"
ini "gopkg.in/ini.v1"
)
// enumerate all storage types
const (
LocalStorageType = "local"
MinioStorageType = "minio"
)
// Storage represents configuration of storages
type Storage struct {
Type string
Path string
ServeDirect bool
Minio struct {
Endpoint string
AccessKeyID string
SecretAccessKey string
UseSSL bool
Bucket string
Location string
BasePath string
}
}
var (
storages = make(map[string]Storage)
)
func getStorage(sec *ini.Section) Storage {
var storage Storage
storage.Type = sec.Key("STORAGE_TYPE").MustString(LocalStorageType)
storage.ServeDirect = sec.Key("SERVE_DIRECT").MustBool(false)
switch storage.Type {
case LocalStorageType:
case MinioStorageType:
storage.Minio.Endpoint = sec.Key("MINIO_ENDPOINT").MustString("localhost:9000")
storage.Minio.AccessKeyID = sec.Key("MINIO_ACCESS_KEY_ID").MustString("")
storage.Minio.SecretAccessKey = sec.Key("MINIO_SECRET_ACCESS_KEY").MustString("")
storage.Minio.Bucket = sec.Key("MINIO_BUCKET").MustString("gitea")
storage.Minio.Location = sec.Key("MINIO_LOCATION").MustString("us-east-1")
storage.Minio.UseSSL = sec.Key("MINIO_USE_SSL").MustBool(false)
}
return storage
}
func newStorageService() {
sec := Cfg.Section("storage")
storages["default"] = getStorage(sec)
for _, sec := range Cfg.Section("storage").ChildSections() {
name := strings.TrimPrefix(sec.Name(), "storage.")
if name == "default" || name == LocalStorageType || name == MinioStorageType {
log.Error("storage name %s is system reserved!", name)
continue
}
storages[name] = getStorage(sec)
}
}

View File

@ -59,7 +59,7 @@ func (l *LocalStorage) Save(path string, r io.Reader) (int64, error) {
} }
// Stat returns the info of the file // Stat returns the info of the file
func (l *LocalStorage) Stat(path string) (ObjectInfo, error) { func (l *LocalStorage) Stat(path string) (os.FileInfo, error) {
return os.Stat(filepath.Join(l.dir, path)) return os.Stat(filepath.Join(l.dir, path))
} }
@ -73,3 +73,28 @@ func (l *LocalStorage) Delete(path string) error {
func (l *LocalStorage) URL(path, name string) (*url.URL, error) { func (l *LocalStorage) URL(path, name string) (*url.URL, error) {
return nil, ErrURLNotSupported return nil, ErrURLNotSupported
} }
// IterateObjects iterates across the objects in the local storage
func (l *LocalStorage) IterateObjects(fn func(path string, obj Object) error) error {
return filepath.Walk(l.dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == l.dir {
return nil
}
if info.IsDir() {
return nil
}
relPath, err := filepath.Rel(l.dir, path)
if err != nil {
return err
}
obj, err := os.Open(path)
if err != nil {
return err
}
defer obj.Close()
return fn(relPath, obj)
})
}

View File

@ -22,6 +22,19 @@ var (
quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
) )
type minioObject struct {
*minio.Object
}
func (m *minioObject) Stat() (os.FileInfo, error) {
oi, err := m.Object.Stat()
if err != nil {
return nil, err
}
return &minioFileInfo{oi}, nil
}
// MinioStorage returns a minio bucket storage // MinioStorage returns a minio bucket storage
type MinioStorage struct { type MinioStorage struct {
ctx context.Context ctx context.Context
@ -69,7 +82,7 @@ func (m *MinioStorage) Open(path string) (Object, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return object, nil return &minioObject{object}, nil
} }
// Save save a file to minio // Save save a file to minio
@ -104,8 +117,20 @@ func (m minioFileInfo) ModTime() time.Time {
return m.LastModified return m.LastModified
} }
func (m minioFileInfo) IsDir() bool {
return strings.HasSuffix(m.ObjectInfo.Key, "/")
}
func (m minioFileInfo) Mode() os.FileMode {
return os.ModePerm
}
func (m minioFileInfo) Sys() interface{} {
return nil
}
// Stat returns the stat information of the object // Stat returns the stat information of the object
func (m *MinioStorage) Stat(path string) (ObjectInfo, error) { func (m *MinioStorage) Stat(path string) (os.FileInfo, error) {
info, err := m.client.StatObject( info, err := m.client.StatObject(
m.ctx, m.ctx,
m.bucket, m.bucket,
@ -135,3 +160,26 @@ func (m *MinioStorage) URL(path, name string) (*url.URL, error) {
reqParams.Set("response-content-disposition", "attachment; filename=\""+quoteEscaper.Replace(name)+"\"") reqParams.Set("response-content-disposition", "attachment; filename=\""+quoteEscaper.Replace(name)+"\"")
return m.client.PresignedGetObject(m.ctx, m.bucket, m.buildMinioPath(path), 5*time.Minute, reqParams) return m.client.PresignedGetObject(m.ctx, m.bucket, m.buildMinioPath(path), 5*time.Minute, reqParams)
} }
// IterateObjects iterates across the objects in the miniostorage
func (m *MinioStorage) IterateObjects(fn func(path string, obj Object) error) error {
var opts = minio.GetObjectOptions{}
lobjectCtx, cancel := context.WithCancel(m.ctx)
defer cancel()
for mObjInfo := range m.client.ListObjects(lobjectCtx, m.bucket, minio.ListObjectsOptions{
Prefix: m.basePath,
Recursive: true,
}) {
object, err := m.client.GetObject(lobjectCtx, m.bucket, mObjInfo.Key, opts)
if err != nil {
return err
}
if err := func(object *minio.Object, fn func(path string, obj Object) error) error {
defer object.Close()
return fn(strings.TrimPrefix(m.basePath, mObjInfo.Key), &minioObject{object})
}(object, fn); err != nil {
return err
}
}
return nil
}

View File

@ -10,7 +10,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/url" "net/url"
"time" "os"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
@ -18,28 +18,25 @@ import (
var ( var (
// ErrURLNotSupported represents url is not supported // ErrURLNotSupported represents url is not supported
ErrURLNotSupported = errors.New("url method not supported") ErrURLNotSupported = errors.New("url method not supported")
// ErrIterateObjectsNotSupported represents IterateObjects not supported
ErrIterateObjectsNotSupported = errors.New("iterateObjects method not supported")
) )
// Object represents the object on the storage // Object represents the object on the storage
type Object interface { type Object interface {
io.ReadCloser io.ReadCloser
io.Seeker io.Seeker
} Stat() (os.FileInfo, error)
// ObjectInfo represents the object info on the storage
type ObjectInfo interface {
Name() string
Size() int64
ModTime() time.Time
} }
// ObjectStorage represents an object storage to handle a bucket and files // ObjectStorage represents an object storage to handle a bucket and files
type ObjectStorage interface { type ObjectStorage interface {
Open(path string) (Object, error) Open(path string) (Object, error)
Save(path string, r io.Reader) (int64, error) Save(path string, r io.Reader) (int64, error)
Stat(path string) (ObjectInfo, error) Stat(path string) (os.FileInfo, error)
Delete(path string) error Delete(path string) error
URL(path, name string) (*url.URL, error) URL(path, name string) (*url.URL, error)
IterateObjects(func(path string, obj Object) error) error
} }
// Copy copys a file from source ObjectStorage to dest ObjectStorage // Copy copys a file from source ObjectStorage to dest ObjectStorage
@ -70,14 +67,15 @@ func Init() error {
return initLFS() return initLFS()
} }
func initAttachments() error { func initStorage(storageCfg setting.Storage) (ObjectStorage, error) {
var err error var err error
switch setting.Attachment.StoreType { var s ObjectStorage
case "local": switch storageCfg.Type {
Attachments, err = NewLocalStorage(setting.Attachment.Path) case setting.LocalStorageType:
case "minio": s, err = NewLocalStorage(storageCfg.Path)
minio := setting.Attachment.Minio case setting.MinioStorageType:
Attachments, err = NewMinioStorage( minio := storageCfg.Minio
s, err = NewMinioStorage(
context.Background(), context.Background(),
minio.Endpoint, minio.Endpoint,
minio.AccessKeyID, minio.AccessKeyID,
@ -88,40 +86,22 @@ func initAttachments() error {
minio.UseSSL, minio.UseSSL,
) )
default: default:
return fmt.Errorf("Unsupported attachment store type: %s", setting.Attachment.StoreType) return nil, fmt.Errorf("Unsupported attachment store type: %s", storageCfg.Type)
} }
if err != nil { if err != nil {
return err return nil, err
} }
return nil return s, nil
} }
func initLFS() error { func initAttachments() (err error) {
var err error Attachments, err = initStorage(setting.Attachment.Storage)
switch setting.LFS.StoreType { return
case "local": }
LFS, err = NewLocalStorage(setting.LFS.ContentPath)
case "minio": func initLFS() (err error) {
minio := setting.LFS.Minio LFS, err = initStorage(setting.LFS.Storage)
LFS, err = NewMinioStorage( return
context.Background(),
minio.Endpoint,
minio.AccessKeyID,
minio.SecretAccessKey,
minio.Bucket,
minio.Location,
minio.BasePath,
minio.UseSSL,
)
default:
return fmt.Errorf("Unsupported LFS store type: %s", setting.LFS.StoreType)
}
if err != nil {
return err
}
return nil
} }

View File

@ -71,7 +71,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.ContentPath form.LFSRootPath = setting.LFS.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.

View File

@ -19,7 +19,7 @@ import (
func renderAttachmentSettings(ctx *context.Context) { func renderAttachmentSettings(ctx *context.Context) {
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
ctx.Data["AttachmentStoreType"] = setting.Attachment.StoreType ctx.Data["AttachmentStoreType"] = setting.Attachment.Storage.Type
ctx.Data["AttachmentAllowedTypes"] = setting.Attachment.AllowedTypes ctx.Data["AttachmentAllowedTypes"] = setting.Attachment.AllowedTypes
ctx.Data["AttachmentMaxSize"] = setting.Attachment.MaxSize ctx.Data["AttachmentMaxSize"] = setting.Attachment.MaxSize
ctx.Data["AttachmentMaxFiles"] = setting.Attachment.MaxFiles ctx.Data["AttachmentMaxFiles"] = setting.Attachment.MaxFiles

View File

@ -12,7 +12,6 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"path" "path"
"path/filepath"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -29,7 +28,6 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/util"
gogit "github.com/go-git/go-git/v5" gogit "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
@ -355,8 +353,8 @@ func LFSDelete(ctx *context.Context) {
// FIXME: Warning: the LFS store is not locked - and can't be locked - there could be a race condition here // FIXME: Warning: the LFS store is not locked - and can't be locked - there could be a race condition here
// Please note a similar condition happens in models/repo.go DeleteRepository // Please note a similar condition happens in models/repo.go DeleteRepository
if count == 0 { if count == 0 {
oidPath := filepath.Join(oid[0:2], oid[2:4], oid[4:]) oidPath := path.Join(oid[0:2], oid[2:4], oid[4:])
err = util.Remove(filepath.Join(setting.LFS.ContentPath, oidPath)) err = storage.LFS.Delete(oidPath)
if err != nil { if err != nil {
ctx.ServerError("LFSDelete", err) ctx.ServerError("LFSDelete", err)
return return