Commit 93372e06 authored by Abdul R. Wahid's avatar Abdul R. Wahid
Browse files

Merge branch 'super-modular' into 'develop'

Super modular

See merge request riset/onest!2
parents 7b5f7c19 7feca411
......@@ -40,5 +40,8 @@ lerna-debug.log*
package-lock.json
# Uploaded File
src/upload/*
!src/upload/.gitkeep
\ No newline at end of file
storage/upload/*
!storage/upload/.gitkeep
# Documentation
documentation
......@@ -18,6 +18,71 @@ const schema = {
default: 10
}
},
auth: {
register: {
admin: {
format: Boolean,
default: false
},
adminGroup: {
format: Number,
default: 2
},
adminOrg: {
format: Number,
default: 2
},
user: {
format: Boolean,
default: true
},
userGroup: {
format: Number,
default: 3
},
userOrg: {
format: Number,
default: 3
}
},
password: {
hash: {
format: ['md5','bcrypt','plain'],
default: 'md5'
},
compatibility: {
format: Boolean,
default: false
}
},
verification: {
expiry: {
format: Number,
default: 2
}
},
login: {
unverified: {
format: Boolean,
default: true
}
},
forgot: {
expiry: {
format: Number,
default: 5
}
},
},
profile: {
change: {
expiry: {
doc: "Change expiry time",
format: Number,
default: 3
}
}
},
jwt: {
secret: {
doc: "JWT Secret Key",
......@@ -169,7 +234,82 @@ const schema = {
private_key: ''
}
}
}
},
sms: {
msg_tpl: {
doc: "SMS Template",
format: String,
default: 'Kode OTP Anda : %CODEOTP% berlaku dalam 5 menit. Demi keamanan akun, mohon Tidak memberikan kode ini kepada siapapun.'
},
from: {
doc: "SMS From",
format: String,
default: 'speeda'
},
infobip: {
username: {
doc: "Username Info Bip",
format: String,
default: 'gamatechno2'
},
password: {
doc: "Pass Info Bip",
format: String,
default: 'Test12345'
},
masking: {
doc: "Masking Info Bip",
format: String,
default: 'speeda'
},
fake: {
doc: "Info Bip Fake",
format: Boolean,
default: false
}
},
ayosms: {
url: {
doc: "AyoSMS Url",
format: String,
default: 'https://api.ayosms.com/mconnect/gw/sendsms.php'
},
api_key: {
doc: "AyoSMS Api Key",
format: String,
default: '11877c8e0ac231ca93ec2cfcbe1820eb'
}
},
gtsms: {
user: {
doc: "gtSMS User",
format: String,
default: 'speeda'
},
pass: {
doc: "gtSMS Pass",
format: String,
default: 'sp33da'
}
},
mdmedia: {
user: {
doc: "MDMedia User",
format: String,
default: 'gamatechprem'
},
pass: {
doc: "MDMedia Pass",
format: String,
default: 'saL7KDH5'
},
senderid: {
doc: "MDMedia SenderId",
format: String,
default: 'Speeda'
}
}
},
}
}
export default schema
\ No newline at end of file
......@@ -18,7 +18,30 @@ const extras = {
public_key: 'f513cc219a8108e85b6cab2cd09dae8a',
private_key: '6d10f2bf8f6a0156062d54920dcc2120'
},
}
},
sms: {
msg_tpl: 'Kode OTP Anda : %CODEOTP% berlaku dalam 5 menit. Demi keamanan akun, mohon Tidak memberikan kode ini kepada siapapun.',
from: 'speeda',
infobip: {
username: 'gamatechno2',
password: 'Test12345',
masking: 'speeda',
fake: false
},
ayosms: {
url: 'https://api.ayosms.com/mconnect/gw/sendsms.php',
api_key: '11877c8e0ac231ca93ec2cfcbe1820eb'
},
gtsms: {
user: 'speeda',
pass: 'sp33da'
},
mdmedia: {
user: 'gamatechprem',
pass: 'saL7KDH5',
senderid: 'Speeda'
}
}
}
export {
extras
......
......@@ -8,6 +8,11 @@ const development = {
page: 1,
view: 10
},
auth: {
password: {
hash: 'bcrypt'
}
},
jwt: {
secret: 'onOILDsdafo;;dosf!!!2019',
authExpire: '24h'
......
ALTER TABLE `admin`
ADD COLUMN `adm_org` INT(11) DEFAULT 0 NULL AFTER `adm_photo`;
ALTER TABLE `users`
ADD COLUMN `user_org` INT(11) DEFAULT 0 NULL AFTER `user_photo`;
ALTER TABLE `admin`
CHANGE `adm_phone` `adm_phone` VARCHAR(17) NULL;
ALTER TABLE `users`
CHANGE `user_org` `user_org` INT(11) NULL AFTER `user_group`,
CHANGE `user_gender` `user_gender` ENUM('L','P') CHARSET latin1 COLLATE latin1_swedish_ci DEFAULT 'L' NULL AFTER `user_org`,
ADD COLUMN `user_birth` DATE NULL AFTER `user_gender`,
CHANGE `user_created_at` `user_created_at` DATETIME NULL AFTER `user_photo`;
ALTER TABLE `admin`
CHANGE `adm_org` `adm_org` INT(11) NULL AFTER `adm_group`,
CHANGE `adm_gender` `adm_gender` ENUM('L','P') CHARSET latin1 COLLATE latin1_swedish_ci DEFAULT 'L' NULL AFTER `adm_org`,
ADD COLUMN `adm_birth` DATE NULL AFTER `adm_gender`,
CHANGE `adm_created_at` `adm_created_at` DATETIME NULL AFTER `adm_photo`;
CREATE TABLE `verifications`(
`verify_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`verify_entity` VARCHAR(255),
`verify_entityid` INT(11),
`verify_type` ENUM('VERIFY_PHONE','VERIFY_EMAIL','FORGOT_BYPHONE','FORGOT_BYEMAIL','CHANGE_PHONE','CHANGE_EMAIL'),
`verify_email` VARCHAR(255),
`verify_phone` VARCHAR(17),
`verify_token_public` VARCHAR(255),
`verify_token_private` VARCHAR(11),
`verify_delivery` INT(1) COMMENT '0 = sent to vendor, pending. 1 = sent to vendor. 2 = not sent to vendor.',
`verify_created_at` DATETIME,
`verify_expired_at` DATETIME,
`verify_verified_at` DATETIME,
PRIMARY KEY (`verify_id`)
);
ALTER TABLE `users`
CHANGE `user_verified` `user_verified` TINYINT(1) DEFAULT 0 NULL COMMENT '0 = unverified, 1 = by phone, 3 = by email, 4 = by phone + email';
ALTER TABLE `admin`
CHANGE `adm_verified` `adm_verified` TINYINT(1) DEFAULT 0 NULL COMMENT '0 = unverified, 1 = by phone, 3 = by email, 4 = by phone + email';
......@@ -28,8 +28,10 @@
"@nestjs/passport": "^6.1.0",
"@nestjs/platform-express": "^6.7.2",
"@nestjs/platform-socket.io": "^6.11.9",
"@nestjs/swagger": "^4.3.2",
"@nestjs/swagger": "^4.5.1",
"@nestjs/websockets": "^6.11.9",
"bcrypt": "^4.0.1",
"bipsms": "^0.5.11",
"class-transformer": "^0.2.3",
"class-validator": "^0.11.0",
"cloudinary": "^1.19.0",
......@@ -37,6 +39,7 @@
"crypto-js": "^3.1.9-1",
"fcm-node": "^1.5.2",
"fcm-notification": "^2.0.0",
"fs-extra": "^9.0.0",
"knex": "^0.20.1",
"mjml": "^4.5.1",
"module-alias": "^2.2.2",
......@@ -57,9 +60,6 @@
"swagger-ui-express": "^4.1.2"
},
"devDependencies": {
"@nestjs/cli": "^6.9.0",
"@nestjs/schematics": "^6.7.0",
"@nestjs/testing": "^6.7.1",
"@types/convict": "^4.2.1",
"@types/express": "^4.17.1",
"@types/jest": "^24.0.18",
......@@ -80,7 +80,7 @@
"ts-loader": "^6.1.1",
"ts-node": "^8.4.1",
"tslint": "^5.20.0",
"typescript": "^3.6.3"
"typescript": "^3.8.3"
},
"jest": {
"moduleFileExtensions": [
......@@ -101,6 +101,7 @@
"@utils": "./dist/src/utils",
"@database": "./dist/src/database",
"@module": "./dist/src/module",
"@core": "./dist/src/core",
"@config": "./dist/config"
}
}
}
\ No newline at end of file
import config from '@config/config.loader';
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AppGateway } from './app.gateway';
import { KnexModule } from '@database/knex/knex.module';
import { MongooseLoaderModule } from '@database/mongoose/mongoose.module';
import { AdminModule } from '@module/admin/admin.module';
import { AuthModule } from '@module/auth/auth.module';
import { AuthlogModule } from '@module/authlog/authlog.module';
import { OrganizationModule } from '@module/organization/organization.module';
import { GroupModule } from '@module/group/group.module';
import { CloudinaryModule } from '@utils/cloudinary/cloudinary.module';
import { MediaModule } from '@module/media/media.module';
import { MJMLModule } from '@utils/mjml/mjml.module';
import { MailjetModule } from '@utils/mailer/mailjet/mailjet.module';
import { SocketModule } from '@utils/socket/socket.module';
import { FcmModule } from '@utils/fcm/fcm.module';
import config from '@config/config.loader'
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { AppGateway } from './app.gateway'
import { KnexModule } from '@database/knex/knex.module'
import { MongooseLoaderModule } from '@database/mongoose/mongoose.module'
import { AdminModule } from '@module/admin/admin.module'
import { UserModule } from '@module/user/user.module'
import { AuthModule } from '@module/auth/auth.module'
import { AuthlogModule } from '@module/authlog/authlog.module'
import { OrganizationModule } from '@module/organization/organization.module'
import { GroupModule } from '@module/group/group.module'
import { CloudinaryModule } from '@utils/cloudinary/cloudinary.module'
import { MediaModule } from '@module/media/media.module'
import { MJMLModule } from '@utils/mjml/mjml.module'
import { MailjetModule } from '@utils/mailer/mailjet/mailjet.module'
import { SocketModule } from '@utils/socket/socket.module'
import { FcmModule } from '@utils/fcm/fcm.module'
import { VerificationModule } from '@module/verification/verification.module'
import { ProfileModule } from '@core/profile/profile.module';
import { SmsModule } from '@utils/sms/sms.module'
let BaseModules = [KnexModule, AdminModule, AuthModule, CloudinaryModule, MJMLModule, MailjetModule, SocketModule, FcmModule]
let CoreModules = [
KnexModule,
AdminModule,
UserModule,
AuthModule,
OrganizationModule,
GroupModule,
MediaModule,
VerificationModule,
ProfileModule
]
let UtilModules = [
CloudinaryModule,
MJMLModule,
MailjetModule,
SocketModule,
FcmModule,
SmsModule
]
if (config.get('db_mongo.active')) {
BaseModules.push(MongooseLoaderModule)
CoreModules.push(MongooseLoaderModule)
}
if (config.get('authlog.active')) {
BaseModules.push(AuthlogModule)
CoreModules.push(AuthlogModule)
}
let Modules = [...BaseModules, OrganizationModule, GroupModule, MediaModule]
let Modules = [
...CoreModules,
...UtilModules
]
@Module({
imports: Modules,
......
......@@ -5,7 +5,7 @@ export const KNEX_MYSQL = 'KNEX_MYSQL'
export const MAX_UPLOAD = 20
export const MULTER_OPTION = {
storage: diskStorage({
destination: 'src/upload',
destination: 'storage/upload',
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now() +
path.extname(file.originalname));
......
import { Controller } from '@nestjs/common';
import { AdminService } from './admin.service';
@Controller()
export class AdminController {
constructor(protected readonly adminService: AdminService){
}
}
import { Module } from '@nestjs/common'
import { AdminService } from '@module/admin/admin.service'
import { MediaModule } from '@module/media/media.module'
@Module({
imports: [MediaModule],
providers: [AdminService],
exports: [AdminService],
})
export class AdminModule {}
import { Injectable, Inject } from '@nestjs/common'
import { Admin } from './admin'
import { KNEX_MYSQL } from '@common/constant'
import { MediaService } from '@module/media/media.service'
import { AuthRegisterDto } from '@module/auth/dto/authregister.dto'
import { ILocalAuthableRepository } from '@core/authable/interfaces/authable.repository.interface'
@Injectable()
export class AdminService implements ILocalAuthableRepository {
VERIFIED_PHONE: number = 1
VERIFIED_EMAIL: number = 3
constructor(
@Inject(KNEX_MYSQL) protected readonly knex,
protected readonly mediaService: MediaService
) { }
findAuthenticate (
adminid: string,
knex: any = this.knex,
): Promise<Admin | undefined> {
return knex('admin')
.where((query) => {
query.where('adm_email', adminid)
query.orWhere('adm_name', adminid)
query.orWhere('adm_phone', adminid)
})
.andWhere('adm_deleted', 0)
.first()
}
async findAuthenticated (
adminid: number,
knex: any = this.knex,
): Promise<Admin | undefined> {
let admin = knex('admins')
.where('adm_id', adminid)
.andWhere('adm_deleted', 0)
.first()
let groups = await this.findGroups(adminid)
admin.groups = [
{ org: admin.adm_org, group: admin.adm_group },
...groups
]
if (admin.adm_photo) {
let photo = await this.mediaService.findById(admin.adm_photo)
admin.adm_photo = photo.media_url
}
const {
adm_password,
adm_deleted,
adm_created_by,
adm_updated_by,
...result
} = admin
return result
}
findGroups (adminid: number, knex: any = this.knex): Promise<any | undefined> {
return knex
.select(
'adm_group_org AS org',
'adm_group_groupid AS group'
)
.from('admin_group')
.where({ adm_group_adminid: adminid })
}
register (admin: AuthRegisterDto, knex: any = this.knex) {
return new Promise(async (resolve, reject) => {
while (true) {
let longid = Math.floor(Math.random() * 99999999)
const admins = await this.knex('admin').select('*').where('adm_longid', longid)
if (admins.length < 1) {
knex('admin').insert({
adm_longid: longid,
adm_fullname: admin.fullname,
adm_name: admin.name,
adm_email: admin.email,
adm_created_at: admin.created_at,
adm_password: admin.password,
adm_phone: admin.phone,
adm_gender: admin.gender,
adm_birth: admin.birth,
adm_group: admin.group,
adm_org: admin.org
}).then((resp) => {
resolve(true)
}).catch((err) => {
console.log(err)
reject(err)
})
return false
}
}
})
}
findOne (
adm_email: string = '',
adm_name: string = '',
adm_phone: string = '',
knex: any = this.knex,
): Promise<Admin | undefined> {
return knex('admin')
.where('adm_email', adm_email)
.orWhere('adm_name', adm_name)
.orWhere('adm_phone', adm_phone)
.first()
}
async setVerified(adm_id: number, verification: number, knex: any = this.knex): Promise<number> {
let user = await this.findById(adm_id)
let adm_verified = user ? user.adm_verified : 0
if (
(verification === this.VERIFIED_EMAIL && [0, this.VERIFIED_PHONE].includes(adm_verified)) ||
(verification === this.VERIFIED_PHONE && [0, this.VERIFIED_EMAIL].includes(adm_verified))
) {
adm_verified = adm_verified + verification
}
return knex('admin')
.where('adm_id', adm_id)
.update({
adm_verified,
adm_updated_at: knex.fn.now(),
adm_updated_by: 0
})
}
async findById(adm_id: number, knex: any = this.knex): Promise<Admin | undefined> {
return knex('admin')
.where('adm_id', adm_id)
.first()
}
async edit(user: any, id: number, knex: any = this.knex) {
let updated = {}
if(user.user_fullname){
updated['adm_fullname'] = user.user_fullname
}
if(user.user_gender){
updated['adm_gender'] = user.user_gender
}
if(user.user_birth){
updated['adm_birth'] = user.user_birth
}
if(user.user_phone){
updated['adm_phone'] = user.user_phone
}
if(user.user_email){
updated['adm_email'] = user.user_email
}
if(user.user_password){
updated['adm_password'] = user.user_password
}
if(updated !== {}){
updated['adm_updated_at'] = knex.fn.now()
updated['adm_updated_by'] = 0
}
return knex('admin')
.where({adm_id: id})
.update(updated)
}
async updatePassword(adm_id: number, adm_password: string, knex: any = this.knex): Promise<number> {
return knex('admin')
.where('adm_id', adm_id)
.update({
adm_password,
adm_updated_at: knex.fn.now(),
adm_updated_by: 0
})
}
}