Added auth and request storage
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import type { AuthSecp256k1 } from '../auth/auth.ts';
|
||||
import type { Database } from "../services/storage/database.ts";
|
||||
import type { RouteDefinition, RouteStream } from './types.ts';
|
||||
import type { Accounts } from '../auth/accounts.ts';
|
||||
|
||||
import { HTTP_STATUS_CODE_NOT_IMPLEMENTED, HTTP_STATUS_CODE_SUCCESS } from "../constants.ts";
|
||||
import { UnauthorizedError } from '../errors/index.ts';
|
||||
|
||||
const accountSchema = z.object({
|
||||
publicKey: z.string(),
|
||||
signature: z.string(),
|
||||
timestamp: z.coerce.number(),
|
||||
});
|
||||
|
||||
const setBalanceSchema = z.object({
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
export class AccountRoute {
|
||||
|
||||
constructor(private readonly database: Database, private readonly auth: AuthSecp256k1, private readonly accounts: Accounts) {}
|
||||
|
||||
async getRoutes(): Promise<Array<RouteDefinition>> {
|
||||
return [
|
||||
{
|
||||
url: '/account',
|
||||
handler: this.getAccount.bind(this),
|
||||
},
|
||||
{
|
||||
url: '/account/balance',
|
||||
handler: this.getAccount.bind(this),
|
||||
},
|
||||
{
|
||||
url: '/account/setbalance',
|
||||
handler: this.setBalance.bind(this),
|
||||
},
|
||||
// This one may not make sense. It could be a large overhead for something that most wont use?
|
||||
// Maybe make this an optional endpoint or one that we don't support, but the client can try to hit or something?
|
||||
{
|
||||
url: '/account/ledger',
|
||||
handler: this.getLedger.bind(this),
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async getAccount(stream: RouteStream): Promise<void> {
|
||||
// Get the public key, signature and timestamp from the headers
|
||||
const { publicKey, signature, timestamp } = accountSchema.parse(stream.headers);
|
||||
|
||||
// Create the canonical payload for the signature verification
|
||||
const payload = `${stream.path}:${timestamp}`;
|
||||
|
||||
// Verify the signature
|
||||
const verified = await this.auth.verifySignature(publicKey, signature, payload);
|
||||
|
||||
if (!verified) {
|
||||
throw new UnauthorizedError('Invalid signature');
|
||||
}
|
||||
|
||||
// Get the account's balance from the database
|
||||
const balance = await this.accounts.getBalance(publicKey);
|
||||
|
||||
// Send the balance to the client
|
||||
stream.send({
|
||||
statusCode: HTTP_STATUS_CODE_SUCCESS,
|
||||
body: balance,
|
||||
});
|
||||
}
|
||||
|
||||
async setBalance(stream: RouteStream): Promise<void> {
|
||||
const { publicKey, signature, timestamp } = accountSchema.parse(stream.headers);
|
||||
const { amount } = setBalanceSchema.parse(stream.body);
|
||||
|
||||
// Create the canonical payload for the signature verification
|
||||
const payload = `${stream.path}:${timestamp}`;
|
||||
|
||||
// Verify the signature
|
||||
const verified = await this.auth.verifySignature(publicKey, signature, payload);
|
||||
|
||||
if (!verified) {
|
||||
throw new UnauthorizedError('Invalid signature');
|
||||
}
|
||||
|
||||
// Get the account's balance from the database
|
||||
const balance = await this.accounts.getBalance(publicKey);
|
||||
|
||||
// Set the balance to the database
|
||||
await this.accounts.setBalance(publicKey, amount);
|
||||
|
||||
// Send the balance to the client
|
||||
stream.send({
|
||||
statusCode: HTTP_STATUS_CODE_SUCCESS,
|
||||
body: balance,
|
||||
});
|
||||
}
|
||||
|
||||
async getLedger(stream: RouteStream): Promise<void> {
|
||||
stream.send({
|
||||
statusCode: HTTP_STATUS_CODE_NOT_IMPLEMENTED,
|
||||
body: 'Not implemented',
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user