Merge pull request #121 from fpco/no-database

Remove all social features
This commit is contained in:
Chris Done 2015-09-28 11:42:04 -07:00
commit 45741016dc
27 changed files with 9 additions and 813 deletions

View File

@ -36,9 +36,6 @@ import qualified Echo
-- Don't forget to add new modules to your cabal file! -- Don't forget to add new modules to your cabal file!
import Handler.Home import Handler.Home
import Handler.Snapshots import Handler.Snapshots
import Handler.Profile
import Handler.Email
import Handler.ResetToken
import Handler.StackageHome import Handler.StackageHome
import Handler.StackageIndex import Handler.StackageIndex
import Handler.StackageSdist import Handler.StackageSdist
@ -46,8 +43,6 @@ import Handler.System
import Handler.Haddock import Handler.Haddock
import Handler.Package import Handler.Package
import Handler.PackageList import Handler.PackageList
import Handler.Tag
import Handler.BannedTags
import Handler.Hoogle import Handler.Hoogle
import Handler.BuildVersion import Handler.BuildVersion
import Handler.Sitemap import Handler.Sitemap
@ -100,20 +95,12 @@ nicerExceptions app req send = catch (app req send) $ \e -> do
send $ responseLBS status500 [("Content-Type", "text/plain")] $ send $ responseLBS status500 [("Content-Type", "text/plain")] $
fromStrict $ encodeUtf8 text fromStrict $ encodeUtf8 text
getDbConf :: AppConfig DefaultEnv Extra -> IO Settings.PersistConf
getDbConf conf =
withYamlEnvironment "config/postgresql.yml" (appEnv conf)
Database.Persist.loadConfig >>=
Database.Persist.applyEnv
-- | Loads up any necessary settings, creates your foundation datatype, and -- | Loads up any necessary settings, creates your foundation datatype, and
-- performs some initialization. -- performs some initialization.
makeFoundation :: Bool -> AppConfig DefaultEnv Extra -> IO App makeFoundation :: Bool -> AppConfig DefaultEnv Extra -> IO App
makeFoundation useEcho conf = do makeFoundation useEcho conf = do
manager <- newManager manager <- newManager
s <- staticSite s <- staticSite
dbconf <- getDbConf conf
p <- Database.Persist.createPoolConfig dbconf
loggerSet' <- if useEcho loggerSet' <- if useEcho
then newFileLoggerSet defaultBufSize "/dev/null" then newFileLoggerSet defaultBufSize "/dev/null"
@ -149,27 +136,13 @@ makeFoundation useEcho conf = do
foundation = App foundation = App
{ settings = conf { settings = conf
, getStatic = s , getStatic = s
, connPool = p
, httpManager = manager , httpManager = manager
, persistConfig = dbconf
, appLogger = logger , appLogger = logger
, genIO = gen , genIO = gen
, websiteContent = websiteContent' , websiteContent = websiteContent'
, stackageDatabase = stackageDatabase' , stackageDatabase = stackageDatabase'
} }
-- Perform database migration using our application's logging settings.
when (lookup "STACKAGE_SKIP_MIGRATION" env /= Just "1") $
runResourceT $
flip runReaderT gen $
flip runLoggingT (messageLoggerSource foundation logger) $
flip (Database.Persist.runPool dbconf) p $ do
runMigration migrateAll
{-
checkMigration 1 fixSnapSlugs
checkMigration 2 setCorePackages
-}
return foundation return foundation
-- for yesod devel -- for yesod devel
@ -180,13 +153,3 @@ 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

View File

@ -1,13 +1,12 @@
module Foundation where module Foundation where
import ClassyPrelude.Yesod import ClassyPrelude.Yesod
import Data.Slug (safeMakeSlug, HasGenIO (getGenIO), randomSlug, Slug) import Data.Slug (HasGenIO (getGenIO), randomSlug, Slug)
import Data.WebsiteContent import Data.WebsiteContent
import qualified Database.Persist import qualified Database.Persist
import Database.Persist.Sql (PersistentSqlException (Couldn'tGetSQLConnection)) import Database.Persist.Sql (PersistentSqlException (Couldn'tGetSQLConnection))
import Model
import qualified Settings import qualified Settings
import Settings (widgetFile, Extra (..), GoogleAuth (..)) import Settings (widgetFile, Extra (..))
import Settings.Development (development) import Settings.Development (development)
import Settings.StaticFiles import Settings.StaticFiles
import qualified System.Random.MWC as MWC import qualified System.Random.MWC as MWC
@ -29,9 +28,7 @@ import Stackage.Database
data App = App data App = App
{ settings :: AppConfig DefaultEnv Extra { settings :: AppConfig DefaultEnv Extra
, getStatic :: Static -- ^ Settings for static file serving. , getStatic :: Static -- ^ Settings for static file serving.
, connPool :: Database.Persist.PersistConfigPool Settings.PersistConf -- ^ Database connection pool.
, httpManager :: Manager , httpManager :: Manager
, persistConfig :: Settings.PersistConf
, appLogger :: Logger , appLogger :: Logger
, genIO :: MWC.GenIO , genIO :: MWC.GenIO
, websiteContent :: GitRepo WebsiteContent , websiteContent :: GitRepo WebsiteContent
@ -44,9 +41,6 @@ instance HasGenIO App where
instance HasHttpManager App where instance HasHttpManager App where
getHttpManager = httpManager getHttpManager = httpManager
instance HasHackageRoot App where
getHackageRoot = hackageRoot . appExtra . settings
-- This is where we define all of the routes in our application. For a full -- This is where we define all of the routes in our application. For a full
-- explanation of the syntax, please see: -- explanation of the syntax, please see:
-- http://www.yesodweb.com/book/routing-and-handlers -- http://www.yesodweb.com/book/routing-and-handlers
@ -64,9 +58,6 @@ defaultLayoutNoContainer = defaultLayoutWithContainer False
defaultLayoutWithContainer :: Bool -> Widget -> Handler Html defaultLayoutWithContainer :: Bool -> Widget -> Handler Html
defaultLayoutWithContainer insideContainer widget = do defaultLayoutWithContainer insideContainer widget = do
mmsg <- getMessage mmsg <- getMessage
muser <- catch maybeAuth $ \e -> case e of
Couldn'tGetSQLConnection -> return Nothing
_ -> throwM e
-- We break up the default layout into two components: -- We break up the default layout into two components:
-- default-layout is the contents of the body tag, and -- default-layout is the contents of the body tag, and
@ -118,9 +109,6 @@ instance Yesod App where
Just $ uncurry (joinPath y "") $ renderRoute route Just $ uncurry (joinPath y "") $ renderRoute route
urlRenderOverride _ _ = Nothing urlRenderOverride _ _ = Nothing
-- The page to be redirected to when authentication is required.
authRoute _ = Just $ AuthR LoginR
{- Temporarily disable to allow for horizontal scaling {- Temporarily disable to allow for horizontal scaling
-- This function creates static content files in the static folder -- This function creates static content files in the static folder
-- and names them based on a hash of their content. This allows -- and names them based on a hash of their content. This allows
@ -152,108 +140,8 @@ instance ToMarkup (Route App) where
toMarkup c = toMarkup c =
case c of case c of
AllSnapshotsR{} -> "Snapshots" AllSnapshotsR{} -> "Snapshots"
AuthR (LoginR{}) -> "Login"
_ -> "" _ -> ""
-- How to run database actions.
instance YesodPersist App where
type YesodPersistBackend App = SqlBackend
runDB = defaultRunDB persistConfig connPool
instance YesodPersistRunner App where
getDBRunner = defaultGetDBRunner connPool
instance YesodAuth App where
type AuthId App = UserId
-- Where to send a user after successful login
loginDest _ = HomeR
-- Where to send a user after logout
logoutDest _ = HomeR
redirectToReferer _ = True
getAuthId creds = do
muid <- maybeAuthId
join $ runDB $ case muid of
Nothing -> do
x <- getBy $ UniqueEmail $ credsIdent creds
case x of
Just (Entity _ email) -> return $ return $ Just $ emailUser email
Nothing -> do
handle' <- getHandle (0 :: Int)
token <- getToken
userid <- insert User
{ userHandle = handle'
, userDisplay = credsIdent creds
, userToken = token
}
insert_ Email
{ emailEmail = credsIdent creds
, emailUser = userid
}
return $ return $ Just userid
Just uid -> do
memail <- getBy $ UniqueEmail $ credsIdent creds
case memail of
Nothing -> do
insert_ Email
{ emailEmail = credsIdent creds
, emailUser = uid
}
return $ do
setMessage $ toHtml $ concat
[ "Email address "
, credsIdent creds
, " added to your account."
]
redirect ProfileR
Just (Entity _ email)
| emailUser email == uid -> return $ do
setMessage $ toHtml $ concat
[ "The email address "
, credsIdent creds
, " is already part of your account"
]
redirect ProfileR
| otherwise -> invalidArgs $ return $ concat
[ "The email address "
, credsIdent creds
, " is already associated with a different account."
]
where
handleBase = takeWhile (/= '@') (credsIdent creds)
getHandle cnt | cnt > 50 = error "Could not get a unique slug"
getHandle cnt = do
slug <- lift $ safeMakeSlug handleBase (cnt > 0)
muser <- getBy $ UniqueHandle slug
case muser of
Nothing -> return slug
Just _ -> getHandle (cnt + 1)
-- You can add other plugins like BrowserID, email or OAuth here
authPlugins app =
authBrowserId def : google
where
google =
case googleAuth $ appExtra $ settings app of
Nothing -> []
Just GoogleAuth {..} -> [authGoogleEmail gaClientId gaClientSecret]
authHttpManager = httpManager
instance YesodAuthPersist App
getToken :: YesodDB App Slug
getToken =
go (0 :: Int)
where
go cnt | cnt > 50 = error "Could not get a unique token"
go cnt = do
slug <- lift $ randomSlug 25
muser <- getBy $ UniqueToken slug
case muser of
Nothing -> return slug
Just _ -> go (cnt + 1)
-- This instance is required to use forms. You can modify renderMessage to -- This instance is required to use forms. You can modify renderMessage to
-- achieve customized and internationalized form validation messages. -- achieve customized and internationalized form validation messages.
instance RenderMessage App FormMessage where instance RenderMessage App FormMessage where

View File

@ -1,14 +0,0 @@
module Handler.Email where
import Import
import Database.Persist.Sql (deleteWhereCount)
deleteEmailR :: EmailId -> Handler ()
deleteEmailR eid = do
Entity uid _ <- requireAuth
cnt <- runDB $ deleteWhereCount [EmailUser ==. uid, EmailId ==. eid]
setMessage $
if cnt > 0
then "Email address deleted"
else "No matching email address found"
redirect ProfileR

View File

@ -5,10 +5,6 @@
module Handler.Package module Handler.Package
( getPackageR ( getPackageR
, getPackageSnapshotsR , getPackageSnapshotsR
, postPackageLikeR
, postPackageUnlikeR
, postPackageTagR
, postPackageUntagR
, packagePage , packagePage
) where ) where
@ -39,13 +35,6 @@ packagePage mversion pname = do
let pname' = toPathPiece pname let pname' = toPathPiece pname
(deprecated, inFavourOf) <- getDeprecated pname' (deprecated, inFavourOf) <- getDeprecated pname'
latests <- getLatests pname' latests <- getLatests pname'
muid <- maybeAuthId
(nLikes, liked) <- runDB $ do
nLikes <- count [LikePackage ==. pname]
let getLiked uid = (>0) <$> count [LikePackage ==. pname, LikeVoter ==. uid]
liked <- maybe (return False) getLiked muid
return (nLikes, liked)
deps' <- getDeps pname' deps' <- getDeps pname'
revdeps' <- getRevDeps pname' revdeps' <- getRevDeps pname'
Entity _ package <- getPackage pname' >>= maybe notFound return Entity _ package <- getPackage pname' >>= maybe notFound return
@ -65,14 +54,6 @@ packagePage mversion pname = do
let ixInFavourOf = zip [0::Int ..] inFavourOf let ixInFavourOf = zip [0::Int ..] inFavourOf
displayedVersion = maybe (packageLatest package) (toPathPiece . snd) mversion displayedVersion = maybe (packageLatest package) (toPathPiece . snd) mversion
myTags <- maybe (return []) (runDB . user'sTagsOf pname) muid
tags <- fmap (map (\(v,count') -> (v,count',any (==v) myTags)))
(runDB (packageTags pname))
let likeTitle = if liked
then "You liked this!"
else "I like this!" :: Text
let homepage = case T.strip (packageHomepage package) of let homepage = case T.strip (packageHomepage package) of
x | null x -> Nothing x | null x -> Nothing
| otherwise -> Just x | otherwise -> Just x
@ -94,32 +75,6 @@ packagePage mversion pname = do
$(widgetFile "package") $(widgetFile "package")
where enumerate = zip [0::Int ..] where enumerate = zip [0::Int ..]
-- | Get tags of the given package.
packageTags :: PackageName -> YesodDB App [(Slug,Int)]
packageTags pn =
fmap (map boilerplate)
(E.select
(E.from (\(t `E.LeftOuterJoin` bt) -> do
E.on $ t E.^. TagTag E.==. bt E.^. BannedTagTag
E.where_
$ (t ^. TagPackage E.==. E.val pn) E.&&.
(E.isNothing $ E.just $ bt E.^. BannedTagTag)
E.groupBy (t ^. TagTag)
E.orderBy [E.asc (t ^. TagTag)]
return (t ^. TagTag,E.count (t ^. TagTag)))))
where boilerplate (E.Value a,E.Value b) = (a,b)
-- | Get tags of the package by the user.
user'sTagsOf :: PackageName -> UserId -> YesodDB App [Slug]
user'sTagsOf pn uid =
fmap (map (\(E.Value v) -> v))
(E.select
(E.from (\t ->
do E.where_ (t ^. TagPackage E.==. E.val pn E.&&.
t ^. TagVoter E.==. E.val uid)
E.orderBy [E.asc (t ^. TagTag)]
return (t ^. TagTag))))
-- | An identifier specified in a package. Because this field has -- | An identifier specified in a package. Because this field has
-- quite liberal requirements, we often encounter various forms. A -- quite liberal requirements, we often encounter various forms. A
-- name, a name and email, just an email, or maybe nothing at all. -- name, a name and email, just an email, or maybe nothing at all.
@ -211,47 +166,6 @@ parseChunk chunk =
renderEmail :: EmailAddress -> Text renderEmail :: EmailAddress -> Text
renderEmail = T.decodeUtf8 . toByteString renderEmail = T.decodeUtf8 . toByteString
postPackageLikeR :: PackageName -> Handler ()
postPackageLikeR packageName = maybeAuthId >>= \muid -> case muid of
Nothing -> return ()
Just uid -> runDB $ P.insert_ $ Like packageName uid
postPackageUnlikeR :: PackageName -> Handler ()
postPackageUnlikeR name = maybeAuthId >>= \muid -> case muid of
Nothing -> return ()
Just uid -> runDB $ P.deleteWhere [LikePackage ==. name, LikeVoter ==. uid]
postPackageTagR :: PackageName -> Handler ()
postPackageTagR packageName =
maybeAuthId >>=
\muid ->
case muid of
Nothing -> return ()
Just uid ->
do mtag <- lookupPostParam "slug"
case mtag of
Just tag ->
do slug <- mkTag tag
void (runDB (P.insert (Tag packageName slug uid)))
Nothing -> error "Need a slug"
postPackageUntagR :: PackageName -> Handler ()
postPackageUntagR packageName =
maybeAuthId >>=
\muid ->
case muid of
Nothing -> return ()
Just uid ->
do mtag <- lookupPostParam "slug"
case mtag of
Just tag ->
do slug <- mkTag tag
void (runDB (P.deleteWhere
[TagPackage ==. packageName
,TagTag ==. slug
,TagVoter ==. uid]))
Nothing -> error "Need a slug"
getPackageSnapshotsR :: PackageName -> Handler Html getPackageSnapshotsR :: PackageName -> Handler Html
getPackageSnapshotsR pn = getPackageSnapshotsR pn =
do snapshots <- getSnapshotsForPackage $ toPathPiece pn do snapshots <- getSnapshotsForPackage $ toPathPiece pn

View File

@ -1,30 +0,0 @@
module Handler.Profile where
import Import
import Data.Slug (slugField)
userForm :: User -> Form User
userForm user = renderBootstrap2 $ User
<$> areq slugField "User handle"
{ fsTooltip = Just "Used for URLs"
} (Just $ userHandle user)
<*> areq textField "Display name" (Just $ userDisplay user)
<*> pure (userToken user)
getProfileR :: Handler Html
getProfileR = do
Entity uid user <- requireAuth
((result, userWidget), enctype) <- runFormPost $ userForm user
case result of
FormSuccess user' -> do
runDB $ replace uid user'
setMessage "Profile updated"
redirect ProfileR
_ -> return ()
emails <- runDB $ selectList [EmailUser ==. uid] [Asc EmailEmail]
defaultLayout $ do
setTitle "Your Profile"
$(widgetFile "profile")
putProfileR :: Handler Html
putProfileR = getProfileR

View File

@ -1,12 +0,0 @@
module Handler.ResetToken where
import Import
postResetTokenR :: Handler ()
postResetTokenR = do
Entity uid _ <- requireAuth
runDB $ do
token <- getToken
update uid [UserToken =. token]
setMessage "Token updated"
redirect ProfileR

View File

@ -20,23 +20,21 @@ getSitemapR = sitemap $ do
priority 0.7 $ AllSnapshotsR priority 0.7 $ AllSnapshotsR
priority 0.7 $ PackageListR priority 0.7 $ PackageListR
priority 0.6 $ TagListR
priority 0.6 $ AuthorsR priority 0.6 $ AuthorsR
priority 0.6 $ InstallR priority 0.6 $ InstallR
priority 0.6 $ OlderReleasesR priority 0.6 $ OlderReleasesR
{- FIXME
runDBSource $ do runDBSource $ do
--selectAll $= ltsSitemaps --selectAll $= ltsSitemaps
return () $= snapshotSitemaps -- FIXME return () $= snapshotSitemaps -- FIXME
return () $= packageMetadataSitemaps -- FIXME return () $= packageMetadataSitemaps -- FIXME
selectAll $= tagSitemaps
selectAll :: (PersistEntity val, PersistEntityBackend val ~ YesodPersistBackend App) selectAll :: (PersistEntity val, PersistEntityBackend val ~ YesodPersistBackend App)
=> Source (YesodDB App) val => Source (YesodDB App) val
selectAll = selectSource [] [] $= CL.map entityVal selectAll = selectSource [] [] $= CL.map entityVal
{- FIXME
clNub :: (Monad m, Eq a) => Conduit a m a clNub :: (Monad m, Eq a) => Conduit a m a
clNub = evalStateC [] $ awaitForever $ \a -> do clNub = evalStateC [] $ awaitForever $ \a -> do
seen <- State.get seen <- State.get
@ -83,11 +81,6 @@ packageMetadataSitemaps = awaitForever go
where where
url' floc = url $ floc $ PackageName $ packageName m url' floc = url $ floc $ PackageName $ packageName m
tagSitemaps :: SitemapFor Tag
tagSitemaps = awaitForever go
where
go t = url $ TagR $ tagTag t
priority :: Double -> Route App -> Sitemap priority :: Double -> Route App -> Sitemap
priority p loc = yield $ SitemapUrl priority p loc = yield $ SitemapUrl

View File

@ -1,39 +0,0 @@
module Handler.Tag where
import qualified Database.Esqueleto as E
import Data.Slug (Slug, unSlug)
import Import
import Stackage.Database
getTagListR :: Handler Html
getTagListR = do
tags <- fmap (zip [0::Int ..] . (map (\(E.Value v,E.Value i) -> (v,i::Int)))) $ runDB $
E.select $ E.from $ \(tag `E.LeftOuterJoin` bt) -> do
E.groupBy (tag E.^. TagTag)
E.orderBy [E.desc (E.count (tag E.^. TagTag) :: E.SqlExpr (E.Value Int))]
E.on $ tag E.^. TagTag E.==. bt E.^. BannedTagTag
E.where_ $ E.isNothing $ E.just $ bt E.^. BannedTagTag
return (tag E.^. TagTag, E.count (tag E.^. TagTag))
defaultLayout $ do
setTitle "Stackage tags"
$(widgetFile "tag-list")
getTagR :: Slug -> Handler Html
getTagR tagSlug = do
-- FIXME arguably: check if this tag is banned. Leaving it as displayed for
-- now, since someone needs to go out of their way to find it.
packages' <- runDB $ E.select $ E.from $ \tag -> do
E.groupBy (tag E.^. TagPackage)
E.where_ $ tag E.^. TagTag E.==. E.val tagSlug
E.orderBy [E.asc $ tag E.^. TagPackage]
return $ tag E.^. TagPackage
packages <- fmap catMaybes $ forM packages' $ \(E.Value pname) -> do
mp <- getPackage $ toPathPiece pname
return $ case mp of
Nothing -> Nothing
Just (Entity _ p) -> Just (pname, strip $ packageSynopsis p)
let tag = unSlug tagSlug
defaultLayout $ do
setTitle $ "Stackage tag"
$(widgetFile "tag")
where strip x = fromMaybe x (stripSuffix "." x)

View File

@ -4,7 +4,6 @@ module Import
import ClassyPrelude.Yesod as Import import ClassyPrelude.Yesod as Import
import Foundation as Import import Foundation as Import
import Model as Import
import Settings as Import import Settings as Import
import Settings.Development as Import import Settings.Development as Import
import Settings.StaticFiles as Import import Settings.StaticFiles as Import
@ -15,20 +14,6 @@ import Data.WebsiteContent as Import (WebsiteContent (..))
import Data.Text.Read (decimal) import Data.Text.Read (decimal)
import Stackage.Database (SnapName) import Stackage.Database (SnapName)
requireAuthIdOrToken :: Handler UserId
requireAuthIdOrToken = do
mtoken <- lookupHeader "authorization"
case decodeUtf8 <$> mtoken of
Nothing -> requireAuthId
Just token -> do
case mkSlug token of
Nothing -> invalidArgs ["Invalid token: " ++ token]
Just token' -> do
muser <- runDB $ getBy $ UniqueToken token'
case muser of
Nothing -> invalidArgs ["Unknown token: " ++ token]
Just (Entity uid _) -> return uid
parseLtsPair :: Text -> Maybe (Int, Int) parseLtsPair :: Text -> Maybe (Int, Int)
parseLtsPair t1 = do parseLtsPair t1 = do
(x, t2) <- either (const Nothing) Just $ decimal t1 (x, t2) <- either (const Nothing) Just $ decimal t1

View File

@ -1,13 +0,0 @@
module Model where
import ClassyPrelude.Yesod
import Database.Persist.Quasi
import Data.Slug (Slug)
import Types
-- You can define all of your database entities in the entities file.
-- You can find more information on persistent and how to declare entities
-- at:
-- http://www.yesodweb.com/book/persistent/
share [mkPersist sqlSettings, mkMigrate "migrateAll"]
$(persistFileWith lowerCaseSettings "config/models")

View File

@ -8,17 +8,11 @@ module Settings where
import ClassyPrelude.Yesod import ClassyPrelude.Yesod
import Text.Shakespeare.Text (st) import Text.Shakespeare.Text (st)
import Language.Haskell.TH.Syntax import Language.Haskell.TH.Syntax
import Database.Persist.Postgresql (PostgresConf)
import Yesod.Default.Config import Yesod.Default.Config
import Yesod.Default.Util import Yesod.Default.Util
import Data.Yaml import Data.Yaml
import Settings.Development import Settings.Development
import Text.Hamlet import Text.Hamlet
import Data.Aeson (withText, withObject)
import Types
-- | Which Persistent backend this site is using.
type PersistConf = PostgresConf
-- Static setting below. Changing these requires a recompile -- Static setting below. Changing these requires a recompile
@ -65,45 +59,7 @@ widgetFile = (if development then widgetFileReload
widgetFileSettings widgetFileSettings
data Extra = Extra data Extra = Extra
{ storeConfig :: !BlobStoreConfig
, hackageRoot :: !HackageRoot
, adminUsers :: !(HashSet Text)
, googleAuth :: !(Maybe GoogleAuth)
}
deriving Show deriving Show
parseExtra :: DefaultEnv -> Object -> Parser Extra parseExtra :: DefaultEnv -> Object -> Parser Extra
parseExtra _ o = Extra parseExtra _ _ = pure Extra
<$> o .: "blob-store"
<*> (HackageRoot <$> o .: "hackage-root")
<*> o .:? "admin-users" .!= mempty
<*> o .:? "google-auth"
data BlobStoreConfig = BSCFile !FilePath
| BSCAWS !FilePath !Text !Text !Text !Text
deriving Show
instance FromJSON BlobStoreConfig where
parseJSON v = file v <|> aws v
where
file = withText "BlobStoreConfig" $ \t ->
case () of
()
| Just root <- stripPrefix "file:" t -> return $ BSCFile $ fpFromText root
| otherwise -> fail $ "Invalid BlobStoreConfig: " ++ show t
aws = withObject "BlobStoreConfig" $ \o -> BSCAWS
<$> (fpFromText <$> (o .: "local"))
<*> o .: "access"
<*> o .: "secret"
<*> o .: "bucket"
<*> o .:? "prefix" .!= ""
data GoogleAuth = GoogleAuth
{ gaClientId :: !Text
, gaClientSecret :: !Text
}
deriving Show
instance FromJSON GoogleAuth where
parseJSON = withObject "GoogleAuth" $ \o -> GoogleAuth
<$> o .: "client-id"
<*> o .: "client-secret"

View File

@ -64,14 +64,6 @@ newtype HoogleVersion = HoogleVersion Text
currentHoogleVersion :: HoogleVersion currentHoogleVersion :: HoogleVersion
currentHoogleVersion = HoogleVersion VERSION_hoogle currentHoogleVersion = HoogleVersion VERSION_hoogle
newtype HackageRoot = HackageRoot { unHackageRoot :: Text }
deriving (Show, Read, Typeable, Eq, Ord, Hashable, PathPiece, ToMarkup)
class HasHackageRoot a where
getHackageRoot :: a -> HackageRoot
instance HasHackageRoot HackageRoot where
getHackageRoot = id
data UnpackStatus = USReady data UnpackStatus = USReady
| USBusy | USBusy
| USFailed !Text | USFailed !Text

View File

@ -1,35 +0,0 @@
User
handle Slug
display Text
token Slug
UniqueHandle handle
UniqueToken token
deriving Typeable
Email
email Text
user UserId
UniqueEmail email
Verkey
email Text
verkey Text
Tag
package PackageName
tag Slug
voter UserId
UniqueTagPackageVoter package tag voter
Like
package PackageName
voter UserId
UniqueLikePackageVoter package voter
BannedTag
tag Slug
UniqueBannedTag tag
Migration
num Int
UniqueMigration num

View File

@ -1,24 +0,0 @@
Default: &defaults
user: stackage_server
password: stackage-server
host: localhost
port: 5432
database: stackage_server
poolsize: 10
Development:
<<: *defaults
Testing:
database: stackage_server_test
<<: *defaults
Staging:
database: stackage_server_staging
poolsize: 100
<<: *defaults
Production:
database: stackage_server_production
poolsize: 100
<<: *defaults

View File

@ -1,7 +1,6 @@
!/#LtsMajor/*Texts OldLtsMajorR GET !/#LtsMajor/*Texts OldLtsMajorR GET
/static StaticR Static getStatic /static StaticR Static getStatic
/auth AuthR Auth getAuth
/reload WebsiteContentR GitRepo-WebsiteContent websiteContent /reload WebsiteContentR GitRepo-WebsiteContent websiteContent
/favicon.ico FaviconR GET /favicon.ico FaviconR GET
@ -10,9 +9,6 @@
/ HomeR GET / HomeR GET
/snapshots AllSnapshotsR GET /snapshots AllSnapshotsR GET
/profile ProfileR GET PUT
/email/#EmailId EmailR DELETE
/reset-token ResetTokenR POST
/snapshot/#Text/*Texts OldSnapshotR GET /snapshot/#Text/*Texts OldSnapshotR GET
@ -33,13 +29,6 @@
/package/#PackageName PackageR GET /package/#PackageName PackageR GET
/package/#PackageName/snapshots PackageSnapshotsR GET /package/#PackageName/snapshots PackageSnapshotsR GET
/package PackageListR GET /package PackageListR GET
/package/#PackageName/like PackageLikeR POST
/package/#PackageName/unlike PackageUnlikeR POST
/package/#PackageName/tag PackageTagR POST
/package/#PackageName/untag PackageUntagR POST
/tags TagListR GET
/tag/#Slug TagR GET
/banned-tags BannedTagsR GET PUT
/lts/*Texts OldLtsR GET /lts/*Texts OldLtsR GET
/nightly/*Texts OldNightlyR GET /nightly/*Texts OldNightlyR GET

View File

@ -15,7 +15,6 @@ library
exposed-modules: Application exposed-modules: Application
Foundation Foundation
Import Import
Model
Echo Echo
Settings Settings
Settings.StaticFiles Settings.StaticFiles
@ -34,9 +33,6 @@ library
Handler.Home Handler.Home
Handler.Snapshots Handler.Snapshots
Handler.Profile
Handler.Email
Handler.ResetToken
Handler.StackageHome Handler.StackageHome
Handler.StackageIndex Handler.StackageIndex
Handler.StackageSdist Handler.StackageSdist
@ -45,8 +41,6 @@ library
Handler.Hoogle Handler.Hoogle
Handler.Package Handler.Package
Handler.PackageList Handler.PackageList
Handler.Tag
Handler.BannedTags
Handler.BuildVersion Handler.BuildVersion
Handler.Sitemap Handler.Sitemap
Handler.BuildPlan Handler.BuildPlan
@ -119,7 +113,6 @@ library
, mtl >= 2.1 , mtl >= 2.1
, mwc-random >= 0.13 , mwc-random >= 0.13
, persistent >= 1.3.1 , persistent >= 1.3.1
, persistent-postgresql >= 1.3
, persistent-template >= 1.3 , persistent-template >= 1.3
, resourcet >= 1.1.2 , resourcet >= 1.1.2
, shakespeare >= 2.0 , shakespeare >= 2.0

View File

@ -1,6 +0,0 @@
<div .container>
<h1>Banned Tags
<a href=@{TagListR}>List of viewable tags
<form method=post action=@{BannedTagsR}?_method=PUT enctype=#{enctype}>
^{widget}
<button .btn>Update banned tags

View File

@ -1,4 +0,0 @@
textarea {
width: 500px;
height: 400px;
}

View File

@ -20,32 +20,16 @@
$nothing $nothing
<li> <li>
<a href=@{route}>#{route} <a href=@{route}>#{route}
$maybe Entity _ user <- muser
<li>
<a href=@{ProfileR} .user-handle>
#{userDisplay user} (#{userHandle user})
<li>
<a href=@{AuthR LogoutR}>Logout
$nothing
<li>
<a href=@{AuthR LoginR}>Login
$maybe msg <- mmsg $maybe msg <- mmsg
<div .container> <div .container>
<div .alert .alter-info>#{msg} <div .alert .alter-info>#{msg}
$case cur $if insideContainer
$of Just (AuthR _) <div .container>
<div .container> ^{widget}
<h1>Authorization $else
<p>Please login with an authorization method below: ^{widget}
^{widget}
$of _
$if insideContainer
<div .container>
^{widget}
$else
^{widget}
<div .footer> <div .footer>
<div .container> <div .container>

View File

@ -39,33 +39,6 @@ $newline never
<div .row> <div .row>
<div .span12> <div .span12>
<div .social>
<span .likes>
<span #likes>
#{nLikes}
<span #like-or-likes>
\ #{format (plural "like" "likes") nLikes} #
<i .fa :liked:.fa-thumbs-up :not liked:.fa-thumbs-o-up #like title="#{likeTitle}">
<span .tags>
$if null tags
<span .no-tags>
No tags yet. #
$forall (tag,count,tagged) <- tags
<span .tag>
<a href=@{TagR tag} .tag-name>
#{tag} #
<a .tag-count .tag-toggle data-slug=#{tag} :tagged:.tagged title="Add/remove vote for: #{tag}">
#{count}
, #
<i #add-tag class="fa fa-plus-square" title="Show/hide tag form">
<form #add-tag-form .hidden>
<p>
<strong>Add tag
<div .input-append>
<input type="text" id="new-tag">
<input type="submit" .btn #add-form-btn value="Confirm">
<p #tag-msg .alert .alert-error style="display:none">
<div .authorship> <div .authorship>
<span .license> <span .license>
<a href=""> <a href="">

View File

@ -1,151 +0,0 @@
$(function(){
var loggedIn = $('.user-handle').length > 0;
var tags = Object.create(null);
function toggleClick(){
if (!loggedIn) {
login();
return false;
}
var $this = $(this);
var normalized = $this.data('slug');
var upvote = !$this.hasClass('tagged');
$this.text($this.text() * 1 + (upvote? 1 : -1));
$this.toggleClass('tagged');
if (upvote)
$.ajax({
method: 'POST',
url: '@{PackageTagR pn}',
data: {slug:normalized}
});
else
$.ajax({
method: 'POST',
url: '@{PackageUntagR pn}',
data: {slug:normalized}
});
}
$('.tags').find('.tag').each(function(){
tags[$(this).find('.tag-name').text()] = true;
});
$('.expanding').each(function(){
var $this = $(this);
if ($this.height() > 300) {
$this.addClass('collapsed');
$this.find('.bottom-gradient').click(function(){
$this.removeClass('collapsed');
});
}
});
$('#like').click(function(){
var $this = $(this);
if (loggedIn) {
var action = 'like';
if ($this.hasClass('fa-thumbs-up')) {
action = 'unlike';
}
$this.toggleClass('fa-thumbs-up');
$this.toggleClass('fa-thumbs-o-up');
$likes = $('#likes');
nLikes = parseInt($likes.text(), 10);
if (action == 'like') {
$this.attr('title','You liked this!');
nLikes += 1;
$.post("@{PackageLikeR pn}");
} else {
$this.attr('title','You disliked this.');
nLikes -= 1;
$.post("@{PackageUnlikeR pn}");
}
$likes.text(nLikes);
$('#like-or-likes').text(nLikes == 1 ? ' like ' : ' likes ');
} else {
login();
}
});
$('.tag-toggle').click(toggleClick);
$('#add-tag').click(function(){
if (!loggedIn) login();
$('#add-tag-form').toggleClass('hidden');
$('#new-tag').focus();
});
$('#new-tag').change(function(){
$('#add-form-btn').val('Confirm');
$('#tag-msg').hide();
});
$('#new-tag').keypress(function(){
$('#add-form-btn').val('Confirm');
});
$('#add-tag-form').submit(function(){
try {
var candidate = $('#new-tag').val();
var normalized = candidate
.replace(/[^a-zA-Z0-9-.]/g,'-')
.replace(/-+/g,'-')
.replace(/^-/,'')
.replace(/-$/,'')
.toLowerCase();
if (candidate !== normalized) {
$('#new-tag').val(normalized);
$('#add-form-btn').val('Done');
} else {
$.ajax({
method: 'POST',
url: '@{PackageTagR pn}',
data: {slug:normalized},
success: function(){
$('.no-tags').remove();
$('#new-tag').val('');
$('#add-form-btn').val('Confirm');
if (!tags[normalized]) {
var tag = $('<span><a></a></span>');
tag.find('a').text(normalized + ' ').attr('href','/tag/' + normalized);
$('.tags').prepend(', ');
var count = $('<a>1</a>')
.addClass('tag-count')
.addClass('tag-toggle')
.addClass('tagged')
.data('slug',normalized)
.attr('title','Add/remove vote for: ' + normalized)
.click(toggleClick);
$('.tags').prepend(count);
$('.tags').prepend(tag);
}
tags[normalized] = true;
},
error: function(err){
$('#tag-msg').text("Couldn't add that tag").show();
}
});
}
} finally {
return false;
}
});
});
// Workaround for missing functionality in IE 8 and earlier.
if( Object.create === undefined ) {
Object.create = function( o ) {
function F(){}
F.prototype = o;
return new F();
};
}
function login(){
window.location.href = '@{AuthR LoginR}';
}

View File

@ -1,33 +0,0 @@
<div .container>
<h1>
Profile
<div .row>
<div .span12>
<h2>Email addresses
$if length emails <= 1
$forall Entity _ email <- emails
<p>#{emailEmail email}
$else
<ul>
$forall Entity eid email <- emails
<li .email>
#{emailEmail email}
<form method=post action=@{EmailR eid}?_method=DELETE>
<button .btn>Remove
<p>
<a href=@{AuthR LoginR}>Add another email address.
<h2>Profile
<form method=post action=@{ProfileR}?_method=PUT enctype=#{enctype} role=form>
<div .form-group>
^{userWidget}
<button .btn>Update
<h2>Security token
<p>
Your security token is #{userToken user}.
<form method=post action=@{ResetTokenR}>
<button>Reset token

View File

@ -1,13 +0,0 @@
.email > form {
display: inline-block;
}
#aliases {
display: block;
width: 400px;
height: 200px;
}
h2 {
font-size: 30px !important;
}

View File

@ -1,11 +0,0 @@
$newline never
<div .container>
<h1>Tags
<p .tags>
$forall (i,(tag,count)) <- tags
$if i /= 0
, #
<span .tag>
<a href=@{TagR tag}>
#{tag} #
<span .count>(#{count})

View File

@ -1,16 +0,0 @@
.tags > li {
list-style-type: none;
}
.tags {
margin: 1em 0 0 0;
padding: 0;
}
h1 {
margin-bottom: 0.5em;
}
.count {
color: #666;
}

View File

@ -1,21 +0,0 @@
$newline never
<div .container>
<h1>
Tag: #{tag} (
<a href=@{TagListR}>
all tags
)
<div .packages>
<table .table>
<thead>
<th>Package
<th>Synopsis
<tbody>
$forall (name,synopsis) <- packages
<tr>
<td>
<a href=@{PackageR name}>
#{name}
<td>
#{synopsis}

View File

@ -1,12 +0,0 @@
.packages > li {
list-style-type: none;
}
.packages {
margin: 1em 0 0 0;
padding: 0;
}
h1 {
margin-bottom: 0.5em;
}