diff --git a/.github/workflows/ci-compiled-scripts.yaml b/.github/workflows/ci-compiled-scripts.yaml index e997d0f..3038ac2 100644 --- a/.github/workflows/ci-compiled-scripts.yaml +++ b/.github/workflows/ci-compiled-scripts.yaml @@ -45,4 +45,5 @@ jobs: # git diff --quiet implies --exit-code run: | cabal run export-smart-tokens + cabal run write-openapi-schema -- generated/openapi/schema.json git diff --quiet diff --git a/generated/openapi/schema.json b/generated/openapi/schema.json index 077eb34..f08f0ad 100644 --- a/generated/openapi/schema.json +++ b/generated/openapi/schema.json @@ -213,7 +213,7 @@ }, "openapi": "3.0.0", "paths": { - "/healthcheck": { + "/api/v1/healthcheck": { "get": { "description": "Is the server alive?", "responses": { @@ -226,7 +226,7 @@ } } }, - "/query/address/{address}": { + "/api/v1/query/address/{address}": { "get": { "description": "The user's receiving address for programmable tokens", "parameters": [ @@ -258,7 +258,7 @@ } } }, - "/query/all-funds": { + "/api/v1/query/all-funds": { "get": { "description": "Total value of all programmable tokens", "responses": { @@ -275,7 +275,7 @@ } } }, - "/query/blacklist/{address}": { + "/api/v1/query/blacklist/{address}": { "get": { "description": "The list of addresses that have been blacklisted", "parameters": [ @@ -310,7 +310,7 @@ } } }, - "/query/global-params": { + "/api/v1/query/global-params": { "get": { "description": "The UTxO with the global parameters", "responses": { @@ -327,7 +327,7 @@ } } }, - "/query/user-funds/{address}": { + "/api/v1/query/user-funds/{address}": { "get": { "description": "Total value locked in programmable token outputs addressed to the user", "parameters": [ @@ -359,7 +359,7 @@ } } }, - "/tx/programmable-token/blacklist": { + "/api/v1/tx/programmable-token/blacklist": { "post": { "description": "Add a credential to the blacklist", "requestBody": { @@ -388,7 +388,7 @@ } } }, - "/tx/programmable-token/issue": { + "/api/v1/tx/programmable-token/issue": { "post": { "description": "Create some programmable tokens", "requestBody": { @@ -417,7 +417,7 @@ } } }, - "/tx/programmable-token/seize": { + "/api/v1/tx/programmable-token/seize": { "post": { "description": "Seize a user's funds", "requestBody": { @@ -446,7 +446,7 @@ } } }, - "/tx/programmable-token/transfer": { + "/api/v1/tx/programmable-token/transfer": { "post": { "description": "Transfer programmable tokens from one address to another", "requestBody": { diff --git a/src/lib/Wst/Cli.hs b/src/lib/Wst/Cli.hs index aa7db85..3424892 100644 --- a/src/lib/Wst/Cli.hs +++ b/src/lib/Wst/Cli.hs @@ -8,6 +8,7 @@ import Control.Monad.IO.Class (MonadIO (..)) import Convex.Wallet.Operator (OperatorConfigSigning) import Convex.Wallet.Operator qualified as Operator import Data.Functor.Identity (Identity) +import Data.Maybe (fromMaybe) import Data.Proxy (Proxy) import Data.String (IsString (..)) import Options.Applicative (customExecParser, disambiguate, helper, idm, info, @@ -55,6 +56,6 @@ deploy config = do pure () startServer :: (MonadIO m, MonadLogger m) => Env.CombinedEnv Proxy Identity Proxy Identity w -> Server.ServerArgs -> m () -startServer env' serverArgs@ServerArgs{saPort} = do - logInfo $ "starting server" :# ["port" .= saPort] +startServer env' serverArgs@ServerArgs{saPort, saStaticFiles} = do + logInfo $ "starting server" :# ["port" .= saPort, "static_files" .= fromMaybe "(no static files)" saStaticFiles] liftIO (Server.runServer env' serverArgs) diff --git a/src/lib/Wst/Cli/Command.hs b/src/lib/Wst/Cli/Command.hs index b11b641..3e3326e 100644 --- a/src/lib/Wst/Cli/Command.hs +++ b/src/lib/Wst/Cli/Command.hs @@ -15,6 +15,7 @@ import Options.Applicative (CommandFields, Mod, Parser, ReadM, argument, auto, command, eitherReader, fullDesc, help, info, long, metavar, option, optional, progDesc, short, subparser, value) +import Options.Applicative.Builder (strOption) import Text.Read (readMaybe) import Wst.Server (ServerArgs (..)) @@ -64,7 +65,7 @@ parseServerArgs :: Parser ServerArgs parseServerArgs = ServerArgs <$> option auto (help "The port" <> value 8080 <> long "port" <> short 'p') - <*> optional (option auto (help "Folder to serve static files from" <> long "static-files")) + <*> optional (strOption (help "Folder to serve static files from" <> long "static-files")) parseTxIn :: Parser TxIn parseTxIn = diff --git a/src/lib/Wst/Server.hs b/src/lib/Wst/Server.hs index 10ca11d..2ee272d 100644 --- a/src/lib/Wst/Server.hs +++ b/src/lib/Wst/Server.hs @@ -6,6 +6,7 @@ module Wst.Server( runServer, ServerArgs(..), + CombinedAPI, defaultServerArgs ) where @@ -19,8 +20,9 @@ import Data.Data (Proxy (..)) import Network.Wai.Handler.Warp qualified as Warp import PlutusTx.Prelude qualified as P import Servant (Server, ServerT) -import Servant.API (NoContent (..), (:<|>) (..)) +import Servant.API (NoContent (..), Raw, (:<|>) (..)) import Servant.Server (hoistServer, serve) +import Servant.Server.StaticFiles (serveDirectoryWebApp) import SmartTokens.Types.PTokenDirectory (blnKey) import Wst.App (WstApp, runWstAppServant) import Wst.AppError (AppError) @@ -34,6 +36,12 @@ import Wst.Server.Types (APIInEra, AddToBlacklistArgs (..), BuildTxAPI, TextEnvelopeJSON (..), TransferProgrammableTokenArgs (..)) +-- | Rest API combined with a Raw endpoint +-- for static files +type CombinedAPI = + APIInEra + :<|> Raw + data ServerArgs = ServerArgs { saPort :: !Int @@ -49,8 +57,10 @@ defaultServerArgs = } runServer :: (Env.HasRuntimeEnv env, Env.HasDirectoryEnv env) => env -> ServerArgs -> IO () -runServer env ServerArgs{saPort} = do - let app = serve (Proxy @APIInEra) (server env) +runServer env ServerArgs{saPort, saStaticFiles} = do + let app = case saStaticFiles of + Nothing -> serve (Proxy @APIInEra) (server env) + Just fp -> serve (Proxy @CombinedAPI) (server env :<|> serveDirectoryWebApp fp) port = saPort Warp.run port app diff --git a/src/lib/Wst/Server/Types.hs b/src/lib/Wst/Server/Types.hs index fbb58ca..b145cb2 100644 --- a/src/lib/Wst/Server/Types.hs +++ b/src/lib/Wst/Server/Types.hs @@ -46,7 +46,7 @@ import SmartTokens.Types.ProtocolParams (ProgrammableLogicGlobalParams) import Wst.JSON.Utils qualified as JSON import Wst.Offchain.Query (UTxODat (..)) -type APIInEra = API C.ConwayEra +type APIInEra = "api" :> "v1" :> API C.ConwayEra newtype TextEnvelopeJSON a = TextEnvelopeJSON{ unTextEnvelopeJSON :: a }