feat: add plugin system (#1034)

* feat: add plugin system

* fix db docker build

* fix hammerhead readme, add strava subscription news to docs

* fixes and sdk improvements

* fix: reduce Meilisearch load, debounce federation sync (#1012)

* optimize meili trail index

* several fixes

---------

Co-authored-by: Flomp <Flomp@users.noreply.github.com>

* Bump svelte from 5.55.5 to 5.56.0 in /docs (#1032)

Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.55.5 to 5.56.0.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.56.0/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.56.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Flomp <Flomp@users.noreply.github.com>

* Release v0.19.2 (#1035)

* chore: release v0.19.2

* add changelog

---------

Co-authored-by: Flomp <26000991+Flomp@users.noreply.github.com>
Co-authored-by: Christian Beutel <>

* speed up plugin sync and several small fixes

* concepts for security improvements and process stability

* improve concept

* security concept implemented

* remove insecure TLS

* worker concept implemented

* fixes and cleanup

* fixes

* docu

* mermaid, namings

* WASM plugin host improvements, plugin logging

* fix db migration

* Improve plugin config and category mapping UI

* fixes

* further fixes

* remove manual test sync

* fix db migration and strava mapping

* type added, UI improvements

* fix plugin card toggle clickable area

* optimize synch status card layout

* plugin type 'trails' instead of 'integration'

* session auth validation in UI

* fix komoot date and waypoints

* improve category mapping

* fix send to hammerhead: trail name

* plugin setup error handling improved

* fix review findings

* re-mapping added

* rename remote_category

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Flomp <Flomp@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Flomp <26000991+Flomp@users.noreply.github.com>
This commit is contained in:
slothful-vassal
2026-06-22 15:00:44 +02:00
committed by GitHub
parent 2e3d8537b8
commit 485ec53f6d
196 changed files with 20947 additions and 5454 deletions

View File

@@ -3,11 +3,6 @@
import { page } from "$app/state";
import type { List } from "$lib/models/list";
import type { Trail } from "$lib/models/trail";
import {
integrations,
integrations_index,
uploadGpx,
} from "$lib/stores/integration_store";
import {
lists_add_trail,
lists_index,
@@ -25,9 +20,8 @@
import { trail2gpx } from "$lib/util/gpx_util";
import { gpx } from "$lib/vendor/toGeoJSON/toGeoJSON";
import JSZip from "jszip";
import { onMount, type Snippet } from "svelte";
import { type Snippet } from "svelte";
import { _ } from "svelte-i18n";
import { get } from "svelte/store";
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
import ConfirmModal from "../confirm_modal.svelte";
import ListSearchModal from "../list/list_search_modal.svelte";
@@ -43,6 +37,7 @@
import type { MergeSelection, MergeSettings } from "./trail_merge_modal.svelte";
import MergeDialog from "$lib/components/trail/trail_merge_dialog.svelte";
import { trail_merge } from "$lib/stores/trail_merge_api";
import { hasSendCapablePlugin } from "$lib/stores/plugin_store";
export interface MergeResult {
targetTrail: Trail;
@@ -65,58 +60,11 @@
let confirmModal: ConfirmModal;
let listSelectModal: ListSearchModal;
let trailExportModal: TrailExportModal;
let trailSendModal: TrailSendModal;
let trailShareModal: TrailShareModal;
let trailMergeModal: TrailMergeModal;
let trailSendModal: TrailSendModal;
const hammerheadIntegration = $derived(
$integrations.find((integration) =>
Boolean(integration.hammerhead?.active)
)
);
let lists: List[] = $state([]);
let integrationsLoading = false;
let integrationsLoadedForUser: string | undefined;
onMount(() => {
const unsubscribe = currentUser.subscribe(async (user) => {
if (!user) {
integrationsLoadedForUser = undefined;
return;
}
const existing = get(integrations);
if (
existing.length &&
existing[0]?.user === user.id
) {
integrationsLoadedForUser = user.id;
return;
}
if (
integrationsLoading ||
integrationsLoadedForUser === user.id
) {
return;
}
integrationsLoading = true;
try {
await integrations_index();
integrationsLoadedForUser = user.id;
} catch (error) {
console.error("Failed to load integrations", error);
} finally {
integrationsLoading = false;
}
});
return () => {
unsubscribe();
};
});
let loading: boolean = $state(false);
@@ -214,6 +162,16 @@
return !isMultiselectMode();
}
function allowSend(): boolean {
return (
hasTrail() &&
!isMultiselectMode() &&
hasGpx() &&
Boolean($currentUser) &&
$hasSendCapablePlugin
);
}
function allowPublish(): boolean {
if (mode !== "multi-select") return false;
@@ -240,7 +198,7 @@
});
const allowListManagement = isFromCurrentUser();
const allowShareSingleTrail = !isMultiselectMode() && isFromCurrentUser();
const allowSingleOutput = canExport() || (!isMultiselectMode() && hammerheadIntegration && canExport());
const allowSingleOutput = canExport();
if (isMultiselectMode()) {
return [
@@ -389,7 +347,7 @@
},
]
: []),
...(!isMultiselectMode() && hammerheadIntegration && canExport()
...(allowSend()
? [
{
text: $_("send-to"),
@@ -528,6 +486,8 @@
}
} else if (ddVal == "share") {
trailShareModal.openModal();
} else if (ddVal == "send-to") {
trailSendModal.openModal();
} else if (ddVal == "download") {
trailExportModal.openModal();
} else if (ddVal == "edit") {
@@ -548,8 +508,6 @@
if (trail()) {
await trailMergeModal.openSimilarTrailsModal(trail()!);
}
} else if (item.value == "send-to") {
trailSendModal.openModal();
}
}
@@ -605,49 +563,6 @@
await trail_merge(trailSource.id, trailTarget.id, settings);
onProgress?.(1);
}
async function uploadToHammerhead() {
if (!hammerheadIntegration || !hasTrail()) {
console.error("No Hammerhead integration found.");
return;
}
for (const uTrail of trails!) {
try {
if (uTrail.gpx) {
const gpxData = await trail2gpx(uTrail, $currentUser);
const formData = new FormData();
const gpxFile = new File(
[gpxData],
`${uTrail.name || "trail"}.gpx`,
{ type: "application/gpx+xml" },
);
formData.append("file", gpxFile);
await uploadGpx("hammerhead", gpxFile);
show_toast({
type: "success",
icon: "check",
text: $_("uploaded-trail-to-hammerhead"),
});
} else {
show_toast({
type: "error",
icon: "close",
text: $_("trail-has-no-gpx"),
});
}
} catch (e) {
console.error(e);
show_toast({
type: "error",
icon: "close",
text: $_("error-uploading-trail-to-hammerhead"),
});
}
}
}
async function updateTrailsVisibility() {
const newVisibility = !majorityOfSelectedTrailsArePublic();
@@ -926,17 +841,14 @@
onsave={handleShareUpdate}
bind:this={trailShareModal}
></TrailShareModal>
<TrailSendModal
trail={trail()}
share={page.url.searchParams.get("share") ?? undefined}
bind:this={trailSendModal}
></TrailSendModal>
<TrailMergeModal
bind:this={trailMergeModal}
onmerge={(settings, selection) => mergeTrails(settings, selection)}
></TrailMergeModal>
<MergeDialog/>
<TrailSendModal
bind:this={trailSendModal}
onsend={async (settings) => {
if (settings.integrationName === "hammerhead") {
await uploadToHammerhead();
}
}}
></TrailSendModal>