summaryrefslogtreecommitdiff
path: root/elm3/Lib/Api.elm
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2019-07-25 14:30:04 +0200
committerYorhel <git@yorhel.nl>2019-07-25 14:36:21 +0200
commitf296495a912ce759df11c43e78b4552788bdbff2 (patch)
tree0c10802de65fb7c8475722e12234bff5eb980628 /elm3/Lib/Api.elm
parent0f3cfeb85caec6424bcbea47142eefbf8011636b (diff)
Merge the v3 branch into separate namespace + fix Docker stuff (again)
I was getting tired of having to keep two branches up-to-date with the latest developments, so decided to throw v3 into the same branch - just different files (...which will get mostly rewritten again soon). The two versions aren't very different in terms of dependencies, build system and support code, so they can now properly share files. Added a section to the README to avoid confusion. This merge also makes it easier to quickly switch between the different versions, which is handy for development. It's even possible to run both at the same time, but my scripts use the same port so that needs a workaround. And it's amazing how often I break the Docker scripts.
Diffstat (limited to 'elm3/Lib/Api.elm')
-rw-r--r--elm3/Lib/Api.elm265
1 files changed, 265 insertions, 0 deletions
diff --git a/elm3/Lib/Api.elm b/elm3/Lib/Api.elm
new file mode 100644
index 00000000..512038e8
--- /dev/null
+++ b/elm3/Lib/Api.elm
@@ -0,0 +1,265 @@
+module Lib.Api exposing (..)
+
+import Json.Encode as JE
+import Json.Decode as JD
+import File exposing (File)
+import Http
+import Html exposing (Attribute)
+import Html.Events exposing (on)
+
+
+-- Handy state enum for forms
+type State
+ = Normal
+ | Loading
+ | Error Response
+
+
+type alias VN =
+ { id : Int
+ , title : String
+ , original : String
+ , hidden : Bool
+ }
+
+decodeVN : JD.Decoder VN
+decodeVN = JD.map4
+ (\a b c d -> { id = a, title = b, original = c, hidden = d})
+ (JD.field "id" JD.int)
+ (JD.field "title" JD.string)
+ (JD.field "original" JD.string)
+ (JD.field "hidden" JD.bool)
+
+
+type alias Staff =
+ { id : Int
+ , aid : Int
+ , name : String
+ , original : String
+ }
+
+decodeStaff : JD.Decoder Staff
+decodeStaff = JD.map4
+ (\a b c d -> { id = a, aid = b, name = c, original = d })
+ (JD.field "id" JD.int)
+ (JD.field "aid" JD.int)
+ (JD.field "name" JD.string)
+ (JD.field "original" JD.string)
+
+
+type alias Producer =
+ { id : Int
+ , name : String
+ , original : String
+ , hidden : Bool
+ }
+
+decodeProducer : JD.Decoder Producer
+decodeProducer = JD.map4
+ (\a b c d -> { id = a, name = b, original = c, hidden = d })
+ (JD.field "id" JD.int)
+ (JD.field "name" JD.string)
+ (JD.field "original" JD.string)
+ (JD.field "hidden" JD.bool)
+
+
+type alias Char =
+ { id : Int
+ , name : String
+ , original : String
+ , main : Maybe
+ { id : Int
+ , name : String
+ }
+ }
+
+decodeChar : JD.Decoder Char
+decodeChar = JD.map5
+ (\a b c d e ->
+ { id = a, name = b, original = c
+ , main = case (d, e) of
+ (Just id, Just name) -> Just { id = id, name = name }
+ _ -> Nothing
+ })
+ (JD.field "id" JD.int)
+ (JD.field "name" JD.string)
+ (JD.field "original" JD.string)
+ (JD.field "main" (JD.nullable JD.int ))
+ (JD.field "main_name" (JD.nullable JD.string))
+
+
+type alias Trait =
+ { id : Int
+ , name : String
+ , gid : Maybe Int
+ , group : Maybe String
+ }
+
+decodeTrait : JD.Decoder Trait
+decodeTrait = JD.map4
+ (\a b c d -> { id = a, name = b, gid = c, group = d })
+ (JD.field "id" JD.int)
+ (JD.field "name" JD.string)
+ (JD.field "gid" (JD.nullable JD.int))
+ (JD.field "group" (JD.nullable JD.string))
+
+
+-- Same as Lib.Gen.CharEditVnrelsReleases
+type alias Release =
+ { id : Int
+ , title : String
+ , lang : List String
+ }
+
+decodeRelease : JD.Decoder Release
+decodeRelease = JD.map3
+ (\a b c -> { id = a, title = b, lang = c })
+ (JD.field "id" JD.int)
+ (JD.field "title" JD.string)
+ (JD.field "lang" (JD.list JD.string))
+
+
+-- Possible server responses. This only includes "expected" responses. Much of
+-- the form validation is performed client side, so a constraint violation in
+-- the JSON structure or data fields is unexpected and is reported by the
+-- server as a 400 or 500 response.
+type Response
+ = HTTPError Http.Error
+ | Success
+ | CSRF
+ | Throttled
+ | Invalid JE.Value -- JSON structure constraint violation, contains TUWF::Validate error for low-level error reporting
+ | Unauth
+ | BadEmail
+ | BadLogin
+ | BadPass
+ | Bot
+ | Taken
+ | DoubleEmail
+ | DoubleIP
+ | Unchanged
+ | Changed Int Int -- DB entry updated, entry ID and revision number
+ | VNResult (List VN)
+ | StaffResult (List Staff)
+ | ProducerResult (List Producer)
+ | CharResult (List Char)
+ | TraitResult (List Trait)
+ | ReleaseResult (List Release)
+ | ImgFormat
+ | Image Int Int Int -- Uploaded image (id, width, height)
+ | Content String -- Text content
+
+
+decodeResponse : JD.Decoder Response
+decodeResponse = JD.oneOf
+ [ JD.field "Success" <| JD.succeed Success
+ , JD.field "Throttled" <| JD.succeed Throttled
+ , JD.field "CSRF" <| JD.succeed CSRF
+ , JD.field "Invalid" <| JD.map Invalid JD.value
+ , JD.field "Unauth" <| JD.succeed Unauth
+ , JD.field "BadEmail" <| JD.succeed BadEmail
+ , JD.field "BadLogin" <| JD.succeed BadLogin
+ , JD.field "BadPass" <| JD.succeed BadPass
+ , JD.field "Bot" <| JD.succeed Bot
+ , JD.field "Taken" <| JD.succeed Taken
+ , JD.field "DoubleEmail" <| JD.succeed DoubleEmail
+ , JD.field "DoubleIP" <| JD.succeed DoubleIP
+ , JD.field "Unchanged" <| JD.succeed Unchanged
+ , JD.field "Changed" <| JD.map2 Changed (JD.index 0 JD.int) (JD.index 1 JD.int)
+ , JD.field "VNResult" <| JD.map VNResult <| JD.list decodeVN
+ , JD.field "StaffResult" <| JD.map StaffResult <| JD.list decodeStaff
+ , JD.field "ProducerResult"<| JD.map ProducerResult <| JD.list decodeProducer
+ , JD.field "CharResult" <| JD.map CharResult <| JD.list decodeChar
+ , JD.field "TraitResult" <| JD.map TraitResult <| JD.list decodeTrait
+ , JD.field "ReleaseResult" <| JD.map ReleaseResult <| JD.list decodeRelease
+ , JD.field "ImgFormat" <| JD.succeed ImgFormat
+ , JD.field "Image" <| JD.map3 Image (JD.index 0 JD.int) (JD.index 1 JD.int) (JD.index 2 JD.int)
+ , JD.field "Content" <| JD.map Content JD.string
+ ]
+
+
+-- User-friendly error message if the response isn't what the code expected
+showResponse : Response -> String
+showResponse res =
+ let unexp = "Unexpected response, please report a bug."
+ in case res of
+ HTTPError (Http.Timeout) -> "Network timeout, please try again later."
+ HTTPError (Http.NetworkError) -> "Network error, please try again later."
+ HTTPError (Http.BadStatus r) -> "Server error " ++ String.fromInt r ++ ", please try again later, or report an issue if this persists."
+ HTTPError (Http.BadBody r) -> "Invalid response from the server, please report a bug (debug info: " ++ r ++")."
+ HTTPError (Http.BadUrl _) -> unexp
+ Success -> unexp
+ CSRF -> "Invalid CSRF token, please refresh the page and try again."
+ Throttled -> "Action throttled."
+ Invalid _ -> "Invalid form data, please report a bug." -- This error is already logged server-side, no debug info necessary
+ Unauth -> "You do not have the permission to perform this action."
+ BadEmail -> "Unknown email address."
+ BadLogin -> "Invalid username or password."
+ BadPass -> "Your chosen password is in a database of leaked passwords, please choose another one."
+ Bot -> "Invalid answer to the anti-bot question."
+ Taken -> "Username already taken, please choose a different name."
+ DoubleEmail -> "Email address already used for another account."
+ DoubleIP -> "You can only register one account from the same IP within 24 hours."
+ Unchanged -> "No changes"
+ Changed _ _ -> unexp
+ VNResult _ -> unexp
+ StaffResult _ -> unexp
+ ProducerResult _ -> unexp
+ CharResult _ -> unexp
+ TraitResult _ -> unexp
+ ReleaseResult _ -> unexp
+ ImgFormat -> "Unrecognized image format, please upload a JPG or PNG file."
+ Image _ _ _ -> unexp
+ Content _ -> unexp
+
+
+expectResponse : (Response -> msg) -> Http.Expect msg
+expectResponse msg =
+ let
+ res r = msg <| case r of
+ Err e -> HTTPError e
+ Ok v -> v
+ in Http.expectJson res decodeResponse
+
+
+-- Send a POST request with a JSON body to the VNDB API and get a Response back.
+post : String -> JE.Value -> (Response -> msg) -> Cmd msg
+post url body msg =
+ Http.post
+ { url = url
+ , body = Http.jsonBody body
+ , expect = expectResponse msg
+ }
+
+
+
+-- Simple image upload API
+
+type ImageType
+ = Cv
+ | Sf
+ | Ch
+
+
+onFileChange : (List File -> m) -> Attribute m
+onFileChange msg = on "change" <| JD.map msg <| JD.at ["target","files"] <| JD.list File.decoder
+
+
+-- Upload an image to /js/imageupload.json
+postImage : ImageType -> File -> (Response -> msg) -> Cmd msg
+postImage ty file msg =
+ let
+ tys = case ty of
+ Cv -> "cv"
+ Sf -> "sf"
+ Ch -> "ch"
+
+ body = Http.multipartBody
+ [ Http.stringPart "type" tys
+ , Http.filePart "img" file
+ ]
+ in Http.post
+ { url = "/js/imageupload.json"
+ , body = body
+ , expect = expectResponse msg
+ }