diff --git a/.dockerignore b/.dockerignore index 6c3840719..64bcb6983 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,3 +5,6 @@ /docs /npm-debug.log node_modules + +src/**/*.ts +!src/services/asset_path.ts \ No newline at end of file diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml new file mode 100644 index 000000000..392046d2c --- /dev/null +++ b/.github/workflows/dev.yml @@ -0,0 +1,25 @@ +name: Dev +on: + push: +jobs: + build_docker: + name: Build Docker image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up node & dependencies + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: "npm" + - run: npm ci + - name: Run the TypeScript build + run: npx tsc + - name: Create server-package.json + run: cat package.json | grep -v electron > server-package.json + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v6 + with: + context: . + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..529e685e9 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,131 @@ +name: Main +on: + push: + branches: + - 'develop' +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} +jobs: + build_darwin-x64: + name: Build macOS x86_64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up node & dependencies + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: "npm" + - run: npm ci + - run: ./bin/build-mac-x64.sh + - uses: actions/upload-artifact@v4 + with: + name: trilium-mac-x64.zip + path: dist/trilium-mac-x64*.zip + build_linux-x64: + name: Build Linux x86_64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up node & dependencies + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: "npm" + - run: npm ci + - run: ./bin/build-linux-x64.sh + - uses: actions/upload-artifact@v4 + with: + name: trilium-linux-x64.tar.xz + path: dist/trilium-linux-x64-*.tar.xz + - uses: actions/upload-artifact@v4 + with: + name: trilium_amd64.deb + path: dist/trilium_*.deb + build_linux_server-x64: + name: Build Linux Server x86_64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up node & dependencies + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: "npm" + - run: npm ci + - run: ./bin/build-server.sh + - uses: actions/upload-artifact@v4 + with: + name: trilium-linux-x64-server.tar.xz + path: dist/trilium-linux-x64-server-*.tar.xz + build_windows-x64: + name: Build Windows x86_64 + runs-on: ubuntu-latest + steps: + - name: Set up Wine + run: | + sudo dpkg --add-architecture i386 + wget -qO - https://dl.winehq.org/wine-builds/winehq.key | sudo apt-key add - + sudo add-apt-repository ppa:cybermax-dexter/sdl2-backport + sudo apt-add-repository "deb https://dl.winehq.org/wine-builds/ubuntu $(lsb_release -cs) main" + sudo apt install --install-recommends winehq-stable + - uses: actions/checkout@v4 + - name: Set up node & dependencies + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: "npm" + - run: npm ci + - run: ./bin/build-win-x64.sh + - uses: actions/upload-artifact@v4 + with: + name: trilium-windows-x64.zip + path: dist/trilium-windows-x64-*.zip + build_docker: + name: Build Docker image + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + attestations: write + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + - name: Set up node & dependencies + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: "npm" + - run: npm ci + - name: Run the TypeScript build + run: npx tsc + - name: Create server-package.json + run: cat package.json | grep -v electron > server-package.json + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v6 + id: push + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows_old/codeql-analysis.yml similarity index 100% rename from .github/workflows/codeql-analysis.yml rename to .github/workflows_old/codeql-analysis.yml diff --git a/.github/workflows/docker.yaml b/.github/workflows_old/docker.yaml similarity index 100% rename from .github/workflows/docker.yaml rename to .github/workflows_old/docker.yaml diff --git a/.gitignore b/.gitignore index 0e095b075..2a427512e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store node_modules/ dist/ +build/ src/public/app-dist/ npm-debug.log yarn-error.log diff --git a/Dockerfile b/Dockerfile index d8cb9b0c3..78f058529 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,18 @@ # !!! Don't try to build this Dockerfile directly, run it through bin/build-docker.sh script !!! FROM node:18.18.2-alpine +# Configure system dependencies +RUN apk add --no-cache --virtual .build-dependencies \ + autoconf \ + automake \ + g++ \ + gcc \ + libtool \ + make \ + nasm \ + libpng-dev \ + python3 + # Create app directory WORKDIR /usr/src/app @@ -9,25 +21,21 @@ COPY . . COPY server-package.json package.json +# Copy TypeScript build artifacts into the original directory structure. +RUN ls +RUN cp -R build/src/* src/. +RUN rm -r build + # Install app dependencies RUN set -x \ - && apk add --no-cache --virtual .build-dependencies \ - autoconf \ - automake \ - g++ \ - gcc \ - libtool \ - make \ - nasm \ - libpng-dev \ - python3 \ && npm install \ && apk del .build-dependencies \ && npm run webpack \ && npm prune --omit=dev \ && cp src/public/app/share.js src/public/app-dist/. \ && cp -r src/public/app/doc_notes src/public/app-dist/. \ - && rm -rf src/public/app + && rm -rf src/public/app \ + && rm src/services/asset_path.ts # Some setup tools need to be kept RUN apk add --no-cache su-exec shadow diff --git a/bin/build-docker.sh b/bin/build-docker.sh index c77850eba..2b3794677 100755 --- a/bin/build-docker.sh +++ b/bin/build-docker.sh @@ -5,6 +5,9 @@ SERIES=${VERSION:0:4}-latest cat package.json | grep -v electron > server-package.json +echo "Compiling typescript..." +npx tsc + sudo docker build -t zadam/trilium:$VERSION --network host -t zadam/trilium:$SERIES . if [[ $VERSION != *"beta"* ]]; then diff --git a/bin/copy-trilium.sh b/bin/copy-trilium.sh index 9fdfe0293..8affb7be3 100755 --- a/bin/copy-trilium.sh +++ b/bin/copy-trilium.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +shopt -s globstar +set -e + if [[ $# -eq 0 ]] ; then echo "Missing argument of target directory" exit 1 @@ -9,25 +12,36 @@ if ! [[ $(which npm) ]]; then exit 1 fi -n exec 18.18.2 npm run webpack || npm run webpack +# Trigger the TypeScript build +echo TypeScript build start +npx tsc +echo TypeScript build finished +# Copy the TypeScript artifacts DIR="$1" - rm -rf "$DIR" mkdir -pv "$DIR" +echo Webpack start +npm run webpack +echo Webpack finish + echo "Copying Trilium to build directory $DIR" for d in 'images' 'libraries' 'src' 'db'; do cp -r "$d" "$DIR"/ done -for f in 'package.json' 'package-lock.json' 'README.md' 'LICENSE' 'config-sample.ini' 'electron.js'; do + +for f in 'package.json' 'package-lock.json' 'README.md' 'LICENSE' 'config-sample.ini'; do cp "$f" "$DIR"/ done -cp webpack-* "$DIR"/ # here warning because there is no 'webpack-*', but webpack.config.js only + +script_dir=$(realpath $(dirname $0)) +cp -Rv "$script_dir/../build/src" "$DIR" +cp "$script_dir/../build/electron.js" "$DIR" # run in subshell (so we return to original dir) -(cd $DIR && n exec 18.18.2 npm install --only=prod) +(cd $DIR && npm install --only=prod) if [[ -d "$DIR"/node_modules ]]; then # cleanup of useless files in dependencies diff --git a/package.json b/package.json index 7183b9867..4e37e4c7d 100644 --- a/package.json +++ b/package.json @@ -22,13 +22,12 @@ "start-test-server": "npm run qswitch-server; rm -rf ./data-test; cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data-test TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev TRILIUM_PORT=9999 ts-node src/www.ts", "switch-server": "rm -rf ./node_modules/better-sqlite3 && npm install", "switch-electron": "./node_modules/.bin/electron-rebuild", - "rebuild": "electron-rebuild", "qswitch-server": "rm -rf ./node_modules/better-sqlite3/bin ; mkdir -p ./node_modules/better-sqlite3/build ; cp ./bin/better-sqlite3/linux-server-better_sqlite3.node ./node_modules/better-sqlite3/build/better_sqlite3.node", "qswitch-electron": "rm -rf ./node_modules/better-sqlite3/bin ; mkdir -p ./node_modules/better-sqlite3/build ; cp ./bin/better-sqlite3/linux-desktop-better_sqlite3.node ./node_modules/better-sqlite3/build/better_sqlite3.node", "build-backend-docs": "rm -rf ./docs/backend_api && ./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/becca/entities/*.js src/services/backend_script_api.js src/services/sql.js", "build-frontend-docs": "rm -rf ./docs/frontend_api && ./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js", "build-docs": "npm run build-backend-docs && npm run build-frontend-docs", - "webpack": "webpack -c webpack.config.js", + "webpack": "webpack -c webpack.config.ts", "test-jasmine": "TRILIUM_DATA_DIR=~/trilium/data-test jasmine", "test-es6": "node -r esm spec-es6/attribute_parser.spec.js ", "test": "npm run test-jasmine && npm run test-es6", diff --git a/src/becca/entities/rows.ts b/src/becca/entities/rows.ts index 4428b6dde..119d392ec 100644 --- a/src/becca/entities/rows.ts +++ b/src/becca/entities/rows.ts @@ -91,7 +91,8 @@ export interface BranchRow { * end user. Those types should be used only for checking against, they are * not for direct use. */ -export type NoteType = ("file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code"); +export const ALLOWED_NOTE_TYPES = [ "file", "image", "search", "noteMap", "launcher", "doc", "contentWidget", "text", "relationMap", "render", "canvas", "mermaid", "book", "webView", "code" ] as const; +export type NoteType = typeof ALLOWED_NOTE_TYPES[number]; export interface NoteRow { noteId: string; @@ -106,5 +107,5 @@ export interface NoteRow { dateModified: string; utcDateCreated: string; utcDateModified: string; - content?: string; + content?: string | Buffer; } diff --git a/src/services/import/zip.ts b/src/services/import/zip.ts index 93ccc2da9..0c4008ddf 100644 --- a/src/services/import/zip.ts +++ b/src/services/import/zip.ts @@ -20,7 +20,7 @@ import BNote = require('../../becca/entities/bnote'); import NoteMeta = require('../meta/note_meta'); import AttributeMeta = require('../meta/attribute_meta'); import { Stream } from 'stream'; -import { NoteType } from '../../becca/entities/rows'; +import { ALLOWED_NOTE_TYPES, NoteType } from '../../becca/entities/rows'; interface MetaFile { files: NoteMeta[] @@ -499,10 +499,6 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo } } else { - if (typeof content !== "string") { - throw new Error("Incorrect content type."); - } - ({note} = noteService.createNewNote({ parentNoteId: parentNoteId, title: noteTitle || "", @@ -653,7 +649,11 @@ function resolveNoteType(type: string | undefined): NoteType { return 'webView'; } - return "text"; + if (type && (ALLOWED_NOTE_TYPES as readonly string[]).includes(type)) { + return type as NoteType; + } else { + return "text"; + } } export = { diff --git a/src/services/note-interface.ts b/src/services/note-interface.ts index 0b99c31ef..2cfa0b86a 100644 --- a/src/services/note-interface.ts +++ b/src/services/note-interface.ts @@ -7,7 +7,7 @@ export interface NoteParams { parentNoteId: string; templateNoteId?: string; title: string; - content: string; + content: string | Buffer; /** text, code, file, image, search, book, relationMap, canvas, webView */ type: NoteType; /** default value is derived from default mimes for type */ diff --git a/src/services/search/expressions/note_content_fulltext.ts b/src/services/search/expressions/note_content_fulltext.ts index ada9705a2..02626bf16 100644 --- a/src/services/search/expressions/note_content_fulltext.ts +++ b/src/services/search/expressions/note_content_fulltext.ts @@ -74,7 +74,7 @@ class NoteContentFulltextExp extends Expression { } if (isProtected) { - if (!protectedSessionService.isProtectedSessionAvailable() || !content) { + if (!protectedSessionService.isProtectedSessionAvailable() || !content || typeof content !== "string") { return; } @@ -86,7 +86,7 @@ class NoteContentFulltextExp extends Expression { } } - if (!content) { + if (!content || typeof content !== "string") { return; } diff --git a/tsconfig.json b/tsconfig.json index bf97f7be2..f0f1a3fc2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "moduleResolution": "Node", "declaration": false, "sourceMap": true, - "outDir": "./dist", + "outDir": "./build", "strict": true, "noImplicitAny": true, "resolveJsonModule": true, diff --git a/webpack.config.js b/webpack.config.ts similarity index 74% rename from webpack.config.js rename to webpack.config.ts index 2e3bd7b17..bd7135e24 100644 --- a/webpack.config.js +++ b/webpack.config.ts @@ -1,10 +1,10 @@ -const path = require('path'); -const assetPath = require('./src/services/asset_path'); +import path = require('path'); +import assetPath = require('./src/services/asset_path'); module.exports = { mode: 'production', entry: { - setup: './src/public/app/setup.ts', + setup: './src/public/app/setup.js', mobile: './src/public/app/mobile.js', desktop: './src/public/app/desktop.js', },