fix: add static file serving with custom middleware
- Replaced wai-app-static with lightweight inline static file server - Static files served from frontend/static/ under /static/ path - Fixed ByteString/FilePath type mismatches for GHC 9.10 - Verified: CSS, manifest, login, signup, dashboard all HTTP 200
This commit is contained in:
+40
-7
@@ -1,15 +1,20 @@
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE TypeOperators #-}
|
||||
{-# OPTIONS_GHC -Wno-unused-imports -Wno-missing-export-lists #-}
|
||||
{-# OPTIONS_GHC -Wno-unused-imports -Wno-missing-export-lists -Wno-name-shadowing #-}
|
||||
|
||||
module Main where
|
||||
|
||||
import Data.ByteString.Char8 qualified as C8
|
||||
import Data.ByteString qualified as BS
|
||||
import Data.ByteString.Lazy qualified as BL
|
||||
import Effectful
|
||||
import Network.Wai.Application.Static qualified as Static
|
||||
import Network.HTTP.Types qualified as HTTP
|
||||
import Network.Wai qualified as Wai
|
||||
import Network.Wai.Handler.Warp qualified as Warp
|
||||
import System.Directory (createDirectoryIfMissing)
|
||||
import System.Directory (createDirectoryIfMissing, doesFileExist)
|
||||
import System.FilePath (takeDirectory)
|
||||
import Data.List (isSuffixOf)
|
||||
|
||||
import Sis.Database
|
||||
import Sis.Page.Activity
|
||||
@@ -26,6 +31,16 @@ import Web.Hyperbole.Effect.Response
|
||||
import Web.Hyperbole.Page
|
||||
import Web.Hyperbole.Route
|
||||
|
||||
-- Simple MIME type resolver for static files
|
||||
mimeType :: FilePath -> BS.ByteString
|
||||
mimeType fp
|
||||
| ".css" `isSuffixOf` fp = "text/css"
|
||||
| ".js" `isSuffixOf` fp = "application/javascript"
|
||||
| ".json" `isSuffixOf` fp = "application/json"
|
||||
| ".png" `isSuffixOf` fp = "image/png"
|
||||
| ".svg" `isSuffixOf` fp = "image/svg+xml"
|
||||
| otherwise = "application/octet-stream"
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
let dbPath = "data/sis.db"
|
||||
@@ -37,14 +52,32 @@ main = do
|
||||
let port = 8080
|
||||
putStrLn $ "[sis] listening on 0.0.0.0:" <> show port
|
||||
|
||||
let app = liveAppWith
|
||||
(ServerOptions
|
||||
let hyperboleApp =
|
||||
liveAppWith
|
||||
( ServerOptions
|
||||
{ toDocument = document documentHead
|
||||
, serverError = defaultError
|
||||
, parseRequestBody = defaultParseRequestBodyOptions
|
||||
})
|
||||
}
|
||||
)
|
||||
(runDB conn $ routeRequest router)
|
||||
Warp.run port $ app
|
||||
|
||||
-- Serve static files under /static/, fall through to Hyperbole app
|
||||
let staticDir = "frontend/static"
|
||||
Warp.run port $ \req respond -> do
|
||||
let rawPath = Wai.rawPathInfo req
|
||||
if "/static/" `BS.isPrefixOf` rawPath
|
||||
then do
|
||||
let relPath = C8.unpack (C8.drop (C8.length "/static") rawPath)
|
||||
filePath = staticDir ++ relPath
|
||||
exists <- doesFileExist filePath
|
||||
if exists
|
||||
then do
|
||||
content <- BS.readFile filePath
|
||||
let ct = mimeType filePath
|
||||
respond $ Wai.responseLBS HTTP.status200 [("Content-Type", ct)] (BL.fromStrict content)
|
||||
else respond $ Wai.responseLBS HTTP.status404 [] "File not found"
|
||||
else hyperboleApp req respond
|
||||
|
||||
router :: (Hyperbole :> es, DB :> es, IOE :> es) => AppRoute -> Eff es Response
|
||||
router Home = do
|
||||
|
||||
Reference in New Issue
Block a user