parent
6230c2703e
commit
283bc78b4f
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,3 +7,4 @@ assets/*.css
|
||||||
/mastodon
|
/mastodon
|
||||||
mastodon.log
|
mastodon.log
|
||||||
assets/robots.txt
|
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 }
|
export { themes }
|
||||||
```
|
```
|
||||||
|
|
||||||
Add your theme in `templates/2xx.html`.
|
Add your theme in `inline-script.js`.
|
||||||
```js
|
```js
|
||||||
window.__themeColors = {
|
window.__themeColors = {
|
||||||
'default': "royalblue",
|
'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": {
|
"caniuse-api": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
|
||||||
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
|
"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": {
|
"content-type": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
"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": {
|
"date-now": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
|
||||||
|
@ -4863,6 +4878,18 @@
|
||||||
"sntp": "1.0.9"
|
"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": {
|
"highlight-es": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/highlight-es/-/highlight-es-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/highlight-es/-/highlight-es-1.0.1.tgz",
|
||||||
|
@ -5783,6 +5810,11 @@
|
||||||
"lodash.keys": "3.1.2"
|
"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": {
|
"lodash.uniq": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
"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": {
|
"pluralize": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz",
|
||||||
|
|
|
@ -4,14 +4,15 @@
|
||||||
"version": "0.1.6",
|
"version": "0.1.6",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "standard",
|
"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-dev": "run-p --race build-sass-watch serve",
|
||||||
"serve": "node server.js",
|
"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",
|
"sapper-build": "cross-env NODE_ENV=production sapper build",
|
||||||
"start": "cross-env NODE_ENV=production node server.js",
|
"start": "cross-env NODE_ENV=production node server.js",
|
||||||
"build-and-start": "run-s build start",
|
"build-and-start": "run-s build start",
|
||||||
"build-svg": "node ./bin/build-svg.js",
|
"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": "node ./bin/build-sass.js",
|
||||||
"build-sass-watch": "node ./bin/build-sass.js --watch",
|
"build-sass-watch": "node ./bin/build-sass.js --watch",
|
||||||
"run-mastodon": "node -r esm ./bin/run-mastodon",
|
"run-mastodon": "node -r esm ./bin/run-mastodon",
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
"font-awesome-svg-png": "^1.2.2",
|
"font-awesome-svg-png": "^1.2.2",
|
||||||
"form-data": "^2.3.2",
|
"form-data": "^2.3.2",
|
||||||
"glob": "^7.1.2",
|
"glob": "^7.1.2",
|
||||||
|
"helmet-csp": "^2.7.0",
|
||||||
"indexeddb-getall-shim": "^1.3.1",
|
"indexeddb-getall-shim": "^1.3.1",
|
||||||
"intersection-observer": "^0.5.0",
|
"intersection-observer": "^0.5.0",
|
||||||
"lodash": "^4.17.5",
|
"lodash": "^4.17.5",
|
||||||
|
@ -140,6 +142,7 @@
|
||||||
"package.json",
|
"package.json",
|
||||||
"package-lock.json",
|
"package-lock.json",
|
||||||
"server.js",
|
"server.js",
|
||||||
|
"inline-script.js",
|
||||||
"webpack.client.config.js",
|
"webpack.client.config.js",
|
||||||
"webpack.server.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 sapper = require('sapper')
|
||||||
const serveStatic = require('serve-static')
|
const serveStatic = require('serve-static')
|
||||||
const app = express()
|
const app = express()
|
||||||
|
const csp = require('helmet-csp')
|
||||||
|
|
||||||
|
const headScriptChecksum = require('./inline-script-checksum').checksum
|
||||||
|
|
||||||
const { PORT = 4002 } = process.env
|
const { PORT = 4002 } = process.env
|
||||||
|
|
||||||
|
@ -15,6 +18,17 @@ global.fetch = (url, opts) => {
|
||||||
|
|
||||||
app.use(compression({ threshold: 0 }))
|
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', {
|
app.use(serveStatic('assets', {
|
||||||
setHeaders: (res) => {
|
setHeaders: (res) => {
|
||||||
res.setHeader('Cache-Control', 'public,max-age=600')
|
res.setHeader('Cache-Control', 'public,max-age=600')
|
||||||
|
|
|
@ -40,22 +40,24 @@ body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-o
|
||||||
%sapper.head%
|
%sapper.head%
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script>
|
<!-- auto-generated w/ build-inline-script.js -->
|
||||||
<!-- load theme on startup (handled outside of Sapper/Svelte) -->
|
<!-- insert inline script here --><script>(function () {'use strict'; // For perf reasons, this script is run inline to quickly set certain styles.
|
||||||
window.__themeColors = {
|
// To allow CSP to work correctly, we also calculate a sha256 hash during
|
||||||
'default': "royalblue",
|
// the build process and write it to inline-script-checksum.json.
|
||||||
scarlet: "#e04e41",
|
window.__themeColors = {
|
||||||
seafoam: "#177380",
|
'default': 'royalblue',
|
||||||
hotpants: "hotpink",
|
scarlet: '#e04e41',
|
||||||
oaken: "saddlebrown",
|
seafoam: '#177380',
|
||||||
majesty: "blueviolet",
|
hotpants: 'hotpink',
|
||||||
gecko: "#4ab92f",
|
oaken: 'saddlebrown',
|
||||||
ozark: "#5263af",
|
majesty: 'blueviolet',
|
||||||
cobalt: "#08439b",
|
gecko: '#4ab92f',
|
||||||
sorcery: "#ae91e8",
|
ozark: '#5263af',
|
||||||
offline: "#999999"
|
cobalt: '#08439b',
|
||||||
}
|
sorcery: '#ae91e8',
|
||||||
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
|
offline: '#999999'
|
||||||
|
}
|
||||||
|
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
|
||||||
let safeParse = (str) => str === 'undefined' ? undefined : JSON.parse(str)
|
let safeParse = (str) => str === 'undefined' ? undefined : JSON.parse(str)
|
||||||
let theme = safeParse(localStorage.store_instanceThemes)[safeParse(localStorage.store_currentInstance)]
|
let theme = safeParse(localStorage.store_instanceThemes)[safeParse(localStorage.store_currentInstance)]
|
||||||
if (theme !== 'default') {
|
if (theme !== 'default') {
|
||||||
|
@ -68,14 +70,14 @@ body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-o
|
||||||
document.getElementById('theThemeColor').content = window.__themeColors[theme]
|
document.getElementById('theThemeColor').content = window.__themeColors[theme]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!localStorage.store_currentInstance) {
|
if (!localStorage.store_currentInstance) {
|
||||||
// if not logged in, show all these "hidden-from-ssr" elements
|
// if not logged in, show all these 'hidden-from-ssr' elements
|
||||||
let style = document.createElement('style')
|
let style = document.createElement('style')
|
||||||
style.textContent = '.hidden-from-ssr { opacity: 1 !important; }'
|
style.textContent = '.hidden-from-ssr { opacity: 1 !important; }'
|
||||||
document.head.appendChild(style)
|
document.head.appendChild(style)
|
||||||
}
|
}
|
||||||
</script>
|
})()</script><!-- end insert inline script here -->
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none;">
|
<svg xmlns="http://www.w3.org/2000/svg" style="display:none;">
|
||||||
<!-- auto-generated w/ build-svg.js -->
|
<!-- auto-generated w/ build-svg.js -->
|
||||||
<!-- insert svg here --><svg xmlns="http://www.w3.org/2000/svg" style="display:none;">
|
<!-- insert svg here --><svg xmlns="http://www.w3.org/2000/svg" style="display:none;">
|
||||||
|
|
Loading…
Reference in a new issue