Save workspace changes
This commit is contained in:
9
.deploy/artwork-evolution-release/config/antispam.php
Normal file
9
.deploy/artwork-evolution-release/config/antispam.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'register' => [
|
||||
'ip_per_minute' => (int) env('REGISTER_IP_PER_MINUTE', 20),
|
||||
'email_per_minute' => (int) env('REGISTER_EMAIL_PER_MINUTE', 6),
|
||||
'resend_cooldown_seconds' => (int) env('REGISTER_RESEND_COOLDOWN_SECONDS', 60),
|
||||
],
|
||||
];
|
||||
126
.deploy/artwork-evolution-release/config/app.php
Normal file
126
.deploy/artwork-evolution-release/config/app.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the name of your application, which will be used when the
|
||||
| framework needs to place the application's name in a notification or
|
||||
| other UI elements where an application name needs to be displayed.
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the "environment" your application is currently
|
||||
| running in. This may determine how you prefer to configure various
|
||||
| services the application utilizes. Set this in your ".env" file.
|
||||
|
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| the application so that it's available within Artisan commands.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. The timezone
|
||||
| is set to "UTC" by default as it is suitable for most use cases.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by Laravel's translation / localization methods. This option can be
|
||||
| set to any locale for which you plan to have translation strings.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => env('APP_LOCALE', 'en'),
|
||||
|
||||
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
|
||||
|
||||
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is utilized by Laravel's encryption services and should be set
|
||||
| to a random, 32 character string to ensure that all encrypted values
|
||||
| are secure. You should do this prior to deploying the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
'key' => env('APP_KEY'),
|
||||
|
||||
'previous_keys' => [
|
||||
...array_filter(
|
||||
explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
|
||||
),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maintenance Mode Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options determine the driver used to determine and
|
||||
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
||||
| allow maintenance mode to be controlled across multiple machines.
|
||||
|
|
||||
| Supported drivers: "file", "cache"
|
||||
|
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
||||
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||
],
|
||||
|
||||
];
|
||||
15
.deploy/artwork-evolution-release/config/artwork_medals.php
Normal file
15
.deploy/artwork-evolution-release/config/artwork_medals.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enabled' => env('ARTWORK_MEDALS_ENABLED', true),
|
||||
|
||||
'weights' => [
|
||||
'gold' => 5,
|
||||
'silver' => 3,
|
||||
'bronze' => 1,
|
||||
],
|
||||
|
||||
'require_verified_email' => env('ARTWORK_MEDALS_REQUIRE_VERIFIED_EMAIL', true),
|
||||
'minimum_account_age_hours' => (int) env('ARTWORK_MEDALS_MINIMUM_ACCOUNT_AGE_HOURS', 24),
|
||||
'rate_limit_per_minute' => (int) env('ARTWORK_MEDALS_RATE_LIMIT_PER_MINUTE', 10),
|
||||
];
|
||||
126
.deploy/artwork-evolution-release/config/auth.php
Normal file
126
.deploy/artwork-evolution-release/config/auth.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default authentication "guard" and password
|
||||
| reset "broker" for your application. You may change these values
|
||||
| as required, but they're a perfect start for most applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => env('AUTH_GUARD', 'web'),
|
||||
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, you may define every authentication guard for your application.
|
||||
| Of course, a great default configuration has been defined for you
|
||||
| which utilizes session storage plus the Eloquent user provider.
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| Supported: "session"
|
||||
|
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
// ControlPanel guard used by the ControlPanel package
|
||||
'controlpanel' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'controlpanel_users',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| If you have multiple user tables or models you may configure multiple
|
||||
| providers to represent the model / table. These providers may then
|
||||
| be assigned to any extra authentication guards you have defined.
|
||||
|
|
||||
| Supported: "database", "eloquent"
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||
],
|
||||
|
||||
// Provider for ControlPanel users
|
||||
'controlpanel_users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => Klevze\ControlPanel\Models\Auth\User::class,
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resetting Passwords
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options specify the behavior of Laravel's password
|
||||
| reset functionality, including the table utilized for token storage
|
||||
| and the user provider that is invoked to actually retrieve users.
|
||||
|
|
||||
| The expiry time is the number of minutes that each reset token will be
|
||||
| considered valid. This security feature keeps tokens short-lived so
|
||||
| they have less time to be guessed. You may change this as needed.
|
||||
|
|
||||
| The throttle setting is the number of seconds a user must wait before
|
||||
| generating more password reset tokens. This prevents the user from
|
||||
| quickly generating a very large amount of password reset tokens.
|
||||
|
|
||||
*/
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Confirmation Timeout
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define the number of seconds before a password confirmation
|
||||
| window expires and users are asked to re-enter their password via the
|
||||
| confirmation screen. By default, the timeout lasts for three hours.
|
||||
|
|
||||
*/
|
||||
|
||||
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
||||
|
||||
];
|
||||
7
.deploy/artwork-evolution-release/config/avatars.php
Normal file
7
.deploy/artwork-evolution-release/config/avatars.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'disk' => env('AVATAR_DISK', 's3'),
|
||||
'sizes' => [32, 64, 128, 256, 512],
|
||||
'quality' => (int) env('AVATAR_WEBP_QUALITY', 85),
|
||||
];
|
||||
82
.deploy/artwork-evolution-release/config/broadcasting.php
Normal file
82
.deploy/artwork-evolution-release/config/broadcasting.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Broadcaster
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default broadcaster that will be used by the
|
||||
| framework when an event needs to be broadcast. You may set this to
|
||||
| any of the connections defined in the "connections" array below.
|
||||
|
|
||||
| Supported: "reverb", "pusher", "ably", "redis", "log", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('BROADCAST_CONNECTION', 'null'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Broadcast Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the broadcast connections that will be used
|
||||
| to broadcast events to other systems or over WebSockets. Samples of
|
||||
| each available type of connection are provided inside this array.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'reverb' => [
|
||||
'driver' => 'reverb',
|
||||
'key' => env('REVERB_APP_KEY'),
|
||||
'secret' => env('REVERB_APP_SECRET'),
|
||||
'app_id' => env('REVERB_APP_ID'),
|
||||
'options' => [
|
||||
'host' => env('REVERB_HOST'),
|
||||
'port' => env('REVERB_PORT', 443),
|
||||
'scheme' => env('REVERB_SCHEME', 'https'),
|
||||
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
|
||||
],
|
||||
'client_options' => [
|
||||
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
|
||||
],
|
||||
],
|
||||
|
||||
'pusher' => [
|
||||
'driver' => 'pusher',
|
||||
'key' => env('PUSHER_APP_KEY'),
|
||||
'secret' => env('PUSHER_APP_SECRET'),
|
||||
'app_id' => env('PUSHER_APP_ID'),
|
||||
'options' => [
|
||||
'cluster' => env('PUSHER_APP_CLUSTER'),
|
||||
'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
|
||||
'port' => env('PUSHER_PORT', 443),
|
||||
'scheme' => env('PUSHER_SCHEME', 'https'),
|
||||
'encrypted' => true,
|
||||
'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
|
||||
],
|
||||
'client_options' => [
|
||||
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
|
||||
],
|
||||
],
|
||||
|
||||
'ably' => [
|
||||
'driver' => 'ably',
|
||||
'key' => env('ABLY_KEY'),
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'driver' => 'log',
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'null',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
126
.deploy/artwork-evolution-release/config/cache.php
Normal file
126
.deploy/artwork-evolution-release/config/cache.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default cache store that will be used by the
|
||||
| framework. This connection is utilized if another isn't explicitly
|
||||
| specified when running a cache operation inside the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_STORE', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the cache "stores" for your application as
|
||||
| well as their drivers. You may even define multiple stores for the
|
||||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
| Supported drivers: "array", "database", "file", "memcached",
|
||||
| "redis", "dynamodb", "octane",
|
||||
| "failover", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
'serialize' => false,
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_CACHE_CONNECTION'),
|
||||
'table' => env('DB_CACHE_TABLE', 'cache'),
|
||||
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
|
||||
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
|
||||
],
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path('framework/cache/data'),
|
||||
'lock_path' => storage_path('framework/cache/data'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||
'sasl' => [
|
||||
env('MEMCACHED_USERNAME'),
|
||||
env('MEMCACHED_PASSWORD'),
|
||||
],
|
||||
'options' => [
|
||||
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||
],
|
||||
'servers' => [
|
||||
[
|
||||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||
'port' => env('MEMCACHED_PORT', 11211),
|
||||
'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
|
||||
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
|
||||
],
|
||||
|
||||
'homepage' => [
|
||||
'driver' => 'failover',
|
||||
'stores' => [
|
||||
'redis',
|
||||
'database',
|
||||
'array',
|
||||
],
|
||||
],
|
||||
|
||||
'dynamodb' => [
|
||||
'driver' => 'dynamodb',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||||
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||||
],
|
||||
|
||||
'octane' => [
|
||||
'driver' => 'octane',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'driver' => 'failover',
|
||||
'stores' => [
|
||||
'database',
|
||||
'array',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
|
||||
| stores, there might be other applications using the same cache. For
|
||||
| that reason, you may prefix every cache key to avoid collisions.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'),
|
||||
|
||||
];
|
||||
22
.deploy/artwork-evolution-release/config/cdn.php
Normal file
22
.deploy/artwork-evolution-release/config/cdn.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'files_url' => env('FILES_CDN_URL', 'https://cdn.skinbase.org'),
|
||||
'avatar_url' => env('AVATAR_CDN_URL', 'https://cdn.skinbase.org'),
|
||||
|
||||
/**
|
||||
* Optional CDN purge webhook URL.
|
||||
* When set, the artwork versioning system will POST { paths: [...] }
|
||||
* after every file replacement to bust stale thumbnails.
|
||||
*
|
||||
* Example: https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache
|
||||
*/
|
||||
'purge_url' => env('CDN_PURGE_URL', null),
|
||||
|
||||
'cloudflare' => [
|
||||
'zone_id' => env('CLOUDFLARE_ZONE_ID', null),
|
||||
'api_token' => env('CLOUDFLARE_API_TOKEN', null),
|
||||
],
|
||||
];
|
||||
155
.deploy/artwork-evolution-release/config/collections.php
Normal file
155
.deploy/artwork-evolution-release/config/collections.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'featured_limit' => 3,
|
||||
'invites' => [
|
||||
'expires_after_days' => (int) env('COLLECTIONS_INVITES_EXPIRE_AFTER_DAYS', 7),
|
||||
],
|
||||
'submissions' => [
|
||||
'max_per_hour' => (int) env('COLLECTIONS_SUBMISSIONS_MAX_PER_HOUR', 8),
|
||||
'duplicate_cooldown_minutes' => (int) env('COLLECTIONS_SUBMISSIONS_DUPLICATE_COOLDOWN_MINUTES', 15),
|
||||
],
|
||||
'editorial' => [
|
||||
'system_owner_username' => env('COLLECTIONS_EDITORIAL_SYSTEM_OWNER_USERNAME'),
|
||||
'system_owner_label' => env('COLLECTIONS_EDITORIAL_SYSTEM_OWNER_LABEL', 'Skinbase Editorial'),
|
||||
],
|
||||
'smart_rules' => [
|
||||
'max_rules' => 8,
|
||||
'preview_limit' => 12,
|
||||
'allowed_match' => ['all', 'any'],
|
||||
'allowed_sort' => ['newest', 'oldest', 'popular'],
|
||||
'allowed_fields' => ['tags', 'category', 'subcategory', 'medium', 'style', 'color', 'ai_tag', 'created_at', 'is_featured', 'is_mature'],
|
||||
'style_terms' => [
|
||||
'abstract',
|
||||
'anime',
|
||||
'cartoon',
|
||||
'cel shaded',
|
||||
'cinematic',
|
||||
'comic style',
|
||||
'concept art',
|
||||
'digital art',
|
||||
'digital painting',
|
||||
'fantasy art',
|
||||
'illustration',
|
||||
'low poly',
|
||||
'matte painting',
|
||||
'minimalist',
|
||||
'oil painting',
|
||||
'painterly',
|
||||
'photorealistic',
|
||||
'pixel art',
|
||||
'realism',
|
||||
'retro',
|
||||
'sci fi',
|
||||
'sketch',
|
||||
'surreal',
|
||||
'vector art',
|
||||
'watercolor',
|
||||
'3d render',
|
||||
],
|
||||
'color_terms' => [
|
||||
'black and white',
|
||||
'blue tones',
|
||||
'cool colors',
|
||||
'cyan tones',
|
||||
'earth tones',
|
||||
'gold tones',
|
||||
'green tones',
|
||||
'monochrome',
|
||||
'neon colors',
|
||||
'orange tones',
|
||||
'pastel colors',
|
||||
'pink tones',
|
||||
'purple tones',
|
||||
'red tones',
|
||||
'teal tones',
|
||||
'vibrant colors',
|
||||
'warm colors',
|
||||
'yellow tones',
|
||||
],
|
||||
],
|
||||
'discovery' => [
|
||||
'featured_limit' => 18,
|
||||
'featured_cache_seconds' => 120,
|
||||
],
|
||||
'recommendations' => [
|
||||
'related_limit' => 6,
|
||||
],
|
||||
'v5' => [
|
||||
'health' => [
|
||||
'stale_after_days' => (int) env('COLLECTIONS_V5_STALE_AFTER_DAYS', 90),
|
||||
'low_engagement_after_days' => (int) env('COLLECTIONS_V5_LOW_ENGAGEMENT_AFTER_DAYS', 14),
|
||||
],
|
||||
'queue' => [
|
||||
'name' => env('COLLECTIONS_V5_QUEUE', 'collections'),
|
||||
'health_batch_size' => (int) env('COLLECTIONS_V5_HEALTH_BATCH_SIZE', 40),
|
||||
'recommendation_batch_size' => (int) env('COLLECTIONS_V5_RECOMMENDATION_BATCH_SIZE', 40),
|
||||
'duplicate_batch_size' => (int) env('COLLECTIONS_V5_DUPLICATE_BATCH_SIZE', 30),
|
||||
'health_stale_after_hours' => (int) env('COLLECTIONS_V5_HEALTH_STALE_AFTER_HOURS', 24),
|
||||
'recommendation_stale_after_hours' => (int) env('COLLECTIONS_V5_RECOMMENDATION_STALE_AFTER_HOURS', 12),
|
||||
'duplicate_stale_after_hours' => (int) env('COLLECTIONS_V5_DUPLICATE_STALE_AFTER_HOURS', 24),
|
||||
],
|
||||
'search' => [
|
||||
'public_per_page' => (int) env('COLLECTIONS_V5_PUBLIC_SEARCH_PER_PAGE', 18),
|
||||
'owner_per_page' => (int) env('COLLECTIONS_V5_OWNER_SEARCH_PER_PAGE', 20),
|
||||
],
|
||||
],
|
||||
'ai' => [
|
||||
'enabled' => true,
|
||||
'max_candidate_artworks' => 36,
|
||||
],
|
||||
'layout_modules' => [
|
||||
'intro_block' => [
|
||||
'label' => 'Intro Block',
|
||||
'description' => 'Show the subtitle, curator summary, and smart context in the hero area.',
|
||||
'default_slot' => 'full',
|
||||
'slots' => ['full'],
|
||||
'locked' => false,
|
||||
],
|
||||
'featured_artworks' => [
|
||||
'label' => 'Featured Artworks Block',
|
||||
'description' => 'Spotlight a compact highlights strip ahead of the main artwork grid.',
|
||||
'default_slot' => 'main',
|
||||
'slots' => ['full', 'main'],
|
||||
],
|
||||
'artwork_grid' => [
|
||||
'label' => 'Artwork Grid',
|
||||
'description' => 'Primary artwork gallery for the collection.',
|
||||
'default_slot' => 'full',
|
||||
'slots' => ['full', 'main'],
|
||||
'locked' => true,
|
||||
],
|
||||
'editorial_note' => [
|
||||
'label' => 'Editorial Note',
|
||||
'description' => 'Premium context block for staff picks and campaign notes.',
|
||||
'default_slot' => 'full',
|
||||
'slots' => ['full', 'main', 'sidebar'],
|
||||
],
|
||||
'collaborators' => [
|
||||
'label' => 'Contributor Block',
|
||||
'description' => 'Show the curation team on the page.',
|
||||
'default_slot' => 'sidebar',
|
||||
'slots' => ['main', 'sidebar'],
|
||||
],
|
||||
'submissions' => [
|
||||
'label' => 'Submission CTA',
|
||||
'description' => 'Highlight community submission controls when enabled.',
|
||||
'default_slot' => 'sidebar',
|
||||
'slots' => ['main', 'sidebar'],
|
||||
],
|
||||
'discussion' => [
|
||||
'label' => 'Discussion Block',
|
||||
'description' => 'Render lightweight collection discussion.',
|
||||
'default_slot' => 'main',
|
||||
'slots' => ['main', 'sidebar'],
|
||||
],
|
||||
'related_collections' => [
|
||||
'label' => 'Related Collections',
|
||||
'description' => 'Surface collection recommendations.',
|
||||
'default_slot' => 'main',
|
||||
'slots' => ['main', 'sidebar'],
|
||||
],
|
||||
],
|
||||
];
|
||||
358
.deploy/artwork-evolution-release/config/content_moderation.php
Normal file
358
.deploy/artwork-evolution-release/config/content_moderation.php
Normal file
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Content Moderation Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'scanner_version' => '3.0',
|
||||
|
||||
'queue_threshold' => 30,
|
||||
|
||||
'rules' => [
|
||||
'enabled' => [
|
||||
\App\Services\Moderation\Rules\LinkPresenceRule::class,
|
||||
\App\Services\Moderation\Rules\DomainBlacklistRule::class,
|
||||
\App\Services\Moderation\Rules\SuspiciousKeywordRule::class,
|
||||
\App\Services\Moderation\Rules\RegexPatternRule::class,
|
||||
\App\Services\Moderation\Rules\UnicodeObfuscationRule::class,
|
||||
\App\Services\Moderation\Rules\RepeatedPhraseRule::class,
|
||||
\App\Services\Moderation\Rules\DuplicateCommentRule::class,
|
||||
\App\Services\Moderation\Rules\NearDuplicateCampaignRule::class,
|
||||
\App\Services\Moderation\Rules\KeywordStuffingRule::class,
|
||||
\App\Services\Moderation\Rules\ExcessivePunctuationRule::class,
|
||||
],
|
||||
],
|
||||
|
||||
'auto_hide' => [
|
||||
'enabled' => true,
|
||||
'threshold' => 95,
|
||||
'supported_types' => [
|
||||
'artwork_comment',
|
||||
'artwork_description',
|
||||
'artwork_title',
|
||||
],
|
||||
],
|
||||
|
||||
'future_content_types' => [
|
||||
'user_bio',
|
||||
'user_profile_link',
|
||||
'collection_title',
|
||||
'collection_description',
|
||||
'story_title',
|
||||
'story_content',
|
||||
'card_title',
|
||||
'card_text',
|
||||
],
|
||||
|
||||
'suggestions' => [
|
||||
'provider' => env('CONTENT_MODERATION_SUGGESTION_PROVIDER', 'heuristic'),
|
||||
],
|
||||
|
||||
'policies' => [
|
||||
'default' => [
|
||||
'queue_threshold' => 30,
|
||||
'auto_hide_threshold' => 95,
|
||||
'priority_bonus' => 0,
|
||||
'review_bucket' => 'standard',
|
||||
],
|
||||
'strict_seo_protection' => [
|
||||
'queue_threshold' => 22,
|
||||
'auto_hide_threshold' => 90,
|
||||
'priority_bonus' => 20,
|
||||
'review_bucket' => 'urgent',
|
||||
],
|
||||
'new_user_strict_mode' => [
|
||||
'queue_threshold' => 24,
|
||||
'auto_hide_threshold' => 92,
|
||||
'priority_bonus' => 14,
|
||||
'review_bucket' => 'high',
|
||||
],
|
||||
'trusted_user_relaxed_mode' => [
|
||||
'queue_threshold' => 38,
|
||||
'auto_hide_threshold' => 100,
|
||||
'priority_bonus' => -8,
|
||||
'review_bucket' => 'standard',
|
||||
],
|
||||
'comments_high_volume_antispam' => [
|
||||
'queue_threshold' => 20,
|
||||
'auto_hide_threshold' => 88,
|
||||
'priority_bonus' => 18,
|
||||
'review_bucket' => 'high',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Severity Thresholds
|
||||
|--------------------------------------------------------------------------
|
||||
| Score boundaries for severity levels.
|
||||
*/
|
||||
'severity_thresholds' => [
|
||||
'critical' => 90,
|
||||
'high' => 60,
|
||||
'medium' => 30,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Rule Weights
|
||||
|--------------------------------------------------------------------------
|
||||
| Score contribution per rule match.
|
||||
*/
|
||||
'weights' => [
|
||||
'blacklisted_domain' => 70,
|
||||
'suspicious_domain' => 40,
|
||||
'single_external_link' => 20,
|
||||
'multiple_links' => 40,
|
||||
'shortened_link' => 30,
|
||||
'suspicious_keyword' => 25,
|
||||
'high_risk_keyword' => 40,
|
||||
'unicode_obfuscation' => 30,
|
||||
'repeated_phrase' => 25,
|
||||
'duplicate_comment' => 35,
|
||||
'near_duplicate_campaign' => 30,
|
||||
'keyword_stuffing' => 20,
|
||||
'excessive_punctuation' => 15,
|
||||
'regex_pattern' => 30,
|
||||
],
|
||||
|
||||
'duplicate_detection' => [
|
||||
'near_duplicate_similarity' => 84,
|
||||
],
|
||||
|
||||
'user_risk' => [
|
||||
'high_modifier' => 18,
|
||||
'medium_modifier' => 10,
|
||||
'low_modifier' => 4,
|
||||
'trusted_modifier' => -6,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Blacklisted Domains
|
||||
|--------------------------------------------------------------------------
|
||||
| Domains that immediately score high. Patterns support * wildcard.
|
||||
*/
|
||||
'blacklisted_domains' => [
|
||||
'*.casino-*.com',
|
||||
'*.bet365*',
|
||||
'*.pokerstars*',
|
||||
'*.1xbet*',
|
||||
'*.parimatch*',
|
||||
'*.888casino*',
|
||||
'*.slotmachine*',
|
||||
'*.onlinecasino*',
|
||||
'*.buycheap*',
|
||||
'*.cheapviagra*',
|
||||
'*.pillsonline*',
|
||||
'*.pharma-*',
|
||||
'*.xn--*',
|
||||
'*.webcam-show*',
|
||||
'*.livecam*',
|
||||
'*.adult-*',
|
||||
'*.porn*',
|
||||
'*.xxx*',
|
||||
'*.cryptoairdrop*',
|
||||
'*.free-bitcoin*',
|
||||
'*.moonshot-token*',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Suspicious Domains
|
||||
|--------------------------------------------------------------------------
|
||||
| Domains that add moderate score.
|
||||
*/
|
||||
'suspicious_domains' => [
|
||||
'*.tk',
|
||||
'*.ml',
|
||||
'*.ga',
|
||||
'*.cf',
|
||||
'*.gq',
|
||||
'*.xyz',
|
||||
'*.top',
|
||||
'*.buzz',
|
||||
'*.club',
|
||||
'*.work',
|
||||
'*.click',
|
||||
'*.link',
|
||||
'*.info',
|
||||
'*.site',
|
||||
'*.online',
|
||||
'*.icu',
|
||||
'*.fun',
|
||||
'*.monster',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| URL Shortener Domains
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
'shortener_domains' => [
|
||||
'bit.ly',
|
||||
'tinyurl.com',
|
||||
't.co',
|
||||
'goo.gl',
|
||||
'ow.ly',
|
||||
'is.gd',
|
||||
'buff.ly',
|
||||
'adf.ly',
|
||||
'bl.ink',
|
||||
'shorte.st',
|
||||
'clck.ru',
|
||||
'cutt.ly',
|
||||
'rb.gy',
|
||||
'shorturl.at',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Allowed Domains
|
||||
|--------------------------------------------------------------------------
|
||||
| Domains that should not be flagged.
|
||||
*/
|
||||
'allowed_domains' => [
|
||||
'skinbase.org',
|
||||
'skinbase.si',
|
||||
'deviantart.com',
|
||||
'artstation.com',
|
||||
'behance.net',
|
||||
'dribbble.com',
|
||||
'pixiv.net',
|
||||
'instagram.com',
|
||||
'twitter.com',
|
||||
'x.com',
|
||||
'youtube.com',
|
||||
'youtu.be',
|
||||
'vimeo.com',
|
||||
'github.com',
|
||||
'wikipedia.org',
|
||||
'imgur.com',
|
||||
'flickr.com',
|
||||
'unsplash.com',
|
||||
'pinterest.com',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Suspicious Keywords
|
||||
|--------------------------------------------------------------------------
|
||||
| Phrases that indicate spam. grouped by risk level.
|
||||
*/
|
||||
'keywords' => [
|
||||
'high_risk' => [
|
||||
'buy followers',
|
||||
'buy likes',
|
||||
'buy subscribers',
|
||||
'free bitcoin',
|
||||
'crypto giveaway',
|
||||
'crypto airdrop',
|
||||
'double your bitcoin',
|
||||
'send btc',
|
||||
'send eth',
|
||||
'online casino',
|
||||
'best casino',
|
||||
'casino bonus',
|
||||
'free spins',
|
||||
'slot machine',
|
||||
'sports betting',
|
||||
'bet now',
|
||||
'viagra',
|
||||
'cialis',
|
||||
'pharmacy online',
|
||||
'buy cheap',
|
||||
'discount pills',
|
||||
'weight loss pills',
|
||||
'diet pills',
|
||||
'earn money fast',
|
||||
'make money online',
|
||||
'work from home opportunity',
|
||||
'investment opportunity',
|
||||
'guaranteed income',
|
||||
'adult content',
|
||||
'webcam show',
|
||||
'live cam',
|
||||
'hookup',
|
||||
'dating site',
|
||||
'meet singles',
|
||||
'sexy girls',
|
||||
'hot women',
|
||||
'malware',
|
||||
'hack account',
|
||||
'free robux',
|
||||
'free v-bucks',
|
||||
],
|
||||
'suspicious' => [
|
||||
'visit my site',
|
||||
'check my profile',
|
||||
'click here',
|
||||
'click the link',
|
||||
'follow me',
|
||||
'subscribe to my',
|
||||
'check out my website',
|
||||
'link in bio',
|
||||
'link in description',
|
||||
'free download',
|
||||
'limited time offer',
|
||||
'act now',
|
||||
'don\'t miss out',
|
||||
'exclusive deal',
|
||||
'best price',
|
||||
'cheap price',
|
||||
'seo service',
|
||||
'seo expert',
|
||||
'web development service',
|
||||
'marketing service',
|
||||
'backlink service',
|
||||
'guest post',
|
||||
'sponsored post',
|
||||
'promote your',
|
||||
'boost your',
|
||||
'increase traffic',
|
||||
'grow your followers',
|
||||
'dm for collab',
|
||||
'dm for business',
|
||||
'whatsapp me',
|
||||
'telegram me',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Keyword Stuffing Detection
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
'keyword_stuffing' => [
|
||||
'min_word_count' => 20,
|
||||
'max_unique_ratio' => 0.3, // if unique/total < this, likely stuffing
|
||||
'max_single_word_frequency' => 0.25, // single word > 25% of total
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Repeated Phrase Detection
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
'repeated_phrase' => [
|
||||
'min_phrase_length' => 4, // min words in phrase
|
||||
'min_repetitions' => 3, // min times phrase must appear
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Excessive Punctuation Detection
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
'excessive_punctuation' => [
|
||||
'max_exclamation_ratio' => 0.1, // ! per char ratio
|
||||
'max_question_ratio' => 0.1,
|
||||
'max_caps_ratio' => 0.7, // if > 70% uppercase
|
||||
'min_length' => 20,
|
||||
],
|
||||
|
||||
];
|
||||
65
.deploy/artwork-evolution-release/config/content_types.php
Normal file
65
.deploy/artwork-evolution-release/config/content_types.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'cache' => [
|
||||
'public_list_key' => 'content-types.public-list',
|
||||
'slug_map_key' => 'content-types.slug-map',
|
||||
'history_map_key' => 'content-types.slug-history-map',
|
||||
],
|
||||
|
||||
'virtual_types' => [
|
||||
'artworks' => [
|
||||
'name' => 'All Artworks',
|
||||
],
|
||||
],
|
||||
|
||||
'reserved_slugs' => [
|
||||
'news',
|
||||
'help',
|
||||
'groups',
|
||||
'creators',
|
||||
'cards',
|
||||
'search',
|
||||
'upload',
|
||||
'studio',
|
||||
'dashboard',
|
||||
'settings',
|
||||
'login',
|
||||
'register',
|
||||
'password',
|
||||
'rss',
|
||||
'sitemap',
|
||||
'robots',
|
||||
'pages',
|
||||
'tag',
|
||||
'tags',
|
||||
'categories',
|
||||
'stories',
|
||||
'blog',
|
||||
'art',
|
||||
'feed',
|
||||
'messages',
|
||||
'leaderboard',
|
||||
'following',
|
||||
'about',
|
||||
'contact',
|
||||
'faq',
|
||||
'staff',
|
||||
'members',
|
||||
'discover',
|
||||
'featured',
|
||||
'downloads',
|
||||
'comments',
|
||||
'collections',
|
||||
'community',
|
||||
'creator',
|
||||
'manage',
|
||||
'home',
|
||||
'cp',
|
||||
'admin',
|
||||
'forum',
|
||||
'legal',
|
||||
'bug-report',
|
||||
'rss-feeds',
|
||||
],
|
||||
];
|
||||
27
.deploy/artwork-evolution-release/config/controlpanel.php
Normal file
27
.deploy/artwork-evolution-release/config/controlpanel.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| cPad Control Panel Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file contains the configuration options for cPad Control Panel.
|
||||
| You can publish the full configuration using:
|
||||
| php artisan vendor:publish --provider="Klevze\ControlPanel\ServiceProvider"
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => env('CPAD_ENABLED', true),
|
||||
|
||||
'debug' => env('CPAD_DEBUG', false),
|
||||
|
||||
'route_prefix' => env('CPAD_ROUTE_PREFIX', 'admin'),
|
||||
|
||||
'middleware' => ['web', 'auth'],
|
||||
|
||||
'permissions' => [
|
||||
'enabled' => true,
|
||||
'super_admin_role' => 'super-admin',
|
||||
],
|
||||
];
|
||||
34
.deploy/artwork-evolution-release/config/cors.php
Normal file
34
.deploy/artwork-evolution-release/config/cors.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cross-Origin Resource Sharing (CORS) Configuration
|
||||
|
|
||||
| This file configures CORS for the application. Toggle CORS on/off using
|
||||
| the `CP_ENABLE_CORS` environment variable. When disabled the `paths`
|
||||
| array is empty and the CORS middleware will not apply to any routes.
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'paths' => env('CP_ENABLE_CORS', false)
|
||||
? [
|
||||
'api/*',
|
||||
'sanctum/csrf-cookie',
|
||||
]
|
||||
: [],
|
||||
|
||||
'allowed_methods' => ['*'],
|
||||
|
||||
'allowed_origins' => ['*'],
|
||||
|
||||
'allowed_origins_patterns' => [],
|
||||
|
||||
'allowed_headers' => ['*'],
|
||||
|
||||
'exposed_headers' => [],
|
||||
|
||||
'max_age' => 0,
|
||||
|
||||
'supports_credentials' => false,
|
||||
];
|
||||
5
.deploy/artwork-evolution-release/config/covers.php
Normal file
5
.deploy/artwork-evolution-release/config/covers.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'disk' => env('COVER_DISK', env('AVATAR_DISK', 's3')),
|
||||
];
|
||||
23
.deploy/artwork-evolution-release/config/cp.php
Normal file
23
.deploy/artwork-evolution-release/config/cp.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'logo' => '/admin/images/cp/logo.png',
|
||||
'footer' => '2020 © <a href="mailto:info@klevze.net">info@klevze.net</a>',
|
||||
'flag_folder' => '/admin/images/flags/languages/16/',
|
||||
'flag_folder_24' => '/admin/images/flags/languages/24/',
|
||||
'flag_folder_32' => '/admin/images/flags/languages/32/',
|
||||
'flag_folder_64' => '/admin/images/flags/languages/64/',
|
||||
'admin_path' => '/admin',
|
||||
'webroot' => '/cp',
|
||||
'theme' => 'adminlte',
|
||||
//'theme' => 'porto',
|
||||
|
||||
'login' => [
|
||||
'footer' => '2020 © klevze.net',
|
||||
'logo' => '/admin/images/cp/logo.png',
|
||||
],
|
||||
|
||||
'tinymce' => [
|
||||
'apikey' => 'xbqp7qz3idlwqmbessgwzlptb87ffxwphdgadio4dyp72sbw',
|
||||
],
|
||||
];
|
||||
14
.deploy/artwork-evolution-release/config/cpad.php
Normal file
14
.deploy/artwork-evolution-release/config/cpad.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
return array (
|
||||
'debug' => false,
|
||||
'cache_enabled' => true,
|
||||
'log_level' => 'warning',
|
||||
'features' =>
|
||||
array (
|
||||
0 => 'core',
|
||||
1 => 'security',
|
||||
),
|
||||
'security_level' => 'maximum',
|
||||
'backup_enabled' => true,
|
||||
);
|
||||
203
.deploy/artwork-evolution-release/config/database.php
Normal file
203
.deploy/artwork-evolution-release/config/database.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the database connections below you wish
|
||||
| to use as your default connection for database operations. This is
|
||||
| the connection which will be utilized unless another connection
|
||||
| is explicitly specified when you execute a query / statement.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'sqlite'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below are all of the database connections defined for your application.
|
||||
| An example configuration is provided for each database system which
|
||||
| is supported by Laravel. You're free to add / remove connections.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'url' => env('DB_URL'),
|
||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||
'prefix' => '',
|
||||
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||
'busy_timeout' => null,
|
||||
'journal_mode' => null,
|
||||
'synchronous' => null,
|
||||
'transaction_mode' => 'DEFERRED',
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'legacy' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('DB_LEGACY_URL', env('LEGACY_DB_URL')),
|
||||
'host' => env('DB_LEGACY_HOST', env('LEGACY_DB_HOST', env('DB_HOST', '127.0.0.1'))),
|
||||
'port' => env('DB_LEGACY_PORT', env('LEGACY_DB_PORT', env('DB_PORT', '3306'))),
|
||||
'database' => env('DB_LEGACY_DATABASE', env('LEGACY_DB_DATABASE', 'projekti_old_skinbase')),
|
||||
'username' => env('DB_LEGACY_USERNAME', env('LEGACY_DB_USERNAME', env('DB_USERNAME', 'root'))),
|
||||
'password' => env('DB_LEGACY_PASSWORD', env('LEGACY_DB_PASSWORD', env('DB_PASSWORD', ''))),
|
||||
'unix_socket' => env('LEGACY_DB_SOCKET', ''),
|
||||
'charset' => env('LEGACY_DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('LEGACY_DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => false, // legacy schema may rely on relaxed SQL modes
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('LEGACY_MYSQL_ATTR_SSL_CA', env('MYSQL_ATTR_SSL_CA')),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'mariadb' => [
|
||||
'driver' => 'mariadb',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'search_path' => 'public',
|
||||
'sslmode' => env('DB_SSLMODE', 'prefer'),
|
||||
],
|
||||
|
||||
'sqlsrv' => [
|
||||
'driver' => 'sqlsrv',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', '1433'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
|
||||
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This table keeps track of all the migrations that have already run for
|
||||
| your application. Using this information, we can determine which of
|
||||
| the migrations on disk haven't actually been run on the database.
|
||||
|
|
||||
*/
|
||||
|
||||
'migrations' => [
|
||||
'table' => 'migrations',
|
||||
'update_date_on_publish' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis is an open source, fast, and advanced key-value store that also
|
||||
| provides a richer body of commands than a typical key-value system
|
||||
| such as Memcached. You may define your connection settings here.
|
||||
|
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'client' => env('REDIS_CLIENT', 'phpredis'),
|
||||
|
||||
'options' => [
|
||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||
'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),
|
||||
'persistent' => env('REDIS_PERSISTENT', false),
|
||||
],
|
||||
|
||||
'default' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_CACHE_DB', '1'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
201
.deploy/artwork-evolution-release/config/discovery.php
Normal file
201
.deploy/artwork-evolution-release/config/discovery.php
Normal file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'queue' => env('DISCOVERY_QUEUE', env('RECOMMENDATIONS_QUEUE', env('VISION_QUEUE', 'default'))),
|
||||
|
||||
// Versioned from day one for safe future migrations/experiments.
|
||||
'profile_version' => env('DISCOVERY_PROFILE_VERSION', 'profile-v1'),
|
||||
'event_version' => env('DISCOVERY_EVENT_VERSION', 'event-v1'),
|
||||
'algo_version' => env('DISCOVERY_ALGO_VERSION', env('RECOMMENDATIONS_ALGO_VERSION', 'clip-cosine-v1')),
|
||||
'cache_version' => env('DISCOVERY_CACHE_VERSION', 'cache-v1'),
|
||||
|
||||
'decay' => [
|
||||
// Exponential half-life: score * 0.5 every N hours.
|
||||
'half_life_hours' => (float) env('DISCOVERY_DECAY_HALF_LIFE_HOURS', 72),
|
||||
],
|
||||
|
||||
// Baseline event contribution weights.
|
||||
'weights' => [
|
||||
'view' => (float) env('DISCOVERY_WEIGHT_VIEW', 1.0),
|
||||
'click' => (float) env('DISCOVERY_WEIGHT_CLICK', 2.0),
|
||||
'favorite' => (float) env('DISCOVERY_WEIGHT_FAVORITE', 4.0),
|
||||
'download' => (float) env('DISCOVERY_WEIGHT_DOWNLOAD', 3.0),
|
||||
'dwell' => (float) env('DISCOVERY_WEIGHT_DWELL', 1.5),
|
||||
'scroll' => (float) env('DISCOVERY_WEIGHT_SCROLL', 0.75),
|
||||
],
|
||||
|
||||
// Recommendation cache TTL in minutes (schema foundation only for now).
|
||||
'cache_ttl_minutes' => (int) env('DISCOVERY_CACHE_TTL_MINUTES', 60),
|
||||
|
||||
'v2' => [
|
||||
'enabled' => (bool) env('DISCOVERY_V2_ENABLED', false),
|
||||
'algo_version' => env('DISCOVERY_V2_ALGO_VERSION', 'clip-cosine-v2-adaptive'),
|
||||
'cache_version' => env('DISCOVERY_V2_CACHE_VERSION', 'cache-v2'),
|
||||
'cache_ttl_minutes' => (int) env('DISCOVERY_V2_CACHE_TTL_MINUTES', 15),
|
||||
'candidate_pool_max' => (int) env('DISCOVERY_V2_CANDIDATE_POOL_MAX', 300),
|
||||
'feed_pool_size' => (int) env('DISCOVERY_V2_FEED_POOL_SIZE', 240),
|
||||
'fresh_upload_hours' => (int) env('DISCOVERY_V2_FRESH_UPLOAD_HOURS', 72),
|
||||
'new_creator_days' => (int) env('DISCOVERY_V2_NEW_CREATOR_DAYS', 45),
|
||||
'max_per_creator' => (int) env('DISCOVERY_V2_MAX_PER_CREATOR', 3),
|
||||
'repetition_window' => (int) env('DISCOVERY_V2_REPETITION_WINDOW', 24),
|
||||
'rollout_percentage' => (int) env('DISCOVERY_V2_ROLLOUT_PERCENTAGE', 0),
|
||||
|
||||
'layers' => [
|
||||
'personalized' => (float) env('DISCOVERY_V2_LAYER_PERSONALIZED', 0.50),
|
||||
'social' => (float) env('DISCOVERY_V2_LAYER_SOCIAL', 0.20),
|
||||
'trending' => (float) env('DISCOVERY_V2_LAYER_TRENDING', 0.20),
|
||||
'exploration' => (float) env('DISCOVERY_V2_LAYER_EXPLORATION', 0.10),
|
||||
],
|
||||
|
||||
'session' => [
|
||||
'ttl_seconds' => (int) env('DISCOVERY_V2_SESSION_TTL_SECONDS', 14400),
|
||||
'max_items' => (int) env('DISCOVERY_V2_SESSION_MAX_ITEMS', 120),
|
||||
'half_life_hours' => (float) env('DISCOVERY_V2_SESSION_HALF_LIFE_HOURS', 8),
|
||||
'merge_multiplier' => (float) env('DISCOVERY_V2_SESSION_MERGE_MULTIPLIER', 1.35),
|
||||
'artwork_similarity_weight' => (float) env('DISCOVERY_V2_SESSION_ARTWORK_SIMILARITY_WEIGHT', 0.85),
|
||||
'event_weights' => [
|
||||
'view' => (float) env('DISCOVERY_V2_SESSION_WEIGHT_VIEW', 1.0),
|
||||
'click' => (float) env('DISCOVERY_V2_SESSION_WEIGHT_CLICK', 2.0),
|
||||
'favorite' => (float) env('DISCOVERY_V2_SESSION_WEIGHT_FAVORITE', 4.5),
|
||||
'download' => (float) env('DISCOVERY_V2_SESSION_WEIGHT_DOWNLOAD', 3.5),
|
||||
'dwell' => (float) env('DISCOVERY_V2_SESSION_WEIGHT_DWELL', 1.75),
|
||||
'scroll' => (float) env('DISCOVERY_V2_SESSION_WEIGHT_SCROLL', 0.75),
|
||||
],
|
||||
],
|
||||
|
||||
'weights' => [
|
||||
'base' => (float) env('DISCOVERY_V2_WEIGHT_BASE', 1.0),
|
||||
'session' => (float) env('DISCOVERY_V2_WEIGHT_SESSION', 1.4),
|
||||
'social' => (float) env('DISCOVERY_V2_WEIGHT_SOCIAL', 1.1),
|
||||
'trending' => (float) env('DISCOVERY_V2_WEIGHT_TRENDING', 0.95),
|
||||
'exploration' => (float) env('DISCOVERY_V2_WEIGHT_EXPLORATION', 0.7),
|
||||
'creator' => (float) env('DISCOVERY_V2_WEIGHT_CREATOR', 0.5),
|
||||
'repetition_penalty' => (float) env('DISCOVERY_V2_WEIGHT_REPETITION_PENALTY', 0.45),
|
||||
'negative_tag_penalty' => (float) env('DISCOVERY_V2_WEIGHT_NEGATIVE_TAG_PENALTY', 0.65),
|
||||
'followed_creator' => (float) env('DISCOVERY_V2_WEIGHT_FOLLOWED_CREATOR', 0.85),
|
||||
'followed_like' => (float) env('DISCOVERY_V2_WEIGHT_FOLLOWED_LIKE', 0.55),
|
||||
],
|
||||
|
||||
'trending' => [
|
||||
'period_weights' => [
|
||||
'1h' => (float) env('DISCOVERY_V2_TRENDING_WEIGHT_1H', 1.0),
|
||||
'24h' => (float) env('DISCOVERY_V2_TRENDING_WEIGHT_24H', 0.7),
|
||||
'7d' => (float) env('DISCOVERY_V2_TRENDING_WEIGHT_7D', 0.45),
|
||||
],
|
||||
'velocity_weights' => [
|
||||
'views' => (float) env('DISCOVERY_V2_TRENDING_VIEWS_WEIGHT', 1.0),
|
||||
'favorites' => (float) env('DISCOVERY_V2_TRENDING_FAVORITES_WEIGHT', 3.0),
|
||||
'comments' => (float) env('DISCOVERY_V2_TRENDING_COMMENTS_WEIGHT', 2.5),
|
||||
'shares' => (float) env('DISCOVERY_V2_TRENDING_SHARES_WEIGHT', 2.0),
|
||||
],
|
||||
'age_decay_per_hour' => (float) env('DISCOVERY_V2_TRENDING_AGE_DECAY_PER_HOUR', 0.08),
|
||||
],
|
||||
|
||||
'exploration' => [
|
||||
'creator_bonus' => (float) env('DISCOVERY_V2_EXPLORATION_CREATOR_BONUS', 0.6),
|
||||
'tag_bonus' => (float) env('DISCOVERY_V2_EXPLORATION_TAG_BONUS', 0.45),
|
||||
'freshness_bonus' => (float) env('DISCOVERY_V2_EXPLORATION_FRESHNESS_BONUS', 0.55),
|
||||
],
|
||||
|
||||
'negative_signals' => [
|
||||
'hide_artwork_penalty' => (float) env('DISCOVERY_V2_HIDE_ARTWORK_PENALTY', 5.0),
|
||||
'dislike_tag_penalty' => (float) env('DISCOVERY_V2_DISLIKE_TAG_PENALTY', 0.75),
|
||||
],
|
||||
],
|
||||
|
||||
'v3' => [
|
||||
'enabled' => (bool) env('DISCOVERY_V3_ENABLED', false),
|
||||
'cache_version' => env('DISCOVERY_V3_CACHE_VERSION', 'cache-v3'),
|
||||
'cache_ttl_minutes' => (int) env('DISCOVERY_V3_CACHE_TTL_MINUTES', 5),
|
||||
'vector_similarity_weight' => (float) env('DISCOVERY_V3_VECTOR_SIMILARITY_WEIGHT', 0.8),
|
||||
'vector_base_score' => (float) env('DISCOVERY_V3_VECTOR_BASE_SCORE', 0.75),
|
||||
'max_seed_artworks' => (int) env('DISCOVERY_V3_MAX_SEED_ARTWORKS', 3),
|
||||
'vector_candidate_pool' => (int) env('DISCOVERY_V3_VECTOR_CANDIDATE_POOL', 60),
|
||||
'sections' => [
|
||||
'similar_style_limit' => (int) env('DISCOVERY_V3_SECTION_SIMILAR_STYLE_LIMIT', 3),
|
||||
'you_may_also_like_limit' => (int) env('DISCOVERY_V3_SECTION_YOU_MAY_ALSO_LIKE_LIMIT', 6),
|
||||
'visually_related_limit' => (int) env('DISCOVERY_V3_SECTION_VISUALLY_RELATED_LIMIT', 6),
|
||||
],
|
||||
],
|
||||
|
||||
// Phase 8B: versioned ranking blend weights.
|
||||
// Blend components: w1=interest, w2=recency, w3=popularity, w4=novelty.
|
||||
'ranking' => [
|
||||
'default_weights' => [
|
||||
'version' => env('DISCOVERY_RANKING_WEIGHTS_VERSION', 'rank-w-v1'),
|
||||
'w1' => (float) env('DISCOVERY_RANKING_W1', 0.65),
|
||||
'w2' => (float) env('DISCOVERY_RANKING_W2', 0.20),
|
||||
'w3' => (float) env('DISCOVERY_RANKING_W3', 0.10),
|
||||
'w4' => (float) env('DISCOVERY_RANKING_W4', 0.05),
|
||||
],
|
||||
|
||||
// Per-algo overrides for safe rollout by algo_version.
|
||||
'algo_weight_sets' => [
|
||||
'clip-cosine-v1' => [
|
||||
'version' => env('DISCOVERY_RANKING_WEIGHTS_VERSION_CLIP_COSINE_V1', 'rank-w-v1'),
|
||||
'w1' => (float) env('DISCOVERY_RANKING_W1_CLIP_COSINE_V1', 0.65),
|
||||
'w2' => (float) env('DISCOVERY_RANKING_W2_CLIP_COSINE_V1', 0.20),
|
||||
'w3' => (float) env('DISCOVERY_RANKING_W3_CLIP_COSINE_V1', 0.10),
|
||||
'w4' => (float) env('DISCOVERY_RANKING_W4_CLIP_COSINE_V1', 0.05),
|
||||
],
|
||||
'clip-cosine-v2' => [
|
||||
'version' => env('DISCOVERY_RANKING_WEIGHTS_VERSION_CLIP_COSINE_V2', 'rank-w-v2-prod-1'),
|
||||
'w1' => (float) env('DISCOVERY_RANKING_W1_CLIP_COSINE_V2', 0.52),
|
||||
'w2' => (float) env('DISCOVERY_RANKING_W2_CLIP_COSINE_V2', 0.23),
|
||||
'w3' => (float) env('DISCOVERY_RANKING_W3_CLIP_COSINE_V2', 0.15),
|
||||
'w4' => (float) env('DISCOVERY_RANKING_W4_CLIP_COSINE_V2', 0.10),
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
// Phase 8 production rollout gates (deterministic user bucketing).
|
||||
'rollout' => [
|
||||
'enabled' => (bool) env('DISCOVERY_ROLLOUT_ENABLED', false),
|
||||
'baseline_algo_version' => env('DISCOVERY_ROLLOUT_BASELINE_ALGO_VERSION', 'clip-cosine-v1'),
|
||||
'candidate_algo_version' => env('DISCOVERY_ROLLOUT_CANDIDATE_ALGO_VERSION', 'clip-cosine-v2'),
|
||||
|
||||
// One of: g10, g50, g100.
|
||||
'active_gate' => env('DISCOVERY_ROLLOUT_ACTIVE_GATE', 'g10'),
|
||||
'gates' => [
|
||||
'g10' => [
|
||||
'name' => '10%',
|
||||
'percentage' => (int) env('DISCOVERY_ROLLOUT_GATE_10_PERCENT', 10),
|
||||
],
|
||||
'g50' => [
|
||||
'name' => '50%',
|
||||
'percentage' => (int) env('DISCOVERY_ROLLOUT_GATE_50_PERCENT', 50),
|
||||
],
|
||||
'g100' => [
|
||||
'name' => '100%',
|
||||
'percentage' => (int) env('DISCOVERY_ROLLOUT_GATE_100_PERCENT', 100),
|
||||
],
|
||||
],
|
||||
|
||||
// Emergency rollback toggle: force all traffic to one algo_version.
|
||||
'force_algo_version' => env('DISCOVERY_FORCE_ALGO_VERSION', ''),
|
||||
|
||||
// Guardrails (used operationally in runbook and dashboards).
|
||||
'monitoring_thresholds' => [
|
||||
'ctr_warn_drop_pct' => (float) env('DISCOVERY_ROLLOUT_WARN_CTR_DROP_PCT', 3.0),
|
||||
'ctr_rollback_drop_pct' => (float) env('DISCOVERY_ROLLOUT_ROLLBACK_CTR_DROP_PCT', 5.0),
|
||||
'long_dwell_warn_drop_pct' => (float) env('DISCOVERY_ROLLOUT_WARN_LONG_DWELL_DROP_PCT', 4.0),
|
||||
'long_dwell_rollback_drop_pct' => (float) env('DISCOVERY_ROLLOUT_ROLLBACK_LONG_DWELL_DROP_PCT', 8.0),
|
||||
'diversity_warn_concentration_rise_pct' => (float) env('DISCOVERY_ROLLOUT_WARN_DIVERSITY_CONCENTRATION_RISE_PCT', 10.0),
|
||||
'diversity_rollback_concentration_rise_pct' => (float) env('DISCOVERY_ROLLOUT_ROLLBACK_DIVERSITY_CONCENTRATION_RISE_PCT', 15.0),
|
||||
],
|
||||
],
|
||||
|
||||
// Offline evaluation objective weights (manual/data-driven tuning).
|
||||
'evaluation' => [
|
||||
'objective_weights' => [
|
||||
'ctr' => (float) env('DISCOVERY_EVAL_WEIGHT_CTR', 0.45),
|
||||
'save_rate' => (float) env('DISCOVERY_EVAL_WEIGHT_SAVE_RATE', 0.35),
|
||||
'long_dwell_share' => (float) env('DISCOVERY_EVAL_WEIGHT_LONG_DWELL', 0.25),
|
||||
'bounce_rate_penalty' => (float) env('DISCOVERY_EVAL_WEIGHT_BOUNCE_PENALTY', 0.15),
|
||||
],
|
||||
// Temporary switch: keep save_rate visible but exclude it from objective score.
|
||||
'save_rate_informational' => (bool) env('DISCOVERY_EVAL_SAVE_RATE_INFORMATIONAL', true),
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'domains' => [
|
||||
'mailinator.com',
|
||||
'10minutemail.com',
|
||||
'guerrillamail.com',
|
||||
'tempmail.com',
|
||||
'yopmail.com',
|
||||
],
|
||||
];
|
||||
78
.deploy/artwork-evolution-release/config/early_growth.php
Normal file
78
.deploy/artwork-evolution-release/config/early_growth.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Early-Stage Growth System (EGS) Configuration
|
||||
*
|
||||
* Controls the Nova Early-Stage Growth System — a reversible, non-deceptive
|
||||
* layer that keeps the site feeling alive when organic traffic is low.
|
||||
*
|
||||
* TOGGLE:
|
||||
* .env NOVA_EARLY_GROWTH_ENABLED=true
|
||||
* .env NOVA_EARLY_GROWTH_MODE=light (off | light | aggressive)
|
||||
*
|
||||
* Set NOVA_EARLY_GROWTH_ENABLED=false (or NOVA_EARLY_GROWTH_MODE=off)
|
||||
* to revert all behaviour to standard production logic instantly.
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
// ─── Master switch ────────────────────────────────────────────────────────
|
||||
'enabled' => (bool) env('NOVA_EARLY_GROWTH_ENABLED', false),
|
||||
|
||||
// operating mode: off | light | aggressive
|
||||
'mode' => env('NOVA_EARLY_GROWTH_MODE', 'off'),
|
||||
|
||||
// ─── Module toggles (each respects master 'enabled') ─────────────────────
|
||||
'adaptive_time_window' => (bool) env('NOVA_EGS_ADAPTIVE_WINDOW', true),
|
||||
'grid_filler' => (bool) env('NOVA_EGS_GRID_FILLER', true),
|
||||
'spotlight' => (bool) env('NOVA_EGS_SPOTLIGHT', true),
|
||||
'activity_layer' => (bool) env('NOVA_EGS_ACTIVITY_LAYER', false),
|
||||
|
||||
// ─── AdaptiveTimeWindow thresholds ───────────────────────────────────────
|
||||
// uploads_per_day is the 7-day rolling average of published artworks/day.
|
||||
'thresholds' => [
|
||||
'uploads_per_day_narrow' => (int) env('NOVA_EGS_UPLOADS_PER_DAY_NARROW', 10),
|
||||
'uploads_per_day_wide' => (int) env('NOVA_EGS_UPLOADS_PER_DAY_WIDE', 3),
|
||||
// Days to look back for trending / rising queries
|
||||
'window_narrow_days' => (int) env('NOVA_EGS_WINDOW_NARROW_DAYS', 7),
|
||||
'window_medium_days' => (int) env('NOVA_EGS_WINDOW_MEDIUM_DAYS', 30),
|
||||
'window_wide_days' => (int) env('NOVA_EGS_WINDOW_WIDE_DAYS', 90),
|
||||
],
|
||||
|
||||
// ─── FeedBlender ratios per mode ─────────────────────────────────────────
|
||||
// Values are proportions; they are normalised internally (sum need not equal 1).
|
||||
'blend_ratios' => [
|
||||
'light' => [
|
||||
'fresh' => 0.60,
|
||||
'curated' => 0.25,
|
||||
'spotlight' => 0.15,
|
||||
],
|
||||
'aggressive' => [
|
||||
'fresh' => 0.30,
|
||||
'curated' => 0.50,
|
||||
'spotlight' => 0.20,
|
||||
],
|
||||
],
|
||||
|
||||
// ─── GridFiller ──────────────────────────────────────────────────────────
|
||||
// Minimum number of items to display per grid page.
|
||||
'grid_min_results' => (int) env('NOVA_EGS_GRID_MIN_RESULTS', 12),
|
||||
|
||||
// ─── Auto-disable when site reaches organic scale ────────────────────────
|
||||
'auto_disable' => [
|
||||
'enabled' => (bool) env('NOVA_EGS_AUTO_DISABLE', false),
|
||||
'uploads_per_day' => (int) env('NOVA_EGS_AUTO_DISABLE_UPLOADS', 50),
|
||||
'active_users' => (int) env('NOVA_EGS_AUTO_DISABLE_USERS', 500),
|
||||
],
|
||||
|
||||
// ─── Cache TTLs (seconds) ─────────────────────────────────────────────────
|
||||
'cache_ttl' => [
|
||||
'spotlight' => (int) env('NOVA_EGS_SPOTLIGHT_TTL', 3600), // 1 h – rotated daily
|
||||
'feed_blend' => (int) env('NOVA_EGS_BLEND_TTL', 300), // 5 min
|
||||
'time_window' => (int) env('NOVA_EGS_WINDOW_TTL', 600), // 10 min
|
||||
'activity' => (int) env('NOVA_EGS_ACTIVITY_TTL', 1800), // 30 min
|
||||
],
|
||||
|
||||
];
|
||||
24
.deploy/artwork-evolution-release/config/features.php
Normal file
24
.deploy/artwork-evolution-release/config/features.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'uploads_v2' => (bool) env('SKINBASE_UPLOADS_V2', true),
|
||||
'groups' => (bool) env('SKINBASE_GROUPS_V1', true),
|
||||
'groups_v1' => (bool) env('SKINBASE_GROUPS_V1', true),
|
||||
'groups_v2' => (bool) env('SKINBASE_GROUPS_V2', true),
|
||||
'group_posts' => (bool) env('SKINBASE_GROUP_POSTS', true),
|
||||
'group_recruitment' => (bool) env('SKINBASE_GROUP_RECRUITMENT', true),
|
||||
'group_join_requests' => (bool) env('SKINBASE_GROUP_JOIN_REQUESTS', true),
|
||||
'group_review_queue' => (bool) env('SKINBASE_GROUP_REVIEW_QUEUE', true),
|
||||
'group_projects' => (bool) env('SKINBASE_GROUP_PROJECTS', true),
|
||||
'group_challenges' => (bool) env('SKINBASE_GROUP_CHALLENGES', true),
|
||||
'group_events' => (bool) env('SKINBASE_GROUP_EVENTS', true),
|
||||
'group_assets' => (bool) env('SKINBASE_GROUP_ASSETS', true),
|
||||
'group_activity_feed' => (bool) env('SKINBASE_GROUP_ACTIVITY_FEED', true),
|
||||
'groups_v4' => (bool) env('SKINBASE_GROUPS_V4', true),
|
||||
'group_releases' => (bool) env('SKINBASE_GROUP_RELEASES', true),
|
||||
'group_milestones' => (bool) env('SKINBASE_GROUP_MILESTONES', true),
|
||||
'group_reputation' => (bool) env('SKINBASE_GROUP_REPUTATION', true),
|
||||
'group_badges' => (bool) env('SKINBASE_GROUP_BADGES', true),
|
||||
'group_discovery_v2' => (bool) env('SKINBASE_GROUP_DISCOVERY_V2', true),
|
||||
'similarity_vector' => (bool) env('SIMILARITY_VECTOR_ENABLED', false),
|
||||
];
|
||||
166
.deploy/artwork-evolution-release/config/file-manager.php
Normal file
166
.deploy/artwork-evolution-release/config/file-manager.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
use Alexusmai\LaravelFileManager\Services\ConfigService\DefaultConfigRepository;
|
||||
use Alexusmai\LaravelFileManager\Services\ACLService\ConfigACLRepository;
|
||||
|
||||
return [
|
||||
|
||||
/**
|
||||
* Set Config repository
|
||||
*
|
||||
* Default - DefaultConfigRepository get config from this file
|
||||
*/
|
||||
'configRepository' => DefaultConfigRepository::class,
|
||||
|
||||
/**
|
||||
* ACL rules repository
|
||||
*
|
||||
* Default - ConfigACLRepository (see rules in - aclRules)
|
||||
*/
|
||||
'aclRepository' => ConfigACLRepository::class,
|
||||
|
||||
//********* Default configuration for DefaultConfigRepository **************
|
||||
|
||||
/**
|
||||
* LFM Route prefix
|
||||
* !!! WARNING - if you change it, you should compile frontend with new prefix(baseUrl) !!!
|
||||
*/
|
||||
'routePrefix' => 'file-manager',
|
||||
|
||||
/**
|
||||
* List of disk names that you want to use
|
||||
* (from config/filesystems)
|
||||
*/
|
||||
'diskList' => ['public'],
|
||||
|
||||
/**
|
||||
* Default disk for left manager
|
||||
*
|
||||
* null - auto select the first disk in the disk list
|
||||
*/
|
||||
'leftDisk' => null,
|
||||
|
||||
/**
|
||||
* Default disk for right manager
|
||||
*
|
||||
* null - auto select the first disk in the disk list
|
||||
*/
|
||||
'rightDisk' => null,
|
||||
|
||||
/**
|
||||
* Default path for left manager
|
||||
*
|
||||
* null - root directory
|
||||
*/
|
||||
'leftPath' => null,
|
||||
|
||||
/**
|
||||
* Default path for right manager
|
||||
*
|
||||
* null - root directory
|
||||
*/
|
||||
'rightPath' => null,
|
||||
|
||||
/**
|
||||
* File manager modules configuration
|
||||
*
|
||||
* 1 - only one file manager window
|
||||
* 2 - one file manager window with directories tree module
|
||||
* 3 - two file manager windows
|
||||
*/
|
||||
'windowsConfig' => 2,
|
||||
|
||||
/**
|
||||
* File upload - Max file size in KB
|
||||
*
|
||||
* null - no restrictions
|
||||
*/
|
||||
'maxUploadFileSize' => null,
|
||||
|
||||
/**
|
||||
* File upload - Allow these file types
|
||||
*
|
||||
* [] - no restrictions
|
||||
*/
|
||||
'allowFileTypes' => [],
|
||||
|
||||
/**
|
||||
* Show / Hide system files and folders
|
||||
*/
|
||||
'hiddenFiles' => true,
|
||||
|
||||
/***************************************************************************
|
||||
* Middleware
|
||||
*
|
||||
* Add your middleware name to array -> ['web', 'auth', 'admin']
|
||||
* !!!! RESTRICT ACCESS FOR NON ADMIN USERS !!!!
|
||||
*/
|
||||
'middleware' => ['web'],
|
||||
|
||||
/***************************************************************************
|
||||
* ACL mechanism ON/OFF
|
||||
*
|
||||
* default - false(OFF)
|
||||
*/
|
||||
'acl' => false,
|
||||
|
||||
/**
|
||||
* Hide files and folders from file-manager if user doesn't have access
|
||||
*
|
||||
* ACL access level = 0
|
||||
*/
|
||||
'aclHideFromFM' => true,
|
||||
|
||||
/**
|
||||
* ACL strategy
|
||||
*
|
||||
* blacklist - Allow everything(access - 2 - r/w) that is not forbidden by the ACL rules list
|
||||
*
|
||||
* whitelist - Deny anything(access - 0 - deny), that not allowed by the ACL rules list
|
||||
*/
|
||||
'aclStrategy' => 'blacklist',
|
||||
|
||||
/**
|
||||
* ACL Rules cache
|
||||
*
|
||||
* null or value in minutes
|
||||
*/
|
||||
'aclRulesCache' => null,
|
||||
|
||||
//********* Default configuration for DefaultConfigRepository END **********
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* ACL rules list - used for default ACL repository (ConfigACLRepository)
|
||||
*
|
||||
* 1 it's user ID
|
||||
* null - for not authenticated user
|
||||
*
|
||||
* 'disk' => 'disk-name'
|
||||
*
|
||||
* 'path' => 'folder-name'
|
||||
* 'path' => 'folder1*' - select folder1, folder12, folder1/sub-folder, ...
|
||||
* 'path' => 'folder2/*' - select folder2/sub-folder,... but not select folder2 !!!
|
||||
* 'path' => 'folder-name/file-name.jpg'
|
||||
* 'path' => 'folder-name/*.jpg'
|
||||
*
|
||||
* * - wildcard
|
||||
*
|
||||
* access: 0 - deny, 1 - read, 2 - read/write
|
||||
*/
|
||||
'aclRules' => [
|
||||
null => [
|
||||
//['disk' => 'public', 'path' => '/', 'access' => 2],
|
||||
],
|
||||
1 => [
|
||||
//['disk' => 'public', 'path' => 'images/arch*.jpg', 'access' => 2],
|
||||
//['disk' => 'public', 'path' => 'files/*', 'access' => 1],
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* Enable slugification of filenames of uploaded files.
|
||||
*
|
||||
*/
|
||||
'slugifyNames' => false,
|
||||
];
|
||||
81
.deploy/artwork-evolution-release/config/filesystems.php
Normal file
81
.deploy/artwork-evolution-release/config/filesystems.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default filesystem disk that should be used
|
||||
| by the framework. The "local" disk, as well as a variety of cloud
|
||||
| based disks are available to your application for file storage.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('FILESYSTEM_DISK', 'local'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filesystem Disks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below you may configure as many filesystem disks as necessary, and you
|
||||
| may even configure multiple disks for the same driver. Examples for
|
||||
| most supported storage drivers are configured here for reference.
|
||||
|
|
||||
| Supported drivers: "local", "ftp", "sftp", "s3"
|
||||
|
|
||||
*/
|
||||
|
||||
'disks' => [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/private'),
|
||||
'serve' => true,
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION'),
|
||||
'bucket' => env('AWS_BUCKET'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Symbolic Links
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the symbolic links that will be created when the
|
||||
| `storage:link` Artisan command is executed. The array keys should be
|
||||
| the locations of the links and the values should be their targets.
|
||||
|
|
||||
*/
|
||||
|
||||
'links' => [
|
||||
public_path('storage') => storage_path('app/public'),
|
||||
],
|
||||
|
||||
];
|
||||
10
.deploy/artwork-evolution-release/config/forum.php
Normal file
10
.deploy/artwork-evolution-release/config/forum.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'preview_images' => [
|
||||
'default' => '/images/forum/default.jpg',
|
||||
'map' => [
|
||||
// 'announcements' => '/images/forum/defaults/announcements.jpg',
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enabled' => env('FORUM_BOT_PROTECTION_ENABLED', env('APP_ENV') === 'production'),
|
||||
|
||||
'thresholds' => [
|
||||
'allow' => 20,
|
||||
'log' => 20,
|
||||
'captcha' => 40,
|
||||
'moderate' => 60,
|
||||
'block' => 80,
|
||||
],
|
||||
|
||||
'honeypots' => [
|
||||
'fields' => ['homepage_url', 'company_name'],
|
||||
'penalty' => 60,
|
||||
],
|
||||
|
||||
'captcha' => [
|
||||
'provider' => env('FORUM_BOT_CAPTCHA_PROVIDER', 'turnstile'),
|
||||
'actions' => [
|
||||
'register',
|
||||
'login',
|
||||
'follow',
|
||||
'forum_topic_create',
|
||||
'forum_reply_create',
|
||||
'forum_post_update',
|
||||
'profile_update',
|
||||
'api_write',
|
||||
],
|
||||
'input' => env('FORUM_BOT_CAPTCHA_INPUT', ''),
|
||||
'message' => 'Complete the captcha challenge to continue.',
|
||||
],
|
||||
|
||||
'behavior' => [
|
||||
'new_account_days' => 7,
|
||||
'rapid_post_window_minutes' => 1,
|
||||
'rapid_post_threshold' => 5,
|
||||
'rapid_thread_threshold' => 2,
|
||||
'recent_action_window_seconds' => 45,
|
||||
'recent_action_threshold' => 6,
|
||||
'login_attempt_window_minutes' => 10,
|
||||
'login_attempt_threshold' => 8,
|
||||
'profile_update_threshold' => 6,
|
||||
'profile_update_window_minutes' => 60,
|
||||
'api_request_window_minutes' => 1,
|
||||
'api_request_threshold' => 100,
|
||||
'repeated_content_penalty' => 50,
|
||||
'new_account_links_penalty' => 30,
|
||||
'rapid_post_penalty' => 40,
|
||||
'recent_action_penalty' => 40,
|
||||
'login_burst_penalty' => 35,
|
||||
'profile_burst_penalty' => 20,
|
||||
'api_burst_penalty' => 60,
|
||||
],
|
||||
|
||||
'account_farm' => [
|
||||
'window_minutes' => 10,
|
||||
'register_attempt_threshold' => 10,
|
||||
'same_ip_users_threshold' => 5,
|
||||
'same_fingerprint_users_threshold' => 3,
|
||||
'same_pattern_users_threshold' => 3,
|
||||
'register_attempt_penalty' => 50,
|
||||
'same_ip_penalty' => 35,
|
||||
'same_fingerprint_penalty' => 40,
|
||||
'same_pattern_penalty' => 45,
|
||||
],
|
||||
|
||||
'ip' => [
|
||||
'cache_ttl_minutes' => 15,
|
||||
'recent_high_risk_window_hours' => 24,
|
||||
'recent_high_risk_threshold' => 3,
|
||||
'recent_high_risk_penalty' => 20,
|
||||
'known_proxy_penalty' => 20,
|
||||
'datacenter_penalty' => 25,
|
||||
'tor_penalty' => 40,
|
||||
'blacklist_penalty' => 100,
|
||||
'known_proxies' => [],
|
||||
'datacenter_ranges' => [],
|
||||
'provider_ranges' => [
|
||||
'aws' => [],
|
||||
'azure' => [],
|
||||
'gcp' => [],
|
||||
'digitalocean' => [],
|
||||
'hetzner' => [],
|
||||
'ovh' => [],
|
||||
],
|
||||
'tor_exit_nodes' => [],
|
||||
],
|
||||
|
||||
'rate_limits' => [
|
||||
'penalties' => [
|
||||
'default' => 35,
|
||||
'minute' => 35,
|
||||
'hour' => 45,
|
||||
],
|
||||
],
|
||||
|
||||
'geo_behavior' => [
|
||||
'enabled' => true,
|
||||
'login_actions' => ['login'],
|
||||
'country_headers' => [
|
||||
'CF-IPCountry',
|
||||
'CloudFront-Viewer-Country',
|
||||
'X-Country-Code',
|
||||
'X-App-Country-Code',
|
||||
],
|
||||
'recent_login_window_minutes' => 60,
|
||||
'country_change_penalty' => 50,
|
||||
],
|
||||
|
||||
'patterns' => [
|
||||
'seo' => [
|
||||
'best seo service',
|
||||
'cheap backlinks',
|
||||
'guaranteed traffic',
|
||||
'rank your website',
|
||||
],
|
||||
'casino' => [
|
||||
'online casino',
|
||||
'jackpot bonus',
|
||||
'slot machine',
|
||||
'betting tips',
|
||||
],
|
||||
'crypto' => [
|
||||
'crypto signal',
|
||||
'double your bitcoin',
|
||||
'guaranteed profit',
|
||||
'token presale',
|
||||
],
|
||||
'affiliate' => [
|
||||
'affiliate link',
|
||||
'promo code',
|
||||
'limited offer',
|
||||
'work from home',
|
||||
],
|
||||
'repeated_phrase_penalty' => 40,
|
||||
'category_penalty' => 30,
|
||||
],
|
||||
|
||||
'scan' => [
|
||||
'lookback_minutes' => 5,
|
||||
'auto_blacklist_attempts' => 10,
|
||||
'auto_blacklist_risk' => 80,
|
||||
'auto_blacklist_reason' => 'Automatically blacklisted by bot activity monitor.',
|
||||
'queue' => env('FORUM_BOT_SCAN_QUEUE', 'forum-moderation'),
|
||||
],
|
||||
];
|
||||
65
.deploy/artwork-evolution-release/config/forum_security.php
Normal file
65
.deploy/artwork-evolution-release/config/forum_security.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enabled' => env('FORUM_SECURITY_ENABLED', true),
|
||||
|
||||
'thresholds' => [
|
||||
'safe' => 20,
|
||||
'log' => 20,
|
||||
'captcha' => 40,
|
||||
'moderate' => 60,
|
||||
'block' => 80,
|
||||
'firewall_block' => 70,
|
||||
],
|
||||
|
||||
'queues' => [
|
||||
'moderation' => env('FORUM_SECURITY_MODERATION_QUEUE', 'forum-moderation'),
|
||||
'firewall' => env('FORUM_SECURITY_FIREWALL_QUEUE', 'forum-security'),
|
||||
],
|
||||
|
||||
'firewall' => [
|
||||
'enabled' => true,
|
||||
'request_pattern' => [
|
||||
'window_seconds' => 60,
|
||||
'burst_threshold' => 15,
|
||||
'burst_penalty' => 25,
|
||||
'missing_user_agent_penalty' => 10,
|
||||
'suspicious_path_penalty' => 20,
|
||||
'repeat_route_penalty' => 20,
|
||||
],
|
||||
'spam_wave' => [
|
||||
'window_minutes' => 15,
|
||||
'same_hash_threshold' => 3,
|
||||
'same_hash_penalty' => 30,
|
||||
'same_ip_flagged_threshold' => 4,
|
||||
'same_ip_flagged_penalty' => 25,
|
||||
'same_signature_threshold' => 3,
|
||||
'same_signature_penalty' => 20,
|
||||
],
|
||||
'thread_attack' => [
|
||||
'window_minutes' => 10,
|
||||
'topic_threshold' => 4,
|
||||
'reply_threshold' => 8,
|
||||
'topic_penalty' => 25,
|
||||
'reply_penalty' => 20,
|
||||
],
|
||||
'login_attack' => [
|
||||
'window_minutes' => 15,
|
||||
'login_threshold' => 10,
|
||||
'register_threshold' => 6,
|
||||
'login_penalty' => 30,
|
||||
'register_penalty' => 35,
|
||||
],
|
||||
'scan' => [
|
||||
'lookback_minutes' => 15,
|
||||
'auto_blacklist_attempts' => 4,
|
||||
'auto_blacklist_risk' => 70,
|
||||
'auto_blacklist_reason' => 'Automatically blacklisted by forum firewall activity monitor.',
|
||||
],
|
||||
],
|
||||
|
||||
'logging' => [
|
||||
'store_request_payload' => false,
|
||||
'reason_limit' => 8,
|
||||
],
|
||||
];
|
||||
121
.deploy/artwork-evolution-release/config/groups.php
Normal file
121
.deploy/artwork-evolution-release/config/groups.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'recruitment' => [
|
||||
'roles' => [
|
||||
'Animator',
|
||||
'Composer',
|
||||
'Concept Artist',
|
||||
'Curator',
|
||||
'Designer',
|
||||
'Developer',
|
||||
'Editor',
|
||||
'Illustrator',
|
||||
'Moderator',
|
||||
'Photographer',
|
||||
'Pixel Artist',
|
||||
'Producer',
|
||||
'3D Artist',
|
||||
'Sound Designer',
|
||||
'Texture Artist',
|
||||
'UI Artist',
|
||||
'Wallpaper Creator',
|
||||
'Writer',
|
||||
],
|
||||
'skills' => [
|
||||
'After Effects',
|
||||
'Aseprite',
|
||||
'Blender',
|
||||
'Color Grading',
|
||||
'Compositing',
|
||||
'Figma',
|
||||
'Illustrator',
|
||||
'Lighting',
|
||||
'Photoshop',
|
||||
'Pixel Art',
|
||||
'Premiere Pro',
|
||||
'Procreate',
|
||||
'Retouching',
|
||||
'Sound Design',
|
||||
'Storyboarding',
|
||||
'Substance Painter',
|
||||
'Typography',
|
||||
'UI Design',
|
||||
],
|
||||
'contact_modes' => [
|
||||
'join_request',
|
||||
'direct_message',
|
||||
'external_link',
|
||||
],
|
||||
'visibility_options' => [
|
||||
'public',
|
||||
'members_only',
|
||||
'private',
|
||||
],
|
||||
],
|
||||
'projects' => [
|
||||
'statuses' => ['planned', 'active', 'review', 'released', 'archived'],
|
||||
'visibility_options' => ['public', 'unlisted', 'private'],
|
||||
'handoff_states' => ['waiting_for_review', 'waiting_for_assets', 'waiting_for_packaging', 'waiting_for_final_approval'],
|
||||
],
|
||||
'releases' => [
|
||||
'statuses' => ['planned', 'in_progress', 'internal_review', 'scheduled', 'released', 'archived', 'cancelled'],
|
||||
'stages' => ['concept', 'production', 'review', 'packaging', 'approval', 'publishing', 'released'],
|
||||
'visibility_options' => ['public', 'unlisted', 'private'],
|
||||
],
|
||||
'milestones' => [
|
||||
'statuses' => ['pending', 'active', 'blocked', 'completed', 'cancelled'],
|
||||
],
|
||||
'challenges' => [
|
||||
'statuses' => ['draft', 'published', 'active', 'ended', 'archived'],
|
||||
'visibility_options' => ['public', 'unlisted', 'private'],
|
||||
'participation_scopes' => ['group_only', 'invite_only', 'public'],
|
||||
'judging_modes' => ['curated', 'community_vote', 'staff_pick'],
|
||||
],
|
||||
'events' => [
|
||||
'types' => ['launch', 'challenge', 'livestream', 'meetup', 'milestone', 'showcase', 'internal_session', 'release_window'],
|
||||
'statuses' => ['draft', 'published', 'archived', 'cancelled'],
|
||||
'visibility_options' => ['public', 'members_only', 'private'],
|
||||
],
|
||||
'assets' => [
|
||||
'categories' => ['logo', 'brand', 'palette', 'watermark', 'template', 'reference', 'source_pack', 'promo', 'misc'],
|
||||
'visibility_options' => ['internal', 'members_only', 'public_download'],
|
||||
'statuses' => ['active', 'archived'],
|
||||
'allowed_mime_types' => [
|
||||
'application/pdf',
|
||||
'application/postscript',
|
||||
'application/zip',
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/svg+xml',
|
||||
'image/webp',
|
||||
],
|
||||
'allowed_extensions' => ['pdf', 'psd', 'svg', 'zip', 'png', 'jpg', 'jpeg', 'webp'],
|
||||
'max_upload_kb' => 20480,
|
||||
],
|
||||
'badges' => [
|
||||
'group' => [
|
||||
'first_release' => 'First Release',
|
||||
'ten_releases' => '10 Releases',
|
||||
'hundred_published_artworks' => '100 Published Artworks',
|
||||
'recruiting_success' => 'Recruiting Success',
|
||||
'community_favorite' => 'Community Favorite',
|
||||
'consistent_activity' => 'Consistent Activity',
|
||||
'event_host' => 'Event Host',
|
||||
'challenge_organizer' => 'Challenge Organizer',
|
||||
'collaborative_group' => 'Collaborative Group',
|
||||
'trusted_group' => 'Trusted Group',
|
||||
],
|
||||
'member' => [
|
||||
'first_group_contribution' => 'First Group Contribution',
|
||||
'ten_group_contributions' => '10 Group Contributions',
|
||||
'release_contributor' => 'Release Contributor',
|
||||
'project_lead' => 'Project Lead',
|
||||
'reliable_reviewer' => 'Reliable Reviewer',
|
||||
'challenge_winner' => 'Challenge Winner',
|
||||
'long_term_collaborator' => 'Long-Term Collaborator',
|
||||
'founding_member' => 'Founding Member',
|
||||
'asset_builder' => 'Asset Builder',
|
||||
],
|
||||
],
|
||||
];
|
||||
7
.deploy/artwork-evolution-release/config/homepage.php
Normal file
7
.deploy/artwork-evolution-release/config/homepage.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'cache_store' => env('HOMEPAGE_CACHE_STORE', 'homepage'),
|
||||
'guest_payload_key' => env('HOMEPAGE_GUEST_PAYLOAD_KEY', 'homepage.payload.guest'),
|
||||
'guest_payload_ttl_seconds' => (int) env('HOMEPAGE_GUEST_PAYLOAD_TTL_SECONDS', 1800),
|
||||
];
|
||||
277
.deploy/artwork-evolution-release/config/horizon.php
Normal file
277
.deploy/artwork-evolution-release/config/horizon.php
Normal file
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This name appears in notifications and in the Horizon UI. Unique names
|
||||
| can be useful while running multiple instances of Horizon within an
|
||||
| application, allowing you to identify the Horizon you're viewing.
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => env('HORIZON_NAME'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the subdomain where Horizon will be accessible from. If this
|
||||
| setting is null, Horizon will reside under the same domain as the
|
||||
| application. Otherwise, this value will serve as the subdomain.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('HORIZON_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the URI path where Horizon will be accessible from. Feel free
|
||||
| to change this path to anything you like. Note that the URI will not
|
||||
| affect the paths of its internal API that aren't exposed to users.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => env('HORIZON_PATH', 'horizon'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Redis Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the name of the Redis connection where Horizon will store the
|
||||
| meta information required for it to function. It includes the list
|
||||
| of supervisors, failed jobs, job metrics, and other information.
|
||||
|
|
||||
*/
|
||||
|
||||
'use' => 'default',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Redis Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This prefix will be used when storing all Horizon data in Redis. You
|
||||
| may modify the prefix when you are running multiple installations
|
||||
| of Horizon on the same server so that they don't have problems.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env(
|
||||
'HORIZON_PREFIX',
|
||||
Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
|
||||
),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Route Middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These middleware will get attached onto each Horizon route, giving you
|
||||
| the chance to add your own middleware to this list or change any of
|
||||
| the existing middleware. Or, you can simply stick with this list.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware' => ['web'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Wait Time Thresholds
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to configure when the LongWaitDetected event
|
||||
| will be fired. Every connection / queue combination may have its
|
||||
| own, unique threshold (in seconds) before this event is fired.
|
||||
|
|
||||
*/
|
||||
|
||||
'waits' => [
|
||||
'redis:broadcasts' => 15,
|
||||
'redis:default' => 60,
|
||||
'redis:notifications' => 90,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Job Trimming Times
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you can configure for how long (in minutes) you desire Horizon to
|
||||
| persist the recent and failed jobs. Typically, recent jobs are kept
|
||||
| for one hour while all failed jobs are stored for an entire week.
|
||||
|
|
||||
*/
|
||||
|
||||
'trim' => [
|
||||
'recent' => 60,
|
||||
'pending' => 60,
|
||||
'completed' => 60,
|
||||
'recent_failed' => 10080,
|
||||
'failed' => 10080,
|
||||
'monitored' => 10080,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Silenced Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Silencing a job will instruct Horizon to not place the job in the list
|
||||
| of completed jobs within the Horizon dashboard. This setting may be
|
||||
| used to fully remove any noisy jobs from the completed jobs list.
|
||||
|
|
||||
*/
|
||||
|
||||
'silenced' => [
|
||||
// App\Jobs\ExampleJob::class,
|
||||
],
|
||||
|
||||
'silenced_tags' => [
|
||||
// 'notifications',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metrics
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you can configure how many snapshots should be kept to display in
|
||||
| the metrics graph. This will get used in combination with Horizon's
|
||||
| `horizon:snapshot` schedule to define how long to retain metrics.
|
||||
|
|
||||
*/
|
||||
|
||||
'metrics' => [
|
||||
'trim_snapshots' => [
|
||||
'job' => 24,
|
||||
'queue' => 24,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Fast Termination
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When this option is enabled, Horizon's "terminate" command will not
|
||||
| wait on all of the workers to terminate unless the --wait option
|
||||
| is provided. Fast termination can shorten deployment delay by
|
||||
| allowing a new instance of Horizon to start while the last
|
||||
| instance will continue to terminate each of its workers.
|
||||
|
|
||||
*/
|
||||
|
||||
'fast_termination' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Memory Limit (MB)
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value describes the maximum amount of memory the Horizon master
|
||||
| supervisor may consume before it is terminated and restarted. For
|
||||
| configuring these limits on your workers, see the next section.
|
||||
|
|
||||
*/
|
||||
|
||||
'memory_limit' => 64,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Worker Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define the queue worker settings used by your application
|
||||
| in all environments. These supervisors and settings handle all your
|
||||
| queued jobs and will be provisioned by Horizon during deployment.
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'supervisor-default' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['default'],
|
||||
'balance' => 'auto',
|
||||
'autoScalingStrategy' => 'time',
|
||||
'maxProcesses' => 1,
|
||||
'maxTime' => 0,
|
||||
'maxJobs' => 0,
|
||||
'memory' => 128,
|
||||
'tries' => 1,
|
||||
'timeout' => 60,
|
||||
'nice' => 0,
|
||||
],
|
||||
'supervisor-messaging' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['broadcasts', 'notifications'],
|
||||
'balance' => 'auto',
|
||||
'autoScalingStrategy' => 'time',
|
||||
'maxProcesses' => 2,
|
||||
'maxTime' => 0,
|
||||
'maxJobs' => 0,
|
||||
'memory' => 128,
|
||||
'tries' => 1,
|
||||
'timeout' => 90,
|
||||
'nice' => 0,
|
||||
],
|
||||
],
|
||||
|
||||
'environments' => [
|
||||
'production' => [
|
||||
'supervisor-default' => [
|
||||
'maxProcesses' => 10,
|
||||
'balanceMaxShift' => 1,
|
||||
'balanceCooldown' => 3,
|
||||
],
|
||||
'supervisor-messaging' => [
|
||||
'maxProcesses' => 6,
|
||||
'balanceMaxShift' => 1,
|
||||
'balanceCooldown' => 3,
|
||||
],
|
||||
],
|
||||
|
||||
'local' => [
|
||||
'supervisor-default' => [
|
||||
'maxProcesses' => 3,
|
||||
],
|
||||
'supervisor-messaging' => [
|
||||
'maxProcesses' => 2,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| File Watcher Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following list of directories and files will be watched when using
|
||||
| the `horizon:listen` command. Whenever any directories or files are
|
||||
| changed, Horizon will automatically restart to apply all changes.
|
||||
|
|
||||
*/
|
||||
|
||||
'watch' => [
|
||||
'app',
|
||||
'bootstrap',
|
||||
'config/**/*.php',
|
||||
'database/**/*.php',
|
||||
'public/**/*.php',
|
||||
'resources/**/*.php',
|
||||
'routes',
|
||||
'composer.lock',
|
||||
'composer.json',
|
||||
'.env',
|
||||
],
|
||||
];
|
||||
132
.deploy/artwork-evolution-release/config/logging.php
Normal file
132
.deploy/artwork-evolution-release/config/logging.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
use Monolog\Handler\NullHandler;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\SyslogUdpHandler;
|
||||
use Monolog\Processor\PsrLogMessageProcessor;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default log channel that is utilized to write
|
||||
| messages to your logs. The value provided here should match one of
|
||||
| the channels present in the list of "channels" configured below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Deprecations Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the log channel that should be used to log warnings
|
||||
| regarding deprecated PHP and library features. This allows you to get
|
||||
| your application ready for upcoming major versions of dependencies.
|
||||
|
|
||||
*/
|
||||
|
||||
'deprecations' => [
|
||||
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
|
||||
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Channels
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the log channels for your application. Laravel
|
||||
| utilizes the Monolog PHP logging library, which includes a variety
|
||||
| of powerful log handlers and formatters that you're free to use.
|
||||
|
|
||||
| Available drivers: "single", "daily", "slack", "syslog",
|
||||
| "errorlog", "monolog", "custom", "stack"
|
||||
|
|
||||
*/
|
||||
|
||||
'channels' => [
|
||||
|
||||
'stack' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => explode(',', (string) env('LOG_STACK', 'single')),
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'days' => env('LOG_DAILY_DAYS', 14),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'driver' => 'slack',
|
||||
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
||||
'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
|
||||
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
|
||||
'level' => env('LOG_LEVEL', 'critical'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'papertrail' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
|
||||
'handler_with' => [
|
||||
'host' => env('PAPERTRAIL_URL'),
|
||||
'port' => env('PAPERTRAIL_PORT'),
|
||||
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
|
||||
],
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'stderr' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => StreamHandler::class,
|
||||
'handler_with' => [
|
||||
'stream' => 'php://stderr',
|
||||
],
|
||||
'formatter' => env('LOG_STDERR_FORMATTER'),
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'errorlog' => [
|
||||
'driver' => 'errorlog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => NullHandler::class,
|
||||
],
|
||||
|
||||
'emergency' => [
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
118
.deploy/artwork-evolution-release/config/mail.php
Normal file
118
.deploy/artwork-evolution-release/config/mail.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Mailer
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default mailer that is used to send all email
|
||||
| messages unless another mailer is explicitly specified when sending
|
||||
| the message. All additional mailers can be configured within the
|
||||
| "mailers" array. Examples of each type of mailer are provided.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('MAIL_MAILER', 'log'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mailer Configurations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure all of the mailers used by your application plus
|
||||
| their respective settings. Several examples have been configured for
|
||||
| you and you are free to add your own as your application requires.
|
||||
|
|
||||
| Laravel supports a variety of mail "transport" drivers that can be used
|
||||
| when delivering an email. You may specify which one you're using for
|
||||
| your mailers below. You may also add additional mailers if needed.
|
||||
|
|
||||
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
|
||||
| "postmark", "resend", "log", "array",
|
||||
| "failover", "roundrobin"
|
||||
|
|
||||
*/
|
||||
|
||||
'mailers' => [
|
||||
|
||||
'smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'scheme' => env('MAIL_SCHEME'),
|
||||
'url' => env('MAIL_URL'),
|
||||
'host' => env('MAIL_HOST', '127.0.0.1'),
|
||||
'port' => env('MAIL_PORT', 2525),
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'timeout' => null,
|
||||
'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'transport' => 'ses',
|
||||
],
|
||||
|
||||
'postmark' => [
|
||||
'transport' => 'postmark',
|
||||
// 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
|
||||
// 'client' => [
|
||||
// 'timeout' => 5,
|
||||
// ],
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'transport' => 'resend',
|
||||
],
|
||||
|
||||
'sendmail' => [
|
||||
'transport' => 'sendmail',
|
||||
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'transport' => 'log',
|
||||
'channel' => env('MAIL_LOG_CHANNEL'),
|
||||
],
|
||||
|
||||
'array' => [
|
||||
'transport' => 'array',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'transport' => 'failover',
|
||||
'mailers' => [
|
||||
'smtp',
|
||||
'log',
|
||||
],
|
||||
'retry_after' => 60,
|
||||
],
|
||||
|
||||
'roundrobin' => [
|
||||
'transport' => 'roundrobin',
|
||||
'mailers' => [
|
||||
'ses',
|
||||
'postmark',
|
||||
],
|
||||
'retry_after' => 60,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global "From" Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may wish for all emails sent by your application to be sent from
|
||||
| the same address. Here you may specify a name and address that is
|
||||
| used globally for all emails that are sent by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'gregor@klevze.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Skinbase'),
|
||||
],
|
||||
|
||||
];
|
||||
38
.deploy/artwork-evolution-release/config/maturity.php
Normal file
38
.deploy/artwork-evolution-release/config/maturity.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'viewer' => [
|
||||
'default_mode' => env('MATURITY_DEFAULT_MODE', 'blur'),
|
||||
'default_warn_on_detail' => env('MATURITY_DEFAULT_WARN_ON_DETAIL', true),
|
||||
],
|
||||
|
||||
'ai' => [
|
||||
'threshold' => (float) env('MATURITY_AI_THRESHOLD', 0.68),
|
||||
'queue' => env('MATURITY_AI_QUEUE', env('VISION_QUEUE', 'default')),
|
||||
'strong_keywords' => [
|
||||
'adult',
|
||||
'bare breasts',
|
||||
'breasts',
|
||||
'explicit',
|
||||
'genitals',
|
||||
'lingerie',
|
||||
'nude',
|
||||
'nudity',
|
||||
'nsfw',
|
||||
'porn',
|
||||
'sex',
|
||||
'sexual',
|
||||
'topless',
|
||||
],
|
||||
'medium_keywords' => [
|
||||
'bikini',
|
||||
'erotic',
|
||||
'fetish',
|
||||
'intimate',
|
||||
'sensual',
|
||||
'underwear',
|
||||
],
|
||||
],
|
||||
];
|
||||
46
.deploy/artwork-evolution-release/config/messaging.php
Normal file
46
.deploy/artwork-evolution-release/config/messaging.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'realtime' => (bool) env('MESSAGING_REALTIME', false),
|
||||
|
||||
'broadcast' => [
|
||||
'queue' => env('MESSAGING_BROADCAST_QUEUE', 'broadcasts'),
|
||||
],
|
||||
|
||||
'typing' => [
|
||||
'ttl_seconds' => (int) env('MESSAGING_TYPING_TTL', 8),
|
||||
'cache_store' => env('MESSAGING_TYPING_CACHE_STORE', 'redis'),
|
||||
],
|
||||
|
||||
'presence' => [
|
||||
'ttl_seconds' => (int) env('MESSAGING_PRESENCE_TTL', 90),
|
||||
'conversation_ttl_seconds' => (int) env('MESSAGING_CONVERSATION_PRESENCE_TTL', 45),
|
||||
'cache_store' => env('MESSAGING_PRESENCE_CACHE_STORE', env('MESSAGING_TYPING_CACHE_STORE', 'redis')),
|
||||
],
|
||||
|
||||
'recovery' => [
|
||||
'max_messages' => (int) env('MESSAGING_RECOVERY_MAX_MESSAGES', 100),
|
||||
],
|
||||
|
||||
'notifications' => [
|
||||
'offline_fallback_only' => (bool) env('MESSAGING_OFFLINE_FALLBACK_ONLY', true),
|
||||
],
|
||||
|
||||
'search' => [
|
||||
'index' => env('MESSAGING_MEILI_INDEX', 'messages'),
|
||||
'page_size' => (int) env('MESSAGING_SEARCH_PAGE_SIZE', 20),
|
||||
],
|
||||
|
||||
'reactions' => [
|
||||
'allowed' => ['👍', '❤️', '🔥', '😂', '👏', '😮'],
|
||||
],
|
||||
|
||||
'attachments' => [
|
||||
'disk' => env('MESSAGING_ATTACHMENTS_DISK', 'local'),
|
||||
'max_files' => (int) env('MESSAGING_ATTACHMENTS_MAX_FILES', 5),
|
||||
'max_image_kb' => (int) env('MESSAGING_ATTACHMENTS_MAX_IMAGE_KB', 10240),
|
||||
'max_file_kb' => (int) env('MESSAGING_ATTACHMENTS_MAX_FILE_KB', 25600),
|
||||
'allowed_image_mimes' => ['image/jpeg', 'image/png', 'image/webp'],
|
||||
'allowed_file_mimes' => ['application/pdf', 'application/zip', 'application/x-zip-compressed'],
|
||||
],
|
||||
];
|
||||
387
.deploy/artwork-evolution-release/config/nova_cards.php
Normal file
387
.deploy/artwork-evolution-release/config/nova_cards.php
Normal file
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'brand' => [
|
||||
'name' => 'Nova Cards',
|
||||
'subtitle' => 'Create beautiful quote cards, mood cards, and visual text art.',
|
||||
],
|
||||
|
||||
'formats' => [
|
||||
'square' => ['width' => 1080, 'height' => 1080, 'label' => 'Square'],
|
||||
'portrait' => ['width' => 1080, 'height' => 1350, 'label' => 'Portrait'],
|
||||
'story' => ['width' => 1080, 'height' => 1920, 'label' => 'Story'],
|
||||
'landscape' => ['width' => 1920, 'height' => 1080, 'label' => 'Landscape'],
|
||||
],
|
||||
|
||||
'validation' => [
|
||||
'quote_min' => 3,
|
||||
'quote_max' => 420,
|
||||
'title_min' => 3,
|
||||
'title_max' => 120,
|
||||
'description_max' => 500,
|
||||
'max_tags' => 8,
|
||||
'max_decorations' => 6,
|
||||
'max_text_blocks' => 8,
|
||||
'max_asset_items' => 12,
|
||||
'max_background_upload_bytes' => 8 * 1024 * 1024,
|
||||
'allowed_background_mimes' => [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
],
|
||||
'allowed_alignments' => ['left', 'center', 'right'],
|
||||
'allowed_layouts' => ['quote_heavy', 'author_emphasis', 'centered', 'minimal'],
|
||||
'allowed_overlay_styles' => ['none', 'dark-soft', 'dark-strong', 'light-soft'],
|
||||
'allowed_positions' => ['top', 'upper-middle', 'center', 'lower-middle', 'bottom'],
|
||||
'allowed_blur_levels' => [0, 4, 8, 12, 20],
|
||||
'allowed_opacity_levels' => [20, 35, 50, 65, 80],
|
||||
'allowed_padding_presets' => ['tight', 'comfortable', 'airy'],
|
||||
'allowed_max_widths' => ['compact', 'balanced', 'wide'],
|
||||
],
|
||||
|
||||
'storage' => [
|
||||
'public_disk' => env('NOVA_CARDS_PUBLIC_DISK', 'public'),
|
||||
'private_disk' => env('NOVA_CARDS_PRIVATE_DISK', 'local'),
|
||||
'public_prefix' => 'cards',
|
||||
'background_original_prefix' => 'cards/backgrounds/original',
|
||||
'background_processed_prefix' => 'cards/backgrounds/processed',
|
||||
'preview_prefix' => 'cards/previews',
|
||||
],
|
||||
|
||||
'render' => [
|
||||
'queue' => env('NOVA_CARDS_QUEUE', 'default'),
|
||||
'preview_quality' => 86,
|
||||
'og_quality' => 88,
|
||||
'preview_format' => 'webp',
|
||||
'og_format' => 'jpg',
|
||||
// Directory containing TrueType font files named {preset}.ttf and default.ttf.
|
||||
'fonts_dir' => env('NOVA_CARDS_FONTS_DIR', storage_path('app/fonts')),
|
||||
],
|
||||
|
||||
// Set NOVA_CARDS_PLAYWRIGHT_RENDER=true to use the CSS/Playwright renderer
|
||||
// instead of the GD fallback. Requires Node.js + `npx playwright install chromium`.
|
||||
'playwright_render' => (bool) env('NOVA_CARDS_PLAYWRIGHT_RENDER', false),
|
||||
|
||||
'seed_demo_cards' => [
|
||||
'enabled' => (bool) env('NOVA_CARDS_SEED_DEMO_CARDS', false),
|
||||
'user' => [
|
||||
'username' => env('NOVA_CARDS_DEMO_USERNAME', 'nova.cards'),
|
||||
'name' => env('NOVA_CARDS_DEMO_NAME', 'Nova Cards'),
|
||||
'email' => env('NOVA_CARDS_DEMO_EMAIL', 'nova-cards-demo@skinbase.test'),
|
||||
'password' => env('NOVA_CARDS_DEMO_PASSWORD', 'password'),
|
||||
],
|
||||
],
|
||||
|
||||
'rate_limits' => [
|
||||
'drafts' => ['per_user' => 20, 'per_ip' => 40],
|
||||
'autosave' => ['per_user' => 120, 'per_ip' => 240],
|
||||
'publish' => ['per_user' => 12, 'per_ip' => 24],
|
||||
'background_upload' => ['per_user' => 30, 'per_ip' => 60],
|
||||
'render' => ['per_user' => 30, 'per_ip' => 60],
|
||||
],
|
||||
|
||||
'font_presets' => [
|
||||
'modern-sans' => [
|
||||
'label' => 'Modern Sans',
|
||||
'family' => 'Inter, sans-serif',
|
||||
'render_family' => 'arial',
|
||||
'weight' => '600',
|
||||
'recommended_use' => 'Clean motivational cards and bold statements.',
|
||||
],
|
||||
'elegant-serif' => [
|
||||
'label' => 'Elegant Serif',
|
||||
'family' => '"Playfair Display", serif',
|
||||
'render_family' => 'georgia',
|
||||
'weight' => '700',
|
||||
'recommended_use' => 'Classic quotes and romantic layouts.',
|
||||
],
|
||||
'bold-poster' => [
|
||||
'label' => 'Bold Poster',
|
||||
'family' => 'Anton, sans-serif',
|
||||
'render_family' => 'arial-black',
|
||||
'weight' => '700',
|
||||
'recommended_use' => 'High-contrast poster-like statements.',
|
||||
],
|
||||
'soft-handwritten' => [
|
||||
'label' => 'Soft Handwritten',
|
||||
'family' => 'Caveat, cursive',
|
||||
'render_family' => 'comic-sans',
|
||||
'weight' => '400',
|
||||
'recommended_use' => 'Warm, intimate, diary-like cards.',
|
||||
],
|
||||
'minimal-editorial' => [
|
||||
'label' => 'Minimal Editorial',
|
||||
'family' => '"Libre Franklin", sans-serif',
|
||||
'render_family' => 'trebuchet',
|
||||
'weight' => '600',
|
||||
'recommended_use' => 'Minimal editorial compositions.',
|
||||
],
|
||||
'dreamy-aesthetic' => [
|
||||
'label' => 'Dreamy Aesthetic',
|
||||
'family' => '"Cormorant Garamond", serif',
|
||||
'render_family' => 'palatino',
|
||||
'weight' => '600',
|
||||
'recommended_use' => 'Poetry, wallpaper quotes, and soft mood cards.',
|
||||
],
|
||||
],
|
||||
|
||||
'gradient_presets' => [
|
||||
'midnight-nova' => ['label' => 'Midnight Nova', 'colors' => ['#0f172a', '#1d4ed8']],
|
||||
'soft-pastel' => ['label' => 'Soft Pastel', 'colors' => ['#f9a8d4', '#c4b5fd']],
|
||||
'amber-glow' => ['label' => 'Amber Glow', 'colors' => ['#451a03', '#f59e0b']],
|
||||
'emerald-bloom' => ['label' => 'Emerald Bloom', 'colors' => ['#052e16', '#34d399']],
|
||||
'romantic-dusk' => ['label' => 'Romantic Dusk', 'colors' => ['#4c0519', '#fb7185']],
|
||||
'deep-cinema' => ['label' => 'Deep Cinema', 'colors' => ['#020617', '#334155']],
|
||||
'dream-glow' => ['label' => 'Dream Glow', 'colors' => ['#312e81', '#ec4899']],
|
||||
'nature-calm' => ['label' => 'Nature Calm', 'colors' => ['#064e3b', '#93c5fd']],
|
||||
],
|
||||
|
||||
'decor_presets' => [
|
||||
['key' => 'sparkle-cluster', 'label' => 'Sparkles', 'glyph' => '✦'],
|
||||
['key' => 'soft-heart', 'label' => 'Heart', 'glyph' => '♥'],
|
||||
['key' => 'moon-stars', 'label' => 'Moon', 'glyph' => '☾'],
|
||||
['key' => 'minimal-dot', 'label' => 'Dot', 'glyph' => '•'],
|
||||
['key' => 'asterisk-burst', 'label' => 'Burst', 'glyph' => '✷'],
|
||||
],
|
||||
|
||||
'asset_packs' => [
|
||||
[
|
||||
'slug' => 'official-celestial',
|
||||
'name' => 'Official Celestial Pack',
|
||||
'description' => 'Sparkles, moons, and accent glyphs tuned for dreamy Nova Cards compositions.',
|
||||
'type' => 'asset',
|
||||
'official' => true,
|
||||
'active' => true,
|
||||
'manifest_json' => [
|
||||
'items' => [
|
||||
['key' => 'nova-sparkle', 'label' => 'Nova Sparkle', 'type' => 'glyph', 'glyph' => '✦'],
|
||||
['key' => 'nova-moon', 'label' => 'Moon Accent', 'type' => 'glyph', 'glyph' => '☾'],
|
||||
['key' => 'nova-heart', 'label' => 'Soft Heart', 'type' => 'glyph', 'glyph' => '♥'],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'official-editorial-frames',
|
||||
'name' => 'Editorial Frame Pack',
|
||||
'description' => 'Minimal frame treatments and lines for poster-style cards.',
|
||||
'type' => 'asset',
|
||||
'official' => true,
|
||||
'active' => true,
|
||||
'manifest_json' => [
|
||||
'items' => [
|
||||
['key' => 'line-top', 'label' => 'Top Line', 'type' => 'frame'],
|
||||
['key' => 'line-bottom', 'label' => 'Bottom Line', 'type' => 'frame'],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'template_packs' => [
|
||||
[
|
||||
'slug' => 'official-story-pack',
|
||||
'name' => 'Official Story Pack',
|
||||
'description' => 'Tall layouts optimized for punchy story cards and serial prompts.',
|
||||
'type' => 'template',
|
||||
'official' => true,
|
||||
'active' => true,
|
||||
'manifest_json' => [
|
||||
'templates' => ['story-vertical', 'dream-glow'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'official-editorial-pack',
|
||||
'name' => 'Official Editorial Pack',
|
||||
'description' => 'Minimal serif and poster templates for headline-driven cards.',
|
||||
'type' => 'template',
|
||||
'official' => true,
|
||||
'active' => true,
|
||||
'manifest_json' => [
|
||||
'templates' => ['classic-typography', 'golden-serif'],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'background_modes' => [
|
||||
['key' => 'template', 'label' => 'Template Base'],
|
||||
['key' => 'gradient', 'label' => 'Gradient'],
|
||||
['key' => 'solid', 'label' => 'Solid'],
|
||||
['key' => 'upload', 'label' => 'Upload'],
|
||||
],
|
||||
|
||||
'layout_presets' => [
|
||||
['key' => 'quote_heavy', 'label' => 'Quote Heavy'],
|
||||
['key' => 'author_emphasis', 'label' => 'Author Emphasis'],
|
||||
['key' => 'centered', 'label' => 'Centered'],
|
||||
['key' => 'minimal', 'label' => 'Minimal'],
|
||||
],
|
||||
|
||||
'alignment_presets' => [
|
||||
['key' => 'left', 'label' => 'Left'],
|
||||
['key' => 'center', 'label' => 'Center'],
|
||||
['key' => 'right', 'label' => 'Right'],
|
||||
],
|
||||
|
||||
'position_presets' => [
|
||||
['key' => 'top', 'label' => 'Top'],
|
||||
['key' => 'upper-middle', 'label' => 'Upper Middle'],
|
||||
['key' => 'center', 'label' => 'Center'],
|
||||
['key' => 'lower-middle', 'label' => 'Lower Middle'],
|
||||
['key' => 'bottom', 'label' => 'Bottom'],
|
||||
],
|
||||
|
||||
'padding_presets' => [
|
||||
['key' => 'tight', 'label' => 'Tight'],
|
||||
['key' => 'comfortable', 'label' => 'Comfortable'],
|
||||
['key' => 'airy', 'label' => 'Airy'],
|
||||
],
|
||||
|
||||
'max_width_presets' => [
|
||||
['key' => 'compact', 'label' => 'Compact'],
|
||||
['key' => 'balanced', 'label' => 'Balanced'],
|
||||
['key' => 'wide', 'label' => 'Wide'],
|
||||
],
|
||||
|
||||
'line_height_presets' => [
|
||||
['key' => 'tight', 'label' => 'Tight', 'value' => 1.05],
|
||||
['key' => 'balanced', 'label' => 'Balanced', 'value' => 1.2],
|
||||
['key' => 'airy', 'label' => 'Airy', 'value' => 1.38],
|
||||
],
|
||||
|
||||
'shadow_presets' => [
|
||||
['key' => 'none', 'label' => 'None'],
|
||||
['key' => 'soft', 'label' => 'Soft'],
|
||||
['key' => 'strong', 'label' => 'Strong'],
|
||||
],
|
||||
|
||||
'focal_positions' => [
|
||||
['key' => 'center', 'label' => 'Center'],
|
||||
['key' => 'top', 'label' => 'Top'],
|
||||
['key' => 'bottom', 'label' => 'Bottom'],
|
||||
['key' => 'left', 'label' => 'Left'],
|
||||
['key' => 'right', 'label' => 'Right'],
|
||||
['key' => 'top-left', 'label' => 'Top Left'],
|
||||
['key' => 'top-right', 'label' => 'Top Right'],
|
||||
['key' => 'bottom-left', 'label' => 'Bottom Left'],
|
||||
['key' => 'bottom-right', 'label' => 'Bottom Right'],
|
||||
],
|
||||
|
||||
'starter_templates' => [
|
||||
'minimal-black-white',
|
||||
'neon-nova',
|
||||
'soft-pastel',
|
||||
'romantic',
|
||||
'cinematic-dark',
|
||||
'golden-serif',
|
||||
'nature-calm',
|
||||
'bold-statement',
|
||||
'wallpaper-style',
|
||||
'story-vertical',
|
||||
'dream-glow',
|
||||
'classic-typography',
|
||||
],
|
||||
|
||||
// v3 additions
|
||||
|
||||
'quote_mark_presets' => [
|
||||
['key' => 'none', 'label' => 'None'],
|
||||
['key' => 'classic', 'label' => 'Classic "…"'],
|
||||
['key' => 'guillemets', 'label' => 'Guillemets «…»'],
|
||||
['key' => 'large-open', 'label' => 'Large Open "'],
|
||||
['key' => 'minimal-dash', 'label' => 'Dash —'],
|
||||
['key' => 'decorative-star', 'label' => 'Star ✦'],
|
||||
],
|
||||
|
||||
'text_panel_styles' => [
|
||||
['key' => 'none', 'label' => 'None'],
|
||||
['key' => 'frosted', 'label' => 'Frosted Glass'],
|
||||
['key' => 'dark-solid', 'label' => 'Dark Solid'],
|
||||
['key' => 'light-solid', 'label' => 'Light Solid'],
|
||||
['key' => 'outline', 'label' => 'Outline'],
|
||||
['key' => 'editorial-rule', 'label' => 'Editorial Rule'],
|
||||
],
|
||||
|
||||
'frame_presets' => [
|
||||
['key' => 'none', 'label' => 'None'],
|
||||
['key' => 'thin-border', 'label' => 'Thin Border'],
|
||||
['key' => 'double-line', 'label' => 'Double Line'],
|
||||
['key' => 'corner-marks', 'label' => 'Corner Marks'],
|
||||
['key' => 'editorial-top', 'label' => 'Editorial Top Rule'],
|
||||
['key' => 'editorial-both', 'label' => 'Editorial Top & Bottom Rules'],
|
||||
['key' => 'rounded-card', 'label' => 'Rounded Card'],
|
||||
],
|
||||
|
||||
'color_grade_presets' => [
|
||||
['key' => 'none', 'label' => 'None'],
|
||||
['key' => 'warm', 'label' => 'Warm'],
|
||||
['key' => 'cool', 'label' => 'Cool'],
|
||||
['key' => 'cinematic', 'label' => 'Cinematic'],
|
||||
['key' => 'muted', 'label' => 'Muted'],
|
||||
['key' => 'vivid', 'label' => 'Vivid'],
|
||||
['key' => 'noir', 'label' => 'Noir'],
|
||||
['key' => 'golden-hour', 'label' => 'Golden Hour'],
|
||||
],
|
||||
|
||||
'effect_presets' => [
|
||||
['key' => 'none', 'label' => 'None'],
|
||||
['key' => 'grain', 'label' => 'Film Grain'],
|
||||
['key' => 'vignette', 'label' => 'Vignette'],
|
||||
['key' => 'noise', 'label' => 'Noise'],
|
||||
['key' => 'halftone', 'label' => 'Halftone'],
|
||||
['key' => 'glitch', 'label' => 'Glitch'],
|
||||
['key' => 'bokeh', 'label' => 'Bokeh'],
|
||||
],
|
||||
|
||||
'style_families' => [
|
||||
['key' => 'aesthetic', 'label' => 'Aesthetic'],
|
||||
['key' => 'editorial', 'label' => 'Editorial'],
|
||||
['key' => 'cinematic', 'label' => 'Cinematic'],
|
||||
['key' => 'minimal', 'label' => 'Minimal'],
|
||||
['key' => 'bold', 'label' => 'Bold'],
|
||||
['key' => 'romantic', 'label' => 'Romantic'],
|
||||
['key' => 'dark', 'label' => 'Dark'],
|
||||
['key' => 'pastel', 'label' => 'Pastel'],
|
||||
['key' => 'retro', 'label' => 'Retro'],
|
||||
['key' => 'nature', 'label' => 'Nature'],
|
||||
],
|
||||
|
||||
'palette_families' => [
|
||||
['key' => 'monochrome', 'label' => 'Monochrome'],
|
||||
['key' => 'warm-tones', 'label' => 'Warm Tones'],
|
||||
['key' => 'cool-tones', 'label' => 'Cool Tones'],
|
||||
['key' => 'earth-tones', 'label' => 'Earth Tones'],
|
||||
['key' => 'pastels', 'label' => 'Pastels'],
|
||||
['key' => 'neons', 'label' => 'Neons'],
|
||||
['key' => 'jewel-tones', 'label' => 'Jewel Tones'],
|
||||
],
|
||||
|
||||
'mood_families' => [
|
||||
['key' => 'romantic', 'label' => 'Romantic', 'tag_slugs' => ['romantic', 'love', 'tender']],
|
||||
['key' => 'dark-poetry', 'label' => 'Dark Poetry', 'tag_slugs' => ['dark', 'shadow', 'night']],
|
||||
['key' => 'inspirational', 'label' => 'Inspirational', 'tag_slugs' => ['inspire', 'hope', 'believe']],
|
||||
['key' => 'soft-morning', 'label' => 'Soft Morning', 'tag_slugs' => ['calm', 'morning', 'peace']],
|
||||
['key' => 'minimal', 'label' => 'Minimal', 'tag_slugs' => ['minimal', 'quiet', 'still']],
|
||||
['key' => 'motivational', 'label' => 'Motivational', 'tag_slugs' => ['power', 'bold', 'fire']],
|
||||
['key' => 'cyber-mood', 'label' => 'Cyber Mood', 'tag_slugs' => ['cyber', 'neon', 'digital']],
|
||||
],
|
||||
|
||||
'seasonal_hubs' => [
|
||||
['key' => 'spring', 'label' => 'Spring', 'tag_slugs' => ['spring', 'bloom', 'floral']],
|
||||
['key' => 'summer', 'label' => 'Summer', 'tag_slugs' => ['summer', 'sunlight', 'vacation']],
|
||||
['key' => 'autumn', 'label' => 'Autumn', 'tag_slugs' => ['autumn', 'harvest', 'amber']],
|
||||
['key' => 'winter', 'label' => 'Winter', 'tag_slugs' => ['winter', 'snow', 'frost']],
|
||||
['key' => 'holiday', 'label' => 'Holiday', 'tag_slugs' => ['holiday', 'festive', 'celebration']],
|
||||
],
|
||||
|
||||
'export_formats' => [
|
||||
'preview' => ['width' => 1080, 'height' => 1080, 'format' => 'webp', 'label' => 'Preview (WebP)'],
|
||||
'hires' => ['width' => 2160, 'height' => 2160, 'format' => 'png', 'label' => 'Hi-Res PNG'],
|
||||
'square' => ['width' => 1080, 'height' => 1080, 'format' => 'png', 'label' => 'Square PNG'],
|
||||
'story' => ['width' => 1080, 'height' => 1920, 'format' => 'png', 'label' => 'Story PNG'],
|
||||
'wallpaper' => ['width' => 1920, 'height' => 1080, 'format' => 'png', 'label' => 'Wallpaper PNG'],
|
||||
'og' => ['width' => 1200, 'height' => 630, 'format' => 'jpg', 'label' => 'OG Image (JPG)'],
|
||||
],
|
||||
|
||||
'export_ttl_hours' => (int) env('NOVA_CARDS_EXPORT_TTL_HOURS', 48),
|
||||
];
|
||||
129
.deploy/artwork-evolution-release/config/queue.php
Normal file
129
.deploy/artwork-evolution-release/config/queue.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Queue Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Laravel's queue supports a variety of backends via a single, unified
|
||||
| API, giving you convenient access to each backend using identical
|
||||
| syntax for each. The default queue connection is defined below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_CONNECTION', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the connection options for every queue backend
|
||||
| used by your application. An example configuration is provided for
|
||||
| each backend supported by Laravel. You're also free to add more.
|
||||
|
|
||||
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis",
|
||||
| "deferred", "background", "failover", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sync' => [
|
||||
'driver' => 'sync',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_QUEUE_CONNECTION'),
|
||||
'table' => env('DB_QUEUE_TABLE', 'jobs'),
|
||||
'queue' => env('DB_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'beanstalkd' => [
|
||||
'driver' => 'beanstalkd',
|
||||
'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
|
||||
'queue' => env('BEANSTALKD_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => 0,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'sqs' => [
|
||||
'driver' => 'sqs',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
|
||||
'queue' => env('SQS_QUEUE', 'default'),
|
||||
'suffix' => env('SQS_SUFFIX'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
|
||||
'queue' => env('REDIS_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => null,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'deferred' => [
|
||||
'driver' => 'deferred',
|
||||
],
|
||||
|
||||
'background' => [
|
||||
'driver' => 'background',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'driver' => 'failover',
|
||||
'connections' => [
|
||||
'database',
|
||||
'deferred',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Job Batching
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following options configure the database and table that store job
|
||||
| batching information. These options can be updated to any database
|
||||
| connection and table which has been defined by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'batching' => [
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'job_batches',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Failed Queue Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options configure the behavior of failed queue job logging so you
|
||||
| can control how and where failed jobs are stored. Laravel ships with
|
||||
| support for storing failed jobs in a simple file or in a database.
|
||||
|
|
||||
| Supported drivers: "database-uuids", "dynamodb", "file", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => [
|
||||
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
||||
];
|
||||
93
.deploy/artwork-evolution-release/config/ranking.php
Normal file
93
.deploy/artwork-evolution-release/config/ranking.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Ranking system configuration — Skinbase Nova rank_v1
|
||||
*
|
||||
* All weights, half-lives, and thresholds are tunable here.
|
||||
* Increment model_version when changing weights so caches expire gracefully.
|
||||
*/
|
||||
return [
|
||||
|
||||
// ── Model versioning ────────────────────────────────────────────────────
|
||||
'model_version' => 'rank_v2',
|
||||
|
||||
// ── Engagement signal weights (log-scaled) ──────────────────────────────
|
||||
'weights' => [
|
||||
'views' => 1.0,
|
||||
'favourites' => 3.0,
|
||||
'downloads' => 2.5,
|
||||
],
|
||||
|
||||
// ── V2 ranking formula weights ──────────────────────────────────────────
|
||||
'v2' => [
|
||||
// Base engagement weights (linear, applied to raw counts)
|
||||
'weights' => [
|
||||
'views' => 0.2,
|
||||
'downloads' => 1.5,
|
||||
'favourites' => 2.5,
|
||||
'comments' => 3.0,
|
||||
'shares' => 4.0, // highest — viral intent
|
||||
],
|
||||
|
||||
// 24h velocity weights
|
||||
'velocity_weights' => [
|
||||
'views' => 1.0,
|
||||
'favourites' => 3.0,
|
||||
'comments' => 4.0,
|
||||
'shares' => 5.0,
|
||||
],
|
||||
|
||||
// Velocity multiplier applied to the 24h velocity sum
|
||||
'velocity_multiplier' => 0.5,
|
||||
|
||||
// Recency decay half-life in hours
|
||||
'half_life' => 48,
|
||||
|
||||
// Author authority: multiplier = 1 + (authority * factor)
|
||||
'authority_factor' => 0.05,
|
||||
'authority_fav_divisor' => 1000,
|
||||
],
|
||||
|
||||
// ── Time-decay half-lives (hours) ───────────────────────────────────────
|
||||
'half_life' => [
|
||||
'trending' => 72, // Explore / global trending
|
||||
'new_hot' => 36, // New & Hot novelty feed
|
||||
'best' => 720, // Evergreen / Best-of (30 days)
|
||||
'category' => 96, // Per-category trending
|
||||
],
|
||||
|
||||
// ── Novelty boost (New & Hot only) ──────────────────────────────────────
|
||||
'novelty_weight' => 0.35,
|
||||
|
||||
// ── Quality modifiers ───────────────────────────────────────────────────
|
||||
'quality' => [
|
||||
'has_tags' => 0.05,
|
||||
'has_thumbnail' => 0.02,
|
||||
'tag_count_max' => 10,
|
||||
'tag_count_bonus' => 0.01, // per normalised tag fraction (max 0.01 total)
|
||||
'penalty_hidden' => 0.50, // deducted if hidden/inactive
|
||||
],
|
||||
|
||||
// ── Diversity constraints ────────────────────────────────────────────────
|
||||
'diversity' => [
|
||||
'max_per_author' => 3,
|
||||
'list_size' => 50,
|
||||
'candidate_pool' => 200, // top N candidates to run diversity filter on
|
||||
],
|
||||
|
||||
// ── Anti-spam / burst-view damping ──────────────────────────────────────
|
||||
'spam' => [
|
||||
'views_24h_threshold' => 2000,
|
||||
'fav_ratio_threshold' => 0.002,
|
||||
'dl_ratio_threshold' => 0.001,
|
||||
'trending_penalty_factor' => 0.5,
|
||||
],
|
||||
|
||||
// ── Redis cache ─────────────────────────────────────────────────────────
|
||||
'cache' => [
|
||||
'ttl' => 900, // seconds (15 min) — lists are rebuilt hourly
|
||||
'prefix' => 'rank',
|
||||
],
|
||||
];
|
||||
135
.deploy/artwork-evolution-release/config/recommendations.php
Normal file
135
.deploy/artwork-evolution-release/config/recommendations.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
// Uses same queue family as vision jobs by default; keeps embedding work async and non-blocking.
|
||||
'queue' => env('RECOMMENDATIONS_QUEUE', env('VISION_QUEUE', 'default')),
|
||||
|
||||
// ─── Phase 1 "For You" feed scoring weights ───────────────────────────────
|
||||
// Influences the PHP reranking pass after Meilisearch candidate retrieval.
|
||||
// Tweak here without code changes.
|
||||
'weights' => [
|
||||
// Tag overlap score weight (0–1 normalized overlap fraction)
|
||||
'tag_overlap' => (float) env('RECO_W_TAG_OVERLAP', 0.40),
|
||||
// Creator affinity score weight (1.0 if followed, 0 otherwise)
|
||||
'creator_affinity' => (float) env('RECO_W_CREATOR_AFFINITY', 0.25),
|
||||
// Popularity boost (log-normalised views/downloads)
|
||||
'popularity' => (float) env('RECO_W_POPULARITY', 0.20),
|
||||
// Freshness boost (exponential decay over 30 days)
|
||||
'freshness' => (float) env('RECO_W_FRESHNESS', 0.15),
|
||||
],
|
||||
|
||||
// ─── User preference signal weights ──────────────────────────────────────
|
||||
// How much each user action contributes to building the reco profile.
|
||||
'signal_weights' => [
|
||||
'award' => (float) env('RECO_SIG_AWARD', 5.0),
|
||||
'favorite' => (float) env('RECO_SIG_FAVORITE', 3.0),
|
||||
'reaction' => (float) env('RECO_SIG_REACTION', 2.0),
|
||||
'view' => (float) env('RECO_SIG_VIEW', 1.0),
|
||||
'follow' => (float) env('RECO_SIG_FOLLOW', 2.0),
|
||||
],
|
||||
|
||||
// ─── Candidate generation ──────────────────────────────────────────────────
|
||||
// How many Meilisearch candidates to fetch before PHP reranking.
|
||||
'candidate_pool_size' => (int) env('RECO_CANDIDATE_POOL', 200),
|
||||
|
||||
// ─── Diversity controls ────────────────────────────────────────────────────
|
||||
// Maximum artworks per creator allowed in a single page of results.
|
||||
'max_per_creator' => (int) env('RECO_MAX_PER_CREATOR', 3),
|
||||
// Minimum distinct tag count in first 20 feed results.
|
||||
'min_unique_tags' => (int) env('RECO_MIN_UNIQUE_TAGS', 5),
|
||||
|
||||
// ─── TTLs (seconds) ────────────────────────────────────────────────────────
|
||||
'ttl' => [
|
||||
// User reco profile cache (tag/creator affinity data)
|
||||
'user_reco_profile' => (int) env('RECO_TTL_PROFILE', 6 * 3600),
|
||||
// For You paginated results cache
|
||||
'for_you_feed' => (int) env('RECO_TTL_FOR_YOU', 5 * 60),
|
||||
// Similar artworks per artwork
|
||||
'similar_artworks' => (int) env('RECO_TTL_SIMILAR', 30 * 60),
|
||||
// Suggested creators per user
|
||||
'creator_suggestions' => (int) env('RECO_TTL_CREATORS', 30 * 60),
|
||||
// Suggested tags per user
|
||||
'tag_suggestions' => (int) env('RECO_TTL_TAGS', 60 * 60),
|
||||
],
|
||||
|
||||
// ─── Profile limits ────────────────────────────────────────────────────────
|
||||
'profile' => [
|
||||
'top_tags_limit' => (int) env('RECO_PROFILE_TAGS', 20),
|
||||
'top_categories_limit' => (int) env('RECO_PROFILE_CATS', 5),
|
||||
'strong_creators_limit' => (int) env('RECO_PROFILE_CREATORS', 50),
|
||||
],
|
||||
|
||||
'embedding' => [
|
||||
'enabled' => env('RECOMMENDATIONS_EMBEDDING_ENABLED', true),
|
||||
'model' => env('RECOMMENDATIONS_EMBEDDING_MODEL', 'clip'),
|
||||
'model_version' => env('RECOMMENDATIONS_EMBEDDING_MODEL_VERSION', 'v1'),
|
||||
'algo_version' => env('RECOMMENDATIONS_ALGO_VERSION', 'clip-cosine-v1'),
|
||||
|
||||
// Preferred CLIP endpoint for embeddings. The service also accepts an embedding payload from the analyze endpoint response.
|
||||
'endpoint' => env('CLIP_EMBED_ENDPOINT', '/embed'),
|
||||
'timeout_seconds' => (int) env('CLIP_EMBED_TIMEOUT_SECONDS', 8),
|
||||
'connect_timeout_seconds' => (int) env('CLIP_EMBED_CONNECT_TIMEOUT_SECONDS', 2),
|
||||
'retries' => (int) env('CLIP_EMBED_HTTP_RETRIES', 1),
|
||||
'retry_delay_ms' => (int) env('CLIP_EMBED_HTTP_RETRY_DELAY_MS', 200),
|
||||
|
||||
// Guardrails for malformed service responses.
|
||||
'min_dim' => (int) env('RECOMMENDATIONS_MIN_DIM', 64),
|
||||
'max_dim' => (int) env('RECOMMENDATIONS_MAX_DIM', 4096),
|
||||
],
|
||||
|
||||
// Backfill chunk size for resumable queue fan-out.
|
||||
'backfill_batch_size' => (int) env('RECOMMENDATIONS_BACKFILL_BATCH', 200),
|
||||
|
||||
// A/B support for recommendation ranking variants.
|
||||
'ab' => [
|
||||
'algo_versions' => array_values(array_filter(array_map(
|
||||
static fn (string $value): string => trim($value),
|
||||
explode(',', (string) env('RECOMMENDATIONS_AB_ALGO_VERSIONS', env('RECOMMENDATIONS_ALGO_VERSION', 'clip-cosine-v1')))
|
||||
))),
|
||||
],
|
||||
|
||||
// ─── Similar Artworks (hybrid recommender) ─────────────────────────────────
|
||||
'similarity' => [
|
||||
'model_version' => env('SIMILARITY_MODEL_VERSION', 'sim_v1'),
|
||||
|
||||
// Vector DB integration (behind feature flag)
|
||||
'vector_enabled' => (bool) env('SIMILARITY_VECTOR_ENABLED', false),
|
||||
'vector_adapter' => env('SIMILARITY_VECTOR_ADAPTER', 'pgvector'), // pgvector | pinecone
|
||||
|
||||
// Hybrid blend weights (spec §5.4)
|
||||
'weights_with_vector' => [
|
||||
'visual' => (float) env('SIM_W_VISUAL', 0.45),
|
||||
'tag' => (float) env('SIM_W_TAG_VEC', 0.25),
|
||||
'behavior' => (float) env('SIM_W_BEH_VEC', 0.20),
|
||||
'category' => (float) env('SIM_W_CAT_VEC', 0.10),
|
||||
],
|
||||
'weights_without_vector' => [
|
||||
'tag' => (float) env('SIM_W_TAG', 0.55),
|
||||
'behavior' => (float) env('SIM_W_BEH', 0.35),
|
||||
'category' => (float) env('SIM_W_CAT', 0.10),
|
||||
],
|
||||
|
||||
// Diversity caps (spec §6)
|
||||
'max_per_author' => (int) env('SIM_MAX_PER_AUTHOR', 2),
|
||||
'result_limit' => (int) env('SIM_RESULT_LIMIT', 30),
|
||||
'candidate_pool' => (int) env('SIM_CANDIDATE_POOL', 100),
|
||||
'min_categories_top12' => (int) env('SIM_MIN_CATS_TOP12', 2),
|
||||
|
||||
// Behavior pair building
|
||||
'user_favourites_cap' => (int) env('SIM_USER_FAV_CAP', 50),
|
||||
|
||||
// Cache TTL for precomputed lists (sec)
|
||||
'cache_ttl' => (int) env('SIM_CACHE_TTL', 6 * 3600),
|
||||
|
||||
// Pinecone adapter settings
|
||||
'pinecone' => [
|
||||
'api_key' => env('PINECONE_API_KEY'),
|
||||
'index_host' => env('PINECONE_INDEX_HOST'),
|
||||
'index_name' => env('PINECONE_INDEX_NAME', 'skinbase-artworks'),
|
||||
'namespace' => env('PINECONE_NAMESPACE', ''),
|
||||
'top_k' => (int) env('PINECONE_TOP_K', 100),
|
||||
],
|
||||
],
|
||||
];
|
||||
16
.deploy/artwork-evolution-release/config/registration.php
Normal file
16
.deploy/artwork-evolution-release/config/registration.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'ip_per_minute_limit' => (int) env('REGISTRATION_IP_PER_MINUTE_LIMIT', 3),
|
||||
'ip_per_day_limit' => (int) env('REGISTRATION_IP_PER_DAY_LIMIT', 20),
|
||||
'email_per_minute_limit' => (int) env('REGISTRATION_EMAIL_PER_MINUTE_LIMIT', 6),
|
||||
'email_cooldown_minutes' => (int) env('REGISTRATION_EMAIL_COOLDOWN_MINUTES', 30),
|
||||
'verify_token_ttl_hours' => (int) env('REGISTRATION_VERIFY_TOKEN_TTL_HOURS', 24),
|
||||
'enable_turnstile' => (bool) env('REGISTRATION_ENABLE_TURNSTILE', true),
|
||||
'disposable_domains_enabled' => (bool) env('REGISTRATION_DISPOSABLE_DOMAINS_ENABLED', true),
|
||||
'turnstile_suspicious_attempts' => (int) env('REGISTRATION_TURNSTILE_SUSPICIOUS_ATTEMPTS', 2),
|
||||
'turnstile_attempt_window_minutes' => (int) env('REGISTRATION_TURNSTILE_ATTEMPT_WINDOW_MINUTES', 30),
|
||||
'email_global_send_per_minute' => (int) env('REGISTRATION_EMAIL_GLOBAL_SEND_PER_MINUTE', 30),
|
||||
'monthly_email_limit' => (int) env('REGISTRATION_MONTHLY_EMAIL_LIMIT', 10000),
|
||||
'generic_success_message' => 'If that email is valid, we sent a verification link.',
|
||||
];
|
||||
103
.deploy/artwork-evolution-release/config/reverb.php
Normal file
103
.deploy/artwork-evolution-release/config/reverb.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Reverb Server
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default server used by Reverb to handle
|
||||
| incoming messages as well as broadcasting message to all your
|
||||
| connected clients. At this time only "reverb" is supported.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('REVERB_SERVER', 'reverb'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Reverb Servers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define details for each of the supported Reverb servers.
|
||||
| Each server has its own configuration options that are defined in
|
||||
| the array below. You should ensure all the options are present.
|
||||
|
|
||||
*/
|
||||
|
||||
'servers' => [
|
||||
|
||||
'reverb' => [
|
||||
'host' => env('REVERB_SERVER_HOST', '0.0.0.0'),
|
||||
'port' => env('REVERB_SERVER_PORT', 8080),
|
||||
'path' => env('REVERB_SERVER_PATH', ''),
|
||||
'hostname' => env('REVERB_HOST'),
|
||||
'options' => [
|
||||
'tls' => [],
|
||||
],
|
||||
'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000),
|
||||
'scaling' => [
|
||||
'enabled' => env('REVERB_SCALING_ENABLED', false),
|
||||
'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
|
||||
'server' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
'timeout' => env('REDIS_TIMEOUT', 60),
|
||||
],
|
||||
],
|
||||
'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15),
|
||||
'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 15),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Reverb Applications
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define how Reverb applications are managed. If you choose
|
||||
| to use the "config" provider, you may define an array of apps which
|
||||
| your server will support, including their connection credentials.
|
||||
|
|
||||
*/
|
||||
|
||||
'apps' => [
|
||||
|
||||
'provider' => 'config',
|
||||
|
||||
'apps' => [
|
||||
[
|
||||
'key' => env('REVERB_APP_KEY'),
|
||||
'secret' => env('REVERB_APP_SECRET'),
|
||||
'app_id' => env('REVERB_APP_ID'),
|
||||
'options' => [
|
||||
'host' => env('REVERB_HOST'),
|
||||
'port' => env('REVERB_PORT', 443),
|
||||
'scheme' => env('REVERB_SCHEME', 'https'),
|
||||
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
|
||||
],
|
||||
'allowed_origins' => [
|
||||
'skinbase.org',
|
||||
'www.skinbase.org',
|
||||
'ws.skinbase.org',
|
||||
'skinbase.top',
|
||||
'www.skinbase.top',
|
||||
'skinbase26.test'
|
||||
],
|
||||
'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60),
|
||||
'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 30),
|
||||
'max_connections' => env('REVERB_APP_MAX_CONNECTIONS'),
|
||||
'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000),
|
||||
'accept_client_events_from' => env('REVERB_APP_ACCEPT_CLIENT_EVENTS_FROM', 'members'),
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
172
.deploy/artwork-evolution-release/config/scout.php
Normal file
172
.deploy/artwork-evolution-release/config/scout.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Search Engine
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'driver' => env('SCOUT_DRIVER', 'meilisearch'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Index Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'prefix' => env('SCOUT_PREFIX', env('MEILI_PREFIX', '')),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Data Syncing
|
||||
|--------------------------------------------------------------------------
|
||||
| Always queue Scout index operations so they never block HTTP requests.
|
||||
*/
|
||||
|
||||
'queue' => [
|
||||
'connection' => env('SCOUT_QUEUE_CONNECTION', env('QUEUE_CONNECTION', 'redis')),
|
||||
'queue' => env('SCOUT_QUEUE_NAME', 'search'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Transactions
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'after_commit' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Chunk Sizes
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'chunk' => [
|
||||
'searchable' => 500,
|
||||
'unsearchable' => 500,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Soft Deletes
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'soft_delete' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Identify User
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'identify' => env('SCOUT_IDENTIFY', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Meilisearch
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'meilisearch' => [
|
||||
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
|
||||
'key' => env('MEILISEARCH_KEY'),
|
||||
|
||||
/*
|
||||
| Index-level settings pushed via: php artisan scout:sync-index-settings
|
||||
*/
|
||||
'index-settings' => [
|
||||
/*
|
||||
* Key must match the full Meilisearch index name (prefix + model index).
|
||||
* Prefix is controlled by MEILI_PREFIX / SCOUT_PREFIX env variable.
|
||||
* Default local dev: 'artworks'. Production: 'skinbase_prod_artworks'.
|
||||
*/
|
||||
env('SCOUT_PREFIX', env('MEILI_PREFIX', '')) . 'artworks' => [
|
||||
'searchableAttributes' => [
|
||||
'title',
|
||||
'tags',
|
||||
'author_name',
|
||||
'description',
|
||||
],
|
||||
'filterableAttributes' => [
|
||||
'id',
|
||||
'tags',
|
||||
'category',
|
||||
'content_type',
|
||||
'orientation',
|
||||
'resolution',
|
||||
'author_id',
|
||||
'is_public',
|
||||
'is_approved',
|
||||
'has_missing_thumbnails',
|
||||
'created_at',
|
||||
],
|
||||
'sortableAttributes' => [
|
||||
'created_at',
|
||||
'published_at_ts',
|
||||
'missing_thumbnail_rank',
|
||||
'downloads',
|
||||
'likes',
|
||||
'views',
|
||||
'trending_score_24h',
|
||||
'trending_score_7d',
|
||||
'favorites_count',
|
||||
'awards_received_count',
|
||||
'awards_score_7d',
|
||||
'awards_score_30d',
|
||||
'downloads_count',
|
||||
'ranking_score',
|
||||
'shares_count',
|
||||
'engagement_velocity',
|
||||
'comments_count',
|
||||
'heat_score',
|
||||
],
|
||||
'rankingRules' => [
|
||||
'words',
|
||||
'typo',
|
||||
'proximity',
|
||||
'attribute',
|
||||
'sort',
|
||||
'exactness',
|
||||
],
|
||||
'typoTolerance' => [
|
||||
'enabled' => true,
|
||||
'minWordSizeForTypos' => [
|
||||
'oneTypo' => 4,
|
||||
'twoTypos' => 8,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
env('SCOUT_PREFIX', env('MEILI_PREFIX', '')) . 'messages' => [
|
||||
'searchableAttributes' => [
|
||||
'body_text',
|
||||
'sender_username',
|
||||
],
|
||||
'filterableAttributes' => [
|
||||
'conversation_id',
|
||||
'sender_id',
|
||||
'has_attachments',
|
||||
],
|
||||
'sortableAttributes' => [
|
||||
'created_at',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Algolia (unused but required by package)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'algolia' => [
|
||||
'id' => env('ALGOLIA_APP_ID', ''),
|
||||
'secret' => env('ALGOLIA_SECRET', ''),
|
||||
],
|
||||
|
||||
];
|
||||
15
.deploy/artwork-evolution-release/config/seo.php
Normal file
15
.deploy/artwork-evolution-release/config/seo.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'site_name' => env('SEO_SITE_NAME', 'Skinbase'),
|
||||
'default_title' => env('SEO_DEFAULT_TITLE', 'Skinbase'),
|
||||
'title_separator' => env('SEO_TITLE_SEPARATOR', ' — '),
|
||||
'default_description' => env(
|
||||
'SEO_DEFAULT_DESCRIPTION',
|
||||
'Discover digital art, wallpapers, skins, photography, and creator collections from the Skinbase community.'
|
||||
),
|
||||
'default_robots' => env('SEO_DEFAULT_ROBOTS', 'index,follow'),
|
||||
'keywords_enabled' => (bool) env('SEO_META_KEYWORDS', true),
|
||||
'twitter_card' => env('SEO_TWITTER_CARD', 'summary_large_image'),
|
||||
'fallback_image_path' => env('SEO_FALLBACK_IMAGE_PATH', '/gfx/skinbase_back_001.webp'),
|
||||
];
|
||||
92
.deploy/artwork-evolution-release/config/services.php
Normal file
92
.deploy/artwork-evolution-release/config/services.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Third Party Services
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is for storing the credentials for third party services such
|
||||
| as Mailgun, Postmark, AWS and more. This file provides the de facto
|
||||
| location for this type of information, allowing packages to have
|
||||
| a conventional file to locate the various service credentials.
|
||||
|
|
||||
*/
|
||||
|
||||
'postmark' => [
|
||||
'key' => env('POSTMARK_API_KEY'),
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'key' => env('RESEND_API_KEY'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'notifications' => [
|
||||
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
|
||||
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
|
||||
],
|
||||
],
|
||||
|
||||
'image' => [
|
||||
'driver' => env('IMAGE_DRIVER', 'gd'),
|
||||
],
|
||||
|
||||
'recaptcha' => [
|
||||
'enabled' => env('RECAPTCHA_ENABLED', false),
|
||||
'site_key' => env('RECAPTCHA_SITE_KEY'),
|
||||
'secret' => env('RECAPTCHA_SECRET_KEY'),
|
||||
'script_url' => env('RECAPTCHA_SCRIPT_URL', 'https://www.google.com/recaptcha/api.js'),
|
||||
'verify_url' => env('RECAPTCHA_VERIFY_URL', 'https://www.google.com/recaptcha/api/siteverify'),
|
||||
'timeout' => (int) env('RECAPTCHA_TIMEOUT', 5),
|
||||
],
|
||||
|
||||
'hcaptcha' => [
|
||||
'enabled' => env('HCAPTCHA_ENABLED', false),
|
||||
'site_key' => env('HCAPTCHA_SITE_KEY'),
|
||||
'secret' => env('HCAPTCHA_SECRET_KEY'),
|
||||
'script_url' => env('HCAPTCHA_SCRIPT_URL', 'https://js.hcaptcha.com/1/api.js'),
|
||||
'verify_url' => env('HCAPTCHA_VERIFY_URL', 'https://hcaptcha.com/siteverify'),
|
||||
'timeout' => (int) env('HCAPTCHA_TIMEOUT', 5),
|
||||
],
|
||||
|
||||
'turnstile' => [
|
||||
'site_key' => env('TURNSTILE_SITE_KEY'),
|
||||
'secret_key' => env('TURNSTILE_SECRET_KEY'),
|
||||
'script_url' => env('TURNSTILE_SCRIPT_URL', 'https://challenges.cloudflare.com/turnstile/v0/api.js'),
|
||||
'verify_url' => env('TURNSTILE_VERIFY_URL', 'https://challenges.cloudflare.com/turnstile/v0/siteverify'),
|
||||
'timeout' => (int) env('TURNSTILE_TIMEOUT', 5),
|
||||
],
|
||||
|
||||
// ── OAuth providers ──────────────────────────────────────────────────────
|
||||
|
||||
'google' => [
|
||||
'client_id' => env('GOOGLE_CLIENT_ID'),
|
||||
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
|
||||
'redirect' => env('GOOGLE_REDIRECT_URI', '/auth/google/callback'),
|
||||
],
|
||||
|
||||
'discord' => [
|
||||
'client_id' => env('DISCORD_CLIENT_ID'),
|
||||
'client_secret' => env('DISCORD_CLIENT_SECRET'),
|
||||
'redirect' => env('DISCORD_REDIRECT_URI', '/auth/discord/callback'),
|
||||
],
|
||||
|
||||
|
||||
/*
|
||||
* Google AdSense
|
||||
* Set GOOGLE_ADSENSE_PUBLISHER_ID to your ca-pub-XXXXXXXXXXXXXXXX value.
|
||||
* Ads are only loaded after the user accepts cookies via the consent banner.
|
||||
*/
|
||||
'google_adsense' => [
|
||||
'publisher_id' => env('GOOGLE_ADSENSE_PUBLISHER_ID'),
|
||||
],
|
||||
|
||||
];
|
||||
217
.deploy/artwork-evolution-release/config/session.php
Normal file
217
.deploy/artwork-evolution-release/config/session.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Session Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines the default session driver that is utilized for
|
||||
| incoming requests. Laravel supports a variety of storage options to
|
||||
| persist session data. Database storage is a great default choice.
|
||||
|
|
||||
| Supported: "file", "cookie", "database", "memcached",
|
||||
| "redis", "dynamodb", "array"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Lifetime
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the number of minutes that you wish the session
|
||||
| to be allowed to remain idle before it expires. If you want them
|
||||
| to expire immediately when the browser is closed then you may
|
||||
| indicate that via the expire_on_close configuration option.
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => (int) env('SESSION_LIFETIME', 120),
|
||||
|
||||
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Encryption
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to easily specify that all of your session data
|
||||
| should be encrypted before it's stored. All encryption is performed
|
||||
| automatically by Laravel and you may use the session like normal.
|
||||
|
|
||||
*/
|
||||
|
||||
'encrypt' => env('SESSION_ENCRYPT', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session File Location
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the "file" session driver, the session files are placed
|
||||
| on disk. The default storage location is defined here; however, you
|
||||
| are free to provide another location where they should be stored.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => storage_path('framework/sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" or "redis" session drivers, you may specify a
|
||||
| connection that should be used to manage these sessions. This should
|
||||
| correspond to a connection in your database configuration options.
|
||||
|
|
||||
*/
|
||||
|
||||
'connection' => env('SESSION_CONNECTION'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" session driver, you may specify the table to
|
||||
| be used to store sessions. Of course, a sensible default is defined
|
||||
| for you; however, you're welcome to change this to another table.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => env('SESSION_TABLE', 'sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using one of the framework's cache driven session backends, you may
|
||||
| define the cache store which should be used to store the session data
|
||||
| between requests. This must match one of your defined cache stores.
|
||||
|
|
||||
| Affects: "dynamodb", "memcached", "redis"
|
||||
|
|
||||
*/
|
||||
|
||||
'store' => env('SESSION_STORE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Sweeping Lottery
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some session drivers must manually sweep their storage location to get
|
||||
| rid of old sessions from storage. Here are the chances that it will
|
||||
| happen on a given request. By default, the odds are 2 out of 100.
|
||||
|
|
||||
*/
|
||||
|
||||
'lottery' => [2, 100],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the name of the session cookie that is created by
|
||||
| the framework. Typically, you should not need to change this value
|
||||
| since doing so does not grant a meaningful security improvement.
|
||||
|
|
||||
*/
|
||||
|
||||
'cookie' => env(
|
||||
'SESSION_COOKIE',
|
||||
Str::slug((string) env('APP_NAME', 'laravel')).'-session'
|
||||
),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The session cookie path determines the path for which the cookie will
|
||||
| be regarded as available. Typically, this will be the root path of
|
||||
| your application, but you're free to change this when necessary.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => env('SESSION_PATH', '/'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the domain and subdomains the session cookie is
|
||||
| available to. By default, the cookie will be available to the root
|
||||
| domain without subdomains. Typically, this shouldn't be changed.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('SESSION_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTPS Only Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By setting this option to true, session cookies will only be sent back
|
||||
| to the server if the browser has a HTTPS connection. This will keep
|
||||
| the cookie from being sent to you when it can't be done securely.
|
||||
|
|
||||
*/
|
||||
|
||||
'secure' => env('SESSION_SECURE_COOKIE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTP Access Only
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will prevent JavaScript from accessing the
|
||||
| value of the cookie and the cookie will only be accessible through
|
||||
| the HTTP protocol. It's unlikely you should disable this option.
|
||||
|
|
||||
*/
|
||||
|
||||
'http_only' => env('SESSION_HTTP_ONLY', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Same-Site Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines how your cookies behave when cross-site requests
|
||||
| take place, and can be used to mitigate CSRF attacks. By default, we
|
||||
| will set this value to "lax" to permit secure cross-site requests.
|
||||
|
|
||||
| See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
|
||||
|
|
||||
| Supported: "lax", "strict", "none", null
|
||||
|
|
||||
*/
|
||||
|
||||
'same_site' => env('SESSION_SAME_SITE', 'lax'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Partitioned Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will tie the cookie to the top-level site for
|
||||
| a cross-site context. Partitioned cookies are accepted by the browser
|
||||
| when flagged "secure" and the Same-Site attribute is set to "none".
|
||||
|
|
||||
*/
|
||||
|
||||
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
|
||||
|
||||
];
|
||||
111
.deploy/artwork-evolution-release/config/sitemaps.php
Normal file
111
.deploy/artwork-evolution-release/config/sitemaps.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'cache_ttl_seconds' => (int) env('SITEMAPS_CACHE_TTL', 900),
|
||||
|
||||
'refresh' => [
|
||||
'build_on_request' => (bool) env('SITEMAPS_BUILD_ON_REQUEST', true),
|
||||
],
|
||||
|
||||
'delivery' => [
|
||||
'prefer_published_release' => (bool) env('SITEMAPS_PREFER_PUBLISHED_RELEASE', true),
|
||||
'fallback_to_live_build' => (bool) env('SITEMAPS_FALLBACK_TO_LIVE_BUILD', true),
|
||||
],
|
||||
|
||||
'pre_generated' => [
|
||||
'enabled' => (bool) env('SITEMAPS_PREGENERATED_ENABLED', true),
|
||||
'prefer' => (bool) env('SITEMAPS_PREGENERATED_PREFER', false),
|
||||
'disk' => env('SITEMAPS_PREGENERATED_DISK', 'local'),
|
||||
'path' => trim((string) env('SITEMAPS_PREGENERATED_PATH', 'generated-sitemaps'), '/'),
|
||||
],
|
||||
|
||||
'releases' => [
|
||||
'disk' => env('SITEMAPS_RELEASES_DISK', 'local'),
|
||||
'path' => trim((string) env('SITEMAPS_RELEASES_PATH', 'sitemaps'), '/'),
|
||||
'retain_successful' => (int) env('SITEMAPS_RELEASES_RETAIN_SUCCESSFUL', 3),
|
||||
'retain_failed' => (int) env('SITEMAPS_RELEASES_RETAIN_FAILED', 2),
|
||||
'lock_seconds' => (int) env('SITEMAPS_RELEASES_LOCK_SECONDS', 900),
|
||||
],
|
||||
|
||||
'shards' => [
|
||||
'enabled' => (bool) env('SITEMAPS_SHARDS_ENABLED', true),
|
||||
'zero_pad_length' => (int) env('SITEMAPS_SHARD_ZERO_PAD_LENGTH', 4),
|
||||
'force_family_indexes' => (bool) env('SITEMAPS_SHARD_FORCE_FAMILY_INDEXES', false),
|
||||
'artworks' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_ARTWORKS_SIZE', 10000),
|
||||
],
|
||||
'users' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_USERS_SIZE', 10000),
|
||||
],
|
||||
'cards' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_CARDS_SIZE', 10000),
|
||||
],
|
||||
'stories' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_STORIES_SIZE', 10000),
|
||||
],
|
||||
'news' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_NEWS_SIZE', 0),
|
||||
],
|
||||
'forum-threads' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_FORUM_THREADS_SIZE', 10000),
|
||||
],
|
||||
'collections' => [
|
||||
'size' => (int) env('SITEMAPS_SHARD_COLLECTIONS_SIZE', 10000),
|
||||
],
|
||||
],
|
||||
|
||||
'validation' => [
|
||||
'forbidden_paths' => [
|
||||
'/admin',
|
||||
'/cp',
|
||||
'/dashboard',
|
||||
'/studio',
|
||||
'/account',
|
||||
'/login',
|
||||
'/register',
|
||||
'/creator/',
|
||||
],
|
||||
],
|
||||
|
||||
'news' => [
|
||||
'google_variant_enabled' => (bool) env('SITEMAPS_NEWS_GOOGLE_VARIANT', true),
|
||||
'google_variant_name' => 'news-google',
|
||||
'google_publication_name' => env('SITEMAPS_NEWS_GOOGLE_PUBLICATION', env('APP_NAME', 'Skinbase Nova')),
|
||||
'google_language' => env('SITEMAPS_NEWS_GOOGLE_LANGUAGE', env('APP_LOCALE', 'en')),
|
||||
'google_lookback_hours' => (int) env('SITEMAPS_NEWS_GOOGLE_LOOKBACK_HOURS', 48),
|
||||
'google_max_items' => (int) env('SITEMAPS_NEWS_GOOGLE_MAX_ITEMS', 1000),
|
||||
],
|
||||
|
||||
'enabled' => [
|
||||
'artworks',
|
||||
'users',
|
||||
'tags',
|
||||
'categories',
|
||||
'collections',
|
||||
'cards',
|
||||
'stories',
|
||||
'news',
|
||||
'news-google',
|
||||
'forum-index',
|
||||
'forum-categories',
|
||||
'forum-threads',
|
||||
'static-pages',
|
||||
],
|
||||
|
||||
'content_type_slugs' => [
|
||||
'photography',
|
||||
'wallpapers',
|
||||
'skins',
|
||||
'other',
|
||||
'digital-art',
|
||||
],
|
||||
|
||||
'static_page_excluded_slugs' => [
|
||||
'about',
|
||||
'help',
|
||||
'contact',
|
||||
'legal-terms',
|
||||
'legal-privacy',
|
||||
'legal-cookies',
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enabled' => (bool) env('SKINBASE_COUNTRIES_ENABLED', true),
|
||||
'remote_source' => env('SKINBASE_COUNTRIES_REMOTE_SOURCE', 'restcountries'),
|
||||
'endpoint' => env(
|
||||
'SKINBASE_COUNTRIES_ENDPOINT',
|
||||
'https://restcountries.com/v3.1/all?fields=cca2,cca3,ccn3,name,region,subregion,flags,flag'
|
||||
),
|
||||
'connect_timeout' => (int) env('SKINBASE_COUNTRIES_CONNECT_TIMEOUT', 5),
|
||||
'timeout' => (int) env('SKINBASE_COUNTRIES_TIMEOUT', 10),
|
||||
'retry_times' => (int) env('SKINBASE_COUNTRIES_RETRY_TIMES', 2),
|
||||
'retry_sleep_ms' => (int) env('SKINBASE_COUNTRIES_RETRY_SLEEP_MS', 250),
|
||||
'deactivate_missing' => (bool) env('SKINBASE_COUNTRIES_DEACTIVATE_MISSING', false),
|
||||
'cache_ttl' => (int) env('SKINBASE_COUNTRIES_CACHE_TTL', 86400),
|
||||
'featured_countries' => array_values(array_filter(array_map(
|
||||
static fn (string $iso2): string => strtoupper(trim($iso2)),
|
||||
explode(',', (string) env('SKINBASE_COUNTRIES_FEATURED', 'SI,HR,AT,DE,IT,US')),
|
||||
))),
|
||||
'use_local_flags' => (bool) env('SKINBASE_COUNTRIES_USE_LOCAL_FLAGS', true),
|
||||
'fallback_seed_enabled' => (bool) env('SKINBASE_COUNTRIES_FALLBACK_ENABLED', true),
|
||||
'fallback_seed_path' => database_path('data/countries-fallback.json'),
|
||||
];
|
||||
22
.deploy/artwork-evolution-release/config/skinbase.php
Normal file
22
.deploy/artwork-evolution-release/config/skinbase.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'reserved_usernames' => [
|
||||
'admin',
|
||||
'administrator',
|
||||
'support',
|
||||
'staff',
|
||||
'system',
|
||||
'root',
|
||||
'api',
|
||||
'cdn',
|
||||
'upload',
|
||||
'settings',
|
||||
'login',
|
||||
'logout',
|
||||
'register',
|
||||
'skinbase',
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enabled' => env('SKINBASE_AI_MODERATION_ENABLED', true),
|
||||
|
||||
'provider' => env('SKINBASE_AI_MODERATION_PROVIDER', 'openai'),
|
||||
|
||||
'queue' => [
|
||||
'name' => env('SKINBASE_AI_MODERATION_QUEUE', 'forum-moderation'),
|
||||
],
|
||||
|
||||
'preflight' => [
|
||||
'run_ai_sync' => (bool) env('SKINBASE_AI_PREFLIGHT_SYNC', true),
|
||||
],
|
||||
|
||||
'thresholds' => [
|
||||
'safe' => 20,
|
||||
'low_quality' => 40,
|
||||
'suspicious' => 60,
|
||||
'block' => 80,
|
||||
],
|
||||
|
||||
'behavior' => [
|
||||
'new_account_days' => 7,
|
||||
'rapid_post_window_minutes' => 2,
|
||||
'rapid_post_threshold' => 5,
|
||||
'same_ip_window_days' => 7,
|
||||
'same_ip_accounts_threshold' => 2,
|
||||
'repeat_content_penalty' => 40,
|
||||
'new_account_with_links_penalty' => 30,
|
||||
'rapid_post_penalty' => 20,
|
||||
'same_ip_penalty' => 25,
|
||||
'high_link_frequency_penalty' => 10,
|
||||
'flagged_history_penalty' => 15,
|
||||
],
|
||||
|
||||
'links' => [
|
||||
'too_many_links_penalty' => 15,
|
||||
'suspicious_domain_penalty' => 40,
|
||||
'shortener_penalty' => 10,
|
||||
'suspicious_tld_penalty' => 15,
|
||||
'too_many_links_threshold' => 3,
|
||||
'shorteners' => [
|
||||
'bit.ly',
|
||||
'tinyurl.com',
|
||||
'goo.gl',
|
||||
't.co',
|
||||
'ow.ly',
|
||||
'cutt.ly',
|
||||
'rebrand.ly',
|
||||
],
|
||||
'suspicious_tlds' => [
|
||||
'xyz',
|
||||
'top',
|
||||
'click',
|
||||
'loan',
|
||||
'work',
|
||||
'gq',
|
||||
'ml',
|
||||
'tk',
|
||||
],
|
||||
],
|
||||
|
||||
'trust' => [
|
||||
'high' => 80,
|
||||
'medium' => 50,
|
||||
'high_modifier' => -15,
|
||||
'medium_modifier' => -8,
|
||||
'low_modifier' => 0,
|
||||
'flagged_ratio_penalty' => 10,
|
||||
],
|
||||
|
||||
'learning' => [
|
||||
'spam_penalty' => 40,
|
||||
'safe_modifier' => -12,
|
||||
'max_spam_penalty' => 60,
|
||||
'max_safe_modifier' => -20,
|
||||
],
|
||||
|
||||
'scan' => [
|
||||
'limit' => 200,
|
||||
'stale_after_minutes' => 10,
|
||||
],
|
||||
|
||||
'privacy' => [
|
||||
'redact_emails' => true,
|
||||
'redact_ip_addresses' => true,
|
||||
'redact_mentions' => false,
|
||||
],
|
||||
|
||||
'heuristics' => [
|
||||
'promotional_phrases' => [
|
||||
'buy now',
|
||||
'limited offer',
|
||||
'cheap seo',
|
||||
'guaranteed traffic',
|
||||
'visit my profile',
|
||||
'work from home',
|
||||
'crypto signal',
|
||||
'telegram me',
|
||||
'whatsapp me',
|
||||
'dm for service',
|
||||
],
|
||||
'toxic_phrases' => [
|
||||
'kill yourself',
|
||||
'you idiot',
|
||||
'piece of trash',
|
||||
'hate you',
|
||||
'worthless',
|
||||
],
|
||||
],
|
||||
|
||||
'providers' => [
|
||||
'openai' => [
|
||||
'api_key' => env('OPENAI_API_KEY'),
|
||||
'base_url' => env('OPENAI_BASE_URL', 'https://api.openai.com/v1'),
|
||||
'model' => env('SKINBASE_AI_OPENAI_MODEL', 'gpt-4.1-mini'),
|
||||
'timeout' => (int) env('SKINBASE_AI_OPENAI_TIMEOUT', 5),
|
||||
],
|
||||
'perspective_api' => [
|
||||
'api_key' => env('PERSPECTIVE_API_KEY'),
|
||||
'base_url' => env('PERSPECTIVE_API_BASE_URL', 'https://commentanalyzer.googleapis.com/v1alpha1/comments:analyze'),
|
||||
'timeout' => (int) env('SKINBASE_AI_PERSPECTIVE_TIMEOUT', 5),
|
||||
],
|
||||
'local_llm' => [
|
||||
'endpoint' => env('SKINBASE_AI_LOCAL_LLM_ENDPOINT'),
|
||||
'model' => env('SKINBASE_AI_LOCAL_LLM_MODEL', 'moderation'),
|
||||
'timeout' => (int) env('SKINBASE_AI_LOCAL_LLM_TIMEOUT', 5),
|
||||
'token' => env('SKINBASE_AI_LOCAL_LLM_TOKEN'),
|
||||
],
|
||||
],
|
||||
];
|
||||
28
.deploy/artwork-evolution-release/config/tags.php
Normal file
28
.deploy/artwork-evolution-release/config/tags.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Tag system configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Keep moderation/banning logic in config (not hardcoded in code).
|
||||
| Populate these lists in production as needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'max_length' => 32,
|
||||
'max_user_tags' => 15,
|
||||
|
||||
// Exact-match banned tags after normalization.
|
||||
'banned' => [
|
||||
// e.g. 'nsfw', 'hate', 'spam'
|
||||
],
|
||||
|
||||
// Optional regex patterns (PCRE) to block tags.
|
||||
'banned_regex' => [
|
||||
// e.g. '/\\b(?:badword1|badword2)\\b/i'
|
||||
],
|
||||
];
|
||||
140
.deploy/artwork-evolution-release/config/uploads.php
Normal file
140
.deploy/artwork-evolution-release/config/uploads.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'storage_root' => env('SKINBASE_STORAGE_ROOT', storage_path('app/artworks')),
|
||||
|
||||
'local_originals_root' => env('ARTWORKS_LOCAL_ORIGINALS_ROOT', storage_path('app/originals/artworks')),
|
||||
|
||||
'object_storage' => [
|
||||
'disk' => env('ARTWORKS_OBJECT_DISK', 's3'),
|
||||
'prefix' => env('ARTWORKS_OBJECT_PREFIX', 'artworks'),
|
||||
],
|
||||
|
||||
'paths' => [
|
||||
'tmp' => 'tmp',
|
||||
'quarantine' => 'quarantine',
|
||||
'original' => 'original',
|
||||
'xs' => 'xs',
|
||||
'sm' => 'sm',
|
||||
'md' => 'md',
|
||||
'lg' => 'lg',
|
||||
'xl' => 'xl',
|
||||
'sq' => 'sq',
|
||||
],
|
||||
|
||||
'max_size_mb' => 50,
|
||||
'max_archive_size_mb' => 200,
|
||||
'max_pixels' => 12000,
|
||||
|
||||
'allowed_mimes' => [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
],
|
||||
|
||||
'allow_gif' => env('UPLOAD_ALLOW_GIF', false),
|
||||
|
||||
'allowed_archive_mimes' => [
|
||||
'application/zip',
|
||||
'application/x-zip-compressed',
|
||||
'application/x-rar-compressed',
|
||||
'application/vnd.rar',
|
||||
'application/x-7z-compressed',
|
||||
'application/x-tar',
|
||||
'application/gzip',
|
||||
'application/x-gzip',
|
||||
'application/octet-stream',
|
||||
],
|
||||
|
||||
'derivatives' => [
|
||||
'xs' => ['max' => 320],
|
||||
'sm' => ['max' => 680],
|
||||
'md' => ['max' => 1024],
|
||||
'lg' => ['max' => 1920],
|
||||
'xl' => ['max' => 2560],
|
||||
'sq' => ['size' => 512],
|
||||
],
|
||||
|
||||
'square_thumbnails' => [
|
||||
'width' => env('UPLOAD_SQ_WIDTH', 512),
|
||||
'height' => env('UPLOAD_SQ_HEIGHT', 512),
|
||||
'quality' => env('UPLOAD_SQ_QUALITY', 82),
|
||||
'smart_crop' => env('UPLOAD_SQ_SMART_CROP', true),
|
||||
'padding_ratio' => env('UPLOAD_SQ_PADDING_RATIO', 0.18),
|
||||
'allow_upscale' => env('UPLOAD_SQ_ALLOW_UPSCALE', false),
|
||||
'fallback_strategy' => env('UPLOAD_SQ_FALLBACK_STRATEGY', 'center'),
|
||||
'log' => env('UPLOAD_SQ_LOG', false),
|
||||
'preview_size' => env('UPLOAD_PREVIEW_SQ_SIZE', 320),
|
||||
'subject_detector' => [
|
||||
'preferred_labels' => [
|
||||
'person',
|
||||
'portrait',
|
||||
'face',
|
||||
'human',
|
||||
'animal',
|
||||
'lion',
|
||||
'dog',
|
||||
'cat',
|
||||
'bird',
|
||||
],
|
||||
],
|
||||
'saliency' => [
|
||||
'sample_max_dimension' => env('UPLOAD_SQ_SALIENCY_SAMPLE_MAX', 96),
|
||||
'min_total_energy' => env('UPLOAD_SQ_SALIENCY_MIN_TOTAL', 2400),
|
||||
'window_ratios' => [0.55, 0.7, 0.82, 1.0],
|
||||
],
|
||||
],
|
||||
|
||||
'quality' => 85,
|
||||
|
||||
'queue_derivatives' => env('UPLOAD_QUEUE_DERIVATIVES', false),
|
||||
|
||||
'rate_limits' => [
|
||||
'decay_minutes' => env('UPLOAD_RATE_DECAY_MINUTES', 1),
|
||||
'init' => [
|
||||
'per_user' => env('UPLOAD_RATE_INIT_USER', 10),
|
||||
'per_ip' => env('UPLOAD_RATE_INIT_IP', 30),
|
||||
],
|
||||
'chunk' => [
|
||||
'per_user' => env('UPLOAD_RATE_CHUNK_USER', 180),
|
||||
'per_ip' => env('UPLOAD_RATE_CHUNK_IP', 360),
|
||||
],
|
||||
'finish' => [
|
||||
'per_user' => env('UPLOAD_RATE_FINISH_USER', 6),
|
||||
'per_ip' => env('UPLOAD_RATE_FINISH_IP', 12),
|
||||
],
|
||||
'status' => [
|
||||
'per_user' => env('UPLOAD_RATE_STATUS_USER', 60),
|
||||
'per_ip' => env('UPLOAD_RATE_STATUS_IP', 120),
|
||||
],
|
||||
],
|
||||
|
||||
'quotas' => [
|
||||
'max_active_sessions' => env('UPLOAD_MAX_ACTIVE_SESSIONS', 100),
|
||||
'max_daily_sessions' => env('UPLOAD_MAX_DAILY_SESSIONS', 250),
|
||||
],
|
||||
|
||||
'draft_quota' => [
|
||||
'max_drafts_per_user' => env('SKINBASE_MAX_DRAFTS', 10),
|
||||
'max_draft_storage_mb_per_user' => env('SKINBASE_MAX_DRAFT_STORAGE_MB', 1024),
|
||||
'duplicate_hash_policy' => env('SKINBASE_DUPLICATE_HASH_POLICY', 'block'), // block|warn
|
||||
],
|
||||
|
||||
'tokens' => [
|
||||
'ttl_minutes' => env('UPLOAD_TOKEN_TTL_MINUTES', 60),
|
||||
],
|
||||
|
||||
'chunk' => [
|
||||
'max_bytes' => env('UPLOAD_CHUNK_MAX_BYTES', 5242880),
|
||||
'lock_seconds' => env('UPLOAD_CHUNK_LOCK_SECONDS', 10),
|
||||
'lock_wait_seconds' => env('UPLOAD_CHUNK_LOCK_WAIT_SECONDS', 5),
|
||||
'request_timeout_ms' => env('UPLOAD_CHUNK_REQUEST_TIMEOUT_MS', 45000),
|
||||
],
|
||||
|
||||
'scan' => [
|
||||
'enabled' => env('UPLOAD_SCAN_ENABLED', false),
|
||||
'command' => env('UPLOAD_SCAN_COMMAND', []),
|
||||
],
|
||||
];
|
||||
44
.deploy/artwork-evolution-release/config/usernames.php
Normal file
44
.deploy/artwork-evolution-release/config/usernames.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'min' => 3,
|
||||
'max' => 20,
|
||||
'regex' => '/^[a-zA-Z0-9_]{3,20}$/',
|
||||
'rename_cooldown_days' => 30,
|
||||
'similarity_threshold' => 2,
|
||||
'reserved' => [
|
||||
'admin',
|
||||
'root',
|
||||
'support',
|
||||
'staff',
|
||||
'moderator',
|
||||
'mod',
|
||||
'system',
|
||||
'api',
|
||||
'www',
|
||||
'mail',
|
||||
'ftp',
|
||||
'skinbase',
|
||||
'official',
|
||||
'help',
|
||||
'security',
|
||||
'login',
|
||||
'register',
|
||||
'auth',
|
||||
'dashboard',
|
||||
'settings',
|
||||
'forum',
|
||||
'gallery',
|
||||
'upload',
|
||||
'search',
|
||||
'static',
|
||||
'cdn',
|
||||
'assets',
|
||||
'images',
|
||||
'profile',
|
||||
'user',
|
||||
'users',
|
||||
],
|
||||
];
|
||||
91
.deploy/artwork-evolution-release/config/vision.php
Normal file
91
.deploy/artwork-evolution-release/config/vision.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'enabled' => env('VISION_ENABLED', true),
|
||||
|
||||
'queue' => env('VISION_QUEUE', 'default'),
|
||||
|
||||
'clip' => [
|
||||
'base_url' => env('CLIP_BASE_URL', ''),
|
||||
'endpoint' => env('CLIP_ANALYZE_ENDPOINT', '/analyze'),
|
||||
'api_key' => env('CLIP_API_KEY', env('VISION_API_KEY', env('VISION_VECTOR_GATEWAY_API_KEY', ''))),
|
||||
'timeout_seconds' => (int) env('CLIP_TIMEOUT_SECONDS', 8),
|
||||
'connect_timeout_seconds' => (int) env('CLIP_CONNECT_TIMEOUT_SECONDS', 2),
|
||||
'retries' => (int) env('CLIP_HTTP_RETRIES', 1),
|
||||
'retry_delay_ms' => (int) env('CLIP_HTTP_RETRY_DELAY_MS', 200),
|
||||
],
|
||||
|
||||
'yolo' => [
|
||||
'enabled' => env('YOLO_ENABLED', true),
|
||||
'base_url' => env('YOLO_BASE_URL', ''),
|
||||
'endpoint' => env('YOLO_ANALYZE_ENDPOINT', '/analyze'),
|
||||
'api_key' => env('YOLO_API_KEY', env('VISION_API_KEY', env('VISION_VECTOR_GATEWAY_API_KEY', ''))),
|
||||
'timeout_seconds' => (int) env('YOLO_TIMEOUT_SECONDS', 8),
|
||||
'connect_timeout_seconds' => (int) env('YOLO_CONNECT_TIMEOUT_SECONDS', 2),
|
||||
'retries' => (int) env('YOLO_HTTP_RETRIES', 1),
|
||||
'retry_delay_ms' => (int) env('YOLO_HTTP_RETRY_DELAY_MS', 200),
|
||||
|
||||
// Only run YOLO for photography content type.
|
||||
'photography_only' => env('YOLO_PHOTOGRAPHY_ONLY', true),
|
||||
],
|
||||
|
||||
// Which derivative variant to send to vision services.
|
||||
'image_variant' => env('VISION_IMAGE_VARIANT', 'md'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Vision Gateway (aggregates CLIP + BLIP + YOLO via /analyze/all)
|
||||
|--------------------------------------------------------------------------
|
||||
| Falls back to CLIP base_url when VISION_GATEWAY_URL is not set.
|
||||
*/
|
||||
'gateway' => [
|
||||
'base_url' => env('VISION_GATEWAY_URL', env('CLIP_BASE_URL', '')),
|
||||
'api_key' => env('VISION_GATEWAY_API_KEY', env('VISION_API_KEY', env('VISION_VECTOR_GATEWAY_API_KEY', ''))),
|
||||
'timeout_seconds' => (int) env('VISION_GATEWAY_TIMEOUT', 60),
|
||||
'connect_timeout_seconds'=> (int) env('VISION_GATEWAY_CONNECT_TIMEOUT', 3),
|
||||
],
|
||||
|
||||
'maturity' => [
|
||||
'base_url' => env('VISION_MATURITY_URL', env('VISION_GATEWAY_URL', env('CLIP_BASE_URL', ''))),
|
||||
'endpoint' => env('VISION_MATURITY_ENDPOINT', '/analyze/maturity'),
|
||||
'file_endpoint' => env('VISION_MATURITY_FILE_ENDPOINT', '/analyze/maturity/file'),
|
||||
'api_key' => env('VISION_MATURITY_API_KEY', env('VISION_GATEWAY_API_KEY', env('VISION_API_KEY', env('VISION_VECTOR_GATEWAY_API_KEY', '')))),
|
||||
'timeout_seconds' => (int) env('VISION_MATURITY_TIMEOUT', 20),
|
||||
'connect_timeout_seconds' => (int) env('VISION_MATURITY_CONNECT_TIMEOUT', 3),
|
||||
'retries' => (int) env('VISION_MATURITY_RETRIES', 1),
|
||||
'retry_delay_ms' => (int) env('VISION_MATURITY_RETRY_DELAY_MS', 200),
|
||||
],
|
||||
|
||||
'vector_gateway' => [
|
||||
'enabled' => env('VISION_VECTOR_GATEWAY_ENABLED', true),
|
||||
'base_url' => env('VISION_VECTOR_GATEWAY_URL', ''),
|
||||
'api_key' => env('VISION_VECTOR_GATEWAY_API_KEY', ''),
|
||||
'collection' => env('VISION_VECTOR_GATEWAY_COLLECTION', 'images'),
|
||||
'timeout_seconds' => (int) env('VISION_VECTOR_GATEWAY_TIMEOUT', 20),
|
||||
'connect_timeout_seconds' => (int) env('VISION_VECTOR_GATEWAY_CONNECT_TIMEOUT', 5),
|
||||
'retries' => (int) env('VISION_VECTOR_GATEWAY_RETRIES', 1),
|
||||
'retry_delay_ms' => (int) env('VISION_VECTOR_GATEWAY_RETRY_DELAY_MS', 250),
|
||||
'upsert_endpoint' => env('VISION_VECTOR_GATEWAY_UPSERT_ENDPOINT', '/vectors/upsert'),
|
||||
'search_endpoint' => env('VISION_VECTOR_GATEWAY_SEARCH_ENDPOINT', '/vectors/search'),
|
||||
'delete_endpoint' => env('VISION_VECTOR_GATEWAY_DELETE_ENDPOINT', '/vectors/delete'),
|
||||
'collections_endpoint' => env('VISION_VECTOR_GATEWAY_COLLECTIONS_ENDPOINT', '/vectors/collections'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| LM Studio – local multimodal inference (tag generation)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
'lm_studio' => [
|
||||
'base_url' => env('LM_STUDIO_URL', 'http://192.168.0.100:8200'),
|
||||
'model' => env('LM_STUDIO_MODEL', 'google/gemma-3-4b'),
|
||||
'timeout' => (int) env('LM_STUDIO_TIMEOUT', 60),
|
||||
'connect_timeout' => (int) env('LM_STUDIO_CONNECT_TIMEOUT', 5),
|
||||
'temperature' => (float) env('LM_STUDIO_TEMPERATURE', 0.3),
|
||||
'max_tokens' => (int) env('LM_STUDIO_MAX_TOKENS', 300),
|
||||
// Maximum number of AI-suggested tags to keep per artwork.
|
||||
'max_tags' => (int) env('LM_STUDIO_MAX_TAGS', 12),
|
||||
],
|
||||
];
|
||||
Reference in New Issue
Block a user