Compare commits
No commits in common. "main" and "v1.1.2" have entirely different histories.
@ -3,7 +3,7 @@ run-name: ${{ gitea.actor }} build
|
|||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-go:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@ -21,21 +21,3 @@ 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,6 +85,8 @@ __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:"alerts"`
|
Alert bool `json:"alert"`
|
||||||
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", fmt.Sprintf("Bearer %s", token))
|
req.Header.Set("Authorization", 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", fmt.Sprintf("Bearer %s", token))
|
req.Header.Set("Authorization", 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: "opened",
|
Name: "open",
|
||||||
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,38 +131,34 @@ func main() {
|
|||||||
Action: getOpened,
|
Action: getOpened,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "locked",
|
Name: "lock",
|
||||||
Usage: "change lock status",
|
Usage: "change lock status",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "yes",
|
Name: "on",
|
||||||
Usage: "lock the door",
|
Usage: "lock the door",
|
||||||
Aliases: []string{"y", "on", "1", "do"},
|
|
||||||
Action: createManageLock(true),
|
Action: createManageLock(true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "no",
|
Name: "off",
|
||||||
Usage: "unlock the door",
|
Usage: "unlock the door",
|
||||||
Aliases: []string{"n", "off", "0", "undo"},
|
|
||||||
Action: createManageLock(false),
|
Action: createManageLock(false),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: getLocked,
|
Action: getLocked,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "alerts",
|
Name: "alert",
|
||||||
Usage: "change alert status",
|
Usage: "change alert status",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "yes",
|
Name: "on",
|
||||||
Usage: "resume alerts",
|
Usage: "resume alerts",
|
||||||
Aliases: []string{"y", "on", "1", "do"},
|
|
||||||
Action: createManageAlert(true),
|
Action: createManageAlert(true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "no",
|
Name: "off",
|
||||||
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",
|
||||||
@ -199,13 +195,13 @@ func test(context.Context, *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOpened(_ context.Context, cmd *cli.Command) error {
|
func getOpened(ctx 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/opened", server))
|
opened, err := makeGetReq(fmt.Sprintf("%s/open", server))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -229,7 +225,7 @@ func getLocked(context.Context, *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
locked, err := makeGetReq(fmt.Sprintf("%s/locked", server))
|
locked, err := makeGetReq(fmt.Sprintf("%s/lock", server))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -249,7 +245,7 @@ func getAlert(context.Context, *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
alert, err := makeGetReq(fmt.Sprintf("%s/alerts", server))
|
alert, err := makeGetReq(fmt.Sprintf("%s/alert", server))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -273,7 +269,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/locked", server), data)
|
_, err = makePostReq(fmt.Sprintf("%s/lock", server), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -310,7 +306,7 @@ func createManageAlert(alert bool) func(context.Context, *cli.Command) error {
|
|||||||
data.For = secs
|
data.For = secs
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = makePostReq(fmt.Sprintf("%s/alerts", server), data)
|
_, err = makePostReq(fmt.Sprintf("%s/alert", server), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -326,7 +322,7 @@ func createManageAlert(alert bool) func(context.Context, *cli.Command) error {
|
|||||||
action = "paused"
|
action = "paused"
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("alerts were %s%s\n", action, rest)
|
fmt.Printf("alerts were %sd%s\n", action, rest)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import gc
|
|
||||||
import utime
|
import utime
|
||||||
import ujson
|
import ujson
|
||||||
import network
|
import network
|
||||||
@ -18,7 +17,6 @@ class App:
|
|||||||
|
|
||||||
opened: bool = False
|
opened: bool = False
|
||||||
previously_opened: bool = False
|
previously_opened: bool = False
|
||||||
wlan: network.WLAN
|
|
||||||
|
|
||||||
led = Pin(15, Pin.OUT)
|
led = Pin(15, Pin.OUT)
|
||||||
trigger = Pin(2, Pin.OUT)
|
trigger = Pin(2, Pin.OUT)
|
||||||
@ -57,18 +55,18 @@ class App:
|
|||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
print("Connecting to Wi-Fi...")
|
print("Connecting to Wi-Fi...")
|
||||||
self.wlan = network.WLAN(network.STA_IF)
|
wlan = network.WLAN(network.STA_IF)
|
||||||
|
|
||||||
self.wlan.active(False)
|
wlan.active(False)
|
||||||
utime.sleep_ms(250)
|
utime.sleep_ms(250)
|
||||||
self.wlan.active(True)
|
wlan.active(True)
|
||||||
utime.sleep_ms(250)
|
utime.sleep_ms(250)
|
||||||
|
|
||||||
self.wlan.connect(self.ssid, self.password)
|
wlan.connect(self.ssid, self.password)
|
||||||
utime.sleep_ms(100)
|
utime.sleep_ms(100)
|
||||||
|
|
||||||
retry_count = 0
|
retry_count = 0
|
||||||
while not self.wlan.isconnected():
|
while not wlan.isconnected():
|
||||||
if retry_count >= MAX_CONNECTION_RETRIES:
|
if retry_count >= MAX_CONNECTION_RETRIES:
|
||||||
print("Max connection retries reached")
|
print("Max connection retries reached")
|
||||||
exit(1)
|
exit(1)
|
||||||
@ -82,14 +80,22 @@ class App:
|
|||||||
|
|
||||||
if retry_count % 10 == 0:
|
if retry_count % 10 == 0:
|
||||||
print("Attempting to restart connection...")
|
print("Attempting to restart connection...")
|
||||||
self.wlan.connect(self.ssid, self.password)
|
wlan.connect(self.ssid, self.password)
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
self.led.toggle()
|
self.led.toggle()
|
||||||
utime.sleep_ms(50)
|
utime.sleep_ms(50)
|
||||||
|
|
||||||
print(f"Connected with IP {self.wlan.ifconfig()[0]}")
|
print(f"Connected with IP {wlan.ifconfig()[0]}")
|
||||||
self.update_server()
|
self.update_server()
|
||||||
|
|
||||||
|
def health_check_server(self):
|
||||||
|
print("Health checking server...", end="\r")
|
||||||
|
try:
|
||||||
|
r = urequests.get(f"{self.server}/")
|
||||||
|
print(f"Server healthy [{r.status_code}]{" " * 8}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error occurred: {e}")
|
||||||
|
|
||||||
def update_server(self):
|
def update_server(self):
|
||||||
print("Updating state...", end="\r")
|
print("Updating state...", end="\r")
|
||||||
data = {"opened": self.opened}
|
data = {"opened": self.opened}
|
||||||
@ -97,40 +103,32 @@ class App:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
r = urequests.post(
|
r = urequests.post(
|
||||||
f"{self.server}/opened",
|
f"{self.server}/open",
|
||||||
headers={"Authorization": f"Bearer {self.token}", "Content-Type": "application/json"},
|
headers={"Authorization": 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()}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error occurred: {e}")
|
print(f"Error occurred: {e}")
|
||||||
|
|
||||||
def measure_distance(self) -> float:
|
def measure_distance(self):
|
||||||
self.trigger.low()
|
self.trigger.low()
|
||||||
utime.sleep_us(2)
|
utime.sleep_us(2)
|
||||||
self.trigger.high()
|
self.trigger.high()
|
||||||
utime.sleep_us(10)
|
utime.sleep_us(10)
|
||||||
self.trigger.low()
|
self.trigger.low()
|
||||||
|
|
||||||
start_time = utime.ticks_us()
|
|
||||||
sent_time = utime.ticks_us()
|
sent_time = utime.ticks_us()
|
||||||
while self.echo.value() == 0:
|
while self.echo.value() == 0:
|
||||||
print("hey", end="\r")
|
|
||||||
sent_time = utime.ticks_us()
|
sent_time = utime.ticks_us()
|
||||||
if sent_time - start_time >= 100_000: # if it takes more than 100ms, stop
|
|
||||||
return -1
|
|
||||||
|
|
||||||
start_time = utime.ticks_us()
|
|
||||||
received_time = utime.ticks_us()
|
received_time = utime.ticks_us()
|
||||||
while self.echo.value() == 1:
|
while self.echo.value() == 1:
|
||||||
print("ho", end="\r")
|
|
||||||
received_time = utime.ticks_us()
|
received_time = utime.ticks_us()
|
||||||
if received_time - start_time >= 100_000: # same
|
|
||||||
return -1
|
|
||||||
|
|
||||||
delta_time = received_time - sent_time
|
delta_time = received_time - sent_time
|
||||||
distance = delta_time / 1_000_000 * SOUND_SPEED / 2
|
distance = delta_time / 1_000_000 * SOUND_SPEED / 2
|
||||||
print(f"Distance: {distance:0<5} cm; mem_free = {gc.mem_free()}")
|
print(f"Distance: {distance} cm")
|
||||||
|
|
||||||
return distance
|
return distance
|
||||||
|
|
||||||
@ -141,9 +139,6 @@ class App:
|
|||||||
i = 0
|
i = 0
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
if not self.wlan.isconnected():
|
|
||||||
self.connect()
|
|
||||||
|
|
||||||
distance = self.measure_distance()
|
distance = self.measure_distance()
|
||||||
self.opened = distance >= THRESHOLD_DISTANCE
|
self.opened = distance >= THRESHOLD_DISTANCE
|
||||||
|
|
||||||
@ -162,14 +157,13 @@ class App:
|
|||||||
self.previously_opened = True
|
self.previously_opened = True
|
||||||
|
|
||||||
if i >= 20:
|
if i >= 20:
|
||||||
print("Blinking...")
|
|
||||||
self.led.toggle()
|
self.led.toggle()
|
||||||
utime.sleep_ms(100)
|
utime.sleep_ms(100)
|
||||||
self.led.toggle()
|
self.led.toggle()
|
||||||
utime.sleep_ms(400)
|
utime.sleep_ms(400)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
gc.collect()
|
self.health_check_server()
|
||||||
else:
|
else:
|
||||||
utime.sleep_ms(500)
|
utime.sleep_ms(500)
|
||||||
|
|
||||||
@ -178,6 +172,5 @@ class App:
|
|||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
App().run()
|
App().run()
|
||||||
|
@ -9,7 +9,6 @@ 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,7 +1,5 @@
|
|||||||
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=
|
||||||
|
128
server/main.go
128
server/main.go
@ -23,14 +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 = false
|
var opened bool
|
||||||
var openedChange = make(chan any)
|
|
||||||
|
|
||||||
// is door locked
|
// is door locked
|
||||||
var locked = false
|
var locked bool = false
|
||||||
|
|
||||||
// alerts the user when door locked and but open?
|
// alert the user when door locked and but open?
|
||||||
var alerts = false
|
var alert bool = false
|
||||||
var gotifyToken string
|
var gotifyToken string
|
||||||
var gotifyURL string
|
var gotifyURL string
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ type ChangeLockReq struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChangeAlertReq struct {
|
type ChangeAlertReq struct {
|
||||||
Alerts bool `json:"alerts"`
|
Alert bool `json:"alert"`
|
||||||
For int `json:"for"`
|
For int `json:"for"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,15 +52,15 @@ func main() {
|
|||||||
_ = godotenv.Load()
|
_ = godotenv.Load()
|
||||||
token = os.Getenv("TOKEN")
|
token = os.Getenv("TOKEN")
|
||||||
|
|
||||||
alertsRaw := strings.ToLower(os.Getenv("USE_ALERTS"))
|
alertRaw := strings.ToLower(os.Getenv("USE_ALERTS"))
|
||||||
alerts = alertsRaw == "true" || alertsRaw == "t" || alertsRaw == "1" || alertsRaw == "y" || alertsRaw == "yes"
|
alert = alertRaw == "true" || alertRaw == "t" || alertRaw == "1" || alertRaw == "y" || alertRaw == "yes"
|
||||||
|
|
||||||
gotifyToken = os.Getenv("GOTIFY_TOKEN")
|
gotifyToken = os.Getenv("GOTIFY_TOKEN")
|
||||||
gotifyURL = os.Getenv("GOTIFY_URL")
|
gotifyURL = os.Getenv("GOTIFY_URL")
|
||||||
if alerts && gotifyToken == "" {
|
if alert && 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 alerts && gotifyURL == "" {
|
if alert && 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,10 +73,6 @@ func main() {
|
|||||||
LogLevel: log.ERROR,
|
LogLevel: log.ERROR,
|
||||||
}))
|
}))
|
||||||
app.Use(middleware.Secure())
|
app.Use(middleware.Secure())
|
||||||
app.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
|
||||||
AllowOrigins: []string{"*"},
|
|
||||||
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAuthorization},
|
|
||||||
}))
|
|
||||||
|
|
||||||
app.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
|
app.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
|
||||||
Format: "${time_custom} ${method} ${uri} ---> ${status} in ${latency_human} (${bytes_out} bytes)\n",
|
Format: "${time_custom} ${method} ${uri} ---> ${status} in ${latency_human} (${bytes_out} bytes)\n",
|
||||||
@ -115,25 +110,110 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.GET("/", helloWorld)
|
app.GET("/", func(c echo.Context) error {
|
||||||
|
return c.JSON(http.StatusOK, "Hello world!")
|
||||||
|
})
|
||||||
|
|
||||||
app.GET("/opened", authed(getOpened))
|
app.GET("/open", authed(func(c echo.Context) error {
|
||||||
app.GET("/opened/ws", authed(getOpenedWs))
|
mut.Lock()
|
||||||
app.POST("/opened", authed(setOpened))
|
o := opened
|
||||||
|
mut.Unlock()
|
||||||
|
|
||||||
app.GET("/locked", authed(getLocked))
|
return c.JSON(http.StatusOK, o)
|
||||||
app.POST("/locked", authed(setLocked))
|
}))
|
||||||
|
|
||||||
app.GET("/alerts", authed(getAlerts))
|
app.POST("/open", authed(func(c echo.Context) error {
|
||||||
app.POST("/alerts", authed(setAlerts))
|
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 && 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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func authed(next echo.HandlerFunc) echo.HandlerFunc {
|
func authed(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
provided := c.Request().Header.Get("Authorization")
|
if c.Request().Header.Get("Authorization") != token {
|
||||||
if provided != fmt.Sprintf("Bearer %s", token) {
|
|
||||||
return c.NoContent(http.StatusUnauthorized)
|
return c.NoContent(http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
146
server/routes.go
146
server/routes.go
@ -1,146 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
"github.com/labstack/gommon/log"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{}
|
|
||||||
|
|
||||||
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 getOpenedWs(c echo.Context) error {
|
|
||||||
ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() { _ = ws.Close() }()
|
|
||||||
|
|
||||||
// empty the channel
|
|
||||||
emptied := false
|
|
||||||
for !emptied {
|
|
||||||
select {
|
|
||||||
case <-openedChange:
|
|
||||||
default:
|
|
||||||
emptied = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
<-openedChange
|
|
||||||
mut.Lock()
|
|
||||||
|
|
||||||
err = ws.WriteJSON(opened)
|
|
||||||
mut.Unlock()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
openedChange <- 0
|
|
||||||
}()
|
|
||||||
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)
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
.idea/
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
node_modules/
|
|
||||||
.output/
|
|
||||||
.data/
|
|
||||||
.nuxt/
|
|
||||||
.cache/
|
|
||||||
|
|
||||||
.env*
|
|
24
web/.gitignore
vendored
24
web/.gitignore
vendored
@ -1,24 +0,0 @@
|
|||||||
# Nuxt dev/build outputs
|
|
||||||
.output
|
|
||||||
.data
|
|
||||||
.nuxt
|
|
||||||
.nitro
|
|
||||||
.cache
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Node dependencies
|
|
||||||
node_modules
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
|
|
||||||
# Misc
|
|
||||||
.DS_Store
|
|
||||||
.fleet
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Local env files
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"trailingComma": "es5",
|
|
||||||
"tabWidth": 4,
|
|
||||||
"semi": false,
|
|
||||||
"singleQuote": false
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
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,9 +0,0 @@
|
|||||||
<template>
|
|
||||||
<UApp>
|
|
||||||
<NuxtPage />
|
|
||||||
</UApp>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
@import "assets/css/main.css";
|
|
||||||
</style>
|
|
@ -1,2 +0,0 @@
|
|||||||
@import "tailwindcss";
|
|
||||||
@import "@nuxt/ui";
|
|
@ -1,3 +0,0 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
2027
web/bun.lock
2027
web/bun.lock
File diff suppressed because it is too large
Load Diff
@ -1,42 +0,0 @@
|
|||||||
import type { FetchResponse } from "ofetch"
|
|
||||||
import { AxiosError, 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",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
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 +0,0 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
compatibilityDate: "2025-05-15",
|
|
||||||
devtools: { enabled: true },
|
|
||||||
modules: ["@nuxt/icon", "@nuxt/ui", "@nuxtjs/tailwindcss"],
|
|
||||||
});
|
|
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"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,164 +0,0 @@
|
|||||||
<template>
|
|
||||||
<main
|
|
||||||
class="flex items-center justify-center min-w-screen min-h-screen gap-4 flex-wrap"
|
|
||||||
>
|
|
||||||
<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-${open ? (locked ? 'error' : 'secondary') : 'primary'} rounded-lg w-full h-32`"
|
|
||||||
>
|
|
||||||
<UIcon
|
|
||||||
:name="
|
|
||||||
open
|
|
||||||
? 'material-symbols:door-open'
|
|
||||||
: 'material-symbols:door-front'
|
|
||||||
"
|
|
||||||
class="size-12 !w-16 !h-16"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p
|
|
||||||
:class="`flex items-center justify-center text-${open ? (locked ? 'error' : 'secondary') : 'primary'} text-xl w-full h-10`"
|
|
||||||
>
|
|
||||||
{{ open ? "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-${alert ? 'primary' : 'secondary'} rounded-lg w-full h-32`"
|
|
||||||
>
|
|
||||||
<UIcon
|
|
||||||
:name="
|
|
||||||
alert
|
|
||||||
? 'material-symbols:volume-up'
|
|
||||||
: 'material-symbols:volume-off'
|
|
||||||
"
|
|
||||||
class="size-12 !w-16 !h-16"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<UButton
|
|
||||||
:icon="
|
|
||||||
alert
|
|
||||||
? 'material-symbols:volume-off'
|
|
||||||
: 'material-symbols:volume-up'
|
|
||||||
"
|
|
||||||
size="xl"
|
|
||||||
:color="alert ? 'primary' : 'secondary'"
|
|
||||||
variant="solid"
|
|
||||||
class="flex items-center justify-center w-full h-10"
|
|
||||||
@click="toggleAlert"
|
|
||||||
>
|
|
||||||
{{ alert ? "Turn off" : "Turn on" }}
|
|
||||||
</UButton>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import axios from "axios"
|
|
||||||
|
|
||||||
const token = useToken()
|
|
||||||
|
|
||||||
const open = ref(true)
|
|
||||||
const locked = ref(true)
|
|
||||||
const alert = 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 = !alert.value
|
|
||||||
axios
|
|
||||||
.post(useAPI("/alerts"), { alert: desired }, { headers: useHeaders() })
|
|
||||||
.then((res) => {
|
|
||||||
handleResponse(res, () => {
|
|
||||||
toast.add({
|
|
||||||
title: `Alerts were turned ${desired ? "on" : "off"}`,
|
|
||||||
})
|
|
||||||
alert.value = desired
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(handleRequestError)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (!token.value) {
|
|
||||||
return navigateTo("/token")
|
|
||||||
}
|
|
||||||
|
|
||||||
axios
|
|
||||||
.get<boolean>(useAPI("/locked"), {
|
|
||||||
headers: useHeaders(),
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
handleResponse(res, (res) => {
|
|
||||||
locked.value = res.data
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(handleRequestError)
|
|
||||||
|
|
||||||
axios
|
|
||||||
.get<boolean>(useAPI("/alerts"), {
|
|
||||||
headers: useHeaders(),
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
handleResponse(res, (res) => {
|
|
||||||
alert.value = res.data
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(handleRequestError)
|
|
||||||
})
|
|
||||||
</script>
|
|
@ -1,60 +0,0 @@
|
|||||||
<template>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
<UFormField label="Token" name="token" size="xl" required>
|
|
||||||
<UInput
|
|
||||||
v-model="state.token"
|
|
||||||
placeholder="Your token..."
|
|
||||||
size="xl"
|
|
||||||
/>
|
|
||||||
</UFormField>
|
|
||||||
|
|
||||||
<UButton type="submit" size="xl"> Submit </UButton>
|
|
||||||
</UForm>
|
|
||||||
</main>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as v from "valibot"
|
|
||||||
import type { FormSubmitEvent } from "@nuxt/ui"
|
|
||||||
import axios from "axios"
|
|
||||||
|
|
||||||
const token = useToken()
|
|
||||||
|
|
||||||
const schema = v.object({
|
|
||||||
token: v.pipe(v.string(), v.nonEmpty("Please enter your token")),
|
|
||||||
})
|
|
||||||
type Schema = v.InferOutput<typeof schema>
|
|
||||||
|
|
||||||
const state = reactive({
|
|
||||||
token: "",
|
|
||||||
})
|
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
function onSubmit(event: FormSubmitEvent<Schema>) {
|
|
||||||
const received = event.data.token
|
|
||||||
axios
|
|
||||||
.get<boolean>(useAPI("/opened"), {
|
|
||||||
headers: useHeaders(received),
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
handleResponse(res, () => {
|
|
||||||
toast.add({ title: "Token saved", color: "success" })
|
|
||||||
token.value = received
|
|
||||||
navigateTo("/")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(handleRequestError)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (token.value) {
|
|
||||||
return navigateTo("/")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
Binary file not shown.
Before Width: | Height: | Size: 264 KiB |
@ -1,2 +0,0 @@
|
|||||||
User-Agent: *
|
|
||||||
Disallow: /
|
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../.nuxt/tsconfig.server.json"
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
purge: [],
|
|
||||||
darkMode: "class", // or 'media' or 'class'
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
};
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
// https://nuxt.com/docs/guide/concepts/typescript
|
|
||||||
"extends": "./.nuxt/tsconfig.json"
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user