feat: Add presence heartbeat for Matrix online status
- matrix-gateway: POST /internal/matrix/presence/online endpoint - usePresenceHeartbeat hook with activity tracking - Auto away after 5 min inactivity - Offline on page close/visibility change - Integrated in MatrixChatRoom component
This commit is contained in:
100
backend/services/pdp/pdp.service.ts
Normal file
100
backend/services/pdp/pdp.service.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* PDP Service (MVP)
|
||||
* Based on: pdp_access.md, core-services-mvp.md
|
||||
*
|
||||
* Responsibilities:
|
||||
* - Centralized access decision making
|
||||
* - Interpret policies from pdp_access.md
|
||||
* - Provide simple API for other services
|
||||
*/
|
||||
|
||||
import type { PdpRequest, PdpResponse, PolicyId, PdpContext } from '../../domain/pdp/policy.model';
|
||||
import { policiesConfig } from './policies.config';
|
||||
import { walletService } from '../wallet/wallet.service';
|
||||
|
||||
export class PdpService {
|
||||
/**
|
||||
* Check policy and return decision
|
||||
*/
|
||||
async check(
|
||||
policyId: PolicyId,
|
||||
resource: Record<string, unknown>,
|
||||
context: PdpContext
|
||||
): Promise<PdpResponse> {
|
||||
const policy = policiesConfig[policyId];
|
||||
|
||||
if (!policy) {
|
||||
return {
|
||||
decision: 'deny',
|
||||
reason: `Policy ${policyId} not found`,
|
||||
};
|
||||
}
|
||||
|
||||
// Evaluate policy conditions
|
||||
const result = await this.evaluatePolicy(policy, resource, context);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async evaluatePolicy(
|
||||
policy: any,
|
||||
resource: Record<string, unknown>,
|
||||
context: PdpContext
|
||||
): Promise<PdpResponse> {
|
||||
// MVP: Simple evaluation
|
||||
// Future: More complex condition evaluation
|
||||
|
||||
// Example: policy.dao.create
|
||||
if (policy.id === 'policy.dao.create') {
|
||||
const hasEnough = await walletService.hasEnoughForDaoCreate(context.userId || '');
|
||||
if (!hasEnough) {
|
||||
return {
|
||||
decision: 'deny',
|
||||
reason: 'INSUFFICIENT_BALANCE',
|
||||
details: {
|
||||
required: { DAAR: 1.0, DAARION: 0.01 },
|
||||
},
|
||||
};
|
||||
}
|
||||
return { decision: 'allow' };
|
||||
}
|
||||
|
||||
// Example: policy.platform.create
|
||||
if (policy.id === 'policy.platform.create') {
|
||||
const hasEnough = await walletService.hasEnoughForPlatformCreate(context.userId || '');
|
||||
if (!hasEnough) {
|
||||
return {
|
||||
decision: 'deny',
|
||||
reason: 'INSUFFICIENT_BALANCE',
|
||||
details: {
|
||||
required: { DAARION: 1.0 },
|
||||
},
|
||||
};
|
||||
}
|
||||
return { decision: 'allow' };
|
||||
}
|
||||
|
||||
// Example: policy.vendor.register
|
||||
if (policy.id === 'policy.vendor.register') {
|
||||
const hasEnough = await walletService.hasEnoughForVendorRegister(context.userId || '');
|
||||
if (!hasEnough) {
|
||||
return {
|
||||
decision: 'deny',
|
||||
reason: 'INSUFFICIENT_STAKED_DAARION',
|
||||
details: {
|
||||
required: { DAARION: 0.01 },
|
||||
},
|
||||
};
|
||||
}
|
||||
return { decision: 'allow' };
|
||||
}
|
||||
|
||||
// Default: allow (for MVP, can be more restrictive later)
|
||||
return { decision: 'allow' };
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance
|
||||
export const pdpService = new PdpService();
|
||||
|
||||
|
||||
76
backend/services/pdp/policies.config.ts
Normal file
76
backend/services/pdp/policies.config.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Policies Configuration
|
||||
* Based on: pdp_access.md
|
||||
*
|
||||
* Initial set of policies for MVP
|
||||
*/
|
||||
|
||||
export const policiesConfig = {
|
||||
'policy.dao.create': {
|
||||
id: 'policy.dao.create',
|
||||
description: 'Створення нового MicroDAO',
|
||||
conditions: [
|
||||
{
|
||||
type: 'or',
|
||||
rules: [
|
||||
{ type: 'balance', token: 'DAAR', gte: 1 },
|
||||
{ type: 'balance', token: 'DAARION', gte: 0.01 },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
'policy.vendor.register': {
|
||||
id: 'policy.vendor.register',
|
||||
description: 'Реєстрація вендора на платформі',
|
||||
conditions: [
|
||||
{ type: 'staked', token: 'DAARION', gte: 0.01 },
|
||||
],
|
||||
},
|
||||
'policy.platform.create': {
|
||||
id: 'policy.platform.create',
|
||||
description: 'Створення платформи',
|
||||
conditions: [
|
||||
{ type: 'staked', token: 'DAARION', gte: 1 },
|
||||
],
|
||||
},
|
||||
'policy.federation.join': {
|
||||
id: 'policy.federation.join',
|
||||
description: 'Вступ DAO до SuperDAO',
|
||||
conditions: [
|
||||
{ type: 'role', value: 'owner' },
|
||||
{ type: 'target', property: 'federation_mode', value: 'superdao' },
|
||||
],
|
||||
},
|
||||
'policy.federation.leave': {
|
||||
id: 'policy.federation.leave',
|
||||
description: 'Вихід DAO з SuperDAO',
|
||||
conditions: [
|
||||
{ type: 'role', value: 'owner' },
|
||||
],
|
||||
},
|
||||
'policy.federation.create-superdao': {
|
||||
id: 'policy.federation.create-superdao',
|
||||
description: 'Створення SuperDAO',
|
||||
conditions: [
|
||||
{ type: 'role', value: 'owner' },
|
||||
{ type: 'dao', property: 'child_count', gte: 1 },
|
||||
],
|
||||
},
|
||||
'policy.federation.dissolve': {
|
||||
id: 'policy.federation.dissolve',
|
||||
description: 'Розформування федерації',
|
||||
conditions: [
|
||||
{ type: 'role', value: 'owner' },
|
||||
{ type: 'dao', property: 'level', ne: 'A1' },
|
||||
],
|
||||
},
|
||||
'policy.agent.run': {
|
||||
id: 'policy.agent.run',
|
||||
description: 'Запуск агента',
|
||||
conditions: [
|
||||
{ type: 'agent', property: 'registered', value: true },
|
||||
],
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user