feat: production-ready deployment with multi-language UI
- Add multi-language support (NL, AR, EN) with RTL
- Improve health checks (SSL-tolerant, multi-endpoint)
- Add DELETE /api/stack/:name for cleanup
- Add persistent storage (portal-ai-workspace-{name})
- Improve rollback (delete domain, app, project)
- Increase SSE timeout to 255s
- Add deployment strategy documentation
This commit is contained in:
66
src/index.ts
66
src/index.ts
@@ -86,8 +86,8 @@ async function deployStack(deploymentId: string): Promise<void> {
|
||||
dockerImage: process.env.STACK_IMAGE || 'git.app.flexinit.nl/oussamadouhou/oh-my-opencode-free:latest',
|
||||
domainSuffix: process.env.STACK_DOMAIN_SUFFIX || 'ai.flexinit.nl',
|
||||
port: 8080,
|
||||
healthCheckTimeout: 60000, // 60 seconds
|
||||
healthCheckInterval: 5000, // 5 seconds
|
||||
healthCheckTimeout: 180000,
|
||||
healthCheckInterval: 5000,
|
||||
});
|
||||
|
||||
// Final update with logs
|
||||
@@ -371,9 +371,66 @@ app.get('/api/check/:name', async (c) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/stack/:name', async (c) => {
|
||||
try {
|
||||
const name = c.req.param('name');
|
||||
const normalizedName = name.trim().toLowerCase();
|
||||
const projectName = `ai-stack-${normalizedName}`;
|
||||
|
||||
const client = createProductionDokployClient();
|
||||
const existingProject = await client.findProjectByName(projectName);
|
||||
|
||||
if (!existingProject) {
|
||||
return c.json({
|
||||
success: false,
|
||||
error: 'Stack not found',
|
||||
code: 'NOT_FOUND'
|
||||
}, 404);
|
||||
}
|
||||
|
||||
console.log(`Deleting stack: ${projectName} (projectId: ${existingProject.project.projectId})`);
|
||||
|
||||
await client.deleteProject(existingProject.project.projectId);
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
message: `Stack ${normalizedName} deleted successfully`,
|
||||
deletedProjectId: existingProject.project.projectId
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Delete endpoint error:', error);
|
||||
return c.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to delete stack',
|
||||
code: 'DELETE_FAILED'
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Serve static files (frontend)
|
||||
app.use('/static/*', serveStatic({ root: './src/frontend' }));
|
||||
app.use('/*', serveStatic({ root: './src/frontend', path: '/index.html' }));
|
||||
// Serve CSS and JS files directly
|
||||
app.get('/style.css', async (c) => {
|
||||
const file = Bun.file('./src/frontend/style.css');
|
||||
return new Response(file, {
|
||||
headers: { 'Content-Type': 'text/css' }
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/app.js', async (c) => {
|
||||
const file = Bun.file('./src/frontend/app.js');
|
||||
return new Response(file, {
|
||||
headers: { 'Content-Type': 'application/javascript' }
|
||||
});
|
||||
});
|
||||
|
||||
// Serve index.html for all other routes (SPA fallback)
|
||||
app.get('/', async (c) => {
|
||||
const file = Bun.file('./src/frontend/index.html');
|
||||
return new Response(file, {
|
||||
headers: { 'Content-Type': 'text/html' }
|
||||
});
|
||||
});
|
||||
|
||||
console.log(`🚀 AI Stack Deployer (Production) starting on http://${HOST}:${PORT}`);
|
||||
console.log(`✅ Production features enabled:`);
|
||||
@@ -387,4 +444,5 @@ export default {
|
||||
port: PORT,
|
||||
hostname: HOST,
|
||||
fetch: app.fetch,
|
||||
idleTimeout: 255,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user