diff --git a/.gitignore b/.gitignore index 5ef6a52..f390d12 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +/src/generated/prisma diff --git a/API文档.md b/API文档.md new file mode 100644 index 0000000..00ada70 --- /dev/null +++ b/API文档.md @@ -0,0 +1,943 @@ +# CarrotSkin 后端 API 文档 + +## 概述 + +本文档总结了 CarrotSkin 后端 API,主要关注前端需要的接口,不包括 Yggdrasil 相关接口(除了更换 Yggdrasil 密码)。 + +## 基础信息 + +- **基础URL**: `/api/v1` +- **认证方式**: JWT Bearer Token +- **数据格式**: JSON +- **字符编码**: UTF-8 + +## 通用响应格式 + +所有API响应都遵循以下格式: + +```json +{ + "code": 200, + "message": "操作成功", + "data": { + // 具体数据内容 + } +} +``` + +分页响应格式: + +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "list": [], + "total": 100, + "page": 1, + "page_size": 20, + "total_pages": 5 + } +} +``` + +## 认证相关 API + +### 1. 用户注册 + +- **URL**: `POST /api/v1/auth/register` +- **认证**: 无需认证 +- **请求参数**: +```json +{ + "username": "newuser", // 用户名,3-50字符 + "email": "user@example.com", // 邮箱地址 + "password": "password123", // 密码,6-128字符 + "verification_code": "123456", // 邮箱验证码,6位数字 + "avatar": "https://example.com/avatar.png" // 可选,头像URL +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "注册成功", + "data": { + "token": "jwt_token_here", + "user_info": { + "id": 1, + "username": "newuser", + "email": "user@example.com", + "avatar": "https://example.com/avatar.png", + "points": 0, + "role": "user", + "status": 1, + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } + } +} +``` + +### 2. 用户登录 + +- **URL**: `POST /api/v1/auth/login` +- **认证**: 无需认证 +- **请求参数**: +```json +{ + "username": "testuser", // 用户名或邮箱 + "password": "password123" // 密码 +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "登录成功", + "data": { + "token": "jwt_token_here", + "user_info": { + "id": 1, + "username": "testuser", + "email": "test@example.com", + "avatar": "https://example.com/avatar.png", + "points": 100, + "role": "user", + "status": 1, + "last_login_at": "2025-10-01T12:00:00Z", + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } + } +} +``` + +### 3. 发送验证码 + +- **URL**: `POST /api/v1/auth/send-code` +- **认证**: 无需认证 +- **请求参数**: +```json +{ + "email": "user@example.com", // 邮箱地址 + "type": "register" // 类型: register/reset_password/change_email +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "验证码已发送,请查收邮件", + "data": { + "message": "验证码已发送,请查收邮件" + } +} +``` + +### 4. 重置密码 + +- **URL**: `POST /api/v1/auth/reset-password` +- **认证**: 无需认证 +- **请求参数**: +```json +{ + "email": "user@example.com", // 邮箱地址 + "verification_code": "123456", // 邮箱验证码 + "new_password": "newpassword123" // 新密码 +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "密码重置成功", + "data": { + "message": "密码重置成功" + } +} +``` + +## 用户相关 API + +### 1. 获取用户信息 + +- **URL**: `GET /api/v1/user/profile` +- **认证**: 需要JWT认证 +- **请求参数**: 无 +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "id": 1, + "username": "testuser", + "email": "test@example.com", + "avatar": "https://example.com/avatar.png", + "points": 100, + "role": "user", + "status": 1, + "last_login_at": "2025-10-01T12:00:00Z", + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 2. 更新用户信息 + +- **URL**: `PUT /api/v1/user/profile` +- **认证**: 需要JWT认证 +- **请求参数**: +```json +{ + "avatar": "https://example.com/new-avatar.png", // 可选,新头像URL + "old_password": "oldpassword123", // 可选,修改密码时需要 + "new_password": "newpassword123" // 可选,新密码 +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "id": 1, + "username": "testuser", + "email": "test@example.com", + "avatar": "https://example.com/new-avatar.png", + "points": 100, + "role": "user", + "status": 1, + "last_login_at": "2025-10-01T12:00:00Z", + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 3. 生成头像上传URL + +- **URL**: `POST /api/v1/user/avatar/upload-url` +- **认证**: 需要JWT认证 +- **请求参数**: +```json +{ + "file_name": "avatar.png" // 文件名 +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "post_url": "https://rustfs.example.com/avatars", + "form_data": { + "key": "user_1/xxx.png", + "policy": "base64_policy", + "x-amz-signature": "signature" + }, + "avatar_url": "https://rustfs.example.com/avatars/user_1/xxx.png", + "expires_in": 900 + } +} +``` + +### 4. 更新头像URL + +- **URL**: `PUT /api/v1/user/avatar` +- **认证**: 需要JWT认证 +- **请求参数**: + - Query参数: `avatar_url` - 头像URL +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "id": 1, + "username": "testuser", + "email": "test@example.com", + "avatar": "https://example.com/new-avatar.png", + "points": 100, + "role": "user", + "status": 1, + "last_login_at": "2025-10-01T12:00:00Z", + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 5. 更换邮箱 + +- **URL**: `POST /api/v1/user/change-email` +- **认证**: 需要JWT认证 +- **请求参数**: +```json +{ + "new_email": "newemail@example.com", // 新邮箱地址 + "verification_code": "123456" // 邮箱验证码 +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "id": 1, + "username": "testuser", + "email": "newemail@example.com", + "avatar": "https://example.com/avatar.png", + "points": 100, + "role": "user", + "status": 1, + "last_login_at": "2025-10-01T12:00:00Z", + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 6. 重置Yggdrasil密码 + +- **URL**: `POST /api/v1/user/yggdrasil-password/reset` +- **认证**: 需要JWT认证 +- **请求参数**: 无 +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "password": "new_yggdrasil_password" + } +} +``` + +## 材质相关 API + +### 1. 搜索材质 + +- **URL**: `GET /api/v1/texture` +- **认证**: 无需认证 +- **请求参数**: + - Query参数: + - `keyword`: 搜索关键词 + - `type`: 材质类型 (SKIN/CAPE) + - `public_only`: 是否只搜索公开材质 (true/false) + - `page`: 页码,默认1 + - `page_size`: 每页数量,默认20 +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "list": [ + { + "id": 1, + "uploader_id": 1, + "name": "My Skin", + "description": "A cool skin", + "type": "SKIN", + "url": "https://rustfs.example.com/textures/xxx.png", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 2048, + "is_public": true, + "download_count": 100, + "favorite_count": 50, + "is_slim": false, + "status": 1, + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } + ], + "total": 100, + "page": 1, + "page_size": 20, + "total_pages": 5 + } +} +``` + +### 2. 获取材质详情 + +- **URL**: `GET /api/v1/texture/{id}` +- **认证**: 无需认证 +- **请求参数**: + - 路径参数: `id` - 材质ID +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "id": 1, + "uploader_id": 1, + "name": "My Skin", + "description": "A cool skin", + "type": "SKIN", + "url": "https://rustfs.example.com/textures/xxx.png", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 2048, + "is_public": true, + "download_count": 100, + "favorite_count": 50, + "is_slim": false, + "status": 1, + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 3. 直接上传材质文件(推荐) + +- **URL**: `POST /api/v1/texture/upload` +- **认证**: 需要JWT认证 +- **Content-Type**: `multipart/form-data` +- **请求参数**: + - `file`: 材质文件(PNG格式,1KB-10MB) + - `name`: 材质名称(必填,1-100字符) + - `description`: 材质描述(可选,最多500字符) + - `type`: 材质类型(可选,默认SKIN,可选值:SKIN/CAPE) + - `is_public`: 是否公开(可选,默认false,true/false) + - `is_slim`: 是否为细臂模型(可选,默认false,true/false) +- **说明**: + - 后端会自动计算文件的SHA256哈希值 + - 如果已存在相同哈希的材质,会复用已存在的文件URL,不重复上传 + - 允许多次上传相同哈希的材质(包括同一用户),每次都会创建新的数据库记录 + - 文件存储路径格式:`{type}/{hash[:2]}/{hash[2:4]}/{hash}.png` +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "id": 1, + "uploader_id": 1, + "name": "My Cool Skin", + "description": "A very cool skin", + "type": "SKIN", + "url": "https://rustfs.example.com/textures/skin/e3/b0/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.png", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 2048, + "is_public": true, + "download_count": 0, + "favorite_count": 0, + "is_slim": false, + "status": 1, + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 4. 生成材质上传URL(兼容接口) + +- **URL**: `POST /api/v1/texture/upload-url` +- **认证**: 需要JWT认证 +- **请求参数**: +```json +{ + "file_name": "skin.png", // 文件名 + "texture_type": "SKIN" // 材质类型: SKIN/CAPE +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "post_url": "https://rustfs.example.com/textures", + "form_data": { + "key": "user_1/skin/xxx.png", + "policy": "base64_policy", + "x-amz-signature": "signature" + }, + "texture_url": "https://rustfs.example.com/textures/user_1/skin/xxx.png", + "expires_in": 900 + } +} +``` + +### 5. 创建材质记录(配合预签名URL使用) + +- **URL**: `POST /api/v1/texture` +- **认证**: 需要JWT认证 +- **请求参数**: +```json +{ + "name": "My Cool Skin", // 材质名称,1-100字符 + "description": "A very cool skin", // 描述,最多500字符 + "type": "SKIN", // 材质类型: SKIN/CAPE + "url": "https://rustfs.example.com/textures/user_1/skin/xxx.png", // 材质URL + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", // SHA256哈希 + "size": 2048, // 文件大小(字节) + "is_public": true, // 是否公开 + "is_slim": false // 是否为细臂模型(Alex) +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "id": 1, + "uploader_id": 1, + "name": "My Cool Skin", + "description": "A very cool skin", + "type": "SKIN", + "url": "https://rustfs.example.com/textures/user_1/skin/xxx.png", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 2048, + "is_public": true, + "download_count": 0, + "favorite_count": 0, + "is_slim": false, + "status": 1, + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 6. 更新材质 + +- **URL**: `PUT /api/v1/texture/{id}` +- **认证**: 需要JWT认证 +- **请求参数**: + - 路径参数: `id` - 材质ID + - 请求体: +```json +{ + "name": "Updated Skin Name", // 可选,新名称 + "description": "Updated description", // 可选,新描述 + "is_public": false // 可选,是否公开 +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "id": 1, + "uploader_id": 1, + "name": "Updated Skin Name", + "description": "Updated description", + "type": "SKIN", + "url": "https://rustfs.example.com/textures/user_1/skin/xxx.png", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 2048, + "is_public": false, + "download_count": 100, + "favorite_count": 50, + "is_slim": false, + "status": 1, + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 7. 删除材质 + +- **URL**: `DELETE /api/v1/texture/{id}` +- **认证**: 需要JWT认证 +- **请求参数**: + - 路径参数: `id` - 材质ID +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": null +} +``` + +### 8. 切换收藏状态 + +- **URL**: `POST /api/v1/texture/{id}/favorite` +- **认证**: 需要JWT认证 +- **请求参数**: + - 路径参数: `id` - 材质ID +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "is_favorited": true + } +} +``` + +### 9. 获取用户上传的材质列表 + +- **URL**: `GET /api/v1/texture/my` +- **认证**: 需要JWT认证 +- **请求参数**: + - Query参数: + - `page`: 页码,默认1 + - `page_size`: 每页数量,默认20 +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "list": [ + { + "id": 1, + "uploader_id": 1, + "name": "My Skin", + "description": "A cool skin", + "type": "SKIN", + "url": "https://rustfs.example.com/textures/xxx.png", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 2048, + "is_public": true, + "download_count": 100, + "favorite_count": 50, + "is_slim": false, + "status": 1, + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } + ], + "total": 50, + "page": 1, + "page_size": 20, + "total_pages": 3 + } +} +``` + +### 10. 获取用户收藏的材质列表 + +- **URL**: `GET /api/v1/texture/favorites` +- **认证**: 需要JWT认证 +- **请求参数**: + - Query参数: + - `page`: 页码,默认1 + - `page_size`: 每页数量,默认20 +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "list": [ + { + "id": 1, + "uploader_id": 2, + "name": "Cool Skin", + "description": "A very cool skin", + "type": "SKIN", + "url": "https://rustfs.example.com/textures/xxx.png", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 2048, + "is_public": true, + "download_count": 100, + "favorite_count": 50, + "is_slim": false, + "status": 1, + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } + ], + "total": 30, + "page": 1, + "page_size": 20, + "total_pages": 2 + } +} +``` + +## 档案相关 API + +### 1. 创建档案 + +- **URL**: `POST /api/v1/profile` +- **认证**: 需要JWT认证 +- **请求参数**: +```json +{ + "name": "PlayerName" // 角色名,1-16字符 +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "uuid": "550e8400-e29b-41d4-a716-446655440000", + "user_id": 1, + "name": "PlayerName", + "skin_id": null, + "cape_id": null, + "is_active": false, + "last_used_at": null, + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 2. 获取档案列表 + +- **URL**: `GET /api/v1/profile` +- **认证**: 需要JWT认证 +- **请求参数**: 无 +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": [ + { + "uuid": "550e8400-e29b-41d4-a716-446655440000", + "user_id": 1, + "name": "PlayerName", + "skin_id": 1, + "cape_id": 2, + "is_active": true, + "last_used_at": "2025-10-01T12:00:00Z", + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } + ] +} +``` + +### 3. 获取档案详情 + +- **URL**: `GET /api/v1/profile/{uuid}` +- **认证**: 无需认证 +- **请求参数**: + - 路径参数: `uuid` - 档案UUID +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "uuid": "550e8400-e29b-41d4-a716-446655440000", + "user_id": 1, + "name": "PlayerName", + "skin_id": 1, + "cape_id": 2, + "is_active": true, + "last_used_at": "2025-10-01T12:00:00Z", + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 4. 更新档案 + +- **URL**: `PUT /api/v1/profile/{uuid}` +- **认证**: 需要JWT认证 +- **请求参数**: + - 路径参数: `uuid` - 档案UUID + - 请求体: +```json +{ + "name": "NewPlayerName", // 可选,新角色名 + "skin_id": 1, // 可选,皮肤ID + "cape_id": 2 // 可选,披风ID +} +``` +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "uuid": "550e8400-e29b-41d4-a716-446655440000", + "user_id": 1, + "name": "NewPlayerName", + "skin_id": 1, + "cape_id": 2, + "is_active": true, + "last_used_at": "2025-10-01T12:00:00Z", + "created_at": "2025-10-01T10:00:00Z", + "updated_at": "2025-10-01T10:00:00Z" + } +} +``` + +### 5. 删除档案 + +- **URL**: `DELETE /api/v1/profile/{uuid}` +- **认证**: 需要JWT认证 +- **请求参数**: + - 路径参数: `uuid` - 档案UUID +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "message": "删除成功" + } +} +``` + +### 6. 设置活跃档案 + +- **URL**: `POST /api/v1/profile/{uuid}/activate` +- **认证**: 需要JWT认证 +- **请求参数**: + - 路径参数: `uuid` - 档案UUID +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "message": "设置成功" + } +} +``` + +## 验证码相关 API + +### 1. 生成验证码 + +- **URL**: `GET /api/v1/captcha/generate` +- **认证**: 无需认证 +- **请求参数**: 无 +- **响应数据**: +```json +{ + "code": 200, + "data": { + "masterImage": "base64_encoded_master_image", + "tileImage": "base64_encoded_tile_image", + "captchaId": "captcha_id_here", + "y": 100 + } +} +``` + +### 2. 验证验证码 + +- **URL**: `POST /api/v1/captcha/verify` +- **认证**: 无需认证 +- **请求参数**: +```json +{ + "captchaId": "captcha_id_here", + "dx": 150 // 滑动距离 +} +``` +- **响应数据**: +```json +{ + "code": 200, + "msg": "验证成功" +} +``` + +## 系统相关 API + +### 1. 获取系统配置 + +- **URL**: `GET /api/v1/system/config` +- **认证**: 无需认证 +- **请求参数**: 无 +- **响应数据**: +```json +{ + "code": 200, + "message": "操作成功", + "data": { + "site_name": "CarrotSkin", + "site_description": "A Minecraft Skin Station", + "registration_enabled": true, + "max_textures_per_user": 100, + "max_profiles_per_user": 5 + } +} +``` + +## CustomSkin API + +### 1. 获取玩家信息 + +- **URL**: `GET /api/v1/csl/{username}` +- **认证**: 无需认证 +- **请求参数**: + - 路径参数: `username` - 玩家用户名 +- **响应数据**: +```json +{ + "username": "PlayerName", + "textures": { + "default": "skin_hash_here", + "slim": "skin_hash_here", + "cape": "cape_hash_here", + "elytra": "cape_hash_here" + } +} +``` +或简化格式: +```json +{ + "username": "PlayerName", + "skin": "skin_hash_here" +} +``` + +### 2. 获取资源文件 + +- **URL**: `GET /api/v1/csl/textures/{hash}` +- **认证**: 无需认证 +- **请求参数**: + - 路径参数: `hash` - 资源哈希值 +- **响应数据**: 二进制文件内容 + +## 健康检查 + +### 1. 健康检查 + +- **URL**: `GET /health` +- **认证**: 无需认证 +- **请求参数**: 无 +- **响应数据**: +```json +{ + "status": "ok" +} +``` + +## 错误码说明 + +| 错误码 | 说明 | +|--------|------| +| 200 | 操作成功 | +| 400 | 请求参数错误 | +| 401 | 未认证或认证失败 | +| 403 | 无权限操作 | +| 404 | 资源不存在 | +| 500 | 服务器内部错误 | + +## 认证说明 + +需要JWT认证的API需要在请求头中添加: + +``` +Authorization: Bearer {jwt_token} +``` + +JWT Token在用户登录或注册成功后返回,有效期内可用于访问需要认证的API。 diff --git a/README.md b/README.md index e215bc4..8269615 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,125 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# 🥕 CarrotSkin -## Getting Started +新一代现代化Minecraft Yggdrasil皮肤站,为创作者打造的专业皮肤管理平台。 -First, run the development server: +## ✨ 项目特色 +### 🎨 现代化设计 +- **清新橘色主题**:温暖的橙色渐变配色方案,营造舒适的视觉体验 +- **玻璃态效果**:使用backdrop-filter实现现代化的毛玻璃效果 +- **流畅动画**:基于Framer Motion的丝滑动画和交互效果 +- **响应式布局**:完美适配桌面端、平板和移动设备 + +### 🔧 技术栈 +- **Next.js 16**:最新的React框架,支持App Router +- **TypeScript**:类型安全的开发体验 +- **Tailwind CSS v4**:现代化的CSS框架,支持自定义主题 +- **Framer Motion**:专业级动画库 +- **Heroicons**:精美的图标系统 + +### 🚀 核心功能 + +#### Yggdrasil API支持 +- 完整的Minecraft Yggdrasil认证系统 +- 安全可靠的API接口 +- 支持第三方启动器集成 + +#### 皮肤管理 +- 无限皮肤存储空间 +- 3D皮肤预览功能 +- 多角色管理系统 +- 皮肤版本控制 + +#### 社区功能 +- 皮肤分享和发现 +- 用户关注和互动 +- 皮肤收藏和点赞 +- 评论和评价系统 + +#### 现代化体验 +- 拖拽上传皮肤 +- 实时预览效果 +- 批量管理操作 +- 智能搜索筛选 + +## 🎯 页面结构 + +### 主页 (/) +- 现代化英雄区域展示 +- Yggdrasil特色功能介绍 +- 统计数据展示 +- 行动召唤区域 + +### 皮肤库 (/skins) +- 网格化皮肤展示 +- 高级搜索和筛选 +- 分类标签系统 +- 排序和分页功能 + +### 个人中心 (/profile) +- 用户信息管理 +- 角色和皮肤管理 +- 账户设置 +- API密钥管理 + +## 🛠️ 开发环境 + +### 安装依赖 ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +npm install ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +### 启动开发服务器 +```bash +npm run dev +``` -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +### 构建生产版本 +```bash +npm run build +``` -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +## 🎨 设计亮点 -## Learn More +### 视觉层次 +- 使用不同深浅的橙色创建视觉层次 +- 渐变背景增强深度感 +- 阴影效果营造立体感 -To learn more about Next.js, take a look at the following resources: +### 交互体验 +- 鼠标跟随效果 +- 滚动视差动画 +- 悬停状态反馈 +- 加载动画效果 -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +### 现代化元素 +- 圆角卡片设计 +- 渐变按钮效果 +- 玻璃态导航栏 +- 响应式断点优化 -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +## 🔮 未来规划 -## Deploy on Vercel +### 即将推出 +- 3D皮肤编辑器 +- 皮肤模板系统 +- 高级统计分析 +- 移动端APP -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +### 长期目标 +- AI皮肤生成 +- 皮肤交易市场 +- 创作者激励计划 +- 国际化支持 -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +## 🤝 贡献指南 + +欢迎提交Issue和Pull Request来帮助我们改进项目! + +## 📄 许可证 + +MIT License - 详见 [LICENSE](LICENSE) 文件 + +--- + +**CarrotSkin** - 让Minecraft皮肤管理变得简单而优雅 🥕 diff --git a/package-lock.json b/package-lock.json index 563edfd..744ec00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,20 @@ "name": "carrotskin", "version": "0.1.0", "dependencies": { + "@auth/prisma-adapter": "^2.11.1", + "@headlessui/react": "^2.2.9", + "@heroicons/react": "^2.2.0", + "@prisma/client": "^7.1.0", + "@types/three": "^0.181.0", + "framer-motion": "^12.23.25", + "lucide-react": "^0.555.0", "next": "16.0.7", + "next-auth": "^4.24.13", + "prisma": "^7.1.0", "react": "19.2.0", - "react-dom": "19.2.0" + "react-dom": "19.2.0", + "skinview3d": "^3.4.1", + "three": "^0.181.2" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -36,6 +47,139 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@auth/core": { + "version": "0.34.3", + "resolved": "https://registry.npmmirror.com/@auth/core/-/core-0.34.3.tgz", + "integrity": "sha512-jMjY/S0doZnWYNV90x0jmU3B+UcrsfGYnukxYrRbj0CVvGI/MX3JbHsxSrx2d4mbnXaUsqJmAcDfoQWA6r0lOw==", + "license": "ISC", + "optional": true, + "dependencies": { + "@panva/hkdf": "^1.1.1", + "@types/cookie": "0.6.0", + "cookie": "0.6.0", + "jose": "^5.1.3", + "oauth4webapi": "^2.10.4", + "preact": "10.11.3", + "preact-render-to-string": "5.2.3" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^7" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/@auth/core/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@auth/core/node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmmirror.com/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@auth/core/node_modules/preact-render-to-string": { + "version": "5.2.3", + "resolved": "https://registry.npmmirror.com/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz", + "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==", + "license": "MIT", + "optional": true, + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, + "node_modules/@auth/prisma-adapter": { + "version": "2.11.1", + "resolved": "https://registry.npmmirror.com/@auth/prisma-adapter/-/prisma-adapter-2.11.1.tgz", + "integrity": "sha512-Ke7DXP0Fy0Mlmjz/ZJLXwQash2UkA4621xCM0rMtEczr1kppLc/njCbUkHkIQ/PnmILjqSPEKeTjDPsYruvkug==", + "license": "ISC", + "dependencies": { + "@auth/core": "0.41.1" + }, + "peerDependencies": { + "@prisma/client": ">=2.26.0 || >=3 || >=4 || >=5 || >=6" + } + }, + "node_modules/@auth/prisma-adapter/node_modules/@auth/core": { + "version": "0.41.1", + "resolved": "https://registry.npmmirror.com/@auth/core/-/core-0.41.1.tgz", + "integrity": "sha512-t9cJ2zNYAdWMacGRMT6+r4xr1uybIdmYa49calBPeTqwgAFPV/88ac9TEvCR85pvATiSPt8VaNf+Gt24JIT/uw==", + "license": "ISC", + "dependencies": { + "@panva/hkdf": "^1.2.1", + "jose": "^6.0.6", + "oauth4webapi": "^3.3.0", + "preact": "10.24.3", + "preact-render-to-string": "6.5.11" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^7.0.7" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/@auth/prisma-adapter/node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmmirror.com/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@auth/prisma-adapter/node_modules/oauth4webapi": { + "version": "3.8.3", + "resolved": "https://registry.npmmirror.com/oauth4webapi/-/oauth4webapi-3.8.3.tgz", + "integrity": "sha512-pQ5BsX3QRTgnt5HxgHwgunIRaDXBdkT23tf8dfzmtTIL2LTpdmxgbpbBm0VgFWAIDlezQvQCTgnVIUmHupXHxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@auth/prisma-adapter/node_modules/preact-render-to-string": { + "version": "6.5.11", + "resolved": "https://registry.npmmirror.com/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz", + "integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==", + "license": "MIT", + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -229,6 +373,15 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz", @@ -277,6 +430,73 @@ "node": ">=6.9.0" } }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmmirror.com/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmmirror.com/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmmirror.com/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmmirror.com/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", + "license": "Apache-2.0" + }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/@electric-sql/pglite/-/pglite-0.3.2.tgz", + "integrity": "sha512-zfWWa+V2ViDCY/cmUfRqeWY1yLto+EpxjXnZzenB1TyxsTiXaTWeZFIZw6mac52BsuQm0RjCnisjBtdBaXOI6w==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@electric-sql/pglite-socket": { + "version": "0.0.6", + "resolved": "https://registry.npmmirror.com/@electric-sql/pglite-socket/-/pglite-socket-0.0.6.tgz", + "integrity": "sha512-6RjmgzphIHIBA4NrMGJsjNWK4pu+bCWJlEWlwcxFTVY3WT86dFpKwbZaGWZV6C5Rd7sCk1Z0CI76QEfukLAUXw==", + "license": "Apache-2.0", + "bin": { + "pglite-server": "dist/scripts/server.js" + }, + "peerDependencies": { + "@electric-sql/pglite": "0.3.2" + } + }, + "node_modules/@electric-sql/pglite-tools": { + "version": "0.2.7", + "resolved": "https://registry.npmmirror.com/@electric-sql/pglite-tools/-/pglite-tools-0.2.7.tgz", + "integrity": "sha512-9dAccClqxx4cZB+Ar9B+FZ5WgxDc/Xvl9DPrTWv+dYTf0YNubLzi4wHHRGRGhrJv15XwnyKcGOZAP1VXSneSUg==", + "license": "Apache-2.0", + "peerDependencies": { + "@electric-sql/pglite": "0.3.2" + } + }, "node_modules/@emnapi/core": { "version": "1.7.1", "resolved": "https://registry.npmmirror.com/@emnapi/core/-/core-1.7.1.tgz", @@ -454,6 +674,100 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmmirror.com/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@headlessui/react": { + "version": "2.2.9", + "resolved": "https://registry.npmmirror.com/@headlessui/react/-/react-2.2.9.tgz", + "integrity": "sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.20.2", + "@react-aria/interactions": "^3.25.0", + "@tanstack/react-virtual": "^3.13.9", + "use-sync-external-store": "^1.5.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/@heroicons/react": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/@heroicons/react/-/react-2.2.0.tgz", + "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16 || ^19.0.0-rc" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.6", + "resolved": "https://registry.npmmirror.com/@hono/node-server/-/node-server-1.19.6.tgz", + "integrity": "sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", @@ -1022,6 +1336,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mrleebo/prisma-ast": { + "version": "0.12.1", + "resolved": "https://registry.npmmirror.com/@mrleebo/prisma-ast/-/prisma-ast-0.12.1.tgz", + "integrity": "sha512-JwqeCQ1U3fvccttHZq7Tk0m/TMC6WcFAQZdukypW3AzlJYKYTGNVd1ANU2GuhKnv4UQuOFj3oAl0LLG/gxFN1w==", + "license": "MIT", + "dependencies": { + "chevrotain": "^10.5.0", + "lilconfig": "^2.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -1227,6 +1554,266 @@ "node": ">=12.4.0" } }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@prisma/client": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/@prisma/client/-/client-7.1.0.tgz", + "integrity": "sha512-qf7GPYHmS/xybNiSOpzv9wBo+UwqfL2PeyX+08v+KVHDI0AlSCQIh5bBySkH3alu06NX9wy98JEnckhMHoMFfA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@prisma/client-runtime-utils": "7.1.0" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/client-runtime-utils": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/@prisma/client-runtime-utils/-/client-runtime-utils-7.1.0.tgz", + "integrity": "sha512-39xmeBrNTN40FzF34aJMjfX1PowVCqoT3UKUWBBSP3aXV05NRqGBC3x2wCDs96ti6ZgdiVzqnRDHtbzU8X+lPQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/config": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/@prisma/config/-/config-7.1.0.tgz", + "integrity": "sha512-Uz+I43Wn1RYNHtuYtOhOnUcNMWp2Pd3GUDDKs37xlHptCGpzEG3MRR9L+8Y2ISMsMI24z/Ni+ww6OB/OO8M0sQ==", + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/@prisma/debug/-/debug-7.1.0.tgz", + "integrity": "sha512-pPAckG6etgAsEBusmZiFwM9bldLSNkn++YuC4jCTJACdK5hLOVnOzX7eSL2FgaU6Gomd6wIw21snUX2dYroMZQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/dev": { + "version": "0.15.0", + "resolved": "https://registry.npmmirror.com/@prisma/dev/-/dev-0.15.0.tgz", + "integrity": "sha512-KhWaipnFlS/fWEs6I6Oqjcy2S08vKGmxJ5LexqUl/3Ve0EgLUsZwdKF0MvqPM5F5ttw8GtfZarjM5y7VLwv9Ow==", + "license": "ISC", + "dependencies": { + "@electric-sql/pglite": "0.3.2", + "@electric-sql/pglite-socket": "0.0.6", + "@electric-sql/pglite-tools": "0.2.7", + "@hono/node-server": "1.19.6", + "@mrleebo/prisma-ast": "0.12.1", + "@prisma/get-platform": "6.8.2", + "@prisma/query-plan-executor": "6.18.0", + "foreground-child": "3.3.1", + "get-port-please": "3.1.2", + "hono": "4.10.6", + "http-status-codes": "2.3.0", + "pathe": "2.0.3", + "proper-lockfile": "4.1.2", + "remeda": "2.21.3", + "std-env": "3.9.0", + "valibot": "1.2.0", + "zeptomatch": "2.0.2" + } + }, + "node_modules/@prisma/engines": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/@prisma/engines/-/engines-7.1.0.tgz", + "integrity": "sha512-KQlraOybdHAzVv45KWKJzpR9mJLkib7/TyApQpqrsL7FUHfgjIcy8jrVGt3iNfG6/GDDl+LNlJ84JSQwIfdzxA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.1.0", + "@prisma/engines-version": "7.1.0-6.ab635e6b9d606fa5c8fb8b1a7f909c3c3c1c98ba", + "@prisma/fetch-engine": "7.1.0", + "@prisma/get-platform": "7.1.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.1.0-6.ab635e6b9d606fa5c8fb8b1a7f909c3c3c1c98ba", + "resolved": "https://registry.npmmirror.com/@prisma/engines-version/-/engines-version-7.1.0-6.ab635e6b9d606fa5c8fb8b1a7f909c3c3c1c98ba.tgz", + "integrity": "sha512-qZUevUh+yPhGT28rDQnV8V2kLnFjirzhVD67elRPIJHRsUV/mkII10HSrJrhK/U2GYgAxXR2VEREtq7AsfS8qw==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/@prisma/get-platform/-/get-platform-7.1.0.tgz", + "integrity": "sha512-lq8hMdjKiZftuT5SssYB3EtQj8+YjL24/ZTLflQqzFquArKxBcyp6Xrblto+4lzIKJqnpOjfMiBjMvl7YuD7+Q==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.1.0" + } + }, + "node_modules/@prisma/fetch-engine": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/@prisma/fetch-engine/-/fetch-engine-7.1.0.tgz", + "integrity": "sha512-GZYF5Q8kweXWGfn87hTu17kw7x1DgnehgKoE4Zg1BmHYF3y1Uu0QRY/qtSE4veH3g+LW8f9HKqA0tARG66bxxQ==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.1.0", + "@prisma/engines-version": "7.1.0-6.ab635e6b9d606fa5c8fb8b1a7f909c3c3c1c98ba", + "@prisma/get-platform": "7.1.0" + } + }, + "node_modules/@prisma/fetch-engine/node_modules/@prisma/get-platform": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/@prisma/get-platform/-/get-platform-7.1.0.tgz", + "integrity": "sha512-lq8hMdjKiZftuT5SssYB3EtQj8+YjL24/ZTLflQqzFquArKxBcyp6Xrblto+4lzIKJqnpOjfMiBjMvl7YuD7+Q==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.1.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.8.2", + "resolved": "https://registry.npmmirror.com/@prisma/get-platform/-/get-platform-6.8.2.tgz", + "integrity": "sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.8.2" + } + }, + "node_modules/@prisma/get-platform/node_modules/@prisma/debug": { + "version": "6.8.2", + "resolved": "https://registry.npmmirror.com/@prisma/debug/-/debug-6.8.2.tgz", + "integrity": "sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/query-plan-executor": { + "version": "6.18.0", + "resolved": "https://registry.npmmirror.com/@prisma/query-plan-executor/-/query-plan-executor-6.18.0.tgz", + "integrity": "sha512-jZ8cfzFgL0jReE1R10gT8JLHtQxjWYLiQ//wHmVYZ2rVkFHoh0DT8IXsxcKcFlfKN7ak7k6j0XMNn2xVNyr5cA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/studio-core": { + "version": "0.8.2", + "resolved": "https://registry.npmmirror.com/@prisma/studio-core/-/studio-core-0.8.2.tgz", + "integrity": "sha512-/iAEWEUpTja+7gVMu1LtR2pPlvDmveAwMHdTWbDeGlT7yiv0ZTCPpmeAGdq/Y9aJ9Zj1cEGBXGRbmmNPj022PQ==", + "license": "UNLICENSED", + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-aria/focus": { + "version": "3.21.2", + "resolved": "https://registry.npmmirror.com/@react-aria/focus/-/focus-3.21.2.tgz", + "integrity": "sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.25.6", + "@react-aria/utils": "^3.31.0", + "@react-types/shared": "^3.32.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.25.6", + "resolved": "https://registry.npmmirror.com/@react-aria/interactions/-/interactions-3.25.6.tgz", + "integrity": "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-aria/utils": "^3.31.0", + "@react-stately/flags": "^3.1.2", + "@react-types/shared": "^3.32.1", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.10", + "resolved": "https://registry.npmmirror.com/@react-aria/ssr/-/ssr-3.9.10.tgz", + "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.31.0", + "resolved": "https://registry.npmmirror.com/@react-aria/utils/-/utils-3.31.0.tgz", + "integrity": "sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-stately/flags": "^3.1.2", + "@react-stately/utils": "^3.10.8", + "@react-types/shared": "^3.32.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/flags": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@react-stately/flags/-/flags-3.1.2.tgz", + "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.10.8", + "resolved": "https://registry.npmmirror.com/@react-stately/utils/-/utils-3.10.8.tgz", + "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.32.1", + "resolved": "https://registry.npmmirror.com/@react-types/shared/-/shared-3.32.1.tgz", + "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1234,6 +1821,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.15.tgz", @@ -1514,6 +2107,39 @@ "tailwindcss": "4.1.17" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.12", + "resolved": "https://registry.npmmirror.com/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz", + "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.12", + "resolved": "https://registry.npmmirror.com/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", + "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmmirror.com/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -1525,6 +2151,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT", + "optional": true + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", @@ -1560,7 +2193,6 @@ "version": "19.2.7", "resolved": "https://registry.npmmirror.com/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", - "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -1577,6 +2209,33 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmmirror.com/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.181.0", + "resolved": "https://registry.npmmirror.com/@types/three/-/three-0.181.0.tgz", + "integrity": "sha512-MLF1ks8yRM2k71D7RprFpDb9DOX0p22DbdPqT/uAkc6AtQXjxWCVDjCy23G9t1o8HcQPk7woD2NIyiaWcWPYmA==", + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": "*", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.22.0" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmmirror.com/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.48.1", "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.1.tgz", @@ -2117,6 +2776,12 @@ "win32" ] }, + "node_modules/@webgpu/types": { + "version": "0.1.67", + "resolved": "https://registry.npmmirror.com/@webgpu/types/-/types-0.1.67.tgz", + "integrity": "sha512-uk53+2ECGUkWoDFez/hymwpRfdgdIn6y1ref70fEecGMe5607f4sozNFgBk0oxlr7j2CRGWBEc3IBYMmFdGGTQ==", + "license": "BSD-3-Clause" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", @@ -2384,6 +3049,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/axe-core": { "version": "4.11.0", "resolved": "https://registry.npmmirror.com/axe-core/-/axe-core-4.11.0.tgz", @@ -2480,6 +3154,34 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz", @@ -2577,12 +3279,59 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chevrotain": { + "version": "10.5.0", + "resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-10.5.0.tgz", + "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "10.5.0", + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "@chevrotain/utils": "10.5.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmmirror.com/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", @@ -2610,6 +3359,21 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2617,11 +3381,19 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -2636,7 +3408,6 @@ "version": "3.2.3", "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -2725,6 +3496,15 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmmirror.com/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", @@ -2761,6 +3541,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", @@ -2784,6 +3585,18 @@ "node": ">=0.10.0" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2799,6 +3612,16 @@ "node": ">= 0.4" } }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmmirror.com/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.264", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.264.tgz", @@ -2813,6 +3636,15 @@ "dev": true, "license": "MIT" }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -3453,6 +4285,34 @@ "node": ">=0.10.0" } }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmmirror.com/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3514,6 +4374,12 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3594,6 +4460,49 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/framer-motion": { + "version": "12.23.25", + "resolved": "https://registry.npmmirror.com/framer-motion/-/framer-motion-12.23.25.tgz", + "integrity": "sha512-gUHGl2e4VG66jOcH0JHhuJQr6ZNwrET9g31ZG0xdXzT0CznP7fHX4P8Bcvuc4MiUB90ysNnWX2ukHRIggkl6hQ==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.23", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", @@ -3635,6 +4544,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz", @@ -3680,6 +4598,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-port-please": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/get-port-please/-/get-port-please-3.1.2.tgz", + "integrity": "sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==", + "license": "MIT" + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", @@ -3725,6 +4649,23 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3785,9 +4726,14 @@ "version": "4.2.11", "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, + "node_modules/grammex": { + "version": "3.1.12", + "resolved": "https://registry.npmmirror.com/grammex/-/grammex-3.1.12.tgz", + "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", + "license": "MIT" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", @@ -3906,6 +4852,38 @@ "hermes-estree": "0.25.1" } }, + "node_modules/hono": { + "version": "4.10.6", + "resolved": "https://registry.npmmirror.com/hono/-/hono-4.10.6.tgz", + "integrity": "sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", @@ -4228,6 +5206,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz", @@ -4384,7 +5368,6 @@ "version": "2.0.0", "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/iterator.prototype": { @@ -4409,12 +5392,20 @@ "version": "2.6.1", "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmmirror.com/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4803,6 +5794,15 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", @@ -4819,6 +5819,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4826,6 +5832,12 @@ "dev": true, "license": "MIT" }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4849,6 +5861,30 @@ "yallist": "^3.0.2" } }, + "node_modules/lru.min": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.3.tgz", + "integrity": "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/lucide-react": { + "version": "0.555.0", + "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.555.0.tgz", + "integrity": "sha512-D8FvHUGbxWBRQM90NZeIyhAvkFfsh3u9ekrMvJ30Z6gnpBHS6HC6ldLg7tL45hwiIz/u66eKDtdA23gwwGsAHA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", @@ -4879,6 +5915,12 @@ "node": ">= 8" } }, + "node_modules/meshoptimizer": { + "version": "0.22.0", + "resolved": "https://registry.npmmirror.com/meshoptimizer/-/meshoptimizer-0.22.0.tgz", + "integrity": "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==", + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", @@ -4916,6 +5958,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/motion-dom": { + "version": "12.23.23", + "resolved": "https://registry.npmmirror.com/motion-dom/-/motion-dom-12.23.23.tgz", + "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmmirror.com/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", @@ -4923,6 +5980,47 @@ "dev": true, "license": "MIT" }, + "node_modules/mysql2": { + "version": "3.15.3", + "resolved": "https://registry.npmmirror.com/mysql2/-/mysql2-3.15.3.tgz", + "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", @@ -5016,6 +6114,38 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.13", + "resolved": "https://registry.npmmirror.com/next-auth/-/next-auth-4.24.13.tgz", + "integrity": "sha512-sgObCfcfL7BzIK76SS5TnQtc3yo2Oifp/yIpfv6fMfeBOiBJkDWF3A2y9+yqnmJ4JKc2C+nMjSjmgDeTwgN1rQ==", + "license": "ISC", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.7.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "@auth/core": "0.34.3", + "next": "^12.2.5 || ^13 || ^14 || ^15 || ^16", + "nodemailer": "^7.0.7", + "react": "^17.0.2 || ^18 || ^19", + "react-dom": "^17.0.2 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@auth/core": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz", @@ -5044,6 +6174,12 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz", @@ -5051,6 +6187,41 @@ "dev": true, "license": "MIT" }, + "node_modules/nypm": { + "version": "0.6.2", + "resolved": "https://registry.npmmirror.com/nypm/-/nypm-0.6.2.tgz", + "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.2", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "tinyexec": "^1.0.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmmirror.com/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==", + "license": "MIT" + }, + "node_modules/oauth4webapi": { + "version": "2.17.0", + "resolved": "https://registry.npmmirror.com/oauth4webapi/-/oauth4webapi-2.17.0.tgz", + "integrity": "sha512-lbC0Z7uzAFNFyzEYRIC+pkSVvDHJTbEW+dYlSBAlCYDe6RxUkJ26bClhk8ocBZip1wfI9uKTe0fm4Ib4RHn6uQ==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", @@ -5061,6 +6232,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", @@ -5174,6 +6354,54 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/oidc-token-hash": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/oidc-token-hash/-/oidc-token-hash-5.2.0.tgz", + "integrity": "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, + "node_modules/openid-client": { + "version": "5.7.1", + "resolved": "https://registry.npmmirror.com/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "license": "MIT", + "dependencies": { + "jose": "^4.15.9", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/openid-client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", @@ -5269,7 +6497,6 @@ "version": "3.1.1", "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5282,6 +6509,18 @@ "dev": true, "license": "MIT" }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", @@ -5301,6 +6540,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -5340,6 +6590,42 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postgres": { + "version": "3.4.7", + "resolved": "https://registry.npmmirror.com/postgres/-/postgres-3.4.7.tgz", + "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmmirror.com/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmmirror.com/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "license": "MIT", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5350,6 +6636,46 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==", + "license": "MIT" + }, + "node_modules/prisma": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/prisma/-/prisma-7.1.0.tgz", + "integrity": "sha512-dy/3urE4JjhdiW5b09pGjVhGI7kPESK2VlCDrCqeYK5m5SslAtG5FCGnZWP7E8Sdg+Ow1wV2mhJH5RTFL5gEsw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@prisma/config": "7.1.0", + "@prisma/dev": "0.15.0", + "@prisma/engines": "7.1.0", + "@prisma/studio-core": "0.8.2", + "mysql2": "3.15.3", + "postgres": "3.4.7" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0" + }, + "peerDependencies": { + "better-sqlite3": ">=9.0.0", + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", @@ -5362,6 +6688,23 @@ "react-is": "^16.13.1" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", @@ -5372,6 +6715,22 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5393,6 +6752,16 @@ ], "license": "MIT" }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, "node_modules/react": { "version": "19.2.0", "resolved": "https://registry.npmmirror.com/react/-/react-19.2.0.tgz", @@ -5423,6 +6792,19 @@ "dev": true, "license": "MIT" }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5446,6 +6828,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -5467,6 +6855,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/remeda": { + "version": "2.21.3", + "resolved": "https://registry.npmmirror.com/remeda/-/remeda-2.21.3.tgz", + "integrity": "sha512-XXrZdLA10oEOQhLLzEJEiFFSKi21REGAkHdImIb4rt/XXy8ORGXh5HCcpUOsElfPNDb+X6TA/+wkh+p2KffYmg==", + "license": "MIT", + "dependencies": { + "type-fest": "^4.39.1" + } + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz", @@ -5508,6 +6905,15 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", @@ -5598,6 +7004,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz", @@ -5614,6 +7026,11 @@ "semver": "bin/semver.js" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmmirror.com/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", @@ -5725,7 +7142,6 @@ "version": "2.0.0", "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -5738,7 +7154,6 @@ "version": "3.0.0", "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5820,6 +7235,65 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/skinview-utils": { + "version": "0.7.1", + "resolved": "https://registry.npmmirror.com/skinview-utils/-/skinview-utils-0.7.1.tgz", + "integrity": "sha512-4eLrMqR526ehlZbsd8SuZ/CHpS9GiH0xUMoV+PYlJVi95ZFz5HJu7Spt5XYa72DRS7wgt5qquvHZf0XZJgmu9Q==", + "license": "MIT" + }, + "node_modules/skinview3d": { + "version": "3.4.1", + "resolved": "https://registry.npmmirror.com/skinview3d/-/skinview3d-3.4.1.tgz", + "integrity": "sha512-WVN1selfDSAoQB7msLs3ueJjW/pge3nsmbqxJeXPnN/qIJ1GJKpMZO8mavSvMojaMrmpSgOJWfYUkK9B34ts2g==", + "license": "MIT", + "dependencies": { + "@types/three": "^0.156.0", + "skinview-utils": "^0.7.1", + "three": "^0.156.0" + } + }, + "node_modules/skinview3d/node_modules/@types/three": { + "version": "0.156.0", + "resolved": "https://registry.npmmirror.com/@types/three/-/three-0.156.0.tgz", + "integrity": "sha512-733bXDSRdlrxqOmQuOmfC1UBRuJ2pREPk8sWnx9MtIJEVDQMx8U0NQO5MVVaOrjzDPyLI+cFPim2X/ss9v0+LQ==", + "license": "MIT", + "dependencies": { + "@types/stats.js": "*", + "@types/webxr": "*", + "fflate": "~0.6.10", + "meshoptimizer": "~0.18.1" + } + }, + "node_modules/skinview3d/node_modules/fflate": { + "version": "0.6.10", + "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.6.10.tgz", + "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==", + "license": "MIT" + }, + "node_modules/skinview3d/node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmmirror.com/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "license": "MIT" + }, + "node_modules/skinview3d/node_modules/three": { + "version": "0.156.1", + "resolved": "https://registry.npmmirror.com/three/-/three-0.156.1.tgz", + "integrity": "sha512-kP7H0FK9d/k6t/XvQ9FO6i+QrePoDcNhwl0I02+wmUJRNSLCUIDMcfObnzQvxb37/0Uc9TDT0T1HgsRRrO6SYQ==", + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5829,6 +7303,15 @@ "node": ">=0.10.0" } }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmmirror.com/stable-hash/-/stable-hash-0.0.5.tgz", @@ -5836,6 +7319,12 @@ "dev": true, "license": "MIT" }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -6035,6 +7524,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/tabbable/-/tabbable-6.3.0.tgz", + "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==", + "license": "MIT" + }, "node_modules/tailwindcss": { "version": "4.1.17", "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.1.17.tgz", @@ -6056,6 +7551,21 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/three": { + "version": "0.181.2", + "resolved": "https://registry.npmmirror.com/three/-/three-0.181.2.tgz", + "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -6176,6 +7686,18 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -6258,7 +7780,7 @@ "version": "5.9.3", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -6395,11 +7917,42 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/valibot": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/valibot/-/valibot-1.2.0.tgz", + "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -6530,6 +8083,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zeptomatch": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/zeptomatch/-/zeptomatch-2.0.2.tgz", + "integrity": "sha512-H33jtSKf8Ijtb5BW6wua3G5DhnFjbFML36eFu+VdOoVY4HD9e7ggjqdM6639B+L87rjnR6Y+XeRzBXZdy52B/g==", + "license": "MIT", + "dependencies": { + "grammex": "^3.1.10" + } + }, "node_modules/zod": { "version": "4.1.13", "resolved": "https://registry.npmmirror.com/zod/-/zod-4.1.13.tgz", diff --git a/package.json b/package.json index f70bb43..b6e4e62 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,20 @@ "lint": "eslint" }, "dependencies": { + "@auth/prisma-adapter": "^2.11.1", + "@headlessui/react": "^2.2.9", + "@heroicons/react": "^2.2.0", + "@prisma/client": "^7.1.0", + "@types/three": "^0.181.0", + "framer-motion": "^12.23.25", + "lucide-react": "^0.555.0", "next": "16.0.7", + "next-auth": "^4.24.13", + "prisma": "^7.1.0", "react": "19.2.0", - "react-dom": "19.2.0" + "react-dom": "19.2.0", + "skinview3d": "^3.4.1", + "three": "^0.181.2" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/postcss.config.mjs b/postcss.config.mjs index 61e3684..a7f73a2 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,7 +1,5 @@ -const config = { +export default { plugins: { - "@tailwindcss/postcss": {}, + '@tailwindcss/postcss': {}, }, -}; - -export default config; +} diff --git a/prisma.config.ts b/prisma.config.ts new file mode 100644 index 0000000..9c5e959 --- /dev/null +++ b/prisma.config.ts @@ -0,0 +1,14 @@ +// This file was generated by Prisma and assumes you have installed the following: +// npm install --save-dev prisma dotenv +import "dotenv/config"; +import { defineConfig, env } from "prisma/config"; + +export default defineConfig({ + schema: "prisma/schema.prisma", + migrations: { + path: "prisma/migrations", + }, + datasource: { + url: env("DATABASE_URL"), + }, +}); diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..f51cc2c --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,14 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +generator client { + provider = "prisma-client" + output = "../src/generated/prisma" +} + +datasource db { + provider = "postgresql" +} diff --git a/src/app/auth/layout.tsx b/src/app/auth/layout.tsx new file mode 100644 index 0000000..0fe95bb --- /dev/null +++ b/src/app/auth/layout.tsx @@ -0,0 +1,11 @@ +export default function AuthLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+ {children} +
+ ); +} diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx new file mode 100644 index 0000000..ca91280 --- /dev/null +++ b/src/app/auth/page.tsx @@ -0,0 +1,732 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { motion, AnimatePresence } from 'framer-motion'; +import { EyeIcon, EyeSlashIcon, CheckCircleIcon, XCircleIcon } from '@heroicons/react/24/outline'; +import { useAuth } from '@/contexts/AuthContext'; +import { errorManager } from '@/components/ErrorNotification'; + +export default function AuthPage() { + const [isLoginMode, setIsLoginMode] = useState(true); + const [formData, setFormData] = useState({ + username: '', + email: '', + password: '', + confirmPassword: '', + verificationCode: '', + rememberMe: false, + agreeToTerms: false + }); + + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [errors, setErrors] = useState>({}); + const [isLoading, setIsLoading] = useState(false); + const [authError, setAuthError] = useState(''); + const [isSendingCode, setIsSendingCode] = useState(false); + const [codeTimer, setCodeTimer] = useState(0); + + const { login, register } = useAuth(); + const router = useRouter(); + + useEffect(() => { + let interval: NodeJS.Timeout; + if (codeTimer > 0) { + interval = setInterval(() => { + setCodeTimer(prev => prev - 1); + }, 1000); + } + return () => clearInterval(interval); + }, [codeTimer]); + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value, type, checked } = e.target; + setFormData(prev => ({ + ...prev, + [name]: type === 'checkbox' ? checked : value + })); + + if (errors[name]) { + setErrors(prev => ({ ...prev, [name]: '' })); + } + if (authError) { + setAuthError(''); + } + }; + + const validateLoginForm = () => { + const newErrors: Record = {}; + + if (!formData.username.trim()) { + newErrors.username = '请输入用户名或邮箱'; + } + + if (!formData.password) { + newErrors.password = '请输入密码'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const validateRegisterForm = () => { + const newErrors: Record = {}; + + if (!formData.username.trim()) { + newErrors.username = '用户名不能为空'; + } else if (formData.username.length < 3) { + newErrors.username = '用户名至少需要3个字符'; + } else if (formData.username.length > 50) { + newErrors.username = '用户名不能超过50个字符'; + } + + if (!formData.email.trim()) { + newErrors.email = '邮箱不能为空'; + } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { + newErrors.email = '请输入有效的邮箱地址'; + } + + if (!formData.password) { + newErrors.password = '密码不能为空'; + } else if (formData.password.length < 6) { + newErrors.password = '密码至少需要6个字符'; + } else if (formData.password.length > 128) { + newErrors.password = '密码不能超过128个字符'; + } + + if (formData.password !== formData.confirmPassword) { + newErrors.confirmPassword = '两次输入的密码不一致'; + } + + if (!formData.verificationCode) { + newErrors.verificationCode = '请输入验证码'; + } else if (!/^\d{6}$/.test(formData.verificationCode)) { + newErrors.verificationCode = '验证码应为6位数字'; + } + + if (!formData.agreeToTerms) { + newErrors.agreeToTerms = '请同意服务条款'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const getPasswordStrength = () => { + const password = formData.password; + if (password.length === 0) return { strength: 0, label: '', color: 'bg-gray-200' }; + if (password.length < 6) return { strength: 1, label: '弱', color: 'bg-red-500' }; + if (password.length < 10) return { strength: 2, label: '中等', color: 'bg-yellow-500' }; + if (password.length >= 15) return { strength: 4, label: '很强', color: 'bg-green-500' }; + return { strength: 3, label: '强', color: 'bg-blue-500' }; + }; + + const passwordStrength = getPasswordStrength(); + + const handleSendCode = async () => { + if (!formData.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { + setErrors(prev => ({ ...prev, email: '请输入有效的邮箱地址' })); + return; + } + + setIsSendingCode(true); + try { + const response = await fetch('/api/v1/auth/send-code', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email: formData.email, + type: 'register' + }), + }); + + const data = await response.json(); + + if (data.code === 200) { + setCodeTimer(60); + errorManager.showSuccess('验证码已发送到您的邮箱'); + } else { + setErrors(prev => ({ ...prev, email: data.message || '发送验证码失败' })); + errorManager.showError(data.message || '发送验证码失败'); + } + } catch (error) { + setErrors(prev => ({ ...prev, email: '发送验证码失败,请稍后重试' })); + errorManager.showError('发送验证码失败,请稍后重试'); + } finally { + setIsSendingCode(false); + } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (isLoginMode) { + if (!validateLoginForm()) return; + + setIsLoading(true); + setAuthError(''); + + try { + await login(formData.username, formData.password); + + if (formData.rememberMe) { + localStorage.setItem('rememberMe', 'true'); + } else { + localStorage.removeItem('rememberMe'); + } + + errorManager.showSuccess('登录成功!'); + router.push('/'); + + } catch (error) { + const errorMessage = error instanceof Error ? error.message : '登录失败,请检查用户名和密码'; + setAuthError(errorMessage); + errorManager.showError(errorMessage); + } finally { + setIsLoading(false); + } + } else { + if (!validateRegisterForm()) return; + + setIsLoading(true); + setAuthError(''); + + try { + await register(formData.username, formData.email, formData.password, formData.verificationCode); + errorManager.showSuccess('注册成功!欢迎加入CarrotSkin!'); + router.push('/'); + + } catch (error) { + const errorMessage = error instanceof Error ? error.message : '注册失败,请稍后重试'; + setAuthError(errorMessage); + errorManager.showError(errorMessage); + } finally { + setIsLoading(false); + } + } + }; + + const switchMode = () => { + setIsLoginMode(!isLoginMode); + setAuthError(''); + setErrors({}); + setFormData({ + username: '', + email: '', + password: '', + confirmPassword: '', + verificationCode: '', + rememberMe: false, + agreeToTerms: false + }); + }; + + return ( +
+ {/* Left Side - Orange Section */} +
+ + + CS + + + + {isLoginMode ? '欢迎回来' : '加入我们'} + + + + {isLoginMode ? '继续你的创作之旅' : '开始你的创作之旅'} + + + +
+
+ + + +
+ 创建独特的皮肤设计 +
+
+
+ + + +
+ 与社区分享作品 +
+
+
+ + + +
+ 发现精彩创意 +
+
+ + +

+ 已有超过 10,000 位创作者加入我们 +

+
+
+
+ + {/* Right Side - White Section */} +
+ + {/* Back to Home Button */} + + + + + + 返回主页 + + + + {/* Header */} + +

+ {isLoginMode ? '登录账户' : '创建账户'} +

+

+ {isLoginMode ? '登录您的CarrotSkin账户' : '加入我们,开始创作'} +

+
+ + {authError && ( + +

{authError}

+
+ )} + +
+ {/* Username */} + + +
+ + {!isLoginMode && formData.username && !errors.username && ( + + )} + {errors.username && ( + + )} +
+ {errors.username && ( +

{errors.username}

+ )} +
+ + {/* Email - Only for registration */} + + {!isLoginMode && ( + + +
+ + {formData.email && !errors.email && ( + + )} + {errors.email && ( + + )} +
+ {errors.email && ( +

{errors.email}

+ )} +
+ )} +
+ + {/* Password */} + + +
+ + +
+ {!isLoginMode && formData.password && ( +
+
+ 密码强度 + + {passwordStrength.label} + +
+
+
+
+
+ )} + {errors.password && ( +

{errors.password}

+ )} + + + {/* Confirm Password - Only for registration */} + + {!isLoginMode && ( + + +
+ + +
+ {formData.confirmPassword && formData.password === formData.confirmPassword && !errors.confirmPassword && ( + + )} + {errors.confirmPassword && ( +

{errors.confirmPassword}

+ )} +
+ )} +
+ + {/* Verification Code - Only for registration */} + + {!isLoginMode && ( + + +
+
+ + {errors.verificationCode && ( +

{errors.verificationCode}

+ )} +
+ +
+
+ )} +
+ + {/* Remember Me - Only for login */} + + {isLoginMode && ( + + + + 忘记密码? + + + )} + + + {/* Terms Agreement - Only for registration */} + + {!isLoginMode && ( + + + {errors.agreeToTerms && ( +

{errors.agreeToTerms}

+ )} +
+ )} +
+ + {/* Submit Button */} + + + {isLoading ? ( +
+
+ {isLoginMode ? '登录中...' : '注册中...'} +
+ ) : ( + isLoginMode ? '登录' : '创建账户' + )} + + + + + {/* Social Login */} + +
+
+
+
+
+ + 或者 + +
+
+ +
+ + + + + GitHub + + + + + + + + + + Microsoft + +
+ + + {/* Mode Switch */} + +

+ {isLoginMode ? '还没有账户?' : '已有账户?'} + +

+
+ +
+
+ ); +} diff --git a/src/app/globals.css b/src/app/globals.css index a2dc41e..12290ab 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,15 +1,13 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); @import "tailwindcss"; +@tailwind base; +@tailwind components; +@tailwind utilities; :root { --background: #ffffff; --foreground: #171717; -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --navbar-height: 64px; /* 与pt-16对应 */ } @media (prefers-color-scheme: dark) { @@ -20,7 +18,90 @@ } body { - background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + background: var(--background); + font-family: 'Inter', Arial, Helvetica, sans-serif; +} + +/* Custom utility classes */ +.text-balance { + text-wrap: balance; +} + +/* Custom component classes */ +.btn-carrot { + background-color: #f97316; + color: white; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: background-color 0.2s; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); +} + +.btn-carrot:hover { + background-color: #ea580c; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); +} + +.btn-carrot-outline { + border: 2px solid #f97316; + color: #f97316; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: all 0.2s; +} + +.btn-carrot-outline:hover { + background-color: #f97316; + color: white; +} + +.card-minecraft { + background-color: white; + border: 2px solid #fed7aa; + border-radius: 0.5rem; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); + transition: all 0.2s; +} + +.card-minecraft:hover { + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); +} + +@media (prefers-color-scheme: dark) { + .card-minecraft { + background-color: #1f2937; + border-color: #c2410c; + } +} + +.text-gradient { + background: linear-gradient(to right, #fb923c, #f97316); + background-clip: text; + -webkit-background-clip: text; + color: transparent; +} + +.bg-gradient-carrot { + background: linear-gradient(to bottom right, #fb923c, #f97316, #ea580c); +} + +/* 现代布局解决方案 */ +@layer utilities { + /* 全屏减去navbar高度 */ + .h-screen-nav { + height: calc(100vh - var(--navbar-height)); + } + + /* 侧栏最大高度,确保底部按钮可见 */ + .sidebar-max-height { + max-height: calc(100vh - var(--navbar-height) - 120px); + } + + /* 首页hero section专用高度 */ + .min-h-screen-nav { + min-height: calc(100vh - var(--navbar-height)); + } } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f7fa87e..41af848 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,20 +1,28 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Inter } from "next/font/google"; import "./globals.css"; +import Navbar from "@/components/Navbar"; +import { AuthProvider } from "@/contexts/AuthContext"; +import { MainContent } from "@/components/MainContent"; +import { ErrorNotificationContainer } from "@/components/ErrorNotification"; +import ScrollToTop from "@/components/ScrollToTop"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", +const inter = Inter({ subsets: ["latin"], + weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'], + display: 'swap', }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "CarrotSkin - 现代化Minecraft Yggdrasil皮肤站", + description: "新一代Minecraft Yggdrasil皮肤站,为创作者打造的现代化皮肤管理平台", + keywords: "Minecraft, 皮肤站, Yggdrasil, CarrotSkin, 我的世界, 皮肤管理", + authors: [{ name: "CarrotSkin Team" }], + openGraph: { + title: "CarrotSkin - 现代化Minecraft Yggdrasil皮肤站", + description: "新一代Minecraft Yggdrasil皮肤站,为创作者打造的现代化皮肤管理平台", + type: "website", + }, }; export default function RootLayout({ @@ -23,11 +31,14 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - - {children} + + + + + {children} + + + ); diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx new file mode 100644 index 0000000..fff1627 --- /dev/null +++ b/src/app/not-found.tsx @@ -0,0 +1,108 @@ +'use client'; + +import Link from 'next/link'; +import { motion } from 'framer-motion'; +import { HomeIcon, ArrowLeftIcon } from '@heroicons/react/24/outline'; + +export default function NotFound() { + return ( +
+ + {/* 404 数字 */} + +

+ 404 +

+
+ + + {/* 错误信息 */} + +

+ 页面不见了 +

+

+ 抱歉,我们找不到您要访问的页面。它可能已被移动、删除,或者您输入的链接不正确。 +

+
+ + {/* Minecraft 风格的装饰 */} + +
+
+ ? +
+
+ ! +
+
+
+ + {/* 操作按钮 */} + + + + 返回主页 + + + + + + {/* 额外的帮助信息 */} + +

如果问题持续存在,请 + + 联系我们 + + 的支持团队 +

+
+ + + {/* 背景装饰 */} +
+
+
+
+
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 295f8fd..2a10a68 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,65 +1,304 @@ -import Image from "next/image"; +'use client'; + +import { useState, useEffect } from 'react'; +import Link from 'next/link'; +import { motion, useScroll, useTransform } from 'framer-motion'; +import { + ArrowRightIcon, + ShieldCheckIcon, + CloudArrowUpIcon, + ShareIcon, + CubeIcon, + UserGroupIcon, + SparklesIcon, + RocketLaunchIcon +} from '@heroicons/react/24/outline'; export default function Home() { + const { scrollYProgress } = useScroll(); + const opacity = useTransform(scrollYProgress, [0, 0.3], [1, 0]); + const scale = useTransform(scrollYProgress, [0, 0.3], [1, 0.8]); + + const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); + const [isHovered, setIsHovered] = useState(false); + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + setMousePosition({ x: e.clientX, y: e.clientY }); + }; + window.addEventListener('mousemove', handleMouseMove); + return () => window.removeEventListener('mousemove', handleMouseMove); + }, []); + + const features = [ + { + icon: ShieldCheckIcon, + title: "Yggdrasil认证", + description: "完整的Minecraft Yggdrasil API支持,安全可靠的用户认证系统", + color: "from-amber-400 to-orange-500" + }, + { + icon: CloudArrowUpIcon, + title: "云端存储", + description: "无限皮肤存储空间,自动备份,随时随地访问你的皮肤库", + color: "from-orange-400 to-red-500" + }, + { + icon: ShareIcon, + title: "社区分享", + description: "与全球玩家分享创作,发现灵感,建立你的粉丝群体", + color: "from-red-400 to-pink-500" + }, + { + icon: CubeIcon, + title: "3D预览", + description: "实时3D皮肤预览,360度旋转查看,支持多种渲染模式", + color: "from-pink-400 to-purple-500" + } + ]; + + const stats = [ + { number: "50K+", label: "注册用户" }, + { number: "200K+", label: "皮肤上传" }, + { number: "1M+", label: "月活用户" }, + { number: "99.9%", label: "服务可用性" } + ]; + return ( -
-
- Next.js logo + {/* Animated Background */} +
+
+
+
-
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
+ + + {/* Features Section */} +
+
+ +

+ 为创作者而生的 + 强大功能 +

+

+ 从上传到分享,从管理到展示,每一个细节都为提升你的创作体验而设计 +

+
+ +
+ {features.map((feature, index) => ( + +
+
+ +
+

+ {feature.title} +

+

+ {feature.description} +

+
+
+ ))} +
+
+
+ + {/* CTA Section */} +
+
+
+
+ + + + + + +

+ 准备开启创作之旅? +

+

+ 加入CarrotSkin,体验新一代Minecraft皮肤管理平台,让你的创意无限绽放 +

+ +
+ + 免费注册 + + + + + 查看API文档 + + +
+
+
); } diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx new file mode 100644 index 0000000..6ae314f --- /dev/null +++ b/src/app/profile/page.tsx @@ -0,0 +1,1359 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { + UserCircleIcon, + PencilIcon, + TrashIcon, + PlusIcon, + EyeIcon, + ArrowDownTrayIcon, + Cog6ToothIcon, + UserIcon, + PhotoIcon, + KeyIcon, + EnvelopeIcon, + + HeartIcon, + ArrowLeftOnRectangleIcon, + CloudArrowUpIcon, + XMarkIcon, + ArrowPathIcon, + XCircleIcon +} from '@heroicons/react/24/outline'; +import { useAuth } from '@/contexts/AuthContext'; +import { + getMyTextures, + getFavoriteTextures, + toggleFavorite, + getProfiles, + createProfile, + updateProfile, + deleteProfile, + setActiveProfile, + getUserProfile, + updateUserProfile, + uploadTexture, + type Texture, + type Profile +} from '@/lib/api'; + +interface UserProfile { + id: number; + username: string; + email: string; + avatar?: string; + points: number; + role: string; + status: number; + last_login_at?: string; + created_at: string; + updated_at: string; +} + +export default function ProfilePage() { + const [activeTab, setActiveTab] = useState<'characters' | 'skins' | 'favorites' | 'settings'>('characters'); + const [profiles, setProfiles] = useState([]); + const [mySkins, setMySkins] = useState([]); + const [favoriteSkins, setFavoriteSkins] = useState([]); + const [userProfile, setUserProfile] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isUploading, setIsUploading] = useState(false); + const [showCreateCharacter, setShowCreateCharacter] = useState(false); + const [showUploadSkin, setShowUploadSkin] = useState(false); + const [newCharacterName, setNewCharacterName] = useState(''); + const [newSkinData, setNewSkinData] = useState({ + name: '', + description: '', + type: 'SKIN' as 'SKIN' | 'CAPE', + is_public: false, + is_slim: false + }); + const [selectedFile, setSelectedFile] = useState(null); + const [editingProfile, setEditingProfile] = useState(null); + const [editProfileName, setEditProfileName] = useState(''); + const [uploadProgress, setUploadProgress] = useState(0); + const [avatarFile, setAvatarFile] = useState(null); + const [isUploadingAvatar, setIsUploadingAvatar] = useState(false); + const [avatarUploadProgress, setAvatarUploadProgress] = useState(0); + const [error, setError] = useState(null); + + const { user, isAuthenticated, logout } = useAuth(); + + // 加载用户数据 + useEffect(() => { + if (isAuthenticated) { + loadUserData(); + } + }, [isAuthenticated]); + + const loadUserData = async () => { + setIsLoading(true); + setError(null); + try { + // 加载用户信息 + const userResponse = await getUserProfile(); + if (userResponse.code === 200) { + setUserProfile(userResponse.data); + } else { + throw new Error(userResponse.message || '获取用户信息失败'); + } + + // 加载用户档案 + const profilesResponse = await getProfiles(); + if (profilesResponse.code === 200) { + setProfiles(profilesResponse.data); + } else { + throw new Error(profilesResponse.message || '获取角色列表失败'); + } + + // 加载用户皮肤 + const mySkinsResponse = await getMyTextures({ page: 1, page_size: 50 }); + if (mySkinsResponse.code === 200) { + setMySkins(mySkinsResponse.data.list || []); + } else { + throw new Error(mySkinsResponse.message || '获取皮肤列表失败'); + } + + // 加载收藏的皮肤 + const favoritesResponse = await getFavoriteTextures({ page: 1, page_size: 50 }); + if (favoritesResponse.code === 200) { + setFavoriteSkins(favoritesResponse.data.list || []); + } else { + throw new Error(favoritesResponse.message || '获取收藏列表失败'); + } + } catch (error) { + console.error('加载用户数据失败:', error); + setError(error instanceof Error ? error.message : '加载数据失败,请稍后重试'); + } finally { + setIsLoading(false); + } + }; + + const handleCreateCharacter = async () => { + if (!newCharacterName.trim()) { + alert('请输入角色名称'); + return; + } + + try { + const response = await createProfile(newCharacterName.trim()); + if (response.code === 200) { + setProfiles(prev => [...prev, response.data]); + setNewCharacterName(''); + setShowCreateCharacter(false); + alert('角色创建成功!'); + } else { + throw new Error(response.message || '创建角色失败'); + } + } catch (error) { + console.error('创建角色失败:', error); + alert(error instanceof Error ? error.message : '创建角色失败,请稍后重试'); + } + }; + + const handleDeleteCharacter = async (uuid: string) => { + const character = profiles.find(p => p.uuid === uuid); + if (!character) return; + + if (!confirm(`确定要删除角色 "${character.name}" 吗?此操作不可恢复。`)) return; + + try { + const response = await deleteProfile(uuid); + if (response.code === 200) { + setProfiles(prev => prev.filter(profile => profile.uuid !== uuid)); + alert('角色删除成功!'); + } else { + throw new Error(response.message || '删除角色失败'); + } + } catch (error) { + console.error('删除角色失败:', error); + alert(error instanceof Error ? error.message : '删除角色失败,请稍后重试'); + } + }; + + const handleSetActiveCharacter = async (uuid: string) => { + try { + const response = await setActiveProfile(uuid); + if (response.code === 200) { + setProfiles(prev => prev.map(profile => ({ + ...profile, + is_active: profile.uuid === uuid + }))); + alert('角色切换成功!'); + } else { + throw new Error(response.message || '设置活跃角色失败'); + } + } catch (error) { + console.error('设置活跃角色失败:', error); + alert(error instanceof Error ? error.message : '设置活跃角色失败,请稍后重试'); + } + }; + + const handleEditCharacter = async (uuid: string) => { + if (!editProfileName.trim()) { + alert('请输入角色名称'); + return; + } + + try { + const response = await updateProfile(uuid, { name: editProfileName.trim() }); + if (response.code === 200) { + setProfiles(prev => prev.map(profile => + profile.uuid === uuid ? response.data : profile + )); + setEditingProfile(null); + setEditProfileName(''); + alert('角色编辑成功!'); + } else { + throw new Error(response.message || '编辑角色失败'); + } + } catch (error) { + console.error('编辑角色失败:', error); + alert(error instanceof Error ? error.message : '编辑角色失败,请稍后重试'); + } + }; + + const handleDeleteSkin = async (skinId: number) => { + if (!confirm('确定要删除这个皮肤吗?')) return; + + setMySkins(prev => prev.filter(skin => skin.id !== skinId)); + }; + + const handleToggleSkinVisibility = async (skinId: number) => { + try { + const skin = mySkins.find(s => s.id === skinId); + if (!skin) return; + + // TODO: 添加更新皮肤API调用 + setMySkins(prev => prev.map(skin => + skin.id === skinId ? { ...skin, is_public: !skin.is_public } : skin + )); + } catch (error) { + console.error('切换皮肤可见性失败:', error); + } + }; + + const handleToggleFavorite = async (skinId: number) => { + try { + const response = await toggleFavorite(skinId); + if (response.code === 200) { + // 重新加载收藏数据 + const favoritesResponse = await getFavoriteTextures({ page: 1, page_size: 50 }); + if (favoritesResponse.code === 200) { + setFavoriteSkins(favoritesResponse.data.list || []); + } + } + } catch (error) { + console.error('切换收藏状态失败:', error); + } + }; + + const handleFileSelect = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + setSelectedFile(file); + } + }; + + const handleUploadSkin = async () => { + if (!selectedFile || !newSkinData.name.trim()) { + alert('请选择皮肤文件并输入皮肤名称'); + return; + } + + setIsUploading(true); + setUploadProgress(0); + + try { + // 使用直接上传接口 + const progressInterval = setInterval(() => { + setUploadProgress(prev => Math.min(prev + 10, 80)); + }, 200); + + const response = await uploadTexture(selectedFile, { + name: newSkinData.name.trim(), + description: newSkinData.description.trim(), + type: newSkinData.type, + is_public: newSkinData.is_public, + is_slim: newSkinData.is_slim + }); + + clearInterval(progressInterval); + setUploadProgress(100); + + if (response.code === 200) { + // 重新加载皮肤数据 + const mySkinsResponse = await getMyTextures({ page: 1, page_size: 50 }); + if (mySkinsResponse.code === 200) { + setMySkins(mySkinsResponse.data.list || []); + } + + // 重置表单 + setSelectedFile(null); + setNewSkinData({ + name: '', + description: '', + type: 'SKIN', + is_public: false, + is_slim: false + }); + setShowUploadSkin(false); + alert('皮肤上传成功!'); + } else { + throw new Error(response.message || '上传皮肤失败'); + } + + } catch (error) { + console.error('上传皮肤失败:', error); + alert(error instanceof Error ? error.message : '上传皮肤失败,请稍后重试'); + } finally { + setIsUploading(false); + setUploadProgress(0); + } + }; + + const handleAvatarFileSelect = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + // 验证文件类型和大小 + if (!file.type.startsWith('image/')) { + alert('请选择图片文件'); + return; + } + if (file.size > 2 * 1024 * 1024) { + alert('文件大小不能超过2MB'); + return; + } + setAvatarFile(file); + } + }; + + const handleUploadAvatar = async () => { + if (!avatarFile) return; + + setIsUploadingAvatar(true); + setAvatarUploadProgress(0); + + try { + // 获取上传URL + const uploadUrlResponse = await generateAvatarUploadUrl(avatarFile.name); + if (uploadUrlResponse.code !== 200) { + throw new Error(uploadUrlResponse.message || '获取上传URL失败'); + } + + const { post_url, form_data, avatar_url } = uploadUrlResponse.data; + + // 模拟上传进度 + const progressInterval = setInterval(() => { + setAvatarUploadProgress(prev => Math.min(prev + 20, 80)); + }, 200); + + // 上传文件到预签名URL + const formData = new FormData(); + Object.entries(form_data).forEach(([key, value]) => { + formData.append(key, value as string); + }); + formData.append('file', avatarFile); + + const uploadResponse = await fetch(post_url, { + method: 'POST', + body: formData, + }); + + if (!uploadResponse.ok) { + throw new Error('文件上传失败'); + } + + clearInterval(progressInterval); + setAvatarUploadProgress(100); + + // 更新用户头像URL + const updateResponse = await updateAvatarUrl(avatar_url); + if (updateResponse.code === 200) { + setUserProfile(prev => prev ? { ...prev, avatar: avatar_url } : null); + alert('头像上传成功!'); + } else { + throw new Error(updateResponse.message || '更新头像URL失败'); + } + + } catch (error) { + console.error('头像上传失败:', error); + alert(error instanceof Error ? error.message : '头像上传失败,请稍后重试'); + } finally { + setIsUploadingAvatar(false); + setAvatarUploadProgress(0); + setAvatarFile(null); + } + }; + + const handleDeleteAvatar = async () => { + if (!confirm('确定要删除头像吗?')) return; + + try { + const response = await updateAvatarUrl(''); + if (response.code === 200) { + setUserProfile(prev => prev ? { ...prev, avatar: undefined } : null); + alert('头像删除成功!'); + } else { + throw new Error(response.message || '删除头像失败'); + } + } catch (error) { + console.error('删除头像失败:', error); + alert(error instanceof Error ? error.message : '删除头像失败,请稍后重试'); + } + }; + + const sidebarVariants = { + hidden: { x: -100, opacity: 0 }, + visible: { x: 0, opacity: 1, transition: { duration: 0.5, ease: "easeOut" as const } } + }; + + const contentVariants = { + hidden: { x: 100, opacity: 0 }, + visible: { x: 0, opacity: 1, transition: { duration: 0.5, ease: "easeOut" as const, delay: 0.1 } } + }; + + if (!isAuthenticated) { + return ( +
+
+ +
+ +
+
+

请先登录

+

登录后即可查看个人资料

+
+
+ ); + } + + if (isLoading) { + return ( +
+
+ +
+ +
+
+

加载中...

+

正在加载您的个人资料

+
+
+ ); + } + + if (error) { + return ( +
+
+ +
+ +
+
+

加载失败

+

{error}

+ + + 重新加载 + +
+
+ ); + } + + return ( +
+ {/* Animated Background */} +
+
+
+
+ +
+
+ {/* Left Sidebar - 完全固定的 */} + + {/* User Profile Card */} +
+
+ {userProfile?.avatar ? ( + {userProfile.username} + ) : ( +
+ +
+ )} +
+

{userProfile?.username}

+

{userProfile?.email}

+
+
+ +
+
+
{mySkins.length}
+
皮肤
+
+
+
{favoriteSkins.length}
+
收藏
+
+
+
{userProfile?.points || 0}
+
积分
+
+
+
+ + {/* Navigation Menu - 固定的 */} + + + {/* Logout Button - 始终在底部可见 */} + + + 退出登录 + +
+ + {/* Right Content Area - 考虑左侧fixed侧栏的空间 */} + + + {/* Characters Tab */} + {activeTab === 'characters' && ( + +
+

角色管理

+ setShowCreateCharacter(true)} + className="bg-gradient-to-r from-orange-500 to-amber-500 text-white px-4 py-2 rounded-xl flex items-center space-x-2 shadow-lg hover:shadow-xl transition-all duration-200" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + + 创建角色 + +
+ + {/* Create Character Modal */} + + {showCreateCharacter && ( + + +
+

创建新角色

+ +
+
+
+ + setNewCharacterName(e.target.value)} + className="w-full px-4 py-3 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-xl focus:ring-2 focus:ring-orange-500 focus:border-transparent" + placeholder="请输入角色名称" + /> +
+
+ + +
+
+
+
+ )} +
+ + {profiles.length === 0 ? ( +
+ +

暂无角色

+

创建你的第一个Minecraft角色吧!

+
+ ) : ( +
+ {profiles.map((profile) => ( + +
+ {editingProfile === profile.uuid ? ( + setEditProfileName(e.target.value)} + className="text-lg font-semibold bg-transparent border-b border-orange-500 focus:outline-none text-gray-900 dark:text-white" + onBlur={() => handleEditCharacter(profile.uuid)} + onKeyPress={(e) => e.key === 'Enter' && handleEditCharacter(profile.uuid)} + autoFocus + /> + ) : ( +

{profile.name}

+ )} + {profile.is_active && ( + + 当前使用 + + )} +
+ +
+
+
+ +
+ {!profile.is_active && ( + + )} + + +
+
+ ))} +
+ )} +
+ )} + + {/* Skins Tab */} + {activeTab === 'skins' && ( + +
+

我的皮肤

+ setShowUploadSkin(true)} + className="bg-gradient-to-r from-orange-500 to-amber-500 text-white px-4 py-2 rounded-xl flex items-center space-x-2 shadow-lg hover:shadow-xl transition-all duration-200" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + + 上传皮肤 + +
+ + {/* Upload Skin Modal */} + + {showUploadSkin && ( + + +
+

上传皮肤

+ +
+
+
+ +
+ +

+ 点击选择文件或拖拽到此处 +

+ + + {selectedFile && ( +

+ 已选择: {selectedFile.name} +

+ )} +
+
+ +
+ + setNewSkinData(prev => ({ ...prev, name: e.target.value }))} + className="w-full px-4 py-3 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-xl focus:ring-2 focus:ring-orange-500 focus:border-transparent" + placeholder="请输入皮肤名称" + /> +
+ +
+ +