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:
74
.env.example
74
.env.example
@@ -3,24 +3,20 @@ APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost:8000
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
# --- Logging & Performance ---
|
||||
PHP_CLI_SERVER_WORKERS=4
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
# Database Configuration
|
||||
# --- Database ---
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
@@ -28,34 +24,41 @@ DB_DATABASE=dy_app_db
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
# Session Configuration
|
||||
# --- Session & Cache ---
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
# Broadcasting & Filesystem
|
||||
BROADCAST_CONNECTION=log
|
||||
CACHE_STORE=database
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
# Cache Configuration
|
||||
CACHE_STORE=database
|
||||
# CACHE_PREFIX=
|
||||
|
||||
# Memcached Configuration
|
||||
# --- Memcached & Redis ---
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
# Redis Configuration
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
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_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=null
|
||||
@@ -64,7 +67,7 @@ MAIL_ENCRYPTION=tls
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
# Support Mailer (Secondary)
|
||||
# --- Support Mailer ---
|
||||
MAIL_SUPPORT_MAILER=smtp
|
||||
MAIL_SUPPORT_HOST=127.0.0.1
|
||||
MAIL_SUPPORT_PORT=587
|
||||
@@ -74,58 +77,43 @@ MAIL_SUPPORT_ENCRYPTION=tls
|
||||
MAIL_SUPPORT_FROM_ADDRESS="support@example.com"
|
||||
MAIL_SUPPORT_FROM_NAME="DyDev Support"
|
||||
|
||||
# AWS Configuration
|
||||
# --- Services (AWS) ---
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
# VITE Configuration
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
|
||||
# Social Authentication (OAuth) Configuration
|
||||
# --- Social Authentication ---
|
||||
GITHUB_CLIENT_ID=
|
||||
GITHUB_CLIENT_SECRET=
|
||||
GITHUB_REDIRECT_URI=${APP_URL}/auth/github/callback
|
||||
GITHUB_REDIRECT_URI="${APP_URL}/auth/github/callback"
|
||||
|
||||
GOOGLE_CLIENT_ID=
|
||||
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_ORGANIZATION_NAME="DyDev TrustLab OpenSource"
|
||||
CA_ROOT_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com"
|
||||
CA_ROOT_COMMON_NAME="DyzulkDev"
|
||||
|
||||
# CA INTERMEDIATE 4096
|
||||
CA_4096_COUNTRY_NAME="ID"
|
||||
CA_4096_ORGANIZATION_NAME="DyDev TrustLab"
|
||||
CA_4096_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com"
|
||||
CA_4096_COMMON_NAME="DyDev Infinity CA1"
|
||||
|
||||
# CA INTERMEDIATE 2048
|
||||
CA_2048_COUNTRY_NAME="ID"
|
||||
CA_2048_ORGANIZATION_NAME="Twinpath TrustLab"
|
||||
CA_2048_ORGANIZATIONAL_UNIT_NAME="www.dyzulk.com"
|
||||
CA_2048_COMMON_NAME="Twinpath Infinity CA2"
|
||||
|
||||
# CA LEAF DEFAULT
|
||||
CA_LEAF_DEFAULT_COUNTRY_NAME="ID"
|
||||
CA_LEAF_DEFAULT_LOCALITY="Jakarta"
|
||||
CA_LEAF_DEFAULT_STATE="DKI Jakarta"
|
||||
CA_LEAF_DEFAULT_ORGANIZATION_NAME="MyLab Secured"
|
||||
|
||||
# Reverb (WebSockets)
|
||||
REVERB_APP_ID=
|
||||
REVERB_APP_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}"
|
||||
# --- Security (Turnstile) ---
|
||||
TURNSTILE_SITE_KEY=
|
||||
TURNSTILE_SECRET_KEY=
|
||||
|
||||
@@ -22,7 +22,10 @@ class ForgotPasswordController extends Controller
|
||||
*/
|
||||
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
|
||||
// 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([
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required'],
|
||||
'cf-turnstile-response' => ['required', new \App\Rules\Turnstile],
|
||||
]);
|
||||
|
||||
// Find user by email
|
||||
@@ -72,6 +73,7 @@ class AuthController extends Controller
|
||||
'email' => 'required|string|email|max:255|unique:users',
|
||||
'password' => 'required|string|min:8',
|
||||
'terms' => 'required|accepted',
|
||||
'cf-turnstile-response' => ['required', new \App\Rules\Turnstile],
|
||||
]);
|
||||
|
||||
$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',
|
||||
'subject' => 'required|string|max:255',
|
||||
'message' => 'required|string|max:2000',
|
||||
'cf-turnstile-response' => ['required', new \App\Rules\Turnstile],
|
||||
]);
|
||||
|
||||
ContactSubmission::create([
|
||||
|
||||
@@ -33,7 +33,10 @@ class SearchController extends Controller
|
||||
|
||||
if ($user->isAdmin()) {
|
||||
// 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%")
|
||||
->limit(5)
|
||||
->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
|
||||
</div>
|
||||
|
||||
<!-- Turnstile -->
|
||||
<x-turnstile class="mb-5" />
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<button type="submit"
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
Forgot password?
|
||||
</a>
|
||||
</div>
|
||||
<!-- Turnstile -->
|
||||
<x-turnstile class="mb-5" />
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<button type="submit"
|
||||
|
||||
@@ -146,6 +146,8 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Turnstile -->
|
||||
<x-turnstile class="mb-5" />
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- Turnstile -->
|
||||
<x-turnstile class="mb-5" />
|
||||
<div>
|
||||
<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]">
|
||||
|
||||
Reference in New Issue
Block a user