diff --git a/data/device_database.go b/data/device_database.go index 48d1068..a46596b 100644 --- a/data/device_database.go +++ b/data/device_database.go @@ -104,27 +104,31 @@ func (db *DeviceDatabase) DeleteDevice(ID string) error { 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) if err != nil { - return "", "", err - } - - token, err := gonanoid.Nanoid(16) - if err != nil { - return "", "", err + return "", err } hashed_token, err := hashPassword(token) if err != nil { - return "", "", err + return "", err } _, err = db.Connection.Exec("INSERT INTO integrations (id, name, token) VALUES (?, ?, ?)", id, name, hashed_token) 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) { diff --git a/data/integration.go b/data/integration.go new file mode 100644 index 0000000..b62f0a0 --- /dev/null +++ b/data/integration.go @@ -0,0 +1,7 @@ +package data + +type Integration struct { + ID string `json:"id"` + Name string `json:"name"` + Token string `json:"token"` +} diff --git a/management/device_manager.go b/management/device_manager.go index a3c9132..55890cd 100644 --- a/management/device_manager.go +++ b/management/device_manager.go @@ -1,7 +1,9 @@ package management import ( + "fmt" d "playback-device-server/data" + "slices" gonanoid "github.com/matoous/go-nanoid" ) @@ -87,7 +89,68 @@ func (um *DeviceManager) DeleteDevice(ID string) error { } func (um *DeviceManager) GetRegistrationCode() (string, error) { - code := gonanoid.MustGenerate("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 6) - um.registrationCodes = append(um.registrationCodes, code) + code := um.createCode() 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 +} diff --git a/server/device_api_handler.go b/server/device_api_handler.go index 65a0440..f685201 100644 --- a/server/device_api_handler.go +++ b/server/device_api_handler.go @@ -25,6 +25,7 @@ func (r *DeviceApiHandler) Initialize(authenticator *Authenticator) { devicesApi.DELETE("/:id", r.handleDeleteDevice) integrationsApi := r.router.Group("/api/integrations") integrationsApi.GET("/register", r.handleIntegrationRegistration) + integrationsApi.POST("", r.handleCreateIntegration) } 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) } +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 { // var registrationData struct { // Code string `json:"code"` diff --git a/www/integration.html b/www/integration.html new file mode 100644 index 0000000..5cf0bbf --- /dev/null +++ b/www/integration.html @@ -0,0 +1,92 @@ + + + + + + + Integration + + +
+
+

Register

+ + + + + +
+
+ + + diff --git a/www/package.json b/www/package.json index 14ad734..4fb7e69 100644 --- a/www/package.json +++ b/www/package.json @@ -5,7 +5,7 @@ "scripts": { "start": "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" }, "license": "MIT",