streamby-core
Version:
StreamBy middleware framework for media storage management
324 lines (323 loc) • 17.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.exportRouter = exportRouter;
const express_1 = require("express");
const manager_1 = require("../../models/manager");
const auth_1 = require("../../utils/auth");
const export_1 = require("../../services/export");
const connectionManager_1 = require("../../adapters/database/connectionManager");
const mongodb_1 = require("mongodb");
const encryption_1 = require("../../utils/encryption");
function exportRouter(config) {
const router = (0, express_1.Router)();
const Project = (0, manager_1.getModel)('projects');
router.get('/projects/:id/exports/:export_id', async (req, res) => {
try {
const auth = await config.authProvider(req);
if (!auth ||
!auth.userId ||
!auth.role) {
return res.status(401).json({ message: 'Unauthorized' });
}
const projectId = req.params.id;
const exportId = req.params.export_id;
const project = await Project.findOne({ _id: projectId });
if (!project) {
return res.status(404).json({ message: 'Project not found' });
}
if (!(0, auth_1.isProjectMember)(project, auth.userId)) {
return res.status(403).json({ message: 'Unauthorized access' });
}
const exportMetadata = project.exports.find((e) => e.id.toString() === exportId);
if (!exportMetadata) {
return res.status(404).json({ message: 'Export not found in this project' });
}
const targetDb = config.databases?.find(db => db.type === project.dbType);
if (!targetDb) {
return res.status(500).json({ message: `Database connection not found for type ${project.dbType}` });
}
const connection = (0, connectionManager_1.getConnection)(targetDb.id);
let data;
if (project.dbType === 'nosql') {
const db = connection.client.db();
if (exportMetadata.type === 'json') {
const rawData = await db.collection(exportMetadata.collectionName).findOne({ _id: new mongodb_1.ObjectId(exportId) });
data = {
json: rawData?.json,
name: rawData?.name,
method: rawData?.method,
collectionName: rawData?.collectionName,
createdAt: rawData?.createdAt,
updatedAt: rawData?.updatedAt,
type: exportMetadata.type,
fields: exportMetadata.fields,
description: rawData?.description,
};
}
else if (exportMetadata.type === 'externalApi') {
const ProjectModel = (0, manager_1.getModel)('projects', 'nosql');
const currentProject = await ProjectModel.findOne({ _id: projectId });
if (!currentProject) {
return res.status(404).json({ message: 'Project not found.' });
}
let headers = {};
if (exportMetadata.credentialId) {
if (!(0, encryption_1.isEncryptionKeySet)()) {
throw new Error('Encryption key is not set. Cannot use encrypted credentials.');
}
const credential = project.credentials?.find((cred) => cred.id === exportMetadata.credentialId);
if (credential) {
const decryptedValue = (0, encryption_1.decrypt)(credential.encryptedValue);
const authPrefix = exportMetadata.prefix ? `${exportMetadata.prefix} ` : '';
headers = {
'Authorization': `${authPrefix}${decryptedValue}`,
'Content-Type': 'application/json',
};
}
}
const externalApiData = await fetch(exportMetadata.apiUrl, { headers });
const apiResponse = await externalApiData.json();
let rawData;
if (exportMetadata.fields && exportMetadata.fields.length > 0) {
rawData = apiResponse.map((item) => {
return exportMetadata.fields.reduce((filtered, field) => {
filtered[field.name] = item[field.name];
return filtered;
}, {});
});
}
data = {
apiResponse: apiResponse,
json: rawData || {},
name: exportMetadata.name,
createdAt: exportMetadata.createdAt,
updatedAt: exportMetadata.updatedAt,
type: exportMetadata.type,
apiUrl: exportMetadata.apiUrl,
prefix: exportMetadata.prefix,
collectionName: exportMetadata.collectionName,
fields: exportMetadata.fields,
description: exportMetadata?.description,
};
}
else {
const rawData = await db.collection(exportMetadata.collectionName).findOne({ _id: new mongodb_1.ObjectId(exportId) });
data = {
json: rawData?.json,
name: rawData?.name,
method: rawData?.method,
createdAt: rawData?.createdAt,
updatedAt: rawData?.updatedAt,
collectionName: rawData?.collectionName,
type: exportMetadata.type,
credentialId: exportMetadata.credentialId,
description: rawData?.description,
fields: exportMetadata.fields,
};
}
}
else if (project.dbType === 'sql') {
// ... SQL implementation needed
}
if (!data) {
return res.status(404).json({ message: 'Export data not found' });
}
let responseData;
responseData = {
...data,
allowedOrigin: exportMetadata.allowedOrigin,
private: exportMetadata.private,
};
res.json({
data: responseData,
message: 'Export data fetched successfully',
});
}
catch (err) {
res.status(500).json({ message: 'Failed to fetch export data', details: err.message });
}
});
router.post('/projects/:id/exports', async (req, res) => {
try {
const auth = await config.authProvider(req);
if (!auth ||
!auth.userId ||
!auth.role) {
return res.status(401).json({ message: 'Unauthorized' });
}
const projectId = req.params.id;
const { name, description, fields, collectionName, jsonData, isPrivate, allowedOrigin, exportType, apiUrl, credentialId, prefix } = req.body;
if (!name || !collectionName) {
return res.status(400).json({ message: 'Missing export name or collectionName' });
}
if (exportType === 'externalApi') {
if (!apiUrl) {
return res.status(400).json({ message: 'API URL is required for externalApi export type' });
}
}
else if (!jsonData) {
return res.status(400).json({ message: 'Missing jsonData for non-externalApi export types' });
}
const project = await Project.findOne({ _id: projectId });
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
return res.status(403).json({ message: 'Unauthorized project access' });
}
const result = await (0, export_1.createExport)(config, projectId, description, fields, name, jsonData, project.dbType, exportType, isPrivate, allowedOrigin, apiUrl, credentialId, prefix);
res.status(201).json({ data: result, message: result.message });
}
catch (err) {
res.status(500).json({ message: 'Failed to create raw export', details: err.message });
}
});
router.patch('/projects/:id/exports/:export_id', async (req, res) => {
try {
const auth = await config.authProvider(req);
if (!auth || !auth.userId) {
return res.status(401).json({ message: 'Unauthorized' });
}
const { id: projectId, export_id: exportId } = req.params;
const { name, collectionName, description, fields, jsonData, isPrivate, allowedOrigin, exportType, apiUrl, credentialId, prefix } = req.body;
if (!name || !collectionName) {
return res.status(400).json({ message: 'Missing name or collectionName' });
}
if (exportType === 'externalApi') {
if (!apiUrl) {
return res.status(400).json({ message: 'API URL is required for externalApi export type' });
}
}
else if (!jsonData) {
return res.status(400).json({ message: 'Missing jsonData for non-externalApi export types' });
}
const project = await Project.findOne({ _id: projectId });
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
return res.status(403).json({ message: 'Unauthorized project access' });
}
const result = await (0, export_1.updateExport)(config, projectId, exportId, description, fields, name, collectionName, jsonData, project.dbType, exportType, isPrivate, allowedOrigin, apiUrl, credentialId, prefix);
res.status(200).json({ data: result, message: result.message });
}
catch (err) {
res.status(500).json({ message: 'Failed to update raw export', details: err.message });
}
});
router.delete('/projects/:id/exports/:export_id', async (req, res) => {
try {
const auth = await config.authProvider(req);
if (!auth || !auth.userId) {
return res.status(401).json({ message: 'Unauthorized' });
}
const { id: projectId, export_id: exportId } = req.params;
const project = await Project.findOne({ _id: projectId });
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
return res.status(403).json({ message: 'Unauthorized project access' });
}
const exportMetadata = project.exports.find((e) => e.id.toString() === exportId);
if (!exportMetadata) {
return res.status(404).json({ message: 'Export not found in this project' });
}
await (0, export_1.deleteExport)(config, projectId, exportId, project.dbType, exportMetadata.collectionName);
res.status(200).json({ message: 'Export deleted successfully' });
}
catch (err) {
res.status(500).json({ message: 'Failed to delete export', details: err.message });
}
});
router.get('/:projectId/get-export/:exportName', async (req, res) => {
try {
const { projectId, exportName } = req.params;
const project = await Project.findOne({ _id: projectId });
if (!project) {
return res.status(404).json({ message: 'Project not found' });
}
const exportMetadata = project.exports.find((e) => e.name === exportName);
if (!exportMetadata) {
return res.status(404).json({ message: 'Export not found in this project' });
}
const origin = req.headers.origin;
let effectiveAllowedOrigins = exportMetadata.allowedOrigin;
const projectOrigins = project.allowedOrigin;
// If export's allowedOrigin is ['*'] or it's not set, it inherits from the project.
if (!effectiveAllowedOrigins || (effectiveAllowedOrigins.length === 1 && effectiveAllowedOrigins[0] === '*')) {
effectiveAllowedOrigins = projectOrigins;
}
else {
// If the export has its own list, ensure it's a subset of the project's list (if project is not public).
if (projectOrigins && !projectOrigins.includes('*')) {
const isSubset = effectiveAllowedOrigins.every((o) => projectOrigins.includes(o));
if (!isSubset) {
return res.status(403).json({ message: 'Unauthorized: Export origins are not allowed by the parent project.' });
}
}
}
// If there's no origin header, deny access unless the effective scope is public.
if (!origin && !(effectiveAllowedOrigins && effectiveAllowedOrigins.includes('*'))) {
return res.status(403).json({ message: 'Origin header required' });
}
// Check for public access ('*') or if the request's origin is in the effective list.
const isAllowed = effectiveAllowedOrigins && (effectiveAllowedOrigins.includes('*') || (origin && effectiveAllowedOrigins.includes(origin)));
if (!isAllowed) {
return res.status(403).json({ message: 'Unauthorized' });
}
const targetDb = config.databases?.find(db => db.type === project.dbType);
if (!targetDb) {
return res.status(500).json({ message: `Database connection not found for type ${project.dbType}` });
}
const connection = (0, connectionManager_1.getConnection)(targetDb.id);
let data;
if (project.dbType === 'nosql') {
const db = connection.client.db();
if (exportMetadata.type === 'json') {
const rawData = await db.collection(exportMetadata.collectionName).findOne({ _id: new mongodb_1.ObjectId(exportMetadata.id) });
data = rawData ? rawData.json : null;
}
else if (exportMetadata.type === 'externalApi') {
let headers = {};
if (exportMetadata.credentialId) {
if (!(0, encryption_1.isEncryptionKeySet)()) {
throw new Error('Encryption key is not set. Cannot use encrypted credentials.');
}
const credential = project.credentials?.find((cred) => cred.id === exportMetadata.credentialId);
if (!credential) {
throw new Error(`Credential with ID ${exportMetadata.credentialId} not found.`);
}
const decryptedValue = (0, encryption_1.decrypt)(credential.encryptedValue);
const authPrefix = exportMetadata.prefix ? `${exportMetadata.prefix} ` : '';
headers = {
'Authorization': `${authPrefix}${decryptedValue}`,
'Content-Type': 'application/json',
};
}
try {
const response = await fetch(exportMetadata.apiUrl, { headers });
if (!response.ok) {
throw new Error(`Failed to fetch from external API: ${response.statusText}`);
}
data = await response.json();
}
catch (error) {
throw new Error(`Error fetching from external API: ${error.message}`);
}
if (exportMetadata.fields && exportMetadata.fields.length > 0) {
data = data.map((item) => {
return exportMetadata.fields.reduce((filtered, field) => {
filtered[field.name] = item[field.name];
return filtered;
}, {});
});
}
}
}
else if (project.dbType === 'sql') {
// SQL implementation for public exports will go here
return res.status(501).json({ message: 'SQL public exports not yet implemented' });
}
if (!data) {
return res.status(404).json({ message: 'Export data not found' });
}
res.json(data);
}
catch (err) {
res.status(500).json({ message: 'Failed to fetch public export data', details: err.message });
}
});
return router;
}