{storagenode/console,web/storagenode}: fetch pricing model from storagenode API
Instead of the hardcoded payout rates that is assumed for all satellites, this change adds a new endpoint for fetching the pricing model for each satellite. The pricing model is then displayed on the Info & Estimation table on the dashboard Updates https://github.com/storj/storj-private/issues/245 Change-Id: Iac7669e3e6eb690bbaad6e64bbbe42dfd775f078
This commit is contained in:
parent
d80d674863
commit
c64f3f3132
@ -155,6 +155,38 @@ func (dashboard *StorageNode) EstimatedPayout(w http.ResponseWriter, r *http.Req
|
||||
}
|
||||
}
|
||||
|
||||
// Pricing returns pricing model for specific satellite.
|
||||
func (dashboard *StorageNode) Pricing(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
w.Header().Set(contentType, applicationJSON)
|
||||
|
||||
params := mux.Vars(r)
|
||||
id, ok := params["id"]
|
||||
if !ok {
|
||||
dashboard.serveJSONError(w, http.StatusInternalServerError, ErrStorageNodeAPI.Wrap(err))
|
||||
return
|
||||
}
|
||||
satelliteID, err := storj.NodeIDFromString(id)
|
||||
if err != nil {
|
||||
dashboard.serveJSONError(w, http.StatusBadRequest, ErrStorageNodeAPI.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
data, err := dashboard.service.GetSatellitePricingModel(ctx, satelliteID)
|
||||
if err != nil {
|
||||
dashboard.serveJSONError(w, http.StatusInternalServerError, ErrStorageNodeAPI.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(data); err != nil {
|
||||
dashboard.log.Error("failed to encode json response", zap.Error(ErrStorageNodeAPI.Wrap(err)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// serveJSONError writes JSON error to response output stream.
|
||||
func (dashboard *StorageNode) serveJSONError(w http.ResponseWriter, status int, err error) {
|
||||
w.WriteHeader(status)
|
||||
|
@ -72,6 +72,7 @@ func NewServer(logger *zap.Logger, assets fs.FS, notifications *notifications.Se
|
||||
storageNodeRouter.HandleFunc("/", storageNodeController.StorageNode).Methods(http.MethodGet)
|
||||
storageNodeRouter.HandleFunc("/satellites", storageNodeController.Satellites).Methods(http.MethodGet)
|
||||
storageNodeRouter.HandleFunc("/satellite/{id}", storageNodeController.Satellite).Methods(http.MethodGet)
|
||||
storageNodeRouter.HandleFunc("/satellites/{id}/pricing", storageNodeController.Pricing).Methods(http.MethodGet)
|
||||
storageNodeRouter.HandleFunc("/estimated-payout", storageNodeController.EstimatedPayout).Methods(http.MethodGet)
|
||||
|
||||
notificationController := consoleapi.NewNotifications(server.log, server.notifications)
|
||||
|
@ -482,3 +482,15 @@ func (s *Service) VerifySatelliteID(ctx context.Context, satelliteID storj.NodeI
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSatellitePricingModel returns pricing model for the specified satellite.
|
||||
func (s *Service) GetSatellitePricingModel(ctx context.Context, satelliteID storj.NodeID) (pricingModel *pricing.Pricing, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
pricingModel, err = s.pricingDB.Get(ctx, satelliteID)
|
||||
if err != nil {
|
||||
return nil, SNOServiceErr.Wrap(err)
|
||||
}
|
||||
|
||||
return pricingModel, nil
|
||||
}
|
||||
|
@ -187,6 +187,12 @@ export default class SNOHeader extends Vue {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(PAYOUT_ACTIONS.GET_PRICING_MODEL, selectedSatelliteId);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
|
||||
|
||||
try {
|
||||
|
@ -127,6 +127,12 @@ export default class SatelliteSelectionDropdown extends Vue {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(PAYOUT_ACTIONS.GET_PRICING_MODEL, id);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(PAYOUT_ACTIONS.GET_TOTAL, id);
|
||||
} catch (error) {
|
||||
|
@ -146,9 +146,6 @@ import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { APPSTATE_ACTIONS } from '@/app/store/modules/appState';
|
||||
import {
|
||||
BANDWIDTH_DOWNLOAD_PRICE_PER_TB,
|
||||
BANDWIDTH_REPAIR_PRICE_PER_TB,
|
||||
DISK_SPACE_PRICE_PER_TB,
|
||||
PAYOUT_ACTIONS,
|
||||
} from '@/app/store/modules/payout';
|
||||
import {
|
||||
@ -156,7 +153,12 @@ import {
|
||||
PayoutInfoRange,
|
||||
} from '@/app/types/payout';
|
||||
import { Size } from '@/private/memory/size';
|
||||
import { EstimatedPayout, PayoutPeriod, TotalPaystubForPeriod } from '@/storagenode/payouts/payouts';
|
||||
import {
|
||||
EstimatedPayout,
|
||||
PayoutPeriod,
|
||||
SatellitePricingModel,
|
||||
TotalPaystubForPeriod,
|
||||
} from '@/storagenode/payouts/payouts';
|
||||
|
||||
import EstimationPeriodDropdown from '@/app/components/payments/EstimationPeriodDropdown.vue';
|
||||
|
||||
@ -263,6 +265,13 @@ export default class EstimationArea extends Vue {
|
||||
return this.$store.state.payoutModule.estimation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns satellite pricing model.
|
||||
*/
|
||||
public get pricing(): SatellitePricingModel {
|
||||
return this.$store.state.payoutModule.pricingModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns calculated or stored held amount.
|
||||
*/
|
||||
@ -375,7 +384,7 @@ export default class EstimationArea extends Vue {
|
||||
new EstimationTableRow(
|
||||
'Download',
|
||||
'Egress',
|
||||
`$${BANDWIDTH_DOWNLOAD_PRICE_PER_TB / 100} / TB`,
|
||||
`$${this.pricing.egressBandwidth} / TB`,
|
||||
'--',
|
||||
Size.toBase10String(estimatedPayout.egressBandwidth),
|
||||
estimatedPayout.egressBandwidthPayout,
|
||||
@ -383,7 +392,7 @@ export default class EstimationArea extends Vue {
|
||||
new EstimationTableRow(
|
||||
'Repair & Audit',
|
||||
'Egress',
|
||||
`$${BANDWIDTH_REPAIR_PRICE_PER_TB / 100} / TB`,
|
||||
`$${this.pricing.repairBandwidth} / TB`,
|
||||
'--',
|
||||
Size.toBase10String(estimatedPayout.egressRepairAudit),
|
||||
estimatedPayout.egressRepairAuditPayout,
|
||||
@ -391,7 +400,7 @@ export default class EstimationArea extends Vue {
|
||||
new EstimationTableRow(
|
||||
'Disk Average Month',
|
||||
'Storage',
|
||||
`$${DISK_SPACE_PRICE_PER_TB / 100} / TBm`,
|
||||
`$${this.pricing.diskSpace} / TBm`,
|
||||
Size.toBase10String(estimatedPayout.diskSpace) + 'm',
|
||||
'--',
|
||||
estimatedPayout.diskSpacePayout,
|
||||
|
@ -12,13 +12,14 @@ import {
|
||||
EstimatedPayout,
|
||||
PayoutPeriod,
|
||||
SatelliteHeldHistory,
|
||||
SatellitePayoutForPeriod,
|
||||
SatellitePayoutForPeriod, SatellitePricingModel,
|
||||
TotalPayments,
|
||||
TotalPaystubForPeriod,
|
||||
} from '@/storagenode/payouts/payouts';
|
||||
import { PayoutService } from '@/storagenode/payouts/service';
|
||||
|
||||
export const PAYOUT_MUTATIONS = {
|
||||
SET_PRICING_MODEL: 'SET_PRICING_MODEL',
|
||||
SET_PAYOUT_INFO: 'SET_PAYOUT_INFO',
|
||||
SET_RANGE: 'SET_RANGE',
|
||||
SET_TOTAL: 'SET_TOTAL',
|
||||
@ -32,6 +33,7 @@ export const PAYOUT_MUTATIONS = {
|
||||
};
|
||||
|
||||
export const PAYOUT_ACTIONS = {
|
||||
GET_PRICING_MODEL: 'GET_PRICING_MODEL',
|
||||
GET_PAYOUT_INFO: 'GET_PAYOUT_INFO',
|
||||
SET_PERIODS_RANGE: 'SET_PERIODS_RANGE',
|
||||
GET_TOTAL: 'GET_TOTAL',
|
||||
@ -42,11 +44,6 @@ export const PAYOUT_ACTIONS = {
|
||||
SET_PAYOUT_HISTORY_PERIOD: 'SET_PAYOUT_HISTORY_PERIOD',
|
||||
};
|
||||
|
||||
// TODO: move to config in storagenode/payouts
|
||||
export const BANDWIDTH_DOWNLOAD_PRICE_PER_TB = 2000;
|
||||
export const BANDWIDTH_REPAIR_PRICE_PER_TB = 1000;
|
||||
export const DISK_SPACE_PRICE_PER_TB = 150;
|
||||
|
||||
interface PayoutContext {
|
||||
rootState: {
|
||||
node: StorageNodeState;
|
||||
@ -83,6 +80,9 @@ export function newPayoutModule(service: PayoutService): StoreModule<PayoutState
|
||||
state.estimation = estimatedInfo;
|
||||
state.currentMonthEarnings = estimatedInfo.currentMonth.payout + estimatedInfo.currentMonth.held;
|
||||
},
|
||||
[PAYOUT_MUTATIONS.SET_PRICING_MODEL](state: PayoutState, pricing: SatellitePricingModel): void {
|
||||
state.pricingModel = pricing;
|
||||
},
|
||||
[PAYOUT_MUTATIONS.SET_PERIODS](state: PayoutState, periods: PayoutPeriod[]): void {
|
||||
state.payoutPeriods = periods;
|
||||
},
|
||||
@ -139,6 +139,11 @@ export function newPayoutModule(service: PayoutService): StoreModule<PayoutState
|
||||
|
||||
commit(PAYOUT_MUTATIONS.SET_ESTIMATION, estimatedInfo);
|
||||
},
|
||||
[PAYOUT_ACTIONS.GET_PRICING_MODEL]: async function ({ commit }: PayoutContext, satelliteId): Promise<void> {
|
||||
const pricing = await service.pricingModel(satelliteId);
|
||||
|
||||
commit(PAYOUT_MUTATIONS.SET_PRICING_MODEL, pricing);
|
||||
},
|
||||
[PAYOUT_ACTIONS.GET_PAYOUT_HISTORY]: async function ({ commit, state }: PayoutContext): Promise<void> {
|
||||
if (!state.payoutHistoryPeriod) return;
|
||||
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
EstimatedPayout,
|
||||
PayoutPeriod,
|
||||
SatelliteHeldHistory,
|
||||
SatellitePayoutForPeriod,
|
||||
SatellitePayoutForPeriod, SatellitePricingModel,
|
||||
TotalPayments,
|
||||
TotalPaystubForPeriod,
|
||||
} from '@/storagenode/payouts/payouts';
|
||||
@ -36,6 +36,7 @@ export class PayoutState {
|
||||
public payoutHistoryPeriod: string = '',
|
||||
public estimation: EstimatedPayout = new EstimatedPayout(),
|
||||
public payoutHistoryAvailablePeriods: PayoutPeriod[] = [],
|
||||
public pricingModel: SatellitePricingModel = new SatellitePricingModel(),
|
||||
) {}
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,12 @@ export default class PayoutArea extends Vue {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(PAYOUT_ACTIONS.GET_PRICING_MODEL, this.$store.state.node.selectedSatellite.id);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(PAYOUT_ACTIONS.GET_TOTAL);
|
||||
} catch (error) {
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
Paystub,
|
||||
PreviousMonthEstimatedPayout,
|
||||
SatelliteHeldHistory,
|
||||
SatellitePayoutForPeriod,
|
||||
SatellitePayoutForPeriod, SatellitePricingModel,
|
||||
} from '@/storagenode/payouts/payouts';
|
||||
import { HttpClient } from '@/storagenode/utils/httpClient';
|
||||
|
||||
@ -214,4 +214,28 @@ export class PayoutHttpApi implements PayoutApi {
|
||||
data.currentMonthExpectations,
|
||||
);
|
||||
}
|
||||
|
||||
public async getPricingModel(satelliteId: string): Promise<SatellitePricingModel> {
|
||||
if (!satelliteId) {
|
||||
return new SatellitePricingModel();
|
||||
}
|
||||
|
||||
const path = '/api/sno/satellites/'+ satelliteId +'/pricing';
|
||||
|
||||
const response = await this.client.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('can not get satellite pricing information');
|
||||
}
|
||||
|
||||
const data: any = await response.json() || new SatellitePricingModel(); // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
return new SatellitePricingModel(
|
||||
data.satelliteID,
|
||||
data.egressBandwidth,
|
||||
data.repairBandwidth,
|
||||
data.auditBandwidth,
|
||||
data.diskSpace,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,12 @@ export interface PayoutApi {
|
||||
*/
|
||||
getEstimatedPayout(satelliteId: string): Promise<EstimatedPayout>;
|
||||
|
||||
/**
|
||||
* Fetch satellite payout rate.
|
||||
* @throws Error
|
||||
*/
|
||||
getPricingModel(satelliteId: string): Promise<SatellitePricingModel>;
|
||||
|
||||
/**
|
||||
* Fetches payout history for all satellites.
|
||||
* @throws Error
|
||||
@ -320,3 +326,21 @@ export class SatellitePayoutForPeriod {
|
||||
return value / PRICE_DIVIDER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains satellite payout rates.
|
||||
*/
|
||||
export class SatellitePricingModel {
|
||||
public constructor(
|
||||
public satelliteID: string = '',
|
||||
public egressBandwidth: number = 0,
|
||||
public repairBandwidth: number = 0,
|
||||
public auditBandwidth: number = 0,
|
||||
public diskSpace: number = 0,
|
||||
) {
|
||||
this.egressBandwidth = this.egressBandwidth / 100;
|
||||
this.repairBandwidth = this.repairBandwidth / 100;
|
||||
this.auditBandwidth = this.auditBandwidth / 100;
|
||||
this.diskSpace = this.diskSpace / 100;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
Paystub,
|
||||
SatelliteHeldHistory,
|
||||
SatellitePayoutForPeriod,
|
||||
SatellitePricingModel,
|
||||
TotalPayments,
|
||||
TotalPaystubForPeriod,
|
||||
} from '@/storagenode/payouts/payouts';
|
||||
@ -79,4 +80,12 @@ export class PayoutService {
|
||||
public async estimatedPayout(satelliteId: string): Promise<EstimatedPayout> {
|
||||
return await this.payouts.getEstimatedPayout(satelliteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets satellite pricing model.
|
||||
* @param satelliteId
|
||||
*/
|
||||
public async pricingModel(satelliteId: string): Promise<SatellitePricingModel> {
|
||||
return await this.payouts.getPricingModel(satelliteId);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user