Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
FROM ubuntu:24.04

ARG NODE_MAJOR=24

# Avoid interactive prompts during package install
ENV DEBIAN_FRONTEND=noninteractive

# Install Node.js 20.x from NodeSource repository (required for selenium-webdriver and other packages)
# Install the Node 24 LTS baseline required by Babel 8 and the web toolchain.
RUN apt-get update && apt-get install -y curl ca-certificates gnupg \
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \
&& apt-get install -y nodejs

# Install required packages
Expand All @@ -32,4 +34,4 @@ RUN apt-get update && apt-get install -y \
# Create a non-root user for VS Code
RUN useradd -m vscode
USER vscode
WORKDIR /home/vscode
WORKDIR /home/vscode
12 changes: 9 additions & 3 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,15 +308,22 @@ jobs:
curl \
jq \
golang-go \
nodejs \
npm \
procps

- name: Checkout
uses: actions/checkout@v6
with:
submodules: recursive

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: npm
cache-dependency-path: |
package-lock.json
web/package-lock.json

- name: Build go2rtc from source
run: |
# Build go2rtc from the opensensor fork submodule
Expand Down Expand Up @@ -491,4 +498,3 @@ jobs:

# Clean up test artifacts
rm -rf /tmp/lightnvr-test

1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24.19.0
19 changes: 14 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ ARG SQLITE_YEAR=2026
ARG SQLITE_AUTOCONF_VERSION=3520000
ARG LIBUV_VERSION=1.52.1
ARG LLHTTP_VERSION=9.3.1
ARG NODE_MAJOR=24
ARG DEB_BUILD=false

FROM debian:${DEBIAN_SUITE}-slim AS builder
Expand All @@ -13,28 +14,36 @@ ARG SQLITE_YEAR
ARG SQLITE_AUTOCONF_VERSION
ARG LIBUV_VERSION
ARG LLHTTP_VERSION
ARG NODE_MAJOR
ARG DEB_BUILD

# Set non-interactive mode
ENV DEBIAN_FRONTEND=noninteractive

# Install build dependencies including Node.js and FFmpeg dev libraries
# sid ships Go 1.26+/Node 22.x/FFmpeg 8.x; trixie ships Go 1.24+/Node 20.x/FFmpeg 7.x
# Install build dependencies including Node.js and FFmpeg dev libraries.
# Node.js comes from NodeSource so every Debian suite uses the Node 24 LTS
# baseline required by the Babel 8 web test toolchain.
# sid ships Go 1.26+/FFmpeg 8.x; trixie ships Go 1.24+/FFmpeg 7.x.
#
# Pre-install systemd-standalone-sysusers to satisfy the sysusers virtual
# dependency without pulling in the full systemd package. The full systemd
# postinst crashes under QEMU ARM emulation (SIGSEGV in systemd 260.x),
# breaking all cross-architecture builds.
RUN apt-get update && \
apt-get install -y --no-install-recommends systemd-standalone-sysusers && \
apt-get install -y --no-install-recommends \
systemd-standalone-sysusers curl ca-certificates gpg && \
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" \
-o /tmp/nodesource_setup.sh && \
bash /tmp/nodesource_setup.sh && \
rm /tmp/nodesource_setup.sh && \
apt-get install -y \
git cmake build-essential pkg-config file \
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
libcurl4-openssl-dev \
libmbedtls-dev curl wget ca-certificates gpg libcjson-dev \
libmbedtls-dev wget libcjson-dev \
libmosquitto-dev \
libyaml-dev \
nodejs npm \
nodejs \
golang-go && \
# Verify installation
node --version && \
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,10 @@ Powerful object detection using modern ONNX and TFLite models with zone-aware fi
22.04 ship FFmpeg 5.x/6.x, which is too old — build FFmpeg from source or
use a newer distro release (Debian 13 / Ubuntu 24.04+ ship a compatible
version).
- **Node.js ≥ 20** for the web build. Debian 12's packaged `nodejs` is 18.x
— install a newer version via [NodeSource](https://github.com/nodesource/distributions)
or [nvm](https://github.com/nvm-sh/nvm) if `node --version` is below 20.
- **Node.js ≥ 24.11 and < 25** for the web build. Use the pinned version in
`.nvmrc`; distribution packages may be older, so install Node 24 via
[NodeSource](https://github.com/nodesource/distributions) or
[nvm](https://github.com/nvm-sh/nvm) when needed.
- **`libsqlite3-dev`** (not pulled in automatically by any of the above).

```bash
Expand All @@ -242,7 +243,7 @@ Powerful object detection using modern ONNX and TFLite models with zone-aware fi
# Initialize submodules (required for go2rtc)
git submodule update --init --recursive

# Build web assets (requires Node.js >= 20 -- see prerequisites above).
# Build web assets (requires Node.js >= 24.11 and < 25 -- see above).
# Use this script rather than running: cd web && npm install && npm run build
# It also runs scripts/extract_version.js first, which generates web/js/version.js
# from CMakeLists.txt. Skipping that step fails the Vite build with:
Expand Down
17 changes: 9 additions & 8 deletions docs/RELEASE_PROCESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ When you push a tag, GitHub Actions automatically:
- linux/arm/v7

2. **Builds web assets** during the Docker build:
- Installs Node.js 20.x LTS
- Runs `npm ci --omit=dev` to install production dependencies only
- Installs Node.js 24.x LTS
- Runs `npm ci --ignore-scripts` from the committed lockfile
- Runs `npm run build` to build with Vite
- Copies built assets to `/usr/share/lightnvr/web-template/`

Expand Down Expand Up @@ -206,14 +206,16 @@ If the Docker build fails to find web assets:

### Local build doesn't match Docker build

This can happen if you have different Node.js versions. The Docker build uses Node.js 20.x LTS. To match locally:
This can happen if you have different Node.js versions. The Docker build uses Node.js 24.x LTS. To match locally:

```bash
# Install Node.js 20.x
# Then rebuild
# Install the version pinned in .nvmrc (Node.js 24.x LTS)
nvm install
nvm use

# Then rebuild from the committed dependency graph
cd web
rm -rf node_modules package-lock.json
npm install
npm ci
npm run build
```

Expand Down Expand Up @@ -253,4 +255,3 @@ The new release process is:
4. **Clean repository:** No binary assets in git

This ensures consistency, reduces errors, and makes releases faster and more reliable.

8 changes: 3 additions & 5 deletions docs/TROUBLESHOOTING_WEB_INTERFACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,9 @@ sudo systemctl restart lightnvr
# Navigate to LightNVR source directory
cd /path/to/lightnvr

# Install Node.js and npm if not already installed
# On Debian/Ubuntu:
sudo apt-get update
sudo apt-get install -y nodejs npm
# Install the Node.js version pinned by the repository (requires nvm)
nvm install
nvm use

# Build web assets
cd web
Expand Down Expand Up @@ -364,4 +363,3 @@ To avoid web interface issues in future installations:

3. **Keep backups**:
The install_web_assets.sh script automatically creates backups before overwriting files.

3 changes: 3 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"name": "lightnvr",
"version": "1.0.0",
"description": "LightNVR Integration Tests",
"engines": {
"node": ">=24.11.0 <25"
},
"scripts": {
"test": "npx playwright test",
"test:api": "npx playwright test --grep @api",
Expand Down
2 changes: 1 addition & 1 deletion scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ sudo systemctl start lightnvr

**Building C backend:** GCC/Clang, CMake, libsqlite3-dev, libuv1-dev

**Building web frontend:** Node.js (v14+), npm (v6+)
**Building web frontend:** Node.js (v24.11+ and <25), npm

**Screenshot capture:** Node.js, Playwright, Chromium

Expand Down
5 changes: 4 additions & 1 deletion src/web/api_handlers_system_logs_tail.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ cJSON * get_json_logs_tail(int max_verbosity, const char *last_timestamp, int ma
cJSON *json_lvl = cJSON_CreateStringReference(level);

cJSON_AddStringToObject(log_entry, "timestamp", timestamp[0] ? timestamp : "Unknown");
cJSON_AddItemReferenceToObject(log_entry, "level", json_lvl);
// json_lvl is already a reference node. Attach that node directly so
// the parent owns and frees it; AddItemReferenceToObject would create
// a second node and leak the original one for every log entry.
cJSON_AddItemToObject(log_entry, "level", json_lvl);
cJSON_AddStringToObject(log_entry, "message", message);

cJSON_AddItemToArray(logs_array, log_entry);
Expand Down
30 changes: 30 additions & 0 deletions tests/unit/test_api_handlers_system.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,35 @@ void test_handle_get_system_info_includes_empty_stream_storage_array(void) {
http_response_free(&res);
}

void test_get_json_logs_tail_owns_level_reference_nodes(void) {
char log_path[MAX_PATH_LENGTH + sizeof("/system.log")];
snprintf(log_path, sizeof(log_path), "%s/system.log", g_tmp_root);

FILE *log_file = fopen(log_path, "w");
TEST_ASSERT_NOT_NULL(log_file);
fputs("[2026-08-03 12:00:00.000] [INFO] first message\n", log_file);
fputs("[2026-08-03 12:00:01.000] [WARN] second message\n", log_file);
fputs("[2026-08-03 12:00:02.000] [ERROR] third message\n", log_file);
fclose(log_file);

safe_strcpy(g_config.log_file, log_path, sizeof(g_config.log_file), 0);

cJSON *logs = get_json_logs_tail(LOG_LEVEL_DEBUG, NULL, 10);
TEST_ASSERT_NOT_NULL(logs);
TEST_ASSERT_EQUAL_INT(3, cJSON_GetArraySize(logs));

cJSON *entry = cJSON_GetArrayItem(logs, 0);
cJSON *level = cJSON_GetObjectItemCaseSensitive(entry, "level");
TEST_ASSERT_TRUE(cJSON_IsString(level));
TEST_ASSERT_EQUAL_STRING("INFO", level->valuestring);

/* LeakSanitizer verifies that deleting the array also releases each
* cJSON string-reference node owned by its log entry. */
cJSON_Delete(logs);
g_config.log_file[0] = '\0';
unlink(log_path);
}

/* ================================================================
* handle_get_streams — motion_trigger_source field present in JSON
* ================================================================ */
Expand Down Expand Up @@ -540,6 +569,7 @@ int main(void) {
UNITY_BEGIN();
RUN_TEST(test_handle_get_system_info_includes_versions_summary);
RUN_TEST(test_handle_get_system_info_includes_empty_stream_storage_array);
RUN_TEST(test_get_json_logs_tail_owns_level_reference_nodes);
RUN_TEST(test_handle_get_streams_includes_motion_trigger_source);
RUN_TEST(test_handle_put_stream_parses_motion_trigger_source);
RUN_TEST(test_handle_get_streams_includes_audio_voice_enhancement);
Expand Down
Loading
Loading