mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-10-22 21:52:51 +00:00
Merge branch 'master' into rcv-services
This commit is contained in:
41 files changed
+108
-62
No files matched your search
@@ -0,0 +1,15 @@
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
|
||||
module Web.Embedded where
|
||||
|
||||
import Data.FileEmbed (embedDir, embedFile)
|
||||
import Simplex.Messaging.Server.Web (EmbeddedContent (..))
|
||||
|
||||
embeddedContent :: EmbeddedContent
|
||||
embeddedContent =
|
||||
EmbeddedContent
|
||||
{ indexHtml = $(embedFile "apps/common/Web/static/index.html"),
|
||||
linkHtml = $(embedFile "apps/common/Web/static/link.html"),
|
||||
mediaContent = $(embedDir "apps/common/Web/static/media/"),
|
||||
wellKnown = $(embedDir "apps/common/Web/static/.well-known/")
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -8,23 +8,23 @@ module SMPWeb
|
||||
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.String (fromString)
|
||||
import Web.Embedded (embeddedContent)
|
||||
import Simplex.Messaging.Encoding.String (strEncode)
|
||||
import Simplex.Messaging.Server.Information
|
||||
import Simplex.Messaging.Server.Main (simplexmqSource)
|
||||
import qualified Simplex.Messaging.Server.Web as Web
|
||||
import Simplex.Messaging.Server.Web (render, serverInfoSubsts, timedTTLText)
|
||||
import Simplex.Messaging.Server.Web.Embedded as E
|
||||
import Simplex.Messaging.Transport.Client (TransportHost (..))
|
||||
|
||||
smpGenerateSite :: ServerInformation -> Maybe TransportHost -> FilePath -> IO ()
|
||||
smpGenerateSite si onionHost path =
|
||||
Web.generateSite (serverInformation si onionHost) smpLinkPages path
|
||||
Web.generateSite embeddedContent (serverInformation si onionHost) smpLinkPages path
|
||||
|
||||
smpLinkPages :: [String]
|
||||
smpLinkPages = ["contact", "invitation", "a", "c", "g", "r", "i"]
|
||||
|
||||
serverInformation :: ServerInformation -> Maybe TransportHost -> ByteString
|
||||
serverInformation ServerInformation {config, information} onionHost = render E.indexHtml substs
|
||||
serverInformation ServerInformation {config, information} onionHost = render (Web.indexHtml embeddedContent) substs
|
||||
where
|
||||
substs = [("smpConfig", Just "y"), ("xftpConfig", Nothing)] <> substConfig <> serverInfoSubsts simplexmqSource information <> [("onionHost", strEncode <$> onionHost), ("iniFileName", Just "smp-server.ini")]
|
||||
substConfig =
|
||||
|
||||
@@ -9,6 +9,7 @@ module XFTPWeb
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Maybe (isJust)
|
||||
import Data.String (fromString)
|
||||
import Web.Embedded (embeddedContent)
|
||||
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..))
|
||||
import Simplex.Messaging.Encoding.String (strEncode)
|
||||
import Simplex.Messaging.Server.Expiration (ExpirationConfig (..))
|
||||
@@ -16,15 +17,14 @@ import Simplex.Messaging.Server.Information (ServerPublicInfo)
|
||||
import Simplex.Messaging.Server.Main (simplexmqSource)
|
||||
import qualified Simplex.Messaging.Server.Web as Web
|
||||
import Simplex.Messaging.Server.Web (render, serverInfoSubsts, timedTTLText)
|
||||
import Simplex.Messaging.Server.Web.Embedded as E
|
||||
import Simplex.Messaging.Transport.Client (TransportHost (..))
|
||||
|
||||
xftpGenerateSite :: XFTPServerConfig -> Maybe ServerPublicInfo -> Maybe TransportHost -> FilePath -> IO ()
|
||||
xftpGenerateSite cfg info onionHost path =
|
||||
Web.generateSite (xftpServerInformation cfg info onionHost) [] path
|
||||
Web.generateSite embeddedContent (xftpServerInformation cfg info onionHost) [] path
|
||||
|
||||
xftpServerInformation :: XFTPServerConfig -> Maybe ServerPublicInfo -> Maybe TransportHost -> ByteString
|
||||
xftpServerInformation XFTPServerConfig {fileExpiration, logStatsInterval, allowNewFiles, newFileBasicAuth} information onionHost = render E.indexHtml substs
|
||||
xftpServerInformation XFTPServerConfig {fileExpiration, logStatsInterval, allowNewFiles, newFileBasicAuth} information onionHost = render (Web.indexHtml embeddedContent) substs
|
||||
where
|
||||
substs = [("smpConfig", Nothing), ("xftpConfig", Just "y")] <> substConfig <> serverInfoSubsts simplexmqSource information <> [("onionHost", strEncode <$> onionHost), ("iniFileName", Just "file-server.ini")]
|
||||
substConfig =
|
||||
|
||||
+36
-29
@@ -24,33 +24,33 @@ extra-source-files:
|
||||
CHANGELOG.md
|
||||
cbits/sha512.h
|
||||
cbits/sntrup761.h
|
||||
src/Simplex/Messaging/Server/Web/index.html
|
||||
src/Simplex/Messaging/Server/Web/link.html
|
||||
src/Simplex/Messaging/Server/Web/media/apk_icon.png
|
||||
src/Simplex/Messaging/Server/Web/media/apple_store.svg
|
||||
src/Simplex/Messaging/Server/Web/media/contact.js
|
||||
src/Simplex/Messaging/Server/Web/media/contact_page_mobile.png
|
||||
src/Simplex/Messaging/Server/Web/media/f_droid.svg
|
||||
src/Simplex/Messaging/Server/Web/media/favicon.ico
|
||||
src/Simplex/Messaging/Server/Web/media/GilroyBold.woff2
|
||||
src/Simplex/Messaging/Server/Web/media/GilroyLight.woff2
|
||||
src/Simplex/Messaging/Server/Web/media/GilroyMedium.woff2
|
||||
src/Simplex/Messaging/Server/Web/media/GilroyRegular.woff2
|
||||
src/Simplex/Messaging/Server/Web/media/GilroyRegularItalic.woff2
|
||||
src/Simplex/Messaging/Server/Web/media/google_play.svg
|
||||
src/Simplex/Messaging/Server/Web/media/logo-dark.png
|
||||
src/Simplex/Messaging/Server/Web/media/logo-light.png
|
||||
src/Simplex/Messaging/Server/Web/media/logo-symbol-dark.svg
|
||||
src/Simplex/Messaging/Server/Web/media/logo-symbol-light.svg
|
||||
src/Simplex/Messaging/Server/Web/media/moon.svg
|
||||
src/Simplex/Messaging/Server/Web/media/qrcode.js
|
||||
src/Simplex/Messaging/Server/Web/media/script.js
|
||||
src/Simplex/Messaging/Server/Web/media/style.css
|
||||
src/Simplex/Messaging/Server/Web/media/sun.svg
|
||||
src/Simplex/Messaging/Server/Web/media/swiper-bundle.min.css
|
||||
src/Simplex/Messaging/Server/Web/media/swiper-bundle.min.js
|
||||
src/Simplex/Messaging/Server/Web/media/tailwind.css
|
||||
src/Simplex/Messaging/Server/Web/media/testflight.png
|
||||
apps/common/Web/static/index.html
|
||||
apps/common/Web/static/link.html
|
||||
apps/common/Web/static/media/apk_icon.png
|
||||
apps/common/Web/static/media/apple_store.svg
|
||||
apps/common/Web/static/media/contact.js
|
||||
apps/common/Web/static/media/contact_page_mobile.png
|
||||
apps/common/Web/static/media/f_droid.svg
|
||||
apps/common/Web/static/media/favicon.ico
|
||||
apps/common/Web/static/media/GilroyBold.woff2
|
||||
apps/common/Web/static/media/GilroyLight.woff2
|
||||
apps/common/Web/static/media/GilroyMedium.woff2
|
||||
apps/common/Web/static/media/GilroyRegular.woff2
|
||||
apps/common/Web/static/media/GilroyRegularItalic.woff2
|
||||
apps/common/Web/static/media/google_play.svg
|
||||
apps/common/Web/static/media/logo-dark.png
|
||||
apps/common/Web/static/media/logo-light.png
|
||||
apps/common/Web/static/media/logo-symbol-dark.svg
|
||||
apps/common/Web/static/media/logo-symbol-light.svg
|
||||
apps/common/Web/static/media/moon.svg
|
||||
apps/common/Web/static/media/qrcode.js
|
||||
apps/common/Web/static/media/script.js
|
||||
apps/common/Web/static/media/style.css
|
||||
apps/common/Web/static/media/sun.svg
|
||||
apps/common/Web/static/media/swiper-bundle.min.css
|
||||
apps/common/Web/static/media/swiper-bundle.min.js
|
||||
apps/common/Web/static/media/tailwind.css
|
||||
apps/common/Web/static/media/testflight.png
|
||||
|
||||
flag swift
|
||||
description: Enable swift JSON format
|
||||
@@ -249,7 +249,6 @@ library
|
||||
Simplex.Messaging.Server.Main.GitCommit
|
||||
Simplex.Messaging.Server.Main.Init
|
||||
Simplex.Messaging.Server.Web
|
||||
Simplex.Messaging.Server.Web.Embedded
|
||||
Simplex.Messaging.Server.MsgStore
|
||||
Simplex.Messaging.Server.MsgStore.Journal
|
||||
Simplex.Messaging.Server.MsgStore.Journal.SharedLock
|
||||
@@ -345,7 +344,6 @@ library
|
||||
if !flag(client_library)
|
||||
build-depends:
|
||||
case-insensitive ==1.2.*
|
||||
, file-embed >=0.0.10 && <0.1
|
||||
, hashable ==1.4.*
|
||||
, ini ==0.4.1
|
||||
, optparse-applicative >=0.15 && <0.17
|
||||
@@ -412,15 +410,18 @@ executable smp-server
|
||||
main-is: Main.hs
|
||||
other-modules:
|
||||
SMPWeb
|
||||
Web.Embedded
|
||||
Paths_simplexmq
|
||||
hs-source-dirs:
|
||||
apps/smp-server
|
||||
apps/common
|
||||
default-extensions:
|
||||
StrictData
|
||||
ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=incomplete-uni-patterns -Werror=missing-methods -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -O2 -threaded -rtsopts
|
||||
build-depends:
|
||||
base
|
||||
, bytestring
|
||||
, file-embed >=0.0.10 && <0.1
|
||||
, simple-logger
|
||||
, simplexmq
|
||||
, text
|
||||
@@ -448,15 +449,18 @@ executable xftp-server
|
||||
main-is: Main.hs
|
||||
other-modules:
|
||||
XFTPWeb
|
||||
Web.Embedded
|
||||
Paths_simplexmq
|
||||
hs-source-dirs:
|
||||
apps/xftp-server
|
||||
apps/common
|
||||
default-extensions:
|
||||
StrictData
|
||||
ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=incomplete-uni-patterns -Werror=missing-methods -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -O2 -threaded -rtsopts
|
||||
build-depends:
|
||||
base
|
||||
, bytestring
|
||||
, file-embed >=0.0.10 && <0.1
|
||||
, simple-logger
|
||||
, simplexmq
|
||||
default-language: Haskell2010
|
||||
@@ -502,6 +506,7 @@ test-suite simplexmq-test
|
||||
XFTPWebTests
|
||||
SMPWeb
|
||||
XFTPWeb
|
||||
Web.Embedded
|
||||
Paths_simplexmq
|
||||
if flag(client_postgres)
|
||||
other-modules:
|
||||
@@ -522,6 +527,7 @@ test-suite simplexmq-test
|
||||
tests
|
||||
apps/smp-server
|
||||
apps/xftp-server
|
||||
apps/common
|
||||
default-extensions:
|
||||
StrictData
|
||||
-- add -fhpc to ghc-options to run tests with coverage
|
||||
@@ -539,6 +545,7 @@ test-suite simplexmq-test
|
||||
, crypton-x509-store
|
||||
, crypton-x509-validation
|
||||
, directory
|
||||
, file-embed >=0.0.10 && <0.1
|
||||
, filepath
|
||||
, generic-random ==1.5.*
|
||||
, hashable
|
||||
|
||||
@@ -18,6 +18,7 @@ module Simplex.Messaging.Parsers
|
||||
sumTypeJSON,
|
||||
taggedObjectJSON,
|
||||
singleFieldJSON,
|
||||
singleFieldJSON_,
|
||||
defaultJSON,
|
||||
textP,
|
||||
pattern SingleFieldJSONTag,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
module Simplex.Messaging.Server.Web
|
||||
( EmbeddedWebParams (..),
|
||||
WebHttpsParams (..),
|
||||
EmbeddedContent (..),
|
||||
serveStaticFiles,
|
||||
attachStaticFiles,
|
||||
serveStaticPageH2,
|
||||
@@ -41,7 +42,6 @@ import Simplex.Messaging.Encoding.String (strEncode)
|
||||
import Simplex.Messaging.Server (AttachHTTP)
|
||||
import Simplex.Messaging.Server.CLI (simplexmqCommit)
|
||||
import Simplex.Messaging.Server.Information
|
||||
import Simplex.Messaging.Server.Web.Embedded as E
|
||||
import Simplex.Messaging.Transport (simplexMQVersion)
|
||||
import Simplex.Messaging.Util (ifM, tshow)
|
||||
import System.Directory (canonicalizePath, createDirectoryIfMissing, doesFileExist)
|
||||
@@ -62,6 +62,13 @@ data WebHttpsParams = WebHttpsParams
|
||||
key :: FilePath
|
||||
}
|
||||
|
||||
data EmbeddedContent = EmbeddedContent
|
||||
{ indexHtml :: ByteString,
|
||||
linkHtml :: ByteString,
|
||||
mediaContent :: [(FilePath, ByteString)],
|
||||
wellKnown :: [(FilePath, ByteString)]
|
||||
}
|
||||
|
||||
serveStaticFiles :: EmbeddedWebParams -> IO ()
|
||||
serveStaticFiles EmbeddedWebParams {webStaticPath, webHttpPort, webHttpsParams} = do
|
||||
forM_ webHttpPort $ \port -> flip forkFinally (\e -> logError $ "HTTP server crashed: " <> tshow e) $ do
|
||||
@@ -118,14 +125,14 @@ staticFiles root = S.staticApp settings . changeWellKnownPath
|
||||
_ -> req
|
||||
pfxLen = B.length "/.well-known/"
|
||||
|
||||
generateSite :: ByteString -> [String] -> FilePath -> IO ()
|
||||
generateSite indexContent linkPages sitePath = do
|
||||
generateSite :: EmbeddedContent -> ByteString -> [String] -> FilePath -> IO ()
|
||||
generateSite embedded indexContent linkPages sitePath = do
|
||||
createDirectoryIfMissing True sitePath
|
||||
B.writeFile (sitePath </> "index.html") indexContent
|
||||
copyDir "media" E.mediaContent
|
||||
copyDir "media" $ mediaContent embedded
|
||||
-- `.well-known` path is re-written in changeWellKnownPath,
|
||||
-- staticApp does not allow hidden folders.
|
||||
copyDir "well-known" E.wellKnown
|
||||
copyDir "well-known" $ wellKnown embedded
|
||||
forM_ linkPages createLinkPage
|
||||
logInfo $ "Generated static site contents at " <> tshow sitePath
|
||||
where
|
||||
@@ -134,7 +141,7 @@ generateSite indexContent linkPages sitePath = do
|
||||
forM_ content $ \(path, s) -> B.writeFile (sitePath </> dir </> path) s
|
||||
createLinkPage path = do
|
||||
createDirectoryIfMissing True $ sitePath </> path
|
||||
B.writeFile (sitePath </> path </> "index.html") E.linkHtml
|
||||
B.writeFile (sitePath </> path </> "index.html") $ linkHtml embedded
|
||||
|
||||
-- | Serve static files via HTTP/2 directly (without WAI).
|
||||
-- Path traversal protection: resolved path must stay under canonicalRoot.
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
|
||||
module Simplex.Messaging.Server.Web.Embedded where
|
||||
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.FileEmbed (embedDir, embedFile)
|
||||
|
||||
indexHtml :: ByteString
|
||||
indexHtml = $(embedFile "src/Simplex/Messaging/Server/Web/index.html")
|
||||
|
||||
linkHtml :: ByteString
|
||||
linkHtml = $(embedFile "src/Simplex/Messaging/Server/Web/link.html")
|
||||
|
||||
mediaContent :: [(FilePath, ByteString)]
|
||||
mediaContent = $(embedDir "src/Simplex/Messaging/Server/Web/media/")
|
||||
|
||||
wellKnown :: [(FilePath, ByteString)]
|
||||
wellKnown = $(embedDir "src/Simplex/Messaging/Server/Web/.well-known/")
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@simplex-chat/xftp-web",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"description": "XFTP file transfer protocol client for web/browser environments",
|
||||
"license": "AGPL-3.0-only",
|
||||
"repository": {
|
||||
|
||||
@@ -52,3 +52,23 @@ export function parseXFTPServer(address: string): XFTPServer {
|
||||
export function formatXFTPServer(srv: XFTPServer): string {
|
||||
return "xftp://" + base64urlEncode(srv.keyHash) + "@" + srv.host + ":" + srv.port
|
||||
}
|
||||
|
||||
// Extract unique XFTP servers referenced in a file description's chunk replicas.
|
||||
export function getDescriptionServers(fd: {chunks: {replicas: {server: string}[]}[]}): XFTPServer[] {
|
||||
const seen = new Set<string>()
|
||||
const servers: XFTPServer[] = []
|
||||
for (const chunk of fd.chunks) {
|
||||
for (const replica of chunk.replicas) {
|
||||
if (!seen.has(replica.server)) {
|
||||
seen.add(replica.server)
|
||||
servers.push(parseXFTPServer(replica.server))
|
||||
}
|
||||
}
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
// Build an HTTPS origin from an XFTP server address.
|
||||
export function serverOrigin(server: XFTPServer): string {
|
||||
return server.port === "443" ? `https://${server.host}` : `https://${server.host}:${server.port}`
|
||||
}
|
||||
@@ -75,7 +75,7 @@ export default defineConfig(({mode}) => {
|
||||
servers = ['xftp://fp@localhost:443']
|
||||
} else {
|
||||
// In production mode, use the preset servers
|
||||
servers = [...presets.simplex, ...presets.flux]
|
||||
servers = [...presets.simplex]
|
||||
define['__XFTP_SERVERS__'] = JSON.stringify(servers)
|
||||
define['__XFTP_PROXY_PORT__'] = JSON.stringify(null)
|
||||
}
|
||||
@@ -94,6 +94,7 @@ export default defineConfig(({mode}) => {
|
||||
outDir: resolve(__dirname, 'dist-web'),
|
||||
emptyOutDir: true,
|
||||
target: 'esnext',
|
||||
minify: false,
|
||||
chunkSizeWarningLimit: 1200,
|
||||
rollupOptions: {
|
||||
external: ['node:http2', 'url'],
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
newXFTPAgent, closeXFTPAgent,
|
||||
decodeDescriptionURI, downloadFileRaw
|
||||
} from '../src/agent.js'
|
||||
import {getDescriptionServers} from '../src/protocol/address.js'
|
||||
import {XFTPPermanentError} from '../src/client.js'
|
||||
|
||||
const DECRYPT_WEIGHT = 0.15
|
||||
@@ -20,6 +21,8 @@ export function initDownload(app: HTMLElement, hash: string) {
|
||||
return
|
||||
}
|
||||
|
||||
const wrongServer = !getDescriptionServers(fd).map(s => s.host).includes(window.location.hostname)
|
||||
|
||||
const size = fd.redirect ? fd.redirect.size : fd.size
|
||||
app.innerHTML = `
|
||||
<div class="card">
|
||||
@@ -62,6 +65,11 @@ export function initDownload(app: HTMLElement, hash: string) {
|
||||
showStage(errorStage)
|
||||
}
|
||||
|
||||
if (wrongServer) {
|
||||
readyStage.innerHTML = `<p class="error">${t('wrongServer', 'This file is not hosted on this server.')}</p>`
|
||||
return
|
||||
}
|
||||
|
||||
dlBtn.addEventListener('click', startDownload)
|
||||
retryBtn.addEventListener('click', () => showStage(readyStage))
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
newXFTPAgent, closeXFTPAgent, uploadFile, encodeDescriptionURI,
|
||||
type EncryptedFileMetadata
|
||||
} from '../src/agent.js'
|
||||
import {getDescriptionServers, serverOrigin} from '../src/protocol/address.js'
|
||||
import {XFTPPermanentError} from '../src/client.js'
|
||||
|
||||
const MAX_SIZE = 100 * 1024 * 1024
|
||||
@@ -170,7 +171,11 @@ export function initUpload(app: HTMLElement) {
|
||||
})
|
||||
if (aborted) return
|
||||
|
||||
const url = window.location.origin + window.location.pathname + '#' + result.uri
|
||||
const descServers = getDescriptionServers(result.rcvDescription)
|
||||
const origin = descServers.length > 0
|
||||
? serverOrigin(descServers[0])
|
||||
: window.location.origin
|
||||
const url = origin + window.location.pathname + '#' + result.uri
|
||||
shareLink.value = url
|
||||
showStage(completeStage)
|
||||
app.dispatchEvent(new CustomEvent('xftp:upload-complete', {detail: {url}, bubbles: true}))
|
||||
|
||||
Reference in new issue
Block a user