add port scan

This commit is contained in:
Maxi Quoß
2023-01-23 00:24:08 +01:00
parent d83f574396
commit 037f718a2e
20 changed files with 524 additions and 390 deletions

View File

@@ -34,11 +34,11 @@ Open up [localhost:5173/](localhost:5173/)
- [ ] form for adding new devices
- [ ] add per device settings
- [x] theme toggle button
- [ ] add device ports to cards
- [x] add device ports to cards
- backend
- [ ] make sure ping works
- [x] make sure ping works
- [ ] make sure arp works
- [ ] make sure wake works
- [ ] remove nmap?

View File

@@ -42,7 +42,17 @@ func RunCron(app *pocketbase.PocketBase) {
// init cronjob
c := cron.New()
c.AddFunc(fmt.Sprintf("@every %s", interval), func() {
// expand ports field
expandFetchFunc := func(c *models.Collection, ids []string) ([]*models.Record, error) {
return app.Dao().FindRecordsByIds(c.Id, ids, nil)
}
merr := app.Dao().ExpandRecords(Devices, []string{"ports"}, expandFetchFunc)
if len(merr) > 0 {
return
}
for _, device := range Devices {
// ping
go func(device *models.Record) {
oldStatus := device.Get("status")
newStatus := networking.PingDevice(device)
@@ -58,6 +68,23 @@ func RunCron(app *pocketbase.PocketBase) {
}
}
}(device)
// scan ports
go func(device *models.Record) {
ports, err := app.Dao().FindRecordsByIds("ports", device.GetStringSlice("ports"))
if err != nil {
logger.Error.Println(err)
}
for _, port := range ports {
isUp := networking.CheckPort(device.GetString("ip"), port.GetString("number"))
if isUp != port.GetBool("status") {
port.Set("status", isUp)
app.Dao().SaveRecord(port)
device.RefreshUpdated()
app.Dao().SaveRecord(device)
}
}
}(device)
}
})
c.Run()

View File

@@ -1,214 +0,0 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models"
)
func init() {
m.Register(func(db dbx.Builder) error {
jsonData := `[
{
"id": "z5lghx2r3tm45n1",
"created": "2023-01-14 21:50:42.797Z",
"updated": "2023-01-14 21:50:42.797Z",
"name": "devices",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "tiqcmnjo",
"name": "name",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "1si6ajha",
"name": "ip",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "fyqmpon6",
"name": "mac",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "s8c5z7n0",
"name": "netmask",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "gdctb8hj",
"name": "link",
"type": "url",
"required": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
},
{
"system": false,
"id": "ilrwvlev",
"name": "port",
"type": "relation",
"required": false,
"unique": false,
"options": {
"maxSelect": null,
"collectionId": "cti4l8f4mz8df3r",
"cascadeDelete": false
}
},
{
"system": false,
"id": "qqvyfrex",
"name": "status",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "1a7yrwo9",
"name": "shutdown_cmd",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}
],
"listRule": "",
"viewRule": "",
"createRule": "",
"updateRule": "",
"deleteRule": "",
"options": {}
},
{
"id": "cti4l8f4mz8df3r",
"created": "2023-01-14 21:50:42.797Z",
"updated": "2023-01-14 21:50:42.797Z",
"name": "ports",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "8nwuncgg",
"name": "number",
"type": "number",
"required": true,
"unique": false,
"options": {
"min": null,
"max": 65535
}
},
{
"system": false,
"id": "o0he3pu6",
"name": "name",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}
],
"listRule": "",
"viewRule": "",
"createRule": "",
"updateRule": "",
"deleteRule": "",
"options": {}
},
{
"id": "nmj3ko20gzkg8n3",
"created": "2023-01-14 21:50:42.797Z",
"updated": "2023-01-14 21:50:42.797Z",
"name": "settings",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "ysutxavs",
"name": "interval",
"type": "number",
"required": true,
"unique": false,
"options": {
"min": 1,
"max": null
}
}
],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
}
]`
collections := []*models.Collection{}
if err := json.Unmarshal([]byte(jsonData), &collections); err != nil {
return err
}
return daos.New(db).ImportCollections(collections, true, nil)
}, func(db dbx.Builder) error {
return nil
})
}

View File

@@ -1,52 +0,0 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("z5lghx2r3tm45n1")
if err != nil {
return err
}
// add
new_wake := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "kzvdbbws",
"name": "wake",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}`), new_wake)
collection.Schema.AddField(new_wake)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("z5lghx2r3tm45n1")
if err != nil {
return err
}
// remove
collection.Schema.RemoveField("kzvdbbws")
return dao.SaveCollection(collection)
})
}

View File

@@ -1,66 +0,0 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("z5lghx2r3tm45n1")
if err != nil {
return err
}
// update
edit_shutdown := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "1a7yrwo9",
"name": "shutdown",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}`), edit_shutdown)
collection.Schema.AddField(edit_shutdown)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("z5lghx2r3tm45n1")
if err != nil {
return err
}
// update
edit_shutdown := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "1a7yrwo9",
"name": "shutdown_cmd",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}`), edit_shutdown)
collection.Schema.AddField(edit_shutdown)
return dao.SaveCollection(collection)
})
}

View File

@@ -0,0 +1,163 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models"
)
func init() {
m.Register(func(db dbx.Builder) error {
jsonData := `{
"id": "z5lghx2r3tm45n1",
"created": "2023-01-22 23:21:44.280Z",
"updated": "2023-01-22 23:21:44.280Z",
"name": "devices",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "tiqcmnjo",
"name": "name",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "1si6ajha",
"name": "ip",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "fyqmpon6",
"name": "mac",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "s8c5z7n0",
"name": "netmask",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "qqvyfrex",
"name": "status",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "ilrwvlev",
"name": "ports",
"type": "relation",
"required": false,
"unique": false,
"options": {
"maxSelect": null,
"collectionId": "cti4l8f4mz8df3r",
"cascadeDelete": false
}
},
{
"system": false,
"id": "gdctb8hj",
"name": "link",
"type": "url",
"required": false,
"unique": false,
"options": {
"exceptDomains": null,
"onlyDomains": null
}
},
{
"system": false,
"id": "1a7yrwo9",
"name": "shutdown",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "kzvdbbws",
"name": "wake",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}
],
"listRule": "",
"viewRule": "",
"createRule": "",
"updateRule": "",
"deleteRule": "",
"options": {}
}`
collection := &models.Collection{}
if err := json.Unmarshal([]byte(jsonData), &collection); err != nil {
return err
}
return daos.New(db).SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("z5lghx2r3tm45n1")
if err != nil {
return err
}
return dao.DeleteCollection(collection)
})
}

View File

@@ -0,0 +1,81 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models"
)
func init() {
m.Register(func(db dbx.Builder) error {
jsonData := `{
"id": "cti4l8f4mz8df3r",
"created": "2023-01-22 23:21:44.280Z",
"updated": "2023-01-22 23:21:44.280Z",
"name": "ports",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "8nwuncgg",
"name": "number",
"type": "number",
"required": true,
"unique": false,
"options": {
"min": null,
"max": 65535
}
},
{
"system": false,
"id": "o0he3pu6",
"name": "name",
"type": "text",
"required": true,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "w0bh39gv",
"name": "status",
"type": "bool",
"required": false,
"unique": false,
"options": {}
}
],
"listRule": "",
"viewRule": "",
"createRule": "",
"updateRule": "",
"deleteRule": "",
"options": {}
}`
collection := &models.Collection{}
if err := json.Unmarshal([]byte(jsonData), &collection); err != nil {
return err
}
return daos.New(db).SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("cti4l8f4mz8df3r")
if err != nil {
return err
}
return dao.DeleteCollection(collection)
})
}

View File

@@ -0,0 +1,59 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models"
)
func init() {
m.Register(func(db dbx.Builder) error {
jsonData := `{
"id": "nmj3ko20gzkg8n3",
"created": "2023-01-22 23:21:44.281Z",
"updated": "2023-01-22 23:21:44.281Z",
"name": "settings",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "ysutxavs",
"name": "interval",
"type": "number",
"required": true,
"unique": false,
"options": {
"min": 1,
"max": null
}
}
],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
}`
collection := &models.Collection{}
if err := json.Unmarshal([]byte(jsonData), &collection); err != nil {
return err
}
return daos.New(db).SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("nmj3ko20gzkg8n3")
if err != nil {
return err
}
return dao.DeleteCollection(collection)
})
}

View File

@@ -0,0 +1,90 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("_pb_users_auth_")
if err != nil {
return err
}
return dao.DeleteCollection(collection)
}, func(db dbx.Builder) error {
jsonData := `{
"id": "_pb_users_auth_",
"created": "2023-01-22 23:21:16.599Z",
"updated": "2023-01-22 23:21:16.600Z",
"name": "users",
"type": "auth",
"system": false,
"schema": [
{
"system": false,
"id": "users_name",
"name": "name",
"type": "text",
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "users_avatar",
"name": "avatar",
"type": "file",
"required": false,
"unique": false,
"options": {
"maxSelect": 1,
"maxSize": 5242880,
"mimeTypes": [
"image/jpg",
"image/jpeg",
"image/png",
"image/svg+xml",
"image/gif",
"image/webp"
],
"thumbs": null
}
}
],
"listRule": "id = @request.auth.id",
"viewRule": "id = @request.auth.id",
"createRule": "",
"updateRule": "id = @request.auth.id",
"deleteRule": "id = @request.auth.id",
"options": {
"allowEmailAuth": true,
"allowOAuth2Auth": true,
"allowUsernameAuth": true,
"exceptEmailDomains": null,
"manageRule": null,
"minPasswordLength": 8,
"onlyEmailDomains": null,
"requireEmail": false
}
}`
collection := &models.Collection{}
if err := json.Unmarshal([]byte(jsonData), &collection); err != nil {
return err
}
return daos.New(db).SaveCollection(collection)
})
}

View File

@@ -1,7 +1,9 @@
package networking
import (
"net"
"runtime"
"time"
"github.com/go-ping/ping"
"github.com/pocketbase/pocketbase/models"
@@ -31,3 +33,16 @@ func PingDevice(device *models.Record) bool {
return true
}
}
func CheckPort(host string, port string) bool {
timeout := 500 * time.Millisecond
conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), timeout)
if err != nil {
return false
}
if conn != nil {
defer conn.Close()
return true
}
return false
}

View File

@@ -10,8 +10,8 @@
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@sveltejs/adapter-static": "^1.0.1",
"@sveltejs/kit": "^1.1.1",
"@sveltejs/adapter-static": "^1.0.5",
"@sveltejs/kit": "^1.2.2",
"eslint": "^8.32.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-svelte3": "^4.0.0",
@@ -27,7 +27,7 @@
"@popperjs/core": "^2.11.6",
"bootstrap": "5.3.0-alpha1",
"date-fns": "^2.29.3",
"pocketbase": "^0.10.0",
"pocketbase": "^0.10.1",
"sass": "^1.57.1",
"svelte-fa": "^3.0.3"
}

View File

@@ -3,14 +3,14 @@ lockfileVersion: 5.4
specifiers:
'@fortawesome/free-solid-svg-icons': ^6.2.1
'@popperjs/core': ^2.11.6
'@sveltejs/adapter-static': ^1.0.1
'@sveltejs/kit': ^1.1.1
'@sveltejs/adapter-static': ^1.0.5
'@sveltejs/kit': ^1.2.2
bootstrap: 5.3.0-alpha1
date-fns: ^2.29.3
eslint: ^8.32.0
eslint-config-prettier: ^8.6.0
eslint-plugin-svelte3: ^4.0.0
pocketbase: ^0.10.0
pocketbase: ^0.10.1
prettier: ^2.8.3
prettier-plugin-svelte: ^2.9.0
sass: ^1.57.1
@@ -24,13 +24,13 @@ dependencies:
'@popperjs/core': 2.11.6
bootstrap: 5.3.0-alpha1_@popperjs+core@2.11.6
date-fns: 2.29.3
pocketbase: 0.10.0
pocketbase: 0.10.1
sass: 1.57.1
svelte-fa: 3.0.3
devDependencies:
'@sveltejs/adapter-static': 1.0.4_@sveltejs+kit@1.1.1
'@sveltejs/kit': 1.1.1_svelte@3.55.1+vite@4.0.4
'@sveltejs/adapter-static': 1.0.5_@sveltejs+kit@1.2.2
'@sveltejs/kit': 1.2.2_svelte@3.55.1+vite@4.0.4
eslint: 8.32.0
eslint-config-prettier: 8.6.0_eslint@8.32.0
eslint-plugin-svelte3: 4.0.0_tmo5zkisvhu6htudosk5k7m6pu
@@ -324,16 +324,16 @@ packages:
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
dev: false
/@sveltejs/adapter-static/1.0.4_@sveltejs+kit@1.1.1:
resolution: {integrity: sha512-L6kGg19MBHW3kv3GWAPl9tJo3ohNM+dcj9Yq1U1XVu0J0MKrusyCWQ62XOfxFgS6T8Fq0KYJ5NSSAPGppvX4xA==}
/@sveltejs/adapter-static/1.0.5_@sveltejs+kit@1.2.2:
resolution: {integrity: sha512-W5jbgvy9sbYEHs27NQOSFEun+zQwdcL4kpk5qc2kSHl8cKsP5wfXuWDTDRmD1Co40aFcesi5Az5ZzdnPI8KCVg==}
peerDependencies:
'@sveltejs/kit': ^1.0.0
dependencies:
'@sveltejs/kit': 1.1.1_svelte@3.55.1+vite@4.0.4
'@sveltejs/kit': 1.2.2_svelte@3.55.1+vite@4.0.4
dev: true
/@sveltejs/kit/1.1.1_svelte@3.55.1+vite@4.0.4:
resolution: {integrity: sha512-5hZef6yTqiLm8fLMFD78udJI3pG9ptSndg3NNmDqxv5fS9KFABkASQwnWB25Pf1HkPNqvL+x4Tl8kyW1N8thKQ==}
/@sveltejs/kit/1.2.2_svelte@3.55.1+vite@4.0.4:
resolution: {integrity: sha512-aZUjAZ/6gWEYFQDrDNINuvOi6VxlG86kCcIDRWDIFJjI38Ueieo1fySb0j0d2VkQVrYXU7VqjTVMBZFix+hByA==}
engines: {node: ^16.14 || >=18}
hasBin: true
requiresBuild: true
@@ -354,7 +354,7 @@ packages:
sirv: 2.0.2
svelte: 3.55.1
tiny-glob: 0.2.9
undici: 5.15.0
undici: 5.15.1
vite: 4.0.4_sass@1.57.1
transitivePeerDependencies:
- supports-color
@@ -699,7 +699,7 @@ packages:
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
js-sdsl: 4.2.0
js-sdsl: 4.3.0
js-yaml: 4.1.0
json-stable-stringify-without-jsonify: 1.0.1
levn: 0.4.1
@@ -944,8 +944,8 @@ packages:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
/js-sdsl/4.2.0:
resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==}
/js-sdsl/4.3.0:
resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==}
dev: true
/js-yaml/4.1.0:
@@ -1122,8 +1122,8 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
/pocketbase/0.10.0:
resolution: {integrity: sha512-OWcPRcOPNiTqek5wkf56mCmkox4w5/h1x+ySBNqzQ0Ux7ztEv3aq3Gr91bwn3VcXN7o48XVbKny7A922hNIbQw==}
/pocketbase/0.10.1:
resolution: {integrity: sha512-FMBhF+9o2AmdKJYbfz2qunnk4Q5/efYMRcukdQF49UfhsIuaSYl39fSPN1l880bYI4XAvDDeySAjK1MlxrK37A==}
dev: false
/postcss/8.4.21:
@@ -1156,8 +1156,8 @@ packages:
hasBin: true
dev: true
/punycode/2.2.0:
resolution: {integrity: sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==}
/punycode/2.3.0:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}
dev: true
@@ -1209,8 +1209,8 @@ packages:
glob: 7.2.3
dev: true
/rollup/3.10.0:
resolution: {integrity: sha512-JmRYz44NjC1MjVF2VKxc0M1a97vn+cDxeqWmnwyAF4FvpjK8YFdHpaqvQB+3IxCvX05vJxKZkoMDU8TShhmJVA==}
/rollup/3.10.1:
resolution: {integrity: sha512-3Er+yel3bZbZX1g2kjVM+FW+RUWDxbG87fcqFM5/9HbPCTpbVp6JOLn7jlxnNlbu7s/N/uDA4EV/91E2gWnxzw==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
@@ -1431,8 +1431,8 @@ packages:
engines: {node: '>=10'}
dev: true
/undici/5.15.0:
resolution: {integrity: sha512-wCAZJDyjw9Myv+Ay62LAoB+hZLPW9SmKbQkbHIhMw/acKSlpn7WohdMUc/Vd4j1iSMBO0hWwU8mjB7a5p5bl8g==}
/undici/5.15.1:
resolution: {integrity: sha512-XLk8g0WAngdvFqTI+VKfBtM4YWXgdxkf1WezC771Es0Dd+Pm1KmNx8t93WTC+Hh9tnghmVxkclU1HN+j+CvIUA==}
engines: {node: '>=12.18'}
dependencies:
busboy: 1.6.0
@@ -1441,7 +1441,7 @@ packages:
/uri-js/4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies:
punycode: 2.2.0
punycode: 2.3.0
dev: true
/vite/4.0.4_sass@1.57.1:
@@ -1472,7 +1472,7 @@ packages:
esbuild: 0.16.17
postcss: 8.4.21
resolve: 1.22.1
rollup: 3.10.0
rollup: 3.10.1
sass: 1.57.1
optionalDependencies:
fsevents: 2.3.2

View File

@@ -1,7 +1,7 @@
<script>
import { dev } from '$app/environment';
import { parseISO, formatDistance } from 'date-fns';
import { faPowerOff, faEllipsisVertical } from '@fortawesome/free-solid-svg-icons';
import { faPowerOff, faEllipsisVertical, faCircle } from '@fortawesome/free-solid-svg-icons';
import Fa from 'svelte-fa';
export let device;
@@ -14,6 +14,10 @@
function wake() {
fetch(`${dev ? 'http://127.0.0.1:8090' : ''}/api/upsnap/wake/${device.id}`);
}
function sortPorts(a, b) {
return a.number - b.number;
}
</script>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3 g-4">
@@ -49,8 +53,18 @@
</div>
</div>
<p class="m-0 fw-bold fs-5">{device.name}</p>
<p class="text-muted">{device.ip}</p>
<p class="m-0 text-muted">
<p class="text-muted mb-2">{device.ip}</p>
{#if device?.expand?.ports}
<div class="mb-2">
{#each device.expand.ports.sort(sortPorts) as port}
<p class="m-0">
<Fa icon={faCircle} class="me-1 {port.status ? 'port-up' : 'port-down'}" />{port.name}
<span class="text-muted">({port.number})</span>
</p>
{/each}
</div>
{/if}
<p class="text-muted m-0">
{formatDistance(parseISO(device.updated), now, {
includeSeconds: true,
addSuffix: true

View File

@@ -1,9 +1,13 @@
<script>
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { theme } from '@stores/themestore';
import { theme } from '@stores/theme';
import { faSun, faMoon, faCircleHalfStroke, faBrush } from '@fortawesome/free-solid-svg-icons';
import Fa from 'svelte-fa';
import('bootstrap/js/dist/dropdown');
onMount(() => {
import('bootstrap/js/dist/dropdown');
})
</script>
<nav class="navbar navbar-expand">

View File

@@ -1,9 +1,9 @@
<script>
import { onMount } from 'svelte';
import Navbar from '@components/Navbar.svelte';
import Transition from '@components/Transition.svelte';
import { page } from '$app/stores';
import { onMount } from 'svelte';
import { theme } from '@stores/themestore';
import { theme } from '@stores/theme';
let preferesDark;

View File

@@ -12,14 +12,33 @@
});
// get all devices in pocketbase
const result = await pb.collection('devices').getFullList();
const result = await pb.collection('devices').getFullList(200, {
expand: 'ports'
});
result.forEach((device) => {
devices[device.id] = device;
});
// subscribe to database events
pb.collection('devices').subscribe('*', function (e) {
devices[e.record.id] = e.record;
pb.collection('devices').subscribe('*', async (e) => {
if (e.action === 'create') {
devices[e.record.id] = e.record;
} else if (e.action === 'update') {
const device = await pb.collection('devices').getOne(e.record.id, {
expand: 'ports'
})
devices[device.id] = device
} else if (e.action === 'delete') {
delete devices[e.record.id]
}
});
pb.collection('ports').subscribe('*', async (e) => {
if (e.action === 'update') {
const device = await pb.collection('devices').getFirstListItem(`ports.id ?= "${e.record.id}"`, {
expand: 'ports'
})
devices[device.id] = device
}
});
});

View File

@@ -33,28 +33,22 @@
return;
}
let createdPorts = {};
// loop devices
for (let index = 0; index < data.length; index++) {
const device = data[index];
// create ports
let thisDevicePorts = [];
for (let index = 0; index < device.ports.length; index++) {
// keep track of already created ports
const port = device.ports[index];
if (createdPorts[port.number] === undefined) {
const record = await pb.collection('ports').create(
{
name: port.name,
number: port.number
},
{ $autoCancel: false }
);
createdPorts[port.number] = record;
thisDevicePorts.push(record.id);
} else {
thisDevicePorts.push(createdPorts[port.number].id);
}
const record = await pb.collection('ports').create(
{
name: port.name,
number: port.number
},
{ $autoCancel: false }
);
thisDevicePorts.push(record.id);
}
// create device
@@ -64,7 +58,7 @@
ip: device.ip,
mac: device.mac,
netmask: device.netmask,
port: thisDevicePorts,
ports: thisDevicePorts,
link: device.link,
wake: device.wake,
shutdown: device.shutdown

View File

@@ -56,7 +56,7 @@ section {
}
}
.text-success {
.text-success, .port-up {
color: $teal-500 !important;
}
@@ -64,7 +64,7 @@ section {
color: $yellow !important;
}
.text-danger {
.text-danger, .port-down {
color: #ef476f !important;
}