commit
46819446d5
18 changed files with 1752 additions and 1263 deletions
|
|
@ -18,6 +18,18 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- This methods has an __empty__ __request__ body
|
||||
- output: [UserInfo](#UserInfo)
|
||||
|
||||
- AddProduct
|
||||
- auth type: __User__
|
||||
- input: [AddProductRequest](#AddProductRequest)
|
||||
- output: [Product](#Product)
|
||||
|
||||
- NewProductInvoice
|
||||
- auth type: __User__
|
||||
- the request url __query__ can take the following string items:
|
||||
- id
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [NewInvoiceResponse](#NewInvoiceResponse)
|
||||
|
||||
- GetUserOperations
|
||||
- auth type: __User__
|
||||
- input: [GetUserOperationsRequest](#GetUserOperationsRequest)
|
||||
|
|
@ -126,6 +138,22 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- This methods has an __empty__ __request__ body
|
||||
- output: [UserInfo](#UserInfo)
|
||||
|
||||
- AddProduct
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/product/add__
|
||||
- input: [AddProductRequest](#AddProductRequest)
|
||||
- output: [Product](#Product)
|
||||
|
||||
- NewProductInvoice
|
||||
- auth type: __User__
|
||||
- http method: __get__
|
||||
- http route: __/api/user/product/get/invoice__
|
||||
- the request url __query__ can take the following string items:
|
||||
- id
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [NewInvoiceResponse](#NewInvoiceResponse)
|
||||
|
||||
- GetUserOperations
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
|
|
@ -232,15 +260,78 @@ 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
|
||||
|
||||
### OpenChannelResponse
|
||||
- __channelId__: _string_
|
||||
|
||||
### UserInfo
|
||||
- __userId__: _string_
|
||||
- __balance__: _number_
|
||||
|
||||
### GetUserOperationsRequest
|
||||
- __latestIncomingInvoice__: _number_
|
||||
- __latestOutgoingInvoice__: _number_
|
||||
- __latestIncomingTx__: _number_
|
||||
- __latestOutgoingTx__: _number_
|
||||
|
||||
### AddProductRequest
|
||||
- __name__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### Product
|
||||
- __id__: _string_
|
||||
- __name__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### EncryptionExchangeRequest
|
||||
- __publicKey__: _string_
|
||||
- __deviceId__: _string_
|
||||
|
||||
### NewInvoiceRequest
|
||||
- __amountSats__: _number_
|
||||
- __memo__: _string_
|
||||
|
||||
### PayInvoiceResponse
|
||||
- __preimage__: _string_
|
||||
|
||||
### HandleLnurlPayResponse
|
||||
- __pr__: _string_
|
||||
- __routes__: ARRAY of: _[Empty](#Empty)_
|
||||
|
||||
### AuthUserRequest
|
||||
- __name__: _string_
|
||||
- __secret__: _string_
|
||||
|
||||
### UserOperations
|
||||
- __fromIndex__: _number_
|
||||
- __toIndex__: _number_
|
||||
- __operations__: ARRAY of: _[UserOperation](#UserOperation)_
|
||||
|
||||
### PayAddressRequest
|
||||
- __address__: _string_
|
||||
- __amoutSats__: _number_
|
||||
- __targetConf__: _number_
|
||||
|
||||
### OpenChannelRequest
|
||||
- __destination__: _string_
|
||||
- __fundingAmount__: _number_
|
||||
- __pushAmount__: _number_
|
||||
- __closeAddress__: _string_
|
||||
|
||||
### LnurlLinkResponse
|
||||
- __lnurl__: _string_
|
||||
- __k1__: _string_
|
||||
|
||||
### Empty
|
||||
|
||||
### NewInvoiceResponse
|
||||
- __invoice__: _string_
|
||||
|
||||
### DecodeInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
|
||||
### AddUserRequest
|
||||
- __callbackUrl__: _string_
|
||||
- __name__: _string_
|
||||
- __secret__: _string_
|
||||
### AuthUserResponse
|
||||
- __userId__: _string_
|
||||
- __authToken__: _string_
|
||||
|
||||
### GetUserOperationsResponse
|
||||
- __latestOutgoingInvoiceOperations__: _[UserOperations](#UserOperations)_
|
||||
|
|
@ -248,8 +339,15 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __latestOutgoingTxOperations__: _[UserOperations](#UserOperations)_
|
||||
- __latestIncomingTxOperations__: _[UserOperations](#UserOperations)_
|
||||
|
||||
### PayAddressResponse
|
||||
- __txId__: _string_
|
||||
### LndGetInfoRequest
|
||||
- __nodeId__: _number_
|
||||
|
||||
### LndGetInfoResponse
|
||||
- __alias__: _string_
|
||||
|
||||
### AddUserResponse
|
||||
- __userId__: _string_
|
||||
- __authToken__: _string_
|
||||
|
||||
### LnurlWithdrawInfoResponse
|
||||
- __tag__: _string_
|
||||
|
|
@ -261,21 +359,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __balanceCheck__: _string_
|
||||
- __payLink__: _string_
|
||||
|
||||
### UserInfo
|
||||
- __userId__: _string_
|
||||
- __balance__: _number_
|
||||
### GetProductBuyLinkResponse
|
||||
- __link__: _string_
|
||||
|
||||
### NewAddressResponse
|
||||
- __address__: _string_
|
||||
|
||||
### PayAddressRequest
|
||||
- __address__: _string_
|
||||
- __amoutSats__: _number_
|
||||
- __targetConf__: _number_
|
||||
|
||||
### HandleLnurlPayResponse
|
||||
- __pr__: _string_
|
||||
- __routes__: ARRAY of: _[Empty](#Empty)_
|
||||
### AddUserRequest
|
||||
- __callbackUrl__: _string_
|
||||
- __name__: _string_
|
||||
- __secret__: _string_
|
||||
|
||||
### UserOperation
|
||||
- __paidAtUnix__: _number_
|
||||
|
|
@ -283,25 +373,21 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __inbound__: _boolean_
|
||||
- __amount__: _number_
|
||||
|
||||
### DecodeInvoiceResponse
|
||||
- __amount__: _number_
|
||||
|
||||
### AddUserResponse
|
||||
- __userId__: _string_
|
||||
- __authToken__: _string_
|
||||
|
||||
### NewInvoiceResponse
|
||||
- __invoice__: _string_
|
||||
|
||||
### PayInvoiceResponse
|
||||
- __preimage__: _string_
|
||||
|
||||
### NewAddressRequest
|
||||
- __addressType__: _[AddressType](#AddressType)_
|
||||
|
||||
### LnurlLinkResponse
|
||||
- __lnurl__: _string_
|
||||
- __k1__: _string_
|
||||
### NewAddressResponse
|
||||
- __address__: _string_
|
||||
|
||||
### PayInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### PayAddressResponse
|
||||
- __txId__: _string_
|
||||
|
||||
### DecodeInvoiceResponse
|
||||
- __amount__: _number_
|
||||
|
||||
### LnurlPayInfoResponse
|
||||
- __tag__: _string_
|
||||
|
|
@ -309,52 +395,6 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __maxSendable__: _number_
|
||||
- __minSendable__: _number_
|
||||
- __metadata__: _string_
|
||||
|
||||
### GetUserOperationsRequest
|
||||
- __latestIncomingInvoice__: _number_
|
||||
- __latestOutgoingInvoice__: _number_
|
||||
- __latestIncomingTx__: _number_
|
||||
- __latestOutgoingTx__: _number_
|
||||
|
||||
### AuthUserResponse
|
||||
- __userId__: _string_
|
||||
- __authToken__: _string_
|
||||
|
||||
### EncryptionExchangeRequest
|
||||
- __publicKey__: _string_
|
||||
- __deviceId__: _string_
|
||||
|
||||
### LndGetInfoRequest
|
||||
- __nodeId__: _number_
|
||||
|
||||
### LndGetInfoResponse
|
||||
- __alias__: _string_
|
||||
|
||||
### PayInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
- __amount__: _number_
|
||||
|
||||
### OpenChannelRequest
|
||||
- __destination__: _string_
|
||||
- __fundingAmount__: _number_
|
||||
- __pushAmount__: _number_
|
||||
- __closeAddress__: _string_
|
||||
|
||||
### OpenChannelResponse
|
||||
- __channelId__: _string_
|
||||
|
||||
### AuthUserRequest
|
||||
- __name__: _string_
|
||||
- __secret__: _string_
|
||||
|
||||
### UserOperations
|
||||
- __fromIndex__: _number_
|
||||
- __toIndex__: _number_
|
||||
- __operations__: ARRAY of: _[UserOperation](#UserOperation)_
|
||||
|
||||
### NewInvoiceRequest
|
||||
- __amountSats__: _number_
|
||||
- __memo__: _string_
|
||||
## Enums
|
||||
### The enumerators used in the messages
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -105,6 +105,31 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.AddProduct) throw new Error('method: AddProduct is not implemented')
|
||||
app.post('/api/user/product/add', async (req, res) => {
|
||||
try {
|
||||
if (!methods.AddProduct) throw new Error('method: AddProduct is not implemented')
|
||||
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
|
||||
const request = req.body
|
||||
const error = Types.AddProductRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddProduct({ ...authContext, ...query, ...params }, request)
|
||||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.NewProductInvoice) throw new Error('method: NewProductInvoice is not implemented')
|
||||
app.get('/api/user/product/get/invoice', async (req, res) => {
|
||||
try {
|
||||
if (!methods.NewProductInvoice) throw new Error('method: NewProductInvoice is not implemented')
|
||||
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.NewProductInvoice({ ...authContext, ...query, ...params })
|
||||
res.json({status: 'OK', ...response})
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.GetUserOperations) throw new Error('method: GetUserOperations is not implemented')
|
||||
app.post('/api/user/operations', async (req, res) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -92,6 +92,36 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddProduct: async (request: Types.AddProductRequest): Promise<ResultError | ({ status: 'OK' }& Types.Product)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/product/add'
|
||||
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.ProductValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
NewProductInvoice: async (query: Types.NewProductInvoice_Query): Promise<ResultError | ({ status: 'OK' }& Types.NewInvoiceResponse)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/product/get/invoice'
|
||||
const q = (new URLSearchParams(query)).toString()
|
||||
finalRoute = finalRoute + (q === '' ? '' : '?' + q)
|
||||
const { data } = await axios.get(params.baseUrl + finalRoute, { 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.NewInvoiceResponseValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetUserOperations: async (request: Types.GetUserOperationsRequest): Promise<ResultError | ({ status: 'OK' }& Types.GetUserOperationsResponse)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
|
|
|
|||
|
|
@ -23,6 +23,36 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddProduct: async (request: Types.AddProductRequest): Promise<ResultError | ({ status: 'OK' }& Types.Product)> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
nostrRequest.body = request
|
||||
const data = await send(params.pubDestination, {rpcName:'AddProduct',authIdentifier:auth, ...nostrRequest })
|
||||
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.ProductValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
NewProductInvoice: async (query: Types.NewProductInvoice_Query): Promise<ResultError | ({ status: 'OK' }& Types.NewInvoiceResponse)> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
nostrRequest.query = query
|
||||
const data = await send(params.pubDestination, {rpcName:'NewProductInvoice',authIdentifier:auth, ...nostrRequest })
|
||||
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.NewInvoiceResponseValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetUserOperations: async (request: Types.GetUserOperationsRequest): Promise<ResultError | ({ status: 'OK' }& Types.GetUserOperationsResponse)> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
|
|
|
|||
|
|
@ -31,6 +31,29 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
res({status: 'OK', ...response})
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'AddProduct':
|
||||
try {
|
||||
if (!methods.AddProduct) throw new Error('method: AddProduct is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.authIdentifier)
|
||||
const request = req.body
|
||||
const error = Types.AddProductRequestValidate(request)
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AddProduct({ ...authContext, ...query, ...params }, request)
|
||||
res({status: 'OK', ...response})
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'NewProductInvoice':
|
||||
try {
|
||||
if (!methods.NewProductInvoice) throw new Error('method: NewProductInvoice is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.authIdentifier)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.NewProductInvoice({ ...authContext, ...query, ...params })
|
||||
res({status: 'OK', ...response})
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'GetUserOperations':
|
||||
try {
|
||||
if (!methods.GetUserOperations) throw new Error('method: GetUserOperations is not implemented')
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -78,7 +78,6 @@ service LightningPub {
|
|||
option (http_method) = "post";
|
||||
option (http_route) = "/api/lnd/getinfo";
|
||||
};
|
||||
|
||||
rpc AddUser(structs.AddUserRequest)returns (structs.AddUserResponse){
|
||||
option (auth_type) = "Guest";
|
||||
option (http_method) = "post";
|
||||
|
|
@ -96,6 +95,19 @@ service LightningPub {
|
|||
option (nostr) = true;
|
||||
}
|
||||
// USER
|
||||
rpc AddProduct(structs.AddProductRequest) returns (structs.Product){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/product/add";
|
||||
option (nostr) = true;
|
||||
};
|
||||
rpc NewProductInvoice(structs.Empty) returns (structs.NewInvoiceResponse){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "get";
|
||||
option (http_route) = "/api/user/product/get/invoice";
|
||||
option (query) = {items: ["id"]};
|
||||
option (nostr) = true;
|
||||
};
|
||||
rpc GetUserOperations(structs.GetUserOperationsRequest) returns (structs.GetUserOperationsResponse) {
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
|
|
|
|||
|
|
@ -153,3 +153,18 @@ message GetUserOperationsResponse{
|
|||
UserOperations latestOutgoingTxOperations=3;
|
||||
UserOperations latestIncomingTxOperations=4;
|
||||
}
|
||||
|
||||
message AddProductRequest {
|
||||
string name = 1;
|
||||
int64 price_sats = 2;
|
||||
}
|
||||
|
||||
message Product {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
int64 price_sats = 3;
|
||||
}
|
||||
|
||||
message GetProductBuyLinkResponse {
|
||||
string link = 1;
|
||||
}
|
||||
|
|
@ -5,17 +5,8 @@ import * as Types from '../../../proto/autogenerated/ts/types.js'
|
|||
import LND, { AddressPaidCb, InvoicePaidCb, LndSettings, LoadLndSettingsFromEnv } from '../lnd/index.js'
|
||||
import { EnvMustBeInteger, EnvMustBeNonEmptyString } from '../helpers/envParser.js'
|
||||
import { UserReceivingInvoice } from '../storage/entity/UserReceivingInvoice.js'
|
||||
import { Type } from 'typescript'
|
||||
export type MainSettings = {
|
||||
storageSettings: StorageSettings,
|
||||
lndSettings: LndSettings,
|
||||
jwtSecret: string
|
||||
incomingTxFee: number
|
||||
outgoingTxFee: number
|
||||
incomingInvoiceFee: number
|
||||
outgoingInvoiceFee: number
|
||||
serviceUrl: string
|
||||
}
|
||||
import ProductManager from './product.js'
|
||||
import { MainSettings } from './settings.js'
|
||||
export const LoadMainSettingsFromEnv = (test = false): MainSettings => {
|
||||
return {
|
||||
lndSettings: LoadLndSettingsFromEnv(test),
|
||||
|
|
@ -47,10 +38,12 @@ export default class {
|
|||
lnd: LND
|
||||
settings: MainSettings
|
||||
userOperationsSub: UserOperationsSub | null = null
|
||||
productManager: ProductManager
|
||||
constructor(settings: MainSettings) {
|
||||
this.settings = settings
|
||||
this.storage = new Storage(settings.storageSettings)
|
||||
this.lnd = new LND(settings.lndSettings, this.addressPaidCb, this.invoicePaidCb)
|
||||
this.productManager = new ProductManager(this.storage, this.lnd, this.settings)
|
||||
}
|
||||
getServiceFee(action: Types.UserOperationType, amount: number): number {
|
||||
switch (action) {
|
||||
|
|
@ -119,8 +112,9 @@ export default class {
|
|||
}
|
||||
|
||||
async NewInvoice(userId: string, req: Types.NewInvoiceRequest): Promise<Types.NewInvoiceResponse> {
|
||||
const user = await this.storage.GetUser(userId)
|
||||
const res = await this.lnd.NewInvoice(req.amountSats, req.memo)
|
||||
const userInvoice = await this.storage.AddUserInvoice(userId, res.paymentRequest)
|
||||
const userInvoice = await this.storage.AddUserInvoice(user, res.paymentRequest)
|
||||
return {
|
||||
invoice: userInvoice.invoice
|
||||
}
|
||||
|
|
|
|||
36
src/services/main/product.ts
Normal file
36
src/services/main/product.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import Storage from '../storage/index.js'
|
||||
import * as Types from '../../../proto/autogenerated/ts/types.js'
|
||||
import LND from '../lnd/index.js'
|
||||
|
||||
import { MainSettings } from './settings.js'
|
||||
|
||||
export default class {
|
||||
|
||||
storage: Storage
|
||||
settings: MainSettings
|
||||
lnd: LND
|
||||
constructor(storage: Storage, lnd: LND, settings: MainSettings) {
|
||||
this.storage = storage
|
||||
this.settings = settings
|
||||
this.lnd = lnd
|
||||
}
|
||||
|
||||
async AddProduct(userId: string, req: Types.AddProductRequest): Promise<Types.Product> {
|
||||
const user = await this.storage.GetUser(userId)
|
||||
const newProduct = await this.storage.productStorage.AddProduct(req.name, req.price_sats, user)
|
||||
return {
|
||||
id: newProduct.product_id,
|
||||
name: newProduct.name,
|
||||
price_sats: newProduct.price_sats,
|
||||
}
|
||||
}
|
||||
|
||||
async NewProductInvoice(id: string): Promise<Types.NewInvoiceResponse> {
|
||||
const product = await this.storage.productStorage.GetProduct(id)
|
||||
const newInvoice = await this.lnd.NewInvoice(product.price_sats, product.name)
|
||||
await this.storage.AddUserInvoice(product.owner, newInvoice.paymentRequest, product)
|
||||
return {
|
||||
invoice: newInvoice.paymentRequest
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/services/main/settings.ts
Normal file
12
src/services/main/settings.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { StorageSettings } from '../storage/index.js'
|
||||
import { LndSettings } from '../lnd/index.js'
|
||||
export type MainSettings = {
|
||||
storageSettings: StorageSettings,
|
||||
lndSettings: LndSettings,
|
||||
jwtSecret: string
|
||||
incomingTxFee: number
|
||||
outgoingTxFee: number
|
||||
incomingInvoiceFee: number
|
||||
outgoingInvoiceFee: number
|
||||
serviceUrl: string
|
||||
}
|
||||
|
|
@ -90,8 +90,17 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
}
|
||||
return mainHandler.HandleLnurlPay(ctx.k1, +ctx.amount)
|
||||
},
|
||||
AddProduct: async (ctx, req) => {
|
||||
return mainHandler.productManager.AddProduct(ctx.user_id, req)
|
||||
},
|
||||
NewProductInvoice: async (ctx) => {
|
||||
if (!ctx.id) {
|
||||
throw new Error("product id must be non empty")
|
||||
}
|
||||
return mainHandler.productManager.NewProductInvoice(ctx.id)
|
||||
},
|
||||
GetLNURLChannelLink: async (ctx) => {
|
||||
throw new Error("unimplemented")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import { UserTransactionPayment } from "./entity/UserTransactionPayment.js"
|
|||
import { UserNostrAuth } from "./entity/UserNostrAuth.js"
|
||||
import { UserBasicAuth } from "./entity/UserBasicAuth.js"
|
||||
import { UserEphemeralKey } from "./entity/UserEphemeralKey.js"
|
||||
import { Product } from "./entity/Product.js"
|
||||
export type DbSettings = {
|
||||
databaseFile: string
|
||||
}
|
||||
|
|
@ -21,7 +22,7 @@ export default async (settings: DbSettings) => {
|
|||
type: "sqlite",
|
||||
database: settings.databaseFile,
|
||||
//logging: true,
|
||||
entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment, UserNostrAuth, UserBasicAuth, UserEphemeralKey],
|
||||
entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment, UserNostrAuth, UserBasicAuth, UserEphemeralKey, Product],
|
||||
synchronize: true
|
||||
}).initialize()
|
||||
}
|
||||
18
src/services/storage/entity/Product.ts
Normal file
18
src/services/storage/entity/Product.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
export class Product {
|
||||
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
product_id: string
|
||||
|
||||
@ManyToOne(type => User, { eager: true })
|
||||
owner: User
|
||||
|
||||
@Column()
|
||||
name: string
|
||||
|
||||
@Column()
|
||||
price_sats: number
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn } from "typeorm"
|
||||
import { Product } from "./Product.js"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
|
|
@ -26,4 +27,7 @@ export class UserReceivingInvoice {
|
|||
|
||||
@Column({ default: 0 })
|
||||
service_fee: number
|
||||
|
||||
@ManyToOne(type => Product, { eager: true })
|
||||
product: Product | null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import { UserTransactionPayment } from "./entity/UserTransactionPayment.js";
|
|||
import { UserNostrAuth } from "./entity/UserNostrAuth.js";
|
||||
import { UserBasicAuth } from "./entity/UserBasicAuth.js";
|
||||
import { EphemeralKeyType, UserEphemeralKey } from "./entity/UserEphemeralKey.js";
|
||||
import ProductStorage from './productStorage.js'
|
||||
import { Product } from "./entity/Product.js";
|
||||
export type StorageSettings = {
|
||||
dbSettings: DbSettings
|
||||
}
|
||||
|
|
@ -20,11 +22,13 @@ export const LoadStorageSettingsFromEnv = (test = false): StorageSettings => {
|
|||
export default class {
|
||||
DB: DataSource | EntityManager
|
||||
settings: StorageSettings
|
||||
productStorage: ProductStorage
|
||||
constructor(settings: StorageSettings) {
|
||||
this.settings = settings
|
||||
}
|
||||
async Connect() {
|
||||
this.DB = await NewDB(this.settings.dbSettings)
|
||||
this.productStorage = new ProductStorage(this.DB)
|
||||
}
|
||||
StartTransaction(exec: (entityManager: EntityManager) => Promise<void>) {
|
||||
return this.DB.transaction(exec)
|
||||
|
|
@ -134,11 +138,12 @@ export default class {
|
|||
})
|
||||
}
|
||||
|
||||
async AddUserInvoice(userId: string, invoice: string, callbackUrl = "", entityManager = this.DB): Promise<UserReceivingInvoice> {
|
||||
async AddUserInvoice(user: User, invoice: string, product?: Product, callbackUrl = "", entityManager = this.DB): Promise<UserReceivingInvoice> {
|
||||
const newUserInvoice = entityManager.getRepository(UserReceivingInvoice).create({
|
||||
invoice: invoice,
|
||||
callbackUrl,
|
||||
user: await this.GetUser(userId, entityManager)
|
||||
user: user,
|
||||
product: product
|
||||
})
|
||||
return entityManager.getRepository(UserReceivingInvoice).save(newUserInvoice)
|
||||
}
|
||||
|
|
|
|||
23
src/services/storage/productStorage.ts
Normal file
23
src/services/storage/productStorage.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { DataSource, EntityManager } from "typeorm"
|
||||
import { Product } from "./entity/Product.js"
|
||||
import { User } from "./entity/User.js"
|
||||
export default class {
|
||||
DB: DataSource | EntityManager
|
||||
constructor(DB: DataSource | EntityManager) {
|
||||
this.DB = DB
|
||||
}
|
||||
async AddProduct(name: string, priceSats: number, user: User, entityManager = this.DB): Promise<Product> {
|
||||
const newProduct = entityManager.getRepository(Product).create({
|
||||
name: name, price_sats: priceSats, owner: user
|
||||
})
|
||||
return entityManager.getRepository(Product).save(newProduct)
|
||||
}
|
||||
|
||||
async GetProduct(id: string, entityManager = this.DB): Promise<Product> {
|
||||
const product = await entityManager.getRepository(Product).findOne({ where: { product_id: id } })
|
||||
if (!product) {
|
||||
throw new Error("product not found")
|
||||
}
|
||||
return product
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue