mirror of
https://github.com/commercialhaskell/stackage-server.git
synced 2026-01-12 04:08:29 +01:00
Better URLs #37
URLs now look like /snapshot/2014-11-23-7.8hp-exc and similar.
This commit is contained in:
parent
e588f9e45c
commit
a8911dbb3b
@ -13,6 +13,7 @@ import Control.Monad.Logger (runLoggingT, LoggingT, defaultLogStr)
|
|||||||
import Data.BlobStore (fileStore, storeWrite, cachedS3Store)
|
import Data.BlobStore (fileStore, storeWrite, cachedS3Store)
|
||||||
import Data.Hackage
|
import Data.Hackage
|
||||||
import Data.Hackage.Views
|
import Data.Hackage.Views
|
||||||
|
import Data.Slug (SnapSlug (..), safeMakeSlug, HasGenIO)
|
||||||
import Data.Time (diffUTCTime)
|
import Data.Time (diffUTCTime)
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
import qualified Database.Persist
|
import qualified Database.Persist
|
||||||
@ -36,6 +37,7 @@ import System.Environment (getEnvironment)
|
|||||||
import Data.BlobStore (HasBlobStore (..), BlobStore)
|
import Data.BlobStore (HasBlobStore (..), BlobStore)
|
||||||
import System.IO (hSetBuffering, BufferMode (LineBuffering))
|
import System.IO (hSetBuffering, BufferMode (LineBuffering))
|
||||||
import qualified Data.ByteString as S
|
import qualified Data.ByteString as S
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
import qualified Echo
|
import qualified Echo
|
||||||
|
|
||||||
@ -180,9 +182,12 @@ makeFoundation useEcho conf = do
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- Perform database migration using our application's logging settings.
|
-- Perform database migration using our application's logging settings.
|
||||||
runLoggingT
|
runResourceT $
|
||||||
(Database.Persist.runPool dbconf (runMigration migrateAll) p)
|
flip runReaderT gen $
|
||||||
(messageLoggerSource foundation logger)
|
flip runLoggingT (messageLoggerSource foundation logger) $
|
||||||
|
flip (Database.Persist.runPool dbconf) p $ do
|
||||||
|
runMigration migrateAll
|
||||||
|
checkMigration 1 $ fixSnapSlugs
|
||||||
|
|
||||||
env <- getEnvironment
|
env <- getEnvironment
|
||||||
let updateDB = lookup "STACKAGE_CABAL_LOADER" env /= Just "0"
|
let updateDB = lookup "STACKAGE_CABAL_LOADER" env /= Just "0"
|
||||||
@ -315,3 +320,33 @@ getApplicationDev useEcho =
|
|||||||
loader = Yesod.Default.Config.loadConfig (configSettings Development)
|
loader = Yesod.Default.Config.loadConfig (configSettings Development)
|
||||||
{ csParseExtra = parseExtra
|
{ csParseExtra = parseExtra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkMigration :: MonadIO m
|
||||||
|
=> Int
|
||||||
|
-> ReaderT SqlBackend m ()
|
||||||
|
-> ReaderT SqlBackend m ()
|
||||||
|
checkMigration num f = do
|
||||||
|
eres <- insertBy $ Migration num
|
||||||
|
case eres of
|
||||||
|
Left _ -> return ()
|
||||||
|
Right _ -> f
|
||||||
|
|
||||||
|
fixSnapSlugs :: (MonadResource m, HasGenIO env, MonadReader env m)
|
||||||
|
=> ReaderT SqlBackend m ()
|
||||||
|
fixSnapSlugs =
|
||||||
|
selectSource [] [Asc StackageUploaded] $$ mapM_C go
|
||||||
|
where
|
||||||
|
go (Entity sid Stackage {..}) =
|
||||||
|
loop (1 :: Int)
|
||||||
|
where
|
||||||
|
base = T.replace "haskell platform" "hp"
|
||||||
|
$ T.replace "stackage build for " ""
|
||||||
|
$ toLower stackageTitle
|
||||||
|
loop 50 = error "fixSnapSlugs can't find a good slug"
|
||||||
|
loop i = do
|
||||||
|
slug' <- lift $ safeMakeSlug base $ if i == 1 then False else True
|
||||||
|
let slug = SnapSlug slug'
|
||||||
|
ms <- getBy $ UniqueSnapshot slug
|
||||||
|
case ms of
|
||||||
|
Nothing -> update sid [StackageSlug =. slug]
|
||||||
|
Just _ -> loop (i + 1)
|
||||||
|
|||||||
@ -8,6 +8,7 @@ module Data.Slug
|
|||||||
, HasGenIO (..)
|
, HasGenIO (..)
|
||||||
, randomSlug
|
, randomSlug
|
||||||
, slugField
|
, slugField
|
||||||
|
, SnapSlug (..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import ClassyPrelude.Yesod
|
import ClassyPrelude.Yesod
|
||||||
@ -96,3 +97,9 @@ slugField =
|
|||||||
checkMMap go unSlug textField
|
checkMMap go unSlug textField
|
||||||
where
|
where
|
||||||
go = return . either (Left . tshow) Right . mkSlug
|
go = return . either (Left . tshow) Right . mkSlug
|
||||||
|
|
||||||
|
-- | Unique identifier for a snapshot.
|
||||||
|
newtype SnapSlug = SnapSlug { unSnapSlug :: Slug }
|
||||||
|
deriving (Show, Read, Eq, Typeable, PersistField, ToMarkup, PathPiece)
|
||||||
|
instance PersistFieldSql SnapSlug where
|
||||||
|
sqlType = sqlType . liftM unSnapSlug
|
||||||
|
|||||||
@ -2,7 +2,7 @@ module Foundation where
|
|||||||
|
|
||||||
import ClassyPrelude.Yesod
|
import ClassyPrelude.Yesod
|
||||||
import Data.BlobStore
|
import Data.BlobStore
|
||||||
import Data.Slug (safeMakeSlug, HasGenIO (getGenIO), randomSlug, Slug)
|
import Data.Slug (safeMakeSlug, HasGenIO (getGenIO), randomSlug, Slug, SnapSlug)
|
||||||
import qualified Database.Persist
|
import qualified Database.Persist
|
||||||
import Model
|
import Model
|
||||||
import qualified Settings
|
import qualified Settings
|
||||||
|
|||||||
@ -16,16 +16,24 @@ import qualified Data.ByteString.Base16 as B16
|
|||||||
import Data.Byteable (toBytes)
|
import Data.Byteable (toBytes)
|
||||||
import Crypto.Hash (Digest, SHA1)
|
import Crypto.Hash (Digest, SHA1)
|
||||||
import qualified Filesystem.Path.CurrentOS as F
|
import qualified Filesystem.Path.CurrentOS as F
|
||||||
|
import Data.Slug (SnapSlug)
|
||||||
|
|
||||||
form :: Form FileInfo
|
form :: Form FileInfo
|
||||||
form = renderDivs $ areq fileField "tarball containing docs"
|
form = renderDivs $ areq fileField "tarball containing docs"
|
||||||
{ fsName = Just "tarball"
|
{ fsName = Just "tarball"
|
||||||
} Nothing
|
} Nothing
|
||||||
|
|
||||||
getUploadHaddockR, putUploadHaddockR :: PackageSetIdent -> Handler Html
|
getUploadHaddockR, putUploadHaddockR :: SnapSlug -> Handler Html
|
||||||
getUploadHaddockR ident = do
|
getUploadHaddockR slug0 = do
|
||||||
uid <- requireAuthIdOrToken
|
uid <- requireAuthIdOrToken
|
||||||
Entity sid Stackage {..} <- runDB $ getBy404 $ UniqueStackage ident
|
Entity sid Stackage {..} <- runDB $ do
|
||||||
|
-- Provide fallback for old URLs
|
||||||
|
ment <- getBy $ UniqueSnapshot slug0
|
||||||
|
case ment of
|
||||||
|
Just ent -> return ent
|
||||||
|
Nothing -> getBy404 $ UniqueStackage $ PackageSetIdent $ toPathPiece slug0
|
||||||
|
let ident = stackageIdent
|
||||||
|
slug = stackageSlug
|
||||||
unless (uid == stackageUser) $ permissionDenied "You do not control this snapshot"
|
unless (uid == stackageUser) $ permissionDenied "You do not control this snapshot"
|
||||||
((res, widget), enctype) <- runFormPostNoToken form
|
((res, widget), enctype) <- runFormPostNoToken form
|
||||||
case res of
|
case res of
|
||||||
@ -35,16 +43,25 @@ getUploadHaddockR ident = do
|
|||||||
master <- getYesod
|
master <- getYesod
|
||||||
void $ liftIO $ forkIO $ haddockUnpacker master True ident
|
void $ liftIO $ forkIO $ haddockUnpacker master True ident
|
||||||
setMessage "Haddocks uploaded"
|
setMessage "Haddocks uploaded"
|
||||||
redirect $ StackageHomeR ident
|
redirect $ StackageHomeR slug
|
||||||
_ -> defaultLayout $ do
|
_ -> defaultLayout $ do
|
||||||
setTitle "Upload Haddocks"
|
setTitle "Upload Haddocks"
|
||||||
$(widgetFile "upload-haddock")
|
$(widgetFile "upload-haddock")
|
||||||
|
|
||||||
putUploadHaddockR = getUploadHaddockR
|
putUploadHaddockR = getUploadHaddockR
|
||||||
|
|
||||||
getHaddockR :: PackageSetIdent -> [Text] -> Handler ()
|
getHaddockR :: SnapSlug -> [Text] -> Handler ()
|
||||||
getHaddockR ident rest = do
|
getHaddockR slug rest = do
|
||||||
sanitize $ toPathPiece ident
|
ident <- runDB $ do
|
||||||
|
ment <- getBy $ UniqueSnapshot slug
|
||||||
|
case ment of
|
||||||
|
Just ent -> return $ stackageIdent $ entityVal ent
|
||||||
|
Nothing -> do
|
||||||
|
Entity _ stackage <- getBy404
|
||||||
|
$ UniqueStackage
|
||||||
|
$ PackageSetIdent
|
||||||
|
$ toPathPiece slug
|
||||||
|
redirectWith status301 $ HaddockR (stackageSlug stackage) rest
|
||||||
mapM_ sanitize rest
|
mapM_ sanitize rest
|
||||||
dirs <- getDirs -- (gzdir, rawdir) <- getHaddockDir ident
|
dirs <- getDirs -- (gzdir, rawdir) <- getHaddockDir ident
|
||||||
master <- getYesod
|
master <- getYesod
|
||||||
@ -55,9 +72,9 @@ getHaddockR ident rest = do
|
|||||||
mime = defaultMimeLookup $ fpToText $ filename rawfp
|
mime = defaultMimeLookup $ fpToText $ filename rawfp
|
||||||
|
|
||||||
whenM (liftIO $ isDirectory rawfp)
|
whenM (liftIO $ isDirectory rawfp)
|
||||||
$ redirect $ HaddockR ident $ rest ++ ["index.html"]
|
$ redirect $ HaddockR slug $ rest ++ ["index.html"]
|
||||||
whenM (liftIO $ isDirectory gzfp)
|
whenM (liftIO $ isDirectory gzfp)
|
||||||
$ redirect $ HaddockR ident $ rest ++ ["index.html"]
|
$ redirect $ HaddockR slug $ rest ++ ["index.html"]
|
||||||
|
|
||||||
whenM (liftIO $ isFile gzfp) $ do
|
whenM (liftIO $ isFile gzfp) $ do
|
||||||
addHeader "Content-Encoding" "gzip"
|
addHeader "Content-Encoding" "gzip"
|
||||||
|
|||||||
@ -27,12 +27,13 @@ getHomeR = do
|
|||||||
linkFor name =
|
linkFor name =
|
||||||
do slug <- mkSlug name
|
do slug <- mkSlug name
|
||||||
fpcomplete <- mkSlug "fpcomplete"
|
fpcomplete <- mkSlug "fpcomplete"
|
||||||
selecting (\(alias, user) ->
|
selecting (\(alias, user, stackage) ->
|
||||||
do where_ $
|
do where_ $
|
||||||
alias ^. AliasName ==. val slug &&.
|
alias ^. AliasName ==. val slug &&.
|
||||||
alias ^. AliasUser ==. user ^. UserId &&.
|
alias ^. AliasUser ==. user ^. UserId &&.
|
||||||
user ^. UserHandle ==. val fpcomplete
|
user ^. UserHandle ==. val fpcomplete &&.
|
||||||
return (alias ^. AliasTarget))
|
alias ^. AliasTarget ==. stackage ^. StackageIdent
|
||||||
|
return (stackage ^. StackageSlug))
|
||||||
where selecting =
|
where selecting =
|
||||||
fmap (fmap unValue . listToMaybe) .
|
fmap (fmap unValue . listToMaybe) .
|
||||||
runDB .
|
runDB .
|
||||||
|
|||||||
@ -31,7 +31,7 @@ getPackageR pn = do
|
|||||||
E.orderBy [E.desc $ s ^. StackageUploaded]
|
E.orderBy [E.desc $ s ^. StackageUploaded]
|
||||||
E.limit maxSnaps
|
E.limit maxSnaps
|
||||||
--selectList [PackageName' ==. pn] [LimitTo 10, Desc PackageStackage]
|
--selectList [PackageName' ==. pn] [LimitTo 10, Desc PackageStackage]
|
||||||
return (p ^. PackageVersion, s ^. StackageTitle, s ^. StackageIdent, s ^. StackageHasHaddocks)
|
return (p ^. PackageVersion, s ^. StackageTitle, s ^. StackageSlug, s ^. StackageHasHaddocks)
|
||||||
nLikes <- count [LikePackage ==. pn]
|
nLikes <- count [LikePackage ==. pn]
|
||||||
let getLiked uid = (>0) <$> count [LikePackage ==. pn, LikeVoter ==. uid]
|
let getLiked uid = (>0) <$> count [LikePackage ==. pn, LikeVoter ==. uid]
|
||||||
liked <- maybe (return False) getLiked muid
|
liked <- maybe (return False) getLiked muid
|
||||||
|
|||||||
@ -23,7 +23,7 @@ getAllSnapshotsR = do
|
|||||||
E.on (stackage E.^. StackageUser E.==. user E.^. UserId)
|
E.on (stackage E.^. StackageUser E.==. user E.^. UserId)
|
||||||
E.orderBy [E.desc $ stackage E.^. StackageUploaded]
|
E.orderBy [E.desc $ stackage E.^. StackageUploaded]
|
||||||
return
|
return
|
||||||
( stackage E.^. StackageIdent
|
( stackage E.^. StackageSlug
|
||||||
, stackage E.^. StackageTitle
|
, stackage E.^. StackageTitle
|
||||||
, stackage E.^. StackageUploaded
|
, stackage E.^. StackageUploaded
|
||||||
, user E.^. UserDisplay
|
, user E.^. UserDisplay
|
||||||
|
|||||||
@ -3,16 +3,17 @@ module Handler.StackageHome where
|
|||||||
import Data.BlobStore (storeExists)
|
import Data.BlobStore (storeExists)
|
||||||
import Import
|
import Import
|
||||||
import Data.Time (FormatTime)
|
import Data.Time (FormatTime)
|
||||||
|
import Data.Slug (SnapSlug)
|
||||||
|
|
||||||
getStackageHomeR :: PackageSetIdent -> Handler Html
|
getStackageHomeR :: SnapSlug -> Handler Html
|
||||||
getStackageHomeR ident = do
|
getStackageHomeR slug = do
|
||||||
muid <- maybeAuthId
|
muid <- maybeAuthId
|
||||||
stackage <- runDB $ do
|
stackage <- runDB $ do
|
||||||
Entity _ stackage <- getBy404 $ UniqueStackage ident
|
Entity _ stackage <- getBy404 $ UniqueSnapshot slug
|
||||||
return stackage
|
return stackage
|
||||||
let isOwner = muid == Just (stackageUser stackage)
|
let isOwner = muid == Just (stackageUser stackage)
|
||||||
|
|
||||||
hasBundle <- storeExists $ SnapshotBundle ident
|
hasBundle <- storeExists $ SnapshotBundle $ stackageIdent stackage
|
||||||
let minclusive =
|
let minclusive =
|
||||||
if "inclusive" `isSuffixOf` stackageTitle stackage
|
if "inclusive" `isSuffixOf` stackageTitle stackage
|
||||||
then Just True
|
then Just True
|
||||||
@ -24,9 +25,9 @@ getStackageHomeR ident = do
|
|||||||
setTitle $ toHtml $ stackageTitle stackage
|
setTitle $ toHtml $ stackageTitle stackage
|
||||||
$(widgetFile "stackage-home")
|
$(widgetFile "stackage-home")
|
||||||
|
|
||||||
getStackageMetadataR :: PackageSetIdent -> Handler TypedContent
|
getStackageMetadataR :: SnapSlug -> Handler TypedContent
|
||||||
getStackageMetadataR ident = do
|
getStackageMetadataR slug = do
|
||||||
Entity sid _ <- runDB $ getBy404 $ UniqueStackage ident
|
Entity sid _ <- runDB $ getBy404 $ UniqueSnapshot slug
|
||||||
respondSourceDB typePlain $ do
|
respondSourceDB typePlain $ do
|
||||||
sendChunkBS "Override packages\n"
|
sendChunkBS "Override packages\n"
|
||||||
sendChunkBS "=================\n"
|
sendChunkBS "=================\n"
|
||||||
@ -51,9 +52,9 @@ getStackageMetadataR ident = do
|
|||||||
, "\n"
|
, "\n"
|
||||||
]
|
]
|
||||||
|
|
||||||
getStackageCabalConfigR :: PackageSetIdent -> Handler TypedContent
|
getStackageCabalConfigR :: SnapSlug -> Handler TypedContent
|
||||||
getStackageCabalConfigR ident = do
|
getStackageCabalConfigR slug = do
|
||||||
Entity sid _ <- runDB $ getBy404 $ UniqueStackage ident
|
Entity sid _ <- runDB $ getBy404 $ UniqueSnapshot slug
|
||||||
respondSourceDB typePlain $ stream sid
|
respondSourceDB typePlain $ stream sid
|
||||||
where
|
where
|
||||||
stream sid =
|
stream sid =
|
||||||
@ -81,3 +82,10 @@ getStackageCabalConfigR ident = do
|
|||||||
|
|
||||||
yearMonthDay :: FormatTime t => t -> String
|
yearMonthDay :: FormatTime t => t -> String
|
||||||
yearMonthDay = formatTime defaultTimeLocale "%Y-%m-%d"
|
yearMonthDay = formatTime defaultTimeLocale "%Y-%m-%d"
|
||||||
|
|
||||||
|
getOldStackageR :: PackageSetIdent -> [Text] -> Handler ()
|
||||||
|
getOldStackageR ident pieces = do
|
||||||
|
Entity _ stackage <- runDB $ getBy404 $ UniqueStackage ident
|
||||||
|
case parseRoute ("snapshot" : toPathPiece (stackageSlug stackage) : pieces, []) of
|
||||||
|
Nothing -> notFound
|
||||||
|
Just route -> redirect (route :: Route App)
|
||||||
|
|||||||
@ -2,9 +2,12 @@ module Handler.StackageIndex where
|
|||||||
|
|
||||||
import Import
|
import Import
|
||||||
import Data.BlobStore
|
import Data.BlobStore
|
||||||
|
import Data.Slug (SnapSlug)
|
||||||
|
|
||||||
getStackageIndexR :: PackageSetIdent -> Handler TypedContent
|
getStackageIndexR :: SnapSlug -> Handler TypedContent
|
||||||
getStackageIndexR ident = do
|
getStackageIndexR slug = do
|
||||||
|
Entity _ stackage <- runDB $ getBy404 $ UniqueSnapshot slug
|
||||||
|
let ident = stackageIdent stackage
|
||||||
msrc <- storeRead $ CabalIndex ident
|
msrc <- storeRead $ CabalIndex ident
|
||||||
case msrc of
|
case msrc of
|
||||||
Nothing -> notFound
|
Nothing -> notFound
|
||||||
@ -14,8 +17,10 @@ getStackageIndexR ident = do
|
|||||||
neverExpires
|
neverExpires
|
||||||
respondSource "application/x-gzip" $ mapOutput (Chunk . toBuilder) src
|
respondSource "application/x-gzip" $ mapOutput (Chunk . toBuilder) src
|
||||||
|
|
||||||
getStackageBundleR :: PackageSetIdent -> Handler TypedContent
|
getStackageBundleR :: SnapSlug -> Handler TypedContent
|
||||||
getStackageBundleR ident = do
|
getStackageBundleR slug = do
|
||||||
|
Entity _ stackage <- runDB $ getBy404 $ UniqueSnapshot slug
|
||||||
|
let ident = stackageIdent stackage
|
||||||
msrc <- storeRead $ SnapshotBundle ident
|
msrc <- storeRead $ SnapshotBundle ident
|
||||||
case msrc of
|
case msrc of
|
||||||
Nothing -> notFound
|
Nothing -> notFound
|
||||||
|
|||||||
@ -3,9 +3,12 @@ module Handler.StackageSdist where
|
|||||||
import Import
|
import Import
|
||||||
import Data.BlobStore
|
import Data.BlobStore
|
||||||
import Data.Hackage
|
import Data.Hackage
|
||||||
|
import Data.Slug (SnapSlug)
|
||||||
|
|
||||||
getStackageSdistR :: PackageSetIdent -> PackageNameVersion -> Handler TypedContent
|
getStackageSdistR :: SnapSlug -> PackageNameVersion -> Handler TypedContent
|
||||||
getStackageSdistR ident (PackageNameVersion name version) = do
|
getStackageSdistR slug (PackageNameVersion name version) = do
|
||||||
|
Entity _ stackage <- runDB $ getBy404 $ UniqueSnapshot slug
|
||||||
|
let ident = stackageIdent stackage
|
||||||
addDownload (Just ident) Nothing name version
|
addDownload (Just ident) Nothing name version
|
||||||
msrc1 <- storeRead (CustomSdist ident name version)
|
msrc1 <- storeRead (CustomSdist ident name version)
|
||||||
msrc <-
|
msrc <-
|
||||||
|
|||||||
@ -18,11 +18,14 @@ import Control.Monad.Trans.Resource (allocate)
|
|||||||
import System.Directory (removeFile, getTemporaryDirectory)
|
import System.Directory (removeFile, getTemporaryDirectory)
|
||||||
import System.Process (runProcess, waitForProcess)
|
import System.Process (runProcess, waitForProcess)
|
||||||
import System.Exit (ExitCode (ExitSuccess))
|
import System.Exit (ExitCode (ExitSuccess))
|
||||||
import Data.Slug (mkSlug)
|
import Data.Slug (mkSlug, SnapSlug (..), safeMakeSlug)
|
||||||
|
|
||||||
fileKey :: Text
|
fileKey :: Text
|
||||||
fileKey = "stackage"
|
fileKey = "stackage"
|
||||||
|
|
||||||
|
slugKey :: Text
|
||||||
|
slugKey = "slug"
|
||||||
|
|
||||||
getUploadStackageR :: Handler Html
|
getUploadStackageR :: Handler Html
|
||||||
getUploadStackageR = do
|
getUploadStackageR = do
|
||||||
_ <- requireAuth
|
_ <- requireAuth
|
||||||
@ -34,6 +37,7 @@ putUploadStackageR :: Handler TypedContent
|
|||||||
putUploadStackageR = do
|
putUploadStackageR = do
|
||||||
uid <- requireAuthIdOrToken
|
uid <- requireAuthIdOrToken
|
||||||
mfile <- lookupFile fileKey
|
mfile <- lookupFile fileKey
|
||||||
|
mslug0 <- lookupPostParam slugKey
|
||||||
case mfile of
|
case mfile of
|
||||||
Nothing -> invalidArgs ["Upload missing"]
|
Nothing -> invalidArgs ["Upload missing"]
|
||||||
Just file -> do
|
Just file -> do
|
||||||
@ -75,6 +79,7 @@ putUploadStackageR = do
|
|||||||
|
|
||||||
forkHandler onExc $ do
|
forkHandler onExc $ do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
|
baseSlug <- fmap SnapSlug $ mkSlug $ fromMaybe (tshow $ utctDay now) mslug0
|
||||||
let initial = Stackage
|
let initial = Stackage
|
||||||
{ stackageUser = uid
|
{ stackageUser = uid
|
||||||
, stackageIdent = ident
|
, stackageIdent = ident
|
||||||
@ -82,6 +87,7 @@ putUploadStackageR = do
|
|||||||
, stackageTitle = "Untitled Stackage"
|
, stackageTitle = "Untitled Stackage"
|
||||||
, stackageDesc = "No description provided"
|
, stackageDesc = "No description provided"
|
||||||
, stackageHasHaddocks = False
|
, stackageHasHaddocks = False
|
||||||
|
, stackageSlug = baseSlug
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Evil lazy I/O thanks to tar package
|
-- Evil lazy I/O thanks to tar package
|
||||||
@ -106,25 +112,27 @@ putUploadStackageR = do
|
|||||||
then do
|
then do
|
||||||
sourceFile (fpFromString fp') $$ storeWrite (CabalIndex ident)
|
sourceFile (fpFromString fp') $$ storeWrite (CabalIndex ident)
|
||||||
sourceFile (fpFromString fp) $$ gzip =$ storeWrite (SnapshotBundle ident)
|
sourceFile (fpFromString fp) $$ gzip =$ storeWrite (SnapshotBundle ident)
|
||||||
runDB $ do
|
slug <- runDB $ do
|
||||||
sid <- insert stackage
|
slug <- getUniqueSlug $ stackageSlug stackage
|
||||||
|
sid <- insert stackage { stackageSlug = slug}
|
||||||
forM_ contents $ \(name, version, overwrite) -> insert_ Package
|
forM_ contents $ \(name, version, overwrite) -> insert_ Package
|
||||||
{ packageStackage = sid
|
{ packageStackage = sid
|
||||||
, packageName' = name
|
, packageName' = name
|
||||||
, packageVersion = version
|
, packageVersion = version
|
||||||
, packageOverwrite = overwrite
|
, packageOverwrite = overwrite
|
||||||
}
|
}
|
||||||
|
return slug
|
||||||
|
|
||||||
setAlias
|
setAlias
|
||||||
|
|
||||||
done "Stackage created" $ StackageHomeR ident
|
done "Stackage created" $ StackageHomeR slug
|
||||||
else do
|
else do
|
||||||
done "Error creating index file" ProfileR
|
done "Error creating index file" ProfileR
|
||||||
|
|
||||||
addHeader "X-Stackage-Ident" $ toPathPiece ident
|
addHeader "X-Stackage-Ident" $ toPathPiece ident
|
||||||
redirect $ ProgressR key
|
redirect $ ProgressR key
|
||||||
where
|
where
|
||||||
loop _ Tar.Done = return ()
|
loop update Tar.Done = update "Finished processing files"
|
||||||
loop _ (Tar.Fail e) = throwM e
|
loop _ (Tar.Fail e) = throwM e
|
||||||
loop update (Tar.Next entry entries) = do
|
loop update (Tar.Next entry entries) = do
|
||||||
addEntry update entry
|
addEntry update entry
|
||||||
@ -147,6 +155,10 @@ putUploadStackageR = do
|
|||||||
, stackageDesc = desc
|
, stackageDesc = desc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"slug" -> do
|
||||||
|
slug <- safeMakeSlug (decodeUtf8 $ toStrict lbs) False
|
||||||
|
ls <- get
|
||||||
|
put ls { lsStackage = (lsStackage ls) { stackageSlug = SnapSlug slug } }
|
||||||
"hackage" -> forM_ (lines $ decodeUtf8 $ toStrict lbs) $ \line ->
|
"hackage" -> forM_ (lines $ decodeUtf8 $ toStrict lbs) $ \line ->
|
||||||
case parseName line of
|
case parseName line of
|
||||||
Just (name, version) -> do
|
Just (name, version) -> do
|
||||||
@ -245,3 +257,31 @@ extractCabal lbs name version =
|
|||||||
, toPathPiece name
|
, toPathPiece name
|
||||||
, ".cabal"
|
, ".cabal"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- | Get a unique version of the given slug by appending random numbers to the
|
||||||
|
-- end.
|
||||||
|
getUniqueSlug :: MonadIO m => SnapSlug -> ReaderT SqlBackend m SnapSlug
|
||||||
|
getUniqueSlug base =
|
||||||
|
loop Nothing
|
||||||
|
where
|
||||||
|
loop msuffix = do
|
||||||
|
slug <- checkSlug $ addSuffix msuffix
|
||||||
|
ment <- getBy $ UniqueSnapshot slug
|
||||||
|
case ment of
|
||||||
|
Nothing -> return slug
|
||||||
|
Just _ ->
|
||||||
|
case msuffix of
|
||||||
|
Nothing -> loop $ Just (1 :: Int)
|
||||||
|
Just i
|
||||||
|
| i > 50 -> error "No unique slug found"
|
||||||
|
| otherwise -> loop $ Just $ i + 1
|
||||||
|
|
||||||
|
txt = toPathPiece base
|
||||||
|
|
||||||
|
addSuffix Nothing = txt
|
||||||
|
addSuffix (Just i) = txt ++ pack ('-' : show i)
|
||||||
|
|
||||||
|
checkSlug slug =
|
||||||
|
case fromPathPiece slug of
|
||||||
|
Nothing -> error $ "Invalid snapshot slug: " ++ unpack slug
|
||||||
|
Just s -> return s
|
||||||
|
|||||||
2
Model.hs
2
Model.hs
@ -2,7 +2,7 @@ module Model where
|
|||||||
|
|
||||||
import ClassyPrelude.Yesod
|
import ClassyPrelude.Yesod
|
||||||
import Database.Persist.Quasi
|
import Database.Persist.Quasi
|
||||||
import Data.Slug (Slug)
|
import Data.Slug (Slug, SnapSlug)
|
||||||
import Types
|
import Types
|
||||||
|
|
||||||
-- You can define all of your database entities in the entities file.
|
-- You can define all of your database entities in the entities file.
|
||||||
|
|||||||
@ -18,11 +18,13 @@ Verkey
|
|||||||
Stackage
|
Stackage
|
||||||
user UserId
|
user UserId
|
||||||
ident PackageSetIdent
|
ident PackageSetIdent
|
||||||
|
slug SnapSlug default="md5((random())::text)"
|
||||||
uploaded UTCTime
|
uploaded UTCTime
|
||||||
title Text
|
title Text
|
||||||
desc Text
|
desc Text
|
||||||
hasHaddocks Bool default=false
|
hasHaddocks Bool default=false
|
||||||
UniqueStackage ident
|
UniqueStackage ident
|
||||||
|
UniqueSnapshot slug
|
||||||
|
|
||||||
Uploaded
|
Uploaded
|
||||||
name PackageName
|
name PackageName
|
||||||
@ -88,3 +90,7 @@ Metadata
|
|||||||
BannedTag
|
BannedTag
|
||||||
tag Slug
|
tag Slug
|
||||||
UniqueBannedTag tag
|
UniqueBannedTag tag
|
||||||
|
|
||||||
|
Migration
|
||||||
|
num Int
|
||||||
|
UniqueMigration num
|
||||||
|
|||||||
@ -10,20 +10,24 @@
|
|||||||
/email/#EmailId EmailR DELETE
|
/email/#EmailId EmailR DELETE
|
||||||
/reset-token ResetTokenR POST
|
/reset-token ResetTokenR POST
|
||||||
/upload UploadStackageR GET PUT
|
/upload UploadStackageR GET PUT
|
||||||
/upload-haddock/#PackageSetIdent UploadHaddockR GET PUT
|
/upload-haddock/#SnapSlug UploadHaddockR GET PUT
|
||||||
/stackage/#PackageSetIdent StackageHomeR GET
|
|
||||||
/stackage/#PackageSetIdent/metadata StackageMetadataR GET
|
/stackage/#PackageSetIdent/*Texts OldStackageR GET
|
||||||
/stackage/#PackageSetIdent/cabal.config StackageCabalConfigR GET
|
|
||||||
/stackage/#PackageSetIdent/00-index.tar.gz StackageIndexR GET
|
/snapshot/#SnapSlug StackageHomeR GET
|
||||||
/stackage/#PackageSetIdent/bundle StackageBundleR GET
|
/snapshot/#SnapSlug/metadata StackageMetadataR GET
|
||||||
/stackage/#PackageSetIdent/package/#PackageNameVersion StackageSdistR GET
|
/snapshot/#SnapSlug/cabal.config StackageCabalConfigR GET
|
||||||
|
/snapshot/#SnapSlug/00-index.tar.gz StackageIndexR GET
|
||||||
|
/snapshot/#SnapSlug/bundle StackageBundleR GET
|
||||||
|
/snapshot/#SnapSlug/package/#PackageNameVersion StackageSdistR GET
|
||||||
|
|
||||||
/hackage-view/#HackageView/00-index.tar.gz HackageViewIndexR GET
|
/hackage-view/#HackageView/00-index.tar.gz HackageViewIndexR GET
|
||||||
/hackage-view/#HackageView/package/#PackageNameVersion HackageViewSdistR GET
|
/hackage-view/#HackageView/package/#PackageNameVersion HackageViewSdistR GET
|
||||||
/aliases AliasesR PUT
|
/aliases AliasesR PUT
|
||||||
/alias/#Slug/#Slug/*Texts AliasR
|
/alias/#Slug/#Slug/*Texts AliasR
|
||||||
/progress/#Int ProgressR GET
|
/progress/#Int ProgressR GET
|
||||||
/system SystemR GET
|
/system SystemR GET
|
||||||
/haddock/#PackageSetIdent/*Texts HaddockR GET
|
/haddock/#SnapSlug/*Texts HaddockR GET
|
||||||
/package/#PackageName PackageR GET
|
/package/#PackageName PackageR GET
|
||||||
/package PackageListR GET
|
/package PackageListR GET
|
||||||
/compressor-status CompressorStatusR GET
|
/compressor-status CompressorStatusR GET
|
||||||
|
|||||||
@ -111,16 +111,16 @@ $newline never
|
|||||||
Package
|
Package
|
||||||
<th>
|
<th>
|
||||||
Snapshot
|
Snapshot
|
||||||
$forall (version, title, ident, hasHaddocks) <- packages
|
$forall (version, title, slug, hasHaddocks) <- packages
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
$if hasHaddocks
|
$if hasHaddocks
|
||||||
<a href=@{haddocksLink ident version}>
|
<a href=@{haddocksLink slug version}>
|
||||||
Docs
|
Docs
|
||||||
<td>
|
<td>
|
||||||
#{version}
|
#{version}
|
||||||
<td>
|
<td>
|
||||||
<a href=@{StackageHomeR ident}>#{fromMaybe title $ stripSuffix ", exclusive" title}
|
<a href=@{StackageHomeR slug}>#{fromMaybe title $ stripSuffix ", exclusive" title}
|
||||||
|
|
||||||
<div .markdown-container .readme-container>
|
<div .markdown-container .readme-container>
|
||||||
<div .container>
|
<div .container>
|
||||||
|
|||||||
@ -7,28 +7,28 @@ $newline never
|
|||||||
$if hasBundle
|
$if hasBundle
|
||||||
<span .separator>
|
<span .separator>
|
||||||
<span>
|
<span>
|
||||||
<a href=@{StackageMetadataR ident} title="View metadata on this snapshot, such as package versions">
|
<a href=@{StackageMetadataR slug} title="View metadata on this snapshot, such as package versions">
|
||||||
\Metadata
|
\Metadata
|
||||||
<span .separator>
|
<span .separator>
|
||||||
<span>
|
<span>
|
||||||
<a href=@{StackageBundleR ident} title="This is useful for making modifications to an existing snapshot">
|
<a href=@{StackageBundleR slug} title="This is useful for making modifications to an existing snapshot">
|
||||||
\Bundle
|
\Bundle
|
||||||
<span .separator>
|
<span .separator>
|
||||||
<span>
|
<span>
|
||||||
<a href=@{StackageCabalConfigR ident} title="If you want to stick with upstream Hackage but get a stable package set">
|
<a href=@{StackageCabalConfigR slug} title="If you want to stick with upstream Hackage but get a stable package set">
|
||||||
\cabal.config
|
\cabal.config
|
||||||
$if stackageHasHaddocks stackage
|
$if stackageHasHaddocks stackage
|
||||||
<span .separator>
|
<span .separator>
|
||||||
<span>
|
<span>
|
||||||
<a href=@{HaddockR ident []}>Haddocks
|
<a href=@{HaddockR slug []}>Haddocks
|
||||||
$if isOwner
|
$if isOwner
|
||||||
<p>
|
<p>
|
||||||
You are the owner of this snapshot. You can #
|
You are the owner of this snapshot. You can #
|
||||||
<a href=@{UploadHaddockR ident}>upload haddocks#
|
<a href=@{UploadHaddockR slug}>upload haddocks#
|
||||||
.
|
.
|
||||||
<p>
|
<p>
|
||||||
<pre>
|
<pre>
|
||||||
remote-repo: stackage-#{ident}:@{StackageHomeR ident}
|
remote-repo: stackage-#{slug}:@{StackageHomeR slug}
|
||||||
$maybe _ <- minclusive
|
$maybe _ <- minclusive
|
||||||
<p>
|
<p>
|
||||||
<a href="https://github.com/fpco/stackage/wiki/Stackage-Server-FAQ#whats-the-difference-between-inclusive-and-exclusive-snapshots">What's the difference between inclusive and exclusive snapshots?</a>
|
<a href="https://github.com/fpco/stackage/wiki/Stackage-Server-FAQ#whats-the-difference-between-inclusive-and-exclusive-snapshots">What's the difference between inclusive and exclusive snapshots?</a>
|
||||||
|
|||||||
@ -2,12 +2,12 @@
|
|||||||
<h1>Upload Haddocks
|
<h1>Upload Haddocks
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href=@{StackageHomeR ident}>Return to snapshot
|
<a href=@{StackageHomeR slug}>Return to snapshot
|
||||||
|
|
||||||
$if stackageHasHaddocks
|
$if stackageHasHaddocks
|
||||||
<div .alert .alert-warning>You have already uploaded Haddocks. Uploading against will delete the old contents.
|
<div .alert .alert-warning>You have already uploaded Haddocks. Uploading against will delete the old contents.
|
||||||
|
|
||||||
<form method=POST action=@{UploadHaddockR ident}?_method=PUT enctype=#{enctype}>
|
<form method=POST action=@{UploadHaddockR slug}?_method=PUT enctype=#{enctype}>
|
||||||
^{widget}
|
^{widget}
|
||||||
<div>
|
<div>
|
||||||
<button .btn>Upload
|
<button .btn>Upload
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user