Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
18a6bef20a | ||
![]() |
7b99b75def | ||
![]() |
48e433ff1e | ||
![]() |
f3d43bbd65 | ||
![]() |
186275d1dd | ||
![]() |
8068f82f13 | ||
![]() |
c4f2006e8f | ||
![]() |
7b50441da2 | ||
![]() |
940f6ea1b5 | ||
![]() |
a48d1238a2 | ||
![]() |
7aaa0c0aab | ||
![]() |
00bf392931 | ||
![]() |
7d2ad1cd49 | ||
![]() |
307b467ac1 | ||
![]() |
995bf90efb | ||
![]() |
100bab01e4 | ||
![]() |
2f3ccf14db |
@ -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
|
3
.gitignore
vendored
3
.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
|
||||||
@ -104,3 +102,4 @@ flake.lock
|
|||||||
# Before adding new lines, see the comment at the top.
|
# Before adding new lines, see the comment at the top.
|
||||||
|
|
||||||
.env*
|
.env*
|
||||||
|
.idea/
|
||||||
|
@ -85,6 +85,7 @@ func makePostReq(url string, body any) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Authorization", token)
|
req.Header.Set("Authorization", token)
|
||||||
|
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 {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -119,8 +120,8 @@ func main() {
|
|||||||
Action: test,
|
Action: test,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "get",
|
Name: "open",
|
||||||
Usage: "get door state",
|
Usage: "get door state (opened or closed)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "raw",
|
Name: "raw",
|
||||||
@ -200,7 +201,7 @@ func getOpened(ctx context.Context, cmd *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
opened, err := makeGetReq(fmt.Sprintf("%s/read", server))
|
opened, err := makeGetReq(fmt.Sprintf("%s/open", server))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -244,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
|
||||||
}
|
}
|
||||||
@ -305,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
|
||||||
}
|
}
|
||||||
|
2
peripheral/.gitignore
vendored
2
peripheral/.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
|
__pycache__/
|
||||||
|
|
||||||
.env.json
|
.env.json
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import gc
|
||||||
import utime
|
import utime
|
||||||
import ujson
|
import ujson
|
||||||
import network
|
import network
|
||||||
@ -7,7 +8,6 @@ from machine import Pin
|
|||||||
THRESHOLD_DISTANCE = 15 # cm
|
THRESHOLD_DISTANCE = 15 # cm
|
||||||
SOUND_SPEED = 340 * 100 # m/s * centi = cm/s
|
SOUND_SPEED = 340 * 100 # m/s * centi = cm/s
|
||||||
MAX_CONNECTION_RETRIES = 50
|
MAX_CONNECTION_RETRIES = 50
|
||||||
ULTRA_OPENED_THRESHOLD = 3
|
|
||||||
|
|
||||||
|
|
||||||
class App:
|
class App:
|
||||||
@ -18,7 +18,7 @@ class App:
|
|||||||
|
|
||||||
opened: bool = False
|
opened: bool = False
|
||||||
previously_opened: bool = False
|
previously_opened: bool = False
|
||||||
ultra_opened_counter: int = 0
|
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 +57,18 @@ class App:
|
|||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
print("Connecting to Wi-Fi...")
|
print("Connecting to Wi-Fi...")
|
||||||
wlan = network.WLAN(network.STA_IF)
|
self.wlan = network.WLAN(network.STA_IF)
|
||||||
|
|
||||||
wlan.active(False)
|
self.wlan.active(False)
|
||||||
utime.sleep_ms(250)
|
utime.sleep_ms(250)
|
||||||
wlan.active(True)
|
self.wlan.active(True)
|
||||||
utime.sleep_ms(250)
|
utime.sleep_ms(250)
|
||||||
|
|
||||||
wlan.connect(self.ssid, self.password)
|
self.wlan.connect(self.ssid, self.password)
|
||||||
utime.sleep_ms(100)
|
utime.sleep_ms(100)
|
||||||
|
|
||||||
retry_count = 0
|
retry_count = 0
|
||||||
while not wlan.isconnected():
|
while not self.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,12 +82,12 @@ class App:
|
|||||||
|
|
||||||
if retry_count % 10 == 0:
|
if retry_count % 10 == 0:
|
||||||
print("Attempting to restart connection...")
|
print("Attempting to restart connection...")
|
||||||
wlan.connect(self.ssid, self.password)
|
self.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 {wlan.ifconfig()[0]}")
|
print(f"Connected with IP {self.wlan.ifconfig()[0]}")
|
||||||
self.update_server()
|
self.update_server()
|
||||||
|
|
||||||
def update_server(self):
|
def update_server(self):
|
||||||
@ -101,28 +101,36 @@ class App:
|
|||||||
headers={"Authorization": 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}]")
|
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):
|
def measure_distance(self) -> float:
|
||||||
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} cm")
|
print(f"Distance: {distance:0<5} cm; mem_free = {gc.mem_free()}")
|
||||||
|
|
||||||
return distance
|
return distance
|
||||||
|
|
||||||
@ -133,36 +141,43 @@ 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
|
||||||
|
|
||||||
if not opened:
|
if not self.opened:
|
||||||
self.led.low()
|
self.led.low()
|
||||||
self.ultra_opened_counter = 0
|
|
||||||
|
|
||||||
|
# was it just closed?
|
||||||
if self.previously_opened:
|
if self.previously_opened:
|
||||||
self.update_server()
|
self.update_server()
|
||||||
|
self.previously_opened = False
|
||||||
else:
|
else:
|
||||||
self.led.high()
|
# was it just opened? +wait delay
|
||||||
self.ultra_opened_counter += 1
|
if not self.previously_opened:
|
||||||
|
self.led.high()
|
||||||
if self.ultra_opened_counter >= ULTRA_OPENED_THRESHOLD:
|
|
||||||
self.update_server()
|
self.update_server()
|
||||||
|
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()
|
||||||
else:
|
else:
|
||||||
utime.sleep_ms(500)
|
utime.sleep_ms(500)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Fatal exception occurred: {e}")
|
print(f"Fatal error occurred: {e}")
|
||||||
|
|
||||||
self.previously_opened = self.opened
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
App().run()
|
App().run()
|
||||||
|
@ -73,6 +73,10 @@ 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",
|
||||||
@ -130,7 +134,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if data.Opened == opened {
|
if data.Opened == opened {
|
||||||
mut.Unlock()
|
|
||||||
return c.NoContent(http.StatusOK)
|
return c.NoContent(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +217,8 @@ func main() {
|
|||||||
|
|
||||||
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 {
|
||||||
if c.Request().Header.Get("Authorization") != token {
|
provided := c.Request().Header.Get("Authorization")
|
||||||
|
if provided != fmt.Sprintf("Bearer %s", token) {
|
||||||
return c.NoContent(http.StatusUnauthorized)
|
return c.NoContent(http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
web/.gitignore
vendored
Normal file
24
web/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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
|
6
web/.prettierrc
Normal file
6
web/.prettierrc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 4,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": false
|
||||||
|
}
|
9
web/app.vue
Normal file
9
web/app.vue
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<UApp>
|
||||||
|
<NuxtPage />
|
||||||
|
</UApp>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import "assets/css/main.css";
|
||||||
|
</style>
|
2
web/assets/css/main.css
Normal file
2
web/assets/css/main.css
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@import "@nuxt/ui";
|
3
web/assets/css/tailwind.css
Normal file
3
web/assets/css/tailwind.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
2004
web/bun.lock
Normal file
2004
web/bun.lock
Normal file
File diff suppressed because it is too large
Load Diff
3
web/composables/useToken.ts
Normal file
3
web/composables/useToken.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function useToken() {
|
||||||
|
return useCookie<string | undefined>("token");
|
||||||
|
}
|
6
web/nuxt.config.ts
Normal file
6
web/nuxt.config.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// 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"],
|
||||||
|
});
|
25
web/package.json
Normal file
25
web/package.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"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",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
100
web/pages/index.vue
Normal file
100
web/pages/index.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<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="() => (locked = !locked)"
|
||||||
|
>
|
||||||
|
{{ 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="() => (alert = !alert)"
|
||||||
|
>
|
||||||
|
{{ alert ? "Turn off" : "Turn on" }}
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const token = useToken()
|
||||||
|
const open = ref(true)
|
||||||
|
const locked = ref(true)
|
||||||
|
const alert = ref(false)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!token.value) {
|
||||||
|
return navigateTo("/token")
|
||||||
|
}
|
||||||
|
console.log(token.value)
|
||||||
|
})
|
||||||
|
</script>
|
75
web/pages/token.vue
Normal file
75
web/pages/token.vue
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<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"
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||||
|
const received = event.data.token
|
||||||
|
await $fetch("https://door.svitan.dev/open", {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${received}`,
|
||||||
|
},
|
||||||
|
async onRequestError({ error }) {
|
||||||
|
toast.add({
|
||||||
|
title: "Error occurred",
|
||||||
|
description: error.message,
|
||||||
|
color: "error",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async onResponse({ response }) {
|
||||||
|
if (response.status === 200) {
|
||||||
|
toast.add({ title: "Token saved", color: "success" })
|
||||||
|
token.value = received
|
||||||
|
navigateTo("/")
|
||||||
|
} else if (response.status === 401) {
|
||||||
|
toast.add({ title: "Token not valid", color: "error" })
|
||||||
|
} else {
|
||||||
|
toast.add({
|
||||||
|
title: "Error occurred",
|
||||||
|
description: await response.text(),
|
||||||
|
color: "error",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (token.value) {
|
||||||
|
return navigateTo("/")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
BIN
web/public/favicon.ico
Normal file
BIN
web/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 264 KiB |
2
web/public/robots.txt
Normal file
2
web/public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-Agent: *
|
||||||
|
Disallow: /
|
3
web/server/tsconfig.json
Normal file
3
web/server/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "../.nuxt/tsconfig.server.json"
|
||||||
|
}
|
11
web/tailwind.config.js
Normal file
11
web/tailwind.config.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = {
|
||||||
|
purge: [],
|
||||||
|
darkMode: "class", // or 'media' or 'class'
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
4
web/tsconfig.json
Normal file
4
web/tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user