feat(auth): implement complete API-key authentication with modular architecture (#47)
- Add comprehensive API-key authentication system with X-API-Key header validation - Implement permission-based access control (mail:send, * for admin) - Add rate-limiting system (60 req/hour per API key, 100 req/hour per IP) - Refactor monolithic 590-line main.lua into 6 modular components (<200 lines each) - Add IP-restriction support with CIDR notation (127.0.0.1, 10.0.0.0/8) - Implement Hugo integration with CORS support for localhost:1313 - Add production-ready configuration with environment variable support - Create comprehensive testing suite (auth, rate-limiting, stress tests) - Add production deployment checklist and cleanup scripts This refactoring transforms the API gateway from a single-file monolith into a biocodie-compliant modular architecture while adding enterprise-grade security features. Performance testing shows 79 RPS concurrent throughput with <100ms latency. Hugo contact form integration tested and working. System is now production-ready for deployment to walter/aitvaras. Resolves #47
This commit is contained in:
parent
445e751c16
commit
901f5eb2d8
14 changed files with 1160 additions and 80 deletions
113
furt-lua/src/routes/mail.lua
Normal file
113
furt-lua/src/routes/mail.lua
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
-- furt-lua/src/routes/mail.lua
|
||||
-- Mail service route handler
|
||||
-- Dragons@Work Digital Sovereignty Project
|
||||
|
||||
local cjson = require("cjson")
|
||||
|
||||
local MailRoute = {}
|
||||
|
||||
-- Load configuration
|
||||
local config = require("config.server")
|
||||
|
||||
-- Validate email format
|
||||
local function validate_email(email)
|
||||
return email and email:match("^[^@]+@[^@]+%.[^@]+$") ~= nil
|
||||
end
|
||||
|
||||
-- Validate required fields
|
||||
local function validate_mail_data(data)
|
||||
if not data.name or type(data.name) ~= "string" or data.name:match("^%s*$") then
|
||||
return false, "Name is required and cannot be empty"
|
||||
end
|
||||
|
||||
if not data.email or not validate_email(data.email) then
|
||||
return false, "Valid email address is required"
|
||||
end
|
||||
|
||||
if not data.message or type(data.message) ~= "string" or data.message:match("^%s*$") then
|
||||
return false, "Message is required and cannot be empty"
|
||||
end
|
||||
|
||||
-- Optional subject validation
|
||||
if data.subject and (type(data.subject) ~= "string" or #data.subject > 200) then
|
||||
return false, "Subject must be a string with max 200 characters"
|
||||
end
|
||||
|
||||
-- Message length validation
|
||||
if #data.message > 5000 then
|
||||
return false, "Message too long (max 5000 characters)"
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Generate unique request ID
|
||||
local function generate_request_id()
|
||||
return os.time() .. "-" .. math.random(1000, 9999)
|
||||
end
|
||||
|
||||
-- Mail service handler
|
||||
function MailRoute.handle_mail_send(request, server)
|
||||
print("Mail endpoint called - Method: " .. request.method .. ", Path: " .. request.path)
|
||||
print("Authenticated as: " .. request.auth.key_name .. " (" .. request.auth.api_key .. ")")
|
||||
|
||||
-- Basic request validation
|
||||
if not request.body or request.body == "" then
|
||||
return {error = "No request body", code = "MISSING_BODY"}
|
||||
end
|
||||
|
||||
-- Parse JSON
|
||||
local success, data = pcall(cjson.decode, request.body)
|
||||
if not success then
|
||||
return {error = "Invalid JSON", body = request.body, code = "INVALID_JSON"}
|
||||
end
|
||||
|
||||
-- Validate mail data
|
||||
local valid, error_message = validate_mail_data(data)
|
||||
if not valid then
|
||||
return {error = error_message, code = "VALIDATION_ERROR"}
|
||||
end
|
||||
|
||||
-- Generate request ID for tracking
|
||||
local request_id = generate_request_id()
|
||||
|
||||
-- Prepare email content
|
||||
local subject = data.subject or "Contact Form Message"
|
||||
local email_content = string.format(
|
||||
"From: %s <%s>\nSubject: %s\n\n%s",
|
||||
data.name, data.email, subject, data.message
|
||||
)
|
||||
|
||||
-- Send email via SMTP
|
||||
local SMTP = require("src.smtp")
|
||||
local smtp_client = SMTP:new(config.mail)
|
||||
|
||||
local smtp_success, smtp_result = smtp_client:send_email(
|
||||
config.mail.to_address,
|
||||
subject,
|
||||
email_content,
|
||||
data.name
|
||||
)
|
||||
|
||||
if smtp_success then
|
||||
-- Success response
|
||||
return {
|
||||
success = true,
|
||||
message = "Mail sent successfully",
|
||||
request_id = request_id,
|
||||
api_key_name = request.auth.key_name
|
||||
}
|
||||
else
|
||||
-- SMTP error - log and return error
|
||||
print("SMTP Error: " .. tostring(smtp_result))
|
||||
return server:create_response(500, {
|
||||
success = false,
|
||||
error = "Failed to send email: " .. tostring(smtp_result),
|
||||
request_id = request_id,
|
||||
code = "SMTP_ERROR"
|
||||
}, nil, nil, request)
|
||||
end
|
||||
end
|
||||
|
||||
return MailRoute
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue