Word2PPT
Word2PPT adalah aplikasi konversi dokumen yang memungkinkan pengguna mengubah dokumen Word (DOC & DOCX) menjadi presentasi PowerPoint (PPTX) dengan bantuan AI. Aplikasi ini dibangun dengan fokus pada:
- Modularitas: Setiap komponen memiliki tanggung jawab spesifik dan dapat diuji secara independen
- Reliability: Implementasi failover strategy untuk menjamin ketersediaan layanan
- Type Safety: Penggunaan TypeScript untuk meminimalisir runtime errors
Arsitektur Sistem

1. Server Layer
Server dibangun dengan Express.js + TypeScript untuk menjamin type safety dan developer experience yang lebih baik:
- API Routes: Penanganan endpoint untuk upload dan konversi dokumen
- File Upload: Validasi dan manajemen file upload dengan rate limiting
- Error Handler: Penanganan error terstruktur dengan logging komprehensif
- Response Formatter: Standarisasi format response API
2. Service Layer
Menggunakan Provider pattern untuk ekstensibilitas optimal:
Document Processing
- Validator: Memastikan dokumen sesuai format yang didukung
- Converter: Transformasi DOC/DOCX ke format Markdown,
- Processor: Ekstraksi dan strukturisasi konten untuk AI processing
AI Summarization
Implementasi failover strategy dengan multiple provider:
- Gemini (prioritas pertama, tersedia versi gratis dengan limit paling tinggi)
- Groq (backup, versi gratis memiliki batasan lebih rendah daripada Gemini)
- Deepseek (backup, versi berbayar)
Penerapan Arsitektur
Proyek ini menggunakan teknologi sebagai berikut
- Backend: Node.js + Express + TypeScript
- Document Processing: Mammoth.js
- AI Integration: Gemini API, Groq API
- Testing: Jest
- Deployment: Docker, Nginx
Dan dalam penerapannya, menggunakan prinsip Modularity, Reliability & Maintainability yang contohnya dapat dilihat di bawah
Modularity: Provider Pattern untuk AI Services
interface SummaryProvider {
readonly priority: number
isAvailable(): boolean
summarize(content: string): Promise<string>
}
class GeminiProvider implements SummaryProvider {
readonly priority = 1
isAvailable(): boolean {
return !!process.env.GEMINI_API_KEY
}
async summarize(content: string): Promise<string> {
const model = genAI.getGenerativeModel({ model: 'gemini-pro' })
const result = await model.generateContent(content)
return result.response.text()
}
}
Reliability: Failover Strategy
class SummarizerService {
private static providers = [
new GeminiProvider(), // Primary
new GroqProvider(), // Secondary
new DeepseekProvider() // Tertiary
].sort((a, b) => a.priority - b.priority)
static async summarize(content: string): Promise<string> {
for (const provider of this.providers) {
if (!provider.isAvailable()) continue
try {
return await provider.summarize(content)
} catch (error) {
Logger.error(`${provider.constructor.name} failed: ${error.message}`)
}
}
throw new Error('No available providers')
}
}Maintainability: Unit testing yang komprehensif
describe('GeminiProvider', () => {
let provider: GeminiProvider;
beforeEach(() => {
process.env.GEMINI_API_KEY = 'test_api_key';
provider = new GeminiProvider();
});
it('should be available when API key is set', () => {
expect(provider.isAvailable()).toBe(true);
});
it('should handle API errors', async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: false,
status: 400,
statusText: 'Bad Request'
});
await expect(provider.summarize('Test'))
.rejects.toThrow('Gemini API request failed');
});
});
Deployment
Aplikasi di-deploy menggunakan Docker dengan multi-stage build untuk optimasi ukuran image:
# Build Stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production Stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
ENV NODE_ENV=production
HEALTHCHECK CMD curl -f http://localhost:3000/health
EXPOSE 3000
CMD ["node", "dist/index.js"]Struktur ini menggunakan dua stage untuk mengoptimalkan ukuran image:
- Build Stage: Melakukan kompilasi TypeScript dan menginstall semua dependensi
- Production Stage: Hanya menyalin hasil build dan dependensi untuk env production
Keuntungan pendekatan ini:
- Image size yang lebih kecil (tidak ada dev dependencies dan source code)
- Keamanan yang lebih baik (source code tidak ada di final image)
- Startup time yang lebih cepat
Tantangan
1. Parsing Dokumen Word
Parsing dokumen Word menghadirkan beberapa tantangan teknis:
- Dokumen Word (terutama .doc) memiliki struktur biner kompleks
- Mempertahankan formatting seperti heading, list, dan tabel
- Menangani embedded content (gambar, grafik)
- Memproses paragraf dan menjaga struktur dokumen
- Mengkonversi tabel ke format teks dengan separator
| - Menangani nested elements (teks dalam tabel)
- Menjaga spacing dan formatting dasar
- Validasi dan Error Handling
- Validasi format file yang didukung
- Batasan ukuran file (5MB)
- Error handling yang konsisten
- Custom error class untuk konversi
Cara Konversi Dokumen Word
Sistem mendukung konversi dari dua format dokumen Word: .doc (legacy) dan .docx (modern XML-based). Setiap format memiliki pendekatan konversi yang berbeda:
export class ConverterService {
// Validasi file input
static validateFile(file: Express.Multer.File | undefined): void {
if (!file) throw new ConversionError('No file uploaded')
const ext = extname(file.originalname).toLowerCase()
if (!['.doc', '.docx'].includes(ext)) {
throw new ConversionError('Only DOC and DOCX files are allowed')
}
if (file.size > MAX_FILE_SIZE) {
throw new ConversionError('File too large')
}
}
// Konversi format .doc menggunakan word.js
static async convertDoc(buffer: Buffer): Promise<string> {
try {
const doc = read(buffer)
if (!doc || !doc.p) {
throw new ConversionError('Could not convert DOC file')
}
return this.extractText(doc)
} catch (error) {
throw new ConversionError('Could not convert DOC file')
}
}
// Konversi format .docx menggunakan mammoth + turndown
static async convertDocx(buffer: Buffer): Promise<string> {
try {
const { value: html } = await mammoth.convertToHtml({ buffer })
return turndown.turndown(html)
} catch (error) {
throw new ConversionError('Could not convert DOCX file')
}
}
}
Format Modern (.docx)
// Contoh penggunaan mammoth untuk konversi docx
async function convertDocxToHtml(buffer: Buffer): Promise<string> {
const result = await mammoth.convertToHtml(
{ buffer },
{
styleMap: [
"p[style-name='Heading 1'] => h1",
"p[style-name='Heading 2'] => h2",
"p[style-name='List Paragraph'] => li"
]
}
);
return result.value;
}
Format Legacy (.doc)
Library word membaca dokumen .doc sebagai struktur kompleks yang terdiri dari paragraf, teks, dan tabel. Proses ekstraksi teks memerlukan penanganan khusus:
interface DocElement {
type: 'text' | 'table'
content: string | string[][]
}
static extractText(doc: DocElement[]): string {
return doc.map(element => {
if (element.type === 'text') {
return element.content as string
}
if (element.type === 'table') {
return (element.content as string[][])
.map(row => row.join(' | '))
.join('\n')
}
return ''
}).join('\n\n')
}
2. Chunking untuk API Limits
Chunking/membagi teks ke dalam bagian yang lebih kecil dilakukan untuk menghindari limit dari API LLM gratis yang dipakai. Proses chunking yang dilakukan harus:
- Memastikan chunk tidak memotong kalimat di tengah
- Menangani referensi antar bagian dokumen
- Jumlah chunk tidak terlalu banyak agar tidak terkena rate limit
- Menggabungkan hasil AI summary dari multiple chunks
Pembuatan Chunk
export class SummarizerService {
private static readonly MAX_CHUNK_SIZE = 20000;
static splitByWhitespace(chunk: string): string[] {
// Hapus gambar base64 untuk menghemat token
chunk = this.removeBase64Images(chunk);
const subChunks: string[] = [];
let currentSubChunk = "";
// Split berdasarkan baris untuk menjaga konteks
const lines = chunk.split("\n");
for (const line of lines) {
if ((currentSubChunk + line).length > this.MAX_CHUNK_SIZE) {
subChunks.push(currentSubChunk.trim());
currentSubChunk = line;
} else {
currentSubChunk += "\n" + line;
}
}
if (currentSubChunk.trim()) {
subChunks.push(currentSubChunk.trim());
}
return subChunks;
}
}
export class ChunkOptimizer {
static async processBatch(chunks: string[]): Promise<string> {
// Proses parallel dengan rate limiting
const summaries = await Promise.all(
chunks.map(async (chunk, index) => {
// Delay antar request untuk menghindari rate limit
await new Promise(r => setTimeout(r, index * 1000));
return await SummarizerService.summarize(chunk);
})
);
// Gabungkan hasil dengan tetap menjaga struktur
return this.mergeSummaries(summaries);
}
}
Solusi untuk tantangan-tantangan ini memerlukan kombinasi dari:
- Preprocessing dokumen yang robust
- Strategi chunking yang cerdas
- Rate limiting dan error handling yang baik
- Sistem caching untuk mengoptimalkan penggunaan API
Penerapan AI Assisted Coding
Pengembangan Word2PPT memanfaatkan AI dalam beberapa aspek:
- Code Generation dan Refactoring
- Type Safety dan Error Handling.
AI membantu mengidentifikasi potential type errors dan mengusulkan penanganan yang lebih baik: - Testing Strategy
AI membantu mengidentifikasi test cases penting dan edge cases: - Dokumentasi dan Code Review
AI membantu dalam:- Mengidentifikasi bagian kode yang memerlukan dokumentasi
- Mendeteksi potential bugs dan anti-patterns
- Memberikan saran untuk optimasi performa
- Memastikan konsistensi gaya penulisan kode
- Dependency Management
AI membantu memilih dan mengkonfigurasi dependencies:- Memilih library yang well-maintained (
mammoth,turndown) - Mengidentifikasi potential security vulnerabilities
- Menyarankan alternatif package yang lebih ringan
- Membantu dalam upgrade dependencies
- Memilih library yang well-maintained (
- Continuous Improvement
AI berperan dalam proses iteratif:- Menganalisis code smells dan technical debt
- Menyarankan refactoring opportunities
- Membantu implementasi best practices
- Memberikan insight dari pattern umum di komunitas
Kesimpulan
Arsitektur Word2PPT dirancang dengan fokus pada extensibility, reliability & maintainability. Penggunaan TypeScript dan implementasi unit testing memastikan codebase tetap terstruktur dan mudah dikembangkan. Provider Pattern yang diimplementasikan memungkinkan penambahan fitur baru tanpa mengubah core functionality.