feat: add create integration with code
This commit is contained in:
parent
d869e8d119
commit
54a2b7418a
@ -104,27 +104,31 @@ func (db *DeviceDatabase) DeleteDevice(ID string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DeviceDatabase) CreateIntegration(name string) (string, string, error) {
|
func (db *DeviceDatabase) CreateIntegration(name, token string) (string, error) {
|
||||||
id, err := gonanoid.Nanoid(10)
|
id, err := gonanoid.Nanoid(10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", err
|
||||||
}
|
|
||||||
|
|
||||||
token, err := gonanoid.Nanoid(16)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hashed_token, err := hashPassword(token)
|
hashed_token, err := hashPassword(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Connection.Exec("INSERT INTO integrations (id, name, token) VALUES (?, ?, ?)", id, name, hashed_token)
|
_, err = db.Connection.Exec("INSERT INTO integrations (id, name, token) VALUES (?, ?, ?)", id, name, hashed_token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return id, token, nil
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DeviceDatabase) IntegrationNameExists(name string) (bool, error) {
|
||||||
|
var exists bool
|
||||||
|
err := db.Connection.QueryRow("SELECT EXISTS(SELECT 1 FROM integrations WHERE name = ?)", name).Scan(&exists)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return exists, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DeviceDatabase) GetSession(sessionToken string) (*DeviceSession, error) {
|
func (db *DeviceDatabase) GetSession(sessionToken string) (*DeviceSession, error) {
|
||||||
|
|||||||
7
data/integration.go
Normal file
7
data/integration.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
type Integration struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
package management
|
package management
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
d "playback-device-server/data"
|
d "playback-device-server/data"
|
||||||
|
"slices"
|
||||||
|
|
||||||
gonanoid "github.com/matoous/go-nanoid"
|
gonanoid "github.com/matoous/go-nanoid"
|
||||||
)
|
)
|
||||||
@ -87,7 +89,68 @@ func (um *DeviceManager) DeleteDevice(ID string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (um *DeviceManager) GetRegistrationCode() (string, error) {
|
func (um *DeviceManager) GetRegistrationCode() (string, error) {
|
||||||
code := gonanoid.MustGenerate("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 6)
|
code := um.createCode()
|
||||||
um.registrationCodes = append(um.registrationCodes, code)
|
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (um *DeviceManager) CreateIntegration(name, code string) (d.Integration, error) {
|
||||||
|
exists, err := um.deviceDatabase.IntegrationNameExists(name)
|
||||||
|
if err != nil {
|
||||||
|
return d.Integration{}, err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return d.Integration{}, fmt.Errorf("Integration name already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := um.redeemCode(code); err != nil {
|
||||||
|
return d.Integration{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := gonanoid.Nanoid(16)
|
||||||
|
if err != nil {
|
||||||
|
return d.Integration{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := um.deviceDatabase.CreateIntegration(name, token)
|
||||||
|
if err != nil {
|
||||||
|
return d.Integration{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
integration := d.Integration{
|
||||||
|
ID: id,
|
||||||
|
Name: name,
|
||||||
|
Token: token,
|
||||||
|
}
|
||||||
|
|
||||||
|
return integration, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (um *DeviceManager) checkCode(code string) bool {
|
||||||
|
for _, c := range um.registrationCodes {
|
||||||
|
if c == code {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (um *DeviceManager) createCode() string {
|
||||||
|
code := gonanoid.MustGenerate("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 6)
|
||||||
|
um.registrationCodes = append(um.registrationCodes, code)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
func (um *DeviceManager) redeemCode(code string) error {
|
||||||
|
if !um.checkCode(code) {
|
||||||
|
return fmt.Errorf("Invalid registration code")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range um.registrationCodes {
|
||||||
|
if c == code {
|
||||||
|
um.registrationCodes = slices.Delete(um.registrationCodes, i, i+1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@ func (r *DeviceApiHandler) Initialize(authenticator *Authenticator) {
|
|||||||
devicesApi.DELETE("/:id", r.handleDeleteDevice)
|
devicesApi.DELETE("/:id", r.handleDeleteDevice)
|
||||||
integrationsApi := r.router.Group("/api/integrations")
|
integrationsApi := r.router.Group("/api/integrations")
|
||||||
integrationsApi.GET("/register", r.handleIntegrationRegistration)
|
integrationsApi.GET("/register", r.handleIntegrationRegistration)
|
||||||
|
integrationsApi.POST("", r.handleCreateIntegration)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DeviceApiHandler) handleIntegrationRegistration(context echo.Context) error {
|
func (r *DeviceApiHandler) handleIntegrationRegistration(context echo.Context) error {
|
||||||
@ -44,6 +45,28 @@ func (r *DeviceApiHandler) handleIntegrationRegistration(context echo.Context) e
|
|||||||
return context.JSON(200, response)
|
return context.JSON(200, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *DeviceApiHandler) handleCreateIntegration(context echo.Context) error {
|
||||||
|
var data struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
error := context.Bind(&data)
|
||||||
|
|
||||||
|
if error != nil {
|
||||||
|
SendError(500, context, fmt.Sprintf("Failed to create integration: %s", error))
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
integration, error := r.deviceManager.CreateIntegration(data.Name, data.Code)
|
||||||
|
|
||||||
|
if error != nil {
|
||||||
|
SendError(400, context, fmt.Sprintf("Failed to create integration: %s", error))
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.JSON(200, integration)
|
||||||
|
}
|
||||||
|
|
||||||
//func (r DeviceApiHandler) handleRegister(context echo.Context) error {
|
//func (r DeviceApiHandler) handleRegister(context echo.Context) error {
|
||||||
// var registrationData struct {
|
// var registrationData struct {
|
||||||
// Code string `json:"code"`
|
// Code string `json:"code"`
|
||||||
|
|||||||
92
www/integration.html
Normal file
92
www/integration.html
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" data-bs-theme="dark">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<title>Integration</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div style="padding: 1em; border: 1px solid #444">
|
||||||
|
<p style="font-weight: bold">Register</p>
|
||||||
|
<label for="register-name">Name:</label>
|
||||||
|
<input type="text" id="register-name" name="register-name" required />
|
||||||
|
<label for="register-code">Code:</label>
|
||||||
|
<input type="text" id="register-code" name="register-code" required />
|
||||||
|
<button id="register-button">Register</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="module">
|
||||||
|
let registerButton = document.getElementById("register-button");
|
||||||
|
registerButton.addEventListener("click", register);
|
||||||
|
async function register() {
|
||||||
|
let name = document.getElementById("register-name").value;
|
||||||
|
let code = document.getElementById("register-code").value;
|
||||||
|
console.log("Registering with name:", name, "and code:", code);
|
||||||
|
let response = await sendJsonRequest({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/integrations",
|
||||||
|
data: { name, code },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
console.log("Registration successful");
|
||||||
|
console.log(JSON.parse(response.data));
|
||||||
|
} else {
|
||||||
|
console.error("Registration failed with status code:", response.status);
|
||||||
|
console.log(JSON.parse(response.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendJsonRequest({ method, url, data, headers }) {
|
||||||
|
if (typeof data !== "string") {
|
||||||
|
data = JSON.stringify(data);
|
||||||
|
}
|
||||||
|
if (!headers) headers = {};
|
||||||
|
headers["Content-Type"] = "application/json";
|
||||||
|
return sendRequest({ method, url, data, headers });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendRequest({ method, url, data, headers, queryParams }) {
|
||||||
|
if (queryParams) {
|
||||||
|
let params = new URLSearchParams();
|
||||||
|
for (var queryParam in queryParams) {
|
||||||
|
params.append(queryParam, queryParams[queryParam]);
|
||||||
|
}
|
||||||
|
let queryString = params.toString();
|
||||||
|
queryString = queryString ? "?" + queryString : "";
|
||||||
|
url = url + queryString;
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.addEventListener("load", function () {
|
||||||
|
resolve({ status: xhr.status, data: xhr.response });
|
||||||
|
});
|
||||||
|
|
||||||
|
xhr.addEventListener("readystatechange", function () {
|
||||||
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
|
resolve({ status: xhr.status, data: xhr.response });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
xhr.addEventListener("error", function () {
|
||||||
|
reject(xhr.response);
|
||||||
|
});
|
||||||
|
|
||||||
|
xhr.open(method, url, true);
|
||||||
|
for (var header in headers) {
|
||||||
|
xhr.setRequestHeader(header, headers[header]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
xhr.send(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -5,7 +5,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "rm -rf dist; vite build && cp -r src/lib dist",
|
"build": "rm -rf dist; vite build && cp -r src/lib dist; cp integration.html dist",
|
||||||
"serve": "vite preview"
|
"serve": "vite preview"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user