markmv
Version:
TypeScript CLI for markdown file operations with intelligent link refactoring
145 lines • 5.14 kB
JavaScript
/**
* Native Node.js REST API server for markmv
*
* Provides a lightweight HTTP API using only Node.js built-in modules. Uses auto-generated route
* definitions from JSON Schema-first approach. Exposes markmv functionality via RESTful endpoints
* for language-agnostic access.
*/
import * as http from 'node:http';
import * as url from 'node:url';
import { createMarkMv } from './index.js';
import { autoGeneratedApiRoutes, getApiRoutePaths } from './generated/api-routes.js';
const markmv = createMarkMv();
const startTime = Date.now();
/** Type guard to check if an object is a valid API route */
function isApiRoute(obj) {
return (typeof obj === 'object' &&
obj !== null &&
'method' in obj &&
'path' in obj &&
'handler' in obj &&
typeof obj.method === 'string' &&
typeof obj.path === 'string' &&
typeof obj.handler === 'function');
}
/** Send JSON response with proper headers */
function sendJSON(response, statusCode, data) {
response.writeHead(statusCode, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
});
response.end(JSON.stringify(data));
}
/** Create a standardized API response */
function createApiResponse(success, data, error, details) {
const response = {
success,
timestamp: new Date().toISOString(),
};
if (data !== undefined) {
response.data = data;
}
if (error !== undefined) {
response.error = error;
}
if (details !== undefined) {
response.details = details;
}
return response;
}
/** Create an error response */
function createErrorResponse(statusCode, error, message, details) {
const response = {
error,
message,
statusCode,
};
if (details !== undefined) {
response.details = details;
}
return response;
}
/** Handle CORS preflight requests */
function handleCORS(request, response) {
if (request.method === 'OPTIONS') {
response.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
});
response.end();
return true;
}
return false;
}
/** Handle health check endpoint */
async function handleHealth(response) {
const healthResponse = {
status: 'ok',
version: '1.0.0',
uptime: Date.now() - startTime,
info: {
service: 'markmv-api',
description: 'Markdown file operations API',
},
};
const apiResponse = createApiResponse(true, healthResponse);
sendJSON(response, 200, apiResponse);
}
/** Main request handler */
async function handleRequest(request, response) {
// Handle CORS preflight
if (handleCORS(request, response)) {
return;
}
const { method, url: reqUrl } = request;
const parsedUrl = url.parse(reqUrl || '', true);
const path = parsedUrl.pathname;
try {
// Handle health endpoint
if (method === 'GET' && path === '/health') {
await handleHealth(response);
return;
}
// Handle auto-generated API routes
const autoRoute = autoGeneratedApiRoutes.find((route) => {
return isApiRoute(route) && route.method === method && route.path === path;
});
if (autoRoute && isApiRoute(autoRoute)) {
// Call the auto-generated handler with the markmv instance
await autoRoute.handler(request, response, markmv);
return;
}
// Route not found
const availableRoutes = ['GET /health', ...getApiRoutePaths().map((p) => `POST ${p}`)];
const errorResponse = createErrorResponse(404, 'NotFound', `Route ${method} ${path} not found`, [`Available routes: ${availableRoutes.join(', ')}`]);
sendJSON(response, 404, errorResponse);
}
catch (error) {
console.error('Unhandled error:', error);
const errorResponse = createErrorResponse(500, 'InternalServerError', 'An unexpected error occurred');
sendJSON(response, 500, errorResponse);
}
}
/** Create and start the HTTP server */
export function createApiServer(port = 3000) {
const server = http.createServer(handleRequest);
server.listen(port, () => {
console.log(`markmv API server running on port ${port}`);
console.log(`Health check: http://localhost:${port}/health`);
console.log(`API endpoints: http://localhost:${port}/api/*`);
});
return server;
}
/** Start the API server with environment-based configuration */
export function startApiServer() {
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000;
return createApiServer(port);
}
// For direct execution
if (process.argv[1] && process.argv[1].endsWith('api-server.js')) {
startApiServer();
}
//# sourceMappingURL=api-server.js.map