feat(admin): 添加APK管理功能
实现APK文件的上传、下载和列表功能 - 新增APKInfo数据结构 - 添加/admin/apk/upload、/admin/apk/download和/admin/apk/list路由 - 实现APK存储服务方法 - 添加相关测试用例
This commit is contained in:
@@ -167,3 +167,82 @@ func (s *LocalStore) ListReleases(runtimeVersion string) ([]model.ReleaseInfo, e
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *LocalStore) SaveAPK(runtimeVersion string, reader io.Reader) (string, error) {
|
||||
apkDir := filepath.Join(s.root, "apk")
|
||||
if err := os.MkdirAll(apkDir, 0o755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
filename := fmt.Sprintf("%s.apk", runtimeVersion)
|
||||
apkPath := filepath.Join(apkDir, filename)
|
||||
tmpPath := apkPath + ".tmp"
|
||||
|
||||
f, err := os.Create(tmpPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, copyErr := io.Copy(f, reader)
|
||||
syncErr := f.Sync()
|
||||
closeErr := f.Close()
|
||||
|
||||
if copyErr != nil {
|
||||
os.Remove(tmpPath)
|
||||
return "", copyErr
|
||||
}
|
||||
if syncErr != nil {
|
||||
os.Remove(tmpPath)
|
||||
return "", syncErr
|
||||
}
|
||||
if closeErr != nil {
|
||||
os.Remove(tmpPath)
|
||||
return "", closeErr
|
||||
}
|
||||
if err := os.Rename(tmpPath, apkPath); err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return "", err
|
||||
}
|
||||
return apkPath, nil
|
||||
}
|
||||
|
||||
func (s *LocalStore) GetAPKPath(runtimeVersion string) (string, error) {
|
||||
filename := fmt.Sprintf("%s.apk", runtimeVersion)
|
||||
apkPath := filepath.Join(s.root, "apk", filename)
|
||||
if _, err := os.Stat(apkPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return apkPath, nil
|
||||
}
|
||||
|
||||
func (s *LocalStore) ListAPKs() ([]model.APKInfo, error) {
|
||||
apkDir := filepath.Join(s.root, "apk")
|
||||
if _, err := os.Stat(apkDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return []model.APKInfo{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
entries, err := os.ReadDir(apkDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]model.APKInfo, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
if e.IsDir() || !strings.HasSuffix(e.Name(), ".apk") {
|
||||
continue
|
||||
}
|
||||
runtimeVersion := strings.TrimSuffix(e.Name(), ".apk")
|
||||
full := filepath.Join(apkDir, e.Name())
|
||||
fi, err := os.Stat(full)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, model.APKInfo{
|
||||
RuntimeVersion: runtimeVersion,
|
||||
Path: full,
|
||||
Size: fi.Size(),
|
||||
CreatedAt: fi.ModTime(),
|
||||
})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].RuntimeVersion > out[j].RuntimeVersion })
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user