Files
chiyiandDevin 834ecacdb9 cyi-box: Memphis-style toolbox admin platform
Go(fun)+SQLite backend, Vue 3 frontend. Modules: dashboard, pay gateway
(Epay-compatible + CDK stock), license/card system (per-app appid, domain
binding), AI memory bank with built-in MCP server (/mcp, FTS5 search,
signed read links), server SSH monitor + terminal + SFTP, proxy pool with
protocol-level alive checks, DB manager (mysql/pg/sqlite + SQL
autocomplete + import/export), disk share, vault (AES-GCM + TOTP),
bookmarks, dev tools.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-01 01:58:47 +08:00

149 lines
3.4 KiB
Go

package store
import (
"database/sql"
"errors"
"time"
"github.com/cyi-cc/cyi-box/backend/internal/db"
)
// ---- disk 网盘 ----
type File struct {
Id int64
UserID int64
Name string // 原始文件名
Stored string // 落盘文件名(随机)
Size int64
Mime string
CreatedAt int64
}
type Share struct {
Id int64
Code string
FileID int64
UserID int64
FileName string
ExpiresAt int64 // 0 = 永久
Downloads int64
CreatedAt int64
}
func fileFromRow(f db.File) File {
return File{
Id: f.ID, UserID: f.UserID, Name: f.Name, Stored: f.Stored,
Size: f.Size, Mime: f.Mime, CreatedAt: f.CreatedAt,
}
}
func shareFromRow(s db.Share) Share {
return Share{
Id: s.ID, Code: s.Code, FileID: s.FileID, UserID: s.UserID,
FileName: s.FileName, ExpiresAt: s.ExpiresAt,
Downloads: s.Downloads, CreatedAt: s.CreatedAt,
}
}
func (s *Store) InsertFile(f *File) error {
id, err := s.q.InsertFile(storeCtx, db.InsertFileParams{
UserID: f.UserID, Name: f.Name, Stored: f.Stored,
Size: f.Size, Mime: f.Mime, CreatedAt: time.Now().Unix(),
})
if err != nil {
return err
}
f.Id = id
return nil
}
func (s *Store) ListFiles(userID int64) ([]File, error) {
rows, err := s.q.ListFiles(storeCtx, userID)
if err != nil {
return nil, err
}
files := make([]File, 0, len(rows))
for _, r := range rows {
files = append(files, fileFromRow(r))
}
return files, nil
}
func (s *Store) GetFile(id int64) (File, error) {
f, err := s.q.GetFile(storeCtx, id)
if errors.Is(err, sql.ErrNoRows) {
return File{}, ErrNotFound
}
return fileFromRow(f), err
}
// DeleteFile 仅限属主;返回被删行数(0 = 不存在或非属主)
func (s *Store) DeleteFile(id, userID int64) (int64, error) {
return s.q.DeleteFile(storeCtx, db.DeleteFileParams{ID: id, UserID: userID})
}
func (s *Store) InsertShare(sh *Share) error {
sh.CreatedAt = time.Now().Unix()
id, err := s.q.InsertShare(storeCtx, db.InsertShareParams{
Code: sh.Code, FileID: sh.FileID, UserID: sh.UserID,
FileName: sh.FileName, ExpiresAt: sh.ExpiresAt,
CreatedAt: sh.CreatedAt,
})
if err != nil {
return err
}
sh.Id = id
return nil
}
func (s *Store) ListShares(userID int64) ([]Share, error) {
rows, err := s.q.ListShares(storeCtx, userID)
if err != nil {
return nil, err
}
shares := make([]Share, 0, len(rows))
for _, r := range rows {
shares = append(shares, shareFromRow(r))
}
return shares, nil
}
// ShareFileView 公开下载需要的联表视图
type ShareFileView struct {
Share
Stored string
Size int64
Mime string
}
func (s *Store) GetShareFile(code string) (ShareFileView, error) {
r, err := s.q.GetShareByCode(storeCtx, code)
if errors.Is(err, sql.ErrNoRows) {
return ShareFileView{}, ErrNotFound
}
if err != nil {
return ShareFileView{}, err
}
return ShareFileView{
Share: Share{
Id: r.ID, Code: r.Code, FileID: r.FileID, UserID: r.UserID,
FileName: r.FileName, ExpiresAt: r.ExpiresAt,
Downloads: r.Downloads, CreatedAt: r.CreatedAt,
},
Stored: r.Stored, Size: r.Size, Mime: r.Mime,
}, nil
}
func (s *Store) DeleteShare(id, userID int64) (int64, error) {
return s.q.DeleteShare(storeCtx, db.DeleteShareParams{ID: id, UserID: userID})
}
func (s *Store) DeleteSharesByFile(fileID int64) error {
return s.q.DeleteSharesByFile(storeCtx, fileID)
}
func (s *Store) IncShareDownloads(code string) error {
return s.q.IncShareDownloads(storeCtx, code)
}