decouple files from "." and allow data and view to live anywhere
This commit is contained in:
parent
9ac852c01e
commit
79fd165948
5
Makefile
5
Makefile
|
@ -1,7 +1,10 @@
|
||||||
|
|
||||||
all: honk
|
all: honk
|
||||||
|
|
||||||
honk: *.go go.mod
|
schema.go: schema.sql
|
||||||
|
sh ./genschemago.sh
|
||||||
|
|
||||||
|
honk: schema.go *.go go.mod
|
||||||
go build -mod=`ls -d vendor 2> /dev/null` -o honk
|
go build -mod=`ls -d vendor 2> /dev/null` -o honk
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -2,6 +2,8 @@ changelog
|
||||||
|
|
||||||
-- next
|
-- next
|
||||||
|
|
||||||
|
++ Make it easier to upgrade by decoupling data dir from ".".
|
||||||
|
|
||||||
+ Amend changelog for 0.8.0 in include omitted elements:
|
+ Amend changelog for 0.8.0 in include omitted elements:
|
||||||
Syntax highlighting for code blocks.
|
Syntax highlighting for code blocks.
|
||||||
Something resembling an actual manual.
|
Something resembling an actual manual.
|
||||||
|
|
48
docs/honk.8
48
docs/honk.8
|
@ -49,6 +49,20 @@ to be installed.
|
||||||
Run make.
|
Run make.
|
||||||
Please be patient.
|
Please be patient.
|
||||||
Even on fast machines, building from source can take several seconds.
|
Even on fast machines, building from source can take several seconds.
|
||||||
|
.Ss OPTIONS
|
||||||
|
The following options control where
|
||||||
|
.Nm
|
||||||
|
looks for data.
|
||||||
|
.Bl -tag -width datadirx
|
||||||
|
.It Fl datadir
|
||||||
|
The root data directory, where the database and other user data are stored.
|
||||||
|
Requires write access.
|
||||||
|
Defaults to ".".
|
||||||
|
.It Fl viewdir
|
||||||
|
The root view directory, where html and other templates are stored.
|
||||||
|
Read only.
|
||||||
|
Defaults to ".".
|
||||||
|
.El
|
||||||
.Ss INIT
|
.Ss INIT
|
||||||
Run the
|
Run the
|
||||||
.Ic init
|
.Ic init
|
||||||
|
@ -69,17 +83,17 @@ Log messages are sent to stderr and should probably be redirected to a file.
|
||||||
.Ss CUSTOMIZATION
|
.Ss CUSTOMIZATION
|
||||||
Add custom memes (stickers) to the
|
Add custom memes (stickers) to the
|
||||||
.Pa memes
|
.Pa memes
|
||||||
directory.
|
data directory.
|
||||||
Image and video files are supported.
|
Image and video files are supported.
|
||||||
.Pp
|
.Pp
|
||||||
Add custom emus (emoji) to the
|
Add custom emus (emoji) to the
|
||||||
.Pa emus
|
.Pa emus
|
||||||
directory.
|
data directory.
|
||||||
Image files are supported.
|
Image files are supported.
|
||||||
.Pp
|
.Pp
|
||||||
Site CSS may be overridden by creating a
|
Site CSS may be overridden by creating a
|
||||||
.Pa views/local.css
|
.Pa views/local.css
|
||||||
file.
|
file in the data directory.
|
||||||
A restart is required after changes.
|
A restart is required after changes.
|
||||||
.Pp
|
.Pp
|
||||||
Custom HTML messages may be added to select pages by using the
|
Custom HTML messages may be added to select pages by using the
|
||||||
|
@ -129,16 +143,40 @@ Restart.
|
||||||
There's also a
|
There's also a
|
||||||
.Pa blob.db
|
.Pa blob.db
|
||||||
file which is important to backup and restore.
|
file which is important to backup and restore.
|
||||||
.Sh SECURITY
|
.Ss SECURITY
|
||||||
.Nm
|
.Nm
|
||||||
is not currently hardened against SSRF, server side request forgery.
|
is not currently hardened against SSRF, server side request forgery.
|
||||||
Be mindful of what other services may be exposed via localhost or the
|
Be mindful of what other services may be exposed via localhost or the
|
||||||
local network.
|
local network.
|
||||||
.Sh DEBUG
|
.Ss DEBUG
|
||||||
Debug mode may be enabled or disabled by running
|
Debug mode may be enabled or disabled by running
|
||||||
.Ic debug Ar on|off .
|
.Ic debug Ar on|off .
|
||||||
In debug mode, secure cookies are disabled and templates are reloaded
|
In debug mode, secure cookies are disabled and templates are reloaded
|
||||||
every request.
|
every request.
|
||||||
|
.Sh FILES
|
||||||
|
.Nm
|
||||||
|
files are split between the data directory and the view directory.
|
||||||
|
Both default to "." but may be specified by command line options.
|
||||||
|
.Pp
|
||||||
|
The data directory contains:
|
||||||
|
.Bl -tag -width views/local.css
|
||||||
|
.It Pa honk.db
|
||||||
|
The main database.
|
||||||
|
.It Pa blob.db
|
||||||
|
Media and attachment storage.
|
||||||
|
.It Pa emus
|
||||||
|
Custom emoji.
|
||||||
|
.It Pa memes
|
||||||
|
Stickers and such.
|
||||||
|
.It Pa views/local.css
|
||||||
|
Locally customized CSS.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The view directory contains:
|
||||||
|
.Bl -tag -width views
|
||||||
|
.It Pa views
|
||||||
|
HTML templates and CSS files.
|
||||||
|
.El
|
||||||
.Sh ENVIRONMENT
|
.Sh ENVIRONMENT
|
||||||
Image processing and scaling requires considerable memory.
|
Image processing and scaling requires considerable memory.
|
||||||
It is recommended to adjust the datasize ulimit to at least 1GB.
|
It is recommended to adjust the datasize ulimit to at least 1GB.
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
echo "package main" > schema.go
|
||||||
|
echo "var sqlSchema = \`" >> schema.go
|
||||||
|
cat schema.sql >> schema.go
|
||||||
|
echo "\`" >> schema.go
|
||||||
|
go fmt schema.go
|
||||||
|
|
26
honk.go
26
honk.go
|
@ -17,11 +17,11 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
notrand "math/rand"
|
notrand "math/rand"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -184,6 +184,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var serverName string
|
var serverName string
|
||||||
|
var dataDir = "."
|
||||||
|
var viewDir = "."
|
||||||
var iconName = "icon.png"
|
var iconName = "icon.png"
|
||||||
var serverMsg template.HTML
|
var serverMsg template.HTML
|
||||||
var aboutMsg template.HTML
|
var aboutMsg template.HTML
|
||||||
|
@ -193,9 +195,13 @@ func ElaborateUnitTests() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
|
||||||
|
flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
|
||||||
|
flag.Parse()
|
||||||
|
args := flag.Args()
|
||||||
cmd := "run"
|
cmd := "run"
|
||||||
if len(os.Args) > 1 {
|
if len(args) > 0 {
|
||||||
cmd = os.Args[1]
|
cmd = args[0]
|
||||||
}
|
}
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case "init":
|
case "init":
|
||||||
|
@ -220,10 +226,10 @@ func main() {
|
||||||
case "admin":
|
case "admin":
|
||||||
adminscreen()
|
adminscreen()
|
||||||
case "debug":
|
case "debug":
|
||||||
if len(os.Args) != 3 {
|
if len(args) != 2 {
|
||||||
log.Fatal("need an argument: debug (on|off)")
|
log.Fatal("need an argument: debug (on|off)")
|
||||||
}
|
}
|
||||||
switch os.Args[2] {
|
switch args[1] {
|
||||||
case "on":
|
case "on":
|
||||||
updateconfig("debug", 1)
|
updateconfig("debug", 1)
|
||||||
case "off":
|
case "off":
|
||||||
|
@ -237,17 +243,17 @@ func main() {
|
||||||
chpass()
|
chpass()
|
||||||
case "cleanup":
|
case "cleanup":
|
||||||
arg := "30"
|
arg := "30"
|
||||||
if len(os.Args) > 2 {
|
if len(args) > 1 {
|
||||||
arg = os.Args[2]
|
arg = args[1]
|
||||||
}
|
}
|
||||||
cleanupdb(arg)
|
cleanupdb(arg)
|
||||||
case "ping":
|
case "ping":
|
||||||
if len(os.Args) < 4 {
|
if len(args) < 3 {
|
||||||
fmt.Printf("usage: honk ping from to\n")
|
fmt.Printf("usage: honk ping from to\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name := os.Args[2]
|
name := args[1]
|
||||||
targ := os.Args[3]
|
targ := args[2]
|
||||||
user, err := butwhatabout(name)
|
user, err := butwhatabout(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unknown user")
|
log.Printf("unknown user")
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
var sqlSchema = `
|
||||||
|
|
||||||
|
create table honks (honkid integer primary key, userid integer, what text, honker text, xid text, rid text, dt text, url text, audience text, noise text, convoy text, whofore integer, format text, precis text, oonker text, flags integer);
|
||||||
|
create table donks (honkid integer, fileid integer);
|
||||||
|
create table filemeta (fileid integer primary key, xid text, name text, description text, url text, media text, local integer);
|
||||||
|
create table honkers (honkerid integer primary key, userid integer, name text, xid text, flavor text, combos text, owner text);
|
||||||
|
create table xonkers (xonkerid integer primary key, name text, info text, flavor text);
|
||||||
|
create table zonkers (zonkerid integer primary key, userid integer, name text, wherefore text);
|
||||||
|
create table doovers(dooverid integer primary key, dt text, tries integer, userid integer, rcpt text, msg blob);
|
||||||
|
create table onts (ontology text, honkid integer);
|
||||||
|
create table honkmeta (honkid integer, genus text, json text);
|
||||||
|
create table hfcs (hfcsid integer primary key, userid integer, json text);
|
||||||
|
|
||||||
|
create index idx_honksxid on honks(xid);
|
||||||
|
create index idx_honksconvoy on honks(convoy);
|
||||||
|
create index idx_honkshonker on honks(honker);
|
||||||
|
create index idx_honksoonker on honks(oonker);
|
||||||
|
create index idx_honkerxid on honkers(xid);
|
||||||
|
create index idx_xonkername on xonkers(name);
|
||||||
|
create index idx_zonkersname on zonkers(name);
|
||||||
|
create index idx_filesxid on filemeta(xid);
|
||||||
|
create index idx_filesurl on filemeta(url);
|
||||||
|
create index idx_ontology on onts(ontology);
|
||||||
|
create index idx_onthonkid on onts(honkid);
|
||||||
|
create index idx_honkmetaid on honkmeta(honkid);
|
||||||
|
create index idx_hfcsuser on hfcs(userid);
|
||||||
|
|
||||||
|
create table config (key text, value text);
|
||||||
|
|
||||||
|
create table users (userid integer primary key, username text, hash text, displayname text, about text, pubkey text, seckey text, options text);
|
||||||
|
create table auth (authid integer primary key, userid integer, hash text, expiry text);
|
||||||
|
CREATE index idxusers_username on users(username);
|
||||||
|
CREATE index idxauth_userid on auth(userid);
|
||||||
|
CREATE index idxauth_hash on auth(hash);
|
||||||
|
|
||||||
|
`
|
|
@ -125,10 +125,7 @@ func upgradedb() {
|
||||||
case 21:
|
case 21:
|
||||||
// here we go...
|
// here we go...
|
||||||
initblobdb()
|
initblobdb()
|
||||||
blobdb, err := sql.Open("sqlite3", blobdbname)
|
blobdb := openblobdb()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
tx, err := blobdb.Begin()
|
tx, err := blobdb.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("can't begin: %s", err)
|
log.Fatalf("can't begin: %s", err)
|
||||||
|
|
20
util.go
20
util.go
|
@ -71,16 +71,11 @@ func getassetparam(file string) string {
|
||||||
var dbtimeformat = "2006-01-02 15:04:05"
|
var dbtimeformat = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
var alreadyopendb *sql.DB
|
var alreadyopendb *sql.DB
|
||||||
var dbname = "honk.db"
|
|
||||||
var blobdbname = "blob.db"
|
|
||||||
var stmtConfig *sql.Stmt
|
var stmtConfig *sql.Stmt
|
||||||
|
|
||||||
func initdb() {
|
func initdb() {
|
||||||
schema, err := ioutil.ReadFile("schema.sql")
|
dbname := dataDir + "/honk.db"
|
||||||
if err != nil {
|
_, err := os.Stat(dbname)
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = os.Stat(dbname)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Fatalf("%s already exists", dbname)
|
log.Fatalf("%s already exists", dbname)
|
||||||
}
|
}
|
||||||
|
@ -103,7 +98,7 @@ func initdb() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for _, line := range strings.Split(string(schema), ";") {
|
for _, line := range strings.Split(sqlSchema, ";") {
|
||||||
_, err = db.Exec(line)
|
_, err = db.Exec(line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
|
@ -168,6 +163,7 @@ func initdb() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initblobdb() {
|
func initblobdb() {
|
||||||
|
blobdbname := dataDir + "/blob.db"
|
||||||
_, err := os.Stat(blobdbname)
|
_, err := os.Stat(blobdbname)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Fatalf("%s already exists", blobdbname)
|
log.Fatalf("%s already exists", blobdbname)
|
||||||
|
@ -337,8 +333,8 @@ func opendatabase() *sql.DB {
|
||||||
if alreadyopendb != nil {
|
if alreadyopendb != nil {
|
||||||
return alreadyopendb
|
return alreadyopendb
|
||||||
}
|
}
|
||||||
var err error
|
dbname := dataDir + "/honk.db"
|
||||||
_, err = os.Stat(dbname)
|
_, err := os.Stat(dbname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to open database: %s", err)
|
log.Fatalf("unable to open database: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -355,8 +351,8 @@ func opendatabase() *sql.DB {
|
||||||
}
|
}
|
||||||
|
|
||||||
func openblobdb() *sql.DB {
|
func openblobdb() *sql.DB {
|
||||||
var err error
|
blobdbname := dataDir + "/blob.db"
|
||||||
_, err = os.Stat(blobdbname)
|
_, err := os.Stat(blobdbname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to open database: %s", err)
|
log.Fatalf("unable to open database: %s", err)
|
||||||
}
|
}
|
||||||
|
|
56
web.go
56
web.go
|
@ -61,9 +61,9 @@ func getuserstyle(u *login.UserInfo) template.CSS {
|
||||||
func getInfo(r *http.Request) map[string]interface{} {
|
func getInfo(r *http.Request) map[string]interface{} {
|
||||||
u := login.GetUserInfo(r)
|
u := login.GetUserInfo(r)
|
||||||
templinfo := make(map[string]interface{})
|
templinfo := make(map[string]interface{})
|
||||||
templinfo["StyleParam"] = getassetparam("views/style.css")
|
templinfo["StyleParam"] = getassetparam(viewDir + "/views/style.css")
|
||||||
templinfo["LocalStyleParam"] = getassetparam("views/local.css")
|
templinfo["LocalStyleParam"] = getassetparam(viewDir + "/views/local.css")
|
||||||
templinfo["JSParam"] = getassetparam("views/honkpage.js")
|
templinfo["JSParam"] = getassetparam(viewDir + "/views/honkpage.js")
|
||||||
templinfo["UserStyle"] = getuserstyle(u)
|
templinfo["UserStyle"] = getuserstyle(u)
|
||||||
templinfo["ServerName"] = serverName
|
templinfo["ServerName"] = serverName
|
||||||
templinfo["IconName"] = iconName
|
templinfo["IconName"] = iconName
|
||||||
|
@ -127,7 +127,7 @@ func homepage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func showfunzone(w http.ResponseWriter, r *http.Request) {
|
func showfunzone(w http.ResponseWriter, r *http.Request) {
|
||||||
var emunames, memenames []string
|
var emunames, memenames []string
|
||||||
dir, err := os.Open("emus")
|
dir, err := os.Open(dataDir + "/emus")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
emunames, _ = dir.Readdirnames(0)
|
emunames, _ = dir.Readdirnames(0)
|
||||||
dir.Close()
|
dir.Close()
|
||||||
|
@ -137,7 +137,7 @@ func showfunzone(w http.ResponseWriter, r *http.Request) {
|
||||||
emunames[i] = e[:len(e)-4]
|
emunames[i] = e[:len(e)-4]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dir, err = os.Open("memes")
|
dir, err = os.Open(dataDir + "/memes")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
memenames, _ = dir.Readdirnames(0)
|
memenames, _ = dir.Readdirnames(0)
|
||||||
dir.Close()
|
dir.Close()
|
||||||
|
@ -1784,12 +1784,16 @@ func avatate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func serveasset(w http.ResponseWriter, r *http.Request) {
|
func serveasset(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Cache-Control", "max-age=7776000")
|
w.Header().Set("Cache-Control", "max-age=7776000")
|
||||||
http.ServeFile(w, r, "views"+r.URL.Path)
|
dir := viewDir
|
||||||
|
if r.URL.Path == "/local.css" {
|
||||||
|
dir = dataDir
|
||||||
|
}
|
||||||
|
http.ServeFile(w, r, dir+"/views"+r.URL.Path)
|
||||||
}
|
}
|
||||||
func servehelp(w http.ResponseWriter, r *http.Request) {
|
func servehelp(w http.ResponseWriter, r *http.Request) {
|
||||||
name := mux.Vars(r)["name"]
|
name := mux.Vars(r)["name"]
|
||||||
w.Header().Set("Cache-Control", "max-age=3600")
|
w.Header().Set("Cache-Control", "max-age=3600")
|
||||||
http.ServeFile(w, r, "docs/"+name)
|
http.ServeFile(w, r, viewDir+"/docs/"+name)
|
||||||
}
|
}
|
||||||
func servehtml(w http.ResponseWriter, r *http.Request) {
|
func servehtml(w http.ResponseWriter, r *http.Request) {
|
||||||
u := login.GetUserInfo(r)
|
u := login.GetUserInfo(r)
|
||||||
|
@ -1807,12 +1811,12 @@ func servehtml(w http.ResponseWriter, r *http.Request) {
|
||||||
func serveemu(w http.ResponseWriter, r *http.Request) {
|
func serveemu(w http.ResponseWriter, r *http.Request) {
|
||||||
xid := mux.Vars(r)["xid"]
|
xid := mux.Vars(r)["xid"]
|
||||||
w.Header().Set("Cache-Control", "max-age="+somedays())
|
w.Header().Set("Cache-Control", "max-age="+somedays())
|
||||||
http.ServeFile(w, r, "emus/"+xid)
|
http.ServeFile(w, r, dataDir+"/emus/"+xid)
|
||||||
}
|
}
|
||||||
func servememe(w http.ResponseWriter, r *http.Request) {
|
func servememe(w http.ResponseWriter, r *http.Request) {
|
||||||
xid := mux.Vars(r)["xid"]
|
xid := mux.Vars(r)["xid"]
|
||||||
w.Header().Set("Cache-Control", "max-age="+somedays())
|
w.Header().Set("Cache-Control", "max-age="+somedays())
|
||||||
http.ServeFile(w, r, "memes/"+xid)
|
http.ServeFile(w, r, dataDir+"/memes/"+xid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func servefile(w http.ResponseWriter, r *http.Request) {
|
func servefile(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -1918,25 +1922,25 @@ func serve() {
|
||||||
debug := false
|
debug := false
|
||||||
getconfig("debug", &debug)
|
getconfig("debug", &debug)
|
||||||
readviews = templates.Load(debug,
|
readviews = templates.Load(debug,
|
||||||
"views/honkpage.html",
|
viewDir+"/views/honkpage.html",
|
||||||
"views/honkfrags.html",
|
viewDir+"/views/honkfrags.html",
|
||||||
"views/honkers.html",
|
viewDir+"/views/honkers.html",
|
||||||
"views/hfcs.html",
|
viewDir+"/views/hfcs.html",
|
||||||
"views/combos.html",
|
viewDir+"/views/combos.html",
|
||||||
"views/honkform.html",
|
viewDir+"/views/honkform.html",
|
||||||
"views/honk.html",
|
viewDir+"/views/honk.html",
|
||||||
"views/account.html",
|
viewDir+"/views/account.html",
|
||||||
"views/about.html",
|
viewDir+"/views/about.html",
|
||||||
"views/funzone.html",
|
viewDir+"/views/funzone.html",
|
||||||
"views/login.html",
|
viewDir+"/views/login.html",
|
||||||
"views/xzone.html",
|
viewDir+"/views/xzone.html",
|
||||||
"views/msg.html",
|
viewDir+"/views/msg.html",
|
||||||
"views/header.html",
|
viewDir+"/views/header.html",
|
||||||
"views/onts.html",
|
viewDir+"/views/onts.html",
|
||||||
"views/honkpage.js",
|
viewDir+"/views/honkpage.js",
|
||||||
)
|
)
|
||||||
if !debug {
|
if !debug {
|
||||||
assets := []string{"views/style.css", "views/local.css", "views/honkpage.js"}
|
assets := []string{viewDir + "/views/style.css", dataDir + "/views/local.css", viewDir + "/views/honkpage.js"}
|
||||||
for _, s := range assets {
|
for _, s := range assets {
|
||||||
savedassetparams[s] = getassetparam(s)
|
savedassetparams[s] = getassetparam(s)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue