Skip to content

Commit 9ad483c

Browse files
authored
Merge pull request #31 from windingtree/feat/deals-db
Deals database class
2 parents 8e6cea9 + 7a739dc commit 9ad483c

18 files changed

+550
-765
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@
112112
"@trpc/client": "^10.32.0",
113113
"jsonwebtoken": "^9.0.0",
114114
"zod": "^3.21.4",
115-
"bcryptjs": "^2.4.3"
115+
"bcryptjs": "^2.4.3",
116+
"superjson": "^1.12.4"
116117
},
117118
"scripts": {
118119
"clean": "rm -rf ./lib",

src/client/dealsManager.ts

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
GenericOfferOptions,
1515
GenericQuery,
1616
OfferData,
17+
DealRecord,
1718
} from '../shared/types.js';
1819
import {
1920
DealStatus,
@@ -26,31 +27,6 @@ import { parseSeconds } from '../utils/time.js';
2627

2728
const logger = createLogger('ClientDealsManager');
2829

29-
/**
30-
* Deals registry record
31-
*/
32-
export interface DealRecord<
33-
CustomRequestQuery extends GenericQuery = GenericQuery,
34-
CustomOfferOptions extends GenericOfferOptions = GenericOfferOptions,
35-
> {
36-
/** Network chain Id */
37-
chainId: number;
38-
/** Deal creation time in seconds */
39-
created: bigint;
40-
/** Offer */
41-
offer: OfferData<CustomRequestQuery, CustomOfferOptions>;
42-
/** Deal retailer Id */
43-
retailerId: string;
44-
/** Deal owner */
45-
buyer: Address;
46-
/** Deal price */
47-
price: bigint;
48-
/** Deal asset */
49-
asset: Address;
50-
/** Current deal status */
51-
status: DealStatus;
52-
}
53-
5430
export interface DealCurrentStatus {
5531
offerId: Hash;
5632
status: DealStatus;

src/node/api/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export * from './db.js';
1+
export * from '../db/users.js';
22
export * from './server.js';
33
export * from './router/admin.js';
44
export * from './router/user.js';

src/node/api/router/admin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { TRPCError } from '@trpc/server';
22
import { Address, Hash, verifyTypedData } from 'viem';
33
import { TypedDataDomain } from 'abitype';
44
import { Account } from '../../../index.js';
5-
import { User, UserInputSchema } from '../db.js';
5+
import { User, UserInputSchema } from '../../db/users.js';
66
import { router, procedure } from '../index.js';
77
import { createLogger } from '../../../utils/logger.js';
88

src/node/api/router/user.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { TRPCError } from '@trpc/server';
2-
import { User, UserInputSchema, comparePassword } from '../db.js';
2+
import { User, UserInputSchema, comparePassword } from '../../db/users.js';
33
import {
44
APIContext,
55
router,

src/node/api/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import * as jwt from 'jsonwebtoken';
1010
import { Address } from 'viem';
1111
import { Storage } from '../../storage/index.js';
12-
import { User, UsersDb } from './db.js';
12+
import { User, UsersDb } from '../db/users.js';
1313
import { createLogger } from '../../utils/logger.js';
1414

1515
const logger = createLogger('NodeApiServer');

src/node/db/deals.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { Hash } from 'viem';
2+
import { Storage } from '../../storage/index.js';
3+
import { DealRecord, PaginationOptions } from '../../shared/types.js';
4+
import { createLogger } from '../../utils/logger.js';
5+
6+
const logger = createLogger('DealsDb');
7+
8+
/**
9+
* Interface defining the properties of a Deal.
10+
*/
11+
// export interface Deal {}
12+
13+
/**
14+
* Interface defining the properties of DealsDb initialization options.
15+
*/
16+
export interface DealsDbOptions {
17+
/** Instance of storage used for persisting the state of the API server */
18+
storage: Storage;
19+
/** Prefix used for the storage key to avoid potential key collisions */
20+
prefix: string;
21+
}
22+
23+
/**
24+
* Class that implements an API to the deals records storage
25+
*
26+
* @export
27+
* @class UsersDb
28+
*/
29+
export class DealsDb {
30+
/** Storage instance for persisting the state of the API server */
31+
storage: Storage;
32+
/** Specific key prefix for the storage key to avoid potential key collisions */
33+
prefix: string;
34+
35+
/**
36+
* Creates an instance of DealsDb.
37+
* Initializes an instance of DealsDb with given options.
38+
*
39+
* @param {UsersDbOptions} options
40+
* @memberof DealsDb
41+
*/
42+
constructor(options: DealsDbOptions) {
43+
const { storage, prefix } = options;
44+
45+
// TODO Validate DealsDbOptions
46+
47+
this.prefix = `${prefix}_api_deals_`;
48+
this.storage = storage;
49+
}
50+
51+
/**
52+
* Adds/Updates the record of the deal in the storage
53+
*
54+
* @param {DealRecord} deal The deal object
55+
* @returns {Promise<void>}
56+
* @memberof UsersDb
57+
*/
58+
async set(deal: DealRecord): Promise<void> {
59+
await this.storage.set<DealRecord>(`${this.prefix}${deal.offer.id}`, deal);
60+
}
61+
62+
/**
63+
* Retrieves the deal from storage.
64+
*
65+
* @param {Hash} offerId The Id (offerId) of the deal to be retrieved
66+
* @returns {Promise<DealRecord>} The deal object associated with the given Id
67+
* @throws Will throw an error if the user is not found
68+
* @memberof UsersDb
69+
*/
70+
async get(offerId: Hash): Promise<DealRecord> {
71+
const deal = await this.storage.get<DealRecord>(`${this.prefix}${offerId}`);
72+
73+
if (!deal) {
74+
throw new Error(`Deal ${offerId} not found`);
75+
}
76+
77+
return deal;
78+
}
79+
80+
/**
81+
* Retrieves all the deals from storage.
82+
*
83+
* @param {PaginationOptions} [pagination] Pagination options
84+
* @returns {Promise<DealRecord[]>} Deals records
85+
* @memberof DealsDb
86+
*/
87+
async getAll(pagination?: PaginationOptions): Promise<DealRecord[]> {
88+
return new Promise((resolve, reject) => {
89+
try {
90+
pagination = pagination ?? {
91+
start: 0,
92+
skip: 10,
93+
};
94+
let cursor = 0;
95+
const from = pagination.start >= 0 ? pagination.start : 0;
96+
const to = from + pagination.skip ?? 0;
97+
const records: DealRecord[] = [];
98+
99+
for (const record of this.storage.entries<DealRecord>()) {
100+
if (to > 0 && cursor >= from && cursor < to) {
101+
records.push(record[1]);
102+
}
103+
104+
cursor++;
105+
}
106+
107+
resolve(records);
108+
} catch (error) {
109+
logger.error('getAll', error);
110+
reject(error);
111+
}
112+
});
113+
}
114+
}

src/node/api/db.ts renamed to src/node/db/users.ts

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ export interface UsersDbOptions {
4343
prefix: string;
4444
}
4545

46+
/**
47+
* Class that implements an API to the users records storage
48+
*
49+
* @export
50+
* @class UsersDb
51+
*/
4652
export class UsersDb {
4753
/** Storage instance for persisting the state of the API server */
4854
storage: Storage;
@@ -54,12 +60,12 @@ export class UsersDb {
5460
* Initializes an instance of UsersDb with given options.
5561
*
5662
* @param {UsersDbOptions} options
57-
* @memberof NodeApiServer
63+
* @memberof UsersDb
5864
*/
5965
constructor(options: UsersDbOptions) {
6066
const { storage, prefix } = options;
6167

62-
// TODO Validate NodeApiServerOptions
68+
// TODO Validate UsersDbOptions
6369

6470
this.prefix = `${prefix}_api_users_`;
6571
this.storage = storage;
@@ -77,18 +83,6 @@ export class UsersDb {
7783
return await hash(password, 10);
7884
}
7985

80-
/**
81-
* Generates a prefixed login key
82-
*
83-
* @private
84-
* @param {string} login The login for which the key is generated
85-
* @returns {string} The prefixed login key
86-
* @memberof UsersDb
87-
*/
88-
private loginKey(login: string): string {
89-
return `${this.prefix}${login}`;
90-
}
91-
9286
/**
9387
* Retrieves the user with the given login from storage.
9488
*
@@ -98,7 +92,7 @@ export class UsersDb {
9892
* @memberof UsersDb
9993
*/
10094
async get(login: string): Promise<User> {
101-
const user = await this.storage.get<User>(this.loginKey(login));
95+
const user = await this.storage.get<User>(`${this.prefix}${login}`);
10296

10397
if (!user) {
10498
throw new Error(`User ${login} not found`);
@@ -118,15 +112,15 @@ export class UsersDb {
118112
* @memberof UsersDb
119113
*/
120114
async add(login: string, password: string, isAdmin = false): Promise<void> {
121-
const knownUser = await this.storage.get<User>(this.loginKey(login));
115+
const knownUser = await this.storage.get<User>(`${this.prefix}${login}`);
122116

123117
// Check if the user already exists
124118
if (knownUser) {
125119
throw new Error(`User ${login} already exists`);
126120
}
127121

128122
// Save the user into the storage
129-
await this.storage.set<User>(this.loginKey(login), {
123+
await this.storage.set<User>(`${this.prefix}${login}`, {
130124
login,
131125
hashedPassword: await UsersDb.hashPassword(password),
132126
isAdmin,
@@ -141,7 +135,7 @@ export class UsersDb {
141135
* @memberof UsersDb
142136
*/
143137
async set(user: User): Promise<void> {
144-
await this.storage.set<User>(this.loginKey(user.login), user);
138+
await this.storage.set<User>(`${this.prefix}${user.login}`, user);
145139
}
146140

147141
/**
@@ -153,7 +147,7 @@ export class UsersDb {
153147
* @memberof UsersDb
154148
*/
155149
async delete(login: string): Promise<void> {
156-
const deleted = await this.storage.delete(this.loginKey(login));
150+
const deleted = await this.storage.delete(`${this.prefix}${login}`);
157151

158152
if (!deleted) {
159153
throw new Error(`Unable to delete user ${login}`);

0 commit comments

Comments
 (0)