mirror of https://github.com/jlelse/GoBlog
Add CSP and rework speak.js
This commit is contained in:
parent
99789efcd3
commit
58f41085dd
21
config.go
21
config.go
|
@ -23,16 +23,17 @@ type config struct {
|
|||
}
|
||||
|
||||
type configServer struct {
|
||||
Logging bool `mapstructure:"logging"`
|
||||
LogFile string `mapstructure:"logFile"`
|
||||
Debug bool `mapstructure:"Debug"`
|
||||
Port int `mapstructure:"port"`
|
||||
PublicAddress string `mapstructure:"publicAddress"`
|
||||
ShortPublicAddress string `mapstructure:"shortPublicAddress"`
|
||||
PublicHTTPS bool `mapstructure:"publicHttps"`
|
||||
SecurityHeaders bool `mapstructure:"securityHeaders"`
|
||||
LetsEncryptMail string `mapstructure:"letsEncryptMail"`
|
||||
JWTSecret string `mapstructure:"jwtSecret"`
|
||||
Logging bool `mapstructure:"logging"`
|
||||
LogFile string `mapstructure:"logFile"`
|
||||
Debug bool `mapstructure:"Debug"`
|
||||
Port int `mapstructure:"port"`
|
||||
PublicAddress string `mapstructure:"publicAddress"`
|
||||
ShortPublicAddress string `mapstructure:"shortPublicAddress"`
|
||||
PublicHTTPS bool `mapstructure:"publicHttps"`
|
||||
SecurityHeaders bool `mapstructure:"securityHeaders"`
|
||||
CSPDomains []string `mapstructure:"cspDomains"`
|
||||
LetsEncryptMail string `mapstructure:"letsEncryptMail"`
|
||||
JWTSecret string `mapstructure:"jwtSecret"`
|
||||
publicHostname string
|
||||
shortPublicHostname string
|
||||
}
|
||||
|
|
13
http.go
13
http.go
|
@ -4,7 +4,9 @@ import (
|
|||
"compress/flate"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
|
@ -388,13 +390,22 @@ func buildHandler() (http.Handler, error) {
|
|||
}
|
||||
|
||||
func securityHeaders(next http.Handler) http.Handler {
|
||||
extraCSPDomains := ""
|
||||
if mp := appConfig.Micropub.MediaStorage; mp != nil && mp.MediaURL != "" {
|
||||
if u, err := url.Parse(mp.MediaURL); err == nil {
|
||||
extraCSPDomains += " " + u.Hostname()
|
||||
}
|
||||
}
|
||||
if len(appConfig.Server.CSPDomains) > 0 {
|
||||
extraCSPDomains += " " + strings.Join(appConfig.Server.CSPDomains, " ")
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Strict-Transport-Security", "max-age=31536000;")
|
||||
w.Header().Add("Referrer-Policy", "no-referrer")
|
||||
w.Header().Add("X-Content-Type-Options", "nosniff")
|
||||
w.Header().Add("X-Frame-Options", "SAMEORIGIN")
|
||||
w.Header().Add("X-Xss-Protection", "1; mode=block")
|
||||
// TODO: Add CSP
|
||||
w.Header().Add("Content-Security-Policy", "default-src 'self'"+extraCSPDomains)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,45 +1,37 @@
|
|||
"use strict";
|
||||
|
||||
function getVoice() {
|
||||
if (window.speechSynthesis) {
|
||||
return window.speechSynthesis.getVoices().filter(voice => voice.lang.startsWith(document.querySelector('html').lang))[0];
|
||||
}
|
||||
return false;
|
||||
let sb = document.getElementById('speakBtn')
|
||||
let s = window.speechSynthesis
|
||||
|
||||
function gv() {
|
||||
return s ? s.getVoices().filter(voice => voice.lang.startsWith(document.querySelector('html').lang))[0] : false
|
||||
}
|
||||
|
||||
function initSpeak() {
|
||||
if (window.speechSynthesis) {
|
||||
let speakBtn = document.querySelector('#speakBtn');
|
||||
speakBtn.style.display = '';
|
||||
speakBtn.onclick = function() { speak() };
|
||||
speakBtn.textContent = speakText;
|
||||
function is() {
|
||||
if (s) {
|
||||
sb.classList.remove('hide')
|
||||
sb.onclick = sp
|
||||
sb.textContent = sb.dataset.speak
|
||||
}
|
||||
}
|
||||
|
||||
function speak() {
|
||||
console.log("Start speaking")
|
||||
let speakBtn = document.querySelector('#speakBtn');
|
||||
speakBtn.onclick = function() { stopSpeak() };
|
||||
speakBtn.textContent = stopSpeakText;
|
||||
let textContent =
|
||||
((document.querySelector('article .p-name')) ? document.querySelector('article .p-name').innerText + "\n\n" : "")
|
||||
+ document.querySelector('article .e-content').innerText;
|
||||
let utterThis = new SpeechSynthesisUtterance(textContent);
|
||||
utterThis.voice = getVoice();
|
||||
utterThis.onerror = stopSpeak;
|
||||
utterThis.onend = stopSpeak;
|
||||
window.speechSynthesis.speak(utterThis);
|
||||
function sp() {
|
||||
sb.onclick = ssp
|
||||
sb.textContent = sb.dataset.stopspeak
|
||||
let ut = new SpeechSynthesisUtterance(
|
||||
((document.querySelector('article .p-name')) ? document.querySelector('article .p-name').innerText + "\n\n" : '') + document.querySelector('article .e-content').innerText
|
||||
)
|
||||
ut.voice = gv()
|
||||
ut.onerror = ssp
|
||||
ut.onend = ssp
|
||||
s.speak(ut)
|
||||
}
|
||||
|
||||
function stopSpeak() {
|
||||
console.log("Stop speaking")
|
||||
window.speechSynthesis.cancel();
|
||||
let speakBtn = document.querySelector('#speakBtn');
|
||||
speakBtn.onclick = function() { speak() };
|
||||
speakBtn.textContent = speakText;
|
||||
function ssp() {
|
||||
s.cancel()
|
||||
sb.onclick = sp
|
||||
sb.textContent = sb.dataset.speak
|
||||
}
|
||||
|
||||
window.onbeforeunload = function () {
|
||||
stopSpeak();
|
||||
}
|
||||
initSpeak();
|
||||
window.onbeforeunload = ssp
|
||||
is()
|
|
@ -1,8 +1,7 @@
|
|||
{{ define "postactions" }}
|
||||
<div class="p flex" id="post-actions">
|
||||
<a href="https://www.addtoany.com/share#url={{ absolute .Data.Path }}{{ with title .Data }}&title={{ . }}{{ end }}" target="_blank" rel="nofollow noopener noreferrer" class="button invert">{{ string .Blog.Lang "share" }}</a>
|
||||
<button id="speakBtn" class="invert" style="display: none;"></button>
|
||||
<script>const speakText = "{{ string .Blog.Lang "speak" }}";const stopSpeakText = "{{ string .Blog.Lang "stopspeak" }}";</script>
|
||||
<button id="speakBtn" class="button invert hide" data-speak="{{ string .Blog.Lang "speak" }}" data-stopspeak="{{ string .Blog.Lang "stopspeak" }}"></button>
|
||||
<script defer src="{{ asset "js/speak.js" }}"></script>
|
||||
</div>
|
||||
{{ end }}
|
Loading…
Reference in New Issue