193 lines
4.2 KiB
Go
193 lines
4.2 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
"woke/db"
|
|
)
|
|
|
|
// Client implements db.AlarmStore via HTTP calls to a remote woke server.
|
|
type Client struct {
|
|
baseURL string
|
|
password string
|
|
http *http.Client
|
|
}
|
|
|
|
func NewClient(baseURL, password string) *Client {
|
|
return &Client{
|
|
baseURL: baseURL,
|
|
password: password,
|
|
http: &http.Client{Timeout: 10 * time.Second},
|
|
}
|
|
}
|
|
|
|
func (c *Client) do(method, path string, body any) (*http.Response, error) {
|
|
var reqBody *bytes.Buffer
|
|
if body != nil {
|
|
data, err := json.Marshal(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reqBody = bytes.NewBuffer(data)
|
|
} else {
|
|
reqBody = &bytes.Buffer{}
|
|
}
|
|
|
|
req, err := http.NewRequest(method, c.baseURL+path, reqBody)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
if c.password != "" {
|
|
req.Header.Set("Authorization", "Bearer "+c.password)
|
|
}
|
|
return c.http.Do(req)
|
|
}
|
|
|
|
func (c *Client) ListAlarms() ([]db.Alarm, error) {
|
|
resp, err := c.do("GET", "/api/alarms", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("server returned %d", resp.StatusCode)
|
|
}
|
|
|
|
var alarms []alarmResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&alarms); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]db.Alarm, len(alarms))
|
|
for i, a := range alarms {
|
|
result[i] = a.toAlarm()
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (c *Client) GetAlarm(id int) (db.Alarm, error) {
|
|
resp, err := c.do("GET", fmt.Sprintf("/api/alarms/%d", id), nil)
|
|
if err != nil {
|
|
return db.Alarm{}, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return db.Alarm{}, fmt.Errorf("server returned %d", resp.StatusCode)
|
|
}
|
|
|
|
var a alarmResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&a); err != nil {
|
|
return db.Alarm{}, err
|
|
}
|
|
return a.toAlarm(), nil
|
|
}
|
|
|
|
func (c *Client) CreateAlarm(a db.Alarm) (int, error) {
|
|
req := alarmRequest{
|
|
Name: a.Name,
|
|
Description: a.Description,
|
|
Time: a.Time,
|
|
Trigger: a.Trigger,
|
|
SoundPath: a.SoundPath,
|
|
Enabled: &a.Enabled,
|
|
}
|
|
resp, err := c.do("POST", "/api/alarms", req)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusCreated {
|
|
return 0, fmt.Errorf("server returned %d", resp.StatusCode)
|
|
}
|
|
|
|
var created alarmResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&created); err != nil {
|
|
return 0, err
|
|
}
|
|
return created.ID, nil
|
|
}
|
|
|
|
func (c *Client) UpdateAlarm(a db.Alarm) error {
|
|
req := alarmRequest{
|
|
Name: a.Name,
|
|
Description: a.Description,
|
|
Time: a.Time,
|
|
Trigger: a.Trigger,
|
|
SoundPath: a.SoundPath,
|
|
Enabled: &a.Enabled,
|
|
}
|
|
resp, err := c.do("PUT", fmt.Sprintf("/api/alarms/%d", a.ID), req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("server returned %d", resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) DeleteAlarm(id int) error {
|
|
resp, err := c.do("DELETE", fmt.Sprintf("/api/alarms/%d", id), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusNoContent {
|
|
return fmt.Errorf("server returned %d", resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) ToggleAlarm(id int) error {
|
|
resp, err := c.do("PATCH", fmt.Sprintf("/api/alarms/%d/toggle", id), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("server returned %d", resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) MarkTriggered(id int) error {
|
|
// Client mode doesn't mark triggered — server handles this
|
|
return nil
|
|
}
|
|
|
|
func (a alarmResponse) toAlarm() db.Alarm {
|
|
alarm := db.Alarm{
|
|
ID: a.ID,
|
|
Name: a.Name,
|
|
Description: a.Description,
|
|
Time: a.Time,
|
|
Trigger: a.Trigger,
|
|
SoundPath: a.SoundPath,
|
|
Enabled: a.Enabled,
|
|
SnoozeCount: a.SnoozeCount,
|
|
}
|
|
if t, err := time.Parse("2006-01-02T15:04:05Z", a.CreatedAt); err == nil {
|
|
alarm.CreatedAt = t
|
|
}
|
|
if t, err := time.Parse("2006-01-02T15:04:05Z", a.UpdatedAt); err == nil {
|
|
alarm.UpdatedAt = t
|
|
}
|
|
if a.LastTriggered != nil {
|
|
if t, err := time.Parse("2006-01-02T15:04:05Z", *a.LastTriggered); err == nil {
|
|
alarm.LastTriggered = &t
|
|
}
|
|
}
|
|
return alarm
|
|
}
|