Initial commit

This commit is contained in:
2026-01-28 20:44:34 +08:00
commit 500e8b74a7
236 changed files with 29886 additions and 0 deletions

67
scripts/build.bat Normal file
View File

@@ -0,0 +1,67 @@
@echo off
chcp 65001 > nul
setlocal enabledelayedexpansion
:: 切换到项目根目录
cd /d "%~dp0.."
set APP_NAME=filerelay
set OUTPUT_DIR=output
echo 开始构建 %APP_NAME% 多平台二进制文件...
:: 清理 output 目录
if exist "%OUTPUT_DIR%" (
echo 正在清理 %OUTPUT_DIR% 目录...
rd /s /q "%OUTPUT_DIR%"
)
mkdir "%OUTPUT_DIR%"
:: 前端构建
echo 正在构建前端项目...
pushd webapp
call npm install
if %ERRORLEVEL% neq 0 (
echo npm install 失败,停止编译。
popd
exit /b %ERRORLEVEL%
)
call npm run build
if %ERRORLEVEL% neq 0 (
echo 前端构建失败,停止编译。
popd
exit /b %ERRORLEVEL%
)
popd
:: 定义目标平台 (OS/Arch)
set PLATFORMS=linux/amd64 linux/arm64 windows/amd64 windows/arm64 darwin/amd64 darwin/arm64
for %%P in (%PLATFORMS%) do (
for /f "tokens=1,2 delims=/" %%A in ("%%P") do (
set GOOS=%%A
set GOARCH=%%B
set OUTPUT_NAME=%APP_NAME%-%%A-%%B
if "%%A"=="windows" set OUTPUT_NAME=!OUTPUT_NAME!.exe
echo 正在编译 %%A/%%B...
go build -o "%OUTPUT_DIR%\!OUTPUT_NAME!" main.go
if !ERRORLEVEL! equ 0 (
echo %%A/%%B 编译成功
:: 压缩为 tar.gz (Windows 10+ 自带 tar)
tar -czf "%OUTPUT_DIR%\!OUTPUT_NAME!.tar.gz" -C "%OUTPUT_DIR%" "!OUTPUT_NAME!"
:: 删除原始二进制文件
del "%OUTPUT_DIR%\!OUTPUT_NAME!"
) else (
echo %%A/%%B 编译失败
)
)
)
echo ----------------------------------------
echo 多平台打包完成!输出目录: %OUTPUT_DIR%
pause

78
scripts/build.ps1 Normal file
View File

@@ -0,0 +1,78 @@
# 构建脚本
# 切换到项目根目录
Set-Location -Path (Join-Path $PSScriptRoot "..")
$APP_NAME = "filerelay"
$OUTPUT_DIR = "output"
# 定义目标平台
$PLATFORMS = @(
"linux/amd64",
"linux/arm64",
"windows/amd64",
"windows/arm64",
"darwin/amd64",
"darwin/arm64"
)
Write-Host "开始构建 $APP_NAME 多平台二进制文件..." -ForegroundColor Cyan
# 清理 output 目录
if (Test-Path $OUTPUT_DIR) {
Write-Host "正在清理 $OUTPUT_DIR 目录..."
Remove-Item -Path $OUTPUT_DIR -Recurse -Force
}
New-Item -Path $OUTPUT_DIR -ItemType Directory -Force | Out-Null
# 前端构建
Write-Host "正在构建前端项目..." -ForegroundColor Cyan
Push-Location webapp
npm install
if ($LASTEXITCODE -ne 0) {
Write-Host "npm install 失败,停止编译。" -ForegroundColor Red
Pop-Location
exit $LASTEXITCODE
}
npm run build
if ($LASTEXITCODE -ne 0) {
Write-Host "前端构建失败,停止编译。" -ForegroundColor Red
Pop-Location
exit $LASTEXITCODE
}
Pop-Location
# 循环构建各平台
foreach ($PLATFORM in $PLATFORMS) {
$parts = $PLATFORM -split "/"
$os = $parts[0]
$arch = $parts[1]
$outputName = "$($APP_NAME)-$($os)-$($arch)"
if ($os -eq "windows") {
$outputName += ".exe"
}
Write-Host "正在编译 $($os)/$($arch)..."
$env:GOOS = $os
$env:GOARCH = $arch
go build -o (Join-Path $OUTPUT_DIR $outputName) main.go
if ($LASTEXITCODE -eq 0) {
Write-Host " $($os)/$($arch) 编译成功" -ForegroundColor Green
# 压缩为 tar.gz
tar -czf (Join-Path $OUTPUT_DIR "$outputName.tar.gz") -C $OUTPUT_DIR $outputName
# 删除原始二进制文件
Remove-Item (Join-Path $OUTPUT_DIR $outputName)
} else {
Write-Host " $($os)/$($arch) 编译失败" -ForegroundColor Red
}
}
# 重置环境变量
$env:GOOS = $null
$env:GOARCH = $null
Write-Host "----------------------------------------" -ForegroundColor Cyan
Write-Host "多平台打包完成!输出目录: $OUTPUT_DIR" -ForegroundColor Green

69
scripts/build.sh Normal file
View File

@@ -0,0 +1,69 @@
#!/bin/bash
# 获取脚本所在目录并切换到项目根目录
cd "$(dirname "$0")/.."
# 设置变量
APP_NAME="filerelay"
OUTPUT_DIR="output"
# 定义目标平台
PLATFORMS=(
"linux/amd64"
"linux/arm64"
"windows/amd64"
"windows/arm64"
"darwin/amd64"
"darwin/arm64"
)
echo "开始构建 $APP_NAME 多平台二进制文件..."
# 清理 output 目录
if [ -d "$OUTPUT_DIR" ]; then
echo "正在清理 $OUTPUT_DIR 目录..."
rm -rf "$OUTPUT_DIR"
fi
mkdir -p "$OUTPUT_DIR"
# 前端构建
echo "正在构建前端项目..."
cd webapp
npm install
npm run build
if [ $? -ne 0 ]; then
echo "前端构建失败,停止编译。"
exit 1
fi
cd ..
# 循环编译各平台
for PLATFORM in "${PLATFORMS[@]}"; do
# 分离 OS 和 ARCH
OS=$(echo $PLATFORM | cut -d'/' -f1)
ARCH=$(echo $PLATFORM | cut -d'/' -f2)
# 设置输出名称
OUTPUT_NAME="${APP_NAME}-${OS}-${ARCH}"
if [ "$OS" = "windows" ]; then
OUTPUT_NAME="${OUTPUT_NAME}.exe"
fi
echo "正在编译 ${OS}/${ARCH}..."
GOOS=$OS GOARCH=$ARCH go build -o "${OUTPUT_DIR}/${OUTPUT_NAME}" main.go
if [ $? -eq 0 ]; then
echo " ${OS}/${ARCH} 编译成功"
# 压缩为 tar.gz
tar -czf "${OUTPUT_DIR}/${OUTPUT_NAME}.tar.gz" -C "${OUTPUT_DIR}" "${OUTPUT_NAME}"
# 删除原始二进制文件
rm "${OUTPUT_DIR}/${OUTPUT_NAME}"
else
echo " ${OS}/${ARCH} 编译失败"
fi
done
echo "----------------------------------------"
echo "多平台打包完成!输出目录: $OUTPUT_DIR"
ls -R "$OUTPUT_DIR"

101
scripts/deploy.sh Normal file
View File

@@ -0,0 +1,101 @@
#!/bin/bash
# 定时任务示例 (每30分钟执行一次):
# */30 * * * * /path/to/project/scripts/deploy.sh >> /path/to/project/scripts/deploy_cron.log 2>&1
# 项目根目录
# 假设脚本位于 scripts 目录下
PROJECT_DIR=$(cd $(dirname $0)/.. && pwd)
cd $PROJECT_DIR
# 日志文件
LOG_FILE="$PROJECT_DIR/scripts/deploy.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# 确保在项目根目录
if [ ! -f "docker-compose.yaml" ]; then
log "错误: 未能在 $PROJECT_DIR 找到 docker-compose.yml请确保脚本位置正确。"
exit 1
fi
log "开始检查更新..."
# 获取远程代码
git fetch origin
# 获取当前分支名
BRANCH=$(git rev-parse --abbrev-ref HEAD)
# 检查本地分支是否有上游分支
UPSTREAM=$(git rev-parse --abbrev-ref @{u} 2>/dev/null)
if [ -z "$UPSTREAM" ]; then
log "错误: 当前分支 $BRANCH 没有设置上游分支,无法自动对比更新。"
exit 1
fi
# 检查本地是否落后于远程
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse "$UPSTREAM")
if [ "$LOCAL" = "$REMOTE" ]; then
log "代码已是最新 ($LOCAL),无需更新。"
exit 0
fi
log "检测到远程变更 ($LOCAL -> $REMOTE),准备开始升级..."
# 检查是否有本地修改
HAS_CHANGES=$(git status --porcelain)
if [ -n "$HAS_CHANGES" ]; then
log "检测到本地修改,正在暂存以保留个性化配置..."
git stash
STASHED=true
else
STASHED=false
fi
# 拉取新代码
log "正在拉取远程代码 ($BRANCH)..."
if git pull origin "$BRANCH"; then
log "代码拉取成功。"
else
log "错误: 代码拉取失败。"
if [ "$STASHED" = true ]; then
git stash pop
fi
exit 1
fi
# 恢复本地修改
if [ "$STASHED" = true ]; then
log "正在恢复本地修改..."
if git stash pop; then
log "本地修改已成功恢复。"
else
log "警告: 恢复本地修改时发生冲突,请手动检查 docker-compose.yml 等文件。"
# 即使冲突也尝试继续,或者你可以选择在此退出
fi
fi
# 确定 docker-compose 命令
DOCKER_COMPOSE_BIN="docker-compose"
if ! command -v $DOCKER_COMPOSE_BIN &> /dev/null; then
DOCKER_COMPOSE_BIN="docker compose"
fi
# 执行 docker-compose 部署
log "正在执行 $DOCKER_COMPOSE_BIN 部署..."
if $DOCKER_COMPOSE_BIN up -d --build; then
log "服务升级成功!"
# 清理无用镜像(可选)
docker image prune -f
else
log "错误: $DOCKER_COMPOSE_BIN 部署失败。"
exit 1
fi
log "部署任务完成。"

59
scripts/release_tag.bat Normal file
View File

@@ -0,0 +1,59 @@
@echo off
chcp 65001 > nul
setlocal
:: 切换到项目根目录
cd /d "%~dp0.."
if "%~1"=="" (
echo 使用方法: release_tag.bat <tag_name>
echo 例如: release_tag.bat v1.0.0
exit /b 1
)
set TAG_NAME=%~1
REM 1. 检查当前分支是否为 master
for /f "tokens=*" %%i in ('git rev-parse --abbrev-ref HEAD') do set CURRENT_BRANCH=%%i
if not "%CURRENT_BRANCH%"=="master" (
echo 错误:当前分支是 %CURRENT_BRANCH%,只能在 master 分支上创建 Tag。
exit /b 1
)
REM 2. 检查本地是否有未提交的内容
for /f "tokens=*" %%i in ('git status --porcelain') do (
echo 错误:本地有未提交的更改,请先提交或 stash。
exit /b 1
)
REM 3. 检查本地与远程 master 分支是否一致
echo 正在从远程获取最新 master 分支信息...
git fetch origin master
if %ERRORLEVEL% neq 0 (
echo 错误:获取远程分支信息失败。
exit /b 1
)
for /f "tokens=*" %%i in ('git rev-parse HEAD') do set LOCAL_HASH=%%i
for /f "tokens=*" %%i in ('git rev-parse origin/master') do set REMOTE_HASH=%%i
if not "%LOCAL_HASH%"=="%REMOTE_HASH%" (
echo 错误:本地 master 分支与远程 origin/master 不一致,请先 pull 或 push。
exit /b 1
)
echo 安全检查通过,正在处理 Tag: %TAG_NAME%
:: 在本地创建或更新 Tag
git tag -f %TAG_NAME%
:: 强制推送到远程
echo 正在强制推送到远程...
git push origin %TAG_NAME% --force
if %ERRORLEVEL% equ 0 (
echo 成功Tag %TAG_NAME% 已发布并同步至远程。
) else (
echo 失败:推送过程中出现错误。
exit /b 1
)

53
scripts/release_tag.ps1 Normal file
View File

@@ -0,0 +1,53 @@
param (
[Parameter(Mandatory=$true, Position=0)]
[string]$TagName
)
# 切换到项目根目录
Set-Location -Path (Join-Path $PSScriptRoot "..")
# 1. 检查当前分支是否为 master
$currentBranch = git rev-parse --abbrev-ref HEAD
if ($currentBranch -ne "master") {
Write-Host "错误:当前分支是 $currentBranch,只能在 master 分支上创建 Tag。" -ForegroundColor Red
exit 1
}
# 2. 检查本地是否有未提交的内容
$status = git status --porcelain
if ($null -ne $status -and $status.Length -gt 0) {
Write-Host "错误:本地有未提交的更改,请先提交或 stash。" -ForegroundColor Red
exit 1
}
# 3. 检查本地与远程 master 分支是否一致
Write-Host "正在从远程获取最新 master 分支信息..." -ForegroundColor Cyan
git fetch origin master
$localHash = git rev-parse HEAD
$remoteHash = git rev-parse origin/master
if ($localHash -ne $remoteHash) {
Write-Host "错误:本地 master 分支与远程 origin/master 不一致,请先 pull 或 push。" -ForegroundColor Red
exit 1
}
Write-Host "安全检查通过,正在处理 Tag: $TagName" -ForegroundColor Cyan
# 在本地创建或更新 Tag
git tag -f $TagName
if ($LASTEXITCODE -ne 0) {
Write-Host "本地创建 Tag 失败。" -ForegroundColor Red
exit 1
}
# 强制推送到远程
Write-Host "正在强制推送到远程..." -ForegroundColor Cyan
git push origin $TagName --force
if ($LASTEXITCODE -eq 0) {
Write-Host "成功Tag $TagName 已发布并同步至远程。" -ForegroundColor Green
} else {
Write-Host "失败:推送过程中出现错误。" -ForegroundColor Red
exit 1
}

55
scripts/release_tag.sh Normal file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
# 获取脚本所在目录并切换到项目根目录
cd "$(dirname "$0")/.."
# 检查是否提供了 tag 名称
if [ -z "$1" ]; then
echo "使用方法: scripts/release_tag.sh <tag_name>"
echo "例如: scripts/release_tag.sh v1.0.0"
exit 1
fi
TAG_NAME=$1
# 1. 检查当前分支是否为 master
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT_BRANCH" != "master" ]; then
echo "错误:当前分支是 $CURRENT_BRANCH,只能在 master 分支上创建 Tag。"
exit 1
fi
# 2. 检查本地是否有未提交的内容
if [ -n "$(git status --porcelain)" ]; then
echo "错误:本地有未提交的更改,请先提交或 stash。"
exit 1
fi
# 3. 检查本地与远程 master 分支是否一致
echo "正在从远程获取最新 master 分支信息..."
git fetch origin master
LOCAL_HASH=$(git rev-parse HEAD)
REMOTE_HASH=$(git rev-parse origin/master)
if [ "$LOCAL_HASH" != "$REMOTE_HASH" ]; then
echo "错误:本地 master 分支与远程 origin/master 不一致,请先 pull 或 push。"
exit 1
fi
echo "安全检查通过,正在处理 Tag: $TAG_NAME"
# 在本地创建或更新 Tag
# -f 表示如果存在则强制更新
git tag -f "$TAG_NAME"
# 强制推送到远程
# --force 会覆盖远程同名 Tag
echo "正在强制推送到远程..."
git push origin "$TAG_NAME" --force
if [ $? -eq 0 ]; then
echo "成功Tag $TAG_NAME 已发布并同步至远程。"
else
echo "失败:推送过程中出现错误。"
exit 1
fi

View File

@@ -0,0 +1,39 @@
-- MySQL 初始化语句
CREATE TABLE IF NOT EXISTS `file_batches` (
`id` varchar(36) PRIMARY KEY,
`pickup_code` varchar(255) UNIQUE NOT NULL,
`remark` longtext,
`expire_type` varchar(255),
`expire_at` datetime(3),
`max_downloads` bigint,
`download_count` bigint DEFAULT 0,
`status` varchar(255) DEFAULT 'active',
`type` varchar(255) DEFAULT 'file',
`content` longtext,
`created_at` datetime(3),
`updated_at` datetime(3),
`deleted_at` datetime(3),
KEY `idx_file_batches_deleted_at` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `file_items` (
`id` varchar(36) PRIMARY KEY,
`batch_id` varchar(36) NOT NULL,
`original_name` longtext,
`storage_path` longtext,
`size` bigint,
`mime_type` longtext,
`created_at` datetime(3),
KEY `idx_file_items_batch_id` (`batch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `api_tokens` (
`id` bigint unsigned AUTO_INCREMENT PRIMARY KEY,
`name` longtext,
`token_hash` varchar(255) UNIQUE NOT NULL,
`scope` longtext,
`expire_at` datetime(3),
`last_used_at` datetime(3),
`revoked` boolean DEFAULT false,
`created_at` datetime(3)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@@ -0,0 +1,40 @@
-- PostgreSQL 初始化语句
CREATE TABLE IF NOT EXISTS "file_batches" (
"id" varchar(36) PRIMARY KEY,
"pickup_code" varchar(255) UNIQUE NOT NULL,
"remark" text,
"expire_type" text,
"expire_at" timestamptz,
"max_downloads" bigint,
"download_count" bigint DEFAULT 0,
"status" text DEFAULT 'active',
"type" text DEFAULT 'file',
"content" text,
"created_at" timestamptz,
"updated_at" timestamptz,
"deleted_at" timestamptz
);
CREATE INDEX IF NOT EXISTS "idx_file_batches_deleted_at" ON "file_batches" ("deleted_at");
CREATE TABLE IF NOT EXISTS "file_items" (
"id" varchar(36) PRIMARY KEY,
"batch_id" varchar(36) NOT NULL,
"original_name" text,
"storage_path" text,
"size" bigint,
"mime_type" text,
"created_at" timestamptz,
CONSTRAINT "fk_file_batches_file_items" FOREIGN KEY ("batch_id") REFERENCES "file_batches"("id")
);
CREATE INDEX IF NOT EXISTS "idx_file_items_batch_id" ON "file_items" ("batch_id");
CREATE TABLE IF NOT EXISTS "api_tokens" (
"id" bigserial PRIMARY KEY,
"name" text,
"token_hash" varchar(255) UNIQUE NOT NULL,
"scope" text,
"expire_at" timestamptz,
"last_used_at" timestamptz,
"revoked" boolean DEFAULT false,
"created_at" timestamptz
);

View File

@@ -0,0 +1,40 @@
-- SQLite 初始化语句
CREATE TABLE IF NOT EXISTS `file_batches` (
`id` varchar(36) PRIMARY KEY,
`pickup_code` varchar(255) UNIQUE NOT NULL,
`remark` text,
`expire_type` text,
`expire_at` datetime,
`max_downloads` integer,
`download_count` integer DEFAULT 0,
`status` text DEFAULT 'active',
`type` text DEFAULT 'file',
`content` text,
`created_at` datetime,
`updated_at` datetime,
`deleted_at` datetime
);
CREATE INDEX IF NOT EXISTS `idx_file_batches_deleted_at` ON `file_batches` (`deleted_at`);
CREATE TABLE IF NOT EXISTS `file_items` (
`id` varchar(36) PRIMARY KEY,
`batch_id` varchar(36) NOT NULL,
`original_name` text,
`storage_path` text,
`size` bigint,
`mime_type` text,
`created_at` datetime,
FOREIGN KEY (`batch_id`) REFERENCES `file_batches`(`id`)
);
CREATE INDEX IF NOT EXISTS `idx_file_items_batch_id` ON `file_items` (`batch_id`);
CREATE TABLE IF NOT EXISTS `api_tokens` (
`id` integer PRIMARY KEY AUTOINCREMENT,
`name` text,
`token_hash` varchar(255) UNIQUE NOT NULL,
`scope` text,
`expire_at` datetime,
`last_used_at` datetime,
`revoked` boolean DEFAULT 0,
`created_at` datetime
);