0.1.0
This commit is contained in:
23
.dockerignore
Normal file
23
.dockerignore
Normal file
@@ -0,0 +1,23 @@
|
||||
node_modules
|
||||
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
.netlify
|
||||
.wrangler
|
||||
/.svelte-kit
|
||||
/build
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
# Vite
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
39
.gitea/workflows/ci.yml
Normal file
39
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Svelte check (fail on warnings)
|
||||
run: bash scripts/check.sh
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: check
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build
|
||||
run: bash scripts/build.sh
|
||||
20
.gitea/workflows/osv-pr.yml
Normal file
20
.gitea/workflows/osv-pr.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
name: OSV-Scanner PR Scan
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
merge_group:
|
||||
branches: [master]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
scan-pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: OSV scan
|
||||
run: bash scripts/osv_scan.sh
|
||||
20
.gitea/workflows/osv-scheduled.yml
Normal file
20
.gitea/workflows/osv-scheduled.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
name: OSV-Scanner Scheduled Scan
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 12 * * 1'
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
scan-scheduled:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: OSV scan
|
||||
run: bash scripts/osv_scan.sh
|
||||
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
node_modules
|
||||
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
.netlify
|
||||
.wrangler
|
||||
/.svelte-kit
|
||||
/build
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
# Vite
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
6
.prettierignore
Normal file
6
.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
.svelte-kit
|
||||
build
|
||||
dist
|
||||
.DS_Store
|
||||
|
||||
8
.prettierrc
Normal file
8
.prettierrc
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
||||
33
Dockerfile
Normal file
33
Dockerfile
Normal file
@@ -0,0 +1,33 @@
|
||||
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"]
|
||||
|
||||
22
LICENSE
Normal file
22
LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
MIT 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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
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.
|
||||
|
||||
32
Makefile
Normal file
32
Makefile
Normal file
@@ -0,0 +1,32 @@
|
||||
.PHONY: help install dev build preview check lint format clean
|
||||
|
||||
help:
|
||||
@echo 'Usage: make [target]'
|
||||
@echo ''
|
||||
@echo 'Available targets:'
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
|
||||
install:
|
||||
npm install
|
||||
|
||||
dev:
|
||||
npm run dev
|
||||
|
||||
build:
|
||||
npm run build
|
||||
|
||||
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
|
||||
|
||||
76
eslint.config.js
Normal file
76
eslint.config.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import js from '@eslint/js';
|
||||
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import sveltePlugin from 'eslint-plugin-svelte';
|
||||
import svelteParser from 'svelte-eslint-parser';
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ['**/*.{js,mjs,cjs,ts,svelte}'],
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
},
|
||||
globals: {
|
||||
fetch: 'readonly',
|
||||
caches: 'readonly',
|
||||
URL: 'readonly',
|
||||
console: 'readonly',
|
||||
HTMLElement: 'readonly',
|
||||
HTMLImageElement: 'readonly',
|
||||
HTMLInputElement: 'readonly',
|
||||
HTMLTextAreaElement: 'readonly',
|
||||
HTMLSelectElement: 'readonly',
|
||||
HTMLDivElement: 'readonly',
|
||||
SVGSVGElement: 'readonly',
|
||||
navigator: 'readonly',
|
||||
window: 'readonly',
|
||||
document: 'readonly',
|
||||
Blob: 'readonly',
|
||||
Event: 'readonly',
|
||||
MouseEvent: 'readonly',
|
||||
WheelEvent: 'readonly',
|
||||
KeyboardEvent: 'readonly',
|
||||
URLSearchParams: 'readonly',
|
||||
setTimeout: 'readonly',
|
||||
clearTimeout: 'readonly',
|
||||
localStorage: 'readonly',
|
||||
atob: 'readonly',
|
||||
btoa: 'readonly',
|
||||
alert: 'readonly',
|
||||
prompt: 'readonly',
|
||||
XMLSerializer: 'readonly',
|
||||
Image: 'readonly',
|
||||
FileReader: 'readonly',
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
'@typescript-eslint': tsPlugin,
|
||||
svelte: sveltePlugin,
|
||||
},
|
||||
rules: {
|
||||
...tsPlugin.configs.recommended.rules,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.svelte'],
|
||||
languageOptions: {
|
||||
parser: svelteParser,
|
||||
parserOptions: {
|
||||
parser: tsParser,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
svelte: sveltePlugin,
|
||||
},
|
||||
rules: {
|
||||
...sveltePlugin.configs.recommended.rules,
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['node_modules/**', '.svelte-kit/**', 'build/**', 'dist/**', 'archive/**'],
|
||||
},
|
||||
];
|
||||
4125
package-lock.json
generated
Normal file
4125
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
39
package.json
Normal file
39
package.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "quad4-linking-tool",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"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",
|
||||
"preview": "VITE_APP_VERSION=$(node -p \"require('./package.json').version\") vite preview",
|
||||
"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 ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@sveltejs/adapter-auto": "^7.0.0",
|
||||
"@sveltejs/kit": "^2.49.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
||||
"@typescript-eslint/parser": "^8.50.1",
|
||||
"eslint": "^9.39.2",
|
||||
"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-eslint-parser": "^1.4.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.2.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"autoprefixer": "^10.4.23",
|
||||
"lucide-svelte": "^0.562.0",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.19"
|
||||
}
|
||||
}
|
||||
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
6
scripts/build.sh
Normal file
6
scripts/build.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Building app..."
|
||||
VITE_APP_VERSION=$(node -p "require('./package.json').version") npm run build
|
||||
|
||||
9
scripts/check.sh
Normal file
9
scripts/check.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Running Svelte sync..."
|
||||
npx svelte-kit sync
|
||||
|
||||
echo "Running svelte-check (fail on errors)..."
|
||||
npx svelte-check --tsconfig ./tsconfig.json
|
||||
|
||||
42
scripts/osv_scan.sh
Normal file
42
scripts/osv_scan.sh
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
OSV_VERSION="${OSV_VERSION:-v2.3.1}"
|
||||
|
||||
echo "Installing OSV-Scanner ${OSV_VERSION}..."
|
||||
curl -sSL "https://github.com/google/osv-scanner/releases/download/${OSV_VERSION}/osv-scanner_linux_amd64" -o /tmp/osv-scanner
|
||||
chmod +x /tmp/osv-scanner
|
||||
sudo mv /tmp/osv-scanner /usr/local/bin/osv-scanner
|
||||
|
||||
echo "Running OSV-Scanner recursively..."
|
||||
OSV_JSON="$(mktemp)"
|
||||
trap 'rm -f "$OSV_JSON"' EXIT
|
||||
|
||||
osv-scanner --recursive ./ --format json > "$OSV_JSON" || true
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "Error: jq is not installed. Please install jq to parse OSV results."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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 "$VULNS" | while IFS= read -r line; do
|
||||
echo " - $line"
|
||||
done
|
||||
exit 1
|
||||
else
|
||||
echo "OSV scan: no HIGH/CRITICAL vulnerabilities found."
|
||||
fi
|
||||
|
||||
78
src/app.css
Normal file
78
src/app.css
Normal file
@@ -0,0 +1,78 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src:
|
||||
local(''),
|
||||
url('/fonts/nunito-v16-latin-regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
html {
|
||||
@apply bg-bg-primary text-text-primary;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply m-0 p-0 min-h-screen bg-bg-primary text-text-primary font-sans antialiased;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply bg-accent-red text-text-primary border-none px-4 py-2 rounded-md cursor-pointer text-sm font-medium flex items-center gap-2 transition-colors;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
@apply bg-accent-red-dark;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
@apply bg-accent-red-dark;
|
||||
}
|
||||
|
||||
.btn.active {
|
||||
@apply bg-accent-red-dark;
|
||||
}
|
||||
|
||||
.btn-icon-only {
|
||||
@apply p-2;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
@apply flex items-center gap-1;
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
@apply p-1.5 rounded text-text-primary hover:bg-bg-primary/50 transition-colors cursor-pointer border-none bg-transparent;
|
||||
}
|
||||
|
||||
.toolbar-btn.active {
|
||||
@apply bg-accent-red/20 text-accent-red-light;
|
||||
}
|
||||
|
||||
.toolbar-select {
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
.toolbar-select option {
|
||||
background-color: #171717;
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
.loading-bar {
|
||||
@apply h-1 bg-accent-red transition-all duration-300;
|
||||
width: 0%;
|
||||
}
|
||||
|
||||
.loading-bar.active {
|
||||
@apply w-full;
|
||||
}
|
||||
}
|
||||
13
src/app.d.ts
vendored
Normal file
13
src/app.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
17
src/app.html
Normal file
17
src/app.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" content="#dc2626" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Linking Tool" />
|
||||
<link rel="apple-touch-icon" href="/favicon.svg" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
1733
src/components/IdentityGraph.svelte
Normal file
1733
src/components/IdentityGraph.svelte
Normal file
File diff suppressed because it is too large
Load Diff
5
src/lib/assets/favicon.svg
Normal file
5
src/lib/assets/favicon.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="2" y="2" width="20" height="20" rx="4" fill="none"/>
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 374 B |
45
src/lib/identityGraph.ts
Normal file
45
src/lib/identityGraph.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
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',
|
||||
};
|
||||
1
src/lib/index.ts
Normal file
1
src/lib/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
1
src/lib/version.ts
Normal file
1
src/lib/version.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const APP_VERSION = import.meta.env.VITE_APP_VERSION || 'dev';
|
||||
19
src/routes/+layout.svelte
Normal file
19
src/routes/+layout.svelte
Normal file
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
|
||||
navigator.serviceWorker
|
||||
.register('/sw.js')
|
||||
.then((registration) => {
|
||||
console.log('Service Worker registered:', registration);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Service Worker registration failed:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
57
src/routes/+page.svelte
Normal file
57
src/routes/+page.svelte
Normal file
@@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import IdentityGraph from '../components/IdentityGraph.svelte';
|
||||
import { APP_VERSION } from '$lib/version';
|
||||
import { Link as LinkIcon, GitBranch } from 'lucide-svelte';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Linking Tool - Identity Graph</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Linking Tool - A client-side identity graph visualization tool for mapping relationships between entities."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="Linking Tool - Identity Graph" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="A client-side identity graph visualization tool for mapping relationships between entities."
|
||||
/>
|
||||
<meta property="og:image" content="/favicon.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
</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-4 sm:px-6 py-3 flex flex-col sm:flex-row justify-between items-center gap-2 flex-shrink-0 shadow-lg"
|
||||
>
|
||||
<h1 class="text-lg sm:text-xl font-semibold text-accent-red-light flex items-center gap-2">
|
||||
<div
|
||||
class="h-5 w-5 rounded border border-accent-red-light flex items-center justify-center bg-neutral-900"
|
||||
>
|
||||
<LinkIcon size={14} class="text-accent-red-light" />
|
||||
</div>
|
||||
Linking Tool
|
||||
</h1>
|
||||
<div class="text-text-secondary text-xs sm:text-sm flex items-center gap-2">
|
||||
<span
|
||||
>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
|
||||
>
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex-1 relative overflow-hidden bg-bg-primary p-4">
|
||||
<IdentityGraph />
|
||||
</main>
|
||||
</div>
|
||||
5
static/favicon.svg
Normal file
5
static/favicon.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="2" y="2" width="20" height="20" rx="4" fill="none"/>
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 374 B |
0
static/fonts/nunito-v16-latin-regular.woff2
Normal file
0
static/fonts/nunito-v16-latin-regular.woff2
Normal file
19
static/manifest.json
Normal file
19
static/manifest.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Linking Tool",
|
||||
"short_name": "Linking Tool",
|
||||
"description": "A client-side identity graph visualization tool for mapping relationships between entities",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#0a0a0a",
|
||||
"theme_color": "#dc2626",
|
||||
"orientation": "any",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
3
static/robots.txt
Normal file
3
static/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# disallow all
|
||||
User-agent: *
|
||||
Disallow: /
|
||||
58
static/sw.js
Normal file
58
static/sw.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const CACHE_NAME = 'quad4-linking-tool-v1';
|
||||
const urlsToCache = [
|
||||
'/',
|
||||
'/favicon.svg',
|
||||
'/fonts/nunito-v16-latin-regular.woff2',
|
||||
'/manifest.json'
|
||||
];
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
return cache.addAll(urlsToCache).catch(() => {
|
||||
console.log('Some resources failed to cache');
|
||||
});
|
||||
})
|
||||
);
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames.map((cacheName) => {
|
||||
if (cacheName !== CACHE_NAME) {
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
if (event.request.method !== 'GET') {
|
||||
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 response;
|
||||
}
|
||||
const responseToCache = response.clone();
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
cache.put(event.request, responseToCache);
|
||||
});
|
||||
return response;
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
11
svelte.config.docker.js
Normal file
11
svelte.config.docker.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import adapter from '@sveltejs/adapter-node';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
const config = {
|
||||
preprocess: vitePreprocess(),
|
||||
kit: {
|
||||
adapter: adapter(),
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
18
svelte.config.js
Normal file
18
svelte.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://svelte.dev/docs/kit/integrations
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||
adapter: adapter(),
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
22
tailwind.config.js
Normal file
22
tailwind.config.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'bg-primary': '#0a0a0a',
|
||||
'bg-secondary': '#171717',
|
||||
'text-primary': '#fafafa',
|
||||
'text-secondary': '#a3a3a3',
|
||||
'accent-red': '#dc2626',
|
||||
'accent-red-dark': '#991b1b',
|
||||
'accent-red-light': '#ef4444',
|
||||
'border-color': '#262626',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Nunito', 'Inter', 'Segoe UI', 'system-ui', '-apple-system', 'sans-serif'],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
20
tsconfig.json
Normal file
20
tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rewriteRelativeImportExtensions": true,
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
// To make changes to top-level options such as include and exclude, we recommend extending
|
||||
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
|
||||
}
|
||||
6
vite.config.ts
Normal file
6
vite.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
});
|
||||
Reference in New Issue
Block a user