From bc46bb0e586fc5dc63705574ada05c9d1e84e155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20HEYD?= Date: Wed, 2 Oct 2024 17:32:04 +0200 Subject: [PATCH 1/5] feat: keep alive disabling --- docs/docs/8.x/configuration/app.md | 38 ++++++++++++++++++---- src/config/app.php | 3 ++ src/resources/views/gpt/layout.blade.php | 2 ++ src/resources/views/layout/index.blade.php | 2 ++ src/routes/boilerplate.php | 4 ++- tests/config.php | 1 + 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/docs/docs/8.x/configuration/app.md b/docs/docs/8.x/configuration/app.md index 041b0f3e..02d1c77a 100644 --- a/docs/docs/8.x/configuration/app.md +++ b/docs/docs/8.x/configuration/app.md @@ -6,22 +6,34 @@ The `config/boilerplate/app.php` file allows to define the general parameters of ```php "http://..../admin" - 'prefix' => 'admin', + 'prefix' => 'admin', // Backend domain if different as current domain. Ex: "admin.mydomain.tld" - 'domain' => '', + 'domain' => '', // Redirect to this route after login - 'redirectTo' => 'boilerplate.dashboard', + 'redirectTo' => 'boilerplate.dashboard', // Activating daily logs and showing log viewer - 'logs' => true, - + 'logs' => true, + // When set to true, allows admins to view the site as a user of their choice 'allowImpersonate' => false, + + // If true, the session will be kept alive and the user must log out + 'keepalive' => true, + + // Allows to generate text with ChatGPT in TinyMCE + 'openai' => [ + 'key' => env('OPENAI_API_KEY'), + 'model' => 'gpt-3.5-turbo', + 'organization' => env('OPENAI_API_ORGANIZATION'), + ], ]; + ``` --- @@ -76,4 +88,18 @@ Log viewer is only visible by administrators by default. When `allowImpersonate` is set to true, admins are allowed to view the site as the user of their choice by using a switch in the navbar. -> You can't switch to an admin user \ No newline at end of file +> You can't switch to an admin user + +--- + +## keepalive + +Allows enabling or disabling session keep alive. If the value is set to `true`, the session will be maintained until the user logs out. + +Conversely, if the value is set to `false`, the user will be logged out when the session expires. + +--- + +## openai + +Allows setting variables for using ChatGPT in TinyMCE \ No newline at end of file diff --git a/src/config/app.php b/src/config/app.php index 29e17fb4..f77ff70d 100755 --- a/src/config/app.php +++ b/src/config/app.php @@ -16,6 +16,9 @@ // When set to true, allows admins to view the site as a user of their choice 'allowImpersonate' => false, + // If true, the session will be kept alive and the user must log out + 'keepalive' => true, + // Allows to generate text with ChatGPT in TinyMCE 'openai' => [ 'key' => env('OPENAI_API_KEY'), diff --git a/src/resources/views/gpt/layout.blade.php b/src/resources/views/gpt/layout.blade.php index 75295d01..e45b9180 100644 --- a/src/resources/views/gpt/layout.blade.php +++ b/src/resources/views/gpt/layout.blade.php @@ -20,12 +20,14 @@ @component('boilerplate::minify') @endcomponent @stack('plugin-js') diff --git a/src/resources/views/layout/index.blade.php b/src/resources/views/layout/index.blade.php index 90f6dddf..35a46d94 100755 --- a/src/resources/views/layout/index.blade.php +++ b/src/resources/views/layout/index.blade.php @@ -26,12 +26,14 @@ var bpRoutes={ settings:"{{ route('boilerplate.user.settings',null,false) }}" }; +@if(config('boilerplate.app.keepalive', false)) var session={ keepalive:"{{ route('boilerplate.keepalive', null, false) }}", expire:{{ time() + config('session.lifetime') * 60 }}, lifetime:{{ config('session.lifetime') * 60 }}, id:"{{ session()->getId() }}" }; +@endif @endcomponent @stack('plugin-js') diff --git a/src/routes/boilerplate.php b/src/routes/boilerplate.php index 2ebd9c77..59ec18b3 100755 --- a/src/routes/boilerplate.php +++ b/src/routes/boilerplate.php @@ -89,7 +89,9 @@ Route::get('/demo', [DemoController::class, 'index'])->name('demo'); // Session keep-alive - Route::post('keep-alive', [UsersController::class, 'keepAlive'])->name('keepalive'); + if (config('boilerplate.app.keepalive', false)) { + Route::post('keep-alive', [UsersController::class, 'keepAlive'])->name('keepalive'); + } // Datatables Route::post('datatables/{slug}', [DatatablesController::class, 'make'])->name('datatables'); diff --git a/tests/config.php b/tests/config.php index dfb5dd98..22f5adfb 100644 --- a/tests/config.php +++ b/tests/config.php @@ -8,6 +8,7 @@ 'app.url' => 'http://localhost', 'app.fallback_locale' => 'en', 'boilerplate.app.locale' => 'en', + 'boilerplate.app.keepalive' => 'true', 'boilerplate.locale.switch' => true, 'boilerplate.locale.allowed' => ['fr'], 'boilerplate.app.allowImpersonate' => true, From 369074cba5417ccf7090e49bf83bccfb7d183ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20HEYD?= Date: Fri, 4 Oct 2024 12:58:30 +0200 Subject: [PATCH 2/5] feat: optional keep alive --- src/Controllers/Auth/LoginController.php | 11 +++-- src/Controllers/GptController.php | 2 + src/Controllers/Users/UsersController.php | 2 +- src/package.json | 7 +-- src/public/boilerplate.min.js | 2 +- src/public/mix-manifest.json | 2 +- src/resources/assets/js/boilerplate.js | 48 +++++++++++++++----- src/resources/lang/bg/auth.php | 4 ++ src/resources/lang/en/auth.php | 4 ++ src/resources/lang/es/auth.php | 4 ++ src/resources/lang/fa/auth.php | 4 ++ src/resources/lang/fr/auth.php | 4 ++ src/resources/lang/it/auth.php | 4 ++ src/resources/lang/tr/auth.php | 4 ++ src/resources/views/auth/login.blade.php | 22 +++++---- src/resources/views/gpt/layout.blade.php | 14 +----- src/resources/views/layout/index.blade.php | 18 +------- src/resources/views/layout/session.blade.php | 16 +++++++ src/routes/boilerplate.php | 3 +- 19 files changed, 116 insertions(+), 59 deletions(-) create mode 100644 src/resources/views/layout/session.blade.php diff --git a/src/Controllers/Auth/LoginController.php b/src/Controllers/Auth/LoginController.php index 08ffa7d5..87256a0f 100755 --- a/src/Controllers/Auth/LoginController.php +++ b/src/Controllers/Auth/LoginController.php @@ -34,7 +34,7 @@ public function redirectTo() * * @return View|Redirector */ - public function showLoginForm() + public function showLoginForm(Request $request) { $userModel = config('auth.providers.users.model'); @@ -42,7 +42,10 @@ public function showLoginForm() return redirect(route('boilerplate.register')); } - return view('boilerplate::auth.login'); + return view('boilerplate::auth.login', [ + 'expired' => $request->get('expired', false), + 'redirect' => $request->get('path', false), + ]); } /** @@ -77,9 +80,11 @@ protected function sendLoginResponse(Request $request) if ($this->authenticated($request, $this->guard()->user())) { $this->guard()->user()->update(['last_login' => Carbon::now()->toDateTimeString()]); + $path = $request->post('redirect'); + return $request->wantsJson() ? new JsonResponse([], 204) - : redirect()->intended($this->redirectPath()); + : redirect()->intended($path ?: $this->redirectPath()); } } diff --git a/src/Controllers/GptController.php b/src/Controllers/GptController.php index 9682f419..c1c385ae 100755 --- a/src/Controllers/GptController.php +++ b/src/Controllers/GptController.php @@ -151,6 +151,7 @@ private function processPrompt($request) /** * Stream the result from OpenAI API. * + * @codeCoverageIgnore * @param Request $request * @return void */ @@ -191,6 +192,7 @@ public function stream(Request $request) /** * Send curl request to OpenAI Api. * + * @codeCoverageIgnore * @param $prompt * @param $callback * @return bool|string diff --git a/src/Controllers/Users/UsersController.php b/src/Controllers/Users/UsersController.php index 57799440..fed39685 100755 --- a/src/Controllers/Users/UsersController.php +++ b/src/Controllers/Users/UsersController.php @@ -301,7 +301,7 @@ public function getAvatarFromGravatar() * * @param Request $request */ - public function keepAlive(Request $request) + public function sessionKeepAlive(Request $request) { if ($request->post('id') !== null) { session()->setId($request->post('id')); diff --git a/src/package.json b/src/package.json index 7e43d1e5..6ce92f94 100755 --- a/src/package.json +++ b/src/package.json @@ -12,14 +12,14 @@ }, "devDependencies": { "clean-webpack-plugin": "^3.0.0", + "husky": "^9.1.6", "laravel-echo": "^1.11.7", "laravel-mix": "^6.0.42", "postcss": "^8.4.6", "pusher-js": "^7.1.1-beta", "resolve-url-loader": "^3.1.4", "sass": "1.32.13", - "sass-loader": "^12.6.0", - "husky": "^9.1.6" + "sass-loader": "^12.6.0" }, "dependencies": { "@fortawesome/fontawesome-free": "^6.1.1", @@ -35,6 +35,7 @@ "moment": "^2.30.1", "patch-package": "^8.0.0", "spectrum-colorpicker2": "^2.0.10", - "tinymce": "^5.10.9" + "tinymce": "^5.10.9", + "worker-timers": "^8.0.7" } } diff --git a/src/public/boilerplate.min.js b/src/public/boilerplate.min.js index 583f88c5..a6e6fa9d 100644 --- a/src/public/boilerplate.min.js +++ b/src/public/boilerplate.min.js @@ -1 +1 @@ -(()=>{function e(e,t){$.ajax({url:bpRoutes.settings,type:"post",data:{name:e,value:t}})}function t(e,t){if("undefined"==typeof tinymce||0===tinymce.get().length)return!1;var n=[];tinymce.editors.forEach((function(a,o){1===$("#"+a.settings.id).length&&(a.settings.skin=e,a.settings.content_css=t,n.push(a.settings))})),tinymce.remove(),n.forEach((function(e){$("#"+e.id).tinymce(e)}))}window.toastr.options={},window.growl=function(e,t){types=["info","error","warning","success"],void 0!==t&&types.includes(t)||(t="info"),window.toastr[t](e)},window.intervals=[],window.loadedAssets=[],window.registerAsset=function(e,t){loadedAssets.includes(e)||(loadedAssets.push(e),"function"==typeof t&&t())},window.loadScript=function(e,t){if(!loadedAssets.includes(e))if(document.querySelector('script[src="'+e+'"]'))registerAsset(e,t);else if(!loadedAssets.includes(e)){var n=document.createElement("script");n.onload=function(){registerAsset(e,t)},n.src=e,document.body.appendChild(n)}},window.loadStylesheet=function(e,t){if(!loadedAssets.includes(e))if(document.querySelector('link[rel="stylesheet"][href="'+e+'"]'))registerAsset(e,t);else{var n=document.createElement("link");n.onload=function(){registerAsset(e,t)},n.href=e,n.rel="stylesheet",document.querySelector("title").appendChild(n)}},window.whenAssetIsLoaded=function(e,t){var n=getIntervalUid();intervals[n]=setInterval((function(){loadedAssets.includes(e)&&(clearInterval(intervals[n]),t())}))},window.whenIsLoaded=function(e,t){return window.whenAssetIsLoaded(e,t)},window.getIntervalUid=function(){return String.fromCharCode(Math.floor(11*Math.random()))+Math.floor(1e6*Math.random())},$(document).on("focusin",(function(e){$(e.target).closest(".tox-tinymce, .tox-tinymce-aux, .moxman-window, .tam-assetmanager-root").length&&e.stopImmediatePropagation()})),$((function(){$(document).tooltip({container:"body",selector:'[data-toggle="tooltip"]',delay:{show:500,hide:100},html:!0,trigger:"hover"}),"undefined"!=typeof session&&setInterval((function(){var e=Math.round(+new Date/1e3);Math.round(+new Date/1e3)===session.expire-10&&(session.expire=e+session.lifetime,$.ajax({url:session.keepalive,type:"post",data:{id:session.id}}))}),1e3);var n=function(){$('[data-widget="darkmode"]').html(''),$("body").addClass("dark-mode"),$("#content-wrapper").addClass("accent-light").removeClass("accent-dark"),$("nav.main-header").addClass("navbar-dark"),e("darkmode",!0),t("boilerplate-dark","boilerplate-dark")};"2"==$("body").data("darkmode")&&window.matchMedia("(prefers-color-scheme: dark)")&&n(),$('[data-widget="darkmode"]').on("click",(function(a){a.preventDefault(),$("body").hasClass("dark-mode")?($(this).html(''),$("body").removeClass("dark-mode"),$("#content-wrapper").removeClass("accent-light").addClass("accent-dark"),$("nav.main-header").removeClass("navbar-dark").addClass("navbar-"+$("nav.main-header").data("type")),e("darkmode",!1),t("oxide",null)):n()})),$(".sidebar-toggle").on("click",(function(t){t.preventDefault(),e("sidebar-collapsed",!$("body").hasClass("sidebar-collapse"))})),$(".logout").click((function(e){e.preventDefault(),bootbox.confirm($(this).attr("data-question"),(function(e){!1!==e&&$("#logout-form").submit()}))})),$("#impersonate-user").on("select2:select",(function(){$.ajax({url:$(this).data("route"),type:"post",data:{id:$(this).val()},success:function(){window.location.reload()}})})).on("select2:clear",(function(){$.ajax({url:$(this).data("clear"),type:"get",success:function(){window.location.reload()}})})),$("[data-toggle=password]").on("click",(function(e){e.preventDefault(),$(this).children().toggleClass("fa-eye fa-eye-slash");var t=$(this).closest(".input-group").find("input");t.attr("type","text"===t.attr("type")?"password":"text")}))})),$(document).on("keyup",".input-clearable input",(function(){$(this).parent().find(".fa").hide(),""!==$(this).val()&&$(this).parent().find(".fa").show()})),$(document).on("click",".input-clearable .fa",(function(){$(this).parent().find("input").val("").trigger("keyup")}))})(); \ No newline at end of file +(()=>{var e={389:function(e,t){!function(e){"use strict";var t=function(e){return function(t){var n=e(t);return t.add(n),n}},n=function(e){return function(t,n){return e.set(t,n),n}},r=void 0===Number.MAX_SAFE_INTEGER?9007199254740991:Number.MAX_SAFE_INTEGER,o=536870912,i=2*o,s=function(e,t){return function(n){var s=t.get(n),a=void 0===s?n.size:sr)throw new Error("Congratulations, you created a collection of unique numbers which uses all available integers!");for(;n.has(a);)a=Math.floor(Math.random()*r);return e(n,a)}},a=new WeakMap,d=n(a),l=s(d,a),c=t(l);e.addUniqueNumber=c,e.generateUniqueNumber=l}(t)}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var i=t[r]={exports:{}};return e[r].call(i.exports,i,i.exports,n),i.exports}(()=>{"use strict";var e=n(389);const t=((e,t)=>{let n=null;return()=>{if(null!==n)return n;const r=new Blob([t],{type:"application/javascript; charset=utf-8"}),o=URL.createObjectURL(r);return n=e(o),setTimeout((()=>URL.revokeObjectURL(o))),n}})((t=>{const n=new Map([[0,()=>{}]]),r=new Map([[0,()=>{}]]),o=new Map,i=new Worker(t);i.addEventListener("message",(({data:e})=>{if(void 0!==(t=e).method&&"call"===t.method){const{params:{timerId:t,timerType:i}}=e;if("interval"===i){const e=n.get(t);if(void 0===typeof e)throw new Error("The timer is in an undefined state.");if("number"==typeof e){const n=o.get(e);if(void 0===n||n.timerId!==t||n.timerType!==i)throw new Error("The timer is in an undefined state.")}else"function"==typeof e&&e()}else if("timeout"===i){const e=r.get(t);if(void 0===typeof e)throw new Error("The timer is in an undefined state.");if("number"==typeof e){const n=o.get(e);if(void 0===n||n.timerId!==t||n.timerType!==i)throw new Error("The timer is in an undefined state.")}else"function"==typeof e&&(e(),r.delete(t))}}else{if(!(e=>"number"==typeof e.id&&"boolean"==typeof e.result)(e)){const{error:{message:t}}=e;throw new Error(t)}{const{id:t}=e,i=o.get(t);if(void 0===i)throw new Error("The timer is in an undefined state.");const{timerId:s,timerType:a}=i;o.delete(t),"interval"===a?n.delete(s):r.delete(s)}}var t}));return{clearInterval:t=>{if("function"==typeof n.get(t)){const r=(0,e.generateUniqueNumber)(o);o.set(r,{timerId:t,timerType:"interval"}),n.set(t,r),i.postMessage({id:r,method:"clear",params:{timerId:t,timerType:"interval"}})}},clearTimeout:t=>{if("function"==typeof r.get(t)){const n=(0,e.generateUniqueNumber)(o);o.set(n,{timerId:t,timerType:"timeout"}),r.set(t,n),i.postMessage({id:n,method:"clear",params:{timerId:t,timerType:"timeout"}})}},setInterval:(t,r=0,...o)=>{const s=(0,e.generateUniqueNumber)(n);return n.set(s,(()=>{t(...o),"function"==typeof n.get(s)&&i.postMessage({id:null,method:"set",params:{delay:r,now:performance.timeOrigin+performance.now(),timerId:s,timerType:"interval"}})})),i.postMessage({id:null,method:"set",params:{delay:r,now:performance.timeOrigin+performance.now(),timerId:s,timerType:"interval"}}),s},setTimeout:(t,n=0,...o)=>{const s=(0,e.generateUniqueNumber)(r);return r.set(s,(()=>t(...o))),i.postMessage({id:null,method:"set",params:{delay:n,now:performance.timeOrigin+performance.now(),timerId:s,timerType:"timeout"}}),s}}}),'(()=>{"use strict";const e=new Map,t=new Map,r=t=>{const r=e.get(t);return void 0!==r&&(clearTimeout(r),e.delete(t),!0)},s=e=>{const r=t.get(e);return void 0!==r&&(clearTimeout(r),t.delete(e),!0)},o=(e,t)=>{const r=performance.now(),s=e+t-r-performance.timeOrigin;return{expected:r+s,remainingDelay:s}},i=(e,t,r,s)=>{const o=r-performance.now();o>0?e.set(t,setTimeout(i,o,e,t,r,s)):(e.delete(t),postMessage({id:null,method:"call",params:{timerId:t,timerType:s}}))};addEventListener("message",(({data:n})=>{try{if("clear"===n.method){const{id:e,params:{timerId:t,timerType:o}}=n;if("interval"===o)postMessage({id:e,result:r(t)});else{if("timeout"!==o)throw new Error(\'The given type "\'.concat(o,\'" is not supported\'));postMessage({id:e,result:s(t)})}}else{if("set"!==n.method)throw new Error(\'The given method "\'.concat(n.method,\'" is not supported\'));{const{params:{delay:r,now:s,timerId:a,timerType:m}}=n;if("interval"===m)((t,r,s)=>{const{expected:n,remainingDelay:a}=o(t,s);e.set(r,setTimeout(i,a,e,r,n,"interval"))})(r,a,s);else{if("timeout"!==m)throw new Error(\'The given type "\'.concat(m,\'" is not supported\'));((e,r,s)=>{const{expected:n,remainingDelay:a}=o(e,s);t.set(r,setTimeout(i,a,t,r,n,"timeout"))})(r,a,s)}}}}catch(e){postMessage({error:{message:e.message},id:n.id,result:null})}}))})();'),r=e=>t().clearInterval(e),o=(...e)=>t().setInterval(...e);function i(e,t){$.ajax({url:bpRoutes.settings,type:"post",data:{name:e,value:t}})}function s(e,t){if("undefined"==typeof tinymce||0===tinymce.get().length)return!1;var n=[];tinymce.editors.forEach((function(r,o){1===$("#"+r.settings.id).length&&(r.settings.skin=e,r.settings.content_css=t,n.push(r.settings))})),tinymce.remove(),n.forEach((function(e){$("#"+e.id).tinymce(e)}))}window.toastr.options={},window.growl=function(e,t){void 0!==t&&["info","error","warning","success"].includes(t)||(t="info"),window.toastr[t](e)},window.intervals=[],window.loadedAssets=[],window.registerAsset=function(e,t){loadedAssets.includes(e)||(loadedAssets.push(e),"function"==typeof t&&t())},window.loadScript=function(e,t){if(!loadedAssets.includes(e))if(document.querySelector('script[src="'+e+'"]'))registerAsset(e,t);else if(!loadedAssets.includes(e)){var n=document.createElement("script");n.onload=function(){registerAsset(e,t)},n.src=e,document.body.appendChild(n)}},window.loadStylesheet=function(e,t){if(!loadedAssets.includes(e))if(document.querySelector('link[rel="stylesheet"][href="'+e+'"]'))registerAsset(e,t);else{var n=document.createElement("link");n.onload=function(){registerAsset(e,t)},n.href=e,n.rel="stylesheet",document.querySelector("title").appendChild(n)}},window.whenAssetIsLoaded=function(e,t){var n=getIntervalUid();intervals[n]=o((function(){loadedAssets.includes(e)&&(r(intervals[n]),t())}))},window.whenIsLoaded=function(e,t){return window.whenAssetIsLoaded(e,t)},window.getIntervalUid=function(){return String.fromCharCode(Math.floor(11*Math.random()))+Math.floor(1e6*Math.random())},$(document).on("focusin",(function(e){$(e.target).closest(".tox-tinymce, .tox-tinymce-aux, .moxman-window, .tam-assetmanager-root").length&&e.stopImmediatePropagation()})),$((function(){if($(document).tooltip({container:"body",selector:'[data-toggle="tooltip"]',delay:{show:500,hide:100},html:!0,trigger:"hover"}),"undefined"!=typeof session)if(void 0!==session.keepalive)var e=o((function(){var t=Math.round(+new Date/1e3);t===session.expire-10&&(session.expire=t+session.lifetime,$.ajax({url:session.keepalive,type:"post",data:{id:session.id}})),t>session.expire+1&&(r(e),window.location=session.login)}),1e3);else var t=o((function(){var e=Math.round(+new Date/1e3);e===session.expire-15&&growl(session.warning,"warning"),e>session.expire+1&&(r(t),window.location=session.login)}),1e3);var n=function(){$('[data-widget="darkmode"]').html(''),$("body").addClass("dark-mode"),$("#content-wrapper").addClass("accent-light").removeClass("accent-dark"),$("nav.main-header").addClass("navbar-dark"),i("darkmode",!0),s("boilerplate-dark","boilerplate-dark")};"2"==$("body").data("darkmode")&&window.matchMedia("(prefers-color-scheme: dark)")&&n(),$('[data-widget="darkmode"]').on("click",(function(e){e.preventDefault(),$("body").hasClass("dark-mode")?($(this).html(''),$("body").removeClass("dark-mode"),$("#content-wrapper").removeClass("accent-light").addClass("accent-dark"),$("nav.main-header").removeClass("navbar-dark").addClass("navbar-"+$("nav.main-header").data("type")),i("darkmode",!1),s("oxide",null)):n()})),$(".sidebar-toggle").on("click",(function(e){e.preventDefault(),i("sidebar-collapsed",!$("body").hasClass("sidebar-collapse"))})),$(".logout").click((function(e){e.preventDefault(),bootbox.confirm($(this).attr("data-question"),(function(e){!1!==e&&$("#logout-form").submit()}))})),$("#impersonate-user").on("select2:select",(function(){$.ajax({url:$(this).data("route"),type:"post",data:{id:$(this).val()},success:function(){window.location.reload()}})})).on("select2:clear",(function(){$.ajax({url:$(this).data("clear"),type:"get",success:function(){window.location.reload()}})})),$("[data-toggle=password]").on("click",(function(e){e.preventDefault(),$(this).children().toggleClass("fa-eye fa-eye-slash");var t=$(this).closest(".input-group").find("input");t.attr("type","text"===t.attr("type")?"password":"text")}))})),$(document).on("keyup",".input-clearable input",(function(){$(this).parent().find(".fa").hide(),""!==$(this).val()&&$(this).parent().find(".fa").show()})),$(document).on("click",".input-clearable .fa",(function(){$(this).parent().find("input").val("").trigger("keyup")}))})()})(); \ No newline at end of file diff --git a/src/public/mix-manifest.json b/src/public/mix-manifest.json index 715be8a5..b66ef9ef 100644 --- a/src/public/mix-manifest.json +++ b/src/public/mix-manifest.json @@ -1,7 +1,7 @@ { "/bootstrap.min.js": "/bootstrap.min.js?id=97e6c8c49b931c59c6c2da28d0ce5efe", "/admin-lte.min.js": "/admin-lte.min.js?id=c85385f67373feb418b4d9b031c7fe5c", - "/boilerplate.min.js": "/boilerplate.min.js?id=a8bf041ba64d0bda7f2539906d53a370", + "/boilerplate.min.js": "/boilerplate.min.js?id=dcbfff59e206dde9493a120546040204", "/avatar.min.js": "/avatar.min.js?id=b88909deff24143b389d29219a8c47a9", "/dashboard.min.js": "/dashboard.min.js?id=6513cfc47ed3cfac4507bb27ea361320", "/plugins/codemirror/jquery.codemirror.min.js": "/plugins/codemirror/jquery.codemirror.min.js?id=aa557346ea5f36bf351fd2fccc8fa8d8", diff --git a/src/resources/assets/js/boilerplate.js b/src/resources/assets/js/boilerplate.js index 6c3745cc..1cc273be 100755 --- a/src/resources/assets/js/boilerplate.js +++ b/src/resources/assets/js/boilerplate.js @@ -1,7 +1,8 @@ window.toastr.options = {} +import { clearInterval, setInterval } from 'worker-timers'; window.growl = (message, type) => { - types = ['info', 'error', 'warning', 'success']; + let types = ['info', 'error', 'warning', 'success']; if (typeof type === "undefined" || !types.includes(type)) { type = 'info'; @@ -123,17 +124,42 @@ $(() => { }) if(typeof session !== 'undefined') { - setInterval(function () { - var timestamp = Math.round(+new Date() / 1000); - if (Math.round(+new Date() / 1000) === (session.expire - 10)) { - session.expire = timestamp + session.lifetime; - $.ajax({ - url: session.keepalive, - type: 'post', - data: {id: session.id} - }) + if (typeof session.keepalive !== 'undefined') { + let sessionKeepAlive = function() { + let timestamp = Math.round(+new Date() / 1000); + + if (timestamp === (session.expire - 10)) { + session.expire = timestamp + session.lifetime; + $.ajax({ + url: session.keepalive, + type: 'post', + data: {id: session.id} + }) + } + + if (timestamp > (session.expire + 1)) { + clearInterval(sessionKeepAliveInterval); + window.location = session.login; + } } - }, 1000); + + let sessionKeepAliveInterval = setInterval(sessionKeepAlive, 1000); + } else { + let expireSession = function() { + let timestamp = Math.round(+new Date() / 1000); + + if (timestamp === (session.expire - 15)) { + growl(session.warning, 'warning'); + } + + if (timestamp > (session.expire + 1)) { + clearInterval(sessionCheckInterval); + window.location = session.login; + } + } + + let sessionCheckInterval = setInterval(expireSession, 1000); + } } let enableDarkMode = function() { diff --git a/src/resources/lang/bg/auth.php b/src/resources/lang/bg/auth.php index 1d83e290..5133ae48 100755 --- a/src/resources/lang/bg/auth.php +++ b/src/resources/lang/bg/auth.php @@ -43,4 +43,8 @@ 'not_authorized' => ':user няма разрешение да достъпи страницата :page.', 'back_to_dashboard' => 'Връщане към контролния панел', ], + 'session' => [ + 'warning' => 'Сесията ви скоро ще изтече', + 'expired' => 'Сесията ви изтече, моля влезте отново' + ] ]; diff --git a/src/resources/lang/en/auth.php b/src/resources/lang/en/auth.php index 09fa8eb3..64762cda 100755 --- a/src/resources/lang/en/auth.php +++ b/src/resources/lang/en/auth.php @@ -43,4 +43,8 @@ 'not_authorized' => ':user is not authorized to access page :page.', 'back_to_dashboard' => 'Return to the dashboard', ], + 'session' => [ + 'warning' => 'Your session will expire soon', + 'expired' => 'Your session has expired, please log in again' + ] ]; diff --git a/src/resources/lang/es/auth.php b/src/resources/lang/es/auth.php index 495e706b..06fa1869 100644 --- a/src/resources/lang/es/auth.php +++ b/src/resources/lang/es/auth.php @@ -43,4 +43,8 @@ 'not_authorized' => ':user no está autorizado para acceder a la página :page.', 'back_to_dashboard' => 'Volver al panel de control', ], + 'session' => [ + 'warning' => 'Tu sesión está a punto de expirar', + 'expired' => 'Tu sesión ha expirado, por favor inicia sesión de nuevo' + ] ]; diff --git a/src/resources/lang/fa/auth.php b/src/resources/lang/fa/auth.php index 28aabf44..b32a8aba 100644 --- a/src/resources/lang/fa/auth.php +++ b/src/resources/lang/fa/auth.php @@ -38,4 +38,8 @@ 'intro' => 'این اولین ورود شماست لطفا رمز عبور خود را وارد کنید.', 'button' => 'ورود', ], + 'session' => [ + 'warning' => 'نشست شما به زودی منقضی می‌شود', + 'expired' => 'نشست شما منقضی شده است، لطفاً دوباره وارد شوید' + ] ]; diff --git a/src/resources/lang/fr/auth.php b/src/resources/lang/fr/auth.php index fccc9429..33135c70 100755 --- a/src/resources/lang/fr/auth.php +++ b/src/resources/lang/fr/auth.php @@ -43,4 +43,8 @@ 'not_authorized' => ':user n\'est pas autorisé(e) à accéder à la page :page.', 'back_to_dashboard' => 'Retourner au tableau de bord', ], + 'session' => [ + 'warning' => 'Votre session va bientôt expirer', + 'expired' => 'Votre session a expirée, veuillez vous reconnecter' + ] ]; diff --git a/src/resources/lang/it/auth.php b/src/resources/lang/it/auth.php index 95c65cc2..8b36e8b8 100644 --- a/src/resources/lang/it/auth.php +++ b/src/resources/lang/it/auth.php @@ -43,4 +43,8 @@ 'not_authorized' => ':user non è autorizzato ad accedere alla pagina :page.', 'back_to_dashboard' => 'Torna alla dashboard', ], + 'session' => [ + 'warning' => 'La tua sessione sta per scadere', + 'expired' => 'La tua sessione è scaduta, per favore effettua nuovamente il login' + ] ]; diff --git a/src/resources/lang/tr/auth.php b/src/resources/lang/tr/auth.php index 61d07662..b15d6f32 100755 --- a/src/resources/lang/tr/auth.php +++ b/src/resources/lang/tr/auth.php @@ -43,4 +43,8 @@ 'not_authorized' => ':user, :page sayfasına erişim izni bulunmamaktadır.', 'back_to_dashboard' => 'Kontrol paneline dön', ], + 'session' => [ + 'warning' => 'Oturumunuz yakında sona erecek', + 'expired' => 'Oturumunuz sona erdi, lütfen tekrar giriş yapın' + ] ]; diff --git a/src/resources/views/auth/login.blade.php b/src/resources/views/auth/login.blade.php index ed180582..c3e3f2c7 100755 --- a/src/resources/views/auth/login.blade.php +++ b/src/resources/views/auth/login.blade.php @@ -2,15 +2,21 @@ @section('content') @component('boilerplate::auth.loginbox') + @if($expired) +

@lang('boilerplate::auth.session.expired')

+ @endif - @component('boilerplate::form', ['route' => 'boilerplate.login']) - @component('boilerplate::input', ['name' => 'email', 'placeholder' => 'boilerplate::auth.fields.email', 'append-text' => 'fas fa-fw fa-envelope', 'type' => 'email'])@endcomponent - @component('boilerplate::password', ['name' => 'password', 'placeholder' => 'boilerplate::auth.fields.password', 'check' => false])@endcomponent + + @if($redirect) + + @endif + +
- @component('boilerplate::icheck', ['name' => 'remember', 'checked' => old('remember') == 'on', 'label' => 'boilerplate::auth.login.rememberme', 'class' => 'text-sm'])@endcomponent +
- @endcomponent +

@@ -25,9 +31,9 @@ @if(config('boilerplate.locale.switch', false))

@endif diff --git a/src/resources/views/gpt/layout.blade.php b/src/resources/views/gpt/layout.blade.php index e45b9180..ffa04139 100644 --- a/src/resources/views/gpt/layout.blade.php +++ b/src/resources/views/gpt/layout.blade.php @@ -17,19 +17,7 @@ @stack('css') - @component('boilerplate::minify') - - @endcomponent + @include('boilerplate::layout.session') @stack('plugin-js') diff --git a/src/resources/views/layout/index.blade.php b/src/resources/views/layout/index.blade.php index 35a46d94..bc7d5a7c 100755 --- a/src/resources/views/layout/index.blade.php +++ b/src/resources/views/layout/index.blade.php @@ -19,23 +19,7 @@ -@component('boilerplate::minify') - -@endcomponent + @include('boilerplate::layout.session') @stack('plugin-js') diff --git a/src/resources/views/layout/session.blade.php b/src/resources/views/layout/session.blade.php new file mode 100644 index 00000000..abe5f8a4 --- /dev/null +++ b/src/resources/views/layout/session.blade.php @@ -0,0 +1,16 @@ +@component('boilerplate::minify') + +@endcomponent \ No newline at end of file diff --git a/src/routes/boilerplate.php b/src/routes/boilerplate.php index 59ec18b3..82e0cbb7 100755 --- a/src/routes/boilerplate.php +++ b/src/routes/boilerplate.php @@ -1,5 +1,6 @@ name('keepalive'); + Route::post('keep-alive', [UsersController::class, 'sessionKeepAlive'])->name('session.keepalive'); } // Datatables From aa45e81db2cd763611d4ec9e8f8ef915e5c3de9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20HEYD?= Date: Fri, 4 Oct 2024 13:12:46 +0200 Subject: [PATCH 3/5] fix: styleCI --- src/Controllers/Auth/LoginController.php | 2 +- src/Controllers/GptController.php | 4 ++-- src/resources/lang/bg/auth.php | 4 ++-- src/resources/lang/en/auth.php | 4 ++-- src/resources/lang/es/auth.php | 4 ++-- src/resources/lang/fa/auth.php | 4 ++-- src/resources/lang/fr/auth.php | 4 ++-- src/resources/lang/it/auth.php | 4 ++-- src/resources/lang/tr/auth.php | 4 ++-- src/routes/boilerplate.php | 1 - 10 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Controllers/Auth/LoginController.php b/src/Controllers/Auth/LoginController.php index 87256a0f..dd44e3bd 100755 --- a/src/Controllers/Auth/LoginController.php +++ b/src/Controllers/Auth/LoginController.php @@ -43,7 +43,7 @@ public function showLoginForm(Request $request) } return view('boilerplate::auth.login', [ - 'expired' => $request->get('expired', false), + 'expired' => $request->get('expired', false), 'redirect' => $request->get('path', false), ]); } diff --git a/src/Controllers/GptController.php b/src/Controllers/GptController.php index c1c385ae..c3e15471 100755 --- a/src/Controllers/GptController.php +++ b/src/Controllers/GptController.php @@ -151,9 +151,9 @@ private function processPrompt($request) /** * Stream the result from OpenAI API. * - * @codeCoverageIgnore * @param Request $request * @return void + * @codeCoverageIgnore */ public function stream(Request $request) { @@ -192,10 +192,10 @@ public function stream(Request $request) /** * Send curl request to OpenAI Api. * - * @codeCoverageIgnore * @param $prompt * @param $callback * @return bool|string + * @codeCoverageIgnore */ private function sendRequest($prompt, $callback) { diff --git a/src/resources/lang/bg/auth.php b/src/resources/lang/bg/auth.php index 5133ae48..a507c666 100755 --- a/src/resources/lang/bg/auth.php +++ b/src/resources/lang/bg/auth.php @@ -45,6 +45,6 @@ ], 'session' => [ 'warning' => 'Сесията ви скоро ще изтече', - 'expired' => 'Сесията ви изтече, моля влезте отново' - ] + 'expired' => 'Сесията ви изтече, моля влезте отново', + ], ]; diff --git a/src/resources/lang/en/auth.php b/src/resources/lang/en/auth.php index 64762cda..e9b3cba3 100755 --- a/src/resources/lang/en/auth.php +++ b/src/resources/lang/en/auth.php @@ -45,6 +45,6 @@ ], 'session' => [ 'warning' => 'Your session will expire soon', - 'expired' => 'Your session has expired, please log in again' - ] + 'expired' => 'Your session has expired, please log in again', + ], ]; diff --git a/src/resources/lang/es/auth.php b/src/resources/lang/es/auth.php index 06fa1869..024e72e3 100644 --- a/src/resources/lang/es/auth.php +++ b/src/resources/lang/es/auth.php @@ -45,6 +45,6 @@ ], 'session' => [ 'warning' => 'Tu sesión está a punto de expirar', - 'expired' => 'Tu sesión ha expirado, por favor inicia sesión de nuevo' - ] + 'expired' => 'Tu sesión ha expirado, por favor inicia sesión de nuevo', + ], ]; diff --git a/src/resources/lang/fa/auth.php b/src/resources/lang/fa/auth.php index b32a8aba..bd057964 100644 --- a/src/resources/lang/fa/auth.php +++ b/src/resources/lang/fa/auth.php @@ -40,6 +40,6 @@ ], 'session' => [ 'warning' => 'نشست شما به زودی منقضی می‌شود', - 'expired' => 'نشست شما منقضی شده است، لطفاً دوباره وارد شوید' - ] + 'expired' => 'نشست شما منقضی شده است، لطفاً دوباره وارد شوید', + ], ]; diff --git a/src/resources/lang/fr/auth.php b/src/resources/lang/fr/auth.php index 33135c70..1d8f0e54 100755 --- a/src/resources/lang/fr/auth.php +++ b/src/resources/lang/fr/auth.php @@ -45,6 +45,6 @@ ], 'session' => [ 'warning' => 'Votre session va bientôt expirer', - 'expired' => 'Votre session a expirée, veuillez vous reconnecter' - ] + 'expired' => 'Votre session a expirée, veuillez vous reconnecter', + ], ]; diff --git a/src/resources/lang/it/auth.php b/src/resources/lang/it/auth.php index 8b36e8b8..d7e2b2d7 100644 --- a/src/resources/lang/it/auth.php +++ b/src/resources/lang/it/auth.php @@ -45,6 +45,6 @@ ], 'session' => [ 'warning' => 'La tua sessione sta per scadere', - 'expired' => 'La tua sessione è scaduta, per favore effettua nuovamente il login' - ] + 'expired' => 'La tua sessione è scaduta, per favore effettua nuovamente il login', + ], ]; diff --git a/src/resources/lang/tr/auth.php b/src/resources/lang/tr/auth.php index b15d6f32..c04ea575 100755 --- a/src/resources/lang/tr/auth.php +++ b/src/resources/lang/tr/auth.php @@ -45,6 +45,6 @@ ], 'session' => [ 'warning' => 'Oturumunuz yakında sona erecek', - 'expired' => 'Oturumunuz sona erdi, lütfen tekrar giriş yapın' - ] + 'expired' => 'Oturumunuz sona erdi, lütfen tekrar giriş yapın', + ], ]; diff --git a/src/routes/boilerplate.php b/src/routes/boilerplate.php index 82e0cbb7..30d0f017 100755 --- a/src/routes/boilerplate.php +++ b/src/routes/boilerplate.php @@ -1,6 +1,5 @@ Date: Fri, 4 Oct 2024 13:14:00 +0200 Subject: [PATCH 4/5] fix: styleCI --- src/Controllers/GptController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controllers/GptController.php b/src/Controllers/GptController.php index c3e15471..01df9903 100755 --- a/src/Controllers/GptController.php +++ b/src/Controllers/GptController.php @@ -153,6 +153,7 @@ private function processPrompt($request) * * @param Request $request * @return void + * * @codeCoverageIgnore */ public function stream(Request $request) @@ -195,6 +196,7 @@ public function stream(Request $request) * @param $prompt * @param $callback * @return bool|string + * * @codeCoverageIgnore */ private function sendRequest($prompt, $callback) From 0d513565025d653da2cd3832ba1b0b134511569c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20HEYD?= Date: Fri, 4 Oct 2024 13:15:54 +0200 Subject: [PATCH 5/5] fix: styleCI --- src/Controllers/GptController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controllers/GptController.php b/src/Controllers/GptController.php index 01df9903..8406babd 100755 --- a/src/Controllers/GptController.php +++ b/src/Controllers/GptController.php @@ -196,7 +196,7 @@ public function stream(Request $request) * @param $prompt * @param $callback * @return bool|string - * + * * @codeCoverageIgnore */ private function sendRequest($prompt, $callback)