app metrics
This commit is contained in:
parent
f0dd9a08ac
commit
7e1157caa4
16 changed files with 3056 additions and 2462 deletions
|
|
@ -141,13 +141,20 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [AuthAppRequest](#AuthAppRequest)
|
||||
- output: [AuthApp](#AuthApp)
|
||||
|
||||
- GetMetrics
|
||||
- GetUsageMetrics
|
||||
- auth type: __Admin__
|
||||
- http method: __post__
|
||||
- http route: __/api/admin/metrics__
|
||||
- http route: __/api/admin/metrics/usage__
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [UsageMetrics](#UsageMetrics)
|
||||
|
||||
- GetAppsMetrics
|
||||
- auth type: __Admin__
|
||||
- http method: __post__
|
||||
- http route: __/api/admin/metrics/apps__
|
||||
- input: [AppsMetricsRequest](#AppsMetricsRequest)
|
||||
- output: [AppsMetrics](#AppsMetrics)
|
||||
|
||||
- Health
|
||||
- auth type: __Guest__
|
||||
- http method: __get__
|
||||
|
|
@ -414,105 +421,7 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
## Messages
|
||||
### The content of requests and response from the methods
|
||||
|
||||
### NewAddressRequest
|
||||
- __addressType__: _[AddressType](#AddressType)_
|
||||
|
||||
### DecodeInvoiceResponse
|
||||
- __amount__: _number_
|
||||
|
||||
### Product
|
||||
- __id__: _string_
|
||||
- __name__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### MigrationUpdate
|
||||
- __closure__: _[ClosureMigration](#ClosureMigration)_ *this field is optional
|
||||
- __relays__: _[RelaysMigration](#RelaysMigration)_ *this field is optional
|
||||
|
||||
### AuthAppRequest
|
||||
- __name__: _string_
|
||||
- __allow_user_creation__: _boolean_ *this field is optional
|
||||
|
||||
### LnurlWithdrawInfoResponse
|
||||
- __tag__: _string_
|
||||
- __callback__: _string_
|
||||
- __k1__: _string_
|
||||
- __defaultDescription__: _string_
|
||||
- __minWithdrawable__: _number_
|
||||
- __maxWithdrawable__: _number_
|
||||
- __balanceCheck__: _string_
|
||||
- __payLink__: _string_
|
||||
|
||||
### ClosureMigration
|
||||
- __closes_at_unix__: _number_
|
||||
|
||||
### SetMockAppBalanceRequest
|
||||
- __amount__: _number_
|
||||
|
||||
### SendAppUserToAppPaymentRequest
|
||||
- __from_user_identifier__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### NewInvoiceResponse
|
||||
- __invoice__: _string_
|
||||
|
||||
### LnurlLinkResponse
|
||||
- __lnurl__: _string_
|
||||
- __k1__: _string_
|
||||
|
||||
### AddProductRequest
|
||||
- __name__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### AddAppInvoiceRequest
|
||||
- __payer_identifier__: _string_
|
||||
- __http_callback_url__: _string_
|
||||
- __invoice_req__: _[NewInvoiceRequest](#NewInvoiceRequest)_
|
||||
|
||||
### AddAppRequest
|
||||
- __name__: _string_
|
||||
- __allow_user_creation__: _boolean_
|
||||
|
||||
### PayAddressRequest
|
||||
- __address__: _string_
|
||||
- __amoutSats__: _number_
|
||||
- __satsPerVByte__: _number_
|
||||
|
||||
### PayInvoiceResponse
|
||||
- __preimage__: _string_
|
||||
- __amount_paid__: _number_
|
||||
- __operation_id__: _string_
|
||||
- __service_fee__: _number_
|
||||
- __network_fee__: _number_
|
||||
|
||||
### OpenChannelRequest
|
||||
- __destination__: _string_
|
||||
- __fundingAmount__: _number_
|
||||
- __pushAmount__: _number_
|
||||
- __closeAddress__: _string_
|
||||
|
||||
### LnurlPayInfoResponse
|
||||
- __tag__: _string_
|
||||
- __callback__: _string_
|
||||
- __maxSendable__: _number_
|
||||
- __minSendable__: _number_
|
||||
- __metadata__: _string_
|
||||
- __allowsNostr__: _boolean_
|
||||
- __nostrPubkey__: _string_
|
||||
|
||||
### HandleLnurlPayResponse
|
||||
- __pr__: _string_
|
||||
- __routes__: ARRAY of: _[Empty](#Empty)_
|
||||
|
||||
### GetUserOperationsRequest
|
||||
- __latestIncomingInvoice__: _number_
|
||||
- __latestOutgoingInvoice__: _number_
|
||||
- __latestIncomingTx__: _number_
|
||||
- __latestOutgoingTx__: _number_
|
||||
- __latestIncomingUserToUserPayment__: _number_
|
||||
- __latestOutgoingUserToUserPayment__: _number_
|
||||
|
||||
### SetMockInvoiceAsPaidRequest
|
||||
### PayInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
|
|
@ -527,49 +436,124 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __network_fee__: _number_
|
||||
- __confirmed__: _boolean_
|
||||
|
||||
### OpenChannelResponse
|
||||
- __channelId__: _string_
|
||||
|
||||
### UserOperations
|
||||
- __fromIndex__: _number_
|
||||
- __toIndex__: _number_
|
||||
- __operations__: ARRAY of: _[UserOperation](#UserOperation)_
|
||||
|
||||
### AuthApp
|
||||
- __app__: _[Application](#Application)_
|
||||
- __auth_token__: _string_
|
||||
### RelaysMigration
|
||||
- __relays__: ARRAY of: _string_
|
||||
|
||||
### UsageMetrics
|
||||
- __metrics__: ARRAY of: _[UsageMetric](#UsageMetric)_
|
||||
|
||||
### Application
|
||||
### AddAppRequest
|
||||
- __name__: _string_
|
||||
- __id__: _string_
|
||||
- __balance__: _number_
|
||||
- __npub__: _string_
|
||||
- __allow_user_creation__: _boolean_
|
||||
|
||||
### AddAppUserRequest
|
||||
- __identifier__: _string_
|
||||
- __fail_if_exists__: _boolean_
|
||||
- __balance__: _number_
|
||||
|
||||
### AddAppUserInvoiceRequest
|
||||
- __receiver_identifier__: _string_
|
||||
- __payer_identifier__: _string_
|
||||
- __http_callback_url__: _string_
|
||||
- __invoice_req__: _[NewInvoiceRequest](#NewInvoiceRequest)_
|
||||
### OpenChannelResponse
|
||||
- __channelId__: _string_
|
||||
|
||||
### GetAppUserRequest
|
||||
### SetMockInvoiceAsPaidRequest
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### AuthApp
|
||||
- __app__: _[Application](#Application)_
|
||||
- __auth_token__: _string_
|
||||
|
||||
### GetAppUserLNURLInfoRequest
|
||||
- __user_identifier__: _string_
|
||||
- __base_url_override__: _string_
|
||||
|
||||
### PayAddressRequest
|
||||
- __address__: _string_
|
||||
- __amoutSats__: _number_
|
||||
- __satsPerVByte__: _number_
|
||||
|
||||
### NewAddressResponse
|
||||
- __address__: _string_
|
||||
|
||||
### PayAddressResponse
|
||||
- __txId__: _string_
|
||||
- __operation_id__: _string_
|
||||
- __service_fee__: _number_
|
||||
- __network_fee__: _number_
|
||||
|
||||
### DecodeInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
|
||||
### LnurlWithdrawInfoResponse
|
||||
- __tag__: _string_
|
||||
- __callback__: _string_
|
||||
- __k1__: _string_
|
||||
- __defaultDescription__: _string_
|
||||
- __minWithdrawable__: _number_
|
||||
- __maxWithdrawable__: _number_
|
||||
- __balanceCheck__: _string_
|
||||
- __payLink__: _string_
|
||||
|
||||
### MigrationUpdate
|
||||
- __closure__: _[ClosureMigration](#ClosureMigration)_ *this field is optional
|
||||
- __relays__: _[RelaysMigration](#RelaysMigration)_ *this field is optional
|
||||
|
||||
### UsageMetrics
|
||||
- __metrics__: ARRAY of: _[UsageMetric](#UsageMetric)_
|
||||
|
||||
### AppsMetricsRequest
|
||||
- __from_unix__: _number_ *this field is optional
|
||||
- __to_unix__: _number_ *this field is optional
|
||||
- __big_user_sats__: _number_ *this field is optional
|
||||
- __huge_user_sats__: _number_ *this field is optional
|
||||
- __include_operations__: _boolean_ *this field is optional
|
||||
|
||||
### SendAppUserToAppUserPaymentRequest
|
||||
- __from_user_identifier__: _string_
|
||||
- __to_user_identifier__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### GetAppUserLNURLInfoRequest
|
||||
### LnurlPayInfoResponse
|
||||
- __tag__: _string_
|
||||
- __callback__: _string_
|
||||
- __maxSendable__: _number_
|
||||
- __minSendable__: _number_
|
||||
- __metadata__: _string_
|
||||
- __allowsNostr__: _boolean_
|
||||
- __nostrPubkey__: _string_
|
||||
|
||||
### AppMetrics
|
||||
- __app_name__: _string_
|
||||
- __app_id__: _string_
|
||||
- __app_npub__: _string_
|
||||
- __app_balance__: _number_
|
||||
- __total_received__: _number_
|
||||
- __total_spent__: _number_
|
||||
- __total_available__: _number_
|
||||
- __total_users__: _number_
|
||||
- __total_big_users__: _number_
|
||||
- __total_huge_users__: _number_
|
||||
- __unpaid_invoices__: _number_
|
||||
- __unpaid_addresses__: _number_
|
||||
- __operations__: ARRAY of: _[UserOperation](#UserOperation)_
|
||||
|
||||
### PayAppUserInvoiceRequest
|
||||
- __user_identifier__: _string_
|
||||
- __base_url_override__: _string_
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### LiveUserOperation
|
||||
- __operation__: _[UserOperation](#UserOperation)_
|
||||
|
||||
### NewInvoiceRequest
|
||||
- __amountSats__: _number_
|
||||
- __memo__: _string_
|
||||
|
||||
### Product
|
||||
- __id__: _string_
|
||||
- __name__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### UsageMetric
|
||||
- __processed_at_nano__: _string_
|
||||
|
|
@ -581,17 +565,97 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __batch__: _boolean_
|
||||
- __nostr__: _boolean_
|
||||
- __batch_size__: _number_
|
||||
- __success__: _boolean_
|
||||
- __app_id__: _string_
|
||||
|
||||
### PayAddressResponse
|
||||
- __txId__: _string_
|
||||
### AppsMetrics
|
||||
- __apps__: ARRAY of: _[AppMetrics](#AppMetrics)_
|
||||
|
||||
### Application
|
||||
- __name__: _string_
|
||||
- __id__: _string_
|
||||
- __balance__: _number_
|
||||
- __npub__: _string_
|
||||
|
||||
### LndGetInfoRequest
|
||||
- __nodeId__: _number_
|
||||
|
||||
### SendAppUserToAppPaymentRequest
|
||||
- __from_user_identifier__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### NewInvoiceResponse
|
||||
- __invoice__: _string_
|
||||
|
||||
### HandleLnurlPayResponse
|
||||
- __pr__: _string_
|
||||
- __routes__: ARRAY of: _[Empty](#Empty)_
|
||||
|
||||
### LndGetInfoResponse
|
||||
- __alias__: _string_
|
||||
|
||||
### Empty
|
||||
|
||||
### AuthAppRequest
|
||||
- __name__: _string_
|
||||
- __allow_user_creation__: _boolean_ *this field is optional
|
||||
|
||||
### AddProductRequest
|
||||
- __name__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### LnurlLinkResponse
|
||||
- __lnurl__: _string_
|
||||
- __k1__: _string_
|
||||
|
||||
### UserInfo
|
||||
- __userId__: _string_
|
||||
- __balance__: _number_
|
||||
- __max_withdrawable__: _number_
|
||||
|
||||
### ClosureMigration
|
||||
- __closes_at_unix__: _number_
|
||||
|
||||
### GetAppUserRequest
|
||||
- __user_identifier__: _string_
|
||||
|
||||
### DecodeInvoiceResponse
|
||||
- __amount__: _number_
|
||||
|
||||
### SetMockAppBalanceRequest
|
||||
- __amount__: _number_
|
||||
|
||||
### EncryptionExchangeRequest
|
||||
- __publicKey__: _string_
|
||||
- __deviceId__: _string_
|
||||
|
||||
### AddAppUserInvoiceRequest
|
||||
- __receiver_identifier__: _string_
|
||||
- __payer_identifier__: _string_
|
||||
- __http_callback_url__: _string_
|
||||
- __invoice_req__: _[NewInvoiceRequest](#NewInvoiceRequest)_
|
||||
|
||||
### PayInvoiceResponse
|
||||
- __preimage__: _string_
|
||||
- __amount_paid__: _number_
|
||||
- __operation_id__: _string_
|
||||
- __service_fee__: _number_
|
||||
- __network_fee__: _number_
|
||||
|
||||
### DecodeInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
### GetUserOperationsRequest
|
||||
- __latestIncomingInvoice__: _number_
|
||||
- __latestOutgoingInvoice__: _number_
|
||||
- __latestIncomingTx__: _number_
|
||||
- __latestOutgoingTx__: _number_
|
||||
- __latestIncomingUserToUserPayment__: _number_
|
||||
- __latestOutgoingUserToUserPayment__: _number_
|
||||
|
||||
### NewAddressRequest
|
||||
- __addressType__: _[AddressType](#AddressType)_
|
||||
|
||||
### OpenChannelRequest
|
||||
- __destination__: _string_
|
||||
- __fundingAmount__: _number_
|
||||
- __pushAmount__: _number_
|
||||
- __closeAddress__: _string_
|
||||
|
||||
### GetUserOperationsResponse
|
||||
- __latestOutgoingInvoiceOperations__: _[UserOperations](#UserOperations)_
|
||||
|
|
@ -601,56 +665,22 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __latestOutgoingUserToUserPayemnts__: _[UserOperations](#UserOperations)_
|
||||
- __latestIncomingUserToUserPayemnts__: _[UserOperations](#UserOperations)_
|
||||
|
||||
### NewAddressResponse
|
||||
- __address__: _string_
|
||||
|
||||
### PayAppUserInvoiceRequest
|
||||
- __user_identifier__: _string_
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### SetMockAppUserBalanceRequest
|
||||
- __user_identifier__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### NewInvoiceRequest
|
||||
- __amountSats__: _number_
|
||||
- __memo__: _string_
|
||||
|
||||
### GetProductBuyLinkResponse
|
||||
- __link__: _string_
|
||||
|
||||
### Empty
|
||||
|
||||
### LndGetInfoRequest
|
||||
- __nodeId__: _number_
|
||||
|
||||
### LndGetInfoResponse
|
||||
- __alias__: _string_
|
||||
|
||||
### AppUser
|
||||
- __identifier__: _string_
|
||||
- __info__: _[UserInfo](#UserInfo)_
|
||||
- __max_withdrawable__: _number_
|
||||
|
||||
### PayInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
### AddAppInvoiceRequest
|
||||
- __payer_identifier__: _string_
|
||||
- __http_callback_url__: _string_
|
||||
- __invoice_req__: _[NewInvoiceRequest](#NewInvoiceRequest)_
|
||||
|
||||
### SetMockAppUserBalanceRequest
|
||||
- __user_identifier__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### UserInfo
|
||||
- __userId__: _string_
|
||||
- __balance__: _number_
|
||||
- __max_withdrawable__: _number_
|
||||
|
||||
### LiveUserOperation
|
||||
- __operation__: _[UserOperation](#UserOperation)_
|
||||
|
||||
### RelaysMigration
|
||||
- __relays__: ARRAY of: _string_
|
||||
|
||||
### EncryptionExchangeRequest
|
||||
- __publicKey__: _string_
|
||||
- __deviceId__: _string_
|
||||
## Enums
|
||||
### The enumerators used in the messages
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -98,20 +98,42 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.GetMetrics) throw new Error('method: GetMetrics is not implemented')
|
||||
app.post('/api/admin/metrics', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'GetMetrics', batch: false, nostr: false, batchSize: 0}
|
||||
if (!opts.allowNotImplementedMethods && !methods.GetUsageMetrics) throw new Error('method: GetUsageMetrics is not implemented')
|
||||
app.post('/api/admin/metrics/usage', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'GetUsageMetrics', batch: false, nostr: false, batchSize: 0}
|
||||
const stats: Types.RequestStats = { start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||
let authCtx: Types.AuthContext = {}
|
||||
try {
|
||||
if (!methods.GetMetrics) throw new Error('method: GetMetrics is not implemented')
|
||||
if (!methods.GetUsageMetrics) throw new Error('method: GetUsageMetrics is not implemented')
|
||||
const authContext = await opts.AdminAuthGuard(req.headers['authorization'])
|
||||
authCtx = authContext
|
||||
stats.guard = process.hrtime.bigint()
|
||||
stats.validate = stats.guard
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetMetrics({rpcName:'GetMetrics', ctx:authContext })
|
||||
const response = await methods.GetUsageMetrics({rpcName:'GetUsageMetrics', ctx:authContext })
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK', ...response})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.GetAppsMetrics) throw new Error('method: GetAppsMetrics is not implemented')
|
||||
app.post('/api/admin/metrics/apps', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'GetAppsMetrics', batch: false, nostr: false, batchSize: 0}
|
||||
const stats: Types.RequestStats = { start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||
let authCtx: Types.AuthContext = {}
|
||||
try {
|
||||
if (!methods.GetAppsMetrics) throw new Error('method: GetAppsMetrics is not implemented')
|
||||
const authContext = await opts.AdminAuthGuard(req.headers['authorization'])
|
||||
authCtx = authContext
|
||||
stats.guard = process.hrtime.bigint()
|
||||
const request = req.body
|
||||
const error = Types.AppsMetricsRequestValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetAppsMetrics({rpcName:'GetAppsMetrics', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK', ...response})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
|
|
|
|||
|
|
@ -57,10 +57,10 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetMetrics: async (): Promise<ResultError | ({ status: 'OK' }& Types.UsageMetrics)> => {
|
||||
GetUsageMetrics: async (): Promise<ResultError | ({ status: 'OK' }& Types.UsageMetrics)> => {
|
||||
const auth = await params.retrieveAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveAdminAuth() returned null')
|
||||
let finalRoute = '/api/admin/metrics'
|
||||
let finalRoute = '/api/admin/metrics/usage'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, {}, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
|
|
@ -71,6 +71,20 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetAppsMetrics: async (request: Types.AppsMetricsRequest): Promise<ResultError | ({ status: 'OK' }& Types.AppsMetrics)> => {
|
||||
const auth = await params.retrieveAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveAdminAuth() returned null')
|
||||
let finalRoute = '/api/admin/metrics/apps'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.AppsMetricsValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
Health: async (): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveGuestAuth()
|
||||
if (auth === null) throw new Error('retrieveGuestAuth() returned null')
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -97,10 +97,16 @@ service LightningPub {
|
|||
option (http_route) = "/api/admin/app/auth";
|
||||
}
|
||||
|
||||
rpc GetMetrics(structs.Empty) returns (structs.UsageMetrics) {
|
||||
rpc GetUsageMetrics(structs.Empty) returns (structs.UsageMetrics) {
|
||||
option (auth_type) = "Admin";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/admin/metrics";
|
||||
option (http_route) = "/api/admin/metrics/usage";
|
||||
}
|
||||
|
||||
rpc GetAppsMetrics(structs.AppsMetricsRequest) returns (structs.AppsMetrics) {
|
||||
option (auth_type) = "Admin";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/admin/metrics/apps";
|
||||
}
|
||||
// </Admin>
|
||||
|
||||
|
|
|
|||
|
|
@ -22,14 +22,44 @@ message UsageMetric {
|
|||
bool batch = 7;
|
||||
bool nostr = 8;
|
||||
int64 batch_size = 9;
|
||||
bool success = 10;
|
||||
string app_id = 11;
|
||||
}
|
||||
|
||||
message UsageMetrics {
|
||||
repeated UsageMetric metrics = 1;
|
||||
}
|
||||
|
||||
message AppsMetricsRequest {
|
||||
optional int64 from_unix = 1;
|
||||
optional int64 to_unix = 2;
|
||||
optional int64 big_user_sats = 3;
|
||||
optional int64 huge_user_sats = 4;
|
||||
optional bool include_operations = 5;
|
||||
}
|
||||
|
||||
message AppMetrics {
|
||||
string app_name = 1;
|
||||
string app_id = 2;
|
||||
string app_npub = 3;
|
||||
int64 app_balance = 4;
|
||||
|
||||
int64 total_received = 5;
|
||||
int64 total_spent = 6;
|
||||
int64 total_available = 7;
|
||||
|
||||
int64 total_users = 8;
|
||||
int64 total_big_users = 9;
|
||||
int64 total_huge_users = 10;
|
||||
|
||||
int64 unpaid_invoices = 11;
|
||||
int64 unpaid_addresses = 12;
|
||||
|
||||
repeated UserOperation operations = 100;
|
||||
}
|
||||
|
||||
message AppsMetrics {
|
||||
repeated AppMetrics apps = 1;
|
||||
}
|
||||
|
||||
message LndGetInfoRequest {
|
||||
int64 nodeId = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export default class {
|
|||
constructor(settings: MainSettings) {
|
||||
this.settings = settings
|
||||
this.storage = new Storage(settings.storageSettings)
|
||||
this.metricsManager = new MetricsManager()
|
||||
this.metricsManager = new MetricsManager(this.storage)
|
||||
this.lnd = NewLightningHandler(settings.lndSettings, this.addressPaidCb, this.invoicePaidCb, this.newBlockCb)
|
||||
|
||||
this.paymentManager = new PaymentManager(this.storage, this.lnd, this.settings, this.addressPaidCb, this.invoicePaidCb)
|
||||
|
|
|
|||
|
|
@ -90,11 +90,13 @@ export default class {
|
|||
|
||||
async NewAddress(ctx: Types.UserContext, req: Types.NewAddressRequest): Promise<Types.NewAddressResponse> {
|
||||
const app = await this.storage.applicationStorage.GetApplication(ctx.app_id)
|
||||
const existingAddress = await this.storage.paymentStorage.GetExistingUserAddress(ctx.user_id, app)
|
||||
if (existingAddress) {
|
||||
return { address: existingAddress.address }
|
||||
}
|
||||
const res = await this.lnd.NewAddress(req.addressType)
|
||||
const userAddress = await this.storage.paymentStorage.AddUserAddress(ctx.user_id, res.address, { linkedApplication: app })
|
||||
return {
|
||||
address: userAddress.address
|
||||
}
|
||||
return { address: userAddress.address }
|
||||
}
|
||||
|
||||
async NewInvoice(userId: string, req: Types.NewInvoiceRequest, options: InboundOptionals = { expiry: defaultInvoiceExpiry }): Promise<Types.NewInvoiceResponse> {
|
||||
|
|
@ -172,7 +174,7 @@ export default class {
|
|||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, serviceFee)
|
||||
}
|
||||
const routingFees = payment ? payment.feeSat : 0
|
||||
const newPayment = await this.storage.paymentStorage.AddUserInvoicePayment(userId, req.invoice, payAmount, routingFees, serviceFee, !!internalInvoice)
|
||||
const newPayment = await this.storage.paymentStorage.AddUserInvoicePayment(userId, req.invoice, payAmount, routingFees, serviceFee, !!internalInvoice, linkedApplication)
|
||||
return {
|
||||
preimage: payment ? payment.paymentPreimage : "",
|
||||
amount_paid: payment ? Number(payment.valueSat) : payAmount,
|
||||
|
|
@ -215,7 +217,7 @@ export default class {
|
|||
await this.storage.userStorage.IncrementUserBalance(app.owner.user_id, serviceFee)
|
||||
}
|
||||
|
||||
const newTx = await this.storage.paymentStorage.AddUserTransactionPayment(ctx.user_id, req.address, txId, 0, req.amoutSats, chainFees, serviceFee, !!internalAddress, blockHeight)
|
||||
const newTx = await this.storage.paymentStorage.AddUserTransactionPayment(ctx.user_id, req.address, txId, 0, req.amoutSats, chainFees, serviceFee, !!internalAddress, blockHeight, app)
|
||||
return {
|
||||
txId: txId,
|
||||
operation_id: `${Types.UserOperationType.OUTGOING_TX}-${newTx.serial_id}`,
|
||||
|
|
@ -471,7 +473,7 @@ export default class {
|
|||
const toIncrement = amount - fee
|
||||
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, amount, tx)
|
||||
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, toIncrement, tx)
|
||||
await this.storage.paymentStorage.AddUserToUserPayment(fromUserId, toUserId, amount, fee)
|
||||
await this.storage.paymentStorage.AddUserToUserPayment(fromUserId, toUserId, amount, fee, linkedApplication)
|
||||
if (isAppUserPayment && fee > 0) {
|
||||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, fee)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
import * as Types from '../../../proto/autogenerated/ts/types'
|
||||
import Storage from '../storage/index.js'
|
||||
import * as Types from '../../../proto/autogenerated/ts/types.js'
|
||||
import { Application } from '../storage/entity/Application.js'
|
||||
const maxEvents = 100_000
|
||||
export default class Handler {
|
||||
storage: Storage
|
||||
metrics: Types.UsageMetric[] = []
|
||||
constructor(storage: Storage) {
|
||||
this.storage = storage
|
||||
}
|
||||
AddMetrics(newMetrics: (Types.RequestMetric & { app_id?: string })[]) {
|
||||
const parsed: Types.UsageMetric[] = newMetrics.map(m => ({
|
||||
rpc_name: m.rpcName,
|
||||
|
|
@ -21,9 +27,95 @@ export default class Handler {
|
|||
this.metrics.splice(0, len - maxEvents)
|
||||
}
|
||||
}
|
||||
async GetMetrics(): Promise<Types.UsageMetrics> {
|
||||
async GetUsageMetrics(): Promise<Types.UsageMetrics> {
|
||||
return {
|
||||
metrics: this.metrics
|
||||
}
|
||||
}
|
||||
async GetAppsMetrics(req: Types.AppsMetricsRequest): Promise<Types.AppsMetrics> {
|
||||
const dbApps = await this.storage.applicationStorage.GetApplications()
|
||||
const apps = await Promise.all(dbApps.map(app => this.GetAppMetrics(req, app)))
|
||||
const unlinked = await this.GetAppMetrics(req, null)
|
||||
apps.push(unlinked)
|
||||
return {
|
||||
apps
|
||||
}
|
||||
}
|
||||
|
||||
async GetAppMetrics(req: Types.AppsMetricsRequest, app: Application | null): Promise<Types.AppMetrics> {
|
||||
const { receivingInvoices, receivingTransactions, outgoingInvoices, outgoingTransactions, receivingAddresses, userToUser } = await this.storage.paymentStorage.GetAppOperations(app, { from: req.from_unix, to: req.to_unix })
|
||||
const bigUser = req.big_user_sats ? req.big_user_sats : 10_000
|
||||
const hugeUser = req.big_user_sats ? req.big_user_sats : 500_000
|
||||
let totalReceived = 0
|
||||
let totalSpent = 0
|
||||
let totalAvailable = 0
|
||||
let totalBigUsers = 0
|
||||
let totalHugeUsers = 0
|
||||
let unpaidInvoices = 0
|
||||
let paidAddresses = 0
|
||||
const operations: Types.UserOperation[] = []
|
||||
receivingInvoices.forEach(i => {
|
||||
if (i.paid_at_unix > 0) {
|
||||
totalReceived += i.paid_amount
|
||||
operations.push({ type: Types.UserOperationType.INCOMING_INVOICE, amount: i.paid_amount, inbound: true, paidAtUnix: i.paid_at_unix, confirmed: true, service_fee: i.service_fee, network_fee: 0, identifier: "", operationId: "" })
|
||||
} else {
|
||||
unpaidInvoices++
|
||||
}
|
||||
})
|
||||
receivingTransactions.forEach(txs => {
|
||||
if (txs.length > 0) {
|
||||
paidAddresses++
|
||||
} else {
|
||||
txs.forEach(tx => {
|
||||
operations.push({ type: Types.UserOperationType.INCOMING_TX, amount: tx.paid_amount, inbound: true, paidAtUnix: tx.paid_at_unix, confirmed: tx.confs > 1, service_fee: tx.service_fee, network_fee: 0, identifier: "", operationId: "" })
|
||||
if (tx.confs > 1) {
|
||||
totalReceived += tx.paid_amount
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
outgoingInvoices.forEach(i => {
|
||||
operations.push({ type: Types.UserOperationType.OUTGOING_INVOICE, amount: i.paid_amount, inbound: false, paidAtUnix: i.paid_at_unix, confirmed: true, service_fee: i.service_fees, network_fee: i.routing_fees, identifier: "", operationId: "" })
|
||||
totalSpent += i.paid_amount
|
||||
})
|
||||
outgoingTransactions.forEach(tx => {
|
||||
operations.push({ type: Types.UserOperationType.OUTGOING_TX, amount: tx.paid_amount, inbound: false, paidAtUnix: tx.paid_at_unix, confirmed: tx.confs > 1, service_fee: tx.service_fees, network_fee: tx.chain_fees, identifier: "", operationId: "" })
|
||||
totalSpent += tx.paid_amount
|
||||
})
|
||||
|
||||
userToUser.forEach(op => {
|
||||
operations.push({ type: Types.UserOperationType.INCOMING_USER_TO_USER, amount: op.paid_amount, inbound: true, paidAtUnix: op.paid_at_unix, confirmed: true, service_fee: op.service_fees, network_fee: 0, identifier: "", operationId: "" })
|
||||
})
|
||||
|
||||
const users = await this.storage.applicationStorage.GetApplicationUsers(app, { from: req.from_unix, to: req.to_unix })
|
||||
|
||||
users.forEach(u => {
|
||||
totalAvailable += u.user.balance_sats
|
||||
if (u.user.balance_sats > bigUser) {
|
||||
totalBigUsers++
|
||||
}
|
||||
if (u.user.balance_sats > hugeUser) {
|
||||
totalHugeUsers++
|
||||
}
|
||||
})
|
||||
return {
|
||||
app_name: app ? app.name : "unlinked to app",
|
||||
app_id: app ? app.app_id : "unlinked",
|
||||
app_npub: app ? (app.nostr_public_key || "") : "",
|
||||
app_balance: app ? app.owner.balance_sats : 0,
|
||||
|
||||
total_received: totalReceived,
|
||||
total_spent: totalSpent,
|
||||
total_available: totalAvailable,
|
||||
|
||||
total_users: users.length,
|
||||
total_big_users: totalBigUsers,
|
||||
total_huge_users: totalHugeUsers,
|
||||
|
||||
unpaid_invoices: unpaidInvoices,
|
||||
unpaid_addresses: receivingAddresses.length - paidAddresses,
|
||||
|
||||
operations
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,11 @@ import main from '../main/index.js'
|
|||
import Main from '../main/index.js'
|
||||
export default (mainHandler: Main): Types.ServerMethods => {
|
||||
return {
|
||||
GetMetrics: async ({ ctx }) => {
|
||||
return mainHandler.metricsManager.GetMetrics()
|
||||
GetUsageMetrics: async ({ ctx }) => {
|
||||
return mainHandler.metricsManager.GetUsageMetrics()
|
||||
},
|
||||
GetAppsMetrics: async ({ ctx, req }) => {
|
||||
return mainHandler.metricsManager.GetAppsMetrics(req)
|
||||
},
|
||||
EncryptionExchange: async () => { },
|
||||
Health: async () => { await mainHandler.lnd.Health() },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import crypto from 'crypto';
|
||||
import { DataSource, EntityManager } from "typeorm"
|
||||
import { Between, DataSource, EntityManager, FindOperator, IsNull, LessThanOrEqual, MoreThanOrEqual } from "typeorm"
|
||||
import { generatePrivateKey, getPublicKey } from 'nostr-tools';
|
||||
import { Application } from "./entity/Application.js"
|
||||
import UserStorage from './userStorage.js';
|
||||
|
|
@ -123,6 +123,19 @@ export default class {
|
|||
return found
|
||||
}
|
||||
|
||||
async GetApplicationUsers(application: Application | null, { from, to }: { from?: number, to?: number }, entityManager = this.DB) {
|
||||
const q = application ? { app_id: application.app_id } : IsNull()
|
||||
let time: { created_at?: FindOperator<Date> } = {}
|
||||
if (!!from && !!to) {
|
||||
time.created_at = Between<Date>(new Date(from * 1000), new Date(to * 1000))
|
||||
} else if (!!from) {
|
||||
time.created_at = MoreThanOrEqual<Date>(new Date(from * 1000))
|
||||
} else if (!!to) {
|
||||
time.created_at = LessThanOrEqual<Date>(new Date(to * 1000))
|
||||
}
|
||||
return entityManager.getRepository(ApplicationUser).find({ where: { application: q, ...time } })
|
||||
}
|
||||
|
||||
async GetAppUserFromUser(application: Application, userId: string, entityManager = this.DB): Promise<ApplicationUser | null> {
|
||||
return await entityManager.getRepository(ApplicationUser).findOne({ where: { user: { user_id: userId }, application: { app_id: application.app_id } } })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
import { Application } from "./Application.js"
|
||||
|
||||
@Entity()
|
||||
export class UserInvoicePayment {
|
||||
|
|
@ -30,6 +31,9 @@ export class UserInvoicePayment {
|
|||
@Column({ default: false })
|
||||
internal: boolean
|
||||
|
||||
@ManyToOne(type => Application, { eager: true })
|
||||
linkedApplication: Application | null
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
import { Application } from "./Application.js"
|
||||
|
||||
@Entity()
|
||||
export class UserToUserPayment {
|
||||
|
|
@ -24,6 +25,9 @@ export class UserToUserPayment {
|
|||
@Column()
|
||||
paid_at_unix: number
|
||||
|
||||
@ManyToOne(type => Application, { eager: true })
|
||||
linkedApplication: Application | null
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
import { Application } from "./Application.js"
|
||||
|
||||
@Entity()
|
||||
@Index("user_transaction_unique", ["tx_hash", "output_index"], { unique: true })
|
||||
|
|
@ -42,6 +43,9 @@ export class UserTransactionPayment {
|
|||
@Column({ default: 0 })
|
||||
broadcast_height: number
|
||||
|
||||
@ManyToOne(type => Application, { eager: true })
|
||||
linkedApplication: Application | null
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import crypto from 'crypto';
|
||||
import { DataSource, EntityManager, MoreThan, MoreThanOrEqual } from "typeorm"
|
||||
import { Between, DataSource, EntityManager, FindOperator, IsNull, LessThanOrEqual, MoreThan, MoreThanOrEqual } from "typeorm"
|
||||
import { User } from './entity/User.js';
|
||||
import { UserTransactionPayment } from './entity/UserTransactionPayment.js';
|
||||
import { EphemeralKeyType, UserEphemeralKey } from './entity/UserEphemeralKey.js';
|
||||
|
|
@ -48,7 +48,9 @@ export default class {
|
|||
})
|
||||
}
|
||||
|
||||
|
||||
async GetExistingUserAddress(userId: string, linkedApplication: Application, entityManager = this.DB) {
|
||||
return entityManager.getRepository(UserReceivingAddress).findOne({ where: { user: { user_id: userId }, linkedApplication: { app_id: linkedApplication.app_id } } })
|
||||
}
|
||||
|
||||
async AddUserAddress(userId: string, address: string, opts: { callbackUrl?: string, linkedApplication?: Application } = {}, entityManager = this.DB): Promise<UserReceivingAddress> {
|
||||
const newUserAddress = entityManager.getRepository(UserReceivingAddress).create({
|
||||
|
|
@ -113,7 +115,7 @@ export default class {
|
|||
})
|
||||
}
|
||||
|
||||
async AddUserInvoicePayment(userId: string, invoice: string, amount: number, routingFees: number, serviceFees: number, internal: boolean, entityManager = this.DB): Promise<UserInvoicePayment> {
|
||||
async AddUserInvoicePayment(userId: string, invoice: string, amount: number, routingFees: number, serviceFees: number, internal: boolean, linkedApplication: Application, entityManager = this.DB): Promise<UserInvoicePayment> {
|
||||
const newPayment = entityManager.getRepository(UserInvoicePayment).create({
|
||||
user: await this.userStorage.GetUser(userId),
|
||||
paid_amount: amount,
|
||||
|
|
@ -121,7 +123,8 @@ export default class {
|
|||
routing_fees: routingFees,
|
||||
service_fees: serviceFees,
|
||||
paid_at_unix: Math.floor(Date.now() / 1000),
|
||||
internal
|
||||
internal,
|
||||
linkedApplication
|
||||
})
|
||||
return entityManager.getRepository(UserInvoicePayment).save(newPayment)
|
||||
}
|
||||
|
|
@ -141,7 +144,7 @@ export default class {
|
|||
})
|
||||
}
|
||||
|
||||
async AddUserTransactionPayment(userId: string, address: string, txHash: string, txOutput: number, amount: number, chainFees: number, serviceFees: number, internal: boolean, height: number, entityManager = this.DB): Promise<UserTransactionPayment> {
|
||||
async AddUserTransactionPayment(userId: string, address: string, txHash: string, txOutput: number, amount: number, chainFees: number, serviceFees: number, internal: boolean, height: number, linkedApplication: Application, entityManager = this.DB): Promise<UserTransactionPayment> {
|
||||
const newTx = entityManager.getRepository(UserTransactionPayment).create({
|
||||
user: await this.userStorage.GetUser(userId),
|
||||
address,
|
||||
|
|
@ -153,7 +156,8 @@ export default class {
|
|||
paid_at_unix: Math.floor(Date.now() / 1000),
|
||||
internal,
|
||||
broadcast_height: height,
|
||||
confs: internal ? 10 : 0
|
||||
confs: internal ? 10 : 0,
|
||||
linkedApplication
|
||||
})
|
||||
return entityManager.getRepository(UserTransactionPayment).save(newTx)
|
||||
}
|
||||
|
|
@ -217,13 +221,14 @@ export default class {
|
|||
return found
|
||||
}
|
||||
|
||||
async AddUserToUserPayment(fromUserId: string, toUserId: string, amount: number, fee: number, entityManager = this.DB) {
|
||||
async AddUserToUserPayment(fromUserId: string, toUserId: string, amount: number, fee: number, linkedApplication: Application, entityManager = this.DB) {
|
||||
const newKey = entityManager.getRepository(UserToUserPayment).create({
|
||||
from_user: await this.userStorage.GetUser(fromUserId, entityManager),
|
||||
to_user: await this.userStorage.GetUser(toUserId, entityManager),
|
||||
paid_at_unix: Math.floor(Date.now() / 1000),
|
||||
paid_amount: amount,
|
||||
service_fees: fee
|
||||
service_fees: fee,
|
||||
linkedApplication
|
||||
})
|
||||
return entityManager.getRepository(UserToUserPayment).save(newKey)
|
||||
}
|
||||
|
|
@ -258,4 +263,30 @@ export default class {
|
|||
})
|
||||
}
|
||||
|
||||
async GetAppOperations(application: Application | null, { from, to }: { from?: number, to?: number }, entityManager = this.DB) {
|
||||
const q = application ? { app_id: application.app_id } : IsNull()
|
||||
let time: { created_at?: FindOperator<Date> } = {}
|
||||
if (!!from && !!to) {
|
||||
time.created_at = Between<Date>(new Date(from * 1000), new Date(to * 1000))
|
||||
} else if (!!from) {
|
||||
time.created_at = MoreThanOrEqual<Date>(new Date(from * 1000))
|
||||
} else if (!!to) {
|
||||
time.created_at = LessThanOrEqual<Date>(new Date(to * 1000))
|
||||
}
|
||||
|
||||
const [receivingInvoices, receivingAddresses, outgoingInvoices, outgoingTransactions, userToUser] = await Promise.all([
|
||||
entityManager.getRepository(UserReceivingInvoice).find({ where: { linkedApplication: q, ...time } }),
|
||||
entityManager.getRepository(UserReceivingAddress).find({ where: { linkedApplication: q, ...time } }),
|
||||
entityManager.getRepository(UserInvoicePayment).find({ where: { linkedApplication: q, ...time } }),
|
||||
entityManager.getRepository(UserTransactionPayment).find({ where: { linkedApplication: q, ...time } }),
|
||||
entityManager.getRepository(UserToUserPayment).find({ where: { linkedApplication: q, ...time } })
|
||||
])
|
||||
const receivingTransactions = await Promise.all(receivingAddresses.map(addr => entityManager.getRepository(AddressReceivingTransaction).find({ where: { user_address: { serial_id: addr.serial_id } } })))
|
||||
return {
|
||||
receivingInvoices, receivingAddresses, receivingTransactions,
|
||||
outgoingInvoices, outgoingTransactions,
|
||||
userToUser
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue