@habityzer/nuxt-symfony-kinde-layer 2.1.2 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +52 -0
- package/.releaserc.json +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +1 -0
- package/SETUP_NPM_PUBLISH.md +134 -0
- package/eslint.config.mjs +1 -1
- package/nuxt.config.ts +5 -5
- package/package.json +1 -1
- package/server/api/symfony/[...].ts +93 -79
- package/pnpm-workspace.yaml +0 -5
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- master
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
release:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
issues: write
|
|
15
|
+
pull-requests: write
|
|
16
|
+
id-token: write
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout code
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
|
|
25
|
+
- name: Setup pnpm
|
|
26
|
+
uses: pnpm/action-setup@v4
|
|
27
|
+
with:
|
|
28
|
+
version: 9
|
|
29
|
+
|
|
30
|
+
- name: Setup Node.js
|
|
31
|
+
uses: actions/setup-node@v4
|
|
32
|
+
with:
|
|
33
|
+
node-version: '20'
|
|
34
|
+
cache: 'pnpm'
|
|
35
|
+
registry-url: 'https://registry.npmjs.org'
|
|
36
|
+
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: pnpm install --frozen-lockfile
|
|
39
|
+
|
|
40
|
+
- name: Prepare Nuxt
|
|
41
|
+
run: pnpm nuxt prepare
|
|
42
|
+
|
|
43
|
+
- name: Run linter
|
|
44
|
+
run: pnpm lint
|
|
45
|
+
|
|
46
|
+
- name: Release
|
|
47
|
+
env:
|
|
48
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
49
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
50
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
51
|
+
run: pnpm release
|
|
52
|
+
|
package/.releaserc.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [2.1.4](https://github.com/Habityzer/nuxt-symfony-kinde-layer/compare/v2.1.3...v2.1.4) (2026-01-01)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* Add Nuxt prepare step to CI and correct ESLint config import path ([07471f5](https://github.com/Habityzer/nuxt-symfony-kinde-layer/commit/07471f5234e1322a2ccfe5709cb8109badededda))
|
|
7
|
+
* Remove invalid pnpm-workspace.yaml for single package ([7e7d6a1](https://github.com/Habityzer/nuxt-symfony-kinde-layer/commit/7e7d6a19ffd2edccfee80b944c947c364b6b2863))
|
|
8
|
+
|
|
9
|
+
## [2.1.3](https://github.com/Habityzer/nuxt-symfony-kinde-layer/compare/v2.1.2...v2.1.3) (2026-01-01)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* Update Kinde configuration to use environment variables and enhance Symfony proxy request handling ([3e0d0bd](https://github.com/Habityzer/nuxt-symfony-kinde-layer/commit/3e0d0bd09ea26a21940f0a0d16cc9aaf5a327be8))
|
|
15
|
+
|
|
1
16
|
## [2.1.2](https://github.com/Habityzer/nuxt-symfony-kinde-layer/compare/v2.1.1...v2.1.2) (2025-12-26)
|
|
2
17
|
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Setting Up Automated NPM Publishing
|
|
2
|
+
|
|
3
|
+
This project uses GitHub Actions with semantic-release to automatically publish to npm when you push to the `master` branch.
|
|
4
|
+
|
|
5
|
+
## Setup Instructions
|
|
6
|
+
|
|
7
|
+
### 1. Create NPM Access Token
|
|
8
|
+
|
|
9
|
+
> **Important**: As of December 9, 2025, npm classic tokens have been permanently revoked. You must use **granular access tokens** for CI/CD workflows.
|
|
10
|
+
|
|
11
|
+
**Option A: Using npm CLI (recommended):**
|
|
12
|
+
```bash
|
|
13
|
+
npm token create --type automation --scope @habityzer
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Option B: Using the web interface:**
|
|
17
|
+
1. Go to [npmjs.com/settings/~/tokens](https://www.npmjs.com/settings/~/tokens) and log in
|
|
18
|
+
2. Click **Generate New Token** → **Granular Access Token**
|
|
19
|
+
3. Configure your token:
|
|
20
|
+
- **Token name**: `github-actions-publish` (or any descriptive name)
|
|
21
|
+
- **Expiration**: Up to 90 days (maximum for publish tokens)
|
|
22
|
+
- **Packages and scopes**: Select your package or `@habityzer` scope
|
|
23
|
+
- **Permissions**: Select **Read and write**
|
|
24
|
+
- ✅ **Enable "Bypass 2FA"** for automated workflows
|
|
25
|
+
4. Click **Generate Token**
|
|
26
|
+
5. Copy the token (starts with `npm_...`)
|
|
27
|
+
|
|
28
|
+
> **Note**: Granular tokens for publishing expire after a maximum of 90 days. Set a reminder to regenerate the token before expiration, or consider using [OIDC trusted publishing](https://docs.npmjs.com/generating-provenance-statements) for a more secure, token-free approach.
|
|
29
|
+
|
|
30
|
+
### 2. Add NPM Token to GitHub
|
|
31
|
+
|
|
32
|
+
1. Go to your GitHub repository
|
|
33
|
+
2. Navigate to **Settings** → **Secrets and variables** → **Actions**
|
|
34
|
+
3. Click **New repository secret**
|
|
35
|
+
4. Name: `NPM_TOKEN`
|
|
36
|
+
5. Value: Paste your npm token from step 1
|
|
37
|
+
6. Click **Add secret**
|
|
38
|
+
|
|
39
|
+
### 3. Configure GitHub Actions in NPM
|
|
40
|
+
|
|
41
|
+
When setting up your package on npmjs.com for automated publishing:
|
|
42
|
+
|
|
43
|
+
1. Go to your package page on npmjs.com
|
|
44
|
+
2. Navigate to **Settings** → **Publishing Access**
|
|
45
|
+
3. Under **GitHub Actions**, you'll be asked for:
|
|
46
|
+
- **Workflow filename**: Enter `publish.yml`
|
|
47
|
+
- This tells npm which workflow file in `.github/workflows/` will publish your package
|
|
48
|
+
|
|
49
|
+
### 4. How It Works
|
|
50
|
+
|
|
51
|
+
The workflow (`.github/workflows/publish.yml`) will:
|
|
52
|
+
|
|
53
|
+
1. **Trigger**: Automatically runs when you push to the `master` branch
|
|
54
|
+
2. **Analyze**: Semantic-release reads your commit messages (following conventional commits)
|
|
55
|
+
3. **Version**: Automatically bumps the version based on your commits:
|
|
56
|
+
- `feat:` → minor version (1.0.0 → 1.1.0)
|
|
57
|
+
- `fix:` → patch version (1.0.0 → 1.0.1)
|
|
58
|
+
- `BREAKING CHANGE:` → major version (1.0.0 → 2.0.0)
|
|
59
|
+
4. **Changelog**: Updates `CHANGELOG.md`
|
|
60
|
+
5. **Git Tag**: Creates a git tag for the new version
|
|
61
|
+
6. **Publish**: Publishes to npm
|
|
62
|
+
7. **Commit**: Commits the changelog and version bump back to your repo
|
|
63
|
+
|
|
64
|
+
### 5. Commit Message Format
|
|
65
|
+
|
|
66
|
+
Use conventional commits for automatic versioning:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Patch release (1.0.0 → 1.0.1)
|
|
70
|
+
git commit -m "fix: resolve authentication bug"
|
|
71
|
+
|
|
72
|
+
# Minor release (1.0.0 → 1.1.0)
|
|
73
|
+
git commit -m "feat: add new login method"
|
|
74
|
+
|
|
75
|
+
# Major release (1.0.0 → 2.0.0)
|
|
76
|
+
git commit -m "feat: redesign API
|
|
77
|
+
|
|
78
|
+
BREAKING CHANGE: API structure has changed"
|
|
79
|
+
|
|
80
|
+
# No release (documentation, etc.)
|
|
81
|
+
git commit -m "docs: update README"
|
|
82
|
+
git commit -m "chore: update dependencies"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 6. Manual Release (Optional)
|
|
86
|
+
|
|
87
|
+
If you prefer to release manually:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pnpm release
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This runs semantic-release locally. Make sure you have:
|
|
94
|
+
- `NPM_TOKEN` environment variable set
|
|
95
|
+
- Committed all your changes
|
|
96
|
+
- Pushed to the master branch
|
|
97
|
+
|
|
98
|
+
## Troubleshooting
|
|
99
|
+
|
|
100
|
+
### "No NPM_TOKEN found"
|
|
101
|
+
- Make sure you've added `NPM_TOKEN` as a GitHub secret
|
|
102
|
+
- Check that the secret name is exactly `NPM_TOKEN` (case-sensitive)
|
|
103
|
+
|
|
104
|
+
### "No release published"
|
|
105
|
+
- Check your commit messages follow conventional commits format
|
|
106
|
+
- Semantic-release only publishes if there are releasable commits
|
|
107
|
+
- View the GitHub Actions logs for details
|
|
108
|
+
|
|
109
|
+
### "Permission denied" or "Invalid npm token"
|
|
110
|
+
- Ensure you're using a **granular access token** (not classic token)
|
|
111
|
+
- Check that the token has **Read and write** permissions
|
|
112
|
+
- Ensure **"Bypass 2FA"** is enabled for CI/CD workflows
|
|
113
|
+
- Check that your npm account has publish access to the `@habityzer` scope
|
|
114
|
+
- Verify the token hasn't expired (granular tokens expire after max 90 days)
|
|
115
|
+
|
|
116
|
+
### "Package already published"
|
|
117
|
+
- Semantic-release automatically handles versions
|
|
118
|
+
- If you manually published the same version, semantic-release will skip it
|
|
119
|
+
|
|
120
|
+
## Current Configuration
|
|
121
|
+
|
|
122
|
+
- **Branch**: `master` (configured in `.releaserc.json`)
|
|
123
|
+
- **Package**: `@habityzer/nuxt-symfony-kinde-layer`
|
|
124
|
+
- **Workflow**: `.github/workflows/publish.yml`
|
|
125
|
+
- **NPM Publish**: Enabled (set in `.releaserc.json`)
|
|
126
|
+
|
|
127
|
+
## Additional Resources
|
|
128
|
+
|
|
129
|
+
- [npm Granular Access Tokens Documentation](https://docs.npmjs.com/about-access-tokens#granular-access-tokens)
|
|
130
|
+
- [GitHub Blog: npm Classic Tokens Revoked (Dec 2025)](https://github.blog/changelog/2025-12-09-npm-classic-tokens-revoked-session-based-auth-and-cli-token-management-now-available/)
|
|
131
|
+
- [Semantic Release Documentation](https://semantic-release.gitbook.io/)
|
|
132
|
+
- [Conventional Commits](https://www.conventionalcommits.org/)
|
|
133
|
+
- [OIDC Trusted Publishing for npm](https://docs.npmjs.com/generating-provenance-statements)
|
|
134
|
+
|
package/eslint.config.mjs
CHANGED
package/nuxt.config.ts
CHANGED
|
@@ -48,11 +48,11 @@ export default defineNuxtConfig({
|
|
|
48
48
|
|
|
49
49
|
// Default Kinde configuration (projects MUST override with their credentials)
|
|
50
50
|
kindeAuth: {
|
|
51
|
-
authDomain: '', // Project must provide
|
|
52
|
-
clientId: '', // Project must provide
|
|
53
|
-
clientSecret: '', // Project must provide
|
|
54
|
-
redirectURL: '', // Project must provide
|
|
55
|
-
logoutRedirectURL: '', // Project must provide
|
|
51
|
+
authDomain: process.env.KINDE_AUTH_DOMAIN || 'https://dummy.kinde.com', // Project must provide, dummy for build
|
|
52
|
+
clientId: process.env.KINDE_CLIENT_ID || 'dummy-client-id', // Project must provide, dummy for build
|
|
53
|
+
clientSecret: process.env.KINDE_CLIENT_SECRET || 'dummy-secret', // Project must provide, dummy for build
|
|
54
|
+
redirectURL: process.env.KINDE_REDIRECT_URL || 'http://localhost:3000/api/auth/kinde_callback', // Project must provide, dummy for build
|
|
55
|
+
logoutRedirectURL: process.env.KINDE_LOGOUT_REDIRECT_URL || 'http://localhost:3000', // Project must provide, dummy for build
|
|
56
56
|
postLoginRedirectURL: '/dashboard', // Default, can be overridden
|
|
57
57
|
cookie: {
|
|
58
58
|
prefix: 'app_', // Projects MUST override this
|
package/package.json
CHANGED
|
@@ -13,95 +13,95 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// Auth constants (defined inline to avoid import issues during Nitro bundling)
|
|
16
|
-
const E2E_TOKEN_COOKIE_NAME =
|
|
17
|
-
const APP_TOKEN_PREFIX =
|
|
18
|
-
const KINDE_ID_TOKEN_COOKIE_NAME =
|
|
16
|
+
const E2E_TOKEN_COOKIE_NAME = 'kinde_token'
|
|
17
|
+
const APP_TOKEN_PREFIX = 'app_'
|
|
18
|
+
const KINDE_ID_TOKEN_COOKIE_NAME = 'id_token'
|
|
19
19
|
|
|
20
20
|
export default defineEventHandler(async (event) => {
|
|
21
|
-
const config = useRuntimeConfig()
|
|
21
|
+
const config = useRuntimeConfig()
|
|
22
22
|
|
|
23
23
|
// Get the path (remove /api/symfony prefix)
|
|
24
|
-
let path
|
|
25
|
-
event.context.params?.path || event.path.replace(
|
|
24
|
+
let path
|
|
25
|
+
= event.context.params?.path || event.path.replace('/api/symfony', '')
|
|
26
26
|
|
|
27
27
|
// Ensure path starts with / (for catch-all routes it might not)
|
|
28
|
-
if (!path.startsWith(
|
|
29
|
-
path = `/${path}
|
|
28
|
+
if (!path.startsWith('/')) {
|
|
29
|
+
path = `/${path}`
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// Check if this is a public API route (no auth required)
|
|
33
|
-
const publicApiRoutes
|
|
34
|
-
config.public.kindeAuth?.middleware?.publicApiRoutes || []
|
|
33
|
+
const publicApiRoutes
|
|
34
|
+
= config.public.kindeAuth?.middleware?.publicApiRoutes || []
|
|
35
35
|
const isPublicRoute = publicApiRoutes.some((route: string) => {
|
|
36
|
-
if (route.endsWith(
|
|
37
|
-
const prefix = route.slice(0, -3)
|
|
38
|
-
return path.startsWith(prefix)
|
|
36
|
+
if (route.endsWith('/**')) {
|
|
37
|
+
const prefix = route.slice(0, -3)
|
|
38
|
+
return path.startsWith(prefix)
|
|
39
39
|
}
|
|
40
|
-
return path === route
|
|
41
|
-
})
|
|
40
|
+
return path === route
|
|
41
|
+
})
|
|
42
42
|
|
|
43
|
-
let token: string | undefined
|
|
43
|
+
let token: string | undefined
|
|
44
44
|
|
|
45
45
|
// Skip authentication for public routes
|
|
46
46
|
if (isPublicRoute) {
|
|
47
|
-
console.log(
|
|
47
|
+
console.log('🔓 [SYMFONY PROXY] Public route, skipping auth:', path)
|
|
48
48
|
// Set token to empty to skip auth headers
|
|
49
|
-
token =
|
|
49
|
+
token = ''
|
|
50
50
|
} else {
|
|
51
51
|
// Check for E2E test token first (from cookie)
|
|
52
52
|
// Only use E2E token if it's a valid app token (starts with APP_TOKEN_PREFIX)
|
|
53
|
-
const e2eToken = getCookie(event, E2E_TOKEN_COOKIE_NAME)
|
|
53
|
+
const e2eToken = getCookie(event, E2E_TOKEN_COOKIE_NAME)
|
|
54
54
|
if (e2eToken && e2eToken.startsWith(APP_TOKEN_PREFIX)) {
|
|
55
|
-
token = e2eToken
|
|
55
|
+
token = e2eToken
|
|
56
56
|
} else {
|
|
57
57
|
// Use Kinde authentication from the module
|
|
58
|
-
const kinde = event.context.kinde
|
|
58
|
+
const kinde = event.context.kinde
|
|
59
59
|
|
|
60
60
|
if (!kinde?.client || !kinde?.sessionManager) {
|
|
61
61
|
throw createError({
|
|
62
62
|
statusCode: 500,
|
|
63
63
|
statusMessage:
|
|
64
|
-
|
|
65
|
-
})
|
|
64
|
+
'Kinde authentication not initialized. Module may not be loaded correctly.'
|
|
65
|
+
})
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const { client, sessionManager } = kinde
|
|
68
|
+
const { client, sessionManager } = kinde
|
|
69
69
|
|
|
70
70
|
try {
|
|
71
71
|
// Try to get access token first
|
|
72
|
-
let accessToken: string | null = null
|
|
72
|
+
let accessToken: string | null = null
|
|
73
73
|
try {
|
|
74
|
-
accessToken = await client.getToken(sessionManager)
|
|
74
|
+
accessToken = await client.getToken(sessionManager)
|
|
75
75
|
} catch {
|
|
76
76
|
// Silent - will try id_token fallback
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// If access token is not available, try id_token as fallback
|
|
80
|
-
if (!accessToken || accessToken.trim() ===
|
|
80
|
+
if (!accessToken || accessToken.trim() === '') {
|
|
81
81
|
const idToken = (await sessionManager.getSessionItem(
|
|
82
82
|
KINDE_ID_TOKEN_COOKIE_NAME
|
|
83
|
-
)) as string | undefined
|
|
83
|
+
)) as string | undefined
|
|
84
84
|
|
|
85
85
|
if (idToken) {
|
|
86
|
-
token = idToken
|
|
86
|
+
token = idToken
|
|
87
87
|
}
|
|
88
88
|
} else {
|
|
89
|
-
token = accessToken
|
|
89
|
+
token = accessToken
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
if (!token || token.trim() ===
|
|
92
|
+
if (!token || token.trim() === '') {
|
|
93
93
|
throw createError({
|
|
94
94
|
statusCode: 401,
|
|
95
|
-
statusMessage:
|
|
96
|
-
})
|
|
95
|
+
statusMessage: 'Unauthorized - Please log in'
|
|
96
|
+
})
|
|
97
97
|
}
|
|
98
98
|
} catch (error) {
|
|
99
|
-
console.error(
|
|
99
|
+
console.error('❌ [SYMFONY PROXY] Auth error:', error)
|
|
100
100
|
throw createError({
|
|
101
101
|
statusCode: 401,
|
|
102
102
|
statusMessage:
|
|
103
|
-
error instanceof Error ? error.message :
|
|
104
|
-
})
|
|
103
|
+
error instanceof Error ? error.message : 'Authentication failed'
|
|
104
|
+
})
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -109,53 +109,66 @@ export default defineEventHandler(async (event) => {
|
|
|
109
109
|
if (!token && !isPublicRoute) {
|
|
110
110
|
throw createError({
|
|
111
111
|
statusCode: 401,
|
|
112
|
-
statusMessage:
|
|
113
|
-
})
|
|
112
|
+
statusMessage: 'No authentication token available'
|
|
113
|
+
})
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
try {
|
|
117
|
-
// Get request method
|
|
118
|
-
const method = event.method
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
// Get request method
|
|
118
|
+
const method = event.method
|
|
119
|
+
|
|
120
|
+
// Get Content-Type to determine how to handle body
|
|
121
|
+
const contentType = getHeader(event, 'content-type') || ''
|
|
122
|
+
|
|
123
|
+
let body: string | undefined
|
|
124
|
+
|
|
125
|
+
// Handle multipart/form-data specially (preserve binary data and MIME types)
|
|
126
|
+
if (contentType.includes('multipart/form-data')) {
|
|
127
|
+
// For multipart/form-data, read the raw body without parsing
|
|
128
|
+
// This preserves the boundary and binary data including MIME types
|
|
129
|
+
body = await readRawBody(event, false)
|
|
130
|
+
} else if (method !== 'GET' && method !== 'HEAD') {
|
|
131
|
+
// For other content types (JSON, etc.), read and parse the body
|
|
132
|
+
body = await readBody(event)
|
|
133
|
+
}
|
|
121
134
|
|
|
122
135
|
// Get query parameters from original request
|
|
123
|
-
const query = getQuery(event)
|
|
136
|
+
const query = getQuery(event)
|
|
124
137
|
|
|
125
138
|
// Prepare headers for Symfony
|
|
126
139
|
// IMPORTANT: Forward Content-Type and Accept headers for proper API negotiation
|
|
127
|
-
const headers: Record<string, string> = {}
|
|
140
|
+
const headers: Record<string, string> = {}
|
|
128
141
|
|
|
129
142
|
// Only add Authorization header if we have a token (not public route)
|
|
130
|
-
if (token && token !==
|
|
131
|
-
headers.Authorization = `Bearer kinde_${token}
|
|
143
|
+
if (token && token !== '') {
|
|
144
|
+
headers.Authorization = `Bearer kinde_${token}`
|
|
132
145
|
}
|
|
133
146
|
|
|
134
|
-
// Forward Content-Type header
|
|
135
|
-
const contentType = getHeader(event, "content-type");
|
|
147
|
+
// Forward Content-Type header (CRITICAL for multipart/form-data with boundary)
|
|
136
148
|
if (contentType) {
|
|
137
|
-
headers[
|
|
149
|
+
headers['Content-Type'] = contentType
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
// Forward Accept header (CRITICAL for content negotiation)
|
|
141
153
|
// Without this, backend returns default format instead of requested format (e.g., JSON vs Hydra)
|
|
142
|
-
const accept = getHeader(event,
|
|
154
|
+
const accept = getHeader(event, 'accept')
|
|
143
155
|
if (accept) {
|
|
144
|
-
headers[
|
|
156
|
+
headers['Accept'] = accept
|
|
145
157
|
}
|
|
146
158
|
|
|
147
159
|
// Log the request to backend
|
|
148
|
-
const backendUrl = `${config.apiBaseUrl}${path}
|
|
149
|
-
console.log(
|
|
160
|
+
const backendUrl = `${config.apiBaseUrl}${path}`
|
|
161
|
+
console.log('🔵 [SYMFONY PROXY] Request to backend:', {
|
|
150
162
|
url: backendUrl,
|
|
151
163
|
method,
|
|
152
164
|
headers: {
|
|
153
165
|
...headers,
|
|
154
|
-
Authorization: `Bearer kinde_${token.substring(0, 10)}
|
|
166
|
+
Authorization: token && token !== '' ? `Bearer kinde_${token.substring(0, 10)}...` : 'none'
|
|
155
167
|
},
|
|
156
168
|
query,
|
|
157
169
|
hasBody: !!body,
|
|
158
|
-
|
|
170
|
+
isMultipart: contentType.includes('multipart/form-data')
|
|
171
|
+
})
|
|
159
172
|
|
|
160
173
|
// Forward request to Symfony with Kinde token
|
|
161
174
|
const response = await $fetch(path, {
|
|
@@ -163,46 +176,47 @@ export default defineEventHandler(async (event) => {
|
|
|
163
176
|
method,
|
|
164
177
|
headers,
|
|
165
178
|
body,
|
|
166
|
-
retry: false, // Disable automatic retries
|
|
167
179
|
query,
|
|
168
|
-
|
|
180
|
+
retry: false, // Disable automatic retries
|
|
181
|
+
timeout: 30000 // 30 second timeout
|
|
182
|
+
})
|
|
169
183
|
|
|
170
|
-
console.log(
|
|
184
|
+
console.log('✅ [SYMFONY PROXY] Backend response received:', {
|
|
171
185
|
url: backendUrl,
|
|
172
186
|
method,
|
|
173
|
-
status:
|
|
174
|
-
})
|
|
187
|
+
status: 'success'
|
|
188
|
+
})
|
|
175
189
|
|
|
176
|
-
return response
|
|
190
|
+
return response
|
|
177
191
|
} catch (error) {
|
|
178
|
-
console.error(
|
|
192
|
+
console.error('❌ [SYMFONY PROXY] Symfony API error:', {
|
|
179
193
|
path,
|
|
180
194
|
statusCode:
|
|
181
|
-
error && typeof error ===
|
|
195
|
+
error && typeof error === 'object' && 'statusCode' in error
|
|
182
196
|
? error.statusCode
|
|
183
|
-
:
|
|
184
|
-
message: error instanceof Error ? error.message :
|
|
185
|
-
})
|
|
197
|
+
: 'unknown',
|
|
198
|
+
message: error instanceof Error ? error.message : 'unknown'
|
|
199
|
+
})
|
|
186
200
|
// Handle Symfony API errors
|
|
187
|
-
const statusCode
|
|
188
|
-
error && typeof error ===
|
|
201
|
+
const statusCode
|
|
202
|
+
= error && typeof error === 'object' && 'statusCode' in error
|
|
189
203
|
? (error.statusCode as number)
|
|
190
|
-
: 500
|
|
191
|
-
const statusMessage
|
|
192
|
-
error && typeof error ===
|
|
204
|
+
: 500
|
|
205
|
+
const statusMessage
|
|
206
|
+
= error && typeof error === 'object' && 'statusMessage' in error
|
|
193
207
|
? (error.statusMessage as string)
|
|
194
208
|
: error instanceof Error
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const data
|
|
198
|
-
error && typeof error ===
|
|
209
|
+
? error.message
|
|
210
|
+
: 'Symfony API error'
|
|
211
|
+
const data
|
|
212
|
+
= error && typeof error === 'object' && 'data' in error
|
|
199
213
|
? error.data
|
|
200
|
-
: undefined
|
|
214
|
+
: undefined
|
|
201
215
|
|
|
202
216
|
throw createError({
|
|
203
217
|
statusCode,
|
|
204
218
|
statusMessage,
|
|
205
|
-
data
|
|
206
|
-
})
|
|
219
|
+
data
|
|
220
|
+
})
|
|
207
221
|
}
|
|
208
|
-
})
|
|
222
|
+
})
|