CasaOS/service/person.go
link 33acfababd new file manager
Added CasaOS own file manager, now you can browse, upload, download files from the system, even edit code online, preview photos and videos through it. It will appear in the first position of Apps.
Added CPU core count display and memory capacity display.
2022-03-09 16:37:03 +08:00

512 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"log"
"math/big"
"net"
"os"
"path/filepath"
"reflect"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/config"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/types"
"github.com/lucas-clemente/quic-go"
"gorm.io/gorm"
)
type PersonService interface {
GetPersionInfo(token string) (m model.PersionModel, err error)
Handshake(m model.ConnectState)
Download(m model.MessageModel)
GetFileDetail(uuid, path, to string)
SendFileData(m model.MessageModel, blockSize int, length int)
ReplyGetFileDetail(m model.MessageModel)
ReceiveFileData(m model.MessageModel)
ReceiveGetFileDetail(m model.MessageModel)
//------------ database
AddDownloadTask(m model2.PersionDownloadDBModel) //添加下载任务
EditDownloadState(m model2.PersionDownloadDBModel) //只修改状态
EditDownloading(m model2.PersionDownloadDBModel, section model2.PersionFileSectionModel)
SaveDownloadState(m model2.PersionDownloadDBModel)
DelDownload(uuid string)
GetDownloadById(uuid string) model2.PersionDownloadDBModel
}
type personService struct {
db *gorm.DB
}
var IpInfo model.PersionModel
func PushIpInfo(token string) {
m := model.PersionModel{}
m.Ips = GetDeviceAllIP()
m.Token = token
b, _ := json.Marshal(m)
if reflect.DeepEqual(IpInfo, m) {
return
}
head := make(map[string]string)
infoS := httper2.Post(config.ServerInfo.Handshake+"/v1/update", b, "application/json", head)
fmt.Println(infoS)
}
func (p *personService) GetPersionInfo(token string) (m model.PersionModel, err error) {
infoS := httper2.Get(config.ServerInfo.Handshake+"/v1/ips/"+token, nil)
err = json.Unmarshal([]byte(infoS), &m)
return
}
//尝试连接
func (p *personService) Handshake(m model.ConnectState) {
//1先进行udp打通成功
srcAddr := &net.UDPAddr{
IP: net.IPv4zero, Port: 9901} //注意端口必须固定
dstAddr := &net.UDPAddr{
IP: net.ParseIP(config.ServerInfo.Handshake), Port: 9527}
//DialTCP在网络协议net上连接本地地址laddr和远端地址raddr。net必须是"udp"、"udp4"、"udp6"如果laddr不是nil将使用它作为本地地址否则自动选择一个本地地址。
//(conn)UDPConn代表一个UDP网络连接实现了Conn和PacketConn接口
conn, err := net.DialUDP("udp", srcAddr, dstAddr)
if err != nil {
fmt.Println(err)
}
b, _ := json.Marshal(m)
if _, err = conn.Write(b); err != nil {
fmt.Println(err)
}
data := make([]byte, 1024)
//ReadFromUDP从c读取一个UDP数据包将有效负载拷贝到b返回拷贝字节数和数据包来源地址。
//ReadFromUDP方***在超过一个固定的时间点之后超时,并返回一个错误。
n, _, err := conn.ReadFromUDP(data)
if err != nil {
fmt.Printf("error during read: %s", err)
}
conn.Close()
toPersion := model.PersionModel{}
err = json.Unmarshal(data[:n], &toPersion)
if err != nil {
fmt.Println(err)
}
//websocket 连接
// bidirectionHole(srcAddr, &anotherPeer)
//2udp打洞成功向服务器汇报打洞结果
//3转udp打洞
}
func (p *personService) AddDownloadTask(m model2.PersionDownloadDBModel) {
p.db.Create(&m)
}
func (p *personService) EditDownloadState(m model2.PersionDownloadDBModel) {
p.db.Model(&m).Where("uuid = ?", m.UUID).Update("state", m.State)
}
func (p *personService) EditDownloading(m model2.PersionDownloadDBModel, section model2.PersionFileSectionModel) {
b, _ := json.Marshal(section)
m.Section = string(b)
p.db.Model(&m).Where("uuid = ?", m.UUID).Update("section", m.Section)
}
func (p *personService) DelDownload(uuid string) {
var m model2.PersionDownloadDBModel
p.db.Where("uuid = ?", uuid).Delete(&m)
}
func (p *personService) GetDownloadById(uuid string) model2.PersionDownloadDBModel {
var m model2.PersionDownloadDBModel
p.db.Model(m).Where("uuid = ?", uuid).First(&m)
return m
}
func (p *personService) SaveDownloadState(m model2.PersionDownloadDBModel) {
p.db.Save(&m)
}
var ipAddress chan string
type sysConn struct {
conn *net.UDPConn
header string
auth cipher.AEAD
}
func UDPConnect(ips []string) {
quicConfig := &quic.Config{
ConnectionIDLength: 12,
HandshakeIdleTimeout: time.Second * 8,
MaxIdleTimeout: time.Second * 45,
MaxIncomingStreams: 32,
MaxIncomingUniStreams: -1,
KeepAlive: true,
}
fmt.Println(quicConfig)
//PersonUDPMap = make(map[string]*net.UDPAddr)
ipAddress = make(chan string)
srcAddr := &net.UDPAddr{
IP: net.IPv4zero, Port: 9901}
fmt.Println(srcAddr)
//UDPconn, err := net.ListenUDP("udp", srcAddr)
// sysconn := &sysConn{
// conn: UDPconn,
// header: "",
// auth: nil,
// }
// if err != nil {
// fmt.Println(err)
// }
// liste, err := quic.Listen(UDPconn, generateTLSConfig(), nil)
// if err != nil {
// fmt.Println(err)
// }
// ssss, err := liste.Accept(context.Background())
// if err != nil {
// fmt.Println(err)
// }
// st, err := ssss.AcceptStream(context.Background())
// if err != nil {
// fmt.Println(err)
// }
// st.Write([]byte("ssss"))
qlister, err := quic.ListenAddr("0.0.0.0:9901", generateTLSConfig(), nil)
//qlister, err := quic.Listen(UDPconn, nil, nil)
if err != nil {
fmt.Println("quic错误", qlister)
}
//session, e := qlister.Accept()
sess, err := qlister.Accept(context.Background())
sess.SendMessage([]byte("aaaa"))
stream, err := sess.AcceptStream(context.Background())
stream.Write([]byte("bbb"))
//quic.Dial()
if err != nil {
fmt.Println("quic错误", qlister)
}
if err != nil {
fmt.Println("监听错误", err.Error())
}
for _, v := range ips {
dstAddr := &net.UDPAddr{
IP: net.ParseIP(v), Port: 9901}
fmt.Println(v, "开始监听")
//quic.Dial()
go AsyncUDPConnect(dstAddr)
}
for {
data := make([]byte, 1024)
n, add, err := UDPconn.ReadFromUDP(data)
fmt.Println(add)
if err != nil {
log.Printf("error during read:%s\n", err)
} else {
fmt.Println("收到数据:", string(data[:n]))
msg := model.MessageModel{}
err := json.Unmarshal(data[:n], &msg)
if err != nil {
log.Printf("转义错误:%s\n", err)
}
//todo:检查数据库是否为合法请求
if msg.Type == "hi" {
//add ip
//PersonUDPMap[msg.From] = add
} else if msg.Type == "browse" {
//获取目录结构
} else if msg.Type == "file_detail" {
MyService.Person().ReplyGetFileDetail(msg)
} else if msg.Type == "file_detail_reply" {
MyService.Person().ReceiveGetFileDetail(msg)
} else if msg.Type == "file_data_reply" {
MyService.Person().ReceiveFileData(msg)
} else {
fmt.Println("未知事件")
}
}
}
}
// Setup a bare-bones TLS config for the server
func generateTLSConfig() *tls.Config {
key, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
panic(err)
}
template := x509.Certificate{SerialNumber: big.NewInt(1)}
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
if err != nil {
panic(err)
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
panic(err)
}
// return &tls.Config{
// ClientSessionCache: globalSessionCache,
// RootCAs: root,
// InsecureSkipVerify: false,
// NextProtos: nil,
// SessionTicketsDisabled: true,
// }
return &tls.Config{
Certificates: []tls.Certificate{tlsCert},
NextProtos: []string{"quic-echo-example"},
}
}
//首次获取文件信息
func (p *personService) GetFileList(uuid, path, to string) {
msg := model.MessageModel{}
msg.Type = "file_list"
msg.Data = path
msg.To = to
msg.From = config.ServerInfo.Token
msg.UUId = uuid
b, _ := json.Marshal(msg)
fmt.Println(b)
// if ip, ok := PersonUDPMap[msg.To]; ok {
// _, err := UDPconn.WriteToUDP(b, ip)
// if err != nil {
// fmt.Println("写入错误", err)
// }
// }
//接收
}
//首次获取文件信息
func (p *personService) GetFileDetail(uuid, path, to string) {
msg := model.MessageModel{}
msg.Type = "file_detail"
msg.Data = path
msg.To = to
msg.From = config.ServerInfo.Token
msg.UUId = uuid
b, _ := json.Marshal(msg)
fmt.Println(b)
// if ip, ok := PersonUDPMap[msg.To]; ok {
// _, err := UDPconn.WriteToUDP(b, ip)
// if err != nil {
// fmt.Println("写入错误", err)
// }
// }
//创建临时文件夹
file.MkDir("/oasis/download/" + uuid)
}
func (p *personService) Download(m model.MessageModel) {
fDetail, err := os.Stat("/Users/liangjianli/Documents/images")
//发送需要发送的数据摘要
if err != nil {
fmt.Println("未获取到文件信息")
}
summary := model.FileSummaryModel{}
summary.Hash = file.GetHashByPath(fDetail.Name())
summary.Path = m.Data.(string)
summary.BlockSize, summary.Length = file.GetBlockInfo(fDetail.Size())
msg := model.MessageModel{}
msg.Type = "download-reply"
msg.Data = summary
msg.From = config.ServerInfo.Token
msg.UUId = ""
b, _ := json.Marshal(msg)
fmt.Println(b)
// if ip, ok := PersonUDPMap[m.From]; ok {
// _, err := UDPconn.WriteToUDP(b, ip)
// if err != nil {
// fmt.Println("写入错误", err)
// }
// }
}
//receive file data
func (p *personService) ReceiveFileData(m model.MessageModel) {
task := p.GetDownloadById(m.UUId)
//需要重置参数
tempPath := "/oasis/download/" + task.UUID
tempFilePath := tempPath + "/" + task.Name
fmt.Println(tempFilePath)
filePath := "/oasis/download/" + task.Name
bss, _ := json.Marshal(m.Data)
tran := model.TranFileModel{}
err := json.Unmarshal(bss, &tran)
if err != nil {
fmt.Println(err)
}
// if file.ComparisonHash(tran.Hash) {
// f, err := os.Create(tempFilePath + strconv.Itoa(tran.Index))
// if err != nil {
// fmt.Println("创建文件错误", err)
// }
// defer f.Close()
// // _, err = f.Write(tran.Data)
// if err != nil {
// fmt.Println("写入错误", err, tran.Index)
// }
// }
var k int
err = filepath.Walk(tempPath, func(filename string, fi os.FileInfo, err error) error { //遍历目录
if fi.IsDir() { // 忽略目录
return nil
}
k++
return nil
})
if err != nil {
fmt.Println("获取文件错误", err)
}
if task.Length == k {
//err := file.SpliceFiles(tempPath, filePath)
if err == nil {
if h := file.GetHashByPath(filePath); h == task.Hash {
//最终文件比对成功
task.State = types.DOWNLOADFINISH
p.EditDownloadState(task)
//remove temp path
file.RMDir(tempPath)
}
}
}
}
//1:say hi
//2:发送文件名称
//3:发送数据
//========================================接收端============================================================================================
// reply file detail
func (p *personService) ReplyGetFileDetail(m model.MessageModel) {
path := m.Data.(string)
f, err := os.Stat(path)
if err != nil {
fmt.Println(err)
}
summary := model.FileSummaryModel{}
summary.Name = f.Name()
summary.Size = f.Size()
summary.Hash = file.GetHashByPath(path)
summary.Path = path
summary.BlockSize, summary.Length = file.GetBlockInfo(f.Size())
msg := model.MessageModel{}
msg.Type = "file_detail_reply"
msg.Data = summary
msg.From = config.ServerInfo.Token
msg.To = m.From
msg.UUId = m.UUId
b, _ := json.Marshal(msg)
// if ip, ok := PersonUDPMap[m.To]; ok {
// _, err := UDPconn.WriteToUDP(b, ip)
// if err != nil {
// fmt.Println("写入错误", err)
// }
// }
fmt.Println(b)
//开始发送数据
p.SendFileData(m, summary.BlockSize, summary.Length)
}
func (p *personService) SendFileData(m model.MessageModel, blockSize int, length int) {
path := m.Data.(string)
f, err := os.Open(path)
if err != nil {
//读取时移动了文件,需要保存数据到数据库
fmt.Println("读取失败", err)
}
buf := make([]byte, blockSize)
for i := 0; i < length; i++ {
tran := model.TranFileModel{}
_, err := f.Read(buf)
if err == io.EOF {
fmt.Println("读取完毕", err)
}
tran.Hash = file.GetHashByContent(buf)
tran.Index = i + 1
msg := model.MessageModel{}
msg.Type = "file_data_reply"
msg.Data = tran
msg.From = config.ServerInfo.Token
msg.To = m.From
msg.UUId = m.UUId
b, _ := json.Marshal(msg)
// if ip, ok := PersonUDPMap[m.To]; ok {
// _, err := UDPconn.WriteToUDP(b, ip)
// if err != nil {
// fmt.Println("写入错误", err)
// }
// }
fmt.Println(b)
}
}
// 文件摘要返回
func (p *personService) ReceiveGetFileDetail(m model.MessageModel) {
task := p.GetDownloadById("")
bss, _ := json.Marshal(m.Data)
summary := model.FileSummaryModel{}
err := json.Unmarshal(bss, &summary)
if err != nil {
fmt.Println(err)
}
task.Hash = summary.Hash
task.Length = summary.Length
task.Size = summary.Size
p.SaveDownloadState(task)
}
func AsyncUDPConnect(dst *net.UDPAddr) {
for {
time.Sleep(2 * time.Second)
if _, err := UDPconn.WriteToUDP([]byte(dst.IP.String()+" is ok"), dst); err != nil {
log.Println("send msg fail", err)
return
} else {
fmt.Println(dst.IP)
fmt.Println(dst.IP.To4())
}
}
}
func NewPersonService(db *gorm.DB) PersonService {
return &personService{db: db}
}