Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e335df81d8 | ||
|
|
891d4067a8 | ||
|
|
9686fe887a |
No files matched your search
@@ -1,42 +0,0 @@
|
|||||||
name: CI 测试
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
tags: ["v*"]
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
# 镜像发布不再走 CI:GoReleaser Pro(本地 goreleaser release --clean)
|
|
||||||
# 负责多平台归档 + Gitea Release + ACR 双架构镜像(见 .goreleaser.yaml)。
|
|
||||||
# 本工作流只做推送/PR 前置测试门禁。
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: go vet + go test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: golang:1.27.1-alpine
|
|
||||||
timeout-minutes: 30
|
|
||||||
steps:
|
|
||||||
- name: 安装工具并检出
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
|
||||||
apk add --no-cache git curl bash >/dev/null
|
|
||||||
SCHEME="${GITHUB_SERVER_URL%%://*}"
|
|
||||||
SRV="${GITHUB_SERVER_URL#*://}"
|
|
||||||
echo "clone from ${SCHEME}://${SRV}"
|
|
||||||
git clone --depth=1 --branch "$GITHUB_REF_NAME" \
|
|
||||||
"${SCHEME}://oauth2:${GITHUB_TOKEN}@${SRV}/${GITHUB_REPOSITORY}.git" .
|
|
||||||
|
|
||||||
- name: go vet + go test
|
|
||||||
working-directory: server
|
|
||||||
env:
|
|
||||||
GOCACHE: /tmp/.gocache
|
|
||||||
GOMODCACHE: /tmp/.gomodcache
|
|
||||||
CGO_ENABLED: "0"
|
|
||||||
run: |
|
|
||||||
go vet ./...
|
|
||||||
go test ./... -count=1
|
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
name: Release 镜像
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
tags: ["v*"]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: registry.cn-hangzhou.aliyuncs.com
|
||||||
|
IMAGE: registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: 测试(推送前置门禁)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: golang:1.27.1-alpine
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: 安装工具并检出
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||||
|
apk add --no-cache git curl bash >/dev/null
|
||||||
|
git clone --depth=1 --branch "$GITHUB_REF_NAME" \
|
||||||
|
"https://oauth2:${GITHUB_TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" .
|
||||||
|
|
||||||
|
- name: go vet + go test
|
||||||
|
working-directory: server
|
||||||
|
env:
|
||||||
|
GOCACHE: /tmp/.gocache
|
||||||
|
GOMODCACHE: /tmp/.gomodcache
|
||||||
|
CGO_ENABLED: "0"
|
||||||
|
run: |
|
||||||
|
go vet ./...
|
||||||
|
go test ./... -count=1
|
||||||
|
|
||||||
|
build-push:
|
||||||
|
name: 多架构构建并推送 ACR
|
||||||
|
needs: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
steps:
|
||||||
|
- name: 安装工具并检出
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||||
|
apk add --no-cache git curl bash >/dev/null
|
||||||
|
git clone --depth=1 --branch "$GITHUB_REF_NAME" \
|
||||||
|
"https://oauth2:${GITHUB_TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" .
|
||||||
|
# 嵌入产物同步(保证二进制内前端与仓库一致)
|
||||||
|
if [ -f web-embed/index.html ]; then :; fi
|
||||||
|
|
||||||
|
- name: 安装 Docker CLI(挂宿主 daemon)
|
||||||
|
run: apk add --no-cache docker-cli buildx >/dev/null
|
||||||
|
|
||||||
|
- name: 计算 tag 与平台
|
||||||
|
id: meta
|
||||||
|
env:
|
||||||
|
REF: ${{ gitea.ref }}
|
||||||
|
run: |
|
||||||
|
case "$REF" in
|
||||||
|
refs/tags/v*)
|
||||||
|
VER="${REF#refs/tags/v}"
|
||||||
|
echo "tags=${IMAGE}:${VER} ${IMAGE}:latest" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "发布 tag: ${VER} + latest" ;;
|
||||||
|
*)
|
||||||
|
echo "tags=${IMAGE}:latest" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "main 构建: latest" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
- name: 登录阿里云 ACR
|
||||||
|
env:
|
||||||
|
ACR_USER: ${{ secrets.ACR_USERNAME }}
|
||||||
|
ACR_PASS: ${{ secrets.ACR_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
PASS_LEN=${#ACR_PASS}
|
||||||
|
echo "ACR 用户: $ACR_USER (密码 ${PASS_LEN} 位)"
|
||||||
|
printf '%s' "$ACR_PASS" | docker login "$REGISTRY" -u "$ACR_USER" --password-stdin
|
||||||
|
|
||||||
|
- name: 多架构构建并推送
|
||||||
|
env:
|
||||||
|
TAGS: ${{ steps.meta.outputs.tags }}
|
||||||
|
# provenance/sbom 必须关:阿里云 ACR 不识别 OCI empty manifest(attestation),
|
||||||
|
# 否则报 "denied: unknown manifest class for application/vnd.oci.empty.v1+json"
|
||||||
|
run: |
|
||||||
|
ARGS=""
|
||||||
|
for t in $TAGS; do ARGS="$ARGS -t $t"; done
|
||||||
|
docker buildx build \
|
||||||
|
--builder default \
|
||||||
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
--provenance=false --sbom=false \
|
||||||
|
--push \
|
||||||
|
-f deploy/Dockerfile \
|
||||||
|
$ARGS \
|
||||||
|
.
|
||||||
|
|
||||||
|
- name: 校验远程 manifest(双架构)
|
||||||
|
run: docker buildx imagetools inspect "${IMAGE}:latest" | grep -E "linux/amd64|linux/arm64"
|
||||||
@@ -35,4 +35,3 @@ deploy/.env
|
|||||||
|
|
||||||
# 会话/协作目录(工具私有,非项目源码)
|
# 会话/协作目录(工具私有,非项目源码)
|
||||||
.agent-teams/
|
.agent-teams/
|
||||||
dist/
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
# 文件快传 GoReleaser 配置(Pro 2.18.1,二进制在 ~/Code/Releaser/goreleaser)
|
|
||||||
#
|
|
||||||
# 用法:
|
|
||||||
# goreleaser release --snapshot --clean --skip=publish # 本地试跑,产出 ./dist
|
|
||||||
# goreleaser release --clean # 正式发布(需 semver tag)
|
|
||||||
#
|
|
||||||
# tag 必须是语义化版本(goreleaser 强制),如 v26.9.0(→ 版本 26.9.0),
|
|
||||||
# 并与 server/cmd/server/main.go 的 APP_VERSION 保持一致。
|
|
||||||
#
|
|
||||||
# 发布所需环境变量(不入库):
|
|
||||||
# GITEA_TOKEN Gitea Personal Access Token(release + 资产上传)
|
|
||||||
# BUILDX_CONFIG=/tmp/buildx-config 可选:buildx 状态目录重定向(受限环境)
|
|
||||||
# DOCKER_CONFIG=/tmp/docker-config 可选:docker 配置重定向(manifest 需写 ~/.docker 时)
|
|
||||||
# ACR 登录:docker login registry.cn-hangzhou.aliyuncs.com(镜像推送用本机 docker 凭证)
|
|
||||||
#
|
|
||||||
# 产物:
|
|
||||||
# - 归档:linux/darwin/windows × amd64/arm64(tar.gz + zip)→ Gitea Release
|
|
||||||
# - 镜像:registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare:{版本}-amd64/-arm64
|
|
||||||
# + 多架构 manifest {版本} 与 latest → 阿里云 ACR
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
|
|
||||||
project_name: fileshare
|
|
||||||
|
|
||||||
# 全局环境(goreleaser 会注入到所有 go build 子进程)。
|
|
||||||
# 把 GOMODCACHE 写死在 /tmp/.../goreleaser-pkg-mod,避开工作区下 server/.gomodcache
|
|
||||||
# 被 docker extra_files 拷进 context 时 macOS provenance xattr 触发的权限拒绝。
|
|
||||||
env:
|
|
||||||
- GOCACHE=/tmp/goreleaser-go-build
|
|
||||||
- GOMODCACHE=/tmp/goreleaser-pkg-mod
|
|
||||||
- GOSUMDB=off
|
|
||||||
- GOPROXY=https://goproxy.cn,direct
|
|
||||||
|
|
||||||
# 模块/入口:GoReleaser v2 不再在项目根找 main,须显式指到 server/cmd/server。
|
|
||||||
builds:
|
|
||||||
- id: server
|
|
||||||
main: ./cmd/server
|
|
||||||
dir: ./server
|
|
||||||
binary: fileshare-server
|
|
||||||
env:
|
|
||||||
- CGO_ENABLED=0
|
|
||||||
# GOCACHE/GOMODCACHE 由顶层 env: 注入
|
|
||||||
- GOSUMDB=off
|
|
||||||
goos:
|
|
||||||
- linux
|
|
||||||
- darwin
|
|
||||||
- windows
|
|
||||||
goarch:
|
|
||||||
- amd64
|
|
||||||
- arm64
|
|
||||||
# Windows ARM64 极少用,跳过以节省 CI 时间
|
|
||||||
ignore:
|
|
||||||
- goos: windows
|
|
||||||
goarch: arm64
|
|
||||||
flags:
|
|
||||||
- -trimpath
|
|
||||||
ldflags:
|
|
||||||
- -s -w
|
|
||||||
# 把版本号注入 main.APP_VERSION(const → var),发布时由 .Version 提供
|
|
||||||
- -X 'main.APP_VERSION={{ .Version }}'
|
|
||||||
- -X 'main.BuildCommit={{ .ShortCommit }}'
|
|
||||||
- -X 'main.BuildDate={{ .Date }}'
|
|
||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
|
||||||
|
|
||||||
archives:
|
|
||||||
- id: default
|
|
||||||
formats:
|
|
||||||
- tar.gz
|
|
||||||
- zip
|
|
||||||
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
|
||||||
files:
|
|
||||||
- LICENSE*
|
|
||||||
- README*
|
|
||||||
- src: ./deploy/.env.example
|
|
||||||
dst: deploy
|
|
||||||
# Changelog:从 git tag 之间收集提交,按 kind 分组
|
|
||||||
changelog:
|
|
||||||
use: git
|
|
||||||
sort: asc
|
|
||||||
filters:
|
|
||||||
exclude:
|
|
||||||
- '^Merge '
|
|
||||||
- '^ci:'
|
|
||||||
- '^chore:'
|
|
||||||
- '^docs:'
|
|
||||||
- '^test:'
|
|
||||||
groups:
|
|
||||||
- title: '新功能'
|
|
||||||
regexp: '^.*?(feat|feature)(\(.+\))?!?:\s*.+'
|
|
||||||
order: 0
|
|
||||||
- title: '修复'
|
|
||||||
regexp: '^.*?(fix|bug)(\(.+\))?!?:\s*.+'
|
|
||||||
order: 1
|
|
||||||
- title: '性能 / 重构'
|
|
||||||
regexp: '^.*?(perf|refactor|revert)(\(.+\))?!?:\s*.+'
|
|
||||||
order: 2
|
|
||||||
- title: '构建 / CI'
|
|
||||||
regexp: '^.*?(build|ci)(\(.+\))?!?:\s*.+'
|
|
||||||
order: 3
|
|
||||||
- title: '其它'
|
|
||||||
order: 999
|
|
||||||
|
|
||||||
# ─────────────────────────────────────────────
|
|
||||||
# Docker 镜像(多架构)
|
|
||||||
# 使用现成的 deploy/Dockerfile(前端用 web-embed 预构建产物;缺失时容器内重建)
|
|
||||||
# extra_files 不列 .gocache/.gomodcache(goreleaser 默认仍会拷 . 下的隐藏目录;
|
|
||||||
# 已在 .dockerignore 排除,并由项目根 .dockerignore 屏蔽)
|
|
||||||
# ─────────────────────────────────────────────
|
|
||||||
dockers:
|
|
||||||
- id: server-amd64
|
|
||||||
use: buildx
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
dockerfile: deploy/Dockerfile
|
|
||||||
image_templates:
|
|
||||||
- 'registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare:{{ .Version }}-amd64'
|
|
||||||
build_flag_templates:
|
|
||||||
- --platform=linux/amd64
|
|
||||||
- --build-arg=APK_MIRROR=https://mirrors.aliyun.com
|
|
||||||
- --build-arg=NPM_REGISTRY=https://registry.npmmirror.com
|
|
||||||
- --label=org.opencontainers.image.title=fileshare
|
|
||||||
- --label=org.opencontainers.image.version={{ .Version }}
|
|
||||||
- --label=org.opencontainers.image.revision={{ .ShortCommit }}
|
|
||||||
- --label=org.opencontainers.image.source=https://git.skymirror.top/SKYMirrorPublicTeam/FileShare
|
|
||||||
- --label=org.opencontainers.image.licenses=MIT
|
|
||||||
extra_files:
|
|
||||||
- server
|
|
||||||
- deploy
|
|
||||||
- web-embed
|
|
||||||
- web
|
|
||||||
- server/go.mod
|
|
||||||
- server/go.sum
|
|
||||||
- id: server-arm64
|
|
||||||
use: buildx
|
|
||||||
goos: linux
|
|
||||||
goarch: arm64
|
|
||||||
dockerfile: deploy/Dockerfile
|
|
||||||
image_templates:
|
|
||||||
- 'registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare:{{ .Version }}-arm64'
|
|
||||||
build_flag_templates:
|
|
||||||
- --platform=linux/arm64
|
|
||||||
- --build-arg=APK_MIRROR=https://mirrors.aliyun.com
|
|
||||||
- --build-arg=NPM_REGISTRY=https://registry.npmmirror.com
|
|
||||||
- --label=org.opencontainers.image.title=fileshare
|
|
||||||
- --label=org.opencontainers.image.version={{ .Version }}
|
|
||||||
- --label=org.opencontainers.image.revision={{ .ShortCommit }}
|
|
||||||
- --label=org.opencontainers.image.source=https://git.skymirror.top/SKYMirrorPublicTeam/FileShare
|
|
||||||
- --label=org.opencontainers.image.licenses=MIT
|
|
||||||
extra_files:
|
|
||||||
- server
|
|
||||||
- deploy
|
|
||||||
- web-embed
|
|
||||||
- web
|
|
||||||
- server/go.mod
|
|
||||||
- server/go.sum
|
|
||||||
|
|
||||||
# 多架构 manifest(amd64 / arm64 都推成功后合并成单一 manifest + latest 别名)
|
|
||||||
docker_manifests:
|
|
||||||
- name_template: 'registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare:{{ .Version }}'
|
|
||||||
image_templates:
|
|
||||||
- 'registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare:{{ .Version }}-amd64'
|
|
||||||
- 'registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare:{{ .Version }}-arm64'
|
|
||||||
- name_template: 'registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare:latest'
|
|
||||||
image_templates:
|
|
||||||
- 'registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare:{{ .Version }}-amd64'
|
|
||||||
- 'registry.cn-hangzhou.aliyuncs.com/skymirror/fileshare:{{ .Version }}-arm64'
|
|
||||||
|
|
||||||
# Gitea 服务器(token 走 GITEA_TOKEN env)
|
|
||||||
gitea_urls:
|
|
||||||
api: https://git.skymirror.top/api/v1
|
|
||||||
|
|
||||||
# 不推 Homebrew / nfpm / snap / flatpak
|
|
||||||
nfpms: []
|
|
||||||
brews: []
|
|
||||||
snapcrafts: []
|
|
||||||
aurs: []
|
|
||||||
scoops: []
|
|
||||||
chocolateys: []
|
|
||||||
|
|
||||||
# Gitea Release(把 archives 上传 + 创建 Release)
|
|
||||||
release:
|
|
||||||
gitea:
|
|
||||||
owner: SKYMirrorPublicTeam
|
|
||||||
name: FileShare
|
|
||||||
draft: false
|
|
||||||
prerelease: 'auto'
|
|
||||||
name_template: '{{ .ProjectName }} {{ .Version }}'
|
|
||||||
header: |
|
|
||||||
## 文件快传 {{ .Version }}
|
|
||||||
|
|
||||||
完整变更列表见 [CHANGELOG](https://git.skymirror.top/SKYMirrorPublicTeam/FileShare/src/tag/{{ .Tag }}).
|
|
||||||
ids:
|
|
||||||
- default
|
|
||||||
@@ -2,13 +2,9 @@
|
|||||||
|
|
||||||
文件快传
|
文件快传
|
||||||
|
|
||||||
数据库**默认 SQLite 零依赖**(modernc.org/sqlite 纯 Go 驱动,数据文件 `./data/fileshare.db`),
|
数据库**默认 SQLite 零依赖**(modernc.org/sqlite 纯 Go 驱动,数据文件 `./data/filecodebox.db`),
|
||||||
可选切换 Postgres(`FCB_DB_DRIVER=postgres` + DSN);Redis 为**可选**增强(未配置时自动降级为进程内存缓存)。
|
可选切换 Postgres(`FCB_DB_DRIVER=postgres` + DSN);Redis 为**可选**增强(未配置时自动降级为进程内存缓存)。
|
||||||
存储引擎支持 **本地 / S3 / WebDAV**(运行时热切换,健康检查通过才生效;WebDAV 重点优化:流式、Range、重试、连接复用)。
|
存储引擎支持 **本地 / S3 / WebDAV**(运行时热切换,健康检查通过才生效;WebDAV 重点优化:流式、Range、重试、连接复用)。
|
||||||
26.9 起支持**上下行带宽限速**(`upload_rate` / `download_rate` 字节/秒,0=不限速,管理端改后立即生效)。
|
|
||||||
|
|
||||||
**26.9 新增**:上下行带宽限速(见 [专题](docs/api/13-bandwidth.md))· 站点对外域名、自定义提取码(26.9)·
|
|
||||||
本地/S3/WebDAV 存储引擎 26.9 运行时热切换。
|
|
||||||
|
|
||||||
| 目录 | 说明 |
|
| 目录 | 说明 |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -21,7 +17,7 @@
|
|||||||
## 默认 Logo 与自定义
|
## 默认 Logo 与自定义
|
||||||
|
|
||||||
- 页面导航 Logo:本地资源 `web/src/assets/brand/logo.svg`
|
- 页面导航 Logo:本地资源 `web/src/assets/brand/logo.svg`
|
||||||
- favicon / 备用 Logo:本地资源 `web/src/assets/brand/favicon.png`(26.9 起不再使用远程 URL 默认值)
|
- favicon / 备用 Logo:本地资源 `web/src/assets/brand/favicon.png`(v2 起不再使用远程 URL 默认值)
|
||||||
|
|
||||||
管理端自定义 Logo 三步:
|
管理端自定义 Logo 三步:
|
||||||
|
|
||||||
@@ -41,7 +37,7 @@ docker compose up -d --build
|
|||||||
```
|
```
|
||||||
|
|
||||||
- **数据库双路径**:默认 SQLite 零依赖——`docker compose up -d --build` 即可(无需 postgres profile,
|
- **数据库双路径**:默认 SQLite 零依赖——`docker compose up -d --build` 即可(无需 postgres profile,
|
||||||
数据落 `serverdata` 卷 `/app/data/fileshare.db`);Postgres 模式——`.env` 设
|
数据落 `serverdata` 卷 `/app/data/filecodebox.db`);Postgres 模式——`.env` 设
|
||||||
`FCB_DB_DRIVER=postgres` 与 `FCB_DB_DSN` 后 `docker compose --profile postgres up -d --build`。
|
`FCB_DB_DRIVER=postgres` 与 `FCB_DB_DSN` 后 `docker compose --profile postgres up -d --build`。
|
||||||
- Redis 可选:`--profile redis` 并在 `.env` 设 `FCB_REDIS_ADDR=redis:6379`;未配置时自动降级为内存缓存。
|
- Redis 可选:`--profile redis` 并在 `.env` 设 `FCB_REDIS_ADDR=redis:6379`;未配置时自动降级为内存缓存。
|
||||||
- 存储引擎切换:`.env` 改 `FCB_STORAGE_ENGINE=s3|webdav` 并带对应 profile 启动:
|
- 存储引擎切换:`.env` 改 `FCB_STORAGE_ENGINE=s3|webdav` 并带对应 profile 启动:
|
||||||
@@ -55,7 +51,7 @@ docker compose up -d --build
|
|||||||
```bash
|
```bash
|
||||||
# 1) 后端(:8466)——默认 SQLite 零依赖,无需任何数据库
|
# 1) 后端(:8466)——默认 SQLite 零依赖,无需任何数据库
|
||||||
cd server
|
cd server
|
||||||
go run ./cmd/server # 数据落 ./data/fileshare.db;go test ./... 运行单测
|
go run ./cmd/server # 数据落 ./data/filecodebox.db;go test ./... 运行单测
|
||||||
|
|
||||||
# 2) 后端 Postgres 模式(可选)
|
# 2) 后端 Postgres 模式(可选)
|
||||||
docker run -d --name fcb-pg -p 5432:5432 \
|
docker run -d --name fcb-pg -p 5432:5432 \
|
||||||
@@ -80,9 +76,7 @@ npm run build # 产出 web/dist/,构建时按 deploy/Dockerfil
|
|||||||
|
|
||||||
| 内容 | 入口 |
|
| 内容 | 入口 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| API 操作文档(概述/认证/分享/分片/预签名/管理后台/审计/存储/配置/错误码/Logo/
|
| API 操作文档(概述/认证/分享/分片/预签名/管理后台/审计/存储/配置/错误码/Logo) | 站内 `/docs`,源文件 `docs/api/*.md` |
|
||||||
**带宽限速**) | 站内 `/docs`,源文件 [docs/api/](docs/api/) |
|
|
||||||
| ↳ **26.9 带宽限速**(`upload_rate` / `download_rate`,管理端改后立即生效) | [docs/api/13-bandwidth.md](docs/api/13-bandwidth.md) |
|
|
||||||
| OpenAPI 3.0 规范 + Swagger UI | 站内 `/openapi`,源文件 `docs/openapi.yaml` |
|
| OpenAPI 3.0 规范 + Swagger UI | 站内 `/openapi`,源文件 `docs/openapi.yaml` |
|
||||||
| 后端设计契约 | [server/README.md](server/README.md) |
|
| 后端设计契约 | [server/README.md](server/README.md) |
|
||||||
| 前端说明与渲染约定 | [web/README.md](web/README.md) |
|
| 前端说明与渲染约定 | [web/README.md](web/README.md) |
|
||||||
@@ -94,8 +88,8 @@ npm run build # 产出 web/dist/,构建时按 deploy/Dockerfil
|
|||||||
|
|
||||||
| 变量 | 必需 | 默认 | 说明 |
|
| 变量 | 必需 | 默认 | 说明 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` \| `postgres`(26.9 需求 ⑧) |
|
| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` \| `postgres`(v2 需求 ⑧) |
|
||||||
| `FCB_DB_DSN` | 视驱动 | - | postgres:连接串(**必需**);sqlite:文件路径(可空,默认 `./data/fileshare.db`) |
|
| `FCB_DB_DSN` | 视驱动 | - | postgres:连接串(**必需**);sqlite:文件路径(可空,默认 `./data/filecodebox.db`) |
|
||||||
| `FCB_REDIS_ADDR` | ❌ | 空 | 为空时缓存降级为内存实现;支持 `redis://[:password@]host:port[/db]` / `rediss://` URL 形式 |
|
| `FCB_REDIS_ADDR` | ❌ | 空 | 为空时缓存降级为内存实现;支持 `redis://[:password@]host:port[/db]` / `rediss://` URL 形式 |
|
||||||
| `FCB_REDIS_DB` | ❌ | `0` | Redis 逻辑库号 0-15(URL 显式 `/N` 时以 URL 为准) |
|
| `FCB_REDIS_DB` | ❌ | `0` | Redis 逻辑库号 0-15(URL 显式 `/N` 时以 URL 为准) |
|
||||||
| `FCB_LISTEN` | ❌ | `:8466` | 监听地址 |
|
| `FCB_LISTEN` | ❌ | `:8466` | 监听地址 |
|
||||||
@@ -115,16 +109,14 @@ npm run build # 产出 web/dist/,构建时按 deploy/Dockerfil
|
|||||||
|
|
||||||
### 运行时配置(settings KV,管理端可改)
|
### 运行时配置(settings KV,管理端可改)
|
||||||
|
|
||||||
站点信息(`site_name`、`logo_url`、`favicon_url`、`page_explain` 等)、26.9 展示与通知
|
站点信息(`site_name`、`logo_url`、`favicon_url`、`page_explain` 等)、v2 展示与通知
|
||||||
(`background_url`、`footer_text`、`footer_beian`、`notify_enabled`、`notify_title/content`)、
|
(`background_url`、`footer_text`、`footer_beian`、`notify_enabled`、`notify_title/content`)、
|
||||||
上传策略(`openUpload`、`enableChunk`、`uploadSize`、`allowed_file_types`、`expireStyle`、
|
上传策略(`openUpload`、`enableChunk`、`uploadSize`、`allowed_file_types`、`expireStyle`、
|
||||||
`code_generate_type`、`max_save_seconds`、`storageLimit`,及 26.9 上限键 `max_save_count`、
|
`code_generate_type`、`max_save_seconds`、`storageLimit`,及 v2 上限键 `max_save_count`、
|
||||||
`max_file_size`)、限流(`uploadCount/uploadMinute`、`errorCount/errorMinute`、
|
`max_file_size`)、限流(`uploadCount/uploadMinute`、`errorCount/errorMinute`、
|
||||||
`loginCount/loginMinute`)、安全(`adminSessionExpire`;`admin_token`/`jwt_secret` 由系统管理)。
|
`loginCount/loginMinute`)、安全(`adminSessionExpire`;`admin_token`/`jwt_secret` 由系统管理)。
|
||||||
26.9 带宽(`upload_rate` / `download_rate`,字节/秒,0=不限速)。
|
|
||||||
|
|
||||||
完整键表与默认值见《[环境变量与配置项](docs/api/10-config.md)》;
|
完整键表与默认值见《[环境变量与配置项](docs/api/10-config.md)》;
|
||||||
26.9 带宽限速专题《[带宽限速](docs/api/13-bandwidth.md)》;
|
|
||||||
修改接口见《[管理后台 API](docs/api/07-admin.md)》(`PATCH /admin/config/update`,改密自动轮换 jwt_secret)。
|
修改接口见《[管理后台 API](docs/api/07-admin.md)》(`PATCH /admin/config/update`,改密自动轮换 jwt_secret)。
|
||||||
|
|
||||||
## 审计日志
|
## 审计日志
|
||||||
|
|||||||
+6
-6
@@ -5,17 +5,17 @@
|
|||||||
WEB_PORT=8466
|
WEB_PORT=8466
|
||||||
|
|
||||||
# ---- 数据库(需求 ⑧)----
|
# ---- 数据库(需求 ⑧)----
|
||||||
# 默认 SQLite:零依赖,无需任何下方 Postgres 变量(数据落 serverdata 卷 /app/data/fileshare.db)
|
# 默认 SQLite:零依赖,无需任何下方 Postgres 变量(数据落 serverdata 卷 /app/data/filecodebox.db)
|
||||||
FCB_DB_DRIVER=sqlite
|
FCB_DB_DRIVER=sqlite
|
||||||
FCB_DB_DSN=
|
FCB_DB_DSN=
|
||||||
# Postgres 模式(可选):先 `docker compose --profile postgres up -d --build`,再改为:
|
# Postgres 模式(可选):先 `docker compose --profile postgres up -d --build`,再改为:
|
||||||
# FCB_DB_DRIVER=postgres
|
# FCB_DB_DRIVER=postgres
|
||||||
# FCB_DB_DSN=postgres://fileshare:fileshare@postgres:5432/fileshare?sslmode=disable
|
# FCB_DB_DSN=postgres://filecodebox:filecodebox@postgres:5432/filecodebox?sslmode=disable
|
||||||
|
|
||||||
# ---- Postgres(仅 --profile postgres 时使用;生产务必修改默认口令并启用 TLS)----
|
# ---- Postgres(仅 --profile postgres 时使用;生产务必修改默认口令并启用 TLS)----
|
||||||
POSTGRES_USER=fileshare
|
POSTGRES_USER=filecodebox
|
||||||
POSTGRES_PASSWORD=fileshare
|
POSTGRES_PASSWORD=filecodebox
|
||||||
POSTGRES_DB=fileshare
|
POSTGRES_DB=filecodebox
|
||||||
|
|
||||||
# ---- 存储引擎切换:local | s3 | webdav ----
|
# ---- 存储引擎切换:local | s3 | webdav ----
|
||||||
FCB_STORAGE_ENGINE=local
|
FCB_STORAGE_ENGINE=local
|
||||||
@@ -47,7 +47,7 @@ FCB_TRUSTED_PROXIES=
|
|||||||
# 注意:minioadmin/minioadmin 仅为本机冒烟默认值,对外部署必须修改
|
# 注意:minioadmin/minioadmin 仅为本机冒烟默认值,对外部署必须修改
|
||||||
# (并在 compose 中删除 minio 的 ports 发布或仅绑定 127.0.0.1)
|
# (并在 compose 中删除 minio 的 ports 发布或仅绑定 127.0.0.1)
|
||||||
FCB_S3_ENDPOINT_URL=http://minio:9000
|
FCB_S3_ENDPOINT_URL=http://minio:9000
|
||||||
FCB_S3_BUCKET_NAME=fileshare
|
FCB_S3_BUCKET_NAME=filecodebox
|
||||||
FCB_S3_ACCESS_KEY_ID=minioadmin
|
FCB_S3_ACCESS_KEY_ID=minioadmin
|
||||||
FCB_S3_SECRET_ACCESS_KEY=minioadmin
|
FCB_S3_SECRET_ACCESS_KEY=minioadmin
|
||||||
FCB_S3_REGION_NAME=us-east-1
|
FCB_S3_REGION_NAME=us-east-1
|
||||||
|
|||||||
+5
-16
@@ -2,23 +2,14 @@
|
|||||||
# 1) node:20-alpine 构建 Vue 前端产物(web/dist)
|
# 1) node:20-alpine 构建 Vue 前端产物(web/dist)
|
||||||
# 2) golang:1.27.1-alpine 编译 server(go:embed 前端产物,缓存指向 /tmp 避免写权限问题)
|
# 2) golang:1.27.1-alpine 编译 server(go:embed 前端产物,缓存指向 /tmp 避免写权限问题)
|
||||||
# 3) 最小运行时镜像:server 二进制 + web/dist 静态产物
|
# 3) 最小运行时镜像:server 二进制 + web/dist 静态产物
|
||||||
# 基础镜像可通过 build-arg 覆盖:CI/受限网络下指向 ACR 命名空间镜像
|
FROM node:20-alpine AS frontend
|
||||||
# (--build-arg NODE_IMAGE=… GO_IMAGE=… RUNTIME_IMAGE=… APK_MIRROR=mirrors.aliyun.com)
|
|
||||||
ARG NODE_IMAGE=node:20-alpine
|
|
||||||
ARG GO_IMAGE=golang:1.27.1-alpine
|
|
||||||
ARG RUNTIME_IMAGE=alpine:3.20
|
|
||||||
# NPM_REGISTRY:CI/受限网络下注入 npm 镜像源(默认官方源)
|
|
||||||
ARG NPM_REGISTRY=https://registry.npmjs.org
|
|
||||||
FROM ${NODE_IMAGE} AS frontend
|
|
||||||
ARG NPM_REGISTRY
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
RUN npm config set registry "$NPM_REGISTRY"
|
|
||||||
COPY web/package.json web/package-lock.json* ./
|
COPY web/package.json web/package-lock.json* ./
|
||||||
RUN npm ci || npm install
|
RUN npm ci || npm install
|
||||||
COPY web/ ./
|
COPY web/ ./
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM ${GO_IMAGE} AS backend
|
FROM golang:1.27.1-alpine AS backend
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
# GOPROXY:默认走 goproxy.cn(proxy.golang.org 的 GCS 后端在本环境网络下 EOF/超时),direct 兜底
|
# GOPROXY:默认走 goproxy.cn(proxy.golang.org 的 GCS 后端在本环境网络下 EOF/超时),direct 兜底
|
||||||
ENV CGO_ENABLED=0 GOSUMDB=off GOPROXY=https://goproxy.cn,direct GOCACHE=/tmp/.gocache GOMODCACHE=/tmp/.gomodcache
|
ENV CGO_ENABLED=0 GOSUMDB=off GOPROXY=https://goproxy.cn,direct GOCACHE=/tmp/.gocache GOMODCACHE=/tmp/.gomodcache
|
||||||
@@ -39,11 +30,9 @@ RUN if [ -f web-embed/index.html ]; then \
|
|||||||
&& rm -rf web-embed frontend-dist
|
&& rm -rf web-embed frontend-dist
|
||||||
RUN go build -trimpath -ldflags "-s -w" -o /out/server ./cmd/server
|
RUN go build -trimpath -ldflags "-s -w" -o /out/server ./cmd/server
|
||||||
|
|
||||||
FROM ${RUNTIME_IMAGE}
|
FROM alpine:3.20
|
||||||
# APK_MIRROR:受限网络下注入 alpine 镜像源(空=默认源)
|
# curl 供容器健康检查使用
|
||||||
ARG APK_MIRROR=""
|
RUN apk add --no-cache ca-certificates tzdata curl && adduser -D -u 10001 fcb
|
||||||
RUN if [ -n "$APK_MIRROR" ]; then sed -i "s|https://dl-cdn.alpinelinux.org|$APK_MIRROR|g" /etc/apk/repositories; fi \
|
|
||||||
&& apk add --no-cache ca-certificates tzdata curl && adduser -D -u 10001 fcb
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=backend /out/server /app/server
|
COPY --from=backend /out/server /app/server
|
||||||
# dist 随镜像分发(服务端已 go:embed,磁盘副本供运维检查/替换)
|
# dist 随镜像分发(服务端已 go:embed,磁盘副本供运维检查/替换)
|
||||||
|
|||||||
+7
-9
@@ -1,13 +1,11 @@
|
|||||||
# 文件快传 部署编排(deploy/)
|
# 文件快传 部署编排(deploy/)
|
||||||
|
|
||||||
Go 1.27.1(Gin + GORM)+ Vue 3 重写版(26.9)的容器化部署。数据库**默认 SQLite,零外部依赖**
|
Go 1.27.1(Gin + GORM)+ Vue 3 重写版(v3.1)的容器化部署。数据库**默认 SQLite,零外部依赖**
|
||||||
(modernc.org/sqlite 纯 Go 驱动,无需 Postgres),可选切换 Postgres(`--profile postgres`);
|
(modernc.org/sqlite 纯 Go 驱动,无需 Postgres),可选切换 Postgres(`--profile postgres`);
|
||||||
Redis 为可选增强(`--profile redis`),未配置 `FCB_REDIS_ADDR` 时服务端自动降级为进程内存缓存。
|
Redis 为可选增强(`--profile redis`),未配置 `FCB_REDIS_ADDR` 时服务端自动降级为进程内存缓存。
|
||||||
|
|
||||||
> 26.9 功能提示:管理后台可设「站点对外域名」(内网部署生成公网分享链接)、
|
> v3.1 功能提示:管理后台可设「站点对外域名」(内网部署生成公网分享链接)、
|
||||||
> 分享时支持自定义提取码(4-8 位字母数字);这些均为运行时配置,无需改部署。
|
> 分享时支持自定义提取码(4-8 位字母数字);这些均为运行时配置,无需改部署。
|
||||||
> 26.9 新增:管理后台可设「上行/下行带宽」限速(字节/秒,0=不限速),立即生效。
|
|
||||||
> S3 预签名直传(客户端→S3)服务端无法介入限速,其余下载/上传路径均覆盖。详见《[带宽限速](../docs/api/13-bandwidth.md)》。
|
|
||||||
|
|
||||||
## compose 服务与 profile 一览
|
## compose 服务与 profile 一览
|
||||||
|
|
||||||
@@ -32,7 +30,7 @@ docker compose up -d --build
|
|||||||
# 打开 http://localhost:8466(.env 可用 WEB_PORT 改端口)→ 自动跳转 /setup 完成初始化
|
# 打开 http://localhost:8466(.env 可用 WEB_PORT 改端口)→ 自动跳转 /setup 完成初始化
|
||||||
```
|
```
|
||||||
|
|
||||||
- 数据落 **`serverdata` 卷**:容器内 `/app/data/fileshare.db`(WAL 模式,父目录自动创建)。
|
- 数据落 **`serverdata` 卷**:容器内 `/app/data/filecodebox.db`(WAL 模式,父目录自动创建)。
|
||||||
- `.env` 中 Postgres 段变量(`POSTGRES_USER/PASSWORD/DB`)在此模式下不生效,无需修改。
|
- `.env` 中 Postgres 段变量(`POSTGRES_USER/PASSWORD/DB`)在此模式下不生效,无需修改。
|
||||||
|
|
||||||
### 路径 ②:Postgres 模式(可选)
|
### 路径 ②:Postgres 模式(可选)
|
||||||
@@ -41,7 +39,7 @@ docker compose up -d --build
|
|||||||
|
|
||||||
```dotenv
|
```dotenv
|
||||||
FCB_DB_DRIVER=postgres
|
FCB_DB_DRIVER=postgres
|
||||||
FCB_DB_DSN=postgres://fileshare:fileshare@postgres:5432/fileshare?sslmode=disable
|
FCB_DB_DSN=postgres://filecodebox:filecodebox@postgres:5432/filecodebox?sslmode=disable
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 带 postgres profile 启动(server 会等 postgres 健康检查通过后再启动):
|
2. 带 postgres profile 启动(server 会等 postgres 健康检查通过后再启动):
|
||||||
@@ -67,13 +65,13 @@ docker compose up -d --build
|
|||||||
```bash
|
```bash
|
||||||
# S3 引擎(MinIO 冒烟)
|
# S3 引擎(MinIO 冒烟)
|
||||||
docker compose --profile minio up -d --build # .env: FCB_STORAGE_ENGINE=s3
|
docker compose --profile minio up -d --build # .env: FCB_STORAGE_ENGINE=s3
|
||||||
# minio-init 一次性任务自动建桶(mc mb fileshare)
|
# minio-init 一次性任务自动建桶(mc mb filecodebox)
|
||||||
|
|
||||||
# WebDAV 引擎(dufs 冒烟,Basic 认证 admin/admin123@/:rw)
|
# WebDAV 引擎(dufs 冒烟,Basic 认证 admin/admin123@/:rw)
|
||||||
docker compose --profile webdav up -d --build # .env: FCB_STORAGE_ENGINE=webdav
|
docker compose --profile webdav up -d --build # .env: FCB_STORAGE_ENGINE=webdav
|
||||||
```
|
```
|
||||||
|
|
||||||
> **26.9 起支持运行时热切换**:也可不改 `.env`,直接在管理后台「系统设置 → 存储引擎」
|
> **v3 起支持运行时热切换**:也可不改 `.env`,直接在管理后台「系统设置 → 存储引擎」
|
||||||
> 三选并保存引擎参数后点切换(健康检查通过才生效,失败保持原引擎),或调用
|
> 三选并保存引擎参数后点切换(健康检查通过才生效,失败保持原引擎),或调用
|
||||||
> `POST /admin/storage/switch`——均无需重启容器。`.env` 的 `FCB_STORAGE_ENGINE`
|
> `POST /admin/storage/switch`——均无需重启容器。`.env` 的 `FCB_STORAGE_ENGINE`
|
||||||
> 仅作为首次启动(KV 为空时)的默认引擎。
|
> 仅作为首次启动(KV 为空时)的默认引擎。
|
||||||
@@ -127,7 +125,7 @@ curl -I http://localhost:8466/docs # 200(SPA 回退)
|
|||||||
或在 `.env` 设 `FCB_ADMIN_PASSWORD`(≥8 位)让服务启动即自动初始化,
|
或在 `.env` 设 `FCB_ADMIN_PASSWORD`(≥8 位)让服务启动即自动初始化,
|
||||||
消除「公网上被抢先访问 /setup 接管」的窗口。初始化完成后建议从 `.env` 移除该变量。
|
消除「公网上被抢先访问 /setup 接管」的窗口。初始化完成后建议从 `.env` 移除该变量。
|
||||||
3. **修改组件默认凭据**:minio(`minioadmin/minioadmin`)、webdav(`admin/admin123`)、
|
3. **修改组件默认凭据**:minio(`minioadmin/minioadmin`)、webdav(`admin/admin123`)、
|
||||||
Postgres(`fileshare/fileshare`)默认凭据仅限本机冒烟;对外部署必须修改,
|
Postgres(`filecodebox/filecodebox`)默认凭据仅限本机冒烟;对外部署必须修改,
|
||||||
且不建议把 9000/9001/5005 端口发布到公网(compose 中删除对应 `ports` 或仅绑 127.0.0.1)。
|
且不建议把 9000/9001/5005 端口发布到公网(compose 中删除对应 `ports` 或仅绑 127.0.0.1)。
|
||||||
4. **Postgres 建议启用 TLS**:默认示例 DSN 为 `sslmode=disable`,生产请改为
|
4. **Postgres 建议启用 TLS**:默认示例 DSN 为 `sslmode=disable`,生产请改为
|
||||||
`sslmode=require` 及以上。
|
`sslmode=require` 及以上。
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
# docker compose --profile minio up -d # 可选:S3 引擎冒烟(FCB_STORAGE_ENGINE=s3)
|
# docker compose --profile minio up -d # 可选:S3 引擎冒烟(FCB_STORAGE_ENGINE=s3)
|
||||||
# docker compose --profile webdav up -d # 可选:WebDAV 引擎冒烟(FCB_STORAGE_ENGINE=webdav)
|
# docker compose --profile webdav up -d # 可选:WebDAV 引擎冒烟(FCB_STORAGE_ENGINE=webdav)
|
||||||
#
|
#
|
||||||
# 数据库默认 SQLite(modernc.org/sqlite 纯 Go 驱动,数据落 serverdata 卷 /app/data/fileshare.db);
|
# 数据库默认 SQLite(modernc.org/sqlite 纯 Go 驱动,数据落 serverdata 卷 /app/data/filecodebox.db);
|
||||||
# postgres 为可选 profile 服务,启用时 server 经 FCB_DB_DRIVER/FCB_DB_DSN 自动接入;
|
# postgres 为可选 profile 服务,启用时 server 经 FCB_DB_DRIVER/FCB_DB_DSN 自动接入;
|
||||||
# Redis 同为可选,未配置时服务端内存降级。
|
# Redis 同为可选,未配置时服务端内存降级。
|
||||||
name: fileshare
|
name: filecodebox
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
@@ -18,13 +18,13 @@ services:
|
|||||||
# 需要时 docker compose --profile postgres up -d --build
|
# 需要时 docker compose --profile postgres up -d --build
|
||||||
profiles: ["postgres"]
|
profiles: ["postgres"]
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: ${POSTGRES_USER:-fileshare}
|
POSTGRES_USER: ${POSTGRES_USER:-filecodebox}
|
||||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-fileshare}
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-filecodebox}
|
||||||
POSTGRES_DB: ${POSTGRES_DB:-fileshare}
|
POSTGRES_DB: ${POSTGRES_DB:-filecodebox}
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-fileshare}"]
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-filecodebox}"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 20
|
retries: 20
|
||||||
@@ -52,7 +52,7 @@ services:
|
|||||||
start_period: 5s
|
start_period: 5s
|
||||||
environment:
|
environment:
|
||||||
# 需求 ⑧:数据库驱动 sqlite(默认,零依赖)| postgres(需 --profile postgres)
|
# 需求 ⑧:数据库驱动 sqlite(默认,零依赖)| postgres(需 --profile postgres)
|
||||||
# 默认 SQLite:DSN 留空 → 数据库文件落 /app/data/fileshare.db(serverdata 卷)
|
# 默认 SQLite:DSN 留空 → 数据库文件落 /app/data/filecodebox.db(serverdata 卷)
|
||||||
# postgres 模式:在 .env 设 FCB_DB_DRIVER=postgres 与 FCB_DB_DSN(模板见 deploy/.env.example)
|
# postgres 模式:在 .env 设 FCB_DB_DRIVER=postgres 与 FCB_DB_DSN(模板见 deploy/.env.example)
|
||||||
FCB_DB_DRIVER: ${FCB_DB_DRIVER:-sqlite}
|
FCB_DB_DRIVER: ${FCB_DB_DRIVER:-sqlite}
|
||||||
FCB_DB_DSN: ${FCB_DB_DSN:-}
|
FCB_DB_DSN: ${FCB_DB_DSN:-}
|
||||||
@@ -67,7 +67,7 @@ services:
|
|||||||
FCB_LOCAL_STORAGE_PATH: /app/data
|
FCB_LOCAL_STORAGE_PATH: /app/data
|
||||||
# S3 引擎(MinIO profile)
|
# S3 引擎(MinIO profile)
|
||||||
FCB_S3_ENDPOINT_URL: ${FCB_S3_ENDPOINT_URL:-}
|
FCB_S3_ENDPOINT_URL: ${FCB_S3_ENDPOINT_URL:-}
|
||||||
FCB_S3_BUCKET_NAME: ${FCB_S3_BUCKET_NAME:-fileshare}
|
FCB_S3_BUCKET_NAME: ${FCB_S3_BUCKET_NAME:-filecodebox}
|
||||||
FCB_S3_ACCESS_KEY_ID: ${FCB_S3_ACCESS_KEY_ID:-minioadmin}
|
FCB_S3_ACCESS_KEY_ID: ${FCB_S3_ACCESS_KEY_ID:-minioadmin}
|
||||||
FCB_S3_SECRET_ACCESS_KEY: ${FCB_S3_SECRET_ACCESS_KEY:-minioadmin}
|
FCB_S3_SECRET_ACCESS_KEY: ${FCB_S3_SECRET_ACCESS_KEY:-minioadmin}
|
||||||
FCB_S3_REGION_NAME: ${FCB_S3_REGION_NAME:-us-east-1}
|
FCB_S3_REGION_NAME: ${FCB_S3_REGION_NAME:-us-east-1}
|
||||||
@@ -122,7 +122,7 @@ services:
|
|||||||
entrypoint: >
|
entrypoint: >
|
||||||
/bin/sh -c "
|
/bin/sh -c "
|
||||||
mc alias set local http://minio:9000 ${FCB_S3_ACCESS_KEY_ID:-minioadmin} ${FCB_S3_SECRET_ACCESS_KEY:-minioadmin} &&
|
mc alias set local http://minio:9000 ${FCB_S3_ACCESS_KEY_ID:-minioadmin} ${FCB_S3_SECRET_ACCESS_KEY:-minioadmin} &&
|
||||||
mc mb --ignore-existing local/${FCB_S3_BUCKET_NAME:-fileshare} &&
|
mc mb --ignore-existing local/${FCB_S3_BUCKET_NAME:-filecodebox} &&
|
||||||
echo 'MinIO 桶已就绪'"
|
echo 'MinIO 桶已就绪'"
|
||||||
|
|
||||||
# WebDAV 冒烟服务器(dufs,简单读写 + Basic 认证)
|
# WebDAV 冒烟服务器(dufs,简单读写 + Basic 认证)
|
||||||
|
|||||||
+2
-18
@@ -1,14 +1,7 @@
|
|||||||
# API 概述
|
# API 概述
|
||||||
|
|
||||||
文件快传 Go 版(26.9)对外提供一套 REST API,覆盖文本/文件分享、分片上传、
|
文件快传 Go 版(v2.5.6)对外提供一套 REST API,覆盖文本/文件分享、分片上传、
|
||||||
预签名直传、管理后台与审计日志查询。
|
预签名直传、管理后台与审计日志查询。本文档与 `server/internal/api/` 实际实现逐一对齐,
|
||||||
|
|
||||||
## 更新日志
|
|
||||||
|
|
||||||
- **26.9**:上下行带宽限速(`upload_rate` / `download_rate`,详见《[带宽限速](13-bandwidth.md)》)、
|
|
||||||
站点对外域名(`site_domain`)、自定义提取码(5~8 位)
|
|
||||||
|
|
||||||
本文档与 `server/internal/api/` 实际实现逐一对齐,
|
|
||||||
交互式规范见站内 `/openapi`(源文件 `docs/openapi.yaml`)。
|
交互式规范见站内 `/openapi`(源文件 `docs/openapi.yaml`)。
|
||||||
|
|
||||||
## Base URL
|
## Base URL
|
||||||
@@ -62,7 +55,6 @@
|
|||||||
| `error` | 取件失败(404/过期)时计数 | 10 次 / 1 分钟 | `errorCount` / `errorMinute` |
|
| `error` | 取件失败(404/过期)时计数 | 10 次 / 1 分钟 | `errorCount` / `errorMinute` |
|
||||||
| `login` | 登录失败时计数 | 5 次 / 15 分钟 | `loginCount` / `loginMinute` |
|
| `login` | 登录失败时计数 | 5 次 / 15 分钟 | `loginCount` / `loginMinute` |
|
||||||
| `metadata` | 每次访问即计数 | 同 `error` | `errorCount` / `errorMinute` |
|
| `metadata` | 每次访问即计数 | 同 `error` | `errorCount` / `errorMinute` |
|
||||||
| `bandwidth` | 每 Read 块按 `rate` 字节/秒对齐 sleep,0=不限速 | 0 / 0(不限) | `upload_rate` / `download_rate` |
|
|
||||||
|
|
||||||
## 初始化守卫
|
## 初始化守卫
|
||||||
|
|
||||||
@@ -94,14 +86,6 @@
|
|||||||
| 管理后台 | `POST /admin/login` · `GET /admin/verify` · `POST /admin/logout` · `GET /admin/dashboard` · 文件管理 `/admin/file/*` · 配置 `/admin/config/*` · 密码 `/admin/settings/password` |
|
| 管理后台 | `POST /admin/login` · `GET /admin/verify` · `POST /admin/logout` · `GET /admin/dashboard` · 文件管理 `/admin/file/*` · 配置 `/admin/config/*` · 密码 `/admin/settings/password` |
|
||||||
| 审计日志 | `GET /admin/audit/list`(别名 `/admin/audit/logs`) |
|
| 审计日志 | `GET /admin/audit/list`(别名 `/admin/audit/logs`) |
|
||||||
|
|
||||||
## 专题文档
|
|
||||||
|
|
||||||
| 主题 | 文档 |
|
|
||||||
|---|---|
|
|
||||||
| 站点 Logo 自定义 | [12-logo.md](12-logo.md) |
|
|
||||||
| 错误码 | [11-errors.md](11-errors.md) |
|
|
||||||
| 带宽限速(26.9) | [13-bandwidth.md](13-bandwidth.md) |
|
|
||||||
|
|
||||||
## 时间与编码
|
## 时间与编码
|
||||||
|
|
||||||
- 时间字段一律 RFC 3339(如 `2025-06-01T12:00:00+08:00`);管理员会话过期时间为 Unix 秒。
|
- 时间字段一律 RFC 3339(如 `2025-06-01T12:00:00+08:00`);管理员会话过期时间为 Unix 秒。
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
## 管理员令牌
|
## 管理员令牌
|
||||||
|
|
||||||
- 由 `POST /admin/login` 用管理员密码换取,HS256 JWT,默认有效期 **7 天**(`adminSessionExpire`,1~365 整天,26.9 起由 30 天缩短)。
|
- 由 `POST /admin/login` 用管理员密码换取,HS256 JWT,默认有效期 **7 天**(`adminSessionExpire`,1~365 整天,v2.5.6 起由 30 天缩短)。
|
||||||
- 请求头格式:`Authorization: Bearer <token>`。
|
- 请求头格式:`Authorization: Bearer <token>`。
|
||||||
- **改密/重置管理员密码会轮换 `jwt_secret`,所有已签发令牌立即失效**(401)。
|
- **改密/重置管理员密码会轮换 `jwt_secret`,所有已签发令牌立即失效**(401)。
|
||||||
- 密码存储为 bcrypt(cost 12);历史 `sha256$`/明文格式在登录成功后自动升级重哈希,无需手动迁移。
|
- 密码存储为 bcrypt(cost 12);历史 `sha256$`/明文格式在登录成功后自动升级重哈希,无需手动迁移。
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
- `day`/`hour`/`minute`:按时间过期,`expired_count = -1`。
|
- `day`/`hour`/`minute`:按时间过期,`expired_count = -1`。
|
||||||
- `count`:按次数过期,取件 `expire_value` 次后失效(`expired_count = expire_value`);
|
- `count`:按次数过期,取件 `expire_value` 次后失效(`expired_count = expire_value`);
|
||||||
**26.9 需求 ④**:`max_save_count>0` 时 `expire_value` 不得超出该上限,超限 403。
|
**v2 需求 ④**:`max_save_count>0` 时 `expire_value` 不得超出该上限,超限 403。
|
||||||
- `forever`:永久(需站点允许;`max_save_seconds>0` 时其他方式受最长保存上限约束,超限 403)。
|
- `forever`:永久(需站点允许;`max_save_seconds>0` 时其他方式受最长保存上限约束,超限 403)。
|
||||||
|
|
||||||
> 可选值与上限来自公开配置 `GET /api/v1/config`(`expireStyle`、`max_save_seconds`、
|
> 可选值与上限来自公开配置 `GET /api/v1/config`(`expireStyle`、`max_save_seconds`、
|
||||||
@@ -88,4 +88,4 @@ curl -s "http://localhost:8466/share/select?code=8XQ2M"
|
|||||||
你好,文件快传
|
你好,文件快传
|
||||||
```
|
```
|
||||||
|
|
||||||
**26.9 变更**:① 支持 JSON 提交(`Content-Type: application/json`,字段同名);② 空文本 400「分享内容不能为空」;③ 可选 `code` 自定义提取码(5-8 位字母数字,占用 400)。
|
**v3.1 变更**:① 支持 JSON 提交(`Content-Type: application/json`,字段同名);② 空文本 400「分享内容不能为空」;③ 可选 `code` 自定义提取码(5-8 位字母数字,占用 400)。
|
||||||
@@ -39,7 +39,7 @@ curl -s -X POST http://localhost:8466/share/file \
|
|||||||
{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" }
|
{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" }
|
||||||
```
|
```
|
||||||
|
|
||||||
> 大小上限为动态策略(26.9 需求 ④⑩):管理端改 `max_file_size`(0=回落 `uploadSize`)后
|
> 大小上限为动态策略(v2 需求 ④⑩):管理端改 `max_file_size`(0=回落 `uploadSize`)后
|
||||||
> **下一次上传立即按新上限执行**,无需重启;上限值可经 `GET /api/v1/config` 的
|
> **下一次上传立即按新上限执行**,无需重启;上限值可经 `GET /api/v1/config` 的
|
||||||
> `max_file_size`/`maxFileSize` 字段读取。
|
> `max_file_size`/`maxFileSize` 字段读取。
|
||||||
|
|
||||||
@@ -101,31 +101,3 @@ curl -s -H 'Range: bytes=0-1023' -o part.bin \
|
|||||||
```json
|
```json
|
||||||
{ "code": 416, "msg": "请求范围超出文件大小" }
|
{ "code": 416, "msg": "请求范围超出文件大小" }
|
||||||
```
|
```
|
||||||
|
|
||||||
## 直链下载(26.9)
|
|
||||||
|
|
||||||
存储引擎为对象存储(S3)且 `direct_download=1` 时,`GET /share/select` 与
|
|
||||||
`GET /share/download` 不再代理文件流,而是 `302` 重定向到限时预签名 URL——
|
|
||||||
文件字节不经过本服务器,带宽成本转嫁对象存储。
|
|
||||||
|
|
||||||
- 签名有效期 = `direct_link_expire`(默认 900 秒)与分享剩余时效的较小值;
|
|
||||||
- 引擎不支持直链(如 local/WebDAV)时自动回落代理下载,取件不中断;
|
|
||||||
- 审计照常记录(`transferred_bytes` 记为文件大小)。
|
|
||||||
|
|
||||||
## 下载防盗链(26.9)
|
|
||||||
|
|
||||||
`hotlink_enabled=1` 时,`/share/download` 校验 `Referer`:
|
|
||||||
|
|
||||||
| Referer | 行为 |
|
|
||||||
|---|---|
|
|
||||||
| 空(直接访问 / curl / 地址栏) | 放行 |
|
|
||||||
| 与请求 Host 同源 | 放行 |
|
|
||||||
| 命中 `hotlink_whitelist`(逗号分隔域名,支持 `*.example.com` 通配) | 放行 |
|
|
||||||
| 其余 | `403`(JSON 错误体) |
|
|
||||||
|
|
||||||
白名单为空时仅同源放行。开关与白名单均为管理端 KV,修改后立即生效。
|
|
||||||
|
|
||||||
## 文件夹上传(26.9)
|
|
||||||
|
|
||||||
不支持文件夹上传(前端已移除目录选择;拖拽目录会提示"建议压缩后上传")。
|
|
||||||
后端 `SanitizeFileName` 会剥离文件名中的路径分隔符,多级路径无法成体保存。
|
|
||||||
@@ -130,7 +130,7 @@ curl -s -X POST http://localhost:8466/chunk/upload/3f6b8c2a4d5e6f708192a3b4c5d6e
|
|||||||
|
|
||||||
> 约束:单分片 ≤ `chunk_size`(init 声明值)且 ≤ **32MiB 硬上限**(init 时 `chunk_size>33554432` 直接 400「chunk_size 过大」);
|
> 约束:单分片 ≤ `chunk_size`(init 声明值)且 ≤ **32MiB 硬上限**(init 时 `chunk_size>33554432` 直接 400「chunk_size 过大」);
|
||||||
> 总大小(init 按分片数上限、上传/合并按累计)受**动态策略上限**约束——`max_file_size>0` 时为其,
|
> 总大小(init 按分片数上限、上传/合并按累计)受**动态策略上限**约束——`max_file_size>0` 时为其,
|
||||||
> 否则回落 `uploadSize`(26.9 需求 ④⑩,管理端改后立即生效,超限清理会话);首个分片做 magic bytes
|
> 否则回落 `uploadSize`(v2 需求 ④⑩,管理端改后立即生效,超限清理会话);首个分片做 magic bytes
|
||||||
> 防伪校验(403「文件内容校验失败:…」)。分片哈希由服务端计算;合并时与分片记录交叉校验,不一致报 400。
|
> 防伪校验(403「文件内容校验失败:…」)。分片哈希由服务端计算;合并时与分片记录交叉校验,不一致报 400。
|
||||||
>
|
>
|
||||||
> **enableChunk 开关**:管理端关闭分片上传后,`/chunk/upload/*` 全部端点返回 403「分片上传未启用」(后端强制,前端仅隐藏入口)。
|
> **enableChunk 开关**:管理端关闭分片上传后,`/chunk/upload/*` 全部端点返回 403「分片上传未启用」(后端强制,前端仅隐藏入口)。
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ curl -s -X POST http://localhost:8466/presign/upload/init \
|
|||||||
"code": 200, "msg": "ok",
|
"code": 200, "msg": "ok",
|
||||||
"data": {
|
"data": {
|
||||||
"upload_id": "6a1e…",
|
"upload_id": "6a1e…",
|
||||||
"upload_url": "https://minio:9000/fileshare/share/data/2025/06/01/6a1e…/backup.zip?X-Amz-…",
|
"upload_url": "https://minio:9000/filecodebox/share/data/2025/06/01/6a1e…/backup.zip?X-Amz-…",
|
||||||
"mode": "direct",
|
"mode": "direct",
|
||||||
"expires_in": 900,
|
"expires_in": 900,
|
||||||
"file_path": "share/data/2025/06/01/6a1e…"
|
"file_path": "share/data/2025/06/01/6a1e…"
|
||||||
@@ -82,7 +82,7 @@ curl -s -X POST http://localhost:8466/presign/upload/init \
|
|||||||
{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" }
|
{ "code": 403, "msg": "大小超过限制,最大为10.00 MB" }
|
||||||
```
|
```
|
||||||
|
|
||||||
> 大小上限为动态策略(26.9 需求 ④⑩):管理端改 `max_file_size`(0=回落 `uploadSize`)后
|
> 大小上限为动态策略(v2 需求 ④⑩):管理端改 `max_file_size`(0=回落 `uploadSize`)后
|
||||||
> 立即按新上限校验 init 声明的 `file_size`。
|
> 立即按新上限校验 init 声明的 `file_size`。
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
+6
-23
@@ -318,7 +318,7 @@ curl -s "http://localhost:8466/admin/file/preview?id=41&maxChars=100" -H "Author
|
|||||||
|
|
||||||
返回运行时配置 KV(含默认值与管理端修改)。`admin_token` 恒返回空串(屏蔽);
|
返回运行时配置 KV(含默认值与管理端修改)。`admin_token` 恒返回空串(屏蔽);
|
||||||
`jwt_secret` 不下发;存储引擎为进程级单例,`_engine_hint` 提示引擎配置修改需重启。
|
`jwt_secret` 不下发;存储引擎为进程级单例,`_engine_hint` 提示引擎配置修改需重启。
|
||||||
26.9 新增键(需求 ①②③④⑩)一并返回:`background_url`、`footer_text`、`footer_beian`、
|
v2 新增键(需求 ①②③④⑩)一并返回:`background_url`、`footer_text`、`footer_beian`、
|
||||||
`notify_enabled`、`max_save_count`、`max_file_size` 等。
|
`notify_enabled`、`max_save_count`、`max_file_size` 等。
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -351,7 +351,7 @@ curl -s "http://localhost:8466/admin/file/preview?id=41&maxChars=100" -H "Author
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 存储引擎热切换:POST /admin/storage/switch(26.9)
|
## 存储引擎热切换:POST /admin/storage/switch(v3)
|
||||||
|
|
||||||
运行时切换存储引擎,**无需重启**:
|
运行时切换存储引擎,**无需重启**:
|
||||||
|
|
||||||
@@ -374,14 +374,14 @@ curl -s -X POST http://localhost:8466/admin/storage/switch \
|
|||||||
部分更新(JSON 对象,未提供的键不变;表单亦可)。**PATCH 为主名,POST 为兼容别名**。
|
部分更新(JSON 对象,未提供的键不变;表单亦可)。**PATCH 为主名,POST 为兼容别名**。
|
||||||
|
|
||||||
- 仅接受管理端可见键(见上响应键集合),未知键忽略。
|
- 仅接受管理端可见键(见上响应键集合),未知键忽略。
|
||||||
- 数值型键自动转型:`openUpload`、`enableChunk`、`uploadSize`、`storageLimit`、限流四组、`max_save_seconds`、`adminSessionExpire`、`showAdminAddr`;26.9 新增 `max_save_count`、`max_file_size`、`notify_enabled`;`opacity` 为浮点。
|
- 数值型键自动转型:`openUpload`、`enableChunk`、`uploadSize`、`storageLimit`、限流四组、`max_save_seconds`、`adminSessionExpire`、`showAdminAddr`;v2 新增 `max_save_count`、`max_file_size`、`notify_enabled`;`opacity` 为浮点。
|
||||||
- **26.9**:`site_domain`(站点对外域名)可经本端点设置,非法格式 400(仅 http/https、主机+端口、不带路径)。
|
- **v3.1**:`site_domain`(站点对外域名)可经本端点设置,非法格式 400(仅 http/https、主机+端口、不带路径)。
|
||||||
- **26.9 引擎键**:`storage_engine` 不经本端点修改(走 `POST /admin/storage/switch`);引擎参数键
|
- **v3 引擎键**:`storage_engine` 不经本端点修改(走 `POST /admin/storage/switch`);引擎参数键
|
||||||
(`local_storage_path`、`webdav_url`、`webdav_root_path`、`webdav_username`、`webdav_password`、
|
(`local_storage_path`、`webdav_url`、`webdav_root_path`、`webdav_username`、`webdav_password`、
|
||||||
`s3_endpoint_url`、`s3_region_name`、`s3_bucket_name`、`s3_access_key_id`、`s3_secret_access_key`、
|
`s3_endpoint_url`、`s3_region_name`、`s3_bucket_name`、`s3_access_key_id`、`s3_secret_access_key`、
|
||||||
`aws_session_token`、`s3_addressing_style`)可经本端点保存——保存后对应引擎实例缓存失效,
|
`aws_session_token`、`s3_addressing_style`)可经本端点保存——保存后对应引擎实例缓存失效,
|
||||||
下次切换/构建生效;敏感键空串或 `******` 表示不修改。
|
下次切换/构建生效;敏感键空串或 `******` 表示不修改。
|
||||||
- **26.9 schema 校验**(`settings.KVSchema`,越界一律 400,中文错误信息):
|
- **v2 schema 校验**(`settings.KVSchema`,越界一律 400,中文错误信息):
|
||||||
- 整型边界:`max_file_size` ≤ 10GiB(10737418240)、`max_save_count` ≤ 100000、`max_save_seconds` ≤ 31536000(365 天)、`notify_enabled` ∈ {0,1}、`uploadCount` 1~10000、`uploadMinute` 1~1440 等;
|
- 整型边界:`max_file_size` ≤ 10GiB(10737418240)、`max_save_count` ≤ 100000、`max_save_seconds` ≤ 31536000(365 天)、`notify_enabled` ∈ {0,1}、`uploadCount` 1~10000、`uploadMinute` 1~1440 等;
|
||||||
- 字符串长度:`background_url` ≤ 2048、`footer_text` ≤ 2000、`footer_beian` ≤ 128、`notify_title` ≤ 128、`notify_content` ≤ 2000 字符;
|
- 字符串长度:`background_url` ≤ 2048、`footer_text` ≤ 2000、`footer_beian` ≤ 128、`notify_title` ≤ 128、`notify_content` ≤ 2000 字符;
|
||||||
- 列表键 `expireStyle` / `allowed_file_types`:须为字符串数组(或逗号分隔串)且至少保留一项;
|
- 列表键 `expireStyle` / `allowed_file_types`:须为字符串数组(或逗号分隔串)且至少保留一项;
|
||||||
@@ -432,20 +432,3 @@ curl -s -X PATCH http://localhost:8466/admin/settings/password \
|
|||||||
```json
|
```json
|
||||||
{ "code": 401, "msg": "旧密码错误" }
|
{ "code": 401, "msg": "旧密码错误" }
|
||||||
```
|
```
|
||||||
|
|
||||||
## 手动回收:POST /admin/recycle/run
|
|
||||||
|
|
||||||
**26.9**:手动触发一轮过期分享回收(定时循环之外的管理端入口)。回收范围:
|
|
||||||
时间已过期、次数已耗尽、创建时间超过 `retention_days` 的分享——删除记录并
|
|
||||||
连带删除存储对象(SHA512 去重开启时做引用计数,仍有其他分享引用的对象保留)。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -s -X POST "http://localhost:8466/admin/recycle/run" -H "Authorization: Bearer $TOKEN"
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{ "code": 200, "msg": "ok", "data": { "removed": 3 } }
|
|
||||||
```
|
|
||||||
|
|
||||||
相关配置键:`recycle_enabled`(定时开关)、`recycle_interval`(扫描间隔)、
|
|
||||||
`retention_days`(最长存储时长)、`dedup_enabled`(引用计数开关)。
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
| 客户端 | `ip` | 可信代理场景解析 XFF 后的真实 IP |
|
| 客户端 | `ip` | 可信代理场景解析 XFF 后的真实 IP |
|
||||||
| 终端信息 | `user_agent` | 原始 UA |
|
| 终端信息 | `user_agent` | 原始 UA |
|
||||||
| 设备解析 | `device_os` / `device_browser` / `device_type` | 由 UA 解析(如 Windows/Chrome/desktop) |
|
| 设备解析 | `device_os` / `device_browser` / `device_type` | 由 UA 解析(如 Windows/Chrome/desktop) |
|
||||||
| 动作 | `action` | `upload`(上传类) / `download`(取件/下载类) / `admin`(管理端敏感操作,26.9 新增) |
|
| 动作 | `action` | `upload`(上传类) / `download`(取件/下载类) / `admin`(管理端敏感操作,v2.5.6 新增) |
|
||||||
| 结果 | `result` | `success` / `denied`(拒绝:401/403/423/428)/ `failed`(失败:其余 4xx/5xx 或业务报错) |
|
| 结果 | `result` | `success` / `denied`(拒绝:401/403/423/428)/ `failed`(失败:其余 4xx/5xx 或业务报错) |
|
||||||
| 字节数 | `size_bytes` | 文件总大小;`transferred_bytes` 实际传输(**Range 下载只计实际区间字节**;下载由中间件自动统计,上传由各 handler 填充) |
|
| 字节数 | `size_bytes` | 文件总大小;`transferred_bytes` 实际传输(**Range 下载只计实际区间字节**;下载由中间件自动统计,上传由各 handler 填充) |
|
||||||
| 耗时 | `duration_ms` | 毫秒 |
|
| 耗时 | `duration_ms` | 毫秒 |
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ FCB_STORAGE_ENGINE=webdav
|
|||||||
|
|
||||||
非法值直接启动失败:`FCB_STORAGE_ENGINE 无效值 "xxx",仅支持 local|s3|webdav`。
|
非法值直接启动失败:`FCB_STORAGE_ENGINE 无效值 "xxx",仅支持 local|s3|webdav`。
|
||||||
|
|
||||||
**26.9 运行时热切换**:管理端 `POST /admin/storage/switch`(或后台设置页「存储引擎」卡)可在不重启的情况下切换引擎——
|
**v3 运行时热切换**:管理端 `POST /admin/storage/switch`(或后台设置页「存储引擎」卡)可在不重启的情况下切换引擎——
|
||||||
先构建新引擎并健康检查,通过才生效;失败 503 保持原引擎。当前引擎持久化在 settings KV `storage_engine`(空=回落启动值)。
|
先构建新引擎并健康检查,通过才生效;失败 503 保持原引擎。当前引擎持久化在 settings KV `storage_engine`(空=回落启动值)。
|
||||||
各引擎参数(存储目录/服务地址/存储桶/密钥)同样在后台设置页运行时可改;保存后对应引擎实例缓存失效,下次切换/构建生效。
|
各引擎参数(存储目录/服务地址/存储桶/密钥)同样在后台设置页运行时可改;保存后对应引擎实例缓存失效,下次切换/构建生效。
|
||||||
|
|
||||||
## 文件归属引擎(26.9)
|
## 文件归属引擎(v3)
|
||||||
|
|
||||||
每条分享记录(`file_codes.engine`)与上传会话(`upload_chunks.engine` / `presign_upload_sessions.engine`)
|
每条分享记录(`file_codes.engine`)与上传会话(`upload_chunks.engine` / `presign_upload_sessions.engine`)
|
||||||
在创建时戳记当时的引擎名。下载、分片合并、删除按**归属引擎**操作——切换引擎后,旧引擎里的文件仍可正常下载与删除
|
在创建时戳记当时的引擎名。下载、分片合并、删除按**归属引擎**操作——切换引擎后,旧引擎里的文件仍可正常下载与删除
|
||||||
|
|||||||
+20
-29
@@ -1,15 +1,15 @@
|
|||||||
# 环境变量与配置项
|
# 环境变量与配置项
|
||||||
|
|
||||||
配置分三层:**默认值 → `FCB_*` 环境变量 → 数据库 settings KV(管理端运行时修改)**。
|
配置分三层:**默认值 → `FCB_*` 环境变量 → 数据库 settings KV(管理端运行时修改)**。
|
||||||
26.9 起进程必需的环境变量为空集:数据库默认 **SQLite**(modernc.org/sqlite 纯 Go 驱动,零外部依赖,
|
v2 起进程必需的环境变量为空集:数据库默认 **SQLite**(modernc.org/sqlite 纯 Go 驱动,零外部依赖,
|
||||||
DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DSN` 必需(需求 ⑧)。
|
DSN 缺省落 `./data/filecodebox.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DSN` 必需(需求 ⑧)。
|
||||||
|
|
||||||
## 环境变量(进程级)
|
## 环境变量(进程级)
|
||||||
|
|
||||||
| 变量 | 必需 | 默认 | 说明 |
|
| 变量 | 必需 | 默认 | 说明 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` / `postgres`(需求 ⑧) |
|
| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` / `postgres`(需求 ⑧) |
|
||||||
| `FCB_DB_DSN` | 视驱动 | `./data/fileshare.db` | postgres:连接串(**必需**,如 `postgres://user:pass@host:5432/filecodebox?sslmode=disable`);sqlite:数据库文件路径(可空,父目录自动创建) |
|
| `FCB_DB_DSN` | 视驱动 | `./data/filecodebox.db` | postgres:连接串(**必需**,如 `postgres://user:pass@host:5432/filecodebox?sslmode=disable`);sqlite:数据库文件路径(可空,父目录自动创建) |
|
||||||
| `FCB_REDIS_ADDR` | ❌ | 空 | Redis 地址(如 `redis:6379`),也支持 `redis://[:password@]host:port[/db]` / `rediss://`(TLS)URL 形式;**空则缓存降级为进程内存实现**(缓存故障时自动降级为进程内限流计数) |
|
| `FCB_REDIS_ADDR` | ❌ | 空 | Redis 地址(如 `redis:6379`),也支持 `redis://[:password@]host:port[/db]` / `rediss://`(TLS)URL 形式;**空则缓存降级为进程内存实现**(缓存故障时自动降级为进程内限流计数) |
|
||||||
| `FCB_REDIS_DB` | ❌ | `0` | Redis 逻辑库号 0-15;URL 形式地址显式携带 `/N` 时以 URL 为准 |
|
| `FCB_REDIS_DB` | ❌ | `0` | Redis 逻辑库号 0-15;URL 形式地址显式携带 `/N` 时以 URL 为准 |
|
||||||
| `FCB_ADMIN_PASSWORD` | ❌ | 空 | 设置后服务首次启动即自动初始化管理员(≥8 位,不足告警跳过),消除 `/setup` 被抢占窗口;初始化完成后建议移除 |
|
| `FCB_ADMIN_PASSWORD` | ❌ | 空 | 设置后服务首次启动即自动初始化管理员(≥8 位,不足告警跳过),消除 `/setup` 被抢占窗口;初始化完成后建议移除 |
|
||||||
@@ -26,19 +26,19 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
|
|||||||
`FCB_WEBDAV_URL`、`FCB_WEBDAV_USERNAME`、`FCB_WEBDAV_PASSWORD`、`FCB_WEBDAV_ROOT_PATH`。
|
`FCB_WEBDAV_URL`、`FCB_WEBDAV_USERNAME`、`FCB_WEBDAV_PASSWORD`、`FCB_WEBDAV_ROOT_PATH`。
|
||||||
|
|
||||||
部署用编排变量(`deploy/.env.example`):`WEB_PORT`(默认 8466)、
|
部署用编排变量(`deploy/.env.example`):`WEB_PORT`(默认 8466)、
|
||||||
`POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB`(默认 fileshare,仅 `--profile postgres` 时使用)。
|
`POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB`(默认 filecodebox,仅 `--profile postgres` 时使用)。
|
||||||
|
|
||||||
## 配置项(settings KV,默认值对齐参考实现)
|
## 配置项(settings KV,默认值对齐参考实现)
|
||||||
|
|
||||||
> 键名/类型/默认值/边界以 `server/internal/config/schema.go` 的 `KVSchema()` 为单一事实来源
|
> 键名/类型/默认值/边界以 `server/internal/config/schema.go` 的 `KVSchema()` 为单一事实来源
|
||||||
> (schema 同步测试保证与 defaults() 逐键一致);26.9 新增键统一 snake_case。
|
> (schema 同步测试保证与 defaults() 逐键一致);v2 新增键统一 snake_case。
|
||||||
|
|
||||||
### 站点信息与展示(需求 ①②③)
|
### 站点信息与展示(需求 ①②③)
|
||||||
|
|
||||||
| 键 | 类型/边界 | 默认 | 说明 |
|
| 键 | 类型/边界 | 默认 | 说明 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `site_name` / `name` | string | 文件快传 | 站点名称(`site_name` 优先) |
|
| `site_name` / `name` | string | 文件快传 | 站点名称(`site_name` 优先) |
|
||||||
| `site_domain` | string,≤256 | 空 | **26.9**:站点对外域名(`http(s)://host[:port]`,不带路径;裸主机自动补 `http://`)。配置后分享链接(结果卡/管理端复制)用该域名生成——内网部署也能把公网链接发出去;留空=用当前访问地址 |
|
| `site_domain` | string,≤256 | 空 | **v3.1**:站点对外域名(`http(s)://host[:port]`,不带路径;裸主机自动补 `http://`)。配置后分享链接(结果卡/管理端复制)用该域名生成——内网部署也能把公网链接发出去;留空=用当前访问地址 |
|
||||||
| `description` | string | 开箱即用的文件快传系统 | 站点描述 |
|
| `description` | string | 开箱即用的文件快传系统 | 站点描述 |
|
||||||
| `page_explain` | string | (合规声明) | 页面说明文案 |
|
| `page_explain` | string | (合规声明) | 页面说明文案 |
|
||||||
| `keywords` | string | 文件快传, 文件分享… | SEO 关键词 |
|
| `keywords` | string | 文件快传, 文件分享… | SEO 关键词 |
|
||||||
@@ -46,10 +46,10 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
|
|||||||
| `favicon_url` | string | 空(前端回落本地打包 `/assets/favicon-*.png`,需求 ⑤) | favicon / 备用 Logo |
|
| `favicon_url` | string | 空(前端回落本地打包 `/assets/favicon-*.png`,需求 ⑤) | favicon / 备用 Logo |
|
||||||
| `opacity` | float | 0.9 | 界面不透明度 |
|
| `opacity` | float | 0.9 | 界面不透明度 |
|
||||||
| `background` | string | 空 | 背景图 URL(参考实现既有键,v1 兼容保留) |
|
| `background` | string | 空 | 背景图 URL(参考实现既有键,v1 兼容保留) |
|
||||||
| `background_url` | string,≤2048 字符 | 空 | **26.9 需求 ①**:背景图 URL 或上传后地址(空=主题默认;取值时 legacy `background` 键兜底)。管理端保存时校验协议白名单:仅 `http(s)`、`data:image/*` 与站内相对路径(防 `javascript:` 注入,非法 400) |
|
| `background_url` | string,≤2048 字符 | 空 | **v2 需求 ①**:背景图 URL 或上传后地址(空=主题默认;取值时 legacy `background` 键兜底)。管理端保存时校验协议白名单:仅 `http(s)`、`data:image/*` 与站内相对路径(防 `javascript:` 注入,非法 400) |
|
||||||
| `footer_text` | string,≤2000 字符 | 空 | **26.9 需求 ②**:页脚自定义内容(纯文本或受控 HTML 片段) |
|
| `footer_text` | string,≤2000 字符 | 空 | **v2 需求 ②**:页脚自定义内容(纯文本或受控 HTML 片段) |
|
||||||
| `footer_beian` | string,≤128 字符 | 空 | **26.9 需求 ②**:备案号(如 `京ICP备2024xxxxxx号-1`),展示于页脚 |
|
| `footer_beian` | string,≤128 字符 | 空 | **v2 需求 ②**:备案号(如 `京ICP备2024xxxxxx号-1`),展示于页脚 |
|
||||||
| `notify_enabled` | int(0/1) | 1 | **26.9 需求 ③**:通知开关(1=前台右上角悬浮窗展示 / 0=关闭) |
|
| `notify_enabled` | int(0/1) | 1 | **v2 需求 ③**:通知开关(1=前台右上角悬浮窗展示 / 0=关闭) |
|
||||||
| `notify_title` | string,≤128 字符 | 系统通知 | 通知标题 |
|
| `notify_title` | string,≤128 字符 | 系统通知 | 通知标题 |
|
||||||
| `notify_content` | string,≤2000 字符 | 欢迎使用… | 通知正文(**服务端白名单净化**:仅保留纯文本与 `<a href>` 为 http(s)/站内相对/`#` 锚点的链接,其余标签与事件属性剥离,保存与读取双侧生效) |
|
| `notify_content` | string,≤2000 字符 | 欢迎使用… | 通知正文(**服务端白名单净化**:仅保留纯文本与 `<a href>` 为 http(s)/站内相对/`#` 锚点的链接,其余标签与事件属性剥离,保存与读取双侧生效) |
|
||||||
| `showAdminAddr` | int(0/1) | 0 | 是否展示后台入口 |
|
| `showAdminAddr` | int(0/1) | 0 | 是否展示后台入口 |
|
||||||
@@ -59,8 +59,8 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
|
|||||||
|
|
||||||
| 键 | 类型/边界 | 默认 | 说明 |
|
| 键 | 类型/边界 | 默认 | 说明 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `max_save_seconds` | int64,0~31536000 | 0 | 最长保存秒数上限(0=仅默认 7 天兜底;>0 时按时间过期超限 403「限制最长时间为 X,可换用其他方式」)。**26.9**:管理界面以「小时/天」下拉单位编辑(≥1 天自动显示天),提交时前端换算为秒——canonical 单位保持秒,接口语义不变 |
|
| `max_save_seconds` | int64,0~31536000 | 0 | 最长保存秒数上限(0=仅默认 7 天兜底;>0 时按时间过期超限 403「限制最长时间为 X,可换用其他方式」)。**v3**:管理界面以「小时/天」下拉单位编辑(≥1 天自动显示天),提交时前端换算为秒——canonical 单位保持秒,接口语义不变 |
|
||||||
| `max_save_count` | int,0~100000 | 0 | **26.9 新增**:单次分享最大可取(保存)次数上限(0=不限制;`expire_style=count` 且 `expire_value` 超上限时 403「限制次数最多为 N 次」) |
|
| `max_save_count` | int,0~100000 | 0 | **v2 新增**:单次分享最大可取(保存)次数上限(0=不限制;`expire_style=count` 且 `expire_value` 超上限时 403「限制次数最多为 N 次」) |
|
||||||
| `expireStyle` | []string | `["day","hour","minute","forever","count"]` | 允许的过期方式白名单(上传时不在白名单 400「过期时间类型错误」) |
|
| `expireStyle` | []string | `["day","hour","minute","forever","count"]` | 允许的过期方式白名单(上传时不在白名单 400「过期时间类型错误」) |
|
||||||
|
|
||||||
### 存储策略(需求 ④⑩)
|
### 存储策略(需求 ④⑩)
|
||||||
@@ -68,7 +68,7 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
|
|||||||
| 键 | 类型/边界 | 默认 | 说明 |
|
| 键 | 类型/边界 | 默认 | 说明 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `uploadSize` | int64,1024~10GiB | 10485760(10MB) | 单文件大小上限(字节),参考实现语义;`max_file_size=0` 时作为生效上限 |
|
| `uploadSize` | int64,1024~10GiB | 10485760(10MB) | 单文件大小上限(字节),参考实现语义;`max_file_size=0` 时作为生效上限 |
|
||||||
| `max_file_size` | int64,0~10GiB | 0 | **26.9 新增**:存储策略-单文件上限(字节),0=回落 `uploadSize`;超出 403(文案 humanSize 自适应 B/KB/MB/GB)。**26.9**:管理界面以「MB/GB」下拉单位编辑(≥1 GiB 自动显示 GB),提交时前端换算为字节 |
|
| `max_file_size` | int64,0~10GiB | 0 | **v2 新增**:存储策略-单文件上限(字节),0=回落 `uploadSize`;超出 403(文案 humanSize 自适应 B/KB/MB/GB)。**v3**:管理界面以「MB/GB」下拉单位编辑(≥1 GiB 自动显示 GB),提交时前端换算为字节 |
|
||||||
| `allowed_file_types` | []string | `["*"]` | 允许类型白名单(扩展名/MIME 通配,`*` 不限制;非白名单 403「不允许上传该类型文件」) |
|
| `allowed_file_types` | []string | `["*"]` | 允许类型白名单(扩展名/MIME 通配,`*` 不限制;非白名单 403「不允许上传该类型文件」) |
|
||||||
| `storageLimit` | int64,≥0 | 0 | 站点总容量(字节),0=不限制(超限 507) |
|
| `storageLimit` | int64,≥0 | 0 | 站点总容量(字节),0=不限制(超限 507) |
|
||||||
| `openUpload` | int(0/1) | 1 | 游客上传开关(0 时上传接口要求管理员令牌 403) |
|
| `openUpload` | int(0/1) | 1 | 游客上传开关(0 时上传接口要求管理员令牌 403) |
|
||||||
@@ -79,15 +79,6 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
|
|||||||
| 键 | 类型/边界 | 默认 | 说明 |
|
| 键 | 类型/边界 | 默认 | 说明 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `uploadCount` / `uploadMinute` | int(1~10000 / 1~1440) | 10 / 1 | 窗口内允许上传次数 / 窗口分钟(上传成功才计数,超限 423;管理端修改后运行时同步限流规则,立即生效) |
|
| `uploadCount` / `uploadMinute` | int(1~10000 / 1~1440) | 10 / 1 | 窗口内允许上传次数 / 窗口分钟(上传成功才计数,超限 423;管理端修改后运行时同步限流规则,立即生效) |
|
||||||
| `upload_rate` / `download_rate` | int64(0~1 GiB/s) | 0 / 0 | **26.9**:上下行带宽字节/秒,0=不限速;管理端改后立即生效(每请求动态读 KV)。详见《[带宽限速](13-bandwidth.md)》 |
|
|
||||||
| `recycle_enabled` | 0/1 | 1 | **26.9**:过期分享自动回收开关(定时扫描 + 取件惰性回收) |
|
|
||||||
| `recycle_interval` | int64(60~86400 秒) | 1800 | **26.9**:回收扫描间隔(秒;管理端以分钟展示) |
|
|
||||||
| `retention_days` | int64(0~3650 天) | 0 | **26.9**:最长存储时长(天),上传超过该天数的分享自动回收;0=不限制 |
|
|
||||||
| `dedup_enabled` | 0/1 | 1 | **26.9**:SHA512 内容去重,相同文件仅存储一份(多分享引用同一对象,引用计数删除) |
|
|
||||||
| `direct_download` | 0/1 | 1 | **26.9**:对象存储直链下载(S3 引擎 302 到预签名 URL,文件不经过本站带宽) |
|
|
||||||
| `direct_link_expire` | int64(60~3600 秒) | 900 | **26.9**:直链签名有效期(秒;不超过分享剩余时效) |
|
|
||||||
| `hotlink_enabled` | 0/1 | 0 | **26.9**:下载防盗链(Referer 白名单校验;空 Referer 放行) |
|
|
||||||
| `hotlink_whitelist` | string(≤2048) | 空 | **26.9**:防盗链白名单,逗号分隔域名,支持 `*.example.com` 通配;空=仅同源放行 |
|
|
||||||
| `errorCount` / `errorMinute` | int | 10 / 1 | 取件错误(失败计数)+ metadata 每次计数 |
|
| `errorCount` / `errorMinute` | int | 10 / 1 | 取件错误(失败计数)+ metadata 每次计数 |
|
||||||
| `loginCount` / `loginMinute` | int | 5 / 15 | 登录失败计数 |
|
| `loginCount` / `loginMinute` | int | 5 / 15 | 登录失败计数 |
|
||||||
|
|
||||||
@@ -99,9 +90,9 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
|
|||||||
| `jwt_secret` | 空 | JWT 签名密钥(初始化/改密时自动生成轮换;不下发;`settings.SensitiveKeys` 双模式下一致屏蔽) |
|
| `jwt_secret` | 空 | JWT 签名密钥(初始化/改密时自动生成轮换;不下发;`settings.SensitiveKeys` 双模式下一致屏蔽) |
|
||||||
| `adminSessionExpire` | 604800(7 天) | 管理员会话秒数(须 1~365 整天) |
|
| `adminSessionExpire` | 604800(7 天) | 管理员会话秒数(须 1~365 整天) |
|
||||||
|
|
||||||
### 存储引擎(26.9 运行时可配 + 热切换)
|
### 存储引擎(v3 运行时可配 + 热切换)
|
||||||
|
|
||||||
**`storage_engine`**(26.9 新增键):string,`local|s3|webdav`,默认空=回落启动值 `FCB_STORAGE_ENGINE`。
|
**`storage_engine`**(v3 新增键):string,`local|s3|webdav`,默认空=回落启动值 `FCB_STORAGE_ENGINE`。
|
||||||
运行时切换走 **`POST /admin/storage/switch`**(JWT 保护):构建新引擎 → 健康检查通过才生效;
|
运行时切换走 **`POST /admin/storage/switch`**(JWT 保护):构建新引擎 → 健康检查通过才生效;
|
||||||
失败返回 503「存储引擎切换失败,已保持原引擎: …」且不改 KV。成功后持久化 `storage_engine`,重启沿用。
|
失败返回 503「存储引擎切换失败,已保持原引擎: …」且不改 KV。成功后持久化 `storage_engine`,重启沿用。
|
||||||
`GET /api/v1/config` 公开下发 `storage_engine` 当前名(仅名称,任何引擎参数/凭据不下发)。
|
`GET /api/v1/config` 公开下发 `storage_engine` 当前名(仅名称,任何引擎参数/凭据不下发)。
|
||||||
@@ -123,10 +114,10 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
|
|||||||
| `webdav_root_path` | `filebox_storage` |
|
| `webdav_root_path` | `filebox_storage` |
|
||||||
| `webdav_proxy` | 0 |
|
| `webdav_proxy` | 0 |
|
||||||
|
|
||||||
> 敏感键 `webdav_password` / `s3_secret_access_key` / `aws_session_token`(26.9 加入 `settings.SensitiveKeys`):
|
> 敏感键 `webdav_password` / `s3_secret_access_key` / `aws_session_token`(v3 加入 `settings.SensitiveKeys`):
|
||||||
> 管理端 GET 返回掩码 `******`;PATCH 时空串或 `******` 表示不修改。直接写库 settings KV 后重启同样生效。
|
> 管理端 GET 返回掩码 `******`;PATCH 时空串或 `******` 表示不修改。直接写库 settings KV 后重启同样生效。
|
||||||
|
|
||||||
## 策略动态生效机制(26.9 需求 ④⑩)
|
## 策略动态生效机制(v2 需求 ④⑩)
|
||||||
|
|
||||||
上传页通过 `GET /api/v1/config` 的 `config` 字段读取**当前策略快照**并在范围内渲染选项;
|
上传页通过 `GET /api/v1/config` 的 `config` 字段读取**当前策略快照**并在范围内渲染选项;
|
||||||
上传链路(`/share/file`、`/chunk/*`、`/presign/*`)**每次请求实时读取** settings KV 同一组值校验:
|
上传链路(`/share/file`、`/chunk/*`、`/presign/*`)**每次请求实时读取** settings KV 同一组值校验:
|
||||||
@@ -137,7 +128,7 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
|
|||||||
|
|
||||||
## 公共配置接口
|
## 公共配置接口
|
||||||
|
|
||||||
前端启动时经 `GET /api/v1/config` 获取站点公开配置(无需认证;26.9 扩展需求 ①②③④⑩):
|
前端启动时经 `GET /api/v1/config` 获取站点公开配置(无需认证;v2 扩展需求 ①②③④⑩):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -170,7 +161,7 @@ DSN 缺省落 `./data/fileshare.db`);`FCB_DB_DRIVER=postgres` 时 `FCB_DB_DS
|
|||||||
"openUpload": true
|
"openUpload": true
|
||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"version": "26.9",
|
"version": "2.5.6",
|
||||||
"features": { "chunkUpload": false, "guestUpload": true }
|
"features": { "chunkUpload": false, "guestUpload": true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +182,7 @@ curl -s http://localhost:8466/api/v1/health
|
|||||||
"code": 200, "msg": "ok",
|
"code": 200, "msg": "ok",
|
||||||
"data": {
|
"data": {
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"version": "26.9",
|
"version": "2.5.6",
|
||||||
"storage": "local",
|
"storage": "local",
|
||||||
"time": "2025-06-01T12:00:00+08:00"
|
"time": "2025-06-01T12:00:00+08:00"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,13 +1,13 @@
|
|||||||
# Logo 自定义
|
# Logo 自定义
|
||||||
|
|
||||||
## 默认 Logo(内置,26.9 需求 ⑤)
|
## 默认 Logo(内置,v2 需求 ⑤)
|
||||||
|
|
||||||
| 项 | 默认值 | 用途 |
|
| 项 | 默认值 | 用途 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 页面导航 Logo | 前端打包本地资源 `/assets/logo-*.svg`(源:`web/src/assets/brand/logo.svg`) | 导航栏 `<img>`;`config.logo_url` 为空时回落使用 |
|
| 页面导航 Logo | 前端打包本地资源 `/assets/logo-*.svg`(源:`web/src/assets/brand/logo.svg`) | 导航栏 `<img>`;`config.logo_url` 为空时回落使用 |
|
||||||
| favicon / 备用 Logo | 前端打包本地资源 `/assets/favicon-*.png`(源:`web/src/assets/brand/favicon.png`) | `index.html` `<link rel="icon">` + 动态 favicon 回落 |
|
| favicon / 备用 Logo | 前端打包本地资源 `/assets/favicon-*.png`(源:`web/src/assets/brand/favicon.png`) | `index.html` `<link rel="icon">` + 动态 favicon 回落 |
|
||||||
|
|
||||||
26.9 起默认不再引用远程 URL:`GET /api/v1/config` 中 `logo_url`/`favicon_url` 默认下发空串,
|
v2 起默认不再引用远程 URL:`GET /api/v1/config` 中 `logo_url`/`favicon_url` 默认下发空串,
|
||||||
前端 `displayLogoUrl`/`displayFaviconUrl` 判空后回落到打包的本地资源。
|
前端 `displayLogoUrl`/`displayFaviconUrl` 判空后回落到打包的本地资源。
|
||||||
管理端仍可设置任意 URL 全站替换(三步如下)。
|
管理端仍可设置任意 URL 全站替换(三步如下)。
|
||||||
|
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
# 带宽限速(26.9)
|
|
||||||
|
|
||||||
> 26.9 新增。管理端可独立设置**上行(上传)/ 下行(下载)带宽**,单位字节/秒(前端 UI 友好单位为 MB/s),
|
|
||||||
> 0=不限速。修改后**立即生效**(每请求动态读取最新 KV,不需重启容器/进程)。
|
|
||||||
|
|
||||||
## 适用对象
|
|
||||||
|
|
||||||
| 方向 | 范围 | 说明 |
|
|
||||||
|---|---|---|
|
|
||||||
| 上行 | `POST /share/file`、`POST /chunk/upload/{id}/{idx}`、`POST /chunk/upload`(扁平兼容)、`PUT /presign/upload/proxy/{id}` | 包裹 `c.Request.Body`,对 multipart 解析与表单字段读取天然节流 |
|
|
||||||
| 下行 | `GET /share/download?key=&code=`、`GET /share/select?code=`(文件流)、`GET /admin/file/download?id=` | 包裹 `storage.ReadCloser`(local/webdav/s3 代理下载),Range 分段也按节流后的字节流推进 |
|
|
||||||
|
|
||||||
> **不在限速范围**:S3 预签名**直传**(客户端 → S3 桶直连,**服务端无法介入**);
|
|
||||||
> 文本分享下载(GET `/share/select` 命中 `text` 字段时直接返回 JSON 字符串,体积通常远小于 1MB)。
|
|
||||||
|
|
||||||
## 配置项
|
|
||||||
|
|
||||||
| 键 | 类型/边界 | 默认 | 说明 |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `upload_rate` | int64(0 ~ 1 073 741 824,即 1 GiB/s) | 0 | 上行字节/秒;0=不限速 |
|
|
||||||
| `download_rate` | int64(0 ~ 1 073 741 824) | 0 | 下行字节/秒;0=不限速 |
|
|
||||||
|
|
||||||
UI 输入:管理端「系统设置 → 上传频率限制」卡底部两个字段,**MB/s** 整数,0=不限速。
|
|
||||||
保存时前端把 `MB/s × 1024 × 1024` 换算为字节/秒写入 KV。
|
|
||||||
|
|
||||||
## 算法(实现原理)
|
|
||||||
|
|
||||||
基于**时间窗的精确调度**,每个 `Read` 调用:
|
|
||||||
|
|
||||||
```
|
|
||||||
start ← 首次 Read 的时刻
|
|
||||||
bytes ← 累计已读字节(每次 Read 累加返回值 n)
|
|
||||||
expected = start + bytes / rate // 按限速值推算"应到达时间"
|
|
||||||
if now < expected:
|
|
||||||
sleep(expected - now) // 超前则阻塞补齐
|
|
||||||
return n, err
|
|
||||||
```
|
|
||||||
|
|
||||||
保证长期速率严格 ≤ `rate`,瞬时由调用方 Read 块大小自然突发。**不按字节微睡眠**——避免单次 Read 在低速场景下被调度抖动放大。
|
|
||||||
|
|
||||||
> 早期版本按每字节 sleep 1/rate 秒,被 Mac 调度粒度(约 10µs)放大后实际速率只有目标值的 1/3;26.9 修正为整块对齐 sleep。
|
|
||||||
|
|
||||||
## 公开接口
|
|
||||||
|
|
||||||
`GET /api/v1/config` 与 `GET /admin/config/get` 均下发当前生效值(数值型,字节/秒):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"config": {
|
|
||||||
"upload_rate": 0,
|
|
||||||
"download_rate": 0,
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 修改(管理端)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# PATCH /admin/config/update
|
|
||||||
curl -X PATCH http://localhost:8466/admin/config/update \
|
|
||||||
-H "Authorization: Bearer <admin-jwt>" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"upload_rate": 1048576, "download_rate": 524288}'
|
|
||||||
# ↑ 上行 1 MB/s、下行 512 KB/s
|
|
||||||
```
|
|
||||||
|
|
||||||
边界校验(来自 `settings.KVSchema`,越界 400 中文错误):
|
|
||||||
|
|
||||||
- `upload_rate` / `download_rate` 必须是整数,0 ≤ 值 ≤ 1073741824
|
|
||||||
|
|
||||||
## 验证(端到端)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1) 设下载限速 200 KB/s
|
|
||||||
curl -X PATCH http://localhost:8466/admin/config/update \
|
|
||||||
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
|
|
||||||
-d '{"download_rate": 204800}' -w '\nHTTP %{http_code}\n'
|
|
||||||
# 2) 上传 2 MB 测试文件(自动随机取件码或自定义 5~8 位)
|
|
||||||
curl -X POST http://localhost:8466/share/file -F file=@/tmp/2mb.bin \
|
|
||||||
-F "expire_value=1" -F "expire_style=day" -F "code="
|
|
||||||
# 3) 管理员接口下载(不消耗取件次数,便于压测)
|
|
||||||
ID=$(curl -s "http://localhost:8466/admin/file/list?page=1&size=20" \
|
|
||||||
-H "Authorization: Bearer <token>" | jq '.data.data[0].id')
|
|
||||||
time curl -s -o /tmp/dl.bin "http://localhost:8466/admin/file/download?id=$ID" \
|
|
||||||
-H "Authorization: Bearer <token>"
|
|
||||||
# 期望 ~10s(2 097 152 字节 / 200 000 B/s = 10.486 s)
|
|
||||||
# 实测 10.65s(误差 1.5%,含 HTTP 头/响应开销)
|
|
||||||
```
|
|
||||||
|
|
||||||
> 测完记得恢复:`PATCH /admin/config/update` 设回 0。
|
|
||||||
|
|
||||||
## 性能开销
|
|
||||||
|
|
||||||
- 0 速率路径:完全透传,无任何额外分配/锁/计时。
|
|
||||||
- 限速路径:每次 Read 多两次 `time.Now()` 与一次整数除法;`mutex.Lock` 仅在并发复用同一 reader 时阻塞(每请求独立实例,正常无争用)。
|
|
||||||
|
|
||||||
## 边界与注意
|
|
||||||
|
|
||||||
1. **S3 预签名直传不可限速**:客户端拿到预签名 URL 后直连 S3 桶,不经本服务。`PUT /presign/upload/proxy/{id}`(**代理模式**)仍受上行限速;前端实现可在管理端提示用户这一点。
|
|
||||||
2. **共享存储后端的"突发"**:令牌桶改为时间窗对齐后,单次 Read 仍可能瞬时突发到调用方请求的块大小(典型 32~64 KB)。需更严格平滑的可在调用方调小读取块大小(如 4 KB)。
|
|
||||||
3. **代理/反代层限速**:如部署在 Nginx / Cloudflare 后,公网入站还会经过代理的限速/带宽上限;本服务限速只对服务进程侧可见。
|
|
||||||
4. **Range 多区间**:HTTP Range 单区间请求的字节流仍按限速推进;多区间未实现,回退全量。
|
|
||||||
5. **重置**:UI 「恢复默认」按钮把两个速率归 0(不限速)。
|
|
||||||
|
|
||||||
## 相关
|
|
||||||
|
|
||||||
- 配置 KV 概览:见《环境变量与配置》`upload_rate` / `download_rate` 条目
|
|
||||||
- 管理端修改示例:见《管理后台 API》`PATCH /admin/config/update`
|
|
||||||
- OpenAPI schema:`docs/openapi.yaml` 的 `/admin/config/update` requestBody.properties
|
|
||||||
+2
-49
@@ -93,7 +93,7 @@ paths:
|
|||||||
footer_text: { type: string, description: '需求 ② 页脚自定义内容' }
|
footer_text: { type: string, description: '需求 ② 页脚自定义内容' }
|
||||||
footer_beian: { type: string, description: '需求 ② 备案号' }
|
footer_beian: { type: string, description: '需求 ② 备案号' }
|
||||||
storage_engine: { type: string, description: 当前存储引擎名(仅名称,凭据不下发) }
|
storage_engine: { type: string, description: 当前存储引擎名(仅名称,凭据不下发) }
|
||||||
site_domain: { type: string, description: '26.9 站点对外域名(空=用当前访问地址)' }
|
site_domain: { type: string, description: 'v3.1 站点对外域名(空=用当前访问地址)' }
|
||||||
notify_enabled: { type: integer, enum: [0, 1], description: '需求 ③ 通知开关(1=右上角悬浮窗)' }
|
notify_enabled: { type: integer, enum: [0, 1], description: '需求 ③ 通知开关(1=右上角悬浮窗)' }
|
||||||
notify_title: { type: string }
|
notify_title: { type: string }
|
||||||
notify_content: { type: string }
|
notify_content: { type: string }
|
||||||
@@ -166,16 +166,6 @@ paths:
|
|||||||
loginMinute: { type: integer, default: 15 }
|
loginMinute: { type: integer, default: 15 }
|
||||||
uploadCount: { type: integer, default: 10 }
|
uploadCount: { type: integer, default: 10 }
|
||||||
uploadMinute: { type: integer, default: 1 }
|
uploadMinute: { type: integer, default: 1 }
|
||||||
upload_rate: { type: integer, format: int64, default: 0, description: '26.9 上行带宽字节/秒;0=不限速' }
|
|
||||||
download_rate: { type: integer, format: int64, default: 0, description: '26.9 下行带宽字节/秒;0=不限速' }
|
|
||||||
recycle_enabled: { type: integer, enum: [0, 1], default: 1, description: '26.9 过期分享自动回收开关' }
|
|
||||||
recycle_interval: { type: integer, format: int64, default: 1800, description: '26.9 回收扫描间隔秒(60~86400)' }
|
|
||||||
retention_days: { type: integer, format: int64, default: 0, description: '26.9 最长存储时长天(0~3650;0=不限制)' }
|
|
||||||
dedup_enabled: { type: integer, enum: [0, 1], default: 1, description: '26.9 SHA512 内容去重开关' }
|
|
||||||
direct_download: { type: integer, enum: [0, 1], default: 1, description: '26.9 对象存储直链下载开关' }
|
|
||||||
direct_link_expire: { type: integer, format: int64, default: 900, description: '26.9 直链签名有效期秒(60~3600)' }
|
|
||||||
hotlink_enabled: { type: integer, enum: [0, 1], default: 0, description: '26.9 下载防盗链开关' }
|
|
||||||
hotlink_whitelist: { type: string, maxLength: 2048, default: '', description: '26.9 防盗链白名单(逗号分隔域名,支持 *.example.com)' }
|
|
||||||
allowed_file_types: { type: string, default: '*' }
|
allowed_file_types: { type: string, default: '*' }
|
||||||
openUpload: { type: boolean, default: true }
|
openUpload: { type: boolean, default: true }
|
||||||
enableChunk: { type: boolean, default: false }
|
enableChunk: { type: boolean, default: false }
|
||||||
@@ -215,7 +205,7 @@ paths:
|
|||||||
文本 ≤222KB(请求体全局上限 1MiB,Content-Length>441KB 直接 403);上传类接口(成功计数 upload 限流,423 超限),经审计中间件落库。
|
文本 ≤222KB(请求体全局上限 1MiB,Content-Length>441KB 直接 403);上传类接口(成功计数 upload 限流,423 超限),经审计中间件落库。
|
||||||
保存策略(需求 ④):expire_style 须在 expireStyle 白名单(400);count 型受
|
保存策略(需求 ④):expire_style 须在 expireStyle 白名单(400);count 型受
|
||||||
max_save_count 约束(403「限制次数最多为 N 次」);时间型受 max_save_seconds 约束(403)。
|
max_save_count 约束(403「限制次数最多为 N 次」);时间型受 max_save_seconds 约束(403)。
|
||||||
26.9:支持 JSON 提交(字段同名);空文本 400「分享内容不能为空」;
|
v3.1:支持 JSON 提交(字段同名);空文本 400「分享内容不能为空」;
|
||||||
可选 code 自定义提取码(5-8 位字母或数字,占用 400「该提取码已被占用」)。
|
可选 code 自定义提取码(5-8 位字母或数字,占用 400「该提取码已被占用」)。
|
||||||
requestBody:
|
requestBody:
|
||||||
content:
|
content:
|
||||||
@@ -1385,16 +1375,6 @@ paths:
|
|||||||
storageLimit: { type: integer, format: int64 }
|
storageLimit: { type: integer, format: int64 }
|
||||||
uploadCount: { type: integer }
|
uploadCount: { type: integer }
|
||||||
uploadMinute: { type: integer }
|
uploadMinute: { type: integer }
|
||||||
upload_rate: { type: integer, format: int64, description: '26.9 上行带宽字节/秒;0=不限速' }
|
|
||||||
download_rate: { type: integer, format: int64, description: '26.9 下行带宽字节/秒;0=不限速' }
|
|
||||||
recycle_enabled: { type: integer, enum: [0, 1], description: '26.9 过期分享自动回收开关' }
|
|
||||||
recycle_interval: { type: integer, format: int64, description: '26.9 回收扫描间隔秒(60~86400)' }
|
|
||||||
retention_days: { type: integer, format: int64, description: '26.9 最长存储时长天(0~3650)' }
|
|
||||||
dedup_enabled: { type: integer, enum: [0, 1], description: '26.9 SHA512 内容去重开关' }
|
|
||||||
direct_download: { type: integer, enum: [0, 1], description: '26.9 对象存储直链下载开关' }
|
|
||||||
direct_link_expire: { type: integer, format: int64, description: '26.9 直链签名有效期秒(60~3600)' }
|
|
||||||
hotlink_enabled: { type: integer, enum: [0, 1], description: '26.9 下载防盗链开关' }
|
|
||||||
hotlink_whitelist: { type: string, maxLength: 2048, description: '26.9 防盗链白名单' }
|
|
||||||
admin_token: { type: string, example: '' }
|
admin_token: { type: string, example: '' }
|
||||||
_engine_hint:
|
_engine_hint:
|
||||||
type: object
|
type: object
|
||||||
@@ -1545,33 +1525,6 @@ paths:
|
|||||||
"401": { $ref: "#/components/responses/Unauthorized" }
|
"401": { $ref: "#/components/responses/Unauthorized" }
|
||||||
"503": { $ref: "#/components/responses/ServiceUnavailable" }
|
"503": { $ref: "#/components/responses/ServiceUnavailable" }
|
||||||
|
|
||||||
/admin/recycle/run:
|
|
||||||
post:
|
|
||||||
tags: [admin]
|
|
||||||
summary: 手动触发一轮过期回收(26.9)
|
|
||||||
description: |
|
|
||||||
回收时间已过期、次数已耗尽、创建时间超过 retention_days 的分享:
|
|
||||||
删除记录并连带删除存储对象(dedup_enabled 开启时做引用计数,
|
|
||||||
仍有其他分享引用的对象保留)。返回本轮回收条数。
|
|
||||||
operationId: adminRecycleRun
|
|
||||||
security:
|
|
||||||
- bearerAuth: []
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: 回收完成
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
allOf:
|
|
||||||
- $ref: '#/components/schemas/Envelope'
|
|
||||||
- type: object
|
|
||||||
properties:
|
|
||||||
data:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
removed: { type: integer, description: 本轮回收条数 }
|
|
||||||
"401": { $ref: '#/components/responses/Unauthorized' }
|
|
||||||
|
|
||||||
/admin/settings/password:
|
/admin/settings/password:
|
||||||
patch:
|
patch:
|
||||||
tags: [管理后台]
|
tags: [管理后台]
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
|
|
||||||
- 位置:`server/internal/api/helpers.go:114-125`、`validatePickupCode`(4 位下限)
|
- 位置:`server/internal/api/helpers.go:114-125`、`validatePickupCode`(4 位下限)
|
||||||
- 原状:`code_generate_type=number` 时仅 9 万空间(5 位数字),默认 `errorCount=10/分/IP` 下单 IP 需约 6 天扫完,分布式多 IP 可显著缩短;自定义码允许 4 位(36⁴≈168 万)。
|
- 原状:`code_generate_type=number` 时仅 9 万空间(5 位数字),默认 `errorCount=10/分/IP` 下单 IP 需约 6 天扫完,分布式多 IP 可显著缩短;自定义码允许 4 位(36⁴≈168 万)。
|
||||||
- ✅ 修复:自定义提码最小长度 4 → **5 位**(`pickupCodeMinLen=5`,36⁵≈6000 万空间);测试 `custom_code_test.go` 与 `TestPickupCodeMinLen` 同步更新。
|
- ✅ 修复:自定义提码最小长度 4 → **5 位**(`pickupCodeMinLen=5`,36⁵≈6000 万空间);测试 `v31_test.go` 与 `TestPickupCodeMinLen` 同步更新。
|
||||||
|
|
||||||
### L4 `enableChunk` 开关后端不强制 ✅ 已修复
|
### L4 `enableChunk` 开关后端不强制 ✅ 已修复
|
||||||
|
|
||||||
|
|||||||
+6
-7
@@ -11,7 +11,7 @@ server/
|
|||||||
├── cmd/server/main.go # 入口:装配 配置→DB→缓存→设置→审计→中间件→路由
|
├── cmd/server/main.go # 入口:装配 配置→DB→缓存→设置→审计→中间件→路由
|
||||||
└── internal/
|
└── internal/
|
||||||
├── config/ # 配置:FCB_* 环境变量基线 + DB settings KV 运行时覆盖
|
├── config/ # 配置:FCB_* 环境变量基线 + DB settings KV 运行时覆盖
|
||||||
│ └── schema.go # 26.9 新配置键 schema(键名/类型/默认值/边界)
|
│ └── schema.go # v2 新配置键 schema(键名/类型/默认值/边界)
|
||||||
├── model/ # GORM 模型 + 双方言 AutoMigrate
|
├── model/ # GORM 模型 + 双方言 AutoMigrate
|
||||||
├── cache/ # 缓存统一接口:redis.go / memory.go 双实现
|
├── cache/ # 缓存统一接口:redis.go / memory.go 双实现
|
||||||
├── database/ # 双方言连接与迁移(sqlite 默认 / postgres 可选)
|
├── database/ # 双方言连接与迁移(sqlite 默认 / postgres 可选)
|
||||||
@@ -27,7 +27,7 @@ server/
|
|||||||
| 变量 | 必需 | 默认 | 说明 |
|
| 变量 | 必需 | 默认 | 说明 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` \| `postgres`(需求 ⑧) |
|
| `FCB_DB_DRIVER` | ❌ | `sqlite` | 数据库驱动:`sqlite` \| `postgres`(需求 ⑧) |
|
||||||
| `FCB_DB_DSN` | 视驱动 | `./data/fileshare.db` | postgres:连接串(**必需**),如 `postgres://user:pass@host:5432/fileshare?sslmode=disable`;sqlite:数据库文件路径(可空,自动创建 `data/` 目录) |
|
| `FCB_DB_DSN` | 视驱动 | `./data/filecodebox.db` | postgres:连接串(**必需**),如 `postgres://user:pass@host:5432/filecodebox?sslmode=disable`;sqlite:数据库文件路径(可空,自动创建 `data/` 目录) |
|
||||||
| `FCB_REDIS_ADDR` | ❌ | 空 | 为空时缓存降级为内存实现 |
|
| `FCB_REDIS_ADDR` | ❌ | 空 | 为空时缓存降级为内存实现 |
|
||||||
| `FCB_LISTEN` | ❌ | `:8466` | 监听地址 |
|
| `FCB_LISTEN` | ❌ | `:8466` | 监听地址 |
|
||||||
| `FCB_STORAGE_ENGINE` | ❌ | `local` | `local` \| `s3` \| `webdav` |
|
| `FCB_STORAGE_ENGINE` | ❌ | `local` | `local` \| `s3` \| `webdav` |
|
||||||
@@ -36,14 +36,14 @@ server/
|
|||||||
### 数据库模式(需求 ⑧)
|
### 数据库模式(需求 ⑧)
|
||||||
|
|
||||||
- **SQLite(默认)**:`FCB_DB_DRIVER=sqlite`(或缺省)。零 DSN 零依赖启动,数据库文件
|
- **SQLite(默认)**:`FCB_DB_DRIVER=sqlite`(或缺省)。零 DSN 零依赖启动,数据库文件
|
||||||
默认 `./data/fileshare.db`(`FCB_DB_DSN` 可覆盖路径;父目录自动创建)。
|
默认 `./data/filecodebox.db`(`FCB_DB_DSN` 可覆盖路径;父目录自动创建)。
|
||||||
连接参数:`busy_timeout=10s` + `WAL` 日志模式 + `foreign_keys=1`(通过 DSN pragma 注入)。
|
连接参数:`busy_timeout=10s` + `WAL` 日志模式 + `foreign_keys=1`(通过 DSN pragma 注入)。
|
||||||
- **Postgres(可选)**:`FCB_DB_DRIVER=postgres` 且必须提供 `FCB_DB_DSN`,否则启动报错。
|
- **Postgres(可选)**:`FCB_DB_DRIVER=postgres` 且必须提供 `FCB_DB_DSN`,否则启动报错。
|
||||||
连接池沿用 v1 参数(32/8、1h 轮换)。
|
连接池沿用 v1 参数(32/8、1h 轮换)。
|
||||||
- 双方言共用 GORM 抽象:AutoMigrate、settings KV、全部业务查询方言无关;
|
- 双方言共用 GORM 抽象:AutoMigrate、settings KV、全部业务查询方言无关;
|
||||||
唯一原生 DDL(migrates 台账表)在 `database.createMigratesTable` 内部分支处理。
|
唯一原生 DDL(migrates 台账表)在 `database.createMigratesTable` 内部分支处理。
|
||||||
|
|
||||||
## 26.9 新增配置键(internal/config/schema.go 为单一事实来源)
|
## v2 新增配置键(internal/config/schema.go 为单一事实来源)
|
||||||
|
|
||||||
| 键 | 类型 | 默认 | 说明 |
|
| 键 | 类型 | 默认 | 说明 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
@@ -57,7 +57,6 @@ server/
|
|||||||
| `max_file_size` | int64 | `0` | 需求 ⑩:存储策略-单文件上限字节(0=回落 `uploadSize`,≤10GiB) |
|
| `max_file_size` | int64 | `0` | 需求 ⑩:存储策略-单文件上限字节(0=回落 `uploadSize`,≤10GiB) |
|
||||||
| `uploadSize` / `allowed_file_types` / `storageLimit` / `openUpload` | 既有 | - | 存储策略既有键(语义不变) |
|
| `uploadSize` / `allowed_file_types` / `storageLimit` / `openUpload` | 既有 | - | 存储策略既有键(语义不变) |
|
||||||
| `uploadCount` / `uploadMinute` | 既有 | `10` / `1` | 上传频率限制(对齐参考 `ip_limit["upload"]`) |
|
| `uploadCount` / `uploadMinute` | 既有 | `10` / `1` | 上传频率限制(对齐参考 `ip_limit["upload"]`) |
|
||||||
| `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)为元数据源;
|
管理端与文档(t2/t4)以 `config.KVSchema()`(`settings.KVSchema()` re-export)为元数据源;
|
||||||
schema 同步测试保证 `KVSchema()` 与 `defaults()` 逐键一致。
|
schema 同步测试保证 `KVSchema()` 与 `defaults()` 逐键一致。
|
||||||
@@ -145,13 +144,13 @@ middleware.AuditRecordRequest(c, auditSvc, model.AuditResultSuccess, "")
|
|||||||
## 本地开发
|
## 本地开发
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 默认 SQLite 模式:零依赖,数据库落 ./data/fileshare.db
|
# 默认 SQLite 模式:零依赖,数据库落 ./data/filecodebox.db
|
||||||
go run ./cmd/server # 启动于 :8466
|
go run ./cmd/server # 启动于 :8466
|
||||||
curl localhost:8466/api/v1/health
|
curl localhost:8466/api/v1/health
|
||||||
|
|
||||||
# Postgres 模式(可选)
|
# Postgres 模式(可选)
|
||||||
export FCB_DB_DRIVER=postgres
|
export FCB_DB_DRIVER=postgres
|
||||||
export FCB_DB_DSN='postgres://postgres:postgres@localhost:5432/fileshare?sslmode=disable'
|
export FCB_DB_DSN='postgres://postgres:postgres@localhost:5432/filecodebox?sslmode=disable'
|
||||||
go run ./cmd/server
|
go run ./cmd/server
|
||||||
|
|
||||||
# 双方言单测:sqlite 始终执行;postgres 需真实实例(FCB_TEST_PG_DSN 指向测试库)
|
# 双方言单测:sqlite 始终执行;postgres 需真实实例(FCB_TEST_PG_DSN 指向测试库)
|
||||||
|
|||||||
+15
-26
@@ -15,26 +15,19 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/api"
|
"filecodebox/internal/api"
|
||||||
"fileshare/internal/audit"
|
"filecodebox/internal/audit"
|
||||||
"fileshare/internal/cache"
|
"filecodebox/internal/cache"
|
||||||
"fileshare/internal/config"
|
"filecodebox/internal/config"
|
||||||
"fileshare/internal/database"
|
"filecodebox/internal/database"
|
||||||
"fileshare/internal/janitor"
|
"filecodebox/internal/janitor"
|
||||||
"fileshare/internal/middleware"
|
"filecodebox/internal/middleware"
|
||||||
"fileshare/internal/settings"
|
"filecodebox/internal/settings"
|
||||||
"fileshare/internal/storage"
|
"filecodebox/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APP_VERSION 版本号,对齐参考仓库 VERSION。26.9 起改为 var 以便
|
// APP_VERSION 版本号,对齐参考仓库 VERSION。
|
||||||
// goreleaser 通过 -ldflags -X main.APP_VERSION=… 注入发布版本。
|
const APP_VERSION = "2.5.6"
|
||||||
//
|
|
||||||
// 编译时可选注入(goreleaser 触发),不注入则保持默认 26.9。
|
|
||||||
var (
|
|
||||||
APP_VERSION = "26.9"
|
|
||||||
BuildCommit = "dev"
|
|
||||||
BuildDate = "unknown"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||||
@@ -105,7 +98,7 @@ func main() {
|
|||||||
middleware.LimitMeta: {Count: cfg.GetInt("errorCount"), Window: minutes(cfg.GetInt("errorMinute"))},
|
middleware.LimitMeta: {Count: cfg.GetInt("errorCount"), Window: minutes(cfg.GetInt("errorMinute"))},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 7. 存储引擎:注入配置 → 构造 → 健康预检(需求 ④;26.9 包装为可热切换 Manager)
|
// 7. 存储引擎:注入配置 → 构造 → 健康预检(需求 ④;v3 包装为可热切换 Manager)
|
||||||
storage.SetEngineOptions(buildEngineOptions(cfg))
|
storage.SetEngineOptions(buildEngineOptions(cfg))
|
||||||
bootStore, err := storage.NewEngine(ctx, cfg.Engine())
|
bootStore, err := storage.NewEngine(ctx, cfg.Engine())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -117,7 +110,7 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
log.Printf("[boot] 存储引擎 %s 健康检查通过", cfg.Engine())
|
log.Printf("[boot] 存储引擎 %s 健康检查通过", cfg.Engine())
|
||||||
}
|
}
|
||||||
// 26.9:Manager 包装——保存/读取委托当前引擎;管理端可热切换
|
// v3:Manager 包装——保存/读取委托当前引擎;管理端可热切换
|
||||||
//(构建闭包在每次切换前用最新 KV 刷新 EngineOptions,参数改动即时生效)
|
//(构建闭包在每次切换前用最新 KV 刷新 EngineOptions,参数改动即时生效)
|
||||||
store := storage.NewManager(cfg.Engine(), bootStore, func(name string) (storage.Storage, error) {
|
store := storage.NewManager(cfg.Engine(), bootStore, func(name string) (storage.Storage, error) {
|
||||||
storage.SetEngineOptions(buildEngineOptions(cfg))
|
storage.SetEngineOptions(buildEngineOptions(cfg))
|
||||||
@@ -149,12 +142,8 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 9. HTTP 服务
|
// 9. HTTP 服务
|
||||||
// M5:后台清理循环(过期预留/超时会话/直传残留对象),启动后 10 分钟首跑。
|
// M5:后台清理循环(过期预留/超时会话/直传残留对象),启动后 10 分钟首跑
|
||||||
// 26.9:过期分享回收(recycle_enabled/recycle_interval/retention_days 动态读取)
|
janitor.Start(ctx, db, store, 10*time.Minute)
|
||||||
janitor.Start(ctx, db, store, 10*time.Minute, &janitor.Recycler{
|
|
||||||
Enabled: cfg.RecycleEnabled,
|
|
||||||
RetentionDays: cfg.RetentionDays,
|
|
||||||
})
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: cfg.Env.Listen,
|
Addr: cfg.Env.Listen,
|
||||||
Handler: r,
|
Handler: r,
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
module fileshare
|
module filecodebox
|
||||||
|
|
||||||
go 1.27.1
|
go 1.27.1
|
||||||
|
|
||||||
@@ -13,7 +13,6 @@ require (
|
|||||||
github.com/glebarez/sqlite v1.11.0
|
github.com/glebarez/sqlite v1.11.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||||
github.com/redis/go-redis/v9 v9.17.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/driver/postgres v1.6.2
|
||||||
gorm.io/gorm v1.31.2
|
gorm.io/gorm v1.31.2
|
||||||
)
|
)
|
||||||
@@ -67,6 +66,7 @@ require (
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
golang.org/x/arch v0.20.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/net v0.58.0 // indirect
|
||||||
golang.org/x/sync v0.22.0 // indirect
|
golang.org/x/sync v0.22.0 // indirect
|
||||||
golang.org/x/sys v0.47.0 // indirect
|
golang.org/x/sys v0.47.0 // indirect
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@@ -13,13 +12,12 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/config"
|
"filecodebox/internal/config"
|
||||||
"fileshare/internal/janitor"
|
"filecodebox/internal/middleware"
|
||||||
"fileshare/internal/middleware"
|
"filecodebox/internal/model"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/response"
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/settings"
|
||||||
"fileshare/internal/settings"
|
"filecodebox/internal/storage"
|
||||||
"fileshare/internal/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// minutesDuration 分钟数转 Duration(0 回退 1 分钟,对齐 main.go 语义)。
|
// minutesDuration 分钟数转 Duration(0 回退 1 分钟,对齐 main.go 语义)。
|
||||||
@@ -80,12 +78,9 @@ func registerAdmin(r *gin.Engine, d *Deps) {
|
|||||||
authed.PATCH("/settings/password", d.adminChangePassword)
|
authed.PATCH("/settings/password", d.adminChangePassword)
|
||||||
authed.POST("/settings/password", d.adminChangePassword)
|
authed.POST("/settings/password", d.adminChangePassword)
|
||||||
|
|
||||||
// 26.9 存储引擎:运行时热切换(健康检查通过才生效,失败保持原引擎)
|
// v3 存储引擎:运行时热切换(健康检查通过才生效,失败保持原引擎)
|
||||||
authed.POST("/storage/switch", d.adminStorageSwitch)
|
authed.POST("/storage/switch", d.adminStorageSwitch)
|
||||||
|
|
||||||
// 26.9 过期回收:手动触发一轮(定时循环之外的管理端入口)
|
|
||||||
authed.POST("/recycle/run", d.adminRecycleRun)
|
|
||||||
|
|
||||||
// 审计日志查询(需求 ③;logs 为 list 的别名)
|
// 审计日志查询(需求 ③;logs 为 list 的别名)
|
||||||
authed.GET("/audit/list", d.adminAuditList)
|
authed.GET("/audit/list", d.adminAuditList)
|
||||||
authed.GET("/audit/logs", d.adminAuditList)
|
authed.GET("/audit/logs", d.adminAuditList)
|
||||||
@@ -409,7 +404,7 @@ func buildAdminFileItem(fc *model.FileCodes, now time.Time) gin.H {
|
|||||||
"hasDownloadLimit": fc.ExpiredCount >= 0, "has_download_limit": fc.ExpiredCount >= 0,
|
"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,
|
"isPermanent": fc.ExpiredAt == nil && fc.ExpiredCount < 0, "is_permanent": fc.ExpiredAt == nil && fc.ExpiredCount < 0,
|
||||||
"remainingDownloads": remaining, "remaining_downloads": remaining,
|
"remainingDownloads": remaining, "remaining_downloads": remaining,
|
||||||
"engine": fc.Engine, // 26.9:归属引擎(管理端展示/排查用)
|
"engine": fc.Engine, // v3:归属引擎(管理端展示/排查用)
|
||||||
}
|
}
|
||||||
if fc.FileHash != nil {
|
if fc.FileHash != nil {
|
||||||
item["fileHash"] = *fc.FileHash
|
item["fileHash"] = *fc.FileHash
|
||||||
@@ -789,7 +784,7 @@ func (d *Deps) adminFilePreview(c *gin.Context) {
|
|||||||
// ============ 配置 ============
|
// ============ 配置 ============
|
||||||
|
|
||||||
// configKeys 管理端可见/可改的配置键(不含 jwt_secret;admin_token 屏蔽展示)。
|
// configKeys 管理端可见/可改的配置键(不含 jwt_secret;admin_token 屏蔽展示)。
|
||||||
// 26.9 新增键(需求 ①②③④⑩):背景图、页脚、通知开关、保存/存储策略、频率限制。
|
// v2 新增键(需求 ①②③④⑩):背景图、页脚、通知开关、保存/存储策略、频率限制。
|
||||||
var configKeys = []string{
|
var configKeys = []string{
|
||||||
"site_name", "name", "description", "page_explain", "keywords",
|
"site_name", "name", "description", "page_explain", "keywords",
|
||||||
"notify_title", "notify_content", "notify_enabled", "logo_url", "favicon_url",
|
"notify_title", "notify_content", "notify_enabled", "logo_url", "favicon_url",
|
||||||
@@ -799,14 +794,10 @@ var configKeys = []string{
|
|||||||
"code_generate_type", "enableChunk",
|
"code_generate_type", "enableChunk",
|
||||||
"uploadMinute", "uploadCount", "errorMinute", "errorCount",
|
"uploadMinute", "uploadCount", "errorMinute", "errorCount",
|
||||||
"loginCount", "loginMinute",
|
"loginCount", "loginMinute",
|
||||||
"opacity", "background", "showAdminAddr", "robotsText", "site_domain", // 26.9:站点对外域名
|
"opacity", "background", "showAdminAddr", "robotsText", "site_domain", // v3.1:站点对外域名
|
||||||
"upload_rate", "download_rate", // 26.9:上下行带宽字节/秒(0=不限速)
|
|
||||||
// 26.9 回收与下载安全
|
|
||||||
"recycle_enabled", "recycle_interval", "retention_days", "dedup_enabled",
|
|
||||||
"hotlink_enabled", "hotlink_whitelist", "direct_download", "direct_link_expire",
|
|
||||||
"adminSessionExpire", "storage_path", "local_storage_path",
|
"adminSessionExpire", "storage_path", "local_storage_path",
|
||||||
"file_storage",
|
"file_storage",
|
||||||
// 26.9 存储引擎与引擎参数(热切换;凭据为敏感键,get 掩码/update 空跳过)
|
// v3 存储引擎与引擎参数(热切换;凭据为敏感键,get 掩码/update 空跳过)
|
||||||
"storage_engine",
|
"storage_engine",
|
||||||
"local_storage_path",
|
"local_storage_path",
|
||||||
"webdav_url", "webdav_root_path", "webdav_username", "webdav_password",
|
"webdav_url", "webdav_root_path", "webdav_username", "webdav_password",
|
||||||
@@ -815,7 +806,7 @@ var configKeys = []string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// intConfigKeys 需按 schema 边界校验的整型键(adminConfigUpdate 归一化用)。
|
// intConfigKeys 需按 schema 边界校验的整型键(adminConfigUpdate 归一化用)。
|
||||||
// v1 既有键保留原语义;26.9 新增键(max_file_size/max_save_count/notify_enabled)
|
// v1 既有键保留原语义;v2 新增键(max_file_size/max_save_count/notify_enabled)
|
||||||
// 的边界来自 settings.KVSchema(单一事实来源在 config/schema.go)。
|
// 的边界来自 settings.KVSchema(单一事实来源在 config/schema.go)。
|
||||||
var intConfigKeys = []string{
|
var intConfigKeys = []string{
|
||||||
"openUpload", "enableChunk", "showAdminAddr", "storageLimit",
|
"openUpload", "enableChunk", "showAdminAddr", "storageLimit",
|
||||||
@@ -823,9 +814,6 @@ var intConfigKeys = []string{
|
|||||||
"loginCount", "loginMinute", "max_save_seconds", "uploadSize",
|
"loginCount", "loginMinute", "max_save_seconds", "uploadSize",
|
||||||
"adminSessionExpire",
|
"adminSessionExpire",
|
||||||
"max_save_count", "max_file_size", "notify_enabled",
|
"max_save_count", "max_file_size", "notify_enabled",
|
||||||
"upload_rate", "download_rate", // 26.9
|
|
||||||
"recycle_enabled", "recycle_interval", "retention_days", "dedup_enabled",
|
|
||||||
"hotlink_enabled", "direct_download", "direct_link_expire", // 26.9(hotlink_whitelist 为字符串键)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateConfigValue 按 settings.KVSchema 校验单个配置值:
|
// validateConfigValue 按 settings.KVSchema 校验单个配置值:
|
||||||
@@ -915,7 +903,7 @@ func toStrSlice(v any) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// adminConfigGet 读取配置(对齐参考 get_config:admin_token 屏蔽、jwt_secret 不下发)。
|
// adminConfigGet 读取配置(对齐参考 get_config:admin_token 屏蔽、jwt_secret 不下发)。
|
||||||
// 26.9:引擎凭据类敏感键返回掩码占位(前端表单"留空=不修改");storage_engine 为当前热切换后的引擎。
|
// v3:引擎凭据类敏感键返回掩码占位(前端表单"留空=不修改");storage_engine 为当前热切换后的引擎。
|
||||||
func (d *Deps) adminConfigGet(c *gin.Context) {
|
func (d *Deps) adminConfigGet(c *gin.Context) {
|
||||||
cfg := d.Cfg
|
cfg := d.Cfg
|
||||||
out := gin.H{}
|
out := gin.H{}
|
||||||
@@ -935,7 +923,7 @@ func (d *Deps) adminConfigGet(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
// jwt_secret 永不下发
|
// jwt_secret 永不下发
|
||||||
delete(out, "jwt_secret")
|
delete(out, "jwt_secret")
|
||||||
// 引擎运行时状态(26.9:热切换即时生效,无需重启)
|
// 引擎运行时状态(v3:热切换即时生效,无需重启)
|
||||||
out["_engine_hint"] = gin.H{
|
out["_engine_hint"] = gin.H{
|
||||||
"storage_backend": d.Store.CurrentName(),
|
"storage_backend": d.Store.CurrentName(),
|
||||||
"engines": gin.H{"local": true, "s3": true, "webdav": true},
|
"engines": gin.H{"local": true, "s3": true, "webdav": true},
|
||||||
@@ -983,7 +971,7 @@ func (d *Deps) adminConfigUpdate(c *gin.Context) {
|
|||||||
if !known {
|
if !known {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// 类型归一 + schema 边界校验(26.9:数值/字符串长度/列表键统一走 KVSchema)
|
// 类型归一 + schema 边界校验(v2:数值/字符串长度/列表键统一走 KVSchema)
|
||||||
isInt := false
|
isInt := false
|
||||||
for _, key := range intConfigKeys {
|
for _, key := range intConfigKeys {
|
||||||
if key == k {
|
if key == k {
|
||||||
@@ -1077,7 +1065,7 @@ func (d *Deps) adminConfigUpdate(c *gin.Context) {
|
|||||||
dbPatch["jwt_secret"] = settings.GenerateJWTSecret()
|
dbPatch["jwt_secret"] = settings.GenerateJWTSecret()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 26.9:site_domain 规范化(http(s)://host[:port];空=用当前地址)
|
// v3.1:site_domain 规范化(http(s)://host[:port];空=用当前地址)
|
||||||
if raw, ok := dbPatch["site_domain"]; ok {
|
if raw, ok := dbPatch["site_domain"]; ok {
|
||||||
sv, _ := raw.(string)
|
sv, _ := raw.(string)
|
||||||
normalized, err := normalizeSiteDomain(sv)
|
normalized, err := normalizeSiteDomain(sv)
|
||||||
@@ -1088,7 +1076,7 @@ func (d *Deps) adminConfigUpdate(c *gin.Context) {
|
|||||||
dbPatch["site_domain"] = normalized
|
dbPatch["site_domain"] = normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
// 26.9 引擎键处理:参数键与 storage_engine 分离。
|
// v3 引擎键处理:参数键与 storage_engine 分离。
|
||||||
// 1) storage_engine 只接受合法枚举;
|
// 1) storage_engine 只接受合法枚举;
|
||||||
// 2) 敏感凭据键空串/掩码=不修改(避免管理端表单回显把密钥抹掉);
|
// 2) 敏感凭据键空串/掩码=不修改(避免管理端表单回显把密钥抹掉);
|
||||||
// 3) 先持久化普通键+参数键 → Invalidate 对应引擎缓存 → 再尝试 Switch 新引擎;
|
// 3) 先持久化普通键+参数键 → Invalidate 对应引擎缓存 → 再尝试 Switch 新引擎;
|
||||||
@@ -1162,7 +1150,7 @@ func engineOfParamKeys(patch map[string]any) []string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// adminStorageSwitch 26.9 存储引擎热切换:{engine:"local"|"s3"|"webdav"}。
|
// adminStorageSwitch v3 存储引擎热切换:{engine:"local"|"s3"|"webdav"}。
|
||||||
// 成功:持久化 storage_engine KV 并返回当前引擎;失败:503 且原引擎不变。
|
// 成功:持久化 storage_engine KV 并返回当前引擎;失败:503 且原引擎不变。
|
||||||
func (d *Deps) adminStorageSwitch(c *gin.Context) {
|
func (d *Deps) adminStorageSwitch(c *gin.Context) {
|
||||||
var body struct {
|
var body struct {
|
||||||
@@ -1310,15 +1298,11 @@ func (d *Deps) fileByID(c *gin.Context, id int64) (*model.FileCodes, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deleteFileCode 删除分享记录与存储文件(文本分享无存储文件)。
|
// deleteFileCode 删除分享记录与存储文件(文本分享无存储文件)。
|
||||||
// 26.9:SHA512 去重开启时同一对象可能被多条分享引用——删除前按
|
|
||||||
// ContentHash+Engine+UUIDFileName 引用计数,仍有其他引用则保留对象。
|
|
||||||
func (d *Deps) deleteFileCode(c *gin.Context, fc *model.FileCodes) error {
|
func (d *Deps) deleteFileCode(c *gin.Context, fc *model.FileCodes) error {
|
||||||
if fc.Text == nil && fc.FilePath != nil && fc.UUIDFileName != nil {
|
if fc.Text == nil && fc.FilePath != nil && fc.UUIDFileName != nil {
|
||||||
// 26.9:删除走文件归属引擎(旧引擎里的文件也要能删掉)
|
// v3:删除走文件归属引擎(旧引擎里的文件也要能删掉)
|
||||||
if delStore, dErr := d.storeFor(fc.Engine); dErr == nil {
|
if delStore, dErr := d.storeFor(fc.Engine); dErr == nil {
|
||||||
if d.referencedByOther(c.Request.Context(), fc) {
|
if err := delStore.DeleteFile(c.Request.Context(), fileSavePath(fc)); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
||||||
// 还有其他分享引用该对象:仅删记录
|
|
||||||
} else if err := delStore.DeleteFile(c.Request.Context(), fileSavePath(fc)); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
|
||||||
return errInternal("存储文件删除失败: " + err.Error())
|
return errInternal("存储文件删除失败: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1330,31 +1314,6 @@ func (d *Deps) deleteFileCode(c *gin.Context, fc *model.FileCodes) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// referencedByOther 该分享的存储对象是否仍被其他分享引用(SHA512 去重)。
|
|
||||||
// 无 ContentHash(历史数据/去重未开启)时恒 false——按旧语义直接删对象。
|
|
||||||
func (d *Deps) referencedByOther(ctx context.Context, fc *model.FileCodes) bool {
|
|
||||||
if fc.ContentHash == nil || *fc.ContentHash == "" || fc.UUIDFileName == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var cnt int64
|
|
||||||
_ = d.DB.WithContext(ctx).Model(&model.FileCodes{}).
|
|
||||||
Where("content_hash = ? AND engine = ? AND uuid_file_name = ? AND id <> ?",
|
|
||||||
*fc.ContentHash, fc.Engine, *fc.UUIDFileName, fc.ID).
|
|
||||||
Count(&cnt).Error
|
|
||||||
return cnt > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// adminRecycleRun 手动触发一轮过期回收(26.9;定时循环之外的入口)。
|
|
||||||
// 返回 {removed: 本轮回收条数}。
|
|
||||||
func (d *Deps) adminRecycleRun(c *gin.Context) {
|
|
||||||
removed := janitor.RecycleExpired(c.Request.Context(), d.DB, d.Store, &janitor.Recycler{
|
|
||||||
Enabled: d.Cfg.RecycleEnabled,
|
|
||||||
RetentionDays: d.Cfg.RetentionDays,
|
|
||||||
})
|
|
||||||
auditRecordSuccess(c, d.AuditSvc)
|
|
||||||
response.OK(c, gin.H{"removed": removed})
|
|
||||||
}
|
|
||||||
|
|
||||||
// deleteMany 批量删除:返回 (已删除, 不存在, 失败)。
|
// deleteMany 批量删除:返回 (已删除, 不存在, 失败)。
|
||||||
func (d *Deps) deleteMany(c *gin.Context, ids []int64) (deleted []int64, missing []int64, failed []gin.H) {
|
func (d *Deps) deleteMany(c *gin.Context, ids []int64) (deleted []int64, missing []int64, failed []gin.H) {
|
||||||
deleted, missing = []int64{}, []int64{}
|
deleted, missing = []int64{}, []int64{}
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/middleware"
|
"filecodebox/internal/middleware"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
"fileshare/internal/storage"
|
"filecodebox/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// chunkExpireTTL 分片会话保留时长(M5:预留窗口由 24h 缩短为 2h;
|
// chunkExpireTTL 分片会话保留时长(M5:预留窗口由 24h 缩短为 2h;
|
||||||
@@ -99,7 +99,7 @@ func (d *Deps) chunkInit(c *gin.Context) {
|
|||||||
// 服务端按分片数上限校验总大小(防分片声明绕过)
|
// 服务端按分片数上限校验总大小(防分片声明绕过)
|
||||||
totalChunks := (req.FileSize + chunkSize - 1) / chunkSize
|
totalChunks := (req.FileSize + chunkSize - 1) / chunkSize
|
||||||
maxPossible := totalChunks * chunkSize
|
maxPossible := totalChunks * chunkSize
|
||||||
// 26.9 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
// v2 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
||||||
if err := d.CurrentUploadPolicy().CheckSize(maxPossible); err != nil {
|
if err := d.CurrentUploadPolicy().CheckSize(maxPossible); err != nil {
|
||||||
auditUploadEntry(c, "", safeName, req.FileSize, 0)
|
auditUploadEntry(c, "", safeName, req.FileSize, 0)
|
||||||
auditRecordFailed(c, d.AuditSvc, "文件大小超过限制")
|
auditRecordFailed(c, d.AuditSvc, "文件大小超过限制")
|
||||||
@@ -165,7 +165,7 @@ func (d *Deps) chunkInit(c *gin.Context) {
|
|||||||
ChunkHash: req.FileHash,
|
ChunkHash: req.FileHash,
|
||||||
FileName: safeName,
|
FileName: safeName,
|
||||||
SavePath: savePath,
|
SavePath: savePath,
|
||||||
Engine: d.Store.CurrentName(), // 26.9:会话归属引擎(分片/合并全程走同一引擎)
|
Engine: d.Store.CurrentName(), // v3:会话归属引擎(分片/合并全程走同一引擎)
|
||||||
}
|
}
|
||||||
if err := d.DB.WithContext(ctx).Create(&session).Error; err != nil {
|
if err := d.DB.WithContext(ctx).Create(&session).Error; err != nil {
|
||||||
releaseStorage(ctx, d.DB, resToken)
|
releaseStorage(ctx, d.DB, resToken)
|
||||||
@@ -389,7 +389,7 @@ func (d *Deps) saveOneChunk(c *gin.Context, ctx context.Context, session *model.
|
|||||||
ChunkSize: session.ChunkSize,
|
ChunkSize: session.ChunkSize,
|
||||||
FileName: session.FileName,
|
FileName: session.FileName,
|
||||||
SavePath: session.SavePath,
|
SavePath: session.SavePath,
|
||||||
Engine: session.Engine, // 26.9:继承会话引擎
|
Engine: session.Engine, // v3:继承会话引擎
|
||||||
}
|
}
|
||||||
if err := d.DB.WithContext(ctx).
|
if err := d.DB.WithContext(ctx).
|
||||||
Where("upload_id = ? AND chunk_index = ?", session.UploadID, idx).
|
Where("upload_id = ? AND chunk_index = ?", session.UploadID, idx).
|
||||||
@@ -444,7 +444,7 @@ func (d *Deps) chunkStatus(c *gin.Context) {
|
|||||||
type chunkCompleteRequest struct {
|
type chunkCompleteRequest struct {
|
||||||
ExpireValue int `json:"expire_value" form:"expire_value"`
|
ExpireValue int `json:"expire_value" form:"expire_value"`
|
||||||
ExpireStyle string `json:"expire_style" form:"expire_style"`
|
ExpireStyle string `json:"expire_style" form:"expire_style"`
|
||||||
Code string `json:"code" form:"code"` // 26.9:自定义提取码(4-8 位字母数字,空=随机)
|
Code string `json:"code" form:"code"` // v3.1:自定义提取码(4-8 位字母数字,空=随机)
|
||||||
}
|
}
|
||||||
|
|
||||||
// chunkComplete 合并分片并创建分享(对齐参考 complete_upload)。
|
// chunkComplete 合并分片并创建分享(对齐参考 complete_upload)。
|
||||||
@@ -489,7 +489,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
|||||||
respondError(c, err)
|
respondError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 26.9:自定义提取码(合并前校验,失败快速返回)
|
// v3.1:自定义提取码(合并前校验,失败快速返回)
|
||||||
if err := validatePickupCode(req.Code); err != nil {
|
if err := validatePickupCode(req.Code); err != nil {
|
||||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||||
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
||||||
@@ -539,7 +539,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
return rec.ChunkHash, nil
|
return rec.ChunkHash, nil
|
||||||
}
|
}
|
||||||
// 26.9:合并走会话归属引擎(会话创建时的引擎,即使中途热切换也不受影响)
|
// v3:合并走会话归属引擎(会话创建时的引擎,即使中途热切换也不受影响)
|
||||||
mergeStore, sErr := d.storeFor(session.Engine)
|
mergeStore, sErr := d.storeFor(session.Engine)
|
||||||
if sErr != nil {
|
if sErr != nil {
|
||||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, session.FileSize)
|
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, session.FileSize)
|
||||||
@@ -556,7 +556,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建分享记录(26.9:支持自定义提取码)
|
// 创建分享记录(v3.1:支持自定义提取码)
|
||||||
code, err := pickCustomCode(ctx, d.DB, d.Cfg, req.Code)
|
code, err := pickCustomCode(ctx, d.DB, d.Cfg, req.Code)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fc := model.FileCodes{
|
fc := model.FileCodes{
|
||||||
@@ -568,7 +568,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
|||||||
ExpiredAt: exp.ExpiredAt,
|
ExpiredAt: exp.ExpiredAt,
|
||||||
ExpiredCount: exp.ExpiredCount,
|
ExpiredCount: exp.ExpiredCount,
|
||||||
UsedCount: exp.UsedCount,
|
UsedCount: exp.UsedCount,
|
||||||
Engine: session.Engine, // 26.9:归属引擎戳
|
Engine: session.Engine, // v3:归属引擎戳
|
||||||
}
|
}
|
||||||
// 拆分路径与文件名(对齐参考:path=dirname(save_path), uuid=basename)
|
// 拆分路径与文件名(对齐参考:path=dirname(save_path), uuid=basename)
|
||||||
dir, name := splitDirBase(session.SavePath)
|
dir, name := splitDirBase(session.SavePath)
|
||||||
@@ -578,11 +578,7 @@ func (d *Deps) chunkComplete(c *gin.Context) {
|
|||||||
fc.Prefix = trimExt(name)
|
fc.Prefix = trimExt(name)
|
||||||
fc.Suffix = ext
|
fc.Suffix = ext
|
||||||
err = d.DB.WithContext(ctx).Create(&fc).Error
|
err = d.DB.WithContext(ctx).Create(&fc).Error
|
||||||
err = mapCodeConflict(err) // 26.9
|
err = mapCodeConflict(err) // v3.1
|
||||||
if err == nil {
|
|
||||||
// 26.9:SHA512 内容去重(命中则复用旧对象并删除本次副本)
|
|
||||||
d.applyDedup(ctx, mergeStore, session.SavePath, &fc)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// 成功:清理分片与记录(走归属引擎)
|
// 成功:清理分片与记录(走归属引擎)
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
// Package api — dedup.go SHA512 内容去重(26.9):
|
|
||||||
// 上传完成后计算对象 SHA512,命中已有分享(同哈希+同引擎)则复用其存储对象、
|
|
||||||
// 删除本次上传的副本——相同文件只存储一份。历史数据(无哈希)不受影响。
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
"fileshare/internal/model"
|
|
||||||
"fileshare/internal/storage"
|
|
||||||
)
|
|
||||||
|
|
||||||
// hashObject 流式计算存储对象 SHA512(hex);读取失败返回空串(去重按尽力而为降级)。
|
|
||||||
func hashObject(ctx context.Context, store storage.Storage, savePath string) string {
|
|
||||||
dl, err := store.Open(ctx, savePath, nil)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
defer func() { _ = dl.Close() }()
|
|
||||||
h := sha512.New()
|
|
||||||
if _, err := io.Copy(h, dl); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// applyDedup 上传落库后执行去重:
|
|
||||||
// 1. 计算刚保存对象的 SHA512;
|
|
||||||
// 2. 命中同哈希+同引擎的其他分享 → 复用其 FilePath/UUIDFileName,删除本次副本;
|
|
||||||
// 3. 未命中 → 只回填 ContentHash。
|
|
||||||
//
|
|
||||||
// 任何失败都不影响上传结果(记录保留、哈希留空 = 不参与去重)。
|
|
||||||
func (d *Deps) applyDedup(ctx context.Context, store storage.Storage, savedPath string, fc *model.FileCodes) {
|
|
||||||
if !d.Cfg.DedupEnabled() || fc == nil || fc.ID == 0 || fc.Text != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hash := hashObject(ctx, store, savedPath)
|
|
||||||
if hash == "" {
|
|
||||||
log.Printf("[dedup] 哈希计算失败 code=%s(跳过去重)", fc.Code)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
updates := map[string]any{"content_hash": hash}
|
|
||||||
|
|
||||||
var old model.FileCodes
|
|
||||||
err := d.DB.WithContext(ctx).
|
|
||||||
Where("content_hash = ? AND engine = ? AND id <> ? AND uuid_file_name IS NOT NULL",
|
|
||||||
hash, fc.Engine, fc.ID).
|
|
||||||
First(&old).Error
|
|
||||||
switch {
|
|
||||||
case err == nil && old.UUIDFileName != nil && old.FilePath != nil:
|
|
||||||
// 命中:复用旧对象,删除本次副本
|
|
||||||
updates["file_path"] = *old.FilePath
|
|
||||||
updates["uuid_file_name"] = *old.UUIDFileName
|
|
||||||
if err := store.DeleteFile(ctx, savedPath); err != nil {
|
|
||||||
log.Printf("[dedup] 删除重复副本失败 code=%s: %v", fc.Code, err)
|
|
||||||
}
|
|
||||||
log.Printf("[dedup] 命中同内容分享 code=%s 复用 %s", fc.Code, old.Code)
|
|
||||||
case err != nil && err != gorm.ErrRecordNotFound:
|
|
||||||
log.Printf("[dedup] 去重查询失败 code=%s: %v", fc.Code, err)
|
|
||||||
}
|
|
||||||
if err := d.DB.WithContext(ctx).Model(fc).Updates(updates).Error; err != nil {
|
|
||||||
log.Printf("[dedup] 回填哈希失败 code=%s: %v", fc.Code, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fc.ContentHash = &hash
|
|
||||||
if fp, ok := updates["file_path"]; ok {
|
|
||||||
s := fp.(string)
|
|
||||||
fc.FilePath = &s
|
|
||||||
}
|
|
||||||
if un, ok := updates["uuid_file_name"]; ok {
|
|
||||||
s := un.(string)
|
|
||||||
fc.UUIDFileName = &s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,12 +30,12 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/audit"
|
"filecodebox/internal/audit"
|
||||||
"fileshare/internal/config"
|
"filecodebox/internal/config"
|
||||||
"fileshare/internal/middleware"
|
"filecodebox/internal/middleware"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
"fileshare/internal/storage"
|
"filecodebox/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// apiError 统一的业务错误:handler 返回该错误并由 respondError 映射 HTTP 状态。
|
// apiError 统一的业务错误:handler 返回该错误并由 respondError 映射 HTTP 状态。
|
||||||
@@ -125,7 +125,7 @@ func generateCode(style string) string {
|
|||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ 自定义提取码(26.9,防撞库)============
|
// ============ 自定义提取码(v3.1,防撞库)============
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// pickupCodeMinLen 最小长度:L3 由 4 提升至 5(4 位码空间仅 168 万,
|
// 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
|
return custom, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapCodeConflict 26.9:分享记录创建失败时,若是自定义码唯一索引冲突(并发兜底,
|
// mapCodeConflict v3.1:分享记录创建失败时,若是自定义码唯一索引冲突(并发兜底,
|
||||||
// pickCustomCode 的预查重未覆盖竞态),转为友好 400;其余错误原样返回。
|
// pickCustomCode 的预查重未覆盖竞态),转为友好 400;其余错误原样返回。
|
||||||
func mapCodeConflict(err error) error {
|
func mapCodeConflict(err error) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -188,9 +188,9 @@ func mapCodeConflict(err error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ 站点对外域名(26.9)============
|
// ============ 站点对外域名(v3.1)============
|
||||||
|
|
||||||
// SiteDomain 站点对外域名规范化(26.9):空串合法(分享链接用当前访问地址)。
|
// SiteDomain 站点对外域名规范化(v3.1):空串合法(分享链接用当前访问地址)。
|
||||||
// 接受 http(s)://host[:port] 或裸 host[:port](自动补 http://,内网场景)。
|
// 接受 http(s)://host[:port] 或裸 host[:port](自动补 http://,内网场景)。
|
||||||
// 拒绝路径/查询/片段/用户信息/非 http(s) 协议/非法主机字符(防 javascript: 注入分享链接)。
|
// 拒绝路径/查询/片段/用户信息/非 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])?)*$`)
|
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 白名单并计算过期信息。
|
// resolveExpire 校验 expire_style 白名单并计算过期信息。
|
||||||
// 对齐参考:max_save_seconds>0 时为最长保存上限(超限 403),否则默认 7 天上限;
|
// 对齐参考:max_save_seconds>0 时为最长保存上限(超限 403),否则默认 7 天上限;
|
||||||
// 26.9 需求 ④:style=count 时 expire_value 不得超出 max_save_count(0=不限制,超限 403)。
|
// v2 需求 ④:style=count 时 expire_value 不得超出 max_save_count(0=不限制,超限 403)。
|
||||||
func resolveExpire(cfg *config.Config, expireValue int, expireStyle string) (*expireResult, error) {
|
func resolveExpire(cfg *config.Config, expireValue int, expireStyle string) (*expireResult, error) {
|
||||||
allowed := cfg.ExpireStyle()
|
allowed := cfg.ExpireStyle()
|
||||||
okStyle := false
|
okStyle := false
|
||||||
@@ -358,7 +358,7 @@ func formatDurationCN(d time.Duration) string {
|
|||||||
|
|
||||||
// ============ 存储路径 / 容量预留 ============
|
// ============ 存储路径 / 容量预留 ============
|
||||||
|
|
||||||
// storeFor 26.9:按归属引擎取存储实例(空戳/未知名回落当前引擎,兼容历史数据)。
|
// storeFor v3:按归属引擎取存储实例(空戳/未知名回落当前引擎,兼容历史数据)。
|
||||||
func (d *Deps) storeFor(engine string) (storage.Storage, error) {
|
func (d *Deps) storeFor(engine string) (storage.Storage, error) {
|
||||||
if engine == "" || !storage.ValidEngine(engine) {
|
if engine == "" || !storage.ValidEngine(engine) {
|
||||||
return d.Store, nil
|
return d.Store, nil
|
||||||
@@ -656,7 +656,7 @@ func readMultipartHeader(f multipart.File, n int64) []byte {
|
|||||||
// ============ 杂项 ============
|
// ============ 杂项 ============
|
||||||
|
|
||||||
// humanSize 把字节数转成人类可读描述(B/KB/MB/GB 自适应;
|
// humanSize 把字节数转成人类可读描述(B/KB/MB/GB 自适应;
|
||||||
// 26.9 需求④:max_file_size 支持子 MB 上限,固定 MB 格式会显示 0.00 MB)。
|
// v2 需求④:max_file_size 支持子 MB 上限,固定 MB 格式会显示 0.00 MB)。
|
||||||
func humanSize(n int64) string {
|
func humanSize(n int64) string {
|
||||||
const kb, mb, gb = int64(1024), int64(1024 * 1024), int64(1024 * 1024 * 1024)
|
const kb, mb, gb = int64(1024), int64(1024 * 1024), int64(1024 * 1024 * 1024)
|
||||||
switch {
|
switch {
|
||||||
@@ -728,7 +728,7 @@ func bindJSONOrForm(c *gin.Context, obj any) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// 26.9 兼容归一化:旧前端 bundle(fetch 字符串 body 默认 text/plain)发的
|
// v3.1.1 兼容归一化:旧前端 bundle(fetch 字符串 body 默认 text/plain)发的
|
||||||
// 是 text/plain + urlencoded 格式。此类请求改写 Content-Type 后走表单绑定,
|
// 是 text/plain + urlencoded 格式。此类请求改写 Content-Type 后走表单绑定,
|
||||||
// 否则 ShouldBind 对 text/plain 不解析,非空字段全部丢失。
|
// 否则 ShouldBind 对 text/plain 不解析,非空字段全部丢失。
|
||||||
base := ct
|
base := ct
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fileshare/internal/config"
|
"filecodebox/internal/config"
|
||||||
"fileshare/internal/storage"
|
"filecodebox/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// newTestConfig 构造测试配置(defaults 基线,无 KV 覆盖;需求 ⑧ 默认 sqlite,无需真实数据库)。
|
// newTestConfig 构造测试配置(defaults 基线,无 KV 覆盖;需求 ⑧ 默认 sqlite,无需真实数据库)。
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// policy.go — 26.9 上传策略统一读取与校验(需求 ④⑩)。
|
// policy.go — v2 上传策略统一读取与校验(需求 ④⑩)。
|
||||||
//
|
//
|
||||||
// 管理端在后台设置页修改策略(settings KV,t1 schema)后,上传链路
|
// 管理端在后台设置页修改策略(settings KV,t1 schema)后,上传链路
|
||||||
// (share/file、chunk、presign)每次请求实时读取当前策略并动态校验:
|
// (share/file、chunk、presign)每次请求实时读取当前策略并动态校验:
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/audit"
|
"filecodebox/internal/audit"
|
||||||
"fileshare/internal/cache"
|
"filecodebox/internal/cache"
|
||||||
"fileshare/internal/config"
|
"filecodebox/internal/config"
|
||||||
"fileshare/internal/database"
|
"filecodebox/internal/database"
|
||||||
"fileshare/internal/middleware"
|
"filecodebox/internal/middleware"
|
||||||
"fileshare/internal/settings"
|
"filecodebox/internal/settings"
|
||||||
"fileshare/internal/storage"
|
"filecodebox/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ============ 测试环境装配(真实 sqlite + 内存缓存 + 本地存储)============
|
// ============ 测试环境装配(真实 sqlite + 内存缓存 + 本地存储)============
|
||||||
@@ -55,7 +55,7 @@ func newPolicyTestDeps(t *testing.T) *Deps {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("storage.NewLocalStorage: %v", err)
|
t.Fatalf("storage.NewLocalStorage: %v", err)
|
||||||
}
|
}
|
||||||
// 26.9:包装为 Manager(build 直接返回 local 实例,测试无需真实多引擎)
|
// v3:包装为 Manager(build 直接返回 local 实例,测试无需真实多引擎)
|
||||||
storeMgr := storage.NewManager("local", store, func(string) (storage.Storage, error) {
|
storeMgr := storage.NewManager("local", store, func(string) (storage.Storage, error) {
|
||||||
return storage.NewLocalStorage(filepath.Join(dir, "storage"))
|
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 校验可识别)。
|
// pngMagic 最小合法 PNG 头(magic 校验可识别)。
|
||||||
var pngMagic = []byte{0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'}
|
var pngMagic = []byte{0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'}
|
||||||
|
|
||||||
// ============ ① 公开 config:26.9 展示与策略字段下发 ============
|
// ============ ① 公开 config:v2 展示与策略字段下发 ============
|
||||||
|
|
||||||
// TestPublicConfigV2Fields 验证 /api/v1/config 下发背景/页脚/备案/通知与策略范围,
|
// TestPublicConfigV2Fields 验证 /api/v1/config 下发背景/页脚/备案/通知与策略范围,
|
||||||
// 且响应不包含任何敏感键(admin_token/jwt_secret)。
|
// 且响应不包含任何敏感键(admin_token/jwt_secret)。
|
||||||
func TestPublicConfigV2Fields(t *testing.T) {
|
func TestPublicConfigV2Fields(t *testing.T) {
|
||||||
d := newPolicyTestDeps(t)
|
d := newPolicyTestDeps(t)
|
||||||
// 管理端先设置 26.9 展示字段
|
// 管理端先设置 v2 展示字段
|
||||||
if w := patchConfig(d, map[string]any{
|
if w := patchConfig(d, map[string]any{
|
||||||
"background_url": "https://cdn.example.com/bg.png",
|
"background_url": "https://cdn.example.com/bg.png",
|
||||||
"footer_text": "自定义页脚内容",
|
"footer_text": "自定义页脚内容",
|
||||||
@@ -204,9 +204,9 @@ func TestPublicConfigV2Fields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ ② 管理端 get/update:26.9 键全链路 + 类型范围校验 ============
|
// ============ ② 管理端 get/update:v2 键全链路 + 类型范围校验 ============
|
||||||
|
|
||||||
// TestAdminConfigV2RoundTrip 验证 26.9 新键 update → get → public 的往返,
|
// TestAdminConfigV2RoundTrip 验证 v2 新键 update → get → public 的往返,
|
||||||
// 且 admin_token 屏蔽、jwt_secret 不下发。
|
// 且 admin_token 屏蔽、jwt_secret 不下发。
|
||||||
func TestAdminConfigV2RoundTrip(t *testing.T) {
|
func TestAdminConfigV2RoundTrip(t *testing.T) {
|
||||||
d := newPolicyTestDeps(t)
|
d := newPolicyTestDeps(t)
|
||||||
@@ -480,7 +480,7 @@ func TestPolicySnapshotMatchesConfig(t *testing.T) {
|
|||||||
// 编译期保证 fmt 被使用(测试辅助函数中错误路径占位)。
|
// 编译期保证 fmt 被使用(测试辅助函数中错误路径占位)。
|
||||||
var _ = fmt.Sprintf
|
var _ = fmt.Sprintf
|
||||||
|
|
||||||
// ============ 26.9 存储引擎热切换 ============
|
// ============ v3 存储引擎热切换 ============
|
||||||
|
|
||||||
// switchEngine 调用 POST /admin/storage/switch。
|
// switchEngine 调用 POST /admin/storage/switch。
|
||||||
func switchEngine(d *Deps, engine string) *httptest.ResponseRecorder {
|
func switchEngine(d *Deps, engine string) *httptest.ResponseRecorder {
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/middleware"
|
"filecodebox/internal/middleware"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
"fileshare/internal/storage"
|
"filecodebox/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// presignSessionExpires 预签名会话有效期(对齐参考 PRESIGN_SESSION_EXPIRES=900 秒)。
|
// presignSessionExpires 预签名会话有效期(对齐参考 PRESIGN_SESSION_EXPIRES=900 秒)。
|
||||||
@@ -51,7 +51,7 @@ type presignInitRequest struct {
|
|||||||
FileSize int64 `json:"file_size" form:"file_size"`
|
FileSize int64 `json:"file_size" form:"file_size"`
|
||||||
ExpireValue int `json:"expire_value" form:"expire_value"`
|
ExpireValue int `json:"expire_value" form:"expire_value"`
|
||||||
ExpireStyle string `json:"expire_style" form:"expire_style"`
|
ExpireStyle string `json:"expire_style" form:"expire_style"`
|
||||||
Code string `json:"code" form:"code"` // 26.9:自定义提取码(init 时校验,完成时落库)
|
Code string `json:"code" form:"code"` // v3.1:自定义提取码(init 时校验,完成时落库)
|
||||||
}
|
}
|
||||||
|
|
||||||
// presignInit 初始化预签名上传(对齐参考 presign_upload_init):
|
// presignInit 初始化预签名上传(对齐参考 presign_upload_init):
|
||||||
@@ -74,7 +74,7 @@ func (d *Deps) presignInit(c *gin.Context) {
|
|||||||
response.Fail(c, http.StatusBadRequest, "文件名非法")
|
response.Fail(c, http.StatusBadRequest, "文件名非法")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 26.9:自定义提取码提前校验(init 时快速失败;完成请求须再次携带)
|
// v3.1:自定义提取码提前校验(init 时快速失败;完成请求须再次携带)
|
||||||
if err := validatePickupCode(req.Code); err != nil {
|
if err := validatePickupCode(req.Code); err != nil {
|
||||||
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
||||||
respondError(c, err)
|
respondError(c, err)
|
||||||
@@ -86,7 +86,7 @@ func (d *Deps) presignInit(c *gin.Context) {
|
|||||||
respondError(c, err)
|
respondError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 26.9 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
// v2 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
||||||
if err := d.CurrentUploadPolicy().CheckSize(req.FileSize); err != nil {
|
if err := d.CurrentUploadPolicy().CheckSize(req.FileSize); err != nil {
|
||||||
auditUploadEntry(c, "", safeName, req.FileSize, 0)
|
auditUploadEntry(c, "", safeName, req.FileSize, 0)
|
||||||
auditRecordFailed(c, d.AuditSvc, "文件大小超过限制")
|
auditRecordFailed(c, d.AuditSvc, "文件大小超过限制")
|
||||||
@@ -146,7 +146,7 @@ func (d *Deps) presignInit(c *gin.Context) {
|
|||||||
FileSize: req.FileSize,
|
FileSize: req.FileSize,
|
||||||
SavePath: savePath,
|
SavePath: savePath,
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
Engine: d.Store.CurrentName(), // 26.9:会话归属引擎
|
Engine: d.Store.CurrentName(), // v3:会话归属引擎
|
||||||
ExpireValue: req.ExpireValue,
|
ExpireValue: req.ExpireValue,
|
||||||
ExpireStyle: req.ExpireStyle,
|
ExpireStyle: req.ExpireStyle,
|
||||||
ExpiresAt: time.Now().Add(presignSessionExpires * time.Second),
|
ExpiresAt: time.Now().Add(presignSessionExpires * time.Second),
|
||||||
@@ -195,7 +195,7 @@ func (d *Deps) presignProxy(c *gin.Context) {
|
|||||||
respondError(c, err)
|
respondError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 26.9:自定义提取码随代理上传表单携带(init 时已预校验)
|
// v3.1:自定义提取码随代理上传表单携带(init 时已预校验)
|
||||||
if err := validatePickupCode(c.PostForm("code")); err != nil {
|
if err := validatePickupCode(c.PostForm("code")); err != nil {
|
||||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||||
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
||||||
@@ -234,7 +234,7 @@ func (d *Deps) presignProxy(c *gin.Context) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
defer func() { _ = f.Close() }()
|
defer func() { _ = f.Close() }()
|
||||||
if err = validateFileMagic(d.Cfg, session.FileName, fh.Header.Get("Content-Type"), readMultipartHeader(f, 64)); err == nil {
|
if err = validateFileMagic(d.Cfg, session.FileName, fh.Header.Get("Content-Type"), readMultipartHeader(f, 64)); err == nil {
|
||||||
// 26.9:落盘走会话归属引擎
|
// v3:落盘走会话归属引擎
|
||||||
var ps storage.Storage
|
var ps storage.Storage
|
||||||
ps, sErr := d.storeFor(session.Engine)
|
ps, sErr := d.storeFor(session.Engine)
|
||||||
if sErr != nil {
|
if sErr != nil {
|
||||||
@@ -259,7 +259,7 @@ func (d *Deps) presignProxy(c *gin.Context) {
|
|||||||
releaseStorage(ctx, d.DB, "presign:"+uploadID)
|
releaseStorage(ctx, d.DB, "presign:"+uploadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ps, sErr := d.storeFor(session.Engine); sErr == nil {
|
if ps, sErr := d.storeFor(session.Engine); sErr == nil {
|
||||||
_ = ps.DeleteFile(ctx, session.SavePath) // 26.9:清理走归属引擎
|
_ = ps.DeleteFile(ctx, session.SavePath) // v3:清理走归属引擎
|
||||||
}
|
}
|
||||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||||
auditRecordFailed(c, d.AuditSvc, "创建分享失败")
|
auditRecordFailed(c, d.AuditSvc, "创建分享失败")
|
||||||
@@ -299,7 +299,7 @@ func (d *Deps) presignConfirm(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 26.9:直传文件存在性按会话归属引擎检查(直传可能落在旧引擎)
|
// v3:直传文件存在性按会话归属引擎检查(直传可能落在旧引擎)
|
||||||
psCheck, sErr := d.storeFor(session.Engine)
|
psCheck, sErr := d.storeFor(session.Engine)
|
||||||
if sErr != nil {
|
if sErr != nil {
|
||||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||||
@@ -307,7 +307,7 @@ func (d *Deps) presignConfirm(c *gin.Context) {
|
|||||||
respondError(c, mapStorageError(sErr))
|
respondError(c, mapStorageError(sErr))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 26.9:自定义提取码随确认请求携带(query 或 JSON/form body,均可选)
|
// v3.1:自定义提取码随确认请求携带(query 或 JSON/form body,均可选)
|
||||||
customCode := c.Query("code")
|
customCode := c.Query("code")
|
||||||
if customCode == "" && c.Request.Body != nil && c.Request.ContentLength != 0 {
|
if customCode == "" && c.Request.Body != nil && c.Request.ContentLength != 0 {
|
||||||
var fin struct {
|
var fin struct {
|
||||||
@@ -378,7 +378,7 @@ func (d *Deps) presignConfirm(c *gin.Context) {
|
|||||||
releaseStorage(ctx, d.DB, "presign:"+uploadID)
|
releaseStorage(ctx, d.DB, "presign:"+uploadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ps, sErr := d.storeFor(session.Engine); sErr == nil {
|
if ps, sErr := d.storeFor(session.Engine); sErr == nil {
|
||||||
_ = ps.DeleteFile(ctx, session.SavePath) // 26.9:清理走归属引擎
|
_ = ps.DeleteFile(ctx, session.SavePath) // v3:清理走归属引擎
|
||||||
}
|
}
|
||||||
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
auditUploadEntry(c, uploadID, session.FileName, session.FileSize, 0)
|
||||||
auditRecordFailed(c, d.AuditSvc, "创建分享失败")
|
auditRecordFailed(c, d.AuditSvc, "创建分享失败")
|
||||||
@@ -399,7 +399,7 @@ func (d *Deps) createRecordFromSession(c *gin.Context, session *model.PresignUpl
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
// 26.9:完成请求的自定义提取码兜底校验(init 已验,防只发完成请求绕过)
|
// v3.1:完成请求的自定义提取码兜底校验(init 已验,防只发完成请求绕过)
|
||||||
if err := validatePickupCode(customCode); err != nil {
|
if err := validatePickupCode(customCode); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -420,14 +420,10 @@ func (d *Deps) createRecordFromSession(c *gin.Context, session *model.PresignUpl
|
|||||||
ExpiredAt: exp.ExpiredAt,
|
ExpiredAt: exp.ExpiredAt,
|
||||||
ExpiredCount: exp.ExpiredCount,
|
ExpiredCount: exp.ExpiredCount,
|
||||||
UsedCount: exp.UsedCount,
|
UsedCount: exp.UsedCount,
|
||||||
Engine: session.Engine, // 26.9:归属引擎戳
|
Engine: session.Engine, // v3:归属引擎戳
|
||||||
}
|
}
|
||||||
if err := d.DB.WithContext(ctx).Create(&fc).Error; err != nil {
|
if err := d.DB.WithContext(ctx).Create(&fc).Error; err != nil {
|
||||||
return "", mapCodeConflict(err) // 26.9:并发占用自定义码 → 友好 400
|
return "", mapCodeConflict(err) // v3.1:并发占用自定义码 → 友好 400
|
||||||
}
|
|
||||||
// 26.9:SHA512 内容去重(命中则复用旧对象并删除本次副本)
|
|
||||||
if store, sErr := d.storeFor(session.Engine); sErr == nil {
|
|
||||||
d.applyDedup(ctx, store, session.SavePath, &fc)
|
|
||||||
}
|
}
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
@@ -478,7 +474,7 @@ func (d *Deps) presignCancel(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if session.Mode == "direct" {
|
if session.Mode == "direct" {
|
||||||
// 26.9:清理走会话归属引擎
|
// v3:清理走会话归属引擎
|
||||||
if ps, sErr := d.storeFor(session.Engine); sErr == nil {
|
if ps, sErr := d.storeFor(session.Engine); sErr == nil {
|
||||||
if exists, eErr := ps.FileExists(ctx, session.SavePath); eErr == nil && exists {
|
if exists, eErr := ps.FileExists(ctx, session.SavePath); eErr == nil && exists {
|
||||||
_ = ps.DeleteFile(ctx, session.SavePath)
|
_ = ps.DeleteFile(ctx, session.SavePath)
|
||||||
|
|||||||
@@ -1,406 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
// recycle_dedup_test.go — 26.9 回收与下载安全测试:
|
|
||||||
// SHA512 去重(同内容单存储 + 引用计数删除)、过期回收(时间/次数/留存期)、
|
|
||||||
// 防盗链中间件、S3 直链 302 重定向。
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"fileshare/internal/janitor"
|
|
||||||
"fileshare/internal/middleware"
|
|
||||||
"fileshare/internal/model"
|
|
||||||
"fileshare/internal/storage"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ============ 辅助 ============
|
|
||||||
|
|
||||||
// uploadOK 上传文件并断言 200,返回取件码(复用 policy_test 的 uploadFile/respBody)。
|
|
||||||
func uploadOK(t *testing.T, d *Deps, name string, content []byte) string {
|
|
||||||
t.Helper()
|
|
||||||
w := uploadFile(d, name, content, nil)
|
|
||||||
if w.Code != http.StatusOK {
|
|
||||||
t.Fatalf("上传失败: %d %s", w.Code, w.Body.String())
|
|
||||||
}
|
|
||||||
_, data := respBody(t, w)
|
|
||||||
code, _ := data["code"].(string)
|
|
||||||
if code == "" {
|
|
||||||
t.Fatalf("响应缺少 code: %s", w.Body.String())
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
// fileByID 按 code 查记录。
|
|
||||||
func fileByCode(t *testing.T, d *Deps, code string) model.FileCodes {
|
|
||||||
t.Helper()
|
|
||||||
var fc model.FileCodes
|
|
||||||
if err := d.DB.Where("code = ?", code).First(&fc).Error; err != nil {
|
|
||||||
t.Fatalf("查询分享 %s: %v", code, err)
|
|
||||||
}
|
|
||||||
return fc
|
|
||||||
}
|
|
||||||
|
|
||||||
// objectExists 检查本地引擎对象是否存在。
|
|
||||||
func objectExists(t *testing.T, d *Deps, fc model.FileCodes) bool {
|
|
||||||
t.Helper()
|
|
||||||
store, err := d.storeFor(fc.Engine)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
ok, err := store.FileExists(context.Background(), fc.SavePath())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("FileExists: %v", err)
|
|
||||||
}
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============ SHA512 去重 ============
|
|
||||||
|
|
||||||
// TestDedupSameContentSingleObject 同内容上传两次 → 单存储对象 + 记录互引 +
|
|
||||||
// 删除其一对象保留,删除最后一条对象才删除。
|
|
||||||
func TestDedupSameContentSingleObject(t *testing.T) {
|
|
||||||
d := newPolicyTestDeps(t)
|
|
||||||
content := []byte("dedup-me-26.9-同一个内容")
|
|
||||||
|
|
||||||
code1 := uploadOK(t, d, "a.txt", content)
|
|
||||||
code2 := uploadOK(t, d, "b.txt", content)
|
|
||||||
if code1 == code2 {
|
|
||||||
t.Fatal("两次上传应生成不同取件码")
|
|
||||||
}
|
|
||||||
fc1, fc2 := fileByCode(t, d, code1), fileByCode(t, d, code2)
|
|
||||||
|
|
||||||
if fc1.ContentHash == nil || *fc1.ContentHash == "" {
|
|
||||||
t.Fatal("第一条记录未回填 content_hash")
|
|
||||||
}
|
|
||||||
if fc1.ContentHash == nil || fc2.ContentHash == nil || *fc1.ContentHash != *fc2.ContentHash {
|
|
||||||
t.Fatalf("两条记录哈希应一致: %v vs %v", fc1.ContentHash, fc2.ContentHash)
|
|
||||||
}
|
|
||||||
if fc1.UUIDFileName == nil || fc2.UUIDFileName == nil || *fc1.UUIDFileName != *fc2.UUIDFileName {
|
|
||||||
t.Fatalf("去重应复用同一 UUID 文件名: %v vs %v", fc1.UUIDFileName, fc2.UUIDFileName)
|
|
||||||
}
|
|
||||||
if fc1.SavePath() != fc2.SavePath() {
|
|
||||||
t.Fatal("去重应指向同一存储路径")
|
|
||||||
}
|
|
||||||
// 去重后对象应存在
|
|
||||||
if !objectExists(t, d, fc1) {
|
|
||||||
t.Fatal("去重后对象应存在")
|
|
||||||
}
|
|
||||||
// 删除其一:对象保留(另一条仍引用);删除第二条:对象随之删除
|
|
||||||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
|
||||||
c.Request = httptest.NewRequest(http.MethodDelete, "/admin/file/delete", nil)
|
|
||||||
if err := d.deleteFileCode(c, &fc1); err != nil {
|
|
||||||
t.Fatalf("删除第一条: %v", err)
|
|
||||||
}
|
|
||||||
if !objectExists(t, d, fc2) {
|
|
||||||
t.Fatal("仍有引用时对象不应被删除")
|
|
||||||
}
|
|
||||||
if err := d.deleteFileCode(c, &fc2); err != nil {
|
|
||||||
t.Fatalf("删除第二条: %v", err)
|
|
||||||
}
|
|
||||||
store, _ := d.storeFor("local")
|
|
||||||
if ok, _ := store.FileExists(context.Background(), fc2.SavePath()); ok {
|
|
||||||
t.Fatal("最后一个引用删除后对象应被删除")
|
|
||||||
}
|
|
||||||
var cnt int64
|
|
||||||
d.DB.Model(&model.FileCodes{}).Count(&cnt)
|
|
||||||
if cnt != 0 {
|
|
||||||
t.Fatalf("记录应全部删除,剩余 %d", cnt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDedupDisabled 不去重:两记录各自独立对象。
|
|
||||||
func TestDedupDisabled(t *testing.T) {
|
|
||||||
d := newPolicyTestDeps(t)
|
|
||||||
setKV(t, d, "dedup_enabled", "0")
|
|
||||||
content := []byte("no-dedup-content")
|
|
||||||
c1 := uploadOK(t, d, "x.txt", content)
|
|
||||||
c2 := uploadOK(t, d, "y.txt", content)
|
|
||||||
fc1, fc2 := fileByCode(t, d, c1), fileByCode(t, d, c2)
|
|
||||||
if fc1.ContentHash != nil && *fc1.ContentHash != "" {
|
|
||||||
t.Fatal("去重关闭时不应回填 content_hash")
|
|
||||||
}
|
|
||||||
if fc1.SavePath() == fc2.SavePath() {
|
|
||||||
t.Fatal("去重关闭时不应共享路径")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setKV 直写 KV 并应用到内存 Config(对齐生产链路:UpdateKV 落库 + ApplyKV 生效)。
|
|
||||||
func setKV(t *testing.T, d *Deps, key, value string) {
|
|
||||||
t.Helper()
|
|
||||||
if err := d.Mgr.UpdateKV(context.Background(), map[string]any{key: value}); err != nil {
|
|
||||||
t.Fatalf("setKV %s: %v", key, err)
|
|
||||||
}
|
|
||||||
d.Cfg.ApplyKV(map[string]any{key: value})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============ 过期回收 ============
|
|
||||||
|
|
||||||
// makeFileRecord 直插一条文件分享记录(可指定过期形态)。
|
|
||||||
func makeFileRecord(t *testing.T, d *Deps, code string, expiredAt *time.Time, expiredCount int, createdAt time.Time) model.FileCodes {
|
|
||||||
t.Helper()
|
|
||||||
name := "obj-" + code + ".bin"
|
|
||||||
dir := "share/data/test"
|
|
||||||
store, _ := d.storeFor("local")
|
|
||||||
if _, err := store.SaveFile(context.Background(), bytes.NewReader([]byte("recycle-body")), dir+"/"+name); err != nil {
|
|
||||||
t.Fatalf("写入测试对象: %v", err)
|
|
||||||
}
|
|
||||||
fc := model.FileCodes{
|
|
||||||
Code: code, Prefix: "obj-" + code, Suffix: ".bin",
|
|
||||||
UUIDFileName: &name, FilePath: &dir, Size: 12,
|
|
||||||
ExpiredAt: expiredAt, ExpiredCount: expiredCount,
|
|
||||||
Engine: "local",
|
|
||||||
}
|
|
||||||
if err := d.DB.Create(&fc).Error; err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// 校正 created_at(GORM 自动填 now)
|
|
||||||
if err := d.DB.Model(&model.FileCodes{}).Where("id = ?", fc.ID).Update("created_at", createdAt).Error; err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
fc.CreatedAt = createdAt
|
|
||||||
return fc
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRecycleExpiredTimeAndCount 时间过期与次数耗尽都被回收。
|
|
||||||
func TestRecycleExpiredTimeAndCount(t *testing.T) {
|
|
||||||
d := newPolicyTestDeps(t)
|
|
||||||
past := time.Now().Add(-time.Hour)
|
|
||||||
r1 := makeFileRecord(t, d, "RECYA", &past, -1, time.Now().Add(-2*time.Hour)) // 时间过期
|
|
||||||
r2 := makeFileRecord(t, d, "RECYB", &past, 0, time.Now().Add(-2*time.Hour)) // 次数耗尽
|
|
||||||
r3 := makeFileRecord(t, d, "RECYC", nil, 5, time.Now().Add(-2*time.Hour)) // 存活(无过期时间且有余量)
|
|
||||||
|
|
||||||
removed := janitor.RecycleExpired(context.Background(), d.DB, d.Store, &janitor.Recycler{
|
|
||||||
Enabled: func() bool { return true },
|
|
||||||
RetentionDays: func() int64 { return 0 },
|
|
||||||
})
|
|
||||||
if removed != 2 {
|
|
||||||
t.Fatalf("应回收 2 条,实际 %d", removed)
|
|
||||||
}
|
|
||||||
for _, fc := range []model.FileCodes{r1, r2} {
|
|
||||||
var cnt int64
|
|
||||||
d.DB.Model(&model.FileCodes{}).Where("code = ?", fc.Code).Count(&cnt)
|
|
||||||
if cnt != 0 {
|
|
||||||
t.Fatalf("%s 记录应被回收", fc.Code)
|
|
||||||
}
|
|
||||||
if objectExists(t, d, fc) {
|
|
||||||
t.Fatalf("%s 存储对象应被删除", fc.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var cnt int64
|
|
||||||
d.DB.Model(&model.FileCodes{}).Where("code = ?", r3.Code).Count(&cnt)
|
|
||||||
if cnt != 1 {
|
|
||||||
t.Fatal("存活分享不应被回收")
|
|
||||||
}
|
|
||||||
if !objectExists(t, d, r3) {
|
|
||||||
t.Fatal("存活分享对象应保留")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRecycleRetentionDays 留存期:创建超 retention_days 的分享被回收。
|
|
||||||
func TestRecycleRetentionDays(t *testing.T) {
|
|
||||||
d := newPolicyTestDeps(t)
|
|
||||||
fresh := makeFileRecord(t, d, "RETEN1", nil, 5, time.Now()) // 新
|
|
||||||
stale := makeFileRecord(t, d, "RETEN2", nil, 5, time.Now().Add(-48*time.Hour)) // 超 1 天留存
|
|
||||||
removed := janitor.RecycleExpired(context.Background(), d.DB, d.Store, &janitor.Recycler{
|
|
||||||
Enabled: func() bool { return true },
|
|
||||||
RetentionDays: func() int64 { return 1 },
|
|
||||||
})
|
|
||||||
if removed != 1 {
|
|
||||||
t.Fatalf("应回收 1 条,实际 %d", removed)
|
|
||||||
}
|
|
||||||
var cnt int64
|
|
||||||
d.DB.Model(&model.FileCodes{}).Where("code = ?", stale.Code).Count(&cnt)
|
|
||||||
if cnt != 0 {
|
|
||||||
t.Fatal("超留存期分享应被回收")
|
|
||||||
}
|
|
||||||
if !objectExists(t, d, fresh) {
|
|
||||||
t.Fatal("未超留存期的分享对象应保留")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestLazyRecycleOnPickup 取件次数耗尽后再取 → 惰性回收(记录与对象删除)。
|
|
||||||
func TestLazyRecycleOnPickup(t *testing.T) {
|
|
||||||
d := newPolicyTestDeps(t)
|
|
||||||
past := time.Now().Add(time.Hour)
|
|
||||||
fc := makeFileRecord(t, d, "LAZYa", &past, 1, time.Now())
|
|
||||||
if !d.consumeUsage(invokeContext(t), &fc) {
|
|
||||||
// 第一次:count 1→0 成功
|
|
||||||
t.Fatal("首次取件应成功")
|
|
||||||
}
|
|
||||||
if d.consumeUsage(invokeContext(t), &fc) {
|
|
||||||
t.Fatal("次数耗尽后取件应失败")
|
|
||||||
}
|
|
||||||
// 惰性回收是异步的:同步触发一次等价清理验证语义
|
|
||||||
janitor.RecycleRecord(context.Background(), d.DB, d.Store, &fc, &janitor.Recycler{
|
|
||||||
Enabled: func() bool { return true },
|
|
||||||
RetentionDays: func() int64 { return 0 },
|
|
||||||
})
|
|
||||||
var cnt int64
|
|
||||||
d.DB.Model(&model.FileCodes{}).Where("code = ?", fc.Code).Count(&cnt)
|
|
||||||
if cnt != 0 {
|
|
||||||
t.Fatal("惰性回收应删除记录")
|
|
||||||
}
|
|
||||||
if objectExists(t, d, fc) {
|
|
||||||
t.Fatal("惰性回收应删除对象")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// invokeContext 构造带请求的测试 context。
|
|
||||||
func invokeContext(t *testing.T) *gin.Context {
|
|
||||||
t.Helper()
|
|
||||||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
|
||||||
c.Request = httptest.NewRequest(http.MethodGet, "/", nil)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============ 防盗链 ============
|
|
||||||
|
|
||||||
// TestHotlinkMiddleware 防盗链中间件矩阵。
|
|
||||||
func TestHotlinkMiddleware(t *testing.T) {
|
|
||||||
d := newPolicyTestDeps(t)
|
|
||||||
mw := hotlinkProbe(d)
|
|
||||||
req := func(referer, host string) int {
|
|
||||||
r := httptest.NewRequest(http.MethodGet, "/share/download", nil)
|
|
||||||
if referer != "" {
|
|
||||||
r.Header.Set("Referer", referer)
|
|
||||||
}
|
|
||||||
r.Host = host
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
c, _ := gin.CreateTestContext(w)
|
|
||||||
c.Request = r
|
|
||||||
mw(c)
|
|
||||||
if !c.IsAborted() {
|
|
||||||
return http.StatusOK
|
|
||||||
}
|
|
||||||
return w.Code
|
|
||||||
}
|
|
||||||
setKV(t, d, "hotlink_enabled", "0")
|
|
||||||
if got := req("https://evil.com/leech", "mysite.com"); got != http.StatusOK {
|
|
||||||
t.Fatalf("开关关闭时应全放行,got %d", got)
|
|
||||||
}
|
|
||||||
setKV(t, d, "hotlink_enabled", "1")
|
|
||||||
setKV(t, d, "hotlink_whitelist", "")
|
|
||||||
if got := req("https://evil.com/leech", "mysite.com"); got != http.StatusForbidden {
|
|
||||||
t.Fatalf("外站 Referer 应 403,got %d", got)
|
|
||||||
}
|
|
||||||
if got := req("https://mysite.com/page", "mysite.com"); got != http.StatusOK {
|
|
||||||
t.Fatalf("同源 Referer 应放行,got %d", got)
|
|
||||||
}
|
|
||||||
if got := req("", "mysite.com"); got != http.StatusOK {
|
|
||||||
t.Fatalf("空 Referer 应放行,got %d", got)
|
|
||||||
}
|
|
||||||
setKV(t, d, "hotlink_whitelist", "friend.org, *.cdn.net")
|
|
||||||
if got := req("https://friend.org/x", "mysite.com"); got != http.StatusOK {
|
|
||||||
t.Fatalf("白名单精确命中应放行,got %d", got)
|
|
||||||
}
|
|
||||||
if got := req("https://sub.cdn.net/x", "mysite.com"); got != http.StatusOK {
|
|
||||||
t.Fatalf("白名单通配命中应放行,got %d", got)
|
|
||||||
}
|
|
||||||
if got := req("https://other.net/x", "mysite.com"); got != http.StatusForbidden {
|
|
||||||
t.Fatalf("非白名单应 403,got %d", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hotlinkProbe 直接调用中间件构造器。
|
|
||||||
func hotlinkProbe(d *Deps) gin.HandlerFunc {
|
|
||||||
return middleware.HotlinkMiddleware(d.Cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============ 直链下载 ============
|
|
||||||
|
|
||||||
// presignFake 包装本地引擎,仅覆盖 PresignGetURL 返回固定签名 URL。
|
|
||||||
type presignFake struct {
|
|
||||||
storage.Storage
|
|
||||||
gotExpires int64
|
|
||||||
url string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *presignFake) PresignGetURL(_ context.Context, _ string, expires int64) (string, error) {
|
|
||||||
p.gotExpires = expires
|
|
||||||
return p.url, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDirectDownloadRedirect 直链开启 + 引擎支持 → 302 到签名 URL,且
|
|
||||||
// 有效期不超过分享剩余时效;直链关闭 → 走代理 200。
|
|
||||||
func TestDirectDownloadRedirect(t *testing.T) {
|
|
||||||
d := newPolicyTestDeps(t)
|
|
||||||
// 桩包装原 local 引擎:302 不落盘,代理回落时仍能读到真实对象
|
|
||||||
origLocal, err := d.storeFor("local")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
fake := &presignFake{Storage: origLocal, url: "https://s3.example.com/signed?X-Amz-Signature=abc"}
|
|
||||||
swapLocal(t, d, fake)
|
|
||||||
setKV(t, d, "direct_download", "1")
|
|
||||||
setKV(t, d, "direct_link_expire", "900")
|
|
||||||
|
|
||||||
content := []byte("direct-link-body")
|
|
||||||
code := uploadOK(t, d, "d.txt", content)
|
|
||||||
|
|
||||||
// 时间型分享剩余 5 分钟 → 直链有效期应被钳到 300s
|
|
||||||
exp := time.Now().Add(5 * time.Minute)
|
|
||||||
if err := d.DB.Model(&model.FileCodes{}).Where("code = ?", code).
|
|
||||||
Updates(map[string]any{"expired_at": exp, "expired_count": -1}).Error; err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
fc := fileByCode(t, d, code) // 重新取(带上过期时间)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
c, _ := gin.CreateTestContext(w)
|
|
||||||
c.Request = httptest.NewRequest(http.MethodGet, "/share/download", nil)
|
|
||||||
d.serveFile(c, &fc)
|
|
||||||
if w.Code != http.StatusFound {
|
|
||||||
t.Fatalf("应 302 直链,实际 %d %s", w.Code, w.Body.String())
|
|
||||||
}
|
|
||||||
if loc := w.Header().Get("Location"); loc != fake.url {
|
|
||||||
t.Fatalf("Location 应为签名 URL,实际 %q", loc)
|
|
||||||
}
|
|
||||||
if fake.gotExpires > 300 {
|
|
||||||
t.Fatalf("直链有效期应被分享剩余时效钳位(≤300),实际 %d", fake.gotExpires)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭直链 → 回落代理 200
|
|
||||||
setKV(t, d, "direct_download", "0")
|
|
||||||
fc2 := fileByCode(t, d, code)
|
|
||||||
w2 := httptest.NewRecorder()
|
|
||||||
c2, _ := gin.CreateTestContext(w2)
|
|
||||||
c2.Request = httptest.NewRequest(http.MethodGet, "/share/download", nil)
|
|
||||||
d.serveFile(c2, &fc2)
|
|
||||||
if w2.Code != http.StatusOK {
|
|
||||||
t.Fatalf("直链关闭应走代理 200,实际 %d", w2.Code)
|
|
||||||
}
|
|
||||||
if !strings.Contains(w2.Body.String(), "direct-link-body") {
|
|
||||||
t.Fatal("代理响应应包含文件内容")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDirectDownloadLocalFallback 本地引擎不支持直链 → 自动回落代理 200。
|
|
||||||
func TestDirectDownloadLocalFallback(t *testing.T) {
|
|
||||||
d := newPolicyTestDeps(t)
|
|
||||||
setKV(t, d, "direct_download", "1")
|
|
||||||
code := uploadOK(t, d, "f.txt", []byte("local-fallback"))
|
|
||||||
fc := fileByCode(t, d, code)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
c, _ := gin.CreateTestContext(w)
|
|
||||||
c.Request = httptest.NewRequest(http.MethodGet, "/share/download", nil)
|
|
||||||
d.serveFile(c, &fc)
|
|
||||||
if w.Code != http.StatusOK {
|
|
||||||
t.Fatalf("本地引擎应回落代理 200,实际 %d", w.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============ 存储桩 ============
|
|
||||||
|
|
||||||
// swapLocal 用桩替换 local 引擎(重建 Manager,工厂恒返回桩)。
|
|
||||||
func swapLocal(t *testing.T, d *Deps, fake storage.Storage) {
|
|
||||||
t.Helper()
|
|
||||||
factory := func(string) (storage.Storage, error) { return fake, nil }
|
|
||||||
d.Store = storage.NewManager("local", fake, factory)
|
|
||||||
}
|
|
||||||
@@ -6,12 +6,12 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/audit"
|
"filecodebox/internal/audit"
|
||||||
"fileshare/internal/config"
|
"filecodebox/internal/config"
|
||||||
"fileshare/internal/middleware"
|
"filecodebox/internal/middleware"
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
"fileshare/internal/settings"
|
"filecodebox/internal/settings"
|
||||||
"fileshare/internal/storage"
|
"filecodebox/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Deps API 层共享依赖(main.go 装配后注入)。
|
// Deps API 层共享依赖(main.go 装配后注入)。
|
||||||
@@ -21,7 +21,7 @@ type Deps struct {
|
|||||||
Mgr *settings.Manager
|
Mgr *settings.Manager
|
||||||
AuditSvc *audit.Service
|
AuditSvc *audit.Service
|
||||||
Limiter *middleware.RateLimiter
|
Limiter *middleware.RateLimiter
|
||||||
Store *storage.Manager // 26.9:可热切换引擎管理器(实现 Storage 接口)
|
Store *storage.Manager // v3:可热切换引擎管理器(实现 Storage 接口)
|
||||||
Version string
|
Version string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,14 +46,13 @@ func Register(r *gin.Engine, d *Deps) {
|
|||||||
share := r.Group("/share")
|
share := r.Group("/share")
|
||||||
{
|
{
|
||||||
share.POST("/text", d.shareText)
|
share.POST("/text", d.shareText)
|
||||||
share.POST("/file", middleware.UploadBandwidthMiddleware(d.Cfg.UploadRate), d.shareFile)
|
share.POST("/file", d.shareFile)
|
||||||
// metadata:每次访问即计数(RequireRateLimit=进入检查+完成计数)
|
// metadata:每次访问即计数(RequireRateLimit=进入检查+完成计数)
|
||||||
share.GET("/metadata", d.Limiter.RequireRateLimit(middleware.LimitMeta), d.shareMetadata)
|
share.GET("/metadata", d.Limiter.RequireRateLimit(middleware.LimitMeta), d.shareMetadata)
|
||||||
share.POST("/metadata", d.Limiter.RequireRateLimit(middleware.LimitMeta), d.shareMetadataPost)
|
share.POST("/metadata", d.Limiter.RequireRateLimit(middleware.LimitMeta), d.shareMetadataPost)
|
||||||
share.GET("/select", d.shareSelect)
|
share.GET("/select", d.shareSelect)
|
||||||
share.POST("/select", d.shareSelectPost)
|
share.POST("/select", d.shareSelectPost)
|
||||||
// 26.9:下载防盗链(动态开关,空 Referer 放行)
|
share.GET("/download", d.shareDownload)
|
||||||
share.GET("/download", middleware.HotlinkMiddleware(d.Cfg), d.shareDownload)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// —— 分片上传 ——
|
// —— 分片上传 ——
|
||||||
@@ -62,8 +61,8 @@ func Register(r *gin.Engine, d *Deps) {
|
|||||||
chunk.POST("/upload/init", d.chunkInit)
|
chunk.POST("/upload/init", d.chunkInit)
|
||||||
// 主路径(参考语义):/chunk/upload/{uploadID}/{index};
|
// 主路径(参考语义):/chunk/upload/{uploadID}/{index};
|
||||||
// 扁平兼容:/chunk/upload + 表单/query 传 upload_id/chunk_index
|
// 扁平兼容:/chunk/upload + 表单/query 传 upload_id/chunk_index
|
||||||
chunk.POST("/upload/:uploadID/:chunkIndex", middleware.UploadBandwidthMiddleware(d.Cfg.UploadRate), d.chunkUpload)
|
chunk.POST("/upload/:uploadID/:chunkIndex", d.chunkUpload)
|
||||||
chunk.POST("/upload", middleware.UploadBandwidthMiddleware(d.Cfg.UploadRate), d.chunkUploadFlat)
|
chunk.POST("/upload", d.chunkUploadFlat)
|
||||||
chunk.GET("/upload/status/:uploadID", d.chunkStatus)
|
chunk.GET("/upload/status/:uploadID", d.chunkStatus)
|
||||||
chunk.POST("/upload/complete/:uploadID", d.chunkComplete)
|
chunk.POST("/upload/complete/:uploadID", d.chunkComplete)
|
||||||
chunk.DELETE("/upload/:uploadID", d.chunkCancel)
|
chunk.DELETE("/upload/:uploadID", d.chunkCancel)
|
||||||
@@ -73,7 +72,7 @@ func Register(r *gin.Engine, d *Deps) {
|
|||||||
presign := r.Group("/presign")
|
presign := r.Group("/presign")
|
||||||
{
|
{
|
||||||
presign.POST("/upload/init", d.presignInit)
|
presign.POST("/upload/init", d.presignInit)
|
||||||
presign.PUT("/upload/proxy/:uploadID", middleware.UploadBandwidthMiddleware(d.Cfg.UploadRate), d.presignProxy)
|
presign.PUT("/upload/proxy/:uploadID", d.presignProxy)
|
||||||
presign.POST("/upload/confirm/:uploadID", d.presignConfirm)
|
presign.POST("/upload/confirm/:uploadID", d.presignConfirm)
|
||||||
presign.GET("/upload/status/:uploadID", d.presignStatus)
|
presign.GET("/upload/status/:uploadID", d.presignStatus)
|
||||||
presign.DELETE("/upload/:uploadID", d.presignCancel)
|
presign.DELETE("/upload/:uploadID", d.presignCancel)
|
||||||
@@ -105,7 +104,7 @@ func (d *Deps) robotsText(c *gin.Context) {
|
|||||||
c.Data(http.StatusOK, "text/plain; charset=utf-8", []byte(d.Cfg.GetString("robotsText")))
|
c.Data(http.StatusOK, "text/plain; charset=utf-8", []byte(d.Cfg.GetString("robotsText")))
|
||||||
}
|
}
|
||||||
|
|
||||||
// publicConfig 公共配置(前端首页/上传页所需;26.9 需求 ①②③④⑩ 扩展):
|
// publicConfig 公共配置(前端首页/上传页所需;v2 需求 ①②③④⑩ 扩展):
|
||||||
// - 展示字段:站点信息、Logo/favicon、背景图、页脚文案/备案号、通知;
|
// - 展示字段:站点信息、Logo/favicon、背景图、页脚文案/备案号、通知;
|
||||||
// - 策略范围(上传页动态渲染):大小上限、类型白名单、过期方式、保存
|
// - 策略范围(上传页动态渲染):大小上限、类型白名单、过期方式、保存
|
||||||
// 时间/次数上限、上传频率(仅范围,不含内部实现键)。
|
// 时间/次数上限、上传频率(仅范围,不含内部实现键)。
|
||||||
@@ -133,12 +132,9 @@ func (d *Deps) publicConfig(c *gin.Context) {
|
|||||||
// 需求 ②:页脚自定义内容与备案号
|
// 需求 ②:页脚自定义内容与备案号
|
||||||
"footer_text": cfg.FooterText(),
|
"footer_text": cfg.FooterText(),
|
||||||
"footer_beian": cfg.FooterBeian(),
|
"footer_beian": cfg.FooterBeian(),
|
||||||
// 26.9:当前存储引擎名(仅名称,任何引擎参数/凭据不下发)
|
// v3:当前存储引擎名(仅名称,任何引擎参数/凭据不下发)
|
||||||
"storage_engine": d.Store.CurrentName(),
|
"storage_engine": d.Store.CurrentName(),
|
||||||
"site_domain": d.Cfg.SiteDomain(),
|
"site_domain": d.Cfg.SiteDomain(),
|
||||||
// 26.9:上下行带宽字节/秒(公开下发,0=不限速,方便管理端展示当前值)
|
|
||||||
"upload_rate": d.Cfg.UploadRate(),
|
|
||||||
"download_rate": d.Cfg.DownloadRate(),
|
|
||||||
// 需求 ③:系统通知(开关 + 内容,前台右上角悬浮窗)
|
// 需求 ③:系统通知(开关 + 内容,前台右上角悬浮窗)
|
||||||
// L7:读取侧再做一次白名单净化,覆盖历史存量与直改库的数据
|
// L7:读取侧再做一次白名单净化,覆盖历史存量与直改库的数据
|
||||||
"notify_enabled": boolToInt(cfg.NotifyEnabled()),
|
"notify_enabled": boolToInt(cfg.NotifyEnabled()),
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
"fileshare/internal/settings"
|
"filecodebox/internal/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// postJSON 以 JSON body 调用 POST 端点。
|
// postJSON 以 JSON body 调用 POST 端点。
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
"fileshare/internal/settings"
|
"filecodebox/internal/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fileSizeUnits 文件大小单位(对齐参考 FILE_SIZE_UNITS)。
|
// fileSizeUnits 文件大小单位(对齐参考 FILE_SIZE_UNITS)。
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -13,12 +12,11 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/audit"
|
"filecodebox/internal/audit"
|
||||||
"fileshare/internal/janitor"
|
"filecodebox/internal/middleware"
|
||||||
"fileshare/internal/middleware"
|
"filecodebox/internal/model"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/response"
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/storage"
|
||||||
"fileshare/internal/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// nowRFC3339 当前时间的 RFC3339 表示。
|
// nowRFC3339 当前时间的 RFC3339 表示。
|
||||||
@@ -63,22 +61,9 @@ func (d *Deps) consumeUsage(c *gin.Context, fc *model.FileCodes) bool {
|
|||||||
"expired_count": gorm.Expr("CASE WHEN expired_count > 0 THEN expired_count - 1 ELSE expired_count END"),
|
"expired_count": gorm.Expr("CASE WHEN expired_count > 0 THEN expired_count - 1 ELSE expired_count END"),
|
||||||
"used_count": gorm.Expr("used_count + 1"),
|
"used_count": gorm.Expr("used_count + 1"),
|
||||||
})
|
})
|
||||||
if res.Error == nil && res.RowsAffected == 0 {
|
|
||||||
// 26.9:取件时惰性回收——记录已过期/次数耗尽,后台异步删除记录与对象
|
|
||||||
// (定时回收循环之外的"更好检查方法":访问即发现即回收,不等下一轮扫描)
|
|
||||||
d.recycleAsync(fc)
|
|
||||||
}
|
|
||||||
return res.Error == nil && res.RowsAffected > 0
|
return res.Error == nil && res.RowsAffected > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// recycleAsync 异步回收单条过期分享(不阻塞请求;记录不存在时为幂等空操作)。
|
|
||||||
func (d *Deps) recycleAsync(fc *model.FileCodes) {
|
|
||||||
go janitor.RecycleRecord(context.Background(), d.DB, d.Store, fc, &janitor.Recycler{
|
|
||||||
Enabled: d.Cfg.RecycleEnabled,
|
|
||||||
RetentionDays: d.Cfg.RetentionDays,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// fileSavePath 拼接分享记录的存储相对路径(file_path/uuid_file_name)。
|
// fileSavePath 拼接分享记录的存储相对路径(file_path/uuid_file_name)。
|
||||||
func fileSavePath(fc *model.FileCodes) string {
|
func fileSavePath(fc *model.FileCodes) string {
|
||||||
dir := ""
|
dir := ""
|
||||||
@@ -105,7 +90,7 @@ func (d *Deps) shareText(c *gin.Context) {
|
|||||||
if !requireUploadLimit(c, d.Limiter) {
|
if !requireUploadLimit(c, d.Limiter) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 26.9 修复:JSON/表单/ultipart 统一绑定(form+json 双标签——此前仅 PostForm 时,
|
// v3.1 修复:JSON/表单/ultipart 统一绑定(form+json 双标签——此前仅 PostForm 时,
|
||||||
// JSON 提交会静默存成空文本并 200,取件页空白)。
|
// JSON 提交会静默存成空文本并 200,取件页空白)。
|
||||||
var body struct {
|
var body struct {
|
||||||
Text string `json:"text" form:"text"`
|
Text string `json:"text" form:"text"`
|
||||||
@@ -135,7 +120,7 @@ func (d *Deps) shareText(c *gin.Context) {
|
|||||||
if expireStyle == "" {
|
if expireStyle == "" {
|
||||||
expireStyle = "day"
|
expireStyle = "day"
|
||||||
}
|
}
|
||||||
// 26.9:自定义提取码格式校验(4-8 位字母数字,空=随机)
|
// v3.1:自定义提取码格式校验(4-8 位字母数字,空=随机)
|
||||||
if err := validatePickupCode(body.Code); err != nil {
|
if err := validatePickupCode(body.Code); err != nil {
|
||||||
respondError(c, err)
|
respondError(c, err)
|
||||||
return
|
return
|
||||||
@@ -168,11 +153,11 @@ func (d *Deps) shareText(c *gin.Context) {
|
|||||||
ExpiredAt: exp.ExpiredAt,
|
ExpiredAt: exp.ExpiredAt,
|
||||||
ExpiredCount: exp.ExpiredCount,
|
ExpiredCount: exp.ExpiredCount,
|
||||||
UsedCount: exp.UsedCount,
|
UsedCount: exp.UsedCount,
|
||||||
Engine: d.Store.CurrentName(), // 26.9:归属引擎戳(文本也记录,保持一致性)
|
Engine: d.Store.CurrentName(), // v3:归属引擎戳(文本也记录,保持一致性)
|
||||||
}
|
}
|
||||||
err = d.DB.WithContext(ctx).Create(&fc).Error
|
err = d.DB.WithContext(ctx).Create(&fc).Error
|
||||||
}
|
}
|
||||||
err = mapCodeConflict(err) // 26.9:自定义码唯一索引冲突 → 友好 400
|
err = mapCodeConflict(err) // v3.1:自定义码唯一索引冲突 → 友好 400
|
||||||
releaseStorage(ctx, d.DB, token)
|
releaseStorage(ctx, d.DB, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
auditRecordFailed(c, d.AuditSvc, "文本分享创建失败")
|
auditRecordFailed(c, d.AuditSvc, "文本分享创建失败")
|
||||||
@@ -203,7 +188,7 @@ func (d *Deps) shareFile(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
origName := fh.Filename
|
origName := fh.Filename
|
||||||
// 26.9 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
// v2 需求 ④⑩:动态策略校验(max_file_size,0=回落 uploadSize)
|
||||||
if err := d.CurrentUploadPolicy().CheckSize(fh.Size); err != nil {
|
if err := d.CurrentUploadPolicy().CheckSize(fh.Size); err != nil {
|
||||||
auditUploadEntry(c, "", origName, fh.Size, 0)
|
auditUploadEntry(c, "", origName, fh.Size, 0)
|
||||||
auditRecordFailed(c, d.AuditSvc, "大小超过限制")
|
auditRecordFailed(c, d.AuditSvc, "大小超过限制")
|
||||||
@@ -220,7 +205,7 @@ func (d *Deps) shareFile(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 26.9:自定义提取码(落盘前校验,失败快速返回不占容量预留)
|
// v3.1:自定义提取码(落盘前校验,失败快速返回不占容量预留)
|
||||||
if err := validatePickupCode(c.PostForm("code")); err != nil {
|
if err := validatePickupCode(c.PostForm("code")); err != nil {
|
||||||
auditUploadEntry(c, "", origName, fh.Size, 0)
|
auditUploadEntry(c, "", origName, fh.Size, 0)
|
||||||
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
auditRecordFailed(c, d.AuditSvc, "提取码非法")
|
||||||
@@ -272,15 +257,12 @@ func (d *Deps) shareFile(c *gin.Context) {
|
|||||||
ExpiredAt: exp.ExpiredAt,
|
ExpiredAt: exp.ExpiredAt,
|
||||||
ExpiredCount: exp.ExpiredCount,
|
ExpiredCount: exp.ExpiredCount,
|
||||||
UsedCount: exp.UsedCount,
|
UsedCount: exp.UsedCount,
|
||||||
Engine: d.Store.CurrentName(), // 26.9:归属引擎戳(下载按此取回)
|
Engine: d.Store.CurrentName(), // v3:归属引擎戳(下载按此取回)
|
||||||
}
|
}
|
||||||
if err = d.DB.WithContext(ctx).Create(&fc).Error; err != nil {
|
if err = d.DB.WithContext(ctx).Create(&fc).Error; err != nil {
|
||||||
err = mapCodeConflict(err) // 26.9
|
err = mapCodeConflict(err) // v3.1
|
||||||
// 记录创建失败:清理已落盘文件
|
// 记录创建失败:清理已落盘文件
|
||||||
_ = d.Store.DeleteFile(ctx, savePath)
|
_ = d.Store.DeleteFile(ctx, savePath)
|
||||||
} else {
|
|
||||||
// 26.9:SHA512 内容去重(命中则复用旧对象并删除本次副本)
|
|
||||||
d.applyDedup(ctx, d.Store, savePath, &fc)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 保存失败:尽力清理半写文件
|
// 保存失败:尽力清理半写文件
|
||||||
@@ -291,7 +273,7 @@ func (d *Deps) shareFile(c *gin.Context) {
|
|||||||
auditUploadEntry(c, "", origName, fh.Size, 0)
|
auditUploadEntry(c, "", origName, fh.Size, 0)
|
||||||
auditRecordFailed(c, d.AuditSvc, "文件保存失败")
|
auditRecordFailed(c, d.AuditSvc, "文件保存失败")
|
||||||
if be, ok := err.(*apiError); ok && be.Status == http.StatusBadRequest {
|
if be, ok := err.(*apiError); ok && be.Status == http.StatusBadRequest {
|
||||||
respondError(c, err) // 26.9:提取码冲突等业务 400 原样透出
|
respondError(c, err) // v3.1:提取码冲突等业务 400 原样透出
|
||||||
} else {
|
} else {
|
||||||
respondError(c, mapStorageError(err))
|
respondError(c, mapStorageError(err))
|
||||||
}
|
}
|
||||||
@@ -562,7 +544,7 @@ func (d *Deps) serveFile(c *gin.Context, fc *model.FileCodes) {
|
|||||||
savePath := fileSavePath(fc)
|
savePath := fileSavePath(fc)
|
||||||
name := fc.Prefix + fc.Suffix
|
name := fc.Prefix + fc.Suffix
|
||||||
|
|
||||||
// 26.9:按文件归属引擎取回(切换引擎后旧文件仍可下载);空戳=历史数据回落当前引擎
|
// v3:按文件归属引擎取回(切换引擎后旧文件仍可下载);空戳=历史数据回落当前引擎
|
||||||
store, err := d.storeFor(fc.Engine)
|
store, err := d.storeFor(fc.Engine)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
auditUploadEntry(c, fc.Code, name, fc.Size, 0)
|
auditUploadEntry(c, fc.Code, name, fc.Size, 0)
|
||||||
@@ -571,29 +553,6 @@ func (d *Deps) serveFile(c *gin.Context, fc *model.FileCodes) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 26.9:对象存储直链下载——引擎支持 + 直链开关开启时 302 到限时预签名 URL,
|
|
||||||
// 文件字节不再经过本服务器(带宽成本转嫁对象存储)。签名有效期取
|
|
||||||
// direct_link_expire 与分享剩余时效的较小值;直链不可用静默回落代理。
|
|
||||||
if d.Cfg.DirectDownload() {
|
|
||||||
expires := d.Cfg.DirectLinkExpire()
|
|
||||||
if fc.ExpiredAt != nil {
|
|
||||||
if remain := int64(time.Until(*fc.ExpiredAt).Seconds()); remain > 0 && remain < expires {
|
|
||||||
expires = remain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if url, err := store.PresignGetURL(ctx, savePath, expires); err == nil && url != "" {
|
|
||||||
auditUploadEntry(c, fc.Code, name, fc.Size, fc.Size)
|
|
||||||
middleware.AuditSet(c, func(e *audit.Entry) {
|
|
||||||
e.TransferredBytes = fc.Size
|
|
||||||
e.SizeBytes = fc.Size
|
|
||||||
})
|
|
||||||
auditRecordSuccess(c, d.AuditSvc)
|
|
||||||
c.Redirect(http.StatusFound, url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 直链不可用:继续走代理下载(不中断取件)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 先 Stat 拿总大小(用于审计与 Range 后缀解析)
|
// 先 Stat 拿总大小(用于审计与 Range 后缀解析)
|
||||||
var total int64 = -1
|
var total int64 = -1
|
||||||
if meta, err := store.Stat(ctx, savePath); err == nil && meta != nil {
|
if meta, err := store.Stat(ctx, savePath); err == nil && meta != nil {
|
||||||
@@ -638,9 +597,7 @@ func (d *Deps) serveFile(c *gin.Context, fc *model.FileCodes) {
|
|||||||
}
|
}
|
||||||
auditUploadEntry(c, fc.Code, name, total, 0)
|
auditUploadEntry(c, fc.Code, name, total, 0)
|
||||||
c.Status(status)
|
c.Status(status)
|
||||||
// 26.9:下载带宽限速(storage.ReadCloser → 限速 reader → c.Writer)
|
n, _ := io.Copy(c.Writer, dl)
|
||||||
dlReader := middleware.WrapReadCloser(dl, d.Cfg.DownloadRate())
|
|
||||||
n, _ := io.Copy(c.Writer, dlReader)
|
|
||||||
middleware.AuditSet(c, func(e *audit.Entry) {
|
middleware.AuditSet(c, func(e *audit.Entry) {
|
||||||
if e.TransferredBytes == 0 {
|
if e.TransferredBytes == 0 {
|
||||||
e.TransferredBytes = n
|
e.TransferredBytes = n
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
// 26.9:自定义提取码与站点域名单元测试。
|
// v3.1:自定义提取码与站点域名单元测试。
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// postForm 以 urlencoded 表单调用 handler(26.9 测试辅助)。
|
// postForm 以 urlencoded 表单调用 handler(v3.1 测试辅助)。
|
||||||
func postForm(d *Deps, path string, fields map[string]string) *httptest.ResponseRecorder {
|
func postForm(d *Deps, path string, fields map[string]string) *httptest.ResponseRecorder {
|
||||||
form := url.Values{}
|
form := url.Values{}
|
||||||
for k, v := range fields {
|
for k, v := range fields {
|
||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
web "fileshare/web"
|
web "filecodebox/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// registerWeb 注册前端静态资源与 SPA 回退(必须最后注册):
|
// registerWeb 注册前端静态资源与 SPA 回退(必须最后注册):
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service 审计日志服务。
|
// Service 审计日志服务。
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const (
|
|||||||
AdminSessionExpireMax = 365 * 24 * 60 * 60 // 最大 365 天
|
AdminSessionExpireMax = 365 * 24 * 60 * 60 // 最大 365 天
|
||||||
|
|
||||||
// DefaultSQLitePath SQLite 模式默认数据库文件路径(相对运行目录,自动创建 data/)。
|
// DefaultSQLitePath SQLite 模式默认数据库文件路径(相对运行目录,自动创建 data/)。
|
||||||
DefaultSQLitePath = "./data/fileshare.db"
|
DefaultSQLitePath = "./data/filecodebox.db"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 数据库驱动常量(需求 ⑧:SQLite 默认、Postgres 可选)。
|
// 数据库驱动常量(需求 ⑧:SQLite 默认、Postgres 可选)。
|
||||||
@@ -28,7 +28,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// DefaultLogoURL / DefaultFaviconURL 默认 Logo 与 favicon(需求 ⑤):
|
// DefaultLogoURL / DefaultFaviconURL 默认 Logo 与 favicon(需求 ⑤):
|
||||||
// 26.9 起默认改用前端打包的本地资源(web/src/assets/brand/logo.svg + favicon.png,
|
// v2 起默认改用前端打包的本地资源(web/src/assets/brand/logo.svg + favicon.png,
|
||||||
// 经 Vite 产出 /assets/logo-*.svg 与 /assets/favicon-*.png)。此处留空,
|
// 经 Vite 产出 /assets/logo-*.svg 与 /assets/favicon-*.png)。此处留空,
|
||||||
// GET /api/v1/config 下发空值时前端 displayLogoUrl/displayFaviconUrl 回落到本地打包资源;
|
// GET /api/v1/config 下发空值时前端 displayLogoUrl/displayFaviconUrl 回落到本地打包资源;
|
||||||
// 管理端仍可设置任意 URL 全站替换。
|
// 管理端仍可设置任意 URL 全站替换。
|
||||||
@@ -62,20 +62,9 @@ func defaults() map[string]any {
|
|||||||
"file_storage": "local",
|
"file_storage": "local",
|
||||||
"storage_path": "",
|
"storage_path": "",
|
||||||
"storageLimit": 0,
|
"storageLimit": 0,
|
||||||
// 26.9:存储引擎运行时可配(热切换);空=沿用 Env.StorageEngine 启动值
|
// v3:存储引擎运行时可配(热切换);空=沿用 Env.StorageEngine 启动值
|
||||||
"storage_engine": "",
|
"storage_engine": "",
|
||||||
"site_domain": "",
|
"site_domain": "",
|
||||||
"upload_rate": "0",
|
|
||||||
"download_rate": "0",
|
|
||||||
// 26.9 回收与下载安全
|
|
||||||
"recycle_enabled": 1,
|
|
||||||
"recycle_interval": 1800,
|
|
||||||
"retention_days": 0,
|
|
||||||
"dedup_enabled": 1,
|
|
||||||
"hotlink_enabled": 0,
|
|
||||||
"hotlink_whitelist": "",
|
|
||||||
"direct_download": 1,
|
|
||||||
"direct_link_expire": 900,
|
|
||||||
// 站点信息
|
// 站点信息
|
||||||
"name": "文件快传",
|
"name": "文件快传",
|
||||||
"site_name": "文件快传", // 新增:管理端可自定义
|
"site_name": "文件快传", // 新增:管理端可自定义
|
||||||
@@ -87,7 +76,7 @@ func defaults() map[string]any {
|
|||||||
// 需求 ⑤:默认 Logo 与 favicon(空 = 前端使用打包的本地资源)
|
// 需求 ⑤:默认 Logo 与 favicon(空 = 前端使用打包的本地资源)
|
||||||
"logo_url": DefaultLogoURL,
|
"logo_url": DefaultLogoURL,
|
||||||
"favicon_url": DefaultFaviconURL,
|
"favicon_url": DefaultFaviconURL,
|
||||||
// 需求 ①:背景图(26.9 新增 background_url;background 为参考实现既有键,保留兼容)
|
// 需求 ①:背景图(v2 新增 background_url;background 为参考实现既有键,保留兼容)
|
||||||
"background": "",
|
"background": "",
|
||||||
"background_url": "",
|
"background_url": "",
|
||||||
// 需求 ②:页脚自定义内容与备案号
|
// 需求 ②:页脚自定义内容与备案号
|
||||||
@@ -289,89 +278,7 @@ func (c *Config) GetBool(key string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetStringSlice 取字符串切片配置。
|
// GetStringSlice 取字符串切片配置。
|
||||||
// UploadRate 上传带宽字节/秒(26.9,0=不限速)。
|
// SiteDomain 站点对外域名(v3.1):空=分享链接用当前访问地址。
|
||||||
func (c *Config) UploadRate() int {
|
|
||||||
v := c.GetInt(KeyUploadRate)
|
|
||||||
if v < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// DownloadRate 下载带宽字节/秒(26.9,0=不限速)。
|
|
||||||
func (c *Config) DownloadRate() int {
|
|
||||||
v := c.GetInt(KeyDownloadRate)
|
|
||||||
if v < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// —— 26.9 回收与下载安全 ——
|
|
||||||
|
|
||||||
// RecycleEnabled 过期自动回收开关。
|
|
||||||
func (c *Config) RecycleEnabled() bool { return c.GetInt(KeyRecycleEnabled) == 1 }
|
|
||||||
|
|
||||||
// RecycleInterval 回收扫描间隔(秒,钳位 60~86400)。
|
|
||||||
func (c *Config) RecycleInterval() int64 {
|
|
||||||
v := c.GetInt64(KeyRecycleInterval)
|
|
||||||
if v < RecycleIntervalMin {
|
|
||||||
return RecycleIntervalMin
|
|
||||||
}
|
|
||||||
if v > RecycleIntervalMax {
|
|
||||||
return RecycleIntervalMax
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetentionDays 全局最长存储时长(天,0=不限制)。
|
|
||||||
func (c *Config) RetentionDays() int64 {
|
|
||||||
v := c.GetInt64(KeyRetentionDays)
|
|
||||||
if v < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// DedupEnabled SHA512 内容去重开关。
|
|
||||||
func (c *Config) DedupEnabled() bool { return c.GetInt(KeyDedupEnabled) == 1 }
|
|
||||||
|
|
||||||
// HotlinkEnabled 下载防盗链开关。
|
|
||||||
func (c *Config) HotlinkEnabled() bool { return c.GetInt(KeyHotlinkEnabled) == 1 }
|
|
||||||
|
|
||||||
// HotlinkWhitelist 防盗链 Referer 白名单(逗号分隔域名,返回小写去空白切片)。
|
|
||||||
func (c *Config) HotlinkWhitelist() []string {
|
|
||||||
raw := c.GetString(KeyHotlinkWhitelist)
|
|
||||||
if strings.TrimSpace(raw) == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
parts := strings.Split(raw, ",")
|
|
||||||
out := make([]string, 0, len(parts))
|
|
||||||
for _, p := range parts {
|
|
||||||
p = strings.ToLower(strings.TrimSpace(p))
|
|
||||||
if p != "" {
|
|
||||||
out = append(out, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectDownload 对象存储直链下载开关。
|
|
||||||
func (c *Config) DirectDownload() bool { return c.GetInt(KeyDirectDownload) == 1 }
|
|
||||||
|
|
||||||
// DirectLinkExpire 直链有效期(秒,钳位 60~3600)。
|
|
||||||
func (c *Config) DirectLinkExpire() int64 {
|
|
||||||
v := c.GetInt64(KeyDirectLinkExpire)
|
|
||||||
if v < DirectLinkExpireMin {
|
|
||||||
return DirectLinkExpireMin
|
|
||||||
}
|
|
||||||
if v > DirectLinkExpireMax {
|
|
||||||
return DirectLinkExpireMax
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiteDomain 站点对外域名(26.9):空=分享链接用当前访问地址。
|
|
||||||
func (c *Config) SiteDomain() string {
|
func (c *Config) SiteDomain() string {
|
||||||
return strings.TrimRight(strings.TrimSpace(c.GetString("site_domain")), "/")
|
return strings.TrimRight(strings.TrimSpace(c.GetString("site_domain")), "/")
|
||||||
}
|
}
|
||||||
@@ -494,7 +401,7 @@ func (c *Config) AdminSessionExpireSeconds() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Engine 当前存储引擎。
|
// Engine 当前存储引擎。
|
||||||
// Engine 返回当前存储引擎名:KV storage_engine 优先(26.9 运行时可改),
|
// Engine 返回当前存储引擎名:KV storage_engine 优先(v3 运行时可改),
|
||||||
// 空(未设置/历史数据)回落启动值 Env.StorageEngine(env 校验过的 local|s3|webdav)。
|
// 空(未设置/历史数据)回落启动值 Env.StorageEngine(env 校验过的 local|s3|webdav)。
|
||||||
// 枚举校验内联(避免 config→storage 反向依赖)。
|
// 枚举校验内联(避免 config→storage 反向依赖)。
|
||||||
func (c *Config) Engine() string {
|
func (c *Config) Engine() string {
|
||||||
|
|||||||
@@ -1,49 +1,38 @@
|
|||||||
// Package config — schema.go 定义 26.9 新增配置键(KV)schema:
|
// Package config — schema.go 定义 v2 新增配置键(KV)schema:
|
||||||
// 键名常量、类型、默认值与取值边界。管理与 API 层(t2)按下表读写与校验,
|
// 键名常量、类型、默认值与取值边界。管理与 API 层(t2)按下表读写与校验,
|
||||||
// 文档(t4)按本表生成说明。键名除参考实现既有 camelCase 键外,
|
// 文档(t4)按本表生成说明。键名除参考实现既有 camelCase 键外,
|
||||||
// 26.9 新增键统一 snake_case。
|
// v2 新增键统一 snake_case。
|
||||||
package config
|
package config
|
||||||
|
|
||||||
// —— 26.9 新增/沿用键名常量(单一事实来源;settings 包会 re-export)——
|
// —— v2 新增/沿用键名常量(单一事实来源;settings 包会 re-export)——
|
||||||
// 命名规则:26.9 新增键 snake_case;与参考实现对齐的既有键保持原拼写。
|
// 命名规则:v2 新增键 snake_case;与参考实现对齐的既有键保持原拼写。
|
||||||
const (
|
const (
|
||||||
// 需求 ①:背景图
|
// 需求 ①:背景图
|
||||||
KeyBackground = "background" // 参考实现既有键(v1 兼容保留)
|
KeyBackground = "background" // 参考实现既有键(v1 兼容保留)
|
||||||
KeyBackgroundURL = "background_url" // 26.9 新增:背景图 URL 或上传后的访问地址(空=默认主题)
|
KeyBackgroundURL = "background_url" // v2 新增:背景图 URL 或上传后的访问地址(空=默认主题)
|
||||||
// 需求 ②:页脚
|
// 需求 ②:页脚
|
||||||
KeyFooterText = "footer_text" // 26.9 新增:页脚自定义内容(纯文本或受控 HTML 片段)
|
KeyFooterText = "footer_text" // v2 新增:页脚自定义内容(纯文本或受控 HTML 片段)
|
||||||
KeyFooterBeian = "footer_beian" // 26.9 新增:备案号(如 京ICP备2024xxxxxx号-1)
|
KeyFooterBeian = "footer_beian" // v2 新增:备案号(如 京ICP备2024xxxxxx号-1)
|
||||||
// 需求 ③:系统通知
|
// 需求 ③:系统通知
|
||||||
KeyNotifyEnabled = "notify_enabled" // 26.9 新增:通知开关,1 开启 / 0 关闭
|
KeyNotifyEnabled = "notify_enabled" // v2 新增:通知开关,1 开启 / 0 关闭
|
||||||
KeyNotifyTitle = "notify_title" // 既有键:通知标题
|
KeyNotifyTitle = "notify_title" // 既有键:通知标题
|
||||||
KeyNotifyContent = "notify_content" // 既有键:通知内容(允许 <a> 等受控 HTML)
|
KeyNotifyContent = "notify_content" // 既有键:通知内容(允许 <a> 等受控 HTML)
|
||||||
// 需求 ④:保存策略(上传页动态读取并在范围内选择)
|
// 需求 ④:保存策略(上传页动态读取并在范围内选择)
|
||||||
KeyMaxSaveSeconds = "max_save_seconds" // 既有键:最长保存秒数,0=不限制(仅受默认 7 天兜底)
|
KeyMaxSaveSeconds = "max_save_seconds" // 既有键:最长保存秒数,0=不限制(仅受默认 7 天兜底)
|
||||||
KeyMaxSaveCount = "max_save_count" // 26.9 新增:单次分享最大可取(保存)次数上限,0=不限制
|
KeyMaxSaveCount = "max_save_count" // v2 新增:单次分享最大可取(保存)次数上限,0=不限制
|
||||||
KeyExpireStyle = "expireStyle" // 既有键:允许的过期方式白名单
|
KeyExpireStyle = "expireStyle" // 既有键:允许的过期方式白名单
|
||||||
// 需求 ④:上传频率限制(既有键,对齐参考 ip_limit["upload"])
|
// 需求 ④:上传频率限制(既有键,对齐参考 ip_limit["upload"])
|
||||||
KeyUploadCount = "uploadCount" // 窗口内允许上传次数
|
KeyUploadCount = "uploadCount" // 窗口内允许上传次数
|
||||||
KeyUploadMinute = "uploadMinute" // 频率窗口(分钟)
|
KeyUploadMinute = "uploadMinute" // 频率窗口(分钟)
|
||||||
// 需求 ④⑩:存储策略(最大文件大小/允许类型/总容量)
|
// 需求 ④⑩:存储策略(最大文件大小/允许类型/总容量)
|
||||||
KeyUploadSize = "uploadSize" // 既有键:单文件上限(字节),参考实现语义
|
KeyUploadSize = "uploadSize" // 既有键:单文件上限(字节),参考实现语义
|
||||||
KeyMaxFileSize = "max_file_size" // 26.9 新增:存储策略-单文件上限(字节),0=回落 uploadSize
|
KeyMaxFileSize = "max_file_size" // v2 新增:存储策略-单文件上限(字节),0=回落 uploadSize
|
||||||
KeyAllowedTypes = "allowed_file_types" // 既有键:允许类型白名单("*" 不限制)
|
KeyAllowedTypes = "allowed_file_types" // 既有键:允许类型白名单("*" 不限制)
|
||||||
KeyStorageLimit = "storageLimit" // 既有键:站点总容量(字节),0=不限制
|
KeyStorageLimit = "storageLimit" // 既有键:站点总容量(字节),0=不限制
|
||||||
KeyOpenUpload = "openUpload" // 既有键:游客上传开关
|
KeyOpenUpload = "openUpload" // 既有键:游客上传开关
|
||||||
// 26.9:存储引擎运行时可配(热切换;file_storage 为参考既有键保留兼容)
|
// v3:存储引擎运行时可配(热切换;file_storage 为参考既有键保留兼容)
|
||||||
KeyStorageEngine = "storage_engine" // 当前存储引擎:local|s3|webdav
|
KeyStorageEngine = "storage_engine" // 当前存储引擎:local|s3|webdav
|
||||||
KeySiteDomain = "site_domain" // 站点对外域名(空=分享链接用当前地址)
|
KeySiteDomain = "site_domain" // 站点对外域名(空=分享链接用当前地址)
|
||||||
KeyUploadRate = "upload_rate" // 上传带宽字节/秒(0=不限速)
|
|
||||||
KeyDownloadRate = "download_rate" // 下载带宽字节/秒(0=不限速)
|
|
||||||
// —— 26.9 回收与下载安全 ——
|
|
||||||
KeyRecycleEnabled = "recycle_enabled" // 过期分享自动回收开关(1 开 / 0 关)
|
|
||||||
KeyRecycleInterval = "recycle_interval" // 回收扫描间隔(秒,60~86400)
|
|
||||||
KeyRetentionDays = "retention_days" // 全局最长存储时长(天,0=不限制)
|
|
||||||
KeyDedupEnabled = "dedup_enabled" // SHA512 内容去重开关
|
|
||||||
KeyHotlinkEnabled = "hotlink_enabled" // 下载防盗链开关
|
|
||||||
KeyHotlinkWhitelist = "hotlink_whitelist" // 防盗链 Referer 白名单(逗号分隔域名)
|
|
||||||
KeyDirectDownload = "direct_download" // 对象存储直链下载开关(仅 S3 引擎生效)
|
|
||||||
KeyDirectLinkExpire = "direct_link_expire" // 直链有效期(秒,60~3600)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// —— 取值边界(管理端保存与 API 校验用)——
|
// —— 取值边界(管理端保存与 API 校验用)——
|
||||||
@@ -63,16 +52,6 @@ const (
|
|||||||
// 通知标题/内容最大长度。
|
// 通知标题/内容最大长度。
|
||||||
NotifyTitleMaxLen = 128
|
NotifyTitleMaxLen = 128
|
||||||
NotifyContentMaxLen = 2000
|
NotifyContentMaxLen = 2000
|
||||||
// 回收扫描间隔边界(秒):最快 1 分钟一轮,最慢 1 天一轮。
|
|
||||||
RecycleIntervalMin = 60
|
|
||||||
RecycleIntervalMax = 86400
|
|
||||||
// 全局存储时长上限(天):0=不限制,最长 10 年。
|
|
||||||
RetentionDaysMax = 3650
|
|
||||||
// 防盗链白名单最大长度。
|
|
||||||
HotlinkWhitelistMaxLen = 2048
|
|
||||||
// 直链有效期边界(秒)。
|
|
||||||
DirectLinkExpireMin = 60
|
|
||||||
DirectLinkExpireMax = 3600
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// KVSchemaEntry 配置键元数据:类型 / 默认值 / 说明,供管理端 UI 与文档生成。
|
// KVSchemaEntry 配置键元数据:类型 / 默认值 / 说明,供管理端 UI 与文档生成。
|
||||||
@@ -85,7 +64,7 @@ type KVSchemaEntry struct {
|
|||||||
Description string // 中文说明
|
Description string // 中文说明
|
||||||
}
|
}
|
||||||
|
|
||||||
// KVSchema 26.9 全量配置键 schema 表(含既有策略键,供管理端/文档/AI 校验)。
|
// KVSchema v2 全量配置键 schema 表(含既有策略键,供管理端/文档/AI 校验)。
|
||||||
// 注意:Default 与 config defaults() 逐一对应(schema_test 保证)。
|
// 注意:Default 与 config defaults() 逐一对应(schema_test 保证)。
|
||||||
func KVSchema() []KVSchemaEntry {
|
func KVSchema() []KVSchemaEntry {
|
||||||
return []KVSchemaEntry{
|
return []KVSchemaEntry{
|
||||||
@@ -111,19 +90,8 @@ func KVSchema() []KVSchemaEntry {
|
|||||||
{KeyAllowedTypes, "[]string", []string{"*"}, -1, -1, "允许上传类型白名单(\"*\" 不限制)"},
|
{KeyAllowedTypes, "[]string", []string{"*"}, -1, -1, "允许上传类型白名单(\"*\" 不限制)"},
|
||||||
{KeyStorageLimit, "int64", int64(0), 0, -1, "站点总容量(字节),0=不限制"},
|
{KeyStorageLimit, "int64", int64(0), 0, -1, "站点总容量(字节),0=不限制"},
|
||||||
{KeyOpenUpload, "int", 1, 0, 1, "游客上传开关:1 开 / 0 需管理员登录"},
|
{KeyOpenUpload, "int", 1, 0, 1, "游客上传开关:1 开 / 0 需管理员登录"},
|
||||||
// —— 26.9 存储引擎(热切换;引擎参数键沿用 defaults() 既有键,管理端经 config get/update 读写)——
|
// —— v3 存储引擎(热切换;引擎参数键沿用 defaults() 既有键,管理端经 config get/update 读写)——
|
||||||
{KeyStorageEngine, "string", "", 0, 16, "当前存储引擎:local|s3|webdav(热切换,健康检查通过才生效;空=回落启动值 FCB_STORAGE_ENGINE)"},
|
{KeyStorageEngine, "string", "", 0, 16, "当前存储引擎:local|s3|webdav(热切换,健康检查通过才生效;空=回落启动值 FCB_STORAGE_ENGINE)"},
|
||||||
{KeySiteDomain, "string", "", 0, 256, "站点对外域名(http(s)://host[:port],不带路径;空=分享链接用当前访问地址)"},
|
{KeySiteDomain, "string", "", 0, 256, "站点对外域名(http(s)://host[:port],不带路径;空=分享链接用当前访问地址)"},
|
||||||
{KeyUploadRate, "int64", "0", 0, 1073741824, "上传带宽字节/秒(0=不限速;范围 0~1 GiB/s)"},
|
|
||||||
{KeyDownloadRate, "int64", "0", 0, 1073741824, "下载带宽字节/秒(0=不限速;范围 0~1 GiB/s)"},
|
|
||||||
// —— 26.9 回收与下载安全 ——
|
|
||||||
{KeyRecycleEnabled, "int", 1, 0, 1, "过期分享自动回收开关:1 定时清理过期记录与存储对象 / 0 关闭"},
|
|
||||||
{KeyRecycleInterval, "int64", int64(1800), RecycleIntervalMin, RecycleIntervalMax, "回收扫描间隔(秒;范围 60~86400,默认 30 分钟)"},
|
|
||||||
{KeyRetentionDays, "int64", int64(0), 0, RetentionDaysMax, "全局最长存储时长(天):上传超过该天数的分享将被回收,0=不限制"},
|
|
||||||
{KeyDedupEnabled, "int", 1, 0, 1, "SHA512 内容去重:相同文件仅存储一份(多分享引用同一对象)"},
|
|
||||||
{KeyHotlinkEnabled, "int", 0, 0, 1, "下载防盗链:校验 Referer 白名单(空 Referer 放行)"},
|
|
||||||
{KeyHotlinkWhitelist, "string", "", 0, HotlinkWhitelistMaxLen, "防盗链白名单:逗号分隔域名(如 a.com,b.org;空=仅本站域名)"},
|
|
||||||
{KeyDirectDownload, "int", 1, 0, 1, "对象存储直链下载:S3 引擎时 302 跳转到限时预签名 URL(不走服务器代理)"},
|
|
||||||
{KeyDirectLinkExpire, "int64", int64(900), DirectLinkExpireMin, DirectLinkExpireMax, "直链有效期(秒;范围 60~3600,默认 15 分钟;不超过分享剩余时效)"},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ func TestKVSchemaNoDuplicates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestV2NewKeysPresent 26.9 新增键必须在 schema 与 defaults 中同时存在。
|
// TestV2NewKeysPresent v2 新增键必须在 schema 与 defaults 中同时存在。
|
||||||
func TestV2NewKeysPresent(t *testing.T) {
|
func TestV2NewKeysPresent(t *testing.T) {
|
||||||
def := defaults()
|
def := defaults()
|
||||||
newKeys := []string{
|
newKeys := []string{
|
||||||
@@ -44,13 +44,13 @@ func TestV2NewKeysPresent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, k := range newKeys {
|
for _, k := range newKeys {
|
||||||
if _, ok := def[k]; !ok {
|
if _, ok := def[k]; !ok {
|
||||||
t.Fatalf("26.9 新键 %q 缺少默认值", k)
|
t.Fatalf("v2 新键 %q 缺少默认值", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestKVAccessorDefaults 26.9 便捷访问器默认语义。
|
// TestV2AccessorDefaults v2 便捷访问器默认语义。
|
||||||
func TestKVAccessorDefaults(t *testing.T) {
|
func TestV2AccessorDefaults(t *testing.T) {
|
||||||
t.Setenv("FCB_DB_DRIVER", "sqlite")
|
t.Setenv("FCB_DB_DRIVER", "sqlite")
|
||||||
t.Setenv("FCB_DB_DSN", "")
|
t.Setenv("FCB_DB_DSN", "")
|
||||||
c, err := New()
|
c, err := New()
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
|
|
||||||
"fileshare/internal/config"
|
"filecodebox/internal/config"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options 连接选项(main.go 从 config.Env 装配)。
|
// Options 连接选项(main.go 从 config.Env 装配)。
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import (
|
|||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/database"
|
"filecodebox/internal/database"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// pgTestDSN 返回 Postgres 测试连接串;未设置 FCB_TEST_PG_DSN 时返回空。
|
// pgTestDSN 返回 Postgres 测试连接串;未设置 FCB_TEST_PG_DSN 时返回空。
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Package janitor 后台清理循环(安全审计 M5 / 26.9 过期回收):
|
// Package janitor 后台清理循环(安全审计 M5):
|
||||||
// 回收过期容量预留、超时未完成的上传会话(含其分片对象)、过期预签名会话
|
// 回收过期容量预留、超时未完成的上传会话(含其分片对象)与过期预签名会话
|
||||||
// (direct 模式残留对象一并删除),以及过期/超留存期的分享记录与存储对象。
|
// (direct 模式残留对象一并删除)。此前这些资源仅在同 token 复用/显式取消时
|
||||||
|
// 释放,恶意 init 可长期占用容量预留或累积垃圾数据。
|
||||||
package janitor
|
package janitor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -11,8 +12,8 @@ import (
|
|||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
"fileshare/internal/storage"
|
"filecodebox/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// chunkSessionMaxAge 未完成分片会话的最大保留时长(预留 TTL 为 2h,
|
// chunkSessionMaxAge 未完成分片会话的最大保留时长(预留 TTL 为 2h,
|
||||||
@@ -22,22 +23,8 @@ const chunkSessionMaxAge = 24 * time.Hour
|
|||||||
// presignGrace 过期预签名会话的宽限时长(到点即删,避免与在途 confirm 竞争)。
|
// presignGrace 过期预签名会话的宽限时长(到点即删,避免与在途 confirm 竞争)。
|
||||||
const presignGrace = time.Hour
|
const presignGrace = time.Hour
|
||||||
|
|
||||||
// 回收批次上限:单轮每类最多处理 200 条,避免大清理阻塞下一 tick。
|
|
||||||
const recycleBatch = 200
|
|
||||||
|
|
||||||
// Recycler 回收配置(26.9):由 API 层注入(管理端 KV 动态读取)。
|
|
||||||
type Recycler struct {
|
|
||||||
// Enabled 过期自动回收开关。
|
|
||||||
Enabled func() bool
|
|
||||||
// RetentionDays 全局最长存储时长(天,0=不限制)。
|
|
||||||
RetentionDays func() int64
|
|
||||||
// OnRecycled 回收成功后的回调(审计可选),参数:码、文件名、字节数。
|
|
||||||
OnRecycled func(code, name string, size int64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start 启动周期清理循环;ctx 取消时退出。
|
// Start 启动周期清理循环;ctx 取消时退出。
|
||||||
// interval 为兜底默认间隔;recycler 非 nil 时按 RecycleInterval 动态取间隔。
|
func Start(ctx context.Context, db *gorm.DB, store *storage.Manager, interval time.Duration) {
|
||||||
func Start(ctx context.Context, db *gorm.DB, store *storage.Manager, interval time.Duration, recycler *Recycler) {
|
|
||||||
go func() {
|
go func() {
|
||||||
ticker := time.NewTicker(interval)
|
ticker := time.NewTicker(interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
@@ -47,15 +34,12 @@ func Start(ctx context.Context, db *gorm.DB, store *storage.Manager, interval ti
|
|||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
Run(ctx, db, store)
|
Run(ctx, db, store)
|
||||||
if recycler != nil && recycler.Enabled != nil && recycler.Enabled() {
|
|
||||||
RecycleExpired(ctx, db, store, recycler)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run 执行一轮基础设施清理;单项失败仅记日志,不影响其他项。
|
// Run 执行一轮清理;单项失败仅记日志,不影响其他项。
|
||||||
func Run(ctx context.Context, db *gorm.DB, store *storage.Manager) {
|
func Run(ctx context.Context, db *gorm.DB, store *storage.Manager) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
cleanExpiredReservations(ctx, db, now)
|
cleanExpiredReservations(ctx, db, now)
|
||||||
@@ -138,102 +122,3 @@ func cleanExpiredPresignSessions(ctx context.Context, db *gorm.DB, store *storag
|
|||||||
log.Printf("[janitor] 已清理过期预签名会话 upload_id=%s mode=%s", s.UploadID, s.Mode)
|
log.Printf("[janitor] 已清理过期预签名会话 upload_id=%s mode=%s", s.UploadID, s.Mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ 26.9:过期分享回收 ============
|
|
||||||
|
|
||||||
// RecycleExpired 回收过期/超存储时长的分享记录与存储对象:
|
|
||||||
// - 时间过期:expired_count<0 且 expired_at 已过;
|
|
||||||
// - 次数耗尽:expired_count>=0 且 <=0;
|
|
||||||
// - 超留存期:retentionDays>0 且 created_at 早于 now-retentionDays;
|
|
||||||
// - 内容去重开启时同一存储对象可能被多条分享引用,删除前做引用计数
|
|
||||||
// (按 ContentHash/Engine/UUIDFileName 统计),仅删除最后一个引用。
|
|
||||||
//
|
|
||||||
// 返回本轮回收的分享数。由 janitor 定时循环与管理端手动触发共用。
|
|
||||||
func RecycleExpired(ctx context.Context, db *gorm.DB, store *storage.Manager, r *Recycler) int {
|
|
||||||
now := time.Now()
|
|
||||||
q := db.WithContext(ctx).Model(&model.FileCodes{}).
|
|
||||||
Where("(expired_count < 0 AND expired_at IS NOT NULL AND expired_at < ?)"+
|
|
||||||
" OR (expired_count >= 0 AND expired_count <= 0)", now)
|
|
||||||
if r.RetentionDays != nil && r.RetentionDays() > 0 {
|
|
||||||
cutoff := now.AddDate(0, 0, -int(r.RetentionDays()))
|
|
||||||
q = q.Or("created_at < ?", cutoff)
|
|
||||||
}
|
|
||||||
var ids []int64
|
|
||||||
if err := q.Limit(recycleBatch).Pluck("id", &ids).Error; err != nil {
|
|
||||||
log.Printf("[recycle] 查询过期分享失败: %v", err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if len(ids) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
n := 0
|
|
||||||
for _, id := range ids {
|
|
||||||
var fc model.FileCodes
|
|
||||||
if err := db.WithContext(ctx).First(&fc, id).Error; err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// 复核:Expired 语义(避免查询窗口内被取件续期)
|
|
||||||
if !fc.Expired(now) {
|
|
||||||
if r.RetentionDays == nil || r.RetentionDays() <= 0 || fc.CreatedAt.After(now.AddDate(0, 0, -int(r.RetentionDays()))) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recycleOne(ctx, db, store, &fc, r)
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
if n > 0 {
|
|
||||||
log.Printf("[recycle] 本轮回收 %d 条过期分享", n)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecycleRecord 回收单条分享(取件惰性回收入口):删除记录与存储对象(带引用计数)。
|
|
||||||
// 记录不存在时为幂等空操作。
|
|
||||||
func RecycleRecord(ctx context.Context, db *gorm.DB, store *storage.Manager, fc *model.FileCodes, r *Recycler) {
|
|
||||||
// 存在性复核:可能已被定时循环/其他请求回收
|
|
||||||
var cur model.FileCodes
|
|
||||||
if err := db.WithContext(ctx).Where("id = ?", fc.ID).First(&cur).Error; err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
recycleOne(ctx, db, store, &cur, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// recycleOne 删除单条分享记录及其存储对象(带去重引用计数)。
|
|
||||||
func recycleOne(ctx context.Context, db *gorm.DB, store *storage.Manager, fc *model.FileCodes, r *Recycler) {
|
|
||||||
if fc.Text == nil && fc.UUIDFileName != nil {
|
|
||||||
engine, err := engineFor(store, fc.Engine)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[recycle] 引擎不可用 code=%s: %v", fc.Code, err)
|
|
||||||
// 引擎不可用也删记录,避免永久堆积;对象留给对账巡检
|
|
||||||
} else {
|
|
||||||
// 去重引用计数:同 ContentHash+Engine+UUIDFileName 的其他分享还在,则不删对象
|
|
||||||
if fc.ContentHash != nil && *fc.ContentHash != "" {
|
|
||||||
var cnt int64
|
|
||||||
_ = db.WithContext(ctx).Model(&model.FileCodes{}).
|
|
||||||
Where("content_hash = ? AND engine = ? AND uuid_file_name = ? AND id <> ?",
|
|
||||||
*fc.ContentHash, fc.Engine, *fc.UUIDFileName, fc.ID).
|
|
||||||
Count(&cnt).Error
|
|
||||||
if cnt == 0 && fc.SavePath() != "" {
|
|
||||||
delFile(ctx, engine, fc.SavePath(), fc.Code)
|
|
||||||
}
|
|
||||||
} else if fc.SavePath() != "" {
|
|
||||||
delFile(ctx, engine, fc.SavePath(), fc.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := db.WithContext(ctx).Delete(fc).Error; err != nil {
|
|
||||||
log.Printf("[recycle] 删除分享记录失败 code=%s: %v", fc.Code, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if r != nil && r.OnRecycled != nil {
|
|
||||||
r.OnRecycled(fc.Code, fc.Prefix+fc.Suffix, fc.Size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// delFile 删除存储对象,NotFound 视为成功(幂等)。
|
|
||||||
func delFile(ctx context.Context, engine storage.Storage, savePath, code string) {
|
|
||||||
if err := engine.DeleteFile(ctx, savePath); err != nil &&
|
|
||||||
!errors.Is(err, storage.ErrNotFound) && !errors.Is(err, storage.ErrInvalidPath) {
|
|
||||||
log.Printf("[recycle] 删除存储对象失败 code=%s path=%s: %v", code, savePath, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/audit"
|
"filecodebox/internal/audit"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
// auditHooks 审计钩子:由 API 层在响应前后填充与落库。
|
// auditHooks 审计钩子:由 API 层在响应前后填充与落库。
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/audit"
|
"filecodebox/internal/audit"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type captureSink struct {
|
type captureSink struct {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/audit"
|
"filecodebox/internal/audit"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// memSink 测试用内存落库实现。
|
// memSink 测试用内存落库实现。
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
package middleware
|
|
||||||
|
|
||||||
// 26.9:上传/下载带宽限速(字节/秒,0=不限速)。
|
|
||||||
//
|
|
||||||
// 设计要点:
|
|
||||||
// - 令牌桶(token bucket):每 Read 计算自上次起累计可消费字节,
|
|
||||||
// 未达上限则阻塞至令牌补足;保证长期平均速率,瞬时按桶容量突发。
|
|
||||||
// - 桶容量 = rate(1 秒突发量):简单且足够友好;后续若要更细粒度可参数化。
|
|
||||||
// - 0 速率直接透传,不引入任何开销(性能基线保持)。
|
|
||||||
// - 对 gin 中间件"上传限速":包裹 c.Request.Body,使 multipart 解析/单分片读取
|
|
||||||
// 都被天然节流;下载侧由 api 层在 storage.Open 之后包裹 ReadCloser。
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"fileshare/internal/response"
|
|
||||||
)
|
|
||||||
|
|
||||||
// rateLimitedReader 按 bytes/sec 限速的 io.Reader(基于时间窗的精确调度)。
|
|
||||||
// 实现:累计已读字节,按 rate 推算"应到达时间";超前则 sleep 补齐。
|
|
||||||
// - rate <= 0 时退化为直通;
|
|
||||||
// - 长期速率严格 ≤ rate,瞬时按调用方 Read 块大小自然突发;
|
|
||||||
// - 并发安全(每次请求持独立实例即可,但保留锁便于复用)。
|
|
||||||
type rateLimitedReader struct {
|
|
||||||
r io.Reader
|
|
||||||
rate int // 字节/秒
|
|
||||||
start time.Time // 起始时间(首 Read 时记)
|
|
||||||
bytes int64 // 累计已读字节
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRateLimitedReader 构造限速 reader;rate<=0 返回原始 reader。
|
|
||||||
func NewRateLimitedReader(r io.Reader, rate int) io.Reader {
|
|
||||||
if r == nil || rate <= 0 {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
return &rateLimitedReader{r: r, rate: rate}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read 限速:底层 Read 后若已超时应到达时间,sleep 补齐;错误/EOF 透传。
|
|
||||||
func (rl *rateLimitedReader) Read(p []byte) (int, error) {
|
|
||||||
if rl.rate <= 0 {
|
|
||||||
return rl.r.Read(p)
|
|
||||||
}
|
|
||||||
n, err := rl.r.Read(p)
|
|
||||||
if n <= 0 {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
rl.mu.Lock()
|
|
||||||
if rl.start.IsZero() {
|
|
||||||
rl.start = time.Now()
|
|
||||||
}
|
|
||||||
rl.bytes += int64(n)
|
|
||||||
// 已读 n 字节的"应到达时间" = start + bytes/rate
|
|
||||||
expected := rl.start.Add(time.Duration(float64(time.Second) * float64(rl.bytes) / float64(rl.rate)))
|
|
||||||
now := time.Now()
|
|
||||||
rl.mu.Unlock()
|
|
||||||
if now.Before(expected) {
|
|
||||||
time.Sleep(expected.Sub(now))
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadBandwidthMiddleware 限速上传请求体(包裹 c.Request.Body)。
|
|
||||||
// rateProvider 每次请求动态读取最新配置(管理端改 KV 后立即生效)。
|
|
||||||
func UploadBandwidthMiddleware(rateProvider func() int) gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
rate := 0
|
|
||||||
if rateProvider != nil {
|
|
||||||
rate = rateProvider()
|
|
||||||
}
|
|
||||||
if rate > 0 && c.Request != nil && c.Request.Body != nil {
|
|
||||||
c.Request.Body = WrapReadCloser(c.Request.Body, rate)
|
|
||||||
}
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapReadCloser 包裹任意 io.ReadCloser 为限速版本(io.Copy 等下游使用)。
|
|
||||||
func WrapReadCloser(rc io.ReadCloser, rate int) io.ReadCloser {
|
|
||||||
if rc == nil || rate <= 0 {
|
|
||||||
return rc
|
|
||||||
}
|
|
||||||
return &rateLimitedReadCloser{rc: rc, rl: &rateLimitedReader{r: rc, rate: rate}}
|
|
||||||
}
|
|
||||||
|
|
||||||
type rateLimitedReadCloser struct {
|
|
||||||
rc io.ReadCloser
|
|
||||||
rl *rateLimitedReader
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rateLimitedReadCloser) Read(p []byte) (int, error) { return r.rl.Read(p) }
|
|
||||||
func (r *rateLimitedReadCloser) Close() error { return r.rc.Close() }
|
|
||||||
|
|
||||||
// DownloadRateReporter 限速命中提示:超长下载时可选上报。
|
|
||||||
// 当前仅占位——真正接入可在 audit 中按"下载耗时/字节比"标记慢速,但用户暂不需要。
|
|
||||||
var _ = response.Fail // 保留 response 包以备后续扩展
|
|
||||||
var _ = http.StatusOK
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestRateLimitedReaderAtRate 验证长读大致符合设定速率。
|
|
||||||
// 设 100 KB/s,读取 400 KB 总字节,期望耗时约 4s(±1s 容忍)。
|
|
||||||
func TestRateLimitedReaderAtRate(t *testing.T) {
|
|
||||||
const rate = 100 * 1024
|
|
||||||
const total = 400 * 1024
|
|
||||||
src := io.LimitReader(bytes.NewReader(make([]byte, total+1024)), total)
|
|
||||||
rl := NewRateLimitedReader(src, rate).(*rateLimitedReader)
|
|
||||||
|
|
||||||
buf := make([]byte, 32*1024) // 32KB 块
|
|
||||||
start := time.Now()
|
|
||||||
read := 0
|
|
||||||
for {
|
|
||||||
n, err := rl.Read(buf)
|
|
||||||
read += n
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elapsed := time.Since(start)
|
|
||||||
want := time.Duration(float64(time.Second) * float64(total) / float64(rate))
|
|
||||||
if elapsed < want-time.Second {
|
|
||||||
t.Fatalf("读取过快 elapsed=%v want>=%v", elapsed, want)
|
|
||||||
}
|
|
||||||
if elapsed > want+1500*time.Millisecond {
|
|
||||||
t.Fatalf("读取过慢 elapsed=%v want<=%v", elapsed, want+1500*time.Millisecond)
|
|
||||||
}
|
|
||||||
if read != total {
|
|
||||||
t.Fatalf("读到 %d 字节,期望 %d", read, total)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRateLimitedReaderZeroPassthrough 速率 0 时不应引入任何延迟/包封。
|
|
||||||
func TestRateLimitedReaderZeroPassthrough(t *testing.T) {
|
|
||||||
src := bytes.NewReader([]byte("hello"))
|
|
||||||
rl := NewRateLimitedReader(src, 0)
|
|
||||||
if rl == src {
|
|
||||||
// 透传:返回原 reader
|
|
||||||
} else if _, ok := rl.(*rateLimitedReader); ok {
|
|
||||||
// 0 速率时按实现可走 enabled=false(不退化亦可)
|
|
||||||
}
|
|
||||||
// 关键:必须能读完
|
|
||||||
b, err := io.ReadAll(rl)
|
|
||||||
if err != nil || string(b) != "hello" {
|
|
||||||
t.Fatalf("0 速率透传失败: %q %v", b, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestWrapReadCloserClose 验证包裹后 Close 透传到底层。
|
|
||||||
func TestWrapReadCloserClose(t *testing.T) {
|
|
||||||
src := &closeCount{Reader: bytes.NewReader([]byte("xyz")), closed: 0}
|
|
||||||
rc := WrapReadCloser(src, 50*1024)
|
|
||||||
if rc == nil {
|
|
||||||
t.Fatal("WrapReadCloser nil")
|
|
||||||
}
|
|
||||||
_, _ = io.ReadAll(rc)
|
|
||||||
if err := rc.Close(); err != nil {
|
|
||||||
t.Fatalf("close err: %v", err)
|
|
||||||
}
|
|
||||||
if src.closed != 1 {
|
|
||||||
t.Fatalf("底层 Close 未被调用: %d", src.closed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type closeCount struct {
|
|
||||||
io.Reader
|
|
||||||
closed int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *closeCount) Close() error { c.closed++; return nil }
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
// Package middleware — hotlink.go 下载防盗链(26.9):
|
|
||||||
// 校验 Referer 白名单。规则:
|
|
||||||
// - Referer 为空(直接访问/curl/浏览器地址栏):放行(不误伤正常取件);
|
|
||||||
// - Referer 与当前请求 Host 同源:放行;
|
|
||||||
// - Referer 主机命中管理端白名单(hotlink_whitelist,逗号分隔域名,支持 *.example.com 通配):放行;
|
|
||||||
// - 其余一律 403。
|
|
||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"fileshare/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HotlinkMiddleware 返回防盗链中间件;cfg 动态读取开关与白名单(管理端改后立即生效)。
|
|
||||||
func HotlinkMiddleware(cfg *config.Config) gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
if !cfg.HotlinkEnabled() {
|
|
||||||
c.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ref := c.GetHeader("Referer")
|
|
||||||
if ref == "" {
|
|
||||||
c.Next() // 空 Referer 放行
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u, err := url.Parse(ref)
|
|
||||||
if err != nil || u.Host == "" {
|
|
||||||
c.Next() // 非法 Referer 视同空,放行(避免误伤)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if strings.EqualFold(u.Host, c.Request.Host) {
|
|
||||||
c.Next() // 同源放行
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if hostAllowed(u.Host, cfg.HotlinkWhitelist()) {
|
|
||||||
c.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.AbortWithStatusJSON(403, gin.H{"message": "防盗链:外部站点引用不允许访问该资源"})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hostAllowed 判断主机是否命中白名单(精确匹配或 *. 通配后缀匹配)。
|
|
||||||
// 白名单条目可带端口;通配写作 .example.com 或 *.example.com。
|
|
||||||
func hostAllowed(host string, whitelist []string) bool {
|
|
||||||
if len(whitelist) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
host = strings.ToLower(host)
|
|
||||||
for _, w := range whitelist {
|
|
||||||
w = strings.ToLower(strings.TrimSpace(w))
|
|
||||||
w = strings.TrimPrefix(w, "*") // *.example.com → .example.com
|
|
||||||
if w == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if host == strings.TrimPrefix(w, ".") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(host, w) && strings.HasPrefix(w, ".") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
// jwtClaims 自定义声明:对齐参考实现(payload 含 is_admin 与 exp)。
|
// jwtClaims 自定义声明:对齐参考实现(payload 含 is_admin 与 exp)。
|
||||||
@@ -36,7 +36,7 @@ func SignAdminToken(secret string, expires time.Duration) (string, time.Time, er
|
|||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
Issuer: "fileshare",
|
Issuer: "filecodebox",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testSecret = "unit-test-secret-0123456789abcdef"
|
const testSecret = "unit-test-secret-0123456789abcdef"
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/cache"
|
"filecodebox/internal/cache"
|
||||||
"fileshare/internal/response"
|
"filecodebox/internal/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 限流类别(对齐参考 apps/base/utils.py 的 ip_limit)。
|
// 限流类别(对齐参考 apps/base/utils.py 的 ip_limit)。
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/cache"
|
"filecodebox/internal/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
// failingCache 模拟缓存故障(Get/Incr 均返回非 ErrNotFound 错误)。
|
// failingCache 模拟缓存故障(Get/Incr 均返回非 ErrNotFound 错误)。
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"fileshare/internal/cache"
|
"filecodebox/internal/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
func rateLimitRouter(rl *RateLimiter, kind string) *gin.Engine {
|
func rateLimitRouter(rl *RateLimiter, kind string) *gin.Engine {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -23,33 +22,15 @@ type FileCodes struct {
|
|||||||
ExpiredCount int `gorm:"default:0" json:"expired_count"` // 剩余可取次数;<0 表示按时间过期
|
ExpiredCount int `gorm:"default:0" json:"expired_count"` // 剩余可取次数;<0 表示按时间过期
|
||||||
UsedCount int `gorm:"default:0" json:"used_count"` // 已取次数
|
UsedCount int `gorm:"default:0" json:"used_count"` // 已取次数
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
FileHash *string `gorm:"size:64" json:"file_hash"` // SHA256
|
FileHash *string `gorm:"size:64" json:"file_hash"` // SHA256
|
||||||
ContentHash *string `gorm:"size:128;index" json:"content_hash"` // 26.9:SHA512(内容去重;同哈希分享复用同一存储对象)
|
|
||||||
IsChunked bool `gorm:"default:false" json:"is_chunked"`
|
IsChunked bool `gorm:"default:false" json:"is_chunked"`
|
||||||
UploadID *string `gorm:"size:36" json:"upload_id"` // 分片上传会话 ID
|
UploadID *string `gorm:"size:36" json:"upload_id"` // 分片上传会话 ID
|
||||||
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(26.9:local|s3|webdav;空=历史数据按当前引擎取)
|
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(v3:local|s3|webdav;空=历史数据按当前引擎取)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 表名。
|
// TableName 表名。
|
||||||
func (FileCodes) TableName() string { return "file_codes" }
|
func (FileCodes) TableName() string { return "file_codes" }
|
||||||
|
|
||||||
// SavePath 存储侧相对路径(file_path/uuid_file_name 拼接;对齐 api 层 fileSavePath)。
|
|
||||||
// 26.9:上移到模型层,供 api 与 janitor 共用(去重引用计数与回收删除都需要)。
|
|
||||||
func (f *FileCodes) SavePath() string {
|
|
||||||
dir := ""
|
|
||||||
if f.FilePath != nil {
|
|
||||||
dir = strings.Trim(*f.FilePath, "/")
|
|
||||||
}
|
|
||||||
name := ""
|
|
||||||
if f.UUIDFileName != nil {
|
|
||||||
name = *f.UUIDFileName
|
|
||||||
}
|
|
||||||
if dir == "" {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
return dir + "/" + name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expired 判断是否已过期(对齐参考语义:expired_count<0 按时间,否则按次数)。
|
// Expired 判断是否已过期(对齐参考语义:expired_count<0 按时间,否则按次数)。
|
||||||
func (f *FileCodes) Expired(now time.Time) bool {
|
func (f *FileCodes) Expired(now time.Time) bool {
|
||||||
if f.ExpiredAt == nil {
|
if f.ExpiredAt == nil {
|
||||||
@@ -74,7 +55,7 @@ type UploadChunk struct {
|
|||||||
SavePath string `gorm:"size:512" json:"save_path"`
|
SavePath string `gorm:"size:512" json:"save_path"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
Completed bool `gorm:"default:false" json:"completed"`
|
Completed bool `gorm:"default:false" json:"completed"`
|
||||||
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(26.9:分片与会话记录当时引擎,合并走同一引擎)
|
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(v3:分片与会话记录当时引擎,合并走同一引擎)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 表名。
|
// TableName 表名。
|
||||||
@@ -103,7 +84,7 @@ type PresignUploadSession struct {
|
|||||||
ExpireStyle string `gorm:"size:20;default:day" json:"expire_style"`
|
ExpireStyle string `gorm:"size:20;default:day" json:"expire_style"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
ExpiresAt time.Time `json:"expires_at"`
|
ExpiresAt time.Time `json:"expires_at"`
|
||||||
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(26.9:直传/代理完成走同一引擎取回)
|
Engine string `gorm:"size:16;default:''" json:"engine"` // 归属存储引擎(v3:直传/代理完成走同一引擎取回)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 表名。
|
// TableName 表名。
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import (
|
|||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/config"
|
"filecodebox/internal/config"
|
||||||
"fileshare/internal/database"
|
"filecodebox/internal/database"
|
||||||
"fileshare/internal/settings"
|
"filecodebox/internal/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// pgTestDSN 返回 Postgres 测试连接串;未设置 FCB_TEST_PG_DSN 时跳过。
|
// pgTestDSN 返回 Postgres 测试连接串;未设置 FCB_TEST_PG_DSN 时跳过。
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Package settings — schema.go:26.9 配置键 schema 常量与元数据表。
|
// Package settings — schema.go:v2 配置键 schema 常量与元数据表。
|
||||||
//
|
//
|
||||||
// 键名常量的单一事实来源在 internal/config/schema.go(defaults() 需引用);
|
// 键名常量的单一事实来源在 internal/config/schema.go(defaults() 需引用);
|
||||||
// 本文件 re-export 供 API/管理层使用,并提供「键名/类型/默认值」全量表,
|
// 本文件 re-export 供 API/管理层使用,并提供「键名/类型/默认值」全量表,
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
// 4. schema 同步测试(config schema_test / settings schema_test)
|
// 4. schema 同步测试(config schema_test / settings schema_test)
|
||||||
package settings
|
package settings
|
||||||
|
|
||||||
import "fileshare/internal/config"
|
import "filecodebox/internal/config"
|
||||||
|
|
||||||
// —— 键名 re-export(与 config 包保持同一字符串,避免魔法值散落)——
|
// —— 键名 re-export(与 config 包保持同一字符串,避免魔法值散落)——
|
||||||
const (
|
const (
|
||||||
@@ -35,7 +35,7 @@ const (
|
|||||||
KeyAllowedTypes = config.KeyAllowedTypes
|
KeyAllowedTypes = config.KeyAllowedTypes
|
||||||
KeyStorageLimit = config.KeyStorageLimit
|
KeyStorageLimit = config.KeyStorageLimit
|
||||||
KeyOpenUpload = config.KeyOpenUpload
|
KeyOpenUpload = config.KeyOpenUpload
|
||||||
// 26.9 存储引擎
|
// v3 存储引擎
|
||||||
KeyStorageEngine = config.KeyStorageEngine
|
KeyStorageEngine = config.KeyStorageEngine
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 敏感键:不允许出现在管理端 config get 下发/前端可见集合中(双模式下一致生效)。
|
// 敏感键:不允许出现在管理端 config get 下发/前端可见集合中(双模式下一致生效)。
|
||||||
// 26.9:引擎凭据(webdav_password/s3_secret_access_key/aws_session_token)加入敏感集——
|
// v3:引擎凭据(webdav_password/s3_secret_access_key/aws_session_token)加入敏感集——
|
||||||
// 管理端 get 返回掩码占位,update 时空串/掩码=不修改;公开 config 永不下发。
|
// 管理端 get 返回掩码占位,update 时空串/掩码=不修改;公开 config 永不下发。
|
||||||
var SensitiveKeys = []string{
|
var SensitiveKeys = []string{
|
||||||
"admin_token", "jwt_secret",
|
"admin_token", "jwt_secret",
|
||||||
@@ -72,7 +72,7 @@ func IsSensitiveKey(key string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// KVSchema 返回 26.9 全量配置键元数据(键名/类型/默认值/边界/说明)。
|
// KVSchema 返回 v2 全量配置键元数据(键名/类型/默认值/边界/说明)。
|
||||||
// 默认值必须与 config defaults() 一致(schema 同步测试保证)。
|
// 默认值必须与 config defaults() 一致(schema 同步测试保证)。
|
||||||
func KVSchema() []config.KVSchemaEntry { return config.KVSchema() }
|
func KVSchema() []config.KVSchemaEntry { return config.KVSchema() }
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"fileshare/internal/config"
|
"filecodebox/internal/config"
|
||||||
"fileshare/internal/model"
|
"filecodebox/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// settingsKey 数据库中的配置键(对齐参考实现)。
|
// settingsKey 数据库中的配置键(对齐参考实现)。
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ type LocalStorage struct {
|
|||||||
rootReal string
|
rootReal string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalStorage 构造本地引擎。root 为空时使用系统临时目录下的 fileshare_storage。
|
// NewLocalStorage 构造本地引擎。root 为空时使用系统临时目录下的 filecodebox_storage。
|
||||||
func NewLocalStorage(root string) (*LocalStorage, error) {
|
func NewLocalStorage(root string) (*LocalStorage, error) {
|
||||||
if strings.TrimSpace(root) == "" {
|
if strings.TrimSpace(root) == "" {
|
||||||
root = filepath.Join(os.TempDir(), "fileshare_storage")
|
root = filepath.Join(os.TempDir(), "filecodebox_storage")
|
||||||
}
|
}
|
||||||
abs, err := filepath.Abs(root)
|
abs, err := filepath.Abs(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func sha256Hex(b []byte) string {
|
|||||||
func TestLocalSaveOpenRange(t *testing.T) {
|
func TestLocalSaveOpenRange(t *testing.T) {
|
||||||
st := newTestLocal(t)
|
st := newTestLocal(t)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
data := []byte("hello fileshare 本地引擎 0123456789")
|
data := []byte("hello filecodebox 本地引擎 0123456789")
|
||||||
|
|
||||||
n, err := st.SaveFile(ctx, bytes.NewReader(data), "2025/08/测试文件.bin")
|
n, err := st.SaveFile(ctx, bytes.NewReader(data), "2025/08/测试文件.bin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// Manager 存储引擎管理器:实现 Storage 全接口并支持运行时热切换。
|
// Manager 存储引擎管理器:实现 Storage 全接口并支持运行时热切换。
|
||||||
//
|
//
|
||||||
// 26.9 需求:管理后台可设置存储类型(local|s3|webdav)与各引擎参数,
|
// v3 需求:管理后台可设置存储类型(local|s3|webdav)与各引擎参数,
|
||||||
// 保存后无需重启即生效。设计要点:
|
// 保存后无需重启即生效。设计要点:
|
||||||
// - 读写/保存类操作全部委托到"当前引擎"(原子指针,无锁热路径);
|
// - 读写/保存类操作全部委托到"当前引擎"(原子指针,无锁热路径);
|
||||||
// - Switch 先构建并健康检查新引擎,成功才替换指针,失败保持原引擎;
|
// - Switch 先构建并健康检查新引擎,成功才替换指针,失败保持原引擎;
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func TestSwitchSuccessAndCurrentName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSwitchFailureKeepsCurrent 健康检查失败时保持原引擎(26.9 核心语义)。
|
// TestSwitchFailureKeepsCurrent 健康检查失败时保持原引擎(v3 核心语义)。
|
||||||
func TestSwitchFailureKeepsCurrent(t *testing.T) {
|
func TestSwitchFailureKeepsCurrent(t *testing.T) {
|
||||||
var s3Fail atomic.Bool
|
var s3Fail atomic.Bool
|
||||||
s3Fail.Store(true) // s3 不健康
|
s3Fail.Store(true) // s3 不健康
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
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-DYsKpclu.js";import{_ as x}from"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";import{u as A}from"./auth-B7MDTgxJ.js";import"./admin-DEOkRyTC.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};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import{d as U,u as I,i as M,I as S,c as u,b as t,t as a,f as l,C as r,Z as w,D as v,w as $,q as x,F as E,r as F,G as q,j as g,R as T,H as R,K as j,e as B,$ as H,s as _,o as c,_ as L}from"./index-DYsKpclu.js";import{e as O}from"./admin-DEOkRyTC.js";import{_ as G}from"./Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js";const K={class:"toolbar"},P={class:"page-title"},Z={class:"page-sub"},J={value:""},Q={value:"upload"},W={value:"download"},X={value:""},Y={value:"success"},tt={value:"failed"},et={value:"denied"},at={class:"btn",type:"submit"},nt={key:0,class:"loading-block"},lt={key:1,class:"card empty"},st={key:2,class:"table-wrap"},it={class:"table"},ot=["title"],dt={class:"code-cell"},ut={class:"ip-cell"},ct=["title"],rt=U({__name:"AuditView",setup(mt){const{t:n}=I(),z=M(),p=g([]),b=g(0),f=g(!1),o=T({action:"",result:"",ip:"",start:"",end:""}),d=T({page:1,size:20});function h(s,i=!1){if(!s)return"";const e=new Date(s);return Number.isNaN(e.getTime())?"":(i&&e.setHours(23,59,59,999),e.toISOString())}async function m(){f.value=!0;try{const s=await O({page:d.page,size:d.size,action:o.action||void 0,result:o.result||void 0,ip:o.ip.trim()||void 0,startTime:h(o.start)||void 0,endTime:h(o.end,!0)||void 0});p.value=s.data,b.value=s.total}catch(s){z.error(s instanceof R?s.msg:n("admin.audit.loadFailed"))}finally{f.value=!1}}function D(){d.page=1,m()}function V(){o.action="",o.result="",o.ip="",o.start="",o.end="",d.page=1,m()}function k(s,i){d.page=s,d.size=i,m()}function A(s){return s==="upload"?n("admin.audit.actionUpload"):s==="download"?n("admin.audit.actionDownload"):s||"-"}function y(s){return s==="success"?{cls:"badge-success",text:n("common.success")}:s==="denied"?{cls:"badge-warn",text:n("common.denied")}:s==="failed"?{cls:"badge-danger",text:n("common.failed")}:{cls:"badge-muted",text:s||"-"}}function C(s){const i=[s.deviceOs,s.deviceBrowser].filter(Boolean),e=s.deviceType?` · ${s.deviceType}`:"";return i.length?i.join(" · ")+e:"-"}function N(s){const i=s.transferredBytes;if(i==null)return"-";const e=s.sizeBytes;return e!=null&&e!==i?`${_(i)} / ${_(e)}`:_(i)}return S(m),(s,i)=>(c(),u("div",null,[t("div",K,[t("div",null,[t("h2",P,a(l(n)("admin.audit.title")),1),t("p",Z,a(l(n)("admin.audit.subtitle")),1)])]),t("form",{class:"filter-bar",onSubmit:$(D,["prevent"])},[t("label",null,[t("span",null,a(l(n)("admin.audit.action")),1),r(t("select",{"onUpdate:modelValue":i[0]||(i[0]=e=>o.action=e),class:"select"},[t("option",J,a(l(n)("common.all")),1),t("option",Q,a(l(n)("admin.audit.actionUpload")),1),t("option",W,a(l(n)("admin.audit.actionDownload")),1)],512),[[w,o.action]])]),t("label",null,[t("span",null,a(l(n)("admin.audit.result")),1),r(t("select",{"onUpdate:modelValue":i[1]||(i[1]=e=>o.result=e),class:"select"},[t("option",X,a(l(n)("common.all")),1),t("option",Y,a(l(n)("common.success")),1),t("option",tt,a(l(n)("common.failed")),1),t("option",et,a(l(n)("common.denied")),1)],512),[[w,o.result]])]),t("label",null,[t("span",null,a(l(n)("admin.audit.filterIp")),1),r(t("input",{"onUpdate:modelValue":i[2]||(i[2]=e=>o.ip=e),class:"input",placeholder:"10.0.0.1",style:{"max-width":"150px"}},null,512),[[v,o.ip]])]),t("label",null,[t("span",null,a(l(n)("admin.audit.filterStart")),1),r(t("input",{"onUpdate:modelValue":i[3]||(i[3]=e=>o.start=e),class:"input",type:"datetime-local"},null,512),[[v,o.start]])]),t("label",null,[t("span",null,a(l(n)("admin.audit.filterEnd")),1),r(t("input",{"onUpdate:modelValue":i[4]||(i[4]=e=>o.end=e),class:"input",type:"datetime-local"},null,512),[[v,o.end]])]),t("button",at,a(l(n)("common.query")),1),t("button",{class:"btn btn-ghost",type:"button",onClick:V},a(l(n)("common.reset")),1)],32),f.value?(c(),u("div",nt,[i[5]||(i[5]=t("span",{class:"spin","aria-hidden":"true"},null,-1)),x(" "+a(l(n)("common.loading")),1)])):p.value.length?(c(),u("div",st,[t("table",it,[t("thead",null,[t("tr",null,[t("th",null,a(l(n)("admin.audit.colTime")),1),t("th",null,a(l(n)("admin.audit.colAction")),1),t("th",null,a(l(n)("admin.audit.colResult")),1),t("th",null,a(l(n)("admin.audit.colFile")),1),t("th",null,a(l(n)("admin.audit.colCode")),1),t("th",null,a(l(n)("admin.audit.colBytes")),1),t("th",null,a(l(n)("admin.audit.colIp")),1),t("th",null,a(l(n)("admin.audit.colDevice")),1),t("th",null,a(l(n)("admin.audit.colDuration")),1),t("th",null,a(l(n)("admin.audit.colUaError")),1)])]),t("tbody",null,[(c(!0),u(E,null,F(p.value,e=>(c(),u("tr",{key:e.id},[t("td",null,a(l(j)(e.createdAt)),1),t("td",null,[t("span",{class:B(["badge",e.action==="upload"?"":"badge-muted"])},a(A(e.action)),3)]),t("td",null,[t("span",{class:B(["badge",y(e.result).cls])},a(y(e.result).text),3)]),t("td",{class:"wrap",title:e.fileName},a(e.fileName||"-"),9,ot),t("td",dt,a(e.fileCode||"-"),1),t("td",null,a(N(e)),1),t("td",ut,a(e.ip||"-"),1),t("td",null,a(C(e)),1),t("td",null,a(l(H)(e.durationMs)),1),t("td",{class:"wrap ua-cell",title:e.errorMsg||e.userAgent},a(e.errorMsg||e.userAgent||"-"),9,ct)]))),128))])])])):(c(),u("div",lt,[i[6]||(i[6]=t(Line truncated
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
import{d as R,u as V,i as z,j as y,I,J as $,z as E,A as O,N as W,b as c,C as j,D as G,f as h,c as l,q as x,t as u,F as C,r as A,w as T,e as M,g as S,h as H,B as K,o as r}from"./index-DYsKpclu.js";import{P as U}from"./PageShell-D87JkPkG.js";import{d as B,a as J,l as Q}from"./docsSource-BXkkn0HE.js";import{k as X}from"./markdown-B5D8JARp.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";function Y(m,o){const n=m.toLowerCase().replace(/[^\p{L}\p{N}\s-]/gu,"").trim().replace(/\s+/g,"-")||"section";let d=n,i=2;for(;o.has(d);)d=`${n}-${i++}`;return o.add(d),d}const Z=new Set(["script","style","iframe","object","embed","form","link","meta","base","svg","math","frame","frameset","applet","template","noscript","title"]),tt=new Set(["srcdoc","sandbox","formaction","action","xlink:href","srcset","poster","background","dynsrc","lowsrc","data"]);function et(m,o){const n=m.trim();return o&&/^data:image\//i.test(n)?!0:/^(https?:|mailto:|\/|#|\.\/)/i.test(n)||!/^[a-z][a-z0-9+.-]*:/i.test(n)}function st(m){const o=new DOMParser().parseFromString(m,"text/html");for(const n of[...o.body.querySelectorAll("*")]){const d=n.tagName.toLowerCase();if(Z.has(d)){n.remove();continue}for(const i of[...n.attributes]){const e=i.name.toLowerCase();if(e.startsWith("on")||tt.has(e)){n.removeAttribute(i.name);continue}(e==="href"||e==="src"||e.endsWith(":src")||e.endsWith(":href"))&&!et(i.value,e==="src")&&n.removeAttribute(i.name)}}return o.body.innerHTML}function ot(m){const o=X.parse(m,{gfm:!0,breaks:!1}),n=new DOMParser().parseFromString(o,"text/html");for(const e of[...n.body.querySelectorAll("a[href]")]){const f=e.getAttribute("href")??"";/^https?:\/\//i.test(f)&&(e.setAttribute("target","_blank"),e.setAttribute("rel","noopener noreferrer"))}const d=new Set,i=[];for(const e of[...n.body.querySelectorAll("h1, h2, h3")]){const f=Number(e.tagName.substring(1)),p=(e.textContent??"").trim();if(!p)continue;const v=Y(p,d);e.setAttribute("id",v),f>=2&&i.push({id:v,text:p,level:f})}return{html:st(n.body.innerHTML),toc:i}}const nt={class:"docs-shell"},at={class:"docs-sidebar"},rt=["placeholder"],lt={key:0,class:"empty",style:{padding:"20px 8px"}},ct={class:"hint"},it=["aria-label"],ut=["onClick"],dt={key:0,class:"badge badge-muted",style:{"margin-left":"6px"}},ht={key:0,class:"hint",style:{padding:"0 11px"}},mt={key:1,class:"doc-toc"},ft={class:"toc-title"},pt=["href","onClick"],vt={class:"docs-content"},gt={key:0,class:"loading-block"},yt={key:1,class:"empty"},_t={style:{"font-weight":"600",color:"var(--c-text)"}},bt={class:"hint",style:{"max-width":"420px",margin:"0 auto"}},kt={key:2,class:"empty"},wt=["innerHTML"],At=R({__name:"DocsView",setup(m){const{t:o}=V(),n=W(),d=K(),i=z(),e=y([...B]),f=y(""),p=y(""),v=y(""),_=y([]),k=y(!1),b=y("");I(async()=>{const a=await J();a.length&&(e.value=[...B,...a]),L()});const N=()=>{if(typeof n.params.slug=="string")return n.params.slug;const a=n.query.p;return typeof a=="string"?a:""};async function L(){const a=N(),s=e.value;if(!s.length)return;const t=s.find(g=>g.slug===a)??s[0];if(t&&!(t.slug===f.value&&p.value)){f.value=t.slug,k.value=!0;try{p.value=await Q(t);const{html:g,toc:P}=ot(p.value);v.value=g,_.value=P}catch{v.value="",_.value=[],i.error(o("docs.loadFailed",{title:t.title}))}finally{k.value=!1}}}$(()=>n.fullPath,()=>{L()});function F(a){d.push({name:"docs-detail",params:{slug:a}})}function q(a){document.getElementById(a)?.scrollIntoView({behavior:"smooth"})}const D=H(()=>{const a=b.value.trim().toLowerCase();return a?e.value.map(s=>{const g=(s.embedded??"").toLowerCase().split(a).length-1+(s.title.toLowerCase().includes(a)?1:0);return{doc:s,hits:g}}).filter(s=>s.hits>0).sort((s,t)=>t.hits-s.hits):e.value.map(s=>({doc:s,hits:-1}))}),w=H(()=>b.value.trim().length>0);return(a,s)=>(r(),E(U,null,{default:O(()=>[c("div",nt,[c("aside",at,[j(c("input",{"onUpdate:modelValue":s[0]||(s[0]=t=>b.value=t),class:"input docs-search",placeholder:h(o)("docs.searchPlaceholder")},null,8,rt),[[G,b.value]]),e.value.length?(r(),l(C,{key:1},[c("nav",{class:"doc-list","aria-label":h(o)("docs.sidebar")},[(r(!0),l(C,null,A(D.value,t=>(r(),l("a",{key:t.doc.slug,class:M({active:t.doc.slug===f.value}),href:"#",onClick:T(g=>F(t.doc.slug),["prevent"])},[x(u(t.doc.title)+" ",1),w.value&&t.hits>0?(r(),l("span",dt,u(t.hits),1)):S("",!0)],10,ut))),128))],8,it),w.value&&!D.value.length?(r(),l("p",ht,u(h(o)("docs.noMatch")),1)):S("",!0),_.value.length&&!w.value?(r(),l("div",mt,[c("div",ft,u(h(o)("docs.tocTitle")),1),(r(!0),l(C,null,A(_.value,t=>(r(),l("a",{key:t.id,href:`#${t.id}`,class:M({"lvl-3":t.level>=3}),onClick:T(g=>q(t.id),["prevent"])},u(t.text),11,pt))),128))])):S("",!0)],64)):(r(),l("div",lt,[x(u(h(o)("docs.notGenerated")),1),s[1]||(s[1]=c("br",null,null,-1)),c("span",ct,u(h(o)("docs.buildHint")),1)]))]),c("article",vt,[k.value?(r(),l("div",gt,[s[2]||(s[2]=c("span",{class:"spin","aria-hidden":"true"},null,-1)),x(" "+u(h(o)("docs.loading")),1)])):e.value.length?v.value?(r(),l("div",{key:3,class:"markdoLine truncated
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import{d as N,I as V,P as Y,z as J,c as f,w as $,b as e,t as a,Q as X,n as Z,g as L,T as ee,o as p,u as te,a as ne,i as ie,f as l,C as S,D as T,q as z,F as se,r as le,G as D,A as ae,j as _,R as A,H as x,e as B,s as oe,K as E,k as w,p as de,M as ce}from"./index-DYsKpclu.js";import{a as ue,b as re,c as me,d as pe}from"./admin-DEOkRyTC.js";import{p as fe}from"./share-CqDDLJWI.js";import{_ as he}from"./Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js";const be=["aria-label"],ve={class:"modal-title"},ge=N({__name:"AppModal",props:{open:{type:Boolean},title:{},width:{}},emits:["close"],setup(h,{emit:t}){const k=h,d=t;function r(b){b.key==="Escape"&&k.open&&d("close")}return V(()=>document.addEventListener("keydown",r)),Y(()=>document.removeEventListener("keydown",r)),(b,g)=>(p(),J(ee,{to:"body"},[h.open?(p(),f("div",{key:0,class:"modal-overlay",onClick:g[0]||(g[0]=$(m=>d("close"),["self"]))},[e("div",{class:"modal",style:Z(h.width?{maxWidth:h.width}:void 0),role:"dialog","aria-modal":"true","aria-label":h.title},[e("h3",ve,a(h.title),1),X(b.$slots,"default")],12,be)])):L("",!0)]))}}),ye={class:"toolbar"},_e={class:"page-title"},xe={class:"page-sub"},ke=["placeholder"],Ce={class:"btn",type:"submit"},Se=["disabled","title"],Te={key:0,class:"loading-block"},$e={key:1,class:"card empty"},Fe={key:2,class:"table-wrap"},ze={class:"table"},De={style:{width:"36px"}},Ae=["checked"],Be={style:{"min-width":"210px"}},Ee=["checked","onChange"],we={class:"code-cell"},Ne=["title"],Ve={class:"row-actions"},Le=["onClick"],Me=["onClick"],Ue=["onClick"],Ie=["onClick"],Re=["onClick"],He={class:"field"},Pe={class:"field"},Oe={class:"field"},je=["value"],qe={class:"modal-actions"},Ke=["disabled"],Je=N({__name:"FilesView",setup(h){const{t}=te(),k=ne(),d=ie(),r=_([]),b=_(0),g=_(!1),m=A({page:1,size:10,keyword:""}),c=_(new Set);async function v(){g.value=!0;try{const n=await ue({...m});r.value=n.data,b.value=n.total,c.value=new Set}catch(n){d.error(n instanceof x?n.msg:t("admin.files.loadFailed"))}finally{g.value=!1}}function M(){m.page=1,v()}function U(n,i){m.page=n,m.size=i,v()}function I(n){const i=new Set(c.value);i.has(n)?i.delete(n):i.add(n),c.value=i}const F=()=>r.value.length>0&&r.value.every(n=>c.value.has(n.id));function R(){c.value=F()?new Set:new Set(r.value.map(n=>n.id))}async function H(n){if(window.confirm(t("admin.files.confirmDelete",{name:n.name||n.code})))try{await me(n.id),d.success(t("admin.files.deleteSuccess")),v()}catch(i){d.error(i instanceof x?i.msg:t("admin.files.deleteFailed"))}}async function P(){if(c.value.size&&window.confirm(t("admin.files.confirmBatchDelete",{count:c.value.size})))try{await re([...c.value]),d.success(t("admin.files.batchDeleteSuccess")),v()}catch(n){d.error(n instanceof x?n.msg:t("admin.files.batchDeleteFailed"))}}const y=_(!1),C=_(!1),o=A({id:0,code:"",expired_at:"",expired_count:null,original:null});function O(n){o.id=n.id,o.code=n.code,o.original=n,o.expired_at=n.expiredAt?j(n.expiredAt):"",o.expired_count=n.expiredCount,y.value=!0}function j(n){const i=new Date(n);if(Number.isNaN(i.getTime()))return"";const s=u=>`${u}`.padStart(2,"0");return`${i.getFullYear()}-${s(i.getMonth()+1)}-${s(i.getDate())}T${s(i.getHours())}:${s(i.getMinutes())}`}async function q(){const n=o.original;if(!n)return;const i={id:o.id};o.code.trim()&&o.code.trim()!==n.code&&(i.code=o.code.trim());const s=o.expired_count;if(s!==null&&s!==n.expiredCount&&(i.expired_count=s),o.expired_at){const u=new Date(o.expired_at).toISOString();u!==n.expiredAt&&(i.expired_at=u)}if(Object.keys(i).length===1){d.info(t("admin.files.nothingChanged")),y.value=!1;return}C.value=!0;try{await pe(i),d.success(t("admin.files.updateSuccess")),y.value=!1,v()}catch(u){d.error(u instanceof x?u.msg:t("admin.files.updateFailed"))}finally{C.value=!1}}async function K(n){await w(de(n.code,k.shareLinkBase))?d.success(t("admin.files.linkCopied")):d.error(t("common.copyFailed"))}async function W(n){await w(n.code)?d.success(t("admin.files.codeCopied")):d.error(t("common.copyFailed"))}async function G(n){try{const i=await fe(n.code);ce(new Blob([i],{type:"text/plain;charset=utf-8"}),n.name||`${n.code}.txt`)}catch(i){d.error(i instanceof x?i.msg:t("admin.files.fetchTextFailed"))}}function Q(n){return n.expiredCount===null||n.expiredCount<0?t("admin.files.remainingUnlimited"):t("admin.files.remainingCount",{n:n.expiredCount})}return V(v),(n,i)=>(p(),f("div",null,[e("div",ye,[e("div",null,[e("h2",_e,a(l(t)("admin.files.title")),1),e("p",xe,a(l(t)("admin.files.totalRecords",{total:b.value})),1)]),e("form",{class:"toolbar-actions",onSubmit:$(M,["prevent"])},[S(e("input",{"onUpdate:modelValue":i[0]||(i[0]=s=>m.keyword=s),class:"input",placeholder:l(t)("admin.files.searchPlaceholder"),style:{"max-width":"220px"}},null,8,ke),[[T,m.keyword]]),e("button",Ce,a(l(t)("common.search")),1),e("button",{class:"btn btn-ghost",type:"button",onClick:v},a(l(t)("common.refresh")),1),e("button",{class:"btn btn-danger-ghost",type:"button",disabled:!c.value.size,title:cLine truncated
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
import{d as W,u as Y,a as G,o as m,c as h,b as t,n as L,e as K,t as i,f as n,g as B,F as H,r as ne,h as $,E as q,_ as Z,i as ee,j as w,k as le,p as ae,l as se,w as O,m as ie,q as P,s as R,v as I,x as A,y as ue,z as re,A as ce,B as de,C as N,D as j,G as X,H as ve}from"./index-DYsKpclu.js";import{P as pe}from"./PageShell-D87JkPkG.js";import{s as me,a as he}from"./share-CqDDLJWI.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";const fe={class:"field-row"},ye={class:"field-sub"},xe=["max","value"],be={class:"field-sub"},ke=["value"],_e=["value"],Se={key:0,class:"hint",style:{color:"var(--c-warn)"}},ge={key:1,class:"hint"},Ce={key:2,class:"hint"},$e={key:3,class:"hint"},ze=W({__name:"ExpirePicker",props:{value:{},style:{},compact:{type:Boolean}},emits:["update:value","update:style"],setup(a,{emit:e}){const o=a,c=e,{t:r,te:C}=Y(),f=G(),z=$(()=>{const g=f.expireStyle.length?f.expireStyle:["day","hour","minute","forever","count"],d=q.filter(x=>g.includes(x.value)),v=g.filter(x=>!q.some(E=>E.value===x)).map(x=>({value:x,label:x}));return[...d,...v]}),S=$(()=>o.style==="forever"),y=$(()=>o.style==="count"),p=$(()=>f.maxSaveCount>0?f.maxSaveCount:9999),b=$(()=>f.maxSaveSeconds>0?f.maxSaveSeconds:0),V={day:86400,hour:3600,minute:60};function u(g){const d=b.value,v=V[g];return v?d<=0?9999:Math.max(1,Math.floor(d/v)):g==="count"?p.value:9999}const l=$(()=>{if(y.value&&f.maxSaveCount>0&&o.value>f.maxSaveCount)return r("expire.maxCountHint",{n:f.maxSaveCount});if(!S.value&&!y.value&&b.value>0){const g=u(o.style);if(o.value>g){const d=q.find(v=>v.value===o.style)?.label??o.style;return r("expire.maxSecondsHint",{value:`${g} ${d}`})}}return""}),T=$(()=>q.find(g=>g.value===o.style)?.label??o.style);function M(g,d){const v=`expireStyle.${g}`;return C(v)?r(v):d}function F(g){const d=g.target.value;c("update:style",d),d==="count"&&o.value>p.value&&c("update:value",1),d==="minute"&&o.value<1&&c("update:value",10)}return(g,d)=>(m(),h(H,null,[t("div",fe,[S.value?B("",!0):(m(),h("label",{key:0,class:K(["expire-value",{compact:a.compact}]),style:L(a.compact?"flex:0 0 110px":"")},[t("span",ye,i(n(r)("expire.value")),1),t("input",{class:"input",type:"number",min:1,max:u(a.style),value:a.value,onInput:d[0]||(d[0]=v=>c("update:value",Math.max(1,Number(v.target.value)||1)))},null,40,xe)],6)),t("label",{style:L(S.value?"flex:1":"")},[t("span",be,i(n(r)("expire.label")),1),t("select",{class:"select",value:a.style,onChange:F},[(m(!0),h(H,null,ne(z.value,v=>(m(),h("option",{key:v.value,value:v.value},i(S.value&&v.value==="forever"?n(r)("expire.foreverOption"):v.value==="count"?n(r)("expire.countOption"):M(v.value,v.label)),9,_e))),128))],40,ke)],4)]),l.value?(m(),h("p",Se,i(l.value),1)):a.style==="count"?(m(),h("p",ge,i(n(r)("expire.countHint")),1)):S.value?(m(),h("p",$e,i(n(r)("expire.foreverHint")),1)):(m(),h("p",Ce,i(n(r)("expire.timeHint",{value:a.value,unit:T.value})),1))],64))}}),Q=Z(ze,[["__scopeId","data-v-d049fcf9"]]),we={class:"card result-card"},Te={class:"result-head"},Ve={class:"badge badge-success"},Ee={key:0,class:"result-name"},Me={class:"field"},Be=["title"],De={class:"field"},Fe={class:"link-row"},Pe=["value"],Ue={class:"result-meta"},Le={key:0},He={class:"hint",style:{"margin-top":"10px"}},Oe=W({__name:"ResultCard",props:{code:{},name:{},expireValue:{},expireStyle:{}},setup(a){const e=a,{t:o}=Y(),c=ee(),r=G(),C=w(null),f=$(()=>ae(e.code,r.shareLinkBase));async function z(y){const p=y==="link"?f.value:e.code;await le(p)?(C.value=y,c.success(o(y==="link"?"result.linkCopied":"result.codeCopied")),setTimeout(()=>C.value=null,1600)):c.error(o("result.copyFailed"))}const S=$(()=>e.expireStyle==="forever"?o("result.forever"):`${e.expireValue??"-"} ${se(e.expireStyle??"")}`);return(y,p)=>(m(),h("div",we,[t("div",Te,[t("span",Ve,i(n(o)("result.badge")),1),a.name?(m(),h("span",Ee,i(a.name),1)):B("",!0)]),t("div",Me,[t("label",null,i(n(o)("result.code")),1),t("button",{class:"code-display code-copy",type:"button",title:n(o)("result.clickCopyCode"),onClick:p[0]||(p[0]=b=>z("code"))},i(a.code),9,Be)]),t("div",De,[t("label",null,i(n(o)("result.link")),1),t("div",Fe,[t("input",{class:"input input-mono",value:f.value,readonly:"",onFocus:p[1]||(p[1]=b=>b.target.select())},null,40,Pe),t("button",{class:"btn btn-ghost",type:"button",onClick:p[2]||(p[2]=b=>z("link"))},i(C.value==="link"?n(o)("common.copied"):n(o)("result.copyLink")),1)])]),t("div",Ue,[a.expireStyle?(m(),h("span",Le,i(n(o)("result.expires",{value:S.value})),1)):B("",!0)]),t("p",He,i(n(o)("result.hint")),1)]))}}),Re=Z(Oe,[["__scopeId","data-v-8bebf3d9"]]),Ie=["aria-label"],Ae={class:"dz-main"},qe={class:"dz-sub"},Ne={key:1,class:"file-chip"},je={class:"fc-name"},Xe={class:"fc-size"},Ke=["title"],We={key:2,class:"hint",style:{color:"var(--c-danger)"}},Ye=["accept"],Ge=W({__name:"FileDrop",props:{modelValue:{},maxSize:{},disabled:{type:Boolean},acceptTypes:{}},emits:["update:modelValue"],setup(a,{emit:e}){const o=a,c=e,{t:r}=Y(),C=w(null),f=w(!1),z=w(""),S=$(Line truncated
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
.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}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
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-DYsKpclu.js";import{P as A}from"./PageShell-D87JkPkG.js";import{u as D}from"./auth-B7MDTgxJ.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";import"./admin-DEOkRyTC.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};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
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-DYsKpclu.js";import{P as m}from"./PageShell-D87JkPkG.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.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
@@ -0,0 +1,267 @@
|
|||||||
|
import{d as jU,u as PU,I as NU,ax as Xw,z as TU,A as IU,j as rA,b as Lc,t as Km,f as Gm,e as MU,c as RU,g as DU,h as nA,o as aA,_ as FU}from"./index-DYsKpclu.js";import{P as LU}from"./PageShell-D87JkPkG.js";import{g as $U}from"./swagger-CqkleIqs.js";import{e as oA}from"./docsSource-BXkkn0HE.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js";var Qw={exports:{}};var iA;function BU(){return iA||(iA=1,(()=>{var eE={67526(w,N){N.byteLength=function(_){var T=m(_),I=T[0],j=T[1];return 3*(I+j)/4-j},N.toByteArray=function(_){var T,I,j=m(_),M=j[0],z=j[1],K=new v((function(te,ie,se){return 3*(ie+se)/4-se})(0,M,z)),Y=0,B=z>0?M-4:M;for(I=0;I<B;I+=4)T=h[_.charCodeAt(I)]<<18|h[_.charCodeAt(I+1)]<<12|h[_.charCodeAt(I+2)]<<6|h[_.charCodeAt(I+3)],K[Y++]=T>>16&255,K[Y++]=T>>8&255,K[Y++]=255&T;return z===2&&(T=h[_.charCodeAt(I)]<<2|h[_.charCodeAt(I+1)]>>4,K[Y++]=255&T),z===1&&(T=h[_.charCodeAt(I)]<<10|h[_.charCodeAt(I+1)]<<4|h[_.charCodeAt(I+2)]>>2,K[Y++]=T>>8&255,K[Y++]=255&T),K},N.fromByteArray=function(_){for(var T,I=_.length,j=I%3,M=[],z=16383,K=0,Y=I-j;K<Y;K+=z)M.push(g(_,K,K+z>Y?Y:K+z));return j===1?(T=_[I-1],M.push(s[T>>2]+s[T<<4&63]+"==")):j===2&&(T=(_[I-2]<<8)+_[I-1],M.push(s[T>>10]+s[T>>4&63]+s[T<<2&63]+"=")),M.join("")};for(var s=[],h=[],v=typeof Uint8Array<"u"?Uint8Array:Array,f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",y=0;y<64;++y)s[y]=f[y],h[f.charCodeAt(y)]=y;function m(S){var _=S.length;if(_%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var T=S.indexOf("=");return T===-1&&(T=_),[T,T===_?0:4-T%4]}function g(S,_,T){for(var I,j,M=[],z=_;z<T;z+=3)I=(S[z]<<16&16711680)+(S[z+1]<<8&65280)+(255&S[z+2]),M.push(s[(j=I)>>18&63]+s[j>>12&63]+s[j>>6&63]+s[63&j]);return M.join("")}h[45]=62,h[95]=63},48287(w,N,s){const h=s(67526),v=s(251),f=typeof Symbol=="function"&&typeof Symbol.for=="function"?Symbol.for("nodejs.util.inspect.custom"):null;N.Buffer=g,N.SlowBuffer=function(U){return+U!=U&&(U=0),g.alloc(+U)},N.INSPECT_MAX_BYTES=50;const y=2147483647;function m(pe){if(pe>y)throw new RangeError('The value "'+pe+'" is invalid for option "size"');const U=new Uint8Array(pe);return Object.setPrototypeOf(U,g.prototype),U}function g(pe,U,Z){if(typeof pe=="number"){if(typeof U=="string")throw new TypeError('The "string" argument must be of type string. Received type number');return T(pe)}return S(pe,U,Z)}function S(pe,U,Z){if(typeof pe=="string")return(function(Fe,St){if(typeof St=="string"&&St!==""||(St="utf8"),!g.isEncoding(St))throw new TypeError("Unknown encoding: "+St);const Bt=0|z(Fe,St);let Qe=m(Bt);const $t=Qe.write(Fe,St);return $t!==Bt&&(Qe=Qe.slice(0,$t)),Qe})(pe,U);if(ArrayBuffer.isView(pe))return(function(Fe){if(Gt(Fe,Uint8Array)){const St=new Uint8Array(Fe);return j(St.buffer,St.byteOffset,St.byteLength)}return I(Fe)})(pe);if(pe==null)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof pe);if(Gt(pe,ArrayBuffer)||pe&&Gt(pe.buffer,ArrayBuffer)||typeof SharedArrayBuffer<"u"&&(Gt(pe,SharedArrayBuffer)||pe&&Gt(pe.buffer,SharedArrayBuffer)))return j(pe,U,Z);if(typeof pe=="number")throw new TypeError('The "value" argument must not be of type number. Received type number');const ne=pe.valueOf&&pe.valueOf();if(ne!=null&&ne!==pe)return g.from(ne,U,Z);const ye=(function(Fe){if(g.isBuffer(Fe)){const St=0|M(Fe.length),Bt=m(St);return Bt.length===0||Fe.copy(Bt,0,0,St),Bt}if(Fe.length!==void 0)return typeof Fe.length!="number"||Sr(Fe.length)?m(0):I(Fe);if(Fe.type==="Buffer"&&Array.isArray(Fe.data))return I(Fe.data)})(pe);if(ye)return ye;if(typeof Symbol<"u"&&Symbol.toPrimitive!=null&&typeof pe[Symbol.toPrimitive]=="function")return g.from(pe[Symbol.toPrimitive]("string"),U,Z);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof pe)}function _(pe){if(typeof pe!="number")throw new TypeError('"size" argument must be of type number');if(pe<0)throw new RangeError('The value "'+pe+'" is invalid for option "size"')}function T(pe){return _(pe),m(pe<0?0:0|M(pe))}function I(pe){const U=pe.length<0?0:0|M(pe.length),Z=m(U);for(let ne=0;ne<U;ne+=1)Z[ne]=255&pe[ne];return Z}function j(pe,U,Z){if(U<0||pe.byteLength<U)throw new RangeError('"offset" is outside of buffer bounds');if(pe.byteLength<U+(Z||0))throw new RangeError('"length" is outside of buffer bounds');let ne;return ne=U===void 0&&Z===void 0?new Uint8Array(pe):Z===void 0?new Uint8Array(pe,U):new Uint8Array(pe,U,Z),Object.setPrototypeOf(ne,g.prototype),ne}function M(pe){if(pe>=y)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+y.toString(16)+" bytes");return 0|pe}function z(pe,U){if(g.isBuffer(pe))return pe.length;if(ArrayBuffer.isView(pe)||Gt(pe,ArrayBuffer))return pe.byteLength;if(typeof pe!="string")throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof peLine truncated
|
||||||
|
https://github.com/highlightjs/highlight.js/issues/2277`),Nn=sr,Mr=br);const nn={code:Mr,language:Nn};ds("before:highlight",nn);const fn=nn.result?nn.result:An(nn.language,nn.code,tn,Cr);return fn.code=nn.code,ds("after:highlight",fn),fn}function An(sr,br,tn,Cr){function Mr(Yt,gr){const mr=ea.case_insensitive?gr[0].toLowerCase():gr[0];return Object.prototype.hasOwnProperty.call(Yt.keywords,mr)&&Yt.keywords[mr]}function Nn(){Rr.subLanguage!=null?(function(){if(xn==="")return;let gr=null;if(typeof Rr.subLanguage=="string"){if(!ye[Rr.subLanguage])return void Nr.addText(xn);gr=An(Rr.subLanguage,xn,!0,ko[Rr.subLanguage]),ko[Rr.subLanguage]=gr.top}else gr=Wr(xn,Rr.subLanguage.length?Rr.subLanguage:null);Rr.relevance>0&&(fi+=gr.relevance),Nr.addSublanguage(gr.emitter,gr.language)})():(function(){if(!Rr.keywords)return void Nr.addText(xn);let gr=0;Rr.keywordPatternRe.lastIndex=0;let mr=Rr.keywordPatternRe.exec(xn),Vr="";for(;mr;){Vr+=xn.substring(gr,mr.index);const Gr=Mr(Rr,mr);if(Gr){const[sa,Sa]=Gr;if(Nr.addText(Vr),Vr="",fi+=Sa,sa.startsWith("_"))Vr+=mr[0];else{const Ja=ea.classNameAliases[sa]||sa;Nr.addKeyword(mr[0],Ja)}}else Vr+=mr[0];gr=Rr.keywordPatternRe.lastIndex,mr=Rr.keywordPatternRe.exec(xn)}Vr+=xn.substr(gr),Nr.addText(Vr)})(),xn=""}function nn(Yt){return Yt.className&&Nr.openNode(ea.classNameAliases[Yt.className]||Yt.className),Rr=Object.create(Yt,{parent:{value:Rr}}),Rr}function fn(Yt,gr,mr){let Vr=(function(sa,Sa){const Ja=sa&&sa.exec(Sa);return Ja&&Ja.index===0})(Yt.endRe,mr);if(Vr){if(Yt["on:end"]){const Gr=new v(Yt);Yt["on:end"](gr,Gr),Gr.isMatchIgnored&&(Vr=!1)}if(Vr){for(;Yt.endsParent&&Yt.parent;)Yt=Yt.parent;return Yt}}if(Yt.endsWithParent)return fn(Yt.parent,gr,mr)}function Ln(Yt){return Rr.matcher.regexIndex===0?(xn+=Yt[0],1):(ms=!0,0)}function Hr(Yt){const gr=Yt[0],mr=Yt.rule,Vr=new v(mr),Gr=[mr.__beforeBegin,mr["on:begin"]];for(const sa of Gr)if(sa&&(sa(Yt,Vr),Vr.isMatchIgnored))return Ln(gr);return mr&&mr.endSameAsBegin&&(mr.endRe=(function(Sa){return new RegExp(Sa.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")})(gr)),mr.skip?xn+=gr:(mr.excludeBegin&&(xn+=gr),Nn(),mr.returnBegin||mr.excludeBegin||(xn=gr)),nn(mr),mr.returnBegin?0:gr.length}function ia(Yt){const gr=Yt[0],mr=br.substr(Yt.index),Vr=fn(Rr,Yt,mr);if(!Vr)return U;const Gr=Rr;Gr.skip?xn+=gr:(Gr.returnEnd||Gr.excludeEnd||(xn+=gr),Nn(),Gr.excludeEnd&&(xn=gr));do Rr.className&&Nr.closeNode(),Rr.skip||Rr.subLanguage||(fi+=Rr.relevance),Rr=Rr.parent;while(Rr!==Vr.parent);return Vr.starts&&(Vr.endSameAsBegin&&(Vr.starts.endRe=Vr.endRe),nn(Vr.starts)),Gr.returnEnd?0:gr.length}let za={};function fs(Yt,gr){const mr=gr&&gr[0];if(xn+=Yt,mr==null)return Nn(),0;if(za.type==="begin"&&gr.type==="end"&&za.index===gr.index&&mr===""){if(xn+=br.slice(gr.index,gr.index+1),!St){const Vr=new Error("0 width match regex");throw Vr.languageName=sr,Vr.badRule=za.rule,Vr}return 1}if(za=gr,gr.type==="begin")return Hr(gr);if(gr.type==="illegal"&&!tn){const Vr=new Error('Illegal lexeme "'+mr+'" for mode "'+(Rr.className||"<unnamed>")+'"');throw Vr.mode=Rr,Vr}if(gr.type==="end"){const Vr=ia(gr);if(Vr!==U)return Vr}if(gr.type==="illegal"&&mr==="")return 1;if(Jo>1e5&&Jo>3*gr.index)throw new Error("potential infinite loop, way more iterations than matches");return xn+=mr,mr.length}const ea=Fn(sr);if(!ea)throw Sr(Qe.replace("{}",sr)),new Error('Unknown language: "'+sr+'"');const di=we(ea,{plugins:Fe});let Va="",Rr=Cr||di;const ko={},Nr=new Pt.__emitter(Pt);(function(){const gr=[];for(let mr=Rr;mr!==ea;mr=mr.parent)mr.className&&gr.unshift(mr.className);gr.forEach((mr=>Nr.openNode(mr)))})();let xn="",fi=0,ta=0,Jo=0,ms=!1;try{for(Rr.matcher.considerAll();;){Jo++,ms?ms=!1:Rr.matcher.considerAll(),Rr.matcher.lastIndex=ta;const Yt=Rr.matcher.exec(br);if(!Yt)break;const gr=fs(br.substring(ta,Yt.index),Yt);ta=Yt.index+gr}return fs(br.substr(ta)),Nr.closeAllNodes(),Nr.finalize(),Va=Nr.toHTML(),{relevance:Math.floor(fi),value:Va,language:sr,illegal:!1,emitter:Nr,top:Rr}}catch(Yt){if(Yt.message&&Yt.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:Yt.message,context:br.slice(ta-100,ta+100),mode:Yt.mode},sofar:Va,relevance:0,value:Zt(br),emitter:Nr};if(St)return{illegal:!1,relevance:0,value:Zt(br),emitter:Nr,language:sr,top:Rr,errorRaised:Yt};throw Yt}}function Wr(sr,br){br=br||Pt.languages||Object.keys(ye);const tn=(function(Hr){const ia={relevance:0,emitter:new Pt.__emitter(Pt),value:Zt(Hr),illegal:!1,top:$t};return ia.emitter.addText(Hr),ia})(sr),Cr=br.filter(Fn).filter(Vo).map((Ln=>An(Ln,sr,!1)));Cr.unshift(tn);const Mr=Cr.sort(((Ln,Hr)=>{if(Ln.relevance!==Hr.relevance)return Hr.relevance-Ln.relevance;if(Ln.language&&Hr.language){if(Fn(Ln.language).supersetOf===Hr.language)return 1;if(Fn(Hr.language).supersetOf===Ln.language)return-1}return 0})),[Nn,nn]=Mr,fn=Nn;return fn.second_best=nn,fn}const Jn={"before:highlightElement":({el:sr})=>{Pt.useBR&&(sr.innerHTML=sr.innerHTML.replace(/\n/g,"").replace(/<br[ /]*>/g,`
|
||||||
|
`))},"after:highlightElement":({result:sr})=>{Pt.useBR&&(sr.value=sr.value.replace(/\n/g,"<br>"))}},Ea=/^(<[^>]+>|\t)+/gm,Zn={"after:highlightElement":({result:sr})=>{Pt.tabReplace&&(sr.value=sr.value.replace(Ea,(br=>br.replace(/\t/g,Pt.tabReplace))))}};function un(sr){let br=null;const tn=(function(nn){let fn=nn.className+" ";fn+=nn.parentNode?nn.parentNode.className:"";const Ln=Pt.languageDetectRe.exec(fn);if(Ln){const Hr=Fn(Ln[1]);return Hr||(kt(Qe.replace("{}",Ln[1])),kt("Falling back to no-highlight mode for this block.",nn)),Hr?Ln[1]:"no-highlight"}return fn.split(/\s+/).find((Hr=>rn(Hr)||Fn(Hr)))})(sr);if(rn(tn))return;ds("before:highlightElement",{el:sr,language:tn}),br=sr;const Cr=br.textContent,Mr=tn?kr(Cr,{language:tn,ignoreIllegals:!0}):Wr(Cr);ds("after:highlightElement",{el:sr,result:Mr,text:Cr}),sr.innerHTML=Mr.value,(function(nn,fn,Ln){const Hr=fn?Ee[fn]:Ln;nn.classList.add("hljs"),Hr&&nn.classList.add(Hr)})(sr,tn,Mr.language),sr.result={language:Mr.language,re:Mr.relevance,relavance:Mr.relevance},Mr.second_best&&(sr.second_best={language:Mr.second_best.language,re:Mr.second_best.relevance,relavance:Mr.second_best.relevance})}const ti=()=>{ti.called||(ti.called=!0,yt("10.6.0","initHighlighting() is deprecated. Use highlightAll() instead."),document.querySelectorAll("pre code").forEach(un))};let Oo=!1;function Li(){if(document.readyState==="loading")return void(Oo=!0);document.querySelectorAll("pre code").forEach(un)}function Fn(sr){return sr=(sr||"").toLowerCase(),ye[sr]||ye[Ee[sr]]}function zo(sr,{languageName:br}){typeof sr=="string"&&(sr=[sr]),sr.forEach((tn=>{Ee[tn.toLowerCase()]=br}))}function Vo(sr){const br=Fn(sr);return br&&!br.disableAutodetect}function ds(sr,br){const tn=sr;Fe.forEach((function(Cr){Cr[tn]&&Cr[tn](br)}))}typeof window<"u"&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(function(){Oo&&Li()}),!1),Object.assign(ne,{highlight:kr,highlightAuto:Wr,highlightAll:Li,fixMarkup:function(br){return yt("10.2.0","fixMarkup will be removed entirely in v11.0"),yt("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),(function(Cr){return Pt.tabReplace||Pt.useBR?Cr.replace(Bt,(Mr=>Mr===`
|
||||||
|
`?Pt.useBR?"<br>":Mr:Pt.tabReplace?Mr.replace(/\t/g,Pt.tabReplace):Mr)):Cr})(br)},highlightElement:un,highlightBlock:function(br){return yt("10.7.0","highlightBlock will be removed entirely in v12.0"),yt("10.7.0","Please use highlightElement now."),un(br)},configure:function(br){br.useBR&&(yt("10.3.0","'useBR' will be removed entirely in v11.0"),yt("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")),Pt=pe(Pt,br)},initHighlighting:ti,initHighlightingOnLoad:function(){yt("10.6.0","initHighlightingOnLoad() is deprecated. Use highlightAll() instead."),Oo=!0},registerLanguage:function(br,tn){let Cr=null;try{Cr=tn(ne)}catch(Mr){if(Sr("Language definition for '{}' could not be registered.".replace("{}",br)),!St)throw Mr;Sr(Mr),Cr=$t}Cr.name||(Cr.name=br),ye[br]=Cr,Cr.rawDefinition=tn.bind(null,ne),Cr.aliases&&zo(Cr.aliases,{languageName:br})},unregisterLanguage:function(br){delete ye[br];for(const tn of Object.keys(Ee))Ee[tn]===br&&delete Ee[tn]},listLanguages:function(){return Object.keys(ye)},getLanguage:Fn,registerAliases:zo,requireLanguage:function(br){yt("10.4.0","requireLanguage will be removed entirely in v11."),yt("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844");const tn=Fn(br);if(tn)return tn;throw new Error("The '{}' language is required, but not loaded.".replace("{}",br))},autoDetection:Vo,inherit:pe,addPlugin:function(br){(function(Cr){Cr["before:highlightBlock"]&&!Cr["before:highlightElement"]&&(Cr["before:highlightElement"]=Mr=>{Cr["before:highlightBlock"](Object.assign({block:Mr.el},Mr))}),Cr["after:highlightBlock"]&&!Cr["after:highlightElement"]&&(Cr["after:highlightElement"]=Mr=>{Cr["after:highlightBlock"](Object.assign({block:Mr.el},Mr))})})(br),Fe.push(br)},vuePlugin:mt(ne).VuePlugin}),ne.debugMode=function(){St=!1},ne.safeMode=function(){St=!0},ne.versionString="10.7.3";for(const sr in at)typeof at[sr]=="object"&&s(at[sr]);return Object.assign(ne,at),ne.addPlugin(Jn),ne.addPlugin(ur),ne.addPlugin(Zn),ne})({});w.exports=Z},35344(w){function N(...s){return s.map((h=>(function(f){return f?typeof f=="string"?f:f.source:null})(h))).join("")}w.exports=function(h){const v={},f={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[v]}]};Object.assign(v,{className:"variable",variants:[{begin:N(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},f]});const y={className:"subst",begin:/\$\(/,end:/\)/,contains:[h.BACKSLASH_ESCAPE]},m={begin:/<<-?\s*(?=\w+)/,starts:{contains:[h.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},g={className:"string",begin:/"/,end:/"/,contains:[h.BACKSLASH_ESCAPE,v,y]};y.contains.push(g);const S={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},h.NUMBER_MODE,v]},_=h.SHEBANG({binary:`(${["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"].join("|")})`,relevance:10}),T={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[h.inherit(h.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp"},contains:[_,h.SHEBANG(),T,S,h.HASH_COMMENT_MODE,m,g,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},v]}}},73402(w){function N(...s){return s.map((h=>(function(f){return f?typeof f=="string"?f:f.source:null})(h))).join("")}w.exports=function(h){const v="HTTP/(2|1\\.[01])",f={className:"attribute",begin:N("^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"),starts:{contains:[{className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]}},y=[f,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+v+" \\d{3})",end:/$/,contains:[{className:"meta",begin:v},{className:"number",begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:y}},{begin:"(?=^[A-Z]+ (.*?) "+v+"$)",end:/$/,contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:v},{className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:y}},h.inherit(f,{relevance:0})]}}},95089(w){const N="[A-Za-z$_][0-9A-Za-z$_]*",s=["as","in","of",
Line truncated
|
||||||
|
/* [wrapped with `+v+`] */
|
||||||
|
`)}},45891(w,N,s){var h=s(51873),v=s(72428),f=s(56449),y=h?h.isConcatSpreadable:void 0;w.exports=function(g){return f(g)||v(g)||!!(y&&g&&g[y])}},30361(w){var N=/^(?:0|[1-9]\d*)$/;w.exports=function(h,v){var f=typeof h;return!!(v=v??9007199254740991)&&(f=="number"||f!="symbol"&&N.test(h))&&h>-1&&h%1==0&&h<v}},36800(w,N,s){var h=s(75288),v=s(64894),f=s(30361),y=s(23805);w.exports=function(g,S,_){if(!y(_))return!1;var T=typeof S;return!!(T=="number"?v(_)&&f(S,_.length):T=="string"&&S in _)&&h(_[S],g)}},28586(w,N,s){var h=s(56449),v=s(44394),f=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,y=/^\w*$/;w.exports=function(g,S){if(h(g))return!1;var _=typeof g;return!(_!="number"&&_!="symbol"&&_!="boolean"&&g!=null&&!v(g))||y.test(g)||!f.test(g)||S!=null&&g in Object(S)}},74218(w){w.exports=function(s){var h=typeof s;return h=="string"||h=="number"||h=="symbol"||h=="boolean"?s!=="__proto__":s===null}},85087(w,N,s){var h=s(30980),v=s(37381),f=s(62284),y=s(53758);w.exports=function(g){var S=f(g),_=y[S];if(typeof _!="function"||!(S in h.prototype))return!1;if(g===_)return!0;var T=v(_);return!!T&&g===T[0]}},87296(w,N,s){var h,v=s(55481),f=(h=/[^.]+$/.exec(v&&v.keys&&v.keys.IE_PROTO||""))?"Symbol(src)_1."+h:"";w.exports=function(m){return!!f&&f in m}},55527(w){var N=Object.prototype;w.exports=function(h){var v=h&&h.constructor;return h===(typeof v=="function"&&v.prototype||N)}},30756(w,N,s){var h=s(23805);w.exports=function(f){return f==f&&!h(f)}},63702(w){w.exports=function(){this.__data__=[],this.size=0}},70080(w,N,s){var h=s(26025),v=Array.prototype.splice;w.exports=function(y){var m=this.__data__,g=h(m,y);return!(g<0)&&(g==m.length-1?m.pop():v.call(m,g,1),--this.size,!0)}},24739(w,N,s){var h=s(26025);w.exports=function(f){var y=this.__data__,m=h(y,f);return m<0?void 0:y[m][1]}},48655(w,N,s){var h=s(26025);w.exports=function(f){return h(this.__data__,f)>-1}},31175(w,N,s){var h=s(26025);w.exports=function(f,y){var m=this.__data__,g=h(m,f);return g<0?(++this.size,m.push([f,y])):m[g][1]=y,this}},63040(w,N,s){var h=s(21549),v=s(80079),f=s(68223);w.exports=function(){this.size=0,this.__data__={hash:new h,map:new(f||v),string:new h}}},17670(w,N,s){var h=s(12651);w.exports=function(f){var y=h(this,f).delete(f);return this.size-=y?1:0,y}},90289(w,N,s){var h=s(12651);w.exports=function(f){return h(this,f).get(f)}},4509(w,N,s){var h=s(12651);w.exports=function(f){return h(this,f).has(f)}},72949(w,N,s){var h=s(12651);w.exports=function(f,y){var m=h(this,f),g=m.size;return m.set(f,y),this.size+=m.size==g?0:1,this}},20317(w){w.exports=function(s){var h=-1,v=Array(s.size);return s.forEach((function(f,y){v[++h]=[y,f]})),v}},67197(w){w.exports=function(s,h){return function(v){return v!=null&&v[s]===h&&(h!==void 0||s in Object(v))}}},62224(w,N,s){var h=s(50104);w.exports=function(f){var y=h(f,(function(g){return m.size===500&&m.clear(),g})),m=y.cache;return y}},3209(w,N,s){var h=s(91596),v=s(53320),f=s(36306),y="__lodash_placeholder__",m=128,g=Math.min;w.exports=function(_,T){var I=_[1],j=T[1],M=I|j,z=M<131,K=j==m&&I==8||j==m&&I==256&&_[7].length<=T[8]||j==384&&T[7].length<=T[8]&&I==8;if(!z&&!K)return _;1&j&&(_[2]=T[2],M|=1&I?0:4);var Y=T[3];if(Y){var B=_[3];_[3]=B?h(B,Y,T[4]):Y,_[4]=B?f(_[3],y):T[4]}return(Y=T[5])&&(B=_[5],_[5]=B?v(B,Y,T[6]):Y,_[6]=B?f(_[5],y):T[6]),(Y=T[7])&&(_[7]=Y),j&m&&(_[8]=_[8]==null?T[8]:g(_[8],T[8])),_[9]==null&&(_[9]=T[9]),_[0]=T[0],_[1]=M,_}},48152(w,N,s){var h=s(28303),v=h&&new h;w.exports=v},81042(w,N,s){var h=s(56110)(Object,"create");w.exports=h},3650(w,N,s){var h=s(74335)(Object.keys,Object);w.exports=h},90181(w){w.exports=function(s){var h=[];if(s!=null)for(var v in Object(s))h.push(v);return h}},86009(w,N,s){w=s.nmd(w);var h=s(34840),v=N&&!N.nodeType&&N,f=v&&w&&!w.nodeType&&w,y=f&&f.exports===v&&h.process,m=(function(){try{var g=f&&f.require&&f.require("util").types;return g||y&&y.binding&&y.binding("util")}catch{}})();w.exports=m},59350(w){var N=Object.prototype.toString;w.exports=function(h){return N.call(h)}},74335(w){w.exports=function(s,h){return function(v){return s(h(v))}}},56757(w,N,s){var h=s(91033),v=Math.max;w.exports=function(y,m,g){return m=v(m===void 0?y.length-1:m,0),function(){for(var S=arguments,_=-1,T=v(S.length-m,0),I=Array(T);++_<T;)I[_]=S[m+_];_=-1;for(var j=Array(m+1);++_<m;)j[_]=S[_];return j[m]=g(I),h(y,this,j)}}},68969(w,N,s){var h=s(47422),v=s(25160);w.exports=function(y,m){return m.length<2?y:h(y,v(m,0,-1))}},84629(w){w.exports={}},68294(w,N,s){var h=s(23007),v=s(30361),f=Math.min;w.exports=function(m,g){for(var S=m.length,_=f(g.length,S),T=h(m);_--;){var I=g[_];m[_]=v(I,S)?T[I]:void 0}return m}},36306(w){var N="__lodash_placeholder__";w.exports=function(h,v){for(var f=-1,y=h.length,m=0,g=[];++f<y;){var S=h[f];S!==v&&S!==N||(h[f]=N,g[m++]=f)}return g}},9325(w,N,s){var h=s(34840),v=typeof self=="object"&&self&&self.Object===Object&&self,f=h||v||Function("return this")();w.exports=f},14974(w){w.exports=function(s,h){if((h!=="constructor"||typeof s[h]!="function")&&h!="__p
Line truncated
|
||||||
|
Use Chrome, Firefox or Internet Explorer 11`)}},25264(w,N,s){Object.defineProperty(N,"__esModule",{value:!0}),N.CopyToClipboard=void 0;var h=y(s(17965)),v=y(s(96540)),f=["text","onCopy","options","children"];function y(B){return B&&B.__esModule?B:{default:B}}function m(B){return m=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(X){return typeof X}:function(X){return X&&typeof Symbol=="function"&&X.constructor===Symbol&&X!==Symbol.prototype?"symbol":typeof X},m(B)}function g(B,X){var te=Object.keys(B);if(Object.getOwnPropertySymbols){var ie=Object.getOwnPropertySymbols(B);X&&(ie=ie.filter((function(se){return Object.getOwnPropertyDescriptor(B,se).enumerable}))),te.push.apply(te,ie)}return te}function S(B){for(var X=1;X<arguments.length;X++){var te=arguments[X]!=null?arguments[X]:{};X%2?g(Object(te),!0).forEach((function(ie){z(B,ie,te[ie])})):Object.getOwnPropertyDescriptors?Object.defineProperties(B,Object.getOwnPropertyDescriptors(te)):g(Object(te)).forEach((function(ie){Object.defineProperty(B,ie,Object.getOwnPropertyDescriptor(te,ie))}))}return B}function _(B,X){for(var te=0;te<X.length;te++){var ie=X[te];ie.enumerable=ie.enumerable||!1,ie.configurable=!0,"value"in ie&&(ie.writable=!0),Object.defineProperty(B,K(ie.key),ie)}}function T(B,X,te){return X=j(X),(function(se,Te){if(Te&&(m(Te)=="object"||typeof Te=="function"))return Te;if(Te!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return(function(ge){if(ge===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return ge})(se)})(B,I()?Reflect.construct(X,te||[],j(B).constructor):X.apply(B,te))}function I(){try{var B=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch{}return(I=function(){return!!B})()}function j(B){return j=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(X){return X.__proto__||Object.getPrototypeOf(X)},j(B)}function M(B,X){return M=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(te,ie){return te.__proto__=ie,te},M(B,X)}function z(B,X,te){return(X=K(X))in B?Object.defineProperty(B,X,{value:te,enumerable:!0,configurable:!0,writable:!0}):B[X]=te,B}function K(B){var X=(function(ie,se){if(m(ie)!="object"||!ie)return ie;var Te=ie[Symbol.toPrimitive];if(Te!==void 0){var he=Te.call(ie,se);if(m(he)!="object")return he;throw new TypeError("@@toPrimitive must return a primitive value.")}return(se==="string"?String:Number)(ie)})(B,"string");return m(X)=="symbol"?X:X+""}var Y=N.CopyToClipboard=(function(B){function X(){var te;(function(ge,ke){if(!(ge instanceof ke))throw new TypeError("Cannot call a class as a function")})(this,X);for(var ie=arguments.length,se=new Array(ie),Te=0;Te<ie;Te++)se[Te]=arguments[Te];return z(te=T(this,X,[].concat(se)),"onClick",(function(he){var ge=te.props,ke=ge.text,Ve=ge.onCopy,He=ge.children,qe=ge.options,nt=v.default.Children.only(He),gt=(0,h.default)(ke,qe);Ve&&Ve(ke,gt),nt!=null&&nt.props&&typeof nt.props.onClick=="function"&&nt.props.onClick(he)})),te}return(function(ie,se){if(typeof se!="function"&&se!==null)throw new TypeError("Super expression must either be null or a function");ie.prototype=Object.create(se&&se.prototype,{constructor:{value:ie,writable:!0,configurable:!0}}),Object.defineProperty(ie,"prototype",{writable:!1}),se&&M(ie,se)})(X,B),(function(ie,se,Te){return se&&_(ie.prototype,se),Object.defineProperty(ie,"prototype",{writable:!1}),ie})(X,[{key:"render",value:function(){var ie=this.props,se=(ie.text,ie.onCopy,ie.options,ie.children),Te=(function(ke,Ve){if(ke==null)return{};var He,qe,nt=(function(u,at){if(u==null)return{};var Xe={};for(var Se in u)if({}.hasOwnProperty.call(u,Se)){if(at.indexOf(Se)!==-1)continue;Xe[Se]=u[Se]}return Xe})(ke,Ve);if(Object.getOwnPropertySymbols){var gt=Object.getOwnPropertySymbols(ke);for(qe=0;qe<gt.length;qe++)He=gt[qe],Ve.indexOf(He)===-1&&{}.propertyIsEnumerable.call(ke,He)&&(nt[He]=ke[He])}return nt})(ie,f),he=v.default.Children.only(se);return v.default.cloneElement(he,S(S({},Te),{},{onClick:this.onClick}))}}])})(v.default.PureComponent);z(Y,"defaultProps",{onCopy:void 0,options:void 0})},59399(w,N,s){var h=s(25264).CopyToClipboard;h.CopyToClipboard=h,w.exports=h},81214(w,N,s){function h(B){return h=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(X){return typeof X}:function(X){return X&&typeof Symbol=="function"&&X.constructor===Symbol&&X!==Symbol.prototype?"symbol":typeof X},h(B)}Object.defineProperty(N,"__esModule",{value:!0}),N.DebounceInput=void 0;var v=m(s(96540)),f=m(s(20181)),y=["element","onChange","value","minLength","debounceTimeout","forceNotifyByEnter","forceNotifyOnBlur","onKeyDown","onBlur","inputRef"];function m(B){return B&&B.__esModule?B:{default:B}}function g(B,X){if(B==null)return{};var te,ie,se=(function(ge,ke){if(ge==null)return{};var Ve,He,qe={},nt=Object.keys(ge);for(He=0;He<nt.length;He++)Ve=nt[He],ke.indexOf(Ve)>=0||(qe[Ve]=ge[Ve]);return qe})(B,X);if(Object.get
Line truncated
|
||||||
|
`+Se+i}var ft=!1;function Nt(i,l){if(!i||ft)return"";ft=!0;var b=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(l)if(l=function(){throw Error()},Object.defineProperty(l.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(l,[])}catch(Ue){var C=Ue}Reflect.construct(i,[],l)}else{try{l.call()}catch(Ue){C=Ue}i.call(l.prototype)}else{try{throw Error()}catch(Ue){C=Ue}i()}}catch(Ue){if(Ue&&C&&typeof Ue.stack=="string"){for(var $=Ue.stack.split(`
|
||||||
|
`),ee=C.stack.split(`
|
||||||
|
`),ue=$.length-1,Ce=ee.length-1;1<=ue&&0<=Ce&&$[ue]!==ee[Ce];)Ce--;for(;1<=ue&&0<=Ce;ue--,Ce--)if($[ue]!==ee[Ce]){if(ue!==1||Ce!==1)do if(ue--,0>--Ce||$[ue]!==ee[Ce]){var Me=`
|
||||||
|
`+$[ue].replace(" at new "," at ");return i.displayName&&Me.includes("<anonymous>")&&(Me=Me.replace("<anonymous>",i.displayName)),Me}while(1<=ue&&0<=Ce);break}}}finally{ft=!1,Error.prepareStackTrace=b}return(i=i?i.displayName||i.name:"")?Ke(i):""}function Wt(i){switch(i.tag){case 5:return Ke(i.type);case 16:return Ke("Lazy");case 13:return Ke("Suspense");case 19:return Ke("SuspenseList");case 0:case 2:case 15:return i=Nt(i.type,!1);case 11:return i=Nt(i.type.render,!1);case 1:return i=Nt(i.type,!0);default:return""}}function H(i){if(i==null)return null;if(typeof i=="function")return i.displayName||i.name||null;if(typeof i=="string")return i;switch(i){case Te:return"Fragment";case se:return"Portal";case ge:return"Profiler";case he:return"StrictMode";case qe:return"Suspense";case nt:return"SuspenseList"}if(typeof i=="object")switch(i.$$typeof){case Ve:return(i.displayName||"Context")+".Consumer";case ke:return(i._context.displayName||"Context")+".Provider";case He:var l=i.render;return(i=i.displayName)||(i=(i=l.displayName||l.name||"")!==""?"ForwardRef("+i+")":"ForwardRef"),i;case gt:return(l=i.displayName||null)!==null?l:H(i.type)||"Memo";case Re:l=i._payload,i=i._init;try{return H(i(l))}catch{}}return null}function we(i){var l=i.type;switch(i.tag){case 24:return"Cache";case 9:return(l.displayName||"Context")+".Consumer";case 10:return(l._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return i=(i=l.render).displayName||i.name||"",l.displayName||(i!==""?"ForwardRef("+i+")":"ForwardRef");case 7:return"Fragment";case 5:return l;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return H(l);case 8:return l===he?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof l=="function")return l.displayName||l.name||null;if(typeof l=="string")return l}return null}function ct(i){switch(typeof i){case"boolean":case"number":case"string":case"undefined":case"object":return i;default:return""}}function mt(i){var l=i.type;return(i=i.nodeName)&&i.toLowerCase()==="input"&&(l==="checkbox"||l==="radio")}function ur(i){i._valueTracker||(i._valueTracker=(function(b){var C=mt(b)?"checked":"value",$=Object.getOwnPropertyDescriptor(b.constructor.prototype,C),ee=""+b[C];if(!b.hasOwnProperty(C)&&$!==void 0&&typeof $.get=="function"&&typeof $.set=="function"){var ue=$.get,Ce=$.set;return Object.defineProperty(b,C,{configurable:!0,get:function(){return ue.call(this)},set:function(Me){ee=""+Me,Ce.call(this,Me)}}),Object.defineProperty(b,C,{enumerable:$.enumerable}),{getValue:function(){return ee},setValue:function(Me){ee=""+Me},stopTracking:function(){b._valueTracker=null,delete b[C]}}}})(i))}function ar(i){if(!i)return!1;var l=i._valueTracker;if(!l)return!0;var b=l.getValue(),C="";return i&&(C=mt(i)?i.checked?"true":"false":i.value),(i=C)!==b&&(l.setValue(i),!0)}function Tt(i){if((i=i||(typeof document<"u"?document:void 0))===void 0)return null;try{return i.activeElement||i.body}catch{return i.body}}function Gt(i,l){var b=l.checked;return De({},l,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:b??i._wrapperState.initialChecked})}function Sr(i,l){var b=l.defaultValue==null?"":l.defaultValue,C=l.checked!=null?l.checked:l.defaultChecked;b=ct(l.value!=null?l.value:b),i._wrapperState={initialChecked:C,initialValue:b,controlled:l.type==="checkbox"||l.type==="radio"?l.checked!=null:l.value!=null}}function kt(i,l){(l=l.checked)!=null&&X(i,"checked",l,!1)}function yt(i,l){kt(i,l);var b=ct(l.value),C=l.type;if(b!=null)C==="number"?(b===0&&i.value===""||i.value!=b)&&(i.value=""+b):i.value!==""+b&&(i.value=""+b);else if(C==="submit"||C==="reset")return void i.removeAttribute("value");l.hasOwnProperty("value")?pe(i,l.type,b):l.hasOwnProperty("defaultValue")&&pe(i,l.type,ct(l.defaultValue)),l.checked==null&&l.defaultChecked!=null&&(i.defaultChecked=!!l.defaultChecked)}function Zt(i,l,b){if(l.hasOwnProperty("value")||l.hasOwnProperty("defaultValue")){var C=l.type;if(!(C!=="submit"&&C!=="reset"||l.value!==void 0&&l.value!==null))return;l=""+i._wrapperState.initialValue,b||l===i.value||(i.value=l),i.defaultValue=l}(b=i.name)!==""&&(i.name=""),i.defaultChecked=!!i._wrapperState.initialChecked,b!==""&&(i.name=b)}function pe(i,l,b){l==="number"&&Tt(i.ownerDocument)===i||(b==null?i.defaultValue=""+i._wrapperState.initialValue:i.defaultValue!==""+b&&(i.defaultValue=""+b))}var U=Array.isArray;function Z(i,l,b,C){if(i=i.options,l){l={};for(var $=0;$<b.length;$++)l["$"+b[$]]=!0;for(b=0;b<i.length;b++)$=l.hasOwnProperty("$"+i[b].value),i[b].selected!==$&&(i[b].selected=$),$&&C&&(i[b].defaultSelected=!0)}else{for(b=""+ct(b),l=null,$=0;$<i.length;$++){if(i[$].value===b)return i[$].selected=!0,void(C&&(i[$].defaultSelected=!0));l!==null||i[$].disabled||(l=i[$])}l!==null&&(l.selected=!0)}}function ne(i,l){if(l.dangerous
Line truncated
|
||||||
|
`).replace(Fa,"")}function Tn(i,l,b){if(l=ai(l),ai(i)!==l&&b)throw Error(f(425))}function Hn(){}var na=null,aa=null;function Bn(i,l){return i==="textarea"||i==="noscript"||typeof l.children=="string"||typeof l.children=="number"||typeof l.dangerouslySetInnerHTML=="object"&&l.dangerouslySetInnerHTML!==null&&l.dangerouslySetInnerHTML.__html!=null}var Ga=typeof setTimeout=="function"?setTimeout:void 0,Ya=typeof clearTimeout=="function"?clearTimeout:void 0,po=typeof Promise=="function"?Promise:void 0,n0=typeof queueMicrotask=="function"?queueMicrotask:po!==void 0?function(i){return po.resolve(null).then(i).catch(a0)}:Ga;function a0(i){setTimeout((function(){throw i}))}function Pd(i,l){var b=l,C=0;do{var $=b.nextSibling;if(i.removeChild(b),$&&$.nodeType===8)if((b=$.data)==="/$"){if(C===0)return i.removeChild($),void Po(l);C--}else b!=="$"&&b!=="$?"&&b!=="$!"||C++;b=$}while(b);Po(l)}function bl(i){for(;i!=null;i=i.nextSibling){var l=i.nodeType;if(l===1||l===3)break;if(l===8){if((l=i.data)==="$"||l==="$!"||l==="$?")break;if(l==="/$")return null}}return i}function Nd(i){i=i.previousSibling;for(var l=0;i;){if(i.nodeType===8){var b=i.data;if(b==="$"||b==="$!"||b==="$?"){if(l===0)return i;l--}else b==="/$"&&l++}i=i.previousSibling}return null}var wl=Math.random().toString(36).slice(2),Ki="__reactFiber$"+wl,lp="__reactProps$"+wl,Gi="__reactContainer$"+wl,ih="__reactEvents$"+wl,o0="__reactListeners$"+wl,i0="__reactHandles$"+wl;function El(i){var l=i[Ki];if(l)return l;for(var b=i.parentNode;b;){if(l=b[Gi]||b[Ki]){if(b=l.alternate,l.child!==null||b!==null&&b.child!==null)for(i=Nd(i);i!==null;){if(b=i[Ki])return b;i=Nd(i)}return l}b=(i=b).parentNode}return null}function cp(i){return!(i=i[Ki]||i[Gi])||i.tag!==5&&i.tag!==6&&i.tag!==13&&i.tag!==3?null:i}function ru(i){if(i.tag===5||i.tag===6)return i.stateNode;throw Error(f(33))}function up(i){return i[lp]||null}var Td=[],nu=-1;function Sl(i){return{current:i}}function qn(i){0>nu||(i.current=Td[nu],Td[nu]=null,nu--)}function Cn(i,l){nu++,Td[nu]=i.current,i.current=l}var xl={},Xa=Sl(xl),Oa=Sl(!1),ks=xl;function hc(i,l){var b=i.type.contextTypes;if(!b)return xl;var C=i.stateNode;if(C&&C.__reactInternalMemoizedUnmaskedChildContext===l)return C.__reactInternalMemoizedMaskedChildContext;var $,ee={};for($ in b)ee[$]=l[$];return C&&((i=i.stateNode).__reactInternalMemoizedUnmaskedChildContext=l,i.__reactInternalMemoizedMaskedChildContext=ee),ee}function Ko(i){return(i=i.childContextTypes)!=null}function sh(){qn(Oa),qn(Xa)}function Xm(i,l,b){if(Xa.current!==xl)throw Error(f(168));Cn(Xa,l),Cn(Oa,b)}function Id(i,l,b){var C=i.stateNode;if(l=l.childContextTypes,typeof C.getChildContext!="function")return b;for(var $ in C=C.getChildContext())if(!($ in l))throw Error(f(108,we(i)||"Unknown",$));return De({},b,C)}function lh(i){return i=(i=i.stateNode)&&i.__reactInternalMemoizedMergedChildContext||xl,ks=Xa.current,Cn(Xa,i),Cn(Oa,Oa.current),!0}function Yr(i,l,b){var C=i.stateNode;if(!C)throw Error(f(169));b?(i=Id(i,l,ks),C.__reactInternalMemoizedMergedChildContext=i,qn(Oa),qn(Xa),Cn(Xa,i)):qn(Oa),Cn(Oa,b)}var As=null,ch=!1,Md=!1;function Qm(i){As===null?As=[i]:As.push(i)}function _l(){if(!Md&&As!==null){Md=!0;var i=0,l=an;try{var b=As;for(an=1;i<b.length;i++){var C=b[i];do C=C(!0);while(C!==null)}As=null,ch=!1}catch($){throw As!==null&&(As=As.slice(i+1)),fi(mr,_l),$}finally{an=l,Md=!1}}return null}var au=[],ou=0,pp=null,iu=0,Io=[],Mo=0,Cs=null,Yi=1,js="";function dc(i,l){au[ou++]=iu,au[ou++]=pp,pp=i,iu=l}function Zm(i,l,b){Io[Mo++]=Yi,Io[Mo++]=js,Io[Mo++]=Cs,Cs=i;var C=Yi;i=js;var $=32-Ia(C)-1;C&=~(1<<$),b+=1;var ee=32-Ia(l)+$;if(30<ee){var ue=$-$%5;ee=(C&(1<<ue)-1).toString(32),C>>=ue,$-=ue,Yi=1<<32-Ia(l)+$|b<<$|C,js=ee+i}else Yi=1<<ee|b<<$|C,js=i}function Rd(i){i.return!==null&&(dc(i,1),Zm(i,1,0))}function Dd(i){for(;i===pp;)pp=au[--ou],au[ou]=null,iu=au[--ou],au[ou]=null;for(;i===Cs;)Cs=Io[--Mo],Io[Mo]=null,js=Io[--Mo],Io[Mo]=null,Yi=Io[--Mo],Io[Mo]=null}var Go=null,Ro=null,Un=!1,Oi=null;function eg(i,l){var b=Bo(5,null,null,0);b.elementType="DELETED",b.stateNode=l,b.return=i,(l=i.deletions)===null?(i.deletions=[b],i.flags|=16):l.push(b)}function Fd(i,l){switch(i.tag){case 5:var b=i.type;return(l=l.nodeType!==1||b.toLowerCase()!==l.nodeName.toLowerCase()?null:l)!==null&&(i.stateNode=l,Go=i,Ro=bl(l.firstChild),!0);case 6:return(l=i.pendingProps===""||l.nodeType!==3?null:l)!==null&&(i.stateNode=l,Go=i,Ro=null,!0);case 13:return(l=l.nodeType!==8?null:l)!==null&&(b=Cs!==null?{id:Yi,overflow:js}:null,i.memoizedState={dehydrated:l,treeContext:b,retryLane:1073741824},(b=Bo(18,null,null,0)).stateNode=l,b.return=i,i.child=b,Go=i,Ro=null,!0);default:return!1}}function uh(i){return!(!(1&i.mode)||128&i.flags)}function hp(i){if(Un){var l=Ro;if(l){var b=l;if(!Fd(i,l)){if(uh(i))throw Error(f(418));l=bl(b.nextSibling);var C=Go;l&&Fd(i,l)?eg(C,b):(i.flags=-4097&i.flags|2,Un=!1,Go=i)}}else{if(uh(i))throw Error(f(418));i.flags=-4097&i.flags|2,Un=!1,Go=i}}}function Ld(i){for(i=i.return;i!==null&&i.tag!==5&&
Line truncated
|
||||||
|
Error generating stack: `+ee.message+`
|
||||||
|
`+ee.stack}return{value:i,source:l,stack:$,digest:null}}function af(i,l,b){return{value:i,source:null,stack:b??null,digest:l??null}}function Ah(i,l){try{console.error(l.value)}catch(b){setTimeout((function(){throw b}))}}var c0=typeof WeakMap=="function"?WeakMap:Map;function vu(i,l,b){(b=Ns(-1,b)).tag=3,b.payload={element:null};var C=l.value;return b.callback=function(){bc||(bc=!0,Cp=C),Ah(0,l)},b}function Eg(i,l,b){(b=Ns(-1,b)).tag=3;var C=i.type.getDerivedStateFromError;if(typeof C=="function"){var $=l.value;b.payload=function(){return C($)},b.callback=function(){Ah(0,l)}}var ee=i.stateNode;return ee!==null&&typeof ee.componentDidCatch=="function"&&(b.callback=function(){Ah(0,l),typeof C!="function"&&(Pi===null?Pi=new Set([this]):Pi.add(this));var ue=l.stack;this.componentDidCatch(l.value,{componentStack:ue!==null?ue:""})}),b}function ji(i,l,b){var C=i.pingCache;if(C===null){C=i.pingCache=new c0;var $=new Set;C.set(l,$)}else($=C.get(l))===void 0&&($=new Set,C.set(l,$));$.has(b)||($.add(b),i=f0.bind(null,i,l,b),l.then(i,i))}function Sg(i){do{var l;if((l=i.tag===13)&&(l=(l=i.memoizedState)===null||l.dehydrated!==null),l)return i;i=i.return}while(i!==null);return null}function xg(i,l,b,C,$){return 1&i.mode?(i.flags|=65536,i.lanes=$,i):(i===l?i.flags|=65536:(i.flags|=128,b.flags|=131072,b.flags&=-52805,b.tag===1&&(b.alternate===null?b.tag=17:((l=Ns(-1,1)).tag=2,Ts(b,l,1))),b.lanes|=1),i)}var u0=te.ReactCurrentOwner,Do=!1;function Za(i,l,b,C){l.child=i===null?ng(l,null,b,C):fc(l,i.child,b,C)}function _g(i,l,b,C,$){b=b.render;var ee=l.ref;return pu(l,$),C=Is(i,l,b,C,ee,$),b=Hd(),i===null||Do?(Un&&b&&Rd(l),l.flags|=1,Za(i,l,C,$),l.child):(l.updateQueue=i.updateQueue,l.flags&=-2053,i.lanes&=~$,Ms(i,l,$))}function Og(i,l,b,C,$){if(i===null){var ee=b.type;return typeof ee!="function"||Pp(ee)||ee.defaultProps!==void 0||b.compare!==null||b.defaultProps!==void 0?((i=ku(b.type,null,C,l,l.mode,$)).ref=l.ref,i.return=l,l.child=i):(l.tag=15,l.type=ee,kg(i,l,ee,C,$))}if(ee=i.child,!(i.lanes&$)){var ue=ee.memoizedProps;if((b=(b=b.compare)!==null?b:_s)(ue,C)&&i.ref===l.ref)return Ms(i,l,$)}return l.flags|=1,(i=pa(ee,C)).ref=l.ref,i.return=l,l.child=i}function kg(i,l,b,C,$){if(i!==null){var ee=i.memoizedProps;if(_s(ee,C)&&i.ref===l.ref){if(Do=!1,l.pendingProps=C=ee,!(i.lanes&$))return l.lanes=i.lanes,Ms(i,l,$);131072&i.flags&&(Do=!0)}}return eo(i,l,b,C,$)}function of(i,l,b){var C=l.pendingProps,$=C.children,ee=i!==null?i.memoizedState:null;if(C.mode==="hidden")if(1&l.mode){if(!(1073741824&b))return i=ee!==null?ee.baseLanes|b:b,l.lanes=l.childLanes=1073741824,l.memoizedState={baseLanes:i,cachePool:null,transitions:null},l.updateQueue=null,Cn(jl,fo),fo|=i,null;l.memoizedState={baseLanes:0,cachePool:null,transitions:null},C=ee!==null?ee.baseLanes:b,Cn(jl,fo),fo|=C}else l.memoizedState={baseLanes:0,cachePool:null,transitions:null},Cn(jl,fo),fo|=b;else ee!==null?(C=ee.baseLanes|b,l.memoizedState=null):C=b,Cn(jl,fo),fo|=C;return Za(i,l,$,b),l.child}function li(i,l){var b=l.ref;(i===null&&b!==null||i!==null&&i.ref!==b)&&(l.flags|=512,l.flags|=2097152)}function eo(i,l,b,C,$){var ee=Ko(b)?ks:Xa.current;return ee=hc(l,ee),pu(l,$),b=Is(i,l,b,C,ee,$),C=Hd(),i===null||Do?(Un&&C&&Rd(l),l.flags|=1,Za(i,l,b,$),l.child):(l.updateQueue=i.updateQueue,l.flags&=-2053,i.lanes&=~$,Ms(i,l,$))}function Ag(i,l,b,C,$){if(Ko(b)){var ee=!0;lh(l)}else ee=!1;if(pu(l,$),l.stateNode===null)Th(i,l),bg(l,b,C),nf(l,b,C,$),C=!0;else if(i===null){var ue=l.stateNode,Ce=l.memoizedProps;ue.props=Ce;var Me=ue.context,Ue=b.contextType;typeof Ue=="object"&&Ue!==null?Ue=oi(Ue):Ue=hc(l,Ue=Ko(b)?ks:Xa.current);var ot=b.getDerivedStateFromProps,st=typeof ot=="function"||typeof ue.getSnapshotBeforeUpdate=="function";st||typeof ue.UNSAFE_componentWillReceiveProps!="function"&&typeof ue.componentWillReceiveProps!="function"||(Ce!==C||Me!==Ue)&&wg(l,ue,C,Ue),kl=!1;var lt=l.memoizedState;ue.state=lt,mh(l,C,ue,$),Me=l.memoizedState,Ce!==C||lt!==Me||Oa.current||kl?(typeof ot=="function"&&(Oh(l,b,ot,C),Me=l.memoizedState),(Ce=kl||vg(l,b,Ce,C,lt,Me,Ue))?(st||typeof ue.UNSAFE_componentWillMount!="function"&&typeof ue.componentWillMount!="function"||(typeof ue.componentWillMount=="function"&&ue.componentWillMount(),typeof ue.UNSAFE_componentWillMount=="function"&&ue.UNSAFE_componentWillMount()),typeof ue.componentDidMount=="function"&&(l.flags|=4194308)):(typeof ue.componentDidMount=="function"&&(l.flags|=4194308),l.memoizedProps=C,l.memoizedState=Me),ue.props=C,ue.state=Me,ue.context=Ue,C=Ce):(typeof ue.componentDidMount=="function"&&(l.flags|=4194308),C=!1)}else{ue=l.stateNode,og(i,l),Ce=l.memoizedProps,Ue=l.type===l.elementType?Ce:si(l.type,Ce),ue.props=Ue,st=l.pendingProps,lt=ue.context,typeof(Me=b.contextType)=="object"&&Me!==null?Me=oi(Me):Me=hc(l,Me=Ko(b)?ks:Xa.current);var vt=b.getDerivedStateFromProps;(ot=typeof vt=="function"||typeof ue.getSnapshotBeforeUpdate=="function")||typeof ue.UNSAFE_componentWillReceiveProps!="function"&&typeof ue.componentWillReceiveProps!="functio
Line truncated
|
||||||
|
`));const Ve=he-9,He=Math.round(Math.random()*(Ve>15?15:Ve)),qe=this.randomUUID(Ve);return"".concat(qe.substring(0,He)).concat(ke).concat(qe.substring(He)).concat(He.toString(16))})),_(this,"parseStamp",((he,ge)=>{if(ge&&!/t0|t[1-9]\d{1,}/.test(ge))throw new Error("Cannot extract date from a formated UUID with no timestamp in the format");const ke=ge?ge.replace(/\$[rs]\d{0,}|\$t0|\$t[1-9]\d{1,}/g,(He=>{const qe={$r:Re=>[...Array(Re)].map((()=>"r")).join(""),$s:Re=>[...Array(Re)].map((()=>"s")).join(""),$t:Re=>[...Array(Re)].map((()=>"t")).join("")},nt=He.slice(0,2),gt=Number.parseInt(He.slice(2),10);return qe[nt](gt)})).replace(/^(.*?)(t{8,})(.*)$/g,((He,qe,nt)=>he.substring(qe.length,qe.length+nt.length))):he;if(ke.length===8)return new Date(1e3*Number.parseInt(ke,16));if(ke.length<10)throw new Error("Stamp length invalid");const Ve=Number.parseInt(ke.substring(ke.length-1),16);return new Date(1e3*Number.parseInt(ke.substring(Ve,Ve+8),16))})),_(this,"setCounter",(he=>{this.counter=he})),_(this,"validate",((he,ge)=>{const ke=ge?this._normalizeDictionary(ge):this.dict;return he.split("").every((Ve=>ke.includes(Ve)))}));const X=S(S({},j),B);this.counter=0,this.debug=!1,this.dict=[],this.version="5.3.2";const{dictionary:te,shuffle:ie,length:se,counter:Te}=X;this.uuidLength=se,this.setDictionary(te,ie),this.setCounter(Te),this.debug=X.debug,this.log(this.dict),this.log("Generator instantiated with Dictionary Size ".concat(this.dictLength," and counter set to ").concat(this.counter)),this.log=this.log.bind(this),this.setDictionary=this.setDictionary.bind(this),this.setCounter=this.setCounter.bind(this),this.seq=this.seq.bind(this),this.sequentialUUID=this.sequentialUUID.bind(this),this.rnd=this.rnd.bind(this),this.randomUUID=this.randomUUID.bind(this),this.fmt=this.fmt.bind(this),this.formattedUUID=this.formattedUUID.bind(this),this.availableUUIDs=this.availableUUIDs.bind(this),this.approxMaxBeforeCollision=this.approxMaxBeforeCollision.bind(this),this.collisionProbability=this.collisionProbability.bind(this),this.uniqueness=this.uniqueness.bind(this),this.getVersion=this.getVersion.bind(this),this.stamp=this.stamp.bind(this),this.parseStamp=this.parseStamp.bind(this)}};_(M,"default",M);var z,K=M;return z=T,((Y,B,X,te)=>{if(B&&typeof B=="object"||typeof B=="function")for(let ie of v(B))y.call(Y,ie)||ie===X||s(Y,ie,{get:()=>B[ie],enumerable:!(te=h(B,ie))||te.enumerable});return Y})(s({},"__esModule",{value:!0}),z)})();w.exports=N.default,typeof window<"u"&&(N=N.default)},88310(w,N,s){w.exports=v;var h=s(37007).EventEmitter;function v(){h.call(this)}s(56698)(v,h),v.Readable=s(45412),v.Writable=s(16708),v.Duplex=s(25382),v.Transform=s(74610),v.PassThrough=s(63600),v.finished=s(86238),v.pipeline=s(57758),v.Stream=v,v.prototype.pipe=function(f,y){var m=this;function g(z){f.writable&&f.write(z)===!1&&m.pause&&m.pause()}function S(){m.readable&&m.resume&&m.resume()}m.on("data",g),f.on("drain",S),f._isStdio||y&&y.end===!1||(m.on("end",T),m.on("close",I));var _=!1;function T(){_||(_=!0,f.end())}function I(){_||(_=!0,typeof f.destroy=="function"&&f.destroy())}function j(z){if(M(),h.listenerCount(this,"error")===0)throw z}function M(){m.removeListener("data",g),f.removeListener("drain",S),m.removeListener("end",T),m.removeListener("close",I),m.removeListener("error",j),f.removeListener("error",j),m.removeListener("end",M),m.removeListener("close",M),f.removeListener("close",M)}return m.on("error",j),f.on("error",j),m.on("end",M),m.on("close",M),f.on("close",M),f.emit("pipe",m),f}},83141(w,N,s){var h=s(92861).Buffer,v=h.isEncoding||function(M){switch((M=""+M)&&M.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function f(M){var z;switch(this.encoding=(function(Y){var B=(function(te){if(!te)return"utf8";for(var ie;;)switch(te){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return te;default:if(ie)return;te=(""+te).toLowerCase(),ie=!0}})(Y);if(typeof B!="string"&&(h.isEncoding===v||!v(Y)))throw new Error("Unknown encoding: "+Y);return B||Y})(M),this.encoding){case"utf16le":this.text=g,this.end=S,z=4;break;case"utf8":this.fillLast=m,z=4;break;case"base64":this.text=_,this.end=T,z=3;break;default:return this.write=I,void(this.end=j)}this.lastNeed=0,this.lastTotal=0,this.lastChar=h.allocUnsafe(z)}function y(M){return M<=127?0:M>>5==6?2:M>>4==14?3:M>>3==30?4:M>>6==2?-1:-2}function m(M){var z=this.lastTotal-this.lastNeed,K=(function(B,X,te){if((192&X[0])!=128)return B.lastNeed=0,"�";if(B.lastNeed>1&&X.length>1){if((192&X[1])!=128)return B.lastNeed=1,"�";if(B.lastNeed>2&&X.length>2&&(192&X[2])!=128)return B.lastNeed=2,"�"}})(this,M);return K!==void 0?K:this.lastNeed<=M.length?(M.copy(this.lastChar,z,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)
Line truncated
|
||||||
|
`)}},31499(w){var N={"&":"&",'"':""","'":"'","<":"<",">":">"};w.exports=function(h){return h&&h.replace?h.replace(/([&"<>'])/g,(function(v,f){return N[f]})):h}},19123(w,N,s){var h=s(65606),v=s(31499),f=s(88310).Stream;function y(g,S,_){var T,I=(function(X,te){return new Array(te||0).join(X||"")})(S,_=_||0),j=g;if(typeof g=="object"&&(j=g[T=Object.keys(g)[0]])&&j._elem)return j._elem.name=T,j._elem.icount=_,j._elem.indent=S,j._elem.indents=I,j._elem.interrupt=j,j._elem;var M,z=[],K=[];function Y(B){Object.keys(B).forEach((function(X){z.push((function(ie,se){return ie+'="'+v(se)+'"'})(X,B[X]))}))}switch(typeof j){case"object":if(j===null)break;j._attr&&Y(j._attr),j._cdata&&K.push(("<![CDATA["+j._cdata).replace(/\]\]>/g,"]]]]><![CDATA[>")+"]]>"),j.forEach&&(M=!1,K.push(""),j.forEach((function(B){typeof B=="object"?Object.keys(B)[0]=="_attr"?Y(B._attr):K.push(y(B,S,_+1)):(K.pop(),M=!0,K.push(v(B)))})),M||K.push(""));break;default:K.push(v(j))}return{name:T,interrupt:!1,attributes:z,content:K,icount:_,indents:I,indent:S}}function m(g,S,_){if(typeof S!="object")return g(!1,S);var T=S.interrupt?1:S.content.length;function I(){for(;S.content.length;){var M=S.content.shift();if(M!==void 0){if(j(M))return;m(g,M)}}g(!1,(T>1?S.indents:"")+(S.name?"</"+S.name+">":"")+(S.indent&&!_?`
|
||||||
|
`:"")),_&&_()}function j(M){return!!M.interrupt&&(M.interrupt.append=g,M.interrupt.end=I,M.interrupt=!1,g(!0),!0)}if(g(!1,S.indents+(S.name?"<"+S.name:"")+(S.attributes.length?" "+S.attributes.join(" "):"")+(T?S.name?">":"":S.name?"/>":"")+(S.indent&&T>1?`
|
||||||
|
`:"")),!T)return g(!1,S.indent?`
|
||||||
|
`:"");j(S)||I()}w.exports=function(S,_){typeof _!="object"&&(_={indent:_});var T=_.stream?new f:null,I="",j=!1,M=_.indent?_.indent===!0?" ":_.indent:"",z=!0;function K(te){z?h.nextTick(te):te()}function Y(te,ie){if(ie!==void 0&&(I+=ie),te&&!j&&(T=T||new f,j=!0),te&&j){var se=I;K((function(){T.emit("data",se)})),I=""}}function B(te,ie){m(Y,y(te,M,M?1:0),ie)}function X(){if(T){var te=I;K((function(){T.emit("data",te),T.emit("end"),T.readable=!1,T.emit("close")}))}}return K((function(){z=!1})),_.declaration&&(function(ie){var se={version:"1.0",encoding:ie.encoding||"UTF-8"};ie.standalone&&(se.standalone=ie.standalone),B({"?xml":{_attr:se}}),I=I.replace("/>","?>")})(_.declaration),S&&S.forEach?S.forEach((function(te,ie){var se;ie+1===S.length&&(se=X),B(te,se)})):B(S,X),T?(T.readable=!0,T):I},w.exports.element=w.exports.Element=function(){var S={_elem:y(Array.prototype.slice.call(arguments)),push:function(_){if(!this.append)throw new Error("not assigned to a parent!");var T=this,I=this._elem.indent;m(this.append,y(_,I,this._elem.icount+(I?1:0)),(function(){T.append(!0)}))},close:function(_){_!==void 0&&this.push(_),this.end&&this.end()}};return S}},86215(w,N){var s,h,v;h=[],s=(function(){var f=function(j){return j&&"getComputedStyle"in window&&window.getComputedStyle(j)["scroll-behavior"]==="smooth"};if(typeof window>"u"||!("document"in window))return{};var y=function(j,M,z){var K;M=M||999,z||z===0||(z=9);var Y=function(he){K=he},B=function(){clearTimeout(K),Y(0)},X=function(he){return Math.max(0,j.getTopOf(he)-z)},te=function(he,ge,ke){if(B(),ge===0||ge&&ge<0||f(j.body))j.toY(he),ke&&ke();else{var Ve=j.getY(),He=Math.max(0,he)-Ve,qe=new Date().getTime();ge=ge||Math.min(Math.abs(He),M),(function nt(){Y(setTimeout((function(){var gt=Math.min(1,(new Date().getTime()-qe)/ge),Re=Math.max(0,Math.floor(Ve+He*(gt<.5?2*gt*gt:gt*(4-2*gt)-1)));j.toY(Re),gt<1&&j.getHeight()+Re<j.body.scrollHeight?nt():(setTimeout(B,99),ke&&ke())}),9))})()}},ie=function(he,ge,ke){te(X(he),ge,ke)},se=function(he,ge,ke){var Ve=he.getBoundingClientRect().height,He=j.getTopOf(he)+Ve,qe=j.getHeight(),nt=j.getY(),gt=nt+qe;X(he)<nt||Ve+z>qe?ie(he,ge,ke):He+z>gt?te(He-qe+z,ge,ke):ke&&ke()},Te=function(he,ge,ke,Ve){te(Math.max(0,j.getTopOf(he)-j.getHeight()/2+(ke||he.getBoundingClientRect().height/2)),ge,Ve)};return{setup:function(he,ge){return(he===0||he)&&(M=he),(ge===0||ge)&&(z=ge),{defaultDuration:M,edgeOffset:z}},to:ie,toY:te,intoView:se,center:Te,stop:B,moving:function(){return!!K},getY:j.getY,getTopOf:j.getTopOf}},m=document.documentElement,g=function(){return window.scrollY||m.scrollTop},S=y({body:document.scrollingElement||document.body,toY:function(j){window.scrollTo(0,j)},getY:g,getHeight:function(){return window.innerHeight||m.clientHeight},getTopOf:function(j){return j.getBoundingClientRect().top+g()-m.offsetTop}});if(S.createScroller=function(j,M,z){return y({body:j,toY:function(K){j.scrollTop=K},getY:function(){return j.scrollTop},getHeight:function(){return Math.min(j.clientHeight,window.innerHeight||m.clientHeight)},getTopOf:function(K){return K.offsetTop}},M,z)},"addEventListener"in window&&!window.noZensmooth&&!f(document.body)){var _="history"in window&&"pushState"in history,T=_&&"scrollRestoration"in history;T&&(history.scrollRestoration="auto"),window.addEventListener("load",(function(){T&&(setTimeout((function(){history.scrollRestoration="manual"}),9),window.addEventListener("popstate",(function(j){j.state&&"zenscrollY"in j.state&&S.toY(j.state.zenscrollY)}),!1)),window.location.hash&&setTimeout((function(){var j=S.setup().edgeOffset;if(j){var M=document.getElementById(window.location.href.split("#")[1]);if(M){var z=Math.max(0,S.getTopOf(M)-j),K=S.getY()-z;0<=K&&K<9&&window.scrollTo(0,z)}}}),9)}),!1);var I=new RegExp("(^|\\s)noZensmooth(\\s|$)");window.addEventListener("click",(function(j){for(var M=j.target;M&&M.tagName!=="A";)M=M.parentNode;if(!(!M||j.which!==1||j.shiftKey||j.metaKey||j.ctrlKey||j.altKey)){if(T){var z=history.state&&typeof history.state=="object"?history.state:{};z.zenscrollY=S.getY();try{history.replaceState(z,"")}catch{}}var K=M.getAttribute("href")||"";if(K.indexOf("#")===0&&!I.test(M.className)){var Y=0,B=document.getElementById(K.substring(1));if(K!=="#"){if(!B)return;Y=S.getTopOf(B)}j.preventDefault();var X=function(){window.location=K},te=S.setup().edgeOffset;te&&(Y=Math.max(0,Y-te),_&&(X=function(){history.pushState({},"",K)})),S.toY(Y,null,X)}}}),!1)}return S})(),(v=typeof s=="function"?s.apply(N,h):s)===void 0||(w.exports=v)},15340(){},79838(){},8628(w,N,s){w.exports=s(76343)},48675(w,N,s){w.exports=s(20850)},7666(w,N,s){var h=s(84851),v=s(953);function f(){var y;return w.exports=f=h?v(y=h).call(y):function(m){for(var g=1;g<arguments.length;g++){var S=arguments[g];for(var _ in S)({}).hasOwnProperty.call(S,_)&&(m[_]=S[_])}return m},w.exports.__esModule=!0,w.exports.default=w.exports,f.apply(null,arguments)}w.exports=f,w.exports.__esModule=!0,w.exports.default=w.exports},39209(w,N,s){var h=s(7657
Line truncated
|
||||||
|
|
||||||
|
`+r.mark.snippet),o+" "+a):o}function e(r,n){Error.call(this),this.name="YAMLException",this.reason=r,this.mark=n,this.message=t(this,!1),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack||""}return sc=1,e.prototype=Object.create(Error.prototype),e.prototype.constructor=e,e.prototype.toString=function(n){return this.name+": "+t(this,n)},sl=e}function _a(){if(Wc)return Ra;Wc=1;const t=Ho(),e=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],r=["scalar","sequence","mapping"];return Ra=function(a,o){if(o=o||{},Object.keys(o).forEach((function(p){if(e.indexOf(p)===-1)throw new t('Unknown option "'+p+'" is met in definition of "'+a+'" YAML type.')})),this.options=o,this.tag=a,this.kind=o.kind||null,this.resolve=o.resolve||function(){return!0},this.construct=o.construct||function(p){return p},this.instanceOf=o.instanceOf||null,this.predicate=o.predicate||null,this.represent=o.represent||null,this.representName=o.representName||null,this.defaultStyle=o.defaultStyle||null,this.multi=o.multi||!1,this.styleAliases=(function(c){const d={};return c!==null&&Object.keys(c).forEach((function(E){c[E].forEach((function(x){d[String(x)]=E}))})),d})(o.styleAliases||null),r.indexOf(this.kind)===-1)throw new t('Unknown kind "'+this.kind+'" is specified for "'+a+'" YAML type.')},Ra}function _i(){if(Hc)return ll;Hc=1;const t=Ho(),e=_a();function r(a,o){const p=[];return a[o].forEach((function(c){let d=p.length;p.forEach((function(E,x){E.tag===c.tag&&E.kind===c.kind&&E.multi===c.multi&&(d=x)})),p[d]=c})),p}function n(a){return this.extend(a)}return n.prototype.extend=function(o){let p=[],c=[];if(o instanceof e)c.push(o);else if(Array.isArray(o))c=c.concat(o);else{if(!o||!Array.isArray(o.implicit)&&!Array.isArray(o.explicit))throw new t("Schema.extend argument should be a Type, [ Type ], or a schema definition ({ implicit: [...], explicit: [...] })");o.implicit&&(p=p.concat(o.implicit)),o.explicit&&(c=c.concat(o.explicit))}p.forEach((function(E){if(!(E instanceof e))throw new t("Specified list of YAML types (or a single Type object) contains a non-Type object.");if(E.loadKind&&E.loadKind!=="scalar")throw new t("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.");if(E.multi)throw new t("There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.")})),c.forEach((function(E){if(!(E instanceof e))throw new t("Specified list of YAML types (or a single Type object) contains a non-Type object.")}));const d=Object.create(n.prototype);return d.implicit=(this.implicit||[]).concat(p),d.explicit=(this.explicit||[]).concat(c),d.compiledImplicit=r(d,"implicit"),d.compiledExplicit=r(d,"explicit"),d.compiledTypeMap=(function(){const x={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}};function k(P){P.multi?(x.multi[P.kind].push(P),x.multi.fallback.push(P)):x[P.kind][P.tag]=x.fallback[P.tag]=P}for(let P=0,R=arguments.length;P<R;P+=1)arguments[P].forEach(k);return x})(d.compiledImplicit,d.compiledExplicit),d},ll=n}function cc(){if(cl)return ep;cl=1;const t=_a();return ep=new t("tag:yaml.org,2002:str",{kind:"scalar",construct:function(e){return e!==null?e:""}})}function jn(){if(tp)return ul;tp=1;const t=_a();return ul=new t("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(e){return e!==null?e:[]}})}function mn(){if(qi)return xa;qi=1;const t=_a();return xa=new t("tag:yaml.org,2002:map",{kind:"mapping",construct:function(e){return e!==null?e:{}}})}function uc(){if(rp)return Si;rp=1;const t=_i();return Si=new t({explicit:[cc(),jn(),mn()]})}function Os(){if(pl)return Ui;pl=1;const t=_a();return Ui=new t("tag:yaml.org,2002:null",{kind:"scalar",resolve:function(r){if(r===null)return!0;const n=r.length;return n===1&&r==="~"||n===4&&(r==="null"||r==="Null"||r==="NULL")},construct:function(){return null},predicate:function(r){return r===null},represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"},empty:function(){return""}},defaultStyle:"lowercase"}),Ui}function To(){if(xi)return hl;xi=1;const t=_a();return hl=new t("tag:yaml.org,2002:bool",{kind:"scalar",resolve:function(r){if(r===null)return!1;const n=r.length;return n===4&&(r==="true"||r==="True"||r==="TRUE")||n===5&&(r==="false"||r==="False"||r==="FALSE")},construct:function(r){return r==="true"||r==="True"||r==="TRUE"},predicate:function(r){return Object.prototype.toString.call(r)==="[object Boolean]"},represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"}),hl}function Qc(){if(No)return xs;No=1;const t=Hi(),e=_a();function r(o){return o>=48&&o<=55}function n(o){return o>=48&&o<=57}function a(o){let p=o,c=1,d=p[0];
Line truncated
|
||||||
|
\r`;return gl=new t("tag:yaml.org,2002:binary",{kind:"scalar",resolve:function(n){if(n===null)return!1;let a=0;const o=n.length,p=e;for(let c=0;c<o;c++){const d=p.indexOf(n.charAt(c));if(!(d>64)){if(d<0)return!1;a+=6}}return a%8==0},construct:function(n){const a=n.replace(/[\r\n=]/g,""),o=a.length,p=e;let c=0;const d=[];for(let x=0;x<o;x++)x%4==0&&x&&(d.push(c>>16&255),d.push(c>>8&255),d.push(255&c)),c=c<<6|p.indexOf(a.charAt(x));const E=o%4*6;return E===0?(d.push(c>>16&255),d.push(c>>8&255),d.push(255&c)):E===18?(d.push(c>>10&255),d.push(c>>2&255)):E===12&&d.push(c>>4&255),new Uint8Array(d)},predicate:function(n){return Object.prototype.toString.call(n)==="[object Uint8Array]"},represent:function(n){let a="",o=0;const p=n.length,c=e;for(let E=0;E<p;E++)E%3==0&&E&&(a+=c[o>>18&63],a+=c[o>>12&63],a+=c[o>>6&63],a+=c[63&o]),o=(o<<8)+n[E];const d=p%3;return d===0?(a+=c[o>>18&63],a+=c[o>>12&63],a+=c[o>>6&63],a+=c[63&o]):d===2?(a+=c[o>>10&63],a+=c[o>>4&63],a+=c[o<<2&63],a+=c[64]):d===1&&(a+=c[o>>2&63],a+=c[o<<4&63],a+=c[64],a+=c[64]),a}}),gl}function ip(){if(Ji)return Vi;Ji=1;const t=_a(),e=Object.prototype.hasOwnProperty,r=Object.prototype.toString;return Vi=new t("tag:yaml.org,2002:omap",{kind:"sequence",resolve:function(a){if(a===null)return!0;const o={},p=a;for(let c=0,d=p.length;c<d;c+=1){const E=p[c];let x,k=!1;if(r.call(E)!=="[object Object]")return!1;for(x in E)if(e.call(E,x)){if(k)return!1;k=!0}if(!k||e.call(o,x))return!1;Object.defineProperty(o,x,{value:!0})}return!0},construct:function(a){return a!==null?a:[]}}),Vi}function sp(){if(np)return Wi;np=1;const t=_a(),e=Object.prototype.toString;return Wi=new t("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:function(n){if(n===null)return!0;const a=n,o=new Array(a.length);for(let p=0,c=a.length;p<c;p+=1){const d=a[p];if(e.call(d)!=="[object Object]")return!1;const E=Object.keys(d);if(E.length!==1)return!1;o[p]=[E[0],d[E[0]]]}return!0},construct:function(n){if(n===null)return[];const a=n,o=new Array(a.length);for(let p=0,c=a.length;p<c;p+=1){const d=a[p],E=Object.keys(d);o[p]=[E[0],d[E[0]]]}return o}}),Wi}function A(){if(oh)return ap;oh=1;const t=_a(),e=Object.prototype.hasOwnProperty;return ap=new t("tag:yaml.org,2002:set",{kind:"mapping",resolve:function(n){if(n===null)return!0;const a=n;for(const o in a)if(e.call(a,o)&&a[o]!==null)return!1;return!0},construct:function(n){return n!==null?n:{}}}),ap}function O(){return _s?Ka:(_s=1,Ka=Zc().extend({implicit:[eu(),op()],explicit:[tu(),ip(),sp(),A()]}))}function D(){if(Xc)return yl;Xc=1;const t=Hi(),e=Ho(),r=(function(){if(Zu)return Jc;Zu=1;const $e=Hi();function Je(it,bt,ut,Xt,zt){let tr="",Er="";const dr=Math.floor(zt/2)-1;return Xt-bt>dr&&(tr=" ... ",bt=Xt-dr+tr.length),ut-Xt>dr&&(Er=" ...",ut=Xt+dr-Er.length),{str:tr+it.slice(bt,ut).replace(/\t/g,"→")+Er,pos:Xt-bt+tr.length}}function Ft(it,bt){return $e.repeat(" ",bt-it.length)+it}return Jc=function(bt,ut){if(ut=Object.create(ut||null),!bt.buffer)return null;ut.maxLength||(ut.maxLength=79),typeof ut.indent!="number"&&(ut.indent=1),typeof ut.linesBefore!="number"&&(ut.linesBefore=3),typeof ut.linesAfter!="number"&&(ut.linesAfter=2);const Xt=/\r?\n|\r|\0/g,zt=[0],tr=[];let Er,dr=-1;for(;Er=Xt.exec(bt.buffer);)tr.push(Er.index),zt.push(Er.index+Er[0].length),bt.position<=Er.index&&dr<0&&(dr=zt.length-2);dr<0&&(dr=zt.length-1);let qr="";const en=Math.min(bt.line+ut.linesAfter,tr.length).toString().length,kn=ut.maxLength-(ut.indent+en+3);for(let jt=1;jt<=ut.linesBefore&&!(dr-jt<0);jt++){const We=Je(bt.buffer,zt[dr-jt],tr[dr-jt],bt.position-(zt[dr]-zt[dr-jt]),kn);qr=$e.repeat(" ",ut.indent)+Ft((bt.line-jt+1).toString(),en)+" | "+We.str+`
|
||||||
|
`+qr}const ln=Je(bt.buffer,zt[dr],tr[dr],bt.position,kn);qr+=$e.repeat(" ",ut.indent)+Ft((bt.line+1).toString(),en)+" | "+ln.str+`
|
||||||
|
`,qr+=$e.repeat("-",ut.indent+en+3+ln.pos)+`^
|
||||||
|
`;for(let jt=1;jt<=ut.linesAfter&&!(dr+jt>=tr.length);jt++){const We=Je(bt.buffer,zt[dr+jt],tr[dr+jt],bt.position-(zt[dr]-zt[dr+jt]),kn);qr+=$e.repeat(" ",ut.indent)+Ft((bt.line+jt+1).toString(),en)+" | "+We.str+`
|
||||||
|
`}return qr.replace(/\n$/,"")},Jc})(),n=O(),a=Object.prototype.hasOwnProperty,o=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,p=/[\x85\u2028\u2029]/,c=/[,\[\]{}]/,d=/^(?:!|!!|![0-9A-Za-z-]+!)$/,E=/^(?:!|[^,\[\]{}])(?:%[0-9a-f]{2}|[0-9a-z\-#;/?:@&=+$,_.!~*'()\[\]])*$/i;function x(W){return Object.prototype.toString.call(W)}function k(W){return W===10||W===13}function P(W){return W===9||W===32}function R(W){return W===9||W===32||W===10||W===13}function L(W){return W===44||W===91||W===93||W===123||W===125}function V(W){if(W>=48&&W<=57)return W-48;const $e=32|W;return $e>=97&&$e<=102?$e-97+10:-1}function Q(W){switch(W){case 48:return"\0";case 97:return"\x07";case 98:return"\b";case 116:case 9:return" ";case 110:return`
|
||||||
|
`;case 118:return"\v";case 102:return"\f";case 114:return"\r";case 101:return"\x1B";case 32:return" ";case 34:return'"';case 47:return"/";case 92:return"\\";case 78:return"
";case 95:return" ";case 76:return"\u2028";case 80:return"\u2029";default:return""}}function J(W){return W<=65535?String.fromCharCode(W):String.fromCharCode(55296+(W-65536>>10),56320+(W-65536&1023))}function re(W,$e,Je){$e==="__proto__"?Object.defineProperty(W,$e,{configurable:!0,enumerable:!0,writable:!0,value:Je}):W[$e]=Je}const me=new Array(256),le=new Array(256);for(let W=0;W<256;W++)me[W]=Q(W)?1:0,le[W]=Q(W);function ve(W,$e){this.input=W,this.filename=$e.filename||null,this.schema=$e.schema||n,this.onWarning=$e.onWarning||null,this.legacy=$e.legacy||!1,this.json=$e.json||!1,this.listener=$e.listener||null,this.maxDepth=typeof $e.maxDepth=="number"?$e.maxDepth:100,this.maxTotalMergeKeys=typeof $e.maxTotalMergeKeys=="number"?$e.maxTotalMergeKeys:1e4,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=W.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.depth=0,this.totalMergeKeys=0,this.firstTabInLine=-1,this.documents=[],this.anchorMapTransactions=[]}function be(W,$e){const Je={name:W.filename,buffer:W.input.slice(0,-1),position:W.position,line:W.line,column:W.position-W.lineStart};return Je.snippet=r(Je),new e($e,Je)}function ce(W,$e){throw be(W,$e)}function _e(W,$e){W.onWarning&&W.onWarning.call(null,be(W,$e))}function Ae(W,$e,Je){const Ft=W.anchorMapTransactions;if(Ft.length!==0){const it=Ft[Ft.length-1];a.call(it,$e)||(it[$e]={existed:a.call(W.anchorMap,$e),value:W.anchorMap[$e]})}W.anchorMap[$e]=Je}function xe(W){return{position:W.position,line:W.line,lineStart:W.lineStart,lineIndent:W.lineIndent,firstTabInLine:W.firstTabInLine,tag:W.tag,anchor:W.anchor,kind:W.kind,result:W.result}}function Oe(W,$e){W.position=$e.position,W.line=$e.line,W.lineStart=$e.lineStart,W.lineIndent=$e.lineIndent,W.firstTabInLine=$e.firstTabInLine,W.tag=$e.tag,W.anchor=$e.anchor,W.kind=$e.kind,W.result=$e.result}const je={YAML:function($e,Je,Ft){$e.version!==null&&ce($e,"duplication of %YAML directive"),Ft.length!==1&&ce($e,"YAML directive accepts exactly one argument");const it=/^([0-9]+)\.([0-9]+)$/.exec(Ft[0]);it===null&&ce($e,"ill-formed argument of the YAML directive");const bt=parseInt(it[1],10),ut=parseInt(it[2],10);bt!==1&&ce($e,"unacceptable YAML version of the document"),$e.version=Ft[0],$e.checkLineBreaks=ut<2,ut!==1&&ut!==2&&_e($e,"unsupported YAML version of the document")},TAG:function($e,Je,Ft){let it;Ft.length!==2&&ce($e,"TAG directive accepts exactly two arguments");const bt=Ft[0];it=Ft[1],d.test(bt)||ce($e,"ill-formed tag handle (first argument) of the TAG directive"),a.call($e.tagMap,bt)&&ce($e,'there is a previously declared suffix for "'+bt+'" tag handle'),E.test(it)||ce($e,"ill-formed tag prefix (second argument) of the TAG directive");try{it=decodeURIComponent(it)}catch{ce($e,"tag prefix is malformed: "+it)}$e.tagMap[bt]=it}};function G(W,$e,Je,Ft){if($e<Je){const it=W.input.slice($e,Je);if(Ft)for(let bt=0,ut=it.length;bt<ut;bt+=1){const Xt=it.charCodeAt(bt);Xt===9||Xt>=32&&Xt<=1114111||ce(W,"expected valid JSON character")}else o.test(it)&&ce(W,"the stream contains non-printable characters");W.result+=it}}function de(W,$e,Je,Ft){t.isObject(Je)||ce(W,"cannot merge mappings; the provided source object is unacceptable");const it=Object.keys(Je);for(let bt=0,ut=it.length;bt<ut;bt+=1){const Xt=it[bt];W.maxTotalMergeKeys!==-1&&++W.totalMergeKeys>W.maxTotalMergeKeys&&ce(W,"merge keys exceeded maxTotalMergeKeys ("+W.maxTotalMergeKeys+")"),a.call($e,Xt)||(re($e,Xt,Je[Xt]),Ft[Xt]=!0)}}function Ne(W,$e,Je,Ft,it,bt,ut,Xt,zt){if(Array.isArray(it))for(let tr=0,Er=(it=Array.prototype.slice.call(it)).length;tr<Er;tr+=1)Array.isArray(it[tr])&&ce(W,"nested arrays are not supported inside keys"),typeof it=="object"&&x(it[tr])==="[object Object]"&&(it[tr]="[object Object]");if(typeof it=="object"&&x(it)==="[object Object]"&&(it="[object Object]"),it=String(it),$e===null&&($e={}),Ft==="tag:yaml.org,2002:merge")if(Array.isArray(bt))for(let tr=0,Er=bt.length;tr<Er;tr+=1)de(W,$e,bt[tr],Je);else de(W,$e,bt,Je);else W.json||a.call(Je,it)||!a.call($e,it)||(W.line=ut||W.line,W.lineStart=Xt||W.lineStart,W.position=zt||W.position,ce(W,"duplicated mapping key")),re($e,it,bt),delete Je[it];return $e}function Ye(W){const $e=W.input.charCodeAt(W.position);$e===10?W.position++:$e===13?(W.position++,W.input.charCodeAt(W.position)===10&&W.position++):ce(W,"a line break is expected"),W.line+=1,W.lineStart=W.position,W.firstTabInLine=-1}function ht(W,$e,Je){let Ft=0,it=W.input.charCodeAt(W.position);for(;it!==0;){for(;P(it);)it===9&&W.firstTabInLine===-1&&(W.firstTabInLine=W.position),it=W.input.charCodeAt(++W.position);if($e&&it===35)do it=W.input.charCodeAt(++W.position);while(it!==10&&it!==13&&it!==0);if(!k(it))break;for(Ye(W),it=W.input.charCodeAt(W.position),Ft++,W.lineIndent=0;it===32;)W
Line truncated
|
||||||
|
`,$e-1))}function qt(W,$e){const Je=W.tag,Ft=W.anchor,it=[];let bt=!1;if(W.firstTabInLine!==-1)return!1;W.anchor!==null&&Ae(W,W.anchor,it);let ut=W.input.charCodeAt(W.position);for(;ut!==0&&(W.firstTabInLine!==-1&&(W.position=W.firstTabInLine,ce(W,"tab characters must not be used in indentation")),ut===45)&&R(W.input.charCodeAt(W.position+1));){if(bt=!0,W.position++,ht(W,!0,-1)&&W.lineIndent<=$e){it.push(null),ut=W.input.charCodeAt(W.position);continue}const Xt=W.line;if(or(W,$e,3,!1,!0),it.push(W.result),ht(W,!0,-1),ut=W.input.charCodeAt(W.position),(W.line===Xt||W.lineIndent>$e)&&ut!==0)ce(W,"bad indentation of a sequence entry");else if(W.lineIndent<$e)break}return!!bt&&(W.tag=Je,W.anchor=Ft,W.kind="sequence",W.result=it,!0)}function It(W,$e,Je){let Ft,it,bt,ut;const Xt=W.tag,zt=W.anchor,tr={},Er=Object.create(null);let dr=null,qr=null,en=null,kn=!1,ln=!1;if(W.firstTabInLine!==-1)return!1;W.anchor!==null&&Ae(W,W.anchor,tr);let jt=W.input.charCodeAt(W.position);for(;jt!==0;){kn||W.firstTabInLine===-1||(W.position=W.firstTabInLine,ce(W,"tab characters must not be used in indentation"));const We=W.input.charCodeAt(W.position+1),Kr=W.line;if(jt!==63&&jt!==58||!R(We)){if(it=W.line,bt=W.lineStart,ut=W.position,!or(W,Je,2,!1,!0))break;if(W.line===Kr){for(jt=W.input.charCodeAt(W.position);P(jt);)jt=W.input.charCodeAt(++W.position);if(jt===58)jt=W.input.charCodeAt(++W.position),R(jt)||ce(W,"a whitespace character is expected after the key-value separator within a block mapping"),kn&&(Ne(W,tr,Er,dr,qr,null,it,bt,ut),dr=qr=en=null),ln=!0,kn=!1,Ft=!1,dr=W.tag,qr=W.result;else{if(!ln)return W.tag=Xt,W.anchor=zt,!0;ce(W,"can not read an implicit mapping pair; a colon is missed")}}else{if(!ln)return W.tag=Xt,W.anchor=zt,!0;ce(W,"can not read a block mapping entry; a multiline key may not be an implicit key")}}else jt===63?(kn&&(Ne(W,tr,Er,dr,qr,null,it,bt,ut),dr=qr=en=null),ln=!0,kn=!0,Ft=!0):kn?(kn=!1,Ft=!0):ce(W,"incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line"),W.position+=1,jt=We;if((W.line===Kr||W.lineIndent>$e)&&(kn&&(it=W.line,bt=W.lineStart,ut=W.position),or(W,$e,4,!0,Ft)&&(kn?qr=W.result:en=W.result),kn||(Ne(W,tr,Er,dr,qr,en,it,bt,ut),dr=qr=en=null),ht(W,!0,-1),jt=W.input.charCodeAt(W.position)),(W.line===Kr||W.lineIndent>$e)&&jt!==0)ce(W,"bad indentation of a mapping entry");else if(W.lineIndent<$e)break}return kn&&Ne(W,tr,Er,dr,qr,null,it,bt,ut),ln&&(W.tag=Xt,W.anchor=zt,W.kind="mapping",W.result=tr),ln}function Ot(W){let $e,Je,Ft=!1,it=!1,bt=W.input.charCodeAt(W.position);if(bt!==33)return!1;W.tag!==null&&ce(W,"duplication of a tag property"),bt=W.input.charCodeAt(++W.position),bt===60?(Ft=!0,bt=W.input.charCodeAt(++W.position)):bt===33?(it=!0,$e="!!",bt=W.input.charCodeAt(++W.position)):$e="!";let ut=W.position;if(Ft){do bt=W.input.charCodeAt(++W.position);while(bt!==0&&bt!==62);W.position<W.length?(Je=W.input.slice(ut,W.position),bt=W.input.charCodeAt(++W.position)):ce(W,"unexpected end of the stream within a verbatim tag")}else{for(;bt!==0&&!R(bt);)bt===33&&(it?ce(W,"tag suffix cannot contain exclamation marks"):($e=W.input.slice(ut-1,W.position+1),d.test($e)||ce(W,"named tag handle cannot contain such characters"),it=!0,ut=W.position+1)),bt=W.input.charCodeAt(++W.position);Je=W.input.slice(ut,W.position),c.test(Je)&&ce(W,"tag suffix cannot contain flow indicator characters")}Je&&!E.test(Je)&&ce(W,"tag name cannot contain such characters: "+Je);try{Je=decodeURIComponent(Je)}catch{ce(W,"tag name is malformed: "+Je)}return Ft?W.tag=Je:a.call(W.tagMap,$e)?W.tag=W.tagMap[$e]+Je:$e==="!"?W.tag="!"+Je:$e==="!!"?W.tag="tag:yaml.org,2002:"+Je:ce(W,'undeclared tag handle "'+$e+'"'),!0}function et(W){let $e=W.input.charCodeAt(W.position);if($e!==38)return!1;W.anchor!==null&&ce(W,"duplication of an anchor property"),$e=W.input.charCodeAt(++W.position);const Je=W.position;for(;$e!==0&&!R($e)&&!L($e);)$e=W.input.charCodeAt(++W.position);return W.position===Je&&ce(W,"name of an anchor node must contain at least one character"),W.anchor=W.input.slice(Je,W.position),!0}function Dt(W,$e,Je,Ft){const it=xe(W);return(function(ut){ut.anchorMapTransactions.push(Object.create(null))})(W),Oe(W,$e),W.tag=null,W.anchor=null,W.kind=null,W.result=null,It(W,Je,Ft)&&W.kind==="mapping"?((function(ut){const Xt=ut.anchorMapTransactions.pop(),zt=ut.anchorMapTransactions;if(zt.length===0)return;const tr=zt[zt.length-1],Er=Object.keys(Xt);for(let dr=0,qr=Er.length;dr<qr;dr+=1){const en=Er[dr];a.call(tr,en)||(tr[en]=Xt[en])}})(W),!0):((function(ut){const Xt=ut.anchorMapTransactions.pop(),zt=Object.keys(Xt);for(let tr=zt.length-1;tr>=0;tr-=1){const Er=Xt[zt[tr]];Er.existed?ut.anchorMap[zt[tr]]=Er.value:delete ut.anchorMap[zt[tr]]}})(W),Oe(W,it),!1)}function or(W,$e,Je,Ft,it){let bt,ut,Xt,zt,tr,Er=1,dr=!1,qr=!1,en=null;W.depth>=W.maxDepth&&ce(W,"nesting exceeded maxDepth ("+W.maxDepth+")"),W.depth+=1,W.listener!==null&&W.listener("open",W),W.tag=null,W.anchor=null,W.kind=null,W.result=null;c
Line truncated
|
||||||
|
`,En?1+Dn:Dn):yn===1&&En&&(We.result+=`
|
||||||
|
`);break}Lr?P(vn)?(Di=!0,We.result+=t.repeat(`
|
||||||
|
`,En?1+Dn:Dn)):Di?(Di=!1,We.result+=t.repeat(`
|
||||||
|
`,Dn+1)):Dn===0?En&&(We.result+=" "):We.result+=t.repeat(`
|
||||||
|
`,Dn):We.result+=t.repeat(`
|
||||||
|
`,En?1+Dn:Dn),En=!0,Pn=!0,Dn=0;const hi=We.position;for(;!k(vn)&&vn!==0;)vn=We.input.charCodeAt(++We.position);G(We,hi,We.position,!1)}return!0})(W,zt)||(function(We,Kr){let Lr,zr,yn=We.input.charCodeAt(We.position);if(yn!==39)return!1;for(We.kind="scalar",We.result="",We.position++,Lr=zr=We.position;(yn=We.input.charCodeAt(We.position))!==0;)if(yn===39){if(G(We,Lr,We.position,!0),yn=We.input.charCodeAt(++We.position),yn!==39)return!0;Lr=We.position,We.position++,zr=We.position}else k(yn)?(G(We,Lr,zr,!0),_t(We,ht(We,!1,Kr)),Lr=zr=We.position):We.position===We.lineStart&&Ct(We)?ce(We,"unexpected end of the document within a single quoted scalar"):(We.position++,P(yn)||(zr=We.position));ce(We,"unexpected end of the stream within a single quoted scalar")})(W,zt)||(function(We,Kr){let Lr,zr,yn,En=We.input.charCodeAt(We.position);if(En!==34)return!1;for(We.kind="scalar",We.result="",We.position++,Lr=zr=We.position;(En=We.input.charCodeAt(We.position))!==0;){if(En===34)return G(We,Lr,We.position,!0),We.position++,!0;if(En===92){if(G(We,Lr,We.position,!0),En=We.input.charCodeAt(++We.position),k(En))ht(We,!1,Kr);else if(En<256&&me[En])We.result+=le[En],We.position++;else if((yn=(Pn=En)===120?2:Pn===117?4:Pn===85?8:0)>0){let Ua=yn,Dn=0;for(;Ua>0;Ua--)En=We.input.charCodeAt(++We.position),(yn=V(En))>=0?Dn=(Dn<<4)+yn:ce(We,"expected hexadecimal character");We.result+=J(Dn),We.position++}else ce(We,"unknown escape sequence");Lr=zr=We.position}else k(En)?(G(We,Lr,zr,!0),_t(We,ht(We,!1,Kr)),Lr=zr=We.position):We.position===We.lineStart&&Ct(We)?ce(We,"unexpected end of the document within a double quoted scalar"):(We.position++,P(En)||(zr=We.position))}var Pn;ce(We,"unexpected end of the stream within a double quoted scalar")})(W,zt)?qr=!0:(function(We){let Kr=We.input.charCodeAt(We.position);if(Kr!==42)return!1;Kr=We.input.charCodeAt(++We.position);const Lr=We.position;for(;Kr!==0&&!R(Kr)&&!L(Kr);)Kr=We.input.charCodeAt(++We.position);We.position===Lr&&ce(We,"name of an alias node must contain at least one character");const zr=We.input.slice(Lr,We.position);return a.call(We.anchorMap,zr)||ce(We,'unidentified alias "'+zr+'"'),We.result=We.anchorMap[zr],ht(We,!0,-1),!0})(W)?(qr=!0,W.tag===null&&W.anchor===null||ce(W,"alias node should not have any properties")):(function(We,Kr,Lr){let zr,yn,En,Pn,Ua,Dn;const Di=We.kind,vn=We.result;let cn=We.input.charCodeAt(We.position);if(R(cn)||L(cn)||cn===35||cn===38||cn===42||cn===33||cn===124||cn===62||cn===39||cn===34||cn===37||cn===64||cn===96)return!1;if(cn===63||cn===45){const hi=We.input.charCodeAt(We.position+1);if(R(hi)||Lr&&L(hi))return!1}for(We.kind="scalar",We.result="",zr=yn=We.position,En=!1;cn!==0;){if(cn===58){const hi=We.input.charCodeAt(We.position+1);if(R(hi)||Lr&&L(hi))break}else if(cn===35){if(R(We.input.charCodeAt(We.position-1)))break}else{if(We.position===We.lineStart&&Ct(We)||Lr&&L(cn))break;if(k(cn)){if(Pn=We.line,Ua=We.lineStart,Dn=We.lineIndent,ht(We,!1,-1),We.lineIndent>=Kr){En=!0,cn=We.input.charCodeAt(We.position);continue}We.position=yn,We.line=Pn,We.lineStart=Ua,We.lineIndent=Dn;break}}En&&(G(We,zr,yn,!1),_t(We,We.line-Pn),zr=yn=We.position,En=!1),P(cn)||(yn=We.position+1),cn=We.input.charCodeAt(++We.position)}return G(We,zr,yn,!1),!!We.result||(We.kind=Di,We.result=vn,!1)})(W,zt,Je===1)&&(qr=!0,W.tag===null&&(W.tag="?")),W.anchor!==null&&Ae(W,W.anchor,W.result)}else Er===0&&(qr=ut&&qt(W,tr));if(W.tag===null)W.anchor!==null&&Ae(W,W.anchor,W.result);else if(W.tag==="?"){W.result!==null&&W.kind!=="scalar"&&ce(W,'unacceptable node kind for !<?> tag; it should be "scalar", not "'+W.kind+'"');for(let ln=0,jt=W.implicitTypes.length;ln<jt;ln+=1)if(Xt=W.implicitTypes[ln],Xt.resolve(W.result)){W.result=Xt.construct(W.result),W.tag=Xt.tag,W.anchor!==null&&Ae(W,W.anchor,W.result);break}}else if(W.tag!=="!"){if(a.call(W.typeMap[W.kind||"fallback"],W.tag))Xt=W.typeMap[W.kind||"fallback"][W.tag];else{Xt=null;const ln=W.typeMap.multi[W.kind||"fallback"];for(let jt=0,We=ln.length;jt<We;jt+=1)if(W.tag.slice(0,ln[jt].tag.length)===ln[jt].tag){Xt=ln[jt];break}}Xt||ce(W,"unknown tag !<"+W.tag+">"),W.result!==null&&Xt.kind!==W.kind&&ce(W,"unacceptable node kind for !<"+W.tag+'> tag; it should be "'+Xt.kind+'", not "'+W.kind+'"'),Xt.resolve(W.result,W.tag)?(W.result=Xt.construct(W.result,W.tag),W.anchor!==null&&Ae(W,W.anchor,W.result)):ce(W,"cannot resolve a node with !<"+W.tag+"> explicit tag")}return W.listener!==null&&W.listener("close",W),W.depth-=1,W.tag!==null||W.anchor!==null||qr}function Ut(W){const $e=W.position;let Je,Ft=!1;for(W.version=null,W.checkLineBreaks=W.legacy,W.tagMap=Object.create(null),W.anchorMap=Object.create(null);(Je=W.input.charCodeAt(W.position))!==0&&(ht(W,!0,-1),Je=W.input.charCodeAt(W.position),!(W.lineIndent>0||Je!==37));){Ft=!0,Je=W.input.charCodeAt(++W.position);let it=W.position;for(;Je!==0&&!R(Je);)Je=W.input.charCodeAt(++W.position);const bt=W.input.slice(it,W.position),ut=[];for(bt.length<1&&ce(W,"directive name must not be less than on
Line truncated
|
||||||
|
`),W.charCodeAt(0)===65279&&(W=W.slice(1)));const Je=new ve(W,$e),Ft=W.indexOf("\0");for(Ft!==-1&&(Je.position=Ft,ce(Je,"null byte is not allowed in input")),Je.input+="\0";Je.input.charCodeAt(Je.position)===32;)Je.lineIndent+=1,Je.position+=1;for(;Je.position<Je.length-1;)Ut(Je);return Je.documents}return yl.loadAll=function($e,Je,Ft){Je!==null&&typeof Je=="object"&&Ft===void 0&&(Ft=Je,Je=null);const it=pr($e,Ft);if(typeof Je!="function")return it;for(let bt=0,ut=it.length;bt<ut;bt+=1)Je(it[bt])},yl.load=function($e,Je){const Ft=pr($e,Je);if(Ft.length!==0){if(Ft.length===1)return Ft[0];throw new e("expected a single document in the stream, but found more")}},yl}var F,q,ae={};function oe(){if(F)return ae;F=1;const t=Hi(),e=Ho(),r=O(),n=Object.prototype.toString,a=Object.prototype.hasOwnProperty,o=65279,p={0:"\\0",7:"\\a",8:"\\b",9:"\\t",10:"\\n",11:"\\v",12:"\\f",13:"\\r",27:"\\e",34:'\\"',92:"\\\\",133:"\\N",160:"\\_",8232:"\\L",8233:"\\P"},c=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"],d=/^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/;function E(G){let de,Ne;const Ye=G.toString(16).toUpperCase();if(G<=255)de="x",Ne=2;else if(G<=65535)de="u",Ne=4;else{if(!(G<=4294967295))throw new e("code point within a string may not be greater than 0xFFFFFFFF");de="U",Ne=8}return"\\"+de+t.repeat("0",Ne-Ye.length)+Ye}function x(G){this.schema=G.schema||r,this.indent=Math.max(1,G.indent||2),this.noArrayIndent=G.noArrayIndent||!1,this.skipInvalid=G.skipInvalid||!1,this.flowLevel=t.isNothing(G.flowLevel)?-1:G.flowLevel,this.styleMap=(function(Ne,Ye){if(Ye===null)return{};const ht={},Ct=Object.keys(Ye);for(let _t=0,qt=Ct.length;_t<qt;_t+=1){let It=Ct[_t],Ot=String(Ye[It]);It.slice(0,2)==="!!"&&(It="tag:yaml.org,2002:"+It.slice(2));const et=Ne.compiledTypeMap.fallback[It];et&&a.call(et.styleAliases,Ot)&&(Ot=et.styleAliases[Ot]),ht[It]=Ot}return ht})(this.schema,G.styles||null),this.sortKeys=G.sortKeys||!1,this.lineWidth=G.lineWidth||80,this.noRefs=G.noRefs||!1,this.noCompatMode=G.noCompatMode||!1,this.condenseFlow=G.condenseFlow||!1,this.quotingType=G.quotingType==='"'?2:1,this.forceQuotes=G.forceQuotes||!1,this.replacer=typeof G.replacer=="function"?G.replacer:null,this.implicitTypes=this.schema.compiledImplicit,this.explicitTypes=this.schema.compiledExplicit,this.tag=null,this.result="",this.duplicates=[],this.usedDuplicates=null}function k(G,de){const Ne=t.repeat(" ",de);let Ye=0,ht="";const Ct=G.length;for(;Ye<Ct;){let _t;const qt=G.indexOf(`
|
||||||
|
`,Ye);qt===-1?(_t=G.slice(Ye),Ye=Ct):(_t=G.slice(Ye,qt+1),Ye=qt+1),_t.length&&_t!==`
|
||||||
|
`&&(ht+=Ne),ht+=_t}return ht}function P(G,de){return`
|
||||||
|
`+t.repeat(" ",G.indent*de)}function R(G){return G===32||G===9}function L(G){return G>=32&&G<=126||G>=161&&G<=55295&&G!==8232&&G!==8233||G>=57344&&G<=65533&&G!==o||G>=65536&&G<=1114111}function V(G){return L(G)&&G!==o&&G!==13&&G!==10}function Q(G,de,Ne){const Ye=V(G),ht=Ye&&!R(G);return(Ne?Ye:Ye&&G!==44&&G!==91&&G!==93&&G!==123&&G!==125)&&G!==35&&!(de===58&&!ht)||V(de)&&!R(de)&&G===35||de===58&&ht}function J(G,de){const Ne=G.charCodeAt(de);let Ye;return Ne>=55296&&Ne<=56319&&de+1<G.length&&(Ye=G.charCodeAt(de+1),Ye>=56320&&Ye<=57343)?1024*(Ne-55296)+Ye-56320+65536:Ne}function re(G){return/^\n* /.test(G)}function me(G,de,Ne,Ye,ht,Ct,_t,qt){let It,Ot=0,et=null,Dt=!1,or=!1;const Ut=Ye!==-1;let pr=-1,W=(function(Je){return L(Je)&&Je!==o&&!R(Je)&&Je!==45&&Je!==63&&Je!==58&&Je!==44&&Je!==91&&Je!==93&&Je!==123&&Je!==125&&Je!==35&&Je!==38&&Je!==42&&Je!==33&&Je!==124&&Je!==61&&Je!==62&&Je!==39&&Je!==34&&Je!==37&&Je!==64&&Je!==96})(J(G,0))&&(function(Je){return!R(Je)&&Je!==58})(J(G,G.length-1));if(de||_t)for(It=0;It<G.length;Ot>=65536?It+=2:It++){if(Ot=J(G,It),!L(Ot))return 5;W=W&&Q(Ot,et,qt),et=Ot}else{for(It=0;It<G.length;Ot>=65536?It+=2:It++){if(Ot=J(G,It),Ot===10)Dt=!0,Ut&&(or=or||It-pr-1>Ye&&G[pr+1]!==" ",pr=It);else if(!L(Ot))return 5;W=W&&Q(Ot,et,qt),et=Ot}or=or||Ut&&It-pr-1>Ye&&G[pr+1]!==" "}return Dt||or?Ne>9&&re(G)?5:_t?Ct===2?5:2:or?4:3:!W||_t||ht(G)?Ct===2?5:2:1}function le(G,de,Ne,Ye,ht){G.dump=(function(){if(de.length===0)return G.quotingType===2?'""':"''";if(!G.noCompatMode&&(c.indexOf(de)!==-1||d.test(de)))return G.quotingType===2?'"'+de+'"':"'"+de+"'";const Ct=G.indent*Math.max(1,Ne),_t=G.lineWidth===-1?-1:Math.max(Math.min(G.lineWidth,40),G.lineWidth-Ct),qt=Ye||G.flowLevel>-1&&Ne>=G.flowLevel;switch(me(de,qt,G.indent,_t,(function(Ot){return(function(Dt,or){for(let Ut=0,pr=Dt.implicitTypes.length;Ut<pr;Ut+=1)if(Dt.implicitTypes[Ut].resolve(or))return!0;return!1})(G,Ot)}),G.quotingType,G.forceQuotes&&!Ye,ht)){case 1:return de;case 2:return"'"+de.replace(/'/g,"''")+"'";case 3:return"|"+ve(de,G.indent)+be(k(de,Ct));case 4:return">"+ve(de,G.indent)+be(k((function(Ot,et){const Dt=/(\n+)([^\n]*)/g;let or,Ut,pr=(function(){let $e=Ot.indexOf(`
|
||||||
|
`);return $e=$e!==-1?$e:Ot.length,Dt.lastIndex=$e,ce(Ot.slice(0,$e),et)})(),W=Ot[0]===`
|
||||||
|
`||Ot[0]===" ";for(;Ut=Dt.exec(Ot);){const $e=Ut[1],Je=Ut[2];or=Je[0]===" ",pr+=$e+(W||or||Je===""?"":`
|
||||||
|
`)+ce(Je,et),W=or}return pr})(de,_t),Ct));case 5:return'"'+(function(Ot){let et="",Dt=0;for(let or=0;or<Ot.length;Dt>=65536?or+=2:or++){Dt=J(Ot,or);const Ut=p[Dt];!Ut&&L(Dt)?(et+=Ot[or],Dt>=65536&&(et+=Ot[or+1])):et+=Ut||E(Dt)}return et})(de)+'"';default:throw new e("impossible error: invalid scalar style")}})()}function ve(G,de){const Ne=re(G)?String(de):"",Ye=G[G.length-1]===`
|
||||||
|
`;return Ne+(Ye&&(G[G.length-2]===`
|
||||||
|
`||G===`
|
||||||
|
`)?"+":Ye?"":"-")+`
|
||||||
|
`}function be(G){return G[G.length-1]===`
|
||||||
|
`?G.slice(0,-1):G}function ce(G,de){if(G===""||G[0]===" ")return G;const Ne=/ [^ ]/g;let Ye,ht,Ct=0,_t=0,qt=0,It="";for(;Ye=Ne.exec(G);)qt=Ye.index,qt-Ct>de&&(ht=_t>Ct?_t:qt,It+=`
|
||||||
|
`+G.slice(Ct,ht),Ct=ht+1),_t=qt;return It+=`
|
||||||
|
`,G.length-Ct>de&&_t>Ct?It+=G.slice(Ct,_t)+`
|
||||||
|
`+G.slice(_t+1):It+=G.slice(Ct),It.slice(1)}function _e(G,de,Ne,Ye){let ht="";const Ct=G.tag;for(let _t=0,qt=Ne.length;_t<qt;_t+=1){let It=Ne[_t];G.replacer&&(It=G.replacer.call(Ne,String(_t),It)),(xe(G,de+1,It,!0,!0,!1,!0)||It===void 0&&xe(G,de+1,null,!0,!0,!1,!0))&&(Ye&&ht===""||(ht+=P(G,de)),G.dump&&G.dump.charCodeAt(0)===10?ht+="-":ht+="- ",ht+=G.dump)}G.tag=Ct,G.dump=ht||"[]"}function Ae(G,de,Ne){const Ye=Ne?G.explicitTypes:G.implicitTypes;for(let ht=0,Ct=Ye.length;ht<Ct;ht+=1){const _t=Ye[ht];if((_t.instanceOf||_t.predicate)&&(!_t.instanceOf||typeof de=="object"&&de instanceof _t.instanceOf)&&(!_t.predicate||_t.predicate(de))){if(Ne?_t.multi&&_t.representName?G.tag=_t.representName(de):G.tag=_t.tag:G.tag="?",_t.represent){const qt=G.styleMap[_t.tag]||_t.defaultStyle;let It;if(n.call(_t.represent)==="[object Function]")It=_t.represent(de,qt);else{if(!a.call(_t.represent,qt))throw new e("!<"+_t.tag+'> tag resolver accepts not "'+qt+'" style');It=_t.represent[qt](de,qt)}G.dump=It}return!0}}return!1}function xe(G,de,Ne,Ye,ht,Ct,_t){G.tag=null,G.dump=Ne,Ae(G,Ne,!1)||Ae(G,Ne,!0);const qt=n.call(G.dump),It=Ye;Ye&&(Ye=G.flowLevel<0||G.flowLevel>de);const Ot=qt==="[object Object]"||qt==="[object Array]";let et,Dt;if(Ot&&(et=G.duplicates.indexOf(Ne),Dt=et!==-1),(G.tag!==null&&G.tag!=="?"||Dt||G.indent!==2&&de>0)&&(ht=!1),Dt&&G.usedDuplicates[et])G.dump="*ref_"+et;else{if(Ot&&Dt&&!G.usedDuplicates[et]&&(G.usedDuplicates[et]=!0),qt==="[object Object]")Ye&&Object.keys(G.dump).length!==0?((function(Ut,pr,W,$e){let Je="";const Ft=Ut.tag,it=Object.keys(W);if(Ut.sortKeys===!0)it.sort();else if(typeof Ut.sortKeys=="function")it.sort(Ut.sortKeys);else if(Ut.sortKeys)throw new e("sortKeys must be a boolean or a function");for(let bt=0,ut=it.length;bt<ut;bt+=1){let Xt="";$e&&Je===""||(Xt+=P(Ut,pr));const zt=it[bt];let tr=W[zt];if(Ut.replacer&&(tr=Ut.replacer.call(W,zt,tr)),!xe(Ut,pr+1,zt,!0,!0,!0))continue;const Er=Ut.tag!==null&&Ut.tag!=="?"||Ut.dump&&Ut.dump.length>1024;Er&&(Ut.dump&&Ut.dump.charCodeAt(0)===10?Xt+="?":Xt+="? "),Xt+=Ut.dump,Er&&(Xt+=P(Ut,pr)),xe(Ut,pr+1,tr,!0,Er)&&(Ut.dump&&Ut.dump.charCodeAt(0)===10?Xt+=":":Xt+=": ",Xt+=Ut.dump,Je+=Xt)}Ut.tag=Ft,Ut.dump=Je||"{}"})(G,de,G.dump,ht),Dt&&(G.dump="&ref_"+et+G.dump)):((function(Ut,pr,W){let $e="";const Je=Ut.tag,Ft=Object.keys(W);for(let it=0,bt=Ft.length;it<bt;it+=1){let ut="";$e!==""&&(ut+=", "),Ut.condenseFlow&&(ut+='"');const Xt=Ft[it];let zt=W[Xt];Ut.replacer&&(zt=Ut.replacer.call(W,Xt,zt)),xe(Ut,pr,Xt,!1,!1)&&(Ut.dump.length>1024&&(ut+="? "),ut+=Ut.dump+(Ut.condenseFlow?'"':"")+":"+(Ut.condenseFlow?"":" "),xe(Ut,pr,zt,!1,!1)&&(ut+=Ut.dump,$e+=ut))}Ut.tag=Je,Ut.dump="{"+$e+"}"})(G,de,G.dump),Dt&&(G.dump="&ref_"+et+" "+G.dump));else if(qt==="[object Array]")Ye&&G.dump.length!==0?(G.noArrayIndent&&!_t&&de>0?_e(G,de-1,G.dump,ht):_e(G,de,G.dump,ht),Dt&&(G.dump="&ref_"+et+G.dump)):((function(Ut,pr,W){let $e="";const Je=Ut.tag;for(let Ft=0,it=W.length;Ft<it;Ft+=1){let bt=W[Ft];Ut.replacer&&(bt=Ut.replacer.call(W,String(Ft),bt)),(xe(Ut,pr,bt,!1,!1)||bt===void 0&&xe(Ut,pr,null,!1,!1))&&($e!==""&&($e+=","+(Ut.condenseFlow?"":" ")),$e+=Ut.dump)}Ut.tag=Je,Ut.dump="["+$e+"]"})(G,de,G.dump),Dt&&(G.dump="&ref_"+et+" "+G.dump));else{if(qt!=="[object String]"){if(qt==="[object Undefined]"||G.skipInvalid)return!1;throw new e("unacceptable kind of an object to dump "+qt)}G.tag!=="?"&&le(G,G.dump,de,Ct,It)}if(G.tag!==null&&G.tag!=="?"){let or=encodeURI(G.tag[0]==="!"?G.tag.slice(1):G.tag).replace(/!/g,"%21");or=G.tag[0]==="!"?"!"+or:or.slice(0,18)==="tag:yaml.org,2002:"?"!!"+or.slice(18):"!<"+or+">",G.dump=or+" "+G.dump}}return!0}function Oe(G,de){const Ne=[],Ye=[];je(G,Ne,Ye);const ht=Ye.length;for(let Ct=0;Ct<ht;Ct+=1)de.duplicates.push(Ne[Ye[Ct]]);de.usedDuplicates=new Array(ht)}function je(G,de,Ne){if(G!==null&&typeof G=="object"){const Ye=de.indexOf(G);if(Ye!==-1)Ne.indexOf(Ye)===-1&&Ne.push(Ye);else if(de.push(G),Array.isArray(G))for(let ht=0,Ct=G.length;ht<Ct;ht+=1)je(G[ht],de,Ne);else{const ht=Object.keys(G);for(let Ct=0,_t=ht.length;Ct<_t;Ct+=1)je(G[ht[Ct]],de,Ne)}}}return ae.dump=function(de,Ne){const Ye=new x(Ne=Ne||{});Ye.noRefs||Oe(de,Ye);let ht=de;return Ye.replacer&&(ht=Ye.replacer.call({"":ht},"",ht)),xe(Ye,0,ht,!0,!0)?Ye.dump+`
|
||||||
|
`:""},ae}var fe=(function(){if(q)return ca;q=1;const e=D(),r=oe();function n(a,o){return function(){throw new Error("Function yaml."+a+" is removed in js-yaml 4. Use yaml."+o+" instead, which is now safe by default.")}}return ca.Type=_a(),ca.Schema=_i(),ca.FAILSAFE_SCHEMA=uc(),ca.JSON_SCHEMA=vl(),ca.CORE_SCHEMA=Zc(),ca.DEFAULT_SCHEMA=O(),ca.load=e.load,ca.loadAll=e.loadAll,ca.dump=r.dump,ca.YAMLException=Ho(),ca.types={binary:tu(),float:pc(),map:mn(),null:Os(),pairs:sp(),set:A(),timestamp:eu(),bool:To(),int:Qc(),merge:op(),omap:ip(),seq:jn(),str:cc()},ca.safeLoad=n("safeLoad","load"),ca.safeLoadAll=n("safeLoadAll","loadAll"),ca.safeDump=n("safeDump","dump"),ca})();const Pe=ah(fe),{Type:ze,Schema:tt,FAILSAFE_SCHEMA:At,JSON_SCHEMA:Rt,CORE_SCHEMA:lr,DEFAULT_SCHEMA:vr,load:Ar,loadAll:_n,dump:ya,YAMLException:Fa,types:ai,safeLoad:Tn,safeLoadAll:Hn,safeDump:na}=Pe,aa="configs_update",Bn="configs_toggle";function Ga(t,e){return{type:aa,payload:{[t]:e}}}function Ya(t){return{type:Bn,payload:t}}const po=()=>()=>{},n0=t=>e=>{const{fn:{fetch:r}}=e;return r(t)},a0=(t,e)=>r=>{const{specActions:n,configsActions:a}=r;if(t)return a.downloadConfig(t).then(o,o);function o(p){p instanceof Error||p.status>=400?(n.updateLoadingStatus("failedConfig"),n.updateLoadingStatus("failedConfig"),n.updateUrl(""),console.error(p.statusText+" "+t.url),e(null)):e(((c,d)=>{try{return Pe.load(c)}catch(E){return d&&d.errActions.newThrownErr(new Error(E)),{}}})(p.text,r))}},Pd=(t,e)=>t.getIn(Array.isArray(e)?e:[e]),bl={[aa]:(t,e)=>t.merge((0,H.fromJS)(e.payload)),[Bn]:(t,e)=>{const r=e.payload,n=t.get(r);return t.set(r,!n)}};function Nd(){return{statePlugins:{configs:{reducers:bl,actions:h,selectors:v}}}}const wl=t=>t?history.pushState(null,null,`#${t}`):window.location.hash="";var Ki=Be(86215),lp=Be.n(Ki);const Gi="layout_scroll_to",ih="layout_clear_scroll",o0={fn:{getScrollParent:function(e,r){const n=document.documentElement;let a=getComputedStyle(e);const o=a.position==="absolute",p=r?/(auto|scroll|hidden)/:/(auto|scroll)/;if(a.position==="fixed")return n;for(let c=e;c=c.parentElement;)if(a=getComputedStyle(c),(!o||a.position!=="static")&&p.test(a.overflow+a.overflowY+a.overflowX))return c;return n}},statePlugins:{layout:{actions:{scrollToElement:(t,e)=>r=>{try{e=e||r.fn.getScrollParent(t),lp().createScroller(e).to(t)}catch(n){console.error(n)}},scrollTo:t=>({type:Gi,payload:Array.isArray(t)?t:[t]}),clearScrollTo:()=>({type:ih}),readyToScroll:(t,e)=>r=>{const n=r.layoutSelectors.getScrollToKey();H.default.is(n,(0,H.fromJS)(t))&&(r.layoutActions.scrollToElement(e),r.layoutActions.clearScrollTo())},parseDeepLinkHash:t=>({layoutActions:e,layoutSelectors:r,getConfigs:n})=>{if(n().deepLinking&&t){let a=t.slice(1);a[0]==="!"&&(a=a.slice(1)),a[0]==="/"&&(a=a.slice(1));const o=a.split("/").map((x=>x||"")),p=r.isShownKeyFromUrlHashArray(o),[c,d="",E=""]=p;if(c==="operations"){const x=r.isShownKeyFromUrlHashArray([d]);d.indexOf("_")>-1&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),e.show(x.map((k=>k.replace(/_/g," "))),!0)),e.show(x,!0)}(d.indexOf("_")>-1||E.indexOf("_")>-1)&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),e.show(p.map((x=>x.replace(/_/g," "))),!0)),e.show(p,!0),e.scrollTo(p)}}},selectors:{getScrollToKey:t=>t.get("scrollToKey"),isShownKeyFromUrlHashArray(t,e){const[r,n]=e;return n?["operations",r,n]:r?["operations-tag",r]:[]},urlHashArrayFromIsShownKey(t,e){let[r,n,a]=e;return r=="operations"?[n,a]:r=="operations-tag"?[n]:[]}},reducers:{[Gi]:(t,e)=>t.set("scrollToKey",H.default.fromJS(e.payload)),[ih]:t=>t.delete("scrollToKey")},wrapActions:{show:(t,{getConfigs:e,layoutSelectors:r})=>(...n)=>{if(t(...n),e().deepLinking)try{let[a,o]=n;a=Array.isArray(a)?a:[a];const p=r.urlHashArrayFromIsShownKey(a);if(!p.length)return;const[c,d]=p;if(!o)return wl("/");p.length===2?wl(Jo(`/${encodeURIComponent(c)}/${encodeURIComponent(d)}`)):p.length===1&&wl(Jo(`/${encodeURIComponent(c)}`))}catch(a){console.error(a)}}}}}};var i0=Be(2209),El=Be.n(i0);const cp=(t,e)=>class extends u.Component{onLoad=n=>{const{operation:a}=this.props,{tag:o,operationId:p}=a.toObject();let{isShownKey:c}=a.toObject();c=c||["operations",o,p],e.layoutActions.readyToScroll(c,n)};render(){return u.createElement("span",{ref:this.onLoad},u.createElement(t,this.props))}},ru=(t,e)=>class extends u.Component{onLoad=n=>{const{tag:a}=this.props,o=["operations-tag",a];e.layoutActions.readyToScroll(o,n)};render(){return u.createElement("span",{ref:this.onLoad},u.createElement(t,this.props))}};function up(){return[o0,{statePlugins:{configs:{wrapActions:{loaded:(t,e)=>(...r)=>{t(...r);const n=decodeURIComponent(window.location.hash);e.layoutActions.parseDeepLinkHash(n)}}}},wrapComponents:{operation:cp,OperationTag:ru}}]}var Td=Be(40860),nu=Be.n(Td);function Sl(t){return t.map((e=>{let r="is not of a type(s)",n=e.get("message").indexOf(r);if(n>-1){let a=e.ge
Line truncated
|
||||||
|
`))==="-d "?t.replace(/-d /g,`-d ^
|
||||||
|
`):/^[_\/-]/g.test(t)?t:'"'+t+'"',$d=t=>t==="-d "?t:/\n/.test(t)?`@"
|
||||||
|
${t.replace(/`/g,"``").replace(/\$/g,"`$")}
|
||||||
|
"@`:/^[_\/-]/.test(t)?t:`'${t.replace(/'/g,"''")}'`,Bd=(t,e,r,n="")=>{let a=!1,o="";const p=(...R)=>o+=" "+R.map(e).join(" "),c=(...R)=>o+=R.map(e).join(" "),d=()=>o+=` ${r}`,E=(R=1)=>o+=" ".repeat(R);let x=t.get("headers");o+="curl"+n;const k=t.get("curlOptions");if(H.List.isList(k)&&!k.isEmpty()&&p(...t.get("curlOptions")),p("-X",t.get("method")),d(),E(),c(`${t.get("url")}`),x&&x.size)for(let R of t.get("headers").entries()){d(),E();let[L,V]=R;c("-H",`${L}: ${V}`),a=a||/^content-type$/i.test(L)&&/^multipart\/form-data$/i.test(V)}const P=t.get("body");if(P)if(a&&["POST","PUT","PATCH"].includes(t.get("method")))for(let[R,L]of P.entrySeq()){let V=dp(R);d(),E(),c("-F"),L instanceof Qe.File&&typeof L.valueOf()=="string"?p(`${V}=${L.data}${L.type?`;type=${L.type}`:""}`):L instanceof Qe.File?p(`${V}=@${L.name}${L.type?`;type=${L.type}`:""}`):p(`${V}=${L}`)}else if(P instanceof Qe.File)d(),E(),c(`--data-binary '@${P.name}'`);else{d(),E(),c("-d ");let R=P;H.Map.isMap(R)?c((function(V){let Q=[];for(let[J,re]of V.get("body").entrySeq()){let me=dp(J);re instanceof Qe.File?Q.push(` "${me}": {
|
||||||
|
"name": "${re.name}"${re.type?`,
|
||||||
|
"type": "${re.type}"`:""}
|
||||||
|
}`):Q.push(` "${me}": ${JSON.stringify(re,null,2).replace(/(\r\n|\r|\n)/g,`
|
||||||
|
`)}`)}return`{
|
||||||
|
${Q.join(`,
|
||||||
|
`)}
|
||||||
|
}`})(t)):(typeof R!="string"&&(R=JSON.stringify(R)),c(R))}else P||t.get("method")!=="POST"||(d(),E(),c("-d ''"));return o},fp=t=>Bd(t,$d,"`\n",".exe"),mp=t=>Bd(t,tg,`\\
|
||||||
|
`),rg=t=>Bd(t,su,`^
|
||||||
|
`),ph=t=>t||(0,H.Map)(),fc=yr(ph,(t=>{const e=t.get("languages"),r=t.get("generators",(0,H.Map)());return!e||e.isEmpty()?r:r.filter(((n,a)=>e.includes(a)))})),ng=t=>({fn:e})=>fc(t).map(((r,n)=>{const a=(o=>e[`requestSnippetGenerator_${o}`])(n);return typeof a!="function"?null:r.set("fn",a)})).filter((r=>r)),hh=yr(ph,(t=>t.get("activeLanguage"))),dh=yr(ph,(t=>t.get("defaultExpanded")));var lu=Be(46942),Xr=Be.n(lu),cu=Be(59399);const uu="Copy request snippet to clipboard",qd={cursor:"pointer",lineHeight:1,display:"inline-flex",backgroundColor:"rgb(250, 250, 250)",paddingBottom:"0",paddingTop:"0",border:"1px solid rgb(51, 51, 51)",borderRadius:"4px 4px 0 0",boxShadow:"none",borderBottom:"none"},pu={cursor:"pointer",lineHeight:1,display:"inline-flex",backgroundColor:"rgb(51, 51, 51)",boxShadow:"none",border:"1px solid rgb(51, 51, 51)",paddingBottom:"0",paddingTop:"0",borderRadius:"4px 4px 0 0",marginTop:"-5px",marginRight:"-5px",marginLeft:"-5px",zIndex:"9999",borderBottom:"none"},oi=({request:t,requestSnippetsSelectors:e,getComponent:r})=>{const n=(0,u.useRef)(null),a=r("ArrowUpIcon"),o=r("ArrowDownIcon"),p=r("SyntaxHighlighter",!0),[c,d]=(0,u.useState)(e.getSnippetGenerators()?.keySeq().first()),[E,x]=(0,u.useState)(e?.getDefaultExpanded()),k=e.getSnippetGenerators(),P=k.get(c),R=P.get("fn")(t),L=()=>{x(!E)},V=J=>J===c?pu:qd,Q=J=>{const{target:re,deltaY:me}=J,{scrollHeight:le,offsetHeight:ve,scrollTop:be}=re;le>ve&&(be===0&&me<0||ve+be>=le&&me>0)&&J.preventDefault()};return(0,u.useEffect)((()=>{}),[]),(0,u.useEffect)((()=>{const J=Array.from(n.current.childNodes).filter((re=>!!re.nodeType&&re.classList?.contains("curl-command")));return J.forEach((re=>re.addEventListener("mousewheel",Q,{passive:!1}))),()=>{J.forEach((re=>re.removeEventListener("mousewheel",Q)))}}),[t]),u.createElement("div",{className:"request-snippets",ref:n},u.createElement("div",{style:{width:"100%",display:"flex",justifyContent:"flex-start",alignItems:"center",marginBottom:"15px"}},u.createElement("h4",{onClick:()=>L(),style:{cursor:"pointer"}},"Snippets"),u.createElement("button",{onClick:()=>L(),style:{border:"none",background:"none"},title:E?"Collapse operation":"Expand operation"},E?u.createElement(o,{className:"arrow",width:"10",height:"10"}):u.createElement(a,{className:"arrow",width:"10",height:"10"}))),E&&u.createElement("div",{className:"curl-command"},u.createElement("div",{style:{paddingLeft:"15px",paddingRight:"10px",width:"100%",display:"flex"}},k.entrySeq().map((([J,re])=>u.createElement("div",{className:Xr()("btn",{active:J===c}),style:V(J),key:J,onClick:()=>(me=>{c!==me&&d(me)})(J)},u.createElement("h4",{style:J===c?{color:"white"}:{}},re.get("title")))))),u.createElement("div",{className:"copy-to-clipboard",title:uu,"aria-label":uu},u.createElement(cu.CopyToClipboard,{text:R},u.createElement("button",{"aria-label":uu,title:uu,type:"button"}))),u.createElement("div",null,u.createElement(p,{language:P.get("syntax"),className:"curl microlight",renderPlainText:({children:J,PlainTextViewer:re})=>u.createElement(re,{className:"curl"},J)},R))))},Ol=()=>({components:{RequestSnippets:oi},fn:{requestSnippetGenerator_curl_bash:mp,requestSnippetGenerator_curl_cmd:rg,requestSnippetGenerator_curl_powershell:fp},statePlugins:{requestSnippets:{selectors:T}}});class gp extends u.Component{static defaultProps={collapsedContent:"{...}",expanded:!1,title:null,onToggle:()=>{},hideSelfOnExpand:!1,specPath:H.default.List([])};constructor(e,r){super(e,r);let{expanded:n,collapsedContent:a}=this.props;this.state={expanded:n,collapsedContent:a||gp.defaultProps.collapsedContent}}componentDidMount(){const{hideSelfOnExpand:e,expanded:r,modelName:n}=this.props;e&&r&&this.props.onToggle(n,r)}UNSAFE_componentWillReceiveProps(e){this.props.expanded!==e.expanded&&this.setState({expanded:e.expanded})}toggleCollapsed=()=>{this.props.onToggle&&this.props.onToggle(this.props.modelName,!this.state.expanded),this.setState({expanded:!this.state.expanded})};onLoad=e=>{if(e&&this.props.layoutSelectors){const r=this.props.layoutSelectors.getScrollToKey();H.default.is(r,this.props.specPath)&&this.toggleCollapsed(),this.props.layoutActions.readyToScroll(this.props.specPath,e.parentElement)}};render(){const{title:e,classes:r}=this.props;return this.state.expanded&&this.props.hideSelfOnExpand?u.createElement("span",{className:r||""},this.props.children):u.createElement("span",{className:r||"",ref:this.onLoad},u.createElement("button",{"aria-expanded":this.state.expanded,className:"model-box-control",onClick:this.toggleCollapsed},e&&u.createElement("span",{className:"pointer"},e),u.createElement("span",{className:"model-toggle"+(this.state.expanded?"":" collapsed")}),!this.state.expanded&&u.createElement("span",null,this.state.collapsedContent)),this.state.expanded&&this.props.children)}}const ag=({initialTab:t,isExecute:e,schema:r,example:n})=>{const a=(0,u.useMemo)((()=>({example:"example",model:"model"})),[]),o=(0,u.useMemo)((()=>Object.keys(a)),[a]).includes(t)&&r&&
Line truncated
|
||||||
|
`&&(c=c.slice(0,c.length-1))}catch(d){return console.error(d),"error: could not generate yaml example"}return c.replace(/\t/g," ")},yu=t=>(e,r,n)=>{const{fn:a}=t();if(e&&!e.xml&&(e.xml={}),e&&!e.xml.name){if(!e.$$ref&&(e.type||e.items||e.properties||e.additionalProperties))return`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- XML example cannot be generated; root element name is undefined -->`;if(e.$$ref){let o=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=o[1]}}return a.memoizedCreateXMLExample(e,r,n)},af=t=>(e,r="",n={},a=void 0)=>{const{fn:o}=t();return typeof a?.toJS=="function"&&(a=a.toJS()),/xml/.test(r)?(typeof e?.toJS=="function"&&(e=e.toJS()),o.getXmlSampleSchema(e,n,a)):/(yaml|yml)/.test(r)?o.getYamlSampleSchema(e,n,r,a):o.getJsonSampleSchema(e,n,r,a)},Ah=({getSystem:t})=>{const e=wg(t),r=nf(t),n=yu(t),a=af(t);return{fn:{jsonSchema5:{inferSchema:_p,sampleFromSchema:rf,sampleFromSchemaGeneric:Yo,createXMLExample:tf,memoizedSampleFromSchema:Oh,memoizedCreateXMLExample:si,getJsonSampleSchema:e,getYamlSampleSchema:r,getXmlSampleSchema:n,getSampleSchema:a,mergeJsonSchema:gc},inferSchema:_p,sampleFromSchema:rf,sampleFromSchemaGeneric:Yo,createXMLExample:tf,memoizedSampleFromSchema:Oh,memoizedCreateXMLExample:si,getJsonSampleSchema:e,getYamlSampleSchema:r,getXmlSampleSchema:n,getSampleSchema:a,mergeJsonSchema:gc,getSchemaObjectType:kh}}};var c0=Be(37334),vu=Be.n(c0);const Eg=["get","put","post","delete","options","head","patch","trace","query"],ji=t=>t||(0,H.Map)(),Sg=yr(ji,(t=>t.get("lastError"))),xg=yr(ji,(t=>t.get("url"))),u0=yr(ji,(t=>t.get("spec")||"")),Do=yr(ji,(t=>t.get("specSource")||"not-editor")),Za=yr(ji,(t=>t.get("json",(0,H.Map)()))),_g=yr(Za,(t=>t.toJS())),Og=yr(ji,(t=>t.get("resolved",(0,H.Map)()))),kg=(t,e)=>t.getIn(["resolvedSubtrees",...e],void 0),of=(t,e)=>H.Map.isMap(t)&&H.Map.isMap(e)?e.get("$$ref")?e:(0,H.OrderedMap)().mergeWith(of,t,e):e,li=yr(ji,(t=>(0,H.OrderedMap)().mergeWith(of,t.get("json"),t.get("resolvedSubtrees")))),eo=t=>Za(t),Ag=yr(eo,(()=>!1)),Ch=yr(eo,(t=>gf(t&&t.get("info")))),Cg=yr(eo,(t=>gf(t&&t.get("externalDocs")))),sf=yr(Ch,(t=>t&&t.get("version"))),jg=yr(sf,(t=>/v?([0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(t).slice(1))),lf=yr(li,(t=>t.get("paths"))),Pg=vu()(["get","put","post","delete","options","head","patch"]),jh=yr(lf,(t=>{let e=(0,H.List)();return!H.Map.isMap(t)||t.isEmpty()||t.forEach(((r,n)=>{if(!r||!r.forEach)return{};r.forEach(((a,o)=>{Eg.indexOf(o)<0||(e=e.push((0,H.fromJS)({path:n,method:o,operation:a,id:`${o}-${n}`,specPath:["paths",n,o]})))}))})),e})),Ph=yr(eo,(t=>(0,H.Set)(t.get("consumes")))),cf=yr(eo,(t=>(0,H.Set)(t.get("produces")))),uf=yr(eo,(t=>t.get("security",(0,H.List)()))),Nh=yr(eo,(t=>t.get("securityDefinitions"))),Ng=(t,e)=>{const r=t.getIn(["resolvedSubtrees","definitions",e],null),n=t.getIn(["json","definitions",e],null);return r||n||null},pf=yr(eo,(t=>{const e=t.get("definitions");return H.Map.isMap(e)?e:(0,H.Map)()})),Tg=yr(eo,(t=>t.get("basePath"))),Th=yr(eo,(t=>t.get("host"))),Ms=yr(eo,(t=>t.get("schemes",(0,H.Map)()))),bu=yr([jh,Ph,cf],((t,e,r)=>t.map((n=>n.update("operation",(a=>H.Map.isMap(a)?a.withMutations((o=>(o.get("consumes")||o.update("consumes",(p=>(0,H.Set)(p).merge(e))),o.get("produces")||o.update("produces",(p=>(0,H.Set)(p).merge(r))),o))):(0,H.Map)())))))),La=yr(eo,(t=>{const e=t.get("tags",(0,H.List)());return H.List.isList(e)?e.filter((r=>H.Map.isMap(r))):(0,H.List)()})),Ig=(t,e)=>(La(t)||(0,H.List)()).filter(H.Map.isMap).find((r=>r.get("name")===e),(0,H.Map)()),Mg=yr(bu,La,((t,e)=>t.reduce(((r,n)=>{let a=(0,H.Set)(n.getIn(["operation","tags"]));return a.count()<1?r.update("default",(0,H.List)(),(o=>o.push(n))):a.reduce(((o,p)=>o.update(p,(0,H.List)(),(c=>c.push(n)))),r)}),e.reduce(((r,n)=>r.set(n.get("name"),(0,H.List)())),(0,H.OrderedMap)())))),Ih=t=>({getConfigs:e})=>{let{tagsSorter:r,operationsSorter:n}=e();return Mg(t).sortBy(((a,o)=>o),((a,o)=>{let p=typeof r=="function"?r:Nr.tagsSorter[r];return p?p(a,o):null})).map(((a,o)=>{let p=typeof n=="function"?n:Nr.operationsSorter[n],c=p?a.sort(p):a;return(0,H.Map)({tagDetails:Ig(t,o),operations:c})}))},$a=yr(ji,(t=>t.get("responses",(0,H.Map)()))),Rg=yr(ji,(t=>t.get("requests",(0,H.Map)()))),cr=yr(ji,(t=>t.get("mutatedRequests",(0,H.Map)()))),wu=(t,e,r)=>$a(t).getIn([e,r],null),Dg=(t,e,r)=>Rg(t).getIn([e,r],null),Fg=(t,e,r)=>cr(t).getIn([e,r],null),Op=()=>!0,Eu=(t,e,r)=>{const n=li(t).getIn(["paths",...e,"parameters"],(0,H.OrderedMap)()),a=t.getIn(["meta","paths",...e,"parameters"],(0,H.OrderedMap)());return n.map((o=>{const p=a.get(`${r.get("in")}.${r.get("name")}`),c=a.get(`${r.get("in")}.${r.get("name")}.hash-${r.hashCode()}`);return(0,H.OrderedMap)().merge(o,p,c)})).find((o=>o.get("in")===r.get("in")&&o.get("name")===r.get("name")),(0,H.OrderedMap)())},Mh=(t,e,r,n)=>{const a=`${n}.${r}`;return t.getIn(["meta","paths",...e,"parameter_inclusions",a],!1)},Lg=(t,e,r,n)=>{const a=li(t).getIn(["paths",...e,"parameters"],(0,H.OrderedMap)()).find((o=>o.get("in")===n&&o.get("name")===r),(0,H.OrderedMap)());return Eu(t,e,a)},hf=(t,e,r)=>{const n=li(t).getIn(["paths",e,r],(0,H.OrderedMap)()),a=t.getIn(["meta","paths",e,r],(0,H.OrderedMap)()),o=n.get("parameters",(0,H.List)()).map((p=>Eu(t,[e,r],p)));return(0,H.OrderedMap)().merge(n,a).set("parameters",o)};function $g(t,e,r,n){return e=e||[],t.getIn(["me
Line truncated
|
||||||
|
**Possible Reasons:**
|
||||||
|
- CORS
|
||||||
|
- Network Failure
|
||||||
|
- URL scheme must be "http" or "https" for CORS request.`),r.setResponse(t.pathName,t.method,{error:!0,err:L})}))},Yg=({path:t,method:e,...r}={})=>n=>{let{fn:{fetch:a},specSelectors:o,specActions:p}=n,c=o.specJsonWithResolvedSubtrees().toJS(),d=o.operationScheme(t,e),{requestContentType:E,responseContentType:x}=o.contentTypeValues([t,e]).toJS(),k=/xml/i.test(E),P=o.parameterValues([t,e],k).toJS();return p.executeRequest({...r,fetch:a,spec:c,pathName:t,method:e,parameters:P,requestContentType:E,scheme:d,responseContentType:x})};function Xg(t,e){return{type:Ap,payload:{path:t,method:e}}}function _u(t,e){return{type:Pl,payload:{path:t,method:e}}}function Ou(t,e,r){return{type:xu,payload:{scheme:t,path:e,method:r}}}const Qg={[wa]:(t,e)=>typeof e.payload=="string"?t.set("spec",e.payload):t,[fo]:(t,e)=>t.set("url",e.payload+""),[jl]:(t,e)=>t.set("json",nn(e.payload)),[Cp]:(t,e)=>t.setIn(["resolved"],nn(e.payload)),[Pi]:(t,e)=>{const{value:r,path:n}=e.payload;return t.setIn(["resolvedSubtrees",...n],nn(r))},[Kn]:(t,{payload:e})=>{let{path:r,paramName:n,paramIn:a,param:o,value:p,isXml:c}=e,d=o?sa(o):`${a}.${n}`;const E=c?"value_xml":"value";return t.setIn(["meta","paths",...r,"parameters",d,E],(0,H.fromJS)(p))},[yc]:(t,{payload:e})=>{let{pathMethod:r,paramName:n,paramIn:a,includeEmptyValue:o}=e;if(!n||!a)return console.warn("Warning: UPDATE_EMPTY_PARAM_INCLUSION could not generate a paramKey."),t;const p=`${a}.${n}`;return t.setIn(["meta","paths",...r,"parameter_inclusions",p],o)},[Ds]:(t,{payload:{pathMethod:e,isOAS3:r}})=>{const n=li(t).getIn(["paths",...e]),a=Rh(t,e).toJS();return t.updateIn(["meta","paths",...e,"parameters"],(0,H.fromJS)({}),(o=>n.get("parameters",(0,H.List)()).reduce(((p,c)=>{const d=Sa(c,a),E=Mh(t,e,c.get("name"),c.get("in")),x=((k,P,{isOAS3:R=!1,bypassRequiredCheck:L=!1}={})=>{let V=k.get("required"),{schema:Q,parameterContentMediaType:J}=sr(k,{isOAS3:R});return Rr(P,Q,V,L,J,R)})(c,d,{bypassRequiredCheck:E,isOAS3:r});return p.setIn([sa(c),"errors"],(0,H.fromJS)(x))}),o)))},[Zi]:(t,{payload:{pathMethod:e}})=>t.updateIn(["meta","paths",...e,"parameters"],(0,H.fromJS)([]),(r=>r.map((n=>n.set("errors",(0,H.fromJS)([])))))),[Su]:(t,{payload:{res:e,path:r,method:n}})=>{let a;a=e.error?Object.assign({error:!0,name:e.err.name,message:e.err.message,statusCode:e.err.statusCode},e.err.response):e,a.headers=a.headers||{};let o=t.setIn(["responses",r,n],nn(a));return Qe.Blob&&a.data instanceof Qe.Blob&&(o=o.setIn(["responses",r,n,"text"],a.data)),o},[kp]:(t,{payload:{req:e,path:r,method:n}})=>t.setIn(["requests",r,n],nn(e)),[vc]:(t,{payload:{req:e,path:r,method:n}})=>t.setIn(["mutatedRequests",r,n],nn(e)),[bc]:(t,{payload:{path:e,value:r,key:n}})=>{let a=["paths",...e],o=["meta","paths",...e];return t.getIn(["json",...a])||t.getIn(["resolved",...a])||t.getIn(["resolvedSubtrees",...a])?t.setIn([...o,n],(0,H.fromJS)(r)):t},[Ap]:(t,{payload:{path:e,method:r}})=>t.deleteIn(["responses",e,r]),[Pl]:(t,{payload:{path:e,method:r}})=>t.deleteIn(["requests",e,r]),[xu]:(t,{payload:{scheme:e,path:r,method:n}})=>r&&n?t.setIn(["scheme",r,n],e):r||n?void 0:t.setIn(["scheme","_defaultScheme"],e)},Gn=(t,{specActions:e})=>(...r)=>{t(...r),e.parseToJson(...r)},f0=(t,{specActions:e})=>(...r)=>{t(...r),e.invalidateResolvedSubtreeCache();const[n]=r,a=Cn()(n,["paths"])||{};Object.keys(a).forEach((o=>{const p=Cn()(a,[o]);Ai()(p)&&p.$ref&&e.requestResolvedSubtree(["paths",o])})),e.requestResolvedSubtree(["components","securitySchemes"])},Zg=(t,{specActions:e})=>r=>(e.logRequest(r),t(r)),m0=(t,{specSelectors:e})=>r=>t(r,e.isOAS3()),ey=()=>({statePlugins:{spec:{wrapActions:{...z},reducers:{...Qg},actions:{...M},selectors:{...j}}}});var ty=(function(){var t=function(e,r){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,a){n.__proto__=a}||function(n,a){for(var o in a)a.hasOwnProperty(o)&&(n[o]=a[o])},t(e,r)};return function(e,r){function n(){this.constructor=e}t(e,r),e.prototype=r===null?Object.create(r):(n.prototype=r.prototype,new n)}})(),g0=Object.prototype.hasOwnProperty;function Bo(t,e){return g0.call(t,e)}function Pp(t){if(Array.isArray(t)){for(var e=new Array(t.length),r=0;r<e.length;r++)e[r]=""+r;return e}if(Object.keys)return Object.keys(t);var n=[];for(var a in t)Bo(t,a)&&n.push(a);return n}function pa(t){switch(typeof t){case"object":return JSON.parse(JSON.stringify(t));case"undefined":return null;default:return t}}function ku(t){for(var e,r=0,n=t.length;r<n;){if(!((e=t.charCodeAt(r))>=48&&e<=57))return!1;r++}return!0}function Xo(t){return t.indexOf("/")===-1&&t.indexOf("~")===-1?t:t.replace(/~/g,"~0").replace(/\//g,"~1")}function Np(t){return t.replace(/~1/g,"/").replace(/~0/g,"~")}function Tp(t){if(t===void 0)return!0;if(t){if(Array.isArray(t)){for(var e=0,r=t.length;e<r;e++)if(Tp(t[e]))return!0}else if(typeof t=="object"){for(var n=Pp(t),a=n.length,o=0;o<a;o++)if(Tp(t[n[o]]))return!0}}return!1}function qh(t,e){var r=[t];for(var n in e){var a=typeof e[n]=="object"?JSON.stringify(e[n],null,2)
Line truncated
|
||||||
|
`)}var ry=(function(t){function e(r,n,a,o,p){var c=this.constructor,d=t.call(this,qh(r,{name:n,index:a,operation:o,tree:p}))||this;return d.name=n,d.index=a,d.operation=o,d.tree=p,Object.setPrototypeOf(d,c.prototype),d.message=qh(r,{name:n,index:a,operation:o,tree:p}),d}return ty(e,t),e})(Error),zn=ry,ny=pa,Sc={add:function(t,e,r){return t[e]=this.value,{newDocument:r}},remove:function(t,e,r){var n=t[e];return delete t[e],{newDocument:r,removed:n}},replace:function(t,e,r){var n=t[e];return t[e]=this.value,{newDocument:r,removed:n}},move:function(t,e,r){var n=Ml(r,this.path);n&&(n=pa(n));var a=Rl(r,{op:"remove",path:this.from}).removed;return Rl(r,{op:"add",path:this.path,value:a}),{newDocument:r,removed:n}},copy:function(t,e,r){var n=Ml(r,this.from);return Rl(r,{op:"add",path:this.path,value:pa(n)}),{newDocument:r}},test:function(t,e,r){return{newDocument:r,test:xc(t[e],this.value)}},_get:function(t,e,r){return this.value=t[e],{newDocument:r}}},Uh={add:function(t,e,r){return ku(e)?t.splice(e,0,this.value):t[e]=this.value,{newDocument:r,index:e}},remove:function(t,e,r){return{newDocument:r,removed:t.splice(e,1)[0]}},replace:function(t,e,r){var n=t[e];return t[e]=this.value,{newDocument:r,removed:n}},move:Sc.move,copy:Sc.copy,test:Sc.test,_get:Sc._get};function Ml(t,e){if(e=="")return t;var r={op:"_get",path:e};return Rl(t,r),r.value}function Rl(t,e,r,n,a,o){if(r===void 0&&(r=!1),n===void 0&&(n=!0),a===void 0&&(a=!0),o===void 0&&(o=0),r&&(typeof r=="function"?r(e,0,t,e.path):Au(e,0)),e.path===""){var p={newDocument:t};if(e.op==="add")return p.newDocument=e.value,p;if(e.op==="replace")return p.newDocument=e.value,p.removed=t,p;if(e.op==="move"||e.op==="copy")return p.newDocument=Ml(t,e.from),e.op==="move"&&(p.removed=t),p;if(e.op==="test"){if(p.test=xc(t,e.value),p.test===!1)throw new zn("Test operation failed","TEST_OPERATION_FAILED",o,e,t);return p.newDocument=t,p}if(e.op==="remove")return p.removed=t,p.newDocument=null,p;if(e.op==="_get")return e.value=t,p;if(r)throw new zn("Operation `op` property is not one of operations defined in RFC-6902","OPERATION_OP_INVALID",o,e,t);return p}n||(t=pa(t));var c=(e.path||"").split("/"),d=t,E=1,x=c.length,k=void 0,P=void 0,R=void 0;for(R=typeof r=="function"?r:Au;;){if((P=c[E])&&P.indexOf("~")!=-1&&(P=Np(P)),a&&(P=="__proto__"||P=="prototype"&&E>0&&c[E-1]=="constructor"))throw new TypeError("JSON-Patch: modifying `__proto__` or `constructor/prototype` prop is banned for security reasons, if this was on purpose, please set `banPrototypeModifications` flag false and pass it to this function. More info in fast-json-patch README");if(r&&k===void 0&&(d[P]===void 0?k=c.slice(0,E).join("/"):E==x-1&&(k=e.path),k!==void 0&&R(e,0,t,k)),E++,Array.isArray(d)){if(P==="-")P=d.length;else{if(r&&!ku(P))throw new zn("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",o,e,t);ku(P)&&(P=~~P)}if(E>=x){if(r&&e.op==="add"&&P>d.length)throw new zn("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",o,e,t);if((p=Uh[e.op].call(e,d,P,t)).test===!1)throw new zn("Test operation failed","TEST_OPERATION_FAILED",o,e,t);return p}}else if(E>=x){if((p=Sc[e.op].call(e,d,P,t)).test===!1)throw new zn("Test operation failed","TEST_OPERATION_FAILED",o,e,t);return p}if(d=d[P],r&&E<x&&(!d||typeof d!="object"))throw new zn("Cannot perform operation at the desired path","OPERATION_PATH_UNRESOLVABLE",o,e,t)}}function es(t,e,r,n,a){if(n===void 0&&(n=!0),a===void 0&&(a=!0),r&&!Array.isArray(e))throw new zn("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");n||(t=pa(t));for(var o=new Array(e.length),p=0,c=e.length;p<c;p++)o[p]=Rl(t,e[p],r,!0,a,p),t=o[p].newDocument;return o.newDocument=t,o}function ay(t,e,r){var n=Rl(t,e);if(n.test===!1)throw new zn("Test operation failed","TEST_OPERATION_FAILED",r,e,t);return n.newDocument}function Au(t,e,r,n){if(typeof t!="object"||t===null||Array.isArray(t))throw new zn("Operation is not an object","OPERATION_NOT_AN_OBJECT",e,t,r);if(!Sc[t.op])throw new zn("Operation `op` property is not one of operations defined in RFC-6902","OPERATION_OP_INVALID",e,t,r);if(typeof t.path!="string")throw new zn("Operation `path` property is not a string","OPERATION_PATH_INVALID",e,t,r);if(t.path.indexOf("/")!==0&&t.path.length>0)throw new zn('Operation `path` property must start with "/"',"OPERATION_PATH_INVALID",e,t,r);if((t.op==="move"||t.op==="copy")&&typeof t.from!="string")throw new zn("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",e,t,r);if((t.op==="add"||t.op==="replace"||t.op==="test")&&t.value===void 0)throw new zn("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",e,t,r);if((t.op==="add"||t.op==="replace"||t.op==="test")&&Tp(t.value))throw new zn("Operation `value` propert
Line truncated
|
||||||
|
CAUSE: ${a.stack}`)}}};class Fr extends Error{static[Symbol.hasInstance](e){return super[Symbol.hasInstance](e)||Function.prototype[Symbol.hasInstance].call(nr,e)}constructor(e,r){if(super(e,r),this.name=this.constructor.name,typeof e=="string"&&(this.message=e),typeof Error.captureStackTrace=="function"?Error.captureStackTrace(this,this.constructor):this.stack=new Error(e).stack,r!=null&&typeof r=="object"&&Object.hasOwn(r,"cause")&&!("cause"in this)){const{cause:n}=r;this.cause=n,n instanceof Error&&"stack"in n&&(this.stack=`${this.stack}
|
||||||
|
CAUSE: ${n.stack}`)}}}const Mt=Fr,xr=class extends Mt{constructor(e,r){if(super(e,r),r!=null&&typeof r=="object"){const{cause:n,...a}=r;Object.assign(this,a)}}};function Qr(t,e){switch(t){case 0:return function(){return e.apply(this,arguments)};case 1:return function(r){return e.apply(this,arguments)};case 2:return function(r,n){return e.apply(this,arguments)};case 3:return function(r,n,a){return e.apply(this,arguments)};case 4:return function(r,n,a,o){return e.apply(this,arguments)};case 5:return function(r,n,a,o,p){return e.apply(this,arguments)};case 6:return function(r,n,a,o,p,c){return e.apply(this,arguments)};case 7:return function(r,n,a,o,p,c,d){return e.apply(this,arguments)};case 8:return function(r,n,a,o,p,c,d,E){return e.apply(this,arguments)};case 9:return function(r,n,a,o,p,c,d,E,x){return e.apply(this,arguments)};case 10:return function(r,n,a,o,p,c,d,E,x,k){return e.apply(this,arguments)};default:throw new Error("First argument to _arity must be a non-negative integer no greater than ten")}}function sn(t,e){return function(){return e.call(this,t.apply(this,arguments))}}function wr(t){return t!=null&&typeof t=="object"&&t["@@functional/placeholder"]===!0}function _r(t){return function e(r){return arguments.length===0||wr(r)?e:t.apply(this,arguments)}}function Tr(t){return function e(r,n){switch(arguments.length){case 0:return e;case 1:return wr(r)?e:_r((function(a){return t(r,a)}));default:return wr(r)&&wr(n)?e:wr(r)?_r((function(a){return t(a,n)})):wr(n)?_r((function(a){return t(r,a)})):t(r,n)}}}function ha(t){return function e(r,n,a){switch(arguments.length){case 0:return e;case 1:return wr(r)?e:Tr((function(o,p){return t(r,o,p)}));case 2:return wr(r)&&wr(n)?e:wr(r)?Tr((function(o,p){return t(o,n,p)})):wr(n)?Tr((function(o,p){return t(r,o,p)})):_r((function(o){return t(r,n,o)}));default:return wr(r)&&wr(n)&&wr(a)?e:wr(r)&&wr(n)?Tr((function(o,p){return t(o,p,a)})):wr(r)&&wr(a)?Tr((function(o,p){return t(o,n,p)})):wr(n)&&wr(a)?Tr((function(o,p){return t(r,o,p)})):wr(r)?_r((function(o){return t(o,n,a)})):wr(n)?_r((function(o){return t(r,o,a)})):wr(a)?_r((function(o){return t(r,n,o)})):t(r,n,a)}}}const Dl=Array.isArray||function(e){return e!=null&&e.length>=0&&Object.prototype.toString.call(e)==="[object Array]"};function Of(t){return Object.prototype.toString.call(t)==="[object String]"}const iy=_r((function(e){return!!Dl(e)||!!e&&typeof e=="object"&&!Of(e)&&(e.length===0||e.length>0&&e.hasOwnProperty(0)&&e.hasOwnProperty(e.length-1))}));var sy=typeof Symbol<"u"?Symbol.iterator:"@@iterator";function _c(t,e,r){return function(a,o,p){if(iy(p))return t(a,o,p);if(p==null)return o;if(typeof p["fantasy-land/reduce"]=="function")return e(a,o,p,"fantasy-land/reduce");if(p[sy]!=null)return r(a,o,p[sy]());if(typeof p.next=="function")return r(a,o,p);if(typeof p.reduce=="function")return e(a,o,p,"reduce");throw new TypeError("reduce: list must be array or iterable")}}function ly(t,e,r){for(var n=0,a=r.length;n<a;){if((e=t["@@transducer/step"](e,r[n]))&&e["@@transducer/reduced"]){e=e["@@transducer/value"];break}n+=1}return t["@@transducer/result"](e)}const tE=Tr((function(e,r){return Qr(e.length,(function(){return e.apply(r,arguments)}))}));function sA(t,e,r){for(var n=r.next();!n.done;){if((e=t["@@transducer/step"](e,n.value))&&e["@@transducer/reduced"]){e=e["@@transducer/value"];break}n=r.next()}return t["@@transducer/result"](e)}function lA(t,e,r,n){return t["@@transducer/result"](r[n](tE(t["@@transducer/step"],t),e))}const v0=_c(ly,lA,sA);var cA=(function(){function t(e){this.f=e}return t.prototype["@@transducer/init"]=function(){throw new Error("init not implemented on XWrap")},t.prototype["@@transducer/result"]=function(e){return e},t.prototype["@@transducer/step"]=function(e,r){return this.f(e,r)},t})();function rE(t){return new cA(t)}var uA=ha((function(t,e,r){return v0(typeof t=="function"?rE(t):t,e,r)}));const Vh=uA;function b0(t,e){return function(){var r=arguments.length;if(r===0)return e();var n=arguments[r-1];return Dl(n)||typeof n[t]!="function"?e.apply(this,arguments):n[t].apply(n,Array.prototype.slice.call(arguments,0,r-1))}}var pA=ha(b0("slice",(function(e,r,n){return Array.prototype.slice.call(n,e,r)})));const kf=pA,hA=_r(b0("tail",kf(1,1/0)));function mo(){if(arguments.length===0)throw new Error("pipe requires at least one argument");return Qr(arguments[0].length,Vh(sn,arguments[0],hA(arguments)))}const Jh=Tr((function(e,r){return r==null||r!=r?e:r})),Wh=Number.isInteger||function(e){return(e|0)===e};function cy(t,e){var r=t<0?e.length+t:t;return Of(e)?e.charAt(r):e[r]}const Af=Tr((function(e,r){if(r!=null)return Wh(e)?cy(e,r):r[e]})),uy=ha((function(e,r,n){return Jh(e,Af(r,n))}));var dA=_r((function(t){return cy(-1,t)}));const py=dA;function w0(t,e,r){return function(){for(var n=[],a=0,o=t,p=0,c=!1;p<e.length||a<arguments.length;){var d;p<e.length&&(!wr(e[p])||a>=arguments.length)?d=e[p]:(d=arguments[a],a+=1),n[p]=d,wr(d)?c=!0:o-=1,p+=1}return!c&&o<=0?r.apply(this,n):Qr(Math.max(0,o),
Line truncated
|
||||||
|
`,P+=`<root nodes="${x.length/2}" characters="${p.length}">
|
||||||
|
`,P+=`<!-- input string -->
|
||||||
|
`,P+=k(R+2),P+=r.charsToString(p),P+=`
|
||||||
|
`,x.forEach((L=>{L.state===e.SEM_PRE?(R+=1,P+=k(R),P+=`<node name="${L.name}" index="${L.phraseIndex}" length="${L.phraseLength}">
|
||||||
|
`,P+=k(R+2),P+=r.charsToString(p,L.phraseIndex,L.phraseLength),P+=`
|
||||||
|
`):(P+=k(R),P+=`</node><!-- name="${L.name}" -->
|
||||||
|
`,R-=1)})),P+=`</root>
|
||||||
|
`,P}},cj=function(){const e=Br,r=Ni,n="parser.js: Trace(): ";let a,o,p,c="",d=0;const E=this,x=P=>{let R="",L=0;if(P>=0)for(;P--;)L+=1,L===5?(R+="|",L=0):R+=".";return R};E.init=(P,R,L)=>{o=P,p=R,a=L};const k=P=>{let R;switch(P.type){case e.ALT:R="ALT";break;case e.CAT:R="CAT";break;case e.REP:R=P.max===1/0?`REP(${P.min},inf)`:`REP(${P.min},${P.max})`;break;case e.RNM:R=`RNM(${o[P.index].name})`;break;case e.TRG:R=`TRG(${P.min},${P.max})`;break;case e.TBS:R=P.string.length>6?`TBS(${r.charsToString(P.string,0,3)}...)`:`TBS(${r.charsToString(P.string,0,6)})`;break;case e.TLS:R=P.string.length>6?`TLS(${r.charsToString(P.string,0,3)}...)`:`TLS(${r.charsToString(P.string,0,6)})`;break;case e.UDT:R=`UDT(${p[P.index].name})`;break;case e.AND:R="AND";break;case e.NOT:R="NOT";break;default:throw new Error(`${n}Trace: opName: unrecognized opcode`)}return R};E.down=(P,R)=>{const L=x(d),V=Math.min(100,a.length-R);let Q=r.charsToString(a,R,V);V<a.length-R&&(Q+="..."),Q=`${L}|-|[${k(P)}]${Q}
|
||||||
|
`,c+=Q,d+=1},E.up=(P,R,L,V)=>{const Q=`${n}trace.up: `;d-=1;const J=x(d);let re,me,le;switch(R){case e.EMPTY:le="|E|",me="''";break;case e.MATCH:le="|M|",re=Math.min(100,V),me=re<V?`'${r.charsToString(a,L,re)}...'`:`'${r.charsToString(a,L,re)}'`;break;case e.NOMATCH:le="|N|",me="";break;default:throw new Error(`${Q} unrecognized state`)}me=`${J}${le}[${k(P)}]${me}
|
||||||
|
`,c+=me},E.displayTrace=()=>c},uj=function(){const e=Br;let r,n,a;const o=[],p=[],c=[];this.init=(V,Q)=>{r=V,n=Q,R()},this.collect=(V,Q)=>{L(a,Q.state,Q.phraseLength),L(o[V.type],Q.state,Q.phraseLength),V.type===e.RNM&&L(p[V.index],Q.state,Q.phraseLength),V.type===e.UDT&&L(c[V.index],Q.state,Q.phraseLength)},this.displayStats=()=>{let V="";const Q={match:0,empty:0,nomatch:0,total:0},J=(re,me,le,ve,be)=>(Q.match+=me,Q.empty+=le,Q.nomatch+=ve,Q.total+=be,`${re} | ${d(me)} | ${d(le)} | ${d(ve)} | ${d(be)} |
|
||||||
|
`);return V+=` OPERATOR STATS
|
||||||
|
`,V+=` | MATCH | EMPTY | NOMATCH | TOTAL |
|
||||||
|
`,V+=J(" ALT",o[e.ALT].match,o[e.ALT].empty,o[e.ALT].nomatch,o[e.ALT].total),V+=J(" CAT",o[e.CAT].match,o[e.CAT].empty,o[e.CAT].nomatch,o[e.CAT].total),V+=J(" REP",o[e.REP].match,o[e.REP].empty,o[e.REP].nomatch,o[e.REP].total),V+=J(" RNM",o[e.RNM].match,o[e.RNM].empty,o[e.RNM].nomatch,o[e.RNM].total),V+=J(" TRG",o[e.TRG].match,o[e.TRG].empty,o[e.TRG].nomatch,o[e.TRG].total),V+=J(" TBS",o[e.TBS].match,o[e.TBS].empty,o[e.TBS].nomatch,o[e.TBS].total),V+=J(" TLS",o[e.TLS].match,o[e.TLS].empty,o[e.TLS].nomatch,o[e.TLS].total),V+=J(" UDT",o[e.UDT].match,o[e.UDT].empty,o[e.UDT].nomatch,o[e.UDT].total),V+=J(" AND",o[e.AND].match,o[e.AND].empty,o[e.AND].nomatch,o[e.AND].total),V+=J(" NOT",o[e.NOT].match,o[e.NOT].empty,o[e.NOT].nomatch,o[e.NOT].total),V+=J("TOTAL",Q.match,Q.empty,Q.nomatch,Q.total),V},this.displayHits=V=>{let Q="";const J=(re,me,le,ve,be)=>(a.match+=re,a.empty+=me,a.nomatch+=le,a.total+=ve,`| ${d(re)} | ${d(me)} | ${d(le)} | ${d(ve)} | ${be}
|
||||||
|
`);typeof V=="string"&&V.toLowerCase()[0]==="a"?(p.sort(E),c.sort(E),Q+=` RULES/UDTS ALPHABETICALLY
|
||||||
|
`):typeof V=="string"&&V.toLowerCase()[0]==="i"?(p.sort(k),c.sort(k),Q+=` RULES/UDTS BY INDEX
|
||||||
|
`):(p.sort(x),c.sort(x),Q+=` RULES/UDTS BY HIT COUNT
|
||||||
|
`),Q+=`| MATCH | EMPTY | NOMATCH | TOTAL | NAME
|
||||||
|
`;for(let re=0;re<p.length;re+=1){let me=p[re];me.total&&(Q+=J(me.match,me.empty,me.nomatch,me.total,me.name))}for(let re=0;re<c.length;re+=1){let me=c[re];me.total&&(Q+=J(me.match,me.empty,me.nomatch,me.total,me.name))}return Q};const d=V=>V<10?` ${V}`:V<100?` ${V}`:V<1e3?` ${V}`:V<1e4?` ${V}`:V<1e5?` ${V}`:V<1e6?` ${V}`:`${V}`,E=(V,Q)=>V.lower<Q.lower?-1:V.lower>Q.lower?1:0,x=(V,Q)=>V.total<Q.total?1:V.total>Q.total?-1:E(V,Q),k=(V,Q)=>V.index<Q.index?-1:V.index>Q.index?1:0,P=function(){this.empty=0,this.match=0,this.nomatch=0,this.total=0},R=()=>{o.length=0,a=new P,o[e.ALT]=new P,o[e.CAT]=new P,o[e.REP]=new P,o[e.RNM]=new P,o[e.TRG]=new P,o[e.TBS]=new P,o[e.TLS]=new P,o[e.UDT]=new P,o[e.AND]=new P,o[e.NOT]=new P,p.length=0;for(let V=0;V<r.length;V+=1)p.push({empty:0,match:0,nomatch:0,total:0,name:r[V].name,lower:r[V].lower,index:r[V].index});if(n.length>0){c.length=0;for(let V=0;V<n.length;V+=1)c.push({empty:0,match:0,nomatch:0,total:0,name:n[V].name,lower:n[V].lower,index:n[V].index})}},L=(V,Q)=>{switch(V.total+=1,Q){case e.EMPTY:V.empty+=1;break;case e.MATCH:V.match+=1;break;case e.NOMATCH:V.nomatch+=1;break;default:throw new Error(`parser.js: Stats(): collect(): incStat(): unrecognized state: ${Q}`)}}},Ni={stringToChars:t=>[...t].map((e=>e.codePointAt(0))),charsToString:(t,e,r)=>{let n=t;for(;!(e===void 0||e<0);){if(r===void 0){n=t.slice(e);break}if(r<=0)return"";n=t.slice(e,e+r);break}return String.fromCodePoint(...n)}},Br={ALT:1,CAT:2,REP:3,RNM:4,TRG:5,TBS:6,TLS:7,UDT:11,AND:12,NOT:13,ACTIVE:100,MATCH:101,EMPTY:102,NOMATCH:103,SEM_PRE:200,SEM_POST:201,SEM_OK:300,idName:t=>{switch(t){case Br.ALT:return"ALT";case Br.CAT:return"CAT";case Br.REP:return"REP";case Br.RNM:return"RNM";case Br.TRG:return"TRG";case Br.TBS:return"TBS";case Br.TLS:return"TLS";case Br.UDT:return"UDT";case Br.AND:return"AND";case Br.NOT:return"NOT";case Br.ACTIVE:return"ACTIVE";case Br.EMPTY:return"EMPTY";case Br.MATCH:return"MATCH";case Br.NOMATCH:return"NOMATCH";case Br.SEM_PRE:return"SEM_PRE";case Br.SEM_POST:return"SEM_POST";case Br.SEM_OK:return"SEM_OK";default:return"UNRECOGNIZED STATE"}}};function Uf(){this.grammarObject="grammarObject",this.rules=[],this.rules[0]={name:"json-pointer",lower:"json-pointer",index:0,isBkr:!1},this.rules[1]={name:"reference-token",lower:"reference-token",index:1,isBkr:!1},this.rules[2]={name:"unescaped",lower:"unescaped",index:2,isBkr:!1},this.rules[3]={name:"escaped",lower:"escaped",index:3,isBkr:!1},this.rules[4]={name:"array-location",lower:"array-location",index:4,isBkr:!1},this.rules[5]={name:"array-index",lower:"array-index",index:5,isBkr:!1},this.rules[6]={name:"array-dash",lower:"array-dash",index:6,isBkr:!1},this.rules[7]={name:"slash",lower:"slash",index:7,isBkr:!1},this.udts=[],this.rules[0].opcodes=[],this.rules[0].opcodes[0]={type:3,min:0,max:1/0},this.rules[0].opcodes[1]={type:2,children:[2,3]},this.rules[0].opcodes[2]={type:4,index:7},this.rules[0].opcodes[3]={type:4,index:1},this.rules[1].opcodes=[],this.rules[1].opcodes[0]={type:3,min:0,max:1/0},this.rules[1].opcodes[1]={type:1,children:[2,3]},this.rules[1].opcodes[2]={type:4,index:2},this.rules[1].opcodes[3]={type:4,index:3},this.rules[2].opcodes=[],this.rules[2].opcodes[0]={type:1,children:[1,2,3]},this.rules[2].opcodes[1]={type:5,min:0,max:46},this.rules[2].opcodes[2]={type:5,min:48,max:125},this.rules[2].opcodes[3]={type:5,min:127,max:1114111},this.rules[3].opcodes=[],this.rules[3].opcodes[0]={type:2,children:[1,2]},this.rules[3].opcodes[1]={type:7,string:[126]},this.rules[3].opcodes[2]={type:1,children:[3,4]},this.rules[3].opcodes[3]={type:7,string:[48]},this.rules[3].opcodes[4]={type:7,string:[49]},this.rules[4].opcodes=[],this.rules[4].opcodes[0]={type:1,children:[1,2]},this.rules[4].opcodes[1]={type:4,index:5},this.rules[4].opcodes[2]={type:4,index:6},this.rules[5].opcodes=[],this.rules[5].opcodes[0]={type:1,children:[1,2]},this.rules[5].opcodes[1]={type:6,string:[48]},this.rules[5].opcodes[2]={type:2,children:[3,4]},this.rules[5].opcodes[3]={type:5,min:49,max:57},this.rules[5].opcodes[4]={type:3,min:0,max:1/0},this.rules[5].opcodes[5]={type:5,min:48,max:57},this.rules[6].opcodes=[],this.rules[6].opcodes[0]={type:7,string:[45]},this.rules[7].opcodes=[],this.rules[7].opcodes[0]={type:7,string:[47]},this.toString=function(){return`; JavaScript Object Notation (JSON) Pointer ABNF syntax
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc6901
|
||||||
|
json-pointer = *( slash reference-token ) ; MODIFICATION: surrogate text rule used
|
||||||
|
reference-token = *( unescaped / escaped )
|
||||||
|
unescaped = %x00-2E / %x30-7D / %x7F-10FFFF
|
||||||
|
; %x2F ('/') and %x7E ('~') are excluded from 'unescaped'
|
||||||
|
escaped = "~" ( "0" / "1" )
|
||||||
|
; representing '~' and '/', respectively
|
||||||
|
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc6901#section-4
|
||||||
|
array-location = array-index / array-dash
|
||||||
|
array-index = %x30 / ( %x31-39 *(%x30-39) )
|
||||||
|
; "0", or digits without a leading "0"
|
||||||
|
array-dash = "-"
|
||||||
|
|
||||||
|
; Surrogate named rules
|
||||||
|
slash = "/"
|
||||||
|
`}}class pj extends Error{constructor(e,r=void 0){if(super(e,r),this.name=this.constructor.name,typeof e=="string"&&(this.message=e),typeof Error.captureStackTrace=="function"?Error.captureStackTrace(this,this.constructor):this.stack=new Error(e).stack,r!=null&&typeof r=="object"&&Object.prototype.hasOwnProperty.call(r,"cause")&&!("cause"in this)){const{cause:n}=r;this.cause=n,n instanceof Error&&"stack"in n&&(this.stack=`${this.stack}
|
||||||
|
CAUSE: ${n.stack}`)}if(r!=null&&typeof r=="object"){const{cause:n,...a}=r;Object.assign(this,a)}}}const Du=pj,xS=class extends Du{},sb=t=>(e,r,n,a,o)=>{if(typeof o!="object"||o===null||Array.isArray(o))throw new xS("parser's user data must be an object");if(e===Br.SEM_PRE){const p={type:t,text:Ni.charsToString(r,n,a),start:n,length:a,children:[]};o.stack.length>0?o.stack[o.stack.length-1].children.push(p):o.root=p,o.stack.push(p)}e===Br.SEM_POST&&o.stack.pop()},hj=class extends ib{constructor(){super(),this.callbacks["json-pointer"]=sb("json-pointer"),this.callbacks["reference-token"]=sb("reference-token"),this.callbacks.slash=sb("text")}getTree(){const e={stack:[],root:null};return this.translate(e),delete e.stack,e}},dj=t=>{if(typeof t!="string")throw new TypeError("Reference token must be a string");return t.replace(/~1/g,"/").replace(/~0/g,"~")},fj=class extends hj{getTree(){const{root:e}=super.getTree();return e.children.filter((({type:r})=>r==="reference-token")).map((({text:r})=>dj(r)))}},mj=class extends Array{toString(){return this.map((e=>`"${String(e)}"`)).join(", ")}},gj=class extends cj{inferExpectations(){const e=this.displayTrace().split(`
|
||||||
|
`),r=new Set;let n=-1;for(let a=0;a<e.length;a++){const o=e[a];if(o.includes("M|")){const p=o.match(/]'(.*)'$/);p&&p[1]&&(n=a)}if(a>n){const p=o.match(/N\|\[TLS\(([^)]+)\)]/);p&&r.add(p[1])}}return new mj(...r)}},yj=new Uf,vj=(t,{translator:e=new fj,stats:r=!1,trace:n=!1}={})=>{if(typeof t!="string")throw new TypeError("JSON Pointer must be a string");try{const a=new ns;e&&(a.ast=e),r&&(a.stats=new uj),n&&(a.trace=new gj);const o=a.parse(yj,"json-pointer",t);return{result:o,tree:o.success&&e?a.ast.getTree():void 0,stats:a.stats,trace:a.trace}}catch(a){throw new xS("Unexpected error during JSON Pointer parsing",{cause:a,jsonPointer:t})}};new Uf,new ns,new Uf,new ns;const bj=new Uf,wj=new ns,Ej=t=>{if(typeof t!="string")return!1;try{return wj.parse(bj,"array-index",t).success}catch{return!1}},Sj=new Uf,xj=new ns,_j=t=>{if(typeof t!="string")return!1;try{return xj.parse(Sj,"array-dash",t).success}catch{return!1}},Oj=t=>{if(typeof t!="string"&&typeof t!="number")throw new TypeError("Reference token must be a string or number");return String(t).replace(/~/g,"~0").replace(/\//g,"~1")},kj=class extends Du{},_S=t=>{if(!Array.isArray(t))throw new TypeError("Reference tokens must be a list of strings or numbers");try{return t.length===0?"":`/${t.map((e=>{if(typeof e!="string"&&typeof e!="number")throw new TypeError("Reference token must be a string or number");return Oj(String(e))})).join("/")}`}catch(e){throw new kj("Unexpected error during JSON Pointer compilation",{cause:e,referenceTokens:t})}},Aj=class{#e;#t;#r;constructor(e,r={}){this.#e=e,this.#e.steps=[],this.#e.failed=!1,this.#e.failedAt=-1,this.#e.message=`JSON Pointer "${r.jsonPointer}" was successfully evaluated against the provided value`,this.#e.context={...r,realm:r.realm.name},this.#t=[],this.#r=r.realm}step({referenceToken:e,input:r,output:n,success:a=!0,reason:o}){const p=this.#t.length;this.#t.push(e);const c={referenceToken:e,referenceTokenPosition:p,input:r,inputType:this.#r.isObject(r)?"object":this.#r.isArray(r)?"array":"unrecognized",output:n,success:a};o&&(c.reason=o),this.#e.steps.push(c),a||(this.#e.failed=!0,this.#e.failedAt=p,this.#e.message=o)}},OS=class{name="";isArray(e){throw new Du("Realm.isArray(node) must be implemented in a subclass")}isObject(e){throw new Du("Realm.isObject(node) must be implemented in a subclass")}sizeOf(e){throw new Du("Realm.sizeOf(node) must be implemented in a subclass")}has(e,r){throw new Du("Realm.has(node) must be implemented in a subclass")}evaluate(e,r){throw new Du("Realm.evaluate(node) must be implemented in a subclass")}},Bp=class extends Du{},Qh=class extends Bp{},Cj=class extends OS{name="json";isArray(e){return Array.isArray(e)}isObject(e){return typeof e=="object"&&e!==null&&!this.isArray(e)}sizeOf(e){return this.isArray(e)?e.length:this.isObject(e)?Object.keys(e).length:0}has(e,r){if(this.isArray(e)){const n=Number(r),a=n>>>0;if(n!==a)throw new Qh(`Invalid array index "${r}": index must be an unsinged 32-bit integer`,{referenceToken:r,currentValue:e,realm:this.name});return a<this.sizeOf(e)&&Object.prototype.hasOwnProperty.call(e,n)}return!!this.isObject(e)&&Object.prototype.hasOwnProperty.call(e,r)}evaluate(e,r){return this.isArray(e)?e[Number(r)]:e[r]}},jj=class extends Bp{},kS=class extends Bp{},Pj=(t,e,{strictArrays:r=!0,strictObjects:n=!0,realm:a=new Cj,trace:o=!0}={})=>{const{result:p,tree:c,trace:d}=vj(e,{trace:!!o}),E=typeof o=="object"&&o!==null?new Aj(o,{jsonPointer:e,referenceTokens:c,strictArrays:r,strictObjects:n,realm:a,value:t}):null;try{let x;if(!p.success){let k=`Invalid JSON Pointer: "${e}". Syntax error at position ${p.maxMatched}`;throw k+=d?`, expected ${d.inferExpectations()}`:"",new Bp(k,{jsonPointer:e,currentValue:t,realm:a.name})}return c.reduce(((k,P,R)=>{if(a.isArray(k)){if(_j(P)){if(r)throw new Qh(`Invalid array index "-" at position ${R} in "${e}". The "-" token always refers to a nonexistent element during evaluation`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name});return x=a.evaluate(k,String(a.sizeOf(k))),E?.step({referenceToken:P,input:k,output:x}),x}if(!Ej(P))throw new Qh(`Invalid array index "${P}" at position ${R} in "${e}": index MUST be "0", or digits without a leading "0"`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name});const L=Number(P);if(!Number.isSafeInteger(L))throw new Qh(`Invalid array index "${P}" at position ${R} in "${e}": index must be a safe integer`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name});if(!a.has(k,P)&&r)throw new Qh(`Invalid array index "${P}" at position ${R} in "${e}": index not found in array`,{jsonPointer:e,referenceTokens:c,referenceToken:P,referenceTokenPosition:R,currentValue:k,realm:a.name});return x=a.evaluate(k,P),E?.step({referenceToken:P,input:k,output:x}),x}if(a.isObject(k)){if(!a.has(k,P)&&n)throw new kS(`Invalid object key "${P}" at position ${R} in "Line truncated
|
||||||
|
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)})()}function pb(t,e){(e==null||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);r<e;r++)n[r]=t[r];return n}var W3=mo($3(F3((function(t,e){return t.length>e.length}))),q3,Af("length")),H3=E0((function(t,e,r){var n=r.apply(void 0,J3(t));return z3(n)?U3(n):e}));const US=ub(V3,(function(e){var r=W3(e);return ja(r,(function(){for(var n=arguments.length,a=new Array(n),o=0;o<n;o++)a[o]=arguments[o];return Vh(H3(a),void 0,e)}))}),vy),K3=class extends Ti{alternator;constructor({alternator:e,...r}){super({...r}),this.alternator=e}enter(e){const r=this.alternator.map((({predicate:a,specPath:o})=>ub(a,jr(o),vy))),n=US(r)(e);return this.element=this.toRefractedElement(n,e),on}},rd=class extends K3{constructor(e){super(e),this.alternator=[{predicate:Ul,specPath:["document","objects","JSONReference"]},{predicate:kc,specPath:["document","objects","JSONSchema"]}]}};class G3 extends Vt(Up,gn){constructor(e){super(e),this.element=new cm,this.specPath=jr(["document","objects","Media"])}}const Y3=G3;class X3 extends Vt(Up,gn){constructor(e){super(e),this.element=new um,this.specPath=jr(["document","objects","LinkDescription"])}}const zS=X3,bo={visitors:{value:gn,JSONSchemaOrJSONReferenceVisitor:rd,document:{objects:{JSONSchema:{$visitor:BS,fixedFields:{id:{$ref:"#/visitors/value"},$schema:{$ref:"#/visitors/value"},multipleOf:{$ref:"#/visitors/value"},maximum:{$ref:"#/visitors/value"},exclusiveMaximum:{$ref:"#/visitors/value"},minimum:{$ref:"#/visitors/value"},exclusiveMinimum:{$ref:"#/visitors/value"},maxLength:{$ref:"#/visitors/value"},minLength:{$ref:"#/visitors/value"},pattern:{$ref:"#/visitors/value"},additionalItems:rd,items:qS,maxItems:{$ref:"#/visitors/value"},minItems:{$ref:"#/visitors/value"},uniqueItems:{$ref:"#/visitors/value"},maxProperties:{$ref:"#/visitors/value"},minProperties:{$ref:"#/visitors/value"},required:f3,properties:y3,additionalProperties:rd,patternProperties:b3,dependencies:E3,enum:S3,type:x3,allOf:O3,anyOf:A3,oneOf:j3,not:rd,definitions:N3,title:{$ref:"#/visitors/value"},description:{$ref:"#/visitors/value"},default:{$ref:"#/visitors/value"},format:{$ref:"#/visitors/value"},base:{$ref:"#/visitors/value"},links:I3,media:{$ref:"#/visitors/document/objects/Media"},readOnly:{$ref:"#/visitors/value"}}},JSONReference:{$visitor:R3,fixedFields:{$ref:D3}},Media:{$visitor:Y3,fixedFields:{binaryEncoding:{$ref:"#/visitors/value"},type:{$ref:"#/visitors/value"}}},LinkDescription:{$visitor:zS,fixedFields:{href:{$ref:"#/visitors/value"},rel:{$ref:"#/visitors/value"},title:{$ref:"#/visitors/value"},targetSchema:rd,mediaType:{$ref:"#/visitors/value"},method:{$ref:"#/visitors/value"},encType:{$ref:"#/visitors/value"},schema:rd}}}}}},hm=t=>{if(da(t))return`${t.element.charAt(0).toUpperCase()+t.element.slice(1)}Element`},Q3={JSONSchemaDraft4Element:["content"],JSONReferenceElement:["content"],MediaElement:["content"],LinkDescriptionElement:["content"],...Bl},Z3={namespace:t=>{const{base:e}=t;return e.register("jSONSchemaDraft4",Zh),e.register("jSONReference",ed),e.register("media",cm),e.register("linkDescription",um),e}},eP=Z3,tP=()=>{const t=Ac(eP);return{predicates:{...X,isStringElement:Ir},namespace:t}},rP=(t,{specPath:e=["visitors","document","objects","JSONSchema","$visitor"],plugins:r=[],specificationObj:n=bo}={})=>{const a=(0,Ge.e)(t),o=Ru(n),p=new(ro(e,o))({specObj:o});return Pa(a,p),ql(p.element,r,{toolboxCreator:tP,visitorOptions:{keyMap:Q3,nodeTypeGetter:hm}})},By=t=>(e,r={})=>rP(e,{specPath:t,...r});Zh.refract=By(["visitors","document","objects","JSONSchema","$visitor"]),ed.refract=By(["visitors","document","objects","JSONReference","$visitor"]),cm.refract=By(["visitors","document","objects","Media","$visitor"]),um.refract=By(["visitors","document","objects","LinkDescription","$visitor"]);const qy=class extends Zh{constructor(e,r,n){super(e,r,n),this.element="schema",this.classes.push("json-schema-draft-4")}get idProp(){throw new Zr("idProp getter in Schema class is not not supported.")}set idProp(e){throw new Zr("idProp setter in Schema class is not not supported.")}get $schema(){throw new Zr("$schema getter in Schema class is not not supported.")}set $schema(e){throw new Zr("$schema setter in Schema class is not not supported.")}get additionalItems(){return this.get("additionalItems")}set additionalItems(e){this.set("additionalItems",e)}get items(){return this.get("items")}set items(e){this.set("items",e)}get additionalProperties(){return this.get("additionalProperties")}set additionalProperties(e){this.set("additionalProperties",e)}get patternProperties(){throw new Zr("patternProperties getter in Schema class is not not supported.")}set patternProperties(e){throw new Zr("patternProperties setter in Schema class is not not supported.")}get dependencies(){throw new Zr("dependencies getter in Schema class is not not supported.")}set dependencies(e){throw new Zr("dependencies setter in Schema class is not not supported.")}get
Line truncated
|
||||||
|
server-url-template = 1*( literals / server-variable ) ; variant of https://www.rfc-editor.org/rfc/rfc6570#section-2
|
||||||
|
server-variable = "{" server-variable-name "}"
|
||||||
|
server-variable-name = 1*( %x00-7A / %x7C / %x7E-10FFFF ) ; every UTF8 character except { and } (from OpenAPI)
|
||||||
|
|
||||||
|
; https://www.rfc-editor.org/rfc/rfc6570#section-2.1
|
||||||
|
; https://www.rfc-editor.org/errata/eid6937
|
||||||
|
literals = 1*( %x21 / %x23-24 / %x26-3B / %x3D / %x3F-5B
|
||||||
|
/ %x5D / %x5F / %x61-7A / %x7E / ucschar / iprivate
|
||||||
|
/ pct-encoded)
|
||||||
|
; any Unicode character except: CTL, SP,
|
||||||
|
; DQUOTE, "%" (aside from pct-encoded),
|
||||||
|
; "<", ">", "\\", "^", "\`", "{", "|", "}"
|
||||||
|
|
||||||
|
; https://www.rfc-editor.org/rfc/rfc6570#section-1.5
|
||||||
|
DIGIT = %x30-39 ; 0-9
|
||||||
|
HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" ; case-insensitive
|
||||||
|
|
||||||
|
pct-encoded = "%" HEXDIG HEXDIG
|
||||||
|
|
||||||
|
ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
|
||||||
|
/ %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
|
||||||
|
/ %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
|
||||||
|
/ %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
|
||||||
|
/ %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
|
||||||
|
/ %xD0000-DFFFD / %xE1000-EFFFD
|
||||||
|
|
||||||
|
iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD
|
||||||
|
`}},ex=t=>{const e=new ns;return e.ast=new ib,e.ast.callbacks["server-url-template"]=lM,e.ast.callbacks["server-variable"]=cM,e.ast.callbacks["server-variable-name"]=uM,e.ast.callbacks.literals=pM,{result:e.parse(hM,"server-url-template",t),ast:e.ast}},dM=(t,{strict:e=!1}={})=>{try{const r=ex(t);if(!r.result.success)return!1;const n=[];r.ast.translate(n);const a=n.some((([o])=>o==="server-variable"));if(!e&&!a)try{return new URL(t,"https://vladimirgorej.com"),!0}catch{return!1}return!e||a}catch{return!1}},fM=t=>(e=>{try{return typeof e=="string"&&decodeURIComponent(e)!==e}catch{return!1}})(t)?t:encodeURIComponent(t).replace(/%5B/g,"[").replace(/%5D/g,"]"),mM=["literals","server-variable-name"],gM=(t,e,r={})=>{const n={encoder:fM,...r},a=ex(t);if(!a.result.success)return t;const o=[];return a.ast.translate(o),o.filter((([c])=>mM.includes(c))).map((([c,d])=>c==="server-variable-name"?Object.hasOwn(e,d)?n.encoder(e[d],d):`{${d}}`:d)).join("")};function tx(){this.grammarObject="grammarObject",this.rules=[],this.rules[0]={name:"path-template",lower:"path-template",index:0,isBkr:!1},this.rules[1]={name:"path-segment",lower:"path-segment",index:1,isBkr:!1},this.rules[2]={name:"slash",lower:"slash",index:2,isBkr:!1},this.rules[3]={name:"path-literal",lower:"path-literal",index:3,isBkr:!1},this.rules[4]={name:"template-expression",lower:"template-expression",index:4,isBkr:!1},this.rules[5]={name:"template-expression-param-name",lower:"template-expression-param-name",index:5,isBkr:!1},this.rules[6]={name:"pchar",lower:"pchar",index:6,isBkr:!1},this.rules[7]={name:"unreserved",lower:"unreserved",index:7,isBkr:!1},this.rules[8]={name:"pct-encoded",lower:"pct-encoded",index:8,isBkr:!1},this.rules[9]={name:"sub-delims",lower:"sub-delims",index:9,isBkr:!1},this.rules[10]={name:"ALPHA",lower:"alpha",index:10,isBkr:!1},this.rules[11]={name:"DIGIT",lower:"digit",index:11,isBkr:!1},this.rules[12]={name:"HEXDIG",lower:"hexdig",index:12,isBkr:!1},this.udts=[],this.rules[0].opcodes=[],this.rules[0].opcodes[0]={type:2,children:[1,2,6]},this.rules[0].opcodes[1]={type:4,index:2},this.rules[0].opcodes[2]={type:3,min:0,max:1/0},this.rules[0].opcodes[3]={type:2,children:[4,5]},this.rules[0].opcodes[4]={type:4,index:1},this.rules[0].opcodes[5]={type:4,index:2},this.rules[0].opcodes[6]={type:3,min:0,max:1},this.rules[0].opcodes[7]={type:4,index:1},this.rules[1].opcodes=[],this.rules[1].opcodes[0]={type:3,min:1,max:1/0},this.rules[1].opcodes[1]={type:1,children:[2,3]},this.rules[1].opcodes[2]={type:4,index:3},this.rules[1].opcodes[3]={type:4,index:4},this.rules[2].opcodes=[],this.rules[2].opcodes[0]={type:7,string:[47]},this.rules[3].opcodes=[],this.rules[3].opcodes[0]={type:3,min:1,max:1/0},this.rules[3].opcodes[1]={type:4,index:6},this.rules[4].opcodes=[],this.rules[4].opcodes[0]={type:2,children:[1,2,3]},this.rules[4].opcodes[1]={type:7,string:[123]},this.rules[4].opcodes[2]={type:4,index:5},this.rules[4].opcodes[3]={type:7,string:[125]},this.rules[5].opcodes=[],this.rules[5].opcodes[0]={type:3,min:1,max:1/0},this.rules[5].opcodes[1]={type:1,children:[2,3,4]},this.rules[5].opcodes[2]={type:5,min:0,max:122},this.rules[5].opcodes[3]={type:6,string:[124]},this.rules[5].opcodes[4]={type:5,min:126,max:1114111},this.rules[6].opcodes=[],this.rules[6].opcodes[0]={type:1,children:[1,2,3,4,5]},this.rules[6].opcodes[1]={type:4,index:7},this.rules[6].opcodes[2]={type:4,index:8},this.rules[6].opcodes[3]={type:4,index:9},this.rules[6].opcodes[4]={type:7,string:[58]},this.rules[6].opcodes[5]={type:7,string:[64]},this.rules[7].opcodes=[],this.rules[7].opcodes[0]={type:1,children:[1,2,3,4,5,6]},this.rules[7].opcodes[1]={type:4,index:10},this.rules[7].opcodes[2]={type:4,index:11},this.rules[7].opcodes[3]={type:7,string:[45]},this.rules[7].opcodes[4]={type:7,string:[46]},this.rules[7].opcodes[5]={type:7,string:[95]},this.rules[7].opcodes[6]={type:7,string:[126]},this.rules[8].opcodes=[],this.rules[8].opcodes[0]={type:2,children:[1,2,3]},this.rules[8].opcodes[1]={type:7,string:[37]},this.rules[8].opcodes[2]={type:4,index:12},this.rules[8].opcodes[3]={type:4,index:12},this.rules[9].opcodes=[],this.rules[9].opcodes[0]={type:1,children:[1,2,3,4,5,6,7,8,9,10,11]},this.rules[9].opcodes[1]={type:7,string:[33]},this.rules[9].opcodes[2]={type:7,string:[36]},this.rules[9].opcodes[3]={type:7,string:[38]},this.rules[9].opcodes[4]={type:7,string:[39]},this.rules[9].opcodes[5]={type:7,string:[40]},this.rules[9].opcodes[6]={type:7,string:[41]},this.rules[9].opcodes[7]={type:7,string:[42]},this.rules[9].opcodes[8]={type:7,string:[43]},this.rules[9].opcodes[9]={type:7,string:[44]},this.rules[9].opcodes[10]={type:7,string:[59]},this.rules[9].opcodes[11]={type:7,string:[61]},this.rules[10].opcodes=[],this.rules[10].opcodes[0]={type:1,children:[1,2]},this.rules[10].opcodes[1]={type:5,min:65,max:90},this.rules[10].opcodes[2]={type:5,min:97,max:122},this.rules[11].opcodes=[],this.rules[11].opcodes[0]={type:5,min:48,max:57},this.rules[12].opcodes=[],this.rules[12].opcodes[0]
Line truncated
|
||||||
|
; variant of https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
|
||||||
|
path-template = slash *( path-segment slash ) [ path-segment ]
|
||||||
|
path-segment = 1*( path-literal / template-expression )
|
||||||
|
slash = "/"
|
||||||
|
path-literal = 1*pchar
|
||||||
|
template-expression = "{" template-expression-param-name "}"
|
||||||
|
template-expression-param-name = 1*( %x00-7A / %x7C / %x7E-10FFFF ) ; every UTF8 character except { and } (from OpenAPI)
|
||||||
|
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
|
||||||
|
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||||
|
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
|
||||||
|
pct-encoded = "%" HEXDIG HEXDIG
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc3986#section-2.1
|
||||||
|
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||||
|
/ "*" / "+" / "," / ";" / "="
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
|
||||||
|
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc5234#appendix-B.1
|
||||||
|
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
|
||||||
|
DIGIT = %x30-39 ; 0-9
|
||||||
|
HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
|
||||||
|
`}}const yM=(t,e,r,n,a)=>(t===Br.SEM_PRE?a.push(["slash",Ni.charsToString(e,r,n)]):Br.SEM_POST,Br.SEM_OK),vM=(t,e,r,n,a)=>{if(t===Br.SEM_PRE){if(Array.isArray(a)===!1)throw new Error("parser's user data must be an array");a.push(["path-template",Ni.charsToString(e,r,n)])}return Br.SEM_OK},bM=(t,e,r,n,a)=>(t===Br.SEM_PRE?a.push(["path-literal",Ni.charsToString(e,r,n)]):Br.SEM_POST,Br.SEM_OK),wM=(t,e,r,n,a)=>(t===Br.SEM_PRE?a.push(["template-expression",Ni.charsToString(e,r,n)]):Br.SEM_POST,Br.SEM_OK),EM=(t,e,r,n,a)=>(t===Br.SEM_PRE?a.push(["template-expression-param-name",Ni.charsToString(e,r,n)]):Br.SEM_POST,Br.SEM_OK),SM=new tx,xM=t=>{const e=new ns;return e.ast=new ib,e.ast.callbacks["path-template"]=vM,e.ast.callbacks.slash=yM,e.ast.callbacks["path-literal"]=bM,e.ast.callbacks["template-expression"]=wM,e.ast.callbacks["template-expression-param-name"]=EM,{result:e.parse(SM,"path-template",t),ast:e.ast}},_M=t=>(e=>{try{return typeof e=="string"&&decodeURIComponent(e)!==e}catch{return!1}})(t)?t:encodeURIComponent(t).replace(/%5B/g,"[").replace(/%5D/g,"]"),OM=["slash","path-literal","template-expression-param-name"],x1=(t,e,r={})=>{const n={encoder:_M,...r},a=xM(t);if(!a.result.success)return t;const o=[];return a.ast.translate(o),o.filter((([c])=>OM.includes(c))).map((([c,d])=>c==="template-expression-param-name"?Object.prototype.hasOwnProperty.call(e,d)?n.encoder(e[d],d):`{${d}}`:d)).join("")};new tx,new ns;const kM={body:function({req:e,value:r}){r!==void 0&&(e.body=r)},header:function({req:e,parameter:r,value:n}){e.headers=e.headers||{},n!==void 0&&(e.headers[r.name]=n)},query:function({req:e,value:r,parameter:n}){if(e.query=e.query||{},r===!1&&n.type==="boolean"&&(r="false"),r===0&&["number","integer"].indexOf(n.type)>-1&&(r="0"),r)e.query[n.name]={collectionFormat:n.collectionFormat,value:r};else if(n.allowEmptyValue&&r!==void 0){const a=n.name;e.query[a]=e.query[a]||{},e.query[a].allowEmptyValue=!0}},path:function({req:e,value:r,parameter:n,baseURL:a}){if(r!==void 0){const o=e.url.replace(a,""),p=x1(o,{[n.name]:r});e.url=a+p}},formData:function({req:e,value:r,parameter:n}){if(r===!1&&n.type==="boolean"&&(r="false"),r===0&&["number","integer"].indexOf(n.type)>-1&&(r="0"),r)e.form=e.form||{},e.form[n.name]={collectionFormat:n.collectionFormat,value:r};else if(n.allowEmptyValue&&r!==void 0){e.form=e.form||{};const a=n.name;e.form[a]=e.form[a]||{},e.form[a].allowEmptyValue=!0}}};function bv(t,e){return Yn()(e).call(e,"application/json")?typeof t=="string"?t:(Array.isArray(t)&&(t=t.map((r=>{try{return JSON.parse(r)}catch{return r}}))),JSON.stringify(t)):String(t)}function Pm(){this.grammarObject="grammarObject",this.rules=[],this.rules[0]={name:"lenient-cookie-string",lower:"lenient-cookie-string",index:0,isBkr:!1},this.rules[1]={name:"lenient-cookie-entry",lower:"lenient-cookie-entry",index:1,isBkr:!1},this.rules[2]={name:"lenient-cookie-pair",lower:"lenient-cookie-pair",index:2,isBkr:!1},this.rules[3]={name:"lenient-cookie-pair-invalid",lower:"lenient-cookie-pair-invalid",index:3,isBkr:!1},this.rules[4]={name:"lenient-cookie-name",lower:"lenient-cookie-name",index:4,isBkr:!1},this.rules[5]={name:"lenient-cookie-value",lower:"lenient-cookie-value",index:5,isBkr:!1},this.rules[6]={name:"lenient-quoted-value",lower:"lenient-quoted-value",index:6,isBkr:!1},this.rules[7]={name:"lenient-quoted-char",lower:"lenient-quoted-char",index:7,isBkr:!1},this.rules[8]={name:"lenient-cookie-octet",lower:"lenient-cookie-octet",index:8,isBkr:!1},this.rules[9]={name:"cookie-string",lower:"cookie-string",index:9,isBkr:!1},this.rules[10]={name:"cookie-pair",lower:"cookie-pair",index:10,isBkr:!1},this.rules[11]={name:"cookie-name",lower:"cookie-name",index:11,isBkr:!1},this.rules[12]={name:"cookie-value",lower:"cookie-value",index:12,isBkr:!1},this.rules[13]={name:"cookie-octet",lower:"cookie-octet",index:13,isBkr:!1},this.rules[14]={name:"OWS",lower:"ows",index:14,isBkr:!1},this.rules[15]={name:"token",lower:"token",index:15,isBkr:!1},this.rules[16]={name:"tchar",lower:"tchar",index:16,isBkr:!1},this.rules[17]={name:"CHAR",lower:"char",index:17,isBkr:!1},this.rules[18]={name:"CTL",lower:"ctl",index:18,isBkr:!1},this.rules[19]={name:"separators",lower:"separators",index:19,isBkr:!1},this.rules[20]={name:"SP",lower:"sp",index:20,isBkr:!1},this.rules[21]={name:"HT",lower:"ht",index:21,isBkr:!1},this.rules[22]={name:"ALPHA",lower:"alpha",index:22,isBkr:!1},this.rules[23]={name:"DIGIT",lower:"digit",index:23,isBkr:!1},this.rules[24]={name:"DQUOTE",lower:"dquote",index:24,isBkr:!1},this.rules[25]={name:"WSP",lower:"wsp",index:25,isBkr:!1},this.rules[26]={name:"HTAB",lower:"htab",index:26,isBkr:!1},this.rules[27]={name:"CRLF",lower:"crlf",index:27,isBkr:!1},this.rules[28]={name:"CR",lower:"cr",index:28,isBkr:!1},this.rules[29]={name:"LF",lower:"lf",index:29,isBkr:!1},this.udts=[],this.rules[0].opcodes=[],this.rules[0].opcodes[0]={type:2,children:[1,2]},this.rules[0].opcodes[1]={type:4,index:1},this.rules[0].opcodes[2]={type:3,min:
Line truncated
|
||||||
|
lenient-cookie-string = lenient-cookie-entry *( ";" OWS lenient-cookie-entry )
|
||||||
|
lenient-cookie-entry = lenient-cookie-pair / lenient-cookie-pair-invalid
|
||||||
|
lenient-cookie-pair = OWS lenient-cookie-name OWS "=" OWS lenient-cookie-value OWS
|
||||||
|
lenient-cookie-pair-invalid = OWS 1*tchar OWS ; Allow for standalone entries like "fizz" to be ignored
|
||||||
|
lenient-cookie-name = 1*( %x21-3A / %x3C / %x3E-7E ) ; Allow all printable US-ASCII except "="
|
||||||
|
lenient-cookie-value = lenient-quoted-value [ *lenient-cookie-octet ] / *lenient-cookie-octet
|
||||||
|
lenient-quoted-value = DQUOTE *( lenient-quoted-char ) DQUOTE
|
||||||
|
lenient-quoted-char = %x20-21 / %x23-7E ; Allow all printable US-ASCII except DQUOTE
|
||||||
|
lenient-cookie-octet = %x21-2B / %x2D-3A / %x3C-7E
|
||||||
|
; Allow all printable characters except CTLs, semicolon and SP
|
||||||
|
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc6265#section-4.2.1
|
||||||
|
cookie-string = cookie-pair *( ";" SP cookie-pair )
|
||||||
|
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1
|
||||||
|
; https://www.rfc-editor.org/errata/eid5518
|
||||||
|
cookie-pair = cookie-name "=" cookie-value
|
||||||
|
cookie-name = token
|
||||||
|
cookie-value = ( DQUOTE *cookie-octet DQUOTE ) / *cookie-octet
|
||||||
|
; https://www.rfc-editor.org/errata/eid8242
|
||||||
|
cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
|
||||||
|
; US-ASCII characters excluding CTLs,
|
||||||
|
; whitespace, DQUOTE, comma, semicolon,
|
||||||
|
; and backslash
|
||||||
|
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc6265#section-2.2
|
||||||
|
OWS = *( [ CRLF ] WSP ) ; "optional" whitespace
|
||||||
|
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.2
|
||||||
|
token = 1*(tchar)
|
||||||
|
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
||||||
|
/ "+" / "-" / "." / "^" / "_" / "\`" / "|" / "~"
|
||||||
|
/ DIGIT / ALPHA
|
||||||
|
; any VCHAR, except delimiters
|
||||||
|
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc2616#section-2.2
|
||||||
|
CHAR = %x01-7F ; any US-ASCII character (octets 0 - 127)
|
||||||
|
CTL = %x00-1F / %x7F ; any US-ASCII control character
|
||||||
|
separators = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\\" / %x22 / "/" / "[" / "]" / "?" / "=" / "{" / "}" / SP / HT
|
||||||
|
SP = %x20 ; US-ASCII SP, space (32)
|
||||||
|
HT = %x09 ; US-ASCII HT, horizontal-tab (9)
|
||||||
|
|
||||||
|
; https://datatracker.ietf.org/doc/html/rfc5234#appendix-B.1
|
||||||
|
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
|
||||||
|
DIGIT = %x30-39 ; 0-9
|
||||||
|
DQUOTE = %x22 ; " (Double Quote)
|
||||||
|
WSP = SP / HTAB ; white space
|
||||||
|
HTAB = %x09 ; horizontal tab
|
||||||
|
CRLF = CR LF ; Internet standard newline
|
||||||
|
CR = %x0D ; carriage return
|
||||||
|
LF = %x0A ; linefeed
|
||||||
|
`}}new Pm;const AM=t=>{if(typeof t!="string"||[...t].length!==1)throw new TypeError("Input must be a single character string.");const e=t.codePointAt(0);return e<=127?`%${e.toString(16).toUpperCase().padStart(2,"0")}`:encodeURIComponent(t)},wv=t=>t.length>=2&&t.startsWith('"')&&t.endsWith('"'),rx=t=>wv(t)?t.slice(1,-1):t,nx=t=>`"${t}"`,ax=t=>t,CM=new ns,jM=new Pm,_1=(t,{strict:e=!0,quoted:r=null}={})=>{try{const n=e?"cookie-value":"lenient-cookie-value",a=CM.parse(jM,n,t);return typeof r=="boolean"?a.success&&r===wv(t):a.success}catch{return!1}},ox=t=>{const e=new TextEncoder().encode(t).reduce(((r,n)=>r+String.fromCharCode(n)),"");return btoa(e)},PM=(t,e=ox)=>{const r=String(t);if(_1(r))return r;const n=wv(r),a=e(n?rx(r):r);return n?nx(a):a},NM=t=>(e=>e.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,""))(ox(t)),TM=t=>PM(t,NM),IM=new ns,MM=new Pm,ix=(t,{strict:e=!0}={})=>{try{const r=e?"cookie-name":"lenient-cookie-name";return IM.parse(MM,r,t).success}catch{return!1}},RM=t=>{if(!ix(t))throw new TypeError(`Invalid cookie name: ${t}`)},sx=t=>{if(!_1(t))throw new TypeError(`Invalid cookie value: ${t}`)},lx={encoders:{name:ax,value:TM},validators:{name:RM,value:sx}},DM=(t,e,r={})=>{const n={...r,encoders:{...lx.encoders,...r.encoders},validators:{...lx.validators,...r.validators}},a=n.encoders.name(t),o=n.encoders.value(e);return n.validators.name(a),n.validators.value(o),`${a}=${o}`},FM=(t,e={})=>(Array.isArray(t)?t:typeof t=="object"&&t!==null?Object.entries(t):[]).map((([r,n])=>DM(r,n,e))).join("; "),LM=new ns,$M=new Pm,BM=t=>{const e=String(t);if(_1(e))return e;const r=wv(e),n=r?rx(e):e;let a="";for(const o of n)a+=LM.parse($M,"cookie-octet",o).success?o:AM(o);return r?nx(a):a};new ns,new Pm;const qM=t=>{if(!ix(t,{strict:!1}))throw new TypeError(`Invalid cookie name: ${t}`)},UM=t=>BM(t).replace(/[=&]/gu,(e=>e==="="?"%3D":"%26")),O1=(t,e={})=>FM(t,Py({encoders:{name:ax,value:UM},validators:{name:qM,value:sx}},e));function zM({req:t,value:e,parameter:r,baseURL:n}){const{name:a,style:o,explode:p,content:c}=r;if(e===void 0)return;const d=t.url.replace(n,"");let E;if(c){const x=Object.keys(c)[0];E=x1(d,{[a]:e},{encoder:k=>JE(bv(k,x))})}else E=x1(d,{[a]:e},{encoder:x=>q0({key:r.name,value:x,style:o||"simple",explode:p??!1,escape:"reserved"})});t.url=n+E}function VM({req:t,value:e,parameter:r}){if(t.query=t.query||{},e!==void 0&&r.content){const n=bv(e,Object.keys(r.content)[0]);if(n)t.query[r.name]=n;else if(r.allowEmptyValue){const a=r.name;t.query[a]=t.query[a]||{},t.query[a].allowEmptyValue=!0}}else if(e===!1&&(e="false"),e===0&&(e="0"),e){const{style:n,explode:a,allowReserved:o}=r;t.query[r.name]={value:e,serializationOption:{style:n,explode:a,allowReserved:o}}}else if(r.allowEmptyValue&&e!==void 0){const n=r.name;t.query[n]=t.query[n]||{},t.query[n].allowEmptyValue=!0}}const JM=["accept","authorization","content-type"];function WM({req:t,parameter:e,value:r}){if(t.headers=t.headers||{},!(JM.indexOf(e.name.toLowerCase())>-1))if(r!==void 0&&e.content){const n=Object.keys(e.content)[0];t.headers[e.name]=bv(r,n)}else r===void 0||Array.isArray(r)&&r.length===0||(t.headers[e.name]=q0({key:e.name,value:r,style:e.style||"simple",explode:e.explode!==void 0&&e.explode,escape:!1}))}function HM({req:t,parameter:e,value:r}){const{name:n}=e;if(t.headers=t.headers||{},r!==void 0&&e.content){const a=bv(r,Object.keys(e.content)[0]);t.headers.Cookie=O1({[n]:a})}else if(r!==void 0&&(!Array.isArray(r)||r.length!==0)){const a=q0({key:e.name,value:r,escape:!1,style:e.style||"form",explode:e.explode??!1}),o=Array.isArray(r)&&e.explode?`${n}=${a}`:a;t.headers.Cookie=O1({[n]:o})}}const KM=typeof globalThis<"u"?globalThis:typeof self<"u"?self:window,{btoa:GM}=KM,cx=GM;function YM(t,e){const{operation:r,requestBody:n,securities:a,spec:o,attachContentTypeForEmptyPayload:p}=t;let{requestContentType:c}=t;e=(function({request:P,securities:R={},operation:L={},spec:V}){const Q={...P},{authorized:J={}}=R,re=L.security||V.security||[],me=J&&!!Object.keys(J).length,le=V?.components?.securitySchemes||{};return Q.headers=Q.headers||{},Q.query=Q.query||{},!Object.keys(R).length||!me||!re||Array.isArray(L.security)&&!L.security.length?P:(re.forEach((ve=>{Object.keys(ve).forEach((be=>{const ce=J[be],_e=le[be];if(!ce)return;const Ae=ce.value||ce,{type:xe}=_e;if(ce){if(xe==="apiKey")_e.in==="query"&&(Q.query[_e.name]=Ae),_e.in==="header"&&(Q.headers[_e.name]=Ae),_e.in==="cookie"&&(Q.cookies[_e.name]=Ae);else if(xe==="http"){if(/^basic$/i.test(_e.scheme)){const Oe=Ae.username||"",je=Ae.password||"",G=cx(`${Oe}:${je}`);Q.headers.Authorization=`Basic ${G}`}/^bearer$/i.test(_e.scheme)&&(Q.headers.Authorization=`Bearer ${Ae}`)}else if(xe==="oauth2"||xe==="openIdConnect"){const Oe=ce.token||{},je=Oe[_e["x-tokenName"]||"access_token"];let G=Oe.token_type;G&&G.toLowerCase()!=="bearer"||(G="Bearer"),Q.headers.Authorization=`${G} ${je}`}}}))})),Q)})({request:e,securities:a,operation:r,spec:o});const d=r.requestBody||{},E=Object.keys(d.content||{}),x=c&
Line truncated
|
||||||
|
The error may be correlated with this previous error:
|
||||||
|
${Dt.current.stack}
|
||||||
|
|
||||||
|
`),$e}Ev((()=>{Dt.current=void 0,It.current=void 0,_t.current=pr}));const W=u.useMemo((()=>u.createElement(V,{...pr,ref:ce})),[ce,V,pr]);return u.useMemo((()=>L?u.createElement(Ae.Provider,{value:Ct},W):W),[Ae,W,Ct])}const le=u.memo(me);if(le.WrappedComponent=V,le.displayName=me.displayName=J,d){const ve=u.forwardRef((function(ce,_e){return u.createElement(le,{...ce,reactReduxForwardedRef:_e})}));return ve.displayName=J,ve.WrappedComponent=V,I1(ve,V)}return I1(le,V)}}var LR=FR,$R=function(e){const{children:r,context:n,serverState:a,store:o}=e,p=u.useMemo((()=>{const E=Ex(o);return{store:o,subscription:E,getServerState:a?()=>a:void 0}}),[o,a]),c=u.useMemo((()=>o.getState()),[o]);Ev((()=>{const{subscription:E}=p;return E.onStateChange=E.notifyNestedSubs,E.trySubscribe(),c!==o.getState()&&E.notifyNestedSubs(),()=>{E.tryUnsubscribe(),E.onStateChange=void 0}}),[p,c]);const d=n||Ax;return u.createElement(d.Provider,{value:p},r)},BR=Be(83488),Cx=Be.n(BR);const qR=t=>e=>{const{fn:r}=t();class n extends u.Component{render(){return u.createElement(e,Yr()({},t(),this.props,this.context))}}return n.displayName=`WithSystem(${r.getDisplayName(e)})`,n},UR=(t,e)=>r=>{const{fn:n}=t();class a extends u.Component{render(){return u.createElement($R,{store:e},u.createElement(r,Yr()({},this.props,this.context)))}}return a.displayName=`WithRoot(${n.getDisplayName(r)})`,a},jx=(t,e,r)=>Wt(r?UR(t,r):Cx(),LR(((n,a)=>{const o={...a,...t()};return(e.prototype?.mapStateToProps||(c=>({state:c})))(n,o)})),qR(t))(e),Px=(t,e,r,n)=>{for(const a in e){const o=e[a];typeof o=="function"&&o(r[a],n[a],t())}},zR=(t,e,r)=>(n,a)=>{const{fn:o}=t(),p=r(n,"root");class c extends u.Component{constructor(E,x){super(E,x),Px(t,a,E,{})}UNSAFE_componentWillReceiveProps(E){Px(t,a,E,this.props)}render(){const E=Ha()(this.props,a?Object.keys(a):[]);return u.createElement(p,E)}}return c.displayName=`WithMappedContainer(${o.getDisplayName(p)})`,c},VR=(t,e,r,n)=>a=>{const o=r(t,e,n)("App","root"),{createRoot:p}=gx;p(a).render(u.createElement(o,null))},M1=(t,e,r)=>(n,a,o={})=>{if(typeof n!="string")throw new TypeError("Need a string, to fetch a component. Was given a "+typeof n);const p=r(n);return p?a?a==="root"?jx(t,p,e()):jx(t,p):p:(o.failSilently||t().log.warn("Could not find component:",n),null)},JR=t=>t.displayName||t.name||"Component",Nx=({getComponents:t,getStore:e,getSystem:r})=>{const n=(a=M1(r,e,t),fs(a,((...p)=>JSON.stringify(p))));var a;const o=(p=>gu(p,((...c)=>c)))(zR(r,0,n));return{rootInjects:{getComponent:n,makeMappedContainer:o,render:VR(r,e,M1,t)},fn:{getDisplayName:JR}}},Tx=({React:t,getSystem:e,getStore:r,getComponents:n})=>{const a={},o=parseInt(t?.version,10);return o>=16&&o<18&&(a.render=((p,c,d,E)=>x=>{const k=d(p,c,E)("App","root");gx.render(u.createElement(k,null),x)})(e,r,M1,n)),{rootInjects:a}};function Ix(t){let{fn:e}=t;const r={download:a=>({errActions:o,specSelectors:p,specActions:c,getConfigs:d})=>{let{fetch:E}=e;const x=d();function k(P){if(P instanceof Error||P.status>=400)return c.updateLoadingStatus("failed"),o.newThrownErr(Object.assign(new Error((P.message||P.statusText)+" "+a),{source:"fetch"})),void(!P.status&&P instanceof Error&&(function(){try{let L;if("URL"in Qe?L=new URL(a):(L=document.createElement("a"),L.href=a),L.protocol!=="https:"&&Qe.location.protocol==="https:"){const V=Object.assign(new Error(`Possible mixed-content issue? The page was loaded over https:// but a ${L.protocol}// URL was specified. Check that you are not attempting to load mixed content.`),{source:"fetch"});return void o.newThrownErr(V)}if(L.origin!==Qe.location.origin){const V=Object.assign(new Error(`Possible cross-origin (CORS) issue? The URL origin (${L.origin}) does not match the page (${Qe.location.origin}). Check the server returns the correct 'Access-Control-Allow-*' headers.`),{source:"fetch"});o.newThrownErr(V)}}catch{return}})());c.updateLoadingStatus("success"),c.updateSpec(P.text),p.url()!==a&&c.updateUrl(a)}a=a||p.url(),c.updateLoadingStatus("loading"),o.clear({source:"fetch"}),E({url:a,loadSpec:!0,requestInterceptor:x.requestInterceptor||(P=>P),responseInterceptor:x.responseInterceptor||(P=>P),credentials:"same-origin",headers:{Accept:"application/json,*/*"}}).then(k,k)},updateLoadingStatus:a=>{let o=[null,"loading","failed","success","failedConfig"];return o.indexOf(a)===-1&&console.error(`Error: ${a} is not one of ${JSON.stringify(o)}`),{type:"spec_update_loading_status",payload:a}}};let n={loadingStatus:yr((a=>a||(0,H.Map)()),(a=>a.get("loadingStatus")||null))};return{statePlugins:{spec:{actions:r,reducers:{spec_update_loading_status:(a,o)=>typeof o.payload=="string"?a.set("loadingStatus",o.payload):a},selectors:n}}}}function R1(t,e){(e==null||e>t.length)&&(e=t.length);for(var r=0,n=Array(e);r<e;r++)n[r]=t[r];return n}function D1(t){return(function(r){if(Array.isArray(r))return R1(r)})(t)||(function(r){if(typeof Symbol<"u"&&r[Symbol.iterator]!=null||r["@@iterator"]!=null)return Array.from(r)})(t)||(function(r,n){if(r){if(typeof
Line truncated
|
||||||
|
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)})()}function Tm(t){return Tm=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Tm(t)}function WR(t){var e=(function(n,a){if(Tm(n)!="object"||!n)return n;var o=n[Symbol.toPrimitive];if(o!==void 0){var p=o.call(n,a);if(Tm(p)!="object")return p;throw new TypeError("@@toPrimitive must return a primitive value.")}return(a==="string"?String:Number)(n)})(t,"string");return Tm(e)=="symbol"?e:e+""}function Mx(t,e,r){return(e=WR(e))in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function F1(){return F1=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var n in r)({}).hasOwnProperty.call(r,n)&&(t[n]=r[n])}return t},F1.apply(null,arguments)}function Rx(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(a){return Object.getOwnPropertyDescriptor(t,a).enumerable}))),r.push.apply(r,n)}return r}function hd(t){for(var e=1;e<arguments.length;e++){var r=arguments[e]!=null?arguments[e]:{};e%2?Rx(Object(r),!0).forEach((function(n){Mx(t,n,r[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):Rx(Object(r)).forEach((function(n){Object.defineProperty(t,n,Object.getOwnPropertyDescriptor(r,n))}))}return t}var L1={};function HR(t){var e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r=arguments.length>2?arguments[2]:void 0;return(function(a){if(a.length===0||a.length===1)return a;var o=a.join(".");return L1[o]||(L1[o]=(function(c){var d=c.length;return d===0||d===1?c:d===2?[c[0],c[1],"".concat(c[0],".").concat(c[1]),"".concat(c[1],".").concat(c[0])]:d===3?[c[0],c[1],c[2],"".concat(c[0],".").concat(c[1]),"".concat(c[0],".").concat(c[2]),"".concat(c[1],".").concat(c[0]),"".concat(c[1],".").concat(c[2]),"".concat(c[2],".").concat(c[0]),"".concat(c[2],".").concat(c[1]),"".concat(c[0],".").concat(c[1],".").concat(c[2]),"".concat(c[0],".").concat(c[2],".").concat(c[1]),"".concat(c[1],".").concat(c[0],".").concat(c[2]),"".concat(c[1],".").concat(c[2],".").concat(c[0]),"".concat(c[2],".").concat(c[0],".").concat(c[1]),"".concat(c[2],".").concat(c[1],".").concat(c[0])]:d>=4?[c[0],c[1],c[2],c[3],"".concat(c[0],".").concat(c[1]),"".concat(c[0],".").concat(c[2]),"".concat(c[0],".").concat(c[3]),"".concat(c[1],".").concat(c[0]),"".concat(c[1],".").concat(c[2]),"".concat(c[1],".").concat(c[3]),"".concat(c[2],".").concat(c[0]),"".concat(c[2],".").concat(c[1]),"".concat(c[2],".").concat(c[3]),"".concat(c[3],".").concat(c[0]),"".concat(c[3],".").concat(c[1]),"".concat(c[3],".").concat(c[2]),"".concat(c[0],".").concat(c[1],".").concat(c[2]),"".concat(c[0],".").concat(c[1],".").concat(c[3]),"".concat(c[0],".").concat(c[2],".").concat(c[1]),"".concat(c[0],".").concat(c[2],".").concat(c[3]),"".concat(c[0],".").concat(c[3],".").concat(c[1]),"".concat(c[0],".").concat(c[3],".").concat(c[2]),"".concat(c[1],".").concat(c[0],".").concat(c[2]),"".concat(c[1],".").concat(c[0],".").concat(c[3]),"".concat(c[1],".").concat(c[2],".").concat(c[0]),"".concat(c[1],".").concat(c[2],".").concat(c[3]),"".concat(c[1],".").concat(c[3],".").concat(c[0]),"".concat(c[1],".").concat(c[3],".").concat(c[2]),"".concat(c[2],".").concat(c[0],".").concat(c[1]),"".concat(c[2],".").concat(c[0],".").concat(c[3]),"".concat(c[2],".").concat(c[1],".").concat(c[0]),"".concat(c[2],".").concat(c[1],".").concat(c[3]),"".concat(c[2],".").concat(c[3],".").concat(c[0]),"".concat(c[2],".").concat(c[3],".").concat(c[1]),"".concat(c[3],".").concat(c[0],".").concat(c[1]),"".concat(c[3],".").concat(c[0],".").concat(c[2]),"".concat(c[3],".").concat(c[1],".").concat(c[0]),"".concat(c[3],".").concat(c[1],".").concat(c[2]),"".concat(c[3],".").concat(c[2],".").concat(c[0]),"".concat(c[3],".").concat(c[2],".").concat(c[1]),"".concat(c[0],".").concat(c[1],".").concat(c[2],".").concat(c[3]),"".concat(c[0],".").concat(c[1],".").concat(c[3],".").concat(c[2]),"".concat(c[0],".").concat(c[2],".").concat(c[1],".").concat(c[3]),"".concat(c[0],".").concat(c[2],".").concat(c[3],".").concat(c[1]),"".concat(c[0],".").concat(c[3],".").concat(c[1],".").concat(c[2]),"".concat(c[0],".").concat(c[3],".").concat(c[2],".").concat(c[1]),"".concat(c[1],".").concat(c[0],".").concat(c[2],".").concat(c[3]),"".concat(c[1],".").concat(c[0],".").concat(c[3],".").concat(c[2]),"".concat(c[1],".").concat(c[2],".").concat(c[0],".").concat(c[3]),"".concat(c[1],".").concat(c[2],".").concat(c[3],".").concat(c[0]),"".concat(c[1],".").concat(c[3],".").concat(c[0],".").concat(c[2]),"".concat(c[1],".").concat(c[3],".").concat(c[2],".").concat(c[0]),"".concat(c[2],".").concat(c[0],".").concat(c[1],".").concat(c[3]),"".concat(c[2],".").concat(c[0],".").concat(c[3],".").concat(c[1]),""
Line truncated
|
||||||
|
`))}))})({lines:e.replace(/\n$/,"").split(`
|
||||||
|
`),style:p,startingLineNumber:c}))}function $x(t,e){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(t),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:e},children:[{type:"text",value:t}]}}function Bx(t,e,r){var n,a={display:"inline-block",minWidth:(n=r,"".concat(n.toString().length,".25em")),paddingRight:"1em",textAlign:"right",userSelect:"none"},o=typeof t=="function"?t(e):t;return Wp(Wp({},a),o)}function Sv(t){var e=t.children,r=t.lineNumber,n=t.lineNumberStyle,a=t.largestLineNumber,o=t.showInlineLineNumbers,p=t.lineProps,c=p===void 0?{}:p,d=t.className,E=d===void 0?[]:d,x=t.showLineNumbers,k=t.wrapLongLines,P=t.wrapLines,R=P!==void 0&&P?Wp({},typeof c=="function"?c(r):c):{};if(R.className=R.className?[].concat(D1(R.className.trim().split(/\s+/)),D1(E)):E,r&&o){var L=Bx(n,r,a);e.unshift($x(r,L))}return k&x&&(R.style=Wp({display:"flex"},R.style)),{type:"element",tagName:"span",properties:R,children:e}}function qx(t){var e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:[],r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];t.length===void 0&&(t=[t]);for(var n=0;n<t.length;n++){var a=t[n];if(a.type==="text")r.push(Sv({children:[a],className:D1(new Set(e))}));else if(a.children){var o,p=e.concat(((o=a.properties)===null||o===void 0?void 0:o.className)||[]);qx(a.children,p).forEach((function(c){return r.push(c)}))}}return r}function XR(t,e,r,n,a,o,p,c,d){var E,x=qx(t.value),k=[],P=-1,R=0;function L(re,me){var le=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];return e||le.length>0?(function(be,ce){return Sv({children:be,lineNumber:ce,lineNumberStyle:c,largestLineNumber:p,showInlineLineNumbers:a,lineProps:r,className:arguments.length>2&&arguments[2]!==void 0?arguments[2]:[],showLineNumbers:n,wrapLongLines:d,wrapLines:e})})(re,me,le):(function(be,ce){if(n&&ce&&a){var _e=Bx(c,ce,p);be.unshift($x(ce,_e))}return be})(re,me)}for(var V=function(){var me=x[R],le=me.children[0].value,ve=(function(_e){return _e.match(GR)})(le);if(ve){var be=le.split(`
|
||||||
|
`);be.forEach((function(ce,_e){var Ae=n&&k.length+o,xe={type:"text",value:"".concat(ce,`
|
||||||
|
`)};if(_e===0){var Oe=L(x.slice(P+1,R).concat(Sv({children:[xe],className:me.properties.className})),Ae);k.push(Oe)}else if(_e===be.length-1){var je=x[R+1]&&x[R+1].children&&x[R+1].children[0],G={type:"text",value:"".concat(ce)};if(je){var de=Sv({children:[G],className:me.properties.className});x.splice(R+1,0,de)}else{var Ne=L([G],Ae,me.properties.className);k.push(Ne)}}else{var Ye=L([xe],Ae,me.properties.className);k.push(Ye)}})),P=R}R++};R<x.length;)V();if(P!==x.length-1){var Q=x.slice(P+1,x.length);if(Q&&Q.length){var J=L(Q,n&&k.length+o);k.push(J)}}return e?k:(E=[]).concat.apply(E,k)}function QR(t){var e=t.rows,r=t.stylesheet,n=t.useInlineStyles;return e.map((function(a,o){return Fx({node:a,stylesheet:r,useInlineStyles:n,key:"code-segment-".concat(o)})}))}function Ux(t){return t&&t.highlightAuto!==void 0}var zx=Be(43768),Vx=(function(e,r){return function(a){var o,p,c=a.language,d=a.children,E=a.style,x=E===void 0?r:E,k=a.customStyle,P=k===void 0?{}:k,R=a.codeTagProps,L=R===void 0?{className:c?"language-".concat(c):void 0,style:Wp(Wp({},x['code[class*="language-"]']),x['code[class*="language-'.concat(c,'"]')])}:R,V=a.useInlineStyles,Q=V===void 0||V,J=a.showLineNumbers,re=J!==void 0&&J,me=a.showInlineLineNumbers,le=me===void 0||me,ve=a.startingLineNumber,be=ve===void 0?1:ve,ce=a.lineNumberContainerStyle,_e=a.lineNumberStyle,Ae=_e===void 0?{}:_e,xe=a.wrapLines,Oe=a.wrapLongLines,je=Oe!==void 0&&Oe,G=a.lineProps,de=G===void 0?{}:G,Ne=a.renderer,Ye=a.PreTag,ht=Ye===void 0?"pre":Ye,Ct=a.CodeTag,_t=Ct===void 0?"code":Ct,qt=a.code,It=qt===void 0?(Array.isArray(d)?d[0]:d)||"":qt,Ot=a.astGenerator,et=(function(it,bt){if(it==null)return{};var ut,Xt,zt=(function(dr,qr){if(dr==null)return{};var en={};for(var kn in dr)if({}.hasOwnProperty.call(dr,kn)){if(qr.indexOf(kn)!==-1)continue;en[kn]=dr[kn]}return en})(it,bt);if(Object.getOwnPropertySymbols){var tr=Object.getOwnPropertySymbols(it);for(Xt=0;Xt<tr.length;Xt++)ut=tr[Xt],bt.indexOf(ut)===-1&&{}.propertyIsEnumerable.call(it,ut)&&(zt[ut]=it[ut])}return zt})(a,KR);Ot=Ot||e;var Dt=re?u.createElement(YR,{containerStyle:ce,codeStyle:L.style||{},numberStyle:Ae,startingLineNumber:be,codeString:It}):null,or=x.hljs||x['pre[class*="language-"]']||{backgroundColor:"#fff"},Ut=Ux(Ot)?"hljs":"prismjs",pr=Q?Object.assign({},et,{style:Object.assign({},or,P)}):Object.assign({},et,{className:et.className?"".concat(Ut," ").concat(et.className):Ut,style:Object.assign({},P)});if(L.style=Wp(je?{whiteSpace:"pre-wrap"}:{whiteSpace:"pre"},L.style),!Ot)return u.createElement(ht,pr,Dt,u.createElement(_t,L,It));(xe===void 0&&Ne||je)&&(xe=!0),Ne=Ne||QR;var W=[{type:"text",value:It}],$e=(function(it){var bt=it.astGenerator,ut=it.language,Xt=it.code,zt=it.defaultCodeValue;if(Ux(bt)){var tr=(function(Er,dr){return Er.listLanguages().indexOf(dr)!==-1})(bt,ut);return ut==="text"?{value:zt,language:"text"}:tr?bt.highlight(ut,Xt):bt.highlightAuto(Xt)}try{return ut&&ut!=="text"?{value:bt.highlight(Xt,ut)}:{value:zt}}catch{return{value:zt}}})({astGenerator:Ot,language:c,code:It,defaultCodeValue:W});$e.language===null&&($e.value=W);var Je=XR($e,xe,de,re,le,be,be+((o=(p=It.match(/\n/g))===null||p===void 0?void 0:p.length)!==null&&o!==void 0?o:0),Ae,je);return u.createElement(ht,pr,u.createElement(_t,L,!le&&Dt,Ne({rows:Je,stylesheet:x,useInlineStyles:Q})))}})(zx,{});Vx.registerLanguage=zx.registerLanguage;const Pc=Vx;var ZR=Be(95089);const Jx=Be.n(ZR)();var e9=Be(65772);const t9=Be.n(e9)();var r9=Be(17285);const n9=Be.n(r9)();var a9=Be(35344);const o9=Be.n(a9)();var i9=Be(17533);const s9=Be.n(i9)();var l9=Be(73402);const c9=Be.n(l9)();var u9=Be(26571);const p9=Be.n(u9)(),h9=()=>{Pc.registerLanguage("json",t9),Pc.registerLanguage("js",Jx),Pc.registerLanguage("xml",n9),Pc.registerLanguage("yaml",s9),Pc.registerLanguage("http",c9),Pc.registerLanguage("bash",o9),Pc.registerLanguage("powershell",p9),Pc.registerLanguage("javascript",Jx)},Wx={hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#333",color:"white"},"hljs-name":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"},"hljs-code":{fontStyle:"italic",color:"#888"},"hljs-emphasis":{fontStyle:"italic"},"hljs-tag":{color:"#62c8f3"},"hljs-variable":{color:"#ade5fc"},"hljs-template-variable":{color:"#ade5fc"},"hljs-selector-id":{color:"#ade5fc"},"hljs-selector-class":{color:"#ade5fc"},"hljs-string":{color:"#a2fca2"},"hljs-bullet":{color:"#d36363"},"hljs-type":{color:"#ffa"},"hljs-title":{color:"#ffa"},"hljs-section":{color:"#ffa"},"hljs-attribute":{color:"#ffa"},"hljs-quote":{color:"#ffa"},"hljs-built_in":{color:"#ffa"},"hljs-builtin-name":{color:"#ffa"},"hljs-number":{color:"#d36363"},"hljs-symbol":{color:"#d36363"},"hljs-keyword":{color:"#fcc28c"},"hljs-selector-tag":{color:"#fcc28c"},"hljs-literal":{color:"#fcc28c"},"hljs-comment":{color:"#888"},"hljs-deletion":{color:"#333",backgroundColor:"#fc9b9b"},"hljs-regexp":{color:"#c6b4f0"},"hljs-link":{color:"#c6b4f0"},"hljs-meta":{color:"#fc9b9b"},"hljs-addition":{backgroundColor:"#a2fca2",color
Line truncated
|
||||||
|
|
||||||
|
`+e}x=u.createElement(c,{language:k,downloadable:!0,fileName:`${d}.json`,canCopy:!0},E)}else/xml/i.test(r)?(E=u7()(e,{textNodesOnSameLine:!0,indentor:" "}),x=u.createElement(c,{downloadable:!0,fileName:`${d}.xml`,canCopy:!0},E)):x=Zx()(r)==="text/html"||/text\/plain/.test(r)?u.createElement(c,{downloadable:!0,fileName:`${d}.html`,canCopy:!0},e):Zx()(r)==="text/csv"||/text\/csv/.test(r)?u.createElement(c,{downloadable:!0,fileName:`${d}.csv`,canCopy:!0},e):/^image\//i.test(r)?r.includes("svg")?u.createElement("div",null," ",e," "):u.createElement("img",{src:window.URL.createObjectURL(e)}):/^audio\//i.test(r)?u.createElement("pre",{className:"microlight"},u.createElement("audio",{controls:!0,key:n},u.createElement("source",{src:n,type:r}))):typeof e=="string"?u.createElement(c,{downloadable:!0,fileName:`${d}.txt`,canCopy:!0},e):e.size>0?p?u.createElement("div",null,u.createElement("p",{className:"i"},"Unrecognized response type; displaying content as text."),u.createElement(c,{downloadable:!0,fileName:`${d}.txt`,canCopy:!0},p)):u.createElement("p",{className:"i"},"Unrecognized response type; unable to display."):null;return x?u.createElement("div",null,u.createElement("h5",null,"Response body"),x):null}}class d7 extends u.Component{constructor(e){super(e),this.state={callbackVisible:!1,parametersVisible:!0}}static defaultProps={onTryoutClick:Function.prototype,onCancelClick:Function.prototype,tryItOutEnabled:!1,allowTryItOut:!0,onChangeKey:[],specPath:[]};onChange=(e,r,n)=>{let{specActions:{changeParamByIdentity:a},onChangeKey:o}=this.props;a(o,e,r,n)};onChangeConsumesWrapper=e=>{let{specActions:{changeConsumesValue:r},onChangeKey:n}=this.props;r(n,e)};toggleTab=e=>e==="parameters"?this.setState({parametersVisible:!0,callbackVisible:!1}):e==="callbacks"?this.setState({callbackVisible:!0,parametersVisible:!1}):void 0;onChangeMediaType=({value:e,pathMethod:r})=>{let{specActions:n,oas3Selectors:a,oas3Actions:o}=this.props;const p=a.hasUserEditedBody(...r),c=a.shouldRetainRequestBodyValue(...r);o.setRequestContentType({value:e,pathMethod:r}),o.initRequestBodyValidateError({pathMethod:r}),p||(c||o.setRequestBodyValue({value:void 0,pathMethod:r}),n.clearResponse(...r),n.clearRequest(...r),n.clearValidateParams(r))};render(){let{onTryoutClick:e,onResetClick:r,parameters:n,allowTryItOut:a,tryItOutEnabled:o,specPath:p,fn:c,getComponent:d,getConfigs:E,specSelectors:x,specActions:k,pathMethod:P,oas3Actions:R,oas3Selectors:L,operation:V}=this.props;const Q=d("parameterRow"),J=d("TryItOutButton"),re=d("contentType"),me=d("Callbacks",!0),le=d("RequestBody",!0),ve=o&&a,be=x.isOAS3(),ce=`${B1(`${P[1]}${P[0]}_requests`)}_select`,_e=V.get("requestBody"),Ae=Object.values(n.reduce(((xe,Oe)=>{if(H.Map.isMap(Oe)){const je=Oe.get("in");xe[je]??=[],xe[je].push(Oe)}return xe}),{})).reduce(((xe,Oe)=>xe.concat(Oe)),[]);return u.createElement("div",{className:"opblock-section"},u.createElement("div",{className:"opblock-section-header"},be?u.createElement("div",{className:"tab-header"},u.createElement("div",{onClick:()=>this.toggleTab("parameters"),className:`tab-item ${this.state.parametersVisible&&"active"}`},u.createElement("h4",{className:"opblock-title"},u.createElement("span",null,"Parameters"))),V.get("callbacks")?u.createElement("div",{onClick:()=>this.toggleTab("callbacks"),className:`tab-item ${this.state.callbackVisible&&"active"}`},u.createElement("h4",{className:"opblock-title"},u.createElement("span",null,"Callbacks"))):null):u.createElement("div",{className:"tab-header"},u.createElement("h4",{className:"opblock-title"},"Parameters")),a?u.createElement(J,{isOAS3:x.isOAS3(),hasUserEditedBody:L.hasUserEditedBody(...P),enabled:o,onCancelClick:this.props.onCancelClick,onTryoutClick:e,onResetClick:()=>r(P)}):null),this.state.parametersVisible?u.createElement("div",{className:"parameters-container"},Ae.length?u.createElement("div",{className:"table-container"},u.createElement("table",{className:"parameters"},u.createElement("thead",null,u.createElement("tr",null,u.createElement("th",{className:"col_header parameters-col_name"},"Name"),u.createElement("th",{className:"col_header parameters-col_description"},"Description"))),u.createElement("tbody",null,Ae.map(((xe,Oe)=>u.createElement(Q,{fn:c,specPath:p.push(Oe.toString()),getComponent:d,getConfigs:E,rawParam:xe,param:x.parameterWithMetaByIdentity(P,xe),key:`${xe.get("in")}.${xe.get("name")}`,onChange:this.onChange,onChangeConsumes:this.onChangeConsumesWrapper,specSelectors:x,specActions:k,oas3Actions:R,oas3Selectors:L,pathMethod:P,isExecute:ve})))))):u.createElement("div",{className:"opblock-description-wrapper"},u.createElement("p",null,"No parameters"))):null,this.state.callbackVisible?u.createElement("div",{className:"callbacks-container opblock-description-wrapper"},u.createElement(me,{callbacks:(0,H.Map)(V.get("callbacks")),specPath:p.slice(0,-1).push("callbacks")})):null,be&&_e&&this.state.parametersVisible&&u.createElement("div",{className:"opblock-section opblock-section
Line truncated
|
||||||
|
`},fr.blockquote_close=function(t,e){return"</blockquote>"+Kp(t,e)},fr.code=function(t,e){return t[e].block?"<pre><code>"+Mi(t[e].content)+"</code></pre>"+Kp(t,e):"<code>"+Mi(t[e].content)+"</code>"},fr.fence=function(t,e,r,n,a){var o,p,c=t[e],d="",E=r.langPrefix;if(c.params){if(p=(o=c.params.split(/\s+/g)).join(" "),aD(a.rules.fence_custom,o[0]))return a.rules.fence_custom[o[0]](t,e,r,n,a);d=' class="'+E+Mi(Hp(Mm(p)))+'"'}return"<pre><code"+d+">"+(r.highlight&&r.highlight.apply(r.highlight,[c.content].concat(o))||Mi(c.content))+"</code></pre>"+Kp(t,e)},fr.fence_custom={},fr.heading_open=function(t,e){return"<h"+t[e].hLevel+">"},fr.heading_close=function(t,e){return"</h"+t[e].hLevel+`>
|
||||||
|
`},fr.hr=function(t,e,r){return(r.xhtmlOut?"<hr />":"<hr>")+Kp(t,e)},fr.bullet_list_open=function(){return`<ul>
|
||||||
|
`},fr.bullet_list_close=function(t,e){return"</ul>"+Kp(t,e)},fr.list_item_open=function(){return"<li>"},fr.list_item_close=function(){return`</li>
|
||||||
|
`},fr.ordered_list_open=function(t,e){var r=t[e];return"<ol"+(r.order>1?' start="'+r.order+'"':"")+`>
|
||||||
|
`},fr.ordered_list_close=function(t,e){return"</ol>"+Kp(t,e)},fr.paragraph_open=function(t,e){return t[e].tight?"":"<p>"},fr.paragraph_close=function(t,e){var r=!(t[e].tight&&e&&t[e-1].type==="inline"&&!t[e-1].content);return(t[e].tight?"":"</p>")+(r?Kp(t,e):"")},fr.link_open=function(t,e,r){var n=t[e].title?' title="'+Mi(Hp(t[e].title))+'"':"",a=r.linkTarget?' target="'+r.linkTarget+'"':"";return'<a href="'+Mi(t[e].href)+'"'+n+a+">"},fr.link_close=function(){return"</a>"},fr.image=function(t,e,r){var n=' src="'+Mi(t[e].src)+'"',a=t[e].title?' title="'+Mi(Hp(t[e].title))+'"':"";return"<img"+n+(' alt="'+(t[e].alt?Mi(Hp(Mm(t[e].alt))):"")+'"')+a+(r.xhtmlOut?" /":"")+">"},fr.table_open=function(){return`<table>
|
||||||
|
`},fr.table_close=function(){return`</table>
|
||||||
|
`},fr.thead_open=function(){return`<thead>
|
||||||
|
`},fr.thead_close=function(){return`</thead>
|
||||||
|
`},fr.tbody_open=function(){return`<tbody>
|
||||||
|
`},fr.tbody_close=function(){return`</tbody>
|
||||||
|
`},fr.tr_open=function(){return"<tr>"},fr.tr_close=function(){return`</tr>
|
||||||
|
`},fr.th_open=function(t,e){var r=t[e];return"<th"+(r.align?' style="text-align:'+r.align+'"':"")+">"},fr.th_close=function(){return"</th>"},fr.td_open=function(t,e){var r=t[e];return"<td"+(r.align?' style="text-align:'+r.align+'"':"")+">"},fr.td_close=function(){return"</td>"},fr.strong_open=function(){return"<strong>"},fr.strong_close=function(){return"</strong>"},fr.em_open=function(){return"<em>"},fr.em_close=function(){return"</em>"},fr.del_open=function(){return"<del>"},fr.del_close=function(){return"</del>"},fr.ins_open=function(){return"<ins>"},fr.ins_close=function(){return"</ins>"},fr.mark_open=function(){return"<mark>"},fr.mark_close=function(){return"</mark>"},fr.sub=function(t,e){return"<sub>"+Mi(t[e].content)+"</sub>"},fr.sup=function(t,e){return"<sup>"+Mi(t[e].content)+"</sup>"},fr.hardbreak=function(t,e,r){return r.xhtmlOut?`<br />
|
||||||
|
`:`<br>
|
||||||
|
`},fr.softbreak=function(t,e,r){return r.breaks?r.xhtmlOut?`<br />
|
||||||
|
`:`<br>
|
||||||
|
`:`
|
||||||
|
`},fr.text=function(t,e){return Mi(t[e].content)},fr.htmlblock=function(t,e){return t[e].content},fr.htmltag=function(t,e){return t[e].content},fr.abbr_open=function(t,e){return'<abbr title="'+Mi(Hp(t[e].title))+'">'},fr.abbr_close=function(){return"</abbr>"},fr.footnote_ref=function(t,e){var r=Number(t[e].id+1).toString(),n="fnref"+r;return t[e].subId>0&&(n+=":"+t[e].subId),'<sup class="footnote-ref"><a href="#fn'+r+'" id="'+n+'">['+r+"]</a></sup>"},fr.footnote_block_open=function(t,e,r){return(r.xhtmlOut?`<hr class="footnotes-sep" />
|
||||||
|
`:`<hr class="footnotes-sep">
|
||||||
|
`)+`<section class="footnotes">
|
||||||
|
<ol class="footnotes-list">
|
||||||
|
`},fr.footnote_block_close=function(){return`</ol>
|
||||||
|
</section>
|
||||||
|
`},fr.footnote_open=function(t,e){return'<li id="fn'+Number(t[e].id+1).toString()+'" class="footnote-item">'},fr.footnote_close=function(){return`</li>
|
||||||
|
`},fr.footnote_anchor=function(t,e){var r="fnref"+Number(t[e].id+1).toString();return t[e].subId>0&&(r+=":"+t[e].subId),' <a href="#'+r+'" class="footnote-backref">↩</a>'},fr.dl_open=function(){return`<dl>
|
||||||
|
`},fr.dt_open=function(){return"<dt>"},fr.dd_open=function(){return"<dd>"},fr.dl_close=function(){return`</dl>
|
||||||
|
`},fr.dt_close=function(){return`</dt>
|
||||||
|
`},fr.dd_close=function(){return`</dd>
|
||||||
|
`};var Kp=fr.getBreak=function(e,r){return(r=i_(e,r))<e.length&&e[r].type==="list_item_close"?"":`
|
||||||
|
`};function K1(){this.rules=a_({},fr),this.getBreak=fr.getBreak}function Ri(){this.__rules__=[],this.__cache__=null}function Gp(t,e,r,n,a){this.src=t,this.env=n,this.options=r,this.parser=e,this.tokens=a,this.pos=0,this.posMax=this.src.length,this.level=0,this.pending="",this.pendingLevel=0,this.cache=[],this.isInLabel=!1,this.linkLevel=0,this.linkContent="",this.labelUnmatchedScopes=0}function Rm(t,e){var r,n,a,o=-1,p=t.posMax,c=t.pos,d=t.isInLabel;if(t.isInLabel)return-1;if(t.labelUnmatchedScopes)return t.labelUnmatchedScopes--,-1;for(t.pos=e+1,t.isInLabel=!0,r=1;t.pos<p;){if((a=t.src.charCodeAt(t.pos))===91)r++;else if(a===93&&--r===0){n=!0;break}t.parser.skipToken(t)}return n?(o=t.pos,t.labelUnmatchedScopes=0):t.labelUnmatchedScopes=r-1,t.pos=c,t.isInLabel=d,o}function dD(t,e,r,n){var a,o,p,c,d,E;if(t.charCodeAt(0)!==42||t.charCodeAt(1)!==91||t.indexOf("]:")===-1||(o=Rm(a=new Gp(t,e,r,n,[]),1))<0||t.charCodeAt(o+1)!==58)return-1;for(c=a.posMax,p=o+2;p<c&&a.src.charCodeAt(p)!==10;p++);return d=t.slice(2,o),(E=t.slice(o+2,p).trim()).length===0?-1:(n.abbreviations||(n.abbreviations={}),n.abbreviations[":"+d]===void 0&&(n.abbreviations[":"+d]=E),p)}function G1(t){var e=Hp(t);try{e=decodeURI(e)}catch{}return encodeURI(e)}function s_(t,e){var r,n,a,o=e,p=t.posMax;if(t.src.charCodeAt(e)===60){for(e++;e<p;){if((r=t.src.charCodeAt(e))===10)return!1;if(r===62)return a=G1(Mm(t.src.slice(o+1,e))),!!t.parser.validateLink(a)&&(t.pos=e+1,t.linkContent=a,!0);r===92&&e+1<p?e+=2:e++}return!1}for(n=0;e<p&&(r=t.src.charCodeAt(e))!==32&&!(r<32||r===127);)if(r===92&&e+1<p)e+=2;else{if(r===40&&++n>1||r===41&&--n<0)break;e++}return o!==e&&(a=Mm(t.src.slice(o,e)),!!t.parser.validateLink(a)&&(t.linkContent=a,t.pos=e,!0))}function l_(t,e){var r,n=e,a=t.posMax,o=t.src.charCodeAt(e);if(o!==34&&o!==39&&o!==40)return!1;for(e++,o===40&&(o=41);e<a;){if((r=t.src.charCodeAt(e))===o)return t.pos=e+1,t.linkContent=Mm(t.src.slice(n+1,e)),!0;r===92&&e+1<a?e+=2:e++}return!1}function c_(t){return t.trim().replace(/\s+/g," ").toUpperCase()}function fD(t,e,r,n){var a,o,p,c,d,E,x,k,P;if(t.charCodeAt(0)!==91||t.indexOf("]:")===-1||(o=Rm(a=new Gp(t,e,r,n,[]),0))<0||t.charCodeAt(o+1)!==58)return-1;for(c=a.posMax,p=o+2;p<c&&((d=a.src.charCodeAt(p))===32||d===10);p++);if(!s_(a,p))return-1;for(x=a.linkContent,E=p=a.pos,p+=1;p<c&&((d=a.src.charCodeAt(p))===32||d===10);p++);for(p<c&&E!==p&&l_(a,p)?(k=a.linkContent,p=a.pos):(k="",p=E);p<c&&a.src.charCodeAt(p)===32;)p++;return p<c&&a.src.charCodeAt(p)!==10?-1:(P=c_(t.slice(1,o)),n.references[P]===void 0&&(n.references[P]={title:k,href:x}),p)}K1.prototype.renderInline=function(t,e,r){for(var n=this.rules,a=t.length,o=0,p="";a--;)p+=n[t[o].type](t,o++,e,r,this);return p},K1.prototype.render=function(t,e,r){for(var n=this.rules,a=t.length,o=-1,p="";++o<a;)t[o].type==="inline"?p+=this.renderInline(t[o].children,e,r):p+=n[t[o].type](t,o,e,r,this);return p},Ri.prototype.__find__=function(t){for(var e=this.__rules__.length,r=-1;e--;)if(this.__rules__[++r].name===t)return r;return-1},Ri.prototype.__compile__=function(){var t=this,e=[""];t.__rules__.forEach((function(r){r.enabled&&r.alt.forEach((function(n){e.indexOf(n)<0&&e.push(n)}))})),t.__cache__={},e.forEach((function(r){t.__cache__[r]=[],t.__rules__.forEach((function(n){n.enabled&&(r&&n.alt.indexOf(r)<0||t.__cache__[r].push(n.fn))}))}))},Ri.prototype.at=function(t,e,r){var n=this.__find__(t),a=r||{};if(n===-1)throw new Error("Parser rule not found: "+t);this.__rules__[n].fn=e,this.__rules__[n].alt=a.alt||[],this.__cache__=null},Ri.prototype.before=function(t,e,r,n){var a=this.__find__(t),o=n||{};if(a===-1)throw new Error("Parser rule not found: "+t);this.__rules__.splice(a,0,{name:e,enabled:!0,fn:r,alt:o.alt||[]}),this.__cache__=null},Ri.prototype.after=function(t,e,r,n){var a=this.__find__(t),o=n||{};if(a===-1)throw new Error("Parser rule not found: "+t);this.__rules__.splice(a+1,0,{name:e,enabled:!0,fn:r,alt:o.alt||[]}),this.__cache__=null},Ri.prototype.push=function(t,e,r){var n=r||{};this.__rules__.push({name:t,enabled:!0,fn:e,alt:n.alt||[]}),this.__cache__=null},Ri.prototype.enable=function(t,e){t=Array.isArray(t)?t:[t],e&&this.__rules__.forEach((function(r){r.enabled=!1})),t.forEach((function(r){var n=this.__find__(r);if(n<0)throw new Error("Rules manager: invalid rule name "+r);this.__rules__[n].enabled=!0}),this),this.__cache__=null},Ri.prototype.disable=function(t){(t=Array.isArray(t)?t:[t]).forEach((function(e){var r=this.__find__(e);if(r<0)throw new Error("Rules manager: invalid rule name "+e);this.__rules__[r].enabled=!1}),this),this.__cache__=null},Ri.prototype.getRules=function(t){return this.__cache__===null&&this.__compile__(),this.__cache__[t]||[]},Gp.prototype.pushPending=function(){this.tokens.push({type:"text",content:this.pending,level:this.pendingLevel}),this.pending=""},Gp.prototype.push=function(t){this.pending&&this.pushPending(),this.tokens.push(t),this.pendingLevel=this.level},Gp.prototype.cacheSet=function(t,e){for(var r=this.cach
Line truncated
|
||||||
|
()[]'".,!?-`;function Y1(t){return t.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1")}var mD=/\+-|\.\.|\?\?\?\?|!!!!|,,|--/,gD=/\((c|tm|r|p)\)/gi,yD={c:"©",r:"®",p:"§",tm:"™"};function vD(t){return t.indexOf("(")<0?t:t.replace(gD,(function(e,r){return yD[r.toLowerCase()]}))}var bD=/['"]/,p_=/['"]/g,wD=/[-\s()\[\]]/;function h_(t,e){return!(e<0||e>=t.length)&&!wD.test(t[e])}function dd(t,e,r){return t.substr(0,e)+r+t.substr(e+1)}var X1=[["block",function(e){e.inlineMode?e.tokens.push({type:"inline",content:e.src.replace(/\n/g," ").trim(),level:0,lines:[0,1],children:[]}):e.block.parse(e.src,e.options,e.env,e.tokens)}],["abbr",function(e){var r,n,a,o,p=e.tokens;if(!e.inlineMode){for(r=1,n=p.length-1;r<n;r++)if(p[r-1].type==="paragraph_open"&&p[r].type==="inline"&&p[r+1].type==="paragraph_close"){for(a=p[r].content;a.length&&!((o=dD(a,e.inline,e.options,e.env))<0);)a=a.slice(o).trim();p[r].content=a,a.length||(p[r-1].tight=!0,p[r+1].tight=!0)}}}],["references",function(e){var r,n,a,o,p=e.tokens;if(e.env.references=e.env.references||{},!e.inlineMode){for(r=1,n=p.length-1;r<n;r++)if(p[r].type==="inline"&&p[r-1].type==="paragraph_open"&&p[r+1].type==="paragraph_close"){for(a=p[r].content;a.length&&!((o=fD(a,e.inline,e.options,e.env))<0);)a=a.slice(o).trim();p[r].content=a,a.length||(p[r-1].tight=!0,p[r+1].tight=!0)}}}],["inline",function(e){var r,n,a,o=e.tokens;for(n=0,a=o.length;n<a;n++)(r=o[n]).type==="inline"&&e.inline.parse(r.content,e.options,e.env,r.children)}],["footnote_tail",function(e){var r,n,a,o,p,c,d,E,x,k=0,P=!1,R={};if(e.env.footnotes&&(e.tokens=e.tokens.filter((function(L){return L.type==="footnote_reference_open"?(P=!0,E=[],x=L.label,!1):L.type==="footnote_reference_close"?(P=!1,R[":"+x]=E,!1):(P&&E.push(L),!P)})),e.env.footnotes.list)){for(c=e.env.footnotes.list,e.tokens.push({type:"footnote_block_open",level:k++}),r=0,n=c.length;r<n;r++){for(e.tokens.push({type:"footnote_open",id:r,level:k++}),c[r].tokens?((d=[]).push({type:"paragraph_open",tight:!1,level:k++}),d.push({type:"inline",content:"",level:k,children:c[r].tokens}),d.push({type:"paragraph_close",tight:!1,level:--k})):c[r].label&&(d=R[":"+c[r].label]),e.tokens=e.tokens.concat(d),p=e.tokens[e.tokens.length-1].type==="paragraph_close"?e.tokens.pop():null,o=c[r].count>0?c[r].count:1,a=0;a<o;a++)e.tokens.push({type:"footnote_anchor",id:r,subId:a,level:k});p&&e.tokens.push(p),e.tokens.push({type:"footnote_close",level:--k})}e.tokens.push({type:"footnote_block_close",level:--k})}}],["abbr2",function(e){var r,n,a,o,p,c,d,E,x,k,P,R,L=e.tokens;if(e.env.abbreviations){for(e.env.abbrRegExp||(R="(^|["+u_.split("").map(Y1).join("")+"])("+Object.keys(e.env.abbreviations).map((function(V){return V.substr(1)})).sort((function(V,Q){return Q.length-V.length})).map(Y1).join("|")+")($|["+u_.split("").map(Y1).join("")+"])",e.env.abbrRegExp=new RegExp(R,"g")),k=e.env.abbrRegExp,n=0,a=L.length;n<a;n++)if(L[n].type==="inline"){for(r=(o=L[n].children).length-1;r>=0;r--)if((p=o[r]).type==="text"){for(E=0,c=p.content,k.lastIndex=0,x=p.level,d=[];P=k.exec(c);)k.lastIndex>E&&d.push({type:"text",content:c.slice(E,P.index+P[1].length),level:x}),d.push({type:"abbr_open",title:e.env.abbreviations[":"+P[2]],level:x++}),d.push({type:"text",content:P[2],level:x}),d.push({type:"abbr_close",level:--x}),E=k.lastIndex-P[3].length;d.length&&(E<c.length&&d.push({type:"text",content:c.slice(E),level:x}),L[n].children=o=[].concat(o.slice(0,r),d,o.slice(r+1)))}}}}],["replacements",function(e){var r,n,a,o,p;if(e.options.typographer){for(p=e.tokens.length-1;p>=0;p--)if(e.tokens[p].type==="inline")for(r=(o=e.tokens[p].children).length-1;r>=0;r--)(n=o[r]).type==="text"&&(a=vD(a=n.content),mD.test(a)&&(a=a.replace(/\+-/g,"±").replace(/\.{2,}/g,"…").replace(/([?!])…/g,"$1..").replace(/([?!]){4,}/g,"$1$1$1").replace(/,{2,}/g,",").replace(/(^|[^-])---([^-]|$)/gm,"$1—$2").replace(/(^|\s)--(\s|$)/gm,"$1–$2").replace(/(^|[^-\s])--([^-\s]|$)/gm,"$1–$2")),n.content=a)}}],["smartquotes",function(e){var r,n,a,o,p,c,d,E,x,k,P,R,L,V,Q,J,re;if(e.options.typographer){for(re=[],Q=e.tokens.length-1;Q>=0;Q--)if(e.tokens[Q].type==="inline"){for(J=e.tokens[Q].children,re.length=0,r=0;r<J.length;r++)if((n=J[r]).type==="text"&&!bD.test(n.text)){for(d=J[r].level,L=re.length-1;L>=0&&!(re[L].level<=d);L--);re.length=L+1,p=0,c=(a=n.content).length;e:for(;p<c&&(p_.lastIndex=p,o=p_.exec(a));)if(E=!h_(a,o.index-1),p=o.index+1,V=o[0]==="'",(x=!h_(a,p))||E){if(P=!x,R=!E){for(L=re.length-1;L>=0&&(k=re[L],!(re[L].level<d));L--)if(k.single===V&&re[L].level===d){k=re[L],V?(J[k.token].content=dd(J[k.token].content,k.pos,e.options.quotes[2]),n.content=dd(n.content,o.index,e.options.quotes[3])):(J[k.token].content=dd(J[k.token].content,k.pos,e.options.quotes[0]),n.content=dd(n.content,o.index,e.options.quotes[1])),re.length=L;continue e}}P?re.push({token:r,pos:o.index,single:V,level:d}):R&&V&&(n.content=dd(n.content,o.index,"’"))}else V&&(n.content=dd(n.content,o.index,"’"))}}}}]];function d_(){this.opLine truncated
|
||||||
|
`)).indexOf(" ")>=0&&(t=t.replace(xD,(function(c,d){var E;return t.charCodeAt(d)===10?(o=d+1,p=0,c):(E=" ".slice((d-o-p)%4),p=d-o+1,E)}))),a=new Yp(t,this,e,r,n),this.tokenize(a,a.line,a.lineMax)};for(var ew=[],y_=0;y_<256;y_++)ew.push(0);function v_(t){return t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122}function b_(t,e){var r,n,a,o=e,p=!0,c=!0,d=t.posMax,E=t.src.charCodeAt(e);for(r=e>0?t.src.charCodeAt(e-1):-1;o<d&&t.src.charCodeAt(o)===E;)o++;return o>=d&&(p=!1),(a=o-e)>=4?p=c=!1:((n=o<d?t.src.charCodeAt(o):-1)!==32&&n!==10||(p=!1),r!==32&&r!==10||(c=!1),E===95&&(v_(r)&&(p=!1),v_(n)&&(c=!1))),{can_open:p,can_close:c,delims:a}}"\\!\"#$%&'()*+,./:;<=>?@[]^_`{|}~-".split("").forEach((function(t){ew[t.charCodeAt(0)]=1}));var AD=/\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g,CD=/\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g,jD=["coap","doi","javascript","aaa","aaas","about","acap","cap","cid","crid","data","dav","dict","dns","file","ftp","geo","go","gopher","h323","http","https","iax","icap","im","imap","info","ipp","iris","iris.beep","iris.xpc","iris.xpcs","iris.lwz","ldap","mailto","mid","msrp","msrps","mtqp","mupdate","news","nfs","ni","nih","nntp","opaquelocktoken","pop","pres","rtsp","service","session","shttp","sieve","sip","sips","sms","snmp","soap.beep","soap.beeps","tag","tel","telnet","tftp","thismessage","tn3270","tip","tv","urn","vemmi","ws","wss","xcon","xcon-userid","xmlrpc.beep","xmlrpc.beeps","xmpp","z39.50r","z39.50s","adiumxtra","afp","afs","aim","apt","attachment","aw","beshare","bitcoin","bolo","callto","chrome","chrome-extension","com-eventbrite-attendee","content","cvs","dlna-playsingle","dlna-playcontainer","dtn","dvb","ed2k","facetime","feed","finger","fish","gg","git","gizmoproject","gtalk","hcp","icon","ipn","irc","irc6","ircs","itms","jar","jms","keyparc","lastfm","ldaps","magnet","maps","market","message","mms","ms-help","msnim","mumble","mvn","notes","oid","palm","paparazzi","platform","proxy","psyc","query","res","resource","rmi","rsync","rtmp","secondlife","sftp","sgn","skype","smb","soldat","spotify","ssh","steam","svn","teamspeak","things","udp","unreal","ut2004","ventrilo","view-source","webcal","wtai","wyciwyg","xfire","xri","ymsgr"],PD=/^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/,ND=/^<([a-zA-Z.\-]{1,25}):([^<>\x00-\x20]*)>/;function jv(t,e){return t=t.source,e=e||"",function r(n,a){return n?(a=a.source||a,t=t.replace(n,a),r):new RegExp(t,e)}}var TD=jv(/(?:unquoted|single_quoted|double_quoted)/)("unquoted",/[^"'=<>`\x00-\x20]+/)("single_quoted",/'[^']*'/)("double_quoted",/"[^"]*"/)(),ID=jv(/(?:\s+attr_name(?:\s*=\s*attr_value)?)/)("attr_name",/[a-zA-Z_:][a-zA-Z0-9:._-]*/)("attr_value",TD)(),MD=jv(/<[A-Za-z][A-Za-z0-9]*attribute*\s*\/?>/)("attribute",ID)(),RD=jv(/^(?:open_tag|close_tag|comment|processing|declaration|cdata)/)("open_tag",MD)("close_tag",/<\/[A-Za-z][A-Za-z0-9]*\s*>/)("comment",/<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->/)("processing",/<[?].*?[?]>/)("declaration",/<![A-Z]+\s+[^>]*>/)("cdata",/<!\[CDATA\[[\s\S]*?\]\]>/)(),DD=/^&#((?:x[a-f0-9]{1,8}|[0-9]{1,8}));/i,FD=/^&([a-z][a-z0-9]{1,31});/i,tw=[["text",function(e,r){for(var n=e.pos;n<e.posMax&&!kD(e.src.charCodeAt(n));)n++;return n!==e.pos&&(r||(e.pending+=e.src.slice(e.pos,n)),e.pos=n,!0)}],["newline",function(e,r){var n,a,o=e.pos;if(e.src.charCodeAt(o)!==10)return!1;if(n=e.pending.length-1,a=e.posMax,!r)if(n>=0&&e.pending.charCodeAt(n)===32)if(n>=1&&e.pending.charCodeAt(n-1)===32){for(var p=n-2;p>=0;p--)if(e.pending.charCodeAt(p)!==32){e.pending=e.pending.substring(0,p+1);break}e.push({type:"hardbreak",level:e.level})}else e.pending=e.pending.slice(0,-1),e.push({type:"softbreak",level:e.level});else e.push({type:"softbreak",level:e.level});for(o++;o<a&&e.src.charCodeAt(o)===32;)o++;return e.pos=o,!0}],["escape",function(e,r){var n,a=e.pos,o=e.posMax;if(e.src.charCodeAt(a)!==92)return!1;if(++a<o){if((n=e.src.charCodeAt(a))<256&&ew[n]!==0)return r||(e.pending+=e.src[a]),e.pos+=2,!0;if(n===10){for(r||e.push({type:"hardbreak",level:e.level}),a++;a<o&&e.src.charCodeAt(a)===32;)a++;return e.pos=a,!0}}return r||(e.pending+="\\"),e.pos++,!0}],["backticks",function(e,r){var n,a,o,p,c,d=e.pos;if(e.src.charCodeAt(d)!==96)return!1;for(n=d,d++,a=e.posMax;d<a&&e.src.charCodeAt(d)===96;)d++;for(o=e.src.slice(n,d),p=c=d;(p=e.src.indexOf("`",c))!==-1;){for(c=p+1;c<a&&e.src.charCodeAt(c)===96;)c++;if(c-p===o.length)return r||e.push({type:"code",content:e.src.slice(d,p).replace(/[ \n]+/g," ").trim(),block:!1,level:e.level}),e.pos=c,!0}return r||(e.pending+=o),e.pos+=o.length,!0}],["del",function(e,r){var n,a,o,p,c,d=e.posMax,E=e.pos;if(e.src.charCodeAt(E)!==126||r||E+4>=d||e.src.charCodeAt(E+1)!==126||e.level>=e.options.maxNesting||(p=E>0?e.src.charCodeAt(E-1):-1,c=e.src.charCodeAt(E+2),p===126)||c===126||c===32||c===10)return!1;for(a=E+2;a<d&&e.src.charCodeAt(a)===126;)a++;if(a>E+3)return e.pos+=a-E,r||(e.pending+=e.src.slice(E,a)),!0;forLine truncated
|
||||||
|
|
||||||
|
import Remarkable from 'remarkable';
|
||||||
|
import linkify from 'remarkable/linkify';
|
||||||
|
new Remarkable().use(linkify)
|
||||||
|
`),this.inline=new Pv,this.block=new Z1,this.core=new d_,this.renderer=new K1,this.ruler=new Ri,this.options={},this.configure($D[t]),this.set(e||{})}Nc.prototype.set=function(t){a_(this.options,t)},Nc.prototype.configure=function(t){var e=this;if(!t)throw new Error("Wrong `remarkable` preset, check name/content");t.options&&e.set(t.options),t.components&&Object.keys(t.components).forEach((function(r){t.components[r].rules&&e[r].ruler.enable(t.components[r].rules,!0)}))},Nc.prototype.use=function(t,e){return t(this,e),this},Nc.prototype.parse=function(t,e){var r=new w_(this,t,e);return this.core.process(r),r.tokens},Nc.prototype.render=function(t,e){return e=e||{},this.renderer.render(this.parse(t,e),this.options,e)},Nc.prototype.parseInline=function(t,e){var r=new w_(this,t,e);return r.inlineMode=!0,this.core.process(r),r.tokens},Nc.prototype.renderInline=function(t,e){return e=e||{},this.renderer.render(this.parseInline(t,e),this.options,e)};function E_(t,e){if(Array.prototype.indexOf)return t.indexOf(e);for(var r=0,n=t.length;r<n;r++)if(t[r]===e)return r;return-1}function Xp(t,e){for(var r=t.length-1;r>=0;r--)e(t[r])===!0&&t.splice(r,1)}function rw(t){throw new Error("Unhandled case for value: '".concat(t,"'"))}var nw=(function(){function t(e){e===void 0&&(e={}),this.tagName="",this.attrs={},this.innerHTML="",this.whitespaceRegex=/\s+/,this.tagName=e.tagName||"",this.attrs=e.attrs||{},this.innerHTML=e.innerHtml||e.innerHTML||""}return t.prototype.setTagName=function(e){return this.tagName=e,this},t.prototype.getTagName=function(){return this.tagName||""},t.prototype.setAttr=function(e,r){return this.getAttrs()[e]=r,this},t.prototype.getAttr=function(e){return this.getAttrs()[e]},t.prototype.setAttrs=function(e){return Object.assign(this.getAttrs(),e),this},t.prototype.getAttrs=function(){return this.attrs||(this.attrs={})},t.prototype.setClass=function(e){return this.setAttr("class",e)},t.prototype.addClass=function(e){for(var r,n=this.getClass(),a=this.whitespaceRegex,o=n?n.split(a):[],p=e.split(a);r=p.shift();)E_(o,r)===-1&&o.push(r);return this.getAttrs().class=o.join(" "),this},t.prototype.removeClass=function(e){for(var r,n=this.getClass(),a=this.whitespaceRegex,o=n?n.split(a):[],p=e.split(a);o.length&&(r=p.shift());){var c=E_(o,r);c!==-1&&o.splice(c,1)}return this.getAttrs().class=o.join(" "),this},t.prototype.getClass=function(){return this.getAttrs().class||""},t.prototype.hasClass=function(e){return(" "+this.getClass()+" ").indexOf(" "+e+" ")!==-1},t.prototype.setInnerHTML=function(e){return this.innerHTML=e,this},t.prototype.setInnerHtml=function(e){return this.setInnerHTML(e)},t.prototype.getInnerHTML=function(){return this.innerHTML||""},t.prototype.getInnerHtml=function(){return this.getInnerHTML()},t.prototype.toAnchorString=function(){var e=this.getTagName(),r=this.buildAttrsStr();return["<",e,r=r?" "+r:"",">",this.getInnerHtml(),"</",e,">"].join("")},t.prototype.buildAttrsStr=function(){if(!this.attrs)return"";var e=this.getAttrs(),r=[];for(var n in e)e.hasOwnProperty(n)&&r.push(n+'="'+e[n]+'"');return r.join(" ")},t})(),S_=(function(){function t(e){e===void 0&&(e={}),this.newWindow=!1,this.truncate={},this.className="",this.newWindow=e.newWindow||!1,this.truncate=e.truncate||{},this.className=e.className||""}return t.prototype.build=function(e){return new nw({tagName:"a",attrs:this.createAttrs(e),innerHtml:this.processAnchorText(e.getAnchorText())})},t.prototype.createAttrs=function(e){var r={href:e.getAnchorHref()},n=this.createCssClass(e);return n&&(r.class=n),this.newWindow&&(r.target="_blank",r.rel="noopener noreferrer"),this.truncate&&this.truncate.length&&this.truncate.length<e.getAnchorText().length&&(r.title=e.getAnchorHref()),r},t.prototype.createCssClass=function(e){var r=this.className;if(r){for(var n=[r],a=e.getCssClassSuffixes(),o=0,p=a.length;o<p;o++)n.push(r+"-"+a[o]);return n.join(" ")}return""},t.prototype.processAnchorText=function(e){return e=this.doTruncate(e)},t.prototype.doTruncate=function(e){var r=this.truncate;if(!r||!r.length)return e;var n=r.length,a=r.location;return a==="smart"?(function(p,c,d){var E,x;d==null?(d="…",x=3,E=8):(x=d.length,E=d.length);var k=function(ve){var be="";return ve.scheme&&ve.host&&(be+=ve.scheme+"://"),ve.host&&(be+=ve.host),ve.path&&(be+="/"+ve.path),ve.query&&(be+="?"+ve.query),ve.fragment&&(be+="#"+ve.fragment),be},P=function(ve,be){var ce=be/2,_e=Math.ceil(ce),Ae=-1*Math.floor(ce),xe="";return Ae<0&&(xe=ve.substr(Ae)),ve.substr(0,_e)+d+xe};if(p.length<=c)return p;var R=c-x,L=(function(ve){var be={},ce=ve,_e=ce.match(/^([a-z]+):\/\//i);return _e&&(be.scheme=_e[1],ce=ce.substr(_e[0].length)),(_e=ce.match(/^(.*?)(?=(\?|#|\/|$))/i))&&(be.host=_e[1],ce=ce.substr(_e[0].length)),(_e=ce.match(/^\/(.*?)(?=(\?|#|$))/i))&&(be.path=_e[1],ce=ce.substr(_e[0].length)),(_e=ce.match(/^\?(.*?)(?=(#|$))/i))&&(be.query=_e[1],ce=ce.substr(_e[0].length)),(_e=ce.match(/^#(.*?)$/i))&&(be.fragment=_e[1]),be})(p);if(L.query){var V=L.query.match(/^(.
Line truncated
|
||||||
|
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)})()}const B_=Object.entries,q_=Object.setPrototypeOf,fF=Object.isFrozen,mF=Object.getPrototypeOf,gF=Object.getOwnPropertyDescriptor;let xo=Object.freeze,_o=Object.seal,gd=Object.create,U_=typeof Reflect<"u"&&Reflect,lw=U_.apply,cw=U_.construct;xo||(xo=function(e){return e}),_o||(_o=function(e){return e}),lw||(lw=function(e,r){for(var n=arguments.length,a=new Array(n>2?n-2:0),o=2;o<n;o++)a[o-2]=arguments[o];return e.apply(r,a)}),cw||(cw=function(e){for(var r=arguments.length,n=new Array(r>1?r-1:0),a=1;a<r;a++)n[a-1]=arguments[a];return new e(...n)});const yd=Ta(Array.prototype.forEach),yF=Ta(Array.prototype.lastIndexOf),z_=Ta(Array.prototype.pop),vd=Ta(Array.prototype.push),vF=Ta(Array.prototype.splice),Uu=Array.isArray,Fm=Ta(String.prototype.toLowerCase),uw=Ta(String.prototype.toString),V_=Ta(String.prototype.match),Lm=Ta(String.prototype.replace),J_=Ta(String.prototype.indexOf),bF=Ta(String.prototype.trim),wF=Ta(Number.prototype.toString),EF=Ta(Boolean.prototype.toString),W_=typeof BigInt>"u"?null:Ta(BigInt.prototype.toString),H_=typeof Symbol>"u"?null:Ta(Symbol.prototype.toString),ao=Ta(Object.prototype.hasOwnProperty),Mv=Ta(Object.prototype.toString),oo=Ta(RegExp.prototype.test),Zp=(function(e){return function(){for(var r=arguments.length,n=new Array(r),a=0;a<r;a++)n[a]=arguments[a];return cw(e,n)}})(TypeError);function Ta(t){return function(e){e instanceof RegExp&&(e.lastIndex=0);for(var r=arguments.length,n=new Array(r>1?r-1:0),a=1;a<r;a++)n[a-1]=arguments[a];return lw(t,e,n)}}function dn(t,e){let r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:Fm;if(q_&&q_(t,null),!Uu(e))return t;let n=e.length;for(;n--;){let a=e[n];if(typeof a=="string"){const o=r(a);o!==a&&(fF(e)||(e[n]=o),a=o)}t[a]=!0}return t}function SF(t){for(let e=0;e<t.length;e++)ao(t,e)||(t[e]=null);return t}function Uo(t){const e=gd(null);for(const n of B_(t)){var r=dF(n,2);const a=r[0],o=r[1];ao(t,a)&&(Uu(o)?e[a]=SF(o):o&&typeof o=="object"&&o.constructor===Object?e[a]=Uo(o):e[a]=o)}return e}function Hs(t,e){for(;t!==null;){const r=gF(t,e);if(r){if(r.get)return Ta(r.get);if(typeof r.value=="function")return Ta(r.value)}t=mF(t)}return function(){return null}}const K_=xo(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","search","section","select","shadow","slot","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),pw=xo(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","enterkeyhint","exportparts","filter","font","g","glyph","glyphref","hkern","image","inputmode","line","lineargradient","marker","mask","metadata","mpath","part","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),hw=xo(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),xF=xo(["animate","color-profile","cursor","discard","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),dw=xo(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover","mprescripts"]),_F=xo(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),G_=xo(["#text"]),Y_=xo(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","command","commandfor","controls","control
Line truncated
|
||||||
|
`+ga),jt&&(ga=e0(ga)),_e&&Dn?de(ga):ga},r.setConfig=function(){Ww(arguments.length>0&&arguments[0]!==void 0?arguments[0]:{}),Lr=!0,zr=it,yn=ut},r.clearConfig=function(){Od=null,Lr=!1,zr=null,yn=null,_e=Ae,xe=""},r.isValidAttribute=function(rr,Ie,dt){Od||Ww({});const Ht=oa(rr),ir=oa(Ie);return Qk(Ht,ir,dt)},r.addHook=function(rr,Ie){typeof Ie=="function"&&ao(Ot,rr)&&vd(Ot[rr],Ie)},r.removeHook=function(rr,Ie){if(ao(Ot,rr)){if(Ie!==void 0){const dt=yF(Ot[rr],Ie);return dt===-1?void 0:vF(Ot[rr],dt,1)[0]}return z_(Ot[rr])}},r.removeHooks=function(rr){ao(Ot,rr)&&(Ot[rr]=[])},r.removeAllHooks=function(){Ot={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},r})();mw.addHook&&mw.addHook("beforeSanitizeElements",(function(t){return t.href&&t.setAttribute("rel","noopener noreferrer"),t}));const BF=function({source:e,className:r="",getConfigs:n=()=>({useUnsafeMarkdown:!1})}){if(typeof e!="string")return null;const a=new Nc({html:!0,typographer:!0,breaks:!0,linkTarget:"_blank"}).use(hF);a.core.ruler.disable(["replacements","smartquotes"]);const{useUnsafeMarkdown:o}=n(),p=a.render(e),c=$m(p,{useUnsafeMarkdown:o});return e&&p&&c?u.createElement("div",{className:Xr()(r,"markdown"),dangerouslySetInnerHTML:{__html:c}}):null};function $m(t,{useUnsafeMarkdown:e=!1}={}){const r=e,n=e?[]:["style","class"];return e&&!$m.hasWarnedAboutDeprecation&&(console.warn("useUnsafeMarkdown display configuration parameter is deprecated since >3.26.0 and will be removed in v4.0.0."),$m.hasWarnedAboutDeprecation=!0),mw.sanitize(t,{ADD_ATTR:["target"],FORBID_TAGS:["style","form"],ALLOW_DATA_ATTR:r,FORBID_ATTR:n})}$m.hasWarnedAboutDeprecation=!1;class qF extends u.Component{render(){const{errSelectors:e,specSelectors:r,getComponent:n}=this.props,a=n("SvgAssets"),o=n("InfoContainer",!0),p=n("VersionPragmaFilter"),c=n("operations",!0),d=n("Models",!0),E=n("Webhooks",!0),x=n("Row"),k=n("Col"),P=n("errors",!0),R=n("ServersContainer",!0),L=n("SchemesContainer",!0),V=n("AuthorizeBtnContainer",!0),Q=n("FilterContainer",!0),J=r.isSwagger2(),re=r.isOAS3(),me=r.isOAS31(),le=r.isOAS32(),ve=!r.specStr(),be=r.loadingStatus();let ce=null;if(be==="loading"&&(ce=u.createElement("div",{className:"info"},u.createElement("div",{className:"loading-container"},u.createElement("div",{className:"loading"})))),be==="failed"&&(ce=u.createElement("div",{className:"info"},u.createElement("div",{className:"loading-container"},u.createElement("h4",{className:"title"},"Failed to load API definition."),u.createElement(P,null)))),be==="failedConfig"){const G=e.lastError(),de=G?G.get("message"):"";ce=u.createElement("div",{className:"info failed-config"},u.createElement("div",{className:"loading-container"},u.createElement("h4",{className:"title"},"Failed to load remote configuration."),u.createElement("p",null,de)))}if(!ce&&ve&&(ce=u.createElement("h4",null,"No API definition provided.")),ce)return u.createElement("div",{className:"swagger-ui"},u.createElement("div",{className:"loading-container"},ce));const _e=r.servers(),Ae=r.schemes(),xe=_e&&_e.size,Oe=Ae&&Ae.size,je=!!r.securityDefinitions();return u.createElement("div",{className:"swagger-ui"},u.createElement(a,null),u.createElement(p,{isSwagger2:J,isOAS3:re,isOAS31:me,isOAS32:le,alsoShow:u.createElement(P,null)},u.createElement(P,null),u.createElement(x,{className:"information-container"},u.createElement(k,{mobile:12},u.createElement(o,null))),xe||Oe||je?u.createElement("div",{className:"scheme-container"},u.createElement(k,{className:"schemes wrapper",mobile:12},xe||Oe?u.createElement("div",{className:"schemes-server-container"},xe?u.createElement(R,null):null,Oe?u.createElement(L,null):null):null,je?u.createElement(V,null):null)):null,u.createElement(Q,null),u.createElement("main",{id:"operations",tabIndex:"-1",style:{outline:"none"}},u.createElement(x,null,u.createElement(k,{mobile:12,desktop:12},u.createElement(c,null))),me&&u.createElement(x,{className:"webhooks-container"},u.createElement(k,{mobile:12,desktop:12},u.createElement(E,null))),u.createElement(x,null,u.createElement(k,{mobile:12,desktop:12},u.createElement(d,null))))))}}const UF=()=>({components:{App:P9,authorizationPopup:N9,authorizeBtn:T9,AuthorizeBtnContainer:I9,authorizeOperationBtn:M9,auths:R9,AuthItem:D9,authError:F9,oauth2:V9,apiKeyAuth:L9,basicAuth:$9,clear:J9,liveResponse:K9,InitializedInput:R7,info:$7,InfoContainer:B7,InfoUrl:F7,InfoBasePath:D7,Contact:U7,License:V7,JumpToPath:J7,CopyToClipboardBtn:W7,onlineValidatorBadge:Qx,operations:Y9,operation:Q9,OperationSummary:r7,OperationSummaryMethod:n7,OperationSummaryPath:a7,responses:q1,response:s7,ResponseExtension:l7,responseBody:h7,parameters:d7,parameterRow:y7,execute:v7,headers:b7,errors:w7,contentType:_7,overview:I7,footer:H7,FilterContainer:K7,ParamBody:J1,curl:G7,Property:Y7,TryItOutButton:X7,Markdown:BF,Ba
Line truncated
|
||||||
|
`).map(((k,P)=>P>0?Array(E+1).join(" ")+k:k)).join(`
|
||||||
|
`)})(0,JSON.stringify(p,null,2))||"{}",u.createElement("br",null)))}}const hL=pL,dL=({servers:t,currentServer:e,setSelectedServer:r,setServerVariableValue:n,getServerVariable:a,getEffectiveServerValue:o})=>{const p=(t.find((x=>x.get("url")===e))||(0,H.OrderedMap)()).get("variables")||(0,H.OrderedMap)(),c=p.size!==0;(0,u.useEffect)((()=>{e||r(t.first()?.get("url"))}),[]),(0,u.useEffect)((()=>{const x=t.find((k=>k.get("url")===e));if(!x)return void r(t.first().get("url"));(x.get("variables")||(0,H.OrderedMap)()).map(((k,P)=>{n({server:e,key:P,val:k.get("default")||""})}))}),[e,t]);const d=(0,u.useCallback)((x=>{r(x.target.value)}),[r]),E=(0,u.useCallback)((x=>{const k=x.target.getAttribute("data-variable"),P=x.target.value;n({server:e,key:k,val:P})}),[n,e]);return u.createElement("div",{className:"servers"},u.createElement("label",{htmlFor:"servers"},u.createElement("select",{onChange:d,value:e,id:"servers"},t.valueSeq().map((x=>u.createElement("option",{value:x.get("url"),key:x.get("url")},x.get("url"),x.get("description")&&` - ${x.get("description")}`))).toArray())),c&&u.createElement("div",null,u.createElement("div",{className:"computed-url"},"Computed URL:",u.createElement("code",null,o(e))),u.createElement("h4",null,"Server variables"),u.createElement("table",null,u.createElement("tbody",null,p.entrySeq().map((([x,k])=>u.createElement("tr",{key:x},u.createElement("td",null,x),u.createElement("td",null,k.get("enum")?u.createElement("select",{"data-variable":x,onChange:E},k.get("enum").map((P=>u.createElement("option",{selected:P===a(e,x),key:P,value:P},P)))):u.createElement("input",{type:"text",value:a(e,x)||"",onChange:E,"data-variable":x})))))))))};class fL extends u.Component{render(){const{specSelectors:e,oas3Selectors:r,oas3Actions:n,getComponent:a}=this.props,o=e.servers(),p=a("Servers");return o&&o.size?u.createElement("div",null,u.createElement("span",{className:"servers-title"},"Servers"),u.createElement(p,{servers:o,currentServer:r.selectedServer(),setSelectedServer:n.setSelectedServer,setServerVariableValue:n.setServerVariableValue,getServerVariable:r.serverVariableValue,getEffectiveServerValue:r.serverEffectiveValue})):null}}const mL=Function.prototype;class gL extends u.PureComponent{static defaultProps={onChange:mL,userHasEditedBody:!1};constructor(e,r){super(e,r),this.state={value:Gr(e.value)||e.defaultValue},e.onChange(e.value)}applyDefaultValue=e=>{const{onChange:r,defaultValue:n}=e||this.props;return this.setState({value:n}),r(n)};onChange=e=>{this.props.onChange(Gr(e))};onDomChange=e=>{const r=e.target.value;this.setState({value:r},(()=>this.onChange(r)))};UNSAFE_componentWillReceiveProps(e){this.props.value!==e.value&&e.value!==this.state.value&&this.setState({value:Gr(e.value)}),!e.value&&e.defaultValue&&this.state.value&&this.applyDefaultValue(e)}render(){let{getComponent:e,errors:r}=this.props,{value:n}=this.state,a=r.size>0;const o=e("TextArea");return u.createElement("div",{className:"body-param"},u.createElement(o,{className:Xr()("body-param__text",{invalid:a}),title:r.size?r.join(", "):"",value:n,onChange:this.onDomChange}))}}class yL extends u.Component{constructor(e,r){super(e,r);let{name:n,schema:a}=this.props,o=this.getValue();this.state={name:n,schema:a,value:o}}getValue(){let{name:e,authorized:r}=this.props;return r&&r.getIn([e,"value"])}onChange=e=>{let{onChange:r}=this.props,{value:n,name:a}=e.target,o=Object.assign({},this.state.value);a?o[a]=n:o=n,this.setState({value:o},(()=>r(this.state)))};render(){let{schema:e,getComponent:r,errSelectors:n,name:a,authSelectors:o}=this.props;const p=r("Input"),c=r("Row"),d=r("Col"),E=r("authError"),x=r("Markdown",!0),k=r("JumpToPath",!0),P=(e.get("scheme")||"").toLowerCase(),R=o.selectAuthPath(a);let L=this.getValue(),V=n.allErrors().filter((Q=>Q.get("authId")===a));if(P==="basic"){let Q=L?L.get("username"):null;return u.createElement("div",null,u.createElement("h4",null,u.createElement("code",null,a)," (http, Basic)",u.createElement(k,{path:R})),Q&&u.createElement("h6",null,"Authorized"),u.createElement(c,null,u.createElement(x,{source:e.get("description")})),u.createElement(c,null,u.createElement("label",{htmlFor:"auth-basic-username"},"Username:"),Q?u.createElement("code",null," ",Q," "):u.createElement(d,null,u.createElement(p,{id:"auth-basic-username",type:"text",required:"required",name:"username","aria-label":"auth-basic-username",onChange:this.onChange,autoFocus:!0}))),u.createElement(c,null,u.createElement("label",{htmlFor:"auth-basic-password"},"Password:"),Q?u.createElement("code",null," ****** "):u.createElement(d,null,u.createElement(p,{id:"auth-basic-password",autoComplete:"new-password",name:"password",type:"password","aria-label":"auth-basic-password",onChange:this.onChange}))),V.valueSeq().map(((J,re)=>u.createElement(E,{error:J,key:re}))))}return P==="bearer"?u.createElement("div",null,u.createElement("h4",null,u.createElement("code",null,a)," (http, Bearer)",u.createElement(k,{path:R})),L&&u.createElement("h6
Line truncated
|
||||||
|
`;else if(n>126){const a=unescape(encodeURIComponent(t.charAt(r)));for(let o=0;o<a.length;o++)e+="="+("0"+a.charCodeAt(o).toString(16)).slice(-2).toUpperCase()}else e+="="+("0"+n.toString(16)).slice(-2).toUpperCase()}return e};var Tq=Be(48287).Buffer;const Iq=t=>Tq.from(t).toString("hex");var Mq=Be(48287).Buffer;const Rq=t=>{const e=Mq.from(t).toString("utf8"),r="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";let n=0,a="",o=0,p=0;for(let c=0;c<e.length;c++)for(o=o<<8|e.charCodeAt(c),p+=8;p>=5;)a+=r.charAt(o>>>p-5&31),p-=5;p>0&&(a+=r.charAt(o<<5-p&31),n=(8-8*e.length%5)%5);for(let c=0;c<n;c++)a+="=";return a};var Dq=Be(48287).Buffer;const Fq=t=>Dq.from(t).toString("base64");var Lq=Be(48287).Buffer;const $q=t=>Lq.from(t).toString("base64url"),Jv=new class extends Uv{#e={"7bit":kq,"8bit":Cq,binary:Pq,"quoted-printable":Nq,base16:Iq,base32:Rq,base64:Fq,base64url:$q};data={...this.#e};get defaults(){return{...this.#e}}},Sk=(t,e)=>typeof e=="function"?Jv.register(t,e):e===null?Jv.unregister(t):Jv.get(t);Sk.getDefaults=()=>Jv.defaults;const xk=Sk,Bq={"text/plain":()=>"string","text/css":()=>".selector { border: 1px solid red }","text/csv":()=>"value1,value2,value3","text/html":()=>"<p>content</p>","text/calendar":()=>"BEGIN:VCALENDAR","text/javascript":()=>"console.dir('Hello world!');","text/xml":()=>'<person age="30">John Doe</person>',"text/*":()=>"string"},qq={"image/*":()=>Bv(25).toString("binary")},Uq={"audio/*":()=>Bv(25).toString("binary")},zq={"video/*":()=>Bv(25).toString("binary")},Vq={"application/json":()=>'{"key":"value"}',"application/ld+json":()=>'{"name": "John Doe"}',"application/x-httpd-php":()=>"<?php echo '<p>Hello World!</p>'; ?>","application/rtf":()=>String.raw`{\rtf1\adeflang1025\ansi\ansicpg1252\uc1`,"application/x-sh":()=>'echo "Hello World!"',"application/xhtml+xml":()=>"<p>content</p>","application/*":()=>Bv(25).toString("binary")},wd=new class extends Uv{#e={...Bq,...qq,...Uq,...zq,...Vq};data={...this.#e};get defaults(){return{...this.#e}}},_k=(t,e)=>{if(typeof e=="function")return wd.register(t,e);if(e===null)return wd.unregister(t);const r=t.split(";").at(0),n=`${r.split("/").at(0)}/*`;return wd.get(t)||wd.get(r)||wd.get(n)};_k.getDefaults=()=>wd.defaults;const Ok=_k,Mw=(t,e={})=>{const{maxLength:r,minLength:n}=e;let a=t;if(Number.isInteger(r)&&r>0&&(a=a.slice(0,r)),Number.isInteger(n)&&n>0){let o=0;for(;a.length<n;)a+=a[o++%a.length]}return a},Jq=(t,{sample:e}={})=>{const{contentEncoding:r,contentMediaType:n,contentSchema:a}=t,{pattern:o,format:p}=t,c=xk(r)||Cx();let d;return d=typeof o=="string"?Mw((E=>{try{const x=new RegExp("(?<=(?<!\\\\)\\{)(\\d{3,})(?=\\})|(?<=(?<!\\\\)\\{\\d*,)(\\d{3,})(?=\\})|(?<=(?<!\\\\)\\{)(\\d{3,})(?=,\\d*\\})","g"),k=E.replace(x,"100"),P=new(xh())(k);return P.max=100,P.gen()}catch{return"string"}})(o),t):typeof p=="string"?(E=>{const{format:x}=E,k=Vv(x);return typeof k=="function"?k(E):"string"})(t):Fc(a)&&typeof n=="string"&&e!==void 0?Array.isArray(e)||typeof e=="object"?JSON.stringify(e):Mw(String(e),t):typeof n=="string"?(E=>{const{contentMediaType:x}=E,k=Ok(x);return typeof k=="function"?k(E):"string"})(t):Mw("string",t),c(d)},kk=(t,e={})=>{const{minimum:r,maximum:n,exclusiveMinimum:a,exclusiveMaximum:o}=e,{multipleOf:p}=e,c=Number.isInteger(t)?1:Number.EPSILON;let d=typeof r=="number"?r:null,E=typeof n=="number"?n:null,x=t;if(typeof a=="number"&&(d=d!==null?Math.max(d,a+c):a+c),typeof o=="number"&&(E=E!==null?Math.min(E,o-c):o-c),x=d>E&&t||d||E||x,typeof p=="number"&&p>0){const k=x%p;x=k===0?x:x+p-k}return x},Wq=t=>{const{format:e}=t;let r;return r=typeof e=="string"?(n=>{const{format:a}=n,o=Vv(a);return typeof o=="function"?o(n):0})(t):0,kk(r,t)},Hq=t=>{const{format:e}=t;let r;return r=typeof e=="string"?(n=>{const{format:a}=n,o=Vv(a);if(typeof o=="function")return o(n);switch(a){case"int32":return bk();case"int64":return wk()}return 0})(t):0,kk(r,t)},Kq=t=>typeof t.default!="boolean"||t.default,Wv=new Proxy({array:tq,object:rq,string:Jq,number:Wq,integer:Hq,boolean:Kq,null:()=>null},{get:(t,e)=>typeof e=="string"&&Object.hasOwn(t,e)?t[e]:()=>`Unknown Type: ${e}`}),Ak=["array","object","number","integer","string","boolean","null"],zm=t=>{if(!Ys(t))return!1;const{examples:e,example:r,default:n}=t;return!!(Array.isArray(e)&&e.length>=1)||n!==void 0||r!==void 0},Rw=t=>{if(!Ys(t))return null;const{examples:e,example:r,default:n}=t;return Array.isArray(e)&&e.length>=1?e.at(0):n!==void 0?n:r!==void 0?r:void 0},Hv={array:["items","prefixItems","contains","maxContains","minContains","maxItems","minItems","uniqueItems","unevaluatedItems"],object:["properties","additionalProperties","patternProperties","propertyNames","minProperties","maxProperties","required","dependentSchemas","dependentRequired","unevaluatedProperties"],string:["pattern","format","minLength","maxLength","contentEncoding","contentMediaType","contentSchema"],integer:["minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf"]};Hv.number=Hv.integer;const Dw="string",Ck=t=>t===void 0?null:
Line truncated
|
||||||
|
`&&(c=c.slice(0,c.length-1))}catch(d){return console.error(d),"error: could not generate yaml example"}return c.replace(/\t/g," ")},rU=t=>(e,r,n)=>{const{fn:a}=t();if(e&&!e.xml&&(e.xml={}),e&&!e.xml.name){if(!e.$$ref&&(e.type||e.items||e.properties||e.additionalProperties))return`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- XML example cannot be generated; root element name is undefined -->`;if(e.$$ref){let o=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=o[1]}}return a.jsonSchema202012.memoizedCreateXMLExample(e,r,n)},nU=t=>(e,r="",n={},a=void 0)=>{const{fn:o}=t();return typeof e?.toJS=="function"&&(e=e.toJS()),typeof a?.toJS=="function"&&(a=a.toJS()),/xml/.test(r)?o.jsonSchema202012.getXmlSampleSchema(e,n,a):/(yaml|yml)/.test(r)?o.jsonSchema202012.getYamlSampleSchema(e,n,r,a):o.jsonSchema202012.getJsonSampleSchema(e,n,r,a)},Mk=({getSystem:t})=>{const e=eU(t),r=tU(t),n=rU(t),a=nU(t);return{fn:{jsonSchema202012:{sampleFromSchema:Nk,sampleFromSchemaGeneric:io,sampleOptionAPI:Xq,sampleEncoderAPI:xk,sampleFormatAPI:Vv,sampleMediaTypeAPI:Ok,createXMLExample:Pk,memoizedSampleFromSchema:Yq,memoizedCreateXMLExample:Gq,getJsonSampleSchema:e,getYamlSampleSchema:r,getXmlSampleSchema:n,getSampleSchema:a,mergeJsonSchema:Ed,foldType:Kv}}}};function Rk(){return[nO,Aw,vk,Mk,fB,WB]}const aU=t=>()=>({fn:t.fn,components:t.components}),oU=t=>{const e=ct()({layout:{layout:t.layout,filter:t.filter},spec:{spec:"",url:t.url},requestSnippets:t.requestSnippets},t.initialState);if(t.initialState)for(const[r,n]of Object.entries(t.initialState))n===void 0&&delete e[r];return{system:{configs:t.configs},plugins:t.presets,state:e}},iU=()=>t=>{const e=t.queryConfigEnabled?(()=>{const r=new URLSearchParams(Qe.location.search);return Object.fromEntries(r)})():{};return Object.entries(e).reduce(((r,[n,a])=>(n==="config"?r.configUrl=a:n==="urls.primaryName"?r[n]=a:r=pn()(r,n,a),r)),{})},sU=({url:t,system:e})=>async r=>{if(!t)return{};if(typeof e.configsActions?.getConfigByUrl!="function")return{};const n=(()=>{const a={};return a.promise=new Promise(((o,p)=>{a.resolve=o,a.reject=p})),a})();return e.configsActions.getConfigByUrl({url:t,loadRemoteConfig:!0,requestInterceptor:r.requestInterceptor,responseInterceptor:r.responseInterceptor},(a=>{n.resolve(a)})),n.promise},lU=()=>()=>{const t={};return globalThis.location&&(t.oauth2RedirectUrl=`${globalThis.location.protocol}//${globalThis.location.host}${globalThis.location.pathname.substring(0,globalThis.location.pathname.lastIndexOf("/"))}/oauth2-redirect.html`),t},Xn=Object.freeze({dom_id:null,domNode:null,spec:{},url:"",urls:null,configUrl:null,layout:"BaseLayout",docExpansion:"list",maxDisplayedTags:-1,filter:!1,validatorUrl:"https://validator.swagger.io/validator",oauth2RedirectUrl:void 0,persistAuthorization:!1,configs:{},displayOperationId:!1,displayRequestDuration:!1,deepLinking:!1,tryItOutEnabled:!1,requestInterceptor:t=>(t.curlOptions=[],t),responseInterceptor:t=>t,showMutatedRequest:!0,defaultModelRendering:"example",defaultModelExpandDepth:1,defaultModelsExpandDepth:1,showExtensions:!1,showCommonExtensions:!1,withCredentials:!1,requestSnippetsEnabled:!1,requestSnippets:{generators:{curl_bash:{title:"cURL (bash)",syntax:"bash"},curl_powershell:{title:"cURL (PowerShell)",syntax:"powershell"},curl_cmd:{title:"cURL (CMD)",syntax:"bash"}},defaultExpanded:!0,languages:null},supportedSubmitMethods:["get","put","post","delete","options","head","patch","trace"],queryConfigEnabled:!1,presets:[Rk],plugins:[],initialState:{},fn:{},components:{},syntaxHighlight:{activated:!0,theme:"agate"},operationsSorter:null,tagsSorter:null,onComplete:null,modelPropertyMacro:null,parameterMacro:null,fileUploadMediaTypes:["application/octet-stream","image/","audio/","video/"],uncaughtExceptionHandler:null});var cU=Be(61448),uU=Be.n(cU),pU=Be(77731),hU=Be.n(pU);const Gv=(t,e=[])=>Array.isArray(t)?t:e,us=(t,e=!1)=>t===!0||t==="true"||t===1||t==="1"||t!==!1&&t!=="false"&&t!==0&&t!=="0"&&e,dU=t=>t===null||t==="null"?null:t,fU=t=>{const e=String(t);return us(t,e)},Dk=(t,e)=>typeof t=="function"?t:e,mU=t=>Array.isArray(t)?t:null,Yv=t=>typeof t=="function"?t:null,$w=t=>t===null||t==="null"?null:String(t),Bw=(t,e=-1)=>{const r=parseInt(t,10);return Number.isNaN(r)?e:r},Sd=(t,e={})=>Ai()(t)?t:e,Fk=t=>typeof t=="function"||typeof t=="string"?t:null,xd=t=>String(t),gU=(t,e)=>Ai()(t)?t:t===!1||t==="false"||t===0||t==="0"?{activated:!1}:e,yU=t=>t===void 0||t==="undefined"?void 0:String(t),Lk={components:{typeCaster:Sd},configs:{typeCaster:Sd},configUrl:{typeCaster:$w},deepLinking:{typeCaster:us,defaultValue:Xn.deepLinking},defaultModelExpandDepth:{typeCaster:Bw,defaultValue:Xn.defaultModelExpandDepth},defaultModelRendering:{typeCaster:xd},defaultModelsExpandDepth:{typeCaster:Bw,defaultValue:Xn.defaultModelsExpandDepth},displayOperationId:{typeCaster:us,defaultValue:Xn.displayOperationId},displayRequestDuration:{typeCaster:us,defaultValue:Xn.displayRequestDuration},docExpansion:{typeCaster:xd},dom_id:{typeCaster:$w},domNode:{typeCaster:dU},fileUploadMediaTypes:{typeCaster:Gv,defaultValue:Xn.fileUploadMediaTypes},filter:{typeCaster:fU},fn:{typeCaster:Sd},initialState:{typeCaster:Sd},layout:{typeCaster:xd},maxDisplayedTags:{typeCaster:Bw,defaultValue:Xn.maxDisplayedTags},modelPropertyMacro:{typeCaster:Yv},oauth2RedirectUrl:{typeCaster:yU},onComplet
Line truncated
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
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-DYsKpclu.js";import{_ as w}from"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.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};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
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-DYsKpclu.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 _};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
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-DYsKpclu.js";import{P as Q}from"./PageShell-D87JkPkG.js";import{g as X,p as Y,b as Z}from"./share-CqDDLJWI.js";import"./SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.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
@@ -0,0 +1,129 @@
|
|||||||
|
import{a0 as ge,h as F,a1 as Ze,a2 as Me,a3 as Xe,a4 as Qe,a5 as Pe,a6 as et,o as w,a7 as ze,z as ce,c as y,F as G,b as e,a8 as We,a9 as tt,e as st,aa as nt,G as Fe,A as it,ab as ot,ac as at,j as V,ad as Ae,ae as K,J as lt,I as Ye,af as rt,P as dt,ag as ct,ah as ut,d as ve,ai as _t,aj as mt,ak as Ue,al as b,n as Re,am as Ge,an as ft,ao as ht,ap as pt,aq as I,ar as Ee,as as qe,at as Q,au as gt,u as vt,i as bt,a as wt,t as l,f as o,q as M,w as He,C as f,D as v,g as L,Z as _e,s as Oe,R as ke,S as p,H as re,B as yt,_ as xt}from"./index-DYsKpclu.js";import{f as kt,g as St,h as De,i as Ct}from"./admin-DEOkRyTC.js";import{u as Bt}from"./auth-B7MDTgxJ.js";function Vt(s={},t={defaultBordered:!0}){const d=ge(Me,null);return{inlineThemeDisabled:d?.inlineThemeDisabled,mergedRtlRef:d?.mergedRtlRef,mergedComponentPropsRef:d?.mergedComponentPropsRef,mergedBreakpointsRef:d?.mergedBreakpointsRef,mergedBorderedRef:F(()=>{const{bordered:u}=s;return u!==void 0?u:d?.mergedBorderedRef.value??t.defaultBordered??!0}),mergedClsPrefixRef:d?d.mergedClsPrefixRef:Ze("n"),namespaceRef:F(()=>d?.mergedNamespaceRef.value)}}function $t(s,t,d){if(!t)return;const u=Xe(),_=ge(Me,null),h=()=>{const x=d.value;t.mount({id:x===void 0?s:x+s,head:!0,anchorMetaName:Pe,props:{bPrefix:x?`.${x}-`:void 0},ssr:u,parent:_?.styleMountTarget}),_?.preflightStyleDisabled||et.mount({id:"n-global",head:!0,anchorMetaName:Pe,ssr:u,parent:_?.styleMountTarget})};u?h():Qe(h)}function z(s,t=1){let d=Fe,u=!1;return typeof s=="function"&&(u=!0,w(),d=ce,s=s()),ze(s)?u?ce(Ie(s)):Ie(s):Array.isArray(s)?u?y(G,null,s.map(_=>z(()=>_)),-2):e(G,null,s.slice()):s==null||typeof s=="boolean"?d(We):d(tt,null,String(s),t)}function Ie(s){return s.el===null&&s.patchFlag!==-1||s.memo?s:nt(s)}const Tt=s=>typeof s=="function"||Object.prototype.toString.call(s)==="[object Object]"&&!ze(s)?s:{default:it(()=>[z(()=>s)])},C=s=>st(s)||null;function j(s){return typeof s=="string"?s.endsWith("px")?Number(s.slice(0,s.length-2)):Number(s):s}function Se(s){if(s!=null)return typeof s=="number"?`${s}px`:s.endsWith("px")?s:`${s}px`}function Ut(s,t,d,u){d||ot("useThemeClass","cssVarsRef is not passed");const _=ge(Me,null),h=_?.mergedThemeHashRef,x=_?.styleMountTarget,B=V(""),a=Xe();let k;const $=`__${s}`,R=()=>{let m=$;const T=t?t.value:void 0,N=h?.value;N&&(m+=`-${N}`),T&&(m+=`-${T}`);const{themeOverrides:E,builtinThemeOverrides:q}=u;E&&(m+=`-${Ae(JSON.stringify(E))}`),q&&(m+=`-${Ae(JSON.stringify(q))}`),B.value=m,k=()=>{const te=d.value;let se="";for(const ne in te)se+=`${ne}: ${te[ne]};`;K(`.${m}`,se).mount({id:m,ssr:a,parent:x}),k=void 0}};return at(()=>{R()}),{themeClass:B,onRender:()=>{k?.()}}}function Rt(s,t){return lt(s,d=>{d!==void 0&&(t.value=d)}),F(()=>s.value===void 0?t.value:s.value)}function Mt(){const s=V(!1);return Ye(()=>{s.value=!0}),rt(s)}function he(s,...t){if(Array.isArray(s))s.forEach(d=>he(d,...t));else return s(...t)}function Ne(s){return s.some(t=>ze(t)?!(t.type===We||t.type===G&&!Ne(t.children)):!0)?s:null}function ee(s,t){return t(s&&Ne(s())||null)}function Ce(s){return!(s&&Ne(s()))}const Le=ct("n-form-item");function zt(s,{defaultSize:t="medium",mergedSize:d,mergedDisabled:u}={}){const _=ge(Le,null);ut(Le,null);const h=F(d?()=>d(_):()=>{const{size:a}=s;if(a)return a;if(_){const{mergedSize:k}=_;if(k.value!==void 0)return k.value}return t}),x=F(u?()=>u(_):()=>{const{disabled:a}=s;return a!==void 0?a:_?_.disabled.value:!1}),B=F(()=>{const{status:a}=s;return a||_?.mergedValidationStatus.value});return dt(()=>{_&&_.restoreValidation()}),{mergedSizeRef:h,mergedDisabledRef:x,mergedStatusRef:B,nTriggerFormBlur(){_&&_.handleContentBlur()},nTriggerFormChange(){_&&_.handleContentChange()},nTriggerFormFocus(){_&&_.handleContentFocus()},nTriggerFormInput(){_&&_.handleContentInput()}}}var Je=ve({name:"BaseIconSwitchTransition",setup(s,{slots:t}){const d=Mt();return()=>(w(),ce(_t,{name:"icon-switch-transition",appear:d.value},Tt(t),1032,["appear"]))}});const{cubicBezierEaseInOut:Ft}=mt;function pe({originalTransform:s="",left:t=0,top:d=0,transition:u=`all .3s ${Ft} !important`}={}){return[K("&.icon-switch-transition-enter-from, &.icon-switch-transition-leave-to",{transform:`${s} scale(0.75)`,left:t,top:d,opacity:0}),K("&.icon-switch-transition-enter-to, &.icon-switch-transition-leave-from",{transform:`scale(1) ${s}`,left:t,top:d,opacity:1}),K("&.icon-switch-transition-enter-active, &.icon-switch-transition-leave-active",{transformOrigin:"center",position:"absolute",left:t,top:d,transition:u})]}var Nt=K([K("@keyframes rotator",`
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}`),Ue("base-loading",`
|
||||||
|
position: relative;
|
||||||
|
line-height: 0;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
`,[b("transition-wrapper",`
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
`,[pe()]),b("placeholder",`
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
`,[pe({left:"50%",top:"50%",originalTransform:"translateX(-50%) translateY(-50%)"})]),b("container",`
|
||||||
|
animation: rotator 3s linear infinite both;
|
||||||
|
`,[b("icon",`
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
`)])])]);const Pt=["viewBox"],At=["values","dur"],Et=["stroke-width","cx","cy","r","stroke-dasharray","stroke-dashoffset"],Ht=["values","dur"],Ot=["values","dur"],Be="1.6s",Dt={strokeWidth:{type:Number,default:28},stroke:{type:String,default:void 0},scale:{type:Number,default:1},radius:{type:Number,default:100}};var It=ve({name:"BaseLoading",props:{clsPrefix:{type:String,required:!0},show:{type:Boolean,default:!0},...Dt},setup(s){$t("-base-loading",Nt,Ge(s,"clsPrefix"))},render(){const{clsPrefix:s,radius:t,strokeWidth:d,stroke:u,scale:_}=this,h=t/_;return w(),y("div",{class:C(`${s}-base-loading`),role:"img","aria-label":"loading"},[Fe(Je,null,{default:()=>this.show?(w(),y("div",{key:"icon",class:C(`${s}-base-loading__transition-wrapper`)},[e("div",{class:C(`${s}-base-loading__container`)},[(w(),y("svg",{class:C(`${s}-base-loading__icon`),viewBox:`0 0 ${2*h} ${2*h}`,xmlns:"http://www.w3.org/2000/svg",style:Re({color:u})},[e("g",null,[e("animateTransform",{attributeName:"transform",type:"rotate",values:`0 ${h} ${h};270 ${h} ${h}`,begin:"0s",dur:Be,fill:"freeze",repeatCount:"indefinite"},null,8,At),e("circle",{class:C(`${s}-base-loading__icon`),fill:"none",stroke:"currentColor","stroke-width":d,"stroke-linecap":"round",cx:h,cy:h,r:t-d/2,"stroke-dasharray":5.67*t,"stroke-dashoffset":18.48*t},[e("animateTransform",{attributeName:"transform",type:"rotate",values:`0 ${h} ${h};135 ${h} ${h};450 ${h} ${h}`,begin:"0s",dur:Be,fill:"freeze",repeatCount:"indefinite"},null,8,Ht),e("animate",{attributeName:"stroke-dashoffset",values:`${5.67*t};${1.42*t};${5.67*t}`,begin:"0s",dur:Be,fill:"freeze",repeatCount:"indefinite"},null,8,Ot)],10,Et)])],14,Pt))],2)],2)):(w(),y("div",{key:"placeholder",class:C(`${s}-base-loading__placeholder`)},[z(()=>this.$slots.default?.())],2))},1024)],2)}});function Lt(s){const{primaryColor:t,opacityDisabled:d,borderRadius:u,textColor3:_}=s;return{...ht,iconColor:_,textColor:"white",loadingColor:t,opacityDisabled:d,railColor:"rgba(0, 0, 0, .14)",railColorActive:t,buttonBoxShadow:"0 1px 4px 0 rgba(0, 0, 0, 0.3), inset 0 0 1px 0 rgba(0, 0, 0, 0.05)",buttonColor:"#FFF",railBorderRadiusSmall:u,railBorderRadiusMedium:u,railBorderRadiusLarge:u,buttonBorderRadiusSmall:u,buttonBorderRadiusMedium:u,buttonBorderRadiusLarge:u,boxShadowFocus:`0 0 0 2px ${pt(t,{alpha:.2})}`}}const jt={common:ft,self:Lt};var Kt=Ue("switch",`
|
||||||
|
height: var(--n-height);
|
||||||
|
min-width: var(--n-width);
|
||||||
|
vertical-align: middle;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
display: inline-flex;
|
||||||
|
outline: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`,[b("children-placeholder",`
|
||||||
|
height: var(--n-rail-height);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
visibility: hidden;
|
||||||
|
`),b("rail-placeholder",`
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: none;
|
||||||
|
`),b("button-placeholder",`
|
||||||
|
width: calc(1.75 * var(--n-rail-height));
|
||||||
|
height: var(--n-rail-height);
|
||||||
|
`),Ue("base-loading",`
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
font-size: calc(var(--n-button-width) - 4px);
|
||||||
|
color: var(--n-loading-color);
|
||||||
|
transition: color .3s var(--n-bezier);
|
||||||
|
`,[pe({left:"50%",top:"50%",originalTransform:"translateX(-50%) translateY(-50%)"})]),b("checked, unchecked",`
|
||||||
|
transition: color .3s var(--n-bezier);
|
||||||
|
color: var(--n-text-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
white-space: nowrap;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1;
|
||||||
|
`),b("checked",`
|
||||||
|
right: 0;
|
||||||
|
padding-right: calc(1.25 * var(--n-rail-height) - var(--n-offset));
|
||||||
|
`),b("unchecked",`
|
||||||
|
left: 0;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-left: calc(1.25 * var(--n-rail-height) - var(--n-offset));
|
||||||
|
`),K("&:focus",[b("rail",`
|
||||||
|
box-shadow: var(--n-box-shadow-focus);
|
||||||
|
`)]),I("round",[b("rail","border-radius: calc(var(--n-rail-height) / 2);",[b("button","border-radius: calc(var(--n-button-height) / 2);")])]),Ee("disabled",[Ee("icon",[I("rubber-band",[I("pressed",[b("rail",[b("button","max-width: var(--n-button-width-pressed);")])]),b("rail",[K("&:active",[b("button","max-width: var(--n-button-width-pressed);")])]),I("active",[I("pressed",[b("rail",[b("button","left: calc(100% - var(--n-offset) - var(--n-button-width-pressed));")])]),b("rail",[K("&:active",[b("button","left: calc(100% - var(--n-offset) - var(--n-button-width-pressed));")])])])])])]),I("active",[b("rail",[b("button","left: calc(100% - var(--n-button-width) - var(--n-offset))")])]),b("rail",`
|
||||||
|
overflow: hidden;
|
||||||
|
height: var(--n-rail-height);
|
||||||
|
min-width: var(--n-rail-width);
|
||||||
|
border-radius: var(--n-rail-border-radius);
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
transition:
|
||||||
|
opacity .3s var(--n-bezier),
|
||||||
|
background .3s var(--n-bezier),
|
||||||
|
box-shadow .3s var(--n-bezier);
|
||||||
|
background-color: var(--n-rail-color);
|
||||||
|
`,[b("button-icon",`
|
||||||
|
color: var(--n-icon-color);
|
||||||
|
transition: color .3s var(--n-bezier);
|
||||||
|
font-size: calc(var(--n-button-height) - 4px);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1;
|
||||||
|
`,[pe()]),b("button",`
|
||||||
|
align-items: center;
|
||||||
|
top: var(--n-offset);
|
||||||
|
left: var(--n-offset);
|
||||||
|
height: var(--n-button-height);
|
||||||
|
width: var(--n-button-width-pressed);
|
||||||
|
max-width: var(--n-button-width);
|
||||||
|
border-radius: var(--n-button-border-radius);
|
||||||
|
background-color: var(--n-button-color);
|
||||||
|
box-shadow: var(--n-button-box-shadow);
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: inherit;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
transition:
|
||||||
|
background-color .3s var(--n-bezier),
|
||||||
|
left .3s var(--n-bezier),
|
||||||
|
opacity .3s var(--n-bezier),
|
||||||
|
max-width .3s var(--n-bezier),
|
||||||
|
box-shadow .3s var(--n-bezier);
|
||||||
|
`)]),I("active",[b("rail","background-color: var(--n-rail-color-active);")]),I("loading",[b("rail",`
|
||||||
|
cursor: wait;
|
||||||
|
`)]),I("disabled",[b("rail",`
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: .5;
|
||||||
|
`)])]);const Xt=["aria-checked","tabindex","onClick","onFocus","onBlur","onKeyup","onKeydown"],Wt={...qe.props,size:String,value:{type:[String,Number,Boolean],default:void 0},loading:Boolean,defaultValue:{type:[String,Number,Boolean],default:!1},disabled:{type:Boolean,default:void 0},round:{type:Boolean,default:!0},"onUpdate:value":[Function,Array],onUpdateValue:[Function,Array],checkedValue:{type:[String,Number,Boolean],default:!0},uncheckedValue:{type:[String,Number,Boolean],default:!1},railStyle:Function,rubberBand:{type:Boolean,default:!0},spinProps:Object,onChange:[Function,Array]};let de;var Yt=ve({name:"Switch",props:Wt,slots:Object,setup(s){de===void 0&&(typeof CSS<"u"?typeof CSS.supports<"u"?de=CSS.supports("width","max(1px)"):de=!1:de=!0);const{mergedClsPrefixRef:t,inlineThemeDisabled:d,mergedComponentPropsRef:u}=Vt(s),_=qe("Switch","-switch",Kt,jt,s,t),h=zt(s,{mergedSize(g){if(s.size!==void 0)return s.size;if(g)return g.mergedSize.value;const H=u?.value?.Switch?.size;return H||"medium"}}),{mergedSizeRef:x,mergedDisabledRef:B}=h,a=V(s.defaultValue),k=Ge(s,"value"),$=Rt(k,a),R=F(()=>$.value===s.checkedValue),m=V(!1),T=V(!1),N=F(()=>{const{railStyle:g}=s;if(g)return g({focused:T.value,checked:R.value})});function E(g){const{"onUpdate:value":H,onChange:O,onUpdateValue:U}=s,{nTriggerFormInput:X,nTriggerFormChange:W}=h;H&&he(H,g),U&&he(U,g),O&&he(O,g),a.value=g,X(),W()}function q(){const{nTriggerFormFocus:g}=h;g()}function te(){const{nTriggerFormBlur:g}=h;g()}function se(){s.loading||B.value||($.value!==s.checkedValue?E(s.checkedValue):E(s.uncheckedValue))}function ne(){T.value=!0,q()}function J(){T.value=!1,te(),m.value=!1}function be(g){s.loading||B.value||g.key===" "&&($.value!==s.checkedValue?E(s.checkedValue):E(s.uncheckedValue),m.value=!1)}function Z(g){s.loading||B.value||g.key===" "&&(g.preventDefault(),m.value=!0)}const ue=F(()=>{const{value:g}=x,{self:{opacityDisabled:H,railColor:O,railColorActive:U,buttonBoxShadow:X,buttonColor:W,boxShadowFocus:c,loadingColor:we,textColor:ae,iconColor:ye,[Q("buttonHeight",g)]:P,[Q("buttonWidth",g)]:S,[Q("buttonWidthPressed",g)]:ie,[Q("railHeight",g)]:A,[Q("railWidth",g)]:i,[Q("railBorderRadius",g)]:n,[Q("buttonBorderRadius",g)]:r},common:{cubicBezierEaseInOut:Y}}=_.value;let D,le,xe;return de?(D=`calc((${A} - ${P}) / 2)`,le=`max(${A}, ${P})`,xe=`max(${i}, calc(${i} + ${P} - ${A}))`):(D=Se((j(A)-j(P))/2),le=Se(Math.max(j(A),j(P))),xe=j(A)>j(P)?i:Se(j(i)+j(P)-j(A))),{"--n-bezier":Y,"--n-button-border-radius":r,"--n-button-box-shadow":X,"--n-button-color":W,"--n-button-width":S,"--n-button-width-pressed":ie,"--n-button-height":P,"--n-height":le,"--n-offset":D,"--n-opacity-disabled":H,"--n-rail-border-radius":n,"--n-rail-color":O,"--n-rail-color-active":U,"--n-rail-height":A,"--n-rail-width":i,"--n-width":xe,"--n-box-shadow-focus":c,"--n-loading-color":we,"--n-text-color":ae,"--n-icon-color":ye}}),oe=d?Ut("switch",F(()=>x.value[0]),ue,s):void 0;return{handleClick:se,handleBlur:J,handleFocus:ne,handleKeyup:be,handleKeydown:Z,mergedRailStyle:N,pressed:m,mergedClsPrefix:t,mergedValue:$,checked:R,mergedDisabled:B,cssVars:d?void 0:ue,themeClass:oe?.themeClass,onRender:oe?.onRender}},render(){const{mergedClsPrefix:s,mergedDisabled:t,checked:d,mergedRailStyle:u,onRender:_,$slots:h}=this;_?.();const{checked:x,unchecked:B,icon:a,"checked-icon":k,"unchecked-icon":$}=h,R=!(Ce(a)&&Ce(k)&&Ce($));return w(),y("div",{role:"switch","aria-checked":d,class:C([`${s}-switch`,this.themeClass,R&&`${s}-switch--icon`,d&&`${s}-switch--active`,t&&`${s}-switch--disabled`,this.round&&`${s}-switch--round`,this.loading&&`${s}-switch--loading`,this.pressed&&`${s}-switch--pressed`,this.rubberBand&&`${s}-switch--rubber-band`]),tabindex:this.mergedDisabled?void 0:0,style:Re(this.cssVars),onClick:this.handleClick,onFocus:this.handleFocus,onBlur:this.handleBlur,onKeyup:this.handleKeyup,onKeydown:this.handleKeydown},[e("div",{class:C(`${s}-switch__rail`),"aria-hidden":"true",style:Re(u)},[z(()=>ee(x,m=>ee(B,T=>m||T?(w(),y("div",{key:4,"aria-hidden":!0,class:C(`${s}-switch__children-placeholder`)},[e("div",{class:C(`${s}-switch__rail-placeholder`)},[e("div",{class:C(`${s}-switch__button-placeholder`)},null,2),z(()=>m)],2),e("div",{class:C(`${s}-switch__rail-placeholder`)},[e("div",{class:C(`${s}-switch__button-placeholder`)},null,2),z(()=>T)],2)],2)):null))),e("div",{class:C(`${s}-switch__button`)},[z(()=>ee(a,m=>ee(k,T=>ee($,N=>(w(),ce(Je,null,{default:()=>this.loading?(w(),ce(It,gt({key:"loading",clsPrefix:s,strokeWidth:20},this.spinProps),null,16,["clsPrefix"])):this.checked&&(T||m)?(w(),y("div",{class:C(`${s}-switch__button-icon`),key:T?"checked-icon":"icon"},[z(()=>T||m)],2)):!this.checked&&(N||m)?(w(),y("div",{class:C(`${s}-switch__button-icon`),key:N?"unchecked-icon":"icon"},[z(()=>N||m)],2)):null},1024)))))),z(()=>ee(x,m=>m&&(w(),y("div",{key:"checked",class:C(`${s}-switch__checked`)},[z(()=>m)],2)))),z(()=>ee(B,m=>m&&(w(),y("div",{key:"unchecked",class:C(`${s}-switch__unchecked`)},[z(()=>
Line truncated
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
.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}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
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-DYsKpclu.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
@@ -0,0 +1 @@
|
|||||||
|
import{v as n,S as e,x as s}from"./index-DYsKpclu.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
@@ -0,0 +1 @@
|
|||||||
|
import{av as o,aw as a,W as r}from"./index-DYsKpclu.js";import{j as n,k as i,l as c}from"./admin-DEOkRyTC.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 it is too large.
Load diff
+28
@@ -0,0 +1,28 @@
|
|||||||
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/HomeView-D38U_AAJ.js","assets/PageShell-D87JkPkG.js","assets/SiteNav.vue_vue_type_script_setup_true_lang-CwaEJKIZ.js","assets/PageShell-PYQzNiuf.css","assets/share-CqDDLJWI.js","assets/HomeView-DcN_X0wH.css","assets/PickupView-ClAAhXsM.js","assets/LoginView-BnVXNxFO.js","assets/auth-B7MDTgxJ.js","assets/admin-DEOkRyTC.js","assets/LoginView-BgHqRIwi.css","assets/AdminLayout-DJ2FR9f-.js","assets/FilesView-ZJ5eqPp5.js","assets/Pager.vue_vue_type_script_setup_true_lang-B3GeH0mY.js","assets/AuditView-BHN4e5xt.js","assets/AuditView-f2PBwbQe.css","assets/SettingsView-CjSvbH8z.js","assets/SettingsView-GpXVMuCV.css","assets/DocsView-Ds78oqBY.js","assets/docsSource-BXkkn0HE.js","assets/markdown-B5D8JARp.js","assets/OpenApiView-4SbIV1Bx.js","assets/swagger-CqkleIqs.js","assets/OpenApiView-BCH8BgeP.css","assets/NotFoundView-CbfqSXyJ.js"])))=>i.map(i=>d[i]);
|
||||||
|
(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))r(n);new MutationObserver(n=>{for(const i of n)if(i.type==="childList")for(const l of i.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&r(l)}).observe(document,{childList:!0,subtree:!0});function o(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerPolicy&&(i.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?i.credentials="include":n.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(n){if(n.ep)return;n.ep=!0;const i=o(n);fetch(n.href,i)}})();function $l(e){const t=Object.create(null);for(const o of e.split(","))t[o]=1;return o=>o in t}const Se={},dr=[],Gt=()=>{},Fc=()=>!1,Jn=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Qn=e=>e.startsWith("onUpdate:"),Be=Object.assign,Bl=(e,t)=>{const o=e.indexOf(t);o>-1&&e.splice(o,1)},zd=Object.prototype.hasOwnProperty,_e=(e,t)=>zd.call(e,t),oe=Array.isArray,To=e=>un(e)==="[object Map]",er=e=>un(e)==="[object Set]",Ta=e=>un(e)==="[object Date]",le=e=>typeof e=="function",Le=e=>typeof e=="string",yt=e=>typeof e=="symbol",be=e=>e!==null&&typeof e=="object",Oc=e=>(be(e)||le(e))&&le(e.then)&&le(e.catch),Nc=Object.prototype.toString,un=e=>Nc.call(e),Ud=e=>un(e).slice(8,-1),Mc=e=>un(e)==="[object Object]",Zn=e=>Le(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Hr=$l(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),ei=e=>{const t=Object.create(null);return(o=>t[o]||(t[o]=e(o)))},Vd=/-\w/g,ct=ei(e=>e.replace(Vd,t=>t.slice(1).toUpperCase())),jd=/\B([A-Z])/g,Ro=ei(e=>e.replace(jd,"-$1").toLowerCase()),ti=ei(e=>e.charAt(0).toUpperCase()+e.slice(1)),Di=ei(e=>e?`on${ti(e)}`:""),Vt=(e,t)=>!Object.is(e,t),In=(e,...t)=>{for(let o=0;o<e.length;o++)e[o](...t)},kc=(e,t,o,r=!1)=>{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:r,value:o})},oi=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Gd=e=>{const t=Le(e)?Number(e):NaN;return isNaN(t)?e:t};let Pa;const ri=()=>Pa||(Pa=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function ni(e){if(oe(e)){const t={};for(let o=0;o<e.length;o++){const r=e[o],n=Le(r)?Xd(r):ni(r);if(n)for(const i in n)t[i]=n[i]}return t}else if(Le(e)||be(e))return e}const Kd=/;(?![^(]*\))/g,Yd=/:([^]+)/,qd=/\/\*[^]*?\*\//g;function Xd(e){const t={};return e.replace(qd,"").split(Kd).forEach(o=>{if(o){const r=o.split(Yd);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function ii(e){let t="";if(Le(e))t=e;else if(oe(e))for(let o=0;o<e.length;o++){const r=ii(e[o]);r&&(t+=r+" ")}else if(be(e))for(const o in e)e[o]&&(t+=o+" ");return t.trim()}const Jd="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",Qd=$l(Jd);function Hc(e){return!!e||e===""}function Zd(e,t){if(e.length!==t.length)return!1;let o=!0;for(let r=0;o&&r<e.length;r++)o=Po(e[r],t[r]);return o}function Ia(e,t){if(e.size!==t.size)return!1;const o=Array.from(t),r=new Uint8Array(o.length);for(const n of e){let i=-1;for(let l=0;l<o.length;l++)if(!r[l]&&Po(n,o[l])){i=l;break}if(i<0)return!1;r[i]=1}return!0}function Po(e,t){if(e===t)return!0;let o=Ta(e),r=Ta(t);if(o||r)return o&&r?e.getTime()===t.getTime():!1;if(o=yt(e),r=yt(t),o||r)return e===t;if(o=oe(e),r=oe(t),o||r)return o&&r?Zd(e,t):!1;if(o=be(e),r=be(t),o||r){if(!o||!r)return!1;if(o=To(e),r=To(t),o||r||(o=er(e),r=er(t),o||r))return o&&r?Ia(e,t):!1;const n=Object.keys(e).length,i=Object.keys(t).length;if(n!==i)return!1;for(const l in e){const a=e.hasOwnProperty(l),s=t.hasOwnProperty(l);if(a&&!s||!a&&s||!Po(e[l],t[l]))return!1}}return String(e)===String(t)}function ep(e,t){return e.findIndex(o=>Po(o,t))}const $c=e=>!!(e&&e.__v_isRef===!0),Dn=e=>Le(e)?e:e==null?"":oe(e)||be(e)&&(e.toString===Nc||!le(e.toString))?$c(e)?Dn(e.value):JSON.stringify(e,Bc,2):String(e),Bc=(e,t)=>$c(t)?Bc(e,t.value):To(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((o,[r,n],i)=>(o[Ri(r,i)+" =>"]=n,o),{})}:er(t)?{[`Set(${t.size})`]:[...t.values()].map(o=>Ri(o))}:yt(t)?Ri(t):be(t)&&!oe(t)&&!Mc(t)?String(t):t,Ri=(e,t="")=>{var o;return yt(e)?`Symbol(${(o=e.description)!=null?o:t})`:e};let He;class Wc{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!t&&He&&(He.active?(this.parent=He,this.index=(He.scopes||(He.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,o;if(this.scopes){const r=this.scopes.slice();for(t=0,o=r.length;t<o;t++)r[t].pause()}for(t=0,o=this.effects.length;t<o;t++)this.effects[t].pause()}}resume(){if(this._active&&this._isPaused){this._isPaused=!1;let t,o;if(this.scopes){const n=this.scopes.slice();foLine truncated
|
||||||
|
`))),e[Ko]=zn(n);const i=r||n.props&&n.props.type==="number";Go(e,t?"change":"input",l=>{l.target.composing||e[Ko](Ui(e.value,o,i))}),(o||i)&&Go(e,"change",()=>{e.value=Ui(e.value,o,i)}),t||(Go(e,"compositionstart",hh),Go(e,"compositionend",as),Go(e,"change",as))},mounted(e,{value:t,modifiers:{trim:o,number:r}}){const n=t??"",i=e[Sn];delete e[Sn],i!==void 0&&(e.type==="text"||e.type==="textarea")&&e.value!==i?e[Ko](Ui(e.value,o,r)):e.value=n},beforeUpdate(e,{value:t,oldValue:o,modifiers:{lazy:r,trim:n,number:i}},l){if(e[Ko]=zn(l),e.composing)return;const a=(i||e.type==="number")&&!/^0\d/.test(e.value)?oi(e.value):e.value,s=t??"";if(a===s)return;const c=e.getRootNode();(c instanceof Document||c instanceof ShadowRoot)&&c.activeElement===e&&e.type!=="range"&&(r&&t===o||n&&e.value.trim()===s)||(e.value=s)}},mT={deep:!0,created(e,{value:t,modifiers:{number:o}},r){e._modelValue=t,Go(e,"change",()=>{const n=Array.prototype.filter.call(e.options,s=>s.selected).map(s=>o?oi(Un(s)):Un(s)),i=e.multiple,l=i?er(e._modelValue)?new Set(n):n:n[0],a=e._pendingValue=[i,i?oe(l)?n.slice():n:l];try{e[Ko](l)}finally{ci(()=>{e._pendingValue===a&&(e._pendingValue=void 0)})}}),e[Ko]=zn(r)},mounted(e,{value:t}){ss(e,t)},beforeUpdate(e,{value:t},o){e._modelValue=t,e[Ko]=zn(o)},updated(e,{value:t}){const o=e._pendingValue;e._pendingValue=void 0,(!o||o[0]!==e.multiple||!gh(t,o[1],o[0]))&&ss(e,t)}};function gh(e,t,o){if(!o||oe(e))return Po(e,t);if(er(e)){if(e.size!==t.length)return!1;for(const r of t)if(!e.has(r))return!1;return!0}return!1}function ss(e,t){const o=e.multiple,r=oe(t);if(!(o&&!r&&!er(t))){for(let n=0,i=e.options.length;n<i;n++){const l=e.options[n],a=Un(l);if(o)if(r){const s=typeof a;s==="string"||s==="number"?l.selected=t.some(c=>String(c)===String(a)):l.selected=ep(t,a)>-1}else l.selected=t.has(a);else if(Po(Un(l),t)){e.selectedIndex!==n&&(e.selectedIndex=n);return}}!o&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function Un(e){return"_value"in e?e._value:e.value}const Ch=["ctrl","shift","alt","meta"],bh={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Ch.some(o=>e[`${o}Key`]&&!t.includes(o))},hT=(e,t)=>{if(!e)return e;const o=e._withMods||(e._withMods={}),r=t.join(".");return o[r]||(o[r]=((n,...i)=>{for(let l=0;l<t.length;l++){const a=bh[t[l]];if(a&&a(n,t))return}return e(n,...i)}))},xh={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},gT=(e,t)=>{const o=e._withKeys||(e._withKeys={}),r=t.join(".");return o[r]||(o[r]=(n=>{if(!("key"in n))return;const i=Ro(n.key);if(t.some(l=>l===i||xh[l]===i))return e(n)}))},_h=Be({patchProp:dh},Vm);let cs;function vh(){return cs||(cs=ym(_h))}const Sh=((...e)=>{const t=vh().createApp(...e),{mount:o}=t;return t.mount=r=>{const n=Eh(r);if(!n)return;const i=t._component;!le(i)&&!i.render&&!i.template&&(i.template=n.innerHTML),n.nodeType===1&&(n.textContent="");const l=o(n,!1,yh(n));return n instanceof Element&&(n.removeAttribute("v-cloak"),n.setAttribute("data-v-app","")),l},t});function yh(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function Eh(e){return Le(e)?document.querySelector(e):e}let Gu;const gi=e=>Gu=e,Ku=Symbol();function hl(e){return e&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"&&typeof e.toJSON!="function"}var Vr;(function(e){e.direct="direct",e.patchObject="patch object",e.patchFunction="patch function"})(Vr||(Vr={}));function Th(){const e=Wl(!0),t=e.run(()=>mt({}));let o=[],r=[];const n=qr({install(i){gi(n),n._a=i,i.provide(Ku,n),i.config.globalProperties.$pinia=n,r.forEach(l=>o.push(l)),r=[]},use(i){return this._a?o.push(i):r.push(i),this},_p:o,_a:null,_e:e,_s:new Map,state:t});return n}const Yu=()=>{};function us(e,t,o,r=Yu){e.add(t);const n=()=>{e.delete(t)&&r()};return!o&&zc()&&tp(n),n}function lr(e,...t){e.forEach(o=>{o(...t)})}const Ph=e=>e(),fs=Symbol(),Vi=Symbol();function gl(e,t){e instanceof Map&&t instanceof Map?t.forEach((o,r)=>e.set(r,o)):e instanceof Set&&t instanceof Set&&t.forEach(e.add,e);for(const o in t){if(!t.hasOwnProperty(o))continue;const r=t[o],n=e[o];hl(n)&&hl(r)&&e.hasOwnProperty(o)&&!we(r)&&!lo(r)?e[o]=gl(n,r):e[o]=r}return e}const Ih=Symbol();function Ah(e){return!hl(e)||!Object.prototype.hasOwnProperty.call(e,Ih)}const{assign:_o}=Object;function Lh(e){return!!(we(e)&&e.effect)}function wh(e,t,o,r){const{state:n,actions:i,getters:l}=t,a=o.state.value[e];let s;function c(){a||(o.state.value[e]=n?n():{});const u=Sp(o.state.value[e]);return _o(u,i,Object.keys(l||{}).reduce((f,d)=>(f[d]=qr(fe(()=>{gi(o);const p=o._s.get(e);return l[d].call(p,p)})),f),{}))}return s=qu(e,c,t,o,r,!0),s}function qu(e,t,o={},r,n,i){let l;const a=_o({actions:{}},o),s=
Line truncated
|
||||||
|
`+Object.entries(e).map(o=>t+` ${ff(o[0])}: ${o[1]};`).join(`
|
||||||
|
`)+`
|
||||||
|
`+t+"}":`: ${e};`}function Kg(e,t,o){return typeof e=="function"?e({context:t.context,props:o}):e}function Ls(e,t,o,r){if(!t)return"";const n=Kg(t,o,r);if(!n)return"";if(typeof n=="string")return`${e} {
|
||||||
|
${n}
|
||||||
|
}`;const i=Object.keys(n);if(i.length===0)return o.config.keepEmptyBlock?e+` {
|
||||||
|
}`:"";const l=e?[e+" {"]:[];return i.forEach(a=>{const s=n[a];if(a==="raw"){l.push(`
|
||||||
|
`+s+`
|
||||||
|
`);return}a=ff(a),s!=null&&l.push(` ${a}${Gg(s)}`)}),e&&l.push("}"),l.join(`
|
||||||
|
`)}function vl(e,t,o){e&&e.forEach(r=>{if(Array.isArray(r))vl(r,t,o);else if(typeof r=="function"){const n=r(t);Array.isArray(n)?vl(n,t,o):n&&o(n)}else r&&o(r)})}function df(e,t,o,r,n){const i=e.$;let l="";if(!i||typeof i=="string")yn(i)?l=i:t.push(i);else if(typeof i=="function"){const c=i({context:r.context,props:n});yn(c)?l=c:t.push(c)}else if(i.before&&i.before(r.context),!i.$||typeof i.$=="string")yn(i.$)?l=i.$:t.push(i.$);else if(i.$){const c=i.$({context:r.context,props:n});yn(c)?l=c:t.push(c)}const a=Ug(t),s=Ls(a,e.props,r,n);l?o.push(`${l} {`):s.length&&o.push(s),e.children&&vl(e.children,{context:r.context,props:n},c=>{if(typeof c=="string"){const u=Ls(a,{raw:c},r,n);o.push(u)}else df(c,t,o,r,n)}),t.pop(),l&&o.push("}"),i&&i.after&&i.after(r.context)}function Yg(e,t,o){const r=[];return df(e,[],r,t,o),r.join(`
|
||||||
|
|
||||||
|
`)}function Sl(e){for(var t=0,o,r=0,n=e.length;n>=4;++r,n-=4)o=e.charCodeAt(r)&255|(e.charCodeAt(++r)&255)<<8|(e.charCodeAt(++r)&255)<<16|(e.charCodeAt(++r)&255)<<24,o=(o&65535)*1540483477+((o>>>16)*59797<<16),o^=o>>>24,t=(o&65535)*1540483477+((o>>>16)*59797<<16)^(t&65535)*1540483477+((t>>>16)*59797<<16);switch(n){case 3:t^=(e.charCodeAt(r+2)&255)<<16;case 2:t^=(e.charCodeAt(r+1)&255)<<8;case 1:t^=e.charCodeAt(r)&255,t=(t&65535)*1540483477+((t>>>16)*59797<<16)}return t^=t>>>13,t=(t&65535)*1540483477+((t>>>16)*59797<<16),((t^t>>>15)>>>0).toString(36)}typeof window<"u"&&(window.__cssrContext={});function qg(e,t,o,r){const{els:n}=t;if(o===void 0)n.forEach(As),t.els=[];else{const i=ia(o,r);i&&n.includes(i)&&(As(i),t.els=n.filter(l=>l!==i))}}function ws(e,t){e.push(t)}function Xg(e,t,o,r,n,i,l,a,s){let c;if(o===void 0&&(c=t.render(r),o=Sl(c)),s){s.adapter(o,c??t.render(r));return}a===void 0&&(a=document.head);const u=ia(o,a);if(u!==null&&!i)return u;const f=u??Vg(o);if(c===void 0&&(c=t.render(r)),f.textContent=c,u!==null)return u;if(l){const d=a.querySelector(`meta[name="${l}"]`);if(d)return a.insertBefore(f,d),ws(t.els,f),f}return n?a.insertBefore(f,a.querySelector("style, link")):a.appendChild(f),ws(t.els,f),f}function Jg(e){return Yg(this,this.instance,e)}function Qg(e={}){const{id:t,ssr:o,props:r,head:n=!1,force:i=!1,anchorMetaName:l,parent:a}=e;return Xg(this.instance,this,t,r,n,i,l,a,o)}function Zg(e={}){const{id:t,parent:o}=e;qg(this.instance,this,t,o)}const En=function(e,t,o,r){return{instance:e,$:t,props:o,children:r,els:[],render:Jg,mount:Qg,unmount:Zg}},eC=function(e,t,o,r){return Array.isArray(t)?En(e,{$:null},null,t):Array.isArray(o)?En(e,t,null,o):Array.isArray(r)?En(e,t,o,r):En(e,t,o,null)};function tC(e={}){const t={c:((...o)=>eC(t,...o)),use:(o,...r)=>o.install(t,...r),find:ia,context:{},config:e};return t}const oC=".n-",rC="__",nC="--",pf=tC(),mf=Hg({blockPrefix:oC,elementPrefix:rC,modifierPrefix:nC});pf.use(mf);const{c:Ds,find:bT}=pf,{cB:xT,cE:_T,cM:vT,cNotM:ST}=mf;function yT(e,t){return e+(t==="default"?"":t.replace(/^[a-z]/,o=>o.toUpperCase()))}var hf=typeof global=="object"&&global&&global.Object===Object&&global,iC=typeof self=="object"&&self&&self.Object===Object&&self,Sr=hf||iC||Function("return this")(),Vn=Sr.Symbol,gf=Object.prototype,lC=gf.hasOwnProperty,aC=gf.toString,Rr=Vn?Vn.toStringTag:void 0;function sC(e){var t=lC.call(e,Rr),o=e[Rr];try{e[Rr]=void 0;var r=!0}catch{}var n=aC.call(e);return r&&(t?e[Rr]=o:delete e[Rr]),n}var cC=Object.prototype,uC=cC.toString;function fC(e){return uC.call(e)}var dC="[object Null]",pC="[object Undefined]",Rs=Vn?Vn.toStringTag:void 0;function xi(e){return e==null?e===void 0?pC:dC:Rs&&Rs in Object(e)?sC(e):fC(e)}function hn(e){return e!=null&&typeof e=="object"}var yl=Array.isArray;function or(e){var t=typeof e;return e!=null&&(t=="object"||t=="function")}function Cf(e){return e}var mC="[object AsyncFunction]",hC="[object Function]",gC="[object GeneratorFunction]",CC="[object Proxy]";function la(e){if(!or(e))return!1;var t=xi(e);return t==hC||t==gC||t==mC||t==CC}var qi=Sr["__core-js_shared__"],Fs=(function(){var e=/[^.]+$/.exec(qi&&qi.keys&&qi.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""})();function bC(e){return!!Fs&&Fs in e}var xC=Function.prototype,_C=xC.toString;function vC(e){if(e!=null){try{return _C.call(e)}catch{}try{return e+""}catch{}}return""}var SC=/[\\^$.*+?()[\]{}|]/g,yC=/^\[object .+?Constructor\]$/,EC=Function.prototype,TC=Object.prototype,PC=EC.toString,IC=TC.hasOwnProperty,AC=RegExp("^"+PC.call(IC).replace(SC,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");function LC(e){if(!or(e)||bC(e))return!1;var t=la(e)?AC:yC;return t.test(vC(e))}function wC(e,t){return e?.[t]}function aa(e,t){var o=wC(e,t);return LC(o)?o:void 0}var Os=Object.create,DC=(function(){function e(){}return function(t){if(!or(t))return{};if(Os)return Os(t);e.prototype=t;var o=new e;return e.prototype=void 0,o}})();function RC(e,t,o){switch(o.length){case 0:return e.call(t);case 1:return e.call(t,o[0]);case 2:return e.call(t,o[0],o[1]);case 3:return e.call(t,o[0],o[1],o[2])}return e.apply(t,o)}function FC(e,t){var o=-1,r=e.length;for(t||(t=Array(r));++o<r;)t[o]=e[o];return t}var OC=800,NC=16,MC=Date.now;function kC(e){var t=0,o=0;return function(){var r=MC(),n=NC-(r-o);if(o=r,n>0){if(++t>=OC)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}function HC(e){return function(){return e}}var jn=(function(){try{var e=aa(Object,"defineProperty");return e({},"",{}),e}catch{}})(),$C=jn?function(e,t){return jn(e,"toString",{configurable:!0,enumerable:!1,value:HC(t),writable:!0})}:Cf,BC=kC($C),WC=9007199254740991,zC=/^(?:0|[1-9]\d*)$/;function bf(e,t){var o=typeof e;return t=t??WC,!!t&&(o=="number"||o!="symbol"&&zC.test(e))&&e>-1&&e%1==0&&e<t}function sa(e,t,o){t=="__proto__"&&jn?jn(e,t,{configurable:!0,enumerable:!0,value:o,writable:!0}):e[t]=o}function _i(e,t){return e===t||e!==e&&t!==t}var UC=Object.prototype,VC=UC.has
Line truncated
|
||||||
|
margin: 0;
|
||||||
|
font-size: ${Rx};
|
||||||
|
font-family: ${Fx};
|
||||||
|
line-height: ${Ox};
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
`,[Ds("input",`
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
`)]);const Mx="@css-render/vue3-ssr";function kx(e,t){return`<style cssr-id="${e}">
|
||||||
|
${t}
|
||||||
|
</style>`}function Hx(e,t,o){const{styles:r,ids:n}=o;n.has(e)||r!==null&&(n.add(e),r.push(kx(e,t)))}const $x=typeof document<"u";function Bx(){if($x)return;const e=Ze(Mx,null);if(e!==null)return{adapter:(t,o)=>Hx(t,o,e),context:e}}const js={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aqua:"#0FF",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",bisque:"#FFE4C4",black:"#000",blanchedalmond:"#FFEBCD",blue:"#00F",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#0FF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgrey:"#A9A9A9",darkgreen:"#006400",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",fuchsia:"#F0F",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgray:"#D3D3D3",lightgrey:"#D3D3D3",lightgreen:"#90EE90",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",lime:"#0F0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#F0F",maroon:"#800000",mediumaquamarine:"#66CDAA",mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",navy:"#000080",oldlace:"#FDF5E6",olive:"#808000",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",purple:"#800080",rebeccapurple:"#663399",red:"#F00",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",silver:"#C0C0C0",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",teal:"#008080",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",white:"#FFF",whitesmoke:"#F5F5F5",yellow:"#FF0",yellowgreen:"#9ACD32",transparent:"#0000"};function Wx(e,t,o){t/=100,o/=100;let r=(n,i=(n+e/60)%6)=>o-o*t*Math.max(Math.min(i,4-i,1),0);return[r(5)*255,r(3)*255,r(1)*255]}function zx(e,t,o){t/=100,o/=100;let r=t*Math.min(o,1-o),n=(i,l=(i+e/30)%12)=>o-r*Math.max(Math.min(l-3,9-l,1),-1);return[n(0)*255,n(8)*255,n(4)*255]}const Yt="^\\s*",qt="\\s*$",Lo="\\s*((\\.\\d+)|(\\d+(\\.\\d*)?))%\\s*",_t="\\s*((\\.\\d+)|(\\d+(\\.\\d*)?))\\s*",qo="([0-9A-Fa-f])",Xo="([0-9A-Fa-f]{2})",Rf=new RegExp(`${Yt}hsl\\s*\\(${_t},${Lo},${Lo}\\)${qt}`),Ff=new RegExp(`${Yt}hsv\\s*\\(${_t},${Lo},${Lo}\\)${qt}`),Of=new RegExp(`${Yt}hsla\\s*\\(${_t},${Lo},${Lo},${_t}\\)${qt}`),Nf=new RegExp(`${Yt}hsva\\s*\\(${_t},${Lo},${Lo},${_t}\\)${qt}`),Ux=new RegExp(`${Yt}rgb\\s*\\(${_t},${_t},${_t}\\)${qt}`),Vx=new RegExp(`${Yt}rgba\\s*\\(${_t},${_t},${_t},${_t}\\)${qt}`),jx=new RegExp(`${Yt}#${qo}${qo}${qo}${qt}`),Gx=new RegExp(`${Yt}#${Xo}${Xo}${Xo}${qt}`),Kx=new RegExp(`${Yt}#${qo}${qo}${qo}${qo}${qt}`),Yx=new RegExp(`${Yt}#${Xo}${Xo}${Xo}${Xo}${qt}`);function dt(e){return parseInt(e,16)}function qx(e){try{let t;if(t=Of.exec(e))return[Gn(t[1]),Eo(t[5]),Eo(t[9]),Zo(t[13])];if(t=Rf.exec(e))return[Gn(t[1]),Eo(t[5]),Eo(t[9]),1];throw new Error(`[seemly/hsla]: Invalid color value ${e}.`)}catch(t){throw t}}function Xx(e){try{let t;if(t=Nf.exec(e))return[Gn(t[1]),Eo(t[5]),Eo(t[9]),Zo(t[13])];if(t=Ff.exec(e))return[Gn(t[1]),Eo(t[5]),Eo(t[9]),1];throw new Error(`[seemly/hsva]: Invalid color value ${e}.`)}catch(t){throw t}}function fo(e){try{let t;if(t=Gx.exec(e))return[dt(t[1]),dt(t[2]),dt(t[3]),1];if(t=Ux.exec(e))return[Xe(t[1]),Xe(t[5]),Xe(t[9]),1];if(t=Vx.exec(e))return[Xe(t[1]),Xe(t[5]),Xe(t[9]),Zo(t[13])];if(t=jx.exec(e))return[dt(t[1]+t[1]),dt(t[2]+t[2]),dt(t[3]+t[3]),1];if(t=Yx.exec(e))return[dt(t[1]),dt(t[2]),dt(t[3]),Zo(dt(t[4])/255)];if(t=Kx.exec(e))return[dt(t[1]+Line truncated
|
||||||
|
`,OS="\u2028",NS="\u2029";function MS(e){const t=e;let o=0,r=1,n=1,i=0;const l=D=>t[D]===FS&&t[D+1]===it,a=D=>t[D]===it,s=D=>t[D]===NS,c=D=>t[D]===OS,u=D=>l(D)||a(D)||s(D)||c(D),f=()=>o,d=()=>r,p=()=>n,g=()=>i,C=D=>l(D)||s(D)||c(D)?it:t[D],S=()=>C(o),E=()=>C(o+i);function T(){return i=0,u(o)&&(r++,n=0),l(o)&&o++,o++,n++,t[o]}function v(){return l(o+i)&&i++,i++,t[o+i]}function y(){o=0,r=1,n=1,i=0}function L(D=0){i=D}function w(){const D=o+i;for(;D!==o;)T();i=0}return{index:f,line:d,column:p,peekOffset:g,charAt:C,currentChar:S,currentPeek:E,next:T,peek:v,reset:y,resetPeek:L,skipToPeek:w}}const bo=void 0,kS=".",Js="'",HS="tokenizer";function $S(e,t={}){const o=t.location!==!1,r=MS(e),n=()=>r.index(),i=()=>PS(r.line(),r.column(),r.index()),l=i(),a=n(),s={currentType:14,offset:a,startLoc:l,endLoc:l,lastType:14,lastOffset:a,lastStartLoc:l,lastEndLoc:l,braceNest:0,inLinked:!1,text:""},c=()=>s,{onError:u}=t;function f(m,h,A,...O){const j=c();if(h.column+=A,h.offset+=A,u){const B=o?Yn(j.startLoc,h):null,I=Pr(m,B,{domain:HS,args:O});u(I)}}function d(m,h,A){m.endLoc=i(),m.currentType=h;const O={type:h};return o&&(O.loc=Yn(m.startLoc,m.endLoc)),A!=null&&(O.value=A),O}const p=m=>d(m,14);function g(m,h){return m.currentChar()===h?(m.next(),h):(f(ie.EXPECTED_TOKEN,i(),0,h),"")}function C(m){let h="";for(;m.currentPeek()===Zt||m.currentPeek()===it;)h+=m.currentPeek(),m.peek();return h}function S(m){const h=C(m);return m.skipToPeek(),h}function E(m){if(m===bo)return!1;const h=m.charCodeAt(0);return h>=97&&h<=122||h>=65&&h<=90||h===95}function T(m){if(m===bo)return!1;const h=m.charCodeAt(0);return h>=48&&h<=57}function v(m,h){const{currentType:A}=h;if(A!==2)return!1;C(m);const O=E(m.currentPeek());return m.resetPeek(),O}function y(m,h){const{currentType:A}=h;if(A!==2)return!1;C(m);const O=m.currentPeek()==="-"?m.peek():m.currentPeek(),j=T(O);return m.resetPeek(),j}function L(m,h){const{currentType:A}=h;if(A!==2)return!1;C(m);const O=m.currentPeek()===Js;return m.resetPeek(),O}function w(m,h){const{currentType:A}=h;if(A!==8)return!1;C(m);const O=m.currentPeek()===".";return m.resetPeek(),O}function D(m,h){const{currentType:A}=h;if(A!==9)return!1;C(m);const O=E(m.currentPeek());return m.resetPeek(),O}function F(m,h){const{currentType:A}=h;if(!(A===8||A===12))return!1;C(m);const O=m.currentPeek()===":";return m.resetPeek(),O}function P(m,h){const{currentType:A}=h;if(A!==10)return!1;const O=()=>{const B=m.currentPeek();return B==="{"?E(m.peek()):B==="@"||B==="%"||B==="|"||B===":"||B==="."||B===Zt||!B?!1:B===it?(m.peek(),O()):k(m,!1)},j=O();return m.resetPeek(),j}function U(m){C(m);const h=m.currentPeek()==="|";return m.resetPeek(),h}function X(m){const h=C(m),A=m.currentPeek()==="%"&&m.peek()==="{";return m.resetPeek(),{isModulo:A,hasSpace:h.length>0}}function k(m,h=!0){const A=(j=!1,B="",I=!1)=>{const M=m.currentPeek();return M==="{"?B==="%"?!1:j:M==="@"||!M?B==="%"?!0:j:M==="%"?(m.peek(),A(j,"%",!0)):M==="|"?B==="%"||I?!0:!(B===Zt||B===it):M===Zt?(m.peek(),A(!0,Zt,I)):M===it?(m.peek(),A(!0,it,I)):!0},O=A();return h&&m.resetPeek(),O}function Q(m,h){const A=m.currentChar();return A===bo?bo:h(A)?(m.next(),A):null}function me(m){const h=m.charCodeAt(0);return h>=97&&h<=122||h>=65&&h<=90||h>=48&&h<=57||h===95||h===36}function ye(m){return Q(m,me)}function se(m){const h=m.charCodeAt(0);return h>=97&&h<=122||h>=65&&h<=90||h>=48&&h<=57||h===95||h===36||h===45}function ne(m){return Q(m,se)}function de(m){const h=m.charCodeAt(0);return h>=48&&h<=57}function tt(m){return Q(m,de)}function ft(m){const h=m.charCodeAt(0);return h>=48&&h<=57||h>=65&&h<=70||h>=97&&h<=102}function Re(m){return Q(m,ft)}function Fe(m){let h="",A="";for(;h=tt(m);)A+=h;return A}function Tt(m){S(m);const h=m.currentChar();return h!=="%"&&f(ie.EXPECTED_TOKEN,i(),0,h),m.next(),"%"}function ht(m){let h="";for(;;){const A=m.currentChar();if(A==="{"||A==="}"||A==="@"||A==="|"||!A)break;if(A==="%")if(k(m))h+=A,m.next();else break;else if(A===Zt||A===it)if(k(m))h+=A,m.next();else{if(U(m))break;h+=A,m.next()}else h+=A,m.next()}return h}function gt(m){S(m);let h="",A="";for(;h=ne(m);)A+=h;return m.currentChar()===bo&&f(ie.UNTERMINATED_CLOSING_BRACE,i(),0),A}function We(m){S(m);let h="";return m.currentChar()==="-"?(m.next(),h+=`-${Fe(m)}`):h+=Fe(m),m.currentChar()===bo&&f(ie.UNTERMINATED_CLOSING_BRACE,i(),0),h}function H(m){return m!==Js&&m!==it}function Y(m){S(m),g(m,"'");let h="",A="";for(;h=Q(m,H);)h==="\\"?A+=G(m):A+=h;const O=m.currentChar();return O===it||O===bo?(f(ie.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER,i(),0),O===it&&(m.next(),g(m,"'")),A):(g(m,"'"),A)}function G(m){const h=m.currentChar();switch(h){case"\\":case"'":return m.next(),`\\${h}`;case"u":return ee(m,h,4);case"U":return ee(m,h,6);default:return f(ie.UNKNOWN_ESCAPE_SEQUENCE,i(),0,h),""}}function ee(m,h,A){g(m,h);let O="";for(let j=0;j<A;j++){const B=Re(m);if(!B){f(ie.INVALID_UNICODE_ESCAPE_SEQUENCE,i(),0,`\\${h}${O}${m.currentChar()}`);break}O+=B}return`\\${h}${O}`}function ue(m){return m!=="{"&&m!=="}"
Line truncated
|
||||||
|
`,i=t.needIndent?t.needIndent:o!=="arrow",l=e.helpers||[],a=qS(e,{filename:r,breakLineCode:n,needIndent:i});a.push(o==="normal"?"function __msg__ (ctx) {":"(ctx) => {"),a.indent(i),l.length>0&&(a.push(`const { ${sd(l.map(u=>`${u}: _${u}`),", ")} } = ctx`),a.newline()),a.push("return "),xr(a,e),a.deindent(i),a.push("}"),delete e.helpers;const{code:s,map:c}=a.context();return{ast:e,code:s,map:c?c.toJSON():void 0}};function ty(e,t={}){const o=ad({},t),r=!!o.jit,n=!!o.minify,i=o.optimize==null?!0:o.optimize,a=US(o).parse(e);return r?(i&&GS(a),n&&ur(a),{ast:a,code:""}):(jS(a,o),ey(a,o))}function oy(){typeof __INTLIFY_PROD_DEVTOOLS__!="boolean"&&(io().__INTLIFY_PROD_DEVTOOLS__=!1),typeof __INTLIFY_JIT_COMPILATION__!="boolean"&&(io().__INTLIFY_JIT_COMPILATION__=!1),typeof __INTLIFY_DROP_MESSAGE_COMPILER__!="boolean"&&(io().__INTLIFY_DROP_MESSAGE_COMPILER__=!1)}function Kt(e){return Ce(e)&&Ca(e)===0&&(Ot(e,"b")||Ot(e,"body"))}const cd=["b","body"];function ry(e){return Oo(e,cd)}const ud=["c","cases"];function ny(e){return Oo(e,ud,[])}const fd=["s","static"];function iy(e){return Oo(e,fd)}const dd=["i","items"];function ly(e){return Oo(e,dd,[])}const pd=["t","type"];function Ca(e){return Oo(e,pd)}const md=["v","value"];function Pn(e,t){const o=Oo(e,md);if(o!=null)return o;throw an(t)}const hd=["m","modifier"];function ay(e){return Oo(e,hd)}const gd=["k","key"];function sy(e){const t=Oo(e,gd);if(t)return t;throw an(6)}function Oo(e,t,o){for(let r=0;r<t.length;r++){const n=t[r];if(Ot(e,n)&&e[n]!=null)return e[n]}return o}const Cd=[...cd,...ud,...fd,...dd,...gd,...hd,...md,...pd];function an(e){return new Error(`unhandled node type: ${e}`)}const No=[];No[0]={w:[0],i:[3,0],"[":[4],o:[7]};No[1]={w:[1],".":[2],"[":[4],o:[7]};No[2]={w:[2],i:[3,0],0:[3,0]};No[3]={i:[3,0],0:[3,0],w:[1,1],".":[2,1],"[":[4,1],o:[7,1]};No[4]={"'":[5,0],'"':[6,0],"[":[4,2],"]":[1,3],o:8,l:[4,0]};No[5]={"'":[4,0],o:8,l:[5,0]};No[6]={'"':[4,0],o:8,l:[6,0]};const cy=/^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;function uy(e){return cy.test(e)}function fy(e){const t=e.charCodeAt(0),o=e.charCodeAt(e.length-1);return t===o&&(t===34||t===39)?e.slice(1,-1):e}function dy(e){if(e==null)return"o";switch(e.charCodeAt(0)){case 91:case 93:case 46:case 34:case 39:return e;case 95:case 36:case 45:return"i";case 9:case 10:case 13:case 160:case 65279:case 8232:case 8233:return"w"}return"i"}function py(e){const t=e.trim();return e.charAt(0)==="0"&&isNaN(parseInt(e))?!1:uy(t)?fy(t):"*"+t}function my(e){const t=[];let o=-1,r=0,n=0,i,l,a,s,c,u,f;const d=[];d[0]=()=>{l===void 0?l=a:l+=a},d[1]=()=>{l!==void 0&&(t.push(l),l=void 0)},d[2]=()=>{d[0](),n++},d[3]=()=>{if(n>0)n--,r=4,d[0]();else{if(n=0,l===void 0||(l=py(l),l===!1))return!1;d[1]()}};function p(){const g=e[o+1];if(r===5&&g==="'"||r===6&&g==='"')return o++,a="\\"+g,d[0](),!0}for(;r!==null;)if(o++,i=e[o],!(i==="\\"&&p())){if(s=dy(i),f=No[r],c=f[s]||f.l||8,c===8||(r=c[0],c[1]!==void 0&&(u=d[c[1]],u&&(a=i,u()===!1))))return;if(r===7)return t}}const ec=new Map;function hy(e,t){return Ce(e)?e[t]:null}function gy(e,t){if(!Ce(e))return null;let o=ec.get(t);if(o||(o=my(t),o&&ec.set(t,o)),!o)return null;const r=o.length;let n=e,i=0;for(;i<r;){const l=o[i];if(Cd.includes(l)&&Kt(n))return null;const a=n[l];if(a===void 0||Pe(n))return null;n=a,i++}return n}const Cy=e=>e,by=e=>"",xy="text",_y=e=>e.length===0?"":ES(e),vy=yS;function tc(e,t){return e=Math.abs(e),t===2?e?e>1?1:0:1:e?Math.min(e,2):0}function Sy(e){const t=Me(e.pluralIndex)?e.pluralIndex:-1;return e.named&&(Me(e.named.count)||Me(e.named.n))?Me(e.named.count)?e.named.count:Me(e.named.n)?e.named.n:t:t}function yy(e,t){t.count||(t.count=e),t.n||(t.n=e)}function Ey(e={}){const t=e.locale,o=Sy(e),r=Ce(e.pluralRules)&&K(t)&&Pe(e.pluralRules[t])?e.pluralRules[t]:tc,n=Ce(e.pluralRules)&&K(t)&&Pe(e.pluralRules[t])?tc:void 0,i=E=>E[r(o,E.length,n)],l=e.list||[],a=E=>l[E],s=e.named||ve();Me(e.pluralIndex)&&yy(o,s);const c=E=>s[E];function u(E){const T=Pe(e.messages)?e.messages(E):Ce(e.messages)?e.messages[E]:!1;return T||(e.parent?e.parent.message(E):by)}const f=E=>e.modifiers?e.modifiers[E]:Cy,d=ae(e.processor)&&Pe(e.processor.normalize)?e.processor.normalize:_y,p=ae(e.processor)&&Pe(e.processor.interpolate)?e.processor.interpolate:vy,g=ae(e.processor)&&K(e.processor.type)?e.processor.type:xy,S={list:a,named:c,plural:i,linked:(E,...T)=>{const[v,y]=T;let L="text",w="";T.length===1?Ce(v)?(w=v.modifier||w,L=v.type||L):K(v)&&(w=v||w):T.length===2&&(K(v)&&(w=v||w),K(y)&&(L=y||L));const D=u(E)(S),F=L==="vnode"&&Ae(D)&&w?D[0]:D;return w?f(w)(F,L):F},message:u,type:g,interpolate:p,normalize:d,values:Ge(ve(),l,s)};return S}let sn=null;function Ty(e){sn=e}function Py(e,t,o){sn&&sn.emit("i18n:init",{timestamp:Date.now(),i18n:e,version:t,meta:o})}const Iy=Ay("function:translate");function Ay(e){return t=>sn&&sn.emit(e,t)}const Ly=ha.__EXTEND_POINT__,Wo=Pi(Ly),wy={FALLBACK_TO_TRANSLATE:Wo(),CANNOT_FORMAT_NUMBER:Wo(),FALLBACK_TO_NUMBER_FORMAT:Wo(),CANNOT_FORMAT_DATE:Wo(),FALLBACK
Line truncated
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
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-DYsKpclu.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;
|
background: #0b0f1a;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="module" crossorigin src="/assets/index-BumeFNdu.js"></script>
|
<script type="module" crossorigin src="/assets/index-DYsKpclu.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-CtyCxWf5.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-CtyCxWf5.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Whitespace-only changes.
+4
-7
@@ -2,13 +2,10 @@
|
|||||||
|
|
||||||
Vue 3 + Vite + TypeScript 单页应用:分享 / 取件 / 管理后台(文件管理、审计日志、系统设置)/ 站内文档。
|
Vue 3 + Vite + TypeScript 单页应用:分享 / 取件 / 管理后台(文件管理、审计日志、系统设置)/ 站内文档。
|
||||||
|
|
||||||
26.9(任务 t3):液态玻璃主题(亮/暗/跟随系统)、中英双语(vue-i18n)、本地 Logo/favicon 打包资源、
|
v2(任务 t3):液态玻璃主题(亮/暗/跟随系统)、中英双语(vue-i18n)、本地 Logo/favicon 打包资源、
|
||||||
背景图 / 页脚自定义 / 系统通知(读 `GET /api/v1/config` 下发的 26.9 字段)、上传页策略动态渲染、
|
背景图 / 页脚自定义 / 系统通知(读 `GET /api/v1/config` 下发的 v2 字段)、上传页策略动态渲染、
|
||||||
管理设置页全量策略表单(PATCH `/admin/config/update`)。
|
管理设置页全量策略表单(PATCH `/admin/config/update`)。
|
||||||
|
|
||||||
26.9 起在管理设置「上传频率限制」卡底部新增**上下行带宽限速**字段(MB/s,0=不限速,
|
|
||||||
提交后换算为字节/秒写入 `upload_rate`/`download_rate` KV)。
|
|
||||||
|
|
||||||
## 页面
|
## 页面
|
||||||
|
|
||||||
| 路由 | 说明 |
|
| 路由 | 说明 |
|
||||||
@@ -29,7 +26,7 @@ Vue 3 + Vite + TypeScript 单页应用:分享 / 取件 / 管理后台(文件
|
|||||||
- 语言(导航栏切换 + `localStorage` 键 `fcb_locale`):默认 `navigator.language`(zh 开头 → zh-CN,其余 → en-US)。
|
- 语言(导航栏切换 + `localStorage` 键 `fcb_locale`):默认 `navigator.language`(zh 开头 → zh-CN,其余 → en-US)。
|
||||||
文案在 `src/locales/`,新增文案先加 zh-CN 再同步 en-US。
|
文案在 `src/locales/`,新增文案先加 zh-CN 再同步 en-US。
|
||||||
|
|
||||||
## 26.9 配置字段(消费 `GET /api/v1/config`,管理端经 `PATCH /admin/config/update` 写回)
|
## v2 配置字段(消费 `GET /api/v1/config`,管理端经 `PATCH /admin/config/update` 写回)
|
||||||
|
|
||||||
`background_url`、`footer_text`、`footer_beian`、`notify_enabled/notify_title/notify_content`、
|
`background_url`、`footer_text`、`footer_beian`、`notify_enabled/notify_title/notify_content`、
|
||||||
`max_file_size`、`max_save_seconds`、`max_save_count`、`allowedFileTypes`、`expireStyle`、
|
`max_file_size`、`max_save_seconds`、`max_save_count`、`allowedFileTypes`、`expireStyle`、
|
||||||
@@ -66,7 +63,7 @@ npm run preview # 预览构建产物
|
|||||||
- 构建产物输出到 `web/dist/`(入口 `web/dist/index.html`)。**注意**:`web/` 在 Go module(server/)之外,go:embed 无法直接引用 —— go-api 需在构建脚本中拷贝(如 `cp -a web/dist server/web/dist`)或以 `os.DirFS` 方式挂载;`base: '/'` + history 路由配合其 NoRoute SPA 回退(静态资源优先、GET+text/html 回 index.html、/api/* JSON 404)。
|
- 构建产物输出到 `web/dist/`(入口 `web/dist/index.html`)。**注意**:`web/` 在 Go module(server/)之外,go:embed 无法直接引用 —— go-api 需在构建脚本中拷贝(如 `cp -a web/dist server/web/dist`)或以 `os.DirFS` 方式挂载;`base: '/'` + history 路由配合其 NoRoute SPA 回退(静态资源优先、GET+text/html 回 index.html、/api/* JSON 404)。
|
||||||
- 如需改输出目录,调整 `vite.config.ts` 的 `build.outDir`。
|
- 如需改输出目录,调整 `vite.config.ts` 的 `build.outDir`。
|
||||||
|
|
||||||
## Logo 自定义(26.9:本地资源兜底)
|
## Logo 自定义(v2:本地资源兜底)
|
||||||
|
|
||||||
- 默认导航 Logo:`src/assets/brand/logo.svg`(构建内嵌;`DEFAULT_LOGO_URL`)
|
- 默认导航 Logo:`src/assets/brand/logo.svg`(构建内嵌;`DEFAULT_LOGO_URL`)
|
||||||
- 默认 favicon:`src/assets/brand/favicon.png`(`index.html` + `DEFAULT_FAVICON_URL`)
|
- 默认 favicon:`src/assets/brand/favicon.png`(`index.html` + `DEFAULT_FAVICON_URL`)
|
||||||
|
|||||||
Generated
+4
-4
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "fileshare-web",
|
"name": "filecodebox-web",
|
||||||
"version": "26.9",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "fileshare-web",
|
"name": "filecodebox-web",
|
||||||
"version": "26.9",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"marked": "^16.3.0",
|
"marked": "^16.3.0",
|
||||||
"naive-ui": "^2.45.3",
|
"naive-ui": "^2.45.3",
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "fileshare-web",
|
"name": "filecodebox-web",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "26.9",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "FileCodeBox Go 版前端:分享 / 取件 / 管理后台 / 审计日志 / 站内文档",
|
"description": "FileCodeBox Go 版前端:分享 / 取件 / 管理后台 / 审计日志 / 站内文档",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ export async function request<T>(path: string, opts: RequestOptions = {}): Promi
|
|||||||
if (json !== undefined) body = JSON.stringify(json)
|
if (json !== undefined) body = JSON.stringify(json)
|
||||||
else if (formData) body = formData
|
else if (formData) body = formData
|
||||||
else if (form) {
|
else if (form) {
|
||||||
// 26.9 根因修复:URLSearchParams.toString() 产出的是字符串 body,
|
// v3.1.1 根因修复:URLSearchParams.toString() 产出的是字符串 body,
|
||||||
// fetch 对字符串默认 Content-Type: text/plain —— 后端无法按表单解析,
|
// fetch 对字符串默认 Content-Type: text/plain —— 后端无法按表单解析,
|
||||||
// 文本分享被静默存成空(或被空文本校验拒绝)。必须显式声明 urlencoded。
|
// 文本分享被静默存成空(或被空文本校验拒绝)。必须显式声明 urlencoded。
|
||||||
body = new URLSearchParams(form).toString()
|
body = new URLSearchParams(form).toString()
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function routerBase(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function pickupPageUrl(code: string, base?: string): string {
|
export function pickupPageUrl(code: string, base?: string): string {
|
||||||
// 26.9:配置了站点对外域名时用之(内网访问也能生成公网链接),否则当前地址
|
// v3.1:配置了站点对外域名时用之(内网访问也能生成公网链接),否则当前地址
|
||||||
const origin = (base?.trim() || location.origin).replace(/\/$/, '')
|
const origin = (base?.trim() || location.origin).replace(/\/$/, '')
|
||||||
return `${origin}${routerBase()}${encodeURIComponent(code)}`
|
return `${origin}${routerBase()}${encodeURIComponent(code)}`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ export async function getMetadata(code: string): Promise<ShareMetadata> {
|
|||||||
remainingDownloads:
|
remainingDownloads:
|
||||||
(pick<number>(raw, ['remaining_downloads', 'remainingDownloads', 'expired_count', 'expiredCount']) as number) ??
|
(pick<number>(raw, ['remaining_downloads', 'remainingDownloads', 'expired_count', 'expiredCount']) as number) ??
|
||||||
null,
|
null,
|
||||||
downloadUrl: (pick<string>(raw, ['download_url', 'downloadUrl']) as string) ?? null,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ export interface ShareMetadata {
|
|||||||
expiredCount: number | null
|
expiredCount: number | null
|
||||||
usedCount: number
|
usedCount: number
|
||||||
remainingDownloads: number | null
|
remainingDownloads: number | null
|
||||||
/** 26.9:select 接口返回的下载地址(代理路径或 S3 预签名直链) */
|
|
||||||
downloadUrl?: string | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShareTextResult {
|
export interface ShareTextResult {
|
||||||
|
|||||||
@@ -40,12 +40,6 @@ const acceptHint = computed(() => {
|
|||||||
function accept(f: File | null | undefined): void {
|
function accept(f: File | null | undefined): void {
|
||||||
error.value = ''
|
error.value = ''
|
||||||
if (!f) return
|
if (!f) return
|
||||||
// 26.9:拒绝文件夹条目(webkitRelativePath 非空 = 来自文件夹选择)
|
|
||||||
if ((f as File & { webkitRelativePath?: string }).webkitRelativePath) {
|
|
||||||
error.value = t('drop.folderHint')
|
|
||||||
emit('update:modelValue', null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (props.maxSize && f.size > props.maxSize) {
|
if (props.maxSize && f.size > props.maxSize) {
|
||||||
error.value = t('drop.tooLarge', { size: formatBytes(f.size), limit: formatBytes(props.maxSize) })
|
error.value = t('drop.tooLarge', { size: formatBytes(f.size), limit: formatBytes(props.maxSize) })
|
||||||
emit('update:modelValue', null)
|
emit('update:modelValue', null)
|
||||||
@@ -54,25 +48,9 @@ function accept(f: File | null | undefined): void {
|
|||||||
emit('update:modelValue', f)
|
emit('update:modelValue', f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 26.9:拖拽条目里是否夹带目录(dataTransfer.files 对目录为空,须用 entries 探测) */
|
|
||||||
function hasDirectory(items: DataTransferItemList | null | undefined): boolean {
|
|
||||||
if (!items) return false
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
const entry = items[i]?.webkitGetAsEntry?.()
|
|
||||||
if (entry?.isDirectory) return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDrop(e: DragEvent): void {
|
function onDrop(e: DragEvent): void {
|
||||||
dragover.value = false
|
dragover.value = false
|
||||||
if (props.disabled) return
|
if (props.disabled) return
|
||||||
// 26.9:拖入目录 → 明确提示"建议压缩后上传"(替代此前的静默忽略)
|
|
||||||
if (hasDirectory(e.dataTransfer?.items)) {
|
|
||||||
error.value = t('drop.folderHint')
|
|
||||||
emit('update:modelValue', null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
accept(e.dataTransfer?.files?.[0])
|
accept(e.dataTransfer?.files?.[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const link = computed(() => pickupPageUrl(props.code, config.shareLinkBase))
|
|||||||
|
|
||||||
async function copy(value: 'code' | 'link'): Promise<void> {
|
async function copy(value: 'code' | 'link'): Promise<void> {
|
||||||
// 复制链接本身即可:链接已含取件码(/s/<code>),再拼一遍会导致
|
// 复制链接本身即可:链接已含取件码(/s/<code>),再拼一遍会导致
|
||||||
// 「/s/XVR3H XVR3H」被整串当码查询而 404(26.9 遗留缺陷)
|
// 「/s/XVR3H XVR3H」被整串当码查询而 404(v3.1.2 遗留缺陷)
|
||||||
const text = value === 'link' ? link.value : props.code
|
const text = value === 'link' ? link.value : props.code
|
||||||
const ok = await copyText(text)
|
const ok = await copyText(text)
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
|||||||
@@ -47,9 +47,6 @@ export default {
|
|||||||
close: 'Close',
|
close: 'Close',
|
||||||
actions: 'Actions',
|
actions: 'Actions',
|
||||||
all: 'All',
|
all: 'All',
|
||||||
enabled: 'Enabled',
|
|
||||||
disabled: 'Disabled',
|
|
||||||
days: 'days',
|
|
||||||
query: 'Query',
|
query: 'Query',
|
||||||
reset: 'Reset',
|
reset: 'Reset',
|
||||||
previousPage: 'Previous',
|
previousPage: 'Previous',
|
||||||
@@ -147,8 +144,6 @@ export default {
|
|||||||
loadingText: 'Fetching content…',
|
loadingText: 'Fetching content…',
|
||||||
copyContent: 'Copy content',
|
copyContent: 'Copy content',
|
||||||
downloadTxt: 'Download as .txt',
|
downloadTxt: 'Download as .txt',
|
||||||
previewImage: 'Image preview',
|
|
||||||
previewAudio: 'Audio playback',
|
|
||||||
downloaded: 'Download complete',
|
downloaded: 'Download complete',
|
||||||
downloadFailed: 'Download failed, please retry',
|
downloadFailed: 'Download failed, please retry',
|
||||||
copied: 'Content copied',
|
copied: 'Content copied',
|
||||||
@@ -175,7 +170,6 @@ export default {
|
|||||||
remove: 'Remove',
|
remove: 'Remove',
|
||||||
tooLarge: 'File size {size} exceeds the limit {limit}',
|
tooLarge: 'File size {size} exceeds the limit {limit}',
|
||||||
typeHint: 'Allowed types: {types}',
|
typeHint: 'Allowed types: {types}',
|
||||||
folderHint: 'Folder upload is not supported. Please compress it into an archive first.',
|
|
||||||
},
|
},
|
||||||
docs: {
|
docs: {
|
||||||
searchPlaceholder: 'Search documentation…',
|
searchPlaceholder: 'Search documentation…',
|
||||||
@@ -366,28 +360,6 @@ export default {
|
|||||||
uploadCountHint: 'Min 1, max {max}',
|
uploadCountHint: 'Min 1, max {max}',
|
||||||
uploadMinute: 'Window length (minutes) · uploadMinute',
|
uploadMinute: 'Window length (minutes) · uploadMinute',
|
||||||
uploadMinuteHint: 'Min 1, max {max}',
|
uploadMinuteHint: 'Min 1, max {max}',
|
||||||
uploadRate: 'Upload bandwidth (optional)',
|
|
||||||
uploadRateHint: '0 = unlimited; MB/s; range 0~1024. Live-effective (admin reads latest KV each request)',
|
|
||||||
sectionRecycle: 'Recycle & Download Safety',
|
|
||||||
recycleHint: 'Expired-share recycling, retention limit, content dedup, direct download and hotlink protection',
|
|
||||||
recycleEnabled: 'Auto-recycle expired',
|
|
||||||
recycleEnabledHint: 'Periodically delete expired/exhausted shares together with their stored objects',
|
|
||||||
recycleInterval: 'Recycle scan interval',
|
|
||||||
recycleIntervalHint: '1~1440 minutes; accessing an expired share also triggers immediate recycling',
|
|
||||||
retentionDays: 'Max retention (days)',
|
|
||||||
retentionDaysHint: 'Shares older than this many days are recycled automatically; 0 = unlimited',
|
|
||||||
dedupEnabled: 'SHA512 dedup',
|
|
||||||
dedupEnabledHint: 'Identical files are stored once (multiple shares reference the same object)',
|
|
||||||
directDownload: 'Direct download',
|
|
||||||
directDownloadHint: '302 redirect to a presigned URL on S3-like engines; bytes bypass this server',
|
|
||||||
directLinkExpire: 'Direct link TTL',
|
|
||||||
directLinkExpireHint: '1~60 minutes (never longer than the share remains valid)',
|
|
||||||
hotlinkEnabled: 'Hotlink protection',
|
|
||||||
hotlinkEnabledHint: 'Validates Referer against a whitelist; empty Referer (direct visit) is allowed',
|
|
||||||
hotlinkWhitelist: 'Hotlink whitelist',
|
|
||||||
hotlinkWhitelistHint: 'Comma-separated domains, supports *.example.com; empty = same-site only',
|
|
||||||
downloadRate: 'Download bandwidth (optional)',
|
|
||||||
downloadRateHint: '0 = unlimited; MB/s; range 0~1024. S3 presigned direct upload cannot be throttled server-side',
|
|
||||||
// —— v3: friendly units + storage engine ——
|
// —— v3: friendly units + storage engine ——
|
||||||
unitHour: 'Hour(s)',
|
unitHour: 'Hour(s)',
|
||||||
unitDay: 'Day(s)',
|
unitDay: 'Day(s)',
|
||||||
|
|||||||
@@ -47,9 +47,6 @@ export default {
|
|||||||
close: '关闭',
|
close: '关闭',
|
||||||
actions: '操作',
|
actions: '操作',
|
||||||
all: '全部',
|
all: '全部',
|
||||||
enabled: '开启',
|
|
||||||
disabled: '关闭',
|
|
||||||
days: '天',
|
|
||||||
query: '查询',
|
query: '查询',
|
||||||
reset: '重置',
|
reset: '重置',
|
||||||
previousPage: '上一页',
|
previousPage: '上一页',
|
||||||
@@ -143,8 +140,6 @@ export default {
|
|||||||
loadingText: '正在获取内容…',
|
loadingText: '正在获取内容…',
|
||||||
copyContent: '复制内容',
|
copyContent: '复制内容',
|
||||||
downloadTxt: '下载为 .txt',
|
downloadTxt: '下载为 .txt',
|
||||||
previewImage: '图片预览',
|
|
||||||
previewAudio: '音频播放',
|
|
||||||
downloaded: '下载完成',
|
downloaded: '下载完成',
|
||||||
downloadFailed: '下载失败,请重试',
|
downloadFailed: '下载失败,请重试',
|
||||||
copied: '内容已复制',
|
copied: '内容已复制',
|
||||||
@@ -171,7 +166,6 @@ export default {
|
|||||||
remove: '移除',
|
remove: '移除',
|
||||||
tooLarge: '文件大小 {size} 超过限制 {limit}',
|
tooLarge: '文件大小 {size} 超过限制 {limit}',
|
||||||
typeHint: '仅支持 {types}',
|
typeHint: '仅支持 {types}',
|
||||||
folderHint: '暂不支持文件夹上传,建议压缩后上传',
|
|
||||||
},
|
},
|
||||||
docs: {
|
docs: {
|
||||||
searchPlaceholder: '检索文档内容…',
|
searchPlaceholder: '检索文档内容…',
|
||||||
@@ -360,28 +354,6 @@ export default {
|
|||||||
uploadCountHint: '最小 1,最大 {max}',
|
uploadCountHint: '最小 1,最大 {max}',
|
||||||
uploadMinute: '频率窗口(分钟)uploadMinute',
|
uploadMinute: '频率窗口(分钟)uploadMinute',
|
||||||
uploadMinuteHint: '最小 1,最大 {max}',
|
uploadMinuteHint: '最小 1,最大 {max}',
|
||||||
uploadRate: '上行带宽(可选)',
|
|
||||||
uploadRateHint: '0 = 不限速;单位 MB/s;范围 0~1024。修改后立即生效(管理端读最新 KV)',
|
|
||||||
sectionRecycle: '回收与下载安全',
|
|
||||||
recycleHint: '过期分享自动回收、存储时长上限、内容去重、直链下载与防盗链',
|
|
||||||
recycleEnabled: '过期自动回收',
|
|
||||||
recycleEnabledHint: '开启后定时清理已过期/次数耗尽的分享(含其存储对象)',
|
|
||||||
recycleInterval: '回收扫描间隔',
|
|
||||||
recycleIntervalHint: '范围 1~1440 分钟;取件时发现过期也会立即触发回收',
|
|
||||||
retentionDays: '最长存储时长',
|
|
||||||
retentionDaysHint: '上传超过该天数的分享将被自动回收;0 = 不限制',
|
|
||||||
dedupEnabled: 'SHA512 内容去重',
|
|
||||||
dedupEnabledHint: '相同内容的文件仅存储一份(多分享引用同一对象)',
|
|
||||||
directDownload: '直链下载',
|
|
||||||
directDownloadHint: 'S3 等对象存储引擎时 302 跳转到限时直链,文件不经过本站带宽',
|
|
||||||
directLinkExpire: '直链有效期',
|
|
||||||
directLinkExpireHint: '范围 1~60 分钟(不超过分享剩余时效)',
|
|
||||||
hotlinkEnabled: '下载防盗链',
|
|
||||||
hotlinkEnabledHint: '校验 Referer 白名单;空 Referer(直接访问)放行',
|
|
||||||
hotlinkWhitelist: '防盗链白名单',
|
|
||||||
hotlinkWhitelistHint: '逗号分隔域名,支持 *.example.com;空 = 仅本站域名放行',
|
|
||||||
downloadRate: '下行带宽(可选)',
|
|
||||||
downloadRateHint: '0 = 不限速;单位 MB/s;范围 0~1024。S3 预签名直传(客户端→S3)无法限速',
|
|
||||||
// —— v3:单位友好化 + 存储引擎 ——
|
// —— v3:单位友好化 + 存储引擎 ——
|
||||||
unitHour: '小时',
|
unitHour: '小时',
|
||||||
unitDay: '天',
|
unitDay: '天',
|
||||||
|
|||||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user