├── docs ├── CNAME ├── assets │ ├── script.js │ ├── favicon.ico │ ├── favicon.png │ ├── logo.svg │ └── style.css ├── robot.txt ├── security │ ├── encryption.md │ └── hashing.md ├── digging-deeper │ ├── isolate.md │ ├── env.md │ ├── deployment.md │ ├── testing.md │ └── services.md ├── others │ ├── contribute.md │ └── releases │ │ └── query-builder-release.md ├── the-basic │ └── installation.md └── database │ ├── migration.md │ └── model │ └── serializer.md ├── packages ├── dox-auth │ ├── LICENSE │ ├── README.md │ ├── CHANGELOG.md │ ├── .gitignore │ ├── lib │ │ ├── dox_auth.dart │ │ └── src │ │ │ ├── auth_exception.dart │ │ │ ├── auth_engine.dart │ │ │ ├── hash.dart │ │ │ ├── auth_middleware.dart │ │ │ ├── interfaces.dart │ │ │ └── auth.dart │ ├── pubspec.yaml │ ├── test │ │ └── hash_test.dart │ ├── melos_dox_auth.iml │ ├── example │ │ └── example.md │ └── analysis_options.yaml ├── dox-cli │ ├── LICENSE │ ├── README.md │ ├── lib │ │ ├── src │ │ │ ├── version.dart │ │ │ ├── tools │ │ │ │ ├── update_dox.dart │ │ │ │ ├── create_model.dart │ │ │ │ ├── create_request.dart │ │ │ │ ├── create_serializer.dart │ │ │ │ ├── create_middleware.dart │ │ │ │ ├── generate_key.dart │ │ │ │ └── create_migration.dart │ │ │ └── types.dart │ │ └── dox.dart │ ├── Makefile │ ├── .gitignore │ ├── .env.example │ ├── pubspec.yaml │ ├── melos_dox.iml │ ├── CHANGELOG.md │ ├── test │ │ └── dox_test.dart │ ├── analysis_options.yaml │ └── bin │ │ └── dox.dart ├── dox-core │ ├── LICENSE │ ├── README.md │ ├── .env │ ├── lib │ │ ├── interfaces │ │ │ ├── dox_service.dart │ │ │ ├── router.dart │ │ │ └── response_handler_interface.dart │ │ ├── utils │ │ │ ├── utils.dart │ │ │ ├── extensions │ │ │ │ └── num.dart │ │ │ ├── json.dart │ │ │ ├── hash.dart │ │ │ ├── aes_encryptor.dart │ │ │ └── extensions.dart │ │ ├── constants │ │ │ ├── http_request_method.dart │ │ │ └── constants.dart │ │ ├── storage │ │ │ └── storage_driver_interface.dart │ │ ├── validation │ │ │ └── validation_item.dart │ │ ├── http │ │ │ ├── http_error_handler.dart │ │ │ ├── response │ │ │ │ ├── serializer.dart │ │ │ │ └── dox_cookie.dart │ │ │ ├── http_websocket_handler.dart │ │ │ ├── request │ │ │ │ └── http_request_body.dart │ │ │ └── http_cors_handler.dart │ │ ├── exception │ │ │ ├── not_found_exception.dart │ │ │ ├── query_exception.dart │ │ │ ├── internal_error_exception.dart │ │ │ └── validation_exception.dart │ │ ├── isolate │ │ │ ├── isolate_interfaces.dart │ │ │ ├── isolate_handler.dart │ │ │ └── dox_isolate.dart │ │ ├── cache │ │ │ └── cache_driver_interface.dart │ │ ├── router │ │ │ └── route_data.dart │ │ ├── middleware │ │ │ └── log_middleware.dart │ │ ├── dox_core.dart │ │ └── server │ │ │ └── dox_server.dart │ ├── test │ │ ├── integration │ │ │ ├── storage │ │ │ │ └── dox.png │ │ │ ├── requirements │ │ │ │ ├── handler.dart │ │ │ │ ├── controllers │ │ │ │ │ ├── example.controller.dart │ │ │ │ │ ├── blog.controller.dart │ │ │ │ │ └── admin.controller.dart │ │ │ │ ├── middleware │ │ │ │ │ └── custom_middleware.dart │ │ │ │ ├── serializers │ │ │ │ │ ├── blog_serializer.dart │ │ │ │ │ └── serializer_test.dart │ │ │ │ ├── config │ │ │ │ │ ├── api_router.dart │ │ │ │ │ └── app.dart │ │ │ │ └── requests │ │ │ │ │ └── blog_request.dart │ │ │ ├── isolate_test.dart │ │ │ └── date_response_test.dart │ │ ├── utils │ │ │ ├── start_http_server.dart │ │ │ └── file_upload.dart │ │ └── unit │ │ │ ├── exception │ │ │ ├── query_exception_test.dart │ │ │ ├── not_found_exception_test.dart │ │ │ └── internal_error_exception_test.dart │ │ │ ├── env_test.dart │ │ │ ├── hash_test.dart │ │ │ ├── dox_cookie_test.dart │ │ │ ├── aes_encryptor_test.dart │ │ │ ├── json_test.dart │ │ │ └── utils_test.dart │ ├── .gitignore │ ├── Makefile │ ├── pubspec.yaml │ ├── melos_dox_core.iml │ └── analysis_options.yaml ├── dox-builder │ ├── LICENSE │ ├── README.md │ ├── example │ │ └── dox_model_builder_example.dart │ ├── test │ │ └── dox_model_builder_test.dart │ ├── .gitignore │ ├── lib │ │ ├── dox_builder.dart │ │ └── src │ │ │ └── util.dart │ ├── pubspec.yaml │ ├── build.yaml │ ├── CHANGELOG.md │ ├── melos_dox_builder.iml │ └── analysis_options.yaml ├── dox-migration │ ├── LICENSE │ ├── README.md │ ├── example │ │ └── dox_migration_example.dart │ ├── lib │ │ ├── dox_migration.dart │ │ └── src │ │ │ └── utils │ │ │ ├── file_extension.dart │ │ │ ├── string_extension.dart │ │ │ ├── logger.dart │ │ │ └── env.dart │ ├── test │ │ └── dox_migration_test.dart │ ├── .gitignore │ ├── CHANGELOG.md │ ├── pubspec.yaml │ ├── melos_dox_migration.iml │ └── analysis_options.yaml ├── dox-websocket │ ├── LICENSE │ ├── README.md │ ├── example │ │ └── dox_websocket_example.dart │ ├── CHANGELOG.md │ ├── lib │ │ ├── src │ │ │ ├── adapters │ │ │ │ ├── websocket_adapter.dart │ │ │ │ └── websocket_redis_adapter.dart │ │ │ ├── websocket_info.dart │ │ │ ├── utils │ │ │ │ ├── constant.dart │ │ │ │ └── json.dart │ │ │ ├── websocket.dart │ │ │ └── websocket_emit_event.dart │ │ └── dox_websocket.dart │ ├── .gitignore │ ├── pubspec.yaml │ ├── test │ │ └── config │ │ │ ├── router.dart │ │ │ └── app_config.dart │ ├── melos_dox_websocket.iml │ └── analysis_options.yaml ├── dox-annotation │ ├── LICENSE │ ├── README.md │ ├── CHANGELOG.md │ ├── lib │ │ ├── dox_annotation.dart │ │ └── src │ │ │ ├── auth_annotation.dart │ │ │ ├── websocket_annotation.dart │ │ │ └── dox_model.dart │ ├── test │ │ └── dox_annotation_test.dart │ ├── .gitignore │ ├── pubspec.yaml │ ├── melos_dox_annotation.iml │ └── analysis_options.yaml ├── dox-query-builder │ ├── LICENSE │ ├── README.md │ ├── lib │ │ ├── src │ │ │ ├── printers │ │ │ │ ├── printer.dart │ │ │ │ ├── console_printer.dart │ │ │ │ └── file_printer.dart │ │ │ ├── soft_deletes.dart │ │ │ ├── utils │ │ │ │ ├── functions.dart │ │ │ │ ├── logger.dart │ │ │ │ └── annotation.dart │ │ │ ├── schema │ │ │ │ ├── table.shared_mixin.dart │ │ │ │ └── table.column.dart │ │ │ ├── truncate.dart │ │ │ ├── shared_mixin.dart │ │ │ ├── count.dart │ │ │ ├── group_by.dart │ │ │ ├── order_by.dart │ │ │ ├── raw.dart │ │ │ ├── update.dart │ │ │ ├── types │ │ │ │ ├── database_config.dart │ │ │ │ └── pagination_result.dart │ │ │ ├── select.dart │ │ │ ├── limit.dart │ │ │ ├── drivers │ │ │ │ └── db_driver.dart │ │ │ ├── delete.dart │ │ │ ├── relationships │ │ │ │ ├── has_one.dart │ │ │ │ ├── has_many.dart │ │ │ │ └── belongs_to.dart │ │ │ └── schema.dart │ │ └── dox_query_builder.dart │ ├── .gitignore │ ├── test │ │ ├── models │ │ │ ├── artist_song │ │ │ │ └── artist_song.model.dart │ │ │ ├── user │ │ │ │ └── user.model.dart │ │ │ ├── song │ │ │ │ └── song.model.dart │ │ │ ├── blog_info │ │ │ │ └── blog_info.model.dart │ │ │ ├── artist │ │ │ │ └── artist.model.dart │ │ │ └── blog │ │ │ │ └── blog.model.dart │ │ ├── mysql.dart │ │ ├── postgres.dart │ │ ├── model_custom_table_name_test.dart │ │ └── connection.dart │ ├── Makefile │ ├── pubspec.yaml │ ├── melos_dox_query_builder.iml │ ├── analysis_options.yaml │ ├── CHANGELOG.md │ └── example │ │ └── example.md └── dox-app │ ├── .dockerignore │ ├── lib │ ├── app │ │ ├── http │ │ │ ├── controllers │ │ │ │ ├── api.controller.dart │ │ │ │ ├── web.controller.dart │ │ │ │ ├── blog.controller.dart │ │ │ │ └── auth.controller.dart │ │ │ ├── middleware │ │ │ │ ├── function.middleware.dart │ │ │ │ └── custom.middleware.dart │ │ │ ├── handler.dart │ │ │ ├── serializers │ │ │ │ └── user.serializer.dart │ │ │ ├── requests │ │ │ │ └── blog.request.dart │ │ │ └── services │ │ │ │ └── blog.service.dart │ │ ├── ws │ │ │ └── controllers │ │ │ │ └── ws_controller.dart │ │ └── models │ │ │ ├── user │ │ │ └── user.model.dart │ │ │ ├── category │ │ │ └── category.model.dart │ │ │ └── blog │ │ │ └── blog.model.dart │ ├── config │ │ ├── cache.dart │ │ ├── storage.dart │ │ ├── services.dart │ │ ├── redis.dart │ │ ├── logger.dart │ │ └── database.dart │ ├── services │ │ ├── websocket_service.dart │ │ ├── orm_service.dart │ │ └── auth_service.dart │ ├── utils │ │ ├── log_filter.dart │ │ └── extensions.dart │ └── routes │ │ ├── websocket.dart │ │ ├── api.dart │ │ └── web.dart │ ├── db │ └── migration │ │ ├── 2023_07_16_131922862_create_blog_category.sql │ │ ├── 2023_07_16_131933100_create_category_table.sql │ │ ├── 2023_07_16_131937700_create_user_table.sql │ │ └── 2023_07_16_131915229_create_blog_table.sql │ ├── .env.example │ ├── README.md │ ├── pubspec.yaml │ ├── bin │ └── server.dart │ ├── test │ └── http_test.dart │ ├── Dockerfile │ ├── melos_dox_app.iml │ ├── docker-compose.yml │ └── analysis_options.yaml ├── pubspec.yaml ├── .gitignore ├── melos.yaml ├── melos_dox_framework.iml ├── .github ├── FUNDING.yml └── workflows │ ├── publish.yaml │ └── docs.yaml └── LICENSE /docs/CNAME: -------------------------------------------------------------------------------- 1 | dartondox.dev -------------------------------------------------------------------------------- /docs/assets/script.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/dox-auth/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/dox-cli/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/dox-core/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/dox-auth/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /packages/dox-builder/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/dox-cli/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /packages/dox-core/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /packages/dox-migration/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/dox-websocket/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/dox-annotation/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/dox-annotation/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /packages/dox-builder/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /packages/dox-migration/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /packages/dox-query-builder/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/dox-websocket/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /packages/dox-query-builder/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /docs/robot.txt: -------------------------------------------------------------------------------- 1 | # Algolia-Crawler-Verif: 7556FC76DBDC63BA 2 | -------------------------------------------------------------------------------- /packages/dox-websocket/example/dox_websocket_example.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/dox-builder/example/dox_model_builder_example.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/dox-cli/lib/src/version.dart: -------------------------------------------------------------------------------- 1 | const version = '3.0.0'; 2 | -------------------------------------------------------------------------------- /packages/dox-core/.env: -------------------------------------------------------------------------------- 1 | APP_KEY=4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt 2 | -------------------------------------------------------------------------------- /packages/dox-migration/example/dox_migration_example.dart: -------------------------------------------------------------------------------- 1 | void main() {} 2 | -------------------------------------------------------------------------------- /packages/dox-websocket/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version. 4 | -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dartondox/dox/HEAD/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dartondox/dox/HEAD/docs/assets/favicon.png -------------------------------------------------------------------------------- /packages/dox-cli/lib/dox.dart: -------------------------------------------------------------------------------- 1 | export 'src/tools/create_model.dart'; 2 | export 'src/utils/utils.dart'; 3 | -------------------------------------------------------------------------------- /packages/dox-cli/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | dart compile exe bin/dox.dart -o bin/dox 3 | 4 | publish: 5 | dart pub publish -------------------------------------------------------------------------------- /packages/dox-auth/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.0.0 2 | 3 | - Compatible for core version 2.0. 4 | 5 | ## 1.0.1 6 | 7 | - Initial version. 8 | -------------------------------------------------------------------------------- /packages/dox-core/lib/interfaces/dox_service.dart: -------------------------------------------------------------------------------- 1 | /// coverage:ignore-file 2 | abstract class DoxService { 3 | dynamic setup() {} 4 | } 5 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/printers/printer.dart: -------------------------------------------------------------------------------- 1 | abstract class QueryPrinter { 2 | void log(String query, List params); 3 | } 4 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/storage/dox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dartondox/dox/HEAD/packages/dox-core/test/integration/storage/dox.png -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox_framework 2 | publish_to: 'none' 3 | version: 3.0.0 4 | 5 | environment: 6 | sdk: '>=3.0.0 <4.0.0' 7 | dev_dependencies: 8 | melos: ^3.2.0 9 | -------------------------------------------------------------------------------- /packages/dox-app/.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | Dockerfile 3 | build/ 4 | .dart_tool/ 5 | .git/ 6 | .github/ 7 | .gitignore 8 | .idea/ 9 | packages 10 | .docker/build -------------------------------------------------------------------------------- /packages/dox-migration/lib/dox_migration.dart: -------------------------------------------------------------------------------- 1 | library dox_migration; 2 | 3 | export 'src/create_migration/create_migration.dart'; 4 | export 'src/dox_migration_base.dart'; 5 | -------------------------------------------------------------------------------- /packages/dox-cli/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | lib/models 5 | db 6 | .env 7 | pubspec.lock 8 | bin/dox -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/soft_deletes.dart: -------------------------------------------------------------------------------- 1 | import 'shared_mixin.dart'; 2 | 3 | mixin SoftDeletes implements SharedMixin { 4 | @override 5 | bool isSoftDeletes = true; 6 | } 7 | -------------------------------------------------------------------------------- /packages/dox-core/lib/utils/utils.dart: -------------------------------------------------------------------------------- 1 | String sanitizeRoutePath(String path) { 2 | path = path.replaceAll(RegExp(r'/+'), '/'); 3 | return "/${path.replaceAll(RegExp('^\\/+|\\/+\$'), '')}"; 4 | } 5 | -------------------------------------------------------------------------------- /packages/dox-annotation/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.5 2 | 3 | - Add IDoxMiddleware interface 4 | 5 | ## 1.0.5-alpha1.9 6 | 7 | - Remove auth annotation 8 | 9 | ## 1.0.4 10 | 11 | - Initial version. 12 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/controllers/api.controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class ApiController { 4 | String pong(DoxRequest req) { 5 | return 'pong'; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/dox-core/lib/interfaces/router.dart: -------------------------------------------------------------------------------- 1 | /// coverage:ignore-file 2 | abstract class Router { 3 | String get prefix => ''; 4 | List get middleware => []; 5 | void register(); 6 | } 7 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/middleware/function.middleware.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | DoxRequest functionMiddleware(DoxRequest req) { 4 | /// write your logic here 5 | return req; 6 | } 7 | -------------------------------------------------------------------------------- /packages/dox-websocket/lib/src/adapters/websocket_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_websocket/src/websocket_emit_event.dart'; 2 | 3 | abstract class WebsocketAdapterInterface { 4 | emit(WebsocketEmitEvent event); 5 | } 6 | -------------------------------------------------------------------------------- /packages/dox-core/test/utils/start_http_server.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | Future startHttpServer(AppConfig config) async { 4 | Dox().initialize(config); 5 | await Dox().startServer(); 6 | } 7 | -------------------------------------------------------------------------------- /packages/dox-annotation/lib/dox_annotation.dart: -------------------------------------------------------------------------------- 1 | library dox_annotation; 2 | 3 | export 'src/auth_annotation.dart'; 4 | export 'src/core_annotation.dart'; 5 | export 'src/dox_model.dart'; 6 | export 'src/websocket_annotation.dart'; 7 | -------------------------------------------------------------------------------- /packages/dox-core/lib/utils/extensions/num.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | extension NumberExtension on num { 4 | num toFixed(int decimal) { 5 | num mod = pow(10.0, decimal); 6 | return (this * mod).round() / mod; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/handler.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class ResponseHandler extends ResponseHandlerInterface { 4 | @override 5 | DoxResponse handle(DoxResponse res) { 6 | return res; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/dox-migration/test/dox_migration_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | void main() { 4 | group('A group of tests', () { 5 | test('First test', () { 6 | expect(true, true); 7 | }); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /packages/dox-annotation/test/dox_annotation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | void main() { 4 | group('A group of tests', () { 5 | test('First Test', () { 6 | expect(true, true); 7 | }); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /packages/dox-builder/test/dox_model_builder_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | void main() { 4 | group('A group of tests', () { 5 | test('First Test', () { 6 | expect(true, true); 7 | }); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /packages/dox-core/lib/interfaces/response_handler_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | abstract class ResponseHandlerInterface { 4 | const ResponseHandlerInterface(); 5 | 6 | DoxResponse handle(DoxResponse res); 7 | } 8 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/ws/controllers/ws_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_websocket/dox_websocket.dart'; 2 | 3 | class BlogController { 4 | void index(WebsocketEmitter emitter, dynamic message) async { 5 | /// write your logic here 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/handler.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class ResponseHandler extends ResponseHandlerInterface { 4 | @override 5 | DoxResponse handle(DoxResponse res) { 6 | return res; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/dox-auth/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | 5 | # Avoid committing pubspec.lock for library packages; see 6 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 7 | pubspec.lock 8 | -------------------------------------------------------------------------------- /packages/dox-annotation/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | 5 | # Avoid committing pubspec.lock for library packages; see 6 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 7 | pubspec.lock 8 | -------------------------------------------------------------------------------- /packages/dox-annotation/lib/src/auth_annotation.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_annotation/dox_annotation.dart'; 2 | 3 | abstract class IAuth { 4 | Future verifyToken(IDoxRequest req); 5 | bool isLoggedIn(); 6 | T? user(); 7 | Map? toJson(); 8 | } 9 | -------------------------------------------------------------------------------- /packages/dox-builder/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | 5 | # Avoid committing pubspec.lock for library packages; see 6 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 7 | pubspec.lock 8 | -------------------------------------------------------------------------------- /packages/dox-migration/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | 5 | # Avoid committing pubspec.lock for library packages; see 6 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 7 | pubspec.lock 8 | -------------------------------------------------------------------------------- /packages/dox-websocket/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | 5 | # Avoid committing pubspec.lock for library packages; see 6 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 7 | pubspec.lock 8 | main.dart -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | .DS_Store 5 | bin/server 6 | .docker 7 | packages/dox-app/storage 8 | pubspec_overrides.yaml 9 | .idea 10 | site 11 | .env 12 | pubspec.yaml.backup 13 | packages/dox-app/bin/dox 14 | -------------------------------------------------------------------------------- /packages/dox-app/db/migration/2023_07_16_131922862_create_blog_category.sql: -------------------------------------------------------------------------------- 1 | -- up 2 | CREATE TABLE IF NOT EXISTS blog_category ( 3 | id serial PRIMARY KEY, 4 | blog_id BIGINT NOT NULL, 5 | category_id BIGINT NOT NULL 6 | ) 7 | 8 | -- down 9 | DROP TABLE IF EXISTS blog_category 10 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/middleware/custom.middleware.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class CustomMiddleware extends IDoxMiddleware { 4 | @override 5 | IDoxRequest handle(IDoxRequest req) { 6 | /// write your logic here 7 | return req; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/dox-query-builder/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | 5 | # Avoid committing pubspec.lock for library packages; see 6 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 7 | pubspec.lock 8 | 9 | coverage -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/utils/functions.dart: -------------------------------------------------------------------------------- 1 | DateTime now() { 2 | return DateTime.now().toUtc(); 3 | } 4 | 5 | dynamic toMap(dynamic data) { 6 | if (data is List) { 7 | return data.map((dynamic e) => e.toJson()).toList(); 8 | } else { 9 | return data?.toJson(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/dox-core/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | .DS_Store 5 | 6 | # Avoid committing pubspec.lock for library packages; see 7 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 8 | pubspec.lock 9 | coverage 10 | /storage 11 | -------------------------------------------------------------------------------- /packages/dox-app/.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=dox 2 | APP_ENV=development 3 | APP_PORT=3003 4 | APP_KEY=ZrNbOZJAjC2MDzqehhsGV2DzFFI4pz5R 5 | 6 | DB_CONNECTION=postgres 7 | DB_HOST=localhost 8 | DB_PORT=5432 9 | DB_USERNAME=postgres 10 | DB_PASSWORD=postgres 11 | DB_NAME=postgres 12 | 13 | REDIS_HOST=localhost 14 | REDIS_PORT=6379 15 | -------------------------------------------------------------------------------- /packages/dox-app/README.md: -------------------------------------------------------------------------------- 1 | # Dox Example App 2 | 3 | ## Development 4 | 5 | - dart pub get 6 | - cp .env.example .env 7 | - dox migrate 8 | - dox s 9 | 10 | application will be running on http://127.0.0.1:3001 11 | 12 | ## Production 13 | 14 | - dart pub get 15 | - cp .env.example .env 16 | - dox build 17 | - bin/server 18 | -------------------------------------------------------------------------------- /packages/dox-cli/.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=dox 2 | APP_ENV=development 3 | APP_PORT=3003 4 | APP_KEY=ZrNbOZJAjC2MDzqehhsGV2DzFFI4pz5R 5 | 6 | DB_CONNECTION=postgres 7 | DB_HOST=localhost 8 | DB_PORT=5432 9 | DB_USERNAME=postgres 10 | DB_PASSWORD=postgres 11 | DB_NAME=postgres 12 | 13 | REDIS_HOST=localhost 14 | REDIS_PORT=6379 15 | -------------------------------------------------------------------------------- /packages/dox-core/lib/constants/http_request_method.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: constant_identifier_names 2 | enum HttpRequestMethod { 3 | GET, 4 | POST, 5 | PUT, 6 | PATCH, 7 | DELETE, 8 | PURGE, 9 | OPTIONS, 10 | COPY, 11 | VIEW, 12 | LINK, 13 | UNLINK, 14 | LOCK, 15 | UNLOCK, 16 | PROPFIND, 17 | } 18 | -------------------------------------------------------------------------------- /packages/dox-websocket/lib/dox_websocket.dart: -------------------------------------------------------------------------------- 1 | library dox_websocket; 2 | 3 | export 'src/adapters/websocket_adapter.dart'; 4 | export 'src/adapters/websocket_redis_adapter.dart'; 5 | export 'src/websocket.dart'; 6 | export 'src/websocket_emit_event.dart'; 7 | export 'src/websocket_emitter.dart'; 8 | export 'src/websocket_info.dart'; 9 | -------------------------------------------------------------------------------- /packages/dox-annotation/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox_annotation 2 | description: Dox annotation for dox framework 3 | version: 3.0.0 4 | repository: https://github.com/dartondox/dox 5 | homepage: https://dartondox.dev 6 | 7 | environment: 8 | sdk: '>=3.0.0 <4.0.0' 9 | 10 | dev_dependencies: 11 | lints: ^2.0.0 12 | test: ^1.21.0 13 | -------------------------------------------------------------------------------- /packages/dox-app/db/migration/2023_07_16_131933100_create_category_table.sql: -------------------------------------------------------------------------------- 1 | -- up 2 | CREATE TABLE IF NOT EXISTS category ( 3 | id serial PRIMARY KEY, 4 | name VARCHAR ( 255 ) UNIQUE NOT NULL, 5 | deleted_at TIMESTAMP, 6 | created_at TIMESTAMP, 7 | updated_at TIMESTAMP 8 | ) 9 | 10 | 11 | -- down 12 | DROP TABLE IF EXISTS category -------------------------------------------------------------------------------- /packages/dox-core/lib/storage/storage_driver_interface.dart: -------------------------------------------------------------------------------- 1 | abstract class StorageDriverInterface { 2 | Future put(String filePath, List bytes, {String? extension}); 3 | 4 | Future?> get(String filepath); 5 | 6 | Future exists(String filepath); 7 | 8 | Future delete(String filepath); 9 | } 10 | -------------------------------------------------------------------------------- /packages/dox-core/lib/validation/validation_item.dart: -------------------------------------------------------------------------------- 1 | class ValidationItem { 2 | final String field; 3 | final String name; 4 | final String rule; 5 | final dynamic value; 6 | 7 | const ValidationItem({ 8 | required this.field, 9 | required this.name, 10 | required this.rule, 11 | this.value, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /docs/security/encryption.md: -------------------------------------------------------------------------------- 1 | # Encryption 2 | 3 | ## Encode 4 | 5 | ```dart 6 | String encodedMessage = AESEncryptor.encode('Hello world', 'your-secret'); 7 | print(encodedMessage); 8 | ``` 9 | 10 | ## Decode 11 | 12 | ```dart 13 | String decodedMessage = AESEncryptor.decode(encodedMessage, 'your-secret'); 14 | print(decodedMessage); 15 | ``` -------------------------------------------------------------------------------- /packages/dox-auth/lib/dox_auth.dart: -------------------------------------------------------------------------------- 1 | library dox_auth; 2 | 3 | export 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; 4 | 5 | export 'src/auth.dart'; 6 | export 'src/auth_engine.dart'; 7 | export 'src/auth_exception.dart'; 8 | export 'src/auth_middleware.dart'; 9 | export 'src/drivers/jwt_auth_driver.dart'; 10 | export 'src/interfaces.dart'; 11 | -------------------------------------------------------------------------------- /packages/dox-annotation/lib/src/websocket_annotation.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_annotation/src/core_annotation.dart'; 4 | 5 | abstract class IDoxWebsocket { 6 | dynamic create(); 7 | } 8 | 9 | abstract class WebsocketEvent { 10 | void on(String event, Function controller); 11 | Future handle(IDoxRequest req); 12 | } 13 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/serializers/user.serializer.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | import '../../../app/models/user/user.model.dart'; 4 | 5 | class UserSerializer extends Serializer { 6 | UserSerializer(super.data); 7 | 8 | @override 9 | Map convert(User m) { 10 | return m.toJson(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/dox-builder/lib/dox_builder.dart: -------------------------------------------------------------------------------- 1 | library dox_builder; 2 | 3 | import 'package:build/build.dart'; 4 | import 'package:dox_builder/src/dox_builder.dart'; 5 | import 'package:source_gen/source_gen.dart'; 6 | 7 | Builder buildDoxModel(BuilderOptions options) => SharedPartBuilder( 8 | [DoxModelBuilder()], 9 | 'dox_model_generator', 10 | ); 11 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/controllers/example.controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class ExampleController { 4 | void testException(DoxRequest req) { 5 | throw Exception('something went wrong'); 6 | } 7 | 8 | void httpException(DoxRequest req) { 9 | throw ValidationException(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/middleware/custom_middleware.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | DoxRequest customMiddleware(DoxRequest req) { 4 | return req; 5 | } 6 | 7 | class ClassBasedMiddleware implements IDoxMiddleware { 8 | @override 9 | IDoxRequest handle(IDoxRequest req) { 10 | return req; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/models/artist_song/artist_song.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | part 'artist_song.model.g.dart'; 4 | 5 | @DoxModel() 6 | class ArtistSong extends ArtistSongGenerator { 7 | @Column(name: 'blog_id') 8 | int? songId; 9 | 10 | @Column(name: 'artist_id') 11 | int? artistId; 12 | } 13 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/models/user/user.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | part 'user.model.g.dart'; 4 | 5 | @DoxModel( 6 | table: 'users', 7 | createdAt: 'created_at', 8 | updatedAt: 'updated_at', 9 | softDelete: true, 10 | ) 11 | class User extends UserGenerator { 12 | @Column() 13 | String? name; 14 | } 15 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/models/song/song.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | import '../artist/artist.model.dart'; 4 | 5 | part 'song.model.g.dart'; 6 | 7 | @DoxModel() 8 | class Song extends SongGenerator { 9 | @Column() 10 | String? title; 11 | 12 | @ManyToMany(Artist) 13 | List artists = []; 14 | } 15 | -------------------------------------------------------------------------------- /packages/dox-websocket/lib/src/websocket_info.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class WebsocketInfo { 4 | final String socketId; 5 | final WebSocket websocket; 6 | String? activeRoom; 7 | String? previousRoom; 8 | 9 | WebsocketInfo({ 10 | required this.socketId, 11 | required this.websocket, 12 | this.activeRoom, 13 | this.previousRoom, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /packages/dox-websocket/lib/src/utils/constant.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: constant_identifier_names 2 | 3 | const String WEB_SOCKET_JOIN_ROOM_EVENT_NAME = 'joinRoom'; 4 | 5 | const String WEB_SOCKET_EVENT_KEY = 'event'; 6 | 7 | const String WEB_SOCKET_MESSAGE_KEY = 'message'; 8 | 9 | const String WEB_SOCKET_SENDER_KEY = 'sender'; 10 | 11 | const String WEB_SOCKET_ROOM_KEY = 'room'; 12 | -------------------------------------------------------------------------------- /packages/dox-core/Makefile: -------------------------------------------------------------------------------- 1 | coverage: 2 | dart run test --concurrency=1 --coverage=./coverage 3 | dart pub global run coverage:format_coverage --check-ignore --packages=.dart_tool/package_config.json --report-on=lib --lcov -o ./coverage/lcov.info -i ./coverage 4 | genhtml -o ./coverage/report ./coverage/lcov.info 5 | open ./coverage/report/index.html 6 | 7 | test: 8 | dart run test --concurrency=1 9 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/controllers/web.controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/config/redis.dart'; 2 | import 'package:dox_core/dox_core.dart'; 3 | 4 | class WebController { 5 | String pong(DoxRequest req) { 6 | return 'pong'; 7 | } 8 | 9 | dynamic testRedis(DoxRequest req) async { 10 | await redis.set('dox', 'awesome'); 11 | return await redis.get('dox'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/dox-core/test/unit/exception/query_exception_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('query_exception', () { 6 | QueryException exception = QueryException(); 7 | 8 | dynamic res = exception.toResponse(); 9 | expect(res, 'Error in sql query'); 10 | expect(exception.code, 500); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /packages/dox-websocket/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox_websocket 2 | description: Websocket library for dox framework. 3 | version: 3.0.0 4 | repository: https://github.com/dartondox/dox 5 | environment: 6 | sdk: ^3.1.0 7 | 8 | dependencies: 9 | uuid: ^3.0.6 10 | dox_annotation: ^1.0.5 11 | ioredis: ^1.0.2 12 | 13 | dev_dependencies: 14 | lints: ^2.0.0 15 | test: ^1.21.0 16 | dox_core: ^2.0.0 17 | -------------------------------------------------------------------------------- /docs/security/hashing.md: -------------------------------------------------------------------------------- 1 | # Hashing 2 | 3 | The Dox Hash class provides the capability to utilize bcrypt for hashing values. 4 | 5 | ## Hash a password 6 | 7 | ```dart 8 | String secret = 'password'; 9 | String hashedPassword = Hash.make(secret); 10 | ``` 11 | 12 | ## Verify hashed password 13 | 14 | ```dart 15 | String secret = 'password'; 16 | bool verified = Hash.verify(secret, hashedPassword); 17 | ``` -------------------------------------------------------------------------------- /packages/dox-app/db/migration/2023_07_16_131937700_create_user_table.sql: -------------------------------------------------------------------------------- 1 | -- up 2 | CREATE TABLE IF NOT EXISTS users ( 3 | id serial PRIMARY KEY, 4 | name VARCHAR ( 255 ) NOT NULL, 5 | email VARCHAR ( 255 ) UNIQUE NOT NULL, 6 | password VARCHAR ( 255 ) NOT NULL, 7 | deleted_at TIMESTAMP, 8 | created_at TIMESTAMP, 9 | updated_at TIMESTAMP 10 | ) 11 | 12 | -- down 13 | DROP TABLE IF EXISTS users 14 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/schema/table.shared_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | import '../utils/logger.dart'; 4 | import 'table.column.dart'; 5 | 6 | abstract class TableSharedMixin { 7 | final List columns = []; 8 | String tableName = ''; 9 | bool debug = false; 10 | DBDriver get dbDriver; 11 | Logger get logger; 12 | } 13 | -------------------------------------------------------------------------------- /docs/digging-deeper/isolate.md: -------------------------------------------------------------------------------- 1 | # Isolate 2 | 3 | Dox support multi-threaded HTTP server using isolates that can handle 10x concurrency requests with high speed. 4 | 5 | By default, Dox runs on three isolates. You can configure this setting in the `lib/config/app.dart` file. Or simply add like `APP_TOTAL_ISOLATE=6` in environment variable. 6 | 7 | 8 | ```dart 9 | totalIsolate: Env.get('APP_TOTAL_ISOLATE', 6), 10 | ``` -------------------------------------------------------------------------------- /packages/dox-annotation/lib/src/dox_model.dart: -------------------------------------------------------------------------------- 1 | class DoxModel { 2 | final String? table; 3 | final String primaryKey; 4 | final String createdAt; 5 | final String updatedAt; 6 | final bool softDelete; 7 | 8 | const DoxModel({ 9 | this.table, 10 | this.primaryKey = 'id', 11 | this.createdAt = 'created_at', 12 | this.updatedAt = 'updated_at', 13 | this.softDelete = false, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /packages/dox-app/db/migration/2023_07_16_131915229_create_blog_table.sql: -------------------------------------------------------------------------------- 1 | -- up 2 | CREATE TABLE IF NOT EXISTS blog ( 3 | id serial PRIMARY KEY, 4 | user_id int NOT NULL, 5 | title VARCHAR ( 255 ) NOT NULL, 6 | slug VARCHAR ( 255 ) NOT NULL, 7 | description TEXT, 8 | deleted_at TIMESTAMP, 9 | created_at TIMESTAMP, 10 | updated_at TIMESTAMP 11 | ) 12 | 13 | -- down 14 | DROP TABLE IF EXISTS blog 15 | 16 | -------------------------------------------------------------------------------- /packages/dox-app/lib/config/cache.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/cache/drivers/file/file_cache_driver.dart'; 2 | import 'package:dox_core/dox_core.dart'; 3 | 4 | CacheConfig cache = CacheConfig( 5 | /// default cache driver 6 | defaultDriver: Env.get('CACHE_DRIVER', 'file'), 7 | 8 | /// register cache driver list 9 | drivers: { 10 | 'file': FileCacheDriver(), 11 | }, 12 | ); 13 | -------------------------------------------------------------------------------- /packages/dox-core/lib/http/http_error_handler.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | import 'package:dox_core/http/http_response_handler.dart'; 5 | 6 | void httpErrorHandler(HttpRequest req, Object? error, StackTrace stackTrace) { 7 | if (error is Exception || error is Error) { 8 | Dox().config.errorHandler(error, stackTrace); 9 | } 10 | httpResponseHandler(error, req); 11 | } 12 | -------------------------------------------------------------------------------- /packages/dox-migration/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.0.0 2 | 3 | - Release stable version v2.0 4 | 5 | ## 2.0.0-alpha.2 6 | 7 | - Remove .dart option 8 | 9 | ## 1.0.4 10 | 11 | - Improve loading .env 12 | 13 | ## 1.0.3 14 | 15 | - Bug fixed on sql rollback 16 | 17 | ## 1.0.2 18 | 19 | - Fixed rollback issues 20 | 21 | ## 1.0.1 22 | 23 | - Close connection when running from cli 24 | 25 | ## 1.0.0 26 | 27 | - Initial version. 28 | -------------------------------------------------------------------------------- /packages/dox-app/lib/config/storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:dox_core/storage/local_storage_driver.dart'; 3 | 4 | FileStorageConfig storage = FileStorageConfig( 5 | /// default storage driver 6 | defaultDriver: Env.get('STORAGE_DRIVER', 'local'), 7 | 8 | // register storage driver list 9 | drivers: { 10 | 'local': LocalStorageDriver(), 11 | }, 12 | ); 13 | -------------------------------------------------------------------------------- /packages/dox-migration/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox_migration 2 | description: Database migration package for Postgres SQL 3 | version: 3.0.0 4 | repository: https://github.com/dartondox/dox 5 | 6 | environment: 7 | sdk: ^3.0.0 8 | 9 | # Add regular dependencies here. 10 | dependencies: 11 | postgres: ^3.0.4 12 | 13 | dev_dependencies: 14 | lints: ^2.0.0 15 | test: ^1.21.0 16 | 17 | executables: 18 | dox_migration: dox_migration 19 | -------------------------------------------------------------------------------- /packages/dox-builder/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox_builder 2 | description: Dox builder for dox framework 3 | version: 3.0.0 4 | repository: https://github.com/dartondox/dox 5 | homepage: https://dartondox.dev 6 | 7 | environment: 8 | sdk: '>=3.0.0 <4.0.0' 9 | 10 | dependencies: 11 | build: ^2.4.0 12 | source_gen: ^1.3.1 13 | analyzer: ^5.12.0 14 | dox_annotation: ^1.0.5 15 | 16 | dev_dependencies: 17 | lints: ^2.0.0 18 | test: ^1.21.0 19 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/models/user/user.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | part 'user.model.g.dart'; 4 | 5 | @DoxModel(softDelete: true, table: 'users') 6 | class User extends UserGenerator { 7 | @override 8 | List get hidden => ['password']; 9 | 10 | @Column() 11 | String? name; 12 | 13 | @Column() 14 | String? email; 15 | 16 | @Column() 17 | String? password; 18 | } 19 | -------------------------------------------------------------------------------- /packages/dox-auth/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox_auth 2 | description: Authentication package for dox framework with jsonwebtoken(jwt) driver. 3 | version: 3.0.0 4 | repository: https://github.com/dartondox/dox 5 | 6 | environment: 7 | sdk: '>=3.0.0 <4.0.0' 8 | 9 | dependencies: 10 | bcrypt: ^1.1.3 11 | dart_jsonwebtoken: ^2.8.2 12 | dox_annotation: ^1.0.5 13 | dox_query_builder: ^2.0.0 14 | 15 | dev_dependencies: 16 | lints: ^2.0.0 17 | test: ^1.21.0 18 | -------------------------------------------------------------------------------- /packages/dox-core/lib/exception/not_found_exception.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | 5 | class NotFoundHttpException extends IHttpException { 6 | NotFoundHttpException({ 7 | String message = 'Not Found', 8 | String errorCode = 'not_found', 9 | int code = HttpStatus.notFound, 10 | }) { 11 | super.code = code; 12 | super.errorCode = errorCode; 13 | super.message = message; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/serializers/blog_serializer.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class Blog { 4 | String title = 'hello'; 5 | } 6 | 7 | class BlogSerializer extends Serializer { 8 | BlogSerializer(super.data); 9 | 10 | /// convert model into Map 11 | @override 12 | Map convert(Blog m) { 13 | return { 14 | 'title': m.title, 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/dox-core/test/unit/exception/not_found_exception_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('not_found_exception', () { 6 | NotFoundHttpException exception = NotFoundHttpException( 7 | message: '404 Not Found', 8 | ); 9 | 10 | dynamic res = exception.toResponse(); 11 | expect(res, '404 Not Found'); 12 | expect(exception.code, 404); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /packages/dox-query-builder/Makefile: -------------------------------------------------------------------------------- 1 | coverage: 2 | dart run test --coverage=./coverage --concurrency=1 3 | dart pub global run coverage:format_coverage --check-ignore --packages=.dart_tool/package_config.json --report-on=lib --lcov -o ./coverage/lcov.info -i ./coverage 4 | genhtml -o ./coverage/report ./coverage/lcov.info 5 | open ./coverage/report/index.html 6 | 7 | test: 8 | dart run test --concurrency=1 9 | 10 | build: 11 | dart run build_runner build 12 | -------------------------------------------------------------------------------- /packages/dox-core/lib/exception/query_exception.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | 5 | class QueryException extends IHttpException { 6 | QueryException({ 7 | String message = 'Error in sql query', 8 | String errorCode = 'sql_query_error', 9 | int code = HttpStatus.internalServerError, 10 | }) { 11 | super.code = code; 12 | super.errorCode = errorCode; 13 | super.message = message; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/dox-core/lib/utils/json.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class JSON { 4 | static String stringify(dynamic object) { 5 | return jsonEncode(object, toEncodable: advanceEncode); 6 | } 7 | 8 | static dynamic parse(String object) { 9 | return jsonDecode(object); 10 | } 11 | } 12 | 13 | dynamic advanceEncode(dynamic object) { 14 | if (object is DateTime) { 15 | return object.toIso8601String(); 16 | } 17 | return object.toJson(); 18 | } 19 | -------------------------------------------------------------------------------- /packages/dox-auth/lib/src/auth_exception.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_annotation/dox_annotation.dart'; 4 | 5 | class UnAuthorizedException extends IHttpException { 6 | UnAuthorizedException({ 7 | String message = 'Authentication failed', 8 | String errorCode = 'unauthorized', 9 | int code = HttpStatus.unauthorized, 10 | }) { 11 | super.code = code; 12 | super.errorCode = errorCode; 13 | super.message = message; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/config/api_router.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | import '../middleware/custom_middleware.dart'; 4 | 5 | class ApiRouter extends Router { 6 | @override 7 | String get prefix => 'api'; 8 | 9 | @override 10 | void register() { 11 | Route.use(customMiddleware); 12 | Route.use([ClassBasedMiddleware()]); 13 | 14 | Route.get('ping', (DoxRequest req) => 'pong'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/dox-websocket/lib/src/utils/json.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class JSON { 4 | static String stringify(dynamic object) { 5 | return jsonEncode(object, toEncodable: advanceEncode); 6 | } 7 | 8 | static dynamic parse(String object) { 9 | return jsonDecode(object); 10 | } 11 | } 12 | 13 | dynamic advanceEncode(dynamic object) { 14 | if (object is DateTime) { 15 | return object.toIso8601String(); 16 | } 17 | return object.toJson(); 18 | } 19 | -------------------------------------------------------------------------------- /packages/dox-core/lib/exception/internal_error_exception.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | 5 | class InternalErrorException extends IHttpException { 6 | InternalErrorException({ 7 | String message = 'Server Error', 8 | String errorCode = 'server_error', 9 | int code = HttpStatus.internalServerError, 10 | }) { 11 | super.code = code; 12 | super.errorCode = errorCode; 13 | super.message = message; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/dox-core/test/unit/env_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('ENV get key', () { 6 | Env().load(); 7 | String key = Env.get('APP_KEY'); 8 | expect(key, '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt'); 9 | }); 10 | 11 | test('ENV get key which not exist', () { 12 | Env().load(); 13 | String key = Env.get('DB_NAME', 'postgres'); 14 | expect(key, 'postgres'); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /packages/dox-core/lib/exception/validation_exception.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | 5 | class ValidationException extends IHttpException { 6 | ValidationException({ 7 | dynamic message = const {}, 8 | String errorCode = 'validation_failed', 9 | int code = HttpStatus.unprocessableEntity, 10 | }) { 11 | super.code = code; 12 | super.errorCode = errorCode; 13 | super.message = message; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/dox-core/lib/isolate/isolate_interfaces.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:dox_core/router/route_data.dart'; 3 | 4 | class IsolateSpawnParameter { 5 | final int isolateId; 6 | final AppConfig config; 7 | final List services; 8 | final List routes; 9 | 10 | const IsolateSpawnParameter( 11 | this.isolateId, 12 | this.config, 13 | this.services, { 14 | this.routes = const [], 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /packages/dox-builder/build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | dox_builder|annotations: 5 | enabled: true 6 | 7 | builders: 8 | dox_builder: 9 | target: ":dox_builder" 10 | import: "package:dox_builder/dox_builder.dart" 11 | builder_factories: ["buildDoxModel"] 12 | build_extensions: { ".dart": [".g.dart", ".dox_model_generator.g.part"] } 13 | auto_apply: dependents 14 | build_to: cache 15 | applies_builders: ["source_gen|combining_builder"] 16 | -------------------------------------------------------------------------------- /docs/digging-deeper/env.md: -------------------------------------------------------------------------------- 1 | # Environment Variables 2 | 3 | Dox allows you to access the environment variables from `.env` using `Env` class. 4 | 5 | === "Example" 6 | 7 | ```dart 8 | Env.get('APP_KEY'); 9 | 10 | // With default values 11 | Env.get('APP_KEY', 'default_value'); 12 | ``` 13 | 14 | ## With type 15 | 16 | === "Int" 17 | 18 | ```dart 19 | Env.get('APP_PORT', 3000) 20 | ``` 21 | 22 | === "String" 23 | 24 | ```dart 25 | Env.get('APP_KEY') 26 | ``` -------------------------------------------------------------------------------- /packages/dox-core/test/unit/exception/internal_error_exception_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('internal_error_exception', () { 6 | InternalErrorException exception = InternalErrorException( 7 | message: 'something went wrong', 8 | errorCode: 'internal_error', 9 | ); 10 | 11 | dynamic res = exception.toResponse(); 12 | expect(res, 'something went wrong'); 13 | expect(exception.code, 500); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /packages/dox-app/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox_app 2 | publish_to: 'none' 3 | 4 | environment: 5 | sdk: '>=3.0.0 <4.0.0' 6 | 7 | dependencies: 8 | postgres: ^3.0.5 9 | mysql1: ^0.20.0 10 | ioredis: ^1.0.2 11 | short_uuids: ^2.0.0 12 | dox_auth: ^2.0.0 13 | dox_migration: ^2.0.0 14 | dox_core: ^2.0.0 15 | dox_websocket: ^1.0.0 16 | dox_query_builder: ^2.1.0 17 | 18 | dev_dependencies: 19 | dox_builder: ^2.0.0 20 | build_runner: ^2.3.3 21 | lints: ^2.0.0 22 | test: ^1.21.0 23 | http: ^1.0.0 24 | 25 | assets: 26 | - .env -------------------------------------------------------------------------------- /packages/dox-app/bin/server.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/config/app.dart'; 2 | import 'package:dox_core/dox_core.dart'; 3 | import 'package:dox_migration/dox_migration.dart'; 4 | 5 | void main() async { 6 | /// Initialize Dox 7 | Dox().initialize(appConfig); 8 | 9 | /// Run database migration before starting server. 10 | /// Since Migration need to process only once, 11 | /// it do not required to register in services. 12 | await Migration().migrate(); 13 | 14 | /// Start dox http server 15 | await Dox().startServer(); 16 | } 17 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/models/blog_info/blog_info.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | import '../blog/blog.model.dart'; 4 | 5 | part 'blog_info.model.g.dart'; 6 | 7 | @DoxModel() 8 | class BlogInfo extends BlogInfoGenerator { 9 | @Column() 10 | Map? info; 11 | 12 | @Column(name: 'blog_id') 13 | int? blogId; 14 | 15 | @BelongsTo(Blog, onQuery: onQuery) 16 | Blog? blog; 17 | 18 | static Model onQuery(Blog q) { 19 | return q.debug(false); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/dox-core/lib/cache/cache_driver_interface.dart: -------------------------------------------------------------------------------- 1 | /// coverage:ignore-file 2 | 3 | abstract class CacheDriverInterface { 4 | String tag = ''; 5 | 6 | String get prefix => 'dox-framework-cache-$tag:'; 7 | 8 | Future put(String key, String value, {Duration? duration}); 9 | 10 | Future forever(String key, String value); 11 | 12 | Future forget(String key); 13 | 14 | Future flush(); 15 | 16 | void setTag(String tagName); 17 | 18 | Future get(String key); 19 | 20 | Future has(String key); 21 | } 22 | -------------------------------------------------------------------------------- /melos.yaml: -------------------------------------------------------------------------------- 1 | name: dox_framework 2 | 3 | packages: 4 | - packages/** 5 | 6 | scripts: 7 | 8 | test: 9 | exec: dart test --concurrency=1 10 | packageFilters: 11 | noSelect: true 12 | ignore: 13 | - "dox_query_builder" 14 | 15 | test_pg: 16 | exec: DRIVER=postgres dart test --concurrency=1 17 | packageFilters: 18 | noSelect: true 19 | scope: "dox_query_builder" 20 | 21 | test_mysql: 22 | exec: DRIVER=mysql dart test --concurrency=1 23 | packageFilters: 24 | noSelect: true 25 | scope: "dox_query_builder" -------------------------------------------------------------------------------- /packages/dox-builder/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.0.0 2 | 3 | - Bug fix on the builder 4 | 5 | ## 1.0.4 6 | 7 | - Change `newQuery` to `query()` and deprecate `newQuery` 8 | - Bug fix on `save()` 9 | 10 | ## 1.0.3 11 | 12 | - Bug fixed on eager/preload data missing in `toMap` response. 13 | - Auto convert camelCase variable into snake_case column. 14 | 15 | ## 1.0.2 16 | 17 | - Bug fixed .dox_model_generator.g.part from .dart which is not specified 18 | 19 | ## 1.0.1 20 | 21 | - Bug fixed on soft deletes and custom table name 22 | 23 | ## 1.0.0 24 | 25 | - Dox Builder 26 | -------------------------------------------------------------------------------- /packages/dox-cli/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox 2 | description: This is a cli tool to generate migration, model and to handle database migration with dox framework. It support up and down methods to control migration version. 3 | version: 3.0.0 4 | repository: https://github.com/dartondox/dox 5 | homepage: https://dartondox.dev 6 | 7 | environment: 8 | sdk: '>=3.0.0 <4.0.0' 9 | 10 | dependencies: 11 | watcher: ^1.0.2 12 | path: ^1.8.3 13 | dox_migration: ^2.0.0 14 | 15 | dev_dependencies: 16 | lints: ^2.0.0 17 | test: ^1.21.0 18 | 19 | executables: 20 | dox: dox 21 | -------------------------------------------------------------------------------- /packages/dox-query-builder/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox_query_builder 2 | description: PostgresSQL, MYSQL query builder, Support Model, Where, orWhere, Find, Join, softDeletes, Debugging and many mores. 3 | version: 3.0.0 4 | repository: https://github.com/dartondox/dox 5 | homepage: https://dartondox.dev 6 | 7 | environment: 8 | sdk: '>=3.0.0 <4.0.0' 9 | 10 | dependencies: 11 | postgres: ^3.0.5 12 | mysql1: ^0.20.0 13 | dox_annotation: ^1.0.5 14 | 15 | dev_dependencies: 16 | lints: ^2.0.0 17 | test: ^1.21.0 18 | build_runner: ^2.3.3 19 | dox_builder: ^2.0.0 20 | -------------------------------------------------------------------------------- /melos_dox_framework.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/dox-app/lib/services/websocket_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/config/redis.dart'; 2 | import 'package:dox_core/dox_core.dart'; 3 | import 'package:dox_websocket/dox_websocket.dart'; 4 | import 'package:ioredis/ioredis.dart'; 5 | 6 | class WebsocketService implements DoxService { 7 | @override 8 | void setup() { 9 | Redis sub = redis.duplicate(); 10 | Redis pub = sub.duplicate(); 11 | 12 | WebsocketServer io = WebsocketServer(Dox()); 13 | io.adapter(WebsocketRedisAdapter( 14 | subscriber: sub, 15 | publisher: pub, 16 | )); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/dox-app/lib/utils/log_filter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | Map logFilter(Map log) { 4 | Map newMap = jsonDecode(jsonEncode(log)); 5 | modifyMap(newMap, 'password'); 6 | modifyMap(newMap, 'authorization'); 7 | return newMap; 8 | } 9 | 10 | void modifyMap(Map map, String keyName) { 11 | map.forEach((dynamic key, dynamic value) { 12 | if (value is Map) { 13 | modifyMap(value, keyName); 14 | } 15 | if (key == keyName) { 16 | map[keyName] = '[REDACTED]'; 17 | } 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/mysql.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:mysql1/mysql1.dart'; 4 | 5 | Future mysqlConnection() { 6 | String host = Platform.environment['DB_HOST'] ?? 'localhost'; 7 | int port = int.parse(Platform.environment['PORT'] ?? '3306'); 8 | String user = Platform.environment['DB_USER'] ?? 'root'; 9 | ConnectionSettings settings = ConnectionSettings( 10 | host: host, 11 | port: port, 12 | user: user, 13 | password: 'password', 14 | db: 'dox-framework', 15 | ); 16 | return MySqlConnection.connect(settings); 17 | } 18 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/postgres.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:postgres/postgres.dart'; 4 | 5 | Future postgresConnection() { 6 | String host = Platform.environment['DB_HOST'] ?? 'localhost'; 7 | int port = int.parse(Platform.environment['PORT'] ?? '5432'); 8 | return Connection.open( 9 | Endpoint( 10 | host: host, 11 | port: port, 12 | database: 'postgres', 13 | username: 'postgres', 14 | password: 'postgres', 15 | ), 16 | settings: PoolSettings( 17 | sslMode: SslMode.disable, 18 | ), 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /packages/dox-app/lib/config/services.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/services/auth_service.dart'; 2 | import 'package:dox_app/services/orm_service.dart'; 3 | import 'package:dox_app/services/websocket_service.dart'; 4 | import 'package:dox_core/dox_core.dart'; 5 | 6 | /// Services to register on dox 7 | /// ------------------------------- 8 | /// Since dox run on multi thread isolate, we need to register 9 | /// below extra services to dox. 10 | /// So that dox can register again on new isolate. 11 | List services = [ 12 | ORMService(), 13 | AuthService(), 14 | WebsocketService(), 15 | ]; 16 | -------------------------------------------------------------------------------- /packages/dox-core/lib/utils/hash.dart: -------------------------------------------------------------------------------- 1 | import 'package:bcrypt/bcrypt.dart'; 2 | 3 | class Hash { 4 | /// has the password using BCrypt technique 5 | /// ``` 6 | /// Hash.make('secret'); 7 | /// ``` 8 | static String make(String password) { 9 | return BCrypt.hashpw(password, BCrypt.gensalt()); 10 | } 11 | 12 | /// verity the password with input password and stored hash string 13 | /// ``` 14 | /// Hash.verify('secret', ''); 15 | /// ``` 16 | static bool verify(String password, String hashedPassword) { 17 | return BCrypt.checkpw(password, hashedPassword); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/requests/blog.request.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class BlogRequest extends FormRequest { 4 | String? title; 5 | 6 | String? description; 7 | 8 | @override 9 | void setUp() { 10 | title = input('title'); 11 | description = input('description'); 12 | } 13 | 14 | @override 15 | Map rules() { 16 | return { 17 | 'title': 'required', 18 | 'description': 'required', 19 | }; 20 | } 21 | 22 | @override 23 | Map messages() { 24 | return {}; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/dox-app/lib/config/redis.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:ioredis/ioredis.dart'; 3 | 4 | Redis redis = Redis( 5 | /// Redis configuration 6 | /// ------------------------------- 7 | /// Following is the configuration used by the Redis provider to connect to 8 | /// the redis server and execute redis commands. 9 | RedisOptions( 10 | host: Env.get('REDIS_HOST', '127.0.0.1'), 11 | port: Env.get('REDIS_PORT', 6379), 12 | db: Env.get('REDIS_DB', 0), 13 | username: Env.get('REDIS_USERNAME', ''), 14 | password: Env.get('REDIS_PASSWORD', ''), 15 | ), 16 | ); 17 | -------------------------------------------------------------------------------- /packages/dox-core/lib/constants/constants.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: constant_identifier_names 2 | 3 | const String AUTH_REQUEST_KEY = 'dox_authentication_instance'; 4 | 5 | const String UNKNOWN = 'unknown'; 6 | 7 | const String WEB_SOCKET_JOIN_ROOM_EVENT_NAME = 'joinRoom'; 8 | 9 | const String WEB_SOCKET_EVENT_KEY = 'event'; 10 | 11 | const String WEB_SOCKET_MESSAGE_KEY = 'message'; 12 | 13 | const String WEB_SOCKET_SENDER_KEY = 'sender'; 14 | 15 | const String WEB_SOCKET_ROOM_KEY = 'room'; 16 | 17 | const String WEB_SOCKET_DEFAULT_ROOM_NAME = '__default'; 18 | 19 | const String FILE_DOWNLOAD_HEADER = 'Content-Disposition'; 20 | -------------------------------------------------------------------------------- /packages/dox-core/lib/router/route_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class RouteData { 4 | final String method; 5 | String path; 6 | final dynamic controllers; 7 | Map params = {}; 8 | final List preMiddleware; 9 | FormRequest Function()? formRequest; 10 | 11 | final String? domain; 12 | final bool? corsEnabled; 13 | 14 | RouteData({ 15 | required this.method, 16 | required this.path, 17 | required this.controllers, 18 | this.corsEnabled, 19 | this.preMiddleware = const [], 20 | this.domain, 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /packages/dox-auth/lib/src/auth_engine.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_auth/src/interfaces.dart'; 2 | 3 | class AuthEngine { 4 | late AuthConfig config; 5 | 6 | /// get auth guard 7 | AuthGuard get guard => config.guards[config.defaultGuard]!; 8 | 9 | /// get driver 10 | AuthDriver get driver => guard.driver; 11 | 12 | /// get auth provider 13 | AuthProvider get provider => guard.provider; 14 | 15 | /// singleton 16 | static final AuthEngine _singleton = AuthEngine._internal(); 17 | factory AuthEngine() => _singleton; 18 | AuthEngine._internal(); 19 | 20 | void init(AuthConfig authConfig) { 21 | config = authConfig; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/dox-core/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dox_core 2 | description: A perfect solution for your web backend development with dart that offers a wide range of features to help you build powerful and scalable web applications. 3 | version: 3.0.0 4 | repository: https://github.com/dartondox/dox 5 | 6 | environment: 7 | sdk: '>=3.0.0 <4.0.0' 8 | 9 | dependencies: 10 | encrypt: ^5.0.3 11 | bcrypt: ^1.1.3 12 | path: ^1.8.3 13 | sprintf: ^7.0.0 14 | mime: ^1.0.4 15 | string_scanner: ^1.1.0 16 | uuid: ^3.0.6 17 | dox_annotation: ^1.0.5 18 | 19 | dev_dependencies: 20 | lints: ^2.0.0 21 | test: ^1.21.0 22 | http: ^0.13.6 23 | http_parser: ^4.0.2 24 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/models/artist/artist.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | import '../song/song.model.dart'; 4 | 5 | part 'artist.model.g.dart'; 6 | 7 | @DoxModel() 8 | class Artist extends ArtistGenerator { 9 | @Column() 10 | String? name; 11 | 12 | @ManyToMany( 13 | Song, 14 | localKey: 'id', 15 | relatedKey: 'id', 16 | pivotForeignKey: 'artist_id', 17 | pivotRelatedForeignKey: 'song_id', 18 | pivotTable: 'artist_song', 19 | onQuery: onQuery, 20 | ) 21 | List songs = []; 22 | 23 | static Model onQuery(Song q) { 24 | return q.debug(false); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/dox-auth/lib/src/hash.dart: -------------------------------------------------------------------------------- 1 | import 'package:bcrypt/bcrypt.dart'; 2 | 3 | class Hash { 4 | /// has the password using BCrypt technique 5 | /// ``` 6 | /// Hash.make('secret'); 7 | /// ``` 8 | static String make(String password) { 9 | return BCrypt.hashpw(password, BCrypt.gensalt()); 10 | } 11 | 12 | /// verity the password with input password and stored hash string 13 | /// ``` 14 | /// Hash.verify('secret', ''); 15 | /// ``` 16 | static bool verify(String password, String hashedPassword) { 17 | try { 18 | return BCrypt.checkpw(password, hashedPassword); 19 | } catch (error) { 20 | return false; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/dox-cli/lib/src/tools/update_dox.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | updateDox() async { 5 | final process = 6 | await Process.start('dart', ['pub', 'global', 'activate', 'dox']); 7 | process.stdout.transform(utf8.decoder).listen((data) { 8 | List lines = data.split("\n"); 9 | for (String line in lines) { 10 | if (line.isNotEmpty) { 11 | print(line); 12 | } 13 | } 14 | }); 15 | 16 | process.stderr.transform(utf8.decoder).listen((data) { 17 | List lines = data.split("\n"); 18 | for (String line in lines) { 19 | if (line.isNotEmpty) { 20 | print(line); 21 | } 22 | } 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /packages/dox-app/lib/services/orm_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/config/database.dart'; 2 | import 'package:dox_core/dox_core.dart'; 3 | import 'package:dox_query_builder/dox_query_builder.dart'; 4 | 5 | /// Query builder service 6 | /// -------------------------- 7 | /// Initializing to setup query builder so that this project can use ORM. 8 | /// If this project do not require database, you can simply delete this file 9 | /// and remove from config/services.dart list. 10 | class ORMService implements DoxService { 11 | @override 12 | Future setup() async { 13 | /// Initialize Sql QueryBuilder 14 | SqlQueryBuilder.initializeWithDatabaseConfig(databaseConfig); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/truncate.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | import 'shared_mixin.dart'; 4 | 5 | mixin Truncate implements SharedMixin { 6 | /// Truncate table 7 | /// 8 | /// ``` 9 | /// await Blog().truncate(); 10 | /// await Blog().truncate(resetId: true); 11 | /// ``` 12 | Future truncate({bool resetId = true}) async { 13 | String reset = resetId 14 | ? queryBuilder.dbDriver.getName() == Driver.postgres 15 | ? 'RESTART IDENTITY CASCADE' 16 | : '' 17 | : ''; 18 | String query = "TRUNCATE TABLE $tableName $reset"; 19 | await helper.runQuery(query); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/shared_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | import 'utils/helper.dart'; 4 | import 'utils/logger.dart'; 5 | 6 | abstract class SharedMixin { 7 | QueryBuilder get queryBuilder; 8 | QueryBuilderHelper get helper; 9 | Logger get logger; 10 | DBDriver get dbDriver; 11 | Map get substitutionValues; 12 | String get selectQueryString; 13 | String primaryKey = 'id'; 14 | bool shouldDebug = false; 15 | String tableName = ''; 16 | bool isSoftDeletes = false; 17 | String? connection; 18 | 19 | void addSubstitutionValues(String key, dynamic value); 20 | void resetSubstitutionValues(); 21 | } 22 | -------------------------------------------------------------------------------- /packages/dox-auth/test/hash_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_auth/src/hash.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('Hash |', () { 6 | test('make/verify', () { 7 | String secret = '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt'; 8 | String hashedPassword = Hash.make(secret); 9 | bool result = Hash.verify(secret, hashedPassword); 10 | expect(result, true); 11 | }); 12 | 13 | test('invalid should return false', () { 14 | String secret = '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt'; 15 | String hashedPassword = Hash.make(secret); 16 | bool result = Hash.verify('password', hashedPassword); 17 | expect(result, false); 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /packages/dox-app/lib/services/auth_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/app/models/user/user.model.dart'; 2 | import 'package:dox_auth/dox_auth.dart'; 3 | import 'package:dox_core/dox_core.dart'; 4 | 5 | class AuthService implements DoxService { 6 | @override 7 | void setup() { 8 | Auth.initialize(AuthConfig( 9 | /// default auth guard 10 | defaultGuard: 'web', 11 | 12 | /// list of auth guards 13 | guards: { 14 | 'web': AuthGuard( 15 | driver: JwtAuthDriver(secret: SecretKey(Env.get('APP_KEY'))), 16 | provider: AuthProvider( 17 | model: () => User(), 18 | ), 19 | ), 20 | }, 21 | )); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/dox-app/test/http_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/config/app.dart'; 2 | import 'package:dox_core/dox_core.dart'; 3 | import 'package:http/http.dart' as http; 4 | import 'package:test/test.dart'; 5 | 6 | String baseUrl = 'http://localhost:${appConfig.serverPort}'; 7 | 8 | void main() { 9 | setUpAll(() async { 10 | Dox().initialize(appConfig); 11 | await Dox().startServer(); 12 | await Future.delayed(Duration(milliseconds: 500)); 13 | }); 14 | 15 | test('ping route', () async { 16 | Uri url = Uri.parse('$baseUrl/api/ping'); 17 | http.Response response = await http.get(url); 18 | expect(response.statusCode, 200); 19 | expect(response.body, 'pong'); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /packages/dox-core/test/unit/hash_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('Hash |', () { 6 | test('make/verify', () { 7 | String secret = '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt'; 8 | String hashedPassword = Hash.make(secret); 9 | bool result = Hash.verify(secret, hashedPassword); 10 | expect(result, true); 11 | }); 12 | 13 | test('invalid should return false', () { 14 | String secret = '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt'; 15 | String hashedPassword = Hash.make(secret); 16 | bool result = Hash.verify('password', hashedPassword); 17 | expect(result, false); 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/requests/blog_request.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class BlogRequest extends FormRequest { 4 | String? title; 5 | 6 | @override 7 | void setUp() { 8 | title = input('title'); 9 | } 10 | 11 | @override 12 | Map mapInputs() { 13 | return { 14 | 'foo': 'bar', 15 | }; 16 | } 17 | 18 | @override 19 | Map rules() { 20 | return { 21 | 'title': 'required', 22 | }; 23 | } 24 | 25 | @override 26 | Map messages() { 27 | return { 28 | 'required': 'The {field} is missing', 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/dox-migration/lib/src/utils/file_extension.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | extension FileName on File { 4 | String get name { 5 | Uri uri = Platform.script.resolve(path); 6 | return uri.pathSegments.last.split('.').first; 7 | } 8 | 9 | String get ext { 10 | Uri uri = Platform.script.resolve(path); 11 | return uri.pathSegments.last.split('.').last.toLowerCase(); 12 | } 13 | 14 | bool get isUp { 15 | Uri uri = Platform.script.resolve(path); 16 | return uri.pathSegments.last.toLowerCase().contains('.up.'); 17 | } 18 | 19 | bool get isDown { 20 | Uri uri = Platform.script.resolve(path); 21 | return uri.pathSegments.last.toLowerCase().contains('.down.'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/count.dart: -------------------------------------------------------------------------------- 1 | import 'shared_mixin.dart'; 2 | 3 | mixin Count implements SharedMixin { 4 | /// count the record 5 | /// count() cannot use with select() 6 | /// ``` 7 | /// await Blog().count(); 8 | /// ``` 9 | // ignore: always_specify_types 10 | Future count() async { 11 | String q = "SELECT count(*) as total FROM $tableName"; 12 | q += helper.getCommonQuery(isCountQuery: true); 13 | List result = helper.getMapResult(await helper.runQuery(q)); 14 | if (result.isNotEmpty) { 15 | if (result.first['total'] != null) { 16 | return int.parse(result.first['total'].toString()); 17 | } 18 | } 19 | return 0; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/dox-migration/lib/src/utils/string_extension.dart: -------------------------------------------------------------------------------- 1 | extension ToSnake on String { 2 | String toSnake() { 3 | StringBuffer result = StringBuffer(); 4 | for (int letter in codeUnits) { 5 | if (letter >= 65 && letter <= 90) { 6 | // Check if uppercase ASCII 7 | if (result.isNotEmpty) { 8 | // Add underscore if not first letter 9 | result.write('_'); 10 | } 11 | result.write(String.fromCharCode(letter + 32)); // Add lowercase ASCII 12 | } else { 13 | result.write(String.fromCharCode(letter)); // Add original character 14 | } 15 | } 16 | String finalString = result.toString().replaceAll(RegExp('_+'), '_'); 17 | return finalString; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/group_by.dart: -------------------------------------------------------------------------------- 1 | import 'query_builder.dart'; 2 | import 'shared_mixin.dart'; 3 | 4 | mixin GroupBy implements SharedMixin { 5 | String _groupBy = ''; 6 | 7 | String getGroupByQuery() { 8 | return _groupBy; 9 | } 10 | 11 | /// Group by query 12 | /// 13 | /// ``` 14 | /// String query = await Blog() 15 | /// .select('count (*) as total, status') 16 | /// .groupBy('status') 17 | /// .get(); 18 | /// ``` 19 | QueryBuilder groupBy(dynamic column) { 20 | if (column is String) { 21 | _groupBy = ' GROUP BY $column'; 22 | } 23 | if (column is List) { 24 | _groupBy = ' GROUP BY ${column.join(',')}'; 25 | } 26 | return queryBuilder; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/order_by.dart: -------------------------------------------------------------------------------- 1 | import 'query_builder.dart'; 2 | import 'shared_mixin.dart'; 3 | 4 | mixin OrderBy implements SharedMixin { 5 | final List _orderBy = []; 6 | 7 | String getOrderByQuery() { 8 | if (_orderBy.isNotEmpty) { 9 | return " ORDER BY ${_orderBy.join(',')}"; 10 | } 11 | return ""; 12 | } 13 | 14 | /// order by query 15 | /// 16 | /// ``` 17 | /// List blogs = await Blog().orderBy('id').get() 18 | /// List blogs = await Blog().orderBy('id', 'desc').get() 19 | /// ``` 20 | QueryBuilder orderBy(dynamic column, [dynamic type]) { 21 | _orderBy.add('$column ${type == null ? '' : type.toString()}'); 22 | return queryBuilder; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/raw.dart: -------------------------------------------------------------------------------- 1 | import 'query_builder.dart'; 2 | import 'shared_mixin.dart'; 3 | 4 | mixin Raw implements SharedMixin { 5 | String _rawQuery = ''; 6 | 7 | String getRawQuery() { 8 | return _rawQuery; 9 | } 10 | 11 | /// Raw query (this function cannot used with Model) 12 | /// 13 | /// ``` 14 | /// await QueryBuilder.rawQuery('select * from blog').get(); 15 | /// ``` 16 | QueryBuilder rawQuery(String query, [dynamic substitutionValues]) { 17 | _rawQuery = query; 18 | if (substitutionValues is Map) { 19 | substitutionValues.forEach((String key, dynamic value) { 20 | addSubstitutionValues(key, value); 21 | }); 22 | } 23 | return queryBuilder; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/schema/table.column.dart: -------------------------------------------------------------------------------- 1 | class TableColumn { 2 | final String name; 3 | final String? type; 4 | 5 | bool isNullable = false; 6 | dynamic defaultValue; 7 | bool isUnique = false; 8 | bool? shouldDrop = false; 9 | String? renameTo; 10 | 11 | TableColumn({ 12 | required this.name, 13 | this.type, 14 | this.shouldDrop, 15 | this.renameTo, 16 | }); 17 | 18 | TableColumn nullable([dynamic nullable]) { 19 | isNullable = (nullable is bool) ? nullable : true; 20 | return this; 21 | } 22 | 23 | TableColumn withDefault(dynamic val) { 24 | defaultValue = val; 25 | return this; 26 | } 27 | 28 | TableColumn unique() { 29 | isUnique = true; 30 | return this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/models/category/category.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/app/models/blog/blog.model.dart'; 2 | import 'package:dox_query_builder/dox_query_builder.dart'; 3 | 4 | part 'category.model.g.dart'; 5 | 6 | @DoxModel() 7 | class Category extends CategoryGenerator { 8 | @override 9 | List get hidden => []; 10 | 11 | @Column() 12 | String? name; 13 | 14 | @ManyToMany( 15 | Blog, 16 | localKey: 'id', 17 | relatedKey: 'id', 18 | pivotForeignKey: 'category_id', 19 | pivotRelatedForeignKey: 'blog_id', 20 | onQuery: onQueryActiveUser, 21 | ) 22 | List blogs = []; 23 | 24 | static QueryBuilder onQueryActiveUser(Blog q) { 25 | return q.debug(true).where('user_id', 1); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/dox-app/lib/utils/extensions.dart: -------------------------------------------------------------------------------- 1 | extension Slugify on String { 2 | String slugify({String separator = '-'}) { 3 | String input = this; 4 | // Remove leading and trailing whitespaces 5 | String slug = input.trim(); 6 | 7 | // Convert to lowercase 8 | slug = slug.toLowerCase(); 9 | 10 | // Replace spaces and underscores with dashes 11 | slug = slug.replaceAll(RegExp(r'[\s_]+'), separator); 12 | 13 | // Remove special characters 14 | slug = slug.replaceAll(RegExp(r'[^a-z0-9\u0080-\uFFFF-]'), ''); 15 | 16 | // Remove consecutive dashes 17 | slug = slug.replaceAll(RegExp(r'-+'), separator); 18 | 19 | // Remove leading and trailing dashes 20 | slug = slug.replaceAll(RegExp(r'^-|-$'), ''); 21 | 22 | return slug; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/dox-app/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use latest stable channel SDK. 2 | FROM dart:stable AS build 3 | 4 | # Resolve app dependencies. 5 | WORKDIR /app 6 | COPY pubspec.* ./ 7 | RUN dart pub get 8 | 9 | # Copy app source code (except anything in .dockerignore) and AOT compile app. 10 | COPY . . 11 | RUN dart run build_runner build --delete-conflicting-outputs 12 | RUN dart compile exe bin/server.dart -o bin/server 13 | 14 | # Build minimal serving image from AOT-compiled `/server` 15 | # and the pre-built AOT-runtime in the `/runtime/` directory of the base image. 16 | FROM scratch 17 | COPY --from=build /runtime/ / 18 | COPY --from=build /app/.env / 19 | COPY --from=build /app/bin/server /bin/ 20 | COPY --from=build /app/db/migration /db/migration 21 | 22 | # Start server. 23 | CMD ["/bin/server"] 24 | -------------------------------------------------------------------------------- /packages/dox-core/test/unit/dox_cookie_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | import '../integration/requirements/config/app.dart'; 5 | 6 | void main() { 7 | group('DoxCookie |', () { 8 | test('get', () { 9 | Dox().config = config; 10 | DoxCookie cookie = DoxCookie('x-auth', 'Bearerxxxxxxxxx'); 11 | String cookieValue = cookie.get(); 12 | expect(cookieValue, 13 | 'x-auth=EzBl7TV9yA+U1lLsyfMgTPjbCRh/5FQOODjbEwej58Y=; Max-Age=3600000'); 14 | }); 15 | 16 | test('expire', () { 17 | Dox().config = config; 18 | DoxCookie cookie = DoxCookie('x-auth', ''); 19 | String cookieValue = cookie.expire(); 20 | expect(cookieValue, 'x-auth=; Max-Age=-1000'); 21 | }); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/serializers/serializer_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | import 'blog_serializer.dart'; 4 | 5 | void main() { 6 | group('serializer', () { 7 | test('singular', () { 8 | BlogSerializer serializer = BlogSerializer(Blog()); 9 | dynamic map = serializer.toJson(); 10 | expect(map['title'], 'hello'); 11 | }); 12 | 13 | test('singular null', () { 14 | BlogSerializer serializer = BlogSerializer(null); 15 | dynamic map = serializer.toJson(); 16 | expect(map, null); 17 | }); 18 | 19 | test('list', () { 20 | BlogSerializer serializer = BlogSerializer([Blog()]); 21 | dynamic map = serializer.toJson(); 22 | expect(map[0]['title'], 'hello'); 23 | }); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /packages/dox-auth/lib/src/auth_middleware.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: depend_on_referenced_packages 2 | // ignore_for_file: constant_identifier_names 3 | 4 | import 'package:dox_auth/dox_auth.dart'; 5 | import 'package:dox_query_builder/dox_query_builder.dart'; 6 | 7 | const String AUTH_REQUEST_KEY = 'dox_authentication_instance'; 8 | 9 | class AuthMiddleware implements IDoxMiddleware { 10 | @override 11 | Future handle(IDoxRequest req) async { 12 | Auth auth = Auth(); 13 | 14 | // verify token from header and get user data from database 15 | await auth.verifyToken(req); 16 | 17 | if (auth.isLoggedIn()) { 18 | /// merge into request auth 19 | req.merge({AUTH_REQUEST_KEY: auth}); 20 | return req; 21 | } 22 | throw UnAuthorizedException(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/dox-websocket/test/config/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:dox_websocket/dox_websocket.dart'; 3 | 4 | class WsController { 5 | intro(WebsocketEmitter emitter, dynamic message) { 6 | emitter.emit('intro', message); 7 | } 8 | 9 | json(WebsocketEmitter emitter, dynamic message) { 10 | emitter.emit('json', message); 11 | } 12 | } 13 | 14 | class WebsocketRouter implements Router { 15 | @override 16 | List get middleware => []; 17 | 18 | @override 19 | String get prefix => ''; 20 | 21 | @override 22 | void register() { 23 | WsController wsController = WsController(); 24 | 25 | Route.websocket('ws', (WebsocketEvent event) { 26 | event.on('intro', wsController.intro); 27 | event.on('json', wsController.json); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/update.dart: -------------------------------------------------------------------------------- 1 | import 'shared_mixin.dart'; 2 | 3 | mixin Update implements SharedMixin { 4 | /// where raw condition 5 | /// 6 | /// ``` 7 | /// Blog blog = await Blog().where('id', 1).update({ 8 | /// "title" : "new title", 9 | /// }); 10 | /// ``` 11 | Future update(Map data) async { 12 | String q; 13 | q = "UPDATE $tableName SET "; 14 | 15 | List columnToUpdate = []; 16 | data.forEach((String column, dynamic value) { 17 | String columnKey = helper.parseColumnKey(column); 18 | columnToUpdate.add("$column = $columnKey"); 19 | addSubstitutionValues(columnKey, value); 20 | }); 21 | q += columnToUpdate.join(','); 22 | q += helper.getCommonQuery(); 23 | await helper.runQuery(q); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/dox-cli/melos_dox.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/dox-app/melos_dox_app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/dox-auth/melos_dox_auth.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/dox-core/melos_dox_core.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/types/database_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | class DatabaseConfig { 4 | /// Name of the connection 5 | String connection; 6 | 7 | /// List of database connections 8 | Map connections; 9 | 10 | DatabaseConfig({ 11 | required this.connection, 12 | required this.connections, 13 | }); 14 | 15 | /// get database connection configuration 16 | ConnectionConfig getConnectionConfig([String? connectionName]) { 17 | ConnectionConfig? conn; 18 | if (connectionName == null) { 19 | conn = connections[connection]; 20 | } else { 21 | conn = connections[connectionName]; 22 | } 23 | if (conn == null) { 24 | throw Exception('$connection not found'); 25 | } 26 | return conn; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/dox-builder/melos_dox_builder.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/dox-annotation/melos_dox_annotation.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/dox-cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.0.1 2 | 3 | - bug fix on dox create command 4 | 5 | ## 2.0.0 6 | 7 | - Release stable version v2.0 8 | 9 | ## 2.0.0-alpha.3 10 | 11 | - Fix migration issue 12 | 13 | ## 2.0.0-alpha.2 14 | 15 | - Add option to create project with specific version 16 | 17 | ## 2.0.0-alpha.1 18 | 19 | - Compatible with core version 2 20 | 21 | ## 1.1.1 22 | 23 | - handle migration with dox_migration 24 | - set default migration file with .sql 25 | 26 | ## 1.0.55 27 | 28 | - Add command to create serializer 29 | - Add help option 30 | 31 | ## 1.0.54 32 | 33 | - update request template 34 | - Add help option 35 | 36 | ## 1.0.53 37 | 38 | - Added command to create form request `dox create:request Blog` 39 | 40 | ## 1.0.52 41 | 42 | - Added command to create websocket controller 43 | 44 | ## 1.0.51 45 | 46 | - Initial version. 47 | -------------------------------------------------------------------------------- /packages/dox-migration/melos_dox_migration.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/select.dart: -------------------------------------------------------------------------------- 1 | import 'query_builder.dart'; 2 | import 'shared_mixin.dart'; 3 | 4 | mixin Select implements SharedMixin { 5 | String _select = '*'; 6 | 7 | String getSelectQuery() { 8 | return _select; 9 | } 10 | 11 | /// Select query 12 | /// 13 | /// ``` 14 | /// await Blog().select('title, body').get(); 15 | /// await Blog().select('title').select('body').get(); 16 | /// await Blog().select(['title', 'body']).get(); 17 | /// await Blog().select(['title', 'body']).get(); 18 | /// ``` 19 | /// [selection] can be string or array 20 | QueryBuilder select(dynamic selection) { 21 | if (selection is List) { 22 | _select = selection.join(','); 23 | } 24 | if (selection is String) { 25 | _select = selection; 26 | } 27 | return queryBuilder; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/dox-websocket/melos_dox_websocket.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/digging-deeper/deployment.md: -------------------------------------------------------------------------------- 1 | # Deployment 2 | 3 | ## Build 4 | 5 | Compile dart into machine code. 6 | 7 | ```py 8 | dox build 9 | ``` 10 | 11 | Start the application. 12 | 13 | ```py 14 | bin/server 15 | ``` 16 | 17 | ## Nginx Configuration 18 | 19 | Running server with Nginx reverse proxy. 20 | 21 | ```py 22 | server { 23 | listen 80; 24 | 25 | server_name ; 26 | 27 | location / { 28 | proxy_pass http://localhost:; 29 | proxy_http_version 1.1; 30 | proxy_set_header Upgrade $http_upgrade; 31 | proxy_set_header Connection 'upgrade'; 32 | proxy_set_header Host $host; 33 | proxy_set_header X-Real-IP $remote_addr; 34 | proxy_set_header X-Forwarded-Proto $scheme; 35 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 36 | proxy_cache_bypass $http_upgrade; 37 | } 38 | } 39 | ``` -------------------------------------------------------------------------------- /packages/dox-query-builder/melos_dox_query_builder.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/dox-core/test/unit/aes_encryptor_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/utils/aes_encryptor.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('AES Encryptor |', () { 6 | test('encode/decode success', () { 7 | String secret = '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt'; 8 | String message = 'Dox Framework'; 9 | String encoded = AESEncryptor.encode(message, secret); 10 | String decoded = AESEncryptor.decode(encoded, secret); 11 | expect(decoded, message); 12 | }); 13 | 14 | test('encode/decode failed', () { 15 | String secret = '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt'; 16 | String message = 'Dox Framework'; 17 | String encoded = AESEncryptor.encode(message, secret); 18 | String decoded = AESEncryptor.decode(encoded, 'abcd'); 19 | expect(decoded, ''); 20 | }); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /docs/digging-deeper/testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | ## Integration test 4 | 5 | ```dart 6 | import 'package:dox_app/config/app.dart'; 7 | import 'package:dox_core/dox_core.dart'; 8 | import 'package:http/http.dart' as http; 9 | import 'package:test/test.dart'; 10 | 11 | String baseUrl = 'http://localhost:${config.serverPort}'; 12 | 13 | void main() { 14 | setUpAll(() async { 15 | Dox().initialize(appConfig); 16 | await Dox().startServer(); 17 | 18 | // for for few seconds to fully started http server 19 | await Future.delayed(Duration(milliseconds: 500)); 20 | }); 21 | 22 | test('/api/ping route', () async { 23 | var url = Uri.parse('$baseUrl/api/ping'); 24 | var response = await http.get(url); 25 | expect(response.statusCode, 200); 26 | expect(response.body, 'pong'); 27 | }); 28 | } 29 | ``` -------------------------------------------------------------------------------- /packages/dox-app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | # Http server 4 | dox: 5 | build: . 6 | hostname: dox 7 | ports: 8 | # export:internal 9 | - "${APP_PORT}:${APP_PORT}" 10 | depends_on: 11 | postgres: 12 | condition: service_healthy 13 | 14 | # Postgres database 15 | postgres: 16 | image: postgres:14.1 17 | volumes: 18 | - ../.docker/build/postgres:/var/lib/postgresql/data 19 | environment: 20 | - POSTGRES_USER=${DB_USERNAME} 21 | - POSTGRES_PASSWORD=${DB_PASSWORD} 22 | - POSTGRES_DB=${DB_NAME} 23 | expose: 24 | - ${DB_PORT} 25 | ports: 26 | - "${DB_PORT}:${DB_PORT}" 27 | command: -p ${DB_PORT} 28 | restart: always 29 | healthcheck: 30 | test: ['CMD', 'pg_isready', '-q', '-U', 'postgres', '-p', '${DB_PORT}'] 31 | interval: 5s 32 | retries: 5 33 | -------------------------------------------------------------------------------- /packages/dox-app/lib/config/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | LoggerConfig logger = LoggerConfig( 4 | /// Application name 5 | /// ------------------------------- 6 | /// The name of the application you want to add to the log. 7 | name: Env.get('APP_NAME'), 8 | 9 | /// Toggle logger 10 | /// ------------------------------- 11 | /// Enable or disable logger in application. 12 | enabled: true, 13 | 14 | /// Logging level 15 | /// ------------------------------- 16 | /// The level from which you want the logger to flush logs. 17 | level: Env.get('LOG_LEVEL', 'info'), 18 | 19 | /// Pretty print 20 | /// ------------------------------- 21 | /// It is highly advised NOT to use `prettyPrint` in production, since it 22 | /// can have huge impact on performance. 23 | prettyPrint: Env.get('APP_ENV') == 'development', 24 | ); 25 | -------------------------------------------------------------------------------- /packages/dox-core/lib/http/response/serializer.dart: -------------------------------------------------------------------------------- 1 | class Serializer { 2 | List _payload = []; 3 | 4 | bool _isSingular = true; 5 | 6 | Serializer(dynamic data) { 7 | if (data is List) { 8 | _isSingular = false; 9 | _payload = data; 10 | } 11 | if (data is T) { 12 | _isSingular = true; 13 | _payload = [data]; 14 | } 15 | } 16 | 17 | /// coverage:ignore-start 18 | /// convert model into Map 19 | Map convert(T m) { 20 | return {}; 21 | } 22 | 23 | /// coverage:ignore-end 24 | 25 | dynamic toJson() { 26 | List> ret = >[]; 27 | for (T p in _payload) { 28 | ret.add(convert(p)); 29 | } 30 | if (_isSingular) { 31 | return ret.isNotEmpty ? ret.first : null; 32 | } 33 | return ret; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/printers/console_printer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dox_query_builder/dox_query_builder.dart'; 4 | 5 | // coverage:ignore-file 6 | class ConsoleQueryPrinter implements QueryPrinter { 7 | Function(String)? filter; 8 | 9 | ConsoleQueryPrinter({ 10 | this.filter, 11 | }); 12 | 13 | @override 14 | void log(String query, List params) { 15 | Map payload = { 16 | 'timestamp': DateTime.now().toIso8601String(), 17 | 'level': 'INFO', 18 | 'message': query, 19 | 'payload': { 20 | 'query': query, 21 | 'params': params, 22 | } 23 | }; 24 | String stringToPrint = jsonEncode(payload); 25 | if (filter != null) { 26 | stringToPrint = filter!(stringToPrint); 27 | } 28 | print(stringToPrint); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/dox-cli/test/dox_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox/dox.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('string format test', () { 6 | test('toPascalWithFirstLetterLowerCase', () { 7 | String res1 = toPascalWithFirstLetterLowerCase('CamelCase'); 8 | String res2 = toPascalWithFirstLetterLowerCase('camel_case'); 9 | String res3 = toPascalWithFirstLetterLowerCase('Camel_Case'); 10 | String res4 = toPascalWithFirstLetterLowerCase('Camel_case'); 11 | String res5 = toPascalWithFirstLetterLowerCase('camelCase'); 12 | String res6 = toPascalWithFirstLetterLowerCase('camel_Case'); 13 | 14 | expect(res1, 'camelCase'); 15 | expect(res2, 'camelCase'); 16 | expect(res3, 'camelCase'); 17 | expect(res4, 'camelCase'); 18 | expect(res5, 'camelCase'); 19 | expect(res6, 'camelCase'); 20 | }); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/model_custom_table_name_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | import 'connection.dart'; 5 | import 'models/user/user.model.dart'; 6 | 7 | void main() async { 8 | await initQueryBuilder(); 9 | 10 | group('Model with custom table name', () { 11 | setUp(() async { 12 | await Schema.create('users', (Table table) { 13 | table.id(); 14 | table.string('name'); 15 | table.softDeletes(); 16 | }); 17 | }); 18 | 19 | tearDown(() async { 20 | await Schema.drop('users'); 21 | }); 22 | 23 | test('get user list', () async { 24 | await User().insert({'name': 'dox'}); 25 | 26 | List users = await User().get(); 27 | expect(users.length, 1); 28 | expect(users.first.name, 'dox'); 29 | }); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ['necessarylion'] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /packages/dox-migration/lib/src/utils/logger.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | class Logger { 3 | /// log the text using info color (lightblue) 4 | /// ``` 5 | /// DoxLogger.info('using dox is fun'); 6 | /// ``` 7 | static void info(dynamic text) { 8 | print('\x1B[36m$text\x1B[0m'); 9 | } 10 | 11 | /// log the text using warn color (yellow) 12 | /// ``` 13 | /// DoxLogger.info('careful!'); 14 | /// 15 | static void warn(dynamic text) { 16 | print('\x1B[34m$text\x1B[0m'); 17 | } 18 | 19 | /// log the text using success color (green) 20 | /// ``` 21 | /// DoxLogger.info('success'); 22 | /// 23 | static void success(dynamic text) { 24 | print('\x1B[32m$text\x1B[0m'); 25 | } 26 | 27 | /// log the text using danger color (red) 28 | /// ``` 29 | /// DoxLogger.danger('failed'); 30 | /// 31 | static void danger(dynamic text) { 32 | print('\x1B[31m$text\x1B[0m'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/controllers/blog.controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class BlogController { 4 | /// GET /admins 5 | Future index(DoxRequest req) async { 6 | return 'GET /admins'; 7 | } 8 | 9 | /// GET /admins/create 10 | String create(DoxRequest req) { 11 | return 'GET /admins/create'; 12 | } 13 | 14 | /// GET /admins/{id} 15 | Future show(DoxRequest req, String id) async { 16 | return 'GET /admins/{id}'; 17 | } 18 | 19 | /// GET /admins/{id}/edit 20 | Future edit(DoxRequest req, String id) async { 21 | return 'GET /admins/{id}/edit'; 22 | } 23 | 24 | /// PUT|PATCH /admins/{id} 25 | Future update(DoxRequest req, String id) async { 26 | return 'PUT|PATCH /admins/{id}'; 27 | } 28 | 29 | /// DELETE /admins/{id} 30 | Future destroy(DoxRequest req, String id) async { 31 | return 'DELETE /admins/{id}'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/dox-core/test/unit/json_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/utils/json.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | class JsonSerializableObject { 5 | Map toJson() { 6 | return {'success': 'true'}; 7 | } 8 | } 9 | 10 | void main() { 11 | group('JSON |', () { 12 | test('stringify & parse', () { 13 | Map data = { 14 | 'name': 'dox', 15 | 'date': DateTime(2023), 16 | }; 17 | String jsonString = JSON.stringify(data); 18 | expect(jsonString.contains('dox'), true); 19 | 20 | Map jsond = JSON.parse(jsonString); 21 | expect(jsond['name'], 'dox'); 22 | expect(jsond['date'], '2023-01-01T00:00:00.000'); 23 | }); 24 | 25 | test('json serializable object', () { 26 | String jsonString = JSON.stringify(JsonSerializableObject()); 27 | expect(jsonString.contains('success'), true); 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /packages/dox-app/lib/routes/websocket.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:dox_websocket/dox_websocket.dart'; 3 | 4 | class WebsocketRouter extends Router { 5 | @override 6 | List get middleware => []; 7 | 8 | @override 9 | void register() { 10 | Route.websocket('ws', (WebsocketEvent event) { 11 | event.on('intro', (WebsocketEmitter emitter, dynamic message) { 12 | emitter.emit('intro', message); 13 | }); 14 | 15 | event.on('json', (WebsocketEmitter emitter, dynamic message) { 16 | emitter.emit('json_response', message); 17 | }); 18 | }); 19 | 20 | Route.websocket('chat', (WebsocketEvent event) { 21 | event.on('intro', (WebsocketEmitter emitter, dynamic message) { 22 | emitter.emit('intro', message); 23 | }); 24 | 25 | event.on('json', (WebsocketEmitter emitter, dynamic message) { 26 | emitter.emit('json_response', message); 27 | }); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/dox_query_builder.dart: -------------------------------------------------------------------------------- 1 | library dox_query_builder; 2 | 3 | export 'package:dox_annotation/dox_annotation.dart'; 4 | 5 | export 'src/drivers/db_driver.dart'; 6 | export 'src/drivers/postgres_driver.dart'; 7 | export 'src/main.dart'; 8 | export 'src/model.dart'; 9 | export 'src/printers/console_printer.dart'; 10 | export 'src/printers/file_printer.dart'; 11 | export 'src/printers/pretty_printer.dart'; 12 | export 'src/printers/printer.dart'; 13 | export 'src/query_builder.dart'; 14 | export 'src/relationships/belongs_to.dart'; 15 | export 'src/relationships/has_many.dart'; 16 | export 'src/relationships/has_one.dart'; 17 | export 'src/relationships/many_to_many.dart'; 18 | export 'src/schema.dart'; 19 | export 'src/schema/table.dart'; 20 | export 'src/soft_deletes.dart'; 21 | export 'src/types/connection_config.dart'; 22 | export 'src/types/database_config.dart'; 23 | export 'src/types/pagination_result.dart'; 24 | export 'src/utils/annotation.dart'; 25 | export 'src/utils/functions.dart'; 26 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/types/pagination_result.dart: -------------------------------------------------------------------------------- 1 | /// pagination result 2 | class Pagination { 3 | /// total records 4 | final int total; 5 | 6 | /// record per page 7 | final int perPage; 8 | 9 | /// last page of the pagination 10 | final int lastPage; 11 | 12 | /// current page of pagination 13 | final int currentPage; 14 | 15 | // ignore: always_specify_types 16 | List data; 17 | 18 | /// constructor 19 | Pagination({ 20 | required this.total, 21 | required this.perPage, 22 | required this.lastPage, 23 | required this.currentPage, 24 | required this.data, 25 | }); 26 | 27 | /// get list of data 28 | List getData() { 29 | return data as List; 30 | } 31 | 32 | /// support json encode 33 | Map toJson() { 34 | return { 35 | 'total': total, 36 | 'per_page': perPage, 37 | 'last_page': lastPage, 38 | 'current_page': currentPage, 39 | 'data': data, 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/others/contribute.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | Want to contribute? Great! Fork the repo and create PR to us. 4 | 5 | # Setting up local development 6 | 7 | #### 1. Clone repo 8 | 9 | ```py 10 | git clone git@github.com:dartondox/dox.git 11 | ``` 12 | 13 | #### 2. Install melos if not installed 14 | 15 | ```py 16 | dart pub global activate melos 17 | ``` 18 | 19 | #### 3. Run melos bootstrap 20 | 21 | ```py 22 | melos bs 23 | ``` 24 | 25 | #### 4. Other Useful Commands 26 | 27 | ```py 28 | # Run all tests 29 | melos run test 30 | 31 | # Run tests for dox_query_builder with postgres 32 | melos run test_query_builder_postgres 33 | 34 | # Run tests for dox_query_builder with mysql 35 | melos run test_query_builder_mysql 36 | 37 | # Generate dox cli executable file 38 | cd packages/dox-cli 39 | dart compile exe bin/dox.dart -o bin/dox 40 | 41 | # Run dox cli executable file 42 | bin/dox --version 43 | ``` 44 | 45 | #### If failed to run the project 46 | 47 | - remove all pubspec.lock files 48 | - and run `melos bs` again 49 | -------------------------------------------------------------------------------- /packages/dox-core/lib/isolate/isolate_handler.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:dox_core/isolate/isolate_interfaces.dart'; 3 | import 'package:dox_core/server/dox_server.dart'; 4 | 5 | /// process middleware and controller and sent data via sentPort 6 | void isolateHandler(IsolateSpawnParameter param) async { 7 | /// send port of main isolate 8 | AppConfig appConfig = param.config; 9 | List services = param.services; 10 | 11 | /// creating dox in new isolate; 12 | Dox().isolateId = param.isolateId; 13 | Dox().initialize(appConfig); 14 | Dox().addServices(services); 15 | 16 | /// register routes 17 | Route().setRoutes(param.routes); 18 | 19 | /// starting registered services in new isolate; 20 | await Dox().startServices(); 21 | 22 | /// starting server in new isolate 23 | DoxServer().setResponseHandler(param.config.responseHandler); 24 | 25 | await DoxServer().listen( 26 | param.config.serverPort, 27 | isolateId: param.isolateId, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /packages/dox-cli/lib/src/tools/create_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../utils/utils.dart'; 4 | 5 | String _getSample(className, filename) { 6 | return ''' 7 | import 'package:dox_query_builder/dox_query_builder.dart'; 8 | 9 | part '$filename.model.g.dart'; 10 | 11 | @DoxModel() 12 | class $className extends ${className}Generator { 13 | @override 14 | List get hidden => []; 15 | } 16 | '''; 17 | } 18 | 19 | bool createModel(filename) { 20 | filename = pascalToSnake(filename); 21 | String className = snakeToPascal(filename); 22 | String path = '${Directory.current.path}/lib/app/models/$filename/'; 23 | final file = File('$path$filename.model.dart'); 24 | 25 | if (file.existsSync()) { 26 | print('\x1B[32m$className model already exists\x1B[0m'); 27 | return false; 28 | } 29 | 30 | file.createSync(recursive: true); 31 | file.writeAsStringSync(_getSample(className, filename), mode: FileMode.write); 32 | print('\x1B[32m$className model created successfully.\x1B[0m'); 33 | return true; 34 | } 35 | -------------------------------------------------------------------------------- /packages/dox-core/lib/http/http_websocket_handler.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | import 'package:dox_core/http/http_controller_handler.dart'; 5 | import 'package:dox_core/http/http_error_handler.dart'; 6 | import 'package:dox_core/http/http_request_handler.dart'; 7 | import 'package:dox_core/http/http_response_handler.dart'; 8 | import 'package:dox_core/router/route_data.dart'; 9 | 10 | void httpWebSocketHandler(HttpRequest req, RouteData route) { 11 | getDoxRequest(req, route).then((DoxRequest doxReq) { 12 | middlewareAndControllerHandler(doxReq).then((dynamic result) { 13 | httpResponseHandler(result, req); 14 | }).onError((Object? error, StackTrace stackTrace) { 15 | /// coverage:ignore-start 16 | httpErrorHandler(req, error, stackTrace); 17 | 18 | /// coverage:ignore-end 19 | }); 20 | }).onError((Object? error, StackTrace stackTrace) { 21 | /// coverage:ignore-start 22 | httpErrorHandler(req, error, stackTrace); 23 | 24 | /// coverage:ignore-end 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /packages/dox-app/lib/routes/api.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/app/http/controllers/api.controller.dart'; 2 | import 'package:dox_app/app/http/controllers/auth.controller.dart'; 3 | import 'package:dox_app/app/http/controllers/blog.controller.dart'; 4 | import 'package:dox_auth/dox_auth.dart'; 5 | import 'package:dox_core/dox_core.dart'; 6 | 7 | class ApiRouter extends Router { 8 | @override 9 | String get prefix => 'api'; 10 | 11 | @override 12 | List get middleware => []; 13 | 14 | @override 15 | void register() { 16 | ApiController api = ApiController(); 17 | BlogController blogController = BlogController(); 18 | AuthController authController = AuthController(); 19 | 20 | Route.get('ping', [api.pong]); 21 | 22 | Route.resource('blogs', blogController); 23 | 24 | Route.post('/auth/login', authController.login); 25 | Route.post('/auth/register', authController.register); 26 | Route.get('/auth/user', [ 27 | AuthMiddleware(), 28 | authController.user, 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/controllers/admin.controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | 3 | class AdminController { 4 | /// GET /admins 5 | Future index(DoxRequest req) async { 6 | return 'GET /admins'; 7 | } 8 | 9 | /// GET /admins/create 10 | String create(DoxRequest req) { 11 | return 'GET /admins/create'; 12 | } 13 | 14 | /// POST /admins 15 | Future store(DoxRequest req) async { 16 | return 'POST /admins'; 17 | } 18 | 19 | /// GET /admins/{id} 20 | Future show(DoxRequest req, String id) async { 21 | return 'GET /admins/{id}'; 22 | } 23 | 24 | /// GET /admins/{id}/edit 25 | Future edit(DoxRequest req, String id) async { 26 | return 'GET /admins/{id}/edit'; 27 | } 28 | 29 | /// PUT|PATCH /admins/{id} 30 | Future update(DoxRequest req, String id) async { 31 | return 'PUT|PATCH /admins/{id}'; 32 | } 33 | 34 | /// DELETE /admins/{id} 35 | Future destroy(DoxRequest req, String id) async { 36 | return 'DELETE /admins/{id}'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/limit.dart: -------------------------------------------------------------------------------- 1 | import 'query_builder.dart'; 2 | import 'shared_mixin.dart'; 3 | 4 | mixin Limit implements SharedMixin { 5 | String _limit = ''; 6 | String _offset = '0'; 7 | 8 | String getLimitQuery() { 9 | if (_limit.isNotEmpty) { 10 | return " LIMIT $_limit OFFSET $_offset"; 11 | } 12 | return ""; 13 | } 14 | 15 | /// limit query 16 | /// 17 | /// ``` 18 | /// List blogs = await Blog().take(10).get() 19 | /// ``` 20 | QueryBuilder take(int value) { 21 | _limit = value.toString(); 22 | return queryBuilder; 23 | } 24 | 25 | /// limit query 26 | /// 27 | /// ``` 28 | /// List blogs = await Blog().limit(10).get() 29 | /// ``` 30 | QueryBuilder limit(int value) { 31 | _limit = value.toString(); 32 | return queryBuilder; 33 | } 34 | 35 | /// offset limit 36 | /// 37 | /// ``` 38 | /// List blogs = await Blog().limit(10).offset(10).get() 39 | /// ``` 40 | QueryBuilder offset(int value) { 41 | _offset = value.toString(); 42 | return queryBuilder; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/dox-auth/lib/src/interfaces.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | class AuthGuard { 4 | final AuthDriver driver; 5 | final AuthProvider provider; 6 | 7 | const AuthGuard({ 8 | required this.driver, 9 | required this.provider, 10 | }); 11 | } 12 | 13 | class AuthProvider { 14 | final dynamic Function() model; 15 | 16 | final List identifierFields; 17 | final String passwordField; 18 | 19 | const AuthProvider({ 20 | required this.model, 21 | this.identifierFields = const ['email'], 22 | this.passwordField = 'password', 23 | }); 24 | } 25 | 26 | class AuthConfig { 27 | final String defaultGuard; 28 | final Map guards; 29 | 30 | AuthConfig({ 31 | required this.defaultGuard, 32 | this.guards = const {}, 33 | }); 34 | } 35 | 36 | abstract class AuthDriver { 37 | Future?> verifyToken(IDoxRequest req); 38 | 39 | Future?> attempt(Map credentials); 40 | 41 | String? createToken(Map? userPayload); 42 | } 43 | -------------------------------------------------------------------------------- /packages/dox-migration/lib/src/utils/env.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | Map loadEnv() { 4 | Map data = {}; 5 | 6 | File envFile = File('${Directory.current.path}/.env'); 7 | 8 | if (!envFile.existsSync()) { 9 | throw Exception('Could not find .env file'); 10 | } 11 | 12 | String contents = envFile.readAsStringSync(); 13 | 14 | // splitting with new line for each variables 15 | List list = contents.split('\n'); 16 | 17 | for (String d in list) { 18 | // splitting with equal sign to get key and value 19 | List keyValue = d.toString().split('='); 20 | if (keyValue.first.isNotEmpty) { 21 | data[keyValue.first.trim()] = _getValue(keyValue); 22 | } 23 | } 24 | 25 | return data; 26 | } 27 | 28 | String _getValue(List elements) { 29 | if (elements.length > 1) { 30 | List elementsExceptFirst = elements.sublist(1); 31 | String value = elementsExceptFirst.join('='); 32 | return value 33 | .replaceAll('"', '') 34 | .replaceAll("'", '') 35 | .replaceAll('`', '') 36 | .trim(); 37 | } 38 | return ''; 39 | } 40 | -------------------------------------------------------------------------------- /packages/dox-websocket/test/config/app_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/cache/drivers/file/file_cache_driver.dart'; 2 | import 'package:dox_core/dox_core.dart'; 3 | 4 | import 'router.dart'; 5 | 6 | class ResponseHandler extends ResponseHandlerInterface { 7 | @override 8 | DoxResponse handle(DoxResponse res) { 9 | return res; 10 | } 11 | } 12 | 13 | AppConfig appConfig = AppConfig( 14 | /// application key 15 | appKey: '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt', 16 | 17 | /// application server port 18 | serverPort: 3004, 19 | 20 | /// total multi-thread isolate to run 21 | totalIsolate: 1, 22 | 23 | // cors configuration 24 | cors: CORSConfig( 25 | origin: '*', 26 | methods: '*', 27 | credentials: true, 28 | ), 29 | 30 | /// response handler 31 | responseHandler: ResponseHandler(), 32 | 33 | /// global middleware 34 | globalMiddleware: [], 35 | 36 | /// routers 37 | routers: [ 38 | WebsocketRouter(), 39 | ], 40 | 41 | /// cache driver configuration 42 | cache: CacheConfig( 43 | drivers: { 44 | 'file': FileCacheDriver(), 45 | }, 46 | ), 47 | ); 48 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/drivers/db_driver.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | import 'package:dox_query_builder/src/drivers/mysql_driver.dart'; 3 | 4 | enum Driver { postgres, mysql } 5 | 6 | /// interface for database driver 7 | abstract class DBDriver { 8 | Driver getName(); 9 | 10 | /// run query and return map result 11 | Future>> query( 12 | String query, { 13 | String? primaryKey, 14 | Map? substitutionValues, 15 | }); 16 | 17 | /// run query, this function do not return any value 18 | Future execute(String query, 19 | {Map? substitutionValues}); 20 | } 21 | 22 | /// get database driver to run queries 23 | DBDriver getDatabaseDriver(Driver driver, dynamic database) { 24 | /// checking validation for postgres connection 25 | if (driver == Driver.postgres) { 26 | return PostgresDriver(conn: database); 27 | } 28 | 29 | /// checking validation for mysql connection 30 | else if (driver == Driver.mysql) { 31 | return MysqlDriver(conn: database); 32 | } 33 | throw Exception('Invalid driver or not supported'); 34 | } 35 | -------------------------------------------------------------------------------- /packages/dox-core/lib/middleware/log_middleware.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:dox_core/utils/json.dart'; 3 | 4 | class LogMiddleware implements IDoxMiddleware { 5 | Map Function(Map)? filter; 6 | 7 | final bool withHeader; 8 | final bool enabled; 9 | 10 | LogMiddleware({ 11 | required this.enabled, 12 | this.filter, 13 | this.withHeader = false, 14 | }); 15 | 16 | @override 17 | IDoxRequest handle(IDoxRequest req) { 18 | if (!enabled) { 19 | return req; 20 | } 21 | Map payload = { 22 | 'request': req.all(), 23 | }; 24 | 25 | if (withHeader) { 26 | payload['headers'] = req.headers; 27 | } 28 | 29 | Map text = { 30 | 'level': 'INFO', 31 | 'message': '${req.method} ${req.uri.path}', 32 | 'source_ip': req.ip(), 33 | 'timestamp': DateTime.now().toIso8601String(), 34 | 'payload': payload 35 | }; 36 | if (filter != null) { 37 | text = filter!(text); 38 | } 39 | print(JSON.stringify(text)); 40 | return req; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/dox-core/lib/utils/aes_encryptor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:encrypt/encrypt.dart'; 5 | 6 | class AESEncryptor { 7 | static IV iv = IV(Uint8List(16)); 8 | 9 | /// encode a message 10 | ///``` 11 | /// AESEncryptor.encode('message'); 12 | ///``` 13 | static String encode(String content, String secret) { 14 | String plainText = base64.encode(utf8.encode(content)); 15 | Key key = Key.fromUtf8(secret); 16 | Encrypter encrypter = Encrypter(AES(key)); 17 | Encrypted encrypted = encrypter.encrypt(plainText, iv: iv); 18 | return encrypted.base64; 19 | } 20 | 21 | /// decode a message 22 | ///``` 23 | /// AESEncryptor.decode('encoded_message'); 24 | ///``` 25 | static String decode(String content, String secret) { 26 | try { 27 | Key key = Key.fromUtf8(secret); 28 | Encrypter encrypter = Encrypter(AES(key)); 29 | String decrypted = encrypter.decrypt64(content, iv: iv); 30 | return utf8.decode(base64.decode(decrypted)); 31 | } catch (error) { 32 | print('AESEncryption Error: ${error.toString()}'); 33 | return ''; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/dox-cli/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | # linter: 19 | # rules: 20 | # - camel_case_types 21 | 22 | # analyzer: 23 | # exclude: 24 | # - path/to/excluded/files/** 25 | 26 | # For more information about the core and recommended set of lints, see 27 | # https://dart.dev/go/core-lints 28 | 29 | # For additional information about configuring this file, see 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /packages/dox-builder/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | # linter: 19 | # rules: 20 | # - camel_case_types 21 | 22 | # analyzer: 23 | # exclude: 24 | # - path/to/excluded/files/** 25 | 26 | # For more information about the core and recommended set of lints, see 27 | # https://dart.dev/go/core-lints 28 | 29 | # For additional information about configuring this file, see 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /packages/dox-core/lib/http/request/http_request_body.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:dox_core/http/request/form_data_visitor.dart'; 5 | 6 | class HttpBody { 7 | static Future> read(HttpRequest request) async { 8 | if (HttpBody.isJson(request.headers.contentType)) { 9 | String bodyString = await utf8.decoder.bind(request).join(); 10 | try { 11 | return jsonDecode(bodyString); 12 | } catch (err) { 13 | return {}; 14 | } 15 | } 16 | 17 | if (HttpBody.isFormData(request.headers.contentType)) { 18 | FormDataVisitor visitor = FormDataVisitor(request); 19 | await visitor.process(); 20 | return visitor.inputs; 21 | } 22 | 23 | return {}; 24 | } 25 | 26 | /// http request data is form data 27 | static bool isFormData(ContentType? contentType) { 28 | return contentType?.mimeType.toLowerCase().contains('form-data') == true; 29 | } 30 | 31 | /// http request data is json 32 | static bool isJson(ContentType? contentType) { 33 | return contentType.toString().toLowerCase().contains('json') == true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/dox-websocket/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | # linter: 19 | # rules: 20 | # - camel_case_types 21 | 22 | # analyzer: 23 | # exclude: 24 | # - path/to/excluded/files/** 25 | 26 | # For more information about the core and recommended set of lints, see 27 | # https://dart.dev/go/core-lints 28 | 29 | # For additional information about configuring this file, see 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /packages/dox-annotation/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | # linter: 19 | # rules: 20 | # - camel_case_types 21 | 22 | # analyzer: 23 | # exclude: 24 | # - path/to/excluded/files/** 25 | 26 | # For more information about the core and recommended set of lints, see 27 | # https://dart.dev/go/core-lints 28 | 29 | # For additional information about configuring this file, see 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/delete.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | import 'shared_mixin.dart'; 4 | 5 | mixin Delete implements SharedMixin { 6 | /// delete a record 7 | /// 8 | /// if SoftDeletes is used in model it will act as soft delete, 9 | /// otherwise it will delete completely from record 10 | /// 11 | /// ``` 12 | /// await Blog().where('id', 1).delete(); 13 | /// ``` 14 | Future delete() async { 15 | if (isSoftDeletes) { 16 | await queryBuilder.update({'deleted_at': now()}); 17 | } else { 18 | String q; 19 | q = "DELETE FROM $tableName"; 20 | q += helper.getCommonQuery(); 21 | await helper.runQuery(q); 22 | } 23 | } 24 | 25 | /// force delete a record 26 | /// 27 | /// if SoftDeletes is used in model and if you would like to delete 28 | /// completely from the record, use forceDelete() 29 | /// 30 | /// ``` 31 | /// await Blog().where('id', 1).forceDelete(); 32 | /// ` 33 | Future forceDelete() async { 34 | String q; 35 | q = "DELETE FROM $tableName"; 36 | q += helper.getCommonQuery(); 37 | await helper.runQuery(q); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/models/blog/blog.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | import '../blog_info/blog_info.model.dart'; 4 | 5 | part 'blog.model.g.dart'; 6 | 7 | @DoxModel( 8 | primaryKey: 'uid', 9 | createdAt: 'created_at', 10 | updatedAt: 'updated_at', 11 | softDelete: true, 12 | ) 13 | class Blog extends BlogGenerator { 14 | @override 15 | List get hidden => ['status']; 16 | 17 | @Column(beforeSave: slugTitle, beforeGet: beforeGet) 18 | String? title; 19 | 20 | @Column() 21 | String? status; 22 | 23 | @Column(name: 'body') 24 | String? description; 25 | 26 | @Column(name: 'deleted_at') 27 | DateTime? deletedAt; 28 | 29 | @HasOne(BlogInfo, eager: true, onQuery: onQuery) 30 | BlogInfo? blogInfo; 31 | 32 | @HasMany(BlogInfo, eager: true, onQuery: onQuery) 33 | List blogInfos = []; 34 | 35 | static String? slugTitle(Map map) { 36 | return map['title']; 37 | } 38 | 39 | static String? beforeGet(Map map) { 40 | return map['title']; 41 | } 42 | 43 | static Model onQuery(BlogInfo q) { 44 | return q.debug(false); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | concurrency: 9 | group: "publish" 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | publish: 14 | if: | 15 | startsWith(github.event.head_commit.message, 'release') 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: dart-lang/setup-dart@v1 20 | with: 21 | sdk: stable 22 | - name: Set dart credentials 23 | run: | 24 | mkdir "$XDG_CONFIG_HOME/dart" 25 | echo '${{ secrets.PUB_CREDENTIALS }}' > "$XDG_CONFIG_HOME/dart/pub-credentials.json" 26 | - name: Prepare git 27 | run: | 28 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 29 | git config --local user.name "github-actions[bot]" 30 | - name: Activate melos 31 | run: dart pub global activate melos 32 | - name: Publish package 33 | run: melos publish --no-dry-run --git-tag-version --yes 34 | - name: Create tags 35 | uses: CasperWA/push-protected@v2 36 | with: 37 | token: ${{ secrets.PUSH_TO_PROTECTED_BRANCH }} 38 | tags: true 39 | branch: main 40 | -------------------------------------------------------------------------------- /packages/dox-websocket/lib/src/websocket.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_annotation/dox_annotation.dart'; 2 | import 'package:dox_websocket/src/adapters/websocket_adapter.dart'; 3 | import 'package:dox_websocket/src/websocket_event.dart'; 4 | import 'package:uuid/uuid.dart'; 5 | 6 | Uuid uuid = Uuid(); 7 | 8 | class WebsocketServer implements IDoxWebsocket { 9 | // Parameter for the singleton class 10 | final IDox? dox; 11 | 12 | // Static instance variable for the singleton 13 | static WebsocketServer? _instance; 14 | 15 | WebsocketServer._internal([this.dox]); 16 | 17 | factory WebsocketServer([IDox? d]) { 18 | if (_instance == null) { 19 | _instance = WebsocketServer._internal(d); 20 | _instance!._initialize(); 21 | } 22 | return _instance!; 23 | } 24 | 25 | WebsocketAdapterInterface? _adaptor; 26 | 27 | _initialize() { 28 | if (dox != null) { 29 | dox?.setWebsocket(this); 30 | } 31 | } 32 | 33 | @override 34 | WebsocketEventHandler create() { 35 | return WebsocketEventHandler(); 36 | } 37 | 38 | adapter(WebsocketAdapterInterface adaptor) { 39 | _adaptor = adaptor; 40 | } 41 | 42 | WebsocketAdapterInterface? getAdapter() { 43 | return _adaptor; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/printers/file_printer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:dox_query_builder/dox_query_builder.dart'; 5 | 6 | // coverage:ignore-file 7 | class FileQueryPrinter implements QueryPrinter { 8 | Function(String)? filter; 9 | final File file; 10 | 11 | FileQueryPrinter({ 12 | required this.file, 13 | this.filter, 14 | }); 15 | 16 | @override 17 | void log(String query, List params) { 18 | Map payload = { 19 | 'timestamp': DateTime.now().toIso8601String(), 20 | 'level': 'INFO', 21 | 'message': query, 22 | 'payload': { 23 | 'query': query, 24 | 'params': params, 25 | } 26 | }; 27 | 28 | Directory directory = file.parent; 29 | if (!directory.existsSync()) { 30 | directory.createSync(recursive: true); 31 | } 32 | 33 | String stringToPrint = jsonEncode(payload); 34 | if (filter != null) { 35 | stringToPrint = filter!(stringToPrint); 36 | } 37 | 38 | RandomAccessFile fileToWrite = file.openSync(mode: FileMode.append); 39 | fileToWrite.writeStringSync('$stringToPrint\n'); 40 | fileToWrite.closeSync(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/dox-cli/bin/dox.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox/src/command_registry.dart'; 2 | 3 | void main(List args) async { 4 | if (args.isEmpty) { 5 | print('Could not find a command. Run `dox help` for more information.'); 6 | return; 7 | } 8 | 9 | final command = args[0]; 10 | final commandArgs = args.skip(1).toList(); 11 | 12 | try { 13 | await _executeCommand(command, commandArgs); 14 | } catch (e) { 15 | print(e); 16 | } 17 | } 18 | 19 | Future _executeCommand(String command, List args) async { 20 | final commandDefinition = CommandRegistry.findCommand(command); 21 | 22 | if (commandDefinition == null) { 23 | print( 24 | 'Could not find a command named "$command". Run `dox help` for more information.'); 25 | return; 26 | } 27 | 28 | if (!commandDefinition.validateArgs(args)) { 29 | print('Invalid number of arguments for command "$command".'); 30 | print( 31 | 'Expected: ${commandDefinition.minArgs}${commandDefinition.maxArgs != -1 ? ' to ${commandDefinition.maxArgs}' : '+'} arguments'); 32 | print('Received: ${args.length} arguments'); 33 | return; 34 | } 35 | 36 | try { 37 | await commandDefinition.function(args); 38 | } catch (e) { 39 | print(e); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/dox-cli/lib/src/tools/create_request.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../utils/utils.dart'; 4 | 5 | String _getSample(className, filename) { 6 | return ''' 7 | import 'package:dox_core/dox_core.dart'; 8 | 9 | class ${className}Request extends FormRequest { 10 | @override 11 | void setUp() {} 12 | 13 | @override 14 | Map rules() { 15 | return {}; 16 | } 17 | 18 | @override 19 | Map messages() { 20 | return {}; 21 | } 22 | } 23 | '''; 24 | } 25 | 26 | bool createRequest(String filename) { 27 | filename = filename.toLowerCase().replaceAll('request', ''); 28 | filename = pascalToSnake(filename); 29 | String className = snakeToPascal(filename); 30 | String path = '${Directory.current.path}/lib/app/http/requests/'; 31 | String requestName = '$filename.request'; 32 | final file = File('$path$filename.request.dart'); 33 | 34 | if (file.existsSync()) { 35 | print('\x1B[32m$requestName already exists\x1B[0m'); 36 | return false; 37 | } 38 | 39 | file.createSync(recursive: true); 40 | file.writeAsStringSync(_getSample(className, filename), mode: FileMode.write); 41 | print('\x1B[32m$requestName created successfully.\x1B[0m'); 42 | return true; 43 | } 44 | -------------------------------------------------------------------------------- /docs/the-basic/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ```py 4 | dart pub global activate dox 5 | ``` 6 | 7 | ## Create a new project 8 | 9 | ```py 10 | dox create new_blog 11 | ``` 12 | !!! warning "Export bin path" 13 | Please make sure you have included `bin` path to your profile. If you did not added path to your profile yet, open `~/.bashrc` or `~/.zshrc` and paste below line. 14 | 15 | ```bash 16 | export PATH="$PATH":"~/.pub-cache/bin" 17 | ``` 18 | 19 | ## Create a specific version 20 | 21 | ```py 22 | dox create new_blog --version v2.0.0 23 | ``` 24 | 25 | ## Or download from github 26 | 27 | ```py 28 | https://github.com/dartondox/dox-sample/archive/refs/tags/v2.0.0.zip 29 | ``` 30 | 31 | ## Start server 32 | 33 | ```py 34 | dox s 35 | 36 | or 37 | 38 | bin/dox s 39 | ``` 40 | 41 | ## Start server with docker 42 | 43 | ```py 44 | docker-compose up -d --build 45 | ``` 46 | 47 | !!! tips 48 | Ensure that setting `APP_ENV` in `.env` to `development` facilitates server operations with hot reloading during development, while configuring it as `production` ensures compilation into machine code for server deployment. 49 | 50 | ## Watch the builder 51 | 52 | ```py 53 | dart run build_runner watch 54 | 55 | or 56 | 57 | dox build_runner:watch 58 | ``` -------------------------------------------------------------------------------- /packages/dox-app/lib/app/models/blog/blog.model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/app/models/category/category.model.dart'; 2 | import 'package:dox_app/app/models/user/user.model.dart'; 3 | import 'package:dox_app/utils/extensions.dart'; 4 | import 'package:dox_query_builder/dox_query_builder.dart'; 5 | 6 | part 'blog.model.g.dart'; 7 | 8 | @DoxModel(softDelete: true) 9 | class Blog extends BlogGenerator { 10 | @override 11 | List get hidden => []; 12 | 13 | @Column() 14 | int? userId; 15 | 16 | @Column() 17 | String? title; 18 | 19 | @Column(beforeSave: makeSlug) 20 | String? slug; 21 | 22 | @Column() 23 | String? description; 24 | 25 | @BelongsTo(User, eager: false, foreignKey: 'user_id', localKey: 'id') 26 | User? user; 27 | 28 | @ManyToMany( 29 | Category, 30 | localKey: 'id', 31 | relatedKey: 'id', 32 | pivotForeignKey: 'blog_id', 33 | pivotRelatedForeignKey: 'category_id', 34 | onQuery: onQueryActiveUser, 35 | ) 36 | List categories = []; 37 | 38 | static String makeSlug(Map map) { 39 | return map['title'].toString().slugify(); 40 | } 41 | 42 | static QueryBuilder onQueryActiveUser(Category q) { 43 | return q.debug(true).where('user_id', 1); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/dox-builder/lib/src/util.dart: -------------------------------------------------------------------------------- 1 | String toCamelCase(String snakeCase) { 2 | List words = snakeCase.split('_'); 3 | String camelCase = words[0]; // Add the first word as it is 4 | 5 | for (int i = 1; i < words.length; i++) { 6 | String word = words[i]; 7 | String capitalizedWord = 8 | word[0].toUpperCase() + word.substring(1).toLowerCase(); 9 | camelCase += capitalizedWord; 10 | } 11 | return camelCase; 12 | } 13 | 14 | String toSnakeCase(String input) { 15 | String snakeCase = ''; 16 | 17 | for (int i = 0; i < input.length; i++) { 18 | String char = input[i]; 19 | // Check if the current character is uppercase 20 | if (char == char.toUpperCase() && i > 0) { 21 | snakeCase += 22 | '_'; // Add an underscore if it's uppercase and not the first character 23 | } 24 | snakeCase += char 25 | .toLowerCase(); // Convert the character to lowercase and add it to the result 26 | } 27 | 28 | return snakeCase.replaceAll(RegExp(r'_+'), '_'); 29 | } 30 | 31 | ucFirst(String str) { 32 | return str.substring(0, 1).toUpperCase() + str.substring(1); 33 | } 34 | 35 | lcFirst(String? str) { 36 | if (str == null) { 37 | return ''; 38 | } 39 | return str.substring(0, 1).toLowerCase() + str.substring(1); 40 | } 41 | -------------------------------------------------------------------------------- /packages/dox-cli/lib/src/tools/create_serializer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../utils/utils.dart'; 4 | 5 | String _getSample(className, filename) { 6 | return ''' 7 | import 'package:dox_core/dox_core.dart'; 8 | 9 | import '../../../app/models/$filename/$filename.model.dart'; 10 | 11 | class ${className}Serializer extends Serializer<$className> { 12 | ${className}Serializer(super.data); 13 | 14 | @override 15 | Map convert($className m) { 16 | return {}; 17 | } 18 | } 19 | '''; 20 | } 21 | 22 | bool createSerializer(String filename) { 23 | filename = filename.toLowerCase().replaceAll('serializer', ''); 24 | filename = pascalToSnake(filename); 25 | String className = snakeToPascal(filename); 26 | String path = '${Directory.current.path}/lib/app/http/serializers/'; 27 | String serializerName = '$filename.serializer'; 28 | final file = File('$path$filename.serializer.dart'); 29 | 30 | if (file.existsSync()) { 31 | print('\x1B[32m$serializerName already exists\x1B[0m'); 32 | return false; 33 | } 34 | 35 | file.createSync(recursive: true); 36 | file.writeAsStringSync(_getSample(className, filename), mode: FileMode.write); 37 | print('\x1B[32m$serializerName created successfully.\x1B[0m'); 38 | return true; 39 | } 40 | -------------------------------------------------------------------------------- /docs/digging-deeper/services.md: -------------------------------------------------------------------------------- 1 | # Services 2 | 3 | If your application requires additional services like a database, auth etc.., you'll need to create a class that implements the `DoxService` interface and then register it with Dox. Since Dox operates with isolates (multi-threading), these extra services must be passed to each isolate to ensure their availability on all isolates. 4 | 5 | ### Example with auth 6 | 7 | === "AuthService" 8 | 9 | ```dart 10 | class AuthService implements DoxService { 11 | @override 12 | void setup() { 13 | Auth.initialize(AuthConfig( 14 | /// default auth guard 15 | defaultGuard: 'web', 16 | 17 | /// list of auth guards 18 | guards: { 19 | 'web': AuthGuard( 20 | driver: JwtAuthDriver(secret: SecretKey(Env.get('APP_KEY'))), 21 | provider: AuthProvider( 22 | model: () => User(), 23 | ), 24 | ), 25 | }, 26 | )); 27 | } 28 | } 29 | ``` 30 | 31 | ##### 32 | === "Register into dox `app/config/services.dart`" 33 | 34 | ```dart 35 | List services = [ 36 | ... /// other services 37 | AuthService, 38 | ]; 39 | ``` 40 | 41 | -------------------------------------------------------------------------------- /packages/dox-cli/lib/src/tools/create_middleware.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../utils/utils.dart'; 4 | 5 | String _getSample(className, filename) { 6 | return ''' 7 | import 'package:dox_core/dox_core.dart'; 8 | 9 | class ${className}Middleware extends IDoxMiddleware { 10 | @override 11 | dynamic handle(IDoxRequest req) { 12 | /// add your logic here 13 | /// return req (IDoxRequest) to process next to the controller 14 | /// or throw an error or return Map, String, List etc to return 200 response 15 | return req; 16 | } 17 | } 18 | '''; 19 | } 20 | 21 | bool createMiddleware(String filename) { 22 | filename = filename.toLowerCase().replaceAll('middleware', ''); 23 | filename = pascalToSnake(filename); 24 | String className = snakeToPascal(filename); 25 | String path = '${Directory.current.path}/lib/app/http/middleware/'; 26 | String middlewareName = '$filename.middleware'; 27 | final file = File('$path$filename.middleware.dart'); 28 | 29 | if (file.existsSync()) { 30 | print('\x1B[32m$middlewareName already exists\x1B[0m'); 31 | return false; 32 | } 33 | 34 | file.createSync(recursive: true); 35 | file.writeAsStringSync(_getSample(className, filename), mode: FileMode.write); 36 | print('\x1B[32m$middlewareName created successfully.\x1B[0m'); 37 | return true; 38 | } 39 | -------------------------------------------------------------------------------- /packages/dox-websocket/lib/src/adapters/websocket_redis_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_websocket/src/adapters/websocket_adapter.dart'; 2 | import 'package:dox_websocket/src/websocket_emit_event.dart'; 3 | import 'package:dox_websocket/src/websocket_emitter.dart'; 4 | import 'package:ioredis/ioredis.dart'; 5 | 6 | class WebsocketRedisAdapter implements WebsocketAdapterInterface { 7 | final String _channelKey = 'dox_websocket_redis_adapter_new_emitter'; 8 | 9 | bool _alreadyListen = false; 10 | 11 | final Redis subscriber; 12 | final Redis publisher; 13 | 14 | WebsocketRedisAdapter({ 15 | required this.subscriber, 16 | required this.publisher, 17 | }) { 18 | listen(); 19 | } 20 | 21 | @override 22 | emit(WebsocketEmitEvent event) async { 23 | publisher.publish(_channelKey, event.toString()); 24 | } 25 | 26 | Future listen() async { 27 | /// end if already listen; 28 | if (_alreadyListen) return; 29 | _alreadyListen = true; 30 | 31 | RedisSubscriber sub = await subscriber.subscribe(_channelKey); 32 | sub.onMessage = (String channel, String? message) { 33 | if (message != null && message.isNotEmpty) { 34 | WebsocketEmitEvent event = WebsocketEmitEvent.fromString(message); 35 | WebsocketEmitter.emitEventToSockets(event); 36 | } 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/database/migration.md: -------------------------------------------------------------------------------- 1 | # Migration 2 | 3 | ## Create migration 4 | 5 | === "Create migration file" 6 | 7 | ```py 8 | dox create:migration create_blog_table 9 | ``` 10 | 11 | !!! info 12 | This command will produce a migration file within the `db/migration` directory. In this file, you can make changes to your schema, including creating, updating, or deleting tables. 13 | 14 | 15 | ## Run migration 16 | 17 | === "Command" 18 | 19 | ```py 20 | dox migrate 21 | ``` 22 | 23 | !!! info 24 | Please make sure that you have created `.env` file with with below variable names to run migration. 25 | 26 | ```bash 27 | DB_HOST=localhost 28 | DB_PORT=5432 29 | DB_NAME=postgres 30 | DB_USERNAME=admin 31 | DB_PASSWORD=password 32 | ``` 33 | 34 | ## Rollback migration 35 | 36 | === "Command" 37 | 38 | ```py 39 | dox migrate:rollback 40 | ``` 41 | ## Example 42 | 43 | ```py 44 | -- up 45 | CREATE TABLE IF NOT EXISTS blog ( 46 | id serial PRIMARY KEY, 47 | user_id int NOT NULL, 48 | title VARCHAR ( 255 ) NOT NULL, 49 | slug VARCHAR ( 255 ) NOT NULL, 50 | description TEXT, 51 | deleted_at TIMESTAMP, 52 | created_at TIMESTAMP, 53 | updated_at TIMESTAMP 54 | ) 55 | 56 | -- down 57 | DROP TABLE IF EXISTS blog 58 | ``` 59 | 60 | !!! info 61 | Employ `--up` and `--down` to differentiate the migration scripts for upward and downward actions. 62 | -------------------------------------------------------------------------------- /packages/dox-auth/lib/src/auth.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: depend_on_referenced_packages 2 | import 'package:dox_auth/src/auth_engine.dart'; 3 | import 'package:dox_auth/src/interfaces.dart'; 4 | import 'package:dox_query_builder/dox_query_builder.dart'; 5 | 6 | class Auth implements IAuth { 7 | /// user data 8 | Model? _userData; 9 | 10 | AuthDriver get _driver => AuthEngine().driver; 11 | 12 | static void initialize(AuthConfig authConfig) { 13 | AuthEngine().init(authConfig); 14 | } 15 | 16 | @override 17 | Future verifyToken(IDoxRequest req) async { 18 | _userData = await _driver.verifyToken(req); 19 | } 20 | 21 | @override 22 | bool isLoggedIn() { 23 | return _userData != null; 24 | } 25 | 26 | @override 27 | T? user() { 28 | return _userData as T?; 29 | } 30 | 31 | Future attempt(Map credentials) async { 32 | _userData = await _driver.attempt(credentials); 33 | return _createToken(); 34 | } 35 | 36 | String? login(Model user) { 37 | _userData = user; 38 | return _createToken(); 39 | } 40 | 41 | @override 42 | Map? toJson() { 43 | return _userData?.toJson(); 44 | } 45 | 46 | String? _createToken() { 47 | if (_userData == null) { 48 | return null; 49 | } 50 | return _driver.createToken(_userData?.toJson()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/dox-core/lib/dox_core.dart: -------------------------------------------------------------------------------- 1 | library dox_core; 2 | 3 | export 'package:dox_annotation/dox_annotation.dart'; 4 | 5 | export 'app.dart'; 6 | 7 | /// cache 8 | export 'cache/cache.dart'; 9 | export 'cache/cache_driver_interface.dart'; 10 | export 'constants/constants.dart'; 11 | export 'env/env.dart'; 12 | 13 | /// Exceptions 14 | export 'exception/internal_error_exception.dart'; 15 | export 'exception/not_found_exception.dart'; 16 | export 'exception/query_exception.dart'; 17 | export 'exception/validation_exception.dart'; 18 | 19 | /// Request 20 | export 'http/request/dox_request.dart'; 21 | export 'http/request/form_request.dart'; 22 | export 'http/request/request_file.dart'; 23 | 24 | /// Response 25 | export 'http/response/dox_cookie.dart'; 26 | export 'http/response/dox_response.dart'; 27 | export 'http/response/serializer.dart'; 28 | 29 | /// interfaces 30 | export 'interfaces/app_config.dart'; 31 | export 'interfaces/dox_service.dart'; 32 | export 'interfaces/response_handler_interface.dart'; 33 | export 'interfaces/router.dart'; 34 | 35 | /// Tools 36 | export 'ioc/ioc_container.dart'; 37 | export 'middleware/log_middleware.dart'; 38 | 39 | /// Router 40 | export 'router/route.dart'; 41 | 42 | /// storage 43 | export 'storage/storage.dart'; 44 | export 'storage/storage_driver_interface.dart'; 45 | 46 | /// Utils 47 | export 'utils/extensions.dart'; 48 | export 'utils/hash.dart'; 49 | -------------------------------------------------------------------------------- /.github/workflows/docs.yaml: -------------------------------------------------------------------------------- 1 | name: MkDocs Deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | if: | 11 | startsWith(github.event.head_commit.message, 'update:docs') 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v2 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v2 20 | with: 21 | python-version: '3.x' 22 | 23 | - name: Install dependencies 24 | run: | 25 | pip install mkdocs 26 | pip install mkdocs-material 27 | 28 | - name: Build MkDocs site 29 | run: mkdocs build 30 | 31 | - name: Git config 32 | run: | 33 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 34 | git config --global user.name "github-actions[bot]" 35 | git config --global init.defaultBranch main 36 | 37 | - name: Deploy to GitHub Pages with Force Push 38 | run: | 39 | cd site 40 | git init 41 | git remote add origin https://github-actions[bot]:${{ secrets.PUSH_TO_PROTECTED_BRANCH }}@github.com/dartondox/dox.git 42 | git checkout -b gh-pages 43 | git add . 44 | git commit -m "Deploy MkDocs to GitHub Pages" 45 | git push --set-upstream origin gh-pages -f 46 | -------------------------------------------------------------------------------- /packages/dox-cli/lib/src/tools/generate_key.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:math'; 3 | 4 | generateKey() { 5 | const chars = 6 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 7 | final random = Random.secure(); 8 | var secret = String.fromCharCodes( 9 | Iterable.generate( 10 | 32, 11 | (_) => chars.codeUnitAt(random.nextInt(chars.length)), 12 | ), 13 | ); 14 | print("\x1B[34m$secret : Key have been updated successfully in .env!\x1B[0m"); 15 | overrideKey(secret); 16 | } 17 | 18 | void overrideKey(secret) { 19 | List content = []; 20 | final file = File('${Directory.current.path}/.env'); 21 | final contents = file.readAsStringSync(); 22 | List list = contents.split('\n'); 23 | 24 | List keys = []; 25 | 26 | for (var d in list) { 27 | if (d.toString().trim().isEmpty) { 28 | content.add(''); 29 | } else { 30 | List keyValue = d.toString().split('='); 31 | if (keyValue.length == 2) { 32 | String key = keyValue[0]; 33 | String value = keyValue[1]; 34 | if (key == 'APP_KEY') { 35 | value = secret; 36 | } 37 | keys.add(key); 38 | content.add("$key=$value"); 39 | } 40 | } 41 | } 42 | if (!keys.contains('APP_KEY')) { 43 | content = ["APP_KEY=$secret", ...content]; 44 | } 45 | file.writeAsStringSync(content.join("\n")); 46 | } 47 | -------------------------------------------------------------------------------- /packages/dox-cli/lib/src/types.dart: -------------------------------------------------------------------------------- 1 | enum CommandCategory { 2 | project('Project', 0), 3 | development('Development', 1), 4 | build('Build', 2), 5 | buildRunner('Build Runner', 3), 6 | generation('Generation', 4), 7 | database('Database', 5), 8 | system('System', 6); 9 | 10 | final String displayName; 11 | final int order; 12 | const CommandCategory(this.displayName, this.order); 13 | } 14 | 15 | typedef CommandFunction = Future Function(List args); 16 | 17 | class CommandDefinition { 18 | final String command; 19 | final String helpInfo; 20 | final String description; 21 | final CommandCategory category; 22 | final int minArgs; 23 | final int maxArgs; 24 | final CommandFunction function; 25 | final List aliases; 26 | 27 | const CommandDefinition({ 28 | required this.command, 29 | required this.helpInfo, 30 | required this.description, 31 | required this.category, 32 | required this.function, 33 | this.minArgs = 0, 34 | this.maxArgs = -1, // -1 means unlimited 35 | this.aliases = const [], 36 | }); 37 | 38 | bool matches(String inputCommand) { 39 | return command == inputCommand || aliases.contains(inputCommand); 40 | } 41 | 42 | bool validateArgs(List args) { 43 | if (args.length < minArgs) return false; 44 | if (maxArgs != -1 && args.length > maxArgs) return false; 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/dox-websocket/lib/src/websocket_emit_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_websocket/src/utils/constant.dart'; 2 | import 'package:dox_websocket/src/utils/json.dart'; 3 | 4 | class WebsocketEmitEvent { 5 | final String senderId; 6 | final String roomId; 7 | final dynamic message; 8 | final String event; 9 | final List exclude; 10 | 11 | const WebsocketEmitEvent({ 12 | required this.senderId, 13 | required this.roomId, 14 | required this.message, 15 | required this.event, 16 | required this.exclude, 17 | }); 18 | 19 | @override 20 | toString() { 21 | return JSON.stringify({ 22 | 'senderId': senderId, 23 | 'roomId': roomId, 24 | 'message': message, 25 | 'event': event, 26 | 'exclude': exclude, 27 | }); 28 | } 29 | 30 | static fromString(String json) { 31 | Map jsond = JSON.parse(json); 32 | return WebsocketEmitEvent( 33 | senderId: jsond['senderId'], 34 | roomId: jsond['roomId'], 35 | message: jsond['message'], 36 | event: jsond['event'], 37 | exclude: jsond['exclude'], 38 | ); 39 | } 40 | 41 | String toPayload() { 42 | return JSON.stringify({ 43 | WEB_SOCKET_EVENT_KEY: event, 44 | WEB_SOCKET_MESSAGE_KEY: message, 45 | WEB_SOCKET_SENDER_KEY: senderId, 46 | WEB_SOCKET_ROOM_KEY: roomId, 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/controllers/blog.controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/app/http/requests/blog.request.dart'; 2 | import 'package:dox_app/app/http/services/blog.service.dart'; 3 | import 'package:dox_core/dox_core.dart'; 4 | 5 | class BlogController { 6 | /// GET /blogs 7 | Future index(DoxRequest req) async { 8 | req.validate( 9 | { 10 | 'page': 'required|integer', 11 | 'limit': 'required|integer', 12 | }, 13 | ); 14 | bool withTrashed = req.input('with_trashed').toString() == '1'; 15 | return BlogService().listing( 16 | req.input('limit'), 17 | req.input('page'), 18 | withTrashed: withTrashed, 19 | ); 20 | } 21 | 22 | /// POST /blogs 23 | Future store(BlogRequest req) async { 24 | return BlogService().create(req); 25 | } 26 | 27 | /// GET /blogs/{id} 28 | Future show(DoxRequest req, String id) async { 29 | return BlogService().findById(id); 30 | } 31 | 32 | /// PUT|PATCH /blogs/{id} 33 | Future update(BlogRequest req, String id) async { 34 | return BlogService().update(req, id); 35 | } 36 | 37 | /// DELETE /blogs/{id} 38 | Future destroy(DoxRequest req, String id) async { 39 | await BlogService().delete(id); 40 | return { 41 | 'status': 'success', 42 | 'message': 'Blog deleted successfully', 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/dox-app/lib/routes/web.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/app/http/controllers/web.controller.dart'; 2 | import 'package:dox_core/dox_core.dart'; 3 | 4 | class WebRouter extends Router { 5 | @override 6 | List get middleware => []; 7 | 8 | @override 9 | void register() { 10 | WebController webController = WebController(); 11 | Route.get('/redis', webController.testRedis); 12 | 13 | Route.get('/ping', (DoxRequest req) async { 14 | return 'pong'; 15 | }); 16 | 17 | Route.get('/download', (DoxRequest req) async { 18 | try { 19 | DownloadableFile file = await Storage() 20 | .download('/images/3d975270-2bbb-11ee-9f99-ddd56a1df7a6.jpeg'); 21 | return response(file); 22 | } catch (error) { 23 | return error.toString(); 24 | } 25 | }); 26 | 27 | Route.delete('/image', (DoxRequest req) async { 28 | try { 29 | print(req.input('image')); 30 | await Storage().delete(req.input('image')); 31 | return 'success'; 32 | } catch (error) { 33 | return error.toString(); 34 | } 35 | }); 36 | 37 | Route.delete('/ping', (DoxRequest req) async { 38 | return await Cache().flush(); 39 | }); 40 | 41 | Route.post('/ping', (DoxRequest req) async { 42 | RequestFile file = req.input('image'); 43 | return Storage().put('images', await file.bytes); 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/relationships/has_one.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | /// one to one relationship query 4 | M? hasOne( 5 | List> list, 6 | Model Function() model, { 7 | String? foreignKey, 8 | String? localKey, 9 | dynamic onQuery, 10 | }) { 11 | if (list.isEmpty) return null; 12 | 13 | Model owner = list.first; 14 | localKey = localKey ?? owner.primaryKey; 15 | foreignKey = foreignKey ?? "${owner.tableName}_id"; 16 | 17 | List ids = list.map((Model i) { 18 | Map map = i.toMap(); 19 | return map[localKey].toString(); 20 | }).toList(); 21 | 22 | Model m = model().debug(owner.shouldDebug); 23 | 24 | m.select('*, $foreignKey as _owner_id').whereIn(foreignKey, ids); 25 | 26 | if (onQuery != null) { 27 | m = onQuery(m); 28 | } 29 | return m as M; 30 | } 31 | 32 | /// get result of one to one relationship query 33 | Future> getHasOne(dynamic q, List> list) async { 34 | if (q == null) return {}; // coverage:ignore-line 35 | List results = await q.get(); 36 | 37 | Map ret = {}; 38 | 39 | /// filter matched values with local id value 40 | for (M r in results) { 41 | Map map = (r as Model).toMap(original: true); 42 | String ownerId = map['_owner_id'].toString(); 43 | ret[ownerId] = r; 44 | } 45 | 46 | return ret; 47 | } 48 | -------------------------------------------------------------------------------- /docs/assets/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --md-primary-fg-color: #186bad; 3 | --md-primary-fg-color--light: #186bad; 4 | --md-primary-fg-color--dark: #186bad; 5 | } 6 | 7 | .md-typeset table:not([class]) code { 8 | font-size: .65rem; 9 | } 10 | 11 | .md-typeset table:not([class]) td, 12 | .md-typeset table:not([class]) th { 13 | padding: 0.5em 1.25em; 14 | } 15 | 16 | .md-typeset table:not([class]) td:not(:last-child), 17 | .md-typeset table:not([class]) th:not(:last-child) { 18 | border-right: 0.05rem solid var(--md-typeset-table-color); 19 | } 20 | 21 | .md-tabs { 22 | background-color: #025596; 23 | } 24 | 25 | [dir=ltr] .md-header__title { 26 | margin-left: 0px; 27 | } 28 | 29 | .highlight .n:first-child { 30 | color: #526cfe !important; 31 | } 32 | 33 | body[data-md-color-scheme="slate"] .highlight .kd { 34 | color: #C792E9 !important; 35 | } 36 | 37 | body[data-md-color-scheme="default"] .highlight .kd { 38 | color: #9212e4 !important; 39 | } 40 | 41 | body[data-md-color-scheme="slate"] .highlight .n { 42 | color: #8acbff; 43 | } 44 | 45 | body[data-md-color-scheme="default"] .highlight .n { 46 | color: #025596; 47 | } 48 | 49 | body[data-md-color-scheme="default"] p code { 50 | font-weight: bold !important; 51 | color: #176bad !important; 52 | background: #d5d5d5 !important; 53 | } 54 | 55 | body[data-md-color-scheme="slate"] p code { 56 | font-weight: bold !important; 57 | color: #176bad !important; 58 | } -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/controllers/auth.controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/app/http/serializers/user.serializer.dart'; 2 | import 'package:dox_app/app/models/user/user.model.dart'; 3 | import 'package:dox_auth/dox_auth.dart'; 4 | import 'package:dox_core/dox_core.dart'; 5 | import 'package:dox_core/utils/logger.dart'; 6 | 7 | class AuthController { 8 | Future login(DoxRequest req) async { 9 | Map credentials = req.only(['email', 'password']); 10 | 11 | Auth auth = Auth(); 12 | String? token = await auth.attempt(credentials); 13 | User? user = auth.user(); 14 | 15 | if (token != null) { 16 | return response({ 17 | 'success': true, 18 | 'token': token, 19 | 'user': user, 20 | }).header('Authorization', token); 21 | } 22 | 23 | return { 24 | 'success': false, 25 | }; 26 | } 27 | 28 | Future register(DoxRequest req) async { 29 | User user = User(); 30 | user.name = 'AJ'; 31 | user.email = 'aj@mail.com'; 32 | user.password = Hash.make('password'); 33 | await user.save(); 34 | return user; 35 | } 36 | 37 | Future user(DoxRequest req) async { 38 | IAuth? auth = req.auth; 39 | if (auth?.isLoggedIn() == true) { 40 | Logger.info('${auth?.user()?.name} is logged in'); 41 | return UserSerializer(auth?.user()); 42 | } 43 | return UnAuthorizedException(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /docs/others/releases/query-builder-release.md: -------------------------------------------------------------------------------- 1 | # Query builder 2 | 3 | ### v1.1.15 4 | 5 | - Bug fixed on toMap 6 | 7 | ### v1.1.14 8 | 9 | - Bug fixed on eager/preload data missing in `toMap` response. 10 | - Bug fixed on `deleted_at` column conflict. 11 | - Support for `withTrash` chain on any query builder function. 12 | 13 | ### v1.1.13 14 | 15 | - Update readme 16 | - bug fixed on count() with order by 17 | 18 | ### v1.1.12 19 | 20 | - Add support for paginate() function 21 | - Add support for query printer, file printer, consoler printer and pretty printer. 22 | 23 | ### v1.1.11 24 | 25 | - Add missing types on function and arguments 26 | - Bug fixed on count, custom query 27 | 28 | ### v1.1.10 29 | 30 | - Remove hidden fields on toJson 31 | 32 | ### v1.1.9 33 | 34 | - Bug fixed on jsonEncode(Model) 35 | 36 | ### v1.1.8 37 | 38 | - Update readme 39 | 40 | ### v1.1.7 41 | 42 | - Add option in `toMap()` to remove hidden fields 43 | 44 | ### v1.1.6 45 | 46 | - Create own annotation and builder 47 | - Added belongsTo support 48 | - Added hasOne support 49 | - Added hasMany support 50 | - Added manyToMany support 51 | - Added eager loading support 52 | 53 | ### v1.0.11 54 | 55 | - Bug fixed id null on save 56 | 57 | ### v1.0.10 58 | 59 | - Bug fixed on debug option in model 60 | - Bug fixed on debug query param missing 61 | - Support Postgres Pool 62 | - Support hidden fields in model 63 | 64 | ### v1.0.1 65 | 66 | - Update documentation 67 | 68 | ### v1.0.0 69 | 70 | - Initial release 71 | -------------------------------------------------------------------------------- /packages/dox-core/lib/server/dox_server.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | import 'package:dox_core/http/http_request_handler.dart'; 5 | 6 | class DoxServer { 7 | /// register singleton 8 | static final DoxServer _singleton = DoxServer._internal(); 9 | factory DoxServer() => _singleton; 10 | DoxServer._internal(); 11 | 12 | /// httpServer dart:io 13 | late HttpServer httpServer; 14 | 15 | /// set responseHandler 16 | ResponseHandlerInterface? responseHandler; 17 | 18 | /// listen the request 19 | /// ``` 20 | /// DoxServer().listen(3000); 21 | /// ``` 22 | Future listen(int port, 23 | {Function? onError, int? isolateId}) async { 24 | HttpServer server = await HttpServer.bind( 25 | InternetAddress.anyIPv6, 26 | port, 27 | shared: true, 28 | ); 29 | server.listen( 30 | (HttpRequest req) { 31 | httpRequestHandler(req); 32 | }, 33 | onError: onError ?? (dynamic error) => print(error), 34 | ); 35 | httpServer = server; 36 | return server; 37 | } 38 | 39 | /// close http server 40 | /// ``` 41 | /// server.close(); 42 | /// ``` 43 | Future close({bool force = false}) async { 44 | await httpServer.close(force: force); 45 | } 46 | 47 | /// set response handler 48 | /// ``` 49 | /// server.setResponseHandler(Handler()); 50 | /// ``` 51 | void setResponseHandler(ResponseHandlerInterface? handler) { 52 | responseHandler = handler; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/dox-core/test/unit/utils_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/utils/utils.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('Utils |', () { 6 | group('sanitizeRoutePath |', () { 7 | test('normal', () { 8 | String route = sanitizeRoutePath('admin'); 9 | expect(route, '/admin'); 10 | }); 11 | 12 | test('with slash', () { 13 | String route = sanitizeRoutePath('/admin'); 14 | expect(route, '/admin'); 15 | }); 16 | 17 | test('with double slash', () { 18 | String route = sanitizeRoutePath('//admin'); 19 | expect(route, '/admin'); 20 | }); 21 | 22 | test('with double slash on both side', () { 23 | String route = sanitizeRoutePath('//admin//'); 24 | expect(route, '/admin'); 25 | }); 26 | 27 | test('with single slash on both side', () { 28 | String route = sanitizeRoutePath('/admin/'); 29 | expect(route, '/admin'); 30 | }); 31 | 32 | test('long route', () { 33 | String route = sanitizeRoutePath('/admin//something'); 34 | expect(route, '/admin/something'); 35 | }); 36 | 37 | test('with param', () { 38 | String route = sanitizeRoutePath('/admin//{name}'); 39 | expect(route, '/admin/{name}'); 40 | }); 41 | 42 | test('with 2 param', () { 43 | String route = sanitizeRoutePath('/admin//{name}//{id}'); 44 | expect(route, '/admin/{name}/{id}'); 45 | }); 46 | }); 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /docs/database/model/serializer.md: -------------------------------------------------------------------------------- 1 | # Serializer 2 | 3 | Serializers are employed to transform the controller's response before sending it as an HTTP response. 4 | 5 | ## Create a serializer 6 | 7 | ```bash 8 | dox create:serializer Blog 9 | ``` 10 | 11 | ## Usage 12 | 13 | === "Serializer" 14 | 15 | ```dart 16 | class BlogSerializer extends Serializer { 17 | BlogSerializer(super.data); 18 | 19 | @override 20 | Map convert(Blog m) { 21 | return { 22 | 'uid': m.id, 23 | 'title_en' : m.title, 24 | }; 25 | } 26 | } 27 | ``` 28 | 29 | === "Controller" 30 | 31 | ```dart 32 | class BlogController { 33 | getAllBlogs(DoxRequest req) async { 34 | List blogs = await Blog().all(); 35 | 36 | /// you can pass as list of data 37 | return BlogSerializer(blogs); 38 | } 39 | 40 | findBlog(DoxRequest req, id) async { 41 | Blog blog = await Blog().find(id); 42 | 43 | /// or you can also pass as single data 44 | return BlogSerializer(blog); 45 | } 46 | } 47 | ``` 48 | 49 | === "Response" 50 | 51 | ```json 52 | { 53 | "uid" : "1", 54 | "title_en": "This is title" 55 | } 56 | ``` 57 | 58 | !!! info 59 | You can pass as **list of data** or **single data** into the serializer. Just make sure that you have injected your model type in serializer `Serializer`. 60 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/requirements/config/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/cache/drivers/file/file_cache_driver.dart'; 2 | import 'package:dox_core/dox_core.dart'; 3 | import 'package:dox_core/utils/logger.dart'; 4 | 5 | import '../handler.dart'; 6 | import '../middleware/custom_middleware.dart'; 7 | import '../requests/blog_request.dart'; 8 | import 'api_router.dart'; 9 | 10 | AppConfig config = AppConfig( 11 | /// application key 12 | appKey: '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt', 13 | 14 | /// application server port 15 | serverPort: 50010, 16 | 17 | /// total multi-thread isolate to run 18 | totalIsolate: 1, 19 | 20 | // cors configuration 21 | cors: CORSConfig( 22 | enabled: false, 23 | origin: '*', 24 | methods: ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'], 25 | credentials: true, 26 | ), 27 | 28 | /// response handler 29 | responseHandler: ResponseHandler(), 30 | 31 | /// global middleware 32 | globalMiddleware: [customMiddleware], 33 | 34 | /// form requests 35 | formRequests: { 36 | BlogRequest: () => BlogRequest(), 37 | }, 38 | 39 | /// routers 40 | routers: [ 41 | ApiRouter(), 42 | ], 43 | 44 | /// error handler 45 | errorHandler: (Object? error, StackTrace stackTrace) { 46 | Logger.danger(error); 47 | }, 48 | 49 | /// cache driver configuration 50 | cache: CacheConfig( 51 | drivers: { 52 | 'file': FileCacheDriver(), 53 | }, 54 | ), 55 | ); 56 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/schema.dart: -------------------------------------------------------------------------------- 1 | import '/dox_query_builder.dart'; 2 | 3 | class Schema { 4 | /// create table with schema 5 | /// 6 | /// ``` 7 | /// await Schema.create('table', (Table table) { 8 | /// table.id(); 9 | /// table.string('title').nullable(); 10 | /// table.string('status').withDefault('active'); 11 | /// table.text('body'); 12 | /// table.softDeletes(); 13 | /// table.timestamps(); 14 | /// }); 15 | /// ``` 16 | static Future create(String tableName, Function(Table) callback) async { 17 | Table table = Table().table(tableName); 18 | callback(table); 19 | await table.create(); 20 | } 21 | 22 | /// update table with schema 23 | /// 24 | /// ``` 25 | /// await Schema.table('table', (Table table) { 26 | /// table.id(); 27 | /// table.string('title').nullable(); 28 | /// table.string('status').withDefault('active'); 29 | /// table.text('body'); 30 | /// table.softDeletes(); 31 | /// table.timestamps(); 32 | /// }); 33 | /// ``` 34 | static Future table(String tableName, Function(Table) callback) async { 35 | Table table = Table().table(tableName); 36 | callback(table); 37 | await table.update(); 38 | } 39 | 40 | /// drop table 41 | /// 42 | /// ``` 43 | /// await Schema.drop('table'); 44 | /// ``` 45 | static Future drop(String tableName) async { 46 | await SqlQueryBuilder() 47 | .dbDriver 48 | .query("DROP TABLE IF EXISTS $tableName RESTRICT"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/dox-core/lib/http/http_cors_handler.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | 5 | void httpCorsHandler(bool? enabled, HttpRequest req) { 6 | CORSConfig cors = Dox().config.cors; 7 | if (enabled ?? cors.enabled) { 8 | Map headers = { 9 | HttpHeaders.accessControlAllowOriginHeader: cors.origin, 10 | HttpHeaders.accessControlAllowMethodsHeader: cors.methods, 11 | HttpHeaders.accessControlAllowHeadersHeader: cors.headers, 12 | HttpHeaders.accessControlExposeHeadersHeader: cors.exposeHeaders, 13 | HttpHeaders.accessControlAllowCredentialsHeader: cors.credentials, 14 | HttpHeaders.accessControlMaxAgeHeader: cors.maxAge, 15 | }; 16 | 17 | headers.forEach((String key, dynamic value) { 18 | _setCorsValue(req.response, key, value); 19 | }); 20 | } 21 | } 22 | 23 | // set cors in header 24 | void _setCorsValue(HttpResponse res, String key, dynamic data) { 25 | if (data == null) { 26 | return; 27 | } 28 | 29 | /// when data is list of string, eg. ['GET', 'POST'] 30 | if (data is List && data.isNotEmpty) { 31 | res.headers.add(key, data.join(',')); 32 | return; 33 | } 34 | 35 | /// when data is string, eg. 'GET' 36 | if (data is String && data.isNotEmpty) { 37 | res.headers.add(key, data); 38 | return; 39 | } 40 | 41 | /// when data is other type and has value, just convert to string 42 | if (data != null) { 43 | res.headers.add(key, data.toString()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/dox-auth/example/example.md: -------------------------------------------------------------------------------- 1 | 1. Create auth config 2 | 3 | ```dart 4 | import 'package:dox_auth/dox_auth.dart'; 5 | import 'package:dox_core/dox_core.dart'; 6 | import 'package:dox/models/user/user.model.dart'; 7 | 8 | class AuthConfig extends AuthConfigInterface { 9 | @override 10 | String get defaultGuard => 'web'; 11 | 12 | @override 13 | Map get guards => { 14 | 'web': Guard( 15 | driver: JwtDriver( 16 | secret: SecretKey(Env.get('APP_KEY')), 17 | ), 18 | provider: Provider( 19 | model: () => User(), 20 | ), 21 | ), 22 | }; 23 | } 24 | ``` 25 | 26 | 2. Modify `bin/server.dart` to add auth config 27 | 28 | ```dart 29 | Dox dox = Dox(); 30 | await dox.initialize(config); 31 | dox.setAuthConfig(AuthConfig()); 32 | ``` 33 | 34 | 3. Attempt Login 35 | 36 | ```dart 37 | Map credentials = req.only(['email', 'password']); 38 | 39 | Auth auth = Auth(); 40 | String? token = await auth.attempt(credentials); 41 | User? user = auth.user(); 42 | ``` 43 | 44 | 4. Register `doxAuthMiddleware` in route 45 | 46 | ```dart 47 | Route.get('/auth/user', [doxAuthMiddleware, authController.user]); 48 | ``` 49 | 50 | 5. Verify Logged In or Fetch User information 51 | 52 | ```dart 53 | Future fetchUser(DoxRequest req) async { 54 | Auth? auth = req.auth(); 55 | if (auth?.isLoggedIn() == true) { 56 | return auth?.user(); 57 | } 58 | throw UnAuthorizedException(); 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/isolate_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:dox_core/isolate/dox_isolate.dart'; 3 | import 'package:http/http.dart' as http; 4 | import 'package:test/test.dart'; 5 | 6 | import 'requirements/config/api_router.dart'; 7 | import 'requirements/handler.dart'; 8 | 9 | AppConfig isolateConfig = AppConfig( 10 | /// application key 11 | appKey: '4HyiSrq4N5Nfg6bOadIhbFEI8zbUkpxt', 12 | 13 | /// application server port 14 | serverPort: 50010, 15 | 16 | /// total multi-thread isolate to run 17 | totalIsolate: 1, 18 | 19 | /// response handler 20 | responseHandler: ResponseHandler(), 21 | 22 | /// routers 23 | routers: [ 24 | ApiRouter(), 25 | ], 26 | ); 27 | 28 | String baseUrl = 'http://localhost:${isolateConfig.serverPort}'; 29 | 30 | class ExampleService implements DoxService { 31 | @override 32 | void setup() {} 33 | } 34 | 35 | void main() { 36 | group('Isolate', () { 37 | setUpAll(() async { 38 | Dox().initialize(isolateConfig); 39 | Dox().addService(ExampleService()); 40 | Dox().totalIsolate(5); 41 | await Dox().startServer(); 42 | }); 43 | 44 | tearDownAll(() async { 45 | DoxIsolate().killAll(); 46 | }); 47 | 48 | test('test', () async { 49 | Uri url = Uri.parse('$baseUrl/api/ping'); 50 | http.Response res = await http.get(url); 51 | 52 | expect(res.statusCode, 200); 53 | expect(res.body, 'pong'); 54 | expect(DoxIsolate().isolates.length, 5 - 1); 55 | }); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/utils/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | // coverage:ignore-file 4 | class Logger { 5 | void log(String query, [dynamic params]) { 6 | query = query.replaceAll(RegExp(r'@\w+'), '?'); 7 | params = _parseParams(params); 8 | QueryPrinter printer = SqlQueryBuilder().printer; 9 | printer.log(query, params); 10 | } 11 | 12 | List _parseParams(dynamic params) { 13 | List list = []; 14 | if (params != null && (params as Map).isNotEmpty) { 15 | params.forEach((String key, dynamic value) { 16 | list.add(value.toString()); 17 | }); 18 | } 19 | return list; 20 | } 21 | 22 | /// log the text using info color (lightblue) 23 | /// ``` 24 | /// Logger.info('using dox is fun'); 25 | /// ``` 26 | static void info(dynamic text) { 27 | print('\x1B[36m$text\x1B[0m'); 28 | } 29 | 30 | /// log the text using warn color (yellow) 31 | /// ``` 32 | /// Logger.warn('careful!'); 33 | /// 34 | static void warn(dynamic text) { 35 | print('\x1B[34m$text\x1B[0m'); 36 | } 37 | 38 | /// log the text using success color (green) 39 | /// ``` 40 | /// Logger.success('success'); 41 | /// 42 | static void success(dynamic text) { 43 | print('\x1B[32m$text\x1B[0m'); 44 | } 45 | 46 | /// log the text using danger color (red) 47 | /// ``` 48 | /// Logger.danger('failed'); 49 | /// 50 | static void danger(dynamic text) { 51 | print('\x1B[31m$text\x1B[0m'); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/dox-core/test/utils/file_upload.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:http/http.dart'; 5 | import 'package:http_parser/http_parser.dart'; 6 | 7 | Future uploadImage(String apiUrl, File imageFile) async { 8 | try { 9 | // Open a byte stream 10 | ByteStream stream = ByteStream(imageFile.openRead()); 11 | // Get the length of the file 12 | int length = await imageFile.length(); 13 | 14 | // Create the multipart request, add file and headers 15 | MultipartRequest request = MultipartRequest('POST', Uri.parse(apiUrl)); 16 | String filename = imageFile.path.split('/').last; 17 | String ext = filename.split('.').last; 18 | MultipartFile multipartFile = MultipartFile( 19 | 'image', 20 | stream, 21 | length, 22 | contentType: MediaType('image', ext), 23 | filename: filename, 24 | ); 25 | 26 | request.fields['foo'] = 'bar'; 27 | request.files.add(multipartFile); 28 | 29 | // Optional: If your API requires any headers, you can set them here 30 | // request.headers['Authorization'] = 'Bearer YOUR_ACCESS_TOKEN'; 31 | 32 | // Send the request and wait for the response 33 | StreamedResponse response = await request.send(); 34 | 35 | // Check if the request was successful (status code 200-299) 36 | if (response.statusCode >= 200 && response.statusCode < 300) { 37 | return await response.stream.transform(utf8.decoder).join(); 38 | } else { 39 | return null; 40 | } 41 | } catch (e) { 42 | return null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/dox-core/test/integration/date_response_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | import 'package:http/http.dart' as http; 5 | import 'package:test/test.dart'; 6 | 7 | import '../utils/start_http_server.dart'; 8 | import 'requirements/config/app.dart'; 9 | 10 | String baseUrl = 'http://localhost:${config.serverPort}'; 11 | 12 | void main() { 13 | group('Http |', () { 14 | setUpAll(() async { 15 | await startHttpServer(config); 16 | }); 17 | 18 | tearDownAll(() async { 19 | await Dox().server.close(); 20 | }); 21 | 22 | test('date response', () async { 23 | Route.get('/date/response', (DoxRequest req, String name) { 24 | return DateTime(2023); 25 | }); 26 | 27 | /// response DateTime 28 | Uri url = Uri.parse('$baseUrl/date/response'); 29 | http.Response res = await http.get(url); 30 | expect(res.statusCode, 200); 31 | expect(res.body, '2023-01-01T00:00:00.000'); 32 | }); 33 | 34 | test('date response', () async { 35 | Route.get('/date/response/json', (DoxRequest req, String name) { 36 | return { 37 | 'date': DateTime(2023), 38 | }; 39 | }); 40 | 41 | /// response DateTime in json 42 | Uri url2 = Uri.parse('$baseUrl/date/response/json'); 43 | http.Response res2 = await http.get(url2); 44 | Map data = jsonDecode(res2.body); 45 | expect(res2.statusCode, 200); 46 | expect(data['date'], '2023-01-01T00:00:00.000'); 47 | }); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /packages/dox-query-builder/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | linter: 19 | rules: 20 | - avoid_empty_else 21 | - always_declare_return_types 22 | - leading_newlines_in_multiline_strings 23 | - unnecessary_brace_in_string_interps 24 | - avoid_redundant_argument_values 25 | - slash_for_doc_comments 26 | - avoid_final_parameters 27 | - prefer_const_declarations 28 | - unnecessary_overrides 29 | - unawaited_futures 30 | - unnecessary_final 31 | - unnecessary_const 32 | - always_specify_types 33 | 34 | # analyzer: 35 | # exclude: 36 | # - test/** 37 | 38 | # For more information about the core and recommended set of lints, see 39 | # https://dart.dev/go/core-lints 40 | 41 | # For additional information about configuring this file, see 42 | # https://dart.dev/guides/language/analysis-options 43 | -------------------------------------------------------------------------------- /packages/dox-app/lib/app/http/services/blog.service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_app/app/http/requests/blog.request.dart'; 2 | import 'package:dox_app/app/models/blog/blog.model.dart'; 3 | import 'package:dox_app/app/models/category/category.model.dart'; 4 | import 'package:dox_query_builder/dox_query_builder.dart'; 5 | 6 | class BlogService { 7 | BlogService(); 8 | 9 | Future listing(dynamic limit, dynamic page, 10 | {bool withTrashed = false}) async { 11 | int limitValue = int.parse(limit.toString()); 12 | int pageValue = int.parse(page.toString()); 13 | int offset = (pageValue - 1) * limitValue; 14 | return Blog() 15 | .withTrash(withTrashed) 16 | .limit(limitValue) 17 | .offset(offset) 18 | .orderBy('id') 19 | .paginate( 20 | currentPage: int.parse(page.toString()), 21 | perPage: int.parse(limit.toString()), 22 | ); 23 | } 24 | 25 | Future findById(dynamic id) async { 26 | Category? cat = await Category().preload('blogs').find(1); 27 | return cat; 28 | } 29 | 30 | Future delete(dynamic id) async { 31 | return Blog().where('id', id).delete(); 32 | } 33 | 34 | Future create(BlogRequest req) async { 35 | Blog blog = Blog(); 36 | blog.title = req.title; 37 | blog.description = req.description; 38 | await blog.save(); 39 | return blog; 40 | } 41 | 42 | Future update(BlogRequest req, dynamic id) async { 43 | Blog? blog = await Blog().find(id); 44 | blog?.title = req.title; 45 | blog?.description = req.description; 46 | await blog?.save(); 47 | return blog; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/dox-core/lib/http/response/dox_cookie.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_core/app.dart'; 4 | import 'package:dox_core/utils/aes_encryptor.dart'; 5 | 6 | class DoxCookie { 7 | final String key; 8 | final String value; 9 | final bool encrypt; 10 | 11 | String? path; 12 | String? domain; 13 | bool httpOnly; 14 | bool secure; 15 | DateTime? expires; 16 | Duration maxAge; 17 | 18 | DoxCookie( 19 | this.key, 20 | this.value, { 21 | this.encrypt = true, 22 | this.domain, 23 | this.path, 24 | this.httpOnly = false, 25 | this.secure = false, 26 | this.expires, 27 | this.maxAge = const Duration(hours: 1), 28 | }); 29 | 30 | /// set expire the cookie 31 | /// ``` 32 | /// cookie.expire(); 33 | /// ``` 34 | String expire() { 35 | Cookie cookie = Cookie(key, value); 36 | cookie.maxAge = Duration(milliseconds: -1).inMicroseconds; 37 | cookie.path = path; 38 | cookie.domain = domain; 39 | cookie.httpOnly = httpOnly; 40 | cookie.secure = secure; 41 | cookie.expires = expires; 42 | return cookie.toString(); 43 | } 44 | 45 | /// get the cookie to response in http response 46 | /// ``` 47 | /// cookie.get(); 48 | /// ``` 49 | String get() { 50 | String val = 51 | encrypt ? AESEncryptor.encode(value, Dox().config.appKey) : value; 52 | 53 | Cookie cookie = Cookie(key, val); 54 | cookie.maxAge = maxAge.inMilliseconds; 55 | cookie.path = path; 56 | cookie.domain = domain; 57 | cookie.httpOnly = httpOnly; 58 | cookie.secure = secure; 59 | cookie.expires = expires; 60 | return cookie.toString(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/dox-app/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | linter: 19 | rules: 20 | - camel_case_types 21 | - avoid_empty_else 22 | - always_specify_types 23 | - always_declare_return_types 24 | - leading_newlines_in_multiline_strings 25 | - unnecessary_brace_in_string_interps 26 | - avoid_redundant_argument_values 27 | - slash_for_doc_comments 28 | - avoid_final_parameters 29 | - prefer_const_declarations 30 | - unnecessary_overrides 31 | - unawaited_futures 32 | - unnecessary_final 33 | - unnecessary_const 34 | - prefer_single_quotes 35 | 36 | # analyzer: 37 | # exclude: 38 | # - path/to/excluded/files/** 39 | 40 | # For more information about the core and recommended set of lints, see 41 | # https://dart.dev/go/core-lints 42 | 43 | # For additional information about configuring this file, see 44 | # https://dart.dev/guides/language/analysis-options 45 | -------------------------------------------------------------------------------- /packages/dox-auth/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | linter: 19 | rules: 20 | - camel_case_types 21 | - avoid_empty_else 22 | - always_specify_types 23 | - always_declare_return_types 24 | - leading_newlines_in_multiline_strings 25 | - unnecessary_brace_in_string_interps 26 | - avoid_redundant_argument_values 27 | - slash_for_doc_comments 28 | - avoid_final_parameters 29 | - prefer_const_declarations 30 | - unnecessary_overrides 31 | - unawaited_futures 32 | - unnecessary_final 33 | - unnecessary_const 34 | - prefer_single_quotes 35 | 36 | # analyzer: 37 | # exclude: 38 | # - path/to/excluded/files/** 39 | 40 | # For more information about the core and recommended set of lints, see 41 | # https://dart.dev/go/core-lints 42 | 43 | # For additional information about configuring this file, see 44 | # https://dart.dev/guides/language/analysis-options 45 | -------------------------------------------------------------------------------- /packages/dox-core/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | linter: 19 | rules: 20 | - camel_case_types 21 | - avoid_empty_else 22 | - always_specify_types 23 | - always_declare_return_types 24 | - leading_newlines_in_multiline_strings 25 | - unnecessary_brace_in_string_interps 26 | - avoid_redundant_argument_values 27 | - slash_for_doc_comments 28 | - avoid_final_parameters 29 | - prefer_const_declarations 30 | - unnecessary_overrides 31 | - unawaited_futures 32 | - unnecessary_final 33 | - unnecessary_const 34 | - prefer_single_quotes 35 | 36 | # analyzer: 37 | # exclude: 38 | # - path/to/excluded/files/** 39 | 40 | # For more information about the core and recommended set of lints, see 41 | # https://dart.dev/go/core-lints 42 | 43 | # For additional information about configuring this file, see 44 | # https://dart.dev/guides/language/analysis-options 45 | -------------------------------------------------------------------------------- /packages/dox-cli/lib/src/tools/create_migration.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox/src/utils/utils.dart'; 4 | 5 | class MigrationFile { 6 | final String name; 7 | 8 | MigrationFile(this.name, String fileType) { 9 | String filename = _getFileName(name); 10 | String type = fileType.isEmpty ? 'sql' : fileType.replaceAll('--', ''); 11 | 12 | if (type == 'sql') { 13 | _createSqlFile(filename); 14 | } 15 | 16 | print('\x1B[32m$filename migration created successfully.\x1B[0m'); 17 | } 18 | 19 | void _createSqlFile(String filename) { 20 | Directory migrationDirectory = _getMigrationDirectory(); 21 | File file = File('${migrationDirectory.path}/$filename.sql'); 22 | file.createSync(recursive: true); 23 | file.writeAsStringSync(_sampleSql); 24 | } 25 | 26 | String _getFileName(String name) { 27 | DateTime now = DateTime.now(); 28 | String uuid = 29 | '${now.year}_${_formatNumber(now.month)}_${_formatNumber(now.day)}_${_formatNumber(now.hour)}${_formatNumber(now.minute)}${_formatNumber(now.second)}${now.microsecond}'; 30 | return pascalToSnake('${uuid}_$name'.replaceAll(RegExp(r'[^\w]'), '')); 31 | } 32 | 33 | String _formatNumber(int number) { 34 | return number.toString().padLeft(2, '0'); 35 | } 36 | 37 | /// get migration directory `db/migration` 38 | Directory _getMigrationDirectory() { 39 | return Directory('${Directory.current.path}/db/migration'); 40 | } 41 | } 42 | 43 | String _sampleSql = ''' 44 | -- up 45 | -- Write your up query here. Do not remove `-- up` comment. 46 | 47 | -- down 48 | -- Write your down query here. Do not remove `-- down` comment. 49 | '''; 50 | -------------------------------------------------------------------------------- /packages/dox-migration/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | linter: 19 | rules: 20 | - camel_case_types 21 | - avoid_empty_else 22 | - always_specify_types 23 | - always_declare_return_types 24 | - leading_newlines_in_multiline_strings 25 | - unnecessary_brace_in_string_interps 26 | - avoid_redundant_argument_values 27 | - slash_for_doc_comments 28 | - avoid_final_parameters 29 | - prefer_const_declarations 30 | - unnecessary_overrides 31 | - unawaited_futures 32 | - unnecessary_final 33 | - unnecessary_const 34 | - prefer_single_quotes 35 | 36 | # analyzer: 37 | # exclude: 38 | # - path/to/excluded/files/** 39 | 40 | # For more information about the core and recommended set of lints, see 41 | # https://dart.dev/go/core-lints 42 | 43 | # For additional information about configuring this file, see 44 | # https://dart.dev/guides/language/analysis-options 45 | -------------------------------------------------------------------------------- /packages/dox-core/lib/isolate/dox_isolate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:isolate'; 2 | 3 | import 'package:dox_core/dox_core.dart'; 4 | import 'package:dox_core/isolate/isolate_handler.dart'; 5 | import 'package:dox_core/isolate/isolate_interfaces.dart'; 6 | 7 | class DoxIsolate { 8 | /// singleton 9 | static final DoxIsolate _singleton = DoxIsolate._internal(); 10 | factory DoxIsolate() => _singleton; 11 | DoxIsolate._internal(); 12 | 13 | final Map _isolates = {}; 14 | final Map _receivePorts = {}; 15 | final Map _sendPorts = {}; 16 | 17 | /// get list of running isolates 18 | Map get isolates => _isolates; 19 | Map get sendPorts => _sendPorts; 20 | 21 | /// create threads 22 | /// ``` 23 | /// await DoxIsolate().spawn(3) 24 | /// ``` 25 | Future spawn(int count) async { 26 | for (int i = 1; i < count; i++) { 27 | await _spawn(i + 1); 28 | } 29 | } 30 | 31 | /// kill all the isolate 32 | void killAll() { 33 | _isolates.forEach((int id, Isolate isolate) { 34 | isolate.kill(); 35 | }); 36 | 37 | _receivePorts.forEach((int id, ReceivePort receivePort) { 38 | receivePort.close(); 39 | }); 40 | } 41 | 42 | /// create a thread 43 | Future _spawn(int isolateId) async { 44 | Isolate isolate = await Isolate.spawn( 45 | isolateHandler, 46 | IsolateSpawnParameter( 47 | isolateId, 48 | Dox().config, 49 | Dox().doxServices, 50 | routes: Route().routes, 51 | ), 52 | ); 53 | 54 | _isolates[isolateId] = isolate; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/dox-query-builder/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.1.0 2 | 3 | - Added support for MYSQL driver 4 | 5 | ## 2.0.0 6 | 7 | - Support postgres driver v3 8 | 9 | ## 1.1.16 10 | 11 | - Bug fixed on save 12 | - Change `newQuery` to `query()` and deprecate `newQuery` 13 | 14 | ## 1.1.15 15 | 16 | - Bug fixed on toMap 17 | 18 | ## 1.1.14 19 | 20 | - Bug fixed on eager/preload data missing in `toMap` response. 21 | - Bug fixed on `deleted_at` column conflict. 22 | - Support for `withTrash` chain on any query builder function. 23 | 24 | ## 1.1.13 25 | 26 | - Update readme 27 | - bug fixed on count() with order by 28 | 29 | ## 1.1.12 30 | 31 | - Add support for paginate() function 32 | - Add support for query printer, file printer, consoler printer and pretty printer. 33 | 34 | ## 1.1.11 35 | 36 | - Add missing types on function and arguments 37 | - Bug fixed on count, custom query 38 | 39 | ## 1.1.10 40 | 41 | - Remove hidden fields on toJson 42 | 43 | ## 1.1.9 44 | 45 | - Bug fixed on jsonEncode(Model) 46 | 47 | ## 1.1.8 48 | 49 | - Update readme 50 | 51 | ## 1.1.7 52 | 53 | - Add option in `toMap()` to remove hidden fields 54 | 55 | ## 1.1.6 56 | 57 | - Create own annotation and builder 58 | - Added belongsTo support 59 | - Added hasOne support 60 | - Added hasMany support 61 | - Added manyToMany support 62 | - Added eager loading support 63 | 64 | ## 1.0.11 65 | 66 | - Bug fixed id null on save 67 | 68 | ## 1.0.10 69 | 70 | - Bug fixed on debug option in model 71 | - Bug fixed on debug query param missing 72 | - Support Postgres Pool 73 | - Support hidden fields in model 74 | 75 | ## 1.0.1 76 | 77 | - Update documentation 78 | 79 | ## 1.0.0 80 | 81 | - Initial release 82 | -------------------------------------------------------------------------------- /packages/dox-core/lib/utils/extensions.dart: -------------------------------------------------------------------------------- 1 | extension MapExtensions on Map { 2 | /// remove parameter from map with dot 3 | /// ``` 4 | /// map.removeParam('user.info'); 5 | /// ``` 6 | Map removeParam(String keys) { 7 | dynamic value = this; 8 | List parts = keys.split('.'); 9 | List k = parts.sublist(0, parts.length - 1); 10 | 11 | Map data = value; 12 | for (String i in k) { 13 | data = data[i]; 14 | } 15 | data.remove(parts.last); 16 | return value; 17 | } 18 | 19 | /// get parameter from map with dot 20 | /// ``` 21 | /// map.getParam('user.info'); 22 | /// `` 23 | dynamic getParam(String keys) { 24 | dynamic value = this; 25 | List parts = keys.split('.'); 26 | List k = parts.sublist(0, parts.length - 1); 27 | 28 | Map data = value; 29 | for (String i in k) { 30 | if (data[i] is List) { 31 | return data[i]; 32 | } 33 | data = data[i]; 34 | } 35 | return data[parts.last]; 36 | } 37 | } 38 | 39 | extension JoinWithAnd on List { 40 | /// jon list with separator and the last item with 'and' or 'or' 41 | /// ``` 42 | /// list.joinWithAnd(); 43 | /// list.joinWithAnd(',', 'and'); 44 | /// ``` 45 | String joinWithAnd([String separator = ', ', String lastJoinText = 'and']) { 46 | List items = this; 47 | if (items.length <= 1) { 48 | return items.join(); 49 | } else { 50 | String lastItem = items.removeLast(); 51 | String joinedItems = items.join(separator); 52 | return '$joinedItems, $lastJoinText $lastItem'; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/relationships/has_many.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | /// has many relationships query 4 | M? hasMany( 5 | List> list, 6 | Model Function() model, { 7 | String? foreignKey, 8 | String? localKey, 9 | dynamic onQuery, 10 | }) { 11 | if (list.isEmpty) return null; 12 | 13 | Model owner = list.first; 14 | localKey = localKey ?? owner.primaryKey; 15 | foreignKey = foreignKey ?? "${owner.tableName}_id"; 16 | 17 | List ids = list.map((Model i) { 18 | Map map = i.toMap(); 19 | return map[localKey].toString(); 20 | }).toList(); 21 | 22 | Model m = model().debug(owner.shouldDebug); 23 | 24 | m.select('*, $foreignKey as _owner_id').whereIn(foreignKey, ids); 25 | 26 | if (onQuery != null) { 27 | m = onQuery(m); 28 | } 29 | return m as M; 30 | } 31 | 32 | /// get has many relationships result 33 | Future>> getHasMany( 34 | dynamic q, 35 | List> list, 36 | ) async { 37 | if (q == null) return >{}; // coverage:ignore-line 38 | List results = await q.get(); 39 | 40 | Map> ret = >{}; 41 | 42 | /// filter matched values with local id value 43 | for (M r in results) { 44 | Map map = (r as Model).toMap(original: true); 45 | String ownerId = map['_owner_id'].toString(); 46 | if (ret[ownerId] == null) { 47 | ret[ownerId] = []; 48 | } 49 | ret[ownerId]?.add(r); 50 | } 51 | 52 | /// to prevent result null instead return empty list 53 | for (Model i in list) { 54 | ret[i.tempIdValue.toString()] = ret[i.tempIdValue.toString()] ?? []; 55 | } 56 | 57 | return ret; 58 | } 59 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/relationships/belongs_to.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_query_builder/dox_query_builder.dart'; 2 | 3 | /// belongs to relationship query 4 | M? belongsTo( 5 | List> list, 6 | Model Function() model, { 7 | String? foreignKey, 8 | String? localKey, 9 | dynamic onQuery, 10 | }) { 11 | if (list.isEmpty) return null; 12 | 13 | Model foreign = list.first; 14 | Model owner = model().debug(foreign.shouldDebug); 15 | localKey = localKey ?? owner.primaryKey; 16 | foreignKey = foreignKey ?? "${owner.tableName}_id"; 17 | 18 | List ids = list.map((dynamic i) { 19 | Map map = i.toMap(); 20 | return map[foreignKey].toString(); 21 | }).toList(); 22 | 23 | owner 24 | .select([ 25 | '${owner.tableName}.*', 26 | '${foreign.tableName}.${foreign.primaryKey} as _foreign_id' 27 | ]) 28 | .leftJoin( 29 | foreign.tableName, 30 | '${foreign.tableName}.$foreignKey', 31 | '${owner.tableName}.$localKey', 32 | ) 33 | .whereIn('${foreign.tableName}.$foreignKey', ids); 34 | 35 | if (onQuery != null) { 36 | owner = onQuery(owner); 37 | } 38 | return owner as M; 39 | } 40 | 41 | /// get result from belongs to relationship query 42 | Future> getBelongsTo( 43 | dynamic q, 44 | List> list, 45 | ) async { 46 | if (q == null) return {}; // coverage:ignore-line 47 | List results = await q.get(); 48 | 49 | Map ret = {}; 50 | 51 | /// filter matched values with local id value 52 | for (M r in results) { 53 | Map map = (r as Model).toMap(original: true); 54 | ret[map['_foreign_id'].toString()] = r; 55 | } 56 | 57 | return ret; 58 | } 59 | -------------------------------------------------------------------------------- /packages/dox-query-builder/lib/src/utils/annotation.dart: -------------------------------------------------------------------------------- 1 | class Column { 2 | final String? name; 3 | final Function? beforeSave; 4 | final Function? beforeGet; 5 | 6 | const Column({this.name, this.beforeSave, this.beforeGet}); 7 | } 8 | 9 | class HasOne { 10 | final Type model; 11 | final String? foreignKey; 12 | final String? localKey; 13 | final Function? onQuery; 14 | final bool? eager; 15 | 16 | const HasOne(this.model, 17 | {this.foreignKey, this.localKey, this.onQuery, this.eager}); 18 | } 19 | 20 | class HasMany { 21 | final Type model; 22 | final String? foreignKey; 23 | final String? localKey; 24 | final Function? onQuery; 25 | final bool? eager; 26 | 27 | const HasMany(this.model, 28 | {this.foreignKey, this.localKey, this.onQuery, this.eager}); 29 | } 30 | 31 | class BelongsTo { 32 | final Type model; 33 | final String? foreignKey; 34 | final String? localKey; 35 | final Function? onQuery; 36 | final bool? eager; 37 | 38 | const BelongsTo(this.model, 39 | {this.foreignKey, this.localKey, this.onQuery, this.eager}); 40 | } 41 | 42 | class ManyToMany { 43 | final Type model; 44 | final Function? onQuery; 45 | final bool? eager; 46 | 47 | /// id column on artist table 48 | final String? localKey; 49 | 50 | /// id column on song table 51 | final String? relatedKey; 52 | 53 | /// artist_id column on related table 54 | final String? pivotForeignKey; 55 | 56 | /// artist_id column on related table 57 | final String? pivotRelatedForeignKey; 58 | 59 | /// song_artist table 60 | final String? pivotTable; 61 | 62 | const ManyToMany( 63 | this.model, { 64 | this.eager, 65 | this.onQuery, 66 | this.localKey, 67 | this.relatedKey, 68 | this.pivotForeignKey, 69 | this.pivotRelatedForeignKey, 70 | this.pivotTable, 71 | }); 72 | } 73 | -------------------------------------------------------------------------------- /packages/dox-query-builder/example/example.md: -------------------------------------------------------------------------------- 1 | # Dart SQL Query Builder 2 | 3 | ## [Documentation](https://dox.zinkyawkyaw.dev/) 4 | 5 | ## Example Usage 6 | 7 | - Install required dependency 8 | 9 | ```bash 10 | $ dart pub add json_serializable --dev 11 | $ dart pub add build_runner --dev 12 | ``` 13 | 14 | - Setup model 15 | 16 | ```dart 17 | import 'package:postgres/postgres.dart'; 18 | import 'package:dox_query_builder/dox_query_builder.dart'; 19 | 20 | part 'actor.model.g.dart'; 21 | 22 | @IsModel() 23 | class Actor extends Model { 24 | @Column() 25 | int? id; 26 | 27 | @Column(name: 'name') 28 | String? actorName; 29 | 30 | @Column() 31 | int? age; 32 | 33 | @Column(name: 'created_at') 34 | DateTime? createdAt; 35 | 36 | @Column(name: 'updated_at') 37 | DateTime? updatedAt; 38 | 39 | @override 40 | fromJson(Map json) => _$ActorFromJson(json); 41 | 42 | @override 43 | toMap() => _$ActorToJson(this); 44 | } 45 | ``` 46 | 47 | - Run build runner 48 | 49 | ```bash 50 | $ dart run build_runner build 51 | ``` 52 | 53 | ```dart 54 | void main() async { 55 | // create database connection and open 56 | PostgreSQLConnection db = PostgreSQLConnection( 57 | 'localhost', 58 | 5432, 59 | 'postgres', 60 | username: 'admin', 61 | password: 'password', 62 | ); 63 | await db.open(); 64 | 65 | // initialize SqlQueryBuilder, only once at main function 66 | SqlQueryBuilder.initialize(database: db, debug: true); 67 | 68 | // and finally use model from anywhere in the project. 69 | Actor actor = Actor(); 70 | actor.actorName = 'John Wick'; 71 | actor.age = 60; 72 | actor = await actor.save(); 73 | 74 | actor.actorName = "Tom Cruise"; 75 | actor = await actor.save(); // save again 76 | 77 | print(actor.id); 78 | 79 | Actor result = await Actor().where('name', 'Tom Cruise').get(); 80 | print(result.age); 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /packages/dox-query-builder/test/connection.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dox_query_builder/dox_query_builder.dart'; 4 | import 'package:mysql1/mysql1.dart' as mysql; 5 | 6 | DatabaseConfig databaseConfig = DatabaseConfig( 7 | /// ------------------------------- 8 | /// Connection 9 | /// ------------------------------- 10 | /// The primary connection for making database queries across the application 11 | /// You can use any key from the `connections` Map defined in this same 12 | /// file. 13 | connection: Platform.environment['DRIVER'] ?? 'mysql', 14 | 15 | connections: { 16 | /// ------------------------------- 17 | /// Postgres config 18 | /// ------------------------------- 19 | 'postgres': ConnectionConfig( 20 | driver: Driver.postgres, 21 | port: int.parse(Platform.environment['DB_PORT'] ?? '5432'), 22 | user: 'postgres', 23 | password: 'postgres', 24 | database: 'postgres', 25 | extra: { 26 | 'maxConnectionCount': 20, 27 | 'maxConnectionAge': Duration(milliseconds: 500), 28 | }, 29 | debug: false, 30 | printer: ConsoleQueryPrinter(), 31 | ), 32 | 33 | /// ------------------------------- 34 | /// Mysql config 35 | /// ------------------------------- 36 | 'mysql': ConnectionConfig( 37 | driver: Driver.mysql, 38 | port: int.parse(Platform.environment['DB_PORT'] ?? '3306'), 39 | user: Platform.environment['DB_USER'] ?? 'root', 40 | password: 'password', 41 | database: 'dox-framework', 42 | extra: { 43 | 'characterSet': mysql.CharacterSet.UTF8MB4, 44 | }, 45 | debug: false, 46 | printer: ConsoleQueryPrinter(), 47 | ), 48 | }, 49 | ); 50 | 51 | Future initQueryBuilder() async { 52 | SqlQueryBuilder.initializeWithDatabaseConfig(databaseConfig); 53 | } 54 | -------------------------------------------------------------------------------- /packages/dox-app/lib/config/database.dart: -------------------------------------------------------------------------------- 1 | import 'package:dox_core/dox_core.dart'; 2 | import 'package:dox_query_builder/dox_query_builder.dart'; 3 | import 'package:mysql1/mysql1.dart' as mysql; 4 | 5 | DatabaseConfig databaseConfig = DatabaseConfig( 6 | /// ------------------------------- 7 | /// Connection 8 | /// ------------------------------- 9 | /// The primary connection for making database queries across the application 10 | /// You can use any key from the `connections` Map defined in this same 11 | /// file. 12 | connection: Env.get('DB_CONNECTION', 'postgres'), 13 | 14 | connections: { 15 | /// ------------------------------- 16 | /// Postgres config 17 | /// ------------------------------- 18 | 'postgres': ConnectionConfig( 19 | driver: Driver.postgres, 20 | host: Env.get('DB_HOST', 'localhost'), 21 | port: Env.get('DB_PORT', 5432), 22 | user: Env.get('DB_USERNAME', 'postgres'), 23 | password: Env.get('DB_PASSWORD', 'postgres'), 24 | database: Env.get('DB_NAME', 'dox'), 25 | extra: { 26 | 'maxConnectionCount': 10, 27 | 'maxConnectionAge': Duration(hours: 1), 28 | }, 29 | debug: true, 30 | printer: ConsoleQueryPrinter(), 31 | ), 32 | 33 | /// ------------------------------- 34 | /// Mysql config 35 | /// ------------------------------- 36 | 'mysql': ConnectionConfig( 37 | driver: Driver.mysql, 38 | host: Env.get('DB_HOST', 'localhost'), 39 | port: Env.get('DB_PORT', 3306), 40 | user: Env.get('DB_USERNAME', 'root'), 41 | password: Env.get('DB_PASSWORD', 'password'), 42 | database: Env.get('DB_NAME', 'dox'), 43 | extra: { 44 | 'characterSet': mysql.CharacterSet.UTF8MB4, 45 | }, 46 | debug: true, 47 | printer: ConsoleQueryPrinter(), 48 | ), 49 | }, 50 | ); 51 | --------------------------------------------------------------------------------