Skip to content

Commit 60652f9

Browse files
authored
Merge pull request #17 from taskiq-python/feat/add-postgres-source
feat: add postgres storage source
2 parents eab7711 + c33702c commit 60652f9

18 files changed

Lines changed: 448 additions & 84 deletions

Dockerfile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ RUN corepack enable
99

1010
WORKDIR /app
1111

12+
ENV TASKIQ_ADMIN_DB_DRIVER=sqlite
13+
ENV TASKIQ_ADMIN_DB_FILE_PATH=/tmp/taskiq-admin.db
14+
1215
# install dependencies
1316
COPY ./package.json /app/
1417
COPY ./pnpm-lock.yaml /app/
@@ -33,8 +36,9 @@ COPY --from=build /app/entrypoint.sh /usr/app/entrypoint.sh
3336

3437
EXPOSE 3000
3538
ENV HOST=0.0.0.0 NODE_ENV=production
36-
ENV DB_FILE_PATH=/usr/database/database.db
37-
ENV BACKUP_FILE_PATH=/usr/database/backup.db
39+
ENV TASKIQ_ADMIN_DB_DRIVER=sqlite
40+
ENV TASKIQ_ADMIN_DB_FILE_PATH=/usr/database/database.db
41+
ENV TASKIQ_ADMIN_BACKUP_FILE_PATH=/usr/database/backup.db
3842

3943
RUN npm install dotenv
4044

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Broker-agnostic admin panel for Taskiq
22

3-
Standalone admin panel with all data stored in SQLite database
3+
Standalone admin panel with task results storage in SQLite or Postgres database
44

55

66
- [Broker-agnostic admin panel for Taskiq](#broker-agnostic-admin-panel-for-taskiq)
@@ -91,6 +91,11 @@ volumes:
9191
2) run `make dev` to run it locally in dev mode
9292
3) run `make prod` to run it locally in prod mode
9393

94+
Environment variables for DB setup:
95+
- `TASKIQ_ADMIN_DB_DRIVER`: required, one of `sqlite` or `postgres`
96+
- `TASKIQ_ADMIN_DB_FILE_PATH`: required when `TASKIQ_ADMIN_DB_DRIVER=sqlite`
97+
- `TASKIQ_ADMIN_DB_URL`: required when `TASKIQ_ADMIN_DB_DRIVER=postgres`
98+
9499
### Task States
95100
Let's assume we have a task 'do_smth', there are all states it can embrace:
96101
1) `queued` - the task has been sent to the queue without an error

app/lib/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import dayjs from 'dayjs'
22
import utc from 'dayjs/plugin/utc.js'
33
import { type ClassValue, clsx } from 'clsx'
44
import { twMerge } from 'tailwind-merge'
5-
import type { TaskSelect } from '~/server/db/schema'
5+
import type { TaskSelect } from '../../shared/db/schema'
66

77
dayjs.extend(utc)
88

drizzle.config.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,39 @@
11
import 'dotenv/config'
22
import { defineConfig } from 'drizzle-kit'
33

4+
const dbDriver = process.env.TASKIQ_ADMIN_DB_DRIVER
5+
6+
if (!dbDriver) {
7+
throw new Error('Environment variable TASKIQ_ADMIN_DB_DRIVER is required')
8+
}
9+
10+
if (dbDriver !== 'sqlite' && dbDriver !== 'postgres') {
11+
throw new Error(
12+
'Environment variable TASKIQ_ADMIN_DB_DRIVER must be "sqlite" or "postgres"'
13+
)
14+
}
15+
16+
const dbUrl =
17+
dbDriver === 'sqlite'
18+
? process.env.TASKIQ_ADMIN_DB_FILE_PATH
19+
: process.env.TASKIQ_ADMIN_DB_URL
20+
21+
if (!dbUrl) {
22+
throw new Error(
23+
dbDriver === 'sqlite'
24+
? 'Environment variable TASKIQ_ADMIN_DB_FILE_PATH is required for sqlite driver'
25+
: 'Environment variable TASKIQ_ADMIN_DB_URL is required for postgres driver'
26+
)
27+
}
28+
429
export default defineConfig({
530
out: './drizzle',
6-
schema: './shared/db/schema.ts',
7-
dialect: 'sqlite',
31+
schema:
32+
dbDriver === 'sqlite'
33+
? './shared/db/schema.sqlite.ts'
34+
: './shared/db/schema.postgres.ts',
35+
dialect: dbDriver === 'sqlite' ? 'sqlite' : 'postgresql',
836
dbCredentials: {
9-
url: process.env.DB_FILE_PATH!
37+
url: dbUrl
1038
}
1139
})

env-example

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
DB_FILE_PATH=database/database.db
2-
BACKUP_FILE_PATH=database/backup.db
1+
TASKIQ_ADMIN_DB_DRIVER=sqlite
2+
TASKIQ_ADMIN_DB_FILE_PATH=database/database.db
3+
TASKIQ_ADMIN_DB_URL=postgres://postgres:postgres@localhost:5432/taskiq_admin
4+
TASKIQ_ADMIN_BACKUP_FILE_PATH=database/backup.db
35
TASKIQ_ADMIN_API_TOKEN=supersecret

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"drizzle-orm": "^0.45.1",
3030
"lucide-vue-next": "^0.524.0",
3131
"nuxt": "^4.2.2",
32+
"pg": "^8.16.3",
3233
"reka-ui": "^2.6.1",
3334
"tailwind-merge": "^3.4.0",
3435
"tailwindcss": "^4.1.17",

pnpm-lock.yaml

Lines changed: 101 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/api/tasks/backup.get.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
11
import fs from 'fs'
2-
import { db } from '../../../shared/db'
2+
import { backupDatabase } from '../../../shared/db'
33
import { utcNow } from '../../../shared/utils'
44
import { envVariables } from '../../../shared/env'
5-
import { defineEventHandler, sendStream, setHeader } from '#imports'
5+
import {
6+
createError,
7+
defineEventHandler,
8+
sendStream,
9+
setHeader
10+
} from '#imports'
611

712
export default defineEventHandler(async (event) => {
8-
await db.$client.backup(envVariables.backupFilePath)
13+
try {
14+
await backupDatabase(envVariables.backupFilePath)
15+
} catch (error) {
16+
throw createError({
17+
status: 400,
18+
statusMessage:
19+
error instanceof Error
20+
? error.message
21+
: 'Could not create database backup'
22+
})
23+
}
24+
925
const stream = fs.createReadStream(envVariables.backupFilePath)
1026

1127
const now = utcNow()

server/plugins/startup.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
1-
import fs from 'fs/promises'
2-
import { db } from '../../shared/db'
1+
import { db, initializeDatabase } from '../../shared/db'
32
import { defineNitroPlugin } from '#imports'
43
import { taskiqAdminSettingsTable } from '~~/shared/db/schema'
54
import { SETTINGS } from '~~/shared/constants/settings'
65

7-
export default defineNitroPlugin(async (nitroApp) => {
6+
export default defineNitroPlugin(async () => {
87
console.log('Running DB initialization...')
9-
const sqlScript = await fs.readFile('dbschema.sql', 'utf-8')
10-
11-
// executing the SQL script to create tables and indexes
12-
db.$client.exec(sqlScript)
8+
await initializeDatabase()
139

1410
// seeding default settings
15-
db.insert(taskiqAdminSettingsTable)
11+
await db
12+
.insert(taskiqAdminSettingsTable)
1613
.values(
1714
Object.entries(SETTINGS).map(([_, value]) => ({
1815
key: value.key,
1916
value: value.defaultValue
2017
}))
2118
)
2219
.onConflictDoNothing()
23-
.run()
2420

2521
console.log('DB initialization completed.')
2622
})

shared/db/constants.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export const DB_TABLE_NAMES = {
2+
tasks: 'taskiq_admin_tasks',
3+
settings: 'taskiq_admin_settings'
4+
} as const
5+
6+
export const DB_INDEX_NAMES = {
7+
tasksState: 'idx_taskiq_admin_tasks__state',
8+
tasksQueuedAt: 'idx_taskiq_admin_tasks__queued_at',
9+
tasksStartedAt: 'idx_taskiq_admin_tasks__started_at',
10+
tasksFinishedAt: 'idx_taskiq_admin_tasks__finished_at',
11+
tasksExecutionTime: 'idx_taskiq_admin_tasks__execution_time',
12+
tasksName: 'idx_taskiq_admin_tasks__name',
13+
tasksWorker: 'idx_taskiq_admin_tasks__worker'
14+
} as const

0 commit comments

Comments
 (0)