mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-17 10:56:24 -07:00
Add support for native Go plugins
This commit is contained in:
parent
6edca0edee
commit
6a7f938d04
3 changed files with 96 additions and 19 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,6 +12,7 @@
|
|||
*.log
|
||||
*.swp
|
||||
*.db
|
||||
*.so
|
||||
__debug_bin
|
||||
node_modules
|
||||
/frontend/coverage
|
||||
|
|
|
@ -3,9 +3,12 @@ package gcplugin
|
|||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"plugin"
|
||||
"reflect"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
|
@ -24,8 +27,9 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
lState *lua.LState
|
||||
eventPlugins map[string][]*lua.LFunction
|
||||
lState *lua.LState
|
||||
eventPlugins map[string][]*lua.LFunction
|
||||
ErrorInvalidInitFunc = errors.New("invalid InitPlugin, expected function with 0 arguments and 1 return value (error type)")
|
||||
)
|
||||
|
||||
func initLua() {
|
||||
|
@ -98,7 +102,7 @@ func createLuaLogFunc(which string) lua.LGFunction {
|
|||
}
|
||||
|
||||
func luaEventRegisterHandlerAdapter(l *lua.LState, fn *lua.LFunction) events.EventHandler {
|
||||
return func(trigger string, data ...interface{}) {
|
||||
return func(trigger string, data ...interface{}) error {
|
||||
args := []lua.LValue{
|
||||
luar.New(l, trigger),
|
||||
}
|
||||
|
@ -107,9 +111,14 @@ func luaEventRegisterHandlerAdapter(l *lua.LState, fn *lua.LFunction) events.Eve
|
|||
}
|
||||
l.CallByParam(lua.P{
|
||||
Fn: fn,
|
||||
NRet: 0,
|
||||
NRet: 1,
|
||||
// Protect: true,
|
||||
}, args...)
|
||||
errStr := lua.LVAsString(l.Get(-1))
|
||||
if errStr != "" {
|
||||
return errors.New(errStr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,9 +217,9 @@ func registerLuaFunctions() {
|
|||
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,
|
||||
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
|
||||
}
|
||||
|
@ -272,20 +281,47 @@ func registerEventFunction(name string, fn *lua.LFunction) {
|
|||
|
||||
func LoadPlugins(paths []string) error {
|
||||
var err error
|
||||
var luaInitialized bool
|
||||
for _, pluginPath := range paths {
|
||||
initLua()
|
||||
if err = lState.DoFile(pluginPath); err != nil {
|
||||
return err
|
||||
}
|
||||
pluginTable := lState.NewTable()
|
||||
pluginTable.ForEach(func(key, val lua.LValue) {
|
||||
keyStr := key.String()
|
||||
fn, ok := val.(*lua.LFunction)
|
||||
if !ok {
|
||||
return
|
||||
ext := path.Ext(pluginPath)
|
||||
fmt.Println("Loading plugin", pluginPath)
|
||||
switch ext {
|
||||
case ".lua":
|
||||
if !luaInitialized {
|
||||
initLua()
|
||||
luaInitialized = true
|
||||
}
|
||||
registerEventFunction(keyStr, fn)
|
||||
})
|
||||
if err = lState.DoFile(pluginPath); err != nil {
|
||||
return err
|
||||
}
|
||||
pluginTable := lState.NewTable()
|
||||
pluginTable.ForEach(func(key, val lua.LValue) {
|
||||
keyStr := key.String()
|
||||
fn, ok := val.(*lua.LFunction)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
registerEventFunction(keyStr, fn)
|
||||
})
|
||||
case ".so":
|
||||
nativePlugin, err := plugin.Open(pluginPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
initFuncSymbol, err := nativePlugin.Lookup("InitPlugin")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
initFunc, ok := initFuncSymbol.(func() error)
|
||||
if !ok {
|
||||
return ErrorInvalidInitFunc
|
||||
}
|
||||
if err = initFunc(); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unrecognized plugin type (expected .lua or .so extension): %s", pluginPath)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
40
sample-plugins/require-distinct-filenames/main.go
Normal file
40
sample-plugins/require-distinct-filenames/main.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/events"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
)
|
||||
|
||||
var (
|
||||
errDuplicateFilename = errors.New("a file with that filename has already been uploaded")
|
||||
)
|
||||
|
||||
func InitPlugin() error {
|
||||
events.RegisterEvent([]string{"incoming-upload"}, func(trigger string, uploadInterface ...interface{}) error {
|
||||
if len(uploadInterface) == 0 {
|
||||
return nil
|
||||
}
|
||||
upload, ok := uploadInterface[0].(*gcsql.Upload)
|
||||
if !ok {
|
||||
return errors.New("invalid upload interface passed to incoming-upload")
|
||||
}
|
||||
if upload == nil {
|
||||
return nil
|
||||
}
|
||||
var count int
|
||||
err := gcsql.QueryRowSQL("SELECT COUNT(*) FROM DBPREFIXfiles WHERE original_filename = ?",
|
||||
[]interface{}{upload.OriginalFilename}, []interface{}{&count})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
// one or more posts with the same filename exist
|
||||
return errDuplicateFilename
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue