# คู่มือการปรับแต่ง Postiz Media Manager
เอกสารนี้อธิบายจุดที่คุณต้องปรับแต่งให้เข้ากับ Postiz API ของคุณ
## 🔧 จุดสำคัญที่ต้องแก้ไข
### 1. API Endpoints (`src/postizClient.ts`)
#### ⚠️ PLACEHOLDER Endpoints ที่ต้องแก้
ไฟล์: `src/postizClient.ts`
```typescript
// ❌ PLACEHOLDER - ต้องแก้ตามเอกสาร API ของคุณ
'/v1/posts' // สำหรับดึงรายการโพสต์
'/v1/media' // สำหรับดึงรายการ media
'/v1/media/:id' // สำหรับลบ media
```
#### วิธีแก้ไข:
1. **เปิดเอกสาร Postiz API** ของคุณ
2. **ค้นหา endpoint สำหรับ:**
- List posts
- List media
- Delete media
3. **แก้ไขใน `postizClient.ts`:**
```typescript
// ตัวอย่าง: ถ้า API จริงของคุณเป็น
async listPosts(params: ListPostsParams = {}): Promise<PostizPost[]> {
// เปลี่ยนจาก '/v1/posts' เป็น endpoint จริง
const response = await this.client.get<ApiResponse<PostizPost>>('/api/posts', {
params: queryParams,
});
// ...
}
```
#### พารามิเตอร์ที่อาจต้องปรับ:
```typescript
// ❌ PLACEHOLDER
queryParams.statuses = params.statuses.join(',');
// อาจต้องเปลี่ยนเป็น
queryParams['filter[status]'] = params.statuses.join(',');
// หรือ
queryParams.status = params.statuses;
// หรือรูปแบบอื่นตามเอกสาร API
```
---
### 2. Media Field Extraction (`src/mediaService.ts`)
#### ⚠️ ปรับให้ตรงกับโครงสร้าง Response
ไฟล์: `src/mediaService.ts` → ฟังก์ชัน `extractMediaIds()`
**สาเหตุ:** Postiz แต่ละเวอร์ชันอาจใช้ชื่อฟิลด์ต่างกัน
#### ตัวอย่างโครงสร้าง Response ที่เป็นไปได้:
**กรณีที่ 1: Array of strings**
```json
{
"id": "post_123",
"status": "draft",
"mediaIds": ["media_1", "media_2"]
}
```
✅ Code ปัจจุบันรองรับอยู่แล้ว
**กรณีที่ 2: Array of objects**
```json
{
"id": "post_123",
"status": "draft",
"media": [
{ "id": "media_1", "url": "..." },
{ "id": "media_2", "url": "..." }
]
}
```
✅ Code ปัจจุบันรองรับอยู่แล้ว
**กรณีที่ 3: Nested structure**
```json
{
"id": "post_123",
"status": "draft",
"content": {
"attachments": ["media_1", "media_2"]
}
}
```
❌ ต้องเพิ่ม code:
```typescript
private extractMediaIds(post: PostizPost): string[] {
const ids: string[] = [];
// เพิ่มการจัดการ nested structure
if (post.content?.attachments && Array.isArray(post.content.attachments)) {
ids.push(...post.content.attachments);
}
// ... code อื่น ๆ
return ids;
}
```
#### วิธีตรวจสอบโครงสร้างจริง:
1. **เรียก API ดู response:**
```bash
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-postiz-api.com/v1/posts?limit=1
```
2. **ดูว่า media อยู่ในฟิลด์ไหน:**
```json
{
"data": [{
"id": "...",
"mediaIds": [...] // <-- ฟิลด์นี้
}]
}
```
3. **แก้ไขใน `extractMediaIds()`** ให้ตรงกับฟิลด์ที่เจอ
---
### 3. Response Structure (`src/postizClient.ts`)
#### ⚠️ ปรับให้ตรงกับ Pagination
**กรณีที่ 1: Response แบบ wrapper**
```json
{
"data": [...],
"pagination": {
"page": 1,
"hasNext": true
}
}
```
✅ Code ปัจจุบันรองรับ
**กรณีที่ 2: Response แบบ flat array**
```json
[
{ "id": "post_1" },
{ "id": "post_2" }
]
```
❌ ต้องแก้:
```typescript
async listPosts(params: ListPostsParams = {}): Promise<PostizPost[]> {
const response = await this.client.get<PostizPost[]>('/v1/posts', {
params: queryParams,
});
// เปลี่ยนจาก
const posts = response.data.data || response.data as any;
// เป็น
const posts = response.data;
// ...
}
```
**กรณีที่ 3: Cursor-based pagination**
```json
{
"items": [...],
"nextCursor": "abc123"
}
```
❌ ต้องแก้:
```typescript
while (true) {
const queryParams: Record<string, any> = {
cursor: currentCursor,
limit: perPage,
};
const response = await this.client.get<any>('/v1/posts', {
params: queryParams,
});
allPosts.push(...response.data.items);
if (!response.data.nextCursor) {
break;
}
currentCursor = response.data.nextCursor;
}
```
---
### 4. Authentication (`src/postizClient.ts`)
#### ⚠️ ปรับวิธี Auth ให้ถูกต้อง
**ปัจจุบัน: Bearer Token**
```typescript
headers: {
'Authorization': `Bearer ${token}`,
}
```
**ถ้า API ใช้วิธีอื่น:**
**API Key in header:**
```typescript
headers: {
'X-API-Key': token,
}
```
**Basic Auth:**
```typescript
auth: {
username: 'api',
password: token,
}
```
**Query parameter:**
```typescript
params: {
api_key: token,
}
```
---
### 5. Post Statuses (`src/types.ts`)
#### ⚠️ ตรวจสอบชื่อ status ที่ถูกต้อง
ไฟล์: `src/types.ts`
```typescript
export const FUTURE_POST_STATUSES = ['draft', 'scheduled', 'queued'] as const;
```
**ถ้า Postiz ของคุณใช้ชื่อต่างกัน:**
```typescript
// ตัวอย่าง
export const FUTURE_POST_STATUSES = [
'DRAFT', // uppercase
'PENDING', // แทน 'queued'
'SCHEDULED', // uppercase
] as const;
```
#### วิธีตรวจสอบ:
```bash
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-postiz-api.com/v1/posts
```
ดูฟิลด์ `status` ในผลลัพธ์
---
## 🧪 วิธีทดสอบหลังแก้ไข
### 1. Test API Connection
สร้างไฟล์ `test.ts`:
```typescript
import { postizClient } from './src/postizClient.js';
async function test() {
try {
const posts = await postizClient.listPosts({ perPage: 1 });
console.log('✅ Connection OK:', posts.length);
const media = await postizClient.listMedia({ perPage: 1 });
console.log('✅ Media OK:', media.length);
} catch (error) {
console.error('❌ Error:', error);
}
}
test();
```
รัน:
```bash
npm run build
node dist/test.js
```
### 2. Test Media Extraction
เพิ่ม logging ใน `extractMediaIds()`:
```typescript
private extractMediaIds(post: PostizPost): string[] {
const ids: string[] = [];
console.log('DEBUG: Post structure:', JSON.stringify(post, null, 2));
// ... code อื่น ๆ
console.log('DEBUG: Extracted IDs:', ids);
return ids;
}
```
### 3. Test แบบ Dry-Run
```json
{
"tool": "postiz_cleanup_orphan_media",
"input": {
"dryRun": true,
"limit": 1
}
}
```
---
## 📋 Checklist การปรับแต่ง
- [ ] แก้ `POSTIZ_BASE_URL` ใน `.env`
- [ ] แก้ `POSTIZ_API_TOKEN` ใน `.env`
- [ ] ตรวจสอบ endpoint URLs ใน `postizClient.ts`
- [ ] ปรับ query parameters ตามเอกสาร API
- [ ] ปรับ response structure parsing
- [ ] ตรวจสอบ pagination mechanism
- [ ] แก้ `extractMediaIds()` ให้ตรงกับโครงสร้างข้อมูล
- [ ] ตรวจสอบ post statuses ที่ใช้
- [ ] ตรวจสอบวิธี authentication
- [ ] ทดสอบด้วย `npm run build`
- [ ] ทดสอบ API connection
- [ ] ทดสอบด้วย dry-run
- [ ] ทดสอบลบจริงด้วย limit น้อย ๆ
---
## 🆘 การ Debug
### เปิด Debug Logging
แก้ไข `.env`:
```env
LOG_LEVEL=debug
```
### เพิ่ม Console Logs
ใน `postizClient.ts`:
```typescript
async listPosts(params: ListPostsParams = {}): Promise<PostizPost[]> {
console.log('DEBUG: Request params:', queryParams);
const response = await this.client.get<ApiResponse<PostizPost>>('/v1/posts', {
params: queryParams,
});
console.log('DEBUG: Response structure:', JSON.stringify(response.data, null, 2));
// ...
}
```
### ดู Network Requests
ใช้ Axios interceptor:
```typescript
this.client.interceptors.request.use((config) => {
console.log('→ Request:', config.method?.toUpperCase(), config.url, config.params);
return config;
});
this.client.interceptors.response.use((response) => {
console.log('← Response:', response.status, response.data);
return response;
});
```
---
## 💡 Tips
1. **เริ่มจาก API Documentation** - อ่านเอกสารก่อนแก้ code
2. **ทดสอบทีละขั้น** - แก้ไขครั้งละนิดทดสอบทันที
3. **ใช้ curl ทดสอบ** - ยิง API ด้วย curl ก่อนจะแก้ code
4. **เก็บ log** - เพิ่ม console.log ตอน debug
5. **Backup ก่อน** - สำรองข้อมูลก่อนลบจริง