generated from coulomb/repo-seed
feat: minimal IHP scaffold — T01-T05, T08 of IRP-WP-0001
- flake.nix adapted from inter-hub: appName=ihp-railiance-probe, stripped to core packages, GHC 9.10.3 Bug 1+2 overlays carried verbatim (pname check updated to ihp-railiance-probe-models) - IHP project scaffold: Main.hs, Config.hs, App.cabal, Setup.hs, Makefile - Schema: probes table (id, name, created_at) - Health endpoint: GET /healthz → "ok" (HealthController) - Probes CRUD: ProbesController + 4 views (Index, New, Show, Edit) - Hspec test suite: Test/ProbeControllerSpec covers /probes and /healthz - Helm chart in chart/: deployment, service, ingress, secret templates - devenv.nix, devenv.yaml, .ghci, tailwind config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
7
Web/Controller/Health.hs
Normal file
7
Web/Controller/Health.hs
Normal file
@@ -0,0 +1,7 @@
|
||||
module Web.Controller.Health where
|
||||
|
||||
import Web.Controller.Prelude
|
||||
import Web.Types
|
||||
|
||||
instance Controller HealthController where
|
||||
action HealthAction = renderPlain "ok"
|
||||
12
Web/Controller/Prelude.hs
Normal file
12
Web/Controller/Prelude.hs
Normal file
@@ -0,0 +1,12 @@
|
||||
module Web.Controller.Prelude
|
||||
( module Web.Types
|
||||
, module Generated.Types
|
||||
, module IHP.Prelude
|
||||
, module IHP.ControllerPrelude
|
||||
) where
|
||||
|
||||
import Web.Types
|
||||
import Generated.Types
|
||||
import IHP.Prelude
|
||||
import IHP.ControllerPrelude
|
||||
import Web.Routes ()
|
||||
42
Web/Controller/Probes.hs
Normal file
42
Web/Controller/Probes.hs
Normal file
@@ -0,0 +1,42 @@
|
||||
module Web.Controller.Probes where
|
||||
|
||||
import Web.Controller.Prelude
|
||||
import Web.View.Probes.Index
|
||||
import Web.View.Probes.New
|
||||
import Web.View.Probes.Show
|
||||
import Web.View.Probes.Edit
|
||||
|
||||
instance Controller ProbesController where
|
||||
action ProbesAction = do
|
||||
probes <- query @Probe |> fetch
|
||||
render IndexView { .. }
|
||||
|
||||
action NewProbeAction = do
|
||||
let probe = newRecord @Probe
|
||||
render NewView { .. }
|
||||
|
||||
action ShowProbeAction { probeId } = do
|
||||
probe <- fetch probeId
|
||||
render ShowView { .. }
|
||||
|
||||
action CreateProbeAction = do
|
||||
newRecord @Probe
|
||||
|> fill @'["name"]
|
||||
|> createRecord
|
||||
redirectTo ProbesAction
|
||||
|
||||
action EditProbeAction { probeId } = do
|
||||
probe <- fetch probeId
|
||||
render EditView { .. }
|
||||
|
||||
action UpdateProbeAction { probeId } = do
|
||||
probe <- fetch probeId
|
||||
probe
|
||||
|> fill @'["name"]
|
||||
|> updateRecord
|
||||
redirectTo ProbesAction
|
||||
|
||||
action DeleteProbeAction { probeId } = do
|
||||
probe <- fetch probeId
|
||||
deleteRecord probe
|
||||
redirectTo ProbesAction
|
||||
36
Web/FrontController.hs
Normal file
36
Web/FrontController.hs
Normal file
@@ -0,0 +1,36 @@
|
||||
module Web.FrontController where
|
||||
|
||||
import IHP.RouterPrelude
|
||||
import IHP.ControllerPrelude
|
||||
import IHP.ViewPrelude (Html, hsx, Layout)
|
||||
import Generated.Types
|
||||
import Web.Types
|
||||
import Web.Routes ()
|
||||
|
||||
import Web.Controller.Health ()
|
||||
import Web.Controller.Probes ()
|
||||
|
||||
instance FrontController WebApplication where
|
||||
controllers =
|
||||
[ parseRoute @HealthController
|
||||
, parseRoute @ProbesController
|
||||
]
|
||||
|
||||
instance InitControllerContext WebApplication where
|
||||
initContext = do
|
||||
setLayout defaultLayout
|
||||
|
||||
defaultLayout :: (?context :: ControllerContext, ?request :: Request) => Layout
|
||||
defaultLayout inner = [hsx|
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>ihp-railiance-probe</title>
|
||||
</head>
|
||||
<body>
|
||||
{inner}
|
||||
</body>
|
||||
</html>
|
||||
|]
|
||||
16
Web/Routes.hs
Normal file
16
Web/Routes.hs
Normal file
@@ -0,0 +1,16 @@
|
||||
module Web.Routes where
|
||||
|
||||
import IHP.RouterPrelude
|
||||
import Generated.Types
|
||||
import Web.Types
|
||||
|
||||
instance CanRoute HealthController where
|
||||
parseRoute' = do
|
||||
pathPrefix "/healthz"
|
||||
endOfInput
|
||||
pure HealthAction
|
||||
|
||||
instance HasPath HealthController where
|
||||
pathTo HealthAction = "/healthz"
|
||||
|
||||
instance AutoRoute ProbesController
|
||||
21
Web/Types.hs
Normal file
21
Web/Types.hs
Normal file
@@ -0,0 +1,21 @@
|
||||
module Web.Types where
|
||||
|
||||
import IHP.Prelude
|
||||
import IHP.ModelSupport
|
||||
import Generated.Types
|
||||
|
||||
data WebApplication = WebApplication deriving (Eq, Show)
|
||||
|
||||
data HealthController
|
||||
= HealthAction
|
||||
deriving (Eq, Show, Data)
|
||||
|
||||
data ProbesController
|
||||
= ProbesAction
|
||||
| NewProbeAction
|
||||
| ShowProbeAction { probeId :: !(Id Probe) }
|
||||
| CreateProbeAction
|
||||
| EditProbeAction { probeId :: !(Id Probe) }
|
||||
| UpdateProbeAction { probeId :: !(Id Probe) }
|
||||
| DeleteProbeAction { probeId :: !(Id Probe) }
|
||||
deriving (Eq, Show, Data)
|
||||
12
Web/View/Prelude.hs
Normal file
12
Web/View/Prelude.hs
Normal file
@@ -0,0 +1,12 @@
|
||||
module Web.View.Prelude
|
||||
( module Web.Types
|
||||
, module Generated.Types
|
||||
, module IHP.Prelude
|
||||
, module IHP.ViewPrelude
|
||||
) where
|
||||
|
||||
import Web.Types
|
||||
import Generated.Types
|
||||
import IHP.Prelude
|
||||
import IHP.ViewPrelude
|
||||
import Web.Routes ()
|
||||
17
Web/View/Probes/Edit.hs
Normal file
17
Web/View/Probes/Edit.hs
Normal file
@@ -0,0 +1,17 @@
|
||||
module Web.View.Probes.Edit where
|
||||
|
||||
import Web.View.Prelude
|
||||
|
||||
data EditView = EditView { probe :: Probe }
|
||||
|
||||
instance View EditView where
|
||||
html EditView { .. } = [hsx|
|
||||
<h1>Edit Probe</h1>
|
||||
{renderForm probe}
|
||||
|]
|
||||
|
||||
renderForm :: Probe -> Html
|
||||
renderForm probe = formFor probe [hsx|
|
||||
{textField #name}
|
||||
{submitButton}
|
||||
|]
|
||||
22
Web/View/Probes/Index.hs
Normal file
22
Web/View/Probes/Index.hs
Normal file
@@ -0,0 +1,22 @@
|
||||
module Web.View.Probes.Index where
|
||||
|
||||
import Web.View.Prelude
|
||||
|
||||
data IndexView = IndexView { probes :: [Probe] }
|
||||
|
||||
instance View IndexView where
|
||||
html IndexView { .. } = [hsx|
|
||||
<h1>Probes</h1>
|
||||
<a href={NewProbeAction}>New Probe</a>
|
||||
<ul>
|
||||
{forEach probes renderProbe}
|
||||
</ul>
|
||||
|]
|
||||
|
||||
renderProbe :: Probe -> Html
|
||||
renderProbe probe = [hsx|
|
||||
<li>
|
||||
<a href={ShowProbeAction probe.id}>{probe.name}</a>
|
||||
<a href={DeleteProbeAction probe.id} class="js-delete">Delete</a>
|
||||
</li>
|
||||
|]
|
||||
17
Web/View/Probes/New.hs
Normal file
17
Web/View/Probes/New.hs
Normal file
@@ -0,0 +1,17 @@
|
||||
module Web.View.Probes.New where
|
||||
|
||||
import Web.View.Prelude
|
||||
|
||||
data NewView = NewView { probe :: Probe }
|
||||
|
||||
instance View NewView where
|
||||
html NewView { .. } = [hsx|
|
||||
<h1>New Probe</h1>
|
||||
{renderForm probe}
|
||||
|]
|
||||
|
||||
renderForm :: Probe -> Html
|
||||
renderForm probe = formFor probe [hsx|
|
||||
{textField #name}
|
||||
{submitButton}
|
||||
|]
|
||||
11
Web/View/Probes/Show.hs
Normal file
11
Web/View/Probes/Show.hs
Normal file
@@ -0,0 +1,11 @@
|
||||
module Web.View.Probes.Show where
|
||||
|
||||
import Web.View.Prelude
|
||||
|
||||
data ShowView = ShowView { probe :: Probe }
|
||||
|
||||
instance View ShowView where
|
||||
html ShowView { .. } = [hsx|
|
||||
<h1>{probe.name}</h1>
|
||||
<a href={ProbesAction}>Back</a>
|
||||
|]
|
||||
Reference in New Issue
Block a user