Add Hatchet workflow stubs for order and referral jobs

This commit is contained in:
Ruslan Bakiev
2026-03-30 21:41:41 +07:00
parent 67a4a8c8e8
commit 728139e617
9 changed files with 1470 additions and 13 deletions

View File

@@ -5,5 +5,8 @@ VAULT_KV_MOUNT=secret
VAULT_SHARED_PATH=
VAULT_PROJECT_PATH=
APOLLO_BACKEND_GRAPHQL_URL=http://apollo-backend:4000/graphql
HATCHET_CLIENT_TOKEN=
HATCHET_CLIENT_HOST_PORT=fregat-hatchet-engine:7070
HATCHET_WORKER_NAME=fregat-hatchet-worker

View File

@@ -1,6 +1,11 @@
# fregat-hatchet-worker
Separate worker service for Hatchet workflows in the Fregat project.
Hatchet worker for Fregat asynchronous jobs.
## Included workflows
- `fregat-order-status-notifier`
- `fregat-referral-balance-sync`
## Run

1352
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@
"dev": "node --watch src/worker.js"
},
"dependencies": {
"@hatchet-dev/typescript-sdk": "^1.19.0",
"dotenv": "^17.3.1"
}
}

View File

@@ -0,0 +1,27 @@
const backendGraphqlUrl = process.env.APOLLO_BACKEND_GRAPHQL_URL;
if (!backendGraphqlUrl) {
throw new Error('APOLLO_BACKEND_GRAPHQL_URL is required');
}
export async function requestBackendGraphQL({ query, variables = {}, userId = null }) {
const response = await fetch(backendGraphqlUrl, {
method: 'POST',
headers: {
'content-type': 'application/json',
...(userId ? { 'x-user-id': userId } : {}),
},
body: JSON.stringify({ query, variables }),
});
if (!response.ok) {
throw new Error(`Backend GraphQL request failed: HTTP ${response.status}`);
}
const payload = await response.json();
if (payload.errors?.length) {
throw new Error(payload.errors.map((error) => error.message).join('; '));
}
return payload.data;
}

3
src/hatchet-client.js Normal file
View File

@@ -0,0 +1,3 @@
import { HatchetClient } from '@hatchet-dev/typescript-sdk/v1/index.js';
export const hatchet = HatchetClient.init();

View File

@@ -1,17 +1,22 @@
import 'dotenv/config';
const requiredEnv = [
'HATCHET_CLIENT_HOST_PORT',
];
import { hatchet } from './hatchet-client.js';
import { orderStatusNotifier } from './workflows/order-status-notifier.js';
import { referralBalanceSync } from './workflows/referral-balance-sync.js';
const missing = requiredEnv.filter((name) => !process.env[name]);
if (missing.length > 0) {
throw new Error(`Missing required env vars: ${missing.join(', ')}`);
const workerName = process.env.HATCHET_WORKER_NAME || 'fregat-hatchet-worker';
async function main() {
const worker = await hatchet.worker(workerName, {
workflows: [orderStatusNotifier, referralBalanceSync],
slots: 10,
});
console.log(`[${workerName}] starting...`);
await worker.start();
}
console.log('fregat-hatchet-worker started');
console.log(`Hatchet endpoint: ${process.env.HATCHET_CLIENT_HOST_PORT}`);
setInterval(() => {
console.log('worker heartbeat');
}, 60_000);
main().catch((error) => {
console.error('[fregat-hatchet-worker] fatal error', error);
process.exit(1);
});

View File

@@ -0,0 +1,23 @@
import { hatchet } from '../hatchet-client.js';
export const orderStatusNotifier = hatchet.workflow({
name: 'fregat-order-status-notifier',
});
orderStatusNotifier.task({
name: 'notify-order-status',
fn: async (input) => {
const orderCode = String(input.orderCode || 'unknown');
const status = String(input.status || 'unknown');
const customerId = String(input.customerId || 'unknown');
console.log(`[fregat-order-status-notifier] Order ${orderCode} for customer ${customerId} moved to ${status}`);
return {
delivered: true,
orderCode,
status,
customerId,
};
},
});

View File

@@ -0,0 +1,38 @@
import { hatchet } from '../hatchet-client.js';
import { requestBackendGraphQL } from '../fregat-backend-client.js';
export const referralBalanceSync = hatchet.workflow({
name: 'fregat-referral-balance-sync',
});
referralBalanceSync.task({
name: 'sync-referral-balance',
fn: async (input) => {
const userId = String(input.userId || '');
if (!userId) {
throw new Error('userId is required for referral sync');
}
const data = await requestBackendGraphQL({
userId,
query: `
query ReferralStats {
referralStats {
referrerId
availableBalance
referralsCount
}
}
`,
});
const stats = data.referralStats;
console.log(`[fregat-referral-balance-sync] user=${stats.referrerId} balance=${stats.availableBalance} referrals=${stats.referralsCount}`);
return {
referrerId: stats.referrerId,
availableBalance: stats.availableBalance,
referralsCount: stats.referralsCount,
};
},
});