media

package module
v0.0.0-...-f7c4c4e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: AGPL-3.0 Imports: 21 Imported by: 0

README

StuHelper Media

通用文件模块管理逻辑文件、不可变版本、上传会话和业务引用。课程资料及其他业务负责自己的审核和访问权益;物理副本通过 storage 公开接口管理,供应商账号与凭据由 backend 装配。

当前实现

上传用途由服务端配置,固定内容类型、大小和保护要求。上传先进入平台受控实例,通过实际类型、长度和摘要校验后等待业务批准。批准后的公开化保留可核验来源,首次引用发布要求两份健康副本,第三份由 storage 补齐。

引用发布需要当前业务授权。后台可以恢复存储操作,但不能仅凭旧请求自动开放业务引用。下载通过网关,在输出首字节前再次检查引用、停用状态和当前权限;不返回供应商地址或长期签名。

引用释放立即停止对应入口。其他有效引用继续使用同一版本;最后一个引用释放后进入持久化清理。崩溃恢复使用稳定对象键,避免超过请求重放窗口后创建第二个存储对象。

文件清单按上传者分页并检查当前权限。引用可以生成清单或 ZIP 下载包,保存原文件名、版本和摘要;打包逐项重查权限,文件数量、总大小、执行时间与并发均有上限。

接入与检查

配置必须明确用途、授权器、存储服务身份、上传期限、未引用保留期限与容量上限。media 使用独立数据库身份及迁移,不得读取 storage 数据表。正式构建设置 GOWORK=off,源码组合由 go.mod 固定。

make setup
make generate
bash scripts/check-with-postgres.sh

检查使用专用、仅本地 socket 的 PostgreSQL,结束清理数据库。普通测试使用供应商和业务授权夹具,不登录真实 BHpan。OpenAPI 先定义接口,再生成 Go 与 TypeScript SDK;生成一致性纳入检查。

具体规则、资源上限和验收场景见模块规格。后台装配与界面由应用仓库负责;Next SSO、贡献激励、受限文件加密和阅读器不在本模块实现。

许可证为 AGPL-3.0-only。

新文件使用 storage 共享内容引用,重复实际上传不会生成另一组同范围、同保护要求的物理副本。创建上传时启用 reuse_existing,可免传输本人相同用途、内容类型和内容标识的已完成文件。复用保留独立版本、文件名、审核及引用,仍检查当前来源权限。

reused 为 true 且状态为 verifying 时,只查询原会话等待恢复。复用成功后新版本进入 awaiting_approval。跨用户仅提交摘要仍需实际上传;旧版对象继续保留原身份,重复执行迁移不会重写历史绑定。

Documentation

Overview

Package media owns logical files and business references, independent of providers.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInput       = errors.New("media_invalid_input")
	ErrDenied      = errors.New("media_permission_denied")
	ErrNotFound    = errors.New("media_not_found")
	ErrConflict    = errors.New("media_conflict")
	ErrUnavailable = errors.New("media_unavailable")
	ErrIntegrity   = errors.New("media_integrity_failed")
	ErrType        = errors.New("media_content_type_rejected")
	ErrCapacity    = errors.New("media_capacity_unavailable")
	ErrExpired     = errors.New("media_upload_expired")
)

Functions

This section is empty.

Types

type Access

type Access struct {
	FileID, VersionID, Purpose string
	Owner                      Principal
	Protection                 Protection
	Reference                  *Reference
}

Access is trusted persisted context, never an authorization claim from JSON.

type ArchiveEntry

type ArchiveEntry struct {
	ReferenceID string `json:"reference_id"`
	VersionID   string `json:"version_id"`
	Path        string `json:"path"`
	Filename    string `json:"filename"`
	Size        int64  `json:"size"`
	SHA256      string `json:"sha256"`
}

type ArchiveManifest

type ArchiveManifest struct {
	Entries   []ArchiveEntry `json:"entries"`
	TotalSize int64          `json:"total_size"`
}

type ArchiveRequest

type ArchiveRequest struct {
	References []string `json:"references"`
}

type Authorizer

type Authorizer interface {
	Authorize(context.Context, Principal, string, Access) error
}

type Binding

type Binding struct {
	Module     string `json:"module"`
	ResourceID string `json:"resource_id"`
}

type Config

type Config struct {
	Profiles                                                                        map[string]Profile
	Actor                                                                           Principal
	Clock                                                                           func() time.Time
	UploadTTL, UnreferencedTTL, OperationTimeout                                    time.Duration
	ParallelTransfers, MaxOpenUploads, MaxFilesPerOwner, MaxVersions, MaxReferences int
	MaxAttempts                                                                     int
	MaxArchiveFiles                                                                 int
	MaxArchiveBytes                                                                 int64
}

type Event

type Event struct {
	SpecVersion     string    `json:"specversion"`
	Source          string    `json:"source"`
	DataContentType string    `json:"datacontenttype"`
	Data            EventData `json:"data"`
	ID              string    `json:"id"`
	Type            string    `json:"type"`
	Subject         string    `json:"subject"`
	Revision        int64     `json:"-"`
	Time            time.Time `json:"time"`
	Actor           Principal `json:"-"`
}

type EventData

type EventData struct {
	Revision int64 `json:"revision"`
}

type File

type File struct {
	ID          string    `json:"id"`
	Owner       Principal `json:"-"`
	Purpose     string    `json:"purpose"`
	Disabled    bool      `json:"disabled"`
	Deleting    bool      `json:"deleting"`
	Revision    int64     `json:"revision"`
	NextVersion int       `json:"-"`
	CreatedAt   time.Time `json:"created_at"`
}

type FileListing

type FileListing struct {
	File
	Protection Protection
}

type FilePage

type FilePage struct {
	Items      []File `json:"items"`
	NextCursor string `json:"next_cursor"`
}

type FileView

type FileView struct {
	File
	Versions []Version `json:"versions"`
}

type Job

type Job struct {
	VersionID               string
	State                   string
	Generation              int64
	LeaseOwner              string
	LeaseUntil, AvailableAt time.Time
	Attempts                int
	ErrorCode               string
}

type Principal

type Principal = storage.Principal

type Profile

type Profile struct {
	Module       string     `json:"module"`
	Quarantine   Protection `json:"quarantine"`
	Published    Protection `json:"published"`
	AllowedTypes []string   `json:"allowed_types"`
	MaxSize      int64      `json:"max_size"`
}

type Protection

type Protection = storage.Protection

type Reference

type Reference struct {
	ID         string `json:"id"`
	FileID     string `json:"file_id"`
	VersionID  string `json:"version_id"`
	Module     string `json:"module"`
	ResourceID string `json:"resource_id"`
	State      string `json:"state"`
	Revision   int64  `json:"revision"`
}

type Replay

type Replay struct {
	Key, Digest string
	Result      json.RawMessage
}

type Repository

type Repository interface {
	Transaction(context.Context, func(Transaction) error) error
	Claim(context.Context, string, time.Time, time.Duration) (Job, error)
	PendingEvents(context.Context, int) ([]Event, error)
	AcknowledgeEvent(context.Context, string) error
}

type Service

type Service struct {
	// contains filtered or unexported fields
}

func New

func New(r Repository, a Authorizer, store Store, c Config) (*Service, error)

func (*Service) Approve

func (s *Service) Approve(ctx context.Context, p Principal, id, key string) (Version, error)

func (*Service) Archive

func (s *Service) Archive(ctx context.Context, p Principal, ids []string, dst io.Writer) error

Archive streams verified entries without retaining archive bytes. A single transfer slot covers the entire package, including all entry downloads.

func (*Service) ArchiveLimits

func (s *Service) ArchiveLimits() (files int, bytes int64)

ArchiveLimits returns the configured limits so a composing business service can reject an incompatible package policy before accepting user requests.

func (*Service) ArchiveManifest

func (s *Service) ArchiveManifest(ctx context.Context, p Principal, ids []string) (ArchiveManifest, error)

func (*Service) Attach

func (s *Service) Attach(ctx context.Context, p Principal, id, key string, b Binding) (Reference, error)

func (*Service) CancelUpload

func (s *Service) CancelUpload(ctx context.Context, p Principal, id, key string) (Version, error)

func (*Service) Close

func (s *Service) Close(ctx context.Context) error

func (*Service) CreateUpload

func (s *Service) CreateUpload(ctx context.Context, p Principal, key string, r UploadRequest) (Version, error)

func (*Service) DeleteFile

func (s *Service) DeleteFile(ctx context.Context, p Principal, id, key string, revision int64) (FileView, error)

func (*Service) Download

func (s *Service) Download(ctx context.Context, p Principal, id string, dst io.Writer) error

Only a currently active business reference can deliver content. Storage verifies the complete bytes before Write, then this guard rechecks access.

func (*Service) GetFile

func (s *Service) GetFile(ctx context.Context, p Principal, id string) (FileView, error)

func (*Service) GetReference

func (s *Service) GetReference(ctx context.Context, p Principal, id string) (Reference, error)

func (*Service) GetUpload

func (s *Service) GetUpload(ctx context.Context, p Principal, id string) (VersionView, error)

func (*Service) GetVersion

func (s *Service) GetVersion(ctx context.Context, p Principal, id string) (VersionView, error)

func (*Service) ListFiles

func (s *Service) ListFiles(ctx context.Context, p Principal, limit int, cursor string) (FilePage, error)

func (*Service) Publish

func (s *Service) Publish(ctx context.Context, p Principal, id, key string) (Reference, error)

func (*Service) ReferenceVersion

func (s *Service) ReferenceVersion(ctx context.Context, p Principal, id string) (Version, error)

func (*Service) Release

func (s *Service) Release(ctx context.Context, p Principal, id, key string) (Reference, error)

func (*Service) ReviewDownload

func (s *Service) ReviewDownload(ctx context.Context, p Principal, id string, dst io.Writer) error

ReviewDownload serves complete, verified bytes to a separately authorized reviewer. It never activates a reference or grants ordinary download access.

func (*Service) RunOne

func (s *Service) RunOne(ctx context.Context, worker string) (bool, error)

func (*Service) SetFileState

func (s *Service) SetFileState(ctx context.Context, p Principal, id, key string, revision int64, disabled bool) (FileView, error)

func (*Service) Sweep

func (s *Service) Sweep(ctx context.Context, n int, after string) (string, error)

Sweep only schedules bounded work. Persistent job claims coordinate instances.

func (*Service) Upload

func (s *Service) Upload(ctx context.Context, p Principal, id, key string, src io.Reader) (Version, error)

type Store

Store is fulfilled by the storage public service. The distinct service actor must have explicit grants; end users never inherit those grants.

type Transaction

type Transaction interface {
	ReuseCandidates(Principal, UploadRequest) ([]Version, error)
	Lock(string) error
	File(string) (File, error)
	Files(Principal, int, string) ([]FileListing, error)
	SaveFile(File) error
	Version(string) (Version, error)
	Upload(string) (Version, error)
	Reference(string) (Version, Reference, error)
	Versions(string) ([]Version, error)
	ScanVersions(int, string) ([]Version, error)
	SaveVersion(Version) error
	Counts(Principal) (files, open int, err error)
	Replay(string) (Replay, error)
	SaveReplay(Replay) error
	Job(string) (Job, error)
	SaveJob(Job) error
	Event(Event) error
}

type UploadRequest

type UploadRequest struct {
	ReuseExisting bool   `json:"reuse_existing,omitempty"`
	FileID        string `json:"file_id,omitempty"`
	Purpose       string `json:"purpose"`
	Filename      string `json:"filename"`
	ContentType   string `json:"content_type"`
	Size          int64  `json:"size"`
	SHA256        string `json:"sha256"`
}

type Version

type Version struct {
	Reused          bool      `json:"reused,omitempty"`
	Shared          bool      `json:"-"`
	ReuseSource     string    `json:"-"`
	ReuseObject     string    `json:"-"`
	ID              string    `json:"id"`
	FileID          string    `json:"file_id"`
	Number          int       `json:"number"`
	Revision        int64     `json:"revision"`
	UploadID        string    `json:"upload_id"`
	Filename        string    `json:"filename"`
	ContentType     string    `json:"content_type"`
	Size            int64     `json:"size"`
	SHA256          string    `json:"sha256"`
	State           string    `json:"state"`
	Approved        bool      `json:"approved"`
	ExpiresAt       time.Time `json:"upload_expires_at"`
	RetainUntil     time.Time `json:"retain_until"`
	CleanupComplete bool      `json:"cleanup_complete"`
	ErrorCode       string    `json:"error_code"`
	// Persistence uses Record so internal locations never enter public JSON.
	ObjectID         string      `json:"-"`
	ObjectIssued     bool        `json:"-"`
	TypeValidated    bool        `json:"-"`
	UploadGeneration int64       `json:"-"`
	UploadUntil      time.Time   `json:"-"`
	Profile          Profile     `json:"-"`
	References       []Reference `json:"-"`
}

type VersionView

type VersionView struct {
	Version
	StorageState string `json:"storage_state"`
	Healthy      int    `json:"healthy"`
	Desired      int    `json:"desired"`
}

Directories

Path Synopsis
Package httpapi exposes authenticated file operations without provider details.
Package httpapi exposes authenticated file operations without provider details.
Package postgres owns media metadata and migrations, using a separate role.
Package postgres owns media metadata and migrations, using a separate role.
Package sdk provides primitives to interact with the openapi HTTP API.
Package sdk provides primitives to interact with the openapi HTTP API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL