mirror of
https://github.com/twinpath/app.git
synced 2026-01-26 05:15:28 +07:00
Fix: Resolve ENV parsing issue and implement Cloudflare Turnstile
This commit is contained in:
80
.env.example
80
.env.example
@@ -3,24 +3,20 @@ APP_ENV=local
|
|||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_URL=http://localhost:8000
|
APP_URL=http://localhost:8000
|
||||||
|
|
||||||
APP_LOCALE=en
|
APP_LOCALE=en
|
||||||
APP_FALLBACK_LOCALE=en
|
APP_FALLBACK_LOCALE=en
|
||||||
APP_FAKER_LOCALE=en_US
|
APP_FAKER_LOCALE=en_US
|
||||||
|
|
||||||
APP_MAINTENANCE_DRIVER=file
|
APP_MAINTENANCE_DRIVER=file
|
||||||
# APP_MAINTENANCE_STORE=database
|
|
||||||
|
|
||||||
|
# --- Logging & Performance ---
|
||||||
PHP_CLI_SERVER_WORKERS=4
|
PHP_CLI_SERVER_WORKERS=4
|
||||||
|
|
||||||
BCRYPT_ROUNDS=12
|
BCRYPT_ROUNDS=12
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
LOG_CHANNEL=stack
|
||||||
LOG_STACK=single
|
LOG_STACK=single
|
||||||
LOG_DEPRECATIONS_CHANNEL=null
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
# Database Configuration
|
# --- Database ---
|
||||||
DB_CONNECTION=mysql
|
DB_CONNECTION=mysql
|
||||||
DB_HOST=127.0.0.1
|
DB_HOST=127.0.0.1
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
@@ -28,34 +24,41 @@ DB_DATABASE=dy_app_db
|
|||||||
DB_USERNAME=root
|
DB_USERNAME=root
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
|
|
||||||
# Session Configuration
|
# --- Session & Cache ---
|
||||||
SESSION_DRIVER=database
|
SESSION_DRIVER=database
|
||||||
SESSION_LIFETIME=120
|
SESSION_LIFETIME=120
|
||||||
SESSION_ENCRYPT=false
|
SESSION_ENCRYPT=false
|
||||||
SESSION_PATH=/
|
SESSION_PATH=/
|
||||||
SESSION_DOMAIN=null
|
SESSION_DOMAIN=null
|
||||||
|
CACHE_STORE=database
|
||||||
# Broadcasting & Filesystem
|
|
||||||
BROADCAST_CONNECTION=log
|
|
||||||
FILESYSTEM_DISK=local
|
FILESYSTEM_DISK=local
|
||||||
QUEUE_CONNECTION=database
|
QUEUE_CONNECTION=database
|
||||||
|
|
||||||
# Cache Configuration
|
# --- Memcached & Redis ---
|
||||||
CACHE_STORE=database
|
|
||||||
# CACHE_PREFIX=
|
|
||||||
|
|
||||||
# Memcached Configuration
|
|
||||||
MEMCACHED_HOST=127.0.0.1
|
MEMCACHED_HOST=127.0.0.1
|
||||||
|
|
||||||
# Redis Configuration
|
|
||||||
REDIS_CLIENT=phpredis
|
REDIS_CLIENT=phpredis
|
||||||
REDIS_HOST=127.0.0.1
|
REDIS_HOST=127.0.0.1
|
||||||
REDIS_PASSWORD=null
|
REDIS_PASSWORD=null
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
# Mail Configuration
|
# --- Broadcasting (Reverb) ---
|
||||||
|
BROADCAST_CONNECTION=log
|
||||||
|
REVERB_APP_ID=
|
||||||
|
REVERB_APP_KEY=
|
||||||
|
REVERB_APP_SECRET=
|
||||||
|
REVERB_HOST="localhost"
|
||||||
|
REVERB_PORT=8080
|
||||||
|
REVERB_SCHEME=http
|
||||||
|
|
||||||
|
# --- Vite ---
|
||||||
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
|
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
|
||||||
|
VITE_REVERB_HOST="${REVERB_HOST}"
|
||||||
|
VITE_REVERB_PORT="${REVERB_PORT}"
|
||||||
|
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
|
||||||
|
|
||||||
|
# --- Mail Configuration ---
|
||||||
MAIL_MAILER=smtp
|
MAIL_MAILER=smtp
|
||||||
# MAIL_SCHEME=null
|
|
||||||
MAIL_HOST=127.0.0.1
|
MAIL_HOST=127.0.0.1
|
||||||
MAIL_PORT=587
|
MAIL_PORT=587
|
||||||
MAIL_USERNAME=null
|
MAIL_USERNAME=null
|
||||||
@@ -64,7 +67,7 @@ MAIL_ENCRYPTION=tls
|
|||||||
MAIL_FROM_ADDRESS="hello@example.com"
|
MAIL_FROM_ADDRESS="hello@example.com"
|
||||||
MAIL_FROM_NAME="${APP_NAME}"
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
# Support Mailer (Secondary)
|
# --- Support Mailer ---
|
||||||
MAIL_SUPPORT_MAILER=smtp
|
MAIL_SUPPORT_MAILER=smtp
|
||||||
MAIL_SUPPORT_HOST=127.0.0.1
|
MAIL_SUPPORT_HOST=127.0.0.1
|
||||||
MAIL_SUPPORT_PORT=587
|
MAIL_SUPPORT_PORT=587
|
||||||
@@ -74,58 +77,43 @@ MAIL_SUPPORT_ENCRYPTION=tls
|
|||||||
MAIL_SUPPORT_FROM_ADDRESS="support@example.com"
|
MAIL_SUPPORT_FROM_ADDRESS="support@example.com"
|
||||||
MAIL_SUPPORT_FROM_NAME="DyDev Support"
|
MAIL_SUPPORT_FROM_NAME="DyDev Support"
|
||||||
|
|
||||||
# AWS Configuration
|
# --- Services (AWS) ---
|
||||||
AWS_ACCESS_KEY_ID=
|
AWS_ACCESS_KEY_ID=
|
||||||
AWS_SECRET_ACCESS_KEY=
|
AWS_SECRET_ACCESS_KEY=
|
||||||
AWS_DEFAULT_REGION=us-east-1
|
AWS_DEFAULT_REGION=us-east-1
|
||||||
AWS_BUCKET=
|
AWS_BUCKET=
|
||||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
# VITE Configuration
|
# --- Social Authentication ---
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
|
||||||
|
|
||||||
# Social Authentication (OAuth) Configuration
|
|
||||||
GITHUB_CLIENT_ID=
|
GITHUB_CLIENT_ID=
|
||||||
GITHUB_CLIENT_SECRET=
|
GITHUB_CLIENT_SECRET=
|
||||||
GITHUB_REDIRECT_URI=${APP_URL}/auth/github/callback
|
GITHUB_REDIRECT_URI="${APP_URL}/auth/github/callback"
|
||||||
|
|
||||||
GOOGLE_CLIENT_ID=
|
GOOGLE_CLIENT_ID=
|
||||||
GOOGLE_CLIENT_SECRET=
|
GOOGLE_CLIENT_SECRET=
|
||||||
GOOGLE_REDIRECT_URI=${APP_URL}/auth/google/callback
|
GOOGLE_REDIRECT_URI="${APP_URL}/auth/google/callback"
|
||||||
|
|
||||||
# CA ROOT
|
# --- Certificate Authority (CA) ---
|
||||||
CA_ROOT_COUNTRY_NAME="ID"
|
CA_ROOT_COUNTRY_NAME="ID"
|
||||||
CA_ROOT_ORGANIZATION_NAME="DyDev TrustLab OpenSource"
|
CA_ROOT_ORGANIZATION_NAME="DyDev TrustLab OpenSource"
|
||||||
CA_ROOT_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com"
|
CA_ROOT_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com"
|
||||||
CA_ROOT_COMMON_NAME="DyzulkDev"
|
CA_ROOT_COMMON_NAME="DyzulkDev"
|
||||||
|
|
||||||
# CA INTERMEDIATE 4096
|
|
||||||
CA_4096_COUNTRY_NAME="ID"
|
CA_4096_COUNTRY_NAME="ID"
|
||||||
CA_4096_ORGANIZATION_NAME="DyDev TrustLab"
|
CA_4096_ORGANIZATION_NAME="DyDev TrustLab"
|
||||||
CA_4096_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com"
|
CA_4096_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com"
|
||||||
CA_4096_COMMON_NAME="DyDev Infinity CA1"
|
CA_4096_COMMON_NAME="DyDev Infinity CA1"
|
||||||
|
|
||||||
# CA INTERMEDIATE 2048
|
|
||||||
CA_2048_COUNTRY_NAME="ID"
|
CA_2048_COUNTRY_NAME="ID"
|
||||||
CA_2048_ORGANIZATION_NAME="Twinpath TrustLab"
|
CA_2048_ORGANIZATION_NAME="Twinpath TrustLab"
|
||||||
CA_2048_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com"
|
CA_2048_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com"
|
||||||
CA_2048_COMMON_NAME="Twinpath Infinity CA2"
|
CA_2048_COMMON_NAME="Twinpath Infinity CA2"
|
||||||
|
|
||||||
# CA LEAF DEFAULT
|
|
||||||
CA_LEAF_DEFAULT_COUNTRY_NAME="ID"
|
CA_LEAF_DEFAULT_COUNTRY_NAME="ID"
|
||||||
CA_LEAF_DEFAULT_LOCALITY="Jakarta"
|
CA_LEAF_DEFAULT_LOCALITY="Jakarta"
|
||||||
CA_LEAF_DEFAULT_STATE="DKI Jakarta"
|
CA_LEAF_DEFAULT_STATE="DKI Jakarta"
|
||||||
CA_LEAF_DEFAULT_ORGANIZATION_NAME="MyLab Secured"
|
CA_LEAF_DEFAULT_ORGANIZATION_NAME="MyLab Secured"
|
||||||
|
|
||||||
# Reverb (WebSockets)
|
# --- Security (Turnstile) ---
|
||||||
REVERB_APP_ID=
|
TURNSTILE_SITE_KEY=
|
||||||
REVERB_APP_KEY=
|
TURNSTILE_SECRET_KEY=
|
||||||
REVERB_APP_SECRET=
|
|
||||||
REVERB_HOST="localhost"
|
|
||||||
REVERB_PORT=8080
|
|
||||||
REVERB_SCHEME=http
|
|
||||||
|
|
||||||
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
|
|
||||||
VITE_REVERB_HOST="${REVERB_HOST}"
|
|
||||||
VITE_REVERB_PORT="${REVERB_PORT}"
|
|
||||||
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
|
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ class ForgotPasswordController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function sendResetLinkEmail(Request $request)
|
public function sendResetLinkEmail(Request $request)
|
||||||
{
|
{
|
||||||
$request->validate(['email' => 'required|email']);
|
$request->validate([
|
||||||
|
'email' => 'required|email',
|
||||||
|
'cf-turnstile-response' => ['required', new \App\Rules\Turnstile],
|
||||||
|
]);
|
||||||
|
|
||||||
// We will send the password reset link to this user. Once we have attempted
|
// We will send the password reset link to this user. Once we have attempted
|
||||||
// to send the link, we will examine the response then see the message we
|
// to send the link, we will examine the response then see the message we
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class AuthController extends Controller
|
|||||||
$credentials = $request->validate([
|
$credentials = $request->validate([
|
||||||
'email' => ['required', 'email'],
|
'email' => ['required', 'email'],
|
||||||
'password' => ['required'],
|
'password' => ['required'],
|
||||||
|
'cf-turnstile-response' => ['required', new \App\Rules\Turnstile],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Find user by email
|
// Find user by email
|
||||||
@@ -72,6 +73,7 @@ class AuthController extends Controller
|
|||||||
'email' => 'required|string|email|max:255|unique:users',
|
'email' => 'required|string|email|max:255|unique:users',
|
||||||
'password' => 'required|string|min:8',
|
'password' => 'required|string|min:8',
|
||||||
'terms' => 'required|accepted',
|
'terms' => 'required|accepted',
|
||||||
|
'cf-turnstile-response' => ['required', new \App\Rules\Turnstile],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$roleInfo = \App\Models\Role::where('name', 'customer')->first();
|
$roleInfo = \App\Models\Role::where('name', 'customer')->first();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class ContactController extends Controller
|
|||||||
'category' => 'required|string|in:Legal Inquiry,Technical Support,Partnership,Other',
|
'category' => 'required|string|in:Legal Inquiry,Technical Support,Partnership,Other',
|
||||||
'subject' => 'required|string|max:255',
|
'subject' => 'required|string|max:255',
|
||||||
'message' => 'required|string|max:2000',
|
'message' => 'required|string|max:2000',
|
||||||
|
'cf-turnstile-response' => ['required', new \App\Rules\Turnstile],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
ContactSubmission::create([
|
ContactSubmission::create([
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ class SearchController extends Controller
|
|||||||
|
|
||||||
if ($user->isAdmin()) {
|
if ($user->isAdmin()) {
|
||||||
// 2. Admin: User Search
|
// 2. Admin: User Search
|
||||||
$users = User::where('name', 'like', "%$query%")
|
$users = User::where(function($q) use ($query) {
|
||||||
|
$q->where('first_name', 'like', "%$query%")
|
||||||
|
->orWhere('last_name', 'like', "%$query%");
|
||||||
|
})
|
||||||
->orWhere('email', 'like', "%$query%")
|
->orWhere('email', 'like', "%$query%")
|
||||||
->limit(5)
|
->limit(5)
|
||||||
->get()
|
->get()
|
||||||
|
|||||||
28
app/Rules/Turnstile.php
Normal file
28
app/Rules/Turnstile.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Rules;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Contracts\Validation\ValidationRule;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
class Turnstile implements ValidationRule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the validation rule.
|
||||||
|
*
|
||||||
|
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||||
|
*/
|
||||||
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||||
|
{
|
||||||
|
$response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
|
||||||
|
'secret' => env('TURNSTILE_SECRET_KEY'),
|
||||||
|
'response' => $value,
|
||||||
|
'remoteip' => request()->ip(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (! $response->json('success')) {
|
||||||
|
$fail('The :attribute verification failed. Please try again.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
resources/views/components/turnstile.blade.php
Normal file
11
resources/views/components/turnstile.blade.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
@props(['theme' => 'auto', 'size' => 'normal', 'tabindex' => 0])
|
||||||
|
|
||||||
|
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||||
|
|
||||||
|
<div class="cf-turnstile"
|
||||||
|
data-sitekey="{{ env('TURNSTILE_SITE_KEY') }}"
|
||||||
|
data-theme="{{ $theme }}"
|
||||||
|
data-size="{{ $size }}"
|
||||||
|
data-tabindex="{{ $tabindex }}"
|
||||||
|
{{ $attributes }}
|
||||||
|
></div>
|
||||||
@@ -52,6 +52,8 @@
|
|||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Turnstile -->
|
||||||
|
<x-turnstile class="mb-5" />
|
||||||
<!-- Button -->
|
<!-- Button -->
|
||||||
<div>
|
<div>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
|
|||||||
@@ -125,6 +125,8 @@
|
|||||||
Forgot password?
|
Forgot password?
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Turnstile -->
|
||||||
|
<x-turnstile class="mb-5" />
|
||||||
<!-- Button -->
|
<!-- Button -->
|
||||||
<div>
|
<div>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
|
|||||||
@@ -146,6 +146,8 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Turnstile -->
|
||||||
|
<x-turnstile class="mb-5" />
|
||||||
<!-- Button -->
|
<!-- Button -->
|
||||||
<div>
|
<div>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
|
|||||||
@@ -73,6 +73,8 @@
|
|||||||
class="w-full rounded-2xl border-gray-200 bg-gray-50/50 px-5 py-4 text-sm text-gray-900 transition focus:ring-brand-500 focus:border-brand-500 dark:border-gray-700 dark:bg-gray-800 dark:text-white resize-none">{{ old('message') }}</textarea>
|
class="w-full rounded-2xl border-gray-200 bg-gray-50/50 px-5 py-4 text-sm text-gray-900 transition focus:ring-brand-500 focus:border-brand-500 dark:border-gray-700 dark:bg-gray-800 dark:text-white resize-none">{{ old('message') }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Turnstile -->
|
||||||
|
<x-turnstile class="mb-5" />
|
||||||
<div>
|
<div>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
class="flex w-full justify-center rounded-2xl bg-brand-500 px-4 py-5 text-sm font-bold text-white shadow-xl shadow-brand-500/30 hover:bg-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-500 transition-all active:scale-[0.98]">
|
class="flex w-full justify-center rounded-2xl bg-brand-500 px-4 py-5 text-sm font-bold text-white shadow-xl shadow-brand-500/30 hover:bg-brand-600 focus:outline-none focus:ring-2 focus:ring-brand-500 transition-all active:scale-[0.98]">
|
||||||
|
|||||||
Reference in New Issue
Block a user