diff --git a/backend/cmd/typescript_converter/main.go b/backend/cmd/typescript_converter/main.go index 2a3ce86f5..360e57188 100644 --- a/backend/cmd/typescript_converter/main.go +++ b/backend/cmd/typescript_converter/main.go @@ -21,7 +21,7 @@ const ( ) // Files that should not be converted to TypeScript -var ignoredFiles = []string{"data_access", "search_types"} +var ignoredFiles = []string{"data_access", "search_types", "archiver"} var typeMappings = map[string]string{ "decimal.Decimal": "string /* decimal.Decimal */", diff --git a/backend/pkg/api/data_access/app.go b/backend/pkg/api/data_access/app.go index e3c29d621..812549b1a 100644 --- a/backend/pkg/api/data_access/app.go +++ b/backend/pkg/api/data_access/app.go @@ -1,6 +1,7 @@ package dataaccess import ( + "context" "database/sql" "fmt" "time" @@ -19,6 +20,8 @@ type AppRepository interface { AddMobileNotificationToken(userID uint64, deviceID, notifyToken string) error GetAppSubscriptionCount(userID uint64) (uint64, error) AddMobilePurchase(tx *sql.Tx, userID uint64, paymentDetails t.MobileSubscription, verifyResponse *userservice.VerifyResponse, extSubscriptionId string) error + GetLatestBundleForNativeVersion(ctx context.Context, nativeVersion uint64) (*t.MobileAppBundleStats, error) + IncrementBundleDeliveryCount(ctx context.Context, bundleVerison uint64) error } // GetUserIdByRefreshToken basically used to confirm the claimed user id with the refresh token. Returns the userId if successful @@ -105,3 +108,13 @@ func (d *DataAccessService) AddMobilePurchase(tx *sql.Tx, userID uint64, payment return err } + +func (d *DataAccessService) GetLatestBundleForNativeVersion(ctx context.Context, nativeVersion uint64) (*t.MobileAppBundleStats, error) { + // @TODO data access + return d.dummy.GetLatestBundleForNativeVersion(ctx, nativeVersion) +} + +func (d *DataAccessService) IncrementBundleDeliveryCount(ctx context.Context, bundleVerison uint64) error { + // @TODO data access + return d.dummy.IncrementBundleDeliveryCount(ctx, bundleVerison) +} diff --git a/backend/pkg/api/data_access/dummy.go b/backend/pkg/api/data_access/dummy.go index 6197a72b2..36247ea76 100644 --- a/backend/pkg/api/data_access/dummy.go +++ b/backend/pkg/api/data_access/dummy.go @@ -11,7 +11,6 @@ import ( "github.com/go-faker/faker/v4" "github.com/go-faker/faker/v4/pkg/options" "github.com/gobitfly/beaconchain/pkg/api/enums" - "github.com/gobitfly/beaconchain/pkg/api/types" t "github.com/gobitfly/beaconchain/pkg/api/types" "github.com/gobitfly/beaconchain/pkg/userservice" "github.com/shopspring/decimal" @@ -637,7 +636,15 @@ func (d *DummyService) GetRocketPoolOverview(ctx context.Context) (*t.RocketPool return getDummyStruct[t.RocketPoolData]() } -func (d *DummyService) GetHealthz(ctx context.Context, showAll bool) types.HealthzData { - r, _ := getDummyData[types.HealthzData]() +func (d *DummyService) GetHealthz(ctx context.Context, showAll bool) t.HealthzData { + r, _ := getDummyData[t.HealthzData]() return r } + +func (d *DummyService) GetLatestBundleForNativeVersion(ctx context.Context, nativeVersion uint64) (*t.MobileAppBundleStats, error) { + return getDummyStruct[t.MobileAppBundleStats]() +} + +func (d *DummyService) IncrementBundleDeliveryCount(ctx context.Context, bundleVerison uint64) error { + return nil +} diff --git a/backend/pkg/api/handlers/internal.go b/backend/pkg/api/handlers/internal.go index 1c4157290..d111ab7ca 100644 --- a/backend/pkg/api/handlers/internal.go +++ b/backend/pkg/api/handlers/internal.go @@ -461,6 +461,52 @@ func (h *HandlerService) InternalGetValidatorDashboardRocketPoolMinipools(w http h.PublicGetValidatorDashboardRocketPoolMinipools(w, r) } +// -------------------------------------- +// Mobile + +func (h *HandlerService) InternalGetMobileLatestBundle(w http.ResponseWriter, r *http.Request) { + var v validationError + q := r.URL.Query() + force := v.checkBool(q.Get("force"), "force") + bundleVersion := v.checkUint(q.Get("bundle_version"), "bundle_version") + nativeVersion := v.checkUint(q.Get("native_version"), "native_version") + if v.hasErrors() { + handleErr(w, r, v) + return + } + stats, err := h.dai.GetLatestBundleForNativeVersion(r.Context(), nativeVersion) + if err != nil { + handleErr(w, r, err) + return + } + var data types.MobileBundleData + data.HasNativeUpdateAvailable = stats.MaxNativeVersion > nativeVersion + // if given bundle version is smaller than the latest and delivery count is less than target count, return the latest bundle + if force || (bundleVersion < stats.LatestBundleVersion && (stats.TargetCount == 0 || stats.DeliveryCount < stats.TargetCount)) { + data.BundleUrl = stats.BundleUrl + } + response := types.GetMobileLatestBundleResponse{ + Data: data, + } + returnOk(w, r, response) +} + +func (h *HandlerService) InternalPostMobileBundleDeliveries(w http.ResponseWriter, r *http.Request) { + var v validationError + vars := mux.Vars(r) + bundleVersion := v.checkUint(vars["bundle_version"], "bundle_version") + if v.hasErrors() { + handleErr(w, r, v) + return + } + err := h.dai.IncrementBundleDeliveryCount(r.Context(), bundleVersion) + if err != nil { + handleErr(w, r, err) + return + } + returnNoContent(w, r) +} + // -------------------------------------- // Notifications diff --git a/backend/pkg/api/router.go b/backend/pkg/api/router.go index 5603e4856..0b3d6a819 100644 --- a/backend/pkg/api/router.go +++ b/backend/pkg/api/router.go @@ -93,6 +93,8 @@ func addRoutes(hs *handlers.HandlerService, publicRouter, internalRouter *mux.Ro {http.MethodGet, "/mobile/authorize", nil, hs.InternalPostMobileAuthorize}, {http.MethodPost, "/mobile/equivalent-exchange", nil, hs.InternalPostMobileEquivalentExchange}, {http.MethodPost, "/mobile/purchase", nil, hs.InternalHandleMobilePurchase}, + {http.MethodGet, "/mobile/latest-bundle", nil, hs.InternalGetMobileLatestBundle}, + {http.MethodPost, "/mobile/bundles/{bundle_version}/deliveries", nil, hs.InternalPostMobileBundleDeliveries}, {http.MethodPost, "/logout", nil, hs.InternalPostLogout}, diff --git a/backend/pkg/api/types/data_access.go b/backend/pkg/api/types/data_access.go index fb1257f62..49d88b926 100644 --- a/backend/pkg/api/types/data_access.go +++ b/backend/pkg/api/types/data_access.go @@ -227,3 +227,14 @@ type HealthzData struct { DeploymentType string `json:"deployment_type"` Reports map[string][]HealthzResult `json:"status_reports"` } + +// ------------------------- +// Mobile structs + +type MobileAppBundleStats struct { + LatestBundleVersion uint64 + BundleUrl string + TargetCount uint64 // coalesce to 0 if column is null + DeliveryCount uint64 + MaxNativeVersion uint64 // the max native version of the whole table for the given environment +} diff --git a/backend/pkg/api/types/mobile.go b/backend/pkg/api/types/mobile.go new file mode 100644 index 000000000..7f84a999d --- /dev/null +++ b/backend/pkg/api/types/mobile.go @@ -0,0 +1,8 @@ +package types + +type MobileBundleData struct { + BundleUrl string `json:"bundle_url,omitempty"` + HasNativeUpdateAvailable bool `json:"has_native_update_available"` +} + +type GetMobileLatestBundleResponse ApiDataResponse[MobileBundleData] diff --git a/frontend/types/api/mobile.ts b/frontend/types/api/mobile.ts new file mode 100644 index 000000000..d6b234a18 --- /dev/null +++ b/frontend/types/api/mobile.ts @@ -0,0 +1,12 @@ +// Code generated by tygo. DO NOT EDIT. +/* eslint-disable */ +import type { ApiDataResponse } from './common' + +////////// +// source: mobile.go + +export interface MobileBundleData { + bundle_url?: string; + has_native_update_available: boolean; +} +export type GetMobileLatestBundleResponse = ApiDataResponse;