Spaces:
Sleeping
Sleeping
push
Browse files- WARDROBE_FEATURE_FLOW.md +358 -0
- src/index.ts +0 -1
- src/routes/profile.ts +6 -31
- src/routes/suggest.ts +13 -6
- src/routes/upload.ts +7 -11
WARDROBE_FEATURE_FLOW.md
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# StyleGPT Wardrobe Feature - Complete Flow
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
The wardrobe feature allows users to upload clothing items, automatically classify them using AI, organize them by categories, and get personalized outfit suggestions based on their actual wardrobe.
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## 1. Upload Flow
|
| 9 |
+
|
| 10 |
+
### 1.1 User Initiates Upload
|
| 11 |
+
- **User Action**: Navigates to Upload page
|
| 12 |
+
- **UI**: Shows file upload area with style selection dropdown
|
| 13 |
+
- **User Selects**:
|
| 14 |
+
- Images (1-20 files)
|
| 15 |
+
- Style: Casual, Formal, Streetwear, or Sportswear
|
| 16 |
+
|
| 17 |
+
### 1.2 Image Processing Pipeline
|
| 18 |
+
|
| 19 |
+
#### Step 1: Background Removal
|
| 20 |
+
```
|
| 21 |
+
User uploads image → Background Remover API
|
| 22 |
+
→ Returns: Image with transparent background (PNG)
|
| 23 |
+
→ Purpose: Better accuracy for FashionClip classification
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
#### Step 2: FashionClip Identification
|
| 27 |
+
```
|
| 28 |
+
Background-removed image → FashionClip /identify endpoint
|
| 29 |
+
→ Returns: {
|
| 30 |
+
precise_identification: [
|
| 31 |
+
{ label: "bape", score: 0.82, type: "brand" },
|
| 32 |
+
{ label: "t-shirt", score: 0.61, type: "item" },
|
| 33 |
+
{ label: "brown", score: 0.53, type: "color" }
|
| 34 |
+
],
|
| 35 |
+
detected_components: {
|
| 36 |
+
top_brands: [{ brand: "bape", score: 0.82 }],
|
| 37 |
+
top_colors: [{ color: "brown", score: 0.53 }],
|
| 38 |
+
top_items: [{ item: "t-shirt", score: 0.61 }]
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
#### Step 3: Category Normalization
|
| 44 |
+
```
|
| 45 |
+
FashionClip item label → Category Normalizer
|
| 46 |
+
Examples:
|
| 47 |
+
- "t-shirt", "shirt", "polo shirt" → "shirts"
|
| 48 |
+
- "pants", "jeans", "trousers" → "pants"
|
| 49 |
+
- "sneakers", "boots", "heels" → "shoes"
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
#### Step 4: Item Name Generation
|
| 53 |
+
```
|
| 54 |
+
Brand + Color + Category = Item Name
|
| 55 |
+
Example: "bape" + "brown" + "t-shirt" = "bape brown t-shirt"
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
#### Step 5: Storage
|
| 59 |
+
```
|
| 60 |
+
Original image (with background) → Cloudinary
|
| 61 |
+
→ Stored with public_id: "wardrobe/bape_brown_t_shirt_[timestamp]"
|
| 62 |
+
|
| 63 |
+
Database Record:
|
| 64 |
+
{
|
| 65 |
+
imageUrl: "https://cloudinary.../wardrobe/bape_brown_t_shirt_...",
|
| 66 |
+
category: "shirts", // normalized
|
| 67 |
+
style: "streetwear", // user-selected
|
| 68 |
+
name: "bape brown t-shirt",
|
| 69 |
+
brand: "bape",
|
| 70 |
+
color: "brown",
|
| 71 |
+
userId: [user_id]
|
| 72 |
+
}
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
---
|
| 76 |
+
|
| 77 |
+
## 2. Wardrobe Organization Flow
|
| 78 |
+
|
| 79 |
+
### 2.1 Category-Based Display
|
| 80 |
+
- **User Action**: Opens Wardrobe page
|
| 81 |
+
- **System**: Groups items by normalized category
|
| 82 |
+
- **UI Shows**:
|
| 83 |
+
- Category buttons (e.g., "Shirts (5)", "Pants (3)", "Shoes (2)")
|
| 84 |
+
- All items grid (default view)
|
| 85 |
+
|
| 86 |
+
### 2.2 Category Selection
|
| 87 |
+
- **User Action**: Clicks a category button (e.g., "Shirts")
|
| 88 |
+
- **Animation**: Items slide out from left with staggered delay
|
| 89 |
+
- **UI Shows**: Only items from selected category
|
| 90 |
+
- **Display**: Each item shows:
|
| 91 |
+
- Image (original with background)
|
| 92 |
+
- Item name (e.g., "bape brown t-shirt")
|
| 93 |
+
- Style badge (Casual/Formal/Streetwear/Sportswear)
|
| 94 |
+
- Delete button (on hover)
|
| 95 |
+
|
| 96 |
+
### 2.3 Filtering & Search
|
| 97 |
+
- **User Can**:
|
| 98 |
+
- Search by category name
|
| 99 |
+
- Filter by style (Casual/Formal/Streetwear/Sportswear)
|
| 100 |
+
- Clear filters to see all items
|
| 101 |
+
|
| 102 |
+
---
|
| 103 |
+
|
| 104 |
+
## 3. AI Suggestion Flow
|
| 105 |
+
|
| 106 |
+
### 3.1 User Requests Suggestion
|
| 107 |
+
- **User Action**: Types message in Chat (e.g., "Suggest an elegant outfit for dinner")
|
| 108 |
+
- **System**: Authenticates user and fetches their wardrobe
|
| 109 |
+
|
| 110 |
+
### 3.2 Wardrobe Data Preparation
|
| 111 |
+
```
|
| 112 |
+
User's Wardrobe Items → Formatted for AI:
|
| 113 |
+
Item 1: "bape brown t-shirt" - shirts (streetwear style)
|
| 114 |
+
Item 2: "zara black pants" - pants (casual style)
|
| 115 |
+
Item 3: "nike white sneakers" - shoes (sportswear style)
|
| 116 |
+
...
|
| 117 |
+
|
| 118 |
+
Available categories: shirts, pants, shoes, jackets, ...
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
### 3.3 AI Prompt Construction
|
| 122 |
+
```
|
| 123 |
+
Prompt to AI:
|
| 124 |
+
"User Wardrobe (X items total):
|
| 125 |
+
Item 1: "bape brown t-shirt" - shirts (streetwear style)
|
| 126 |
+
Item 2: "zara black pants" - pants (casual style)
|
| 127 |
+
...
|
| 128 |
+
|
| 129 |
+
Available categories: shirts, pants, shoes, ...
|
| 130 |
+
|
| 131 |
+
User request: Suggest an elegant outfit for dinner
|
| 132 |
+
|
| 133 |
+
IMPORTANT INSTRUCTIONS:
|
| 134 |
+
1. Use ONLY items listed above
|
| 135 |
+
2. Reference items by EXACT names (e.g., "the black zara pants", "brown shoes")
|
| 136 |
+
3. Use brand, color, and category when available
|
| 137 |
+
4. Only suggest from available categories
|
| 138 |
+
5. Make response natural and fluid, mentioning specific item names"
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
### 3.4 AI Response Processing
|
| 142 |
+
```
|
| 143 |
+
AI Response: "For an elegant dinner, I suggest pairing the black zara pants
|
| 144 |
+
with a crisp white shirt and brown leather shoes. This creates a sophisticated
|
| 145 |
+
look that balances formal and casual elements..."
|
| 146 |
+
|
| 147 |
+
System Extracts:
|
| 148 |
+
- Mentions "pants" → Matches to "zara black pants" (pants category)
|
| 149 |
+
- Mentions "shirt" → Matches to available shirt items
|
| 150 |
+
- Mentions "shoes" → Matches to available shoe items
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
### 3.5 Item Matching Algorithm
|
| 154 |
+
```
|
| 155 |
+
1. Parse AI response for category keywords
|
| 156 |
+
2. Match keywords to user's wardrobe categories
|
| 157 |
+
3. Select best matching item from each category
|
| 158 |
+
4. Return selected items with images
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
### 3.6 Response Display
|
| 162 |
+
```
|
| 163 |
+
Chat UI Shows:
|
| 164 |
+
1. AI text response (formatted)
|
| 165 |
+
2. Selected items grid with images:
|
| 166 |
+
- "zara black pants" (image)
|
| 167 |
+
- "white shirt" (image)
|
| 168 |
+
- "brown shoes" (image)
|
| 169 |
+
3. "Suggest Another Outfit" button
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
### 3.7 Multiple Suggestions
|
| 173 |
+
- **User Action**: Clicks "Suggest Another Outfit" or types new request
|
| 174 |
+
- **System**: Removes previous suggestion, generates new one
|
| 175 |
+
- **Behavior**: Only one suggestion visible at a time
|
| 176 |
+
|
| 177 |
+
---
|
| 178 |
+
|
| 179 |
+
## 4. Data Flow Diagram
|
| 180 |
+
|
| 181 |
+
```
|
| 182 |
+
┌─────────────┐
|
| 183 |
+
│ User Upload │
|
| 184 |
+
│ Images │
|
| 185 |
+
└──────┬──────┘
|
| 186 |
+
│
|
| 187 |
+
▼
|
| 188 |
+
┌──────────────────┐
|
| 189 |
+
│ Background │
|
| 190 |
+
│ Removal API │
|
| 191 |
+
└──────┬───────────┘
|
| 192 |
+
│
|
| 193 |
+
▼
|
| 194 |
+
┌──────────────────┐
|
| 195 |
+
│ FashionClip │
|
| 196 |
+
│ /identify │
|
| 197 |
+
│ (brand, color, │
|
| 198 |
+
│ item detection)│
|
| 199 |
+
└──────┬───────────┘
|
| 200 |
+
│
|
| 201 |
+
▼
|
| 202 |
+
┌──────────────────┐
|
| 203 |
+
│ Category │
|
| 204 |
+
│ Normalization │
|
| 205 |
+
│ (t-shirt → shirts)│
|
| 206 |
+
└──────┬───────────┘
|
| 207 |
+
│
|
| 208 |
+
▼
|
| 209 |
+
┌──────────────────┐
|
| 210 |
+
│ Name Generation │
|
| 211 |
+
│ (brand+color+cat)│
|
| 212 |
+
└──────┬───────────┘
|
| 213 |
+
│
|
| 214 |
+
▼
|
| 215 |
+
┌──────────────────┐
|
| 216 |
+
│ Cloudinary │
|
| 217 |
+
│ (store original) │
|
| 218 |
+
└──────┬───────────┘
|
| 219 |
+
│
|
| 220 |
+
▼
|
| 221 |
+
┌──────────────────┐
|
| 222 |
+
│ Database │
|
| 223 |
+
│ (save metadata) │
|
| 224 |
+
└──────┬───────────┘
|
| 225 |
+
│
|
| 226 |
+
▼
|
| 227 |
+
┌──────────────────┐
|
| 228 |
+
│ Wardrobe UI │
|
| 229 |
+
│ (categorized) │
|
| 230 |
+
└──────┬───────────┘
|
| 231 |
+
│
|
| 232 |
+
▼
|
| 233 |
+
┌──────────────────┐
|
| 234 |
+
│ AI Suggestion │
|
| 235 |
+
│ (uses item names)│
|
| 236 |
+
└──────────────────┘
|
| 237 |
+
```
|
| 238 |
+
|
| 239 |
+
---
|
| 240 |
+
|
| 241 |
+
## 5. Key Features
|
| 242 |
+
|
| 243 |
+
### 5.1 User-Selected Style
|
| 244 |
+
- **Purpose**: Better organization and filtering
|
| 245 |
+
- **Options**: Casual, Formal, Streetwear, Sportswear
|
| 246 |
+
- **Applied**: To all items in upload batch
|
| 247 |
+
|
| 248 |
+
### 5.2 Item Naming
|
| 249 |
+
- **Format**: `[brand] [color] [category]`
|
| 250 |
+
- **Examples**:
|
| 251 |
+
- "bape brown t-shirt"
|
| 252 |
+
- "zara black pants"
|
| 253 |
+
- "nike white sneakers"
|
| 254 |
+
- "louis vuitton white shirt"
|
| 255 |
+
|
| 256 |
+
### 5.3 Category Organization
|
| 257 |
+
- **Normalized Categories**: shirts, pants, shoes, jackets, dresses, etc.
|
| 258 |
+
- **Visual**: Category buttons with item counts
|
| 259 |
+
- **Interaction**: Click to filter, animation on selection
|
| 260 |
+
|
| 261 |
+
### 5.4 AI Integration
|
| 262 |
+
- **Context**: Full wardrobe with item names
|
| 263 |
+
- **Response Style**: Natural, references actual items
|
| 264 |
+
- **Item Matching**: Keyword-based extraction from AI response
|
| 265 |
+
- **Visual Display**: Shows selected items with images
|
| 266 |
+
|
| 267 |
+
### 5.5 Item Management
|
| 268 |
+
- **View**: By category or all items
|
| 269 |
+
- **Delete**: Hover to reveal delete button
|
| 270 |
+
- **Search**: By category name
|
| 271 |
+
- **Filter**: By style
|
| 272 |
+
|
| 273 |
+
---
|
| 274 |
+
|
| 275 |
+
## 6. User Journey Example
|
| 276 |
+
|
| 277 |
+
### Scenario: User wants elegant dinner outfit
|
| 278 |
+
|
| 279 |
+
1. **Upload Phase**:
|
| 280 |
+
- User uploads 5 items (shirt, pants, shoes, jacket, watch)
|
| 281 |
+
- Selects "Formal" style
|
| 282 |
+
- System processes: removes backgrounds, identifies items, stores with names
|
| 283 |
+
|
| 284 |
+
2. **Organization Phase**:
|
| 285 |
+
- Wardrobe shows: "Shirts (1)", "Pants (1)", "Shoes (1)", etc.
|
| 286 |
+
- User clicks "Pants" → sees "zara black pants" with animation
|
| 287 |
+
|
| 288 |
+
3. **Suggestion Phase**:
|
| 289 |
+
- User types: "Suggest an elegant outfit for dinner"
|
| 290 |
+
- AI receives: Full wardrobe with item names
|
| 291 |
+
- AI responds: "I suggest the black zara pants with a crisp white shirt..."
|
| 292 |
+
- System extracts: Matches "pants" and "shirt" to user's items
|
| 293 |
+
- UI displays: AI response + images of selected items
|
| 294 |
+
|
| 295 |
+
4. **Refinement Phase**:
|
| 296 |
+
- User clicks "Suggest Another Outfit"
|
| 297 |
+
- System generates new suggestion with different items
|
| 298 |
+
- Previous suggestion removed, new one displayed
|
| 299 |
+
|
| 300 |
+
---
|
| 301 |
+
|
| 302 |
+
## 7. Technical Components
|
| 303 |
+
|
| 304 |
+
### Backend
|
| 305 |
+
- `src/routes/upload.ts`: Handles image upload and processing
|
| 306 |
+
- `src/routes/suggest.ts`: Generates AI suggestions
|
| 307 |
+
- `src/routes/wardrobe.ts`: Manages wardrobe CRUD operations
|
| 308 |
+
- `src/utils/backgroundRemoval.ts`: Background removal service
|
| 309 |
+
- `src/utils/hfClient.ts`: FashionClip API client
|
| 310 |
+
- `src/utils/categoryNormalizer.ts`: Category normalization logic
|
| 311 |
+
|
| 312 |
+
### Frontend
|
| 313 |
+
- `stylegptUI/src/pages/Upload.jsx`: Upload interface with style selection
|
| 314 |
+
- `stylegptUI/src/pages/Wardrobe.jsx`: Category-based wardrobe display
|
| 315 |
+
- `stylegptUI/src/pages/Chat.jsx`: AI suggestion interface
|
| 316 |
+
- `stylegptUI/src/utils/api.js`: API communication
|
| 317 |
+
|
| 318 |
+
### Database
|
| 319 |
+
- `WardrobeItem` entity: Stores item metadata
|
| 320 |
+
- Fields: imageUrl, category, style, name, brand, color, userId
|
| 321 |
+
|
| 322 |
+
---
|
| 323 |
+
|
| 324 |
+
## 8. Data Flow Summary
|
| 325 |
+
|
| 326 |
+
```
|
| 327 |
+
Upload → Background Removal → FashionClip → Normalization → Storage
|
| 328 |
+
↓
|
| 329 |
+
Wardrobe UI ← Category Grouping ← Database Query ← User Request
|
| 330 |
+
↓
|
| 331 |
+
Category Selection → Animation → Item Display
|
| 332 |
+
↓
|
| 333 |
+
Chat Request → AI Prompt (with item names) → AI Response → Item Extraction → Display
|
| 334 |
+
```
|
| 335 |
+
|
| 336 |
+
---
|
| 337 |
+
|
| 338 |
+
## 9. Key Design Decisions
|
| 339 |
+
|
| 340 |
+
1. **Store Original Images**: Users see items with original backgrounds
|
| 341 |
+
2. **Background Removal for Classification**: Better AI accuracy
|
| 342 |
+
3. **User-Selected Style**: More accurate than auto-detection
|
| 343 |
+
4. **Category Normalization**: Consistent organization
|
| 344 |
+
5. **Full Item Names**: AI can reference items naturally
|
| 345 |
+
6. **One Suggestion at a Time**: Cleaner UI, less confusion
|
| 346 |
+
7. **Category Animation**: Better UX, visual feedback
|
| 347 |
+
|
| 348 |
+
---
|
| 349 |
+
|
| 350 |
+
## 10. Future Enhancements
|
| 351 |
+
|
| 352 |
+
- Outfit saving/favorites
|
| 353 |
+
- Style mixing suggestions
|
| 354 |
+
- Seasonal wardrobe organization
|
| 355 |
+
- Item tagging (favorite, worn recently)
|
| 356 |
+
- Outfit history tracking
|
| 357 |
+
- Social sharing of outfits
|
| 358 |
+
|
src/index.ts
CHANGED
|
@@ -49,7 +49,6 @@ app.get("/", (req, res) => {
|
|
| 49 |
"Express.js",
|
| 50 |
"PostgreSQL",
|
| 51 |
"TypeORM",
|
| 52 |
-
"Cloudinary",
|
| 53 |
"Hugging Face Fashion-CLIP",
|
| 54 |
"JWT Authentication"
|
| 55 |
]
|
|
|
|
| 49 |
"Express.js",
|
| 50 |
"PostgreSQL",
|
| 51 |
"TypeORM",
|
|
|
|
| 52 |
"Hugging Face Fashion-CLIP",
|
| 53 |
"JWT Authentication"
|
| 54 |
]
|
src/routes/profile.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
import express from "express";
|
| 2 |
import multer from "multer";
|
| 3 |
-
import cloudinary from "../utils/cloudinary";
|
| 4 |
import { AppDataSource } from "../utils/dataSource";
|
| 5 |
import { User } from "../entity/User";
|
| 6 |
import { authenticateToken, AuthRequest } from "../middleware/auth";
|
|
@@ -136,48 +135,24 @@ router.put("/", authenticateToken, upload.single("profilePicture"), async (req:
|
|
| 136 |
}
|
| 137 |
|
| 138 |
// Handle profile picture removal (check if removePicture flag is set)
|
| 139 |
-
if (req.body.removePicture === "true"
|
| 140 |
-
const publicId = user.profilePicture.split("/").slice(-2).join("/").split(".")[0];
|
| 141 |
-
try {
|
| 142 |
-
await cloudinary.uploader.destroy(`profile-pictures/${publicId}`);
|
| 143 |
-
} catch (deleteError) {
|
| 144 |
-
console.error("Error deleting old profile picture:", deleteError);
|
| 145 |
-
}
|
| 146 |
user.profilePicture = undefined;
|
| 147 |
}
|
| 148 |
|
| 149 |
// Handle profile picture upload
|
| 150 |
if (file) {
|
| 151 |
try {
|
| 152 |
-
// Convert buffer to base64
|
| 153 |
const base64Image = `data:${file.mimetype};base64,${file.buffer.toString("base64")}`;
|
| 154 |
|
| 155 |
-
//
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
resource_type: "image",
|
| 159 |
-
transformation: [
|
| 160 |
-
{ width: 400, height: 400, crop: "fill", gravity: "face" },
|
| 161 |
-
{ quality: "auto" },
|
| 162 |
-
],
|
| 163 |
-
});
|
| 164 |
-
|
| 165 |
-
// Delete old profile picture if exists
|
| 166 |
-
if (user.profilePicture) {
|
| 167 |
-
const publicId = user.profilePicture.split("/").slice(-2).join("/").split(".")[0];
|
| 168 |
-
try {
|
| 169 |
-
await cloudinary.uploader.destroy(`profile-pictures/${publicId}`);
|
| 170 |
-
} catch (deleteError) {
|
| 171 |
-
console.error("Error deleting old profile picture:", deleteError);
|
| 172 |
-
}
|
| 173 |
-
}
|
| 174 |
-
|
| 175 |
-
user.profilePicture = uploadResult.secure_url;
|
| 176 |
} catch (uploadError: any) {
|
| 177 |
console.error("Profile picture upload error:", uploadError);
|
| 178 |
return res.status(500).json({
|
| 179 |
success: false,
|
| 180 |
-
error: "Failed to
|
| 181 |
});
|
| 182 |
}
|
| 183 |
}
|
|
|
|
| 1 |
import express from "express";
|
| 2 |
import multer from "multer";
|
|
|
|
| 3 |
import { AppDataSource } from "../utils/dataSource";
|
| 4 |
import { User } from "../entity/User";
|
| 5 |
import { authenticateToken, AuthRequest } from "../middleware/auth";
|
|
|
|
| 135 |
}
|
| 136 |
|
| 137 |
// Handle profile picture removal (check if removePicture flag is set)
|
| 138 |
+
if (req.body.removePicture === "true") {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
user.profilePicture = undefined;
|
| 140 |
}
|
| 141 |
|
| 142 |
// Handle profile picture upload
|
| 143 |
if (file) {
|
| 144 |
try {
|
| 145 |
+
// Convert buffer to base64 data URL
|
| 146 |
const base64Image = `data:${file.mimetype};base64,${file.buffer.toString("base64")}`;
|
| 147 |
|
| 148 |
+
// Store base64 directly in database
|
| 149 |
+
user.profilePicture = base64Image;
|
| 150 |
+
console.log("✅ Profile picture converted to base64 and stored");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
} catch (uploadError: any) {
|
| 152 |
console.error("Profile picture upload error:", uploadError);
|
| 153 |
return res.status(500).json({
|
| 154 |
success: false,
|
| 155 |
+
error: "Failed to process profile picture",
|
| 156 |
});
|
| 157 |
}
|
| 158 |
}
|
src/routes/suggest.ts
CHANGED
|
@@ -118,7 +118,7 @@ function extractSelectedItems(aiResponse: string, wardrobe: WardrobeItem[]): War
|
|
| 118 |
*/
|
| 119 |
router.post("/", authenticateToken, async (req: AuthRequest, res) => {
|
| 120 |
try {
|
| 121 |
-
const { message, session_id } = req.body;
|
| 122 |
const userId = req.userId!;
|
| 123 |
|
| 124 |
if (!message) {
|
|
@@ -158,13 +158,20 @@ router.post("/", authenticateToken, async (req: AuthRequest, res) => {
|
|
| 158 |
|
| 159 |
console.log(`[Suggest] Sending structured wardrobe data with ${wardrobe.length} items`);
|
| 160 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
const response = await axios.post(
|
| 162 |
process.env.CHAT_ENDPOINT!,
|
| 163 |
-
|
| 164 |
-
message: message,
|
| 165 |
-
session_id: session_id || `user_${userId}`,
|
| 166 |
-
wardrobe: wardrobeArray,
|
| 167 |
-
},
|
| 168 |
{ headers: { "Content-Type": "application/json" } }
|
| 169 |
);
|
| 170 |
|
|
|
|
| 118 |
*/
|
| 119 |
router.post("/", authenticateToken, async (req: AuthRequest, res) => {
|
| 120 |
try {
|
| 121 |
+
const { message, session_id, images } = req.body;
|
| 122 |
const userId = req.userId!;
|
| 123 |
|
| 124 |
if (!message) {
|
|
|
|
| 158 |
|
| 159 |
console.log(`[Suggest] Sending structured wardrobe data with ${wardrobe.length} items`);
|
| 160 |
|
| 161 |
+
const requestBody: any = {
|
| 162 |
+
message: message,
|
| 163 |
+
session_id: session_id || `user_${userId}`,
|
| 164 |
+
wardrobe: wardrobeArray,
|
| 165 |
+
};
|
| 166 |
+
|
| 167 |
+
if (images && Array.isArray(images) && images.length > 0) {
|
| 168 |
+
requestBody.images = images;
|
| 169 |
+
console.log(`[Suggest] Sending ${images.length} image(s) with request`);
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
const response = await axios.post(
|
| 173 |
process.env.CHAT_ENDPOINT!,
|
| 174 |
+
requestBody,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
{ headers: { "Content-Type": "application/json" } }
|
| 176 |
);
|
| 177 |
|
src/routes/upload.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
import express from "express";
|
| 2 |
import multer from "multer";
|
| 3 |
-
import cloudinary from "../utils/cloudinary";
|
| 4 |
import { classifyFashionImage, identifyFashionItem } from "../utils/hfClient";
|
| 5 |
import { removeBackground } from "../utils/backgroundRemoval";
|
| 6 |
import { AppDataSource } from "../utils/dataSource";
|
|
@@ -144,21 +143,18 @@ router.post("/", authenticateToken, upload.array("image", 20), async (req: AuthR
|
|
| 144 |
nameParts.push(itemLabel);
|
| 145 |
const itemName = nameParts.join(" ");
|
| 146 |
|
| 147 |
-
const sanitizedName = itemName.replace(/[^a-zA-Z0-9\s]/g, "").replace(/\s+/g, "_").toLowerCase();
|
| 148 |
-
const publicId = `wardrobe/${sanitizedName}_${Date.now()}`;
|
| 149 |
-
|
| 150 |
const base64Image = `data:${file.mimetype};base64,${file.buffer.toString("base64")}`;
|
| 151 |
-
const
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
console.log(`✅
|
| 156 |
|
| 157 |
const style = selectedStyle;
|
| 158 |
|
| 159 |
const newItem = itemRepo.create({
|
| 160 |
-
imageUrl:
|
| 161 |
-
processedImageUrl:
|
| 162 |
category: normalizedCategory,
|
| 163 |
style: style,
|
| 164 |
name: itemName,
|
|
|
|
| 1 |
import express from "express";
|
| 2 |
import multer from "multer";
|
|
|
|
| 3 |
import { classifyFashionImage, identifyFashionItem } from "../utils/hfClient";
|
| 4 |
import { removeBackground } from "../utils/backgroundRemoval";
|
| 5 |
import { AppDataSource } from "../utils/dataSource";
|
|
|
|
| 143 |
nameParts.push(itemLabel);
|
| 144 |
const itemName = nameParts.join(" ");
|
| 145 |
|
|
|
|
|
|
|
|
|
|
| 146 |
const base64Image = `data:${file.mimetype};base64,${file.buffer.toString("base64")}`;
|
| 147 |
+
const processedBase64Image = processedBuffer
|
| 148 |
+
? `data:image/png;base64,${processedBuffer.toString("base64")}`
|
| 149 |
+
: undefined;
|
| 150 |
+
|
| 151 |
+
console.log(`✅ Image converted to base64: ${itemName}`);
|
| 152 |
|
| 153 |
const style = selectedStyle;
|
| 154 |
|
| 155 |
const newItem = itemRepo.create({
|
| 156 |
+
imageUrl: base64Image,
|
| 157 |
+
processedImageUrl: processedBase64Image,
|
| 158 |
category: normalizedCategory,
|
| 159 |
style: style,
|
| 160 |
name: itemName,
|