From d4c5fb558930d49ff4aebd562f5016778ef7a040 Mon Sep 17 00:00:00 2001 From: ct Date: Wed, 16 Jul 2025 12:38:06 +0800 Subject: [PATCH] Update --- app/Facades/TrackingAnalytics.php | 2 +- app/Http/Controllers/FrontMediaController.php | 4 +- app/Http/Controllers/UserExportController.php | 60 +++++++++++++++++++ app/Jobs/TrackContentSelectionJob.php | 4 +- app/Jobs/TrackExportJob.php | 4 +- app/Jobs/TrackSearchJob.php | 4 +- app/Jobs/UpdateExportStatusJob.php | 6 +- app/Models/ExportToken.php | 5 ++ app/Models/TrackingContentSelection.php | 9 ++- app/Models/TrackingExport.php | 4 +- app/Models/TrackingSearch.php | 4 +- app/Providers/AppServiceProvider.php | 2 +- app/Services/TrackingAnalyticsService.php | 18 +++--- ..._034613_create_tracking_searches_table.php | 8 +-- ...eate_tracking_content_selections_table.php | 8 +-- ...6_034642_create_tracking_exports_table.php | 10 ++-- ...xport_tokens_table_for_anonymous_users.php | 42 +++++++++++++ resources/js/modules/editor/editor.jsx | 3 +- .../video-download/video-download-modal.jsx | 36 ++++++++--- resources/js/pages/FrontPages/Privacy.tsx | 22 +++---- resources/js/pages/FrontPages/Terms.tsx | 22 +++---- resources/js/pages/home/home.tsx | 3 +- resources/js/stores/UserStore.js | 40 +++++++++++++ resources/js/ziggy.js | 2 +- routes/api.php | 13 +++- 25 files changed, 249 insertions(+), 86 deletions(-) create mode 100644 database/migrations/2025_07_16_042908_modify_export_tokens_table_for_anonymous_users.php diff --git a/app/Facades/TrackingAnalytics.php b/app/Facades/TrackingAnalytics.php index 0e48be7..c5c4688 100644 --- a/app/Facades/TrackingAnalytics.php +++ b/app/Facades/TrackingAnalytics.php @@ -21,4 +21,4 @@ protected static function getFacadeAccessor(): string { return 'tracking-analytics'; } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/FrontMediaController.php b/app/Http/Controllers/FrontMediaController.php index 3972ae5..59d89fc 100644 --- a/app/Http/Controllers/FrontMediaController.php +++ b/app/Http/Controllers/FrontMediaController.php @@ -59,7 +59,7 @@ public function background(Request $request) public function searchMemes(Request $request) { $query = $request->input('query', ''); - $limit = $request->input('limit', 30); + $limit = 30; if (empty($query)) { // Return random memes if no search query @@ -106,7 +106,7 @@ public function searchMemes(Request $request) public function searchBackgrounds(Request $request) { $query = $request->input('query', ''); - $limit = $request->input('limit', 30); + $limit = 30; if (empty($query)) { // Return random backgrounds if no search query diff --git a/app/Http/Controllers/UserExportController.php b/app/Http/Controllers/UserExportController.php index ddc19af..a9cd9b6 100644 --- a/app/Http/Controllers/UserExportController.php +++ b/app/Http/Controllers/UserExportController.php @@ -89,4 +89,64 @@ public function premiumExportComplete(Request $request) ], ]); } + + public function basicExportRequest(Request $request) + { + // No authentication required for basic exports + // Create export token (expires in 30 minutes) + $token = ExportToken::create([ + 'user_id' => null, // Anonymous user + 'token' => Str::uuid()->toString(), + 'is_premium' => false, + 'credits_reserved' => 0, // No credits for basic exports + 'expires_at' => now()->addMinutes(30), + ]); + + return response()->json([ + 'success' => [ + 'data' => [ + 'export_token' => $token->token, + ], + ], + ]); + } + + public function basicExportComplete(Request $request) + { + $request->validate([ + 'export_token' => 'required|string', + ]); + + // Find the token (no user requirement) + $token = ExportToken::where('token', $request->export_token) + ->whereNull('user_id') // Only anonymous tokens + ->first(); + + if (! $token) { + return response()->json([ + 'error' => [ + 'message' => 'Invalid export token.', + ], + ]); + } + + if (! $token->isValid()) { + return response()->json([ + 'error' => [ + 'message' => 'Export token has expired or already been used.', + ], + ]); + } + + // Mark token as used + $token->markAsUsed(); + + return response()->json([ + 'success' => [ + 'data' => [ + 'message' => 'Export completed successfully.', + ], + ], + ]); + } } diff --git a/app/Jobs/TrackContentSelectionJob.php b/app/Jobs/TrackContentSelectionJob.php index b979dde..b985c58 100644 --- a/app/Jobs/TrackContentSelectionJob.php +++ b/app/Jobs/TrackContentSelectionJob.php @@ -52,7 +52,7 @@ public function handle(): void 'content_id' => $this->contentId, 'error' => $e->getMessage(), ]); - + throw $e; } } @@ -66,4 +66,4 @@ public function failed(\Throwable $exception): void 'exception' => $exception->getMessage(), ]); } -} \ No newline at end of file +} diff --git a/app/Jobs/TrackExportJob.php b/app/Jobs/TrackExportJob.php index fb6f84a..91b458b 100644 --- a/app/Jobs/TrackExportJob.php +++ b/app/Jobs/TrackExportJob.php @@ -57,7 +57,7 @@ public function handle(): int 'export_format' => $this->exportFormat, 'error' => $e->getMessage(), ]); - + throw $e; } } @@ -71,4 +71,4 @@ public function failed(\Throwable $exception): void 'exception' => $exception->getMessage(), ]); } -} \ No newline at end of file +} diff --git a/app/Jobs/TrackSearchJob.php b/app/Jobs/TrackSearchJob.php index 075bb19..3c45421 100644 --- a/app/Jobs/TrackSearchJob.php +++ b/app/Jobs/TrackSearchJob.php @@ -48,7 +48,7 @@ public function handle(): void 'search_query' => $this->searchQuery, 'error' => $e->getMessage(), ]); - + throw $e; } } @@ -62,4 +62,4 @@ public function failed(\Throwable $exception): void 'exception' => $exception->getMessage(), ]); } -} \ No newline at end of file +} diff --git a/app/Jobs/UpdateExportStatusJob.php b/app/Jobs/UpdateExportStatusJob.php index ea98b55..51329a6 100644 --- a/app/Jobs/UpdateExportStatusJob.php +++ b/app/Jobs/UpdateExportStatusJob.php @@ -28,7 +28,7 @@ public function handle(): void { try { $trackingExport = TrackingExport::findOrFail($this->trackingExportId); - + $updateData = [ 'export_status' => $this->status, ]; @@ -50,7 +50,7 @@ public function handle(): void 'status' => $this->status, 'error' => $e->getMessage(), ]); - + throw $e; } } @@ -63,4 +63,4 @@ public function failed(\Throwable $exception): void 'exception' => $exception->getMessage(), ]); } -} \ No newline at end of file +} diff --git a/app/Models/ExportToken.php b/app/Models/ExportToken.php index 86b3793..3846965 100644 --- a/app/Models/ExportToken.php +++ b/app/Models/ExportToken.php @@ -30,6 +30,11 @@ public function user(): BelongsTo return $this->belongsTo(User::class); } + public function isAnonymous(): bool + { + return is_null($this->user_id); + } + public function isExpired(): bool { return $this->expires_at->isPast(); diff --git a/app/Models/TrackingContentSelection.php b/app/Models/TrackingContentSelection.php index 680e1e1..cbcad90 100644 --- a/app/Models/TrackingContentSelection.php +++ b/app/Models/TrackingContentSelection.php @@ -2,9 +2,8 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Model; class TrackingContentSelection extends Model { @@ -41,11 +40,11 @@ public function content() if ($this->content_type === 'meme') { return $this->belongsTo(MemeMedia::class, 'content_id'); } - + if ($this->content_type === 'background') { return $this->belongsTo(BackgroundMedia::class, 'content_id'); } - + return null; } -} \ No newline at end of file +} diff --git a/app/Models/TrackingExport.php b/app/Models/TrackingExport.php index b3a8cfd..0595c19 100644 --- a/app/Models/TrackingExport.php +++ b/app/Models/TrackingExport.php @@ -2,8 +2,8 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class TrackingExport extends Model @@ -88,4 +88,4 @@ public function scopeProcessing($query) { return $query->where('export_status', 'processing'); } -} \ No newline at end of file +} diff --git a/app/Models/TrackingSearch.php b/app/Models/TrackingSearch.php index 24b2bb9..36f9747 100644 --- a/app/Models/TrackingSearch.php +++ b/app/Models/TrackingSearch.php @@ -2,8 +2,8 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; class TrackingSearch extends Model { @@ -30,4 +30,4 @@ class TrackingSearch extends Model protected $attributes = [ 'platform' => 'web', ]; -} \ No newline at end of file +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 0520d5c..93cbab7 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -15,7 +15,7 @@ class AppServiceProvider extends ServiceProvider public function register(): void { $this->app->singleton('tracking-analytics', function ($app) { - return new TrackingAnalyticsService(); + return new TrackingAnalyticsService; }); } diff --git a/app/Services/TrackingAnalyticsService.php b/app/Services/TrackingAnalyticsService.php index 7486324..73dc1d7 100644 --- a/app/Services/TrackingAnalyticsService.php +++ b/app/Services/TrackingAnalyticsService.php @@ -2,9 +2,9 @@ namespace App\Services; -use App\Jobs\TrackSearchJob; use App\Jobs\TrackContentSelectionJob; use App\Jobs\TrackExportJob; +use App\Jobs\TrackSearchJob; use App\Jobs\UpdateExportStatusJob; use Carbon\Carbon; @@ -118,7 +118,7 @@ public function updateExportStatus( public function getDeviceContext(): array { $request = request(); - + return [ 'user_agent' => $request->userAgent(), 'ip_address' => $request->ip(), @@ -132,15 +132,15 @@ public function getDeviceContext(): array public function generateDeviceId(): string { $request = request(); - + // Generate a consistent device ID based on session or create new one if ($request->session()->has('device_id')) { return $request->session()->get('device_id'); } - + $deviceId = str()->uuid()->toString(); $request->session()->put('device_id', $deviceId); - + return $deviceId; } @@ -151,7 +151,7 @@ public function quickTrackSearch(string $searchType, string $searchQuery, ?array { $context = $this->getDeviceContext(); $deviceId = $this->generateDeviceId(); - + $this->trackSearch( $deviceId, $searchType, @@ -168,7 +168,7 @@ public function quickTrackContentSelection(string $contentType, int $contentId, { $context = $this->getDeviceContext(); $deviceId = $this->generateDeviceId(); - + $this->trackContentSelection( $deviceId, $contentType, @@ -187,7 +187,7 @@ public function quickTrackExport(?int $memeId, ?int $memeMediaId, ?int $backgrou { $context = $this->getDeviceContext(); $deviceId = $this->generateDeviceId(); - + return $this->trackExport( $deviceId, $memeId, @@ -202,4 +202,4 @@ public function quickTrackExport(?int $memeId, ?int $memeMediaId, ?int $backgrou $context['platform'] ); } -} \ No newline at end of file +} diff --git a/database/migrations/2025_07_16_034613_create_tracking_searches_table.php b/database/migrations/2025_07_16_034613_create_tracking_searches_table.php index b7ce4d8..2e394cd 100644 --- a/database/migrations/2025_07_16_034613_create_tracking_searches_table.php +++ b/database/migrations/2025_07_16_034613_create_tracking_searches_table.php @@ -13,22 +13,22 @@ public function up(): void { Schema::create('tracking_searches', function (Blueprint $table) { $table->id(); - + // Common fields $table->string('device_id'); $table->text('user_agent')->nullable(); $table->string('ip_address')->nullable(); $table->enum('platform', ['web', 'ios', 'android'])->default('web'); - + // Search-specific fields $table->enum('search_type', ['meme', 'background']); $table->text('search_query'); $table->json('search_filters')->nullable(); - + // Timestamps $table->timestamp('action_at'); $table->timestamps(); - + // Indexes $table->index(['device_id', 'action_at']); $table->index(['search_type', 'action_at']); diff --git a/database/migrations/2025_07_16_034628_create_tracking_content_selections_table.php b/database/migrations/2025_07_16_034628_create_tracking_content_selections_table.php index 82f417e..60f9efe 100644 --- a/database/migrations/2025_07_16_034628_create_tracking_content_selections_table.php +++ b/database/migrations/2025_07_16_034628_create_tracking_content_selections_table.php @@ -13,24 +13,24 @@ public function up(): void { Schema::create('tracking_content_selections', function (Blueprint $table) { $table->id(); - + // Common fields $table->string('device_id'); $table->text('user_agent')->nullable(); $table->string('ip_address')->nullable(); $table->enum('platform', ['web', 'ios', 'android'])->default('web'); - + // Content selection fields $table->enum('content_type', ['meme', 'background']); $table->unsignedBigInteger('content_id'); $table->string('content_name'); $table->text('search_query')->nullable(); $table->enum('selection_method', ['search', 'browse', 'featured', 'recent']); - + // Timestamps $table->timestamp('action_at'); $table->timestamps(); - + // Indexes $table->index(['device_id', 'action_at']); $table->index(['content_type', 'content_id']); diff --git a/database/migrations/2025_07_16_034642_create_tracking_exports_table.php b/database/migrations/2025_07_16_034642_create_tracking_exports_table.php index c08596a..c6b435e 100644 --- a/database/migrations/2025_07_16_034642_create_tracking_exports_table.php +++ b/database/migrations/2025_07_16_034642_create_tracking_exports_table.php @@ -13,13 +13,13 @@ public function up(): void { Schema::create('tracking_exports', function (Blueprint $table) { $table->id(); - + // Common fields $table->string('device_id'); $table->text('user_agent')->nullable(); $table->string('ip_address')->nullable(); $table->enum('platform', ['web', 'ios', 'android'])->default('web'); - + // Export-specific fields $table->unsignedBigInteger('meme_id')->nullable(); $table->unsignedBigInteger('meme_media_id')->nullable(); @@ -29,17 +29,17 @@ public function up(): void $table->enum('export_quality', ['standard', 'premium'])->default('standard'); $table->enum('export_status', ['initiated', 'processing', 'completed', 'failed'])->default('initiated'); $table->text('error_message')->nullable(); - + // Timestamps $table->timestamp('action_at'); $table->timestamp('completed_at')->nullable(); $table->timestamps(); - + // Foreign key constraints $table->foreign('meme_id')->references('id')->on('memes')->onDelete('set null'); $table->foreign('meme_media_id')->references('id')->on('meme_medias')->onDelete('set null'); $table->foreign('background_media_id')->references('id')->on('background_medias')->onDelete('set null'); - + // Indexes $table->index(['device_id', 'action_at']); $table->index(['export_status', 'action_at']); diff --git a/database/migrations/2025_07_16_042908_modify_export_tokens_table_for_anonymous_users.php b/database/migrations/2025_07_16_042908_modify_export_tokens_table_for_anonymous_users.php new file mode 100644 index 0000000..ab33ce8 --- /dev/null +++ b/database/migrations/2025_07_16_042908_modify_export_tokens_table_for_anonymous_users.php @@ -0,0 +1,42 @@ +dropForeign(['user_id']); + + // Make user_id nullable + $table->unsignedBigInteger('user_id')->nullable()->change(); + + // Add foreign key back with nullable support + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('export_tokens', function (Blueprint $table) { + // Drop foreign key constraint + $table->dropForeign(['user_id']); + + // Make user_id NOT nullable again + $table->unsignedBigInteger('user_id')->nullable(false)->change(); + + // Add foreign key back without nullable + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } +}; diff --git a/resources/js/modules/editor/editor.jsx b/resources/js/modules/editor/editor.jsx index a071cd5..4ba0880 100644 --- a/resources/js/modules/editor/editor.jsx +++ b/resources/js/modules/editor/editor.jsx @@ -8,7 +8,6 @@ import useVideoEditorStore from '@/stores/VideoEditorStore'; // Import fonts first - this loads all Fontsource packages import '@/modules/editor/fonts'; -import UpgradeSheet from '../upgrade/upgrade-sheet'; import EditNavSidebar from './partials/edit-nav-sidebar'; import EditSidebar from './partials/edit-sidebar'; import EditorAISheet from './partials/editor-ai-sheet'; @@ -218,7 +217,7 @@ const Editor = () => { )} - + {/* */} ); diff --git a/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx b/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx index 866010a..8ea5131 100644 --- a/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx +++ b/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx @@ -30,7 +30,7 @@ const VideoDownloadModal = ({ const lastProgressTime = useRef(null); const lastProgress = useRef(0); - const { premiumExportRequest, premiumExportComplete } = useUserStore(); + const { premiumExportRequest, premiumExportComplete, basicExportRequest, basicExportComplete } = useUserStore(); const handleShareOrDownload = async () => { if (!videoBlob || !videoBlobFilename) { @@ -116,11 +116,28 @@ const VideoDownloadModal = ({ } }; - const handleExportWithWatermark = () => { + const handleExportWithWatermark = async () => { setIsPremiumExport(false); setEstimatedTimeRemaining(null); - setStatus('processing'); - handleDownloadButton(); + + // Call basicExportRequest and check response + const response = await basicExportRequest(); + + if (response?.error) { + // Halt the process if there's an error + setIsPremiumExport(false); + return; + } + + if (response?.success) { + // Store the export token + const token = response.success.data.export_token; + console.log('Received basic export token:', token); + setExportToken(token); + // Continue with export if successful + setStatus('processing'); + handleDownloadButton(); + } }; const handleClose = async () => { @@ -140,15 +157,18 @@ const VideoDownloadModal = ({ useEffect(() => { if (status === 'processing' && exportProgress >= 100) { setStatus('complete'); - // Call premiumExportComplete immediately when export completes + // Call appropriate export complete method based on export type if (isPremiumExport && exportToken) { console.log('Calling premiumExportComplete with token:', exportToken); premiumExportComplete(exportToken); - } else if (isPremiumExport && !exportToken) { - console.error('Premium export completed but no token available'); + } else if (!isPremiumExport && exportToken) { + console.log('Calling basicExportComplete with token:', exportToken); + basicExportComplete(exportToken); + } else if (!exportToken) { + console.error('Export completed but no token available'); } } - }, [exportProgress, status, isPremiumExport, exportToken, premiumExportComplete]); + }, [exportProgress, status, isPremiumExport, exportToken, premiumExportComplete, basicExportComplete]); // Calculate estimated time remaining based on progress speed useEffect(() => { diff --git a/resources/js/pages/FrontPages/Privacy.tsx b/resources/js/pages/FrontPages/Privacy.tsx index be930fd..410b23a 100644 --- a/resources/js/pages/FrontPages/Privacy.tsx +++ b/resources/js/pages/FrontPages/Privacy.tsx @@ -1,7 +1,6 @@ -import React from 'react'; -import { Head } from '@inertiajs/react'; -import AuthUser from '@/modules/auth/auth-user'; import Footer from '@/pages/home/partials/Footer.jsx'; +import { Head } from '@inertiajs/react'; +import React from 'react'; interface PrivacyProps { content: string; @@ -12,22 +11,19 @@ const Privacy: React.FC = ({ content, title }) => { return (
- +
-
-
-
+
+
+
- +
- + {/* */}
); }; -export default Privacy; \ No newline at end of file +export default Privacy; diff --git a/resources/js/pages/FrontPages/Terms.tsx b/resources/js/pages/FrontPages/Terms.tsx index 72b6d61..6853912 100644 --- a/resources/js/pages/FrontPages/Terms.tsx +++ b/resources/js/pages/FrontPages/Terms.tsx @@ -1,7 +1,6 @@ -import React from 'react'; -import { Head } from '@inertiajs/react'; -import AuthUser from '@/modules/auth/auth-user'; import Footer from '@/pages/home/partials/Footer.jsx'; +import { Head } from '@inertiajs/react'; +import React from 'react'; interface TermsProps { content: string; @@ -12,22 +11,19 @@ const Terms: React.FC = ({ content, title }) => { return (
- +
-
-
-
+
+
+
- +
- + {/* */}
); }; -export default Terms; \ No newline at end of file +export default Terms; diff --git a/resources/js/pages/home/home.tsx b/resources/js/pages/home/home.tsx index d4f4668..1cedc64 100644 --- a/resources/js/pages/home/home.tsx +++ b/resources/js/pages/home/home.tsx @@ -1,4 +1,3 @@ -import AuthUser from '@/modules/auth/auth-user'; import { useEffect, useState } from 'react'; import FAQDiscord from './partials/FAQDiscord.jsx'; import Features from './partials/Features.jsx'; @@ -36,7 +35,7 @@ const Home = ({ faqData }) => {
); }; diff --git a/resources/js/stores/UserStore.js b/resources/js/stores/UserStore.js index 4d82b4f..99a486f 100644 --- a/resources/js/stores/UserStore.js +++ b/resources/js/stores/UserStore.js @@ -108,6 +108,46 @@ const useUserStore = create( console.error('Error fetching:', error); } }, + + basicExportRequest: async () => { + try { + const response = await axiosInstance.post(route('api.basic_export.request')); + + if (response?.data?.success?.message) { + toast.success(response.data.success.message); + } + + if (response?.data?.error?.message) { + toast.error(response.data.error.message); + } + + return response.data; + } catch (error) { + console.error(route('api.basic_export.request')); + console.error('Error fetching:', error); + } + }, + + basicExportComplete: async (exportToken) => { + try { + const response = await axiosInstance.post(route('api.basic_export.complete'), { + export_token: exportToken, + }); + + if (response?.data?.success?.message) { + toast.success(response.data.success.message); + } + + if (response?.data?.error?.message) { + toast.error(response.data.error.message); + } + + return response.data; + } catch (error) { + console.error(route('api.basic_export.complete')); + console.error('Error fetching:', error); + } + }, })), { name: 'UserStore', diff --git a/resources/js/ziggy.js b/resources/js/ziggy.js index 97beff2..54dc3ee 100644 --- a/resources/js/ziggy.js +++ b/resources/js/ziggy.js @@ -1,4 +1,4 @@ -const Ziggy = {"url":"https:\/\/memefa.st","port":null,"defaults":{},"routes":{"cashier.payment":{"uri":"stripe\/payment\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"cashier.webhook":{"uri":"stripe\/webhook","methods":["POST"]},"horizon.stats.index":{"uri":"horizon\/api\/stats","methods":["GET","HEAD"]},"horizon.workload.index":{"uri":"horizon\/api\/workload","methods":["GET","HEAD"]},"horizon.masters.index":{"uri":"horizon\/api\/masters","methods":["GET","HEAD"]},"horizon.monitoring.index":{"uri":"horizon\/api\/monitoring","methods":["GET","HEAD"]},"horizon.monitoring.store":{"uri":"horizon\/api\/monitoring","methods":["POST"]},"horizon.monitoring-tag.paginate":{"uri":"horizon\/api\/monitoring\/{tag}","methods":["GET","HEAD"],"parameters":["tag"]},"horizon.monitoring-tag.destroy":{"uri":"horizon\/api\/monitoring\/{tag}","methods":["DELETE"],"wheres":{"tag":".*"},"parameters":["tag"]},"horizon.jobs-metrics.index":{"uri":"horizon\/api\/metrics\/jobs","methods":["GET","HEAD"]},"horizon.jobs-metrics.show":{"uri":"horizon\/api\/metrics\/jobs\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.queues-metrics.index":{"uri":"horizon\/api\/metrics\/queues","methods":["GET","HEAD"]},"horizon.queues-metrics.show":{"uri":"horizon\/api\/metrics\/queues\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.jobs-batches.index":{"uri":"horizon\/api\/batches","methods":["GET","HEAD"]},"horizon.jobs-batches.show":{"uri":"horizon\/api\/batches\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.jobs-batches.retry":{"uri":"horizon\/api\/batches\/retry\/{id}","methods":["POST"],"parameters":["id"]},"horizon.pending-jobs.index":{"uri":"horizon\/api\/jobs\/pending","methods":["GET","HEAD"]},"horizon.completed-jobs.index":{"uri":"horizon\/api\/jobs\/completed","methods":["GET","HEAD"]},"horizon.silenced-jobs.index":{"uri":"horizon\/api\/jobs\/silenced","methods":["GET","HEAD"]},"horizon.failed-jobs.index":{"uri":"horizon\/api\/jobs\/failed","methods":["GET","HEAD"]},"horizon.failed-jobs.show":{"uri":"horizon\/api\/jobs\/failed\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.retry-jobs.show":{"uri":"horizon\/api\/jobs\/retry\/{id}","methods":["POST"],"parameters":["id"]},"horizon.jobs.show":{"uri":"horizon\/api\/jobs\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.index":{"uri":"horizon\/{view?}","methods":["GET","HEAD"],"wheres":{"view":"(.*)"},"parameters":["view"]},"sanctum.csrf-cookie":{"uri":"sanctum\/csrf-cookie","methods":["GET","HEAD"]},"api.pricing_page":{"uri":"api\/pricing","methods":["POST"]},"api.ai_hints":{"uri":"api\/ai-hints","methods":["POST"]},"api.user":{"uri":"api\/user","methods":["POST"]},"api.user.subscribe":{"uri":"api\/user\/subscribe","methods":["POST"]},"api.user.purchase":{"uri":"api\/user\/purchase","methods":["POST"]},"api.user.billing_portal":{"uri":"api\/user\/billing-portal","methods":["POST"]},"api.user.premium_export.request":{"uri":"api\/user\/premium-export\/request","methods":["POST"]},"api.user.premium_export.complete":{"uri":"api\/user\/premium-export\/complete","methods":["POST"]},"api.user.generate_meme":{"uri":"api\/user\/generate_meme","methods":["POST"]},"api.user.check_meme_job_status":{"uri":"api\/user\/generate_meme\/status","methods":["POST"]},"api.user.get_active_job":{"uri":"api\/user\/generate_meme\/active","methods":["POST"]},"api.user.get_meme_history":{"uri":"api\/user\/generate_meme\/history","methods":["POST"]},"api.app.init":{"uri":"api\/app\/init","methods":["POST"]},"api.app.memes":{"uri":"api\/app\/memes","methods":["POST"]},"api.app.search.memes":{"uri":"api\/app\/search\/memes","methods":["POST"]},"api.app.background":{"uri":"api\/app\/background","methods":["POST"]},"api.app.search.background":{"uri":"api\/app\/search\/background","methods":["POST"]},"auth.google.redirect":{"uri":"auth\/google\/redirect","methods":["GET","HEAD"]},"auth.google.callback":{"uri":"auth\/google\/callback","methods":["GET","HEAD"]},"dashboard":{"uri":"dashboard","methods":["GET","HEAD"]},"subscribe.success":{"uri":"subscribe\/success","methods":["GET","HEAD"]},"subscribe.cancelled":{"uri":"subscribe\/cancelled","methods":["GET","HEAD"]},"purchase.success":{"uri":"purchase\/success","methods":["GET","HEAD"]},"purchase.cancelled":{"uri":"purchase\/cancelled","methods":["GET","HEAD"]},"admin.dashboard":{"uri":"admin","methods":["GET","HEAD"]},"admin.background-generation":{"uri":"admin\/background-generation","methods":["GET","HEAD"]},"admin.background-generation.generate":{"uri":"admin\/background-generation\/generate","methods":["POST"]},"admin.background-generation.save":{"uri":"admin\/background-generation\/save","methods":["POST"]},"admin.background-generation.delete":{"uri":"admin\/background-generation\/delete\/{id}","methods":["POST"],"parameters":["id"]},"profile.edit":{"uri":"settings\/profile","methods":["GET","HEAD"]},"profile.update":{"uri":"settings\/profile","methods":["PATCH"]},"profile.destroy":{"uri":"settings\/profile","methods":["DELETE"]},"password.edit":{"uri":"settings\/password","methods":["GET","HEAD"]},"password.update":{"uri":"settings\/password","methods":["PUT"]},"appearance":{"uri":"settings\/appearance","methods":["GET","HEAD"]},"register":{"uri":"register","methods":["GET","HEAD"]},"login":{"uri":"login","methods":["GET","HEAD"]},"password.request":{"uri":"forgot-password","methods":["GET","HEAD"]},"password.email":{"uri":"forgot-password","methods":["POST"]},"password.reset":{"uri":"reset-password\/{token}","methods":["GET","HEAD"],"parameters":["token"]},"password.store":{"uri":"reset-password","methods":["POST"]},"verification.notice":{"uri":"verify-email","methods":["GET","HEAD"]},"verification.verify":{"uri":"verify-email\/{id}\/{hash}","methods":["GET","HEAD"],"parameters":["id","hash"]},"verification.send":{"uri":"email\/verification-notification","methods":["POST"]},"password.confirm":{"uri":"confirm-password","methods":["GET","HEAD"]},"logout":{"uri":"logout","methods":["POST"]},"home":{"uri":"\/","methods":["GET","HEAD"]},"privacy":{"uri":"privacy","methods":["GET","HEAD"]},"terms":{"uri":"terms","methods":["GET","HEAD"]},"admin.duplicates":{"uri":"duplicates","methods":["GET","HEAD"]},"admin.duplicates.scan":{"uri":"duplicates\/scan","methods":["POST"]},"admin.duplicates.delete":{"uri":"duplicates\/delete","methods":["POST"]},"admin.duplicates.regenerate-hash":{"uri":"duplicates\/regenerate-hash","methods":["POST"]},"storage.local":{"uri":"storage\/{path}","methods":["GET","HEAD"],"wheres":{"path":".*"},"parameters":["path"]}}}; +const Ziggy = {"url":"https:\/\/memefast.test","port":null,"defaults":{},"routes":{"cashier.payment":{"uri":"stripe\/payment\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"cashier.webhook":{"uri":"stripe\/webhook","methods":["POST"]},"horizon.stats.index":{"uri":"horizon\/api\/stats","methods":["GET","HEAD"]},"horizon.workload.index":{"uri":"horizon\/api\/workload","methods":["GET","HEAD"]},"horizon.masters.index":{"uri":"horizon\/api\/masters","methods":["GET","HEAD"]},"horizon.monitoring.index":{"uri":"horizon\/api\/monitoring","methods":["GET","HEAD"]},"horizon.monitoring.store":{"uri":"horizon\/api\/monitoring","methods":["POST"]},"horizon.monitoring-tag.paginate":{"uri":"horizon\/api\/monitoring\/{tag}","methods":["GET","HEAD"],"parameters":["tag"]},"horizon.monitoring-tag.destroy":{"uri":"horizon\/api\/monitoring\/{tag}","methods":["DELETE"],"wheres":{"tag":".*"},"parameters":["tag"]},"horizon.jobs-metrics.index":{"uri":"horizon\/api\/metrics\/jobs","methods":["GET","HEAD"]},"horizon.jobs-metrics.show":{"uri":"horizon\/api\/metrics\/jobs\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.queues-metrics.index":{"uri":"horizon\/api\/metrics\/queues","methods":["GET","HEAD"]},"horizon.queues-metrics.show":{"uri":"horizon\/api\/metrics\/queues\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.jobs-batches.index":{"uri":"horizon\/api\/batches","methods":["GET","HEAD"]},"horizon.jobs-batches.show":{"uri":"horizon\/api\/batches\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.jobs-batches.retry":{"uri":"horizon\/api\/batches\/retry\/{id}","methods":["POST"],"parameters":["id"]},"horizon.pending-jobs.index":{"uri":"horizon\/api\/jobs\/pending","methods":["GET","HEAD"]},"horizon.completed-jobs.index":{"uri":"horizon\/api\/jobs\/completed","methods":["GET","HEAD"]},"horizon.silenced-jobs.index":{"uri":"horizon\/api\/jobs\/silenced","methods":["GET","HEAD"]},"horizon.failed-jobs.index":{"uri":"horizon\/api\/jobs\/failed","methods":["GET","HEAD"]},"horizon.failed-jobs.show":{"uri":"horizon\/api\/jobs\/failed\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.retry-jobs.show":{"uri":"horizon\/api\/jobs\/retry\/{id}","methods":["POST"],"parameters":["id"]},"horizon.jobs.show":{"uri":"horizon\/api\/jobs\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.index":{"uri":"horizon\/{view?}","methods":["GET","HEAD"],"wheres":{"view":"(.*)"},"parameters":["view"]},"sanctum.csrf-cookie":{"uri":"sanctum\/csrf-cookie","methods":["GET","HEAD"]},"api.pricing_page":{"uri":"api\/pricing","methods":["POST"]},"api.ai_hints":{"uri":"api\/ai-hints","methods":["POST"]},"api.basic_export.request":{"uri":"api\/basic-export\/request","methods":["POST"]},"api.basic_export.complete":{"uri":"api\/basic-export\/complete","methods":["POST"]},"api.user":{"uri":"api\/user","methods":["POST"]},"api.user.subscribe":{"uri":"api\/user\/subscribe","methods":["POST"]},"api.user.purchase":{"uri":"api\/user\/purchase","methods":["POST"]},"api.user.billing_portal":{"uri":"api\/user\/billing-portal","methods":["POST"]},"api.user.premium_export.request":{"uri":"api\/user\/premium-export\/request","methods":["POST"]},"api.user.premium_export.complete":{"uri":"api\/user\/premium-export\/complete","methods":["POST"]},"api.user.generate_meme":{"uri":"api\/user\/generate_meme","methods":["POST"]},"api.user.check_meme_job_status":{"uri":"api\/user\/generate_meme\/status","methods":["POST"]},"api.user.get_active_job":{"uri":"api\/user\/generate_meme\/active","methods":["POST"]},"api.user.get_meme_history":{"uri":"api\/user\/generate_meme\/history","methods":["POST"]},"api.app.init":{"uri":"api\/app\/init","methods":["POST"]},"api.app.memes":{"uri":"api\/app\/memes","methods":["POST"]},"api.app.search.memes":{"uri":"api\/app\/search\/memes","methods":["POST"]},"api.app.background":{"uri":"api\/app\/background","methods":["POST"]},"api.app.search.background":{"uri":"api\/app\/search\/background","methods":["POST"]},"auth.google.redirect":{"uri":"auth\/google\/redirect","methods":["GET","HEAD"]},"auth.google.callback":{"uri":"auth\/google\/callback","methods":["GET","HEAD"]},"dashboard":{"uri":"dashboard","methods":["GET","HEAD"]},"subscribe.success":{"uri":"subscribe\/success","methods":["GET","HEAD"]},"subscribe.cancelled":{"uri":"subscribe\/cancelled","methods":["GET","HEAD"]},"purchase.success":{"uri":"purchase\/success","methods":["GET","HEAD"]},"purchase.cancelled":{"uri":"purchase\/cancelled","methods":["GET","HEAD"]},"admin.dashboard":{"uri":"admin","methods":["GET","HEAD"]},"admin.background-generation":{"uri":"admin\/background-generation","methods":["GET","HEAD"]},"admin.background-generation.generate":{"uri":"admin\/background-generation\/generate","methods":["POST"]},"admin.background-generation.save":{"uri":"admin\/background-generation\/save","methods":["POST"]},"admin.background-generation.delete":{"uri":"admin\/background-generation\/delete\/{id}","methods":["POST"],"parameters":["id"]},"profile.edit":{"uri":"settings\/profile","methods":["GET","HEAD"]},"profile.update":{"uri":"settings\/profile","methods":["PATCH"]},"profile.destroy":{"uri":"settings\/profile","methods":["DELETE"]},"password.edit":{"uri":"settings\/password","methods":["GET","HEAD"]},"password.update":{"uri":"settings\/password","methods":["PUT"]},"appearance":{"uri":"settings\/appearance","methods":["GET","HEAD"]},"register":{"uri":"register","methods":["GET","HEAD"]},"login":{"uri":"login","methods":["GET","HEAD"]},"password.request":{"uri":"forgot-password","methods":["GET","HEAD"]},"password.email":{"uri":"forgot-password","methods":["POST"]},"password.reset":{"uri":"reset-password\/{token}","methods":["GET","HEAD"],"parameters":["token"]},"password.store":{"uri":"reset-password","methods":["POST"]},"verification.notice":{"uri":"verify-email","methods":["GET","HEAD"]},"verification.verify":{"uri":"verify-email\/{id}\/{hash}","methods":["GET","HEAD"],"parameters":["id","hash"]},"verification.send":{"uri":"email\/verification-notification","methods":["POST"]},"password.confirm":{"uri":"confirm-password","methods":["GET","HEAD"]},"logout":{"uri":"logout","methods":["POST"]},"home":{"uri":"\/","methods":["GET","HEAD"]},"privacy":{"uri":"privacy","methods":["GET","HEAD"]},"terms":{"uri":"terms","methods":["GET","HEAD"]},"admin.duplicates":{"uri":"duplicates","methods":["GET","HEAD"]},"admin.duplicates.scan":{"uri":"duplicates\/scan","methods":["POST"]},"admin.duplicates.delete":{"uri":"duplicates\/delete","methods":["POST"]},"admin.duplicates.regenerate-hash":{"uri":"duplicates\/regenerate-hash","methods":["POST"]},"storage.local":{"uri":"storage\/{path}","methods":["GET","HEAD"],"wheres":{"path":".*"},"parameters":["path"]}}}; if (typeof window !== 'undefined' && typeof window.Ziggy !== 'undefined') { Object.assign(Ziggy.routes, window.Ziggy.routes); } diff --git a/routes/api.php b/routes/api.php index 8d7b742..056876d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -17,6 +17,12 @@ Route::post('/ai-hints', [UserAIController::class, 'aiHints'])->name('api.ai_hints'); +// Basic export routes (no authentication required) +Route::group(['prefix' => 'basic-export'], function () { + Route::post('/request', [UserExportController::class, 'basicExportRequest'])->name('api.basic_export.request'); + Route::post('/complete', [UserExportController::class, 'basicExportComplete'])->name('api.basic_export.complete'); +}); + Route::middleware('auth:sanctum')->group(function () { Route::group(['prefix' => 'user'], function () { @@ -31,9 +37,10 @@ Route::post('/logout', [SanctumAuthController::class, 'logout']); - Route::post('/premium-export/request', [UserExportController::class, 'premiumExportRequest'])->name('api.user.premium_export.request'); - - Route::post('/premium-export/complete', [UserExportController::class, 'premiumExportComplete'])->name('api.user.premium_export.complete'); + Route::group(['prefix' => 'premium-export'], function () { + Route::post('/request', [UserExportController::class, 'premiumExportRequest'])->name('api.user.premium_export.request'); + Route::post('/complete', [UserExportController::class, 'premiumExportComplete'])->name('api.user.premium_export.complete'); + }); Route::group(['prefix' => 'generate_meme'], function () { Route::post('/', [UserAIController::class, 'generateMeme'])->name('api.user.generate_meme');