optional lan

This commit is contained in:
Keagan McClelland
2021-03-05 12:14:13 -07:00
parent a21bd91460
commit adab9e7fca
4 changed files with 120 additions and 34 deletions

View File

@@ -7,29 +7,29 @@
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
module Handler.Apps where module Handler.Apps where
import Startlude hiding ( modify import Startlude hiding ( Reader
, execState
, asks , asks
, Reader
, runReader
, catchError , catchError
, forkFinally
, empty , empty
, execState
, forkFinally
, modify
, runReader
) )
import Control.Carrier.Reader
import Control.Carrier.Error.Church import Control.Carrier.Error.Church
import Control.Carrier.Lift import Control.Carrier.Lift
import Control.Carrier.Reader
import qualified Control.Concurrent.Async.Lifted import qualified Control.Concurrent.Async.Lifted
as LAsync as LAsync
import qualified Control.Concurrent.Lifted as Lifted import qualified Control.Concurrent.Lifted as Lifted
import qualified Control.Exception.Lifted as Lifted
import Control.Concurrent.STM.TVar import Control.Concurrent.STM.TVar
import Control.Effect.Empty hiding ( guard ) import Control.Effect.Empty hiding ( guard )
import Control.Effect.Labelled ( HasLabelled import Control.Effect.Labelled ( HasLabelled
, Labelled , Labelled
, runLabelled , runLabelled
) )
import qualified Control.Exception.Lifted as Lifted
import Control.Lens hiding ( (??) ) import Control.Lens hiding ( (??) )
import Control.Monad.Logger import Control.Monad.Logger
import Control.Monad.Trans.Control ( MonadBaseControl ) import Control.Monad.Trans.Control ( MonadBaseControl )
@@ -38,13 +38,13 @@ import Data.Aeson
import Data.Aeson.Lens import Data.Aeson.Lens
import Data.Aeson.Types ( parseMaybe ) import Data.Aeson.Types ( parseMaybe )
import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy as LBS
import Data.IORef
import qualified Data.HashMap.Lazy as HML import qualified Data.HashMap.Lazy as HML
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import Data.Singletons import Data.Singletons
import Data.Singletons.Prelude.Bool ( SBool(..) import Data.Singletons.Prelude.Bool ( If
, If , SBool(..)
) )
import Data.Singletons.Prelude.List ( Elem ) import Data.Singletons.Prelude.List ( Elem )
import qualified Data.Text as Text import qualified Data.Text as Text
@@ -55,8 +55,8 @@ import Exinst
import Network.HTTP.Types import Network.HTTP.Types
import qualified Network.JSONRPC as JSONRPC import qualified Network.JSONRPC as JSONRPC
import Yesod.Core.Content import Yesod.Core.Content
import Yesod.Core.Json
import Yesod.Core.Handler hiding ( cached ) import Yesod.Core.Handler hiding ( cached )
import Yesod.Core.Json
import Yesod.Core.Types ( JSONResponse(..) ) import Yesod.Core.Types ( JSONResponse(..) )
import Yesod.Persist.Core import Yesod.Persist.Core
@@ -70,9 +70,9 @@ import qualified Lib.Algebra.Domain.AppMgr as AppMgr2
import Lib.Algebra.State.RegistryUrl import Lib.Algebra.State.RegistryUrl
import Lib.Background import Lib.Background
import Lib.Error import Lib.Error
import qualified Lib.External.AppManifest as AppManifest
import qualified Lib.External.AppMgr as AppMgr import qualified Lib.External.AppMgr as AppMgr
import qualified Lib.External.Registry as Reg import qualified Lib.External.Registry as Reg
import qualified Lib.External.AppManifest as AppManifest
import Lib.IconCache import Lib.IconCache
import qualified Lib.Notifications as Notifications import qualified Lib.Notifications as Notifications
import Lib.SystemPaths import Lib.SystemPaths
@@ -249,19 +249,28 @@ getInstalledAppsLogic = do
, appInstalledPreviewVersionInstalled = storeAppVersionInfoVersion , appInstalledPreviewVersionInstalled = storeAppVersionInfoVersion
, appInstalledPreviewTorAddress = Nothing , appInstalledPreviewTorAddress = Nothing
, appInstalledPreviewLanAddress = Nothing , appInstalledPreviewLanAddress = Nothing
, appInstalledPreviewUi = False , appInstalledPreviewTorUi = False
, appInstalledPreviewLanUi = False
} }
installedPreviews = flip installedPreviews = flip
HML.mapWithKey HML.mapWithKey
remapped remapped
\appId (s, v, AppMgr2.InfoRes {..}) -> \appId (s, v, AppMgr2.InfoRes {..}) ->
let lanAddress = LanAddress . (".onion" `Text.replace` ".local") . unTorAddress <$> infoResTorAddress let
mLanAddress = do -- Maybe
addrBase <- infoResTorAddress
let
lanConfs = mapMaybe AppManifest.portMapEntryLan
$ AppManifest.appManifestPortMapping infoResManifest
guard (not . null $ lanConfs)
pure $ LanAddress . (".onion" `Text.replace` ".local") . unTorAddress $ addrBase
in AppInstalledPreview { appInstalledPreviewBase = AppBase appId infoResTitle (iconUrl appId v) in AppInstalledPreview { appInstalledPreviewBase = AppBase appId infoResTitle (iconUrl appId v)
, appInstalledPreviewStatus = s , appInstalledPreviewStatus = s
, appInstalledPreviewVersionInstalled = v , appInstalledPreviewVersionInstalled = v
, appInstalledPreviewTorAddress = infoResTorAddress , appInstalledPreviewTorAddress = infoResTorAddress
, appInstalledPreviewLanAddress = lanAddress , appInstalledPreviewLanAddress = mLanAddress
, appInstalledPreviewUi = AppManifest.uiAvailable infoResManifest , appInstalledPreviewTorUi = AppManifest.torUiAvailable infoResManifest
, appInstalledPreviewLanUi = AppManifest.lanUiAvailable infoResManifest
} }
pure $ HML.elems $ HML.union installingPreviews installedPreviews pure $ HML.elems $ HML.union installingPreviews installedPreviews

View File

@@ -47,7 +47,8 @@ data AppInstalledPreview = AppInstalledPreview
, appInstalledPreviewVersionInstalled :: Version , appInstalledPreviewVersionInstalled :: Version
, appInstalledPreviewTorAddress :: Maybe TorAddress , appInstalledPreviewTorAddress :: Maybe TorAddress
, appInstalledPreviewLanAddress :: Maybe LanAddress , appInstalledPreviewLanAddress :: Maybe LanAddress
, appInstalledPreviewUi :: Bool , appInstalledPreviewTorUi :: Bool
, appInstalledPreviewLanUi :: Bool
} }
deriving (Eq, Show) deriving (Eq, Show)
instance ToJSON AppInstalledPreview where instance ToJSON AppInstalledPreview where
@@ -56,7 +57,8 @@ instance ToJSON AppInstalledPreview where
, "versionInstalled" .= appInstalledPreviewVersionInstalled , "versionInstalled" .= appInstalledPreviewVersionInstalled
, "torAddress" .= (unTorAddress <$> appInstalledPreviewTorAddress) , "torAddress" .= (unTorAddress <$> appInstalledPreviewTorAddress)
, "lanAddress" .= (unLanAddress <$> appInstalledPreviewLanAddress) , "lanAddress" .= (unLanAddress <$> appInstalledPreviewLanAddress)
, "ui" .= appInstalledPreviewUi , "torUi" .= appInstalledPreviewTorUi
, "lanUi" .= appInstalledPreviewLanUi
] ]
data InstallNewAppReq = InstallNewAppReq data InstallNewAppReq = InstallNewAppReq

View File

@@ -9,12 +9,12 @@ import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import qualified Data.Yaml as Yaml import qualified Data.Yaml as Yaml
import Control.Monad.Fail ( MonadFail(fail) )
import Lib.Error import Lib.Error
import Lib.SystemPaths import Lib.SystemPaths
import Lib.Types.Core import Lib.Types.Core
import Lib.Types.Emver import Lib.Types.Emver
import Lib.Types.Emver.Orphans ( ) import Lib.Types.Emver.Orphans ( )
import Control.Monad.Fail ( MonadFail(fail) )
data ImageType = ImageTypeTar data ImageType = ImageTypeTar
deriving (Eq, Show) deriving (Eq, Show)
@@ -54,6 +54,7 @@ data Action = Action
, actionWarning :: Maybe Text , actionWarning :: Maybe Text
, actionAllowedStatuses :: [AppContainerStatus] , actionAllowedStatuses :: [AppContainerStatus]
} }
deriving Show
instance FromJSON Action where instance FromJSON Action where
parseJSON = withObject "AppAction" $ \o -> do parseJSON = withObject "AppAction" $ \o -> do
actionId <- o .: "id" actionId <- o .: "id"
@@ -80,7 +81,7 @@ data AppManifest where
, appManifestDescShort :: Text , appManifestDescShort :: Text
, appManifestDescLong :: Text , appManifestDescLong :: Text
, appManifestReleaseNotes :: Text , appManifestReleaseNotes :: Text
, appManifestPortMapping :: HM.HashMap Word16 Word16 , appManifestPortMapping :: [PortMapEntry]
, appManifestImageType :: ImageType , appManifestImageType :: ImageType
, appManifestMount :: FilePath , appManifestMount :: FilePath
, appManifestAssets :: [AssetMapping] , appManifestAssets :: [AssetMapping]
@@ -91,9 +92,16 @@ data AppManifest where
, appManifestStartAlert :: Maybe Text , appManifestStartAlert :: Maybe Text
, appManifestActions :: [Action] , appManifestActions :: [Action]
} -> AppManifest } -> AppManifest
deriving instance Show AppManifest
uiAvailable :: AppManifest -> Bool torUiAvailable :: AppManifest -> Bool
uiAvailable AppManifest {..} = isJust $ HM.lookup 80 appManifestPortMapping torUiAvailable AppManifest {..} = any (== 80) $ portMapEntryTor <$> appManifestPortMapping
lanUiAvailable :: AppManifest -> Bool
lanUiAvailable AppManifest {..} = any id $ fmap portMapEntryLan appManifestPortMapping <&> \case
Just Standard -> True
Just (Custom 443) -> True
_ -> False
instance FromJSON AppManifest where instance FromJSON AppManifest where
parseJSON = withObject "App Manifest " $ \o -> do parseJSON = withObject "App Manifest " $ \o -> do
@@ -103,7 +111,7 @@ instance FromJSON AppManifest where
appManifestDescShort <- o .: "description" >>= (.: "short") appManifestDescShort <- o .: "description" >>= (.: "short")
appManifestDescLong <- o .: "description" >>= (.: "long") appManifestDescLong <- o .: "description" >>= (.: "long")
appManifestReleaseNotes <- o .: "release-notes" appManifestReleaseNotes <- o .: "release-notes"
appManifestPortMapping <- o .: "ports" >>= fmap HM.fromList . traverse parsePortMapping appManifestPortMapping <- o .: "ports"
appManifestImageType <- o .: "image" >>= (.: "type") appManifestImageType <- o .: "image" >>= (.: "type")
appManifestMount <- o .: "mount" appManifestMount <- o .: "mount"
appManifestAssets <- o .: "assets" >>= traverse parseJSON appManifestAssets <- o .: "assets" >>= traverse parseJSON
@@ -114,12 +122,31 @@ instance FromJSON AppManifest where
appManifestStartAlert <- o .:? "start-alert" appManifestStartAlert <- o .:? "start-alert"
appManifestActions <- o .: "actions" appManifestActions <- o .: "actions"
pure $ AppManifest { .. } pure $ AppManifest { .. }
where where parseDepInfo = withObject "Dep Info" $ (.: "version")
parsePortMapping = withObject "Port Mapping" $ \o -> liftA2 (,) (o .: "tor") (o .: "internal")
parseDepInfo = withObject "Dep Info" $ (.: "version")
getAppManifest :: (MonadIO m, HasFilesystemBase sig m) => AppId -> S9ErrT m (Maybe AppManifest) getAppManifest :: (MonadIO m, HasFilesystemBase sig m) => AppId -> S9ErrT m (Maybe AppManifest)
getAppManifest appId = do getAppManifest appId = do
base <- ask @"filesystemBase" base <- ask @"filesystemBase"
ExceptT $ first (ManifestParseE appId) <$> liftIO ExceptT $ first (ManifestParseE appId) <$> liftIO
(Yaml.decodeFileEither . toS $ (appMgrAppPath appId <> "manifest.yaml") `relativeTo` base) (Yaml.decodeFileEither . toS $ (appMgrAppPath appId <> "manifest.yaml") `relativeTo` base)
data LanConfiguration = Standard | Custom Word16 deriving (Eq, Show)
instance FromJSON LanConfiguration where
parseJSON = liftA2 (<|>) standard custom
where
standard =
withText "Standard Lan" \t -> if t == "standard" then pure Standard else fail "Not Standard Lan Conf"
custom = withObject "Custom Lan" $ \o -> do
Custom <$> o .: "port"
data PortMapEntry = PortMapEntry
{ portMapEntryInternal :: Word16
, portMapEntryTor :: Word16
, portMapEntryLan :: Maybe LanConfiguration
}
deriving (Eq, Show)
instance FromJSON PortMapEntry where
parseJSON = withObject "Port Map Entry" $ \o -> do
portMapEntryInternal <- o .: "internal"
portMapEntryTor <- o .: "tor"
portMapEntryLan <- o .:? "lan"
pure PortMapEntry { .. }

View File

@@ -66,12 +66,60 @@ assets:
hidden-service-version: v3 hidden-service-version: v3
|] |]
mastodon330Manifest :: ByteString
mastodon330Manifest = [i|
---
id: mastodon
version: 3.3.0.1
title: Mastodon
description:
short: "A free, open-source social network server."
long: "Mastodon is a free, open-source social network server based on ActivityPub where users can follow friends and discover new ones. On Mastodon, users can publish anything they want: links, pictures, text, video. All Mastodon servers are interoperable as a federated network (users on one server can seamlessly communicate with users from another one, including non-Mastodon software that implements ActivityPub)!"
release-notes: Added an acation to reset the admin password
install-alert: "After starting mastodon for the first time, it can take a long time (several minutes) to be ready.\nPlease be patient. On future starts of the service, it will be faster, but still takes longer than other services.\nMake sure to sign up for a user before giving out your link. The first user to sign up is set as the admin user.\n"
uninstall-alert: ~
restore-alert: ~
start-alert: "It may take several minutes after startup for this service to be ready for use.\n"
has-instructions: true
os-version-required: ">=0.2.8"
os-version-recommended: ">=0.2.8"
ports:
- internal: 80
tor: 80
lan: standard
- internal: 3000
tor: 3000
lan: ~
- internal: 4000
tor: 4000
lan: ~
image:
type: tar
shm-size-mb: ~
mount: /root/persistence
public: ~
shared: ~
assets: []
hidden-service-version: v3
dependencies: {}
actions:
- id: reset-admin-password
name: Reset Admin Password
description: This action will reset your admin password to a random value
allowed-statuses:
- RUNNING
command:
- docker_entrypoint.sh
- reset_admin_password.sh
|]
spec :: Spec spec :: Spec
spec = do spec = do
describe "parsing app manifest ports" $ do describe "parsing app manifest ports" $ do
it "should yield true for cups 0.2.3" $ do it "should parse mastodon 3.3.0" $ do
res <- decodeThrow @IO @AppManifest cups023Manifest res <- decodeThrow @IO @AppManifest mastodon330Manifest
uiAvailable res `shouldBe` True print res
it "should yield false for cups 0.2.3 Mod" $ do lanUiAvailable res `shouldBe` True
res <- decodeThrow @IO @AppManifest cups023ManifestModNoUI torUiAvailable res `shouldBe` True
uiAvailable res `shouldBe` False