188 lines
6.1 KiB
TypeScript
188 lines
6.1 KiB
TypeScript
|
|
import * as plugins from '../plugins.js';
|
||
|
|
import type { Reception } from './classes.reception.js';
|
||
|
|
import { AppConnection } from './classes.appconnection.js';
|
||
|
|
|
||
|
|
export class AppConnectionManager {
|
||
|
|
public receptionRef: Reception;
|
||
|
|
public get db() {
|
||
|
|
return this.receptionRef.db.smartdataDb;
|
||
|
|
}
|
||
|
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||
|
|
|
||
|
|
public CAppConnection = plugins.smartdata.setDefaultManagerForDoc(this, AppConnection);
|
||
|
|
|
||
|
|
constructor(receptionRefArg: Reception) {
|
||
|
|
this.receptionRef = receptionRefArg;
|
||
|
|
this.receptionRef.typedrouter.addTypedRouter(this.typedrouter);
|
||
|
|
|
||
|
|
// Handler: Get app connections for an organization
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_GetAppConnections>(
|
||
|
|
'getAppConnections',
|
||
|
|
async (requestArg) => {
|
||
|
|
// Verify JWT and get user
|
||
|
|
const jwtData = await this.receptionRef.jwtManager.verifyJWTAndGetData(requestArg.jwt);
|
||
|
|
const user = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
id: jwtData.data.userId,
|
||
|
|
});
|
||
|
|
|
||
|
|
// Check user has access to the organization
|
||
|
|
const organization = await this.receptionRef.organizationmanager.COrganization.getInstance({
|
||
|
|
id: requestArg.organizationId,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!organization) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError('Organization not found');
|
||
|
|
}
|
||
|
|
|
||
|
|
const role = await this.receptionRef.roleManager.CRole.getInstance({
|
||
|
|
data: {
|
||
|
|
organizationId: organization.id,
|
||
|
|
userId: user.id,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!role) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError(
|
||
|
|
'User not authorized for this organization'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get all connections for this organization
|
||
|
|
const connections = await this.CAppConnection.getInstances({
|
||
|
|
'data.organizationId': requestArg.organizationId,
|
||
|
|
});
|
||
|
|
|
||
|
|
const connectionObjects = await Promise.all(
|
||
|
|
connections.map(async (conn) => await conn.createSavableObject())
|
||
|
|
);
|
||
|
|
|
||
|
|
return {
|
||
|
|
connections: connectionObjects,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Handler: Toggle app connection (connect/disconnect)
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_ToggleAppConnection>(
|
||
|
|
'toggleAppConnection',
|
||
|
|
async (requestArg) => {
|
||
|
|
// Verify JWT and get user
|
||
|
|
const jwtData = await this.receptionRef.jwtManager.verifyJWTAndGetData(requestArg.jwt);
|
||
|
|
const user = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
id: jwtData.data.userId,
|
||
|
|
});
|
||
|
|
|
||
|
|
// Check user has admin access to the organization
|
||
|
|
const organization = await this.receptionRef.organizationmanager.COrganization.getInstance({
|
||
|
|
id: requestArg.organizationId,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!organization) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError('Organization not found');
|
||
|
|
}
|
||
|
|
|
||
|
|
const isAdmin = await organization.checkIfUserIsAdmin(user);
|
||
|
|
if (!isAdmin) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError(
|
||
|
|
'Only organization admins can manage app connections'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the app
|
||
|
|
const app = await this.receptionRef.appManager.getAppById(requestArg.appId);
|
||
|
|
if (!app) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError('App not found');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Find existing connection
|
||
|
|
let connection = await this.CAppConnection.getInstance({
|
||
|
|
'data.organizationId': requestArg.organizationId,
|
||
|
|
'data.appId': requestArg.appId,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (requestArg.action === 'connect') {
|
||
|
|
if (connection && connection.isActive()) {
|
||
|
|
// Already connected
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
connection: await connection.createSavableObject(),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
if (connection) {
|
||
|
|
// Reconnect existing connection
|
||
|
|
await connection.reconnect(user.id);
|
||
|
|
} else {
|
||
|
|
// Create new connection
|
||
|
|
connection = new AppConnection();
|
||
|
|
connection.id = plugins.smartunique.shortId();
|
||
|
|
connection.data = {
|
||
|
|
organizationId: requestArg.organizationId,
|
||
|
|
appId: requestArg.appId,
|
||
|
|
appType: app.type,
|
||
|
|
status: 'active',
|
||
|
|
connectedAt: Date.now(),
|
||
|
|
connectedByUserId: user.id,
|
||
|
|
grantedScopes: app.data.oauthCredentials?.allowedScopes || [],
|
||
|
|
};
|
||
|
|
await connection.save();
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
connection: await connection.createSavableObject(),
|
||
|
|
};
|
||
|
|
} else {
|
||
|
|
// Disconnect
|
||
|
|
if (!connection) {
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
await connection.disconnect();
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
connection: await connection.createSavableObject(),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get all connections for an organization
|
||
|
|
*/
|
||
|
|
public async getConnectionsForOrganization(organizationId: string): Promise<AppConnection[]> {
|
||
|
|
return await this.CAppConnection.getInstances({
|
||
|
|
'data.organizationId': organizationId,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get connection for a specific app and organization
|
||
|
|
*/
|
||
|
|
public async getConnection(
|
||
|
|
organizationId: string,
|
||
|
|
appId: string
|
||
|
|
): Promise<AppConnection | null> {
|
||
|
|
return await this.CAppConnection.getInstance({
|
||
|
|
'data.organizationId': organizationId,
|
||
|
|
'data.appId': appId,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if an app is connected to an organization
|
||
|
|
*/
|
||
|
|
public async isAppConnected(organizationId: string, appId: string): Promise<boolean> {
|
||
|
|
const connection = await this.getConnection(organizationId, appId);
|
||
|
|
return connection?.isActive() || false;
|
||
|
|
}
|
||
|
|
}
|