2012-07-31 05:11:32 +00:00
|
|
|
{- git-annex assistant webapp dashboard
|
|
|
|
-
|
|
|
|
- Copyright 2012 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
2012-08-03 13:44:43 +00:00
|
|
|
{-# LANGUAGE CPP, TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}
|
2012-07-31 05:11:32 +00:00
|
|
|
|
|
|
|
module Assistant.WebApp.DashBoard where
|
|
|
|
|
|
|
|
import Assistant.Common
|
|
|
|
import Assistant.WebApp
|
|
|
|
import Assistant.WebApp.SideBar
|
|
|
|
import Assistant.WebApp.Notifications
|
|
|
|
import Assistant.WebApp.Configurators
|
|
|
|
import Assistant.DaemonStatus
|
|
|
|
import Assistant.TransferQueue
|
2012-08-10 22:42:44 +00:00
|
|
|
import Assistant.TransferSlots
|
2012-08-12 16:11:20 +00:00
|
|
|
import qualified Assistant.Threads.Transferrer as Transferrer
|
2012-07-31 05:11:32 +00:00
|
|
|
import Utility.NotificationBroadcaster
|
|
|
|
import Utility.Yesod
|
|
|
|
import Logs.Transfer
|
|
|
|
import Utility.Percentage
|
|
|
|
import Utility.DataUnits
|
|
|
|
import Types.Key
|
|
|
|
import qualified Remote
|
2012-08-03 13:44:43 +00:00
|
|
|
import qualified Git
|
2012-08-27 17:43:03 +00:00
|
|
|
import Locations.UserConfig
|
2012-07-31 05:11:32 +00:00
|
|
|
|
|
|
|
import Yesod
|
|
|
|
import Text.Hamlet
|
|
|
|
import qualified Data.Map as M
|
2012-08-03 14:18:57 +00:00
|
|
|
import Control.Concurrent
|
2012-08-10 19:45:00 +00:00
|
|
|
import System.Posix.Signals (signalProcessGroup, sigTERM, sigKILL)
|
|
|
|
import System.Posix.Process (getProcessGroupIDOf)
|
2012-07-31 05:11:32 +00:00
|
|
|
|
|
|
|
{- A display of currently running and queued transfers.
|
|
|
|
-
|
|
|
|
- Or, if there have never been any this run, an intro display. -}
|
|
|
|
transfersDisplay :: Bool -> Widget
|
|
|
|
transfersDisplay warnNoScript = do
|
|
|
|
webapp <- lift getYesod
|
2012-08-12 16:11:20 +00:00
|
|
|
current <- lift $ M.toList <$> getCurrentTransfers
|
2012-07-31 05:11:32 +00:00
|
|
|
queued <- liftIO $ getTransferQueue $ transferQueue webapp
|
|
|
|
let ident = "transfers"
|
|
|
|
autoUpdate ident NotifierTransfersR (10 :: Int) (10 :: Int)
|
2012-08-29 17:41:47 +00:00
|
|
|
let transfers = simplifyTransfers $ current ++ queued
|
2012-07-31 05:11:32 +00:00
|
|
|
if null transfers
|
|
|
|
then ifM (lift $ showIntro <$> getWebAppState)
|
|
|
|
( introDisplay ident
|
2012-07-31 05:24:49 +00:00
|
|
|
, $(widgetFile "dashboard/transfers")
|
2012-07-31 05:11:32 +00:00
|
|
|
)
|
2012-07-31 05:24:49 +00:00
|
|
|
else $(widgetFile "dashboard/transfers")
|
2012-08-10 22:50:21 +00:00
|
|
|
where
|
|
|
|
isrunning info = not $
|
|
|
|
transferPaused info || isNothing (startedTime info)
|
2012-07-31 05:11:32 +00:00
|
|
|
|
2012-08-29 19:24:09 +00:00
|
|
|
{- Simplifies a list of transfers, avoiding display of redundant
|
|
|
|
- equivilant transfers. -}
|
2012-08-29 17:41:47 +00:00
|
|
|
simplifyTransfers :: [(Transfer, TransferInfo)] -> [(Transfer, TransferInfo)]
|
|
|
|
simplifyTransfers [] = []
|
|
|
|
simplifyTransfers (x:[]) = [x]
|
|
|
|
simplifyTransfers (v@(t1, _):r@((t2, _):l))
|
2012-08-29 19:24:09 +00:00
|
|
|
| equivilantTransfer t1 t2 = simplifyTransfers (v:l)
|
2012-08-29 17:41:47 +00:00
|
|
|
| otherwise = v : (simplifyTransfers r)
|
|
|
|
|
2012-07-31 05:11:32 +00:00
|
|
|
{- Called by client to get a display of currently in process transfers.
|
|
|
|
-
|
|
|
|
- Returns a div, which will be inserted into the calling page.
|
|
|
|
-
|
|
|
|
- Note that the head of the widget is not included, only its
|
|
|
|
- body is. To get the widget head content, the widget is also
|
|
|
|
- inserted onto the getHomeR page.
|
|
|
|
-}
|
|
|
|
getTransfersR :: NotificationId -> Handler RepHtml
|
|
|
|
getTransfersR nid = do
|
|
|
|
waitNotifier transferNotifier nid
|
|
|
|
|
|
|
|
page <- widgetToPageContent $ transfersDisplay False
|
|
|
|
hamletToRepHtml $ [hamlet|^{pageBody page}|]
|
|
|
|
|
|
|
|
{- The main dashboard. -}
|
|
|
|
dashboard :: Bool -> Widget
|
|
|
|
dashboard warnNoScript = do
|
2012-08-04 00:40:34 +00:00
|
|
|
sideBarDisplay
|
2012-07-31 05:11:32 +00:00
|
|
|
let content = transfersDisplay warnNoScript
|
2012-07-31 05:24:49 +00:00
|
|
|
$(widgetFile "dashboard/main")
|
2012-07-31 05:11:32 +00:00
|
|
|
|
|
|
|
getHomeR :: Handler RepHtml
|
2012-07-31 18:23:17 +00:00
|
|
|
getHomeR = ifM (inFirstRun)
|
|
|
|
( redirect ConfigR
|
|
|
|
, bootstrap (Just DashBoard) $ dashboard True
|
|
|
|
)
|
2012-07-31 06:30:26 +00:00
|
|
|
|
|
|
|
{- Same as HomeR, except no autorefresh at all (and no noscript warning). -}
|
|
|
|
getNoScriptR :: Handler RepHtml
|
2012-07-31 18:23:17 +00:00
|
|
|
getNoScriptR = bootstrap (Just DashBoard) $ dashboard False
|
2012-07-31 05:11:32 +00:00
|
|
|
|
|
|
|
{- Same as HomeR, except with autorefreshing via meta refresh. -}
|
|
|
|
getNoScriptAutoR :: Handler RepHtml
|
2012-07-31 06:30:26 +00:00
|
|
|
getNoScriptAutoR = bootstrap (Just DashBoard) $ do
|
2012-07-31 05:11:32 +00:00
|
|
|
let ident = NoScriptR
|
|
|
|
let delayseconds = 3 :: Int
|
|
|
|
let this = NoScriptAutoR
|
2012-07-31 05:24:49 +00:00
|
|
|
toWidgetHead $(hamletFile $ hamletTemplate "dashboard/metarefresh")
|
2012-07-31 05:11:32 +00:00
|
|
|
dashboard False
|
2012-08-03 13:44:43 +00:00
|
|
|
|
|
|
|
{- The javascript code does a post. -}
|
|
|
|
postFileBrowserR :: Handler ()
|
|
|
|
postFileBrowserR = void openFileBrowser
|
|
|
|
|
|
|
|
{- Used by non-javascript browsers, where clicking on the link actually
|
|
|
|
- opens this page, so we redirect back to the referrer. -}
|
|
|
|
getFileBrowserR :: Handler ()
|
2012-08-08 20:06:01 +00:00
|
|
|
getFileBrowserR = whenM openFileBrowser $ redirectBack
|
|
|
|
|
|
|
|
redirectBack :: Handler ()
|
|
|
|
redirectBack = do
|
2012-08-03 13:44:43 +00:00
|
|
|
clearUltDest
|
|
|
|
setUltDestReferer
|
|
|
|
redirectUltDest HomeR
|
|
|
|
|
|
|
|
{- Opens the system file browser on the repo, or, as a fallback,
|
|
|
|
- goes to a file:// url. Returns True if it's ok to redirect away
|
2012-08-03 14:18:57 +00:00
|
|
|
- from the page (ie, the system file browser was opened).
|
|
|
|
-
|
|
|
|
- Note that the command is opened using a different thread, to avoid
|
|
|
|
- blocking the response to the browser on it. -}
|
2012-08-03 13:44:43 +00:00
|
|
|
openFileBrowser :: Handler Bool
|
|
|
|
openFileBrowser = do
|
|
|
|
path <- runAnnex (error "no configured repository") $
|
|
|
|
fromRepo Git.repoPath
|
2012-08-03 14:18:57 +00:00
|
|
|
ifM (liftIO $ inPath cmd <&&> inPath cmd)
|
|
|
|
( do
|
|
|
|
void $ liftIO $ forkIO $ void $
|
|
|
|
boolSystem cmd [Param path]
|
|
|
|
return True
|
2012-08-03 13:44:43 +00:00
|
|
|
, do
|
|
|
|
clearUltDest
|
|
|
|
setUltDest $ "file://" ++ path
|
|
|
|
void $ redirectUltDest HomeR
|
|
|
|
return False
|
|
|
|
)
|
|
|
|
where
|
|
|
|
#if OSX
|
|
|
|
cmd = "open"
|
|
|
|
#else
|
|
|
|
cmd = "xdg-open"
|
|
|
|
#endif
|
2012-08-08 20:06:01 +00:00
|
|
|
|
|
|
|
{- Transfer controls. The GET is done in noscript mode and redirects back
|
|
|
|
- to the referring page. The POST is called by javascript. -}
|
|
|
|
getPauseTransferR :: Transfer -> Handler ()
|
|
|
|
getPauseTransferR t = pauseTransfer t >> redirectBack
|
|
|
|
postPauseTransferR :: Transfer -> Handler ()
|
|
|
|
postPauseTransferR t = pauseTransfer t
|
|
|
|
getStartTransferR :: Transfer -> Handler ()
|
|
|
|
getStartTransferR t = startTransfer t >> redirectBack
|
|
|
|
postStartTransferR :: Transfer -> Handler ()
|
|
|
|
postStartTransferR t = startTransfer t
|
|
|
|
getCancelTransferR :: Transfer -> Handler ()
|
2012-08-10 22:42:44 +00:00
|
|
|
getCancelTransferR t = cancelTransfer False t >> redirectBack
|
2012-08-08 20:06:01 +00:00
|
|
|
postCancelTransferR :: Transfer -> Handler ()
|
2012-08-10 22:42:44 +00:00
|
|
|
postCancelTransferR t = cancelTransfer False t
|
2012-08-08 20:06:01 +00:00
|
|
|
|
2012-08-10 22:42:44 +00:00
|
|
|
pauseTransfer :: Transfer -> Handler ()
|
|
|
|
pauseTransfer = cancelTransfer True
|
|
|
|
|
|
|
|
cancelTransfer :: Bool -> Transfer-> Handler ()
|
|
|
|
cancelTransfer pause t = do
|
2012-08-08 21:55:56 +00:00
|
|
|
webapp <- getYesod
|
2012-08-10 20:00:24 +00:00
|
|
|
let dstatus = daemonStatus webapp
|
2012-08-12 16:11:20 +00:00
|
|
|
m <- getCurrentTransfers
|
2012-08-10 20:00:24 +00:00
|
|
|
liftIO $ do
|
2012-08-29 19:13:12 +00:00
|
|
|
unless pause $
|
|
|
|
{- remove queued transfer -}
|
2012-08-29 19:56:47 +00:00
|
|
|
void $ dequeueTransfers (transferQueue webapp) dstatus $
|
|
|
|
equivilantTransfer t
|
2012-08-10 20:00:24 +00:00
|
|
|
{- stop running transfer -}
|
2012-08-12 16:11:20 +00:00
|
|
|
maybe noop (stop dstatus) (M.lookup t m)
|
fork off git-annex copy for transfers
This doesn't quite work, because canceling a transfer sends a signal
to git-annex, but not to rsync (etc).
Looked at making git-annex run in its own process group, which could then
be killed, and would kill child processes. But, rsync checks if it's
process group is the foreground process group and doesn't show progress if
not, and when git has run git-annex, if git-annex makes a new process
group, that is not the case. Also, if git has run git-annex, ctrl-c
wouldn't be propigated to it if it made a new process group.
So this seems like a blind alley, but recording it here just in case.
2012-08-10 18:14:08 +00:00
|
|
|
where
|
2012-08-10 22:42:44 +00:00
|
|
|
stop dstatus info = do
|
fork off git-annex copy for transfers
This doesn't quite work, because canceling a transfer sends a signal
to git-annex, but not to rsync (etc).
Looked at making git-annex run in its own process group, which could then
be killed, and would kill child processes. But, rsync checks if it's
process group is the foreground process group and doesn't show progress if
not, and when git has run git-annex, if git-annex makes a new process
group, that is not the case. Also, if git has run git-annex, ctrl-c
wouldn't be propigated to it if it made a new process group.
So this seems like a blind alley, but recording it here just in case.
2012-08-10 18:14:08 +00:00
|
|
|
{- When there's a thread associated with the
|
|
|
|
- transfer, it's killed first, to avoid it
|
|
|
|
- displaying any alert about the transfer having
|
|
|
|
- failed when the transfer process is killed. -}
|
2012-08-10 22:42:44 +00:00
|
|
|
maybe noop signalthread $ transferTid info
|
fork off git-annex copy for transfers
This doesn't quite work, because canceling a transfer sends a signal
to git-annex, but not to rsync (etc).
Looked at making git-annex run in its own process group, which could then
be killed, and would kill child processes. But, rsync checks if it's
process group is the foreground process group and doesn't show progress if
not, and when git has run git-annex, if git-annex makes a new process
group, that is not the case. Also, if git has run git-annex, ctrl-c
wouldn't be propigated to it if it made a new process group.
So this seems like a blind alley, but recording it here just in case.
2012-08-10 18:14:08 +00:00
|
|
|
maybe noop killproc $ transferPid info
|
2012-08-10 22:42:44 +00:00
|
|
|
if pause
|
|
|
|
then void $
|
2012-08-29 18:14:57 +00:00
|
|
|
alterTransferInfo dstatus t $ info
|
2012-08-29 19:13:12 +00:00
|
|
|
{ transferPaused = True
|
|
|
|
, transferPid = Nothing }
|
2012-08-10 22:42:44 +00:00
|
|
|
else void $
|
|
|
|
removeTransfer dstatus t
|
|
|
|
signalthread tid
|
|
|
|
| pause = throwTo tid PauseTransfer
|
|
|
|
| otherwise = killThread tid
|
2012-08-10 19:45:00 +00:00
|
|
|
{- In order to stop helper processes like rsync,
|
|
|
|
- kill the whole process group of the process running the
|
|
|
|
- transfer. -}
|
fork off git-annex copy for transfers
This doesn't quite work, because canceling a transfer sends a signal
to git-annex, but not to rsync (etc).
Looked at making git-annex run in its own process group, which could then
be killed, and would kill child processes. But, rsync checks if it's
process group is the foreground process group and doesn't show progress if
not, and when git has run git-annex, if git-annex makes a new process
group, that is not the case. Also, if git has run git-annex, ctrl-c
wouldn't be propigated to it if it made a new process group.
So this seems like a blind alley, but recording it here just in case.
2012-08-10 18:14:08 +00:00
|
|
|
killproc pid = do
|
2012-08-10 19:45:00 +00:00
|
|
|
g <- getProcessGroupIDOf pid
|
|
|
|
void $ tryIO $ signalProcessGroup sigTERM g
|
2012-08-29 19:13:12 +00:00
|
|
|
threadDelay 50000 -- 0.05 second grace period
|
2012-08-10 19:45:00 +00:00
|
|
|
void $ tryIO $ signalProcessGroup sigKILL g
|
2012-08-12 16:11:20 +00:00
|
|
|
|
|
|
|
startTransfer :: Transfer -> Handler ()
|
|
|
|
startTransfer t = do
|
|
|
|
m <- getCurrentTransfers
|
2012-08-29 18:14:57 +00:00
|
|
|
maybe noop go (M.lookup t m)
|
2012-08-12 16:11:20 +00:00
|
|
|
-- TODO: handle starting a queued transfer
|
|
|
|
where
|
2012-08-29 18:14:57 +00:00
|
|
|
go info = maybe (start info) (resume info) $ transferTid info
|
|
|
|
resume info tid = do
|
|
|
|
webapp <- getYesod
|
|
|
|
let dstatus = daemonStatus webapp
|
|
|
|
liftIO $ do
|
|
|
|
alterTransferInfo dstatus t $ info
|
|
|
|
{ transferPaused = False }
|
|
|
|
throwTo tid ResumeTransfer
|
2012-08-12 16:11:20 +00:00
|
|
|
start info = do
|
|
|
|
webapp <- getYesod
|
|
|
|
let dstatus = daemonStatus webapp
|
|
|
|
let slots = transferSlots webapp
|
|
|
|
{- This transfer was being run by another process,
|
|
|
|
- forget that old pid, and start a new one. -}
|
2012-08-29 18:14:57 +00:00
|
|
|
liftIO $ alterTransferInfo dstatus t $ info
|
|
|
|
{ transferPid = Nothing, transferPaused = False }
|
2012-08-28 21:17:09 +00:00
|
|
|
liftIO $ inImmediateTransferSlot dstatus slots $ do
|
|
|
|
program <- readProgramFile
|
|
|
|
let a = Transferrer.doTransfer dstatus t info program
|
|
|
|
return $ Just (t, info, a)
|
2012-08-12 16:11:20 +00:00
|
|
|
|
|
|
|
getCurrentTransfers :: Handler TransferMap
|
|
|
|
getCurrentTransfers = currentTransfers
|
|
|
|
<$> (liftIO . getDaemonStatus =<< daemonStatus <$> getYesod)
|