mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-07-27 10:42:06 +00:00
Payment plugin fixes (#396)
* Avoid deducting from free quota for queries with unconfigured rates * Check query selections on an introspection query * Update comment
This commit is contained in:
parent
198d3e65db
commit
47d4b667f4
@ -21,6 +21,7 @@
|
|||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"graphql-subscriptions": "^2.0.0",
|
"graphql-subscriptions": "^2.0.0",
|
||||||
|
"pluralize": "^8.0.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"typeorm": "0.2.37",
|
"typeorm": "0.2.37",
|
||||||
"yargs": "^17.0.1"
|
"yargs": "^17.0.1"
|
||||||
@ -28,6 +29,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.14",
|
||||||
"@types/node": "16.11.7",
|
"@types/node": "16.11.7",
|
||||||
|
"@types/pluralize": "^0.0.29",
|
||||||
"@types/yargs": "^17.0.0",
|
"@types/yargs": "^17.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||||
"@typescript-eslint/parser": "^5.47.1",
|
"@typescript-eslint/parser": "^5.47.1",
|
||||||
|
@ -14,6 +14,8 @@ import { BaseRatesConfig, PaymentsConfig } from './config';
|
|||||||
const log = debug('laconic:payments');
|
const log = debug('laconic:payments');
|
||||||
|
|
||||||
const IntrospectionQuery = 'IntrospectionQuery';
|
const IntrospectionQuery = 'IntrospectionQuery';
|
||||||
|
const IntrospectionQuerySelection = '__schema';
|
||||||
|
|
||||||
const PAYMENT_HEADER_KEY = 'x-payment';
|
const PAYMENT_HEADER_KEY = 'x-payment';
|
||||||
const PAYMENT_HEADER_REGEX = /vhash:(.*),vsig:(.*)/;
|
const PAYMENT_HEADER_REGEX = /vhash:(.*),vsig:(.*)/;
|
||||||
|
|
||||||
@ -84,8 +86,12 @@ export class PaymentsManager {
|
|||||||
return this.ratesConfig.freeQueriesList ?? DEFAULT_FREE_QUERIES_LIST;
|
return this.ratesConfig.freeQueriesList ?? DEFAULT_FREE_QUERIES_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get queryRates (): { [key: string]: string } {
|
||||||
|
return this.ratesConfig.queries ?? {};
|
||||||
|
}
|
||||||
|
|
||||||
get mutationRates (): { [key: string]: string } {
|
get mutationRates (): { [key: string]: string } {
|
||||||
return this.ratesConfig.mutations;
|
return this.ratesConfig.mutations ?? {};
|
||||||
}
|
}
|
||||||
|
|
||||||
async subscribeToVouchers (client: Client): Promise<void> {
|
async subscribeToVouchers (client: Client): Promise<void> {
|
||||||
@ -151,9 +157,7 @@ export class PaymentsManager {
|
|||||||
await this.stopSubscriptionLoop.close();
|
await this.stopSubscriptionLoop.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async allowRequest (voucherHash: string, voucherSig: string, querySelection: string): Promise<[false, string] | [true, null]> {
|
async allowRequest (voucherHash: string, signerAddress: string, querySelection: string): Promise<[false, string] | [true, null]> {
|
||||||
const signerAddress = nitroUtils.getSignerAddress(voucherHash, voucherSig);
|
|
||||||
|
|
||||||
// Use free quota if EMPTY_VOUCHER_HASH passed
|
// Use free quota if EMPTY_VOUCHER_HASH passed
|
||||||
if (voucherHash === EMPTY_VOUCHER_HASH) {
|
if (voucherHash === EMPTY_VOUCHER_HASH) {
|
||||||
let remainingFreeQueries = this.remainingFreeQueriesMap.get(signerAddress);
|
let remainingFreeQueries = this.remainingFreeQueriesMap.get(signerAddress);
|
||||||
@ -173,14 +177,8 @@ export class PaymentsManager {
|
|||||||
return [false, ERR_FREE_QUOTA_EXHUASTED];
|
return [false, ERR_FREE_QUOTA_EXHUASTED];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve a query for free if rate is not configured
|
|
||||||
const configuredQueryCost = this.ratesConfig.queries[querySelection];
|
|
||||||
if (configuredQueryCost === undefined) {
|
|
||||||
log(`Query rate not configured for "${querySelection}", serving a free query to ${signerAddress}`);
|
|
||||||
return [true, null];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if required payment received from the Nitro account
|
// Check if required payment received from the Nitro account
|
||||||
|
const configuredQueryCost = this.ratesConfig.queries[querySelection];
|
||||||
const [paymentReceived, paymentError] = await this.authenticatePayment(voucherHash, signerAddress, BigInt(configuredQueryCost));
|
const [paymentReceived, paymentError] = await this.authenticatePayment(voucherHash, signerAddress, BigInt(configuredQueryCost));
|
||||||
|
|
||||||
if (paymentReceived) {
|
if (paymentReceived) {
|
||||||
@ -282,8 +280,21 @@ export const paymentsPlugin = (paymentsManager?: PaymentsManager): ApolloServerP
|
|||||||
async requestDidStart (requestContext: GraphQLRequestContext) {
|
async requestDidStart (requestContext: GraphQLRequestContext) {
|
||||||
return {
|
return {
|
||||||
async responseForOperation (requestContext: GraphQLRequestContext): Promise<GraphQLResponse | null> {
|
async responseForOperation (requestContext: GraphQLRequestContext): Promise<GraphQLResponse | null> {
|
||||||
// Continue if payments is not setup or it's an introspection query
|
// Continue if payments is not setup
|
||||||
if (!paymentsManager || requestContext.operationName === IntrospectionQuery) {
|
if (!paymentsManager) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const querySelections = requestContext.operation?.selectionSet.selections
|
||||||
|
.map((selection: any) => (selection as FieldNode).name.value);
|
||||||
|
|
||||||
|
// Continue if it's an introspection query for schema
|
||||||
|
// (made by ApolloServer playground / default landing page)
|
||||||
|
if (
|
||||||
|
requestContext.operationName === IntrospectionQuery &&
|
||||||
|
querySelections && querySelections.length === 1 &&
|
||||||
|
querySelections[0] === IntrospectionQuerySelection
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,8 +324,7 @@ export const paymentsPlugin = (paymentsManager?: PaymentsManager): ApolloServerP
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const querySelections = requestContext.operation?.selectionSet.selections
|
const signerAddress = nitroUtils.getSignerAddress(vhash, vsig);
|
||||||
.map((selection: any) => (selection as FieldNode).name.value);
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
for await (const querySelection of querySelections ?? []) {
|
for await (const querySelection of querySelections ?? []) {
|
||||||
@ -322,7 +332,14 @@ export const paymentsPlugin = (paymentsManager?: PaymentsManager): ApolloServerP
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [allowRequest, rejectionMessage] = await paymentsManager.allowRequest(vhash, vsig, querySelection);
|
// Serve a query for free if rate is not configured
|
||||||
|
const configuredQueryCost = paymentsManager.queryRates[querySelection];
|
||||||
|
if (configuredQueryCost === undefined) {
|
||||||
|
log(`Query rate not configured for "${querySelection}", serving a free query to ${signerAddress}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [allowRequest, rejectionMessage] = await paymentsManager.allowRequest(vhash, signerAddress, querySelection);
|
||||||
if (!allowRequest) {
|
if (!allowRequest) {
|
||||||
const failResponse: GraphQLResponse = {
|
const failResponse: GraphQLResponse = {
|
||||||
errors: [{ message: rejectionMessage }],
|
errors: [{ message: rejectionMessage }],
|
||||||
|
Loading…
Reference in New Issue
Block a user