Skip to content

ci(release)!: v14#2189

Draft
dargmuesli wants to merge 66 commits intomainfrom
beta
Draft

ci(release)!: v14#2189
dargmuesli wants to merge 66 commits intomainfrom
beta

Conversation

@dargmuesli
Copy link
Copy Markdown
Member

@dargmuesli dargmuesli commented Jan 26, 2026

📚 Description

Breaking changes:

  • runtime configuration key rename from private to vibetype
  • route api/service/traefik/authentication moved to /api/internal/service/postgraphile/authentication
  • all routes are csrf-protected by default now and the csrf token header is named x-csrf-token
  • feat(postgraphile)!: upgrade to v5 #2201

Configuration and Environment Variables:

📝 Checklist

  • All commits follow the Conventional Commit format or I'm fine with a squash merge of this PR
  • The PR's title follows the Conventional Commit format

dargmuesli and others added 27 commits July 8, 2025 06:15
…rename

feat(nuxt)!: rename runtime configuration key
## [13.0.0-beta.1](12.15.1...13.0.0-beta.1) (2025-07-08)

### ⚠ BREAKING CHANGES

* **nuxt:** rename runtime configuration key

### Features

* **nuxt:** rename runtime configuration key ([325238f](325238f))
## [13.0.0-beta.2](13.0.0-beta.1...13.0.0-beta.2) (2025-12-02)

### Features

* **account:** rework creation success page ([7d51369](7d51369))
* **account:** show profile picture in menu ([2d454fd](2d454fd))
* **account:** unify detail buttons ([09ec5e6](09ec5e6))
* **app:** disable text selection ([df7b53d](df7b53d))
* **components:** double menu bottom padding for ios ([54a81f3](54a81f3))
* **components:** replace toast with sonner ([500e1bd](500e1bd))
* **components:** update error design ([07e75d7](07e75d7))
* **composables:** add alert error ([3119c37](3119c37))
* **composables:** centralize urql result handling ([cec435e](cec435e))
* **content:** remove dispute resolution section from imprint ([ce18763](ce18763))
* **docs:** add redirection to app stores ([4369d28](4369d28))
* **docs:** do not redirect when app is installed ([c82b422](c82b422))
* **event:** show link option no matter the attendance type ([1c28141](1c28141))
* **head:** test disabled scaling ([e8730bd](e8730bd))
* **i18n:** uppercase global site name ([bd96c08](bd96c08))
* **notification:** replace `moment-timezone` with `Intl` ([8c88d57](8c88d57))
* **pages:** drop marketing ([c67f333](c67f333))
* **recommendation:** add ([19b3f5b](19b3f5b))
* **recommendation:** show up to 10 ([ec9678d](ec9678d))
* **session:** allow to clear api client data ([278b484](278b484))
* **session:** redirect to root on sign out ([295bc31](295bc31))
* **tiptap:** support justified alignment ([f17ca9c](f17ca9c))

### Bug Fixes

* **account:** unref date for registration ([e35cb03](e35cb03))
* **attendance:** add missing hint to camera error ([d3b7233](d3b7233))
* **attendance:** don't log error twice ([d2e12d1](d2e12d1))
* **components:** add more padding for bottom menu on ios ([#2071](#2071)) ([6126590](6126590))
* **components:** align dashboard menu item name ([11c7702](11c7702))
* **components:** correct time zone display ([33d1583](33d1583))
* **early-bird:** correct button label ([f980ed3](f980ed3))
* **event:** correct maximum guest count fetching ([95c6528](95c6528))
* **event:** relax ical data requirements ([2ccf25b](2ccf25b))
* **event:** show match only on recommendation ([24a8c3c](24a8c3c))
* **event:** update card style ([69a8170](69a8170))
* **gql:** correct variable computation ([42a5885](42a5885))
* **legal-term:** correct loading ([65e9c80](65e9c80))
* **modules:** correct pwa scope extensions ([3b5d072](3b5d072))
* **notification:** correct enum capitalization for invitation visibility ([6b7c4bd](6b7c4bd))
* **notification:** only attach successfully fetched ical data ([ba52f0b](ba52f0b))
* **notification:** skip empty payloads ([2f34b68](2f34b68))
* **preference:** await asynchronous data at initialization ([bb3422b](bb3422b))
* **pwa:** set id ([cf486d9](cf486d9))
* **recommendation:** don't show section if content is empty ([ce65f88](ce65f88))
* **recommendation:** show just one for now ([77aaacf](77aaacf))
* schedule release ([b695e10](b695e10))
* schedule release ([67695d9](67695d9))
* schedule release ([f8f5a6c](f8f5a6c))
* schedule release ([e273b95](e273b95))
* **security:** apply camera permission globally ([88043a3](88043a3))
* **session:** move developer information button up ([9e1bc5c](9e1bc5c))
* **session:** redirect from root to dashboard when signed in ([41fdbfe](41fdbfe))
* **upload:** unref uppy ([e7dd28e](e7dd28e))

### Performance Improvements

* **aws:** complete migration to ses client v2 ([1ed17ac](1ed17ac))
* **docker:** add volume for development node modules ([9ec8a43](9ec8a43))
* **docker:** cache playwright node modules separately ([7187cf0](7187cf0))
* **docker:** use cache mounts ([ef0a6db](ef0a6db))
* **event:** replace `dayjs` with `Intl` ([44240d7](44240d7))
* **utils:** replace copy dependency ([4eb7c65](4eb7c65))
## [13.0.0-beta.3](13.0.0-beta.2...13.0.0-beta.3) (2025-12-02)

### ⚠ BREAKING CHANGES

* **store:** remove plain jwt

### Bug Fixes

* **store:** remove plain jwt ([aa03b04](aa03b04))
## [14.0.0-beta.1](13.3.0...14.0.0-beta.1) (2026-01-06)

### ⚠ BREAKING CHANGES

* **store:** remove plain jwt
* **nuxt:** rename runtime configuration key

### Features

* **nuxt:** rename runtime configuration key ([325238f](325238f))

### Bug Fixes

* **store:** remove plain jwt ([aa03b04](aa03b04))
## [14.0.0-beta.2](14.0.0-beta.1...14.0.0-beta.2) (2026-01-17)

### Bug Fixes

* schedule release ([c6806e4](c6806e4))
feat(security)!: use http-only cookie
## [14.0.0-beta.3](14.0.0-beta.2...14.0.0-beta.3) (2026-01-26)

### ⚠ BREAKING CHANGES

* **security:** use http-only cookie

### Features

* **content:** improve error handling ([f441aa0](f441aa0))
* **security:** use http-only cookie ([547df19](547df19))

### Bug Fixes

* **components:** deduplicate icon wrapper ([357a623](357a623))
* **deps:** patch @nuxt/content ([495a671](495a671))
## [14.0.0-beta.4](14.0.0-beta.3...14.0.0-beta.4) (2026-02-20)

### Bug Fixes

* **urql:** correct site name ([742aa8c](742aa8c))
@maevsi-bot
Copy link
Copy Markdown

🎉 This PR is included in version 14.0.0-beta.4 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

dargmuesli and others added 6 commits March 1, 2026 20:41
perf(guest): allow immediate data display
## [14.0.0-beta.7](14.0.0-beta.6...14.0.0-beta.7) (2026-03-01)

### Features

* **cookie:** add `getResponseCookie` utility ([bff2be4](bff2be4))
* **guest:** enable JWT guest mutation endpoint ([88f7b92](88f7b92))
* **guest:** use JWT composable for server-side authentication ([4b6a97a](4b6a97a))
* **jwt:** add JWT and security composables ([cfd74d8](cfd74d8))

### Reverts

* **security:** drop response promise return ([6f719ce](6f719ce))
@maevsi-bot
Copy link
Copy Markdown

🎉 This PR is included in version 14.0.0-beta.7 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

dargmuesli and others added 2 commits March 3, 2026 17:58
## [14.0.0-beta.8](14.0.0-beta.7...14.0.0-beta.8) (2026-03-03)

### Bug Fixes

* **nuxt:** access headers without h3 imports ([40a9f1c](40a9f1c))
@maevsi-bot
Copy link
Copy Markdown

🎉 This PR is included in version 14.0.0-beta.8 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This is a major version release (v14) introducing several breaking changes as part of the PostGraphile v5 upgrade and security hardening. The PR refactors JWT handling to use server-side httpOnly cookies exclusively, renames the runtime configuration key from private to vibetype, adds CSRF protection to all routes by default, migrates from nodeId/id to rowId/id throughout the GraphQL layer, and switches to HTTPS for all development and test environments.

Changes:

  • Refactored authentication to use server-managed JWT cookies via new /api/model/jwt endpoints (GET/POST/PUT/DELETE), removing client-side JWT storage and enabling Sentry's Pinia integration
  • Migrated all GraphQL fragments, queries, and mutations from nodeId/id to id/rowId for PostGraphile v5 compatibility, and updated all component references accordingly
  • Added default CSRF protection with x-csrf-token header, HTTPS-only development certificates, and runtime config key rename from private to vibetype

Reviewed changes

Copilot reviewed 170 out of 178 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/server/api/model/jwt.*.ts New server endpoints for JWT CRUD operations
src/app/app.vue JWT initialization and update logic moved from useAuth to inline code
src/app/composables/jwt.ts New JWT composables replacing auth.ts
src/app/composables/security.ts New CSRF composables for token handling
src/shared/utils/urql.ts Updated cache invalidation for rowId/id mapping
src/shared/utils/jwt.ts Refactored JWT utilities (cookie params, public key fetch)
src/shared/utils/constants.ts Moved many constants to node/static
src/shared/utils/dependencies/urql.ts New server-side urql mutation helper
src/shared/types/model.d.ts New Jwt payload type definition
src/node/static/index.ts New central constants including CSRF, JWT cookie names
src/nuxt.config.ts Runtime config rename, CSRF route config
src/config/modules/security.ts CSRF configuration
src/config/modules/cookieControl.ts Added CSRF cookie to consent
src/config/environments/development.ts HTTPS dev server with app.localhost host
src/gql/documents/** All fragments/mutations renamed for rowId
src/app/components/** Updated all component references from id/nodeId to rowId
src/app/pages/** Updated all page references, sign-in flow, event management
src/server/plugins/urql.ts Added SITE_NAME to service href, moved type declaration
src/server/utils/networking.ts New PostGraphile service href helpers (port 5678)
src/server/utils/notification.ts Updated enum comparison, dynamic PostGraphile URL
tests/** Updated imports, snapshots, HTTP→HTTPS, devdevelopment
Dockerfile Updated base image to PostGraphile 2.0.0-beta.1
AGENTS.md New project coding guidelines
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (2)

src/app/components/form/FormSessionCreate.vue:100

  • The error messages in FormSessionCreate.vue have been simplified from specific PostgreSQL error codes (postgres55000 for unverified email, postgresP0002 for login failure) to a single generic message. This significantly reduces the ability of users to understand what went wrong during sign-in. The original messages told users whether their email wasn't verified or whether their credentials were wrong — the new generic message doesn't differentiate these cases. Consider preserving the more specific error feedback, possibly by mapping error responses from the server endpoint.
    src/app/components/form/FormSessionCreate.vue:83
  • The TURNSTILE_HEADER_KEY used here on line 83 seems like it should be TURNSTILE_HEADER_NAME to be consistent with the new constant name exported from ~~/node/static (line 24 of src/node/static/index.ts). If TURNSTILE_HEADER_KEY is a different auto-imported constant, this may be fine, but it's worth verifying consistency.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +53 to +55
...('rowId' in args.input
? { id: args.input.rowId }
: { nodeId: args.input.id }),
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invalidateCache function's field mapping looks inverted. When the input has rowId, you map it to { id: args.input.rowId } which is correct (since urql cache uses id as the key). But when the input has id (the new GraphQL global node ID), you map it to { nodeId: args.input.id }. After the PostGraphile v5 migration where nodeId has been replaced by id in fragments, shouldn't the fallback also use id instead of nodeId? The cache key name nodeId is no longer present in the fragments (they all now use id and rowId).

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +28
const variables = // TODO: remove with Postgraphile v5
typeof query.variables === 'string'
? JSON.parse(query.variables)
: query.variables

const graphql = await event.$fetch('/graphql', {
baseURL,
body: {
query: query.query,
operationName: query.operationName,
variables,
}, // TODO: remove with Postgraphile v5
headers: {
...(jwt
? {
authorization: `Bearer ${jwt}`,
}
: {}),
},
method: 'GET',
query,
method: 'POST', // TODO: 'GET' with Postgraphile v5
// query, // TODO: reenable with Postgraphile v5
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TODO comment says "remove with Postgraphile v5", but this PR is the PostGraphile v5 upgrade (as stated in referenced PR #2201). The GET handler now manually parses query.variables from a string and converts the request to POST — this workaround and both TODO comments should be removed or updated since PostGraphile v5 is being adopted in this very PR.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +73
if (mutationJwtCreate.error) {
if (mutationJwtCreate.error.networkError) {
return throwError({
status: 500,
statusText:
(mutationJwtCreate.error.networkError.cause as { message?: string })
?.message || mutationJwtCreate.error.networkError.message,
})
}

if (mutationJwtCreate.error.graphQLErrors.length) {
const messages = mutationJwtCreate.error.graphQLErrors
.map((error: GraphQLError) => error.message)
.join('; ')
return throwError({
status: 500,
statusText: `GraphQL error(s) during JWT creation: ${messages}`,
})
}

return throwError({
status: 500,
statusText:
mutationJwtCreate.error.message ||
'Unexpected error during JWT creation.',
})
}

if (!mutationJwtCreate.data) {
return throwError({
status: 500,
statusText: `No data returned from JWT creation mutation.`,
})
}

const jwt = mutationJwtCreate.data?.jwtCreate?.result

if (!jwt) {
return throwError({
status: 500,
statusText: 'No JWT returned from creation mutation.',
})
}
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jwt.post.ts duplicates the error-handling logic that already exists in getJwtFromResult (from src/shared/utils/api.ts). The jwt.put.ts handler uses getJwtFromResult for the same purpose. This handler should also use getJwtFromResult to avoid maintaining the same error-handling code in two places.

Copilot uses AI. Check for mistakes.
graphql(`
mutation DeleteEventFavoriteById($input: DeleteEventFavoriteByIdInput!) {
deleteEventFavoriteById(input: $input) {
mutation DeleteEventFavoriteByRowId($input: DeleteEventFavoriteInput!) {
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DeleteEventFavoriteByRowId mutation name is inconsistent with the GraphQL operation it calls. The mutation is named DeleteEventFavoriteByRowId but calls deleteEventFavorite (not deleteEventFavoriteByRowId), and the input type is DeleteEventFavoriteInput! (not DeleteEventFavoriteByRowIdInput!). While this may match the actual PostGraphile v5 schema, the mutation operation name DeleteEventFavoriteByRowId is misleading since it doesn't actually delete by row ID — it uses a generic DeleteEventFavoriteInput.

Suggested change
mutation DeleteEventFavoriteByRowId($input: DeleteEventFavoriteInput!) {
mutation DeleteEventFavorite($input: DeleteEventFavoriteInput!) {

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +111
const getJwt = async ({
event,
body,
}: {
event: H3Event
body: z.infer<typeof jwtUpdateBodySchema>
}) => {
if ('id' in body) {
const jwtUpdateMutation = await urqlMutate({
event,
urql: {
mutation: mutationJwtUpdate,
variables: {
...body,
},
},
})
return getJwtFromResult({
context: 'JWT update',
extract: (data: JwtUpdateMutation) => data.jwtUpdate?.result,
result: jwtUpdateMutation,
})
}

if ('attendanceId' in body) {
const jwtUpdateAttendanceAddMutation = await urqlMutate({
event,
urql: {
mutation: mutationJwtUpdateAttendanceAdd,
variables: {
input: {
...body,
},
},
},
})
return getJwtFromResult({
context: 'JWT attendance add',
extract: (data: JwtUpdateAttendanceAddMutation) =>
data.jwtUpdateAttendanceAdd?.result,
result: jwtUpdateAttendanceAddMutation,
})
}

if ('guestId' in body) {
const jwtUpdateGuestAddMutation = await urqlMutate({
event,
urql: {
mutation: mutationJwtUpdateGuestAdd,
variables: {
input: {
...body,
},
},
},
})
return getJwtFromResult({
context: 'JWT guest add',
extract: (data: JwtUpdateGuestAddMutation) =>
data.jwtUpdateGuestAdd?.result,
result: jwtUpdateGuestAddMutation,
})
}
}
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getJwt function can return undefined when none of the if conditions match (i.e., the body has none of id, attendanceId, or guestId). While the Zod schema (z.union) should prevent this at runtime, TypeScript won't know that all branches are exhaustive. Consider adding an explicit throw or return throwError(...) at the end of getJwt to make the function's return type non-optional and catch any future schema changes that could introduce a gap.

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +20
const csrfHeaderValue = headers.get(CSRF_HEADER_NAME)
const turnstileHeaderValue = headers.get(TURNSTILE_HEADER_NAME)
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSRF_COOKIE_NAME and CSRF_HEADER_NAME constants are used in src/shared/utils/dependencies/urql.ts (lines 19, 27) but are not imported. Since this file is under src/shared/utils/ (not a Nuxt auto-import location for server utils), these constants won't be auto-imported. Similarly, TURNSTILE_HEADER_NAME on line 20 is used without import. These need explicit imports from ~~/node/static.

Copilot uses AI. Check for mistakes.
@@ -1,3 +1,4 @@
import { createError } from 'h3'
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The createError import from h3 is added but throwError (which is used throughout the codebase) already exists in this file. If createError is not actually used in this file, this import is unnecessary. If it's meant to be used inside throwError, the function body should be updated accordingly.

Copilot uses AI. Check for mistakes.
dargmuesli and others added 4 commits March 14, 2026 06:42
## [14.0.0-beta.9](14.0.0-beta.8...14.0.0-beta.9) (2026-03-14)

### ⚠ BREAKING CHANGES

* **jwt:** use ecdsa key

### Features

* **jwt:** use ecdsa key ([80885a8](80885a8))
@maevsi-bot
Copy link
Copy Markdown

🎉 This PR is included in version 14.0.0-beta.9 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@dargmuesli dargmuesli force-pushed the beta branch 3 times, most recently from 50c9cb2 to 28c9fa5 Compare April 2, 2026 04:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants