Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0648dcb96c |
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.env
|
||||||
|
**/node_modules/
|
||||||
|
package-lock.json
|
||||||
|
*.swp
|
||||||
|
.prettyrc
|
||||||
|
.vscode
|
||||||
1
api/.gitignore
vendored
1
api/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
.env
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import fetchEbayApplicationToken from "../utils/fetchEbayApplicationToken.js";
|
import fetchEbayReadToken from "../utils/fetchEbayReadToken.js"; // Adjust the import path according to your project structure
|
||||||
|
|
||||||
export const itemLookup = async (req, res) => {
|
export const itemLookup = async (req, res) => {
|
||||||
const productCode = req.query.productCode;
|
const productCode = req.query.productCode;
|
||||||
const token = await fetchEbayApplicationToken();
|
const oauthToken = await fetchEbayReadToken();
|
||||||
console.log(productCode);
|
console.log(productCode);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -11,7 +11,7 @@ export const itemLookup = async (req, res) => {
|
|||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${oauthToken}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,265 +1,45 @@
|
|||||||
|
// inventoryController.js
|
||||||
|
import buildAddFixedPriceItemRequestXML from "../utils/buildAddFixedPriceItemRequestXML.js";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import fetchEbayUserToken from "../utils/fetchEbayUserToken.js";
|
import fetchEbayUserToken from "../utils/fetchEbayUserToken.js"; // Adjust the import path according to your project structure
|
||||||
|
|
||||||
export const createAndListItem = async (req, res) => {
|
export const addItem = async (req, res) => {
|
||||||
const {
|
const itemDetails = req.body; // Assuming item details are sent in the request body
|
||||||
sku,
|
itemDetails.userToken =
|
||||||
marketplaceId,
|
"v^1.1#i^1#r^0#I^3#f^0#p^3#t^H4sIAAAAAAAAAOVZf2wbVx2P86Nd1HWAViWsLZrntQMtPd+7O9vnO2ozJ3bidPnhxnaXBKHo3d075zX3q/fu4ngTKI22wIS6CaqhijIUxoSEJjQ2YCCtUHVD2h/AtE6CakKiILSu4w8KRQwmYOLO+VE3U3/EDqol/M/53n1/fb4/770D81s671/MLv5je2Br69I8mG8NBJhtoHNLR88dba07O1pADUFgaX7PfPtC28X9BOqaJY4hYpkGQcE5XTOIWF1MhFzbEE1IMBENqCMiOrKYTw0PiWwYiJZtOqZsaqHgYDoR4gQ1zsQERQE8w3AxwVs1VmUWzEQIMjwAQkyNCHxc5TjgPSfERYMGcaDhJEIsYCMU4CiWKTCCGAUiAGGBFyZDwUPIJtg0PJIwCCWr5opVXrvG1uubCglBtuMJCSUHU/350dRgOjNS2E/XyEqu+CHvQMclV9/1mQoKHoKai66vhlSpxbwry4iQEJ1c1nC1UDG1akwd5lddzQtcjFeRzHMgGpPjaFNc2W/aOnSub4e/ghVKrZKKyHCwU7mRRz1vSIeR7KzcjXgiBtNB/3LQhRpWMbIToUxvaqKYz4yFgvlczjZnsYIUHykbY0AsxvM8F0paNtaRho+4WIG+L1RsKITjVlQuy11x+DqdfaahYJ+FBEdMpxd59qP1XorUeMkjGjVG7ZTq+LbV0LHMmjejk354l+PpOtOGH2Gkey4JVm9vHIvV5LiSDpuVHiyrCKogKJIKUBww/Ep6+LXeUIok/SilcjnatwVJsELp0J5BjqVBGVGy515XRzZWRC6qslxcRZQSE1QqIqgqJUWVGMWoCAGEJEkW4v+fmeI4NpZcB61ly/oHVbiJUF42LZQzNSxXQutJqn1oJTfmSCI07TiWSNPlcjlc5sKmXaJZABh6fHgoL08jHYbWaPGNiSlczRDZ6ykevehULM+aOS8JPeVGKZTkbCUHbaeSR5rmLaym8FW2JdevXgNkn4Y9DxQ8Fc2FMWsSBykNQVPQLJbRFFZuETK/1q+BjmWEaIT3SiUCQLQhkJpZwsYwcqbNWwXzGhAzw6nBoYaged0UOs0FqrYJsatNCPAU4L1/DYFNWdagrrsOlDQ02GShjHgNPc40BM9y3VtWh9dAZT6sz/Lyw7EjHGwImj+ERb/WMVRFx5xBRvN107FM/1gmn50qjD6YGWkI7RhSbUSmCz7OZsvT1MHUgZT3G+4VCtlcL9APFA9l7QltTp9UmTTmsxwuThxhyw6TS+OeUfnwXLGnTGjWOFKuGENmsTzQO17IjD1EH0wkGnJSHsk2arLWNX64kBbyA1LKnaDTWQtN9tuj/dwQN53q0elocaDc2zsRSRV6xtVSY+CHS81W6Zs3batp79d685W4vVyYU9UONOXdNQQ0U2q6fg1jgFGkSISJSwDKPKso3lWKQ1WVBIGTpYbHb5PhLVQ0ZOdcjfIHDNYtk8qNpSkoxDnA8rJKCVJMiktSY8PLarowb9ZYJv7urWFoK+/wmwfP5yeeAGjhsB/YsGzqtAldZ9pfmqpaHbwZIpp4u7/w8ubfkxy2EVRMQ6vUw7wBHmzMevtF067Uo3CNeQM8UJZN13DqUbfCugEO1dVUrGn+oUA9CmvYN2KmAbWKg2VSl0ps+NlGNsBiwUoVoIKJ5dfLTXF6azqyZRTGyvKRYz3G2shTWD0NqotpgyrXTDZMB6tYXpZBXInINrY+bIVf6/XLqscfxKuFDYVumWFNVWPba6RgG8nOlGvj5hoB1ck35Y0+gqh1U5DSkDKjV8yGkPuebcYjk1wqn39odCzdELg0mm22NxlV5RDgZECxKKZQkRirUt6LG0OpcZ6PA6hADjZ20rfxc6L2o7/8H4NmeIYBURAVhJuFtm6h5nj6Q98o6Ks/FyZbqj9mIfAKWAj8rDUQAPvBXuZecM+WtmJ72+07CXa8zg3VMMElAzqujcIzqGJBbLfe2XL5maeyfTszo1+7/5FC5Y2Tr7XcXvO1culz4ONr3ys725htNR8vwe4rTzqYj3RvZyOAYxlvtwUAmAT3XnnaznS170DfPVe66y8zj59/7zL9aHLg6HPHXimC7WtEgUBHS/tCoGXhAv3uyO/Gz8gf7DjPn7rjm7suJWInTle2HrvISn8MHbv75B9gl/DkF9+57e7vLQ088dzx9NP2qy+h0z+U9m6d6/70j8+d/cF7u5a+3fc6aQlkfv+VF8Lf+flPs/R/7jox0bP13d17U0v3DL5zZvGjv33xr2/96/BTn1/s7tzdsfTsSBeZ/0Tuto4dU5k9f+rdta+r+Frq/e7Pvn36+Y6Xf/Po+08kzh4dv/TMBz/ZfuLJb2Tf1sao7IL6z5dePv6jT77xq19MHy/NPuYKr3+r/yz/sS/8+q39X7Ze7DrX/7evX3h2T/bvb54ipx54IT+9T7tA/1u5RN/32Kcun7l47s/7WvJvSo+3fX9x29NffeAzrx7ofOT8l86c3Ne9HMv/AqyXZcRHHgAA";
|
||||||
format,
|
console.log(itemDetails);
|
||||||
listingDuration,
|
const xmlRequest = buildAddFixedPriceItemRequestXML(itemDetails);
|
||||||
availableQuantity,
|
|
||||||
categoryId,
|
|
||||||
condition,
|
|
||||||
listingDescription,
|
|
||||||
pricingSummary,
|
|
||||||
mpn,
|
|
||||||
merchantLocationKey,
|
|
||||||
quantityLimitPerBuyer,
|
|
||||||
product, // Extract the entire product object
|
|
||||||
packageWeightAndSize, // Adjusted to packageWeightAndSize from the refined payload
|
|
||||||
imageUrls,
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
const token = await fetchEbayUserToken(req, res); // Get the eBay user token
|
|
||||||
|
|
||||||
// only need these if payloads is there?
|
|
||||||
let dynamicAspects = {};
|
|
||||||
for (const [key, value] of Object.entries(product.aspects)) {
|
|
||||||
dynamicAspects[key] = Array.isArray(value) ? value : [value];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Create the inventory item
|
|
||||||
const inventoryItemPayload = {
|
|
||||||
sku,
|
|
||||||
condition,
|
|
||||||
quantity: availableQuantity,
|
|
||||||
product: {
|
|
||||||
title: product.title.substring(0, 80), // Utilizing product title
|
|
||||||
brand: product.brand, // Using brand from the product object
|
|
||||||
mpn: mpn,
|
|
||||||
description: product.description, // Using product description
|
|
||||||
imageUrls: product.imageUrls, // Using all imageUrls from product
|
|
||||||
},
|
|
||||||
availability: {
|
|
||||||
shipToLocationAvailability: {
|
|
||||||
quantity: availableQuantity,
|
|
||||||
availabilityDistributions: [ // Adding availability distributions
|
|
||||||
{
|
|
||||||
availabilityType: "IN_STOCK", // Assuming items are in stock for pickup
|
|
||||||
fulfillmentTime: { // Fulfillment time for shipping
|
|
||||||
unit: "DAY",
|
|
||||||
value: 1 // Adjust based on your shipping fulfillment time
|
|
||||||
},
|
|
||||||
merchantLocationKey: merchantLocationKey, // Specify the location for shipping availability
|
|
||||||
quantity: availableQuantity // Quantity available for shipping from the specified location
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
packageWeightAndSize: {
|
|
||||||
weight: {
|
|
||||||
value: parseFloat(packageWeightAndSize.weight.value),
|
|
||||||
unit: packageWeightAndSize.weight.unit,
|
|
||||||
},
|
|
||||||
dimensions: {
|
|
||||||
length: parseFloat(packageWeightAndSize.dimensions.length),
|
|
||||||
width: parseFloat(packageWeightAndSize.dimensions.width),
|
|
||||||
height: parseFloat(packageWeightAndSize.dimensions.height),
|
|
||||||
unit: packageWeightAndSize.dimensions.unit,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createItemResponse = await fetch(
|
|
||||||
`https://api.ebay.com/sell/inventory/v1/inventory_item/${sku}`,
|
|
||||||
{
|
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Content-Language": "en-US",
|
|
||||||
"Authorization": `Bearer ${token}`,
|
|
||||||
"Accept": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(inventoryItemPayload),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Optionally, you can parse the response body if eBay's API returns useful information in it
|
|
||||||
const itemResponseData = await createItemResponse;
|
|
||||||
|
|
||||||
// 2. Create the offer for the inventory item
|
|
||||||
const offerPayload = {
|
|
||||||
sku,
|
|
||||||
marketplaceId,
|
|
||||||
format, // This is equivalent to format: format,
|
|
||||||
categoryId, // Moved to top-level, outside of the 'listing' object
|
|
||||||
listingDescription, // Moved to top-level, outside of the 'listing' object
|
|
||||||
merchantLocationKey, // Moved to top-level, outside of the 'listing' object
|
|
||||||
pricingSummary: {
|
|
||||||
price: pricingSummary.price,
|
|
||||||
},
|
|
||||||
availableQuantity,
|
|
||||||
listingDuration,
|
|
||||||
listingPolicies: { // Include the listing policies with their respective IDs
|
|
||||||
fulfillmentPolicyId: "246576176016",
|
|
||||||
returnPolicyId: "246576188016",
|
|
||||||
paymentPolicyId: "246576154016",
|
|
||||||
},
|
|
||||||
// Additional fields from the sample payload can be included here as needed
|
|
||||||
};
|
|
||||||
|
|
||||||
const offerResponse = await fetch(
|
|
||||||
`https://api.ebay.com/sell/inventory/v1/offer`,
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Content-Language": "en-US",
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(offerPayload),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
if (!offerResponse.ok) {
|
|
||||||
// Handle error in creating the offer
|
|
||||||
const errorBody = await offerResponse.text();
|
|
||||||
throw new Error(
|
|
||||||
`Failed to create offer: ${offerResponse.status} - ${errorBody}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const offerData = await offerResponse.json();
|
|
||||||
const offerId = offerData.offerId;
|
|
||||||
console.log(offerId)
|
|
||||||
|
|
||||||
// 3. Publish the offer to convert it into a live listing
|
|
||||||
const publishOfferResponse = await fetch(
|
|
||||||
`https://api.ebay.com/sell/inventory/v1/offer/${offerId}/publish/`, // Ensure the offerId is correctly inserted
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`, // Ensure your token is valid
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const responseBody = await publishOfferResponse.json(); // Assuming the response will be in JSON format
|
|
||||||
console.log(responseBody);
|
|
||||||
|
|
||||||
|
|
||||||
res.json({ success: true, message: "Item listed successfully" });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error in createAndListItem endpoint:", error);
|
|
||||||
res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: "Failed to create and list item on eBay",
|
|
||||||
error: error.message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteItemBySku = async (req, res) => {
|
|
||||||
const { sku } = req.params; // Assuming SKU is provided as a URL parameter
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = await fetchEbayUserToken(req, res); // Get the eBay user token
|
const response = await fetch("https://api.ebay.com/ws/api.dll", {
|
||||||
|
method: "POST",
|
||||||
// Perform the deletion of the inventory item
|
|
||||||
const deleteResponse = await fetch(`https://api.ebay.com/sell/inventory/v1/inventory_item/${sku}`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `Bearer ${token}`,
|
"Content-Type": "text/xml",
|
||||||
"Content-Type": "application/json",
|
"X-EBAY-API-SITEID": "0",
|
||||||
"Accept": "application/json",
|
"X-EBAY-API-CALL-NAME": "AddFixedPriceItem",
|
||||||
},
|
"X-EBAY-API-COMPATIBILITY-LEVEL": "967", // Ensure this is the current compatibility level
|
||||||
});
|
"X-EBAY-API-APP-NAME": process.env.EBAY_CLIENT_ID,
|
||||||
|
"X-EBAY-API-DEV-NAME": process.env.EBAY_DEV_ID,
|
||||||
if (!deleteResponse.ok) {
|
"X-EBAY-API-CERT-NAME": process.env.EBAY_CLIENT_SECRET,
|
||||||
// Handle unsuccessful deletion
|
|
||||||
const errorBody = await deleteResponse.text();
|
|
||||||
throw new Error(`Failed to delete inventory item: ${deleteResponse.status} - ${errorBody}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Successfully deleted the item
|
|
||||||
res.json({ success: true, message: `Item with SKU: ${sku} deleted successfully.` });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error in deleteItemBySku endpoint:", error);
|
|
||||||
res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: `Failed to delete item with SKU: ${sku} from eBay`,
|
|
||||||
error: error.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAllInventory = async (req, res) => {
|
|
||||||
const limit = req.query.limit || '100'; // Set default limit
|
|
||||||
const offset = req.query.offset || '0'; // Set default offset
|
|
||||||
|
|
||||||
try {
|
|
||||||
const token = await fetchEbayUserToken(req, res); // Assuming fetchEbayUserToken doesn't need req, res passed and handles token internally
|
|
||||||
|
|
||||||
const response = await fetch(`https://api.ebay.com/sell/inventory/v1/inventory_item?limit=${limit}&offset=${offset}`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": `Bearer ${token}`,
|
|
||||||
"Accept-Language": "en-US",
|
|
||||||
},
|
},
|
||||||
|
body: xmlRequest,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
// If the response is not okay, throw an error with status and statusText
|
throw new Error(`eBay API responded with status ${response.status}`);
|
||||||
throw new Error(`Failed to retrieve inventory items: ${response.status} ${response.statusText}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const inventoryItems = await response.json(); // Parsing the JSON body of the response
|
const responseData = await response.text();
|
||||||
res.json(inventoryItems); // Sending the inventory items back to the client
|
// Parse the XML response and handle it appropriately
|
||||||
|
// You might need an XML parser here to handle the response
|
||||||
|
|
||||||
|
res.json({ success: true, data: responseData }); // Send back a success response
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching inventory:', error);
|
console.error("Error adding item to eBay:", error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
error: 'Internal Server Error',
|
success: false,
|
||||||
message: error.message,
|
message: "Failed to add item to eBay",
|
||||||
|
error: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getOffers = async (req, res) => {
|
|
||||||
const sku = "LEMUG2022212234120245"; // Assuming this is your SKU
|
|
||||||
const offerId = 418806088016
|
|
||||||
|
|
||||||
try {
|
|
||||||
const token = await fetchEbayUserToken(req, res); // Assuming fetchEbayUserToken handles token retrieval internally
|
|
||||||
|
|
||||||
const response = await fetch('https://api.ebay.com/commerce/taxonomy/v1/get_default_category_tree_id?marketplace_id=EBAY_US', {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Content-Language": "en-US",
|
|
||||||
"Authorization": `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
console.log(response)
|
|
||||||
const offers = await response.json(); // Parsing the JSON body of the response
|
|
||||||
|
|
||||||
res.json(offers); // Sending the offers back to the client
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching offers:', error);
|
|
||||||
res.status(500).json({
|
|
||||||
error: 'Internal Server Error',
|
|
||||||
message: error.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// fulfill policy = 246576176016
|
|
||||||
// return policy = 246576188016
|
|
||||||
// payment policy = 246576154016
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
20
api/node_modules/.package-lock.json
generated
vendored
20
api/node_modules/.package-lock.json
generated
vendored
@@ -177,26 +177,6 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cookie-parser": {
|
|
||||||
"version": "1.4.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
|
||||||
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
|
||||||
"dependencies": {
|
|
||||||
"cookie": "0.4.1",
|
|
||||||
"cookie-signature": "1.0.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cookie-parser/node_modules/cookie": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cookie-signature": {
|
"node_modules/cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
|||||||
100
api/node_modules/cookie-parser/HISTORY.md
generated
vendored
100
api/node_modules/cookie-parser/HISTORY.md
generated
vendored
@@ -1,100 +0,0 @@
|
|||||||
1.4.6 / 2021-11-16
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: cookie@0.4.1
|
|
||||||
|
|
||||||
1.4.5 / 2020-03-14
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: cookie@0.4.0
|
|
||||||
|
|
||||||
1.4.4 / 2019-02-12
|
|
||||||
==================
|
|
||||||
|
|
||||||
* perf: normalize `secret` argument only once
|
|
||||||
|
|
||||||
1.4.3 / 2016-05-26
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: cookie@0.3.1
|
|
||||||
- perf: use for loop in parse
|
|
||||||
|
|
||||||
1.4.2 / 2016-05-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: cookie@0.2.4
|
|
||||||
- perf: enable strict mode
|
|
||||||
- perf: use for loop in parse
|
|
||||||
- perf: use string concatenation for serialization
|
|
||||||
|
|
||||||
1.4.1 / 2016-01-11
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: cookie@0.2.3
|
|
||||||
* perf: enable strict mode
|
|
||||||
|
|
||||||
1.4.0 / 2015-09-18
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Accept array of secrets in addition to a single secret
|
|
||||||
* Fix `JSONCookie` to return `undefined` for non-string arguments
|
|
||||||
* Fix `signedCookie` to return `undefined` for non-string arguments
|
|
||||||
* deps: cookie@0.2.2
|
|
||||||
|
|
||||||
1.3.5 / 2015-05-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: cookie@0.1.3
|
|
||||||
- Slight optimizations
|
|
||||||
|
|
||||||
1.3.4 / 2015-02-15
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: cookie-signature@1.0.6
|
|
||||||
|
|
||||||
1.3.3 / 2014-09-05
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: cookie-signature@1.0.5
|
|
||||||
|
|
||||||
1.3.2 / 2014-06-26
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: cookie-signature@1.0.4
|
|
||||||
- fix for timing attacks
|
|
||||||
|
|
||||||
1.3.1 / 2014-06-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* actually export `signedCookie`
|
|
||||||
|
|
||||||
1.3.0 / 2014-06-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add `signedCookie` export for single cookie unsigning
|
|
||||||
|
|
||||||
1.2.0 / 2014-06-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* export parsing functions
|
|
||||||
* `req.cookies` and `req.signedCookies` are now plain objects
|
|
||||||
* slightly faster parsing of many cookies
|
|
||||||
|
|
||||||
1.1.0 / 2014-05-12
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Support for NodeJS version 0.8
|
|
||||||
* deps: cookie@0.1.2
|
|
||||||
- Fix for maxAge == 0
|
|
||||||
- made compat with expires field
|
|
||||||
- tweak maxAge NaN error message
|
|
||||||
|
|
||||||
1.0.1 / 2014-02-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add missing dependencies
|
|
||||||
|
|
||||||
1.0.0 / 2014-02-15
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Genesis from `connect`
|
|
||||||
23
api/node_modules/cookie-parser/LICENSE
generated
vendored
23
api/node_modules/cookie-parser/LICENSE
generated
vendored
@@ -1,23 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca>
|
|
||||||
Copyright (c) 2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
'Software'), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
119
api/node_modules/cookie-parser/README.md
generated
vendored
119
api/node_modules/cookie-parser/README.md
generated
vendored
@@ -1,119 +0,0 @@
|
|||||||
# cookie-parser
|
|
||||||
|
|
||||||
[![NPM Version][npm-version-image]][npm-url]
|
|
||||||
[![NPM Downloads][npm-downloads-image]][npm-url]
|
|
||||||
[![Build Status][ci-image]][ci-url]
|
|
||||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
Parse `Cookie` header and populate `req.cookies` with an object keyed by the
|
|
||||||
cookie names. Optionally you may enable signed cookie support by passing a
|
|
||||||
`secret` string, which assigns `req.secret` so it may be used by other
|
|
||||||
middleware.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm install cookie-parser
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
```js
|
|
||||||
var cookieParser = require('cookie-parser')
|
|
||||||
```
|
|
||||||
|
|
||||||
### cookieParser(secret, options)
|
|
||||||
|
|
||||||
Create a new cookie parser middleware function using the given `secret` and
|
|
||||||
`options`.
|
|
||||||
|
|
||||||
- `secret` a string or array used for signing cookies. This is optional and if
|
|
||||||
not specified, will not parse signed cookies. If a string is provided, this
|
|
||||||
is used as the secret. If an array is provided, an attempt will be made to
|
|
||||||
unsign the cookie with each secret in order.
|
|
||||||
- `options` an object that is passed to `cookie.parse` as the second option. See
|
|
||||||
[cookie](https://www.npmjs.org/package/cookie) for more information.
|
|
||||||
- `decode` a function to decode the value of the cookie
|
|
||||||
|
|
||||||
The middleware will parse the `Cookie` header on the request and expose the
|
|
||||||
cookie data as the property `req.cookies` and, if a `secret` was provided, as
|
|
||||||
the property `req.signedCookies`. These properties are name value pairs of the
|
|
||||||
cookie name to cookie value.
|
|
||||||
|
|
||||||
When `secret` is provided, this module will unsign and validate any signed cookie
|
|
||||||
values and move those name value pairs from `req.cookies` into `req.signedCookies`.
|
|
||||||
A signed cookie is a cookie that has a value prefixed with `s:`. Signed cookies
|
|
||||||
that fail signature validation will have the value `false` instead of the tampered
|
|
||||||
value.
|
|
||||||
|
|
||||||
In addition, this module supports special "JSON cookies". These are cookie where
|
|
||||||
the value is prefixed with `j:`. When these values are encountered, the value will
|
|
||||||
be exposed as the result of `JSON.parse`. If parsing fails, the original value will
|
|
||||||
remain.
|
|
||||||
|
|
||||||
### cookieParser.JSONCookie(str)
|
|
||||||
|
|
||||||
Parse a cookie value as a JSON cookie. This will return the parsed JSON value
|
|
||||||
if it was a JSON cookie, otherwise, it will return the passed value.
|
|
||||||
|
|
||||||
### cookieParser.JSONCookies(cookies)
|
|
||||||
|
|
||||||
Given an object, this will iterate over the keys and call `JSONCookie` on each
|
|
||||||
value, replacing the original value with the parsed value. This returns the
|
|
||||||
same object that was passed in.
|
|
||||||
|
|
||||||
### cookieParser.signedCookie(str, secret)
|
|
||||||
|
|
||||||
Parse a cookie value as a signed cookie. This will return the parsed unsigned
|
|
||||||
value if it was a signed cookie and the signature was valid. If the value was
|
|
||||||
not signed, the original value is returned. If the value was signed but the
|
|
||||||
signature could not be validated, `false` is returned.
|
|
||||||
|
|
||||||
The `secret` argument can be an array or string. If a string is provided, this
|
|
||||||
is used as the secret. If an array is provided, an attempt will be made to
|
|
||||||
unsign the cookie with each secret in order.
|
|
||||||
|
|
||||||
### cookieParser.signedCookies(cookies, secret)
|
|
||||||
|
|
||||||
Given an object, this will iterate over the keys and check if any value is a
|
|
||||||
signed cookie. If it is a signed cookie and the signature is valid, the key
|
|
||||||
will be deleted from the object and added to the new object that is returned.
|
|
||||||
|
|
||||||
The `secret` argument can be an array or string. If a string is provided, this
|
|
||||||
is used as the secret. If an array is provided, an attempt will be made to
|
|
||||||
unsign the cookie with each secret in order.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```js
|
|
||||||
var express = require('express')
|
|
||||||
var cookieParser = require('cookie-parser')
|
|
||||||
|
|
||||||
var app = express()
|
|
||||||
app.use(cookieParser())
|
|
||||||
|
|
||||||
app.get('/', function (req, res) {
|
|
||||||
// Cookies that have not been signed
|
|
||||||
console.log('Cookies: ', req.cookies)
|
|
||||||
|
|
||||||
// Cookies that have been signed
|
|
||||||
console.log('Signed Cookies: ', req.signedCookies)
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(8080)
|
|
||||||
|
|
||||||
// curl command that sends an HTTP request with two cookies
|
|
||||||
// curl http://127.0.0.1:8080 --cookie "Cho=Kim;Greet=Hello"
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT](LICENSE)
|
|
||||||
|
|
||||||
[ci-image]: https://badgen.net/github/checks/expressjs/cookie-parser/master?label=ci
|
|
||||||
[ci-url]: https://github.com/expressjs/cookie-parser/actions?query=workflow%3Aci
|
|
||||||
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/cookie-parser/master
|
|
||||||
[coveralls-url]: https://coveralls.io/r/expressjs/cookie-parser?branch=master
|
|
||||||
[npm-downloads-image]: https://badgen.net/npm/dm/cookie-parser
|
|
||||||
[npm-url]: https://npmjs.org/package/cookie-parser
|
|
||||||
[npm-version-image]: https://badgen.net/npm/v/cookie-parser
|
|
||||||
182
api/node_modules/cookie-parser/index.js
generated
vendored
182
api/node_modules/cookie-parser/index.js
generated
vendored
@@ -1,182 +0,0 @@
|
|||||||
/*!
|
|
||||||
* cookie-parser
|
|
||||||
* Copyright(c) 2014 TJ Holowaychuk
|
|
||||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var cookie = require('cookie')
|
|
||||||
var signature = require('cookie-signature')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = cookieParser
|
|
||||||
module.exports.JSONCookie = JSONCookie
|
|
||||||
module.exports.JSONCookies = JSONCookies
|
|
||||||
module.exports.signedCookie = signedCookie
|
|
||||||
module.exports.signedCookies = signedCookies
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse Cookie header and populate `req.cookies`
|
|
||||||
* with an object keyed by the cookie names.
|
|
||||||
*
|
|
||||||
* @param {string|array} [secret] A string (or array of strings) representing cookie signing secret(s).
|
|
||||||
* @param {Object} [options]
|
|
||||||
* @return {Function}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function cookieParser (secret, options) {
|
|
||||||
var secrets = !secret || Array.isArray(secret)
|
|
||||||
? (secret || [])
|
|
||||||
: [secret]
|
|
||||||
|
|
||||||
return function cookieParser (req, res, next) {
|
|
||||||
if (req.cookies) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
var cookies = req.headers.cookie
|
|
||||||
|
|
||||||
req.secret = secrets[0]
|
|
||||||
req.cookies = Object.create(null)
|
|
||||||
req.signedCookies = Object.create(null)
|
|
||||||
|
|
||||||
// no cookies
|
|
||||||
if (!cookies) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
req.cookies = cookie.parse(cookies, options)
|
|
||||||
|
|
||||||
// parse signed cookies
|
|
||||||
if (secrets.length !== 0) {
|
|
||||||
req.signedCookies = signedCookies(req.cookies, secrets)
|
|
||||||
req.signedCookies = JSONCookies(req.signedCookies)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse JSON cookies
|
|
||||||
req.cookies = JSONCookies(req.cookies)
|
|
||||||
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse JSON cookie string.
|
|
||||||
*
|
|
||||||
* @param {String} str
|
|
||||||
* @return {Object} Parsed object or undefined if not json cookie
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function JSONCookie (str) {
|
|
||||||
if (typeof str !== 'string' || str.substr(0, 2) !== 'j:') {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return JSON.parse(str.slice(2))
|
|
||||||
} catch (err) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse JSON cookies.
|
|
||||||
*
|
|
||||||
* @param {Object} obj
|
|
||||||
* @return {Object}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function JSONCookies (obj) {
|
|
||||||
var cookies = Object.keys(obj)
|
|
||||||
var key
|
|
||||||
var val
|
|
||||||
|
|
||||||
for (var i = 0; i < cookies.length; i++) {
|
|
||||||
key = cookies[i]
|
|
||||||
val = JSONCookie(obj[key])
|
|
||||||
|
|
||||||
if (val) {
|
|
||||||
obj[key] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a signed cookie string, return the decoded value.
|
|
||||||
*
|
|
||||||
* @param {String} str signed cookie string
|
|
||||||
* @param {string|array} secret
|
|
||||||
* @return {String} decoded value
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function signedCookie (str, secret) {
|
|
||||||
if (typeof str !== 'string') {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str.substr(0, 2) !== 's:') {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
var secrets = !secret || Array.isArray(secret)
|
|
||||||
? (secret || [])
|
|
||||||
: [secret]
|
|
||||||
|
|
||||||
for (var i = 0; i < secrets.length; i++) {
|
|
||||||
var val = signature.unsign(str.slice(2), secrets[i])
|
|
||||||
|
|
||||||
if (val !== false) {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse signed cookies, returning an object containing the decoded key/value
|
|
||||||
* pairs, while removing the signed key from obj.
|
|
||||||
*
|
|
||||||
* @param {Object} obj
|
|
||||||
* @param {string|array} secret
|
|
||||||
* @return {Object}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function signedCookies (obj, secret) {
|
|
||||||
var cookies = Object.keys(obj)
|
|
||||||
var dec
|
|
||||||
var key
|
|
||||||
var ret = Object.create(null)
|
|
||||||
var val
|
|
||||||
|
|
||||||
for (var i = 0; i < cookies.length; i++) {
|
|
||||||
key = cookies[i]
|
|
||||||
val = obj[key]
|
|
||||||
dec = signedCookie(val, secret)
|
|
||||||
|
|
||||||
if (val !== dec) {
|
|
||||||
ret[key] = dec
|
|
||||||
delete obj[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
128
api/node_modules/cookie-parser/node_modules/cookie/HISTORY.md
generated
vendored
128
api/node_modules/cookie-parser/node_modules/cookie/HISTORY.md
generated
vendored
@@ -1,128 +0,0 @@
|
|||||||
0.4.1 / 2020-04-21
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix `maxAge` option to reject invalid values
|
|
||||||
|
|
||||||
0.4.0 / 2019-05-15
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add `SameSite=None` support
|
|
||||||
|
|
||||||
0.3.1 / 2016-05-26
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix `sameSite: true` to work with draft-7 clients
|
|
||||||
- `true` now sends `SameSite=Strict` instead of `SameSite`
|
|
||||||
|
|
||||||
0.3.0 / 2016-05-26
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add `sameSite` option
|
|
||||||
- Replaces `firstPartyOnly` option, never implemented by browsers
|
|
||||||
* Improve error message when `encode` is not a function
|
|
||||||
* Improve error message when `expires` is not a `Date`
|
|
||||||
|
|
||||||
0.2.4 / 2016-05-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* perf: enable strict mode
|
|
||||||
* perf: use for loop in parse
|
|
||||||
* perf: use string concatination for serialization
|
|
||||||
|
|
||||||
0.2.3 / 2015-10-25
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix cookie `Max-Age` to never be a floating point number
|
|
||||||
|
|
||||||
0.2.2 / 2015-09-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix regression when setting empty cookie value
|
|
||||||
- Ease the new restriction, which is just basic header-level validation
|
|
||||||
* Fix typo in invalid value errors
|
|
||||||
|
|
||||||
0.2.1 / 2015-09-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Throw on invalid values provided to `serialize`
|
|
||||||
- Ensures the resulting string is a valid HTTP header value
|
|
||||||
|
|
||||||
0.2.0 / 2015-08-13
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add `firstPartyOnly` option
|
|
||||||
* Throw better error for invalid argument to parse
|
|
||||||
* perf: hoist regular expression
|
|
||||||
|
|
||||||
0.1.5 / 2015-09-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix regression when setting empty cookie value
|
|
||||||
- Ease the new restriction, which is just basic header-level validation
|
|
||||||
* Fix typo in invalid value errors
|
|
||||||
|
|
||||||
0.1.4 / 2015-09-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Throw better error for invalid argument to parse
|
|
||||||
* Throw on invalid values provided to `serialize`
|
|
||||||
- Ensures the resulting string is a valid HTTP header value
|
|
||||||
|
|
||||||
0.1.3 / 2015-05-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Reduce the scope of try-catch deopt
|
|
||||||
* Remove argument reassignments
|
|
||||||
|
|
||||||
0.1.2 / 2014-04-16
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Remove unnecessary files from npm package
|
|
||||||
|
|
||||||
0.1.1 / 2014-02-23
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix bad parse when cookie value contained a comma
|
|
||||||
* Fix support for `maxAge` of `0`
|
|
||||||
|
|
||||||
0.1.0 / 2013-05-01
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add `decode` option
|
|
||||||
* Add `encode` option
|
|
||||||
|
|
||||||
0.0.6 / 2013-04-08
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Ignore cookie parts missing `=`
|
|
||||||
|
|
||||||
0.0.5 / 2012-10-29
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Return raw cookie value if value unescape errors
|
|
||||||
|
|
||||||
0.0.4 / 2012-06-21
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Use encode/decodeURIComponent for cookie encoding/decoding
|
|
||||||
- Improve server/client interoperability
|
|
||||||
|
|
||||||
0.0.3 / 2012-06-06
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Only escape special characters per the cookie RFC
|
|
||||||
|
|
||||||
0.0.2 / 2012-06-01
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix `maxAge` option to not throw error
|
|
||||||
|
|
||||||
0.0.1 / 2012-05-28
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add more tests
|
|
||||||
|
|
||||||
0.0.0 / 2012-05-28
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Initial release
|
|
||||||
24
api/node_modules/cookie-parser/node_modules/cookie/LICENSE
generated
vendored
24
api/node_modules/cookie-parser/node_modules/cookie/LICENSE
generated
vendored
@@ -1,24 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2012-2014 Roman Shtylman <shtylman@gmail.com>
|
|
||||||
Copyright (c) 2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
'Software'), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
257
api/node_modules/cookie-parser/node_modules/cookie/README.md
generated
vendored
257
api/node_modules/cookie-parser/node_modules/cookie/README.md
generated
vendored
@@ -1,257 +0,0 @@
|
|||||||
# cookie
|
|
||||||
|
|
||||||
[![NPM Version][npm-version-image]][npm-url]
|
|
||||||
[![NPM Downloads][npm-downloads-image]][npm-url]
|
|
||||||
[![Node.js Version][node-version-image]][node-version-url]
|
|
||||||
[![Build Status][travis-image]][travis-url]
|
|
||||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
Basic HTTP cookie parser and serializer for HTTP servers.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
|
||||||
[npm registry](https://www.npmjs.com/). Installation is done using the
|
|
||||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm install cookie
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
```js
|
|
||||||
var cookie = require('cookie');
|
|
||||||
```
|
|
||||||
|
|
||||||
### cookie.parse(str, options)
|
|
||||||
|
|
||||||
Parse an HTTP `Cookie` header string and returning an object of all cookie name-value pairs.
|
|
||||||
The `str` argument is the string representing a `Cookie` header value and `options` is an
|
|
||||||
optional object containing additional parsing options.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2');
|
|
||||||
// { foo: 'bar', equation: 'E=mc^2' }
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
`cookie.parse` accepts these properties in the options object.
|
|
||||||
|
|
||||||
##### decode
|
|
||||||
|
|
||||||
Specifies a function that will be used to decode a cookie's value. Since the value of a cookie
|
|
||||||
has a limited character set (and must be a simple string), this function can be used to decode
|
|
||||||
a previously-encoded cookie value into a JavaScript string or other object.
|
|
||||||
|
|
||||||
The default function is the global `decodeURIComponent`, which will decode any URL-encoded
|
|
||||||
sequences into their byte representations.
|
|
||||||
|
|
||||||
**note** if an error is thrown from this function, the original, non-decoded cookie value will
|
|
||||||
be returned as the cookie's value.
|
|
||||||
|
|
||||||
### cookie.serialize(name, value, options)
|
|
||||||
|
|
||||||
Serialize a cookie name-value pair into a `Set-Cookie` header string. The `name` argument is the
|
|
||||||
name for the cookie, the `value` argument is the value to set the cookie to, and the `options`
|
|
||||||
argument is an optional object containing additional serialization options.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var setCookie = cookie.serialize('foo', 'bar');
|
|
||||||
// foo=bar
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
`cookie.serialize` accepts these properties in the options object.
|
|
||||||
|
|
||||||
##### domain
|
|
||||||
|
|
||||||
Specifies the value for the [`Domain` `Set-Cookie` attribute][rfc-6265-5.2.3]. By default, no
|
|
||||||
domain is set, and most clients will consider the cookie to apply to only the current domain.
|
|
||||||
|
|
||||||
##### encode
|
|
||||||
|
|
||||||
Specifies a function that will be used to encode a cookie's value. Since value of a cookie
|
|
||||||
has a limited character set (and must be a simple string), this function can be used to encode
|
|
||||||
a value into a string suited for a cookie's value.
|
|
||||||
|
|
||||||
The default function is the global `encodeURIComponent`, which will encode a JavaScript string
|
|
||||||
into UTF-8 byte sequences and then URL-encode any that fall outside of the cookie range.
|
|
||||||
|
|
||||||
##### expires
|
|
||||||
|
|
||||||
Specifies the `Date` object to be the value for the [`Expires` `Set-Cookie` attribute][rfc-6265-5.2.1].
|
|
||||||
By default, no expiration is set, and most clients will consider this a "non-persistent cookie" and
|
|
||||||
will delete it on a condition like exiting a web browser application.
|
|
||||||
|
|
||||||
**note** the [cookie storage model specification][rfc-6265-5.3] states that if both `expires` and
|
|
||||||
`maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,
|
|
||||||
so if both are set, they should point to the same date and time.
|
|
||||||
|
|
||||||
##### httpOnly
|
|
||||||
|
|
||||||
Specifies the `boolean` value for the [`HttpOnly` `Set-Cookie` attribute][rfc-6265-5.2.6]. When truthy,
|
|
||||||
the `HttpOnly` attribute is set, otherwise it is not. By default, the `HttpOnly` attribute is not set.
|
|
||||||
|
|
||||||
**note** be careful when setting this to `true`, as compliant clients will not allow client-side
|
|
||||||
JavaScript to see the cookie in `document.cookie`.
|
|
||||||
|
|
||||||
##### maxAge
|
|
||||||
|
|
||||||
Specifies the `number` (in seconds) to be the value for the [`Max-Age` `Set-Cookie` attribute][rfc-6265-5.2.2].
|
|
||||||
The given number will be converted to an integer by rounding down. By default, no maximum age is set.
|
|
||||||
|
|
||||||
**note** the [cookie storage model specification][rfc-6265-5.3] states that if both `expires` and
|
|
||||||
`maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,
|
|
||||||
so if both are set, they should point to the same date and time.
|
|
||||||
|
|
||||||
##### path
|
|
||||||
|
|
||||||
Specifies the value for the [`Path` `Set-Cookie` attribute][rfc-6265-5.2.4]. By default, the path
|
|
||||||
is considered the ["default path"][rfc-6265-5.1.4].
|
|
||||||
|
|
||||||
##### sameSite
|
|
||||||
|
|
||||||
Specifies the `boolean` or `string` to be the value for the [`SameSite` `Set-Cookie` attribute][rfc-6265bis-03-4.1.2.7].
|
|
||||||
|
|
||||||
- `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
|
||||||
- `false` will not set the `SameSite` attribute.
|
|
||||||
- `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
|
|
||||||
- `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.
|
|
||||||
- `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
|
||||||
|
|
||||||
More information about the different enforcement levels can be found in
|
|
||||||
[the specification][rfc-6265bis-03-4.1.2.7].
|
|
||||||
|
|
||||||
**note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
|
||||||
This also means many clients may ignore this attribute until they understand it.
|
|
||||||
|
|
||||||
##### secure
|
|
||||||
|
|
||||||
Specifies the `boolean` value for the [`Secure` `Set-Cookie` attribute][rfc-6265-5.2.5]. When truthy,
|
|
||||||
the `Secure` attribute is set, otherwise it is not. By default, the `Secure` attribute is not set.
|
|
||||||
|
|
||||||
**note** be careful when setting this to `true`, as compliant clients will not send the cookie back to
|
|
||||||
the server in the future if the browser does not have an HTTPS connection.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
The following example uses this module in conjunction with the Node.js core HTTP server
|
|
||||||
to prompt a user for their name and display it back on future visits.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var cookie = require('cookie');
|
|
||||||
var escapeHtml = require('escape-html');
|
|
||||||
var http = require('http');
|
|
||||||
var url = require('url');
|
|
||||||
|
|
||||||
function onRequest(req, res) {
|
|
||||||
// Parse the query string
|
|
||||||
var query = url.parse(req.url, true, true).query;
|
|
||||||
|
|
||||||
if (query && query.name) {
|
|
||||||
// Set a new cookie with the name
|
|
||||||
res.setHeader('Set-Cookie', cookie.serialize('name', String(query.name), {
|
|
||||||
httpOnly: true,
|
|
||||||
maxAge: 60 * 60 * 24 * 7 // 1 week
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Redirect back after setting cookie
|
|
||||||
res.statusCode = 302;
|
|
||||||
res.setHeader('Location', req.headers.referer || '/');
|
|
||||||
res.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the cookies on the request
|
|
||||||
var cookies = cookie.parse(req.headers.cookie || '');
|
|
||||||
|
|
||||||
// Get the visitor name set in the cookie
|
|
||||||
var name = cookies.name;
|
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
|
||||||
|
|
||||||
if (name) {
|
|
||||||
res.write('<p>Welcome back, <b>' + escapeHtml(name) + '</b>!</p>');
|
|
||||||
} else {
|
|
||||||
res.write('<p>Hello, new visitor!</p>');
|
|
||||||
}
|
|
||||||
|
|
||||||
res.write('<form method="GET">');
|
|
||||||
res.write('<input placeholder="enter your name" name="name"> <input type="submit" value="Set Name">');
|
|
||||||
res.end('</form>');
|
|
||||||
}
|
|
||||||
|
|
||||||
http.createServer(onRequest).listen(3000);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Benchmark
|
|
||||||
|
|
||||||
```
|
|
||||||
$ npm run bench
|
|
||||||
|
|
||||||
> cookie@0.3.1 bench cookie
|
|
||||||
> node benchmark/index.js
|
|
||||||
|
|
||||||
http_parser@2.8.0
|
|
||||||
node@6.14.2
|
|
||||||
v8@5.1.281.111
|
|
||||||
uv@1.16.1
|
|
||||||
zlib@1.2.11
|
|
||||||
ares@1.10.1-DEV
|
|
||||||
icu@58.2
|
|
||||||
modules@48
|
|
||||||
napi@3
|
|
||||||
openssl@1.0.2o
|
|
||||||
|
|
||||||
> node benchmark/parse.js
|
|
||||||
|
|
||||||
cookie.parse
|
|
||||||
|
|
||||||
6 tests completed.
|
|
||||||
|
|
||||||
simple x 1,200,691 ops/sec ±1.12% (189 runs sampled)
|
|
||||||
decode x 1,012,994 ops/sec ±0.97% (186 runs sampled)
|
|
||||||
unquote x 1,074,174 ops/sec ±2.43% (186 runs sampled)
|
|
||||||
duplicates x 438,424 ops/sec ±2.17% (184 runs sampled)
|
|
||||||
10 cookies x 147,154 ops/sec ±1.01% (186 runs sampled)
|
|
||||||
100 cookies x 14,274 ops/sec ±1.07% (187 runs sampled)
|
|
||||||
```
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [RFC 6265: HTTP State Management Mechanism][rfc-6265]
|
|
||||||
- [Same-site Cookies][rfc-6265bis-03-4.1.2.7]
|
|
||||||
|
|
||||||
[rfc-6265bis-03-4.1.2.7]: https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7
|
|
||||||
[rfc-6265]: https://tools.ietf.org/html/rfc6265
|
|
||||||
[rfc-6265-5.1.4]: https://tools.ietf.org/html/rfc6265#section-5.1.4
|
|
||||||
[rfc-6265-5.2.1]: https://tools.ietf.org/html/rfc6265#section-5.2.1
|
|
||||||
[rfc-6265-5.2.2]: https://tools.ietf.org/html/rfc6265#section-5.2.2
|
|
||||||
[rfc-6265-5.2.3]: https://tools.ietf.org/html/rfc6265#section-5.2.3
|
|
||||||
[rfc-6265-5.2.4]: https://tools.ietf.org/html/rfc6265#section-5.2.4
|
|
||||||
[rfc-6265-5.2.5]: https://tools.ietf.org/html/rfc6265#section-5.2.5
|
|
||||||
[rfc-6265-5.2.6]: https://tools.ietf.org/html/rfc6265#section-5.2.6
|
|
||||||
[rfc-6265-5.3]: https://tools.ietf.org/html/rfc6265#section-5.3
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT](LICENSE)
|
|
||||||
|
|
||||||
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/cookie/master
|
|
||||||
[coveralls-url]: https://coveralls.io/r/jshttp/cookie?branch=master
|
|
||||||
[node-version-image]: https://badgen.net/npm/node/cookie
|
|
||||||
[node-version-url]: https://nodejs.org/en/download
|
|
||||||
[npm-downloads-image]: https://badgen.net/npm/dm/cookie
|
|
||||||
[npm-url]: https://npmjs.org/package/cookie
|
|
||||||
[npm-version-image]: https://badgen.net/npm/v/cookie
|
|
||||||
[travis-image]: https://badgen.net/travis/jshttp/cookie/master
|
|
||||||
[travis-url]: https://travis-ci.org/jshttp/cookie
|
|
||||||
202
api/node_modules/cookie-parser/node_modules/cookie/index.js
generated
vendored
202
api/node_modules/cookie-parser/node_modules/cookie/index.js
generated
vendored
@@ -1,202 +0,0 @@
|
|||||||
/*!
|
|
||||||
* cookie
|
|
||||||
* Copyright(c) 2012-2014 Roman Shtylman
|
|
||||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
exports.parse = parse;
|
|
||||||
exports.serialize = serialize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module variables.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var decode = decodeURIComponent;
|
|
||||||
var encode = encodeURIComponent;
|
|
||||||
var pairSplitRegExp = /; */;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match field-content in RFC 7230 sec 3.2
|
|
||||||
*
|
|
||||||
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
|
||||||
* field-vchar = VCHAR / obs-text
|
|
||||||
* obs-text = %x80-FF
|
|
||||||
*/
|
|
||||||
|
|
||||||
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a cookie header.
|
|
||||||
*
|
|
||||||
* Parse the given cookie header string into an object
|
|
||||||
* The object has the various cookies as keys(names) => values
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @param {object} [options]
|
|
||||||
* @return {object}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function parse(str, options) {
|
|
||||||
if (typeof str !== 'string') {
|
|
||||||
throw new TypeError('argument str must be a string');
|
|
||||||
}
|
|
||||||
|
|
||||||
var obj = {}
|
|
||||||
var opt = options || {};
|
|
||||||
var pairs = str.split(pairSplitRegExp);
|
|
||||||
var dec = opt.decode || decode;
|
|
||||||
|
|
||||||
for (var i = 0; i < pairs.length; i++) {
|
|
||||||
var pair = pairs[i];
|
|
||||||
var eq_idx = pair.indexOf('=');
|
|
||||||
|
|
||||||
// skip things that don't look like key=value
|
|
||||||
if (eq_idx < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = pair.substr(0, eq_idx).trim()
|
|
||||||
var val = pair.substr(++eq_idx, pair.length).trim();
|
|
||||||
|
|
||||||
// quoted values
|
|
||||||
if ('"' == val[0]) {
|
|
||||||
val = val.slice(1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// only assign once
|
|
||||||
if (undefined == obj[key]) {
|
|
||||||
obj[key] = tryDecode(val, dec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize data into a cookie header.
|
|
||||||
*
|
|
||||||
* Serialize the a name value pair into a cookie string suitable for
|
|
||||||
* http headers. An optional options object specified cookie parameters.
|
|
||||||
*
|
|
||||||
* serialize('foo', 'bar', { httpOnly: true })
|
|
||||||
* => "foo=bar; httpOnly"
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* @param {string} val
|
|
||||||
* @param {object} [options]
|
|
||||||
* @return {string}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function serialize(name, val, options) {
|
|
||||||
var opt = options || {};
|
|
||||||
var enc = opt.encode || encode;
|
|
||||||
|
|
||||||
if (typeof enc !== 'function') {
|
|
||||||
throw new TypeError('option encode is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fieldContentRegExp.test(name)) {
|
|
||||||
throw new TypeError('argument name is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
var value = enc(val);
|
|
||||||
|
|
||||||
if (value && !fieldContentRegExp.test(value)) {
|
|
||||||
throw new TypeError('argument val is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
var str = name + '=' + value;
|
|
||||||
|
|
||||||
if (null != opt.maxAge) {
|
|
||||||
var maxAge = opt.maxAge - 0;
|
|
||||||
|
|
||||||
if (isNaN(maxAge) || !isFinite(maxAge)) {
|
|
||||||
throw new TypeError('option maxAge is invalid')
|
|
||||||
}
|
|
||||||
|
|
||||||
str += '; Max-Age=' + Math.floor(maxAge);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.domain) {
|
|
||||||
if (!fieldContentRegExp.test(opt.domain)) {
|
|
||||||
throw new TypeError('option domain is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
str += '; Domain=' + opt.domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.path) {
|
|
||||||
if (!fieldContentRegExp.test(opt.path)) {
|
|
||||||
throw new TypeError('option path is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
str += '; Path=' + opt.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.expires) {
|
|
||||||
if (typeof opt.expires.toUTCString !== 'function') {
|
|
||||||
throw new TypeError('option expires is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
str += '; Expires=' + opt.expires.toUTCString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.httpOnly) {
|
|
||||||
str += '; HttpOnly';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.secure) {
|
|
||||||
str += '; Secure';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.sameSite) {
|
|
||||||
var sameSite = typeof opt.sameSite === 'string'
|
|
||||||
? opt.sameSite.toLowerCase() : opt.sameSite;
|
|
||||||
|
|
||||||
switch (sameSite) {
|
|
||||||
case true:
|
|
||||||
str += '; SameSite=Strict';
|
|
||||||
break;
|
|
||||||
case 'lax':
|
|
||||||
str += '; SameSite=Lax';
|
|
||||||
break;
|
|
||||||
case 'strict':
|
|
||||||
str += '; SameSite=Strict';
|
|
||||||
break;
|
|
||||||
case 'none':
|
|
||||||
str += '; SameSite=None';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new TypeError('option sameSite is invalid');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try decoding a string using a decoding function.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @param {function} decode
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function tryDecode(str, decode) {
|
|
||||||
try {
|
|
||||||
return decode(str);
|
|
||||||
} catch (e) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
api/node_modules/cookie-parser/node_modules/cookie/package.json
generated
vendored
40
api/node_modules/cookie-parser/node_modules/cookie/package.json
generated
vendored
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "cookie",
|
|
||||||
"description": "HTTP server cookie parsing and serialization",
|
|
||||||
"version": "0.4.1",
|
|
||||||
"author": "Roman Shtylman <shtylman@gmail.com>",
|
|
||||||
"contributors": [
|
|
||||||
"Douglas Christopher Wilson <doug@somethingdoug.com>"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"cookie",
|
|
||||||
"cookies"
|
|
||||||
],
|
|
||||||
"repository": "jshttp/cookie",
|
|
||||||
"devDependencies": {
|
|
||||||
"beautify-benchmark": "0.2.4",
|
|
||||||
"benchmark": "2.1.4",
|
|
||||||
"eslint": "6.8.0",
|
|
||||||
"eslint-plugin-markdown": "1.0.2",
|
|
||||||
"mocha": "7.1.1",
|
|
||||||
"nyc": "15.0.1"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"HISTORY.md",
|
|
||||||
"LICENSE",
|
|
||||||
"README.md",
|
|
||||||
"index.js"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"bench": "node benchmark/index.js",
|
|
||||||
"lint": "eslint --plugin markdown --ext js,md .",
|
|
||||||
"test": "mocha --reporter spec --bail --check-leaks --ui qunit test/",
|
|
||||||
"test-ci": "nyc --reporter=text npm test",
|
|
||||||
"test-cov": "nyc --reporter=html --reporter=text npm test",
|
|
||||||
"version": "node scripts/version-history.js && git add HISTORY.md"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
45
api/node_modules/cookie-parser/package.json
generated
vendored
45
api/node_modules/cookie-parser/package.json
generated
vendored
@@ -1,45 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "cookie-parser",
|
|
||||||
"description": "Parse HTTP request cookies",
|
|
||||||
"version": "1.4.6",
|
|
||||||
"author": "TJ Holowaychuk <tj@vision-media.ca> (http://tjholowaychuk.com)",
|
|
||||||
"contributors": [
|
|
||||||
"Douglas Christopher Wilson <doug@somethingdoug.com>"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": "expressjs/cookie-parser",
|
|
||||||
"keywords": [
|
|
||||||
"cookie",
|
|
||||||
"middleware"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"cookie": "0.4.1",
|
|
||||||
"cookie-signature": "1.0.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"eslint": "7.32.0",
|
|
||||||
"eslint-config-standard": "14.1.1",
|
|
||||||
"eslint-plugin-import": "2.25.2",
|
|
||||||
"eslint-plugin-markdown": "2.2.1",
|
|
||||||
"eslint-plugin-node": "11.1.0",
|
|
||||||
"eslint-plugin-promise": "4.3.1",
|
|
||||||
"eslint-plugin-standard": "4.1.0",
|
|
||||||
"mocha": "9.1.3",
|
|
||||||
"nyc": "15.1.0",
|
|
||||||
"supertest": "6.1.6"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"LICENSE",
|
|
||||||
"HISTORY.md",
|
|
||||||
"index.js"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"lint": "eslint .",
|
|
||||||
"test": "mocha --reporter spec --bail --check-leaks test/",
|
|
||||||
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
|
|
||||||
"test-cov": "nyc --reporter=html --reporter=text npm test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
37
api/package-lock.json
generated
37
api/package-lock.json
generated
@@ -5,7 +5,6 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie-parser": "^1.4.6",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.19.0",
|
"express": "^4.19.0",
|
||||||
@@ -187,26 +186,6 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cookie-parser": {
|
|
||||||
"version": "1.4.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
|
||||||
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
|
||||||
"dependencies": {
|
|
||||||
"cookie": "0.4.1",
|
|
||||||
"cookie-signature": "1.0.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cookie-parser/node_modules/cookie": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cookie-signature": {
|
"node_modules/cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
@@ -1265,22 +1244,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
|
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
|
||||||
},
|
},
|
||||||
"cookie-parser": {
|
|
||||||
"version": "1.4.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
|
||||||
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
|
||||||
"requires": {
|
|
||||||
"cookie": "0.4.1",
|
|
||||||
"cookie-signature": "1.0.6"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"cookie": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cookie-signature": {
|
"cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie-parser": "^1.4.6",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.19.0",
|
"express": "^4.19.0",
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
// routes/dataRoutes.js
|
// routes/dataRoutes.js
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { createAndListItem, deleteItemBySku, getAllInventory, getOffers } from "../controllers/inventoryController.js";
|
import { addItem } from "../controllers/inventoryController.js";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.post("/create-list-item", createAndListItem);
|
router.post("/add-item", addItem);
|
||||||
router.get("/delete-item-by-sku", deleteItemBySku);
|
|
||||||
router.get("/get-all-inventory", getAllInventory);
|
|
||||||
router.get("/get-offers", getOffers)
|
|
||||||
|
|
||||||
|
// You can add more data-related routes here in the future
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
10
api/routes/itemLookup.js
Normal file
10
api/routes/itemLookup.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// routes/itemLookup.js
|
||||||
|
import express from "express";
|
||||||
|
import itemLookup from "../controllers/itemLookup.js";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
// Use the itemLookup controller for the POST request to '/item-lookup'
|
||||||
|
router.get("/item-lookup", itemLookup);
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -4,7 +4,6 @@ import cors from "cors";
|
|||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import dataRoutes from "./routes/dataRoutes.js";
|
import dataRoutes from "./routes/dataRoutes.js";
|
||||||
import inventoryRoutes from "./routes/inventoryRoutes.js";
|
import inventoryRoutes from "./routes/inventoryRoutes.js";
|
||||||
import cookieParser from "cookie-parser";
|
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
@@ -12,68 +11,23 @@ const app = express();
|
|||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(cookieParser());
|
|
||||||
|
|
||||||
|
// Use the itemLookupRoute with a base path, e.g., '/api'
|
||||||
app.use("/api/data", dataRoutes);
|
app.use("/api/data", dataRoutes);
|
||||||
app.use("/api/inventory", inventoryRoutes);
|
app.use("/api/inventory", inventoryRoutes);
|
||||||
|
|
||||||
/*
|
|
||||||
the below code needs to be encapsulated somehow. It requires a manual flow, including QUICKLY copy/pasting the code query string from the result of localhost:300/auth/ebay (below)
|
|
||||||
|
|
||||||
Here are the steps:
|
|
||||||
1. Go to localhost:3000/auth/ebay and copy/paste query string from code
|
|
||||||
2. Quickly go to the editor and paste that code into the const code= at /auth/ebay/callback
|
|
||||||
3. Go to localhost:3000/auth/ebay/callback and tokens will be in console - this is an 18 month deal, so we should tuck this code away
|
|
||||||
|
|
||||||
That is not really a callback... that was my intention, but it did not work out that way. That is simply an explicit API call to grab a user/refresh token from the manual process above
|
|
||||||
|
|
||||||
Next ToDos:
|
|
||||||
1. change the name of fetchEbayToken to fetchEbayReadToken
|
|
||||||
2. Add a fetchEbayUserToken endpoint and figure how to cycle user tokens every 2 hours with refresh token
|
|
||||||
3. Add refresh token to .env and figure out how to safely store user tokens serverside (cookie? knowledge gap here)
|
|
||||||
*/
|
|
||||||
app.get("/auth/ebay", async (req, res) => {
|
|
||||||
// the below URL is hardcoded because it's static in the eBay dev dashboard
|
|
||||||
const authUrl = `https://auth.ebay.com/oauth2/authorize?client_id=TylerPul-ebayimpo-PRD-a983027cf-9b6b8bba&response_type=code&redirect_uri=Tyler_Pulse-TylerPul-ebayim-ledkmyo&scope=https://api.ebay.com/oauth/api_scope https://api.ebay.com/oauth/api_scope/sell.marketing.readonly https://api.ebay.com/oauth/api_scope/sell.marketing https://api.ebay.com/oauth/api_scope/sell.inventory.readonly https://api.ebay.com/oauth/api_scope/sell.inventory https://api.ebay.com/oauth/api_scope/sell.account.readonly https://api.ebay.com/oauth/api_scope/sell.account https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly https://api.ebay.com/oauth/api_scope/sell.fulfillment https://api.ebay.com/oauth/api_scope/sell.analytics.readonly https://api.ebay.com/oauth/api_scope/sell.finances https://api.ebay.com/oauth/api_scope/sell.payment.dispute https://api.ebay.com/oauth/api_scope/commerce.identity.readonly https://api.ebay.com/oauth/api_scope/sell.reputation https://api.ebay.com/oauth/api_scope/sell.reputation.readonly https://api.ebay.com/oauth/api_scope/commerce.notification.subscription https://api.ebay.com/oauth/api_scope/commerce.notification.subscription.readonly https://api.ebay.com/oauth/api_scope/sell.stores https://api.ebay.com/oauth/api_scope/sell.stores.readonly`;
|
|
||||||
res.redirect(authUrl); // Redirect the user to eBay's sign-in page
|
|
||||||
});
|
|
||||||
|
|
||||||
// Step 2: Handle the redirect from eBay
|
|
||||||
app.get("/auth/ebay/callback", async (req, res) => {
|
|
||||||
// this code comes from a query string at localhost:3000/auth/ebay when we redirect
|
|
||||||
const code =
|
|
||||||
"v%5E1.1%23i%5E1%23p%5E3%23f%5E0%23r%5E1%23I%5E3%23t%5EUl41Xzc6NUE2ODc0NkU5Q0Q4N0QxQjhENTVCNzAxQTAwMEM2MzlfMF8xI0VeMjYw";
|
|
||||||
try {
|
|
||||||
// Step 3: Exchange the authorization code for access and refresh tokens
|
|
||||||
const tokenResponse = await fetch(
|
|
||||||
"https://api.ebay.com/identity/v1/oauth2/token",
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
|
||||||
Authorization: `Basic ${Buffer.from(
|
|
||||||
`${process.env.EBAY_CLIENT_ID}:${process.env.EBAY_CLIENT_SECRET}`
|
|
||||||
).toString("base64")}`,
|
|
||||||
},
|
|
||||||
// we use Tyler_Pulse-TylerPul-ebayim-ledkmyo and not the initial redirect URL
|
|
||||||
body: `grant_type=authorization_code&code=${code}&redirect_uri=Tyler_Pulse-TylerPul-ebayim-ledkmyo`,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!tokenResponse.ok) {
|
|
||||||
throw new Error("Failed to exchange authorization code for tokens");
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenData = await tokenResponse.json();
|
|
||||||
console.log("Access Token:", tokenData.access_token);
|
|
||||||
console.log("Refresh Token:", tokenData.refresh_token);
|
|
||||||
|
|
||||||
res.send("Authentication successful! Tokens acquired."); // For demonstration purposes; you might want to redirect the user or show a different message
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error during token exchange:", error);
|
|
||||||
res.status(500).send("Internal Server Error");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Environment: \n\n
|
||||||
|
${JSON.stringify(process.env)}`);
|
||||||
|
console.log(`Server running on port ${PORT}`); //These will both get updated to smartLogging later
|
||||||
|
console.log(`System Settings: \n
|
||||||
|
port = ${process.env.PORT} \n
|
||||||
|
debug = ${process.env.DEBUG} \n
|
||||||
|
dotenv_key = ${process.env.DOTENV_KEY} \n
|
||||||
|
ebay_client_id = ${process.env.EBAY_CLIENT_ID} \n
|
||||||
|
ebay_client_secret = ${process.env.EBAY_CLIENT_SECRET} \n
|
||||||
|
ebay_dev_id = ${process.env.EBAY_DEV_ID} \n
|
||||||
|
ebay_redirect_uri = ${process.env.EBAY_REDIRECT_URI} \n
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|||||||
63
api/utils/buildAddFixedPriceItemRequestXML.js
Normal file
63
api/utils/buildAddFixedPriceItemRequestXML.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
const buildAddFixedPriceItemRequestXML = (itemDetails) => {
|
||||||
|
// Constructing the XML payload using template literals
|
||||||
|
return `<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AddFixedPriceItemRequest xmlns="urn:ebay:apis:eBLBaseComponents">
|
||||||
|
<RequesterCredentials>
|
||||||
|
<eBayAuthToken>${itemDetails.userToken}</eBayAuthToken>
|
||||||
|
</RequesterCredentials>
|
||||||
|
<Item>
|
||||||
|
<Title>${itemDetails.title}</Title>
|
||||||
|
<Description><${itemDetails.description}></Description>
|
||||||
|
<PrimaryCategory>
|
||||||
|
<CategoryID>${itemDetails.categoryID}</CategoryID>
|
||||||
|
</PrimaryCategory>
|
||||||
|
<StartPrice>${itemDetails.startPrice}</StartPrice>
|
||||||
|
<ConditionID>${itemDetails.conditionID}</ConditionID>
|
||||||
|
<Country>${itemDetails.country}</Country>
|
||||||
|
<Currency>${itemDetails.currency}</Currency>
|
||||||
|
<DispatchTimeMax>${itemDetails.dispatchTimeMax}</DispatchTimeMax>
|
||||||
|
<ListingDuration>${itemDetails.listingDuration}</ListingDuration>
|
||||||
|
<ListingType>${itemDetails.listingType}</ListingType>
|
||||||
|
<PaymentMethods>
|
||||||
|
<PaymentMethod>${
|
||||||
|
itemDetails.paymentMethods
|
||||||
|
? itemDetails.paymentMethods[0]
|
||||||
|
: "DefaultPaymentMethod"
|
||||||
|
}</PaymentMethod>
|
||||||
|
</PaymentMethods>
|
||||||
|
|
||||||
|
<PayPalEmailAddress>${itemDetails.payPalEmailAddress}</PayPalEmailAddress>
|
||||||
|
<PictureDetails>
|
||||||
|
<PictureURL>${itemDetails.imageURLs}</PictureURL>
|
||||||
|
</PictureDetails>
|
||||||
|
<PostalCode>${itemDetails.postalCode}</PostalCode>
|
||||||
|
<Quantity>${itemDetails.quantity}</Quantity>
|
||||||
|
<ReturnPolicy>
|
||||||
|
<ReturnsAcceptedOption>${
|
||||||
|
itemDetails.returnsAcceptedOption
|
||||||
|
}</ReturnsAcceptedOption>
|
||||||
|
<RefundOption>${itemDetails.refundOption}</RefundOption>
|
||||||
|
<ReturnsWithinOption>${
|
||||||
|
itemDetails.returnsWithinOption
|
||||||
|
}</ReturnsWithinOption>
|
||||||
|
<Description>${itemDetails.returnPolicyDescription}</Description>
|
||||||
|
<ShippingCostPaidByOption>${
|
||||||
|
itemDetails.shippingCostPaidByOption
|
||||||
|
}</ShippingCostPaidByOption>
|
||||||
|
</ReturnPolicy>
|
||||||
|
<ShippingDetails>
|
||||||
|
<ShippingType>${itemDetails.shippingType}</ShippingType>
|
||||||
|
<ShippingServiceOptions>
|
||||||
|
<ShippingServicePriority>1</ShippingServicePriority>
|
||||||
|
<ShippingService>${itemDetails.shippingService}</ShippingService>
|
||||||
|
<ShippingServiceCost>${
|
||||||
|
itemDetails.shippingServiceCost
|
||||||
|
}</ShippingServiceCost>
|
||||||
|
</ShippingServiceOptions>
|
||||||
|
</ShippingDetails>
|
||||||
|
<Site>${itemDetails.site}</Site>
|
||||||
|
</Item>
|
||||||
|
</AddFixedPriceItemRequest>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default buildAddFixedPriceItemRequestXML;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Need to figoure out expiration and make sure to cycle this appropriately to avoid unnecessary calls
|
// Need to figoure out expiration and make sure to cycle this appropriately to avoid unnecessary calls
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
|
|
||||||
const fetchEbayApplicationToken = async () => {
|
const fetchEbayReadToken = async () => {
|
||||||
const ebayClientId = process.env.EBAY_CLIENT_ID;
|
const ebayClientId = process.env.EBAY_CLIENT_ID;
|
||||||
const ebayClientSecret = process.env.EBAY_CLIENT_SECRET;
|
const ebayClientSecret = process.env.EBAY_CLIENT_SECRET;
|
||||||
const credentials = Buffer.from(
|
const credentials = Buffer.from(
|
||||||
@@ -15,7 +15,7 @@ const fetchEbayApplicationToken = async () => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
"authorization": `Basic ${credentials}`,
|
Authorization: `Basic ${credentials}`,
|
||||||
},
|
},
|
||||||
body: "grant_type=client_credentials&scope=https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope",
|
body: "grant_type=client_credentials&scope=https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope",
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,6 @@ const fetchEbayApplicationToken = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
return data.access_token;
|
return data.access_token;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching eBay OAuth token:", error);
|
console.error("Error fetching eBay OAuth token:", error);
|
||||||
@@ -37,4 +36,4 @@ const fetchEbayApplicationToken = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fetchEbayApplicationToken;
|
export default fetchEbayReadToken;
|
||||||
@@ -1,66 +1,41 @@
|
|||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
|
|
||||||
const fetchEbayUserToken = async (req, res) => {
|
const fetchEbayUserToken = async (authorizationCode) => {
|
||||||
const ebayClientId = process.env.EBAY_CLIENT_ID;
|
const clientId = process.env.EBAY_CLIENT_ID;
|
||||||
const ebayClientSecret = process.env.EBAY_CLIENT_SECRET;
|
const clientSecret = process.env.EBAY_CLIENT_SECRET;
|
||||||
const refreshToken = process.env.EBAY_REFRESH_TOKEN; // This is retrieved from a manual process
|
const redirectUri = process.env.EBAY_REDIRECT_URI; // Make sure this matches the URI registered with eBay
|
||||||
const credentials = Buffer.from(`${ebayClientId}:${ebayClientSecret}`).toString("base64");
|
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString(
|
||||||
const scopes = encodeURIComponent([
|
"base64"
|
||||||
"https://api.ebay.com/oauth/api_scope",
|
);
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.marketing.readonly",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.marketing",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.inventory.readonly",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.inventory",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.account.readonly",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.account",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.fulfillment",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.analytics.readonly",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.finances",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.payment.dispute",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/commerce.identity.readonly",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.reputation",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.reputation.readonly",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/commerce.notification.subscription",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/commerce.notification.subscription.readonly",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.stores",
|
|
||||||
"https://api.ebay.com/oauth/api_scope/sell.stores.readonly"
|
|
||||||
].join(' '));
|
|
||||||
|
|
||||||
const token = req.cookies.ebayUserToken;
|
|
||||||
const tokenExpiry = req.cookies.ebayUserTokenExpiry ? new Date(req.cookies.ebayUserTokenExpiry) : null;
|
|
||||||
|
|
||||||
// Check if the token exists and is not expired
|
|
||||||
if (token && tokenExpiry && new Date() < tokenExpiry) {
|
|
||||||
return token; // Token is valid, use it
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch("https://api.ebay.com/identity/v1/oauth2/token", {
|
const response = await fetch(
|
||||||
method: "POST",
|
"https://api.ebay.com/identity/v1/oauth2/token",
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
method: "POST",
|
||||||
"Authorization": `Basic ${credentials}`,
|
headers: {
|
||||||
},
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
body: `grant_type=refresh_token&refresh_token=${refreshToken}&scope=${scopes}`,
|
Authorization: `Basic ${credentials}`,
|
||||||
});
|
},
|
||||||
|
body: `grant_type=authorization_code&code=${authorizationCode}&redirect_uri=${encodeURIComponent(
|
||||||
|
redirectUri
|
||||||
|
)}`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorBody = await response.text();
|
const errorBody = await response.text();
|
||||||
throw new Error(`Failed to fetch eBay user token: ${response.status} ${response.statusText} - ${errorBody}`);
|
throw new Error(
|
||||||
|
`Failed to fetch eBay user token: ${response.status} ${response.statusText} - ${errorBody}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const expiryDuration = 1.92 * 60 * 60 * 1000; // 1 hour 55 minutes in milliseconds
|
return data.access_token; // This is the User access token
|
||||||
|
|
||||||
// Store the new token in an HTTP-only cookie
|
|
||||||
res.cookie('ebayUserToken', data.access_token, { httpOnly: true, maxAge: expiryDuration });
|
|
||||||
|
|
||||||
return data.access_token;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching eBay user token:", error);
|
console.error("Error fetching eBay user token:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fetchEbayUserToken
|
export default fetchEbayUserToken;
|
||||||
|
|||||||
@@ -4,5 +4,15 @@ services:
|
|||||||
build: ./api
|
build: ./api
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
# environment:
|
||||||
|
# PORT: 8005
|
||||||
|
# DEBUG: "false"
|
||||||
|
# DOTENV_KEY: "DotKeyDC"
|
||||||
|
# EBAY_CLIENT_ID: "EbayClientIDDC"
|
||||||
|
# EBAY_CLIENT_SECRET: "EbayClientSecretDC"
|
||||||
|
# EBAY_DEV_ID: "EbayDevIDDC"
|
||||||
|
# EBAY_REDIRECT_URI: "EbayRedirectURIDC"
|
||||||
volumes:
|
volumes:
|
||||||
- ./api:/usr/src/app # Mount the local ./api directory to /usr/src/app in the container
|
- ./api:/usr/src/app # Mount the local ./api directory to /usr/src/app in the container
|
||||||
|
|||||||
11
envSample
Normal file
11
envSample
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
PORT=8003
|
||||||
|
DEBUG=true
|
||||||
|
DOTENV_KEY=DotKey
|
||||||
|
EBAY_CLIENT_ID=EbayClientID
|
||||||
|
EBAY_CLIENT_SECRET=EbayClientSecret
|
||||||
|
EBAY_DEV_ID=EbayDevID
|
||||||
|
EBAY_REDIRECT_URI=EbayRedirectURI
|
||||||
|
|
||||||
|
|
||||||
|
#I'm not 100% on this, but I think the --env-file flag for docker containers should handle this. We just
|
||||||
|
#need to figure out how that gets consumed.
|
||||||
Reference in New Issue
Block a user