Skip to content

Commit

Permalink
feat: .env add GRAVATAR_TYPE
Browse files Browse the repository at this point in the history
  • Loading branch information
SolidZORO committed Jun 2, 2020
1 parent c317528 commit dcb3ba1
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 19 deletions.
5 changes: 3 additions & 2 deletions packages/_leaa-common/src/dtos/user/user.create-one.req.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { IsOptional, IsNotEmpty, Length, MinLength, IsEmail, IsPhoneNumber } fro
export class UserCreateOneReq {
@IsOptional()
@IsPhoneNumber('CN')
@MinLength(11)
phone?: string;

@IsNotEmpty()
@IsOptional()
@IsEmail()
@MinLength(4)
email!: string;
email?: string;

@IsOptional()
@Length(1, 64)
Expand Down
13 changes: 6 additions & 7 deletions packages/_leaa-common/src/entrys/user.entity.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { Index, Entity, Column, JoinTable, ManyToMany, AfterLoad, BeforeInsert } from 'typeorm';
import { Index, Entity, Column, JoinTable, ManyToMany, AfterLoad, BeforeInsert, Unique } from 'typeorm';
import { Exclude } from 'class-transformer';

import { Base, Role, Permission, Address, Attachment } from '@leaa/common/src/entrys';
import { transAvatarUrl, genAvatarUrl } from '@leaa/api/src/utils/attachment.util';

@Entity('users')
// @Index('users_phone_unique', ['phone'], { unique: true })
@Index('users_email_unique', ['email'], { unique: true })
@Unique('account', ['phone', 'email'])
@Index('account_unique', ['phone', 'email'])
export class User extends Base {
@Column({ type: 'varchar', length: 64, nullable: true, default: '' })
name?: string;

// @Column({ type: 'varchar', length: 32, unique: true })
@Column({ type: 'varchar', length: 32, default: '', nullable: true })
@Column({ type: 'varchar', length: 32, default: '' })
phone?: string;

@Column({ type: 'varchar', length: 64, unique: true })
email!: string;
@Column({ type: 'varchar', length: 64, default: '' })
email?: string;

@Column({ type: 'varchar', nullable: true, default: null })
avatar_url?: string | null;
Expand Down
3 changes: 3 additions & 0 deletions packages/leaa-api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ RATELIMIT_MAX=200

ENABLE_CAPTCHA_BY_LOGIN_FAILD_TIMES=5

# 404 | mp | identicon | monsterid | wavatar | retro | robohash | blank
GRAVATAR_TYPE=monsterid

ATTACHMENT_DIR=attachments
ATTACHMENT_LIMIT_SIZE_MB=5
ATTACHMENT_SAVE_IN_LOCAL=true
Expand Down
2 changes: 2 additions & 0 deletions packages/leaa-api/src/interfaces/config.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export interface IDotEnv {
DEBUG_MODE: string;

PUBLIC_DIR: string;
GRAVATAR_TYPE: string;

ATTACHMENT_DIR: string;
ATTACHMENT_LIMIT_SIZE_MB: number;
ATTACHMENT_SAVE_IN_LOCAL: string;
Expand Down
9 changes: 9 additions & 0 deletions packages/leaa-api/src/modules/v1/config/config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export class ConfigService {
return this.envConfig.PUBLIC_DIR;
}

get GRAVATAR_TYPE(): string {
return this.envConfig.GRAVATAR_TYPE;
}

get ATTACHMENT_DIR(): string {
return this.envConfig.ATTACHMENT_DIR;
}
Expand Down Expand Up @@ -194,6 +198,11 @@ export class ConfigService {
DEBUG_MODE: envalid.str({ choices: ['true', 'false'], default: 'false' }),
//
PUBLIC_DIR: envalid.str(),
GRAVATAR_TYPE: envalid.str({
choices: ['404', 'mp', 'identicon', 'monsterid', 'wavatar', 'retro', 'robohash', 'blank'],
default: 'monsterid',
}),
//
ATTACHMENT_DIR: envalid.str(),
ATTACHMENT_LIMIT_SIZE_MB: envalid.num(),
ATTACHMENT_SAVE_IN_LOCAL: envalid.str({ choices: ['true', 'false'], default: 'false' }),
Expand Down
2 changes: 1 addition & 1 deletion packages/leaa-api/src/modules/v1/user/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Controller, UseGuards, Headers } from '@nestjs/common';
import { Controller, UseGuards } from '@nestjs/common';
import { Crud, CrudController, Override, ParsedRequest, CrudRequest, ParsedBody } from '@nestjsx/crud';

import { Permissions, JwtUser } from '@leaa/api/src/decorators';
Expand Down
25 changes: 18 additions & 7 deletions packages/leaa-api/src/utils/attachment.util.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import crypto from 'crypto';
import { Attachment } from '@leaa/common/src/entrys';
import { attachmentConfig } from '@leaa/api/src/configs';
import { envConfig } from '@leaa/api/src/modules/v1/config/config.module';
import { getAt2xPath } from '@leaa/api/src/utils/path.util';
import crypto from 'crypto';

export const isAt2x = (originalname: string): boolean => /[@@_]2x/i.test(originalname);

Expand All @@ -11,8 +12,6 @@ export const filenameAt1xToAt2x = (filename: string): string => {
return filename.replace(ext, `_2x${ext}`);
};

// const CLS_NAME = 'AttachmentProperty';

type IAttachment = Pick<Attachment, 'in_oss' | 'in_local' | 'path' | 'external_url'>;

export const genUrl = (attachment: IAttachment): string | null => {
Expand Down Expand Up @@ -47,8 +46,22 @@ export const genUrlAt2x = (attachment: Attachment): string | null => {
return null;
};

// set default avatar @see https://cn.gravatar.com/site/implement/images/
//
// avatarType
// 404: do not load any image if none is associated with the email hash
// mp: (mystery-person) a simple, cartoon-style silhouetted outline of a person (does not vary by email hash)
// identicon: a geometric pattern based on an email hash
// monsterid: a generated 'monster' with different colors, faces, etc
// wavatar: generated faces with differing features and backgrounds
// retro: awesome generated, 8-bit arcade-style pixelated faces
// robohash: a generated robot with different colors, faces, etc
// blank: a transparent PNG image (border added to HTML below for demonstration purposes)
export const GRAVATAR_AVATAR_TYPE = envConfig.GRAVATAR_TYPE || 'monsterid';
export const GRAVATAR_AVATAR_PARAMS = `?s=160&d=${GRAVATAR_AVATAR_TYPE}`;

export const transAvatarUrl = (path?: string | null): string | null => {
if (path?.includes('gravatar.com')) return path;
if (path?.includes('gravatar.com')) return `${path}${GRAVATAR_AVATAR_PARAMS}`;

if (path?.includes('/attachments/') && !path?.includes('http')) {
return `${attachmentConfig.URL_PREFIX_BY_AUTO}${path}`;
Expand All @@ -58,12 +71,10 @@ export const transAvatarUrl = (path?: string | null): string | null => {
};

export const genAvatarUrl = (hash?: string): string => {
// set default avatar
const hashMd5 = crypto
.createHash('md5')
.update(hash || `hash-${new Date().valueOf()}@local.com`)
.digest('hex');

const avatarParams = 's=160&d=monsterid';
return `//secure.gravatar.com/avatar/${hashMd5}?${avatarParams}`;
return `//secure.gravatar.com/avatar/${hashMd5}`;
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState } from 'react';
import _ from 'lodash';
import { v4 } from 'uuid';
import prettyBytes from 'pretty-bytes';
import cx from 'classnames';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -89,6 +88,14 @@ export const TableCard = <T extends object>(props: IProps<T>) => {
sortOrder: calcTableSortOrder('name', crudQuery.sort),
render: (text: string, record: any) => <Link to={`${props.route.path}/${record.id}`}>{record.name}</Link>,
}),
phone: () => ({
title: t('_lang:phone'),
dataIndex: 'phone',
width: 60,
sorter: true,
sortOrder: calcTableSortOrder('phone', crudQuery.sort),
render: (text: string, record: any) => <Link to={`${props.route.path}/${record.id}`}>{record.phone}</Link>,
}),
email: () => ({
title: t('_lang:email'),
dataIndex: 'email',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export default (props: IPage) => {
'isAdmin',
'avatar',
'email',
'phone',
'roleList',
'createdAt',
'status',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,16 @@ export const UserInfoForm = forwardRef((props: IProps, ref: React.Ref<any>) => {
className={cx(style['form-wrapper'], { [style['form-wrapper--avatar']]: props.item })}
>
<Row gutter={16} className={style['form-row']}>
<Col xs={24} sm={6}>
<Form.Item name="phone" rules={[{ len: 11 }]} validateTrigger={['onBlur']} label={t('_lang:phone')}>
<Input placeholder={t('_lang:phone')} />
</Form.Item>
</Col>

<Col xs={24} sm={6}>
<Form.Item
name="email"
rules={[{ required: true, type: 'email', min: 6 }]}
rules={[{ type: 'email', min: 6 }]}
validateTrigger={['onBlur']}
label={t('_lang:email')}
>
Expand Down

0 comments on commit dcb3ba1

Please sign in to comment.