diff --git a/services/news/src/views/index.ejs b/services/news/src/views/index.ejs index 93d9340c..0885d21c 100644 --- a/services/news/src/views/index.ejs +++ b/services/news/src/views/index.ejs @@ -179,7 +179,10 @@ } async function setupDemo () { - if ('<%= isMultiSeller %>' !== 'true') { + const searchParams = new URLSearchParams(window.location.search); + const multiSellerSearchParams = searchParams.get('auctionType'); + + if (multiSellerSearchParams !== 'multi') { document.getElementById('video-ad-container').style.display = 'none' addScriptEl('<%= SSP_TAG_URL %>', 'ssp_tag') return diff --git a/services/shop/src/index.js b/services/shop/src/index.js index e698b595..a0e5f735 100644 --- a/services/shop/src/index.js +++ b/services/shop/src/index.js @@ -16,182 +16,159 @@ import express from 'express'; import session from 'express-session'; import MemoryStoreFactory from 'memorystore'; -import { - DSP_HOST, - DSP_A_HOST, - DSP_B_HOST, - EXTERNAL_PORT, - PORT, - SHOP_DETAIL, - SHOP_HOST, -} from './env.js'; -import { - addOrder, - displayCategory, - fromSize, - getItem, - getItems, - removeOrder, - updateOrder, -} from './lib/items.js'; +import { DSP_HOST, DSP_A_HOST, DSP_B_HOST, EXTERNAL_PORT, PORT, SHOP_DETAIL, SHOP_HOST, } from './env.js'; +import { addOrder, displayCategory, fromSize, getItem, getItems, removeOrder, updateOrder, } from './lib/items.js'; const app = express(); app.set('trust proxy', 1); // required for Set-Cookie with Secure // Due to express >= 4 changes, we need to pass express-session to the // function memorystore exports in order to extend session.Store: const MemoryStore = MemoryStoreFactory(session); const oneDay = 1000 * 60 * 60 * 24; -app.use( - session({ +app.use(session({ name: '__session', secret: 'THIS IS SECRET FOR DEMO', resave: false, saveUninitialized: true, cookie: { - secure: true, - sameSite: 'strict', - maxAge: oneDay, + secure: true, + sameSite: 'strict', + maxAge: oneDay, }, store: new MemoryStore({ - checkPeriod: oneDay, + checkPeriod: oneDay, }), - }), -); +})); app.use((req, res, next) => { - // res.setHeader("Origin-Trial", NEWS_TOKEN as string) - res.setHeader('Cache-Control', 'private'); - if (!req.session.cart) { - req.session.cart = []; - } - next(); + // res.setHeader("Origin-Trial", NEWS_TOKEN as string) + res.setHeader('Cache-Control', 'private'); + if (!req.session.cart) { + req.session.cart = []; + } + next(); }); -app.use(express.urlencoded({extended: true})); +app.use(express.urlencoded({ extended: true })); app.use(express.static('src/public')); app.set('view engine', 'ejs'); app.set('views', 'src/views'); // view helper app.locals = { - title: SHOP_DETAIL, - displayCategory, - register_trigger: (order) => { - const {item, size, quantity} = order; - const register_trigger = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}`); - register_trigger.pathname = '/register-trigger'; - register_trigger.searchParams.append('id', item.id); - register_trigger.searchParams.append('category', `${item.category}`); - register_trigger.searchParams.append('quantity', `${quantity}`); - register_trigger.searchParams.append('size', `${fromSize(size)}`); - register_trigger.searchParams.append('gross', `${item.price * quantity}`); - return register_trigger.toString(); - }, + title: SHOP_DETAIL, + displayCategory, + register_trigger: (order) => { + const { item, size, quantity } = order; + const register_trigger = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}`); + register_trigger.pathname = '/register-trigger'; + register_trigger.searchParams.append('id', item.id); + register_trigger.searchParams.append('category', `${item.category}`); + register_trigger.searchParams.append('quantity', `${quantity}`); + register_trigger.searchParams.append('size', `${fromSize(size)}`); + register_trigger.searchParams.append('gross', `${item.price * quantity}`); + return register_trigger.toString(); + }, }; app.get('/', async (req, res) => { - const items = await getItems(); - res.render('index', { - items, - }); + const items = await getItems(); + res.render('index', { + items, + }); }); // serves the static ads creative from shop site (redirected from ssp) app.get('/ads/:id', async (req, res) => { - const id = req.params.id; - const imgPath = `/image/svg/emoji_u${id}.svg`; - //res.set("Content-Type", "image/svg+xml") - console.log(`redirecting to /image/svg/emoji_u${id}.svg`); - res.redirect(301, imgPath); + const id = req.params.id; + const imgPath = `/image/svg/emoji_u${id}.svg`; + //res.set("Content-Type", "image/svg+xml") + console.log(`redirecting to /image/svg/emoji_u${id}.svg`); + res.redirect(301, imgPath); }); app.get('/items/:id', async (req, res) => { - const {id} = req.params; - const item = await getItem(id); - const isMultiSeller = req.query.auctionType === 'multi'; - const DSP_TAG_URL = new URL( - `https://${DSP_HOST}:${EXTERNAL_PORT}/dsp-tag.js`, - ); - const DSP_A_TAG_URL = new URL( - `https://${DSP_A_HOST}:${EXTERNAL_PORT}/dsp-tag.js`, - ); - const DSP_B_TAG_URL = new URL( - `https://${DSP_B_HOST}:${EXTERNAL_PORT}/dsp-tag.js`, - ); - res.render('item', { - item, - DSP_TAG_URL, - DSP_A_TAG_URL, - DSP_B_TAG_URL, - SHOP_HOST, - isMultiSeller, - }); + const { id } = req.params; + const item = await getItem(id); + const isMultiSeller = req.query.auctionType === 'multi'; + const DSP_TAG_URL = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}/dsp-tag.js`); + const DSP_A_TAG_URL = new URL(`https://${DSP_A_HOST}:${EXTERNAL_PORT}/dsp-tag.js`); + const DSP_B_TAG_URL = new URL(`https://${DSP_B_HOST}:${EXTERNAL_PORT}/dsp-tag.js`); + res.render('item', { + item, + DSP_TAG_URL, + DSP_A_TAG_URL, + DSP_B_TAG_URL, + SHOP_HOST, + isMultiSeller, + }); }); app.post('/cart', async (req, res, next) => { - const {id, size, quantity} = req.body; - const item = await getItem(id); - const order = {item, size, quantity}; - const cart = addOrder(order, req.session.cart); - req.session.cart = cart; - // save the session before redirection to ensure page - // load does not happen before session is saved - req.session.save(function (err) { - if (err) return next(err); - // console.log("Save session before redirect") - // console.log(req.session) - res.redirect(303, '/cart'); - }); - //res.redirect(303, "/cart") + const { id, size, quantity } = req.body; + const item = await getItem(id); + const order = { item, size, quantity }; + const cart = addOrder(order, req.session.cart); + req.session.cart = cart; + // save the session before redirection to ensure page + // load does not happen before session is saved + req.session.save(function (err) { + if (err) + return next(err); + // console.log("Save session before redirect") + // console.log(req.session) + res.redirect(303, '/cart'); + }); + //res.redirect(303, "/cart") }); app.get('/cart', async (req, res) => { - const cart = req.session.cart; - const subtotal = cart.reduce((sum, {item, quantity}) => { - return sum + item.price * quantity; - }, 0); - const shipping = 40; - res.render('cart', { - cart, - subtotal, - shipping, - }); + const cart = req.session.cart; + const subtotal = cart.reduce((sum, { item, quantity }) => { + return sum + item.price * quantity; + }, 0); + const shipping = 40; + res.render('cart', { + cart, + subtotal, + shipping, + }); }); app.put('/cart/:name', async (req, res) => { - const {name} = req.params; - const {quantity} = req.body; - const [id, size] = name.split(':'); - const item = await getItem(id); - const order = { - item, - size, - quantity, - }; - const cart = updateOrder(order, req.session.cart); - req.session.cart = cart; - res.status(204).end(); + const { name } = req.params; + const { quantity } = req.body; + const [id, size] = name.split(':'); + const item = await getItem(id); + const order = { + item, + size, + quantity, + }; + const cart = updateOrder(order, req.session.cart); + req.session.cart = cart; + res.status(204).end(); }); app.delete('/cart/:name', async (req, res) => { - const {name} = req.params; - const [id, size] = name.split(':'); - const item = await getItem(id); - const order = { - item, - size: size, - quantity: 0, - }; - const cart = removeOrder(order, req.session.cart); - req.session.cart = cart; - res.status(204).end(); + const { name } = req.params; + const [id, size] = name.split(':'); + const item = await getItem(id); + const order = { + item, + size: size, + quantity: 0, + }; + const cart = removeOrder(order, req.session.cart); + req.session.cart = cart; + res.status(204).end(); }); app.post('/checkout', async (req, res) => { - const body = req.body; - res.redirect(303, '/checkout'); + const body = req.body; + res.redirect(303, '/checkout'); }); app.get('/checkout', async (req, res) => { - const cart = req.session.cart; - const subtotal = cart.reduce((sum, {item, quantity}) => { - return sum + item.price * quantity; - }, 0); - const shipping = 40; - await req.session.destroy(() => Promise.resolve()); - res.render('checkout', { - cart, - subtotal, - shipping, - }); + const cart = req.session.cart; + const subtotal = cart.reduce((sum, { item, quantity }) => { + return sum + item.price * quantity; + }, 0); + const shipping = 40; + await req.session.destroy(() => Promise.resolve()); + res.render('checkout', { + cart, + subtotal, + shipping, + }); }); app.listen(PORT, async () => { - console.log(`Listening on port ${PORT}`); + console.log(`Listening on port ${PORT}`); }); diff --git a/services/shop/src/lib/items.js b/services/shop/src/lib/items.js index 2a5cddcb..e3721a98 100644 --- a/services/shop/src/lib/items.js +++ b/services/shop/src/lib/items.js @@ -14,61 +14,62 @@ limitations under the License. */ const items = [ - {id: '1f45e', icon: '👞', price: 180, category: 1, name: "Man's Shoe"}, - {id: '1f45f', icon: '👟', price: 100, category: 0, name: 'Running Shoe'}, - {id: '1f460', icon: '👠', price: 200, category: 1, name: 'High-Heeled Shoe'}, - {id: '1f461', icon: '👡', price: 120, category: 0, name: "Woman's Sandal"}, - {id: '1f462', icon: '👢', price: 400, category: 1, name: "Woman's Boot"}, - {id: '1f6fc', icon: '🛼', price: 230, category: 2, name: 'Roller Skate'}, - {id: '1f97e', icon: '🥾', price: 210, category: 2, name: 'Hiking Boot'}, - {id: '1f97f', icon: '🥿', price: 140, category: 0, name: 'Flat Shoe'}, - {id: '1fa70', icon: '🩰', price: 900, category: 2, name: 'Ballet Shoes'}, - {id: '1fa74', icon: '🩴', price: 12, category: 0, name: 'Thong Sandal'}, - {id: '1f3bf', icon: '🎿', price: 1120, category: 2, name: 'Ski Boots'}, - {id: '26f8', icon: '⛸', price: 1200, category: 2, name: 'Ice Skate'}, + { id: '1f45e', icon: '👞', price: 180, category: 1, name: "Man's Shoe" }, + { id: '1f45f', icon: '👟', price: 100, category: 0, name: 'Running Shoe' }, + { id: '1f460', icon: '👠', price: 200, category: 1, name: 'High-Heeled Shoe' }, + { id: '1f461', icon: '👡', price: 120, category: 0, name: "Woman's Sandal" }, + { id: '1f462', icon: '👢', price: 400, category: 1, name: "Woman's Boot" }, + { id: '1f6fc', icon: '🛼', price: 230, category: 2, name: 'Roller Skate' }, + { id: '1f97e', icon: '🥾', price: 210, category: 2, name: 'Hiking Boot' }, + { id: '1f97f', icon: '🥿', price: 140, category: 0, name: 'Flat Shoe' }, + { id: '1fa70', icon: '🩰', price: 900, category: 2, name: 'Ballet Shoes' }, + { id: '1fa74', icon: '🩴', price: 12, category: 0, name: 'Thong Sandal' }, + { id: '1f3bf', icon: '🎿', price: 1120, category: 2, name: 'Ski Boots' }, + { id: '26f8', icon: '⛸', price: 1200, category: 2, name: 'Ice Skate' }, ]; export const CATEGORIES = ['sale', 'luxury', 'sports']; export async function getItems() { - return items; + return items; } export async function getItem(id) { - return items.find((item) => { - return item.id === id; - }); + return items.find((item) => { + return item.id === id; + }); } export function displayCategory(id) { - return CATEGORIES.at(id) || 'N/A'; + return CATEGORIES.at(id) || 'N/A'; } export function toSize(num) { - return `${num / 10 + 20}`; + return `${num / 10 + 20}`; } export function fromSize(size) { - return (Number(size) - 20) * 10; + return (Number(size) - 20) * 10; } export const addOrder = (order, state) => { - const index = state.findIndex(({item, size}) => { - return order.item.id === item.id && order.size === size; - }); - if (index > -1) { - // increase quantity - const current = state.at(index); - const next = {...order, quantity: order.quantity + current.quantity}; - return [next, ...state.slice(0, index), ...state.slice(index + 1)]; - } - // append - return [order, ...state]; + const index = state.findIndex(({ item, size }) => { + return order.item.id === item.id && order.size === size; + }); + if (index > -1) { + // increase quantity + const current = state.at(index); + const next = { ...order, quantity: order.quantity + current.quantity }; + return [next, ...state.slice(0, index), ...state.slice(index + 1)]; + } + // append + return [order, ...state]; }; export const removeOrder = (order, state) => { - return state.reduce((acc, o) => { - if (o.item.id === order.item.id && o.size === order.size) return acc; - return [...acc, o]; - }, []); + return state.reduce((acc, o) => { + if (o.item.id === order.item.id && o.size === order.size) + return acc; + return [...acc, o]; + }, []); }; export const updateOrder = (order, state) => { - return state.reduce((acc, o) => { - if (order.item.id === o.item.id && order.size === o.size) { - return [...acc, order]; - } - return [...acc, o]; - }, []); + return state.reduce((acc, o) => { + if (order.item.id === o.item.id && order.size === o.size) { + return [...acc, order]; + } + return [...acc, o]; + }, []); }; diff --git a/services/shop/src/public/css/global.css b/services/shop/src/public/css/global.css index 29da48a9..c51b25ab 100644 --- a/services/shop/src/public/css/global.css +++ b/services/shop/src/public/css/global.css @@ -58,23 +58,9 @@ html { -moz-tab-size: 4; /* 3 */ -o-tab-size: 4; - tab-size: 4; + tab-size: 4; /* 3 */ - font-family: - ui-sans-serif, - system-ui, - -apple-system, - BlinkMacSystemFont, - 'Segoe UI', - Roboto, - 'Helvetica Neue', - Arial, - 'Noto Sans', - sans-serif, - 'Apple Color Emoji', - 'Segoe UI Emoji', - 'Segoe UI Symbol', - 'Noto Color Emoji'; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */ font-feature-settings: normal; /* 5 */ @@ -115,7 +101,7 @@ Add the correct text decoration in Chrome, Edge, and Safari. abbr:where([title]) { -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; + text-decoration: underline dotted; } /* @@ -159,8 +145,7 @@ code, kbd, samp, pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, - 'Liberation Mono', 'Courier New', monospace; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */ font-size: 1em; /* 2 */ @@ -397,8 +382,7 @@ textarea { 2. Set the default placeholder color to the user's configured gray 400 color. */ -input::-moz-placeholder, -textarea::-moz-placeholder { +input::-moz-placeholder, textarea::-moz-placeholder { opacity: 1; /* 1 */ color: #9ca3af; @@ -418,7 +402,7 @@ Set the default cursor for buttons. */ button, -[role='button'] { +[role="button"] { cursor: pointer; } @@ -466,9 +450,7 @@ video { display: none; } -*, -::before, -::after { +*, ::before, ::after { --tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-translate-x: 0; @@ -705,12 +687,12 @@ video { .object-cover { -o-object-fit: cover; - object-fit: cover; + object-fit: cover; } .object-\[0\%_5\%\] { -o-object-position: 0% 5%; - object-position: 0% 5%; + object-position: 0% 5%; } .px-2 { @@ -759,8 +741,7 @@ video { } .font-mono { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, - 'Liberation Mono', 'Courier New', monospace; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } .text-2xl { @@ -823,10 +804,8 @@ video { .shadow { --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), - 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), - var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } body {