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

# Anonymous wish wall for birthday messages

> Visitors write anonymous birthday messages at /write. Wishes are stored in your database, color-coded automatically, and displayed on the home page wall.

The wish wall lets anyone leave a birthday message without creating an account. Visitors navigate to `/write`, type their message, and submit — the wish is stored in Supabase and appears on the home page wall. Every message is assigned a random accent color so the wall looks lively and varied.

## Writing a wish

Open `/write` in any browser. You will see a full-screen form with a textarea and a character counter.

<Steps>
  <Step title="Type your message">
    Write anything up to 500 characters. The counter at the bottom of the textarea updates as you type.
  </Step>

  <Step title="Submit">
    Click **送上祝福**. The app posts your message to `/api/messages`. All submissions are anonymous — no name or login is required.
  </Step>

  <Step title="Confirmation">
    On success, the form is replaced by a confirmation screen. After three seconds you are automatically redirected to the home page to see your wish on the wall.
  </Step>
</Steps>

## Validation and error messages

Client-side validation runs before the request is sent. The API also validates server-side.

| Condition                         | Error message       |
| --------------------------------- | ------------------- |
| Empty or whitespace-only content  | `写点什么吧~`            |
| Content longer than 500 chars     | `太长了，控制在 500 字以内哦`  |
| More than 3 messages in 5 minutes | `祝福太多啦，休息 5 分钟再发吧~` |

## Rate limiting

The API limits each IP address to **3 messages per 5-minute window**. The IP is hashed with SHA-256 before being stored, so no raw IP addresses are persisted in your database.

<Warning>
  Rate limiting is based on the `x-forwarded-for` and `x-real-ip` headers. Make sure your hosting platform passes these headers correctly. If both are absent, all requests fall under the key `"unknown"` and share a single rate-limit bucket.
</Warning>

## Message colors

Each submitted message is randomly assigned one of six Tailwind color names: `rose`, `sky`, `amber`, `emerald`, `violet`, or `pink`. The color is stored in the `color` column and used by the home page to style each card.

## API reference

### GET /api/messages

Returns up to 100 messages, ordered newest first.

**Response**

```json theme={null}
{
  "messages": [
    {
      "id": "uuid",
      "content": "Happy birthday!",
      "color": "rose",
      "created_at": "2026-05-24T10:00:00.000Z"
    }
  ]
}
```

<ResponseField name="messages" type="object[]" required>
  Array of message objects, newest first. Maximum 100 items.

  <Expandable title="message properties">
    <ResponseField name="id" type="string" required>
      UUID primary key generated by Supabase.
    </ResponseField>

    <ResponseField name="content" type="string" required>
      The wish text, trimmed of leading and trailing whitespace.
    </ResponseField>

    <ResponseField name="color" type="string" required>
      One of `rose`, `sky`, `amber`, `emerald`, `violet`, `pink`.
    </ResponseField>

    <ResponseField name="created_at" type="string" required>
      ISO 8601 timestamp of when the message was inserted.
    </ResponseField>
  </Expandable>
</ResponseField>

***

### POST /api/messages

Submit a new wish.

**Request body**

<ParamField body="content" type="string" required>
  The birthday message. Must be between 1 and 500 characters after trimming whitespace.
</ParamField>

**Response codes**

| Status | Meaning                                    |
| ------ | ------------------------------------------ |
| 201    | Message created successfully.              |
| 400    | Validation failed (empty or too long).     |
| 429    | Rate limit exceeded.                       |
| 500    | Database error or unexpected server error. |

**Success response (201)**

```json theme={null}
{
  "message": {
    "id": "uuid",
    "content": "Wishing you the best birthday ever!",
    "color": "emerald",
    "created_at": "2026-05-24T10:00:00.000Z"
  }
}
```

**Code examples**

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://your-site.com/api/messages \
    --header 'Content-Type: application/json' \
    --data '{"content": "Happy birthday! Wishing you all the best!"}'
  ```

  ```javascript fetch theme={null}
  const response = await fetch('/api/messages', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ content: 'Happy birthday! Wishing you all the best!' }),
  });

  const data = await response.json();

  if (!response.ok) {
    console.error(data.error);
  } else {
    console.log('Wish submitted:', data.message);
  }
  ```
</CodeGroup>

## Admin panel

A password-protected admin panel lets you view and delete messages. Access it from the navbar when you are authenticated. The panel shows all messages with their timestamps, colors, and the option to remove any entry.

<Note>
  The admin panel requires the `ADMIN_PASSWORD` environment variable to be set. See the deployment guide for details.
</Note>
