1 package server
2
3 import (
4 "bytes"
5 "fmt"
6 "html/template"
7 "io/fs"
8 "log"
9 "net/http"
10 "path/filepath"
11 "strconv"
12 "strings"
13 "time"
14
15 "codeberg.org/tslocum/sriracha/internal/database"
16 . "codeberg.org/tslocum/sriracha/model"
17 . "codeberg.org/tslocum/sriracha/util"
18 )
19
20 func (s *Server) serveStatus(data *templateData, db *database.DB, w http.ResponseWriter, r *http.Request) {
21 if r.Method == http.MethodPost {
22 approve := FormInt(r, "approve")
23 if approve > 0 {
24 boardID := FormInt(r, "board")
25 if boardID > 0 {
26 b := db.BoardByID(boardID)
27 if b != nil {
28 post := db.PostByID(approve)
29 if post != nil {
30 rebuild := post.Moderated == ModeratedHidden
31
32 db.ModeratePost(post.ID, ModeratedApproved)
33 db.DeleteReports(post)
34
35 if rebuild {
36 db.BumpThread(post.Thread(), time.Now().Unix())
37 s.rebuildThread(db, post)
38 s.queueNotifications(db, post)
39 }
40 }
41 }
42 }
43 }
44
45 data.Redirect(w, r, "/sriracha/")
46 return
47 }
48
49 buf := &bytes.Buffer{}
50 data.Template = "manage_status"
51
52
53 if r.URL.Query().Has("remoteAddress") {
54 if data.forbidden(w, RoleSuperAdmin) {
55 return
56 }
57 data.Info = "Remote address: " + s.requestIP(r)
58 }
59
60
61 if r.URL.Query().Has("rebuildNameblocks") {
62 if data.forbidden(w, RoleSuperAdmin) {
63 return
64 }
65 for _, b := range db.AllBoards() {
66 for _, threadInfo := range db.AllThreads(b, false) {
67 for _, p := range db.AllPostsInThread(threadInfo[0], false) {
68 var capcode string
69 if strings.Contains(p.NameBlock, `<span style="color: red`) {
70 capcode = "Mod"
71 } else if strings.Contains(p.NameBlock, `<span style="color: purple`) {
72 capcode = "Admin"
73 }
74 p.SetNameBlock(p.Board.DefaultName, capcode, s.opt.Identifiers)
75
76 db.UpdatePostNameblock(p.ID, p.NameBlock)
77 }
78 }
79 s.rebuildBoard(db, b)
80 }
81 data.Info = "Rebuilt nameblocks"
82 }
83
84
85 if r.URL.Query().Has("scanFiles") {
86 if data.forbidden(w, RoleSuperAdmin) {
87 return
88 }
89 data.Template = "manage_info"
90 data.Message = `<h2 class="managetitle">Scan Files</h2>`
91 var scanned int
92 var found []string
93 checkBoardDir := func(b *Board, dir string) {
94 boardDir := filepath.Join(s.config.Root, b.Dir)
95 checkDir := filepath.Join(boardDir, dir)
96 err := filepath.WalkDir(checkDir, func(path string, d fs.DirEntry, err error) error {
97 if err != nil {
98 return err
99 } else if d.IsDir() {
100 return nil
101 }
102 scanned++
103 if filepath.Dir(path) != checkDir {
104 found = append(found, path)
105 return nil
106 }
107 fieldName := "file"
108 if dir == "thumb" {
109 fieldName = "thumb"
110 }
111 post := db.PostByField(b, fieldName, filepath.Base(path))
112 if post == nil {
113 found = append(found, path)
114 }
115 return nil
116 })
117 if err != nil {
118 log.Fatalf("failed to scan directory %s: %s", checkDir, err)
119 }
120 }
121 for _, b := range db.AllBoards() {
122 checkBoardDir(b, "src")
123 checkBoardDir(b, "thumb")
124 resDir := filepath.Join(s.config.Root, b.Dir, "res")
125 err := filepath.WalkDir(resDir, func(path string, d fs.DirEntry, err error) error {
126 if err != nil {
127 return err
128 } else if d.IsDir() {
129 return nil
130 }
131 scanned++
132 if filepath.Dir(path) != resDir || !strings.HasSuffix(path, ".html") {
133 found = append(found, path)
134 return nil
135 }
136 id, err := strconv.Atoi(strings.TrimSuffix(filepath.Base(path), ".html"))
137 if err != nil || id <= 0 {
138 found = append(found, path)
139 return nil
140 }
141 post := db.PostByID(id)
142 if post == nil || post.Parent != 0 {
143 found = append(found, path)
144 }
145 return nil
146 })
147 if err != nil {
148 log.Fatalf("failed to scan directory %s: %s", resDir, err)
149 }
150 }
151 data.Message += template.HTML(fmt.Sprintf(" Scanned %d files", scanned))
152 if len(found) == 0 {
153 data.Message += template.HTML(" and only found expected files.")
154 } else {
155 data.Message += template.HTML(fmt.Sprintf(" and found %d unexpected files:<ul>", len(found)))
156 for _, filePath := range found {
157 relativePath := strings.TrimPrefix(filePath, s.config.Root)
158 data.Message += template.HTML(fmt.Sprintf(`<li><a href="%s">%s</a></li>`, relativePath, relativePath))
159 }
160 data.Message += template.HTML("</ul><fieldset><legend>Remove unexpected files</legend><textarea style=\"width: 500px;height: 200px;\">")
161 for i, filePath := range found {
162 if i != 0 {
163 data.Message += template.HTML("\n")
164 }
165 relativePath := strings.TrimPrefix(filePath, s.config.Root)
166 data.Message += template.HTML(fmt.Sprintf("rm '%s'", relativePath[1:]))
167 }
168 data.Message += template.HTML("</textarea></fieldset><br>")
169 }
170 return
171 }
172
173 reports := db.AllReports()
174 for i, report := range reports {
175 if i > 0 {
176 buf.WriteString("<hr>\n")
177 }
178
179 d := s.buildData(db, w, r)
180 d.Template = "manage_status_item"
181 d.Board = report.Post.Board
182 d.Post = report.Post
183 d.Threads = [][]*Post{{report.Post}}
184 d.Manage.Report = report
185 d.execute(buf)
186 }
187 data.Message = template.HTML(buf.String())
188
189 buf.Reset()
190 pending := db.PendingPosts()
191 for i, post := range pending {
192 if i > 0 {
193 buf.WriteString("<hr>\n")
194 }
195
196 d := s.buildData(db, w, r)
197 d.Template = "manage_status_item"
198 d.Board = post.Board
199 d.Post = post
200 d.Threads = [][]*Post{{post}}
201 d.execute(buf)
202 }
203 data.Message2 = template.HTML(buf.String())
204 }
205
View as plain text