From 33454444a430e888c114e0d8ca74cd496373a9e4 Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Mon, 9 Mar 2026 14:26:40 +0000 Subject: [PATCH 1/4] xftp-web: new version (#1728) * xftp-web: remove flux from preset * xftp-web: new version --- xftp-web/package.json | 2 +- xftp-web/vite.config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/xftp-web/package.json b/xftp-web/package.json index 7b57e2ad5..f294c9079 100644 --- a/xftp-web/package.json +++ b/xftp-web/package.json @@ -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": { diff --git a/xftp-web/vite.config.ts b/xftp-web/vite.config.ts index 694f1a949..2771a4017 100644 --- a/xftp-web/vite.config.ts +++ b/xftp-web/vite.config.ts @@ -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) } From b6b87a632364f6b96d86e59b0019d3897e81775b Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Wed, 11 Mar 2026 09:08:23 +0000 Subject: [PATCH 2/4] xftp-web: disable minification in vite build (#1731) --- xftp-web/vite.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/xftp-web/vite.config.ts b/xftp-web/vite.config.ts index 2771a4017..4781a1957 100644 --- a/xftp-web/vite.config.ts +++ b/xftp-web/vite.config.ts @@ -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'], From 328d3b941a9385251af24458d5c14b2394c7bc99 Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Wed, 11 Mar 2026 15:59:26 +0000 Subject: [PATCH 3/4] xftp-web: use XFTP server domain in share link, verify on download (#1732) * xftp-web: use XFTP server domain in share link, verify on download Use a random XFTP server host from the file description as the link origin instead of the current page domain. On download, verify that the hosting domain matches one of the servers in the description. * xftp-web: use first description server as link origin * xftp-web: show wrong server error in download UI instead of blank page --- xftp-web/src/protocol/address.ts | 20 ++++++++++++++++++++ xftp-web/web/download.ts | 8 ++++++++ xftp-web/web/upload.ts | 7 ++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/xftp-web/src/protocol/address.ts b/xftp-web/src/protocol/address.ts index a4d4c05f6..e3eb27fd8 100644 --- a/xftp-web/src/protocol/address.ts +++ b/xftp-web/src/protocol/address.ts @@ -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() + 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}` +} diff --git a/xftp-web/web/download.ts b/xftp-web/web/download.ts index 949983659..35e09d701 100644 --- a/xftp-web/web/download.ts +++ b/xftp-web/web/download.ts @@ -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 = `
@@ -62,6 +65,11 @@ export function initDownload(app: HTMLElement, hash: string) { showStage(errorStage) } + if (wrongServer) { + readyStage.innerHTML = `

${t('wrongServer', 'This file is not hosted on this server.')}

` + return + } + dlBtn.addEventListener('click', startDownload) retryBtn.addEventListener('click', () => showStage(readyStage)) diff --git a/xftp-web/web/upload.ts b/xftp-web/web/upload.ts index a3a60cdca..78906bb35 100644 --- a/xftp-web/web/upload.ts +++ b/xftp-web/web/upload.ts @@ -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})) From 782cacfb3cc57883465eecc0b9b30662daf2b81f Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:05:00 +0000 Subject: [PATCH 4/4] fix: using simplexmq as dependency (move embedFile to executables) (#1734) * web: parameterize generateSite, remove Embedded from library Move embedFile/embedDir out of the library so it works when simplexmq is consumed as a dependency. generateSite now accepts mediaContent, wellKnown, and linkHtml as parameters. * smp-server, xftp-server: embed static files in executables Add shared apps/common/Embedded.hs with TH splices, update SMPWeb and XFTPWeb to pass embedded content to generateSite, move file-embed dependency from library to executables and test suite. * refactor * add export, move common files to Web subfolder * fix .cabal --------- Co-authored-by: Evgeny Poberezkin --- apps/common/Web/Embedded.hs | 15 ++++ .../.well-known/apple-app-site-association | 0 .../Web/static}/.well-known/assetlinks.json | 0 .../Web => apps/common/Web/static}/index.html | 0 .../Web => apps/common/Web/static}/link.html | 0 .../common/Web/static}/media/GilroyBold.woff2 | Bin .../Web/static}/media/GilroyLight.woff2 | Bin .../Web/static}/media/GilroyMedium.woff2 | Bin .../Web/static}/media/GilroyRegular.woff2 | Bin .../static}/media/GilroyRegularItalic.woff2 | Bin .../common/Web/static}/media/apk_icon.png | Bin .../common/Web/static}/media/apple_store.svg | 0 .../common/Web/static}/media/contact.js | 0 .../Web/static}/media/contact_page_mobile.png | Bin .../common/Web/static}/media/f_droid.svg | 0 .../common/Web/static}/media/favicon.ico | Bin .../common/Web/static}/media/google_play.svg | 0 .../common/Web/static}/media/logo-dark.png | Bin .../common/Web/static}/media/logo-light.png | Bin .../Web/static}/media/logo-symbol-dark.svg | 0 .../Web/static}/media/logo-symbol-light.svg | 0 .../common/Web/static}/media/moon.svg | 0 .../common/Web/static}/media/qrcode.js | 0 .../common/Web/static}/media/script.js | 0 .../common/Web/static}/media/style.css | 0 .../common/Web/static}/media/sun.svg | 0 .../Web/static}/media/swiper-bundle.min.css | 0 .../Web/static}/media/swiper-bundle.min.js | 0 .../common/Web/static}/media/tailwind.css | 0 .../common/Web/static}/media/testflight.png | Bin apps/smp-server/SMPWeb.hs | 6 +- apps/xftp-server/XFTPWeb.hs | 6 +- simplexmq.cabal | 65 ++++++++++-------- src/Simplex/Messaging/Parsers.hs | 1 + src/Simplex/Messaging/Server/Web.hs | 19 +++-- src/Simplex/Messaging/Server/Web/Embedded.hs | 18 ----- 36 files changed, 71 insertions(+), 59 deletions(-) create mode 100644 apps/common/Web/Embedded.hs rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/.well-known/apple-app-site-association (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/.well-known/assetlinks.json (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/index.html (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/link.html (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/GilroyBold.woff2 (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/GilroyLight.woff2 (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/GilroyMedium.woff2 (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/GilroyRegular.woff2 (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/GilroyRegularItalic.woff2 (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/apk_icon.png (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/apple_store.svg (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/contact.js (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/contact_page_mobile.png (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/f_droid.svg (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/favicon.ico (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/google_play.svg (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/logo-dark.png (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/logo-light.png (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/logo-symbol-dark.svg (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/logo-symbol-light.svg (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/moon.svg (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/qrcode.js (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/script.js (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/style.css (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/sun.svg (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/swiper-bundle.min.css (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/swiper-bundle.min.js (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/tailwind.css (100%) rename {src/Simplex/Messaging/Server/Web => apps/common/Web/static}/media/testflight.png (100%) delete mode 100644 src/Simplex/Messaging/Server/Web/Embedded.hs diff --git a/apps/common/Web/Embedded.hs b/apps/common/Web/Embedded.hs new file mode 100644 index 000000000..9dae7b53f --- /dev/null +++ b/apps/common/Web/Embedded.hs @@ -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/") + } diff --git a/src/Simplex/Messaging/Server/Web/.well-known/apple-app-site-association b/apps/common/Web/static/.well-known/apple-app-site-association similarity index 100% rename from src/Simplex/Messaging/Server/Web/.well-known/apple-app-site-association rename to apps/common/Web/static/.well-known/apple-app-site-association diff --git a/src/Simplex/Messaging/Server/Web/.well-known/assetlinks.json b/apps/common/Web/static/.well-known/assetlinks.json similarity index 100% rename from src/Simplex/Messaging/Server/Web/.well-known/assetlinks.json rename to apps/common/Web/static/.well-known/assetlinks.json diff --git a/src/Simplex/Messaging/Server/Web/index.html b/apps/common/Web/static/index.html similarity index 100% rename from src/Simplex/Messaging/Server/Web/index.html rename to apps/common/Web/static/index.html diff --git a/src/Simplex/Messaging/Server/Web/link.html b/apps/common/Web/static/link.html similarity index 100% rename from src/Simplex/Messaging/Server/Web/link.html rename to apps/common/Web/static/link.html diff --git a/src/Simplex/Messaging/Server/Web/media/GilroyBold.woff2 b/apps/common/Web/static/media/GilroyBold.woff2 similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/GilroyBold.woff2 rename to apps/common/Web/static/media/GilroyBold.woff2 diff --git a/src/Simplex/Messaging/Server/Web/media/GilroyLight.woff2 b/apps/common/Web/static/media/GilroyLight.woff2 similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/GilroyLight.woff2 rename to apps/common/Web/static/media/GilroyLight.woff2 diff --git a/src/Simplex/Messaging/Server/Web/media/GilroyMedium.woff2 b/apps/common/Web/static/media/GilroyMedium.woff2 similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/GilroyMedium.woff2 rename to apps/common/Web/static/media/GilroyMedium.woff2 diff --git a/src/Simplex/Messaging/Server/Web/media/GilroyRegular.woff2 b/apps/common/Web/static/media/GilroyRegular.woff2 similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/GilroyRegular.woff2 rename to apps/common/Web/static/media/GilroyRegular.woff2 diff --git a/src/Simplex/Messaging/Server/Web/media/GilroyRegularItalic.woff2 b/apps/common/Web/static/media/GilroyRegularItalic.woff2 similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/GilroyRegularItalic.woff2 rename to apps/common/Web/static/media/GilroyRegularItalic.woff2 diff --git a/src/Simplex/Messaging/Server/Web/media/apk_icon.png b/apps/common/Web/static/media/apk_icon.png similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/apk_icon.png rename to apps/common/Web/static/media/apk_icon.png diff --git a/src/Simplex/Messaging/Server/Web/media/apple_store.svg b/apps/common/Web/static/media/apple_store.svg similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/apple_store.svg rename to apps/common/Web/static/media/apple_store.svg diff --git a/src/Simplex/Messaging/Server/Web/media/contact.js b/apps/common/Web/static/media/contact.js similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/contact.js rename to apps/common/Web/static/media/contact.js diff --git a/src/Simplex/Messaging/Server/Web/media/contact_page_mobile.png b/apps/common/Web/static/media/contact_page_mobile.png similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/contact_page_mobile.png rename to apps/common/Web/static/media/contact_page_mobile.png diff --git a/src/Simplex/Messaging/Server/Web/media/f_droid.svg b/apps/common/Web/static/media/f_droid.svg similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/f_droid.svg rename to apps/common/Web/static/media/f_droid.svg diff --git a/src/Simplex/Messaging/Server/Web/media/favicon.ico b/apps/common/Web/static/media/favicon.ico similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/favicon.ico rename to apps/common/Web/static/media/favicon.ico diff --git a/src/Simplex/Messaging/Server/Web/media/google_play.svg b/apps/common/Web/static/media/google_play.svg similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/google_play.svg rename to apps/common/Web/static/media/google_play.svg diff --git a/src/Simplex/Messaging/Server/Web/media/logo-dark.png b/apps/common/Web/static/media/logo-dark.png similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/logo-dark.png rename to apps/common/Web/static/media/logo-dark.png diff --git a/src/Simplex/Messaging/Server/Web/media/logo-light.png b/apps/common/Web/static/media/logo-light.png similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/logo-light.png rename to apps/common/Web/static/media/logo-light.png diff --git a/src/Simplex/Messaging/Server/Web/media/logo-symbol-dark.svg b/apps/common/Web/static/media/logo-symbol-dark.svg similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/logo-symbol-dark.svg rename to apps/common/Web/static/media/logo-symbol-dark.svg diff --git a/src/Simplex/Messaging/Server/Web/media/logo-symbol-light.svg b/apps/common/Web/static/media/logo-symbol-light.svg similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/logo-symbol-light.svg rename to apps/common/Web/static/media/logo-symbol-light.svg diff --git a/src/Simplex/Messaging/Server/Web/media/moon.svg b/apps/common/Web/static/media/moon.svg similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/moon.svg rename to apps/common/Web/static/media/moon.svg diff --git a/src/Simplex/Messaging/Server/Web/media/qrcode.js b/apps/common/Web/static/media/qrcode.js similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/qrcode.js rename to apps/common/Web/static/media/qrcode.js diff --git a/src/Simplex/Messaging/Server/Web/media/script.js b/apps/common/Web/static/media/script.js similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/script.js rename to apps/common/Web/static/media/script.js diff --git a/src/Simplex/Messaging/Server/Web/media/style.css b/apps/common/Web/static/media/style.css similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/style.css rename to apps/common/Web/static/media/style.css diff --git a/src/Simplex/Messaging/Server/Web/media/sun.svg b/apps/common/Web/static/media/sun.svg similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/sun.svg rename to apps/common/Web/static/media/sun.svg diff --git a/src/Simplex/Messaging/Server/Web/media/swiper-bundle.min.css b/apps/common/Web/static/media/swiper-bundle.min.css similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/swiper-bundle.min.css rename to apps/common/Web/static/media/swiper-bundle.min.css diff --git a/src/Simplex/Messaging/Server/Web/media/swiper-bundle.min.js b/apps/common/Web/static/media/swiper-bundle.min.js similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/swiper-bundle.min.js rename to apps/common/Web/static/media/swiper-bundle.min.js diff --git a/src/Simplex/Messaging/Server/Web/media/tailwind.css b/apps/common/Web/static/media/tailwind.css similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/tailwind.css rename to apps/common/Web/static/media/tailwind.css diff --git a/src/Simplex/Messaging/Server/Web/media/testflight.png b/apps/common/Web/static/media/testflight.png similarity index 100% rename from src/Simplex/Messaging/Server/Web/media/testflight.png rename to apps/common/Web/static/media/testflight.png diff --git a/apps/smp-server/SMPWeb.hs b/apps/smp-server/SMPWeb.hs index efa7edee3..9a449e80b 100644 --- a/apps/smp-server/SMPWeb.hs +++ b/apps/smp-server/SMPWeb.hs @@ -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 = diff --git a/apps/xftp-server/XFTPWeb.hs b/apps/xftp-server/XFTPWeb.hs index e8ee7040a..dd57d43c0 100644 --- a/apps/xftp-server/XFTPWeb.hs +++ b/apps/xftp-server/XFTPWeb.hs @@ -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 = diff --git a/simplexmq.cabal b/simplexmq.cabal index 41997e5d3..7f856543d 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -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 @@ -246,7 +246,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 @@ -343,7 +342,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 @@ -410,15 +408,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 @@ -446,15 +447,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 @@ -500,6 +504,7 @@ test-suite simplexmq-test XFTPWebTests SMPWeb XFTPWeb + Web.Embedded Paths_simplexmq if flag(client_postgres) other-modules: @@ -518,6 +523,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 @@ -535,6 +541,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 diff --git a/src/Simplex/Messaging/Parsers.hs b/src/Simplex/Messaging/Parsers.hs index 7acbec743..c138cb8aa 100644 --- a/src/Simplex/Messaging/Parsers.hs +++ b/src/Simplex/Messaging/Parsers.hs @@ -18,6 +18,7 @@ module Simplex.Messaging.Parsers sumTypeJSON, taggedObjectJSON, singleFieldJSON, + singleFieldJSON_, defaultJSON, textP, pattern SingleFieldJSONTag, diff --git a/src/Simplex/Messaging/Server/Web.hs b/src/Simplex/Messaging/Server/Web.hs index 23a534429..1f4430af7 100644 --- a/src/Simplex/Messaging/Server/Web.hs +++ b/src/Simplex/Messaging/Server/Web.hs @@ -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. diff --git a/src/Simplex/Messaging/Server/Web/Embedded.hs b/src/Simplex/Messaging/Server/Web/Embedded.hs deleted file mode 100644 index 7345829cc..000000000 --- a/src/Simplex/Messaging/Server/Web/Embedded.hs +++ /dev/null @@ -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/")