Fix files cache problem and static files management

This commit is contained in:
koalasat 2024-09-17 15:14:43 +02:00
parent f2a0d0e632
commit bfda0e93be
No known key found for this signature in database
GPG Key ID: 2F7F61C6146AB157
30 changed files with 810 additions and 467 deletions

15
.gitignore vendored
View File

@ -646,10 +646,17 @@ docs/.jekyll-cache*
docs/_site* docs/_site*
node node
# mobile frontend statics # frontend statics
mobile/html/Web.bundle/js* mobile/html/Web.bundle/static/*
mobile/html/Web.bundle/css* # mobile/html/Web.bundle/index.html
mobile/html/Web.bundle/assets* desktopApp/static/*
# desktopApp/index.html
web/static/*
# web/basic.html
# web/pro.html
nodeapp/static/*
# nodeapp/basic.html
# nodeapp/pro.html
# Protocol Buffers # Protocol Buffers
api/lightning/*.proto api/lightning/*.proto

View File

@ -1,63 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" type="image/png" href="./static/assets/images/favicon-96x96.png" />
<link rel="icon" type="image/png" href="./static/assets/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="./static/assets/images/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="./static/assets/images/favicon-192x192.png" sizes="192x192">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required.">
<title>RoboSats - Simple and Private Bitcoin Exchange</title>
<link rel="stylesheet" type="text/css" href="./static/css/fonts.css"/>
<link rel="stylesheet" type="text/css" href="./static/css/loader.css"/>
<link rel="stylesheet" type="text/css" href="./static/css/index.css"/>
<link rel="stylesheet" type="text/css" href="./static/css/leaflet.css"/>
</head>
<body>
<noscript>
<div>
This site requires JavaScript. This message is only visible if you have it disabled. <br/><br/>
If you are using TOR browser set the "Security Level" to "Standard". If you keep seeing this message clear cache and storage of TOR browser app and retry.<br/><br/>
If the problem persists, ask for support in the RoboSats telegram group<a href="https://t.me/robosats"> (t.me/robosats)</a>
</div>
</noscript>
<div id="main">
<div id="app">
<div class="loaderCenter">
<div class="loaderSpinner"></div>
<div class="content-slider">
<div class="slider">
<div class="mask">
<ul>
<li class="anim1">
<div class="quote">Looking for robot parts ...</div>
</li>
<li class="anim2">
<div class="quote">Adding layers to the onion ...</div>
</li>
<li class="anim3">
<div class="quote">Winning at game theory ...</div>
</li>
<li class="anim4">
<div class="quote">Moving Sats at light speed ...</div>
</li>
<li class="anim5">
<div class="quote">Hiding in 2^256 bits of entropy...</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script>
window.RobosatsSettings = 'web-basic'
window.RobosatsClient = 'desktop-app'
</script>
<script src="./static/frontend/main.js"></script>
</body>
</html>

View File

@ -1 +0,0 @@
../frontend/static

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,7 @@
"eslint-plugin-promise": "^6.1.1", "eslint-plugin-promise": "^6.1.1",
"eslint-plugin-react": "^7.34.0", "eslint-plugin-react": "^7.34.0",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"html-webpack-plugin": "^5.6.0",
"jest": "^29.6.1", "jest": "^29.6.1",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",

View File

@ -15,6 +15,7 @@ import { GarageContextProvider } from './contexts/GarageContext';
import { FederationContextProvider } from './contexts/FederationContext'; import { FederationContextProvider } from './contexts/FederationContext';
const App = (): JSX.Element => { const App = (): JSX.Element => {
const [client, _view] = window.RobosatsSettings.split('-');
return ( return (
<StrictMode> <StrictMode>
<ErrorBoundary> <ErrorBoundary>
@ -24,11 +25,7 @@ const App = (): JSX.Element => {
<FederationContextProvider> <FederationContextProvider>
<GarageContextProvider> <GarageContextProvider>
<CssBaseline /> <CssBaseline />
{window.NativeRobosats === undefined && window.RobosatsClient === undefined ? ( {client !== 'mobile' ? <HostAlert /> : <TorConnectionBadge />}
<HostAlert />
) : (
<TorConnectionBadge />
)}
<Main /> <Main />
</GarageContextProvider> </GarageContextProvider>
</FederationContextProvider> </FederationContextProvider>

View File

@ -12,9 +12,10 @@ import { GarageContext, type UseGarageStoreType } from '../contexts/GarageContex
import Routes from './Routes'; import Routes from './Routes';
const getRouter = (): any => { const getRouter = (): any => {
if (window.NativeRobosats === undefined && window.RobosatsClient === undefined) { const [client, _view] = window.RobosatsSettings.split('-');
if (client === 'web') {
return BrowserRouter; return BrowserRouter;
} else if (window.RobosatsClient === 'desktop-app') { } else if (client === 'desktop') {
return HashRouter; return HashRouter;
} else { } else {
return MemoryRouter; return MemoryRouter;

View File

@ -43,7 +43,7 @@ const RobotProfile = ({
setView, setView,
width, width,
}: RobotProfileProps): JSX.Element => { }: RobotProfileProps): JSX.Element => {
const { windowSize } = useContext<UseAppStoreType>(AppContext); const { windowSize, client } = useContext<UseAppStoreType>(AppContext);
const { garage, slotUpdatedAt } = useContext<UseGarageStoreType>(GarageContext); const { garage, slotUpdatedAt } = useContext<UseGarageStoreType>(GarageContext);
const { t } = useTranslation(); const { t } = useTranslation();
@ -317,7 +317,7 @@ const RobotProfile = ({
</LoadingButton> </LoadingButton>
</Grid> </Grid>
{window.NativeRobosats === undefined ? ( {client !== 'mobile' ? (
<Grid item> <Grid item>
<Button <Button
color='primary' color='primary'

View File

@ -24,7 +24,7 @@ import { FederationContext, type UseFederationStoreType } from '../../contexts/F
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
const RobotPage = (): JSX.Element => { const RobotPage = (): JSX.Element => {
const { torStatus, windowSize, settings, page } = useContext<UseAppStoreType>(AppContext); const { torStatus, windowSize, settings, page, client } = useContext<UseAppStoreType>(AppContext);
const { garage } = useContext<UseGarageStoreType>(GarageContext); const { garage } = useContext<UseGarageStoreType>(GarageContext);
const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext); const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext);
const { t } = useTranslation(); const { t } = useTranslation();
@ -44,7 +44,7 @@ const RobotPage = (): JSX.Element => {
const token = urlToken ?? garage.currentSlot; const token = urlToken ?? garage.currentSlot;
if (token !== undefined && token !== null && page === 'robot') { if (token !== undefined && token !== null && page === 'robot') {
setInputToken(token); setInputToken(token);
if (window.NativeRobosats === undefined || torStatus === 'ON' || !settings.useProxy) { if (client !== 'mobile' || torStatus === 'ON' || !settings.useProxy) {
setView('profile'); setView('profile');
} }
} }
@ -82,7 +82,7 @@ const RobotPage = (): JSX.Element => {
garage.deleteSlot(); garage.deleteSlot();
}; };
if (settings.useProxy && !(window.NativeRobosats === undefined) && !(torStatus === 'ON')) { if (settings.useProxy && client === 'mobile' && !(torStatus === 'ON')) {
return ( return (
<Paper <Paper
elevation={12} elevation={12}

View File

@ -46,7 +46,7 @@ const RobotAvatar: React.FC<Props> = ({
}) => { }) => {
const [avatarSrc, setAvatarSrc] = useState<string>(''); const [avatarSrc, setAvatarSrc] = useState<string>('');
const [activeBackground, setActiveBackground] = useState<boolean>(true); const [activeBackground, setActiveBackground] = useState<boolean>(true);
const { hostUrl } = useContext<UseAppStoreType>(AppContext); const { hostUrl, client } = useContext<UseAppStoreType>(AppContext);
const backgroundFadeTime = 3000; const backgroundFadeTime = 3000;
const [backgroundData] = useState<BackgroundData>(placeholder.loading); const [backgroundData] = useState<BackgroundData>(placeholder.loading);
@ -71,7 +71,7 @@ const RobotAvatar: React.FC<Props> = ({
useEffect(() => { useEffect(() => {
if (shortAlias && shortAlias !== '') { if (shortAlias && shortAlias !== '') {
if (!window.NativeRobosats) { if (client !== 'mobile') {
setAvatarSrc( setAvatarSrc(
`${hostUrl}/static/federation/avatars/${shortAlias}${small ? '.small' : ''}.webp`, `${hostUrl}/static/federation/avatars/${shortAlias}${small ? '.small' : ''}.webp`,
); );

View File

@ -37,7 +37,7 @@ interface SettingsFormProps {
} }
const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => { const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
const { fav, setFav, settings, setSettings } = useContext<UseAppStoreType>(AppContext); const { fav, setFav, settings, setSettings, client } = useContext<UseAppStoreType>(AppContext);
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const fontSizes = [ const fontSizes = [
@ -237,7 +237,7 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
</ToggleButtonGroup> </ToggleButtonGroup>
</ListItem> </ListItem>
{window.NativeRobosats !== undefined && ( {client === 'mobile' && (
<ListItem> <ListItem>
<ListItemIcon> <ListItemIcon>
<TorIcon /> <TorIcon />

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
Box, Box,
@ -33,6 +33,7 @@ import { apiClient } from '../../../services/api';
import { systemClient } from '../../../services/System'; import { systemClient } from '../../../services/System';
import lnproxies from '../../../../static/lnproxies.json'; import lnproxies from '../../../../static/lnproxies.json';
import { UseAppStoreType, AppContext } from '../../../contexts/AppContext';
let filteredProxies: Array<Record<string, any>> = []; let filteredProxies: Array<Record<string, any>> = [];
export interface LightningForm { export interface LightningForm {
invoice: string; invoice: string;
@ -89,6 +90,7 @@ export const LightningPayoutForm = ({
setLightning, setLightning,
settings, settings,
}: LightningPayoutFormProps): JSX.Element => { }: LightningPayoutFormProps): JSX.Element => {
const { client } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();
@ -153,7 +155,7 @@ export const LightningPayoutForm = ({
bitcoinNetwork = settings?.network ?? 'mainnet'; bitcoinNetwork = settings?.network ?? 'mainnet';
if (settings.host?.includes('.i2p') === true) { if (settings.host?.includes('.i2p') === true) {
internetNetwork = 'I2P'; internetNetwork = 'I2P';
} else if (settings.host?.includes('.onion') === true || window.NativeRobosats !== undefined) { } else if (settings.host?.includes('.onion') === true || client === 'mobile') {
internetNetwork = 'TOR'; internetNetwork = 'TOR';
} }

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useContext, useState } from 'react';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
@ -37,6 +37,7 @@ import {
} from '../Icons'; } from '../Icons';
import { type TradeCoordinatorSummary, type TradeRobotSummary } from '../../models/Order.model'; import { type TradeCoordinatorSummary, type TradeRobotSummary } from '../../models/Order.model';
import { systemClient } from '../../services/System'; import { systemClient } from '../../services/System';
import { UseAppStoreType, AppContext } from '../../contexts/AppContext';
interface Props { interface Props {
isMaker: boolean; isMaker: boolean;
@ -61,6 +62,7 @@ const TradeSummary = ({
platformSummary, platformSummary,
orderId, orderId,
}: Props): JSX.Element => { }: Props): JSX.Element => {
const { client } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();
@ -81,7 +83,7 @@ const TradeSummary = ({
taker: takerSummary, taker: takerSummary,
platform: platformSummary, platform: platformSummary,
}; };
if (window.NativeRobosats === undefined) { if (client !== 'mobile') {
saveAsJson(`order${orderId}-summary.json`, summary); saveAsJson(`order${orderId}-summary.json`, summary);
} else { } else {
systemClient.copyToClipboard(JSON.stringify(summary)); systemClient.copyToClipboard(JSON.stringify(summary));

View File

@ -40,7 +40,6 @@ export interface SlideDirection {
export type TorStatus = 'ON' | 'STARTING' | 'STOPPING' | 'OFF'; export type TorStatus = 'ON' | 'STARTING' | 'STOPPING' | 'OFF';
export const isNativeRoboSats = !(window.NativeRobosats === undefined); export const isNativeRoboSats = !(window.NativeRobosats === undefined);
export const isDesktopRoboSats = !(window.RobosatsClient === undefined);
const pageFromPath = window.location.pathname.split('/')[1]; const pageFromPath = window.location.pathname.split('/')[1];
const isPagePathEmpty = pageFromPath === ''; const isPagePathEmpty = pageFromPath === '';
@ -76,13 +75,14 @@ const makeTheme = function (settings: Settings): Theme {
}; };
const getHostUrl = (network = 'mainnet'): string => { const getHostUrl = (network = 'mainnet'): string => {
const [client, _view] = window.RobosatsSettings.split('-');
const randomAlias = const randomAlias =
Object.keys(defaultFederation)[ Object.keys(defaultFederation)[
Math.floor(Math.random() * Object.keys(defaultFederation).length) Math.floor(Math.random() * Object.keys(defaultFederation).length)
]; ];
let host = defaultFederation[randomAlias][network].onion; let host = defaultFederation[randomAlias][network].onion;
let protocol = 'http:'; let protocol = 'http:';
if (window.NativeRobosats === undefined) { if (client !== 'mobile') {
host = getHost(); host = getHost();
protocol = location.protocol; protocol = location.protocol;
} }
@ -93,8 +93,9 @@ const getHostUrl = (network = 'mainnet'): string => {
const getOrigin = (network = 'mainnet'): Origin => { const getOrigin = (network = 'mainnet'): Origin => {
const host = getHostUrl(network); const host = getHostUrl(network);
let origin: Origin = 'onion'; let origin: Origin = 'onion';
const [client, _view] = window.RobosatsSettings.split('-');
if (window.NativeRobosats !== undefined || host.includes('.onion')) { if (client === 'mobile' || client === 'desktop' || host.includes('.onion')) {
origin = 'onion'; origin = 'onion';
} else if (host.includes('i2p')) { } else if (host.includes('i2p')) {
origin = 'i2p'; origin = 'i2p';
@ -106,13 +107,13 @@ const getOrigin = (network = 'mainnet'): Origin => {
}; };
const getSettings = (): Settings => { const getSettings = (): Settings => {
let settings = new Settings(); let settings;
if (window.RobosatsSettings === 'selfhosted-basic') {
settings = new SettingsSelfhosted(); const [client, view] = window.RobosatsSettings.split('-');
} else if (window.RobosatsSettings === 'selfhosted-pro') { if (client === 'selfhosted') {
settings = new SettingsSelfhostedPro(); settings = view === 'pro' ? new SettingsSelfhostedPro() : new SettingsSelfhosted();
} else if (window.RobosatsSettings === 'web-pro') { } else {
settings = new SettingsPro(); settings = view === 'pro' ? new SettingsPro() : new Settings();
} }
return settings; return settings;
@ -152,6 +153,8 @@ export interface UseAppStoreType {
fav: Favorites; fav: Favorites;
setFav: Dispatch<SetStateAction<Favorites>>; setFav: Dispatch<SetStateAction<Favorites>>;
worldmap?: GeoJsonObject; worldmap?: GeoJsonObject;
client: 'mobile' | 'web' | 'desktop' | string;
view: 'basic' | 'pro' | string;
} }
export const initialAppContext: UseAppStoreType = { export const initialAppContext: UseAppStoreType = {
@ -178,6 +181,8 @@ export const initialAppContext: UseAppStoreType = {
fav: { type: null, currency: 0, mode: 'fiat', coordinator: 'any' }, fav: { type: null, currency: 0, mode: 'fiat', coordinator: 'any' },
setFav: () => {}, setFav: () => {},
worldmap: undefined, worldmap: undefined,
client: 'web',
view: 'basic',
}; };
export const AppContext = createContext<UseAppStoreType>(initialAppContext); export const AppContext = createContext<UseAppStoreType>(initialAppContext);
@ -191,6 +196,7 @@ export const AppContextProvider = ({ children }: AppContextProviderProps): JSX.E
const clientVersion = initialAppContext.clientVersion; const clientVersion = initialAppContext.clientVersion;
const hostUrl = initialAppContext.hostUrl; const hostUrl = initialAppContext.hostUrl;
const origin = initialAppContext.origin; const origin = initialAppContext.origin;
const [client, view] = window.RobosatsSettings.split('-');
const [settings, setSettings] = useState<Settings>(getSettings()); const [settings, setSettings] = useState<Settings>(getSettings());
const [theme, setTheme] = useState<Theme>(() => { const [theme, setTheme] = useState<Theme>(() => {
@ -286,6 +292,8 @@ export const AppContextProvider = ({ children }: AppContextProviderProps): JSX.E
fav, fav,
setFav, setFav,
worldmap, worldmap,
client,
view,
}} }}
> >
<ThemeProvider theme={theme}>{children}</ThemeProvider> <ThemeProvider theme={theme}>{children}</ThemeProvider>

View File

@ -46,7 +46,7 @@ export const FederationContext = createContext<UseFederationStoreType>(initialFe
export const FederationContextProvider = ({ export const FederationContextProvider = ({
children, children,
}: FederationContextProviderProps): JSX.Element => { }: FederationContextProviderProps): JSX.Element => {
const { settings, page, origin, hostUrl, open, torStatus } = const { settings, page, origin, hostUrl, open, torStatus, client } =
useContext<UseAppStoreType>(AppContext); useContext<UseAppStoreType>(AppContext);
const { setMaker, garage } = useContext<UseGarageStoreType>(GarageContext); const { setMaker, garage } = useContext<UseGarageStoreType>(GarageContext);
const [federation] = useState(new Federation(origin, settings, hostUrl)); const [federation] = useState(new Federation(origin, settings, hostUrl));
@ -66,7 +66,7 @@ export const FederationContextProvider = ({
}, []); }, []);
useEffect(() => { useEffect(() => {
if (window.NativeRobosats === undefined || torStatus === 'ON' || !settings.useProxy) { if (client !== 'mobile' || torStatus === 'ON' || !settings.useProxy) {
void federation.updateUrl(origin, settings, hostUrl); void federation.updateUrl(origin, settings, hostUrl);
void federation.update(); void federation.update();
} }

View File

@ -64,7 +64,7 @@ export const GarageContext = createContext<UseGarageStoreType>(initialGarageCont
export const GarageContextProvider = ({ children }: GarageContextProviderProps): JSX.Element => { export const GarageContextProvider = ({ children }: GarageContextProviderProps): JSX.Element => {
// All garage data structured // All garage data structured
const { settings, torStatus, open, page } = useContext<UseAppStoreType>(AppContext); const { settings, torStatus, open, page, client } = useContext<UseAppStoreType>(AppContext);
const pageRef = useRef(page); const pageRef = useRef(page);
const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext); const { federation, sortedCoordinators } = useContext<UseFederationStoreType>(FederationContext);
const [garage] = useState<Garage>(initialGarageContext.garage); const [garage] = useState<Garage>(initialGarageContext.garage);
@ -93,14 +93,14 @@ export const GarageContextProvider = ({ children }: GarageContextProviderProps):
}, []); }, []);
useEffect(() => { useEffect(() => {
if (window.NativeRobosats === undefined || torStatus === 'ON' || !settings.useProxy) { if (client !== 'mobile' || torStatus === 'ON' || !settings.useProxy) {
const token = garage.getSlot()?.token; const token = garage.getSlot()?.token;
if (token) void garage.fetchRobot(federation, token); if (token) void garage.fetchRobot(federation, token);
} }
}, [settings.network, settings.useProxy, torStatus]); }, [settings.network, settings.useProxy, torStatus]);
useEffect(() => { useEffect(() => {
if (window.NativeRobosats !== undefined && !systemClient.loading) { if (client === 'mobile' && !systemClient.loading) {
garage.loadSlots(); garage.loadSlots();
} }
}, [systemClient.loading]); }, [systemClient.loading]);

View File

@ -47,7 +47,9 @@ class BaseSettings {
this.host = getHost(); this.host = getHost();
const useProxy = systemClient.getItem('settings_use_proxy'); const useProxy = systemClient.getItem('settings_use_proxy');
this.useProxy = window.NativeRobosats !== undefined && useProxy !== 'false';
const [client, _view] = window.RobosatsSettings.split('-');
this.useProxy = client === 'mobile' && useProxy !== 'false';
apiClient.useProxy = this.useProxy; apiClient.useProxy = this.useProxy;
} }

View File

@ -5,7 +5,6 @@ declare global {
ReactNativeWebView?: ReactNativeWebView; ReactNativeWebView?: ReactNativeWebView;
NativeRobosats?: NativeRobosats; NativeRobosats?: NativeRobosats;
RobosatsSettings: 'web-basic' | 'web-pro' | 'selfhosted-basic' | 'selfhosted-pro'; RobosatsSettings: 'web-basic' | 'web-pro' | 'selfhosted-basic' | 'selfhosted-pro';
RobosatsClient: 'desktop-app' | undefined;
} }
} }

View File

@ -1,7 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta http-equiv="onion-location" content="{{ ONION_LOCATION }}" />
<link rel="shortcut icon" href="/static/assets/images/favicon-96x96.png" /> <link rel="shortcut icon" href="/static/assets/images/favicon-96x96.png" />
<link rel="icon" type="image/png" href="/static/assets/images/favicon-32x32.png" sizes="32x32"> <link rel="icon" type="image/png" href="/static/assets/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-96x96.png" sizes="96x96"> <link rel="icon" type="image/png" href="/static/assets/images/favicon-96x96.png" sizes="96x96">
@ -12,11 +11,11 @@
<meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required."> <meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required.">
<title>RoboSats - Simple and Private Bitcoin Exchange</title> <title>RoboSats - Simple and Private Bitcoin Exchange</title>
{% load static %}
<link rel="stylesheet" href="{% static "css/fonts.css" %}"/> <link rel="stylesheet" href="/static/css/fonts.css"/>
<link rel="stylesheet" type="text/css" href="{% static "css/loader.css" %}"/> <link rel="stylesheet" type="text/css" href="/static/css/loader.css"/>
<link rel="stylesheet" type="text/css" href="{% static "css/index.css" %}"/> <link rel="stylesheet" type="text/css" href="/static/css/index.css"/>
<link rel="stylesheet" type="text/css" href="{% static "css/leaflet.css" %}"/> <link rel="stylesheet" type="text/css" href="/static/css/leaflet.css"/>
</head> </head>
<body> <body>
<noscript> <noscript>
@ -55,7 +54,8 @@
</div> </div>
</div> </div>
</div> </div>
<script>
<script src="{% static "frontend/main.js" %}"></script> window.RobosatsSettings = '<%= htmlWebpackPlugin.options.robosatsSettings %>'
</script>
</body> </body>
</html> </html>

View File

@ -1,7 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta http-equiv="onion-location" content="{{ ONION_LOCATION }}" />
<link rel="shortcut icon" href="/static/assets/images/favicon-96x96.png" /> <link rel="shortcut icon" href="/static/assets/images/favicon-96x96.png" />
<link rel="icon" type="image/png" href="/static/assets/images/favicon-32x32.png" sizes="32x32"> <link rel="icon" type="image/png" href="/static/assets/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-96x96.png" sizes="96x96"> <link rel="icon" type="image/png" href="/static/assets/images/favicon-96x96.png" sizes="96x96">
@ -12,13 +11,13 @@
<meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required."> <meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required.">
<title>RoboSats PRO - Simple and Private Bitcoin Exchange</title> <title>RoboSats PRO - Simple and Private Bitcoin Exchange</title>
{% load static %}
<link rel="stylesheet" href="{% static "css/fonts.css" %}"/> <link rel="stylesheet" href="/static/css_pro/fonts.css"/>
<link rel="stylesheet" type="text/css" href="{% static "css/loader.css" %}"/> <link rel="stylesheet" type="text/css" href="/static/css/loader.css"/>
<link rel="stylesheet" type="text/css" href="{% static "css/index.css" %}"/> <link rel="stylesheet" type="text/css" href="/static/css/index.css"/>
<link rel="stylesheet" type="text/css" href="{% static "css/leaflet.css" %}"/> <link rel="stylesheet" type="text/css" href="/static/css/leaflet.css"/>
<link rel="stylesheet" type="text/css" href="{% static "css_pro/react-grid-layout.css" %}"/> <link rel="stylesheet" type="text/css" href="/static/css_pro/react-grid-layout.css"/>
<link rel="stylesheet" type="text/css" href="{% static "css_pro/react-resizable.css" %}"/> <link rel="stylesheet" type="text/css" href="/static/css_pro/react-resizable.css"/>
</head> </head>
<body> <body>
<noscript> <noscript>
@ -57,7 +56,8 @@
</div> </div>
</div> </div>
</div> </div>
<script>
<script src="{% static "frontend/pro.js" %}"></script> window.RobosatsSettings = '<%= htmlWebpackPlugin.options.robosatsSettings %>'
</script>
</body> </body>
</html> </html>

View File

@ -1,6 +1,8 @@
import path from 'path'; import path from 'path';
import { Configuration } from 'webpack'; import { Configuration } from 'webpack';
import CopyPlugin from 'copy-webpack-plugin'; import CopyPlugin from 'copy-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import { version } from './package.json';
const config: Configuration = { const config: Configuration = {
entry: './src/index.js', entry: './src/index.js',
@ -24,12 +26,82 @@ const config: Configuration = {
}, },
}; };
const configWeb: Configuration = { const configNode: Configuration = {
...config, ...config,
output: { output: {
path: path.resolve(__dirname, 'static/frontend'), path: path.resolve(__dirname, 'static/frontend'),
filename: 'main.js', filename: `main.v${version}.[contenthash].js`,
clean: true,
publicPath: '/static/frontend/',
}, },
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'templates/frontend/basic.html'),
filename: path.resolve(__dirname, '../nodeapp/basic.html'),
robosatsSettings: 'selfhosted-basic',
inject: 'body',
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'templates/frontend/pro.html'),
filename: path.resolve(__dirname, '../nodeapp/pro.html'),
robosatsSettings: 'selfhosted-pro',
inject: 'body',
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'templates/frontend/basic.html'),
filename: path.resolve(__dirname, '../web/basic.html'),
robosatsSettings: 'web-basic',
inject: 'body',
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'templates/frontend/pro.html'),
filename: path.resolve(__dirname, '../web/pro.html'),
robosatsSettings: 'web-pro',
inject: 'body',
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, 'static'),
to: path.resolve(__dirname, '../nodeapp/static'),
},
],
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, 'static'),
to: path.resolve(__dirname, '../web/static'),
},
],
}),
],
};
const configDesktop: Configuration = {
...config,
output: {
path: path.resolve(__dirname, '../desktopApp/static/frontend'),
filename: `main.v${version}.[contenthash].js`,
clean: true,
publicPath: '/static/frontend/',
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'templates/frontend/basic.html'),
filename: path.resolve(__dirname, '../desktopApp/index.html'),
robosatsSettings: 'desktop-basic',
inject: 'body',
}),
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, 'static'),
to: path.resolve(__dirname, '../desktopApp/static'),
},
],
}),
],
}; };
const configMobile: Configuration = { const configMobile: Configuration = {
@ -80,27 +152,27 @@ const configMobile: Configuration = {
], ],
}, },
plugins: [ plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'templates/frontend/basic.html'),
filename: path.resolve(__dirname, '../mobile/html/Web.bundle/index.html'),
robosatsSettings: 'mobile-basic',
inject: 'body',
}),
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ {
from: path.resolve(__dirname, 'static/css'), from: path.resolve(__dirname, 'static/css'),
to: path.resolve(__dirname, '../mobile/html/Web.bundle/css'), to: path.resolve(__dirname, '../mobile/html/Web.bundle/static'),
},
{
from: path.resolve(__dirname, 'static/assets/sounds'),
to: path.resolve(__dirname, '../mobile/html/Web.bundle/assets/sounds'),
},
{
from: path.resolve(__dirname, 'static/federation'),
to: path.resolve(__dirname, '../mobile/html/Web.bundle/assets/federation'),
}, },
], ],
}), }),
], ],
output: { output: {
path: path.resolve(__dirname, '../mobile/html/Web.bundle/js'), path: path.resolve(__dirname, '../mobile/html/Web.bundle/static/frontend'),
filename: 'main.js', filename: `main.v${version}.[contenthash].js`,
clean: true,
publicPath: '/static/frontend/',
}, },
}; };
export default [configWeb, configMobile]; export default [configNode, configDesktop, configMobile];

3
mobile/.gitignore vendored
View File

@ -63,5 +63,4 @@ buck-out/
/vendor/bundle/ /vendor/bundle/
# frontend js # frontend js
/html/Web.bundle/js* /html/Web.bundle/static*
/html/Web.bundle/css*

View File

@ -1,55 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required.">
<title>RoboSats - Simple and Private Bitcoin Exchange</title>
<link rel="stylesheet" href="css/fonts.css"/>
<link rel="stylesheet" type="text/css" href="css/loader.css"/>
<link rel="stylesheet" type="text/css" href="css/index.css"/>
<link rel="stylesheet" type="text/css" href="css/leaflet.css"/>
</head>
<body>
<noscript>
<div>
This site requires JavaScript. This message is only visible if you have it disabled. <br/><br/>
If you are using TOR browser set the "Security Level" to "Standard". If you keep seeing this message clear cache and storage of TOR browser app and retry.<br/><br/>
If the problem persists, ask for support in the RoboSats telegram group<a href="https://t.me/robosats"> (t.me/robosats)</a>
</div>
</noscript>
<div id="main">
<div id="app">
<div class="loaderCenter">
<div class="loaderSpinner"></div>
<div class="content-slider">
<div class="slider">
<div class="mask">
<ul>
<li class="anim1">
<div class="quote">Looking for robot parts ...</div>
</li>
<li class="anim2">
<div class="quote">Adding layers to the onion ...</div>
</li>
<li class="anim3">
<div class="quote">Winning at game theory ...</div>
</li>
<li class="anim4">
<div class="quote">Moving Sats at light speed ...</div>
</li>
<li class="anim5">
<div class="quote">Hiding in 2^256 bits of entropy...</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script src="js/main.js"></script>
</body>
</html>

View File

@ -1,63 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" href="/static/assets/images/favicon-96x96.png" />
<link rel="icon" type="image/png" href="/static/assets/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-192x192.png" sizes="192x192">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required.">
<title>RoboSats - Simple and Private Bitcoin Exchange</title>
<link rel="stylesheet" href="/static/css/fonts.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/loader.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/index.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/leaflet.css"/>
</head>
<body>
<noscript>
<div>
This site requires JavaScript. This message is only visible if you have it disabled. <br/><br/>
If you are using TOR browser set the "Security Level" to "Standard". If you keep seeing this message clear cache and storage of TOR browser app and retry.<br/><br/>
If the problem persists, ask for support in the RoboSats telegram group<a href="https://t.me/robosats"> (t.me/robosats)</a>
</div>
</noscript>
<div id="main">
<div id="app">
<div class="loaderCenter">
<div class="loaderSpinner"></div>
<div class="content-slider">
<div class="slider">
<div class="mask">
<ul>
<li class="anim1">
<div class="quote">Looking for robot parts ...</div>
</li>
<li class="anim2">
<div class="quote">Adding layers to the onion ...</div>
</li>
<li class="anim3">
<div class="quote">Winning at game theory ...</div>
</li>
<li class="anim4">
<div class="quote">Moving Sats at light speed ...</div>
</li>
<li class="anim5">
<div class="quote">Hiding in 2^256 bits of entropy...</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script>
window.RobosatsSettings = 'selfhosted-basic'
</script>
<script src="/static/frontend/main.js"></script>
</body>
</html>

View File

@ -1,19 +1,16 @@
version: '3.9' version: '3.9'
# Frontend and node client development orchestration # Frontend and node client development orchestration
name: robosats-frontend name: robosats-nodeapp-frontend
services: services:
frontend: frontend:
build: ../frontend build: ../frontend
container_name: npm-dev-frontend container_name: nodeapp-dev-frontend
restart: always restart: always
command: npm run dev command: npm run dev
volumes:
- ../frontend:/usr/src/frontend
- ../mobile:/usr/src/mobile
nodeapp: nginx:
build: . build: .
container_name: nodeapp-dev-frontend container_name: nodeapp-dev-nginx
restart: always restart: always
environment: environment:
TOR_PROXY_IP: 127.0.0.1 TOR_PROXY_IP: 127.0.0.1
@ -23,11 +20,10 @@ services:
- ./:/usr/src/robosats/ - ./:/usr/src/robosats/
- ./nginx.conf:/etc/nginx/nginx.conf - ./nginx.conf:/etc/nginx/nginx.conf
- ./coordinators/:/etc/nginx/conf.d/ - ./coordinators/:/etc/nginx/conf.d/
- ../frontend/static:/usr/src/robosats/static
tor: tor:
build: ../docker/tor build: ../docker/tor
container_name: tor-dev-frontend container_name: nodeapp-dev-tor
restart: always restart: always
environment: environment:
LOCAL_USER_ID: 1000 LOCAL_USER_ID: 1000

View File

@ -64,14 +64,6 @@ http {
autoindex on; autoindex on;
} }
location /nostr {
proxy_pass http://127.0.0.1:7777;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
location = /favicon.ico { location = /favicon.ico {
alias /usr/src/robosats/static/assets/images/favicon-96x96.png; alias /usr/src/robosats/static/assets/images/favicon-96x96.png;
} }

View File

@ -1,65 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" href="/static/assets/images/favicon-96x96.png" />
<link rel="icon" type="image/png" href="/static/assets/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-192x192.png" sizes="192x192">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required.">
<title>RoboSats PRO - Simple and Private Bitcoin Exchange</title>
<link rel="stylesheet" href="/static/css_pro/fonts.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/loader.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/index.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/leaflet.css"/>
<link rel="stylesheet" type="text/css" href="/css_pro/react-grid-layout.css"/>
<link rel="stylesheet" type="text/css" href="/css_pro/react-resizable.css"/>
</head>
<body>
<noscript>
<div>
This site requires JavaScript. This message is only visible if you have it disabled. <br/><br/>
If you are using TOR browser set the "Security Level" to "Standard". If you keep seeing this message clear cache and storage of TOR browser app and retry.<br/><br/>
If the problem persists, ask for support in the RoboSats telegram group<a href="https://t.me/robosats"> (t.me/robosats)</a>
</div>
</noscript>
<div id="main">
<div id="app">
<div class="loaderCenter">
<div class="loaderSpinner"></div>
<div class="content-slider">
<div class="slider">
<div class="mask">
<ul>
<li class="anim1">
<div class="quote">Looking for robot parts ...</div>
</li>
<li class="anim2">
<div class="quote">Adding layers to the onion ...</div>
</li>
<li class="anim3">
<div class="quote">Winning at game theory ...</div>
</li>
<li class="anim4">
<div class="quote">Moving Sats at light speed ...</div>
</li>
<li class="anim5">
<div class="quote">Hiding in 2^256 bits of entropy...</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script>
window.RobosatsSettings = 'selfhosted-pro'
</script>
<script src="/static/frontend/main.js"></script>
</body>
</html>

View File

@ -1,63 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" href="/static/assets/images/favicon-96x96.png" />
<link rel="icon" type="image/png" href="/static/assets/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-192x192.png" sizes="192x192">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required.">
<title>RoboSats - Simple and Private Bitcoin Exchange</title>
<link rel="stylesheet" href="/static/css/fonts.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/loader.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/index.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/leaflet.css"/>
</head>
<body>
<noscript>
<div>
This site requires JavaScript. This message is only visible if you have it disabled. <br/><br/>
If you are using TOR browser set the "Security Level" to "Standard". If you keep seeing this message clear cache and storage of TOR browser app and retry.<br/><br/>
If the problem persists, ask for support in the RoboSats telegram group<a href="https://t.me/robosats"> (t.me/robosats)</a>
</div>
</noscript>
<div id="main">
<div id="app">
<div class="loaderCenter">
<div class="loaderSpinner"></div>
<div class="content-slider">
<div class="slider">
<div class="mask">
<ul>
<li class="anim1">
<div class="quote">Looking for robot parts ...</div>
</li>
<li class="anim2">
<div class="quote">Adding layers to the onion ...</div>
</li>
<li class="anim3">
<div class="quote">Winning at game theory ...</div>
</li>
<li class="anim4">
<div class="quote">Moving Sats at light speed ...</div>
</li>
<li class="anim5">
<div class="quote">Hiding in 2^256 bits of entropy...</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script>
window.RobosatsSettings = 'web-basic'
</script>
<script src="/static/frontend/main.js"></script>
</body>
</html>

20
web/docker-compose.yml Normal file
View File

@ -0,0 +1,20 @@
version: '3.9'
# Frontend and node client development orchestration
name: robosats-web-frontend
services:
frontend:
build: ../frontend
container_name: web-dev-frontend
restart: always
command: npm run dev
nginx:
build: .
container_name: web-dev-nginx
restart: always
volumes:
- ./:/usr/src/robosats/
- ./nginx.conf:/etc/nginx/nginx.conf
- ./coordinators/:/etc/nginx/conf.d/
ports:
- 80:80

View File

@ -1,65 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" href="/static/assets/images/favicon-96x96.png" />
<link rel="icon" type="image/png" href="/static/assets/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/static/assets/images/favicon-192x192.png" sizes="192x192">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="A simple and private way to exchange bitcoin for national currencies. Robosats simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. No user registration required.">
<title>RoboSats PRO - Simple and Private Bitcoin Exchange</title>
<link rel="stylesheet" href="/static/css_pro/fonts.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/loader.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/index.css"/>
<link rel="stylesheet" type="text/css" href="/static/css/leaflet.css"/>
<link rel="stylesheet" type="text/css" href="/css_pro/react-grid-layout.css"/>
<link rel="stylesheet" type="text/css" href="/css_pro/react-resizable.css"/>
</head>
<body>
<noscript>
<div>
This site requires JavaScript. This message is only visible if you have it disabled. <br/><br/>
If you are using TOR browser set the "Security Level" to "Standard". If you keep seeing this message clear cache and storage of TOR browser app and retry.<br/><br/>
If the problem persists, ask for support in the RoboSats telegram group<a href="https://t.me/robosats"> (t.me/robosats)</a>
</div>
</noscript>
<div id="main">
<div id="app">
<div class="loaderCenter">
<div class="loaderSpinner"></div>
<div class="content-slider">
<div class="slider">
<div class="mask">
<ul>
<li class="anim1">
<div class="quote">Looking for robot parts ...</div>
</li>
<li class="anim2">
<div class="quote">Adding layers to the onion ...</div>
</li>
<li class="anim3">
<div class="quote">Winning at game theory ...</div>
</li>
<li class="anim4">
<div class="quote">Moving Sats at light speed ...</div>
</li>
<li class="anim5">
<div class="quote">Hiding in 2^256 bits of entropy...</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script>
window.RobosatsSettings = 'web-pro'
</script>
<script src="/static/frontend/main.js"></script>
</body>
</html>