M TODO => TODO +7 -0
@@ 1,6 1,13 @@
Setup fishbb.org (cloud provider)
+RESEARCH:
+- look into email: IMAP uname/pw?
+
FEATURES:
+consider htmx
+user profile should be /u/{profile} not id
+4chan style replies
+
minor/bugs:
rate limit registration
better error handling on login
M db.go => db.go +10 -9
@@ 10,14 10,6 @@ import (
_ "github.com/mattn/go-sqlite3"
)
-var stmtGetForumID, stmtUpdateMe, stmtSearchPosts,
- stmtEditPost, stmtGetPost, stmtGetPostSlug, stmtGetForum,
- stmtGetForumBySlug, stmtCreateUser, stmtGetForums, stmtUpdateForum,
- stmtGetUser, stmtGetUsers, stmtGetPostAuthorID, stmtDeletePost,
- stmtThreadPin, stmtThreadLock, stmtActivateUser,
- stmtCreatePost, stmtGetThread, stmtGetPosts, stmtGetThreadCount,
- stmtQueryPosts, stmtDeleteUser, stmtUpdateUserRole, stmtUpdateBanStatus, stmtUpdateConfig, stmtGetConfig,
- stmtGetThreads, stmtCreateThread, stmtCreateForum *sql.Stmt
var db *sql.DB
func opendb() *sql.DB {
@@ 88,8 80,16 @@ func prepare(db *sql.DB, stmt string) *sql.Stmt {
return s
}
+var stmtGetForumID, stmtUpdateMe, stmtSearchPosts,
+ stmtEditPost, stmtGetPost, stmtGetPostSlug, stmtGetForum,
+ stmtGetForumBySlug, stmtCreateUser, stmtGetForums, stmtUpdateForum,
+ stmtGetUser, stmtGetUsers, stmtGetPostAuthorID, stmtDeletePost,
+ stmtThreadPin, stmtThreadLock, stmtActivateUser, stmtGetAllUsernames,
+ stmtCreatePost, stmtGetThread, stmtGetPosts, stmtGetThreadCount,
+ stmtQueryPosts, stmtDeleteUser, stmtUpdateUserRole, stmtUpdateBanStatus, stmtUpdateConfig, stmtGetConfig,
+ stmtGetThreads, stmtCreateThread, stmtCreateForum *sql.Stmt
+
func prepareStatements(db *sql.DB) {
- // TODO maybe do this in code?
stmtGetForums = prepare(db, `
select forums.id, name, description, permissions,
coalesce(threadid, 0), coalesce(latest.title, ''), coalesce(latest.id, 0), coalesce(latest.authorid, 0),
@@ 199,6 199,7 @@ func prepareStatements(db *sql.DB) {
stmtQueryPosts = prepare(db, "select 1")
stmtUpdateConfig = prepare(db, "insert into config(value) values(?)")
stmtGetConfig = prepare(db, "select value from config order by id desc limit 1")
+ stmtGetAllUsernames = prepare(db, "select username from users;")
LoginInit(LoginInitArgs{
Db: db,
})
A gemtext.go => gemtext.go +15 -0
@@ 0,0 1,15 @@
+package main
+
+import (
+ "html/template"
+ "strings"
+
+ "git.sr.ht/~aw/gmi2html"
+)
+
+func (p Post) Render() template.HTML {
+ r := strings.NewReader(p.Content)
+ g := gmi2html.NewReader(r)
+ g.NestedBlocks = true
+ return template.HTML(g.HTMLString())
+}
M go.mod => go.mod +1 -1
@@ 8,13 8,13 @@ require (
)
require (
+ git.sr.ht/~aw/gmi2html v0.1.3
github.com/BurntSushi/toml v1.4.0
github.com/alexedwards/argon2id v1.0.0
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/httplog/v2 v2.0.11
github.com/go-chi/httprate v0.9.0
github.com/k3a/html2text v1.2.1
- github.com/yuin/goldmark v1.7.1
golang.org/x/oauth2 v0.21.0
)
M go.sum => go.sum +2 -2
@@ 1,5 1,7 @@
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
+git.sr.ht/~aw/gmi2html v0.1.3 h1:Gj2vcmI/HqSs+9t05s0y/9n8o7swXSnJH4H+AQSu1Sc=
+git.sr.ht/~aw/gmi2html v0.1.3/go.mod h1:WXvBJ0UXRsTcMcm/jmE1yJXz4bxUZDdFqv8HDcIueE0=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHcQQP0w=
@@ 27,8 29,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
-github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
M notifications.go => notifications.go +1 -1
@@ 4,7 4,7 @@ import "time"
type Notification struct {
ID int
- Message string // markdown
+ Message string
Created time.Time
}
M post.go => post.go +2 -0
@@ 15,6 15,8 @@ type Post struct {
Edited *time.Time
}
+// Custom inline parser for user and post links
+
// TODO reconcile these somehow maybe
type PostSummary struct {
ID int
M schema.sql => schema.sql +1 -0
@@ 24,6 24,7 @@ create table posts (
threadid integer,
authorid integer,
content text,
+ in_reply_to integer,
created datetime default current_timestamp,
edited datetime,
foreign key (authorid) references users(id),
M user.go => user.go +18 -0
@@ 99,6 99,24 @@ func getUsers() ([]User, error) {
return users, nil
}
+// unused
+func getAllUsernames() ([]string, error) {
+ var usernames []string
+ rows, err := stmtGetUsers.Query()
+ if err != nil {
+ return nil, fmt.Errorf("could not execute query: %w", err)
+ }
+ for rows.Next() {
+ var username string
+ err := rows.Scan(&username)
+ if err != nil {
+ return nil, fmt.Errorf("failed to scan rows: %w", err)
+ }
+ usernames = append(usernames, username)
+ }
+ return usernames, nil
+}
+
func activateUser(id int) error {
_, err := stmtActivateUser.Exec(id)
return err
M util.go => util.go +0 -19
@@ 5,7 5,6 @@ import (
"crypto/rand"
"crypto/sha512"
"fmt"
- "html/template"
"image"
"image/png"
"io"
@@ 14,9 13,6 @@ import (
"strconv"
"time"
- "github.com/yuin/goldmark"
- "github.com/yuin/goldmark/extension"
-
"strings"
)
@@ 185,18 181,3 @@ func timeago(t *time.Time) string {
return fmt.Sprintf("%d %ss ago", amount, metric)
}
}
-
-// TODO investigate extensions
-var gm = goldmark.New(
- goldmark.WithExtensions(extension.Linkify),
-)
-
-func markup(md string) template.HTML {
- var buf bytes.Buffer
- err := gm.Convert([]byte(md), &buf)
- if err != nil {
- // TODO error handling? Bluemonday as backup?
- return template.HTML("(Error rendering post)!")
- }
- return template.HTML(buf.String())
-}
M views/edit-post.html => views/edit-post.html +1 -1
@@ 4,7 4,7 @@
<div class="body-padded" >
<label for="content">Content:</label><br>
<textarea class="post-entry" required name="content" id="content" placeholder='I have something to say...'>{{.Post.Content}}</textarea><br>
- <a href="https://www.markdownguide.org/cheat-sheet/#basic-syntax">markdown guide</a><br>
+ <a href="https://admin.flounder.online/gemini_text_guide.gmi">gemtext guide</a><br>
<input type="hidden" name="CSRF" value="{{.CSRFToken}}">
<button type="submit">Save</button>
M views/new-post.html => views/new-post.html +1 -1
@@ 8,7 8,7 @@
<div class="body-padded" >
<label for="content">Content:</label><br>
<textarea class="post-entry" required name="content" id="content" placeholder='I have something to say...'></textarea><br>
- <a href="https://www.markdownguide.org/cheat-sheet/#basic-syntax">markdown guide</a><br>
+ <a href="https://admin.flounder.online/gemini_text_guide.gmi">gemtext guide</a><br>
<input type="hidden" name="CSRF" value="{{.CSRFToken}}">
<button type="submit">Create</button>
M views/style.css => views/style.css +14 -0
@@ 67,6 67,20 @@ button:not(.link-button) {
font-size: 16px;
}
+.post-body p {
+ margin-top: 0px;
+ margin-bottom: 0.2rem;
+}
+
+
+blockquote {
+ margin-left: .5em;
+ font-style: italic;
+ background: var(--bg-alt);
+ border-left: .5em solid var(--bg-inverse);
+ padding: .5em 10px;
+}
+
.error {
font-weight: bold;
}
M views/thread.html => views/thread.html +1 -1
@@ 32,7 32,7 @@
</div>
</div>
<div class="post-body">
- {{ markup .Content }}
+ {{ .Render }}
</div>
<div class="flex-end">
<a href="/post/new?threadid={{$.Thread.ID}}&reply={{.ID}}"><button class="padded-button">Reply</button></a></div>
M web.go => web.go +0 -1
@@ 366,7 366,6 @@ func loadTemplates() *template.Template {
views, err := template.New("main").Funcs(template.FuncMap{
"timeago": timeago,
"pageArr": pageArray,
- "markup": markup,
"inc": func(i int) int {
return i + 1