Andriy Drozdyuk
Andriy Drozdyuk

Reputation: 61121

Add css and images to snap's cabal build?

When I build my snap project, it does not include any of my static resources or templates.

My project structure is as follows:

site.cabal
\src
\snaplets
      \heist
           *.tpl
\static
       \css
           *.css
       \images
           *.jpg

Currently, I am deploying my binary to the server separately from my static files (which I simply copy over).

Is there a better way to do this? Any help appreciated.

Upvotes: 4

Views: 378

Answers (3)

jecxjo
jecxjo

Reputation: 174

You can embed static files into your code using Data.FileEmbed found in file-embed. I create a res directory and drop everything static in there. Then use the following code to access it.

{-# LANGUAGE TemplateHaskell #-}
-- src/Resources.hs

module Resources ( getResource, listResources ) where

import qualified Data.ByteString as BS
import Data.FileEmbed

resources :: [(FilePath, BS.ByteString)]
resources = $(embedDir "res")

getResource :: FilePath -> Maybe BS.ByteString
getResource path = findResource resources
  where
    findResource [] = Nothing
    findResource ((p,cont):xs) = if p == path
                                    then (Just cont)
                                    else findResource xs

listResources :: [FilePath]
listResources = map fst resources

Then in the code where you define your routes, you create a list of (route, handlers):

-- src/Site.hs
resRoutes :: [(ByteString, Handler App App ())]
resRoutes = map f listResources
  where
    f res =
      (TE.encodeUtf8 $ T.pack res, (writeBS . fromJust) $ getResource res)

-- in your SnapletInit
app = makeSnaplet "app" "" Nothing $ do
  addRoutes resRoutes
  addRoutes otherRoutes
  return $ App ()

File res/css/custom.css in your file system is accessible via http://<site>/css/custom.css. This works with text files, and binary files like images.

You should note that the static files are "consumed" during compilation of src/Resources.hs so if you add, modify or delete resources you'll have to trigger a rebuild of that file.


I'm still looking for a way to build the Heist templates into the executable. Should be similar, only modifying how Heist looks up and reads the files, need to use the resource list instead.

Upvotes: 0

mightybyte
mightybyte

Reputation: 7282

For my most recent project I created a script called deploy.sh that looks something like this:

#!/bin/sh
rf -fr ../myapp-deploy/*
cp -R dist/build/myapp/myapp log snaplets static ../myapp-deploy

Then I deploy everything in myapp-deploy. This can be done a number of ways. One approach is to zip it up and ftp/scp it to your deployment server. Another approach that I like and have used in the past is to make myapp-deploy into its own git repository. Then after I run deploy.sh, I commit everything in myapp-deploy and push it to some centralized repository. Then on my deployment server I can do git pull && killall -HUP myapp to go live with the most recent version. The benefit to having it in a git repository is that I can always revert back to the previous version very easily. If you have dynamic filesystem resources created by your users, then this approach might not work as well for you.

At the end of the day, reliable production deployment is a complex problem that needs an individualized approach. Something like this can be a useful guide, but can't replace the need for good IT engineering.

Upvotes: 2

dflemstr
dflemstr

Reputation: 26167

You can't embed your static files into the executable while still having things work as they should wrt the rest of snap.

Also, in Windows there's a "resource system" in PE files which lets you embed resources, but on Linux/Unix there isn't. So, using external files is the only reliable way to ensure that they are reachable on every platform.

Additionally, you might want to add or remove static files when users e.g. upload them, and that's not possible with embedded resources.

Upvotes: 2

Related Questions