LingStorage

package module
v0.0.0-...-5c91321 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 27, 2026 License: MIT Imports: 24 Imported by: 0

README

LingStorage

LingStorage Logo

Version License Go Version Node Version

统一存储网关 - 支持多种存储后端的文件管理平台

功能特性快速开始技术栈部署指南API 文档


📖 项目简介

LingStorage 是一个统一的存储网关系统,提供多存储后端的文件管理解决方案。通过统一的 API 接口,您可以轻松地在不同的存储后端之间切换,无需修改业务代码。无论是本地存储、云存储还是对象存储,LingStorage 都能为您提供一致的文件管理体验。

核心特性
  • 🔄 统一存储接口 - 一套 API 支持多种存储后端
  • ☁️ 多存储支持 - 本地存储、七牛云、腾讯云COS、阿里云OSS、MinIO、AWS S3
  • 🎨 现代化界面 - 基于 React + TypeScript 的响应式管理后台
  • 🔐 完善的权限系统 - 支持 RBAC/ABAC 权限控制
  • 📊 实时监控 - API 调用统计、带宽监控、性能分析
  • 🚀 Docker 部署 - 一键部署,开箱即用
  • 📝 完整文档 - 详细的 API 文档和使用指南

✨ 功能特性

存储管理
  • ✅ 存储桶管理(创建、删除、权限设置)
  • ✅ 文件上传、下载、删除、复制、移动
  • ✅ 文件列表查询(支持分页、前缀过滤)
  • ✅ 文件预览和访问 URL 生成
  • ✅ 支持大文件上传(默认限制 100MB)
用户管理
  • ✅ 用户注册、登录、认证
  • ✅ 角色权限管理(RBAC/ABAC)
  • ✅ 用户状态管理(启用/禁用)
  • ✅ 设备管理和信任设备
  • ✅ 双因素认证(2FA)
系统功能
  • ✅ 配置管理(公开/私有配置)
  • ✅ 通知系统
  • ✅ 操作日志记录
  • ✅ API 文档自动生成
  • ✅ 健康检查和监控
存储后端支持
存储类型 状态 说明
本地存储 本地文件系统存储
七牛云 七牛云对象存储
腾讯云 COS 腾讯云对象存储
阿里云 OSS 阿里云对象存储
MinIO 开源对象存储(S3 兼容)
AWS S3 Amazon S3 兼容存储

🚀 快速开始

环境要求
  • Go: 1.21+
  • Node.js: 18+
  • 数据库: MySQL 8.0+ / PostgreSQL 13+ / SQLite
  • Docker: 20.10+ (可选,用于 Docker 部署)
方式一:Docker 部署(推荐)
# 克隆项目
git clone https://github.com/LingByte/LingStorage.git
cd LingStorage

# 使用启动脚本
./docker-start.sh

# 或使用 docker-compose
docker-compose up -d

访问地址:

方式二:本地开发
后端启动
# 安装依赖
go mod download

# 配置环境变量
cp env.example .env
# 编辑 .env 文件

# 初始化数据库
go run cmd/bootstrap/main.go

# 启动服务器
go run cmd/server/main.go
前端启动
cd admin

# 安装依赖
npm install
# 或使用 pnpm
pnpm install

# 启动开发服务器
npm run dev

访问地址:http://localhost:3000

🛠 技术栈

后端技术
  • Go 1.21+ - 高性能后端语言
  • Gin 1.9+ - 轻量级 Web 框架
  • GORM 1.25+ - ORM 数据库操作
  • MySQL/PostgreSQL/SQLite - 关系型数据库支持
  • JWT/OAuth2 - 认证授权
  • Zap - 高性能日志库
前端技术
  • React 18+ - 用户界面库
  • TypeScript 5.0+ - 类型安全的 JavaScript
  • Vite 4.0+ - 快速构建工具
  • Tailwind CSS 3.0+ - 实用优先的 CSS 框架
  • Framer Motion 10+ - 动画库
  • Zustand - 状态管理
  • React Router - 路由管理
存储技术
  • 本地存储 - 本地文件系统
  • 七牛云 - 对象存储服务
  • 腾讯云 COS - 对象存储服务
  • 阿里云 OSS - 对象存储服务
  • MinIO - 开源对象存储
  • AWS S3 - Amazon S3 兼容存储

📦 项目结构

LingStorage/
├── cmd/                 # 应用程序入口
│   ├── server/          # Web服务器
│   └── bootstrap/       # 数据库初始化
├── internal/            # 内部包
│   ├── handlers/        # HTTP处理器
│   ├── models/          # 数据模型
│   └── listeners/       # 事件监听器
├── pkg/                 # 公共包
│   ├── auth/            # 认证授权
│   ├── storage/         # 存储管理
│   ├── config/          # 配置管理
│   ├── logger/          # 日志系统
│   ├── middleware/      # 中间件
│   └── utils/           # 工具函数
├── admin/               # 前端管理后台
│   ├── src/
│   │   ├── components/  # React组件
│   │   ├── pages/       # 页面组件
│   │   └── services/    # API服务
└── templates/           # 邮件模板

🔧 配置说明

环境变量

主要配置项(完整配置请参考 env.example):

# 应用环境
APP_ENV=production

# 服务器配置
ADDR=:7075
SERVER_URL=http://localhost:7075

# 数据库配置
DB_DRIVER=sqlite
DSN=./ling.db

# 存储配置
STORAGE_KIND=local
UPLOAD_DIR=./uploads

# JWT 配置
JWT_SECRET_KEY=your-secret-key
JWT_ACCESS_TOKEN_TTL=3600
存储后端配置
本地存储
STORAGE_KIND=local
UPLOAD_DIR=./uploads
七牛云
STORAGE_KIND=qiniu
QINIU_ACCESS_KEY=your-access-key
QINIU_SECRET_KEY=your-secret-key
QINIU_BUCKET=your-bucket
QINIU_DOMAIN=your-domain
腾讯云 COS
STORAGE_KIND=cos
SECRET_ID=your-secret-id
SECRET_KEY=your-secret-key
REGION=ap-guangzhou
BUCKET_NAME=your-bucket

更多配置请参考 部署指南

📚 API 文档

公共上传接口

无需认证的文件上传接口,支持多种编程语言对接:

POST /api/public/upload
Content-Type: multipart/form-data

参数:
- file: 文件(必需)
- bucket: 存储桶名称(可选,默认:default)
- key: 文件存储路径(可选)
- allowedTypes: 允许的文件类型(可选,查询参数)

响应示例:

{
  "code": 200,
  "message": "File uploaded successfully",
  "data": {
    "key": "uploads/1703123456_example.jpg",
    "bucket": "default",
    "filename": "example.jpg",
    "size": 1024000,
    "url": "http://localhost:7075/uploads/1703123456_example.jpg"
  }
}
对接示例

项目提供了多种语言的对接示例:

  • JavaScript/TypeScript - Fetch API、Axios
  • React - 完整组件示例
  • Java - Hutool 工具包
  • Go - 标准库、go-resty
  • gRPC - gRPC-Gateway 对接方案

详细示例请查看:文件上传接口对接文档

完整 API 文档

启动服务后访问:http://localhost:7075/api/docs

🐳 Docker 部署

快速启动
# 使用启动脚本
./docker-start.sh

# 或使用 docker-compose
docker-compose up -d
生产环境部署
# 创建 .env 文件
cp env.example .env
# 编辑 .env 配置

# 启动生产环境
docker-compose -f docker-compose.prod.yml up -d

详细部署文档请参考:Docker 部署指南

📖 使用文档

🤝 贡献指南

我们欢迎所有形式的贡献!

  1. Fork 本项目
  2. 创建特性分支 (git checkout -b feature/AmazingFeature)
  3. 提交更改 (git commit -m 'Add some AmazingFeature')
  4. 推送到分支 (git push origin feature/AmazingFeature)
  5. 开启 Pull Request
代码规范
  • Go: 使用 gofmt 格式化代码,遵循 Go 官方代码规范
  • TypeScript/React: 使用 ESLint 和 Prettier,遵循 React 最佳实践

📄 许可证

本项目采用 MIT 许可证。

📞 联系方式

🙏 致谢

感谢所有为这个项目做出贡献的开发者!


如果这个项目对您有帮助,请给一个 ⭐ Star!

Made with ❤️ by LingByte Team

Documentation

Index

Constants

View Source
const (
	FilterOpIsNot          = "is not"
	FilterOpEqual          = "="
	FilterOpNotEqual       = "<>"
	FilterOpIn             = "in"
	FilterOpNotIn          = "not_in"
	FilterOpGreater        = ">"
	FilterOpGreaterOrEqual = ">="
	FilterOpLess           = "<"
	FilterOpLessOrEqual    = "<="
	FilterOpLike           = "like"
	FilterOpBetween        = "between"
)
View Source
const (
	OrderOpDesc = "desc"
	OrderOpAsc  = "asc"
)
View Source
const (
	GET    = 1 << 1
	CREATE = 1 << 2
	EDIT   = 1 << 3
	DELETE = 1 << 4
	QUERY  = 1 << 5
)
View Source
const (
	DefaultQueryLimit = 102400 // 100k
)

Variables

View Source
var EmbedAdminAssets embed.FS
View Source
var EmbedStaticAssets embed.FS
View Source
var GroupInvitationHTML string
View Source
var PasswordResetHTML string
View Source
var SystemNotFoundHTML string
View Source
var VerificationEmailHTML string
View Source
var VerificationHTML string
View Source
var WelcomeHTML string

Functions

func AbortWithJSONError

func AbortWithJSONError(c *gin.Context, code int, err error)

func GetDbConnection

func GetDbConnection(c *gin.Context, objFn GetDB, isCreate bool) (tx *gorm.DB)

func GetRenderPageContext

func GetRenderPageContext(c *gin.Context) map[string]any

func HintAssetsRoot

func HintAssetsRoot(dirName string) string

func NewTemplateFuncs

func NewTemplateFuncs() template.FuncMap

func RegisterObject

func RegisterObject(r *gin.RouterGroup, obj *WebObject) error

func RegisterObjects

func RegisterObjects(r *gin.RouterGroup, objs []WebObject)

func RenderNotFoundPage

func RenderNotFoundPage(c *gin.Context, path, method string)

RenderNotFoundPage 渲染 404 页面

func SanitizeSensitiveValues

func SanitizeSensitiveValues(prefix string, data any) map[string]any

func WithAdminAssets

func WithAdminAssets(r *gin.Engine, adminPrefix, adminRootDir string) gin.HandlerFunc

func WithStaticAssets

func WithStaticAssets(r *gin.Engine, staticPrefix, staticRootDir string) gin.HandlerFunc

Types

type BeforeCreateFunc

type BeforeCreateFunc func(db *gorm.DB, ctx *gin.Context, vptr any) error

type BeforeDeleteFunc

type BeforeDeleteFunc func(db *gorm.DB, ctx *gin.Context, vptr any) error

type BeforeQueryRenderFunc

type BeforeQueryRenderFunc func(db *gorm.DB, ctx *gin.Context, r *QueryResult) (any, error)

type BeforeRenderFunc

type BeforeRenderFunc func(db *gorm.DB, ctx *gin.Context, vptr any) (any, error)

type BeforeUpdateFunc

type BeforeUpdateFunc func(db *gorm.DB, ctx *gin.Context, vptr any, vals map[string]any) error

type CombineEmbedFS

type CombineEmbedFS struct {
	// contains filtered or unexported fields
}

func NewCombineEmbedFS

func NewCombineEmbedFS(assertDir string, es ...EmbedFS) *CombineEmbedFS

func (*CombineEmbedFS) Open

func (c *CombineEmbedFS) Open(name string) (fs.File, error)

func (*CombineEmbedFS) ReadDir

func (c *CombineEmbedFS) ReadDir(name string) ([]fs.DirEntry, error)

type CombineTemplates

type CombineTemplates struct {
	CombineFS *CombineEmbedFS
	Template  *template.Template
	Delims    render.Delims
	FuncMap   template.FuncMap
}

func NewCombineTemplates

func NewCombineTemplates(combineFS *CombineEmbedFS) *CombineTemplates

func (*CombineTemplates) Instance

func (c *CombineTemplates) Instance(name string, ctx any) render.Render

Instance gin.render.Render

func (*CombineTemplates) RenderError

func (c *CombineTemplates) RenderError(name, source string, data any, err error) render.Render

type DebugTempalte

type DebugTempalte struct {
	Template *template.Template
	Name     string
	Data     any
	// contains filtered or unexported fields
}

HTML contains template reference and its name with given interface object.

func (DebugTempalte) Render

func (r DebugTempalte) Render(w http.ResponseWriter) error

Render (HTML) executes template and writes its result with custom ContentType for response.

func (DebugTempalte) WriteContentType

func (r DebugTempalte) WriteContentType(w http.ResponseWriter)

WriteContentType (HTML) writes HTML ContentType.

type EmbedFS

type EmbedFS struct {
	EmbedRoot string
	Embedfs   embed.FS
}

type EmbedFile

type EmbedFile struct {
	// contains filtered or unexported fields
}

func (EmbedFile) Close

func (ef EmbedFile) Close() error

Close implements http.File

func (EmbedFile) Read

func (ef EmbedFile) Read(p []byte) (n int, err error)

Read implements http.File

func (EmbedFile) Readdir

func (ef EmbedFile) Readdir(count int) ([]fs.FileInfo, error)

Readdir implements http.File

func (EmbedFile) Seek

func (ef EmbedFile) Seek(offset int64, whence int) (int64, error)

Seek implements http.File

func (EmbedFile) Stat

func (ef EmbedFile) Stat() (fs.FileInfo, error)

Stat implements http.File

type ErrorWithCode

type ErrorWithCode interface {
	StatusCode() int
}

type Filter

type Filter struct {
	Name  string `json:"name"`
	Op    string `json:"op"`
	Value any    `json:"value"`
	// contains filtered or unexported fields
}

func (*Filter) GetQuery

func (f *Filter) GetQuery() string

GetQuery return the combined filter SQL statement. such as "age >= ?", "name IN ?".

type GetDB

type GetDB func(c *gin.Context, isCreate bool) *gorm.DB // designed for group

type Order

type Order struct {
	Name string `json:"name"`
	Op   string `json:"op"`
}

func (*Order) GetQuery

func (f *Order) GetQuery() string

GetQuery return the combined order SQL statement. such as "id DESC".

type PrepareQuery

type PrepareQuery func(db *gorm.DB, c *gin.Context) (*gorm.DB, *QueryForm, error)

type QueryForm

type QueryForm struct {
	Pos         int      `json:"pos"`
	Limit       int      `json:"limit"`
	Keyword     string   `json:"keyword,omitempty"`
	Filters     []Filter `json:"filters,omitempty"`
	Orders      []Order  `json:"orders,omitempty"`
	ForeignMode bool     `json:"foreign"` // for foreign key
	ViewFields  []string `json:"-"`       // for view
	// contains filtered or unexported fields
}

func DefaultPrepareQuery

func DefaultPrepareQuery(db *gorm.DB, c *gin.Context) (*gorm.DB, *QueryForm, error)

DefaultPrepareQuery return default QueryForm.

type QueryResult

type QueryResult struct {
	TotalCount int    `json:"total,omitempty"`
	Pos        int    `json:"pos,omitempty"`
	Limit      int    `json:"limit,omitempty"`
	Keyword    string `json:"keyword,omitempty"`
	Items      []any  `json:"items"`
}

type QueryView

type QueryView struct {
	Path    string `json:"path"`
	Method  string `json:"method"`
	Desc    string `json:"desc"`
	Prepare PrepareQuery
}

type WebObject

type WebObject struct {
	Model             any
	Group             string
	Name              string
	Desc              string
	AuthRequired      bool
	Editables         []string
	Filterables       []string
	Orderables        []string
	Searchables       []string
	GetDB             GetDB
	PrepareQuery      PrepareQuery
	BeforeCreate      BeforeCreateFunc
	BeforeUpdate      BeforeUpdateFunc
	BeforeDelete      BeforeDeleteFunc
	BeforeRender      BeforeRenderFunc
	BeforeQueryRender BeforeQueryRenderFunc

	Views        []QueryView
	AllowMethods int
	// contains filtered or unexported fields
}

func (*WebObject) Build

func (obj *WebObject) Build() error

Build fill the properties of obj.

func (*WebObject) BuildPrimaryPath

func (obj *WebObject) BuildPrimaryPath(prefix string) string

func (*WebObject) RegisterObject

func (obj *WebObject) RegisterObject(r *gin.RouterGroup) error

type WebObjectPrimaryField

type WebObjectPrimaryField struct {
	IsPrimary bool
	Name      string
	Kind      reflect.Kind
	JSONName  string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL