> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kazzle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tự động hóa

> Lên lịch các công việc và phản hồi webhooks từ bên trong một ứng dụng Kazzle.

Tự động hóa là những phần của ứng dụng chạy tự động — theo lịch trình hoặc khi một dịch vụ bên ngoài gửi một sự kiện.

Bạn khai báo chúng trên một thành phần process trong `kazzle.config.ts`. Một thành phần có thể có bao nhiêu trigger tùy ý.

## Cấu trúc

```ts theme={"theme":"material-theme-darker"}
{
  name: 'events',
  type: 'process',
  path: './components/events/index.ts',
  processMode: 'persistent', // or 'triggered'
  triggers: [
    { name: 'daily-digest', kind: 'schedule', schedule: '0 9 * * *', path: '/cron/daily-digest' },
    { name: 'stripe',       kind: 'webhook',                          path: '/webhook/stripe' },
  ],
}
```

Hai điều đang xảy ra ở đây:

* `processMode` chọn **vòng đời** — máy chủ chạy lâu dài hoặc chạy một lần cho mỗi trigger.
* `triggers[]` liệt kê các **sự kiện** sẽ kích hoạt thành phần này.

Hai phần này độc lập với nhau. Một máy chủ persistent có thể có một cron. Một quy trình ephemeral có thể có một webhook. Chọn vòng đời phù hợp với khối lượng công việc, sau đó gắn bao nhiêu trigger tùy ý.

## `processMode`

| Chế độ                  | Chạy gì                                                      | Khi nào sử dụng                                                                                              |
| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `persistent` (mặc định) | Một máy chủ HTTP chạy lâu dài. Các trigger được POST vào nó. | Thành phần đã phục vụ HTTP hoặc giữ trạng thái trong bộ nhớ (hàng đợi, websockets, bộ nhớ cache).            |
| `triggered`             | Tập lệnh entry được tạo cho mỗi trigger và thoát.            | Các công việc nền thuần túy — dọn dẹp hàng đêm, xử lý webhook Stripe đơn lẻ, v.v. Không có máy chủ nhàn rỗi. |

## Triggers

Mỗi trigger có một `name` (duy nhất trong thành phần), một `kind`, và — tùy thuộc vào chế độ — một `schedule` và/hoặc `path`.

| Trường     | Khi bắt buộc                    | Ghi chú                                                              |
| ---------- | ------------------------------- | -------------------------------------------------------------------- |
| `name`     | luôn luôn                       | Được sử dụng làm phân đoạn URL webhook và trong nhật ký. Kebab-case. |
| `kind`     | luôn luôn                       | `'schedule'` hoặc `'webhook'`.                                       |
| `schedule` | khi `kind: 'schedule'`          | Biểu thức cron 5 trường. Độ phân giải phút là mức tối thiểu.         |
| `path`     | khi `processMode: 'persistent'` | Tuyến HTTP trên máy chủ của bạn nơi trigger đến.                     |

## Chế độ persistent — HTTP vào máy chủ

Khi một trigger kích hoạt cho một thành phần persistent, Kazzle POST đến máy chủ của bạn tại `path` được khai báo. Yêu cầu mang theo:

| Header                                           | Nó cho bạn biết gì                                  |
| ------------------------------------------------ | --------------------------------------------------- |
| `Authorization: Bearer ${KAZZLE_TRIGGER_SECRET}` | Xác thực điều này. Từ chối các cuộc gọi không khớp. |
| `x-kazzle-trigger-name`                          | `name` của trigger từ manifest.                     |
| `x-kazzle-trigger-run-id`                        | ID không rõ ràng để tương quan nhật ký.             |
| `x-kazzle-triggered-by`                          | `cron` \| `webhook` \| `manual`.                    |

Đối với webhook triggers, phần thân yêu cầu gốc được chuyển tiếp dưới dạng phần thân POST. Đối với schedule triggers, phần thân trống.

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (persistent mode)
const TRIGGER_SECRET = process.env.KAZZLE_TRIGGER_SECRET ?? '';

Bun.serve({
  port: Number(process.env.PORT),
  hostname: process.env.HOST,
  async fetch(req) {
    const url = new URL(req.url);

    if (req.method === 'POST' && url.pathname === '/cron/daily-digest') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      await sendDigest();
      return Response.json({ ok: true });
    }

    if (req.method === 'POST' && url.pathname === '/webhook/stripe') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      const event = await req.json();
      await handleStripe(event);
      return Response.json({ ok: true });
    }

    return new Response('not found', { status: 404 });
  },
});
```

## Chế độ triggered — một lần cho mỗi trigger

Khi một trigger kích hoạt cho một thành phần `triggered`, Kazzle tạo tập lệnh entry mới và chờ nó thoát. Không có `path`; tập lệnh tìm hiểu trigger nào kích hoạt từ các biến môi trường.

| Biến môi trường   | Giá trị                                 |
| ----------------- | --------------------------------------- |
| `TRIGGER_NAME`    | `name` của trigger từ manifest.         |
| `TRIGGERED_BY`    | `cron` \| `webhook` \| `manual`.        |
| `RUN_ID`          | ID không rõ ràng để tương quan nhật ký. |
| `WEBHOOK_PAYLOAD` | Phần thân JSON (chỉ webhook triggers).  |

```ts theme={"theme":"material-theme-darker"}
// components/events/index.ts (triggered mode)
const trigger = process.env.TRIGGER_NAME;
const runId = process.env.RUN_ID;

if (trigger === 'daily-digest') {
  await sendDigest();
} else if (trigger === 'stripe') {
  const event = JSON.parse(process.env.WEBHOOK_PAYLOAD ?? '{}');
  await handleStripe(event);
}

console.log(`run ${runId} done`);
```

Các thành phần triggered không có máy nhàn rỗi trên production — chúng khởi động cho mỗi cuộc gọi và tắt khi thoát.

## URL Webhook

```text theme={"theme":"material-theme-darker"}
POST https://api.kazzle.app/webhooks/{spaceId}/{appId}/{componentName}/{triggerName}
```

Phân đoạn `triggerName` phải khớp với một mục `kind: 'webhook'` trong `triggers[]` của thành phần đó. Các tên trigger không xác định trả về 404.

## Độ phân giải lịch trình

Biểu thức cron là 5 trường (phút, giờ, ngày trong tháng, tháng, ngày trong tuần) và độ phân giải phút là mức tối thiểu. Các lịch trình dưới phút bị từ chối tại thời điểm xác thực manifest.

## Cách ghi lại các lần chạy

Mỗi lần kích hoạt trigger ghi một hàng `process_runs` với `trigger_name`, `triggered_by`, `run_id` và trạng thái thoát của lần chạy. Bạn có thể truy vấn những điều này từ mã của riêng bạn hoặc kiểm tra chúng trong chế độ xem chạy của ứng dụng.

## Hết tín dụng

Một lần chạy không thành công được ghi lại và ghi nhật ký, nhưng lịch trình tiếp tục chạy theo nhịp độ bình thường — một lần chạy không ổn định không bao giờ vô hiệu hóa trigger. Điều duy nhất dừng một lần chạy là tín dụng: mỗi lần kích hoạt trigger được kiểm tra dựa trên số dư của không gian, và trong khi không gian hết tín dụng (hoặc không có thiết lập thanh toán) các lần chạy bị bỏ qua với `402`. Điều này tự khôi phục — lịch trình vẫn được vũ trang và lần kích hoạt tiếp theo sau khi bạn nạp tiền chạy bình thường, không cần tiếp tục thủ công.

## Thêm tự động hóa sau

Một ứng dụng đơn giản có thể bắt đầu mà không có trigger và có được chúng sau — thêm bản tóm tắt hàng ngày, kết nối Stripe, chạy dọn dẹp. Vòng đời của thành phần (`processMode`) và triggers (`triggers[]`) độc lập, vì vậy bạn có thể thay đổi chúng mà không cần viết lại phần còn lại của ứng dụng.
