diff --git a/cmd/lure-api-server/Dockerfile b/cmd/lure-api-server/Dockerfile deleted file mode 100644 index bd2bdf3..0000000 --- a/cmd/lure-api-server/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM alpine:latest -COPY lure-api-server /usr/bin/lure-api-server -ENTRYPOINT lure-api-server diff --git a/cmd/lure-api-server/README.md b/cmd/lure-api-server/README.md deleted file mode 100644 index 2b3fa16..0000000 --- a/cmd/lure-api-server/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# lure-api-server - -`lure-api-server` is the backend API server for lure-web, the web interface for LURE. \ No newline at end of file diff --git a/cmd/lure-api-server/api.go b/cmd/lure-api-server/api.go deleted file mode 100644 index c00671f..0000000 --- a/cmd/lure-api-server/api.go +++ /dev/null @@ -1,166 +0,0 @@ -/* - * LURE - Linux User REpository - * Copyright (C) 2023 Elara Musayelyan - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package main - -import ( - "context" - "io" - - "github.com/twitchtv/twirp" - "go.elara.ws/lure/cmd/lure-api-server/internal/api" - "go.elara.ws/lure/internal/log" - "go.elara.ws/lure/pkg/search" - "golang.org/x/text/language" -) - -type lureWebAPI struct{} - -func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.SearchResponse, error) { - pkgs, err := search.Search(search.Options{ - Filter: search.Filter(req.FilterType), - SortBy: search.SortBy(req.SortBy), - Limit: req.Limit, - Query: req.Query, - }) - return &api.SearchResponse{Packages: searchPkgsToAPI(ctx, pkgs)}, err -} - -func (l lureWebAPI) GetPkg(ctx context.Context, req *api.GetPackageRequest) (*api.Package, error) { - pkg, err := search.GetPkg(req.Repository, req.Name) - if err != nil { - return nil, err - } - return searchPkgToAPI(ctx, pkg), nil -} - -func (l lureWebAPI) GetBuildScript(ctx context.Context, req *api.GetBuildScriptRequest) (*api.GetBuildScriptResponse, error) { - r, err := search.GetScript(req.Repository, req.Name) - if err == search.ErrScriptNotFound { - return nil, twirp.NewError(twirp.NotFound, err.Error()) - } else if err == search.ErrInvalidArgument { - return nil, twirp.NewError(twirp.InvalidArgument, err.Error()) - } else if err != nil { - return nil, err - } - - data, err := io.ReadAll(r) - if err != nil { - return nil, err - } - - return &api.GetBuildScriptResponse{Script: string(data)}, nil -} - -func searchPkgsToAPI(ctx context.Context, pkgs []search.Package) []*api.Package { - out := make([]*api.Package, len(pkgs)) - for i, pkg := range pkgs { - out[i] = searchPkgToAPI(ctx, pkg) - } - return out -} - -func searchPkgToAPI(ctx context.Context, pkg search.Package) *api.Package { - return &api.Package{ - Name: pkg.Name, - Repository: pkg.Repository, - Version: pkg.Version, - Release: int64(pkg.Release), - Epoch: ptr(int64(pkg.Epoch)), - Description: performTranslation(ctx, pkg.Description), - Homepage: performTranslation(ctx, pkg.Homepage), - Maintainer: performTranslation(ctx, pkg.Maintainer), - Architectures: pkg.Architectures, - Licenses: pkg.Licenses, - Provides: pkg.Provides, - Conflicts: pkg.Conflicts, - Replaces: pkg.Replaces, - Depends: mapToAPI(pkg.Depends), - BuildDepends: mapToAPI(pkg.BuildDepends), - } -} - -func ptr[T any](v T) *T { - return &v -} - -func mapToAPI(m map[string][]string) map[string]*api.StringList { - out := make(map[string]*api.StringList, len(m)) - for override, list := range m { - out[override] = &api.StringList{Entries: list} - } - return out -} - -func performTranslation(ctx context.Context, v map[string]string) *string { - alVal := ctx.Value(acceptLanguageKey{}) - langVal := ctx.Value(langParameterKey{}) - - if alVal == nil && langVal == nil { - val, ok := v[""] - if !ok { - return ptr("") - } - return &val - } - - al, _ := alVal.(string) - lang, _ := langVal.(string) - - tags, _, err := language.ParseAcceptLanguage(al) - if err != nil { - log.Warn("Error parsing Accept-Language header").Err(err).Send() - } - - var bases []string - if lang != "" { - langTag, err := language.Parse(lang) - if err != nil { - log.Warn("Error parsing lang parameter").Err(err).Send() - bases = getLangBases(tags) - } else { - bases = getLangBases(append([]language.Tag{langTag}, tags...)) - } - } else { - bases = getLangBases(tags) - } - - if len(bases) == 1 { - bases = []string{"en", ""} - } - - for _, name := range bases { - val, ok := v[name] - if !ok { - continue - } - return &val - } - - return ptr("") -} - -func getLangBases(langs []language.Tag) []string { - out := make([]string, len(langs)+1) - for i, lang := range langs { - base, _ := lang.Base() - out[i] = base.String() - } - out[len(out)-1] = "" - return out -} diff --git a/cmd/lure-api-server/badge-logo.txt b/cmd/lure-api-server/badge-logo.txt deleted file mode 100644 index 37767cc..0000000 --- a/cmd/lure-api-server/badge-logo.txt +++ /dev/null @@ -1 +0,0 @@ -data:image/png;base64,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 \ No newline at end of file diff --git a/cmd/lure-api-server/badge.go b/cmd/lure-api-server/badge.go deleted file mode 100644 index b926cb7..0000000 --- a/cmd/lure-api-server/badge.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - _ "embed" - "net/http" - "net/url" - "strconv" - "strings" - - "github.com/go-chi/chi/v5" - "go.elara.ws/lure/pkg/search" -) - -//go:embed badge-logo.txt -var logoData string - -var _ http.HandlerFunc - -func handleBadge() http.HandlerFunc { - return func(res http.ResponseWriter, req *http.Request) { - repo := chi.URLParam(req, "repo") - name := chi.URLParam(req, "pkg") - - pkg, err := search.GetPkg(repo, name) - if err != nil { - http.Error(res, err.Error(), http.StatusInternalServerError) - return - } - - http.Redirect(res, req, genBadgeURL(pkg.Name, genVersion(pkg)), http.StatusFound) - } -} - -func genVersion(pkg search.Package) string { - sb := strings.Builder{} - if pkg.Epoch != 0 { - sb.WriteString(strconv.Itoa(int(pkg.Epoch))) - sb.WriteByte(':') - } - - sb.WriteString(pkg.Version) - - if pkg.Release != 0 { - sb.WriteByte('-') - sb.WriteString(strconv.Itoa(pkg.Release)) - } - return sb.String() -} - -func genBadgeURL(pkgName, pkgVersion string) string { - v := url.Values{} - v.Set("label", pkgName) - v.Set("message", pkgVersion) - v.Set("logo", logoData) - v.Set("color", "blue") - u := &url.URL{Scheme: "https", Host: "img.shields.io", Path: "/static/v1", RawQuery: v.Encode()} - return u.String() -} diff --git a/cmd/lure-api-server/docker.sh b/cmd/lure-api-server/docker.sh deleted file mode 100755 index c79de3e..0000000 --- a/cmd/lure-api-server/docker.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -docker buildx build --platform linux/amd64 --tag elara6331/lure-api-server:amd64 --no-cache . - -CGO_ENABLED=0 GOARCH=arm64 GOOS=linux go build -docker buildx build --platform linux/arm64/v8 --tag elara6331/lure-api-server:arm64 --no-cache . - -docker login -docker push elara6331/lure-api-server -a - -docker manifest rm elara6331/lure-api-server:latest -docker manifest create elara6331/lure-api-server:latest --amend elara6331/lure-api-server:arm64 --amend elara6331/lure-api-server:amd64 -docker manifest push elara6331/lure-api-server:latest diff --git a/cmd/lure-api-server/internal/api/gen.go b/cmd/lure-api-server/internal/api/gen.go deleted file mode 100644 index 00575f5..0000000 --- a/cmd/lure-api-server/internal/api/gen.go +++ /dev/null @@ -1,22 +0,0 @@ -/* - * LURE - Linux User REpository - * Copyright (C) 2023 Elara Musayelyan - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package api - -//go:generate protoc --twirp_out=. lure.proto -//go:generate protoc --go_out=. lure.proto diff --git a/cmd/lure-api-server/internal/api/lure.pb.go b/cmd/lure-api-server/internal/api/lure.pb.go deleted file mode 100644 index c2e43a9..0000000 --- a/cmd/lure-api-server/internal/api/lure.pb.go +++ /dev/null @@ -1,888 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.31.0 -// protoc v4.24.2 -// source: lure.proto - -package api - -import ( - reflect "reflect" - sync "sync" - - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// SORT_BY represents possible things to sort packages by -type SORT_BY int32 - -const ( - SORT_BY_UNSORTED SORT_BY = 0 - SORT_BY_NAME SORT_BY = 1 - SORT_BY_REPOSITORY SORT_BY = 2 - SORT_BY_VERSION SORT_BY = 3 -) - -// Enum value maps for SORT_BY. -var ( - SORT_BY_name = map[int32]string{ - 0: "UNSORTED", - 1: "NAME", - 2: "REPOSITORY", - 3: "VERSION", - } - SORT_BY_value = map[string]int32{ - "UNSORTED": 0, - "NAME": 1, - "REPOSITORY": 2, - "VERSION": 3, - } -) - -func (x SORT_BY) Enum() *SORT_BY { - p := new(SORT_BY) - *p = x - return p -} - -func (x SORT_BY) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SORT_BY) Descriptor() protoreflect.EnumDescriptor { - return file_lure_proto_enumTypes[0].Descriptor() -} - -func (SORT_BY) Type() protoreflect.EnumType { - return &file_lure_proto_enumTypes[0] -} - -func (x SORT_BY) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SORT_BY.Descriptor instead. -func (SORT_BY) EnumDescriptor() ([]byte, []int) { - return file_lure_proto_rawDescGZIP(), []int{0} -} - -// FILTER_TYPE represents possible filters for packages -type FILTER_TYPE int32 - -const ( - FILTER_TYPE_NO_FILTER FILTER_TYPE = 0 - FILTER_TYPE_IN_REPOSITORY FILTER_TYPE = 1 - FILTER_TYPE_SUPPORTS_ARCH FILTER_TYPE = 2 -) - -// Enum value maps for FILTER_TYPE. -var ( - FILTER_TYPE_name = map[int32]string{ - 0: "NO_FILTER", - 1: "IN_REPOSITORY", - 2: "SUPPORTS_ARCH", - } - FILTER_TYPE_value = map[string]int32{ - "NO_FILTER": 0, - "IN_REPOSITORY": 1, - "SUPPORTS_ARCH": 2, - } -) - -func (x FILTER_TYPE) Enum() *FILTER_TYPE { - p := new(FILTER_TYPE) - *p = x - return p -} - -func (x FILTER_TYPE) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (FILTER_TYPE) Descriptor() protoreflect.EnumDescriptor { - return file_lure_proto_enumTypes[1].Descriptor() -} - -func (FILTER_TYPE) Type() protoreflect.EnumType { - return &file_lure_proto_enumTypes[1] -} - -func (x FILTER_TYPE) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use FILTER_TYPE.Descriptor instead. -func (FILTER_TYPE) EnumDescriptor() ([]byte, []int) { - return file_lure_proto_rawDescGZIP(), []int{1} -} - -// SearchRequest is a request to search for packages -type SearchRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Query string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` - Limit int64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` - SortBy SORT_BY `protobuf:"varint,3,opt,name=sort_by,json=sortBy,proto3,enum=lure.SORT_BY" json:"sort_by,omitempty"` - FilterType FILTER_TYPE `protobuf:"varint,4,opt,name=filter_type,json=filterType,proto3,enum=lure.FILTER_TYPE" json:"filter_type,omitempty"` - FilterValue *string `protobuf:"bytes,5,opt,name=filter_value,json=filterValue,proto3,oneof" json:"filter_value,omitempty"` -} - -func (x *SearchRequest) Reset() { - *x = SearchRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_lure_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SearchRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SearchRequest) ProtoMessage() {} - -func (x *SearchRequest) ProtoReflect() protoreflect.Message { - mi := &file_lure_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead. -func (*SearchRequest) Descriptor() ([]byte, []int) { - return file_lure_proto_rawDescGZIP(), []int{0} -} - -func (x *SearchRequest) GetQuery() string { - if x != nil { - return x.Query - } - return "" -} - -func (x *SearchRequest) GetLimit() int64 { - if x != nil { - return x.Limit - } - return 0 -} - -func (x *SearchRequest) GetSortBy() SORT_BY { - if x != nil { - return x.SortBy - } - return SORT_BY_UNSORTED -} - -func (x *SearchRequest) GetFilterType() FILTER_TYPE { - if x != nil { - return x.FilterType - } - return FILTER_TYPE_NO_FILTER -} - -func (x *SearchRequest) GetFilterValue() string { - if x != nil && x.FilterValue != nil { - return *x.FilterValue - } - return "" -} - -// StringList contains a list of strings -type StringList struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Entries []string `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` -} - -func (x *StringList) Reset() { - *x = StringList{} - if protoimpl.UnsafeEnabled { - mi := &file_lure_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *StringList) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StringList) ProtoMessage() {} - -func (x *StringList) ProtoReflect() protoreflect.Message { - mi := &file_lure_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StringList.ProtoReflect.Descriptor instead. -func (*StringList) Descriptor() ([]byte, []int) { - return file_lure_proto_rawDescGZIP(), []int{1} -} - -func (x *StringList) GetEntries() []string { - if x != nil { - return x.Entries - } - return nil -} - -// Package represents a LURE package -type Package struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"` - Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` - Release int64 `protobuf:"varint,4,opt,name=release,proto3" json:"release,omitempty"` - Epoch *int64 `protobuf:"varint,5,opt,name=epoch,proto3,oneof" json:"epoch,omitempty"` - Description *string `protobuf:"bytes,6,opt,name=description,proto3,oneof" json:"description,omitempty"` - Homepage *string `protobuf:"bytes,7,opt,name=homepage,proto3,oneof" json:"homepage,omitempty"` - Maintainer *string `protobuf:"bytes,8,opt,name=maintainer,proto3,oneof" json:"maintainer,omitempty"` - Architectures []string `protobuf:"bytes,9,rep,name=architectures,proto3" json:"architectures,omitempty"` - Licenses []string `protobuf:"bytes,10,rep,name=licenses,proto3" json:"licenses,omitempty"` - Provides []string `protobuf:"bytes,11,rep,name=provides,proto3" json:"provides,omitempty"` - Conflicts []string `protobuf:"bytes,12,rep,name=conflicts,proto3" json:"conflicts,omitempty"` - Replaces []string `protobuf:"bytes,13,rep,name=replaces,proto3" json:"replaces,omitempty"` - Depends map[string]*StringList `protobuf:"bytes,14,rep,name=depends,proto3" json:"depends,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - BuildDepends map[string]*StringList `protobuf:"bytes,15,rep,name=build_depends,json=buildDepends,proto3" json:"build_depends,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *Package) Reset() { - *x = Package{} - if protoimpl.UnsafeEnabled { - mi := &file_lure_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Package) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Package) ProtoMessage() {} - -func (x *Package) ProtoReflect() protoreflect.Message { - mi := &file_lure_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Package.ProtoReflect.Descriptor instead. -func (*Package) Descriptor() ([]byte, []int) { - return file_lure_proto_rawDescGZIP(), []int{2} -} - -func (x *Package) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Package) GetRepository() string { - if x != nil { - return x.Repository - } - return "" -} - -func (x *Package) GetVersion() string { - if x != nil { - return x.Version - } - return "" -} - -func (x *Package) GetRelease() int64 { - if x != nil { - return x.Release - } - return 0 -} - -func (x *Package) GetEpoch() int64 { - if x != nil && x.Epoch != nil { - return *x.Epoch - } - return 0 -} - -func (x *Package) GetDescription() string { - if x != nil && x.Description != nil { - return *x.Description - } - return "" -} - -func (x *Package) GetHomepage() string { - if x != nil && x.Homepage != nil { - return *x.Homepage - } - return "" -} - -func (x *Package) GetMaintainer() string { - if x != nil && x.Maintainer != nil { - return *x.Maintainer - } - return "" -} - -func (x *Package) GetArchitectures() []string { - if x != nil { - return x.Architectures - } - return nil -} - -func (x *Package) GetLicenses() []string { - if x != nil { - return x.Licenses - } - return nil -} - -func (x *Package) GetProvides() []string { - if x != nil { - return x.Provides - } - return nil -} - -func (x *Package) GetConflicts() []string { - if x != nil { - return x.Conflicts - } - return nil -} - -func (x *Package) GetReplaces() []string { - if x != nil { - return x.Replaces - } - return nil -} - -func (x *Package) GetDepends() map[string]*StringList { - if x != nil { - return x.Depends - } - return nil -} - -func (x *Package) GetBuildDepends() map[string]*StringList { - if x != nil { - return x.BuildDepends - } - return nil -} - -type GetPackageRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"` -} - -func (x *GetPackageRequest) Reset() { - *x = GetPackageRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_lure_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetPackageRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetPackageRequest) ProtoMessage() {} - -func (x *GetPackageRequest) ProtoReflect() protoreflect.Message { - mi := &file_lure_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetPackageRequest.ProtoReflect.Descriptor instead. -func (*GetPackageRequest) Descriptor() ([]byte, []int) { - return file_lure_proto_rawDescGZIP(), []int{3} -} - -func (x *GetPackageRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetPackageRequest) GetRepository() string { - if x != nil { - return x.Repository - } - return "" -} - -// SearchResponse contains returned packages -type SearchResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Packages []*Package `protobuf:"bytes,1,rep,name=packages,proto3" json:"packages,omitempty"` -} - -func (x *SearchResponse) Reset() { - *x = SearchResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_lure_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SearchResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SearchResponse) ProtoMessage() {} - -func (x *SearchResponse) ProtoReflect() protoreflect.Message { - mi := &file_lure_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SearchResponse.ProtoReflect.Descriptor instead. -func (*SearchResponse) Descriptor() ([]byte, []int) { - return file_lure_proto_rawDescGZIP(), []int{4} -} - -func (x *SearchResponse) GetPackages() []*Package { - if x != nil { - return x.Packages - } - return nil -} - -type GetBuildScriptRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"` -} - -func (x *GetBuildScriptRequest) Reset() { - *x = GetBuildScriptRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_lure_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetBuildScriptRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBuildScriptRequest) ProtoMessage() {} - -func (x *GetBuildScriptRequest) ProtoReflect() protoreflect.Message { - mi := &file_lure_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetBuildScriptRequest.ProtoReflect.Descriptor instead. -func (*GetBuildScriptRequest) Descriptor() ([]byte, []int) { - return file_lure_proto_rawDescGZIP(), []int{5} -} - -func (x *GetBuildScriptRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetBuildScriptRequest) GetRepository() string { - if x != nil { - return x.Repository - } - return "" -} - -type GetBuildScriptResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Script string `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` -} - -func (x *GetBuildScriptResponse) Reset() { - *x = GetBuildScriptResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_lure_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetBuildScriptResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBuildScriptResponse) ProtoMessage() {} - -func (x *GetBuildScriptResponse) ProtoReflect() protoreflect.Message { - mi := &file_lure_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetBuildScriptResponse.ProtoReflect.Descriptor instead. -func (*GetBuildScriptResponse) Descriptor() ([]byte, []int) { - return file_lure_proto_rawDescGZIP(), []int{6} -} - -func (x *GetBuildScriptResponse) GetScript() string { - if x != nil { - return x.Script - } - return "" -} - -var File_lure_proto protoreflect.FileDescriptor - -var file_lure_proto_rawDesc = []byte{ - 0x0a, 0x0a, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6c, 0x75, - 0x72, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x12, 0x26, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x0d, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x59, - 0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x32, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, - 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0c, - 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, - 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe4, 0x05, - 0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, - 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, - 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, - 0x65, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x48, 0x00, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x01, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x68, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x08, 0x68, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67, - 0x65, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x69, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x72, 0x63, - 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0d, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, - 0x1a, 0x0a, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x6c, - 0x69, 0x63, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, - 0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, - 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, - 0x73, 0x12, 0x34, 0x0a, 0x07, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x2e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, - 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, - 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x1a, 0x4c, 0x0a, - 0x0c, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, - 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x51, 0x0a, 0x11, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x08, - 0x0a, 0x06, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x68, 0x6f, 0x6d, - 0x65, 0x70, 0x61, 0x67, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x22, 0x47, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x61, 0x63, 0x6b, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, - 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x3b, 0x0a, - 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x29, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, - 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x22, 0x4b, 0x0a, 0x15, 0x47, 0x65, - 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x30, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2a, 0x3e, 0x0a, 0x07, 0x53, 0x4f, 0x52, - 0x54, 0x5f, 0x42, 0x59, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x4e, 0x53, 0x4f, 0x52, 0x54, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, - 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, - 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0b, 0x46, 0x49, 0x4c, - 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x46, - 0x49, 0x4c, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x5f, 0x52, 0x45, - 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x55, - 0x50, 0x50, 0x4f, 0x52, 0x54, 0x53, 0x5f, 0x41, 0x52, 0x43, 0x48, 0x10, 0x02, 0x32, 0xb9, 0x01, - 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x33, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, - 0x13, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72, - 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x47, 0x65, - 0x74, 0x50, 0x6b, 0x67, 0x12, 0x17, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x50, - 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, - 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x4b, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1b, - 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x75, - 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2e, 0x2f, - 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_lure_proto_rawDescOnce sync.Once - file_lure_proto_rawDescData = file_lure_proto_rawDesc -) - -func file_lure_proto_rawDescGZIP() []byte { - file_lure_proto_rawDescOnce.Do(func() { - file_lure_proto_rawDescData = protoimpl.X.CompressGZIP(file_lure_proto_rawDescData) - }) - return file_lure_proto_rawDescData -} - -var ( - file_lure_proto_enumTypes = make([]protoimpl.EnumInfo, 2) - file_lure_proto_msgTypes = make([]protoimpl.MessageInfo, 9) - file_lure_proto_goTypes = []interface{}{ - (SORT_BY)(0), // 0: lure.SORT_BY - (FILTER_TYPE)(0), // 1: lure.FILTER_TYPE - (*SearchRequest)(nil), // 2: lure.SearchRequest - (*StringList)(nil), // 3: lure.StringList - (*Package)(nil), // 4: lure.Package - (*GetPackageRequest)(nil), // 5: lure.GetPackageRequest - (*SearchResponse)(nil), // 6: lure.SearchResponse - (*GetBuildScriptRequest)(nil), // 7: lure.GetBuildScriptRequest - (*GetBuildScriptResponse)(nil), // 8: lure.GetBuildScriptResponse - nil, // 9: lure.Package.DependsEntry - nil, // 10: lure.Package.BuildDependsEntry - } -) -var file_lure_proto_depIdxs = []int32{ - 0, // 0: lure.SearchRequest.sort_by:type_name -> lure.SORT_BY - 1, // 1: lure.SearchRequest.filter_type:type_name -> lure.FILTER_TYPE - 9, // 2: lure.Package.depends:type_name -> lure.Package.DependsEntry - 10, // 3: lure.Package.build_depends:type_name -> lure.Package.BuildDependsEntry - 4, // 4: lure.SearchResponse.packages:type_name -> lure.Package - 3, // 5: lure.Package.DependsEntry.value:type_name -> lure.StringList - 3, // 6: lure.Package.BuildDependsEntry.value:type_name -> lure.StringList - 2, // 7: lure.API.Search:input_type -> lure.SearchRequest - 5, // 8: lure.API.GetPkg:input_type -> lure.GetPackageRequest - 7, // 9: lure.API.GetBuildScript:input_type -> lure.GetBuildScriptRequest - 6, // 10: lure.API.Search:output_type -> lure.SearchResponse - 4, // 11: lure.API.GetPkg:output_type -> lure.Package - 8, // 12: lure.API.GetBuildScript:output_type -> lure.GetBuildScriptResponse - 10, // [10:13] is the sub-list for method output_type - 7, // [7:10] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name -} - -func init() { file_lure_proto_init() } -func file_lure_proto_init() { - if File_lure_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_lure_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SearchRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_lure_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StringList); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_lure_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Package); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_lure_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPackageRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_lure_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SearchResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_lure_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBuildScriptRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_lure_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBuildScriptResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_lure_proto_msgTypes[0].OneofWrappers = []interface{}{} - file_lure_proto_msgTypes[2].OneofWrappers = []interface{}{} - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_lure_proto_rawDesc, - NumEnums: 2, - NumMessages: 9, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_lure_proto_goTypes, - DependencyIndexes: file_lure_proto_depIdxs, - EnumInfos: file_lure_proto_enumTypes, - MessageInfos: file_lure_proto_msgTypes, - }.Build() - File_lure_proto = out.File - file_lure_proto_rawDesc = nil - file_lure_proto_goTypes = nil - file_lure_proto_depIdxs = nil -} diff --git a/cmd/lure-api-server/internal/api/lure.proto b/cmd/lure-api-server/internal/api/lure.proto deleted file mode 100644 index 06c2105..0000000 --- a/cmd/lure-api-server/internal/api/lure.proto +++ /dev/null @@ -1,82 +0,0 @@ -syntax = "proto3"; -package lure; - -// Slight hack to provide protoc with a package name -option go_package = "../api"; - -// SORT_BY represents possible things to sort packages by -enum SORT_BY { - UNSORTED = 0; - NAME = 1; - REPOSITORY = 2; - VERSION = 3; -} - -// FILTER_TYPE represents possible filters for packages -enum FILTER_TYPE { - NO_FILTER = 0; - IN_REPOSITORY = 1; - SUPPORTS_ARCH = 2; -} - -// SearchRequest is a request to search for packages -message SearchRequest { - string query = 1; - int64 limit = 2; - SORT_BY sort_by = 3; - FILTER_TYPE filter_type = 4; - optional string filter_value = 5; -} - -// StringList contains a list of strings -message StringList { - repeated string entries = 1; -} - -// Package represents a LURE package -message Package { - string name = 1; - string repository = 2; - string version = 3; - int64 release = 4; - optional int64 epoch = 5; - optional string description = 6; - optional string homepage = 7; - optional string maintainer = 8; - repeated string architectures = 9; - repeated string licenses = 10; - repeated string provides = 11; - repeated string conflicts = 12; - repeated string replaces = 13; - map depends = 14; - map build_depends = 15; -} - -message GetPackageRequest { - string name = 1; - string repository = 2; -} - -// SearchResponse contains returned packages -message SearchResponse { - repeated Package packages = 1; -} - -message GetBuildScriptRequest { - string name = 1; - string repository = 2; -} - -message GetBuildScriptResponse { - string script = 1; -} - -// Web is the LURE Web service -service API { - // Search searches through LURE packages in the database - rpc Search(SearchRequest) returns (SearchResponse); - // GetPkg gets a single LURE package from the database - rpc GetPkg(GetPackageRequest) returns (Package); - // GetBuildScript returns the build script for the given package - rpc GetBuildScript(GetBuildScriptRequest) returns (GetBuildScriptResponse); -} \ No newline at end of file diff --git a/cmd/lure-api-server/internal/api/lure.twirp.go b/cmd/lure-api-server/internal/api/lure.twirp.go deleted file mode 100644 index c2e1927..0000000 --- a/cmd/lure-api-server/internal/api/lure.twirp.go +++ /dev/null @@ -1,1709 +0,0 @@ -// Code generated by protoc-gen-twirp v8.1.3, DO NOT EDIT. -// source: lure.proto - -package api - -import ( - context "context" - fmt "fmt" - http "net/http" - io "io" - json "encoding/json" - strconv "strconv" - strings "strings" -) - -import ( - protojson "google.golang.org/protobuf/encoding/protojson" - proto "google.golang.org/protobuf/proto" - twirp "github.com/twitchtv/twirp" - ctxsetters "github.com/twitchtv/twirp/ctxsetters" -) - -import ( - bytes "bytes" - errors "errors" - path "path" - url "net/url" -) - -// Version compatibility assertion. -// If the constant is not defined in the package, that likely means -// the package needs to be updated to work with this generated code. -// See https://twitchtv.github.io/twirp/docs/version_matrix.html -const _ = twirp.TwirpPackageMinVersion_8_1_0 - -// ============= -// API Interface -// ============= - -// Web is the LURE Web service -type API interface { - // Search searches through LURE packages in the database - Search(context.Context, *SearchRequest) (*SearchResponse, error) - - // GetPkg gets a single LURE package from the database - GetPkg(context.Context, *GetPackageRequest) (*Package, error) - - // GetBuildScript returns the build script for the given package - GetBuildScript(context.Context, *GetBuildScriptRequest) (*GetBuildScriptResponse, error) -} - -// =================== -// API Protobuf Client -// =================== - -type aPIProtobufClient struct { - client HTTPClient - urls [3]string - interceptor twirp.Interceptor - opts twirp.ClientOptions -} - -// NewAPIProtobufClient creates a Protobuf client that implements the API interface. -// It communicates using Protobuf and can be configured with a custom HTTPClient. -func NewAPIProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) API { - if c, ok := client.(*http.Client); ok { - client = withoutRedirects(c) - } - - clientOpts := twirp.ClientOptions{} - for _, o := range opts { - o(&clientOpts) - } - - // Using ReadOpt allows backwards and forwards compatibility with new options in the future - literalURLs := false - _ = clientOpts.ReadOpt("literalURLs", &literalURLs) - var pathPrefix string - if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { - pathPrefix = "/twirp" // default prefix - } - - // Build method URLs: []/./ - serviceURL := sanitizeBaseURL(baseURL) - serviceURL += baseServicePath(pathPrefix, "lure", "API") - urls := [3]string{ - serviceURL + "Search", - serviceURL + "GetPkg", - serviceURL + "GetBuildScript", - } - - return &aPIProtobufClient{ - client: client, - urls: urls, - interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), - opts: clientOpts, - } -} - -func (c *aPIProtobufClient) Search(ctx context.Context, in *SearchRequest) (*SearchResponse, error) { - ctx = ctxsetters.WithPackageName(ctx, "lure") - ctx = ctxsetters.WithServiceName(ctx, "API") - ctx = ctxsetters.WithMethodName(ctx, "Search") - caller := c.callSearch - if c.interceptor != nil { - caller = func(ctx context.Context, req *SearchRequest) (*SearchResponse, error) { - resp, err := c.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*SearchRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*SearchRequest) when calling interceptor") - } - return c.callSearch(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*SearchResponse) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*SearchResponse) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - return caller(ctx, in) -} - -func (c *aPIProtobufClient) callSearch(ctx context.Context, in *SearchRequest) (*SearchResponse, error) { - out := new(SearchResponse) - ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) - if err != nil { - twerr, ok := err.(twirp.Error) - if !ok { - twerr = twirp.InternalErrorWith(err) - } - callClientError(ctx, c.opts.Hooks, twerr) - return nil, err - } - - callClientResponseReceived(ctx, c.opts.Hooks) - - return out, nil -} - -func (c *aPIProtobufClient) GetPkg(ctx context.Context, in *GetPackageRequest) (*Package, error) { - ctx = ctxsetters.WithPackageName(ctx, "lure") - ctx = ctxsetters.WithServiceName(ctx, "API") - ctx = ctxsetters.WithMethodName(ctx, "GetPkg") - caller := c.callGetPkg - if c.interceptor != nil { - caller = func(ctx context.Context, req *GetPackageRequest) (*Package, error) { - resp, err := c.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*GetPackageRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*GetPackageRequest) when calling interceptor") - } - return c.callGetPkg(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*Package) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*Package) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - return caller(ctx, in) -} - -func (c *aPIProtobufClient) callGetPkg(ctx context.Context, in *GetPackageRequest) (*Package, error) { - out := new(Package) - ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) - if err != nil { - twerr, ok := err.(twirp.Error) - if !ok { - twerr = twirp.InternalErrorWith(err) - } - callClientError(ctx, c.opts.Hooks, twerr) - return nil, err - } - - callClientResponseReceived(ctx, c.opts.Hooks) - - return out, nil -} - -func (c *aPIProtobufClient) GetBuildScript(ctx context.Context, in *GetBuildScriptRequest) (*GetBuildScriptResponse, error) { - ctx = ctxsetters.WithPackageName(ctx, "lure") - ctx = ctxsetters.WithServiceName(ctx, "API") - ctx = ctxsetters.WithMethodName(ctx, "GetBuildScript") - caller := c.callGetBuildScript - if c.interceptor != nil { - caller = func(ctx context.Context, req *GetBuildScriptRequest) (*GetBuildScriptResponse, error) { - resp, err := c.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*GetBuildScriptRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*GetBuildScriptRequest) when calling interceptor") - } - return c.callGetBuildScript(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*GetBuildScriptResponse) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*GetBuildScriptResponse) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - return caller(ctx, in) -} - -func (c *aPIProtobufClient) callGetBuildScript(ctx context.Context, in *GetBuildScriptRequest) (*GetBuildScriptResponse, error) { - out := new(GetBuildScriptResponse) - ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out) - if err != nil { - twerr, ok := err.(twirp.Error) - if !ok { - twerr = twirp.InternalErrorWith(err) - } - callClientError(ctx, c.opts.Hooks, twerr) - return nil, err - } - - callClientResponseReceived(ctx, c.opts.Hooks) - - return out, nil -} - -// =============== -// API JSON Client -// =============== - -type aPIJSONClient struct { - client HTTPClient - urls [3]string - interceptor twirp.Interceptor - opts twirp.ClientOptions -} - -// NewAPIJSONClient creates a JSON client that implements the API interface. -// It communicates using JSON and can be configured with a custom HTTPClient. -func NewAPIJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) API { - if c, ok := client.(*http.Client); ok { - client = withoutRedirects(c) - } - - clientOpts := twirp.ClientOptions{} - for _, o := range opts { - o(&clientOpts) - } - - // Using ReadOpt allows backwards and forwards compatibility with new options in the future - literalURLs := false - _ = clientOpts.ReadOpt("literalURLs", &literalURLs) - var pathPrefix string - if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { - pathPrefix = "/twirp" // default prefix - } - - // Build method URLs: []/./ - serviceURL := sanitizeBaseURL(baseURL) - serviceURL += baseServicePath(pathPrefix, "lure", "API") - urls := [3]string{ - serviceURL + "Search", - serviceURL + "GetPkg", - serviceURL + "GetBuildScript", - } - - return &aPIJSONClient{ - client: client, - urls: urls, - interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), - opts: clientOpts, - } -} - -func (c *aPIJSONClient) Search(ctx context.Context, in *SearchRequest) (*SearchResponse, error) { - ctx = ctxsetters.WithPackageName(ctx, "lure") - ctx = ctxsetters.WithServiceName(ctx, "API") - ctx = ctxsetters.WithMethodName(ctx, "Search") - caller := c.callSearch - if c.interceptor != nil { - caller = func(ctx context.Context, req *SearchRequest) (*SearchResponse, error) { - resp, err := c.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*SearchRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*SearchRequest) when calling interceptor") - } - return c.callSearch(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*SearchResponse) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*SearchResponse) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - return caller(ctx, in) -} - -func (c *aPIJSONClient) callSearch(ctx context.Context, in *SearchRequest) (*SearchResponse, error) { - out := new(SearchResponse) - ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) - if err != nil { - twerr, ok := err.(twirp.Error) - if !ok { - twerr = twirp.InternalErrorWith(err) - } - callClientError(ctx, c.opts.Hooks, twerr) - return nil, err - } - - callClientResponseReceived(ctx, c.opts.Hooks) - - return out, nil -} - -func (c *aPIJSONClient) GetPkg(ctx context.Context, in *GetPackageRequest) (*Package, error) { - ctx = ctxsetters.WithPackageName(ctx, "lure") - ctx = ctxsetters.WithServiceName(ctx, "API") - ctx = ctxsetters.WithMethodName(ctx, "GetPkg") - caller := c.callGetPkg - if c.interceptor != nil { - caller = func(ctx context.Context, req *GetPackageRequest) (*Package, error) { - resp, err := c.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*GetPackageRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*GetPackageRequest) when calling interceptor") - } - return c.callGetPkg(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*Package) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*Package) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - return caller(ctx, in) -} - -func (c *aPIJSONClient) callGetPkg(ctx context.Context, in *GetPackageRequest) (*Package, error) { - out := new(Package) - ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) - if err != nil { - twerr, ok := err.(twirp.Error) - if !ok { - twerr = twirp.InternalErrorWith(err) - } - callClientError(ctx, c.opts.Hooks, twerr) - return nil, err - } - - callClientResponseReceived(ctx, c.opts.Hooks) - - return out, nil -} - -func (c *aPIJSONClient) GetBuildScript(ctx context.Context, in *GetBuildScriptRequest) (*GetBuildScriptResponse, error) { - ctx = ctxsetters.WithPackageName(ctx, "lure") - ctx = ctxsetters.WithServiceName(ctx, "API") - ctx = ctxsetters.WithMethodName(ctx, "GetBuildScript") - caller := c.callGetBuildScript - if c.interceptor != nil { - caller = func(ctx context.Context, req *GetBuildScriptRequest) (*GetBuildScriptResponse, error) { - resp, err := c.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*GetBuildScriptRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*GetBuildScriptRequest) when calling interceptor") - } - return c.callGetBuildScript(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*GetBuildScriptResponse) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*GetBuildScriptResponse) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - return caller(ctx, in) -} - -func (c *aPIJSONClient) callGetBuildScript(ctx context.Context, in *GetBuildScriptRequest) (*GetBuildScriptResponse, error) { - out := new(GetBuildScriptResponse) - ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out) - if err != nil { - twerr, ok := err.(twirp.Error) - if !ok { - twerr = twirp.InternalErrorWith(err) - } - callClientError(ctx, c.opts.Hooks, twerr) - return nil, err - } - - callClientResponseReceived(ctx, c.opts.Hooks) - - return out, nil -} - -// ================== -// API Server Handler -// ================== - -type aPIServer struct { - API - interceptor twirp.Interceptor - hooks *twirp.ServerHooks - pathPrefix string // prefix for routing - jsonSkipDefaults bool // do not include unpopulated fields (default values) in the response - jsonCamelCase bool // JSON fields are serialized as lowerCamelCase rather than keeping the original proto names -} - -// NewAPIServer builds a TwirpServer that can be used as an http.Handler to handle -// HTTP requests that are routed to the right method in the provided svc implementation. -// The opts are twirp.ServerOption modifiers, for example twirp.WithServerHooks(hooks). -func NewAPIServer(svc API, opts ...interface{}) TwirpServer { - serverOpts := newServerOpts(opts) - - // Using ReadOpt allows backwards and forwards compatibility with new options in the future - jsonSkipDefaults := false - _ = serverOpts.ReadOpt("jsonSkipDefaults", &jsonSkipDefaults) - jsonCamelCase := false - _ = serverOpts.ReadOpt("jsonCamelCase", &jsonCamelCase) - var pathPrefix string - if ok := serverOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { - pathPrefix = "/twirp" // default prefix - } - - return &aPIServer{ - API: svc, - hooks: serverOpts.Hooks, - interceptor: twirp.ChainInterceptors(serverOpts.Interceptors...), - pathPrefix: pathPrefix, - jsonSkipDefaults: jsonSkipDefaults, - jsonCamelCase: jsonCamelCase, - } -} - -// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. -// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) -func (s *aPIServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { - writeError(ctx, resp, err, s.hooks) -} - -// handleRequestBodyError is used to handle error when the twirp server cannot read request -func (s *aPIServer) handleRequestBodyError(ctx context.Context, resp http.ResponseWriter, msg string, err error) { - if context.Canceled == ctx.Err() { - s.writeError(ctx, resp, twirp.NewError(twirp.Canceled, "failed to read request: context canceled")) - return - } - if context.DeadlineExceeded == ctx.Err() { - s.writeError(ctx, resp, twirp.NewError(twirp.DeadlineExceeded, "failed to read request: deadline exceeded")) - return - } - s.writeError(ctx, resp, twirp.WrapError(malformedRequestError(msg), err)) -} - -// APIPathPrefix is a convenience constant that may identify URL paths. -// Should be used with caution, it only matches routes generated by Twirp Go clients, -// with the default "/twirp" prefix and default CamelCase service and method names. -// More info: https://twitchtv.github.io/twirp/docs/routing.html -const APIPathPrefix = "/twirp/lure.API/" - -func (s *aPIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { - ctx := req.Context() - ctx = ctxsetters.WithPackageName(ctx, "lure") - ctx = ctxsetters.WithServiceName(ctx, "API") - ctx = ctxsetters.WithResponseWriter(ctx, resp) - - var err error - ctx, err = callRequestReceived(ctx, s.hooks) - if err != nil { - s.writeError(ctx, resp, err) - return - } - - if req.Method != "POST" { - msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) - s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) - return - } - - // Verify path format: []/./ - prefix, pkgService, method := parseTwirpPath(req.URL.Path) - if pkgService != "lure.API" { - msg := fmt.Sprintf("no handler for path %q", req.URL.Path) - s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) - return - } - if prefix != s.pathPrefix { - msg := fmt.Sprintf("invalid path prefix %q, expected %q, on path %q", prefix, s.pathPrefix, req.URL.Path) - s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) - return - } - - switch method { - case "Search": - s.serveSearch(ctx, resp, req) - return - case "GetPkg": - s.serveGetPkg(ctx, resp, req) - return - case "GetBuildScript": - s.serveGetBuildScript(ctx, resp, req) - return - default: - msg := fmt.Sprintf("no handler for path %q", req.URL.Path) - s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) - return - } -} - -func (s *aPIServer) serveSearch(ctx context.Context, resp http.ResponseWriter, req *http.Request) { - header := req.Header.Get("Content-Type") - i := strings.Index(header, ";") - if i == -1 { - i = len(header) - } - switch strings.TrimSpace(strings.ToLower(header[:i])) { - case "application/json": - s.serveSearchJSON(ctx, resp, req) - case "application/protobuf": - s.serveSearchProtobuf(ctx, resp, req) - default: - msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) - twerr := badRouteError(msg, req.Method, req.URL.Path) - s.writeError(ctx, resp, twerr) - } -} - -func (s *aPIServer) serveSearchJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { - var err error - ctx = ctxsetters.WithMethodName(ctx, "Search") - ctx, err = callRequestRouted(ctx, s.hooks) - if err != nil { - s.writeError(ctx, resp, err) - return - } - - d := json.NewDecoder(req.Body) - rawReqBody := json.RawMessage{} - if err := d.Decode(&rawReqBody); err != nil { - s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) - return - } - reqContent := new(SearchRequest) - unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} - if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { - s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) - return - } - - handler := s.API.Search - if s.interceptor != nil { - handler = func(ctx context.Context, req *SearchRequest) (*SearchResponse, error) { - resp, err := s.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*SearchRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*SearchRequest) when calling interceptor") - } - return s.API.Search(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*SearchResponse) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*SearchResponse) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - - // Call service method - var respContent *SearchResponse - func() { - defer ensurePanicResponses(ctx, resp, s.hooks) - respContent, err = handler(ctx, reqContent) - }() - - if err != nil { - s.writeError(ctx, resp, err) - return - } - if respContent == nil { - s.writeError(ctx, resp, twirp.InternalError("received a nil *SearchResponse and nil error while calling Search. nil responses are not supported")) - return - } - - ctx = callResponsePrepared(ctx, s.hooks) - - marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} - respBytes, err := marshaler.Marshal(respContent) - if err != nil { - s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) - return - } - - ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) - resp.Header().Set("Content-Type", "application/json") - resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) - resp.WriteHeader(http.StatusOK) - - if n, err := resp.Write(respBytes); err != nil { - msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) - twerr := twirp.NewError(twirp.Unknown, msg) - ctx = callError(ctx, s.hooks, twerr) - } - callResponseSent(ctx, s.hooks) -} - -func (s *aPIServer) serveSearchProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { - var err error - ctx = ctxsetters.WithMethodName(ctx, "Search") - ctx, err = callRequestRouted(ctx, s.hooks) - if err != nil { - s.writeError(ctx, resp, err) - return - } - - buf, err := io.ReadAll(req.Body) - if err != nil { - s.handleRequestBodyError(ctx, resp, "failed to read request body", err) - return - } - reqContent := new(SearchRequest) - if err = proto.Unmarshal(buf, reqContent); err != nil { - s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) - return - } - - handler := s.API.Search - if s.interceptor != nil { - handler = func(ctx context.Context, req *SearchRequest) (*SearchResponse, error) { - resp, err := s.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*SearchRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*SearchRequest) when calling interceptor") - } - return s.API.Search(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*SearchResponse) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*SearchResponse) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - - // Call service method - var respContent *SearchResponse - func() { - defer ensurePanicResponses(ctx, resp, s.hooks) - respContent, err = handler(ctx, reqContent) - }() - - if err != nil { - s.writeError(ctx, resp, err) - return - } - if respContent == nil { - s.writeError(ctx, resp, twirp.InternalError("received a nil *SearchResponse and nil error while calling Search. nil responses are not supported")) - return - } - - ctx = callResponsePrepared(ctx, s.hooks) - - respBytes, err := proto.Marshal(respContent) - if err != nil { - s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) - return - } - - ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) - resp.Header().Set("Content-Type", "application/protobuf") - resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) - resp.WriteHeader(http.StatusOK) - if n, err := resp.Write(respBytes); err != nil { - msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) - twerr := twirp.NewError(twirp.Unknown, msg) - ctx = callError(ctx, s.hooks, twerr) - } - callResponseSent(ctx, s.hooks) -} - -func (s *aPIServer) serveGetPkg(ctx context.Context, resp http.ResponseWriter, req *http.Request) { - header := req.Header.Get("Content-Type") - i := strings.Index(header, ";") - if i == -1 { - i = len(header) - } - switch strings.TrimSpace(strings.ToLower(header[:i])) { - case "application/json": - s.serveGetPkgJSON(ctx, resp, req) - case "application/protobuf": - s.serveGetPkgProtobuf(ctx, resp, req) - default: - msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) - twerr := badRouteError(msg, req.Method, req.URL.Path) - s.writeError(ctx, resp, twerr) - } -} - -func (s *aPIServer) serveGetPkgJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { - var err error - ctx = ctxsetters.WithMethodName(ctx, "GetPkg") - ctx, err = callRequestRouted(ctx, s.hooks) - if err != nil { - s.writeError(ctx, resp, err) - return - } - - d := json.NewDecoder(req.Body) - rawReqBody := json.RawMessage{} - if err := d.Decode(&rawReqBody); err != nil { - s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) - return - } - reqContent := new(GetPackageRequest) - unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} - if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { - s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) - return - } - - handler := s.API.GetPkg - if s.interceptor != nil { - handler = func(ctx context.Context, req *GetPackageRequest) (*Package, error) { - resp, err := s.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*GetPackageRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*GetPackageRequest) when calling interceptor") - } - return s.API.GetPkg(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*Package) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*Package) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - - // Call service method - var respContent *Package - func() { - defer ensurePanicResponses(ctx, resp, s.hooks) - respContent, err = handler(ctx, reqContent) - }() - - if err != nil { - s.writeError(ctx, resp, err) - return - } - if respContent == nil { - s.writeError(ctx, resp, twirp.InternalError("received a nil *Package and nil error while calling GetPkg. nil responses are not supported")) - return - } - - ctx = callResponsePrepared(ctx, s.hooks) - - marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} - respBytes, err := marshaler.Marshal(respContent) - if err != nil { - s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) - return - } - - ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) - resp.Header().Set("Content-Type", "application/json") - resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) - resp.WriteHeader(http.StatusOK) - - if n, err := resp.Write(respBytes); err != nil { - msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) - twerr := twirp.NewError(twirp.Unknown, msg) - ctx = callError(ctx, s.hooks, twerr) - } - callResponseSent(ctx, s.hooks) -} - -func (s *aPIServer) serveGetPkgProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { - var err error - ctx = ctxsetters.WithMethodName(ctx, "GetPkg") - ctx, err = callRequestRouted(ctx, s.hooks) - if err != nil { - s.writeError(ctx, resp, err) - return - } - - buf, err := io.ReadAll(req.Body) - if err != nil { - s.handleRequestBodyError(ctx, resp, "failed to read request body", err) - return - } - reqContent := new(GetPackageRequest) - if err = proto.Unmarshal(buf, reqContent); err != nil { - s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) - return - } - - handler := s.API.GetPkg - if s.interceptor != nil { - handler = func(ctx context.Context, req *GetPackageRequest) (*Package, error) { - resp, err := s.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*GetPackageRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*GetPackageRequest) when calling interceptor") - } - return s.API.GetPkg(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*Package) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*Package) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - - // Call service method - var respContent *Package - func() { - defer ensurePanicResponses(ctx, resp, s.hooks) - respContent, err = handler(ctx, reqContent) - }() - - if err != nil { - s.writeError(ctx, resp, err) - return - } - if respContent == nil { - s.writeError(ctx, resp, twirp.InternalError("received a nil *Package and nil error while calling GetPkg. nil responses are not supported")) - return - } - - ctx = callResponsePrepared(ctx, s.hooks) - - respBytes, err := proto.Marshal(respContent) - if err != nil { - s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) - return - } - - ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) - resp.Header().Set("Content-Type", "application/protobuf") - resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) - resp.WriteHeader(http.StatusOK) - if n, err := resp.Write(respBytes); err != nil { - msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) - twerr := twirp.NewError(twirp.Unknown, msg) - ctx = callError(ctx, s.hooks, twerr) - } - callResponseSent(ctx, s.hooks) -} - -func (s *aPIServer) serveGetBuildScript(ctx context.Context, resp http.ResponseWriter, req *http.Request) { - header := req.Header.Get("Content-Type") - i := strings.Index(header, ";") - if i == -1 { - i = len(header) - } - switch strings.TrimSpace(strings.ToLower(header[:i])) { - case "application/json": - s.serveGetBuildScriptJSON(ctx, resp, req) - case "application/protobuf": - s.serveGetBuildScriptProtobuf(ctx, resp, req) - default: - msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) - twerr := badRouteError(msg, req.Method, req.URL.Path) - s.writeError(ctx, resp, twerr) - } -} - -func (s *aPIServer) serveGetBuildScriptJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { - var err error - ctx = ctxsetters.WithMethodName(ctx, "GetBuildScript") - ctx, err = callRequestRouted(ctx, s.hooks) - if err != nil { - s.writeError(ctx, resp, err) - return - } - - d := json.NewDecoder(req.Body) - rawReqBody := json.RawMessage{} - if err := d.Decode(&rawReqBody); err != nil { - s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) - return - } - reqContent := new(GetBuildScriptRequest) - unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} - if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { - s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) - return - } - - handler := s.API.GetBuildScript - if s.interceptor != nil { - handler = func(ctx context.Context, req *GetBuildScriptRequest) (*GetBuildScriptResponse, error) { - resp, err := s.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*GetBuildScriptRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*GetBuildScriptRequest) when calling interceptor") - } - return s.API.GetBuildScript(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*GetBuildScriptResponse) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*GetBuildScriptResponse) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - - // Call service method - var respContent *GetBuildScriptResponse - func() { - defer ensurePanicResponses(ctx, resp, s.hooks) - respContent, err = handler(ctx, reqContent) - }() - - if err != nil { - s.writeError(ctx, resp, err) - return - } - if respContent == nil { - s.writeError(ctx, resp, twirp.InternalError("received a nil *GetBuildScriptResponse and nil error while calling GetBuildScript. nil responses are not supported")) - return - } - - ctx = callResponsePrepared(ctx, s.hooks) - - marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} - respBytes, err := marshaler.Marshal(respContent) - if err != nil { - s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) - return - } - - ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) - resp.Header().Set("Content-Type", "application/json") - resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) - resp.WriteHeader(http.StatusOK) - - if n, err := resp.Write(respBytes); err != nil { - msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) - twerr := twirp.NewError(twirp.Unknown, msg) - ctx = callError(ctx, s.hooks, twerr) - } - callResponseSent(ctx, s.hooks) -} - -func (s *aPIServer) serveGetBuildScriptProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { - var err error - ctx = ctxsetters.WithMethodName(ctx, "GetBuildScript") - ctx, err = callRequestRouted(ctx, s.hooks) - if err != nil { - s.writeError(ctx, resp, err) - return - } - - buf, err := io.ReadAll(req.Body) - if err != nil { - s.handleRequestBodyError(ctx, resp, "failed to read request body", err) - return - } - reqContent := new(GetBuildScriptRequest) - if err = proto.Unmarshal(buf, reqContent); err != nil { - s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) - return - } - - handler := s.API.GetBuildScript - if s.interceptor != nil { - handler = func(ctx context.Context, req *GetBuildScriptRequest) (*GetBuildScriptResponse, error) { - resp, err := s.interceptor( - func(ctx context.Context, req interface{}) (interface{}, error) { - typedReq, ok := req.(*GetBuildScriptRequest) - if !ok { - return nil, twirp.InternalError("failed type assertion req.(*GetBuildScriptRequest) when calling interceptor") - } - return s.API.GetBuildScript(ctx, typedReq) - }, - )(ctx, req) - if resp != nil { - typedResp, ok := resp.(*GetBuildScriptResponse) - if !ok { - return nil, twirp.InternalError("failed type assertion resp.(*GetBuildScriptResponse) when calling interceptor") - } - return typedResp, err - } - return nil, err - } - } - - // Call service method - var respContent *GetBuildScriptResponse - func() { - defer ensurePanicResponses(ctx, resp, s.hooks) - respContent, err = handler(ctx, reqContent) - }() - - if err != nil { - s.writeError(ctx, resp, err) - return - } - if respContent == nil { - s.writeError(ctx, resp, twirp.InternalError("received a nil *GetBuildScriptResponse and nil error while calling GetBuildScript. nil responses are not supported")) - return - } - - ctx = callResponsePrepared(ctx, s.hooks) - - respBytes, err := proto.Marshal(respContent) - if err != nil { - s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) - return - } - - ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) - resp.Header().Set("Content-Type", "application/protobuf") - resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) - resp.WriteHeader(http.StatusOK) - if n, err := resp.Write(respBytes); err != nil { - msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) - twerr := twirp.NewError(twirp.Unknown, msg) - ctx = callError(ctx, s.hooks, twerr) - } - callResponseSent(ctx, s.hooks) -} - -func (s *aPIServer) ServiceDescriptor() ([]byte, int) { - return twirpFileDescriptor0, 0 -} - -func (s *aPIServer) ProtocGenTwirpVersion() string { - return "v8.1.3" -} - -// PathPrefix returns the base service path, in the form: "//./" -// that is everything in a Twirp route except for the . This can be used for routing, -// for example to identify the requests that are targeted to this service in a mux. -func (s *aPIServer) PathPrefix() string { - return baseServicePath(s.pathPrefix, "lure", "API") -} - -// ===== -// Utils -// ===== - -// HTTPClient is the interface used by generated clients to send HTTP requests. -// It is fulfilled by *(net/http).Client, which is sufficient for most users. -// Users can provide their own implementation for special retry policies. -// -// HTTPClient implementations should not follow redirects. Redirects are -// automatically disabled if *(net/http).Client is passed to client -// constructors. See the withoutRedirects function in this file for more -// details. -type HTTPClient interface { - Do(req *http.Request) (*http.Response, error) -} - -// TwirpServer is the interface generated server structs will support: they're -// HTTP handlers with additional methods for accessing metadata about the -// service. Those accessors are a low-level API for building reflection tools. -// Most people can think of TwirpServers as just http.Handlers. -type TwirpServer interface { - http.Handler - - // ServiceDescriptor returns gzipped bytes describing the .proto file that - // this service was generated from. Once unzipped, the bytes can be - // unmarshalled as a - // google.golang.org/protobuf/types/descriptorpb.FileDescriptorProto. - // - // The returned integer is the index of this particular service within that - // FileDescriptorProto's 'Service' slice of ServiceDescriptorProtos. This is a - // low-level field, expected to be used for reflection. - ServiceDescriptor() ([]byte, int) - - // ProtocGenTwirpVersion is the semantic version string of the version of - // twirp used to generate this file. - ProtocGenTwirpVersion() string - - // PathPrefix returns the HTTP URL path prefix for all methods handled by this - // service. This can be used with an HTTP mux to route Twirp requests. - // The path prefix is in the form: "//./" - // that is, everything in a Twirp route except for the at the end. - PathPrefix() string -} - -func newServerOpts(opts []interface{}) *twirp.ServerOptions { - serverOpts := &twirp.ServerOptions{} - for _, opt := range opts { - switch o := opt.(type) { - case twirp.ServerOption: - o(serverOpts) - case *twirp.ServerHooks: // backwards compatibility, allow to specify hooks as an argument - twirp.WithServerHooks(o)(serverOpts) - case nil: // backwards compatibility, allow nil value for the argument - continue - default: - panic(fmt.Sprintf("Invalid option type %T, please use a twirp.ServerOption", o)) - } - } - return serverOpts -} - -// WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta). -// Useful outside of the Twirp server (e.g. http middleware), but does not trigger hooks. -// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) -func WriteError(resp http.ResponseWriter, err error) { - writeError(context.Background(), resp, err, nil) -} - -// writeError writes Twirp errors in the response and triggers hooks. -func writeError(ctx context.Context, resp http.ResponseWriter, err error, hooks *twirp.ServerHooks) { - // Convert to a twirp.Error. Non-twirp errors are converted to internal errors. - var twerr twirp.Error - if !errors.As(err, &twerr) { - twerr = twirp.InternalErrorWith(err) - } - - statusCode := twirp.ServerHTTPStatusFromErrorCode(twerr.Code()) - ctx = ctxsetters.WithStatusCode(ctx, statusCode) - ctx = callError(ctx, hooks, twerr) - - respBody := marshalErrorToJSON(twerr) - - resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON - resp.Header().Set("Content-Length", strconv.Itoa(len(respBody))) - resp.WriteHeader(statusCode) // set HTTP status code and send response - - _, writeErr := resp.Write(respBody) - if writeErr != nil { - // We have three options here. We could log the error, call the Error - // hook, or just silently ignore the error. - // - // Logging is unacceptable because we don't have a user-controlled - // logger; writing out to stderr without permission is too rude. - // - // Calling the Error hook would confuse users: it would mean the Error - // hook got called twice for one request, which is likely to lead to - // duplicated log messages and metrics, no matter how well we document - // the behavior. - // - // Silently ignoring the error is our least-bad option. It's highly - // likely that the connection is broken and the original 'err' says - // so anyway. - _ = writeErr - } - - callResponseSent(ctx, hooks) -} - -// sanitizeBaseURL parses the the baseURL, and adds the "http" scheme if needed. -// If the URL is unparsable, the baseURL is returned unchanged. -func sanitizeBaseURL(baseURL string) string { - u, err := url.Parse(baseURL) - if err != nil { - return baseURL // invalid URL will fail later when making requests - } - if u.Scheme == "" { - u.Scheme = "http" - } - return u.String() -} - -// baseServicePath composes the path prefix for the service (without ). -// e.g.: baseServicePath("/twirp", "my.pkg", "MyService") -// -// returns => "/twirp/my.pkg.MyService/" -// -// e.g.: baseServicePath("", "", "MyService") -// -// returns => "/MyService/" -func baseServicePath(prefix, pkg, service string) string { - fullServiceName := service - if pkg != "" { - fullServiceName = pkg + "." + service - } - return path.Join("/", prefix, fullServiceName) + "/" -} - -// parseTwirpPath extracts path components form a valid Twirp route. -// Expected format: "[]/./" -// e.g.: prefix, pkgService, method := parseTwirpPath("/twirp/pkg.Svc/MakeHat") -func parseTwirpPath(path string) (string, string, string) { - parts := strings.Split(path, "/") - if len(parts) < 2 { - return "", "", "" - } - method := parts[len(parts)-1] - pkgService := parts[len(parts)-2] - prefix := strings.Join(parts[0:len(parts)-2], "/") - return prefix, pkgService, method -} - -// getCustomHTTPReqHeaders retrieves a copy of any headers that are set in -// a context through the twirp.WithHTTPRequestHeaders function. -// If there are no headers set, or if they have the wrong type, nil is returned. -func getCustomHTTPReqHeaders(ctx context.Context) http.Header { - header, ok := twirp.HTTPRequestHeaders(ctx) - if !ok || header == nil { - return nil - } - copied := make(http.Header) - for k, vv := range header { - if vv == nil { - copied[k] = nil - continue - } - copied[k] = make([]string, len(vv)) - copy(copied[k], vv) - } - return copied -} - -// newRequest makes an http.Request from a client, adding common headers. -func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) { - req, err := http.NewRequest("POST", url, reqBody) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if customHeader := getCustomHTTPReqHeaders(ctx); customHeader != nil { - req.Header = customHeader - } - req.Header.Set("Accept", contentType) - req.Header.Set("Content-Type", contentType) - req.Header.Set("Twirp-Version", "v8.1.3") - return req, nil -} - -// JSON serialization for errors -type twerrJSON struct { - Code string `json:"code"` - Msg string `json:"msg"` - Meta map[string]string `json:"meta,omitempty"` -} - -// marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body. -// If serialization fails, it will use a descriptive Internal error instead. -func marshalErrorToJSON(twerr twirp.Error) []byte { - // make sure that msg is not too large - msg := twerr.Msg() - if len(msg) > 1e6 { - msg = msg[:1e6] - } - - tj := twerrJSON{ - Code: string(twerr.Code()), - Msg: msg, - Meta: twerr.MetaMap(), - } - - buf, err := json.Marshal(&tj) - if err != nil { - buf = []byte("{\"type\": \"" + twirp.Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback - } - - return buf -} - -// errorFromResponse builds a twirp.Error from a non-200 HTTP response. -// If the response has a valid serialized Twirp error, then it's returned. -// If not, the response status code is used to generate a similar twirp -// error. See twirpErrorFromIntermediary for more info on intermediary errors. -func errorFromResponse(resp *http.Response) twirp.Error { - statusCode := resp.StatusCode - statusText := http.StatusText(statusCode) - - if isHTTPRedirect(statusCode) { - // Unexpected redirect: it must be an error from an intermediary. - // Twirp clients don't follow redirects automatically, Twirp only handles - // POST requests, redirects should only happen on GET and HEAD requests. - location := resp.Header.Get("Location") - msg := fmt.Sprintf("unexpected HTTP status code %d %q received, Location=%q", statusCode, statusText, location) - return twirpErrorFromIntermediary(statusCode, msg, location) - } - - respBodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return wrapInternal(err, "failed to read server error response body") - } - - var tj twerrJSON - dec := json.NewDecoder(bytes.NewReader(respBodyBytes)) - dec.DisallowUnknownFields() - if err := dec.Decode(&tj); err != nil || tj.Code == "" { - // Invalid JSON response; it must be an error from an intermediary. - msg := fmt.Sprintf("Error from intermediary with HTTP status code %d %q", statusCode, statusText) - return twirpErrorFromIntermediary(statusCode, msg, string(respBodyBytes)) - } - - errorCode := twirp.ErrorCode(tj.Code) - if !twirp.IsValidErrorCode(errorCode) { - msg := "invalid type returned from server error response: " + tj.Code - return twirp.InternalError(msg).WithMeta("body", string(respBodyBytes)) - } - - twerr := twirp.NewError(errorCode, tj.Msg) - for k, v := range tj.Meta { - twerr = twerr.WithMeta(k, v) - } - return twerr -} - -// twirpErrorFromIntermediary maps HTTP errors from non-twirp sources to twirp errors. -// The mapping is similar to gRPC: https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md. -// Returned twirp Errors have some additional metadata for inspection. -func twirpErrorFromIntermediary(status int, msg string, bodyOrLocation string) twirp.Error { - var code twirp.ErrorCode - if isHTTPRedirect(status) { // 3xx - code = twirp.Internal - } else { - switch status { - case 400: // Bad Request - code = twirp.Internal - case 401: // Unauthorized - code = twirp.Unauthenticated - case 403: // Forbidden - code = twirp.PermissionDenied - case 404: // Not Found - code = twirp.BadRoute - case 429: // Too Many Requests - code = twirp.ResourceExhausted - case 502, 503, 504: // Bad Gateway, Service Unavailable, Gateway Timeout - code = twirp.Unavailable - default: // All other codes - code = twirp.Unknown - } - } - - twerr := twirp.NewError(code, msg) - twerr = twerr.WithMeta("http_error_from_intermediary", "true") // to easily know if this error was from intermediary - twerr = twerr.WithMeta("status_code", strconv.Itoa(status)) - if isHTTPRedirect(status) { - twerr = twerr.WithMeta("location", bodyOrLocation) - } else { - twerr = twerr.WithMeta("body", bodyOrLocation) - } - return twerr -} - -func isHTTPRedirect(status int) bool { - return status >= 300 && status <= 399 -} - -// wrapInternal wraps an error with a prefix as an Internal error. -// The original error cause is accessible by github.com/pkg/errors.Cause. -func wrapInternal(err error, prefix string) twirp.Error { - return twirp.InternalErrorWith(&wrappedError{prefix: prefix, cause: err}) -} - -type wrappedError struct { - prefix string - cause error -} - -func (e *wrappedError) Error() string { return e.prefix + ": " + e.cause.Error() } -func (e *wrappedError) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As -func (e *wrappedError) Cause() error { return e.cause } // for github.com/pkg/errors - -// ensurePanicResponses makes sure that rpc methods causing a panic still result in a Twirp Internal -// error response (status 500), and error hooks are properly called with the panic wrapped as an error. -// The panic is re-raised so it can be handled normally with middleware. -func ensurePanicResponses(ctx context.Context, resp http.ResponseWriter, hooks *twirp.ServerHooks) { - if r := recover(); r != nil { - // Wrap the panic as an error so it can be passed to error hooks. - // The original error is accessible from error hooks, but not visible in the response. - err := errFromPanic(r) - twerr := &internalWithCause{msg: "Internal service panic", cause: err} - // Actually write the error - writeError(ctx, resp, twerr, hooks) - // If possible, flush the error to the wire. - f, ok := resp.(http.Flusher) - if ok { - f.Flush() - } - - panic(r) - } -} - -// errFromPanic returns the typed error if the recovered panic is an error, otherwise formats as error. -func errFromPanic(p interface{}) error { - if err, ok := p.(error); ok { - return err - } - return fmt.Errorf("panic: %v", p) -} - -// internalWithCause is a Twirp Internal error wrapping an original error cause, -// but the original error message is not exposed on Msg(). The original error -// can be checked with go1.13+ errors.Is/As, and also by (github.com/pkg/errors).Unwrap -type internalWithCause struct { - msg string - cause error -} - -func (e *internalWithCause) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As -func (e *internalWithCause) Cause() error { return e.cause } // for github.com/pkg/errors -func (e *internalWithCause) Error() string { return e.msg + ": " + e.cause.Error() } -func (e *internalWithCause) Code() twirp.ErrorCode { return twirp.Internal } -func (e *internalWithCause) Msg() string { return e.msg } -func (e *internalWithCause) Meta(key string) string { return "" } -func (e *internalWithCause) MetaMap() map[string]string { return nil } -func (e *internalWithCause) WithMeta(key string, val string) twirp.Error { return e } - -// malformedRequestError is used when the twirp server cannot unmarshal a request -func malformedRequestError(msg string) twirp.Error { - return twirp.NewError(twirp.Malformed, msg) -} - -// badRouteError is used when the twirp server cannot route a request -func badRouteError(msg string, method, url string) twirp.Error { - err := twirp.NewError(twirp.BadRoute, msg) - err = err.WithMeta("twirp_invalid_route", method+" "+url) - return err -} - -// withoutRedirects makes sure that the POST request can not be redirected. -// The standard library will, by default, redirect requests (including POSTs) if it gets a 302 or -// 303 response, and also 301s in go1.8. It redirects by making a second request, changing the -// method to GET and removing the body. This produces very confusing error messages, so instead we -// set a redirect policy that always errors. This stops Go from executing the redirect. -// -// We have to be a little careful in case the user-provided http.Client has its own CheckRedirect -// policy - if so, we'll run through that policy first. -// -// Because this requires modifying the http.Client, we make a new copy of the client and return it. -func withoutRedirects(in *http.Client) *http.Client { - copy := *in - copy.CheckRedirect = func(req *http.Request, via []*http.Request) error { - if in.CheckRedirect != nil { - // Run the input's redirect if it exists, in case it has side effects, but ignore any error it - // returns, since we want to use ErrUseLastResponse. - err := in.CheckRedirect(req, via) - _ = err // Silly, but this makes sure generated code passes errcheck -blank, which some people use. - } - return http.ErrUseLastResponse - } - return © -} - -// doProtobufRequest makes a Protobuf request to the remote Twirp service. -func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { - reqBodyBytes, err := proto.Marshal(in) - if err != nil { - return ctx, wrapInternal(err, "failed to marshal proto request") - } - reqBody := bytes.NewBuffer(reqBodyBytes) - if err = ctx.Err(); err != nil { - return ctx, wrapInternal(err, "aborted because context was done") - } - - req, err := newRequest(ctx, url, reqBody, "application/protobuf") - if err != nil { - return ctx, wrapInternal(err, "could not build request") - } - ctx, err = callClientRequestPrepared(ctx, hooks, req) - if err != nil { - return ctx, err - } - - req = req.WithContext(ctx) - resp, err := client.Do(req) - if err != nil { - return ctx, wrapInternal(err, "failed to do request") - } - defer func() { _ = resp.Body.Close() }() - - if err = ctx.Err(); err != nil { - return ctx, wrapInternal(err, "aborted because context was done") - } - - if resp.StatusCode != 200 { - return ctx, errorFromResponse(resp) - } - - respBodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return ctx, wrapInternal(err, "failed to read response body") - } - if err = ctx.Err(); err != nil { - return ctx, wrapInternal(err, "aborted because context was done") - } - - if err = proto.Unmarshal(respBodyBytes, out); err != nil { - return ctx, wrapInternal(err, "failed to unmarshal proto response") - } - return ctx, nil -} - -// doJSONRequest makes a JSON request to the remote Twirp service. -func doJSONRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { - marshaler := &protojson.MarshalOptions{UseProtoNames: true} - reqBytes, err := marshaler.Marshal(in) - if err != nil { - return ctx, wrapInternal(err, "failed to marshal json request") - } - if err = ctx.Err(); err != nil { - return ctx, wrapInternal(err, "aborted because context was done") - } - - req, err := newRequest(ctx, url, bytes.NewReader(reqBytes), "application/json") - if err != nil { - return ctx, wrapInternal(err, "could not build request") - } - ctx, err = callClientRequestPrepared(ctx, hooks, req) - if err != nil { - return ctx, err - } - - req = req.WithContext(ctx) - resp, err := client.Do(req) - if err != nil { - return ctx, wrapInternal(err, "failed to do request") - } - - defer func() { - cerr := resp.Body.Close() - if err == nil && cerr != nil { - err = wrapInternal(cerr, "failed to close response body") - } - }() - - if err = ctx.Err(); err != nil { - return ctx, wrapInternal(err, "aborted because context was done") - } - - if resp.StatusCode != 200 { - return ctx, errorFromResponse(resp) - } - - d := json.NewDecoder(resp.Body) - rawRespBody := json.RawMessage{} - if err := d.Decode(&rawRespBody); err != nil { - return ctx, wrapInternal(err, "failed to unmarshal json response") - } - unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} - if err = unmarshaler.Unmarshal(rawRespBody, out); err != nil { - return ctx, wrapInternal(err, "failed to unmarshal json response") - } - if err = ctx.Err(); err != nil { - return ctx, wrapInternal(err, "aborted because context was done") - } - return ctx, nil -} - -// Call twirp.ServerHooks.RequestReceived if the hook is available -func callRequestReceived(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { - if h == nil || h.RequestReceived == nil { - return ctx, nil - } - return h.RequestReceived(ctx) -} - -// Call twirp.ServerHooks.RequestRouted if the hook is available -func callRequestRouted(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { - if h == nil || h.RequestRouted == nil { - return ctx, nil - } - return h.RequestRouted(ctx) -} - -// Call twirp.ServerHooks.ResponsePrepared if the hook is available -func callResponsePrepared(ctx context.Context, h *twirp.ServerHooks) context.Context { - if h == nil || h.ResponsePrepared == nil { - return ctx - } - return h.ResponsePrepared(ctx) -} - -// Call twirp.ServerHooks.ResponseSent if the hook is available -func callResponseSent(ctx context.Context, h *twirp.ServerHooks) { - if h == nil || h.ResponseSent == nil { - return - } - h.ResponseSent(ctx) -} - -// Call twirp.ServerHooks.Error if the hook is available -func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) context.Context { - if h == nil || h.Error == nil { - return ctx - } - return h.Error(ctx, err) -} - -func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) { - if h == nil || h.ResponseReceived == nil { - return - } - h.ResponseReceived(ctx) -} - -func callClientRequestPrepared(ctx context.Context, h *twirp.ClientHooks, req *http.Request) (context.Context, error) { - if h == nil || h.RequestPrepared == nil { - return ctx, nil - } - return h.RequestPrepared(ctx, req) -} - -func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error) { - if h == nil || h.Error == nil { - return - } - h.Error(ctx, err) -} - -var twirpFileDescriptor0 = []byte{ - // 791 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xdd, 0x6e, 0xdb, 0x36, - 0x14, 0x8e, 0xac, 0xf8, 0xef, 0xc8, 0x72, 0x1d, 0xae, 0xeb, 0x34, 0xaf, 0x58, 0x0d, 0x6f, 0x0b, - 0xbc, 0x5e, 0x78, 0x85, 0xbb, 0x8b, 0x61, 0x03, 0x06, 0x44, 0x8b, 0x97, 0x18, 0xcd, 0x6c, 0x8f, - 0x72, 0x0b, 0x64, 0x37, 0x82, 0x22, 0x9f, 0x26, 0x44, 0x14, 0x49, 0x25, 0xe9, 0x00, 0x7e, 0x83, - 0xbe, 0xd2, 0xde, 0x62, 0x0f, 0xb1, 0x07, 0x19, 0x48, 0x4a, 0xae, 0xbc, 0xec, 0x6a, 0xbd, 0xd3, - 0xf7, 0x73, 0x0e, 0xc9, 0x73, 0x0e, 0x29, 0x80, 0x64, 0xc3, 0x71, 0x9c, 0xf3, 0x4c, 0x66, 0xe4, - 0x50, 0x7d, 0x0f, 0xff, 0xb2, 0xc0, 0x0d, 0x30, 0xe2, 0xf1, 0x0d, 0xc5, 0x77, 0x1b, 0x14, 0x92, - 0x3c, 0x86, 0xfa, 0xbb, 0x0d, 0xf2, 0xad, 0x67, 0x0d, 0xac, 0x51, 0x9b, 0x1a, 0xa0, 0xd8, 0x84, - 0xdd, 0x31, 0xe9, 0xd5, 0x06, 0xd6, 0xc8, 0xa6, 0x06, 0x90, 0x63, 0x68, 0x8a, 0x8c, 0xcb, 0xf0, - 0x6a, 0xeb, 0xd9, 0x03, 0x6b, 0xd4, 0x9d, 0xb8, 0x63, 0xbd, 0x42, 0xb0, 0xa0, 0xab, 0xd0, 0xbf, - 0xa4, 0x0d, 0xa5, 0xfa, 0x5b, 0x32, 0x01, 0xe7, 0x2d, 0x4b, 0x24, 0xf2, 0x50, 0x6e, 0x73, 0xf4, - 0x0e, 0xb5, 0xf7, 0xc8, 0x78, 0x7f, 0x9d, 0x5d, 0xac, 0xa6, 0x34, 0x5c, 0x5d, 0x2e, 0xa7, 0x14, - 0x8c, 0x6b, 0xb5, 0xcd, 0x91, 0x1c, 0x43, 0xa7, 0x88, 0xb9, 0x8f, 0x92, 0x0d, 0x7a, 0x75, 0xb5, - 0x9d, 0xf3, 0x03, 0x5a, 0x64, 0x7a, 0xa3, 0xc8, 0xf7, 0x96, 0xe5, 0x3f, 0x02, 0x37, 0xac, 0x1a, - 0x87, 0xc7, 0x00, 0x81, 0xe4, 0x2c, 0xbd, 0xbe, 0x60, 0x42, 0x12, 0x0f, 0x9a, 0x98, 0x4a, 0xce, - 0x50, 0x78, 0xd6, 0xc0, 0x1e, 0xb5, 0x69, 0x09, 0x87, 0x7f, 0xd7, 0xa1, 0xb9, 0x8c, 0xe2, 0xdb, - 0xe8, 0x1a, 0x09, 0x81, 0xc3, 0x34, 0xba, 0xc3, 0xe2, 0xcc, 0xfa, 0x9b, 0x7c, 0x09, 0xc0, 0x31, - 0xcf, 0x04, 0x93, 0x19, 0xdf, 0xea, 0x73, 0xb7, 0x69, 0x85, 0x51, 0x99, 0xef, 0x91, 0x0b, 0x96, - 0xa5, 0xfa, 0xf0, 0x6d, 0x5a, 0x42, 0xa5, 0x70, 0x4c, 0x30, 0x12, 0xe6, 0xa8, 0x36, 0x2d, 0x21, - 0xf9, 0x1c, 0xea, 0x98, 0x67, 0xf1, 0x8d, 0x3e, 0x8d, 0x7d, 0x7e, 0x40, 0x0d, 0x7c, 0x6f, 0x59, - 0xe4, 0x1b, 0x70, 0xd6, 0x28, 0x62, 0xce, 0x72, 0xa9, 0x52, 0x36, 0xf4, 0x71, 0x2d, 0x5a, 0x25, - 0x95, 0xed, 0x19, 0xb4, 0x6e, 0xb2, 0x3b, 0xcc, 0xa3, 0x6b, 0xf4, 0x9a, 0xda, 0x53, 0xa3, 0x3b, - 0x46, 0x19, 0xbe, 0x02, 0xb8, 0x8b, 0x58, 0x2a, 0x23, 0x96, 0x22, 0xf7, 0x5a, 0xda, 0x62, 0xd3, - 0x0a, 0xa7, 0x4c, 0x5f, 0x83, 0xab, 0x7a, 0xce, 0x24, 0xc6, 0x72, 0xc3, 0x51, 0x78, 0x6d, 0x5d, - 0x9b, 0x7d, 0x92, 0xf4, 0xa1, 0x95, 0xb0, 0x18, 0x53, 0x81, 0xc2, 0x03, 0x6d, 0xd8, 0x61, 0xa5, - 0xe5, 0x3c, 0xbb, 0x67, 0x6b, 0x14, 0x9e, 0x63, 0xb4, 0x12, 0x93, 0xa7, 0xd0, 0x8e, 0xb3, 0xf4, - 0x6d, 0xc2, 0x62, 0x29, 0xbc, 0x8e, 0x16, 0x3f, 0x10, 0x2a, 0x92, 0x63, 0x9e, 0x44, 0x31, 0x0a, - 0xcf, 0x35, 0x91, 0x25, 0x26, 0xdf, 0x43, 0x73, 0x8d, 0x39, 0xa6, 0x6b, 0xe1, 0x75, 0x07, 0xf6, - 0xc8, 0x99, 0xf4, 0xcd, 0x90, 0x14, 0x7d, 0x1a, 0x9f, 0x1a, 0x71, 0x9a, 0x4a, 0xbe, 0xa5, 0xa5, - 0x95, 0x9c, 0x82, 0x7b, 0xb5, 0x61, 0xc9, 0x3a, 0x2c, 0x63, 0x1f, 0xe9, 0xd8, 0x67, 0xfb, 0xb1, - 0xbe, 0xb2, 0xec, 0x25, 0xe8, 0x5c, 0x55, 0xa8, 0xfe, 0x05, 0x74, 0xaa, 0x2a, 0xe9, 0x81, 0x7d, - 0x8b, 0xe5, 0x35, 0x50, 0x9f, 0xe4, 0x18, 0xea, 0x66, 0x16, 0xd5, 0x30, 0x38, 0x93, 0x5e, 0x31, - 0xec, 0xbb, 0x61, 0xa3, 0x46, 0xfe, 0xb1, 0xf6, 0x83, 0xd5, 0xff, 0x1d, 0x8e, 0x1e, 0x2c, 0xf8, - 0x71, 0x29, 0xfd, 0x16, 0x34, 0x42, 0x3d, 0x2e, 0x7e, 0x17, 0x3a, 0x61, 0x65, 0x2e, 0x7c, 0x07, - 0xda, 0x61, 0x39, 0x03, 0xbe, 0x0b, 0x4e, 0xf8, 0xa1, 0xdb, 0xc3, 0x33, 0x38, 0x3a, 0x43, 0x59, - 0x14, 0xa1, 0xbc, 0xe4, 0xff, 0x63, 0xde, 0x87, 0x3f, 0x41, 0xb7, 0x7c, 0x29, 0x44, 0x9e, 0xa5, - 0x02, 0xc9, 0xb7, 0xd0, 0xca, 0x4d, 0x5e, 0x73, 0xb9, 0x9c, 0xf2, 0xfe, 0x97, 0xab, 0xed, 0xe4, - 0xe1, 0x2b, 0xf8, 0xf4, 0x0c, 0xa5, 0xae, 0x48, 0xa0, 0xb7, 0xfd, 0x31, 0x3b, 0x79, 0x01, 0x4f, - 0xfe, 0x9d, 0xac, 0xd8, 0xd1, 0x13, 0x68, 0x98, 0xaa, 0x14, 0xf9, 0x0a, 0xf4, 0xfc, 0x67, 0x68, - 0x16, 0x6f, 0x12, 0xe9, 0x40, 0xeb, 0xf5, 0x5c, 0x81, 0xe9, 0x69, 0xef, 0x80, 0xb4, 0xe0, 0x70, - 0x7e, 0xf2, 0xdb, 0xb4, 0x67, 0x91, 0x2e, 0x00, 0x9d, 0x2e, 0x17, 0xc1, 0x6c, 0xb5, 0xa0, 0x97, - 0xbd, 0x1a, 0x71, 0xa0, 0xf9, 0x66, 0x4a, 0x83, 0xd9, 0x62, 0xde, 0xb3, 0x9f, 0xfb, 0xe0, 0x54, - 0xde, 0x29, 0xe2, 0x42, 0x7b, 0xbe, 0x08, 0x0d, 0xd3, 0x3b, 0x20, 0x47, 0xe0, 0xce, 0xe6, 0x61, - 0x25, 0xda, 0x52, 0x54, 0xf0, 0x7a, 0xb9, 0x5c, 0xd0, 0x55, 0x10, 0x9e, 0xd0, 0x5f, 0xce, 0x7b, - 0xb5, 0xc9, 0x9f, 0x16, 0xd8, 0x27, 0xcb, 0x19, 0x79, 0x09, 0x0d, 0x53, 0x47, 0xf2, 0x49, 0xd1, - 0xed, 0xea, 0xfb, 0xdb, 0x7f, 0xbc, 0x4f, 0x16, 0x07, 0x7b, 0x01, 0x0d, 0xd5, 0xc5, 0xdb, 0x6b, - 0xf2, 0x99, 0xd1, 0x1f, 0xf4, 0xb4, 0xbf, 0x5f, 0x7b, 0xf2, 0x0a, 0xba, 0xfb, 0x45, 0x22, 0x5f, - 0xec, 0x22, 0x1f, 0xf6, 0xa1, 0xff, 0xf4, 0xbf, 0x45, 0xb3, 0xbc, 0xdf, 0xfa, 0xa3, 0x31, 0x1e, - 0x7f, 0x17, 0xe5, 0xec, 0xaa, 0xa1, 0xff, 0x1e, 0x2f, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0xed, - 0x6c, 0xfc, 0x7a, 0x4b, 0x06, 0x00, 0x00, -} diff --git a/cmd/lure-api-server/main.go b/cmd/lure-api-server/main.go deleted file mode 100644 index 10ea657..0000000 --- a/cmd/lure-api-server/main.go +++ /dev/null @@ -1,118 +0,0 @@ -/* - * LURE - Linux User REpository - * Copyright (C) 2023 Elara Musayelyan - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package main - -import ( - "context" - "flag" - "net" - "net/http" - "os" - - "github.com/go-chi/chi/v5" - "github.com/twitchtv/twirp" - "go.elara.ws/logger" - "go.elara.ws/lure/cmd/lure-api-server/internal/api" - "go.elara.ws/lure/internal/log" - "go.elara.ws/lure/pkg/repos" -) - -func init() { - log.Logger = logger.NewPretty(os.Stderr) -} - -func main() { - ctx := context.Background() - - addr := flag.String("a", ":8080", "Listen address for API server") - logFile := flag.String("l", "", "Output file for JSON log") - flag.Parse() - - if *logFile != "" { - fl, err := os.Create(*logFile) - if err != nil { - log.Fatal("Error creating log file").Err(err).Send() - } - defer fl.Close() - - log.Logger = logger.NewMulti(log.Logger, logger.NewJSON(fl)) - } - - err := repos.Pull(ctx, nil) - if err != nil { - log.Fatal("Error pulling repositories").Err(err).Send() - } - - sigCh := make(chan struct{}, 200) - go repoPullWorker(ctx, sigCh) - - apiServer := api.NewAPIServer( - lureWebAPI{}, - twirp.WithServerPathPrefix(""), - ) - - r := chi.NewRouter() - r.With(allowAllCORSHandler, withAcceptLanguage).Handle("/*", apiServer) - r.Post("/webhook", handleWebhook(sigCh)) - r.Get("/badge/{repo}/{pkg}", handleBadge()) - - ln, err := net.Listen("tcp", *addr) - if err != nil { - log.Fatal("Error starting listener").Err(err).Send() - } - - log.Info("Starting HTTP API server").Str("addr", ln.Addr().String()).Send() - - err = http.Serve(ln, r) - if err != nil { - log.Fatal("Error while running server").Err(err).Send() - } -} - -func allowAllCORSHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - res.Header().Set("Access-Control-Allow-Origin", "*") - res.Header().Set("Access-Control-Allow-Headers", "*") - if req.Method == http.MethodOptions { - return - } - h.ServeHTTP(res, req) - }) -} - -type ( - acceptLanguageKey struct{} - langParameterKey struct{} -) - -func withAcceptLanguage(h http.Handler) http.Handler { - return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - ctx := req.Context() - - langs := req.Header.Get("Accept-Language") - ctx = context.WithValue(ctx, acceptLanguageKey{}, langs) - - lang := req.URL.Query().Get("lang") - ctx = context.WithValue(ctx, langParameterKey{}, lang) - - req = req.WithContext(ctx) - - h.ServeHTTP(res, req) - }) -} diff --git a/cmd/lure-api-server/webhook.go b/cmd/lure-api-server/webhook.go deleted file mode 100644 index 63d0bc7..0000000 --- a/cmd/lure-api-server/webhook.go +++ /dev/null @@ -1,98 +0,0 @@ -/* - * LURE - Linux User REpository - * Copyright (C) 2023 Elara Musayelyan - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package main - -import ( - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "errors" - "io" - "net/http" - "os" - "strings" - - "go.elara.ws/lure/internal/log" - "go.elara.ws/lure/pkg/repos" -) - -func handleWebhook(sigCh chan<- struct{}) http.HandlerFunc { - return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - if req.Header.Get("X-GitHub-Event") != "push" { - http.Error(res, "Only push events are accepted by this bot", http.StatusBadRequest) - return - } - - err := verifySecure(req) - if err != nil { - http.Error(res, err.Error(), http.StatusForbidden) - return - } - - sigCh <- struct{}{} - return - }) -} - -func verifySecure(req *http.Request) error { - sigStr := req.Header.Get("X-Hub-Signature-256") - sig, err := hex.DecodeString(strings.TrimPrefix(sigStr, "sha256=")) - if err != nil { - return err - } - - secretStr, ok := os.LookupEnv("LURE_API_GITHUB_SECRET") - if !ok { - return errors.New("LURE_API_GITHUB_SECRET must be set to the secret used for setting up the github webhook\n\n") - } - secret := []byte(secretStr) - - h := hmac.New(sha256.New, secret) - _, err = io.Copy(h, req.Body) - if err != nil { - return err - } - - if !hmac.Equal(h.Sum(nil), sig) { - log.Warn("Insecure webhook request"). - Str("from", req.RemoteAddr). - Bytes("sig", sig). - Bytes("hmac", h.Sum(nil)). - Send() - - return errors.New("webhook signature mismatch") - } - - return nil -} - -func repoPullWorker(ctx context.Context, sigCh <-chan struct{}) { - for { - select { - case <-sigCh: - err := repos.Pull(ctx, nil) - if err != nil { - log.Warn("Error while pulling repositories").Err(err).Send() - } - case <-ctx.Done(): - return - } - } -} diff --git a/go.mod b/go.mod index a11baca..83c9276 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/charmbracelet/bubbles v0.16.1 github.com/charmbracelet/bubbletea v0.24.2 github.com/charmbracelet/lipgloss v0.8.0 - github.com/go-chi/chi/v5 v5.0.10 github.com/go-git/go-billy/v5 v5.5.0 github.com/go-git/go-git/v5 v5.9.0 github.com/goreleaser/nfpm/v2 v2.33.0 @@ -20,7 +19,6 @@ require ( github.com/muesli/reflow v0.3.0 github.com/pelletier/go-toml/v2 v2.1.0 github.com/schollz/progressbar/v3 v3.13.1 - github.com/twitchtv/twirp v8.1.3+incompatible github.com/urfave/cli/v2 v2.25.7 github.com/vmihailenco/msgpack/v5 v5.3.5 go.elara.ws/logger v0.0.0-20230421022458-e80700db2090 @@ -29,7 +27,6 @@ require ( golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/sys v0.12.0 golang.org/x/text v0.13.0 - google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.25.0 mvdan.cc/sh/v3 v3.7.0 diff --git a/go.sum b/go.sum index 75f9773..4a501ef 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= -github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= -github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= @@ -139,7 +137,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -148,7 +145,6 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -309,8 +305,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= -github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= -github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -537,9 +531,6 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=