Tried to get serverless to work

This commit is contained in:
Keith Irwin 2023-03-02 15:35:21 -07:00
parent 537e7c9b9c
commit 3d0a2447ac
Signed by: ki9
GPG Key ID: DF773B3F4A88DA86
12 changed files with 284 additions and 213 deletions

View File

@ -1,191 +0,0 @@
const { DateTime } = require('luxon')
const fs = require('fs')
const pluginRss = require('@11ty/eleventy-plugin-rss')
const pluginSyntaxHighlight = require('@11ty/eleventy-plugin-syntaxhighlight')
const pluginNavigation = require('@11ty/eleventy-navigation')
const EleventyServerlessBundlerPlugin = require('@11ty/eleventy').EleventyServerlessBundlerPlugin
const markdownIt = require('markdown-it')
const dirs ={
input: '_src',
includes: '_includes',
data: '_data',
output: '_site',
}
module.exports = (eleventyConfig) => {
// Add plugins
eleventyConfig.addPlugin(pluginRss)
eleventyConfig.addPlugin(pluginSyntaxHighlight)
eleventyConfig.addPlugin(pluginNavigation)
// Serverless
eleventyConfig.addPlugin(EleventyServerlessBundlerPlugin, {
name: 'serverless',
functionsDir: './functions/',
})
// https://www.11ty.dev/docs/data-deep-merge/
eleventyConfig.setDataDeepMerge(true)
// Alias `layout: post` to `layout: layouts/post.njk`
eleventyConfig.addLayoutAlias('post', 'layouts/post.njk')
eleventyConfig.addFilter('readableDate', dateObj => {
return DateTime.fromJSDate(dateObj, {zone: 'utc'}).toFormat('dd LLL yyyy')
})
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
eleventyConfig.addFilter('htmlDateString', (dateObj) => {
return DateTime.fromJSDate(dateObj, {zone: 'utc'}).toFormat('yyyy-LL-dd')
})
// Get the first `n` elements of a collection.
eleventyConfig.addFilter('head', (array, n) =>
(n<0)? array.slice(n) : array.slice(0, n)
)
// Sanitize out apostrophes
eleventyConfig.addFilter('noApostrophes', (str) =>
str.replace(/['"]+/g,'')
)
// Return the smallest number argument
eleventyConfig.addFilter('min', (...numbers) =>
Math.min.apply(null, numbers)
)
eleventyConfig.addFilter('toFixed', (num,digits) =>
num? num.toFixed(digits) : ''
)
eleventyConfig.addFilter('filterCatList', cats =>
// should match the list in categories.njk
(cats || []).filter(cat => ['all', 'nav', 'post', 'posts'].indexOf(cat) === -1)
)
eleventyConfig.addFilter('filterTagList', tags =>
// should match the list in tags.njk
(tags || []).filter(tag => ['all', 'nav', 'post', 'posts'].indexOf(tag) === -1)
)
eleventyConfig.addFilter('dimsToVol', (dims) => {
return {
in3: Object.values(dims.in).reduce((a,c) => a*c, 1)
}
} )
// Create an array of all tags
eleventyConfig.addCollection('tagList', function(collection) {
let tagSet = new Set()
collection.getAll().forEach(item => {
(item.data.tags || []).forEach(tag => tagSet.add(tag))
});
return [...tagSet];
})
// Customize Markdown library and settings:
let markdownLibrary = markdownIt({
html: true,
breaks: true,
linkify: true
})
eleventyConfig.setLibrary('md', markdownLibrary)
// Markdown "md" filter for nunjucks
// https://github.com/11ty/eleventy/issues/658#issuecomment-599173643
const mdFilter = new markdownIt({html:true})
eleventyConfig.addPairedShortcode('md', (content) =>
mdFilter.render(content) )
// Override Browsersync defaults (used only with --serve)
eleventyConfig.setBrowserSyncConfig({
callbacks: {
ready: function(err, browserSync) {
const content_404 = fs.readFileSync('_site/404.html')
browserSync.addMiddleware('*', (req, res) => {
// Provides the 404 content without redirect.
res.writeHead(404, {'Content-Type': 'text/html; charset=UTF-8'})
res.write(content_404)
res.end()
});
},
},
ui: false,
ghostMode: false,
})
eleventyConfig.addPassthroughCopy('_src/assets/fonts')
eleventyConfig.addPassthroughCopy('_src/assets/base.css')
eleventyConfig.addPassthroughCopy('_src/assets/shop.css')
eleventyConfig.addPassthroughCopy('_src/assets/site.webmanifest')
eleventyConfig.addPassthroughCopy('_src/assets/scripts')
// Video shortcode
eleventyConfig.addShortcode('vid', (videoName) => `
<video controls width="100%">
<source src="${videoName}" type="video/${videoName.split('.').pop()}">
<a href="${videoName}">${videoName}</a>
</video>`)
// Youtube shortcode
eleventyConfig.addShortcode('yt', (shortCode) => `<iframe width="100%" height="400vh" src="https://www.youtube-nocookie.com/embed/${shortCode}" frameborder="0" allow="autoplay;clipboard-write;encrypted-media;picture-in-picture" allowfullscreen></iframe>`)
// Products and categories collections
eleventyConfig.addCollection('products', async (ca) =>
ca.getFilteredByTag('product')
)
eleventyConfig.addCollection('product categories', async (ca) =>
ca.getFilteredByGlob([`${dirs.input}/shop/*/`,`${dirs.input}/shop/**/*/`])
)
// Save product data to data/products/pid-sid.json
console.log(eleventyConfig.collections)
return {
// Control which files Eleventy will process
// e.g.: *.md, *.njk, *.html, *.liquid
templateFormats: [
'md',
'txt',
'njk',
'html',
'liquid',
'pdf',
'png',
'jpg',
'svg',
'ico',
'gif',
'mp4',
],
// -----------------------------------------------------------------
// If your site deploys to a subdirectory, change `pathPrefix`.
// Dont worry about leading and trailing slashes, we normalize these.
// If you dont have a subdirectory, use '' or '/' (they do the same thing)
// This is only used for link URLs (it does not affect your file structure)
// Best paired with the `url` filter: https://www.11ty.dev/docs/filters/url/
// You can also pass this in on the command line using `--pathprefix`
// Optional (default is shown)
pathPrefix: '/',
// -----------------------------------------------------------------
// Pre-process *.md files with: (default: `liquid`)
markdownTemplateEngine: 'njk',
// Pre-process *.html files with: (default: `liquid`)
htmlTemplateEngine: 'njk',
// Opt-out of pre-processing global data JSON files: (default: `liquid`)
dataTemplateEngine: false,
// Set at top
dir: dirs,
}
}

4
.gitignore vendored
View File

@ -9,8 +9,8 @@ _src/_data/
# Build
_site/
functions/serverless/**
!functions/serverless/index.js
_functions/dynamic/**
!_functions/dynamic/index.js
# Backups and tempfiles
**/*.bak

View File

@ -0,0 +1,57 @@
const { EleventyServerless } = require("@11ty/eleventy");
// Explicit dependencies for the bundler from config file and global data.
// The file is generated by the Eleventy Serverless Bundler Plugin.
require("./eleventy-bundler-modules.js");
async function handler(event) {
let elev = new EleventyServerless("dynamic", {
path: new URL(event.rawUrl).pathname,
query: event.multiValueQueryStringParameters || event.queryStringParameters,
functionsDir: "_functions",
});
try {
let [page] = await elev.getOutput();
// If you want some of the data cascade available in `page.data`, use `eleventyConfig.dataFilterSelectors`.
// Read more: https://www.11ty.dev/docs/config/#data-filter-selectors
return {
statusCode: 200,
headers: {
"Content-Type": "text/html; charset=UTF-8",
},
body: page.content,
};
} catch (error) {
// Only console log for matching serverless paths
// (otherwise youll see a bunch of BrowserSync 404s for non-dynamic URLs during --serve)
if (elev.isServerlessUrl(event.path)) {
console.log("Serverless Error:", error);
}
return {
statusCode: error.httpStatusCode || 500,
body: JSON.stringify(
{
error: error.message,
},
null,
2
),
};
}
}
// Choose one:
// * Runs on each request: AWS Lambda, Netlify Function
// * Runs on first request only: Netlify On-demand Builder
// 1. Dont forget to `npm install @netlify/functions`
// 2. Also use `redirects: "netlify-toml-builders"` in your config files serverless bundler options:
// https://www.11ty.dev/docs/plugins/serverless/#bundler-options
exports.handler = handler;
//const { builder } = require("@netlify/functions");
//exports.handler = builder(handler);

View File

@ -192,11 +192,11 @@ class Cart { constructor() {
)
self.checkoutButtonText = ko.pureComputed(() => {
if (self.isLoading())
return '📃 Creating order...'
return 'Checking out...'
else if (self.subtotal()>0)
return `📃 Create order for ${self.total_pretty()}`
return `Checkout for ${self.total_pretty()}`
else
return '📃 Create order'
return 'Checkout'
})
self.checkoutButtonHelp = ko.pureComputed(() => {
if (self.total()===0) return 'Add items to your cart first'
@ -247,17 +247,17 @@ class Cart { constructor() {
if (self.payment_method()==='USD') {
// Send to server
const res = await fetch(`${API_DOMAIN}/order`, {
method: 'POST', mode: 'cors',
headers: {'Content-Type':'application/json'},
body: ko.toJSON(order),
})
localStorage.setItem('order_data', await res.json())
//const res = await fetch('/shop/checkout/order', {
// method: 'POST', mode: 'cors',
// headers: {'Content-Type':'application/json'},
// body: ko.toJSON(order),
//})
//localStorage.setItem('order_data', await res.json())
// Send user to checkout
window.location = '/shop/checkout/stripe/'
} else self.isLoading(false)
}
// On first load, read from localStorage
Object.keys(localStorage).forEach(i => {
if (i.substring(0,5)==='cart_') {

View File

@ -1,6 +1,5 @@
const formatUSD = (v) => v.toLocaleString(undefined, {
style: 'currency', currency: 'USD' })
const order = JSON.parse(localStorage.getItem('order_data'))
const cancel = () => {
localStorage.removeItem('order_data')
window.location = '/shop/cart/'
@ -18,8 +17,8 @@ const elements = stripe.elements({
fontFamily: 'dejavu, monospace',
},
},
clientSecret: order.stripe_secret,
})
clientSecret: JSON.parse(localStorage.getItem('order_data')).stripe_secret,
})
const paymentElement = elements.create('payment', {
paymentMethodOrder: ['card'],
layout: 'tabs', // or 'accordian'

View File

@ -86,14 +86,13 @@ title: Cart
</div>
</div>
<div>
<button style="margin:calc(2vh + 1vw) 0" data-bind="visible:(payment_method()==='USD'), click:checkout,enable:(checkoutEnabled()&&!isLoading()), attr:{title:checkoutButtonHelp()}, text:checkoutButtonText">📃 Create order</button>
<button style="margin:calc(2vh + 1vw) 0" data-bind="visible:(payment_method()==='USD'), click:checkout,enable:(checkoutEnabled()&&!isLoading()), attr:{title:checkoutButtonHelp()}, text:checkoutButtonText">Checkout</button>
</div>
<script>
const API_DOMAIN = '{{env.API_DOMAIN}}'
const ZIPS_TO_STATES = '{{zipsToStates|dump|safe}}'
const ZIPS_TO_ZONES = '{{zipsToZones|dump|safe}}'
const USPS_SHIPPING_RATES = '{{uspsShippingRates|dump|safe}}'
const ZIPS_TO_STATES = JSON.parse('{{zipsToStates|dump|safe}}')
const ZIPS_TO_ZONES = JSON.parse('{{zipsToZones|dump|safe}}')
const USPS_SHIPPING_RATES = JSON.parse('{{uspsShippingRates|dump|safe}}')
</script>
<script src="/assets/scripts/knockout-3.5.1.min.js" integrity="sha512-2AL/VEauKkZqQU9BHgnv48OhXcJPx9vdzxN1JrKDVc4FPU/MEE/BZ6d9l0mP7VmvLsjtYwqiYQpDskK9dG8KBA=="></script>
<script src="/assets/scripts/cart.js" defer></script>

View File

@ -1,10 +1,14 @@
---
layout: layouts/base.njk
title: Checkout
permalink:
dynamic: /shop/checkout/stripe/
---
<h1>📃 Order</h1>
<h1>Checkout</h1>
<script src="https://js.stripe.com/v3/"></script>
<p>page: {{page|dump}}</p>
<p>eleventy: {{eleventy|dump}}</p>
<table style="width:100%;text-align:left"><thead>
<th>Item</th>
<th style="text-align:right">Price</th>

View File

@ -11,4 +11,4 @@ eleventyNavigation:
These are the gadgets we have in stock right now. When you've picked out what you want, visit your [cart](/shop/cart/) to check out.
{% set productslist = collections.products -%}
{% include 'productslist.njk' %}
{% include 'productslist.njk' %}

197
eleventy.config.js Normal file
View File

@ -0,0 +1,197 @@
const { DateTime } = require('luxon')
const fs = require('fs')
const pluginRss = require('@11ty/eleventy-plugin-rss')
const pluginSyntaxHighlight = require('@11ty/eleventy-plugin-syntaxhighlight')
const pluginNavigation = require('@11ty/eleventy-navigation')
const EleventyServerlessBundlerPlugin = require('@11ty/eleventy').EleventyServerlessBundlerPlugin
const markdownIt = require('markdown-it')
const dirs ={
input: '_src',
includes: '_includes',
data: '_data',
output: '_site',
}
module.exports = (eleventyConfig) => {
// Add plugins
eleventyConfig.addPlugin(pluginRss)
eleventyConfig.addPlugin(pluginSyntaxHighlight)
eleventyConfig.addPlugin(pluginNavigation)
// Serverless
eleventyConfig.addPlugin(EleventyServerlessBundlerPlugin, {
name: 'dynamic',
functionsDir: '_functions',
config: (eleventyConfig) => {
console.log(`e.body: ${e.body}`)
eleventyConfig.addGlobalData('postbody', e.body)
},
})
// https://www.11ty.dev/docs/data-deep-merge/
eleventyConfig.setDataDeepMerge(true)
// Alias `layout: post` to `layout: layouts/post.njk`
eleventyConfig.addLayoutAlias('post', 'layouts/post.njk')
eleventyConfig.addFilter('readableDate', dateObj => {
return DateTime.fromJSDate(dateObj, {zone: 'utc'}).toFormat('dd LLL yyyy')
})
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
eleventyConfig.addFilter('htmlDateString', (dateObj) => {
return DateTime.fromJSDate(dateObj, {zone: 'utc'}).toFormat('yyyy-LL-dd')
})
// Get the first `n` elements of a collection.
eleventyConfig.addFilter('head', (array, n) =>
(n<0)? array.slice(n) : array.slice(0, n)
)
// Sanitize out apostrophes
eleventyConfig.addFilter('noApostrophes', (str) =>
str.replace(/['"]+/g,'')
)
// Return the smallest number argument
eleventyConfig.addFilter('min', (...numbers) =>
Math.min.apply(null, numbers)
)
eleventyConfig.addFilter('toFixed', (num,digits) =>
num? num.toFixed(digits) : ''
)
eleventyConfig.addFilter('filterCatList', cats =>
// should match the list in categories.njk
(cats || []).filter(cat => ['all', 'nav', 'post', 'posts'].indexOf(cat) === -1)
)
eleventyConfig.addFilter('filterTagList', tags =>
// should match the list in tags.njk
(tags || []).filter(tag => ['all', 'nav', 'post', 'posts'].indexOf(tag) === -1)
)
eleventyConfig.addFilter('dimsToVol', (dims) => {
return {
in3: Object.values(dims.in).reduce((a,c) => a*c, 1)
}
} )
// Create an array of all tags
eleventyConfig.addCollection('tagList', function(collection) {
let tagSet = new Set()
collection.getAll().forEach(item => {
(item.data.tags || []).forEach(tag => tagSet.add(tag))
});
return [...tagSet];
})
// Customize Markdown library and settings:
let markdownLibrary = markdownIt({
html: true,
breaks: true,
linkify: true
})
eleventyConfig.setLibrary('md', markdownLibrary)
// Markdown "md" filter for nunjucks
// https://github.com/11ty/eleventy/issues/658#issuecomment-599173643
const mdFilter = new markdownIt({html:true})
eleventyConfig.addPairedShortcode('md', (content) =>
mdFilter.render(content) )
// Override Browsersync defaults (used only with --serve)
eleventyConfig.setBrowserSyncConfig({
callbacks: {
ready: function(err, browserSync) {
const content_404 = fs.readFileSync('_site/404.html')
browserSync.addMiddleware('*', (req, res) => {
// Provides the 404 content without redirect.
res.writeHead(404, {'Content-Type': 'text/html; charset=UTF-8'})
res.write(content_404)
res.end()
});
},
},
ui: false,
ghostMode: false,
})
eleventyConfig.addPassthroughCopy('_src/assets/fonts')
eleventyConfig.addPassthroughCopy('_src/assets/base.css')
eleventyConfig.addPassthroughCopy('_src/assets/shop.css')
eleventyConfig.addPassthroughCopy('_src/assets/site.webmanifest')
eleventyConfig.addPassthroughCopy('_src/assets/scripts')
// Video shortcode
eleventyConfig.addShortcode('vid', (videoName) => `
<video controls width="100%">
<source src="${videoName}" type="video/${videoName.split('.').pop()}">
<a href="${videoName}">${videoName}</a>
</video>`)
// Youtube shortcode
eleventyConfig.addShortcode('yt', (shortCode) => `<iframe width="100%" height="400vh" src="https://www.youtube-nocookie.com/embed/${shortCode}" frameborder="0" allow="autoplay;clipboard-write;encrypted-media;picture-in-picture" allowfullscreen></iframe>`)
// Products and categories collections
eleventyConfig.addCollection('products', async (ca) =>
ca.getFilteredByTag('product')
)
eleventyConfig.addCollection('product categories', async (ca) =>
ca.getFilteredByGlob([`${dirs.input}/shop/*/`,`${dirs.input}/shop/**/*/`])
)
// Save product data to data/products/pid-sid.json
// ;(async () => {
// console.log(await eleventyConfig.collections.products)
// })()
return {
// Control which files Eleventy will process
// e.g.: *.md, *.njk, *.html, *.liquid
templateFormats: [
'md',
'txt',
'njk',
'html',
'liquid',
'pdf',
'png',
'jpg',
'svg',
'ico',
'gif',
'mp4',
],
// -----------------------------------------------------------------
// If your site deploys to a subdirectory, change `pathPrefix`.
// Dont worry about leading and trailing slashes, we normalize these.
// If you dont have a subdirectory, use '' or '/' (they do the same thing)
// This is only used for link URLs (it does not affect your file structure)
// Best paired with the `url` filter: https://www.11ty.dev/docs/filters/url/
// You can also pass this in on the command line using `--pathprefix`
// Optional (default is shown)
pathPrefix: '/',
// -----------------------------------------------------------------
// Pre-process *.md files with: (default: `liquid`)
markdownTemplateEngine: 'njk',
// Pre-process *.html files with: (default: `liquid`)
htmlTemplateEngine: 'njk',
// Opt-out of pre-processing global data JSON files: (default: `liquid`)
dataTemplateEngine: false,
// Set at top
dir: dirs,
}
}

6
netlify.toml Normal file
View File

@ -0,0 +1,6 @@
[[redirects]]
from = "/shop/checkout/stripe/"
to = "/.netlify/functions/dynamic"
status = 200
force = true
_generated_by_eleventy_serverless = "dynamic"