parent
6230c2703e
commit
283bc78b4f
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,3 +7,4 @@ assets/*.css
|
|||
/mastodon
|
||||
mastodon.log
|
||||
assets/robots.txt
|
||||
/inline-script-checksum.json
|
||||
|
|
32
bin/build-inline-script.js
Normal file
32
bin/build-inline-script.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const crypto = require('crypto')
|
||||
const fs = require('fs')
|
||||
const pify = require('pify')
|
||||
const readFile = pify(fs.readFile.bind(fs))
|
||||
const writeFile = pify(fs.writeFile.bind(fs))
|
||||
const path = require('path')
|
||||
|
||||
async function main () {
|
||||
let headScriptFilepath = path.join(__dirname, '../inline-script.js')
|
||||
let headScript = await readFile(headScriptFilepath, 'utf8')
|
||||
headScript = `(function () {'use strict'; ${headScript}})()`
|
||||
|
||||
let checksum = crypto.createHash('sha256').update(headScript).digest('base64')
|
||||
|
||||
let checksumFilepath = path.join(__dirname, '../inline-script-checksum.json')
|
||||
await writeFile(checksumFilepath, JSON.stringify({checksum}), 'utf8')
|
||||
|
||||
let html2xxFilepath = path.join(__dirname, '../templates/2xx.html')
|
||||
let html2xxFile = await readFile(html2xxFilepath, 'utf8')
|
||||
html2xxFile = html2xxFile.replace(
|
||||
/<!-- insert inline script here -->[\s\S]+<!-- end insert inline script here -->/,
|
||||
'<!-- insert inline script here --><script>' + headScript + '</script><!-- end insert inline script here -->'
|
||||
)
|
||||
await writeFile(html2xxFilepath, html2xxFile, 'utf8')
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
|
@ -40,7 +40,7 @@ const themes = [
|
|||
export { themes }
|
||||
```
|
||||
|
||||
Add your theme in `templates/2xx.html`.
|
||||
Add your theme in `inline-script.js`.
|
||||
```js
|
||||
window.__themeColors = {
|
||||
'default': "royalblue",
|
||||
|
|
36
inline-script.js
Normal file
36
inline-script.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
// For perf reasons, this script is run inline to quickly set certain styles.
|
||||
// To allow CSP to work correctly, we also calculate a sha256 hash during
|
||||
// the build process and write it to inline-script-checksum.json.
|
||||
window.__themeColors = {
|
||||
'default': 'royalblue',
|
||||
scarlet: '#e04e41',
|
||||
seafoam: '#177380',
|
||||
hotpants: 'hotpink',
|
||||
oaken: 'saddlebrown',
|
||||
majesty: 'blueviolet',
|
||||
gecko: '#4ab92f',
|
||||
ozark: '#5263af',
|
||||
cobalt: '#08439b',
|
||||
sorcery: '#ae91e8',
|
||||
offline: '#999999'
|
||||
}
|
||||
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
|
||||
let safeParse = (str) => str === 'undefined' ? undefined : JSON.parse(str)
|
||||
let theme = safeParse(localStorage.store_instanceThemes)[safeParse(localStorage.store_currentInstance)]
|
||||
if (theme !== 'default') {
|
||||
document.body.classList.add(`theme-${theme}`)
|
||||
let link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
link.href = `/theme-${theme}.css`
|
||||
document.head.appendChild(link)
|
||||
if (window.__themeColors[theme]) {
|
||||
document.getElementById('theThemeColor').content = window.__themeColors[theme]
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!localStorage.store_currentInstance) {
|
||||
// if not logged in, show all these 'hidden-from-ssr' elements
|
||||
let style = document.createElement('style')
|
||||
style.textContent = '.hidden-from-ssr { opacity: 1 !important; }'
|
||||
document.head.appendChild(style)
|
||||
}
|
37
package-lock.json
generated
37
package-lock.json
generated
|
@ -1563,6 +1563,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"camelize": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
|
||||
"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
|
||||
},
|
||||
"caniuse-api": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz",
|
||||
|
@ -2046,6 +2051,11 @@
|
|||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
|
||||
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
|
||||
},
|
||||
"content-security-policy-builder": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.0.0.tgz",
|
||||
"integrity": "sha512-j+Nhmj1yfZAikJLImCvPJFE29x/UuBi+/MWqggGGc515JKaZrjuei2RhULJmy0MsstW3E3htl002bwmBNMKr7w=="
|
||||
},
|
||||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
|
@ -2398,6 +2408,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"dasherize": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz",
|
||||
"integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg="
|
||||
},
|
||||
"date-now": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
|
||||
|
@ -4863,6 +4878,18 @@
|
|||
"sntp": "1.0.9"
|
||||
}
|
||||
},
|
||||
"helmet-csp": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.7.0.tgz",
|
||||
"integrity": "sha512-IGIAkWnxjRbgMXFA2/kmDqSIrIaSfZ6vhMHlSHw7jm7Gm9nVVXqwJ2B1YEpYrJsLrqY+w2Bbimk7snux9+sZAw==",
|
||||
"requires": {
|
||||
"camelize": "1.0.0",
|
||||
"content-security-policy-builder": "2.0.0",
|
||||
"dasherize": "2.0.0",
|
||||
"lodash.reduce": "4.6.0",
|
||||
"platform": "1.3.5"
|
||||
}
|
||||
},
|
||||
"highlight-es": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight-es/-/highlight-es-1.0.1.tgz",
|
||||
|
@ -5783,6 +5810,11 @@
|
|||
"lodash.keys": "3.1.2"
|
||||
}
|
||||
},
|
||||
"lodash.reduce": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
|
||||
"integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
|
||||
},
|
||||
"lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||
|
@ -7038,6 +7070,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
|
||||
"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
|
||||
},
|
||||
"pluralize": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz",
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
"version": "0.1.6",
|
||||
"scripts": {
|
||||
"lint": "standard",
|
||||
"dev": "run-s build-svg serve-dev",
|
||||
"dev": "run-s build-svg build-inline-script serve-dev",
|
||||
"serve-dev": "run-p --race build-sass-watch serve",
|
||||
"serve": "node server.js",
|
||||
"build": "cross-env NODE_ENV=production run-s globalize-css build-sass build-svg sapper-build deglobalize-css",
|
||||
"build": "cross-env NODE_ENV=production run-s globalize-css build-sass build-svg build-inline-script sapper-build deglobalize-css",
|
||||
"sapper-build": "cross-env NODE_ENV=production sapper build",
|
||||
"start": "cross-env NODE_ENV=production node server.js",
|
||||
"build-and-start": "run-s build start",
|
||||
"build-svg": "node ./bin/build-svg.js",
|
||||
"build-inline-script": "node ./bin/build-inline-script.js",
|
||||
"build-sass": "node ./bin/build-sass.js",
|
||||
"build-sass-watch": "node ./bin/build-sass.js --watch",
|
||||
"run-mastodon": "node -r esm ./bin/run-mastodon",
|
||||
|
@ -48,6 +49,7 @@
|
|||
"font-awesome-svg-png": "^1.2.2",
|
||||
"form-data": "^2.3.2",
|
||||
"glob": "^7.1.2",
|
||||
"helmet-csp": "^2.7.0",
|
||||
"indexeddb-getall-shim": "^1.3.1",
|
||||
"intersection-observer": "^0.5.0",
|
||||
"lodash": "^4.17.5",
|
||||
|
@ -140,6 +142,7 @@
|
|||
"package.json",
|
||||
"package-lock.json",
|
||||
"server.js",
|
||||
"inline-script.js",
|
||||
"webpack.client.config.js",
|
||||
"webpack.server.config.js"
|
||||
]
|
||||
|
|
14
server.js
14
server.js
|
@ -3,6 +3,9 @@ const compression = require('compression')
|
|||
const sapper = require('sapper')
|
||||
const serveStatic = require('serve-static')
|
||||
const app = express()
|
||||
const csp = require('helmet-csp')
|
||||
|
||||
const headScriptChecksum = require('./inline-script-checksum').checksum
|
||||
|
||||
const { PORT = 4002 } = process.env
|
||||
|
||||
|
@ -15,6 +18,17 @@ global.fetch = (url, opts) => {
|
|||
|
||||
app.use(compression({ threshold: 0 }))
|
||||
|
||||
app.use(csp({
|
||||
directives: {
|
||||
scriptSrc: [`'self'`, `'sha256-${headScriptChecksum}'`],
|
||||
workerSrc: [`'self'`],
|
||||
styleSrc: [`'self'`, `'unsafe-inline'`],
|
||||
frameSrc: [`'none'`],
|
||||
objectSrc: [`'none'`],
|
||||
manifestSrc: [`'self'`]
|
||||
}
|
||||
}))
|
||||
|
||||
app.use(serveStatic('assets', {
|
||||
setHeaders: (res) => {
|
||||
res.setHeader('Cache-Control', 'public,max-age=600')
|
||||
|
|
|
@ -40,42 +40,44 @@ body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-o
|
|||
%sapper.head%
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
<!-- load theme on startup (handled outside of Sapper/Svelte) -->
|
||||
window.__themeColors = {
|
||||
'default': "royalblue",
|
||||
scarlet: "#e04e41",
|
||||
seafoam: "#177380",
|
||||
hotpants: "hotpink",
|
||||
oaken: "saddlebrown",
|
||||
majesty: "blueviolet",
|
||||
gecko: "#4ab92f",
|
||||
ozark: "#5263af",
|
||||
cobalt: "#08439b",
|
||||
sorcery: "#ae91e8",
|
||||
offline: "#999999"
|
||||
<!-- auto-generated w/ build-inline-script.js -->
|
||||
<!-- insert inline script here --><script>(function () {'use strict'; // For perf reasons, this script is run inline to quickly set certain styles.
|
||||
// To allow CSP to work correctly, we also calculate a sha256 hash during
|
||||
// the build process and write it to inline-script-checksum.json.
|
||||
window.__themeColors = {
|
||||
'default': 'royalblue',
|
||||
scarlet: '#e04e41',
|
||||
seafoam: '#177380',
|
||||
hotpants: 'hotpink',
|
||||
oaken: 'saddlebrown',
|
||||
majesty: 'blueviolet',
|
||||
gecko: '#4ab92f',
|
||||
ozark: '#5263af',
|
||||
cobalt: '#08439b',
|
||||
sorcery: '#ae91e8',
|
||||
offline: '#999999'
|
||||
}
|
||||
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
|
||||
let safeParse = (str) => str === 'undefined' ? undefined : JSON.parse(str)
|
||||
let theme = safeParse(localStorage.store_instanceThemes)[safeParse(localStorage.store_currentInstance)]
|
||||
if (theme !== 'default') {
|
||||
document.body.classList.add(`theme-${theme}`)
|
||||
let link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
link.href = `/theme-${theme}.css`
|
||||
document.head.appendChild(link)
|
||||
if (window.__themeColors[theme]) {
|
||||
document.getElementById('theThemeColor').content = window.__themeColors[theme]
|
||||
}
|
||||
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
|
||||
let safeParse = (str) => str === 'undefined' ? undefined : JSON.parse(str)
|
||||
let theme = safeParse(localStorage.store_instanceThemes)[safeParse(localStorage.store_currentInstance)]
|
||||
if (theme !== 'default') {
|
||||
document.body.classList.add(`theme-${theme}`)
|
||||
let link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
link.href = `/theme-${theme}.css`
|
||||
document.head.appendChild(link)
|
||||
if (window.__themeColors[theme]) {
|
||||
document.getElementById('theThemeColor').content = window.__themeColors[theme]
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!localStorage.store_currentInstance) {
|
||||
// if not logged in, show all these "hidden-from-ssr" elements
|
||||
let style = document.createElement('style')
|
||||
style.textContent = '.hidden-from-ssr { opacity: 1 !important; }'
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
</script>
|
||||
}
|
||||
}
|
||||
if (!localStorage.store_currentInstance) {
|
||||
// if not logged in, show all these 'hidden-from-ssr' elements
|
||||
let style = document.createElement('style')
|
||||
style.textContent = '.hidden-from-ssr { opacity: 1 !important; }'
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
})()</script><!-- end insert inline script here -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none;">
|
||||
<!-- auto-generated w/ build-svg.js -->
|
||||
<!-- insert svg here --><svg xmlns="http://www.w3.org/2000/svg" style="display:none;">
|
||||
|
|
Loading…
Reference in a new issue