-------------------------------------------------------------------------------
-- | Environment configuration
-------------------------------------------------------------------------------
data Env = Env
{ ip :: String
, rawMode :: Maybe RawMode
}
do
let dir = takeDirectory path
withTempFile dir "env.tmp" $ \tmpPath tmpHandle -> do
hPutStrLn tmpHandle (content)
hClose tmpHandle
renameFile tmpPath path -- atomic replacement
do
write file (
"SERVER_IP=" ++ ip env ++ "\n"
++ "UDP2RAW_PWD=" ++ fromMaybe "" udp2raw_pwd ++ "\n"
++ "UDPSPEEDER_PWD=" ++ fromMaybe "" udpspeeder_pwd ++ "\n"
++ "RAW_MODE=" ++ maybe "" rawModeToArg (rawMode env) ++ "\n"
++ "PORT=" ++ show (fromMaybe 443 (rawMode env >>= getPort)))
-------------------------------------------------------------------------------
-- | Load and parse the .env file
-------------------------------------------------------------------------------
catch (do
content <- readFile path
let kvs = parseEnv content
mIP = lookup "SERVER_IP" kvs
mMode = lookup "RAW_MODE" kvs
mPort = lookup "PORT" kvs
case (mIP, mMode) of
(Just ipStr, Just modeStr)->pure $ Right $ Env ipStr (parseRawMode modeStr mPort)
(Just ipStr, _)-> pure $ Right $ Env ipStr Nothing
_ -> pure $ Left "Missing SERVER_IP or RAW_MODE in .env"
) handleReadError
where
pure $ Left $ "Error reading .env: " ++ show e
-------------------------------------------------------------------------------
-- | Parse RAW_MODE + optional PORT into RawMode
-------------------------------------------------------------------------------
let mode = map toUpper modeStr
port = mPort >>= readMaybe
in case mode of
"ICMP" -> Just ICMP
"UDP" -> UDP <$> port -- mandatory port
"FAKETCP" -> FakeTCP <$> port -- mandatory port
_ -> Nothing
-------------------------------------------------------------------------------
-- | Parse .env content into key-value pairs
-------------------------------------------------------------------------------
mapMaybe parseLine . lines
where
parseLine line =
case break (== '=') (trim line) of
(key, '=':value)
| not (null key) && not (isComment key) ->
Just (trim key, trim value)
_ -> Nothing
isComment ('#':_) = True
isComment _ = False
trim = f . f
where f = reverse . dropWhile (`elem` [' ', '\t', '\r'])
-- | Helper: mapMaybe for base < 4.8
foldr (\x acc -> case f x of
Just y -> y:acc
Nothing -> acc) []