diff --git a/.github/templates/cleanup-comment.md b/.github/templates/cleanup-comment.md new file mode 100644 index 00000000000..3c5c8d78293 --- /dev/null +++ b/.github/templates/cleanup-comment.md @@ -0,0 +1,19 @@ +## ๐Ÿงน Preview Cleanup Complete + +The following preview resources have been cleaned up: + + + + + + + + + + +
ServiceStatus
Database (Neon)$NEON_STATUS
+ +Thank you for your contribution! ๐ŸŽ‰ + +--- +*Preview resources have been processed for cleanup* diff --git a/.github/templates/preview-comment.md b/.github/templates/preview-comment.md new file mode 100644 index 00000000000..a428ba0f689 --- /dev/null +++ b/.github/templates/preview-comment.md @@ -0,0 +1,46 @@ +## ๐Ÿš€ Preview Deployment + +### ๐Ÿ”— Preview Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ServiceStatusLink
Database (Neon)$DATABASE_STATUS$DATABASE_LINK
API (Vercel)$API_STATUS$API_LINK
Web (Vercel)$WEB_STATUS$WEB_LINK
Marketing (Vercel)$MARKETING_STATUS$MARKETING_LINK
Admin (Vercel)$ADMIN_STATUS$ADMIN_LINK
Docs (Vercel)$DOCS_STATUS$DOCS_LINK
+ +--- + +*Preview updates automatically with new commits* + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ae949f4fe4..8c9dac5281e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,107 +1,104 @@ name: CI on: - pull_request: push: branches: [main] + pull_request: + types: [opened, synchronize] jobs: lint: name: Lint runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: bun-version: 1.3.3 - name: Cache dependencies uses: actions/cache@v4 with: - path: | - ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - name: Install dependencies run: bun install --frozen - - name: Run lint + - name: Lint run: bun run lint - typecheck: - name: Typecheck + test: + name: Test runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: bun-version: 1.3.3 - name: Cache dependencies uses: actions/cache@v4 with: - path: | - ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - name: Install dependencies run: bun install --frozen - - name: Run typecheck - run: bun run typecheck + - name: Test + run: bun run test - build: - name: Build + typecheck: + name: Typecheck runs-on: ubuntu-latest - env: - # Placeholder values for build validation (not used at runtime) - DATABASE_URL: "postgresql://placeholder:placeholder@localhost:5432/placeholder" - DATABASE_URL_UNPOOLED: "postgresql://placeholder:placeholder@localhost:5432/placeholder" - steps: - - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: bun-version: 1.3.3 - name: Cache dependencies uses: actions/cache@v4 with: - path: | - ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - name: Install dependencies run: bun install --frozen - - name: Run build - run: bun run build + - name: Typecheck + run: bun run typecheck - test: - name: Test + build: + name: Build runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: bun-version: 1.3.3 - name: Cache dependencies uses: actions/cache@v4 with: - path: | - ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - name: Install dependencies run: bun install --frozen - - name: Run test - run: bun run test + - name: Build CLI and Desktop + run: bun turbo run build --filter=@superset/cli --filter=@superset/desktop diff --git a/.github/workflows/cleanup-preview.yml b/.github/workflows/cleanup-preview.yml index 70db7f827de..5ee7f917576 100644 --- a/.github/workflows/cleanup-preview.yml +++ b/.github/workflows/cleanup-preview.yml @@ -11,25 +11,29 @@ jobs: permissions: contents: read pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Delete Neon branch - id: delete-branch - uses: neondatabase/delete-branch-action@v3.2.0 + id: neon-cleanup + uses: neondatabase/delete-branch-action@v3 continue-on-error: true with: project_id: ${{ vars.NEON_PROJECT_ID }} branch: ${{ github.event.pull_request.head.ref }} api_key: ${{ secrets.NEON_API_KEY }} - - name: Update comment with cleanup status + - name: Generate cleanup comment + run: | + NEON_STATUS="${{ steps.neon-cleanup.outcome == 'success' && 'โœ…' || 'โš ๏ธ' }}" + export NEON_STATUS + envsubst < .github/templates/cleanup-comment.md > final-comment.md + + - name: Update comment if: always() uses: thollander/actions-comment-pull-request@v3 with: + file-path: final-comment.md comment-tag: "๐Ÿš€-preview-deployment" - message: | - ## ๐Ÿงน Preview Cleanup Complete - - The following preview resources have been cleaned up: - - ${{ steps.delete-branch.outcome == 'success' && 'โœ…' || 'โš ๏ธ' }} Neon database branch - - Thank you for your contribution! ๐ŸŽ‰ diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index 2236b32d833..76b3783ed56 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -8,17 +8,25 @@ concurrency: group: preview-${{ github.event.pull_request.number }} cancel-in-progress: true +env: + PR_NUMBER: ${{ github.event.pull_request.number }} + API_ALIAS: api-pr-${{ github.event.pull_request.number }}-superset.vercel.app + WEB_ALIAS: web-pr-${{ github.event.pull_request.number }}-superset.vercel.app + MARKETING_ALIAS: marketing-pr-${{ github.event.pull_request.number }}-superset.vercel.app + ADMIN_ALIAS: admin-pr-${{ github.event.pull_request.number }}-superset.vercel.app + DOCS_ALIAS: docs-pr-${{ github.event.pull_request.number }}-superset.vercel.app + jobs: - create-neon-branch: - name: Create Neon Branch + deploy-database: + name: Deploy Database (Neon) runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write + steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: bun-version: 1.3.3 @@ -26,20 +34,20 @@ jobs: uses: actions/cache@v4 with: path: ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - name: Install dependencies run: bun install --frozen - name: Create Neon branch id: create-branch - uses: neondatabase/create-branch-action@v6.1.1 + uses: neondatabase/create-branch-action@v6 with: project_id: ${{ vars.NEON_PROJECT_ID }} branch_name: ${{ github.head_ref }} api_key: ${{ secrets.NEON_API_KEY }} - - name: Validate and run migrations + - name: Validate and push database schema working-directory: packages/db env: DATABASE_URL: ${{ steps.create-branch.outputs.db_url_pooled }} @@ -48,19 +56,404 @@ jobs: bun drizzle-kit check bun drizzle-kit migrate - - name: Post PR comment - uses: thollander/actions-comment-pull-request@v3 + - name: Save database success status + run: | + cat > database-status.env << EOF + DATABASE_STATUS="โœ…" + DATABASE_LINK="View Branch" + DATABASE_URL="${{ steps.create-branch.outputs.db_url_pooled }}" + DATABASE_URL_UNPOOLED="${{ steps.create-branch.outputs.db_url }}" + BRANCH_ID="${{ steps.create-branch.outputs.branch_id }}" + EOF + + - name: Upload database status + uses: actions/upload-artifact@v4 with: - comment-tag: "๐Ÿš€-preview-deployment" - message: | - ## ๐Ÿš€ Preview Deployment + name: database-status + path: database-status.env + + deploy-api: + name: Deploy API + runs-on: ubuntu-latest + environment: preview + needs: deploy-database + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 + + - name: Download database info + uses: actions/download-artifact@v4 + with: + name: database-status + + - name: Load database URL + run: | + source database-status.env + echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV + echo "DATABASE_URL_UNPOOLED=$DATABASE_URL_UNPOOLED" >> $GITHUB_ENV + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy API + id: deploy + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_API_PROJECT_ID }} + DATABASE_URL: ${{ env.DATABASE_URL }} + DATABASE_URL_UNPOOLED: ${{ env.DATABASE_URL_UNPOOLED }} + NEXT_PUBLIC_WEB_URL: https://${{ env.WEB_ALIAS }} + NEXT_PUBLIC_ADMIN_URL: https://${{ env.ADMIN_ALIAS }} + MOCK_USER_ID: ${{ secrets.MOCK_USER_ID }} + run: | + vercel pull --yes --environment=preview --token=$VERCEL_TOKEN + vercel build --token=$VERCEL_TOKEN + VERCEL_URL=$(vercel deploy --prebuilt --token=$VERCEL_TOKEN) + vercel alias $VERCEL_URL ${{ env.API_ALIAS }} --scope=$VERCEL_ORG_ID --token=$VERCEL_TOKEN + echo "vercel_url=$VERCEL_URL" >> $GITHUB_OUTPUT + + - name: Save API success status + run: | + cat > api-status.env << EOF + API_STATUS="โœ…" + API_LINK="Open Preview" + API_URL="https://${{ env.API_ALIAS }}" + EOF + + - name: Upload API status + uses: actions/upload-artifact@v4 + with: + name: api-status + path: api-status.env + + deploy-web: + name: Deploy Web + runs-on: ubuntu-latest + environment: preview + needs: deploy-database + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Download database info + uses: actions/download-artifact@v4 + with: + name: database-status + + - name: Load database URL + run: | + source database-status.env + echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV + echo "DATABASE_URL_UNPOOLED=$DATABASE_URL_UNPOOLED" >> $GITHUB_ENV + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy Web + id: deploy + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_WEB_PROJECT_ID }} + DATABASE_URL: ${{ env.DATABASE_URL }} + DATABASE_URL_UNPOOLED: ${{ env.DATABASE_URL_UNPOOLED }} + NEXT_PUBLIC_API_URL: https://${{ env.API_ALIAS }} + NEXT_PUBLIC_MARKETING_URL: https://${{ env.MARKETING_ALIAS }} + NEXT_PUBLIC_DOCS_URL: https://${{ env.DOCS_ALIAS }} + MOCK_USER_ID: ${{ secrets.MOCK_USER_ID }} + run: | + vercel pull --yes --environment=preview --token=$VERCEL_TOKEN + vercel build --token=$VERCEL_TOKEN + VERCEL_URL=$(vercel deploy --prebuilt --token=$VERCEL_TOKEN) + vercel alias $VERCEL_URL ${{ env.WEB_ALIAS }} --scope=$VERCEL_ORG_ID --token=$VERCEL_TOKEN + echo "vercel_url=$VERCEL_URL" >> $GITHUB_OUTPUT + + - name: Save web success status + run: | + cat > web-status.env << EOF + WEB_STATUS="โœ…" + WEB_LINK="Open Preview" + EOF + + - name: Upload web status + uses: actions/upload-artifact@v4 + with: + name: web-status + path: web-status.env + + deploy-marketing: + name: Deploy Marketing + runs-on: ubuntu-latest + environment: preview + needs: deploy-database + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy Marketing + id: deploy + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_MARKETING_PROJECT_ID }} + NEXT_PUBLIC_API_URL: https://${{ env.API_ALIAS }} + run: | + vercel pull --yes --environment=preview --token=$VERCEL_TOKEN + vercel build --token=$VERCEL_TOKEN + VERCEL_URL=$(vercel deploy --prebuilt --token=$VERCEL_TOKEN) + vercel alias $VERCEL_URL ${{ env.MARKETING_ALIAS }} --scope=$VERCEL_ORG_ID --token=$VERCEL_TOKEN + echo "vercel_url=$VERCEL_URL" >> $GITHUB_OUTPUT + + - name: Save marketing success status + run: | + cat > marketing-status.env << EOF + MARKETING_STATUS="โœ…" + MARKETING_LINK="Open Preview" + EOF + + - name: Upload marketing status + uses: actions/upload-artifact@v4 + with: + name: marketing-status + path: marketing-status.env + + deploy-admin: + name: Deploy Admin + runs-on: ubuntu-latest + environment: preview + needs: deploy-database + + steps: + - name: Checkout repository + uses: actions/checkout@v4 - ### ๐Ÿ”— Preview Links + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 - | Service | Status | Link | - |---------|:------:|------| - | **Database (Neon)** | โœ… | [View Branch](https://console.neon.tech/app/projects/${{ vars.NEON_PROJECT_ID }}/branches/${{ steps.create-branch.outputs.branch_id }}/tables) | + - name: Download database info + uses: actions/download-artifact@v4 + with: + name: database-status - --- + - name: Load database URL + run: | + source database-status.env + echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV + echo "DATABASE_URL_UNPOOLED=$DATABASE_URL_UNPOOLED" >> $GITHUB_ENV - *Preview updates automatically with new commits* + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy Admin + id: deploy + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_ADMIN_PROJECT_ID }} + DATABASE_URL: ${{ env.DATABASE_URL }} + DATABASE_URL_UNPOOLED: ${{ env.DATABASE_URL_UNPOOLED }} + NEXT_PUBLIC_API_URL: https://${{ env.API_ALIAS }} + NEXT_PUBLIC_WEB_URL: https://${{ env.WEB_ALIAS }} + MOCK_USER_ID: ${{ secrets.MOCK_USER_ID }} + run: | + vercel pull --yes --environment=preview --token=$VERCEL_TOKEN + vercel build --token=$VERCEL_TOKEN + VERCEL_URL=$(vercel deploy --prebuilt --token=$VERCEL_TOKEN) + vercel alias $VERCEL_URL ${{ env.ADMIN_ALIAS }} --scope=$VERCEL_ORG_ID --token=$VERCEL_TOKEN + echo "vercel_url=$VERCEL_URL" >> $GITHUB_OUTPUT + + - name: Save admin success status + run: | + cat > admin-status.env << EOF + ADMIN_STATUS="โœ…" + ADMIN_LINK="Open Preview" + EOF + + - name: Upload admin status + uses: actions/upload-artifact@v4 + with: + name: admin-status + path: admin-status.env + + deploy-docs: + name: Deploy Docs + runs-on: ubuntu-latest + environment: preview + needs: deploy-database + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy Docs + id: deploy + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_DOCS_PROJECT_ID }} + NEXT_PUBLIC_API_URL: https://${{ env.API_ALIAS }} + NEXT_PUBLIC_WEB_URL: https://${{ env.WEB_ALIAS }} + NEXT_PUBLIC_MARKETING_URL: https://${{ env.MARKETING_ALIAS }} + run: | + vercel pull --yes --environment=preview --token=$VERCEL_TOKEN + vercel build --token=$VERCEL_TOKEN + VERCEL_URL=$(vercel deploy --prebuilt --token=$VERCEL_TOKEN) + vercel alias $VERCEL_URL ${{ env.DOCS_ALIAS }} --scope=$VERCEL_ORG_ID --token=$VERCEL_TOKEN + echo "vercel_url=$VERCEL_URL" >> $GITHUB_OUTPUT + + - name: Save docs success status + run: | + cat > docs-status.env << EOF + DOCS_STATUS="โœ…" + DOCS_LINK="Open Preview" + EOF + + - name: Upload docs status + uses: actions/upload-artifact@v4 + with: + name: docs-status + path: docs-status.env + + post-final-comment: + name: Post Deployment Comment + runs-on: ubuntu-latest + if: always() + needs: [deploy-database, deploy-api, deploy-web, deploy-marketing, deploy-admin, deploy-docs] + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download all status artifacts + uses: actions/download-artifact@v4 + with: + pattern: "*-status" + merge-multiple: true + + - name: Generate deployment comment + run: | + DATABASE_STATUS="โŒ" + DATABASE_LINK="Failed to create" + API_STATUS="โŒ" + API_LINK="Failed to deploy" + WEB_STATUS="โŒ" + WEB_LINK="Failed to deploy" + MARKETING_STATUS="โŒ" + MARKETING_LINK="Failed to deploy" + ADMIN_STATUS="โŒ" + ADMIN_LINK="Failed to deploy" + DOCS_STATUS="โŒ" + DOCS_LINK="Failed to deploy" + + if [[ "${{ needs.deploy-database.result }}" == "success" ]]; then + source database-status.env + fi + + if [[ "${{ needs.deploy-api.result }}" == "success" ]]; then + source api-status.env + fi + + if [[ "${{ needs.deploy-web.result }}" == "success" ]]; then + source web-status.env + fi + + if [[ "${{ needs.deploy-marketing.result }}" == "success" ]]; then + source marketing-status.env + fi + + if [[ "${{ needs.deploy-admin.result }}" == "success" ]]; then + source admin-status.env + fi + + if [[ "${{ needs.deploy-docs.result }}" == "success" ]]; then + source docs-status.env + fi + + export DATABASE_STATUS DATABASE_LINK API_STATUS API_LINK WEB_STATUS WEB_LINK MARKETING_STATUS MARKETING_LINK ADMIN_STATUS ADMIN_LINK DOCS_STATUS DOCS_LINK + envsubst < .github/templates/preview-comment.md > final-comment.md + + - name: Post final deployment comment + uses: thollander/actions-comment-pull-request@v3 + with: + file-path: final-comment.md + comment-tag: "๐Ÿš€-preview-deployment" diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 67d4a351c32..ec00f4ced67 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -3,31 +3,238 @@ name: Deploy Production on: push: branches: [main] + workflow_dispatch: jobs: deploy-database: name: Deploy Database Migrations runs-on: ubuntu-latest + environment: production steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v1 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - bun-version: '1.3.2' + bun-version: 1.3.3 - name: Cache dependencies uses: actions/cache@v4 with: path: ~/.bun/install/cache - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} - name: Install dependencies - run: bun install --frozen-lockfile + run: bun install --frozen - name: Run database migrations + working-directory: packages/db env: DATABASE_URL: ${{ secrets.DATABASE_URL_UNPOOLED }} + DATABASE_URL_UNPOOLED: ${{ secrets.DATABASE_URL_UNPOOLED }} + run: bun drizzle-kit migrate + + deploy-api: + name: Deploy API to Vercel + runs-on: ubuntu-latest + environment: production + needs: deploy-database + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy API + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_API_PROJECT_ID }} + NEXT_PUBLIC_WEB_URL: ${{ secrets.NEXT_PUBLIC_WEB_URL }} + NEXT_PUBLIC_ADMIN_URL: ${{ secrets.NEXT_PUBLIC_ADMIN_URL }} + MOCK_USER_ID: ${{ secrets.MOCK_USER_ID }} + run: | + vercel pull --yes --environment=production --token=$VERCEL_TOKEN + vercel build --prod --token=$VERCEL_TOKEN + vercel deploy --prod --prebuilt --token=$VERCEL_TOKEN + + deploy-web: + name: Deploy Web to Vercel + runs-on: ubuntu-latest + environment: production + needs: deploy-api + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy Web + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_WEB_PROJECT_ID }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + DATABASE_URL_UNPOOLED: ${{ secrets.DATABASE_URL_UNPOOLED }} + NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }} + NEXT_PUBLIC_MARKETING_URL: ${{ secrets.NEXT_PUBLIC_MARKETING_URL }} + NEXT_PUBLIC_DOCS_URL: ${{ secrets.NEXT_PUBLIC_DOCS_URL }} + MOCK_USER_ID: ${{ secrets.MOCK_USER_ID }} + run: | + vercel pull --yes --environment=production --token=$VERCEL_TOKEN + vercel build --prod --token=$VERCEL_TOKEN + vercel deploy --prod --prebuilt --token=$VERCEL_TOKEN + + deploy-marketing: + name: Deploy Marketing to Vercel + runs-on: ubuntu-latest + environment: production + needs: deploy-database + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy Marketing + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_MARKETING_PROJECT_ID }} + NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }} + run: | + vercel pull --yes --environment=production --token=$VERCEL_TOKEN + vercel build --prod --token=$VERCEL_TOKEN + vercel deploy --prod --prebuilt --token=$VERCEL_TOKEN + + deploy-admin: + name: Deploy Admin to Vercel + runs-on: ubuntu-latest + environment: production + needs: deploy-database + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy Admin + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_ADMIN_PROJECT_ID }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + DATABASE_URL_UNPOOLED: ${{ secrets.DATABASE_URL_UNPOOLED }} + NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }} + NEXT_PUBLIC_WEB_URL: ${{ secrets.NEXT_PUBLIC_WEB_URL }} + MOCK_USER_ID: ${{ secrets.MOCK_USER_ID }} + run: | + vercel pull --yes --environment=production --token=$VERCEL_TOKEN + vercel build --prod --token=$VERCEL_TOKEN + vercel deploy --prod --prebuilt --token=$VERCEL_TOKEN + + deploy-docs: + name: Deploy Docs to Vercel + runs-on: ubuntu-latest + environment: production + needs: deploy-database + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.3 + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + + - name: Install dependencies + run: bun install --frozen + + - name: Install Vercel CLI + run: npm install --global vercel@latest + + - name: Build and Deploy Docs + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_DOCS_PROJECT_ID }} + NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }} + NEXT_PUBLIC_WEB_URL: ${{ secrets.NEXT_PUBLIC_WEB_URL }} + NEXT_PUBLIC_MARKETING_URL: ${{ secrets.NEXT_PUBLIC_MARKETING_URL }} run: | - cd packages/db - bunx drizzle-kit migrate + vercel pull --yes --environment=production --token=$VERCEL_TOKEN + vercel build --prod --token=$VERCEL_TOKEN + vercel deploy --prod --prebuilt --token=$VERCEL_TOKEN diff --git a/.gitignore b/.gitignore index 22dcc12a13b..7383c977d07 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ yarn.lock # Env variables .env .env.* +.envrc mise.toml @@ -45,7 +46,10 @@ mise.toml # Turbo .turbo .cache -.envrc + +# Next.js +.next +out # Superset # Ignore .superset directory except for config and scripts diff --git a/.neon b/.neon deleted file mode 100644 index fc48de70460..00000000000 --- a/.neon +++ /dev/null @@ -1,3 +0,0 @@ -{ - "projectId": "tiny-cherry-82420694" -} diff --git a/.superset/setup.sh b/.superset/setup.sh index 1c0e7a21aec..863f3d03999 100755 --- a/.superset/setup.sh +++ b/.superset/setup.sh @@ -15,6 +15,10 @@ command -v bun &> /dev/null || error "Bun not installed. Install from https://bu command -v neonctl &> /dev/null || error "Neon CLI not installed. Run: npm install -g neonctl" command -v jq &> /dev/null || error "jq not installed. Run: brew install jq" +# Check required environment variables +NEON_PROJECT_ID="${NEON_PROJECT_ID:-}" +[ -z "$NEON_PROJECT_ID" ] && error "NEON_PROJECT_ID environment variable is required" + # Install dependencies echo "๐Ÿ“ฅ Installing dependencies..." bun install @@ -34,23 +38,23 @@ fi echo "๐Ÿ—„๏ธ Creating Neon branch..." WORKSPACE_NAME="${SUPERSET_WORKSPACE_NAME:-$(basename "$PWD")}" NEON_OUTPUT=$(neonctl branches create \ - --project-id tiny-cherry-82420694 \ + --project-id "$NEON_PROJECT_ID" \ --name "$WORKSPACE_NAME" \ --output json) # Parse connection strings from create output BRANCH_ID=$(echo "$NEON_OUTPUT" | jq -r '.branch.id') -DATABASE_URL=$(echo "$NEON_OUTPUT" | jq -r '.connection_uris[0].connection_uri') +DIRECT_URL=$(echo "$NEON_OUTPUT" | jq -r '.connection_uris[0].connection_uri') POOLER_HOST=$(echo "$NEON_OUTPUT" | jq -r '.connection_uris[0].connection_parameters.pooler_host') PASSWORD=$(echo "$NEON_OUTPUT" | jq -r '.connection_uris[0].connection_parameters.password') ROLE=$(echo "$NEON_OUTPUT" | jq -r '.connection_uris[0].connection_parameters.role') DATABASE=$(echo "$NEON_OUTPUT" | jq -r '.connection_uris[0].connection_parameters.database') -DATABASE_POOLED_URL="postgresql://${ROLE}:${PASSWORD}@${POOLER_HOST}/${DATABASE}?sslmode=require" +POOLED_URL="postgresql://${ROLE}:${PASSWORD}@${POOLER_HOST}/${DATABASE}?sslmode=require" -cat > .env << EOF +cat >> .env << EOF NEON_BRANCH_ID=$BRANCH_ID -DATABASE_URL=$DATABASE_URL -DATABASE_POOLED_URL=$DATABASE_POOLED_URL +DATABASE_URL=$POOLED_URL +DATABASE_URL_UNPOOLED=$DIRECT_URL EOF success "Neon branch created: $WORKSPACE_NAME" diff --git a/.superset/teardown.sh b/.superset/teardown.sh index 14269a20f4d..2b4bf203171 100755 --- a/.superset/teardown.sh +++ b/.superset/teardown.sh @@ -13,6 +13,10 @@ echo "๐Ÿงน Tearing down Superset workspace..." # Check dependencies command -v neonctl &> /dev/null || error "Neon CLI not installed. Run: npm install -g neonctl" +# Check required environment variables +NEON_PROJECT_ID="${NEON_PROJECT_ID:-}" +[ -z "$NEON_PROJECT_ID" ] && error "NEON_PROJECT_ID environment variable is required" + # Delete Neon branch for this workspace WORKSPACE_NAME="${SUPERSET_WORKSPACE_NAME:-$(basename "$PWD")}" if [ -f ".env" ]; then @@ -25,7 +29,7 @@ if [ -z "$BRANCH_ID" ]; then fi echo "๐Ÿ—„๏ธ Deleting Neon branch: $WORKSPACE_NAME ($BRANCH_ID)" -if neonctl branches delete "$BRANCH_ID" --project-id tiny-cherry-82420694 --force 2>/dev/null; then +if neonctl branches delete "$BRANCH_ID" --project-id "$NEON_PROJECT_ID" --force 2>/dev/null; then success "Neon branch deleted: $WORKSPACE_NAME" else echo "โš ๏ธ Neon branch '$WORKSPACE_NAME' ($BRANCH_ID) not found or already deleted" diff --git a/AGENTS.md b/AGENTS.md index 81e5485c4b1..6a171f37049 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,10 +6,12 @@ Guidelines for agents and developers working in this repository. Bun + Turbo monorepo with: - **Apps**: - - `apps/website` - Main website application + - `apps/web` - Main web application (app.superset.sh) + - `apps/marketing` - Marketing site (superset.sh) + - `apps/admin` - Admin dashboard + - `apps/api` - API backend - `apps/desktop` - Electron desktop application (see [Desktop App Guide](#desktop-app-electron) below) - `apps/docs` - Documentation site - - `apps/blog` - Blog site - **Packages**: - `packages/ui` - Shared UI components (shadcn/ui + TailwindCSS v4). - Add components: `npx shadcn@latest add ` (run in `packages/ui/`) @@ -151,7 +153,8 @@ The `src/components/ui/`, `src/components/ai-elements`, and `src/components/reac - Always spin up a new neon branch to create migrations. Update our root .env files to point at the neon branch locally. - Use drizzle to manage the migration. You can see the schema at packages/db/src/schema. Never run a migration yourself. - Create migrations by changing drizzle schema then running `pnpm drizzle-kit generate --name=""` -- Neon org id is org-round-base-25422821, Neon project id is tiny-cherry-82420694. list_projects tool requires org_id passed in. +- `NEON_ORG_ID` and `NEON_PROJECT_ID` env vars are set in .env +- list_projects tool requires org_id passed in ## Desktop App (Electron) diff --git a/apps/blog/next-env.d.ts b/apps/admin/next-env.d.ts similarity index 100% rename from apps/blog/next-env.d.ts rename to apps/admin/next-env.d.ts diff --git a/apps/admin/next.config.ts b/apps/admin/next.config.ts new file mode 100644 index 00000000000..737f1468308 --- /dev/null +++ b/apps/admin/next.config.ts @@ -0,0 +1,10 @@ +import type { NextConfig } from "next"; + +const config: NextConfig = { + experimental: { + reactCompiler: true, + }, + typescript: { ignoreBuildErrors: true }, +}; + +export default config; diff --git a/apps/admin/package.json b/apps/admin/package.json new file mode 100644 index 00000000000..36b92d1ffc9 --- /dev/null +++ b/apps/admin/package.json @@ -0,0 +1,45 @@ +{ + "name": "@superset/admin", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "build": "next build", + "clean": "git clean -xdf .cache .next .turbo node_modules", + "dev": "next dev --port 3003", + "start": "next start", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@superset/db": "workspace:*", + "@superset/queries": "workspace:*", + "@superset/shared": "workspace:*", + "@superset/trpc": "workspace:*", + "@superset/ui": "workspace:*", + "@t3-oss/env-nextjs": "^0.13.8", + "@tanstack/react-query": "^5.90.10", + "@tanstack/react-query-devtools": "^5.90.10", + "@trpc/client": "^11.7.1", + "@trpc/server": "^11.7.1", + "@trpc/tanstack-react-query": "^11.7.1", + "geist": "^1.5.1", + "lucide-react": "^0.560.0", + "next": "^15.5.7", + "next-themes": "^0.4.6", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "server-only": "^0.0.1", + "superjson": "^2.2.5", + "zod": "^4.1.13" + }, + "devDependencies": { + "@superset/typescript": "workspace:*", + "@tailwindcss/postcss": "^4.0.9", + "@types/node": "^24.9.1", + "@types/react": "^19.1.11", + "@types/react-dom": "^19.1.7", + "babel-plugin-react-compiler": "^1.0.0", + "tailwindcss": "^4.0.9", + "typescript": "^5.9.3" + } +} diff --git a/apps/blog/postcss.config.mjs b/apps/admin/postcss.config.mjs similarity index 54% rename from apps/blog/postcss.config.mjs rename to apps/admin/postcss.config.mjs index c42f31cab8a..017b34b9cb3 100644 --- a/apps/blog/postcss.config.mjs +++ b/apps/admin/postcss.config.mjs @@ -1,7 +1,5 @@ -const config = { +export default { plugins: { "@tailwindcss/postcss": {}, }, }; - -export default config; diff --git a/apps/website/public/favicon.ico b/apps/admin/public/favicon.ico similarity index 100% rename from apps/website/public/favicon.ico rename to apps/admin/public/favicon.ico diff --git a/apps/admin/src/app/globals.css b/apps/admin/src/app/globals.css new file mode 100644 index 00000000000..ee297b51232 --- /dev/null +++ b/apps/admin/src/app/globals.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; + +@import "@superset/ui/globals.css"; +@source "../../../../packages/ui/src/**/*.{ts,tsx}"; diff --git a/apps/admin/src/app/layout.tsx b/apps/admin/src/app/layout.tsx new file mode 100644 index 00000000000..47fe52f50ca --- /dev/null +++ b/apps/admin/src/app/layout.tsx @@ -0,0 +1,44 @@ +import { Toaster } from "@superset/ui/sonner"; +import { cn } from "@superset/ui/utils"; +import { GeistMono } from "geist/font/mono"; +import { GeistSans } from "geist/font/sans"; +import type { Metadata, Viewport } from "next"; + +import "./globals.css"; + +import { Providers } from "./providers"; + +export const metadata: Metadata = { + title: "Superset Admin", + description: "Admin dashboard for Superset", +}; + +export const viewport: Viewport = { + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "white" }, + { media: "(prefers-color-scheme: dark)", color: "black" }, + ], +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + {children} + + + + + ); +} diff --git a/apps/admin/src/app/page.tsx b/apps/admin/src/app/page.tsx new file mode 100644 index 00000000000..00c034e5bf3 --- /dev/null +++ b/apps/admin/src/app/page.tsx @@ -0,0 +1,25 @@ +import { api } from "@/trpc/server"; + +export default async function Home() { + const users = await (await api()).user.all.query(); + + return ( +
+

Superset Admin

+

Admin dashboard

+
+

tRPC Test Query

+

+ Users in database: {users.length} +

+ {users.length > 0 && ( +
    + {users.slice(0, 5).map((user) => ( +
  • {user.email}
  • + ))} +
+ )} +
+
+ ); +} diff --git a/apps/admin/src/app/providers.tsx b/apps/admin/src/app/providers.tsx new file mode 100644 index 00000000000..4e0d68bfc9e --- /dev/null +++ b/apps/admin/src/app/providers.tsx @@ -0,0 +1,22 @@ +"use client"; + +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { ThemeProvider } from "next-themes"; + +import { TRPCReactProvider } from "../trpc/react"; + +export function Providers({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + + ); +} diff --git a/apps/admin/src/env.ts b/apps/admin/src/env.ts new file mode 100644 index 00000000000..d24b66775de --- /dev/null +++ b/apps/admin/src/env.ts @@ -0,0 +1,32 @@ +import { createEnv } from "@t3-oss/env-nextjs"; +import { vercel } from "@t3-oss/env-nextjs/presets-zod"; +import { z } from "zod"; + +export const env = createEnv({ + extends: [vercel()], + shared: { + NODE_ENV: z + .enum(["development", "production", "test"]) + .default("development"), + }, + + server: { + // Database (needed by @superset/trpc dependency) + DATABASE_URL: z.string().url(), + DATABASE_URL_UNPOOLED: z.string().url(), + }, + + client: { + NEXT_PUBLIC_API_URL: z.string().url(), + NEXT_PUBLIC_WEB_URL: z.string().url(), + }, + + experimental__runtimeEnv: { + NODE_ENV: process.env.NODE_ENV, + NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, + NEXT_PUBLIC_WEB_URL: process.env.NEXT_PUBLIC_WEB_URL, + }, + + skipValidation: !!process.env.SKIP_ENV_VALIDATION, + emptyStringAsUndefined: true, +}); diff --git a/apps/website/src/trpc/query-client.ts b/apps/admin/src/trpc/query-client.ts similarity index 100% rename from apps/website/src/trpc/query-client.ts rename to apps/admin/src/trpc/query-client.ts diff --git a/apps/admin/src/trpc/react.tsx b/apps/admin/src/trpc/react.tsx new file mode 100644 index 00000000000..cd89546d215 --- /dev/null +++ b/apps/admin/src/trpc/react.tsx @@ -0,0 +1,64 @@ +"use client"; + +import type { AppRouter } from "@superset/trpc"; +import type { QueryClient } from "@tanstack/react-query"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { + createTRPCClient, + httpBatchStreamLink, + loggerLink, +} from "@trpc/client"; +import { createTRPCContext } from "@trpc/tanstack-react-query"; +import { useState } from "react"; +import SuperJSON from "superjson"; + +import { env } from "../env"; +import { createQueryClient } from "./query-client"; + +let clientQueryClientSingleton: QueryClient | undefined; +const getQueryClient = () => { + if (typeof window === "undefined") { + return createQueryClient(); + } + if (!clientQueryClientSingleton) { + clientQueryClientSingleton = createQueryClient(); + } + return clientQueryClientSingleton; +}; + +const context = createTRPCContext(); +export const { useTRPC, TRPCProvider } = context; +export type UseTRPC = typeof useTRPC; + +export function TRPCReactProvider(props: { children: React.ReactNode }) { + const queryClient = getQueryClient(); + + const [trpcClient] = useState(() => + createTRPCClient({ + links: [ + loggerLink({ + enabled: (op) => + env.NODE_ENV === "development" || + (op.direction === "down" && op.result instanceof Error), + }), + httpBatchStreamLink({ + transformer: SuperJSON, + url: `${env.NEXT_PUBLIC_API_URL}/api/trpc`, + headers() { + const headers = new Headers(); + headers.set("x-trpc-source", "nextjs-react"); + return headers; + }, + }), + ], + }), + ); + + return ( + + + {props.children} + + + ); +} diff --git a/apps/admin/src/trpc/server.tsx b/apps/admin/src/trpc/server.tsx new file mode 100644 index 00000000000..3042598abba --- /dev/null +++ b/apps/admin/src/trpc/server.tsx @@ -0,0 +1,26 @@ +import "server-only"; + +import type { AppRouter } from "@superset/trpc"; +import { createTRPCClient, httpBatchLink } from "@trpc/client"; +import { headers } from "next/headers"; +import { cache } from "react"; +import SuperJSON from "superjson"; + +import { env } from "../env"; + +export const api = cache(async () => { + const heads = new Headers(await headers()); + heads.set("x-trpc-source", "rsc"); + + return createTRPCClient({ + links: [ + httpBatchLink({ + transformer: SuperJSON, + url: `${env.NEXT_PUBLIC_API_URL}/api/trpc`, + headers() { + return Object.fromEntries(heads.entries()); + }, + }), + ], + }); +}); diff --git a/apps/blog/tsconfig.json b/apps/admin/tsconfig.json similarity index 100% rename from apps/blog/tsconfig.json rename to apps/admin/tsconfig.json diff --git a/apps/api/next-env.d.ts b/apps/api/next-env.d.ts new file mode 100644 index 00000000000..830fb594ca2 --- /dev/null +++ b/apps/api/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/api/next.config.ts b/apps/api/next.config.ts new file mode 100644 index 00000000000..8ea4db5a0d2 --- /dev/null +++ b/apps/api/next.config.ts @@ -0,0 +1,33 @@ +import type { NextConfig } from "next"; + +import { env } from "./src/env"; + +// Allowed origins for CORS +const allowedOrigins = [ + env.NEXT_PUBLIC_WEB_URL, + env.NEXT_PUBLIC_ADMIN_URL, +].filter(Boolean) as string[]; + +const config: NextConfig = { + experimental: { + reactCompiler: true, + }, + typescript: { ignoreBuildErrors: true }, + async headers() { + // Generate CORS headers for each allowed origin + return allowedOrigins.map((origin) => ({ + source: "/api/:path*", + headers: [ + { key: "Access-Control-Allow-Origin", value: origin }, + { key: "Access-Control-Allow-Methods", value: "GET, POST, OPTIONS" }, + { + key: "Access-Control-Allow-Headers", + value: "Content-Type, Authorization, trpc-accept, x-trpc-source", + }, + { key: "Access-Control-Allow-Credentials", value: "true" }, + ], + })); + }, +}; + +export default config; diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 00000000000..e83fde1b612 --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,33 @@ +{ + "name": "@superset/api", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "build": "next build", + "clean": "git clean -xdf .cache .next .turbo node_modules", + "dev": "next dev --port 3001", + "start": "next start --port 3001", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@superset/db": "workspace:*", + "@superset/shared": "workspace:*", + "@superset/trpc": "workspace:*", + "@t3-oss/env-nextjs": "^0.13.8", + "@trpc/server": "^11.7.1", + "drizzle-orm": "0.45.1", + "next": "^15.5.7", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "zod": "^4.1.13" + }, + "devDependencies": { + "@superset/typescript": "workspace:*", + "@types/node": "^24.9.1", + "@types/react": "^19.1.11", + "@types/react-dom": "^19.1.7", + "babel-plugin-react-compiler": "^1.0.0", + "typescript": "^5.9.3" + } +} diff --git a/apps/api/public/favicon.ico b/apps/api/public/favicon.ico new file mode 100644 index 00000000000..9c3517c0d4e Binary files /dev/null and b/apps/api/public/favicon.ico differ diff --git a/apps/api/src/app/api/trpc/[trpc]/route.ts b/apps/api/src/app/api/trpc/[trpc]/route.ts new file mode 100644 index 00000000000..44e37e48e6f --- /dev/null +++ b/apps/api/src/app/api/trpc/[trpc]/route.ts @@ -0,0 +1,18 @@ +import { appRouter, createTRPCContext } from "@superset/trpc"; +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; + +const handler = (req: Request) => + fetchRequestHandler({ + endpoint: "/api/trpc", + req, + router: appRouter, + createContext: () => createTRPCContext({ headers: req.headers }), + onError: ({ path, error }) => { + console.error(`โŒ tRPC error on ${path ?? ""}:`, error); + }, + }); + +// Preflight requests - CORS headers added by next.config.ts +export const OPTIONS = () => new Response(null, { status: 204 }); + +export { handler as GET, handler as POST }; diff --git a/apps/api/src/env.ts b/apps/api/src/env.ts new file mode 100644 index 00000000000..23f37b19c63 --- /dev/null +++ b/apps/api/src/env.ts @@ -0,0 +1,19 @@ +import { createEnv } from "@t3-oss/env-nextjs"; +import { z } from "zod"; + +export const env = createEnv({ + server: { + DATABASE_URL: z.string(), + DATABASE_URL_UNPOOLED: z.string(), + }, + client: { + NEXT_PUBLIC_WEB_URL: z.string().url(), + NEXT_PUBLIC_ADMIN_URL: z.string().url(), + }, + experimental__runtimeEnv: { + NEXT_PUBLIC_WEB_URL: process.env.NEXT_PUBLIC_WEB_URL, + NEXT_PUBLIC_ADMIN_URL: process.env.NEXT_PUBLIC_ADMIN_URL, + }, + emptyStringAsUndefined: true, + skipValidation: !!process.env.SKIP_ENV_VALIDATION, +}); diff --git a/apps/website/tsconfig.json b/apps/api/tsconfig.json similarity index 100% rename from apps/website/tsconfig.json rename to apps/api/tsconfig.json diff --git a/apps/blog/.gitignore b/apps/blog/.gitignore deleted file mode 100644 index 5ef6a520780..00000000000 --- a/apps/blog/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/apps/blog/README.md b/apps/blog/README.md deleted file mode 100644 index e215bc4ccf1..00000000000 --- a/apps/blog/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/apps/blog/mdx-components.tsx b/apps/blog/mdx-components.tsx deleted file mode 100644 index 34a2b1202cf..00000000000 --- a/apps/blog/mdx-components.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { useMDXComponents as getBlogMDXComponents } from "nextra-theme-blog"; - -export function useMDXComponents(components = {}) { - return getBlogMDXComponents(components); -} diff --git a/apps/blog/next.config.ts b/apps/blog/next.config.ts deleted file mode 100644 index 3856fd85073..00000000000 --- a/apps/blog/next.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { NextConfig } from "next"; -import nextra from "nextra"; - -const withNextra = nextra({ - defaultShowCopyCode: true, -}); - -const nextConfig: NextConfig = { - reactStrictMode: true, - transpilePackages: ["@superset/ui"], -}; - -export default withNextra(nextConfig); diff --git a/apps/blog/package.json b/apps/blog/package.json deleted file mode 100644 index 51ce6ff3829..00000000000 --- a/apps/blog/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@superset/blog", - "version": "0.1.0", - "private": true, - "scripts": { - "clean": "git clean -xdf .cache .next .turbo node_modules", - "dev": "next dev --port 3002", - "build": "next build", - "start": "next start", - "typecheck": "tsc --noEmit" - }, - "dependencies": { - "@react-three/drei": "^10.7.6", - "@react-three/fiber": "^9.4.0", - "@superset/ui": "workspace:*", - "framer-motion": "^12.23.24", - "geist": "^1.5.1", - "next": "^15.5.7", - "nextra": "^4.6.0", - "nextra-theme-blog": "^4.6.0", - "nextra-theme-docs": "^4.6.0", - "react": "^19.1.1", - "react-dom": "^19.1.1", - "three": "^0.181.2" - }, - "devDependencies": { - "@superset/typescript": "workspace:*", - "@tailwindcss/postcss": "^4.0.9", - "@types/mdx": "^2.0.13", - "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", - "@types/three": "^0.181.0", - "tailwindcss": "^4.0.9", - "typescript": "^5.9.3" - } -} diff --git a/apps/blog/public/file.svg b/apps/blog/public/file.svg deleted file mode 100644 index 004145cddf3..00000000000 --- a/apps/blog/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/blog/public/globe.svg b/apps/blog/public/globe.svg deleted file mode 100644 index 567f17b0d7c..00000000000 --- a/apps/blog/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/blog/public/next.svg b/apps/blog/public/next.svg deleted file mode 100644 index 5174b28c565..00000000000 --- a/apps/blog/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/blog/public/vercel.svg b/apps/blog/public/vercel.svg deleted file mode 100644 index 77053960334..00000000000 --- a/apps/blog/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/blog/public/window.svg b/apps/blog/public/window.svg deleted file mode 100644 index b2b2a44f6eb..00000000000 --- a/apps/blog/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/blog/src/app/building-with-nextra.mdx b/apps/blog/src/app/building-with-nextra.mdx deleted file mode 100644 index d0056bd055f..00000000000 --- a/apps/blog/src/app/building-with-nextra.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Building Documentation and Blogs with Nextra -date: 2025-01-20 -description: How we integrated Nextra for our documentation and blog -author: Superset Team ---- - -# Building with Nextra - -Today we're excited to share how we integrated Nextra into our project for both documentation and blog content. - -## Why Nextra? - -Nextra is a powerful Next.js-based framework that makes it easy to create beautiful documentation and blog sites: - -- **MDX Support** - Write content in Markdown with React components -- **Built-in Themes** - Docs and blog themes ready to use -- **Search** - Full-text search out of the box -- **Fast** - Built on Next.js App Router for optimal performance - -## Our Setup - -We've created two separate routes: - -### Documentation (`/docs`) - -Our documentation uses the `nextra-theme-docs` theme with: - -```tsx -import { Layout, Navbar, Footer } from "nextra-theme-docs"; -``` - -This provides a clean, organized layout perfect for technical documentation. - -### Blog (`/blog`) - -Our blog uses the `nextra-theme-blog` theme: - -```tsx -import { Layout, Navbar, Footer, ThemeSwitch } from "nextra-theme-blog"; -``` - -This gives us a beautiful blog interface with features like: -- Post listings -- Dark mode support -- RSS feed support -- Tag/category organization - -## Benefits - -The best part about this setup is that we can: - -1. **Write in Markdown** - No complex CMS needed -2. **Version Control** - All content lives in Git -3. **Type Safety** - TypeScript support throughout -4. **Fast Builds** - Optimized by Turborepo - -## Try It Yourself - -Check out the [Nextra documentation](https://nextra.site) to learn more about building with this amazing framework. diff --git a/apps/blog/src/app/favicon.ico b/apps/blog/src/app/favicon.ico deleted file mode 100644 index 6922b9cfbce..00000000000 Binary files a/apps/blog/src/app/favicon.ico and /dev/null differ diff --git a/apps/blog/src/app/globals.css b/apps/blog/src/app/globals.css deleted file mode 100644 index a904e3aa5ee..00000000000 --- a/apps/blog/src/app/globals.css +++ /dev/null @@ -1,8 +0,0 @@ -@import "tailwindcss"; -@import "@superset/ui/globals.css"; - -@theme { - --font-family-sans: - var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif; - --font-family-mono: var(--font-geist-mono), ui-monospace, monospace; -} diff --git a/apps/blog/src/app/hello-world.mdx b/apps/blog/src/app/hello-world.mdx deleted file mode 100644 index c86e32afa63..00000000000 --- a/apps/blog/src/app/hello-world.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Hello World - Welcome to Superset Blog -date: 2025-01-15 -description: Our first blog post introducing Superset and what's to come -author: Superset Team ---- - -# Hello World - -Welcome to the Superset blog! We're excited to share our journey with you. - -## What is Superset? - -Superset is a modern web development platform built with the latest technologies: - -- **Next.js 15** - The React framework for production -- **TailwindCSS v4** - Utility-first CSS framework -- **Turborepo** - High-performance build system -- **TypeScript** - Type-safe development - -## What to Expect - -In this blog, we'll share: - -1. **Technical insights** - Deep dives into our architecture and design decisions -2. **Updates** - New features and improvements -3. **Tutorials** - How-to guides and best practices -4. **Community** - Highlighting contributions and use cases - -## Get Started - -Check out our [documentation](/docs) to learn more about using Superset in your projects. - -Stay tuned for more exciting content! diff --git a/apps/blog/src/app/layout.tsx b/apps/blog/src/app/layout.tsx deleted file mode 100644 index a34f506098f..00000000000 --- a/apps/blog/src/app/layout.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Banner, Search } from "nextra/components"; -import { getPageMap } from "nextra/page-map"; -import { Footer, Layout, Navbar, ThemeSwitch } from "nextra-theme-blog"; -import type * as React from "react"; -import "nextra-theme-blog/style.css"; - -export const metadata = { - title: "Superset Blog", - description: "Latest news and updates from Superset", -}; - -const banner = ( - - Welcome to the Superset Blog - -); - -export default async function BlogLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( - - - - - - - - {children} -
-

ยฉ {new Date().getFullYear()} Superset. All rights reserved.

-
-
- - - ); -} diff --git a/apps/blog/src/app/page.mdx b/apps/blog/src/app/page.mdx deleted file mode 100644 index 28256fcee40..00000000000 --- a/apps/blog/src/app/page.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -type: posts ---- - -# Superset Blog - -Welcome to the Superset blog! Here you'll find the latest news, updates, and insights about Superset. - -Stay tuned for exciting content about web development, best practices, and new features. diff --git a/apps/blog/src/components/Footer.tsx b/apps/blog/src/components/Footer.tsx deleted file mode 100644 index a87e398c052..00000000000 --- a/apps/blog/src/components/Footer.tsx +++ /dev/null @@ -1,80 +0,0 @@ -"use client"; - -import { motion } from "framer-motion"; - -const FOOTER_LINKS = { - Product: [ - { label: "Pricing", href: "#" }, - { label: "Features", href: "#" }, - { label: "Download", href: "#" }, - ], - Company: [ - { label: "About", href: "#" }, - { label: "Careers", href: "#" }, - { label: "Contact", href: "#" }, - ], - Resources: [ - { label: "Documentation", href: "#" }, - { label: "Support", href: "#" }, - { label: "Privacy", href: "#" }, - ], - Connect: [ - { label: "GitHub", href: "#" }, - { label: "Twitter", href: "#" }, - { label: "Discord", href: "#" }, - ], -} as const; - -export function Footer() { - return ( -
-
- {/* Main footer content */} -
- {Object.entries(FOOTER_LINKS).map(([category, links], idx) => ( - -

- {category} -

- -
- ))} -
- - {/* Bottom section */} - -
- โЇ - Superset -
-

- ยฉ {new Date().getFullYear()} Superset. All rights reserved. -

-
-
-
- ); -} diff --git a/apps/blog/src/components/Header.tsx b/apps/blog/src/components/Header.tsx deleted file mode 100644 index cad5b01d36b..00000000000 --- a/apps/blog/src/components/Header.tsx +++ /dev/null @@ -1,142 +0,0 @@ -"use client"; - -import { motion } from "framer-motion"; -import Link from "next/link"; -import { useState } from "react"; - -const NAV_LINKS = [ - { label: "Features", href: "#" }, - { label: "Product", href: "#" }, - { label: "Company", href: "#" }, - { label: "Resources", href: "#" }, -] as const; - -export function Header() { - const [isMenuOpen, setIsMenuOpen] = useState(false); - - return ( -
- -
- ); -} diff --git a/apps/blog/src/components/motion/FadeUp.tsx b/apps/blog/src/components/motion/FadeUp.tsx deleted file mode 100644 index 652d2d795b4..00000000000 --- a/apps/blog/src/components/motion/FadeUp.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client"; - -import { motion, type Variants } from "framer-motion"; -import type { ReactNode } from "react"; - -interface FadeUpProps { - children: ReactNode; - delay?: number; - duration?: number; - className?: string; -} - -const fadeUpVariants: Variants = { - hidden: { - opacity: 0, - y: 24, - }, - visible: { - opacity: 1, - y: 0, - }, -}; - -export function FadeUp({ - children, - delay = 0, - duration = 0.5, - className, -}: FadeUpProps) { - return ( - - {children} - - ); -} diff --git a/apps/blog/src/components/motion/HeroParallax.tsx b/apps/blog/src/components/motion/HeroParallax.tsx deleted file mode 100644 index d2c911a85a1..00000000000 --- a/apps/blog/src/components/motion/HeroParallax.tsx +++ /dev/null @@ -1,63 +0,0 @@ -"use client"; - -import { motion, useScroll, useTransform } from "framer-motion"; -import type { ReactNode } from "react"; -import { createContext, useContext, useEffect, useRef, useState } from "react"; - -interface HeroParallaxProps { - children: ReactNode; - speed?: number; - className?: string; -} - -// Context to share visibility state with Three.js canvas -const HeroVisibilityContext = createContext(true); - -export function useHeroVisibility() { - return useContext(HeroVisibilityContext); -} - -export function HeroParallax({ children, className }: HeroParallaxProps) { - const ref = useRef(null); - const [isVisible, setIsVisible] = useState(true); - - const { scrollYProgress } = useScroll({ - target: ref, - offset: ["start start", "end start"], - }); - - const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [1, 0.5, 0]); - - // Track visibility with Intersection Observer - useEffect(() => { - const element = ref.current; - if (!element) return; - - const observer = new IntersectionObserver( - (entries) => { - const entry = entries[0]; - if (entry) { - setIsVisible(entry.isIntersecting); - } - }, - { - threshold: 0, - rootMargin: "100px", // Start rendering slightly before entering viewport - }, - ); - - observer.observe(element); - - return () => { - observer.disconnect(); - }; - }, []); - - return ( - -
- {children} -
-
- ); -} diff --git a/apps/blog/src/components/motion/TiltCard.tsx b/apps/blog/src/components/motion/TiltCard.tsx deleted file mode 100644 index b42cb491917..00000000000 --- a/apps/blog/src/components/motion/TiltCard.tsx +++ /dev/null @@ -1,56 +0,0 @@ -"use client"; - -import { motion, useMotionTemplate, useMotionValue } from "framer-motion"; -import type { ReactNode } from "react"; - -interface TiltCardProps { - children: ReactNode; - className?: string; -} - -export function TiltCard({ children, className }: TiltCardProps) { - const mouseX = useMotionValue(0); - const mouseY = useMotionValue(0); - - const rotateX = useMotionTemplate`${mouseY}deg`; - const rotateY = useMotionTemplate`${mouseX}deg`; - - function handleMouseMove(event: React.MouseEvent) { - const rect = event.currentTarget.getBoundingClientRect(); - const width = rect.width; - const height = rect.height; - const mouseXPos = event.clientX - rect.left; - const mouseYPos = event.clientY - rect.top; - - const xPct = mouseXPos / width - 0.5; - const yPct = mouseYPos / height - 0.5; - - mouseX.set(xPct * 20); - mouseY.set(yPct * -20); - } - - function handleMouseLeave() { - mouseX.set(0); - mouseY.set(0); - } - - return ( - -
{children}
-
- ); -} diff --git a/apps/blog/src/components/motion/index.ts b/apps/blog/src/components/motion/index.ts deleted file mode 100644 index c6a56bb63bf..00000000000 --- a/apps/blog/src/components/motion/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { FadeUp } from "./FadeUp"; -export { HeroParallax } from "./HeroParallax"; -export { TiltCard } from "./TiltCard"; diff --git a/apps/blog/src/components/three/HeroCanvas.tsx b/apps/blog/src/components/three/HeroCanvas.tsx deleted file mode 100644 index 640ab7afea8..00000000000 --- a/apps/blog/src/components/three/HeroCanvas.tsx +++ /dev/null @@ -1,327 +0,0 @@ -"use client"; - -import { Text } from "@react-three/drei"; -import { Canvas, useFrame, useThree } from "@react-three/fiber"; -import { Suspense, useMemo, useRef } from "react"; -import type { Mesh, PointLight } from "three"; -import * as THREE from "three"; -import { useHeroVisibility } from "../motion/HeroParallax"; - -// Configuration constants -const LIGHT_CONFIG = { - INTENSITY: 25, - Z_POSITION: 2, - DEFAULT_X_RATIO: 0.18, - DEFAULT_Y_RATIO: 0.01, - HUE_MIN: 180, - HUE_MAX: 270, - SATURATION_MIN: 60, - SATURATION_MAX: 100, - LIGHTNESS: 65, -} as const; - -const MATERIAL_CONFIG = { - BASE_COLOR: "#1a1a1a", - ROUGHNESS: 0.8, - METALNESS: 0.2, - AMBIENT_INTENSITY: 0.95, - DIFFUSE_INTENSITY: 0.15, - SPECULAR_INTENSITY: 0.05, - ATTENUATION_LINEAR: 0.1, - ATTENUATION_QUADRATIC: 0.05, -} as const; - -const TEXT_CONFIG = { - POSITION: [0, 0.5, 1] as [number, number, number], - FONT_SIZE: 1.8, - FONT_SIZE_OUTER: 1.805, - LAYER_COUNT: 15, - LAYER_SPACING: 0.05, - COLOR: "#2c3539", - METALNESS: 0.85, - ROUGHNESS: 0.25, -} as const; - -const GEOMETRY_CONFIG = { - PLANE_SIZE_MULTIPLIER: 1.5, - PLANE_SEGMENTS: 40, -} as const; - -// Custom shader for GPU-accelerated wave animation -const waveVertexShader = ` - uniform float uTime; - varying vec3 vNormal; - varying vec3 vPosition; - - void main() { - vec3 pos = position; - - // Create wave effect using sine waves - float wave1 = sin(pos.x * 0.5 + uTime * 0.5) * 0.1; - float wave2 = sin(pos.y * 0.5 + uTime * 0.3) * 0.1; - pos.z += wave1 + wave2; - - // Calculate normal based on wave derivatives for proper lighting - float dx = cos(pos.x * 0.5 + uTime * 0.5) * 0.05; - float dy = cos(pos.y * 0.5 + uTime * 0.3) * 0.05; - vec3 computedNormal = normalize(vec3(-dx, -dy, 1.0)); - - vNormal = normalize(normalMatrix * computedNormal); - vPosition = (modelViewMatrix * vec4(pos, 1.0)).xyz; - gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0); - } -`; - -const waveFragmentShader = ` - uniform vec3 uColor; - uniform float uRoughness; - uniform float uMetalness; - uniform vec3 uLightPosition; - uniform vec3 uLightColor; - uniform float uLightIntensity; - varying vec3 vNormal; - varying vec3 vPosition; - - void main() { - // Calculate light direction and distance - vec3 lightDir = uLightPosition - vPosition; - float distance = length(lightDir); - lightDir = normalize(lightDir); - - // Attenuation (light falloff) - much stronger falloff - float attenuation = uLightIntensity / (1.0 + 0.1 * distance + 0.05 * distance * distance); - - // Diffuse lighting - very subtle - float diff = max(dot(vNormal, lightDir), 0.0); - - // Specular lighting - very subtle - vec3 viewDir = normalize(-vPosition); - vec3 halfDir = normalize(lightDir + viewDir); - float spec = pow(max(dot(vNormal, halfDir), 0.0), 32.0); - - // Combine lighting - much more subtle effect - vec3 ambient = uColor * 0.95; // Mostly base color - vec3 diffuse = uColor * diff * uLightColor * attenuation * 0.15; // Very subtle diffuse - vec3 specular = uLightColor * spec * attenuation * 0.05 * (1.0 - uRoughness); // Very subtle specular - - vec3 finalColor = ambient + diffuse + specular; - gl_FragColor = vec4(finalColor, 1.0); - } -`; - -function LitBackground() { - const meshRef = useRef(null); - const lightRef = useRef(null); - const textGroupRef = useRef(null); - const { viewport, camera } = useThree(); - const isVisible = useHeroVisibility(); - - // Create shader material with uniforms - const shaderMaterial = useMemo( - () => ({ - uniforms: { - uTime: { value: 0 }, - uColor: { value: new THREE.Color(MATERIAL_CONFIG.BASE_COLOR) }, - uRoughness: { value: MATERIAL_CONFIG.ROUGHNESS }, - uMetalness: { value: MATERIAL_CONFIG.METALNESS }, - uLightPosition: { - value: new THREE.Vector3(0, 0, LIGHT_CONFIG.Z_POSITION), - }, - uLightColor: { value: new THREE.Color("#ffffff") }, - uLightIntensity: { value: LIGHT_CONFIG.INTENSITY }, - }, - vertexShader: waveVertexShader, - fragmentShader: waveFragmentShader, - }), - [], - ); - - useFrame((state) => { - // Skip expensive operations when hero is not visible - if (!isVisible) return; - - // Check if mouse has moved (not at default 0,0) - const hasMouseMoved = state.mouse.x !== 0 || state.mouse.y !== 0; - - // Convert normalized mouse coords to full viewport range, or use default position - const x = hasMouseMoved - ? state.mouse.x * viewport.width - : viewport.width * LIGHT_CONFIG.DEFAULT_X_RATIO; - const y = hasMouseMoved - ? state.mouse.y * viewport.height - : viewport.height * LIGHT_CONFIG.DEFAULT_Y_RATIO; - - // Change color based on position - cooler palette (blue to cyan to purple) - const hue = - LIGHT_CONFIG.HUE_MIN + - ((state.mouse.x + 1) / 2) * (LIGHT_CONFIG.HUE_MAX - LIGHT_CONFIG.HUE_MIN); - const saturation = - LIGHT_CONFIG.SATURATION_MIN + - ((state.mouse.y + 1) / 2) * - (LIGHT_CONFIG.SATURATION_MAX - LIGHT_CONFIG.SATURATION_MIN); - const lightness = LIGHT_CONFIG.LIGHTNESS; - - if (lightRef.current) { - // Position light slightly in front of the plane - lightRef.current.position.set(x, y, LIGHT_CONFIG.Z_POSITION); - lightRef.current.color.setHSL( - hue / 360, - saturation / 100, - lightness / 100, - ); - } - - // Make the text group face the camera - if (textGroupRef.current) { - textGroupRef.current.lookAt(camera.position); - } - - // Make the plane always face the camera and update shader uniforms - if (meshRef.current) { - meshRef.current.lookAt(camera.position); - - // Update shader uniforms (GPU handles the animation and lighting) - const material = meshRef.current.material as THREE.ShaderMaterial; - if (material.uniforms) { - if (material.uniforms.uTime) { - material.uniforms.uTime.value = state.clock.elapsedTime; - } - // Update light position and color in shader - if (material.uniforms.uLightPosition) { - material.uniforms.uLightPosition.value.set( - x, - y, - LIGHT_CONFIG.Z_POSITION, - ); - } - if (material.uniforms.uLightColor) { - material.uniforms.uLightColor.value.setHSL( - hue / 360, - saturation / 100, - lightness / 100, - ); - } - } - } - }); - - return ( - <> - {/* Background plane that fills the viewport and faces camera */} - - - - - - {/* 3D Text that reacts to light */} - - {/* Outer edge layer - highly metallic */} - - โЇ - - - - {/* Create depth by layering multiple text instances - reduced from 30 to 15 for performance */} - {Array.from({ length: TEXT_CONFIG.LAYER_COUNT }, (_, i) => ( - - โЇ - - - ))} - - - {/* Ambient light for base visibility */} - - - {/* Static directional lights for consistent highlights */} - - - - {/* Point light that follows mouse */} - - - ); -} - -interface HeroCanvasProps { - className?: string; -} - -export function HeroCanvas({ className }: HeroCanvasProps) { - return ( -
- - - - - -
- ); -} diff --git a/apps/blog/src/components/three/index.ts b/apps/blog/src/components/three/index.ts deleted file mode 100644 index dc3fd4af5fd..00000000000 --- a/apps/blog/src/components/three/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { HeroCanvas } from "./HeroCanvas"; diff --git a/apps/docs/package.json b/apps/docs/package.json index 7e65907d9fb..7b17da44ed8 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "clean": "git clean -xdf .cache .next .turbo node_modules", - "dev": "next dev --port 3001", + "dev": "next dev --port 3004", "build": "next build", "start": "next start", "typecheck": "tsc --noEmit" diff --git a/apps/docs/public/favicon.ico b/apps/docs/public/favicon.ico new file mode 100644 index 00000000000..9c3517c0d4e Binary files /dev/null and b/apps/docs/public/favicon.ico differ diff --git a/apps/marketing/next-env.d.ts b/apps/marketing/next-env.d.ts new file mode 100644 index 00000000000..830fb594ca2 --- /dev/null +++ b/apps/marketing/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/marketing/next.config.ts b/apps/marketing/next.config.ts new file mode 100644 index 00000000000..3b3de1463ba --- /dev/null +++ b/apps/marketing/next.config.ts @@ -0,0 +1,11 @@ +import type { NextConfig } from "next"; + +const config: NextConfig = { + reactStrictMode: true, + experimental: { + reactCompiler: true, + }, + typescript: { ignoreBuildErrors: true }, +}; + +export default config; diff --git a/apps/website/package.json b/apps/marketing/package.json similarity index 78% rename from apps/website/package.json rename to apps/marketing/package.json index 65071371dcb..f843bfa025b 100644 --- a/apps/website/package.json +++ b/apps/marketing/package.json @@ -1,33 +1,29 @@ { - "name": "@superset/website", + "name": "@superset/marketing", "version": "0.1.0", "private": true, + "type": "module", "scripts": { - "clean": "git clean -xdf .cache .next .turbo node_modules", - "dev": "next dev", "build": "next build", + "clean": "git clean -xdf .cache .next .turbo node_modules", + "dev": "next dev --port 3002", "start": "next start", "typecheck": "tsc --noEmit" }, "dependencies": { "@react-three/drei": "^10.7.6", "@react-three/fiber": "^9.4.0", - "@superset/trpc": "workspace:*", "@superset/ui": "workspace:*", "@t3-oss/env-nextjs": "^0.13.8", - "@tanstack/react-query": "^5.90.10", - "@trpc/client": "^11.7.1", - "@trpc/server": "^11.7.1", - "@trpc/tanstack-react-query": "^11.7.1", "framer-motion": "^12.23.24", "geist": "^1.5.1", + "lucide-react": "^0.560.0", "next": "^15.5.7", "next-themes": "^0.4.6", "react": "^19.1.1", "react-dom": "^19.1.1", "react-fast-marquee": "^1.6.5", "react-icons": "^5.5.0", - "superjson": "^2.2.5", "three": "^0.181.2", "zod": "^4.1.13" }, @@ -39,6 +35,7 @@ "@types/react": "^19.1.11", "@types/react-dom": "^19.1.7", "@types/three": "^0.181.0", + "babel-plugin-react-compiler": "^1.0.0", "tailwindcss": "^4.0.9", "typescript": "^5.9.3" } diff --git a/apps/website/postcss.config.mjs b/apps/marketing/postcss.config.mjs similarity index 54% rename from apps/website/postcss.config.mjs rename to apps/marketing/postcss.config.mjs index c42f31cab8a..017b34b9cb3 100644 --- a/apps/website/postcss.config.mjs +++ b/apps/marketing/postcss.config.mjs @@ -1,7 +1,5 @@ -const config = { +export default { plugins: { "@tailwindcss/postcss": {}, }, }; - -export default config; diff --git a/apps/marketing/public/favicon.ico b/apps/marketing/public/favicon.ico new file mode 100644 index 00000000000..9c3517c0d4e Binary files /dev/null and b/apps/marketing/public/favicon.ico differ diff --git a/apps/website/public/hero/change-themes.gif b/apps/marketing/public/hero/change-themes.gif similarity index 100% rename from apps/website/public/hero/change-themes.gif rename to apps/marketing/public/hero/change-themes.gif diff --git a/apps/website/public/hero/manage-terminals.gif b/apps/marketing/public/hero/manage-terminals.gif similarity index 100% rename from apps/website/public/hero/manage-terminals.gif rename to apps/marketing/public/hero/manage-terminals.gif diff --git a/apps/website/public/hero/open-worktrees.gif b/apps/marketing/public/hero/open-worktrees.gif similarity index 100% rename from apps/website/public/hero/open-worktrees.gif rename to apps/marketing/public/hero/open-worktrees.gif diff --git a/apps/website/public/hero/use-agents.gif b/apps/marketing/public/hero/use-agents.gif similarity index 100% rename from apps/website/public/hero/use-agents.gif rename to apps/marketing/public/hero/use-agents.gif diff --git a/apps/website/public/title.svg b/apps/marketing/public/title.svg similarity index 100% rename from apps/website/public/title.svg rename to apps/marketing/public/title.svg diff --git a/apps/website/src/app/components/CTASection/CTASection.tsx b/apps/marketing/src/app/components/CTASection/CTASection.tsx similarity index 100% rename from apps/website/src/app/components/CTASection/CTASection.tsx rename to apps/marketing/src/app/components/CTASection/CTASection.tsx diff --git a/apps/website/src/app/components/CTASection/index.ts b/apps/marketing/src/app/components/CTASection/index.ts similarity index 100% rename from apps/website/src/app/components/CTASection/index.ts rename to apps/marketing/src/app/components/CTASection/index.ts diff --git a/apps/website/src/app/components/ClientLogosSection/ClientLogosSection.tsx b/apps/marketing/src/app/components/ClientLogosSection/ClientLogosSection.tsx similarity index 100% rename from apps/website/src/app/components/ClientLogosSection/ClientLogosSection.tsx rename to apps/marketing/src/app/components/ClientLogosSection/ClientLogosSection.tsx diff --git a/apps/website/src/app/components/ClientLogosSection/constants.ts b/apps/marketing/src/app/components/ClientLogosSection/constants.ts similarity index 100% rename from apps/website/src/app/components/ClientLogosSection/constants.ts rename to apps/marketing/src/app/components/ClientLogosSection/constants.ts diff --git a/apps/website/src/app/components/ClientLogosSection/index.ts b/apps/marketing/src/app/components/ClientLogosSection/index.ts similarity index 100% rename from apps/website/src/app/components/ClientLogosSection/index.ts rename to apps/marketing/src/app/components/ClientLogosSection/index.ts diff --git a/apps/website/src/app/components/DownloadButton/DownloadButton.tsx b/apps/marketing/src/app/components/DownloadButton/DownloadButton.tsx similarity index 100% rename from apps/website/src/app/components/DownloadButton/DownloadButton.tsx rename to apps/marketing/src/app/components/DownloadButton/DownloadButton.tsx diff --git a/apps/website/src/app/components/DownloadButton/index.ts b/apps/marketing/src/app/components/DownloadButton/index.ts similarity index 100% rename from apps/website/src/app/components/DownloadButton/index.ts rename to apps/marketing/src/app/components/DownloadButton/index.ts diff --git a/apps/website/src/app/components/Footer/Footer.tsx b/apps/marketing/src/app/components/Footer/Footer.tsx similarity index 100% rename from apps/website/src/app/components/Footer/Footer.tsx rename to apps/marketing/src/app/components/Footer/Footer.tsx diff --git a/apps/website/src/app/components/Footer/index.ts b/apps/marketing/src/app/components/Footer/index.ts similarity index 100% rename from apps/website/src/app/components/Footer/index.ts rename to apps/marketing/src/app/components/Footer/index.ts diff --git a/apps/website/src/app/components/Header/Header.tsx b/apps/marketing/src/app/components/Header/Header.tsx similarity index 100% rename from apps/website/src/app/components/Header/Header.tsx rename to apps/marketing/src/app/components/Header/Header.tsx diff --git a/apps/website/src/app/components/Header/index.ts b/apps/marketing/src/app/components/Header/index.ts similarity index 100% rename from apps/website/src/app/components/Header/index.ts rename to apps/marketing/src/app/components/Header/index.ts diff --git a/apps/website/src/app/components/HeroSection/HeroSection.tsx b/apps/marketing/src/app/components/HeroSection/HeroSection.tsx similarity index 100% rename from apps/website/src/app/components/HeroSection/HeroSection.tsx rename to apps/marketing/src/app/components/HeroSection/HeroSection.tsx diff --git a/apps/website/src/app/components/HeroSection/index.ts b/apps/marketing/src/app/components/HeroSection/index.ts similarity index 100% rename from apps/website/src/app/components/HeroSection/index.ts rename to apps/marketing/src/app/components/HeroSection/index.ts diff --git a/apps/website/src/app/components/JoinWaitlistButton/JoinWaitlistButton.tsx b/apps/marketing/src/app/components/JoinWaitlistButton/JoinWaitlistButton.tsx similarity index 100% rename from apps/website/src/app/components/JoinWaitlistButton/JoinWaitlistButton.tsx rename to apps/marketing/src/app/components/JoinWaitlistButton/JoinWaitlistButton.tsx diff --git a/apps/website/src/app/components/JoinWaitlistButton/index.ts b/apps/marketing/src/app/components/JoinWaitlistButton/index.ts similarity index 100% rename from apps/website/src/app/components/JoinWaitlistButton/index.ts rename to apps/marketing/src/app/components/JoinWaitlistButton/index.ts diff --git a/apps/website/src/app/components/PlatformDropdown/PlatformDropdown.tsx b/apps/marketing/src/app/components/PlatformDropdown/PlatformDropdown.tsx similarity index 100% rename from apps/website/src/app/components/PlatformDropdown/PlatformDropdown.tsx rename to apps/marketing/src/app/components/PlatformDropdown/PlatformDropdown.tsx diff --git a/apps/website/src/app/components/PlatformDropdown/index.ts b/apps/marketing/src/app/components/PlatformDropdown/index.ts similarity index 100% rename from apps/website/src/app/components/PlatformDropdown/index.ts rename to apps/marketing/src/app/components/PlatformDropdown/index.ts diff --git a/apps/website/src/app/components/SecuritySection/SecuritySection.tsx b/apps/marketing/src/app/components/SecuritySection/SecuritySection.tsx similarity index 100% rename from apps/website/src/app/components/SecuritySection/SecuritySection.tsx rename to apps/marketing/src/app/components/SecuritySection/SecuritySection.tsx diff --git a/apps/website/src/app/components/SecuritySection/index.ts b/apps/marketing/src/app/components/SecuritySection/index.ts similarity index 100% rename from apps/website/src/app/components/SecuritySection/index.ts rename to apps/marketing/src/app/components/SecuritySection/index.ts diff --git a/apps/website/src/app/components/SocialLinks/SocialLinks.tsx b/apps/marketing/src/app/components/SocialLinks/SocialLinks.tsx similarity index 100% rename from apps/website/src/app/components/SocialLinks/SocialLinks.tsx rename to apps/marketing/src/app/components/SocialLinks/SocialLinks.tsx diff --git a/apps/website/src/app/components/SocialLinks/index.ts b/apps/marketing/src/app/components/SocialLinks/index.ts similarity index 100% rename from apps/website/src/app/components/SocialLinks/index.ts rename to apps/marketing/src/app/components/SocialLinks/index.ts diff --git a/apps/website/src/app/components/TrustedBySection/TrustedBySection.tsx b/apps/marketing/src/app/components/TrustedBySection/TrustedBySection.tsx similarity index 100% rename from apps/website/src/app/components/TrustedBySection/TrustedBySection.tsx rename to apps/marketing/src/app/components/TrustedBySection/TrustedBySection.tsx diff --git a/apps/website/src/app/components/TrustedBySection/index.ts b/apps/marketing/src/app/components/TrustedBySection/index.ts similarity index 100% rename from apps/website/src/app/components/TrustedBySection/index.ts rename to apps/marketing/src/app/components/TrustedBySection/index.ts diff --git a/apps/website/src/app/components/VideoSection/VideoSection.tsx b/apps/marketing/src/app/components/VideoSection/VideoSection.tsx similarity index 100% rename from apps/website/src/app/components/VideoSection/VideoSection.tsx rename to apps/marketing/src/app/components/VideoSection/VideoSection.tsx diff --git a/apps/website/src/app/components/VideoSection/index.ts b/apps/marketing/src/app/components/VideoSection/index.ts similarity index 100% rename from apps/website/src/app/components/VideoSection/index.ts rename to apps/marketing/src/app/components/VideoSection/index.ts diff --git a/apps/website/src/app/components/WaitlistModal/WaitlistModal.tsx b/apps/marketing/src/app/components/WaitlistModal/WaitlistModal.tsx similarity index 100% rename from apps/website/src/app/components/WaitlistModal/WaitlistModal.tsx rename to apps/marketing/src/app/components/WaitlistModal/WaitlistModal.tsx diff --git a/apps/website/src/app/components/WaitlistModal/index.ts b/apps/marketing/src/app/components/WaitlistModal/index.ts similarity index 100% rename from apps/website/src/app/components/WaitlistModal/index.ts rename to apps/marketing/src/app/components/WaitlistModal/index.ts diff --git a/apps/website/src/app/globals.css b/apps/marketing/src/app/globals.css similarity index 100% rename from apps/website/src/app/globals.css rename to apps/marketing/src/app/globals.css diff --git a/apps/website/src/app/layout.tsx b/apps/marketing/src/app/layout.tsx similarity index 92% rename from apps/website/src/app/layout.tsx rename to apps/marketing/src/app/layout.tsx index 4f606677b07..ec02542c036 100644 --- a/apps/website/src/app/layout.tsx +++ b/apps/marketing/src/app/layout.tsx @@ -2,7 +2,6 @@ import type { Metadata } from "next"; import { IBM_Plex_Mono, Inter } from "next/font/google"; import Script from "next/script"; import { ThemeProvider } from "next-themes"; -import { TRPCReactProvider } from "@/trpc/react"; import "./globals.css"; const ibmPlexMono = IBM_Plex_Mono({ @@ -42,7 +41,7 @@ export default function RootLayout({ - {children} + {children} diff --git a/apps/website/src/app/page.tsx b/apps/marketing/src/app/page.tsx similarity index 100% rename from apps/website/src/app/page.tsx rename to apps/marketing/src/app/page.tsx diff --git a/apps/website/src/app/privacy-policy/page.tsx b/apps/marketing/src/app/privacy-policy/page.tsx similarity index 100% rename from apps/website/src/app/privacy-policy/page.tsx rename to apps/marketing/src/app/privacy-policy/page.tsx diff --git a/apps/website/src/app/scripts/page.tsx b/apps/marketing/src/app/scripts/page.tsx similarity index 100% rename from apps/website/src/app/scripts/page.tsx rename to apps/marketing/src/app/scripts/page.tsx diff --git a/apps/website/src/app/terms-of-service/page.tsx b/apps/marketing/src/app/terms-of-service/page.tsx similarity index 100% rename from apps/website/src/app/terms-of-service/page.tsx rename to apps/marketing/src/app/terms-of-service/page.tsx diff --git a/apps/website/src/constants.ts b/apps/marketing/src/constants.ts similarity index 100% rename from apps/website/src/constants.ts rename to apps/marketing/src/constants.ts diff --git a/apps/website/src/env.ts b/apps/marketing/src/env.ts similarity index 67% rename from apps/website/src/env.ts rename to apps/marketing/src/env.ts index 4ebd90a0753..154576f6fb0 100644 --- a/apps/website/src/env.ts +++ b/apps/marketing/src/env.ts @@ -9,14 +9,13 @@ export const env = createEnv({ .enum(["development", "production", "test"]) .default("development"), }, - server: { - DATABASE_URL: z.url(), + server: {}, + client: { + NEXT_PUBLIC_API_URL: z.string().url(), }, - client: {}, - /** - * Destructure all variables from `process.env` to make sure they aren't tree-shaken away. - */ experimental__runtimeEnv: { NODE_ENV: process.env.NODE_ENV, + NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, }, + skipValidation: !!process.env.SKIP_ENV_VALIDATION, }); diff --git a/apps/marketing/tsconfig.json b/apps/marketing/tsconfig.json new file mode 100644 index 00000000000..600980abb48 --- /dev/null +++ b/apps/marketing/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@superset/typescript/next.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts new file mode 100644 index 00000000000..830fb594ca2 --- /dev/null +++ b/apps/web/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts new file mode 100644 index 00000000000..737f1468308 --- /dev/null +++ b/apps/web/next.config.ts @@ -0,0 +1,10 @@ +import type { NextConfig } from "next"; + +const config: NextConfig = { + experimental: { + reactCompiler: true, + }, + typescript: { ignoreBuildErrors: true }, +}; + +export default config; diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 00000000000..8de561eb4a4 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,45 @@ +{ + "name": "@superset/web", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "build": "next build", + "clean": "git clean -xdf .cache .next .turbo node_modules", + "dev": "next dev", + "start": "next start", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@superset/db": "workspace:*", + "@superset/queries": "workspace:*", + "@superset/shared": "workspace:*", + "@superset/trpc": "workspace:*", + "@superset/ui": "workspace:*", + "@t3-oss/env-nextjs": "^0.13.8", + "@tanstack/react-query": "^5.90.10", + "@tanstack/react-query-devtools": "^5.90.10", + "@trpc/client": "^11.7.1", + "@trpc/server": "^11.7.1", + "@trpc/tanstack-react-query": "^11.7.1", + "geist": "^1.5.1", + "lucide-react": "^0.560.0", + "next": "^15.5.7", + "next-themes": "^0.4.6", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "server-only": "^0.0.1", + "superjson": "^2.2.5", + "zod": "^4.1.13" + }, + "devDependencies": { + "@superset/typescript": "workspace:*", + "@tailwindcss/postcss": "^4.0.9", + "@types/node": "^24.9.1", + "@types/react": "^19.1.11", + "@types/react-dom": "^19.1.7", + "babel-plugin-react-compiler": "^1.0.0", + "tailwindcss": "^4.0.9", + "typescript": "^5.9.3" + } +} diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs new file mode 100644 index 00000000000..017b34b9cb3 --- /dev/null +++ b/apps/web/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git a/apps/web/public/favicon.ico b/apps/web/public/favicon.ico new file mode 100644 index 00000000000..9c3517c0d4e Binary files /dev/null and b/apps/web/public/favicon.ico differ diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css new file mode 100644 index 00000000000..ee297b51232 --- /dev/null +++ b/apps/web/src/app/globals.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; + +@import "@superset/ui/globals.css"; +@source "../../../../packages/ui/src/**/*.{ts,tsx}"; diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx new file mode 100644 index 00000000000..6614041bfbb --- /dev/null +++ b/apps/web/src/app/layout.tsx @@ -0,0 +1,44 @@ +import { Toaster } from "@superset/ui/sonner"; +import { cn } from "@superset/ui/utils"; +import { GeistMono } from "geist/font/mono"; +import { GeistSans } from "geist/font/sans"; +import type { Metadata, Viewport } from "next"; + +import "./globals.css"; + +import { Providers } from "./providers"; + +export const metadata: Metadata = { + title: "Superset", + description: "Run 10+ parallel coding agents on your machine", +}; + +export const viewport: Viewport = { + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "white" }, + { media: "(prefers-color-scheme: dark)", color: "black" }, + ], +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + {children} + + + + + ); +} diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx new file mode 100644 index 00000000000..4e154f2cd0c --- /dev/null +++ b/apps/web/src/app/page.tsx @@ -0,0 +1,58 @@ +import { api } from "@/trpc/server"; + +export default async function Home() { + const trpc = await api(); + + const users = await trpc.user.all.query(); + + let me = null; + let authError = null; + try { + me = await trpc.user.me.query(); + } catch (e) { + authError = e instanceof Error ? e.message : "Unknown error"; + } + + return ( +
+

Superset Web

+

+ Run 10+ parallel coding agents on your machine +

+ +
+
+

Public Query

+

user.all()

+

+ Users in database: {users.length} +

+ {users.length > 0 && ( +
    + {users.slice(0, 3).map((user) => ( +
  • {user.email}
  • + ))} +
+ )} +
+ +
+

Protected Query

+

user.me()

+ {authError ? ( +

Error: {authError}

+ ) : me ? ( +
+

Logged in as: {me.email}

+

{me.name}

+
+ ) : ( +

+ No user found for MOCK_USER_ID +

+ )} +
+
+
+ ); +} diff --git a/apps/web/src/app/providers.tsx b/apps/web/src/app/providers.tsx new file mode 100644 index 00000000000..aa341abf5cf --- /dev/null +++ b/apps/web/src/app/providers.tsx @@ -0,0 +1,22 @@ +"use client"; + +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { ThemeProvider } from "next-themes"; + +import { TRPCReactProvider } from "../trpc/react"; + +export function Providers({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + + ); +} diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts new file mode 100644 index 00000000000..0f392e34f12 --- /dev/null +++ b/apps/web/src/env.ts @@ -0,0 +1,34 @@ +import { createEnv } from "@t3-oss/env-nextjs"; +import { vercel } from "@t3-oss/env-nextjs/presets-zod"; +import { z } from "zod"; + +export const env = createEnv({ + extends: [vercel()], + shared: { + NODE_ENV: z + .enum(["development", "production", "test"]) + .default("development"), + }, + + server: { + // Database (needed by @superset/trpc dependency) + DATABASE_URL: z.string().url(), + DATABASE_URL_UNPOOLED: z.string().url(), + }, + + client: { + NEXT_PUBLIC_API_URL: z.string().url(), + NEXT_PUBLIC_MARKETING_URL: z.string().url(), + NEXT_PUBLIC_DOCS_URL: z.string().url(), + }, + + experimental__runtimeEnv: { + NODE_ENV: process.env.NODE_ENV, + NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, + NEXT_PUBLIC_MARKETING_URL: process.env.NEXT_PUBLIC_MARKETING_URL, + NEXT_PUBLIC_DOCS_URL: process.env.NEXT_PUBLIC_DOCS_URL, + }, + + skipValidation: !!process.env.SKIP_ENV_VALIDATION, + emptyStringAsUndefined: true, +}); diff --git a/apps/web/src/trpc/query-client.ts b/apps/web/src/trpc/query-client.ts new file mode 100644 index 00000000000..5780964e5e4 --- /dev/null +++ b/apps/web/src/trpc/query-client.ts @@ -0,0 +1,33 @@ +import { + defaultShouldDehydrateQuery, + QueryClient, +} from "@tanstack/react-query"; +import SuperJSON from "superjson"; + +export const createQueryClient = () => + new QueryClient({ + defaultOptions: { + queries: { + // With SSR, we usually want to set some default staleTime + // above 0 to avoid refetching immediately on the client + staleTime: 30 * 1000, + }, + dehydrate: { + serializeData: SuperJSON.serialize, + shouldDehydrateQuery: (query) => + defaultShouldDehydrateQuery(query) || + query.state.status === "pending", + shouldRedactErrors: () => { + // We should not catch Next.js server errors + // as that's how Next.js detects dynamic pages + // so we cannot redact them. + // Next.js also automatically redacts errors for us + // with better digests. + return false; + }, + }, + hydrate: { + deserializeData: SuperJSON.deserialize, + }, + }, + }); diff --git a/apps/website/src/trpc/react.tsx b/apps/web/src/trpc/react.tsx similarity index 81% rename from apps/website/src/trpc/react.tsx rename to apps/web/src/trpc/react.tsx index 6613ccc7e83..a910f0201a7 100644 --- a/apps/website/src/trpc/react.tsx +++ b/apps/web/src/trpc/react.tsx @@ -11,6 +11,8 @@ import { import { createTRPCContext } from "@trpc/tanstack-react-query"; import { useState } from "react"; import SuperJSON from "superjson"; + +import { env } from "../env"; import { createQueryClient } from "./query-client"; let clientQueryClientSingleton: QueryClient | undefined; @@ -26,13 +28,9 @@ const getQueryClient = () => { return clientQueryClientSingleton; }; -export const { useTRPC, TRPCProvider } = createTRPCContext(); - -function getBaseUrl() { - if (typeof window !== "undefined") return window.location.origin; - if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; - return `http://localhost:3000`; -} +const context = createTRPCContext(); +export const { useTRPC, TRPCProvider } = context; +export type UseTRPC = typeof useTRPC; export function TRPCReactProvider(props: { children: React.ReactNode }) { const queryClient = getQueryClient(); @@ -42,12 +40,12 @@ export function TRPCReactProvider(props: { children: React.ReactNode }) { links: [ loggerLink({ enabled: (op) => - process.env.NODE_ENV === "development" || + env.NODE_ENV === "development" || (op.direction === "down" && op.result instanceof Error), }), httpBatchStreamLink({ transformer: SuperJSON, - url: `${getBaseUrl()}/api/trpc`, + url: `${env.NEXT_PUBLIC_API_URL}/api/trpc`, headers() { const headers = new Headers(); headers.set("x-trpc-source", "nextjs-react"); diff --git a/apps/web/src/trpc/server.tsx b/apps/web/src/trpc/server.tsx new file mode 100644 index 00000000000..3042598abba --- /dev/null +++ b/apps/web/src/trpc/server.tsx @@ -0,0 +1,26 @@ +import "server-only"; + +import type { AppRouter } from "@superset/trpc"; +import { createTRPCClient, httpBatchLink } from "@trpc/client"; +import { headers } from "next/headers"; +import { cache } from "react"; +import SuperJSON from "superjson"; + +import { env } from "../env"; + +export const api = cache(async () => { + const heads = new Headers(await headers()); + heads.set("x-trpc-source", "rsc"); + + return createTRPCClient({ + links: [ + httpBatchLink({ + transformer: SuperJSON, + url: `${env.NEXT_PUBLIC_API_URL}/api/trpc`, + headers() { + return Object.fromEntries(heads.entries()); + }, + }), + ], + }); +}); diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 00000000000..600980abb48 --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@superset/typescript/next.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/apps/website/.gitignore b/apps/website/.gitignore deleted file mode 100644 index 5ef6a520780..00000000000 --- a/apps/website/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/apps/website/next.config.ts b/apps/website/next.config.ts deleted file mode 100644 index 89bfe82d3aa..00000000000 --- a/apps/website/next.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - reactStrictMode: true, - transpilePackages: ["@superset/ui"], -}; - -export default nextConfig; diff --git a/apps/website/src/app/api/trpc/[trpc]/route.ts b/apps/website/src/app/api/trpc/[trpc]/route.ts deleted file mode 100644 index 6dbdfbfaf12..00000000000 --- a/apps/website/src/app/api/trpc/[trpc]/route.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { appRouter, createTRPCContext } from "@superset/trpc"; -import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; -import type { NextRequest } from "next/server"; - -const setCorsHeaders = (res: Response) => { - res.headers.set("Access-Control-Allow-Origin", "*"); - res.headers.set("Access-Control-Request-Method", "*"); - res.headers.set("Access-Control-Allow-Methods", "OPTIONS, GET, POST"); - res.headers.set("Access-Control-Allow-Headers", "*"); - return res; -}; - -export const OPTIONS = () => { - return setCorsHeaders(new Response(null, { status: 204 })); -}; - -const handler = async (req: NextRequest) => { - const response = await fetchRequestHandler({ - endpoint: "/api/trpc", - router: appRouter, - req, - createContext: () => - createTRPCContext({ - headers: req.headers, - }), - onError({ error, path }) { - console.error(`[TRPC Error] ${path ?? ""}:`, error); - }, - }); - - return setCorsHeaders(response); -}; - -export { handler as GET, handler as POST }; diff --git a/apps/website/src/trpc/server.tsx b/apps/website/src/trpc/server.tsx deleted file mode 100644 index c5bb5fecf20..00000000000 --- a/apps/website/src/trpc/server.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import "server-only"; - -import { type AppRouter, appRouter, createTRPCContext } from "@superset/trpc"; -import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; -import type { TRPCQueryOptions } from "@trpc/tanstack-react-query"; -import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query"; -import { headers } from "next/headers"; -import { cache } from "react"; -import { createQueryClient } from "./query-client"; - -/** - * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when - * handling a tRPC call from a React Server Component. - */ -const createContext = cache(async () => { - const heads = new Headers(await headers()); - heads.set("x-trpc-source", "rsc"); - - return createTRPCContext({ - headers: heads, - }); -}); - -const getQueryClient = cache(createQueryClient); - -export const trpc = createTRPCOptionsProxy({ - router: appRouter, - ctx: createContext, - queryClient: getQueryClient, -}); - -export function HydrateClient(props: { children: React.ReactNode }) { - const queryClient = getQueryClient(); - return ( - - {props.children} - - ); -} - -// biome-ignore lint/suspicious/noExplicitAny: TRPCQueryOptions requires any for generic inference -export function prefetch>>( - queryOptions: T, -) { - const queryClient = getQueryClient(); - if (queryOptions.queryKey[1]?.type === "infinite") { - // biome-ignore lint/suspicious/noExplicitAny: prefetchInfiniteQuery type mismatch with TRPCQueryOptions - void queryClient.prefetchInfiniteQuery(queryOptions as any); - } else { - void queryClient.prefetchQuery(queryOptions); - } -} diff --git a/bun.lock b/bun.lock index 03f4495d80e..116de928bc9 100644 --- a/bun.lock +++ b/bun.lock @@ -10,35 +10,66 @@ "turbo": "^2.5.8", }, }, - "apps/blog": { - "name": "@superset/blog", + "apps/admin": { + "name": "@superset/admin", "version": "0.1.0", "dependencies": { - "@react-three/drei": "^10.7.6", - "@react-three/fiber": "^9.4.0", + "@superset/db": "workspace:*", + "@superset/queries": "workspace:*", + "@superset/shared": "workspace:*", + "@superset/trpc": "workspace:*", "@superset/ui": "workspace:*", - "framer-motion": "^12.23.24", + "@t3-oss/env-nextjs": "^0.13.8", + "@tanstack/react-query": "^5.90.10", + "@tanstack/react-query-devtools": "^5.90.10", + "@trpc/client": "^11.7.1", + "@trpc/server": "^11.7.1", + "@trpc/tanstack-react-query": "^11.7.1", "geist": "^1.5.1", + "lucide-react": "^0.560.0", "next": "^15.5.7", - "nextra": "^4.6.0", - "nextra-theme-blog": "^4.6.0", - "nextra-theme-docs": "^4.6.0", + "next-themes": "^0.4.6", "react": "^19.1.1", "react-dom": "^19.1.1", - "three": "^0.181.2", + "server-only": "^0.0.1", + "superjson": "^2.2.5", + "zod": "^4.1.13", }, "devDependencies": { "@superset/typescript": "workspace:*", "@tailwindcss/postcss": "^4.0.9", - "@types/mdx": "^2.0.13", "@types/node": "^24.9.1", "@types/react": "^19.1.11", "@types/react-dom": "^19.1.7", - "@types/three": "^0.181.0", + "babel-plugin-react-compiler": "^1.0.0", "tailwindcss": "^4.0.9", "typescript": "^5.9.3", }, }, + "apps/api": { + "name": "@superset/api", + "version": "0.1.0", + "dependencies": { + "@superset/db": "workspace:*", + "@superset/shared": "workspace:*", + "@superset/trpc": "workspace:*", + "@t3-oss/env-nextjs": "^0.13.8", + "@trpc/server": "^11.7.1", + "drizzle-orm": "0.45.1", + "next": "^15.5.7", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "zod": "^4.1.13", + }, + "devDependencies": { + "@superset/typescript": "workspace:*", + "@types/node": "^24.9.1", + "@types/react": "^19.1.11", + "@types/react-dom": "^19.1.7", + "babel-plugin-react-compiler": "^1.0.0", + "typescript": "^5.9.3", + }, + }, "apps/cli": { "name": "@superset/cli", "version": "0.0.0", @@ -195,28 +226,23 @@ "typescript": "^5.9.3", }, }, - "apps/website": { - "name": "@superset/website", + "apps/marketing": { + "name": "@superset/marketing", "version": "0.1.0", "dependencies": { "@react-three/drei": "^10.7.6", "@react-three/fiber": "^9.4.0", - "@superset/trpc": "workspace:*", "@superset/ui": "workspace:*", "@t3-oss/env-nextjs": "^0.13.8", - "@tanstack/react-query": "^5.90.10", - "@trpc/client": "^11.7.1", - "@trpc/server": "^11.7.1", - "@trpc/tanstack-react-query": "^11.7.1", "framer-motion": "^12.23.24", "geist": "^1.5.1", + "lucide-react": "^0.560.0", "next": "^15.5.7", "next-themes": "^0.4.6", "react": "^19.1.1", "react-dom": "^19.1.1", "react-fast-marquee": "^1.6.5", "react-icons": "^5.5.0", - "superjson": "^2.2.5", "three": "^0.181.2", "zod": "^4.1.13", }, @@ -228,6 +254,43 @@ "@types/react": "^19.1.11", "@types/react-dom": "^19.1.7", "@types/three": "^0.181.0", + "babel-plugin-react-compiler": "^1.0.0", + "tailwindcss": "^4.0.9", + "typescript": "^5.9.3", + }, + }, + "apps/web": { + "name": "@superset/web", + "version": "0.1.0", + "dependencies": { + "@superset/db": "workspace:*", + "@superset/queries": "workspace:*", + "@superset/shared": "workspace:*", + "@superset/trpc": "workspace:*", + "@superset/ui": "workspace:*", + "@t3-oss/env-nextjs": "^0.13.8", + "@tanstack/react-query": "^5.90.10", + "@tanstack/react-query-devtools": "^5.90.10", + "@trpc/client": "^11.7.1", + "@trpc/server": "^11.7.1", + "@trpc/tanstack-react-query": "^11.7.1", + "geist": "^1.5.1", + "lucide-react": "^0.560.0", + "next": "^15.5.7", + "next-themes": "^0.4.6", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "server-only": "^0.0.1", + "superjson": "^2.2.5", + "zod": "^4.1.13", + }, + "devDependencies": { + "@superset/typescript": "workspace:*", + "@tailwindcss/postcss": "^4.0.9", + "@types/node": "^24.9.1", + "@types/react": "^19.1.11", + "@types/react-dom": "^19.1.7", + "babel-plugin-react-compiler": "^1.0.0", "tailwindcss": "^4.0.9", "typescript": "^5.9.3", }, @@ -274,7 +337,7 @@ "zod": "^4.1.13", }, "devDependencies": { - "@superset/typescript": "*", + "@superset/typescript": "workspace:*", "typescript": "^5.9.3", }, }, @@ -941,7 +1004,9 @@ "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], - "@superset/blog": ["@superset/blog@workspace:apps/blog"], + "@superset/admin": ["@superset/admin@workspace:apps/admin"], + + "@superset/api": ["@superset/api@workspace:apps/api"], "@superset/cli": ["@superset/cli@workspace:apps/cli"], @@ -951,6 +1016,8 @@ "@superset/docs": ["@superset/docs@workspace:apps/docs"], + "@superset/marketing": ["@superset/marketing@workspace:apps/marketing"], + "@superset/queries": ["@superset/queries@workspace:packages/queries"], "@superset/shared": ["@superset/shared@workspace:packages/shared"], @@ -961,7 +1028,7 @@ "@superset/ui": ["@superset/ui@workspace:packages/ui"], - "@superset/website": ["@superset/website@workspace:apps/website"], + "@superset/web": ["@superset/web@workspace:apps/web"], "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], @@ -1005,8 +1072,12 @@ "@tanstack/query-core": ["@tanstack/query-core@5.90.12", "", {}, "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg=="], + "@tanstack/query-devtools": ["@tanstack/query-devtools@5.91.1", "", {}, "sha512-l8bxjk6BMsCaVQH6NzQEE/bEgFy1hAs5qbgXl0xhzezlaQbPk6Mgz9BqEg2vTLPOHD8N4k+w/gdgCbEzecGyNg=="], + "@tanstack/react-query": ["@tanstack/react-query@5.90.12", "", { "dependencies": { "@tanstack/query-core": "5.90.12" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg=="], + "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.91.1", "", { "dependencies": { "@tanstack/query-devtools": "5.91.1" }, "peerDependencies": { "@tanstack/react-query": "^5.90.10", "react": "^18 || ^19" } }, "sha512-tRnJYwEbH0kAOuToy8Ew7bJw1lX3AjkkgSlf/vzb+NpnqmHPdWM+lA2DSdGQSLi1SU0PDRrrCI1vnZnci96CsQ=="], + "@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.13", "", { "dependencies": { "@tanstack/virtual-core": "3.13.13" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4o6oPMDvQv+9gMi8rE6gWmsOjtUZUYIJHv7EB+GblyYdi8U6OqLl8rhHWIUZSL1dUU2dPwTdTgybCKf9EjIrQg=="], "@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.13", "", {}, "sha512-uQFoSdKKf5S8k51W5t7b2qpfkyIbdHMzAn+AMQvHPxKUPeo1SsGaA4JRISQT87jm28b7z8OEqPcg1IOZagQHcA=="], @@ -1309,6 +1380,8 @@ "auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="], + "babel-plugin-react-compiler": ["babel-plugin-react-compiler@1.0.0", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw=="], + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], @@ -1667,7 +1740,7 @@ "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], - "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], @@ -2541,7 +2614,7 @@ "react-mosaic-component": ["react-mosaic-component@6.1.1", "", { "dependencies": { "classnames": "^2.3.2", "immutability-helper": "^3.1.1", "lodash": "^4.17.21", "prop-types": "^15.8.1", "rdndmb-html5-to-touch": "^8.0.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dnd-multi-backend": "^8.0.0", "react-dnd-touch-backend": "^16.0.1", "uuid": "^9.0.0" }, "peerDependencies": { "react": ">=16" } }, "sha512-Ivuj6AxRDlo/H8OiEDU1mdgivxuKbwGOa5Ub6Yf+bHcu0JWioT7ttlpCWF63/gKrJBlRMB6fW9/eNOXINg9+Gg=="], - "react-reconciler": ["react-reconciler@0.31.0", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ=="], + "react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="], "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], @@ -2693,7 +2766,7 @@ "sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="], - "scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "scroll-into-view-if-needed": ["scroll-into-view-if-needed@3.1.0", "", { "dependencies": { "compute-scroll-into-view": "^3.0.2" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="], @@ -3171,6 +3244,10 @@ "@react-three/drei/cross-env": ["cross-env@7.0.3", "", { "dependencies": { "cross-spawn": "^7.0.1" }, "bin": { "cross-env": "src/bin/cross-env.js", "cross-env-shell": "src/bin/cross-env-shell.js" } }, "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw=="], + "@react-three/fiber/react-reconciler": ["react-reconciler@0.31.0", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ=="], + + "@react-three/fiber/scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], @@ -3183,8 +3260,6 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "@vue/compiler-core/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], - "@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@xyflow/react/zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="], @@ -3281,8 +3356,6 @@ "iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="], - "ink/react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="], - "its-fine/@types/react-reconciler": ["@types/react-reconciler@0.28.9", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg=="], "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], @@ -3353,6 +3426,8 @@ "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + "path-scurry/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], @@ -3373,8 +3448,6 @@ "react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - "react-dom/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - "react-router/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], "read-pkg/normalize-package-data": ["normalize-package-data@3.0.3", "", { "dependencies": { "hosted-git-info": "^4.0.1", "is-core-module": "^2.5.0", "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" } }, "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA=="], @@ -3569,8 +3642,6 @@ "iconv-corefoundation/cli-truncate/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "ink/react-reconciler/scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - "launch-ide/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "log-symbols/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], diff --git a/packages/trpc/src/env.ts b/packages/trpc/src/env.ts index f049168f27c..31d24ce54ca 100644 --- a/packages/trpc/src/env.ts +++ b/packages/trpc/src/env.ts @@ -3,7 +3,7 @@ import { z } from "zod"; export const env = createEnv({ server: { - MOCK_USER_ID: z.string().uuid().optional(), + MOCK_USER_ID: z.string().uuid(), }, clientPrefix: "PUBLIC_", client: {}, diff --git a/packages/trpc/src/router/user.ts b/packages/trpc/src/router/user.ts index 1ea87c25727..5d1d6cafadd 100644 --- a/packages/trpc/src/router/user.ts +++ b/packages/trpc/src/router/user.ts @@ -3,9 +3,15 @@ import { users } from "@superset/db/schema"; import type { TRPCRouterRecord } from "@trpc/server"; import { desc, eq } from "drizzle-orm"; import { z } from "zod"; -import { publicProcedure } from "../trpc"; +import { protectedProcedure, publicProcedure } from "../trpc"; export const userRouter = { + me: protectedProcedure.query(async ({ ctx }) => { + return db.query.users.findFirst({ + where: eq(users.id, ctx.session.user.id), + }); + }), + all: publicProcedure.query(() => { return db.query.users.findMany({ orderBy: desc(users.createdAt), diff --git a/turbo.jsonc b/turbo.jsonc index 8b3f597d112..f204afda77a 100644 --- a/turbo.jsonc +++ b/turbo.jsonc @@ -4,22 +4,12 @@ "globalEnv": [ "DATABASE_URL", "DATABASE_URL_UNPOOLED", - "E2B_API_KEY", - "OPENROUTER_API_KEY", - "LIVEBLOCKS_SECRET_KEY", - "NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_KEY", - "LANGFUSE_HOST", - "LANGFUSE_PUBLIC_KEY", - "LANGFUSE_SECRET_KEY", - "BLOB_READ_WRITE_TOKEN", - "USER_ID", - "NEXT_PUBLIC_POSTHOG_KEY", - "NEXT_PUBLIC_POSTHOG_HOST", - "NEXT_PUBLIC_SENTRY_DSN", + "MOCK_USER_ID", "NEXT_PUBLIC_API_URL", "NEXT_PUBLIC_WEB_URL", - "SENTRY_AUTH_TOKEN", - "SENTRY_ENVIRONMENT" + "NEXT_PUBLIC_MARKETING_URL", + "NEXT_PUBLIC_DOCS_URL", + "NEXT_PUBLIC_ADMIN_URL" ], "globalPassThroughEnv": [ "NODE_ENV",