System inform (#2263)

* feat: Bind Notification Pipe (#2229)

* chore: account page add bind notification modal

* feat: timerlock schema and type

* feat(fe): bind notification method modal

* chore: fe adjust

* feat: clean useless code

* fix: cron lock

* chore: adjust the code

* chore: rename api

* chore: remove unused code

* chore: fe adjust

* perf: bind inform ux

* fix: time ts

* chore: notification (#2251)

* perf: send message code

* perf: sub schema index

* fix: timezone plugin

* fix: format

---------

Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
This commit is contained in:
Archer
2024-08-05 00:29:14 +08:00
committed by GitHub
parent 998e7833e8
commit 56f6e69bc7
31 changed files with 344 additions and 171 deletions

View File

@@ -1,5 +1,10 @@
import dayjs from 'dayjs';
import cronParser from 'cron-parser';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
export const formatTime2YMDHMW = (time?: Date) => dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd');
export const formatTime2YMDHM = (time?: Date) =>

View File

@@ -0,0 +1,9 @@
export type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Omit<T, Keys> &
{
[K in Keys]-?: Required<Pick<T, K>> & Partial<Omit<T, K>>;
}[Keys];
export type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Omit<T, Keys> &
{
[K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>>;
}[Keys];

View File

@@ -1,11 +1,13 @@
export enum UserAuthTypeEnum {
register = 'register',
findPassword = 'findPassword',
wxLogin = 'wxLogin'
wxLogin = 'wxLogin',
bindNotification = 'bindNotification'
}
export const userAuthTypeMap = {
[UserAuthTypeEnum.register]: 'register',
[UserAuthTypeEnum.findPassword]: 'findPassword',
[UserAuthTypeEnum.wxLogin]: 'wxLogin'
[UserAuthTypeEnum.wxLogin]: 'wxLogin',
[UserAuthTypeEnum.bindNotification]: 'bindNotification'
};

View File

@@ -15,3 +15,14 @@ export const InformLevelMap = {
label: '紧急'
}
};
export enum SendInformTemplateCodeEnum {
EXPIRE_SOON = 'EXPIRE_SOON',
EXPIRED = 'EXPIRED',
FREE_CLEAN = 'FREE_CLEAN',
REGISTER = 'REGISTER',
RESET_PASSWORD = 'RESET_PASSWORD',
BIND_NOTIFICATION = 'BIND_NOTIFICATION',
LACK_OF_POINTS = 'LACK_OF_POINTS',
CUSTOM = 'CUSTOM'
}

View File

@@ -1,13 +1,16 @@
import { InformLevelEnum } from './constants';
import { InformLevelEnum, SendInformTemplateCodeEnum } from './constants';
export type SendInformProps = {
title: string;
content: string;
level: `${InformLevelEnum}`;
templateCode: `${SendInformTemplateCodeEnum}`;
templateParam: Record<string, any>;
customLockMinutes?: number; // custom lock minutes
};
export type SendInform2UserProps = SendInformProps & {
tmbId: string;
teamId: string;
};
export type SendInform2User = SendInformProps & {
type: `${InformTypeEnum}`;
tmbId: string;

View File

@@ -18,7 +18,9 @@ export type TeamSchema = {
};
lafAccount: LafAccountType;
defaultPermission: PermissionValueType;
notificationAccount?: string;
};
export type tagsType = {
label: string;
key: string;
@@ -63,6 +65,7 @@ export type TeamTmbItemType = {
role: `${TeamMemberRoleEnum}`;
status: `${TeamMemberStatusEnum}`;
lafAccount?: LafAccountType;
notificationAccount?: string;
permission: TeamPermission;
};
@@ -72,7 +75,6 @@ export type TeamMemberItemType = {
teamId: string;
memberName: string;
avatar: string;
// TODO: this should be deprecated.
role: `${TeamMemberRoleEnum}`;
status: `${TeamMemberStatusEnum}`;
permission: TeamPermission;

View File

@@ -1,12 +1,10 @@
import { TeamPermission } from '../permission/user/controller';
import { UserStatusEnum } from './constant';
import { TeamTmbItemType } from './team/type';
export type UserModelSchema = {
_id: string;
username: string;
email?: string;
phonePrefix?: number;
phone?: string;
password: string;
avatar: string;
promotionRate: number;
@@ -31,4 +29,6 @@ export type UserType = {
openaiAccount: UserModelSchema['openaiAccount'];
team: TeamTmbItemType;
standardInfo?: standardInfoType;
notificationAccount?: string;
permission: TeamPermission;
};

View File

@@ -4,14 +4,21 @@ export enum TimerIdEnum {
checkInvalidVector = 'checkInvalidVector',
clearExpiredSubPlan = 'clearExpiredSubPlan',
updateStandardPlan = 'updateStandardPlan',
scheduleTriggerApp = 'scheduleTriggerApp'
scheduleTriggerApp = 'scheduleTriggerApp',
notification = 'notification'
}
export const timerIdMap = {
[TimerIdEnum.checkInValidDatasetFiles]: 'checkInValidDatasetFiles',
[TimerIdEnum.checkInvalidDatasetData]: 'checkInvalidDatasetData',
[TimerIdEnum.checkInvalidVector]: 'checkInvalidVector',
[TimerIdEnum.clearExpiredSubPlan]: 'clearExpiredSubPlan',
[TimerIdEnum.updateStandardPlan]: 'updateStandardPlan',
[TimerIdEnum.scheduleTriggerApp]: 'scheduleTriggerApp'
};
export enum LockNotificationEnum {
NotificationExpire = 'notification_expire',
NotificationFreeClean = 'notification_free_clean',
NotificationLackOfPoints = 'notification_lack_of_points'
}
export type LockType = `${LockNotificationEnum}`;
// add a new type enum example:
// export enum ExampleLockEnum {
// ExampleLockType1 = 'example_lock_type1'
// }
//
// export type LockType = `${NotificationLockEnum}` | `${ExampleLockEnum}`

View File

@@ -1,6 +1,5 @@
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
import { timerIdMap } from './constants';
const { Schema, model, models } = connectionMongo;
import { connectionMongo, getMongoModel } from '../../mongo';
const { Schema } = connectionMongo;
import { TimerLockSchemaType } from './type.d';
export const collectionName = 'systemtimerlocks';
@@ -9,8 +8,7 @@ const TimerLockSchema = new Schema({
timerId: {
type: String,
required: true,
unique: true,
enum: Object.keys(timerIdMap)
unique: true
},
expiredTime: {
type: Date,

View File

@@ -1,4 +1,3 @@
import { TimerIdEnum } from './constants';
import { MongoTimerLock } from './schema';
import { addMinutes } from 'date-fns';
@@ -9,7 +8,7 @@ export const checkTimerLock = async ({
timerId,
lockMinuted
}: {
timerId: TimerIdEnum;
timerId: string;
lockMinuted: number;
}) => {
try {

View File

@@ -45,6 +45,8 @@ export async function getUserDetail({
timezone: user.timezone,
promotionRate: user.promotionRate,
openaiAccount: user.openaiAccount,
team: tmb
team: tmb,
notificationAccount: tmb.notificationAccount,
permission: tmb.permission
};
}

View File

@@ -1,5 +1,5 @@
import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { connectionMongo, getMongoModel } from '../../common/mongo';
const { Schema } = connectionMongo;
import { hashStr } from '@fastgpt/global/common/string/tools';
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant';
@@ -18,15 +18,9 @@ const UserSchema = new Schema({
required: true,
unique: true // 唯一
},
email: {
type: String
},
phonePrefix: {
type: Number
},
phone: {
type: String
},
password: {
type: String,
required: true,

View File

@@ -40,7 +40,8 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
permission: new TeamPermission({
per: tmbPer?.permission ?? tmb.teamId.defaultPermission,
isOwner: tmb.role === TeamMemberRoleEnum.owner
})
}),
notificationAccount: tmb.teamId.notificationAccount
};
}

View File

@@ -1,5 +1,5 @@
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { connectionMongo, getMongoModel } from '../../../common/mongo';
const { Schema } = connectionMongo;
import { TeamSchema as TeamType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema';
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
@@ -51,6 +51,10 @@ const TeamSchema = new Schema({
pat: {
type: String
}
},
notificationAccount: {
type: String,
required: false
}
});

View File

@@ -3,8 +3,8 @@
1. type=standard: There will only be 1, and each team will have one
2. type=extraDatasetSize/extraPoints: Can buy multiple
*/
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { connectionMongo, getMongoModel } from '../../../common/mongo';
const { Schema } = connectionMongo;
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import {
standardSubLevelMap,
@@ -84,11 +84,13 @@ const SubSchema = new Schema({
});
try {
// get team plan
SubSchema.index({ teamId: 1, type: 1, expiredTime: -1 });
// Get plan by expiredTime
SubSchema.index({ expiredTime: -1, currentSubLevel: 1 });
// timer task. check expired plan; update standard plan;
SubSchema.index({ type: 1, currentSubLevel: 1, expiredTime: -1 });
// Get team plan
SubSchema.index({ teamId: 1, type: 1, expiredTime: -1 });
// timer task. Get standard plan;Get free plan;Clear expired extract plan
SubSchema.index({ type: 1, expiredTime: -1, currentSubLevel: 1 });
} catch (error) {
console.log(error);
}

View File

@@ -63,7 +63,7 @@ const UsageSchema = new Schema({
try {
UsageSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true });
// timer task. clear dead team
UsageSchema.index({ teamId: 1, time: -1 }, { background: true });
// UsageSchema.index({ teamId: 1, time: -1 }, { background: true });
UsageSchema.index({ time: 1 }, { expireAfterSeconds: 180 * 24 * 60 * 60 });
} catch (error) {

View File

@@ -1165,6 +1165,8 @@
"Language": "Language",
"Member Name": "Nickname",
"Notice": "Notice",
"Notification Receive": "Notification Receive",
"Notification Receive Bind": "Please bind notification receive method",
"Old password is error": "Old password is incorrect",
"OpenAI Account Setting": "OpenAI account settings",
"Password": "Password",

View File

@@ -1,4 +1,6 @@
{
"bind_inform_account_error": "Abnormal binding notification account",
"bind_inform_account_success": "Binding notification account successful",
"permission": {
"Manage": "administrator",
"Manage tip": "Team administrator, with full permissions",
@@ -10,4 +12,4 @@
"team": {
"Add manager": "Add manager"
}
}
}

View File

@@ -992,6 +992,8 @@
}
},
"user": {
"Email Or Phone": "邮箱/手机号",
"Verify Code": "验证码",
"Avatar": "头像",
"Go laf env": "点击前往 {{env}} 获取 PAT 凭证。",
"Laf account course": "查看绑定 laf 账号教程。",
@@ -1165,6 +1167,8 @@
"Language": "语言",
"Member Name": "昵称",
"Notice": "通知",
"Notification Receive": "通知接收",
"Notification Receive Bind": "请绑定通知接收途径",
"Old password is error": "旧密码错误",
"OpenAI Account Setting": "OpenAI 账号配置",
"Password": "密码",

View File

@@ -1,4 +1,9 @@
{
"bind_inform_account_error": "绑定通知账号异常",
"bind_inform_account_success": "绑定通知账号成功",
"notification": {
"Bind Notification Pipe Hint": "绑定接收通知的邮箱或手机号,以确保您能及时接收到重要的系统通知。"
},
"permission": {
"Manage": "管理员",
"Manage tip": "团队管理员,拥有全部权限",
@@ -10,4 +15,4 @@
"team": {
"Add manager": "添加管理员"
}
}
}