Enable Typescript in sync-server (#4887)

* attempt at running with typescript

* release notes

* working jest tests for TS files

* working docker image build

* remaining docker images

* cleanup

* ensure vitest is working

* get tests passing in ci

* less strict

* update release notes

* use tsc compiled assets in the published package

* scripts

* update yarn.lock

* Use build path for electron app

* PR feedback: move sync-server build out of bin/build-browser

* PR feedback: undo moduleResolution change

* extend main tsconfig and fix types

* PR feedback on scripts and when the sync-server build runs

* fix lint (unrelated change)

---------

Co-authored-by: alecbakholdin <alecbakholdin@gmail.com>
This commit is contained in:
Roger Goldfinger
2025-05-05 20:45:49 -07:00
committed by GitHub
parent 4a7439da5e
commit 090345bd95
22 changed files with 71 additions and 64 deletions

View File

@@ -57,7 +57,7 @@ jobs:
- name: Set up environment
uses: ./.github/actions/setup
- name: Build Web
run: ./bin/package-browser
run: yarn build:browser
- name: Upload Build
uses: actions/upload-artifact@v4
with:
@@ -76,7 +76,7 @@ jobs:
- name: Set up environment
uses: ./.github/actions/setup
- name: Build Server
run: cd packages/sync-server && yarn build
run: yarn workspace @actual-app/sync-server build
- name: Upload Build
uses: actions/upload-artifact@v4
with:

View File

@@ -78,7 +78,7 @@ jobs:
- name: Set up environment
uses: ./.github/actions/setup
- name: Build Web
run: ./bin/package-browser
run: yarn build:server
- name: Build and push image
uses: docker/build-push-action@v5

View File

@@ -75,7 +75,7 @@ jobs:
- name: Set up environment
uses: ./.github/actions/setup
- name: Build Web
run: ./bin/package-browser
run: yarn build:server
- name: Build and push ubuntu image
uses: docker/build-push-action@v5

View File

@@ -30,7 +30,7 @@ jobs:
run: npm install netlify-cli@17.10.1 -g
- name: Build Actual
run: ./bin/package-browser
run: yarn build:browser
- name: Deploy to Netlify
id: netlify_deploy

View File

@@ -17,7 +17,7 @@ jobs:
uses: ./.github/actions/setup
- name: Build Web
run: yarn build:browser
run: yarn build:server
- name: Pack the web and server packages
run: |

View File

@@ -45,6 +45,7 @@ yarn workspace @actual-app/web build --mode=desktop # electron specific build
# required for running the sync-server server
yarn workspace loot-core build:browser
yarn workspace @actual-app/web build:browser
yarn workspace @actual-app/sync-server build
yarn workspace desktop-electron update-client

View File

@@ -31,7 +31,7 @@
"start:browser": "npm-run-all --parallel 'start:browser-*'",
"start:browser-backend": "yarn workspace loot-core watch:browser",
"start:browser-frontend": "yarn workspace @actual-app/web start:browser",
"build:server": "yarn build:browser",
"build:server": "yarn build:browser && yarn workspace @actual-app/sync-server build",
"build:browser": "./bin/package-browser",
"build:desktop": "./bin/package-electron",
"build:api": "yarn workspace @actual-app/api build",

View File

@@ -228,6 +228,7 @@ async function startSyncServer() {
const serverPath = path.join(
// require.resolve will recursively search up the workspace for the module
path.dirname(require.resolve('@actual-app/sync-server/package.json')),
'build',
'app.js',
);

View File

@@ -53,9 +53,8 @@ ENV NODE_ENV=production
# Pull in only the necessary artifacts (built node_modules, server files, etc.)
COPY --from=builder /app/node_modules /app/node_modules
COPY --from=builder /app/packages/sync-server/package.json /app/packages/sync-server/app.js ./
COPY --from=builder /app/packages/sync-server/src ./src
COPY --from=builder /app/packages/sync-server/migrations ./migrations
COPY --from=builder /app/packages/sync-server/package.json ./
COPY --from=builder /app/packages/sync-server/build ./
ENTRYPOINT ["/sbin/tini","-g", "--"]
EXPOSE 5006

View File

@@ -54,9 +54,8 @@ ENV NODE_ENV=production
# Pull in only the necessary artifacts (built node_modules, server files, etc.)
COPY --from=builder /app/node_modules /app/node_modules
COPY --from=builder /app/packages/sync-server/package.json /app/packages/sync-server/app.js ./
COPY --from=builder /app/packages/sync-server/src ./src
COPY --from=builder /app/packages/sync-server/migrations ./migrations
COPY --from=builder /app/packages/sync-server/package.json ./
COPY --from=builder /app/packages/sync-server/build ./
ENTRYPOINT ["/usr/bin/tini","-g", "--"]
EXPOSE 5006

View File

@@ -4,30 +4,27 @@
"license": "MIT",
"description": "actual syncing server",
"bin": {
"actual-server": "./bin/actual-server.js"
"actual-server": "./build/bin/actual-server.js"
},
"type": "module",
"files": [
"bin",
"src",
"app.js",
"migrations",
"build",
"default-db.sqlite",
"README.md",
"LICENSE"
],
"scripts": {
"start": "node app",
"start-monitor": "nodemon app",
"start": "yarn build && node build/app",
"start-monitor": "nodemon --exec 'tsc && node build/app' --ignore './build/**/*' --ext 'ts,js' build/app",
"build": "tsc",
"test": "NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --trace-warnings' vitest",
"db:migrate": "NODE_ENV=development node src/run-migrations.js up",
"db:downgrade": "NODE_ENV=development node src/run-migrations.js down",
"db:test-migrate": "NODE_ENV=test node src/run-migrations.js up",
"db:test-downgrade": "NODE_ENV=test node src/run-migrations.js down",
"reset-password": "node src/scripts/reset-password.js",
"disable-openid": "node src/scripts/disable-openid.js",
"health-check": "node src/scripts/health-check.js"
"db:migrate": "yarn build && cross-env NODE_ENV=development node build/src/scripts/run-migrations.js up",
"db:downgrade": "yarn build && cross-env NODE_ENV=development node build/src/scripts/run-migrations.js down",
"db:test-migrate": "yarn build && cross-env NODE_ENV=test node build/src/scripts/run-migrations.js up",
"db:test-downgrade": "yarn build && cross-env NODE_ENV=test node build/src/scripts/run-migrations.js down",
"reset-password": "yarn build && node build/src/scripts/reset-password.js",
"disable-openid": "yarn build && node build/src/scripts/disable-openid.js",
"health-check": "yarn build && node build/src/scripts/health-check.js"
},
"dependencies": {
"@actual-app/crdt": "2.1.0",

View File

@@ -1,3 +1,4 @@
// @ts-strict-ignore
import crypto from 'node:crypto';
import fs from 'node:fs';

View File

@@ -1,3 +1,4 @@
// @ts-strict-ignore
import { Buffer } from 'node:buffer';
import fs from 'node:fs/promises';
@@ -55,7 +56,7 @@ const verifyFileExists = (fileId, filesService, res, errorObject) => {
}
};
app.post('/sync', async (req, res) => {
app.post('/sync', async (req, res): Promise<void> => {
let requestPb;
try {
requestPb = SyncProtoBuf.SyncRequest.deserializeBinary(req.body);
@@ -73,11 +74,12 @@ app.post('/sync', async (req, res) => {
const messages = requestPb.getMessagesList();
if (!since) {
return res.status(422).send({
res.status(422).send({
details: 'since-required',
reason: 'unprocessable-entity',
status: 'error',
});
return;
}
const filesService = new FilesService(getAccountDb());
@@ -373,11 +375,12 @@ app.post('/delete-user-file', (req, res) => {
const { fileId } = req.body;
if (!fileId) {
return res.status(422).send({
res.status(422).send({
details: 'fileId-required',
reason: 'unprocessable-entity',
status: 'error',
});
return;
}
const filesService = new FilesService(getAccountDb());

View File

@@ -88,7 +88,6 @@ if (process.env.NODE_ENV === 'development') {
target: 'http://localhost:3001',
changeOrigin: true,
ws: true,
logLevel: 'debug',
}),
);
} else {
@@ -100,7 +99,7 @@ if (process.env.NODE_ENV === 'development') {
);
}
function parseHTTPSConfig(value) {
function parseHTTPSConfig(value: string) {
if (value.startsWith('-----BEGIN')) {
return value;
}
@@ -108,9 +107,13 @@ function parseHTTPSConfig(value) {
}
export async function run() {
const portVal = config.get('port');
const port = typeof portVal === 'string' ? parseInt(portVal) : portVal;
const hostname = config.get('hostname');
const openIdConfig = config?.getProperties()?.openId;
if (
openIdConfig?.discoveryURL ||
// @ts-expect-error FIXME no types for config yet
openIdConfig?.issuer?.authorization_endpoint
) {
console.log('OpenID configuration found. Preparing server to use it');
@@ -129,18 +132,17 @@ export async function run() {
if (config.get('https.key') && config.get('https.cert')) {
const https = await import('node:https');
const httpsOptions = {
...config.https,
...config.get('https'),
key: parseHTTPSConfig(config.get('https.key')),
cert: parseHTTPSConfig(config.get('https.cert')),
};
https
.createServer(httpsOptions, app)
.listen(config.get('port'), config.get('hostname'));
https.createServer(httpsOptions, app).listen(port, hostname);
} else {
app.listen(config.get('port'), config.get('hostname'));
app.listen(port, hostname);
}
// Signify to any parent process that the server has started. Used in electron desktop app
// @ts-ignore-error electron types
process.parentPort?.postMessage({ type: 'server-started' });
console.log(

View File

@@ -10,7 +10,9 @@ const require = createRequire(import.meta.url);
const debug = createDebug('actual:config');
const debugSensitive = createDebug('actual-sensitive:config');
const projectRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
const projectRoot = path
.dirname(path.dirname(fileURLToPath(import.meta.url)))
.replace(/[\\/]build$/, '');
const defaultDataDir = process.env.ACTUAL_DATA_DIR
? process.env.ACTUAL_DATA_DIR
: fs.existsSync('/data')

View File

@@ -15,7 +15,7 @@ export function run(direction = 'up') {
stateStore: `${path.join(config.get('dataDir'), '.migrate')}${
config.get('mode') === 'test' ? '-test' : ''
}`,
migrationsDirectory: `${path.join(config.get('projectRoot'), 'migrations')}`,
migrationsDirectory: `${path.join(config.get('projectRoot'), config.get('mode') === 'test' ? '' : 'build', 'migrations')}`,
},
(err, set) => {
if (err) {

View File

@@ -1,4 +1,4 @@
import { run } from './migrations.js';
import { run } from '../migrations.js';
const direction = process.argv[2] || 'up';

View File

@@ -1,22 +1,19 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"target": "ES2022",
// DOM for URL global in Node 16+
"lib": ["ES2021"],
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"resolveJsonModule": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "preserve",
// Check JS files too
"allowJs": true,
"moduleResolution": "node16",
"module": "node16",
"outDir": "build",
"types": ["vite/client", "vitest/globals"]
"moduleResolution": "node16",
"noEmit": false,
"outDir": "build"
},
"include": ["src/**/*.js", "types/global.d.ts"],
"exclude": ["node_modules", "build", "./app-plaid.js", "coverage"]
"include": [
"src/**/*",
"migrations/**/*",
"types/**/*",
"app.ts",
"bin/**/*"
],
"exclude": ["node_modules", "build", "coverage"]
}

View File

@@ -1,4 +1,4 @@
FROM node:18-bookworm as deps
FROM node:18-bookworm AS deps
# Install required packages
RUN apt-get update && apt-get install -y openssl
@@ -21,12 +21,12 @@ COPY ./bin/package-browser ./bin/package-browser
RUN yarn install
FROM deps as builder
FROM deps AS builder
WORKDIR /app
COPY packages/ ./packages/
RUN yarn build:browser
RUN yarn build:server
# Focus the workspaces in production mode (including @actual-app/web you just built)
RUN yarn workspaces focus @actual-app/sync-server --production
@@ -38,7 +38,7 @@ RUN rm -rf ./node_modules/@actual-app/web ./node_modules/@actual-app/sync-server
COPY ./packages/desktop-client/package.json ./node_modules/@actual-app/web/package.json
RUN cp -r ./packages/desktop-client/build ./node_modules/@actual-app/web/build
FROM node:18-bookworm-slim as prod
FROM node:18-bookworm-slim AS prod
# Minimal runtime dependencies
RUN apt-get update && apt-get install -y tini && apt-get clean -y && rm -rf /var/lib/apt/lists/*
@@ -56,10 +56,9 @@ ENV NODE_ENV=production
# Pull in only the necessary artifacts (built node_modules, server files, etc.)
COPY --from=builder /app/node_modules /app/node_modules
COPY --from=builder /app/packages/sync-server/package.json /app/packages/sync-server/app.js ./
COPY --from=builder /app/packages/sync-server/src ./src
COPY --from=builder /app/packages/sync-server/migrations ./migrations
COPY --from=builder /app/packages/sync-server/package.json ./
COPY --from=builder /app/packages/sync-server/build ./build
ENTRYPOINT ["/usr/bin/tini", "-g", "--"]
EXPOSE 5006
CMD ["node", "app.js"]
CMD ["node", "build/app.js"]

View File

@@ -0,0 +1,6 @@
---
category: Enhancements
authors: [rgoldfinger]
---
Converted sync-server to run with typescript

View File

@@ -121,7 +121,7 @@ __metadata:
vitest: "npm:^3.0.2"
winston: "npm:^3.17.0"
bin:
actual-server: ./bin/actual-server.js
actual-server: ./build/bin/actual-server.js
languageName: unknown
linkType: soft