From 84e6f9bbea12deaa94c5e586750e4a39744d8e2d Mon Sep 17 00:00:00 2001 From: dewanakl Date: Mon, 11 Sep 2023 23:55:43 +0700 Subject: [PATCH 01/25] feat: new update from 2.x --- .env.example | 3 +- app/Error/Error.php | 24 ++++++++++ app/Kernel.php | 42 +++++++++++----- app/Middleware/AuthMiddleware.php | 5 +- app/Middleware/CorsMiddleware.php | 19 ++++---- app/Middleware/CsrfMiddleware.php | 13 +++-- app/Middleware/GuestMiddleware.php | 2 +- app/Middleware/XSSMiddleware.php | 11 ++--- app/Models/User.php | 8 ++++ app/Providers/RouteServiceProvider.php | 10 ---- app/Providers/TranslatorServiceProvide.php | 19 ++++++++ composer.json | 2 +- database/{ => generator}/generator.php | 0 public/index.php | 21 ++++---- resources/lang/id.php | 56 ++++++++++++++++++++++ routes/routes.php | 2 +- saya | 21 ++++---- 17 files changed, 190 insertions(+), 68 deletions(-) create mode 100644 app/Error/Error.php create mode 100644 app/Providers/TranslatorServiceProvide.php rename database/{ => generator}/generator.php (100%) create mode 100644 resources/lang/id.php diff --git a/.env.example b/.env.example index e33ffc3..165b8de 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,7 @@ APP_NAME=Kamu APP_KEY= BASEURL=http://localhost:8000/ DEBUG=true +LOG=true DB_DRIV=mysql DB_HOST=127.0.0.1 @@ -20,4 +21,4 @@ MAIL_FROM_NAME=kamu TIMEZONE=Asia/Jakarta COOKIE=true -COOKIE_LIFETIME=86400 +COOKIE_LIFETIME=120 diff --git a/app/Error/Error.php b/app/Error/Error.php new file mode 100644 index 0000000..d566348 --- /dev/null +++ b/app/Error/Error.php @@ -0,0 +1,24 @@ +path; } /** - * Registrasi service agar bisa dijalankan. + * Kirim errornya lewat class. * - * @return array + * @return string */ - public function services(): array + public function error(): string { - return [ - \App\Providers\AppServiceProvider::class, - \App\Providers\RouteServiceProvider::class, - ]; + return \App\Error\Error::class; } /** * Kumpulan middleware yang dijalankan lebih awal. * - * @return array + * @return array */ public function middlewares(): array { @@ -57,4 +61,18 @@ public function middlewares(): array \App\Middleware\CsrfMiddleware::class ]; } + + /** + * Registrasi service agar bisa dijalankan. + * + * @return array + */ + public function services(): array + { + return [ + \App\Providers\AppServiceProvider::class, + \App\Providers\RouteServiceProvider::class, + \App\Providers\TranslatorServiceProvide::class, + ]; + } } diff --git a/app/Middleware/AuthMiddleware.php b/app/Middleware/AuthMiddleware.php index a85109c..bde12e2 100644 --- a/app/Middleware/AuthMiddleware.php +++ b/app/Middleware/AuthMiddleware.php @@ -3,6 +3,7 @@ namespace App\Middleware; use Closure; +use Core\Auth\Auth; use Core\Http\Request; use Core\Middleware\MiddlewareInterface; @@ -10,10 +11,10 @@ final class AuthMiddleware implements MiddlewareInterface { public function handle(Request $request, Closure $next) { - if (auth()->check()) { + if (Auth::check()) { return $next($request); } - respond()->redirect('/login'); + return respond()->to('/login'); } } diff --git a/app/Middleware/CorsMiddleware.php b/app/Middleware/CorsMiddleware.php index 5be4827..d5483b5 100644 --- a/app/Middleware/CorsMiddleware.php +++ b/app/Middleware/CorsMiddleware.php @@ -10,22 +10,21 @@ final class CorsMiddleware implements MiddlewareInterface { public function handle(Request $request, Closure $next) { - if (!$request->ajax() && $request->method() != 'OPTIONS') { + if (!$request->ajax() && !$request->method(Request::OPTIONS)) { return $next($request); } - header('Access-Control-Allow-Origin: ' . baseurl()); - header('Access-Control-Allow-Credentials: true'); - header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); - header('Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, Token'); - header('Origin: ' . baseurl()); - header('Vary: Accept-Encoding, Origin, User-Agent'); + respond()->getHeader()->set('Access-Control-Allow-Origin', base_url()) + ->set('Access-Control-Allow-Credentials', 'true') + ->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') + ->set('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, Authorization, Token') + ->set('Origin', base_url()) + ->set('Vary', 'Accept-Encoding, Origin, User-Agent'); - if ($request->method() != 'OPTIONS') { + if (!$request->method(Request::OPTIONS)) { return $next($request); } - http_response_code(204); - header('HTTP/1.1 204 No Content', true, 204); + return respond()->setCode(204); } } diff --git a/app/Middleware/CsrfMiddleware.php b/app/Middleware/CsrfMiddleware.php index 8bbfcb6..6258ff6 100644 --- a/app/Middleware/CsrfMiddleware.php +++ b/app/Middleware/CsrfMiddleware.php @@ -4,6 +4,7 @@ use Closure; use Core\Http\Request; +use Core\Http\Session; use Core\Middleware\HasToken; use Core\Middleware\MiddlewareInterface; use Core\Valid\Hash; @@ -14,12 +15,16 @@ final class CsrfMiddleware implements MiddlewareInterface public function handle(Request $request, Closure $next) { - if ($request->method() != 'GET' && (!$request->ajax())) { - $this->checkToken($request->get('_token', Hash::rand(10))); + $result = null; + + if ((!$request->method(Request::GET)) && (!$request->ajax())) { + $result = $this->checkToken($request->get(Session::TOKEN, Hash::rand(10))); + } else if ($request->ajax()) { + $result = $this->checkToken($request->ajax(), true); } - if ($request->ajax()) { - $this->checkToken($request->ajax(), true); + if ($result) { + return $result; } return $next($request); diff --git a/app/Middleware/GuestMiddleware.php b/app/Middleware/GuestMiddleware.php index d949255..5fa28e3 100644 --- a/app/Middleware/GuestMiddleware.php +++ b/app/Middleware/GuestMiddleware.php @@ -14,6 +14,6 @@ public function handle(Request $request, Closure $next) return $next($request); } - respond()->redirect('/'); + return respond()->to('/'); } } diff --git a/app/Middleware/XSSMiddleware.php b/app/Middleware/XSSMiddleware.php index 5610419..d87e8ef 100644 --- a/app/Middleware/XSSMiddleware.php +++ b/app/Middleware/XSSMiddleware.php @@ -14,12 +14,11 @@ public function handle(Request $request, Closure $next) return $next($request); } - header('Referrer-Policy: strict-origin-when-cross-origin'); - header('Content-Security-Policy: upgrade-insecure-requests'); - - header('X-Content-Type-Options: nosniff'); - header('X-XSS-Protection: 1; mode=block'); - header('X-Frame-Options: SAMEORIGIN'); + respond()->getHeader()->set('Referrer-Policy', 'strict-origin-when-cross-origin') + ->set('Content-Security-Policy', 'upgrade-insecure-requests') + ->set('X-Content-Type-Options', 'nosniff') + ->set('X-XSS-Protection', '1; mode=block') + ->set('X-Frame-Options', 'SAMEORIGIN'); return $next($request); } diff --git a/app/Models/User.php b/app/Models/User.php index 0b696a3..b0cc8d5 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -10,6 +10,14 @@ final class User extends Model protected $primaryKey = 'id'; + protected $typeKey = 'int'; + + protected $fillable = [ + 'nama', + 'email', + 'password' + ]; + protected $dates = [ 'created_at', 'updated_at', diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index f7848cc..18bf9ec 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -8,16 +8,6 @@ class RouteServiceProvider extends Provider { - /** - * Registrasi apa aja disini. - * - * @return void - */ - public function registrasi() - { - // - } - /** * Jalankan sewaktu aplikasi dinyalakan. * diff --git a/app/Providers/TranslatorServiceProvide.php b/app/Providers/TranslatorServiceProvide.php new file mode 100644 index 0000000..9fb9da1 --- /dev/null +++ b/app/Providers/TranslatorServiceProvide.php @@ -0,0 +1,19 @@ +run()); +\Core\Kernel\Kernel::web( + new \App\Kernel() +)->run(); diff --git a/resources/lang/id.php b/resources/lang/id.php new file mode 100644 index 0000000..89169e9 --- /dev/null +++ b/resources/lang/id.php @@ -0,0 +1,56 @@ + [ + + 'failed' => ':email atau :password salah.', + ], + + /** + * Time. + */ + \Core\Support\Time::NAME => [ + + 'y' => 'tahun', + 'm' => 'bulan', + 'd' => 'hari', + 'h' => 'jam', + 'i' => 'menit', + 's' => 'detik', + + 'ago' => 'yang lalu.', + 'recently' => 'baru saja.', + ], + + /** + * Validator. + */ + \Core\Valid\Validator::NAME => [ + + 'request' => [ + 'required' => ':field dibutuhkan!.', + 'email' => ':field tidak valid!.', + 'dns' => ':field tidak valid!.', + 'url' => ':field tidak valid!.', + 'int' => ':field harus angka!.', + 'float' => ':field harus desimal!.', + 'min' => ':field panjang minimal: :attribute', + 'max' => ':field panjang maxsimal: :attribute', + 'sama' => ':field tidak sama dengan :attribute', + 'unik' => ':field sudah ada!.', + ], + + 'file' => [ + 'required' => ':field dibutuhkan!.', + 'min' => ':field panjang minimal: :attribute', + 'max' => ':field panjang maxsimal: :attribute', + 'mimetypes' => ':field diperbolehkan: :attribute', + 'mimes' => ':field diperbolehkan: :attribute', + 'unsafe' => ':field terindikasi tidak aman!.', + 'corrupt' => ':field tidak terupload dengan benar!.', + ], + ] +]; diff --git a/routes/routes.php b/routes/routes.php index 82e5c14..db5b0e8 100644 --- a/routes/routes.php +++ b/routes/routes.php @@ -5,7 +5,7 @@ /** * Make something great with this app. - * keep simple yahh. + * keep simple yeah. */ Route::get('/', WelcomeController::class); diff --git a/saya b/saya index 6b3a8a5..711ed33 100644 --- a/saya +++ b/saya @@ -1,22 +1,23 @@ #!/usr/bin/env php run()); +exit(\Core\Kernel\Kernel::console( + new \App\Kernel() +)->run()); From 3169dcb3d2844a9ea3f760775cabe19f3684a764 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Mon, 11 Sep 2023 23:59:31 +0700 Subject: [PATCH 02/25] fix: folder cache ignore --- .gitignore | 8 ++++++-- cache/{ => env}/.gitignore | 0 cache/log/.gitignore | 2 ++ cache/queue/.gitignore | 2 ++ cache/routes/.gitignore | 2 ++ cache/views/.gitignore | 2 ++ 6 files changed, 14 insertions(+), 2 deletions(-) rename cache/{ => env}/.gitignore (100%) create mode 100644 cache/log/.gitignore create mode 100644 cache/queue/.gitignore create mode 100644 cache/routes/.gitignore create mode 100644 cache/views/.gitignore diff --git a/.gitignore b/.gitignore index 77ec66f..84d8ace 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ -/cache/*.key +/cache/env/*.key +/cache/log/*.key +/cache/queue/*.key +/cache/routes/*.key +/cache/views/*.key /node_modules /shared/*.key /vendor @@ -6,4 +10,4 @@ npm-debug.log yarn-error.log .idea -.vscode \ No newline at end of file +.vscode diff --git a/cache/.gitignore b/cache/env/.gitignore similarity index 100% rename from cache/.gitignore rename to cache/env/.gitignore diff --git a/cache/log/.gitignore b/cache/log/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/cache/log/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/cache/queue/.gitignore b/cache/queue/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/cache/queue/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/cache/routes/.gitignore b/cache/routes/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/cache/routes/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/cache/views/.gitignore b/cache/views/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/cache/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file From a89cab60bb9d249c381bc3d0a354af3c1e3c6e3d Mon Sep 17 00:00:00 2001 From: dewanakl Date: Mon, 18 Sep 2023 14:08:48 +0700 Subject: [PATCH 03/25] feat: improve code. --- .gitattributes | 4 +- .gitignore | 2 + CODE_OF_CONDUCT.md | 128 ++++++++++++++++++++++++++++++ SECURITY.md | 11 +++ app/Middleware/CorsMiddleware.php | 23 ++++-- app/Middleware/CsrfMiddleware.php | 2 +- composer.json | 4 +- 7 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 SECURITY.md diff --git a/.gitattributes b/.gitattributes index af644c5..afd6a7c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,4 +5,6 @@ *.css diff=css *.md diff=markdown -/.github export-ignore \ No newline at end of file +/.github export-ignore +CODE_OF_CONDUCT.md export-ignore +SECURITY.md export-ignore diff --git a/.gitignore b/.gitignore index 84d8ace..e26dd3f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ npm-debug.log yarn-error.log .idea .vscode +CODE_OF_CONDUCT.md +SECURITY.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..494abbb --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +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, 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: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their 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 to 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 +dewanakretarta29@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..b612b4d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +The development take all security issues seriously. Please do not make public any uncovered flaws. + +## Reporting a Vulnerability + +If you discover a security vulnerability, please send an e-mail to Dewana Kretarta Lokeswara via dewanakretarta29@gmail.com + +## Comments on this Policy + +If you have suggestions on how this process could be improved please submit a Pull Request. diff --git a/app/Middleware/CorsMiddleware.php b/app/Middleware/CorsMiddleware.php index d5483b5..7d0fa81 100644 --- a/app/Middleware/CorsMiddleware.php +++ b/app/Middleware/CorsMiddleware.php @@ -14,17 +14,28 @@ public function handle(Request $request, Closure $next) return $next($request); } - respond()->getHeader()->set('Access-Control-Allow-Origin', base_url()) - ->set('Access-Control-Allow-Credentials', 'true') - ->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') - ->set('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, Authorization, Token') - ->set('Origin', base_url()) - ->set('Vary', 'Accept-Encoding, Origin, User-Agent'); + $header = respond()->getHeader(); + $header->set('Access-Control-Allow-Origin', '*'); + $header->set('Vary', 'Accept, Accept-Encoding, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, User-Agent'); if (!$request->method(Request::OPTIONS)) { return $next($request); } + if (!$request->server->has('HTTP_ACCESS_CONTROL_REQUEST_METHOD')) { + return respond()->setCode(204); + } + + $header->set( + 'Access-Control-Allow-Methods', + strtoupper($request->server->get('HTTP_ACCESS_CONTROL_REQUEST_METHOD', $request->method())) + ); + + $header->set( + 'Access-Control-Allow-Headers', + $request->server->get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', 'Accept, Authorization, Content-Type, Origin, Token, User-Agent') + ); + return respond()->setCode(204); } } diff --git a/app/Middleware/CsrfMiddleware.php b/app/Middleware/CsrfMiddleware.php index 6258ff6..d0c1844 100644 --- a/app/Middleware/CsrfMiddleware.php +++ b/app/Middleware/CsrfMiddleware.php @@ -17,7 +17,7 @@ public function handle(Request $request, Closure $next) { $result = null; - if ((!$request->method(Request::GET)) && (!$request->ajax())) { + if (!$request->method(Request::GET) && !$request->ajax()) { $result = $this->checkToken($request->get(Session::TOKEN, Hash::rand(10))); } else if ($request->ajax()) { $result = $this->checkToken($request->ajax(), true); diff --git a/composer.json b/composer.json index da23c6c..eeac854 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "license": "MIT", "require": { "php": "^8.1", - "kamu/framework": "^2.0.0" + "kamu/framework": "3.x-dev" }, "autoload": { "psr-4": { @@ -31,4 +31,4 @@ }, "minimum-stability": "stable", "prefer-stable": true -} +} \ No newline at end of file From 09ee1d4014692a3c48b166adc2dbdfbd9e376baa Mon Sep 17 00:00:00 2001 From: dewanakl Date: Mon, 18 Sep 2023 14:10:12 +0700 Subject: [PATCH 04/25] feat: update readme.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cc4caa7..3475627 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@

kamu

+PHP Programming Language Total Downloads repo size Latest Stable Version From baf8052270b78e0796705d7f0e5265be0a57b49f Mon Sep 17 00:00:00 2001 From: dewanakl Date: Tue, 19 Sep 2023 18:11:42 +0700 Subject: [PATCH 05/25] feat: update new version framework --- app/Middleware/CorsMiddleware.php | 5 ++++- app/Middleware/XSSMiddleware.php | 3 ++- composer.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/Middleware/CorsMiddleware.php b/app/Middleware/CorsMiddleware.php index 7d0fa81..527f193 100644 --- a/app/Middleware/CorsMiddleware.php +++ b/app/Middleware/CorsMiddleware.php @@ -16,7 +16,10 @@ public function handle(Request $request, Closure $next) $header = respond()->getHeader(); $header->set('Access-Control-Allow-Origin', '*'); - $header->set('Vary', 'Accept, Accept-Encoding, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, User-Agent'); + $header->set( + 'Vary', + 'Accept, Accept-Encoding, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, User-Agent' + ); if (!$request->method(Request::OPTIONS)) { return $next($request); diff --git a/app/Middleware/XSSMiddleware.php b/app/Middleware/XSSMiddleware.php index d87e8ef..e0b9d65 100644 --- a/app/Middleware/XSSMiddleware.php +++ b/app/Middleware/XSSMiddleware.php @@ -14,7 +14,8 @@ public function handle(Request $request, Closure $next) return $next($request); } - respond()->getHeader()->set('Referrer-Policy', 'strict-origin-when-cross-origin') + respond()->getHeader() + ->set('Referrer-Policy', 'strict-origin-when-cross-origin') ->set('Content-Security-Policy', 'upgrade-insecure-requests') ->set('X-Content-Type-Options', 'nosniff') ->set('X-XSS-Protection', '1; mode=block') diff --git a/composer.json b/composer.json index eeac854..3b44c86 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "license": "MIT", "require": { "php": "^8.1", - "kamu/framework": "3.x-dev" + "kamu/framework": "^3.0" }, "autoload": { "psr-4": { From 64cb7fdc9381ee46f70cf00776679d8cc192a04b Mon Sep 17 00:00:00 2001 From: dewanakl Date: Fri, 22 Sep 2023 23:40:59 +0700 Subject: [PATCH 06/25] feat: add uuid validate. --- resources/lang/id.php | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/lang/id.php b/resources/lang/id.php index 89169e9..926a67c 100644 --- a/resources/lang/id.php +++ b/resources/lang/id.php @@ -35,6 +35,7 @@ 'email' => ':field tidak valid!.', 'dns' => ':field tidak valid!.', 'url' => ':field tidak valid!.', + 'uuid' => ':field bukan uuid!.', 'int' => ':field harus angka!.', 'float' => ':field harus desimal!.', 'min' => ':field panjang minimal: :attribute', From f7ea8e8d88bd03ab98e020c54c472568306956cb Mon Sep 17 00:00:00 2001 From: dewanakl Date: Thu, 30 Nov 2023 17:31:25 +0700 Subject: [PATCH 07/25] feat: update skeleton --- app/Kernel.php | 1 + app/Middleware/CorsMiddleware.php | 19 +++++++---- app/Middleware/GzipMiddleware.php | 52 +++++++++++++++++++++++++++++++ app/Models/User.php | 10 ++++++ composer.json | 6 +++- saya | 2 +- 6 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 app/Middleware/GzipMiddleware.php diff --git a/app/Kernel.php b/app/Kernel.php index 6f8b300..7319b45 100644 --- a/app/Kernel.php +++ b/app/Kernel.php @@ -58,6 +58,7 @@ public function middlewares(): array return [ \App\Middleware\CorsMiddleware::class, \App\Middleware\XSSMiddleware::class, + \App\Middleware\GzipMiddleware::class, \App\Middleware\CsrfMiddleware::class ]; } diff --git a/app/Middleware/CorsMiddleware.php b/app/Middleware/CorsMiddleware.php index 527f193..af7dc1c 100644 --- a/app/Middleware/CorsMiddleware.php +++ b/app/Middleware/CorsMiddleware.php @@ -4,6 +4,7 @@ use Closure; use Core\Http\Request; +use Core\Http\Respond; use Core\Middleware\MiddlewareInterface; final class CorsMiddleware implements MiddlewareInterface @@ -16,17 +17,23 @@ public function handle(Request $request, Closure $next) $header = respond()->getHeader(); $header->set('Access-Control-Allow-Origin', '*'); - $header->set( - 'Vary', - 'Accept, Accept-Encoding, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, User-Agent' - ); + + if ($header->has('Vary')) { + $vary = explode(', ', $header->get('Vary')); + $vary = array_unique([...$vary, 'Accept', 'Access-Control-Request-Method', 'Access-Control-Request-Headers', 'Origin', 'User-Agent']); + $header->set('Vary', join(', ', $vary)); + } else { + $header->set('Vary', 'Accept, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, User-Agent'); + } if (!$request->method(Request::OPTIONS)) { return $next($request); } + $header->unset('Content-Type'); + if (!$request->server->has('HTTP_ACCESS_CONTROL_REQUEST_METHOD')) { - return respond()->setCode(204); + return respond()->setCode(Respond::HTTP_NO_CONTENT); } $header->set( @@ -39,6 +46,6 @@ public function handle(Request $request, Closure $next) $request->server->get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', 'Accept, Authorization, Content-Type, Origin, Token, User-Agent') ); - return respond()->setCode(204); + return respond()->setCode(Respond::HTTP_NO_CONTENT); } } diff --git a/app/Middleware/GzipMiddleware.php b/app/Middleware/GzipMiddleware.php new file mode 100644 index 0000000..4f53617 --- /dev/null +++ b/app/Middleware/GzipMiddleware.php @@ -0,0 +1,52 @@ +transform($response); + + if (env('GZIP', 'true') != 'true') { + return $response; + } + + if (!in_array('gzip', explode(', ', $request->server->get('HTTP_ACCEPT_ENCODING')))) { + return $response; + } + + $compressed = gzencode($response->getContent(false), 1); + + if ($compressed === false) { + return $response; + } + + $response->setContent($compressed); + + if ($response->headers->has('Vary')) { + $vary = explode(', ', $response->headers->get('Vary')); + $vary = array_unique([...$vary, 'Accept-Encoding']); + $response->headers->set('Vary', join(', ', $vary)); + } else { + $response->headers->set('Vary', 'Accept-Encoding'); + } + + $response->headers + ->set('Content-Encoding', 'gzip') + ->set('Content-Length', strlen($compressed)); + + return $response; + } +} diff --git a/app/Models/User.php b/app/Models/User.php index b0cc8d5..71cb2d3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -3,6 +3,7 @@ namespace App\Models; use Core\Model\Model; +use Core\Valid\Hash; final class User extends Model { @@ -22,4 +23,13 @@ final class User extends Model 'created_at', 'updated_at', ]; + + protected function fakes() + { + return [ + 'nama' => fake()->name(), + 'email' => fake()->email(), + 'password' => Hash::make(fake()->text(8)), + ]; + } } diff --git a/composer.json b/composer.json index 3b44c86..74afdaf 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,11 @@ "license": "MIT", "require": { "php": "^8.1", - "kamu/framework": "^3.0" + "kamu/framework": "^3.1" + }, + "require-dev": { + "fakerphp/faker": "^1.23", + "psy/psysh": "^0.11.22" }, "autoload": { "psr-4": { diff --git a/saya b/saya index 711ed33..19d6b7e 100644 --- a/saya +++ b/saya @@ -18,6 +18,6 @@ require_once __DIR__ . '/vendor/autoload.php'; * Ini sangat simple. */ -exit(\Core\Kernel\Kernel::console( +exit(\Core\Kernel\Kernel::cli( new \App\Kernel() )->run()); From 2f5d741e9ac4f794c723b09026e2cc0ee900a21b Mon Sep 17 00:00:00 2001 From: dewanakl Date: Fri, 22 Dec 2023 00:20:00 +0700 Subject: [PATCH 08/25] feat: remove request error class --- app/Error/Error.php | 6 ++---- app/Models/User.php | 2 +- composer.json | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/Error/Error.php b/app/Error/Error.php index d566348..ebceba4 100644 --- a/app/Error/Error.php +++ b/app/Error/Error.php @@ -3,7 +3,6 @@ namespace App\Error; use Core\Support\Error as BaseError; -use Core\Http\Request; use Throwable; class Error extends BaseError @@ -11,14 +10,13 @@ class Error extends BaseError /** * Tampilkan errornya. * - * @param Request $request * @param Throwable $th * @return mixed */ - public function render(Request $request, Throwable $th): mixed + public function render(Throwable $th): mixed { // - return parent::render($request, $th); + return parent::render($th); } } diff --git a/app/Models/User.php b/app/Models/User.php index 71cb2d3..fcef5a5 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -24,7 +24,7 @@ final class User extends Model 'updated_at', ]; - protected function fakes() + protected function fakes(): array { return [ 'nama' => fake()->name(), diff --git a/composer.json b/composer.json index 74afdaf..3621141 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "license": "MIT", "require": { "php": "^8.1", - "kamu/framework": "^3.1" + "kamu/framework": "^3.2" }, "require-dev": { "fakerphp/faker": "^1.23", From 09a002c0f8dda70eac55c2c6113d8b352cd0f707 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Sat, 23 Dec 2023 22:37:36 +0700 Subject: [PATCH 09/25] fix: redirect response --- app/Middleware/GzipMiddleware.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/Middleware/GzipMiddleware.php b/app/Middleware/GzipMiddleware.php index 4f53617..cf967c4 100644 --- a/app/Middleware/GzipMiddleware.php +++ b/app/Middleware/GzipMiddleware.php @@ -4,12 +4,13 @@ use Closure; use Core\Http\Request; +use Core\Http\Respond; use Core\Http\Stream; use Core\Middleware\MiddlewareInterface; final class GzipMiddleware implements MiddlewareInterface { - public function handle(Request $request, Closure $next): Stream|\Core\Http\Respond + public function handle(Request $request, Closure $next): Stream|Respond { $response = $next($request); @@ -17,6 +18,10 @@ public function handle(Request $request, Closure $next): Stream|\Core\Http\Respo return $response; } + if ($response instanceof Respond && $response->getCode() < 400 && $response->getCode() >= 300) { + return $response; + } + $response = respond()->transform($response); if (env('GZIP', 'true') != 'true') { From a974eff18cde946e6d2f6bfddcb75bf8c4006038 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Sun, 24 Dec 2023 00:48:27 +0700 Subject: [PATCH 10/25] feat: event support --- app/Kernel.php | 1 + app/Providers/EventServiceProvider.php | 33 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 app/Providers/EventServiceProvider.php diff --git a/app/Kernel.php b/app/Kernel.php index 7319b45..697d3e9 100644 --- a/app/Kernel.php +++ b/app/Kernel.php @@ -73,6 +73,7 @@ public function services(): array return [ \App\Providers\AppServiceProvider::class, \App\Providers\RouteServiceProvider::class, + \App\Providers\EventServiceProvider::class, \App\Providers\TranslatorServiceProvide::class, ]; } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php new file mode 100644 index 0000000..3683ca8 --- /dev/null +++ b/app/Providers/EventServiceProvider.php @@ -0,0 +1,33 @@ +> + */ + protected $listen = [ + // \App\Events\Registered::class => [ + // \App\Listeners\SendEmailNotification::class, + // ], + ]; + + /** + * Registrasi apa aja disini. + * + * @return void + */ + public function registrasi() + { + $this->app->bind(Dispatch::class, function (): Dispatch { + return new Dispatch(new Listener($this->listen)); + }); + } +} From 3c646bc21f5a9d39c35dabc7c24e7cadd3d8ac83 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Fri, 29 Dec 2023 14:43:20 +0700 Subject: [PATCH 11/25] feat: refactor code --- app/Middleware/CorsMiddleware.php | 10 +++------- app/Middleware/GzipMiddleware.php | 12 ++++-------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/app/Middleware/CorsMiddleware.php b/app/Middleware/CorsMiddleware.php index af7dc1c..911e691 100644 --- a/app/Middleware/CorsMiddleware.php +++ b/app/Middleware/CorsMiddleware.php @@ -18,13 +18,9 @@ public function handle(Request $request, Closure $next) $header = respond()->getHeader(); $header->set('Access-Control-Allow-Origin', '*'); - if ($header->has('Vary')) { - $vary = explode(', ', $header->get('Vary')); - $vary = array_unique([...$vary, 'Accept', 'Access-Control-Request-Method', 'Access-Control-Request-Headers', 'Origin', 'User-Agent']); - $header->set('Vary', join(', ', $vary)); - } else { - $header->set('Vary', 'Accept, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, User-Agent'); - } + $vary = (!$header->has('Vary')) ? [] : explode(', ', $header->get('Vary')); + $vary = array_unique([...$vary, 'Accept', 'Access-Control-Request-Method', 'Access-Control-Request-Headers', 'Origin', 'User-Agent']); + $header->set('Vary', join(', ', $vary)); if (!$request->method(Request::OPTIONS)) { return $next($request); diff --git a/app/Middleware/GzipMiddleware.php b/app/Middleware/GzipMiddleware.php index cf967c4..51e3630 100644 --- a/app/Middleware/GzipMiddleware.php +++ b/app/Middleware/GzipMiddleware.php @@ -28,7 +28,7 @@ public function handle(Request $request, Closure $next): Stream|Respond return $response; } - if (!in_array('gzip', explode(', ', $request->server->get('HTTP_ACCEPT_ENCODING')))) { + if (str_contains($request->server->get('HTTP_ACCEPT_ENCODING'), 'gzip')) { return $response; } @@ -40,15 +40,11 @@ public function handle(Request $request, Closure $next): Stream|Respond $response->setContent($compressed); - if ($response->headers->has('Vary')) { - $vary = explode(', ', $response->headers->get('Vary')); - $vary = array_unique([...$vary, 'Accept-Encoding']); - $response->headers->set('Vary', join(', ', $vary)); - } else { - $response->headers->set('Vary', 'Accept-Encoding'); - } + $vary = (!$response->headers->has('Vary')) ? [] : explode(', ', $response->headers->get('Vary')); + $vary = array_unique([...$vary, 'Accept-Encoding']); $response->headers + ->set('Vary', join(', ', $vary)) ->set('Content-Encoding', 'gzip') ->set('Content-Length', strlen($compressed)); From 1af0425db65eb2b503c14ef16083f6cd404eb6c1 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Fri, 29 Dec 2023 14:47:08 +0700 Subject: [PATCH 12/25] fix: negate op --- app/Middleware/GzipMiddleware.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Middleware/GzipMiddleware.php b/app/Middleware/GzipMiddleware.php index 51e3630..9446dcd 100644 --- a/app/Middleware/GzipMiddleware.php +++ b/app/Middleware/GzipMiddleware.php @@ -28,7 +28,7 @@ public function handle(Request $request, Closure $next): Stream|Respond return $response; } - if (str_contains($request->server->get('HTTP_ACCEPT_ENCODING'), 'gzip')) { + if (!str_contains($request->server->get('HTTP_ACCEPT_ENCODING'), 'gzip')) { return $response; } From 55538e8b8365609f71d8736cb1514648c0cbec50 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Sun, 25 Feb 2024 17:30:17 +0700 Subject: [PATCH 13/25] feat: change route in service provider --- app/Kernel.php | 1 - app/Middleware/CorsMiddleware.php | 4 ++-- app/Middleware/GzipMiddleware.php | 6 +++--- app/Providers/RouteServiceProvider.php | 8 +++++++- composer.json | 4 ++-- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/Kernel.php b/app/Kernel.php index 697d3e9..decc0d4 100644 --- a/app/Kernel.php +++ b/app/Kernel.php @@ -59,7 +59,6 @@ public function middlewares(): array \App\Middleware\CorsMiddleware::class, \App\Middleware\XSSMiddleware::class, \App\Middleware\GzipMiddleware::class, - \App\Middleware\CsrfMiddleware::class ]; } diff --git a/app/Middleware/CorsMiddleware.php b/app/Middleware/CorsMiddleware.php index 911e691..4ab0cae 100644 --- a/app/Middleware/CorsMiddleware.php +++ b/app/Middleware/CorsMiddleware.php @@ -18,7 +18,7 @@ public function handle(Request $request, Closure $next) $header = respond()->getHeader(); $header->set('Access-Control-Allow-Origin', '*'); - $vary = (!$header->has('Vary')) ? [] : explode(', ', $header->get('Vary')); + $vary = $header->has('Vary') ? explode(', ', $header->get('Vary')) : []; $vary = array_unique([...$vary, 'Accept', 'Access-Control-Request-Method', 'Access-Control-Request-Headers', 'Origin', 'User-Agent']); $header->set('Vary', join(', ', $vary)); @@ -39,7 +39,7 @@ public function handle(Request $request, Closure $next) $header->set( 'Access-Control-Allow-Headers', - $request->server->get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', 'Accept, Authorization, Content-Type, Origin, Token, User-Agent') + $request->server->get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', 'Accept, Authorization, Content-Type, Origin, User-Agent') ); return respond()->setCode(Respond::HTTP_NO_CONTENT); diff --git a/app/Middleware/GzipMiddleware.php b/app/Middleware/GzipMiddleware.php index 9446dcd..4c01773 100644 --- a/app/Middleware/GzipMiddleware.php +++ b/app/Middleware/GzipMiddleware.php @@ -28,11 +28,11 @@ public function handle(Request $request, Closure $next): Stream|Respond return $response; } - if (!str_contains($request->server->get('HTTP_ACCEPT_ENCODING'), 'gzip')) { + if (!str_contains($request->server->get('HTTP_ACCEPT_ENCODING', ''), 'gzip')) { return $response; } - $compressed = gzencode($response->getContent(false), 1); + $compressed = gzencode($response->getContent(false), 3); if ($compressed === false) { return $response; @@ -40,7 +40,7 @@ public function handle(Request $request, Closure $next): Stream|Respond $response->setContent($compressed); - $vary = (!$response->headers->has('Vary')) ? [] : explode(', ', $response->headers->get('Vary')); + $vary = $response->headers->has('Vary') ? explode(', ', $response->headers->get('Vary')) : []; $vary = array_unique([...$vary, 'Accept-Encoding']); $response->headers diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 18bf9ec..ac38b65 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Middleware\CsrfMiddleware; use Core\Facades\Provider; use Core\Routing\Route; use Core\Routing\Router; @@ -16,6 +17,11 @@ class RouteServiceProvider extends Provider public function booting() { $this->app->singleton(Router::class); - Route::setRouteFromCacheIfExist(); + + if (!Route::setRouteFromCacheIfExist()) { + Route::middleware(CsrfMiddleware::class)->group(function () { + Route::setRouteFromFile(); + }); + } } } diff --git a/composer.json b/composer.json index 3621141..dc528b0 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "license": "MIT", "require": { "php": "^8.1", - "kamu/framework": "^3.2" + "kamu/framework": "^3.3" }, "require-dev": { "fakerphp/faker": "^1.23", @@ -35,4 +35,4 @@ }, "minimum-stability": "stable", "prefer-stable": true -} \ No newline at end of file +} From d3784698b93e954e8fb1b03b4889fbff199aaee1 Mon Sep 17 00:00:00 2001 From: Dewana Kretarta Lokeswara <53048107+dewanakl@users.noreply.github.com> Date: Fri, 15 Mar 2024 21:20:08 +0700 Subject: [PATCH 14/25] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 3475627..d1ceb30 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,7 @@ ## About Kamu -Kamu adalah PHP framework yang sangat sederhana. Saking sederhananya, serasa di localhost!!. -
-Dibantu dengan saya console untuk mempermudah dalam development aplikasi Anda. +"Kamu" merupakan PHP framework yang sangat simpel, memberikan pengalaman seolah-olah berada di localhost meskipun dalam mode production. Dibantu dengan "Saya" konsol yang membantu pengembangan aplikasi secara efisien. ## Get Started Project - Create a project with composer From 859f39dbf3de9fa56ce69648455f0e70580e889a Mon Sep 17 00:00:00 2001 From: dewanakl Date: Wed, 29 May 2024 10:51:29 +0700 Subject: [PATCH 15/25] feat: add health check --- app/Middleware/CookieMiddleware.php | 24 +++++++ app/Providers/RouteServiceProvider.php | 96 ++++++++++++++++++++++++++ routes/api.php | 11 +++ 3 files changed, 131 insertions(+) create mode 100644 app/Middleware/CookieMiddleware.php create mode 100644 routes/api.php diff --git a/app/Middleware/CookieMiddleware.php b/app/Middleware/CookieMiddleware.php new file mode 100644 index 0000000..62e3340 --- /dev/null +++ b/app/Middleware/CookieMiddleware.php @@ -0,0 +1,24 @@ +server->get('REQUEST_URL'), RouteServiceProvider::$API_PREFIX) !== false + && $request->server->get('REQUEST_URL') !== RouteServiceProvider::$API_PREFIX + ) { + Env::set('COOKIE', 'false'); + } + + return $next($request); + } +} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index ac38b65..74d2934 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -2,13 +2,23 @@ namespace App\Providers; +use App\Middleware\CookieMiddleware; use App\Middleware\CsrfMiddleware; +use Core\Database\DB; use Core\Facades\Provider; use Core\Routing\Route; use Core\Routing\Router; +use Exception; class RouteServiceProvider extends Provider { + /** + * Prefix api. + * + * @var string $API_PREFIX + */ + public static $API_PREFIX = '/api'; + /** * Jalankan sewaktu aplikasi dinyalakan. * @@ -22,6 +32,92 @@ public function booting() Route::middleware(CsrfMiddleware::class)->group(function () { Route::setRouteFromFile(); }); + + Route::prefix(static::$API_PREFIX)->middleware(CookieMiddleware::class)->group(function () { + Route::get('/health', [static::class, 'health']); + require_once base_path('/routes/api.php'); + }); + } + } + + /** + * Get health this application. + * + * @return \Core\Http\Respond + */ + public function health(): \Core\Http\Respond + { + $data = [ + 'status' => true + ]; + + if (!hash_equals(hash('sha3-512', env('APP_KEY')), request()->get('hash', ''))) { + return respond()->setContent(json($data, 200)); + } + + $data['system'] = [ + 'php_version' => PHP_VERSION, + 'execute_time' => execute_time(), + 'memory_peak_usage' => format_bytes(memory_get_peak_usage(true)), + ]; + + $data['system']['opcache'] = [ + 'jit_enabled' => false, + 'opcache_enabled' => false, + 'opcache_used_memory' => format_bytes(0), + 'opcache_free_memory' => format_bytes(0), + ]; + + if (function_exists('opcache_get_status')) { + $opcache = opcache_get_status(); + $data['system']['opcache'] = [ + 'jit_enabled' => $opcache['jit']['enabled'], + 'opcache_enabled' => $opcache['opcache_enabled'], + 'opcache_used_memory' => format_bytes($opcache['memory_usage']['used_memory']), + 'opcache_free_memory' => format_bytes($opcache['memory_usage']['free_memory']), + ]; } + + $data['database'] = [ + 'server_version' => null, + 'client_version' => null, + 'connection_status' => null, + 'server_info' => null, + 'error' => null, + ]; + + if ( + env('DB_DRIV') && + env('DB_HOST') && + env('DB_NAME') && + env('DB_PORT') && + env('DB_USER') + ) { + try { + $database = DB::getInfoDriver(); + $data['database'] = [ + 'server_version' => $database['SERVER_VERSION'], + 'client_version' => $database['CLIENT_VERSION'], + 'connection_status' => $database['CONNECTION_STATUS'], + 'server_info' => $database['SERVER_INFO'], + 'error' => null, + ]; + + $data['system']['execute_time'] = execute_time(); + } catch (Exception $e) { + $data['status'] = false; + $data['database'] = [ + 'server_version' => null, + 'client_version' => null, + 'connection_status' => null, + 'server_info' => null, + 'error' => $e->getMessage(), + ]; + + return respond()->setContent(json($data, 500)); + } + } + + return respond()->setContent(json($data, 200)); } } diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..833533f --- /dev/null +++ b/routes/api.php @@ -0,0 +1,11 @@ + Date: Wed, 29 May 2024 22:05:31 +0700 Subject: [PATCH 16/25] feat: improve health check --- app/Providers/RouteServiceProvider.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 74d2934..4b812f7 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -62,19 +62,27 @@ public function health(): \Core\Http\Respond ]; $data['system']['opcache'] = [ - 'jit_enabled' => false, 'opcache_enabled' => false, 'opcache_used_memory' => format_bytes(0), 'opcache_free_memory' => format_bytes(0), + 'jit' => [ + 'enabled' => false, + 'buffer_size' => format_bytes(0), + 'buffer_free' => format_bytes(0), + ] ]; if (function_exists('opcache_get_status')) { $opcache = opcache_get_status(); $data['system']['opcache'] = [ - 'jit_enabled' => $opcache['jit']['enabled'], 'opcache_enabled' => $opcache['opcache_enabled'], 'opcache_used_memory' => format_bytes($opcache['memory_usage']['used_memory']), 'opcache_free_memory' => format_bytes($opcache['memory_usage']['free_memory']), + 'jit' => [ + 'enabled' => $opcache['jit']['enabled'], + 'buffer_size' => format_bytes($opcache['jit']['buffer_size']), + 'buffer_free' => format_bytes($opcache['jit']['buffer_free']), + ], ]; } From 4c3845190861a34c5af926cda10683e8158a0786 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Fri, 31 May 2024 13:41:43 +0700 Subject: [PATCH 17/25] feat: update framework --- app/Error/Error.php | 6 ++---- app/Middleware/GzipMiddleware.php | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/Error/Error.php b/app/Error/Error.php index ebceba4..410c614 100644 --- a/app/Error/Error.php +++ b/app/Error/Error.php @@ -3,20 +3,18 @@ namespace App\Error; use Core\Support\Error as BaseError; -use Throwable; class Error extends BaseError { /** * Tampilkan errornya. * - * @param Throwable $th * @return mixed */ - public function render(Throwable $th): mixed + public function render(): mixed { // - return parent::render($th); + return parent::render(); } } diff --git a/app/Middleware/GzipMiddleware.php b/app/Middleware/GzipMiddleware.php index 4c01773..698023f 100644 --- a/app/Middleware/GzipMiddleware.php +++ b/app/Middleware/GzipMiddleware.php @@ -45,8 +45,7 @@ public function handle(Request $request, Closure $next): Stream|Respond $response->headers ->set('Vary', join(', ', $vary)) - ->set('Content-Encoding', 'gzip') - ->set('Content-Length', strlen($compressed)); + ->set('Content-Encoding', 'gzip'); return $response; } From 8afcd4168aeb33aff5a9937b925d4f0f2baf8632 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Fri, 30 May 2025 14:16:02 +0700 Subject: [PATCH 18/25] feat: update environment variables, improve CORS and XSS middleware, and add language support --- .env.example | 8 ++--- README.md | 20 +++++------ app/Middleware/CorsMiddleware.php | 12 ++----- app/Middleware/XSSMiddleware.php | 1 - resources/lang/en.php | 58 +++++++++++++++++++++++++++++++ resources/lang/id.php | 1 + 6 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 resources/lang/en.php diff --git a/.env.example b/.env.example index 165b8de..92070cf 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,10 @@ BASEURL=http://localhost:8000/ DEBUG=true LOG=true +TIMEZONE=Asia/Jakarta +COOKIE=true +COOKIE_LIFETIME=120 + DB_DRIV=mysql DB_HOST=127.0.0.1 DB_PORT=3306 @@ -18,7 +22,3 @@ MAIL_USERNAME= MAIL_PASSWORD= MAIL_FROM_ADDRESS= MAIL_FROM_NAME=kamu - -TIMEZONE=Asia/Jakarta -COOKIE=true -COOKIE_LIFETIME=120 diff --git a/README.md b/README.md index d1ceb30..caba7ec 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@

kamu

-PHP Programming Language -Total Downloads -repo size -Latest Stable Version -License +PHP Programming Language +Total Downloads +repo size +Latest Stable Version +License

-## About Kamu +## 📖 About Kamu "Kamu" merupakan PHP framework yang sangat simpel, memberikan pengalaman seolah-olah berada di localhost meskipun dalam mode production. Dibantu dengan "Saya" konsol yang membantu pengembangan aplikasi secara efisien. -## Get Started Project +## 🚀 Get Started Project - Create a project with composer ```bash composer create-project kamu/kamu coba-app @@ -26,14 +26,14 @@ php saya coba ``` -## Contributing +## 🤝 Contributing I'm very open to those of you who want to contribute to the Kamu framework! -## Security Vulnerabilities +## 🐞 Security Vulnerabilities If you find any security vulnerabilities in this Kamu framework, please email DKL via [dewanakretarta29@gmail.com](mailto:dewanakretarta29@gmail.com). -## License +## 📜 License Kamu framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/app/Middleware/CorsMiddleware.php b/app/Middleware/CorsMiddleware.php index 4ab0cae..f756921 100644 --- a/app/Middleware/CorsMiddleware.php +++ b/app/Middleware/CorsMiddleware.php @@ -11,15 +11,12 @@ final class CorsMiddleware implements MiddlewareInterface { public function handle(Request $request, Closure $next) { - if (!$request->ajax() && !$request->method(Request::OPTIONS)) { - return $next($request); - } - $header = respond()->getHeader(); $header->set('Access-Control-Allow-Origin', '*'); + $header->set('Access-Control-Expose-Headers', 'Content-Length, Content-Disposition'); $vary = $header->has('Vary') ? explode(', ', $header->get('Vary')) : []; - $vary = array_unique([...$vary, 'Accept', 'Access-Control-Request-Method', 'Access-Control-Request-Headers', 'Origin', 'User-Agent']); + $vary = array_unique([...$vary, 'Accept', 'Access-Control-Request-Method', 'Access-Control-Request-Headers', 'Origin']); $header->set('Vary', join(', ', $vary)); if (!$request->method(Request::OPTIONS)) { @@ -37,10 +34,7 @@ public function handle(Request $request, Closure $next) strtoupper($request->server->get('HTTP_ACCESS_CONTROL_REQUEST_METHOD', $request->method())) ); - $header->set( - 'Access-Control-Allow-Headers', - $request->server->get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', 'Accept, Authorization, Content-Type, Origin, User-Agent') - ); + $header->set('Access-Control-Allow-Headers', 'Accept, Authorization, Content-Type'); return respond()->setCode(Respond::HTTP_NO_CONTENT); } diff --git a/app/Middleware/XSSMiddleware.php b/app/Middleware/XSSMiddleware.php index e0b9d65..dccd976 100644 --- a/app/Middleware/XSSMiddleware.php +++ b/app/Middleware/XSSMiddleware.php @@ -18,7 +18,6 @@ public function handle(Request $request, Closure $next) ->set('Referrer-Policy', 'strict-origin-when-cross-origin') ->set('Content-Security-Policy', 'upgrade-insecure-requests') ->set('X-Content-Type-Options', 'nosniff') - ->set('X-XSS-Protection', '1; mode=block') ->set('X-Frame-Options', 'SAMEORIGIN'); return $next($request); diff --git a/resources/lang/en.php b/resources/lang/en.php new file mode 100644 index 0000000..6a16f5c --- /dev/null +++ b/resources/lang/en.php @@ -0,0 +1,58 @@ + [ + + 'failed' => ':email or :password incorrect.', + ], + + /** + * Time. + */ + \Core\Support\Time::NAME => [ + + 'y' => 'year', + 'm' => 'month', + 'd' => 'day', + 'h' => 'hour', + 'i' => 'minute', + 's' => 'second', + + 'ago' => 'ago.', + 'future' => 'from now.', + 'recently' => 'just now.', + ], + + /** + * Validator. + */ + \Core\Valid\Validator::NAME => [ + + 'request' => [ + 'required' => ':field is required!.', + 'email' => ':field is invalid!.', + 'dns' => ':field is invalid!.', + 'url' => ':field is invalid!.', + 'uuid' => ':field is not a uuid!.', + 'int' => ':field must be a number!.', + 'float' => ':field must be decimal!.', + 'min' => ':field minimum length: :attribute', + 'max' => ':field maximum length: :attribute', + 'sama' => ':field does not match with :attribute', + 'unik' => ':field already exists!.', + ], + + 'file' => [ + 'required' => ':field is required!.', + 'min' => ':field minimum length: :attribute', + 'max' => ':field maximum length: :attribute', + 'mimetypes' => ':field allowed: :attribute', + 'mimes' => ':field allowed: :attribute', + 'unsafe' => ':field is indicated as unsafe!.', + 'corrupt' => ':field was not uploaded correctly!.', + ], + ] +]; diff --git a/resources/lang/id.php b/resources/lang/id.php index 926a67c..37dbd20 100644 --- a/resources/lang/id.php +++ b/resources/lang/id.php @@ -22,6 +22,7 @@ 's' => 'detik', 'ago' => 'yang lalu.', + 'future' => 'ke depan.', 'recently' => 'baru saja.', ], From 752075e396a729ef81d7eba6f80c36ceab0d3c3f Mon Sep 17 00:00:00 2001 From: dewanakl Date: Mon, 9 Jun 2025 20:51:24 +0700 Subject: [PATCH 19/25] feat: enhance middleware and configuration for improved CORS handling, Gzip compression, and .htaccess updates --- .env.example | 1 + app/Middleware/CorsMiddleware.php | 12 +++++++++--- app/Middleware/GzipMiddleware.php | 15 ++++++++++----- public/.htaccess | 7 +++++-- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index 92070cf..edf33e4 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,7 @@ DEBUG=true LOG=true TIMEZONE=Asia/Jakarta +GZIP=true COOKIE=true COOKIE_LIFETIME=120 diff --git a/app/Middleware/CorsMiddleware.php b/app/Middleware/CorsMiddleware.php index f756921..ecd85b2 100644 --- a/app/Middleware/CorsMiddleware.php +++ b/app/Middleware/CorsMiddleware.php @@ -15,9 +15,15 @@ public function handle(Request $request, Closure $next) $header->set('Access-Control-Allow-Origin', '*'); $header->set('Access-Control-Expose-Headers', 'Content-Length, Content-Disposition'); - $vary = $header->has('Vary') ? explode(', ', $header->get('Vary')) : []; - $vary = array_unique([...$vary, 'Accept', 'Access-Control-Request-Method', 'Access-Control-Request-Headers', 'Origin']); - $header->set('Vary', join(', ', $vary)); + $varyList = ['Accept', 'Access-Control-Request-Method', 'Access-Control-Request-Headers', 'Origin']; + + if ($header->has('Vary')) { + $existing = preg_split('/\s*,\s*/', $header->get('Vary'), -1, PREG_SPLIT_NO_EMPTY); + $varyList = array_merge($existing, $varyList); + } + + $varyList = array_unique(array_map('trim', $varyList)); + $header->set('Vary', implode(', ', $varyList)); if (!$request->method(Request::OPTIONS)) { return $next($request); diff --git a/app/Middleware/GzipMiddleware.php b/app/Middleware/GzipMiddleware.php index 698023f..4eb34be 100644 --- a/app/Middleware/GzipMiddleware.php +++ b/app/Middleware/GzipMiddleware.php @@ -40,12 +40,17 @@ public function handle(Request $request, Closure $next): Stream|Respond $response->setContent($compressed); - $vary = $response->headers->has('Vary') ? explode(', ', $response->headers->get('Vary')) : []; - $vary = array_unique([...$vary, 'Accept-Encoding']); + $varyList = ['Accept-Encoding']; - $response->headers - ->set('Vary', join(', ', $vary)) - ->set('Content-Encoding', 'gzip'); + if ($response->headers->has('Vary')) { + $existing = preg_split('/\s*,\s*/', $response->headers->get('Vary'), -1, PREG_SPLIT_NO_EMPTY); + $varyList = array_merge($existing, $varyList); + } + + $varyList = array_unique(array_map('trim', $varyList)); + $response->headers->set('Vary', implode(', ', $varyList)); + + $response->headers->set('Content-Encoding', 'gzip'); return $response; } diff --git a/public/.htaccess b/public/.htaccess index 10195e6..d559a97 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -14,8 +14,11 @@ RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301] + # Prevent directory listing + Options -Indexes + # Send Requests To Front Controller... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.+)$ index.php/$1 [L] - \ No newline at end of file + RewriteRule ^ index.php [L] + From 8f3ddd232eb9892b6a7a1107db19896af087623d Mon Sep 17 00:00:00 2001 From: dewanakl Date: Mon, 9 Jun 2025 22:02:16 +0700 Subject: [PATCH 20/25] feat: add validation message for IP address in English and Indonesian language files --- resources/lang/en.php | 1 + resources/lang/id.php | 1 + 2 files changed, 2 insertions(+) diff --git a/resources/lang/en.php b/resources/lang/en.php index 6a16f5c..6669cff 100644 --- a/resources/lang/en.php +++ b/resources/lang/en.php @@ -36,6 +36,7 @@ 'email' => ':field is invalid!.', 'dns' => ':field is invalid!.', 'url' => ':field is invalid!.', + 'ip' => ':field is invalid!.', 'uuid' => ':field is not a uuid!.', 'int' => ':field must be a number!.', 'float' => ':field must be decimal!.', diff --git a/resources/lang/id.php b/resources/lang/id.php index 37dbd20..6b764b2 100644 --- a/resources/lang/id.php +++ b/resources/lang/id.php @@ -36,6 +36,7 @@ 'email' => ':field tidak valid!.', 'dns' => ':field tidak valid!.', 'url' => ':field tidak valid!.', + 'ip' => ':field tidak valid!.', 'uuid' => ':field bukan uuid!.', 'int' => ':field harus angka!.', 'float' => ':field harus desimal!.', From f74ecd9b8e99cdaf3e3b043c435592290434ec21 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Tue, 10 Jun 2025 10:46:37 +0700 Subject: [PATCH 21/25] feat: update .htaccess to improve request handling and redirection logic --- public/.htaccess | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/.htaccess b/public/.htaccess index d559a97..2755584 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -17,8 +17,8 @@ # Prevent directory listing Options -Indexes - # Send Requests To Front Controller... - RewriteCond %{REQUEST_FILENAME} !-d + # Redirect all requests to index.php RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ index.php [L] From 9ecf15fc0ef53052f4bc9ac0b8c664529d8eb227 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Wed, 25 Jun 2025 08:50:01 +0700 Subject: [PATCH 22/25] fix: correct color spelling in badge links in README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index caba7ec..48dbd88 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@

kamu

-PHP Programming Language -Total Downloads -repo size -Latest Stable Version -License +PHP Programming Language +Total Downloads +repo size +Latest Stable Version +License

## 📖 About Kamu From 724c9819c7508bfe9b51051870335b97c63e1339 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Wed, 25 Jun 2025 09:37:12 +0700 Subject: [PATCH 23/25] feat: add IpUserAgentMiddleware for IP and User Agent validation; update TranslatorServiceProvide to set language based on request --- app/Error/Error.php | 13 ++++++++- app/Kernel.php | 1 + app/Middleware/IpUserAgentMiddleware.php | 32 ++++++++++++++++++++++ app/Providers/TranslatorServiceProvide.php | 10 ++++++- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 app/Middleware/IpUserAgentMiddleware.php diff --git a/app/Error/Error.php b/app/Error/Error.php index 410c614..329e716 100644 --- a/app/Error/Error.php +++ b/app/Error/Error.php @@ -13,7 +13,18 @@ class Error extends BaseError */ public function render(): mixed { - // + if (!debug()) { + /** + * Jika aplikasi tidak dalam mode debug, maka error tidak ditampilkan secara rinci. + * + * Anda dapat menggunakan `$id = request()->getRequestId();` untuk menelusuri error lebih lanjut. + * Contoh penggunaannya: + * - Cek log berdasarkan ID tersebut, misalnya di folder: cache/log/kamu.log + */ + + // do something here, like logging the error + // and return basic error response to the user + } return parent::render(); } diff --git a/app/Kernel.php b/app/Kernel.php index decc0d4..4f5709b 100644 --- a/app/Kernel.php +++ b/app/Kernel.php @@ -59,6 +59,7 @@ public function middlewares(): array \App\Middleware\CorsMiddleware::class, \App\Middleware\XSSMiddleware::class, \App\Middleware\GzipMiddleware::class, + \App\Middleware\IpUserAgentMiddleware::class, ]; } diff --git a/app/Middleware/IpUserAgentMiddleware.php b/app/Middleware/IpUserAgentMiddleware.php new file mode 100644 index 0000000..477ac1f --- /dev/null +++ b/app/Middleware/IpUserAgentMiddleware.php @@ -0,0 +1,32 @@ + env('HTTP_CF_CONNECTING_IP') ? $request->server->get('HTTP_CF_CONNECTING_IP', $request->ip()) : $request->ip(), + 'user_agent' => $request->userAgent(), + ], [ + 'ip' => ['required', 'str', 'trim', 'min:2', 'max:45', 'ip'], + 'user_agent' => ['required', 'str', 'trim', 'min:64', 'max:512'], + ]); + + if ($valid->fails()) { + throw new Exception('Invalid IP or User Agent'); + } + + context('ip', $valid->ip); + context('user_agent', $valid->user_agent); + + return $next($request); + } +} diff --git a/app/Providers/TranslatorServiceProvide.php b/app/Providers/TranslatorServiceProvide.php index 9fb9da1..200781b 100644 --- a/app/Providers/TranslatorServiceProvide.php +++ b/app/Providers/TranslatorServiceProvide.php @@ -7,6 +7,8 @@ class TranslatorServiceProvide extends Provider { + private array $allowedLanguages = ['id', 'en']; + /** * Jalankan sewaktu aplikasi dinyalakan. * @@ -14,6 +16,12 @@ class TranslatorServiceProvide extends Provider */ public function booting() { - Trans::setLanguage('id'); + $requestLang = strtolower(request()->get('lang', 'id')); + + if (!in_array($requestLang, $this->allowedLanguages, true)) { + $requestLang = 'id'; + } + + Trans::setLanguage($requestLang); } } From 1601fab170b7137e9bd4563745a243e89e38b2e1 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Sun, 6 Jul 2025 19:14:25 +0700 Subject: [PATCH 24/25] feat: replace XSSMiddleware with ResponseMiddleware and update GzipMiddleware to handle Generator responses --- app/Kernel.php | 2 +- app/Middleware/GzipMiddleware.php | 11 ++++++++--- .../{XSSMiddleware.php => ResponseMiddleware.php} | 7 +++++-- 3 files changed, 14 insertions(+), 6 deletions(-) rename app/Middleware/{XSSMiddleware.php => ResponseMiddleware.php} (75%) diff --git a/app/Kernel.php b/app/Kernel.php index 4f5709b..0f6c921 100644 --- a/app/Kernel.php +++ b/app/Kernel.php @@ -57,7 +57,7 @@ public function middlewares(): array { return [ \App\Middleware\CorsMiddleware::class, - \App\Middleware\XSSMiddleware::class, + \App\Middleware\ResponseMiddleware::class, \App\Middleware\GzipMiddleware::class, \App\Middleware\IpUserAgentMiddleware::class, ]; diff --git a/app/Middleware/GzipMiddleware.php b/app/Middleware/GzipMiddleware.php index 4eb34be..d4cdc5c 100644 --- a/app/Middleware/GzipMiddleware.php +++ b/app/Middleware/GzipMiddleware.php @@ -7,14 +7,15 @@ use Core\Http\Respond; use Core\Http\Stream; use Core\Middleware\MiddlewareInterface; +use Generator; final class GzipMiddleware implements MiddlewareInterface { - public function handle(Request $request, Closure $next): Stream|Respond + public function handle(Request $request, Closure $next): Stream|Respond|Generator { $response = $next($request); - if ($response instanceof Stream) { + if ($response instanceof Stream || $response instanceof Generator) { return $response; } @@ -32,7 +33,11 @@ public function handle(Request $request, Closure $next): Stream|Respond return $response; } - $compressed = gzencode($response->getContent(false), 3); + if (!$response->getContent()) { + return $response; + } + + $compressed = gzencode($response->getContent(), 3); if ($compressed === false) { return $response; diff --git a/app/Middleware/XSSMiddleware.php b/app/Middleware/ResponseMiddleware.php similarity index 75% rename from app/Middleware/XSSMiddleware.php rename to app/Middleware/ResponseMiddleware.php index dccd976..62d10e5 100644 --- a/app/Middleware/XSSMiddleware.php +++ b/app/Middleware/ResponseMiddleware.php @@ -6,15 +6,18 @@ use Core\Http\Request; use Core\Middleware\MiddlewareInterface; -final class XSSMiddleware implements MiddlewareInterface +final class ResponseMiddleware implements MiddlewareInterface { public function handle(Request $request, Closure $next) { + $headers = respond()->getHeader(); + $headers->set('X-Accel-Buffering', 'no'); + if (!https()) { return $next($request); } - respond()->getHeader() + $headers ->set('Referrer-Policy', 'strict-origin-when-cross-origin') ->set('Content-Security-Policy', 'upgrade-insecure-requests') ->set('X-Content-Type-Options', 'nosniff') From b6070eda81d2bc84b81076c5e9dd6f7f7eaf55c2 Mon Sep 17 00:00:00 2001 From: dewanakl Date: Mon, 7 Jul 2025 15:22:27 +0700 Subject: [PATCH 25/25] fix: remove redundant directory listing prevention from .htaccess --- public/.htaccess | 3 --- 1 file changed, 3 deletions(-) diff --git a/public/.htaccess b/public/.htaccess index 2755584..88afaf2 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -14,9 +14,6 @@ RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301] - # Prevent directory listing - Options -Indexes - # Redirect all requests to index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d