updates types for orbit and bug fixes, and sanitiza data on document prepare
This commit is contained in:
parent
d39b3c7003
commit
44d19ad628
@ -1,126 +0,0 @@
|
||||
// Example of how to integrate the new @debros/node-core package into an application
|
||||
|
||||
import express from 'express';
|
||||
import { initIpfs, initOrbitDB, createServiceLogger, getConnectedPeers } from '@debros/node-core'; // This would be the import path when installed from npm
|
||||
|
||||
// Create service-specific loggers
|
||||
const apiLogger = createServiceLogger('API');
|
||||
const networkLogger = createServiceLogger('NETWORK');
|
||||
|
||||
// Initialize Express app
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
// Network state
|
||||
let ipfsNode: any;
|
||||
let orbitInstance: any;
|
||||
let messageDB: any;
|
||||
|
||||
// Initialize network components
|
||||
async function initializeNetwork() {
|
||||
try {
|
||||
// Initialize IPFS
|
||||
networkLogger.info('Initializing IPFS node...');
|
||||
ipfsNode = await initIpfs();
|
||||
|
||||
// Initialize OrbitDB
|
||||
networkLogger.info('Initializing OrbitDB...');
|
||||
orbitInstance = await initOrbitDB({
|
||||
getHelia: () => ipfsNode,
|
||||
});
|
||||
|
||||
// Open message database
|
||||
messageDB = await orbitInstance.open('messages', {
|
||||
type: 'feed',
|
||||
});
|
||||
|
||||
networkLogger.info('Network components initialized successfully');
|
||||
|
||||
// Log connected peers every minute
|
||||
setInterval(() => {
|
||||
const peers = getConnectedPeers();
|
||||
networkLogger.info(`Connected to ${peers.size} peers`);
|
||||
}, 60000);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
networkLogger.error('Failed to initialize network:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// API routes
|
||||
app.get('/api/peers', (req, res) => {
|
||||
const peers = getConnectedPeers();
|
||||
const peerList: any[] = [];
|
||||
|
||||
peers.forEach((data, peerId) => {
|
||||
peerList.push({
|
||||
id: peerId,
|
||||
load: data.load,
|
||||
address: data.publicAddress,
|
||||
lastSeen: data.lastSeen,
|
||||
});
|
||||
});
|
||||
|
||||
res.json({ peers: peerList });
|
||||
});
|
||||
|
||||
app.get('/api/messages', async (req, res) => {
|
||||
try {
|
||||
const messages = messageDB.iterator({ limit: 100 }).collect();
|
||||
res.json({ messages });
|
||||
} catch (error) {
|
||||
apiLogger.error('Error fetching messages:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch messages' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/messages', async (req, res) => {
|
||||
try {
|
||||
const { content } = req.body;
|
||||
if (!content) {
|
||||
return res.status(400).json({ error: 'Content is required' });
|
||||
}
|
||||
|
||||
const entry = await messageDB.add({
|
||||
content,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
res.status(201).json({ id: entry });
|
||||
} catch (error) {
|
||||
apiLogger.error('Error creating message:', error);
|
||||
res.status(500).json({ error: 'Failed to create message' });
|
||||
}
|
||||
});
|
||||
|
||||
// Start the application
|
||||
async function startApp() {
|
||||
const networkInitialized = await initializeNetwork();
|
||||
|
||||
if (networkInitialized) {
|
||||
const port = config.env.port;
|
||||
app.listen(port, () => {
|
||||
apiLogger.info(`Server listening on port ${port}`);
|
||||
});
|
||||
} else {
|
||||
apiLogger.error('Cannot start application: Network initialization failed');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown handler
|
||||
process.on('SIGINT', async () => {
|
||||
networkLogger.info('Application shutting down...');
|
||||
if (orbitInstance) {
|
||||
await orbitInstance.stop();
|
||||
}
|
||||
if (ipfsNode) {
|
||||
await initIpfs.stop();
|
||||
}
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// Start the application
|
||||
startApp();
|
2
orbitdb.d.ts
vendored
2
orbitdb.d.ts
vendored
@ -1,7 +1,7 @@
|
||||
// custom.d.ts
|
||||
declare module '@orbitdb/core' {
|
||||
// Import the types from @constl/orbit-db-types
|
||||
import { OrbitDBTypes } from '@constl/orbit-db-types';
|
||||
import { OrbitDBTypes } from '@orbitdb/core-types';
|
||||
|
||||
// Assuming @orbitdb/core exports an interface or type you want to override
|
||||
// Replace 'OrbitDB' with the actual export name from @orbitdb/core you want to type
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@debros/network",
|
||||
"version": "0.0.16-alpha",
|
||||
"version": "0.0.17-alpha",
|
||||
"description": "Debros network core functionality for IPFS, libp2p and OrbitDB",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
@ -59,7 +59,6 @@
|
||||
"typescript": ">=5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@constl/orbit-db-types": "^2.0.6",
|
||||
"@eslint/js": "^9.24.0",
|
||||
"@orbitdb/core-types": "^1.0.14",
|
||||
"@types/express": "^5.0.1",
|
||||
@ -70,12 +69,14 @@
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-plugin-prettier": "^5.2.6",
|
||||
"globals": "^16.0.0",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^15.5.0",
|
||||
"prettier": "^3.5.3",
|
||||
"rimraf": "^5.0.5",
|
||||
"tsc-esm-fix": "^3.1.2",
|
||||
"typescript": "^5.8.2"
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
},
|
||||
"compilerOptions": {
|
||||
"typeRoots": [
|
||||
|
37
pnpm-lock.yaml
generated
37
pnpm-lock.yaml
generated
@ -78,9 +78,6 @@ importers:
|
||||
specifier: ^3.17.0
|
||||
version: 3.17.0
|
||||
devDependencies:
|
||||
'@constl/orbit-db-types':
|
||||
specifier: ^2.0.6
|
||||
version: 2.0.6
|
||||
'@eslint/js':
|
||||
specifier: ^9.24.0
|
||||
version: 9.24.0
|
||||
@ -111,6 +108,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^5.2.6
|
||||
version: 5.2.6(eslint-config-prettier@10.1.1(eslint@9.24.0))(eslint@9.24.0)(prettier@3.5.3)
|
||||
globals:
|
||||
specifier: ^16.0.0
|
||||
version: 16.0.0
|
||||
husky:
|
||||
specifier: ^8.0.3
|
||||
version: 8.0.3
|
||||
@ -129,6 +129,9 @@ importers:
|
||||
typescript:
|
||||
specifier: ^5.8.2
|
||||
version: 5.8.2
|
||||
typescript-eslint:
|
||||
specifier: ^8.29.0
|
||||
version: 8.29.0(eslint@9.24.0)(typescript@5.8.2)
|
||||
|
||||
packages:
|
||||
|
||||
@ -827,9 +830,6 @@ packages:
|
||||
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
|
||||
engines: {node: '>=0.1.90'}
|
||||
|
||||
'@constl/orbit-db-types@2.0.6':
|
||||
resolution: {integrity: sha512-p23e3tEr9izE9zskwqdN66VhFWYrbhxTvJNalhWqKkXnSV/58gbtc+yFobLPClKEDNKtYAhYhO6spLiI+VpZEg==}
|
||||
|
||||
'@dabh/diagnostics@2.0.3':
|
||||
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
|
||||
|
||||
@ -2251,6 +2251,10 @@ packages:
|
||||
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
globals@16.0.0:
|
||||
resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
gopd@1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -3750,6 +3754,13 @@ packages:
|
||||
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
typescript-eslint@8.29.0:
|
||||
resolution: {integrity: sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <5.9.0'
|
||||
|
||||
typescript@5.8.2:
|
||||
resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
|
||||
engines: {node: '>=14.17'}
|
||||
@ -4862,8 +4873,6 @@ snapshots:
|
||||
|
||||
'@colors/colors@1.6.0': {}
|
||||
|
||||
'@constl/orbit-db-types@2.0.6': {}
|
||||
|
||||
'@dabh/diagnostics@2.0.3':
|
||||
dependencies:
|
||||
colorspace: 1.1.4
|
||||
@ -7077,6 +7086,8 @@ snapshots:
|
||||
|
||||
globals@14.0.0: {}
|
||||
|
||||
globals@16.0.0: {}
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
@ -8846,6 +8857,16 @@ snapshots:
|
||||
media-typer: 1.1.0
|
||||
mime-types: 3.0.1
|
||||
|
||||
typescript-eslint@8.29.0(eslint@9.24.0)(typescript@5.8.2):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0)(typescript@5.8.2))(eslint@9.24.0)(typescript@5.8.2)
|
||||
'@typescript-eslint/parser': 8.29.0(eslint@9.24.0)(typescript@5.8.2)
|
||||
'@typescript-eslint/utils': 8.29.0(eslint@9.24.0)(typescript@5.8.2)
|
||||
eslint: 9.24.0
|
||||
typescript: 5.8.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
typescript@5.8.2: {}
|
||||
|
||||
uint8-varint@2.0.4:
|
||||
|
@ -1,11 +1,17 @@
|
||||
import { createServiceLogger } from '../../utils/logger';
|
||||
import { openDB } from '../../orbit/orbitDBService';
|
||||
import { getConnection } from '../core/connection';
|
||||
import * as cache from '../cache/cacheService';
|
||||
import * as events from '../events/eventService';
|
||||
import { validateDocument } from '../schema/validator';
|
||||
import { measurePerformance } from '../metrics/metricsService';
|
||||
import { ErrorCode, StoreType, StoreOptions, CreateResult, UpdateResult, PaginatedResult, QueryOptions, ListOptions } from '../types';
|
||||
import {
|
||||
ErrorCode,
|
||||
StoreType,
|
||||
StoreOptions,
|
||||
CreateResult,
|
||||
UpdateResult,
|
||||
PaginatedResult,
|
||||
QueryOptions,
|
||||
ListOptions,
|
||||
} from '../types';
|
||||
import { DBError } from '../core/error';
|
||||
|
||||
const logger = createServiceLogger('DB_STORE');
|
||||
@ -18,65 +24,57 @@ export interface BaseStore {
|
||||
* Create a new document
|
||||
*/
|
||||
create<T extends Record<string, any>>(
|
||||
collection: string,
|
||||
id: string,
|
||||
data: Omit<T, 'createdAt' | 'updatedAt'>,
|
||||
options?: StoreOptions
|
||||
collection: string,
|
||||
id: string,
|
||||
data: Omit<T, 'createdAt' | 'updatedAt'>,
|
||||
options?: StoreOptions,
|
||||
): Promise<CreateResult>;
|
||||
|
||||
|
||||
/**
|
||||
* Get a document by ID
|
||||
*/
|
||||
get<T extends Record<string, any>>(
|
||||
collection: string,
|
||||
id: string,
|
||||
options?: StoreOptions & { skipCache?: boolean }
|
||||
collection: string,
|
||||
id: string,
|
||||
options?: StoreOptions & { skipCache?: boolean },
|
||||
): Promise<T | null>;
|
||||
|
||||
|
||||
/**
|
||||
* Update a document
|
||||
*/
|
||||
update<T extends Record<string, any>>(
|
||||
collection: string,
|
||||
id: string,
|
||||
data: Partial<Omit<T, 'createdAt' | 'updatedAt'>>,
|
||||
options?: StoreOptions & { upsert?: boolean }
|
||||
collection: string,
|
||||
id: string,
|
||||
data: Partial<Omit<T, 'createdAt' | 'updatedAt'>>,
|
||||
options?: StoreOptions & { upsert?: boolean },
|
||||
): Promise<UpdateResult>;
|
||||
|
||||
|
||||
/**
|
||||
* Delete a document
|
||||
*/
|
||||
remove(
|
||||
collection: string,
|
||||
id: string,
|
||||
options?: StoreOptions
|
||||
): Promise<boolean>;
|
||||
|
||||
remove(collection: string, id: string, options?: StoreOptions): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* List all documents in a collection with pagination
|
||||
*/
|
||||
list<T extends Record<string, any>>(
|
||||
collection: string,
|
||||
options?: ListOptions
|
||||
collection: string,
|
||||
options?: ListOptions,
|
||||
): Promise<PaginatedResult<T>>;
|
||||
|
||||
|
||||
/**
|
||||
* Query documents in a collection with filtering and pagination
|
||||
*/
|
||||
query<T extends Record<string, any>>(
|
||||
collection: string,
|
||||
collection: string,
|
||||
filter: (doc: T) => boolean,
|
||||
options?: QueryOptions
|
||||
options?: QueryOptions,
|
||||
): Promise<PaginatedResult<T>>;
|
||||
|
||||
|
||||
/**
|
||||
* Create an index for a collection to speed up queries
|
||||
*/
|
||||
createIndex(
|
||||
collection: string,
|
||||
field: string,
|
||||
options?: StoreOptions
|
||||
): Promise<boolean>;
|
||||
createIndex(collection: string, field: string, options?: StoreOptions): Promise<boolean>;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,14 +83,21 @@ export interface BaseStore {
|
||||
export async function openStore(
|
||||
collection: string,
|
||||
storeType: StoreType,
|
||||
options?: StoreOptions
|
||||
options?: StoreOptions,
|
||||
): Promise<any> {
|
||||
try {
|
||||
const connection = getConnection(options?.connectionId);
|
||||
return await openDB(collection, storeType);
|
||||
logger.info(`Connection for ${collection}:`, connection);
|
||||
return await openDB(collection, storeType).catch((err) => {
|
||||
throw new Error(`OrbitDB openDB failed: ${err.message}`);
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`Error opening ${storeType} store for collection ${collection}:`, error);
|
||||
throw new DBError(ErrorCode.OPERATION_FAILED, `Failed to open ${storeType} store for collection ${collection}`, error);
|
||||
throw new DBError(
|
||||
ErrorCode.OPERATION_FAILED,
|
||||
`Failed to open ${storeType} store for collection ${collection}`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,31 +107,36 @@ export async function openStore(
|
||||
export function prepareDocument<T extends Record<string, any>>(
|
||||
collection: string,
|
||||
data: Omit<T, 'createdAt' | 'updatedAt'>,
|
||||
existingDoc?: T | null
|
||||
existingDoc?: T | null,
|
||||
): T {
|
||||
const timestamp = Date.now();
|
||||
|
||||
|
||||
// Sanitize the input data by replacing undefined with null
|
||||
const sanitizedData = Object.fromEntries(
|
||||
Object.entries(data).map(([key, value]) => [key, value === undefined ? null : value]),
|
||||
) as Omit<T, 'createdAt' | 'updatedAt'>;
|
||||
|
||||
// If it's an update to an existing document
|
||||
if (existingDoc) {
|
||||
const doc = {
|
||||
...existingDoc,
|
||||
...data,
|
||||
updatedAt: timestamp
|
||||
...sanitizedData,
|
||||
updatedAt: timestamp,
|
||||
} as T;
|
||||
|
||||
|
||||
// Validate the document against its schema
|
||||
validateDocument(collection, doc);
|
||||
return doc;
|
||||
}
|
||||
|
||||
|
||||
// Otherwise it's a new document
|
||||
const doc = {
|
||||
...data,
|
||||
...sanitizedData,
|
||||
createdAt: timestamp,
|
||||
updatedAt: timestamp
|
||||
updatedAt: timestamp,
|
||||
} as unknown as T;
|
||||
|
||||
|
||||
// Validate the document against its schema
|
||||
validateDocument(collection, doc);
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ export const openDB = async (name: string, type: string) => {
|
||||
const dbOptions = {
|
||||
type,
|
||||
overwrite: false,
|
||||
AccessController: IPFSAccessController({ write: ['*'], storage: getHelia() }),
|
||||
AccessController: IPFSAccessController({ write: ['*'] }),
|
||||
};
|
||||
|
||||
if (existingAddress) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user