Add Hatchet workflow stubs for order and referral jobs
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
1352
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@
|
||||
"dev": "node --watch src/worker.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hatchet-dev/typescript-sdk": "^1.19.0",
|
||||
"dotenv": "^17.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
27
src/fregat-backend-client.js
Normal file
27
src/fregat-backend-client.js
Normal 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
3
src/hatchet-client.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import { HatchetClient } from '@hatchet-dev/typescript-sdk/v1/index.js';
|
||||
|
||||
export const hatchet = HatchetClient.init();
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
23
src/workflows/order-status-notifier.js
Normal file
23
src/workflows/order-status-notifier.js
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
38
src/workflows/referral-balance-sync.js
Normal file
38
src/workflows/referral-balance-sync.js
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user