Author SHA1 Message Date
loveuer a6d97b4a6f fix: v0.1.5
/ build ushare (push) Successful in 5s
/ clean (push) Successful in 0s
1. add nginx proxy headers
2025-05-16 21:54:58 +08:00
loveuer 53ece08ba8 fix: 0.1.4
1. meta clean goroutine walk error
  2. clean interval to args(--clean)
2025-05-16 16:51:05 +08:00
loveuer 9146c87cad feat: 0.1.3
/ build ushare (push) Successful in 39s
/ clean (push) Successful in 0s
1. 完善 dockerfile 和 entrypoint
2025-05-12 15:54:52 +08:00
loveuer b1b3ac5b6b feat: 0.1.2
1. login page && auth required
  2. file dir cleanup
2025-05-12 15:54:52 +08:00
loveuer a2635698e0 init: 0.1.1
feat: login page && auth required
todo: 断点上传
2025-05-12 15:54:49 +08:00
loveuer b3ff70167f init: 0.1.0
feat: 基本功能有了
  1. 分片上传
  2. code 回显
  3. 下载
todo: 断点上传
2025-05-12 15:54:49 +08:00
loveuer 7ec8cd5c0e init: 0.0.8
feat: 实现了分片上传
todo:
  1. 上传完成后 code 回显
  2. 清理不用的临时 code file
  3. 断点续传
  4. 多 chunk 并发上传
2025-05-12 15:54:43 +08:00
loveuer fb96934abe init: 0.0.4 2025-05-12 15:54:40 +08:00
8 changed files with 101 additions and 23 deletions
+3 -2
View File
@@ -1,2 +1,3 @@
node_modules frontend/node_modules
dist dist
frontend/dist
+24 -12
View File
@@ -5,7 +5,7 @@ on:
- 'v*' - 'v*'
jobs: jobs:
build and run u-api: build ushare:
runs-on: tencent-sg runs-on: tencent-sg
steps: steps:
- name: prepare enviroment - name: prepare enviroment
@@ -17,9 +17,6 @@ jobs:
- name: print operator - name: print operator
run: whoami run: whoami
- name: print work dir and files
run: pwd & ls -alsh .
- name: print tag name - name: print tag name
run: echo "Tag name = ${{ gitea.ref_name }}" run: echo "Tag name = ${{ gitea.ref_name }}"
@@ -29,14 +26,29 @@ jobs:
${{ secrets.DOCKER_CONFIG }} ${{ secrets.DOCKER_CONFIG }}
EOF EOF
- name: build by kaniko in docker - name: print work dir and files
run: | run: pwd & ls -alsh .
docker run --rm -v $(pwd):/workspace \
-v $(pwd)/.docker.config.json:/kaniko/.docker/config.json:ro \ - name: build image by docker build
gcr.io/kaniko-project/executor:latest \ run: docker build -t gitea.loveuer.com/loveuer/build/ushare:${{ gitea.ref_name }} .
'-f /workspace/Dockerfile' \
'-d "gitea.loveuer.com/loveuer/build/ushare:${{ gitea.ref_name }}"' \ - name: login repository
'--single-snapshot' run: echo ${{ secrets.DOCKER_REPOSITORY_PASSWORD }} | docker login --username loveuer --password-stdin gitea.loveuer.com/loveuer
- name: push image to repository
run: docker push gitea.loveuer.com/loveuer/build/ushare:${{ gitea.ref_name }}
# - name: build by kaniko in docker
# run: |
# docker run --rm -v $(pwd):/workspace \
# -v $(pwd)/.docker.config.json:/kaniko/.docker/config.json:ro \
# alpine:latest \
# ls -alsh /workspace
# gcr.io/kaniko-project/executor:latest \
# --dockerfile=/workspace/Dockerfile \
# --context=/workspace \
# --destination=gitea.loveuer.com/loveuer/build/u-api:${{ gitea.ref_name }} \
# --single-snapshot
clean: clean:
if: always() if: always()
+4 -1
View File
@@ -27,9 +27,12 @@ COPY --from=backend-builder /app/ushare /usr/local/bin/ushare
# 配置 Nginx # 配置 Nginx
RUN rm /etc/nginx/conf.d/default.conf RUN rm /etc/nginx/conf.d/default.conf
COPY deployment/nginx.conf /etc/nginx/conf.d COPY deployment/nginx.conf /etc/nginx/conf.d
COPY deployment/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
# 开放端口 # 开放端口
EXPOSE 80 EXPOSE 80
# 启动服务 # 启动服务
CMD sh -c "ushare & nginx -g 'daemon off;'" ENTRYPOINT ["entrypoint.sh"]
+35
View File
@@ -0,0 +1,35 @@
#!/bin/sh
set -e
# 启动后端服务(带参数后台运行)
echo "启动后端服务,参数: $@"
ushare "$@" &
BACKEND_PID=$!
# 定义优雅关闭函数
graceful_shutdown() {
echo "接收到终止信号,开始优雅关闭..."
# 先关闭 Nginx
echo "关闭 Nginx..."
nginx -s quit 2>/dev/null || kill -TERM $NGINX_PID 2>/dev/null
# 关闭后端服务
echo "关闭后端服务 PID $BACKEND_PID..."
kill -TERM $BACKEND_PID 2>/dev/null
# 等待进程终止
wait $BACKEND_PID $NGINX_PID 2>/dev/null
exit 0
}
# 捕获系统信号
trap 'graceful_shutdown' SIGTERM SIGINT
# 启动 Nginx(前台运行)
echo "启动 Nginx..."
nginx -g "daemon off;" &
NGINX_PID=$!
# 等待所有后台进程
wait $BACKEND_PID $NGINX_PID
+5
View File
@@ -18,5 +18,10 @@ server {
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
send_timeout 3600s;
proxy_buffering off;
gzip off;
} }
} }
+22 -3
View File
@@ -104,6 +104,10 @@ func (m *meta) Start(ctx context.Context) {
ticker := time.NewTicker(time.Minute) ticker := time.NewTicker(time.Minute)
m.ctx = ctx m.ctx = ctx
if err := os.MkdirAll(opt.Cfg.DataPath, 0644); err != nil {
log.Fatal("controller.MetaManager.Start: mkdir datapath failed, path = %s, err = %s", opt.Cfg.DataPath, err.Error())
}
// 清理 2 分钟内没有继续上传的 part // 清理 2 分钟内没有继续上传的 part
go func() { go func() {
for { for {
@@ -131,14 +135,25 @@ func (m *meta) Start(ctx context.Context) {
// 清理一天前的文件 // 清理一天前的文件
go func() { go func() {
if opt.Cfg.CleanInterval <= 0 {
log.Warn("meta.Clean: no clean interval set, plz clean manual!!!")
return
}
ticker := time.NewTicker(5 * time.Minute) ticker := time.NewTicker(5 * time.Minute)
duration := time.Duration(opt.Cfg.CleanInterval) * time.Hour
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return return
case now := <-ticker.C: case now := <-ticker.C:
//log.Debug("meta.Clean: 开始清理过期文件 = %v", duration)
_ = filepath.Walk(opt.Cfg.DataPath, func(path string, info os.FileInfo, err error) error { _ = filepath.Walk(opt.Cfg.DataPath, func(path string, info os.FileInfo, err error) error {
if info == nil {
return nil
}
if info.IsDir() { if info.IsDir() {
return nil return nil
} }
@@ -164,12 +179,16 @@ func (m *meta) Start(ctx context.Context) {
code := strings.TrimPrefix(name, ".meta.") code := strings.TrimPrefix(name, ".meta.")
if now.Sub(time.UnixMilli(mi.CreatedAt)) > 24*time.Hour { if now.Sub(time.UnixMilli(mi.CreatedAt)) > duration {
log.Debug("controller.meta: file out of date, code = %s, user_key = %s", code, mi.Uploader) log.Debug("controller.meta: file out of date, code = %s, user_key = %s", code, mi.Uploader)
os.RemoveAll(opt.FilePath(code)) if err = os.RemoveAll(opt.FilePath(code)); err != nil {
os.RemoveAll(path) log.Warn("meta.Clean: remove file failed, file = %s, err = %s", opt.FilePath(code), err.Error())
}
if err = os.RemoveAll(path); err != nil {
log.Warn("meta.Clean: remove file failed, file = %s, err = %s", path, err.Error())
}
m.Lock() m.Lock()
delete(m.m, code) delete(m.m, code)
+5 -4
View File
@@ -7,10 +7,11 @@ import (
) )
type config struct { type config struct {
Debug bool Debug bool
Address string Address string
DataPath string DataPath string
Auth string Auth string
CleanInterval int
} }
var ( var (
+3 -1
View File
@@ -7,6 +7,7 @@ import (
"github.com/loveuer/ushare/internal/api" "github.com/loveuer/ushare/internal/api"
"github.com/loveuer/ushare/internal/controller" "github.com/loveuer/ushare/internal/controller"
"github.com/loveuer/ushare/internal/opt" "github.com/loveuer/ushare/internal/opt"
"github.com/loveuer/ushare/internal/pkg/tool"
"os/signal" "os/signal"
"syscall" "syscall"
) )
@@ -16,11 +17,12 @@ func init() {
flag.StringVar(&opt.Cfg.Address, "address", "0.0.0.0:9119", "") flag.StringVar(&opt.Cfg.Address, "address", "0.0.0.0:9119", "")
flag.StringVar(&opt.Cfg.DataPath, "data", "/data", "") flag.StringVar(&opt.Cfg.DataPath, "data", "/data", "")
flag.StringVar(&opt.Cfg.Auth, "auth", "", "auth required(admin, password)") flag.StringVar(&opt.Cfg.Auth, "auth", "", "auth required(admin, password)")
flag.IntVar(&opt.Cfg.CleanInterval, "clean", 24, "清理文件的周期, 单位: 小时, 0 则表示不自动清理")
flag.Parse() flag.Parse()
if opt.Cfg.Debug { if opt.Cfg.Debug {
log.SetLogLevel(log.LogLevelDebug) log.SetLogLevel(log.LogLevelDebug)
log.Debug("start server with debug mode") tool.TablePrinter(opt.Cfg)
} }
} }