1 line
94 KiB
JSON
1 line
94 KiB
JSON
{"repo": "chuang1234/photomanager", "files": {"README.md": {"a2d159203691e625d991973774af2ed6c235ef65": {"t": 1785747240.2845814, "n": 14354, "h": "ec833026c60b2f9b", "c": "<div align=\"center\">\n\n# ð· å®¶åºçžå Family Photo Manager\n\n**è®°åœæ¯äžäžªæž©éŠšç¬éŽïŒè®©å®¶äººä¹éŽçè·çŠ»æŽè¿äžç¹ ð**\n\n[](https://www.python.org/)\n[](https://flask.palletsprojects.com/)\n[](https://react.dev/)\n[](https://ant.design/)\n[](https://www.mysql.com/)\n\n</div>\n\n---\n\n## ð 项ç®ç®ä»\n\nå®¶åºçžåæ¯äžäžªäžäžºå®¶åºè®Ÿè®¡çç§å¯ç
§ç管çç³»ç»ãæ¯æå€å®¶åºæåå
±äº«çžåãç
§çäžäŒ /æç
§ãæ¶è管çïŒå¹¶å
眮 AI æºèœå©ææäŸæž©éŠšäºåšãæææ°æ®ååšåšæ¬å°ïŒä¿æ€å®¶åºéç§ã\n\n### âš æ žå¿åèœ\n\n| åèœæš¡å | æè¿° |\n|---------|------|\n| ð **çžå管ç** | å建ãéåœåãå é€çžåïŒèªå®ä¹å°é¢ |\n| ð€ **ç
§çäžäŒ ** | æ¯ææ¹éäžäŒ ïŒè®°åœæææ¶éŽãåœå±æåã倿³š |\n| ð· **æç
§åèœ** | è°çšæåå€ŽçŽæ¥æç
§å¹¶äžäŒ ïŒé HTTPS ç¯å¢ïŒ |\n| ð **ç
§çæçŽ¢** | æåç§°ãåœå±äººãäžäŒ è
ãæ¥æèåŽçé |\n| ð **æ¶è管ç** | å建æ¶è倹ïŒç
§çå å
¥æ¶è/ç§»åºïŒæ¯æå€äžªæ¶è倹 |\n| ðšâð©âð§ **å®¶åºæå** | 倿å管çïŒç
§çè®°åœåœå±äººåäžäŒ è
|\n| ð€ **AI 婿** | å
眮 AI 对è¯åèœïŒæ¯æ OpenAI / Ollama çå€ç§æäŸå |\n| ð **ç»åœéŽæ** | JWT Token 讀è¯ïŒSHA256 + bcrypt åéå¯ç å å¯ |\n\n---\n\n## ðïž ææ¯æ¶æ\n\n```\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (React 19) â\nâ Ant Design 6 · React Router 7 · Axios · Less â\nâ :3000 (craco) â\nââââââââââââââââââââââââ¬âââââââââââââââââââââââââââââââ\n â /api/* /uploads/*\n âŒ\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (Flask 2.3) â\nâ PyJWT · bcrypt · PyMySQL · Werkzeug · python-dotenvâ\nâ :5000 â\nâââââââââ¬ââââââââââââââââââââââââââ¬ââââââââââââââââââââ\n ⌠âŒ\nâââââââââââââââââ ââââââââââââââââââââ\nâ MySQL 5.7+ â â æ¬å°æä»¶ç³»ç» â\nâ family_photo â â uploads/photos â\nâ â â uploads/covers â\nâââââââââââââââââ ââââââââââââââââââââ\n```\n\n---\n\n## ð 项ç®ç»æ\n\n```\nPhotoManager/\nâââ family-photo-backend/ # ð§ å端 (Flask)\nâ âââ config/\nâ â âââ config.py # æ°æ®åº/JWT/å¯ç å å¯é
眮\nâ â âââ log_config.py # æ¥å¿é
çœ®ïŒæå€©èœ®èœ¬ïŒ\nâ âââ src/\nâ â âââ main.py # Flask åºçšå
¥å£\nâ â âââ auth.py # ç»åœ/ç»åº/Token éŽæ\nâ â âââ album.py # çžå CRUD + å°é¢äžäŒ \nâ â âââ photo.py # ç
§çäžäŒ /æçŽ¢/å é€\nâ â âââ favorite.py # æ¶è倹 & æ¶èç
§ç管ç\nâ â âââ member.py # å®¶åºæåæ¥è¯¢\nâ â âââ file.py # éææä»¶æå¡ïŒè·¯åŸéå鲿€ïŒ\nâ â âââ chat.py # AI å¯¹è¯æ¥å£\nâ â âââ ai_service.py # AI æå¡ïŒOpenAI/OllamaïŒ\nâ â âââ utils.py # æ°æ®åºè¿æ¥ & å·¥å
·åœæ°\nâ âââ sql/\nâ â âââ table_info.sql # æ°æ®åºå»ºè¡š SQL\nâ âââ uploads/ # äžäŒ æä»¶ååš\nâ â âââ photos/ # ç
§çæä»¶ïŒæ album_id åç®åœïŒ\nâ â âââ covers/ # çžåå°é¢\nâ âââ logs/ # æ¥å¿æä»¶ïŒèªåšçæïŒ\nâ âââ .env.example # ç¯å¢åéæš¡æ¿\nâ âââ requirements.txt # Python äŸèµ\nâ\nâââ family-photo-frontend/ # ðš å端 (React)\nâ âââ src/\nâ â âââ pages/\nâ â â âââ Login/ # ç»åœé¡µ\nâ â â âââ AlbumList/ # çžåå衚\nâ â â âââ AlbumDetail/ # çžå诊æ
\nâ â â â âââ components/\nâ â â â â âââ AlbumPhotoList/ # ç
§çå衚ïŒå页ïŒ\nâ â â â â âââ UploadArare/ # ç
§çäžäŒ åº\nâ â â â â âââ PhotoCapture/ # æå倎æç
§\nâ â â â âââ popwin/\nâ â â â âââ FilterPopwin/ # æçŽ¢çé匹çª\nâ â â â âââ FavoriteConfirmModal/ # æ¶è确讀匹çª\nâ â â âââ CreateAlbum/ # å建çžå\nâ â â âââ FavoriteManager/ # æ¶è管ç\nâ â âââ components/\nâ â â âââ PrivateRoute.js # è·¯ç±å®å«\nâ â â âââ HeaderComponent.js # é¡¶éšå¯Œèª\nâ â â âââ AiChat/ # AI å¯¹è¯æ¬æµ®çª\nâ â â âââ ButtonWrapper/ # æé®å°è£
\nâ â âââ contexts/\nâ â â âââ MemberContext.js # åœåç»åœæåäžäžæ\nâ â âââ utils/\nâ â â âââ request.js # Axios å°è£
ïŒToken æŠæªåšïŒ\nâ â â âââ dateUtil.js # æ¥ææ ŒåŒåå·¥å
·\nâ â âââ App.js # è·¯ç±é
眮å
¥å£\nâ âââ craco.config.js # Craco é
眮ïŒLess/代çïŒ\nâ âââ package.json\nâ\nâââ README.md\n```\n\n---\n\n## ð³ Docker äžé®éšçœ²ïŒæšèïŒ\n\n> æ éæåšå®è£
PythonãNode.jsãMySQLïŒäžæ¡åœä»€æå®å
šéšéšçœ²ã\n\n### åçœ®èŠæ±\n\n- [Docker Desktop](https://www.docker.com/products/docker-desktop)ïŒWindows/MacïŒæ Docker EngineïŒLinuxïŒ\n\n### äžé®éšçœ²\n\n**WindowsïŒ**\n```bash\n# åå»è¿è¡ deploy.batïŒæåšåœä»€è¡æ§è¡ïŒ\ndeploy.bat\n```\n\n**Linux / macOSïŒ**\n```bash\nchmod +x deploy.sh\n./deploy.sh\n```\n\n**æåšæ§è¡ïŒçæäºèæ¬ïŒïŒ**\n```bash\n# 1. å€å¶ç¯å¢é
眮\ncp .env.docker .env\n# â çŒèŸ .env ä¿®æ¹æ°æ®åºå¯ç ãJWTå¯é¥çïŒå¯éïŒ\n\n# 2. æå»ºå¹¶å¯åš\ndocker compose up -d --build\n\n# 3. æ¥çç¶æ\ndocker compose ps\n```\n\n### 访é®å°å\n\n| æå¡ | å°å |\n|------|------|\n| ð å端 | http://localhost |\n| ð§ å端 API | http://localhost:5000 |\n| ðïž MySQL | localhost:3306 |\n\n### Docker æ¶æ\n\n```\nââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ Docker Compose â\nâ â\nâ âââââââââââââââ ââââââââââââââââ ââââââââââââââââ â\nâ â Frontend â â Backend â â MySQL â â\nâ â (Nginx) ââââ¶â (Gunicorn) ââââ¶â (5.7) â â\nâ â :80 â â :5000 â â :3306 â â\nâ â React éæ â â Flask API â â family_photoâ â\nâ âââââââââââââââ ââââââââ¬ââââââââ ââââââââââââââââ â\nâ â â\nâ ââââââââŒââââââââ â\nâ â Volumes â â\nâ â uploads_data â â\nâ â mysql_data â â\nâ ââââââââââââââââ â\nââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ\n```\n\n### é
眮诎æ\n\nç¯å¢é
眮æä»¶ `.env`ïŒä» `.env.docker` å€å¶ïŒæ¯æä»¥äžåéïŒ\n\n| åé | 诎æ | é»è®€åŒ |\n|------|------|--------|\n| `DB_PASSWORD` | MySQL root å¯ç | Chuang@123456 |\n| `JWT_SECRET` | JWT çŸåå¯é¥ | å
眮é»è®€åŒïŒå»ºè®®ä¿®æ¹ïŒ |\n| `AI_PROVIDER` | AI æå¡æäŸå | ollama |\n| `AI_MODEL` | AI æš¡ååç§° | qwen2.5:14b |\n| `FRONTEND_PORT` | åç«¯ç«¯å£ | 80 |\n| `BACKEND_PORT` | åç«¯ç«¯å£ | 5000 |\n| `MYSQL_PORT` | MySQL ç«¯å£ | 3306 |\n\n### åžžçšåœä»€\n\n```bash\ndocker compose ps # æ¥ç容åšç¶æ\ndocker compose logs -f # 宿¶æ¥çæ¥å¿\ndocker compose down # åæ¢æææå¡\ndocker compose up -d --build # éæ°æå»ºå¹¶å¯åš\ndocker compose down -v # 忢并å 逿°æ®å·ïŒâ ïž æž
ç©ºæ°æ®ïŒ\n```\n\n### Docker éšçœ²æä»¶è¯Žæ\n\n```\nPhotoManager/\nâââ docker-compose.yml # çŒææä»¶ïŒMySQL + å端 + å端ïŒ\nâââ .env.docker # ç¯å¢é
眮暡æ¿\nâââ deploy.bat # Windows äžé®éšçœ²èæ¬\nâââ deploy.sh # Linux/Mac äžé®éšçœ²èæ¬\nâââ docker/\nâ âââ mysql/\nâ âââ init.sql # MySQL èªåšåå§åèæ¬\nâââ family-photo-backend/\nâ âââ Dockerfile # å端éåæå»º\nâ âââ .dockerignore\nâ âââ docker-entrypoint.sh # å¯åšèæ¬ïŒçåŸ
MySQL + GunicornïŒ\nâââ family-photo-frontend/\n âââ Dockerfile # å端éåæå»ºïŒå€é¶æ®µïŒNodeæå»º + Nginxéšçœ²ïŒ\n âââ .dockerignore\n âââ nginx.conf # Nginx é
眮ïŒSPAè·¯ç± + API代çïŒ\n```\n\n---\n\n## ð æåšéšçœ²ïŒå¿«éåŒå§ïŒ\n\n### ç¯å¢èŠæ±\n\n| äŸèµ | çæ¬èŠæ± |\n|------|---------|\n| Python | 3.8+ |\n| Node.js | 16+ |\n| MySQL | 5.7+ |\n| npm / yarn | ææ°çš³å®ç |\n\n### 1ïžâ£ å
é项ç®\n\n```bash\ngit clone <your-repo-url>\ncd PhotoManager\n```\n\n### 2ïžâ£ åå§åæ°æ®åº\n\n```bash\n# ç»åœ MySQL åå»ºæ°æ®åº\nmysql -u root -p\n\nCREATE DATABASE family_photo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nUSE family_photo;\n\n# 富å
¥å»ºè¡š SQLïŒæ°å»ºæ°æ®åºæ¶äœ¿çšïŒ\nsource family-photo-backend/sql/table_info.sql;\n\n# ïŒå¯éïŒæå
¥æµè¯å®¶åºæå\nINSERT INTO family_member (name, relation, username, password, email) VALUES\n('çžçž', 'father', 'father', 'æ€å€å¡«SHA256ååž', 'father@example.com'),\n('åŠåŠ', 'mother', 'mother', 'æ€å€å¡«SHA256ååž', 'mother@example.com');\n```\n\n> ð¡ å¯ç ååšæµçšïŒ**å端 SHA256** â **å端 bcrypt å å¯** â åå
¥æ°æ®åºã \n> åå§å¯ç å¯å
çš `config/config.py` äžç `encrypt_password()` åœæ°çæååžã\n\n### 3ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-backend\n\n# å建èæç¯å¢ïŒæšèïŒ\npython -m venv venv\n# Windows:\nvenv\\Scripts\\activate\n# macOS/Linux:\nsource venv/bin/activate\n\n# å®è£
äŸèµ\npip install -r requirements.txt\n\n# é
眮ç¯å¢åéïŒå¿
é¡»ïŒïŒ\ncp .env.example .env\n# çŒèŸ .envïŒå¡«å
¥ä»¥äžå¿
èŠé
眮ïŒ\n# DB_PASSWORD â æ°æ®åºå¯ç \n# JWT_SECRET â JWT å¯é¥ïŒçš python -c \"import secrets; print(secrets.token_hex(32))\" çæïŒ\n# AI_API_KEY â AI æå¡å¯é¥ïŒå¯éïŒ\n\n# å¯åšæå¡\npython -m src.main\n# å端è¿è¡åš http://localhost:5000\n```\n\n### 4ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-frontend\n\n# å®è£
äŸèµ\nnpm install\n\n# å¯åšåŒåæå¡åš\nnpm start\n# å端è¿è¡åš http://localhost:3000\n# å·²é
眮代çïŒ/api/* å /uploads/* èªåšèœ¬åå°å端 5000 端å£\n```\n\n---\n\n## âïž ç¯å¢åéé
眮\n\nåš `family-photo-backend/.env` äžé
眮ïŒåè `.env.example`ïŒïŒ\n\n```bash\n# ââ æ°æ®åºé
眮ïŒå¿
å¡«ïŒïŒâââââââââââââââââââââ\nDB_HOST=localhost\nDB_USER=root\nDB_PASSWORD=äœ çæ°æ®åºå¯ç \nDB_NAME=family_photo\n\n# ââ JWT å¯é¥ïŒå¿
å¡«ïŒïŒââââââââââââââââââââââ\n# çææ¹åŒïŒpython -c \"import secrets; print(secrets.token_hex(32))\"\nJWT_SECRET=äœ çJWTå¯é¥\nJWT_EXPIRE_HOURS=24\n\n# ââ AI æå¡é
眮 ââââââââââââââââââââââââââââââ\n# æäŸåïŒopenaiïŒé»è®€ïŒ| ollamaïŒæ¬å°éšçœ²ïŒæ é API KeyïŒ\nAI_PROVIDER=openai\n\n# OpenAI / DeepSeek / éä¹åé®\nAI_API_KEY=sk-xxx\nAI_API_BASE=https://api.openai.com/v1\nAI_MODEL=gpt-3.5-turbo\n\n# ââ äœ¿çš Ollama æ¬å°éšçœ²æ¶ïŒæ¹äžºä»¥äžé
眮 âââââââ\n# AI_PROVIDER=ollama\n# AI_MODEL=llama3\n# AI_API_BASE=http://localhost:11434\n\n# ââ éçš âââââââââââââââââââââââââââââââââââââ\nAI_TIMEOUT=60\n\n# ââ Flask ç¯å¢ âââââââââââââââââââââââââââââââ\nFLASK_ENV=dev\n```\n\n> â ïž **å®å
šæç€ºïŒ** \n> - `DB_PASSWORD` å `JWT_SECRET` 䞺å¿
å¡«é¡¹ïŒæªé
çœ®æ¶æå¡å¯åšäŒæ¥é \n> - `.env` æä»¶å·²åš `.gitignore` äžïŒäžäŒè¢«æäº€å°ä»£ç ä»åº \n> - ç产ç¯å¢è¯·å¡å¿
䜿çšåŒºéæºå笊䞲äœäžº `JWT_SECRET`\n\n---\n\n## ðïž æ°æ®åºè®Ÿè®¡\n\n```\nfamily_member (å®¶åºæå)\n â\n âââ< album (çžå)\n â â\n â âââ< photo (ç
§ç)\n â â\n â âââ< favorite_photo (æ¶èè®°åœ) >ââ favorite_folder (æ¶è倹)\n â\n âââ< ai_chat_message (AI è倩记åœ)\n```\n\n| 衚å | 诎æ | å
³é®å段 |\n|------|------|---------|\n| `family_member` | å®¶åºæå / ç»åœèŽŠå· | `username`(å¯äž), `password`(bcrypt), **`is_admin`**(æéæ è¯) |\n| `album` | çžå | `album_name`, `cover_path`, **`creator_id`**(å建è
), `last_upload_user_id` |\n| `photo` | ç
§ç | `file_path`, `album_id`, `member_id`(åœå±), `operator_id`(äžäŒ è
) |\n| `favorite_folder` | æ¶è倹 | `folder_name`, `member_id`, `is_default` |\n| `favorite_photo` | æ¶èè®°åœ | `folder_id`, `photo_id`, `member_id`ïŒå¯äžçºŠæé²éå€ïŒ |\n| `ai_chat_message` | AI èå€©è®°åœ | `member_id`, `role`(user/assistant), `content` |\n\n> å ç²åæ®µäžºæ¬æ¬¡æ°å¢ã宿Žå»ºè¡š SQL è§ `family-photo-backend/sql/table_info.sql`\n\n---\n\n## ð¡ API æ¥å£\n\n### 讀è¯\n\n| æ¹æ³ | è·¯åŸ | 诎æ |\n|------|------|------|\n| POST | `/api/login` | ç»åœïŒè¿å JWT TokenïŒ |\n| POST | `/api/logout` | ç»åº |\n\n### çžå\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/albums` | â
| è·åææçžåå衚 |\n| POST | `/api/album/create` | â
| å建çžåïŒå¯äžäŒ å°é¢ïŒ |\n| POST | `/api/album/rename` | â
| ä¿®æ¹çžååç§° |\n| POST | `/api/album/delete` | â
| å é€çžåïŒçº§èå é€ç
§çïŒ |\n| POST | `/api/album/cover/upload` | â
| äžäŒ /æŽæ¢çžåå°é¢ |\n| GET | `/api/photos/album/:id` | â
| è·åçžåäžç
§çïŒåé¡µïŒ |\n\n### ç
§ç\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| POST | `/api/photos/upload` | â
| äžäŒ ç
§ç |\n| POST | `/api/photos/delete` | â
| å é€ç
§ç |\n| GET | `/api/photos/search` | â
| æçŽ¢ç
§çïŒæ¯æå€æ¡ä»¶çéïŒ |\n\n### æ¶è\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/favorite/folders` | â
| è·åæ¶è倹å衚 |\n| POST | `/api/favorite/folders` | â
| å建æ¶è倹 |\n| PUT | `/api/favorite/folders/:id` | â
| ä¿®æ¹æ¶è倹åç§° |\n| DELETE | `/api/favorite/folders/:id` | â
| å 逿¶è倹 |\n| POST | `/api/favorite/photos` | â
| ç
§çå å
¥æ¶è |\n| DELETE | `/api/favorite/photos` | â
| ç
§çç§»åºæ¶è |\n| GET | `/api/favorite/photos/:folder_id` | â
| è·åæ¶è倹å
ç
§ç |\n\n### æå & AI\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/current-member` | â
| è·ååœåç»åœæåä¿¡æ¯ |\n| GET | `/api/members` | â
| è·åææå®¶åºæå |\n| GET | `/api/chat/history` | â
| è·åè倩记åœïŒé»è®€è¿ 3 䞪æïŒ |\n| POST | `/api/chat/send` | â
| åéæ¶æ¯ & è·å AI åå€ |\n| GET | `/api/chat/models` | â
| æ¥è¯¢ AI æäŸå & å¯çšæš¡å |\n| POST | `/api/chat/clear` | â
| æž
空èå€©è®°åœ |\n\n### éææä»¶\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/uploads/photos/:filename` | â
| è·åç
§çæä»¶ |\n| GET | `/uploads/covers/:filename` | â
| è·åå°é¢æä»¶ |\n\n---\n\n## ð å®å
šæºå¶\n\n| å±é¢ | æªæœ |\n|------|------|\n| **讀è¯** | JWT TokenïŒHS256ïŒ24h è¿æïŒïŒ`@login_required` è£
饰åšä¿æ€æææ¥å£ |\n| **å¯ç ** | å端 SHA256 + å端 bcryptïŒsalt rounds=12ïŒåéå å¯ |\n| **æéé犻** | `is_admin` åæ®µåºå管çå/æ®éçšæ·ïŒæ®éçšæ·åªèœæäœèªå·±å建ççžååç
§ç |\n| **æä»¶äžäŒ ** | æ©å±åçœååæ ¡éªã`secure_filename` è¿æ»€ãå¯äžæä»¶åé²èŠç |\n| **è·¯åŸå®å
š** | äžäŒ ç®åœéå¶ + `_safe_path()` ç»å¯¹è·¯åŸæ ¡éªïŒé²æ¢è·¯åŸéå |\n| **æ°æ®æ ¡éª** | `album_id` çåæ°åŒºå¶æ£æŽæ°æ ¡éªïŒååšæ§æ ¡éªé²æ¢å¯¹äžååšèµæºæäœ |\n| **SQL 泚å
¥** | å
šéšäœ¿çš PyMySQL åæ°åæ¥è¯¢ (`%s`) |\n| **CORS** | Flask-CORS é
眮 `supports_credentials=True` |\n\n---\n\n## ð¥ æéäœç³»\n\n### è§è²è¯Žæ\n\n| è§è² | `is_admin` åŒ | æéèåŽ |\n|------|--------------|---------|\n| **管çå** | `1` | æ¥çææçžåïŒæäœææèµæº |\n| **æ®éæå** | `0`ïŒé»è®€ïŒ | åªèœæ¥ç/æäœ**èªå·±å建ç**çžååç
§ç |\n\n### æéé
眮\n\nåšæ°æ®åºäžæåšè®Ÿçœ®ïŒ\n\n```sql\n-- å°æçšæ·è®Ÿäžºç®¡çå\nUPDATE family_member SET is_admin = 1 WHERE username = 'father';\n\n-- åæ¶ç®¡çåæé\nUPDATE family_member SET is_admin = 0 WHERE username = 'father';\n\n-- æ¥çæææåçæé\nSELECT id, name, username, is_admin FROM family_member;\n```\n\n### æéèŠçèåŽ\n\n| æäœ | 管çå | æ®éæå |\n|------|--------|---------|\n| æ¥ççžåå衚 | ææçžå | ä»
èªå·±å建ç |\n| æ¥ççžåç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| å建çžå | â
| â
ïŒèªåšæäžºå建è
ïŒ |\n| éåœå/å é€çžå | ä»»æçžå | ä»
èªå·±å建ç |\n| æŽæ¢çžåå°é¢ | ä»»æçžå | ä»
èªå·±å建ç |\n| äžäŒ ç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| å é€ç
§ç | ä»»æç
§ç | ä»
èªå·±çžåäžçç
§ç |\n| æçŽ¢ç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| æ¶è/åæ¶æ¶è | â
| â
ïŒæ¶è䞺䞪人è¡äžºïŒæ éæéïŒ |\n\n---\n\n## ð€ AI 婿\n\næ¯æäžç§ AI æäŸåïŒéè¿ç¯å¢åé `AI_PROVIDER` 忢ïŒ\n\n| æäŸå | é
眮æ¹åŒ | ç¹ç¹ |\n|--------|---------|------|\n| **OpenAI** | é
眮 `AI_API_KEY` + `AI_API_BASE` | æ¯æ OpenAI / DeepSeek / éä¹åé®çå
Œå®¹æ¥å£ |\n| **Ollama** | `AI_PROVIDER=ollama` | æ¬å°éšçœ²ïŒæ é API KeyïŒå®å
šçŠ»çº¿ |\n| **æš¡ææš¡åŒ** | äžé
眮 `AI_API_KEY` | èªåšäœ¿çšé¢è®Ÿåå€ïŒæ¹äŸ¿äœéªåèœ |\n\n---\n\n## ðž åèœé¢è§\n\n| é¡µé¢ | åèœ |\n|------|------|\n| ð ç»åœé¡µ | æž©éŠšé£æ Œç»åœïŒSHA256 å¯ç å å¯äŒ èŸ |\n| ð çžåå衚 | å±ç€ºææçžåïŒæŸç€ºå°é¢ãæåäžäŒ äººåæ¶éŽ |\n| ðŒïž çžå诊æ
| ç
§ççœæ Œå±ç€ºãå页å 蜜ãæçŽ¢çé |\n| ð€ äžäŒ åºå | å¡«ååœå±æåãæææ¶éŽã倿³šåæ¹éäžäŒ |\n| ð· æç
§äžäŒ | è°çšæå倎æç
§ â é¢è§ â éæ©åœå±äºº â äžäŒ |\n| ð æ¶è管ç | æ¶è倹å衚 + æ¶èç
§ççœæ ŒïŒæ¯æå¢å æ¹ |\n| ð€ AI 婿 | å
šå±æ¬æµ®å¯¹è¯çªïŒèå€©è®°åœæèŽŠå·é犻 |\n\n---\n\n## ð ïž åŒå诎æ\n\n### å端代çé
眮ïŒ`craco.config.js`ïŒ\n\n```javascript\n// åŒåç¯å¢äžèªåšä»£çå°å端\n'/api' â http://localhost:5000/api\n'/uploads' â http://localhost:5000/uploads\n```\n\n### æ¥å¿ç³»ç»\n\n- æ¥å¿ç®åœïŒ`family-photo-backend/logs/`\n- `info.log`ïŒåžžè§æäœæ¥å¿ïŒä¿ç 30 倩ïŒ\n- `error.log`ïŒé误æ¥å¿ïŒä¿ç 90 倩ïŒ\n- æå€©èªåšèœ®èœ¬ïŒWindows äžå®å
šå€çæä»¶éå®\n\n### å¯ç å 坿µçš\n\n```\nçšæ·èŸå
¥ææå¯ç \n â\n âŒ\nå端 CryptoJS.SHA256(password) âââ SHA256 ååž\n â\n âŒ\nPOST /api/login { password: sha256_hash }\n â\n âŒ\nå端 bcrypt.checkpw(sha256_hash, db_bcrypt_hash) âââ éªè¯ç»æ\n```\n\n---\n\n## ð åŸ
åäºé¡¹\n\n- [ ] å®ç° Token é»ååæºå¶ïŒäœ¿ Logout çæ£çæ\n- [ ] æ·»å ç
§ç EXIF ä¿¡æ¯èªåšæåïŒæææ¶éŽãGPS çïŒ\n- [ ] æ¯æç
§çå猩äžäŒ ïŒåå°ååšç©ºéŽ\n- [ ] æ·»å ç
§çå享åèœïŒçæäžŽæ¶éŸæ¥ïŒ\n\n---\n\n## ð License\n\nMIT License © 2026 Family Photo Manager"}, "50547fdfec0822bc60189a109af0db1cd0cb289e": {"t": 1785771641.277322, "n": 14354, "h": "ec833026c60b2f9b", "c": "<div align=\"center\">\n\n# ð· å®¶åºçžå Family Photo Manager\n\n**è®°åœæ¯äžäžªæž©éŠšç¬éŽïŒè®©å®¶äººä¹éŽçè·çŠ»æŽè¿äžç¹ ð**\n\n[](https://www.python.org/)\n[](https://flask.palletsprojects.com/)\n[](https://react.dev/)\n[](https://ant.design/)\n[](https://www.mysql.com/)\n\n</div>\n\n---\n\n## ð 项ç®ç®ä»\n\nå®¶åºçžåæ¯äžäžªäžäžºå®¶åºè®Ÿè®¡çç§å¯ç
§ç管çç³»ç»ãæ¯æå€å®¶åºæåå
±äº«çžåãç
§çäžäŒ /æç
§ãæ¶è管çïŒå¹¶å
眮 AI æºèœå©ææäŸæž©éŠšäºåšãæææ°æ®ååšåšæ¬å°ïŒä¿æ€å®¶åºéç§ã\n\n### âš æ žå¿åèœ\n\n| åèœæš¡å | æè¿° |\n|---------|------|\n| ð **çžå管ç** | å建ãéåœåãå é€çžåïŒèªå®ä¹å°é¢ |\n| ð€ **ç
§çäžäŒ ** | æ¯ææ¹éäžäŒ ïŒè®°åœæææ¶éŽãåœå±æåã倿³š |\n| ð· **æç
§åèœ** | è°çšæåå€ŽçŽæ¥æç
§å¹¶äžäŒ ïŒé HTTPS ç¯å¢ïŒ |\n| ð **ç
§çæçŽ¢** | æåç§°ãåœå±äººãäžäŒ è
ãæ¥æèåŽçé |\n| ð **æ¶è管ç** | å建æ¶è倹ïŒç
§çå å
¥æ¶è/ç§»åºïŒæ¯æå€äžªæ¶è倹 |\n| ðšâð©âð§ **å®¶åºæå** | 倿å管çïŒç
§çè®°åœåœå±äººåäžäŒ è
|\n| ð€ **AI 婿** | å
眮 AI 对è¯åèœïŒæ¯æ OpenAI / Ollama çå€ç§æäŸå |\n| ð **ç»åœéŽæ** | JWT Token 讀è¯ïŒSHA256 + bcrypt åéå¯ç å å¯ |\n\n---\n\n## ðïž ææ¯æ¶æ\n\n```\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (React 19) â\nâ Ant Design 6 · React Router 7 · Axios · Less â\nâ :3000 (craco) â\nââââââââââââââââââââââââ¬âââââââââââââââââââââââââââââââ\n â /api/* /uploads/*\n âŒ\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (Flask 2.3) â\nâ PyJWT · bcrypt · PyMySQL · Werkzeug · python-dotenvâ\nâ :5000 â\nâââââââââ¬ââââââââââââââââââââââââââ¬ââââââââââââââââââââ\n ⌠âŒ\nâââââââââââââââââ ââââââââââââââââââââ\nâ MySQL 5.7+ â â æ¬å°æä»¶ç³»ç» â\nâ family_photo â â uploads/photos â\nâ â â uploads/covers â\nâââââââââââââââââ ââââââââââââââââââââ\n```\n\n---\n\n## ð 项ç®ç»æ\n\n```\nPhotoManager/\nâââ family-photo-backend/ # ð§ å端 (Flask)\nâ âââ config/\nâ â âââ config.py # æ°æ®åº/JWT/å¯ç å å¯é
眮\nâ â âââ log_config.py # æ¥å¿é
çœ®ïŒæå€©èœ®èœ¬ïŒ\nâ âââ src/\nâ â âââ main.py # Flask åºçšå
¥å£\nâ â âââ auth.py # ç»åœ/ç»åº/Token éŽæ\nâ â âââ album.py # çžå CRUD + å°é¢äžäŒ \nâ â âââ photo.py # ç
§çäžäŒ /æçŽ¢/å é€\nâ â âââ favorite.py # æ¶è倹 & æ¶èç
§ç管ç\nâ â âââ member.py # å®¶åºæåæ¥è¯¢\nâ â âââ file.py # éææä»¶æå¡ïŒè·¯åŸéå鲿€ïŒ\nâ â âââ chat.py # AI å¯¹è¯æ¥å£\nâ â âââ ai_service.py # AI æå¡ïŒOpenAI/OllamaïŒ\nâ â âââ utils.py # æ°æ®åºè¿æ¥ & å·¥å
·åœæ°\nâ âââ sql/\nâ â âââ table_info.sql # æ°æ®åºå»ºè¡š SQL\nâ âââ uploads/ # äžäŒ æä»¶ååš\nâ â âââ photos/ # ç
§çæä»¶ïŒæ album_id åç®åœïŒ\nâ â âââ covers/ # çžåå°é¢\nâ âââ logs/ # æ¥å¿æä»¶ïŒèªåšçæïŒ\nâ âââ .env.example # ç¯å¢åéæš¡æ¿\nâ âââ requirements.txt # Python äŸèµ\nâ\nâââ family-photo-frontend/ # ðš å端 (React)\nâ âââ src/\nâ â âââ pages/\nâ â â âââ Login/ # ç»åœé¡µ\nâ â â âââ AlbumList/ # çžåå衚\nâ â â âââ AlbumDetail/ # çžå诊æ
\nâ â â â âââ components/\nâ â â â â âââ AlbumPhotoList/ # ç
§çå衚ïŒå页ïŒ\nâ â â â â âââ UploadArare/ # ç
§çäžäŒ åº\nâ â â â â âââ PhotoCapture/ # æå倎æç
§\nâ â â â âââ popwin/\nâ â â â âââ FilterPopwin/ # æçŽ¢çé匹çª\nâ â â â âââ FavoriteConfirmModal/ # æ¶è确讀匹çª\nâ â â âââ CreateAlbum/ # å建çžå\nâ â â âââ FavoriteManager/ # æ¶è管ç\nâ â âââ components/\nâ â â âââ PrivateRoute.js # è·¯ç±å®å«\nâ â â âââ HeaderComponent.js # é¡¶éšå¯Œèª\nâ â â âââ AiChat/ # AI å¯¹è¯æ¬æµ®çª\nâ â â âââ ButtonWrapper/ # æé®å°è£
\nâ â âââ contexts/\nâ â â âââ MemberContext.js # åœåç»åœæåäžäžæ\nâ â âââ utils/\nâ â â âââ request.js # Axios å°è£
ïŒToken æŠæªåšïŒ\nâ â â âââ dateUtil.js # æ¥ææ ŒåŒåå·¥å
·\nâ â âââ App.js # è·¯ç±é
眮å
¥å£\nâ âââ craco.config.js # Craco é
眮ïŒLess/代çïŒ\nâ âââ package.json\nâ\nâââ README.md\n```\n\n---\n\n## ð³ Docker äžé®éšçœ²ïŒæšèïŒ\n\n> æ éæåšå®è£
PythonãNode.jsãMySQLïŒäžæ¡åœä»€æå®å
šéšéšçœ²ã\n\n### åçœ®èŠæ±\n\n- [Docker Desktop](https://www.docker.com/products/docker-desktop)ïŒWindows/MacïŒæ Docker EngineïŒLinuxïŒ\n\n### äžé®éšçœ²\n\n**WindowsïŒ**\n```bash\n# åå»è¿è¡ deploy.batïŒæåšåœä»€è¡æ§è¡ïŒ\ndeploy.bat\n```\n\n**Linux / macOSïŒ**\n```bash\nchmod +x deploy.sh\n./deploy.sh\n```\n\n**æåšæ§è¡ïŒçæäºèæ¬ïŒïŒ**\n```bash\n# 1. å€å¶ç¯å¢é
眮\ncp .env.docker .env\n# â çŒèŸ .env ä¿®æ¹æ°æ®åºå¯ç ãJWTå¯é¥çïŒå¯éïŒ\n\n# 2. æå»ºå¹¶å¯åš\ndocker compose up -d --build\n\n# 3. æ¥çç¶æ\ndocker compose ps\n```\n\n### 访é®å°å\n\n| æå¡ | å°å |\n|------|------|\n| ð å端 | http://localhost |\n| ð§ å端 API | http://localhost:5000 |\n| ðïž MySQL | localhost:3306 |\n\n### Docker æ¶æ\n\n```\nââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ Docker Compose â\nâ â\nâ âââââââââââââââ ââââââââââââââââ ââââââââââââââââ â\nâ â Frontend â â Backend â â MySQL â â\nâ â (Nginx) ââââ¶â (Gunicorn) ââââ¶â (5.7) â â\nâ â :80 â â :5000 â â :3306 â â\nâ â React éæ â â Flask API â â family_photoâ â\nâ âââââââââââââââ ââââââââ¬ââââââââ ââââââââââââââââ â\nâ â â\nâ ââââââââŒââââââââ â\nâ â Volumes â â\nâ â uploads_data â â\nâ â mysql_data â â\nâ ââââââââââââââââ â\nââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ\n```\n\n### é
眮诎æ\n\nç¯å¢é
眮æä»¶ `.env`ïŒä» `.env.docker` å€å¶ïŒæ¯æä»¥äžåéïŒ\n\n| åé | 诎æ | é»è®€åŒ |\n|------|------|--------|\n| `DB_PASSWORD` | MySQL root å¯ç | Chuang@123456 |\n| `JWT_SECRET` | JWT çŸåå¯é¥ | å
眮é»è®€åŒïŒå»ºè®®ä¿®æ¹ïŒ |\n| `AI_PROVIDER` | AI æå¡æäŸå | ollama |\n| `AI_MODEL` | AI æš¡ååç§° | qwen2.5:14b |\n| `FRONTEND_PORT` | åç«¯ç«¯å£ | 80 |\n| `BACKEND_PORT` | åç«¯ç«¯å£ | 5000 |\n| `MYSQL_PORT` | MySQL ç«¯å£ | 3306 |\n\n### åžžçšåœä»€\n\n```bash\ndocker compose ps # æ¥ç容åšç¶æ\ndocker compose logs -f # 宿¶æ¥çæ¥å¿\ndocker compose down # åæ¢æææå¡\ndocker compose up -d --build # éæ°æå»ºå¹¶å¯åš\ndocker compose down -v # 忢并å 逿°æ®å·ïŒâ ïž æž
ç©ºæ°æ®ïŒ\n```\n\n### Docker éšçœ²æä»¶è¯Žæ\n\n```\nPhotoManager/\nâââ docker-compose.yml # çŒææä»¶ïŒMySQL + å端 + å端ïŒ\nâââ .env.docker # ç¯å¢é
眮暡æ¿\nâââ deploy.bat # Windows äžé®éšçœ²èæ¬\nâââ deploy.sh # Linux/Mac äžé®éšçœ²èæ¬\nâââ docker/\nâ âââ mysql/\nâ âââ init.sql # MySQL èªåšåå§åèæ¬\nâââ family-photo-backend/\nâ âââ Dockerfile # å端éåæå»º\nâ âââ .dockerignore\nâ âââ docker-entrypoint.sh # å¯åšèæ¬ïŒçåŸ
MySQL + GunicornïŒ\nâââ family-photo-frontend/\n âââ Dockerfile # å端éåæå»ºïŒå€é¶æ®µïŒNodeæå»º + Nginxéšçœ²ïŒ\n âââ .dockerignore\n âââ nginx.conf # Nginx é
眮ïŒSPAè·¯ç± + API代çïŒ\n```\n\n---\n\n## ð æåšéšçœ²ïŒå¿«éåŒå§ïŒ\n\n### ç¯å¢èŠæ±\n\n| äŸèµ | çæ¬èŠæ± |\n|------|---------|\n| Python | 3.8+ |\n| Node.js | 16+ |\n| MySQL | 5.7+ |\n| npm / yarn | ææ°çš³å®ç |\n\n### 1ïžâ£ å
é项ç®\n\n```bash\ngit clone <your-repo-url>\ncd PhotoManager\n```\n\n### 2ïžâ£ åå§åæ°æ®åº\n\n```bash\n# ç»åœ MySQL åå»ºæ°æ®åº\nmysql -u root -p\n\nCREATE DATABASE family_photo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nUSE family_photo;\n\n# 富å
¥å»ºè¡š SQLïŒæ°å»ºæ°æ®åºæ¶äœ¿çšïŒ\nsource family-photo-backend/sql/table_info.sql;\n\n# ïŒå¯éïŒæå
¥æµè¯å®¶åºæå\nINSERT INTO family_member (name, relation, username, password, email) VALUES\n('çžçž', 'father', 'father', 'æ€å€å¡«SHA256ååž', 'father@example.com'),\n('åŠåŠ', 'mother', 'mother', 'æ€å€å¡«SHA256ååž', 'mother@example.com');\n```\n\n> ð¡ å¯ç ååšæµçšïŒ**å端 SHA256** â **å端 bcrypt å å¯** â åå
¥æ°æ®åºã \n> åå§å¯ç å¯å
çš `config/config.py` äžç `encrypt_password()` åœæ°çæååžã\n\n### 3ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-backend\n\n# å建èæç¯å¢ïŒæšèïŒ\npython -m venv venv\n# Windows:\nvenv\\Scripts\\activate\n# macOS/Linux:\nsource venv/bin/activate\n\n# å®è£
äŸèµ\npip install -r requirements.txt\n\n# é
眮ç¯å¢åéïŒå¿
é¡»ïŒïŒ\ncp .env.example .env\n# çŒèŸ .envïŒå¡«å
¥ä»¥äžå¿
èŠé
眮ïŒ\n# DB_PASSWORD â æ°æ®åºå¯ç \n# JWT_SECRET â JWT å¯é¥ïŒçš python -c \"import secrets; print(secrets.token_hex(32))\" çæïŒ\n# AI_API_KEY â AI æå¡å¯é¥ïŒå¯éïŒ\n\n# å¯åšæå¡\npython -m src.main\n# å端è¿è¡åš http://localhost:5000\n```\n\n### 4ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-frontend\n\n# å®è£
äŸèµ\nnpm install\n\n# å¯åšåŒåæå¡åš\nnpm start\n# å端è¿è¡åš http://localhost:3000\n# å·²é
眮代çïŒ/api/* å /uploads/* èªåšèœ¬åå°å端 5000 端å£\n```\n\n---\n\n## âïž ç¯å¢åéé
眮\n\nåš `family-photo-backend/.env` äžé
眮ïŒåè `.env.example`ïŒïŒ\n\n```bash\n# ââ æ°æ®åºé
眮ïŒå¿
å¡«ïŒïŒâââââââââââââââââââââ\nDB_HOST=localhost\nDB_USER=root\nDB_PASSWORD=äœ çæ°æ®åºå¯ç \nDB_NAME=family_photo\n\n# ââ JWT å¯é¥ïŒå¿
å¡«ïŒïŒââââââââââââââââââââââ\n# çææ¹åŒïŒpython -c \"import secrets; print(secrets.token_hex(32))\"\nJWT_SECRET=äœ çJWTå¯é¥\nJWT_EXPIRE_HOURS=24\n\n# ââ AI æå¡é
眮 ââââââââââââââââââââââââââââââ\n# æäŸåïŒopenaiïŒé»è®€ïŒ| ollamaïŒæ¬å°éšçœ²ïŒæ é API KeyïŒ\nAI_PROVIDER=openai\n\n# OpenAI / DeepSeek / éä¹åé®\nAI_API_KEY=sk-xxx\nAI_API_BASE=https://api.openai.com/v1\nAI_MODEL=gpt-3.5-turbo\n\n# ââ äœ¿çš Ollama æ¬å°éšçœ²æ¶ïŒæ¹äžºä»¥äžé
眮 âââââââ\n# AI_PROVIDER=ollama\n# AI_MODEL=llama3\n# AI_API_BASE=http://localhost:11434\n\n# ââ éçš âââââââââââââââââââââââââââââââââââââ\nAI_TIMEOUT=60\n\n# ââ Flask ç¯å¢ âââââââââââââââââââââââââââââââ\nFLASK_ENV=dev\n```\n\n> â ïž **å®å
šæç€ºïŒ** \n> - `DB_PASSWORD` å `JWT_SECRET` 䞺å¿
å¡«é¡¹ïŒæªé
çœ®æ¶æå¡å¯åšäŒæ¥é \n> - `.env` æä»¶å·²åš `.gitignore` äžïŒäžäŒè¢«æäº€å°ä»£ç ä»åº \n> - ç产ç¯å¢è¯·å¡å¿
䜿çšåŒºéæºå笊䞲äœäžº `JWT_SECRET`\n\n---\n\n## ðïž æ°æ®åºè®Ÿè®¡\n\n```\nfamily_member (å®¶åºæå)\n â\n âââ< album (çžå)\n â â\n â âââ< photo (ç
§ç)\n â â\n â âââ< favorite_photo (æ¶èè®°åœ) >ââ favorite_folder (æ¶è倹)\n â\n âââ< ai_chat_message (AI è倩记åœ)\n```\n\n| 衚å | 诎æ | å
³é®å段 |\n|------|------|---------|\n| `family_member` | å®¶åºæå / ç»åœèŽŠå· | `username`(å¯äž), `password`(bcrypt), **`is_admin`**(æéæ è¯) |\n| `album` | çžå | `album_name`, `cover_path`, **`creator_id`**(å建è
), `last_upload_user_id` |\n| `photo` | ç
§ç | `file_path`, `album_id`, `member_id`(åœå±), `operator_id`(äžäŒ è
) |\n| `favorite_folder` | æ¶è倹 | `folder_name`, `member_id`, `is_default` |\n| `favorite_photo` | æ¶èè®°åœ | `folder_id`, `photo_id`, `member_id`ïŒå¯äžçºŠæé²éå€ïŒ |\n| `ai_chat_message` | AI èå€©è®°åœ | `member_id`, `role`(user/assistant), `content` |\n\n> å ç²åæ®µäžºæ¬æ¬¡æ°å¢ã宿Žå»ºè¡š SQL è§ `family-photo-backend/sql/table_info.sql`\n\n---\n\n## ð¡ API æ¥å£\n\n### 讀è¯\n\n| æ¹æ³ | è·¯åŸ | 诎æ |\n|------|------|------|\n| POST | `/api/login` | ç»åœïŒè¿å JWT TokenïŒ |\n| POST | `/api/logout` | ç»åº |\n\n### çžå\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/albums` | â
| è·åææçžåå衚 |\n| POST | `/api/album/create` | â
| å建çžåïŒå¯äžäŒ å°é¢ïŒ |\n| POST | `/api/album/rename` | â
| ä¿®æ¹çžååç§° |\n| POST | `/api/album/delete` | â
| å é€çžåïŒçº§èå é€ç
§çïŒ |\n| POST | `/api/album/cover/upload` | â
| äžäŒ /æŽæ¢çžåå°é¢ |\n| GET | `/api/photos/album/:id` | â
| è·åçžåäžç
§çïŒåé¡µïŒ |\n\n### ç
§ç\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| POST | `/api/photos/upload` | â
| äžäŒ ç
§ç |\n| POST | `/api/photos/delete` | â
| å é€ç
§ç |\n| GET | `/api/photos/search` | â
| æçŽ¢ç
§çïŒæ¯æå€æ¡ä»¶çéïŒ |\n\n### æ¶è\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/favorite/folders` | â
| è·åæ¶è倹å衚 |\n| POST | `/api/favorite/folders` | â
| å建æ¶è倹 |\n| PUT | `/api/favorite/folders/:id` | â
| ä¿®æ¹æ¶è倹åç§° |\n| DELETE | `/api/favorite/folders/:id` | â
| å 逿¶è倹 |\n| POST | `/api/favorite/photos` | â
| ç
§çå å
¥æ¶è |\n| DELETE | `/api/favorite/photos` | â
| ç
§çç§»åºæ¶è |\n| GET | `/api/favorite/photos/:folder_id` | â
| è·åæ¶è倹å
ç
§ç |\n\n### æå & AI\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/current-member` | â
| è·ååœåç»åœæåä¿¡æ¯ |\n| GET | `/api/members` | â
| è·åææå®¶åºæå |\n| GET | `/api/chat/history` | â
| è·åè倩记åœïŒé»è®€è¿ 3 䞪æïŒ |\n| POST | `/api/chat/send` | â
| åéæ¶æ¯ & è·å AI åå€ |\n| GET | `/api/chat/models` | â
| æ¥è¯¢ AI æäŸå & å¯çšæš¡å |\n| POST | `/api/chat/clear` | â
| æž
空èå€©è®°åœ |\n\n### éææä»¶\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/uploads/photos/:filename` | â
| è·åç
§çæä»¶ |\n| GET | `/uploads/covers/:filename` | â
| è·åå°é¢æä»¶ |\n\n---\n\n## ð å®å
šæºå¶\n\n| å±é¢ | æªæœ |\n|------|------|\n| **讀è¯** | JWT TokenïŒHS256ïŒ24h è¿æïŒïŒ`@login_required` è£
饰åšä¿æ€æææ¥å£ |\n| **å¯ç ** | å端 SHA256 + å端 bcryptïŒsalt rounds=12ïŒåéå å¯ |\n| **æéé犻** | `is_admin` åæ®µåºå管çå/æ®éçšæ·ïŒæ®éçšæ·åªèœæäœèªå·±å建ççžååç
§ç |\n| **æä»¶äžäŒ ** | æ©å±åçœååæ ¡éªã`secure_filename` è¿æ»€ãå¯äžæä»¶åé²èŠç |\n| **è·¯åŸå®å
š** | äžäŒ ç®åœéå¶ + `_safe_path()` ç»å¯¹è·¯åŸæ ¡éªïŒé²æ¢è·¯åŸéå |\n| **æ°æ®æ ¡éª** | `album_id` çåæ°åŒºå¶æ£æŽæ°æ ¡éªïŒååšæ§æ ¡éªé²æ¢å¯¹äžååšèµæºæäœ |\n| **SQL 泚å
¥** | å
šéšäœ¿çš PyMySQL åæ°åæ¥è¯¢ (`%s`) |\n| **CORS** | Flask-CORS é
眮 `supports_credentials=True` |\n\n---\n\n## ð¥ æéäœç³»\n\n### è§è²è¯Žæ\n\n| è§è² | `is_admin` åŒ | æéèåŽ |\n|------|--------------|---------|\n| **管çå** | `1` | æ¥çææçžåïŒæäœææèµæº |\n| **æ®éæå** | `0`ïŒé»è®€ïŒ | åªèœæ¥ç/æäœ**èªå·±å建ç**çžååç
§ç |\n\n### æéé
眮\n\nåšæ°æ®åºäžæåšè®Ÿçœ®ïŒ\n\n```sql\n-- å°æçšæ·è®Ÿäžºç®¡çå\nUPDATE family_member SET is_admin = 1 WHERE username = 'father';\n\n-- åæ¶ç®¡çåæé\nUPDATE family_member SET is_admin = 0 WHERE username = 'father';\n\n-- æ¥çæææåçæé\nSELECT id, name, username, is_admin FROM family_member;\n```\n\n### æéèŠçèåŽ\n\n| æäœ | 管çå | æ®éæå |\n|------|--------|---------|\n| æ¥ççžåå衚 | ææçžå | ä»
èªå·±å建ç |\n| æ¥ççžåç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| å建çžå | â
| â
ïŒèªåšæäžºå建è
ïŒ |\n| éåœå/å é€çžå | ä»»æçžå | ä»
èªå·±å建ç |\n| æŽæ¢çžåå°é¢ | ä»»æçžå | ä»
èªå·±å建ç |\n| äžäŒ ç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| å é€ç
§ç | ä»»æç
§ç | ä»
èªå·±çžåäžçç
§ç |\n| æçŽ¢ç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| æ¶è/åæ¶æ¶è | â
| â
ïŒæ¶è䞺䞪人è¡äžºïŒæ éæéïŒ |\n\n---\n\n## ð€ AI 婿\n\næ¯æäžç§ AI æäŸåïŒéè¿ç¯å¢åé `AI_PROVIDER` 忢ïŒ\n\n| æäŸå | é
眮æ¹åŒ | ç¹ç¹ |\n|--------|---------|------|\n| **OpenAI** | é
眮 `AI_API_KEY` + `AI_API_BASE` | æ¯æ OpenAI / DeepSeek / éä¹åé®çå
Œå®¹æ¥å£ |\n| **Ollama** | `AI_PROVIDER=ollama` | æ¬å°éšçœ²ïŒæ é API KeyïŒå®å
šçŠ»çº¿ |\n| **æš¡ææš¡åŒ** | äžé
眮 `AI_API_KEY` | èªåšäœ¿çšé¢è®Ÿåå€ïŒæ¹äŸ¿äœéªåèœ |\n\n---\n\n## ðž åèœé¢è§\n\n| é¡µé¢ | åèœ |\n|------|------|\n| ð ç»åœé¡µ | æž©éŠšé£æ Œç»åœïŒSHA256 å¯ç å å¯äŒ èŸ |\n| ð çžåå衚 | å±ç€ºææçžåïŒæŸç€ºå°é¢ãæåäžäŒ äººåæ¶éŽ |\n| ðŒïž çžå诊æ
| ç
§ççœæ Œå±ç€ºãå页å 蜜ãæçŽ¢çé |\n| ð€ äžäŒ åºå | å¡«ååœå±æåãæææ¶éŽã倿³šåæ¹éäžäŒ |\n| ð· æç
§äžäŒ | è°çšæå倎æç
§ â é¢è§ â éæ©åœå±äºº â äžäŒ |\n| ð æ¶è管ç | æ¶è倹å衚 + æ¶èç
§ççœæ ŒïŒæ¯æå¢å æ¹ |\n| ð€ AI 婿 | å
šå±æ¬æµ®å¯¹è¯çªïŒèå€©è®°åœæèŽŠå·é犻 |\n\n---\n\n## ð ïž åŒå诎æ\n\n### å端代çé
眮ïŒ`craco.config.js`ïŒ\n\n```javascript\n// åŒåç¯å¢äžèªåšä»£çå°å端\n'/api' â http://localhost:5000/api\n'/uploads' â http://localhost:5000/uploads\n```\n\n### æ¥å¿ç³»ç»\n\n- æ¥å¿ç®åœïŒ`family-photo-backend/logs/`\n- `info.log`ïŒåžžè§æäœæ¥å¿ïŒä¿ç 30 倩ïŒ\n- `error.log`ïŒé误æ¥å¿ïŒä¿ç 90 倩ïŒ\n- æå€©èªåšèœ®èœ¬ïŒWindows äžå®å
šå€çæä»¶éå®\n\n### å¯ç å 坿µçš\n\n```\nçšæ·èŸå
¥ææå¯ç \n â\n âŒ\nå端 CryptoJS.SHA256(password) âââ SHA256 ååž\n â\n âŒ\nPOST /api/login { password: sha256_hash }\n â\n âŒ\nå端 bcrypt.checkpw(sha256_hash, db_bcrypt_hash) âââ éªè¯ç»æ\n```\n\n---\n\n## ð åŸ
åäºé¡¹\n\n- [ ] å®ç° Token é»ååæºå¶ïŒäœ¿ Logout çæ£çæ\n- [ ] æ·»å ç
§ç EXIF ä¿¡æ¯èªåšæåïŒæææ¶éŽãGPS çïŒ\n- [ ] æ¯æç
§çå猩äžäŒ ïŒåå°ååšç©ºéŽ\n- [ ] æ·»å ç
§çå享åèœïŒçæäžŽæ¶éŸæ¥ïŒ\n\n---\n\n## ð License\n\nMIT License © 2026 Family Photo Manager"}, "85ce0ce91ead6f3f61c551e62487a7300377a43b": {"t": 1785771642.7517693, "n": 11497, "h": "53b32a548ab312ae", "c": "<div align=\"center\">\n\n# ð· å®¶åºçžå Family Photo Manager\n\n**è®°åœæ¯äžäžªæž©éŠšç¬éŽïŒè®©å®¶äººä¹éŽçè·çŠ»æŽè¿äžç¹ ð**\n\n[](https://www.python.org/)\n[](https://flask.palletsprojects.com/)\n[](https://react.dev/)\n[](https://ant.design/)\n[](https://www.mysql.com/)\n\n</div>\n\n---\n\n## ð 项ç®ç®ä»\n\nå®¶åºçžåæ¯äžäžªäžäžºå®¶åºè®Ÿè®¡çç§å¯ç
§ç管çç³»ç»ãæ¯æå€å®¶åºæåå
±äº«çžåãç
§çäžäŒ /æç
§ãæ¶è管çïŒå¹¶å
眮 AI æºèœå©ææäŸæž©éŠšäºåšãæææ°æ®ååšåšæ¬å°ïŒä¿æ€å®¶åºéç§ã\n\n### âš æ žå¿åèœ\n\n| åèœæš¡å | æè¿° |\n|---------|------|\n| ð **çžå管ç** | å建ãéåœåãå é€çžåïŒèªå®ä¹å°é¢ |\n| ð€ **ç
§çäžäŒ ** | æ¯ææ¹éäžäŒ ïŒè®°åœæææ¶éŽãåœå±æåã倿³š |\n| ð· **æç
§åèœ** | è°çšæåå€ŽçŽæ¥æç
§å¹¶äžäŒ ïŒé HTTPS ç¯å¢ïŒ |\n| ð **ç
§çæçŽ¢** | æåç§°ãåœå±äººãäžäŒ è
ãæ¥æèåŽçé |\n| ð **æ¶è管ç** | å建æ¶è倹ïŒç
§çå å
¥æ¶è/ç§»åºïŒæ¯æå€äžªæ¶è倹 |\n| ðšâð©âð§ **å®¶åºæå** | 倿å管çïŒç
§çè®°åœåœå±äººåäžäŒ è
|\n| ð€ **AI 婿** | å
眮 AI 对è¯åèœïŒæ¯æ OpenAI / Ollama çå€ç§æäŸå |\n| ð **ç»åœéŽæ** | JWT Token 讀è¯ïŒSHA256 + bcrypt åéå¯ç å å¯ |\n\n---\n\n## ðïž ææ¯æ¶æ\n\n```\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (React 19) â\nâ Ant Design 6 · React Router 7 · Axios · Less â\nâ :3000 (craco) â\nââââââââââââââââââââââââ¬âââââââââââââââââââââââââââââââ\n â /api/* /uploads/*\n âŒ\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (Flask 2.3) â\nâ PyJWT · bcrypt · PyMySQL · Werkzeug · python-dotenvâ\nâ :5000 â\nâââââââââ¬ââââââââââââââââââââââââââ¬ââââââââââââââââââââ\n ⌠âŒ\nâââââââââââââââââ ââââââââââââââââââââ\nâ MySQL 5.7+ â â æ¬å°æä»¶ç³»ç» â\nâ family_photo â â uploads/photos â\nâ â â uploads/covers â\nâââââââââââââââââ ââââââââââââââââââââ\n```\n\n---\n\n## ð 项ç®ç»æ\n\n```\nPhotoManager/\nâââ family-photo-backend/ # ð§ å端 (Flask)\nâ âââ config/\nâ â âââ config.py # æ°æ®åº/JWT/å¯ç å å¯é
眮\nâ â âââ log_config.py # æ¥å¿é
çœ®ïŒæå€©èœ®èœ¬ïŒ\nâ âââ src/\nâ â âââ main.py # Flask åºçšå
¥å£\nâ â âââ auth.py # ç»åœ/ç»åº/Token éŽæ\nâ â âââ album.py # çžå CRUD + å°é¢äžäŒ \nâ â âââ photo.py # ç
§çäžäŒ /æçŽ¢/å é€\nâ â âââ favorite.py # æ¶è倹 & æ¶èç
§ç管ç\nâ â âââ member.py # å®¶åºæåæ¥è¯¢\nâ â âââ file.py # éææä»¶æå¡ïŒè·¯åŸéå鲿€ïŒ\nâ â âââ chat.py # AI å¯¹è¯æ¥å£\nâ â âââ ai_service.py # AI æå¡ïŒOpenAI/OllamaïŒ\nâ â âââ utils.py # æ°æ®åºè¿æ¥ & å·¥å
·åœæ°\nâ âââ sql/\nâ â âââ table_info.sql # æ°æ®åºå»ºè¡š SQL\nâ âââ uploads/ # äžäŒ æä»¶ååš\nâ â âââ photos/ # ç
§çæä»¶ïŒæ album_id åç®åœïŒ\nâ â âââ covers/ # çžåå°é¢\nâ âââ logs/ # æ¥å¿æä»¶ïŒèªåšçæïŒ\nâ âââ .env.example # ç¯å¢åéæš¡æ¿\nâ âââ requirements.txt # Python äŸèµ\nâ\nâââ family-photo-frontend/ # ðš å端 (React)\nâ âââ src/\nâ â âââ pages/\nâ â â âââ Login/ # ç»åœé¡µ\nâ â â âââ AlbumList/ # çžåå衚\nâ â â âââ AlbumDetail/ # çžå诊æ
\nâ â â â âââ components/\nâ â â â â âââ AlbumPhotoList/ # ç
§çå衚ïŒå页ïŒ\nâ â â â â âââ UploadArare/ # ç
§çäžäŒ åº\nâ â â â â âââ PhotoCapture/ # æå倎æç
§\nâ â â â âââ popwin/\nâ â â â âââ FilterPopwin/ # æçŽ¢çé匹çª\nâ â â â âââ FavoriteConfirmModal/ # æ¶è确讀匹çª\nâ â â âââ CreateAlbum/ # å建çžå\nâ â â âââ FavoriteManager/ # æ¶è管ç\nâ â âââ components/\nâ â â âââ PrivateRoute.js # è·¯ç±å®å«\nâ â â âââ HeaderComponent.js # é¡¶éšå¯Œèª\nâ â â âââ AiChat/ # AI å¯¹è¯æ¬æµ®çª\nâ â â âââ ButtonWrapper/ # æé®å°è£
\nâ â âââ contexts/\nâ â â âââ MemberContext.js # åœåç»åœæåäžäžæ\nâ â âââ utils/\nâ â â âââ request.js # Axios å°è£
ïŒToken æŠæªåšïŒ\nâ â â âââ dateUtil.js # æ¥ææ ŒåŒåå·¥å
·\nâ â âââ App.js # è·¯ç±é
眮å
¥å£\nâ âââ craco.config.js # Craco é
眮ïŒLess/代çïŒ\nâ âââ package.json\nâ\nâââ README.md\n```\n\n---\n\n## ð å¿«éåŒå§\n\n### ç¯å¢èŠæ±\n\n| äŸèµ | çæ¬èŠæ± |\n|------|---------|\n| Python | 3.8+ |\n| Node.js | 16+ |\n| MySQL | 5.7+ |\n| npm / yarn | ææ°çš³å®ç |\n\n### 1ïžâ£ å
é项ç®\n\n```bash\ngit clone <your-repo-url>\ncd PhotoManager\n```\n\n### 2ïžâ£ åå§åæ°æ®åº\n\n```bash\n# ç»åœ MySQL åå»ºæ°æ®åº\nmysql -u root -p\n\nCREATE DATABASE family_photo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nUSE family_photo;\n\n# 富å
¥å»ºè¡š SQLïŒæ°å»ºæ°æ®åºæ¶äœ¿çšïŒ\nsource family-photo-backend/sql/table_info.sql;\n\n# ïŒå¯éïŒæå
¥æµè¯å®¶åºæå\nINSERT INTO family_member (name, relation, username, password, email) VALUES\n('çžçž', 'father', 'father', 'æ€å€å¡«SHA256ååž', 'father@example.com'),\n('åŠåŠ', 'mother', 'mother', 'æ€å€å¡«SHA256ååž', 'mother@example.com');\n```\n\n> ð¡ å¯ç ååšæµçšïŒ**å端 SHA256** â **å端 bcrypt å å¯** â åå
¥æ°æ®åºã \n> åå§å¯ç å¯å
çš `config/config.py` äžç `encrypt_password()` åœæ°çæååžã\n\n### 3ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-backend\n\n# å建èæç¯å¢ïŒæšèïŒ\npython -m venv venv\n# Windows:\nvenv\\Scripts\\activate\n# macOS/Linux:\nsource venv/bin/activate\n\n# å®è£
äŸèµ\npip install -r requirements.txt\n\n# é
眮ç¯å¢åéïŒå¿
é¡»ïŒïŒ\ncp .env.example .env\n# çŒèŸ .envïŒå¡«å
¥ä»¥äžå¿
èŠé
眮ïŒ\n# DB_PASSWORD â æ°æ®åºå¯ç \n# JWT_SECRET â JWT å¯é¥ïŒçš python -c \"import secrets; print(secrets.token_hex(32))\" çæïŒ\n# AI_API_KEY â AI æå¡å¯é¥ïŒå¯éïŒ\n\n# å¯åšæå¡\npython -m src.main\n# å端è¿è¡åš http://localhost:5000\n```\n\n### 4ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-frontend\n\n# å®è£
äŸèµ\nnpm install\n\n# å¯åšåŒåæå¡åš\nnpm start\n# å端è¿è¡åš http://localhost:3000\n# å·²é
眮代çïŒ/api/* å /uploads/* èªåšèœ¬åå°å端 5000 端å£\n```\n\n---\n\n## âïž ç¯å¢åéé
眮\n\nåš `family-photo-backend/.env` äžé
眮ïŒåè `.env.example`ïŒïŒ\n\n```bash\n# ââ æ°æ®åºé
眮ïŒå¿
å¡«ïŒïŒâââââââââââââââââââââ\nDB_HOST=localhost\nDB_USER=root\nDB_PASSWORD=äœ çæ°æ®åºå¯ç \nDB_NAME=family_photo\n\n# ââ JWT å¯é¥ïŒå¿
å¡«ïŒïŒââââââââââââââââââââââ\n# çææ¹åŒïŒpython -c \"import secrets; print(secrets.token_hex(32))\"\nJWT_SECRET=äœ çJWTå¯é¥\nJWT_EXPIRE_HOURS=24\n\n# ââ AI æå¡é
眮 ââââââââââââââââââââââââââââââ\n# æäŸåïŒopenaiïŒé»è®€ïŒ| ollamaïŒæ¬å°éšçœ²ïŒæ é API KeyïŒ\nAI_PROVIDER=openai\n\n# OpenAI / DeepSeek / éä¹åé®\nAI_API_KEY=sk-xxx\nAI_API_BASE=https://api.openai.com/v1\nAI_MODEL=gpt-3.5-turbo\n\n# ââ äœ¿çš Ollama æ¬å°éšçœ²æ¶ïŒæ¹äžºä»¥äžé
眮 âââââââ\n# AI_PROVIDER=ollama\n# AI_MODEL=llama3\n# AI_API_BASE=http://localhost:11434\n\n# ââ éçš âââââââââââââââââââââââââââââââââââââ\nAI_TIMEOUT=60\n\n# ââ Flask ç¯å¢ âââââââââââââââââââââââââââââââ\nFLASK_ENV=dev\n```\n\n> â ïž **å®å
šæç€ºïŒ** \n> - `DB_PASSWORD` å `JWT_SECRET` 䞺å¿
å¡«é¡¹ïŒæªé
çœ®æ¶æå¡å¯åšäŒæ¥é \n> - `.env` æä»¶å·²åš `.gitignore` äžïŒäžäŒè¢«æäº€å°ä»£ç ä»åº \n> - ç产ç¯å¢è¯·å¡å¿
䜿çšåŒºéæºå笊䞲äœäžº `JWT_SECRET`\n\n---\n\n## ðïž æ°æ®åºè®Ÿè®¡\n\n```\nfamily_member (å®¶åºæå)\n â\n âââ< album (çžå)\n â â\n â âââ< photo (ç
§ç)\n â â\n â âââ< favorite_photo (æ¶èè®°åœ) >ââ favorite_folder (æ¶è倹)\n â\n âââ< ai_chat_message (AI è倩记åœ)\n```\n\n| 衚å | 诎æ | å
³é®å段 |\n|------|------|---------|\n| `family_member` | å®¶åºæå / ç»åœèŽŠå· | `username`(å¯äž), `password`(bcrypt), **`is_admin`**(æéæ è¯) |\n| `album` | çžå | `album_name`, `cover_path`, **`creator_id`**(å建è
), `last_upload_user_id` |\n| `photo` | ç
§ç | `file_path`, `album_id`, `member_id`(åœå±), `operator_id`(äžäŒ è
) |\n| `favorite_folder` | æ¶è倹 | `folder_name`, `member_id`, `is_default` |\n| `favorite_photo` | æ¶èè®°åœ | `folder_id`, `photo_id`, `member_id`ïŒå¯äžçºŠæé²éå€ïŒ |\n| `ai_chat_message` | AI èå€©è®°åœ | `member_id`, `role`(user/assistant), `content` |\n\n> å ç²åæ®µäžºæ¬æ¬¡æ°å¢ã宿Žå»ºè¡š SQL è§ `family-photo-backend/sql/table_info.sql`\n\n---\n\n## ð¡ API æ¥å£\n\n### 讀è¯\n\n| æ¹æ³ | è·¯åŸ | 诎æ |\n|------|------|------|\n| POST | `/api/login` | ç»åœïŒè¿å JWT TokenïŒ |\n| POST | `/api/logout` | ç»åº |\n\n### çžå\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/albums` | â
| è·åææçžåå衚 |\n| POST | `/api/album/create` | â
| å建çžåïŒå¯äžäŒ å°é¢ïŒ |\n| POST | `/api/album/rename` | â
| ä¿®æ¹çžååç§° |\n| POST | `/api/album/delete` | â
| å é€çžåïŒçº§èå é€ç
§çïŒ |\n| POST | `/api/album/cover/upload` | â
| äžäŒ /æŽæ¢çžåå°é¢ |\n| GET | `/api/photos/album/:id` | â
| è·åçžåäžç
§çïŒåé¡µïŒ |\n\n### ç
§ç\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| POST | `/api/photos/upload` | â
| äžäŒ ç
§ç |\n| POST | `/api/photos/delete` | â
| å é€ç
§ç |\n| GET | `/api/photos/search` | â
| æçŽ¢ç
§çïŒæ¯æå€æ¡ä»¶çéïŒ |\n\n### æ¶è\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/favorite/folders` | â
| è·åæ¶è倹å衚 |\n| POST | `/api/favorite/folders` | â
| å建æ¶è倹 |\n| PUT | `/api/favorite/folders/:id` | â
| ä¿®æ¹æ¶è倹åç§° |\n| DELETE | `/api/favorite/folders/:id` | â
| å 逿¶è倹 |\n| POST | `/api/favorite/photos` | â
| ç
§çå å
¥æ¶è |\n| DELETE | `/api/favorite/photos` | â
| ç
§çç§»åºæ¶è |\n| GET | `/api/favorite/photos/:folder_id` | â
| è·åæ¶è倹å
ç
§ç |\n\n### æå & AI\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/current-member` | â
| è·ååœåç»åœæåä¿¡æ¯ |\n| GET | `/api/members` | â
| è·åææå®¶åºæå |\n| GET | `/api/chat/history` | â
| è·åè倩记åœïŒé»è®€è¿ 3 䞪æïŒ |\n| POST | `/api/chat/send` | â
| åéæ¶æ¯ & è·å AI åå€ |\n| GET | `/api/chat/models` | â
| æ¥è¯¢ AI æäŸå & å¯çšæš¡å |\n| POST | `/api/chat/clear` | â
| æž
空èå€©è®°åœ |\n\n### éææä»¶\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/uploads/photos/:filename` | â
| è·åç
§çæä»¶ |\n| GET | `/uploads/covers/:filename` | â
| è·åå°é¢æä»¶ |\n\n---\n\n## ð å®å
šæºå¶\n\n| å±é¢ | æªæœ |\n|------|------|\n| **讀è¯** | JWT TokenïŒHS256ïŒ24h è¿æïŒïŒ`@login_required` è£
饰åšä¿æ€æææ¥å£ |\n| **å¯ç ** | å端 SHA256 + å端 bcryptïŒsalt rounds=12ïŒåéå å¯ |\n| **æéé犻** | `is_admin` åæ®µåºå管çå/æ®éçšæ·ïŒæ®éçšæ·åªèœæäœèªå·±å建ççžååç
§ç |\n| **æä»¶äžäŒ ** | æ©å±åçœååæ ¡éªã`secure_filename` è¿æ»€ãå¯äžæä»¶åé²èŠç |\n| **è·¯åŸå®å
š** | äžäŒ ç®åœéå¶ + `_safe_path()` ç»å¯¹è·¯åŸæ ¡éªïŒé²æ¢è·¯åŸéå |\n| **æ°æ®æ ¡éª** | `album_id` çåæ°åŒºå¶æ£æŽæ°æ ¡éªïŒååšæ§æ ¡éªé²æ¢å¯¹äžååšèµæºæäœ |\n| **SQL 泚å
¥** | å
šéšäœ¿çš PyMySQL åæ°åæ¥è¯¢ (`%s`) |\n| **CORS** | Flask-CORS é
眮 `supports_credentials=True` |\n\n---\n\n## ð¥ æéäœç³»\n\n### è§è²è¯Žæ\n\n| è§è² | `is_admin` åŒ | æéèåŽ |\n|------|--------------|---------|\n| **管çå** | `1` | æ¥çææçžåïŒæäœææèµæº |\n| **æ®éæå** | `0`ïŒé»è®€ïŒ | åªèœæ¥ç/æäœ**èªå·±å建ç**çžååç
§ç |\n\n### æéé
眮\n\nåšæ°æ®åºäžæåšè®Ÿçœ®ïŒ\n\n```sql\n-- å°æçšæ·è®Ÿäžºç®¡çå\nUPDATE family_member SET is_admin = 1 WHERE username = 'father';\n\n-- åæ¶ç®¡çåæé\nUPDATE family_member SET is_admin = 0 WHERE username = 'father';\n\n-- æ¥çæææåçæé\nSELECT id, name, username, is_admin FROM family_member;\n```\n\n### æéèŠçèåŽ\n\n| æäœ | 管çå | æ®éæå |\n|------|--------|---------|\n| æ¥ççžåå衚 | ææçžå | ä»
èªå·±å建ç |\n| æ¥ççžåç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| å建çžå | â
| â
ïŒèªåšæäžºå建è
ïŒ |\n| éåœå/å é€çžå | ä»»æçžå | ä»
èªå·±å建ç |\n| æŽæ¢çžåå°é¢ | ä»»æçžå | ä»
èªå·±å建ç |\n| äžäŒ ç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| å é€ç
§ç | ä»»æç
§ç | ä»
èªå·±çžåäžçç
§ç |\n| æçŽ¢ç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| æ¶è/åæ¶æ¶è | â
| â
ïŒæ¶è䞺䞪人è¡äžºïŒæ éæéïŒ |\n\n---\n\n## ð€ AI 婿\n\næ¯æäžç§ AI æäŸåïŒéè¿ç¯å¢åé `AI_PROVIDER` 忢ïŒ\n\n| æäŸå | é
眮æ¹åŒ | ç¹ç¹ |\n|--------|---------|------|\n| **OpenAI** | é
眮 `AI_API_KEY` + `AI_API_BASE` | æ¯æ OpenAI / DeepSeek / éä¹åé®çå
Œå®¹æ¥å£ |\n| **Ollama** | `AI_PROVIDER=ollama` | æ¬å°éšçœ²ïŒæ é API KeyïŒå®å
šçŠ»çº¿ |\n| **æš¡ææš¡åŒ** | äžé
眮 `AI_API_KEY` | èªåšäœ¿çšé¢è®Ÿåå€ïŒæ¹äŸ¿äœéªåèœ |\n\n---\n\n## ðž åèœé¢è§\n\n| é¡µé¢ | åèœ |\n|------|------|\n| ð ç»åœé¡µ | æž©éŠšé£æ Œç»åœïŒSHA256 å¯ç å å¯äŒ èŸ |\n| ð çžåå衚 | å±ç€ºææçžåïŒæŸç€ºå°é¢ãæåäžäŒ äººåæ¶éŽ |\n| ðŒïž çžå诊æ
| ç
§ççœæ Œå±ç€ºãå页å 蜜ãæçŽ¢çé |\n| ð€ äžäŒ åºå | å¡«ååœå±æåãæææ¶éŽã倿³šåæ¹éäžäŒ |\n| ð· æç
§äžäŒ | è°çšæå倎æç
§ â é¢è§ â éæ©åœå±äºº â äžäŒ |\n| ð æ¶è管ç | æ¶è倹å衚 + æ¶èç
§ççœæ ŒïŒæ¯æå¢å æ¹ |\n| ð€ AI 婿 | å
šå±æ¬æµ®å¯¹è¯çªïŒèå€©è®°åœæèŽŠå·é犻 |\n\n---\n\n## ð ïž åŒå诎æ\n\n### å端代çé
眮ïŒ`craco.config.js`ïŒ\n\n```javascript\n// åŒåç¯å¢äžèªåšä»£çå°å端\n'/api' â http://localhost:5000/api\n'/uploads' â http://localhost:5000/uploads\n```\n\n### æ¥å¿ç³»ç»\n\n- æ¥å¿ç®åœïŒ`family-photo-backend/logs/`\n- `info.log`ïŒåžžè§æäœæ¥å¿ïŒä¿ç 30 倩ïŒ\n- `error.log`ïŒé误æ¥å¿ïŒä¿ç 90 倩ïŒ\n- æå€©èªåšèœ®èœ¬ïŒWindows äžå®å
šå€çæä»¶éå®\n\n### å¯ç å 坿µçš\n\n```\nçšæ·èŸå
¥ææå¯ç \n â\n âŒ\nå端 CryptoJS.SHA256(password) âââ SHA256 ååž\n â\n âŒ\nPOST /api/login { password: sha256_hash }\n â\n âŒ\nå端 bcrypt.checkpw(sha256_hash, db_bcrypt_hash) âââ éªè¯ç»æ\n```\n\n---\n\n## ð åŸ
åäºé¡¹\n\n- [ ] å®ç° Token é»ååæºå¶ïŒäœ¿ Logout çæ£çæ\n- [ ] æ·»å ç
§ç EXIF ä¿¡æ¯èªåšæåïŒæææ¶éŽãGPS çïŒ\n- [ ] æ¯æç
§çå猩äžäŒ ïŒåå°ååšç©ºéŽ\n- [ ] æ·»å ç
§çå享åèœïŒçæäžŽæ¶éŸæ¥ïŒ\n\n---\n\n## ð License\n\nMIT License © 2026 Family Photo Manager"}, "da8bc81128da1c501ef5b7f9432e9480b2020993": {"t": 1785771645.0000422, "n": 11146, "h": "2eacf59d05c05175", "c": "<div align=\"center\">\n\n# ð· å®¶åºçžå Family Photo Manager\n\n**è®°åœæ¯äžäžªæž©éŠšç¬éŽïŒè®©å®¶äººä¹éŽçè·çŠ»æŽè¿äžç¹ ð**\n\n[](https://www.python.org/)\n[](https://flask.palletsprojects.com/)\n[](https://react.dev/)\n[](https://ant.design/)\n[](https://www.mysql.com/)\n\n</div>\n\n---\n\n## ð 项ç®ç®ä»\n\nå®¶åºçžåæ¯äžäžªäžäžºå®¶åºè®Ÿè®¡çç§å¯ç
§ç管çç³»ç»ãæ¯æå€å®¶åºæåå
±äº«çžåãç
§çäžäŒ /æç
§ãæ¶è管çïŒå¹¶å
眮 AI æºèœå©ææäŸæž©éŠšäºåšãæææ°æ®ååšåšæ¬å°ïŒä¿æ€å®¶åºéç§ã\n\n### âš æ žå¿åèœ\n\n| åèœæš¡å | æè¿° |\n|---------|------|\n| ð **çžå管ç** | å建ãéåœåãå é€çžåïŒèªå®ä¹å°é¢ |\n| ð€ **ç
§çäžäŒ ** | æ¯ææ¹éäžäŒ ïŒè®°åœæææ¶éŽãåœå±æåã倿³š |\n| ð· **æç
§åèœ** | è°çšæåå€ŽçŽæ¥æç
§å¹¶äžäŒ ïŒé HTTPS ç¯å¢ïŒ |\n| ð **ç
§çæçŽ¢** | æåç§°ãåœå±äººãäžäŒ è
ãæ¥æèåŽçé |\n| ð **æ¶è管ç** | å建æ¶è倹ïŒç
§çå å
¥æ¶è/ç§»åºïŒæ¯æå€äžªæ¶è倹 |\n| ðšâð©âð§ **å®¶åºæå** | 倿å管çïŒç
§çè®°åœåœå±äººåäžäŒ è
|\n| ð€ **AI 婿** | å
眮 AI 对è¯åèœïŒæ¯æ OpenAI / Ollama çå€ç§æäŸå |\n| ð **ç»åœéŽæ** | JWT Token 讀è¯ïŒSHA256 + bcrypt åéå¯ç å å¯ |\n\n---\n\n## ðïž ææ¯æ¶æ\n\n```\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (React 19) â\nâ Ant Design 6 · React Router 7 · Axios · Less â\nâ :3000 (craco) â\nââââââââââââââââââââââââ¬âââââââââââââââââââââââââââââââ\n â /api/* /uploads/*\n âŒ\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (Flask 2.3) â\nâ PyJWT · bcrypt · PyMySQL · Werkzeug · python-dotenvâ\nâ :5000 â\nâââââââââ¬ââââââââââââââââââââââââââ¬ââââââââââââââââââââ\n ⌠âŒ\nâââââââââââââââââ ââââââââââââââââââââ\nâ MySQL 5.7+ â â æ¬å°æä»¶ç³»ç» â\nâ family_photo â â uploads/photos â\nâ â â uploads/covers â\nâââââââââââââââââ ââââââââââââââââââââ\n```\n\n---\n\n## ð 项ç®ç»æ\n\n```\nPhotoManager/\nâââ family-photo-backend/ # ð§ å端 (Flask)\nâ âââ config/\nâ â âââ config.py # æ°æ®åº/JWT/å¯ç å å¯é
眮\nâ â âââ log_config.py # æ¥å¿é
çœ®ïŒæå€©èœ®èœ¬ïŒ\nâ âââ src/\nâ â âââ main.py # Flask åºçšå
¥å£\nâ â âââ auth.py # ç»åœ/ç»åº/Token éŽæ\nâ â âââ album.py # çžå CRUD + å°é¢äžäŒ \nâ â âââ photo.py # ç
§çäžäŒ /æçŽ¢/å é€\nâ â âââ favorite.py # æ¶è倹 & æ¶èç
§ç管ç\nâ â âââ member.py # å®¶åºæåæ¥è¯¢\nâ â âââ file.py # éææä»¶æå¡ïŒè·¯åŸéå鲿€ïŒ\nâ â âââ chat.py # AI å¯¹è¯æ¥å£\nâ â âââ ai_service.py # AI æå¡ïŒOpenAI/OllamaïŒ\nâ â âââ utils.py # æ°æ®åºè¿æ¥ & å·¥å
·åœæ°\nâ âââ sql/\nâ â âââ table_info.sql # æ°æ®åºå»ºè¡š SQL\nâ âââ uploads/ # äžäŒ æä»¶ååš\nâ â âââ photos/ # ç
§çæä»¶ïŒæ album_id åç®åœïŒ\nâ â âââ covers/ # çžåå°é¢\nâ âââ logs/ # æ¥å¿æä»¶ïŒèªåšçæïŒ\nâ âââ .env.example # ç¯å¢åéæš¡æ¿\nâ âââ requirements.txt # Python äŸèµ\nâ\nâââ family-photo-frontend/ # ðš å端 (React)\nâ âââ src/\nâ â âââ pages/\nâ â â âââ Login/ # ç»åœé¡µ\nâ â â âââ AlbumList/ # çžåå衚\nâ â â âââ AlbumDetail/ # çžå诊æ
\nâ â â â âââ components/\nâ â â â â âââ AlbumPhotoList/ # ç
§çå衚ïŒå页ïŒ\nâ â â â â âââ UploadArare/ # ç
§çäžäŒ åº\nâ â â â â âââ PhotoCapture/ # æå倎æç
§\nâ â â â âââ popwin/\nâ â â â âââ FilterPopwin/ # æçŽ¢çé匹çª\nâ â â â âââ FavoriteConfirmModal/ # æ¶è确讀匹çª\nâ â â âââ CreateAlbum/ # å建çžå\nâ â â âââ FavoriteManager/ # æ¶è管ç\nâ â âââ components/\nâ â â âââ PrivateRoute.js # è·¯ç±å®å«\nâ â â âââ HeaderComponent.js # é¡¶éšå¯Œèª\nâ â â âââ AiChat/ # AI å¯¹è¯æ¬æµ®çª\nâ â â âââ ButtonWrapper/ # æé®å°è£
\nâ â âââ contexts/\nâ â â âââ MemberContext.js # åœåç»åœæåäžäžæ\nâ â âââ utils/\nâ â â âââ request.js # Axios å°è£
ïŒToken æŠæªåšïŒ\nâ â â âââ dateUtil.js # æ¥ææ ŒåŒåå·¥å
·\nâ â âââ App.js # è·¯ç±é
眮å
¥å£\nâ âââ craco.config.js # Craco é
眮ïŒLess/代çïŒ\nâ âââ package.json\nâ\nâââ README.md\n```\n\n---\n\n## ð å¿«éåŒå§\n\n### ç¯å¢èŠæ±\n\n| äŸèµ | çæ¬èŠæ± |\n|------|---------|\n| Python | 3.8+ |\n| Node.js | 16+ |\n| MySQL | 5.7+ |\n| npm / yarn | ææ°çš³å®ç |\n\n### 1ïžâ£ å
é项ç®\n\n```bash\ngit clone <your-repo-url>\ncd PhotoManager\n```\n\n### 2ïžâ£ åå§åæ°æ®åº\n\n```bash\n# ç»åœ MySQL åå»ºæ°æ®åº\nmysql -u root -p\n\nCREATE DATABASE family_photo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nUSE family_photo;\n\n# 富å
¥å»ºè¡š SQLïŒæ°å»ºæ°æ®åºæ¶äœ¿çšïŒ\nsource family-photo-backend/sql/table_info.sql;\n\n# ïŒå¯éïŒæå
¥æµè¯å®¶åºæå\nINSERT INTO family_member (name, relation, username, password, email) VALUES\n('çžçž', 'father', 'father', 'æ€å€å¡«SHA256ååž', 'father@example.com'),\n('åŠåŠ', 'mother', 'mother', 'æ€å€å¡«SHA256ååž', 'mother@example.com');\n```\n\n> ð¡ å¯ç ååšæµçšïŒ**å端 SHA256** â **å端 bcrypt å å¯** â åå
¥æ°æ®åºã \n> åå§å¯ç å¯å
çš `config/config.py` äžç `encrypt_password()` åœæ°çæååžã\n\n### 3ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-backend\n\n# å建èæç¯å¢ïŒæšèïŒ\npython -m venv venv\n# Windows:\nvenv\\Scripts\\activate\n# macOS/Linux:\nsource venv/bin/activate\n\n# å®è£
äŸèµ\npip install -r requirements.txt\n\n# é
眮ç¯å¢åé\ncp .env.example .env\n# çŒèŸ .env å¡«å
¥æ°æ®åºå¯ç å AI é
眮\n\n# ä¿®æ¹ config/config.py äžçæ°æ®åºè¿æ¥ä¿¡æ¯\n# DB_CONFIG = { 'host': 'localhost', 'user': 'root', 'password': 'äœ çå¯ç ', ... }\n\n# å¯åšæå¡\npython -m src.main\n# å端è¿è¡åš http://localhost:5000\n```\n\n### 4ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-frontend\n\n# å®è£
äŸèµ\nnpm install\n\n# å¯åšåŒåæå¡åš\nnpm start\n# å端è¿è¡åš http://localhost:3000\n# å·²é
眮代çïŒ/api/* å /uploads/* èªåšèœ¬åå°å端 5000 端å£\n```\n\n---\n\n## âïž ç¯å¢åéé
眮\n\nåš `family-photo-backend/.env` äžé
眮ïŒ\n\n```bash\n# ââ AI æå¡é
眮 ââââââââââââââââââââââââââââââ\n# æäŸåïŒopenaiïŒé»è®€ïŒ| ollamaïŒæ¬å°éšçœ²ïŒæ é API KeyïŒ\nAI_PROVIDER=openai\n\n# OpenAI / DeepSeek / éä¹åé®\nAI_API_KEY=sk-xxx\nAI_API_BASE=https://api.openai.com/v1\nAI_MODEL=gpt-3.5-turbo\n\n# ââ äœ¿çš Ollama æ¬å°éšçœ²æ¶ïŒæ¹äžºä»¥äžé
眮 âââââââ\n# AI_PROVIDER=ollama\n# AI_MODEL=llama3\n# AI_API_BASE=http://localhost:11434\n\n# ââ éçš âââââââââââââââââââââââââââââââââââââ\nAI_TIMEOUT=60\n\n# ââ Flask ç¯å¢ âââââââââââââââââââââââââââââââ\nFLASK_ENV=dev\n```\n\n---\n\n## ðïž æ°æ®åºè®Ÿè®¡\n\n```\nfamily_member (å®¶åºæå)\n â\n âââ< album (çžå)\n â â\n â âââ< photo (ç
§ç)\n â â\n â âââ< favorite_photo (æ¶èè®°åœ) >ââ favorite_folder (æ¶è倹)\n â\n âââ< ai_chat_message (AI è倩记åœ)\n```\n\n| 衚å | 诎æ | å
³é®å段 |\n|------|------|---------|\n| `family_member` | å®¶åºæå / ç»åœèŽŠå· | `username`(å¯äž), `password`(bcrypt), **`is_admin`**(æéæ è¯) |\n| `album` | çžå | `album_name`, `cover_path`, **`creator_id`**(å建è
), `last_upload_user_id` |\n| `photo` | ç
§ç | `file_path`, `album_id`, `member_id`(åœå±), `operator_id`(äžäŒ è
) |\n| `favorite_folder` | æ¶è倹 | `folder_name`, `member_id`, `is_default` |\n| `favorite_photo` | æ¶èè®°åœ | `folder_id`, `photo_id`, `member_id`ïŒå¯äžçºŠæé²éå€ïŒ |\n| `ai_chat_message` | AI èå€©è®°åœ | `member_id`, `role`(user/assistant), `content` |\n\n> å ç²åæ®µäžºæ¬æ¬¡æ°å¢ã宿Žå»ºè¡š SQL è§ `family-photo-backend/sql/table_info.sql`\n\n---\n\n## ð¡ API æ¥å£\n\n### 讀è¯\n\n| æ¹æ³ | è·¯åŸ | 诎æ |\n|------|------|------|\n| POST | `/api/login` | ç»åœïŒè¿å JWT TokenïŒ |\n| POST | `/api/logout` | ç»åº |\n\n### çžå\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/albums` | â
| è·åææçžåå衚 |\n| POST | `/api/album/create` | â
| å建çžåïŒå¯äžäŒ å°é¢ïŒ |\n| POST | `/api/album/rename` | â
| ä¿®æ¹çžååç§° |\n| POST | `/api/album/delete` | â
| å é€çžåïŒçº§èå é€ç
§çïŒ |\n| POST | `/api/album/cover/upload` | â
| äžäŒ /æŽæ¢çžåå°é¢ |\n| GET | `/api/photos/album/:id` | â
| è·åçžåäžç
§çïŒåé¡µïŒ |\n\n### ç
§ç\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| POST | `/api/photos/upload` | â
| äžäŒ ç
§ç |\n| POST | `/api/photos/delete` | â
| å é€ç
§ç |\n| GET | `/api/photos/search` | â
| æçŽ¢ç
§çïŒæ¯æå€æ¡ä»¶çéïŒ |\n\n### æ¶è\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/favorite/folders` | â
| è·åæ¶è倹å衚 |\n| POST | `/api/favorite/folders` | â
| å建æ¶è倹 |\n| PUT | `/api/favorite/folders/:id` | â
| ä¿®æ¹æ¶è倹åç§° |\n| DELETE | `/api/favorite/folders/:id` | â
| å 逿¶è倹 |\n| POST | `/api/favorite/photos` | â
| ç
§çå å
¥æ¶è |\n| DELETE | `/api/favorite/photos` | â
| ç
§çç§»åºæ¶è |\n| GET | `/api/favorite/photos/:folder_id` | â
| è·åæ¶è倹å
ç
§ç |\n\n### æå & AI\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/current-member` | â
| è·ååœåç»åœæåä¿¡æ¯ |\n| GET | `/api/members` | â
| è·åææå®¶åºæå |\n| GET | `/api/chat/history` | â
| è·åè倩记åœïŒé»è®€è¿ 3 䞪æïŒ |\n| POST | `/api/chat/send` | â
| åéæ¶æ¯ & è·å AI åå€ |\n| GET | `/api/chat/models` | â
| æ¥è¯¢ AI æäŸå & å¯çšæš¡å |\n| POST | `/api/chat/clear` | â
| æž
空èå€©è®°åœ |\n\n### éææä»¶\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/uploads/photos/:filename` | â
| è·åç
§çæä»¶ |\n| GET | `/uploads/covers/:filename` | â
| è·åå°é¢æä»¶ |\n\n---\n\n## ð å®å
šæºå¶\n\n| å±é¢ | æªæœ |\n|------|------|\n| **讀è¯** | JWT TokenïŒHS256ïŒ24h è¿æïŒïŒ`@login_required` è£
饰åšä¿æ€æææ¥å£ |\n| **å¯ç ** | å端 SHA256 + å端 bcryptïŒsalt rounds=12ïŒåéå å¯ |\n| **æéé犻** | `is_admin` åæ®µåºå管çå/æ®éçšæ·ïŒæ®éçšæ·åªèœæäœèªå·±å建ççžååç
§ç |\n| **æä»¶äžäŒ ** | æ©å±åçœååæ ¡éªã`secure_filename` è¿æ»€ãå¯äžæä»¶åé²èŠç |\n| **è·¯åŸå®å
š** | äžäŒ ç®åœéå¶ + `_safe_path()` ç»å¯¹è·¯åŸæ ¡éªïŒé²æ¢è·¯åŸéå |\n| **æ°æ®æ ¡éª** | `album_id` çåæ°åŒºå¶æ£æŽæ°æ ¡éªïŒååšæ§æ ¡éªé²æ¢å¯¹äžååšèµæºæäœ |\n| **SQL 泚å
¥** | å
šéšäœ¿çš PyMySQL åæ°åæ¥è¯¢ (`%s`) |\n| **CORS** | Flask-CORS é
眮 `supports_credentials=True` |\n\n---\n\n## ð¥ æéäœç³»\n\n### è§è²è¯Žæ\n\n| è§è² | `is_admin` åŒ | æéèåŽ |\n|------|--------------|---------|\n| **管çå** | `1` | æ¥çææçžåïŒæäœææèµæº |\n| **æ®éæå** | `0`ïŒé»è®€ïŒ | åªèœæ¥ç/æäœ**èªå·±å建ç**çžååç
§ç |\n\n### æéé
眮\n\nåšæ°æ®åºäžæåšè®Ÿçœ®ïŒ\n\n```sql\n-- å°æçšæ·è®Ÿäžºç®¡çå\nUPDATE family_member SET is_admin = 1 WHERE username = 'father';\n\n-- åæ¶ç®¡çåæé\nUPDATE family_member SET is_admin = 0 WHERE username = 'father';\n\n-- æ¥çæææåçæé\nSELECT id, name, username, is_admin FROM family_member;\n```\n\n### æéèŠçèåŽ\n\n| æäœ | 管çå | æ®éæå |\n|------|--------|---------|\n| æ¥ççžåå衚 | ææçžå | ä»
èªå·±å建ç |\n| æ¥ççžåç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| å建çžå | â
| â
ïŒèªåšæäžºå建è
ïŒ |\n| éåœå/å é€çžå | ä»»æçžå | ä»
èªå·±å建ç |\n| æŽæ¢çžåå°é¢ | ä»»æçžå | ä»
èªå·±å建ç |\n| äžäŒ ç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| å é€ç
§ç | ä»»æç
§ç | ä»
èªå·±çžåäžçç
§ç |\n| æçŽ¢ç
§ç | ä»»æçžå | ä»
èªå·±å建ççžå |\n| æ¶è/åæ¶æ¶è | â
| â
ïŒæ¶è䞺䞪人è¡äžºïŒæ éæéïŒ |\n\n---\n\n## ð€ AI 婿\n\næ¯æäžç§ AI æäŸåïŒéè¿ç¯å¢åé `AI_PROVIDER` 忢ïŒ\n\n| æäŸå | é
眮æ¹åŒ | ç¹ç¹ |\n|--------|---------|------|\n| **OpenAI** | é
眮 `AI_API_KEY` + `AI_API_BASE` | æ¯æ OpenAI / DeepSeek / éä¹åé®çå
Œå®¹æ¥å£ |\n| **Ollama** | `AI_PROVIDER=ollama` | æ¬å°éšçœ²ïŒæ é API KeyïŒå®å
šçŠ»çº¿ |\n| **æš¡ææš¡åŒ** | äžé
眮 `AI_API_KEY` | èªåšäœ¿çšé¢è®Ÿåå€ïŒæ¹äŸ¿äœéªåèœ |\n\n---\n\n## ðž åèœé¢è§\n\n| é¡µé¢ | åèœ |\n|------|------|\n| ð ç»åœé¡µ | æž©éŠšé£æ Œç»åœïŒSHA256 å¯ç å å¯äŒ èŸ |\n| ð çžåå衚 | å±ç€ºææçžåïŒæŸç€ºå°é¢ãæåäžäŒ äººåæ¶éŽ |\n| ðŒïž çžå诊æ
| ç
§ççœæ Œå±ç€ºãå页å 蜜ãæçŽ¢çé |\n| ð€ äžäŒ åºå | å¡«ååœå±æåãæææ¶éŽã倿³šåæ¹éäžäŒ |\n| ð· æç
§äžäŒ | è°çšæå倎æç
§ â é¢è§ â éæ©åœå±äºº â äžäŒ |\n| ð æ¶è管ç | æ¶è倹å衚 + æ¶èç
§ççœæ ŒïŒæ¯æå¢å æ¹ |\n| ð€ AI 婿 | å
šå±æ¬æµ®å¯¹è¯çªïŒèå€©è®°åœæèŽŠå·é犻 |\n\n---\n\n## ð ïž åŒå诎æ\n\n### å端代çé
眮ïŒ`craco.config.js`ïŒ\n\n```javascript\n// åŒåç¯å¢äžèªåšä»£çå°å端\n'/api' â http://localhost:5000/api\n'/uploads' â http://localhost:5000/uploads\n```\n\n### æ¥å¿ç³»ç»\n\n- æ¥å¿ç®åœïŒ`family-photo-backend/logs/`\n- `info.log`ïŒåžžè§æäœæ¥å¿ïŒä¿ç 30 倩ïŒ\n- `error.log`ïŒé误æ¥å¿ïŒä¿ç 90 倩ïŒ\n- æå€©èªåšèœ®èœ¬ïŒWindows äžå®å
šå€çæä»¶éå®\n\n### å¯ç å 坿µçš\n\n```\nçšæ·èŸå
¥ææå¯ç \n â\n âŒ\nå端 CryptoJS.SHA256(password) âââ SHA256 ååž\n â\n âŒ\nPOST /api/login { password: sha256_hash }\n â\n âŒ\nå端 bcrypt.checkpw(sha256_hash, db_bcrypt_hash) âââ éªè¯ç»æ\n```\n\n---\n\n## ð åŸ
åäºé¡¹\n\n- [ ] å®ç° Token é»ååæºå¶ïŒäœ¿ Logout çæ£çæ\n- [ ] æ·»å çžå/ç
§ççè®¿é®æéæ§å¶ïŒææåé犻ïŒ\n- [ ] å°æ°æ®åºå¯ç å JWT å¯é¥è¿ç§»å°ç¯å¢åé\n- [ ] å端 Token æ¹çš HttpOnly Cookie äŒ éïŒé¿å
URL æŽé²\n- [ ] æ·»å ç
§ç EXIF ä¿¡æ¯èªåšæåïŒæææ¶éŽãGPS çïŒ\n- [ ] æ¯æç
§çå猩äžäŒ ïŒåå°ååšç©ºéŽ\n- [ ] æ·»å ç
§çå享åèœïŒçæäžŽæ¶éŸæ¥ïŒ\n\n---\n\n## ð License\n\nMIT License © 2026 Family Photo Manager"}, "0aa5d81b56e4d304b430f28bb0de91f9d83db520": {"t": 1785771648.1729136, "n": 10278, "h": "c23fc6b52522f050", "c": "<div align=\"center\">\n\n# ð· å®¶åºçžå Family Photo Manager\n\n**è®°åœæ¯äžäžªæž©éŠšç¬éŽïŒè®©å®¶äººä¹éŽçè·çŠ»æŽè¿äžç¹ ð**\n\n[](https://www.python.org/)\n[](https://flask.palletsprojects.com/)\n[](https://react.dev/)\n[](https://ant.design/)\n[](https://www.mysql.com/)\n\n</div>\n\n---\n\n## ð 项ç®ç®ä»\n\nå®¶åºçžåæ¯äžäžªäžäžºå®¶åºè®Ÿè®¡çç§å¯ç
§ç管çç³»ç»ãæ¯æå€å®¶åºæåå
±äº«çžåãç
§çäžäŒ /æç
§ãæ¶è管çïŒå¹¶å
眮 AI æºèœå©ææäŸæž©éŠšäºåšãæææ°æ®ååšåšæ¬å°ïŒä¿æ€å®¶åºéç§ã\n\n### âš æ žå¿åèœ\n\n| åèœæš¡å | æè¿° |\n|---------|------|\n| ð **çžå管ç** | å建ãéåœåãå é€çžåïŒèªå®ä¹å°é¢ |\n| ð€ **ç
§çäžäŒ ** | æ¯ææ¹éäžäŒ ïŒè®°åœæææ¶éŽãåœå±æåã倿³š |\n| ð· **æç
§åèœ** | è°çšæåå€ŽçŽæ¥æç
§å¹¶äžäŒ ïŒé HTTPS ç¯å¢ïŒ |\n| ð **ç
§çæçŽ¢** | æåç§°ãåœå±äººãäžäŒ è
ãæ¥æèåŽçé |\n| ð **æ¶è管ç** | å建æ¶è倹ïŒç
§çå å
¥æ¶è/ç§»åºïŒæ¯æå€äžªæ¶è倹 |\n| ðšâð©âð§ **å®¶åºæå** | 倿å管çïŒç
§çè®°åœåœå±äººåäžäŒ è
|\n| ð€ **AI 婿** | å
眮 AI 对è¯åèœïŒæ¯æ OpenAI / Ollama çå€ç§æäŸå |\n| ð **ç»åœéŽæ** | JWT Token 讀è¯ïŒSHA256 + bcrypt åéå¯ç å å¯ |\n\n---\n\n## ðïž ææ¯æ¶æ\n\n```\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (React 19) â\nâ Ant Design 6 · React Router 7 · Axios · Less â\nâ :3000 (craco) â\nââââââââââââââââââââââââ¬âââââââââââââââââââââââââââââââ\n â /api/* /uploads/*\n âŒ\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââ\nâ å端 (Flask 2.3) â\nâ PyJWT · bcrypt · PyMySQL · Werkzeug · python-dotenvâ\nâ :5000 â\nâââââââââ¬ââââââââââââââââââââââââââ¬ââââââââââââââââââââ\n ⌠âŒ\nâââââââââââââââââ ââââââââââââââââââââ\nâ MySQL 5.7+ â â æ¬å°æä»¶ç³»ç» â\nâ family_photo â â uploads/photos â\nâ â â uploads/covers â\nâââââââââââââââââ ââââââââââââââââââââ\n```\n\n---\n\n## ð 项ç®ç»æ\n\n```\nPhotoManager/\nâââ family-photo-backend/ # ð§ å端 (Flask)\nâ âââ config/\nâ â âââ config.py # æ°æ®åº/JWT/å¯ç å å¯é
眮\nâ â âââ log_config.py # æ¥å¿é
çœ®ïŒæå€©èœ®èœ¬ïŒ\nâ âââ src/\nâ â âââ main.py # Flask åºçšå
¥å£\nâ â âââ auth.py # ç»åœ/ç»åº/Token éŽæ\nâ â âââ album.py # çžå CRUD + å°é¢äžäŒ \nâ â âââ photo.py # ç
§çäžäŒ /æçŽ¢/å é€\nâ â âââ favorite.py # æ¶è倹 & æ¶èç
§ç管ç\nâ â âââ member.py # å®¶åºæåæ¥è¯¢\nâ â âââ file.py # éææä»¶æå¡ïŒè·¯åŸéå鲿€ïŒ\nâ â âââ chat.py # AI å¯¹è¯æ¥å£\nâ â âââ ai_service.py # AI æå¡ïŒOpenAI/OllamaïŒ\nâ â âââ utils.py # æ°æ®åºè¿æ¥ & å·¥å
·åœæ°\nâ âââ sql/\nâ â âââ table_info.sql # æ°æ®åºå»ºè¡š SQL\nâ âââ uploads/ # äžäŒ æä»¶ååš\nâ â âââ photos/ # ç
§çæä»¶ïŒæ album_id åç®åœïŒ\nâ â âââ covers/ # çžåå°é¢\nâ âââ logs/ # æ¥å¿æä»¶ïŒèªåšçæïŒ\nâ âââ .env.example # ç¯å¢åéæš¡æ¿\nâ âââ requirements.txt # Python äŸèµ\nâ\nâââ family-photo-frontend/ # ðš å端 (React)\nâ âââ src/\nâ â âââ pages/\nâ â â âââ Login/ # ç»åœé¡µ\nâ â â âââ AlbumList/ # çžåå衚\nâ â â âââ AlbumDetail/ # çžå诊æ
\nâ â â â âââ components/\nâ â â â â âââ AlbumPhotoList/ # ç
§çå衚ïŒå页ïŒ\nâ â â â â âââ UploadArare/ # ç
§çäžäŒ åº\nâ â â â â âââ PhotoCapture/ # æå倎æç
§\nâ â â â âââ popwin/\nâ â â â âââ FilterPopwin/ # æçŽ¢çé匹çª\nâ â â â âââ FavoriteConfirmModal/ # æ¶è确讀匹çª\nâ â â âââ CreateAlbum/ # å建çžå\nâ â â âââ FavoriteManager/ # æ¶è管ç\nâ â âââ components/\nâ â â âââ PrivateRoute.js # è·¯ç±å®å«\nâ â â âââ HeaderComponent.js # é¡¶éšå¯Œèª\nâ â â âââ AiChat/ # AI å¯¹è¯æ¬æµ®çª\nâ â â âââ ButtonWrapper/ # æé®å°è£
\nâ â âââ contexts/\nâ â â âââ MemberContext.js # åœåç»åœæåäžäžæ\nâ â âââ utils/\nâ â â âââ request.js # Axios å°è£
ïŒToken æŠæªåšïŒ\nâ â â âââ dateUtil.js # æ¥ææ ŒåŒåå·¥å
·\nâ â âââ App.js # è·¯ç±é
眮å
¥å£\nâ âââ craco.config.js # Craco é
眮ïŒLess/代çïŒ\nâ âââ package.json\nâ\nâââ README.md\n```\n\n---\n\n## ð å¿«éåŒå§\n\n### ç¯å¢èŠæ±\n\n| äŸèµ | çæ¬èŠæ± |\n|------|---------|\n| Python | 3.8+ |\n| Node.js | 16+ |\n| MySQL | 5.7+ |\n| npm / yarn | ææ°çš³å®ç |\n\n### 1ïžâ£ å
é项ç®\n\n```bash\ngit clone <your-repo-url>\ncd PhotoManager\n```\n\n### 2ïžâ£ åå§åæ°æ®åº\n\n```bash\n# ç»åœ MySQL åå»ºæ°æ®åº\nmysql -u root -p\n\nCREATE DATABASE family_photo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nUSE family_photo;\n\n# 富å
¥å»ºè¡š SQL\nsource family-photo-backend/sql/table_info.sql;\n\n# ïŒå¯éïŒæå
¥æµè¯å®¶åºæå\nINSERT INTO family_member (name, relation, username, password, email) VALUES\n('çžçž', 'father', 'father', 'æ€å€å¡«SHA256ååž', 'father@example.com'),\n('åŠåŠ', 'mother', 'mother', 'æ€å€å¡«SHA256ååž', 'mother@example.com');\n```\n\n> ð¡ å¯ç ååšæµçšïŒ**å端 SHA256** â **å端 bcrypt å å¯** â åå
¥æ°æ®åºã \n> åå§å¯ç å¯å
çš `config/config.py` äžç `encrypt_password()` åœæ°çæååžã\n\n### 3ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-backend\n\n# å建èæç¯å¢ïŒæšèïŒ\npython -m venv venv\n# Windows:\nvenv\\Scripts\\activate\n# macOS/Linux:\nsource venv/bin/activate\n\n# å®è£
äŸèµ\npip install -r requirements.txt\n\n# é
眮ç¯å¢åé\ncp .env.example .env\n# çŒèŸ .env å¡«å
¥æ°æ®åºå¯ç å AI é
眮\n\n# ä¿®æ¹ config/config.py äžçæ°æ®åºè¿æ¥ä¿¡æ¯\n# DB_CONFIG = { 'host': 'localhost', 'user': 'root', 'password': 'äœ çå¯ç ', ... }\n\n# å¯åšæå¡\npython -m src.main\n# å端è¿è¡åš http://localhost:5000\n```\n\n### 4ïžâ£ å¯åšå端\n\n```bash\ncd family-photo-frontend\n\n# å®è£
äŸèµ\nnpm install\n\n# å¯åšåŒåæå¡åš\nnpm start\n# å端è¿è¡åš http://localhost:3000\n# å·²é
眮代çïŒ/api/* å /uploads/* èªåšèœ¬åå°å端 5000 端å£\n```\n\n---\n\n## âïž ç¯å¢åéé
眮\n\nåš `family-photo-backend/.env` äžé
眮ïŒ\n\n```bash\n# ââ AI æå¡é
眮 ââââââââââââââââââââââââââââââ\n# æäŸåïŒopenaiïŒé»è®€ïŒ| ollamaïŒæ¬å°éšçœ²ïŒæ é API KeyïŒ\nAI_PROVIDER=openai\n\n# OpenAI / DeepSeek / éä¹åé®\nAI_API_KEY=sk-xxx\nAI_API_BASE=https://api.openai.com/v1\nAI_MODEL=gpt-3.5-turbo\n\n# ââ äœ¿çš Ollama æ¬å°éšçœ²æ¶ïŒæ¹äžºä»¥äžé
眮 âââââââ\n# AI_PROVIDER=ollama\n# AI_MODEL=llama3\n# AI_API_BASE=http://localhost:11434\n\n# ââ éçš âââââââââââââââââââââââââââââââââââââ\nAI_TIMEOUT=60\n\n# ââ Flask ç¯å¢ âââââââââââââââââââââââââââââââ\nFLASK_ENV=dev\n```\n\n---\n\n## ðïž æ°æ®åºè®Ÿè®¡\n\n```\nfamily_member (å®¶åºæå)\n â\n âââ< album (çžå)\n â â\n â âââ< photo (ç
§ç)\n â â\n â âââ< favorite_photo (æ¶èè®°åœ) >ââ favorite_folder (æ¶è倹)\n â\n âââ< ai_chat_message (AI è倩记åœ)\n```\n\n| 衚å | 诎æ | å
³é®å段 |\n|------|------|---------|\n| `family_member` | å®¶åºæå / ç»åœèŽŠå· | `username`(å¯äž), `password`(bcrypt) |\n| `album` | çžå | `album_name`, `cover_path`, `last_upload_user_id` |\n| `photo` | ç
§ç | `file_path`, `album_id`, `member_id`(åœå±), `operator_id`(äžäŒ è
) |\n| `favorite_folder` | æ¶è倹 | `folder_name`, `member_id`, `is_default` |\n| `favorite_photo` | æ¶èè®°åœ | `folder_id`, `photo_id`, `member_id`ïŒå¯äžçºŠæé²éå€ïŒ |\n| `ai_chat_message` | AI èå€©è®°åœ | `member_id`, `role`(user/assistant), `content` |\n\n> 宿Žå»ºè¡š SQL è§ `family-photo-backend/sql/table_info.sql`\n\n---\n\n## ð¡ API æ¥å£\n\n### 讀è¯\n\n| æ¹æ³ | è·¯åŸ | 诎æ |\n|------|------|------|\n| POST | `/api/login` | ç»åœïŒè¿å JWT TokenïŒ |\n| POST | `/api/logout` | ç»åº |\n\n### çžå\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/albums` | â
| è·åææçžåå衚 |\n| POST | `/api/album/create` | â
| å建çžåïŒå¯äžäŒ å°é¢ïŒ |\n| POST | `/api/album/rename` | â
| ä¿®æ¹çžååç§° |\n| POST | `/api/album/delete` | â
| å é€çžåïŒçº§èå é€ç
§çïŒ |\n| POST | `/api/album/cover/upload` | â
| äžäŒ /æŽæ¢çžåå°é¢ |\n| GET | `/api/photos/album/:id` | â
| è·åçžåäžç
§çïŒåé¡µïŒ |\n\n### ç
§ç\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| POST | `/api/photos/upload` | â
| äžäŒ ç
§ç |\n| POST | `/api/photos/delete` | â
| å é€ç
§ç |\n| GET | `/api/photos/search` | â
| æçŽ¢ç
§çïŒæ¯æå€æ¡ä»¶çéïŒ |\n\n### æ¶è\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/favorite/folders` | â
| è·åæ¶è倹å衚 |\n| POST | `/api/favorite/folders` | â
| å建æ¶è倹 |\n| PUT | `/api/favorite/folders/:id` | â
| ä¿®æ¹æ¶è倹åç§° |\n| DELETE | `/api/favorite/folders/:id` | â
| å 逿¶è倹 |\n| POST | `/api/favorite/photos` | â
| ç
§çå å
¥æ¶è |\n| DELETE | `/api/favorite/photos` | â
| ç
§çç§»åºæ¶è |\n| GET | `/api/favorite/photos/:folder_id` | â
| è·åæ¶è倹å
ç
§ç |\n\n### æå & AI\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/api/current-member` | â
| è·ååœåç»åœæåä¿¡æ¯ |\n| GET | `/api/members` | â
| è·åææå®¶åºæå |\n| GET | `/api/chat/history` | â
| è·åè倩记åœïŒé»è®€è¿ 3 䞪æïŒ |\n| POST | `/api/chat/send` | â
| åéæ¶æ¯ & è·å AI åå€ |\n| GET | `/api/chat/models` | â
| æ¥è¯¢ AI æäŸå & å¯çšæš¡å |\n| POST | `/api/chat/clear` | â
| æž
空èå€©è®°åœ |\n\n### éææä»¶\n\n| æ¹æ³ | è·¯åŸ | éŽæ | 诎æ |\n|------|------|------|------|\n| GET | `/uploads/photos/:filename` | â
| è·åç
§çæä»¶ |\n| GET | `/uploads/covers/:filename` | â
| è·åå°é¢æä»¶ |\n\n---\n\n## ð å®å
šæºå¶\n\n| å±é¢ | æªæœ |\n|------|------|\n| **讀è¯** | JWT TokenïŒHS256ïŒ24h è¿æïŒïŒ`@login_required` è£
饰åšä¿æ€æææ¥å£ |\n| **å¯ç ** | å端 SHA256 + å端 bcryptïŒsalt rounds=12ïŒåéå å¯ |\n| **æä»¶äžäŒ ** | æ©å±åçœååæ ¡éªã`secure_filename` è¿æ»€ãå¯äžæä»¶åé²èŠç |\n| **è·¯åŸå®å
š** | äžäŒ ç®åœéå¶ + `_safe_path()` ç»å¯¹è·¯åŸæ ¡éªïŒé²æ¢è·¯åŸéå |\n| **æ°æ®æ ¡éª** | `album_id` çåæ°åŒºå¶æ£æŽæ°æ ¡éªïŒååšæ§æ ¡éªé²æ¢å¯¹äžååšèµæºæäœ |\n| **SQL 泚å
¥** | å
šéšäœ¿çš PyMySQL åæ°åæ¥è¯¢ (`%s`) |\n| **CORS** | Flask-CORS é
眮 `supports_credentials=True` |\n\n---\n\n## ð€ AI 婿\n\næ¯æäžç§ AI æäŸåïŒéè¿ç¯å¢åé `AI_PROVIDER` 忢ïŒ\n\n| æäŸå | é
眮æ¹åŒ | ç¹ç¹ |\n|--------|---------|------|\n| **OpenAI** | é
眮 `AI_API_KEY` + `AI_API_BASE` | æ¯æ OpenAI / DeepSeek / éä¹åé®çå
Œå®¹æ¥å£ |\n| **Ollama** | `AI_PROVIDER=ollama` | æ¬å°éšçœ²ïŒæ é API KeyïŒå®å
šçŠ»çº¿ |\n| **æš¡ææš¡åŒ** | äžé
眮 `AI_API_KEY` | èªåšäœ¿çšé¢è®Ÿåå€ïŒæ¹äŸ¿äœéªåèœ |\n\n---\n\n## ðž åèœé¢è§\n\n| é¡µé¢ | åèœ |\n|------|------|\n| ð ç»åœé¡µ | æž©éŠšé£æ Œç»åœïŒSHA256 å¯ç å å¯äŒ èŸ |\n| ð çžåå衚 | å±ç€ºææçžåïŒæŸç€ºå°é¢ãæåäžäŒ äººåæ¶éŽ |\n| ðŒïž çžå诊æ
| ç
§ççœæ Œå±ç€ºãå页å 蜜ãæçŽ¢çé |\n| ð€ äžäŒ åºå | å¡«ååœå±æåãæææ¶éŽã倿³šåæ¹éäžäŒ |\n| ð· æç
§äžäŒ | è°çšæå倎æç
§ â é¢è§ â éæ©åœå±äºº â äžäŒ |\n| ð æ¶è管ç | æ¶è倹å衚 + æ¶èç
§ççœæ ŒïŒæ¯æå¢å æ¹ |\n| ð€ AI 婿 | å
šå±æ¬æµ®å¯¹è¯çªïŒèå€©è®°åœæèŽŠå·é犻 |\n\n---\n\n## ð ïž åŒå诎æ\n\n### å端代çé
眮ïŒ`craco.config.js`ïŒ\n\n```javascript\n// åŒåç¯å¢äžèªåšä»£çå°å端\n'/api' â http://localhost:5000/api\n'/uploads' â http://localhost:5000/uploads\n```\n\n### æ¥å¿ç³»ç»\n\n- æ¥å¿ç®åœïŒ`family-photo-backend/logs/`\n- `info.log`ïŒåžžè§æäœæ¥å¿ïŒä¿ç 30 倩ïŒ\n- `error.log`ïŒé误æ¥å¿ïŒä¿ç 90 倩ïŒ\n- æå€©èªåšèœ®èœ¬ïŒWindows äžå®å
šå€çæä»¶éå®\n\n### å¯ç å 坿µçš\n\n```\nçšæ·èŸå
¥ææå¯ç \n â\n âŒ\nå端 CryptoJS.SHA256(password) âââ SHA256 ååž\n â\n âŒ\nPOST /api/login { password: sha256_hash }\n â\n âŒ\nå端 bcrypt.checkpw(sha256_hash, db_bcrypt_hash) âââ éªè¯ç»æ\n```\n\n---\n\n## ð åŸ
åäºé¡¹\n\n- [ ] å®ç° Token é»ååæºå¶ïŒäœ¿ Logout çæ£çæ\n- [ ] æ·»å çžå/ç
§ççè®¿é®æéæ§å¶ïŒææåé犻ïŒ\n- [ ] å°æ°æ®åºå¯ç å JWT å¯é¥è¿ç§»å°ç¯å¢åé\n- [ ] å端 Token æ¹çš HttpOnly Cookie äŒ éïŒé¿å
URL æŽé²\n- [ ] æ·»å ç
§ç EXIF ä¿¡æ¯èªåšæåïŒæææ¶éŽãGPS çïŒ\n- [ ] æ¯æç
§çå猩äžäŒ ïŒåå°ååšç©ºéŽ\n- [ ] æ·»å ç
§çå享åèœïŒçæäžŽæ¶éŸæ¥ïŒ\n\n---\n\n## ð License\n\nMIT License © 2026 Family Photo Manager"}, "e1a416675c1e98239e1344166314c35445cf7309": {"t": 1785771651.5670063, "n": 14, "h": "0f7000c56434d403", "c": "# PhotoManager"}}}} |