diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 01550cf..96227d6 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,6 +23,20 @@ jobs: - name: Test run: go test -v ./... + lint-frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Install dependencies + run: npm --prefix js i + + - name: Run ESLint + run: npm --prefix js run eslint + build-frontend: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 6d8da31..b47f644 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ database/local.db sattrack data +public \ No newline at end of file diff --git a/README.md b/README.md index 0621a55..23986fc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,26 @@ # SatTrack -Modern satellite tracking solution +Modern satellite tracking solution (currently WIP) + +![](img/home.png) ## TODO +### Backend + - [ ] Custom validation messages +- [ ] Check for performance issues & missing best practises +- [ ] API documentation +- [ ] Rate-limiting + +### Frontend + +- [ ] Migrate to Typescript +- [ ] About page +- [ ] Custom UI (Currently using Bootstrap 💀) + enhancements +- [ ] More functionalities (pass prediction, elevation/azimuth graph) +- [ ] GitHub action for ESLint + +### Docker + +- [ ] Dockerhub tag (currently it is only tagging with 'latest') diff --git a/dto/Tracking.go b/dto/Tracking.go index 3a5ac6b..e88e51b 100644 --- a/dto/Tracking.go +++ b/dto/Tracking.go @@ -17,3 +17,10 @@ type TrackingResponse struct { sgp4.Observation GeneratedAt time.Time } + +type ObservationWsResponse struct { + SatelliteName string + Visible bool + GeneratedAt time.Time + sgp4.Observation +} diff --git a/go.mod b/go.mod index dfbaa6f..26b3afb 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,10 @@ require ( require ( github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.9.0 + github.com/go-playground/validator/v10 v10.10.0 github.com/leodido/go-urn v1.2.1 // indirect - golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect + golang.org/x/text v0.3.7 // indirect ) require ( @@ -29,7 +29,7 @@ require ( require ( github.com/andybalholm/brotli v1.0.4 // indirect - github.com/gofiber/fiber/v2 v2.23.0 + github.com/gofiber/fiber/v2 v2.24.0 github.com/klauspost/compress v1.13.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.31.0 // indirect diff --git a/go.sum b/go.sum index 4741a14..b2ac0a2 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,12 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/gofiber/fiber/v2 v2.23.0 h1:kcJGMC6SULJ2G7p7mbs+A28cVLOeJSR694jfGyGZqRI= github.com/gofiber/fiber/v2 v2.23.0/go.mod h1:MR1usVH3JHYRyQwMe2eZXRSZHRX38fkV+A7CPB+DlDQ= +github.com/gofiber/fiber/v2 v2.24.0 h1:18rpLoQMJBVlLtX/PwgHj3hIxPSeWfN1YeDJ2lEnzjU= +github.com/gofiber/fiber/v2 v2.24.0/go.mod h1:MR1usVH3JHYRyQwMe2eZXRSZHRX38fkV+A7CPB+DlDQ= github.com/gofiber/websocket/v2 v2.0.14 h1:9xGDX08jBm1N809Q25MT95y0c72XvIguxsYKo3qG8ec= github.com/gofiber/websocket/v2 v2.0.14/go.mod h1:movf9ZfhvBRpmZjwdv1u/gCvOPFjwznwqz+x75pYr7I= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -65,6 +69,8 @@ github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7Fw golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -78,6 +84,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/handlers/TrackingHandler.go b/handlers/TrackingHandler.go index ddd65b5..6abdf15 100644 --- a/handlers/TrackingHandler.go +++ b/handlers/TrackingHandler.go @@ -2,6 +2,7 @@ package handlers import ( "net/http" + "strings" "time" "github.com/SharkEzz/sattrack/dto" @@ -75,8 +76,16 @@ func HandleWsTracking(c *websocket.Conn, db *gorm.DB) { for { observation := sgp4.ObservationFromLocation(lat, lng, alt) - if err := c.WriteJSON(observation); err != nil { - // error + responseDto := dto.ObservationWsResponse{ + SatelliteName: strings.TrimSpace(tle.Name()), + Visible: observation.Elevation > 0, + GeneratedAt: time.Now().UTC(), + Observation: observation, + } + if err := c.WriteJSON(responseDto); err != nil { + return + } + if _, _, err = c.ReadMessage(); err != nil { return } time.Sleep(time.Second) diff --git a/img/home.png b/img/home.png new file mode 100644 index 0000000..49246eb Binary files /dev/null and b/img/home.png differ diff --git a/js/.eslintrc.json b/js/.eslintrc.json new file mode 100644 index 0000000..38e6b98 --- /dev/null +++ b/js/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "airbnb", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", + "prettier" + ], + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "ecmaVersion": 12, + "sourceType": "module" + }, + "plugins": ["react", "react-hooks", "prettier"], + "rules": { + "prettier/prettier": "error", + "react/jsx-props-no-spreading": 0 + } +} diff --git a/js/hooks/useLocation.js b/js/hooks/useLocation.js new file mode 100644 index 0000000..fb6f8ae --- /dev/null +++ b/js/hooks/useLocation.js @@ -0,0 +1,37 @@ +import { useState } from 'react'; + +function useLocation() { + const [initialized, setInitialized] = useState(false); + const [location, setLocation] = useState({ + lat: null, + lng: null, + alt: null, + }); + + const getLocation = () => { + if (typeof navigator === 'undefined' || !('geolocation' in navigator)) { + return; + } + + navigator.geolocation.getCurrentPosition( + (position) => { + const { latitude, longitude, altitude } = position.coords; + setLocation({ + lat: Number(latitude.toFixed(2)), + lng: Number(longitude.toFixed(2)), + alt: altitude ? Number(altitude.toFixed(2)) : null, + }); + setInitialized(true); + }, + () => null, + ); + }; + + return { + initialized, + location, + getLocation, + }; +} + +export default useLocation; diff --git a/js/hooks/useWebsocket.js b/js/hooks/useWebsocket.js new file mode 100644 index 0000000..817affc --- /dev/null +++ b/js/hooks/useWebsocket.js @@ -0,0 +1,76 @@ +import { useRef, useState } from 'react'; + +function useWebsocket() { + const [opened, setOpened] = useState(false); + const [message, setMessage] = useState(null); + const [error, setError] = useState(null); + const [isOpening, setIsOpening] = useState(false); + const [isClosing, setIsClosing] = useState(false); + const websocket = useRef(null); + + /** + * @param {MessageEvent} event + */ + const handleMessage = (event) => { + const receivedMessage = JSON.parse(event.data); + if (receivedMessage?.Status && receivedMessage.Status !== 200) { + setError({ + status: receivedMessage.Status, + message: receivedMessage.Message, + }); + return; + } + if (error) { + setError(null); + } + setMessage(receivedMessage); + websocket.current.send('ok'); + }; + + const handleClose = () => { + setOpened(false); + setIsClosing(false); + }; + + const handleOpen = () => { + setOpened(true); + setIsOpening(false); + }; + + const closeConnection = () => { + websocket.current.close(); + setIsClosing(true); + }; + + /** + * @param {String} baseUrl + * @param {Number} catNbr + * @param {{ lat: Number, lng: Number, alt: Number }} location + */ + const openConnection = (baseUrl, catNbr, location) => { + const url = new URL(baseUrl); + url.searchParams.append('catnbr', catNbr); + Object.keys(location).forEach((item) => { + url.searchParams.append(item, location[item]); + }); + + websocket.current = new WebSocket(url); + websocket.current.onclose = handleClose; + websocket.current.onopen = handleOpen; + websocket.current.onmessage = handleMessage; + websocket.current.onerror = closeConnection; + setIsOpening(true); + }; + + return { + isOpening, + isClosing, + opened, + message, + error, + openConnection, + closeConnection, + }; +} + +export default useWebsocket; diff --git a/js/index.html b/js/index.html index b46ab83..ea5bb2a 100644 --- a/js/index.html +++ b/js/index.html @@ -1,13 +1,16 @@ -
- - - -