mirror of
https://github.com/certd/certd.git
synced 2026-07-26 22:09:04 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73cab6a6ee | |||
| 7a71e45799 | |||
| fdb1d1e6dd | |||
| 6dd4d6adeb |
@@ -1,3 +1,4 @@
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
@@ -1,8 +1,34 @@
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复阿里云证书订单orderid 选择出错的问题 ([41254d1](https://github.com/certd/certd/commit/41254d10b748a2d3e6ba43c7e11411650c748d1b))
|
||||
* **monitor:** 修复开放接口自动创建证书流水线重复触发和等待时间不足的问题 ([91d5c90](https://github.com/certd/certd/commit/91d5c90eb0eaf65c81dddbd2d4d4b404cb8b4d07))
|
||||
* **pipeline:** 修复批量随机修改定时没有生效的bug ([2e19dda](https://github.com/certd/certd/commit/2e19dda72e70b525a7c269e18e963a5ee602f59f))
|
||||
|
||||
### Features
|
||||
|
||||
* 商业版支持邀请返佣功能 ([f9a310b](https://github.com/certd/certd/commit/f9a310b6c3bbf30f221482a0c59e9c30080bdfc8))
|
||||
* 商业版支持邀请推广功能 ([f1d2a10](https://github.com/certd/certd/commit/f1d2a1033a0f8d3dbd91fc9793e07bd0b858b539))
|
||||
* 新增管理员针对用户流水线和证书监控管理功能 ([0211552](https://github.com/certd/certd/commit/021155278e7375f8487b0531ed1b5ad52512f007))
|
||||
* 新增套餐激活码功能,通过CDK兑换套餐 ([81d6289](https://github.com/certd/certd/commit/81d6289a8631b073b49f24dee4b14bb1c8f31071))
|
||||
* 新增推广等级激励功能 ([5096df5](https://github.com/certd/certd/commit/5096df5cc0d8f0ad8aa327b8e2a900ba23714bd8))
|
||||
* 新增证书申请参数模版管理,开放接口支持使用证书参数模版和指定证书申请参数 ([f8b71a0](https://github.com/certd/certd/commit/f8b71a0e612fad527cf49136335e0b46f0f379cd))
|
||||
* 支持dns-persist-01持久化验证方式申请证书,优化Acme账号的存储方式 ([67b05e2](https://github.com/certd/certd/commit/67b05e2d75e96b9f855e1ca0b3d0d8d03b92d8e6))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 插件全局配置支持下拉选项自定义映射功能 ([c637985](https://github.com/certd/certd/commit/c637985575b09196b04cce37ac14fbe68c029bde))
|
||||
* 商业版提现增加收款二维码上传 ([83a5a21](https://github.com/certd/certd/commit/83a5a21f956e50942541f1532f3a8dcaa5821d34))
|
||||
* **aliyun-apig:** 优化阿里云API网关部署插件的查询及翻页 ([3e4b7f3](https://github.com/certd/certd/commit/3e4b7f30ac6f3c976c8274bdf256c69b8a2c46db))
|
||||
* **trade:** 优化商品购买页面的规格展示和折扣计算,支持订单取消 ([6624769](https://github.com/certd/certd/commit/66247690326ce2789900fc9110c08b3502cea655))
|
||||
|
||||
## [1.40.5](https://github.com/certd/certd/compare/v1.40.4...v1.40.5) (2026-05-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -76,5 +76,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -27,5 +27,5 @@
|
||||
"prettier": "^2.8.8",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -34,5 +34,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -59,5 +59,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -36,5 +36,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -69,5 +69,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -41,5 +41,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -61,5 +61,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
}
|
||||
|
||||
@@ -107,6 +107,9 @@ function install(app: App, options: any = {}) {
|
||||
scroll: {
|
||||
x: 960,
|
||||
},
|
||||
rowSelection: {
|
||||
fixed: "left",
|
||||
},
|
||||
size: "small",
|
||||
pagination: false,
|
||||
onResizeColumn: (w: number, col: any) => {
|
||||
|
||||
@@ -389,7 +389,7 @@ export const certdResources = [
|
||||
meta: {
|
||||
show: () => {
|
||||
const settingStore = useSettingStore();
|
||||
return settingStore.isComm;
|
||||
return settingStore.isInviteCommissionEnabled;
|
||||
},
|
||||
icon: "ion:gift-outline",
|
||||
auth: true,
|
||||
|
||||
@@ -15,6 +15,7 @@ import { resetAllStores, useAccessStore } from "/@/vben/stores";
|
||||
|
||||
import { useUserStore as vbenUserStore } from "/@/vben/stores/modules/user";
|
||||
import { request } from "/@/api/service";
|
||||
import { inviteUtils } from "/@/utils/util.invite";
|
||||
|
||||
interface UserState {
|
||||
userInfo: Nullable<UserInfoRes>;
|
||||
@@ -66,6 +67,9 @@ export const useUserStore = defineStore({
|
||||
},
|
||||
async register(user: RegisterReq) {
|
||||
await UserApi.register(user);
|
||||
if (user.inviteCode) {
|
||||
inviteUtils.clear();
|
||||
}
|
||||
notification.success({
|
||||
message: "注册成功,请登录",
|
||||
});
|
||||
@@ -85,6 +89,9 @@ export const useUserStore = defineStore({
|
||||
let loginRes: any = null;
|
||||
if (loginType === "sms") {
|
||||
loginRes = await UserApi.loginBySms(params as SmsLoginReq);
|
||||
if ((params as SmsLoginReq).inviteCode) {
|
||||
inviteUtils.clear();
|
||||
}
|
||||
} else {
|
||||
loginRes = await UserApi.login(params as LoginReq);
|
||||
}
|
||||
@@ -136,12 +143,12 @@ export const useUserStore = defineStore({
|
||||
} catch (e) {
|
||||
console.error("注销登录请求失败:", e);
|
||||
}
|
||||
// 第三方登录注销
|
||||
this.oauthLogout();
|
||||
}
|
||||
|
||||
this.resetState();
|
||||
resetAllStores();
|
||||
// 第三方登录注销
|
||||
await this.oauthLogout();
|
||||
goLogin && router.push("/login");
|
||||
mitter.emit("app.logout");
|
||||
},
|
||||
|
||||
@@ -41,6 +41,10 @@ export const inviteUtils = {
|
||||
}
|
||||
},
|
||||
|
||||
clear() {
|
||||
localStorage.removeItem(INVITE_STORAGE_KEY);
|
||||
},
|
||||
|
||||
captureFromLocation() {
|
||||
const hashQuery = window.location.hash?.split("?")[1] || "";
|
||||
const search = window.location.search?.replace(/^\?/, "") || "";
|
||||
|
||||
@@ -300,7 +300,7 @@ async function handleTabChange() {
|
||||
}
|
||||
|
||||
async function refreshInvitePage(autoOpenAgreement = true) {
|
||||
await settingStore.initOnce();
|
||||
await settingStore.init();
|
||||
enabled.value = settingStore.isInviteCommissionEnabled;
|
||||
loaded.value = true;
|
||||
if (!enabled.value) {
|
||||
|
||||
@@ -26,13 +26,14 @@ export async function OauthToken(type: string, validationCode: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function AutoRegister(type: string, code: string) {
|
||||
export async function AutoRegister(type: string, code: string, inviteCode?: string) {
|
||||
return await request({
|
||||
url: apiPrefix + `/autoRegister`,
|
||||
method: "post",
|
||||
data: {
|
||||
validationCode: code,
|
||||
type,
|
||||
inviteCode,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import { useRoute, useRouter } from "vue-router";
|
||||
import { useUserStore } from "/@/store/user";
|
||||
import { notification } from "ant-design-vue";
|
||||
import { useSettingStore } from "/@/store/settings";
|
||||
import { inviteUtils } from "/@/utils/util.invite";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -99,7 +100,11 @@ async function goBindUser() {
|
||||
|
||||
async function autoRegister() {
|
||||
//自动注册账号
|
||||
const res = await api.AutoRegister(oauthType, bindCode.value);
|
||||
const inviteCode = inviteUtils.get();
|
||||
const res = await api.AutoRegister(oauthType, bindCode.value, inviteCode);
|
||||
if (inviteCode) {
|
||||
inviteUtils.clear();
|
||||
}
|
||||
//登录成功
|
||||
userStore.onLoginSuccess(res);
|
||||
//跳转到首页
|
||||
|
||||
@@ -215,6 +215,8 @@ export default defineComponent({
|
||||
|
||||
const handleFinish = async (values: any) => {
|
||||
try {
|
||||
//先注销登录
|
||||
userStore.resetState();
|
||||
await userStore.register(
|
||||
toRaw({
|
||||
type: registerType.value,
|
||||
|
||||
@@ -8,7 +8,6 @@ import { LoginService } from "../../../modules/login/service/login-service.js";
|
||||
import { OauthBoundService } from "../../../modules/login/service/oauth-bound-service.js";
|
||||
import { AddonGetterService } from "../../../modules/pipeline/service/addon-getter-service.js";
|
||||
import { UserEntity } from "../../../modules/sys/authority/entity/user.js";
|
||||
import { UserService } from "../../../modules/sys/authority/service/user-service.js";
|
||||
import { IOauthProvider } from "../../../plugins/plugin-oauth/api.js";
|
||||
|
||||
type OauthProviderSetting = {
|
||||
@@ -42,8 +41,6 @@ export class ConnectController extends BaseController {
|
||||
loginService: LoginService;
|
||||
@Inject()
|
||||
codeService: CodeService;
|
||||
@Inject()
|
||||
userService: UserService;
|
||||
|
||||
@Inject()
|
||||
oauthBoundService: OauthBoundService;
|
||||
@@ -199,7 +196,7 @@ export class ConnectController extends BaseController {
|
||||
}
|
||||
|
||||
@Post("/autoRegister", { description: Constants.per.guest })
|
||||
public async autoRegister(@Body(ALL) body: { validationCode: string; type: string }) {
|
||||
public async autoRegister(@Body(ALL) body: { validationCode: string; type: string; inviteCode?: string }) {
|
||||
const validationValue = this.codeService.getValidationValue(body.validationCode);
|
||||
if (!validationValue) {
|
||||
throw new Error("第三方认证授权已过期");
|
||||
@@ -212,7 +209,7 @@ export class ConnectController extends BaseController {
|
||||
newUser.nickName = userInfo.nickName || simpleNanoId(6);
|
||||
newUser.email = userInfo.email || "";
|
||||
|
||||
newUser = await this.userService.register("username", newUser, async txManager => {
|
||||
newUser = await this.loginService.register("username", newUser, body.inviteCode, async txManager => {
|
||||
const oauthBound: OauthBoundEntity = new OauthBoundEntity();
|
||||
oauthBound.userId = newUser.id;
|
||||
oauthBound.type = oauthType;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ALL, Body, Controller, Inject, Post, Provide, RequestIP } from "@midwayjs/core";
|
||||
import { BaseController, Constants, SysSettingsService } from "@certd/lib-server";
|
||||
import { RegisterType, UserService } from "../../../modules/sys/authority/service/user-service.js";
|
||||
import { RegisterType } from "../../../modules/sys/authority/service/user-service.js";
|
||||
import { CodeService } from "../../../modules/basic/service/code-service.js";
|
||||
import { checkComm, checkPlus } from "@certd/plus-core";
|
||||
import { InviteService } from "@certd/commercial-core";
|
||||
import { LoginService } from "../../../modules/login/service/login-service.js";
|
||||
|
||||
export type RegisterReq = {
|
||||
type: RegisterType;
|
||||
@@ -24,16 +24,13 @@ export type RegisterReq = {
|
||||
@Controller("/api/")
|
||||
export class RegisterController extends BaseController {
|
||||
@Inject()
|
||||
userService: UserService;
|
||||
loginService: LoginService;
|
||||
@Inject()
|
||||
codeService: CodeService;
|
||||
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Inject()
|
||||
inviteService: InviteService;
|
||||
|
||||
@Post("/register", { description: Constants.per.guest })
|
||||
public async register(
|
||||
@Body(ALL)
|
||||
@@ -62,9 +59,7 @@ export class RegisterController extends BaseController {
|
||||
username: body.username,
|
||||
password: body.password,
|
||||
} as any;
|
||||
const newUser = await this.userService.register(body.type, registerUser, async txManager => {
|
||||
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: body.inviteCode });
|
||||
});
|
||||
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
|
||||
return this.ok(newUser);
|
||||
} else if (body.type === "mobile") {
|
||||
if (sysPublicSettings.mobileRegisterEnabled === false) {
|
||||
@@ -84,9 +79,7 @@ export class RegisterController extends BaseController {
|
||||
mobile: body.mobile,
|
||||
password: body.password,
|
||||
} as any;
|
||||
const newUser = await this.userService.register(body.type, registerUser, async txManager => {
|
||||
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: body.inviteCode });
|
||||
});
|
||||
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
|
||||
return this.ok(newUser);
|
||||
} else if (body.type === "email") {
|
||||
if (sysPublicSettings.emailRegisterEnabled === false) {
|
||||
@@ -103,9 +96,7 @@ export class RegisterController extends BaseController {
|
||||
email: body.email,
|
||||
password: body.password,
|
||||
} as any;
|
||||
const newUser = await this.userService.register(body.type, registerUser, async txManager => {
|
||||
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: body.inviteCode });
|
||||
});
|
||||
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
|
||||
return this.ok(newUser);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { LoginService } from "./login-service.js";
|
||||
|
||||
function createLoginService() {
|
||||
const service = new LoginService();
|
||||
const calls: any = {
|
||||
register: [],
|
||||
bindInvitee: [],
|
||||
withTx: [],
|
||||
};
|
||||
const txManager = { tx: true };
|
||||
const newUser = { id: 1001, username: "new_user" };
|
||||
service.userService = {
|
||||
async register(type: string, user: any, withTx?: any) {
|
||||
calls.register.push({ type, user, withTx });
|
||||
user.id = newUser.id;
|
||||
if (withTx) {
|
||||
await withTx(txManager);
|
||||
}
|
||||
return newUser;
|
||||
},
|
||||
} as any;
|
||||
service.inviteService = {
|
||||
async bindInvitee(ctx: any, req: any) {
|
||||
calls.bindInvitee.push({ ctx, req });
|
||||
return { id: 1 };
|
||||
},
|
||||
} as any;
|
||||
return { service, calls, newUser, txManager };
|
||||
}
|
||||
|
||||
describe("LoginService.register", () => {
|
||||
it("registers user and binds invite code after user creation", async () => {
|
||||
const { service, calls, newUser } = createLoginService();
|
||||
const registerUser = { username: "alice" } as any;
|
||||
|
||||
const result = await service.register("username", registerUser, "ABC123");
|
||||
|
||||
assert.equal(result, newUser);
|
||||
assert.equal(calls.register.length, 1);
|
||||
assert.equal(calls.register[0].type, "username");
|
||||
assert.equal(calls.register[0].user, registerUser);
|
||||
assert.equal(calls.bindInvitee.length, 1);
|
||||
assert.deepEqual(calls.bindInvitee[0].ctx, {});
|
||||
assert.deepEqual(calls.bindInvitee[0].req, {
|
||||
inviteeUserId: newUser.id,
|
||||
inviteCode: "ABC123",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps registration successful when invite binding fails", async () => {
|
||||
const { service, calls, newUser } = createLoginService();
|
||||
service.inviteService = {
|
||||
async bindInvitee(ctx: any, req: any) {
|
||||
calls.bindInvitee.push({ ctx, req });
|
||||
throw new Error("invalid invite code");
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = await service.register("username", { username: "alice" } as any, "BADCODE");
|
||||
|
||||
assert.equal(result, newUser);
|
||||
assert.equal(calls.register.length, 1);
|
||||
assert.equal(calls.bindInvitee.length, 1);
|
||||
});
|
||||
|
||||
it("keeps original registration transaction callback", async () => {
|
||||
const { service, calls, txManager } = createLoginService();
|
||||
|
||||
await service.register("username", { username: "alice" } as any, "", async tx => {
|
||||
calls.withTx.push(tx);
|
||||
});
|
||||
|
||||
assert.equal(calls.withTx.length, 1);
|
||||
assert.equal(calls.withTx[0], txManager);
|
||||
assert.equal(calls.bindInvitee.length, 0);
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import jwt from "jsonwebtoken";
|
||||
import { AuthException, CommonException, Need2FAException, SysPrivateSettings, SysSettingsService } from "@certd/lib-server";
|
||||
import { RoleService } from "../../sys/authority/service/role-service.js";
|
||||
import { UserEntity } from "../../sys/authority/entity/user.js";
|
||||
import { cache, utils } from "@certd/basic";
|
||||
import { cache, logger, utils } from "@certd/basic";
|
||||
import { LoginErrorException } from "@certd/lib-server";
|
||||
import { CodeService } from "../../basic/service/code-service.js";
|
||||
import { TwoFactorService } from "../../mine/service/two-factor-service.js";
|
||||
@@ -14,6 +14,7 @@ import { AddonService } from "@certd/lib-server";
|
||||
import { OauthBoundService } from "./oauth-bound-service.js";
|
||||
import { PasskeyService } from "./passkey-service.js";
|
||||
import { InviteService } from "@certd/commercial-core";
|
||||
import { EntityManager } from "typeorm";
|
||||
|
||||
/**
|
||||
*/
|
||||
@@ -108,6 +109,19 @@ export class LoginService {
|
||||
throw new LoginErrorException(errorMessage, leftTimes);
|
||||
}
|
||||
|
||||
async register(type: string, user: UserEntity, inviteCode?: string, withTx?: (tx: EntityManager) => Promise<void>) {
|
||||
const newUser = await this.userService.register(type, user, withTx);
|
||||
if (!inviteCode) {
|
||||
return newUser;
|
||||
}
|
||||
try {
|
||||
await this.inviteService.bindInvitee({}, { inviteeUserId: newUser.id, inviteCode });
|
||||
} catch (e) {
|
||||
logger.error("绑定邀请关系失败,不影响用户注册", e);
|
||||
}
|
||||
return newUser;
|
||||
}
|
||||
|
||||
async loginBySmsCode(req: { mobile: string; phoneCode: string; smsCode: string; randomStr: string; inviteCode?: string }) {
|
||||
this.checkIsBlocked(req.mobile);
|
||||
|
||||
@@ -130,9 +144,7 @@ export class LoginService {
|
||||
mobile,
|
||||
password: "",
|
||||
} as any;
|
||||
info = await this.userService.register("mobile", registerUser, async txManager => {
|
||||
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: req.inviteCode });
|
||||
});
|
||||
info = await this.register("mobile", registerUser, req.inviteCode);
|
||||
}
|
||||
this.clearCacheOnSuccess(mobile);
|
||||
return this.onLoginSuccess(info);
|
||||
|
||||
@@ -1 +1 @@
|
||||
14:02
|
||||
12:32
|
||||
|
||||
Reference in New Issue
Block a user