Compare commits

...

2 commits

Author SHA1 Message Date
Magnus Åhall
66c1c2198b Fixed HTML page 2025-08-06 11:16:01 +02:00
94512dff5e Fixed ENV support 2025-08-06 10:56:56 +02:00
3 changed files with 145 additions and 57 deletions

18
Dockerfile Normal file
View file

@ -0,0 +1,18 @@
FROM alpine:latest
COPY nodebb_invite_link /usr/local/bin/
VOLUME /var/lib/nodebb_invite_link
EXPOSE 9876
ENV DOMAIN ""
ENV "INVITE_LISTEN" 9876
ENV "INVITE_SEQ" "/var/lib/nodebb_invite_link/sequence"
ENV "INVITE_DBHOST" "postgres"
ENV "INVITE_DBPORT" 5432
ENV "INVITE_DBNAME" "nodebb"
ENV "INVITE_DBUSERNAME" "nodebb"
ENV "INVITE_DBPASSWORD" "nodebb"
CMD ["/usr/local/bin/nodebb_invite_link"]

1
db.go
View file

@ -22,7 +22,6 @@ func initDB(host string, port int, dbName, username, password string) (err error
password,
dbName,
)
if db, err = sqlx.Connect("postgres", dbConn); err != nil {
return
}

183
main.go
View file

@ -26,23 +26,49 @@ var (
flagDatabase string
flagListenPort int
flagSequenceFilename string
flagDomain string
flagDomain string
)
func init() {
var err error
port := 5432
if os.Getenv("INVITE_DBPORT") != "" {
port, err = strconv.Atoi(os.Getenv("INVITE_DBPORT"))
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
listen := 9876
if os.Getenv("INVITE_LISTEN") != "" {
listen, err = strconv.Atoi(os.Getenv("INVITE_LISTEN"))
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
flag.BoolVar(&flagVersion, "version", false, "Display version and exit")
flag.StringVar(&flagHost, "host", "", "Database host")
flag.StringVar(&flagUsername, "username", "", "Database username")
flag.StringVar(&flagPassword, "password", "", "Database password")
flag.StringVar(&flagDatabase, "database", "", "Database name")
flag.IntVar(&flagPort, "port", 5432, "Database port")
flag.IntVar(&flagListenPort, "listen", 9876, "Web server listen port")
flag.StringVar(&flagSequenceFilename, "seq", "sequence", "Sequence filename")
flag.StringVar(&flagDomain, "domain", "", "Domain FQDN")
flag.StringVar(&flagHost, "host", os.Getenv("INVITE_DBHOST"), "Database host")
flag.StringVar(&flagUsername, "username", os.Getenv("INVITE_DBUSERNAME"), "Database username")
flag.StringVar(&flagPassword, "password", os.Getenv("INVITE_DBPASSWORD"), "Database password")
flag.StringVar(&flagDatabase, "database", os.Getenv("INVITE_DBNAME"), "Database name")
flag.IntVar(&flagPort, "port", port, "Database port")
flag.IntVar(&flagListenPort, "listen", listen, "Web server listen port")
flag.StringVar(&flagSequenceFilename, "seq", os.Getenv("INVITE_SEQ"), "Sequence filename")
flag.StringVar(&flagDomain, "domain", os.Getenv("INVITE_DOMAIN"), "Domain FQDN")
flag.Parse()
}
func main() {
if flagSequenceFilename == "" {
fmt.Println("Please specify -seq with a sequenc filename.")
os.Exit(1)
}
err := initDB(flagHost, flagPort, flagDatabase, flagUsername, flagPassword)
if err != nil {
fmt.Println(err)
@ -50,6 +76,7 @@ func main() {
}
listenOn := fmt.Sprintf("[::]:%d", flagListenPort)
http.HandleFunc("/", pageIndex)
http.HandleFunc("/invite", createInvite)
fmt.Printf("Listen on %s\n", listenOn)
err = http.ListenAndServe(listenOn, nil)
@ -82,6 +109,50 @@ func sequenceNext() (seq int, err error) {
return
}
func pageIndex(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
</head>
<body style="margin: 32px">
<div style="margin-bottom: 32px"><img src="/images/logo.svg"></div>
<div>Skapar länk...</div>
<div id="link"></div>
<script type="text/javascript">
fetch('./invite')
.then(data=>data.json())
.then(json=>{
const el = document.getElementById('link')
const desc = document.createElement('div')
desc.style.marginTop = '32px'
desc.innerText = 'Klicka länken (den kopieras automatiskt) och skicka till den nya medlemmen: '
const l = document.createElement('div')
l.innerText = json.Link
l.style.cursor = 'pointer'
l.style.marginTop = '8px'
l.style.color = '#782144'
l.addEventListener('click', event=>{
navigator.clipboard.writeText(json.Link)
event.target.style.color = '#6c31d7'
setTimeout(()=>event.target.style.color = '#782144', 250)
})
el.append(desc)
el.append(l)
})
.catch(err=>alert(err))
</script>
</body>
</html>
`))
}
func createInvite(w http.ResponseWriter, r *http.Request) {
seq, err := sequenceNext()
if err != nil {
@ -93,57 +164,59 @@ func createInvite(w http.ResponseWriter, r *http.Request) {
email := fmt.Sprintf("%08d@example.com", seq)
expire := time.Now().Add(time.Hour * 24 * 7)
// legacy_object
key := fmt.Sprintf("invitation:uid:2:invited:%s", email)
_, err = db.Exec(` INSERT INTO public.legacy_object(_key, "type", "expireAt") VALUES($1, 'string', null)`, key)
if err != nil {
w.Write([]byte(err.Error()))
return
}
if false {
// legacy_object
key := fmt.Sprintf("invitation:uid:2:invited:%s", email)
_, err = db.Exec(` INSERT INTO public.legacy_object(_key, "type", "expireAt") VALUES($1, 'string', null)`, key)
if err != nil {
w.Write([]byte(err.Error()))
return
}
key = fmt.Sprintf("invitation:invited:%s", email)
_, err = db.Exec(` INSERT INTO public.legacy_object(_key, "type", "expireAt") VALUES($1, 'set', null)`, key)
if err != nil {
w.Write([]byte(err.Error()))
return
}
key = fmt.Sprintf("invitation:invited:%s", email)
_, err = db.Exec(` INSERT INTO public.legacy_object(_key, "type", "expireAt") VALUES($1, 'set', null)`, key)
if err != nil {
w.Write([]byte(err.Error()))
return
}
key = fmt.Sprintf("invitation:token:%s", newUUID)
_, err = db.Exec(` INSERT INTO public.legacy_object(_key, "type", "expireAt") VALUES($1, 'hash', $2)`, key, expire)
if err != nil {
w.Write([]byte(err.Error()))
return
}
key = fmt.Sprintf("invitation:token:%s", newUUID)
_, err = db.Exec(` INSERT INTO public.legacy_object(_key, "type", "expireAt") VALUES($1, 'hash', $2)`, key, expire)
if err != nil {
w.Write([]byte(err.Error()))
return
}
// legacy_hash
key = fmt.Sprintf("invitation:token:%s", newUUID)
data := fmt.Sprintf(`{"email": "%s", "token": "%s", "inviter": 2, "groupsToJoin": "[]"}`, email, newUUID)
_, err = db.Exec(` INSERT INTO public.legacy_hash(_key, "data", "type") VALUES($1, $2, 'hash')`, key, data)
if err != nil {
w.Write([]byte(err.Error()))
return
}
// legacy_hash
key = fmt.Sprintf("invitation:token:%s", newUUID)
data := fmt.Sprintf(`{"email": "%s", "token": "%s", "inviter": 2, "groupsToJoin": "[]"}`, email, newUUID)
_, err = db.Exec(` INSERT INTO public.legacy_hash(_key, "data", "type") VALUES($1, $2, 'hash')`, key, data)
if err != nil {
w.Write([]byte(err.Error()))
return
}
// legacy_set
key = fmt.Sprintf("invitation:uid:2")
_, err = db.Exec(` INSERT INTO public.legacy_set(_key, "member", "type") VALUES($1, $2, 'set')`, key, email)
if err != nil {
w.Write([]byte(err.Error()))
return
}
// legacy_set
key = "invitation:uid:2"
_, err = db.Exec(` INSERT INTO public.legacy_set(_key, "member", "type") VALUES($1, $2, 'set')`, key, email)
if err != nil {
w.Write([]byte(err.Error()))
return
}
key = fmt.Sprintf("invitation:invited:%s", email)
_, err = db.Exec(` INSERT INTO public.legacy_set(_key, "member", "type") VALUES($1, $2, 'set')`, key, newUUID)
if err != nil {
w.Write([]byte(err.Error()))
return
}
key = fmt.Sprintf("invitation:invited:%s", email)
_, err = db.Exec(` INSERT INTO public.legacy_set(_key, "member", "type") VALUES($1, $2, 'set')`, key, newUUID)
if err != nil {
w.Write([]byte(err.Error()))
return
}
key = fmt.Sprintf("invitation:uid:2:invited:%s", email)
_, err = db.Exec(` INSERT INTO public.legacy_string(_key, "data", "type") VALUES($1, $2, 'string')`, key, newUUID)
if err != nil {
w.Write([]byte(err.Error()))
return
key = fmt.Sprintf("invitation:uid:2:invited:%s", email)
_, err = db.Exec(` INSERT INTO public.legacy_string(_key, "data", "type") VALUES($1, $2, 'string')`, key, newUUID)
if err != nil {
w.Write([]byte(err.Error()))
return
}
}
j, _ := json.Marshal(struct {
@ -152,6 +225,4 @@ func createInvite(w http.ResponseWriter, r *http.Request) {
fmt.Sprintf("https://%s/register?token=%s", flagDomain, newUUID),
})
w.Write(j)
return
}