Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] :electron: hosting the sync-server with the desktop app (POC) #3631

Draft
wants to merge 38 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
20e2ef2
start to this epic idea
MikesGlitch Oct 10, 2024
361200e
bits
MikesGlitch Oct 10, 2024
c718b2c
add a note
MikesGlitch Oct 10, 2024
e32fb8b
server starting button erroring on imports due to env differences
MikesGlitch Oct 11, 2024
1802818
working now
MikesGlitch Oct 11, 2024
194f50d
using node_modules and allowing electron-builder to bundle it
MikesGlitch Oct 12, 2024
7782f2d
ensuring deps are built before running server
MikesGlitch Oct 12, 2024
55e9113
unneeded mode
MikesGlitch Oct 12, 2024
3266319
exposing sync server via ngrok
MikesGlitch Oct 13, 2024
b3e6d28
log msg for dev
MikesGlitch Oct 13, 2024
ce5225e
using official ngrok package for smaller size
MikesGlitch Oct 14, 2024
07e7730
comment
MikesGlitch Oct 14, 2024
b9c14dc
ngrok bits
MikesGlitch Oct 14, 2024
c6d2f59
correcting ngrok conditional logic
MikesGlitch Oct 14, 2024
8eb145f
ngrok settings optional
MikesGlitch Oct 14, 2024
23fdf04
bits
MikesGlitch Oct 14, 2024
58aa350
moving server folders
MikesGlitch Oct 15, 2024
2dc34de
package process
MikesGlitch Oct 15, 2024
4873c9c
merge
MikesGlitch Oct 15, 2024
5d01674
what an ordeal
MikesGlitch Oct 15, 2024
8eca643
fixes
MikesGlitch Oct 17, 2024
b83a3c6
reenable sync server now tests have passed
MikesGlitch Oct 17, 2024
39d060b
splitting up server management to allow config of desktop app server …
MikesGlitch Oct 18, 2024
45d9fae
fix the port thing
MikesGlitch Oct 20, 2024
0a333ed
tunnel erorrs cleanup
MikesGlitch Oct 20, 2024
a78ad88
trickle in
MikesGlitch Oct 20, 2024
804425f
resetting state if not available to avoid hard crashes
MikesGlitch Oct 21, 2024
a1de454
small updates until I figure out what the issue is
MikesGlitch Oct 21, 2024
e1c5a68
merge
MikesGlitch Oct 22, 2024
1178bec
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Oct 23, 2024
17f520f
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Oct 24, 2024
6d0363f
settings
MikesGlitch Oct 27, 2024
7d5af1f
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Oct 27, 2024
f3c2743
remove it
MikesGlitch Oct 27, 2024
76b2918
thoughts
MikesGlitch Oct 27, 2024
9ff112c
waiting on sync server before starting
MikesGlitch Oct 28, 2024
312e85e
fix timeout message
MikesGlitch Oct 30, 2024
4f667d0
note
MikesGlitch Oct 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion bin/package-electron
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,17 @@ if [ "$OSTYPE" == "msys" ]; then
fi
fi

# TODO: should do these async if possible

# required for when running in electron
yarn workspace @actual-app/crdt build
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crdt is a dependency of actual-server. Build it before packaging the sync-server

yarn workspace loot-core build:node
yarn workspace @actual-app/web build --mode=desktop # electron specific build

# required for when running in web server (exposed via ngrok)
yarn workspace loot-core build:browser
yarn workspace @actual-app/web build:browser

yarn workspace @actual-app/web build --mode=desktop

yarn workspace desktop-electron update-client

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"start": "yarn start:browser",
"start:desktop": "yarn rebuild-electron && npm-run-all --parallel 'start:desktop-*'",
"start:desktop-node": "yarn workspace loot-core watch:node",
"start:desktop-server-dependencies": "yarn workspace @actual-app/crdt build",
"start:desktop-client": "yarn workspace @actual-app/web watch",
"start:desktop-electron": "yarn workspace desktop-electron watch",
"start:electron": "yarn start:desktop",
Expand Down
1 change: 1 addition & 0 deletions packages/desktop-client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ playwright-report

# production
build
build-electron
build-stats
stats.json

Expand Down
3 changes: 3 additions & 0 deletions packages/desktop-client/src/browser-preload.browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ global.Actual = {
openURLInBrowser: url => {
window.open(url, '_blank');
},
downloadActualServer: () => {},
startActualServer: () => {},
exposeActualServer: () => {},
onEventFromMain: () => {},
applyAppUpdate: () => {},
updateAppMenu: () => {},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
// @ts-strict-ignore
import React, { useState, useEffect, useCallback } from 'react';
import { Trans, useTranslation } from 'react-i18next';

import {
isNonProductionEnvironment,
isElectron,
} from 'loot-core/src/shared/environment';

import { useActions } from '../../hooks/useActions';
import { useGlobalPref } from '../../hooks/useGlobalPref';
import { useNavigate } from '../../hooks/useNavigate';
import { theme } from '../../style';
import { Button, ButtonWithLoading } from '../common/Button2';
import { BigInput } from '../common/Input';
import { Link } from '../common/Link';
import { Text } from '../common/Text';
import { View } from '../common/View';
import { useServerURL, useSetServerURL } from '../ServerContext';

import { Title } from './subscribe/common';

export function ConfigExternalSyncServer() {
const { t } = useTranslation();
const { createBudget, signOut, loggedIn } = useActions();
const navigate = useNavigate();
const [url, setUrl] = useState('');
const currentUrl = useServerURL();
const setServerUrl = useSetServerURL();
useEffect(() => {
setUrl(currentUrl);
}, [currentUrl]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const restartElectronServer = useCallback(() => {
globalThis.window.Actual.restartElectronServer();
setError(null);
}, []);

const [_serverSelfSignedCert, setServerSelfSignedCert] = useGlobalPref(
'serverSelfSignedCert',
restartElectronServer,
);

function getErrorMessage(error: string) {
switch (error) {
case 'network-failure':
return t(
'Server is not running at this URL. Make sure you have HTTPS set up properly.',
);
default:
return t(
'Server does not look like an Actual server. Is it set up correctly?',
);
}
}

async function onSubmit() {
if (url === '' || loading) {
return;
}

setError(null);
setLoading(true);
const { error } = await setServerUrl(url);

if (
['network-failure', 'get-server-failure'].includes(error) &&
!url.startsWith('http://') &&
!url.startsWith('https://')
) {
const { error } = await setServerUrl('https://' + url);
if (error) {
setUrl('https://' + url);
setError(error);
} else {
await signOut();
navigate('/');
}
setLoading(false);
} else if (error) {
setLoading(false);
setError(error);
} else {
setLoading(false);
await signOut();
navigate('/');
}
}

function onSameDomain() {
setUrl(window.location.origin);
}

async function onSelectSelfSignedCertificate() {
const selfSignedCertificateLocation = await window.Actual?.openFileDialog({
properties: ['openFile'],
filters: [
{
name: 'Self Signed Certificate',
extensions: ['crt', 'pem'],
},
],
});

if (selfSignedCertificateLocation) {
setServerSelfSignedCert(selfSignedCertificateLocation[0]);
}
}

async function onSkip() {
await setServerUrl(null);
await loggedIn();
navigate('/');
}

async function onCreateTestFile() {
await setServerUrl(null);
await createBudget({ testMode: true });
window.__navigate('/');
}

if (isElectron()) {
}

return (
<View style={{ maxWidth: 500, marginTop: -30 }}>
<Title text={t('Server configuration')} />

<Text
style={{
fontSize: 16,
color: theme.tableRowHeaderText,
lineHeight: 1.5,
}}
>
{currentUrl ? (
<Trans>
Existing sessions will be logged out and you will log in to this
server. We will validate that Actual is running at this URL.
</Trans>
) : (
<Trans>
There is no server configured. After running the server, specify the
URL here to use the app. You can always change this later. We will
validate that Actual is running at this URL.
</Trans>
)}
</Text>

{error && (
<>
<Text
style={{
marginTop: 20,
color: theme.errorText,
borderRadius: 4,
fontSize: 15,
}}
>
{getErrorMessage(error)}
</Text>
{isElectron() && (
<View
style={{ display: 'flex', flexDirection: 'row', marginTop: 20 }}
>
<Text
style={{
color: theme.errorText,
borderRadius: 4,
fontSize: 15,
}}
>
<Trans>
If the server is using a self-signed certificate{' '}
<Link
variant="text"
style={{ fontSize: 15 }}
onClick={onSelectSelfSignedCertificate}
>
select it here
</Link>
.
</Trans>
</Text>
</View>
)}
</>
)}

<View style={{ display: 'flex', flexDirection: 'row', marginTop: 30 }}>
<BigInput
autoFocus={true}
placeholder={t('https://example.com')}
value={url || ''}
onChangeValue={setUrl}
style={{ flex: 1, marginRight: 10 }}
onEnter={onSubmit}
/>
<ButtonWithLoading
variant="primary"
isLoading={loading}
style={{ fontSize: 15 }}
onPress={onSubmit}
>
{t('OK')}
</ButtonWithLoading>
{currentUrl && (
<Button
variant="bare"
style={{ fontSize: 15, marginLeft: 10 }}
onPress={() => navigate(-1)}
>
{t('Cancel')}
</Button>
)}
</View>

<View
style={{
flexDirection: 'row',
flexFlow: 'row wrap',
justifyContent: 'center',
marginTop: 15,
}}
>
{currentUrl ? (
<Button
variant="bare"
style={{ color: theme.pageTextLight }}
onPress={onSkip}
>
{t('Stop using a server')}
</Button>
) : (
<>
{!isElectron() && (
<Button
variant="bare"
style={{
color: theme.pageTextLight,
margin: 5,
marginRight: 15,
}}
onPress={onSameDomain}
>
{t('Use current domain')}
</Button>
)}
<Button
variant="bare"
style={{ color: theme.pageTextLight, margin: 5 }}
onPress={onSkip}
>
{t('Don’t use a server')}
</Button>

{isNonProductionEnvironment() && (
<Button
variant="primary"
style={{ marginLeft: 15 }}
onPress={onCreateTestFile}
>
{t('Create test file')}
</Button>
)}
</>
)}
</View>
</View>
);
}
Loading
Loading