@actual-app/sync-server 25.4.0-alpha.0
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/.dockerignore +12 -0
- package/README.md +19 -0
- package/app.js +11 -0
- package/babel.config.json +3 -0
- package/bin/@actual-app/sync-server +55 -0
- package/docker/alpine.Dockerfile +62 -0
- package/docker/ubuntu.Dockerfile +63 -0
- package/docker-compose.yml +29 -0
- package/jest.config.json +19 -0
- package/jest.global-setup.js +101 -0
- package/jest.global-teardown.js +6 -0
- package/migrations/1694360000000-create-folders.js +25 -0
- package/migrations/1694360479680-create-account-db.js +30 -0
- package/migrations/1694362247011-create-secret-table.js +16 -0
- package/migrations/1702667624000-rename-nordigen-secrets.js +19 -0
- package/migrations/1718889148000-openid.js +41 -0
- package/migrations/1719409568000-multiuser.js +116 -0
- package/package.json +64 -0
- package/src/account-db.js +239 -0
- package/src/accounts/openid.js +361 -0
- package/src/accounts/password.js +149 -0
- package/src/app-account.js +155 -0
- package/src/app-admin.js +410 -0
- package/src/app-admin.test.js +381 -0
- package/src/app-gocardless/README.md +198 -0
- package/src/app-gocardless/app-gocardless.js +274 -0
- package/src/app-gocardless/bank-factory.js +91 -0
- package/src/app-gocardless/banks/abanca_caglesmm.js +22 -0
- package/src/app-gocardless/banks/abnamro_abnanl2a.js +57 -0
- package/src/app-gocardless/banks/american_express_aesudef1.js +40 -0
- package/src/app-gocardless/banks/bancsabadell_bsabesbbb.js +31 -0
- package/src/app-gocardless/banks/bank.interface.ts +51 -0
- package/src/app-gocardless/banks/bank_of_ireland_b365_bofiie2d.js +39 -0
- package/src/app-gocardless/banks/bankinter_bkbkesmm.js +24 -0
- package/src/app-gocardless/banks/belfius_gkccbebb.js +17 -0
- package/src/app-gocardless/banks/berliner_sparkasse_beladebexxx.js +61 -0
- package/src/app-gocardless/banks/bnp_be_gebabebb.js +73 -0
- package/src/app-gocardless/banks/cbc_cregbebb.js +34 -0
- package/src/app-gocardless/banks/commerzbank_cobadeff.js +51 -0
- package/src/app-gocardless/banks/danskebank_dabno22.js +39 -0
- package/src/app-gocardless/banks/direkt_heladef1822.js +18 -0
- package/src/app-gocardless/banks/easybank_bawaatww.js +50 -0
- package/src/app-gocardless/banks/entercard_swednokk.js +40 -0
- package/src/app-gocardless/banks/fortuneo_ftnofrp1xxx.js +46 -0
- package/src/app-gocardless/banks/hype_hyeeit22.js +74 -0
- package/src/app-gocardless/banks/ing_ingbrobu.js +70 -0
- package/src/app-gocardless/banks/ing_ingddeff.js +47 -0
- package/src/app-gocardless/banks/ing_pl_ingbplpw.js +46 -0
- package/src/app-gocardless/banks/integration-bank.js +115 -0
- package/src/app-gocardless/banks/isybank_itbbitmm.js +18 -0
- package/src/app-gocardless/banks/kbc_kredbebb.js +33 -0
- package/src/app-gocardless/banks/lhv-lhvbee22.js +36 -0
- package/src/app-gocardless/banks/mbank_retail_brexplpw.js +56 -0
- package/src/app-gocardless/banks/nationwide_naiagb21.js +46 -0
- package/src/app-gocardless/banks/nbg_ethngraaxxx.js +51 -0
- package/src/app-gocardless/banks/norwegian_xx_norwnok1.js +74 -0
- package/src/app-gocardless/banks/revolut_revolt21.js +37 -0
- package/src/app-gocardless/banks/sandboxfinance_sfin0000.js +28 -0
- package/src/app-gocardless/banks/seb_kort_bank_ab.js +58 -0
- package/src/app-gocardless/banks/seb_privat.js +29 -0
- package/src/app-gocardless/banks/sparnord_spnodk22.js +24 -0
- package/src/app-gocardless/banks/spk_karlsruhe_karsde66.js +61 -0
- package/src/app-gocardless/banks/spk_marburg_biedenkopf_heladef1mar.js +30 -0
- package/src/app-gocardless/banks/spk_worms_alzey_ried_malade51wor.js +19 -0
- package/src/app-gocardless/banks/ssk_dusseldorf_dussdeddxxx.js +50 -0
- package/src/app-gocardless/banks/swedbank_habalv22.js +47 -0
- package/src/app-gocardless/banks/tests/abanca_caglesmm.spec.js +21 -0
- package/src/app-gocardless/banks/tests/abnamro_abnanl2a.spec.js +61 -0
- package/src/app-gocardless/banks/tests/bancsabadell_bsabesbbb.spec.js +53 -0
- package/src/app-gocardless/banks/tests/belfius_gkccbebb.spec.js +22 -0
- package/src/app-gocardless/banks/tests/cbc_cregbebb.spec.js +34 -0
- package/src/app-gocardless/banks/tests/commerzbank_cobadeff.spec.js +110 -0
- package/src/app-gocardless/banks/tests/easybank_bawaatww.spec.js +54 -0
- package/src/app-gocardless/banks/tests/fortuneo_ftnofrp1xxx.spec.js +206 -0
- package/src/app-gocardless/banks/tests/ing_ingddeff.spec.js +302 -0
- package/src/app-gocardless/banks/tests/ing_pl_ingbplpw.spec.js +202 -0
- package/src/app-gocardless/banks/tests/integration_bank.spec.js +158 -0
- package/src/app-gocardless/banks/tests/kbc_kredbebb.spec.js +38 -0
- package/src/app-gocardless/banks/tests/lhv-lhvbee22.spec.js +68 -0
- package/src/app-gocardless/banks/tests/mbank_retail_brexplpw.spec.js +171 -0
- package/src/app-gocardless/banks/tests/nationwide_naiagb21.spec.js +105 -0
- package/src/app-gocardless/banks/tests/nbg_ethngraaxxx.spec.js +48 -0
- package/src/app-gocardless/banks/tests/revolut_revolt21.spec.js +42 -0
- package/src/app-gocardless/banks/tests/sandboxfinance_sfin0000.spec.js +133 -0
- package/src/app-gocardless/banks/tests/spk_marburg_biedenkopf_heladef1mar.spec.js +256 -0
- package/src/app-gocardless/banks/tests/ssk_dusseldorf_dussdeddxxx.spec.js +102 -0
- package/src/app-gocardless/banks/tests/swedbank_habalv22.spec.js +57 -0
- package/src/app-gocardless/banks/tests/virgin_nrnbgb22.spec.js +54 -0
- package/src/app-gocardless/banks/util/extract-payeeName-from-remittanceInfo.js +36 -0
- package/src/app-gocardless/banks/virgin_nrnbgb22.js +39 -0
- package/src/app-gocardless/errors.js +84 -0
- package/src/app-gocardless/gocardless-node.types.ts +497 -0
- package/src/app-gocardless/gocardless.types.ts +93 -0
- package/src/app-gocardless/link.html +18 -0
- package/src/app-gocardless/services/gocardless-service.js +620 -0
- package/src/app-gocardless/services/tests/fixtures.js +181 -0
- package/src/app-gocardless/services/tests/gocardless-service.spec.js +537 -0
- package/src/app-gocardless/tests/bank-factory.spec.js +20 -0
- package/src/app-gocardless/tests/utils.spec.js +162 -0
- package/src/app-gocardless/util/handle-error.js +16 -0
- package/src/app-gocardless/utils.js +45 -0
- package/src/app-openid.js +108 -0
- package/src/app-pluggyai/app-pluggyai.js +215 -0
- package/src/app-pluggyai/pluggyai-service.js +120 -0
- package/src/app-secrets.js +61 -0
- package/src/app-simplefin/app-simplefin.js +418 -0
- package/src/app-sync/errors.js +13 -0
- package/src/app-sync/services/files-service.js +243 -0
- package/src/app-sync/tests/services/files-service.test.js +250 -0
- package/src/app-sync/validation.js +77 -0
- package/src/app-sync.js +391 -0
- package/src/app-sync.test.js +877 -0
- package/src/app.js +145 -0
- package/src/config-types.ts +44 -0
- package/src/db.js +58 -0
- package/src/load-config.js +307 -0
- package/src/migrations.js +36 -0
- package/src/run-migrations.js +8 -0
- package/src/scripts/disable-openid.js +44 -0
- package/src/scripts/enable-openid.js +53 -0
- package/src/scripts/health-check.js +23 -0
- package/src/scripts/reset-password.js +51 -0
- package/src/secrets.test.js +83 -0
- package/src/services/secrets-service.js +94 -0
- package/src/services/user-service.js +272 -0
- package/src/sql/messages.sql +9 -0
- package/src/sync-simple.js +95 -0
- package/src/util/hash.js +5 -0
- package/src/util/middlewares.js +62 -0
- package/src/util/paths.js +13 -0
- package/src/util/payee-name.js +45 -0
- package/src/util/prompt.js +88 -0
- package/src/util/title/index.js +59 -0
- package/src/util/title/lower-case.js +93 -0
- package/src/util/title/specials.js +21 -0
- package/src/util/validate-user.js +68 -0
- package/tsconfig.json +21 -0
package/.dockerignore
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
This is the main project to run [Actual](https://github.com/actualbudget/actual), a local-first personal finance tool. It comes with the latest version of Actual, and a server to persist changes and make data available across all devices.
|
|
2
|
+
|
|
3
|
+
### Getting Started
|
|
4
|
+
|
|
5
|
+
Actual is a local-first personal finance tool. It is 100% free and open-source, written in NodeJS, it has a synchronization element so that all your changes can move between devices without any heavy lifting.
|
|
6
|
+
|
|
7
|
+
If you are interested in contributing, or want to know how development works, see our [contributing](https://actualbudget.org/docs/contributing/) document we would love to have you.
|
|
8
|
+
|
|
9
|
+
Want to say thanks? Click the ⭐ at the top of the page.
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
We have a wide range of documentation on how to use Actual. This is all available in our [Community Documentation](https://actualbudget.org/docs/), including topics on [installing](https://actualbudget.org/docs/install/), [Budgeting](https://actualbudget.org/docs/budgeting/), [Account Management](https://actualbudget.org/docs/accounts/), [Tips & Tricks](https://actualbudget.org/docs/getting-started/tips-tricks) and some documentation for developers.
|
|
14
|
+
|
|
15
|
+
### Feature Requests
|
|
16
|
+
|
|
17
|
+
Current feature requests can be seen [here](https://github.com/actualbudget/actual/issues?q=is%3Aissue+label%3A%22needs+votes%22+sort%3Areactions-%2B1-desc). Vote for your favorite requests by reacting 👍 to the top comment of the request.
|
|
18
|
+
|
|
19
|
+
To add new feature requests, open a new Issue of the "Feature Request" type.
|
package/app.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { run as runMigrations } from './src/migrations.js';
|
|
2
|
+
|
|
3
|
+
runMigrations()
|
|
4
|
+
.then(() => {
|
|
5
|
+
//import the app here becasue initial migrations need to be run first - they are dependencies of the app.js
|
|
6
|
+
import('./src/app.js').then(app => app.run()); // run the app
|
|
7
|
+
})
|
|
8
|
+
.catch(err => {
|
|
9
|
+
console.log('Error starting app:', err);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
import packageJson from '../../package.json' with { type: 'json' };
|
|
5
|
+
|
|
6
|
+
const getArgs = () =>
|
|
7
|
+
process.argv.reduce((args, arg) => {
|
|
8
|
+
// long arg
|
|
9
|
+
if (arg.slice(0, 2) === '--') {
|
|
10
|
+
const longArg = arg.split('=');
|
|
11
|
+
const longArgFlag = longArg[0].slice(2);
|
|
12
|
+
const longArgValue = longArg.length > 1 ? longArg[1] : true;
|
|
13
|
+
args[longArgFlag] = longArgValue;
|
|
14
|
+
}
|
|
15
|
+
// flags
|
|
16
|
+
else if (arg[0] === '-') {
|
|
17
|
+
const flags = arg.slice(1).split('');
|
|
18
|
+
flags.forEach(flag => {
|
|
19
|
+
args[flag] = true;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return args;
|
|
23
|
+
}, {});
|
|
24
|
+
|
|
25
|
+
const args = getArgs();
|
|
26
|
+
|
|
27
|
+
if (args.h || args.help) {
|
|
28
|
+
console.log(
|
|
29
|
+
[
|
|
30
|
+
'usage: @actual-app/sync-server [options]',
|
|
31
|
+
'',
|
|
32
|
+
'options:',
|
|
33
|
+
' --config Path to config file',
|
|
34
|
+
'',
|
|
35
|
+
' -h --help Print this list and exit.',
|
|
36
|
+
' -v --version Print the version and exit.',
|
|
37
|
+
].join('\n'),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
process.exit();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (args.v || args.version) {
|
|
44
|
+
console.log('v' + packageJson.version);
|
|
45
|
+
|
|
46
|
+
process.exit();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// start the sync server
|
|
50
|
+
if (args.config) {
|
|
51
|
+
console.log(`Loading config from ${args.config}`);
|
|
52
|
+
process.env.ACTUAL_CONFIG_PATH = args.config;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
import('../../app.js');
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
FROM alpine:3.18 AS deps
|
|
2
|
+
|
|
3
|
+
# Install required packages
|
|
4
|
+
RUN apk add --no-cache nodejs yarn python3 openssl build-base
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Copy only the files needed for installing dependencies
|
|
9
|
+
COPY .yarn ./.yarn
|
|
10
|
+
COPY yarn.lock package.json .yarnrc.yml ./
|
|
11
|
+
COPY packages/api/package.json packages/api/package.json
|
|
12
|
+
COPY packages/component-library/package.json packages/component-library/package.json
|
|
13
|
+
COPY packages/crdt/package.json packages/crdt/package.json
|
|
14
|
+
COPY packages/desktop-client/package.json packages/desktop-client/package.json
|
|
15
|
+
COPY packages/desktop-electron/package.json packages/desktop-electron/package.json
|
|
16
|
+
COPY packages/eslint-plugin-actual/package.json packages/eslint-plugin-actual/package.json
|
|
17
|
+
COPY packages/loot-core/package.json packages/loot-core/package.json
|
|
18
|
+
COPY packages/sync-server/package.json packages/sync-server/package.json
|
|
19
|
+
|
|
20
|
+
# Avoiding memory issues with ARMv7
|
|
21
|
+
RUN if [ "$(uname -m)" = "armv7l" ]; then yarn config set taskPoolConcurrency 2; yarn config set networkConcurrency 5; fi
|
|
22
|
+
|
|
23
|
+
# Focus the workspaces in production mode
|
|
24
|
+
RUN if [ "$(uname -m)" = "armv7l" ]; then npm_config_build_from_source=true yarn workspaces focus @actual-app/sync-server --production; else yarn workspaces focus @actual-app/sync-server --production; fi
|
|
25
|
+
|
|
26
|
+
FROM deps AS builder
|
|
27
|
+
|
|
28
|
+
WORKDIR /app
|
|
29
|
+
|
|
30
|
+
COPY packages/sync-server ./packages/sync-server
|
|
31
|
+
|
|
32
|
+
# Remove symbolic links for @actual-app/web and @actual-app/sync-server
|
|
33
|
+
RUN rm -rf ./node_modules/@actual-app/web ./node_modules/@actual-app/sync-server
|
|
34
|
+
|
|
35
|
+
# Copy in the @actual-app/web artifacts manually, so we don't need the entire packages folder
|
|
36
|
+
COPY packages/desktop-client/package.json ./node_modules/@actual-app/web/package.json
|
|
37
|
+
COPY packages/desktop-client/build ./node_modules/@actual-app/web/build
|
|
38
|
+
|
|
39
|
+
FROM alpine:3.18 AS prod
|
|
40
|
+
|
|
41
|
+
# Minimal runtime dependencies
|
|
42
|
+
RUN apk add --no-cache nodejs tini
|
|
43
|
+
|
|
44
|
+
# Create a non-root user
|
|
45
|
+
ARG USERNAME=actual
|
|
46
|
+
ARG USER_UID=1001
|
|
47
|
+
ARG USER_GID=$USER_UID
|
|
48
|
+
RUN addgroup -S ${USERNAME} -g ${USER_GID} && adduser -S ${USERNAME} -G ${USERNAME} -u ${USER_UID}
|
|
49
|
+
RUN mkdir /data && chown -R ${USERNAME}:${USERNAME} /data
|
|
50
|
+
|
|
51
|
+
WORKDIR /app
|
|
52
|
+
ENV NODE_ENV=production
|
|
53
|
+
|
|
54
|
+
# Pull in only the necessary artifacts (built node_modules, server files, etc.)
|
|
55
|
+
COPY --from=builder /app/node_modules /app/node_modules
|
|
56
|
+
COPY --from=builder /app/packages/sync-server/package.json /app/packages/sync-server/app.js ./
|
|
57
|
+
COPY --from=builder /app/packages/sync-server/src ./src
|
|
58
|
+
COPY --from=builder /app/packages/sync-server/migrations ./migrations
|
|
59
|
+
|
|
60
|
+
ENTRYPOINT ["/sbin/tini","-g", "--"]
|
|
61
|
+
EXPOSE 5006
|
|
62
|
+
CMD ["node", "app.js"]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
FROM node:18-bookworm AS deps
|
|
2
|
+
|
|
3
|
+
# Install required packages
|
|
4
|
+
RUN apt-get update && apt-get install -y openssl
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Copy only the files needed for installing dependencies
|
|
9
|
+
COPY .yarn ./.yarn
|
|
10
|
+
COPY yarn.lock package.json .yarnrc.yml ./
|
|
11
|
+
COPY packages/api/package.json packages/api/package.json
|
|
12
|
+
COPY packages/component-library/package.json packages/component-library/package.json
|
|
13
|
+
COPY packages/crdt/package.json packages/crdt/package.json
|
|
14
|
+
COPY packages/desktop-client/package.json packages/desktop-client/package.json
|
|
15
|
+
COPY packages/desktop-electron/package.json packages/desktop-electron/package.json
|
|
16
|
+
COPY packages/eslint-plugin-actual/package.json packages/eslint-plugin-actual/package.json
|
|
17
|
+
COPY packages/loot-core/package.json packages/loot-core/package.json
|
|
18
|
+
COPY packages/sync-server/package.json packages/sync-server/package.json
|
|
19
|
+
|
|
20
|
+
# Avoiding memory issues with ARMv7
|
|
21
|
+
RUN if [ "$(uname -m)" = "armv7l" ]; then yarn config set taskPoolConcurrency 2; yarn config set networkConcurrency 5; fi
|
|
22
|
+
|
|
23
|
+
# Focus the workspaces in production mode
|
|
24
|
+
RUN yarn workspaces focus @actual-app/sync-server --production
|
|
25
|
+
|
|
26
|
+
FROM deps AS builder
|
|
27
|
+
|
|
28
|
+
WORKDIR /app
|
|
29
|
+
|
|
30
|
+
COPY packages/sync-server ./packages/sync-server
|
|
31
|
+
|
|
32
|
+
# Remove symbolic links for @actual-app/web and @actual-app/sync-server
|
|
33
|
+
RUN rm -rf ./node_modules/@actual-app/web ./node_modules/@actual-app/sync-server
|
|
34
|
+
|
|
35
|
+
# Copy in the @actual-app/web artifacts manually, so we don't need the entire packages folder
|
|
36
|
+
COPY packages/desktop-client/package.json ./node_modules/@actual-app/web/package.json
|
|
37
|
+
COPY packages/desktop-client/build ./node_modules/@actual-app/web/build
|
|
38
|
+
|
|
39
|
+
FROM node:18-bookworm-slim AS prod
|
|
40
|
+
|
|
41
|
+
# Minimal runtime dependencies
|
|
42
|
+
RUN apt-get update && apt-get install tini && apt-get clean -y && rm -rf /var/lib/apt/lists/*
|
|
43
|
+
|
|
44
|
+
# Create a non-root user
|
|
45
|
+
ARG USERNAME=actual
|
|
46
|
+
ARG USER_UID=1001
|
|
47
|
+
ARG USER_GID=$USER_UID
|
|
48
|
+
RUN groupadd --gid $USER_GID $USERNAME \
|
|
49
|
+
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
|
|
50
|
+
&& mkdir /data && chown -R ${USERNAME}:${USERNAME} /data
|
|
51
|
+
|
|
52
|
+
WORKDIR /app
|
|
53
|
+
ENV NODE_ENV=production
|
|
54
|
+
|
|
55
|
+
# Pull in only the necessary artifacts (built node_modules, server files, etc.)
|
|
56
|
+
COPY --from=builder /app/node_modules /app/node_modules
|
|
57
|
+
COPY --from=builder /app/packages/sync-server/package.json /app/packages/sync-server/app.js ./
|
|
58
|
+
COPY --from=builder /app/packages/sync-server/src ./src
|
|
59
|
+
COPY --from=builder /app/packages/sync-server/migrations ./migrations
|
|
60
|
+
|
|
61
|
+
ENTRYPOINT ["/usr/bin/tini","-g", "--"]
|
|
62
|
+
EXPOSE 5006
|
|
63
|
+
CMD ["node", "app.js"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
services:
|
|
2
|
+
actual_server:
|
|
3
|
+
image: docker.io/actualbudget/actual-server:latest
|
|
4
|
+
ports:
|
|
5
|
+
# This line makes Actual available at port 5006 of the device you run the server on,
|
|
6
|
+
# i.e. http://localhost:5006. You can change the first number to change the port, if you want.
|
|
7
|
+
- '5006:5006'
|
|
8
|
+
environment:
|
|
9
|
+
# Uncomment any of the lines below to set configuration options.
|
|
10
|
+
# - ACTUAL_HTTPS_KEY=/data/selfhost.key
|
|
11
|
+
# - ACTUAL_HTTPS_CERT=/data/selfhost.crt
|
|
12
|
+
# - ACTUAL_PORT=5006
|
|
13
|
+
# - ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB=20
|
|
14
|
+
# - ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB=50
|
|
15
|
+
# - ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB=20
|
|
16
|
+
# See all options and more details at https://actualbudget.github.io/docs/Installing/Configuration
|
|
17
|
+
# !! If you are not using any of these options, remove the 'environment:' tag entirely.
|
|
18
|
+
volumes:
|
|
19
|
+
# Change './actual-data' below to the path to the folder you want Actual to store its data in on your server.
|
|
20
|
+
# '/data' is the path Actual will look for its files in by default, so leave that as-is.
|
|
21
|
+
- ./actual-data:/data
|
|
22
|
+
healthcheck:
|
|
23
|
+
# Enable health check for the instance
|
|
24
|
+
test: ['CMD-SHELL', 'node src/scripts/health-check.js']
|
|
25
|
+
interval: 60s
|
|
26
|
+
timeout: 10s
|
|
27
|
+
retries: 3
|
|
28
|
+
start_period: 20s
|
|
29
|
+
restart: unless-stopped
|
package/jest.config.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"globalSetup": "./jest.global-setup.js",
|
|
3
|
+
"globalTeardown": "./jest.global-teardown.js",
|
|
4
|
+
"testPathIgnorePatterns": ["dist", "/node_modules/", "/build/"],
|
|
5
|
+
"roots": ["<rootDir>"],
|
|
6
|
+
"moduleFileExtensions": ["ts", "js", "json"],
|
|
7
|
+
"testEnvironment": "node",
|
|
8
|
+
"collectCoverage": true,
|
|
9
|
+
"collectCoverageFrom": ["**/*.{js,ts,tsx}"],
|
|
10
|
+
"coveragePathIgnorePatterns": [
|
|
11
|
+
"dist",
|
|
12
|
+
"/node_modules/",
|
|
13
|
+
"/build/",
|
|
14
|
+
"/coverage/"
|
|
15
|
+
],
|
|
16
|
+
"coverageReporters": ["html", "lcov", "text", "text-summary"],
|
|
17
|
+
"resetMocks": true,
|
|
18
|
+
"restoreMocks": true
|
|
19
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { getAccountDb } from './src/account-db.js';
|
|
2
|
+
import { run as runMigrations } from './src/migrations.js';
|
|
3
|
+
|
|
4
|
+
const GENERIC_ADMIN_ID = 'genericAdmin';
|
|
5
|
+
const GENERIC_USER_ID = 'genericUser';
|
|
6
|
+
const ADMIN_ROLE_ID = 'ADMIN';
|
|
7
|
+
const BASIC_ROLE_ID = 'BASIC';
|
|
8
|
+
|
|
9
|
+
const createUser = (userId, userName, role, owner = 0, enabled = 1) => {
|
|
10
|
+
const missingParams = [];
|
|
11
|
+
if (!userId) missingParams.push('userId');
|
|
12
|
+
if (!userName) missingParams.push('userName');
|
|
13
|
+
if (!role) missingParams.push('role');
|
|
14
|
+
if (missingParams.length > 0) {
|
|
15
|
+
throw new Error(`Missing required parameters: ${missingParams.join(', ')}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (
|
|
19
|
+
typeof userId !== 'string' ||
|
|
20
|
+
typeof userName !== 'string' ||
|
|
21
|
+
typeof role !== 'string'
|
|
22
|
+
) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
'Invalid parameter types. userId, userName, and role must be strings',
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
getAccountDb().mutate(
|
|
30
|
+
'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, ?, ?, ?)',
|
|
31
|
+
[userId, userName, userName, enabled, owner, role],
|
|
32
|
+
);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(`Error creating user ${userName}:`, error);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const setSessionUser = (userId, token = 'valid-token') => {
|
|
40
|
+
if (!userId) {
|
|
41
|
+
throw new Error('userId is required');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const db = getAccountDb();
|
|
46
|
+
const session = db.first('SELECT token FROM sessions WHERE token = ?', [
|
|
47
|
+
token,
|
|
48
|
+
]);
|
|
49
|
+
if (!session) {
|
|
50
|
+
throw new Error(`Session not found for token: ${token}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
db.mutate('UPDATE sessions SET user_id = ? WHERE token = ?', [
|
|
54
|
+
userId,
|
|
55
|
+
token,
|
|
56
|
+
]);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error(`Error updating session for user ${userId}:`, error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// eslint-disable-next-line import/no-default-export
|
|
64
|
+
export default async function setup() {
|
|
65
|
+
const NEVER_EXPIRES = -1; // or consider using a far future timestamp
|
|
66
|
+
|
|
67
|
+
await runMigrations();
|
|
68
|
+
|
|
69
|
+
createUser(GENERIC_ADMIN_ID, 'admin', ADMIN_ROLE_ID, 1);
|
|
70
|
+
|
|
71
|
+
// Insert a fake "valid-token" fixture that can be reused
|
|
72
|
+
const db = getAccountDb();
|
|
73
|
+
try {
|
|
74
|
+
await db.mutate('BEGIN TRANSACTION');
|
|
75
|
+
|
|
76
|
+
await db.mutate('DELETE FROM sessions');
|
|
77
|
+
await db.mutate(
|
|
78
|
+
'INSERT INTO sessions (token, expires_at, user_id) VALUES (?, ?, ?)',
|
|
79
|
+
['valid-token', NEVER_EXPIRES, 'genericAdmin'],
|
|
80
|
+
);
|
|
81
|
+
await db.mutate(
|
|
82
|
+
'INSERT INTO sessions (token, expires_at, user_id) VALUES (?, ?, ?)',
|
|
83
|
+
['valid-token-admin', NEVER_EXPIRES, 'genericAdmin'],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
await db.mutate(
|
|
87
|
+
'INSERT INTO sessions (token, expires_at, user_id) VALUES (?, ?, ?)',
|
|
88
|
+
['valid-token-user', NEVER_EXPIRES, 'genericUser'],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
await db.mutate('COMMIT');
|
|
92
|
+
} catch (error) {
|
|
93
|
+
await db.mutate('ROLLBACK');
|
|
94
|
+
throw new Error(`Failed to setup test sessions: ${error.message}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
setSessionUser('genericAdmin');
|
|
98
|
+
setSessionUser('genericAdmin', 'valid-token-admin');
|
|
99
|
+
|
|
100
|
+
createUser(GENERIC_USER_ID, 'user', BASIC_ROLE_ID, 1);
|
|
101
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import { config } from '../src/load-config.js';
|
|
4
|
+
|
|
5
|
+
async function ensureExists(path) {
|
|
6
|
+
try {
|
|
7
|
+
await fs.mkdir(path);
|
|
8
|
+
} catch (err) {
|
|
9
|
+
if (err.code === 'EEXIST') {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
throw err;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const up = async function () {
|
|
18
|
+
await ensureExists(config.get('serverFiles'));
|
|
19
|
+
await ensureExists(config.get('userFiles'));
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const down = async function () {
|
|
23
|
+
await fs.rm(config.get('serverFiles'), { recursive: true, force: true });
|
|
24
|
+
await fs.rm(config.get('userFiles'), { recursive: true, force: true });
|
|
25
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getAccountDb } from '../src/account-db.js';
|
|
2
|
+
|
|
3
|
+
export const up = async function () {
|
|
4
|
+
await getAccountDb().exec(`
|
|
5
|
+
CREATE TABLE IF NOT EXISTS auth
|
|
6
|
+
(password TEXT PRIMARY KEY);
|
|
7
|
+
|
|
8
|
+
CREATE TABLE IF NOT EXISTS sessions
|
|
9
|
+
(token TEXT PRIMARY KEY);
|
|
10
|
+
|
|
11
|
+
CREATE TABLE IF NOT EXISTS files
|
|
12
|
+
(id TEXT PRIMARY KEY,
|
|
13
|
+
group_id TEXT,
|
|
14
|
+
sync_version SMALLINT,
|
|
15
|
+
encrypt_meta TEXT,
|
|
16
|
+
encrypt_keyid TEXT,
|
|
17
|
+
encrypt_salt TEXT,
|
|
18
|
+
encrypt_test TEXT,
|
|
19
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
20
|
+
name TEXT);
|
|
21
|
+
`);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const down = async function () {
|
|
25
|
+
await getAccountDb().exec(`
|
|
26
|
+
DROP TABLE auth;
|
|
27
|
+
DROP TABLE sessions;
|
|
28
|
+
DROP TABLE files;
|
|
29
|
+
`);
|
|
30
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getAccountDb } from '../src/account-db.js';
|
|
2
|
+
|
|
3
|
+
export const up = async function () {
|
|
4
|
+
await getAccountDb().exec(`
|
|
5
|
+
CREATE TABLE IF NOT EXISTS secrets (
|
|
6
|
+
name TEXT PRIMARY KEY,
|
|
7
|
+
value BLOB
|
|
8
|
+
);
|
|
9
|
+
`);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const down = async function () {
|
|
13
|
+
await getAccountDb().exec(`
|
|
14
|
+
DROP TABLE secrets;
|
|
15
|
+
`);
|
|
16
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getAccountDb } from '../src/account-db.js';
|
|
2
|
+
|
|
3
|
+
export const up = async function () {
|
|
4
|
+
await getAccountDb().exec(
|
|
5
|
+
`UPDATE secrets SET name = 'gocardless_secretId' WHERE name = 'nordigen_secretId'`,
|
|
6
|
+
);
|
|
7
|
+
await getAccountDb().exec(
|
|
8
|
+
`UPDATE secrets SET name = 'gocardless_secretKey' WHERE name = 'nordigen_secretKey'`,
|
|
9
|
+
);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const down = async function () {
|
|
13
|
+
await getAccountDb().exec(
|
|
14
|
+
`UPDATE secrets SET name = 'nordigen_secretId' WHERE name = 'gocardless_secretId'`,
|
|
15
|
+
);
|
|
16
|
+
await getAccountDb().exec(
|
|
17
|
+
`UPDATE secrets SET name = 'nordigen_secretKey' WHERE name = 'gocardless_secretKey'`,
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { getAccountDb } from '../src/account-db.js';
|
|
2
|
+
|
|
3
|
+
export const up = async function () {
|
|
4
|
+
await getAccountDb().exec(
|
|
5
|
+
`
|
|
6
|
+
BEGIN TRANSACTION;
|
|
7
|
+
CREATE TABLE auth_new
|
|
8
|
+
(method TEXT PRIMARY KEY,
|
|
9
|
+
display_name TEXT,
|
|
10
|
+
extra_data TEXT, active INTEGER);
|
|
11
|
+
|
|
12
|
+
INSERT INTO auth_new (method, display_name, extra_data, active)
|
|
13
|
+
SELECT 'password', 'Password', password, 1 FROM auth;
|
|
14
|
+
DROP TABLE auth;
|
|
15
|
+
ALTER TABLE auth_new RENAME TO auth;
|
|
16
|
+
|
|
17
|
+
CREATE TABLE pending_openid_requests
|
|
18
|
+
(state TEXT PRIMARY KEY,
|
|
19
|
+
code_verifier TEXT,
|
|
20
|
+
return_url TEXT,
|
|
21
|
+
expiry_time INTEGER);
|
|
22
|
+
COMMIT;`,
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const down = async function () {
|
|
27
|
+
await getAccountDb().exec(
|
|
28
|
+
`
|
|
29
|
+
BEGIN TRANSACTION;
|
|
30
|
+
ALTER TABLE auth RENAME TO auth_temp;
|
|
31
|
+
CREATE TABLE auth
|
|
32
|
+
(password TEXT);
|
|
33
|
+
INSERT INTO auth (password)
|
|
34
|
+
SELECT extra_data FROM auth_temp WHERE method = 'password';
|
|
35
|
+
DROP TABLE auth_temp;
|
|
36
|
+
|
|
37
|
+
DROP TABLE pending_openid_requests;
|
|
38
|
+
COMMIT;
|
|
39
|
+
`,
|
|
40
|
+
);
|
|
41
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
|
|
3
|
+
import { getAccountDb } from '../src/account-db.js';
|
|
4
|
+
|
|
5
|
+
export const up = async function () {
|
|
6
|
+
const accountDb = getAccountDb();
|
|
7
|
+
|
|
8
|
+
accountDb.transaction(() => {
|
|
9
|
+
accountDb.exec(
|
|
10
|
+
`
|
|
11
|
+
CREATE TABLE users
|
|
12
|
+
(id TEXT PRIMARY KEY,
|
|
13
|
+
user_name TEXT,
|
|
14
|
+
display_name TEXT,
|
|
15
|
+
role TEXT,
|
|
16
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
17
|
+
owner INTEGER NOT NULL DEFAULT 0);
|
|
18
|
+
|
|
19
|
+
CREATE TABLE user_access
|
|
20
|
+
(user_id TEXT,
|
|
21
|
+
file_id TEXT,
|
|
22
|
+
PRIMARY KEY (user_id, file_id),
|
|
23
|
+
FOREIGN KEY (user_id) REFERENCES users(id),
|
|
24
|
+
FOREIGN KEY (file_id) REFERENCES files(id)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
ALTER TABLE files
|
|
28
|
+
ADD COLUMN owner TEXT;
|
|
29
|
+
|
|
30
|
+
ALTER TABLE sessions
|
|
31
|
+
ADD COLUMN expires_at INTEGER;
|
|
32
|
+
|
|
33
|
+
ALTER TABLE sessions
|
|
34
|
+
ADD COLUMN user_id TEXT;
|
|
35
|
+
|
|
36
|
+
ALTER TABLE sessions
|
|
37
|
+
ADD COLUMN auth_method TEXT;
|
|
38
|
+
`,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const userId = uuidv4();
|
|
42
|
+
accountDb.mutate(
|
|
43
|
+
'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, 1, 1, ?)',
|
|
44
|
+
[userId, '', '', 'ADMIN'],
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
accountDb.mutate(
|
|
48
|
+
'UPDATE sessions SET user_id = ?, expires_at = ?, auth_method = ? WHERE auth_method IS NULL',
|
|
49
|
+
[userId, -1, 'password'],
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const down = async function () {
|
|
55
|
+
await getAccountDb().exec(
|
|
56
|
+
`
|
|
57
|
+
BEGIN TRANSACTION;
|
|
58
|
+
|
|
59
|
+
DROP TABLE IF EXISTS user_access;
|
|
60
|
+
|
|
61
|
+
CREATE TABLE sessions_backup (
|
|
62
|
+
token TEXT PRIMARY KEY
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
INSERT INTO sessions_backup (token)
|
|
66
|
+
SELECT token FROM sessions;
|
|
67
|
+
|
|
68
|
+
DROP TABLE sessions;
|
|
69
|
+
|
|
70
|
+
ALTER TABLE sessions_backup RENAME TO sessions;
|
|
71
|
+
|
|
72
|
+
CREATE TABLE files_backup (
|
|
73
|
+
id TEXT PRIMARY KEY,
|
|
74
|
+
group_id TEXT,
|
|
75
|
+
sync_version SMALLINT,
|
|
76
|
+
encrypt_meta TEXT,
|
|
77
|
+
encrypt_keyid TEXT,
|
|
78
|
+
encrypt_salt TEXT,
|
|
79
|
+
encrypt_test TEXT,
|
|
80
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
81
|
+
name TEXT
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
INSERT INTO files_backup (
|
|
85
|
+
id,
|
|
86
|
+
group_id,
|
|
87
|
+
sync_version,
|
|
88
|
+
encrypt_meta,
|
|
89
|
+
encrypt_keyid,
|
|
90
|
+
encrypt_salt,
|
|
91
|
+
encrypt_test,
|
|
92
|
+
deleted,
|
|
93
|
+
name
|
|
94
|
+
)
|
|
95
|
+
SELECT
|
|
96
|
+
id,
|
|
97
|
+
group_id,
|
|
98
|
+
sync_version,
|
|
99
|
+
encrypt_meta,
|
|
100
|
+
encrypt_keyid,
|
|
101
|
+
encrypt_salt,
|
|
102
|
+
encrypt_test,
|
|
103
|
+
deleted,
|
|
104
|
+
name
|
|
105
|
+
FROM files;
|
|
106
|
+
|
|
107
|
+
DROP TABLE files;
|
|
108
|
+
|
|
109
|
+
ALTER TABLE files_backup RENAME TO files;
|
|
110
|
+
|
|
111
|
+
DROP TABLE IF EXISTS users;
|
|
112
|
+
|
|
113
|
+
COMMIT;
|
|
114
|
+
`,
|
|
115
|
+
);
|
|
116
|
+
};
|