Compare commits

...

18 Commits

Author SHA1 Message Date
equa 1e7a016dca add search function 2023-07-12 10:33:27 -04:00
equa 0472c24199 basic email validation
the code for the panes is getting messy. since we have one window we
constantly reuse, and especially now that we have weird state-dependent
validation. probably good to rework?
2023-07-11 22:08:22 -04:00
equa a124b27021 review: make application scrollable; add S to go back to previous entry 2023-07-11 16:41:35 -04:00
vilmibm edf4f68932 stub help command 2023-06-07 07:12:32 +00:00
vilmibm 2fbedb75c2 have wire guy suggest nodding 2023-03-16 22:33:06 +00:00
vilmibm 1ca0893c2e minor 2023-03-16 21:38:54 +00:00
vilmibm ccc357591e fix SignupURL in stats 2023-03-16 07:09:28 +00:00
vilmibm 852a104300 annoying text input things 2023-03-16 07:04:32 +00:00
vilmibm bf2f2e3790 minor 2023-03-16 06:43:58 +00:00
vilmibm c5590cac95 dump script for user db 2023-03-16 06:41:22 +00:00
vilmibm f624483614 oops 2023-03-16 06:17:06 +00:00
vilmibm 1bec2349cb set created and admin when inserting user 2023-03-16 06:15:12 +00:00
vilmibm e12d92735d fix note import 2023-03-15 08:25:06 +00:00
vilmibm 3638685d24 add note 2023-03-15 08:16:16 +00:00
vilmibm cb0d574a76 ok 2023-03-15 08:13:44 +00:00
vilmibm 7291a43e68 oops 2023-03-15 08:11:55 +00:00
vilmibm 869eaa5f3b add dumps script 2023-03-15 07:53:36 +00:00
vilmibm 5876b0ebe3 Merge pull request 'fix spaces in passwords' (#1) from equa/town:trunk into trunk
Reviewed-on: tildetown/town#1
2023-03-14 21:57:09 +00:00
13 changed files with 399 additions and 54 deletions

View File

@ -8,11 +8,15 @@ commands:
- `request` (invokved on town as `town request-gitea` or `town request-gemini`), a helper command for requesting certain featuers be enabled for your town account
- `stats` (invoked as `town stats`), a command that prints out information about the town and its users in JSON.
- `visit` an experimental command for "visiting" a user.
- `signup` command that powers `ssh join@tilde.town`
- `welcome` command that powers `ssh welcome@tilde.town`
- `review` a TUI for town admins to review signups
There are also sundry helpers and scripts under `cmd/`.
A lot of this behavior (for example, `stats`) is exposed as a library. if you want to make some stuff for town and want to work in Go feel free to import this and use it.
## TODO
- [ ] expose `stats` as a library
- [ ] add a command for contributing scripts to the launcher's index
- [ ] consider bringing the launcher's index of commands into this git repo so it can be tracked
- [ ] add a Makefile

BIN
cmd/dumps/dumps 100755

Binary file not shown.

204
cmd/dumps/main.go 100644
View File

@ -0,0 +1,204 @@
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
"time"
"git.tilde.town/tildetown/town/models"
"git.tilde.town/tildetown/town/signup"
"git.tilde.town/tildetown/town/stats"
"git.tilde.town/tildetown/town/towndb"
"github.com/AlecAivazis/survey/v2"
)
// this is basically a pile of scripts. no hope is to be found here. this is
// dirty, one off code stored in case any useful patterns are worth extracting
// or for copypasta fodder.
func confirmContinue(msg string) {
var serr error
var conf bool
if serr = survey.AskOne(&survey.Confirm{
Message: msg,
Default: false,
}, &conf); serr != nil {
os.Exit(2)
}
if !conf {
os.Exit(1)
}
}
type jsonSignup struct {
Created float64
Email string
Username string
Reasons string
Plans string
Referral string
Socials string
Notes string
}
func main() {
db, err := towndb.ConnectDB()
if err != nil {
panic(err)
}
lol, err := os.ReadFile("/town/var/users.json")
if err != nil {
panic(err)
}
td, err := stats.Stats()
if err != nil {
panic(err)
}
lines := strings.Split(string(lol), "\n")
errs := []error{}
signups := make([]jsonSignup, len(lines))
for i, l := range lines {
l = strings.TrimSpace(l)
if l == "" {
continue
}
s := jsonSignup{}
err := json.Unmarshal([]byte(l), &s)
if err != nil {
fmt.Printf("%s %s", l, err.Error())
errs = append(errs, err)
} else {
signups[i] = s
}
}
if len(errs) > 0 {
confirmContinue(fmt.Sprintf("%d errors found deserializing; continue?", len(errs)))
}
ttbirth, err := time.Parse("2006-01-02 3:04pm", "2014-10-11 11:49pm")
if err != nil {
panic(err)
}
me := towndb.TownUser{
Created: ttbirth,
Username: "vilmibm",
Emails: []string{"vilmibm@protonmail.com"},
State: towndb.StateActive,
IsAdmin: true,
}
err = me.Insert(db)
if err != nil {
panic(err)
}
notFound := []jsonSignup{}
found := []jsonSignup{}
for _, su := range signups {
fl := len(found)
for _, u := range td.Users {
if su.Username == u.Username {
found = append(found, su)
break
}
}
if len(found) == fl {
notFound = append(notFound, su)
}
}
if len(notFound) > 0 {
confirmContinue(fmt.Sprintf("%d of those were not found. continue?", len(notFound)))
}
for _, su := range found {
var emails []string
if su.Email != "" {
emails = []string{su.Email}
}
u := towndb.TownUser{
Created: time.Unix(int64(su.Created), 0),
Emails: emails,
Username: su.Username,
State: towndb.StateActive,
}
if err = u.Insert(db); err != nil {
confirmContinue(fmt.Sprintf("%#v led to error %s; continue?", u, err.Error()))
}
if su.Notes != "" {
note := towndb.AdminNote{
Created: time.Time{},
AuthorID: me.ID,
Content: su.Notes,
UserID: u.ID,
}
if err = note.Insert(db); err != nil {
confirmContinue(fmt.Sprintf("%#v led to error %s; continue?", note, err.Error()))
}
}
}
}
func importSignups() {
lol, err := os.ReadFile("/town/var/signups.json")
if err != nil {
panic(err)
}
db, err := signup.ConnectDB()
if err != nil {
panic(err)
}
lines := strings.Split(string(lol), "\n")
errs := []error{}
signups := make([]jsonSignup, len(lines))
for i, l := range lines {
l = strings.TrimSpace(l)
if l == "" {
continue
}
s := jsonSignup{}
err := json.Unmarshal([]byte(l), &s)
if err != nil {
fmt.Printf("%s %s", l, err.Error())
errs = append(errs, err)
} else {
signups[i] = s
}
}
if len(errs) > 0 {
confirmContinue(fmt.Sprintf("%d errors found deserializing; continue?", len(errs)))
}
for _, s := range signups {
ts := models.TownSignup{
Created: time.Unix(int64(s.Created), 0),
Email: s.Email,
How: s.Referral,
Why: s.Reasons + "\n" + s.Plans,
Links: s.Socials,
}
if err = ts.Insert(db); err != nil {
confirmContinue(fmt.Sprintf("%#v led to error %s; continue?", ts, err.Error()))
}
if s.Notes != "" {
note := models.SignupNote{
Created: time.Now(),
Author: "IMPORT",
Content: s.Notes,
SignupID: ts.ID,
}
if err = note.Insert(db); err != nil {
confirmContinue(fmt.Sprintf("%#v led to error %s; continue?", ts, err.Error()))
}
}
}
}

15
cmd/help/README.md 100644
View File

@ -0,0 +1,15 @@
# help
another ssh command:
```
ssh help@tilde.town
hey what's up?
> i'm a user and i need to reset my key
i tried to sign up and it just didn't work
i have a concern or complaint about the town
```
the goal with this is to formalize the "i forgot my key" process and get us off of using an external email inbox which i find irritating.

7
cmd/help/main.go 100644
View File

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("TODO")
}

View File

@ -1,12 +1,15 @@
package main
import (
"bytes"
"database/sql"
"errors"
"fmt"
"math/rand"
"os"
"os/exec"
"os/user"
"strconv"
"strings"
"time"
@ -88,6 +91,42 @@ func renderNotes(s models.TownSignup) string {
return out
}
func searchSignups(signups []*models.TownSignup) (int, error) {
escapeNuls := func(str string) string {
return strings.ReplaceAll(str, "\000", " ")
}
buf := new(bytes.Buffer)
for ix, signup := range signups {
fmt.Fprintf(buf, "%d\t%s\000", ix, escapeNuls(signup.Email))
fmt.Fprintf(buf, "%d\t%s\000", ix, escapeNuls(signup.How))
fmt.Fprintf(buf, "%d\t%s\000", ix, escapeNuls(signup.Links))
fmt.Fprintf(buf, "%d\t%s\000", ix, escapeNuls(signup.Why))
}
cmd := exec.Command("fzf", "--read0", "--delimiter=\t", "--tac", "--with-nth=2..")
cmd.Stdin = buf
cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
return -1, err
}
if len(out) == 0 {
return -1, nil
}
s := strings.Split(string(out[:]), "\t")[0]
n, err := strconv.Atoi(s)
if err != nil {
return -1, err
}
return n, nil
}
func _main() error {
inviteDB, err := invites.ConnectDB()
if err != nil {
@ -132,10 +171,11 @@ func _main() error {
title.SetBackgroundColor(tcell.ColorBlack)
appView := tview.NewTextView()
appView.SetScrollable(true)
appView.SetDynamicColors(true)
legend := tview.NewTextView()
legend.SetText("s: skip r: random A: approve R: reject N: notate Q: quit")
legend.SetText("s/S: next/prev r: random F: find A: approve R: reject N: notate Q: quit")
legend.SetTextColor(tcell.ColorPurple)
legend.SetTextAlign(tview.AlignCenter)
legend.SetBackgroundColor(tcell.ColorBlack)
@ -169,6 +209,11 @@ func _main() error {
mainFlex.AddItem(title, 1, -1, false)
mainFlex.AddItem(innerFlex, 0, 1, false)
mainFlex.AddItem(bottomFlex, 1, -1, false)
// set scrollable
mainFlex.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
appView.InputHandler()(event, func(p tview.Primitive) {})
return nil
})
pages := tview.NewPages()
@ -225,18 +270,18 @@ func _main() error {
decisionFI.SetOptions([]string{"accepted", "rejected"}, func(_ string, _ int) {})
cleanEmailInput := tview.NewInputField()
cleanEmailInput.SetLabel("clean email")
cleanEmailInput.SetLabel("clean email ")
cleanEmailInput.SetAcceptanceFunc(func(tx string, _ rune) bool { return len(tx) > 0 })
reviewForm.AddFormItem(decisionFI)
reviewForm.AddFormItem(cleanEmailInput)
reviewForm.AddButton("submit", func() {
currSignup := signups[signupIx]
cleanEmail := reviewForm.GetFormItemByLabel("clean email").(*tview.InputField).GetText()
cleanEmail := cleanEmailInput.GetText()
currSignup.CleanEmail = cleanEmail
decision := models.SignupRejected
_, d := reviewForm.GetFormItemByLabel("decision").(*tview.DropDown).GetCurrentOption()
_, d := decisionFI.GetCurrentOption()
if d == "accepted" {
decision = models.SignupAccepted
}
@ -306,20 +351,48 @@ func _main() error {
if signupIx == len(signups) {
signupIx = 0
}
updateCount()
render()
return nil
case 'S':
signupIx--
if signupIx < 0 {
signupIx = len(signups) - 1
}
updateCount()
render()
return nil
case 'r':
if len(signups) > 0 {
signupIx = rand.Intn(len(signups))
updateCount()
render()
}
// TODO: there's a bunch of messy state management.
// should we generate this pane functionally?
case 'A':
if len(signups) == 0 {
return nil
}
emailVal := signups[signupIx].Email
providedEmailView.SetText(emailVal)
cleanEmailInput.SetPlaceholder(
strings.TrimSpace(strings.ReplaceAll(emailVal, "\n", " ")))
cleanEmailInput.SetLabel("clean email ")
cleanEmailInput.SetText("")
/*
TODO the placeholder doesn't appear to become the default text which is
what I wanted it to do. Just taking this out so the blank box beckons
input. Also, it seems like the AcceptanceFunc didn't work since a blank
value got through.
cleanEmailInput.SetPlaceholder(
strings.TrimSpace(strings.ReplaceAll(emailVal, "\n", " ")))
*/
cleanEmailInput.SetChangedFunc(func(text string) {
if strings.Contains(emailVal, text) {
cleanEmailInput.SetLabel("clean email ")
} else {
cleanEmailInput.SetLabel("[red]clean email :(")
}
})
decisionFI.SetCurrentOption(0)
pages.SwitchToPage("review")
app.SetFocus(cleanEmailInput)
@ -330,8 +403,23 @@ func _main() error {
}
emailVal := signups[signupIx].Email
providedEmailView.SetText(emailVal)
cleanEmailInput.SetPlaceholder(
strings.TrimSpace(strings.ReplaceAll(emailVal, "\n", " ")))
cleanEmailInput.SetLabel("clean email ")
cleanEmailInput.SetText("")
/*
TODO the placeholder doesn't appear to become the default text which is
what I wanted it to do. Just taking this out so the blank box beckons
input. Also, it seems like the AcceptanceFunc didn't work since a blank
value got through.
cleanEmailInput.SetPlaceholder(
strings.TrimSpace(strings.ReplaceAll(emailVal, "\n", " ")))
*/
cleanEmailInput.SetChangedFunc(func(text string) {
if strings.Contains(emailVal, text) {
cleanEmailInput.SetLabel("clean email ")
} else {
cleanEmailInput.SetLabel("[red]clean email :(")
}
})
decisionFI.SetCurrentOption(1)
pages.SwitchToPage("review")
app.SetFocus(cleanEmailInput)
@ -342,6 +430,28 @@ func _main() error {
}
pages.SwitchToPage("notate")
return nil
case 'F':
app.Suspend(func() {
ix, err := searchSignups(signups)
if err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
// no match or interrupt. who cares
switch exiterr.ExitCode() {
case 1: case 130:
return
}
}
errorModal.SetText(fmt.Sprintf("error! failed to search: %s", err.Error()))
pages.SwitchToPage("error")
} else if ix >= 0 {
signupIx = ix
}
})
updateCount()
render()
return nil
case 'Q':
app.Stop()
}

View File

@ -206,7 +206,7 @@ func _main(l *log.Logger, db *sql.DB) error {
func(s *scene, tv *tview.TextView, msg string) {
// TODO could check and see if it's email shaped and admonish if not
trimmed := strings.TrimSpace(msg)
fmt.Fprintln(tv, s.Host.Say(fmt.Sprintf("I heard '%s'. Is that right?", trimmed)))
fmt.Fprintln(tv, s.Host.Say(fmt.Sprintf("I heard '%s'. Is that right? if so, /nod", trimmed)))
}),
newScene("how", heredoc.Doc(`
The workshop fades away. You hear the sound of a dial up modem

View File

@ -1,6 +1,6 @@
# townstats
This program dumps information about tilde.town in the [Tilde Data Protocol](http://protocol.club/~datagrok/beta-wiki/tdp.html).
Code for dumping information about tilde.town in the [Tilde Data Protocol](http://protocol.club/~datagrok/beta-wiki/tdp.html).
# author
@ -8,4 +8,4 @@ vilmibm, based on python work by [datagrok](https://datagrok.org)
# license
gplv3+
gplv3+

View File

@ -7,6 +7,7 @@ import (
"fmt"
"os"
"os/exec"
"time"
"git.tilde.town/tildetown/town/invites"
"git.tilde.town/tildetown/town/stats"
@ -182,6 +183,7 @@ func createUser(data newUserData) (err error) {
tu := towndb.TownUser{
Username: data.Username,
Created: time.Now(),
Emails: []string{
data.Email,
},

7
go.mod
View File

@ -6,6 +6,9 @@ require (
github.com/AlecAivazis/survey/v2 v2.3.5
github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/charmbracelet/glamour v0.5.0
github.com/charmbracelet/lipgloss v0.6.0
github.com/gdamore/tcell/v2 v2.5.3
github.com/mattn/go-sqlite3 v1.14.16
github.com/rivo/tview v0.0.0-20230130130022-4a1b7a76c01c
github.com/spf13/cobra v1.5.0
gopkg.in/yaml.v3 v3.0.1
@ -14,10 +17,8 @@ require (
require (
github.com/alecthomas/chroma v0.10.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/charmbracelet/lipgloss v0.6.0 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/tcell/v2 v2.5.3 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
@ -25,7 +26,6 @@ require (
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/microcosm-cc/bluemonday v1.0.17 // indirect
github.com/muesli/reflow v0.3.0 // indirect
@ -39,5 +39,4 @@ require (
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/AlecAivazis/survey.v1 v1.8.8 // indirect
)

15
go.sum
View File

@ -1,9 +1,7 @@
github.com/AlecAivazis/survey/v2 v2.0.5/go.mod h1:WYBhg6f0y/fNYUuesWQc0PKbJcEliGcYHB9sNT3Bg74=
github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ=
github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A=
github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
@ -28,20 +26,17 @@ github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV
github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
@ -59,7 +54,6 @@ github.com/microcosm-cc/bluemonday v1.0.17/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8=
github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw=
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 h1:STjmj0uFfRryL9fzRA/OupNppeAID6QJYPMavTL7jtY=
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
@ -79,7 +73,6 @@ github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJ
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -88,15 +81,9 @@ github.com/yuin/goldmark v1.4.4 h1:zNWRjYUW32G9KirMXYHQHVNFkXvMI7LpgNW2AgYAoIs=
github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -114,8 +101,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/AlecAivazis/survey.v1 v1.8.8 h1:5UtTowJZTz1j7NxVzDGKTz6Lm9IWm8DDF6b7a2wq9VY=
gopkg.in/AlecAivazis/survey.v1 v1.8.8/go.mod h1:CaHjv79TCgAvXMSFJSVgonHXYWxnhzI3eoHtnX5UgUo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@ -318,7 +318,7 @@ func Stats() (TildeData, error) {
return TildeData{
Name: "tilde.town",
URL: "https://tilde.town",
SignupURL: "https://cgi.tilde.town/users/signup",
SignupURL: "https://tilde.town/signup",
WantUsers: true,
AdminEmail: "root@tilde.town",
Description: description,

View File

@ -27,34 +27,57 @@ type AdminNote struct {
}
func (n *AdminNote) Insert(db *sql.DB) error {
n.Created = time.Now()
stmt, err := db.Prepare(`
INSERT INTO notes (created, authorid, content, userid)
VALUES (
?, ?, ?, ?
)`)
var (
err error
stmt *sql.Stmt
result sql.Result
liid int64
)
tx, err := db.Begin()
if err != nil {
return err
}
result, err := stmt.Exec(
defer func() {
if err != nil {
tx.Rollback()
}
}()
stmt, err = tx.Prepare(`
INSERT INTO notes (created, author, content)
VALUES (?, ?, ?)`)
if err != nil {
return err
}
result, err = stmt.Exec(
n.Created.Unix(),
n.AuthorID,
n.Content,
n.UserID)
n.Content)
if err != nil {
return err
}
defer stmt.Close()
liid, err := result.LastInsertId()
liid, err = result.LastInsertId()
if err != nil {
return err
}
n.ID = liid
return nil
stmt, err = tx.Prepare(`
INSERT INTO user_notes (noteid, userid) VALUES (?, ?)`)
if err != nil {
return err
}
_, err = stmt.Exec(n.ID, n.UserID)
if err != nil {
return err
}
return tx.Commit()
}
type TownUser struct {
@ -83,21 +106,17 @@ func (u *TownUser) Insert(db *sql.DB) (err error) {
}
}()
// TODO this does not set the admin flag intentionally as rn this code is
// just meant to be called by the welcome binary; other stuff for now is just
// expected to be done via sql
u.Created = time.Now()
if stmt, err = tx.Prepare(`
INSERT INTO users (created, username, state)
VALUES (?, ?, ?)`); err != nil {
INSERT INTO users (created, username, state, admin)
VALUES (?, ?, ?, ?)`); err != nil {
return err
}
if result, err = stmt.Exec(
u.Created.Unix(),
u.Username,
u.State); err != nil {
u.State,
u.IsAdmin); err != nil {
return err
}