Dunlin is a lightweight, self-hosted CDN for personal projects.
at master 4.4 kB view raw
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}