Skip to content

Commit 222c8f6

Browse files
committed
complete API
1 parent a3427cf commit 222c8f6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+10762
-0
lines changed

src/api/server/.DS_Store

6 KB
Binary file not shown.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import WebSocket from 'ws';
2+
import url from 'url';
3+
import security from './security';
4+
5+
let wss = null;
6+
7+
const listen = server => {
8+
wss = new WebSocket.Server({
9+
path: '/ws/dashboard', //Accept only connections matching this path
10+
maxPayload: 1024, //The maximum allowed message size
11+
backlog: 100, //The maximum length of the queue of pending connections.
12+
verifyClient: verifyClient, //An hook to reject connections
13+
server //A pre-created HTTP/S server to use
14+
});
15+
16+
wss.on('connection', onConnection);
17+
wss.broadcast = broadcastToAll;
18+
};
19+
20+
const getTokenFromRequestPath = requestPath => {
21+
try {
22+
const urlObj = url.parse(requestPath, true);
23+
return urlObj.query.token;
24+
} catch (e) {
25+
return null;
26+
}
27+
};
28+
29+
const verifyClient = (info, done) => {
30+
if (security.DEVELOPER_MODE === true) {
31+
done(true);
32+
} else {
33+
const requestPath = info.req.url;
34+
const token = getTokenFromRequestPath(requestPath);
35+
security
36+
.verifyToken(token)
37+
.then(tokenDecoded => {
38+
// TODO: check access to dashboard
39+
done(true);
40+
})
41+
.catch(err => {
42+
done(false, 401);
43+
});
44+
}
45+
};
46+
47+
const onConnection = (ws, req) => {
48+
// TODO: ws.user = token.email
49+
ws.on('error', () => {});
50+
};
51+
52+
const broadcastToAll = data => {
53+
wss.clients.forEach(client => {
54+
if (client.readyState === WebSocket.OPEN) {
55+
client.send(data, error => {});
56+
}
57+
});
58+
};
59+
60+
const send = ({ event, payload }) => {
61+
wss.broadcast(JSON.stringify({ event, payload }));
62+
};
63+
64+
const events = {
65+
ORDER_CREATED: 'order.created',
66+
THEME_INSTALLED: 'theme.installed'
67+
};
68+
69+
export default {
70+
listen: listen,
71+
send: send,
72+
events: events
73+
};

src/api/server/lib/logger.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import winston from 'winston';
2+
const LOGS_FILE = 'logs/server.log';
3+
4+
winston.configure({
5+
transports: [
6+
new winston.transports.Console({
7+
level: 'debug',
8+
handleExceptions: true,
9+
format: winston.format.combine(
10+
winston.format.colorize(),
11+
winston.format.simple()
12+
)
13+
}),
14+
new winston.transports.File({
15+
level: 'info',
16+
handleExceptions: true,
17+
format: winston.format.json(),
18+
filename: LOGS_FILE
19+
})
20+
]
21+
});
22+
23+
const getResponse = message => ({
24+
error: true,
25+
message
26+
});
27+
28+
const logUnauthorizedRequests = req => {
29+
// todo
30+
};
31+
32+
const sendResponse = (err, req, res, next) => {
33+
if (err && err.name === 'UnauthorizedError') {
34+
logUnauthorizedRequests(req);
35+
res.status(401).send(getResponse(err.message));
36+
} else if (err) {
37+
winston.error(err.stack);
38+
res.status(500).send(getResponse(err.message));
39+
} else {
40+
next();
41+
}
42+
};
43+
44+
export default {
45+
sendResponse
46+
};

src/api/server/lib/mailer.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import winston from 'winston';
2+
import nodemailer from 'nodemailer';
3+
import smtpTransport from 'nodemailer-smtp-transport';
4+
import settings from './settings';
5+
import EmailSettingsService from '../services/settings/email';
6+
7+
const SMTP_FROM_CONFIG_FILE = {
8+
host: settings.smtpServer.host,
9+
port: settings.smtpServer.port,
10+
secure: settings.smtpServer.secure,
11+
auth: {
12+
user: settings.smtpServer.user,
13+
pass: settings.smtpServer.pass
14+
}
15+
};
16+
17+
const getSmtpFromEmailSettings = emailSettings => {
18+
return {
19+
host: emailSettings.host,
20+
port: emailSettings.port,
21+
secure: emailSettings.port === 465,
22+
auth: {
23+
user: emailSettings.user,
24+
pass: emailSettings.pass
25+
}
26+
};
27+
};
28+
29+
const getSmtp = emailSettings => {
30+
const useSmtpServerFromConfigFile = emailSettings.host === '';
31+
const smtp = useSmtpServerFromConfigFile
32+
? SMTP_FROM_CONFIG_FILE
33+
: getSmtpFromEmailSettings(emailSettings);
34+
35+
return smtp;
36+
};
37+
38+
const sendMail = (smtp, message) => {
39+
return new Promise((resolve, reject) => {
40+
if (!message.to.includes('@')) {
41+
reject('Invalid email address');
42+
return;
43+
}
44+
45+
const transporter = nodemailer.createTransport(smtpTransport(smtp));
46+
transporter.sendMail(message, (err, info) => {
47+
if (err) {
48+
reject(err);
49+
} else {
50+
resolve(info);
51+
}
52+
});
53+
});
54+
};
55+
56+
const getFrom = emailSettings => {
57+
const useSmtpServerFromConfigFile = emailSettings.host === '';
58+
return useSmtpServerFromConfigFile
59+
? `"${settings.smtpServer.fromName}" <${settings.smtpServer.fromAddress}>`
60+
: `"${emailSettings.from_name}" <${emailSettings.from_address}>`;
61+
};
62+
63+
const send = async message => {
64+
const emailSettings = await EmailSettingsService.getEmailSettings();
65+
const smtp = getSmtp(emailSettings);
66+
message.from = getFrom(emailSettings);
67+
68+
try {
69+
const result = await sendMail(smtp, message);
70+
winston.info('Email sent', result);
71+
return true;
72+
} catch (e) {
73+
winston.error('Email send failed', e);
74+
return false;
75+
}
76+
};
77+
78+
export default {
79+
send: send
80+
};

src/api/server/lib/mongo.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import winston from 'winston';
2+
import url from 'url';
3+
import { MongoClient } from 'mongodb';
4+
import settings from './settings';
5+
6+
const mongodbConnection = settings.mongodbServerUrl;
7+
const mongoPathName = url.parse(mongodbConnection).pathname;
8+
const dbName = mongoPathName.substring(mongoPathName.lastIndexOf('/') + 1);
9+
10+
const RECONNECT_INTERVAL = 1000;
11+
const CONNECT_OPTIONS = {
12+
reconnectTries: 3600,
13+
reconnectInterval: RECONNECT_INTERVAL,
14+
useNewUrlParser: true
15+
};
16+
17+
const onClose = () => {
18+
winston.info('MongoDB connection was closed');
19+
};
20+
21+
const onReconnect = () => {
22+
winston.info('MongoDB reconnected');
23+
};
24+
25+
export let db = null;
26+
27+
const connectWithRetry = () => {
28+
MongoClient.connect(
29+
mongodbConnection,
30+
CONNECT_OPTIONS,
31+
(err, client) => {
32+
if (err) {
33+
winston.error(
34+
`MongoDB connection was failed: ${err.message}`,
35+
err.message
36+
);
37+
setTimeout(connectWithRetry, RECONNECT_INTERVAL);
38+
} else {
39+
db = client.db(dbName);
40+
db.on('close', onClose);
41+
db.on('reconnect', onReconnect);
42+
winston.info('MongoDB connected successfully');
43+
}
44+
}
45+
);
46+
};
47+
48+
connectWithRetry();

src/api/server/lib/parse.js

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { ObjectID } from 'mongodb';
2+
3+
const getString = value => (value || '').toString();
4+
5+
const getDateIfValid = value => {
6+
const date = Date.parse(value);
7+
return isNaN(date) ? null : new Date(date);
8+
};
9+
10+
const getArrayIfValid = value => {
11+
return Array.isArray(value) ? value : null;
12+
};
13+
14+
const getArrayOfObjectID = value => {
15+
if (Array.isArray(value) && value.length > 0) {
16+
return value.map(id => getObjectIDIfValid(id)).filter(id => !!id);
17+
} else {
18+
return [];
19+
}
20+
};
21+
22+
const isNumber = value => !isNaN(parseFloat(value)) && isFinite(value);
23+
24+
const getNumberIfValid = value => (isNumber(value) ? parseFloat(value) : null);
25+
26+
const getNumberIfPositive = value => {
27+
const n = getNumberIfValid(value);
28+
return n && n >= 0 ? n : null;
29+
};
30+
31+
const getBooleanIfValid = (value, defaultValue = null) => {
32+
if (value === 'true' || value === 'false') {
33+
return value === 'true';
34+
} else {
35+
return typeof value === 'boolean' ? value : defaultValue;
36+
}
37+
};
38+
39+
const getObjectIDIfValid = value => {
40+
return ObjectID.isValid(value) ? new ObjectID(value) : null;
41+
};
42+
43+
const getBrowser = browser => {
44+
return browser
45+
? {
46+
ip: getString(browser.ip),
47+
user_agent: getString(browser.user_agent)
48+
}
49+
: {
50+
ip: '',
51+
user_agent: ''
52+
};
53+
};
54+
55+
const getCustomerAddress = address => {
56+
let coordinates = {
57+
latitude: '',
58+
longitude: ''
59+
};
60+
61+
if (address && address.coordinates) {
62+
coordinates.latitude = address.coordinates.latitude;
63+
coordinates.longitude = address.coordinates.longitude;
64+
}
65+
66+
return address
67+
? {
68+
id: new ObjectID(),
69+
address1: getString(address.address1),
70+
address2: getString(address.address2),
71+
city: getString(address.city),
72+
country: getString(address.country).toUpperCase(),
73+
state: getString(address.state),
74+
phone: getString(address.phone),
75+
postal_code: getString(address.postal_code),
76+
full_name: getString(address.full_name),
77+
company: getString(address.company),
78+
tax_number: getString(address.tax_number),
79+
coordinates: coordinates,
80+
details: address.details,
81+
default_billing: false,
82+
default_shipping: false
83+
}
84+
: {};
85+
};
86+
87+
const getOrderAddress = address => {
88+
let coordinates = {
89+
latitude: '',
90+
longitude: ''
91+
};
92+
93+
if (address && address.coordinates) {
94+
coordinates.latitude = address.coordinates.latitude;
95+
coordinates.longitude = address.coordinates.longitude;
96+
}
97+
98+
const emptyAddress = {
99+
address1: '',
100+
address2: '',
101+
city: '',
102+
country: '',
103+
state: '',
104+
phone: '',
105+
postal_code: '',
106+
full_name: '',
107+
company: '',
108+
tax_number: '',
109+
coordinates: coordinates,
110+
details: null
111+
};
112+
113+
return address
114+
? Object.assign(
115+
{},
116+
{
117+
address1: getString(address.address1),
118+
address2: getString(address.address2),
119+
city: getString(address.city),
120+
country: getString(address.country).toUpperCase(),
121+
state: getString(address.state),
122+
phone: getString(address.phone),
123+
postal_code: getString(address.postal_code),
124+
full_name: getString(address.full_name),
125+
company: getString(address.company),
126+
tax_number: getString(address.tax_number),
127+
coordinates: coordinates,
128+
details: address.details
129+
},
130+
address
131+
)
132+
: emptyAddress;
133+
};
134+
135+
export default {
136+
getString: getString,
137+
getObjectIDIfValid: getObjectIDIfValid,
138+
getDateIfValid: getDateIfValid,
139+
getArrayIfValid: getArrayIfValid,
140+
getArrayOfObjectID: getArrayOfObjectID,
141+
getNumberIfValid: getNumberIfValid,
142+
getNumberIfPositive: getNumberIfPositive,
143+
getBooleanIfValid: getBooleanIfValid,
144+
getBrowser: getBrowser,
145+
getCustomerAddress: getCustomerAddress,
146+
getOrderAddress: getOrderAddress
147+
};

0 commit comments

Comments
 (0)