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 73 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
73 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
a5f2f8b
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 3, 2024
79f648f
use env variable instead of https agent for default fetch
MikesGlitch Nov 4, 2024
2265164
updating root ca impl to use node env variable for more support
MikesGlitch Nov 4, 2024
4400ff1
release notes
MikesGlitch Nov 4, 2024
98174bb
removing node-fetch
MikesGlitch Nov 4, 2024
26adfec
clean up
MikesGlitch Nov 4, 2024
8cfd39f
error message
MikesGlitch Nov 4, 2024
4dd1fe0
merg
MikesGlitch Nov 4, 2024
78fbebe
adding the sync server into the workspace as an experiment
MikesGlitch Nov 4, 2024
5886678
merge
MikesGlitch Nov 4, 2024
6513207
update server
MikesGlitch Nov 4, 2024
99b6cb2
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 7, 2024
4036b57
responsive
MikesGlitch Nov 7, 2024
c718e67
test
MikesGlitch Nov 7, 2024
2a1bb2c
put it back
MikesGlitch Nov 7, 2024
8847f64
fix rootpath
MikesGlitch Nov 9, 2024
0ce8c57
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 11, 2024
8037e03
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 12, 2024
6671e2c
merge
MikesGlitch Nov 12, 2024
207d63e
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 14, 2024
b539e7b
merge
MikesGlitch Nov 19, 2024
99a97c6
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 19, 2024
2656d65
bits
MikesGlitch Nov 20, 2024
3f70a9f
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 21, 2024
64b007f
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 22, 2024
3ada82a
update server
MikesGlitch Nov 23, 2024
53f1054
yarn lock
MikesGlitch Nov 23, 2024
2d27e1d
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 24, 2024
f14a118
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Nov 30, 2024
3e3d1cf
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Dec 2, 2024
862c765
Merge branch 'master' of https://github.com/MikesGlitch/actual into e…
MikesGlitch Dec 6, 2024
c2aad50
updated server
MikesGlitch Dec 6, 2024
606fe7e
update the lock
MikesGlitch Dec 6, 2024
851f1ef
merge
MikesGlitch Dec 14, 2024
f57dac2
merge
MikesGlitch Dec 23, 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
9 changes: 8 additions & 1 deletion bin/package-electron
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,16 @@ 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

yarn workspace @actual-app/web build --mode=desktop
# 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 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 @@ -153,6 +153,9 @@ global.Actual = {
openURLInBrowser: url => {
window.open(url, '_blank');
},
downloadActualServer: () => {},
startActualServer: () => {},
exposeActualServer: () => {},
onEventFromMain: () => {},
isUpdateReadyForDownload: () => isUpdateReadyForDownload,
waitForUpdateReadyForDownload: () => isUpdateReadyForDownloadPromise,
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