CasaOS - 您的个人云操作系统
连接社区,建立自治,降低SaaS成本,最大化个性化助手的潜力。
功能特性
- 个人云操作系统:提供完整的Web界面,将您的硬件设备转变为一个功能强大的个人云服务器。
- 应用商店与Docker管理:轻松浏览、安装和管理Docker应用,支持应用克隆、单版本升级和安装进度展示。
- 统一文件管理:支持本地磁盘、USB设备、Samba共享以及Google Drive、Dropbox、OneDrive等云盘的管理和挂载,实现多存储源的统一访问。
- 磁盘存储管理:支持多磁盘合并存储空间,轻松管理磁盘挂载和分区。
- 网络与远程访问:内置对ZeroTier的支持,方便组建虚拟局域网,实现安全远程访问。同时提供SSH Web终端功能。
- 系统健康监控:实时监控CPU、内存、网络等系统状态,并提供系统日志打包下载功能。
- 消息通知系统:内置WebSocket通知服务,实时推送系统事件、文件操作和应用安装状态。
- 多架构支持:同时支持x86_64(amd64)、ARM64(aarch64)和ARMv7l架构设备,包括树莓派等开发板。
安装指南
系统要求
- 操作系统:Linux (Debian, Ubuntu, Mint等)
- 架构:amd64, arm64, arm-7, riscv64
- 依赖:Go (> v1.17.0), Node.js, Yarn, Docker (可选,用于应用管理)
一键安装脚本
最简单的方式是使用官方安装脚本,在终端中执行以下命令:
curl -fsSL https://get.casaos.io | bash
从源码构建开发环境
-
克隆代码仓库
git clone --recurse-submodules --remote-submodules https://github.com/<your GitHub Username>/CasaOS.git -
安装依赖并构建前端
cd CasaOS/UI yarn install yarn build cd .. -
安装Go依赖并运行
go get go run main.go
安装后的配置
安装完成后,CasaOS服务会自动作为systemd服务启用。您可以通过浏览器访问 http://<您的设备IP>:80 来开始使用。
配置文件位于 /etc/casaos/casaos.conf,您可以在此修改端口、数据库路径等设置。
使用说明
基础使用示例
- 访问仪表盘:打开浏览器,输入安装了CasaOS设备的IP地址。
- 安装应用:进入“应用商店”,浏览并点击您想要安装的应用(如Nextcloud、Jellyfin等),系统将自动通过Docker进行部署。
- 挂载云盘:进入“文件”模块,选择添加存储,根据指引(Google Drive, Dropbox, OneDrive)进行OAuth授权,即可将云盘挂载到本地文件系统。
- 管理共享:通过“Samba共享”功能,将本地目录通过SMB协议共享给局域网内的其他设备。
API 概览
CasaOS提供了完善的RESTful API v1/v2,用于系统、文件、应用等的管理。
获取系统版本
GET /v1/sys/version/current
获取存储列表
GET /v1/storages
Authorization: Bearer <your_jwt_token>
文件上传 (支持分块)
POST /v2/upload/file
Content-Type: multipart/form-data
参数:
- path: 目标路径
- identifier: 文件唯一标识
- filename: 文件名
- chunkNumber: 当前分块序号
- totalChunks: 总分块数
- file: 分块文件数据
WebSocket 通知
连接到 ws://<host>/v1/notify/ws 即可实时接收系统状态和应用安装进度的推送消息。
核心代码
主程序入口 (main.go)
负责初始化配置、数据库、日志,并启动HTTP路由服务。
//go:generate bash -c "mkdir -p codegen && go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 -generate types,server,spec -package codegen api/casaos/openapi.yaml > codegen/casaos_api.go"
package main
import (
"flag"
"fmt"
"github.com/IceWhaleTech/CasaOS/common"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/sqlite"
"github.com/IceWhaleTech/CasaOS/route"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/coreos/go-systemd/daemon"
"go.uber.org/zap"
)
var (
commit = "private build"
date = "private build"
configFlag = flag.String("c", "", "config address")
versionFlag = flag.Bool("v", false, "version")
)
func init() {
flag.Parse()
if *versionFlag {
fmt.Println("v" + common.VERSION)
return
}
config.InitSetup(*configFlag, _confSample)
// ... 初始化日志、数据库等
}
func main() {
// 初始化数据库连接
sqliteDB := sqlite.GetDb(config.AppInfo.DBPath + "/db")
// 初始化服务层
service.MyService = service.NewService(sqliteDB, "")
// 启动后台任务(如网络挂载检查)
go route.InitFunction()
// 启动HTTP服务器
r := route.InitV2Router()
// ... 监听端口并启动服务
}
WebSocket通知服务 (notify.go)
通过WebSocket向所有连接的客户端推送系统事件。
package service
import (
"encoding/json"
"github.com/gorilla/websocket"
"gorm.io/gorm"
)
var WebSocketConns []*websocket.Conn
type notifyServer struct {
db *gorm.DB
}
func (i *notifyServer) SendNotify(name string, message map[string]interface{}) {
// 将消息封装并通过消息总线发布
msg := make(map[string]string)
for k, v := range message {
bt, _ := json.Marshal(v)
msg[k] = string(bt)
}
// 发布事件到内部消息总线
MyService.MessageBus().PublishEventWithResponse(context.Background(), common.SERVICENAME, name, msg)
// 可选:直接通过WebSocket推送
for _, wsConn := range WebSocketConns {
wsConn.WriteJSON(map[string]interface{}{
"name": name,
"message": message,
})
}
}
云盘驱动实现 (Google Drive)
实现了对Google Drive的挂载、文件列表读取和下载链接生成功能。
package google_drive
import (
"context"
"fmt"
"net/http"
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/model"
)
type GoogleDrive struct {
model.StorageA
Addition
AccessToken string
}
// 初始化并进行OAuth2.0令牌刷新
func (d *GoogleDrive) Init(ctx context.Context) error {
if len(d.RefreshToken) == 0 {
d.getRefreshToken()
}
return d.refreshToken()
}
// 获取指定目录下的文件列表
func (d *GoogleDrive) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files, err := d.getFiles(dir.GetID())
if err != nil {
return nil, err
}
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
return fileToObj(src), nil
})
}
// 生成文件下载链接
func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?alt=media", file.GetID())
return &model.Link{
Method: http.MethodGet,
URL: url,
Header: http.Header{"Authorization": []string{"Bearer " + d.AccessToken}},
}, nil
}
文件上传分块处理 (file_upload.go)
支持大文件的分块上传,提高上传稳定性和用户体验。
package service
import (
"io"
"mime/multipart"
"os"
"sync"
)
type FileUploadService struct {
uploadStatus sync.Map
}
// 检查指定分块是否已上传
func (s *FileUploadService) TestChunk(identifier string, chunkNumber int64) error {
fileInfoTemp, ok := s.uploadStatus.Load(identifier)
if !ok {
return fmt.Errorf("file not found")
}
fileInfo := fileInfoTemp.(*FileInfo)
if !fileInfo.uploaded[chunkNumber-1] {
return fmt.Errorf("chunk not found")
}
return nil
}
// 保存上传的分块数据
func (s *FileUploadService) UploadFile(..., bin *multipart.FileHeader) error {
// 打开临时文件,在指定偏移量写入分块
file, err := os.OpenFile(path+"/"+relativePath+".tmp", os.O_WRONLY|os.O_CREATE, 0644)
defer file.Close()
_, err = file.Seek((chunkNumber-1)*chunkSize, io.SeekStart)
src, _ := bin.Open()
defer src.Close()
io.Copy(file, src)
// 记录已上传的分块
fileInfo.uploaded[chunkNumber-1] = true
fileInfo.uploadedChunkNum++
// 所有分块上传完成后,重命名临时文件为正式文件
if fileInfo.uploadedChunkNum == totalChunks {
os.Rename(path+"/"+relativePath+".tmp", path+"/"+relativePath)
s.uploadStatus.Delete(identifier)
}
return nil
}
0HBzHnHDYfukGJRlJ1fmsw1nPo5qypnb74SWxuanHBw=