mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
264 lines
9.2 KiB
JavaScript
264 lines
9.2 KiB
JavaScript
import dotenv from 'dotenv';
|
|
dotenv.config({ quiet: true });
|
|
import express from 'express';
|
|
import cors from 'cors';
|
|
import { ParseServer } from 'parse-server';
|
|
import path from 'path';
|
|
const __dirname = path.resolve();
|
|
import http from 'http';
|
|
import formData from 'form-data';
|
|
import Mailgun from 'mailgun.js';
|
|
import { ApiPayloadConverter } from 'parse-server-api-mail-adapter';
|
|
import S3Adapter from '@parse/s3-files-adapter';
|
|
import FSFilesAdapter from '@parse/fs-files-adapter';
|
|
import { app as customRoute } from './cloud/customRoute/customApp.js';
|
|
import { exec } from 'child_process';
|
|
import { createTransport } from 'nodemailer';
|
|
import { appName, cloudServerUrl, serverAppId, smtpenable, smtpsecure, useLocal } from './Utils.js';
|
|
import { SSOAuth } from './auth/authadapter.js';
|
|
import runDbMigrations from './migrationdb/index.js';
|
|
import { validateSignedLocalUrl } from './cloud/parsefunction/getSignedUrl.js';
|
|
let fsAdapter;
|
|
|
|
if (useLocal !== 'true') {
|
|
try {
|
|
// const spacesEndpoint = new AWS.Endpoint(process.env.DO_ENDPOINT);
|
|
const spacesEndpoint = process.env.DO_ENDPOINT?.includes('http')
|
|
? process.env.DO_ENDPOINT
|
|
: `https://${process.env.DO_ENDPOINT}`; //"e.g https://blr1.digitaloceanspaces.com"
|
|
const s3Options = {
|
|
bucket: process.env.DO_SPACE,
|
|
baseUrl: process.env.DO_BASEURL,
|
|
fileAcl: 'none',
|
|
region: process.env.DO_REGION,
|
|
directAccess: true,
|
|
preserveFileName: true,
|
|
presignedUrl: true,
|
|
presignedUrlExpires: 900,
|
|
s3overrides: {
|
|
credentials: {
|
|
accessKeyId: process.env.DO_ACCESS_KEY_ID,
|
|
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY,
|
|
},
|
|
endpoint: spacesEndpoint,
|
|
signatureVersion: 'v4',
|
|
},
|
|
};
|
|
fsAdapter = new S3Adapter(s3Options);
|
|
} catch (err) {
|
|
console.log('Please provide AWS credintials in env file! Defaulting to local storage.');
|
|
fsAdapter = new FSFilesAdapter({
|
|
filesSubDirectory: 'files', // optional, defaults to ./files
|
|
});
|
|
}
|
|
} else {
|
|
fsAdapter = new FSFilesAdapter({
|
|
filesSubDirectory: 'files', // optional, defaults to ./files
|
|
});
|
|
}
|
|
|
|
let transporterMail;
|
|
let mailgunClient;
|
|
let mailgunDomain;
|
|
let isMailAdapter = false;
|
|
if (smtpenable) {
|
|
try {
|
|
let transporterConfig = {
|
|
host: process.env.SMTP_HOST,
|
|
port: process.env.SMTP_PORT || 465,
|
|
secure: smtpsecure,
|
|
};
|
|
|
|
// ✅ Add auth only if BOTH username & password exist
|
|
const smtpUser = process.env.SMTP_USERNAME;
|
|
const smtpPass = process.env.SMTP_PASS;
|
|
|
|
if (smtpUser && smtpPass) {
|
|
transporterConfig.auth = {
|
|
user: process.env.SMTP_USERNAME ? process.env.SMTP_USERNAME : process.env.SMTP_USER_EMAIL,
|
|
pass: smtpPass,
|
|
};
|
|
}
|
|
transporterMail = createTransport(transporterConfig);
|
|
await transporterMail.verify();
|
|
isMailAdapter = true;
|
|
} catch (err) {
|
|
isMailAdapter = false;
|
|
console.log(`Please provide valid SMTP credentials: ${err}`);
|
|
}
|
|
} else if (process.env.MAILGUN_API_KEY) {
|
|
try {
|
|
const mailgun = new Mailgun(formData);
|
|
mailgunClient = mailgun.client({
|
|
username: 'api',
|
|
key: process.env.MAILGUN_API_KEY,
|
|
});
|
|
mailgunDomain = process.env.MAILGUN_DOMAIN;
|
|
isMailAdapter = true;
|
|
} catch (error) {
|
|
isMailAdapter = false;
|
|
console.log('Please provide valid Mailgun credentials');
|
|
}
|
|
}
|
|
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
|
|
export const config = {
|
|
databaseURI:
|
|
process.env.DATABASE_URI || process.env.MONGODB_URI || 'mongodb://localhost:27017/dev',
|
|
cloud: function () {
|
|
import('./cloud/main.js');
|
|
},
|
|
appId: serverAppId,
|
|
logLevel: ['error'],
|
|
maxLimit: 500,
|
|
maxUploadSize: '100mb',
|
|
masterKey: process.env.MASTER_KEY, //Add your master key here. Keep it secret!
|
|
masterKeyIps: ['0.0.0.0/0', '::/0'], // '::1'
|
|
serverURL: cloudServerUrl, // Don't forget to change to https if needed
|
|
verifyUserEmails: false,
|
|
publicServerURL: process.env.SERVER_URL || cloudServerUrl,
|
|
// Your apps name. This will appear in the subject and body of the emails that are sent.
|
|
appName: appName,
|
|
allowClientClassCreation: false,
|
|
allowExpiredAuthDataToken: false,
|
|
enableInsecureAuthAdapters: false,
|
|
databaseOptions: { allowPublicExplain: false },
|
|
encodeParseObjectInCloudFunction: true,
|
|
...(isMailAdapter === true
|
|
? {
|
|
emailAdapter: {
|
|
module: 'parse-server-api-mail-adapter',
|
|
options: {
|
|
// The email address from which emails are sent.
|
|
sender: appName + ' <' + mailsender + '>',
|
|
// The email templates.
|
|
templates: {
|
|
// The template used by Parse Server to send an email for password
|
|
// reset; this is a reserved template name.
|
|
passwordResetEmail: {
|
|
subjectPath: './files/password_reset_email_subject.txt',
|
|
textPath: './files/password_reset_email.txt',
|
|
htmlPath: './files/password_reset_email.html',
|
|
},
|
|
// The template used by Parse Server to send an email for email
|
|
// address verification; this is a reserved template name.
|
|
verificationEmail: {
|
|
subjectPath: './files/verification_email_subject.txt',
|
|
textPath: './files/verification_email.txt',
|
|
htmlPath: './files/verification_email.html',
|
|
},
|
|
},
|
|
apiCallback: async ({ payload, locale }) => {
|
|
if (mailgunClient) {
|
|
const mailgunPayload = ApiPayloadConverter.mailgun(payload);
|
|
await mailgunClient.messages.create(mailgunDomain, mailgunPayload);
|
|
} else if (transporterMail) await transporterMail.sendMail(payload);
|
|
},
|
|
},
|
|
},
|
|
}
|
|
: {}),
|
|
filesAdapter: fsAdapter,
|
|
auth: { google: { clientId: process.env.GOOGLE_CLIENT_ID }, sso: SSOAuth },
|
|
// for fix Adapter prototype don't match expected prototype
|
|
push: { queueOptions: { disablePushWorker: true } },
|
|
};
|
|
// Client-keys like the javascript key or the .NET key are not necessary with parse-server
|
|
// If you wish you require them, you can set them as options in the initialization above:
|
|
// javascriptKey, restAPIKey, dotNetKey, clientKey
|
|
|
|
export const app = express();
|
|
app.use(cors());
|
|
app.use(express.json({ limit: '100mb' }));
|
|
app.use(express.urlencoded({ limit: '100mb', extended: true }));
|
|
app.use(function (req, res, next) {
|
|
req.headers['x-real-ip'] = getUserIP(req);
|
|
const publicUrl = 'https://' + req?.get('host');
|
|
req.headers['public_url'] = publicUrl;
|
|
next();
|
|
});
|
|
function getUserIP(request) {
|
|
let forwardedFor = request.headers['x-forwarded-for'];
|
|
if (forwardedFor) {
|
|
if (forwardedFor.indexOf(',') > -1) {
|
|
return forwardedFor.split(',')[0];
|
|
} else {
|
|
return forwardedFor;
|
|
}
|
|
} else {
|
|
return request.socket.remoteAddress;
|
|
}
|
|
}
|
|
|
|
app.use(async function (req, res, next) {
|
|
const isFilePath = req.path.includes('files') || false;
|
|
if (isFilePath && req.method.toLowerCase() === 'get') {
|
|
const serverUrl = new URL(process.env.SERVER_URL);
|
|
const origin = serverUrl.pathname === '/api/app' ? serverUrl.origin + '/api' : serverUrl.origin;
|
|
const fileUrl = origin + req.originalUrl;
|
|
const params = fileUrl?.split('?')?.[1];
|
|
if (params) {
|
|
const fileRes = await validateSignedLocalUrl(fileUrl);
|
|
if (fileRes === 'Unauthorized') {
|
|
return res.status(400).json({ message: 'unauthorized' });
|
|
}
|
|
} else {
|
|
return res.status(400).json({ message: 'unauthorized' });
|
|
}
|
|
next();
|
|
} else {
|
|
next();
|
|
}
|
|
});
|
|
|
|
// Serve static assets from the /public folder
|
|
app.use('/public', express.static(path.join(__dirname, '/public')));
|
|
|
|
// Serve the Parse API on the /parse URL prefix
|
|
if (!process.env.TESTING) {
|
|
const mountPath = process.env.PARSE_MOUNT || '/app';
|
|
try {
|
|
const server = new ParseServer(config);
|
|
await server.start();
|
|
app.use(mountPath, server.app);
|
|
} catch (err) {
|
|
console.log(err);
|
|
process.exit();
|
|
}
|
|
}
|
|
// Mount your custom express app
|
|
app.use('/', customRoute);
|
|
|
|
// Parse Server plays nicely with the rest of your web routes
|
|
app.get('/', function (req, res) {
|
|
res.status(200).send('opensign-server is running !!!');
|
|
});
|
|
|
|
if (!process.env.TESTING) {
|
|
const port = process.env.PORT || 8080;
|
|
const httpServer = http.createServer(app);
|
|
// Set the Keep-Alive and headers timeout to 100 seconds
|
|
httpServer.keepAliveTimeout = 100000; // in milliseconds
|
|
httpServer.headersTimeout = 100000; // in milliseconds
|
|
httpServer.listen(port, '0.0.0.0', function () {
|
|
console.log('opensign-server running on port ' + port + '.');
|
|
const isWindows = process.platform === 'win32';
|
|
// console.log('isWindows', isWindows);
|
|
runDbMigrations();
|
|
const migrate = isWindows
|
|
? `set APPLICATION_ID=${serverAppId}&& set SERVER_URL=${cloudServerUrl}&& set MASTER_KEY=${process.env.MASTER_KEY}&& npx parse-dbtool migrate`
|
|
: `APPLICATION_ID=${serverAppId} SERVER_URL=${cloudServerUrl} MASTER_KEY=${process.env.MASTER_KEY} npx parse-dbtool migrate`;
|
|
exec(migrate, (error, stdout, stderr) => {
|
|
if (error) {
|
|
console.error(`Error: ${error.message}`);
|
|
return;
|
|
}
|
|
|
|
if (stderr) {
|
|
console.error(`Error: ${stderr}`);
|
|
return;
|
|
}
|
|
console.log(`Command output: ${stdout}`);
|
|
});
|
|
});
|
|
}
|