diff --git a/src/pages/contribute/index.astro b/src/pages/contribute/index.astro index d939d8f..af271ba 100644 --- a/src/pages/contribute/index.astro +++ b/src/pages/contribute/index.astro @@ -39,108 +39,130 @@ const { authenticated, userEmail, userId } = authResult; -
- - -
-
-
- - - -
-

Tools, Methods & Concepts

-
- -

- Add new software tools, forensic methodologies, or fundamental concepts to our database. - Includes detailed forms for metadata, licensing, platforms, and categorization. + +

+ + +
+
+
+ + + +
+

Tools, Methods & Concepts

+
+ +

+ Add new software tools, forensic methodologies, or fundamental concepts to our database. + Includes detailed forms for metadata, licensing, platforms, and categorization. +

+ +
+ Software Tools + Methods + Concepts +
+ +
+ Add New Entry + Edit Existing +
+
+ + +
+
+
+ + + + + + + +
+

Knowledgebase Articles

+
+ +

+ Write detailed guides, tutorials, configuration instructions, and best practices. + Features a markdown editor with live preview and media upload capabilities. +

+ +
+ Installation Guides + Tutorials + Best Practices + Case Studies +
+ +
+ Write Article + View Articles +
+
+ + +
+
+
+ + + + + +
+

Issues & Improvements

+
+ +
+
+

+ Found incorrect information, broken links, or have suggestions for improvements? + Report issues directly in our Git repository or suggest enhancements to existing entries.

- -
- Software Tools - Methods - Concepts -
- -
- Add New Entry - Edit Existing +
+ Bug Reports + Corrections + Suggestions
- - -
-
-
- - - - - - - -
-

Knowledgebase Articles

-
- -

- Write detailed guides, tutorials, configuration instructions, and best practices. - Features a markdown editor with live preview and media upload capabilities. -

- -
- Installation Guides - Tutorials - Best Practices - Case Studies -
- - -
- - -
-
-
- - - - - -
-

Issues & Improvements

-
- -
-
-

- Found incorrect information, broken links, or have suggestions for improvements? - Report issues directly in our Git repository or suggest enhancements to existing entries. -

-
- Bug Reports - Corrections - Suggestions -
-
- -
+
+ +
+
+ +
+ +

Contribution Guidelines

diff --git a/src/utils/api.ts b/src/utils/api.ts new file mode 100644 index 0000000..99a142a --- /dev/null +++ b/src/utils/api.ts @@ -0,0 +1,157 @@ +// src/utils/api.ts + +// Standard JSON headers for all API responses +const JSON_HEADERS = { + 'Content-Type': 'application/json' +} as const; + +/** + * Base function to create consistent API responses + * All other response helpers use this internally + */ +export function createAPIResponse(data: any, status: number = 200, additionalHeaders?: Record): Response { + const headers = additionalHeaders + ? { ...JSON_HEADERS, ...additionalHeaders } + : JSON_HEADERS; + + return new Response(JSON.stringify(data), { + status, + headers + }); +} + +/** + * Success responses (2xx status codes) + */ +export const apiResponse = { + // 200 - Success with data + success: (data: any = { success: true }): Response => + createAPIResponse(data, 200), + + // 201 - Created (for contribution submissions, uploads, etc.) + created: (data: any = { success: true }): Response => + createAPIResponse(data, 201), + + // 202 - Accepted (for async operations) + accepted: (data: any = { success: true, message: 'Request accepted for processing' }): Response => + createAPIResponse(data, 202) +}; + +/** + * Client error responses (4xx status codes) + */ +export const apiError = { + // 400 - Bad Request + badRequest: (message: string = 'Bad request', details?: string[]): Response => + createAPIResponse({ + success: false, + error: message, + ...(details && { details }) + }, 400), + + // 401 - Unauthorized + unauthorized: (message: string = 'Authentication required'): Response => + createAPIResponse({ success: false, error: message }, 401), + + // 403 - Forbidden + forbidden: (message: string = 'Access denied'): Response => + createAPIResponse({ success: false, error: message }, 403), + + // 404 - Not Found + notFound: (message: string = 'Resource not found'): Response => + createAPIResponse({ success: false, error: message }, 404), + + // 422 - Unprocessable Entity (validation errors) + validation: (message: string = 'Validation failed', details?: string[]): Response => + createAPIResponse({ + success: false, + error: message, + ...(details && { details }) + }, 422), + + // 429 - Rate Limited + rateLimit: (message: string = 'Rate limit exceeded. Please wait before trying again.'): Response => + createAPIResponse({ success: false, error: message }, 429) +}; + +/** + * Server error responses (5xx status codes) + */ +export const apiServerError = { + // 500 - Internal Server Error + internal: (message: string = 'Internal server error'): Response => + createAPIResponse({ success: false, error: message }, 500), + + // 502 - Bad Gateway (external service issues) + badGateway: (message: string = 'External service error'): Response => + createAPIResponse({ success: false, error: message }, 502), + + // 503 - Service Unavailable + unavailable: (message: string = 'Service temporarily unavailable'): Response => + createAPIResponse({ success: false, error: message }, 503), + + // 504 - Gateway Timeout + timeout: (message: string = 'Request timeout'): Response => + createAPIResponse({ success: false, error: message }, 504) +}; + +/** + * Specialized response helpers for common patterns + */ +export const apiSpecial = { + // JSON parsing error + invalidJSON: (): Response => + apiError.badRequest('Invalid JSON in request body'), + + // Missing required fields + missingRequired: (fields: string[]): Response => + apiError.badRequest(`Missing required fields: ${fields.join(', ')}`), + + // Empty request body + emptyBody: (): Response => + apiError.badRequest('Request body cannot be empty'), + + // File upload responses + uploadSuccess: (data: { url: string; filename: string; size: number; storage: string }): Response => + apiResponse.created(data), + + uploadFailed: (error: string): Response => + apiServerError.internal(`Upload failed: ${error}`), + + // Contribution responses + contributionSuccess: (data: { prUrl?: string; branchName?: string; message: string }): Response => + apiResponse.created({ success: true, ...data }), + + contributionFailed: (error: string): Response => + apiServerError.internal(`Contribution failed: ${error}`) +}; + +export const apiWithHeaders = { + // Success with custom headers (e.g., Set-Cookie) + successWithHeaders: (data: any, headers: Record): Response => + createAPIResponse(data, 200, headers), + + // Redirect response + redirect: (location: string, temporary: boolean = true): Response => + new Response(null, { + status: temporary ? 302 : 301, + headers: { 'Location': location } + }) +}; + +export async function handleAPIRequest( + operation: () => Promise, + errorMessage: string = 'Request processing failed' +): Promise { + try { + return await operation(); + } catch (error) { + console.error(`API Error: ${errorMessage}:`, error); + return apiServerError.internal(errorMessage); + } +} + +export const createAuthErrorResponse = apiError.unauthorized; +export const createBadRequestResponse = apiError.badRequest; +export const createSuccessResponse = apiResponse.success; +