Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/dashboard/app/lib/queries.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,5 +565,21 @@ export async function getSchedule (
return await queryOne<ScheduleResult>(dbUrl, sql, [name, key])
}

// Find a job by ID across all queues (scans all partitions)
export async function findJobById (
dbUrl: string,
schema: string,
id: string
): Promise<{ id: string; name: string } | null> {
const s = validateIdentifier(schema)
const sql = `
SELECT id, name
FROM ${s}.job
WHERE id = $1
LIMIT 1
`
return queryOne<{ id: string; name: string }>(dbUrl, sql, [id])
}

// Re-export job action methods from boss.server
export { getJobById, cancelJob, resumeJob, deleteJob } from './boss.server'
1 change: 1 addition & 0 deletions packages/dashboard/app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default [
route('schedules', 'routes/schedules.tsx'),
route('schedules/:name/:key', 'routes/schedules.$name.$key.tsx'),
route('schedules/new', 'routes/schedules.new.tsx'),
route('search', 'routes/search.tsx'),
route('send', 'routes/send.tsx'),
route('warnings', 'routes/warnings.tsx'),
] satisfies RouteConfig
18 changes: 15 additions & 3 deletions packages/dashboard/app/routes/jobs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export function ErrorBoundary () {
export default function Jobs ({ loaderData }: Route.ComponentProps) {
const { recentJobs, page, stateFilter, hasNextPage, hasPrevPage } = loaderData
const [searchParams, setSearchParams] = useSearchParams()
const dbParam = searchParams.get('db')

const handleFilterChange = (key: string, value: string | null) => {
const params = new URLSearchParams(searchParams)
Expand Down Expand Up @@ -94,9 +95,20 @@ export default function Jobs ({ loaderData }: Route.ComponentProps) {
Recently created jobs across all queues
</p>
</div>
<DbLink to="/send">
<Button variant="primary" size="md">Send Job</Button>
</DbLink>
<div className="flex items-center gap-3">
<form action="/search" method="get" className="flex items-center">
{dbParam && <input type="hidden" name="db" value={dbParam} />}
<input
type="text"
name="jobId"
placeholder="Search job by ID..."
className="h-9 w-48 lg:w-64 rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-3 text-sm text-gray-900 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
/>
</form>
<DbLink to="/send">
<Button variant="primary" size="md">Send Job</Button>
</DbLink>
</div>
</div>

<Card>
Expand Down
53 changes: 53 additions & 0 deletions packages/dashboard/app/routes/search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { redirect } from 'react-router'
import type { Route } from './+types/search'
import { findJobById } from '~/lib/queries.server'
import { Card, CardContent } from '~/components/ui/card'

const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i

export async function loader ({ request, context }: Route.LoaderArgs) {
const url = new URL(request.url)
const jobId = url.searchParams.get('jobId')?.trim()

if (!jobId) {
return { error: null }
}

if (!UUID_REGEX.test(jobId)) {
return { error: 'Invalid job ID format. Please enter a valid UUID.' }
}

const job = await findJobById(context.DB_URL, context.SCHEMA, jobId)

if (!job) {
return { error: `Job not found: ${jobId}` }
}

// Preserve the db param for multi-database setups
const dbParam = url.searchParams.get('db')
const target = `/queues/${encodeURIComponent(job.name)}/jobs/${job.id}${dbParam ? `?db=${encodeURIComponent(dbParam)}` : ''}`
throw redirect(target)
}

export default function Search ({ loaderData }: Route.ComponentProps) {
const { error } = loaderData

return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">Search</h1>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
Find a job by its ID
</p>
</div>

{error && (
<Card>
<CardContent className="py-8 text-center text-gray-500 dark:text-gray-400">
{error}
</CardContent>
</Card>
)}
</div>
)
}
45 changes: 22 additions & 23 deletions packages/dashboard/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions packages/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,21 @@
"ci": "npm run typecheck && npm run cover:frontend && cross-env DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/pgboss npm run cover:server"
},
"dependencies": {
"@base-ui/react": "^1.1.0",
"@base-ui/react": "^1.2.0",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I went over all existing UIs and didn't notice any regressions, seems to be a safe update

"@hono/node-server": "^1.19.9",
"@react-router/node": "^7.13.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"hono": "^4.11.9",
"isbot": "^5.1.34",
"isbot": "^5.1.35",
"lucide-react": "^0.563.0",
"pg": "^8.18.0",
"pg-boss": "^12.11.1",
"pg-boss": "^12.12.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router": "^7.13.0",
"react-router-hono-server": "^2.24.1",
"tailwind-merge": "^3.4.0"
"tailwind-merge": "^3.4.1"
},
"devDependencies": {
"@react-router/dev": "^7.13.0",
Expand Down