Compare commits
19 Commits
940f6ea1b5
...
v1.2.2
Author | SHA1 | Date | |
---|---|---|---|
aac191521f | |||
96fe2d04cb | |||
d858b9e7c8 | |||
4b2248a6d7 | |||
![]() |
53722bdf18 | ||
![]() |
cf35326bf2 | ||
![]() |
cb397b7cd3 | ||
![]() |
4aad657c9d | ||
![]() |
000c12845c | ||
![]() |
ae7db8290c | ||
![]() |
88c31a5133 | ||
![]() |
18a6bef20a | ||
![]() |
7b99b75def | ||
![]() |
48e433ff1e | ||
![]() |
f3d43bbd65 | ||
![]() |
186275d1dd | ||
![]() |
8068f82f13 | ||
![]() |
c4f2006e8f | ||
![]() |
7b50441da2 |
@@ -3,7 +3,7 @@ run-name: ${{ gitea.actor }} build
|
|||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build-go:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -21,3 +21,21 @@ jobs:
|
|||||||
run: cd admin && go get .
|
run: cd admin && go get .
|
||||||
- name: "[admin] Build"
|
- name: "[admin] Build"
|
||||||
run: cd admin && go build -v ./...
|
run: cd admin && go build -v ./...
|
||||||
|
build-nuxt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Bun 1.2.0
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: 1.2.0
|
||||||
|
- name: Display Bun version
|
||||||
|
run: bun --version
|
||||||
|
- name: test 1
|
||||||
|
run: ls
|
||||||
|
- name: test 2
|
||||||
|
run: cd web && ls
|
||||||
|
- name: "[web] Install dependencies"
|
||||||
|
run: cd web && bun install
|
||||||
|
- name: "[web] Build"
|
||||||
|
run: cd web && bun run build
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -85,8 +85,6 @@ __pycache__/
|
|||||||
|
|
||||||
## Node
|
## Node
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
|
||||||
package.json
|
|
||||||
/src/doc/rustc-dev-guide/mermaid.min.js
|
/src/doc/rustc-dev-guide/mermaid.min.js
|
||||||
|
|
||||||
## Rustdoc GUI tests
|
## Rustdoc GUI tests
|
||||||
|
@@ -27,7 +27,7 @@ type ChangeLockReq struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChangeAlertReq struct {
|
type ChangeAlertReq struct {
|
||||||
Alert bool `json:"alert"`
|
Alert bool `json:"alerts"`
|
||||||
For int `json:"for"`
|
For int `json:"for"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,12 +59,12 @@ func makeGetReq(url string) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Authorization", token)
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||||
res, err := http.DefaultClient.Do(req)
|
res, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() { res.Body.Close() }()
|
defer func() { _ = res.Body.Close() }()
|
||||||
|
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
fmt.Printf("<-- %d\n", res.StatusCode)
|
fmt.Printf("<-- %d\n", res.StatusCode)
|
||||||
@@ -84,7 +84,7 @@ func makePostReq(url string, body any) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Authorization", token)
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
res, err := http.DefaultClient.Do(req)
|
res, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -120,7 +120,7 @@ func main() {
|
|||||||
Action: test,
|
Action: test,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "open",
|
Name: "opened",
|
||||||
Usage: "get door state (opened or closed)",
|
Usage: "get door state (opened or closed)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
@@ -131,34 +131,38 @@ func main() {
|
|||||||
Action: getOpened,
|
Action: getOpened,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "lock",
|
Name: "locked",
|
||||||
Usage: "change lock status",
|
Usage: "change lock status",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "on",
|
Name: "yes",
|
||||||
Usage: "lock the door",
|
Usage: "lock the door",
|
||||||
Action: createManageLock(true),
|
Aliases: []string{"y", "on", "1", "do"},
|
||||||
|
Action: createManageLock(true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "off",
|
Name: "no",
|
||||||
Usage: "unlock the door",
|
Usage: "unlock the door",
|
||||||
Action: createManageLock(false),
|
Aliases: []string{"n", "off", "0", "undo"},
|
||||||
|
Action: createManageLock(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: getLocked,
|
Action: getLocked,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "alert",
|
Name: "alerts",
|
||||||
Usage: "change alert status",
|
Usage: "change alert status",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "on",
|
Name: "yes",
|
||||||
Usage: "resume alerts",
|
Usage: "resume alerts",
|
||||||
Action: createManageAlert(true),
|
Aliases: []string{"y", "on", "1", "do"},
|
||||||
|
Action: createManageAlert(true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "off",
|
Name: "no",
|
||||||
Usage: "pause alerts",
|
Usage: "pause alerts",
|
||||||
|
Aliases: []string{"n", "off", "0", "undo"},
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "for",
|
Name: "for",
|
||||||
@@ -195,13 +199,13 @@ func test(context.Context, *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOpened(ctx context.Context, cmd *cli.Command) error {
|
func getOpened(_ context.Context, cmd *cli.Command) error {
|
||||||
err := load()
|
err := load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
opened, err := makeGetReq(fmt.Sprintf("%s/open", server))
|
opened, err := makeGetReq(fmt.Sprintf("%s/opened", server))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -225,7 +229,7 @@ func getLocked(context.Context, *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
locked, err := makeGetReq(fmt.Sprintf("%s/lock", server))
|
locked, err := makeGetReq(fmt.Sprintf("%s/locked", server))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -245,7 +249,7 @@ func getAlert(context.Context, *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
alert, err := makeGetReq(fmt.Sprintf("%s/alert", server))
|
alert, err := makeGetReq(fmt.Sprintf("%s/alerts", server))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -269,7 +273,7 @@ func createManageLock(locked bool) func(context.Context, *cli.Command) error {
|
|||||||
var data ChangeLockReq
|
var data ChangeLockReq
|
||||||
data.Locked = locked
|
data.Locked = locked
|
||||||
|
|
||||||
_, err = makePostReq(fmt.Sprintf("%s/lock", server), data)
|
_, err = makePostReq(fmt.Sprintf("%s/locked", server), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -306,7 +310,7 @@ func createManageAlert(alert bool) func(context.Context, *cli.Command) error {
|
|||||||
data.For = secs
|
data.For = secs
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = makePostReq(fmt.Sprintf("%s/alert", server), data)
|
_, err = makePostReq(fmt.Sprintf("%s/alerts", server), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -322,7 +326,7 @@ func createManageAlert(alert bool) func(context.Context, *cli.Command) error {
|
|||||||
action = "paused"
|
action = "paused"
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("alerts were %sd%s\n", action, rest)
|
fmt.Printf("alerts were %s%s\n", action, rest)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -97,8 +97,8 @@ class App:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
r = urequests.post(
|
r = urequests.post(
|
||||||
f"{self.server}/open",
|
f"{self.server}/opened",
|
||||||
headers={"Authorization": self.token, "Content-Type": "application/json"},
|
headers={"Authorization": f"Bearer {self.token}", "Content-Type": "application/json"},
|
||||||
data=raw
|
data=raw
|
||||||
)
|
)
|
||||||
print(f"State updated [{r.status_code}] {r.content.decode()}")
|
print(f"State updated [{r.status_code}] {r.content.decode()}")
|
||||||
|
@@ -9,6 +9,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||||
|
121
server/main.go
121
server/main.go
@@ -23,13 +23,13 @@ const TimeFormat = "2006-01-02 15:04:05"
|
|||||||
var token string
|
var token string
|
||||||
|
|
||||||
// the condition is: distance >= threshold (is door open)
|
// the condition is: distance >= threshold (is door open)
|
||||||
var opened bool
|
var opened = false
|
||||||
|
|
||||||
// is door locked
|
// is door locked
|
||||||
var locked bool = false
|
var locked = false
|
||||||
|
|
||||||
// alert the user when door locked and but open?
|
// alerts the user when door locked and but open?
|
||||||
var alert bool = false
|
var alerts = false
|
||||||
var gotifyToken string
|
var gotifyToken string
|
||||||
var gotifyURL string
|
var gotifyURL string
|
||||||
|
|
||||||
@@ -44,23 +44,23 @@ type ChangeLockReq struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChangeAlertReq struct {
|
type ChangeAlertReq struct {
|
||||||
Alert bool `json:"alert"`
|
Alerts bool `json:"alerts"`
|
||||||
For int `json:"for"`
|
For int `json:"for"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
_ = godotenv.Load()
|
_ = godotenv.Load()
|
||||||
token = os.Getenv("TOKEN")
|
token = os.Getenv("TOKEN")
|
||||||
|
|
||||||
alertRaw := strings.ToLower(os.Getenv("USE_ALERTS"))
|
alertsRaw := strings.ToLower(os.Getenv("USE_ALERTS"))
|
||||||
alert = alertRaw == "true" || alertRaw == "t" || alertRaw == "1" || alertRaw == "y" || alertRaw == "yes"
|
alerts = alertsRaw == "true" || alertsRaw == "t" || alertsRaw == "1" || alertsRaw == "y" || alertsRaw == "yes"
|
||||||
|
|
||||||
gotifyToken = os.Getenv("GOTIFY_TOKEN")
|
gotifyToken = os.Getenv("GOTIFY_TOKEN")
|
||||||
gotifyURL = os.Getenv("GOTIFY_URL")
|
gotifyURL = os.Getenv("GOTIFY_URL")
|
||||||
if alert && gotifyToken == "" {
|
if alerts && gotifyToken == "" {
|
||||||
log.Fatal("GOTIFY_TOKEN can't be empty when alerts are enabled")
|
log.Fatal("GOTIFY_TOKEN can't be empty when alerts are enabled")
|
||||||
}
|
}
|
||||||
if alert && gotifyURL == "" {
|
if alerts && gotifyURL == "" {
|
||||||
log.Fatal("GOTIFY_URL can't be empty when alerts are enabled")
|
log.Fatal("GOTIFY_URL can't be empty when alerts are enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,103 +114,16 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.GET("/", func(c echo.Context) error {
|
app.GET("/", helloWorld)
|
||||||
return c.JSON(http.StatusOK, "Hello world!")
|
|
||||||
})
|
|
||||||
|
|
||||||
app.GET("/open", authed(func(c echo.Context) error {
|
app.GET("/opened", authed(getOpened))
|
||||||
mut.Lock()
|
app.POST("/opened", authed(setOpened))
|
||||||
o := opened
|
|
||||||
mut.Unlock()
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, o)
|
app.GET("/locked", authed(getLocked))
|
||||||
}))
|
app.POST("/locked", authed(setLocked))
|
||||||
|
|
||||||
app.POST("/open", authed(func(c echo.Context) error {
|
app.GET("/alerts", authed(getAlerts))
|
||||||
var data ChangeOpenReq
|
app.POST("/alerts", authed(setAlerts))
|
||||||
err := c.Bind(&data)
|
|
||||||
if err != nil {
|
|
||||||
return c.NoContent(http.StatusBadRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.Opened == opened {
|
|
||||||
return c.NoContent(http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
mut.Lock()
|
|
||||||
opened = data.Opened
|
|
||||||
if locked && alert {
|
|
||||||
var action string
|
|
||||||
if opened {
|
|
||||||
action = "opened"
|
|
||||||
} else {
|
|
||||||
action = "closed"
|
|
||||||
}
|
|
||||||
|
|
||||||
go sendAlert(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
mut.Unlock()
|
|
||||||
return c.NoContent(http.StatusOK)
|
|
||||||
}))
|
|
||||||
|
|
||||||
app.GET("/lock", authed(func(c echo.Context) error {
|
|
||||||
mut.Lock()
|
|
||||||
l := locked
|
|
||||||
mut.Unlock()
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, l)
|
|
||||||
}))
|
|
||||||
|
|
||||||
app.POST("/lock", authed(func(c echo.Context) error {
|
|
||||||
var data ChangeLockReq
|
|
||||||
err := c.Bind(&data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.Locked == locked {
|
|
||||||
return c.NoContent(http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
mut.Lock()
|
|
||||||
locked = data.Locked
|
|
||||||
mut.Unlock()
|
|
||||||
|
|
||||||
return c.NoContent(http.StatusOK)
|
|
||||||
}))
|
|
||||||
|
|
||||||
app.GET("/alert", authed(func(c echo.Context) error {
|
|
||||||
mut.Lock()
|
|
||||||
a := alert
|
|
||||||
mut.Unlock()
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, a)
|
|
||||||
}))
|
|
||||||
|
|
||||||
app.POST("/alert", authed(func(c echo.Context) error {
|
|
||||||
var data ChangeAlertReq
|
|
||||||
err := c.Bind(&data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.For > 0 {
|
|
||||||
go func() {
|
|
||||||
time.Sleep(time.Duration(data.For) * time.Second)
|
|
||||||
|
|
||||||
mut.Lock()
|
|
||||||
alert = !data.Alert
|
|
||||||
mut.Unlock()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
mut.Lock()
|
|
||||||
alert = data.Alert
|
|
||||||
mut.Unlock()
|
|
||||||
|
|
||||||
return c.NoContent(http.StatusOK)
|
|
||||||
}))
|
|
||||||
|
|
||||||
log.Fatal(app.Start(":1323"))
|
log.Fatal(app.Start(":1323"))
|
||||||
}
|
}
|
||||||
|
108
server/routes.go
Normal file
108
server/routes.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/labstack/gommon/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func helloWorld(c echo.Context) error {
|
||||||
|
return c.JSON(http.StatusOK, "Hello world!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOpened(c echo.Context) error {
|
||||||
|
mut.Lock()
|
||||||
|
o := opened
|
||||||
|
mut.Unlock()
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setOpened(c echo.Context) error {
|
||||||
|
var data ChangeOpenReq
|
||||||
|
err := c.Bind(&data)
|
||||||
|
if err != nil {
|
||||||
|
return c.NoContent(http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Opened == opened {
|
||||||
|
return c.NoContent(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
mut.Lock()
|
||||||
|
opened = data.Opened
|
||||||
|
if locked && alerts {
|
||||||
|
var action string
|
||||||
|
if opened {
|
||||||
|
action = "opened"
|
||||||
|
} else {
|
||||||
|
action = "closed"
|
||||||
|
}
|
||||||
|
|
||||||
|
go sendAlert(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
mut.Unlock()
|
||||||
|
return c.NoContent(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLocked(c echo.Context) error {
|
||||||
|
mut.Lock()
|
||||||
|
l := locked
|
||||||
|
mut.Unlock()
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setLocked(c echo.Context) error {
|
||||||
|
var data ChangeLockReq
|
||||||
|
err := c.Bind(&data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Locked == locked {
|
||||||
|
return c.NoContent(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
mut.Lock()
|
||||||
|
locked = data.Locked
|
||||||
|
mut.Unlock()
|
||||||
|
|
||||||
|
return c.NoContent(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAlerts(c echo.Context) error {
|
||||||
|
mut.Lock()
|
||||||
|
a := alerts
|
||||||
|
mut.Unlock()
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setAlerts(c echo.Context) error {
|
||||||
|
var data ChangeAlertReq
|
||||||
|
err := c.Bind(&data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.For > 0 {
|
||||||
|
log.Infof("pausing alerts for %d seconds", data.For)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Duration(data.For) * time.Second)
|
||||||
|
|
||||||
|
mut.Lock()
|
||||||
|
alerts = !data.Alerts
|
||||||
|
mut.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
mut.Lock()
|
||||||
|
alerts = data.Alerts
|
||||||
|
mut.Unlock()
|
||||||
|
|
||||||
|
return c.NoContent(http.StatusOK)
|
||||||
|
}
|
10
web/.dockerignore
Normal file
10
web/.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
.output/
|
||||||
|
.data/
|
||||||
|
.nuxt/
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
.env*
|
6
web/.prettierrc
Normal file
6
web/.prettierrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 4,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": false
|
||||||
|
}
|
20
web/Dockerfile
Normal file
20
web/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
FROM oven/bun:1 AS build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json bun.lock .
|
||||||
|
RUN bun install --frozen-lockfile --production
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN bun run build
|
||||||
|
|
||||||
|
FROM oven/bun:1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/.output .
|
||||||
|
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV HOST=0.0.0.0
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["bun", "/app/server/index.mjs"]
|
@@ -1,75 +0,0 @@
|
|||||||
# Nuxt Minimal Starter
|
|
||||||
|
|
||||||
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
Make sure to install dependencies:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# npm
|
|
||||||
npm install
|
|
||||||
|
|
||||||
# pnpm
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# yarn
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
# bun
|
|
||||||
bun install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Server
|
|
||||||
|
|
||||||
Start the development server on `http://localhost:3000`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# npm
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# pnpm
|
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# yarn
|
|
||||||
yarn dev
|
|
||||||
|
|
||||||
# bun
|
|
||||||
bun run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## Production
|
|
||||||
|
|
||||||
Build the application for production:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# npm
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
# pnpm
|
|
||||||
pnpm build
|
|
||||||
|
|
||||||
# yarn
|
|
||||||
yarn build
|
|
||||||
|
|
||||||
# bun
|
|
||||||
bun run build
|
|
||||||
```
|
|
||||||
|
|
||||||
Locally preview production build:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# npm
|
|
||||||
npm run preview
|
|
||||||
|
|
||||||
# pnpm
|
|
||||||
pnpm preview
|
|
||||||
|
|
||||||
# yarn
|
|
||||||
yarn preview
|
|
||||||
|
|
||||||
# bun
|
|
||||||
bun run preview
|
|
||||||
```
|
|
||||||
|
|
||||||
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
|
13
web/app.vue
13
web/app.vue
@@ -1,9 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<UApp>
|
<UApp>
|
||||||
<NuxtPage/>
|
<NuxtPage />
|
||||||
</UApp>
|
</UApp>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import "assets/css/main.css";
|
@import "assets/css/main.css";
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
useHead({
|
||||||
|
title: "Door alarm",
|
||||||
|
link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }],
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
32
web/bun.lock
32
web/bun.lock
@@ -7,12 +7,16 @@
|
|||||||
"@nuxt/icon": "1.13.0",
|
"@nuxt/icon": "1.13.0",
|
||||||
"@nuxt/ui": "3.1.3",
|
"@nuxt/ui": "3.1.3",
|
||||||
"@nuxtjs/tailwindcss": "7.0.0-beta.0",
|
"@nuxtjs/tailwindcss": "7.0.0-beta.0",
|
||||||
|
"axios": "^1.9.0",
|
||||||
"nuxt": "^3.17.5",
|
"nuxt": "^3.17.5",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
"valibot": "^1.1.0",
|
"valibot": "^1.1.0",
|
||||||
"vue": "^3.5.16",
|
"vue": "^3.5.16",
|
||||||
"vue-router": "^4.5.1",
|
"vue-router": "^4.5.1",
|
||||||
},
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
@@ -534,8 +538,12 @@
|
|||||||
|
|
||||||
"async-sema": ["async-sema@3.1.1", "", {}, "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg=="],
|
"async-sema": ["async-sema@3.1.1", "", {}, "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg=="],
|
||||||
|
|
||||||
|
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||||
|
|
||||||
"autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
|
"autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
|
||||||
|
|
||||||
|
"axios": ["axios@1.9.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg=="],
|
||||||
|
|
||||||
"b4a": ["b4a@1.6.7", "", {}, "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg=="],
|
"b4a": ["b4a@1.6.7", "", {}, "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg=="],
|
||||||
|
|
||||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||||
@@ -614,6 +622,8 @@
|
|||||||
|
|
||||||
"colortranslator": ["colortranslator@4.1.0", "", {}, "sha512-bwa5awaMnQ6dpm9D3nbsFwUr6x6FrTKmxPdolNtSYfxCNR7ZM93GG1OF5Y3Sy1LvYdalb3riKC9uTn0X5NB36g=="],
|
"colortranslator": ["colortranslator@4.1.0", "", {}, "sha512-bwa5awaMnQ6dpm9D3nbsFwUr6x6FrTKmxPdolNtSYfxCNR7ZM93GG1OF5Y3Sy1LvYdalb3riKC9uTn0X5NB36g=="],
|
||||||
|
|
||||||
|
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||||
|
|
||||||
"commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
"commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||||
|
|
||||||
"common-path-prefix": ["common-path-prefix@3.0.0", "", {}, "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="],
|
"common-path-prefix": ["common-path-prefix@3.0.0", "", {}, "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="],
|
||||||
@@ -692,6 +702,8 @@
|
|||||||
|
|
||||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||||
|
|
||||||
|
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||||
|
|
||||||
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
||||||
|
|
||||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||||
@@ -790,6 +802,8 @@
|
|||||||
|
|
||||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||||
|
|
||||||
|
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
||||||
|
|
||||||
"esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="],
|
"esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="],
|
||||||
|
|
||||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||||
@@ -854,12 +868,16 @@
|
|||||||
|
|
||||||
"fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="],
|
"fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="],
|
||||||
|
|
||||||
|
"follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
|
||||||
|
|
||||||
"fontaine": ["fontaine@0.6.0", "", { "dependencies": { "@capsizecss/metrics": "^3.5.0", "@capsizecss/unpack": "^2.4.0", "css-tree": "^3.1.0", "magic-regexp": "^0.10.0", "magic-string": "^0.30.17", "pathe": "^2.0.3", "ufo": "^1.6.1", "unplugin": "^2.3.2" } }, "sha512-cfKqzB62GmztJhwJ0YXtzNsmpqKAcFzTqsakJ//5COTzbou90LU7So18U+4D8z+lDXr4uztaAUZBonSoPDcj1w=="],
|
"fontaine": ["fontaine@0.6.0", "", { "dependencies": { "@capsizecss/metrics": "^3.5.0", "@capsizecss/unpack": "^2.4.0", "css-tree": "^3.1.0", "magic-regexp": "^0.10.0", "magic-string": "^0.30.17", "pathe": "^2.0.3", "ufo": "^1.6.1", "unplugin": "^2.3.2" } }, "sha512-cfKqzB62GmztJhwJ0YXtzNsmpqKAcFzTqsakJ//5COTzbou90LU7So18U+4D8z+lDXr4uztaAUZBonSoPDcj1w=="],
|
||||||
|
|
||||||
"fontkit": ["fontkit@2.0.4", "", { "dependencies": { "@swc/helpers": "^0.5.12", "brotli": "^1.3.2", "clone": "^2.1.2", "dfa": "^1.2.0", "fast-deep-equal": "^3.1.3", "restructure": "^3.0.0", "tiny-inflate": "^1.0.3", "unicode-properties": "^1.4.0", "unicode-trie": "^2.0.0" } }, "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g=="],
|
"fontkit": ["fontkit@2.0.4", "", { "dependencies": { "@swc/helpers": "^0.5.12", "brotli": "^1.3.2", "clone": "^2.1.2", "dfa": "^1.2.0", "fast-deep-equal": "^3.1.3", "restructure": "^3.0.0", "tiny-inflate": "^1.0.3", "unicode-properties": "^1.4.0", "unicode-trie": "^2.0.0" } }, "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g=="],
|
||||||
|
|
||||||
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||||
|
|
||||||
|
"form-data": ["form-data@4.0.3", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA=="],
|
||||||
|
|
||||||
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||||
|
|
||||||
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
|
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
|
||||||
@@ -916,6 +934,8 @@
|
|||||||
|
|
||||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||||
|
|
||||||
|
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||||
|
|
||||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||||
|
|
||||||
"hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
|
"hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
|
||||||
@@ -1106,9 +1126,9 @@
|
|||||||
|
|
||||||
"mime": ["mime@4.0.7", "", { "bin": { "mime": "bin/cli.js" } }, "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ=="],
|
"mime": ["mime@4.0.7", "", { "bin": { "mime": "bin/cli.js" } }, "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ=="],
|
||||||
|
|
||||||
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||||
|
|
||||||
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
"mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
|
"mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
|
||||||
|
|
||||||
@@ -1310,6 +1330,8 @@
|
|||||||
|
|
||||||
"precinct": ["precinct@12.2.0", "", { "dependencies": { "@dependents/detective-less": "^5.0.1", "commander": "^12.1.0", "detective-amd": "^6.0.1", "detective-cjs": "^6.0.1", "detective-es6": "^5.0.1", "detective-postcss": "^7.0.1", "detective-sass": "^6.0.1", "detective-scss": "^5.0.1", "detective-stylus": "^5.0.1", "detective-typescript": "^14.0.0", "detective-vue2": "^2.2.0", "module-definition": "^6.0.1", "node-source-walk": "^7.0.1", "postcss": "^8.5.1", "typescript": "^5.7.3" }, "bin": { "precinct": "bin/cli.js" } }, "sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w=="],
|
"precinct": ["precinct@12.2.0", "", { "dependencies": { "@dependents/detective-less": "^5.0.1", "commander": "^12.1.0", "detective-amd": "^6.0.1", "detective-cjs": "^6.0.1", "detective-es6": "^5.0.1", "detective-postcss": "^7.0.1", "detective-sass": "^6.0.1", "detective-scss": "^5.0.1", "detective-stylus": "^5.0.1", "detective-typescript": "^14.0.0", "detective-vue2": "^2.2.0", "module-definition": "^6.0.1", "node-source-walk": "^7.0.1", "postcss": "^8.5.1", "typescript": "^5.7.3" }, "bin": { "precinct": "bin/cli.js" } }, "sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w=="],
|
||||||
|
|
||||||
|
"prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="],
|
||||||
|
|
||||||
"pretty-bytes": ["pretty-bytes@6.1.1", "", {}, "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="],
|
"pretty-bytes": ["pretty-bytes@6.1.1", "", {}, "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="],
|
||||||
|
|
||||||
"process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
|
"process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
|
||||||
@@ -1320,6 +1342,8 @@
|
|||||||
|
|
||||||
"protocols": ["protocols@2.0.2", "", {}, "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ=="],
|
"protocols": ["protocols@2.0.2", "", {}, "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ=="],
|
||||||
|
|
||||||
|
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
||||||
|
|
||||||
"pump": ["pump@3.0.2", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw=="],
|
"pump": ["pump@3.0.2", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw=="],
|
||||||
|
|
||||||
"qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
|
"qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
|
||||||
@@ -1798,6 +1822,8 @@
|
|||||||
|
|
||||||
"rollup-plugin-visualizer/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
"rollup-plugin-visualizer/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
||||||
|
|
||||||
|
"send/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||||
|
|
||||||
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
"string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
"string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
@@ -1950,6 +1976,8 @@
|
|||||||
|
|
||||||
"rollup-plugin-visualizer/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
|
"rollup-plugin-visualizer/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
|
||||||
|
|
||||||
|
"send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||||
|
|
||||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
"string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
41
web/composables/useAPI.ts
Normal file
41
web/composables/useAPI.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { type AxiosResponse } from "axios"
|
||||||
|
|
||||||
|
export function useAPI(route: string = "/") {
|
||||||
|
return `https://api.door.svitan.dev${route}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleRequestError(error: unknown) {
|
||||||
|
const toast = useToast()
|
||||||
|
let message = undefined
|
||||||
|
if (error instanceof Error) {
|
||||||
|
message = error.message
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
title: "Error occurred",
|
||||||
|
description: message,
|
||||||
|
color: "error",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleResponse<T extends { toString(): string }>(
|
||||||
|
response: AxiosResponse<T>,
|
||||||
|
success: (response: AxiosResponse<T>) => void = () => {}
|
||||||
|
) {
|
||||||
|
const token = useToken()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
success(response)
|
||||||
|
} else if (response.status === 401) {
|
||||||
|
toast.add({ title: "Token not valid", color: "error" })
|
||||||
|
token.value = ""
|
||||||
|
navigateTo("/token")
|
||||||
|
} else {
|
||||||
|
toast.add({
|
||||||
|
title: "Error occurred",
|
||||||
|
description: response.data.toString(),
|
||||||
|
color: "error",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
10
web/composables/useToken.ts
Normal file
10
web/composables/useToken.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export function useToken() {
|
||||||
|
return useCookie<string | undefined>("token")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useHeaders(override: string | undefined = undefined) {
|
||||||
|
const token = useToken()
|
||||||
|
return {
|
||||||
|
Authorization: `Bearer ${override ?? token.value}`,
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
compatibilityDate: '2025-05-15',
|
compatibilityDate: "2025-05-15",
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true },
|
||||||
modules: ['@nuxt/icon', '@nuxt/ui', '@nuxtjs/tailwindcss']
|
modules: ["@nuxt/icon", "@nuxt/ui", "@nuxtjs/tailwindcss"],
|
||||||
})
|
});
|
||||||
|
26
web/package.json
Normal file
26
web/package.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "nuxt-app",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "nuxt build",
|
||||||
|
"dev": "nuxt dev",
|
||||||
|
"generate": "nuxt generate",
|
||||||
|
"preview": "nuxt preview",
|
||||||
|
"postinstall": "nuxt prepare"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nuxt/icon": "1.13.0",
|
||||||
|
"@nuxt/ui": "3.1.3",
|
||||||
|
"@nuxtjs/tailwindcss": "7.0.0-beta.0",
|
||||||
|
"axios": "^1.9.0",
|
||||||
|
"nuxt": "^3.17.5",
|
||||||
|
"typescript": "^5.6.3",
|
||||||
|
"valibot": "^1.1.0",
|
||||||
|
"vue": "^3.5.16",
|
||||||
|
"vue-router": "^4.5.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.5.3"
|
||||||
|
}
|
||||||
|
}
|
@@ -1,17 +1,181 @@
|
|||||||
<template>
|
<template>
|
||||||
<main class="flex items-center justify-center min-w-screen min-h-screen">
|
<main
|
||||||
<UButton icon="material-symbols:lock" size="xl" color="primary" variant="solid">Lock</UButton>
|
class="flex items-center justify-center min-w-screen min-h-screen gap-4 flex-wrap flex-col md:flex-row"
|
||||||
{{ res }}
|
>
|
||||||
</main>
|
<div class="flex items-center justify-center flex-col gap-y-2 w-32">
|
||||||
|
<div
|
||||||
|
:class="`flex items-center justify-center border-solid border-2 border-${opened ? (locked ? 'error' : 'secondary') : 'primary'} rounded-lg w-full h-32`"
|
||||||
|
>
|
||||||
|
<UIcon
|
||||||
|
:name="
|
||||||
|
opened
|
||||||
|
? 'material-symbols:door-open'
|
||||||
|
: 'material-symbols:door-front'
|
||||||
|
"
|
||||||
|
class="size-12 !w-16 !h-16"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p
|
||||||
|
:class="`flex items-center justify-center text-${opened ? (locked ? 'error' : 'secondary') : 'primary'} text-xl w-full h-10 border-${opened ? (locked ? 'error' : 'secondary') : 'primary'} border-solid border-2 rounded-md`"
|
||||||
|
>
|
||||||
|
{{ opened ? "Opened" : "Closed" }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-center flex-col gap-y-2 w-32">
|
||||||
|
<div
|
||||||
|
:class="`flex items-center justify-center border-solid border-2 border-${locked ? 'primary' : 'secondary'} rounded-lg w-full h-32`"
|
||||||
|
>
|
||||||
|
<UIcon
|
||||||
|
:name="
|
||||||
|
locked
|
||||||
|
? 'material-symbols:lock'
|
||||||
|
: 'material-symbols:lock-open'
|
||||||
|
"
|
||||||
|
class="size-12 !w-16 !h-16"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
:icon="
|
||||||
|
locked
|
||||||
|
? 'material-symbols:lock-open'
|
||||||
|
: 'material-symbols:lock'
|
||||||
|
"
|
||||||
|
size="xl"
|
||||||
|
:color="locked ? 'primary' : 'secondary'"
|
||||||
|
variant="solid"
|
||||||
|
class="flex items-center justify-center w-full h-10"
|
||||||
|
@click="toggleLock"
|
||||||
|
>
|
||||||
|
{{ locked ? "Unlock" : "Lock" }}
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-center flex-col gap-y-2 w-32">
|
||||||
|
<div
|
||||||
|
:class="`flex items-center justify-center border-solid border-2 border-${alerts ? 'primary' : 'secondary'} rounded-lg w-full h-32`"
|
||||||
|
>
|
||||||
|
<UIcon
|
||||||
|
:name="
|
||||||
|
alerts
|
||||||
|
? 'material-symbols:volume-up'
|
||||||
|
: 'material-symbols:volume-off'
|
||||||
|
"
|
||||||
|
class="size-12 !w-16 !h-16"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
:icon="
|
||||||
|
alerts
|
||||||
|
? 'material-symbols:volume-off'
|
||||||
|
: 'material-symbols:volume-up'
|
||||||
|
"
|
||||||
|
size="xl"
|
||||||
|
:color="alerts ? 'primary' : 'secondary'"
|
||||||
|
variant="solid"
|
||||||
|
class="flex items-center justify-center w-full h-10"
|
||||||
|
@click="toggleAlert"
|
||||||
|
>
|
||||||
|
{{ alerts ? "Turn off" : "Turn on" }}
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const token = useCookie("token")
|
import axios from "axios"
|
||||||
|
|
||||||
|
const token = useToken()
|
||||||
|
|
||||||
|
const opened = ref(false)
|
||||||
|
const locked = ref(false)
|
||||||
|
const alerts = ref(false)
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
async function toggleLock() {
|
||||||
|
const desired = !locked.value
|
||||||
|
axios
|
||||||
|
.post(
|
||||||
|
useAPI("/locked"),
|
||||||
|
{
|
||||||
|
locked: desired,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: useHeaders(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
|
handleResponse(res, () => {
|
||||||
|
toast.add({
|
||||||
|
title: `The door was ${desired ? "locked" : "unlocked"}`,
|
||||||
|
})
|
||||||
|
locked.value = desired
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(handleRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleAlert() {
|
||||||
|
const desired = !alerts.value
|
||||||
|
axios
|
||||||
|
.post(useAPI("/alerts"), { alert: desired }, { headers: useHeaders() })
|
||||||
|
.then((res) => {
|
||||||
|
handleResponse(res, () => {
|
||||||
|
toast.add({
|
||||||
|
title: `Alerts were turned ${desired ? "on" : "off"}`,
|
||||||
|
})
|
||||||
|
alerts.value = desired
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(handleRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!token.value) {
|
if (!token.value) {
|
||||||
return navigateTo("/token")
|
return navigateTo("/token")
|
||||||
}
|
}
|
||||||
console.log(token.value)
|
|
||||||
|
setInterval(() => {
|
||||||
|
axios
|
||||||
|
.get<boolean>(useAPI("/opened"), {
|
||||||
|
headers: useHeaders(),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
handleResponse(res, (res) => {
|
||||||
|
opened.value = res.data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(handleRequestError)
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
axios
|
||||||
|
.get<boolean>(useAPI("/locked"), {
|
||||||
|
headers: useHeaders(),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
handleResponse(res, (res) => {
|
||||||
|
locked.value = res.data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(handleRequestError)
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
axios
|
||||||
|
.get<boolean>(useAPI("/alerts"), {
|
||||||
|
headers: useHeaders(),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
handleResponse(res, (res) => {
|
||||||
|
alerts.value = res.data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(handleRequestError)
|
||||||
|
}, 1000)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@@ -1,42 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<main class="flex items-center justify-center min-w-screen min-h-screen">
|
<main class="flex items-center justify-center min-w-screen min-h-screen">
|
||||||
<UForm :schema="schema" :state="state" @submit="onSubmit" class="flex flex-col items-end justify-center gap-y-2">
|
<UForm
|
||||||
|
:schema="schema"
|
||||||
|
:state="state"
|
||||||
|
@submit="onSubmit"
|
||||||
|
class="flex flex-col items-end justify-center gap-y-2"
|
||||||
|
>
|
||||||
<UFormField label="Token" name="token" size="xl" required>
|
<UFormField label="Token" name="token" size="xl" required>
|
||||||
<UInput v-model="state.token" placeholder="Your token..." size="xl"/>
|
<UInput
|
||||||
|
v-model="state.token"
|
||||||
|
placeholder="Your token..."
|
||||||
|
size="xl"
|
||||||
|
/>
|
||||||
</UFormField>
|
</UFormField>
|
||||||
|
|
||||||
<UButton type="submit" size="xl">
|
<UButton type="submit" size="xl"> Submit </UButton>
|
||||||
Submit
|
|
||||||
</UButton>
|
|
||||||
</UForm>
|
</UForm>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import * as v from "valibot"
|
import * as v from "valibot"
|
||||||
import type { FormSubmitEvent } from "@nuxt/ui"
|
import type { FormSubmitEvent } from "@nuxt/ui"
|
||||||
|
import axios from "axios"
|
||||||
|
|
||||||
|
const token = useToken()
|
||||||
|
|
||||||
const schema = v.object({
|
const schema = v.object({
|
||||||
token: v.pipe(v.string(), v.nonEmpty("Please enter your token"))
|
token: v.pipe(v.string(), v.nonEmpty("Please enter your token")),
|
||||||
})
|
})
|
||||||
type Schema = v.InferOutput<typeof schema>
|
type Schema = v.InferOutput<typeof schema>
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
token: ""
|
token: "",
|
||||||
})
|
})
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||||
const token = event.data.token
|
const received = event.data.token
|
||||||
const res = await $fetch("https://door.svitan.dev/open", {
|
axios
|
||||||
method: "GET",
|
.get<boolean>(useAPI("/opened"), {
|
||||||
headers: {
|
headers: useHeaders(received),
|
||||||
Authorization: token
|
})
|
||||||
}
|
.then((res) => {
|
||||||
})
|
handleResponse(res, () => {
|
||||||
|
toast.add({ title: "Token saved", color: "success" })
|
||||||
console.log(token)
|
token.value = received
|
||||||
console.log(res)
|
navigateTo("/")
|
||||||
toast.add({ title: "Token saved", color: "success" })
|
})
|
||||||
|
})
|
||||||
|
.catch(handleRequestError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (token.value) {
|
||||||
|
return navigateTo("/")
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
purge: [],
|
purge: [],
|
||||||
darkMode: "class", // or 'media' or 'class'
|
darkMode: "class", // or 'media' or 'class'
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
};
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
// https://nuxt.com/docs/guide/concepts/typescript
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
"extends": "./.nuxt/tsconfig.json"
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user