UNPKG

@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,797 lines (1,536 loc) 54.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.shelfTemplate = void 0; exports.shelfTemplate = { id: 'shelf', name: 'shelf', displayName: 'Shelf Framework', description: 'Modular web server framework for Dart with middleware pipeline and routing', language: 'dart', framework: 'shelf', version: '1.4.1', tags: ['dart', 'shelf', 'api', 'rest', 'middleware', 'modular'], port: 8080, dependencies: {}, features: ['middleware', 'routing', 'cors', 'authentication', 'logging', 'static-files'], files: { // Dart project configuration 'pubspec.yaml': `name: {{projectName}} description: A server app using the shelf package and Docker. version: 1.0.0 publish_to: none environment: sdk: ^3.0.0 dependencies: args: ^2.4.0 shelf: ^1.4.1 shelf_router: ^1.1.4 shelf_static: ^1.1.2 shelf_cors_headers: ^0.1.5 shelf_hotreload: ^1.4.1 dotenv: ^4.2.0 postgres: ^3.0.0 mysql_client: ^0.0.27 sqlite3: ^2.1.0 crypto: ^3.0.3 jaguar_jwt: ^3.0.0 uuid: ^4.2.1 logger: ^2.0.2 collection: ^1.18.0 http: ^1.1.0 intl: ^0.18.1 dev_dependencies: build_runner: ^2.4.0 build_web_compilers: ^4.0.0 http: ^1.1.0 lints: ^3.0.0 test: ^1.24.0 test_process: ^2.1.0 coverage: ^1.7.1 mockito: ^5.4.3 build_test: ^2.2.1`, // Main entry point 'bin/server.dart': `import 'dart:io'; import 'package:args/args.dart'; import 'package:shelf/shelf.dart' as shelf; import 'package:shelf/shelf_io.dart' as io; import 'package:shelf_hotreload/shelf_hotreload.dart'; import 'package:{{projectName}}/app.dart'; import 'package:{{projectName}}/config/config.dart'; import 'package:{{projectName}}/database/database.dart'; import 'package:{{projectName}}/utils/logger.dart'; void main(List<String> args) async { var parser = ArgParser() ..addOption('port', abbr: 'p', defaultsTo: '8080') ..addOption('host', abbr: 'h', defaultsTo: '0.0.0.0') ..addFlag('hot-reload', abbr: 'r', defaultsTo: true); var result = parser.parse(args); var port = int.tryParse(result['port'] as String) ?? 8080; var host = result['host'] as String; var hotReload = result['hot-reload'] as bool; // Load configuration await Config.load(); // Initialize logger final logger = AppLogger(); // Initialize database try { await Database.initialize(); logger.info('Database connected successfully'); } catch (e) { logger.error('Failed to connect to database: $e'); exit(1); } // Run migrations in development if (Config.environment == 'development') { try { await Database.runMigrations(); logger.info('Database migrations completed'); } catch (e) { logger.error('Failed to run migrations: $e'); } } if (hotReload && Config.environment == 'development') { // Use hot reload in development withHotreload(() => createApp(), onReloaded: () { logger.info('🔥 Hot reload triggered'); }); } else { // Normal server start final handler = await createApp(); final server = await io.serve(handler, host, port); logger.info('🚀 Server listening on http://$host:$port'); // Graceful shutdown ProcessSignal.sigint.watch().listen((_) async { logger.info('Shutting down server...'); await server.close(); await Database.close(); exit(0); }); } }`, // Application setup 'lib/app.dart': `import 'package:shelf/shelf.dart'; import 'package:shelf_router/shelf_router.dart'; import 'package:shelf_cors_headers/shelf_cors_headers.dart'; import 'package:shelf_static/shelf_static.dart'; import 'controllers/auth_controller.dart'; import 'controllers/user_controller.dart'; import 'controllers/todo_controller.dart'; import 'middleware/auth_middleware.dart'; import 'middleware/error_middleware.dart'; import 'middleware/logging_middleware.dart'; import 'middleware/validation_middleware.dart'; import 'utils/response.dart'; Handler createApp() { final router = Router(); // API info router.get('/', (Request request) { return Response.ok(jsonResponse({ 'name': '{{projectName}} API', 'version': '1.0.0', 'status': 'running', })); }); // Health check router.get('/health', (Request request) async { final health = await _checkHealth(); return Response.ok(jsonResponse({ 'status': health ? 'healthy' : 'unhealthy', 'timestamp': DateTime.now().toIso8601String(), 'database': health, })); }); // API routes router.mount('/api/v1/', _apiRouter()); // Static files final staticHandler = createStaticHandler( 'public', defaultDocument: 'index.html', ); // Create pipeline with middleware final handler = Pipeline() .addMiddleware(corsHeaders()) .addMiddleware(logRequests()) .addMiddleware(loggingMiddleware()) .addMiddleware(errorMiddleware()) .addHandler( Cascade() .add(router) .add(staticHandler) .handler, ); return handler; } Router _apiRouter() { final router = Router(); // Authentication routes router.post('/auth/register', AuthController.register); router.post('/auth/login', AuthController.login); router.post('/auth/refresh', AuthController.refresh); // Protected routes router.mount( '/users', Pipeline() .addMiddleware(authMiddleware()) .addHandler(_userRouter()), ); router.mount( '/todos', Pipeline() .addMiddleware(authMiddleware()) .addHandler(_todoRouter()), ); return router; } Router _userRouter() { final router = Router(); router.get('/', UserController.list); router.get('/<id>', UserController.get); router.put('/<id>', Pipeline() .addMiddleware(validationMiddleware()) .addHandler(UserController.update)); router.delete('/<id>', UserController.delete); return router; } Router _todoRouter() { final router = Router(); router.get('/', TodoController.list); router.post('/', Pipeline() .addMiddleware(validationMiddleware()) .addHandler(TodoController.create)); router.get('/<id>', TodoController.get); router.put('/<id>', Pipeline() .addMiddleware(validationMiddleware()) .addHandler(TodoController.update)); router.delete('/<id>', TodoController.delete); return router; } Future<bool> _checkHealth() async { try { // Check database connection final db = Database.instance; await db.testConnection(); return true; } catch (e) { return false; } }`, // Configuration 'lib/config/config.dart': `import 'dart:io'; import 'package:dotenv/dotenv.dart'; class Config { static late DotEnv _env; static String get environment => _env['ENVIRONMENT'] ?? 'development'; static String get host => _env['HOST'] ?? '0.0.0.0'; static int get port => int.tryParse(_env['PORT'] ?? '') ?? 8080; // Database static String get dbType => _env['DB_TYPE'] ?? 'sqlite'; static String get dbHost => _env['DB_HOST'] ?? 'localhost'; static int get dbPort => int.tryParse(_env['DB_PORT'] ?? '') ?? 5432; static String get dbName => _env['DB_NAME'] ?? '{{projectName}}'; static String get dbUser => _env['DB_USER'] ?? 'postgres'; static String get dbPassword => _env['DB_PASSWORD'] ?? ''; static String get dbPath => _env['DB_PATH'] ?? 'database.db'; // Security static String get jwtSecret => _env['JWT_SECRET'] ?? 'your-secret-key'; static int get jwtExpiryMinutes => int.tryParse(_env['JWT_EXPIRY_MINUTES'] ?? '') ?? 15; static int get refreshTokenDays => int.tryParse(_env['REFRESH_TOKEN_DAYS'] ?? '') ?? 30; static Future<void> load() async { _env = DotEnv(includePlatformEnvironment: true); // Load .env file if it exists final envFile = File('.env'); if (await envFile.exists()) { _env.load(['.env']); } } }`, // Database setup 'lib/database/database.dart': `import 'package:postgres/postgres.dart'; import 'package:mysql_client/mysql_client.dart'; import 'package:sqlite3/sqlite3.dart'; import 'package:{{projectName}}/config/config.dart'; import 'package:{{projectName}}/utils/logger.dart'; export 'package:{{projectName}}/database/database.dart'; abstract class Database { static Database? _instance; static Database get instance => _instance!; static final _logger = AppLogger(); static Future<void> initialize() async { switch (Config.dbType) { case 'postgres': _instance = PostgresDatabase(); break; case 'mysql': _instance = MySQLDatabase(); break; default: _instance = SQLiteDatabase(); } await _instance!.connect(); } static Future<void> close() async { await _instance?.disconnect(); } static Future<void> runMigrations() async { await _instance?.migrate(); } Future<void> connect(); Future<void> disconnect(); Future<void> migrate(); Future<bool> testConnection(); Future<List<Map<String, dynamic>>> query(String sql, [List<Object?>? params]); Future<int> execute(String sql, [List<Object?>? params]); } class PostgresDatabase extends Database { Connection? _connection; @override Future<void> connect() async { _connection = await Connection.open( Endpoint( host: Config.dbHost, port: Config.dbPort, database: Config.dbName, username: Config.dbUser, password: Config.dbPassword, ), settings: ConnectionSettings( sslMode: SslMode.prefer, ), ); } @override Future<void> disconnect() async { await _connection?.close(); } @override Future<bool> testConnection() async { final result = await _connection!.execute('SELECT 1'); return result.isNotEmpty; } @override Future<List<Map<String, dynamic>>> query(String sql, [List<Object?>? params]) async { final result = await _connection!.execute( Sql.named(sql), parameters: params ?? [], ); return result.map((row) => row.toColumnMap()).toList(); } @override Future<int> execute(String sql, [List<Object?>? params]) async { final result = await _connection!.execute( Sql.named(sql), parameters: params ?? [], ); return result.affectedRows; } @override Future<void> migrate() async { // Create users table await execute(''' CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) '''); // Create todos table await execute(''' CREATE TABLE IF NOT EXISTS todos ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, title VARCHAR(255) NOT NULL, description TEXT, completed BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) '''); // Create refresh_tokens table await execute(''' CREATE TABLE IF NOT EXISTS refresh_tokens ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, token VARCHAR(255) UNIQUE NOT NULL, expires_at TIMESTAMP NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) '''); } } class MySQLDatabase extends Database { MySQLConnection? _connection; @override Future<void> connect() async { _connection = await MySQLConnection.createConnection( host: Config.dbHost, port: Config.dbPort, userName: Config.dbUser, password: Config.dbPassword, databaseName: Config.dbName, ); await _connection!.connect(); } @override Future<void> disconnect() async { await _connection?.close(); } @override Future<bool> testConnection() async { final result = await _connection!.execute('SELECT 1'); return result.rows.isNotEmpty; } @override Future<List<Map<String, dynamic>>> query(String sql, [List<Object?>? params]) async { final stmt = await _connection!.prepare(sql); final result = await stmt.execute(params ?? []); await stmt.deallocate(); return result.rows.map((row) => row.assoc()).toList(); } @override Future<int> execute(String sql, [List<Object?>? params]) async { final stmt = await _connection!.prepare(sql); final result = await stmt.execute(params ?? []); await stmt.deallocate(); return result.affectedRows.toInt(); } @override Future<void> migrate() async { // Similar migrations adapted for MySQL syntax await execute(''' CREATE TABLE IF NOT EXISTS users ( id CHAR(36) PRIMARY KEY DEFAULT (UUID()), email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) '''); await execute(''' CREATE TABLE IF NOT EXISTS todos ( id CHAR(36) PRIMARY KEY DEFAULT (UUID()), user_id CHAR(36) NOT NULL, title VARCHAR(255) NOT NULL, description TEXT, completed BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) '''); await execute(''' CREATE TABLE IF NOT EXISTS refresh_tokens ( id CHAR(36) PRIMARY KEY DEFAULT (UUID()), user_id CHAR(36) NOT NULL, token VARCHAR(255) UNIQUE NOT NULL, expires_at TIMESTAMP NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) '''); } } class SQLiteDatabase extends Database { Database? _db; @override Future<void> connect() async { _db = sqlite3.open(Config.dbPath); } @override Future<void> disconnect() async { _db?.dispose(); } @override Future<bool> testConnection() async { final result = _db!.select('SELECT 1'); return result.isNotEmpty; } @override Future<List<Map<String, dynamic>>> query(String sql, [List<Object?>? params]) async { final stmt = _db!.prepare(sql); final result = stmt.select(params ?? []); stmt.dispose(); return result.map((row) => Map<String, dynamic>.from(row)).toList(); } @override Future<int> execute(String sql, [List<Object?>? params]) async { final stmt = _db!.prepare(sql); stmt.execute(params ?? []); final affectedRows = _db!.updatedRows; stmt.dispose(); return affectedRows; } @override Future<void> migrate() async { // Enable foreign keys _db!.execute('PRAGMA foreign_keys = ON'); // Create users table await execute(''' CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))), email TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, name TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ) '''); // Create todos table await execute(''' CREATE TABLE IF NOT EXISTS todos ( id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))), user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, title TEXT NOT NULL, description TEXT, completed INTEGER DEFAULT 0, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ) '''); // Create refresh_tokens table await execute(''' CREATE TABLE IF NOT EXISTS refresh_tokens ( id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))), user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, token TEXT UNIQUE NOT NULL, expires_at TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP ) '''); } }`, // Models 'lib/models/user.dart': `import 'package:uuid/uuid.dart'; import 'package:crypto/crypto.dart'; import 'dart:convert'; class User { final String id; final String email; final String passwordHash; final String name; final DateTime createdAt; final DateTime updatedAt; User({ String? id, required this.email, required this.passwordHash, required this.name, DateTime? createdAt, DateTime? updatedAt, }) : id = id ?? const Uuid().v4(), createdAt = createdAt ?? DateTime.now(), updatedAt = updatedAt ?? DateTime.now(); factory User.fromMap(Map<String, dynamic> map) { return User( id: map['id'] as String, email: map['email'] as String, passwordHash: map['password_hash'] as String, name: map['name'] as String, createdAt: DateTime.parse(map['created_at'] as String), updatedAt: DateTime.parse(map['updated_at'] as String), ); } Map<String, dynamic> toMap() { return { 'id': id, 'email': email, 'password_hash': passwordHash, 'name': name, 'created_at': createdAt.toIso8601String(), 'updated_at': updatedAt.toIso8601String(), }; } Map<String, dynamic> toPublic() { return { 'id': id, 'email': email, 'name': name, 'createdAt': createdAt.toIso8601String(), }; } static String hashPassword(String password) { final bytes = utf8.encode(password); final digest = sha256.convert(bytes); return digest.toString(); } bool verifyPassword(String password) { return hashPassword(password) == passwordHash; } } class CreateUserRequest { final String email; final String password; final String name; CreateUserRequest({ required this.email, required this.password, required this.name, }); factory CreateUserRequest.fromJson(Map<String, dynamic> json) { return CreateUserRequest( email: json['email'] as String, password: json['password'] as String, name: json['name'] as String, ); } String? validate() { if (email.isEmpty || !email.contains('@')) { return 'Invalid email address'; } if (password.length < 8) { return 'Password must be at least 8 characters'; } if (name.isEmpty) { return 'Name is required'; } return null; } } class LoginRequest { final String email; final String password; LoginRequest({ required this.email, required this.password, }); factory LoginRequest.fromJson(Map<String, dynamic> json) { return LoginRequest( email: json['email'] as String, password: json['password'] as String, ); } } class UpdateUserRequest { final String? name; final String? email; UpdateUserRequest({ this.name, this.email, }); factory UpdateUserRequest.fromJson(Map<String, dynamic> json) { return UpdateUserRequest( name: json['name'] as String?, email: json['email'] as String?, ); } String? validate() { if (email != null && (email!.isEmpty || !email!.contains('@'))) { return 'Invalid email address'; } if (name != null && name!.isEmpty) { return 'Name cannot be empty'; } return null; } }`, 'lib/models/todo.dart': `import 'package:uuid/uuid.dart'; class Todo { final String id; final String userId; final String title; final String? description; final bool completed; final DateTime createdAt; final DateTime updatedAt; Todo({ String? id, required this.userId, required this.title, this.description, this.completed = false, DateTime? createdAt, DateTime? updatedAt, }) : id = id ?? const Uuid().v4(), createdAt = createdAt ?? DateTime.now(), updatedAt = updatedAt ?? DateTime.now(); factory Todo.fromMap(Map<String, dynamic> map) { return Todo( id: map['id'] as String, userId: map['user_id'] as String, title: map['title'] as String, description: map['description'] as String?, completed: (map['completed'] as num) == 1, createdAt: DateTime.parse(map['created_at'] as String), updatedAt: DateTime.parse(map['updated_at'] as String), ); } Map<String, dynamic> toMap() { return { 'id': id, 'user_id': userId, 'title': title, 'description': description, 'completed': completed ? 1 : 0, 'created_at': createdAt.toIso8601String(), 'updated_at': updatedAt.toIso8601String(), }; } Map<String, dynamic> toJson() { return { 'id': id, 'title': title, 'description': description, 'completed': completed, 'createdAt': createdAt.toIso8601String(), 'updatedAt': updatedAt.toIso8601String(), }; } } class CreateTodoRequest { final String title; final String? description; CreateTodoRequest({ required this.title, this.description, }); factory CreateTodoRequest.fromJson(Map<String, dynamic> json) { return CreateTodoRequest( title: json['title'] as String, description: json['description'] as String?, ); } String? validate() { if (title.isEmpty) { return 'Title is required'; } return null; } } class UpdateTodoRequest { final String? title; final String? description; final bool? completed; UpdateTodoRequest({ this.title, this.description, this.completed, }); factory UpdateTodoRequest.fromJson(Map<String, dynamic> json) { return UpdateTodoRequest( title: json['title'] as String?, description: json['description'] as String?, completed: json['completed'] as bool?, ); } String? validate() { if (title != null && title!.isEmpty) { return 'Title cannot be empty'; } return null; } }`, 'lib/models/token.dart': `import 'package:uuid/uuid.dart'; class RefreshToken { final String id; final String userId; final String token; final DateTime expiresAt; final DateTime createdAt; RefreshToken({ String? id, required this.userId, String? token, DateTime? expiresAt, DateTime? createdAt, }) : id = id ?? const Uuid().v4(), token = token ?? const Uuid().v4(), expiresAt = expiresAt ?? DateTime.now().add(const Duration(days: 30)), createdAt = createdAt ?? DateTime.now(); factory RefreshToken.fromMap(Map<String, dynamic> map) { return RefreshToken( id: map['id'] as String, userId: map['user_id'] as String, token: map['token'] as String, expiresAt: DateTime.parse(map['expires_at'] as String), createdAt: DateTime.parse(map['created_at'] as String), ); } Map<String, dynamic> toMap() { return { 'id': id, 'user_id': userId, 'token': token, 'expires_at': expiresAt.toIso8601String(), 'created_at': createdAt.toIso8601String(), }; } bool get isValid => expiresAt.isAfter(DateTime.now()); }`, // Repositories 'lib/repositories/user_repository.dart': `import 'package:{{projectName}}/database/database.dart'; import 'package:{{projectName}}/models/user.dart'; class UserRepository { final Database _db = Database.instance; Future<User?> findByEmail(String email) async { final results = await _db.query( 'SELECT * FROM users WHERE email = ?', [email], ); if (results.isEmpty) return null; return User.fromMap(results.first); } Future<User?> findById(String id) async { final results = await _db.query( 'SELECT * FROM users WHERE id = ?', [id], ); if (results.isEmpty) return null; return User.fromMap(results.first); } Future<List<User>> findAll() async { final results = await _db.query('SELECT * FROM users ORDER BY created_at DESC'); return results.map((row) => User.fromMap(row)).toList(); } Future<User> create(User user) async { await _db.execute( ''' INSERT INTO users (id, email, password_hash, name, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?) ''', [ user.id, user.email, user.passwordHash, user.name, user.createdAt.toIso8601String(), user.updatedAt.toIso8601String(), ], ); return user; } Future<User> update(User user) async { await _db.execute( ''' UPDATE users SET email = ?, name = ?, updated_at = ? WHERE id = ? ''', [ user.email, user.name, DateTime.now().toIso8601String(), user.id, ], ); return user; } Future<void> delete(String id) async { await _db.execute('DELETE FROM users WHERE id = ?', [id]); } }`, 'lib/repositories/todo_repository.dart': `import 'package:{{projectName}}/database/database.dart'; import 'package:{{projectName}}/models/todo.dart'; class TodoRepository { final Database _db = Database.instance; Future<List<Todo>> findByUserId(String userId) async { final results = await _db.query( 'SELECT * FROM todos WHERE user_id = ? ORDER BY created_at DESC', [userId], ); return results.map((row) => Todo.fromMap(row)).toList(); } Future<Todo?> findById(String id) async { final results = await _db.query( 'SELECT * FROM todos WHERE id = ?', [id], ); if (results.isEmpty) return null; return Todo.fromMap(results.first); } Future<Todo?> findByIdAndUserId(String id, String userId) async { final results = await _db.query( 'SELECT * FROM todos WHERE id = ? AND user_id = ?', [id, userId], ); if (results.isEmpty) return null; return Todo.fromMap(results.first); } Future<Todo> create(Todo todo) async { await _db.execute( ''' INSERT INTO todos (id, user_id, title, description, completed, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?) ''', [ todo.id, todo.userId, todo.title, todo.description, todo.completed ? 1 : 0, todo.createdAt.toIso8601String(), todo.updatedAt.toIso8601String(), ], ); return todo; } Future<Todo> update(Todo todo) async { await _db.execute( ''' UPDATE todos SET title = ?, description = ?, completed = ?, updated_at = ? WHERE id = ? ''', [ todo.title, todo.description, todo.completed ? 1 : 0, DateTime.now().toIso8601String(), todo.id, ], ); return todo; } Future<void> delete(String id) async { await _db.execute('DELETE FROM todos WHERE id = ?', [id]); } }`, 'lib/repositories/token_repository.dart': `import 'package:{{projectName}}/database/database.dart'; import 'package:{{projectName}}/models/token.dart'; class TokenRepository { final Database _db = Database.instance; Future<RefreshToken?> findByToken(String token) async { final results = await _db.query( 'SELECT * FROM refresh_tokens WHERE token = ?', [token], ); if (results.isEmpty) return null; return RefreshToken.fromMap(results.first); } Future<RefreshToken> create(RefreshToken token) async { await _db.execute( ''' INSERT INTO refresh_tokens (id, user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?, ?) ''', [ token.id, token.userId, token.token, token.expiresAt.toIso8601String(), token.createdAt.toIso8601String(), ], ); return token; } Future<void> delete(String token) async { await _db.execute('DELETE FROM refresh_tokens WHERE token = ?', [token]); } Future<void> deleteByUserId(String userId) async { await _db.execute('DELETE FROM refresh_tokens WHERE user_id = ?', [userId]); } Future<void> deleteExpired() async { await _db.execute( 'DELETE FROM refresh_tokens WHERE expires_at < ?', [DateTime.now().toIso8601String()], ); } }`, // Controllers 'lib/controllers/auth_controller.dart': `import 'dart:convert'; import 'package:shelf/shelf.dart'; import 'package:{{projectName}}/models/user.dart'; import 'package:{{projectName}}/models/token.dart'; import 'package:{{projectName}}/repositories/user_repository.dart'; import 'package:{{projectName}}/repositories/token_repository.dart'; import 'package:{{projectName}}/services/auth_service.dart'; import 'package:{{projectName}}/utils/response.dart'; class AuthController { static final _userRepo = UserRepository(); static final _tokenRepo = TokenRepository(); static final _authService = AuthService(); static Future<Response> register(Request request) async { try { final body = await request.readAsString(); final json = jsonDecode(body) as Map<String, dynamic>; final createRequest = CreateUserRequest.fromJson(json); // Validate request final error = createRequest.validate(); if (error != null) { return Response.badRequest(body: jsonResponse({'error': error})); } // Check if user exists final existingUser = await _userRepo.findByEmail(createRequest.email); if (existingUser != null) { return Response(409, body: jsonResponse({'error': 'User already exists'})); } // Create user final user = User( email: createRequest.email, passwordHash: User.hashPassword(createRequest.password), name: createRequest.name, ); await _userRepo.create(user); // Generate tokens final accessToken = _authService.generateAccessToken(user); final refreshToken = RefreshToken(userId: user.id); await _tokenRepo.create(refreshToken); return Response.ok(jsonResponse({ 'user': user.toPublic(), 'accessToken': accessToken, 'refreshToken': refreshToken.token, })); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Registration failed'}), ); } } static Future<Response> login(Request request) async { try { final body = await request.readAsString(); final json = jsonDecode(body) as Map<String, dynamic>; final loginRequest = LoginRequest.fromJson(json); // Find user final user = await _userRepo.findByEmail(loginRequest.email); if (user == null || !user.verifyPassword(loginRequest.password)) { return Response.unauthorized( body: jsonResponse({'error': 'Invalid credentials'}), ); } // Generate tokens final accessToken = _authService.generateAccessToken(user); final refreshToken = RefreshToken(userId: user.id); await _tokenRepo.create(refreshToken); return Response.ok(jsonResponse({ 'user': user.toPublic(), 'accessToken': accessToken, 'refreshToken': refreshToken.token, })); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Login failed'}), ); } } static Future<Response> refresh(Request request) async { try { final body = await request.readAsString(); final json = jsonDecode(body) as Map<String, dynamic>; final refreshTokenValue = json['refreshToken'] as String?; if (refreshTokenValue == null) { return Response.badRequest( body: jsonResponse({'error': 'Refresh token required'}), ); } // Find and validate token final token = await _tokenRepo.findByToken(refreshTokenValue); if (token == null || !token.isValid) { return Response.unauthorized( body: jsonResponse({'error': 'Invalid refresh token'}), ); } // Get user final user = await _userRepo.findById(token.userId); if (user == null) { return Response.unauthorized( body: jsonResponse({'error': 'User not found'}), ); } // Delete old token await _tokenRepo.delete(refreshTokenValue); // Generate new tokens final accessToken = _authService.generateAccessToken(user); final newRefreshToken = RefreshToken(userId: user.id); await _tokenRepo.create(newRefreshToken); return Response.ok(jsonResponse({ 'accessToken': accessToken, 'refreshToken': newRefreshToken.token, })); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Token refresh failed'}), ); } } }`, 'lib/controllers/user_controller.dart': `import 'dart:convert'; import 'package:shelf/shelf.dart'; import 'package:{{projectName}}/models/user.dart'; import 'package:{{projectName}}/repositories/user_repository.dart'; import 'package:{{projectName}}/utils/response.dart'; class UserController { static final _userRepo = UserRepository(); static Future<Response> list(Request request) async { try { final users = await _userRepo.findAll(); final publicUsers = users.map((u) => u.toPublic()).toList(); return Response.ok(jsonResponse(publicUsers)); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Failed to fetch users'}), ); } } static Future<Response> get(Request request) async { try { final id = request.params['id']; if (id == null) { return Response.badRequest(body: jsonResponse({'error': 'Invalid user ID'})); } final user = await _userRepo.findById(id); if (user == null) { return Response.notFound(body: jsonResponse({'error': 'User not found'})); } return Response.ok(jsonResponse(user.toPublic())); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Failed to fetch user'}), ); } } static Future<Response> update(Request request) async { try { final currentUser = request.context['user'] as User; final id = request.params['id']; if (id == null || id != currentUser.id) { return Response.forbidden(body: jsonResponse({'error': 'Forbidden'})); } final body = await request.readAsString(); final json = jsonDecode(body) as Map<String, dynamic>; final updateRequest = UpdateUserRequest.fromJson(json); // Validate request final error = updateRequest.validate(); if (error != null) { return Response.badRequest(body: jsonResponse({'error': error})); } // Check if email is taken if (updateRequest.email != null && updateRequest.email != currentUser.email) { final existingUser = await _userRepo.findByEmail(updateRequest.email!); if (existingUser != null) { return Response(409, body: jsonResponse({'error': 'Email already taken'})); } } // Update user final updatedUser = User( id: currentUser.id, email: updateRequest.email ?? currentUser.email, passwordHash: currentUser.passwordHash, name: updateRequest.name ?? currentUser.name, createdAt: currentUser.createdAt, updatedAt: DateTime.now(), ); await _userRepo.update(updatedUser); return Response.ok(jsonResponse(updatedUser.toPublic())); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Failed to update user'}), ); } } static Future<Response> delete(Request request) async { try { final currentUser = request.context['user'] as User; final id = request.params['id']; if (id == null || id != currentUser.id) { return Response.forbidden(body: jsonResponse({'error': 'Forbidden'})); } await _userRepo.delete(id); return Response.noContent(); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Failed to delete user'}), ); } } }`, 'lib/controllers/todo_controller.dart': `import 'dart:convert'; import 'package:shelf/shelf.dart'; import 'package:{{projectName}}/models/user.dart'; import 'package:{{projectName}}/models/todo.dart'; import 'package:{{projectName}}/repositories/todo_repository.dart'; import 'package:{{projectName}}/utils/response.dart'; class TodoController { static final _todoRepo = TodoRepository(); static Future<Response> list(Request request) async { try { final user = request.context['user'] as User; final todos = await _todoRepo.findByUserId(user.id); return Response.ok(jsonResponse(todos.map((t) => t.toJson()).toList())); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Failed to fetch todos'}), ); } } static Future<Response> create(Request request) async { try { final user = request.context['user'] as User; final body = await request.readAsString(); final json = jsonDecode(body) as Map<String, dynamic>; final createRequest = CreateTodoRequest.fromJson(json); // Validate request final error = createRequest.validate(); if (error != null) { return Response.badRequest(body: jsonResponse({'error': error})); } // Create todo final todo = Todo( userId: user.id, title: createRequest.title, description: createRequest.description, ); await _todoRepo.create(todo); return Response.ok( jsonResponse(todo.toJson()), headers: {'status': '201'}, ); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Failed to create todo'}), ); } } static Future<Response> get(Request request) async { try { final user = request.context['user'] as User; final id = request.params['id']; if (id == null) { return Response.badRequest(body: jsonResponse({'error': 'Invalid todo ID'})); } final todo = await _todoRepo.findByIdAndUserId(id, user.id); if (todo == null) { return Response.notFound(body: jsonResponse({'error': 'Todo not found'})); } return Response.ok(jsonResponse(todo.toJson())); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Failed to fetch todo'}), ); } } static Future<Response> update(Request request) async { try { final user = request.context['user'] as User; final id = request.params['id']; if (id == null) { return Response.badRequest(body: jsonResponse({'error': 'Invalid todo ID'})); } final todo = await _todoRepo.findByIdAndUserId(id, user.id); if (todo == null) { return Response.notFound(body: jsonResponse({'error': 'Todo not found'})); } final body = await request.readAsString(); final json = jsonDecode(body) as Map<String, dynamic>; final updateRequest = UpdateTodoRequest.fromJson(json); // Validate request final error = updateRequest.validate(); if (error != null) { return Response.badRequest(body: jsonResponse({'error': error})); } // Update todo final updatedTodo = Todo( id: todo.id, userId: todo.userId, title: updateRequest.title ?? todo.title, description: updateRequest.description ?? todo.description, completed: updateRequest.completed ?? todo.completed, createdAt: todo.createdAt, updatedAt: DateTime.now(), ); await _todoRepo.update(updatedTodo); return Response.ok(jsonResponse(updatedTodo.toJson())); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Failed to update todo'}), ); } } static Future<Response> delete(Request request) async { try { final user = request.context['user'] as User; final id = request.params['id']; if (id == null) { return Response.badRequest(body: jsonResponse({'error': 'Invalid todo ID'})); } final todo = await _todoRepo.findByIdAndUserId(id, user.id); if (todo == null) { return Response.notFound(body: jsonResponse({'error': 'Todo not found'})); } await _todoRepo.delete(id); return Response.noContent(); } catch (e) { return Response.internalServerError( body: jsonResponse({'error': 'Failed to delete todo'}), ); } } }`, // Services 'lib/services/auth_service.dart': `import 'package:jaguar_jwt/jaguar_jwt.dart'; import 'package:{{projectName}}/config/config.dart'; import 'package:{{projectName}}/models/user.dart'; class AuthService { static const _issuer = '{{projectName}}'; String generateAccessToken(User user) { final claimSet = JwtClaim( issuer: _issuer, subject: user.id, otherClaims: { 'email': user.email, 'name': user.name, }, maxAge: Duration(minutes: Config.jwtExpiryMinutes), ); return issueJwtHS256(claimSet, Config.jwtSecret); } Map<String, dynamic>? verifyToken(String token) { try { final claimSet = verifyJwtHS256Signature(token, Config.jwtSecret); // Validate claims claimSet.validate(issuer: _issuer); return claimSet.toJson(); } catch (e) { return null; } } String? getUserIdFromToken(String token) { final claims = verifyToken(token); return claims?['sub'] as String?; } }`, // Middleware 'lib/middleware/auth_middleware.dart': `import 'package:shelf/shelf.dart'; import 'package:{{projectName}}/services/auth_service.dart'; import 'package:{{projectName}}/repositories/user_repository.dart'; import 'package:{{projectName}}/utils/response.dart'; Middleware authMiddleware() { final authService = AuthService(); final userRepo = UserRepository(); return (Handler innerHandler) { return (Request request) async { // Extract token from Authorization header final authHeader = request.headers['authorization']; if (authHeader == null || !authHeader.startsWith('Bearer ')) { return Response.unauthorized( body: jsonResponse({'error': 'Authentication required'}), ); } final token = authHeader.substring(7); // Remove 'Bearer ' prefix // Verify token final userId = authService.getUserIdFromToken(token); if (userId == null) { return Response.unauthorized( body: jsonResponse({'error': 'Invalid token'}), ); } // Get user final user = await userRepo.findById(userId); if (user == null) { return Response.unauthorized( body: jsonResponse({'error': 'User not found'}), ); } // Add user to request context final updatedRequest = request.change(context: { ...request.context, 'user': user, }); return innerHandler(updatedRequest); }; }; }`, 'lib/middleware/error_middleware.dart': `import 'package:shelf/shelf.dart'; import 'package:{{projectName}}/utils/logger.dart'; import 'package:{{projectName}}/utils/response.dart'; Middleware errorMiddleware() { final logger = AppLogger(); return (Handler innerHandler) { return (Request request) async { try { return await innerHandler(request); } catch (e, stackTrace) { logger.error('Unhandled error: $e', error: e, stackTrace: stackTrace); return Response.internalServerError( body: jsonResponse({ 'error': 'Internal server error', 'message': e.toString(), }), ); } }; }; }`, 'lib/middleware/logging_middleware.dart': `import 'package:shelf/shelf.dart'; import 'package:{{projectName}}/utils/logger.dart'; Middleware loggingMiddleware() { final logger = AppLogger(); return (Handler innerHandler) { return (Request request) async { final watch = Stopwatch()..start(); try { final response = await innerHandler(request); logger.info( '\${request.method} \${request.url.path} ' '\${response.statusCode} \${watch.elapsedMilliseconds}ms', ); return response; } catch (e) { logger.error( '\${request.method} \${request.url.path} ' 'ERROR \${watch.elapsedMilliseconds}ms', error: e, ); rethrow; } }; }; }`, 'lib/middleware/validation_middleware.dart': `import 'package:shelf/shelf.dart'; import 'package:{{projectName}}/utils/response.dart'; Middleware validationMiddleware() { return (Handler innerHandler) { return (Request request) async { // Ensure content-type is JSON for POST/PUT requests if (request.method == 'POST' || request.method == 'PUT') { final contentType = request.headers['content-type']; if (contentType == null || !contentType.contains('application/json')) { return Response.badRequest( body: jsonResponse({ 'error': 'Content-Type must be application/json', }), ); } } return innerHandler(request); }; }; }`, // Utilities 'lib/utils/response.dart': `import 'dart:convert'; String jsonResponse(Object? data) { return jsonEncode(data); } Map<String, String> jsonHeaders() { return {'content-type': 'application/json'}; }`, 'lib/utils/logger.dart': `import 'package:logger/logger.dart'; class AppLogger { static final _instance = AppLogger._internal(); late final Logger _logger; factory AppLogger() { return _instance; } AppLogger._internal() { _logger = Logger( printer: PrettyPrinter( methodCount: 2, errorMethodCount: 8, lineLength: 120, colors: true, printEmojis: true, printTime: true, ), ); } void debug(String message) => _logger.d(message); void info(String message) => _logger.i(message); void warning(String message) => _logger.w(message); void error(String message, {Object? error, StackTrace? stackTrace}) => _logger.e(message, error: error, stackTrace: stackTrace); }`, // Tests 'test/server_test.dart': `import 'dart:convert'; import 'package:http/http.dart'; import 'package:test/test.dart'; import 'package:test_process/test_process.dart'; void main() { final port = '8080'; final host = 'http://localhost:$port'; group('Server Tests', () { late TestProcess process; setUpAll(() async { process = await TestProcess.start( 'dart', ['run', 'bin/server.dart'], environment: {'PORT': port}, ); await process.stdout.stream .map((bytes) => utf8.decode(bytes)) .firstWhere((line) => line.contains('Server listening')); }); tearDownAll(() async { process.kill(); }); test('Root endpoint returns API info', () async { final response = await get(Uri.parse('$host/')); expect(response.statusCode, equals(200)); final body = jsonDecode(response.body) as Map<String, dynamic>; expect(body['name'], equals('{{projectName}} API')); expect(body['version'], equals('1.0.0')); expect(body['status'], equals('running')); }); test('Health check endpoint', () async { final response = await get(Uri.parse('$host/health')); expect(response.statusCode, equals(200)); final body = jsonDecode(response.body) as Map<String, dynamic>; expect(body['status'], isIn(['healthy', 'unhealthy'])); expect(body['timestamp'], isNotNull); }); test('404 for unknown routes', () async { final response = await get(Uri.parse('$host/unknown')); expect(response.statusCode, equals(404)); }); }); }`, 'test/auth_test.dart': `import 'dart:convert'; import 'package:test/test.dart'; import 'package:{{projectName}}/models/user.dart'; import 'package:{{projectName}}/services/auth_service.dart'; void main() { group('Auth Tests', () { late AuthService authService; setUp(() { authService = AuthService(); }); test('Password hashing', () { final password = 'testpassword123'; final hash1 = User.hashPassword(password); final hash2 = User.hashPassword(password); expect(hash1, equals(hash2)); expect(hash1, isNot(equals(password))); }); test('JWT token generation and verification', () { final user = User( email: 'test@example.com', passwordHash: 'hash', name: 'Test User', ); final token = authService.generateAccessToken(user); expect(token, isNotEmpty); final claims = authService.verifyToken(token); expect(claims, isNotNull); expect(claims!['sub'], equals(user.id)); expect(claims['email'], equals(user.email)); expect(claims['name'], equals(user.name)); }); test('Invalid token verification fails', () { final claims = authService.verifyToken('invalid.token.here'); expect(claims, isNull); }); }); }`, // Configuration files '.env.example': `# Environment ENVIRONMENT=development # Server HOST=0.0.0.0 PORT=8080 # Database DB_TYPE=sqlite DB_HOST=localhost DB_PORT=5432 DB_NAME={{projectName}} DB_USER=postgres DB_PASSWORD= DB_PATH=database.db # Security JWT_SECRET=your-secret-key-here JWT_EXPIRY_MINUTES=15 REFRESH_TOKEN_DAYS=30`, // Docker configuration 'Dockerfile': `# Build stage FROM dart:3.2 AS build WORKDIR