Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
96 lines
3.4 KiB
Go
96 lines
3.4 KiB
Go
package resumable
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"github.com/Tencent/WeKnora/internal/types"
|
|
"time"
|
|
)
|
|
|
|
const MaxFileSize int64 = 20 << 30
|
|
const ChunkSize int64 = 4 << 20
|
|
const UserQuota int64 = 40 << 30
|
|
const TenantQuota int64 = 100 << 30
|
|
const MaxActive = 20
|
|
|
|
var ErrInvalid = errors.New("上传参数或校验和无效")
|
|
var ErrConflict = errors.New("上传状态或分块偏移已变化,请刷新后继续")
|
|
var ErrQuota = errors.New("上传暂存配额不足,请取消其他上传或等待过期清理")
|
|
var ErrForbidden = errors.New("没有此上传或目标资源的权限")
|
|
|
|
// UserError contains only an intentionally public explanation written by the
|
|
// upload implementation. Driver, filesystem and provider errors are never
|
|
// promoted to this type and remain confined to server logs.
|
|
type UserError string
|
|
|
|
func (e UserError) Error() string { return string(e) }
|
|
|
|
func publicError(err error) string {
|
|
var public UserError
|
|
if errors.As(err, &public) {
|
|
return public.Error()
|
|
}
|
|
var input *types.DigitalHumanInputError
|
|
if errors.As(err, &input) {
|
|
return input.Error()
|
|
}
|
|
for _, sentinel := range []error{ErrInvalid, ErrConflict, ErrQuota, ErrForbidden} {
|
|
if errors.Is(err, sentinel) {
|
|
return sentinel.Error()
|
|
}
|
|
}
|
|
return "文件处理暂不可用,请稍后重试或联系管理员检查服务日志"
|
|
}
|
|
|
|
type Upload struct {
|
|
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
|
TenantID uint64 `json:"-" gorm:"index"`
|
|
OwnerID string `json:"-" gorm:"type:varchar(36);index"`
|
|
Kind string `json:"kind"`
|
|
TargetID string `json:"target_id"`
|
|
Name string `json:"name"`
|
|
Size int64 `json:"size"`
|
|
Offset int64 `json:"offset"`
|
|
ChunkSize int64 `json:"chunk_size"`
|
|
SHA256 string `json:"sha256"`
|
|
Fingerprint string `json:"fingerprint"`
|
|
State string `json:"state" gorm:"index"`
|
|
Error string `json:"error,omitempty"`
|
|
Options types.JSON `json:"options" gorm:"type:jsonb"`
|
|
Result types.JSON `json:"result,omitempty" gorm:"type:jsonb"`
|
|
IngestOffset int64 `json:"ingest_offset"`
|
|
Part int `json:"part"`
|
|
ExpiresAt time.Time `json:"expires_at" gorm:"index"`
|
|
LeaseUntil *time.Time `json:"-"`
|
|
LeaseToken string `json:"-"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
func (Upload) TableName() string { return "resumable_uploads" }
|
|
|
|
type Chunk struct {
|
|
UploadID string `json:"-" gorm:"primaryKey;type:varchar(36)"`
|
|
Offset int64 `json:"offset" gorm:"primaryKey"`
|
|
Size int64 `json:"size"`
|
|
SHA256 string `json:"sha256"`
|
|
}
|
|
|
|
func (Chunk) TableName() string { return "resumable_upload_chunks" }
|
|
|
|
type CreateInput struct {
|
|
Kind string `json:"kind"`
|
|
TargetID string `json:"target_id"`
|
|
Name string `json:"name"`
|
|
Size int64 `json:"size"`
|
|
SHA256 string `json:"sha256"`
|
|
Fingerprint string `json:"fingerprint"`
|
|
Options types.JSON `json:"options"`
|
|
}
|
|
|
|
// Import processes one bounded unit, returning a durable checkpoint. The worker
|
|
// renews the lease around each unit and never queues the entire file at once.
|
|
type ImportFunc func(context.Context, *Upload, string) (offset int64, result types.JSON, done bool, err error)
|
|
type ContextFunc func(context.Context, *Upload) (context.Context, error)
|
|
type AuthorizeFunc func(context.Context, string, string) error
|