Compare commits

..

No commits in common. "a1977e3efd6242b3fcc39e231e8a3142dd5d6eea" and "517574720e757b7cfc6f5b1c575cf1459c9c9776" have entirely different histories.

8 changed files with 6 additions and 1008 deletions

View file

@ -16,13 +16,6 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Lint and test
# In the runner's own Docker, so this needs no extra actions. A failure
# here stops the image being built, rather than deploying a broken one.
run: |
docker run --rm -v "$PWD":/src -w /src node:22-bookworm-slim \
sh -c "npm ci --no-audit --no-fund && npm run lint && npm test"
- name: Log in to the Forgejo container registry - name: Log in to the Forgejo container registry
# REGISTRY_TOKEN = a Forgejo access token with write:package scope, added # REGISTRY_TOKEN = a Forgejo access token with write:package scope, added
# as a repo Actions secret. The auto GITHUB_TOKEN can't push packages. # as a repo Actions secret. The auto GITHUB_TOKEN can't push packages.

View file

@ -55,9 +55,7 @@ A self-hosted web app for organizing your 3D-printing files (STL, 3MF, OBJ, FBX,
``` ```
4. Open http://localhost:3000 4. Open http://localhost:3000
Run the unit tests with `npm test`, ESLint with `npm run lint`, or both with `npm run check`. CI runs both before Run the unit tests with `npm test`.
building the image, so a lint error stops a broken image being pushed. The lint rules are deliberately thin — their
job is the class of mistake tests can't catch, chiefly a name that doesn't exist until the line finally runs.
### Where your data lives ### Where your data lives

View file

@ -1,45 +0,0 @@
// Lint rules, kept to the mistakes tests can't catch — chiefly a name that
// doesn't exist, which only shows up when that line finally runs.
const js = require('@eslint/js');
const globals = require('globals');
module.exports = [
{ ignores: ['node_modules/**', 'uploads/**', 'thumbnails/**', 'glb/**', 'extracted/**', 'models/**', 'data/**', 'backups/**', 'tools/venv/**'] },
// The server: Node, CommonJS.
{
files: ['**/*.js'],
languageOptions: {
ecmaVersion: 2023,
sourceType: 'commonjs',
globals: { ...globals.node },
},
rules: {
...js.configs.recommended.rules,
'no-unused-vars': ['error', { argsIgnorePattern: '^_|^next$', caughtErrors: 'none' }],
},
},
// The browser: plain scripts sharing globals across files, plus one module.
{
files: ['public/**/*.js'],
languageOptions: {
sourceType: 'script',
globals: {
...globals.browser,
ModelViewer: 'readonly', // viewer.js hangs it on window
PRINTER_MODELS: 'readonly', // printer-art.js does the same
},
},
},
{
files: ['public/viewer.js'],
languageOptions: { sourceType: 'module' },
},
// Tests.
{
files: ['test/**/*.js'],
languageOptions: { globals: { ...globals.node } },
},
];

View file

@ -52,7 +52,7 @@ async function fetchHtml(url) {
} }
// Extract Open Graph / title metadata from raw HTML. // Extract Open Graph / title metadata from raw HTML.
function parseMetadata(html, _url) { // _url: kept for the call shape, not read function parseMetadata(html, url) {
const titleTag = (html.match(/<title[^>]*>([^<]*)<\/title>/i) || [])[1]; const titleTag = (html.match(/<title[^>]*>([^<]*)<\/title>/i) || [])[1];
return { return {
title: metaContent(html, 'property', 'og:title') || (titleTag ? decodeEntities(titleTag).trim() : null), title: metaContent(html, 'property', 'og:title') || (titleTag ? decodeEntities(titleTag).trim() : null),

943
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,12 @@
{ {
"name": "printhive", "name": "printhive",
"version": "1.0.0", "version": "1.0.0",
"description": "Printhive \u2014 a web app for organizing 3D-printing files (STL, 3MF, OBJ, ...) with URL import", "description": "Printhive — a web app for organizing 3D-printing files (STL, 3MF, OBJ, ...) with URL import",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {
"start": "node server.js", "start": "node server.js",
"dev": "nodemon server.js", "dev": "nodemon server.js",
"test": "node --test test/*.test.js", "test": "node --test test/*.test.js"
"lint": "eslint .",
"check": "npm run lint && npm test"
}, },
"keywords": [ "keywords": [
"stl", "stl",
@ -37,9 +35,6 @@
"tar": "^7.5.22" "tar": "^7.5.22"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1",
"eslint": "^10.11.0",
"globals": "^17.12.0",
"nodemon": "^3.0.2" "nodemon": "^3.0.2"
} }
} }

View file

@ -2908,7 +2908,7 @@ function printerStatusHtml(snap, model, p = {}) {
async function pollPrinters() { async function pollPrinters() {
clearTimeout(printersPoll); clearTimeout(printersPoll);
if (selectedScope !== 'printers' || !document.getElementById('printerCards')) return; if (selectedScope !== 'printers' || !document.getElementById('printerCards')) return;
let live; let live = {};
try { try {
live = await getJson('/api/printers/live'); live = await getJson('/api/printers/live');
} catch { } catch {

View file

@ -1028,7 +1028,7 @@ async function importFromUrl(url, { projects = [], withFiles = true } = {}) {
let filesError = null; // why no files could be downloaded, shown after the import let filesError = null; // why no files could be downloaded, shown after the import
let filesErrorReason = null; // makerworld_token: the app offers to update the token let filesErrorReason = null; // makerworld_token: the app offers to update the token
let license = meta.license || null; let license = meta.license || null;
let commercial; let commercial = null;
if (detected.site === 'makerworld' && html) { if (detected.site === 'makerworld' && html) {
if (withFiles) { if (withFiles) {
files = await downloadMakerworldFiles(html).catch((err) => { files = await downloadMakerworldFiles(html).catch((err) => {