Merge pull request #892 from mauriceboe/fixes-26-04-2026

fixes-26-04-2026
This commit is contained in:
Maurice
2026-04-26 21:59:21 +02:00
committed by GitHub
7 changed files with 1058 additions and 556 deletions
+121
View File
@@ -0,0 +1,121 @@
# Trademark Policy
## Introduction
This is the TREK project's policy for the use of our trademarks. While TREK is
available under the GNU Affero General Public License v3.0 (AGPL-3.0), that
license does not include a license to use our trademarks.
This policy describes how you may use our trademarks. Our goal is to strike a
balance between: 1) our need to ensure that our trademarks remain reliable
indicators of the software we release; and 2) our community members' desire to
be full participants in the TREK project.
## Our trademarks
This policy covers the name "TREK" as well as any associated logos, trade dress,
goodwill, or designs (our "Marks").
## In general
Whenever you use our Marks, you must always do so in a way that does not mislead
anyone about exactly who is the source of the software. For example, you cannot
say you are distributing TREK when you're distributing a modified version of it,
because people would think they would be getting the same software that they
can get directly from us when they aren't. You also cannot use our Marks on
your website in a way that suggests that your website is an official TREK
website or that we endorse your website. But, if true, you can say you like
TREK, that you participate in the TREK community, that you are providing an
unmodified version of TREK, or that you wrote a guide describing how to use
TREK.
This fundamental requirement, that it is always clear to people what they are
getting and from whom, is reflected throughout this policy. It should also
serve as your guide if you are not sure about how you are using the Marks.
In addition:
* You may not use or register, in whole or in part, the Marks as part of your
own trademark, service mark, domain name, company name, trade name, product
name or service name.
* Trademark law does not allow your use of names or trademarks that are too
similar to ours. You therefore may not use an obvious variation of any of our
Marks or any phonetic equivalent, foreign language equivalent, takeoff, or
abbreviation for a similar or compatible product or service.
* You agree that you will not acquire any rights in the Marks and that any
goodwill generated by your use of the Marks and participation in our
community inures solely to our benefit.
## Distribution of unmodified source code or unmodified executable code we have compiled
When you redistribute an unmodified copy of TREK, you are not changing the
quality or nature of it. Therefore, you may retain the Marks we have placed on
the software to identify your redistribution. This kind of use only applies if
you are redistributing an official TREK distribution that has not been changed
in any way.
## Distribution of executable code that you have compiled, or modified code
You may use the word mark "TREK", but not any TREK logos, to truthfully
describe the origin of the software that you are providing, that is, that the
code you are distributing is a modification of TREK. You may say, for example,
that "this software is derived from the source code for TREK."
Of course, you can place your own trademarks or logos on versions of the
software to which you have made substantive modifications, because by modifying
the software, you have become the origin of that exact version. In that case,
you should not use our Marks.
However, you may use our Marks for the distribution of code (source or
executable) on the condition that any executable is built from an official TREK
source code release and that any modifications are limited to switching on or
off features already included in the software, translations into other
languages, and incorporating minor bug-fix patches. Use of our Marks on any
further modification is not permitted.
## Mobile wrappers, hosted instances, and forks
The following clarifications apply specifically to common ways TREK is
redistributed:
* **Self-hosted instances of unmodified TREK.** You may refer to your instance
as "a TREK instance" or "running TREK." You may not name the service itself
in a way that suggests it is the official TREK ("TREK Cloud," "TREK
Official," etc.).
* **Mobile wrappers (WebView shells, Capacitor apps, native apps) pointing at
TREK.** You may describe your app as "a mobile client for TREK" or "for use
with TREK." You may not publish it on app stores under the name "TREK" or a
confusingly similar name, and you may not use the TREK logo as the app icon
unless your wrapper distributes only an unmodified, official TREK instance
and you have obtained permission.
* **Forks of the TREK source code.** Forks that diverge from upstream must use
a different name. You may state that your fork is "based on TREK" or "a fork
of TREK," but the project name itself must be your own.
## Statements about your software's relation to TREK
You may use the word mark, but not TREK logos, to truthfully describe the
relationship between your software and ours. The word mark "TREK" should be
used after a verb or preposition that describes the relationship between your
software and ours. So you may say, for example, "Bob's app for TREK" but may
not say "Bob's TREK app." Some other examples that may work for you are:
* [Your software] uses TREK
* [Your software] is powered by TREK
* [Your software] runs on TREK
* [Your software] for use with TREK
* [Your software] for TREK
## Questions and permission requests
If you are not sure whether your intended use of the Marks is permitted under
this policy, or if you would like to request explicit permission for a use that
is not covered, please open an issue on the TREK GitHub repository or contact
the maintainers directly.
---
These guidelines are based on the
[Model Trademark Guidelines](http://www.modeltrademarkguidelines.org), used
under a
[Creative Commons Attribution 3.0 Unported license](https://creativecommons.org/licenses/by/3.0/deed.en_US).
+3 -3
View File
@@ -8907,9 +8907,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.9", "version": "8.5.10",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
"integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
+12 -5
View File
@@ -266,17 +266,22 @@ export default function DemoBanner(): React.ReactElement | null {
return ( return (
<div style={{ <div style={{
position: 'fixed', inset: 0, zIndex: 9999, position: 'fixed', inset: 0, zIndex: 99999,
background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(8px)', background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(8px)',
display: 'flex', alignItems: 'center', justifyContent: 'center', display: 'flex', alignItems: 'center', justifyContent: 'center',
padding: 16, overflow: 'auto', paddingTop: 'max(16px, env(safe-area-inset-top))',
paddingBottom: 'max(16px, calc(env(safe-area-inset-bottom) + 80px))',
paddingLeft: 16, paddingRight: 16,
overflow: 'auto',
fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif", fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif",
}} onClick={() => setDismissed(true)}> }} onClick={() => setDismissed(true)}>
<div style={{ <div style={{
background: 'white', borderRadius: 20, padding: '28px 24px 20px', background: 'white', borderRadius: 20, padding: '28px 24px 0',
maxWidth: 480, width: '100%', maxWidth: 480, width: '100%',
boxShadow: '0 20px 60px rgba(0,0,0,0.3)', boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
maxHeight: '90vh', overflow: 'auto', maxHeight: 'min(90vh, calc(100dvh - 96px))',
overflow: 'auto',
display: 'flex', flexDirection: 'column',
}} onClick={(e: React.MouseEvent<HTMLDivElement>) => e.stopPropagation()}> }} onClick={(e: React.MouseEvent<HTMLDivElement>) => e.stopPropagation()}>
{/* Header */} {/* Header */}
@@ -367,8 +372,10 @@ export default function DemoBanner(): React.ReactElement | null {
{/* Footer */} {/* Footer */}
<div style={{ <div style={{
paddingTop: 14, borderTop: '1px solid #e5e7eb', padding: '14px 0 20px', borderTop: '1px solid #e5e7eb',
display: 'flex', alignItems: 'center', justifyContent: 'space-between', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
position: 'sticky', bottom: 0, background: 'white',
marginTop: 'auto',
}}> }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, color: '#9ca3af' }}> <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, color: '#9ca3af' }}>
<Github size={13} /> <Github size={13} />
+886 -527
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -23,6 +23,7 @@
"express": "^4.18.3", "express": "^4.18.3",
"fast-xml-parser": "^5.5.10", "fast-xml-parser": "^5.5.10",
"helmet": "^8.1.0", "helmet": "^8.1.0",
"jimp": "^1.6.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"multer": "^2.1.1", "multer": "^2.1.1",
"node-cron": "^4.2.1", "node-cron": "^4.2.1",
@@ -30,7 +31,6 @@
"otplib": "^12.0.1", "otplib": "^12.0.1",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"semver": "^7.7.4", "semver": "^7.7.4",
"sharp": "^0.34.5",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "^6.0.2", "typescript": "^6.0.2",
"undici": "^7.0.0", "undici": "^7.0.0",
@@ -1,7 +1,9 @@
import sharp from 'sharp' import { Jimp } from 'jimp'
import path from 'path' import path from 'path'
import fs from 'fs/promises' import fs from 'fs/promises'
import crypto from 'crypto' import crypto from 'crypto'
import { isAddonEnabled } from '../adminService'
import { ADDON_IDS } from '../../addons'
const THUMB_MAX = 800 const THUMB_MAX = 800
const THUMB_QUALITY = 80 const THUMB_QUALITY = 80
@@ -10,12 +12,14 @@ export async function ensureLocalThumbnail(
uploadsRoot: string, uploadsRoot: string,
originalRelPath: string, originalRelPath: string,
): Promise<{ thumbnailRelPath: string; width: number; height: number } | null> { ): Promise<{ thumbnailRelPath: string; width: number; height: number } | null> {
if (!isAddonEnabled(ADDON_IDS.JOURNEY)) return null
const originalAbs = path.join(uploadsRoot, originalRelPath) const originalAbs = path.join(uploadsRoot, originalRelPath)
try { await fs.access(originalAbs) } catch { return null } try { await fs.access(originalAbs) } catch { return null }
// Deterministic name so concurrent requests don't race on the same photo. // Deterministic name so concurrent requests don't race on the same photo.
const hash = crypto.createHash('sha1').update(originalRelPath).digest('hex').slice(0, 16) const hash = crypto.createHash('sha1').update(originalRelPath).digest('hex').slice(0, 16)
const thumbRel = `journey/thumbs/${hash}.webp` const thumbRel = `journey/thumbs/${hash}.jpg`
const thumbAbs = path.join(uploadsRoot, thumbRel) const thumbAbs = path.join(uploadsRoot, thumbRel)
try { try {
@@ -24,18 +28,21 @@ export async function ensureLocalThumbnail(
fs.stat(thumbAbs).catch(() => null), fs.stat(thumbAbs).catch(() => null),
]) ])
if (dstStat && dstStat.mtimeMs >= srcStat.mtimeMs) { if (dstStat && dstStat.mtimeMs >= srcStat.mtimeMs) {
const meta = await sharp(thumbAbs).metadata() const img = await Jimp.read(thumbAbs)
return { thumbnailRelPath: thumbRel, width: meta.width ?? 0, height: meta.height ?? 0 } return { thumbnailRelPath: thumbRel, width: img.bitmap.width, height: img.bitmap.height }
} }
await fs.mkdir(path.dirname(thumbAbs), { recursive: true }) await fs.mkdir(path.dirname(thumbAbs), { recursive: true })
await sharp(originalAbs)
.rotate() // Jimp auto-applies EXIF orientation on read, matching sharp's .rotate() behavior.
.resize({ width: THUMB_MAX, height: THUMB_MAX, fit: 'inside', withoutEnlargement: true }) const img = await Jimp.read(originalAbs)
.webp({ quality: THUMB_QUALITY }) const { width: w, height: h } = img.bitmap
.toFile(thumbAbs) if (w > THUMB_MAX || h > THUMB_MAX) {
const meta = await sharp(thumbAbs).metadata() img.scaleToFit({ w: THUMB_MAX, h: THUMB_MAX })
return { thumbnailRelPath: thumbRel, width: meta.width ?? 0, height: meta.height ?? 0 } }
await img.write(thumbAbs as `${string}.jpg`, { quality: THUMB_QUALITY })
return { thumbnailRelPath: thumbRel, width: img.bitmap.width, height: img.bitmap.height }
} catch { } catch {
// Unsupported format, corrupt file, etc. — fall back to original in caller. // Unsupported format, corrupt file, etc. — fall back to original in caller.
return null return null
+17 -9
View File
@@ -61,16 +61,24 @@ function resolveDayIdFromTime(
return row?.id ?? null; return row?.id ?? null;
} }
const saveEndpoints = db.transaction((reservationId: number, endpoints: EndpointInput[]) => { function saveEndpoints(reservationId: number, endpoints: EndpointInput[]): void {
db.prepare('DELETE FROM reservation_endpoints WHERE reservation_id = ?').run(reservationId); // Bind the transaction lazily on each call. Binding at module load time
const insert = db.prepare(` // captures the DB connection that was open then, which becomes invalid
INSERT INTO reservation_endpoints (reservation_id, role, sequence, name, code, lat, lng, timezone, local_time, local_date) // after demo-reset / restore-from-backup closes and reinitialises the
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) // connection — every later endpoint save would throw
`); // "The database connection is not open".
endpoints.forEach((e, i) => { const tx = db.transaction((rid: number, eps: EndpointInput[]) => {
insert.run(reservationId, e.role, e.sequence ?? i, e.name, e.code ?? null, e.lat, e.lng, e.timezone ?? null, e.local_time ?? null, e.local_date ?? null); db.prepare('DELETE FROM reservation_endpoints WHERE reservation_id = ?').run(rid);
const insert = db.prepare(`
INSERT INTO reservation_endpoints (reservation_id, role, sequence, name, code, lat, lng, timezone, local_time, local_date)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
eps.forEach((e, i) => {
insert.run(rid, e.role, e.sequence ?? i, e.name, e.code ?? null, e.lat, e.lng, e.timezone ?? null, e.local_time ?? null, e.local_date ?? null);
});
}); });
}); tx(reservationId, endpoints);
}
export function listReservations(tripId: string | number) { export function listReservations(tripId: string | number) {
const reservations = db.prepare(` const reservations = db.prepare(`