Skip to content

Commit a3427cf

Browse files
committed
complete API
1 parent 44d5b3c commit a3427cf

3 files changed

Lines changed: 462 additions & 0 deletions

File tree

src/api/server/ajaxRouter.js

Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
import express from 'express';
2+
import jwt from 'jsonwebtoken';
3+
import CezerinClient from 'cezerin-client';
4+
import serverSettings from './lib/settings';
5+
const ajaxRouter = express.Router();
6+
7+
const TOKEN_PAYLOAD = { email: 'store', scopes: ['admin'] };
8+
const STORE_ACCESS_TOKEN = jwt.sign(TOKEN_PAYLOAD, serverSettings.jwtSecretKey);
9+
10+
const api = new CezerinClient({
11+
apiBaseUrl: serverSettings.apiBaseUrl,
12+
apiToken: STORE_ACCESS_TOKEN
13+
});
14+
15+
const DEFAULT_CACHE_CONTROL = 'public, max-age=60';
16+
const PRODUCTS_CACHE_CONTROL = 'public, max-age=60';
17+
const PRODUCT_DETAILS_CACHE_CONTROL = 'public, max-age=60';
18+
19+
const getCartCookieOptions = isHttps => ({
20+
maxAge: 24 * 60 * 60 * 1000, // 24 hours
21+
httpOnly: true,
22+
signed: true,
23+
secure: isHttps,
24+
sameSite: 'strict'
25+
});
26+
27+
const getIP = req => {
28+
let ip = req.get('x-forwarded-for') || req.ip;
29+
30+
if (ip && ip.includes(', ')) {
31+
ip = ip.split(', ')[0];
32+
}
33+
34+
if (ip && ip.includes('::ffff:')) {
35+
ip = ip.replace('::ffff:', '');
36+
}
37+
38+
return ip;
39+
};
40+
41+
const getUserAgent = req => {
42+
return req.get('user-agent');
43+
};
44+
45+
const getVariantFromProduct = (product, variantId) => {
46+
if (product.variants && product.variants.length > 0) {
47+
return product.variants.find(
48+
variant => variant.id.toString() === variantId.toString()
49+
);
50+
} else {
51+
return null;
52+
}
53+
};
54+
55+
const fillCartItemWithProductData = (products, cartItem) => {
56+
const product = products.find(p => p.id === cartItem.product_id);
57+
if (product) {
58+
cartItem.image_url =
59+
product.images && product.images.length > 0
60+
? product.images[0].url
61+
: null;
62+
cartItem.path = product.path;
63+
cartItem.stock_backorder = product.stock_backorder;
64+
cartItem.stock_preorder = product.stock_preorder;
65+
if (cartItem.variant_id && cartItem.variant_id.length > 0) {
66+
const variant = getVariantFromProduct(product, cartItem.variant_id);
67+
cartItem.stock_quantity = variant ? variant.stock_quantity : 0;
68+
} else {
69+
cartItem.stock_quantity = product.stock_quantity;
70+
}
71+
}
72+
return cartItem;
73+
};
74+
75+
const fillCartItems = cartResponse => {
76+
let cart = cartResponse.json;
77+
if (cart && cart.items && cart.items.length > 0) {
78+
const productIds = cart.items.map(item => item.product_id);
79+
return api.products
80+
.list({
81+
ids: productIds,
82+
fields:
83+
'images,enabled,stock_quantity,variants,path,stock_backorder,stock_preorder'
84+
})
85+
.then(({ status, json }) => {
86+
const newCartItem = cart.items.map(cartItem =>
87+
fillCartItemWithProductData(json.data, cartItem)
88+
);
89+
cartResponse.json.items = newCartItem;
90+
return cartResponse;
91+
});
92+
} else {
93+
return Promise.resolve(cartResponse);
94+
}
95+
};
96+
97+
ajaxRouter.get('/products', (req, res, next) => {
98+
let filter = req.query;
99+
filter.enabled = true;
100+
api.products.list(filter).then(({ status, json }) => {
101+
res
102+
.status(status)
103+
.header('Cache-Control', PRODUCTS_CACHE_CONTROL)
104+
.send(json);
105+
});
106+
});
107+
108+
ajaxRouter.get('/products/:id', (req, res, next) => {
109+
api.products.retrieve(req.params.id).then(({ status, json }) => {
110+
res
111+
.status(status)
112+
.header('Cache-Control', PRODUCT_DETAILS_CACHE_CONTROL)
113+
.send(json);
114+
});
115+
});
116+
117+
ajaxRouter.get('/cart', (req, res, next) => {
118+
const order_id = req.signedCookies.order_id;
119+
if (order_id) {
120+
api.orders
121+
.retrieve(order_id)
122+
.then(cartResponse => fillCartItems(cartResponse))
123+
.then(({ status, json }) => {
124+
json.browser = undefined;
125+
res.status(status).send(json);
126+
});
127+
} else {
128+
res.end();
129+
}
130+
});
131+
132+
ajaxRouter.post('/cart/items', (req, res, next) => {
133+
const isHttps = req.protocol === 'https';
134+
const CART_COOKIE_OPTIONS = getCartCookieOptions(isHttps);
135+
136+
const order_id = req.signedCookies.order_id;
137+
const item = req.body;
138+
if (order_id) {
139+
api.orders.items
140+
.create(order_id, item)
141+
.then(cartResponse => fillCartItems(cartResponse))
142+
.then(({ status, json }) => {
143+
res.status(status).send(json);
144+
});
145+
} else {
146+
let orderDraft = {
147+
draft: true,
148+
referrer_url: req.signedCookies.referrer_url,
149+
landing_url: req.signedCookies.landing_url,
150+
browser: {
151+
ip: getIP(req),
152+
user_agent: getUserAgent(req)
153+
},
154+
shipping_address: {}
155+
};
156+
157+
api.settings
158+
.retrieve()
159+
.then(settingsResponse => {
160+
const storeSettings = settingsResponse.json;
161+
orderDraft.shipping_address.country =
162+
storeSettings.default_shipping_country;
163+
orderDraft.shipping_address.state =
164+
storeSettings.default_shipping_state;
165+
orderDraft.shipping_address.city = storeSettings.default_shipping_city;
166+
return orderDraft;
167+
})
168+
.then(orderDraft => {
169+
api.orders.create(orderDraft).then(orderResponse => {
170+
const orderId = orderResponse.json.id;
171+
res.cookie('order_id', orderId, CART_COOKIE_OPTIONS);
172+
api.orders.items
173+
.create(orderId, item)
174+
.then(cartResponse => fillCartItems(cartResponse))
175+
.then(({ status, json }) => {
176+
res.status(status).send(json);
177+
});
178+
});
179+
});
180+
}
181+
});
182+
183+
ajaxRouter.delete('/cart/items/:item_id', (req, res, next) => {
184+
const order_id = req.signedCookies.order_id;
185+
const item_id = req.params.item_id;
186+
if (order_id && item_id) {
187+
api.orders.items
188+
.delete(order_id, item_id)
189+
.then(cartResponse => fillCartItems(cartResponse))
190+
.then(({ status, json }) => {
191+
res.status(status).send(json);
192+
});
193+
} else {
194+
res.end();
195+
}
196+
});
197+
198+
ajaxRouter.put('/cart/items/:item_id', (req, res, next) => {
199+
const order_id = req.signedCookies.order_id;
200+
const item_id = req.params.item_id;
201+
const item = req.body;
202+
if (order_id && item_id) {
203+
api.orders.items
204+
.update(order_id, item_id, item)
205+
.then(cartResponse => fillCartItems(cartResponse))
206+
.then(({ status, json }) => {
207+
res.status(status).send(json);
208+
});
209+
} else {
210+
res.end();
211+
}
212+
});
213+
214+
ajaxRouter.put('/cart/checkout', (req, res, next) => {
215+
const order_id = req.signedCookies.order_id;
216+
if (order_id) {
217+
api.orders
218+
.checkout(order_id)
219+
.then(cartResponse => fillCartItems(cartResponse))
220+
.then(({ status, json }) => {
221+
res.clearCookie('order_id');
222+
res.status(status).send(json);
223+
});
224+
} else {
225+
res.end();
226+
}
227+
});
228+
229+
ajaxRouter.put('/cart', async (req, res, next) => {
230+
const cartData = req.body;
231+
const {
232+
shipping_address: shippingAddress,
233+
billing_address: billingAddress
234+
} = cartData;
235+
const orderId = req.signedCookies.order_id;
236+
if (orderId) {
237+
if (shippingAddress) {
238+
await api.orders.updateShippingAddress(orderId, shippingAddress);
239+
}
240+
if (billingAddress) {
241+
await api.orders.updateBillingAddress(orderId, billingAddress);
242+
}
243+
244+
await api.orders
245+
.update(orderId, cartData)
246+
.then(cartResponse => fillCartItems(cartResponse))
247+
.then(({ status, json }) => {
248+
res.status(status).send(json);
249+
});
250+
} else {
251+
res.end();
252+
}
253+
});
254+
255+
ajaxRouter.put('/cart/shipping_address', (req, res, next) => {
256+
const order_id = req.signedCookies.order_id;
257+
if (order_id) {
258+
api.orders
259+
.updateShippingAddress(order_id, req.body)
260+
.then(cartResponse => fillCartItems(cartResponse))
261+
.then(({ status, json }) => {
262+
res.status(status).send(json);
263+
});
264+
} else {
265+
res.end();
266+
}
267+
});
268+
269+
ajaxRouter.put('/cart/billing_address', (req, res, next) => {
270+
const order_id = req.signedCookies.order_id;
271+
if (order_id) {
272+
api.orders
273+
.updateBillingAddress(order_id, req.body)
274+
.then(cartResponse => fillCartItems(cartResponse))
275+
.then(({ status, json }) => {
276+
res.status(status).send(json);
277+
});
278+
} else {
279+
res.end();
280+
}
281+
});
282+
283+
ajaxRouter.post('/cart/charge', async (req, res, next) => {
284+
const order_id = req.signedCookies.order_id;
285+
if (order_id) {
286+
const client = api.orders.client;
287+
const chargeResponse = await client.post(`/orders/${order_id}/charge`);
288+
res.status(chargeResponse.status).send(chargeResponse.json);
289+
} else {
290+
res.end();
291+
}
292+
});
293+
294+
ajaxRouter.get('/pages', (req, res, next) => {
295+
api.pages.list(req.query).then(({ status, json }) => {
296+
res
297+
.status(status)
298+
.header('Cache-Control', DEFAULT_CACHE_CONTROL)
299+
.send(json);
300+
});
301+
});
302+
303+
ajaxRouter.get('/pages/:id', (req, res, next) => {
304+
api.pages.retrieve(req.params.id).then(({ status, json }) => {
305+
res
306+
.status(status)
307+
.header('Cache-Control', DEFAULT_CACHE_CONTROL)
308+
.send(json);
309+
});
310+
});
311+
312+
ajaxRouter.get('/sitemap', async (req, res, next) => {
313+
let result = null;
314+
let filter = req.query;
315+
filter.enabled = true;
316+
317+
const sitemapResponse = await api.sitemap.retrieve(req.query);
318+
if (sitemapResponse.status !== 404 || sitemapResponse.json) {
319+
result = sitemapResponse.json;
320+
321+
if (result.type === 'product') {
322+
const productResponse = await api.products.retrieve(result.resource);
323+
result.data = productResponse.json;
324+
} else if (result.type === 'page') {
325+
const pageResponse = await api.pages.retrieve(result.resource);
326+
result.data = pageResponse.json;
327+
}
328+
}
329+
330+
res
331+
.status(sitemapResponse.status)
332+
.header('Cache-Control', DEFAULT_CACHE_CONTROL)
333+
.send(result);
334+
});
335+
336+
ajaxRouter.get('/payment_methods', (req, res, next) => {
337+
const filter = {
338+
enabled: true,
339+
order_id: req.signedCookies.order_id
340+
};
341+
api.paymentMethods.list(filter).then(({ status, json }) => {
342+
const methods = json.map(item => {
343+
delete item.conditions;
344+
return item;
345+
});
346+
347+
res.status(status).send(methods);
348+
});
349+
});
350+
351+
ajaxRouter.get('/shipping_methods', (req, res, next) => {
352+
const filter = {
353+
enabled: true,
354+
order_id: req.signedCookies.order_id
355+
};
356+
api.shippingMethods.list(filter).then(({ status, json }) => {
357+
res.status(status).send(json);
358+
});
359+
});
360+
361+
ajaxRouter.get('/payment_form_settings', (req, res, next) => {
362+
const order_id = req.signedCookies.order_id;
363+
if (order_id) {
364+
api.orders.getPaymentFormSettings(order_id).then(({ status, json }) => {
365+
res.status(status).send(json);
366+
});
367+
} else {
368+
res.end();
369+
}
370+
});
371+
372+
export default ajaxRouter;

0 commit comments

Comments
 (0)