Skip to content

Conversation

@leggetter
Copy link
Collaborator

@leggetter leggetter commented Aug 27, 2025

TODO:

  • Check portal 404 handling
  • Check token redirects that include a path
  • Review creation of tenant

This pull request introduces a new demo project for API platform dashboard integration with Outpost, providing a full-featured Next.js application, infrastructure-as-code for local development, and seamless Outpost event management integration. The PR includes configuration, environment, and documentation files, as well as boilerplate code for authentication, registration, and linting.

The most important changes are:

Project Setup & Documentation

  • Added a comprehensive README.md describing features, setup, architecture, usage, and integration patterns for the dashboard integration demo.
  • Added package.json with all necessary dependencies for Next.js, Outpost SDK, authentication, database, and development tooling.

Environment & Configuration

  • Added example environment variable files for both the dashboard app (.env.example) and Outpost (.env.outpost.example), detailing all required configuration variables. [1] [2]
  • Added a .gitignore tailored for Next.js, Node, Docker, and environment files.
  • Added configuration files for Prettier (.prettierrc), ESLint (eslint.config.mjs), and Next.js (next.config.ts) for code style and linting. [1] [2] [3]
  • Added postcss.config.mjs for Tailwind CSS integration.

Infrastructure & Database

  • Added docker-compose.yml to orchestrate PostgreSQL, Redis, RabbitMQ, and Outpost services for local development and testing.
  • Added init-databases.sql to initialize separate databases and users for the dashboard and Outpost, including schema and permissions setup.

Authentication & User Registration

- Preserve URL path when processing tokens instead of always redirecting to root
- Add NotFound component for better UX on invalid routes
- Enable proper deep linking to portal routes like /new and /destinations/{id}

This allows external applications to redirect users to specific portal pages
while maintaining authentication flow.
Add a comprehensive Next.js dashboard demo that showcases seamless integration
with Outpost event destinations management.

Key Features:
- User authentication with NextAuth.js and PostgreSQL
- Auto-creation of Outpost tenants on user registration
- Dashboard overview showing tenant statistics from Outpost API
- Seamless redirect to Outpost portal for destination management
- Support for deep linking to specific portal pages (/new, /destinations/{id})
- Clickable destinations list with navigation to individual destination details
- Docker Compose setup with PostgreSQL, Redis, RabbitMQ, and Outpost services

Technical Implementation:
- Next.js 15 with App Router and TypeScript
- Tailwind CSS for styling
- NextAuth.js v5 with JWT tokens
- Official Outpost TypeScript SDK integration
- Middleware-based authentication and route protection
- Catch-all route handlers for flexible portal path support

This demo demonstrates how to integrate Outpost with an API platform or
SaaS dashboard, providing users with a seamless experience between the
main application and event destination management.
Replace console.log debugging with proper structured logging using Winston:

- Add winston logger with configurable LOG_LEVEL environment variable
- Replace all console.log/error statements with structured logging
- Remove verbose debug logging from API routes
- Keep valuable operational logging (errors, tenant operations, user events)
- Add log level configuration to environment documentation

Logging levels available:
- error: Critical issues only
- warn: Warnings and errors
- info: General information (default)
- debug: Detailed debugging information

This provides better observability while reducing noise in production.
@vercel
Copy link

vercel bot commented Aug 27, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
outpost-docs Ready Ready Preview Comment Aug 28, 2025 5:46pm
outpost-website Ready Ready Preview Comment Aug 28, 2025 5:46pm

… playground and API endpoints

- Added interactive event testing playground for publishing events to destinations
- Implemented API endpoint for triggering test events
- Created API endpoint for fetching available topics
- Updated dashboard structure to include playground navigation
- Improved user experience with form validation and error handling in playground
- Refactored destination and topic fetching into custom hooks
- Enhanced overall documentation to reflect new features and usage
…reflect available topics for selected destination
@leggetter leggetter marked this pull request as ready for review September 2, 2025 21:33
Copilot AI review requested due to automatic review settings September 2, 2025 21:33
@leggetter leggetter merged commit 4ee1c5d into main Sep 2, 2025
4 checks passed
@leggetter leggetter deleted the feat/dashboard-integration-demo branch September 2, 2025 21:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request introduces a comprehensive dashboard integration demo for API platforms, showcasing how to seamlessly embed Outpost's event destination management capabilities into existing SaaS dashboards. The demo provides a full-stack Next.js application with automatic tenant provisioning, authentication integration, and a complete development environment using Docker Compose.

  • Complete Next.js dashboard application with user registration, authentication, and portal integration
  • Infrastructure-as-code setup with Docker Compose orchestrating PostgreSQL, Redis, RabbitMQ, and Outpost services
  • Comprehensive documentation and implementation guides for real-world integration patterns

Reviewed Changes

Copilot reviewed 52 out of 59 changed files in this pull request and generated 5 comments.

File Description
internal/portal/src/app.tsx Fixed portal navigation to preserve current path during token redirects and added 404 handling
examples/demos/dashboard-integration/ New comprehensive demo with authentication, dashboard, playground, and full infrastructure setup

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +142 to +144
// Preserve the current path from the browser
const currentPath = window.location.pathname;
window.location.replace(currentPath);
Copy link

Copilot AI Sep 2, 2025

Choose a reason for hiding this comment

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

This logic is flawed - if the user is already on the correct path, this will cause an unnecessary page reload and potential infinite redirect loop. The code should check if the current path differs from the root path before redirecting, or use a different approach like removing the token parameter from the URL.

Suggested change
// Preserve the current path from the browser
const currentPath = window.location.pathname;
window.location.replace(currentPath);
// Remove the token parameter from the URL without reloading unnecessarily
searchParams.delete("token");
const newSearch = searchParams.toString();
const newUrl = window.location.pathname + (newSearch ? `?${newSearch}` : "");
if (window.location.search.includes("token")) {
window.location.replace(newUrl);
}

Copilot uses AI. Check for mistakes.
const outpost = getOutpostClient();
const result = await outpost.tenants.getPortalUrl({
tenantId,
theme: theme as any, // SDK accepts theme as string but types are restrictive
Copy link

Copilot AI Sep 2, 2025

Choose a reason for hiding this comment

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

Using 'as any' bypasses TypeScript's type safety. Consider using proper type assertion like 'theme as "light" | "dark"' or create a proper type guard to validate the theme parameter before passing it to the SDK.

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +107
// Handle SDK validation error - the API response is valid but SDK expects different format
if (error && typeof error === "object" && "rawValue" in error) {
const rawResponse = (error as any).rawValue;
Copy link

Copilot AI Sep 2, 2025

Choose a reason for hiding this comment

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

This error handling pattern is brittle and tightly coupled to SDK implementation details. Consider using a more robust approach with proper error type checking or wrapping the SDK calls in a try-catch that handles known error patterns more explicitly.

Suggested change
// Handle SDK validation error - the API response is valid but SDK expects different format
if (error && typeof error === "object" && "rawValue" in error) {
const rawResponse = (error as any).rawValue;
// More robust error handling: check for error type or code before falling back to property checks
if (
error &&
typeof error === "object" &&
(
// Prefer error.name or error.code if available
(error.name === "OutpostValidationError") ||
(error.code === "VALIDATION_ERROR") ||
// Fallback to rawValue property if necessary
("rawValue" in error)
)
) {
const rawResponse = (error as any).rawValue ?? (error as any).response ?? null;

Copilot uses AI. Check for mistakes.
<div className="space-y-4">
{events.slice(0, 5).map((event, index) => (
<div
key={`${event.id}-${(event as any).destination_id || index}`}
Copy link

Copilot AI Sep 2, 2025

Choose a reason for hiding this comment

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

Using 'as any' bypasses type safety. If destination_id is an expected property, it should be added to the Event interface. If it's optional or uncertain, use optional chaining or proper type guards instead.

Suggested change
key={`${event.id}-${(event as any).destination_id || index}`}
key={`${event.id}-${event.destination_id ?? index}`}

Copilot uses AI. Check for mistakes.
</Badge>
</div>
<p className="text-xs text-gray-500 mt-1">
{formatDateTime((event as any).time || event.createdAt)}
Copy link

Copilot AI Sep 2, 2025

Choose a reason for hiding this comment

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

Another 'as any' usage that should be avoided. If 'time' is a valid property on events, add it to the Event interface. Otherwise, use proper type checking or default to createdAt without casting.

Suggested change
{formatDateTime((event as any).time || event.createdAt)}
{formatDateTime('time' in event && event.time ? event.time : event.createdAt)}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants