Skip to content

Commit

Permalink
revert: Build ui image locally in docker compose (#190)
Browse files Browse the repository at this point in the history
* fix: atob error (maybe)

* ci: Add workflow to test pnpm build

* chore: Rename tests workflow to test-python

* ignore frontend changes in python tests

* missing env vars in pnpm build step

* Attempt: change NEXT_SERVER_API_URL to localhost

* refactor(ui): Use custom hook to fetch workflows

* refactor(ui): Use custom hook to fetch playbooks

* refactor(ui): Move common skeletions into component module

* build: Build UI locally

* build: Make changes according to vercel nextjs officiel docker image

---------

Co-authored-by: Daryl Lim <[email protected]>
  • Loading branch information
topher-lo and daryllimyt authored Jun 22, 2024
1 parent 5090afb commit 5c5855e
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 83 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-push-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ jobs:
env:
NEXT_PUBLIC_API_URL: http://localhost:3000
NEXT_PUBLIC_APP_URL: http://localhost:3000
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: "unused"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: "secret-clerk-publishable-key"
NEXT_PUBLIC_DISABLE_AUTH: "true"
NEXT_SERVER_API_URL: http://api:8000
NEXT_SERVER_API_URL: http://localhost:8000
NODE_ENV: development
with:
context: frontend
Expand Down
47 changes: 47 additions & 0 deletions .github/workflows/test-pnpm-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Test pnpm build

on:
push:
branches:
- main
paths:
- "frontend/**"
- ".github/workflows/test-pnpm-build.yml"
pull_request:
branches:
- main
paths:
- "frontend/**"
- ".github/workflows/test-pnpm-build.yml"

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: "22"

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: "latest"

- name: Install dependencies
run: pnpm install
working-directory: ./frontend

- name: Build project
env:
NEXT_PUBLIC_API_URL: http://localhost:3000
NEXT_PUBLIC_APP_URL: http://localhost:3000
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: "secret-clerk-publishable-key"
NEXT_PUBLIC_DISABLE_AUTH: "true"
NEXT_SERVER_API_URL: http://localhost:8000
NODE_ENV: development
run: pnpm build
working-directory: ./frontend
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ name: Tests
on:
push:
branches: ["main"]
paths-ignore:
- "frontend/**"
pull_request:
branches: ["main"]
paths-ignore:
- "frontend/**"

permissions:
contents: read
Expand Down
12 changes: 11 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,17 @@ services:
entrypoint: ["python", "tracecat/dsl/worker.py"]

ui:
image: ghcr.io/tracecathq/tracecat-ui:${TRACECAT__IMAGE_TAG:-latest}
# image: ghcr.io/tracecathq/tracecat-ui:${TRACECAT__IMAGE_TAG:-latest}
build:
context: ./frontend
dockerfile: Dockerfile.prod
args:
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
NEXT_PUBLIC_DISABLE_AUTH: ${TRACECAT__DISABLE_AUTH}
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY} # Sensitive
NEXT_SERVER_API_URL: ${NEXT_SERVER_API_URL}
NODE_ENV: ${NODE_ENV}
container_name: tracecat_ui
ports:
- 3000:3000
Expand Down
19 changes: 15 additions & 4 deletions frontend/Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
FROM node:22-alpine AS deps
WORKDIR /app

Expand Down Expand Up @@ -43,13 +44,23 @@ ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# TODO: Automatically leverage output traces to reduce image size
COPY --from=builder /app/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

CMD ["/app/node_modules/.bin/next", "start"]
ENV PORT 3000

# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js
4 changes: 2 additions & 2 deletions frontend/src/app/playbooks/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { Suspense } from "react"

import { CenteredSpinner } from "@/components/loading/spinner"
import { WorkflowsDashboard } from "@/components/playbooks/workflows-dashboard"
import { PlaybooksDashboard } from "@/components/playbooks/workflows-dashboard"

export default async function Page() {
return (
<Suspense fallback={<CenteredSpinner />}>
<WorkflowsDashboard />
<PlaybooksDashboard />
</Suspense>
)
}
50 changes: 17 additions & 33 deletions frontend/src/components/dashboard/workflows-dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { Suspense } from "react"
"use client"

import Link from "next/link"
import { ConeIcon } from "lucide-react"

import { fetchAllWorkflows } from "@/lib/workflow"
import { useWorkflows } from "@/lib/hooks"
import { Button } from "@/components/ui/button"
import { Skeleton } from "@/components/ui/skeleton"
import CreateWorkflowButton from "@/components/dashboard/create-workflow-button"
import { WorkflowItem } from "@/components/dashboard/workflows-dashboard-item"
import { AlertNotification } from "@/components/notifications"
import { ListItemSkeletion } from "@/components/skeletons"

export async function WorkflowsDashboard() {
export function WorkflowsDashboard() {
return (
<div className="size-full overflow-auto">
<div className="container flex h-full max-w-[800px] flex-col space-y-12 p-16 pt-32">
Expand All @@ -30,26 +31,22 @@ export async function WorkflowsDashboard() {
</Link>
</div>
</div>
<Suspense
fallback={
<div className="flex flex-col gap-2 pt-4">
<Skeleton className="h-24 w-full" />
<Skeleton className="h-24 w-full" />
<Skeleton className="h-24 w-full" />
<Skeleton className="h-24 w-full" />
</div>
}
>
<WorkflowList />
</Suspense>
<WorkflowList />
</div>
</div>
)
}

export async function WorkflowList() {
const workflows = await fetchAllWorkflows()
if (workflows === null) {
export function WorkflowList() {
const { data: workflows, error, isLoading } = useWorkflows()
if (isLoading) {
return (
<div className="flex w-full flex-col items-center space-y-12">
<ListItemSkeletion n={2} />
</div>
)
}
if (error || workflows === undefined) {
return (
<AlertNotification level="error" message="Error fetching workflows" />
)
Expand All @@ -59,20 +56,7 @@ export async function WorkflowList() {
<div className="flex flex-col space-y-4">
{workflows.length === 0 ? (
<div className="flex w-full flex-col items-center space-y-12">
<div className="flex w-full items-center justify-center space-x-4">
<Skeleton className="size-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
<div className="flex w-full items-center justify-center space-x-4">
<Skeleton className="size-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
<ListItemSkeletion n={2} />
<div className="space-y-4 text-center">
<p className="text-sm">Welcome to Tracecat 👋</p>
</div>
Expand Down
54 changes: 19 additions & 35 deletions frontend/src/components/playbooks/workflows-dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Suspense } from "react"
"use client"

import Link from "next/link"
import { InfoIcon } from "lucide-react"

import { fetchAllPlaybooks } from "@/lib/workflow"
import { usePlaybooks } from "@/lib/hooks"
import { Button } from "@/components/ui/button"
import { Skeleton } from "@/components/ui/skeleton"
import { AlertNotification } from "@/components/notifications"
import { WorkflowItem } from "@/components/playbooks/workflows-dashboard-item"
import { ListItemSkeletion } from "@/components/skeletons"

export async function WorkflowsDashboard() {
export function PlaybooksDashboard() {
return (
<div className="size-full overflow-auto">
<div className="container flex h-full max-w-[800px] flex-col space-y-12 p-16 pt-32">
Expand All @@ -31,48 +32,31 @@ export async function WorkflowsDashboard() {
</Link>
</Button>
</div>
<Suspense
fallback={
<div className="flex flex-col gap-2 pt-4">
<Skeleton className="h-24 w-full" />
<Skeleton className="h-24 w-full" />
<Skeleton className="h-24 w-full" />
<Skeleton className="h-24 w-full" />
</div>
}
>
<WorkflowList />
</Suspense>
<PlaybookList />
</div>
</div>
)
}

export async function WorkflowList() {
const workflows = await fetchAllPlaybooks()
if (workflows === null) {
export function PlaybookList() {
const { data: playbooks, error, isLoading } = usePlaybooks()
if (isLoading) {
return (
<div className="flex w-full flex-col items-center space-y-12">
<ListItemSkeletion n={2} />
</div>
)
}
if (error || playbooks === undefined) {
return (
<AlertNotification level="error" message="Error fetching playbooks" />
)
}
return (
<div className="flex flex-col space-y-4">
{workflows.length === 0 ? (
{playbooks.length === 0 ? (
<div className="flex w-full flex-col items-center space-y-12">
<div className="flex w-full items-center justify-center space-x-4">
<Skeleton className="size-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
<div className="flex w-full items-center justify-center space-x-4">
<Skeleton className="size-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
<ListItemSkeletion n={2} />
<div className="space-y-4 text-center">
<p className="text-sm">No playbooks installed 😿</p>
<p className="max-w-lg text-center text-xs text-muted-foreground">
Expand All @@ -83,7 +67,7 @@ export async function WorkflowList() {
</div>
) : (
<>
{workflows.map((wf, idx) => (
{playbooks.map((wf, idx) => (
<WorkflowItem key={idx} workflow={wf} />
))}
</>
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/components/skeletons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Skeleton } from "@/components/ui/skeleton"

export function ListItemSkeletion({ n = 2 }: { n: number }) {
return (
<>
{[...Array(n)].map((_i, idx) => (
<div
key={idx}
className="flex w-full items-center justify-center space-x-4"
>
<Skeleton className="size-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
))}
</>
)
}
30 changes: 28 additions & 2 deletions frontend/src/lib/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { useEffect, useState } from "react"
import { useWorkflowBuilder } from "@/providers/builder"
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"

import { Action, CaseEvent, type Case } from "@/types/schemas"
import {
Action,
CaseEvent,
type Case,
type WorkflowMetadata,
} from "@/types/schemas"
import {
CaseEventParams,
createCaseEvent,
Expand All @@ -11,7 +16,12 @@ import {
updateCase,
} from "@/lib/cases"
import { updateWebhook } from "@/lib/trigger"
import { getActionById, updateAction } from "@/lib/workflow"
import {
fetchAllPlaybooks,
fetchAllWorkflows,
getActionById,
updateAction,
} from "@/lib/workflow"
import { toast } from "@/components/ui/use-toast"
import { UDFNodeType } from "@/components/workspace/canvas/udf-node"

Expand Down Expand Up @@ -226,3 +236,19 @@ export function useUpdateWebhook(workflowId: string) {

return mutation
}

export function useWorkflows() {
const query = useQuery<WorkflowMetadata[], Error>({
queryKey: ["workflows"],
queryFn: fetchAllWorkflows,
})
return query
}

export function usePlaybooks() {
const query = useQuery<WorkflowMetadata[], Error>({
queryKey: ["playbooks"],
queryFn: fetchAllPlaybooks,
})
return query
}
Loading

0 comments on commit 5c5855e

Please sign in to comment.