Files
linkvault/app/templates/_icons.html
Erik Thiele 7262aee63d Add settings page with CSV export, SQL dump export/import
New /settings page (linked from the header's burger menu) offers:
- CSV export of the user's links, for spreadsheet apps.
- SQL dump export as INSERT INTO links (...) statements, scoped to
  the current user only — never a raw full-database dump, since that
  would leak other accounts' password hashes and data. Embeddings are
  excluded (regenerated via "KI neu beschreiben lassen" if needed).
- SQL import that re-adds a previously exported dump to the current
  account (additive, doesn't touch existing links).

Import safety (app/backup.py): uploaded SQL is never executed against
the real database. Each non-comment line is required to start with
"insert into links" and is run one statement at a time against an
isolated in-memory SQLite database with only a whitelisted `links`
schema (no id/user_id columns) — sqlite3.execute() also rejects
multiple statements per call. Only after that succeeds are rows
copied into the real DB via the ORM, with user_id forced to the
logged-in user. Verified this rejects DROP TABLE, ATTACH DATABASE,
stacked statements, cross-table subqueries, and user_id injection.
Upload is capped at 2 MB.

Also adds download/upload icons and a proper file-input styling
pattern (visually-hidden input + <label> trigger + filename readout),
since the browser's ::file-selector-button pseudo-element didn't
render reliably in testing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 23:25:27 +02:00

88 lines
4.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{#
Gemeinsame Icons (angelehnt an Lucide, MIT-lizenziert), inline als SVG.
Monochrom über currentColor Farbe wird vom umgebenden Element gesetzt.
Verwendung: {% import "_icons.html" as icons %} ... {{ icons.edit() }}
#}
{% macro base(path, size=16) -%}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="icon" aria-hidden="true">{{ path | safe }}</svg>
{%- endmacro %}
{% macro grid(size=16) -%}
{{ base('<rect width="7" height="7" x="3" y="3" rx="1"/><rect width="7" height="7" x="14" y="3" rx="1"/><rect width="7" height="7" x="14" y="14" rx="1"/><rect width="7" height="7" x="3" y="14" rx="1"/>', size) }}
{%- endmacro %}
{% macro bookmark(filled=False, size=16) -%}
{{ base('<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"' + (' fill="currentColor"' if filled else '') + '/>', size) }}
{%- endmacro %}
{% macro edit(size=16) -%}
{{ base('<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/>', size) }}
{%- endmacro %}
{% macro trash(size=16) -%}
{{ base('<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/>', size) }}
{%- endmacro %}
{% macro search(size=16) -%}
{{ base('<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>', size) }}
{%- endmacro %}
{% macro plus(size=16) -%}
{{ base('<path d="M5 12h14"/><path d="M12 5v14"/>', size) }}
{%- endmacro %}
{% macro alert(size=16) -%}
{{ base('<path d="m21.73 18-8-14a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>', size) }}
{%- endmacro %}
{% macro sparkles(size=16) -%}
{{ base('<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>', size) }}
{%- endmacro %}
{% macro clock(size=16) -%}
{{ base('<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>', size) }}
{%- endmacro %}
{% macro spinner(size=16) -%}
{{ base('<path d="M21 12a9 9 0 1 1-6.219-8.56"/>', size) }}
{%- endmacro %}
{% macro sun(size=16) -%}
{{ base('<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>', size) }}
{%- endmacro %}
{% macro moon(size=16) -%}
{{ base('<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>', size) }}
{%- endmacro %}
{% macro menu(size=16) -%}
{{ base('<line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="18" y2="18"/>', size) }}
{%- endmacro %}
{% macro user(size=16) -%}
{{ base('<circle cx="12" cy="12" r="10"/><circle cx="12" cy="10" r="3"/><path d="M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662"/>', size) }}
{%- endmacro %}
{% macro log_out(size=16) -%}
{{ base('<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/>', size) }}
{%- endmacro %}
{% macro sliders(size=16) -%}
{{ base('<line x1="21" x2="14" y1="4" y2="4"/><line x1="10" x2="3" y1="4" y2="4"/><line x1="21" x2="12" y1="12" y2="12"/><line x1="8" x2="3" y1="12" y2="12"/><line x1="21" x2="16" y1="20" y2="20"/><line x1="12" x2="3" y1="20" y2="20"/><line x1="14" x2="14" y1="2" y2="6"/><line x1="8" x2="8" y1="10" y2="14"/><line x1="16" x2="16" y1="18" y2="22"/>', size) }}
{%- endmacro %}
{% macro download(size=16) -%}
{{ base('<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/>', size) }}
{%- endmacro %}
{% macro upload(size=16) -%}
{{ base('<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/>', size) }}
{%- endmacro %}
{% macro tag(size=16) -%}
{{ base('<path d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z"/><circle cx="7.5" cy="7.5" r="1.5"/>', size) }}
{%- endmacro %}