Commit Graph
206 Commits
Author SHA1 Message Date
kolaente 7eeb88da23 fix(migration): repair kanban views without a bucket configuration mode
Views broken by the bug above keep their state until something writes
them again, so repair them on startup: set the manual mode, seed the
default buckets when the view has none and place the project's tasks in
the default bucket.

Views whose project or saved filter is gone are skipped rather than
repaired with a dangling creator, soft-deleted tasks stay out of the
backfill, and the mode flip is the last write per view so an
interrupted run picks the view up again.
2026-08-04 01:09:10 +02:00
kolaente c385c0e72a feat(tasks): generalize task creation to batches
createTask becomes a wrapper around createTasks, which creates a whole
batch in one pass: project, creator, views and default buckets are
looked up once, indexes are assigned from a single max-index query
(preset indexes kept when free, collisions get the next free one), and
explicitly provided buckets are resolved once per distinct bucket —
verified to belong to the target project and checked against their
limit with the batch's own members counted. The row insert stays per
task because multi-row inserts don't reliably return autoincrement ids
on all supported databases.

Validation errors always carry the payload index; the single-create
wrapper unwraps them so existing callers keep their raw error types.

A new TasksBatchCreatedEvent fires once per batch (single create is a
batch of one) for listeners which can process all new tasks in one
pass; per-task TaskCreatedEvent semantics stay unchanged for webhooks,
mentions and audit.

Split for reviewability: builds together with the follow-up commit
adding the BulkTaskCreation model and error types.
2026-08-02 16:21:50 +02:00
kolaenteandkolaente de7a039068 fix(notifications): delete stored notifications of unknown types
349e6a590 renamed ListCreatedNotification's name from list.created to
project.created without rewriting the notifications.name column, so instances
upgraded from 0.19 or earlier still hold list.created rows carrying a full
project payload. Nothing can hydrate them, so nothing can scope them either,
and the read paths pass unrecognised names through unfiltered — leaking the
project's title, description, identifier, colour and owner to users since
removed from it.

Retaining rows no type can render only preserves that leak, so they go.
2026-07-29 07:58:17 +00:00
kolaenteandkolaente 0638200ac0 fix(notifications): check project access when reading notifications
Notification rows outlived access. A subscription survives a project being
unshared, so every notification already written for a revoked user stayed
readable — comment bodies, task titles, project names, deletion notices. The
read paths filtered on notifiable_id alone, with no permission check anywhere.
#3325 stopped the sender writing new ones; this is the other half.

The project a notification is about is persisted on the row when it is written
and the read paths filter on it in SQL, so LIMIT, OFFSET and total are all
computed on the filtered set. Notification types declare their project through
a capability interface in pkg/notifications, the same way they already declare
SubjectID, ThreadID and ToTitle — which is what lets the package below
pkg/models stay ignorant of what a project is.

project_id 0 means account-scoped and always visible, a positive value is
checked against the projects the caller can read, and -1 marks a project-scoped
row whose project could not be determined, so it is visible to nobody. Filtering
reuses the existing accessibleProjectIDsSubquery, so the page query and the
count cannot drift apart. A migration backfills existing rows from their stored
payloads, resolving through soft-deleted tasks so task.deleted rows still land
on their project.

Covers every read path: the v1 and v2 list endpoints, mark-as-read (which
echoes the payload back), the Atom feed, and the websocket push — the last of
which is load-bearing, since a row is still written for a revoked subscriber.
Deliberately no instance-admin bypass: notifications are always the caller's
own, and being an admin says nothing about whether they should still read a
comment out of a project they were removed from.
2026-07-29 07:58:17 +00:00
TinkandGitHub c4e40567b4 fix(migration): detect sqlite indexes created with lowercase SQL (#3354) 2026-07-29 09:21:25 +02:00
kolaenteandkolaente e684ac06c5 ci(lint): forbid plain Sync in migrations via forbidigo
tx.Sync/tx.Sync2 in pkg/migration now fails lint; brand-new-table
migrations and the fresh-install initSchema carry an explicit nolint
2026-07-26 13:42:33 +00:00
kolaenteandkolaente 957b114724 fix(migration): recreate indexes dropped by partial-struct sync migrations
Recreates model-declared indexes that are missing, matching by column
set so converted databases with differently-named equivalent indexes
are left alone. Fails with an actionable message if duplicate values
block recreating a unique index.

Fixes #3244
2026-07-26 13:42:33 +00:00
kolaenteandkolaente 73bca9344a fix(migration): stop existing-table struct syncs from dropping indexes
xorm's Sync drops every index and unique constraint the synced struct
doesn't declare (#3244). Adds a partialSync helper (no drops) and
converts all 65 existing-table struct syncs to it — the four v2.4.0
migrations that wiped users/tasks indexes on every upgrade, plus every
historical one back to 2019, each a latent index wipe and a pgloader
2BP01 abort point for installs upgrading from versions predating it.
2026-07-26 13:42:33 +00:00
kolaenteandkolaente 8df183123d perf(tasks): index task_positions on (project_view_id, position)
Every task creation looks for rows with the same position in the view to
resolve conflicts. Without an index on position that scans all positions of
the view, so creating n tasks in one project costs O(n²) row reads - the
second half of the slow import in #3297.
2026-07-26 01:13:08 +00:00
kolaente 00fd2c6155 fix(security): clear plaintext user tokens for reset/confirm/deletion (GHSA-r6w9-259g-gwrv) 2026-07-19 18:59:34 +02:00
Evan BakerandGitHub d0dad0437f fix(user): stop double-encoding FrontendSettings on UpdateUser (#2793) 2026-07-19 12:13:35 +02:00
TinkandGitHub b396bad3bb fix(postgres): resolve all statements against the configured schema (#3156) 2026-07-11 18:02:57 +02:00
TinkandGitHub d83e90541c feat(tasks): soft-delete tasks with permanent deletion after 30 days (#3119) 2026-07-07 11:38:33 +02:00
TinkandGitHub 7208694960 fix(auth): build OIDC end-session URL with RP-Initiated Logout params (#2943) 2026-06-19 18:27:33 +02:00
kolaenteandkolaente 647f1f4def fix(migration): fail loudly if a deduplicated position pair has no row
A pair returned by the GroupBy was just reported as duplicated, so a row
must exist. Continuing on !has would let the delete loop drop every row
for that pair without re-inserting one, silently losing positions. Abort
the migration instead.
2026-06-17 21:16:41 +00:00
kolaenteandkolaente a61e594952 fix(tasks): prevent duplicate task_positions rows and stale identifiers
A task could end up with more than one task_positions row for the same
(task_id, project_view_id): rapid/concurrent creation raced the
check-then-insert paths, and the create path could insert a position that
a triggered RecalculateTaskPositions had already persisted for the new
task. The table had no unique constraint, so the duplicates were stored
silently (#2844).

In the table view this made the LEFT JOIN on task_positions emit the task
twice; getTasksForProjects enriched only the map entry, so the duplicate
slice row kept an empty identifier and rendered as "#N" instead of
"PREFIX-N" (#2725).

- Add a unique index on task_positions(task_id, project_view_id) via a
  dedup migration (mirrors the task_buckets fix in 20250624092830) plus the
  unique(task_view) struct tag so fresh installs get it too.
- Harden the create path: only queue a position insert when one does not
  already exist for the task+view, and dedupe within the batch.
- Dedupe the task slice returned by getTasksForProjects by id, returning
  the enriched entry, so duplicate position rows can never surface a task
  twice or with a missing identifier.

Fixes #2844
Fixes #2725
2026-06-17 21:16:41 +00:00
kolaenteandkolaente 6387d8138a feat(time-tracking): add the time_entries table migration 2026-06-08 13:54:09 +00:00
kolaenteandGitHub 21ce33f8fd feat(projects): always store identifiers as uppercase (#2775) 2026-05-19 10:35:43 +02:00
kolaenteandkolaente c239834070 feat(migration): add bot_owner_id column to users 2026-05-01 14:44:10 +00:00
kolaenteandkolaente 736773ea77 feat(db): add is_admin column to users 2026-04-20 18:55:06 +00:00
kolaenteandkolaente 35f183979c feat: add license comments for agents and humans 2026-04-15 10:32:37 +00:00
kolaenteandkolaente a82bea567a feat(db): add license_status table migration
Add database migration for the license_status table that stores instance
ID, cached license validation response, and validation timestamps.
2026-04-15 10:32:37 +00:00
kolaenteandkolaente ed2632ddb2 feat(license): add license key validation package
Implement the license validation system with:
- Server communication with retry logic and exponential backoff
- In-memory state management for feature flags and user limits
- Cached validation with 72h expiry stored in database
- Background goroutine with adaptive check intervals (24h/1h)
- Graceful degradation to community mode on failure
- Instance ID generation and persistence
2026-04-15 10:32:37 +00:00
kolaenteandkolaente 9206f98d64 feat(tasks): enforce unique (project_id, index) via migration 2026-04-11 20:44:28 +00:00
kolaenteandkolaente 71282dcffd feat: add OAuth 2.0 authorization code model and migration
Add the OAuthCode model for storing short-lived authorization codes
with PKCE challenges. Codes are hashed (SHA-256) before storage and
are single-use with a 10-minute expiry. Add the database migration
and OAuth-specific error types.
2026-03-27 23:05:04 +00:00
kolaenteandkolaente 867c52745f fix: use MySQL-compatible CREATE INDEX in migration 20260224215050
MySQL does not support CREATE INDEX IF NOT EXISTS syntax. Switch on
database type to use IF NOT EXISTS for Postgres/SQLite and plain
CREATE INDEX with duplicate key error suppression for MySQL.

Fixes #2431
2026-03-23 16:45:50 +00:00
kolaente d4577c660f feat: add user_id to webhooks and user-directed event infrastructure
Add user_id column to webhooks table (nullable, for user-level webhooks
vs project-level). Extend webhook model, permissions, and listener to
support user-level webhooks that fire for user-directed events like
task reminders and overdue task notifications.

Add TasksOverdueEvent for dispatching overdue notifications via webhooks.
Update webhook permissions to handle both user-level and project-level
ownership. Add webhook test fixture and register webhooks table in test
fixture loader.
2026-03-08 19:45:53 +01:00
kolaente a5b1a90c42 refactor: remove typesense support
Typesense was an optional external search backend. This commit fully
removes the integration, leaving the database searcher as the only
search implementation.

Changes:
- Delete pkg/models/typesense.go (core integration)
- Delete pkg/cmd/index.go (CLI command for indexing)
- Simplify task search to always use database searcher
- Remove Typesense event listeners for task sync
- Remove TypesenseSync model registration
- Remove Typesense config keys and defaults
- Remove Typesense doctor health check
- Remove Typesense initialization from startup
- Clean up benchmark test
- Add migration to drop typesense_sync table
- Remove golangci-lint suppression for typesense.go
- Remove typesense-go dependency
2026-02-25 12:15:28 +01:00
kolaente 04e60472b7 feat: add sessions table migration
Adds the `sessions` table for storing server-side session records.
Each row tracks a session ID (UUID), user ID, hashed refresh token,
device info, IP address, and timestamps.
2026-02-25 10:30:25 +01:00
kolaente 0c7c07b3b8 fix: preserve teams external_id type when renaming on mysql
Avoid using the generic renameColumn helper for this migration on MySQL because it renames columns as BIGINT. Handle the teams oidc_id -> external_id rename with a MySQL-specific CHANGE statement that keeps VARCHAR(250) and remains idempotent.
2026-02-24 14:29:49 +01:00
kolaente 3d6c527b64 fix: cast bucket_configuration to text in postgres catchup query
The new catchup migration compared the postgres JSON column with string literals using !=, which fails in migration smoke tests. Use a PostgreSQL-specific WHERE clause with ::text and cover it with unit tests.
2026-02-24 14:29:49 +01:00
kolaente 99ac3e65b8 fix: add comprehensive catchup for bucket and filter format migrations
Convert bucket_configuration filters regardless of bucket_configuration_mode and catch remaining view-level filter values still stored in the legacy string format. Includes focused tests for the bucket conversion helper logic.

Fixes #2172

Fixes #2188

Fixes #2202
2026-02-24 14:29:49 +01:00
kolaente 4acad97688 fix: make teams oidc_id rename migration idempotent
Use the shared renameColumn helper for non-SQLite databases and skip the SQLite table rebuild when oidc_id is already absent. This prevents failures after partial upgrades where the column was already renamed.

Refs #2172

Refs #2285
2026-02-24 14:29:49 +01:00
kolaente b1534f1cc8 fix: replace tx.Sync() with explicit ALTER TABLE in webhooks migration
tx.Sync() fails on PostgreSQL because it tries to reconcile primary key constraints/indexes, causing index-drop errors. Use explicit ALTER TABLE ADD COLUMN with existence checks instead.

Fixes #2215
2026-02-24 14:29:49 +01:00
rhclaytoandGitHub cf029cef0c feat: add option to send Basic Auth header with webhook requests (#2137)
Resolves https://github.com/go-vikunja/vikunja/issues/2136
Docs PR: https://github.com/go-vikunja/website/pull/284
2026-01-30 15:07:31 +01:00
kolaenteandGitHub 6f0b685e38 fix: handle mixed-format bucket configurations in migration (#2033)
This change modifies the migration `20251001113831` to flexibly parse bucket configuration filters. 

This fixes this migration issue:

```
json: cannot unmarshal object into Go struct field bucketConfigurationCatchup.filter of type string
```

This occurred when a single `bucket_configuration` JSON array contained
mixed formats - some buckets with old string filters and some with
already-converted object filters.
2026-01-05 22:30:10 +01:00
CopilotandGitHub 166da9763d fix: handle MySQL 8 CREATE INDEX without IF NOT EXISTS support (#1903) 2025-11-28 15:57:54 +00:00
7dddc5dfa2 feat: task unread tracking (#1857)
---------

Co-authored-by: Mithilesh Gupta <guptamithilesh@protonmail.com>
Co-authored-by: kolaente <k@knt.li>
2025-11-27 15:14:42 +01:00
Mithilesh GuptaandGitHub 01a84dd2d5 feat: add comment count to tasks (#1771) 2025-11-11 23:00:05 +01:00
kolaente 67ebd876d3 fix(views): migrate filter bucket configuration
Resolves https://github.com/go-vikunja/vikunja/issues/1580
2025-10-01 11:54:24 +02:00
kolaenteandGitHub a81a3ee0e5 feat!: rename right to permission (#1277) 2025-08-13 11:05:05 +02:00
kolaente da0f6fb366 feat(auth): allow passing custom settings links to user account via openid claims 2025-08-03 13:25:32 +02:00
kolaente b08b43953b feat(plugins): add rudimentary plugin system 2025-08-01 17:06:02 +02:00
kolaente ca83ad1f98 feat: move to slog for logging 2025-07-21 18:15:39 +02:00
kolaente 159961b5e0 fix: add migration for non-unique task buckets 2025-06-25 11:12:24 +02:00
kolaenteandGitHub 842a71019d fix(view): add unique index for task buckets (#1005) 2025-06-24 10:45:17 +00:00
Dominik PschenitschniandGitHub 296577a875 fix: correct license header references (#882)
See originals:
- https://www.gnu.org/licenses/agpl-3.0.txt
- https://www.gnu.org/licenses/gpl-3.0.txt
2025-06-10 12:18:38 +02:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>kolaente
bbf7679dd4 fix(deps): update module github.com/olekukonko/tablewriter to v1 (#750)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kolaente <k@knt.li>
2025-05-23 10:00:58 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>kolaente
4a75f7d4da chore(deps): update golangci/golangci-lint-action action to v7 (#462)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kolaente <k@knt.li>
2025-04-02 09:28:56 +02:00
kolaente 25f3c5d9d7 fix(migration): cast to text 2025-03-25 21:47:52 +01:00