1- import { DataSource } from 'typeorm' ;
1+ import { DataSource , EntityManager } from 'typeorm' ;
22import { JwtService } from '@nestjs/jwt' ;
33import { ConfigService } from '@nestjs/config' ;
44import generateId from 'omnibox-backend/utils/generate-id' ;
55import { UserService } from 'omnibox-backend/user/user.service' ;
66import { NamespacesService } from 'omnibox-backend/namespaces/namespaces.service' ;
77import { WechatCheckResponseDto } from './dto/wechat-login.dto' ;
88import {
9+ BadRequestException ,
910 Injectable ,
11+ InternalServerErrorException ,
12+ Logger ,
1013 UnauthorizedException ,
11- BadRequestException ,
1214} from '@nestjs/common' ;
1315
1416@Injectable ( )
1517export class WechatService {
18+ private readonly logger = new Logger ( WechatService . name ) ;
19+
1620 private readonly appId : string ;
1721 private readonly appSecret : string ;
1822 private readonly redirectUri : string ;
@@ -28,6 +32,9 @@ export class WechatService {
2832 }
2933 > ( ) ;
3034
35+ private readonly minUsernameLength = 2 ;
36+ private readonly maxUsernameLength = 32 ;
37+
3138 constructor (
3239 private readonly configService : ConfigService ,
3340 private readonly jwtService : JwtService ,
@@ -90,6 +97,46 @@ export class WechatService {
9097 return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${ this . appId } &redirect_uri=${ encodeURIComponent ( this . redirectUri ) } &response_type=code&scope=snsapi_userinfo&state=${ state } #wechat_redirect` ;
9198 }
9299
100+ generateSuffix ( ) : string {
101+ return (
102+ '_' +
103+ generateId ( 4 , 'useandomTPXpxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict' )
104+ ) ;
105+ }
106+
107+ async getValidUsername (
108+ nickname : string ,
109+ manager : EntityManager ,
110+ ) : Promise < string > {
111+ let username = nickname ;
112+
113+ if ( username . length > this . maxUsernameLength ) {
114+ username = nickname . slice ( 0 , this . maxUsernameLength ) ;
115+ }
116+ if ( username . length >= this . minUsernameLength ) {
117+ const user = await this . userService . findByUsername ( username , manager ) ;
118+ if ( ! user ) {
119+ return username ;
120+ }
121+ }
122+
123+ username = nickname . slice ( 0 , this . maxUsernameLength - 5 ) ;
124+ for ( let i = 0 ; i < 5 ; i ++ ) {
125+ const suffix = this . generateSuffix ( ) ;
126+ const user = await this . userService . findByUsername (
127+ username + suffix ,
128+ manager ,
129+ ) ;
130+ if ( ! user ) {
131+ return username + suffix ;
132+ }
133+ }
134+
135+ throw new InternalServerErrorException (
136+ 'Unable to generate a valid username' ,
137+ ) ;
138+ }
139+
93140 async handleCallback ( code : string , state : string ) : Promise < any > {
94141 const stateInfo = this . qrCodeStates . get ( state ) ;
95142 if ( ! stateInfo ) {
@@ -98,22 +145,28 @@ export class WechatService {
98145 const isWeixin = stateInfo . type === 'weixin' ;
99146 const appId = isWeixin ? this . appId : this . openAppId ;
100147 const appSecret = isWeixin ? this . appSecret : this . openAppSecret ;
101- const response = await fetch (
148+ const accessTokenResponse = await fetch (
102149 `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${ appId } &secret=${ appSecret } &code=${ code } &grant_type=authorization_code` ,
103150 ) ;
104- if ( ! response . ok ) {
151+ if ( ! accessTokenResponse . ok ) {
105152 throw new UnauthorizedException ( 'Failed to get WeChat access token' ) ;
106153 }
107- const userData = await response . json ( ) ;
154+ const accessTokenData = await accessTokenResponse . json ( ) ;
108155
109- if ( userData . errmsg ) {
110- throw new BadRequestException ( userData . errmsg ) ;
156+ if ( accessTokenData . errmsg ) {
157+ throw new BadRequestException ( accessTokenData . errmsg ) ;
111158 }
112159
113- if ( ! userData . unionid ) {
114- throw new UnauthorizedException (
115- 'Failed to get WeChat UnionID, please make sure you have followed the official account' ,
116- ) ;
160+ const userDataResponse = await fetch (
161+ `https://api.weixin.qq.com/sns/userinfo?access_token=${ accessTokenData . access_token } &openid=${ accessTokenData . openid } &lang=zh_CN` ,
162+ ) ;
163+ if ( ! userDataResponse . ok ) {
164+ throw new UnauthorizedException ( 'Failed to get WeChat user info' ) ;
165+ }
166+ const userData = await userDataResponse . json ( ) ;
167+
168+ if ( userData . errmsg ) {
169+ throw new BadRequestException ( userData . errmsg ) ;
117170 }
118171
119172 const wechatUser = await this . userService . findByLoginId ( userData . unionid ) ;
@@ -127,10 +180,13 @@ export class WechatService {
127180 stateInfo . userInfo = returnValue ;
128181 return returnValue ;
129182 }
130-
131183 return await this . dataSource . transaction ( async ( manager ) => {
184+ const nickname : string = userData . nickname ;
185+ const username : string = await this . getValidUsername ( nickname , manager ) ;
186+ this . logger . debug ( { nickname, username } ) ;
132187 const wechatUser = await this . userService . createUserBinding (
133188 {
189+ username,
134190 loginType : 'wechat' ,
135191 loginId : userData . unionid ,
136192 } ,
0 commit comments