Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 64f162440f | |||
| e30e0395d3 | |||
| d1013c87a1 | |||
| c3c3fc83e3 | |||
| 14ce5c728d | |||
| 60f56a2dea | |||
| 099cc252fe | |||
| 3a6be50e31 |
@@ -0,0 +1,37 @@
|
||||
name: Deploy GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/deploy-pages.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Configure Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: docs
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
@@ -1,4 +1,4 @@
|
||||
# Alta Video Camera Proxy
|
||||
# Alta Video Camera Proxy
|
||||
|
||||
An Electron desktop application for managing Alta Video camera proxy connections. Authenticates via a companion Chrome extension that imports your existing Alta session cookie, discovers cameras, and launches proxy connections.
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# build-kit.ps1 — Build the Alta Proxy Tool Kit zip for distribution
|
||||
# Usage: powershell -ExecutionPolicy Bypass -File build-kit.ps1
|
||||
#
|
||||
# Produces: dist/AltaProxyToolKit.zip containing:
|
||||
# - AltaCameraProxy-<version>-portable.exe
|
||||
# - aware-cam-proxy.exe
|
||||
# - chrome-extension/ (folder)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Read version from package.json
|
||||
$pkg = Get-Content "package.json" -Raw | ConvertFrom-Json
|
||||
$version = $pkg.version
|
||||
Write-Host "Building Alta Proxy Tool Kit v$version" -ForegroundColor Cyan
|
||||
|
||||
# Step 1: Build the Electron portable exe
|
||||
Write-Host "`n[1/4] Building Electron app..." -ForegroundColor Yellow
|
||||
npm run build
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Build failed!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$portableExe = "dist\AltaCameraProxy-$version-portable.exe"
|
||||
if (-not (Test-Path $portableExe)) {
|
||||
Write-Host "Expected output not found: $portableExe" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host " Built: $portableExe" -ForegroundColor Green
|
||||
|
||||
# Step 2: Verify aware-cam-proxy.exe exists
|
||||
Write-Host "`n[2/4] Checking for aware-cam-proxy.exe..." -ForegroundColor Yellow
|
||||
if (-not (Test-Path "aware-cam-proxy.exe")) {
|
||||
Write-Host "aware-cam-proxy.exe not found in project root!" -ForegroundColor Red
|
||||
Write-Host "Place aware-cam-proxy.exe in the project root and try again." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host " Found: aware-cam-proxy.exe" -ForegroundColor Green
|
||||
|
||||
# Step 3: Verify chrome-extension folder exists
|
||||
Write-Host "`n[3/4] Checking for chrome-extension/..." -ForegroundColor Yellow
|
||||
if (-not (Test-Path "chrome-extension\manifest.json")) {
|
||||
Write-Host "chrome-extension/ folder not found or missing manifest.json!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host " Found: chrome-extension/" -ForegroundColor Green
|
||||
|
||||
# Step 4: Create the kit zip
|
||||
Write-Host "`n[4/4] Creating AltaProxyToolKit.zip..." -ForegroundColor Yellow
|
||||
|
||||
$kitDir = "dist\AltaProxyToolKit"
|
||||
$zipPath = "dist\AltaProxyToolKit.zip"
|
||||
|
||||
# Clean previous kit
|
||||
if (Test-Path $kitDir) { Remove-Item $kitDir -Recurse -Force }
|
||||
if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
|
||||
|
||||
# Assemble kit contents
|
||||
New-Item -ItemType Directory -Path $kitDir | Out-Null
|
||||
Copy-Item $portableExe "$kitDir\AltaCameraProxy.exe"
|
||||
Copy-Item "aware-cam-proxy.exe" "$kitDir\aware-cam-proxy.exe"
|
||||
Copy-Item "chrome-extension" "$kitDir\chrome-extension" -Recurse
|
||||
|
||||
# Create zip
|
||||
Compress-Archive -Path "$kitDir\*" -DestinationPath $zipPath -Force
|
||||
|
||||
# Clean up temp directory
|
||||
Remove-Item $kitDir -Recurse -Force
|
||||
|
||||
$zipSize = [math]::Round((Get-Item $zipPath).Length / 1MB, 1)
|
||||
Write-Host "`nKit ready: $zipPath ($zipSize MB)" -ForegroundColor Green
|
||||
Write-Host "Upload this file as a release asset named 'AltaProxyToolKit.zip'" -ForegroundColor Cyan
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
+341
@@ -0,0 +1,341 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- GitHub Pages rebuild marker: 2026-05-22 -->
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Alta Proxy Tool</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-primary: #1E1E1E;
|
||||
--bg-secondary: #2D2D30;
|
||||
--border: #3C3C3C;
|
||||
--text-primary: #E0E0E0;
|
||||
--text-secondary: #999999;
|
||||
--accent-primary: #0E7AFE;
|
||||
--accent-primary-hover: #0A5FD9;
|
||||
--card-bg: #2D2D30;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.header {
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 16px 0;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.header .container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.header-brand img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.header-brand span {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.header-link {
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.header-link:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Hero */
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hero {
|
||||
text-align: center;
|
||||
padding: 100px 0 48px;
|
||||
}
|
||||
|
||||
.hero-icon {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 40px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.hero .tagline {
|
||||
font-size: 18px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 40px;
|
||||
max-width: 500px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.btn-download {
|
||||
display: inline-block;
|
||||
background: var(--accent-primary);
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
padding: 16px 40px;
|
||||
border-radius: 6px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.btn-download:hover {
|
||||
background: var(--accent-primary-hover);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hero-note {
|
||||
margin-top: 14px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Kit contents */
|
||||
.kit-info {
|
||||
padding: 48px 0;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.kit-info h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.kit-contents {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.kit-item {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 20px 28px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.kit-item strong {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.kit-item span {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Setup */
|
||||
.setup {
|
||||
padding: 48px 0;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.setup h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.setup-steps {
|
||||
list-style: none;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
counter-reset: step;
|
||||
}
|
||||
|
||||
.setup-steps li {
|
||||
counter-increment: step;
|
||||
padding: 12px 0 12px 44px;
|
||||
position: relative;
|
||||
font-size: 15px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.setup-steps li + li {
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.setup-steps li::before {
|
||||
content: counter(step);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 12px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: var(--accent-primary);
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.setup-steps code {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
padding: 1px 5px;
|
||||
border-radius: 3px;
|
||||
font-size: 13px;
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.setup-note {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
border-top: 1px solid var(--border);
|
||||
padding: 24px 0;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 600px) {
|
||||
.hero {
|
||||
padding: 60px 0 36px;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.hero .tagline {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.btn-download {
|
||||
font-size: 16px;
|
||||
padding: 12px 28px;
|
||||
}
|
||||
|
||||
.kit-contents {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.kit-item {
|
||||
width: 100%;
|
||||
max-width: 280px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<div class="header-brand">
|
||||
<img src="icon.png" alt="Alta Proxy Tool">
|
||||
<span>Alta Proxy Tool</span>
|
||||
</div>
|
||||
<a href="https://github.com/PageZ948/Alta-Proxy-Tool" class="header-link">GitHub</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="container">
|
||||
|
||||
<div class="hero">
|
||||
<img src="icon.png" alt="" class="hero-icon">
|
||||
<h1>Alta Proxy Tool</h1>
|
||||
<a href="https://github.com/PageZ948/Alta-Proxy-Tool/releases/latest/download/AltaProxyToolKit.zip" class="btn-download">Download Kit for Windows</a>
|
||||
</div>
|
||||
|
||||
<section class="setup">
|
||||
<h2>Setup</h2>
|
||||
<ol class="setup-steps">
|
||||
<li>Extract the zip to any folder</li>
|
||||
<li>Load <code>chrome-extension/</code> in Chrome via <code>chrome://extensions</code> (Developer mode)</li>
|
||||
<li>Log into your Alta deployment in Chrome</li>
|
||||
<li>Click the extension icon and send cookies to the app</li>
|
||||
<li>Run <strong>AltaCameraProxy.exe</strong> and start proxying</li>
|
||||
</ol>
|
||||
<p class="setup-note">Requires Windows 10+, Google Chrome, and an Avigilon Alta account.</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<a href="https://github.com/PageZ948/Alta-Proxy-Tool">Alta Proxy Tool on GitHub</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -13,6 +13,16 @@ let cookieServer = null;
|
||||
const COOKIE_SERVER_PORT = 18247;
|
||||
const COOKIE_SERVER_TOKEN = 'apt-local-bridge-token';
|
||||
|
||||
// Get the directory where the user-facing executable resides.
|
||||
// In portable builds, __dirname points to a temp extraction directory,
|
||||
// so we use the actual .exe location instead.
|
||||
function getAppDirectory() {
|
||||
if (app.isPackaged) {
|
||||
return path.dirname(process.env.PORTABLE_EXECUTABLE_FILE || app.getPath('exe'));
|
||||
}
|
||||
return __dirname;
|
||||
}
|
||||
|
||||
// Sanitize strings before embedding in batch files to prevent command injection
|
||||
function sanitizeBatchInput(input) {
|
||||
if (typeof input !== 'string') return '';
|
||||
@@ -208,6 +218,33 @@ ipcMain.handle('api-get-devices', async (event, { deploymentUrl, cookies }) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('api-get-device-sites', async (event, { deploymentUrl, cookies }) => {
|
||||
try {
|
||||
const sitesUrl = `${deploymentUrl}/api/v1/deviceSites`;
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Cookie': cookies ? cookies.join('; ') : ''
|
||||
}
|
||||
});
|
||||
|
||||
const response = await axiosInstance.get(sitesUrl);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
sites: response.data
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Get device sites error:', error);
|
||||
return {
|
||||
success: false,
|
||||
sites: [],
|
||||
message: error.response?.data?.message || error.message || 'Failed to get device sites'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('api-get-auth-info', async (event, { deploymentUrl, cookies }) => {
|
||||
try {
|
||||
const authUrl = `${deploymentUrl}/api/v1/auth`;
|
||||
@@ -238,7 +275,7 @@ ipcMain.handle('api-get-auth-info', async (event, { deploymentUrl, cookies }) =>
|
||||
ipcMain.handle('camera-proxy-cookie-launch', async (event, { deploymentUrl, cookieKey, deviceUuid }) => {
|
||||
try {
|
||||
// Path to the cookie-based camera proxy executable
|
||||
const proxyExePath = path.join(__dirname, 'aware-cam-proxy.exe');
|
||||
const proxyExePath = path.join(getAppDirectory(), 'aware-cam-proxy.exe');
|
||||
|
||||
// Check if the executable exists
|
||||
if (!fs.existsSync(proxyExePath)) {
|
||||
@@ -502,7 +539,7 @@ ipcMain.handle('download-and-install-update', async (event, { downloadUrl }) =>
|
||||
try {
|
||||
// Determine the path to the currently running executable
|
||||
const currentExePath = process.env.PORTABLE_EXECUTABLE_FILE || app.getPath('exe');
|
||||
const currentDir = path.dirname(currentExePath);
|
||||
const currentDir = getAppDirectory();
|
||||
const currentExeName = path.basename(currentExePath);
|
||||
|
||||
// Check write permission on the app directory
|
||||
|
||||
Generated
+1
-615
@@ -15,9 +15,7 @@
|
||||
"devDependencies": {
|
||||
"electron": "^28.0.0",
|
||||
"electron-builder": "^26.4.0",
|
||||
"electron-packager": "^17.1.2",
|
||||
"png-to-ico": "^3.0.1",
|
||||
"sharp": "^0.34.5"
|
||||
"electron-packager": "^17.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@develar/schema-utils": {
|
||||
@@ -459,507 +457,6 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
|
||||
"integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
|
||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
|
||||
"integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
|
||||
"integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
|
||||
"integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
|
||||
"integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
|
||||
"integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
|
||||
"integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-riscv64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
|
||||
"integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
|
||||
"integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
|
||||
"integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
|
||||
"integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/balanced-match": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
|
||||
@@ -5096,51 +4593,6 @@
|
||||
"node": ">=10.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/png-to-ico": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/png-to-ico/-/png-to-ico-3.0.1.tgz",
|
||||
"integrity": "sha512-S8BOAoaGd9gT5uaemQ62arIY3Jzco7Uc7LwUTqRyqJDTsKqOAiyfyN4dSdT0D+Zf8XvgztgpRbM5wnQd7EgYwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^22.10.3",
|
||||
"minimist": "^1.2.8",
|
||||
"pngjs": "^7.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"png-to-ico": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/png-to-ico/node_modules/@types/node": {
|
||||
"version": "22.19.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.10.tgz",
|
||||
"integrity": "sha512-tF5VOugLS/EuDlTBijk0MqABfP8UxgYazTLo3uIn3b4yJgg26QRbVYJYsDtHrjdDUIRfP70+VfhTTc+CE1yskw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/png-to-ico/node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pngjs": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
|
||||
"integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postject": {
|
||||
"version": "1.0.0-alpha.6",
|
||||
"resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz",
|
||||
@@ -5522,64 +4974,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.5",
|
||||
"@img/sharp-darwin-x64": "0.34.5",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4",
|
||||
"@img/sharp-linux-arm": "0.34.5",
|
||||
"@img/sharp-linux-arm64": "0.34.5",
|
||||
"@img/sharp-linux-ppc64": "0.34.5",
|
||||
"@img/sharp-linux-riscv64": "0.34.5",
|
||||
"@img/sharp-linux-s390x": "0.34.5",
|
||||
"@img/sharp-linux-x64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-arm64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-x64": "0.34.5",
|
||||
"@img/sharp-wasm32": "0.34.5",
|
||||
"@img/sharp-win32-arm64": "0.34.5",
|
||||
"@img/sharp-win32-ia32": "0.34.5",
|
||||
"@img/sharp-win32-x64": "0.34.5"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -6168,14 +5562,6 @@
|
||||
"utf8-byte-length": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
|
||||
|
||||
+5
-6
@@ -8,6 +8,7 @@
|
||||
"dev": "electron . --dev",
|
||||
"build": "electron-builder --win --publish=never",
|
||||
"build-test": "electron-builder --win --dir",
|
||||
"build-kit": "powershell -ExecutionPolicy Bypass -File build-kit.ps1",
|
||||
"prebuild": "echo Checking build requirements..."
|
||||
},
|
||||
"build": {
|
||||
@@ -18,13 +19,13 @@
|
||||
},
|
||||
"win": {
|
||||
"target": "portable",
|
||||
"icon": "assets/icon.png"
|
||||
"icon": "assets/icon.png",
|
||||
"signAndEditExecutable": false
|
||||
},
|
||||
"portable": {
|
||||
"artifactName": "AltaCameraProxy-${version}-portable.exe"
|
||||
},
|
||||
"afterSign": false,
|
||||
"afterAllArtifactBuild": false
|
||||
"forceCodeSigning": false
|
||||
},
|
||||
"keywords": [
|
||||
"electron",
|
||||
@@ -37,9 +38,7 @@
|
||||
"devDependencies": {
|
||||
"electron": "^28.0.0",
|
||||
"electron-builder": "^26.4.0",
|
||||
"electron-packager": "^17.1.2",
|
||||
"png-to-ico": "^3.0.1",
|
||||
"sharp": "^0.34.5"
|
||||
"electron-packager": "^17.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
|
||||
@@ -4,6 +4,7 @@ const { contextBridge, ipcRenderer } = require('electron');
|
||||
// the ipcRenderer without exposing the entire object
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
getDevices: (params) => ipcRenderer.invoke('api-get-devices', params),
|
||||
getDeviceSites: (params) => ipcRenderer.invoke('api-get-device-sites', params),
|
||||
getAuthInfo: (params) => ipcRenderer.invoke('api-get-auth-info', params),
|
||||
|
||||
// Camera proxy functionality
|
||||
|
||||
+178
-30
@@ -37,6 +37,8 @@ const updateLaterBtn = document.getElementById('updateLaterBtn');
|
||||
let selectedDevice = null;
|
||||
let activeCookieProxyConnections = new Map(); // Track cookie-based proxy connections
|
||||
let allDevices = []; // Store all devices for search functionality
|
||||
let allSites = {}; // Store site id → site name mapping
|
||||
let collapsedSites = new Set(); // Track which site groups are collapsed
|
||||
let pendingUpdateInfo = null; // Store update info for install action
|
||||
|
||||
// Event listeners
|
||||
@@ -66,6 +68,8 @@ function handleDisconnect() {
|
||||
selectedDevice = null;
|
||||
activeCookieProxyConnections.clear();
|
||||
allDevices = []; // Clear stored devices
|
||||
allSites = {}; // Clear site data
|
||||
collapsedSites.clear();
|
||||
|
||||
updateConnectionStatus(false);
|
||||
updateButtonStates();
|
||||
@@ -218,6 +222,28 @@ function updateButtonStates() {
|
||||
updateCookieProxyButtonStates();
|
||||
}
|
||||
|
||||
// Fetch device sites and build id → name map
|
||||
async function fetchDeviceSites() {
|
||||
try {
|
||||
const result = await window.electronAPI.getDeviceSites({
|
||||
deploymentUrl: sessionData.deploymentUrl,
|
||||
cookies: sessionData.cookies
|
||||
});
|
||||
|
||||
if (result.success && Array.isArray(result.sites)) {
|
||||
allSites = {};
|
||||
result.sites.forEach(site => {
|
||||
if (site.id) {
|
||||
allSites[site.id] = site.name || 'Unnamed Site';
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Could not fetch device sites:', error.message);
|
||||
allSites = {};
|
||||
}
|
||||
}
|
||||
|
||||
// Handle get devices (now called automatically)
|
||||
async function handleGetDevices() {
|
||||
if (!sessionData.isConnected) {
|
||||
@@ -229,14 +255,18 @@ async function handleGetDevices() {
|
||||
clearDeviceList();
|
||||
|
||||
try {
|
||||
const result = await window.electronAPI.getDevices({
|
||||
deploymentUrl: sessionData.deploymentUrl,
|
||||
cookies: sessionData.cookies
|
||||
});
|
||||
// Fetch devices and sites in parallel
|
||||
const [devicesResult] = await Promise.all([
|
||||
window.electronAPI.getDevices({
|
||||
deploymentUrl: sessionData.deploymentUrl,
|
||||
cookies: sessionData.cookies
|
||||
}),
|
||||
fetchDeviceSites()
|
||||
]);
|
||||
|
||||
if (result.success) {
|
||||
if (devicesResult.success) {
|
||||
// Filter devices to only show non-cloud cameras (localStorage = false)
|
||||
const filteredDevices = result.devices.filter(device => {
|
||||
const filteredDevices = devicesResult.devices.filter(device => {
|
||||
// Check if device has capabilities and localStorage property
|
||||
if (device.capabilities && device.capabilities.localStorage !== undefined) {
|
||||
// Only show devices where localStorage is false (non-cloud cameras)
|
||||
@@ -246,11 +276,15 @@ async function handleGetDevices() {
|
||||
return true;
|
||||
});
|
||||
|
||||
const totalDevices = result.devices.length;
|
||||
const totalDevices = devicesResult.devices.length;
|
||||
const filteredCount = filteredDevices.length;
|
||||
const cloudDevicesHidden = totalDevices - filteredCount;
|
||||
|
||||
const siteCount = Object.keys(allSites).length;
|
||||
let statusMessage = `Found ${filteredCount} local camera${filteredCount !== 1 ? 's' : ''}`;
|
||||
if (siteCount > 0) {
|
||||
statusMessage += ` across ${siteCount} site${siteCount !== 1 ? 's' : ''}`;
|
||||
}
|
||||
if (cloudDevicesHidden > 0) {
|
||||
statusMessage += ` (${cloudDevicesHidden} cloud camera${cloudDevicesHidden !== 1 ? 's' : ''} hidden)`;
|
||||
}
|
||||
@@ -261,7 +295,7 @@ async function handleGetDevices() {
|
||||
allDevices = filteredDevices;
|
||||
displayDevices(filteredDevices);
|
||||
} else {
|
||||
showStatus(deviceStatus, `Failed to get devices: ${result.message}`, 'error');
|
||||
showStatus(deviceStatus, `Failed to get devices: ${devicesResult.message}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Get devices error:', error);
|
||||
@@ -347,25 +381,73 @@ function handleDeviceSearch() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter devices based on search term
|
||||
// Filter devices based on search term (includes site name)
|
||||
const filteredDevices = allDevices.filter(device => {
|
||||
const deviceName = (device.name || '').toLowerCase();
|
||||
const deviceId = (device.guid || device.id || '').toLowerCase();
|
||||
const deviceType = (device.type || '').toLowerCase();
|
||||
const deviceModel = (device.model || '').toLowerCase();
|
||||
const deviceIp = (device.ipAddress || '').toLowerCase();
|
||||
const siteName = (device.server_group_id && allSites[device.server_group_id] || '').toLowerCase();
|
||||
|
||||
return deviceName.includes(searchTerm) ||
|
||||
deviceId.includes(searchTerm) ||
|
||||
deviceType.includes(searchTerm) ||
|
||||
deviceModel.includes(searchTerm) ||
|
||||
deviceIp.includes(searchTerm);
|
||||
deviceIp.includes(searchTerm) ||
|
||||
siteName.includes(searchTerm);
|
||||
});
|
||||
|
||||
displayDevices(filteredDevices);
|
||||
}
|
||||
|
||||
// Display devices in the UI
|
||||
// Group devices by their site using server_group_id → allSites mapping
|
||||
function groupDevicesBySite(devices) {
|
||||
const groups = {};
|
||||
const ungrouped = [];
|
||||
|
||||
devices.forEach(device => {
|
||||
const siteId = device.server_group_id;
|
||||
const siteName = siteId && allSites[siteId] ? allSites[siteId] : null;
|
||||
|
||||
if (siteName) {
|
||||
if (!groups[siteId]) {
|
||||
groups[siteId] = { name: siteName, devices: [] };
|
||||
}
|
||||
groups[siteId].devices.push(device);
|
||||
} else {
|
||||
ungrouped.push(device);
|
||||
}
|
||||
});
|
||||
|
||||
return { groups, ungrouped };
|
||||
}
|
||||
|
||||
// Create a device item DOM element
|
||||
function createDeviceItem(device, index) {
|
||||
const deviceItem = document.createElement('div');
|
||||
deviceItem.className = 'device-item';
|
||||
deviceItem.dataset.deviceIndex = index;
|
||||
deviceItem.dataset.deviceId = device.guid || device.id;
|
||||
|
||||
const status = getDeviceStatus(device);
|
||||
const deviceId = device.guid || device.id;
|
||||
const isCookieProxyActive = activeCookieProxyConnections.has(deviceId);
|
||||
|
||||
if (isCookieProxyActive) {
|
||||
deviceItem.classList.add('cookie-proxy-active');
|
||||
}
|
||||
|
||||
deviceItem.innerHTML = `
|
||||
<div class="device-name">${escapeHtml(device.name || 'Unnamed Device')}</div>
|
||||
<div class="device-status-dot ${status.isOnline ? 'online' : 'offline'}"></div>
|
||||
`;
|
||||
|
||||
deviceItem.addEventListener('click', () => selectDevice(device, deviceItem));
|
||||
return deviceItem;
|
||||
}
|
||||
|
||||
// Display devices in the UI, grouped by site
|
||||
function displayDevices(devices) {
|
||||
clearDeviceList();
|
||||
|
||||
@@ -376,33 +458,99 @@ function displayDevices(devices) {
|
||||
return;
|
||||
}
|
||||
|
||||
devices.forEach((device, index) => {
|
||||
const deviceItem = document.createElement('div');
|
||||
deviceItem.className = 'device-item';
|
||||
deviceItem.dataset.deviceIndex = index;
|
||||
deviceItem.dataset.deviceId = device.guid || device.id;
|
||||
const hasSites = Object.keys(allSites).length > 0;
|
||||
|
||||
const deviceStatus = getDeviceStatus(device);
|
||||
const deviceId = device.guid || device.id;
|
||||
const isCookieProxyActive = activeCookieProxyConnections.has(deviceId);
|
||||
// If no sites were fetched, fall back to flat list
|
||||
if (!hasSites) {
|
||||
devices.forEach((device, index) => {
|
||||
deviceList.appendChild(createDeviceItem(device, index));
|
||||
});
|
||||
updateCookieProxyButtonStates();
|
||||
return;
|
||||
}
|
||||
|
||||
// Add cookie-proxy-active class if this device has an active cookie connection
|
||||
if (isCookieProxyActive) {
|
||||
deviceItem.classList.add('cookie-proxy-active');
|
||||
}
|
||||
const { groups, ungrouped } = groupDevicesBySite(devices);
|
||||
|
||||
deviceItem.innerHTML = `
|
||||
<div class="device-name">${escapeHtml(device.name || 'Unnamed Device')}</div>
|
||||
<div class="device-status-dot ${deviceStatus.isOnline ? 'online' : 'offline'}"></div>
|
||||
// Sort site groups alphabetically by name
|
||||
const sortedSiteIds = Object.keys(groups).sort((a, b) =>
|
||||
groups[a].name.localeCompare(groups[b].name)
|
||||
);
|
||||
|
||||
let globalIndex = 0;
|
||||
|
||||
sortedSiteIds.forEach(siteId => {
|
||||
const group = groups[siteId];
|
||||
const isCollapsed = collapsedSites.has(siteId);
|
||||
|
||||
// Site header
|
||||
const siteHeader = document.createElement('div');
|
||||
siteHeader.className = 'site-group-header' + (isCollapsed ? ' collapsed' : '');
|
||||
siteHeader.innerHTML = `
|
||||
<span class="site-group-arrow">${isCollapsed ? '\u25B6' : '\u25BC'}</span>
|
||||
<span class="site-group-name">${escapeHtml(group.name)}</span>
|
||||
<span class="site-group-count">${group.devices.length}</span>
|
||||
`;
|
||||
|
||||
// Add click handler for device selection
|
||||
deviceItem.addEventListener('click', () => selectDevice(device, deviceItem));
|
||||
siteHeader.addEventListener('click', () => {
|
||||
if (collapsedSites.has(siteId)) {
|
||||
collapsedSites.delete(siteId);
|
||||
} else {
|
||||
collapsedSites.add(siteId);
|
||||
}
|
||||
// Re-render with current search filter
|
||||
const searchTerm = deviceSearch.value.toLowerCase().trim();
|
||||
if (searchTerm) {
|
||||
handleDeviceSearch();
|
||||
} else {
|
||||
displayDevices(allDevices);
|
||||
}
|
||||
});
|
||||
|
||||
deviceList.appendChild(deviceItem);
|
||||
deviceList.appendChild(siteHeader);
|
||||
|
||||
// Device items (hidden if collapsed)
|
||||
if (!isCollapsed) {
|
||||
group.devices.forEach(device => {
|
||||
deviceList.appendChild(createDeviceItem(device, globalIndex++));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Update cookie proxy button states after displaying devices
|
||||
// Ungrouped devices at the bottom
|
||||
if (ungrouped.length > 0) {
|
||||
if (sortedSiteIds.length > 0) {
|
||||
const ungroupedHeader = document.createElement('div');
|
||||
ungroupedHeader.className = 'site-group-header' + (collapsedSites.has('__ungrouped') ? ' collapsed' : '');
|
||||
ungroupedHeader.innerHTML = `
|
||||
<span class="site-group-arrow">${collapsedSites.has('__ungrouped') ? '\u25B6' : '\u25BC'}</span>
|
||||
<span class="site-group-name">Ungrouped</span>
|
||||
<span class="site-group-count">${ungrouped.length}</span>
|
||||
`;
|
||||
|
||||
ungroupedHeader.addEventListener('click', () => {
|
||||
if (collapsedSites.has('__ungrouped')) {
|
||||
collapsedSites.delete('__ungrouped');
|
||||
} else {
|
||||
collapsedSites.add('__ungrouped');
|
||||
}
|
||||
const searchTerm = deviceSearch.value.toLowerCase().trim();
|
||||
if (searchTerm) {
|
||||
handleDeviceSearch();
|
||||
} else {
|
||||
displayDevices(allDevices);
|
||||
}
|
||||
});
|
||||
|
||||
deviceList.appendChild(ungroupedHeader);
|
||||
}
|
||||
|
||||
if (!collapsedSites.has('__ungrouped') || sortedSiteIds.length === 0) {
|
||||
ungrouped.forEach(device => {
|
||||
deviceList.appendChild(createDeviceItem(device, globalIndex++));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateCookieProxyButtonStates();
|
||||
}
|
||||
|
||||
|
||||
+47
@@ -167,6 +167,53 @@ body {
|
||||
box-shadow: 0 0 6px rgba(244, 67, 54, 0.6);
|
||||
}
|
||||
|
||||
/* Site Group Headers */
|
||||
.site-group-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 10px;
|
||||
margin-bottom: 4px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
user-select: none;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
.site-group-header:hover {
|
||||
background: var(--hover-bg);
|
||||
}
|
||||
|
||||
.site-group-arrow {
|
||||
font-size: 10px;
|
||||
color: var(--text-secondary);
|
||||
width: 12px;
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.site-group-name {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
color: var(--accent-primary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.site-group-count {
|
||||
font-size: 10px;
|
||||
color: var(--text-secondary);
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 1px 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.placeholder-text {
|
||||
color: var(--text-secondary);
|
||||
font-style: italic;
|
||||
|
||||
Reference in New Issue
Block a user