- 全项目版本号统一:v3.x 迭代号(26.9/26.9/26.9/26.9 及裸 v2/v3)→ 26.9, 覆盖 Go 注释 / 文档 / openapi.yaml / README×4 / 前端源码(80+ 处) - v31_test.go 更名 custom_code_test.go;TestV2AccessorDefaults → TestKVAccessorDefaults - docs/api/00-overview.md 更新日志合并为单条 26.9 条目(修复错位拼接) - .goreleaser.yaml 头部注释与实际一致(Pro 2.18.1 / GITEA_TOKEN / semver tag 要求) - CI:release-image.yml → ci.yml,仅保留 vet+test 门禁; 镜像发布移交 GoReleaser Pro(原 build-push 的 tag 校验与 26.9 版本方案冲突,历史 9 次失败) - 前端重建:server/web/dist 与 web-embed 同步(docs 文案嵌入更新)
This commit is contained in:
+3
-3
@@ -11,7 +11,7 @@ server/
|
||||
├── cmd/server/main.go # 入口:装配 配置→DB→缓存→设置→审计→中间件→路由
|
||||
└── internal/
|
||||
├── config/ # 配置:FCB_* 环境变量基线 + DB settings KV 运行时覆盖
|
||||
│ └── schema.go # v2 新配置键 schema(键名/类型/默认值/边界)
|
||||
│ └── schema.go # 26.9 新配置键 schema(键名/类型/默认值/边界)
|
||||
├── model/ # GORM 模型 + 双方言 AutoMigrate
|
||||
├── cache/ # 缓存统一接口:redis.go / memory.go 双实现
|
||||
├── database/ # 双方言连接与迁移(sqlite 默认 / postgres 可选)
|
||||
@@ -43,7 +43,7 @@ server/
|
||||
- 双方言共用 GORM 抽象:AutoMigrate、settings KV、全部业务查询方言无关;
|
||||
唯一原生 DDL(migrates 台账表)在 `database.createMigratesTable` 内部分支处理。
|
||||
|
||||
## v2 新增配置键(internal/config/schema.go 为单一事实来源)
|
||||
## 26.9 新增配置键(internal/config/schema.go 为单一事实来源)
|
||||
|
||||
| 键 | 类型 | 默认 | 说明 |
|
||||
|---|---|---|---|
|
||||
@@ -57,7 +57,7 @@ server/
|
||||
| `max_file_size` | int64 | `0` | 需求 ⑩:存储策略-单文件上限字节(0=回落 `uploadSize`,≤10GiB) |
|
||||
| `uploadSize` / `allowed_file_types` / `storageLimit` / `openUpload` | 既有 | - | 存储策略既有键(语义不变) |
|
||||
| `uploadCount` / `uploadMinute` | 既有 | `10` / `1` | 上传频率限制(对齐参考 `ip_limit["upload"]`) |
|
||||
| `upload_rate` / `download_rate` | int64 | `0` / `0` | **v3.2 带宽限速**:上下行字节/秒(0=不限速,≤1 GiB/s)。管理端改后立即生效(每请求动态读 KV);上传侧 middleware 包裹 `Request.Body`,下载侧在 `serveFile` 包裹 `storage.ReadCloser`;S3 预签名直传(客户端→S3)服务端无法限速。算法为时间窗对齐 sleep(`middleware/bandwidth.go`) |
|
||||
| `upload_rate` / `download_rate` | int64 | `0` / `0` | **26.9 带宽限速**:上下行字节/秒(0=不限速,≤1 GiB/s)。管理端改后立即生效(每请求动态读 KV);上传侧 middleware 包裹 `Request.Body`,下载侧在 `serveFile` 包裹 `storage.ReadCloser`;S3 预签名直传(客户端→S3)服务端无法限速。算法为时间窗对齐 sleep(`middleware/bandwidth.go`) |
|
||||
|
||||
管理端与文档(t2/t4)以 `config.KVSchema()`(`settings.KVSchema()` re-export)为元数据源;
|
||||
schema 同步测试保证 `KVSchema()` 与 `defaults()` 逐键一致。
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
"fileshare/internal/storage"
|
||||
)
|
||||
|
||||
// APP_VERSION 版本号,对齐参考仓库 VERSION。v3.2 起改为 var 以便
|
||||
// APP_VERSION 版本号,对齐参考仓库 VERSION。26.9 起改为 var 以便
|
||||
// goreleaser 通过 -ldflags -X main.APP_VERSION=… 注入发布版本。
|
||||
//
|
||||
// 编译时可选注入(goreleaser 触发),不注入则保持默认 26.9。
|
||||
@@ -105,7 +105,7 @@ func main() {
|
||||
middleware.LimitMeta: {Count: cfg.GetInt("errorCount"), Window: minutes(cfg.GetInt("errorMinute"))},
|
||||
})
|
||||
|
||||
// 7. 存储引擎:注入配置 → 构造 → 健康预检(需求 ④;v3 包装为可热切换 Manager)
|
||||
// 7. 存储引擎:注入配置 → 构造 → 健康预检(需求 ④;26.9 包装为可热切换 Manager)
|
||||
storage.SetEngineOptions(buildEngineOptions(cfg))
|
||||
bootStore, err := storage.NewEngine(ctx, cfg.Engine())
|
||||
if err != nil {
|
||||
@@ -117,7 +117,7 @@ func main() {
|
||||
} else {
|
||||
log.Printf("[boot] 存储引擎 %s 健康检查通过", cfg.Engine())
|
||||
}
|
||||
// v3:Manager 包装——保存/读取委托当前引擎;管理端可热切换
|
||||
// 26.9:Manager 包装——保存/读取委托当前引擎;管理端可热切换
|
||||
//(构建闭包在每次切换前用最新 KV 刷新 EngineOptions,参数改动即时生效)
|
||||
store := storage.NewManager(cfg.Engine(), bootStore, func(name string) (storage.Storage, error) {
|
||||
storage.SetEngineOptions(buildEngineOptions(cfg))
|
||||
|
||||
+1
-1
@@ -13,6 +13,7 @@ require (
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
golang.org/x/crypto v0.56.0
|
||||
gorm.io/driver/postgres v1.6.2
|
||||
gorm.io/gorm v1.31.2
|
||||
)
|
||||
@@ -66,7 +67,6 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
golang.org/x/arch v0.20.0 // indirect
|
||||
golang.org/x/crypto v0.56.0 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
|
||||
@@ -78,7 +78,7 @@ func registerAdmin(r *gin.Engine, d *Deps) {
|
||||
authed.PATCH("/settings/password", d.adminChangePassword)
|
||||
authed.POST("/settings/password", d.adminChangePassword)
|
||||
|
||||
// v3 存储引擎:运行时热切换(健康检查通过才生效,失败保持原引擎)
|
||||
// 26.9 存储引擎:运行时热切换(健康检查通过才生效,失败保持原引擎)
|
||||
authed.POST("/storage/switch", d.adminStorageSwitch)
|
||||
|
||||
// 审计日志查询(需求 ③;logs 为 list 的别名)
|
||||
@@ -404,7 +404,7 @@ func buildAdminFileItem(fc *model.FileCodes, now time.Time) gin.H {
|
||||
"hasDownloadLimit": fc.ExpiredCount >= 0, "has_download_limit": fc.ExpiredCount >= 0,
|
||||
"isPermanent": fc.ExpiredAt == nil && fc.ExpiredCount < 0, "is_permanent": fc.ExpiredAt == nil && fc.ExpiredCount < 0,
|
||||
"remainingDownloads": remaining, "remaining_downloads": remaining,
|
||||
"engine": fc.Engine, // v3:归属引擎(管理端展示/排查用)
|
||||
"engine": fc.Engine, // 26.9:归属引擎(管理端展示/排查用)
|
||||
}
|
||||
if fc.FileHash != nil {
|
||||
item["fileHash"] = *fc.FileHash
|
||||
@@ -784,7 +784,7 @@ func (d *Deps) adminFilePreview(c *gin.Context) {
|
||||
// ============ 配置 ============
|
||||
|
||||
// configKeys 管理端可见/可改的配置键(不含 jwt_secret;admin_token 屏蔽展示)。
|
||||
// v2 新增键(需求 ①②③④⑩):背景图、页脚、通知开关、保存/存储策略、频率限制。
|
||||
// 26.9 新增键(需求 ①②③④⑩):背景图、页脚、通知开关、保存/存储策略、频率限制。
|
||||
var configKeys = []string{
|
||||
"site_name", "name", "description", "page_explain", "keywords",
|
||||
"notify_title", "notify_content", "notify_enabled", "logo_url", "favicon_url",
|
||||
@@ -794,11 +794,11 @@ var configKeys = []string{
|
||||
"code_generate_type", "enableChunk",
|
||||
"uploadMinute", "uploadCount", "errorMinute", "errorCount",
|
||||
"loginCount", "loginMinute",
|
||||
"opacity", "background", "showAdminAddr", "robotsText", "site_domain", // v3.1:站点对外域名
|
||||
"upload_rate", "download_rate", // v3.2:上下行带宽字节/秒(0=不限速)
|
||||
"opacity", "background", "showAdminAddr", "robotsText", "site_domain", // 26.9:站点对外域名
|
||||
"upload_rate", "download_rate", // 26.9:上下行带宽字节/秒(0=不限速)
|
||||
"adminSessionExpire", "storage_path", "local_storage_path",
|
||||
"file_storage",
|
||||
// v3 存储引擎与引擎参数(热切换;凭据为敏感键,get 掩码/update 空跳过)
|
||||
// 26.9 存储引擎与引擎参数(热切换;凭据为敏感键,get 掩码/update 空跳过)
|
||||
"storage_engine",
|
||||
"local_storage_path",
|
||||
"webdav_url", "webdav_root_path", "webdav_username", "webdav_password",
|
||||
@@ -807,7 +807,7 @@ var configKeys = []string{
|
||||
}
|
||||
|
||||
// intConfigKeys 需按 schema 边界校验的整型键(adminConfigUpdate 归一化用)。
|
||||
// v1 既有键保留原语义;v2 新增键(max_file_size/max_save_count/notify_enabled)
|
||||
// v1 既有键保留原语义;26.9 新增键(max_file_size/max_save_count/notify_enabled)
|
||||
// 的边界来自 settings.KVSchema(单一事实来源在 config/schema.go)。
|
||||
var intConfigKeys = []string{
|
||||
"openUpload", "enableChunk", "showAdminAddr", "storageLimit",
|
||||
@@ -815,7 +815,7 @@ var intConfigKeys = []string{
|
||||
"loginCount", "loginMinute", "max_save_seconds", "uploadSize",
|
||||
"adminSessionExpire",
|
||||
"max_save_count", "max_file_size", "notify_enabled",
|
||||
"upload_rate", "download_rate", // v3.2
|
||||
"upload_rate", "download_rate", // 26.9
|
||||
}
|
||||
|
||||
// validateConfigValue 按 settings.KVSchema 校验单个配置值:
|
||||
@@ -905,7 +905,7 @@ func toStrSlice(v any) []string {
|
||||
}
|
||||
|
||||
// adminConfigGet 读取配置(对齐参考 get_config:admin_token 屏蔽、jwt_secret 不下发)。
|
||||
// v3:引擎凭据类敏感键返回掩码占位(前端表单"留空=不修改");storage_engine 为当前热切换后的引擎。
|
||||
// 26.9:引擎凭据类敏感键返回掩码占位(前端表单"留空=不修改");storage_engine 为当前热切换后的引擎。
|
||||
func (d *Deps) adminConfigGet(c *gin.Context) {
|
||||
cfg := d.Cfg
|
||||
out := gin.H{}
|
||||
@@ -925,7 +925,7 @@ func (d *Deps) adminConfigGet(c *gin.Context) {
|
||||
}
|
||||
// jwt_secret 永不下发
|
||||
delete(out, "jwt_secret")
|
||||
// 引擎运行时状态(v3:热切换即时生效,无需重启)
|
||||
// 引擎运行时状态(26.9:热切换即时生效,无需重启)
|
||||
out["_engine_hint"] = gin.H{
|
||||
"storage_backend": d.Store.CurrentName(),
|
||||
"engines": gin.H{"local": true, "s3": true, "webdav": true},
|
||||
@@ -973,7 +973,7 @@ func (d *Deps) adminConfigUpdate(c *gin.Context) {
|
||||
if !known {
|
||||
continue
|
||||
}
|
||||
// 类型归一 + schema 边界校验(v2:数值/字符串长度/列表键统一走 KVSchema)
|
||||
// 类型归一 + schema 边界校验(26.9:数值/字符串长度/列表键统一走 KVSchema)
|
||||
isInt := false
|
||||
for _, key := range intConfigKeys {
|
||||
if key == k {
|
||||
@@ -1067,7 +1067,7 @@ func (d *Deps) adminConfigUpdate(c *gin.Context) {
|
||||
dbPatch["jwt_secret"] = settings.GenerateJWTSecret()
|
||||
}
|
||||
|
||||
// v3.1:site_domain 规范化(http(s)://host[:port];空=用当前地址)
|
||||
// 26.9:site_domain 规范化(http(s)://host[:port];空=用当前地址)
|
||||
if raw, ok := dbPatch["site_domain"]; ok {
|
||||
sv, _ := raw.(string)
|
||||
normalized, err := normalizeSiteDomain(sv)
|
||||
@@ -1078,7 +1078,7 @@ func (d *Deps) adminConfigUpdate(c *gin.Context) {
|
||||
dbPatch["site_domain"] = normalized
|
||||
}
|
||||
|
||||
// v3 引擎键处理:参数键与 storage_engine 分离。
|
||||
// 26.9 引擎键处理:参数键与 storage_engine 分离。
|
||||
// 1) storage_engine 只接受合法枚举;
|
||||
// 2) 敏感凭据键空串/掩码=不修改(避免管理端表单回显把密钥抹掉);
|
||||
// 3) 先持久化普通键+参数键 → Invalidate 对应引擎缓存 → 再尝试 Switch 新引擎;
|
||||
@@ -1152,7 +1152,7 @@ func engineOfParamKeys(patch map[string]any) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// adminStorageSwitch v3 存储引擎热切换:{engine:"local"|"s3"|"webdav"}。
|
||||
// adminStorageSwitch 26.9 存储引擎热切换:{engine:"local"|"s3"|"webdav"}。
|
||||
// 成功:持久化 storage_engine KV 并返回当前引擎;失败:503 且原引擎不变。
|
||||
func (d *Deps) adminStorageSwitch(c *gin.Context) {
|
||||
var body struct {
|
||||
@@ -1302,7 +1302,7 @@ func (d *Deps) fileByID(c *gin.Context, id int64) (*model.FileCodes, error) {
|
||||
// deleteFileCode 删除分享记录与存储文件(文本分享无存储文件)。
|
||||
func (d *Deps) deleteFileCode(c *gin.Context, fc *model.FileCodes) error {
|
||||
if fc.Text == nil && fc.FilePath != nil && fc.UUIDFileName != nil {
|
||||
// v3:删除走文件归属引擎(旧引擎里的文件也要能删掉)
|
||||
// 26.9:删除走文件归属引擎(旧引擎里的文件也要能删掉)
|
||||
if delStore, dErr := d.storeFor(fc.Engine); dErr == nil {
|
||||
if err := delStore.DeleteFile(c.Request.Context(), fileSavePath(fc)); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
||||
return errInternal("存储文件删除失败: " + err.Error())
|
||||
|
||||
@@ -99,7 +99,7 @@ func (d *Deps) chunkInit(c *gin.Context) {
|
||||
// 服务端按分片数上限校验总大小(防分片声明绕过)
|
||||
totalChunks := (req.FileSize + chunkSize - 1) / chunkSize
|
||||
maxPossible := totalChunks * chunkSize
|
||||
// v2 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
||||
// 26.9 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
||||
if err := d.CurrentUploadPolicy().CheckSize(maxPossible); err != nil {
|
||||
auditUploadEntry(c, "", safeName, req.FileSize, 0)
|
||||
auditRecordFailed(c, d.AuditSvc, "文件大小超过限制")
|
||||
@@ -165,7 +165,7 @@ func (d *Deps) chunkInit(c *gin.Context) {
|
||||
ChunkHash: req.FileHash,
|
||||
FileName: safeName,
|
||||
SavePath: savePath,
|
||||
Engine: d.Store.CurrentName(), // v3:会话归属引擎(分片/合并全程走同一引擎)
|
||||
Engine: d.Store.CurrentName(), // 26.9:会话归属引擎(分片/合并全程走同一引擎)
|
||||
}
|
||||
if err := d.DB.WithContext(ctx).Create(&session).Error; err != nil {
|
||||
releaseStorage(ctx, d.DB, resToken)
|
||||
@@ -389,7 +389,7 @@ func (d *Deps) saveOneChunk(c *gin.Context, ctx context.Context, session *model.
|
||||
ChunkSize: session.ChunkSize,
|
||||
FileName: session.FileName,
|
||||
SavePath: session.SavePath,
|
||||
Engine: session.Engine, // v3:继承会话引擎
|
||||
Engine: session.Engine, // 26.9:继承会话引擎
|
||||
}
|
||||
if err := d.DB.WithContext(ctx).
|
||||
Where("upload_id = ? AND chunk_index = ?", session.UploadID, idx).
|
||||
@@ -444,7 +444,7 @@ func (d *Deps) chunkStatus(c *gin.Context) {
|
||||
type chunkCompleteRequest struct {
|
||||
ExpireValue int `json:"expire_value" form:"expire_value"`
|
||||
ExpireStyle string `json:"expire_style" form:"expire_style"`
|
||||
Code string `json:"code" form:"code"` // v3.1:自定义提取码(4-8 位字母数字,空=随机)
|
||||
Code string `json:"code" form:"code"` // 26.9:自定义提取码(4-8 位字母数字,空=随机)
|
||||
}
|
||||
|
||||
// chunkComplete 合并分片并创建分享(对齐参考 complete_upload)。
|
||||
@@ -489,7 +489,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
||||
respondError(c, err)
|
||||
return
|
||||
}
|
||||
// v3.1:自定义提取码(合并前校验,失败快速返回)
|
||||
// 26.9:自定义提取码(合并前校验,失败快速返回)
|
||||
if err := validatePickupCode(req.Code); err != nil {
|
||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
||||
@@ -539,7 +539,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
||||
}
|
||||
return rec.ChunkHash, nil
|
||||
}
|
||||
// v3:合并走会话归属引擎(会话创建时的引擎,即使中途热切换也不受影响)
|
||||
// 26.9:合并走会话归属引擎(会话创建时的引擎,即使中途热切换也不受影响)
|
||||
mergeStore, sErr := d.storeFor(session.Engine)
|
||||
if sErr != nil {
|
||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, session.FileSize)
|
||||
@@ -556,7 +556,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建分享记录(v3.1:支持自定义提取码)
|
||||
// 创建分享记录(26.9:支持自定义提取码)
|
||||
code, err := pickCustomCode(ctx, d.DB, d.Cfg, req.Code)
|
||||
if err == nil {
|
||||
fc := model.FileCodes{
|
||||
@@ -568,7 +568,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
||||
ExpiredAt: exp.ExpiredAt,
|
||||
ExpiredCount: exp.ExpiredCount,
|
||||
UsedCount: exp.UsedCount,
|
||||
Engine: session.Engine, // v3:归属引擎戳
|
||||
Engine: session.Engine, // 26.9:归属引擎戳
|
||||
}
|
||||
// 拆分路径与文件名(对齐参考:path=dirname(save_path), uuid=basename)
|
||||
dir, name := splitDirBase(session.SavePath)
|
||||
@@ -578,7 +578,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
||||
fc.Prefix = trimExt(name)
|
||||
fc.Suffix = ext
|
||||
err = d.DB.WithContext(ctx).Create(&fc).Error
|
||||
err = mapCodeConflict(err) // v3.1
|
||||
err = mapCodeConflict(err) // 26.9
|
||||
}
|
||||
if err == nil {
|
||||
// 成功:清理分片与记录(走归属引擎)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package api
|
||||
|
||||
// v3.1:自定义提取码与站点域名单元测试。
|
||||
// 26.9:自定义提取码与站点域名单元测试。
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// postForm 以 urlencoded 表单调用 handler(v3.1 测试辅助)。
|
||||
// postForm 以 urlencoded 表单调用 handler(26.9 测试辅助)。
|
||||
func postForm(d *Deps, path string, fields map[string]string) *httptest.ResponseRecorder {
|
||||
form := url.Values{}
|
||||
for k, v := range fields {
|
||||
@@ -125,7 +125,7 @@ func generateCode(style string) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// ============ 自定义提取码(v3.1,防撞库)============
|
||||
// ============ 自定义提取码(26.9,防撞库)============
|
||||
|
||||
const (
|
||||
// pickupCodeMinLen 最小长度:L3 由 4 提升至 5(4 位码空间仅 168 万,
|
||||
@@ -172,7 +172,7 @@ func pickCustomCode(ctx context.Context, db *gorm.DB, cfg *config.Config, custom
|
||||
return custom, nil
|
||||
}
|
||||
|
||||
// mapCodeConflict v3.1:分享记录创建失败时,若是自定义码唯一索引冲突(并发兜底,
|
||||
// mapCodeConflict 26.9:分享记录创建失败时,若是自定义码唯一索引冲突(并发兜底,
|
||||
// pickCustomCode 的预查重未覆盖竞态),转为友好 400;其余错误原样返回。
|
||||
func mapCodeConflict(err error) error {
|
||||
if err == nil {
|
||||
@@ -188,9 +188,9 @@ func mapCodeConflict(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ============ 站点对外域名(v3.1)============
|
||||
// ============ 站点对外域名(26.9)============
|
||||
|
||||
// SiteDomain 站点对外域名规范化(v3.1):空串合法(分享链接用当前访问地址)。
|
||||
// SiteDomain 站点对外域名规范化(26.9):空串合法(分享链接用当前访问地址)。
|
||||
// 接受 http(s)://host[:port] 或裸 host[:port](自动补 http://,内网场景)。
|
||||
// 拒绝路径/查询/片段/用户信息/非 http(s) 协议/非法主机字符(防 javascript: 注入分享链接)。
|
||||
var siteDomainHostRe = regexp.MustCompile(`^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$`)
|
||||
@@ -275,7 +275,7 @@ type expireResult struct {
|
||||
|
||||
// resolveExpire 校验 expire_style 白名单并计算过期信息。
|
||||
// 对齐参考:max_save_seconds>0 时为最长保存上限(超限 403),否则默认 7 天上限;
|
||||
// v2 需求 ④:style=count 时 expire_value 不得超出 max_save_count(0=不限制,超限 403)。
|
||||
// 26.9 需求 ④:style=count 时 expire_value 不得超出 max_save_count(0=不限制,超限 403)。
|
||||
func resolveExpire(cfg *config.Config, expireValue int, expireStyle string) (*expireResult, error) {
|
||||
allowed := cfg.ExpireStyle()
|
||||
okStyle := false
|
||||
@@ -358,7 +358,7 @@ func formatDurationCN(d time.Duration) string {
|
||||
|
||||
// ============ 存储路径 / 容量预留 ============
|
||||
|
||||
// storeFor v3:按归属引擎取存储实例(空戳/未知名回落当前引擎,兼容历史数据)。
|
||||
// storeFor 26.9:按归属引擎取存储实例(空戳/未知名回落当前引擎,兼容历史数据)。
|
||||
func (d *Deps) storeFor(engine string) (storage.Storage, error) {
|
||||
if engine == "" || !storage.ValidEngine(engine) {
|
||||
return d.Store, nil
|
||||
@@ -656,7 +656,7 @@ func readMultipartHeader(f multipart.File, n int64) []byte {
|
||||
// ============ 杂项 ============
|
||||
|
||||
// humanSize 把字节数转成人类可读描述(B/KB/MB/GB 自适应;
|
||||
// v2 需求④:max_file_size 支持子 MB 上限,固定 MB 格式会显示 0.00 MB)。
|
||||
// 26.9 需求④:max_file_size 支持子 MB 上限,固定 MB 格式会显示 0.00 MB)。
|
||||
func humanSize(n int64) string {
|
||||
const kb, mb, gb = int64(1024), int64(1024 * 1024), int64(1024 * 1024 * 1024)
|
||||
switch {
|
||||
@@ -728,7 +728,7 @@ func bindJSONOrForm(c *gin.Context, obj any) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// v3.1.1 兼容归一化:旧前端 bundle(fetch 字符串 body 默认 text/plain)发的
|
||||
// 26.9 兼容归一化:旧前端 bundle(fetch 字符串 body 默认 text/plain)发的
|
||||
// 是 text/plain + urlencoded 格式。此类请求改写 Content-Type 后走表单绑定,
|
||||
// 否则 ShouldBind 对 text/plain 不解析,非空字段全部丢失。
|
||||
base := ct
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// policy.go — v2 上传策略统一读取与校验(需求 ④⑩)。
|
||||
// policy.go — 26.9 上传策略统一读取与校验(需求 ④⑩)。
|
||||
//
|
||||
// 管理端在后台设置页修改策略(settings KV,t1 schema)后,上传链路
|
||||
// (share/file、chunk、presign)每次请求实时读取当前策略并动态校验:
|
||||
|
||||
@@ -55,7 +55,7 @@ func newPolicyTestDeps(t *testing.T) *Deps {
|
||||
if err != nil {
|
||||
t.Fatalf("storage.NewLocalStorage: %v", err)
|
||||
}
|
||||
// v3:包装为 Manager(build 直接返回 local 实例,测试无需真实多引擎)
|
||||
// 26.9:包装为 Manager(build 直接返回 local 实例,测试无需真实多引擎)
|
||||
storeMgr := storage.NewManager("local", store, func(string) (storage.Storage, error) {
|
||||
return storage.NewLocalStorage(filepath.Join(dir, "storage"))
|
||||
})
|
||||
@@ -141,13 +141,13 @@ func respBody(t *testing.T, w *httptest.ResponseRecorder) (code int, data map[st
|
||||
// pngMagic 最小合法 PNG 头(magic 校验可识别)。
|
||||
var pngMagic = []byte{0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'}
|
||||
|
||||
// ============ ① 公开 config:v2 展示与策略字段下发 ============
|
||||
// ============ ① 公开 config:26.9 展示与策略字段下发 ============
|
||||
|
||||
// TestPublicConfigV2Fields 验证 /api/v1/config 下发背景/页脚/备案/通知与策略范围,
|
||||
// 且响应不包含任何敏感键(admin_token/jwt_secret)。
|
||||
func TestPublicConfigV2Fields(t *testing.T) {
|
||||
d := newPolicyTestDeps(t)
|
||||
// 管理端先设置 v2 展示字段
|
||||
// 管理端先设置 26.9 展示字段
|
||||
if w := patchConfig(d, map[string]any{
|
||||
"background_url": "https://cdn.example.com/bg.png",
|
||||
"footer_text": "自定义页脚内容",
|
||||
@@ -204,9 +204,9 @@ func TestPublicConfigV2Fields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// ============ ② 管理端 get/update:v2 键全链路 + 类型范围校验 ============
|
||||
// ============ ② 管理端 get/update:26.9 键全链路 + 类型范围校验 ============
|
||||
|
||||
// TestAdminConfigV2RoundTrip 验证 v2 新键 update → get → public 的往返,
|
||||
// TestAdminConfigV2RoundTrip 验证 26.9 新键 update → get → public 的往返,
|
||||
// 且 admin_token 屏蔽、jwt_secret 不下发。
|
||||
func TestAdminConfigV2RoundTrip(t *testing.T) {
|
||||
d := newPolicyTestDeps(t)
|
||||
@@ -480,7 +480,7 @@ func TestPolicySnapshotMatchesConfig(t *testing.T) {
|
||||
// 编译期保证 fmt 被使用(测试辅助函数中错误路径占位)。
|
||||
var _ = fmt.Sprintf
|
||||
|
||||
// ============ v3 存储引擎热切换 ============
|
||||
// ============ 26.9 存储引擎热切换 ============
|
||||
|
||||
// switchEngine 调用 POST /admin/storage/switch。
|
||||
func switchEngine(d *Deps, engine string) *httptest.ResponseRecorder {
|
||||
|
||||
@@ -51,7 +51,7 @@ type presignInitRequest struct {
|
||||
FileSize int64 `json:"file_size" form:"file_size"`
|
||||
ExpireValue int `json:"expire_value" form:"expire_value"`
|
||||
ExpireStyle string `json:"expire_style" form:"expire_style"`
|
||||
Code string `json:"code" form:"code"` // v3.1:自定义提取码(init 时校验,完成时落库)
|
||||
Code string `json:"code" form:"code"` // 26.9:自定义提取码(init 时校验,完成时落库)
|
||||
}
|
||||
|
||||
// presignInit 初始化预签名上传(对齐参考 presign_upload_init):
|
||||
@@ -74,7 +74,7 @@ func (d *Deps) presignInit(c *gin.Context) {
|
||||
response.Fail(c, http.StatusBadRequest, "文件名非法")
|
||||
return
|
||||
}
|
||||
// v3.1:自定义提取码提前校验(init 时快速失败;完成请求须再次携带)
|
||||
// 26.9:自定义提取码提前校验(init 时快速失败;完成请求须再次携带)
|
||||
if err := validatePickupCode(req.Code); err != nil {
|
||||
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
||||
respondError(c, err)
|
||||
@@ -86,7 +86,7 @@ func (d *Deps) presignInit(c *gin.Context) {
|
||||
respondError(c, err)
|
||||
return
|
||||
}
|
||||
// v2 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
||||
// 26.9 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
||||
if err := d.CurrentUploadPolicy().CheckSize(req.FileSize); err != nil {
|
||||
auditUploadEntry(c, "", safeName, req.FileSize, 0)
|
||||
auditRecordFailed(c, d.AuditSvc, "文件大小超过限制")
|
||||
@@ -146,7 +146,7 @@ func (d *Deps) presignInit(c *gin.Context) {
|
||||
FileSize: req.FileSize,
|
||||
SavePath: savePath,
|
||||
Mode: mode,
|
||||
Engine: d.Store.CurrentName(), // v3:会话归属引擎
|
||||
Engine: d.Store.CurrentName(), // 26.9:会话归属引擎
|
||||
ExpireValue: req.ExpireValue,
|
||||
ExpireStyle: req.ExpireStyle,
|
||||
ExpiresAt: time.Now().Add(presignSessionExpires * time.Second),
|
||||
@@ -195,7 +195,7 @@ func (d *Deps) presignProxy(c *gin.Context) {
|
||||
respondError(c, err)
|
||||
return
|
||||
}
|
||||
// v3.1:自定义提取码随代理上传表单携带(init 时已预校验)
|
||||
// 26.9:自定义提取码随代理上传表单携带(init 时已预校验)
|
||||
if err := validatePickupCode(c.PostForm("code")); err != nil {
|
||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
||||
@@ -234,7 +234,7 @@ func (d *Deps) presignProxy(c *gin.Context) {
|
||||
if err == nil {
|
||||
defer func() { _ = f.Close() }()
|
||||
if err = validateFileMagic(d.Cfg, session.FileName, fh.Header.Get("Content-Type"), readMultipartHeader(f, 64)); err == nil {
|
||||
// v3:落盘走会话归属引擎
|
||||
// 26.9:落盘走会话归属引擎
|
||||
var ps storage.Storage
|
||||
ps, sErr := d.storeFor(session.Engine)
|
||||
if sErr != nil {
|
||||
@@ -259,7 +259,7 @@ func (d *Deps) presignProxy(c *gin.Context) {
|
||||
releaseStorage(ctx, d.DB, "presign:"+uploadID)
|
||||
if err != nil {
|
||||
if ps, sErr := d.storeFor(session.Engine); sErr == nil {
|
||||
_ = ps.DeleteFile(ctx, session.SavePath) // v3:清理走归属引擎
|
||||
_ = ps.DeleteFile(ctx, session.SavePath) // 26.9:清理走归属引擎
|
||||
}
|
||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||
auditRecordFailed(c, d.AuditSvc, "创建分享失败")
|
||||
@@ -299,7 +299,7 @@ func (d *Deps) presignConfirm(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// v3:直传文件存在性按会话归属引擎检查(直传可能落在旧引擎)
|
||||
// 26.9:直传文件存在性按会话归属引擎检查(直传可能落在旧引擎)
|
||||
psCheck, sErr := d.storeFor(session.Engine)
|
||||
if sErr != nil {
|
||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||
@@ -307,7 +307,7 @@ func (d *Deps) presignConfirm(c *gin.Context) {
|
||||
respondError(c, mapStorageError(sErr))
|
||||
return
|
||||
}
|
||||
// v3.1:自定义提取码随确认请求携带(query 或 JSON/form body,均可选)
|
||||
// 26.9:自定义提取码随确认请求携带(query 或 JSON/form body,均可选)
|
||||
customCode := c.Query("code")
|
||||
if customCode == "" && c.Request.Body != nil && c.Request.ContentLength != 0 {
|
||||
var fin struct {
|
||||
@@ -378,7 +378,7 @@ func (d *Deps) presignConfirm(c *gin.Context) {
|
||||
releaseStorage(ctx, d.DB, "presign:"+uploadID)
|
||||
if err != nil {
|
||||
if ps, sErr := d.storeFor(session.Engine); sErr == nil {
|
||||
_ = ps.DeleteFile(ctx, session.SavePath) // v3:清理走归属引擎
|
||||
_ = ps.DeleteFile(ctx, session.SavePath) // 26.9:清理走归属引擎
|
||||
}
|
||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||
auditRecordFailed(c, d.AuditSvc, "创建分享失败")
|
||||
@@ -399,7 +399,7 @@ func (d *Deps) createRecordFromSession(c *gin.Context, session *model.PresignUpl
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// v3.1:完成请求的自定义提取码兜底校验(init 已验,防只发完成请求绕过)
|
||||
// 26.9:完成请求的自定义提取码兜底校验(init 已验,防只发完成请求绕过)
|
||||
if err := validatePickupCode(customCode); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -420,10 +420,10 @@ func (d *Deps) createRecordFromSession(c *gin.Context, session *model.PresignUpl
|
||||
ExpiredAt: exp.ExpiredAt,
|
||||
ExpiredCount: exp.ExpiredCount,
|
||||
UsedCount: exp.UsedCount,
|
||||
Engine: session.Engine, // v3:归属引擎戳
|
||||
Engine: session.Engine, // 26.9:归属引擎戳
|
||||
}
|
||||
if err := d.DB.WithContext(ctx).Create(&fc).Error; err != nil {
|
||||
return "", mapCodeConflict(err) // v3.1:并发占用自定义码 → 友好 400
|
||||
return "", mapCodeConflict(err) // 26.9:并发占用自定义码 → 友好 400
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
@@ -474,7 +474,7 @@ func (d *Deps) presignCancel(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
if session.Mode == "direct" {
|
||||
// v3:清理走会话归属引擎
|
||||
// 26.9:清理走会话归属引擎
|
||||
if ps, sErr := d.storeFor(session.Engine); sErr == nil {
|
||||
if exists, eErr := ps.FileExists(ctx, session.SavePath); eErr == nil && exists {
|
||||
_ = ps.DeleteFile(ctx, session.SavePath)
|
||||
|
||||
@@ -21,7 +21,7 @@ type Deps struct {
|
||||
Mgr *settings.Manager
|
||||
AuditSvc *audit.Service
|
||||
Limiter *middleware.RateLimiter
|
||||
Store *storage.Manager // v3:可热切换引擎管理器(实现 Storage 接口)
|
||||
Store *storage.Manager // 26.9:可热切换引擎管理器(实现 Storage 接口)
|
||||
Version string
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ func (d *Deps) robotsText(c *gin.Context) {
|
||||
c.Data(http.StatusOK, "text/plain; charset=utf-8", []byte(d.Cfg.GetString("robotsText")))
|
||||
}
|
||||
|
||||
// publicConfig 公共配置(前端首页/上传页所需;v2 需求 ①②③④⑩ 扩展):
|
||||
// publicConfig 公共配置(前端首页/上传页所需;26.9 需求 ①②③④⑩ 扩展):
|
||||
// - 展示字段:站点信息、Logo/favicon、背景图、页脚文案/备案号、通知;
|
||||
// - 策略范围(上传页动态渲染):大小上限、类型白名单、过期方式、保存
|
||||
// 时间/次数上限、上传频率(仅范围,不含内部实现键)。
|
||||
@@ -132,10 +132,10 @@ func (d *Deps) publicConfig(c *gin.Context) {
|
||||
// 需求 ②:页脚自定义内容与备案号
|
||||
"footer_text": cfg.FooterText(),
|
||||
"footer_beian": cfg.FooterBeian(),
|
||||
// v3:当前存储引擎名(仅名称,任何引擎参数/凭据不下发)
|
||||
// 26.9:当前存储引擎名(仅名称,任何引擎参数/凭据不下发)
|
||||
"storage_engine": d.Store.CurrentName(),
|
||||
"site_domain": d.Cfg.SiteDomain(),
|
||||
// v3.2:上下行带宽字节/秒(公开下发,0=不限速,方便管理端展示当前值)
|
||||
// 26.9:上下行带宽字节/秒(公开下发,0=不限速,方便管理端展示当前值)
|
||||
"upload_rate": d.Cfg.UploadRate(),
|
||||
"download_rate": d.Cfg.DownloadRate(),
|
||||
// 需求 ③:系统通知(开关 + 内容,前台右上角悬浮窗)
|
||||
|
||||
@@ -90,7 +90,7 @@ func (d *Deps) shareText(c *gin.Context) {
|
||||
if !requireUploadLimit(c, d.Limiter) {
|
||||
return
|
||||
}
|
||||
// v3.1 修复:JSON/表单/ultipart 统一绑定(form+json 双标签——此前仅 PostForm 时,
|
||||
// 26.9 修复:JSON/表单/ultipart 统一绑定(form+json 双标签——此前仅 PostForm 时,
|
||||
// JSON 提交会静默存成空文本并 200,取件页空白)。
|
||||
var body struct {
|
||||
Text string `json:"text" form:"text"`
|
||||
@@ -120,7 +120,7 @@ func (d *Deps) shareText(c *gin.Context) {
|
||||
if expireStyle == "" {
|
||||
expireStyle = "day"
|
||||
}
|
||||
// v3.1:自定义提取码格式校验(4-8 位字母数字,空=随机)
|
||||
// 26.9:自定义提取码格式校验(4-8 位字母数字,空=随机)
|
||||
if err := validatePickupCode(body.Code); err != nil {
|
||||
respondError(c, err)
|
||||
return
|
||||
@@ -153,11 +153,11 @@ func (d *Deps) shareText(c *gin.Context) {
|
||||
ExpiredAt: exp.ExpiredAt,
|
||||
ExpiredCount: exp.ExpiredCount,
|
||||
UsedCount: exp.UsedCount,
|
||||
Engine: d.Store.CurrentName(), // v3:归属引擎戳(文本也记录,保持一致性)
|
||||
Engine: d.Store.CurrentName(), // 26.9:归属引擎戳(文本也记录,保持一致性)
|
||||
}
|
||||
err = d.DB.WithContext(ctx).Create(&fc).Error
|
||||
}
|
||||
err = mapCodeConflict(err) // v3.1:自定义码唯一索引冲突 → 友好 400
|
||||
err = mapCodeConflict(err) // 26.9:自定义码唯一索引冲突 → 友好 400
|
||||
releaseStorage(ctx, d.DB, token)
|
||||
if err != nil {
|
||||
auditRecordFailed(c, d.AuditSvc, "文本分享创建失败")
|
||||
@@ -188,7 +188,7 @@ func (d *Deps) shareFile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
origName := fh.Filename
|
||||
// v2 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
||||
// 26.9 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
||||
if err := d.CurrentUploadPolicy().CheckSize(fh.Size); err != nil {
|
||||
auditUploadEntry(c, "", origName, fh.Size, 0)
|
||||
auditRecordFailed(c, d.AuditSvc, "大小超过限制")
|
||||
@@ -205,7 +205,7 @@ func (d *Deps) shareFile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// v3.1:自定义提取码(落盘前校验,失败快速返回不占容量预留)
|
||||
// 26.9:自定义提取码(落盘前校验,失败快速返回不占容量预留)
|
||||
if err := validatePickupCode(c.PostForm("code")); err != nil {
|
||||
auditUploadEntry(c, "", origName, fh.Size, 0)
|
||||
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
||||
@@ -257,10 +257,10 @@ func (d *Deps) shareFile(c *gin.Context) {
|
||||
ExpiredAt: exp.ExpiredAt,
|
||||
ExpiredCount: exp.ExpiredCount,
|
||||
UsedCount: exp.UsedCount,
|
||||
Engine: d.Store.CurrentName(), // v3:归属引擎戳(下载按此取回)
|
||||
Engine: d.Store.CurrentName(), // 26.9:归属引擎戳(下载按此取回)
|
||||
}
|
||||
if err = d.DB.WithContext(ctx).Create(&fc).Error; err != nil {
|
||||
err = mapCodeConflict(err) // v3.1
|
||||
err = mapCodeConflict(err) // 26.9
|
||||
// 记录创建失败:清理已落盘文件
|
||||
_ = d.Store.DeleteFile(ctx, savePath)
|
||||
}
|
||||
@@ -273,7 +273,7 @@ func (d *Deps) shareFile(c *gin.Context) {
|
||||
auditUploadEntry(c, "", origName, fh.Size, 0)
|
||||
auditRecordFailed(c, d.AuditSvc, "文件保存失败")
|
||||
if be, ok := err.(*apiError); ok && be.Status == http.StatusBadRequest {
|
||||
respondError(c, err) // v3.1:提取码冲突等业务 400 原样透出
|
||||
respondError(c, err) // 26.9:提取码冲突等业务 400 原样透出
|
||||
} else {
|
||||
respondError(c, mapStorageError(err))
|
||||
}
|
||||
@@ -544,7 +544,7 @@ func (d *Deps) serveFile(c *gin.Context, fc *model.FileCodes) {
|
||||
savePath := fileSavePath(fc)
|
||||
name := fc.Prefix + fc.Suffix
|
||||
|
||||
// v3:按文件归属引擎取回(切换引擎后旧文件仍可下载);空戳=历史数据回落当前引擎
|
||||
// 26.9:按文件归属引擎取回(切换引擎后旧文件仍可下载);空戳=历史数据回落当前引擎
|
||||
store, err := d.storeFor(fc.Engine)
|
||||
if err != nil {
|
||||
auditUploadEntry(c, fc.Code, name, fc.Size, 0)
|
||||
@@ -597,7 +597,7 @@ func (d *Deps) serveFile(c *gin.Context, fc *model.FileCodes) {
|
||||
}
|
||||
auditUploadEntry(c, fc.Code, name, total, 0)
|
||||
c.Status(status)
|
||||
// v3.2:下载带宽限速(storage.ReadCloser → 限速 reader → c.Writer)
|
||||
// 26.9:下载带宽限速(storage.ReadCloser → 限速 reader → c.Writer)
|
||||
dlReader := middleware.WrapReadCloser(dl, d.Cfg.DownloadRate())
|
||||
n, _ := io.Copy(c.Writer, dlReader)
|
||||
middleware.AuditSet(c, func(e *audit.Entry) {
|
||||
|
||||
@@ -28,7 +28,7 @@ const (
|
||||
)
|
||||
|
||||
// DefaultLogoURL / DefaultFaviconURL 默认 Logo 与 favicon(需求 ⑤):
|
||||
// v2 起默认改用前端打包的本地资源(web/src/assets/brand/logo.svg + favicon.png,
|
||||
// 26.9 起默认改用前端打包的本地资源(web/src/assets/brand/logo.svg + favicon.png,
|
||||
// 经 Vite 产出 /assets/logo-*.svg 与 /assets/favicon-*.png)。此处留空,
|
||||
// GET /api/v1/config 下发空值时前端 displayLogoUrl/displayFaviconUrl 回落到本地打包资源;
|
||||
// 管理端仍可设置任意 URL 全站替换。
|
||||
@@ -62,7 +62,7 @@ func defaults() map[string]any {
|
||||
"file_storage": "local",
|
||||
"storage_path": "",
|
||||
"storageLimit": 0,
|
||||
// v3:存储引擎运行时可配(热切换);空=沿用 Env.StorageEngine 启动值
|
||||
// 26.9:存储引擎运行时可配(热切换);空=沿用 Env.StorageEngine 启动值
|
||||
"storage_engine": "",
|
||||
"site_domain": "",
|
||||
"upload_rate": "0",
|
||||
@@ -78,7 +78,7 @@ func defaults() map[string]any {
|
||||
// 需求 ⑤:默认 Logo 与 favicon(空 = 前端使用打包的本地资源)
|
||||
"logo_url": DefaultLogoURL,
|
||||
"favicon_url": DefaultFaviconURL,
|
||||
// 需求 ①:背景图(v2 新增 background_url;background 为参考实现既有键,保留兼容)
|
||||
// 需求 ①:背景图(26.9 新增 background_url;background 为参考实现既有键,保留兼容)
|
||||
"background": "",
|
||||
"background_url": "",
|
||||
// 需求 ②:页脚自定义内容与备案号
|
||||
@@ -280,7 +280,7 @@ func (c *Config) GetBool(key string) bool {
|
||||
}
|
||||
|
||||
// GetStringSlice 取字符串切片配置。
|
||||
// UploadRate 上传带宽字节/秒(v3.2,0=不限速)。
|
||||
// UploadRate 上传带宽字节/秒(26.9,0=不限速)。
|
||||
func (c *Config) UploadRate() int {
|
||||
v := c.GetInt(KeyUploadRate)
|
||||
if v < 0 {
|
||||
@@ -289,7 +289,7 @@ func (c *Config) UploadRate() int {
|
||||
return v
|
||||
}
|
||||
|
||||
// DownloadRate 下载带宽字节/秒(v3.2,0=不限速)。
|
||||
// DownloadRate 下载带宽字节/秒(26.9,0=不限速)。
|
||||
func (c *Config) DownloadRate() int {
|
||||
v := c.GetInt(KeyDownloadRate)
|
||||
if v < 0 {
|
||||
@@ -298,7 +298,7 @@ func (c *Config) DownloadRate() int {
|
||||
return v
|
||||
}
|
||||
|
||||
// SiteDomain 站点对外域名(v3.1):空=分享链接用当前访问地址。
|
||||
// SiteDomain 站点对外域名(26.9):空=分享链接用当前访问地址。
|
||||
func (c *Config) SiteDomain() string {
|
||||
return strings.TrimRight(strings.TrimSpace(c.GetString("site_domain")), "/")
|
||||
}
|
||||
@@ -421,7 +421,7 @@ func (c *Config) AdminSessionExpireSeconds() int {
|
||||
}
|
||||
|
||||
// Engine 当前存储引擎。
|
||||
// Engine 返回当前存储引擎名:KV storage_engine 优先(v3 运行时可改),
|
||||
// Engine 返回当前存储引擎名:KV storage_engine 优先(26.9 运行时可改),
|
||||
// 空(未设置/历史数据)回落启动值 Env.StorageEngine(env 校验过的 local|s3|webdav)。
|
||||
// 枚举校验内联(避免 config→storage 反向依赖)。
|
||||
func (c *Config) Engine() string {
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
// Package config — schema.go 定义 v2 新增配置键(KV)schema:
|
||||
// Package config — schema.go 定义 26.9 新增配置键(KV)schema:
|
||||
// 键名常量、类型、默认值与取值边界。管理与 API 层(t2)按下表读写与校验,
|
||||
// 文档(t4)按本表生成说明。键名除参考实现既有 camelCase 键外,
|
||||
// v2 新增键统一 snake_case。
|
||||
// 26.9 新增键统一 snake_case。
|
||||
package config
|
||||
|
||||
// —— v2 新增/沿用键名常量(单一事实来源;settings 包会 re-export)——
|
||||
// 命名规则:v2 新增键 snake_case;与参考实现对齐的既有键保持原拼写。
|
||||
// —— 26.9 新增/沿用键名常量(单一事实来源;settings 包会 re-export)——
|
||||
// 命名规则:26.9 新增键 snake_case;与参考实现对齐的既有键保持原拼写。
|
||||
const (
|
||||
// 需求 ①:背景图
|
||||
KeyBackground = "background" // 参考实现既有键(v1 兼容保留)
|
||||
KeyBackgroundURL = "background_url" // v2 新增:背景图 URL 或上传后的访问地址(空=默认主题)
|
||||
KeyBackgroundURL = "background_url" // 26.9 新增:背景图 URL 或上传后的访问地址(空=默认主题)
|
||||
// 需求 ②:页脚
|
||||
KeyFooterText = "footer_text" // v2 新增:页脚自定义内容(纯文本或受控 HTML 片段)
|
||||
KeyFooterBeian = "footer_beian" // v2 新增:备案号(如 京ICP备2024xxxxxx号-1)
|
||||
KeyFooterText = "footer_text" // 26.9 新增:页脚自定义内容(纯文本或受控 HTML 片段)
|
||||
KeyFooterBeian = "footer_beian" // 26.9 新增:备案号(如 京ICP备2024xxxxxx号-1)
|
||||
// 需求 ③:系统通知
|
||||
KeyNotifyEnabled = "notify_enabled" // v2 新增:通知开关,1 开启 / 0 关闭
|
||||
KeyNotifyEnabled = "notify_enabled" // 26.9 新增:通知开关,1 开启 / 0 关闭
|
||||
KeyNotifyTitle = "notify_title" // 既有键:通知标题
|
||||
KeyNotifyContent = "notify_content" // 既有键:通知内容(允许 <a> 等受控 HTML)
|
||||
// 需求 ④:保存策略(上传页动态读取并在范围内选择)
|
||||
KeyMaxSaveSeconds = "max_save_seconds" // 既有键:最长保存秒数,0=不限制(仅受默认 7 天兜底)
|
||||
KeyMaxSaveCount = "max_save_count" // v2 新增:单次分享最大可取(保存)次数上限,0=不限制
|
||||
KeyMaxSaveCount = "max_save_count" // 26.9 新增:单次分享最大可取(保存)次数上限,0=不限制
|
||||
KeyExpireStyle = "expireStyle" // 既有键:允许的过期方式白名单
|
||||
// 需求 ④:上传频率限制(既有键,对齐参考 ip_limit["upload"])
|
||||
KeyUploadCount = "uploadCount" // 窗口内允许上传次数
|
||||
KeyUploadMinute = "uploadMinute" // 频率窗口(分钟)
|
||||
// 需求 ④⑩:存储策略(最大文件大小/允许类型/总容量)
|
||||
KeyUploadSize = "uploadSize" // 既有键:单文件上限(字节),参考实现语义
|
||||
KeyMaxFileSize = "max_file_size" // v2 新增:存储策略-单文件上限(字节),0=回落 uploadSize
|
||||
KeyMaxFileSize = "max_file_size" // 26.9 新增:存储策略-单文件上限(字节),0=回落 uploadSize
|
||||
KeyAllowedTypes = "allowed_file_types" // 既有键:允许类型白名单("*" 不限制)
|
||||
KeyStorageLimit = "storageLimit" // 既有键:站点总容量(字节),0=不限制
|
||||
KeyOpenUpload = "openUpload" // 既有键:游客上传开关
|
||||
// v3:存储引擎运行时可配(热切换;file_storage 为参考既有键保留兼容)
|
||||
// 26.9:存储引擎运行时可配(热切换;file_storage 为参考既有键保留兼容)
|
||||
KeyStorageEngine = "storage_engine" // 当前存储引擎:local|s3|webdav
|
||||
KeySiteDomain = "site_domain" // 站点对外域名(空=分享链接用当前地址)
|
||||
KeyUploadRate = "upload_rate" // 上传带宽字节/秒(0=不限速)
|
||||
@@ -66,7 +66,7 @@ type KVSchemaEntry struct {
|
||||
Description string // 中文说明
|
||||
}
|
||||
|
||||
// KVSchema v2 全量配置键 schema 表(含既有策略键,供管理端/文档/AI 校验)。
|
||||
// KVSchema 26.9 全量配置键 schema 表(含既有策略键,供管理端/文档/AI 校验)。
|
||||
// 注意:Default 与 config defaults() 逐一对应(schema_test 保证)。
|
||||
func KVSchema() []KVSchemaEntry {
|
||||
return []KVSchemaEntry{
|
||||
@@ -92,7 +92,7 @@ func KVSchema() []KVSchemaEntry {
|
||||
{KeyAllowedTypes, "[]string", []string{"*"}, -1, -1, "允许上传类型白名单(\"*\" 不限制)"},
|
||||
{KeyStorageLimit, "int64", int64(0), 0, -1, "站点总容量(字节),0=不限制"},
|
||||
{KeyOpenUpload, "int", 1, 0, 1, "游客上传开关:1 开 / 0 需管理员登录"},
|
||||
// —— v3 存储引擎(热切换;引擎参数键沿用 defaults() 既有键,管理端经 config get/update 读写)——
|
||||
// —— 26.9 存储引擎(热切换;引擎参数键沿用 defaults() 既有键,管理端经 config get/update 读写)——
|
||||
{KeyStorageEngine, "string", "", 0, 16, "当前存储引擎:local|s3|webdav(热切换,健康检查通过才生效;空=回落启动值 FCB_STORAGE_ENGINE)"},
|
||||
{KeySiteDomain, "string", "", 0, 256, "站点对外域名(http(s)://host[:port],不带路径;空=分享链接用当前访问地址)"},
|
||||
{KeyUploadRate, "int64", "0", 0, 1073741824, "上传带宽字节/秒(0=不限速;范围 0~1 GiB/s)"},
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestKVSchemaNoDuplicates(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestV2NewKeysPresent v2 新增键必须在 schema 与 defaults 中同时存在。
|
||||
// TestV2NewKeysPresent 26.9 新增键必须在 schema 与 defaults 中同时存在。
|
||||
func TestV2NewKeysPresent(t *testing.T) {
|
||||
def := defaults()
|
||||
newKeys := []string{
|
||||
@@ -44,13 +44,13 @@ func TestV2NewKeysPresent(t *testing.T) {
|
||||
}
|
||||
for _, k := range newKeys {
|
||||
if _, ok := def[k]; !ok {
|
||||
t.Fatalf("v2 新键 %q 缺少默认值", k)
|
||||
t.Fatalf("26.9 新键 %q 缺少默认值", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestV2AccessorDefaults v2 便捷访问器默认语义。
|
||||
func TestV2AccessorDefaults(t *testing.T) {
|
||||
// TestKVAccessorDefaults 26.9 便捷访问器默认语义。
|
||||
func TestKVAccessorDefaults(t *testing.T) {
|
||||
t.Setenv("FCB_DB_DRIVER", "sqlite")
|
||||
t.Setenv("FCB_DB_DSN", "")
|
||||
c, err := New()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package middleware
|
||||
|
||||
// v3.2:上传/下载带宽限速(字节/秒,0=不限速)。
|
||||
// 26.9:上传/下载带宽限速(字节/秒,0=不限速)。
|
||||
//
|
||||
// 设计要点:
|
||||
// - 令牌桶(token bucket):每 Read 计算自上次起累计可消费字节,
|
||||
|
||||
@@ -25,7 +25,7 @@ type FileCodes struct {
|
||||
FileHash *string `gorm:"size:64" json:"file_hash"` // SHA256
|
||||
IsChunked bool `gorm:"default:false" json:"is_chunked"`
|
||||
UploadID *string `gorm:"size:36" json:"upload_id"` // 分片上传会话 ID
|
||||
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(v3:local|s3|webdav;空=历史数据按当前引擎取)
|
||||
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(26.9:local|s3|webdav;空=历史数据按当前引擎取)
|
||||
}
|
||||
|
||||
// TableName 表名。
|
||||
@@ -55,7 +55,7 @@ type UploadChunk struct {
|
||||
SavePath string `gorm:"size:512" json:"save_path"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Completed bool `gorm:"default:false" json:"completed"`
|
||||
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(v3:分片与会话记录当时引擎,合并走同一引擎)
|
||||
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(26.9:分片与会话记录当时引擎,合并走同一引擎)
|
||||
}
|
||||
|
||||
// TableName 表名。
|
||||
@@ -84,7 +84,7 @@ type PresignUploadSession struct {
|
||||
ExpireStyle string `gorm:"size:20;default:day" json:"expire_style"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(v3:直传/代理完成走同一引擎取回)
|
||||
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(26.9:直传/代理完成走同一引擎取回)
|
||||
}
|
||||
|
||||
// TableName 表名。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Package settings — schema.go:v2 配置键 schema 常量与元数据表。
|
||||
// Package settings — schema.go:26.9 配置键 schema 常量与元数据表。
|
||||
//
|
||||
// 键名常量的单一事实来源在 internal/config/schema.go(defaults() 需引用);
|
||||
// 本文件 re-export 供 API/管理层使用,并提供「键名/类型/默认值」全量表,
|
||||
@@ -35,7 +35,7 @@ const (
|
||||
KeyAllowedTypes = config.KeyAllowedTypes
|
||||
KeyStorageLimit = config.KeyStorageLimit
|
||||
KeyOpenUpload = config.KeyOpenUpload
|
||||
// v3 存储引擎
|
||||
// 26.9 存储引擎
|
||||
KeyStorageEngine = config.KeyStorageEngine
|
||||
)
|
||||
|
||||
@@ -52,7 +52,7 @@ const (
|
||||
)
|
||||
|
||||
// 敏感键:不允许出现在管理端 config get 下发/前端可见集合中(双模式下一致生效)。
|
||||
// v3:引擎凭据(webdav_password/s3_secret_access_key/aws_session_token)加入敏感集——
|
||||
// 26.9:引擎凭据(webdav_password/s3_secret_access_key/aws_session_token)加入敏感集——
|
||||
// 管理端 get 返回掩码占位,update 时空串/掩码=不修改;公开 config 永不下发。
|
||||
var SensitiveKeys = []string{
|
||||
"admin_token", "jwt_secret",
|
||||
@@ -72,7 +72,7 @@ func IsSensitiveKey(key string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// KVSchema 返回 v2 全量配置键元数据(键名/类型/默认值/边界/说明)。
|
||||
// KVSchema 返回 26.9 全量配置键元数据(键名/类型/默认值/边界/说明)。
|
||||
// 默认值必须与 config defaults() 一致(schema 同步测试保证)。
|
||||
func KVSchema() []config.KVSchemaEntry { return config.KVSchema() }
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// Manager 存储引擎管理器:实现 Storage 全接口并支持运行时热切换。
|
||||
//
|
||||
// v3 需求:管理后台可设置存储类型(local|s3|webdav)与各引擎参数,
|
||||
// 26.9 需求:管理后台可设置存储类型(local|s3|webdav)与各引擎参数,
|
||||
// 保存后无需重启即生效。设计要点:
|
||||
// - 读写/保存类操作全部委托到"当前引擎"(原子指针,无锁热路径);
|
||||
// - Switch 先构建并健康检查新引擎,成功才替换指针,失败保持原引擎;
|
||||
|
||||
@@ -87,7 +87,7 @@ func TestSwitchSuccessAndCurrentName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSwitchFailureKeepsCurrent 健康检查失败时保持原引擎(v3 核心语义)。
|
||||
// TestSwitchFailureKeepsCurrent 健康检查失败时保持原引擎(26.9 核心语义)。
|
||||
func TestSwitchFailureKeepsCurrent(t *testing.T) {
|
||||
var s3Fail atomic.Bool
|
||||
s3Fail.Store(true) // s3 不健康
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import{d as p,u as v,i as h,I as g,c as w,G as s,b as e,t as n,f as t,A as d,w as k,F as y,O as r,q as m,B as R,o as V}from"./index-D7AAbqvI.js";import{_ as x}from"./SiteNav.vue_vue_type_script_setup_true_lang-B5OHbKDQ.js";import{u as A}from"./auth-DjiKbqje.js";import"./admin-uFxGdgNa.js";const B={class:"admin-shell"},C={class:"admin-aside"},b={class:"aside-title"},L=["aria-label"],N={class:"admin-main"},q=p({__name:"AdminLayout",setup(S){const{t:a}=v(),u=R(),o=A(),l=h();g(async()=>{o.isAuthed&&!o.checked&&await o.verify()});async function _(){await o.logout(),l.success(a("admin.nav.loggedOut")),u.replace({name:"admin-login"})}return(F,c)=>{const i=r("RouterLink"),f=r("RouterView");return V(),w(y,null,[s(x),e("div",B,[e("aside",C,[e("div",b,n(t(a)("admin.nav.title")),1),e("nav",{class:"aside-menu","aria-label":t(a)("admin.nav.menu")},[s(i,{to:{name:"admin-files"}},{default:d(()=>[m("📁 "+n(t(a)("admin.nav.files")),1)]),_:1}),s(i,{to:{name:"admin-audit"}},{default:d(()=>[m("🛡 "+n(t(a)("admin.nav.audit")),1)]),_:1}),s(i,{to:{name:"admin-settings"}},{default:d(()=>[m("⚙️ "+n(t(a)("admin.nav.settings")),1)]),_:1}),c[0]||(c[0]=e("div",{class:"aside-sep"},null,-1)),e("a",{href:"#",onClick:k(_,["prevent"])},"🚪 "+n(t(a)("admin.nav.logout")),1)],8,L)]),e("div",N,[s(f)])])],64)}}});export{q as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as p,u as v,i as h,I as g,c as w,G as s,b as e,t as n,f as t,A as d,w as k,F as y,O as r,q as m,B as R,o as V}from"./index-QLbKGtH7.js";import{_ as x}from"./SiteNav.vue_vue_type_script_setup_true_lang-8XmHL8P7.js";import{u as A}from"./auth-Yfi2oa1E.js";import"./admin-BZX1cFNW.js";const B={class:"admin-shell"},C={class:"admin-aside"},b={class:"aside-title"},L=["aria-label"],N={class:"admin-main"},q=p({__name:"AdminLayout",setup(S){const{t:a}=v(),u=R(),o=A(),l=h();g(async()=>{o.isAuthed&&!o.checked&&await o.verify()});async function _(){await o.logout(),l.success(a("admin.nav.loggedOut")),u.replace({name:"admin-login"})}return(F,c)=>{const i=r("RouterLink"),f=r("RouterView");return V(),w(y,null,[s(x),e("div",B,[e("aside",C,[e("div",b,n(t(a)("admin.nav.title")),1),e("nav",{class:"aside-menu","aria-label":t(a)("admin.nav.menu")},[s(i,{to:{name:"admin-files"}},{default:d(()=>[m("📁 "+n(t(a)("admin.nav.files")),1)]),_:1}),s(i,{to:{name:"admin-audit"}},{default:d(()=>[m("🛡 "+n(t(a)("admin.nav.audit")),1)]),_:1}),s(i,{to:{name:"admin-settings"}},{default:d(()=>[m("⚙️ "+n(t(a)("admin.nav.settings")),1)]),_:1}),c[0]||(c[0]=e("div",{class:"aside-sep"},null,-1)),e("a",{href:"#",onClick:k(_,["prevent"])},"🚪 "+n(t(a)("admin.nav.logout")),1)],8,L)]),e("div",N,[s(f)])])],64)}}});export{q as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as p,u as v,i as h,I as g,c as w,G as s,b as e,t as n,f as t,A as d,w as k,F as y,O as r,q as m,B as R,o as V}from"./index-DSPQyv0Z.js";import{_ as x}from"./SiteNav.vue_vue_type_script_setup_true_lang-DSt3Q6Wq.js";import{u as A}from"./auth-o4aHzrA6.js";import"./admin-IDpKsD_2.js";const B={class:"admin-shell"},C={class:"admin-aside"},b={class:"aside-title"},L=["aria-label"],N={class:"admin-main"},q=p({__name:"AdminLayout",setup(S){const{t:a}=v(),u=R(),o=A(),l=h();g(async()=>{o.isAuthed&&!o.checked&&await o.verify()});async function _(){await o.logout(),l.success(a("admin.nav.loggedOut")),u.replace({name:"admin-login"})}return(F,c)=>{const i=r("RouterLink"),f=r("RouterView");return V(),w(y,null,[s(x),e("div",B,[e("aside",C,[e("div",b,n(t(a)("admin.nav.title")),1),e("nav",{class:"aside-menu","aria-label":t(a)("admin.nav.menu")},[s(i,{to:{name:"admin-files"}},{default:d(()=>[m("📁 "+n(t(a)("admin.nav.files")),1)]),_:1}),s(i,{to:{name:"admin-audit"}},{default:d(()=>[m("🛡 "+n(t(a)("admin.nav.audit")),1)]),_:1}),s(i,{to:{name:"admin-settings"}},{default:d(()=>[m("⚙️ "+n(t(a)("admin.nav.settings")),1)]),_:1}),c[0]||(c[0]=e("div",{class:"aside-sep"},null,-1)),e("a",{href:"#",onClick:k(_,["prevent"])},"🚪 "+n(t(a)("admin.nav.logout")),1)],8,L)]),e("div",N,[s(f)])])],64)}}});export{q as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as p,u as v,i as h,I as g,c as w,G as s,b as e,t as n,f as t,A as d,w as k,F as y,O as r,q as m,B as R,o as V}from"./index-BKnWAKao.js";import{_ as x}from"./SiteNav.vue_vue_type_script_setup_true_lang-D3yLF4uh.js";import{u as A}from"./auth-Cp2GGuZy.js";import"./admin-KnbIpHLF.js";const B={class:"admin-shell"},C={class:"admin-aside"},b={class:"aside-title"},L=["aria-label"],N={class:"admin-main"},q=p({__name:"AdminLayout",setup(S){const{t:a}=v(),u=R(),o=A(),l=h();g(async()=>{o.isAuthed&&!o.checked&&await o.verify()});async function _(){await o.logout(),l.success(a("admin.nav.loggedOut")),u.replace({name:"admin-login"})}return(F,c)=>{const i=r("RouterLink"),f=r("RouterView");return V(),w(y,null,[s(x),e("div",B,[e("aside",C,[e("div",b,n(t(a)("admin.nav.title")),1),e("nav",{class:"aside-menu","aria-label":t(a)("admin.nav.menu")},[s(i,{to:{name:"admin-files"}},{default:d(()=>[m("📁 "+n(t(a)("admin.nav.files")),1)]),_:1}),s(i,{to:{name:"admin-audit"}},{default:d(()=>[m("🛡 "+n(t(a)("admin.nav.audit")),1)]),_:1}),s(i,{to:{name:"admin-settings"}},{default:d(()=>[m("⚙️ "+n(t(a)("admin.nav.settings")),1)]),_:1}),c[0]||(c[0]=e("div",{class:"aside-sep"},null,-1)),e("a",{href:"#",onClick:k(_,["prevent"])},"🚪 "+n(t(a)("admin.nav.logout")),1)],8,L)]),e("div",N,[s(f)])])],64)}}});export{q as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.field-sub[data-v-d049fcf9]{display:block;font-size:13px;font-weight:600;color:var(--c-text-2);margin-bottom:6px}.result-head[data-v-8bebf3d9]{display:flex;align-items:center;gap:10px;margin-bottom:14px}.result-name[data-v-8bebf3d9]{font-weight:600;word-break:break-all}.link-row[data-v-8bebf3d9]{display:flex;gap:8px}.link-row .input[data-v-8bebf3d9]{flex:1}.result-meta[data-v-8bebf3d9]{display:flex;align-items:center;gap:6px;color:var(--c-text-2);font-size:13px;flex-wrap:wrap}.code-copy[data-v-8bebf3d9]{display:block;width:100%;cursor:pointer;appearance:none;-webkit-appearance:none;transition:filter .15s}.code-copy[data-v-8bebf3d9]:hover{filter:brightness(.96)}.code-copy[data-v-8bebf3d9]:active{filter:brightness(.92)}.dropzone.disabled[data-v-1b57fe5c]{opacity:.55;cursor:not-allowed}
|
||||
-1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{d as w,u as b,a as y,z as x,A as k,b as e,f as t,t as a,w as V,C as B,D as C,c as m,g as _,q as N,j as r,N as S,B as q,o as d,H as L,_ as P}from"./index-DSPQyv0Z.js";import{P as A}from"./PageShell-Bv1Rpp4p.js";import{u as D}from"./auth-o4aHzrA6.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-DSt3Q6Wq.js";import"./admin-IDpKsD_2.js";const E={class:"card",style:{"max-width":"380px",margin:"8vh auto 0"}},I={class:"login-head"},M=["src"],R={class:"card-title"},T={class:"card-sub"},U={class:"field"},j={for:"admin-password"},z=["placeholder"],H={key:0,class:"hint",style:{color:"var(--c-danger)","margin-bottom":"12px"}},F=["disabled"],G={key:0,class:"spin","aria-hidden":"true"},J={class:"hint",style:{"margin-top":"16px"}},K=w({__name:"LoginView",setup(O){const{t:s}=b(),c=S(),g=q(),h=D(),u=y(),i=r(""),l=r(!1),n=r("");async function f(){if(!i.value){n.value=s("admin.login.required");return}l.value=!0,n.value="";try{await h.login(i.value);const o=typeof c.query.redirect=="string"?c.query.redirect:"/admin/files";g.replace(o)}catch(o){n.value=o instanceof L?o.code===401?s("admin.login.wrongPassword"):o.msg:s("admin.login.failed"),i.value=""}finally{l.value=!1}}return(o,p)=>(d(),x(A,null,{default:k(()=>[e("section",E,[e("div",I,[e("img",{src:t(u).displayLogoUrl,alt:"Logo",class:"login-logo"},null,8,M),e("h1",R,a(t(s)("admin.login.title")),1),e("p",T,a(t(s)("admin.login.subtitle",{name:t(u).displayName})),1)]),e("form",{onSubmit:V(f,["prevent"])},[e("div",U,[e("label",j,a(t(s)("admin.login.password")),1),B(e("input",{id:"admin-password","onUpdate:modelValue":p[0]||(p[0]=v=>i.value=v),class:"input",type:"password",placeholder:t(s)("admin.login.passwordPlaceholder"),autocomplete:"current-password",autofocus:""},null,8,z),[[C,i.value]])]),n.value?(d(),m("p",H,a(n.value),1)):_("",!0),e("button",{class:"btn btn-block",type:"submit",disabled:l.value},[l.value?(d(),m("span",G)):_("",!0),N(" "+a(t(s)("admin.login.submit")),1)],8,F)],32),e("p",J,a(t(s)("admin.login.hint")),1)])]),_:1}))}}),$=P(K,[["__scopeId","data-v-26e7f2a4"]]);export{$ as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as w,u as b,a as y,z as x,A as k,b as e,f as t,t as a,w as V,C as B,D as C,c as m,g as _,q as N,j as r,N as S,B as q,o as d,H as L,_ as P}from"./index-QLbKGtH7.js";import{P as A}from"./PageShell-D1DY7qw8.js";import{u as D}from"./auth-Yfi2oa1E.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-8XmHL8P7.js";import"./admin-BZX1cFNW.js";const E={class:"card",style:{"max-width":"380px",margin:"8vh auto 0"}},I={class:"login-head"},M=["src"],R={class:"card-title"},T={class:"card-sub"},U={class:"field"},j={for:"admin-password"},z=["placeholder"],H={key:0,class:"hint",style:{color:"var(--c-danger)","margin-bottom":"12px"}},F=["disabled"],G={key:0,class:"spin","aria-hidden":"true"},J={class:"hint",style:{"margin-top":"16px"}},K=w({__name:"LoginView",setup(O){const{t:s}=b(),c=S(),g=q(),h=D(),u=y(),i=r(""),l=r(!1),n=r("");async function f(){if(!i.value){n.value=s("admin.login.required");return}l.value=!0,n.value="";try{await h.login(i.value);const o=typeof c.query.redirect=="string"?c.query.redirect:"/admin/files";g.replace(o)}catch(o){n.value=o instanceof L?o.code===401?s("admin.login.wrongPassword"):o.msg:s("admin.login.failed"),i.value=""}finally{l.value=!1}}return(o,p)=>(d(),x(A,null,{default:k(()=>[e("section",E,[e("div",I,[e("img",{src:t(u).displayLogoUrl,alt:"Logo",class:"login-logo"},null,8,M),e("h1",R,a(t(s)("admin.login.title")),1),e("p",T,a(t(s)("admin.login.subtitle",{name:t(u).displayName})),1)]),e("form",{onSubmit:V(f,["prevent"])},[e("div",U,[e("label",j,a(t(s)("admin.login.password")),1),B(e("input",{id:"admin-password","onUpdate:modelValue":p[0]||(p[0]=v=>i.value=v),class:"input",type:"password",placeholder:t(s)("admin.login.passwordPlaceholder"),autocomplete:"current-password",autofocus:""},null,8,z),[[C,i.value]])]),n.value?(d(),m("p",H,a(n.value),1)):_("",!0),e("button",{class:"btn btn-block",type:"submit",disabled:l.value},[l.value?(d(),m("span",G)):_("",!0),N(" "+a(t(s)("admin.login.submit")),1)],8,F)],32),e("p",J,a(t(s)("admin.login.hint")),1)])]),_:1}))}}),$=P(K,[["__scopeId","data-v-26e7f2a4"]]);export{$ as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as w,u as b,a as y,z as x,A as k,b as e,f as t,t as a,w as V,C as B,D as C,c as m,g as _,q as N,j as r,N as S,B as q,o as d,H as L,_ as P}from"./index-D7AAbqvI.js";import{P as A}from"./PageShell-D3dyUalL.js";import{u as D}from"./auth-DjiKbqje.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-B5OHbKDQ.js";import"./admin-uFxGdgNa.js";const E={class:"card",style:{"max-width":"380px",margin:"8vh auto 0"}},I={class:"login-head"},M=["src"],R={class:"card-title"},T={class:"card-sub"},U={class:"field"},j={for:"admin-password"},z=["placeholder"],H={key:0,class:"hint",style:{color:"var(--c-danger)","margin-bottom":"12px"}},F=["disabled"],G={key:0,class:"spin","aria-hidden":"true"},J={class:"hint",style:{"margin-top":"16px"}},K=w({__name:"LoginView",setup(O){const{t:s}=b(),c=S(),g=q(),h=D(),u=y(),i=r(""),l=r(!1),n=r("");async function f(){if(!i.value){n.value=s("admin.login.required");return}l.value=!0,n.value="";try{await h.login(i.value);const o=typeof c.query.redirect=="string"?c.query.redirect:"/admin/files";g.replace(o)}catch(o){n.value=o instanceof L?o.code===401?s("admin.login.wrongPassword"):o.msg:s("admin.login.failed"),i.value=""}finally{l.value=!1}}return(o,p)=>(d(),x(A,null,{default:k(()=>[e("section",E,[e("div",I,[e("img",{src:t(u).displayLogoUrl,alt:"Logo",class:"login-logo"},null,8,M),e("h1",R,a(t(s)("admin.login.title")),1),e("p",T,a(t(s)("admin.login.subtitle",{name:t(u).displayName})),1)]),e("form",{onSubmit:V(f,["prevent"])},[e("div",U,[e("label",j,a(t(s)("admin.login.password")),1),B(e("input",{id:"admin-password","onUpdate:modelValue":p[0]||(p[0]=v=>i.value=v),class:"input",type:"password",placeholder:t(s)("admin.login.passwordPlaceholder"),autocomplete:"current-password",autofocus:""},null,8,z),[[C,i.value]])]),n.value?(d(),m("p",H,a(n.value),1)):_("",!0),e("button",{class:"btn btn-block",type:"submit",disabled:l.value},[l.value?(d(),m("span",G)):_("",!0),N(" "+a(t(s)("admin.login.submit")),1)],8,F)],32),e("p",J,a(t(s)("admin.login.hint")),1)])]),_:1}))}}),$=P(K,[["__scopeId","data-v-26e7f2a4"]]);export{$ as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as w,u as b,a as y,z as x,A as k,b as e,f as t,t as a,w as V,C as B,D as C,c as m,g as _,q as N,j as r,N as S,B as q,o as d,H as L,_ as P}from"./index-BKnWAKao.js";import{P as A}from"./PageShell-CBo29Oot.js";import{u as D}from"./auth-Cp2GGuZy.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-D3yLF4uh.js";import"./admin-KnbIpHLF.js";const E={class:"card",style:{"max-width":"380px",margin:"8vh auto 0"}},I={class:"login-head"},M=["src"],R={class:"card-title"},T={class:"card-sub"},U={class:"field"},j={for:"admin-password"},z=["placeholder"],H={key:0,class:"hint",style:{color:"var(--c-danger)","margin-bottom":"12px"}},F=["disabled"],G={key:0,class:"spin","aria-hidden":"true"},J={class:"hint",style:{"margin-top":"16px"}},K=w({__name:"LoginView",setup(O){const{t:s}=b(),c=S(),g=q(),h=D(),u=y(),i=r(""),l=r(!1),n=r("");async function f(){if(!i.value){n.value=s("admin.login.required");return}l.value=!0,n.value="";try{await h.login(i.value);const o=typeof c.query.redirect=="string"?c.query.redirect:"/admin/files";g.replace(o)}catch(o){n.value=o instanceof L?o.code===401?s("admin.login.wrongPassword"):o.msg:s("admin.login.failed"),i.value=""}finally{l.value=!1}}return(o,p)=>(d(),x(A,null,{default:k(()=>[e("section",E,[e("div",I,[e("img",{src:t(u).displayLogoUrl,alt:"Logo",class:"login-logo"},null,8,M),e("h1",R,a(t(s)("admin.login.title")),1),e("p",T,a(t(s)("admin.login.subtitle",{name:t(u).displayName})),1)]),e("form",{onSubmit:V(f,["prevent"])},[e("div",U,[e("label",j,a(t(s)("admin.login.password")),1),B(e("input",{id:"admin-password","onUpdate:modelValue":p[0]||(p[0]=v=>i.value=v),class:"input",type:"password",placeholder:t(s)("admin.login.passwordPlaceholder"),autocomplete:"current-password",autofocus:""},null,8,z),[[C,i.value]])]),n.value?(d(),m("p",H,a(n.value),1)):_("",!0),e("button",{class:"btn btn-block",type:"submit",disabled:l.value},[l.value?(d(),m("span",G)):_("",!0),N(" "+a(t(s)("admin.login.submit")),1)],8,F)],32),e("p",J,a(t(s)("admin.login.hint")),1)])]),_:1}))}}),$=P(K,[["__scopeId","data-v-26e7f2a4"]]);export{$ as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as c,u as i,z as d,A as a,b as t,t as e,f as s,G as l,q as p,O as u,o as _}from"./index-D7AAbqvI.js";import{P as m}from"./PageShell-D3dyUalL.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-B5OHbKDQ.js";const f={class:"card empty",style:{"max-width":"480px",margin:"10vh auto 0"}},h={style:{"font-weight":"600",color:"var(--c-text)"}},x={class:"hint"},F=c({__name:"NotFoundView",setup(y){const{t:o}=i();return(g,n)=>{const r=u("RouterLink");return _(),d(m,null,{default:a(()=>[t("div",f,[n[0]||(n[0]=t("div",{class:"empty-icon"},"🧭",-1)),t("p",h,e(s(o)("notFound.title")),1),t("p",x,e(s(o)("notFound.desc")),1),l(r,{class:"btn",to:"/",style:{"margin-top":"10px"}},{default:a(()=>[p(e(s(o)("notFound.back")),1)]),_:1})])]),_:1})}}});export{F as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as c,u as i,z as d,A as a,b as t,t as e,f as s,G as l,q as p,O as u,o as _}from"./index-QLbKGtH7.js";import{P as m}from"./PageShell-D1DY7qw8.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-8XmHL8P7.js";const f={class:"card empty",style:{"max-width":"480px",margin:"10vh auto 0"}},h={style:{"font-weight":"600",color:"var(--c-text)"}},x={class:"hint"},F=c({__name:"NotFoundView",setup(y){const{t:o}=i();return(g,n)=>{const r=u("RouterLink");return _(),d(m,null,{default:a(()=>[t("div",f,[n[0]||(n[0]=t("div",{class:"empty-icon"},"🧭",-1)),t("p",h,e(s(o)("notFound.title")),1),t("p",x,e(s(o)("notFound.desc")),1),l(r,{class:"btn",to:"/",style:{"margin-top":"10px"}},{default:a(()=>[p(e(s(o)("notFound.back")),1)]),_:1})])]),_:1})}}});export{F as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as c,u as i,z as d,A as a,b as t,t as e,f as s,G as l,q as p,O as u,o as _}from"./index-DSPQyv0Z.js";import{P as m}from"./PageShell-Bv1Rpp4p.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-DSt3Q6Wq.js";const f={class:"card empty",style:{"max-width":"480px",margin:"10vh auto 0"}},h={style:{"font-weight":"600",color:"var(--c-text)"}},x={class:"hint"},F=c({__name:"NotFoundView",setup(y){const{t:o}=i();return(g,n)=>{const r=u("RouterLink");return _(),d(m,null,{default:a(()=>[t("div",f,[n[0]||(n[0]=t("div",{class:"empty-icon"},"🧭",-1)),t("p",h,e(s(o)("notFound.title")),1),t("p",x,e(s(o)("notFound.desc")),1),l(r,{class:"btn",to:"/",style:{"margin-top":"10px"}},{default:a(()=>[p(e(s(o)("notFound.back")),1)]),_:1})])]),_:1})}}});export{F as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as c,u as i,z as d,A as a,b as t,t as e,f as s,G as l,q as p,O as u,o as _}from"./index-BKnWAKao.js";import{P as m}from"./PageShell-CBo29Oot.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-D3yLF4uh.js";const f={class:"card empty",style:{"max-width":"480px",margin:"10vh auto 0"}},h={style:{"font-weight":"600",color:"var(--c-text)"}},x={class:"hint"},F=c({__name:"NotFoundView",setup(y){const{t:o}=i();return(g,n)=>{const r=u("RouterLink");return _(),d(m,null,{default:a(()=>[t("div",f,[n[0]||(n[0]=t("div",{class:"empty-icon"},"🧭",-1)),t("p",h,e(s(o)("notFound.title")),1),t("p",x,e(s(o)("notFound.desc")),1),l(r,{class:"btn",to:"/",style:{"margin-top":"10px"}},{default:a(()=>[p(e(s(o)("notFound.back")),1)]),_:1})])]),_:1})}}});export{F as default};
|
||||
-267
File diff suppressed because one or more lines are too long
-267
File diff suppressed because one or more lines are too long
-267
File diff suppressed because one or more lines are too long
-267
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{d as h,u as g,a as k,c as n,G as i,b as t,Q as y,e as v,f as e,t as a,g as c,q as r,A as d,F as x,h as B,O as C,o as l,_ as b}from"./index-DSPQyv0Z.js";import{_ as w}from"./SiteNav.vue_vue_type_script_setup_true_lang-DSt3Q6Wq.js";const N={class:"site-footer"},S={class:"footer-left"},F={key:0,class:"footer-text"},P={key:1,class:"footer-beian"},T={key:2},V=["aria-label"],$=h({__name:"PageShell",setup(D){const{t:s}=g(),o=k(),m=new Date().getFullYear(),u=B(()=>!!(o.footerText.trim()||o.footerBeian.trim()));return(p,_)=>{const f=C("RouterLink");return l(),n(x,null,[i(w),t("main",{class:v(["page",{"page-wide":p.$route.meta.wide}])},[y(p.$slots,"default",{},void 0,!0)],2),t("footer",N,[t("div",S,[e(o).footerText.trim()?(l(),n("span",F,a(e(o).footerText),1)):c("",!0),e(o).footerBeian.trim()?(l(),n("span",P,a(e(o).footerBeian),1)):c("",!0),u.value?c("",!0):(l(),n("span",T,a(e(s)("footer.copyright",{year:e(m),name:e(o).displayName})),1))]),_[0]||(_[0]=t("span",{class:"footer-powered"},[r(" Powered by "),t("a",{href:"https://skymirror.top",target:"_blank",rel:"noopener noreferrer"},"SKYMirror"),r(" 26.9 ")],-1)),t("nav",{"aria-label":e(s)("footer.linkNav")},[i(f,{to:"/docs"},{default:d(()=>[r(a(e(s)("footer.docs")),1)]),_:1}),i(f,{to:"/openapi"},{default:d(()=>[r(a(e(s)("footer.openapi")),1)]),_:1}),i(f,{to:"/admin/files"},{default:d(()=>[r(a(e(s)("footer.admin")),1)]),_:1})],8,V)])],64)}}}),R=b($,[["__scopeId","data-v-80685d6f"]]);export{R as P};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as h,u as g,a as k,c as n,G as i,b as t,Q as y,e as v,f as e,t as a,g as c,q as r,A as d,F as x,h as B,O as C,o as l,_ as b}from"./index-BKnWAKao.js";import{_ as w}from"./SiteNav.vue_vue_type_script_setup_true_lang-D3yLF4uh.js";const N={class:"site-footer"},S={class:"footer-left"},F={key:0,class:"footer-text"},P={key:1,class:"footer-beian"},T={key:2},V=["aria-label"],$=h({__name:"PageShell",setup(D){const{t:s}=g(),o=k(),m=new Date().getFullYear(),u=B(()=>!!(o.footerText.trim()||o.footerBeian.trim()));return(p,_)=>{const f=C("RouterLink");return l(),n(x,null,[i(w),t("main",{class:v(["page",{"page-wide":p.$route.meta.wide}])},[y(p.$slots,"default",{},void 0,!0)],2),t("footer",N,[t("div",S,[e(o).footerText.trim()?(l(),n("span",F,a(e(o).footerText),1)):c("",!0),e(o).footerBeian.trim()?(l(),n("span",P,a(e(o).footerBeian),1)):c("",!0),u.value?c("",!0):(l(),n("span",T,a(e(s)("footer.copyright",{year:e(m),name:e(o).displayName})),1))]),_[0]||(_[0]=t("span",{class:"footer-powered"},[r(" Powered by "),t("a",{href:"https://skymirror.top",target:"_blank",rel:"noopener noreferrer"},"SKYMirror"),r(" 26.9 ")],-1)),t("nav",{"aria-label":e(s)("footer.linkNav")},[i(f,{to:"/docs"},{default:d(()=>[r(a(e(s)("footer.docs")),1)]),_:1}),i(f,{to:"/openapi"},{default:d(()=>[r(a(e(s)("footer.openapi")),1)]),_:1}),i(f,{to:"/admin/files"},{default:d(()=>[r(a(e(s)("footer.admin")),1)]),_:1})],8,V)])],64)}}}),R=b($,[["__scopeId","data-v-80685d6f"]]);export{R as P};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as h,u as g,a as k,c as n,G as i,b as t,Q as y,e as v,f as e,t as a,g as c,q as r,A as d,F as x,h as B,O as C,o as l,_ as b}from"./index-QLbKGtH7.js";import{_ as w}from"./SiteNav.vue_vue_type_script_setup_true_lang-8XmHL8P7.js";const N={class:"site-footer"},S={class:"footer-left"},F={key:0,class:"footer-text"},P={key:1,class:"footer-beian"},T={key:2},V=["aria-label"],$=h({__name:"PageShell",setup(D){const{t:s}=g(),o=k(),m=new Date().getFullYear(),u=B(()=>!!(o.footerText.trim()||o.footerBeian.trim()));return(p,_)=>{const f=C("RouterLink");return l(),n(x,null,[i(w),t("main",{class:v(["page",{"page-wide":p.$route.meta.wide}])},[y(p.$slots,"default",{},void 0,!0)],2),t("footer",N,[t("div",S,[e(o).footerText.trim()?(l(),n("span",F,a(e(o).footerText),1)):c("",!0),e(o).footerBeian.trim()?(l(),n("span",P,a(e(o).footerBeian),1)):c("",!0),u.value?c("",!0):(l(),n("span",T,a(e(s)("footer.copyright",{year:e(m),name:e(o).displayName})),1))]),_[0]||(_[0]=t("span",{class:"footer-powered"},[r(" Powered by "),t("a",{href:"https://skymirror.top",target:"_blank",rel:"noopener noreferrer"},"SKYMirror"),r(" 26.9 ")],-1)),t("nav",{"aria-label":e(s)("footer.linkNav")},[i(f,{to:"/docs"},{default:d(()=>[r(a(e(s)("footer.docs")),1)]),_:1}),i(f,{to:"/openapi"},{default:d(()=>[r(a(e(s)("footer.openapi")),1)]),_:1}),i(f,{to:"/admin/files"},{default:d(()=>[r(a(e(s)("footer.admin")),1)]),_:1})],8,V)])],64)}}}),R=b($,[["__scopeId","data-v-80685d6f"]]);export{R as P};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as h,u as g,a as k,c as n,G as i,b as t,Q as y,e as v,f as e,t as a,g as c,q as r,A as d,F as x,h as B,O as C,o as l,_ as b}from"./index-D7AAbqvI.js";import{_ as w}from"./SiteNav.vue_vue_type_script_setup_true_lang-B5OHbKDQ.js";const N={class:"site-footer"},S={class:"footer-left"},F={key:0,class:"footer-text"},P={key:1,class:"footer-beian"},T={key:2},V=["aria-label"],$=h({__name:"PageShell",setup(D){const{t:s}=g(),o=k(),m=new Date().getFullYear(),u=B(()=>!!(o.footerText.trim()||o.footerBeian.trim()));return(p,_)=>{const f=C("RouterLink");return l(),n(x,null,[i(w),t("main",{class:v(["page",{"page-wide":p.$route.meta.wide}])},[y(p.$slots,"default",{},void 0,!0)],2),t("footer",N,[t("div",S,[e(o).footerText.trim()?(l(),n("span",F,a(e(o).footerText),1)):c("",!0),e(o).footerBeian.trim()?(l(),n("span",P,a(e(o).footerBeian),1)):c("",!0),u.value?c("",!0):(l(),n("span",T,a(e(s)("footer.copyright",{year:e(m),name:e(o).displayName})),1))]),_[0]||(_[0]=t("span",{class:"footer-powered"},[r(" Powered by "),t("a",{href:"https://skymirror.top",target:"_blank",rel:"noopener noreferrer"},"SKYMirror"),r(" 26.9 ")],-1)),t("nav",{"aria-label":e(s)("footer.linkNav")},[i(f,{to:"/docs"},{default:d(()=>[r(a(e(s)("footer.docs")),1)]),_:1}),i(f,{to:"/openapi"},{default:d(()=>[r(a(e(s)("footer.openapi")),1)]),_:1}),i(f,{to:"/admin/files"},{default:d(()=>[r(a(e(s)("footer.admin")),1)]),_:1})],8,V)])],64)}}}),R=b($,[["__scopeId","data-v-80685d6f"]]);export{R as P};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as b,u as p,c as r,b as o,t as i,f as c,h,o as f}from"./index-DSPQyv0Z.js";const v={class:"pager"},x={class:"pager-info"},k=["disabled"],M=["disabled"],y=b({__name:"Pager",props:{page:{},size:{},total:{}},emits:["change"],setup(t,{emit:m}){const a=t,d=m,{t:n}=p(),s=h(()=>Math.max(1,Math.ceil(a.total/a.size)));function g(l){const e=Math.min(Math.max(1,l),s.value);e!==a.page&&d("change",e,a.size)}return(l,e)=>(f(),r("div",v,[o("span",x,i(c(n)("common.pagerInfo",{total:t.total,page:t.page,pages:s.value})),1),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page<=1,onClick:e[0]||(e[0]=u=>g(t.page-1))},i(c(n)("common.previousPage")),9,k),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page>=s.value,onClick:e[1]||(e[1]=u=>g(t.page+1))},i(c(n)("common.nextPage")),9,M)]))}});export{y as _};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as b,u as p,c as r,b as o,t as i,f as c,h,o as f}from"./index-BKnWAKao.js";const v={class:"pager"},x={class:"pager-info"},k=["disabled"],M=["disabled"],y=b({__name:"Pager",props:{page:{},size:{},total:{}},emits:["change"],setup(t,{emit:m}){const a=t,d=m,{t:n}=p(),s=h(()=>Math.max(1,Math.ceil(a.total/a.size)));function g(l){const e=Math.min(Math.max(1,l),s.value);e!==a.page&&d("change",e,a.size)}return(l,e)=>(f(),r("div",v,[o("span",x,i(c(n)("common.pagerInfo",{total:t.total,page:t.page,pages:s.value})),1),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page<=1,onClick:e[0]||(e[0]=u=>g(t.page-1))},i(c(n)("common.previousPage")),9,k),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page>=s.value,onClick:e[1]||(e[1]=u=>g(t.page+1))},i(c(n)("common.nextPage")),9,M)]))}});export{y as _};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as b,u as p,c as r,b as o,t as i,f as c,h,o as f}from"./index-D7AAbqvI.js";const v={class:"pager"},x={class:"pager-info"},k=["disabled"],M=["disabled"],y=b({__name:"Pager",props:{page:{},size:{},total:{}},emits:["change"],setup(t,{emit:m}){const a=t,d=m,{t:n}=p(),s=h(()=>Math.max(1,Math.ceil(a.total/a.size)));function g(l){const e=Math.min(Math.max(1,l),s.value);e!==a.page&&d("change",e,a.size)}return(l,e)=>(f(),r("div",v,[o("span",x,i(c(n)("common.pagerInfo",{total:t.total,page:t.page,pages:s.value})),1),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page<=1,onClick:e[0]||(e[0]=u=>g(t.page-1))},i(c(n)("common.previousPage")),9,k),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page>=s.value,onClick:e[1]||(e[1]=u=>g(t.page+1))},i(c(n)("common.nextPage")),9,M)]))}});export{y as _};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as b,u as p,c as r,b as o,t as i,f as c,h,o as f}from"./index-QLbKGtH7.js";const v={class:"pager"},x={class:"pager-info"},k=["disabled"],M=["disabled"],y=b({__name:"Pager",props:{page:{},size:{},total:{}},emits:["change"],setup(t,{emit:m}){const a=t,d=m,{t:n}=p(),s=h(()=>Math.max(1,Math.ceil(a.total/a.size)));function g(l){const e=Math.min(Math.max(1,l),s.value);e!==a.page&&d("change",e,a.size)}return(l,e)=>(f(),r("div",v,[o("span",x,i(c(n)("common.pagerInfo",{total:t.total,page:t.page,pages:s.value})),1),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page<=1,onClick:e[0]||(e[0]=u=>g(t.page-1))},i(c(n)("common.previousPage")),9,k),o("button",{class:"btn btn-ghost btn-sm",type:"button",disabled:t.page>=s.value,onClick:e[1]||(e[1]=u=>g(t.page+1))},i(c(n)("common.nextPage")),9,M)]))}});export{y as _};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as N,u as U,i as q,I,J as b,z as L,A as R,H as C,h as T,j as p,b as t,c as u,q as z,t as s,f as n,w as E,C as H,D as $,F as y,s as _,g as w,K as j,L as J,n as K,M as B,k as W,N as G,B as O,o as l}from"./index-DSPQyv0Z.js";import{P as Q}from"./PageShell-Bv1Rpp4p.js";import{g as X,p as Y,b as Z}from"./share-DQTp5ax3.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-DSt3Q6Wq.js";const ee={class:"card",style:{"max-width":"640px",margin:"12px auto 0"}},te={key:0,class:"loading-block"},ae={class:"empty"},oe={style:{"font-weight":"600",color:"var(--c-text)"}},ne={class:"hint"},se=["placeholder"],ie={class:"btn",type:"submit"},le={style:{display:"flex","align-items":"center",gap:"10px","flex-wrap":"wrap","margin-bottom":"4px"}},ue={class:"badge"},re={style:{"font-size":"16px","word-break":"break-all"}},ce={key:0,class:"hint",style:{margin:"0"}},pe={class:"hint",style:{"margin-bottom":"18px"}},de={key:0,class:"loading-block"},me={class:"text-view"},ve={style:{display:"flex",gap:"10px","margin-top":"14px","flex-wrap":"wrap"}},ye={class:"file-summary"},ke={style:{"font-weight":"600","word-break":"break-all"}},fe={class:"hint",style:{margin:"0"}},ge={key:0,style:{margin:"16px 0 6px"}},he={class:"progress"},xe={class:"hint"},_e=["disabled"],Be=N({__name:"PickupView",setup(we){const{t:e}=U(),D=G(),F=O(),k=q(),d=T(()=>String(D.params.code??"").trim().split(/\s+/)[0]),r=p("loading"),m=p(""),o=p(null),v=p(""),f=p(!1),c=p(null);async function g(){if(!d.value){r.value="error",m.value=e("pickup.emptyCode");return}r.value="loading",m.value="",o.value=null,v.value="";try{const a=await X(d.value);if(o.value=a,r.value="ready",a.isText){f.value=!0;try{v.value=await Y(d.value)}finally{f.value=!1}}}catch(a){r.value="error",m.value=a instanceof C?a.code===404?e("pickup.notFound"):a.code===423?e("home.rateLimited"):a.code===428?e("home.notInitialized"):a.msg:e("pickup.failedDefault")}}I(g),b(d,()=>{g()}),b(()=>e("pickup.emptyCode"),()=>{r.value==="error"&&m.value&&g()});async function A(){if(o.value){c.value=0;try{const{blob:a,filename:i}=await Z(d.value,x=>c.value=x);B(a,i||o.value.name||"download"),k.success(e("pickup.downloaded"))}catch(a){k.error(a instanceof C?a.msg:e("pickup.downloadFailed"))}finally{c.value=null}}}async function M(){await W(v.value)?k.success(e("pickup.copied")):k.error(e("common.copyFailed"))}function P(){if(!o.value)return;const a=new Blob([v.value],{type:"text/plain;charset=utf-8"}),i=o.value.name?.includes(".")?o.value.name:`${o.value.name||"text"}.txt`;B(a,i)}const h=p("");function S(){const a=h.value.trim();a&&F.push({name:"pickup",params:{code:a}})}const V=T(()=>{const a=o.value;return a?a.remainingDownloads===null||a.remainingDownloads===void 0||a.remainingDownloads<0?e("pickup.remainingUnlimited"):e("pickup.remainingCount",{n:a.remainingDownloads}):""});return(a,i)=>(l(),L(Q,null,{default:R(()=>[t("section",ee,[r.value==="loading"?(l(),u("div",te,[i[1]||(i[1]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.querying",{code:d.value})),1)])):r.value==="error"?(l(),u(y,{key:1},[t("div",ae,[i[2]||(i[2]=t("div",{class:"empty-icon"},"📮",-1)),t("p",oe,s(m.value||n(e)("pickup.failed")),1),t("p",ne,s(n(e)("pickup.confirmHint")),1)]),t("form",{class:"quick-pickup",style:{"margin-top":"6px","max-width":"none"},onSubmit:E(S,["prevent"])},[H(t("input",{"onUpdate:modelValue":i[0]||(i[0]=x=>h.value=x),class:"input input-mono",placeholder:n(e)("pickup.retryPlaceholder"),maxlength:"32"},null,8,se),[[$,h.value]]),t("button",ie,s(n(e)("pickup.retryButton")),1)],32)],64)):o.value?(l(),u(y,{key:2},[t("div",le,[t("span",ue,s(o.value.isText?n(e)("common.text"):n(e)("common.file")),1),t("strong",re,s(o.value.name),1),o.value.isText?w("",!0):(l(),u("span",ce,s(n(_)(o.value.size)),1))]),t("p",pe,s(V.value)+" · "+s(n(e)("pickup.expireAt",{time:o.value.expiredAt?n(j)(o.value.expiredAt):n(e)("time.permanent")}))+" · "+s(n(J)(o.value.expiredAt)),1),o.value.isText?(l(),u(y,{key:0},[f.value?(l(),u("div",de,[i[3]||(i[3]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.loadingText")),1)])):(l(),u(y,{key:1},[t("pre",me,s(v.value),1),t("div",ve,[t("button",{class:"btn",type:"button",onClick:M},s(n(e)("pickup.copyContent")),1),t("button",{class:"btn btn-ghost",type:"button",onClick:P},s(n(e)("pickup.downloadTxt")),1)])],64))],64)):(l(),u(y,{key:1},[t("div",ye,[i[4]||(i[4]=t("span",{style:{"font-size":"30px"},"aria-hidden":"true"},"📄",-1)),t("div",null,[t("div",ke,s(o.value.name),1),t("div",fe,s(n(e)("pickup.sizeUsed",{size:n(_)(o.value.size),n:o.value.usedCount})),1)])]),c.value!==null?(l(),u("div",ge,[t("div",he,[t("i",{style:K({width:`${c.value}%`})},null,4)]),t("p",xe,s(n(e)("pickup.downloading",{percent:c.value})),1)])):w("",!0),t("button",{class:"btn btn-block",type:"button",disabled:c.value!==null,onClick:A}," ⬇ "+s(n(e)("pickup.downloadFile",{size:n(_)(o.value.size)})),9,_e)],64))],64)):w("",!0)])]),_:1}))}});export{Be as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as N,u as U,i as q,I,J as b,z as L,A as R,H as C,h as T,j as p,b as t,c as u,q as z,t as s,f as n,w as E,C as H,D as $,F as y,s as _,g as w,K as j,L as J,n as K,M as B,k as W,N as G,B as O,o as l}from"./index-BKnWAKao.js";import{P as Q}from"./PageShell-CBo29Oot.js";import{g as X,p as Y,b as Z}from"./share-B-zR67vw.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-D3yLF4uh.js";const ee={class:"card",style:{"max-width":"640px",margin:"12px auto 0"}},te={key:0,class:"loading-block"},ae={class:"empty"},oe={style:{"font-weight":"600",color:"var(--c-text)"}},ne={class:"hint"},se=["placeholder"],ie={class:"btn",type:"submit"},le={style:{display:"flex","align-items":"center",gap:"10px","flex-wrap":"wrap","margin-bottom":"4px"}},ue={class:"badge"},re={style:{"font-size":"16px","word-break":"break-all"}},ce={key:0,class:"hint",style:{margin:"0"}},pe={class:"hint",style:{"margin-bottom":"18px"}},de={key:0,class:"loading-block"},me={class:"text-view"},ve={style:{display:"flex",gap:"10px","margin-top":"14px","flex-wrap":"wrap"}},ye={class:"file-summary"},ke={style:{"font-weight":"600","word-break":"break-all"}},fe={class:"hint",style:{margin:"0"}},ge={key:0,style:{margin:"16px 0 6px"}},he={class:"progress"},xe={class:"hint"},_e=["disabled"],Be=N({__name:"PickupView",setup(we){const{t:e}=U(),D=G(),F=O(),k=q(),d=T(()=>String(D.params.code??"").trim().split(/\s+/)[0]),r=p("loading"),m=p(""),o=p(null),v=p(""),f=p(!1),c=p(null);async function g(){if(!d.value){r.value="error",m.value=e("pickup.emptyCode");return}r.value="loading",m.value="",o.value=null,v.value="";try{const a=await X(d.value);if(o.value=a,r.value="ready",a.isText){f.value=!0;try{v.value=await Y(d.value)}finally{f.value=!1}}}catch(a){r.value="error",m.value=a instanceof C?a.code===404?e("pickup.notFound"):a.code===423?e("home.rateLimited"):a.code===428?e("home.notInitialized"):a.msg:e("pickup.failedDefault")}}I(g),b(d,()=>{g()}),b(()=>e("pickup.emptyCode"),()=>{r.value==="error"&&m.value&&g()});async function A(){if(o.value){c.value=0;try{const{blob:a,filename:i}=await Z(d.value,x=>c.value=x);B(a,i||o.value.name||"download"),k.success(e("pickup.downloaded"))}catch(a){k.error(a instanceof C?a.msg:e("pickup.downloadFailed"))}finally{c.value=null}}}async function M(){await W(v.value)?k.success(e("pickup.copied")):k.error(e("common.copyFailed"))}function P(){if(!o.value)return;const a=new Blob([v.value],{type:"text/plain;charset=utf-8"}),i=o.value.name?.includes(".")?o.value.name:`${o.value.name||"text"}.txt`;B(a,i)}const h=p("");function S(){const a=h.value.trim();a&&F.push({name:"pickup",params:{code:a}})}const V=T(()=>{const a=o.value;return a?a.remainingDownloads===null||a.remainingDownloads===void 0||a.remainingDownloads<0?e("pickup.remainingUnlimited"):e("pickup.remainingCount",{n:a.remainingDownloads}):""});return(a,i)=>(l(),L(Q,null,{default:R(()=>[t("section",ee,[r.value==="loading"?(l(),u("div",te,[i[1]||(i[1]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.querying",{code:d.value})),1)])):r.value==="error"?(l(),u(y,{key:1},[t("div",ae,[i[2]||(i[2]=t("div",{class:"empty-icon"},"📮",-1)),t("p",oe,s(m.value||n(e)("pickup.failed")),1),t("p",ne,s(n(e)("pickup.confirmHint")),1)]),t("form",{class:"quick-pickup",style:{"margin-top":"6px","max-width":"none"},onSubmit:E(S,["prevent"])},[H(t("input",{"onUpdate:modelValue":i[0]||(i[0]=x=>h.value=x),class:"input input-mono",placeholder:n(e)("pickup.retryPlaceholder"),maxlength:"32"},null,8,se),[[$,h.value]]),t("button",ie,s(n(e)("pickup.retryButton")),1)],32)],64)):o.value?(l(),u(y,{key:2},[t("div",le,[t("span",ue,s(o.value.isText?n(e)("common.text"):n(e)("common.file")),1),t("strong",re,s(o.value.name),1),o.value.isText?w("",!0):(l(),u("span",ce,s(n(_)(o.value.size)),1))]),t("p",pe,s(V.value)+" · "+s(n(e)("pickup.expireAt",{time:o.value.expiredAt?n(j)(o.value.expiredAt):n(e)("time.permanent")}))+" · "+s(n(J)(o.value.expiredAt)),1),o.value.isText?(l(),u(y,{key:0},[f.value?(l(),u("div",de,[i[3]||(i[3]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.loadingText")),1)])):(l(),u(y,{key:1},[t("pre",me,s(v.value),1),t("div",ve,[t("button",{class:"btn",type:"button",onClick:M},s(n(e)("pickup.copyContent")),1),t("button",{class:"btn btn-ghost",type:"button",onClick:P},s(n(e)("pickup.downloadTxt")),1)])],64))],64)):(l(),u(y,{key:1},[t("div",ye,[i[4]||(i[4]=t("span",{style:{"font-size":"30px"},"aria-hidden":"true"},"📄",-1)),t("div",null,[t("div",ke,s(o.value.name),1),t("div",fe,s(n(e)("pickup.sizeUsed",{size:n(_)(o.value.size),n:o.value.usedCount})),1)])]),c.value!==null?(l(),u("div",ge,[t("div",he,[t("i",{style:K({width:`${c.value}%`})},null,4)]),t("p",xe,s(n(e)("pickup.downloading",{percent:c.value})),1)])):w("",!0),t("button",{class:"btn btn-block",type:"button",disabled:c.value!==null,onClick:A}," ⬇ "+s(n(e)("pickup.downloadFile",{size:n(_)(o.value.size)})),9,_e)],64))],64)):w("",!0)])]),_:1}))}});export{Be as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as N,u as U,i as q,I,J as b,z as L,A as R,H as C,h as T,j as p,b as t,c as u,q as z,t as s,f as n,w as E,C as H,D as $,F as y,s as _,g as w,K as j,L as J,n as K,M as B,k as W,N as G,B as O,o as l}from"./index-D7AAbqvI.js";import{P as Q}from"./PageShell-D3dyUalL.js";import{g as X,p as Y,b as Z}from"./share-x2wQCCnt.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-B5OHbKDQ.js";const ee={class:"card",style:{"max-width":"640px",margin:"12px auto 0"}},te={key:0,class:"loading-block"},ae={class:"empty"},oe={style:{"font-weight":"600",color:"var(--c-text)"}},ne={class:"hint"},se=["placeholder"],ie={class:"btn",type:"submit"},le={style:{display:"flex","align-items":"center",gap:"10px","flex-wrap":"wrap","margin-bottom":"4px"}},ue={class:"badge"},re={style:{"font-size":"16px","word-break":"break-all"}},ce={key:0,class:"hint",style:{margin:"0"}},pe={class:"hint",style:{"margin-bottom":"18px"}},de={key:0,class:"loading-block"},me={class:"text-view"},ve={style:{display:"flex",gap:"10px","margin-top":"14px","flex-wrap":"wrap"}},ye={class:"file-summary"},ke={style:{"font-weight":"600","word-break":"break-all"}},fe={class:"hint",style:{margin:"0"}},ge={key:0,style:{margin:"16px 0 6px"}},he={class:"progress"},xe={class:"hint"},_e=["disabled"],Be=N({__name:"PickupView",setup(we){const{t:e}=U(),D=G(),F=O(),k=q(),d=T(()=>String(D.params.code??"").trim().split(/\s+/)[0]),r=p("loading"),m=p(""),o=p(null),v=p(""),f=p(!1),c=p(null);async function g(){if(!d.value){r.value="error",m.value=e("pickup.emptyCode");return}r.value="loading",m.value="",o.value=null,v.value="";try{const a=await X(d.value);if(o.value=a,r.value="ready",a.isText){f.value=!0;try{v.value=await Y(d.value)}finally{f.value=!1}}}catch(a){r.value="error",m.value=a instanceof C?a.code===404?e("pickup.notFound"):a.code===423?e("home.rateLimited"):a.code===428?e("home.notInitialized"):a.msg:e("pickup.failedDefault")}}I(g),b(d,()=>{g()}),b(()=>e("pickup.emptyCode"),()=>{r.value==="error"&&m.value&&g()});async function A(){if(o.value){c.value=0;try{const{blob:a,filename:i}=await Z(d.value,x=>c.value=x);B(a,i||o.value.name||"download"),k.success(e("pickup.downloaded"))}catch(a){k.error(a instanceof C?a.msg:e("pickup.downloadFailed"))}finally{c.value=null}}}async function M(){await W(v.value)?k.success(e("pickup.copied")):k.error(e("common.copyFailed"))}function P(){if(!o.value)return;const a=new Blob([v.value],{type:"text/plain;charset=utf-8"}),i=o.value.name?.includes(".")?o.value.name:`${o.value.name||"text"}.txt`;B(a,i)}const h=p("");function S(){const a=h.value.trim();a&&F.push({name:"pickup",params:{code:a}})}const V=T(()=>{const a=o.value;return a?a.remainingDownloads===null||a.remainingDownloads===void 0||a.remainingDownloads<0?e("pickup.remainingUnlimited"):e("pickup.remainingCount",{n:a.remainingDownloads}):""});return(a,i)=>(l(),L(Q,null,{default:R(()=>[t("section",ee,[r.value==="loading"?(l(),u("div",te,[i[1]||(i[1]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.querying",{code:d.value})),1)])):r.value==="error"?(l(),u(y,{key:1},[t("div",ae,[i[2]||(i[2]=t("div",{class:"empty-icon"},"📮",-1)),t("p",oe,s(m.value||n(e)("pickup.failed")),1),t("p",ne,s(n(e)("pickup.confirmHint")),1)]),t("form",{class:"quick-pickup",style:{"margin-top":"6px","max-width":"none"},onSubmit:E(S,["prevent"])},[H(t("input",{"onUpdate:modelValue":i[0]||(i[0]=x=>h.value=x),class:"input input-mono",placeholder:n(e)("pickup.retryPlaceholder"),maxlength:"32"},null,8,se),[[$,h.value]]),t("button",ie,s(n(e)("pickup.retryButton")),1)],32)],64)):o.value?(l(),u(y,{key:2},[t("div",le,[t("span",ue,s(o.value.isText?n(e)("common.text"):n(e)("common.file")),1),t("strong",re,s(o.value.name),1),o.value.isText?w("",!0):(l(),u("span",ce,s(n(_)(o.value.size)),1))]),t("p",pe,s(V.value)+" · "+s(n(e)("pickup.expireAt",{time:o.value.expiredAt?n(j)(o.value.expiredAt):n(e)("time.permanent")}))+" · "+s(n(J)(o.value.expiredAt)),1),o.value.isText?(l(),u(y,{key:0},[f.value?(l(),u("div",de,[i[3]||(i[3]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.loadingText")),1)])):(l(),u(y,{key:1},[t("pre",me,s(v.value),1),t("div",ve,[t("button",{class:"btn",type:"button",onClick:M},s(n(e)("pickup.copyContent")),1),t("button",{class:"btn btn-ghost",type:"button",onClick:P},s(n(e)("pickup.downloadTxt")),1)])],64))],64)):(l(),u(y,{key:1},[t("div",ye,[i[4]||(i[4]=t("span",{style:{"font-size":"30px"},"aria-hidden":"true"},"📄",-1)),t("div",null,[t("div",ke,s(o.value.name),1),t("div",fe,s(n(e)("pickup.sizeUsed",{size:n(_)(o.value.size),n:o.value.usedCount})),1)])]),c.value!==null?(l(),u("div",ge,[t("div",he,[t("i",{style:K({width:`${c.value}%`})},null,4)]),t("p",xe,s(n(e)("pickup.downloading",{percent:c.value})),1)])):w("",!0),t("button",{class:"btn btn-block",type:"button",disabled:c.value!==null,onClick:A}," ⬇ "+s(n(e)("pickup.downloadFile",{size:n(_)(o.value.size)})),9,_e)],64))],64)):w("",!0)])]),_:1}))}});export{Be as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as N,u as U,i as q,I,J as b,z as L,A as R,H as C,h as T,j as p,b as t,c as u,q as z,t as s,f as n,w as E,C as H,D as $,F as y,s as _,g as w,K as j,L as J,n as K,M as B,k as W,N as G,B as O,o as l}from"./index-QLbKGtH7.js";import{P as Q}from"./PageShell-D1DY7qw8.js";import{g as X,p as Y,b as Z}from"./share-Y37-qxxb.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-8XmHL8P7.js";const ee={class:"card",style:{"max-width":"640px",margin:"12px auto 0"}},te={key:0,class:"loading-block"},ae={class:"empty"},oe={style:{"font-weight":"600",color:"var(--c-text)"}},ne={class:"hint"},se=["placeholder"],ie={class:"btn",type:"submit"},le={style:{display:"flex","align-items":"center",gap:"10px","flex-wrap":"wrap","margin-bottom":"4px"}},ue={class:"badge"},re={style:{"font-size":"16px","word-break":"break-all"}},ce={key:0,class:"hint",style:{margin:"0"}},pe={class:"hint",style:{"margin-bottom":"18px"}},de={key:0,class:"loading-block"},me={class:"text-view"},ve={style:{display:"flex",gap:"10px","margin-top":"14px","flex-wrap":"wrap"}},ye={class:"file-summary"},ke={style:{"font-weight":"600","word-break":"break-all"}},fe={class:"hint",style:{margin:"0"}},ge={key:0,style:{margin:"16px 0 6px"}},he={class:"progress"},xe={class:"hint"},_e=["disabled"],Be=N({__name:"PickupView",setup(we){const{t:e}=U(),D=G(),F=O(),k=q(),d=T(()=>String(D.params.code??"").trim().split(/\s+/)[0]),r=p("loading"),m=p(""),o=p(null),v=p(""),f=p(!1),c=p(null);async function g(){if(!d.value){r.value="error",m.value=e("pickup.emptyCode");return}r.value="loading",m.value="",o.value=null,v.value="";try{const a=await X(d.value);if(o.value=a,r.value="ready",a.isText){f.value=!0;try{v.value=await Y(d.value)}finally{f.value=!1}}}catch(a){r.value="error",m.value=a instanceof C?a.code===404?e("pickup.notFound"):a.code===423?e("home.rateLimited"):a.code===428?e("home.notInitialized"):a.msg:e("pickup.failedDefault")}}I(g),b(d,()=>{g()}),b(()=>e("pickup.emptyCode"),()=>{r.value==="error"&&m.value&&g()});async function A(){if(o.value){c.value=0;try{const{blob:a,filename:i}=await Z(d.value,x=>c.value=x);B(a,i||o.value.name||"download"),k.success(e("pickup.downloaded"))}catch(a){k.error(a instanceof C?a.msg:e("pickup.downloadFailed"))}finally{c.value=null}}}async function M(){await W(v.value)?k.success(e("pickup.copied")):k.error(e("common.copyFailed"))}function P(){if(!o.value)return;const a=new Blob([v.value],{type:"text/plain;charset=utf-8"}),i=o.value.name?.includes(".")?o.value.name:`${o.value.name||"text"}.txt`;B(a,i)}const h=p("");function S(){const a=h.value.trim();a&&F.push({name:"pickup",params:{code:a}})}const V=T(()=>{const a=o.value;return a?a.remainingDownloads===null||a.remainingDownloads===void 0||a.remainingDownloads<0?e("pickup.remainingUnlimited"):e("pickup.remainingCount",{n:a.remainingDownloads}):""});return(a,i)=>(l(),L(Q,null,{default:R(()=>[t("section",ee,[r.value==="loading"?(l(),u("div",te,[i[1]||(i[1]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.querying",{code:d.value})),1)])):r.value==="error"?(l(),u(y,{key:1},[t("div",ae,[i[2]||(i[2]=t("div",{class:"empty-icon"},"📮",-1)),t("p",oe,s(m.value||n(e)("pickup.failed")),1),t("p",ne,s(n(e)("pickup.confirmHint")),1)]),t("form",{class:"quick-pickup",style:{"margin-top":"6px","max-width":"none"},onSubmit:E(S,["prevent"])},[H(t("input",{"onUpdate:modelValue":i[0]||(i[0]=x=>h.value=x),class:"input input-mono",placeholder:n(e)("pickup.retryPlaceholder"),maxlength:"32"},null,8,se),[[$,h.value]]),t("button",ie,s(n(e)("pickup.retryButton")),1)],32)],64)):o.value?(l(),u(y,{key:2},[t("div",le,[t("span",ue,s(o.value.isText?n(e)("common.text"):n(e)("common.file")),1),t("strong",re,s(o.value.name),1),o.value.isText?w("",!0):(l(),u("span",ce,s(n(_)(o.value.size)),1))]),t("p",pe,s(V.value)+" · "+s(n(e)("pickup.expireAt",{time:o.value.expiredAt?n(j)(o.value.expiredAt):n(e)("time.permanent")}))+" · "+s(n(J)(o.value.expiredAt)),1),o.value.isText?(l(),u(y,{key:0},[f.value?(l(),u("div",de,[i[3]||(i[3]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),z(" "+s(n(e)("pickup.loadingText")),1)])):(l(),u(y,{key:1},[t("pre",me,s(v.value),1),t("div",ve,[t("button",{class:"btn",type:"button",onClick:M},s(n(e)("pickup.copyContent")),1),t("button",{class:"btn btn-ghost",type:"button",onClick:P},s(n(e)("pickup.downloadTxt")),1)])],64))],64)):(l(),u(y,{key:1},[t("div",ye,[i[4]||(i[4]=t("span",{style:{"font-size":"30px"},"aria-hidden":"true"},"📄",-1)),t("div",null,[t("div",ke,s(o.value.name),1),t("div",fe,s(n(e)("pickup.sizeUsed",{size:n(_)(o.value.size),n:o.value.usedCount})),1)])]),c.value!==null?(l(),u("div",ge,[t("div",he,[t("i",{style:K({width:`${c.value}%`})},null,4)]),t("p",xe,s(n(e)("pickup.downloading",{percent:c.value})),1)])):w("",!0),t("button",{class:"btn btn-block",type:"button",disabled:c.value!==null,onClick:A}," ⬇ "+s(n(e)("pickup.downloadFile",{size:n(_)(o.value.size)})),9,_e)],64))],64)):w("",!0)])]),_:1}))}});export{Be as default};
|
||||
-129
File diff suppressed because one or more lines are too long
-129
File diff suppressed because one or more lines are too long
-129
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.settings-grid[data-v-11453c11]{display:grid;gap:16px}.save-row[data-v-11453c11]{display:flex;justify-content:flex-end}.notify-switch-row[data-v-11453c11]{display:flex;align-items:center;justify-content:space-between;gap:12px}.notify-switch-row label[data-v-11453c11]{margin-bottom:0}.unit-row[data-v-11453c11]{display:flex;gap:8px;align-items:center}.unit-row .input[data-v-11453c11]{flex:1}.unit-select[data-v-11453c11]{flex:0 0 110px!important}
|
||||
@@ -1 +0,0 @@
|
||||
.settings-grid[data-v-03aa64c9]{display:grid;gap:16px}.save-row[data-v-03aa64c9]{display:flex;justify-content:flex-end}.notify-switch-row[data-v-03aa64c9]{display:flex;align-items:center;justify-content:space-between;gap:12px}.notify-switch-row label[data-v-03aa64c9]{margin-bottom:0}.unit-row[data-v-03aa64c9]{display:flex;gap:8px;align-items:center}.unit-row .input[data-v-03aa64c9]{flex:1}.unit-select[data-v-03aa64c9]{flex:0 0 110px!important}
|
||||
-129
File diff suppressed because one or more lines are too long
-1
@@ -1 +0,0 @@
|
||||
import{d as z,u as E,a as x,c as i,G as B,f as t,A as m,b as a,F as _,r as v,az as S,t as r,h as d,aA as p,aB as T,O as A,o,z as M,q as V,e as b,aC as w}from"./index-QLbKGtH7.js";const D={class:"site-nav"},F=["src"],I={class:"brand-name"},O=["aria-label"],R={class:"nav-controls"},U=["aria-label"],q=["aria-checked","title","onClick"],G={class:"nav-control-icon","aria-hidden":"true"},H=["title"],K=z({__name:"SiteNav",setup($){const{t:s}=E(),c=x(),{mode:u,setMode:k}=w(),g=d(()=>[{to:"/",label:s("nav.home"),match:n=>n==="/"}]);function y(n){return n.match(location.pathname)}const C={light:"☀️",dark:"🌙",system:"💻"},f=d(()=>({light:s("theme.light"),dark:s("theme.dark"),system:s("theme.system")})),L=d(()=>p()==="zh-CN"?"中文":"English");function N(){const n=p()==="zh-CN"?"en-US":"zh-CN";T(n)}return(n,l)=>{const h=A("RouterLink");return o(),i("header",D,[B(h,{class:"brand",to:"/",title:t(s)("nav.homeTitle",{name:t(c).displayName})},{default:m(()=>[a("img",{src:t(c).displayLogoUrl,alt:"Logo",onError:l[0]||(l[0]=e=>e.target.style.visibility="hidden")},null,40,F),a("span",I,r(t(c).displayName),1)]),_:1},8,["title"]),a("nav",{class:"nav-links","aria-label":t(s)("nav.mainNav")},[(o(!0),i(_,null,v(g.value,e=>(o(),M(h,{key:e.to,to:e.to,class:b({"router-link-active":y(e)})},{default:m(()=>[V(r(e.label),1)]),_:2},1032,["to","class"]))),128))],8,O),a("div",R,[a("div",{class:"theme-seg",role:"radiogroup","aria-label":t(s)("theme.label")},[(o(!0),i(_,null,v(t(S),e=>(o(),i("button",{key:e,type:"button",role:"radio","aria-checked":t(u)===e,class:b({active:t(u)===e}),title:f.value[e],onClick:j=>t(k)(e)},[a("span",G,r(C[e]),1)],10,q))),128))],8,U),a("button",{class:"nav-control",type:"button",title:t(s)("lang.label"),onClick:N},[l[1]||(l[1]=a("span",{class:"nav-control-icon","aria-hidden":"true"},"🌐",-1)),a("span",null,r(L.value),1)],8,H)])])}}});export{K as _};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{d as z,u as E,a as x,c as i,G as B,f as t,A as m,b as a,F as _,r as v,az as S,t as r,h as d,aA as p,aB as T,O as A,o,z as M,q as V,e as b,aC as w}from"./index-D7AAbqvI.js";const D={class:"site-nav"},F=["src"],I={class:"brand-name"},O=["aria-label"],R={class:"nav-controls"},U=["aria-label"],q=["aria-checked","title","onClick"],G={class:"nav-control-icon","aria-hidden":"true"},H=["title"],K=z({__name:"SiteNav",setup($){const{t:s}=E(),c=x(),{mode:u,setMode:k}=w(),g=d(()=>[{to:"/",label:s("nav.home"),match:n=>n==="/"}]);function y(n){return n.match(location.pathname)}const C={light:"☀️",dark:"🌙",system:"💻"},f=d(()=>({light:s("theme.light"),dark:s("theme.dark"),system:s("theme.system")})),L=d(()=>p()==="zh-CN"?"中文":"English");function N(){const n=p()==="zh-CN"?"en-US":"zh-CN";T(n)}return(n,l)=>{const h=A("RouterLink");return o(),i("header",D,[B(h,{class:"brand",to:"/",title:t(s)("nav.homeTitle",{name:t(c).displayName})},{default:m(()=>[a("img",{src:t(c).displayLogoUrl,alt:"Logo",onError:l[0]||(l[0]=e=>e.target.style.visibility="hidden")},null,40,F),a("span",I,r(t(c).displayName),1)]),_:1},8,["title"]),a("nav",{class:"nav-links","aria-label":t(s)("nav.mainNav")},[(o(!0),i(_,null,v(g.value,e=>(o(),M(h,{key:e.to,to:e.to,class:b({"router-link-active":y(e)})},{default:m(()=>[V(r(e.label),1)]),_:2},1032,["to","class"]))),128))],8,O),a("div",R,[a("div",{class:"theme-seg",role:"radiogroup","aria-label":t(s)("theme.label")},[(o(!0),i(_,null,v(t(S),e=>(o(),i("button",{key:e,type:"button",role:"radio","aria-checked":t(u)===e,class:b({active:t(u)===e}),title:f.value[e],onClick:j=>t(k)(e)},[a("span",G,r(C[e]),1)],10,q))),128))],8,U),a("button",{class:"nav-control",type:"button",title:t(s)("lang.label"),onClick:N},[l[1]||(l[1]=a("span",{class:"nav-control-icon","aria-hidden":"true"},"🌐",-1)),a("span",null,r(L.value),1)],8,H)])])}}});export{K as _};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{d as z,u as E,a as x,c as i,G as B,f as t,A as m,b as a,F as _,r as v,az as S,t as r,h as d,aA as p,aB as T,O as A,o,z as M,q as V,e as b,aC as w}from"./index-BKnWAKao.js";const D={class:"site-nav"},F=["src"],I={class:"brand-name"},O=["aria-label"],R={class:"nav-controls"},U=["aria-label"],q=["aria-checked","title","onClick"],G={class:"nav-control-icon","aria-hidden":"true"},H=["title"],K=z({__name:"SiteNav",setup($){const{t:s}=E(),c=x(),{mode:u,setMode:k}=w(),g=d(()=>[{to:"/",label:s("nav.home"),match:n=>n==="/"}]);function y(n){return n.match(location.pathname)}const C={light:"☀️",dark:"🌙",system:"💻"},f=d(()=>({light:s("theme.light"),dark:s("theme.dark"),system:s("theme.system")})),L=d(()=>p()==="zh-CN"?"中文":"English");function N(){const n=p()==="zh-CN"?"en-US":"zh-CN";T(n)}return(n,l)=>{const h=A("RouterLink");return o(),i("header",D,[B(h,{class:"brand",to:"/",title:t(s)("nav.homeTitle",{name:t(c).displayName})},{default:m(()=>[a("img",{src:t(c).displayLogoUrl,alt:"Logo",onError:l[0]||(l[0]=e=>e.target.style.visibility="hidden")},null,40,F),a("span",I,r(t(c).displayName),1)]),_:1},8,["title"]),a("nav",{class:"nav-links","aria-label":t(s)("nav.mainNav")},[(o(!0),i(_,null,v(g.value,e=>(o(),M(h,{key:e.to,to:e.to,class:b({"router-link-active":y(e)})},{default:m(()=>[V(r(e.label),1)]),_:2},1032,["to","class"]))),128))],8,O),a("div",R,[a("div",{class:"theme-seg",role:"radiogroup","aria-label":t(s)("theme.label")},[(o(!0),i(_,null,v(t(S),e=>(o(),i("button",{key:e,type:"button",role:"radio","aria-checked":t(u)===e,class:b({active:t(u)===e}),title:f.value[e],onClick:j=>t(k)(e)},[a("span",G,r(C[e]),1)],10,q))),128))],8,U),a("button",{class:"nav-control",type:"button",title:t(s)("lang.label"),onClick:N},[l[1]||(l[1]=a("span",{class:"nav-control-icon","aria-hidden":"true"},"🌐",-1)),a("span",null,r(L.value),1)],8,H)])])}}});export{K as _};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{d as z,u as E,a as x,c as i,G as B,f as t,A as m,b as a,F as _,r as v,az as S,t as r,h as d,aA as p,aB as T,O as A,o,z as M,q as V,e as b,aC as w}from"./index-DSPQyv0Z.js";const D={class:"site-nav"},F=["src"],I={class:"brand-name"},O=["aria-label"],R={class:"nav-controls"},U=["aria-label"],q=["aria-checked","title","onClick"],G={class:"nav-control-icon","aria-hidden":"true"},H=["title"],K=z({__name:"SiteNav",setup($){const{t:s}=E(),c=x(),{mode:u,setMode:k}=w(),g=d(()=>[{to:"/",label:s("nav.home"),match:n=>n==="/"}]);function y(n){return n.match(location.pathname)}const C={light:"☀️",dark:"🌙",system:"💻"},f=d(()=>({light:s("theme.light"),dark:s("theme.dark"),system:s("theme.system")})),L=d(()=>p()==="zh-CN"?"中文":"English");function N(){const n=p()==="zh-CN"?"en-US":"zh-CN";T(n)}return(n,l)=>{const h=A("RouterLink");return o(),i("header",D,[B(h,{class:"brand",to:"/",title:t(s)("nav.homeTitle",{name:t(c).displayName})},{default:m(()=>[a("img",{src:t(c).displayLogoUrl,alt:"Logo",onError:l[0]||(l[0]=e=>e.target.style.visibility="hidden")},null,40,F),a("span",I,r(t(c).displayName),1)]),_:1},8,["title"]),a("nav",{class:"nav-links","aria-label":t(s)("nav.mainNav")},[(o(!0),i(_,null,v(g.value,e=>(o(),M(h,{key:e.to,to:e.to,class:b({"router-link-active":y(e)})},{default:m(()=>[V(r(e.label),1)]),_:2},1032,["to","class"]))),128))],8,O),a("div",R,[a("div",{class:"theme-seg",role:"radiogroup","aria-label":t(s)("theme.label")},[(o(!0),i(_,null,v(t(S),e=>(o(),i("button",{key:e,type:"button",role:"radio","aria-checked":t(u)===e,class:b({active:t(u)===e}),title:f.value[e],onClick:j=>t(k)(e)},[a("span",G,r(C[e]),1)],10,q))),128))],8,U),a("button",{class:"nav-control",type:"button",title:t(s)("lang.label"),onClick:N},[l[1]||(l[1]=a("span",{class:"nav-control-icon","aria-hidden":"true"},"🌐",-1)),a("span",null,r(L.value),1)],8,H)])])}}});export{K as _};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{v as n,S as e,x as s}from"./index-QLbKGtH7.js";function u(t){return n(s.adminLogin,{method:"POST",json:{password:t}})}function m(){return n(s.adminVerify)}async function l(){await n(s.adminLogout,{method:"POST"})}async function g(t){const a=await n(s.adminFileList,{query:{page:t.page,size:t.size,keyword:t.keyword||void 0}}),o=e(a,["data","list","items","files"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map(i=>({id:Number(e(i,["id"])??0),code:String(e(i,["code"])??""),name:String(e(i,["name"])??`${e(i,["prefix"])??""}${e(i,["suffix"])??""}`),suffix:String(e(i,["suffix"])??""),size:Number(e(i,["size"])??0),isText:!!(e(i,["isText","is_text"])??!1),expiredAt:e(i,["expiredAt","expired_at","expires_at"])??null,expiredCount:e(i,["expiredCount","expired_count"])??null,usedCount:Number(e(i,["usedCount","used_count"])??0),createdAt:e(i,["createdAt","created_at"])??null,isExpired:!!(e(i,["isExpired","is_expired"])??!1)}))}}async function p(t){await n(s.adminFileDelete,{method:"DELETE",json:{id:t}})}async function f(t){await n(s.adminFileBatchDelete,{method:"POST",json:{ids:t}})}async function y(t){await n(s.adminFileUpdate,{method:"PATCH",json:t})}async function w(){const t=await n(s.adminConfigGet);if(t&&typeof t=="object"&&!Array.isArray(t)){const a=t;return e(a,["config","data","settings"])??a}return{}}async function S(t){await n(s.adminConfigUpdate,{method:"PATCH",json:t})}async function _(t){const a=await n(s.adminStorageSwitch,{method:"POST",json:{engine:t}});return String(a?.engine??t)}async function x(t,a){await n(s.adminPasswordUpdate,{method:"PATCH",json:{old_password:t,new_password:a}})}async function b(t){const a=await n(s.adminAuditList,{query:{page:t.page,size:t.size,action:t.action||void 0,result:t.result||void 0,ip:t.ip||void 0,start_time:t.startTime||void 0,end_time:t.endTime||void 0}}),o=e(a,["data","list","items","logs"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map((i,r)=>({id:Number(e(i,["id"])??r+1),action:String(e(i,["action"])??""),result:String(e(i,["result"])??""),fileCode:String(e(i,["file_code","fileCode","code"])??""),fileName:String(e(i,["file_name","fileName","name"])??""),sizeBytes:e(i,["size_bytes","sizeBytes","size"])??null,transferredBytes:e(i,["transferred_bytes","transferredBytes","bytes"])??null,ip:String(e(i,["ip","client_ip","clientIp"])??""),userAgent:String(e(i,["user_agent","userAgent"])??""),deviceOs:String(e(i,["device_os","deviceOs","os"])??""),deviceBrowser:String(e(i,["device_browser","deviceBrowser","browser"])??""),deviceType:String(e(i,["device_type","deviceType"])??""),actor:String(e(i,["actor"])??""),errorMsg:String(e(i,["error_msg","errorMsg","error"])??""),durationMs:e(i,["duration_ms","durationMs","duration"])??null,createdAt:e(i,["created_at","createdAt","time"])??null}))}}export{g as a,f as b,p as c,y as d,b as e,w as f,_ as g,S as h,x as i,l as j,m as k,u as l};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{v as n,S as e,x as s}from"./index-DSPQyv0Z.js";function u(t){return n(s.adminLogin,{method:"POST",json:{password:t}})}function m(){return n(s.adminVerify)}async function l(){await n(s.adminLogout,{method:"POST"})}async function g(t){const a=await n(s.adminFileList,{query:{page:t.page,size:t.size,keyword:t.keyword||void 0}}),o=e(a,["data","list","items","files"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map(i=>({id:Number(e(i,["id"])??0),code:String(e(i,["code"])??""),name:String(e(i,["name"])??`${e(i,["prefix"])??""}${e(i,["suffix"])??""}`),suffix:String(e(i,["suffix"])??""),size:Number(e(i,["size"])??0),isText:!!(e(i,["isText","is_text"])??!1),expiredAt:e(i,["expiredAt","expired_at","expires_at"])??null,expiredCount:e(i,["expiredCount","expired_count"])??null,usedCount:Number(e(i,["usedCount","used_count"])??0),createdAt:e(i,["createdAt","created_at"])??null,isExpired:!!(e(i,["isExpired","is_expired"])??!1)}))}}async function p(t){await n(s.adminFileDelete,{method:"DELETE",json:{id:t}})}async function f(t){await n(s.adminFileBatchDelete,{method:"POST",json:{ids:t}})}async function y(t){await n(s.adminFileUpdate,{method:"PATCH",json:t})}async function w(){const t=await n(s.adminConfigGet);if(t&&typeof t=="object"&&!Array.isArray(t)){const a=t;return e(a,["config","data","settings"])??a}return{}}async function S(t){await n(s.adminConfigUpdate,{method:"PATCH",json:t})}async function _(t){const a=await n(s.adminStorageSwitch,{method:"POST",json:{engine:t}});return String(a?.engine??t)}async function x(t,a){await n(s.adminPasswordUpdate,{method:"PATCH",json:{old_password:t,new_password:a}})}async function b(t){const a=await n(s.adminAuditList,{query:{page:t.page,size:t.size,action:t.action||void 0,result:t.result||void 0,ip:t.ip||void 0,start_time:t.startTime||void 0,end_time:t.endTime||void 0}}),o=e(a,["data","list","items","logs"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map((i,r)=>({id:Number(e(i,["id"])??r+1),action:String(e(i,["action"])??""),result:String(e(i,["result"])??""),fileCode:String(e(i,["file_code","fileCode","code"])??""),fileName:String(e(i,["file_name","fileName","name"])??""),sizeBytes:e(i,["size_bytes","sizeBytes","size"])??null,transferredBytes:e(i,["transferred_bytes","transferredBytes","bytes"])??null,ip:String(e(i,["ip","client_ip","clientIp"])??""),userAgent:String(e(i,["user_agent","userAgent"])??""),deviceOs:String(e(i,["device_os","deviceOs","os"])??""),deviceBrowser:String(e(i,["device_browser","deviceBrowser","browser"])??""),deviceType:String(e(i,["device_type","deviceType"])??""),actor:String(e(i,["actor"])??""),errorMsg:String(e(i,["error_msg","errorMsg","error"])??""),durationMs:e(i,["duration_ms","durationMs","duration"])??null,createdAt:e(i,["created_at","createdAt","time"])??null}))}}export{g as a,f as b,p as c,y as d,b as e,w as f,_ as g,S as h,x as i,l as j,m as k,u as l};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{v as n,S as e,x as s}from"./index-BKnWAKao.js";function u(t){return n(s.adminLogin,{method:"POST",json:{password:t}})}function m(){return n(s.adminVerify)}async function l(){await n(s.adminLogout,{method:"POST"})}async function g(t){const a=await n(s.adminFileList,{query:{page:t.page,size:t.size,keyword:t.keyword||void 0}}),o=e(a,["data","list","items","files"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map(i=>({id:Number(e(i,["id"])??0),code:String(e(i,["code"])??""),name:String(e(i,["name"])??`${e(i,["prefix"])??""}${e(i,["suffix"])??""}`),suffix:String(e(i,["suffix"])??""),size:Number(e(i,["size"])??0),isText:!!(e(i,["isText","is_text"])??!1),expiredAt:e(i,["expiredAt","expired_at","expires_at"])??null,expiredCount:e(i,["expiredCount","expired_count"])??null,usedCount:Number(e(i,["usedCount","used_count"])??0),createdAt:e(i,["createdAt","created_at"])??null,isExpired:!!(e(i,["isExpired","is_expired"])??!1)}))}}async function p(t){await n(s.adminFileDelete,{method:"DELETE",json:{id:t}})}async function f(t){await n(s.adminFileBatchDelete,{method:"POST",json:{ids:t}})}async function y(t){await n(s.adminFileUpdate,{method:"PATCH",json:t})}async function w(){const t=await n(s.adminConfigGet);if(t&&typeof t=="object"&&!Array.isArray(t)){const a=t;return e(a,["config","data","settings"])??a}return{}}async function S(t){await n(s.adminConfigUpdate,{method:"PATCH",json:t})}async function _(t){const a=await n(s.adminStorageSwitch,{method:"POST",json:{engine:t}});return String(a?.engine??t)}async function x(t,a){await n(s.adminPasswordUpdate,{method:"PATCH",json:{old_password:t,new_password:a}})}async function b(t){const a=await n(s.adminAuditList,{query:{page:t.page,size:t.size,action:t.action||void 0,result:t.result||void 0,ip:t.ip||void 0,start_time:t.startTime||void 0,end_time:t.endTime||void 0}}),o=e(a,["data","list","items","logs"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map((i,r)=>({id:Number(e(i,["id"])??r+1),action:String(e(i,["action"])??""),result:String(e(i,["result"])??""),fileCode:String(e(i,["file_code","fileCode","code"])??""),fileName:String(e(i,["file_name","fileName","name"])??""),sizeBytes:e(i,["size_bytes","sizeBytes","size"])??null,transferredBytes:e(i,["transferred_bytes","transferredBytes","bytes"])??null,ip:String(e(i,["ip","client_ip","clientIp"])??""),userAgent:String(e(i,["user_agent","userAgent"])??""),deviceOs:String(e(i,["device_os","deviceOs","os"])??""),deviceBrowser:String(e(i,["device_browser","deviceBrowser","browser"])??""),deviceType:String(e(i,["device_type","deviceType"])??""),actor:String(e(i,["actor"])??""),errorMsg:String(e(i,["error_msg","errorMsg","error"])??""),durationMs:e(i,["duration_ms","durationMs","duration"])??null,createdAt:e(i,["created_at","createdAt","time"])??null}))}}export{g as a,f as b,p as c,y as d,b as e,w as f,_ as g,S as h,x as i,l as j,m as k,u as l};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{v as n,S as e,x as s}from"./index-D7AAbqvI.js";function u(t){return n(s.adminLogin,{method:"POST",json:{password:t}})}function m(){return n(s.adminVerify)}async function l(){await n(s.adminLogout,{method:"POST"})}async function g(t){const a=await n(s.adminFileList,{query:{page:t.page,size:t.size,keyword:t.keyword||void 0}}),o=e(a,["data","list","items","files"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map(i=>({id:Number(e(i,["id"])??0),code:String(e(i,["code"])??""),name:String(e(i,["name"])??`${e(i,["prefix"])??""}${e(i,["suffix"])??""}`),suffix:String(e(i,["suffix"])??""),size:Number(e(i,["size"])??0),isText:!!(e(i,["isText","is_text"])??!1),expiredAt:e(i,["expiredAt","expired_at","expires_at"])??null,expiredCount:e(i,["expiredCount","expired_count"])??null,usedCount:Number(e(i,["usedCount","used_count"])??0),createdAt:e(i,["createdAt","created_at"])??null,isExpired:!!(e(i,["isExpired","is_expired"])??!1)}))}}async function p(t){await n(s.adminFileDelete,{method:"DELETE",json:{id:t}})}async function f(t){await n(s.adminFileBatchDelete,{method:"POST",json:{ids:t}})}async function y(t){await n(s.adminFileUpdate,{method:"PATCH",json:t})}async function w(){const t=await n(s.adminConfigGet);if(t&&typeof t=="object"&&!Array.isArray(t)){const a=t;return e(a,["config","data","settings"])??a}return{}}async function S(t){await n(s.adminConfigUpdate,{method:"PATCH",json:t})}async function _(t){const a=await n(s.adminStorageSwitch,{method:"POST",json:{engine:t}});return String(a?.engine??t)}async function x(t,a){await n(s.adminPasswordUpdate,{method:"PATCH",json:{old_password:t,new_password:a}})}async function b(t){const a=await n(s.adminAuditList,{query:{page:t.page,size:t.size,action:t.action||void 0,result:t.result||void 0,ip:t.ip||void 0,start_time:t.startTime||void 0,end_time:t.endTime||void 0}}),o=e(a,["data","list","items","logs"])??[],d=Number(e(a,["total","count"])??o.length);return{page:Number(e(a,["page"])??t.page),size:Number(e(a,["size"])??t.size),total:d,data:o.map((i,r)=>({id:Number(e(i,["id"])??r+1),action:String(e(i,["action"])??""),result:String(e(i,["result"])??""),fileCode:String(e(i,["file_code","fileCode","code"])??""),fileName:String(e(i,["file_name","fileName","name"])??""),sizeBytes:e(i,["size_bytes","sizeBytes","size"])??null,transferredBytes:e(i,["transferred_bytes","transferredBytes","bytes"])??null,ip:String(e(i,["ip","client_ip","clientIp"])??""),userAgent:String(e(i,["user_agent","userAgent"])??""),deviceOs:String(e(i,["device_os","deviceOs","os"])??""),deviceBrowser:String(e(i,["device_browser","deviceBrowser","browser"])??""),deviceType:String(e(i,["device_type","deviceType"])??""),actor:String(e(i,["actor"])??""),errorMsg:String(e(i,["error_msg","errorMsg","error"])??""),durationMs:e(i,["duration_ms","durationMs","duration"])??null,createdAt:e(i,["created_at","createdAt","time"])??null}))}}export{g as a,f as b,p as c,y as d,b as e,w as f,_ as g,S as h,x as i,l as j,m as k,u as l};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{av as o,aw as a,W as r}from"./index-BKnWAKao.js";import{j as n,k as i,l as c}from"./admin-KnbIpHLF.js";const s="fcb_admin_flag",l=o("auth",{state:()=>({token:r(),checked:!1}),getters:{isAuthed:t=>!!t.token},actions:{async login(t){const e=(await c(t))?.token;if(!e)throw new Error("登录响应缺少 token");this.token=e,a(e);try{localStorage.setItem(s,"1")}catch{}this.checked=!0},async verify(){if(!this.token)return!1;try{return await i(),this.checked=!0,!0}catch{return this.reset(),!1}},async logout(){try{this.token&&await n()}catch{}this.reset()},reset(){this.token="",this.checked=!1,a("");try{localStorage.removeItem(s)}catch{}}}});export{l as u};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{av as o,aw as a,W as r}from"./index-D7AAbqvI.js";import{j as n,k as i,l as c}from"./admin-uFxGdgNa.js";const s="fcb_admin_flag",l=o("auth",{state:()=>({token:r(),checked:!1}),getters:{isAuthed:t=>!!t.token},actions:{async login(t){const e=(await c(t))?.token;if(!e)throw new Error("登录响应缺少 token");this.token=e,a(e);try{localStorage.setItem(s,"1")}catch{}this.checked=!0},async verify(){if(!this.token)return!1;try{return await i(),this.checked=!0,!0}catch{return this.reset(),!1}},async logout(){try{this.token&&await n()}catch{}this.reset()},reset(){this.token="",this.checked=!1,a("");try{localStorage.removeItem(s)}catch{}}}});export{l as u};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{av as o,aw as a,W as r}from"./index-QLbKGtH7.js";import{j as n,k as i,l as c}from"./admin-BZX1cFNW.js";const s="fcb_admin_flag",l=o("auth",{state:()=>({token:r(),checked:!1}),getters:{isAuthed:t=>!!t.token},actions:{async login(t){const e=(await c(t))?.token;if(!e)throw new Error("登录响应缺少 token");this.token=e,a(e);try{localStorage.setItem(s,"1")}catch{}this.checked=!0},async verify(){if(!this.token)return!1;try{return await i(),this.checked=!0,!0}catch{return this.reset(),!1}},async logout(){try{this.token&&await n()}catch{}this.reset()},reset(){this.token="",this.checked=!1,a("");try{localStorage.removeItem(s)}catch{}}}});export{l as u};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{av as o,aw as a,W as r}from"./index-DSPQyv0Z.js";import{j as n,k as i,l as c}from"./admin-IDpKsD_2.js";const s="fcb_admin_flag",l=o("auth",{state:()=>({token:r(),checked:!1}),getters:{isAuthed:t=>!!t.token},actions:{async login(t){const e=(await c(t))?.token;if(!e)throw new Error("登录响应缺少 token");this.token=e,a(e);try{localStorage.setItem(s,"1")}catch{}this.checked=!0},async verify(){if(!this.token)return!1;try{return await i(),this.checked=!0,!0}catch{return this.reset(),!1}},async logout(){try{this.token&&await n()}catch{}this.reset()},reset(){this.token="",this.checked=!1,a("");try{localStorage.removeItem(s)}catch{}}}});export{l as u};
|
||||
-2517
File diff suppressed because one or more lines are too long
-2633
File diff suppressed because one or more lines are too long
-2517
File diff suppressed because one or more lines are too long
-2517
File diff suppressed because one or more lines are too long
-28
File diff suppressed because one or more lines are too long
-28
File diff suppressed because one or more lines are too long
-28
File diff suppressed because one or more lines are too long
-28
File diff suppressed because one or more lines are too long
-1
@@ -1 +0,0 @@
|
||||
import{v as x,S as s,U as m,V as g,x as u,W as w,H as i,X as f,Y as h}from"./index-BKnWAKao.js";function T(n,t,r,a=""){return x(u.shareText,{method:"POST",form:{text:n,expire_value:String(t),expire_style:r,code:a}})}function y(n,t,r,a,o=""){const e=new FormData;return e.append("file",n),e.append("expire_value",String(t)),e.append("expire_style",r),o&&e.append("code",o),h(u.shareFile,e,a)}async function S(n){const t=await x(u.shareMetadata,{query:{code:n}}),r=!!(s(t,["is_text","isText"])??s(t,["type"])==="text");return{code:String(s(t,["code"])??n),name:String(s(t,["name"])??"未命名"),size:Number(s(t,["size"])??0),type:r?"text":"file",isText:r,createdAt:s(t,["created_at","createdAt"])??null,expiredAt:s(t,["expired_at","expires_at","expiredAt","expiresAt"])??null,expiredCount:s(t,["expired_count","expiredCount","remaining_downloads","remainingDownloads"])??null,usedCount:Number(s(t,["used_count","usedCount"])??0),remainingDownloads:s(t,["remaining_downloads","remainingDownloads","expired_count","expiredCount"])??null}}async function b(n){const{blob:t}=await m(u.shareSelect,{query:{code:n}});return t.text()}function A(n,t){return new Promise((r,a)=>{const o=new URL(g(u.shareSelect),location.origin);o.searchParams.set("code",n);const e=new XMLHttpRequest;e.open("GET",o.toString()),e.timeout=6e5,e.responseType="blob";const l=w();l&&e.setRequestHeader("Authorization",`Bearer ${l}`),e.onprogress=p=>{p.lengthComputable&&t&&t(Math.round(p.loaded/p.total*100))},e.onload=()=>{if((e.getResponseHeader("content-type")??"").includes("application/json")){const d=new FileReader;d.onload=()=>{try{const c=JSON.parse(String(d.result));a(new i(c.code??e.status,c.msg||"取件失败",e.status))}catch{a(new i(e.status,"取件失败",e.status))}},d.readAsText(e.response);return}e.status>=200&&e.status<300?r({blob:e.response,filename:f(e.getResponseHeader("content-disposition"))}):a(new i(e.status,`取件失败(HTTP ${e.status})`,e.status))},e.onerror=()=>a(new i(0,"网络异常,取件失败")),e.ontimeout=()=>a(new i(0,"下载超时,请重试")),e.send()})}export{y as a,A as b,S as g,b as p,T as s};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{v as x,S as s,U as m,V as g,x as u,W as w,H as i,X as f,Y as h}from"./index-DSPQyv0Z.js";function T(n,t,r,a=""){return x(u.shareText,{method:"POST",form:{text:n,expire_value:String(t),expire_style:r,code:a}})}function y(n,t,r,a,o=""){const e=new FormData;return e.append("file",n),e.append("expire_value",String(t)),e.append("expire_style",r),o&&e.append("code",o),h(u.shareFile,e,a)}async function S(n){const t=await x(u.shareMetadata,{query:{code:n}}),r=!!(s(t,["is_text","isText"])??s(t,["type"])==="text");return{code:String(s(t,["code"])??n),name:String(s(t,["name"])??"未命名"),size:Number(s(t,["size"])??0),type:r?"text":"file",isText:r,createdAt:s(t,["created_at","createdAt"])??null,expiredAt:s(t,["expired_at","expires_at","expiredAt","expiresAt"])??null,expiredCount:s(t,["expired_count","expiredCount","remaining_downloads","remainingDownloads"])??null,usedCount:Number(s(t,["used_count","usedCount"])??0),remainingDownloads:s(t,["remaining_downloads","remainingDownloads","expired_count","expiredCount"])??null}}async function b(n){const{blob:t}=await m(u.shareSelect,{query:{code:n}});return t.text()}function A(n,t){return new Promise((r,a)=>{const o=new URL(g(u.shareSelect),location.origin);o.searchParams.set("code",n);const e=new XMLHttpRequest;e.open("GET",o.toString()),e.timeout=6e5,e.responseType="blob";const l=w();l&&e.setRequestHeader("Authorization",`Bearer ${l}`),e.onprogress=p=>{p.lengthComputable&&t&&t(Math.round(p.loaded/p.total*100))},e.onload=()=>{if((e.getResponseHeader("content-type")??"").includes("application/json")){const d=new FileReader;d.onload=()=>{try{const c=JSON.parse(String(d.result));a(new i(c.code??e.status,c.msg||"取件失败",e.status))}catch{a(new i(e.status,"取件失败",e.status))}},d.readAsText(e.response);return}e.status>=200&&e.status<300?r({blob:e.response,filename:f(e.getResponseHeader("content-disposition"))}):a(new i(e.status,`取件失败(HTTP ${e.status})`,e.status))},e.onerror=()=>a(new i(0,"网络异常,取件失败")),e.ontimeout=()=>a(new i(0,"下载超时,请重试")),e.send()})}export{y as a,A as b,S as g,b as p,T as s};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{v as x,S as s,U as m,V as g,x as u,W as w,H as i,X as f,Y as h}from"./index-QLbKGtH7.js";function T(n,t,r,a=""){return x(u.shareText,{method:"POST",form:{text:n,expire_value:String(t),expire_style:r,code:a}})}function y(n,t,r,a,o=""){const e=new FormData;return e.append("file",n),e.append("expire_value",String(t)),e.append("expire_style",r),o&&e.append("code",o),h(u.shareFile,e,a)}async function S(n){const t=await x(u.shareMetadata,{query:{code:n}}),r=!!(s(t,["is_text","isText"])??s(t,["type"])==="text");return{code:String(s(t,["code"])??n),name:String(s(t,["name"])??"未命名"),size:Number(s(t,["size"])??0),type:r?"text":"file",isText:r,createdAt:s(t,["created_at","createdAt"])??null,expiredAt:s(t,["expired_at","expires_at","expiredAt","expiresAt"])??null,expiredCount:s(t,["expired_count","expiredCount","remaining_downloads","remainingDownloads"])??null,usedCount:Number(s(t,["used_count","usedCount"])??0),remainingDownloads:s(t,["remaining_downloads","remainingDownloads","expired_count","expiredCount"])??null}}async function b(n){const{blob:t}=await m(u.shareSelect,{query:{code:n}});return t.text()}function A(n,t){return new Promise((r,a)=>{const o=new URL(g(u.shareSelect),location.origin);o.searchParams.set("code",n);const e=new XMLHttpRequest;e.open("GET",o.toString()),e.timeout=6e5,e.responseType="blob";const l=w();l&&e.setRequestHeader("Authorization",`Bearer ${l}`),e.onprogress=p=>{p.lengthComputable&&t&&t(Math.round(p.loaded/p.total*100))},e.onload=()=>{if((e.getResponseHeader("content-type")??"").includes("application/json")){const d=new FileReader;d.onload=()=>{try{const c=JSON.parse(String(d.result));a(new i(c.code??e.status,c.msg||"取件失败",e.status))}catch{a(new i(e.status,"取件失败",e.status))}},d.readAsText(e.response);return}e.status>=200&&e.status<300?r({blob:e.response,filename:f(e.getResponseHeader("content-disposition"))}):a(new i(e.status,`取件失败(HTTP ${e.status})`,e.status))},e.onerror=()=>a(new i(0,"网络异常,取件失败")),e.ontimeout=()=>a(new i(0,"下载超时,请重试")),e.send()})}export{y as a,A as b,S as g,b as p,T as s};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{v as x,S as s,U as m,V as g,x as u,W as w,H as i,X as f,Y as h}from"./index-D7AAbqvI.js";function T(n,t,r,a=""){return x(u.shareText,{method:"POST",form:{text:n,expire_value:String(t),expire_style:r,code:a}})}function y(n,t,r,a,o=""){const e=new FormData;return e.append("file",n),e.append("expire_value",String(t)),e.append("expire_style",r),o&&e.append("code",o),h(u.shareFile,e,a)}async function S(n){const t=await x(u.shareMetadata,{query:{code:n}}),r=!!(s(t,["is_text","isText"])??s(t,["type"])==="text");return{code:String(s(t,["code"])??n),name:String(s(t,["name"])??"未命名"),size:Number(s(t,["size"])??0),type:r?"text":"file",isText:r,createdAt:s(t,["created_at","createdAt"])??null,expiredAt:s(t,["expired_at","expires_at","expiredAt","expiresAt"])??null,expiredCount:s(t,["expired_count","expiredCount","remaining_downloads","remainingDownloads"])??null,usedCount:Number(s(t,["used_count","usedCount"])??0),remainingDownloads:s(t,["remaining_downloads","remainingDownloads","expired_count","expiredCount"])??null}}async function b(n){const{blob:t}=await m(u.shareSelect,{query:{code:n}});return t.text()}function A(n,t){return new Promise((r,a)=>{const o=new URL(g(u.shareSelect),location.origin);o.searchParams.set("code",n);const e=new XMLHttpRequest;e.open("GET",o.toString()),e.timeout=6e5,e.responseType="blob";const l=w();l&&e.setRequestHeader("Authorization",`Bearer ${l}`),e.onprogress=p=>{p.lengthComputable&&t&&t(Math.round(p.loaded/p.total*100))},e.onload=()=>{if((e.getResponseHeader("content-type")??"").includes("application/json")){const d=new FileReader;d.onload=()=>{try{const c=JSON.parse(String(d.result));a(new i(c.code??e.status,c.msg||"取件失败",e.status))}catch{a(new i(e.status,"取件失败",e.status))}},d.readAsText(e.response);return}e.status>=200&&e.status<300?r({blob:e.response,filename:f(e.getResponseHeader("content-disposition"))}):a(new i(e.status,`取件失败(HTTP ${e.status})`,e.status))},e.onerror=()=>a(new i(0,"网络异常,取件失败")),e.ontimeout=()=>a(new i(0,"下载超时,请重试")),e.send()})}export{y as a,A as b,S as g,b as p,T as s};
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@
|
||||
background: #0b0f1a;
|
||||
}
|
||||
</style>
|
||||
<script type="module" crossorigin src="/assets/index-D7AAbqvI.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DuYxbxXK.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CtyCxWf5.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user