mirror of
https://github.com/aljazceru/signal-cli-rest-api.git
synced 2025-12-20 08:04:28 +01:00
added plugin mechanism
This commit is contained in:
99
doc/PLUGINS.md
Normal file
99
doc/PLUGINS.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
Plugins allow to dynamically register custom endpoints without forking this project.
|
||||||
|
|
||||||
|
# Why?
|
||||||
|
|
||||||
|
Imagine that you want to use the Signal REST API in a software component that has some restrictions regarding the payload it supports. To give you a real world example: If you want to use the Signal REST API to send Signal notifications from your Synology NAS to your phone, the HTTP endpoint must have only "flat" parameters - i.e array parameters in the JSON payload aren't allowed. Since the `recipients` parameter in the `/v2/send` endpoint is a array parameter, the send endpoint cannot be used in the Synology NAS. In order to work around that limitation, you can write a small custom plugin in Lua, to create a custom send endpoint, which exposes a single `recipient` string instead of an array.
|
||||||
|
|
||||||
|
# How to write a custom plugin
|
||||||
|
|
||||||
|
In order to use plugins, you first need to enable that feature. This can be done by setting the environment variable `ENABLE_PLUGINS` to `true` in the `docker-compose.yml` file.
|
||||||
|
|
||||||
|
e.g:
|
||||||
|
|
||||||
|
```
|
||||||
|
services:
|
||||||
|
signal-cli-rest-api:
|
||||||
|
image: bbernhard/signal-cli-rest-api:latest
|
||||||
|
environment:
|
||||||
|
- MODE=json-rpc #supported modes: json-rpc, native, normal
|
||||||
|
- ENABLE_PLUGINS=true #enable plugins
|
||||||
|
```
|
||||||
|
|
||||||
|
A valid plugin consists of a definition file (with the file ending `.def`) and a matching lua script file (with the file ending `.lua`). Both of those files must have the same filename and are placed in a folder called `plugins` on the host filesystem. Now, bind mount the `plugins` folder from your host system into the `/plugins` folder inside the docker container. This can be done in the `docker-compose.yml` file:
|
||||||
|
|
||||||
|
```
|
||||||
|
services:
|
||||||
|
signal-cli-rest-api:
|
||||||
|
image: bbernhard/signal-cli-rest-api:latest
|
||||||
|
environment:
|
||||||
|
- MODE=json-rpc #supported modes: json-rpc, native, normal
|
||||||
|
- ENABLE_PLUGINS=true
|
||||||
|
volumes:
|
||||||
|
- "./signal-cli-config:/home/.local/share/signal-cli"
|
||||||
|
- "./plugins:/plugins" #map "plugins" folder on host system into docker container.
|
||||||
|
```
|
||||||
|
|
||||||
|
# The definition file
|
||||||
|
|
||||||
|
The definition file (with the file suffix `.def`) contains some metadata which is necessary to properly register the new endpoint. A proper definition file looks like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
endpoint: my-custom-send-endpoint/:number
|
||||||
|
method: POST
|
||||||
|
```
|
||||||
|
|
||||||
|
The `endpoint` specifies the URI of the newly created endpoint. All custom endpoints are registered under the `/v1/plugins` endpoint. So, our `my-custom-send-endpoint` will be available at `/v1/plugins/my-custom-endpoint`. If you want to use variables inside the endpoint, prefix them with a `:`.
|
||||||
|
|
||||||
|
The `method` parameter specifies the HTTP method that is used for the endpoint registration.
|
||||||
|
|
||||||
|
# The script file
|
||||||
|
|
||||||
|
The script file (with the file suffix `.lua`) contains the implementation of the endpoint.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
local http = require("http")
|
||||||
|
local json = require("json")
|
||||||
|
|
||||||
|
local url = "http://127.0.0.1:8080/v2/send"
|
||||||
|
|
||||||
|
local customEndpointPayload = json.decode(pluginInputData.payload)
|
||||||
|
|
||||||
|
local sendEndpointPayload = {
|
||||||
|
recipients = {customEndpointPayload.recipient},
|
||||||
|
message = customEndpointPayload.message,
|
||||||
|
number = pluginInputData.Params.number
|
||||||
|
}
|
||||||
|
|
||||||
|
local encodedSendEndpointPayload = json.encode(sendEndpointPayload)
|
||||||
|
|
||||||
|
response, error_message = http.request("POST", url, {
|
||||||
|
timeout="30s",
|
||||||
|
headers={
|
||||||
|
Accept="*/*",
|
||||||
|
["Content-Type"]="application/json"
|
||||||
|
},
|
||||||
|
body=encodedSendEndpointPayload
|
||||||
|
})
|
||||||
|
|
||||||
|
pluginOutputData:SetPayload(response["body"])
|
||||||
|
pluginOutputData:SetHttpStatusCode(response.status_code)
|
||||||
|
```
|
||||||
|
|
||||||
|
What the lua script does, is parse the JSON payload from the custom request, extract the `recipient` and the `message` from the payload and the `number` from the URL parameter and call the `/v2/send` endpoint with those parameters. The HTTP status code and the body that is returned by the HTTP request is then returned to the caller (this is done via the `pluginOutputData:SetPayload` and `pluginOutputData:SetHttpStatusCode` functions.
|
||||||
|
|
||||||
|
If you now invoke the following curl command, a message gets sent:
|
||||||
|
|
||||||
|
`curl -X POST -H "Content-Type: application/json" -d '{"message": "test", "recipient": "<recipient>"}' 'http://127.0.0.1:8080/v1/plugins/my-custom-send-endpoint/<registered signal number>'`
|
||||||
|
|
||||||
|
# Pass commands from/to the lua script
|
||||||
|
|
||||||
|
When a new plugin is registered, some parameters are automatically passed as global variables to the lua script:
|
||||||
|
|
||||||
|
* `pluginInputData.payload`: the (JSON) payload that is passed to the custom endpoint
|
||||||
|
* `pluginInputData.Params`: a map of all parameters that are part of the URL, which were defined in the definition file (i.e those parameters that were defined with `:` prefixed in the URL)
|
||||||
|
|
||||||
|
In order to return values from the lua script, the following functions are available:
|
||||||
|
* `pluginOutputData:SetPayload()`: Set the (JSON) payload that is returned to the caller
|
||||||
|
* `pluginOutputData:SetHttpStatusCode()`: Set the HTTP status code that is returned to the caller
|
||||||
2
plugins/example.def
Normal file
2
plugins/example.def
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
endpoint: my-custom-send-endpoint/:number
|
||||||
|
method: POST
|
||||||
27
plugins/example.lua
Normal file
27
plugins/example.lua
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
local http = require("http")
|
||||||
|
local json = require("json")
|
||||||
|
|
||||||
|
local url = "http://127.0.0.1:8080/v2/send"
|
||||||
|
|
||||||
|
local customEndpointPayload = json.decode(pluginInputData.payload)
|
||||||
|
|
||||||
|
local sendEndpointPayload = {
|
||||||
|
recipients = {customEndpointPayload.recipient},
|
||||||
|
message = customEndpointPayload.message,
|
||||||
|
number = pluginInputData.Params.number
|
||||||
|
}
|
||||||
|
|
||||||
|
local encodedSendEndpointPayload = json.encode(sendEndpointPayload)
|
||||||
|
print(encodedSendEndpointPayload)
|
||||||
|
|
||||||
|
response, error_message = http.request("POST", url, {
|
||||||
|
timeout="30s",
|
||||||
|
headers={
|
||||||
|
Accept="*/*",
|
||||||
|
["Content-Type"]="application/json"
|
||||||
|
},
|
||||||
|
body=encodedSendEndpointPayload
|
||||||
|
})
|
||||||
|
|
||||||
|
pluginOutputData:SetPayload(response["body"])
|
||||||
|
pluginOutputData:SetHttpStatusCode(response.status_code)
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/gabriel-vasile/mimetype"
|
"github.com/gabriel-vasile/mimetype"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -19,6 +20,11 @@ import (
|
|||||||
"github.com/bbernhard/signal-cli-rest-api/client"
|
"github.com/bbernhard/signal-cli-rest-api/client"
|
||||||
ds "github.com/bbernhard/signal-cli-rest-api/datastructs"
|
ds "github.com/bbernhard/signal-cli-rest-api/datastructs"
|
||||||
utils "github.com/bbernhard/signal-cli-rest-api/utils"
|
utils "github.com/bbernhard/signal-cli-rest-api/utils"
|
||||||
|
|
||||||
|
"github.com/yuin/gopher-lua"
|
||||||
|
"github.com/cjoudrey/gluahttp"
|
||||||
|
"layeh.com/gopher-luar"
|
||||||
|
luajson "layeh.com/gopher-json"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -2166,3 +2172,69 @@ func (a *Api) ListContacts(c *gin.Context) {
|
|||||||
|
|
||||||
c.JSON(200, contacts)
|
c.JSON(200, contacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PluginInputData struct {
|
||||||
|
Params map[string]string
|
||||||
|
Payload string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginOutputData struct {
|
||||||
|
payload string
|
||||||
|
httpStatusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginOutputData) SetPayload(payload string) {
|
||||||
|
p.payload = payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginOutputData) Payload() string {
|
||||||
|
return p.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginOutputData) SetHttpStatusCode(httpStatusCode int) {
|
||||||
|
p.httpStatusCode = httpStatusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginOutputData) HttpStatusCode() int {
|
||||||
|
return p.httpStatusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) ExecutePlugin(c *gin.Context, pluginConfig utils.PluginConfig) {
|
||||||
|
jsonData, err := io.ReadAll(c.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(400, Error{Msg: "Couldn't process request - invalid input data"})
|
||||||
|
log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginInputData := &PluginInputData{
|
||||||
|
Params: make(map[string]string),
|
||||||
|
Payload: string(jsonData),
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginOutputData := &PluginOutputData{
|
||||||
|
payload: "",
|
||||||
|
httpStatusCode: 200,
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(pluginConfig.Endpoint, "/")
|
||||||
|
for _, part := range parts {
|
||||||
|
if strings.HasPrefix(part, ":") {
|
||||||
|
paramName := strings.TrimPrefix(part, ":")
|
||||||
|
pluginInputData.Params[paramName] = c.Param(paramName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l := lua.NewState()
|
||||||
|
l.SetGlobal("pluginInputData", luar.New(l, pluginInputData))
|
||||||
|
l.SetGlobal("pluginOutputData", luar.New(l, pluginOutputData))
|
||||||
|
l.PreloadModule("http", gluahttp.NewHttpModule(&http.Client{}).Loader)
|
||||||
|
luajson.Preload(l)
|
||||||
|
defer l.Close()
|
||||||
|
if err := l.DoFile(pluginConfig.ScriptPath); err != nil {
|
||||||
|
c.JSON(400, Error{Msg: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(pluginOutputData.HttpStatusCode(), pluginOutputData.Payload())
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ go 1.14
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
||||||
|
github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4
|
github.com/cyphar/filepath-securejoin v0.2.4
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2
|
github.com/gabriel-vasile/mimetype v1.4.2
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
@@ -22,6 +23,9 @@ require (
|
|||||||
github.com/tidwall/gjson v1.14.4 // indirect
|
github.com/tidwall/gjson v1.14.4 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tidwall/sjson v1.2.5
|
github.com/tidwall/sjson v1.2.5
|
||||||
|
github.com/yuin/gopher-lua v1.1.1 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf // indirect
|
||||||
|
layeh.com/gopher-luar v1.0.11 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
13
src/go.sum
13
src/go.sum
@@ -12,6 +12,11 @@ github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZX
|
|||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9 h1:rdWOzitWlNYeUsXmz+IQfa9NkGEq3gA/qQ3mOEqBU6o=
|
||||||
|
github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9/go.mod h1:X97UjDTXp+7bayQSFZk2hPvCTmTZIicUjZQRtkwgAKY=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||||
@@ -168,6 +173,9 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ
|
|||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||||
|
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
|
||||||
|
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
@@ -219,6 +227,7 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
|||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -305,4 +314,8 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:rRz0YsF7VXj9fXRF6yQgFI7DzST+hsI3TeFSGupntu0=
|
||||||
|
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf/go.mod h1:ivKkcY8Zxw5ba0jldhZCYYQfGdb2K6u9tbYK1AwMIBc=
|
||||||
|
layeh.com/gopher-luar v1.0.11 h1:8zJudpKI6HWkoh9eyyNFaTM79PY6CAPcIr6X/KTiliw=
|
||||||
|
layeh.com/gopher-luar v1.0.11/go.mod h1:TPnIVCZ2RJBndm7ohXyaqfhzjlZ+OA2SZR/YwL8tECk=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
|||||||
33
src/main.go
33
src/main.go
@@ -61,6 +61,16 @@ import (
|
|||||||
// @host localhost:8080
|
// @host localhost:8080
|
||||||
// @schemes http
|
// @schemes http
|
||||||
// @BasePath /
|
// @BasePath /
|
||||||
|
|
||||||
|
func PluginHandler(api *api.Api, pluginConfig utils.PluginConfig) gin.HandlerFunc {
|
||||||
|
fn := func(c *gin.Context) {
|
||||||
|
api.ExecutePlugin(c, pluginConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gin.HandlerFunc(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
signalCliConfig := flag.String("signal-cli-config", "/home/.local/share/signal-cli/", "Config directory where signal-cli config is stored")
|
signalCliConfig := flag.String("signal-cli-config", "/home/.local/share/signal-cli/", "Config directory where signal-cli config is stored")
|
||||||
attachmentTmpDir := flag.String("attachment-tmp-dir", "/tmp/", "Attachment tmp directory")
|
attachmentTmpDir := flag.String("attachment-tmp-dir", "/tmp/", "Attachment tmp directory")
|
||||||
@@ -277,6 +287,29 @@ func main() {
|
|||||||
contacts.PUT(":number", api.UpdateContact)
|
contacts.PUT(":number", api.UpdateContact)
|
||||||
contacts.POST(":number/sync", api.SendContacts)
|
contacts.POST(":number/sync", api.SendContacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if utils.GetEnv("ENABLE_PLUGINS", "false") == "true" {
|
||||||
|
plugins := v1.Group("/plugins")
|
||||||
|
{
|
||||||
|
pluginConfigs := utils.NewPluginConfigs()
|
||||||
|
err := pluginConfigs.Load("/plugins")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Couldn't load plugin configs: ", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pluginConfig := range pluginConfigs.Configs {
|
||||||
|
if pluginConfig.Method == "GET" {
|
||||||
|
plugins.GET(pluginConfig.Endpoint, PluginHandler(api, pluginConfig))
|
||||||
|
} else if pluginConfig.Method == "POST" {
|
||||||
|
plugins.POST(pluginConfig.Endpoint, PluginHandler(api, pluginConfig))
|
||||||
|
} else if pluginConfig.Method == "DELETE" {
|
||||||
|
plugins.DELETE(pluginConfig.Endpoint, PluginHandler(api, pluginConfig))
|
||||||
|
} else if pluginConfig.Method == "PUT" {
|
||||||
|
plugins.PUT(pluginConfig.Endpoint, PluginHandler(api, pluginConfig))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v2 := router.Group("/v2")
|
v2 := router.Group("/v2")
|
||||||
|
|||||||
54
src/utils/plugin_config.go
Normal file
54
src/utils/plugin_config.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PluginConfig struct {
|
||||||
|
Endpoint string `yaml:"endpoint"`
|
||||||
|
Method string `yaml:"method"`
|
||||||
|
ScriptPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPluginConfigs() *PluginConfigs {
|
||||||
|
return &PluginConfigs{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginConfigs struct {
|
||||||
|
Configs []PluginConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PluginConfigs) Load(baseDirectory string) error {
|
||||||
|
|
||||||
|
err := filepath.Walk(baseDirectory, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.Ext(path) != ".def" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pluginConfig PluginConfig
|
||||||
|
err = yaml.Unmarshal(data, &pluginConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pluginConfig.ScriptPath = strings.TrimSuffix(path, filepath.Ext(path)) + ".lua"
|
||||||
|
c.Configs = append(c.Configs, pluginConfig)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user