add index and deindex endpoints

This commit is contained in:
Keagan McClelland
2022-05-23 16:16:14 -06:00
parent bb8fe05db6
commit 8929cdb791
8 changed files with 176 additions and 56 deletions

View File

@@ -303,7 +303,7 @@ startApp foundation = do
-- certbot renew loop -- certbot renew loop
void . forkIO $ forever $ flip runReaderT foundation $ do void . forkIO $ forever $ flip runReaderT foundation $ do
shouldRenew <- doesSslNeedRenew shouldRenew <- doesSslNeedRenew
runLog $ $logInfo $ [i|Checking if SSL Certs should be renewed: #{shouldRenew}|] runLog $ $logInfo [i|Checking if SSL Certs should be renewed: #{shouldRenew}|]
when shouldRenew $ do when shouldRenew $ do
runLog $ $logInfo "Renewing SSL Certs." runLog $ $logInfo "Renewing SSL Certs."
renewSslCerts renewSslCerts
@@ -322,9 +322,9 @@ startWeb foundation = do
where where
startWeb' app = (`onException` (appStopFsNotifyPkg foundation *> appStopFsNotifyEos foundation)) $ do startWeb' app = (`onException` (appStopFsNotifyPkg foundation *> appStopFsNotifyEos foundation)) $ do
let AppSettings {..} = appSettings foundation let AppSettings {..} = appSettings foundation
runLog $ $logInfo $ [i|Launching Tor Web Server on port #{torPort}|] runLog $ $logInfo [i|Launching Tor Web Server on port #{torPort}|]
torAction <- async $ runSettings (warpSettings torPort foundation) app torAction <- async $ runSettings (warpSettings torPort foundation) app
runLog $ $logInfo $ [i|Launching Web Server on port #{appPort}|] runLog $ $logInfo [i|Launching Web Server on port #{appPort}|]
action <- async $ if sslAuto action <- async $ if sslAuto
then runTLS (tlsSettings sslCertLocation sslKeyLocation) (warpSettings appPort foundation) app then runTLS (tlsSettings sslCertLocation sslKeyLocation) (warpSettings appPort foundation) app
else runSettings (warpSettings appPort foundation) app else runSettings (warpSettings appPort foundation) app

View File

@@ -1,6 +1,7 @@
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DeriveGeneric #-} {-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Fuse on/on" #-}
module Database.Marketplace where module Database.Marketplace where
@@ -54,24 +55,24 @@ type CategoryTitle = Text
searchServices :: (MonadResource m, MonadIO m) searchServices :: (MonadResource m, MonadIO m)
=> Maybe CategoryTitle => Maybe CategoryTitle
-> Text -> Text
-> ConduitT () (Entity PkgRecord) (ReaderT SqlBackend m) () -> ConduitT () (Entity VersionRecord) (ReaderT SqlBackend m) ()
searchServices Nothing query = selectSource $ do searchServices Nothing query = selectSource $ do
service <- from $ table @PkgRecord service <- from $ table @VersionRecord
where_ where_
( (service ^. PkgRecordDescShort `ilike` (%) ++. val query ++. (%)) ( (service ^. VersionRecordDescShort `ilike` (%) ++. val query ++. (%))
||. (service ^. PkgRecordDescLong `ilike` (%) ++. val query ++. (%)) ||. (service ^. VersionRecordDescLong `ilike` (%) ++. val query ++. (%))
||. (service ^. PkgRecordTitle `ilike` (%) ++. val query ++. (%)) ||. (service ^. VersionRecordTitle `ilike` (%) ++. val query ++. (%))
) )
orderBy [desc (service ^. PkgRecordUpdatedAt)] orderBy [desc (service ^. VersionRecordUpdatedAt)]
pure service pure service
searchServices (Just category) query = selectSource $ do searchServices (Just category) query = selectSource $ do
services <- from services <- from
(do (do
(service :& _ :& cat) <- (service :& _ :& cat) <-
from from
$ table @PkgRecord $ table @VersionRecord
`innerJoin` table @PkgCategory `innerJoin` table @PkgCategory
`on` (\(s :& sc) -> sc ^. PkgCategoryPkgId ==. s ^. PkgRecordId) `on` (\(s :& sc) -> sc ^. PkgCategoryPkgId ==. s ^. VersionRecordPkgId)
`innerJoin` table @Category `innerJoin` table @Category
`on` (\(_ :& sc :& cat) -> sc ^. PkgCategoryCategoryId ==. cat ^. CategoryId) `on` (\(_ :& sc :& cat) -> sc ^. PkgCategoryCategoryId ==. cat ^. CategoryId)
-- if there is a cateogry, only search in category -- if there is a cateogry, only search in category
@@ -80,13 +81,13 @@ searchServices (Just category) query = selectSource $ do
$ cat $ cat
^. CategoryName ^. CategoryName
==. val category ==. val category
&&. ( (service ^. PkgRecordDescShort `ilike` (%) ++. val query ++. (%)) &&. ( (service ^. VersionRecordDescShort `ilike` (%) ++. val query ++. (%))
||. (service ^. PkgRecordDescLong `ilike` (%) ++. val query ++. (%)) ||. (service ^. VersionRecordDescLong `ilike` (%) ++. val query ++. (%))
||. (service ^. PkgRecordTitle `ilike` (%) ++. val query ++. (%)) ||. (service ^. VersionRecordTitle `ilike` (%) ++. val query ++. (%))
) )
pure service pure service
) )
orderBy [desc (services ^. PkgRecordUpdatedAt)] orderBy [desc (services ^. VersionRecordUpdatedAt)]
pure services pure services
getPkgData :: (MonadResource m, MonadIO m) => [PkgId] -> ConduitT () (Entity PkgRecord) (ReaderT SqlBackend m) () getPkgData :: (MonadResource m, MonadIO m) => [PkgId] -> ConduitT () (Entity PkgRecord) (ReaderT SqlBackend m) ()
@@ -98,20 +99,19 @@ getPkgData pkgs = selectSource $ do
getPkgDependencyData :: MonadIO m getPkgDependencyData :: MonadIO m
=> Key PkgRecord => Key PkgRecord
-> Version -> Version
-> ReaderT SqlBackend m ([(Entity PkgDependency, Entity PkgRecord)]) -> ReaderT SqlBackend m [(Entity PkgDependency, Entity PkgRecord)]
getPkgDependencyData pkgId pkgVersion = select $ do getPkgDependencyData pkgId pkgVersion = select $ do
pd <- from from
(do (do
(pkgDepRecord :& depPkgRecord) <- (pkgDepRecord :& depPkgRecord) <-
from from
$ table @PkgDependency $ table @PkgDependency
`innerJoin` table @PkgRecord `innerJoin` table @PkgRecord
`on` (\(pdr :& dpr) -> dpr ^. PkgRecordId ==. pdr ^. PkgDependencyDepId) `on` (\(pdr :& dpr) -> dpr ^. PkgRecordId ==. pdr ^. PkgDependencyDepId)
where_ (pkgDepRecord ^. PkgDependencyPkgId ==. (val pkgId)) where_ (pkgDepRecord ^. PkgDependencyPkgId ==. val pkgId)
where_ (pkgDepRecord ^. PkgDependencyPkgVersion ==. val pkgVersion) where_ (pkgDepRecord ^. PkgDependencyPkgVersion ==. val pkgVersion)
pure (pkgDepRecord, depPkgRecord) pure (pkgDepRecord, depPkgRecord)
) )
pure pd
zipCategories :: MonadUnliftIO m zipCategories :: MonadUnliftIO m
=> ConduitT => ConduitT
@@ -147,7 +147,7 @@ zipDependencyVersions :: (Monad m, MonadIO m)
=> (Entity PkgDependency, Entity PkgRecord) => (Entity PkgDependency, Entity PkgRecord)
-> ReaderT SqlBackend m PackageDependencyMetadata -> ReaderT SqlBackend m PackageDependencyMetadata
zipDependencyVersions (pkgDepRecord, depRecord) = do zipDependencyVersions (pkgDepRecord, depRecord) = do
let pkgDbId = entityKey $ depRecord let pkgDbId = entityKey depRecord
depVers <- select $ do depVers <- select $ do
v <- from $ table @VersionRecord v <- from $ table @VersionRecord
where_ $ v ^. VersionRecordPkgId ==. val pkgDbId where_ $ v ^. VersionRecordPkgId ==. val pkgDbId

View File

@@ -1,15 +1,38 @@
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
module Database.Queries where module Database.Queries where
import Database.Persist.Sql import Database.Persist.Sql ( PersistStoreRead(get)
import Lib.Types.AppIndex , PersistStoreWrite(insertKey, insert_, repsert)
import Lib.Types.Emver , SqlBackend
import Model )
import Lib.Types.AppIndex ( PackageManifest(..)
, PkgId
)
import Lib.Types.Emver ( Version )
import Model ( Key(PkgRecordKey, VersionRecordKey)
, Metric(Metric)
, PkgRecord(PkgRecord)
, VersionRecord(VersionRecord)
)
import Orphans.Emver ( ) import Orphans.Emver ( )
import Startlude hiding ( get ) import Startlude ( ($)
, (.)
, ConvertText(toS)
, Maybe(..)
, MonadIO(..)
, ReaderT
, SomeException
, getCurrentTime
, maybe
)
import System.FilePath ( takeExtension )
import UnliftIO ( MonadUnliftIO
, try
)
fetchApp :: MonadIO m => PkgId -> ReaderT SqlBackend m (Maybe PkgRecord) fetchApp :: MonadIO m => PkgId -> ReaderT SqlBackend m (Maybe PkgRecord)
fetchApp = get . PkgRecordKey fetchApp = get . PkgRecordKey
@@ -21,3 +44,22 @@ createMetric :: MonadIO m => PkgId -> Version -> ReaderT SqlBackend m ()
createMetric appId version = do createMetric appId version = do
time <- liftIO getCurrentTime time <- liftIO getCurrentTime
insert_ $ Metric time (PkgRecordKey appId) version insert_ $ Metric time (PkgRecordKey appId) version
upsertPackageVersion :: (MonadUnliftIO m) => PackageManifest -> ReaderT SqlBackend m ()
upsertPackageVersion PackageManifest {..} = do
now <- liftIO getCurrentTime
let iconType = maybe "png" (toS . takeExtension . toS) packageManifestIcon
let pkgId = PkgRecordKey packageManifestId
let ins = VersionRecord now
(Just now)
pkgId
packageManifestVersion
packageManifestTitle
packageManifestDescriptionShort
packageManifestDescriptionLong
iconType
packageManifestReleaseNotes
packageManifestEosVersion
Nothing
_res <- try @_ @SomeException $ insertKey pkgId (PkgRecord now (Just now))
repsert (VersionRecordKey pkgId packageManifestVersion) ins

View File

@@ -9,16 +9,33 @@ import Conduit ( (.|)
) )
import Control.Monad.Reader.Has ( ask ) import Control.Monad.Reader.Has ( ask )
import Control.Monad.Trans.Maybe ( MaybeT(..) ) import Control.Monad.Trans.Maybe ( MaybeT(..) )
import Data.Aeson ( decodeFileStrict ) import Data.Aeson ( (.:)
, FromJSON(parseJSON)
, decodeFileStrict
, withObject
)
import Data.String.Interpolate.IsString
( i )
import Database.Queries ( upsertPackageVersion )
import Foundation import Foundation
import Lib.PkgRepository ( PkgRepo(PkgRepo, pkgRepoFileRoot) import Lib.PkgRepository ( PkgRepo(PkgRepo, pkgRepoFileRoot)
, extractPkg , extractPkg
, getManifestLocation
)
import Lib.Types.AppIndex ( PackageManifest(..)
, PkgId
)
import Lib.Types.Emver ( Version(..) )
import Model ( Key(PkgRecordKey, VersionRecordKey) )
import Network.HTTP.Types ( status404
, status500
) )
import Lib.Types.AppIndex ( PackageManifest(..) )
import Network.HTTP.Types ( status500 )
import Startlude ( ($) import Startlude ( ($)
, (.) , (.)
, (<$>) , (<$>)
, Applicative(pure)
, Eq
, Show
, SomeException(..) , SomeException(..)
, asum , asum
, hush , hush
@@ -38,9 +55,12 @@ import UnliftIO.Directory ( renameDirectory )
import Util.Shared ( orThrow import Util.Shared ( orThrow
, sendResponseText , sendResponseText
) )
import Yesod ( getsYesod import Yesod ( delete
, getsYesod
, logError , logError
, rawRequestBody , rawRequestBody
, requireCheckJsonBody
, runDB
) )
postPkgUploadR :: Handler () postPkgUploadR :: Handler ()
@@ -58,8 +78,28 @@ postPkgUploadR = do
renameDirectory path (pkgRepoFileRoot </> show packageManifestId </> show packageManifestVersion) renameDirectory path (pkgRepoFileRoot </> show packageManifestId </> show packageManifestVersion)
where retry m = runMaybeT . asum $ replicate 3 (MaybeT $ hush <$> try @_ @SomeException m) where retry m = runMaybeT . asum $ replicate 3 (MaybeT $ hush <$> try @_ @SomeException m)
data IndexPkgReq = IndexPkgReq
{ indexPkgReqId :: !PkgId
, indexPkgReqVersion :: !Version
}
deriving (Eq, Show)
instance FromJSON IndexPkgReq where
parseJSON = withObject "Index Package Request" $ \o -> do
indexPkgReqId <- o .: "id"
indexPkgReqVersion <- o .: "version"
pure IndexPkgReq { .. }
postPkgIndexR :: Handler () postPkgIndexR :: Handler ()
postPkgIndexR = _ postPkgIndexR = do
IndexPkgReq {..} <- requireCheckJsonBody
manifest <- getManifestLocation indexPkgReqId indexPkgReqVersion
man <- liftIO (decodeFileStrict manifest) `orThrow` sendResponseText
status404
[i|Could not locate manifest for #{indexPkgReqId}@#{indexPkgReqVersion}|]
runDB $ upsertPackageVersion man
postPkgDeindexR :: Handler () postPkgDeindexR :: Handler ()
postPkgDeindexR = _ postPkgDeindexR = do
IndexPkgReq {..} <- requireCheckJsonBody
runDB $ delete (VersionRecordKey (PkgRecordKey indexPkgReqId) indexPkgReqVersion)

View File

@@ -286,14 +286,18 @@ watchEosRepoRoot pool = do
Right version -> Right version ->
void $ flip runSqlPool pool $ upsert (EosHash version hashText) [EosHashHash =. hashText] void $ flip runSqlPool pool $ upsert (EosHash version hashText) [EosHashHash =. hashText]
getManifestLocation :: (MonadReader r m, Has PkgRepo r) => PkgId -> Version -> m FilePath
getManifestLocation pkg version = do
root <- asks pkgRepoFileRoot
pure $ root </> show pkg </> show version </> "manifest.json"
getManifest :: (MonadResource m, MonadReader r m, Has PkgRepo r) getManifest :: (MonadResource m, MonadReader r m, Has PkgRepo r)
=> PkgId => PkgId
-> Version -> Version
-> m (Integer, ConduitT () ByteString m ()) -> m (Integer, ConduitT () ByteString m ())
getManifest pkg version = do getManifest pkg version = do
root <- asks pkgRepoFileRoot manifestPath <- getManifestLocation pkg version
let manifestPath = root </> show pkg </> show version </> "manifest.json" n <- getFileSize manifestPath
n <- getFileSize manifestPath
pure (n, sourceFile manifestPath) pure (n, sourceFile manifestPath)
getInstructions :: (MonadResource m, MonadReader r m, Has PkgRepo r) getInstructions :: (MonadResource m, MonadReader r m, Has PkgRepo r)

View File

@@ -100,6 +100,7 @@ data PackageManifest = PackageManifest
, packageManifestIcon :: !(Maybe Text) , packageManifestIcon :: !(Maybe Text)
, packageManifestAlerts :: !(HM.HashMap ServiceAlert (Maybe Text)) , packageManifestAlerts :: !(HM.HashMap ServiceAlert (Maybe Text))
, packageManifestDependencies :: !(HM.HashMap PkgId PackageDependency) , packageManifestDependencies :: !(HM.HashMap PkgId PackageDependency)
, packageManifestEosVersion :: !Version
} }
deriving Show deriving Show
instance FromJSON PackageManifest where instance FromJSON PackageManifest where
@@ -120,6 +121,7 @@ instance FromJSON PackageManifest where
pure (alertType, alertDesc) pure (alertType, alertDesc)
let packageManifestAlerts = HM.fromList a let packageManifestAlerts = HM.fromList a
packageManifestDependencies <- o .: "dependencies" packageManifestDependencies <- o .: "dependencies"
packageManifestEosVersion <- o .: "eos-version"
pure PackageManifest { .. } pure PackageManifest { .. }
-- >>> eitherDecode testManifest :: Either String PackageManifest -- >>> eitherDecode testManifest :: Either String PackageManifest

View File

@@ -24,10 +24,10 @@ PkgRecord
Id PkgId sql=pkg_id Id PkgId sql=pkg_id
createdAt UTCTime createdAt UTCTime
updatedAt UTCTime Maybe updatedAt UTCTime Maybe
title Text -- title Text
descShort Text -- descShort Text
descLong Text -- descLong Text
iconType Text -- iconType Text
deriving Eq deriving Eq
deriving Show deriving Show
@@ -36,6 +36,10 @@ VersionRecord sql=version
updatedAt UTCTime Maybe updatedAt UTCTime Maybe
pkgId PkgRecordId pkgId PkgRecordId
number Version number Version
title Text
descShort Text
descLong Text
iconType Text
releaseNotes Text releaseNotes Text
osVersion Version osVersion Version
arch Text Maybe arch Text Maybe

View File

@@ -1,14 +1,13 @@
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
module Util.Shared where module Util.Shared where
import Startlude hiding ( Any
, Handler
, yield
)
import qualified Data.Text as T import qualified Data.Text as T
import Network.HTTP.Types import Network.HTTP.Types
@@ -18,10 +17,10 @@ import Conduit ( ConduitT
, awaitForever , awaitForever
, yield , yield
) )
import Control.Monad.Reader.Has ( Has ) import Control.Monad.Reader.Has ( Has
import Data.Semigroup ( Max(Max) , MonadReader
, getMax
) )
import Data.Semigroup ( (<>) )
import Data.String.Interpolate.IsString import Data.String.Interpolate.IsString
( i ) ( i )
import Database.Esqueleto.Experimental import Database.Esqueleto.Experimental
@@ -43,10 +42,33 @@ import Lib.Types.Emver
import Model ( Category import Model ( Category
, Key(unPkgRecordKey) , Key(unPkgRecordKey)
, PkgDependency(pkgDependencyDepId, pkgDependencyDepVersionRange) , PkgDependency(pkgDependencyDepId, pkgDependencyDepVersionRange)
, PkgRecord(pkgRecordTitle) , PkgRecord
, VersionRecord(versionRecordNumber, versionRecordOsVersion) , VersionRecord(..)
, pkgDependencyPkgId , pkgDependencyPkgId
) )
import Startlude ( ($)
, (.)
, (<$>)
, Alternative((<|>))
, Applicative(pure)
, Bool(..)
, Down(Down)
, Foldable(foldr, null)
, Functor(fmap)
, Maybe(..)
, Monad((>>=))
, Ord((>))
, Text
, decodeUtf8
, filter
, fromMaybe
, headMay
, isSpace
, not
, readMaybe
, sortOn
, unless
)
getVersionSpecFromQuery :: Handler VersionRange getVersionSpecFromQuery :: Handler VersionRange
getVersionSpecFromQuery = do getVersionSpecFromQuery = do
@@ -116,17 +138,17 @@ filterLatestVersionFromSpec versionMap = awaitForever $ \(a, vs, cats) -> do
-- get best version of the dependency based on what is specified in the db (ie. what is specified in the manifest for the package) -- get best version of the dependency based on what is specified in the db (ie. what is specified in the manifest for the package)
filterDependencyBestVersion :: MonadLogger m => PackageDependencyMetadata -> m (Maybe (Key PkgRecord, Text, Version)) filterDependencyBestVersion :: MonadLogger m => PackageDependencyMetadata -> m (Maybe (Key PkgRecord, Text, Version))
filterDependencyBestVersion PackageDependencyMetadata { packageDependencyMetadataPkgDependencyRecord = pkgDepRecord, packageDependencyMetadataDepPkgRecord = depRecord, packageDependencyMetadataDepVersions = depVersions } filterDependencyBestVersion PackageDependencyMetadata { packageDependencyMetadataPkgDependencyRecord = pkgDepRecord, packageDependencyMetadataDepVersions = depVersions }
= do = do
-- get best version from VersionRange of dependency -- get best version from VersionRange of dependency
let pkgId = pkgDependencyPkgId $ entityVal pkgDepRecord let pkgId = pkgDependencyPkgId $ entityVal pkgDepRecord
let depId = pkgDependencyDepId $ entityVal pkgDepRecord let depId = pkgDependencyDepId $ entityVal pkgDepRecord
let depTitle = pkgRecordTitle $ entityVal depRecord let satisfactory = filter
let satisfactory = filter (<|| (pkgDependencyDepVersionRange $ entityVal pkgDepRecord)) ((<|| (pkgDependencyDepVersionRange $ entityVal pkgDepRecord)) . versionRecordNumber)
(versionRecordNumber . entityVal <$> depVersions) (entityVal <$> depVersions)
case getMax <$> foldMap (Just . Max) satisfactory of case maximumOn versionRecordNumber satisfactory of
-- QUESTION is this an acceptable transformation here? These are the only values that we care about after this filter. -- QUESTION is this an acceptable transformation here? These are the only values that we care about after this filter.
Just bestVersion -> pure $ Just (depId, depTitle, bestVersion) Just bestVersion -> pure $ Just (depId, versionRecordTitle bestVersion, versionRecordNumber bestVersion)
Nothing -> do Nothing -> do
-- TODO it would be better if we could return the requirements for display -- TODO it would be better if we could return the requirements for display
$logInfo [i|No satisfactory version of #{depId} for dependent package #{pkgId}|] $logInfo [i|No satisfactory version of #{depId} for dependent package #{pkgId}|]
@@ -134,3 +156,9 @@ filterDependencyBestVersion PackageDependencyMetadata { packageDependencyMetadat
sendResponseText :: MonadHandler m => Status -> Text -> m a sendResponseText :: MonadHandler m => Status -> Text -> m a
sendResponseText = sendResponseStatus @_ @Text sendResponseText = sendResponseStatus @_ @Text
maximumOn :: forall a b t . (Ord b, Foldable t) => (a -> b) -> t a -> Maybe a
maximumOn f = foldr (\x y -> maxOn f x <$> y <|> Just x) Nothing
maxOn :: Ord b => (a -> b) -> a -> a -> a
maxOn f x y = if f x > f y then x else y