1package services
2
3import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "strings"
8
9 "github.com/dinkelspiel/cdn/dao"
10 "github.com/dinkelspiel/cdn/db"
11 "github.com/dinkelspiel/cdn/models"
12 "github.com/dinkelspiel/cdn/storage"
13 "github.com/gin-gonic/gin"
14 "golang.org/x/sys/unix"
15)
16
17func GetTeamProjectFiles(teamProject models.TeamProject, relativePath string) ([]storage.FSItem, error) {
18 config, _ := LoadConfig()
19 baseDir := fmt.Sprintf("%s/public/%d/%d", config.StorageUrl, *teamProject.Team.Id, *teamProject.Id)
20
21 fullPath := filepath.Join(baseDir, relativePath)
22 fullPath = filepath.Clean(fullPath)
23
24 if !strings.HasPrefix(fullPath, baseDir) {
25 return nil, fmt.Errorf("access to path '%s' is denied", fullPath)
26 }
27
28 return storage.ListFiles(fullPath)
29}
30
31func GetFilePathToFileInTeamProject(teamProject models.TeamProject, relativePath string) (string, error) {
32 config, _ := LoadConfig()
33 baseDir := fmt.Sprintf("%s/public/%d/%d", config.StorageUrl, *teamProject.Team.Id, *teamProject.Id)
34
35 fullPath := filepath.Join(baseDir, relativePath)
36 fullPath = filepath.Clean(fullPath)
37
38 if !strings.HasPrefix(fullPath, baseDir) {
39 return "", fmt.Errorf("access to path '%s' is denied", fullPath)
40 }
41
42 return fullPath, nil
43}
44
45func GetTeamProjectFile(teamProject models.TeamProject, relativePath string) ([]byte, error) {
46 path, err := GetFilePathToFileInTeamProject(teamProject, relativePath)
47 if err != nil {
48 return nil, err
49 }
50
51 return storage.ReadFile(path)
52}
53
54func EnsureFoldersExists() error {
55 config, _ := LoadConfig()
56 err := os.MkdirAll(config.StorageUrl, os.ModePerm)
57 if err != nil {
58 return err
59 }
60
61 publicDir := fmt.Sprintf("%s/public", config.StorageUrl)
62 err = os.MkdirAll(publicDir, os.ModePerm)
63 if err != nil {
64 return err
65 }
66
67 return nil
68}
69
70func CreateTeamFolder(team models.Team) error {
71 config, _ := LoadConfig()
72
73 teamDir := fmt.Sprintf("%s/public/%d", config.StorageUrl, *team.Id)
74 return os.MkdirAll(teamDir, os.ModePerm)
75}
76
77func CreateTeamProjectFolder(teamProject models.TeamProject, relativePath string) (string, error) {
78 fullPath, err := GetFilePathToFileInTeamProject(teamProject, relativePath)
79 if err != nil {
80 return "", err
81 }
82
83 return fullPath, os.MkdirAll(fullPath, os.ModePerm)
84}
85
86type DiskStats struct {
87 Total uint64
88 Free uint64
89 HostUsed uint64
90 DunlinFilesUsed uint64
91 DunlinCacheUsed uint64
92}
93
94func GetHostDiskStats() DiskStats {
95 config, _ := LoadConfig()
96
97 var stat unix.Statfs_t
98 err := unix.Statfs(config.HostRoot, &stat)
99 if err != nil {
100 panic(err)
101 }
102
103 total := stat.Blocks * uint64(stat.Bsize)
104 free := stat.Bfree * uint64(stat.Bsize)
105 hostUsed := total - free
106
107 dunlinFilesUsed := getDirectorySize(filepath.Join(config.StorageUrl, "public"))
108 dunlinCacheUsed := getDirectorySize(filepath.Join(config.StorageUrl, ".cache"))
109
110 return DiskStats{
111 Total: total,
112 Free: free,
113 HostUsed: hostUsed - dunlinFilesUsed - dunlinCacheUsed,
114 DunlinFilesUsed: dunlinFilesUsed,
115 DunlinCacheUsed: dunlinCacheUsed,
116 }
117}
118
119func getDirectorySize(path string) uint64 {
120 var size uint64
121 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
122 if err == nil && !info.IsDir() {
123 size += uint64(info.Size())
124 }
125 return nil
126 })
127 if err != nil {
128 panic(err)
129 }
130 return size
131}
132
133type TeamProjectSize struct {
134 TeamProject models.TeamProject
135 Size uint64
136}
137
138func GetSizeOfTeamProjectsOnDisk(db *db.DB) (*[]TeamProjectSize, error) {
139 teamProjects, err := dao.GetTeamProjects(db)
140 if err != nil {
141 return nil, err
142 }
143
144 var teamProjectsSize []TeamProjectSize
145
146 for _, teamProject := range *teamProjects {
147 path, err := GetFilePathToFileInTeamProject(teamProject, "/")
148 if err != nil {
149 return nil, err
150 }
151
152 size := getDirectorySize(path)
153 teamProjectsSize = append(teamProjectsSize, TeamProjectSize{
154 Size: size,
155 TeamProject: teamProject,
156 })
157
158 }
159
160 return &teamProjectsSize, nil
161}
162
163func SerializeTeamProjectSize(teamProjectSize TeamProjectSize) gin.H {
164 return gin.H{
165 "teamProject": models.SerializeTeamProject(teamProjectSize.TeamProject),
166 "size": teamProjectSize.Size,
167 }
168}
169
170func SerializeDiskStats(diskStats DiskStats) gin.H {
171 return gin.H{
172 "total": diskStats.Total,
173 "free": diskStats.Free,
174 "hostUsed": diskStats.HostUsed,
175 "dunlinFilesUsed": diskStats.DunlinFilesUsed,
176 "dunlinCacheUsed": diskStats.DunlinCacheUsed,
177 }
178}