...

Source file src/codeberg.org/tslocum/sriracha/internal/server/server_status.go

Documentation: codeberg.org/tslocum/sriracha/internal/server

     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  	// Allow super-administrators to verify remote address resolution.
    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  	// Allow super-administrators to rebuild post nameblocks.
    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  	// Allow super-administrators to scan for unexpected files.
    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("&nbsp; 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