diff --git a/.copilot/favourites_artworks.md b/.copilot/favourites_artworks.md new file mode 100644 index 00000000..a46c6286 --- /dev/null +++ b/.copilot/favourites_artworks.md @@ -0,0 +1,181 @@ +# 1️⃣ Final SQL – New Favorites Table (Artwork-only) + +**Table name:** `user_favorites` +**Scope:** users ↔ artworks +**Engine:** InnoDB +**Charset:** utf8mb4 + +```sql +CREATE TABLE user_favorites ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + + user_id BIGINT UNSIGNED NOT NULL, + artwork_id BIGINT UNSIGNED NOT NULL, + + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + + UNIQUE KEY uniq_user_artwork (user_id, artwork_id), + KEY idx_artwork (artwork_id), + KEY idx_user_created (user_id, created_at), + + CONSTRAINT fk_user_favorites_user + FOREIGN KEY (user_id) REFERENCES users(id) + ON DELETE CASCADE, + + CONSTRAINT fk_user_favorites_artwork + FOREIGN KEY (artwork_id) REFERENCES artworks(id) + ON DELETE CASCADE +) ENGINE=InnoDB + DEFAULT CHARSET=utf8mb4 + COLLATE=utf8mb4_unicode_ci; +``` + +This is **production-ready** and matches everything we discussed: + +* no legacy fields +* no duplication +* proper constraints +* fast queries + +--- + +# 2️⃣ VS Code Agent Markdown (Laravel 12 Migration Prompt) + +Save this as for example: + +``` +.vscode/agents/laravel-user-favorites-migration.md +``` + +or + +``` +.cursor/rules/laravel-user-favorites.md +``` + +--- + +```markdown +# Laravel 12 – User Favorites Migration & Model + +## Context +We are migrating legacy "favourites" functionality into a clean, modern Laravel 12 system. +Each user can add artworks to their favorites list. +This is a many-to-many relationship between users and artworks. + +Legacy table MUST NOT be reused. + +--- + +## Goal +Create a Laravel 12 database migration and Eloquent model for a new table named: + +``` + +user_favorites + +```` + +This table stores **which user favorited which artwork**, with a timestamp. + +--- + +## Database Requirements + +### Table: user_favorites + +| Column | Type | Notes | +|------|------|------| +| id | BIGINT UNSIGNED | Primary key | +| user_id | BIGINT UNSIGNED | FK → users.id | +| artwork_id | BIGINT UNSIGNED | FK → artworks.id | +| created_at | TIMESTAMP | When artwork was favorited | + +### Constraints & Indexes + +- UNIQUE (user_id, artwork_id) + → prevents duplicate favorites + +- INDEX artwork_id + → fast favorite count per artwork + +- INDEX (user_id, created_at) + → fast "my favorites" queries + +### Foreign Keys + +- user_id → users.id (ON DELETE CASCADE) +- artwork_id → artworks.id (ON DELETE CASCADE) + +### Engine & Charset + +- Engine: InnoDB +- Charset: utf8mb4 +- Collation: utf8mb4_unicode_ci + +--- + +## Laravel Migration Requirements + +- Use `Schema::create` +- Use `foreignId()->constrained()->cascadeOnDelete()` +- Use `timestamps()` **ONLY if created_at is needed** + (do NOT add updated_at) + +- Add explicit indexes and unique constraints + +--- + +## Laravel Model Requirements + +### Model: UserFavorite + +- Table: `user_favorites` +- `$timestamps = false` (created_at handled manually or via DB default) +- Fillable: + - user_id + - artwork_id + - created_at + +### Relationships + +```php +UserFavorite belongsTo User +UserFavorite belongsTo Artwork +```` + +--- + +## Additional Notes + +* This table is interaction-based, NOT content-based +* Do NOT store favorite counts here +* Favorite counts will be aggregated separately (Redis or statistics table) +* This table must be lean and write-optimized + +--- + +## Deliverables + +* Migration file for Laravel 12 +* Eloquent model `UserFavorite` +* Proper naming and clean schema +* No legacy fields, no polymorphic logic + +Generate clean, production-quality code. + +```` + +--- + +## 3️⃣ How to Use This in VS Code (Quick Steps) + +1. Paste markdown into `.vscode/agents/` or `.cursor/rules/` +2. Open VS Code +3. Ask your AI agent: + > “Create Laravel 12 migration and model based on this document” +4. Review generated migration +5. Run: +```bash +php artisan migrate +```` diff --git a/.copilot/update_user_schema.md b/.copilot/update_user_schema.md new file mode 100644 index 00000000..3c8043a8 --- /dev/null +++ b/.copilot/update_user_schema.md @@ -0,0 +1,312 @@ +# Skinbase – User Schema Review & Upgrade Plan + +**Database:** MySQL / Percona 8.x +**Project:** Skinbase (new system, no legacy dependencies) +**Reviewed tables:** users, user_profiles, user_social_links, user_statistics + +--- + +## 1. Overview + +The current user-related database schema is **well designed**, modern, and suitable +for long-term growth. +Key strengths include: + +- Clear separation of concerns +- Proper use of foreign keys and cascading deletes +- BigInt primary keys +- Soft deletes on users +- Migration-friendly legacy password handling + +This document summarizes: +- what is already good +- recommended optimizations +- future-proofing steps (non-breaking) +- performance considerations for scale + +--- + +## 2. users Table + +### 2.1 What’s Good + +- Unique `username` and `email` +- `legacy_password_algo` allows smooth migration from old systems +- `needs_password_reset` improves security posture +- Role stored as string allows flexibility in early stages +- Soft deletes enabled + +### 2.2 Recommended Indexes + +Add indexes for common query patterns: + +```sql +CREATE INDEX idx_users_active ON users (is_active); +CREATE INDEX idx_users_role ON users (role); +CREATE INDEX idx_users_last_visit ON users (last_visit_at); +```` + +These improve: + +* active user filtering +* admin queries +* “last seen” or online user features + +### 2.3 Future Role Normalization (Planned) + +Current approach is fine short-term: + +```text +role = 'user' | 'admin' | 'moderator' +``` + +Planned future upgrade: + +* `roles` table +* `user_roles` pivot table (many-to-many) + +This allows: + +* multiple roles per user +* temporary or scoped roles +* better permission modeling + +⚠️ No immediate action required — just avoid hard-coding role logic. + +### 2.4 Optional Security Enhancements + +Recommended additions (optional but advised): + +```sql +last_password_change_at TIMESTAMP NULL, +failed_login_attempts INT UNSIGNED DEFAULT 0, +locked_until TIMESTAMP NULL +``` + +Enables: + +* rate limiting +* temporary account locking +* better auditability + +--- + +## 3. user_profiles Table + +### 3.1 Strengths + +* Clean one-to-one relationship with `users` +* Public profile data separated from auth data +* Nullable fields for progressive profile completion +* Inclusive gender enum with safe default +* Localization-ready (`language`, `country_code`) + +### 3.2 Country Handling Recommendation + +Prefer using only: + +```text +country_code (ISO 3166-1 alpha-2) +``` + +Benefits: + +* language-independent +* avoids inconsistent country naming +* easier frontend mapping + +`country` text field can be deprecated later if needed. + +### 3.3 Avatar & Media Metadata (Future) + +Current: + +```text +avatar VARCHAR(255) +``` + +Recommended future approach: + +```text +avatar_hash CHAR(64) +avatar_ext VARCHAR(10) +avatar_updated_at TIMESTAMP +``` + +This aligns with: + +* hash-based file storage +* CDN-friendly URLs +* cache invalidation control + +--- + +## 4. user_social_links Table + +### 4.1 Current Design + +* One row per platform per user +* Unique constraint on `(user_id, platform)` +* Cascade delete enabled + +This is solid. + +### 4.2 Platform Normalization (Recommended) + +Current: + +```text +platform VARCHAR(32) +``` + +Risk: + +* inconsistent values (`twitter`, `x`, `Twitter`, etc.) + +Options: + +**Option A – Enum (simple):** + +```sql +ENUM('twitter','x','instagram','deviantart','artstation','github','website') +``` + +**Option B – Reference Table (best long-term):** + +* `social_platforms` +* foreign key `platform_id` + +Option B is preferred for Skinbase as platforms evolve. + +--- + +## 5. user_statistics Table + +### 5.1 Strengths + +* Isolated counters +* One row per user +* Minimal row size +* Clean FK relationship + +### 5.2 Performance Warning (Important) + +Avoid frequent direct updates like: + +```sql +UPDATE user_statistics SET downloads = downloads + 1; +``` + +At scale, this causes: + +* row locking +* write contention +* degraded performance + +### 5.3 Recommended Strategy + +* Use **Redis** (or in-memory cache) for real-time increments +* Periodically flush aggregated values to MySQL +* Use jobs / cron for batch updates + +This ensures: + +* fast user interactions +* scalable statistics tracking + +--- + +## 6. Charset & Collation + +Current: + +```text +utf8mb4_unicode_ci +``` + +This is correct and safe. + +Optional upgrade (MySQL 8+): + +```text +utf8mb4_0900_ai_ci +``` + +Benefits: + +* newer Unicode rules +* slightly better performance + +Not required immediately. + +--- + +## 7. Tables Planned for Future Expansion + +Not required now, but expected as Skinbase grows: + +### 7.1 user_activity_log + +* logins +* uploads +* profile edits +* moderation actions + +### 7.2 user_followers + +* artist-to-artist following +* social graph features + +### 7.3 user_settings + +* privacy preferences +* notification settings +* email subscriptions + +These should remain **separate tables** to avoid bloating `users`. + +--- + +## 8. Laravel Implementation Notes + +Recommended model casting: + +```php +protected $casts = [ + 'is_active' => 'boolean', + 'needs_password_reset' => 'boolean', + 'email_verified_at' => 'datetime', + 'last_visit_at' => 'datetime', +]; +``` + +Best practices: + +* Eager load profiles (`with('profile')`) +* Cache public profile + statistics +* Keep statistics out of main user queries + +--- + +## 9. Final Verdict + +**Schema quality:** Excellent +**Scalability:** Very good +**Migration readiness:** Excellent +**Long-term Skinbase fit:** Excellent + +The current schema is production-ready and significantly better than typical legacy +user databases. Most future improvements can be introduced **without breaking changes**. + +Primary future wins: + +* Redis-backed statistics +* normalized roles and social platforms +* hash-based media storage + +--- + +**Status:** Approved for production +**Next steps:** API design, public profile queries, legacy user migration + +``` diff --git a/app/Chat.php b/app/Chat.php index faa95f6b..f23720c5 100644 --- a/app/Chat.php +++ b/app/Chat.php @@ -22,7 +22,7 @@ class Chat return; } - $last = DB::connection('legacy')->table('chat') + $last = DB::table('chat') ->select('message') ->where('user_id', $userId) ->orderByDesc('chat_id') @@ -30,7 +30,7 @@ class Chat ->first(); if (!$last || ($last->message ?? '') !== $tekst) { - DB::connection('legacy')->table('chat')->insert([ + DB::table('chat')->insert([ 'time' => now(), 'sender' => $username, 'user_id' => $userId, @@ -43,7 +43,7 @@ class Chat { $output = "