From 3f5e0caef82f5343936acc6afb9e9b7a8ee608a5 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 17 Sep 2025 09:42:35 +0800 Subject: [PATCH] Add user and team handler attachments to OpenAPI server - Integrated user and team handler functions into the OpenAPI server's routing structure. - Established new routes for user and team management under the OpenAPI framework, enhancing the server's capabilities for handling user-related operations. --- openapi/openapi.go | 9 ++ openapi/team/team.go | 17 ++++ openapi/user/README.md | 172 +++++++++++++++++++++++++++++++ openapi/user/user.go | 223 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 421 insertions(+) create mode 100644 openapi/team/team.go create mode 100644 openapi/user/README.md create mode 100644 openapi/user/user.go diff --git a/openapi/openapi.go b/openapi/openapi.go index 3cd71d16..dcf79e13 100644 --- a/openapi/openapi.go +++ b/openapi/openapi.go @@ -16,6 +16,8 @@ import ( "github.com/yaoapp/yao/openapi/oauth" "github.com/yaoapp/yao/openapi/oauth/types" "github.com/yaoapp/yao/openapi/signin" + "github.com/yaoapp/yao/openapi/team" + "github.com/yaoapp/yao/openapi/user" ) // Server is the OpenAPI server @@ -108,5 +110,12 @@ func (openapi *OpenAPI) Attach(router *gin.Engine) { // Captcha handlers captcha.Attach(group.Group("/captcha"), openapi.OAuth) + // User handlers + user.Attach(group.Group("/user"), openapi.OAuth) + + // Team handlers + team.Attach(group.Group("/team"), openapi.OAuth) + // Custom handlers (Defined by developer) + } diff --git a/openapi/team/team.go b/openapi/team/team.go new file mode 100644 index 00000000..a30233a2 --- /dev/null +++ b/openapi/team/team.go @@ -0,0 +1,17 @@ +package team + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/yaoapp/yao/openapi/oauth/types" +) + +// Attach attaches the signin handlers to the router +func Attach(group *gin.RouterGroup, oauth types.OAuth) { + +} + +func placeholder(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"message": "Hello, World!"}) +} diff --git a/openapi/user/README.md b/openapi/user/README.md new file mode 100644 index 00000000..c1f1632f --- /dev/null +++ b/openapi/user/README.md @@ -0,0 +1,172 @@ +# User API Module + +This module provides comprehensive user management APIs including authentication, profile management, security settings, and third-party integrations. + +## API Endpoints + +### Authentication + +| Method | Endpoint | Auth | Description | +| ------ | ---------------- | -------- | ---------------------------- | +| GET | `/user/login` | Public | Get login page configuration | +| POST | `/user/login` | Public | User login | +| POST | `/user/register` | Public | User registration | +| POST | `/user/logout` | Required | User logout | + +### Profile Management + +| Method | Endpoint | Auth | Description | +| ------ | --------------- | -------- | ------------------- | +| GET | `/user/profile` | Required | Get user profile | +| PUT | `/user/profile` | Required | Update user profile | + +### Account Security + +| Method | Endpoint | Auth | Description | +| ------ | ---------------------------------------- | -------- | -------------------------------------------------- | +| PUT | `/user/account/password` | Required | Change password (requires current password or 2FA) | +| POST | `/user/account/password/reset/request` | Public | Request password reset (rate-limited) | +| POST | `/user/account/password/reset/verify` | Public | Verify reset token and set new password | +| GET | `/user/account/email` | Required | Get current email info | +| POST | `/user/account/email/change/request` | Required | Request email change (sends code to current email) | +| POST | `/user/account/email/change/verify` | Required | Verify email change with code | +| POST | `/user/account/email/verification-code` | Required | Send verification code to current email | +| POST | `/user/account/email/verify` | Required | Verify current email | +| GET | `/user/account/mobile` | Required | Get current mobile info | +| POST | `/user/account/mobile/change/request` | Required | Request mobile change | +| POST | `/user/account/mobile/change/verify` | Required | Verify mobile change with code | +| POST | `/user/account/mobile/verification-code` | Required | Send verification code to mobile | +| POST | `/user/account/mobile/verify` | Required | Verify current mobile | + +### Multi-Factor Authentication (MFA) + +| Method | Endpoint | Auth | Description | +| ------ | ------------------------------------------ | -------- | ---------------------------------------- | +| GET | `/user/2fa/totp` | Required | Get TOTP QR code and setup info | +| POST | `/user/2fa/totp/enable` | Required | Enable TOTP with verification | +| POST | `/user/2fa/totp/disable` | Required | Disable TOTP with verification | +| POST | `/user/2fa/totp/verify` | Required | Verify TOTP code | +| GET | `/user/2fa/totp/recovery-codes` | Required | Get TOTP recovery codes | +| POST | `/user/2fa/totp/recovery-codes/regenerate` | Required | Regenerate recovery codes | +| POST | `/user/2fa/totp/reset` | Required | Reset TOTP (requires email verification) | +| GET | `/user/2fa/sms` | Required | Get SMS MFA status | +| POST | `/user/2fa/sms/enable` | Required | Enable SMS MFA | +| POST | `/user/2fa/sms/disable` | Required | Disable SMS MFA | +| POST | `/user/2fa/sms/verification-code` | Required | Send SMS verification code | +| POST | `/user/2fa/sms/verify` | Required | Verify SMS code | + +### OAuth & Third-Party Integration + +| Method | Endpoint | Auth | Description | +| ------ | ----------------------------------------- | -------- | ------------------------------------ | +| GET | `/user/oauth/providers` | Required | Get linked OAuth providers | +| DELETE | `/user/oauth/:provider` | Required | Unlink OAuth provider | +| GET | `/user/oauth/providers/available` | Public | Get available OAuth providers | +| GET | `/user/oauth/:provider/authorize` | Public | Get OAuth authorization URL | +| POST | `/user/oauth/:provider/connect` | Required | Connect OAuth provider | +| POST | `/user/oauth/:provider/authorize/prepare` | Public | Handle POST callback (Apple, WeChat) | +| POST | `/user/oauth/:provider/callback` | Public | Handle GET callback (Google, GitHub) | + +### API Keys Management + +| Method | Endpoint | Auth | Description | +| ------ | ----------------------------------- | -------- | ---------------------------------- | +| GET | `/user/api-keys` | Required | Get all user API keys | +| POST | `/user/api-keys` | Required | Create new API key | +| GET | `/user/api-keys/:key_id` | Required | Get specific API key details | +| PUT | `/user/api-keys/:key_id` | Required | Update API key (name, permissions) | +| DELETE | `/user/api-keys/:key_id` | Required | Delete API key | +| POST | `/user/api-keys/:key_id/regenerate` | Required | Regenerate API key | + +### Balance & Credits + +| Method | Endpoint | Auth | Description | +| ------ | ------------------------------- | -------- | -------------------------- | +| GET | `/user/balance` | Required | Get user balance info | +| GET | `/user/balance/history` | Required | Get balance change history | +| GET | `/user/balance/topup` | Required | Get topup records | +| POST | `/user/balance/topup` | Required | Create topup order | +| GET | `/user/balance/topup/:order_id` | Required | Get topup order status | +| POST | `/user/balance/topup/card-code` | Required | Redeem card code | + +### Subscription Management + +| Method | Endpoint | Auth | Description | +| ------ | -------------------- | -------- | ------------------------ | +| GET | `/user/subscription` | Required | Get user subscription | +| PUT | `/user/subscription` | Required | Update user subscription | + +### Usage Statistics + +| Method | Endpoint | Auth | Description | +| ------ | ------------------------ | -------- | ------------------------- | +| GET | `/user/usage/statistics` | Required | Get user usage statistics | +| GET | `/user/usage/history` | Required | Get user usage history | + +### Billing & Invoices + +| Method | Endpoint | Auth | Description | +| ------ | ----------------------- | -------- | --------------------------- | +| PUT | `/user/billing/history` | Required | Update user billing history | + +### Referral & Invitations + +| Method | Endpoint | Auth | Description | +| ------ | -------------------------- | -------- | --------------------------- | +| GET | `/user/invite/code` | Required | Get user invite code | +| GET | `/user/invite/statistics` | Required | Get user invite statistics | +| GET | `/user/invite/history` | Required | Get user invite history | +| GET | `/user/invite/commissions` | Required | Get user invite commissions | + +### Team Management + +| Method | Endpoint | Auth | Description | +| ------ | ----------------------------------------- | -------- | ---------------------------- | +| GET | `/user/teams` | Required | Get user teams | +| POST | `/user/teams` | Required | Create user team | +| GET | `/user/teams/:team_id` | Required | Get user team details | +| PUT | `/user/teams/:team_id` | Required | Update user team | +| DELETE | `/user/teams/:team_id` | Required | Delete user team | +| GET | `/user/teams/:team_id/members` | Required | Get user team members | +| GET | `/user/teams/:team_id/members/:member_id` | Required | Get user team member details | +| POST | `/user/teams/:team_id/members/:type` | Required | Create user team member | +| PUT | `/user/teams/:team_id/members/:member_id` | Required | Update user team member | +| DELETE | `/user/teams/:team_id/members/:member_id` | Required | Remove user team member | + +### User Preferences + +| Method | Endpoint | Auth | Description | +| ------ | -------------------------- | -------- | --------------------------- | +| GET | `/user/preferences` | Required | Get user preferences | +| GET | `/user/preferences/schema` | Required | Get user preferences schema | +| PUT | `/user/preferences` | Required | Update user preferences | + +### Privacy Settings + +| Method | Endpoint | Auth | Description | +| ------ | ---------------------- | -------- | ---------------------------- | +| GET | `/user/privacy` | Required | Get user privacy settings | +| GET | `/user/privacy/schema` | Required | Get user privacy schema | +| PUT | `/user/privacy` | Required | Update user privacy settings | + +### User Management (Admin) + +| Method | Endpoint | Auth | Description | +| ------ | ---------------------- | -------- | ---------------- | +| GET | `/user/users` | Required | Get users | +| POST | `/user/users` | Required | Create user | +| GET | `/user/users/:user_id` | Required | Get user details | +| PUT | `/user/users/:user_id` | Required | Update user | +| DELETE | `/user/users/:user_id` | Required | Delete user | + +## Authentication + +- **Public**: No authentication required +- **Required**: Requires valid OAuth token via `oauth.Guard` middleware + +## Notes + +- All endpoints return JSON responses +- Rate limiting may apply to sensitive operations (password reset, verification codes) +- This module is designed to eventually replace the `signin` module +- OAuth callbacks support both GET (Google, GitHub) and POST (Apple, WeChat) methods diff --git a/openapi/user/user.go b/openapi/user/user.go new file mode 100644 index 00000000..40c60d0f --- /dev/null +++ b/openapi/user/user.go @@ -0,0 +1,223 @@ +package user + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/yaoapp/yao/openapi/oauth/types" +) + +// Attach attaches the signin handlers to the router +func Attach(group *gin.RouterGroup, oauth types.OAuth) { + + // User Authentication + group.GET("/login", placeholder) // Get login page config (public) + group.POST("/login", placeholder) // User login (public) + group.POST("/register", placeholder) // User register (public) + group.POST("/logout", oauth.Guard, placeholder) // User logout + + // Logined User Settings + attachProfile(group, oauth) // User profile management + attachPreferences(group, oauth) // User preferences management + attachAccount(group, oauth) // Account settings + attachThirdParty(group, oauth) // Third party login + attachMFA(group, oauth) // MFA settings + attachBalance(group, oauth) // User balance management + attachSubscription(group, oauth) // User subscription management + attachAPIKeys(group, oauth) // User API keys management + attachUsage(group, oauth) // User usage management + attachBilling(group, oauth) // User billing management + attachInvite(group, oauth) // User invite management + attachTeam(group, oauth) // User team management + attachPrivacy(group, oauth) // User privacy management + + // User Management + attachUsers(group, oauth) +} + +// User Team Management +func attachTeam(group *gin.RouterGroup, oauth types.OAuth) { + team := group.Group("/teams") + team.Use(oauth.Guard) + team.GET("/", placeholder) // Get user teams + team.GET("/:team_id", placeholder) // Get user team details + team.POST("/", placeholder) // Create user team + team.PUT("/:team_id", placeholder) // Update user team + team.DELETE("/:team_id", placeholder) // Delete user team + + // Member Management + team.GET("/:team_id/members", placeholder) // Get user team members + team.GET("/:team_id/members/:member_id", placeholder) // Get user team member details + team.POST("/:team_id/members/:type", placeholder) // Create user team member + team.PUT("/:team_id/members/:member_id", placeholder) // Update user team member + team.DELETE("/:team_id/members/:member_id", placeholder) // Remove user team member +} + +// User Privacy +func attachPrivacy(group *gin.RouterGroup, oauth types.OAuth) { + privacy := group.Group("/privacy") + privacy.Use(oauth.Guard) + privacy.GET("/", placeholder) // Get user privacy + privacy.GET("/schema", placeholder) // Get user privacy schema + privacy.PUT("/", placeholder) // Update user privacy +} + +// User Preferences +func attachPreferences(group *gin.RouterGroup, oauth types.OAuth) { + preferences := group.Group("/preferences") + preferences.Use(oauth.Guard) + + preferences.GET("/", placeholder) // Get user preferences + preferences.GET("/schema", placeholder) // Get user preferences schema + preferences.PUT("/", placeholder) // Update user preferences +} + +// User Billing Management +func attachBilling(group *gin.RouterGroup, oauth types.OAuth) { + billing := group.Group("/billing") + billing.Use(oauth.Guard) + billing.PUT("/history", placeholder) // Update user billing history +} + +// Invite Management +func attachInvite(group *gin.RouterGroup, oauth types.OAuth) { + invite := group.Group("/invite") + invite.Use(oauth.Guard) + + invite.GET("/code", placeholder) // Get user invite code + invite.GET("/statistics", placeholder) // Get user invite statistics + invite.GET("/history", placeholder) // Get user invite history + invite.GET("/commissions", placeholder) // Get user invite commissions +} + +// User Balance Management +func attachBalance(group *gin.RouterGroup, oauth types.OAuth) { + balance := group.Group("/balance") + balance.Use(oauth.Guard) + + balance.GET("/", placeholder) // Get user balance info + balance.GET("/history", placeholder) // Get balance change history + + // Top-up Management + topup := balance.Group("/topup") + topup.GET("/", placeholder) // Get topup records + topup.POST("/", placeholder) // Create topup order + topup.GET("/:order_id", placeholder) // Get topup order status + topup.POST("/card-code", placeholder) // Redeem card code +} + +// Usage Management +func attachUsage(group *gin.RouterGroup, oauth types.OAuth) { + usage := group.Group("/usage") + usage.Use(oauth.Guard) + usage.GET("/statistics", placeholder) // Get user usage statistics + usage.GET("/history", placeholder) // Get user usage history +} + +// User API Keys Management +func attachAPIKeys(group *gin.RouterGroup, oauth types.OAuth) { + apiKeys := group.Group("/api-keys") + apiKeys.Use(oauth.Guard) + + apiKeys.GET("/", placeholder) // Get all user API keys + apiKeys.POST("/", placeholder) // Create new API key + apiKeys.GET("/:key_id", placeholder) // Get specific API key details + apiKeys.PUT("/:key_id", placeholder) // Update API key (name, permissions) + apiKeys.DELETE("/:key_id", placeholder) // Delete API key + apiKeys.POST("/:key_id/regenerate", placeholder) // Regenerate API key +} + +// User Subscription Management +func attachSubscription(group *gin.RouterGroup, oauth types.OAuth) { + subscription := group.Group("/subscription") + subscription.Use(oauth.Guard) + subscription.GET("/", placeholder) // Get user subscription + subscription.PUT("/", placeholder) // Update user subscription +} + +// User profile management +func attachProfile(group *gin.RouterGroup, oauth types.OAuth) { + profile := group.Group("/profile") + profile.Use(oauth.Guard) + + profile.GET("/", placeholder) // Get user profile + profile.PUT("/", placeholder) // Update user profile +} + +// User management (CRUD) +func attachUsers(group *gin.RouterGroup, oauth types.OAuth) { + users := group.Group("/users") + users.Use(oauth.Guard) + + users.GET("/", placeholder) // Get users + users.GET("/:user_id", placeholder) // Get user details + users.POST("/", placeholder) // Create user + users.PUT("/:user_id", placeholder) // Update user + users.DELETE("/:user_id", placeholder) // Delete user +} + +// Account settings +func attachAccount(group *gin.RouterGroup, oauth types.OAuth) { + account := group.Group("/account") + account.Use(oauth.Guard) + + // Password Management + account.PUT("/password", placeholder) // Change password (requires current password or 2FA) + account.POST("/password/reset/request", placeholder) // Request password reset (public, rate-limited) + account.POST("/password/reset/verify", placeholder) // Verify reset token and set new password (public) + + // Email Management + account.GET("/email", placeholder) // Get current email info + account.POST("/email/change/request", placeholder) // Request email change (sends code to current email) + account.POST("/email/change/verify", placeholder) // Verify email change with code + account.POST("/email/verification-code", placeholder) // Send verification code to current email + account.POST("/email/verify", placeholder) // Verify current email + + // Mobile Management + account.GET("/mobile", placeholder) // Get current mobile info + account.POST("/mobile/change/request", placeholder) // Request mobile change + account.POST("/mobile/change/verify", placeholder) // Verify mobile change with code + account.POST("/mobile/verification-code", placeholder) // Send verification code to mobile + account.POST("/mobile/verify", placeholder) // Verify current mobile +} + +// MFA settings +func attachMFA(group *gin.RouterGroup, oauth types.OAuth) { + mfa := group.Group("/2fa") + mfa.Use(oauth.Guard) + + // TOTP Management + mfa.GET("/totp", placeholder) // Get TOTP QR code and setup info + mfa.POST("/totp/enable", placeholder) // Enable TOTP with verification + mfa.POST("/totp/disable", placeholder) // Disable TOTP with verification + mfa.POST("/totp/verify", placeholder) // Verify TOTP code + mfa.GET("/totp/recovery-codes", placeholder) // Get TOTP recovery codes + mfa.POST("/totp/recovery-codes/regenerate", placeholder) // Regenerate recovery codes + mfa.POST("/totp/reset", placeholder) // Reset TOTP (requires email verification) + + // SMS MFA Management + mfa.GET("/sms", placeholder) // Get SMS MFA status + mfa.POST("/sms/enable", placeholder) // Enable SMS MFA + mfa.POST("/sms/disable", placeholder) // Disable SMS MFA + mfa.POST("/sms/verification-code", placeholder) // Send SMS verification code + mfa.POST("/sms/verify", placeholder) // Verify SMS code +} + +// Third party login (OAuth) +func attachThirdParty(group *gin.RouterGroup, oauth types.OAuth) { + + thirdParty := group.Group("/oauth") // OAuth + thirdParty.GET("/providers", oauth.Guard, placeholder) // Get linked OAuth providers + thirdParty.DELETE("/:provider", oauth.Guard, placeholder) // Unlink OAuth provider + + thirdParty.GET("/providers/available", placeholder) // Get available OAuth providers + thirdParty.GET("/:provider/authorize", placeholder) // Get OAuth authorization URL + thirdParty.POST("/:provider/connect", oauth.Guard, placeholder) // Connect OAuth provider + thirdParty.POST("/:provider/authorize/prepare", placeholder) // Get OAuth authorization URL + thirdParty.POST("/:provider/callback", placeholder) // Handle OAuth callback + +} + +func placeholder(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"message": "Hello, World!"}) +}