diff options
-rw-r--r-- | package-lock.json | 33 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/css/mobile-styles.css | 23 | ||||
-rw-r--r-- | src/css/styles.css | 31 | ||||
-rw-r--r-- | src/html/admin.html | 3 | ||||
-rw-r--r-- | src/index.js | 3 | ||||
-rw-r--r-- | src/server.js | 90 | ||||
-rw-r--r-- | src/templates.js | 115 | ||||
-rw-r--r-- | src/templates/blog-single.html | 23 | ||||
-rw-r--r-- | src/templates/blog.html | 22 | ||||
-rw-r--r-- | src/templates/bread.html | 23 | ||||
-rw-r--r-- | src/templates/feed.html | 24 | ||||
-rw-r--r-- | src/templates/footer.html | 14 | ||||
-rw-r--r-- | src/templates/index.html | 20 | ||||
-rw-r--r-- | src/templates/misc.html (renamed from src/html/misc.html) | 1 | ||||
-rw-r--r-- | src/templates/navigation.html | 10 | ||||
-rw-r--r-- | src/templates/projects.html (renamed from src/html/projects.html) | 1 | ||||
-rw-r--r-- | src/templates/tags.html | 22 |
18 files changed, 303 insertions, 156 deletions
diff --git a/package-lock.json b/package-lock.json index 1f39823..8f43397 100644 --- a/package-lock.json +++ b/package-lock.json @@ -458,6 +458,18 @@ "assert-plus": "^1.0.0" } }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -807,6 +819,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -1075,6 +1092,11 @@ "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, "sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -1179,6 +1201,12 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "uglify-js": { + "version": "3.11.6", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.6.tgz", + "integrity": "sha512-oASI1FOJ7BBFkSCNDZ446EgkSuHkOZBuqRFrwXIKWCoXw8ZXQETooTQjkAcBS03Acab7ubCKsXnwuV2svy061g==", + "optional": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1235,6 +1263,11 @@ "@types/node": "*" } }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, "xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", diff --git a/package.json b/package.json index 8012d6a..4fab4bf 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "body-parser": "^1.19.0", "cookie-parser": "^1.4.4", "express": "^4.17.1", + "handlebars": "^4.7.6", "jsonwebtoken": "^8.5.1", "marked": "^1.1.1", "minimist": ">=0.2.1", diff --git a/src/css/mobile-styles.css b/src/css/mobile-styles.css new file mode 100644 index 0000000..36553cc --- /dev/null +++ b/src/css/mobile-styles.css @@ -0,0 +1,23 @@ +@media screen and (min-width: 900px) { + body { + width: 100%; + /* margin: auto; */ + } + .card-img { + display: flex; + flex-flow: row wrap; + align-content: flex-start; + } + .card img { + width: 400px; + } + form input[type=text], input[type=password], input[type=email] { + max-width: 600px; + } + form textarea { + max-width: 600px; + } + form input[type=submit] { + margin-left: 0px; + } +}
\ No newline at end of file diff --git a/src/css/styles.css b/src/css/styles.css index 522b007..f406104 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -11,8 +11,9 @@ body { - width: 100%; - margin: 0px; + width: 80%; + margin: auto; + /* margin: 0px; */ background-color: var(--background); font-family: Arial, Helvetica, sans-serif; } @@ -93,7 +94,7 @@ a { width: 100%; } .feed { - width: 80%; + /* width: 80%; */ min-width: 200px; margin: auto; display: flex; @@ -109,30 +110,6 @@ p { margin-top: 0; } -@media screen and (min-width: 900px) { - body { - width: 100%; - /* margin: auto; */ - } - .card-img { - display: flex; - flex-flow: row wrap; - align-content: flex-start; - } - .card img { - width: 400px; - } - form input[type=text], input[type=password], input[type=email] { - max-width: 600px; - } - form textarea { - max-width: 600px; - } - form input[type=submit] { - margin-left: 0px; - } -} - .titlebar { padding: 5px; border-top: 1px solid var(--light-text); diff --git a/src/html/admin.html b/src/html/admin.html index c6683c3..3eaa60a 100644 --- a/src/html/admin.html +++ b/src/html/admin.html @@ -32,6 +32,7 @@ </head> <body> + <noscript>Please enable Javascript!</noscript> <h1> <a class="navigation" href="/" title="marks.kitchen"><</a> Admin @@ -135,4 +136,4 @@ </div> </body> -</html>
\ No newline at end of file +</html> diff --git a/src/index.js b/src/index.js index 62d8bb5..4b9a3c6 100644 --- a/src/index.js +++ b/src/index.js @@ -100,8 +100,9 @@ function setUpModels(){ } const models = setUpModels(); +const templates = require('./templates'); sync(); -server.setUpRoutes(models, jwtFunctions, database); +server.setUpRoutes(models, jwtFunctions, database, templates.setUpTemplates()); server.listen(config.port); diff --git a/src/server.js b/src/server.js index 7be1237..e82fc12 100644 --- a/src/server.js +++ b/src/server.js @@ -87,7 +87,7 @@ async function constructFeedFromType(models, postType){ return constructFeed(posts) } -async function constructSinglePost(models, postType, postId){ +async function formatPostsforSingle(models, postType, postId){ var posts = await models.posts.findAll({ where: { type: postType, @@ -96,11 +96,25 @@ async function constructSinglePost(models, postType, postId){ }); posts = posts.map(x => x.get({ plain: true })); await addImagesAndTagsToPosts(models, posts) - - return constructFeed(posts) + posts.forEach(post => { + post.createdAt = post.createdAt.toString().substring(0, 10) + }) + return posts +} + +async function formatPostsForType(models, postType){ + var posts = await models.posts.findAll({ + where: { type: postType }, order: [['createdAt', 'DESC']] + }); + posts = posts.map(x => x.get({ plain: true })); + await addImagesAndTagsToPosts(models, posts) + posts.forEach(post => { + post.createdAt = post.createdAt.toString().substring(0, 10) + }) + return posts; } -function setUpRoutes(models, jwtFunctions, database) { +function setUpRoutes(models, jwtFunctions, database, templates) { // Authentication routine server.use(function (req, res, next) { if (req.path.toLowerCase().startsWith("/admin")) { @@ -142,41 +156,25 @@ function setUpRoutes(models, jwtFunctions, database) { }) server.get('/', async (req, res) => { - var html = [] - html.push(templates["index"]["pre"]) - html.push(templates["titlebar"]) - html.push(await constructFeedFromType(models, "index")) - html.push(templates["footer"]) - html.push(templates["index"]["post"]) - - res.status(200).send(html.join("")) + let posts = await formatPostsForType(models, "index") + let body = templates["index"]({posts}); + res.status(200).send(body) }) server.get('/bread', async (req, res) => { - var html = [] - html.push(templates["bread"]["pre"]) - html.push(await constructFeedFromType(models, "bread")) - html.push(templates["footer"]) - html.push(templates["bread"]["post"]) - - res.status(200).send(html.join("")) + let posts = await formatPostsForType(models, "bread") + let body = templates["bread"]({posts}); + res.status(200).send(body) }) server.get('/blog', async (req, res) => { - var html = [] - html.push(templates["blog"]["pre"]) - html.push(await constructFeedFromType(models, "blog")) - html.push(templates["footer"]) - html.push(templates["blog"]["post"]) - - res.status(200).send(html.join("")) + let posts = await formatPostsForType(models, "blog") + let body = templates["blog"]({posts}); + res.status(200).send(body) }) server.get('/post/:type/:id', async (req, res) => { - var html = [] - html.push(templates["blog"]["pre"]) - html.push(await constructSinglePost(models, req.params.type, req.params.id)) - html.push(templates["footer"]) - html.push(templates["blog"]["post"]) - - res.status(200).send(html.join("")) + let posts = await formatPostsforSingle(models, req.params.type, req.params.id) + let date = posts[0].createdAt; + let body = templates["blog-single"]({posts, date}); + res.status(200).send(body) }) server.get('/tags/:name', async (req, res) => { const { name } = req.params; @@ -189,15 +187,12 @@ function setUpRoutes(models, jwtFunctions, database) { }); posts = posts.map(x => x.get({ plain: true })); await addImagesAndTagsToPosts(models, posts) + posts.forEach(post => { + post.createdAt = post.createdAt.toString().substring(0, 10) + }) - var html = [] - html.push(templates["blog"]["pre"]) - html.push(`<h1>#${name}</h1>`) - html.push(await constructFeed(posts)) - html.push(templates["footer"]) - html.push(templates["blog"]["post"]) - - res.status(200).send(html.join("")) + let body = templates["tags"]({posts, name}) + res.status(200).send(body) }) server.get('/admin', (req, res) => res.sendFile(__dirname + "/html/admin.html")); @@ -206,14 +201,23 @@ function setUpRoutes(models, jwtFunctions, database) { server.get('/email-success', (req, res) => res.sendFile(__dirname + "/html/email-success.html")) server.get('/feed', (req, res) => res.sendFile(__dirname + "/html/feed.html")); server.get('/essay', (req, res) => res.sendFile(__dirname + "/html/essay.html")); - server.get('/misc', (req, res) => res.sendFile(__dirname + "/html/misc.html")); + // server.get('/misc', (req, res) => res.sendFile(__dirname + "/html/misc.html")); server.get('/word-square', (req, res) => res.sendFile(__dirname + "/html/word-square.html")); server.get('/chess', (req, res) => res.sendFile(__dirname + "/html/chess.html")); server.get('/admin/chess', async (req, res, next) => res.sendFile(__dirname + "/html/chess.html")); - server.get('/projects', (req, res) => res.sendFile(__dirname + "/html/projects.html")); + // server.get('/projects', (req, res) => res.sendFile(__dirname + "/html/projects.html")); server.get('/zines', (req, res) => res.sendFile(__dirname + "/public/zines.html")); server.use('/static', express.static(__dirname + '/public')) + server.get('/misc', async (req, res) => { + let body = templates["misc"](); + res.status(200).send(body) + }) + server.get('/projects', async (req, res) => { + let body = templates["projects"](); + res.status(200).send(body) + }) + server.get('/wordsquares/best', async (req, res, next) => { var best = await database.query("select words, name from wordsquares where best = 1", { type: database.QueryTypes.SELECT }) res.status(200).send({ best: best }); diff --git a/src/templates.js b/src/templates.js index b748853..293f0e4 100644 --- a/src/templates.js +++ b/src/templates.js @@ -1,86 +1,33 @@ -module.exports = { - titlebar: `<nav class="titlebar"> - <div> - <a href="bread" class="btn btn-primary">Bread</a> - <a href="blog" class="btn btn-primary">Blog</a> - <!-- <a href="essay" class="btn btn-primary">Essays</a> (Hello inspector, this page exists, but just isn't very interesting, so I'm removing the link) --> - <a href="https://games.marks.kitchen" class="btn btn-primary">Games</a> - <a href="email" class="btn btn-primary">Email</a> - <a href="misc" class="btn btn-primary">Misc</a> -</div> -</nav>`, - footer: `<footer> - <div>Mark Powers (<span class="email">mark</span>) © 2020 </div> - <div> - <a href="/feed.xml">RSS feed</a> - <span class="spacer"></span> - <a href="https://github.com/Mark-Powers">GitHub</a> - <span class="spacer"></span> - <a href="https://git.marks.kitchen/mark">Gitea</a> - <span class="spacer"></span> - <a href="https://fosstodon.org/@markp">Mastodon</a> - <span class="spacer"></span> - <br> - </div> -</footer>`, +const fs = require('fs'); +const path = require('path'); +const handlebars = require("handlebars"); + +function loadTemplate(templates, name, filepath){ + const templateContent = fs.readFileSync(filepath).toString() + templates[name] = handlebars.compile(templateContent); +} + +function loadPartial(name, filepath){ + handlebars.registerPartial(name, fs.readFileSync(filepath).toString()); +} + +function setUpTemplates(){ + loadPartial("navigation", path.join(__dirname, "templates/navigation.html")) + loadPartial("footer", path.join(__dirname, "templates/footer.html")) + loadPartial("feed", path.join(__dirname, "templates/feed.html")) - index: { - pre: `<!doctype html> - <html lang="en"> - - <head> - <title>Mark's Kitchen</title> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - <link rel="stylesheet" type="text/css" href="/css/styles.css"> - <link rel="shortcut icon" href="/favicon.ico"> - <link rel="alternate" type="application/rss+xml" title="RSS Feed for marks.kitchen" href="/feed.xml" /> - </head> - - <body> - <h1>Welcome to Mark's Kitchen</h1>`, - post: `</body> - </html>` - }, - bread: { - pre: `<!doctype html> - <html lang="en"> - - <head> - <title>Mark's Kitchen - Bread</title> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - <link rel="stylesheet" type="text/css" href="/css/styles.css"> - <link rel="shortcut icon" href="/favicon.ico"> - </head> - - <body> - <h1> - <a class="navigation" href="/" title="marks.kitchen"><</a> - Bread - </h1> - Some highlights (and lowlights) of breadmaking`, - post: `</body> - </html>` - }, - blog: { - pre: `<!doctype html> - <html lang="en"> - - <head> - <title>Mark's Kitchen - Blog</title> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - <link rel="stylesheet" type="text/css" href="/css/styles.css"> - <link rel="shortcut icon" href="/favicon.ico"> - </head> - - <body> - <h1> - <a class="navigation" href="/" title="marks.kitchen"><</a> - Blog - </h1>`, - post: `</body> - </html>` - } + let templates = {}; + loadTemplate(templates, "index", path.join(__dirname, 'templates/index.html')) + loadTemplate(templates, "bread", path.join(__dirname, 'templates/bread.html')) + loadTemplate(templates, "blog", path.join(__dirname, 'templates/blog.html')) + loadTemplate(templates, "blog-single", path.join(__dirname, 'templates/blog-single.html')) + loadTemplate(templates, "tags", path.join(__dirname, 'templates/tags.html')) + loadTemplate(templates, "misc", path.join(__dirname, 'templates/misc.html')) + loadTemplate(templates, "projects", path.join(__dirname, 'templates/projects.html')) + return templates +} + + +module.exports = { + setUpTemplates }
\ No newline at end of file diff --git a/src/templates/blog-single.html b/src/templates/blog-single.html new file mode 100644 index 0000000..8622007 --- /dev/null +++ b/src/templates/blog-single.html @@ -0,0 +1,23 @@ +<!doctype html> +<html lang="en"> + +<head> + <title>Mark's Kitchen - Blog</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <link rel="stylesheet" type="text/css" href="/css/styles.css"> + <link rel="shortcut icon" href="/favicon.ico"> +</head> + +<body> + <h1> + <a class="navigation" href="/" title="marks.kitchen"><</a> + <a class="navigation" href="/blog" title="blog"><</a> + Post from {{date}} + </h1> + {{> navigation}} + {{> feed}} + {{> footer}} +</body> + +</html>
\ No newline at end of file diff --git a/src/templates/blog.html b/src/templates/blog.html new file mode 100644 index 0000000..40b86f5 --- /dev/null +++ b/src/templates/blog.html @@ -0,0 +1,22 @@ +<!doctype html> +<html lang="en"> + +<head> + <title>Mark's Kitchen - Blog</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <link rel="stylesheet" type="text/css" href="/css/styles.css"> + <link rel="shortcut icon" href="/favicon.ico"> +</head> + +<body> + <h1> + <a class="navigation" href="/" title="marks.kitchen"><</a> + Blog + </h1> + {{> navigation}} + {{> feed}} + {{> footer}} +</body> + +</html>
\ No newline at end of file diff --git a/src/templates/bread.html b/src/templates/bread.html new file mode 100644 index 0000000..b5d02ed --- /dev/null +++ b/src/templates/bread.html @@ -0,0 +1,23 @@ +<!doctype html> +<html lang="en"> + +<head> + <title>Mark's Kitchen - Bread</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <link rel="stylesheet" type="text/css" href="/css/styles.css"> + <link rel="shortcut icon" href="/favicon.ico"> +</head> + +<body> + <h1> + <a class="navigation" href="/" title="marks.kitchen"><</a> + Bread + </h1> + <p>Some highlights (and lowlights) of breadmaking</p> + {{> navigation}} + {{> feed}} + {{> footer}} +</body> + +</html>
\ No newline at end of file diff --git a/src/templates/feed.html b/src/templates/feed.html new file mode 100644 index 0000000..2158f3e --- /dev/null +++ b/src/templates/feed.html @@ -0,0 +1,24 @@ +<div class="feed"> + {{#each posts}} + <div class="card"> + <p class="card-text">{{{this.description}}}</p> + <div class="card-img"> + {{#each this.images}} + <span> + <a href="/{{this}}"><img src="/{{this}}"></a> + </span> + {{/each}} + </div> + <p class="date"> + <a href="/post/{{this.type}}/{{this.id}}"> + {{this.createdAt}} + </a> + <span> + {{#each this.tags}} + <a class="tag" href="/tags/{{this}}">{{this}}</a> + {{/each}} + </span> + </p> + </div> + {{/each}} +</div>
\ No newline at end of file diff --git a/src/templates/footer.html b/src/templates/footer.html new file mode 100644 index 0000000..617b5fa --- /dev/null +++ b/src/templates/footer.html @@ -0,0 +1,14 @@ +<footer> + <div>Mark Powers (<span class="email">mark</span>) © 2020 </div> + <div> + <a href="/feed.xml">RSS feed</a> + <span class="spacer"></span> + <a href="https://github.com/Mark-Powers">GitHub</a> + <span class="spacer"></span> + <a href="https://git.marks.kitchen">Git Repos</a> + <span class="spacer"></span> + <a href="https://fosstodon.org/@markp">Mastodon</a> + <span class="spacer"></span> + <br> + </div> +</footer>
\ No newline at end of file diff --git a/src/templates/index.html b/src/templates/index.html new file mode 100644 index 0000000..efb7369 --- /dev/null +++ b/src/templates/index.html @@ -0,0 +1,20 @@ +<!doctype html> +<html lang="en"> + +<head> + <title>Mark's Kitchen</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <link rel="stylesheet" type="text/css" href="/css/styles.css"> + <link rel="shortcut icon" href="/favicon.ico"> + <link rel="alternate" type="application/rss+xml" title="RSS Feed for marks.kitchen" href="/feed.xml" /> +</head> + +<body> + <h1>Welcome to Mark's Kitchen</h1> + {{> navigation}} + {{> feed}} + {{> footer}} +</body> + +</html>
\ No newline at end of file diff --git a/src/html/misc.html b/src/templates/misc.html index 6ab0a15..9fb376a 100644 --- a/src/html/misc.html +++ b/src/templates/misc.html @@ -15,6 +15,7 @@ <a class="navigation" href="/" title="marks.kitchen"><</a> Miscellany </h1> + {{> navigation}} <div> <a href="projects" class="btn btn-primary">Projects</a> </div> diff --git a/src/templates/navigation.html b/src/templates/navigation.html new file mode 100644 index 0000000..6bb10f1 --- /dev/null +++ b/src/templates/navigation.html @@ -0,0 +1,10 @@ +<nav class="titlebar"> + <div> + <a href="/" class="btn btn-primary">Home</a> + <a href="/bread" class="btn btn-primary">Bread</a> + <a href="/blog" class="btn btn-primary">Blog</a> + <a href="https://games.marks.kitchen" class="btn btn-primary">Games</a> + <a href="/email" class="btn btn-primary">Email</a> + <a href="/misc" class="btn btn-primary">Misc</a> + </div> +</nav>
\ No newline at end of file diff --git a/src/html/projects.html b/src/templates/projects.html index fef817f..cf3361a 100644 --- a/src/html/projects.html +++ b/src/templates/projects.html @@ -15,6 +15,7 @@ <a class="navigation" href="/misc" title="marks.kitchen/misc"><</a> Projects </h1> + {{> navigation}} <h2>A collection of my projects, mostly programming</h2> <div id="feed" class="feed projects"> <div class="card"> diff --git a/src/templates/tags.html b/src/templates/tags.html new file mode 100644 index 0000000..31858e8 --- /dev/null +++ b/src/templates/tags.html @@ -0,0 +1,22 @@ +<!doctype html> +<html lang="en"> + +<head> + <title>Mark's Kitchen - Tags</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <link rel="stylesheet" type="text/css" href="/css/styles.css"> + <link rel="shortcut icon" href="/favicon.ico"> +</head> + +<body> + <h1> + <a class="navigation" href="/" title="marks.kitchen"><</a> + Tag {{name}} + </h1> + {{> navigation}} + {{> feed}} + {{> footer}} +</body> + +</html>
\ No newline at end of file |