exposed 'listDevices' signal-cli command via REST

* the signal-cli command 'listDevices' is now exposed as
  /v1/devices/<number> endpoint.

see #676
This commit is contained in:
Bernhard B
2025-03-30 23:14:16 +02:00
parent bf929a3aa1
commit 3497b3d806
6 changed files with 222 additions and 0 deletions

View File

@@ -1843,6 +1843,35 @@ func (a *Api) AddDevice(c *gin.Context) {
c.Status(http.StatusNoContent)
}
// @Summary List linked devices.
// @Tags Devices
// @Description List linked devices associated to this device.
// @Accept json
// @Produce json
// @Param number path string true "Registered Phone Number"
// @Success 200 {object} []client.ListDevicesResponse
// @Failure 400 {object} Error
// @Router /v1/devices/{number} [get]
func (a *Api) ListDevices(c *gin.Context) {
number, err := url.PathUnescape(c.Param("number"))
if err != nil {
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
return
}
if number == "" {
c.JSON(400, Error{Msg: "Couldn't process request - number missing"})
return
}
devices, err := a.signalClient.ListDevices(number)
if err != nil {
c.JSON(400, Error{Msg: err.Error()})
return
}
c.JSON(200, devices)
}
// @Summary Set account specific settings.
// @Tags General
// @Description Set account specific settings.

View File

@@ -217,6 +217,12 @@ type ListContactsResponse struct {
Nickname Nickname `json:"nickname"`
}
type ListDevicesResponse struct {
Name string `json:"name"`
LastSeenTimestamp int64 `json:"last_seen_timestamp"`
CreationTimestamp int64 `json:"creation_timestamp"`
}
func cleanupTmpFiles(paths []string) {
for _, path := range paths {
os.Remove(path)
@@ -2026,6 +2032,51 @@ func (s *SignalClient) AddDevice(number string, uri string) error {
return err
}
func (s *SignalClient) ListDevices(number string) ([]ListDevicesResponse, error) {
resp := []ListDevicesResponse{}
type ListDevicesSignalCliResponse struct {
Id int64 `json:"id"`
Name string `json:"name"`
CreatedTimestamp int64 `json:"createdTimestamp"`
LastSeenTimestamp int64 `json:"lastSeenTimestamp"`
}
var err error
var rawData string
if s.signalCliMode == JsonRpc {
jsonRpc2Client, err := s.getJsonRpc2Client()
if err != nil {
return resp, err
}
rawData, err = jsonRpc2Client.getRaw("listDevices", &number, nil)
} else {
cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "listDevices"}
rawData, err = s.cliClient.Execute(true, cmd, "")
}
if err != nil {
return resp, err
}
var signalCliResp []ListDevicesSignalCliResponse
err = json.Unmarshal([]byte(rawData), &signalCliResp)
if err != nil {
return resp, err
}
for _, entry := range signalCliResp {
deviceEntry := ListDevicesResponse{
Name: entry.Name,
CreationTimestamp: entry.CreatedTimestamp,
LastSeenTimestamp: entry.LastSeenTimestamp,
}
resp = append(resp, deviceEntry)
}
return resp, nil
}
func (s *SignalClient) SetTrustMode(number string, trustMode utils.SignalCliTrustMode) error {
s.signalCliApiConfig.SetTrustModeForNumber(number, trustMode)
return s.signalCliApiConfig.Persist()

View File

@@ -666,6 +666,45 @@ const docTemplate = `{
}
},
"/v1/devices/{number}": {
"get": {
"description": "List linked devices associated to this device.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Devices"
],
"summary": "List linked devices.",
"parameters": [
{
"type": "string",
"description": "Registered Phone Number",
"name": "number",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/client.ListDevicesResponse"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
},
"post": {
"description": "Links another device to this device. Only works, if this is the master device.",
"consumes": [
@@ -2722,6 +2761,20 @@ const docTemplate = `{
}
}
},
"client.ListDevicesResponse": {
"type": "object",
"properties": {
"creation_timestamp": {
"type": "integer"
},
"last_seen_timestamp": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"client.ListInstalledStickerPacksResponse": {
"type": "object",
"properties": {

View File

@@ -663,6 +663,45 @@
}
},
"/v1/devices/{number}": {
"get": {
"description": "List linked devices associated to this device.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Devices"
],
"summary": "List linked devices.",
"parameters": [
{
"type": "string",
"description": "Registered Phone Number",
"name": "number",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/client.ListDevicesResponse"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
},
"post": {
"description": "Links another device to this device. Only works, if this is the master device.",
"consumes": [
@@ -2719,6 +2758,20 @@
}
}
},
"client.ListDevicesResponse": {
"type": "object",
"properties": {
"creation_timestamp": {
"type": "integer"
},
"last_seen_timestamp": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"client.ListInstalledStickerPacksResponse": {
"type": "object",
"properties": {

View File

@@ -393,6 +393,15 @@ definitions:
uuid:
type: string
type: object
client.ListDevicesResponse:
properties:
creation_timestamp:
type: integer
last_seen_timestamp:
type: integer
name:
type: string
type: object
client.ListInstalledStickerPacksResponse:
properties:
author:
@@ -879,6 +888,32 @@ paths:
tags:
- Contacts
/v1/devices/{number}:
get:
consumes:
- application/json
description: List linked devices associated to this device.
parameters:
- description: Registered Phone Number
in: path
name: number
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/client.ListDevicesResponse'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
summary: List linked devices.
tags:
- Devices
post:
consumes:
- application/json

View File

@@ -230,6 +230,7 @@ func main() {
devices := v1.Group("devices")
{
devices.POST(":number", api.AddDevice)
devices.GET(":number", api.ListDevices)
}
attachments := v1.Group("attachments")