forgejo/routers/api/packages/rpm/rpm.go
Exploding Dragon ba4d0b8ffb
Support for grouping RPMs using paths ()
The current rpm repository places all packages in the same repository,
and different systems (el7,f34) may hit packages that do not belong to
this distribution (  ) , which now supports grouping of rpm.

![图片](https://github.com/go-gitea/gitea/assets/33776693/d1e1d99f-7799-4b2b-a19b-cb2a5c692914)

Fixes  .
Fixes  .

Refactor: [](https://github.com/go-gitea/gitea/pull/25866)
2024-01-12 03:16:05 +00:00

295 lines
7.8 KiB
Go

// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package rpm
import (
stdctx "context"
"errors"
"fmt"
"io"
"net/http"
"strings"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
packages_module "code.gitea.io/gitea/modules/packages"
rpm_module "code.gitea.io/gitea/modules/packages/rpm"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
notify_service "code.gitea.io/gitea/services/notify"
packages_service "code.gitea.io/gitea/services/packages"
rpm_service "code.gitea.io/gitea/services/packages/rpm"
)
func apiError(ctx *context.Context, status int, obj any) {
helper.LogAndProcessError(ctx, status, obj, func(message string) {
ctx.PlainText(status, message)
})
}
// https://dnf.readthedocs.io/en/latest/conf_ref.html
func GetRepositoryConfig(ctx *context.Context) {
group := ctx.Params("group")
if group != "" {
group = fmt.Sprintf("/%s", group)
}
url := fmt.Sprintf("%sapi/packages/%s/rpm", setting.AppURL, ctx.Package.Owner.Name)
ctx.PlainText(http.StatusOK, `[gitea-`+ctx.Package.Owner.LowerName+strings.ReplaceAll(group, "/", "-")+`]
name=`+ctx.Package.Owner.Name+` - `+setting.AppName+strings.ReplaceAll(group, "/", " - ")+`
baseurl=`+url+group+`/
enabled=1
gpgcheck=1
gpgkey=`+url+`/repository.key`)
}
// Gets or creates the PGP public key used to sign repository metadata files
func GetRepositoryKey(ctx *context.Context) {
_, pub, err := rpm_service.GetOrCreateKeyPair(ctx, ctx.Package.Owner.ID)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
ctx.ServeContent(strings.NewReader(pub), &context.ServeHeaderOptions{
ContentType: "application/pgp-keys",
Filename: "repository.key",
})
}
func CheckRepositoryFileExistence(ctx *context.Context) {
pv, err := rpm_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), ctx.Params("group"))
if err != nil {
if errors.Is(err, util.ErrNotExist) {
ctx.Status(http.StatusNotFound)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
ctx.SetServeHeaders(&context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
ctx.Status(http.StatusOK)
}
// Gets a pre-generated repository metadata file
func GetRepositoryFile(ctx *context.Context) {
pv, err := rpm_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
Filename: ctx.Params("filename"),
CompositeKey: ctx.Params("group"),
},
)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
helper.ServePackageFile(ctx, s, u, pf)
}
func UploadPackageFile(ctx *context.Context) {
upload, close, err := ctx.UploadStream()
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
if close {
defer upload.Close()
}
buf, err := packages_module.CreateHashedBufferFromReader(upload)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer buf.Close()
pck, err := rpm_module.ParsePackage(buf)
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
apiError(ctx, http.StatusBadRequest, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
if _, err := buf.Seek(0, io.SeekStart); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
fileMetadataRaw, err := json.Marshal(pck.FileMetadata)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
group := ctx.Params("group")
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
ctx,
&packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{
Owner: ctx.Package.Owner,
PackageType: packages_model.TypeRpm,
Name: pck.Name,
Version: strings.Trim(fmt.Sprintf("%s/%s", group, pck.Version), "/"),
},
Creator: ctx.Doer,
Metadata: pck.VersionMetadata,
},
&packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
Filename: fmt.Sprintf("%s-%s.%s.rpm", pck.Name, pck.Version, pck.FileMetadata.Architecture),
CompositeKey: group,
},
Creator: ctx.Doer,
Data: buf,
IsLead: true,
Properties: map[string]string{
rpm_module.PropertyMetadata: string(fileMetadataRaw),
},
},
)
if err != nil {
switch err {
case packages_model.ErrDuplicatePackageVersion, packages_model.ErrDuplicatePackageFile:
apiError(ctx, http.StatusConflict, err)
case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
apiError(ctx, http.StatusForbidden, err)
default:
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
if err := rpm_service.BuildRepositoryFiles(ctx, ctx.Package.Owner.ID, group); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
ctx.Status(http.StatusCreated)
}
func DownloadPackageFile(ctx *context.Context) {
group := ctx.Params("group")
name := ctx.Params("name")
version := ctx.Params("version")
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
PackageType: packages_model.TypeRpm,
Name: name,
Version: strings.Trim(fmt.Sprintf("%s/%s", group, version), "/"),
},
&packages_service.PackageFileInfo{
Filename: fmt.Sprintf("%s-%s.%s.rpm", name, version, ctx.Params("architecture")),
CompositeKey: group,
},
)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
helper.ServePackageFile(ctx, s, u, pf)
}
func DeletePackageFile(webctx *context.Context) {
group := webctx.Params("group")
name := webctx.Params("name")
version := webctx.Params("version")
architecture := webctx.Params("architecture")
var pd *packages_model.PackageDescriptor
err := db.WithTx(webctx, func(ctx stdctx.Context) error {
pv, err := packages_model.GetVersionByNameAndVersion(ctx,
webctx.Package.Owner.ID,
packages_model.TypeRpm,
name,
strings.Trim(fmt.Sprintf("%s/%s", group, version), "/"),
)
if err != nil {
return err
}
pf, err := packages_model.GetFileForVersionByName(
ctx,
pv.ID,
fmt.Sprintf("%s-%s.%s.rpm", name, version, architecture),
group,
)
if err != nil {
return err
}
if err := packages_service.DeletePackageFile(ctx, pf); err != nil {
return err
}
has, err := packages_model.HasVersionFileReferences(ctx, pv.ID)
if err != nil {
return err
}
if !has {
pd, err = packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
return err
}
if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
return err
}
}
return nil
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(webctx, http.StatusNotFound, err)
} else {
apiError(webctx, http.StatusInternalServerError, err)
}
return
}
if pd != nil {
notify_service.PackageDelete(webctx, webctx.Doer, pd)
}
if err := rpm_service.BuildRepositoryFiles(webctx, webctx.Package.Owner.ID, group); err != nil {
apiError(webctx, http.StatusInternalServerError, err)
return
}
webctx.Status(http.StatusNoContent)
}