Skip to content

Commit

Permalink
feat: add grid view that allows to custom any fields you want (#265)
Browse files Browse the repository at this point in the history
- add new view called grid
- grid allows users to create custom field in multiple formats: text, number, date, select, multiselect, person, created_at, created_by, updated_at, updated_by
- grid allows to filter dynamically
  • Loading branch information
hudy9x authored Dec 15, 2024
1 parent 14928cb commit a24c73a
Show file tree
Hide file tree
Showing 186 changed files with 8,521 additions and 507 deletions.
4 changes: 2 additions & 2 deletions docker/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# ----------------------- base -----------------
FROM node:lts-alpine AS base
FROM node:22-alpine AS base

USER root
# Update image
Expand Down Expand Up @@ -36,7 +36,7 @@ RUN yarn generate2
RUN yarn build:be

# ------------------------ runner ---------------------
FROM node:lts-alpine AS runner
FROM node:22-alpine AS runner

WORKDIR /app

Expand Down
4 changes: 2 additions & 2 deletions docker/frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:lts-alpine AS base
FROM node:22-alpine AS base

USER root
# Update image
Expand Down Expand Up @@ -40,7 +40,7 @@ RUN yarn generate2
RUN yarn build:fe

# ----------------------- runner -----------------
FROM node:lts-alpine AS runner
FROM node:22-alpine AS runner

# USER namviek
WORKDIR /app
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@
"apexcharts": "^3.41.0",
"axios": "^1.4.0",
"bcryptjs": "^2.4.3",
"bson": "^5.3.0",
"bson": "^6.9.0",
"bullmq": "^5.1.4",
"cors": "^2.8.5",
"date-fns": "^2.30.0",
"docx-preview": "^0.3.3",
"dotenv": "^16.3.1",
"emoji-picker-react": "^4.5.16",
"express": "^4.18.1",
Expand Down Expand Up @@ -93,7 +94,7 @@
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "18.2.0",
"react-hook-form": "^7.44.3",
"react-icons": "^4.9.0",
"react-icons": "^5.3.0",
"react-pdf": "^7.7.1",
"read-excel-file": "^5.6.1",
"resend": "^1.0.0",
Expand All @@ -105,6 +106,7 @@
"zustand": "^4.3.8"
},
"devDependencies": {
"@faker-js/faker": "^9.2.0",
"@nx/cypress": "16.2.2",
"@nx/esbuild": "16.2.2",
"@nx/eslint-plugin": "16.2.2",
Expand Down
18 changes: 16 additions & 2 deletions packages/be-gateway/src/lib/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,21 @@ import { WinstonTransport as AxiomTransport } from '@axiomhq/winston';

// const { combine, timestamp, label, prettyPrint, simple } = format

const axiomDataset = process.env.AXIOM_DATASET || ''
const axiomToken = process.env.AXIOM_TOKEN || ''

const defaultLogger = {
info: (...args: any[]) => console.log(1),
error: (...args: any[]) => console.log(1),
debug: (...args: any[]) => console.log(1)

}

export const createModuleLog = (module: string) => {
if (!axiomToken && !axiomDataset) {
return defaultLogger
}

return createLogger({
// format: combine(label({ label: module }), timestamp(), prettyPrint()),
// transports: [
Expand All @@ -17,8 +31,8 @@ export const createModuleLog = (module: string) => {
defaultMeta: { service: 'user-service' },
transports: [
new AxiomTransport({
dataset: process.env.AXIOM_DATASET || '',
token: process.env.AXIOM_TOKEN || '',
dataset: axiomDataset,
token: axiomToken,
}),
],

Expand Down
11 changes: 8 additions & 3 deletions packages/be-gateway/src/lib/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@ try {
console.log('redis connection established')
})

redis.on('connect', () => {
connected = true
error = true
})

redis.on('error', err => {
if (error) return
console.log('redis connection error')
connected = false
error = true
// console.log(error)
console.log('redis connection error')
if (error) return
})
} catch (error) {
console.log('redis connection error')
Expand Down
35 changes: 32 additions & 3 deletions packages/be-gateway/src/middlewares/authMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,47 @@
import { NextFunction, Response } from 'express';
import { decodeToken, extractToken, generateRefreshToken, generateToken, verifyRefreshToken } from '../lib/jwt';
import { AuthRequest, JWTPayload } from '../types';
import { AuthRequest, JWTPayload, JWTType } from '../types';
import { pmClient } from 'packages/shared-models/src/lib/_prisma';

export const authMiddleware = async (req: AuthRequest, res: Response, next: NextFunction) => {
const headers = req.headers;
const authorization = headers.authorization;
const refreshToken = headers.refreshtoken as string;
const clientId = headers['x-client-id'] as string;
const clientSecret = headers['x-client-secret'] as string;

console.log('auth middleware run', clientId, clientSecret)

if (clientId && clientSecret) {
const application = await pmClient.application.findFirst({
where: {
clientId,
clientSecret,
}
})

if (!application) {
console.log('Application is INACTIVE or doesnt exist')
return res.status(401).end();
}

req.authen = {
id: application.id,
email: application.name,
name: application.name,
type: JWTType.APP,
photo: ''
}

return next();
}

try {
const validToken = extractToken(authorization);
if (validToken) {
// console.log('token is valid');
const { id, email, name, photo } = validToken as JWTPayload;
req.authen = { id, email, name, photo };
req.authen = { id, email, name, photo, type: JWTType.USER };
// make sure that all tokens cleared
res.setHeader('Authorization', '');
res.setHeader('RefreshToken', '');
Expand Down Expand Up @@ -51,7 +80,7 @@ export const authMiddleware = async (req: AuthRequest, res: Response, next: Next

console.log('genereated succesfully');

req.authen = user;
req.authen = {...user, type: JWTType.USER};
return next();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { NextFunction, Response } from 'express'
import { mdMemberBelongToProject } from '@shared/models'
import { AuthRequest } from '../types'
import { AuthRequest, JWTType } from '../types'

export const beProjectMemberMiddleware = async (
req: AuthRequest,
res: Response,
next: NextFunction
) => {
const { id } = req.authen
const { id, type } = req.authen
const { body, query, params } = req
const projectId = query.projectId || body.projectId || params.projectId
const projectIds = query.projectIds as string[]
Expand All @@ -24,6 +24,11 @@ export const beProjectMemberMiddleware = async (
return
}

if (type === JWTType.APP) {
next()
return
}

const result = await mdMemberBelongToProject(id, projectId)

if (!result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ export default class AwsS3StorageProvider {
}
}

console.log('S3 configuration')
console.log('minio', minioEndpoint)
console.log(JSON.stringify(s3Config, null, ' '))

this.client = new S3Client(s3Config)

clientMapper.set(orgId, {
Expand Down
21 changes: 21 additions & 0 deletions packages/be-gateway/src/queues/Common/FieldSortableJob.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { FieldService } from '../../services/field'
import { BaseJob } from '../BaseJob'

interface ISortatbleFieldData {
id: string
order: number
}

export class FieldSortableJob extends BaseJob {
name = 'fieldSortable'
fieldService: FieldService
constructor() {
super()
this.fieldService = new FieldService()
}
async implement(data: ISortatbleFieldData[]) {
console.log('implement field sortable')
await this.fieldService.sortable(data)
console.log('finish implementation')
}
}
22 changes: 22 additions & 0 deletions packages/be-gateway/src/queues/Common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { FieldSortableJob } from './FieldSortableJob'
import { BaseQueue } from '../BaseQueue'

export class CommonQueue extends BaseQueue {
constructor() {
super()
this.queueName = 'Common'
this.jobs = [new FieldSortableJob()]

this.run()
}
}

let instance: CommonQueue = null

export const getCommonQueueInstance = () => {
if (!instance) {
instance = new CommonQueue()
}

return instance
}
101 changes: 101 additions & 0 deletions packages/be-gateway/src/routes/apps/index.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
BaseController,
Body,
Controller,
Delete,
Get,
Post,
Put,
Req,
UseMiddleware
} from '../../core'
import { authMiddleware } from '../../middlewares'
import { ApplicationRepository } from '@shared/models'
import { randomBytes } from 'crypto'
import { AuthRequest } from '../../types'
import { Application } from '@prisma/client'

@Controller('/apps')
@UseMiddleware([authMiddleware])
export class ApplicationController extends BaseController {
private appRepo: ApplicationRepository

constructor() {
super()
this.appRepo = new ApplicationRepository()
}

@Get('/:orgId')
async getApps(@Req() req: AuthRequest) {
const orgId = req.params.orgId

if (!orgId) {
throw new Error('Organization ID is required')
}

const apps = await this.appRepo.getByOrgId(orgId)
return apps
}

@Post('')
async create(@Req() req: AuthRequest, @Body() body: { name: string, desc?: string, orgId: string }) {
const { name, desc, orgId } = body
const { id: uid } = req.authen

// Validate required fields
if (!name || !orgId) {
throw new Error('Name and Organization are required')
}

const clientId = randomBytes(8).toString('hex')
const clientSecret = randomBytes(16).toString('hex')

const result = await this.appRepo.create({
name,
description: desc || '',
organizationId: orgId,
clientId,
scopes: [],
clientSecret,
updatedBy: null,
createdBy: uid,
createdAt: new Date(),
updatedAt: null
})

return result
}

@Put('')
async update(@Body() body: Application, @Req() req: AuthRequest) {
const { id, ...rest } = body
const { id: uid } = req.authen


if (!id) {
throw new Error('Application ID and status are required')
}

const result = await this.appRepo.update(id, {
...rest,
updatedBy: uid,
updatedAt: new Date()
})

return result
}

@Delete('/:id')
async delete(@Req() req: AuthRequest) {
const { id } = req.params

console.log('1', id)

if (!id) {
throw new Error('Application ID is required')
}

const result = await this.appRepo.delete(id)
return result
}
}
Loading

0 comments on commit a24c73a

Please sign in to comment.