Adapt to more recent Go version
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
このコミットが含まれているのは:
コミット
e9e364611a
|
@ -0,0 +1,31 @@
|
||||||
|
GO ?= go
|
||||||
|
RM ?= rm
|
||||||
|
SCDOC ?= scdoc
|
||||||
|
GOFLAGS ?= -v -ldflags "-w -X `go list`.Version=$(VERSION) -X `go list`.Commit=$(COMMIT) -X `go list`.Build=$(BUILD)" -tags "static_build"
|
||||||
|
PREFIX ?= /usr/local
|
||||||
|
BINDIR ?= bin
|
||||||
|
MANDIR ?= share/man
|
||||||
|
MKDIR ?= mkdir
|
||||||
|
CP ?= cp
|
||||||
|
|
||||||
|
VERSION = `git describe --abbrev=0 --tags 2>/dev/null || echo "$VERSION"`
|
||||||
|
COMMIT = `git rev-parse --short HEAD || echo "$COMMIT"`
|
||||||
|
BRANCH = `git rev-parse --abbrev-ref HEAD`
|
||||||
|
BUILD = `git show -s --pretty=format:%cI`
|
||||||
|
|
||||||
|
GOARCH ?= amd64
|
||||||
|
GOOS ?= linux
|
||||||
|
|
||||||
|
all: akyuu
|
||||||
|
|
||||||
|
akyuu:
|
||||||
|
$(GO) build $(GOFLAGS)
|
||||||
|
clean:
|
||||||
|
$(RM) -f akyuu akyuuctl akyuu-znc-import doc/akyuu.1
|
||||||
|
install:
|
||||||
|
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||||
|
$(MKDIR) -p $(DESTDIR)/var/lib/akyuu
|
||||||
|
$(CP) -f akyuu $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||||
|
$(CP) -R templates $(DESTDIR)/var/lib/akyuu
|
||||||
|
$(MKDIR) -p $(DESTDIR)/var/lib/akyuu/data
|
||||||
|
.PHONY: akyuu clean install
|
27
README.md
27
README.md
|
@ -1,4 +1,4 @@
|
||||||
# htwtxt – hosted twtxt server
|
# akyuu – a twtxt server that never forgets
|
||||||
|
|
||||||
## Rationale
|
## Rationale
|
||||||
|
|
||||||
|
@ -24,11 +24,6 @@ space.
|
||||||
- all HTML+CSS is read from a templates directory, which can be freely chosen at
|
- all HTML+CSS is read from a templates directory, which can be freely chosen at
|
||||||
server start so as to ease customization of the interface
|
server start so as to ease customization of the interface
|
||||||
|
|
||||||
## Docker image
|
|
||||||
|
|
||||||
A docker image maintained by the htwtxt user buckket can be found here:
|
|
||||||
<https://hub.docker.com/r/buckket/htwtxt/>
|
|
||||||
|
|
||||||
## Setup and run
|
## Setup and run
|
||||||
|
|
||||||
### Setup Go build environment
|
### Setup Go build environment
|
||||||
|
@ -53,17 +48,15 @@ available.)
|
||||||
|
|
||||||
Once your Go build environment is ready, do this:
|
Once your Go build environment is ready, do this:
|
||||||
|
|
||||||
git clone https://github.com/plomlompom/htwtxt $GOPATH/src/htwtxt
|
go install marisa.chaotic.ninja/akyuu@latest
|
||||||
go get htwtxt
|
$GOPATH/bin/akyuu [options]
|
||||||
mkdir ~/htwtxt
|
|
||||||
$GOPATH/bin/htwtxt
|
|
||||||
|
|
||||||
This will build and start the server, which will store login and feed data below
|
This will build and start the server, which will store login and feed data below
|
||||||
`~/htwtxt`. An alternate directory may be specified with the `--dir` flag.
|
`~/akyuu`. An alternate directory may be specified with the `--dir` flag.
|
||||||
|
|
||||||
### Writing twtxt messages via API
|
### Writing twtxt messages via API
|
||||||
|
|
||||||
Using htwtxt from a web browser for purposes such as writing a twtxt message
|
Using akyuu from a web browser for purposes such as writing a twtxt message
|
||||||
should be self-explanatory (just use the HTML form on the start page). But it's
|
should be self-explanatory (just use the HTML form on the start page). But it's
|
||||||
also possible to write new messages directly to a twtxt feed via a `POST`
|
also possible to write new messages directly to a twtxt feed via a `POST`
|
||||||
request to `/feeds`. Just provide appropriate values for the data fields `name`
|
request to `/feeds`. Just provide appropriate values for the data fields `name`
|
||||||
|
@ -77,10 +70,10 @@ line example utilizing the curl tool:
|
||||||
|
|
||||||
### Configure port number and TLS
|
### Configure port number and TLS
|
||||||
|
|
||||||
By default, htwtxt serves unencrypted HTTP over port 8000. But the executable
|
By default, akyuu serves unencrypted HTTP over port 8000. But the executable
|
||||||
accepts the flag `--port` to provide an alternate port number, and the flags
|
accepts the flag `--port` to provide an alternate port number, and the flags
|
||||||
`--cert` and `--key` to provide paths to an SSL certificate and key file to run
|
`--cert` and `--key` to provide paths to an SSL certificate and key file to run
|
||||||
htwtxt as an HTTPS server.
|
akyuu as an HTTPS server.
|
||||||
|
|
||||||
You might encounter the following issue when trying to set a low port number
|
You might encounter the following issue when trying to set a low port number
|
||||||
(such as the HTTP standard 80, or the HTTPS standard 443):
|
(such as the HTTP standard 80, or the HTTPS standard 443):
|
||||||
|
@ -90,7 +83,7 @@ You might encounter the following issue when trying to set a low port number
|
||||||
This is [a common privilege problem](http://stackoverflow.com/q/413807) and
|
This is [a common privilege problem](http://stackoverflow.com/q/413807) and
|
||||||
[might be solved](http://stackoverflow.com/a/414258) bis this:
|
[might be solved](http://stackoverflow.com/a/414258) bis this:
|
||||||
|
|
||||||
sudo setcap 'cap_net_bind_service=+ep' $GOPATH/bin/htwtxt
|
sudo setcap 'cap_net_bind_service=+ep' $GOPATH/bin/akyuu
|
||||||
|
|
||||||
### Public or closed sign-up
|
### Public or closed sign-up
|
||||||
|
|
||||||
|
@ -118,7 +111,7 @@ posed on the password reset links they enable by setting their mail address.
|
||||||
|
|
||||||
### Change HTML templates
|
### Change HTML templates
|
||||||
|
|
||||||
By default, HTML templates are read out of `$GOPATH/src/htwtxt/templates/`. An
|
By default, HTML templates are read out of `$GOPATH/src/akyuu/templates/`. An
|
||||||
alternate directory can be given with the flag `--templates` (it should contain
|
alternate directory can be given with the flag `--templates` (it should contain
|
||||||
template files of the same names as the default ones, however).
|
template files of the same names as the default ones, however).
|
||||||
|
|
||||||
|
@ -127,6 +120,8 @@ template files of the same names as the default ones, however).
|
||||||
htwtxt (c) 2016 Christian Heller a.k.a. [plomlompom](http://www.plomlompom.de),
|
htwtxt (c) 2016 Christian Heller a.k.a. [plomlompom](http://www.plomlompom.de),
|
||||||
with template design input by [Kai Kubasta](http://kaikubasta.de).
|
with template design input by [Kai Kubasta](http://kaikubasta.de).
|
||||||
|
|
||||||
|
akyuu (c) 2023-present Izuru Yakumo.
|
||||||
|
|
||||||
License: Affero GPL version 3, see `./LICENSE`
|
License: Affero GPL version 3, see `./LICENSE`
|
||||||
|
|
||||||
Current version number: 1.0.7
|
Current version number: 1.0.7
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
module marisa.chaotic.ninja/akyuu
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gorilla/mux v1.8.0
|
||||||
|
golang.org/x/crypto v0.8.0
|
||||||
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/sys v0.7.0 // indirect
|
||||||
|
golang.org/x/term v0.7.0 // indirect
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
|
)
|
|
@ -0,0 +1,12 @@
|
||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||||
|
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||||
|
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||||
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||||
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||||
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||||
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
28
handlers.go
28
handlers.go
|
@ -1,17 +1,21 @@
|
||||||
|
// $TheSupernovaDuo: akyuu,v master 2023/4/14 21:2:6 yakumo_izuru Exp $
|
||||||
|
// See LICENSE for copyright details
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "bufio"
|
import (
|
||||||
import "crypto/rand"
|
"bufio"
|
||||||
import "encoding/base64"
|
"crypto/rand"
|
||||||
import "golang.org/x/crypto/bcrypt"
|
"encoding/base64"
|
||||||
import "gopkg.in/gomail.v2"
|
"golang.org/x/crypto/bcrypt"
|
||||||
import "log"
|
"gopkg.in/gomail.v2"
|
||||||
import "github.com/gorilla/mux"
|
"log"
|
||||||
import "net/http"
|
"github.com/gorilla/mux"
|
||||||
import "os"
|
"net/http"
|
||||||
import "strconv"
|
"os"
|
||||||
import "strings"
|
"strconv"
|
||||||
import "time"
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func passwordResetRequestGetHandler(w http.ResponseWriter, r *http.Request) {
|
func passwordResetRequestGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if "" == mailuser {
|
if "" == mailuser {
|
||||||
|
|
58
io.go
58
io.go
|
@ -1,29 +1,32 @@
|
||||||
// htwtxt – hosted twtxt server; see README for copyright and license info
|
// $TheSupernovaDuo: akyuu,v master 2023/4/14 21:2:6 yakumo_izuru Exp $
|
||||||
|
// See LICENSE for copyright details
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "bufio"
|
import (
|
||||||
import "errors"
|
"bufio"
|
||||||
import "log"
|
"errors"
|
||||||
import "os"
|
"log"
|
||||||
import "strings"
|
"os"
|
||||||
import "io/ioutil"
|
"strings"
|
||||||
|
)
|
||||||
const loginsFile = "logins.txt"
|
const (
|
||||||
const feedsDir = "feeds"
|
loginsFile = "logins.txt"
|
||||||
const ipDelaysFile = "ip_delays.txt"
|
feedsDir = "feeds"
|
||||||
const pwResetFile = "password_reset.txt"
|
ipDelaysFile = "ip_delays.txt"
|
||||||
const pwResetWaitFile = "password_reset_wait.txt"
|
pwResetFile = "password_reset.txt"
|
||||||
|
pwResetWaitFile = "password_reset_wait.txt"
|
||||||
var certPath string
|
)
|
||||||
var dataDir string
|
var (
|
||||||
var feedsPath string
|
certPath string
|
||||||
var ipDelaysPath string
|
dataDir string
|
||||||
var keyPath string
|
feedsPath string
|
||||||
var loginsPath string
|
ipDelaysPath string
|
||||||
var pwResetPath string
|
keyPath string
|
||||||
var pwResetWaitPath string
|
loginsPath string
|
||||||
var templPath string
|
pwResetPath string
|
||||||
|
pwResetWaitPath string
|
||||||
|
templPath string
|
||||||
|
)
|
||||||
|
|
||||||
func createFileIfNotExists(path string) {
|
func createFileIfNotExists(path string) {
|
||||||
if _, err := os.Stat(path); err != nil {
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
@ -45,7 +48,7 @@ func openFile(path string) *os.File {
|
||||||
}
|
}
|
||||||
|
|
||||||
func linesFromFile(path string) []string {
|
func linesFromFile(path string) []string {
|
||||||
text, err := ioutil.ReadFile(path)
|
text, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Can't read file", err)
|
log.Fatal("Can't read file", err)
|
||||||
}
|
}
|
||||||
|
@ -57,7 +60,7 @@ func linesFromFile(path string) []string {
|
||||||
|
|
||||||
func writeAtomic(path, text string) {
|
func writeAtomic(path, text string) {
|
||||||
tmpFile := path + "_tmp"
|
tmpFile := path + "_tmp"
|
||||||
if err := ioutil.WriteFile(tmpFile, []byte(text), 0600); err != nil {
|
if err := os.WriteFile(tmpFile, []byte(text), 0600); err != nil {
|
||||||
log.Fatal("Trouble writing file", err)
|
log.Fatal("Trouble writing file", err)
|
||||||
}
|
}
|
||||||
if err := os.Rename(path, path+"_"); err != nil {
|
if err := os.Rename(path, path+"_"); err != nil {
|
||||||
|
@ -76,7 +79,7 @@ func writeLinesAtomic(path string, lines []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendToFile(path string, msg string) {
|
func appendToFile(path string, msg string) {
|
||||||
text, err := ioutil.ReadFile(path)
|
text, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Can't read file", err)
|
log.Fatal("Can't read file", err)
|
||||||
}
|
}
|
||||||
|
@ -164,6 +167,5 @@ func initFilesAndDirs() {
|
||||||
createFileIfNotExists(pwResetPath)
|
createFileIfNotExists(pwResetPath)
|
||||||
createFileIfNotExists(pwResetWaitPath)
|
createFileIfNotExists(pwResetWaitPath)
|
||||||
createFileIfNotExists(ipDelaysPath)
|
createFileIfNotExists(ipDelaysPath)
|
||||||
// TODO: Handle err here.
|
|
||||||
_ = os.Mkdir(feedsPath, 0700)
|
_ = os.Mkdir(feedsPath, 0700)
|
||||||
}
|
}
|
||||||
|
|
68
main.go
68
main.go
|
@ -1,34 +1,38 @@
|
||||||
// htwtxt – hosted twtxt server; see README for copyright and license info
|
// $TheSupernovaDuo: akyuu,v master 2023/4/14 21:2:6 yakumo_izuru Exp $
|
||||||
|
// See LICENSE for copyright details
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
import "flag"
|
"errors"
|
||||||
import "fmt"
|
"flag"
|
||||||
import "golang.org/x/crypto/bcrypt"
|
"fmt"
|
||||||
import "golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/bcrypt"
|
||||||
import "gopkg.in/gomail.v2"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
import "html/template"
|
"gopkg.in/gomail.v2"
|
||||||
import "io/ioutil"
|
"html/template"
|
||||||
import "log"
|
"io/ioutil"
|
||||||
import "net"
|
"log"
|
||||||
import "net/http"
|
"net"
|
||||||
import "os"
|
"net/http"
|
||||||
import "strconv"
|
"os"
|
||||||
import "strings"
|
"strconv"
|
||||||
import "syscall"
|
"strings"
|
||||||
import "time"
|
"syscall"
|
||||||
|
"time"
|
||||||
const resetLinkExp = 1800
|
)
|
||||||
const resetWaitTime = 3600 * 24
|
const (
|
||||||
const version = "1.0"
|
resetLinkExp = 1800
|
||||||
|
resetWaitTime = 3600 * 24
|
||||||
var contact string
|
version = "1.0"
|
||||||
var dialer *gomail.Dialer
|
)
|
||||||
var mailuser string
|
var (
|
||||||
var myself string
|
contact string
|
||||||
var signupOpen bool
|
dialer *gomail.Dialer
|
||||||
var templ *template.Template
|
mailuser string
|
||||||
|
myself string
|
||||||
|
signupOpen bool
|
||||||
|
templ *template.Template
|
||||||
|
)
|
||||||
|
|
||||||
func execTemplate(w http.ResponseWriter, file string, input string) {
|
func execTemplate(w http.ResponseWriter, file string, input string) {
|
||||||
type data struct{ Msg string }
|
type data struct{ Msg string }
|
||||||
|
@ -243,9 +247,9 @@ func readOptions() (string, int, string, int, string, bool) {
|
||||||
flag.StringVar(&keyPath, "key", "", "SSL key file")
|
flag.StringVar(&keyPath, "key", "", "SSL key file")
|
||||||
flag.StringVar(&certPath, "cert", "", "SSL certificate file")
|
flag.StringVar(&certPath, "cert", "", "SSL certificate file")
|
||||||
flag.StringVar(&templPath, "templates",
|
flag.StringVar(&templPath, "templates",
|
||||||
os.Getenv("GOPATH")+"/src/htwtxt/templates",
|
os.Getenv("GOPATH")+"/src/akyuu/templates",
|
||||||
"directory where to expect HTML templates")
|
"directory where to expect HTML templates")
|
||||||
flag.StringVar(&dataDir, "dir", os.Getenv("HOME")+"/htwtxt",
|
flag.StringVar(&dataDir, "dir", os.Getenv("HOME")+"/akyuu",
|
||||||
"directory to store feeds and login data")
|
"directory to store feeds and login data")
|
||||||
flag.StringVar(&contact, "contact",
|
flag.StringVar(&contact, "contact",
|
||||||
"[operator passed no contact info to server]",
|
"[operator passed no contact info to server]",
|
||||||
|
@ -284,7 +288,7 @@ func main() {
|
||||||
mailserver, mailport, mailpw, port, newLogin, showVersion :=
|
mailserver, mailport, mailpw, port, newLogin, showVersion :=
|
||||||
readOptions()
|
readOptions()
|
||||||
if showVersion {
|
if showVersion {
|
||||||
fmt.Println("htwtxt", version)
|
fmt.Println("akyuu", FullVersion())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
initFilesAndDirs()
|
initFilesAndDirs()
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
// $TheSupernovaDuo: akyuu,v master 2023/4/14 21:2:6 yakumo_izuru Exp $
|
||||||
|
// See LICENSE for copyright details
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultVersion = "0.0.0"
|
||||||
|
defaultCommit = "HEAD"
|
||||||
|
defaultBuild = "0000-01-01:00:00+00:00"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Version is the tagged release version in the form <major>.<minor>.<patch>
|
||||||
|
// following semantic versioning and is overwritten by the build system.
|
||||||
|
Version = defaultVersion
|
||||||
|
|
||||||
|
// Commit is the commit sha of the build (normally from Git) and is overwritten
|
||||||
|
// by the build system.
|
||||||
|
Commit = defaultCommit
|
||||||
|
|
||||||
|
// Build is the date and time of the build as an RFC3339 formatted string
|
||||||
|
// and is overwritten by the build system.
|
||||||
|
Build = defaultBuild
|
||||||
|
)
|
||||||
|
|
||||||
|
// FullVersion display the full version and build
|
||||||
|
func FullVersion() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
isDefault := Version == defaultVersion && Commit == defaultCommit && Build == defaultBuild
|
||||||
|
|
||||||
|
if !isDefault {
|
||||||
|
sb.WriteString(fmt.Sprintf("%s@%s %s", Version, Commit, Build))
|
||||||
|
}
|
||||||
|
|
||||||
|
if info, ok := debug.ReadBuildInfo(); ok {
|
||||||
|
if isDefault {
|
||||||
|
sb.WriteString(fmt.Sprintf(" %s", info.Main.Version))
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf(" %s", info.GoVersion))
|
||||||
|
if info.Main.Sum != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf(" %s", info.Main.Sum))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
読み込み中…
新しいイシューから参照