* fix: Normalize IP addresses to avoid validation errors on audit columns
Koa's `ctx.request.ip` can yield values that fail Sequelize's `isIP`
validation (X-Forwarded-For chains, IPv6 zone identifiers, "unknown"
from misconfigured proxies). This drops the IP metadata silently
instead of raising a 500 on Event/User writes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test: Cover IP normalization on User setters
Reviewer feedback. Also switches the column-options `set` to TypeScript
get/set accessors — the original approach was shadowed by the class
field declaration and never actually fired, which the new tests would
have caught.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix: Suspended users should not be included in cached member count for groups
* fix: Defer CounterCache hook registration until model is initialized
The previous test-only no-op hid a timing bug where setImmediate could
fire before the Sequelize instance had registered the related model,
causing "Model not initialized" failures. Poll until the model is
ready, and unref the pending immediate so it does not keep the event
loop alive in environments where the database is never initialized.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* perf: Reduce overhead of group member count invalidation
Select only the groupId column with raw queries and de-duplicate before
issuing Redis deletes, avoiding loading full GroupUser rows into memory
when a user belongs to many groups.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: unref Redis healthcheck interval
Don't keep the Node event loop alive solely for the periodic ping; the
event loop should drain on its own when the application is shutting
down or a Jest worker is finishing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor: Centralize counter cache key in RedisPrefixHelper
Avoid duplicating the "count:<Model>:<relation>:<id>" string between
the CounterCache decorator and the User suspension hook by routing
both through a single getCounterCacheKey helper.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: Walk to parent transaction when scheduling cache invalidation
Nested savepoints commit independently of their outer transaction, so
afterCommit callbacks attached to the inner transaction may run after
the outer rolls back, or never run at all. Match the pattern used in
Collection, Event, and base/Model and walk to the parent transaction
so the cache invalidation fires after the real outer commit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix: Ensure OTP is bound to teamId
* fix: Address review feedback on OTP tenant scoping
- Trim whitespace in VerificationCode Redis keys to match DB lookup
normalization.
- Redirect with invalid-code (rather than leaking a backend error)
when no user exists for the email in the resolved team.
- Correct retrieve() JSDoc to state undefined instead of null.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* Add installation.create API endpoint
- Add new endpoint that accepts teamName, userName, userEmail
- Use accountProvisioner to create team and user
- Only allow when no existing teams exist (unauthenticated)
- Add comprehensive tests for success, failure, and validation cases
- Add schema validation with Zod
- Include rate limiting (5 per hour)
- Follow existing API patterns and conventions
* Remove changes to .env.test
* fix
* Centralize validation
* test
* test
* test
---------
Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
* fix: rename to group_permissions
* fix: delete null collectionId records before setting non null constraint
* fix: use scope with collectionId not null
* fix: update model with documentId
* fix: rename to GroupPermission
* fix: rename collection_users to user_permissions
* fix: teamPermanentDeleter test
* fix: use scope with collectionId not null
* fix: update model with documentId
* fix: rename to UserPermission
* fix: create views upon table rename for zero downtime
* fix: remove comments
* Split permissions for reading documents from updating collection
* fix: Admins should have collection read permission, tests
* tsc
* Add admin option to permission selector
* Combine publish and create permissions, update -> createDocuments where appropriate
* Plural -> singular
* wip
* Quick version of collection structure loading, will revisit
* Remove documentIds method
* stash
* fixing tests to account for admin creation
* Add self-hosted migration
* fix: Allow groups to have admin permission
* Prefetch collection documents
* fix: Document explorer (move/publish) not working with async documents
* fix: Cannot re-parent document to collection by drag and drop
* fix: Cannot drag to import into collection item without admin permission
* Remove unused isEditor getter
* fix: Logic error in toast
fix: Remove useless component
* fix: Logout not clearing all stores
* Add icons to notification settings
* Add eslint rule to enforce spaced comment
* Add eslint rule for arrow-body-style
* Add eslint rule to enforce self-closing components
* Add menu to api key settings
Fix: Deleting webhook subscription does not remove from UI
Split webhook subscriptions into active and inactive
Styling updates
* helper
* Add script to move notification settings
* wip, removal of NotificationSettings
* event name
* iteration
* test
* test
* Remove last of NotificationSettings model
* refactor
* More fixes
* snapshots
* Change emails to class instances for type safety
* test
* docs
* Update migration for self-hosted
* tsc