Compare commits

..

No commits in common. "master" and "angel3" have entirely different histories.

1225 changed files with 27069 additions and 37330 deletions

View file

@ -1,224 +0,0 @@
The name of our company is Protevus
The name of our project is Platform
Protevus = Laravel and Platform = Illuminate
Our Development Langauge is Dart
Our cross-platform UI Kit is Flutter
Our Implementation is based on Angel3
Our Inspiration is based on Laravel
Our Deployment Target are Windows, MacOS, Linux, iOS, Android, Web, IoT and Edge Devices
Protevus Aims to bring a complete Laravel experience to Dart, and Flutter with a truely Unified Full Stack Experience. We aim to mature the platform to support cross-platform development of complex government, military, financial, medical, supply-chain, enterprise and idustrial applications.
Our 10 Step Development Lifecycle is as follows: We use a hybrid mix of Interface Driven Development, Test Driven Development and AI Software Engineer Agents to assist in generating efficient and reliable code while also providing help with other task like code reviews, documentation, and testing. We call this approach IDD-AI or Individual Driven Developemt AI.
1. Research - Research the requirements and specifications of the project.
2. Identify - Identify the key components and technologies that are needed to build the project. (use opensource)
3. Transform - Transform the requirements and specifications from the code to a langauge agnostic abstract contract (YAML)
4. Inform - Use the abstract contracts to inform the design of the system and the components.
5. Generate - Use AI to generate the initial code based on the abstract contracts.
6. Implement - Implement the code in the codebase.
7. Test - Test the code to ensure it meets the requirements and specifications.
8. Iterate - Iterate the code to improve the design and implementation.
9. Review - Review the code to ensure it meets the requirements and specifications.
10. Release - Release the code to the public.
Or RITIGITIRR pronounced Rih-tih-gih-tirr
The we wash rinse and repeat for each project. When we are done we have a fully functional and robust implementation of the requirements and specifications we will still follow this pattern for enhancements, bug fixes, and new features.
The API Specifications are in inbox/spec_packagename
The Concrete Implementations are in inbox/src_packagename
The directory structure for the project is as follows:
config/ = configuration files
devops/ = docker and kubernetes configuration
docs/ = documentation
examples/ = example applications demonstrating using the Platform API
packages/ = platform packages
resources = images, icons, and other resources
scripts/ = utility scripts
templates/ = starter templates
tests/ = repository wide integration tests
tools/ = project wide tools
utils/ = cli, console, and other utilities
Protevus Platform is a hard-fork of Angel3 that is being refactored and rebranded to be Protevus Platform
For now all references to Angel3 remain the same, but in the future we will be changing them to Protevus or Platform
You must always refer to the project as Platform, not Protevus or Angel3
You must always prefer leveraging existing sanctioned angel3 packages as dependencies where possible as most of them will be replace and can't be relied on initially.
You must always prefer leveraging existing standard dart packages as dependencies where possible
You will always prefer leveraging any packages that we can from pub.dev or github.com dart/flutter ecosystem
Our goal is to reimplement the laravel illuminate packages as angel3 packages in short bringing Laravel to Dart
Our goal is to reach feature parity with the illuminate api so we must adhere to the specifications of laravel's api as we reimplement the illuminate packages
We will not reimplement any packages that are not part of laravel, such as symfony packages, or any other packages that are not part of the laravel framework as our goal is to have pure dart implementations so we must find packages in the dart ecosystem where possible or create the feature in dart.
We will not be re-implementing some features of laravel that would require reinventing wheels, for example angel3's server system is more then sufficiant and consist of the following angel3 packages which we will be initally keeping. Container, Framework, Route Http_Exception, Mock_Request, Modal. All other Illuminate packages will be reimplemented in dart. This list of keepers may change as we begin our phases of development.
You must always take the following points into consderation when planning and executing your task.
1. Dart and Flutter ecosystem awareness:
- Stay updated with the latest Dart language features and best practices.
- Consider Flutter compatibility for components that might be used in mobile/web applications.
- Utilize popular Dart packages when appropriate, but be mindful of adding unnecessary dependencies.
2. Performance considerations:
- Implement benchmarking tools to compare performance with both Laravel and the original Angel3.
- Use Dart's profiling tools to identify and optimize bottlenecks.
- Consider implementing lazy loading and deferred initialization where appropriate.
3. Asynchronous programming:
- Leverage Dart's async/await and Future-based programming extensively.
- Implement proper error handling and cancellation for asynchronous operations.
- Consider using Streams for reactive programming where appropriate.
4. Code style and structure:
- Follow Dart's official style guide and linting rules.
- Implement consistent error handling and logging practices across all packages.
- Use Dart's strong typing system to its full potential, including generics and type inference.
5. Testing strategy:
- Implement both unit and integration tests for all components.
- Use mock objects and dependency injection to facilitate easier testing.
- Implement property-based testing for complex algorithms or data structures.
6. Documentation:
- Use Dart's documentation comments extensively.
- Provide examples in documentation for all public APIs.
- Create architecture decision records (ADRs) for significant design choices.
7. Internationalization and localization:
- Design with i18n in mind from the start.
- Use Dart's intl package for formatting dates, numbers, and plurals.
8. Error handling and logging:
- Implement a consistent error handling strategy across all packages.
- Create custom exception classes where appropriate.
- Implement structured logging with different log levels.
9. Security:
- Implement security best practices, including input validation, output encoding, and CSRF protection.
- Use secure random number generation for cryptographic operations.
- Implement rate limiting and throttling mechanisms.
10. Dependency management:
- Use Dart's pub tool effectively for package management.
- Consider implementing a monorepo structure for managing multiple packages.
11. Compatibility layers:
- Implement compatibility layers or wrappers to ease migration from Angel3 to the new framework.
- Provide migration scripts or tools where possible.
12. Extensibility:
- Design components with extensibility in mind, allowing for easy customization and overriding of default behaviors.
- Implement plugin systems where appropriate.
13. Configuration:
- Implement a flexible configuration system that supports different environments (development, testing, production).
- Support both file-based and environment variable-based configuration.
14. CLI tools:
- Develop CLI tools to assist in common development tasks (e.g., generating boilerplate code, running migrations).
- Ensure CLI tools are cross-platform compatible.
15. Database abstraction:
- Implement database migrations and seeders.
- Support multiple database systems, including NoSQL databases.
16. API design:
- Follow RESTful principles when designing APIs.
- Consider implementing GraphQL support.
17. Caching strategies:
- Implement various caching mechanisms (in-memory, file-based, distributed).
- Provide cache invalidation strategies.
18. Scalability:
- Design components with horizontal scalability in mind.
- Implement support for distributed systems and microservices architecture.
19. Metrics and monitoring:
- Implement instrumentation for gathering metrics.
- Provide hooks for integrating with monitoring systems.
20. Dependency injection:
- Implement a robust dependency injection system.
- Support both constructor and property injection.
21. Reflection and metadata:
- Utilize Dart's reflection capabilities where appropriate.
- Implement custom metadata annotations for declarative programming.
22. Error reporting:
- Implement integration with error reporting services (e.g., Sentry).
- Provide detailed stack traces and context for errors.
23. Code generation:
- Utilize Dart's build system for code generation where appropriate.
- Implement source code generators for repetitive tasks.
24. Versioning:
- Follow semantic versioning principles.
- Maintain a detailed changelog.
25. Community engagement:
- Set up issue templates and contribution guidelines on GitHub.
- Implement a code of conduct for the project.
26. Null Safety
- Implement sound null safety throughout the entire codebase.
- Utilize Dart's null safety features, including:
- Nullable and non-nullable types
- Late variables
- Null-aware operators
- Perform null checks where necessary and provide clear documentation on nullability for all public APIs.
- Use the `required` keyword for named parameters that must not be null.
- Leverage the `?`, `!`, and `??` operators appropriately to handle potential null values.
- Implement proper error handling for cases where null values are unexpected.
- When interfacing with external code or APIs that are not null-safe:
- Use the `dynamic` type or appropriate cast operations cautiously
- Utilize the `Never` type for functions that never return normally (i.e., always throw an exception or loop forever).
- Consider using the `late` keyword for variables that are initialized after their declaration but before they're used.
- When working with collections:
- Prefer using nullable item types (e.g., `List<String?>`) over nullable collections (e.g., `List<String>?`) where appropriate
- Use static analysis tools to catch potential null safety issues early in the development process.
- When migrating existing code:
- Use the Dart migration tool
- Carefully review all changes
- Educate contributors on null safety best practices and include null safety checks in code review processes.
Here are some notes that you should always keep in mind when working with the project
Here's the content formatted in Markdown:
## General Guidelines
- Always consider the idiomatic Dart approach when implementing Laravel features.
- Maintain compatibility with existing Angel3 applications where possible.
- Prioritize performance and scalability in all implementations.
- Write comprehensive tests for each component, aiming for high code coverage.
- Document all public APIs and provide usage examples.
- Consider cross-platform compatibility (server, web, mobile) where applicable.
- Keep security as a top priority, especially when implementing authentication and encryption features.
- Regularly refactor and optimize code as the project progresses.
- Engage with the Angel3 community for feedback and contributions throughout the development process.
## Advanced Implementation Considerations
- When implementing advanced features like Telescope or Dusk, consider how they can be adapted to work well in a Dart ecosystem.
- Pay special attention to how Facades and Macroable traits can be implemented in Dart, as these are core to Laravel's flexibility but may not have direct equivalents in Dart.
- For packages like Scout and Passport, research Dart-specific best practices for implementing search and OAuth2 functionalities.
- When working on Blade-like templating, consider how to balance between staying close to Blade's syntax and leveraging Dart's language features.
## Dart-Specific Considerations
- Always consider the implications of Dart's strong typing when implementing dynamic features from Laravel.
- Look for opportunities to leverage Dart's unique features, such as isolates for concurrency, where appropriate.
The Project Road Map is in @Roadmap Book
You now have access to all the information that all team members need to start working on the project.

View file

@ -1,6 +0,0 @@
{
"image": "dart:3.4",
"forwardPorts": [3000,5000],
"features": {
}
}

1
.fork
View file

@ -1 +0,0 @@
Hard-Fork of Angel3 Master Repo 2024-10-01 Advance to Version 9.x

3
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [thosakwe]

16
.gitignore vendored
View file

@ -36,11 +36,6 @@ pubspec.lock
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# If you're building an application, you may want to check-in your pubspec.lock
#pubspec.lock
pubspec_overrides.yaml
pubspec.yaml.bak
# User-specific stuff:
## VsCode
@ -76,13 +71,8 @@ fabric.properties
# Others
logs/
*.pem
.DS_Store
server_log.txt
backup/
# ignore protevus platform
#wspace/
# temp
#inbox/
ormapp/
backup/

View file

@ -1,2 +0,0 @@
.DS_Store
tasks.json

View file

@ -12,7 +12,6 @@
"js": []
},
"editor.codeActionsOnSave": {
"source.fixAll.markdownlint": "explicit"
},
"cmake.configureOnOpen": false
"source.fixAll.markdownlint": true
}
}

View file

@ -1,36 +1,12 @@
# Protevus Platform Authors
Primary Authors
===============
## Core Team
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
- Patrick Stewart <p.stewart@protevus.com> (Project Lead)
- Vacant Spot <jane.smith@example.com> (Lead Developer)
- Vacant Spot <bob.johnson@example.com> (Developer)
Thomas is the current maintainer of the code base. He has refactored and migrated the
code base to support NNBD.
## AI Team
- Cody <cody@protevus.com> (AI Coding Assistant)
- Claude <claude@protevus.com> (AI Coding Assistant)
- Code Copilot <c.copilot@protevus.com> (AI Coding Assistant)
- Keymate <keymate@protevus.com> (AI Coding Assistant)
## Contributors
- Alice Williams <alice.williams@example.com>
- Implemented the ORM module
- Contributed to the database abstraction layer
- Charlie Brown <charlie.brown@example.com>
- Implemented the authentication and authorization system
- Contributed to the event broadcasting and queueing system
- Eve Green <eve.green@example.com>
- Improved the routing system
- Contributed to the middleware implementation
- Michael Davis <michael.davis@example.com>
- Worked on the caching and performance optimization features
- Contributed to the documentation
# Additional contributors (in alphabetical order)
- Vacant Spot
* __[Tobe O](thosakwe@gmail.com)__
Tobe has written much of the original code prior to NNBD migration. He has moved on and
is no longer involved with the project.

View file

@ -0,0 +1,144 @@
# Change Log
## 4.1.x
* Refactored the framework internal to use [Belatuk Common Utilities](<https://github.com/dart-backend/belatuk-common-utilities>)
* Updated to use `lints` linter
* Updated [website](<https://angel3-framework.web.app/>)
* Updated [examples](<https://github.com/dart-backend/belatuk-examples>)
* Fixed ORM code generator
* Fixed Serializer code generator
* Fixed graphQL code generator
* Fixed CLI
* Fixed failed test cases
## 4.0.0 (NNBD)
* Published all packages with `angel3_` prefix
* Changed Dart SDK requirements for all packages to ">=2.12.0 <3.0.0" to support NNBD.
* Migrated pretty_logging to 3.0.0 (0/0 tests passed)
* Migrated angel_http_exception to 3.0.0 (0/0 tests passed)
* Moved angel_cli to [CLI Repository](<https://github.com/dukefirehawk/cli>) (Not migrated yet)
* Added code_buffer and migrated to 2.0.0 (16/16 tests passed)
* Added combinator and migrated to 2.0.0 (16/16 tests passed)
* Migrated angel_route to 5.0.0 (35/35 tests passed)
* Migrated angel_model to 3.0.0 (0/0 tests passed)
* Migrated angel_container to 3.0.0 (55/55 tests passed)
* Added merge_map and migrated to 2.0.0 (6/6 tests passed)
* Added mock_request and migrated to 2.0.0 (5/5 tests)
* Migrated angel_framework to 4.0.0 (149/150 tests passed)
* Migrated angel_auth to 4.0.0 (31/31 tests passed)
* Migrated angel_configuration to 4.0.0 (8/8 testspassed)
* Migrated angel_validate to 4.0.0 (7/7 tests passed)
* Migrated json_god to 4.0.0 (13/13 tests passed)
* Migrated angel_client to 4.0.0 (13/13 tests passed)
* Migrated angel_websocket to 4.0.0 (3/3 tests passed)
* Migrated angel_test to 4.0.0 (1/1 test passed)
* Added symbol_table and migrated to 2.0.0 (16/16 tests passed)
* Migrated jael to 4.0.0 (20/20 tests passed)
* Migrated jael_preprocessor to 3.0.0 (5/5 tests passed)
* Migrated angel_jael to 4.0.0 (1/1 test passed)
* Migrated pub_sub to 4.0.0 (16/16 tests passed)
* Migrated production to 3.0.0 (0/0 tests passed)
* Added html_builder and migrated to 2.0.0 (1/1 tests passed)
* Migrated hot to 4.0.0 (0/0 tests passed)
* Added range_header and migrated to 3.0.0 (12/12 tests passed)
* Migrated angel_static to 4.0.0 (12/12 test passed)
* Created basic-sdk-2.12.x_nnbd template (1/1 test passed) <= Milestone 1
* Migrated angel_serialize to 4.0.0 (0/0 test passed)
* Migrated angel_serialize_generator to 4.0.0 (33/33 tests passed)
* Migrated angel_orm to 3.0.0 (0/0 tests passed)
* Migrated angel_migration to 3.0.0 (0/0 tests passed)
* Added inflection2 and migrated to 1.0.0 (28/32 tests passed)
* Migrated angel_orm_generator to 4.0.0 (0/0 tests passed)
* Migrated angel_migration_runner to 3.0.0 (0/0 tests passed)
* Migrated angel_orm_test to 3.0.0 (0/0 tests passed)
* Migrated angel_orm_postgres to 3.0.0 (51/54 tests passed)
* Create orm-sdk-2.12.x boilerplate (in progress) <= Milestone 2
* Migrated angel_auth_oauth2 to 4.0.0 (0/0 tests passed)
* Migrated angel_auth_cache to 4.0.0 (7/7 tests passed)
* Migrated angel_auth_cors to 4.0.0 (15/15 tests passed)
* Migrated angel_oauth2 to 4.0.0 (17/25 tests passed)
* Migrated angel_proxy to 4.0.0 (6/7 tests passed)
* Migrated angel_file_service to 4.0.0 (6/6 tests passed)
* Migrated graphql_parser to 2.0.0 (55/55 tests passed)
* Migrated graphql_schema to 2.0.0 (34/35 tests passed)
* Migrated graphql_server to 2.0.0 (9/10 tests passed)
* Migrated graphql_generator to 2.0.0 (0/0 tests passed)
* Migrated data_loader to 2.0.0 (7/7 tests passed)
* Migrated angel_graphql to 2.0.0 (0/0 tests passed)
* Migrated angel_mongo to 3.0.0 (0/0 tests passed)
* Migrated angel_orm_mysql to 2.0.0 (0/0 tests passed)
* Migrated angel_orm_service to 2.0.0 (0/0 tests passed)
* Migrated body_parser to 2.0.0 (11/11 tests passed)
* Migrated angel_markdown to 3.0.0 (0/0 tests passed)
* Migrated angel_jinja to 2.0.0 (0/0 tests passed)
* Migrated angel_html to 3.0.0 (1/3 tests passed)
* Migrated angel_mustache to 2.0.0 (3/3 tests passed)
* Migrated angel_paginate to 3.0.0 (18/18 tests passed)
* Migrated angel_poll to 2.0.0 (0/5 tests passed)
* Migrated angel_redis to 2.0.0 (0/8 tests passed)
* Migrated angel_seeder to 2.0.0 (0/0 tests passed)
* Migrated angel_relations to 2.0.0 (0/0 tests passed)
* Migrated angel_rethink to 2.0.0 (0/0 tests passed)
* Migrated angel_security to 2.0.0 (0/1 tests passed)
* Migrated angel_sembast to 2.0.0 (10/10 tests passed)
* Migrated angel_sync to 3.0.0 (0/1 tests passed)
* Migrated angel_typed_service to 3.0.0 (4/4 tests passed)
* Migrated angel_shelf to 2.0.0 (0/1 tests passed)
* Migrated user_agent to 2.0.0 (0/0 tests passed)
* Migrated angel_user_agent to 2.0.0 (0/0 tests passed)
## 3.0.0 (Non NNBD)
* Changed Dart SDK requirements for all packages to ">=2.10.0 <3.0.0"
* Updated pretty_logging to 2.0.0 (0/0 tests passed)
* Updated angel_http_exception to 2.0.0 (0/0 tests passed)
* Updated angel_cli to 3.0.0. (Rename not working)
* Updated angel_route to 4.0.0 (35/35 tests passed)
* Updated angel_model to 2.0.0 (0/0 tests passed)
* Updated angel_container to 2.0.0 (55/55 tests passed)
* Updated angel_framework to 3.0.0 (150/151 tests passed)
* Updated angel_auth to 3.0.0 (28/32 tests passed)
* Updated angel_configuration to 3.0.0 (6/8 tests passed)
* Updated angel_validate to 3.0.0 (7/7 tests passed)
* Added and updated json_god to 3.0.0 (7/7 tests passed)
* Updated angel_client to 3.0.0 (10/13 tests passed)
* Updated angel_websocket to 3.0.0 (3/3 tests passed)
* Updated jael to 3.0.0 (20/20 tests passed)
* Updated jael_preprocessor to 3.0.0 (5/5 tests passed)
* Updated test to 3.0.0 (1/1 tests passed)
* Updated angel_jael to 3.0.0 (1/1 tests passed, Issue with 2 dependencies)
* Added pub_sub and updated to 3.0.0 (16/16 tests passed)
* Updated production to 2.0.0 (0/0 tests passed)
* Updated hot to 3.0.0 (0/0 tests passed)
* Updated static to 3.0.0 (12/12 tests passed)
* Update basic-sdk-2.12.x boilerplate (1/1 tests passed)
* Updated angel_serialize to 3.0.0 (0/0 tests passed)
* Updated angel_serialize_generator to 3.0.0 (33/33 tests passed)
* Updated angel_orm to 3.0.0 (0/0 tests passed)
* Updated angel_migration to 3.0.0 (0/0 tests passed)
* Updated angel_orm_generator to 3.0.0 (0/0 tests passed, use a fork of postgres)
* Updated angel_migration_runner to 3.0.0 (0/0 tests passed)
* Updated angel_orm_test to 1.0.0 (0/0 tests passed)
* Updated angel_orm_postgres to 2.0.0 (52/54 tests passed)
* Update orm-sdk-2.12.x boilerplate
* Updated angel_auth_oauth2 to 3.0.0 (0/0 tests passed)
* Updated angel_auth_cache to 3.0.0 (0/7 tests passed)
* Updated angel_auth_cors to 3.0.0 (15/15 tests passed)
* Updated angel_oauth2 to 3.0.0 (17/25 tests passed)
* Updated angel_container_generator to 2.0.0
* Updated angel_file_service to 3.0.0
* Updated angel_eventsource to 2.0.0 (use a fork of eventsource)
* Updated angel_auth_twitter to 3.0.0 (use a fork of twitter and oauth)
## 2.2.0
* Changed Dart SDK requirements for all packages to ">=2.10.0 <2.12.0"
* Upgraded 3rd party libraries to the latest version prior to dart 2.12
* Fixed broken code due to 3rd party libraries update
* Revert packages/validate from version 3.0 to version 2.2
## 2.1.x and below
* Refer to the orginal repo before the fork

View file

@ -1,39 +0,0 @@
# Protevus Platform Code of Conduct
## Our Pledge
We, as members, contributors, and leaders of the Protevus Platform community, pledge to make participation in our project and community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our community include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned with this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [insert email

View file

@ -1,71 +1,25 @@
# Contributing to the Protevus Platform
Welcome to the Protevus Platform project! We appreciate your interest in contributing to our open-source application server platform. This document outlines the guidelines and best practices for contributing to the project.
# Contribution
## Code of Conduct
Any help from the open-source community is always welcome and needed:
By participating in this project, you are expected to uphold our [Code of Conduct](CODE_OF_CONDUCT.md). Please review it to understand the behavior standards expected of all contributors.
1. Found an issue?
- Please [fill a bug report][tracker] with error message and steps to reproduce it.
2. Wish a feature?
- Open a feature request with use cases.
3. Are you using and liking the project?
- Create an article about your use case
- Do a post on your likes and dislikes
- Make a donation.
4. Are you a developer?
- Fix a bug and send a [pull request][pull_request]
- Implement a new feature
- Improve the Unit Tests
- Improve the [User Guide][doc] and send a [document pull request][doc_repo]
5. Have you already helped in any way?
- **Many thanks to the contributors and everybody that uses this project!**
## Ways to Contribute
There are many ways to contribute to the Protevus Platform project, including:
- Reporting bugs or issues
- Suggesting new features or improvements
- Submitting pull requests with bug fixes or new features
- Improving documentation
- Participating in discussions and providing feedback
## Getting Started
1. Fork the repository and create a new branch for your contribution.
2. Follow the project's coding standards and conventions.
3. Write clear and descriptive commit messages.
4. Test your changes locally before submitting a pull request.
5. Submit a pull request with a detailed description of your changes.
## Branching Conventions
When creating a new branch for your contribution, please follow these naming conventions:
topic names.
```
docs/<description>
fix/<username>-<description>
feature/<username>-<description>
refactor/<username>-<description>
```
If the scope of the issue changes for any reason, please create a new branch with the appropriate naming convention.
## Local Testing
While we provide CI/CD through GitHub Actions, it is recommended to set up your local testing environment to run tests before pushing commits. Follow the instructions in the project's documentation or the CI configuration files to set up your local testing environment.
### Running Tests
Currently, there are three sets of tests that need to be run:
```bash
melos test-unit
# These two need to be run inside packages/conduit
dart test -j1 test/* # use dart test -j1 for Windows and macOS
dart tool/generated_test_runner.dart
```
The first command will run all the unit tests in the Conduit package and its dependencies. The last two commands test CLI components and string-compiled code, respectively.
## Pull Request Requirements
Document the intent and purpose of the pull request.
All non-documentation pull requests must include automated tests that cover the new code, including failure cases.
If tests work locally but not on the CI, please mention @j4qfrost on the pull request or reach out on the Discord server.
## Commits and Versioning
The project uses melos for tooling, which provides autoversioning based on conventional commits. Commits to the master branch will usually be squashed from pull requests, so ensure that the pull request title uses conventional commits to trigger versioning and publishing CI. You do not need to use conventional commits on each commit to your branch.
## Licensing
Protevus Platform is released under the [MIT License](LICENSE).
Thank you for your interest in contributing to the Protevus Platform project! We look forward to your contributions and appreciate your efforts to make this project better.
[tracker]: https://github.com/dukefirehawk/angel/issues
[pull_request]: https://github.com/dukefirehawk/angel/pulls
[doc]: https://angel3-docs.dukefirehawk.com
[doc_repo]: https://github.com/dukefirehawk/angel3-guide/pulls

42
LICENSE
View file

@ -1,21 +1,29 @@
MIT License
BSD 3-Clause License
Copyright (c) 2024 Protevus
Copyright (c) 2021, dukefirehawk.com
All rights reserved.
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:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
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.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

187
README.md
View file

@ -1,113 +1,128 @@
<p align="center"><a href="https://protevus.com" target="_blank"><img src="https://git.protevus.com/protevus/branding/raw/branch/main/protevus-logo-bg.png"></a></p>
# Angel3 Framework
# Protevus Platform
[![Angel3 Framework](./logo3.png)](https://github.com/dukefirehawk/angel)
Protevus Platform is a highly versatile and extensible application server platform for the Dart programming language. It is a hard fork of the Angel3 framework, inspired by Express.js and Laravel, aiming to provide a familiar and Laravel-compatible API while leveraging the power of Dart.
[![version](https://img.shields.io/badge/pub-v4.0.0-brightgreen)](https://pub.dartlang.org/packages/framework)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/angel_dart/discussion)
> **Note:** This repository contains the core code of the Protevus Platform. If you want to build an application using Protevus, visit the main [Protevus repository](https://github.com/protevus/protevus).
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/LICENSE)
## About Protevus
**A polished, production-ready backend framework in Dart with NNBD support.**
Protevus Platform allows developers to leverage their existing Laravel knowledge and experience in the Dart ecosystem. It combines the best features of Angel3 with Laravel-inspired design patterns and APIs, creating a powerful and familiar environment for web application development.
-----
## AI Assistance
## About
The Protevus Platform project utilizes AI assistance in various aspects of its development process. We believe in leveraging the capabilities of AI to enhance productivity, code quality, and overall project progress while maintaining transparency and adhering to ethical practices.
Angel3 is a fork of the original Angel framework to support NNBD in Dart SDK 2.12.x and later.
It is a full-stack Web framework in Dart that aims to streamline development by providing many common features out-of-the-box in a consistent manner. One of the main goal is to enable developers to build both frontend and backend in the same language, Dart. Angel3 framework is designed as a collection of plugins that enable developers to pick and choose the parts needed for their projects. A series of starter templates are also provided for quick start and trial run with Angel3 framework. Visit our [website](<https://angel3-framework.web.app/>) to learn more.
### AI Tools and Models
The availabe features in Angel3 includes:
The following AI tools and models have been primarily employed in the development of the Protevus Platform:
* Static File Handling
* Basic Authentication
* PostgreSQL ORM
* GraphQL
* And much more...
- **Continue** (continue.dev)
- **OpenRouter** (openrouter.ai)
- **Claude** (claude.ai)
- **Codestral** (mistral.ai)
- **Voyage** (voyage.ai)
- Other tools and LLMs
See all the available [`packages`](https://angel3-docs.dukefirehawk.com/packages) for more information.
### Guidelines and Limitations
## Important Notes
While AI assistance has been invaluable in accelerating certain aspects of development, we adhere to strict guidelines to ensure quality, security, and ethical use of AI in our development process.
The core Angel Framework migration to Angel3 Framework has completed and published under `angel3_` prefix on pub.dev. The migrated packages have passed all the test cases. The development work will now move onto the next phase which is to refactor and to improve on the features for better development and deployment experience.
## Features
The status of the code base is as follows:
- **Laravel API Compatibility**: Familiar API for Laravel developers
- **Modular Architecture**: Separating core components and libraries
- **High Performance**: Leverages Dart's efficient event-driven model and isolates for concurrent processing.
- **Asynchronous Processing**: Built on Dart's async-await paradigm for non-blocking operations.
- **Extensibility**: Support for custom extensions
- **Community-Driven**: Open-source principles and community contributions
- **Modular Packages**: Standalone Dart packages for each component
- **Comprehensive Routing**: Powerful routing capabilities
- **Dependency Injection**: Built-in support
- **Middleware Support**: For filtering HTTP requests
- **Authentication & Authorization**: Robust tools
- **Database Abstraction**: Query builder and ORM
- **Queueing System**: Manage background tasks
- **Event Broadcasting**: Real-time event capabilities
- **Full-Stack Experience**: Server-side views and Flutter support for frontends
- **WebSocket Support**: Real-time communication
- **ORM and Database Integration**: Work with various database systems
- **Templating Engine**: For server-side rendering
- **Static File Serving**: Built-in middleware
- **Scalability**: Designed to handle multiple concurrent connections efficiently.
- **Testing Utilities**: Comprehensive testing support
Branch: `master`
## Getting Started
* Dart version : 2.12.x and above.
* Publish : Yes. Refer to packages with `angel3_` prefix on [pub.dev](https://pub.dev/publishers/dukefirehawk.com/packages).
* NNBD Support : Yes
* Status : Release
* Notes : Use this for PR
To get started with Protevus Platform, follow these steps:
Branch: `angel3`
1. **Install Dart**: Ensure you have the Dart SDK installed on your system.
* Dart version : 2.12.x and above.
* NNBD Support : Yes
* Status : Development
* Notes : This branch is under active development. Features maybe broken from time to time.
2. **Create a new project**:
- dart create -t console my_protevus_app cd
- my_protevus_app
For more details, checkout [Project Status](https://github.com/dukefirehawk/angel/wiki/Project-Status)
3. **Add Protevus dependencies**: Add the following to your `pubspec.yaml`:
```yaml
dependencies:
protevus_framework: ^1.0.0
protevus_configuration: ^1.0.0
```
4. **Run pub get**:
```shell
dart pub get
```
5. **Create your first Protevus application**: Replace the contents of bin/my_protevus_app.dart with:
```dart
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
## Installation and Setup
void main() async {
var app = Protevus();
var http = ProtevusHttp(app);
### Create a new project by cloning from boilerplate templates
app.get('/', (req, res) => res.write('Hello, Protevus!'));
1. Download and install [Dart](https://dart.dev/get-dart)
await http.startServer('localhost', 3000);
print('Server listening at http://localhost:3000');
}
```
6. **Run your application**:
```shell
dart run bin/my_protevus_app.dart
```
Visit http://localhost:3000 in your browser to see your Protevus app in action!
2. Clone one of the following starter projects:
* [Angel3 Basic Template](https://github.com/dukefirehawk/boilerplates/tree/angel3-basic)
* [Angel3 ORM Template](https://github.com/dukefirehawk/boilerplates/tree/angel3-orm)
* [Angel3 Graphql Template](https://github.com/dukefirehawk/boilerplates/tree/angel3-graphql)
## Documentation
Comprehensive documentation for Protevus Platform is available at protevus.com/docs/platform. The documentation covers installation, configuration, usage, and advanced topics, including guides and examples.
3. Run the project in development mode (*hot-reloaded* is enabled on file changes).
## Plugins and Packages
Protevus Platform offers a wide range of official plugins and packages to extend its functionality, building upon the Angel3 ecosystem and introducing new Laravel-inspired components.
```bash
dart --observe bin/dev.dart
```
## Community and Support
GitHub Discussions: github.com/protevus/platform/discussions
Twitter: @Protevus
Contributing
We welcome contributions from the community! Please read our CONTRIBUTING.md for guidelines on how to contribute to Protevus Platform.
4. Run the project in production mode (*hot-reloaded* is disabled).
## License
Protevus Platform is open-source software licensed under the MIT license.
```bash
dart bin/prod.dart
```
## Acknowledgements
Protevus Platform is built upon the foundation of Angel3 and inspired by Laravel. We'd like to thank the creators and contributors of both these frameworks for their invaluable work in the web development ecosystem.
5. Run as docker. Edit and build the image with the provided `Dockerfile` file.
6. Next, refer to the [developer guide](https://angel3-docs.dukefirehawk.com/) to learn more about Angel3 framework.
### Create a new project with Angel3 CLI
1. Download and install [Dart](https://dart.dev/get-dart)
2. Install the [Angel3 CLI](https://pub.dev/packages/angel3_cli):
```bash
dart pub global activate angel3_cli
```
3. On terminal, create a new project:
```bash
angel3 init hello
```
4. Run the project in development mode (*hot-reloaded* is enabled on file changes).
```bash
dart --observe bin/dev.dart
```
5. Run the project in production mode (*hot-reloaded* is disabled).
```bash
dart bin/prod.dart
```
6. Run as docker. Edit and build the image with the provided `Dockerfile` file.
7. Next, refer to the [developer guide](https://angel3-docs.dukefirehawk.com/) to learn more about Angel3 framework.
### Migrating from Angel to Angel3
Check out [Migrating to Angel3](https://angel3-docs.dukefirehawk.com/migration/angel-2.x.x-to-angel3/migration-guide-3)
## Examples and Documentation
Visit the [User Guide](https://angel3-docs.dukefirehawk.com/) for dozens of guides and resources, including video tutorials, to get up and running as quickly as possible with Angel3 framework.
Examples and complete projects can be found [here](https://github.com/dukefirehawk/angel3-examples).
You can also view the [Angel3 API](http://www.dartdocs.org/documentation/angel_framework/latest).
There is also an [Awesome Angel :fire:](https://github.com/dukefirehawk/angel3-awesome) list.
## Contributing
Interested in contributing to Angel3? See the contribution guide [here](CONTRIBUTING.md).

View file

@ -1,49 +0,0 @@
# Protevus Platform Security
The Protevus Platform team takes security seriously and is committed to ensuring the security and integrity of the project. This document outlines the security practices, policies, and guidelines followed by the project.
## Reporting Security Vulnerabilities
If you discover a security vulnerability within the Protevus Platform, we appreciate your help in disclosing it responsibly. Please follow these steps:
1. **Do not** create a public issue or disclose the vulnerability publicly.
2. Send an email to the Protevus Platform security team at [security@protevus.com](mailto:security@protevus.com) with details about the vulnerability, including:
- A brief description of the vulnerability
- Steps to reproduce the issue
- Any potential impact or consequences
- Your contact information (optional)
3. The security team will acknowledge your report and work with you to investigate and address the vulnerability.
4. Once the vulnerability has been addressed, you will be credited in the release notes and the security advisory.
We appreciate your cooperation in responsibly disclosing security vulnerabilities, as it helps us maintain the integrity and security of the Protevus Platform.
## Security Practices
The Protevus Platform team follows industry-standard security practices to ensure the security and integrity of the project:
- **Code Reviews**: All code contributions undergo thorough code reviews by the core team to identify and mitigate potential security risks.
- **Secure Coding Practices**: The project adheres to secure coding practices, including input validation, output encoding, and protection against common web application vulnerabilities (e.g., XSS, CSRF, SQL injection).
- **Dependency Management**: Third-party dependencies are regularly monitored and updated to address known vulnerabilities.
- **Security Testing**: The project undergoes regular security testing, including static code analysis, dynamic application security testing (DAST), and penetration testing.
- **Secure Development Lifecycle**: The project follows a secure development lifecycle, incorporating security considerations throughout the development process, from design to deployment.
## Security Advisories
In the event of a security vulnerability being discovered and addressed, the Protevus Platform team will release a security advisory containing the following information:
- A description of the vulnerability
- Affected versions
- Mitigation steps or patches
- Credit to the individuals who reported the vulnerability (if desired)
Security advisories will be published on the project's website and communicated to the community through appropriate channels.
## Responsible Disclosure
The Protevus Platform team believes in responsible disclosure of security vulnerabilities. We will work with researchers and security professionals to address vulnerabilities in a timely and responsible manner, ensuring that the necessary fixes and mitigations are in place before publicly disclosing the details of the vulnerability.
## Conclusion
The security and integrity of the Protevus Platform are of utmost importance to the project team. We are committed to following industry-standard security practices, responsibly disclosing and addressing vulnerabilities, and maintaining open communication with the community regarding security matters.
If you have any questions or concerns regarding the security of the Protevus Platform, please contact the security team at [security@protevus.com](mailto:security@protevus.com).

15
TODO.md Normal file
View file

@ -0,0 +1,15 @@
# Development Blueprint
## Short Term Goal
* Update examples
* Update User Guide
* Performance testing
## Long Term Goal
* Optimise Angel3 architecture
### In Progress
* Migrate generic packages that has no dependencies on Angel3 to [Common Utilities Repository](<https://github.com/dart-backend/belatuk-common-utilities>). All of these packages operate under the hood with no impact to the application build on Angel3. Updating to newer version of Angel3 packages will automatically upgrade them.

View file

@ -1,30 +0,0 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View file

@ -1,75 +0,0 @@
# Protevus Platform DevOps
This directory contains Docker and Kubernetes configurations for the Protevus Platform. It is organized to support our containerization and orchestration needs across different environments.
## Directory Structure
```
devops/
├── docker/
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── .dockerignore
├── kubernetes/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ └── configmap.yaml
├── scripts/
│ ├── docker-build.sh
│ ├── docker-push.sh
│ ├── k8s-deploy.sh
│ └── k8s-rollback.sh
└── README.md
```
### docker/
This directory contains Docker-related files for building and running the Protevus Platform in containers.
- `Dockerfile`: Defines the container image for the Protevus Platform.
- `docker-compose.yml`: Configures multi-container Docker applications for local development.
- `.dockerignore`: Specifies which files and directories should be excluded when building Docker images.
### kubernetes/
The kubernetes/ directory houses Kubernetes manifests for deploying and managing the Protevus Platform in a Kubernetes cluster.
- `deployment.yaml`: Defines the deployment configuration for the Protevus Platform.
- `service.yaml`: Specifies the service configuration for exposing the platform.
- `ingress.yaml`: Configures ingress rules for routing external traffic to the service.
- `configmap.yaml`: Stores configuration data that can be consumed by pods.
### scripts/
This directory contains utility scripts for Docker and Kubernetes operations.
- `docker-build.sh`: Script for building Docker images.
- `docker-push.sh`: Script for pushing Docker images to a registry.
- `k8s-deploy.sh`: Script for deploying the application to a Kubernetes cluster.
- `k8s-rollback.sh`: Script for rolling back a Kubernetes deployment.
## Usage Guidelines
1. Use the provided scripts in the `scripts/` directory for common Docker and Kubernetes operations.
2. Ensure all configuration files are properly parameterized for different environments (dev, staging, production).
3. Keep sensitive information (like passwords and API keys) out of these files and use Kubernetes secrets instead.
4. Regularly update and test these configurations as the Protevus Platform evolves.
## Deployment Process
1. Build the Docker image using `scripts/docker-build.sh`.
2. Push the image to the container registry with `scripts/docker-push.sh`.
3. Deploy to Kubernetes using `scripts/k8s-deploy.sh`.
4. If needed, rollback the deployment using `scripts/k8s-rollback.sh`.
## Contributing
When contributing to the DevOps configurations:
1. Test all changes thoroughly in a non-production environment before applying to production.
2. Document any new scripts or significant changes to existing configurations.
3. Follow Kubernetes and Docker best practices for security and efficiency.
4. Submit a pull request with a clear description of the changes and their purpose.
For any questions or suggestions regarding the DevOps setup, please contact the Protevus Platform infrastructure team.

View file

@ -1,172 +0,0 @@
# Docker Services
The required applications by the framework can be run using the docker compose files provided in this folder.
## PostreSQL
### Starting the PostreSQL container
```bash
docker compose -f docker-compose-pg.yml -p pg up -d
```
### Stopping the PostreSQL container
```bash
docker compose -f docker-compose-pg.yml -p pg stop
docker compose -f docker-compose-pg.yml -p pg down
```
### Checking the PostreSQL container log
```bash
docker logs docker-pg-1 -f
```
### Running psql
```bash
docker exec -it <container id> /bin/bash
psql --username postgres
```
### Create PostgreSQL database, user and grant access
```sql
create database orm_test;
create user test with encrypted password 'test123';
grant all privileges on database orm_test to test;
```
## MariaDB
### Starting the MariaDB container
```bash
docker compose -f docker-compose-mariadb.yml -p maria up -d
```
### Stopping the MariaDB container
```bash
docker compose -f docker-compose-mariadb.yml -p maria stop
docker compose -f docker-compose-mariadb.yml -p maria down
```
### Checking the MariaDB container log
```bash
docker logs maria-mariadb-1 -f
```
### Create MariaDB database, user and grant access
```sql
create database orm_test;
-- Granting localhost access only
create user 'test'@'localhost' identified by 'test123';
grant all privileges on orm_test.* to 'test'@'localhost';
-- Granting localhost and remote access
create user 'test'@'%' identified by 'test123';
grant all privileges on orm_test.* to 'test'@'%';
```
## MySQL
### Starting the MySQL container
```bash
docker compose -f docker-compose-mysql.yml -p mysql up -d
```
### Stopping the MySQL container
```bash
docker compose -f docker-compose-mysql.yml -p mysql stop
docker compose -f docker-compose-mysql.yml -p mysql down
```
### Checking the MySQL container log
```bash
docker logs mysql-mysql-1 -f
```
### Create MySQL database, user and grant access
```sql
create database orm_test;
-- Granting localhost access only
create user 'test'@'localhost' identified by 'test123';
grant all privileges on orm_test.* to 'test'@'localhost';
-- Granting localhost and remote access
create user 'test'@'%' identified by 'test123';
grant all privileges on orm_test.* to 'test'@'%';
```
## MongoDB
### Starting the MongoDB container
```bash
docker compose -f docker-compose-mongo.yml -p mongo up -d
```
### Stopping the MongoDB container
```bash
docker compose -f docker-compose-mongo.yml -p mongo stop
docker compose -f docker-compose-mongo.yml -p mongo down
```
### Checking the MongoDB container log
```bash
docker logs mongo-mongo-1 -f
```
## rethinkDB
### Starting the rethinkDB container
```bash
docker compose -f docker-compose-rethinkdb.yml -p rethink up -d
```
### Stopping the rethinkDB container
```bash
docker compose -f docker-compose-rethinkdb.yml -p rethink stop
docker compose -f docker-compose-rethinkdb.yml -p rethink down
```
### Checking the rethinkDB container log
```bash
docker logs rethink-rethinkdb-1 -f
```
## Redis
### Starting the Redis container
```bash
docker compose -f docker-compose-redis.yml -p redis up -d
```
### Stopping the Redis container
```bash
docker compose -f docker-compose-redis.yml -p redis stop
docker compose -f docker-compose-redis.yml -p redis down
```
### Checking the Redis container log
```bash
docker logs redis-redis-1 -f
```

View file

@ -1,19 +0,0 @@
services:
mariadb:
image: mariadb:latest
restart: "no"
ports:
- "3306:3306"
environment:
- MARIADB_ROOT_PASSWORD=Qwerty
volumes:
- "mariadb:/var/lib/mysql"
networks:
- appnet
volumes:
mariadb:
driver: local
networks:
appnet:

View file

@ -1,37 +0,0 @@
services:
mongo:
image: mongo
restart: no
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: Qwerty
MONGO_INITDB_DATABASE: local
volumes:
- "mongo:/data/db"
networks:
- appnet
mongo-express:
image: mongo-express
restart: no
depends_on:
- mongo
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: Qwerty
ME_CONFIG_MONGODB_URL: mongodb://root:Qwerty@mongo:27017/
ME_CONFIG_BASICAUTH: false
networks:
- webnet
volumes:
mongo:
driver: local
networks:
appnet:

View file

@ -1,19 +0,0 @@
services:
mysql:
image: mysql:latest
restart: "no"
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=Qwerty
volumes:
- "mysql:/var/lib/mysql"
networks:
- appnet
volumes:
mysql:
driver: local
networks:
appnet:

View file

@ -1,31 +0,0 @@
services:
pgdb:
image: postgres:latest
restart: "no"
ports:
- "5432:5432"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes:
- "db:/var/lib/postgresql/data"
networks:
- appnet
pgadmin4:
image: dpage/pgadmin4:latest
restart: "no"
ports:
- "5050:80"
environment:
- PGADMIN_DEFAULT_EMAIL=admin@mydomain.com
- PGADMIN_DEFAULT_PASSWORD=Qwerty
networks:
- appnet
volumes:
db:
driver: local
networks:
appnet:

View file

@ -1,20 +0,0 @@
services:
redis:
image: redis:latest
restart: "no"
ports:
- "5432:5432"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes:
- "redis:/data"
networks:
- appnet
volumes:
redis:
driver: local
networks:
appnet:

View file

@ -1,19 +0,0 @@
services:
rethinkdb:
image: rethinkdb:latest
restart: "no"
ports:
- "8080:8080"
- "28015:28015"
- "29015:29015"
volumes:
- "rethinkdb:/data"
networks:
- appnet
volumes:
rethinkdb:
driver: local
networks:
appnet:

View file

@ -1,10 +0,0 @@
# Performance Testing
The performance test can be run with the following tools.
## WRT
```bash
wrk -t12 -c400 -d30s http://localhost:8080/query?queries=20
```
This runs a benchmark for 30 seconds, using 12 threads, and keeping 400 HTTP connections open.

View file

@ -1,68 +0,0 @@
# Protevus Platform Helpers
This directory contains various helper functionalities, tools, and utilities for the Protevus Platform. It is organized into subdirectories to maintain a clear structure and separation of concerns.
## Directory Structure
```
helpers/
├── cli/
├── console/
├── tools/
└── utilities/
```
### cli/
This directory contains command-line interface tools and scripts specific to the Protevus Platform. These are typically used for development, deployment, or maintenance tasks that are run directly from the command line.
Examples:
- Database migration scripts
- Code generation tools
- Deployment scripts
### console/
The console/ directory houses console commands and utilities, similar to Laravel's Artisan commands. These are interactive tools that provide a user-friendly interface for various platform operations.
Examples:
- REPL (Read-Eval-Print Loop) for the Protevus Platform
- Interactive configuration tools
- Database seeding commands
### tools/
This directory is for larger, more complex helper applications or scripts used in development, testing, or deployment of the Protevus Platform. These tools often combine multiple functionalities or interact with external services.
Examples:
- Automated testing suites
- Performance profiling tools
- Documentation generators
### utilities/
The utilities/ directory contains general-purpose utility functions and smaller helper scripts. These are typically reusable across different parts of the platform and provide common functionality.
Examples:
- String manipulation functions
- Date and time helpers
- File system operations
## Usage Guidelines
1. Place new helpers in the appropriate subdirectory based on their purpose and complexity.
2. Maintain consistency in naming conventions and file structures within each subdirectory.
3. Document each helper, tool, or utility with clear comments and usage examples.
4. Update this README when adding new significant helpers or changing the structure.
## Contributing
When contributing new helpers:
1. Ensure your code follows the Protevus Platform coding standards.
2. Write tests for your helpers when applicable.
3. Update or create documentation for new functionalities.
4. Submit a pull request with a clear description of the new helper and its purpose.
For any questions or suggestions regarding the helpers structure, please contact the Protevus Platform core development team.

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

BIN
logo3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
logo3.xcf Normal file

Binary file not shown.

View file

@ -1,20 +0,0 @@
name: protevus_platform
repository: https://github.com/protevus/platform
packages:
- core/**
- packages/**
- sandbox/**
- wspace/**
- examples/**
command:
version:
# Generate commit links in package changelogs.
linkToCommits: true
# Only allow versioning to happen on main branch.
branch: master
workspaceChangelog: true
ide:
intellij:
enabled: false

21
packages/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License (MIT)
Copyright (c) 2021 dukefirehawk.com
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.

View file

@ -69,4 +69,3 @@ com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
.DS_Store

View file

@ -0,0 +1,4 @@
language: dart
dart:
- dev
- stable

View file

@ -1,44 +1,5 @@
# Change Log
## 8.2.0
* Require Dart >= 3.3
* Updated `lints` to 4.0.0
## 8.1.1
* Updated repository link
## 8.1.0
* Updated `lints` to 3.0.0
## 8.0.0
* Require Dart >= 3.0
* Upgraded `http` to 1.0.0
* Fixed failed `successRedirect` test case
* Fixed failed `failureRedirect` test case
* Fixed failed `login` test case
* Fixed failed `force basic` test case
* Added `example1` and `example2`
## 7.0.1
* Fixed linter warnings
## 7.0.0
* Require Dart >= 2.17
## 6.0.0
* Require Dart >= 2.16
## 5.0.0
* Skipped release
## 4.1.2
* Fixed `requireAuthentication` to work correctly with null-safety type
@ -78,11 +39,11 @@
## 4.0.0
* Migrated to support Dart >= 2.12 NNBD
* Migrated to support Dart SDK 2.12.x NNBD
## 3.0.0
* Migrated to work with Dart >= 2.12 Non NNBD
* Migrated to work with Dart SDK 2.12.x Non NNBD
## 2.1.5+1
@ -135,13 +96,13 @@
## 2.0.0
* Made `AuthStrategy` generic.
* `ProtevusAuth.strategies` is now a `Map<String, AuthStrategy<User>>`.
* `AngelAuth.strategies` is now a `Map<String, AuthStrategy<User>>`.
* Removed `AuthStrategy.canLogout`.
* Made `ProtevusAuthTokenCallback` generic.
* Made `AngelAuthTokenCallback` generic.
## 2.0.0-alpha
* Depend on Dart 2 and Protevus 2.
* Depend on Dart 2 and Angel 2.
* Remove `dart2_constant`.
* Remove `requireAuth`.
* Remove `userKey`, instead favoring generic parameters.

View file

@ -1,11 +1,11 @@
# Protevus Anthentication
# Angel3 Anthentication
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/protevus_auth?include_prereleases)
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_auth?include_prereleases)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
[![License](https://img.shields.io/github/license/dart-backend/protevus)](https://github.com/dart-backend/protevus/tree/master/packages/auth/LICENSE)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/auth/LICENSE)
A complete authentication plugin for Protevus. Inspired by Passport. More details in the [User Guide](https://protevus-docs.dukefirehawk.com/guides/authentication).
A complete authentication plugin for Angel3. Inspired by Passport. More details in the [User Guide](https://angel3-docs.dukefirehawk.com/guides/authentication).
## Bundled Strategies
@ -14,20 +14,20 @@ A complete authentication plugin for Protevus. Inspired by Passport. More detail
## Example
Ensure you have read the [User Guide](https://protevus-docs.dukefirehawk.com/guides/authentication).
Ensure you have read the [User Guide](https://angel3-docs.dukefirehawk.com/guides/authentication).
```dart
configureServer(Protevus app) async {
var auth = ProtevusAuth<User>(
configureServer(Angel app) async {
var auth = AngelAuth<User>(
serializer: (user) => user.id ?? '',
deserializer: (id) => fetchAUserByIdSomehow(id
deserializer: (id) => fetchAUserByIdSomehow(id)
);
auth.strategies['local'] = LocalAuthStrategy(...);
// POST route to handle username+password
app.post('/local', auth.authenticate('local'));
// Using Protevus's asynchronous injections, we can parse the JWT
// Using Angel's asynchronous injections, we can parse the JWT
// on demand. It won't be parsed until we check.
app.get('/profile', ioc((User user) {
print(user.description);
@ -52,13 +52,13 @@ configureServer(Protevus app) async {
## Default Authentication Callback
A frequent use case within SPA's is opening OAuth login endpoints in a separate window. [`protevus_client`](https://pub.dev/packages/protevus_client) provides a facility for this, which works perfectly with the default callback provided in this package.
A frequent use case within SPA's is opening OAuth login endpoints in a separate window. [`angel3_client`](https://pub.dev/packages/angel3_client) provides a facility for this, which works perfectly with the default callback provided in this package.
```dart
configureServer(Protevus app) async {
configureServer(Angel app) async {
var handler = auth.authenticate(
'facebook',
ProtevusAuthOptions(callback: confirmPopupAuthentication()));
AngelAuthOptions(callback: confirmPopupAuthentication()));
app.get('/auth/facebook', handler);
// Use a comma to try multiple strategies!!!
@ -75,7 +75,7 @@ configureServer(Protevus app) async {
}
```
This renders a simple HTML page that fires the user's JWT as a `token` event in `window.opener`. `protevus_client` [exposes this as a Stream](https://pub.dev/documentation/protevus_client/latest/):
This renders a simple HTML page that fires the user's JWT as a `token` event in `window.opener`. `angel3_client` [exposes this as a Stream](https://pub.dev/documentation/angel3_client/latest/):
```dart
app.authenticateViaPopup('/auth/google').listen((jwt) {

View file

@ -1,22 +0,0 @@
### Load landing page
GET http://localhost:3000/ HTTP/1.1
### login (call_back)
POST http://localhost:3000/login HTTP/1.1
Content-Type: application/json
Authorization: Basic jdoe1:password
### Success redirect (local)
POST http://localhost:3000/login HTTP/1.1
Content-Type: application/json
Authorization: Basic username:password
### Failure redirect (local)
POST http://localhost:3000/login HTTP/1.1
Content-Type: application/json
Authorization: Basic password:username
### Force basic
GET http://localhost:3000/hello HTTP/1.1
Content-Type: application/json
Accept:application/json

View file

@ -1,11 +1,11 @@
import 'dart:async';
import 'package:protevus_auth/protevus_auth.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
void main() async {
var app = Protevus();
var auth = ProtevusAuth<User>(
var app = Angel();
var auth = AngelAuth<User>(
serializer: (user) => user.id ?? '',
deserializer: (id) => fetchAUserByIdSomehow(id));
@ -17,12 +17,11 @@ void main() async {
// If authentication succeeds, return a User object.
//
// Otherwise, return `null`.
return null;
});
app.post('/auth/local', auth.authenticate('local'));
var http = ProtevusHttp(app);
var http = AngelHttp(app);
await http.startServer('127.0.0.1', 3000);
print('Listening at http://127.0.0.1:3000');

View file

@ -1,114 +0,0 @@
import 'package:protevus_auth/protevus_auth.dart';
import 'package:protevus_container/mirrors.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:io/ansi.dart';
import 'package:logging/logging.dart';
import 'package:collection/collection.dart';
class User extends Model {
String? username, password;
User({this.username, this.password});
static User parse(Map<String, dynamic> map) {
return User(
username: map['username'],
password: map['password'],
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'username': username,
'password': password,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String()
};
}
}
/*
* Backend for callback test cases
*/
void main() async {
hierarchicalLoggingEnabled = true;
Protevus app = Protevus(reflector: MirrorsReflector());
ProtevusHttp angelHttp = ProtevusHttp(app);
app.use('/users', MapService());
var oldErrorHandler = app.errorHandler;
app.errorHandler = (e, req, res) {
app.logger.severe(e.message, e, e.stackTrace ?? StackTrace.current);
return oldErrorHandler(e, req, res);
};
app.logger = Logger('protevus_auth')
..level = Level.FINEST
..onRecord.listen((rec) {
print(rec);
if (rec.error != null) {
print(yellow.wrap(rec.error.toString()));
}
if (rec.stackTrace != null) {
print(yellow.wrap(rec.stackTrace.toString()));
}
});
await app
.findService('users')
?.create({'username': 'jdoe1', 'password': 'password'});
var auth = ProtevusAuth<User>(
serializer: (u) => u.id ?? '',
deserializer: (id) async =>
await app.findService('users')?.read(id) as User);
//auth.serializer = (u) => u.id;
//auth.deserializer =
// (id) async => await app.findService('users')!.read(id) as User;
await app.configure(auth.configureServer);
auth.strategies['local'] = LocalAuthStrategy((username, password) async {
var users = await app
.findService('users')
?.index()
.then((it) => it.map<User>((m) => User.parse(m)).toList());
var result = users?.firstWhereOrNull(
(user) => user.username == username && user.password == password);
return Future.value(result);
}, allowBasic: true);
app.post(
'/login',
auth.authenticate('local',
ProtevusAuthOptions(callback: (req, res, token) {
res
..write('Hello!')
..close();
})));
app.get('/', (req, res) => res.write("Hello"));
app.chain([
(req, res) {
if (!req.container!.has<User>()) {
req.container!.registerSingleton<User>(
User(username: req.params['name']?.toString()));
}
return true;
}
]).post(
'/existing/:name',
auth.authenticate('local'),
);
await angelHttp.startServer('127.0.0.1', 3000);
}

View file

@ -1,69 +0,0 @@
import 'dart:async';
import 'package:protevus_auth/protevus_auth.dart';
import 'package:protevus_container/mirrors.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
import 'package:logging/logging.dart';
final Map<String, String> sampleUser = {'hello': 'world'};
final ProtevusAuth<Map<String, String>> auth =
ProtevusAuth<Map<String, String>>(
serializer: (user) async => '1337',
deserializer: (id) async => sampleUser);
//var headers = <String, String>{'accept': 'application/json'};
var localOpts = ProtevusAuthOptions<Map<String, String>>(
failureRedirect: '/failure', successRedirect: '/success');
var localOpts2 =
ProtevusAuthOptions<Map<String, String>>(canRespondWithJson: false);
Future<Map<String, String>> verifier(String? username, String? password) async {
if (username == 'username' && password == 'password') {
return sampleUser;
} else {
return {};
}
}
Future wireAuth(Protevus app) async {
//auth.strategies['local'] = LocalAuthStrategy(verifier);
auth.strategies['local'] =
LocalAuthStrategy(verifier, forceBasic: true, realm: 'test');
await app.configure(auth.configureServer);
}
/*
* Backend for local test cases
*/
void main() async {
Protevus app = Protevus(reflector: MirrorsReflector());
ProtevusHttp angelHttp = ProtevusHttp(app, useZone: false);
await app.configure(wireAuth);
app.get('/hello', (req, res) {
// => 'Woo auth'
return 'Woo auth';
}, middleware: [auth.authenticate('local', localOpts2)]);
app.post('/login', (req, res) => 'This should not be shown',
middleware: [auth.authenticate('local', localOpts)]);
app.get('/success', (req, res) => 'yep', middleware: [
requireAuthentication<Map<String, String>>(),
]);
app.get('/failure', (req, res) => 'nope');
app.logger = Logger('local_test')
..onRecord.listen((rec) {
print(
'${rec.time}: ${rec.level.name}: ${rec.loggerName}: ${rec.message}');
if (rec.error != null) {
print(rec.error);
print(rec.stackTrace);
}
});
await angelHttp.startServer('127.0.0.1', 3000);
}

View file

@ -1,4 +1,4 @@
library protevus_auth;
library angel3_auth;
export 'src/middleware/require_auth.dart';
export 'src/strategies/strategies.dart';

View file

@ -1,4 +1,4 @@
/// Stand-alone JWT library.
library protevus_auth.auth_token;
library angel3_auth.auth_token;
export 'src/auth_token.dart';

View file

@ -1,5 +1,5 @@
import 'dart:collection';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:logging/logging.dart';
@ -68,7 +68,7 @@ class AuthToken {
if (split.length != 3) {
_log.warning('Invalid JWT');
throw ProtevusHttpException.notAuthenticated(message: 'Invalid JWT.');
throw AngelHttpException.notAuthenticated(message: 'Invalid JWT.');
}
var payloadString = decodeBase64(split[1]);
@ -81,17 +81,17 @@ class AuthToken {
if (split.length != 3) {
_log.warning('Invalid JWT');
throw ProtevusHttpException.notAuthenticated(message: 'Invalid JWT.');
throw AngelHttpException.notAuthenticated(message: 'Invalid JWT.');
}
// var headerString = decodeBase64(split[0]);
var payloadString = decodeBase64(split[1]);
var data = '${split[0]}.${split[1]}';
var data = split[0] + '.' + split[1];
var signature = base64Url.encode(hmac.convert(data.codeUnits).bytes);
if (signature != split[2]) {
_log.warning('JWT payload does not match hashed version');
throw ProtevusHttpException.notAuthenticated(
throw AngelHttpException.notAuthenticated(
message: 'JWT payload does not match hashed version.');
}
@ -102,9 +102,9 @@ class AuthToken {
String serialize(Hmac hmac) {
var headerString = base64Url.encode(json.encode(_header).codeUnits);
var payloadString = base64Url.encode(json.encode(toJson()).codeUnits);
var data = '$headerString.$payloadString';
var data = headerString + '.' + payloadString;
var signature = hmac.convert(data.codeUnits).bytes;
return '$data.${base64Url.encode(signature)}';
return data + '.' + base64Url.encode(signature);
}
Map<String, dynamic> toJson() {

View file

@ -1,9 +1,9 @@
import 'dart:async';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
/// Forces Basic authentication over the requested resource, with the given [realm] name, if no JWT is present.
///
/// [realm] defaults to `'protevus_auth'`.
/// [realm] defaults to `'angel3_auth'`.
RequestHandler forceBasicAuth<User>({String? realm}) {
return (RequestContext req, ResponseContext res) async {
if (req.container != null) {
@ -17,7 +17,7 @@ RequestHandler forceBasicAuth<User>({String? realm}) {
}
res.headers['www-authenticate'] = 'Basic realm="${realm ?? 'angel_auth'}"';
throw ProtevusHttpException.notAuthenticated();
throw AngelHttpException.notAuthenticated();
};
}
@ -25,10 +25,10 @@ RequestHandler forceBasicAuth<User>({String? realm}) {
RequestHandler requireAuthentication<User>() {
return (RequestContext req, ResponseContext res,
{bool throwError = true}) async {
bool reject(ResponseContext res) {
bool _reject(ResponseContext res) {
if (throwError) {
res.statusCode = 403;
throw ProtevusHttpException.forbidden();
throw AngelHttpException.forbidden();
} else {
return false;
}
@ -42,10 +42,10 @@ RequestHandler requireAuthentication<User>() {
await reqContainer.makeAsync<User>();
return true;
} else {
return reject(res);
return _reject(res);
}
} else {
return reject(res);
return _reject(res);
}
};
}

View file

@ -1,17 +1,17 @@
import 'dart:async';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'auth_token.dart';
typedef ProtevusAuthCallback = FutureOr Function(
typedef AngelAuthCallback = FutureOr Function(
RequestContext req, ResponseContext res, String token);
typedef ProtevusAuthTokenCallback<User> = FutureOr Function(
typedef AngelAuthTokenCallback<User> = FutureOr Function(
RequestContext req, ResponseContext res, AuthToken token, User user);
class ProtevusAuthOptions<User> {
ProtevusAuthCallback? callback;
ProtevusAuthTokenCallback<User>? tokenCallback;
class AngelAuthOptions<User> {
AngelAuthCallback? callback;
AngelAuthTokenCallback<User>? tokenCallback;
String? successRedirect;
String? failureRedirect;
@ -21,7 +21,7 @@ class ProtevusAuthOptions<User> {
/// Works well with `Basic` authentication.
bool canRespondWithJson;
ProtevusAuthOptions(
AngelAuthOptions(
{this.callback,
this.tokenCallback,
this.canRespondWithJson = true,

View file

@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:crypto/crypto.dart';
import 'package:logging/logging.dart';
@ -9,9 +9,9 @@ import 'auth_token.dart';
import 'options.dart';
import 'strategy.dart';
/// Handles authentication within an Protevus application.
class ProtevusAuth<User> {
final _log = Logger('ProtevusAuth');
/// Handles authentication within an Angel application.
class AngelAuth<User> {
final _log = Logger('AngelAuth');
late Hmac _hs256;
late int _jwtLifeSpan;
@ -78,7 +78,7 @@ class ProtevusAuth<User> {
}
/// `jwtLifeSpan` - should be in *milliseconds*.
ProtevusAuth(
AngelAuth(
{String? jwtKey,
required this.serializer,
required this.deserializer,
@ -95,30 +95,29 @@ class ProtevusAuth<User> {
_jwtLifeSpan = jwtLifeSpan.toInt();
}
/// Configures an Protevus server to decode and validate JSON Web tokens on demand,
/// Configures an Angel server to decode and validate JSON Web tokens on demand,
/// whenever an instance of [User] is injected.
Future<void> configureServer(Protevus app) async {
Future<void> configureServer(Angel app) async {
/*
if (serializer == null) {
throw StateError(
'An `ProtevusAuth` plug-in was called without its `serializer` being set. All authentication will fail.');
'An `AngelAuth` plug-in was called without its `serializer` being set. All authentication will fail.');
}
if (deserializer == null) {
throw StateError(
'An `ProtevusAuth` plug-in was called without its `deserializer` being set. All authentication will fail.');
}
if (app.container == null) {
_log.severe('Protevus container is null');
throw StateError(
'Protevus.container is null. All authentication will fail.');
'An `AngelAuth` plug-in was called without its `deserializer` being set. All authentication will fail.');
}
*/
var appContainer = app.container;
if (app.container == null) {
_log.severe('Angel3 container is null');
throw StateError(
'Angel.container is null. All authentication will fail.');
}
var appContainer = app.container!;
appContainer.registerSingleton(this);
if (runtimeType != ProtevusAuth) {
appContainer.registerSingleton(this, as: ProtevusAuth);
if (runtimeType != AngelAuth) {
appContainer.registerSingleton(this, as: AngelAuth);
}
if (!appContainer.has<_AuthResult<User>>()) {
@ -128,7 +127,7 @@ class ProtevusAuth<User> {
var res = container.make<ResponseContext>();
//if (req == null || res == null) {
// _log.warning('RequestContext or responseContext is null');
// throw ProtevusHttpException.forbidden();
// throw AngelHttpException.forbidden();
//}
var result = await _decodeJwt(req, res);
@ -136,7 +135,7 @@ class ProtevusAuth<User> {
return result;
} else {
_log.warning('JWT is null');
throw ProtevusHttpException.forbidden();
throw AngelHttpException.forbidden();
}
});
@ -184,10 +183,10 @@ class ProtevusAuth<User> {
///
/// Now that `package:angel_framework` supports asynchronous injections, this middleware
/// is no longer directly necessary. Instead, call [configureServer]. You can then use
/// `makeAsync<User>`, or Protevus's injections directly:
/// `makeAsync<User>`, or Angel's injections directly:
///
/// ```dart
/// var auth = ProtevusAuth<User>(...);
/// var auth = AngelAuth<User>(...);
/// await app.configure(auth.configureServer);
///
/// app.get('/hmm', (User user) async {
@ -223,7 +222,7 @@ class ProtevusAuth<User> {
if (enforceIp) {
if (req.ip != token.ipAddress) {
_log.warning('JWT cannot be accessed from this IP address');
throw ProtevusHttpException.forbidden(
throw AngelHttpException.forbidden(
message: 'JWT cannot be accessed from this IP address.');
}
}
@ -234,7 +233,7 @@ class ProtevusAuth<User> {
if (!expiry.isAfter(DateTime.now())) {
_log.warning('Expired JWT');
throw ProtevusHttpException.forbidden(message: 'Expired JWT.');
throw AngelHttpException.forbidden(message: 'Expired JWT.');
}
}
@ -308,13 +307,13 @@ class ProtevusAuth<User> {
if (jwt == null) {
_log.warning('No JWT provided');
throw ProtevusHttpException.forbidden(message: 'No JWT provided');
throw AngelHttpException.forbidden(message: 'No JWT provided');
} else {
var token = AuthToken.validate(jwt, _hs256);
if (enforceIp) {
if (req.ip != token.ipAddress) {
_log.warning('WT cannot be accessed from this IP address');
throw ProtevusHttpException.forbidden(
throw AngelHttpException.forbidden(
message: 'JWT cannot be accessed from this IP address.');
}
}
@ -339,11 +338,11 @@ class ProtevusAuth<User> {
return {'data': data, 'token': token.serialize(_hs256)};
}
} catch (e) {
if (e is ProtevusHttpException) {
if (e is AngelHttpException) {
rethrow;
}
_log.warning('Malformed JWT');
throw ProtevusHttpException.badRequest(message: 'Malformed JWT');
throw AngelHttpException.badRequest(message: 'Malformed JWT');
}
}
@ -355,9 +354,9 @@ class ProtevusAuth<User> {
/// or a `401 Not Authenticated` is thrown, if it is the last one.
///
/// Any other result is considered an authenticated user, and terminates the loop.
RequestHandler authenticate(type, [ProtevusAuthOptions<User>? opt]) {
RequestHandler authenticate(type, [AngelAuthOptions<User>? opt]) {
return (RequestContext req, ResponseContext res) async {
var authOption = opt ?? ProtevusAuthOptions<User>();
var authOption = opt ?? AngelAuthOptions<User>();
var names = <String>[];
@ -452,7 +451,7 @@ class ProtevusAuth<User> {
return false;
} else {
_log.warning('Not authenticated');
throw ProtevusHttpException.notAuthenticated();
throw AngelHttpException.notAuthenticated();
}
}
}
@ -485,7 +484,7 @@ class ProtevusAuth<User> {
}
/// Log an authenticated user out.
RequestHandler logout([ProtevusAuthOptions<User>? options]) {
RequestHandler logout([AngelAuthOptions<User>? options]) {
return (RequestContext req, ResponseContext res) async {
if (req.container?.has<User>() == true) {
var user = req.container?.make<User>();

View file

@ -1,10 +1,10 @@
import 'dart:convert';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:http_parser/http_parser.dart';
import 'options.dart';
/// Displays a default callback page to confirm authentication via popups.
ProtevusAuthCallback confirmPopupAuthentication({String eventName = 'token'}) {
AngelAuthCallback confirmPopupAuthentication({String eventName = 'token'}) {
return (req, ResponseContext res, String jwt) {
var evt = json.encode(eventName);
var detail = json.encode({'detail': jwt});

View file

@ -1,10 +1,12 @@
import 'dart:async';
import 'dart:convert';
import 'package:logging/logging.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
import '../options.dart';
import '../strategy.dart';
bool _validateString(String? str) => str != null && str.isNotEmpty;
/// Determines the validity of an incoming username and password.
// typedef FutureOr<User> LocalAuthVerifier<User>(String? username, String? password);
typedef LocalAuthVerifier<User> = FutureOr<User?> Function(
@ -28,7 +30,7 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
{this.usernameField = 'username',
this.passwordField = 'password',
this.invalidMessage = 'Please provide a valid username and password.',
this.allowBasic = false,
this.allowBasic = true,
this.forceBasic = false,
this.realm = 'Authentication is required.'}) {
_log.info('Using LocalAuthStrategy');
@ -36,8 +38,8 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
@override
Future<User?> authenticate(RequestContext req, ResponseContext res,
[ProtevusAuthOptions? options]) async {
var localOptions = options ?? ProtevusAuthOptions();
[AngelAuthOptions? options]) async {
var _options = options ?? AngelAuthOptions();
User? verificationResult;
if (allowBasic) {
@ -55,7 +57,7 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
await verifier(usrPassMatch.group(1), usrPassMatch.group(2));
} else {
_log.warning('Bad request: $invalidMessage');
throw ProtevusHttpException.badRequest(errors: [invalidMessage]);
throw AngelHttpException.badRequest(errors: [invalidMessage]);
}
if (verificationResult == null) {
@ -66,55 +68,29 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
return null;
}
//Allow non-null to pass through
//return verificationResult;
return verificationResult;
}
} else {
}
if (verificationResult == null) {
var body = await req
.parseBody()
.then((_) => req.bodyAsMap)
.catchError((_) => <String, dynamic>{});
if (_validateString(body[usernameField]?.toString()) &&
_validateString(body[passwordField]?.toString())) {
//if (body != null) {
if (_validateString(body[usernameField].toString()) &&
_validateString(body[passwordField].toString())) {
verificationResult = await verifier(
body[usernameField]?.toString(), body[passwordField]?.toString());
body[usernameField].toString(), body[passwordField].toString());
}
//}
}
// User authentication succeeded can return Map(one element), User(non null) or true
if (verificationResult != null && verificationResult != false) {
if (verificationResult is Map && verificationResult.isNotEmpty) {
return verificationResult;
} else if (verificationResult is! Map) {
return verificationResult;
}
}
// Force basic if set
if (forceBasic) {
//res.headers['www-authenticate'] = 'Basic realm="$realm"';
res
..statusCode = 401
..headers['www-authenticate'] = 'Basic realm="$realm"';
await res.close();
return null;
}
// Redirect failed authentication
if (localOptions.failureRedirect != null &&
localOptions.failureRedirect!.isNotEmpty) {
await res.redirect(localOptions.failureRedirect, code: 401);
return null;
}
_log.info('Not authenticated');
throw ProtevusHttpException.notAuthenticated();
/*
if (verificationResult is Map && verificationResult.isEmpty) {
if (localOptions.failureRedirect != null &&
localOptions.failureRedirect!.isNotEmpty) {
await res.redirect(localOptions.failureRedirect, code: 401);
if (verificationResult == null ||
(verificationResult is Map && verificationResult.isEmpty)) {
if (_options.failureRedirect != null &&
_options.failureRedirect!.isNotEmpty) {
await res.redirect(_options.failureRedirect, code: 401);
return null;
}
@ -129,10 +105,7 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
return verificationResult;
} else {
_log.info('Not authenticated');
throw ProtevusHttpException.notAuthenticated();
throw AngelHttpException.notAuthenticated();
}
*/
}
bool _validateString(String? str) => str != null && str.isNotEmpty;
}

View file

@ -1,10 +1,10 @@
import 'dart:async';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'options.dart';
/// A function that handles login and signup for an Protevus application.
/// A function that handles login and signup for an Angel application.
abstract class AuthStrategy<User> {
/// Authenticates or rejects an incoming user.
FutureOr<User?> authenticate(RequestContext req, ResponseContext res,
[ProtevusAuthOptions<User>? options]);
[AngelAuthOptions<User>? options]);
}

View file

@ -1,35 +1,22 @@
name: protevus_auth
description: A complete authentication plugin for Protevus. Includes support for stateless JWT tokens, Basic Auth, and more.
version: 8.2.0
homepage: https://protevus-framework.web.app/
repository: https://github.com/dart-backend/protevus/tree/master/packages/auth
name: angel3_auth
description: A complete authentication plugin for Angel3. Includes support for stateless JWT tokens, Basic Auth, and more.
version: 4.1.2
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth
environment:
sdk: '>=3.3.0 <4.0.0'
sdk: '>=2.12.0 <3.0.0'
dependencies:
protevus_framework: ^8.0.0
charcode: ^1.3.0
collection: ^1.17.0
angel3_framework: ^4.2.0
charcode: ^1.2.0
collection: ^1.15.0
crypto: ^3.0.0
http_parser: ^4.0.0
meta: ^1.9.0
quiver: ^3.2.0
logging: ^1.2.0
meta: ^1.3.0
quiver: ^3.0.0
logging: ^1.0.0
dev_dependencies:
protevus_container: ^8.0.0
http: ^1.0.0
angel3_container: ^3.1.0
http: ^0.13.1
io: ^1.0.0
test: ^1.24.0
lints: ^4.0.0
# dependency_overrides:
# protevus_container:
# path: ../container/angel_container
# protevus_framework:
# path: ../framework
# protevus_http_exception:
# path: ../http_exception
# protevus_model:
# path: ../model
# protevus_route:
# path: ../route
# protevus_mock_request:
# path: ../mock_request
test: ^1.17.4
lints: ^1.0.0

View file

@ -1,4 +1,4 @@
import 'package:protevus_auth/src/auth_token.dart';
import 'package:angel3_auth/src/auth_token.dart';
import 'package:crypto/crypto.dart';
import 'package:test/test.dart';

View file

@ -1,8 +1,8 @@
import 'dart:io';
import 'package:protevus_auth/protevus_auth.dart';
import 'package:protevus_container/mirrors.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:angel3_container/mirrors.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
import 'dart:convert';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:http/http.dart' as http;
@ -16,7 +16,7 @@ class User extends Model {
User({this.username, this.password});
static User parse(Map<String, dynamic> map) {
static User parse(Map map) {
return User(
username: map['username'] as String?,
password: map['password'] as String?,
@ -35,27 +35,26 @@ class User extends Model {
}
void main() {
late Protevus app;
late ProtevusHttp angelHttp;
ProtevusAuth<User> auth;
late Angel app;
late AngelHttp angelHttp;
AngelAuth<User> auth;
http.Client? client;
HttpServer server;
String? url;
String? encodedAuth;
setUp(() async {
hierarchicalLoggingEnabled = true;
app = Protevus(reflector: MirrorsReflector());
angelHttp = ProtevusHttp(app);
app = Angel(reflector: MirrorsReflector());
angelHttp = AngelHttp(app);
app.use('/users', MapService());
var oldErrorHandler = app.errorHandler;
app.errorHandler = (e, req, res) {
app.logger.severe(e.message, e, e.stackTrace ?? StackTrace.current);
app.logger?.severe(e.message, e, e.stackTrace ?? StackTrace.current);
return oldErrorHandler(e, req, res);
};
app.logger = Logger('protevus_auth')
app.logger = Logger('angel_auth')
..level = Level.FINEST
..onRecord.listen((rec) {
print(rec);
@ -73,7 +72,7 @@ void main() {
.findService('users')
?.create({'username': 'jdoe1', 'password': 'password'});
auth = ProtevusAuth<User>(
auth = AngelAuth<User>(
serializer: (u) => u.id ?? '',
deserializer: (id) async =>
await app.findService('users')?.read(id) as User);
@ -87,18 +86,18 @@ void main() {
var users = await app
.findService('users')
?.index()
.then((it) => it.map<User>((m) => User.parse(m)).toList());
.then((it) => it.map<User>((m) => User.parse(m as Map)).toList());
var result = users?.firstWhereOrNull(
(user) => user.username == username && user.password == password);
return Future.value(result);
}, allowBasic: true);
});
app.post(
'/login',
auth.authenticate('local',
ProtevusAuthOptions(callback: (req, res, token) {
AngelAuthOptions(callback: (req, res, token) {
res
..write('Hello!')
..close();
@ -117,8 +116,6 @@ void main() {
auth.authenticate('local'),
);
encodedAuth = base64.encode(utf8.encode('jdoe1:password'));
client = http.Client();
server = await angelHttp.startServer();
url = 'http://${server.address.address}:${server.port}';
@ -134,7 +131,7 @@ void main() {
test('login', () async {
final response = await client!.post(Uri.parse('$url/login'),
headers: {'Authorization': 'Basic $encodedAuth'});
body: {'username': 'jdoe1', 'password': 'password'});
print('Response: ${response.body}');
expect(response.body, equals('Hello!'));
},

View file

@ -1,4 +1,4 @@
import 'package:protevus_auth/protevus_auth.dart';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:test/test.dart';
void main() {

View file

@ -1,22 +1,19 @@
import 'dart:async';
import 'package:protevus_auth/protevus_auth.dart';
import 'package:protevus_container/mirrors.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart';
import 'package:test/test.dart';
final ProtevusAuth<Map<String, String>> auth =
ProtevusAuth<Map<String, String>>(
serializer: (user) async => '1337',
deserializer: (id) async => sampleUser);
//var headers = <String, String>{'accept': 'application/json'};
var localOpts = ProtevusAuthOptions<Map<String, String>>(
final AngelAuth<Map<String, String>> auth = AngelAuth<Map<String, String>>(
serializer: (user) async => '1337', deserializer: (id) async => sampleUser);
var headers = <String, String>{'accept': 'application/json'};
var localOpts = AngelAuthOptions<Map<String, String>>(
failureRedirect: '/failure', successRedirect: '/success');
var localOpts2 =
ProtevusAuthOptions<Map<String, String>>(canRespondWithJson: false);
AngelAuthOptions<Map<String, String>>(canRespondWithJson: false);
Map<String, String> sampleUser = {'hello': 'world'};
@ -28,27 +25,26 @@ Future<Map<String, String>> verifier(String? username, String? password) async {
}
}
Future wireAuth(Protevus app) async {
Future wireAuth(Angel app) async {
//auth.serializer = (user) async => 1337;
//auth.deserializer = (id) async => sampleUser;
auth.strategies['local'] = LocalAuthStrategy(verifier, allowBasic: true);
auth.strategies['local'] = LocalAuthStrategy(verifier);
await app.configure(auth.configureServer);
}
void main() async {
Protevus app;
late ProtevusHttp angelHttp;
Angel app;
late AngelHttp angelHttp;
late http.Client client;
String? url;
String? basicAuthUrl;
setUp(() async {
client = http.Client();
app = Protevus(reflector: MirrorsReflector());
angelHttp = ProtevusHttp(app, useZone: false);
app = Angel();
angelHttp = AngelHttp(app, useZone: false);
await app.configure(wireAuth);
app.get('/hello', (req, res) {
// => 'Woo auth'
return 'Woo auth';
@ -92,21 +88,19 @@ void main() async {
});
test('successRedirect', () async {
//var postData = {'username': 'username', 'password': 'password'};
var encodedAuth = base64.encode(utf8.encode('username:password'));
var postData = {'username': 'username', 'password': 'password'};
var response = await client.post(Uri.parse('$url/login'),
headers: {'Authorization': 'Basic $encodedAuth'});
body: json.encode(postData),
headers: {'content-type': 'application/json'});
expect(response.statusCode, equals(302));
expect(response.headers['location'], equals('/success'));
});
test('failureRedirect', () async {
//var postData = {'username': 'password', 'password': 'username'};
var encodedAuth = base64.encode(utf8.encode('password:username'));
var postData = {'username': 'password', 'password': 'username'};
var response = await client.post(Uri.parse('$url/login'),
headers: {'Authorization': 'Basic $encodedAuth'});
body: json.encode(postData),
headers: {'content-type': 'application/json'});
print('Status Code: ${response.statusCode}');
print(response.headers);
print(response.body);

View file

@ -1,17 +1,17 @@
import 'dart:io';
import 'package:protevus_auth/protevus_auth.dart';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:test/test.dart';
const Duration threeDays = Duration(days: 3);
void main() {
late Cookie defaultCookie;
var auth = ProtevusAuth(
var auth = AngelAuth(
secureCookies: true,
cookieDomain: 'SECURE',
jwtLifeSpan: threeDays.inMilliseconds,
serializer: (u) => u,
serializer: (u) => u as String,
deserializer: (u) => u);
setUp(() => defaultCookie = Cookie('a', 'b'));

View file

@ -1,40 +1,5 @@
# Change Log
## 8.2.0
* Require Dart >= 3.3
* Updated `lints` to 4.0.0
## 8.1.1
* Updated repository link
## 8.1.0
* Updated `lints` to 3.0.0
* Fixed linter warnings
## 8.0.0
* Require Dart >= 3.0
* Issue: `oauth2` does not support `http` 1.0.0
## 7.0.1
* Updated example
## 7.0.0
* Require Dart >= 2.17
## 6.0.0
* Require Dart >= 2.16
## 5.0.0
* Skipped release
## 4.1.0
* Updated linter to `package:lints`
@ -49,15 +14,15 @@
## 4.0.0
* Migrated to support Dart >= 2.12 NNBD
* Migrated to support Dart SDK 2.12.x NNBD
## 3.0.0
* Migrated to work with Dart >= 2.12.x Non NNBD
* Migrated to work with Dart SDK 2.12.x Non NNBD
## 2.1.0
* Protevus 2 + Dart 2 update
* Angel 2 + Dart 2 update
* Support for handling errors + rejections.
* Use `ExternalAuthOptions`.
@ -67,8 +32,8 @@
## 2.0.0
* Protevus 2 + Dart 2 updates.
* Angel 2 + Dart 2 updates.
## 1.0.2
Added `getParameters` to `ProtevusOAuth2Options`.
Added `getParameters` to `AngelOAuth2Options`.

View file

@ -1,18 +1,18 @@
# Protevus OAuth2 Handler
# Angel3 OAuth2 Handler
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/protevus_auth_oauth2?include_prereleases)
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_auth_oauth2?include_prereleases)
![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)(<https://dart.dev/null-safety>)
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
[![License](https://img.shields.io/github/license/dart-backend/protevus)](https://github.com/dart-backend/protevus/tree/master/packages/auth_oauth2/LICENSE)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/auth_oauth2/LICENSE)
Protevus library for authenticating users with remote identity providers via OAuth2, i.e. Facebook, Google, Azure AD, etc.
Angel3 library for authenticating users with remote identity providers via OAuth2, i.e. Facebook, Google, Azure AD, etc.
## Usage
First, create an options object:
```dart
configureServer(Protevus app) async {
configureServer(Angel app) async {
// Load from a Map, i.e. app config:
var opts = ExternalAuthOptions.fromMap(app.configuration['auth0'] as Map);
@ -55,7 +55,7 @@ OAuth2Verifier oauth2verifier(Service<User> userService) {
Now, initialize an `OAuth2Strategy`, using the options and verifier. You'll also need to provide a name for this instance of the strategy. Consider using the name of the remote authentication provider (ex. `facebook`).
```dart
configureServer(Protevus app) {
configureServer(Angel app) {
auth.strategies['github'] = OAuth2Strategy(
options,
authorizationEndpoint,
@ -71,17 +71,17 @@ configureServer(Protevus app) {
}
```
Lastly, connect it to an `ProtevusAuth` instance, and wire it up to an `Protevus` server. Set up two routes:
Lastly, connect it to an `AngelAuth` instance, and wire it up to an `Angel` server. Set up two routes:
1. Redirect users to the external provider
2. Acts as a callback and handles an access code
In the case of the callback route, you may want to display an HTML page that closes a popup window. In this case, use `confirmPopupAuthentication`, which is bundled with `package:protevus_auth`, as a `callback` function:
In the case of the callback route, you may want to display an HTML page that closes a popup window. In this case, use `confirmPopupAuthentication`, which is bundled with `package:angel3_auth`, as a `callback` function:
```dart
configureServer(Protevus app) async {
configureServer(Angel app) async {
// ...
var auth = ProtevusAuth<User>();
var auth = AngelAuth<User>();
auth.strategies['github'] = oauth2Strategy;
// Redirect
@ -90,7 +90,7 @@ configureServer(Protevus app) async {
// Callback
app.get('/auth/github/callback', auth.authenticate(
'github',
ProtevusAuthOptions(callback: confirmPopupAuthentication())
AngelAuthOptions(callback: confirmPopupAuthentication())
));
// Connect the plug-in!!!
@ -103,7 +103,7 @@ configureServer(Protevus app) async {
This package should work out-of-the-box for most OAuth2 providers, such as Github or Dropbox. However, if your OAuth2 scopes are separated by a delimiter other than the default (`' '`), you can add it in the `OAuth2Strategy` constructor:
```dart
configureServer(Protevus app) async {
configureServer(Angel app) async {
OAuth2Strategy(..., delimiter: ' ');
}
```

View file

@ -1,8 +1,8 @@
import 'dart:convert';
import 'package:protevus_auth/protevus_auth.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
import 'package:protevus_auth_oauth2/protevus_auth_oauth2.dart';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
import 'package:angel3_auth_oauth2/angel3_auth_oauth2.dart';
import 'package:http_parser/http_parser.dart';
import 'package:logging/logging.dart';
@ -33,9 +33,9 @@ Map<String, dynamic> parseParamsFromGithub(MediaType contentType, String body) {
void main() async {
// Create the server instance.
var app = Protevus();
var http = ProtevusHttp(app);
app.logger = Logger('protevus')
var app = Angel();
var http = AngelHttp(app);
app.logger = Logger('angel')
..onRecord.listen((rec) {
print(rec);
if (rec.error != null) print(rec.error);
@ -47,7 +47,7 @@ void main() async {
var mappedUserService = userService.map(User.parse, User.serialize);
// Set up the authenticator plugin.
var auth = ProtevusAuth<User>(
var auth = AngelAuth<User>(
serializer: (user) async => user.id ?? '',
deserializer: (id) => mappedUserService.read(id),
jwtKey: 'oauth2 example secret',
@ -94,12 +94,12 @@ void main() async {
app.get(
'/auth/github/callback',
auth.authenticate('github',
ProtevusAuthOptions(callback: (req, res, jwt) async {
AngelAuthOptions(callback: (req, res, jwt) async {
// In real-life, you might include a pop-up callback script.
//
// Use `confirmPopupAuthentication`, which is bundled with
// `package:angel_auth`.
var user = req.container!.make<User>();
var user = req.container!.make<User>()!;
res.write('Your user info: ${user.toJson()}\n\n');
res.write('Your JWT: $jwt');
await res.close();
@ -113,9 +113,12 @@ void main() async {
}
class User extends Model {
@override
String? id;
int? githubId;
User({super.id, this.githubId});
User({this.id, this.githubId});
static User parse(Map<String, dynamic> map) =>
User(id: map['id'] as String?, githubId: map['github_id'] as int?);

View file

@ -1,12 +1,12 @@
library protevus_auth_oauth2;
library angel3_auth_oauth2;
import 'dart:async';
import 'package:protevus_auth/protevus_auth.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:http_parser/http_parser.dart';
import 'package:oauth2/oauth2.dart' as oauth2;
/// An Protevus [AuthStrategy] that signs users in via a third-party service that speaks OAuth 2.0.
/// An Angel [AuthStrategy] that signs users in via a third-party service that speaks OAuth 2.0.
class OAuth2Strategy<User> implements AuthStrategy<User> {
/// A callback that uses the third-party service to authenticate a [User].
///
@ -47,7 +47,7 @@ class OAuth2Strategy<User> implements AuthStrategy<User> {
@override
FutureOr<User?> authenticate(RequestContext req, ResponseContext res,
[ProtevusAuthOptions<User>? options]) async {
[AngelAuthOptions<User>? options]) async {
if (options != null) {
var result = await authenticateCallback(req, res, options);
if (result is User) {
@ -71,7 +71,7 @@ class OAuth2Strategy<User> implements AuthStrategy<User> {
/// The endpoint that is invoked by the third-party after successful authentication.
Future<dynamic> authenticateCallback(RequestContext req, ResponseContext res,
[ProtevusAuthOptions? options]) async {
[AngelAuthOptions? options]) async {
var grant = _createGrant();
grant.getAuthorizationUrl(this.options.redirectUri,
scopes: this.options.scopes);

View file

@ -1,30 +1,15 @@
name: protevus_auth_oauth2
version: 8.2.0
description: Protevus library for authenticating users with external identity providers via OAuth2.
homepage: https://protevus-framework.web.app/
repository: https://github.com/dart-backend/protevus/tree/master/packages/auth_oauth2
name: angel3_auth_oauth2
version: 4.1.0
description: Angel3 library for authenticating users with external identity providers via OAuth2.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth_oauth2
environment:
sdk: '>=3.3.0 <4.0.0'
sdk: '>=2.12.0 <3.0.0'
dependencies:
protevus_auth: ^8.0.0
protevus_framework: ^8.0.0
angel3_auth: ^4.1.0
angel3_framework: ^4.2.0
http_parser: ^4.0.0
oauth2: ^2.0.0
dev_dependencies:
logging: ^1.2.0
lints: ^4.0.0
# dependency_overrides:
# protevus_container:
# path: ../container/angel_container
# protevus_framework:
# path: ../framework
# protevus_http_exception:
# path: ../http_exception
# protevus_model:
# path: ../model
# protevus_route:
# path: ../route
# protevus_mock_request:
# path: ../mock_request
# protevus_auth:
# path: ../auth
logging: ^1.0.1
lints: ^1.0.0

View file

@ -1,36 +1,16 @@
# Change Log
## 8.0.0
* Require Dart >= 3.3
* Updated `oauth1` to `belatuk_oauth1`
* Updated `lints` to 4.0.0
* Updated repository link
* Fixed linter warnings
## 7.0.0
* Require Dart >= 2.17
## 6.0.0
* Require Dart >= 2.16
## 5.0.0
* Skipped release
## 4.0.0
* Migrated to support Dart >= 2.12 NNBD
* Migrated to support Dart SDK 2.12.x NNBD
## 3.0.0
* Migrated to work with Dart >= 2.12 Non NNBD
* Migrated to work with Dart SDK 2.12.x Non NNBD
## 2.0.0
* Protevus 2 + Dart 2 suppport.
* Angel 2 + Dart 2 suppport.
* Use `package:twitter` instead of `package:twit`.
* Add `TwitterAuthorizationException`.
* Add `onError` callback.

View file

@ -1,29 +1,21 @@
BSD 3-Clause License
MIT License (MIT)
Copyright (c) 2023, dukefirehawk.com
All rights reserved.
Copyright (c) 2021 dukefirehawk.com
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
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:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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.

View file

@ -1,11 +1,10 @@
# Protevus Twitter OAuth1
# Angel3 Auth Twitter
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/protevus_auth_twitter?include_prereleases)
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_auth_twitter?include_prereleases)
![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](<https://dart.dev/null-safety>)
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
[![License](https://img.shields.io/github/license/dart-backend/protevus)](https://github.com/dart-backend/protevus/tree/master/packages/auth_twitter/LICENSE)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/auth_twitter/LICENSE)
**Not ready for release**
Protevus authentication strategy using Twitter OAuth 1.0a.
Angel3 authentication strategy for Twitter login.
See the [example](example/example.dart);
See the [example](example/server.dart);

View file

@ -1,9 +1,9 @@
import 'dart:convert';
import 'dart:io';
import 'package:protevus_auth/protevus_auth.dart';
import 'package:protevus_auth_twitter/protevus_auth_twitter.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
import 'package:angel_auth/angel_auth.dart';
import 'package:angel_auth_twitter/angel_auth_twitter.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
import 'package:logging/logging.dart';
class _User {
@ -15,9 +15,9 @@ class _User {
}
void main() async {
var app = Protevus();
var http = ProtevusHttp(app);
var auth = ProtevusAuth<_User>(
var app = Angel();
var http = AngelHttp(app);
var auth = AngelAuth<_User>(
jwtKey: 'AUTH_TWITTER_SECRET',
allowCookie: false,
serializer: (user) async => user.handle,
@ -38,7 +38,7 @@ void main() async {
'http://localhost:3000/auth/twitter/callback',
),
(twit, req, res) async {
var response = await twit.client.get(Uri.parse(
var response = await twit.twitterClient.get(Uri.parse(
'https://api.twitter.com/1.1/account/verify_credentials.json'));
var userData = json.decode(response.body) as Map;
return _User(userData['screen_name'] as String);
@ -53,13 +53,15 @@ void main() async {
},
);
app.get('/', auth.authenticate('twitter'));
app
..fallback(auth.decodeJwt)
..get('/', auth.authenticate('twitter'));
app.get(
'/auth/twitter/callback',
auth.authenticate(
'twitter',
ProtevusAuthOptions(
AngelAuthOptions(
callback: (req, res, jwt) {
return res.redirect('/home?token=$jwt');
},
@ -72,7 +74,7 @@ void main() async {
chain([
requireAuthentication<_User>(),
(req, res) {
var user = req.container!.make<_User>();
var user = req.container.make<_User>();
res.write('Your Twitter handle is ${user.handle}');
return false;
},

View file

@ -1,13 +1,11 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:protevus_auth/protevus_auth.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel_auth/angel_auth.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:http/http.dart' as http;
import 'package:oauth/oauth.dart' as oauth;
import 'package:path/path.dart' as p;
//import 'package:oauth1/oauth1.dart' as oauth;
import 'package:belatuk_oauth1/belatuk_oauth1.dart' as oauth;
import 'package:dart_twitter_api/twitter_api.dart';
import 'package:twitter/twitter.dart';
/// Authenticates users by connecting to Twitter's API.
class TwitterStrategy<User> extends AuthStrategy<User> {
@ -17,7 +15,7 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
/// A callback that uses Twitter to authenticate a [User].
///
/// As always, return `null` if authentication fails.
final FutureOr<User> Function(TwitterApi, RequestContext, ResponseContext)
final FutureOr<User> Function(Twitter, RequestContext, ResponseContext)
verifier;
/// A callback that is triggered when an OAuth2 error occurs (i.e. the user declines to login);
@ -27,47 +25,17 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
/// The root of Twitter's API. Defaults to `'https://api.twitter.com'`.
final Uri baseUrl;
oauth.Client? _client;
oauth.Client _client;
/// The underlying [oauth.Client] used to query Twitter.
oauth.Client get client => _client!;
oauth.Client get client => _client;
TwitterStrategy(this.options, this.verifier, this.onError,
{http.BaseClient? client, Uri? baseUrl})
{http.BaseClient client, Uri baseUrl})
: baseUrl = baseUrl ?? Uri.parse('https://api.twitter.com') {
// define platform (server)
final oauth.Platform platform = oauth.Platform(
'$baseUrl/oauth/request_token', // temporary credentials request
'$baseUrl/oauth/authorize', // resource owner authorization
'$baseUrl/oauth/access_token', // token credentials request
oauth.SignatureMethods.hmacSha1 // signature method
);
// define client credentials (consumer keys)
final oauth.ClientCredentials clientCredentials =
oauth.ClientCredentials(options.clientId, options.clientSecret);
// create Authorization object with client credentials and platform definition
final oauth.Authorization auth =
oauth.Authorization(clientCredentials, platform);
// request temporary credentials (request tokens)
auth.requestTemporaryCredentials('oob').then((res) {
// redirect to authorization page
print(
"Open with your browser: ${auth.getResourceOwnerAuthorizationURI(res.credentials.token)}");
// get verifier (PIN)
stdout.write("PIN: ");
String verifier = stdin.readLineSync() ?? '';
// request token credentials (access tokens)
return auth.requestTokenCredentials(res.credentials, verifier);
}).then((res) {
// create Client object
_client = oauth.Client(
platform.signatureMethod, clientCredentials, res.credentials);
});
var tokens = oauth.Tokens(
consumerId: options.clientId, consumerKey: options.clientSecret);
_client = oauth.Client(tokens, client: client);
}
/// Handle a response from Twitter.
@ -92,7 +60,7 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
/// Get an access token.
Future<Map<String, String>> getAccessToken(String token, String verifier) {
return client.post(
return _client.post(
baseUrl.replace(path: p.join(baseUrl.path, 'oauth/access_token')),
headers: {
'accept': 'application/json'
@ -107,7 +75,7 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
/// Get a request token.
Future<Map<String, String>> getRequestToken() {
return client.post(
return _client.post(
baseUrl.replace(path: p.join(baseUrl.path, 'oauth/request_token')),
headers: {
'accept': 'application/json'
@ -118,8 +86,8 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
}
@override
Future<User?> authenticate(RequestContext req, ResponseContext res,
[ProtevusAuthOptions? options]) async {
Future<User> authenticate(RequestContext req, ResponseContext res,
[AngelAuthOptions options]) async {
try {
if (options != null) {
var result = await authenticateCallback(req, res, options);
@ -139,14 +107,14 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
}
} on TwitterAuthorizationException catch (e) {
var result = await onError(e, req, res);
await req.app?.executeHandler(result, req, res);
await req.app.executeHandler(result, req, res);
await res.close();
return null;
}
}
Future authenticateCallback(RequestContext req, ResponseContext res,
ProtevusAuthOptions options) async {
Future authenticateCallback(
RequestContext req, ResponseContext res, AngelAuthOptions options) async {
try {
if (req.queryParameters.containsKey('denied')) {
throw TwitterAuthorizationException(
@ -156,13 +124,8 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
var token = req.queryParameters['oauth_token'] as String;
var verifier = req.queryParameters['oauth_verifier'] as String;
var loginData = await getAccessToken(token, verifier);
var twitter = TwitterApi(
client: TwitterClient(
consumerKey: this.options.clientId,
consumerSecret: this.options.clientSecret,
token: loginData['oauth_token'] ?? '',
secret: loginData['oauth_token_secret'] ?? ''));
var twitter = Twitter(this.options.clientId, this.options.clientSecret,
loginData['oauth_token'], loginData['oauth_token_secret']);
return await this.verifier(twitter, req, res);
} on TwitterAuthorizationException catch (e) {
return await onError(e, req, res);

View file

@ -1,34 +1,19 @@
name: "protevus_auth_twitter"
description: Protevus authentication strategy for Twitter login. Auto-signs requests.
version: 8.0.0
homepage: https://protevus-framework.web.app/
repository: https://github.com/dart-backend/protevus/tree/master/packages/auth_twitter
name: "angel3_auth_twitter"
version: 3.0.0
description: "package:angel3_auth strategy for Twitter login. Auto-signs requests."
homepage: "https://github.com/angel-dart/auth_twitter.git"
publish_to: none
environment:
sdk: ">=3.3.0 <4.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
protevus_auth: ^8.0.0
protevus_framework: ^8.0.0
http: ^1.0.0
path: ^1.9.0
belatuk_oauth1: ^3.0.0
dart_twitter_api: ^0.5.6+1
angel_auth:
angel_framework:
http: ^0.13.0
path: ^1.0.0
twitter:
git:
url: https://github.com/dukefirehawk/twitter.dart.git
ref: sdk-2.12.x
dev_dependencies:
logging: ^1.2.0
lints: ^4.0.0
dependency_overrides:
http: ^1.0.0
# protevus_container:
# path: ../container/angel_container
# protevus_framework:
# path: ../framework
# protevus_http_exception:
# path: ../http_exception
# protevus_model:
# path: ../model
# protevus_route:
# path: ../route
# protevus_mock_request:
# path: ../mock_request
# protevus_auth:
# path: ../auth
logging: ^1.0.0
pedantic: ^1.11.0

64
packages/body_parser/.gitignore vendored Normal file
View file

@ -0,0 +1,64 @@
# Created by .ignore support plugin (hsz.mobi)
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Dart template
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
.packages
.pub/
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/

View file

@ -0,0 +1,4 @@
language: dart
dart:
- dev
- stable

View file

@ -0,0 +1,30 @@
# Change Log
## 2.1.2
* Final release. Replaced by `belatuk_body_parser` package.
## 2.1.1
* Fixed calling deprecated methods in unit test
## 2.1.0
* Replaced `http_server` with `belatuk_http_server`
## 2.0.1
* Fixed source code formating warning
* Updated README
## 2.0.0
* Migrated to support Dart SDK 2.12.x NNBD
## 1.1.1
* Dart 2 updates; should fix Angel in Travis.
## 1.1.0
* Add `parseBodyFromStream`

View file

@ -0,0 +1,21 @@
MIT License (MIT)
Copyright (c) 2021 dukefirehawk.com
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.

View file

@ -0,0 +1,77 @@
# Angel3 Body Parser
[![version](https://img.shields.io/badge/pub-v2.1.2-brightgreen)](https://pub.dartlang.org/packages/angel3_body_parser)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/body_parser/LICENSE)
**DEPRECATED: Replaced by [`belatuk_body_parser`](https://pub.dartlang.org/packages/belatuk_body_parser) package**
Parse request bodies and query strings in Dart, as well multipart/form-data uploads. No external dependencies required.
This is the request body parser powering the [Angel3 framework](https://github.com/dukefirehawk/angel). If you are looking for a server-side solution with dependency injection, WebSockets, and more, then I highly recommend it as your first choice. Bam!
## Contents
- [Angel3 Body Parser](#angel3-body-parser)
- [Contents](#contents)
- [About](#about)
- [Installation](#installation)
- [Usage](#usage)
- [Custom Body Parsing](#custom-body-parsing)
### About
I needed something like Express.js's `body-parser` module, so I made it here. It fully supports JSON requests. x-www-form-urlencoded fully supported, as well as query strings. You can also include arrays in your query, in the same way you would for a PHP application. Full file upload support will also be present by the production 1.0.0 release.
A benefit of this is that primitive types are automatically deserialized correctly. As in, if you have a `hello=1.5` request, then `body['hello']` will equal `1.5` and not `'1.5'`. A very semantic difference, yes, but it relieves stress in my head.
### Installation
To install Body Parser for your Dart project, simply add body_parser to your pub dependencies.
dependencies:
angel3_body_parser: ^2.1.0
### Usage
Body Parser exposes a simple class called `BodyParseResult`. You can easily parse the query string and request body for a request by calling `Future<BodyParseResult> parseBody`.
```dart
import 'dart:convert';
import 'package:angel3_body_parser/angel3_body_parser.dart';
main() async {
// ...
await for (HttpRequest request in server) {
request.response.write(JSON.encode(await parseBody(request).body));
await request.response.close();
}
}
```
You can also use `buildMapFromUri(Map, String)` to populate a map from a URL encoded string.
This can easily be used with a library like [Angel3 JSON God](https://pub.dev/packages/angel3_json_god) to build structured JSON/REST APIs. Add validation and you've got an instant backend.
```dart
MyClass create(HttpRequest request) async {
return god.deserialize(await parseBody(request).body, MyClass);
}
```
### Custom Body Parsing
In cases where you need to parse unrecognized content types, `body_parser` won't be of any help to you on its own. However, you can use the `originalBuffer` property of a `BodyParseResult` to see the original request buffer. To get this functionality, pass `storeOriginalBuffer` as `true` when calling `parseBody`.
For example, if you wanted to [parse GraphQL queries within your server](https://github.com/dukefirehawk/graphql_dart)...
```dart
app.get('/graphql', (req, res) async {
if (req.headers.contentType.mimeType == 'application/graphql') {
var graphQlString = String.fromCharCodes(req.originalBuffer);
// ...
}
});
```

View file

@ -0,0 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -0,0 +1,61 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:http_parser/http_parser.dart';
import 'package:angel3_body_parser/angel3_body_parser.dart';
void main() async {
var address = '127.0.0.1';
var port = 3000;
var futures = <Future>[];
for (var i = 1; i < Platform.numberOfProcessors; i++) {
futures.add(Isolate.spawn(start, [address, port, i]));
}
await Future.wait(futures).then((_) {
print('All instances started.');
print(
'Test with "wrk -t12 -c400 -d30s -s ./example/post.lua http://localhost:3000" or similar');
start([address, port, 0]);
});
}
void start(List args) {
var address = InternetAddress(args[0] as String);
var port = 8080;
if (args[1] is int) {
args[1];
}
var id = 0;
if (args[2] is int) {
args[2];
}
HttpServer.bind(address, port, shared: true).then((server) {
server.listen((request) async {
// ignore: deprecated_member_use
var body = await defaultParseBody(request);
request.response
..headers.contentType = ContentType('application', 'json')
..write(json.encode(body.body));
await request.response.close();
});
print(
'Server #$id listening at http://${server.address.address}:${server.port}');
});
}
Future<BodyParseResult> defaultParseBody(HttpRequest request,
{bool storeOriginalBuffer = false}) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: storeOriginalBuffer);
}

View file

@ -0,0 +1,6 @@
-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header
wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

View file

@ -0,0 +1,6 @@
/// A library for parsing HTTP request bodies and queries.
library angel3_body_parser;
export 'src/body_parse_result.dart';
export 'src/file_upload_info.dart';
export 'src/parse_body.dart';

View file

@ -0,0 +1,28 @@
import 'file_upload_info.dart';
/// A representation of data from an incoming request.
abstract class BodyParseResult {
/// The parsed body.
Map<String?, dynamic> get body;
/// The parsed query string.
Map<String?, dynamic> get query;
/// All files uploaded within this request.
List<FileUploadInfo> get files;
/// The original body bytes sent with this request.
///
/// You must set [storeOriginalBuffer] to `true` to see this.
List<int>? get originalBuffer;
/// If an error was encountered while parsing the body, it will appear here.
///
/// Otherwise, this is `null`.
dynamic get error;
/// If an error was encountered while parsing the body, the call stack will appear here.
///
/// Otherwise, this is `null`.
StackTrace? get stack;
}

View file

@ -0,0 +1,7 @@
import 'file_upload_info.dart';
List<FileUploadInfo> getFileDataFromChunk(
String chunk, String boundary, String fileUploadName, Map body) {
var result = <FileUploadInfo>[];
return result;
}

View file

@ -0,0 +1,17 @@
/// Represents a file uploaded to the server.
class FileUploadInfo {
/// The MIME type of the uploaded file.
String? mimeType;
/// The name of the file field from the request.
String? name;
/// The filename of the file.
String? filename;
/// The bytes that make up this file.
List<int> data;
FileUploadInfo(
{this.mimeType, this.name, this.filename, this.data = const []});
}

View file

@ -0,0 +1,22 @@
import 'dart:convert';
dynamic getValue(String value) {
try {
var numValue = num.parse(value);
if (!numValue.isNaN) {
return numValue;
} else {
return value;
}
} on FormatException {
if (value.startsWith('[') && value.endsWith(']')) {
return json.decode(value);
} else if (value.startsWith('{') && value.endsWith('}')) {
return json.decode(value);
} else if (value.trim().toLowerCase() == 'null') {
return null;
} else {
return value;
}
}
}

View file

@ -0,0 +1,44 @@
import 'get_value.dart';
/// Parses a URI-encoded string into real data! **Wow!**
///
/// Whichever map you provide will be automatically populated from the urlencoded body string you provide.
void buildMapFromUri(Map map, String body) {
var parseArrayRgx = RegExp(r'^(.+)\[\]$');
for (var keyValuePair in body.split('&')) {
if (keyValuePair.contains('=')) {
var equals = keyValuePair.indexOf('=');
var key = Uri.decodeQueryComponent(keyValuePair.substring(0, equals));
var value = Uri.decodeQueryComponent(keyValuePair.substring(equals + 1));
if (parseArrayRgx.hasMatch(key)) {
Match queryMatch = parseArrayRgx.firstMatch(key)!;
key = queryMatch.group(1)!;
if (!(map[key] is List)) {
map[key] = [];
}
map[key].add(getValue(value));
} else if (key.contains('.')) {
// i.e. map.foo.bar => [map, foo, bar]
var keys = key.split('.');
var targetMap = map[keys[0]] != null ? map[keys[0]] as Map? : {};
map[keys[0]] = targetMap;
for (var i = 1; i < keys.length; i++) {
if (i < keys.length - 1) {
targetMap![keys[i]] = targetMap[keys[i]] ?? {};
targetMap = targetMap[keys[i]] as Map?;
} else {
targetMap![keys[i]] = getValue(value);
}
}
} else {
map[key] = getValue(value);
}
} else {
map[Uri.decodeQueryComponent(keyValuePair)] = true;
}
}
}

View file

@ -0,0 +1,149 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:http_parser/http_parser.dart';
import 'package:belatuk_http_server/belatuk_http_server.dart';
import 'package:mime/mime.dart';
import 'body_parse_result.dart';
import 'file_upload_info.dart';
import 'map_from_uri.dart';
/// Forwards to [parseBodyFromStream].
@deprecated
Future<BodyParseResult> parseBody(HttpRequest request,
{bool storeOriginalBuffer = false}) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: storeOriginalBuffer);
}
/// Grabs data from an incoming request.
///
/// Supports URL-encoded and JSON, as well as multipart/* forms.
/// On a file upload request, only fields with the name **'file'** are processed
/// as files. Anything else is put in the body. You can change the upload file name
/// via the *fileUploadName* parameter. :)
///
/// Use [storeOriginalBuffer] to add the original request bytes to the result.
Future<BodyParseResult> parseBodyFromStream(
Stream<Uint8List> data, MediaType? contentType, Uri requestUri,
{bool storeOriginalBuffer = false}) async {
var result = _BodyParseResultImpl();
Future<Uint8List> getBytes() {
return data
.fold<BytesBuilder>(BytesBuilder(copy: false), (a, b) => a..add(b))
.then((b) => b.takeBytes());
}
Future<String> getBody() {
if (storeOriginalBuffer) {
return getBytes().then((bytes) {
result.originalBuffer = bytes;
return utf8.decode(bytes);
});
} else {
return utf8.decoder.bind(data).join();
}
}
try {
if (contentType != null) {
if (contentType.type == 'multipart' &&
contentType.parameters.containsKey('boundary')) {
Stream<Uint8List> stream;
if (storeOriginalBuffer) {
var bytes = result.originalBuffer = await getBytes();
var ctrl = StreamController<Uint8List>()..add(bytes);
await ctrl.close();
stream = ctrl.stream;
} else {
stream = data;
}
var parts =
MimeMultipartTransformer(contentType.parameters['boundary']!)
.bind(stream)
.map((part) =>
HttpMultipartFormData.parse(part, defaultEncoding: utf8));
await for (HttpMultipartFormData part in parts) {
if (part.isBinary ||
part.contentDisposition.parameters.containsKey('filename')) {
var builder = await part.fold(
BytesBuilder(copy: false),
(BytesBuilder b, d) =>
b..add(d is! String ? (d as List<int>?)! : d.codeUnits));
var upload = FileUploadInfo(
mimeType: part.contentType!.mimeType,
name: part.contentDisposition.parameters['name'],
filename:
part.contentDisposition.parameters['filename'] ?? 'file',
data: builder.takeBytes());
result.files.add(upload);
} else if (part.isText) {
var text = await part.join();
buildMapFromUri(result.body,
'${part.contentDisposition.parameters["name"]}=$text');
}
}
} else if (contentType.mimeType == 'application/json') {
result.body.addAll(
_foldToStringDynamic(json.decode(await getBody()) as Map?)!);
} else if (contentType.mimeType == 'application/x-www-form-urlencoded') {
var body = await getBody();
buildMapFromUri(result.body, body);
} else if (storeOriginalBuffer == true) {
result.originalBuffer = await getBytes();
}
} else {
if (requestUri.hasQuery) {
buildMapFromUri(result.query, requestUri.query);
}
if (storeOriginalBuffer == true) {
result.originalBuffer = await getBytes();
}
}
} catch (e, st) {
result.error = e;
result.stack = st;
}
return result;
}
class _BodyParseResultImpl implements BodyParseResult {
@override
Map<String?, dynamic> body = {};
@override
List<FileUploadInfo> files = [];
@override
List<int>? originalBuffer;
@override
Map<String?, dynamic> query = {};
@override
var error;
@override
StackTrace? stack;
}
Map<String, dynamic>? _foldToStringDynamic(Map? map) {
return map == null
? null
: map.keys.fold<Map<String, dynamic>>(
<String, dynamic>{}, (out, k) => out..[k.toString()] = map[k]);
}

View file

@ -0,0 +1,15 @@
name: angel3_body_parser
version: 2.1.2
description: Parse request bodies and query strings in Dart. Supports JSON, URL-encoded, and multi-part bodies.
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/body_parser
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
http_parser: ^4.0.0
belatuk_http_server: ^2.0.0
mime: ^1.0.0
dev_dependencies:
http: ^0.13.0
test: ^1.17.0
pedantic: ^1.11.0

View file

@ -0,0 +1,156 @@
import 'dart:io';
import 'dart:convert';
import 'package:angel3_body_parser/angel3_body_parser.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:test/test.dart';
import 'server_test.dart';
Future<BodyParseResult> _parseBody(HttpRequest request) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: false);
}
void main() {
HttpServer? server;
String? url;
http.Client? client;
setUp(() async {
server = await HttpServer.bind('127.0.0.1', 0);
server!.listen((HttpRequest request) async {
//Server will simply return a JSON representation of the parsed body
// ignore: deprecated_member_use
request.response.write(jsonEncodeBody(await _parseBody(request)));
await request.response.close();
});
url = 'http://localhost:${server!.port}';
print('Test server listening on $url');
client = http.Client();
});
tearDown(() async {
await server!.close(force: true);
client!.close();
server = null;
url = null;
client = null;
});
test('No upload', () async {
var boundary = 'myBoundary';
var headers = <String, String>{
'content-type': 'multipart/form-data; boundary=$boundary'
};
var postData = '''
--$boundary
Content-Disposition: form-data; name="hello"
world
--$boundary--
'''
.replaceAll('\n', '\r\n');
print(
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var jsons = json.decode(response.body);
var files = jsons['files'].map((map) {
return map == null
? null
: map.keys.fold<Map<String, dynamic>>(
<String, dynamic>{}, (out, k) => out..[k.toString()] = map[k]);
});
expect(files.length, equals(0));
expect(jsons['body']['hello'], equals('world'));
});
test('Single upload', () async {
var boundary = 'myBoundary';
var headers = <String, String>{
'content-type': ContentType('multipart', 'form-data',
parameters: {'boundary': boundary}).toString()
};
var postData = '''
--$boundary
Content-Disposition: form-data; name="hello"
world
--$boundary
Content-Disposition: form-data; name="file"; filename="app.dart"
Content-Type: application/dart
Hello world
--$boundary--
'''
.replaceAll('\n', '\r\n');
print(
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var jsons = json.decode(response.body);
var files = jsons['files'];
expect(files.length, equals(1));
expect(files[0]['name'], equals('file'));
expect(files[0]['mimeType'], equals('application/dart'));
expect(files[0]['data'].length, equals(11));
expect(files[0]['filename'], equals('app.dart'));
expect(jsons['body']['hello'], equals('world'));
});
test('Multiple upload', () async {
var boundary = 'myBoundary';
var headers = <String, String>{
'content-type': 'multipart/form-data; boundary=$boundary'
};
var postData = '''
--$boundary
Content-Disposition: form-data; name="json"
god
--$boundary
Content-Disposition: form-data; name="num"
14.50000
--$boundary
Content-Disposition: form-data; name="file"; filename="app.dart"
Content-Type: text/plain
Hello world
--$boundary
Content-Disposition: form-data; name="entry-point"; filename="main.js"
Content-Type: text/javascript
function main() {
console.log("Hello, world!");
}
--$boundary--
'''
.replaceAll('\n', '\r\n');
print(
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var jsons = json.decode(response.body);
var files = jsons['files'];
expect(files.length, equals(2));
expect(files[0]['name'], equals('file'));
expect(files[0]['mimeType'], equals('text/plain'));
expect(files[0]['data'].length, equals(11));
expect(files[1]['name'], equals('entry-point'));
expect(files[1]['mimeType'], equals('text/javascript'));
expect(jsons['body']['json'], equals('god'));
expect(jsons['body']['num'], equals(14.5));
});
}

View file

@ -0,0 +1,174 @@
import 'dart:convert';
import 'dart:io' show HttpRequest, HttpServer;
import 'package:angel3_body_parser/angel3_body_parser.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:test/test.dart';
const TOKEN =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIxMjcuMC4wLjEiLCJleHAiOi0xLCJpYXQiOiIyMDE2LTEyLTIyVDEyOjQ5OjUwLjM2MTQ0NiIsImlzcyI6ImFuZ2VsX2F1dGgiLCJzdWIiOiIxMDY2OTQ4Mzk2MDIwMjg5ODM2NTYifQ==.PYw7yUb-cFWD7N0sSLztP7eeRvO44nu1J2OgDNyT060=';
String jsonEncodeBody(BodyParseResult result) {
return json.encode({
'query': result.query,
'body': result.body,
'error': result.error?.toString(),
'files': result.files.map((f) {
return {
'name': f.name,
'mimeType': f.mimeType,
'filename': f.filename,
'data': f.data,
};
}).toList(),
'originalBuffer': result.originalBuffer,
'stack': null, //result.stack.toString(),
});
}
Future<BodyParseResult> _parseBody(HttpRequest request) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: true);
}
void main() {
HttpServer? server;
String? url;
http.Client? client;
setUp(() async {
server = await HttpServer.bind('127.0.0.1', 0);
server!.listen((HttpRequest request) async {
//Server will simply return a JSON representation of the parsed body
request.response.write(
// ignore: deprecated_member_use
jsonEncodeBody(await _parseBody(request)));
await request.response.close();
});
url = 'http://localhost:${server!.port}';
print('Test server listening on $url');
client = http.Client();
});
tearDown(() async {
await server!.close(force: true);
client!.close();
server = null;
url = null;
client = null;
});
group('query string', () {
test('GET Simple', () async {
print('GET $url/?hello=world');
var response = await client!.get(Uri.parse('$url/?hello=world'));
print('Response: ${response.body}');
var result = json.decode(response.body);
expect(result['body'], equals({}));
expect(result['query'], equals({'hello': 'world'}));
expect(result['files'], equals([]));
//expect(result['originalBuffer'], isNull);
});
test('GET Complex', () async {
var postData =
'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz';
print('Body: $postData');
var response = await client!.get(Uri.parse('$url/?$postData'));
print('Response: ${response.body}');
var query = json.decode(response.body)['query'];
expect(query['hello'], equals('world'));
expect(query['nums'][2], equals(2));
expect(query['map'] is Map, equals(true));
expect(query['map']['foo'], equals({'bar': 'baz'}));
});
test('JWT', () async {
var postData = 'token=$TOKEN';
print('Body: $postData');
var response = await client!.get(Uri.parse('$url/?$postData'));
print('Response: ${response.body}');
var query = json.decode(response.body)['query'];
expect(query['token'], equals(TOKEN));
});
});
group('urlencoded', () {
var headers = <String, String>{
'content-type': 'application/x-www-form-urlencoded'
};
test('POST Simple', () async {
print('Body: hello=world');
var response = await client!
.post(Uri.parse(url!), headers: headers, body: 'hello=world');
print('Response: ${response.body}');
var result = json.decode(response.body);
expect(result['query'], equals({}));
expect(result['body'], equals({'hello': 'world'}));
expect(result['files'], equals([]));
expect(result['originalBuffer'], isList);
expect(result['originalBuffer'], isNotEmpty);
});
test('Post Complex', () async {
var postData =
'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz';
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var body = json.decode(response.body)['body'];
expect(body['hello'], equals('world'));
expect(body['nums'][2], equals(2));
expect(body['map'] is Map, equals(true));
expect(body['map']['foo'], equals({'bar': 'baz'}));
});
test('JWT', () async {
var postData = 'token=$TOKEN';
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
var body = json.decode(response.body)['body'];
expect(body['token'], equals(TOKEN));
});
});
group('json', () {
var headers = <String, String>{'content-type': 'application/json'};
test('Post Simple', () async {
var postData = json.encode({'hello': 'world'});
print('Body: $postData');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var result = json.decode(response.body);
expect(result['body'], equals({'hello': 'world'}));
expect(result['query'], equals({}));
expect(result['files'], equals([]));
expect(result['originalBuffer'], allOf(isList, isNotEmpty));
});
test('Post Complex', () async {
var postData = json.encode({
'hello': 'world',
'nums': [1, 2.0, 3 - 1],
'map': {
'foo': {'bar': 'baz'}
}
});
print('Body: $postData');
var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}');
var body = json.decode(response.body)['body'];
expect(body['hello'], equals('world'));
expect(body['nums'][2], equals(2));
expect(body['map'] is Map, equals(true));
expect(body['map']['foo'], equals({'bar': 'baz'}));
});
});
}

4
packages/cache/.travis.yml vendored Normal file
View file

@ -0,0 +1,4 @@
language: dart
dart:
- stable
- dev

View file

@ -1,39 +1,5 @@
# Change Log
## 8.2.0
* Require Dart >= 3.3
* Updated `lints` to 4.0.0
## 8.1.1
* Updated repository link
## 8.1.0
* Updated `lints` to 3.0.0
* Fixed linter warnings
## 8.0.0
* Require Dart >= 3.0
## 7.0.0
* Require Dart >= 2.17
## 6.0.0
* Require Dart >= 2.16
## 5.0.0
* Skipped release
## 4.0.3
* Updated linter to `package:lints`
## 4.0.2
* Updated README
@ -48,11 +14,11 @@
## 4.0.0
* Migrated to support Dart >= 2.12 NNBD
* Migrated to support Dart SDK 2.12.x NNBD
## 3.0.0
* Migrated to work with Dart >= 2.12 Non NNBD
* Migrated to work with Dart SDK 2.12.x Non NNBD
## 2.0.1

View file

@ -1,29 +1,21 @@
BSD 3-Clause License
MIT License (MIT)
Copyright (c) 2021, dukefirehawk.com
All rights reserved.
Copyright (c) 2021 dukefirehawk.com
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
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:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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.

View file

@ -1,15 +1,16 @@
# Protevus HTTP Cache
# Angel3 HTTP Cache
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/protevus_cache?include_prereleases)
[![version](https://img.shields.io/badge/pub-v4.0.2-brightgreen)](https://pub.dev/packages/angel3_cache)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
[![License](https://img.shields.io/github/license/dart-backend/protevus)](https://github.com/dart-backend/protevus/tree/master/packages/cache/LICENSE)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
A service that provides HTTP caching to the response data for [Protevus framework](https://pub.dev/packages/protevus).
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/cache/LICENSE)
A service that provides HTTP caching to the response data for [Angel3 framework](https://pub.dev/packages/angel3).
## `CacheService`
A `Service` class that caches data from one service, storing it in another. An imaginable use case is storing results from MongoDB or another database in Memcache/Redis.
A `Service` class that caches data from one service, storing it in another. An imaginable use case is storing results from MongoDB or another database in MemcacheD/Redis.
## `cacheSerializationResults`
@ -19,18 +20,18 @@ This can improve the performance of sending objects that are complex to serializ
```dart
void main() async {
var app = Protevus()..lazyParseBodies = true;
var app = Angel()..lazyParseBodies = true;
app.use(
'/api/todos',
CacheService(
database: AnonymousService(
index: ([params]) {
print('Fetched directly from the underlying service at ${DateTime.now()}!');
print('Fetched directly from the underlying service at ${new DateTime.now()}!');
return ['foo', 'bar', 'baz'];
},
read: (id, [params]) {
return {id: '$id at ${DateTime.now()}'};
return {id: '$id at ${new DateTime.now()}'};
}
),
),
@ -40,16 +41,16 @@ void main() async {
## `ResponseCache`
A flexible response cache for Protevus.
A flexible response cache for Angel3.
Use this to improve real and perceived response of Web applications, as well as to memorize expensive responses.
Use this to improve real and perceived response of Web applications, as well as to memoize expensive responses.
Supports the `If-Modified-Since` header, as well as storing the contents of response buffers in memory.
To initialize a simple cache:
```dart
Future configureServer(Protevus app) async {
Future configureServer(Angel app) async {
// Simple instance.
var cache = ResponseCache();
@ -81,10 +82,10 @@ Call `invalidate` to remove a resource from a `ResponseCache`.
Some servers expect a reverse proxy or caching layer to support `PURGE` requests. If this is your case, make sure to include some sort of validation (maybe IP-based) to ensure no arbitrary attacker can hack your cache:
```dart
Future configureServer(Protevus app) async {
Future configureServer(Angel app) async {
app.addRoute('PURGE', '*', (req, res) {
if (req.ip != '127.0.0.1')
throw ProtevusHttpException.forbidden();
throw AngelHttpException.forbidden();
return cache.purge(req.uri.path);
});
}

View file

@ -1 +1,4 @@
include: package:lints/recommended.yaml
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,9 +1,9 @@
import 'package:protevus_cache/protevus_cache.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
import 'package:angel3_cache/angel3_cache.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
void main() async {
var app = Protevus();
var app = Angel();
app.use(
'/api/todos',
@ -18,7 +18,7 @@ void main() async {
})),
);
var http = ProtevusHttp(app);
var http = AngelHttp(app);
var server = await http.startServer('127.0.0.1', 3000);
print('Listening at http://${server.address.address}:${server.port}');
}

View file

@ -1,9 +1,9 @@
import 'package:protevus_cache/protevus_cache.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_framework/http.dart';
import 'package:angel3_cache/angel3_cache.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
void main() async {
var app = Protevus();
var app = Angel();
// Cache a glob
var cache = ResponseCache()
@ -21,7 +21,7 @@ void main() async {
// Support purging the cache.
app.addRoute('PURGE', '*', (req, res) {
if (req.ip != '127.0.0.1') {
throw ProtevusHttpException.forbidden();
throw AngelHttpException.forbidden();
}
cache.purge(req.uri!.path);
@ -31,7 +31,7 @@ void main() async {
// The response finalizer that actually saves the content
app.responseFinalizers.add(cache.responseFinalizer);
var http = ProtevusHttp(app);
var http = AngelHttp(app);
var server = await http.startServer('127.0.0.1', 3000);
print('Listening at http://${server.address.address}:${server.port}');
}

View file

@ -1,13 +1,13 @@
import 'dart:async';
import 'dart:io' show HttpDate;
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:pool/pool.dart';
import 'package:logging/logging.dart';
/// A flexible response cache for Protevus.
/// A flexible response cache for Angel.
///
/// Use this to improve real and perceived response of Web applications,
/// as well as to memorize expensive responses.
/// as well as to memoize expensive responses.
class ResponseCache {
/// A set of [Patterns] for which responses will be cached.
///

View file

@ -1,8 +1,8 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
/// An Protevus [Service] that caches data from another service.
/// An Angel [Service] that caches data from another service.
///
/// This is useful for applications of scale, where network latency
/// can have real implications on application performance.
@ -120,7 +120,7 @@ class CacheService<Id, Data> extends Service<Id, Data> {
}
class _CachedItem<Data> {
final dynamic params;
final params;
final DateTime timestamp;
final Data? data;

View file

@ -1,5 +1,5 @@
import 'dart:async';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:angel3_framework/angel3_framework.dart';
/// A middleware that enables the caching of response serialization.
///

View file

@ -1,42 +1,19 @@
name: protevus_cache
version: 8.2.0
description: A service that provides HTTP caching to the response data for Protevus
homepage: https://protevus-framework.web.app/
repository: https://github.com/dart-backend/protevus/tree/master/packages/cache
name: angel3_cache
version: 4.0.2
description: A service that provides HTTP caching to the response data for Angel3
homepage: https://angel3-framework.web.app/
repository: https://github.com/dukefirehawk/angel/tree/angel3/packages/cache
environment:
sdk: '>=3.4.0 <4.0.0'
sdk: '>=2.12.0 <3.0.0'
dependencies:
protevus_framework: ^8.0.0
collection: ^1.17.0
meta: ^1.9.0
angel3_framework: ^4.0.0
collection: ^1.15.0
meta: ^1.4.0
pool: ^1.5.0
logging: ^1.2.0
logging: ^1.0.0
dev_dependencies:
protevus_test: ^8.0.0
angel3_test: ^4.0.0
glob: ^2.0.1
http: ^1.0.0
test: ^1.24.0
lints: ^4.0.0
# dependency_overrides:
# protevus_container:
# path: ../container/angel_container
# protevus_framework:
# path: ../framework
# protevus_http_exception:
# path: ../http_exception
# protevus_model:
# path: ../model
# protevus_route:
# path: ../route
# protevus_mock_request:
# path: ../mock_request
# protevus_test:
# path: ../test
# protevus_websocket:
# path: ../websocket
# protevus_client:
# path: ../client
# protevus_auth:
# path: ../auth
# protevus_validate:
# path: ../validate
http: ^0.13.3
test: ^1.17.5
pedantic: ^1.11.0

View file

@ -1,8 +1,8 @@
import 'dart:async';
import 'dart:io';
import 'package:protevus_cache/protevus_cache.dart';
import 'package:protevus_framework/protevus_framework.dart';
import 'package:protevus_test/protevus_test.dart';
import 'package:angel3_cache/angel3_cache.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_test/angel3_test.dart';
import 'package:http/http.dart' as http;
//import 'package:glob/glob.dart';
import 'package:test/test.dart';
@ -21,7 +21,7 @@ Future<void> main() async {
late http.Response response1, response2;
setUp(() async {
var app = Protevus();
var app = Angel();
var cache = ResponseCache()
..patterns.addAll([
//Glob('/*.txt'), // Requires to create folders and files for testing
@ -53,7 +53,7 @@ Future<void> main() async {
var oldHandler = app.errorHandler;
app.errorHandler = (e, req, res) {
if (e.error == null) {
oldHandler(e, req, res);
return oldHandler(e, req, res);
}
return Zone.current
.handleUncaughtError(e.error as Object, e.stackTrace!);

3
packages/cli/README.md Normal file
View file

@ -0,0 +1,3 @@
# Angel3 CLI
Moved to [`Angel3 CLI Repository`](https://github.com/dukefirehawk/angel3-cli)

View file

@ -69,4 +69,3 @@ com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
.DS_Store

Some files were not shown because too many files have changed in this diff Show more