packages feed

darcsden-0.4: src/WebServer.hs

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Control.Monad (forM_)
import Control.Monad.Trans
import Database.CouchDB
import Snap.Http.Server
import System.Environment
import Text.JSON

import DarcsDen.Handler
import DarcsDen.State.Util


main :: IO ()
main = do
    as <- getArgs
    case as of
        ("--readme":_) ->
            putStr . unlines $
                [ "darcsden is a darcs hosting platform, providing a simple website and"
                , "a SSH server for push/pull and quick repository creation."
                , ""
                , "You will need:"
                , "- CouchDB, with the databases and views described below."
                , "- Redis"
                , ""
                , "Both should be running on their default port, or just edit"
                , "DarcsDen/State/Util.hs to point to different ones."
                , ""
                , ""
                , "To install:"
                , ""
                , "    cabal install"
                , "    mkdir -p /srv/darcs"
                , "    adduser --system --home /srv/darcs --no-create-home --shell /bin/bash --group --gecos DarcsDen darcsden"
                , "    ssh-keygen -- save to /srv/darcs/.ssh/id_rsa"
                , "    chown -R darcsden:darcsden /srv/darcs"
                , ""
                , "    -- make sure CouchDB is running"
                , "    darcsden --install"
                , ""
                , ""
                , "To start:"
                , "This package provides separate executables for the web and SSH servers,"
                , "`darcsden' and `darcsden-ssh', respectively. I recommend running them in"
                , "screen as user 'darcsden'."
                , ""
                , "The web server should be started from the directory containing /public, or"
                , "you can run it behind nginx or something that'll handle /public requests by"
                , "pointing to that directory."
                ]

        ("--install":_) -> runDB $ do
            liftIO (putStrLn "creating databases...")

            createDB "repositories"
            createDB "users"

            liftIO (putStrLn "creating repository design documents...")
            forM_ repoDesigns $ \js ->
                newDoc (db "repositories") js

            liftIO (putStrLn "creating user design documents...")
            forM_ userDesigns $ \js ->
                newDoc (db "users") js

            liftIO (putStrLn "All set!")

        ("--port":p:_) -> do
            checkDBs
            putStrLn $ "darcsden running on port " ++ p
            startHTTP (read p)

        [] -> do
            checkDBs
            putStrLn "darcsden running on port 8080"
            startHTTP 8080

        _ ->
            putStr . unlines $
                [ "usage:"
                , "  darcsden --readme    : figure out how to use this thing"
                , "  darcsden --install   : set up CouchDB databases"
                , "  darcsden --port PORT : start webserver on given port"
                , "  darcsden             : start webserver on port 8080"
                ]
  where
    startHTTP p = httpServe
        "*"
        p
        "127.0.0.1"
        (Just "/srv/darcs/access.log")
        (Just "/srv/darcs/error.log")
        handler

    checkDBs = do
        putStrLn "checking couchdb..."
        runDB (return ())

        putStrLn "checking redis..."
        withRedis (return ())

    repoDesigns =
        [ jsobj
            [ ("_id", jsstr "_design/repositories")
            , ("language", jsstr "javascript")
            , ("views", jsobj
                [ ("by_owner_and_name", jsobj
                    [ ("map", jsstr "function(doc) {\n  if (doc.owner && doc.name && !doc.is_private)\n    emit([doc.owner, doc.name], doc);\n}")
                    ])

                , ("by_owner", jsobj
                    [ ("map", jsstr "function(doc) {\n  if (doc.owner && !doc.is_private)\n    emit(doc.owner, doc);\n}")
                    ])

                , ("by_fork", jsobj
                    [ ("map", jsstr "function(doc) {\n  if (doc.fork_of.Just && !doc.is_private)\n    emit(doc.fork_of.Just, doc);\n}")
                    ])

                , ("by_member", jsobj
                    [ ("map", jsstr "function(doc) {\n  if (!doc.is_private)\n    for (member in doc.members)\n      emit(doc.members[member], doc._id);\n}")
                    ])
                ])
            ]

        , jsobj
            [ ("_id", jsstr "_design/private")
            , ("language", jsstr "javascript")
            , ("views", jsobj
                [ ("by_owner", jsobj
                    [ ("map", jsstr "function(doc) {\n  emit(doc.owner, doc);\n}\n")
                    ])
                , ("by_owner_and_name", jsobj
                    [ ("map", jsstr "function(doc) {\n  if (doc.owner && doc.name)\n    emit([doc.owner, doc.name], doc);\n}")
                    ])
                , ("by_member", jsobj
                    [ ("map", jsstr "function(doc) {\n  for (member in doc.members)\n    emit(doc.members[member], [doc.owner, doc.name]);\n}")
                    ])
                ])
            ]
        ]

    userDesigns =
        [ jsobj
            [ ("_id", jsstr "_design/users")
            , ("language", jsstr "javascript")
            , ("views", jsobj
                [ ("by_email", jsobj
                    [ ("map", jsstr "function(doc) {\n  if (doc.email)\n    emit(doc.email, doc);\n}")
                    ])
                , ("by_name", jsobj
                    [ ("map", jsstr "function(doc) {\n  if (doc.name)\n    emit(doc.name, doc);\n}")
                    ])
                ])
            ]
        ]

    jsstr = JSString . toJSString
    jsobj = JSObject . toJSObject