@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
1,138 lines (950 loc) • 36.5 kB
JavaScript
"use strict";
/**
* Zig HTTP Server Template Generator
* Using Zig's standard library HTTP server
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpServerGenerator = void 0;
const zig_base_generator_1 = require("./zig-base-generator");
const fs_1 = require("fs");
const path = __importStar(require("path"));
class HttpServerGenerator extends zig_base_generator_1.ZigBackendGenerator {
constructor() {
super('HTTP Server');
}
async generateFrameworkFiles(projectPath, options) {
// Generate main application
await this.generateMainApp(projectPath, options);
// Generate server module
await this.generateServer(projectPath);
// Generate router
await this.generateRouter(projectPath);
// Generate handlers
await this.generateHandlers(projectPath);
// Generate middleware
await this.generateMiddleware(projectPath);
// Generate models
await this.generateModels(projectPath);
// Generate utilities
await this.generateUtilities(projectPath);
// Generate configuration
await this.generateConfig(projectPath, options);
// Generate database module
await this.generateDatabase(projectPath);
// Generate authentication
await this.generateAuth(projectPath);
}
async generateMainApp(projectPath, options) {
const mainContent = `const std = ;
const server = ;
const config = ;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Load configuration
const app_config = try config.load(allocator);
defer app_config.deinit();
// Initialize and start server
var http_server = try server.Server.init(allocator, app_config);
defer http_server.deinit();
std.log.info("🚀 Server starting on http://{s}:{d}", .{ app_config.host, app_config.port });
try http_server.listen();
}
test "main tests" {
const testing = std.testing;
// Add main tests here
}
`;
await fs_1.promises.writeFile(path.join(projectPath, 'src', 'main.zig'), mainContent);
}
async generateServer(projectPath) {
const serverContent = `const std = ;
const http = std.http;
const router = ;
const middleware = ;
const Config = .Config;
pub const Server = struct {
allocator: std.mem.Allocator,
config: Config,
server: http.Server,
router: router.Router,
pub fn init(allocator: std.mem.Allocator, config: Config) !Server {
const address = try std.net.Address.parseIp(config.host, config.port);
const server_instance = http.Server.init(allocator, .{
.reuse_address = true,
.reuse_port = true,
});
return Server{
.allocator = allocator,
.config = config,
.server = server_instance,
.router = try router.Router.init(allocator),
};
}
pub fn deinit(self: *Server) void {
self.server.deinit();
self.router.deinit();
}
pub fn listen(self: *Server) !void {
const address = try std.net.Address.parseIp(self.config.host, self.config.port);
try self.server.listen(address);
while (true) {
var response = try self.server.accept(.{
.allocator = self.allocator,
});
defer response.deinit();
// Handle the request in a separate thread
const thread = try std.Thread.spawn(.{}, handleRequest, .{ self, &response });
thread.detach();
}
}
fn handleRequest(self: *Server, response: *http.Server.Response) void {
self.processRequest(response) catch |err| {
std.log.err("Error handling request: {}", .{err});
response.status = .internal_server_error;
response.do() catch {};
};
}
fn processRequest(self: *Server, response: *http.Server.Response) !void {
// Read request body
const body = try response.reader().readAllAlloc(self.allocator, 1024 * 1024); // 1MB limit
defer self.allocator.free(body);
// Apply middleware
try middleware.cors(response);
try middleware.logger(response);
// Route the request
try self.router.route(response, body);
// Send response
try response.do();
}
};
test "server initialization" {
const testing = std.testing;
const allocator = testing.allocator;
const config = Config{
.host = "127.0.0.1",
.port = 8080,
.allocator = allocator,
};
var server = try Server.init(allocator, config);
defer server.deinit();
try testing.expect(server.config.port == 8080);
}
`;
await fs_1.promises.writeFile(path.join(projectPath, 'src', 'server.zig'), serverContent);
}
async generateRouter(projectPath) {
const routerContent = `const std = ;
const http = std.http;
const handlers = ;
pub const Route = struct {
method: http.Method,
path: []const u8,
handler: *const fn (*http.Server.Response, []const u8) anyerror!void,
};
pub const Router = struct {
allocator: std.mem.Allocator,
routes: std.ArrayList(Route),
pub fn init(allocator: std.mem.Allocator) !Router {
var r = Router{
.allocator = allocator,
.routes = std.ArrayList(Route).init(allocator),
};
// Register routes
try r.registerRoutes();
return r;
}
pub fn deinit(self: *Router) void {
self.routes.deinit();
}
fn registerRoutes(self: *Router) !void {
// Health routes
try self.addRoute(.GET, "/health", handlers.health.handleHealth);
try self.addRoute(.GET, "/ready", handlers.health.handleReady);
// Auth routes
try self.addRoute(.POST, "/api/v1/auth/register", handlers.auth.handleRegister);
try self.addRoute(.POST, "/api/v1/auth/login", handlers.auth.handleLogin);
try self.addRoute(.POST, "/api/v1/auth/refresh", handlers.auth.handleRefresh);
try self.addRoute(.POST, "/api/v1/auth/logout", handlers.auth.handleLogout);
// User routes
try self.addRoute(.GET, "/api/v1/users", handlers.users.handleList);
try self.addRoute(.GET, "/api/v1/users/me", handlers.users.handleGetCurrent);
try self.addRoute(.GET, "/api/v1/users/:id", handlers.users.handleGetById);
try self.addRoute(.PUT, "/api/v1/users/:id", handlers.users.handleUpdate);
try self.addRoute(.DELETE, "/api/v1/users/:id", handlers.users.handleDelete);
}
fn addRoute(self: *Router, method: http.Method, path: []const u8, handler: anytype) !void {
try self.routes.append(Route{
.method = method,
.path = path,
.handler = handler,
});
}
pub fn route(self: *Router, response: *http.Server.Response, body: []const u8) !void {
const request = response.request;
const method = request.method;
const target = request.target;
// Find matching route
for (self.routes.items) |r| {
if (r.method == method and self.matchPath(r.path, target)) {
try r.handler(response, body);
return;
}
}
// 404 Not Found
response.status = .not_found;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Not Found", "message": "The requested resource was not found"}
);
}
fn matchPath(self: *Router, pattern: []const u8, path: []const u8) bool {
// Simple path matching (extend for parameter support)
if (std.mem.indexOf(u8, pattern, ":") != null) {
// TODO: Implement parameter matching
return self.matchPathWithParams(pattern, path);
}
return std.mem.eql(u8, pattern, path);
}
fn matchPathWithParams(self: *Router, pattern: []const u8, path: []const u8) bool {
var pattern_parts = std.mem.tokenize(u8, pattern, "/");
var path_parts = std.mem.tokenize(u8, path, "/");
while (pattern_parts.next()) |pattern_part| {
const path_part = path_parts.next() orelse return false;
if (pattern_part[0] == ':') {
// Parameter - matches any value
continue;
}
if (!std.mem.eql(u8, pattern_part, path_part)) {
return false;
}
}
return path_parts.next() == null;
}
};
test "router path matching" {
const testing = std.testing;
const allocator = testing.allocator;
var r = try Router.init(allocator);
defer r.deinit();
try testing.expect(r.matchPath("/api/users", "/api/users"));
try testing.expect(!r.matchPath("/api/users", "/api/posts"));
try testing.expect(r.matchPath("/api/users/:id", "/api/users/123"));
}
`;
await fs_1.promises.writeFile(path.join(projectPath, 'src', 'router.zig'), routerContent);
}
async generateHandlers(projectPath) {
const handlersDir = path.join(projectPath, 'src', 'handlers');
// Handlers index
const handlersIndexContent = `pub const health = ;
pub const auth = ;
pub const users = ;
`;
await fs_1.promises.writeFile(path.join(handlersDir, 'handlers.zig'), handlersIndexContent);
// Auth handlers
const authHandlersContent = `const std = ;
const http = std.http;
const json = std.json;
const auth_service = ;
const models = ;
pub fn handleRegister(response: *http.Server.Response, body: []const u8) !void {
const allocator = response.allocator;
// Parse request body
const parsed = try json.parseFromSlice(models.RegisterRequest, allocator, body, .{});
defer parsed.deinit();
const req = parsed.value;
// Validate input
if (req.email.len == 0 or req.password.len < 6 or req.name.len == 0) {
response.status = .bad_request;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Validation Error", "message": "Invalid input data"}
);
return;
}
// Check if user exists (mock)
// In production, check database
// Hash password
const hashed_password = try auth_service.hashPassword(allocator, req.password);
defer allocator.free(hashed_password);
// Create user (mock)
const user = models.User{
.id = "user123",
.email = req.email,
.name = req.name,
.role = "user",
};
// Generate token
const token = try auth_service.generateToken(allocator, user.id, user.email, user.role);
defer allocator.free(token);
// Send response
response.status = .created;
try response.headers.append("content-type", "application/json");
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
try json.stringify(.{
.user = user,
.token = token,
}, .{}, buffer.writer());
try response.writeAll(buffer.items);
}
pub fn handleLogin(response: *http.Server.Response, body: []const u8) !void {
const allocator = response.allocator;
// Parse request body
const parsed = try json.parseFromSlice(models.LoginRequest, allocator, body, .{});
defer parsed.deinit();
const req = parsed.value;
// Validate input
if (req.email.len == 0 or req.password.len == 0) {
response.status = .bad_request;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Validation Error", "message": "Email and password required"}
);
return;
}
// Find user and verify password (mock)
// In production, query database and verify password
const user = models.User{
.id = "user123",
.email = req.email,
.name = "Test User",
.role = "user",
};
// Generate token
const token = try auth_service.generateToken(allocator, user.id, user.email, user.role);
defer allocator.free(token);
// Send response
response.status = .ok;
try response.headers.append("content-type", "application/json");
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
try json.stringify(.{
.user = user,
.token = token,
}, .{}, buffer.writer());
try response.writeAll(buffer.items);
}
pub fn handleRefresh(response: *http.Server.Response, body: []const u8) !void {
_ = body;
const allocator = response.allocator;
// Get token from header
const auth_header = response.request.headers.getFirstValue("authorization") orelse {
response.status = .unauthorized;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Unauthorized", "message": "Missing authorization header"}
);
return;
};
// Verify token and refresh
// In production, implement proper token refresh logic
const new_token = try auth_service.generateToken(allocator, "user123", "user@example.com", "user");
defer allocator.free(new_token);
response.status = .ok;
try response.headers.append("content-type", "application/json");
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
try json.stringify(.{ .token = new_token }, .{}, buffer.writer());
try response.writeAll(buffer.items);
}
pub fn handleLogout(response: *http.Server.Response, body: []const u8) !void {
_ = body;
// In production, blacklist token or clear session
response.status = .ok;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"message": "Logged out successfully"}
);
}
`;
await fs_1.promises.writeFile(path.join(handlersDir, 'auth.zig'), authHandlersContent);
// Users handlers
const usersHandlersContent = `const std = ;
const http = std.http;
const json = std.json;
const models = ;
const auth = ;
pub fn handleList(response: *http.Server.Response, body: []const u8) !void {
_ = body;
const allocator = response.allocator;
// Check authorization
if (!try auth.isAuthorized(response.request, "admin")) {
response.status = .forbidden;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Forbidden", "message": "Insufficient permissions"}
);
return;
}
// Mock user list
const users = [_]models.User{
.{ .id = "1", .email = "user1@example.com", .name = "User One", .role = "user" },
.{ .id = "2", .email = "user2@example.com", .name = "User Two", .role = "admin" },
};
response.status = .ok;
try response.headers.append("content-type", "application/json");
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
try json.stringify(.{
.data = users,
.meta = .{
.page = 1,
.limit = 10,
.total = users.len,
},
}, .{}, buffer.writer());
try response.writeAll(buffer.items);
}
pub fn handleGetCurrent(response: *http.Server.Response, body: []const u8) !void {
_ = body;
const allocator = response.allocator;
// Get current user from token
const current_user = try auth.getCurrentUser(response.request) orelse {
response.status = .unauthorized;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Unauthorized", "message": "Not authenticated"}
);
return;
};
response.status = .ok;
try response.headers.append("content-type", "application/json");
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
try json.stringify(current_user, .{}, buffer.writer());
try response.writeAll(buffer.items);
}
pub fn handleGetById(response: *http.Server.Response, body: []const u8) !void {
_ = body;
const allocator = response.allocator;
// Extract ID from path
const path = response.request.target;
const id = extractIdFromPath(path) orelse {
response.status = .bad_request;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Bad Request", "message": "Invalid user ID"}
);
return;
};
// Mock user lookup
const user = models.User{
.id = id,
.email = "user@example.com",
.name = "Test User",
.role = "user",
};
response.status = .ok;
try response.headers.append("content-type", "application/json");
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
try json.stringify(user, .{}, buffer.writer());
try response.writeAll(buffer.items);
}
pub fn handleUpdate(response: *http.Server.Response, body: []const u8) !void {
const allocator = response.allocator;
// Extract ID from path
const path = response.request.target;
const id = extractIdFromPath(path) orelse {
response.status = .bad_request;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Bad Request", "message": "Invalid user ID"}
);
return;
};
// Parse update request
const parsed = try json.parseFromSlice(models.UpdateUserRequest, allocator, body, .{});
defer parsed.deinit();
const req = parsed.value;
// Mock user update
const updated_user = models.User{
.id = id,
.email = req.email orelse "user@example.com",
.name = req.name orelse "Updated User",
.role = "user",
};
response.status = .ok;
try response.headers.append("content-type", "application/json");
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
try json.stringify(updated_user, .{}, buffer.writer());
try response.writeAll(buffer.items);
}
pub fn handleDelete(response: *http.Server.Response, body: []const u8) !void {
_ = body;
// Extract ID from path
const path = response.request.target;
const id = extractIdFromPath(path) orelse {
response.status = .bad_request;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Bad Request", "message": "Invalid user ID"}
);
return;
};
// Check authorization
if (!try auth.isAuthorized(response.request, "admin")) {
response.status = .forbidden;
try response.headers.append("content-type", "application/json");
try response.writeAll(
\\{"error": "Forbidden", "message": "Insufficient permissions"}
);
return;
}
// Mock user deletion
_ = id;
response.status = .no_content;
}
fn extractIdFromPath(path: []const u8) ?[]const u8 {
// Simple ID extraction from path like /api/v1/users/123
const prefix = "/api/v1/users/";
if (std.mem.startsWith(u8, path, prefix)) {
return path[prefix.len..];
}
return null;
}
`;
await fs_1.promises.writeFile(path.join(handlersDir, 'users.zig'), usersHandlersContent);
}
async generateMiddleware(projectPath) {
const middlewareDir = path.join(projectPath, 'src', 'middleware');
// Middleware index
const middlewareIndexContent = `const std = ;
const http = std.http;
pub fn cors(response: *http.Server.Response) !void {
try response.headers.append("Access-Control-Allow-Origin", "*");
try response.headers.append("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
try response.headers.append("Access-Control-Allow-Headers", "Content-Type, Authorization");
if (response.request.method == .OPTIONS) {
response.status = .no_content;
}
}
pub fn logger(response: *http.Server.Response) !void {
const method = ;
const path = response.request.target;
const timestamp = std.time.timestamp();
std.log.info("[{d}] {s} {s}", .{ timestamp, method, path });
}
pub fn rateLimiter(allocator: std.mem.Allocator, response: *http.Server.Response) !void {
// Simple rate limiting implementation
const RateLimitData = struct {
requests: u32,
reset_time: i64,
};
// In production, use proper storage
_ = allocator;
const current_time = std.time.timestamp();
const rate_limit = 100; // requests per minute
// Mock rate limit check
const requests_made = 50;
if (requests_made >= rate_limit) {
response.status = .too_many_requests;
try response.headers.append("content-type", "application/json");
try response.headers.append("X-RateLimit-Limit", "100");
try response.headers.append("X-RateLimit-Remaining", "0");
try response.headers.append("X-RateLimit-Reset", try std.fmt.allocPrint(
response.allocator,
"{d}",
.{current_time + 60}
));
try response.writeAll(
\\{"error": "Too Many Requests", "message": "Rate limit exceeded"}
);
return;
}
try response.headers.append("X-RateLimit-Limit", "100");
try response.headers.append("X-RateLimit-Remaining", try std.fmt.allocPrint(
response.allocator,
"{d}",
.{rate_limit - requests_made}
));
}
`;
await fs_1.promises.writeFile(path.join(middlewareDir, 'middleware.zig'), middlewareIndexContent);
}
async generateModels(projectPath) {
const modelsDir = path.join(projectPath, 'src', 'models');
// Models index
const modelsIndexContent = `// User model
pub const User = struct {
id: []const u8,
email: []const u8,
name: []const u8,
role: []const u8,
};
// Auth request models
pub const RegisterRequest = struct {
email: []const u8,
password: []const u8,
name: []const u8,
};
pub const LoginRequest = struct {
email: []const u8,
password: []const u8,
};
// User request models
pub const UpdateUserRequest = struct {
email: ?[]const u8 = null,
name: ?[]const u8 = null,
};
// Response models
pub const ErrorResponse = struct {
error: []const u8,
message: []const u8,
details: ?[]const u8 = null,
};
pub const PaginatedResponse = struct {
data: []const User,
meta: struct {
page: u32,
limit: u32,
total: u32,
total_pages: u32,
},
};
// Token payload
pub const TokenPayload = struct {
sub: []const u8, // user id
email: []const u8,
role: []const u8,
exp: i64, // expiration timestamp
iat: i64, // issued at timestamp
};
`;
await fs_1.promises.writeFile(path.join(modelsDir, 'models.zig'), modelsIndexContent);
}
async generateUtilities(projectPath) {
const utilsDir = path.join(projectPath, 'src', 'utils');
// Validation utilities
const validationContent = `const std = ;
pub fn isValidEmail(email: []const u8) bool {
// Simple email validation
const at_index = std.mem.indexOf(u8, email, "@") orelse return false;
const dot_index = std.mem.lastIndexOf(u8, email, ".") orelse return false;
return at_index > 0 and dot_index > at_index + 1 and dot_index < email.len - 1;
}
pub fn isValidPassword(password: []const u8) bool {
return password.len >= 6;
}
pub fn sanitizeInput(allocator: std.mem.Allocator, input: []const u8) ![]u8 {
var result = std.ArrayList(u8).init(allocator);
defer result.deinit();
for (input) |char| {
switch (char) {
'<', '>', '"', '\'', '&' => {
// Skip potentially dangerous characters
continue;
},
else => try result.append(char),
}
}
return result.toOwnedSlice();
}
test "email validation" {
const testing = std.testing;
try testing.expect(isValidEmail("user@example.com"));
try testing.expect(!isValidEmail("invalid.email"));
try testing.expect(!isValidEmail("@example.com"));
try testing.expect(!isValidEmail("user@"));
}
test "password validation" {
const testing = std.testing;
try testing.expect(isValidPassword("secure123"));
try testing.expect(!isValidPassword("12345"));
}
`;
await fs_1.promises.writeFile(path.join(utilsDir, 'validation.zig'), validationContent);
// JSON utilities
const jsonUtilsContent = `const std = ;
const json = std.json;
pub fn stringify(allocator: std.mem.Allocator, value: anytype) ![]u8 {
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
try json.stringify(value, .{}, buffer.writer());
return buffer.toOwnedSlice();
}
pub fn parse(comptime T: type, allocator: std.mem.Allocator, data: []const u8) !json.Parsed(T) {
return json.parseFromSlice(T, allocator, data, .{});
}
pub fn sendJsonResponse(
response: *std.http.Server.Response,
status: std.http.Status,
data: anytype,
) !void {
response.status = status;
try response.headers.append("content-type", "application/json");
const json_data = try stringify(response.allocator, data);
defer response.allocator.free(json_data);
try response.writeAll(json_data);
}
`;
await fs_1.promises.writeFile(path.join(utilsDir, 'json.zig'), jsonUtilsContent);
}
async generateConfig(projectPath, options) {
const configDir = path.join(projectPath, 'src', 'config');
// Configuration module
const configContent = `const std = ;
pub const Config = struct {
allocator: std.mem.Allocator,
host: []const u8,
port: u16,
log_level: []const u8,
database_url: []const u8,
jwt_secret: []const u8,
cors_origin: []const u8,
pub fn deinit(self: Config) void {
// Free allocated strings if needed
_ = self;
}
};
pub fn load(allocator: std.mem.Allocator) !Config {
return Config{
.allocator = allocator,
.host = std.os.getenv("HOST") orelse "0.0.0.0",
.port = try parsePort(std.os.getenv("PORT") orelse "${options.port || 8080}"),
.log_level = std.os.getenv("LOG_LEVEL") orelse "info",
.database_url = std.os.getenv("DATABASE_URL") orelse "sqlite://./data/app.db",
.jwt_secret = std.os.getenv("JWT_SECRET") orelse "your-secret-key",
.cors_origin = std.os.getenv("CORS_ORIGIN") orelse "*",
};
}
fn parsePort(port_str: []const u8) !u16 {
return std.fmt.parseInt(u16, port_str, 10) catch |err| {
std.log.err("Invalid port number: {s}", .{port_str});
return err;
};
}
test "config loading" {
const testing = std.testing;
const allocator = testing.allocator;
const config = try load(allocator);
defer config.deinit();
try testing.expect(config.port > 0);
try testing.expect(config.host.len > 0);
}
`;
await fs_1.promises.writeFile(path.join(configDir, 'config.zig'), configContent);
}
async generateDatabase(projectPath) {
const dbContent = `const std = ;
const sqlite = ;
pub const Database = struct {
allocator: std.mem.Allocator,
db: ?*sqlite.sqlite3,
pub fn init(allocator: std.mem.Allocator, path: []const u8) !Database {
var db: ?*sqlite.sqlite3 = null;
const c_path = try allocator.dupeZ(u8, path);
defer allocator.free(c_path);
const result = sqlite.sqlite3_open(c_path, &db);
if (result != sqlite.SQLITE_OK) {
std.log.err("Failed to open database: {}", .{result});
return error.DatabaseOpenFailed;
}
// Create tables
try createTables(db.?);
return Database{
.allocator = allocator,
.db = db,
};
}
pub fn deinit(self: *Database) void {
if (self.db) |db| {
_ = sqlite.sqlite3_close(db);
}
}
fn createTables(db: *sqlite.sqlite3) !void {
const create_users_sql =
\\CREATE TABLE IF NOT EXISTS users (
\\ id TEXT PRIMARY KEY,
\\ email TEXT UNIQUE NOT NULL,
\\ password TEXT NOT NULL,
\\ name TEXT NOT NULL,
\\ role TEXT NOT NULL DEFAULT 'user',
\\ created_at INTEGER NOT NULL,
\\ updated_at INTEGER NOT NULL
\\);
;
var err_msg: ?[*:0]u8 = null;
const result = sqlite.sqlite3_exec(db, create_users_sql, null, null, &err_msg);
if (result != sqlite.SQLITE_OK) {
std.log.err("Failed to create tables: {s}", .{err_msg orelse "unknown error"});
if (err_msg) |msg| {
sqlite.sqlite3_free(msg);
}
return error.TableCreationFailed;
}
}
pub fn findUserByEmail(self: *Database, email: []const u8) !?User {
_ = self;
_ = email;
// TODO: Implement database query
return null;
}
pub fn createUser(self: *Database, user: User) !void {
_ = self;
_ = user;
// TODO: Implement database insert
}
};
const User = struct {
id: []const u8,
email: []const u8,
password: []const u8,
name: []const u8,
role: []const u8,
created_at: i64,
updated_at: i64,
};
`;
await fs_1.promises.writeFile(path.join(projectPath, 'src', 'utils', 'database.zig'), dbContent);
}
async generateAuth(projectPath) {
const authContent = `const std = ;
const crypto = std.crypto;
const http = std.http;
const models = ;
// Simple password hashing using SHA256 (use bcrypt in production)
pub fn hashPassword(allocator: std.mem.Allocator, password: []const u8) ![]u8 {
var hash: [crypto.hash.sha2.Sha256.digest_length]u8 = undefined;
crypto.hash.sha2.Sha256.hash(password, &hash, .{});
return std.fmt.allocPrint(allocator, "{}", .{std.fmt.fmtSliceHexLower(&hash)});
}
pub fn verifyPassword(password: []const u8, hash: []const u8) !bool {
const allocator = std.heap.page_allocator;
const computed_hash = try hashPassword(allocator, password);
defer allocator.free(computed_hash);
return std.mem.eql(u8, computed_hash, hash);
}
// Simple JWT implementation (use proper JWT library in production)
pub fn generateToken(
allocator: std.mem.Allocator,
user_id: []const u8,
email: []const u8,
role: []const u8,
) ![]u8 {
const header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; // {"alg":"HS256","typ":"JWT"}
const payload = models.TokenPayload{
.sub = user_id,
.email = email,
.role = role,
.exp = std.time.timestamp() + 7 * 24 * 60 * 60, // 7 days
.iat = std.time.timestamp(),
};
var payload_json = std.ArrayList(u8).init(allocator);
defer payload_json.deinit();
try std.json.stringify(payload, .{}, payload_json.writer());
const payload_base64 = try base64Encode(allocator, payload_json.items);
defer allocator.free(payload_base64);
const token = try std.fmt.allocPrint(allocator, "{s}.{s}.mock-signature", .{ header, payload_base64 });
return token;
}
pub fn verifyToken(allocator: std.mem.Allocator, token: []const u8) !models.TokenPayload {
// Simple token parsing (implement proper verification in production)
var parts = std.mem.tokenize(u8, token, ".");
_ = parts.next() orelse return error.InvalidToken; // header
const payload_part = parts.next() orelse return error.InvalidToken;
const payload_json = try base64Decode(allocator, payload_part);
defer allocator.free(payload_json);
const parsed = try std.json.parseFromSlice(models.TokenPayload, allocator, payload_json, .{});
defer parsed.deinit();
// Check expiration
if (parsed.value.exp < std.time.timestamp()) {
return error.TokenExpired;
}
return parsed.value;
}
pub fn isAuthorized(request: http.Server.Request, required_role: []const u8) !bool {
const auth_header = request.headers.getFirstValue("authorization") orelse return false;
if (!std.mem.startsWith(u8, auth_header, "Bearer ")) {
return false;
}
const token = auth_header[7..];
const allocator = std.heap.page_allocator;
const payload = verifyToken(allocator, token) catch return false;
if (required_role.len > 0 and !std.mem.eql(u8, payload.role, required_role)) {
return false;
}
return true;
}
pub fn getCurrentUser(request: http.Server.Request) !?models.User {
const auth_header = request.headers.getFirstValue("authorization") orelse return null;
if (!std.mem.startsWith(u8, auth_header, "Bearer ")) {
return null;
}
const token = auth_header[7..];
const allocator = std.heap.page_allocator;
const payload = verifyToken(allocator, token) catch return null;
return models.User{
.id = payload.sub,
.email = payload.email,
.name = "Test User", // In production, fetch from database
.role = payload.role,
};
}
fn base64Encode(allocator: std.mem.Allocator, data: []const u8) ![]u8 {
const encoder = std.base64.standard.Encoder;
const encoded_len = encoder.calcSize(data.len);
const encoded = try allocator.alloc(u8, encoded_len);
_ = encoder.encode(encoded, data);
return encoded;
}
fn base64Decode(allocator: std.mem.Allocator, data: []const u8) ![]u8 {
const decoder = std.base64.standard.Decoder;
const decoded_len = try decoder.calcSizeForSlice(data);
const decoded = try allocator.alloc(u8, decoded_len);
try decoder.decode(decoded, data);
return decoded;
}
test "password hashing" {
const testing = std.testing;
const allocator = testing.allocator;
const password = "test123";
const hash = try hashPassword(allocator, password);
defer allocator.free(hash);
try testing.expect(hash.len > 0);
try testing.expect(try verifyPassword(password, hash));
}
`;
await fs_1.promises.writeFile(path.join(projectPath, 'src', 'utils', 'auth.zig'), authContent);
}
}
exports.HttpServerGenerator = HttpServerGenerator;