You've already forked nyx
mirror of
https://github.com/rls-moe/nyx
synced 2025-10-13 12:04:15 +00:00
MVP, no mod tools or anything but it works
This commit is contained in:
119
http/admin/handler.go
Normal file
119
http/admin/handler.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/GeertJohan/go.rice"
|
||||
"github.com/icza/session"
|
||||
"github.com/pressly/chi"
|
||||
"github.com/tidwall/buntdb"
|
||||
"go.rls.moe/nyx/http/errw"
|
||||
"go.rls.moe/nyx/http/middle"
|
||||
"go.rls.moe/nyx/resources"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
var riceConf = rice.Config{
|
||||
LocateOrder: []rice.LocateMethod{
|
||||
rice.LocateWorkingDirectory,
|
||||
rice.LocateEmbedded,
|
||||
rice.LocateAppended,
|
||||
},
|
||||
}
|
||||
|
||||
var box = riceConf.MustFindBox("http/admin/res/")
|
||||
|
||||
var (
|
||||
panelTmpl = template.New("admin/panel")
|
||||
loginTmpl = template.New("admin/login")
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
panelTmpl, err = panelTmpl.Parse(box.MustString("panel.html"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
loginTmpl, err = loginTmpl.Parse(box.MustString("index.html"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Router sets up the Administration Panel
|
||||
// It **must** be setup on the /admin/ basepath
|
||||
func Router(r chi.Router) {
|
||||
r.Get("/", serveLogin)
|
||||
r.Get("/index.html", serveLogin)
|
||||
r.Get("/panel.html", servePanel)
|
||||
r.Post("/new_board.sh", handleNewBoard)
|
||||
r.Post("/login.sh", handleLogin)
|
||||
r.Post("/logout.sh", handleLogout)
|
||||
}
|
||||
|
||||
func serveLogin(w http.ResponseWriter, r *http.Request) {
|
||||
dat := bytes.NewBuffer([]byte{})
|
||||
err := loginTmpl.Execute(dat, middle.GetBaseCtx(r))
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
http.ServeContent(w, r, "index.html", time.Now(),
|
||||
bytes.NewReader(dat.Bytes()))
|
||||
}
|
||||
|
||||
func servePanel(w http.ResponseWriter, r *http.Request) {
|
||||
sess := middle.GetSession(r)
|
||||
if sess == nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Unauthorized"))
|
||||
return
|
||||
}
|
||||
dat := bytes.NewBuffer([]byte{})
|
||||
err := panelTmpl.Execute(dat, middle.GetBaseCtx(r))
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
http.ServeContent(w, r, "panel.html", time.Now(),
|
||||
bytes.NewReader(dat.Bytes()))
|
||||
}
|
||||
|
||||
func handleLogout(w http.ResponseWriter, r *http.Request) {
|
||||
sess := middle.GetSession(r)
|
||||
if sess == nil {
|
||||
http.Redirect(w, r, "/admin/index.html", http.StatusSeeOther)
|
||||
}
|
||||
session.Remove(sess, w)
|
||||
http.Redirect(w, r, "/admin/index.html", http.StatusSeeOther)
|
||||
}
|
||||
func handleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
}
|
||||
db := middle.GetDB(r)
|
||||
|
||||
var admin = &resources.AdminPass{}
|
||||
err = db.View(func(tx *buntdb.Tx) error {
|
||||
var err error
|
||||
admin, err = resources.GetAdmin(tx, r.FormValue("id"))
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
err = errw.MakeErrorWithTitle("Access Denied", "User or Password Invalid")
|
||||
errw.ErrorWriter(err, w, r)
|
||||
}
|
||||
err = admin.VerifyLogin(r.FormValue("pass"))
|
||||
if err != nil {
|
||||
err = errw.MakeErrorWithTitle("Access Denied", "User or Password Invalid")
|
||||
errw.ErrorWriter(err, w, r)
|
||||
}
|
||||
sess := session.NewSessionOptions(&session.SessOptions{
|
||||
CAttrs: map[string]interface{}{"mode": "admin"},
|
||||
})
|
||||
session.Add(sess, w)
|
||||
|
||||
http.Redirect(w, r, "/admin/panel.html", http.StatusSeeOther)
|
||||
}
|
58
http/admin/newboard.go
Normal file
58
http/admin/newboard.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/tidwall/buntdb"
|
||||
"go.rls.moe/nyx/http/errw"
|
||||
"go.rls.moe/nyx/http/middle"
|
||||
"go.rls.moe/nyx/resources"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func handleNewBoard(w http.ResponseWriter, r *http.Request) {
|
||||
sess := middle.GetSession(r)
|
||||
if sess == nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Unauthorized"))
|
||||
return
|
||||
}
|
||||
if sess.CAttr("mode") != "admin" {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Unauthorized"))
|
||||
return
|
||||
}
|
||||
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
}
|
||||
db := middle.GetDB(r)
|
||||
|
||||
var board = &resources.Board{}
|
||||
|
||||
board.ShortName = r.FormValue("shortname")
|
||||
board.LongName = r.FormValue("longname")
|
||||
|
||||
if board.ShortName == "" {
|
||||
errw.ErrorWriter(errors.New("Need shortname"), w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if board.ShortName == "admin" && board.ShortName == "@" {
|
||||
errw.ErrorWriter(errors.New("No"), w, r)
|
||||
}
|
||||
|
||||
if board.LongName == "" && len(board.LongName) < 5 {
|
||||
errw.ErrorWriter(errors.New("Need 5 characters for long name"), w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if err = db.Update(func(tx *buntdb.Tx) error {
|
||||
return resources.NewBoard(tx, r.Host, board)
|
||||
}); err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/admin/panel.html", http.StatusSeeOther)
|
||||
}
|
41
http/admin/res/index.html
Normal file
41
http/admin/res/index.html
Normal file
@@ -0,0 +1,41 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.Config.Site.Title}} Admin Login</title>
|
||||
<link rel="stylesheet" href="/@/style.css">
|
||||
<link rel="stylesheet" href="/@/custom.css">
|
||||
<link rel="stylesheet" href="/@/admin.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin login">
|
||||
<form action="/admin/login.sh" method="POST">
|
||||
<input
|
||||
type="hidden"
|
||||
name="csrf_token"
|
||||
value="{{ .CSRFToken }}" />
|
||||
<div class="admin form row">
|
||||
<input
|
||||
class="admin form input"
|
||||
type="text"
|
||||
name="id"
|
||||
placeholder="admin id"
|
||||
minlength="3"/>
|
||||
</div>
|
||||
<div class="admin form row">
|
||||
<input
|
||||
class="admin form input"
|
||||
type="password"
|
||||
name="pass"
|
||||
placeholder="password"
|
||||
minlength="3"/>
|
||||
</div>
|
||||
<div class="admin form row">
|
||||
<input class="admin form input halfsize" type="submit" value="Login"/>
|
||||
<input class="admin form input halfsize" type="reset" value="Reset"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
57
http/admin/res/panel.html
Normal file
57
http/admin/res/panel.html
Normal file
@@ -0,0 +1,57 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.Config.Site.Title}} Admin Panel</title>
|
||||
<link rel="stylesheet" href="/@/style.css">
|
||||
<link rel="stylesheet" href="/@/custom.css">
|
||||
<link rel="stylesheet" href="/@/admin.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="welcome">
|
||||
Welcome {{.Admin.Id}}<br>
|
||||
<form class="form logout" method="POST" action="/admin/logout.sh">
|
||||
<input
|
||||
type="hidden"
|
||||
name="csrf_token"
|
||||
value="{{ .CSRFToken }}" />
|
||||
<input type="submit" value="Logout" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel boardmgr">
|
||||
<form method="POST" action="/admin/new_board.sh">
|
||||
<input
|
||||
type="hidden"
|
||||
name="csrf_token"
|
||||
value="{{ .CSRFToken }}" />
|
||||
<input type="text" placeholder="shortname" name="shortname"/>
|
||||
<input type="text" placeholder="longname" name="longname"/>
|
||||
<input type="submit" value="Create Board" />
|
||||
<input type="reset" value="Reset" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel remover post">
|
||||
<form method="POST" action="/admin/rem_post.sh">
|
||||
<input
|
||||
type="hidden"
|
||||
name="csrf_token"
|
||||
value="{{ .CSRFToken }}" />
|
||||
<input type="text" placeholder="post id" name="post id"/>
|
||||
<input type="submit" value="Remove Post" />
|
||||
<input type="reset" value="Reset" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel remover thread">
|
||||
<form method="POST" action="/admin/rem_thread.sh">
|
||||
<input
|
||||
type="hidden"
|
||||
name="csrf_token"
|
||||
value="{{ .CSRFToken }}" />
|
||||
<input type="text" placeholder="thread id" name="thread id"/>
|
||||
<input type="submit" value="Remove thread" />
|
||||
<input type="reset" value="Reset" />
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
52
http/board/board.go
Normal file
52
http/board/board.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/pressly/chi"
|
||||
"github.com/tidwall/buntdb"
|
||||
"go.rls.moe/nyx/http/errw"
|
||||
"go.rls.moe/nyx/http/middle"
|
||||
"go.rls.moe/nyx/resources"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func serveBoard(w http.ResponseWriter, r *http.Request) {
|
||||
dat := bytes.NewBuffer([]byte{})
|
||||
db := middle.GetDB(r)
|
||||
ctx := middle.GetBaseCtx(r)
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
bName := chi.URLParam(r, "board")
|
||||
b, err := resources.GetBoard(tx, r.Host, bName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx["Board"] = b
|
||||
|
||||
threads, err := resources.ListThreads(tx, r.Host, bName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("Number of Thread on board: ", len(threads))
|
||||
|
||||
for k := range threads {
|
||||
err := resources.FillReplies(tx, r.Host, threads[k])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ctx["Threads"] = threads
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
err = boardTmpl.Execute(dat, ctx)
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
http.ServeContent(w, r, "board.html", time.Now(), bytes.NewReader(dat.Bytes()))
|
||||
}
|
88
http/board/handler.go
Normal file
88
http/board/handler.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/GeertJohan/go.rice"
|
||||
"github.com/pressly/chi"
|
||||
"github.com/tidwall/buntdb"
|
||||
"go.rls.moe/nyx/http/errw"
|
||||
"go.rls.moe/nyx/http/middle"
|
||||
"go.rls.moe/nyx/resources"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
var riceConf = rice.Config{
|
||||
LocateOrder: []rice.LocateMethod{
|
||||
rice.LocateWorkingDirectory,
|
||||
rice.LocateEmbedded,
|
||||
rice.LocateAppended,
|
||||
},
|
||||
}
|
||||
|
||||
var box = riceConf.MustFindBox("http/board/res/")
|
||||
|
||||
var (
|
||||
dirTmpl = template.New("board/dir")
|
||||
boardTmpl = template.New("board/board")
|
||||
threadTmpl = template.New("board/thread")
|
||||
|
||||
hdlFMap = template.FuncMap{
|
||||
"renderText": resources.OperateReplyText,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
dirTmpl, err = dirTmpl.Parse(box.MustString("dir.html"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
boardTmpl, err = boardTmpl.Funcs(hdlFMap).Parse(box.MustString("board.html"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
threadTmpl, err = threadTmpl.Funcs(hdlFMap).Parse(box.MustString("thread.html"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Router(r chi.Router) {
|
||||
r.Get("/", serveDir)
|
||||
r.Get("/dir.html", serveDir)
|
||||
r.Get("/:board/board.html", serveBoard)
|
||||
r.Post("/:board/new_thread.sh", handleNewThread)
|
||||
r.Get("/:board/:thread/thread.html", serveThread)
|
||||
r.Get("/:board/:thread/:post/post.html", servePost)
|
||||
r.Post("/:board/:thread/reply.sh", handleNewReply)
|
||||
}
|
||||
|
||||
func servePost(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
func serveDir(w http.ResponseWriter, r *http.Request) {
|
||||
dat := bytes.NewBuffer([]byte{})
|
||||
db := middle.GetDB(r)
|
||||
ctx := middle.GetBaseCtx(r)
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
bList, err := resources.ListBoards(tx, r.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx["Boards"] = bList
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
err = dirTmpl.Execute(dat, ctx)
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
http.ServeContent(w, r, "dir.html", time.Now(), bytes.NewReader(dat.Bytes()))
|
||||
}
|
59
http/board/newreply.go
Normal file
59
http/board/newreply.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pressly/chi"
|
||||
"github.com/tidwall/buntdb"
|
||||
"go.rls.moe/nyx/http/errw"
|
||||
"go.rls.moe/nyx/http/middle"
|
||||
"go.rls.moe/nyx/resources"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func handleNewReply(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
var reply = &resources.Reply{}
|
||||
|
||||
reply.Board = chi.URLParam(r, "board")
|
||||
tid, err := strconv.Atoi(chi.URLParam(r, "thread"))
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
reply.Thread = int64(tid)
|
||||
reply.Text = r.FormValue("text")
|
||||
if len(reply.Text) > 1000 {
|
||||
errw.ErrorWriter(errw.MakeErrorWithTitle("I'm sorry but I can't do that", "These are too many characters"), w, r)
|
||||
return
|
||||
}
|
||||
if len(reply.Text) < 10 {
|
||||
errw.ErrorWriter(errw.MakeErrorWithTitle("I'm sorry but I can't do that", "These are not enough characters"), w, r)
|
||||
return
|
||||
}
|
||||
reply.Metadata = map[string]string{}
|
||||
if r.FormValue("tripcode") != "" {
|
||||
reply.Metadata["trip"] = resources.CalcTripCode(r.FormValue("tripcode"))
|
||||
} else {
|
||||
reply.Metadata["trip"] = "Anonymous"
|
||||
}
|
||||
|
||||
db := middle.GetDB(r)
|
||||
if err = db.Update(func(tx *buntdb.Tx) error {
|
||||
thread, err := resources.GetThread(tx, r.Host, reply.Board, reply.Thread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return resources.NewReply(tx, r.Host, reply.Board, thread, reply, false)
|
||||
}); err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, fmt.Sprintf("/%s/%d/thread.html", chi.URLParam(r, "board"), reply.Thread), http.StatusSeeOther)
|
||||
}
|
48
http/board/newthread.go
Normal file
48
http/board/newthread.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pressly/chi"
|
||||
"github.com/tidwall/buntdb"
|
||||
"go.rls.moe/nyx/http/errw"
|
||||
"go.rls.moe/nyx/http/middle"
|
||||
"go.rls.moe/nyx/resources"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func handleNewThread(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
var thread = &resources.Thread{}
|
||||
var mainReply = &resources.Reply{}
|
||||
|
||||
mainReply.Board = chi.URLParam(r, "board")
|
||||
thread.Board = chi.URLParam(r, "board")
|
||||
mainReply.Text = r.FormValue("text")
|
||||
if len(mainReply.Text) > 1000 {
|
||||
errw.ErrorWriter(errw.MakeErrorWithTitle("I'm sorry but I can't do that", "These are too many characters"), w, r)
|
||||
return
|
||||
}
|
||||
if len(mainReply.Text) < 10 {
|
||||
errw.ErrorWriter(errw.MakeErrorWithTitle("I'm sorry but I can't do that", "These are not enough characters"), w, r)
|
||||
return
|
||||
}
|
||||
mainReply.Metadata = map[string]string{}
|
||||
if r.FormValue("tripcode") != "" {
|
||||
mainReply.Metadata["trip"] = resources.CalcTripCode(r.FormValue("tripcode"))
|
||||
}
|
||||
|
||||
db := middle.GetDB(r)
|
||||
if err = db.Update(func(tx *buntdb.Tx) error {
|
||||
return resources.NewThread(tx, r.Host, mainReply.Board, thread, mainReply)
|
||||
}); err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, fmt.Sprintf("/%s/%d/thread.html", chi.URLParam(r, "board"), thread.ID), http.StatusSeeOther)
|
||||
}
|
119
http/board/res/board.html
Normal file
119
http/board/res/board.html
Normal file
@@ -0,0 +1,119 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.Config.Site.Title}} - /{{.Board.ShortName}}/</title>
|
||||
<link rel="stylesheet" href="/@/style.css">
|
||||
<link rel="stylesheet" href="/@/custom.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="banner logo">
|
||||
<div class="site title"><h1><span class="reflink"><a href="/{{.Board.ShortName}}/board.html">/{{.Board.ShortName}}/</a></span></h1></div>
|
||||
<div class="site description"><h2>{{.Board.LongName}}</h2></div>
|
||||
</div>
|
||||
{{ $boardlink := .Board.ShortName }}
|
||||
<div class="postarea">
|
||||
<form id="postform" action="/{{$boardlink}}/new_thread.sh" method="POST">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
TripCode
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="tripcode" size=48 placeholder="Anonymous"/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="csrf_token"
|
||||
value="{{ .CSRFToken }}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
Comment
|
||||
</td>
|
||||
<td>
|
||||
<textarea
|
||||
name="text"
|
||||
placeholder="your comment"
|
||||
rows="4"
|
||||
cols="48"
|
||||
minlength="10"
|
||||
required
|
||||
></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
Image File
|
||||
</td>
|
||||
<td>
|
||||
<input type="file" name="image" />
|
||||
</td>
|
||||
</tr>
|
||||
{{ if ne .Config.Captcha.Mode "disabled" }}
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
Captcha
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="captcha" size=48 />
|
||||
<input type="hidden"
|
||||
value="{{.CaptchaToken}}"/>
|
||||
<img alt=""
|
||||
src="{{.CaptchaImage}}" />
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<input type="submit" value="Post" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="postlists">
|
||||
{{range .Threads}}
|
||||
{{ $threadrid := .GetReply.ID }}
|
||||
<label><span class="postertrip">
|
||||
{{ if .GetReply.Metadata.trip }}
|
||||
{{.GetReply.Metadata.trip}}
|
||||
{{ else }}
|
||||
Anonymous
|
||||
{{ end }}
|
||||
</span></label>
|
||||
<span class="reflink"><a href="/{{$boardlink}}/{{.ID}}/thread.html">No.{{.ID}}</a></span>
|
||||
<blockquote><blockquote>
|
||||
{{ renderText .GetReply.Text}}
|
||||
</blockquote></blockquote>
|
||||
{{range .GetReplies}}
|
||||
{{ if ne .ID $threadrid }}
|
||||
<table><tbody><tr><td class="doubledash">>></td>
|
||||
<td class="reply" id="reply{{.ID}}">
|
||||
<label><span class="postertrip">
|
||||
{{ if .Metadata.trip }}
|
||||
{{.Metadata.trip}}
|
||||
{{ else }}
|
||||
Anonymous
|
||||
{{ end }}
|
||||
</span></label>
|
||||
<span class="reflink"><a href="/{{$boardlink}}/{{.Thread}}/thread.html">No.{{.ID}}</a></span>
|
||||
<blockquote><blockquote>
|
||||
{{ renderText .Text}}
|
||||
</blockquote></blockquote>
|
||||
</td>
|
||||
</tr></tbody></table>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<br clear="left" /><hr />
|
||||
{{end}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
30
http/board/res/dir.html
Normal file
30
http/board/res/dir.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.Config.Site.Title}} Boards</title>
|
||||
<link rel="stylesheet" href="/@/style.css">
|
||||
<link rel="stylesheet" href="/@/custom.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="banner logo">
|
||||
<div class="site title"><h1>{{.Config.Site.Title}}</h1></div>
|
||||
<div class="site description"><h2>{{.Config.Site.Description}}</h2></div>
|
||||
</div>
|
||||
<div class="boardlist">
|
||||
<div class="boardtitle">
|
||||
<h3>Boards</h3>
|
||||
</div>
|
||||
<div class="boardlist">
|
||||
<ul>
|
||||
{{range .Boards}}
|
||||
<li>
|
||||
<a class="boardlink" href="/{{ .ShortName}}/board.html">{{.ShortName}}: {{.LongName}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
129
http/board/res/thread.html
Normal file
129
http/board/res/thread.html
Normal file
@@ -0,0 +1,129 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.Config.Site.Title}} - /{{.Board.ShortName}}/</title>
|
||||
<link rel="stylesheet" href="/@/style.css">
|
||||
<link rel="stylesheet" href="/@/custom.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="banner logo">
|
||||
<div class="site title"><h1><span class="reflink"><a href="/{{.Board.ShortName}}/board.html">/{{.Board.ShortName}}/</a></span></h1></div>
|
||||
<div class="site description"><h2>{{.Board.LongName}}</h2></div>
|
||||
</div>
|
||||
{{ $boardlink := .Board.ShortName }}
|
||||
<hr />
|
||||
<div class="postarea">
|
||||
<form id="postform" action="/{{.Board.ShortName}}/{{.Thread.ID}}/reply.sh" method="POST">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
TripCode
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="tripcode" size=48 placeholder="Anonymous"/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="csrf_token"
|
||||
value="{{ .CSRFToken }}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
Comment
|
||||
</td>
|
||||
<td>
|
||||
<textarea
|
||||
name="text"
|
||||
placeholder="your comment"
|
||||
rows="4"
|
||||
cols="48"
|
||||
minlength="10"
|
||||
required
|
||||
></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
Image File
|
||||
</td>
|
||||
<td>
|
||||
<input type="file" name="image" />
|
||||
</td>
|
||||
</tr>
|
||||
{{ if ne .Config.Captcha.Mode "disabled" }}
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
Captcha
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="captcha" size=48 />
|
||||
<input type="hidden"
|
||||
value="{{.CaptchaToken}}"/>
|
||||
<img alt=""
|
||||
src="{{.CaptchaImage}}" />
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<input type="submit" value="Post" />
|
||||
</td>
|
||||
</tr>
|
||||
{{ if .Board.Metadata.rules }}
|
||||
<tr>
|
||||
<td class="postblock">
|
||||
Rules
|
||||
</td>
|
||||
<td>
|
||||
{{ .Board.Metadata.rules }}
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
<div class="postlists">
|
||||
{{with .Thread }}
|
||||
{{ $threadrid := .GetReply.ID }}
|
||||
<label><span class="postertrip">
|
||||
{{ if .GetReply.Metadata.trip }}
|
||||
{{.GetReply.Metadata.trip}}
|
||||
{{ else }}
|
||||
Anonymous
|
||||
{{ end }}
|
||||
</span></label>
|
||||
<span class="reflink"><a href="/{{$boardlink}}/{{.ID}}/thread.html">No.{{.ID}}</a></span>
|
||||
<blockquote><blockquote>
|
||||
{{ renderText .GetReply.Text}}
|
||||
</blockquote></blockquote>
|
||||
{{range .GetReplies}}
|
||||
{{ if ne .ID $threadrid }}
|
||||
<table><tbody><tr><td class="doubledash">>></td>
|
||||
<td class="reply" id="reply{{.ID}}">
|
||||
<label><span class="postertrip">
|
||||
{{ if .Metadata.trip }}
|
||||
{{.Metadata.trip}}
|
||||
{{ else }}
|
||||
Anonymous
|
||||
{{ end }}
|
||||
</span></label>
|
||||
<span class="reflink"><a href="/{{$boardlink}}/{{.Thread}}/thread.html">No.{{.ID}}</a></span>
|
||||
<blockquote><blockquote>
|
||||
{{ renderText .Text}}
|
||||
</blockquote></blockquote>
|
||||
</td>
|
||||
</tr></tbody></table>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<br clear="left" /><hr />
|
||||
{{end}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
57
http/board/thread.go
Normal file
57
http/board/thread.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/pressly/chi"
|
||||
"github.com/tidwall/buntdb"
|
||||
"go.rls.moe/nyx/http/errw"
|
||||
"go.rls.moe/nyx/http/middle"
|
||||
"go.rls.moe/nyx/resources"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func serveThread(w http.ResponseWriter, r *http.Request) {
|
||||
dat := bytes.NewBuffer([]byte{})
|
||||
db := middle.GetDB(r)
|
||||
ctx := middle.GetBaseCtx(r)
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
bName := chi.URLParam(r, "board")
|
||||
b, err := resources.GetBoard(tx, r.Host, bName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx["Board"] = b
|
||||
|
||||
id, err := strconv.Atoi(chi.URLParam(r, "thread"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
thread, err := resources.GetThread(tx, r.Host, bName, int64(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = resources.FillReplies(tx, r.Host, thread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx["Thread"] = thread
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
err = threadTmpl.Execute(dat, ctx)
|
||||
if err != nil {
|
||||
errw.ErrorWriter(err, w, r)
|
||||
return
|
||||
}
|
||||
http.ServeContent(w, r, "board.html", time.Now(), bytes.NewReader(dat.Bytes()))
|
||||
}
|
77
http/errw/handler.go
Normal file
77
http/errw/handler.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package errw
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/GeertJohan/go.rice"
|
||||
"github.com/pressly/chi/middleware"
|
||||
"go.rls.moe/nyx/http/middle"
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var riceConf = rice.Config{
|
||||
LocateOrder: []rice.LocateMethod{
|
||||
rice.LocateWorkingDirectory,
|
||||
rice.LocateEmbedded,
|
||||
rice.LocateAppended,
|
||||
},
|
||||
}
|
||||
|
||||
var box = riceConf.MustFindBox("http/errw/res/")
|
||||
|
||||
var (
|
||||
errorTmpl = template.New("errw/error")
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
errorTmpl, err = errorTmpl.Parse(box.MustString("error.html"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type ErrorWithTitle interface {
|
||||
error
|
||||
ErrorTitle() string
|
||||
}
|
||||
|
||||
type errorWTInt struct {
|
||||
message, title string
|
||||
}
|
||||
|
||||
func (e errorWTInt) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (e errorWTInt) ErrorTitle() string {
|
||||
return e.title
|
||||
}
|
||||
|
||||
func MakeErrorWithTitle(title, message string) ErrorWithTitle {
|
||||
return errorWTInt{message, title}
|
||||
}
|
||||
|
||||
func ErrorWriter(err error, w http.ResponseWriter, r *http.Request) {
|
||||
ctx := middle.GetBaseCtx(r)
|
||||
|
||||
if err == nil {
|
||||
ErrorWriter(errors.New("Unknonw Error"), w, r)
|
||||
}
|
||||
|
||||
if errWT, ok := err.(ErrorWithTitle); ok {
|
||||
ctx["Error"] = map[string]string{
|
||||
"Code": middleware.GetReqID(r.Context()),
|
||||
"Description": errWT.Error(),
|
||||
"Title": errWT.ErrorTitle(),
|
||||
}
|
||||
} else {
|
||||
ctx["Error"] = map[string]string{
|
||||
"Code": middleware.GetReqID(r.Context()),
|
||||
"Description": err.Error(),
|
||||
"Title": "Error",
|
||||
}
|
||||
}
|
||||
errorTmpl.Execute(w, ctx)
|
||||
return
|
||||
}
|
35
http/errw/res/error.html
Normal file
35
http/errw/res/error.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.Config.Site.Title}} Admin Login</title>
|
||||
<style>
|
||||
div.error {
|
||||
border: 1px solid black;
|
||||
width: 500px;
|
||||
margin: auto;
|
||||
margin-top: 100px;
|
||||
}
|
||||
div.error h1 {
|
||||
margin-bottom: 0px;
|
||||
text-align: center;
|
||||
}
|
||||
div.error h2 {
|
||||
text-align: center;
|
||||
}
|
||||
div.error h3 {
|
||||
margin-top: 0px;
|
||||
text-align: center;
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error">
|
||||
<h1>{{.Error.Title}}</h1><br/>
|
||||
<h3>{{.Error.Code}}</h3><br/>
|
||||
<h2>{{.Error.Description}}</h2>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
21
http/middle/base.go
Normal file
21
http/middle/base.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"github.com/justinas/nosurf"
|
||||
"github.com/pressly/chi/middleware"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetBaseCtx(r *http.Request) map[string]interface{} {
|
||||
val := map[string]interface{}{
|
||||
"Config": GetConfig(r),
|
||||
"ReqID": middleware.GetReqID(r.Context()),
|
||||
"CSRFToken": nosurf.Token(r),
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
func CSRFProtect(next http.Handler) http.Handler {
|
||||
return nosurf.New(next)
|
||||
}
|
24
http/middle/config.go
Normal file
24
http/middle/config.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go.rls.moe/nyx/config"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func ConfigCtx(config *config.Config) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), configKey, config))
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func GetConfig(r *http.Request) *config.Config {
|
||||
val := r.Context().Value(configKey)
|
||||
if val == nil {
|
||||
panic("Config Middleware not configured")
|
||||
}
|
||||
return val.(*config.Config)
|
||||
}
|
9
http/middle/ctx.go
Normal file
9
http/middle/ctx.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package middle
|
||||
|
||||
type ctxKey int64
|
||||
|
||||
const (
|
||||
configKey ctxKey = iota
|
||||
dbCtxKey
|
||||
sessionKey
|
||||
)
|
34
http/middle/db.go
Normal file
34
http/middle/db.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/tidwall/buntdb"
|
||||
"go.rls.moe/nyx/config"
|
||||
"go.rls.moe/nyx/resources"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Database(c *config.Config) (func(http.Handler) http.Handler, error) {
|
||||
db, err := buntdb.Open(c.DB.File)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = resources.InitialSetup(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r = r.WithContext(context.WithValue(r.Context(),
|
||||
dbCtxKey, db))
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetDB(r *http.Request) *buntdb.DB {
|
||||
val := r.Context().Value(dbCtxKey)
|
||||
if val == nil {
|
||||
panic("DB Middleware not configured")
|
||||
}
|
||||
return val.(*buntdb.DB)
|
||||
}
|
15
http/middle/session.go
Normal file
15
http/middle/session.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package middle
|
||||
|
||||
import (
|
||||
"github.com/icza/session"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func init() {
|
||||
session.Global.Close()
|
||||
session.Global = session.NewCookieManager(session.NewInMemStore())
|
||||
}
|
||||
|
||||
func GetSession(r *http.Request) session.Session {
|
||||
return session.Get(r)
|
||||
}
|
26
http/res/admin.css
Normal file
26
http/res/admin.css
Normal file
@@ -0,0 +1,26 @@
|
||||
/* CUSTOM CSS */
|
||||
div.admin.login {
|
||||
border: 1px solid black;
|
||||
width: 500px;
|
||||
margin: auto;
|
||||
margin-top: 100px;
|
||||
}
|
||||
.admin.form.row {
|
||||
margin: auto;
|
||||
padding: 5px;
|
||||
width: 90%;
|
||||
height: 22px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
}
|
||||
.admin.form.input {
|
||||
font-family: "monospace";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 2px;
|
||||
display: inline;
|
||||
}
|
||||
.admin.form.input.halfsize {
|
||||
width: 50%;
|
||||
}
|
21
http/res/custom.css
Normal file
21
http/res/custom.css
Normal file
@@ -0,0 +1,21 @@
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
div {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
blockquote blockquote { max-width: 80%; word-wrap: break-word; white-space: normal; }
|
||||
|
||||
.reply blockquote, blockquote :last-child { max-width: 80%; word-wrap: break-word; white-space: normal; }
|
157
http/res/style.css
Normal file
157
http/res/style.css
Normal file
@@ -0,0 +1,157 @@
|
||||
/* The following CSS is mostly taken from Wakaba, big thanks for the devs there! <3 */
|
||||
|
||||
html, body {
|
||||
background:#FFFFEE;
|
||||
color:#800000;
|
||||
}
|
||||
a {
|
||||
color:#0000EE;
|
||||
}
|
||||
a:hover {
|
||||
color:#DD0000;
|
||||
}
|
||||
.adminbar {
|
||||
text-align:right;
|
||||
clear:both;
|
||||
float:right;
|
||||
}
|
||||
.logo {
|
||||
clear:both;
|
||||
text-align:center;
|
||||
font-size:2em;
|
||||
color:#800000;
|
||||
width:100%;
|
||||
}
|
||||
.theader {
|
||||
background:#E04000;
|
||||
text-align:center;
|
||||
padding:2px;
|
||||
color:#FFFFFF;
|
||||
width:100%;
|
||||
}
|
||||
.postarea {
|
||||
}
|
||||
.rules {
|
||||
font-size:0.7em;
|
||||
}
|
||||
.postblock {
|
||||
background:#EEAA88;
|
||||
color:#800000;
|
||||
font-weight:800;
|
||||
}
|
||||
.footer {
|
||||
text-align:center;
|
||||
font-size:12px;
|
||||
font-family:serif;
|
||||
}
|
||||
.passvalid {
|
||||
background:#EEAA88;
|
||||
text-align:center;
|
||||
width:100%;
|
||||
color:#ffffff;
|
||||
}
|
||||
.dellist {
|
||||
font-weight: bold;
|
||||
text-align:center;
|
||||
}
|
||||
.delbuttons {
|
||||
text-align:center;
|
||||
padding-bottom:4px;
|
||||
|
||||
}
|
||||
.managehead {
|
||||
background:#AAAA66;
|
||||
color:#400000;
|
||||
padding:0px;
|
||||
}
|
||||
.postlists {
|
||||
background:#FFFFFF;
|
||||
width:100%;
|
||||
padding:0px;
|
||||
color:#800000;
|
||||
}
|
||||
.row1 {
|
||||
background:#EEEECC;
|
||||
color:#800000;
|
||||
}
|
||||
.row2 {
|
||||
background:#DDDDAA;
|
||||
color:#800000;
|
||||
}
|
||||
.unkfunc {
|
||||
background:inert;
|
||||
color:#789922;
|
||||
}
|
||||
.filesize {
|
||||
text-decoration:none;
|
||||
}
|
||||
.filetitle {
|
||||
background:inherit;
|
||||
font-size:1.2em;
|
||||
color:#CC1105;
|
||||
font-weight:800;
|
||||
}
|
||||
.postername {
|
||||
color:#117743;
|
||||
font-weight:bold;
|
||||
}
|
||||
.postertrip {
|
||||
color:#228854;
|
||||
}
|
||||
.oldpost {
|
||||
color:#CC1105;
|
||||
font-weight:800;
|
||||
}
|
||||
.omittedposts {
|
||||
color:#707070;
|
||||
}
|
||||
.reply {
|
||||
background:#F0E0D6;
|
||||
color:#800000;
|
||||
}
|
||||
.doubledash {
|
||||
vertical-align:top;
|
||||
clear:both;
|
||||
float:left;
|
||||
}
|
||||
.replytitle {
|
||||
font-size: 1.2em;
|
||||
color:#CC1105;
|
||||
font-weight:800;
|
||||
}
|
||||
.commentpostername {
|
||||
color:#117743;
|
||||
font-weight:800;
|
||||
}
|
||||
.thumbnailmsg {
|
||||
font-size: small;
|
||||
color:#800000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.abbrev {
|
||||
color:#707070;
|
||||
}
|
||||
.highlight {
|
||||
background:#F0E0D6;
|
||||
color:#800000;
|
||||
border: 2px dashed #EEAA88;
|
||||
}
|
||||
|
||||
/* From pl files */
|
||||
|
||||
/* futaba_style.pl */
|
||||
|
||||
blockquote blockquote { margin-left: 0em; }
|
||||
form { margin-bottom: 0px }
|
||||
form .trap { display:none }
|
||||
.postarea { text-align: center }
|
||||
.postarea table { margin: 0px auto; text-align: left }
|
||||
.thumb { border: none; float: left; margin: 2px 20px }
|
||||
.nothumb { float: left; background: #eee; border: 2px dashed #aaa; text-align: center; margin: 2px 20px; padding: 1em 0.5em 1em 0.5em; }
|
||||
.reply blockquote, blockquote :last-child { margin-bottom: 0em; }
|
||||
.reflink a { color: inherit; text-decoration: none }
|
||||
.reply .filesize { margin-left: 20px }
|
||||
.userdelete { float: right; text-align: center; white-space: nowrap }
|
||||
.replypage .replylink { display: none }
|
53
http/server.go
Normal file
53
http/server.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/GeertJohan/go.rice"
|
||||
"github.com/pressly/chi"
|
||||
"github.com/pressly/chi/middleware"
|
||||
"go.rls.moe/nyx/config"
|
||||
"go.rls.moe/nyx/http/admin"
|
||||
"go.rls.moe/nyx/http/board"
|
||||
"go.rls.moe/nyx/http/middle"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var riceConf = rice.Config{
|
||||
LocateOrder: []rice.LocateMethod{
|
||||
rice.LocateWorkingDirectory,
|
||||
rice.LocateEmbedded,
|
||||
rice.LocateAppended,
|
||||
},
|
||||
}
|
||||
|
||||
func Start(config *config.Config) {
|
||||
r := chi.NewRouter()
|
||||
|
||||
fmt.Println("Setting up Router")
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
r.Use(middleware.CloseNotify)
|
||||
r.Use(middleware.DefaultCompress)
|
||||
|
||||
r.Use(middle.ConfigCtx(config))
|
||||
|
||||
r.Use(middle.CSRFProtect)
|
||||
{
|
||||
mw, err := middle.Database(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r.Use(mw)
|
||||
}
|
||||
|
||||
r.Route("/admin/", admin.Router)
|
||||
{
|
||||
box := riceConf.MustFindBox("http/res")
|
||||
atFileServer := http.StripPrefix("/@/", http.FileServer(box.HTTPBox()))
|
||||
r.Mount("/@/", atFileServer)
|
||||
}
|
||||
r.Group(board.Router)
|
||||
|
||||
fmt.Println("Setup Complete, Starting Web Server...")
|
||||
http.ListenAndServe(config.ListenOn, r)
|
||||
}
|
Reference in New Issue
Block a user