fix(admin-web): proxy routes + pages use cookie-based auth
- Add getCurrentUserFromRequest() to auth-server.ts (checks cookies first, then Authorization header)
- Update all 34 proxy routes: getCurrentUser(req.headers.get('authorization')) → getCurrentUserFromRequest(req)
- Add createProxyFetch() shared helper in lib/proxy-fetch.ts (injects auth + product-id headers)
- Update 15 admin pages: replace inline fetch helpers with createProxyFetch
- Root cause: newer pages used bare fetch() without Authorization headers, causing 401s on all proxy routes
This commit is contained in:
parent
f9fa583cae
commit
ce0074d6ee
@ -1,9 +1,9 @@
|
||||
Last refresh: 2026-03-21T06:00:10Z (2026-03-20 23:00:10 PDT)
|
||||
Cascade conversations: 50 (411M)
|
||||
Memories: 91
|
||||
Last refresh: 2026-03-21T21:44:43Z (2026-03-21 14:44:44 PDT)
|
||||
Cascade conversations: 50 (387M)
|
||||
Memories: 95
|
||||
Implicit context: 20
|
||||
Code tracker dirs: 132
|
||||
File edit history: 3365 entries
|
||||
Workspace storage: 35 workspaces
|
||||
Code tracker dirs: 89
|
||||
File edit history: 3457 entries
|
||||
Workspace storage: 36 workspaces
|
||||
Repo docs: 7 files across 2 repos
|
||||
Repo workflows: 43 files across 10 repos
|
||||
|
||||
@ -5,7 +5,6 @@ Product-specific workflows for ChronoMind AI-powered contextual clock.
|
||||
## Repo-management Workflows
|
||||
|
||||
Cross-repo workflows are maintained centrally in `learning_ai_common_plat/.windsurf/workflows/`:
|
||||
|
||||
- `/repo_sync-repos` — Pull latest from all repos
|
||||
- `/repo_commit-workspace` — Commit changes across repos
|
||||
- `/repo_backup-main-branch` — Backup main branches
|
||||
|
||||
@ -9,16 +9,16 @@ Auto-discovers new repos, updates symlinks, and re-copies docs + workflows.
|
||||
|
||||
## Covered Repos (All 8 workspaces)
|
||||
|
||||
| Repo | Product | Workflows | Docs |
|
||||
| ----------------------------------- | ---------------- | --------- | ---- |
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
| Repo | Product | Workflows | Docs |
|
||||
|------|---------|-----------|------|
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
|
||||
## Steps
|
||||
|
||||
|
||||
@ -8,16 +8,16 @@ Regenerates all 8 AI agent configuration files across all repos in the workspace
|
||||
|
||||
## Files Generated Per Repo
|
||||
|
||||
| File | Tool |
|
||||
| --------------------------------- | ----------------------------------------------- |
|
||||
| `AGENTS.md` | Universal (OpenAI Codex, Claude, Copilot, etc.) |
|
||||
| `CLAUDE.md` | Claude Code |
|
||||
| `.cursorrules` | Cursor AI |
|
||||
| `.github/copilot-instructions.md` | GitHub Copilot |
|
||||
| `.windsurfrules` | Windsurf / Cascade |
|
||||
| `.clinerules` | Cline / Roo Code |
|
||||
| `.aider.conf.yml` | Aider |
|
||||
| `.editorconfig` | All editors |
|
||||
| File | Tool |
|
||||
|------|------|
|
||||
| `AGENTS.md` | Universal (OpenAI Codex, Claude, Copilot, etc.) |
|
||||
| `CLAUDE.md` | Claude Code |
|
||||
| `.cursorrules` | Cursor AI |
|
||||
| `.github/copilot-instructions.md` | GitHub Copilot |
|
||||
| `.windsurfrules` | Windsurf / Cascade |
|
||||
| `.clinerules` | Cline / Roo Code |
|
||||
| `.aider.conf.yml` | Aider |
|
||||
| `.editorconfig` | All editors |
|
||||
|
||||
## Steps
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ Product-specific workflows for NomGap fasting visualization app.
|
||||
## Repo-management Workflows
|
||||
|
||||
Cross-repo workflows are maintained centrally in `learning_ai_common_plat/.windsurf/workflows/`:
|
||||
|
||||
- `/repo_sync-repos` — Pull latest from all repos
|
||||
- `/repo_commit-workspace` — Commit changes across repos
|
||||
- `/repo_backup-main-branch` — Backup main branches
|
||||
|
||||
@ -9,16 +9,16 @@ Auto-discovers new repos, updates symlinks, and re-copies docs + workflows.
|
||||
|
||||
## Covered Repos (All 8 workspaces)
|
||||
|
||||
| Repo | Product | Workflows | Docs |
|
||||
| ----------------------------------- | ---------------- | --------- | ---- |
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
| Repo | Product | Workflows | Docs |
|
||||
|------|---------|-----------|------|
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
|
||||
## Steps
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ Product-specific workflows for FlowMonk, the agent-first planning and execution
|
||||
## Repo-management Workflows
|
||||
|
||||
Cross-repo workflows are maintained centrally in `learning_ai_common_plat/.windsurf/workflows/`:
|
||||
|
||||
- `/repo_sync-repos` — Pull latest from all repos
|
||||
- `/repo_commit-workspace` — Commit changes across repos
|
||||
- `/repo_backup-main-branch` — Backup main branches
|
||||
|
||||
@ -5,7 +5,6 @@ Product-specific workflows for JarvisJr voice coaching app.
|
||||
## Repo-management Workflows
|
||||
|
||||
Cross-repo workflows are maintained centrally in `learning_ai_common_plat/.windsurf/workflows/`:
|
||||
|
||||
- `/repo_sync-repos` — Pull latest from all repos
|
||||
- `/repo_commit-workspace` — Commit changes across repos
|
||||
- `/repo_backup-main-branch` — Backup main branches
|
||||
|
||||
@ -9,16 +9,16 @@ Auto-discovers new repos, updates symlinks, and re-copies docs + workflows.
|
||||
|
||||
## Covered Repos (All 8 workspaces)
|
||||
|
||||
| Repo | Product | Workflows | Docs |
|
||||
| ----------------------------------- | ---------------- | --------- | ---- |
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
| Repo | Product | Workflows | Docs |
|
||||
|------|---------|-----------|------|
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
|
||||
## Steps
|
||||
|
||||
|
||||
@ -9,17 +9,14 @@ The primary interface for running audits is the **Sensor Audit Dashboard**.
|
||||
## Steps
|
||||
|
||||
1. **Ensure the dashboard is running**
|
||||
|
||||
```bash
|
||||
/Users/sd9235/code/mygh/learning_ai_mac_tooling/audit-ctl.sh start
|
||||
```
|
||||
|
||||
2. **Open the dashboard**
|
||||
|
||||
```bash
|
||||
/Users/sd9235/code/mygh/learning_ai_mac_tooling/audit-ctl.sh open
|
||||
```
|
||||
|
||||
Navigate to **Run Audit** in the sidebar.
|
||||
|
||||
3. **Choose a mode in the dashboard UI**:
|
||||
|
||||
@ -5,7 +5,6 @@ Product-specific workflows for PeakPulse sensor-driven adventure tracker.
|
||||
## Repo-management Workflows
|
||||
|
||||
Cross-repo workflows are maintained centrally in `learning_ai_common_plat/.windsurf/workflows/`:
|
||||
|
||||
- `/repo_sync-repos` — Pull latest from all repos
|
||||
- `/repo_commit-workspace` — Commit changes across repos
|
||||
- `/repo_backup-main-branch` — Backup main branches
|
||||
|
||||
@ -19,12 +19,12 @@ cd /Users/sd9235/code/mygh/learning_ai_peakpulse/ios && xcodegen generate
|
||||
|
||||
3. Build all iOS targets:
|
||||
```bash
|
||||
cd /Users/sd9235/code/mygh/learning_ai_peakpulse/ios && xcodebuild -project PeakPulse.xcodeproj -scheme PeakPulse -destination 'platform=iOS Simulator,name=iPhone 16' -quiet build
|
||||
cd /Users/sd9235/code/mygh/learning_ai_peakpulse/ios && xcodebuild -project PeakPulse.xcodeproj -scheme PeakPulse -destination 'platform=iOS Simulator,name=iPhone 17' -quiet build
|
||||
```
|
||||
|
||||
4. Run unit tests:
|
||||
```bash
|
||||
cd /Users/sd9235/code/mygh/learning_ai_peakpulse/ios && xcodebuild -project PeakPulse.xcodeproj -scheme PeakPulse -destination 'platform=iOS Simulator,name=iPhone 16' -quiet test
|
||||
cd /Users/sd9235/code/mygh/learning_ai_peakpulse/ios && xcodebuild -project PeakPulse.xcodeproj -scheme PeakPulse -destination 'platform=iOS Simulator,name=iPhone 17' -quiet test
|
||||
```
|
||||
|
||||
5. Verify no print() statements in production code:
|
||||
@ -51,9 +51,9 @@ cat /Users/sd9235/code/mygh/learning_ai_peakpulse/ios/PeakPulse/PeakPulse.entitl
|
||||
cat /Users/sd9235/code/mygh/learning_ai_peakpulse/.env.example
|
||||
```
|
||||
|
||||
9. Run platform-service peak-sessions tests:
|
||||
9. Run backend tests:
|
||||
```bash
|
||||
cd /Users/sd9235/code/mygh/learning_ai_common_plat && npx vitest run services/platform-service/src/modules/peak-sessions/peak-sessions.test.ts
|
||||
cd /Users/sd9235/code/mygh/learning_ai_peakpulse/backend && npm test
|
||||
```
|
||||
|
||||
10. Verify CI workflow exists and is valid:
|
||||
|
||||
@ -9,16 +9,16 @@ Auto-discovers new repos, updates symlinks, and re-copies docs + workflows.
|
||||
|
||||
## Covered Repos (All 8 workspaces)
|
||||
|
||||
| Repo | Product | Workflows | Docs |
|
||||
| ----------------------------------- | ---------------- | --------- | ---- |
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
| Repo | Product | Workflows | Docs |
|
||||
|------|---------|-----------|------|
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
|
||||
## Steps
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ Product-specific workflows for MindLyst role-based life OS.
|
||||
## Repo-management Workflows
|
||||
|
||||
Cross-repo workflows are maintained centrally in `learning_ai_common_plat/.windsurf/workflows/`:
|
||||
|
||||
- `/repo_sync-repos` — Pull latest from all repos
|
||||
- `/repo_commit-workspace` — Commit changes across repos
|
||||
- `/repo_backup-main-branch` — Backup main branches
|
||||
|
||||
@ -9,16 +9,16 @@ Auto-discovers new repos, updates symlinks, and re-copies docs + workflows.
|
||||
|
||||
## Covered Repos (All 8 workspaces)
|
||||
|
||||
| Repo | Product | Workflows | Docs |
|
||||
| ----------------------------------- | ---------------- | --------- | ---- |
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
| Repo | Product | Workflows | Docs |
|
||||
|------|---------|-----------|------|
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
|
||||
## Steps
|
||||
|
||||
|
||||
@ -49,7 +49,6 @@ See `iosApp/README_SETUP.md` for more details.
|
||||
### 4. Verify Gradle/KMP builds
|
||||
|
||||
// turbo
|
||||
|
||||
```bash
|
||||
cd mindlyst-native && ./gradlew :shared:compileKotlinIosSimulatorArm64
|
||||
```
|
||||
@ -67,7 +66,6 @@ cd mindlyst-native && ./gradlew :shared:compileKotlinIosArm64
|
||||
```
|
||||
|
||||
For simulator testing:
|
||||
|
||||
```bash
|
||||
cd mindlyst-native && ./gradlew :shared:compileKotlinIosSimulatorArm64
|
||||
```
|
||||
@ -75,7 +73,6 @@ cd mindlyst-native && ./gradlew :shared:compileKotlinIosSimulatorArm64
|
||||
### 2. Embed the shared framework in Xcode
|
||||
|
||||
If not already linked:
|
||||
|
||||
```bash
|
||||
cd mindlyst-native && ./gradlew :shared:embedAndSignAppleFrameworkForXcode
|
||||
```
|
||||
@ -90,7 +87,6 @@ Or manually: open Xcode → Target → General → Build.
|
||||
### 4. Clean build folder
|
||||
|
||||
// turbo
|
||||
|
||||
```bash
|
||||
xcodebuild clean -project mindlyst-native/MindLyst.xcodeproj -scheme MindLyst -configuration Release 2>&1 | tail -3
|
||||
```
|
||||
@ -144,24 +140,24 @@ The `app-store-connect` export method auto-uploads the IPA.
|
||||
|
||||
### Key Paths
|
||||
|
||||
| Path | Purpose |
|
||||
| ------------------------------------------- | ---------------------------------------- |
|
||||
| `mindlyst-native/iosApp/` | Swift UI source files |
|
||||
| `mindlyst-native/shared/` | KMP shared module (business logic) |
|
||||
| `mindlyst-native/shared/build.gradle.kts` | KMP build config (iOS targets) |
|
||||
| `mindlyst-native/gradle/libs.versions.toml` | Version catalog |
|
||||
| `mindlyst-native/MindLyst.xcodeproj/` | Xcode project (must be created manually) |
|
||||
| `scripts/MindLystExportOptions.plist` | Export options for TestFlight upload |
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `mindlyst-native/iosApp/` | Swift UI source files |
|
||||
| `mindlyst-native/shared/` | KMP shared module (business logic) |
|
||||
| `mindlyst-native/shared/build.gradle.kts` | KMP build config (iOS targets) |
|
||||
| `mindlyst-native/gradle/libs.versions.toml` | Version catalog |
|
||||
| `mindlyst-native/MindLyst.xcodeproj/` | Xcode project (must be created manually) |
|
||||
| `scripts/MindLystExportOptions.plist` | Export options for TestFlight upload |
|
||||
|
||||
### Build Identity
|
||||
|
||||
| Field | Value |
|
||||
| ------------- | ----------------------- |
|
||||
| Team ID | `748N7QPX7J` |
|
||||
| Bundle ID | `com.mindlyst.MindLyst` |
|
||||
| Signing | Automatic |
|
||||
| KMP Framework | `shared` (static) |
|
||||
| Min iOS | 16.0 |
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Team ID | `748N7QPX7J` |
|
||||
| Bundle ID | `com.mindlyst.MindLyst` |
|
||||
| Signing | Automatic |
|
||||
| KMP Framework | `shared` (static) |
|
||||
| Min iOS | 16.0 |
|
||||
|
||||
### KMP Architecture
|
||||
|
||||
@ -177,12 +173,12 @@ All business logic lives in `shared/src/commonMain/`. iOS code in `iosApp/` is a
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
| Problem | Fix |
|
||||
| ----------------------------- | ------------------------------------------------------------------------ |
|
||||
| "No signing certificate" | Xcode → Settings → Accounts → Manage Certificates → + Apple Distribution |
|
||||
| "Provisioning profile" error | Xcode → Target → Signing → Enable "Automatically manage signing" |
|
||||
| "Build number already exists" | Increment build number in step 3 |
|
||||
| KMP build fails | Check Java 17: `java -version`. Install: `brew install openjdk@17` |
|
||||
| "shared.framework not found" | Run `./gradlew :shared:embedAndSignAppleFrameworkForXcode` |
|
||||
| Gradle SSL proxy error | Build on home network (corporate proxy blocks Gradle repos) |
|
||||
| Processing >30 min | Check [Apple system status](https://developer.apple.com/system-status/) |
|
||||
| Problem | Fix |
|
||||
|---------|-----|
|
||||
| "No signing certificate" | Xcode → Settings → Accounts → Manage Certificates → + Apple Distribution |
|
||||
| "Provisioning profile" error | Xcode → Target → Signing → Enable "Automatically manage signing" |
|
||||
| "Build number already exists" | Increment build number in step 3 |
|
||||
| KMP build fails | Check Java 17: `java -version`. Install: `brew install openjdk@17` |
|
||||
| "shared.framework not found" | Run `./gradlew :shared:embedAndSignAppleFrameworkForXcode` |
|
||||
| Gradle SSL proxy error | Build on home network (corporate proxy blocks Gradle repos) |
|
||||
| Processing >30 min | Check [Apple system status](https://developer.apple.com/system-status/) |
|
||||
|
||||
@ -5,7 +5,6 @@ Product-specific workflows for LysnrAI voice-to-text dictation platform.
|
||||
## Repo-management Workflows
|
||||
|
||||
Cross-repo workflows are maintained centrally in `learning_ai_common_plat/.windsurf/workflows/`:
|
||||
|
||||
- `/repo_sync-repos` — Pull latest from all repos
|
||||
- `/repo_commit-workspace` — Commit changes across repos
|
||||
- `/repo_backup-main-branch` — Backup main branches
|
||||
|
||||
@ -9,16 +9,16 @@ Auto-discovers new repos, updates symlinks, and re-copies docs + workflows.
|
||||
|
||||
## Covered Repos (All 8 workspaces)
|
||||
|
||||
| Repo | Product | Workflows | Docs |
|
||||
| ----------------------------------- | ---------------- | --------- | ---- |
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
| Repo | Product | Workflows | Docs |
|
||||
|------|---------|-----------|------|
|
||||
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
|
||||
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
|
||||
| `learning_ai_clock` | ChronoMind | ✅ | — |
|
||||
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
|
||||
| `learning_ai_fastgap` | NomGap | ✅ | — |
|
||||
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
|
||||
| `learning_ai_common_plat` | Common Platform | ✅ | — |
|
||||
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
|
||||
|
||||
## Steps
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
FlaskConical,
|
||||
@ -67,13 +69,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/agent-evals/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/agent-evals');
|
||||
|
||||
export default function AgentEvalsPage() {
|
||||
const [suites, setSuites] = useState<EvalSuite[]>([]);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Coins, Plus, MoreHorizontal, AlertTriangle, TrendingUp, Trash2 } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -73,13 +75,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/ai-budgets/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/ai-budgets');
|
||||
|
||||
export default function AIBudgetsPage() {
|
||||
const [policies, setPolicies] = useState<BudgetPolicy[]>([]);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Mail, RotateCcw } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -49,13 +51,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/delivery/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/delivery');
|
||||
|
||||
export default function DeliveryPage() {
|
||||
const [entries, setEntries] = useState<DeliveryEntry[]>([]);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Radio, Plus, MoreHorizontal, Trash2 } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -47,13 +49,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/event-subscriptions/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/event-subscriptions');
|
||||
|
||||
export default function EventSubscriptionsPage() {
|
||||
const [subs, setSubs] = useState<EventSub[]>([]);
|
||||
@ -65,7 +61,7 @@ export default function EventSubscriptionsPage() {
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
const data = await apiFetch('list');
|
||||
const data = await apiFetch('');
|
||||
setSubs(
|
||||
Array.isArray(data?.subscriptions) ? data.subscriptions : Array.isArray(data) ? data : []
|
||||
);
|
||||
@ -78,7 +74,7 @@ export default function EventSubscriptionsPage() {
|
||||
|
||||
async function handleCreate() {
|
||||
setCreating(true);
|
||||
await apiFetch('create', {
|
||||
await apiFetch('', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ eventType: newEvent, callbackUrl: newUrl }),
|
||||
});
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { ShieldBan, Plus, Trash2 } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -48,13 +50,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/ip-rules/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/ip-rules');
|
||||
|
||||
export default function IpRulesPage() {
|
||||
const [rules, setRules] = useState<IpRule[]>([]);
|
||||
@ -67,7 +63,7 @@ export default function IpRulesPage() {
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
const data = await apiFetch('list');
|
||||
const data = await apiFetch('');
|
||||
setRules(Array.isArray(data?.rules) ? data.rules : Array.isArray(data) ? data : []);
|
||||
setLoading(false);
|
||||
}, []);
|
||||
@ -78,7 +74,7 @@ export default function IpRulesPage() {
|
||||
|
||||
async function handleCreate() {
|
||||
setCreating(true);
|
||||
await apiFetch('create', {
|
||||
await apiFetch('', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ cidr: newCidr, type: newType, reason: newReason || undefined }),
|
||||
});
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
interface Job {
|
||||
id: string;
|
||||
@ -58,21 +59,8 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function jobsFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/jobs/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function runsFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/runs/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const jobsFetch = createProxyFetch('/api/jobs');
|
||||
const runsFetch = createProxyFetch('/api/runs');
|
||||
|
||||
export default function JobsPage() {
|
||||
const [jobs, setJobs] = useState<Job[]>([]);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { BookOpen, Plus, MoreHorizontal, Search, FileText, Trash2, Upload } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -49,13 +51,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/knowledge/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/knowledge');
|
||||
|
||||
export default function KnowledgePage() {
|
||||
const [bases, setBases] = useState<KnowledgeBase[]>([]);
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
interface MaintenanceStatus {
|
||||
mode: string;
|
||||
@ -47,13 +48,7 @@ const modeConfig: Record<string, { label: string; color: string; description: st
|
||||
},
|
||||
};
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/maintenance/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/maintenance');
|
||||
|
||||
export default function MaintenancePage() {
|
||||
const [status, setStatus] = useState<MaintenanceStatus>({ mode: 'off' });
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Store, MoreHorizontal, Search, ThumbsUp, ThumbsDown } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -49,13 +51,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/marketplace/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/marketplace');
|
||||
|
||||
export default function MarketplacePage() {
|
||||
const [listings, setListings] = useState<Listing[]>([]);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Building2, Plus, MoreHorizontal, Users, FolderOpen, Search, Trash2 } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -71,13 +73,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/orgs/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/orgs');
|
||||
|
||||
export default function OrganizationsPage() {
|
||||
const [orgs, setOrgs] = useState<Org[]>([]);
|
||||
@ -98,7 +94,7 @@ export default function OrganizationsPage() {
|
||||
const loadOrgs = useCallback(async () => {
|
||||
setLoading(true);
|
||||
const qs = search ? `?search=${encodeURIComponent(search)}` : '';
|
||||
const data = await apiFetch(`list${qs}`);
|
||||
const data = await apiFetch(qs);
|
||||
if (data?.organizations) {
|
||||
setOrgs(data.organizations);
|
||||
setTotal(data.total ?? data.organizations.length);
|
||||
@ -115,7 +111,7 @@ export default function OrganizationsPage() {
|
||||
|
||||
async function handleCreate() {
|
||||
setCreating(true);
|
||||
await apiFetch('create', {
|
||||
await apiFetch('', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ name: newName, slug: newSlug || undefined }),
|
||||
});
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Star, ThumbsUp, ThumbsDown, MoreHorizontal, Flag } from 'lucide-react';
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@ -48,13 +49,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/reviews/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/reviews');
|
||||
|
||||
export default function ReviewsPage() {
|
||||
const [reviews, setReviews] = useState<Review[]>([]);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { MonitorSmartphone, Search, Globe } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -35,13 +37,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/sessions/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/sessions');
|
||||
|
||||
export default function SessionsAdminPage() {
|
||||
const [sessions, setSessions] = useState<SessionEntry[]>([]);
|
||||
@ -51,7 +47,7 @@ export default function SessionsAdminPage() {
|
||||
const loadData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
const qs = search ? `?search=${encodeURIComponent(search)}` : '';
|
||||
const data = await apiFetch(`list${qs}`);
|
||||
const data = await apiFetch(qs);
|
||||
setSessions(Array.isArray(data?.sessions) ? data.sessions : Array.isArray(data) ? data : []);
|
||||
setLoading(false);
|
||||
}, [search]);
|
||||
@ -62,7 +58,7 @@ export default function SessionsAdminPage() {
|
||||
|
||||
async function handleRevoke(id: string) {
|
||||
if (!confirm('Revoke this session?')) return;
|
||||
await apiFetch(`${id}/revoke`, { method: 'POST' });
|
||||
await apiFetch(`${id}`, { method: 'DELETE' });
|
||||
loadData();
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
LifeBuoy,
|
||||
@ -83,13 +85,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/support/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/support');
|
||||
|
||||
export default function SupportCasesPage() {
|
||||
const [cases, setCases] = useState<SupportCase[]>([]);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
ListOrdered,
|
||||
@ -65,13 +67,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/waitlist/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/waitlist');
|
||||
|
||||
export default function WaitlistPage() {
|
||||
const [entries, setEntries] = useState<WaitlistEntry[]>([]);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Webhook, Plus, MoreHorizontal, RotateCcw, Trash2 } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@ -57,13 +59,7 @@ function formatDate(iso: string) {
|
||||
});
|
||||
}
|
||||
|
||||
async function apiFetch(path: string, opts?: RequestInit) {
|
||||
const res = await fetch(`/api/webhooks/${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...opts,
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
const apiFetch = createProxyFetch('/api/webhooks');
|
||||
|
||||
export default function WebhooksPage() {
|
||||
const [subs, setSubs] = useState<WebhookSub[]>([]);
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { logError } from '@/lib/logger';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import * as actiontrailClient from '@/lib/actiontrail-client';
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/agent-evals/${path.join('/')}`;
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { logError } from '@/lib/logger';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { getCollection } from '@/lib/datastore';
|
||||
import { getRequestProductId } from '@/lib/product-config';
|
||||
interface CohortRow {
|
||||
@ -37,7 +37,7 @@ function getMondayOfWeek(date: Date): string {
|
||||
}
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { getCollection } from '@/lib/datastore';
|
||||
import { getRequestProductId } from '@/lib/product-config';
|
||||
|
||||
@ -32,7 +32,7 @@ interface RevenueResponse {
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { logError } from '@/lib/logger';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import * as platformClient from '@/lib/platform-client';
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/delivery/${path.join('/')}`;
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/event-subscriptions/${path.join('/')}`;
|
||||
@ -38,6 +38,9 @@ export async function GET(req: NextRequest, ctx: { params: Promise<{ path: strin
|
||||
export async function POST(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) {
|
||||
return proxy(req, ctx);
|
||||
}
|
||||
export async function PATCH(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) {
|
||||
return proxy(req, ctx);
|
||||
}
|
||||
export async function DELETE(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) {
|
||||
return proxy(req, ctx);
|
||||
}
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Event Subscriptions base route — handles GET /api/event-subscriptions and POST /api/event-subscriptions.
|
||||
* The [...path] catch-all only matches sub-paths like /api/event-subscriptions/:id.
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxyBase(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const qs = new URL(req.url).search;
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-request-id': req.headers.get('x-request-id') || crypto.randomUUID(),
|
||||
'x-user-id': caller.id,
|
||||
'x-product-id': req.headers.get('x-product-id') || process.env.PRODUCT_ID || 'lysnrai',
|
||||
};
|
||||
const fetchOptions: RequestInit = { method: req.method, headers };
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') fetchOptions.body = await req.text();
|
||||
const res = await fetch(`${PLATFORM_URL}/api/event-subscriptions${qs}`, fetchOptions);
|
||||
const data = await res.json().catch(() => null);
|
||||
return NextResponse.json(data ?? { error: res.statusText }, { status: res.status });
|
||||
} catch (error) {
|
||||
logError('Event subscriptions base proxy error', error);
|
||||
return NextResponse.json({ error: 'Service unavailable' }, { status: 502 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
export async function POST(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/exports/${path.join('/')}`;
|
||||
|
||||
@ -5,18 +5,17 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const EXTRACTION_SERVICE_URL =
|
||||
process.env.EXTRACTION_SERVICE_URL || 'http://localhost:4005';
|
||||
const EXTRACTION_SERVICE_URL = process.env.EXTRACTION_SERVICE_URL || 'http://localhost:4005';
|
||||
|
||||
async function proxyToExtraction(
|
||||
req: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> },
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
@ -50,37 +49,22 @@ async function proxyToExtraction(
|
||||
});
|
||||
} catch (error) {
|
||||
logError('Extraction proxy error', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Extraction service unavailable' },
|
||||
{ status: 502 },
|
||||
);
|
||||
return NextResponse.json({ error: 'Extraction service unavailable' }, { status: 502 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
req: NextRequest,
|
||||
context: { params: Promise<{ path: string[] }> },
|
||||
) {
|
||||
export async function GET(req: NextRequest, context: { params: Promise<{ path: string[] }> }) {
|
||||
return proxyToExtraction(req, context);
|
||||
}
|
||||
|
||||
export async function POST(
|
||||
req: NextRequest,
|
||||
context: { params: Promise<{ path: string[] }> },
|
||||
) {
|
||||
export async function POST(req: NextRequest, context: { params: Promise<{ path: string[] }> }) {
|
||||
return proxyToExtraction(req, context);
|
||||
}
|
||||
|
||||
export async function PUT(
|
||||
req: NextRequest,
|
||||
context: { params: Promise<{ path: string[] }> },
|
||||
) {
|
||||
export async function PUT(req: NextRequest, context: { params: Promise<{ path: string[] }> }) {
|
||||
return proxyToExtraction(req, context);
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
req: NextRequest,
|
||||
context: { params: Promise<{ path: string[] }> },
|
||||
) {
|
||||
export async function DELETE(req: NextRequest, context: { params: Promise<{ path: string[] }> }) {
|
||||
return proxyToExtraction(req, context);
|
||||
}
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { logError } from '@/lib/logger';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import * as growthClient from '@/lib/growth-client';
|
||||
export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller || !['super_admin', 'admin'].includes(caller.role)) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
@ -40,7 +40,7 @@ export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id
|
||||
}
|
||||
export async function DELETE(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller || caller.role !== 'super_admin') {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { logError } from '@/lib/logger';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import * as growthClient from '@/lib/growth-client';
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller || !['super_admin', 'admin'].includes(caller.role)) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
@ -5,12 +5,12 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { logError } from '@/lib/logger';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import * as growthClient from '@/lib/growth-client';
|
||||
import crypto from 'crypto';
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
@ -31,7 +31,7 @@ export async function GET(req: NextRequest) {
|
||||
}
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller || !['super_admin', 'admin'].includes(caller.role)) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/ratelimit/ip-rules/${path.join('/')}`;
|
||||
|
||||
39
dashboards/admin-web/src/app/api/ip-rules/route.ts
Normal file
39
dashboards/admin-web/src/app/api/ip-rules/route.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* IP Rules base route — handles GET /api/ip-rules and POST /api/ip-rules.
|
||||
* Maps to platform-service GET /api/ratelimit/ip-rules and POST /api/ratelimit/ip-rules.
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxyBase(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const qs = new URL(req.url).search;
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-request-id': req.headers.get('x-request-id') || crypto.randomUUID(),
|
||||
'x-user-id': caller.id,
|
||||
'x-product-id': req.headers.get('x-product-id') || process.env.PRODUCT_ID || 'lysnrai',
|
||||
};
|
||||
const fetchOptions: RequestInit = { method: req.method, headers };
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') fetchOptions.body = await req.text();
|
||||
const res = await fetch(`${PLATFORM_URL}/api/ratelimit/ip-rules${qs}`, fetchOptions);
|
||||
const data = await res.json().catch(() => null);
|
||||
return NextResponse.json(data ?? { error: res.statusText }, { status: res.status });
|
||||
} catch (error) {
|
||||
logError('IP rules base proxy error', error);
|
||||
return NextResponse.json({ error: 'Service unavailable' }, { status: 502 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
export async function POST(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/jobs/${path.join('/')}`;
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/knowledge/${path.join('/')}`;
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/settings/maintenance/${path.join('/')}`;
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/marketplace/${path.join('/')}`;
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
39
dashboards/admin-web/src/app/api/orgs/route.ts
Normal file
39
dashboards/admin-web/src/app/api/orgs/route.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Orgs base route — handles GET /api/orgs and POST /api/orgs.
|
||||
* Maps to platform-service GET /api/orgs and POST /api/orgs.
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxyBase(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const qs = new URL(req.url).search;
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-request-id': req.headers.get('x-request-id') || crypto.randomUUID(),
|
||||
'x-user-id': caller.id,
|
||||
'x-product-id': req.headers.get('x-product-id') || process.env.PRODUCT_ID || 'lysnrai',
|
||||
};
|
||||
const fetchOptions: RequestInit = { method: req.method, headers };
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') fetchOptions.body = await req.text();
|
||||
const res = await fetch(`${PLATFORM_URL}/api/orgs${qs}`, fetchOptions);
|
||||
const data = await res.json().catch(() => null);
|
||||
return NextResponse.json(data ?? { error: res.statusText }, { status: res.status });
|
||||
} catch (error) {
|
||||
logError('Orgs base proxy error', error);
|
||||
return NextResponse.json({ error: 'Service unavailable' }, { status: 502 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
export async function POST(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { deactivatePromo } from '@/lib/growth-client';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
@ -14,7 +14,7 @@ type RouteContext = { params: Promise<{ id: string }> };
|
||||
|
||||
export async function DELETE(req: NextRequest, ctx: RouteContext) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller || !['super_admin', 'admin'].includes(caller.role)) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
@ -29,7 +29,7 @@ export async function DELETE(req: NextRequest, ctx: RouteContext) {
|
||||
|
||||
export async function PATCH(req: NextRequest, ctx: RouteContext) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller || !['super_admin', 'admin'].includes(caller.role)) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
@ -8,11 +8,11 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { logError } from '@/lib/logger';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import * as growthClient from '@/lib/growth-client';
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
@ -27,7 +27,7 @@ export async function GET(req: NextRequest) {
|
||||
}
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller || !['super_admin', 'admin'].includes(caller.role)) {
|
||||
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
|
||||
}
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { logError } from '@/lib/logger';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import * as growthClient from '@/lib/growth-client';
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/reviews/${path.join('/')}`;
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/runs/${path.join('/')}`;
|
||||
@ -38,3 +38,6 @@ export async function GET(req: NextRequest, ctx: { params: Promise<{ path: strin
|
||||
export async function POST(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) {
|
||||
return proxy(req, ctx);
|
||||
}
|
||||
export async function PATCH(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) {
|
||||
return proxy(req, ctx);
|
||||
}
|
||||
|
||||
39
dashboards/admin-web/src/app/api/runs/route.ts
Normal file
39
dashboards/admin-web/src/app/api/runs/route.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Runs base route — handles GET /api/runs and POST /api/runs.
|
||||
* Maps to platform-service GET /api/runs and POST /api/runs.
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxyBase(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const qs = new URL(req.url).search;
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-request-id': req.headers.get('x-request-id') || crypto.randomUUID(),
|
||||
'x-user-id': caller.id,
|
||||
'x-product-id': req.headers.get('x-product-id') || process.env.PRODUCT_ID || 'lysnrai',
|
||||
};
|
||||
const fetchOptions: RequestInit = { method: req.method, headers };
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') fetchOptions.body = await req.text();
|
||||
const res = await fetch(`${PLATFORM_URL}/api/runs${qs}`, fetchOptions);
|
||||
const data = await res.json().catch(() => null);
|
||||
return NextResponse.json(data ?? { error: res.statusText }, { status: res.status });
|
||||
} catch (error) {
|
||||
logError('Runs base proxy error', error);
|
||||
return NextResponse.json({ error: 'Service unavailable' }, { status: 502 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
export async function POST(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/sessions/${path.join('/')}`;
|
||||
|
||||
39
dashboards/admin-web/src/app/api/sessions/route.ts
Normal file
39
dashboards/admin-web/src/app/api/sessions/route.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Sessions base route — handles GET /api/sessions and DELETE /api/sessions.
|
||||
* Maps to platform-service GET /api/sessions and DELETE /api/sessions.
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxyBase(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const qs = new URL(req.url).search;
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-request-id': req.headers.get('x-request-id') || crypto.randomUUID(),
|
||||
'x-user-id': caller.id,
|
||||
'x-product-id': req.headers.get('x-product-id') || process.env.PRODUCT_ID || 'lysnrai',
|
||||
};
|
||||
const fetchOptions: RequestInit = { method: req.method, headers };
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') fetchOptions.body = await req.text();
|
||||
const res = await fetch(`${PLATFORM_URL}/api/sessions${qs}`, fetchOptions);
|
||||
const data = await res.json().catch(() => null);
|
||||
return NextResponse.json(data ?? { error: res.statusText }, { status: res.status });
|
||||
} catch (error) {
|
||||
logError('Sessions base proxy error', error);
|
||||
return NextResponse.json({ error: 'Service unavailable' }, { status: 502 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
export async function DELETE(req: NextRequest) {
|
||||
return proxyBase(req);
|
||||
}
|
||||
@ -5,14 +5,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { logError } from '@/lib/logger';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import * as billingClient from '@/lib/billing-client';
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getCurrentUser } from '@/lib/auth-server';
|
||||
import { getCurrentUserFromRequest } from '@/lib/auth-server';
|
||||
import { logError } from '@/lib/logger';
|
||||
|
||||
const PLATFORM_URL = process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003';
|
||||
|
||||
async function proxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
try {
|
||||
const caller = await getCurrentUser(req.headers.get('authorization'));
|
||||
const caller = await getCurrentUserFromRequest(req);
|
||||
if (!caller) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const { path } = await params;
|
||||
const targetPath = `/api/webhooks/${path.join('/')}`;
|
||||
|
||||
@ -42,6 +42,25 @@ export async function getCurrentUser(authHeader: string | null): Promise<UserDoc
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current user from a Request object.
|
||||
* Checks cookies first (Next.js httpOnly cookie), then Authorization header.
|
||||
* Returns null if not authenticated.
|
||||
*/
|
||||
export async function getCurrentUserFromRequest(request: Request): Promise<UserDoc | null> {
|
||||
const cookieHeader = request.headers.get('cookie') || '';
|
||||
const tokenMatch = cookieHeader.match(/(?:^|;\s*)token=([^;]+)/);
|
||||
const authHeader = request.headers.get('authorization');
|
||||
|
||||
if (tokenMatch) {
|
||||
return getCurrentUser(`Bearer ${tokenMatch[1]}`);
|
||||
}
|
||||
if (authHeader) {
|
||||
return getCurrentUser(authHeader);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require an admin user from the request. Checks cookies first, then Authorization header.
|
||||
* Throws Error("Unauthorized") if not authenticated or not admin/super_admin role.
|
||||
|
||||
37
dashboards/admin-web/src/lib/proxy-fetch.ts
Normal file
37
dashboards/admin-web/src/lib/proxy-fetch.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Shared fetch helper for admin-web pages that call proxy API routes.
|
||||
* Automatically injects Authorization + x-product-id headers from localStorage.
|
||||
*/
|
||||
|
||||
function getAuthHeaders(): Record<string, string> {
|
||||
if (typeof window === 'undefined') return {};
|
||||
const headers: Record<string, string> = {};
|
||||
const token = localStorage.getItem('admin_access_token');
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`;
|
||||
const productId = localStorage.getItem('admin_selected_product');
|
||||
if (productId) headers['x-product-id'] = productId;
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scoped fetch helper for a given proxy base path.
|
||||
*
|
||||
* Usage:
|
||||
* const apiFetch = createProxyFetch('/api/jobs');
|
||||
* const data = await apiFetch('list'); // GET /api/jobs/list
|
||||
* await apiFetch('123', { method: 'PUT', body: ... });
|
||||
*/
|
||||
export function createProxyFetch(basePath: string) {
|
||||
return async function proxyFetch(path: string, opts?: RequestInit) {
|
||||
const url = !path || path.startsWith('?') ? `${basePath}${path}` : `${basePath}/${path}`;
|
||||
const res = await fetch(url, {
|
||||
...opts,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...getAuthHeaders(),
|
||||
...opts?.headers,
|
||||
},
|
||||
});
|
||||
return res.json();
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user