@accounter/scraper-app 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/docs/plan.md +76 -0
- package/index.html +12 -0
- package/package.json +40 -0
- package/src/env.template +2 -0
- package/src/server/__tests__/accounts-routes.test.ts +133 -0
- package/src/server/__tests__/check-accounts.test.ts +305 -0
- package/src/server/__tests__/filter-payload.test.ts +193 -0
- package/src/server/__tests__/graphql-client.integration.test.ts +98 -0
- package/src/server/__tests__/graphql-client.test.ts +508 -0
- package/src/server/__tests__/healthz.test.ts +22 -0
- package/src/server/__tests__/history.test.ts +111 -0
- package/src/server/__tests__/otp-manager.test.ts +132 -0
- package/src/server/__tests__/scrape-runner.test.ts +144 -0
- package/src/server/__tests__/settings-routes.test.ts +117 -0
- package/src/server/__tests__/sources-routes.test.ts +149 -0
- package/src/server/__tests__/validate-payload.test.ts +193 -0
- package/src/server/__tests__/vault-routes.test.ts +174 -0
- package/src/server/__tests__/vault.test.ts +33 -0
- package/src/server/__tests__/websocket.test.ts +151 -0
- package/src/server/account-discovery.ts +49 -0
- package/src/server/accounts-routes.ts +74 -0
- package/src/server/check-accounts.ts +79 -0
- package/src/server/filter-payload.ts +145 -0
- package/src/server/graphql/client.ts +103 -0
- package/src/server/graphql/mutations.ts +518 -0
- package/src/server/history-routes.ts +11 -0
- package/src/server/history.ts +53 -0
- package/src/server/index.ts +40 -0
- package/src/server/otp-manager.ts +63 -0
- package/src/server/payload-schemas/amex.schema.ts +2 -0
- package/src/server/payload-schemas/cal.schema.ts +27 -0
- package/src/server/payload-schemas/currency-rates.schema.ts +11 -0
- package/src/server/payload-schemas/discount.schema.ts +26 -0
- package/src/server/payload-schemas/isracard.schema.ts +58 -0
- package/src/server/payload-schemas/max.schema.ts +27 -0
- package/src/server/payload-schemas/poalim-foreign.schema.ts +30 -0
- package/src/server/payload-schemas/poalim-ils.schema.ts +31 -0
- package/src/server/payload-schemas/poalim-swift.schema.ts +21 -0
- package/src/server/scrape-runner.ts +165 -0
- package/src/server/scrapers/__tests__/amex.test.ts +142 -0
- package/src/server/scrapers/__tests__/cal.test.ts +135 -0
- package/src/server/scrapers/__tests__/currency-rates.test.ts +105 -0
- package/src/server/scrapers/__tests__/discount.test.ts +160 -0
- package/src/server/scrapers/__tests__/isracard.test.ts +142 -0
- package/src/server/scrapers/__tests__/max.test.ts +115 -0
- package/src/server/scrapers/__tests__/poalim.test.ts +154 -0
- package/src/server/scrapers/amex.ts +63 -0
- package/src/server/scrapers/cal.ts +56 -0
- package/src/server/scrapers/currency-rates.ts +64 -0
- package/src/server/scrapers/discount.ts +62 -0
- package/src/server/scrapers/isracard.ts +68 -0
- package/src/server/scrapers/max.ts +32 -0
- package/src/server/scrapers/poalim.ts +103 -0
- package/src/server/settings-routes.ts +27 -0
- package/src/server/sources-routes.ts +182 -0
- package/src/server/validate-payload.ts +74 -0
- package/src/server/vault-routes.ts +99 -0
- package/src/server/vault-store.ts +42 -0
- package/src/server/vault.ts +216 -0
- package/src/server/websocket.ts +454 -0
- package/src/shared/source-types.ts +10 -0
- package/src/shared/types.ts +20 -0
- package/src/shared/ws-protocol.ts +177 -0
- package/src/test-setup.ts +6 -0
- package/src/ui/__tests__/accounts-tab.test.tsx +134 -0
- package/src/ui/__tests__/config.test.tsx +99 -0
- package/src/ui/__tests__/history.test.tsx +94 -0
- package/src/ui/__tests__/run.test.tsx +195 -0
- package/src/ui/__tests__/settings-tab.test.tsx +79 -0
- package/src/ui/__tests__/sources-tab.test.tsx +139 -0
- package/src/ui/__tests__/vault-setup.test.tsx +105 -0
- package/src/ui/__tests__/vault-unlock.test.tsx +78 -0
- package/src/ui/app.tsx +109 -0
- package/src/ui/components/error-boundary.tsx +54 -0
- package/src/ui/components/otp-modal.tsx +82 -0
- package/src/ui/components/skeleton.tsx +58 -0
- package/src/ui/components/task-row.tsx +241 -0
- package/src/ui/contexts/vault-context.tsx +77 -0
- package/src/ui/lib/api.ts +117 -0
- package/src/ui/lib/ws.ts +137 -0
- package/src/ui/main.tsx +9 -0
- package/src/ui/screens/config/accounts-tab.tsx +185 -0
- package/src/ui/screens/config/config.tsx +163 -0
- package/src/ui/screens/config/settings-tab.tsx +167 -0
- package/src/ui/screens/config/source-forms.tsx +518 -0
- package/src/ui/screens/config/source-types.ts +91 -0
- package/src/ui/screens/config/sources-tab.tsx +176 -0
- package/src/ui/screens/history.tsx +234 -0
- package/src/ui/screens/run.tsx +266 -0
- package/src/ui/screens/vault-setup.tsx +120 -0
- package/src/ui/screens/vault-unlock.tsx +38 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +10 -0
- package/vite.config.ts +24 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { useState, type ReactElement } from 'react';
|
|
2
|
+
import { useVault } from '../contexts/vault-context.js';
|
|
3
|
+
|
|
4
|
+
type Step = 1 | 2 | 3;
|
|
5
|
+
|
|
6
|
+
export function VaultSetup(): ReactElement {
|
|
7
|
+
const vault = useVault();
|
|
8
|
+
const [step, setStep] = useState<Step>(1);
|
|
9
|
+
const [password, setPassword] = useState('');
|
|
10
|
+
const [confirm, setConfirm] = useState('');
|
|
11
|
+
const [serverUrl, setServerUrl] = useState('');
|
|
12
|
+
const [apiKey, setApiKey] = useState('');
|
|
13
|
+
const [localError, setLocalError] = useState<string | null>(null);
|
|
14
|
+
const [loading, setLoading] = useState(false);
|
|
15
|
+
|
|
16
|
+
function handleStep1(e: React.FormEvent) {
|
|
17
|
+
e.preventDefault();
|
|
18
|
+
if (password.length < 8) {
|
|
19
|
+
setLocalError('Password must be at least 8 characters.');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (password !== confirm) {
|
|
23
|
+
setLocalError('Passwords do not match.');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
setLocalError(null);
|
|
27
|
+
setStep(2);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function handleStep2(e: React.FormEvent) {
|
|
31
|
+
e.preventDefault();
|
|
32
|
+
setLocalError(null);
|
|
33
|
+
setStep(3);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function handleStep3(e: React.FormEvent) {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
setLoading(true);
|
|
39
|
+
await vault.create(password, serverUrl, apiKey);
|
|
40
|
+
setLoading(false);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div>
|
|
45
|
+
<h1>Setup Vault</h1>
|
|
46
|
+
|
|
47
|
+
{step === 1 && (
|
|
48
|
+
<form onSubmit={handleStep1}>
|
|
49
|
+
<h2>Step 1: Choose master password</h2>
|
|
50
|
+
<div>
|
|
51
|
+
<label htmlFor="setup-password">Master password</label>
|
|
52
|
+
<input
|
|
53
|
+
id="setup-password"
|
|
54
|
+
type="password"
|
|
55
|
+
value={password}
|
|
56
|
+
onChange={e => setPassword(e.target.value)}
|
|
57
|
+
autoFocus
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
<div>
|
|
61
|
+
<label htmlFor="setup-confirm">Confirm password</label>
|
|
62
|
+
<input
|
|
63
|
+
id="setup-confirm"
|
|
64
|
+
type="password"
|
|
65
|
+
value={confirm}
|
|
66
|
+
onChange={e => setConfirm(e.target.value)}
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
69
|
+
{localError && <p role="alert">{localError}</p>}
|
|
70
|
+
<button type="submit">Next</button>
|
|
71
|
+
</form>
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
{step === 2 && (
|
|
75
|
+
<form onSubmit={handleStep2}>
|
|
76
|
+
<h2>Step 2: Server connection</h2>
|
|
77
|
+
<div>
|
|
78
|
+
<label htmlFor="setup-server-url">Server URL</label>
|
|
79
|
+
<input
|
|
80
|
+
id="setup-server-url"
|
|
81
|
+
type="url"
|
|
82
|
+
value={serverUrl}
|
|
83
|
+
onChange={e => setServerUrl(e.target.value)}
|
|
84
|
+
autoFocus
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
<div>
|
|
88
|
+
<label htmlFor="setup-api-key">API key</label>
|
|
89
|
+
<input
|
|
90
|
+
id="setup-api-key"
|
|
91
|
+
type="text"
|
|
92
|
+
value={apiKey}
|
|
93
|
+
onChange={e => setApiKey(e.target.value)}
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
{localError && <p role="alert">{localError}</p>}
|
|
97
|
+
<button type="button" onClick={() => setStep(1)}>
|
|
98
|
+
Back
|
|
99
|
+
</button>
|
|
100
|
+
<button type="submit">Next</button>
|
|
101
|
+
</form>
|
|
102
|
+
)}
|
|
103
|
+
|
|
104
|
+
{step === 3 && (
|
|
105
|
+
<form onSubmit={handleStep3}>
|
|
106
|
+
<h2>Step 3: Confirm</h2>
|
|
107
|
+
<p>Server URL: {serverUrl || '(none)'}</p>
|
|
108
|
+
<p>API key: {apiKey ? '••••••••' : '(none)'}</p>
|
|
109
|
+
{vault.error && <p role="alert">{vault.error}</p>}
|
|
110
|
+
<button type="button" onClick={() => setStep(2)}>
|
|
111
|
+
Back
|
|
112
|
+
</button>
|
|
113
|
+
<button type="submit" disabled={loading}>
|
|
114
|
+
{loading ? 'Creating…' : 'Create Vault'}
|
|
115
|
+
</button>
|
|
116
|
+
</form>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useState, type ReactElement } from 'react';
|
|
2
|
+
import { useVault } from '../contexts/vault-context.js';
|
|
3
|
+
|
|
4
|
+
export function VaultUnlock(): ReactElement {
|
|
5
|
+
const vault = useVault();
|
|
6
|
+
const [password, setPassword] = useState('');
|
|
7
|
+
const [loading, setLoading] = useState(false);
|
|
8
|
+
|
|
9
|
+
async function handleSubmit(e: React.FormEvent) {
|
|
10
|
+
e.preventDefault();
|
|
11
|
+
setLoading(true);
|
|
12
|
+
await vault.unlock(password);
|
|
13
|
+
setLoading(false);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div>
|
|
18
|
+
<h1>Unlock Vault</h1>
|
|
19
|
+
<form onSubmit={handleSubmit}>
|
|
20
|
+
<div>
|
|
21
|
+
<label htmlFor="password">Master password</label>
|
|
22
|
+
<input
|
|
23
|
+
id="password"
|
|
24
|
+
type="password"
|
|
25
|
+
value={password}
|
|
26
|
+
onChange={e => setPassword(e.target.value)}
|
|
27
|
+
disabled={loading}
|
|
28
|
+
autoFocus
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
{vault.error && <p role="alert">{vault.error}</p>}
|
|
32
|
+
<button type="submit" disabled={loading}>
|
|
33
|
+
{loading ? 'Unlocking…' : 'Unlock'}
|
|
34
|
+
</button>
|
|
35
|
+
</form>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"outDir": "dist",
|
|
9
|
+
"paths": {
|
|
10
|
+
"@accounter/modern-poalim-scraper": ["../modern-poalim-scraper/src/index"]
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
|
14
|
+
"exclude": ["tsup.config.ts", "vite.config.ts"]
|
|
15
|
+
}
|
package/tsup.config.ts
ADDED
package/vite.config.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
|
|
4
|
+
const scraperServerPort = Number(process.env.PORT ?? 4001);
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [react()],
|
|
8
|
+
server: {
|
|
9
|
+
proxy: {
|
|
10
|
+
'/api': {
|
|
11
|
+
target: `http://localhost:${scraperServerPort}`,
|
|
12
|
+
changeOrigin: true,
|
|
13
|
+
},
|
|
14
|
+
'/ws': {
|
|
15
|
+
target: `ws://localhost:${scraperServerPort}`,
|
|
16
|
+
ws: true,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
build: {
|
|
21
|
+
outDir: 'dist/ui',
|
|
22
|
+
emptyOutDir: true,
|
|
23
|
+
},
|
|
24
|
+
});
|