diff --git a/app/app/Http/Controllers/Api/V1/AdminAnalyticsController.php b/app/app/Http/Controllers/Api/V1/AdminAnalyticsController.php index 669d9d7..0589020 100644 --- a/app/app/Http/Controllers/Api/V1/AdminAnalyticsController.php +++ b/app/app/Http/Controllers/Api/V1/AdminAnalyticsController.php @@ -18,9 +18,10 @@ class AdminAnalyticsController extends Controller { $token = (string) config('dewemoji.admin.token', ''); $provided = trim((string) $request->header('X-Admin-Token', '')); - if ($token === '' || $provided === '' || !hash_equals($token, $provided)) { + if ($token === '' || $provided === '' || ! hash_equals($token, $provided)) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } + return null; } diff --git a/app/app/Http/Controllers/Api/V1/AdminPricingController.php b/app/app/Http/Controllers/Api/V1/AdminPricingController.php index 2f613d6..4a772d8 100644 --- a/app/app/Http/Controllers/Api/V1/AdminPricingController.php +++ b/app/app/Http/Controllers/Api/V1/AdminPricingController.php @@ -15,7 +15,7 @@ class AdminPricingController extends Controller { $adminToken = (string) config('dewemoji.admin.token', ''); $provided = trim((string) $request->header('X-Admin-Token', '')); - if ($adminToken === '' || $provided === '' || !hash_equals($adminToken, $provided)) { + if ($adminToken === '' || $provided === '' || ! hash_equals($adminToken, $provided)) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } diff --git a/app/app/Http/Controllers/Api/V1/AdminSettingsController.php b/app/app/Http/Controllers/Api/V1/AdminSettingsController.php index d9ed5f5..ab59c3d 100644 --- a/app/app/Http/Controllers/Api/V1/AdminSettingsController.php +++ b/app/app/Http/Controllers/Api/V1/AdminSettingsController.php @@ -9,17 +9,16 @@ use Illuminate\Http\Request; class AdminSettingsController extends Controller { - public function __construct(private readonly SettingsService $settings) - { - } + public function __construct(private readonly SettingsService $settings) {} private function authorizeAdmin(Request $request): ?JsonResponse { $token = (string) config('dewemoji.admin.token', ''); $provided = trim((string) $request->header('X-Admin-Token', '')); - if ($token === '' || $provided === '' || !hash_equals($token, $provided)) { + if ($token === '' || $provided === '' || ! hash_equals($token, $provided)) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } + return null; } @@ -46,7 +45,7 @@ class AdminSettingsController extends Controller } $payload = $request->input('settings'); - if (!is_array($payload)) { + if (! is_array($payload)) { return response()->json(['ok' => false, 'error' => 'invalid_payload'], 422); } diff --git a/app/app/Http/Controllers/Api/V1/AdminSubscriptionController.php b/app/app/Http/Controllers/Api/V1/AdminSubscriptionController.php index 840a827..cd7e826 100644 --- a/app/app/Http/Controllers/Api/V1/AdminSubscriptionController.php +++ b/app/app/Http/Controllers/Api/V1/AdminSubscriptionController.php @@ -15,16 +15,16 @@ class AdminSubscriptionController extends Controller { public function __construct( private readonly KeywordQuotaService $keywordQuota - ) { - } + ) {} private function authorizeAdmin(Request $request): ?JsonResponse { $token = (string) config('dewemoji.admin.token', ''); $provided = trim((string) $request->header('X-Admin-Token', '')); - if ($token === '' || $provided === '' || !hash_equals($token, $provided)) { + if ($token === '' || $provided === '' || ! hash_equals($token, $provided)) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } + return null; } @@ -59,7 +59,7 @@ class AdminSubscriptionController extends Controller } $user = $this->resolveUser($request); - if (!$user) { + if (! $user) { return response()->json(['ok' => false, 'error' => 'not_found'], 404); } @@ -99,16 +99,17 @@ class AdminSubscriptionController extends Controller if ($id > 0) { $sub = Subscription::find($id); - if (!$sub) { + if (! $sub) { return response()->json(['ok' => false, 'error' => 'not_found'], 404); } $sub->update(['status' => 'revoked', 'expires_at' => $now]); $this->syncUserTier($sub->user_id); + return response()->json(['ok' => true, 'revoked' => true]); } $user = $this->resolveUser($request); - if (!$user) { + if (! $user) { return response()->json(['ok' => false, 'error' => 'not_found'], 404); } @@ -135,7 +136,7 @@ class AdminSubscriptionController extends Controller private function parseDate(mixed $value): ?Carbon { - if (!$value) { + if (! $value) { return null; } try { @@ -159,7 +160,7 @@ class AdminSubscriptionController extends Controller 'tier' => $active ? 'personal' : 'free', ]); $this->keywordQuota->enforceForUser($userId, $active ? 'personal' : 'free'); - if (!$active) { + if (! $active) { UserApiKey::where('user_id', $userId)->update(['revoked_at' => now()]); } } diff --git a/app/app/Http/Controllers/Api/V1/AdminUserController.php b/app/app/Http/Controllers/Api/V1/AdminUserController.php index 5d50768..eeaef02 100644 --- a/app/app/Http/Controllers/Api/V1/AdminUserController.php +++ b/app/app/Http/Controllers/Api/V1/AdminUserController.php @@ -12,14 +12,13 @@ class AdminUserController extends Controller { public function __construct( private readonly KeywordQuotaService $keywordQuota - ) { - } + ) {} private function authorizeAdmin(Request $request): ?JsonResponse { $adminToken = (string) config('dewemoji.admin.token', ''); $provided = trim((string) $request->header('X-Admin-Token', '')); - if ($adminToken === '' || $provided === '' || !hash_equals($adminToken, $provided)) { + if ($adminToken === '' || $provided === '' || ! hash_equals($adminToken, $provided)) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } @@ -71,7 +70,7 @@ class AdminUserController extends Controller /** @var User|null $user */ $user = $query->first(); - if (!$user) { + if (! $user) { return response()->json(['ok' => false, 'error' => 'not_found'], 404); } @@ -100,9 +99,9 @@ class AdminUserController extends Controller ]); $query = User::query(); - if (!empty($data['email'])) { + if (! empty($data['email'])) { $query->where('email', $data['email']); - } elseif (!empty($data['user_id'])) { + } elseif (! empty($data['user_id'])) { $query->where('id', $data['user_id']); } else { return response()->json(['ok' => false, 'error' => 'missing_target'], 400); @@ -110,7 +109,7 @@ class AdminUserController extends Controller /** @var User|null $user */ $user = $query->first(); - if (!$user) { + if (! $user) { return response()->json(['ok' => false, 'error' => 'not_found'], 404); } diff --git a/app/app/Http/Controllers/Api/V1/AdminWebhookController.php b/app/app/Http/Controllers/Api/V1/AdminWebhookController.php index b6497fb..58e3250 100644 --- a/app/app/Http/Controllers/Api/V1/AdminWebhookController.php +++ b/app/app/Http/Controllers/Api/V1/AdminWebhookController.php @@ -10,17 +10,16 @@ use Illuminate\Http\Request; class AdminWebhookController extends Controller { - public function __construct(private readonly PaypalWebhookProcessor $processor) - { - } + public function __construct(private readonly PaypalWebhookProcessor $processor) {} private function authorizeAdmin(Request $request): ?JsonResponse { $token = (string) config('dewemoji.admin.token', ''); $provided = trim((string) $request->header('X-Admin-Token', '')); - if ($token === '' || $provided === '' || !hash_equals($token, $provided)) { + if ($token === '' || $provided === '' || ! hash_equals($token, $provided)) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } + return null; } @@ -51,7 +50,7 @@ class AdminWebhookController extends Controller } $item = WebhookEvent::find($id); - if (!$item) { + if (! $item) { return response()->json(['ok' => false, 'error' => 'not_found'], 404); } @@ -65,7 +64,7 @@ class AdminWebhookController extends Controller } $item = WebhookEvent::find($id); - if (!$item) { + if (! $item) { return response()->json(['ok' => false, 'error' => 'not_found'], 404); } diff --git a/app/app/Http/Controllers/Api/V1/EmojiApiController.php b/app/app/Http/Controllers/Api/V1/EmojiApiController.php index e0a6439..56e385b 100644 --- a/app/app/Http/Controllers/Api/V1/EmojiApiController.php +++ b/app/app/Http/Controllers/Api/V1/EmojiApiController.php @@ -2,9 +2,9 @@ namespace App\Http\Controllers\Api\V1; +use App\Http\Controllers\Controller; use App\Services\Auth\ApiKeyService; use App\Services\System\SettingsService; -use App\Http\Controllers\Controller; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -13,6 +13,7 @@ use RuntimeException; class EmojiApiController extends Controller { private const TIER_FREE = 'free'; + private const TIER_PRO = 'pro'; /** @var array|null */ @@ -20,8 +21,7 @@ class EmojiApiController extends Controller public function __construct( private readonly ApiKeyService $apiKeys - ) { - } + ) {} /** @var array */ private const CATEGORY_MAP = [ @@ -149,7 +149,7 @@ class EmojiApiController extends Controller } $sourceItem = $itemsBySlug[$slug] ?? null; - if (!$sourceItem) { + if (! $sourceItem) { continue; } @@ -213,12 +213,12 @@ class EmojiApiController extends Controller public function search(Request $request): JsonResponse { $private = filter_var($request->query('private', false), FILTER_VALIDATE_BOOL); - if (!$private) { + if (! $private) { return $this->emojis($request); } $user = $this->apiKeys->resolveUser($request) ?? $request->user(); - if (!$user) { + if (! $user) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } $q = trim((string) ($request->query('q', $request->query('query', '')))); @@ -413,6 +413,7 @@ class EmojiApiController extends Controller private function isNotModified(Request $request, string $etag): bool { $ifNoneMatch = trim((string) $request->header('If-None-Match', '')); + return $ifNoneMatch !== '' && $ifNoneMatch === $etag; } @@ -431,7 +432,7 @@ class EmojiApiController extends Controller ? $activePath : (string) config('dewemoji.data_path'); - if (!is_file($path)) { + if (! is_file($path)) { throw new RuntimeException('Emoji dataset file was not found at: '.$path); } @@ -441,11 +442,12 @@ class EmojiApiController extends Controller } $decoded = json_decode($raw, true); - if (!is_array($decoded)) { + if (! is_array($decoded)) { throw new RuntimeException('Emoji dataset JSON is invalid.'); } self::$dataset = $decoded; + return self::$dataset; } @@ -464,11 +466,12 @@ class EmojiApiController extends Controller $value = strtolower(trim($text)); $value = str_replace('&', 'and', $value); $value = preg_replace('/[^a-z0-9]+/', '-', $value) ?? ''; + return trim($value, '-'); } /** - * @param array> $items + * @param array> $items * @return array> */ private function filterItems(array $items, string $q, string $category, string $subSlug): array @@ -506,16 +509,17 @@ class EmojiApiController extends Controller if ($token === '') { continue; } - if (!str_contains($haystack, $token)) { + if (! str_contains($haystack, $token)) { return false; } } + return true; })); } /** - * @param array $item + * @param array $item * @return array */ private function transformItem(array $item, string $tier): array @@ -560,7 +564,7 @@ class EmojiApiController extends Controller } /** - * @param array $item + * @param array $item * @return array */ private function transformEmojiDetail(array $item, string $tier): array @@ -583,12 +587,12 @@ class EmojiApiController extends Controller return $text; } - return rtrim(mb_substr($text, 0, $max - 1), " ,.;:-").'…'; + return rtrim(mb_substr($text, 0, $max - 1), ' ,.;:-').'…'; } /** - * @param array $payload - * @param array $extraHeaders + * @param array $payload + * @param array $extraHeaders */ private function jsonWithTier(Request $request, array $payload, string $tier, int $status = 200, array $extraHeaders = []): JsonResponse { diff --git a/app/app/Http/Controllers/Api/V1/ExtensionController.php b/app/app/Http/Controllers/Api/V1/ExtensionController.php index 5e2acb7..00d1b73 100644 --- a/app/app/Http/Controllers/Api/V1/ExtensionController.php +++ b/app/app/Http/Controllers/Api/V1/ExtensionController.php @@ -9,9 +9,7 @@ use Illuminate\Http\Request; class ExtensionController extends Controller { - public function __construct(private readonly ExtensionVerificationService $verifier) - { - } + public function __construct(private readonly ExtensionVerificationService $verifier) {} public function verify(Request $request): JsonResponse { diff --git a/app/app/Http/Controllers/Api/V1/PaypalWebhookController.php b/app/app/Http/Controllers/Api/V1/PaypalWebhookController.php index 82670fa..c3a977a 100644 --- a/app/app/Http/Controllers/Api/V1/PaypalWebhookController.php +++ b/app/app/Http/Controllers/Api/V1/PaypalWebhookController.php @@ -10,9 +10,7 @@ use Illuminate\Http\Request; class PaypalWebhookController extends Controller { - public function __construct(private readonly PaypalWebhookProcessor $processor) - { - } + public function __construct(private readonly PaypalWebhookProcessor $processor) {} public function handle(Request $request): JsonResponse { @@ -37,7 +35,7 @@ class PaypalWebhookController extends Controller 'error' => $signatureOk ? null : 'signature_unverified', ]); - if (!$signatureOk) { + if (! $signatureOk) { return response()->json(['ok' => true, 'signature' => 'unverified']); } diff --git a/app/app/Http/Controllers/Api/V1/SystemController.php b/app/app/Http/Controllers/Api/V1/SystemController.php index d13f442..541177a 100644 --- a/app/app/Http/Controllers/Api/V1/SystemController.php +++ b/app/app/Http/Controllers/Api/V1/SystemController.php @@ -21,7 +21,7 @@ class SystemController extends Controller public function metricsLite(Request $request): JsonResponse { - if (!$this->metricsEnabled()) { + if (! $this->metricsEnabled()) { return $this->response($request, [ 'ok' => false, 'error' => 'metrics_disabled', @@ -39,14 +39,14 @@ class SystemController extends Controller public function metrics(Request $request): JsonResponse { - if (!$this->metricsEnabled()) { + if (! $this->metricsEnabled()) { return $this->response($request, [ 'ok' => false, 'error' => 'metrics_disabled', ], 404); } - if (!$this->canAccessMetrics($request)) { + if (! $this->canAccessMetrics($request)) { return $this->response($request, [ 'ok' => false, 'error' => 'forbidden', @@ -83,6 +83,7 @@ class SystemController extends Controller $key = 'dw_metrics_ping'; Cache::put($key, 'ok', 60); $val = Cache::get($key); + return $val === 'ok' ? 'ok' : 'degraded'; } catch (\Throwable) { return 'down'; @@ -111,7 +112,7 @@ class SystemController extends Controller } /** - * @param array $payload + * @param array $payload */ private function response(Request $request, array $payload, int $status = 200): JsonResponse { diff --git a/app/app/Http/Controllers/Api/V1/UserController.php b/app/app/Http/Controllers/Api/V1/UserController.php index 80aa863..558e406 100644 --- a/app/app/Http/Controllers/Api/V1/UserController.php +++ b/app/app/Http/Controllers/Api/V1/UserController.php @@ -15,8 +15,7 @@ class UserController extends Controller { public function __construct( private readonly ApiKeyService $keys - ) { - } + ) {} public function register(Request $request): JsonResponse { @@ -57,7 +56,7 @@ class UserController extends Controller /** @var User|null $user */ $user = User::where('email', $data['email'])->first(); - if (!$user || !Hash::check($data['password'], $user->password)) { + if (! $user || ! Hash::check($data['password'], $user->password)) { return response()->json([ 'ok' => false, 'error' => 'invalid_credentials', @@ -96,7 +95,7 @@ class UserController extends Controller public function listApiKeys(Request $request): JsonResponse { $user = $this->keys->resolveUser($request); - if (!$user) { + if (! $user) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } @@ -121,7 +120,7 @@ class UserController extends Controller public function createApiKey(Request $request): JsonResponse { $user = $this->keys->resolveUser($request); - if (!$user) { + if (! $user) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } if ((string) $user->tier !== 'personal') { @@ -149,7 +148,7 @@ class UserController extends Controller public function revokeApiKey(Request $request, string $key): JsonResponse { $user = $this->keys->resolveUser($request); - if (!$user) { + if (! $user) { return response()->json(['ok' => false, 'error' => 'unauthorized'], 401); } diff --git a/app/app/Http/Controllers/Api/V1/UserKeywordController.php b/app/app/Http/Controllers/Api/V1/UserKeywordController.php index c0622b9..bb3acd4 100644 --- a/app/app/Http/Controllers/Api/V1/UserKeywordController.php +++ b/app/app/Http/Controllers/Api/V1/UserKeywordController.php @@ -14,13 +14,12 @@ class UserKeywordController extends Controller public function __construct( private readonly ApiKeyService $keys, private readonly KeywordQuotaService $keywordQuota - ) { - } + ) {} private function ensureUser(Request $request): ?array { $user = $this->keys->resolveUser($request); - if (!$user) { + if (! $user) { return ['error' => 'unauthorized', 'status' => 401]; } @@ -30,7 +29,7 @@ class UserKeywordController extends Controller public function index(Request $request): JsonResponse { $check = $this->ensureUser($request); - if (!isset($check['user'])) { + if (! isset($check['user'])) { return response()->json(['ok' => false, 'error' => $check['error']], $check['status']); } @@ -45,7 +44,7 @@ class UserKeywordController extends Controller public function store(Request $request): JsonResponse { $check = $this->ensureUser($request); - if (!isset($check['user'])) { + if (! isset($check['user'])) { return response()->json(['ok' => false, 'error' => $check['error']], $check['status']); } @@ -64,7 +63,7 @@ class UserKeywordController extends Controller $targetActive = $existing ? (bool) $existing->is_active : true; $limit = $this->keywordLimitFor($user); if ($limit !== null) { - if (!$existing && $targetActive) { + if (! $existing && $targetActive) { $activeCount = $this->keywordQuota->activeCount((int) $user->id); if ($activeCount >= $limit) { return response()->json(['ok' => false, 'error' => 'free_active_limit_reached', 'limit' => $limit], 403); @@ -93,7 +92,7 @@ class UserKeywordController extends Controller public function destroy(Request $request, int $id): JsonResponse { $check = $this->ensureUser($request); - if (!isset($check['user'])) { + if (! isset($check['user'])) { return response()->json(['ok' => false, 'error' => $check['error']], $check['status']); } @@ -107,7 +106,7 @@ class UserKeywordController extends Controller public function update(Request $request, int $id): JsonResponse { $check = $this->ensureUser($request); - if (!isset($check['user'])) { + if (! isset($check['user'])) { return response()->json(['ok' => false, 'error' => $check['error']], $check['status']); } @@ -121,7 +120,7 @@ class UserKeywordController extends Controller ->where('id', $id) ->first(); - if (!$item) { + if (! $item) { return response()->json(['ok' => false, 'error' => 'not_found'], 404); } @@ -134,11 +133,11 @@ class UserKeywordController extends Controller $item->delete(); $item = $duplicate; } else { - $item->update([ - 'emoji_slug' => $data['emoji_slug'], - 'keyword' => $data['keyword'], - 'lang' => $data['lang'] ?? 'und', - ]); + $item->update([ + 'emoji_slug' => $data['emoji_slug'], + 'keyword' => $data['keyword'], + 'lang' => $data['lang'] ?? 'und', + ]); } return response()->json(['ok' => true, 'item' => $item]); @@ -147,7 +146,7 @@ class UserKeywordController extends Controller public function export(Request $request): JsonResponse { $check = $this->ensureUser($request); - if (!isset($check['user'])) { + if (! isset($check['user'])) { return response()->json(['ok' => false, 'error' => $check['error']], $check['status']); } @@ -161,7 +160,7 @@ class UserKeywordController extends Controller public function import(Request $request): JsonResponse { $check = $this->ensureUser($request); - if (!isset($check['user'])) { + if (! isset($check['user'])) { return response()->json(['ok' => false, 'error' => $check['error']], $check['status']); } @@ -184,8 +183,9 @@ class UserKeywordController extends Controller ->first(); $targetActive = filter_var($row['is_active'] ?? true, FILTER_VALIDATE_BOOL); - if (!$existing && $targetActive && $limit !== null && $activeCount >= $limit) { + if (! $existing && $targetActive && $limit !== null && $activeCount >= $limit) { $skipped += 1; + continue; } @@ -214,7 +214,7 @@ class UserKeywordController extends Controller public function toggleActive(Request $request, int $id): JsonResponse { $check = $this->ensureUser($request); - if (!isset($check['user'])) { + if (! isset($check['user'])) { return response()->json(['ok' => false, 'error' => $check['error']], $check['status']); } @@ -224,13 +224,13 @@ class UserKeywordController extends Controller $user = $check['user']; $item = UserKeyword::where('user_id', $user->id)->where('id', $id)->first(); - if (!$item) { + if (! $item) { return response()->json(['ok' => false, 'error' => 'not_found'], 404); } if ((bool) $data['is_active'] && ($limit = $this->keywordLimitFor($user))) { $activeCount = $this->keywordQuota->activeCount((int) $user->id); - if (!$item->is_active && $activeCount >= $limit) { + if (! $item->is_active && $activeCount >= $limit) { return response()->json(['ok' => false, 'error' => 'free_active_limit_reached', 'limit' => $limit], 403); } } diff --git a/app/app/Http/Controllers/Auth/RegisteredUserController.php b/app/app/Http/Controllers/Auth/RegisteredUserController.php index 2773da2..21d8c70 100644 --- a/app/app/Http/Controllers/Auth/RegisteredUserController.php +++ b/app/app/Http/Controllers/Auth/RegisteredUserController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Models\User; -use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Auth\Events\Registered; +use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; diff --git a/app/app/Http/Controllers/Billing/BillingPaymentController.php b/app/app/Http/Controllers/Billing/BillingPaymentController.php index e56bbb7..7a3dd3a 100644 --- a/app/app/Http/Controllers/Billing/BillingPaymentController.php +++ b/app/app/Http/Controllers/Billing/BillingPaymentController.php @@ -12,7 +12,7 @@ class BillingPaymentController extends Controller public function resume(Request $request, Payment $payment): JsonResponse { $user = $request->user(); - if (!$user || (int) $payment->user_id !== (int) $user->id) { + if (! $user || (int) $payment->user_id !== (int) $user->id) { abort(403); } @@ -21,6 +21,7 @@ class BillingPaymentController extends Controller } $provider = strtolower((string) $payment->provider); + return match ($provider) { 'paypal' => $this->resumePayPal($payment), 'pakasir' => $this->resumePakasir($payment), @@ -34,7 +35,7 @@ class BillingPaymentController extends Controller $links = is_array($raw['links'] ?? null) ? $raw['links'] : []; $approveUrl = null; foreach ($links as $link) { - if (!is_array($link)) { + if (! is_array($link)) { continue; } if ((string) ($link['rel'] ?? '') === 'approve') { diff --git a/app/app/Http/Controllers/Billing/PakasirController.php b/app/app/Http/Controllers/Billing/PakasirController.php index 0cba07f..ee6f8cf 100644 --- a/app/app/Http/Controllers/Billing/PakasirController.php +++ b/app/app/Http/Controllers/Billing/PakasirController.php @@ -22,8 +22,7 @@ class PakasirController extends Controller public function __construct( private readonly SubscriptionTransitionService $subscriptionTransition, private readonly KeywordQuotaService $keywordQuota - ) { - } + ) {} public function createTransaction(Request $request): JsonResponse { @@ -32,7 +31,7 @@ class PakasirController extends Controller ]); $user = $request->user(); - if (!$user) { + if (! $user) { return response()->json(['error' => 'auth_required'], 401); } @@ -51,7 +50,7 @@ class PakasirController extends Controller $project = (string) ($config['project'] ?? ''); $timeout = (int) ($config['timeout'] ?? 10); - if (!$enabled || $apiBase === '' || $apiKey === '' || $project === '') { + if (! $enabled || $apiBase === '' || $apiKey === '' || $project === '') { return response()->json(['error' => 'pakasir_not_configured'], 422); } @@ -84,25 +83,27 @@ class PakasirController extends Controller $endpoint = $apiBase.'/api/transactioncreate/qris'; // Pakasir expects form payloads; keep JSON as fallback for provider-side variations. $res = Http::asForm()->timeout($timeout)->post($endpoint, $payload); - if (!$res->successful()) { + if (! $res->successful()) { $res = Http::timeout($timeout)->post($endpoint, $payload); } - if (!$res->successful()) { + if (! $res->successful()) { Log::warning('Pakasir create transaction failed', [ 'status' => $res->status(), 'endpoint' => $endpoint, 'body' => $res->body(), ]); + return response()->json(['error' => 'pakasir_create_failed'], 502); } $body = $res->json(); - if (!is_array($body)) { + if (! is_array($body)) { Log::warning('Pakasir create transaction invalid response', [ 'endpoint' => $endpoint, 'body' => $res->body(), ]); + return response()->json(['error' => 'pakasir_invalid_response'], 502); } @@ -112,6 +113,7 @@ class PakasirController extends Controller 'endpoint' => $endpoint, 'body' => $body, ]); + return response()->json(['error' => 'pakasir_invalid_response'], 502); } @@ -146,7 +148,7 @@ class PakasirController extends Controller } /** - * @param array $body + * @param array $body * @return array{0:string,1:string,2:string,3:int} */ private function extractPakasirPayload(array $body, int $amountFallback): array @@ -210,7 +212,7 @@ class PakasirController extends Controller ->where('status', 'pending') ->orderByDesc('id') ->first(); - if (!$pending || !$pending->created_at) { + if (! $pending || ! $pending->created_at) { return null; } @@ -232,7 +234,7 @@ class PakasirController extends Controller public function cancelPending(Request $request): JsonResponse { $user = $request->user(); - if (!$user) { + if (! $user) { return response()->json(['error' => 'auth_required'], 401); } @@ -247,7 +249,7 @@ class PakasirController extends Controller } $order = $orderQuery->orderByDesc('id')->first(); - if (!$order) { + if (! $order) { return response()->json(['ok' => true, 'canceled' => false, 'cancelled' => false]); } @@ -264,7 +266,7 @@ class PakasirController extends Controller public function paymentStatus(Request $request): JsonResponse { $user = $request->user(); - if (!$user) { + if (! $user) { return response()->json(['error' => 'auth_required'], 401); } @@ -275,7 +277,7 @@ class PakasirController extends Controller } $order = $query->orderByDesc('id')->first(); - if (!$order) { + if (! $order) { return response()->json(['ok' => true, 'found' => false, 'status' => null, 'paid' => false]); } @@ -319,11 +321,12 @@ class PakasirController extends Controller } $status = strtolower((string) ($data['status'] ?? $payload['status'] ?? '')); - if (!in_array($status, ['paid', 'success', 'settlement', 'completed'], true)) { + if (! in_array($status, ['paid', 'success', 'settlement', 'completed'], true)) { Log::info('Pakasir webhook ignored: status not paid', [ 'status' => $status, 'payload_status' => $payload['status'] ?? null, ]); + return; } @@ -338,17 +341,19 @@ class PakasirController extends Controller )); if ($orderId === '') { Log::warning('Pakasir webhook paid event missing order reference', ['payload' => $payload]); + return; } $order = Order::where('provider', 'pakasir')->where('provider_ref', $orderId)->first(); - if (!$order) { + if (! $order) { if (preg_match('/^DW-(\d+)-/', $orderId, $matches) === 1) { $order = Order::where('provider', 'pakasir')->where('id', (int) $matches[1])->first(); } } - if (!$order) { + if (! $order) { Log::warning('Pakasir webhook order not found', ['order_id' => $orderId, 'payload' => $payload]); + return; } @@ -356,7 +361,7 @@ class PakasirController extends Controller Payment::where('order_id', $order->id)->update(['status' => 'paid']); $user = User::find($order->user_id); - if (!$user) { + if (! $user) { return; } @@ -400,7 +405,7 @@ class PakasirController extends Controller $defaults = collect(config('dewemoji.pricing.defaults', []))->keyBy('code'); $fallback = $defaults->get($planCode); - if (!$fallback) { + if (! $fallback) { return 0; } diff --git a/app/app/Http/Controllers/Billing/PayPalController.php b/app/app/Http/Controllers/Billing/PayPalController.php index 2c25589..ac0fc02 100644 --- a/app/app/Http/Controllers/Billing/PayPalController.php +++ b/app/app/Http/Controllers/Billing/PayPalController.php @@ -17,7 +17,6 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; class PayPalController extends Controller @@ -25,8 +24,7 @@ class PayPalController extends Controller public function __construct( private readonly SubscriptionTransitionService $subscriptionTransition, private readonly KeywordQuotaService $keywordQuota - ) { - } + ) {} public function createSubscription(Request $request): RedirectResponse|JsonResponse { @@ -35,7 +33,7 @@ class PayPalController extends Controller ]); $user = $request->user(); - if (!$user) { + if (! $user) { return response()->json(['error' => 'auth_required'], 401); } @@ -48,7 +46,7 @@ class PayPalController extends Controller } $mode = $this->resolvePaypalMode($this->billingMode()); - if (!$this->paypalConfigured($mode)) { + if (! $this->paypalConfigured($mode)) { return response()->json(['error' => 'paypal_not_configured'], 422); } @@ -57,12 +55,12 @@ class PayPalController extends Controller } $planId = $this->resolvePlanId($data['plan_code'], $mode); - if (!$planId) { + if (! $planId) { return response()->json(['error' => 'paypal_plan_missing'], 422); } $token = $this->getAccessToken($mode); - if (!$token) { + if (! $token) { return response()->json(['error' => 'paypal_auth_failed'], 502); } @@ -94,8 +92,8 @@ class PayPalController extends Controller $subscriptionId = $body['id'] ?? null; $approveUrl = collect($body['links'] ?? [])->firstWhere('rel', 'approve')['href'] ?? null; - if (!$subscriptionId || !$approveUrl) { - if (!$res->ok()) { + if (! $subscriptionId || ! $approveUrl) { + if (! $res->ok()) { Log::warning('PayPal create subscription failed', [ 'status' => $res->status(), 'body' => $res->body(), @@ -106,6 +104,7 @@ class PayPalController extends Controller 'body' => $res->body(), ]); } + return response()->json(['error' => 'paypal_invalid_response'], 502); } @@ -156,13 +155,14 @@ class PayPalController extends Controller $token = trim((string) $request->query('token', '')); if ($token !== '') { $captured = $this->captureLifetimeOrder($token, $request->user()?->id); - if (!$captured) { + if (! $captured) { return redirect()->route('dashboard.billing', ['status' => 'error']); } } } $status = (string) $request->query('status', 'success'); + return redirect()->route('dashboard.billing', ['status' => $status]); } @@ -177,7 +177,7 @@ class PayPalController extends Controller if ($webhookId) { $verified = $this->verifySignature($mode, $webhookId, $payload, $request); - if (!$verified) { + if (! $verified) { return response()->json(['error' => 'invalid_signature'], 401); } } @@ -223,14 +223,14 @@ class PayPalController extends Controller 'provider' => 'paypal', 'provider_ref' => $subscriptionId, ]); - if (!$sub->user_id) { + if (! $sub->user_id) { $order = Order::where('provider', 'paypal')->where('provider_ref', $subscriptionId)->first(); if ($order) { $sub->user_id = $order->user_id; $resolvedPlan = (string) ($order->plan_code ?: $resolvedPlan); } } - if (!empty($sub->plan)) { + if (! empty($sub->plan)) { $resolvedPlan = (string) $sub->plan; } $sub->plan = $resolvedPlan; @@ -267,6 +267,7 @@ class PayPalController extends Controller $sub->canceled_at = now(); $sub->save(); } + return true; } @@ -311,7 +312,7 @@ class PayPalController extends Controller ->where('status', 'pending') ->orderByDesc('id') ->first(); - if (!$pending || !$pending->created_at) { + if (! $pending || ! $pending->created_at) { return null; } @@ -333,20 +334,21 @@ class PayPalController extends Controller private function resolvePlanAmountUsd(string $planCode): int { $plan = PricingPlan::where('code', $planCode)->first(); - if (!$plan) { + if (! $plan) { return 0; } $rate = (int) config('dewemoji.pricing.usd_rate', 15000); if ($rate <= 0) { return 0; } + return (int) round($plan->amount / $rate); } private function resolvePlanAmountUsdValue(string $planCode): string { $plan = PricingPlan::where('code', $planCode)->first(); - if (!$plan) { + if (! $plan) { return '0.00'; } $rate = (int) config('dewemoji.pricing.usd_rate', 15000); @@ -363,7 +365,7 @@ class PayPalController extends Controller $clientSecret = config("dewemoji.billing.providers.paypal.{$mode}.client_secret"); $apiBase = config("dewemoji.billing.providers.paypal.{$mode}.api_base"); - if (!$clientId || !$clientSecret || !$apiBase) { + if (! $clientId || ! $clientSecret || ! $apiBase) { return null; } @@ -374,7 +376,7 @@ class PayPalController extends Controller 'grant_type' => 'client_credentials', ]); - if (!$res->successful()) { + if (! $res->successful()) { return null; } @@ -394,6 +396,7 @@ class PayPalController extends Controller private function billingMode(): string { $settings = app(SettingsService::class); + return (string) ($settings->get('billing_mode', config('dewemoji.billing.mode', 'sandbox')) ?: 'sandbox'); } @@ -414,7 +417,7 @@ class PayPalController extends Controller private function verifySignature(string $mode, string $webhookId, array $payload, Request $request): bool { $token = $this->getAccessToken($mode); - if (!$token) { + if (! $token) { return false; } @@ -443,12 +446,12 @@ class PayPalController extends Controller private function createLifetimeOrder(Request $request, string $mode): JsonResponse { $user = $request->user(); - if (!$user) { + if (! $user) { return response()->json(['error' => 'auth_required'], 401); } $token = $this->getAccessToken($mode); - if (!$token) { + if (! $token) { return response()->json(['error' => 'paypal_auth_failed'], 502); } @@ -487,7 +490,7 @@ class PayPalController extends Controller $body = $res->json(); $orderId = (string) ($body['id'] ?? ''); $approveUrl = collect($body['links'] ?? [])->firstWhere('rel', 'approve')['href'] ?? null; - if ($orderId === '' || !$approveUrl) { + if ($orderId === '' || ! $approveUrl) { Log::warning('PayPal create lifetime order failed', [ 'status' => $res->status(), 'body' => $res->body(), @@ -529,7 +532,7 @@ class PayPalController extends Controller { $mode = $this->resolvePaypalMode($this->billingMode()); $token = $this->getAccessToken($mode); - if (!$token) { + if (! $token) { return false; } @@ -537,7 +540,7 @@ class PayPalController extends Controller ->where('provider_ref', $orderId) ->where('type', 'one_time') ->first(); - if (!$order) { + if (! $order) { return false; } if ($userId !== null && (int) $order->user_id !== $userId) { @@ -581,7 +584,7 @@ class PayPalController extends Controller ->where('provider_ref', $orderId) ->where('type', 'one_time') ->first(); - if (!$order) { + if (! $order) { return false; } @@ -594,7 +597,7 @@ class PayPalController extends Controller ->first(); if ($payment) { $payment->status = 'paid'; - if (!empty($rawPayload)) { + if (! empty($rawPayload)) { $payment->raw_payload = $rawPayload; } $payment->save(); diff --git a/app/app/Http/Controllers/Dashboard/AdminDashboardController.php b/app/app/Http/Controllers/Dashboard/AdminDashboardController.php index e2e857b..005f4e0 100644 --- a/app/app/Http/Controllers/Dashboard/AdminDashboardController.php +++ b/app/app/Http/Controllers/Dashboard/AdminDashboardController.php @@ -3,16 +3,16 @@ namespace App\Http\Controllers\Dashboard; use App\Http\Controllers\Controller; -use App\Models\PricingChange; -use App\Models\PricingPlan; +use App\Models\AdminAuditLog; use App\Models\Order; use App\Models\Payment; +use App\Models\PricingChange; +use App\Models\PricingPlan; use App\Models\Subscription; use App\Models\User; use App\Models\UserApiKey; use App\Models\UserKeyword; use App\Models\WebhookEvent; -use App\Models\AdminAuditLog; use App\Services\Billing\PayPalPlanSyncService; use App\Services\Keywords\KeywordQuotaService; use App\Services\System\SettingsService; @@ -30,9 +30,7 @@ class AdminDashboardController extends Controller public function __construct( private readonly SettingsService $settings, private readonly KeywordQuotaService $keywordQuota - ) - { - } + ) {} public function users(Request $request): View { @@ -206,7 +204,7 @@ class AdminDashboardController extends Controller ]); $user = $this->resolveUser($data['user_id'] ?? null, $data['email'] ?? null); - if (!$user) { + if (! $user) { return back()->withErrors(['user' => 'User not found.']); } @@ -246,9 +244,9 @@ class AdminDashboardController extends Controller 'email' => 'nullable|email|max:255', ]); - if (!empty($data['subscription_id'])) { + if (! empty($data['subscription_id'])) { $sub = Subscription::find($data['subscription_id']); - if (!$sub) { + if (! $sub) { return back()->withErrors(['subscription' => 'Subscription not found.']); } $sub->update(['status' => 'revoked', 'expires_at' => now()]); @@ -257,11 +255,12 @@ class AdminDashboardController extends Controller 'subscription_id' => $sub->id, 'user_id' => $sub->user_id, ]); + return back()->with('status', 'Subscription revoked.'); } $user = $this->resolveUser($data['user_id'] ?? null, $data['email'] ?? null); - if (!$user) { + if (! $user) { return back()->withErrors(['user' => 'User not found.']); } @@ -447,7 +446,7 @@ class AdminDashboardController extends Controller public function replayWebhook(int $id): RedirectResponse { $event = WebhookEvent::find($id); - if (!$event) { + if (! $event) { return back()->withErrors(['webhook' => 'Webhook not found.']); } @@ -557,7 +556,7 @@ class AdminDashboardController extends Controller 'tier' => $active ? 'personal' : 'free', ]); $this->keywordQuota->enforceForUser($userId, $active ? 'personal' : 'free'); - if (!$active) { + if (! $active) { UserApiKey::where('user_id', $userId)->update(['revoked_at' => now()]); } } @@ -565,7 +564,7 @@ class AdminDashboardController extends Controller public function exportCsv(Request $request, string $type): StreamedResponse { $type = strtolower($type); - $filename = "dewemoji-{$type}-export-".now()->format('Ymd_His').".csv"; + $filename = "dewemoji-{$type}-export-".now()->format('Ymd_His').'.csv'; return response()->streamDownload(function () use ($type, $request): void { $out = fopen('php://output', 'w'); @@ -676,7 +675,7 @@ class AdminDashboardController extends Controller private function logAdminAction(string $action, array $payload = []): void { - if (!Schema::hasTable('admin_audit_logs')) { + if (! Schema::hasTable('admin_audit_logs')) { return; } $user = auth()->user(); @@ -692,6 +691,7 @@ class AdminDashboardController extends Controller private function sanitizeSort(mixed $value, array $allowed, string $fallback): string { $sort = is_string($value) ? $value : ''; + return in_array($sort, $allowed, true) ? $sort : $fallback; } @@ -706,6 +706,7 @@ class AdminDashboardController extends Controller private function splitCsv(string $value): array { $items = array_filter(array_map('trim', explode(',', $value))); + return array_values($items); } } diff --git a/app/app/Http/Controllers/Dashboard/AdminEmojiCatalogController.php b/app/app/Http/Controllers/Dashboard/AdminEmojiCatalogController.php index 4188875..be863ea 100644 --- a/app/app/Http/Controllers/Dashboard/AdminEmojiCatalogController.php +++ b/app/app/Http/Controllers/Dashboard/AdminEmojiCatalogController.php @@ -10,16 +10,15 @@ use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use Throwable; use Illuminate\View\View; +use Throwable; class AdminEmojiCatalogController extends Controller { public function __construct( private readonly EmojiCatalogService $catalog, private readonly SettingsService $settings - ) { - } + ) {} public function index(Request $request): View { @@ -64,6 +63,7 @@ class AdminEmojiCatalogController extends Controller $emojiId = $this->catalog->saveItem($validated); } catch (Throwable $e) { report($e); + return back()->withInput()->with('error', $e->getMessage() ?: 'Failed to create catalog item.'); } @@ -85,6 +85,7 @@ class AdminEmojiCatalogController extends Controller $savedId = $this->catalog->saveItem($validated); } catch (Throwable $e) { report($e); + return back() ->withInput() ->with('error', $e->getMessage() ?: 'Failed to save catalog item.'); @@ -105,6 +106,7 @@ class AdminEmojiCatalogController extends Controller $this->catalog->deleteItem($emojiId); } catch (Throwable $e) { report($e); + return back()->with('error', $e->getMessage() ?: 'Failed to delete catalog item.'); } @@ -123,6 +125,7 @@ class AdminEmojiCatalogController extends Controller $result = $this->catalog->importFromDataFile($path); } catch (Throwable $e) { report($e); + return back()->with('error', $e->getMessage() ?: 'Failed to import dataset.'); } @@ -145,6 +148,7 @@ class AdminEmojiCatalogController extends Controller $result = $this->catalog->publishSnapshot($request->user()?->email); } catch (Throwable $e) { report($e); + return back()->with('error', $e->getMessage() ?: 'Failed to publish snapshot.'); } @@ -163,6 +167,7 @@ class AdminEmojiCatalogController extends Controller $result = $this->catalog->activateSnapshot($validated['snapshot'], $request->user()?->email); } catch (Throwable $e) { report($e); + return back()->with('error', $e->getMessage() ?: 'Failed to activate snapshot.'); } @@ -172,12 +177,12 @@ class AdminEmojiCatalogController extends Controller } /** - * @param array $payload + * @param array $payload */ private function logAdminAction(Request $request, string $action, array $payload): void { $admin = $request->user(); - if (!$admin) { + if (! $admin) { return; } diff --git a/app/app/Http/Controllers/Dashboard/UserDashboardController.php b/app/app/Http/Controllers/Dashboard/UserDashboardController.php index 4554550..4e91bb6 100644 --- a/app/app/Http/Controllers/Dashboard/UserDashboardController.php +++ b/app/app/Http/Controllers/Dashboard/UserDashboardController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Dashboard; +use App\Http\Controllers\Api\V1\EmojiApiController; use App\Http\Controllers\Controller; use App\Models\Order; use App\Models\Payment; @@ -10,7 +11,6 @@ use App\Models\User; use App\Models\UserApiKey; use App\Models\UserKeyword; use App\Models\WebhookEvent; -use App\Http\Controllers\Api\V1\EmojiApiController; use App\Services\Auth\ApiKeyService; use App\Services\Keywords\KeywordQuotaService; use Carbon\Carbon; @@ -27,8 +27,7 @@ class UserDashboardController extends Controller public function __construct( private readonly ApiKeyService $keys, private readonly KeywordQuotaService $keywordQuota - ) { - } + ) {} public function overview(Request $request): View { @@ -171,7 +170,7 @@ class UserDashboardController extends Controller public function storeKeyword(Request $request): RedirectResponse|JsonResponse { $user = $request->user(); - if (!$user) { + if (! $user) { abort(403); } @@ -188,7 +187,7 @@ class UserDashboardController extends Controller $targetActive = $existing ? (bool) $existing->is_active : true; if ($limit = $this->keywordLimitFor($user)) { - if (!$existing && $targetActive) { + if (! $existing && $targetActive) { $activeCount = $this->keywordQuota->activeCount((int) $user->id); if ($activeCount >= $limit) { return $this->rejectKeywordLimit($request, $limit, 'free_active_limit_reached'); @@ -221,7 +220,7 @@ class UserDashboardController extends Controller public function updateKeyword(Request $request, UserKeyword $keyword): RedirectResponse|JsonResponse { $user = $request->user(); - if (!$user) { + if (! $user) { abort(403); } @@ -261,7 +260,7 @@ class UserDashboardController extends Controller public function deleteKeyword(Request $request, UserKeyword $keyword): RedirectResponse|JsonResponse { $user = $request->user(); - if (!$user) { + if (! $user) { abort(403); } @@ -281,7 +280,7 @@ class UserDashboardController extends Controller public function importKeywords(Request $request): RedirectResponse { $user = $request->user(); - if (!$user) { + if (! $user) { abort(403); } @@ -291,7 +290,7 @@ class UserDashboardController extends Controller } $items = json_decode((string) $payload, true); - if (!is_array($items)) { + if (! is_array($items)) { return back()->withErrors(['payload' => 'Invalid JSON payload.']); } @@ -300,7 +299,7 @@ class UserDashboardController extends Controller $limit = $this->keywordLimitFor($user); $activeCount = $this->keywordQuota->activeCount((int) $user->id); foreach ($items as $row) { - if (!is_array($row)) { + if (! is_array($row)) { continue; } $emojiSlug = trim((string) ($row['emoji_slug'] ?? '')); @@ -315,8 +314,9 @@ class UserDashboardController extends Controller ->first(); $targetActive = filter_var($row['is_active'] ?? true, FILTER_VALIDATE_BOOL); - if (!$existing && $targetActive && $limit !== null && $activeCount >= $limit) { + if (! $existing && $targetActive && $limit !== null && $activeCount >= $limit) { $skipped += 1; + continue; } @@ -350,7 +350,7 @@ class UserDashboardController extends Controller public function exportKeywords(Request $request): BinaryFileResponse { $user = $request->user(); - if (!$user) { + if (! $user) { abort(403); } @@ -386,7 +386,7 @@ class UserDashboardController extends Controller public function createApiKey(Request $request): RedirectResponse { $user = $request->user(); - if (!$user) { + if (! $user) { abort(403); } if ((string) $user->tier !== 'personal') { @@ -405,7 +405,7 @@ class UserDashboardController extends Controller public function revokeApiKey(Request $request, UserApiKey $key): RedirectResponse { $user = $request->user(); - if (!$user || $key->user_id !== $user->id) { + if (! $user || $key->user_id !== $user->id) { abort(403); } @@ -447,7 +447,7 @@ class UserDashboardController extends Controller public function toggleKeywordActive(Request $request, UserKeyword $keyword): RedirectResponse|JsonResponse { $user = $request->user(); - if (!$user || $keyword->user_id !== $user->id) { + if (! $user || $keyword->user_id !== $user->id) { abort(403); } @@ -458,7 +458,7 @@ class UserDashboardController extends Controller $target = (bool) $data['is_active']; if ($target && ($limit = $this->keywordLimitFor($user))) { $activeCount = $this->keywordQuota->activeCount((int) $user->id); - if (!$keyword->is_active && $activeCount >= $limit) { + if (! $keyword->is_active && $activeCount >= $limit) { return $this->rejectKeywordLimit($request, $limit, 'free_active_limit_reached'); } } @@ -483,7 +483,7 @@ class UserDashboardController extends Controller private function keywordLimitFor(?User $user): ?int { - if (!$user) { + if (! $user) { return null; } if ((string) $user->tier === 'personal') { diff --git a/app/app/Http/Controllers/ProfileController.php b/app/app/Http/Controllers/ProfileController.php index b4f25af..a0111e3 100644 --- a/app/app/Http/Controllers/ProfileController.php +++ b/app/app/Http/Controllers/ProfileController.php @@ -3,8 +3,8 @@ namespace App\Http\Controllers; use App\Http\Requests\ProfileUpdateRequest; -use Illuminate\Http\RedirectResponse; use Illuminate\Contracts\Auth\MustVerifyEmail; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Redirect; diff --git a/app/app/Http/Controllers/Web/SiteController.php b/app/app/Http/Controllers/Web/SiteController.php index ee00fca..982eee8 100644 --- a/app/app/Http/Controllers/Web/SiteController.php +++ b/app/app/Http/Controllers/Web/SiteController.php @@ -18,15 +18,15 @@ class SiteController extends Controller { /** @var array */ private const CATEGORY_TO_SLUG = [ - "Smileys & Emotion" => "smileys", - "People & Body" => "people", - "Animals & Nature" => "animals", - "Food & Drink" => "food", - "Travel & Places" => "travel", - "Activities" => "activities", - "Objects" => "objects", - "Symbols" => "symbols", - "Flags" => "flags", + 'Smileys & Emotion' => 'smileys', + 'People & Body' => 'people', + 'Animals & Nature' => 'animals', + 'Food & Drink' => 'food', + 'Travel & Places' => 'travel', + 'Activities' => 'activities', + 'Objects' => 'objects', + 'Symbols' => 'symbols', + 'Flags' => 'flags', ]; private function billingMode(): string @@ -34,15 +34,15 @@ class SiteController extends Controller $settings = app(SettingsService::class); $preferred = (string) ($settings->get( - "billing_mode", - config("dewemoji.billing.mode", "sandbox"), + 'billing_mode', + config('dewemoji.billing.mode', 'sandbox'), ) ?: - "sandbox"); + 'sandbox'); if ($this->paypalConfiguredMode($preferred)) { return $preferred; } - $fallback = $preferred === "live" ? "sandbox" : "live"; + $fallback = $preferred === 'live' ? 'sandbox' : 'live'; if ($this->paypalConfiguredMode($fallback)) { return $fallback; } @@ -52,60 +52,60 @@ class SiteController extends Controller public function home(Request $request): View { - return view("site.home", [ - "initialQuery" => trim((string) $request->query("q", "")), - "initialCategory" => trim((string) $request->query("category", "")), - "initialSubcategory" => trim( - (string) $request->query("subcategory", ""), + return view('site.home', [ + 'initialQuery' => trim((string) $request->query('q', '')), + 'initialCategory' => trim((string) $request->query('category', '')), + 'initialSubcategory' => trim( + (string) $request->query('subcategory', ''), ), - "canonicalPath" => "/", - "userTier" => $request->user()?->tier, + 'canonicalPath' => '/', + 'userTier' => $request->user()?->tier, ]); } public function browse(Request $request): RedirectResponse|View { - $cat = strtolower(trim((string) $request->query("cat", "all"))); + $cat = strtolower(trim((string) $request->query('cat', 'all'))); if ( - $cat !== "" && - $cat !== "all" && + $cat !== '' && + $cat !== 'all' && array_key_exists($cat, $this->categorySlugMap()) ) { - return redirect("/" . $cat, 301); + return redirect('/'.$cat, 301); } - return view("site.home", [ - "initialQuery" => trim((string) $request->query("q", "")), - "initialCategory" => trim((string) $request->query("category", "")), - "initialSubcategory" => trim( - (string) $request->query("subcategory", ""), + return view('site.home', [ + 'initialQuery' => trim((string) $request->query('q', '')), + 'initialCategory' => trim((string) $request->query('category', '')), + 'initialSubcategory' => trim( + (string) $request->query('subcategory', ''), ), - "canonicalPath" => "/browse", - "userTier" => $request->user()?->tier, + 'canonicalPath' => '/browse', + 'userTier' => $request->user()?->tier, ]); } public function category(string $categorySlug): View { - if ($categorySlug === "all") { - return view("site.home", [ - "initialQuery" => "", - "initialCategory" => "", - "initialSubcategory" => "", - "canonicalPath" => "/", - "userTier" => request()->user()?->tier, + if ($categorySlug === 'all') { + return view('site.home', [ + 'initialQuery' => '', + 'initialCategory' => '', + 'initialSubcategory' => '', + 'canonicalPath' => '/', + 'userTier' => request()->user()?->tier, ]); } - $categoryLabel = $this->categorySlugMap()[$categorySlug] ?? ""; - abort_if($categoryLabel === "", 404); + $categoryLabel = $this->categorySlugMap()[$categorySlug] ?? ''; + abort_if($categoryLabel === '', 404); - return view("site.home", [ - "initialQuery" => "", - "initialCategory" => $categoryLabel, - "initialSubcategory" => "", - "canonicalPath" => "/" . $categorySlug, - "userTier" => request()->user()?->tier, + return view('site.home', [ + 'initialQuery' => '', + 'initialCategory' => $categoryLabel, + 'initialSubcategory' => '', + 'canonicalPath' => '/'.$categorySlug, + 'userTier' => request()->user()?->tier, ]); } @@ -113,61 +113,62 @@ class SiteController extends Controller string $categorySlug, string $subcategorySlug, ): View { - if ($categorySlug === "all") { + if ($categorySlug === 'all') { abort(404); } - $categoryLabel = $this->categorySlugMap()[$categorySlug] ?? ""; - abort_if($categoryLabel === "", 404); + $categoryLabel = $this->categorySlugMap()[$categorySlug] ?? ''; + abort_if($categoryLabel === '', 404); - return view("site.home", [ - "initialQuery" => "", - "initialCategory" => $categoryLabel, - "initialSubcategory" => $subcategorySlug, - "canonicalPath" => "/" . $categorySlug . "/" . $subcategorySlug, - "userTier" => request()->user()?->tier, + return view('site.home', [ + 'initialQuery' => '', + 'initialCategory' => $categoryLabel, + 'initialSubcategory' => $subcategorySlug, + 'canonicalPath' => '/'.$categorySlug.'/'.$subcategorySlug, + 'userTier' => request()->user()?->tier, ]); } public function apiDocs(): View { - return view("site.api-docs"); + return view('site.api-docs'); } public function pricing(): View { $user = request()->user(); - $currencyPref = strtoupper((string) session("pricing_currency", "")); - if (!in_array($currencyPref, ["IDR", "USD"], true)) { + $currencyPref = strtoupper((string) session('pricing_currency', '')); + if (! in_array($currencyPref, ['IDR', 'USD'], true)) { $currencyPref = $this->detectPricingCurrency(request()); - session(["pricing_currency" => $currencyPref]); + session(['pricing_currency' => $currencyPref]); } - $rate = (int) config("dewemoji.pricing.usd_rate", 15000); - $plans = PricingPlan::where("status", "active")->get()->keyBy("code"); - $defaults = config("dewemoji.pricing.defaults", []); - $fallback = collect($defaults)->keyBy("code"); + $rate = (int) config('dewemoji.pricing.usd_rate', 15000); + $plans = PricingPlan::where('status', 'active')->get()->keyBy('code'); + $defaults = config('dewemoji.pricing.defaults', []); + $fallback = collect($defaults)->keyBy('code'); $getPlanAmount = function (string $code) use ($plans, $fallback): int { $plan = $plans->get($code) ?? $fallback->get($code); - return (int) ($plan["amount"] ?? ($plan->amount ?? 0)); + + return (int) ($plan['amount'] ?? ($plan->amount ?? 0)); }; $pricing = [ - "personal_monthly" => [ - "idr" => $getPlanAmount("personal_monthly"), + 'personal_monthly' => [ + 'idr' => $getPlanAmount('personal_monthly'), ], - "personal_annual" => [ - "idr" => $getPlanAmount("personal_annual"), + 'personal_annual' => [ + 'idr' => $getPlanAmount('personal_annual'), ], - "personal_lifetime" => [ - "idr" => $getPlanAmount("personal_lifetime"), + 'personal_lifetime' => [ + 'idr' => $getPlanAmount('personal_lifetime'), ], ]; foreach ($pricing as $key => $row) { - $pricing[$key]["usd"] = - $rate > 0 ? round($row["idr"] / $rate, 2) : 0; + $pricing[$key]['usd'] = + $rate > 0 ? round($row['idr'] / $rate, 2) : 0; } $hasActiveLifetime = false; @@ -175,28 +176,28 @@ class SiteController extends Controller $pendingCooldownRemaining = 0; if ($user) { $hasActiveLifetime = Subscription::query() - ->where("user_id", $user->id) - ->where("plan", "personal_lifetime") - ->where("status", "active") + ->where('user_id', $user->id) + ->where('plan', 'personal_lifetime') + ->where('status', 'active') ->where(function ($query) { $query - ->whereNull("expires_at") - ->orWhere("expires_at", ">", now()); + ->whereNull('expires_at') + ->orWhere('expires_at', '>', now()); }) ->exists(); $hasPendingPayment = Payment::query() - ->where("user_id", $user->id) - ->where("status", "pending") + ->where('user_id', $user->id) + ->where('status', 'pending') ->exists(); $cooldown = (int) config( - "dewemoji.billing.pending_cooldown_seconds", + 'dewemoji.billing.pending_cooldown_seconds', 120, ); if ($cooldown > 0) { $latestPending = Payment::query() - ->where("user_id", $user->id) - ->where("status", "pending") - ->orderByDesc("id") + ->where('user_id', $user->id) + ->where('status', 'pending') + ->orderByDesc('id') ->first(); if ($latestPending && $latestPending->created_at) { $age = max( @@ -209,41 +210,40 @@ class SiteController extends Controller } } - return view("site.pricing", [ - "currencyPref" => $currencyPref, - "usdRate" => $rate, - "pricing" => $pricing, - "payments" => [ - "qris_url" => (string) config("dewemoji.payments.qris_url", ""), - "paypal_url" => (string) config( - "dewemoji.payments.paypal_url", - "", + return view('site.pricing', [ + 'currencyPref' => $currencyPref, + 'usdRate' => $rate, + 'pricing' => $pricing, + 'payments' => [ + 'qris_url' => (string) config('dewemoji.payments.qris_url', ''), + 'paypal_url' => (string) config( + 'dewemoji.payments.paypal_url', + '', ), ], - "pakasirEnabled" => - (bool) config( - "dewemoji.billing.providers.pakasir.enabled", - false, - ) && + 'pakasirEnabled' => (bool) config( + 'dewemoji.billing.providers.pakasir.enabled', + false, + ) && (string) config( - "dewemoji.billing.providers.pakasir.api_base", - "", - ) !== "" && + 'dewemoji.billing.providers.pakasir.api_base', + '', + ) !== '' && (string) config( - "dewemoji.billing.providers.pakasir.api_key", - "", - ) !== "" && + 'dewemoji.billing.providers.pakasir.api_key', + '', + ) !== '' && (string) config( - "dewemoji.billing.providers.pakasir.project", - "", - ) !== "", - "paypalEnabled" => $this->paypalEnabled($this->billingMode()), - "paypalPlans" => $this->paypalPlanAvailability( + 'dewemoji.billing.providers.pakasir.project', + '', + ) !== '', + 'paypalEnabled' => $this->paypalEnabled($this->billingMode()), + 'paypalPlans' => $this->paypalPlanAvailability( $this->billingMode(), ), - "hasActiveLifetime" => $hasActiveLifetime, - "hasPendingPayment" => $hasPendingPayment, - "pendingCooldownRemaining" => $pendingCooldownRemaining, + 'hasActiveLifetime' => $hasActiveLifetime, + 'hasPendingPayment' => $hasPendingPayment, + 'pendingCooldownRemaining' => $pendingCooldownRemaining, ]); } @@ -253,141 +253,142 @@ class SiteController extends Controller return true; } - $fallback = $mode === "live" ? "sandbox" : "live"; + $fallback = $mode === 'live' ? 'sandbox' : 'live'; + return $this->paypalConfiguredMode($fallback); } private function paypalConfiguredMode(string $mode): bool { $enabled = (bool) config( - "dewemoji.billing.providers.paypal.enabled", + 'dewemoji.billing.providers.paypal.enabled', false, ); $clientId = (string) config( "dewemoji.billing.providers.paypal.{$mode}.client_id", - "", + '', ); $clientSecret = (string) config( "dewemoji.billing.providers.paypal.{$mode}.client_secret", - "", + '', ); $apiBase = (string) config( "dewemoji.billing.providers.paypal.{$mode}.api_base", - "", + '', ); return $enabled && - $clientId !== "" && - $clientSecret !== "" && - $apiBase !== ""; + $clientId !== '' && + $clientSecret !== '' && + $apiBase !== ''; } private function paypalPlanAvailability(string $mode): array { - $plans = PricingPlan::whereIn("code", [ - "personal_monthly", - "personal_annual", + $plans = PricingPlan::whereIn('code', [ + 'personal_monthly', + 'personal_annual', ]) ->get() - ->keyBy("code"); + ->keyBy('code'); $fromDb = function (string $code) use ($plans, $mode): bool { $plan = $plans->get($code); - if (!$plan) { + if (! $plan) { return false; } $meta = $plan->meta ?? []; - return (string) ($meta["paypal"][$mode]["plan"]["id"] ?? "") !== ""; + + return (string) ($meta['paypal'][$mode]['plan']['id'] ?? '') !== ''; }; $fromEnv = function (string $code) use ($mode): bool { return (string) config( "dewemoji.billing.providers.paypal.plan_ids.{$mode}.{$code}", - "", - ) !== ""; + '', + ) !== ''; }; return [ - "personal_monthly" => - $fromDb("personal_monthly") || $fromEnv("personal_monthly"), - "personal_annual" => - $fromDb("personal_annual") || $fromEnv("personal_annual"), + 'personal_monthly' => $fromDb('personal_monthly') || $fromEnv('personal_monthly'), + 'personal_annual' => $fromDb('personal_annual') || $fromEnv('personal_annual'), ]; } public function setPricingCurrency(Request $request): RedirectResponse { $data = $request->validate([ - "currency" => "required|string|in:IDR,USD", + 'currency' => 'required|string|in:IDR,USD', ]); - session(["pricing_currency" => $data["currency"]]); + session(['pricing_currency' => $data['currency']]); + return back(); } public function support(): View { - return view("site.support"); + return view('site.support'); } public function download(): View { $downloadBaseUrl = rtrim( - (string) config("dewemoji.apk_release.public_base_url", ""), - "/", + (string) config('dewemoji.apk_release.public_base_url', ''), + '/', ); $androidEnabled = false; - return view("site.download", [ - "androidEnabled" => $androidEnabled, - "androidVersionJsonUrl" => $androidEnabled - ? $downloadBaseUrl . "/version.json" - : "", - "androidLatestApkUrl" => $androidEnabled - ? $downloadBaseUrl . "/dewemoji-latest.apk" - : "", + return view('site.download', [ + 'androidEnabled' => $androidEnabled, + 'androidVersionJsonUrl' => $androidEnabled + ? $downloadBaseUrl.'/version.json' + : '', + 'androidLatestApkUrl' => $androidEnabled + ? $downloadBaseUrl.'/dewemoji-latest.apk' + : '', ]); } public function downloadVersionJson( Request $request, ): RedirectResponse|JsonResponse { - $target = $this->apkReleaseTargetUrl("version_json"); - if ($target === "") { + $target = $this->apkReleaseTargetUrl('version_json'); + if ($target === '') { return response()->json( - ["ok" => false, "error" => "apk_release_not_configured"], + ['ok' => false, 'error' => 'apk_release_not_configured'], 404, ); } return redirect()->away($target, 302, [ - "Cache-Control" => "no-store, no-cache, must-revalidate", - "Pragma" => "no-cache", + 'Cache-Control' => 'no-store, no-cache, must-revalidate', + 'Pragma' => 'no-cache', ]); } public function downloadLatestApk( Request $request, ): RedirectResponse|JsonResponse { - $target = $this->apkReleaseTargetUrl("latest_apk"); - if ($target === "") { + $target = $this->apkReleaseTargetUrl('latest_apk'); + if ($target === '') { return response()->json( - ["ok" => false, "error" => "apk_release_not_configured"], + ['ok' => false, 'error' => 'apk_release_not_configured'], 404, ); } return redirect()->away($target, 302, [ - "Cache-Control" => "no-store, no-cache, must-revalidate", - "Pragma" => "no-cache", + 'Cache-Control' => 'no-store, no-cache, must-revalidate', + 'Pragma' => 'no-cache', ]); } public function assetLinks(): JsonResponse { - $appId = trim((string) config("dewemoji.apk_release.app_id", "")); + $appId = trim((string) config('dewemoji.apk_release.app_id', '')); $rawFingerprints = (array) config( - "dewemoji.apk_release.assetlinks.fingerprints", + 'dewemoji.apk_release.assetlinks.fingerprints', [], ); $fingerprints = []; @@ -395,235 +396,235 @@ class SiteController extends Controller $normalized = $this->normalizeApkCertFingerprint( (string) $fingerprint, ); - if ($normalized !== "") { + if ($normalized !== '') { $fingerprints[] = $normalized; } } $fingerprints = array_values(array_unique($fingerprints)); - if ($appId === "" || $fingerprints === []) { + if ($appId === '' || $fingerprints === []) { return response()->json([], 200, [ - "Cache-Control" => "no-store, no-cache, must-revalidate", - "Pragma" => "no-cache", + 'Cache-Control' => 'no-store, no-cache, must-revalidate', + 'Pragma' => 'no-cache', ]); } return response()->json( [ [ - "relation" => [ - "delegate_permission/common.handle_all_urls", + 'relation' => [ + 'delegate_permission/common.handle_all_urls', ], - "target" => [ - "namespace" => "android_app", - "package_name" => $appId, - "sha256_cert_fingerprints" => $fingerprints, + 'target' => [ + 'namespace' => 'android_app', + 'package_name' => $appId, + 'sha256_cert_fingerprints' => $fingerprints, ], ], ], 200, [ - "Cache-Control" => "no-store, no-cache, must-revalidate", - "Pragma" => "no-cache", + 'Cache-Control' => 'no-store, no-cache, must-revalidate', + 'Pragma' => 'no-cache', ], ); } public function privacy(): View { - return view("site.privacy"); + return view('site.privacy'); } public function terms(): View { - return view("site.terms"); + return view('site.terms'); } private function detectPricingCurrency(Request $request): string { $country = strtoupper( - (string) ($request->header("CF-IPCountry") ?? - ($request->header("X-Country-Code") ?? - ($request->header("X-Geo-Country") ?? - ($request->header("X-Appengine-Country") ?? - ($request->header("CloudFront-Viewer-Country") ?? - ""))))), + (string) ($request->header('CF-IPCountry') ?? + ($request->header('X-Country-Code') ?? + ($request->header('X-Geo-Country') ?? + ($request->header('X-Appengine-Country') ?? + ($request->header('CloudFront-Viewer-Country') ?? + ''))))), ); - return $country === "ID" ? "IDR" : "USD"; + return $country === 'ID' ? 'IDR' : 'USD'; } public function emojiDetail(string $slug): View|Response { $dataPath = $this->datasetPath(); - if (!is_file($dataPath)) { - abort(500, "Emoji dataset file not found."); + if (! is_file($dataPath)) { + abort(500, 'Emoji dataset file not found.'); } $raw = file_get_contents($dataPath); if ($raw === false) { - abort(500, "Emoji dataset file could not be read."); + abort(500, 'Emoji dataset file could not be read.'); } $decoded = json_decode($raw, true); - if (!is_array($decoded)) { - abort(500, "Emoji dataset JSON is invalid."); + if (! is_array($decoded)) { + abort(500, 'Emoji dataset JSON is invalid.'); } - $items = $decoded["emojis"] ?? []; + $items = $decoded['emojis'] ?? []; $match = null; $byEmoji = []; foreach ($items as $item) { - $char = (string) ($item["emoji"] ?? ""); - if ($char !== "" && !isset($byEmoji[$char])) { + $char = (string) ($item['emoji'] ?? ''); + if ($char !== '' && ! isset($byEmoji[$char])) { $byEmoji[$char] = $item; } - if (($item["slug"] ?? "") === $slug) { + if (($item['slug'] ?? '') === $slug) { $match = $item; } } - if (!$match) { + if (! $match) { abort(404); } $relatedDetails = []; - foreach (array_slice($match["related"] ?? [], 0, 8) as $relatedEmoji) { + foreach (array_slice($match['related'] ?? [], 0, 8) as $relatedEmoji) { $relatedEmoji = (string) $relatedEmoji; $ref = $byEmoji[$relatedEmoji] ?? null; $relatedDetails[] = [ - "emoji" => $relatedEmoji, - "slug" => (string) ($ref["slug"] ?? ""), - "name" => (string) ($ref["name"] ?? $relatedEmoji), + 'emoji' => $relatedEmoji, + 'slug' => (string) ($ref['slug'] ?? ''), + 'name' => (string) ($ref['name'] ?? $relatedEmoji), ]; } $user = request()->user(); $canManageKeywords = (bool) $user; - $isPersonal = $user && (string) $user->tier === "personal"; - $freeLimit = (int) config("dewemoji.pagination.free_max_limit", 20); + $isPersonal = $user && (string) $user->tier === 'personal'; + $freeLimit = (int) config('dewemoji.pagination.free_max_limit', 20); $keywordLimit = $isPersonal ? null : $freeLimit; $userKeywords = []; $activeKeywordCount = 0; if ($canManageKeywords) { - $activeKeywordCount = UserKeyword::where("user_id", $user->id) - ->where("is_active", true) + $activeKeywordCount = UserKeyword::where('user_id', $user->id) + ->where('is_active', true) ->count(); - $userKeywords = UserKeyword::where("user_id", $user->id) - ->where("emoji_slug", $slug) - ->orderByDesc("id") + $userKeywords = UserKeyword::where('user_id', $user->id) + ->where('emoji_slug', $slug) + ->orderByDesc('id') ->get(); } $limitReached = $keywordLimit !== null && $activeKeywordCount >= $keywordLimit; - return view("site.emoji-detail", [ - "emoji" => $match, - "relatedDetails" => $relatedDetails, - "canonicalPath" => "/emoji/" . $slug, - "userKeywords" => $userKeywords, - "canManageKeywords" => $canManageKeywords, - "keywordLimit" => $keywordLimit, - "limitReached" => $limitReached, - "activeKeywordCount" => $activeKeywordCount, - "userTier" => $user?->tier, + return view('site.emoji-detail', [ + 'emoji' => $match, + 'relatedDetails' => $relatedDetails, + 'canonicalPath' => '/emoji/'.$slug, + 'userKeywords' => $userKeywords, + 'canManageKeywords' => $canManageKeywords, + 'keywordLimit' => $keywordLimit, + 'limitReached' => $limitReached, + 'activeKeywordCount' => $activeKeywordCount, + 'userTier' => $user?->tier, ]); } public function robotsTxt(): Response { $base = rtrim( - config("app.url", request()->getSchemeAndHttpHost()), - "/", + config('app.url', request()->getSchemeAndHttpHost()), + '/', ); $body = - "User-agent: *\nAllow: /\n\nSitemap: " . $base . "/sitemap.xml\n"; + "User-agent: *\nAllow: /\n\nSitemap: ".$base."/sitemap.xml\n"; return response($body, 200)->header( - "Content-Type", - "text/plain; charset=UTF-8", + 'Content-Type', + 'text/plain; charset=UTF-8', ); } public function sitemapXml(): Response { $data = $this->loadDataset(); - $items = is_array($data["emojis"] ?? null) ? $data["emojis"] : []; + $items = is_array($data['emojis'] ?? null) ? $data['emojis'] : []; $base = rtrim( - config("app.url", request()->getSchemeAndHttpHost()), - "/", + config('app.url', request()->getSchemeAndHttpHost()), + '/', ); - $lastUpdatedTs = isset($data["last_updated_ts"]) - ? (int) $data["last_updated_ts"] + $lastUpdatedTs = isset($data['last_updated_ts']) + ? (int) $data['last_updated_ts'] : time(); $lastUpdated = gmdate("Y-m-d\TH:i:s\Z", $lastUpdatedTs); $urls = [ [ - "loc" => $base . "/", - "priority" => "0.8", - "changefreq" => "daily", + 'loc' => $base.'/', + 'priority' => '0.8', + 'changefreq' => 'daily', ], [ - "loc" => $base . "/api-docs", - "priority" => "0.5", - "changefreq" => "weekly", + 'loc' => $base.'/api-docs', + 'priority' => '0.5', + 'changefreq' => 'weekly', ], [ - "loc" => $base . "/pricing", - "priority" => "0.7", - "changefreq" => "weekly", + 'loc' => $base.'/pricing', + 'priority' => '0.7', + 'changefreq' => 'weekly', ], [ - "loc" => $base . "/privacy", - "priority" => "0.3", - "changefreq" => "monthly", + 'loc' => $base.'/privacy', + 'priority' => '0.3', + 'changefreq' => 'monthly', ], [ - "loc" => $base . "/terms", - "priority" => "0.3", - "changefreq" => "monthly", + 'loc' => $base.'/terms', + 'priority' => '0.3', + 'changefreq' => 'monthly', ], [ - "loc" => $base . "/support", - "priority" => "0.4", - "changefreq" => "weekly", + 'loc' => $base.'/support', + 'priority' => '0.4', + 'changefreq' => 'weekly', ], ]; foreach ($items as $item) { - $slug = trim((string) ($item["slug"] ?? "")); - if ($slug === "" || $this->shouldHideForSitemap($item)) { + $slug = trim((string) ($item['slug'] ?? '')); + if ($slug === '' || $this->shouldHideForSitemap($item)) { continue; } $urls[] = [ - "loc" => $base . "/emoji/" . $slug, - "priority" => "0.6", - "changefreq" => "weekly", + 'loc' => $base.'/emoji/'.$slug, + 'priority' => '0.6', + 'changefreq' => 'weekly', ]; } - $xml = '' . "\n"; + $xml = ''."\n"; $xml .= - '' . + ''. "\n"; foreach ($urls as $url) { $xml .= " \n"; $xml .= - " " . - htmlspecialchars((string) $url["loc"], ENT_XML1) . + ' '. + htmlspecialchars((string) $url['loc'], ENT_XML1). "\n"; - $xml .= " " . $lastUpdated . "\n"; - $xml .= " " . $url["changefreq"] . "\n"; - $xml .= " " . $url["priority"] . "\n"; + $xml .= ' '.$lastUpdated."\n"; + $xml .= ' '.$url['changefreq']."\n"; + $xml .= ' '.$url['priority']."\n"; $xml .= " \n"; } - $xml .= "" . "\n"; + $xml .= ''."\n"; return response($xml, 200)->header( - "Content-Type", - "application/xml; charset=UTF-8", + 'Content-Type', + 'application/xml; charset=UTF-8', ); } @@ -646,18 +647,18 @@ class SiteController extends Controller private function loadDataset(): array { $dataPath = $this->datasetPath(); - if (!is_file($dataPath)) { - return ["emojis" => []]; + if (! is_file($dataPath)) { + return ['emojis' => []]; } $raw = file_get_contents($dataPath); if ($raw === false) { - return ["emojis" => []]; + return ['emojis' => []]; } $decoded = json_decode($raw, true); - if (!is_array($decoded)) { - return ["emojis" => []]; + if (! is_array($decoded)) { + return ['emojis' => []]; } return $decoded; @@ -666,61 +667,61 @@ class SiteController extends Controller private function datasetPath(): string { $settings = app(SettingsService::class); - $activePath = (string) $settings->get("emoji_dataset_active_path", ""); - if ($activePath !== "" && is_file($activePath)) { + $activePath = (string) $settings->get('emoji_dataset_active_path', ''); + if ($activePath !== '' && is_file($activePath)) { return $activePath; } - return (string) config("dewemoji.data_path"); + return (string) config('dewemoji.data_path'); } private function apkReleaseTargetUrl(string $key): string { - if (!(bool) config("dewemoji.apk_release.enabled", false)) { - return ""; + if (! (bool) config('dewemoji.apk_release.enabled', false)) { + return ''; } $base = trim( - (string) config("dewemoji.apk_release.r2_public_base_url", ""), + (string) config('dewemoji.apk_release.r2_public_base_url', ''), ); $objectKey = trim( - (string) config("dewemoji.apk_release.r2_keys.{$key}", ""), + (string) config("dewemoji.apk_release.r2_keys.{$key}", ''), ); - if ($base === "" || $objectKey === "") { - return ""; + if ($base === '' || $objectKey === '') { + return ''; } - return rtrim($base, "/") . "/" . ltrim($objectKey, "/"); + return rtrim($base, '/').'/'.ltrim($objectKey, '/'); } private function normalizeApkCertFingerprint(string $value): string { $clean = strtoupper(trim($value)); - if ($clean === "") { - return ""; + if ($clean === '') { + return ''; } if (preg_match('/^[0-9A-F]{64}$/', $clean) === 1) { - return implode(":", str_split($clean, 2)); + return implode(':', str_split($clean, 2)); } if (preg_match('/^[0-9A-F]{2}(?::[0-9A-F]{2}){31}$/', $clean) === 1) { return $clean; } - return ""; + return ''; } /** - * @param array $emoji + * @param array $emoji */ private function shouldHideForSitemap(array $emoji): bool { - $name = strtolower(trim((string) ($emoji["name"] ?? ""))); - $category = strtolower(trim((string) ($emoji["category"] ?? ""))); - $subcategory = strtolower(trim((string) ($emoji["subcategory"] ?? ""))); + $name = strtolower(trim((string) ($emoji['name'] ?? ''))); + $category = strtolower(trim((string) ($emoji['category'] ?? ''))); + $subcategory = strtolower(trim((string) ($emoji['subcategory'] ?? ''))); - if ($subcategory === "family" || str_starts_with($name, "family:")) { + if ($subcategory === 'family' || str_starts_with($name, 'family:')) { return true; } if (preg_match("~\bwoman: beard\b~i", $name)) { @@ -732,7 +733,7 @@ class SiteController extends Controller if (preg_match("~\bpregnant man\b~i", $name)) { return true; } - if ($category === "people & body") { + if ($category === 'people & body') { if (preg_match("~\bmen holding hands\b~i", $name)) { return true; } diff --git a/app/app/Http/Middleware/CanonicalPathMiddleware.php b/app/app/Http/Middleware/CanonicalPathMiddleware.php index 4677998..1f692a9 100644 --- a/app/app/Http/Middleware/CanonicalPathMiddleware.php +++ b/app/app/Http/Middleware/CanonicalPathMiddleware.php @@ -10,7 +10,7 @@ use Symfony\Component\HttpFoundation\Response; class CanonicalPathMiddleware { /** - * @param Closure(Request): Response $next + * @param Closure(Request): Response $next */ public function handle(Request $request, Closure $next): Response { diff --git a/app/app/Models/User.php b/app/app/Models/User.php index 5edeb1f..549bc9a 100644 --- a/app/app/Models/User.php +++ b/app/app/Models/User.php @@ -2,10 +2,10 @@ namespace App\Models; -use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; -use Illuminate\Auth\MustVerifyEmail; use App\Notifications\ResetPasswordNotification; use App\Notifications\VerifyEmailNotification; +use Illuminate\Auth\MustVerifyEmail; +use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -13,7 +13,7 @@ use Illuminate\Notifications\Notifiable; class User extends Authenticatable implements MustVerifyEmailContract { /** @use HasFactory<\Database\Factories\UserFactory> */ - use HasFactory, Notifiable, MustVerifyEmail; + use HasFactory, MustVerifyEmail, Notifiable; /** * The attributes that are mass assignable. @@ -58,7 +58,7 @@ class User extends Authenticatable implements MustVerifyEmailContract public function sendEmailVerificationNotification(): void { - $this->notify(new VerifyEmailNotification()); + $this->notify(new VerifyEmailNotification); } public function sendPasswordResetNotification($token): void diff --git a/app/app/Providers/AppServiceProvider.php b/app/app/Providers/AppServiceProvider.php index 08a8ada..7d4dcab 100644 --- a/app/app/Providers/AppServiceProvider.php +++ b/app/app/Providers/AppServiceProvider.php @@ -2,10 +2,10 @@ namespace App\Providers; -use Illuminate\Support\ServiceProvider; +use App\Mail\MailketingTransport; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Mail; -use App\Mail\MailketingTransport; +use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { diff --git a/app/app/Providers/NativeAppServiceProvider.php b/app/app/Providers/NativeAppServiceProvider.php index 5cbadf8..dd0e387 100644 --- a/app/app/Providers/NativeAppServiceProvider.php +++ b/app/app/Providers/NativeAppServiceProvider.php @@ -2,8 +2,8 @@ namespace App\Providers; -use Native\Desktop\Facades\Window; use Native\Desktop\Contracts\ProvidesPhpIni; +use Native\Desktop\Facades\Window; class NativeAppServiceProvider implements ProvidesPhpIni { diff --git a/app/app/Services/Auth/ApiKeyService.php b/app/app/Services/Auth/ApiKeyService.php index 69a01b5..52561ff 100644 --- a/app/app/Services/Auth/ApiKeyService.php +++ b/app/app/Services/Auth/ApiKeyService.php @@ -40,7 +40,7 @@ class ApiKeyService ->whereNull('revoked_at') ->first(); - if (!$record) { + if (! $record) { return null; } @@ -48,7 +48,7 @@ class ApiKeyService $record->save(); $user = $record->user; - if (!$user || (string) $user->tier !== 'personal') { + if (! $user || (string) $user->tier !== 'personal') { return null; } diff --git a/app/app/Services/Billing/PayPalPlanSyncService.php b/app/app/Services/Billing/PayPalPlanSyncService.php index 1482630..7dd67bc 100644 --- a/app/app/Services/Billing/PayPalPlanSyncService.php +++ b/app/app/Services/Billing/PayPalPlanSyncService.php @@ -21,14 +21,16 @@ class PayPalPlanSyncService ]; $token = $this->getAccessToken($mode); - if (!$token) { + if (! $token) { Log::warning('PayPal plan sync aborted: missing access token', ['mode' => $mode]); + return $result; } $productId = $this->ensureProduct($mode, $token); - if (!$productId) { + if (! $productId) { Log::warning('PayPal plan sync aborted: missing product id', ['mode' => $mode]); + return $result; } @@ -58,12 +60,14 @@ class PayPalPlanSyncService $keepIds[] = $currentPlanId; } $result['skipped']++; + continue; } $newPlanId = $this->createPlan($mode, $token, $productId, $plan->code, $plan->name, $amountUsd, $plan->period); - if (!$newPlanId) { + if (! $newPlanId) { $result['skipped']++; + continue; } @@ -105,6 +109,7 @@ class PayPalPlanSyncService { $rate = (int) config('dewemoji.pricing.usd_rate', 15000); $usd = $rate > 0 ? $idrAmount / $rate : 0; + return number_format(max($usd, 1), 2, '.', ''); } @@ -125,7 +130,7 @@ class PayPalPlanSyncService $clientSecret = config("dewemoji.billing.providers.paypal.{$mode}.client_secret"); $apiBase = config("dewemoji.billing.providers.paypal.{$mode}.api_base"); - if (!$clientId || !$clientSecret || !$apiBase) { + if (! $clientId || ! $clientSecret || ! $apiBase) { return null; } @@ -136,8 +141,9 @@ class PayPalPlanSyncService 'grant_type' => 'client_credentials', ]); - if (!$res->successful()) { + if (! $res->successful()) { Log::warning('PayPal auth failed', ['body' => $res->body()]); + return null; } @@ -175,12 +181,13 @@ class PayPalPlanSyncService ->post(rtrim($apiBase, '/').'/v1/catalogs/products', $payload); $createdId = $create->json('id'); if ($createdId) { - if (!$create->successful()) { + if (! $create->successful()) { Log::warning('PayPal product create returned non-OK but provided id', [ 'status' => $create->status(), 'body' => $create->body(), ]); } + return $createdId; } @@ -188,6 +195,7 @@ class PayPalPlanSyncService 'status' => $create->status(), 'body' => $create->body(), ]); + return null; } @@ -237,13 +245,14 @@ class PayPalPlanSyncService $planId = $res->json('id'); if ($planId) { - if (!$res->successful()) { + if (! $res->successful()) { Log::warning('PayPal plan create returned non-OK but provided id', [ 'code' => $code, 'status' => $res->status(), 'body' => $res->body(), ]); } + return $planId; } @@ -252,6 +261,7 @@ class PayPalPlanSyncService 'status' => $res->status(), 'body' => $res->body(), ]); + return null; } @@ -262,7 +272,7 @@ class PayPalPlanSyncService ->timeout((int) config('dewemoji.billing.providers.paypal.timeout', 10)) ->get(rtrim($apiBase, '/').'/v1/billing/plans/'.$planId); - if (!$res->successful()) { + if (! $res->successful()) { return null; } @@ -290,12 +300,12 @@ class PayPalPlanSyncService 'page' => 1, ]); - if (!$res->successful()) { + if (! $res->successful()) { return 0; } $items = $res->json('plans') ?? []; - if (!is_array($items)) { + if (! is_array($items)) { return 0; } @@ -303,7 +313,7 @@ class PayPalPlanSyncService foreach ($items as $plan) { $id = $plan['id'] ?? null; $status = strtoupper((string) ($plan['status'] ?? '')); - if (!$id || in_array($id, $keepIds, true)) { + if (! $id || in_array($id, $keepIds, true)) { continue; } if ($status !== 'ACTIVE') { diff --git a/app/app/Services/Billing/PaypalWebhookProcessor.php b/app/app/Services/Billing/PaypalWebhookProcessor.php index 1c743f7..9df6992 100644 --- a/app/app/Services/Billing/PaypalWebhookProcessor.php +++ b/app/app/Services/Billing/PaypalWebhookProcessor.php @@ -14,16 +14,15 @@ class PaypalWebhookProcessor public function __construct( private readonly SubscriptionTransitionService $subscriptionTransition, private readonly KeywordQuotaService $keywordQuota - ) { - } + ) {} /** - * @param array $payload + * @param array $payload */ public function process(string $eventType, array $payload): void { $resource = $payload['resource'] ?? []; - if (!is_array($resource)) { + if (! is_array($resource)) { return; } @@ -35,7 +34,7 @@ class PaypalWebhookProcessor } $user = User::where('email', $email)->first(); - if (!$user) { + if (! $user) { throw new \RuntimeException('User not found for webhook email.'); } @@ -68,6 +67,7 @@ class PaypalWebhookProcessor $subscriptionId, $planCode ); + return; } @@ -99,7 +99,7 @@ class PaypalWebhookProcessor 'tier' => $active ? 'personal' : 'free', ]); $this->keywordQuota->enforceForUser($userId, $active ? 'personal' : 'free'); - if (!$active) { + if (! $active) { UserApiKey::where('user_id', $userId)->update(['revoked_at' => now()]); } } diff --git a/app/app/Services/Billing/SubscriptionTransitionService.php b/app/app/Services/Billing/SubscriptionTransitionService.php index 14834a6..bd63377 100644 --- a/app/app/Services/Billing/SubscriptionTransitionService.php +++ b/app/app/Services/Billing/SubscriptionTransitionService.php @@ -67,6 +67,7 @@ class SubscriptionTransitionService $sub->status = 'canceled'; $sub->canceled_at = now(); $sub->save(); + continue; } @@ -74,12 +75,13 @@ class SubscriptionTransitionService $isRecurring = in_array((string) $sub->plan, ['personal_monthly', 'personal_annual'], true); if ($sub->provider === 'paypal' && $isRecurring && $sub->provider_ref) { $cancelled = $this->cancelPaypalSubscription($mode, (string) $sub->provider_ref); - if (!$cancelled) { + if (! $cancelled) { Log::warning('Could not cancel previous PayPal subscription during plan transition', [ 'user_id' => $userId, 'new_plan' => $newPlanCode, 'old_subscription_id' => $sub->provider_ref, ]); + continue; } } @@ -93,7 +95,7 @@ class SubscriptionTransitionService private function cancelPaypalSubscription(string $mode, string $subscriptionId): bool { $token = $this->getAccessToken($mode); - if (!$token) { + if (! $token) { return false; } @@ -118,7 +120,7 @@ class SubscriptionTransitionService $clientSecret = config("dewemoji.billing.providers.paypal.{$mode}.client_secret"); $apiBase = config("dewemoji.billing.providers.paypal.{$mode}.api_base"); - if (!$clientId || !$clientSecret || !$apiBase) { + if (! $clientId || ! $clientSecret || ! $apiBase) { return null; } @@ -129,7 +131,7 @@ class SubscriptionTransitionService 'grant_type' => 'client_credentials', ]); - if (!$res->successful()) { + if (! $res->successful()) { return null; } diff --git a/app/app/Services/EmojiCatalog/EmojiCatalogService.php b/app/app/Services/EmojiCatalog/EmojiCatalogService.php index b8a206e..2250e81 100644 --- a/app/app/Services/EmojiCatalog/EmojiCatalogService.php +++ b/app/app/Services/EmojiCatalog/EmojiCatalogService.php @@ -11,8 +11,7 @@ class EmojiCatalogService { public function __construct( private readonly SettingsService $settings - ) { - } + ) {} /** * @return array|null @@ -20,7 +19,7 @@ class EmojiCatalogService public function findItem(int $emojiId): ?array { $base = DB::table('emojis')->where('emoji_id', $emojiId)->first(); - if (!$base) { + if (! $base) { return null; } @@ -33,7 +32,7 @@ class EmojiCatalogService } /** - * @param array $input + * @param array $input */ public function saveItem(array $input): int { @@ -151,7 +150,7 @@ class EmojiCatalogService public function deleteItem(int $emojiId): void { $emoji = DB::table('emojis')->where('emoji_id', $emojiId)->first(); - if (!$emoji) { + if (! $emoji) { return; } @@ -176,7 +175,7 @@ class EmojiCatalogService */ public function importFromDataFile(string $path): array { - if (!is_file($path)) { + if (! is_file($path)) { throw new RuntimeException('Dataset file not found: '.$path); } @@ -186,7 +185,7 @@ class EmojiCatalogService } $decoded = json_decode($raw, true); - if (!is_array($decoded) || !is_array($decoded['emojis'] ?? null)) { + if (! is_array($decoded) || ! is_array($decoded['emojis'] ?? null)) { throw new RuntimeException('Invalid emoji dataset format.'); } @@ -195,7 +194,7 @@ class EmojiCatalogService $imported = 0; $skipped = 0; foreach ($rows as $row) { - if (!is_array($row)) { + if (! is_array($row)) { continue; } $total++; @@ -203,10 +202,12 @@ class EmojiCatalogService $emojiId = (int) ($row['emoji_id'] ?? 0); if ($slug !== '' && DB::table('emojis')->where('slug', $slug)->exists()) { $skipped++; + continue; } if ($emojiId > 0 && DB::table('emojis')->where('emoji_id', $emojiId)->exists()) { $skipped++; + continue; } @@ -222,7 +223,7 @@ class EmojiCatalogService } /** - * @param array $input + * @param array $input * @return array */ public function normalizeItemPayload(array $input): array @@ -371,7 +372,7 @@ class EmojiCatalogService ]; $snapshotDir = $this->snapshotDirectory(); - if (!is_dir($snapshotDir) && !mkdir($snapshotDir, 0775, true) && !is_dir($snapshotDir)) { + if (! is_dir($snapshotDir) && ! mkdir($snapshotDir, 0775, true) && ! is_dir($snapshotDir)) { throw new RuntimeException('Could not create snapshot directory.'); } @@ -405,7 +406,7 @@ class EmojiCatalogService public function listSnapshots(): array { $dir = $this->snapshotDirectory(); - if (!is_dir($dir)) { + if (! is_dir($dir)) { return []; } @@ -413,11 +414,11 @@ class EmojiCatalogService $files = glob($dir.'/emojis-*.json') ?: []; $items = []; foreach ($files as $file) { - if (!is_file($file)) { + if (! is_file($file)) { continue; } $name = basename($file); - if (!preg_match('/^emojis-(\d{14})\.json$/', $name, $m)) { + if (! preg_match('/^emojis-(\d{14})\.json$/', $name, $m)) { continue; } $items[] = [ @@ -438,12 +439,12 @@ class EmojiCatalogService public function activateSnapshot(string $filename, ?string $updatedBy = null): array { - if (!preg_match('/^emojis-(\d{14})\.json$/', $filename, $m)) { + if (! preg_match('/^emojis-(\d{14})\.json$/', $filename, $m)) { throw new RuntimeException('Invalid snapshot filename.'); } $fullPath = $this->snapshotDirectory().'/'.$filename; - if (!is_file($fullPath)) { + if (! is_file($fullPath)) { throw new RuntimeException('Snapshot file not found.'); } @@ -461,7 +462,7 @@ class EmojiCatalogService } /** - * @param array $base + * @param array $base * @return array */ private function hydrateEditorItem(array $base): array @@ -509,7 +510,6 @@ class EmojiCatalogService } /** - * @param mixed $input * @return array */ private function normalizeArray(mixed $input): array @@ -517,7 +517,7 @@ class EmojiCatalogService if (is_string($input)) { $input = preg_split('/\r\n|\r|\n|,/', $input) ?: []; } - if (!is_array($input)) { + if (! is_array($input)) { return []; } @@ -547,13 +547,13 @@ class EmojiCatalogService } /** - * @param array $keywordsEn - * @param array $keywordsId - * @param array $aliases - * @param array $shortcodes - * @param array $altShortcodes - * @param array $intentTags - * @param array $codepoints + * @param array $keywordsEn + * @param array $keywordsId + * @param array $aliases + * @param array $shortcodes + * @param array $altShortcodes + * @param array $intentTags + * @param array $codepoints * @return array */ private function buildSearchTokens( diff --git a/app/app/Services/Extension/ExtensionVerificationService.php b/app/app/Services/Extension/ExtensionVerificationService.php index 03b3d56..e1de706 100644 --- a/app/app/Services/Extension/ExtensionVerificationService.php +++ b/app/app/Services/Extension/ExtensionVerificationService.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Http; class ExtensionVerificationService { /** - * @param array $expectedExtensionIds + * @param array $expectedExtensionIds */ public function verifyToken(string $token, array $expectedExtensionIds): bool { @@ -17,7 +17,7 @@ class ExtensionVerificationService } $config = config('dewemoji.extension_verification', []); - if (!(bool) ($config['enabled'] ?? true)) { + if (! (bool) ($config['enabled'] ?? true)) { return true; } @@ -36,12 +36,12 @@ class ExtensionVerificationService 'Authorization' => 'key='.$serverKey, ])->get($url); - if (!$response->ok()) { + if (! $response->ok()) { return false; } $data = $response->json(); - if (!is_array($data)) { + if (! is_array($data)) { return false; } diff --git a/app/app/Services/Keywords/KeywordQuotaService.php b/app/app/Services/Keywords/KeywordQuotaService.php index a011644..132fad1 100644 --- a/app/app/Services/Keywords/KeywordQuotaService.php +++ b/app/app/Services/Keywords/KeywordQuotaService.php @@ -31,6 +31,7 @@ class KeywordQuotaService ->where('user_id', $userId) ->where('is_active', false) ->update(['is_active' => true]); + return; } @@ -55,4 +56,3 @@ class KeywordQuotaService ->update(['is_active' => false]); } } - diff --git a/app/app/Services/LiveSqlImportService.php b/app/app/Services/LiveSqlImportService.php index eb0f9dd..18bf349 100644 --- a/app/app/Services/LiveSqlImportService.php +++ b/app/app/Services/LiveSqlImportService.php @@ -49,7 +49,7 @@ class LiveSqlImportService public function import(string $path, bool $truncate, int $batchSize, ?OutputInterface $output = null): void { - if (!is_file($path)) { + if (! is_file($path)) { throw new \RuntimeException("SQL file not found: {$path}"); } @@ -64,14 +64,14 @@ class LiveSqlImportService $totalStatements = 0; $totalRows = 0; - while (!$file->eof()) { + while (! $file->eof()) { $line = $file->fgets(); if ($line === false) { break; } if ($statement === '') { - if (!Str::startsWith(ltrim($line), 'INSERT INTO')) { + if (! Str::startsWith(ltrim($line), 'INSERT INTO')) { continue; } } @@ -121,7 +121,7 @@ class LiveSqlImportService ]; foreach ($targets as $table) { - if (!Schema::hasTable($table)) { + if (! Schema::hasTable($table)) { continue; } DB::table($table)->truncate(); @@ -136,7 +136,7 @@ class LiveSqlImportService $statement = trim($statement); $pattern = '/^INSERT INTO `([^`]+)` \\(([^)]+)\\) VALUES\\s*(.+);$/s'; - if (!preg_match($pattern, $statement, $matches)) { + if (! preg_match($pattern, $statement, $matches)) { return [0, 'unknown']; } @@ -145,13 +145,13 @@ class LiveSqlImportService $valuesRaw = $matches[3]; $columns = array_map( - static fn (string $col): string => trim($col, " `"), + static fn (string $col): string => trim($col, ' `'), explode(',', $columnsRaw) ); $targetTable = $this->tableRename[$table] ?? $table; - if (!Schema::hasTable($targetTable)) { + if (! Schema::hasTable($targetTable)) { return [0, $targetTable]; } @@ -260,20 +260,24 @@ class LiveSqlImportService if ($escape) { $buffer .= $this->unescapeChar($ch); $escape = false; + continue; } if ($ch === '\\\\') { $escape = true; + continue; } if ($ch === '\'') { $inString = false; + continue; } $buffer .= $ch; + continue; } @@ -282,12 +286,14 @@ class LiveSqlImportService $currentRow = []; $buffer = ''; $valueIsString = false; + continue; } if ($ch === '\'') { $inString = true; $valueIsString = true; + continue; } @@ -295,6 +301,7 @@ class LiveSqlImportService $currentRow[] = $this->convertValue($buffer, $valueIsString); $buffer = ''; $valueIsString = false; + continue; } @@ -306,6 +313,7 @@ class LiveSqlImportService $buffer = ''; $valueIsString = false; $inRow = false; + continue; } @@ -321,7 +329,7 @@ class LiveSqlImportService { $value = trim($buffer); - if (!$valueIsString) { + if (! $valueIsString) { if ($value === '' || strtoupper($value) === 'NULL') { return null; } @@ -352,7 +360,7 @@ class LiveSqlImportService return null; } - if (!is_string($value)) { + if (! is_string($value)) { return json_encode($value); } diff --git a/app/app/Services/System/SettingsService.php b/app/app/Services/System/SettingsService.php index c28ab73..5ab2ae2 100644 --- a/app/app/Services/System/SettingsService.php +++ b/app/app/Services/System/SettingsService.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Cache; class SettingsService { private const CACHE_KEY = 'dw_settings_all'; + private const CACHE_TTL = 30; /** @@ -20,6 +21,7 @@ class SettingsService foreach (Setting::all(['key', 'value']) as $setting) { $out[$setting->key] = $setting->value; } + return $out; }); } @@ -27,11 +29,12 @@ class SettingsService public function get(string $key, mixed $default = null): mixed { $all = $this->all(); + return array_key_exists($key, $all) ? $all[$key] : $default; } /** - * @param array $values + * @param array $values */ public function setMany(array $values, ?string $updatedBy = null): void { diff --git a/app/database/migrations/2026_02_04_000100_create_licenses_table.php b/app/database/migrations/2026_02_04_000100_create_licenses_table.php index f9fc23e..9879439 100644 --- a/app/database/migrations/2026_02_04_000100_create_licenses_table.php +++ b/app/database/migrations/2026_02_04_000100_create_licenses_table.php @@ -29,4 +29,3 @@ return new class extends Migration Schema::dropIfExists('licenses'); } }; - diff --git a/app/database/migrations/2026_02_04_000200_create_license_activations_table.php b/app/database/migrations/2026_02_04_000200_create_license_activations_table.php index 05cd9cc..584f76d 100644 --- a/app/database/migrations/2026_02_04_000200_create_license_activations_table.php +++ b/app/database/migrations/2026_02_04_000200_create_license_activations_table.php @@ -28,4 +28,3 @@ return new class extends Migration Schema::dropIfExists('license_activations'); } }; - diff --git a/app/database/migrations/2026_02_04_000300_create_usage_logs_table.php b/app/database/migrations/2026_02_04_000300_create_usage_logs_table.php index 8d44ce3..ba93498 100644 --- a/app/database/migrations/2026_02_04_000300_create_usage_logs_table.php +++ b/app/database/migrations/2026_02_04_000300_create_usage_logs_table.php @@ -26,4 +26,3 @@ return new class extends Migration Schema::dropIfExists('usage_logs'); } }; - diff --git a/app/database/migrations/2026_02_09_000400_create_admin_audit_logs_table.php b/app/database/migrations/2026_02_09_000400_create_admin_audit_logs_table.php index 080fb8a..b671bd7 100644 --- a/app/database/migrations/2026_02_09_000400_create_admin_audit_logs_table.php +++ b/app/database/migrations/2026_02_09_000400_create_admin_audit_logs_table.php @@ -4,7 +4,8 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration { +return new class extends Migration +{ public function up(): void { Schema::create('admin_audit_logs', function (Blueprint $table): void { diff --git a/app/database/migrations/2026_02_16_000300_add_is_active_to_user_keywords_table.php b/app/database/migrations/2026_02_16_000300_add_is_active_to_user_keywords_table.php index 6b29a58..d8955b4 100644 --- a/app/database/migrations/2026_02_16_000300_add_is_active_to_user_keywords_table.php +++ b/app/database/migrations/2026_02_16_000300_add_is_active_to_user_keywords_table.php @@ -22,4 +22,3 @@ return new class extends Migration }); } }; - diff --git a/app/database/migrations/2026_02_16_001000_add_unique_index_to_emojis_slug.php b/app/database/migrations/2026_02_16_001000_add_unique_index_to_emojis_slug.php index 2a6e97d..0df9580 100644 --- a/app/database/migrations/2026_02_16_001000_add_unique_index_to_emojis_slug.php +++ b/app/database/migrations/2026_02_16_001000_add_unique_index_to_emojis_slug.php @@ -9,7 +9,7 @@ return new class extends Migration { public function up(): void { - if (!Schema::hasTable('emojis')) { + if (! Schema::hasTable('emojis')) { return; } @@ -21,7 +21,7 @@ return new class extends Migration ->pluck('slug') ->all(); - if (!empty($duplicates)) { + if (! empty($duplicates)) { throw new \RuntimeException( 'Cannot add unique index on emojis.slug because duplicate slugs exist: '.implode(', ', $duplicates) ); @@ -34,7 +34,7 @@ return new class extends Migration public function down(): void { - if (!Schema::hasTable('emojis')) { + if (! Schema::hasTable('emojis')) { return; } diff --git a/app/database/seeders/DatabaseSeeder.php b/app/database/seeders/DatabaseSeeder.php index da571f5..8b4291a 100644 --- a/app/database/seeders/DatabaseSeeder.php +++ b/app/database/seeders/DatabaseSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\User; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; diff --git a/app/routes/api.php b/app/routes/api.php index f2ba3fc..75a4cc1 100644 --- a/app/routes/api.php +++ b/app/routes/api.php @@ -1,19 +1,19 @@ comment(Inspiring::quote()); @@ -32,6 +32,7 @@ Artisan::command('dewemoji:webhooks:process {--limit=100 : Max events to process if (empty($statuses)) { $this->error('No statuses provided.'); + return 1; } @@ -68,6 +69,7 @@ Artisan::command('dewemoji:webhooks:process {--limit=100 : Max events to process } $this->info("Processed {$processed} events, failed {$failed}."); + return 0; })->purpose('Process pending webhook events'); @@ -94,11 +96,13 @@ Artisan::command('mailketing:test {email : Recipient email address}', function ( $email = (string) $this->argument('email'); try { - Mail::to($email)->send(new TestMailketing()); + Mail::to($email)->send(new TestMailketing); $this->info("Mailketing test email sent to {$email}."); + return 0; } catch (\Throwable $e) { $this->error('Mailketing test failed: '.$e->getMessage()); + return 1; } })->purpose('Send a Mailketing API test email'); @@ -117,5 +121,6 @@ Artisan::command('dewemoji:normalize-statuses', function () { ->update(['status' => 'canceled']); $this->info("Normalized statuses: subscriptions={$subs}, orders={$orders}, payments={$payments}"); + return 0; })->purpose('Normalize legacy cancelled status spelling to canceled'); diff --git a/app/routes/dashboard.php b/app/routes/dashboard.php index bf9d974..16d6f97 100644 --- a/app/routes/dashboard.php +++ b/app/routes/dashboard.php @@ -3,7 +3,6 @@ use App\Http\Controllers\Dashboard\AdminDashboardController; use App\Http\Controllers\Dashboard\AdminEmojiCatalogController; use App\Http\Controllers\Dashboard\UserDashboardController; -use App\Http\Controllers\ProfileController; use Illuminate\Support\Facades\Route; Route::middleware('auth')->prefix('dashboard')->name('dashboard.')->group(function () { diff --git a/app/routes/web.php b/app/routes/web.php index 178115b..e0a0b39 100644 --- a/app/routes/web.php +++ b/app/routes/web.php @@ -1,10 +1,10 @@ name('home');