1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-09-13 09:26:23 -07:00

Move Lua plugin loaders to their respective gochan packages

This commit is contained in:
Eggbertx 2023-10-24 16:27:27 -07:00
parent c9abd7a162
commit 9688082339
9 changed files with 543 additions and 472 deletions

32
pkg/config/preload.go Normal file
View file

@ -0,0 +1,32 @@
package config
import (
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
)
func PreloadModule(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"system_critical_config": func(l *lua.LState) int {
l.Push(luar.New(l, &cfg.SystemCriticalConfig))
return 1
},
"site_config": func(l *lua.LState) int {
l.Push(luar.New(l, &cfg.SiteConfig))
return 1
},
"board_config": func(l *lua.LState) int {
numArgs := l.GetTop()
board := ""
if numArgs > 0 {
board = l.CheckString(1)
}
l.Push(luar.New(l, GetBoardConfig(board)))
return 1
},
})
l.Push(t)
return 1
}

59
pkg/events/preload.go Normal file
View file

@ -0,0 +1,59 @@
package events
import (
"errors"
"github.com/gochan-org/gochan/pkg/gcplugin/luautil"
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
)
func luaEventRegisterHandlerAdapter(l *lua.LState, fn *lua.LFunction) EventHandler {
return func(trigger string, data ...interface{}) error {
args := []lua.LValue{
luar.New(l, trigger),
}
for _, i := range data {
args = append(args, luar.New(l, i))
}
l.CallByParam(lua.P{
Fn: fn,
NRet: 1,
// Protect: true,
}, args...)
errStr := lua.LVAsString(l.Get(-1))
if errStr != "" {
return errors.New(errStr)
}
return nil
}
}
func PreloadModule(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"register_event": func(l *lua.LState) int {
table := l.CheckTable(-2)
var triggers []string
table.ForEach(func(i, val lua.LValue) {
triggers = append(triggers, val.String())
})
fn := l.CheckFunction(-1)
RegisterEvent(triggers, luaEventRegisterHandlerAdapter(l, fn))
return 0
},
"trigger_event": func(l *lua.LState) int {
trigger := l.CheckString(1)
numArgs := l.GetTop()
var data []interface{}
for i := 2; i <= numArgs; i++ {
v := l.CheckAny(i)
data = append(data, luautil.LValueToInterface(l, v))
}
TriggerEvent(trigger, data...)
return 0
},
})
l.Push(t)
return 1
}

View file

@ -1,18 +1,11 @@
package gcplugin
import (
"database/sql"
"errors"
"fmt"
"html/template"
"io"
"net/http"
"path"
"plugin"
"reflect"
"time"
"github.com/Eggbertx/durationutil"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/events"
"github.com/gochan-org/gochan/pkg/gcsql"
@ -20,19 +13,12 @@ import (
"github.com/gochan-org/gochan/pkg/gcutil"
"github.com/gochan-org/gochan/pkg/manage"
"github.com/gochan-org/gochan/pkg/posting/uploads"
"github.com/gochan-org/gochan/pkg/server/serverutil"
"github.com/rs/zerolog"
gluahttp "github.com/vadv/gopher-lua-libs/http"
async "github.com/CuberL/glua-async"
luaFilePath "github.com/vadv/gopher-lua-libs/filepath"
luaStrings "github.com/vadv/gopher-lua-libs/strings"
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
)
const (
tableArgFmt = "invalid value for key %q passed to table, expected %s, got %s"
)
var (
@ -54,470 +40,19 @@ func ClosePlugins() {
}
}
type lvalueScanner struct {
val lua.LValue
state *lua.LState
}
func (lvs *lvalueScanner) Scan(src any) error {
typeof := reflect.TypeOf(src)
if typeof != nil && typeof.String() == "[]uint8" {
src = string(src.([]uint8))
}
lvs.val = luar.New(lvs.state, src)
return nil
}
func lvalueToInterface(l *lua.LState, v lua.LValue) interface{} {
lt := v.Type()
switch lt {
case lua.LTNil:
return nil
case lua.LTBool:
return lua.LVAsBool(v)
case lua.LTNumber:
return lua.LVAsNumber(v)
case lua.LTString:
return lua.LVAsString(v)
case lua.LTUserData:
l.Push(v)
return l.CheckUserData(l.GetTop()).Value
default:
gcutil.LogError(nil).Caller(1).
Interface("lvalue", v).
Str("type", lt.String()).
Msg("Unrecognized or unsupported Lua type")
}
return nil
}
func createLuaLogFunc(which string) lua.LGFunction {
return func(l *lua.LState) int {
switch which {
case "info":
l.Push(luar.New(l, gcutil.LogInfo()))
case "warn":
l.Push(luar.New(l, gcutil.LogWarning()))
case "error":
numArgs := l.GetTop()
if numArgs == 0 {
l.Push(luar.New(l, gcutil.LogError(nil)))
} else {
errVal := l.CheckAny(-1)
errI := lvalueToInterface(l, errVal)
err := fmt.Errorf("%v", errI)
l.Push(luar.New(l, gcutil.LogError(err)))
}
}
return 1
}
}
func luaEventRegisterHandlerAdapter(l *lua.LState, fn *lua.LFunction) events.EventHandler {
return func(trigger string, data ...interface{}) error {
args := []lua.LValue{
luar.New(l, trigger),
}
for _, i := range data {
args = append(args, luar.New(l, i))
}
l.CallByParam(lua.P{
Fn: fn,
NRet: 1,
// Protect: true,
}, args...)
errStr := lua.LVAsString(l.Get(-1))
if errStr != "" {
return errors.New(errStr)
}
return nil
}
}
func preloadLua() {
luaFilePath.Preload(lState)
luaStrings.Preload(lState)
gluahttp.Preload(lState)
async.Init(lState)
lState.PreloadModule("config", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"system_critical_config": func(l *lua.LState) int {
l.Push(luar.New(l, config.GetSystemCriticalConfig()))
return 1
},
"site_config": func(l *lua.LState) int {
l.Push(luar.New(l, config.GetSiteConfig()))
return 1
},
"board_config": func(l *lua.LState) int {
numArgs := l.GetTop()
board := ""
if numArgs > 0 {
board = l.CheckString(1)
}
l.Push(luar.New(l, config.GetBoardConfig(board)))
return 1
},
})
l.Push(t)
return 1
})
lState.PreloadModule("events", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"register_event": func(l *lua.LState) int {
table := l.CheckTable(-2)
var triggers []string
table.ForEach(func(i, val lua.LValue) {
triggers = append(triggers, val.String())
})
fn := l.CheckFunction(-1)
events.RegisterEvent(triggers, luaEventRegisterHandlerAdapter(l, fn))
return 0
},
"trigger_event": func(l *lua.LState) int {
trigger := l.CheckString(1)
numArgs := l.GetTop()
var data []interface{}
for i := 2; i <= numArgs; i++ {
v := l.CheckAny(i)
data = append(data, lvalueToInterface(l, v))
}
events.TriggerEvent(trigger, data...)
return 0
},
})
l.Push(t)
return 1
})
lState.PreloadModule("gclog", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"info_log": createLuaLogFunc("info"),
"warn_log": createLuaLogFunc("warn"),
"error_log": createLuaLogFunc("error"),
})
l.Push(t)
return 1
})
lState.PreloadModule("gcsql", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"query_rows": func(l *lua.LState) int {
queryStr := l.CheckString(1)
queryArgsL := l.CheckAny(2)
var queryArgs []any
if queryArgsL.Type() != lua.LTNil {
table := queryArgsL.(*lua.LTable)
table.ForEach(func(_ lua.LValue, val lua.LValue) {
arg := lvalueToInterface(l, val)
queryArgs = append(queryArgs, arg)
})
}
rows, err := gcsql.QuerySQL(queryStr, queryArgs...)
l.Push(luar.New(l, rows))
l.Push(luar.New(l, err))
return 2
},
"execute_sql": func(l *lua.LState) int {
execStr := l.CheckString(1)
execArgsL := l.CheckAny(2)
var execArgs []any
if execArgsL.Type() != lua.LTNil {
table := execArgsL.(*lua.LTable)
table.ForEach(func(_, val lua.LValue) {
arg := lvalueToInterface(l, val)
execArgs = append(execArgs, arg)
})
}
result, err := gcsql.ExecSQL(execStr)
l.Push(luar.New(l, result))
l.Push(luar.New(l, err))
return 2
},
"scan_rows": func(l *lua.LState) int {
rows := l.CheckUserData(1).Value.(*sql.Rows)
table := l.CheckTable(2)
var scanners []any
colNames, err := rows.Columns()
if err != nil {
l.Push(luar.New(l, err))
return 1
}
for range colNames {
scanners = append(scanners, &lvalueScanner{state: l})
}
if err = rows.Scan(scanners...); err != nil {
l.Push(luar.New(l, err))
return 1
}
for i, name := range colNames {
table.RawSetString(name, scanners[i].(*lvalueScanner).val)
}
l.Push(lua.LNil)
return 1
},
})
l.Push(t)
return 1
})
lState.PreloadModule("gctemplates", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"load_template": func(l *lua.LState) int {
var tmplPaths []string
for i := 0; i < l.GetTop(); i++ {
tmplPaths = append(tmplPaths, l.CheckString(i+1))
}
tmpl, err := gctemplates.LoadTemplate(tmplPaths...)
l.Push(luar.New(l, tmpl))
l.Push(luar.New(l, err))
return 2
},
"minify_template": func(l *lua.LState) int {
tmplUD := l.CheckUserData(1)
tmpl := tmplUD.Value.(*template.Template)
dataTable := l.CheckTable(2)
data := map[string]interface{}{}
dataTable.ForEach(func(l1, l2 lua.LValue) {
data[l1.String()] = lvalueToInterface(l, l2)
})
writer := l.CheckUserData(3).Value.(io.Writer)
mediaType := l.CheckString(4)
err := serverutil.MinifyTemplate(tmpl, data, writer, mediaType)
l.Push(luar.New(l, err))
return 1
},
"parse_template": func(l *lua.LState) int {
tmplName := l.CheckString(1)
tmplData := l.CheckString(2)
tmpl, err := gctemplates.ParseTemplate(tmplName, tmplData)
l.Push(luar.New(l, tmpl))
l.Push(luar.New(l, err))
return 2
},
})
l.Push(t)
return 1
})
lState.PreloadModule("manage", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"ban_ip": func(l *lua.LState) int {
now := time.Now()
ban := &gcsql.IPBan{
IP: l.CheckString(1),
}
ban.IsActive = true
ban.AppealAt = now
ban.CanAppeal = true
durOrNil := l.CheckAny(2)
var err error
switch durOrNil.Type() {
case lua.LTNil:
ban.Permanent = true
case lua.LTString:
var duration time.Duration
duration, err = durationutil.ParseLongerDuration(lua.LVAsString(durOrNil))
if err != nil {
l.Push(luar.New(l, err))
return 1
}
ban.ExpiresAt = time.Now().Add(duration)
default:
lState.ArgError(2, "Expected string or nil value")
}
ban.Message = l.CheckString(3)
staff := l.CheckAny(4)
switch staff.Type() {
case lua.LTString:
ban.StaffID, err = gcsql.GetStaffID(lua.LVAsString(staff))
if err != nil {
l.Push(luar.New(l, err))
return 1
}
case lua.LTNumber:
ban.StaffID = int(lua.LVAsNumber(staff))
default:
l.TypeError(4, staff.Type())
}
if l.GetTop() > 4 {
t := l.CheckTable(5)
var failed bool
t.ForEach(func(keyLV, val lua.LValue) {
key := lua.LVAsString(keyLV)
valType := val.Type()
switch key {
case "board":
fallthrough
case "BoardID":
fallthrough
case "board_id":
switch valType {
case lua.LTNil:
// global
case lua.LTNumber:
ban.BoardID = new(int)
*ban.BoardID = int(lua.LVAsNumber(val))
case lua.LTString:
boardDir := lua.LVAsString(val)
if boardDir != "" {
var id int
if id, err = gcsql.GetBoardIDFromDir(boardDir); err != nil {
l.Push(luar.New(l, err))
return
}
ban.BoardID = new(int)
*ban.BoardID = id
}
default:
failed = true
l.Push(lua.LNil)
l.RaiseError(tableArgFmt, key, "string, number, or nil", valType)
return
}
case "post_id":
fallthrough
case "post":
fallthrough
case "PostID":
if valType != lua.LTNumber {
failed = true
l.Push(lua.LNil)
l.RaiseError(tableArgFmt, key, "number", valType)
return
}
case "is_thread_ban":
fallthrough
case "IsThreadBan":
ban.IsThreadBan = lua.LVAsBool(val)
case "appeal_after":
fallthrough
case "AppealAfter":
str := lua.LVAsString(val)
dur, err := durationutil.ParseLongerDuration(str)
if err != nil {
l.Push(luar.New(l, err))
failed = true
return
}
ban.AppealAt = now.Add(dur)
case "can_appeal":
fallthrough
case "appealable":
fallthrough
case "CanAppeal":
ban.CanAppeal = lua.LVAsBool(val)
case "staff_note":
fallthrough
case "StaffNote":
ban.StaffNote = lua.LVAsString(val)
}
})
if failed {
return 1
}
}
if ban.StaffID < 1 {
l.Push(luar.New(l, errors.New("missing staff key in table")))
return 1
}
ban.IssuedAt = time.Now()
err = gcsql.NewIPBan(ban)
l.Push(luar.New(l, err))
return 1
},
"register_manage_page": func(l *lua.LState) int {
actionID := l.CheckString(1)
actionTitle := l.CheckString(2)
actionPerms := l.CheckInt(3)
actionJSON := l.CheckInt(4)
fn := l.CheckFunction(5)
actionHandler := func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool, infoEv *zerolog.Event, errEv *zerolog.Event) (output interface{}, err error) {
if err = l.CallByParam(lua.P{
Fn: fn,
NRet: 2,
// Protect: true,
}, luar.New(l, writer), luar.New(l, request), luar.New(l, staff), lua.LBool(wantsJSON), luar.New(l, infoEv), luar.New(l, errEv)); err != nil {
return "", err
}
out := lua.LVAsString(l.Get(-2))
errStr := lua.LVAsString(l.Get(-1))
if errStr != "" {
err = errors.New(errStr)
}
return out, err
}
manage.RegisterManagePage(actionID, actionTitle, actionPerms, actionJSON, actionHandler)
return 0
},
})
l.Push(t)
return 1
})
lState.PreloadModule("uploads", func(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"register_handler": func(l *lua.LState) int {
ext := l.CheckString(1)
handler := l.CheckFunction(2)
uploads.RegisterUploadHandler(ext, func(upload *gcsql.Upload, post *gcsql.Post, board, filePath, thumbPath, catalogThumbPath string, infoEv, accessEv, errEv *zerolog.Event) error {
l.CallByParam(lua.P{
Fn: handler,
NRet: 1,
// Protect: true,
}, luar.New(l, upload), luar.New(l, post), lua.LString(board), lua.LString(filePath), lua.LString(thumbPath), lua.LString(catalogThumbPath))
errRet := l.CheckAny(-1)
if errRet != nil && errRet.Type() != lua.LTNil {
return errors.New(errRet.String())
}
return nil
})
return 0
},
"get_thumbnail_ext": func(l *lua.LState) int {
fileExt := l.CheckString(1)
l.Push(luar.New(l, uploads.GetThumbnailExtension(fileExt)))
return 1
},
"set_thumbnail_ext": func(l *lua.LState) int {
fileExt := l.CheckString(1)
thumbExt := l.CheckString(2)
uploads.SetThumbnailExtension(fileExt, thumbExt)
return 0
},
})
l.Push(t)
return 1
})
lState.PreloadModule("config", config.PreloadModule)
lState.PreloadModule("events", events.PreloadModule)
lState.PreloadModule("gclog", gcutil.PreloadModule)
lState.PreloadModule("gcsql", gcsql.PreloadModule)
lState.PreloadModule("gctemplates", gctemplates.PreloadModule)
lState.PreloadModule("manage", manage.PreloadModule)
lState.PreloadModule("uploads", uploads.PreloadModule)
lState.SetGlobal("_GOCHAN_VERSION", lua.LString(config.GetVersion().String()))
}

View file

@ -0,0 +1,25 @@
package luautil
import (
lua "github.com/yuin/gopher-lua"
)
func LValueToInterface(l *lua.LState, v lua.LValue) interface{} {
lt := v.Type()
switch lt {
case lua.LTNil:
return nil
case lua.LTBool:
return lua.LVAsBool(v)
case lua.LTNumber:
return lua.LVAsNumber(v)
case lua.LTString:
return lua.LVAsString(v)
case lua.LTUserData:
l.Push(v)
return l.CheckUserData(l.GetTop()).Value
default:
l.RaiseError("Incompatible Lua type")
}
return nil
}

94
pkg/gcsql/preload.go Normal file
View file

@ -0,0 +1,94 @@
package gcsql
import (
"database/sql"
"reflect"
"github.com/gochan-org/gochan/pkg/gcplugin/luautil"
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
)
type lvalueScanner struct {
val lua.LValue
state *lua.LState
}
func (lvs *lvalueScanner) Scan(src any) error {
typeof := reflect.TypeOf(src)
if typeof != nil && typeof.String() == "[]uint8" {
src = string(src.([]uint8))
}
lvs.val = luar.New(lvs.state, src)
return nil
}
func PreloadModule(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"query_rows": func(l *lua.LState) int {
queryStr := l.CheckString(1)
queryArgsL := l.CheckAny(2)
var queryArgs []any
if queryArgsL.Type() != lua.LTNil {
table := queryArgsL.(*lua.LTable)
table.ForEach(func(_ lua.LValue, val lua.LValue) {
arg := luautil.LValueToInterface(l, val)
queryArgs = append(queryArgs, arg)
})
}
rows, err := QuerySQL(queryStr, queryArgs...)
l.Push(luar.New(l, rows))
l.Push(luar.New(l, err))
return 2
},
"execute_sql": func(l *lua.LState) int {
execStr := l.CheckString(1)
execArgsL := l.CheckAny(2)
var execArgs []any
if execArgsL.Type() != lua.LTNil {
table := execArgsL.(*lua.LTable)
table.ForEach(func(_, val lua.LValue) {
arg := luautil.LValueToInterface(l, val)
execArgs = append(execArgs, arg)
})
}
result, err := ExecSQL(execStr)
l.Push(luar.New(l, result))
l.Push(luar.New(l, err))
return 2
},
"scan_rows": func(l *lua.LState) int {
rows := l.CheckUserData(1).Value.(*sql.Rows)
table := l.CheckTable(2)
var scanners []any
colNames, err := rows.Columns()
if err != nil {
l.Push(luar.New(l, err))
return 1
}
for range colNames {
scanners = append(scanners, &lvalueScanner{state: l})
}
if err = rows.Scan(scanners...); err != nil {
l.Push(luar.New(l, err))
return 1
}
for i, name := range colNames {
table.RawSetString(name, scanners[i].(*lvalueScanner).val)
}
l.Push(lua.LNil)
return 1
},
})
l.Push(t)
return 1
}

View file

@ -0,0 +1,54 @@
package gctemplates
import (
"html/template"
"io"
"github.com/gochan-org/gochan/pkg/gcplugin/luautil"
"github.com/gochan-org/gochan/pkg/server/serverutil"
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
)
func PreloadModule(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"load_template": func(l *lua.LState) int {
var tmplPaths []string
for i := 0; i < l.GetTop(); i++ {
tmplPaths = append(tmplPaths, l.CheckString(i+1))
}
tmpl, err := LoadTemplate(tmplPaths...)
l.Push(luar.New(l, tmpl))
l.Push(luar.New(l, err))
return 2
},
"minify_template": func(l *lua.LState) int {
tmplUD := l.CheckUserData(1)
tmpl := tmplUD.Value.(*template.Template)
dataTable := l.CheckTable(2)
data := map[string]interface{}{}
dataTable.ForEach(func(l1, l2 lua.LValue) {
data[l1.String()] = luautil.LValueToInterface(l, l2)
})
writer := l.CheckUserData(3).Value.(io.Writer)
mediaType := l.CheckString(4)
err := serverutil.MinifyTemplate(tmpl, data, writer, mediaType)
l.Push(luar.New(l, err))
return 1
},
"parse_template": func(l *lua.LState) int {
tmplName := l.CheckString(1)
tmplData := l.CheckString(2)
tmpl, err := ParseTemplate(tmplName, tmplData)
l.Push(luar.New(l, tmpl))
l.Push(luar.New(l, err))
return 2
},
})
l.Push(t)
return 1
}

43
pkg/gcutil/preload.go Normal file
View file

@ -0,0 +1,43 @@
package gcutil
import (
"fmt"
"github.com/gochan-org/gochan/pkg/gcplugin/luautil"
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
)
func createLuaLogFunc(which string) lua.LGFunction {
return func(l *lua.LState) int {
switch which {
case "info":
l.Push(luar.New(l, LogInfo()))
case "warn":
l.Push(luar.New(l, LogWarning()))
case "error":
numArgs := l.GetTop()
if numArgs == 0 {
l.Push(luar.New(l, LogError(nil)))
} else {
errVal := l.CheckAny(-1)
errI := luautil.LValueToInterface(l, errVal)
err := fmt.Errorf("%v", errI)
l.Push(luar.New(l, LogError(err)))
}
}
return 1
}
}
func PreloadModule(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"info_log": createLuaLogFunc("info"),
"warn_log": createLuaLogFunc("warn"),
"error_log": createLuaLogFunc("error"),
})
l.Push(t)
return 1
}

180
pkg/manage/preload.go Normal file
View file

@ -0,0 +1,180 @@
package manage
import (
"errors"
"net/http"
"time"
"github.com/Eggbertx/durationutil"
"github.com/gochan-org/gochan/pkg/gcsql"
"github.com/rs/zerolog"
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
)
const (
tableArgFmt = "invalid value for key %q passed to table, expected %s, got %s"
)
func luaBanIP(l *lua.LState) int {
now := time.Now()
ban := &gcsql.IPBan{
IP: l.CheckString(1),
}
ban.IsActive = true
ban.AppealAt = now
ban.CanAppeal = true
durOrNil := l.CheckAny(2)
var err error
switch durOrNil.Type() {
case lua.LTNil:
ban.Permanent = true
case lua.LTString:
var duration time.Duration
duration, err = durationutil.ParseLongerDuration(lua.LVAsString(durOrNil))
if err != nil {
l.Push(luar.New(l, err))
return 1
}
ban.ExpiresAt = time.Now().Add(duration)
default:
l.ArgError(2, "Expected string or nil value")
}
ban.Message = l.CheckString(3)
staff := l.CheckAny(4)
switch staff.Type() {
case lua.LTString:
ban.StaffID, err = gcsql.GetStaffID(lua.LVAsString(staff))
if err != nil {
l.Push(luar.New(l, err))
return 1
}
case lua.LTNumber:
ban.StaffID = int(lua.LVAsNumber(staff))
default:
l.TypeError(4, staff.Type())
}
if l.GetTop() > 4 {
t := l.CheckTable(5)
var failed bool
t.ForEach(func(keyLV, val lua.LValue) {
key := lua.LVAsString(keyLV)
valType := val.Type()
switch key {
case "board":
fallthrough
case "BoardID":
fallthrough
case "board_id":
switch valType {
case lua.LTNil:
// global
case lua.LTNumber:
ban.BoardID = new(int)
*ban.BoardID = int(lua.LVAsNumber(val))
case lua.LTString:
boardDir := lua.LVAsString(val)
if boardDir != "" {
var id int
if id, err = gcsql.GetBoardIDFromDir(boardDir); err != nil {
l.Push(luar.New(l, err))
return
}
ban.BoardID = new(int)
*ban.BoardID = id
}
default:
failed = true
l.Push(lua.LNil)
l.RaiseError(tableArgFmt, key, "string, number, or nil", valType)
return
}
case "post_id":
fallthrough
case "post":
fallthrough
case "PostID":
if valType != lua.LTNumber {
failed = true
l.Push(lua.LNil)
l.RaiseError(tableArgFmt, key, "number", valType)
return
}
case "is_thread_ban":
fallthrough
case "IsThreadBan":
ban.IsThreadBan = lua.LVAsBool(val)
case "appeal_after":
fallthrough
case "AppealAfter":
str := lua.LVAsString(val)
dur, err := durationutil.ParseLongerDuration(str)
if err != nil {
l.Push(luar.New(l, err))
failed = true
return
}
ban.AppealAt = now.Add(dur)
case "can_appeal":
fallthrough
case "appealable":
fallthrough
case "CanAppeal":
ban.CanAppeal = lua.LVAsBool(val)
case "staff_note":
fallthrough
case "StaffNote":
ban.StaffNote = lua.LVAsString(val)
}
})
if failed {
return 1
}
}
if ban.StaffID < 1 {
l.Push(luar.New(l, errors.New("missing staff key in table")))
return 1
}
ban.IssuedAt = time.Now()
err = gcsql.NewIPBan(ban)
l.Push(luar.New(l, err))
return 1
}
func PreloadModule(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"ban_ip": luaBanIP,
"register_manage_page": func(l *lua.LState) int {
actionID := l.CheckString(1)
actionTitle := l.CheckString(2)
actionPerms := l.CheckInt(3)
actionJSON := l.CheckInt(4)
fn := l.CheckFunction(5)
actionHandler := func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool, infoEv *zerolog.Event, errEv *zerolog.Event) (output interface{}, err error) {
if err = l.CallByParam(lua.P{
Fn: fn,
NRet: 2,
// Protect: true,
}, luar.New(l, writer), luar.New(l, request), luar.New(l, staff), lua.LBool(wantsJSON), luar.New(l, infoEv), luar.New(l, errEv)); err != nil {
return "", err
}
out := lua.LVAsString(l.Get(-2))
errStr := lua.LVAsString(l.Get(-1))
if errStr != "" {
err = errors.New(errStr)
}
return out, err
}
RegisterManagePage(actionID, actionTitle, actionPerms, actionJSON, actionHandler)
return 0
},
})
l.Push(t)
return 1
}

View file

@ -0,0 +1,49 @@
package uploads
import (
"errors"
"github.com/gochan-org/gochan/pkg/gcsql"
"github.com/rs/zerolog"
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
)
func PreloadModule(l *lua.LState) int {
t := l.NewTable()
l.SetFuncs(t, map[string]lua.LGFunction{
"register_handler": func(l *lua.LState) int {
ext := l.CheckString(1)
handler := l.CheckFunction(2)
RegisterUploadHandler(ext, func(upload *gcsql.Upload, post *gcsql.Post, board, filePath, thumbPath, catalogThumbPath string, infoEv, accessEv, errEv *zerolog.Event) error {
l.CallByParam(lua.P{
Fn: handler,
NRet: 1,
// Protect: true,
}, luar.New(l, upload), luar.New(l, post), lua.LString(board), lua.LString(filePath), lua.LString(thumbPath), lua.LString(catalogThumbPath))
errRet := l.CheckAny(-1)
if errRet != nil && errRet.Type() != lua.LTNil {
return errors.New(errRet.String())
}
return nil
})
return 0
},
"get_thumbnail_ext": func(l *lua.LState) int {
fileExt := l.CheckString(1)
l.Push(luar.New(l, GetThumbnailExtension(fileExt)))
return 1
},
"set_thumbnail_ext": func(l *lua.LState) int {
fileExt := l.CheckString(1)
thumbExt := l.CheckString(2)
SetThumbnailExtension(fileExt, thumbExt)
return 0
},
})
l.Push(t)
return 1
}