mirror of
https://github.com/Start9Labs/registry.git
synced 2026-03-26 02:11:53 +00:00
zip eos image on upload
This commit is contained in:
@@ -74,6 +74,7 @@ dependencies:
|
|||||||
- yesod-auth-basic
|
- yesod-auth-basic
|
||||||
- yesod-core
|
- yesod-core
|
||||||
- yesod-persistent
|
- yesod-persistent
|
||||||
|
- zip-stream
|
||||||
|
|
||||||
library:
|
library:
|
||||||
source-dirs: src
|
source-dirs: src
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import Crypto.Hash (
|
|||||||
SHA256 (SHA256),
|
SHA256 (SHA256),
|
||||||
hashWith,
|
hashWith,
|
||||||
)
|
)
|
||||||
|
import Crypto.Hash.Conduit (hashFile)
|
||||||
import Data.Aeson (
|
import Data.Aeson (
|
||||||
ToJSON,
|
ToJSON,
|
||||||
eitherDecodeStrict,
|
eitherDecodeStrict,
|
||||||
@@ -70,9 +71,7 @@ import Handler.Admin (
|
|||||||
PackageList (..),
|
PackageList (..),
|
||||||
)
|
)
|
||||||
import Lib.External.AppMgr (sourceManifest)
|
import Lib.External.AppMgr (sourceManifest)
|
||||||
import Lib.Types.Core (
|
import Lib.Types.Core (PkgId (..))
|
||||||
PkgId (..),
|
|
||||||
)
|
|
||||||
import Lib.Types.Emver (Version (..))
|
import Lib.Types.Emver (Version (..))
|
||||||
import Lib.Types.Manifest (PackageManifest (..))
|
import Lib.Types.Manifest (PackageManifest (..))
|
||||||
import Network.HTTP.Client.Conduit (
|
import Network.HTTP.Client.Conduit (
|
||||||
@@ -90,7 +89,8 @@ import Network.HTTP.Simple (
|
|||||||
parseRequest,
|
parseRequest,
|
||||||
setRequestBody,
|
setRequestBody,
|
||||||
setRequestBodyJSON,
|
setRequestBodyJSON,
|
||||||
setRequestHeaders, setRequestQueryString
|
setRequestHeaders,
|
||||||
|
setRequestQueryString,
|
||||||
)
|
)
|
||||||
import Network.HTTP.Types (status200)
|
import Network.HTTP.Types (status200)
|
||||||
import Network.URI (
|
import Network.URI (
|
||||||
@@ -150,6 +150,7 @@ import Startlude (
|
|||||||
IsString (..),
|
IsString (..),
|
||||||
Maybe (..),
|
Maybe (..),
|
||||||
Monad ((>>=)),
|
Monad ((>>=)),
|
||||||
|
Num (fromInteger),
|
||||||
ReaderT (runReaderT),
|
ReaderT (runReaderT),
|
||||||
Semigroup ((<>)),
|
Semigroup ((<>)),
|
||||||
Show,
|
Show,
|
||||||
@@ -205,7 +206,6 @@ import Yesod (
|
|||||||
logError,
|
logError,
|
||||||
logWarn,
|
logWarn,
|
||||||
)
|
)
|
||||||
import Crypto.Hash.Conduit (hashFile)
|
|
||||||
|
|
||||||
|
|
||||||
data Upload = Upload
|
data Upload = Upload
|
||||||
@@ -215,6 +215,7 @@ data Upload = Upload
|
|||||||
}
|
}
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
|
|
||||||
data EosUpload = EosUpload
|
data EosUpload = EosUpload
|
||||||
{ eosRepoName :: !String
|
{ eosRepoName :: !String
|
||||||
, eosPath :: !FilePath
|
, eosPath :: !FilePath
|
||||||
@@ -429,6 +430,7 @@ parsePkgCat = subparser $ command "categorize" (info cat $ progDesc "Add or remo
|
|||||||
<*> strArgument (metavar "PACKAGE_ID")
|
<*> strArgument (metavar "PACKAGE_ID")
|
||||||
<*> strArgument (metavar "CATEGORY")
|
<*> strArgument (metavar "CATEGORY")
|
||||||
|
|
||||||
|
|
||||||
parseEosPublish :: Parser EosUpload
|
parseEosPublish :: Parser EosUpload
|
||||||
parseEosPublish =
|
parseEosPublish =
|
||||||
subparser $
|
subparser $
|
||||||
@@ -572,19 +574,25 @@ upload (Upload name mpkg shouldIndex) = do
|
|||||||
sfs2prog :: StreamFileStatus -> Progress ()
|
sfs2prog :: StreamFileStatus -> Progress ()
|
||||||
sfs2prog StreamFileStatus{..} = Progress (fromIntegral readSoFar) (fromIntegral fileSize) ()
|
sfs2prog StreamFileStatus{..} = Progress (fromIntegral readSoFar) (fromIntegral fileSize) ()
|
||||||
|
|
||||||
|
|
||||||
eosUpload :: EosUpload -> IO ()
|
eosUpload :: EosUpload -> IO ()
|
||||||
eosUpload (EosUpload name img version) = do
|
eosUpload (EosUpload name img version) = do
|
||||||
PublishCfgRepo{..} <- findNameInCfg name
|
PublishCfgRepo{..} <- findNameInCfg name
|
||||||
|
size <- fromInteger <$> getFileSize img
|
||||||
noBody <-
|
noBody <-
|
||||||
parseRequest ("POST " <> show publishCfgRepoLocation <> "/admin/v0/eos-upload")
|
parseRequest ("POST " <> show publishCfgRepoLocation <> "/admin/v0/eos-upload")
|
||||||
<&> setRequestHeaders [("accept", "text/plain")]
|
<&> setRequestHeaders [("accept", "text/plain")]
|
||||||
|
<&> setRequestHeaders [("Content-Encoding", "gzip")]
|
||||||
|
<&> setRequestHeaders [("Content-Length", show size)]
|
||||||
<&> applyBasicAuth (B8.pack publishCfgRepoUser) (B8.pack publishCfgRepoPass)
|
<&> applyBasicAuth (B8.pack publishCfgRepoUser) (B8.pack publishCfgRepoPass)
|
||||||
size <- getFileSize img
|
|
||||||
hash <- hashFile @_ @SHA256 img
|
hash <- hashFile @_ @SHA256 img
|
||||||
bar <- newProgressBar defStyle 30 (Progress 0 (fromIntegral size) ())
|
bar <- newProgressBar defStyle 30 (Progress 0 (fromIntegral size) ())
|
||||||
body <- observedStreamFile (updateProgress bar . const . sfs2prog) img
|
body <- observedStreamFile (updateProgress bar . const . sfs2prog) img
|
||||||
let withBody = setRequestBody body noBody
|
let withBody = setRequestBody body noBody
|
||||||
let withQParams = setRequestQueryString [("version", Just $ show version), ("hash", Just $ convertToBase Base16 hash)] withBody
|
let withQParams =
|
||||||
|
setRequestQueryString
|
||||||
|
[("version", Just $ show version), ("hash", Just $ convertToBase Base16 hash)]
|
||||||
|
withBody
|
||||||
manager <- newTlsManager
|
manager <- newTlsManager
|
||||||
res <- runReaderT (httpLbs withQParams) manager
|
res <- runReaderT (httpLbs withQParams) manager
|
||||||
if getResponseStatus res == status200
|
if getResponseStatus res == status200
|
||||||
@@ -598,6 +606,7 @@ eosUpload (EosUpload name img version) = do
|
|||||||
sfs2prog :: StreamFileStatus -> Progress ()
|
sfs2prog :: StreamFileStatus -> Progress ()
|
||||||
sfs2prog StreamFileStatus{..} = Progress (fromIntegral readSoFar) (fromIntegral fileSize) ()
|
sfs2prog StreamFileStatus{..} = Progress (fromIntegral readSoFar) (fromIntegral fileSize) ()
|
||||||
|
|
||||||
|
|
||||||
index :: String -> String -> Version -> IO ()
|
index :: String -> String -> Version -> IO ()
|
||||||
index name pkg v = performHttp name "POST" [i|/admin/v0/index|] (IndexPkgReq (PkgId $ toS pkg) v)
|
index name pkg v = performHttp name "POST" [i|/admin/v0/index|] (IndexPkgReq (PkgId $ toS pkg) v)
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,30 @@
|
|||||||
|
|
||||||
module Handler.Admin where
|
module Handler.Admin where
|
||||||
|
|
||||||
|
import Codec.Archive.Zip.Conduit.Zip (
|
||||||
|
ZipData (ZipDataSource),
|
||||||
|
ZipEntry (
|
||||||
|
ZipEntry,
|
||||||
|
zipEntryExternalAttributes,
|
||||||
|
zipEntryName,
|
||||||
|
zipEntrySize,
|
||||||
|
zipEntryTime
|
||||||
|
),
|
||||||
|
ZipInfo (..),
|
||||||
|
ZipOptions (..),
|
||||||
|
zipStream,
|
||||||
|
)
|
||||||
import Conduit (
|
import Conduit (
|
||||||
|
ConduitT,
|
||||||
|
MonadUnliftIO,
|
||||||
|
fuseBoth,
|
||||||
runConduit,
|
runConduit,
|
||||||
sinkFile,
|
sinkFile,
|
||||||
|
sinkFileBS,
|
||||||
|
yield,
|
||||||
(.|),
|
(.|),
|
||||||
)
|
)
|
||||||
|
import Control.Monad.Extra
|
||||||
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 (
|
import Data.Aeson (
|
||||||
@@ -22,6 +41,7 @@ import Data.Aeson (
|
|||||||
(.:?),
|
(.:?),
|
||||||
(.=),
|
(.=),
|
||||||
)
|
)
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
import Data.HashMap.Internal.Strict (
|
import Data.HashMap.Internal.Strict (
|
||||||
HashMap,
|
HashMap,
|
||||||
differenceWith,
|
differenceWith,
|
||||||
@@ -35,6 +55,7 @@ import Data.List (
|
|||||||
import Data.String.Interpolate.IsString (
|
import Data.String.Interpolate.IsString (
|
||||||
i,
|
i,
|
||||||
)
|
)
|
||||||
|
import Data.Time
|
||||||
import Database.Persist (
|
import Database.Persist (
|
||||||
Entity (entityKey),
|
Entity (entityKey),
|
||||||
PersistStoreRead (get),
|
PersistStoreRead (get),
|
||||||
@@ -64,9 +85,7 @@ import Lib.PkgRepository (
|
|||||||
getPackages,
|
getPackages,
|
||||||
getVersionsFor,
|
getVersionsFor,
|
||||||
)
|
)
|
||||||
import Lib.Types.Core (
|
import Lib.Types.Core (PkgId (unPkgId))
|
||||||
PkgId (unPkgId),
|
|
||||||
)
|
|
||||||
import Lib.Types.Emver (Version (..))
|
import Lib.Types.Emver (Version (..))
|
||||||
import Lib.Types.Manifest (PackageManifest (..))
|
import Lib.Types.Manifest (PackageManifest (..))
|
||||||
import Model (
|
import Model (
|
||||||
@@ -90,28 +109,29 @@ import Settings
|
|||||||
import Startlude (
|
import Startlude (
|
||||||
Applicative (pure),
|
Applicative (pure),
|
||||||
Bool (..),
|
Bool (..),
|
||||||
|
Either (Right),
|
||||||
Eq,
|
Eq,
|
||||||
|
FilePath,
|
||||||
Int,
|
Int,
|
||||||
Maybe (..),
|
Maybe (..),
|
||||||
Monad ((>>=)),
|
|
||||||
Show,
|
Show,
|
||||||
SomeException (..),
|
SomeException (..),
|
||||||
Text,
|
Text,
|
||||||
|
Word64,
|
||||||
asum,
|
asum,
|
||||||
fmap,
|
decodeUtf8,
|
||||||
|
encodeUtf8,
|
||||||
fromMaybe,
|
fromMaybe,
|
||||||
getCurrentTime,
|
|
||||||
guarded,
|
guarded,
|
||||||
hush,
|
hush,
|
||||||
isNothing,
|
isNothing,
|
||||||
liftIO,
|
liftIO,
|
||||||
not,
|
not,
|
||||||
|
readMaybe,
|
||||||
replicate,
|
replicate,
|
||||||
show,
|
show,
|
||||||
toS,
|
toS,
|
||||||
traverse,
|
traverse,
|
||||||
void,
|
|
||||||
when,
|
|
||||||
zip,
|
zip,
|
||||||
($),
|
($),
|
||||||
(&&&),
|
(&&&),
|
||||||
@@ -140,6 +160,7 @@ import Yesod (
|
|||||||
delete,
|
delete,
|
||||||
getsYesod,
|
getsYesod,
|
||||||
logError,
|
logError,
|
||||||
|
lookupHeader,
|
||||||
rawRequestBody,
|
rawRequestBody,
|
||||||
requireCheckJsonBody,
|
requireCheckJsonBody,
|
||||||
runDB,
|
runDB,
|
||||||
@@ -193,16 +214,41 @@ postEosUploadR = do
|
|||||||
hash <- case maybeHash of
|
hash <- case maybeHash of
|
||||||
Nothing -> sendResponseStatus status400 ("Missing Hash" :: Text)
|
Nothing -> sendResponseStatus status400 ("Missing Hash" :: Text)
|
||||||
Just h -> pure h
|
Just h -> pure h
|
||||||
|
(maybeSize :: Maybe Word64) <- maybeM (pure Nothing) (pure . readMaybe . decodeUtf8) (lookupHeader "Content-Length")
|
||||||
resourcesTemp <- getsYesod $ (</> "temp") . resourcesDir . appSettings
|
resourcesTemp <- getsYesod $ (</> "temp") . resourcesDir . appSettings
|
||||||
createDirectoryIfMissing True resourcesTemp
|
createDirectoryIfMissing True resourcesTemp
|
||||||
withTempDirectory resourcesTemp "neweos" $ \dir -> do
|
withTempDirectory resourcesTemp "neweos" $ \dir -> do
|
||||||
let path = dir </> "eos" <.> "img"
|
let path = dir </> "eos" <.> "img"
|
||||||
runConduit $ rawRequestBody .| sinkFile path
|
void . runConduit $ createZipEntry path maybeSize rawRequestBody .| (zipStream zipOptions `fuseBoth` sinkFileBS path)
|
||||||
_ <- runDB $ upsert (EosHash version hash) [EosHashHash =. hash]
|
void . runDB $ upsert (EosHash version hash) [EosHashHash =. hash]
|
||||||
let targetPath = root </> show version
|
let targetPath = root </> show version
|
||||||
removePathForcibly targetPath
|
removePathForcibly targetPath
|
||||||
createDirectoryIfMissing True targetPath
|
createDirectoryIfMissing True targetPath
|
||||||
renameDirectory dir targetPath
|
renameDirectory dir targetPath
|
||||||
|
where
|
||||||
|
zipOptions =
|
||||||
|
ZipOptions
|
||||||
|
{ zipOpt64 = True
|
||||||
|
, zipOptCompressLevel = -1
|
||||||
|
, zipOptInfo = ZipInfo{zipComment = encodeUtf8 "zipped eos image"}
|
||||||
|
}
|
||||||
|
createZipEntry ::
|
||||||
|
(MonadUnliftIO m) =>
|
||||||
|
FilePath ->
|
||||||
|
Maybe Word64 ->
|
||||||
|
ConduitT () ByteString m () ->
|
||||||
|
ConduitT () (ZipEntry, ZipData m) m ()
|
||||||
|
createZipEntry path size dataSource = do
|
||||||
|
utcTime <- liftIO getCurrentTime
|
||||||
|
localTimeZone <- liftIO $ getTimeZone utcTime
|
||||||
|
let zipEntry =
|
||||||
|
ZipEntry
|
||||||
|
{ zipEntryName = Right $ encodeUtf8 $ show path
|
||||||
|
, zipEntryTime = utcToLocalTime localTimeZone utcTime
|
||||||
|
, zipEntrySize = size
|
||||||
|
, zipEntryExternalAttributes = Nothing
|
||||||
|
}
|
||||||
|
yield (zipEntry, ZipDataSource dataSource)
|
||||||
|
|
||||||
|
|
||||||
data IndexPkgReq = IndexPkgReq
|
data IndexPkgReq = IndexPkgReq
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ getVersionSpecFromQuery = do
|
|||||||
Nothing -> sendResponseStatus status400 ("Invalid App Version Specification" :: Text)
|
Nothing -> sendResponseStatus status400 ("Invalid App Version Specification" :: Text)
|
||||||
Just t -> pure t
|
Just t -> pure t
|
||||||
|
|
||||||
|
|
||||||
getVersionFromQuery :: MonadHandler m => m (Maybe Version)
|
getVersionFromQuery :: MonadHandler m => m (Maybe Version)
|
||||||
getVersionFromQuery = do
|
getVersionFromQuery = do
|
||||||
versionString <- lookupGetParam "version"
|
versionString <- lookupGetParam "version"
|
||||||
@@ -106,9 +107,11 @@ getVersionFromQuery = do
|
|||||||
Nothing -> sendResponseStatus status400 ("Invalid Version" :: Text)
|
Nothing -> sendResponseStatus status400 ("Invalid Version" :: Text)
|
||||||
Just t -> pure (Just t)
|
Just t -> pure (Just t)
|
||||||
|
|
||||||
|
|
||||||
getHashFromQuery :: MonadHandler m => m (Maybe Text)
|
getHashFromQuery :: MonadHandler m => m (Maybe Text)
|
||||||
getHashFromQuery = lookupGetParam "hash"
|
getHashFromQuery = lookupGetParam "hash"
|
||||||
|
|
||||||
|
|
||||||
versionPriorityFromQueryIsMin :: MonadHandler m => m Bool
|
versionPriorityFromQueryIsMin :: MonadHandler m => m Bool
|
||||||
versionPriorityFromQueryIsMin = do
|
versionPriorityFromQueryIsMin = do
|
||||||
priorityString <- lookupGetParam "version-priority"
|
priorityString <- lookupGetParam "version-priority"
|
||||||
|
|||||||
Reference in New Issue
Block a user