frontend: start to do switch data
This commit is contained in:
parent
a364713031
commit
da98792d35
|
@ -5,7 +5,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
@ -69,8 +68,10 @@ var publicRoutes map[string]bool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
publicRoutes = map[string]bool{
|
publicRoutes = map[string]bool{
|
||||||
"/webhooks/": true,
|
"/webhooks/": true,
|
||||||
"/static/": true,
|
"/static/": true,
|
||||||
|
"/static/main.js": true,
|
||||||
|
"/static/gruvbox.css": true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ func (pm PasetoMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for k := range publicRoutes {
|
for k := range publicRoutes {
|
||||||
if strings.HasPrefix(r.URL.EscapedPath(), k) {
|
if r.URL.EscapedPath() == k {
|
||||||
goto ok
|
goto ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,14 @@
|
||||||
"elm/html": "1.0.0",
|
"elm/html": "1.0.0",
|
||||||
"elm/http": "2.0.0",
|
"elm/http": "2.0.0",
|
||||||
"elm/json": "1.1.3",
|
"elm/json": "1.1.3",
|
||||||
"elm/url": "1.0.0"
|
"elm/time": "1.0.0",
|
||||||
|
"elm/url": "1.0.0",
|
||||||
|
"rtfeldman/elm-iso8601-date-strings": "1.1.3"
|
||||||
},
|
},
|
||||||
"indirect": {
|
"indirect": {
|
||||||
"elm/bytes": "1.0.8",
|
"elm/bytes": "1.0.8",
|
||||||
"elm/file": "1.0.5",
|
"elm/file": "1.0.5",
|
||||||
"elm/time": "1.0.0",
|
"elm/parser": "1.1.0",
|
||||||
"elm/virtual-dom": "1.0.2"
|
"elm/virtual-dom": "1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,209 +7,278 @@ import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onInput)
|
import Html.Events exposing (onInput)
|
||||||
import Http
|
import Http
|
||||||
import Json.Decode as D
|
import Json.Decode as D
|
||||||
|
import SwitchData
|
||||||
import Url
|
import Url
|
||||||
|
import Url.Builder
|
||||||
|
|
||||||
|
|
||||||
-- MAIN
|
|
||||||
main : Program () Model Msg
|
main : Program () Model Msg
|
||||||
main =
|
main =
|
||||||
Browser.application
|
Browser.application
|
||||||
{ init = init
|
{ init = init
|
||||||
, view = view
|
, view = view
|
||||||
, update = update
|
, update = update
|
||||||
, subscriptions = subscriptions
|
, subscriptions = subscriptions
|
||||||
, onUrlChange = UrlChanged
|
, onUrlChange = UrlChanged
|
||||||
, onUrlRequest = LinkClicked
|
, onUrlRequest = LinkClicked
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ key : Nav.Key
|
||||||
|
, url : Url.Url
|
||||||
|
, token : Maybe String
|
||||||
|
, token_data : Maybe TokenData
|
||||||
|
, switch_cursor : ( Int, Int )
|
||||||
|
, switch_data : Maybe (List SwitchData.Switch)
|
||||||
}
|
}
|
||||||
|
|
||||||
-- MODEL
|
|
||||||
type alias Model =
|
|
||||||
{ key : Nav.Key
|
|
||||||
, url : Url.Url
|
|
||||||
, token: Maybe String
|
|
||||||
, token_data: Maybe TokenData
|
|
||||||
, switch_data : Maybe ( List Switch )
|
|
||||||
}
|
|
||||||
|
|
||||||
init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
|
init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
|
||||||
init flags url key =
|
init flags url key =
|
||||||
( Model key url Nothing Nothing Nothing, Cmd.none )
|
( Model
|
||||||
|
key
|
||||||
|
url
|
||||||
|
Nothing
|
||||||
|
Nothing
|
||||||
|
( 40, 0 )
|
||||||
|
Nothing
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
-- UPDATE
|
|
||||||
type Msg
|
type Msg
|
||||||
= LinkClicked Browser.UrlRequest
|
= LinkClicked Browser.UrlRequest
|
||||||
| UrlChanged Url.Url
|
| UrlChanged Url.Url
|
||||||
| TokenInput String
|
| TokenInput String
|
||||||
| TokenValidate ( Result Http.Error TokenData )
|
| TokenValidate (Result Http.Error TokenData)
|
||||||
| Logout
|
| Logout
|
||||||
| NoSwitchData
|
| GetSwitchData ( Int, Int )
|
||||||
| GotSwitchData ( List Switch )
|
| GotSwitchData (Result Http.Error (List SwitchData.Switch))
|
||||||
|
|
||||||
type alias Switch
|
|
||||||
= { id : String
|
|
||||||
, who : String
|
|
||||||
, started_at : String
|
|
||||||
, ended_at : Maybe String
|
|
||||||
, duration : Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type alias TokenData
|
type alias TokenData =
|
||||||
= { sub : String
|
{ sub : String
|
||||||
, jti : String
|
, jti : String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tokenDecoder : D.Decoder TokenData
|
tokenDecoder : D.Decoder TokenData
|
||||||
tokenDecoder =
|
tokenDecoder =
|
||||||
D.map2 TokenData
|
D.map2 TokenData
|
||||||
(D.field "sub" D.string)
|
(D.field "sub" D.string)
|
||||||
(D.field "jti" D.string)
|
(D.field "jti" D.string)
|
||||||
|
|
||||||
|
|
||||||
request method token path body expect =
|
request method token path body expect =
|
||||||
Http.request
|
Http.request
|
||||||
{ method = method
|
{ method = method
|
||||||
, body = body
|
, body = body
|
||||||
, headers =
|
, headers =
|
||||||
[ Http.header "Authorization" token
|
[ Http.header "Authorization" token
|
||||||
]
|
]
|
||||||
, url = path
|
, url = path
|
||||||
, expect = expect
|
, expect = expect
|
||||||
, timeout = Nothing
|
, timeout = Nothing
|
||||||
, tracker = Nothing
|
, tracker = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
update msg model =
|
update msg model =
|
||||||
case msg of
|
case msg of
|
||||||
LinkClicked urlRequest ->
|
LinkClicked urlRequest ->
|
||||||
case urlRequest of
|
case urlRequest of
|
||||||
Browser.Internal url ->
|
Browser.Internal url ->
|
||||||
( model, Nav.pushUrl model.key (Url.toString url) )
|
( model, Nav.pushUrl model.key (Url.toString url) )
|
||||||
|
|
||||||
Browser.External href ->
|
Browser.External href ->
|
||||||
( model, Nav.load href )
|
( model, Nav.load href )
|
||||||
|
|
||||||
UrlChanged url ->
|
UrlChanged url ->
|
||||||
case url.path of
|
case url.path of
|
||||||
"/logout" ->
|
"/logout" ->
|
||||||
( model, Nav.load "/" )
|
( model, Nav.load "/" )
|
||||||
|
|
||||||
default ->
|
default ->
|
||||||
( { model | url = url }
|
( { model | url = url }
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
TokenInput token ->
|
TokenInput token ->
|
||||||
( { model | token = Just token }
|
( { model | token = Just token }
|
||||||
, request "GET" token "/.within/tokeninfo" Http.emptyBody (expectJson TokenValidate tokenDecoder)
|
, request "GET" token "/.within/tokeninfo" Http.emptyBody (expectJson TokenValidate tokenDecoder)
|
||||||
)
|
)
|
||||||
|
|
||||||
TokenValidate result ->
|
TokenValidate result ->
|
||||||
case result of
|
case result of
|
||||||
Ok data ->
|
Ok data ->
|
||||||
( { model | token_data = Just data }
|
( { model | token_data = Just data }
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
Err _ ->
|
Err _ ->
|
||||||
( { model | token = Nothing }
|
( { model | token = Nothing }
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Logout ->
|
||||||
|
( { model | token = Nothing, token_data = Nothing }
|
||||||
|
, Nav.load "/"
|
||||||
|
)
|
||||||
|
|
||||||
|
GetSwitchData ( limit, page ) ->
|
||||||
|
case model.token of
|
||||||
|
Just token ->
|
||||||
|
( model
|
||||||
|
, request "GET"
|
||||||
|
token
|
||||||
|
(SwitchData.dataURL limit page)
|
||||||
|
Http.emptyBody
|
||||||
|
(expectJson GotSwitchData (D.list SwitchData.decoder))
|
||||||
|
)
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
Logout ->
|
|
||||||
( { model | token = Nothing, token_data = Nothing }
|
|
||||||
, Nav.load "/"
|
|
||||||
)
|
|
||||||
_ ->
|
|
||||||
( model, Cmd.none )
|
|
||||||
|
|
||||||
-- SUBSCRIPTIONS
|
|
||||||
subscriptions : Model -> Sub Msg
|
subscriptions : Model -> Sub Msg
|
||||||
subscriptions _ =
|
subscriptions _ =
|
||||||
Sub.none
|
Sub.none
|
||||||
|
|
||||||
|
|
||||||
-- VIEW
|
|
||||||
view : Model -> Browser.Document Msg
|
view : Model -> Browser.Document Msg
|
||||||
view model =
|
view model =
|
||||||
case model.token of
|
case model.token_data of
|
||||||
Nothing ->
|
Nothing ->
|
||||||
template "Login"
|
{ title = "Login"
|
||||||
[ h1 [] [ text "Login" ]
|
, body =
|
||||||
, viewInput "password" "API Token" "" TokenInput
|
[ node "main"
|
||||||
]
|
[ style "align" "center" ]
|
||||||
Just token ->
|
[ h1 [] [ text "Login" ]
|
||||||
case model.url.path of
|
, viewInput "password" "API Token" "" TokenInput
|
||||||
"/" ->
|
]
|
||||||
template "Mi"
|
]
|
||||||
[ navBar
|
}
|
||||||
, h1 [] [ text "Mi" ]
|
|
||||||
, p [] [ text "TODO: everything" ]
|
Just token_data ->
|
||||||
, p []
|
case model.url.path of
|
||||||
[ text "Token sub: "
|
"/" ->
|
||||||
, text (Maybe.withDefault (TokenData "" "") model.token_data).sub
|
template "Mi"
|
||||||
, text " ID: "
|
[ h1 [] [ text "Mi" ]
|
||||||
, text (Maybe.withDefault (TokenData "" "") model.token_data).jti
|
, h2 [] [ text "TODO" ]
|
||||||
]
|
, ul []
|
||||||
]
|
[ li [] [ text "Switch CRUD" ]
|
||||||
other ->
|
, li [] [ text "POSSE manual announcement" ]
|
||||||
template "Not found"
|
]
|
||||||
[ navBar
|
, h2 [] [ text "Token data" ]
|
||||||
, h1 [] [ text "Not found" ]
|
, p []
|
||||||
, p []
|
[ text "Token sub: "
|
||||||
[ text "The requested URL "
|
, text token_data.sub
|
||||||
, b [] [ text other ]
|
, Html.br [] []
|
||||||
, text " was not found."
|
, text "ID: "
|
||||||
]
|
, text token_data.jti
|
||||||
]
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
"/switch" ->
|
||||||
|
case model.switch_data of
|
||||||
|
Nothing ->
|
||||||
|
template "Switch counter"
|
||||||
|
[ h1 [] [ text "Switch counter" ]
|
||||||
|
, p [] [ text "loading..." ]
|
||||||
|
]
|
||||||
|
|
||||||
|
Just switches ->
|
||||||
|
template "Switch counter"
|
||||||
|
[ h1 [] [ text "TODO: table" ]
|
||||||
|
]
|
||||||
|
|
||||||
|
other ->
|
||||||
|
template "Not found"
|
||||||
|
[ h1 [] [ text "Not found" ]
|
||||||
|
, p []
|
||||||
|
[ text "The requested URL "
|
||||||
|
, b [] [ text other ]
|
||||||
|
, text " was not found."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
viewInput : String -> String -> String -> (String -> msg) -> Html msg
|
viewInput : String -> String -> String -> (String -> msg) -> Html msg
|
||||||
viewInput t p v toMsg =
|
viewInput t p v toMsg =
|
||||||
input [ type_ t, placeholder p, value v, onInput toMsg ] []
|
input [ type_ t, placeholder p, value v, onInput toMsg ] []
|
||||||
|
|
||||||
|
|
||||||
viewLink : String -> String -> Html msg
|
viewLink : String -> String -> Html msg
|
||||||
viewLink path title =
|
viewLink path title =
|
||||||
a [ href path ] [ text title ]
|
a [ href path ] [ text title ]
|
||||||
|
|
||||||
|
|
||||||
template : String -> List (Html msg) -> Browser.Document msg
|
template : String -> List (Html msg) -> Browser.Document msg
|
||||||
template title body =
|
template title body =
|
||||||
{ title = title
|
{ title = title
|
||||||
, body =
|
, body =
|
||||||
[ node "main" []
|
[ node "main"
|
||||||
body
|
[]
|
||||||
]
|
[ navBar
|
||||||
}
|
, div [] body
|
||||||
|
, footer
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
navBar : Html msg
|
navBar : Html msg
|
||||||
navBar =
|
navBar =
|
||||||
node "nav" []
|
node "nav"
|
||||||
[ p []
|
[]
|
||||||
[ viewLink "/" "Mi"
|
[ p []
|
||||||
, text " - "
|
[ viewLink "/" "Mi"
|
||||||
, viewLink "/switch" "Switch tracker"
|
, text " - "
|
||||||
, text " - "
|
, viewLink "/switch" "Switch tracker"
|
||||||
, viewLink "/logout" "Logout"
|
, text " - "
|
||||||
]
|
, viewLink "/logout" "Logout"
|
||||||
]
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
footer : Html msg
|
||||||
|
footer =
|
||||||
|
node "footer"
|
||||||
|
[]
|
||||||
|
[ p []
|
||||||
|
[ a [ href "https://within.website" ] [ text "From Within" ]
|
||||||
|
, text " - "
|
||||||
|
, a [ href "https://tulpa.dev/cadey/mi" ] [ text "Source code" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
expectJson : (Result Http.Error a -> msg) -> D.Decoder a -> Http.Expect msg
|
expectJson : (Result Http.Error a -> msg) -> D.Decoder a -> Http.Expect msg
|
||||||
expectJson toMsg decoder =
|
expectJson toMsg decoder =
|
||||||
Http.expectStringResponse toMsg <|
|
Http.expectStringResponse toMsg <|
|
||||||
\response ->
|
\response ->
|
||||||
case response of
|
case response of
|
||||||
Http.BadUrl_ url ->
|
Http.BadUrl_ url ->
|
||||||
Err (Http.BadUrl url)
|
Err (Http.BadUrl url)
|
||||||
|
|
||||||
Http.Timeout_ ->
|
Http.Timeout_ ->
|
||||||
Err Http.Timeout
|
Err Http.Timeout
|
||||||
|
|
||||||
Http.NetworkError_ ->
|
Http.NetworkError_ ->
|
||||||
Err Http.NetworkError
|
Err Http.NetworkError
|
||||||
|
|
||||||
Http.BadStatus_ metadata body ->
|
Http.BadStatus_ metadata body ->
|
||||||
Err (Http.BadStatus metadata.statusCode)
|
Err (Http.BadStatus metadata.statusCode)
|
||||||
|
|
||||||
Http.GoodStatus_ metadata body ->
|
Http.GoodStatus_ metadata body ->
|
||||||
case D.decodeString decoder body of
|
case D.decodeString decoder body of
|
||||||
Ok value ->
|
Ok value ->
|
||||||
Ok value
|
Ok value
|
||||||
|
|
||||||
Err err ->
|
Err err ->
|
||||||
Err (Http.BadBody (D.errorToString err))
|
Err (Http.BadBody (D.errorToString err))
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
module SwitchData exposing (..)
|
||||||
|
|
||||||
|
import Iso8601
|
||||||
|
import Json.Decode exposing (Decoder, field, int, map5, nullable, string)
|
||||||
|
import Time exposing (Posix)
|
||||||
|
import Url.Builder as UB
|
||||||
|
|
||||||
|
|
||||||
|
type alias Switch =
|
||||||
|
{ id : String
|
||||||
|
, who : String
|
||||||
|
, started_at : Posix
|
||||||
|
, ended_at : Maybe Posix
|
||||||
|
, duration : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
decoder : Decoder Switch
|
||||||
|
decoder =
|
||||||
|
map5 Switch
|
||||||
|
(field "id" string)
|
||||||
|
(field "who" string)
|
||||||
|
(field "started_at" Iso8601.decoder)
|
||||||
|
(field "ended_at" (nullable Iso8601.decoder))
|
||||||
|
(field "duration" int)
|
||||||
|
|
||||||
|
|
||||||
|
dataURL : Int -> Int -> String
|
||||||
|
dataURL limit page =
|
||||||
|
UB.absolute [ "switches" ] [ UB.int "limit" limit, UB.int "page" page ]
|
Loading…
Reference in New Issue