129 Commits

Author SHA1 Message Date
Renovate Bot
3a924fad19 chore(deps): update https://git.quad4.io/actions/setup-go action to v6
All checks were successful
OSV-Scanner PR Scan / scan-pr (pull_request) Successful in 18s
2026-01-01 00:02:17 +00:00
c1ed5ea92f feat: add tag handling for repository links
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 29s
CI / build-frontend (push) Successful in 55s
CI / scan-backend (push) Successful in 9m22s
CI / build-backend (push) Successful in 20s
2025-12-31 15:51:30 -06:00
3256ec63a2 chore: add Linux desktop build dependencies installation step to CI workflow
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 20s
CI / build-frontend (push) Successful in 51s
CI / scan-backend (push) Successful in 9m21s
CI / build-backend (push) Successful in 20s
2025-12-31 15:49:38 -06:00
dd0ef88856 chore: add Wails installation step
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 27s
CI / build-frontend (push) Successful in 1m18s
CI / scan-backend (push) Successful in 9m20s
CI / build-backend (push) Successful in 21s
2025-12-31 15:45:08 -06:00
81869eb6d7 docs: update CHANGELOG for version 1.5.3, detailing CI/CD updates and UI/UX improvements
Some checks failed
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 21s
CI / build-frontend (push) Successful in 46s
Build and Release / build (push) Has been cancelled
CI / scan-backend (push) Successful in 9m19s
CI / build-backend (push) Successful in 24s
Build and Publish Docker Image / build (push) Successful in 10m37s
2025-12-31 15:37:53 -06:00
ab39ddca15 fix: pass VITE_APP_VERSION as an argument during frontend build in Dockerfile 2025-12-31 15:37:44 -06:00
579dc721bc chore: update version to 1.5.3 in package.json and service worker 2025-12-31 15:37:38 -06:00
4c83b97d60 feat: implement version determination logic for frontend and Docker workflows, enhancing VITE_APP_VERSION usage 2025-12-31 15:36:57 -06:00
a5e7784048 feat: enhance VITE_APP_VERSION handling in Taskfile for frontend, backend, and Docker builds 2025-12-31 15:36:50 -06:00
298b62fdc4 chore: simplify VITE_APP_VERSION handling in package.json scripts 2025-12-31 15:36:40 -06:00
936a7e51c3 feat: improve footer to display version information with conditional link to repository 2025-12-31 15:36:35 -06:00
e6cf656556 chore: update dependencies in pnpm-lock.yaml to latest versions
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 21s
CI / build-frontend (push) Successful in 43s
CI / scan-backend (push) Successful in 9m21s
CI / build-backend (push) Successful in 22s
2025-12-31 15:18:23 -06:00
74e0bd403e feat: add Trivy download and SBOM generation to build workflow 2025-12-31 15:16:46 -06:00
c14dc18a65 chore: remove SBOM generation workflow and associated output files 2025-12-31 15:16:38 -06:00
e9eb07ef52 Merge pull request 'Update https://git.quad4.io/actions/checkout action to v6' (#18) from renovate/https-git.quad4.io-actions-checkout-6.x into master
All checks were successful
CI / scan-backend (push) Successful in 12s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m23s
CI / build-frontend (push) Successful in 9m38s
CI / build-backend (push) Successful in 9m25s
Reviewed-on: #18
2025-12-31 21:06:51 +00:00
5ccc6846a7 feat: add settings modal and grid customization options to IdentityGraph component
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 22s
CI / build-frontend (push) Successful in 43s
CI / scan-backend (push) Successful in 9m18s
CI / build-backend (push) Successful in 21s
2025-12-31 08:58:22 -06:00
7e3c8e2b79 Auto-update SBOM [skip ci] 2025-12-31 14:46:32 +00:00
6563d75b48 chore: update CHANGELOG for version 1.5.2 with mobile enhancements and UI/UX improvements
Some checks failed
Generate SBOM / generate-sbom (push) Successful in 50s
Build and Release / build (push) Failing after 1m29s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 25s
CI / build-frontend (push) Successful in 1m6s
CI / scan-backend (push) Successful in 9m18s
CI / build-backend (push) Successful in 21s
Build and Publish Docker Image / build (push) Successful in 12m50s
2025-12-31 08:45:43 -06:00
0973b6f378 chore: bump version to 1.5.2 and update CACHE_VERSION in service worker
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 22s
CI / build-frontend (push) Successful in 43s
CI / scan-backend (push) Successful in 9m21s
CI / build-backend (push) Successful in 20s
2025-12-31 08:44:44 -06:00
51fd93c9a0 fix: update footer text to include 'Linking Tool - Created by' 2025-12-31 08:44:35 -06:00
97b023f1f4 feat: enhance IdentityGraph component with touch gesture support and mobile toolbar improvements 2025-12-31 08:44:30 -06:00
10bfb5d9e4 format: workflows
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 21s
CI / build-frontend (push) Successful in 43s
CI / scan-backend (push) Successful in 9m20s
CI / build-backend (push) Successful in 21s
2025-12-31 08:20:18 -06:00
b09e7f05fd refactor: header and footer structure in +page.svelte, removing the LinkIcon and simplifying layout 2025-12-31 08:20:17 -06:00
Renovate Bot
232d62e5f9 Update https://git.quad4.io/actions/checkout action to v6
All checks were successful
OSV-Scanner PR Scan / scan-pr (pull_request) Successful in 17s
2025-12-31 00:01:37 +00:00
db82c15c51 Auto-update SBOM [skip ci] 2025-12-30 03:41:17 +00:00
ec38a69c57 Add mkdir command to create frontend_dist directory in desktop build tasks
All checks were successful
CI / scan-backend (push) Successful in 11s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m29s
CI / build-frontend (push) Successful in 9m40s
CI / build-backend (push) Successful in 19s
2025-12-29 21:31:20 -06:00
ae08e4dc5f Refactor build workflow
All checks were successful
CI / scan-backend (push) Successful in 10s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m25s
CI / build-frontend (push) Successful in 9m40s
CI / build-backend (push) Successful in 9m27s
2025-12-29 21:18:30 -06:00
cd3d9862c3 Update download-artifact action version in build workflow to v3.0.2
Some checks failed
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 18s
CI / build-frontend (push) Successful in 42s
CI / build-backend (push) Has been cancelled
CI / scan-backend (push) Has been cancelled
2025-12-29 21:15:06 -06:00
0f383d7f44 Update README 2025-12-29 17:36:44 -06:00
1180804025 Add Podman tasks to Taskfile.yml for building and running containers 2025-12-29 17:34:32 -06:00
c22e1af86f Update CACHE_VERSION to 1.5.1 in service worker
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 22s
CI / build-frontend (push) Successful in 43s
CI / scan-backend (push) Successful in 9m22s
CI / build-backend (push) Successful in 19s
2025-12-29 16:42:30 -06:00
09c62bed71 Add flake.lock and update flake.nix with new dependencies and hashes 2025-12-29 16:42:24 -06:00
c780fe040a Update README 2025-12-29 16:36:51 -06:00
1e521b0c59 Add task to build desktop application for Linux in Taskfile.yml 2025-12-29 16:35:42 -06:00
832afe7b90 Add build and release workflow configuration 2025-12-29 16:35:37 -06:00
6d0069a8d3 Add flake.nix 2025-12-29 16:30:53 -06:00
12e3cf9354 Fix SBOM workflow by adding ref parameter for checkout and ensuring push to master branch
All checks were successful
CI / scan-backend (push) Successful in 12s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m22s
CI / build-frontend (push) Successful in 9m37s
CI / build-backend (push) Successful in 9m28s
2025-12-29 16:12:46 -06:00
7ba1cfe6f7 Update SBOM workflow to trigger on version tags instead of branches
Some checks failed
CI / scan-backend (push) Successful in 11s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m23s
CI / build-frontend (push) Successful in 9m38s
Build and Publish Docker Image / build (push) Successful in 10m10s
CI / build-backend (push) Successful in 9m27s
Generate SBOM / generate-sbom (push) Failing after 9m28s
2025-12-29 14:28:22 -06:00
15d697c946 Auto-update SBOM [skip ci] 2025-12-29 20:25:57 +00:00
a8a4405946 1.5.1
All checks were successful
CI / scan-backend (push) Successful in 17s
Generate SBOM / generate-sbom (push) Successful in 23s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m23s
CI / build-frontend (push) Successful in 9m38s
CI / build-backend (push) Successful in 24s
2025-12-29 14:25:31 -06:00
2ddd0bf9fd 1.5.1 2025-12-29 14:25:24 -06:00
c3e10b3945 Auto-update SBOM [skip ci] 2025-12-29 20:15:06 +00:00
6da7b31269 Add HOST environment variable to Dockerfile for host binding
All checks were successful
CI / scan-backend (push) Successful in 17s
Generate SBOM / generate-sbom (push) Successful in 36s
CI / build-frontend (push) Successful in 9m37s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m29s
CI / build-backend (push) Successful in 9m27s
2025-12-29 14:14:25 -06:00
4b553d67d4 Update main.go to allow HOST environment variable for host binding and improve error handling in API response. Update app.go to enforce stricter file permissions and ensure valid file paths when loading files. 2025-12-29 14:14:20 -06:00
3b5807a480 Auto-update SBOM [skip ci] 2025-12-29 19:56:07 +00:00
e6c0387cdd Update Dockerfile path in CI workflow to use ./docker/Dockerfile
All checks were successful
CI / scan-backend (push) Successful in 22s
Generate SBOM / generate-sbom (push) Successful in 25s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m23s
CI / build-frontend (push) Successful in 9m38s
CI / build-backend (push) Successful in 26s
2025-12-29 13:55:39 -06:00
ce65f05bd4 Auto-update SBOM [skip ci] 2025-12-29 19:52:12 +00:00
3190c6f119 Update README.md
All checks were successful
CI / scan-backend (push) Successful in 19s
Generate SBOM / generate-sbom (push) Successful in 25s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m30s
CI / build-frontend (push) Successful in 9m36s
CI / build-backend (push) Successful in 28s
2025-12-29 13:51:47 -06:00
7a725a505f Change default host binding from '0.0.0.0' to '127.0.0.1' in main.go 2025-12-29 13:51:09 -06:00
6a1667b34d Auto-update SBOM [skip ci] 2025-12-29 19:38:07 +00:00
2f1bf6a05a Fix output format for CycloneDX in SBOM generation step of CI workflow
All checks were successful
CI / scan-backend (push) Successful in 22s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 38s
Generate SBOM / generate-sbom (push) Successful in 43s
CI / build-frontend (push) Successful in 49s
CI / build-backend (push) Successful in 22s
2025-12-29 13:37:17 -06:00
3662bda009 Specify version '3.46.3' for Setup Task action in CI workflow
Some checks failed
CI / scan-backend (push) Successful in 20s
Generate SBOM / generate-sbom (push) Failing after 23s
CI / build-backend (push) Has been cancelled
CI / build-frontend (push) Has been cancelled
OSV-Scanner Scheduled Scan / scan-scheduled (push) Has been cancelled
2025-12-29 13:35:29 -06:00
0757ca64f9 Update CI workflow by adding linting step to frontend
Some checks failed
Generate SBOM / generate-sbom (push) Failing after 13s
CI / scan-backend (push) Successful in 15s
CI / build-frontend (push) Has been cancelled
CI / build-backend (push) Has been cancelled
OSV-Scanner Scheduled Scan / scan-scheduled (push) Has been cancelled
2025-12-29 13:32:46 -06:00
d2856b27a8 Update
Some checks failed
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 20s
CI / build-frontend (push) Successful in 40s
Generate SBOM / generate-sbom (push) Has been cancelled
CI / build-backend (push) Successful in 21s
CI / lint-backend (push) Successful in 9m25s
2025-12-29 13:30:05 -06:00
1532bcae31 Update CI workflow to specify version for Setup Task action 2025-12-29 13:29:59 -06:00
c9627a71ea Add GitHub Action workflow to generate Software Bill of Materials (SBOM) using Trivy
Some checks failed
Generate SBOM / generate-sbom (push) Failing after 48s
CI / build-frontend (push) Failing after 4m44s
CI / build-backend (push) Has been skipped
CI / lint-backend (push) Successful in 12s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Has been cancelled
2025-12-29 13:22:20 -06:00
8ed205375b Remove duplicate import
Some checks failed
CI / lint-backend (push) Successful in 24s
CI / build-frontend (push) Failing after 4m41s
CI / build-backend (push) Has been skipped
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m25s
2025-12-29 13:11:17 -06:00
0bf731df66 Update CHANGELOG.md
Some checks failed
CI / build-frontend (push) Failing after 14s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 16s
CI / build-backend (push) Has been cancelled
CI / lint-backend (push) Has been cancelled
2025-12-29 13:10:15 -06:00
2064760ea9 Add new tasks to Taskfile.yml for building frontend and backend, setting up development environment, and installing dependencies for CI. 2025-12-29 13:08:15 -06:00
06300f08e3 Update CI workflow by integrating Task 2025-12-29 13:08:08 -06:00
c463eb1d94 Refactor IdentityGraph component to normalize links and improve data handling. Added normalizeLinks function to ensure link types and strengths are validated against predefined lists. 2025-12-29 13:07:55 -06:00
add7f6e530 Update SECURITY.md to include PNPM in vulnerability scanning and add SAST tools for Go and JavaScript code. 2025-12-29 12:58:48 -06:00
7a3b3ca054 Add eslint-plugin-security to ESLint configuration and update package.json and pnpm-lock.yaml 2025-12-29 12:58:37 -06:00
540f9712db Add cookie version override in package.json and pnpm-lock.yaml
Some checks failed
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 14s
CI / build-frontend (push) Successful in 9m37s
CI / build-backend (push) Has been cancelled
2025-12-29 12:53:54 -06:00
6ac2968b73 Update CI workflow to use pnpm for dependency management and upgrade Go version to 1.25.5
All checks were successful
CI / build-frontend (push) Successful in 39s
CI / build-backend (push) Successful in 20s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m25s
2025-12-29 12:49:44 -06:00
217b10b1fd Update CHANGELOG.md for version 1.5.0
Some checks failed
CI / build-frontend (push) Failing after 3s
CI / build-backend (push) Has been skipped
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 16s
2025-12-29 12:48:32 -06:00
3b6331faea Remove 2025-12-29 12:43:02 -06:00
8eb12a7087 Remove redundant line 2025-12-29 12:42:54 -06:00
5fac643e86 Update IdentityGraph component by adding canvasElement for improved touch event handling and updating event listeners for better state management. 2025-12-29 12:42:37 -06:00
d862b1d222 Update ESLint configuration to add readonly globals for Element and EventListener 2025-12-29 12:28:04 -06:00
46bc6bdb22 Add SECURITY.md 2025-12-29 12:27:56 -06:00
0b90005bf7 refactor IdentityGraph component to improve touch event handling and state management with IndexedDB 2025-12-29 12:27:30 -06:00
bab846cd83 Refactor layout component to use props for children and update event handling for improved readability and functionality. 2025-12-29 12:12:05 -06:00
59030ba2a3 Refactor IdentityGraph component to utilize centralized constants and implement undo/redo functionality with IndexedDB for state management. 2025-12-29 12:12:00 -06:00
9468010981 Improve README 2025-12-29 12:11:41 -06:00
38887b1de2 Remove 2025-12-29 12:11:25 -06:00
2c65a17b12 Remove 2025-12-29 12:11:19 -06:00
c1c823d2b1 Remove 2025-12-29 12:11:11 -06:00
9e7a9f6d2c Add constants and types for database, relationships, and node types in constants.ts 2025-12-29 12:11:07 -06:00
a3a78ae117 Remove unnecessary newline at the end of inject-sw-version.js for cleaner code. 2025-12-29 12:11:00 -06:00
51ce1cbc40 Update build script to use pnpm instead of npm for building the app 2025-12-29 12:10:55 -06:00
1202652e93 Add compiler options in svelte.config.js to manage runes for external dependencies 2025-12-29 12:10:47 -06:00
3b9b8e0a65 Refactor ESLint configuration by formatting the ignores array for improved readability. 2025-12-29 12:10:40 -06:00
44ccc672fc Update package version to 1.5.0, add author and license information, and update dependencies in package.json. Introduce pnpm-lock.yaml for dependency management. Update service worker cache version to 1.5.0. 2025-12-29 12:10:27 -06:00
625dcc11f8 Fix formatting in svelte.config.js by adding a missing comma and ensuring proper structure. 2025-12-29 11:33:57 -06:00
62f3f34e10 Update frontend package manager from npm to pnpm in wails.json 2025-12-29 11:33:51 -06:00
aad59ffe43 Fix indentation in renovate.json schema declaration 2025-12-29 11:33:42 -06:00
4b20bf540e Update Docker workflow 2025-12-29 11:33:36 -06:00
dd5e24ae26 Remove 2025-12-29 11:33:21 -06:00
bc20b06fe1 Remove package-lock 2025-12-29 11:33:11 -06:00
e7801735fa Update Go version to 1.25.5 2025-12-29 11:33:03 -06:00
fbbb6a5e9c Update LICENSE 2025-12-29 11:32:46 -06:00
ebdbd02599 Move Dockerfile 2025-12-29 11:32:19 -06:00
312fe5e746 Replace Makefile with Taskfile 2025-12-29 11:31:58 -06:00
469b59a561 Add IndexedDB support for graph and settings storage
- Implemented functions to initialize and interact with IndexedDB for storing graph data and user settings.
- Migrated existing localStorage data to IndexedDB on application load.
- Updated save and load functions to utilize IndexedDB instead of localStorage.
- Enhanced graph rendering with curved paths for links between nodes.
- Improved theme management by saving user preferences in IndexedDB.
2025-12-29 11:30:49 -06:00
bc5b12b23c Update workflow hashes
All checks were successful
CI / build-frontend (push) Successful in 38s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 9m26s
CI / build-backend (push) Successful in 9m33s
2025-12-29 00:03:21 -06:00
5f5de2272b Update Gitea workflows to use custom action URLs for checkout, setup-node, setup-go, and other actions
All checks were successful
CI / build-frontend (push) Successful in 53s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 31s
CI / build-backend (push) Successful in 20s
2025-12-28 21:08:07 -06:00
7b4598c5ca Update Gitea workflow to use custom action URLs for checkout and setup-go 2025-12-28 21:07:46 -06:00
ab2dd4e4ed Remove renovate workflow 2025-12-28 21:07:41 -06:00
ivan
e366ba8f1b Merge pull request 'Update ghcr.io/renovatebot/renovate Docker tag to v42.66.11' (#5) from renovate/ghcr.io-renovatebot-renovate-42.x into master
Some checks failed
renovate / renovate (push) Failing after 10s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 29s
CI / build-frontend (push) Successful in 9m37s
CI / build-backend (push) Successful in 21s
Reviewed-on: #5
2025-12-29 00:21:44 +00:00
Renovate Bot
6971796c00 Update ghcr.io/renovatebot/renovate Docker tag to v42.66.11
All checks were successful
OSV-Scanner PR Scan / scan-pr (pull_request) Successful in 19s
2025-12-29 00:02:10 +00:00
ivan
d85d960e3d Merge pull request 'Update ghcr.io/renovatebot/renovate Docker tag to v42' (#4) from renovate/ghcr.io-renovatebot-renovate-42.x into master
Some checks failed
renovate / renovate (push) Failing after 1m14s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 10m0s
CI / build-frontend (push) Successful in 10m19s
CI / build-backend (push) Successful in 1m57s
Reviewed-on: #4
2025-12-28 05:30:32 +00:00
Renovate Bot
d99fce9b24 Update ghcr.io/renovatebot/renovate Docker tag to v42
All checks were successful
OSV-Scanner PR Scan / scan-pr (pull_request) Successful in 18s
2025-12-28 00:00:48 +00:00
ivan
d770914aae Merge pull request 'Update ghcr.io/renovatebot/renovate Docker tag to v37.440.7' (#2) from renovate/ghcr.io-renovatebot-renovate-37.x into master
Some checks failed
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 15s
renovate / renovate (push) Failing after 14s
CI / build-frontend (push) Successful in 9m32s
CI / build-backend (push) Successful in 9m27s
Reviewed-on: #2
2025-12-27 22:44:20 +00:00
Renovate Bot
02d745f9ae Update ghcr.io/renovatebot/renovate Docker tag to v37.440.7
All checks were successful
OSV-Scanner PR Scan / scan-pr (pull_request) Successful in 9m45s
2025-12-27 22:29:35 +00:00
ivan
5c7c6c4ca5 Merge pull request 'Configure Renovate' (#1) from renovate/configure into master
Some checks failed
CI / build-frontend (push) Successful in 41s
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 18s
CI / build-backend (push) Successful in 24s
renovate / renovate (push) Failing after 19s
Reviewed-on: #1
2025-12-27 20:52:32 +00:00
Renovate Bot
e6bc79ec27 Add renovate.json
All checks were successful
OSV-Scanner PR Scan / scan-pr (pull_request) Successful in 23s
2025-12-27 20:29:30 +00:00
03f0c71aae Add Renovate workflow for automated dependency management
Some checks failed
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 23s
CI / build-frontend (push) Successful in 46s
CI / build-backend (push) Successful in 28s
renovate / renovate (push) Failing after 11s
2025-12-27 14:28:38 -06:00
4923619b51 Update CI workflows and OSV scan script for improved functionality
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 21s
CI / build-frontend (push) Successful in 39s
CI / build-backend (push) Successful in 21s
- Updated the 'checkout' action to version 4 for consistency across workflows.
- Added a 'Setup Go' step to initialize the Go environment using the latest action version.
- Modified the OSV scan script to generalize vulnerability reporting, removing specific severity checks for a broader output.
2025-12-27 12:46:16 -06:00
7e75064ae5 Update CI workflows to use specific action versions
All checks were successful
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 17s
CI / build-frontend (push) Successful in 53s
CI / build-backend (push) Successful in 29s
- Updated the 'checkout' action to version 4.3.1 across multiple workflows for consistency.
- Updated various actions in the Docker, npm publish, and OSV workflows to their respective latest versions for improved functionality and security.
2025-12-26 21:53:14 -06:00
7c13aee0a7 Update package script to include versioning for build process
- Modified the 'package' script in package.json to set the VITE_APP_VERSION environment variable using the current package version, ensuring the build process incorporates the correct version information.
2025-12-26 21:53:07 -06:00
d5b37ed53f Update Makefile to include new 'package' target
- Added a 'package' target that depends on the 'build' target to streamline the build process.
- Updated the .PHONY section to include the new 'package' target for better organization.
2025-12-26 21:52:59 -06:00
bbc4fd4c32 Fix CI workflow
Some checks failed
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 12s
CI / build-frontend (push) Successful in 51s
CI / build-backend (push) Successful in 35s
Publish NPM Package / publish (push) Failing after 21s
Build and Publish Docker Image / build (push) Successful in 10m25s
- Added a step to upload frontend build artifacts after the build process.
- Included a step to download the frontend assets in the backend build job, ensuring the backend has access to the latest frontend build.
2025-12-26 21:29:55 -06:00
06f3e6fa5a Update package version to 1.4.0 and switch Svelte adapter to static
Some checks failed
OSV-Scanner Scheduled Scan / scan-scheduled (push) Successful in 15s
CI / build-backend (push) Failing after 1m7s
CI / build-frontend (push) Successful in 1m14s
- Bumped the package version from 1.3.0 to 1.4.0 in both package.json and package-lock.json.
- Replaced '@sveltejs/adapter-node' with '@sveltejs/adapter-static' for improved deployment options.
- Added new scripts for desktop development and building.
2025-12-26 21:22:09 -06:00
548d5dbc35 Add script to inject service worker version from package.json
- Created a new script that reads the version from package.json and injects it into the service worker file (sw.js).
- This ensures the service worker uses the correct version for cache management and updates.
2025-12-26 21:21:21 -06:00
78c07e1c6b Update service worker to use versioning and improve cache management
- Changed CACHE_NAME to include a version number for better cache control.
- Enhanced cache deletion logic to target specific cache names.
- Added message event listener to allow clients to skip waiting for updates.
- Improved fetch handling to ensure offline support and better response caching.
2025-12-26 21:21:12 -06:00
4afe001117 Add Window interface to app.d.ts for file handling and logging
- Extended the Window interface to include a go property with methods for saving and loading files, as well as logging messages from the frontend.
- This enhancement supports improved interaction with the application’s file management features.
2025-12-26 21:21:01 -06:00
826e7d10d1 Add service worker update notifications and reload functionality
- Implemented logic to check for service worker updates and notify users when a new version is available.
- Added buttons to reload the application or dismiss the update notification.
- Enhanced service worker registration to handle updates and online status checks.
2025-12-26 21:20:52 -06:00
4646423f1d Change Svelte adapter from Node to Static
- Updated the Svelte configuration to use '@sveltejs/adapter-static' instead of '@sveltejs/adapter-node'.
- Configured adapter options for output directories and fallback handling.
2025-12-26 21:20:42 -06:00
a3a8e29a6d Update README 2025-12-26 21:20:26 -06:00
a2411bd176 Refactor CI workflow for frontend and backend builds
- Renamed jobs for clarity: 'check' to 'build-frontend' and 'build' to 'build-backend'.
- Updated action versions for checkout and setup-node.
- Added frontend build step and consolidated backend build process with Go setup.
- Enhanced frontend checks and build scripts for improved CI pipeline.
2025-12-26 21:20:19 -06:00
2465e2e42b Add file import/export functionality for Wails desktop and web browser
- Implemented file saving and loading capabilities for the IdentityGraph component, supporting both Wails desktop and web browser environments.
- Enhanced error handling for JSON parsing and validation of graph data format.
- Added logging for application startup and Wails environment detection.
2025-12-26 21:20:11 -06:00
3d81697538 Add wails.json configuration for Linking Tool
- Created wails.json to define project metadata, asset directory, and frontend build commands.
- Included author information for project attribution.
2025-12-26 21:20:03 -06:00
5896cd7064 Add main application and desktop API server implementation
- Introduced main.go for the server with CORS middleware and static asset handling.
- Added desktop/app.go for local API server with logging and file handling capabilities.
- Implemented desktop/main.go to initialize the application with Wails framework and asset management.
2025-12-26 21:19:57 -06:00
9ffce1e12f Update Makefile 2025-12-26 21:19:44 -06:00
b4c65bf30b Add go.mod and go.sum files to manage dependencies for the linking tool 2025-12-26 21:19:30 -06:00
7d2eb81e0f Update ESLint configuration 2025-12-26 21:19:24 -06:00
837c5c471d Update Dockerfile to streamline multi-stage builds for frontend and Go binary 2025-12-26 21:19:14 -06:00
4b9a972706 Update .dockerignore and .gitignore 2025-12-26 21:18:41 -06:00
42 changed files with 6769 additions and 5119 deletions

View File

@@ -1,14 +1,32 @@
node_modules
# Dependencies
node_modules/
vendor/
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# SvelteKit & Vite
.svelte-kit/
build/
dist/
.output/
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
# OS
# Go & Binaries
bin/
linking-tool
tmp/
# Wails Desktop
desktop/frontend_dist/
desktop/build/
wailsjs/
# Git
.git
.gitignore
# IDE & OS
.vscode/
.idea/
.DS_Store
Thumbs.db
@@ -16,8 +34,3 @@ Thumbs.db
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

122
.gitea/workflows/build.yml Normal file
View File

@@ -0,0 +1,122 @@
name: Build and Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., v1.0.0)'
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: https://git.quad4.io/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
fetch-depth: 0
- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else
SHORT_SHA=$(git rev-parse --short HEAD)
echo "version=${SHORT_SHA}" >> $GITHUB_OUTPUT
fi
- name: Setup Node.js
uses: https://git.quad4.io/actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: pnpm
- name: Setup Task
uses: https://git.quad4.io/actions/setup-task@0ab1b2a65bc55236a3bc64cde78f80e20e8885c2 # v1
with:
version: '3.46.3'
- name: Setup environment
run: task setup
- name: Install dependencies
run: task install:ci
- name: Build frontend
run: task build:frontend
env:
VITE_APP_VERSION: ${{ steps.version.outputs.version }}
- name: Setup Go
uses: https://git.quad4.io/actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with:
go-version: '1.25.5'
- name: Install Wails
run: go install github.com/wailsapp/wails/v2/cmd/wails@ac867f658730618b79b4fbea194ccbbbddac28ee # v2.11.0
- name: Install Linux desktop build dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
- name: Build server binaries
run: |
task build:backend
task build-linux-amd64
task build-windows-amd64
- name: Build desktop Linux
run: task desktop-linux
- name: Build desktop Windows
run: task desktop-windows
- name: Download Trivy
run: |
curl -L -o /tmp/trivy.deb https://git.quad4.io/Quad4-Extra/assets/raw/commit/90fdcea1bb71d91df2de6ff2e3897f278413f300/bin/trivy_0.68.2_Linux-64bit.deb
sudo dpkg -i /tmp/trivy.deb || sudo apt-get install -f -y
- name: Generate SBOM (CycloneDX)
run: |
mkdir -p release-assets
trivy fs --format cyclonedx --include-dev-deps --output release-assets/sbom.cyclonedx.json .
- name: Prepare release assets
run: |
mkdir -p release-assets
cp bin/linking-tool-linux-amd64 release-assets/
cp bin/linking-tool-windows-amd64.exe release-assets/
if [ -f desktop/build/bin/linking-tool ]; then
cp desktop/build/bin/linking-tool release-assets/linking-tool-desktop-linux-amd64
fi
if [ -f desktop/build/bin/linking-tool.exe ]; then
cp desktop/build/bin/linking-tool.exe release-assets/linking-tool-desktop-windows-amd64.exe
fi
- name: Create Release
uses: https://git.quad4.io/actions/gitea-release-action@4875285c0950474efb7ca2df55233c51333eeb74 # v1
with:
api_url: ${{ secrets.GITEA_API_URL }}
gitea_token: ${{ secrets.GITEA_TOKEN }}
title: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.version }}
body: |
Release ${{ steps.version.outputs.version }}
## Assets
- Server binaries (Linux AMD64, Windows AMD64)
- Desktop applications (Linux AMD64, Windows AMD64)
- SBOM files
files: release-assets/*
draft: false
prerelease: false

View File

@@ -7,33 +7,74 @@ on:
workflow_dispatch:
jobs:
check:
build-frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: https://git.quad4.io/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Setup Node.js
uses: actions/setup-node@v4
uses: https://git.quad4.io/actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: npm
cache: pnpm
- name: Setup Task
uses: https://git.quad4.io/actions/setup-task@0ab1b2a65bc55236a3bc64cde78f80e20e8885c2 # v1
with:
version: '3.46.3'
- name: Setup environment
run: task setup
- name: Install dependencies
run: npm ci
- name: Svelte check (fail on warnings)
run: bash scripts/check.sh
run: task install:ci
- name: Lint
run: task lint
- name: Frontend checks
run: task check
- name: Determine version
id: version
run: |
SHORT_SHA=$(git rev-parse --short HEAD)
echo "version=${SHORT_SHA}" >> $GITHUB_OUTPUT
build:
- name: Build frontend
run: task build:frontend
env:
VITE_APP_VERSION: ${{ steps.version.outputs.version }}
- name: Upload frontend assets
uses: https://git.quad4.io/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3.2.1
with:
name: frontend-build
path: build/
scan-backend:
runs-on: ubuntu-latest
needs: check
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
uses: https://git.quad4.io/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Setup Go
uses: https://git.quad4.io/actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Build
run: bash scripts/build.sh
go-version: '1.25.5'
- name: Run gosec security scan
uses: https://git.quad4.io/actions/gosec@424fc4cd9c82ea0fd6bee9cd49c2db2c3cc0c93f # v2.22.11
build-backend:
runs-on: ubuntu-latest
needs: [build-frontend, scan-backend]
steps:
- name: Checkout
uses: https://git.quad4.io/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Download frontend assets
uses: https://git.quad4.io/actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
name: frontend-build
path: build/
- name: Setup Go
uses: https://git.quad4.io/actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with:
go-version: '1.25.5'
- name: Setup Task
uses: https://git.quad4.io/actions/setup-task@0ab1b2a65bc55236a3bc64cde78f80e20e8885c2 # v1
with:
version: '3.46.3'
- name: Build backend
run: task build:backend

View File

@@ -22,18 +22,30 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
uses: https://git.quad4.io/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Determine version
id: version
run: |
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else
SHORT_SHA=$(git rev-parse --short HEAD)
echo "version=${SHORT_SHA}" >> $GITHUB_OUTPUT
fi
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392
uses: https://git.quad4.io/actions/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
with:
platforms: amd64,arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
uses: https://git.quad4.io/actions/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- name: Log in to the Container registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
uses: https://git.quad4.io/actions/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
@@ -41,7 +53,7 @@ jobs:
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
uses: https://git.quad4.io/actions/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
@@ -54,11 +66,13 @@ jobs:
- name: Build and push Docker image
id: build
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25
uses: https://git.quad4.io/actions/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: ./Dockerfile
file: ./docker/Dockerfile
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
VITE_APP_VERSION=${{ steps.version.outputs.version }}

View File

@@ -1,36 +0,0 @@
name: Publish NPM Package
on:
workflow_dispatch:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: npm ci --registry=https://registry.npmjs.org/
- name: Package
run: make package
- name: Configure npm for publishing
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://git.quad4.io/api/packages/quad4-software/npm/'
- name: Publish
run: npm publish
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -14,7 +14,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: https://git.quad4.io/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Setup Go
uses: https://git.quad4.io/actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with:
go-version-file: 'go.mod'
- name: OSV scan
run: bash scripts/osv_scan.sh

View File

@@ -14,7 +14,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: https://git.quad4.io/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Setup Go
uses: https://git.quad4.io/actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with:
go-version-file: 'go.mod'
- name: OSV scan
run: bash scripts/osv_scan.sh

40
.gitignore vendored
View File

@@ -1,23 +1,35 @@
node_modules
# Dependencies
node_modules/
vendor/
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# SvelteKit & Vite
.svelte-kit/
build/
dist/
.output/
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
# OS
# Go & Binaries
bin/
linking-tool
tmp/
# Wails Desktop
desktop/frontend_dist/
desktop/build/bin/
desktop/build/
wailsjs/
# IDE & OS
.vscode/
.idea/
.DS_Store
Thumbs.db
*.swp
*.swo
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

87
CHANGELOG.md Normal file
View File

@@ -0,0 +1,87 @@
# Changelog
## 1.5.3 - 2025-12-31
### CI/CD Updates
- Moved SBOM generation from `sbom.yml` workflow to `build.yml` workflow as release assets instead of auto-committing to source code
- Removed SPDX format, now only generating CycloneDX SBOM format (more popular and security-focused)
### UI/UX
- Updated version display logic: tag builds show tag version (e.g., `v1.5.2`), branch builds show commit SHA (e.g., `abc1234`), local dev shows `dev`
## 1.5.2 - 2025-12-31
### Features
- **Mobile Enhancements**:
- Added pinch-to-zoom support for graph navigation on touch devices.
- Redesigned mobile toolbar into a single row with a collapsible "More" menu.
- Added a responsive expand/collapse toggle for the mobile toolbar using chevron icons.
- Moved the "Add Node" action to a floating sticky button in the bottom-right on mobile for better accessibility.
- Optimized toolbar width and spacing for mobile screens.
- **UI/UX**:
- Removed top navbar/header to maximize workspace area.
- Simplified layout with a minimal footer.
- Updated footer branding to include "Linking Tool".
### Fixes
- Improved click-outside handling for mobile menus.
- Fixed various mobile layout and justification constraints.
## 1.5.1 - 2025-12-29
### Features
- Added HOST environment variable support for configuring server host binding
### Security
- Fixed unhandled error in HTTP response writing (G104)
- Fixed file write permissions to use more restrictive 0600 instead of 0644 (G306)
- Fixed potential file inclusion vulnerability by adding path validation in file operations (G304)
### Docker
- Added HOST environment variable to Dockerfile (defaults to 0.0.0.0, make sure to set it properly in production)
## 1.5.0 - 2025-12-29
### Features
- Move to IndexedDB for saving graph data (from localStorage)
- Add multiple links support between nodes.
- Increase undo/redo history to 100 steps.
- Move undo/redo operations to IndexedDB instead of memory.
- Mass selection improvements (moving and linking multiple nodes at once).
- Codebase refactor to use Svelte 5 Runes.
- Mobile improvements
- Added SBOM generation as release assets
### Dependency Updates
- `@sveltejs/kit`: ^2.49.1 -> ^2.49.2
- `@typescript-eslint/eslint-plugin`: ^8.50.1 -> ^8.51.0
- `@typescript-eslint/parser`: ^8.50.1 -> ^8.51.0
- `svelte`: ^5.45.6 -> ^5.46.1
- `svelte-check`: ^4.3.4 -> ^4.3.5
- `vite`: ^7.2.6 -> ^7.3.0
- Added `eslint-plugin-security`: ^3.0.1
### Major Codebase Changes
- Moved from `npm` to `pnpm`
- Updated license from `MIT` to `BSD-3-Clause`
- Moved from `Makefile` to `Taskfile`
- Codebase organization and structure changes
### CI/CD Updates
- Updated CI workflows to use `task` commands instead of bash scripts
- Added gosec security scanning to backend build pipeline
### Security
- Overrode `cookie` package to latest version (1.1.1) due to low severity vulnerability in default version.

View File

@@ -1,33 +0,0 @@
FROM cgr.dev/chainguard/node:latest-dev AS builder
WORKDIR /app
COPY --chown=node:node package.json package-lock.json ./
RUN npm ci
RUN npm install --save-dev @sveltejs/adapter-node@latest
COPY --chown=node:node . .
COPY --chown=node:node svelte.config.docker.js svelte.config.js
RUN npm run build
FROM cgr.dev/chainguard/node:latest AS runtime
WORKDIR /app
COPY --from=builder --chown=node:node /app/package.json /app/package-lock.json ./
RUN npm install --omit=dev && \
npm cache clean --force
COPY --from=builder --chown=node:node /app/build ./build
COPY --from=builder --chown=node:node /app/package.json ./
EXPOSE 3000
ENV NODE_ENV=production
ENV PORT=3000
ENV HOST=0.0.0.0
CMD ["build/index.js"]

38
LICENSE
View File

@@ -1,22 +1,28 @@
MIT License
BSD 3-Clause License
Copyright (c) 2025 Quad4.io
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,44 +0,0 @@
.PHONY: help install dev build preview check lint format clean docker-build docker-run docker package publish
help:
@echo 'Usage: make [target]'
@echo ''
@echo 'Available targets:'
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
dev:
npm install
npm run dev
build:
npm run build
package:
npm run package
publish:
npm publish
preview:
npm run preview
check:
npm run check
lint:
npm run lint
format:
npm run format
clean:
rm -rf .svelte-kit build node_modules/.vite dist package
docker-build:
docker build -t linking-tool .
docker-run:
docker run --rm -p 3000:3000 linking-tool
docker: docker-build docker-run

144
README.md
View File

@@ -1,80 +1,142 @@
# Quad4 Linking Tool
A client-side web linking tool for mapping relationships between entities.
A web linking tool for mapping relationships between entities.
<img src="showcase/linkingtool.png" alt="showcase image" width="900">
Desktop apps for Windows, macOS, and Linux are coming soon...
## Quick Start
### Using the Binary
1. Build the binary:
```sh
task build
```
2. Run the server:
```sh
./bin/linking-tool --port 8080
```
3. Open your browser at `http://localhost:8080`
### Using Docker
```sh
docker run -p 8080:8080 git.quad4.io/quad4-software/linking-tool
```
Then open your browser at `http://localhost:8080`
## Features
- Interactive graph visualization
- Multiple entity types (person, email, phone, address, domain, org, IP, social)
- Auto-save to localStorage
- Auto-save to IndexedDB
- Import/Export JSON
- Share link via base64 for smaller graphs
- Undo/Redo support
- PWA support (installable, offline-capable)
- Self-hostable
- Mobile support
- Native desktop app support (via Wails)
- Single-binary lightweight web server
- Support for 32-bit and 64-bit architectures (runs on old Raspberry Pi Zero W)
## Self-Hosting
## Installation Options
### NPM
### Self-Hosted Web Server
The easiest way to self-host is using the single binary:
```sh
npm config set @quad4:registry https://git.quad4.io/api/packages/quad4-software/npm/
npm install -g @quad4/linking-tool
linking-tool
task build
./bin/linking-tool --port 8080
```
Or
The binary will be located in `bin/` after building.
### Desktop Application
Build the desktop application for your platform:
```sh
PORT=3000 HOST=0.0.0.0 linking-tool
```
### Docker
```sh
docker run -p 3000:3000 git.quad4.io/quad4-software/linking-tool
task desktop-build
```
### Podman
```sh
podman run -p 3000:3000 git.quad4.io/quad4-software/linking-tool
```
The binary will be located in `bin/` after building.
## Development
### Prerequisites
- Go `1.25.5`
- Node.js
- pnpm
- Wails (for desktop app development)
### Setup
```sh
git clone https://git.quad4.io/quad4-software/linking-tool.git
cd linking-tool
pnpm install
```
### NPM
### Task
The project uses [Task](https://taskfile.dev/) for all development tasks.
```sh
npm install
npm run dev
```
| Task | Description |
|---------------------|-------------------------------------------|
| default | Show available tasks |
| dev | Run development servers (Go & SvelteKit) |
| build | Build the single binary web server |
| build:frontend | Build frontend only |
| build:backend | Build backend binary only |
| package | Package the application |
| release | Build binaries for all platforms |
| build-linux-amd64 | Build Linux AMD64 binary |
| build-linux-arm64 | Build Linux ARM64 binary |
| build-linux-armv6 | Build Linux ARMv6 binary |
| build-linux-armv7 | Build Linux ARMv7 binary |
| build-windows-amd64 | Build Windows AMD64 binary |
| build-darwin-amd64 | Build Darwin AMD64 binary |
| build-darwin-arm64 | Build Darwin ARM64 binary |
| build-freebsd-amd64 | Build FreeBSD AMD64 binary |
| docker-build | Build Docker image |
| docker-run | Run Docker container |
| docker-builder | Build and extract binaries using Docker |
| podman-build | Build Podman image |
| podman-run | Run Podman container |
| podman | Build and run Podman container |
| desktop-build | Build desktop application |
| desktop-linux | Build desktop application for Linux |
| desktop-windows | Build desktop application for Windows |
| desktop-darwin | Build desktop application for Darwin |
| desktop-dev | Run desktop app in development mode |
| clean | Clean build artifacts |
| setup | Setup development environment |
| install | Install dependencies |
| install:ci | Install dependencies for CI (frozen lock) |
| preview | Preview production build |
| check | Run type checking |
| lint | Run linter |
| format | Format code |
| version:minor | Bump minor version in package.json |
| version:major | Bump major version in package.json |
### Makefile
```sh
make dev
```
## Docker
Uses Chainguard Images which are rootless and very minimal images.
```sh
docker build -t quad4-linking-tool .
docker run -p 3000:3000 quad4-linking-tool
example: task dev
you might to set alias alias task=`go-task`
```
## Contributing
Send us a email at[team@quad4.io](mailto:team@quad4.io) for any issues or feedback.
Send us an email at [team@quad4.io](mailto:team@quad4.io) for any issues or feedback.
## LICENSE
## License
[MIT](LICENSE)
[BSD 3-Clause](LICENSE)

17
SECURITY.md Normal file
View File

@@ -0,0 +1,17 @@
# Security Policy
If you have discovered a security vulnerability, please refer to [our website](https://quad4.io/security) for the latest security reporting procedures and guidelines.
## Vulnerability Management
- We use PNPM and [OSV](https://osv.dev/) to scan for package vulnerabilities in our dependencies.
## SAST
- Gosec for Go code.
- ESLint with eslint-plugin-security for JavaScript code.
## Dependency and Supply Chain
- All GitHub Actions used are forked and hosted on our Gitea instance, view them here https://git.quad4.io/actions.
- Actions are referenced using full URLs and cryptographically pinned to specific commit hashes for enhanced supply chain security.

251
Taskfile.yml Normal file
View File

@@ -0,0 +1,251 @@
version: '3'
vars:
BINARY_NAME: linking-tool
BUILD_DIR: bin
tasks:
default:
desc: Show available tasks
cmds:
- task --list
dev:
desc: Run development servers (Go & SvelteKit)
cmds:
- pnpm install
- pnpm run dev
build:frontend:
desc: Build frontend only
cmds:
- |
if [ -z "$VITE_APP_VERSION" ]; then
if git rev-parse --git-dir > /dev/null 2>&1; then
if git describe --tags --exact-match HEAD > /dev/null 2>&1; then
VITE_APP_VERSION=$(git describe --tags --exact-match HEAD)
else
VITE_APP_VERSION=$(git rev-parse --short HEAD)
fi
else
VITE_APP_VERSION=$(node -p "require('./package.json').version")
fi
fi
VITE_APP_VERSION="$VITE_APP_VERSION" pnpm run build
build:backend:
desc: Build backend binary only
cmds:
- mkdir -p {{.BUILD_DIR}}
- CGO_ENABLED=0 go build -ldflags="-s -w" -o {{.BUILD_DIR}}/{{.BINARY_NAME}} main.go
build:
desc: Build the single binary web server
cmds:
- pnpm install
- |
if [ -z "$VITE_APP_VERSION" ]; then
if git rev-parse --git-dir > /dev/null 2>&1; then
if git describe --tags --exact-match HEAD > /dev/null 2>&1; then
VITE_APP_VERSION=$(git describe --tags --exact-match HEAD)
else
VITE_APP_VERSION=$(git rev-parse --short HEAD)
fi
else
VITE_APP_VERSION=$(node -p "require('./package.json').version")
fi
fi
VITE_APP_VERSION="$VITE_APP_VERSION" pnpm run build
- mkdir -p {{.BUILD_DIR}}
- CGO_ENABLED=0 go build -ldflags="-s -w" -o {{.BUILD_DIR}}/{{.BINARY_NAME}} main.go
package:
desc: Package the application
deps: [build]
release:
desc: Build binaries for all platforms
deps: [build]
cmds:
- task: build-linux-amd64
- task: build-linux-arm64
- task: build-linux-armv6
- task: build-linux-armv7
- task: build-windows-amd64
- task: build-darwin-amd64
- task: build-darwin-arm64
- task: build-freebsd-amd64
build-linux-amd64:
desc: Build Linux AMD64 binary
cmds:
- GOOS=linux GOARCH=amd64 go build -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-linux-amd64 main.go
build-linux-arm64:
desc: Build Linux ARM64 binary
cmds:
- GOOS=linux GOARCH=arm64 go build -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-linux-arm64 main.go
build-linux-armv6:
desc: Build Linux ARMv6 binary
cmds:
- GOOS=linux GOARCH=arm GOARM=6 go build -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-linux-armv6 main.go
build-linux-armv7:
desc: Build Linux ARMv7 binary
cmds:
- GOOS=linux GOARCH=arm GOARM=7 go build -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-linux-armv7 main.go
build-windows-amd64:
desc: Build Windows AMD64 binary
cmds:
- GOOS=windows GOARCH=amd64 go build -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-windows-amd64.exe main.go
build-darwin-amd64:
desc: Build Darwin AMD64 binary
cmds:
- GOOS=darwin GOARCH=amd64 go build -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-darwin-amd64 main.go
build-darwin-arm64:
desc: Build Darwin ARM64 binary
cmds:
- GOOS=darwin GOARCH=arm64 go build -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-darwin-arm64 main.go
build-freebsd-amd64:
desc: Build FreeBSD AMD64 binary
cmds:
- GOOS=freebsd GOARCH=amd64 go build -o {{.BUILD_DIR}}/{{.BINARY_NAME}}-freebsd-amd64 main.go
docker-build:
desc: Build Docker image (VITE_APP_VERSION env var will be passed as build arg if set)
cmds:
- |
if [ -n "$VITE_APP_VERSION" ]; then
docker build --build-arg VITE_APP_VERSION="$VITE_APP_VERSION" -f docker/Dockerfile -t {{.BINARY_NAME}} .
else
docker build -f docker/Dockerfile -t {{.BINARY_NAME}} .
fi
docker-run:
desc: Run Docker container
cmds:
- docker run -p 8080:8080 {{.BINARY_NAME}}
docker-builder:
desc: Build and extract binaries using Docker
cmds:
- docker build -f docker/Dockerfile.build -t {{.BINARY_NAME}}-build .
- docker create --name {{.BINARY_NAME}}-temp {{.BINARY_NAME}}-build
- mkdir -p {{.BUILD_DIR}}
- docker cp {{.BINARY_NAME}}-temp:/bin/. {{.BUILD_DIR}}/
- docker cp {{.BINARY_NAME}}-temp:/desktop-bin/. {{.BUILD_DIR}}/
- docker rm {{.BINARY_NAME}}-temp
podman-build:
desc: Build Podman image
cmds:
- podman build -f docker/Dockerfile -t surveilled .
podman-run:
desc: Run Podman container
cmds:
- podman run --rm -p 3000:3000 surveilled
podman:
desc: Build and run Podman container
deps: [podman-build, podman-run]
desktop-build:
desc: Build desktop application
deps: [build]
cmds:
- mkdir -p desktop/frontend_dist
- rm -rf desktop/frontend_dist/*
- cp -r build/* desktop/frontend_dist/
- cd desktop && wails build -s
desktop-linux:
desc: Build desktop application for Linux
deps: [build]
cmds:
- mkdir -p desktop/frontend_dist
- rm -rf desktop/frontend_dist/*
- cp -r build/* desktop/frontend_dist/
- cd desktop && wails build -s -platform linux/amd64
desktop-windows:
desc: Build desktop application for Windows
deps: [build]
cmds:
- mkdir -p desktop/frontend_dist
- rm -rf desktop/frontend_dist/*
- cp -r build/* desktop/frontend_dist/
- cd desktop && wails build -s -platform windows/amd64
desktop-darwin:
desc: Build desktop application for Darwin
deps: [build]
cmds:
- mkdir -p desktop/frontend_dist
- rm -rf desktop/frontend_dist/*
- cp -r build/* desktop/frontend_dist/
- cd desktop && wails build -s -platform darwin/universal
desktop-dev:
desc: Run desktop application in development mode
deps: [build]
cmds:
- mkdir -p desktop/frontend_dist
- rm -rf desktop/frontend_dist/*
- cp -r build/* desktop/frontend_dist/
- cd desktop && wails dev
clean:
desc: Clean build artifacts
cmds:
- rm -rf .svelte-kit build node_modules node_modules/.vite dist package linking-tool tmp {{.BUILD_DIR}}
setup:
desc: Setup development environment
cmds:
- corepack enable
install:
desc: Install dependencies
cmds:
- pnpm install
install:ci:
desc: Install dependencies for CI (frozen lockfile)
cmds:
- pnpm install --frozen-lockfile
preview:
desc: Preview production build
cmds:
- pnpm run preview
check:
desc: Run type checking
cmds:
- pnpm run check
lint:
desc: Run linter
cmds:
- pnpm run lint
format:
desc: Format code
cmds:
- pnpm run format
version:minor:
desc: Bump minor version in package.json
cmds:
- pnpm version minor --no-git-tag-version
version:major:
desc: Bump major version in package.json
cmds:
- pnpm version major --no-git-tag-version

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env node
process.env.HOST = process.env.HOST || '127.0.0.1';
process.env.PORT = process.env.PORT || '3000';
import '../build/index.js';

165
desktop/app.go Normal file
View File

@@ -0,0 +1,165 @@
package main
import (
"context"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"time"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
// App struct
type App struct {
ctx context.Context
port int
debug bool
}
// NewApp creates a new App struct
func NewApp(debug bool) *App {
return &App{
debug: debug,
}
}
func (a *App) logDebug(format string, args ...any) {
if a != nil && a.debug {
fmt.Printf("[debug] "+format+"\n", args...)
}
}
// logHandler wraps HTTP handlers to log requests when debug is enabled.
func (a *App) logHandler(next http.Handler) http.Handler {
if !a.debug {
return next
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
fmt.Printf("[debug] http %s %s %dms\n", r.Method, r.URL.Path, time.Since(start).Milliseconds())
})
}
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
a.logDebug("startup begin")
// Start local API server on a random port
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
fmt.Printf("Error starting local server: %v\n", err)
return
}
a.port = listener.Addr().(*net.TCPAddr).Port
a.logDebug("local API listener bound on %s", listener.Addr().String())
mux := http.NewServeMux()
// CORS middleware for local desktop API
cors := func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
}
}
mux.HandleFunc("/api/ping", cors(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok"}`)
}))
server := &http.Server{
Addr: listener.Addr().String(),
Handler: a.logHandler(mux),
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
go func() {
if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
fmt.Printf("Error serving desktop API: %v\n", err)
}
}()
fmt.Printf("Desktop API server started on port %d\n", a.port)
a.logDebug("startup complete")
}
// GetAPIPort returns the port the local server is running on
func (a *App) GetAPIPort() int {
a.logDebug("GetAPIPort -> %d", a.port)
return a.port
}
// LogFrontend allows the frontend to log to the terminal
func (a *App) LogFrontend(message string) {
fmt.Printf("[frontend] %s\n", message)
}
// SaveFile shows a save dialog and writes the content to the selected file
func (a *App) SaveFile(filename string, content string) error {
a.logDebug("SaveFile filename=%s", filename)
filePath, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
DefaultFilename: filename,
Title: "Save Graph",
Filters: []runtime.FileFilter{
{
DisplayName: "JSON Files (*.json)",
Pattern: "*.json",
},
},
})
if err != nil {
return err
}
if filePath == "" {
return nil // Cancelled
}
return os.WriteFile(filePath, []byte(content), 0600)
}
// LoadFile shows an open dialog and returns the content of the selected file
func (a *App) LoadFile() (string, error) {
a.logDebug("LoadFile")
filePath, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
Title: "Open Graph",
Filters: []runtime.FileFilter{
{
DisplayName: "JSON Files (*.json)",
Pattern: "*.json",
},
},
})
if err != nil {
return "", err
}
if filePath == "" {
return "", nil // Cancelled
}
absPath, err := filepath.Abs(filePath)
if err != nil {
return "", fmt.Errorf("invalid file path: %w", err)
}
cleanPath := filepath.Clean(absPath)
content, err := os.ReadFile(cleanPath)
if err != nil {
return "", err
}
return string(content), nil
}

52
desktop/main.go Normal file
View File

@@ -0,0 +1,52 @@
package main
import (
"embed"
"os"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)
//go:embed all:frontend_dist
var assets embed.FS
func debugEnabled() bool {
for _, arg := range os.Args[1:] {
if arg == "--debug" || arg == "-d" {
return true
}
}
return false
}
func main() {
debug := debugEnabled()
if debug {
println("Debug logging enabled")
}
// Create an instance of the app structure
app := NewApp(debug)
// Create application with options
err := wails.Run(&options.App{
Title: "Linking Tool",
Width: 1280,
Height: 800,
AssetServer: &assetserver.Options{
Assets: assets,
},
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
OnStartup: app.startup,
Bind: []interface{}{
app,
},
EnableDefaultContextMenu: true,
})
if err != nil {
println("Error:", err.Error())
}
}

14
desktop/wails.json Normal file
View File

@@ -0,0 +1,14 @@
{
"name": "Linking Tool",
"assetdir": "frontend_dist",
"frontend:dir": "..",
"frontend:install": "pnpm install",
"frontend:build": "pnpm run build",
"frontend:dev:watcher": "pnpm run dev",
"frontend:dev:serverUrl": "http://localhost:5173",
"outputfilename": "linking-tool",
"author": {
"name": "Quad4",
"email": "dev@quad4.io"
}
}

36
docker/Dockerfile Normal file
View File

@@ -0,0 +1,36 @@
# Stage 1: Build the frontend
FROM cgr.dev/chainguard/node:latest-dev AS node-builder
ARG VITE_APP_VERSION
WORKDIR /app
USER root
RUN corepack enable && corepack prepare pnpm@10.25.0 --activate
USER node
COPY --chown=node:node package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY --chown=node:node . .
RUN VITE_APP_VERSION=${VITE_APP_VERSION} pnpm run build
# Stage 2: Build the Go binary with embedded assets
FROM cgr.dev/chainguard/go:latest-dev AS go-builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
COPY --from=node-builder /app/build ./build
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o linking-tool main.go
# Stage 3: Minimal runtime image
FROM cgr.dev/chainguard/wolfi-base:latest
WORKDIR /app
COPY --from=go-builder /app/linking-tool .
RUN apk add --no-cache ca-certificates
EXPOSE 8080
ENV PORT=8080
ENV HOST=0.0.0.0
ENV NODE_ENV=production
USER 65532
CMD ["./linking-tool"]

View File

@@ -1,6 +1,7 @@
import js from '@eslint/js';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import securityPlugin from 'eslint-plugin-security';
import sveltePlugin from 'eslint-plugin-svelte';
import svelteParser from 'svelte-eslint-parser';
@@ -19,6 +20,8 @@ export default [
caches: 'readonly',
URL: 'readonly',
console: 'readonly',
Element: 'readonly',
EventListener: 'readonly',
HTMLElement: 'readonly',
HTMLImageElement: 'readonly',
HTMLInputElement: 'readonly',
@@ -39,7 +42,21 @@ export default [
URLSearchParams: 'readonly',
setTimeout: 'readonly',
clearTimeout: 'readonly',
setInterval: 'readonly',
clearInterval: 'readonly',
localStorage: 'readonly',
sessionStorage: 'readonly',
Response: 'readonly',
Request: 'readonly',
Headers: 'readonly',
FormData: 'readonly',
ServiceWorkerRegistration: 'readonly',
location: 'readonly',
history: 'readonly',
addEventListener: 'readonly',
removeEventListener: 'readonly',
requestAnimationFrame: 'readonly',
queueMicrotask: 'readonly',
atob: 'readonly',
btoa: 'readonly',
alert: 'readonly',
@@ -51,10 +68,12 @@ export default [
},
plugins: {
'@typescript-eslint': tsPlugin,
security: securityPlugin,
svelte: sveltePlugin,
},
rules: {
...tsPlugin.configs.recommended.rules,
...securityPlugin.configs.recommended.rules,
},
},
{
@@ -89,10 +108,20 @@ export default [
fetch: 'readonly',
URL: 'readonly',
console: 'readonly',
Response: 'readonly',
Request: 'readonly',
},
},
},
{
ignores: ['node_modules/**', '.svelte-kit/**', 'build/**', 'dist/**', 'archive/**'],
ignores: [
'node_modules/**',
'.svelte-kit/**',
'build/**',
'dist/**',
'archive/**',
'desktop/frontend_dist/**',
'wailsjs/**',
],
},
];

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1766902085,
"narHash": "sha256-coBu0ONtFzlwwVBzmjacUQwj3G+lybcZ1oeNSQkgC0M=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c0b0e0fddf73fd517c3471e546c0df87a42d53f4",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

94
flake.nix Normal file
View File

@@ -0,0 +1,94 @@
{
description = "Quad4 Linking Tool development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
};
go = pkgs.go_1_25;
task = pkgs.buildGoModule rec {
pname = "task";
version = "3.46.3";
src = pkgs.fetchFromGitHub {
owner = "go-task";
repo = "task";
rev = "v${version}";
hash = "sha256-1bS8ZZAcemgRG7PTeGTFfd49T9u6U6CxxrbotwCM15A=";
};
vendorHash = "sha256-Tm0tqureCRwcP5KKDTa9TO1yZ3Px3ulf9/jKQDDTjDw=";
subPackages = [ "cmd/task" ];
doCheck = false;
meta = with pkgs.lib; {
description = "A task runner / simpler Make alternative written in Go";
homepage = "https://taskfile.dev/";
license = licenses.mit;
maintainers = with maintainers; [ ];
};
};
wailsSrc = pkgs.fetchFromGitHub {
owner = "wailsapp";
repo = "wails";
rev = "v2.11.0";
hash = "sha256-H1Nml2vhCx4IB/CT+kDro5joAw8ewpxoQjDgvqamAr8=";
};
wails = pkgs.buildGoModule rec {
pname = "wails";
version = "2.11.0";
src = pkgs.runCommand "${pname}-${version}-src" {} ''
cp -r ${wailsSrc}/v2 $out
chmod -R +w $out
'';
vendorHash = "sha256-HAIKhMKRTNI4hsm8Hvn5pUhnCTcitRxiw+WkVmxpfiU=";
subPackages = [ "cmd/wails" ];
doCheck = false;
meta = with pkgs.lib; {
description = "Build applications using Go + HTML + CSS + JS";
homepage = "https://wails.io/";
license = licenses.mit;
maintainers = with maintainers; [ ];
};
};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
go
task
nodejs_20
nodePackages.pnpm
wails
gcc
pkg-config
];
shellHook = ''
echo "Quad4 Linking Tool Development Environment"
echo "Go version: $(go version)"
echo "Task version: $(task --version 2>/dev/null || echo 'installed')"
echo "Node version: $(node --version)"
echo "pnpm version: $(pnpm --version)"
echo "Wails version: $(wails version 2>/dev/null || echo 'installed')"
'';
};
});
}

35
go.mod Normal file
View File

@@ -0,0 +1,35 @@
module git.quad4.io/Quad4-Software/linking-tool
go 1.25.5
require github.com/wailsapp/wails/v2 v2.11.0
require (
github.com/bep/debounce v1.2.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/labstack/echo/v4 v4.13.3 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
github.com/leaanthony/gosod v1.0.4 // indirect
github.com/leaanthony/slicer v1.6.0 // indirect
github.com/leaanthony/u v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/samber/lo v1.49.1 // indirect
github.com/tkrajina/go-reflector v0.5.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.22 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
)

81
go.sum Normal file
View File

@@ -0,0 +1,81 @@
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
github.com/leaanthony/gosod v1.0.4 h1:YLAbVyd591MRffDgxUOU1NwLhT9T1/YiwjKZpkNFeaI=
github.com/leaanthony/gosod v1.0.4/go.mod h1:GKuIL0zzPj3O1SdWQOdgURSuhkF+Urizzxh26t9f1cw=
github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js=
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6NZijQ58=
github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/wailsapp/wails/v2 v2.11.0 h1:seLacV8pqupq32IjS4Y7V8ucab0WZwtK6VvUVxSBtqQ=
github.com/wailsapp/wails/v2 v2.11.0/go.mod h1:jrf0ZaM6+GBc1wRmXsM8cIvzlg0karYin3erahI4+0k=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

146
main.go Normal file
View File

@@ -0,0 +1,146 @@
package main
import (
"embed"
"flag"
"io/fs"
"log"
"net"
"net/http"
"os"
"strings"
"time"
)
//go:embed build/*
var buildAssets embed.FS
func corsMiddleware(allowedOrigins []string) func(http.HandlerFunc) http.HandlerFunc {
return func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
if origin == "" {
next.ServeHTTP(w, r)
return
}
allowed := false
if len(allowedOrigins) == 0 {
allowed = true
} else {
for _, o := range allowedOrigins {
if o == "*" || o == origin {
allowed = true
break
}
}
}
if allowed {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
}
if r.Method == "OPTIONS" {
if allowed {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusForbidden)
}
return
}
if !allowed && len(allowedOrigins) > 0 {
log.Printf("Blocked CORS request from origin: %s", origin)
http.Error(w, "CORS Origin Not Allowed", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
}
}
}
func main() {
frontendPath := flag.String("frontend", "", "Path to custom frontend build directory (overrides embedded assets)")
host := flag.String("host", "127.0.0.1", "Host to bind the server to")
port := flag.String("port", "", "Port to listen on (overrides PORT env var)")
allowedOriginsStr := flag.String("allowed-origins", os.Getenv("ALLOWED_ORIGINS"), "Comma-separated list of allowed CORS origins")
flag.Parse()
var allowedOrigins []string
if *allowedOriginsStr != "" {
origins := strings.Split(*allowedOriginsStr, ",")
for _, o := range origins {
allowedOrigins = append(allowedOrigins, strings.TrimSpace(o))
}
}
if hostEnv := os.Getenv("HOST"); hostEnv != "" {
*host = hostEnv
}
if *port == "" {
*port = os.Getenv("PORT")
if *port == "" {
*port = "8080"
}
}
// Middleware chains
cors := corsMiddleware(allowedOrigins)
http.HandleFunc("/api/ping", cors(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write([]byte(`{"status":"ok"}`)); err != nil {
log.Printf("Error writing response: %v", err)
}
}))
// Static Assets
var staticFS fs.FS
if *frontendPath != "" {
log.Printf("Using custom frontend from: %s\n", *frontendPath)
staticFS = os.DirFS(*frontendPath)
} else {
sub, err := fs.Sub(buildAssets, "build")
if err != nil {
log.Fatal(err)
}
staticFS = sub
}
fileServer := http.FileServer(http.FS(staticFS))
// SPA Handler
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
path := strings.TrimPrefix(r.URL.Path, "/")
if path == "" {
path = "index.html"
}
_, err := staticFS.Open(path)
if err != nil {
// If file doesn't exist, serve index.html for SPA routing
r.URL.Path = "/"
}
fileServer.ServeHTTP(w, r)
})
addr := net.JoinHostPort(*host, *port)
log.Printf("Linking Tool server starting on %s...\n", addr)
server := &http.Server{
Addr: addr,
Handler: nil,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}

4282
package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,8 @@
{
"name": "@quad4/linking-tool",
"version": "1.3.0",
"version": "1.5.3",
"license": "BSD-3-Clause",
"author": "Quad4",
"type": "module",
"main": "./build/index.js",
"bin": {
@@ -9,9 +11,15 @@
"engines": {
"node": ">=18.0.0"
},
"packageManager": "pnpm@10.25.0",
"publishConfig": {
"registry": "https://git.quad4.io/api/packages/quad4-software/npm/"
},
"pnpm": {
"overrides": {
"cookie": "1.1.1"
}
},
"files": [
"build/**/*",
"bin/**/*",
@@ -19,34 +27,35 @@
"LICENSE"
],
"scripts": {
"dev": "VITE_APP_VERSION=$(node -p \"require('./package.json').version\") vite dev",
"build": "VITE_APP_VERSION=$(node -p \"require('./package.json').version\") vite build",
"dev": "VITE_APP_VERSION=dev vite dev",
"prebuild": "node scripts/inject-sw-version.js",
"build": "VITE_APP_VERSION=${VITE_APP_VERSION:-$(node -p \"require('./package.json').version\")} vite build",
"preview": "VITE_APP_VERSION=$(node -p \"require('./package.json').version\") vite preview",
"start": "HOST=127.0.0.1 PORT=3000 node build",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "eslint .",
"package": "svelte-kit sync && vite build"
"package": "svelte-kit sync && VITE_APP_VERSION=$(node -p \"require('./package.json').version\") vite build",
"desktop:dev": "task desktop-dev",
"desktop:build": "task desktop-build"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/adapter-node": "^5.4.0",
"@sveltejs/kit": "^2.49.1",
"@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.49.2",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@typescript-eslint/eslint-plugin": "^8.50.1",
"@typescript-eslint/parser": "^8.50.1",
"@typescript-eslint/eslint-plugin": "^8.51.0",
"@typescript-eslint/parser": "^8.51.0",
"eslint": "^9.39.2",
"eslint-plugin-security": "^3.0.1",
"eslint-plugin-svelte": "^3.13.1",
"prettier": "^3.7.4",
"prettier-plugin-svelte": "^3.4.1",
"svelte": "^5.45.6",
"svelte-check": "^4.3.4",
"svelte": "^5.46.1",
"svelte-check": "^4.3.5",
"svelte-eslint-parser": "^1.4.1",
"typescript": "^5.9.3",
"vite": "^7.2.6"
"vite": "^7.3.0"
},
"dependencies": {
"autoprefixer": "^10.4.23",

3383
pnpm-lock.yaml generated Normal file
View File

File diff suppressed because it is too large Load Diff

3
renovate.json Normal file
View File

@@ -0,0 +1,3 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}

View File

@@ -2,5 +2,5 @@
set -euo pipefail
echo "Building app..."
VITE_APP_VERSION=$(node -p "require('./package.json').version") npm run build
VITE_APP_VERSION=$(node -p "require('./package.json').version") pnpm run build

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env node
import { readFileSync, writeFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const rootDir = join(__dirname, '..');
const packageJson = JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf-8'));
const version = packageJson.version;
const swPath = join(rootDir, 'static', 'sw.js');
let swContent = readFileSync(swPath, 'utf-8');
swContent = swContent.replace(
/const CACHE_VERSION = ['"](.*?)['"];/,
`const CACHE_VERSION = '${version}';`
);
writeFileSync(swPath, swContent);
console.log(`Injected version ${version} into service worker`);

View File

@@ -23,20 +23,16 @@ VULNS=$(jq -r '
.results[]? |
.source as $src |
.vulns[]? |
select(
(.database_specific.severity // "" | ascii_upcase | test("HIGH|CRITICAL")) or
(.severity[]?.score // "" | tostring | split("/")[0] | tonumber? // 0 | . >= 7.0)
) |
"\(.id) (source: \($src))"
' "$OSV_JSON")
if [ -n "$VULNS" ]; then
echo "OSV scan found HIGH/CRITICAL vulnerabilities:"
echo "OSV scan found vulnerabilities:"
echo "$VULNS" | while IFS= read -r line; do
echo " - $line"
done
exit 1
else
echo "OSV scan: no HIGH/CRITICAL vulnerabilities found."
echo "OSV scan: no vulnerabilities found."
fi

13
src/app.d.ts vendored
View File

@@ -8,6 +8,19 @@ declare global {
// interface PageState {}
// interface Platform {}
}
interface Window {
go?: {
main: {
App: {
SaveFile(filename: string, content: string): Promise<string>;
LoadFile(): Promise<string>;
LogFrontend(message: string): void;
};
};
};
runtime?: unknown;
}
}
export {};

View File

File diff suppressed because it is too large Load Diff

80
src/lib/constants.ts Normal file
View File

@@ -0,0 +1,80 @@
import { User, Mail, Phone, MapPin, Globe, Building2, Network, AtSign } from 'lucide-svelte';
export const DB_NAME = 'quad4-linking-db';
export const DB_VERSION = 2;
export const STORE_NAME = 'graphs';
export const SETTINGS_STORE = 'settings';
export const UNDO_STORE = 'undo_stack';
export const REDO_STORE = 'redo_stack';
export const MAX_HISTORY = 100;
export const ALLOWED_IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/webp'];
export const MAX_IMAGE_BYTES = 2 * 1024 * 1024;
export type RelationshipType =
| 'Linked'
| 'Works For'
| 'Knows'
| 'Owns'
| 'Associated With'
| 'Related To'
| 'Connected To';
export type RelationshipStrength = 'weak' | 'medium' | 'strong';
export const RELATIONSHIP_COLORS: Record<RelationshipType, string> = {
Linked: '#525252',
'Works For': '#3b82f6',
Knows: '#10b981',
Owns: '#f59e0b',
'Associated With': '#8b5cf6',
'Related To': '#ec4899',
'Connected To': '#06b6d4',
};
export const relationshipTypes: RelationshipType[] = [
'Linked',
'Works For',
'Knows',
'Owns',
'Associated With',
'Related To',
'Connected To',
];
export const relationshipStrengths: RelationshipStrength[] = ['weak', 'medium', 'strong'];
export type NodeType =
| 'person'
| 'email'
| 'phone'
| 'address'
| 'domain'
| 'org'
| 'ip'
| 'social';
export const iconMap: Record<NodeType, typeof User> = {
person: User,
email: Mail,
phone: Phone,
address: MapPin,
domain: Globe,
org: Building2,
ip: Network,
social: AtSign,
};
export const nodeTypes = Object.keys(iconMap) as NodeType[];
export const typeColors: Record<NodeType, string> = {
person: '#ef4444',
email: '#f97316',
phone: '#eab308',
address: '#10b981',
domain: '#f43f5e',
org: '#be123c',
ip: '#71717a',
social: '#db2777',
};

View File

@@ -1,36 +0,0 @@
import { User, Mail, Phone, MapPin, Globe, Building2, Network, AtSign } from 'lucide-svelte';
import type { ComponentType } from 'svelte';
export type NodeType =
| 'person'
| 'email'
| 'phone'
| 'address'
| 'domain'
| 'org'
| 'ip'
| 'social';
export const iconMap: Record<NodeType, ComponentType> = {
person: User,
email: Mail,
phone: Phone,
address: MapPin,
domain: Globe,
org: Building2,
ip: Network,
social: AtSign,
};
export const nodeTypes = Object.keys(iconMap) as NodeType[];
export const typeColors: Record<NodeType, string> = {
person: '#ef4444',
email: '#f97316',
phone: '#eab308',
address: '#10b981',
domain: '#f43f5e',
org: '#be123c',
ip: '#71717a',
social: '#db2777',
};

View File

@@ -1,13 +1,64 @@
<script lang="ts">
import '../app.css';
import { onMount } from 'svelte';
import type { Snippet } from 'svelte';
let { children }: { children: Snippet } = $props();
let showUpdateAvailable = $state(false);
let registration: ServiceWorkerRegistration | null = null;
function checkForUpdates() {
if (registration && navigator.onLine) {
registration.update().catch(() => {});
}
}
function reloadApp() {
if (registration && registration.waiting) {
registration.waiting.postMessage({ type: 'SKIP_WAITING' });
window.location.reload();
}
}
onMount(() => {
if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
navigator.serviceWorker
.register('/sw.js')
.then((registration) => {
console.log('Service Worker registered:', registration);
.then((reg) => {
registration = reg;
reg.addEventListener('updatefound', () => {
const newWorker = reg.installing;
if (newWorker) {
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed') {
if (reg.waiting) {
showUpdateAvailable = true;
} else if (navigator.serviceWorker.controller) {
showUpdateAvailable = true;
}
}
});
}
});
if (reg.waiting) {
showUpdateAvailable = true;
}
navigator.serviceWorker.addEventListener('controllerchange', () => {
window.location.reload();
});
if (navigator.onLine) {
setInterval(() => {
checkForUpdates();
}, 60000);
}
window.addEventListener('online', () => {
checkForUpdates();
});
})
.catch((error) => {
console.error('Service Worker registration failed:', error);
@@ -16,4 +67,43 @@
});
</script>
<slot />
{#if showUpdateAvailable}
<div
class="fixed bottom-4 left-1/2 -translate-x-1/2 z-50 bg-neutral-900 border border-neutral-800 rounded-lg shadow-lg p-4 max-w-md mx-4"
>
<div class="flex items-center gap-3">
<div class="flex-1">
<p class="text-sm font-medium text-white">Update Available</p>
<p class="text-xs text-neutral-400 mt-1">A new version is available. Reload to update.</p>
</div>
<button
onclick={reloadApp}
class="px-4 py-2 bg-accent-red text-white rounded-md text-sm font-medium hover:bg-accent-red-dark transition-colors"
>
Reload
</button>
<button
onclick={() => (showUpdateAvailable = false)}
class="text-neutral-400 hover:text-white transition-colors"
aria-label="Dismiss"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
</div>
{/if}
{@render children()}

View File

@@ -1,7 +1,22 @@
<script lang="ts">
import IdentityGraph from '../components/IdentityGraph.svelte';
import { APP_VERSION } from '$lib/version';
import { Link as LinkIcon, GitBranch } from 'lucide-svelte';
import { GitBranch } from 'lucide-svelte';
const REPO_URL = 'https://git.quad4.io/Quad4-Software/Linking-Tool';
const isCommitSha = /^[a-f0-9]{7,}$/i.test(APP_VERSION);
const isTag = APP_VERSION.startsWith('v') && APP_VERSION !== 'dev';
const displayVersion =
APP_VERSION.startsWith('v') || APP_VERSION === 'dev' || isCommitSha
? APP_VERSION
: `v${APP_VERSION}`;
const versionUrl = isCommitSha
? `${REPO_URL}/commit/${APP_VERSION}`
: isTag
? `${REPO_URL}/releases/tag/${APP_VERSION}`
: REPO_URL;
</script>
<svelte:head>
@@ -21,42 +36,40 @@
</svelte:head>
<div class="flex flex-col h-screen bg-bg-primary text-text-primary">
<header
class="bg-neutral-950 border-b border-neutral-800 px-2 sm:px-6 py-1.5 sm:py-3 flex flex-col sm:flex-row justify-between items-center gap-1 sm:gap-2 flex-shrink-0 shadow-lg"
>
<a
href="https://git.quad4.io/Quad4-Software/Linking-Tool"
target="_blank"
rel="noopener noreferrer"
class="text-sm sm:text-xl font-semibold text-accent-red-light flex items-center gap-1.5 sm:gap-2 hover:text-accent-red-dark transition-colors"
>
<div
class="h-4 w-4 sm:h-5 sm:w-5 rounded border border-accent-red-light flex items-center justify-center bg-neutral-900"
>
<LinkIcon size={12} class="sm:w-[14px] sm:h-[14px] text-accent-red-light" />
</div>
Linking Tool
</a>
<div class="text-text-secondary text-[10px] sm:text-sm flex items-center gap-1 sm:gap-2">
<main class="flex-1 relative overflow-hidden bg-bg-primary p-0 sm:p-4">
<IdentityGraph />
</main>
<footer class="bg-neutral-950 border-t border-neutral-800 px-4 py-2 flex-shrink-0">
<div class="text-text-secondary text-xs flex items-center justify-center gap-2">
<span
>Created by <a
>Linking Tool - Created by <a
href="https://quad4.io"
target="_blank"
rel="noopener noreferrer"
class="text-accent-red-light hover:text-accent-red-dark transition-colors">Quad4</a
>
-
<a
href="https://git.quad4.io/Quad4-Software/Linking-Tool"
target="_blank"
rel="noopener noreferrer"
class="text-accent-red-light hover:text-accent-red-dark transition-colors inline-flex items-center gap-1"
>v{APP_VERSION} <GitBranch size={12} /></a
></span
>
<span class="inline-flex items-center gap-1">
{#if isCommitSha || isTag}
<a
href={versionUrl}
target="_blank"
rel="noopener noreferrer"
class="text-accent-red-light hover:text-accent-red-dark transition-colors"
>{displayVersion}</a
>
{:else}
<span>{displayVersion}</span>
{/if}
<a
href={REPO_URL}
target="_blank"
rel="noopener noreferrer"
class="text-accent-red-light hover:text-accent-red-dark transition-colors"
><GitBranch size={12} /></a
>
</span>
</span>
</div>
</header>
<main class="flex-1 relative overflow-hidden bg-bg-primary p-0 sm:p-4">
<IdentityGraph />
</main>
</footer>
</div>

View File

@@ -1,4 +1,5 @@
const CACHE_NAME = 'quad4-linking-tool-v1';
const CACHE_VERSION = '1.5.3';
const CACHE_NAME = `quad4-linking-tool-${CACHE_VERSION}`;
const urlsToCache = ['/', '/favicon.svg', '/manifest.json'];
self.addEventListener('install', (event) => {
@@ -17,7 +18,7 @@ self.addEventListener('activate', (event) => {
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
if (cacheName !== CACHE_NAME && cacheName.startsWith('quad4-linking-tool-')) {
return caches.delete(cacheName);
}
})
@@ -27,26 +28,40 @@ self.addEventListener('activate', (event) => {
self.clients.claim();
});
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') {
return;
}
if (!event.request.url.startsWith(self.location.origin)) {
return;
}
event.respondWith(
caches.match(event.request).then((response) => {
if (response) {
return response;
}
return fetch(event.request).then((response) => {
if (!response || response.status !== 200 || response.type !== 'basic') {
return fetch(event.request)
.then((response) => {
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
const responseToCache = response.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseToCache);
});
return response;
}
const responseToCache = response.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseToCache);
})
.catch(() => {
return caches.match('/') || new Response('Offline', { status: 503 });
});
return response;
});
})
);
});

View File

@@ -1,11 +0,0 @@
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
},
};
export default config;

View File

@@ -1,12 +1,28 @@
import adapter from '@sveltejs/adapter-node';
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
compilerOptions: (id) => {
if (id && id.includes('node_modules')) {
return {
runes: false, // Disable runes for external dependencies that might not support it yet (lucide-svelte)
};
}
return {
runes: true,
};
},
kit: {
adapter: adapter(),
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: 'index.html',
precompress: false,
strict: true,
}),
},
};