-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsubforumIndex.go
More file actions
134 lines (117 loc) · 3.43 KB
/
Copy pathsubforumIndex.go
File metadata and controls
134 lines (117 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package main
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"net/url"
"sort"
"strings"
"time"
"codeberg.org/FiskFan1999/gemini"
"github.com/hako/durafmt"
bolt "go.etcd.io/bbolt"
)
var BadUserInput = gemini.ResponseFormat{
Status: gemini.PermanentFailure,
Mime: "Bad user input",
Lines: nil,
}
var NotFound = gemini.ResponseFormat{
Status: gemini.NotFound,
Mime: "Not found",
Lines: nil,
}
var ErrNotFound = errors.New("Subforum not found in database")
func GetThreadsForSubforum(subforum string) (threads SubforumThreads, err error) {
if err = db.View(func(tx *bolt.Tx) error {
subforumBucketParent := tx.Bucket(DBSUBFORUMS)
if subforumBucketParent == nil {
return errors.New("Subforum root bucket not found")
}
subforumBucket := subforumBucketParent.Bucket([]byte(subforum))
if subforumBucket == nil {
return errors.New("Subforum not found")
}
threadsBucket := tx.Bucket(DBALLTHREADS)
sfbc := subforumBucket.Cursor()
for _, threadID := sfbc.First(); threadID != nil; _, threadID = sfbc.Next() {
threadInfo := threadsBucket.Bucket(threadID)
if threadInfo == nil {
return errors.New("threadInfo == nil")
}
// load thread bucket with this id
var t Thread
t.ID = threadID
t.Title = threadInfo.Get([]byte("title"))
t.User = threadInfo.Get([]byte("user"))
if err := t.LastModified.UnmarshalText(threadInfo.Get([]byte("lastmodified"))); err != nil {
return err
}
t.Locked = bytes.Equal(threadInfo.Get([]byte("locked")), []byte("1"))
t.Archived = bytes.Equal(threadInfo.Get([]byte("archived")), []byte("1"))
threads = append(threads, t)
}
return nil
}); err != nil {
return
}
sort.Sort(threads)
return
}
func SubforumIndexHandler(u *url.URL, c *tls.Conn) gemini.Response {
lines := gemini.Lines{}
pathspl := strings.FieldsFunc(u.EscapedPath(), func(r rune) bool { return r == '/' })
if len(pathspl) == 1 {
/*
Some gemini clients have a "parent directory" function in which the next request is in the parent directory of path (in this case "/f/")
As a friendly fallback, show them the home page instead of throwing an error.
*/
return RootHandler(c)
} else if len(pathspl) != 2 {
return BadUserInput
}
subforumID := pathspl[1]
name, exists := SubforumExists(Configuration.Forum, subforumID)
if !exists {
return NotFound
}
lines = append(lines, fmt.Sprintf("%s%s", gemini.Header, name))
lines = append(lines, fmt.Sprintf("%s/new/thread/%s Post new thread", gemini.Link, subforumID), "")
threads, err := GetThreadsForSubforum(subforumID)
if err != nil {
fmt.Println(err.Error())
return gemini.TemporaryFailure.Error(err)
}
for _, t := range threads {
var buf bytes.Buffer
// timeSinceMod := time.Since(t.LastModified)
fmt.Fprintf(&buf, "%s/thread/%s/ %s (%s)", gemini.Link, t.ID, t.Title, TimeFormatForThread(t.LastModified))
lines = append(lines, buf.String())
}
return gemini.ResponseFormat{
Status: gemini.Success,
Mime: "text/gemini",
Lines: lines,
}
}
func TimeFormatForThread(t time.Time) string {
/*
If time is less than 24 hours old, print
Otherwise, print UTC in some format
*/
if time.Since(t) < time.Hour*24*7 {
return fmt.Sprintf("%s", durafmt.ParseShort(time.Since(t)))
}
return t.UTC().Format("02 Jan 2006")
}
func SubforumExists(rootforum []Forum, id string) (string, bool) {
for _, f := range rootforum {
for _, sub := range f.Subforum {
if sub.ID == id {
return sub.Name, true
}
}
}
return "", false
}