More config settings and age gating
This commit is contained in:
parent
532135094c
commit
aca9734b14
1
blog.go
1
blog.go
|
@ -133,6 +133,7 @@ func (b *Blog) SetupHTTP() {
|
||||||
negroni.HandlerFunc(sessions.Middleware),
|
negroni.HandlerFunc(sessions.Middleware),
|
||||||
negroni.HandlerFunc(middleware.CSRF(responses.Forbidden)),
|
negroni.HandlerFunc(middleware.CSRF(responses.Forbidden)),
|
||||||
negroni.HandlerFunc(auth.Middleware),
|
negroni.HandlerFunc(auth.Middleware),
|
||||||
|
negroni.HandlerFunc(middleware.AgeGate(authctl.AgeGate)),
|
||||||
)
|
)
|
||||||
n.UseHandler(r)
|
n.UseHandler(r)
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,9 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/kirsle/blog/internal/forms"
|
"github.com/kirsle/blog/internal/forms"
|
||||||
"github.com/kirsle/blog/models/settings"
|
|
||||||
"github.com/kirsle/blog/internal/render"
|
"github.com/kirsle/blog/internal/render"
|
||||||
"github.com/kirsle/blog/internal/responses"
|
"github.com/kirsle/blog/internal/responses"
|
||||||
|
"github.com/kirsle/blog/models/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func settingsHandler(w http.ResponseWriter, r *http.Request) {
|
func settingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -21,11 +21,16 @@ func settingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
redisPort, _ := strconv.Atoi(r.FormValue("redis-port"))
|
redisPort, _ := strconv.Atoi(r.FormValue("redis-port"))
|
||||||
redisDB, _ := strconv.Atoi(r.FormValue("redis-db"))
|
redisDB, _ := strconv.Atoi(r.FormValue("redis-db"))
|
||||||
mailPort, _ := strconv.Atoi(r.FormValue("mail-port"))
|
mailPort, _ := strconv.Atoi(r.FormValue("mail-port"))
|
||||||
|
ppp, _ := strconv.Atoi(r.FormValue("posts-per-page"))
|
||||||
|
ppf, _ := strconv.Atoi(r.FormValue("posts-per-feed"))
|
||||||
form := &forms.Settings{
|
form := &forms.Settings{
|
||||||
Title: r.FormValue("title"),
|
Title: r.FormValue("title"),
|
||||||
Description: r.FormValue("description"),
|
Description: r.FormValue("description"),
|
||||||
AdminEmail: r.FormValue("admin-email"),
|
AdminEmail: r.FormValue("admin-email"),
|
||||||
URL: r.FormValue("url"),
|
URL: r.FormValue("url"),
|
||||||
|
NSFW: r.FormValue("nsfw") == "true",
|
||||||
|
PostsPerPage: ppp,
|
||||||
|
PostsPerFeed: ppf,
|
||||||
RedisEnabled: len(r.FormValue("redis-enabled")) > 0,
|
RedisEnabled: len(r.FormValue("redis-enabled")) > 0,
|
||||||
RedisHost: r.FormValue("redis-host"),
|
RedisHost: r.FormValue("redis-host"),
|
||||||
RedisPort: redisPort,
|
RedisPort: redisPort,
|
||||||
|
@ -45,6 +50,9 @@ func settingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
settings.Site.Description = form.Description
|
settings.Site.Description = form.Description
|
||||||
settings.Site.AdminEmail = form.AdminEmail
|
settings.Site.AdminEmail = form.AdminEmail
|
||||||
settings.Site.URL = form.URL
|
settings.Site.URL = form.URL
|
||||||
|
settings.Site.NSFW = form.NSFW
|
||||||
|
settings.Blog.PostsPerPage = form.PostsPerPage
|
||||||
|
settings.Blog.PostsPerFeed = form.PostsPerFeed
|
||||||
settings.Redis.Enabled = form.RedisEnabled
|
settings.Redis.Enabled = form.RedisEnabled
|
||||||
settings.Redis.Host = form.RedisHost
|
settings.Redis.Host = form.RedisHost
|
||||||
settings.Redis.Port = form.RedisPort
|
settings.Redis.Port = form.RedisPort
|
||||||
|
|
35
internal/controllers/authctl/age-gate.go
Normal file
35
internal/controllers/authctl/age-gate.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package authctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/kirsle/blog/internal/log"
|
||||||
|
"github.com/kirsle/blog/internal/render"
|
||||||
|
"github.com/kirsle/blog/internal/responses"
|
||||||
|
"github.com/kirsle/blog/internal/sessions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AgeGate handles age verification for NSFW blogs.
|
||||||
|
func AgeGate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
next := r.FormValue("next")
|
||||||
|
if next == "" {
|
||||||
|
next = "/"
|
||||||
|
}
|
||||||
|
v := map[string]interface{}{
|
||||||
|
"Next": next,
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == http.MethodPost {
|
||||||
|
confirm := r.FormValue("confirm")
|
||||||
|
log.Info("confirm: %s", confirm)
|
||||||
|
if r.FormValue("confirm") == "true" {
|
||||||
|
session := sessions.Get(r)
|
||||||
|
session.Values["age-ok"] = true
|
||||||
|
session.Save(r, w)
|
||||||
|
responses.Redirect(w, next)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Template(w, r, ".age-gate.gohtml", v)
|
||||||
|
}
|
|
@ -11,6 +11,9 @@ type Settings struct {
|
||||||
Description string
|
Description string
|
||||||
AdminEmail string
|
AdminEmail string
|
||||||
URL string
|
URL string
|
||||||
|
NSFW bool
|
||||||
|
PostsPerPage int
|
||||||
|
PostsPerFeed int
|
||||||
RedisEnabled bool
|
RedisEnabled bool
|
||||||
RedisHost string
|
RedisHost string
|
||||||
RedisPort int
|
RedisPort int
|
||||||
|
|
59
internal/middleware/age-gate.go
Normal file
59
internal/middleware/age-gate.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kirsle/blog/internal/responses"
|
||||||
|
"github.com/kirsle/blog/internal/sessions"
|
||||||
|
"github.com/kirsle/blog/models/settings"
|
||||||
|
"github.com/urfave/negroni"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ageGateSuffixes = []string{
|
||||||
|
".js",
|
||||||
|
".css",
|
||||||
|
".txt",
|
||||||
|
".ico",
|
||||||
|
".png",
|
||||||
|
".jpg",
|
||||||
|
".jpeg",
|
||||||
|
".gif",
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgeGate is a middleware generator that does age verification for NSFW sites.
|
||||||
|
func AgeGate(verifyHandler func(http.ResponseWriter, *http.Request)) negroni.HandlerFunc {
|
||||||
|
middleware := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
|
s, _ := settings.Load()
|
||||||
|
if !s.Site.NSFW {
|
||||||
|
next(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path := r.URL.Path
|
||||||
|
if strings.HasPrefix(path, "/age-verify") {
|
||||||
|
verifyHandler(w, r) // defer to the age gate handler itself.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow static files and things through.
|
||||||
|
for _, prefix := range ageGateSuffixes {
|
||||||
|
if strings.HasSuffix(path, prefix) {
|
||||||
|
next(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if they've been cleared.
|
||||||
|
session := sessions.Get(r)
|
||||||
|
if val, _ := session.Values["age-ok"].(bool); !val {
|
||||||
|
// They haven't been verified.
|
||||||
|
responses.Redirect(w, "/age-verify?next="+r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return middleware
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ type Settings struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
AdminEmail string `json:"adminEmail"`
|
AdminEmail string `json:"adminEmail"`
|
||||||
|
NSFW bool `json:"nsfw"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
} `json:"site"`
|
} `json:"site"`
|
||||||
|
|
||||||
|
@ -29,6 +30,12 @@ type Settings struct {
|
||||||
HashCost int `json:"hashCost"` // Bcrypt hash cost for passwords
|
HashCost int `json:"hashCost"` // Bcrypt hash cost for passwords
|
||||||
} `json:"security"`
|
} `json:"security"`
|
||||||
|
|
||||||
|
// Blog settings.
|
||||||
|
Blog struct {
|
||||||
|
PostsPerPage int `json:"postsPerPage"`
|
||||||
|
PostsPerFeed int `json:"postsPerFeed"`
|
||||||
|
} `json:"blog"`
|
||||||
|
|
||||||
// Redis settings for caching in JsonDB.
|
// Redis settings for caching in JsonDB.
|
||||||
Redis struct {
|
Redis struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
|
@ -58,6 +65,8 @@ func Defaults() *Settings {
|
||||||
s.Site.Title = "Untitled Site"
|
s.Site.Title = "Untitled Site"
|
||||||
s.Security.HashCost = 14
|
s.Security.HashCost = 14
|
||||||
s.Security.SecretKey = RandomKey()
|
s.Security.SecretKey = RandomKey()
|
||||||
|
s.Blog.PostsPerPage = 10
|
||||||
|
s.Blog.PostsPerFeed = 10
|
||||||
s.Redis.Host = "localhost"
|
s.Redis.Host = "localhost"
|
||||||
s.Redis.Port = 6379
|
s.Redis.Port = 6379
|
||||||
s.Redis.DB = 0
|
s.Redis.DB = 0
|
||||||
|
|
34
root/.age-gate.gohtml
Normal file
34
root/.age-gate.gohtml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{{ define "title" }}Age Verification{{ end }}
|
||||||
|
{{ define "content" }}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="/age-verify" method="POST">
|
||||||
|
<input type="hidden" name="_csrf" value="{{ .CSRF }}">
|
||||||
|
<input type="hidden" name="next" value="{{ .Data.Next }}">
|
||||||
|
<input type="hidden" name="confirm" value="true">
|
||||||
|
|
||||||
|
<h1>Restricted Content</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This website has been marked <abbr title="Not Safe For Work">NSFW</abbr>
|
||||||
|
by its owner. It may contain nudity or content not suited for users
|
||||||
|
under the age of 18.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
To proceed, you must verify you are at least 18 years or older.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button type="submit"
|
||||||
|
class="btn btn-danger">
|
||||||
|
I am 18 years or older
|
||||||
|
</button>
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
href="https://duckduckgo.com/">
|
||||||
|
Get me out of here!
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
|
@ -50,6 +50,38 @@
|
||||||
placeholder="https://www.example.com/">
|
placeholder="https://www.example.com/">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<strong>NSFW Website</strong>
|
||||||
|
<div class="form-check mb-4">
|
||||||
|
<label class="form-check-label">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="form-check-input"
|
||||||
|
name="nsfw"
|
||||||
|
value="true"
|
||||||
|
{{ if .Site.NSFW }}checked{{ end }}>
|
||||||
|
Website is NSFW. Requires an age verification to enter.
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Blog Settings</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="admin-email">Posts Per Page</label>
|
||||||
|
<input type="text"
|
||||||
|
class="form-control"
|
||||||
|
name="posts-per-page"
|
||||||
|
value="{{ .Blog.PostsPerPage }}"
|
||||||
|
placeholder="https://www.example.com/">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="admin-email">Posts Per (RSS) Feed</label>
|
||||||
|
<input type="text"
|
||||||
|
class="form-control"
|
||||||
|
name="posts-per-page"
|
||||||
|
value="{{ .Blog.PostsPerFeed }}"
|
||||||
|
placeholder="https://www.example.com/">
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3>Redis Cache</h3>
|
<h3>Redis Cache</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user