Skip to content

Commit

Permalink
Merge pull request #27 from akbarsaputrait/develop
Browse files Browse the repository at this point in the history
v24.03.10.00
  • Loading branch information
akbarsaputrait authored Mar 9, 2024
2 parents 922d559 + 447a00f commit 03ef0fc
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 11 deletions.
35 changes: 32 additions & 3 deletions src/app/owner/profile/profile.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@ import { Me } from '@core/decorators/user.decorator';
import { OwnerAuthGuard } from '@core/guards/auth.guard';
import { OwnerGuard } from '@core/guards/owner.guard';
import { AuthService } from '@core/services/auth.service';
import { AwsService } from '@core/services/aws.service';
import { PermAct, PermOwner } from '@core/services/role.service';
import { Media } from '@db/entities/core/media.entity';
import { Location } from '@db/entities/owner/location.entity';
import { Owner, OwnerStatus } from '@db/entities/owner/owner.entity';
import { OwnerTransformer } from '@db/transformers/owner.transformer';
import { NotPermittedException } from '@lib/exceptions/not-permitted.exception';
import { ValidationException } from '@lib/exceptions/validation.exception';
import { time } from '@lib/helpers/time.helper';
import { Validator } from '@lib/helpers/validator.helper';
import { Permissions } from '@lib/rbac';
import { BadRequestException, Body, Controller, Get, Param, Post, Put, Res, UseGuards } from '@nestjs/common';
import { Location } from '@db/entities/owner/location.entity';
import { BadRequestException, Body, Controller, Delete, Get, Param, Post, Put, Req, Res, UseGuards } from '@nestjs/common';
import { isEmpty } from 'lodash';

@Controller()
@UseGuards(OwnerAuthGuard())
export class ProfileController {
constructor(private auth: AuthService) {}
constructor(private auth: AuthService, private aws: AwsService) {}

@Get()
@UseGuards(OwnerGuard)
Expand Down Expand Up @@ -100,4 +103,30 @@ export class ProfileController {

return response.noContent();
}

@Post('/avatar')
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Profile}@${PermAct.C}`)
async uploadAvatar(@Req() request, @Res() response, @Me() me: Owner) {
const file = await this.aws.uploadFile(request, response, 'image', { dynamicPath: `staff/${me.id}/avatar` });
if (!file || isEmpty(file)) {
throw new BadRequestException('Unable to upload image');
}

if (await me.image) {
await this.aws.removeFile(await me.image);
}

await Media.build<Owner>(me, file);

await response.item(me, OwnerTransformer);
}

@Delete('/avatar')
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Profile}@${PermAct.D}`)
async deleteAvatar(@Res() response, @Me() me: Owner) {
await Media.delete({ owner_id: me.id });
return response.noContent();
}
}
4 changes: 4 additions & 0 deletions src/app/owner/restaurant/location/location.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ export class LocationController {
throw new BadRequestException('Location has already existed.');
}

if (isTrue(body.is_default)) {
await Location.update({ restaurant_id: rest.id }, { is_default: !isTrue(body.is_default) });
}

const loc = await Location.findOneByOrFail({ id: param.location_id });
loc.name = body.name;
loc.is_default = isTrue(body.is_default);
Expand Down
58 changes: 57 additions & 1 deletion src/app/owner/restaurant/restaurant.controller.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { Rest } from '@core/decorators/restaurant.decorator';
import { OwnerAuthGuard } from '@core/guards/auth.guard';
import { OwnerGuard } from '@core/guards/owner.guard';
import { AwsService } from '@core/services/aws.service';
import { PermAct, PermOwner } from '@core/services/role.service';
import { Media } from '@db/entities/core/media.entity';
import { Restaurant } from '@db/entities/owner/restaurant.entity';
import { RestaurantTransformer } from '@db/transformers/restaurant.transformer';
import { ValidationException } from '@lib/exceptions/validation.exception';
import { Validator } from '@lib/helpers/validator.helper';
import { Permissions } from '@lib/rbac';
import { Body, Controller, Get, Put, Res, UseGuards } from '@nestjs/common';
import AppDataSource from '@lib/typeorm/datasource.typeorm';
import { BadRequestException, Body, Controller, Delete, Get, Param, Post, Put, Req, Res, UseGuards } from '@nestjs/common';
import { get, isEmpty } from 'lodash';

@Controller()
@UseGuards(OwnerAuthGuard())
export class RestaurantController {
constructor(private aws: AwsService) {}

@Get()
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Restaurant}@${PermAct.R}`)
Expand All @@ -28,6 +34,7 @@ export class RestaurantController {
phone: 'required|phone|unique',
email: 'email',
website: 'url',
description: '',
};
const validation = Validator.init(body, rules);
if (validation.fails()) {
Expand All @@ -38,8 +45,57 @@ export class RestaurantController {
rest.phone = body.phone;
rest.email = body.email;
rest.website = body.website;
rest.description = body.description;
await rest.save();

return response.item(rest, RestaurantTransformer);
}

@Post('/image/:type')
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Restaurant}@${PermAct.C}`)
async uploadAvatar(@Req() request, @Res() response, @Rest() rest: Restaurant, @Param() param) {
if (!['logo', 'banner'].includes(param.type)) {
throw new BadRequestException('Invalid image type');
}

const file = await this.aws.uploadFile(request, response, 'image', { dynamicPath: `restaurant/${rest.id}/avatar` });
if (!file || isEmpty(file)) {
throw new BadRequestException('Unable to upload image');
}

const payload = await Media.getPayload(file);
const url = get(payload, 'url', null);
if (param.type == 'logo') {
rest.logo_url = url;
} else if (param.type == 'banner') {
rest.banner_url = url;
}

await AppDataSource.transaction(async (manager) => {
await manager.getRepository(Restaurant).save(rest);
});

await response.item(rest, RestaurantTransformer);
}

@Delete('/image/:type')
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Restaurant}@${PermAct.D}`)
async deleteAvatar(@Res() response, @Rest() rest: Restaurant, @Param() param) {
if (!['logo', 'banner'].includes(param.type)) {
throw new BadRequestException('Invalid image type');
}

if (param.type == 'logo') {
rest.logo_url = null;
} else if (param.type == 'banner') {
rest.banner_url = null;
}

await AppDataSource.transaction(async (manager) => {
await manager.getRepository(Restaurant).save(rest);
});
return response.noContent();
}
}
4 changes: 3 additions & 1 deletion src/app/owner/restaurant/staff/staff.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class StaffController {
const query = AppDataSource.createQueryBuilder(StaffUser, 't1');
query.where({ restaurant_id: rest.id });

if (loc.id) {
if (loc && loc.id) {
query.andWhere({ location_id: loc.id });
}

Expand All @@ -46,6 +46,7 @@ export class StaffController {
}

@Post()
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Staff}@${PermAct.C}`)
async store(@Body() body, @Res() response, @Rest() rest) {
const rules = {
Expand Down Expand Up @@ -110,6 +111,7 @@ export class StaffController {
}

@Put('/:id')
@UseGuards(OwnerGuard)
@Permissions(`${PermOwner.Staff}@${PermAct.U}`)
async update(@Param() param, @Body() body, @Res() response) {
const rules = {
Expand Down
13 changes: 13 additions & 0 deletions src/database/entities/core/media.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@ export class Media extends BaseEntity {
} as Media;
}

static async build<T>(this, entity: T | any, image: IStorageResponse): Promise<T> {
if (!(await entity.image)) {
return this.add(entity, image);
}

// reload entity
const payload = await Media.getPayload(image);
await entity.image.update({ ...payload });
await entity.reload();

return entity;
}

static async add<T>(entity: T | any, image: IStorageResponse): Promise<T> {
const payload = await Media.getPayload(image);
const klass = snakeCase(get(entity, 'constructor.name', '')).toLowerCase();
Expand Down
2 changes: 1 addition & 1 deletion src/database/entities/owner/owner.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class Owner extends BaseEntity {

@Exclude()
@OneToOne(() => Media, (media) => media.owner)
image: Media;
image: Promise<Media>;

get isVerified() {
return this.verified_at !== null;
Expand Down
9 changes: 9 additions & 0 deletions src/database/entities/owner/restaurant.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export class Restaurant extends BaseEntity {
@PhoneColumn()
phone: string;

@Column({ type: 'longtext' })
description: string;

@Column()
slug: string;

Expand All @@ -37,6 +40,12 @@ export class Restaurant extends BaseEntity {
@StatusColumn()
status: RestaurantStatus;

@Column({ nullable: true })
logo_url: string;

@Column({ nullable: true })
banner_url: string;

@Exclude()
@ForeignColumn()
owner_id: string;
Expand Down
13 changes: 13 additions & 0 deletions src/database/migrations/1709828001230-restaurant-desc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class restaurantDesc1709828001230 implements MigrationInterface {
name = 'restaurantDesc1709828001230';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`restaurant\` ADD \`description\` longtext NOT NULL`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`restaurant\` DROP COLUMN \`description\``);
}
}
15 changes: 15 additions & 0 deletions src/database/migrations/1710002572201-restaurant-image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class restaurantImage1710002572201 implements MigrationInterface {
name = 'restaurantImage1710002572201';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`restaurant\` ADD \`logo_url\` varchar(255) NULL`);
await queryRunner.query(`ALTER TABLE \`restaurant\` ADD \`banner_url\` varchar(255) NULL`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`restaurant\` DROP COLUMN \`banner_url\``);
await queryRunner.query(`ALTER TABLE \`restaurant\` DROP COLUMN \`logo_url\``);
}
}
7 changes: 3 additions & 4 deletions src/database/transformers/owner.transformer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Media } from '@db/entities/core/media.entity';
import { Owner } from '@db/entities/owner/owner.entity';
import { encrypt } from '@lib/helpers/encrypt.helper';
import { RequestHelper } from '@lib/helpers/request.helper';
Expand All @@ -11,13 +10,13 @@ export class OwnerTransformer extends TransformerAbstract {
return ['role', 'restaurant', 'location'];
}

transform(entity: Owner) {
async transform(entity: Owner) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { image, ...rest } = entity.toJSON();

return {
...rest,
avatar: Media.getImage(image),
avatar: await entity.getAvatar(),
};
}

Expand All @@ -41,7 +40,7 @@ export class OwnerTransformer extends TransformerAbstract {
name: location.name,
}
: null,
avatar: Media.getImage(entity.image),
avatar: await entity.getAvatar(),
};
}

Expand Down
2 changes: 1 addition & 1 deletion templates/mails/staff-register.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{{#content "content"}}
<p>Dear {{name}},</p>
<p>You are going to use this email address as your username in Kelola Staff!</p>
<p>You are going to use this email address as your username in Ordero.</p>
<p>To login to staff portal, please use the following password: <strong>{{password}}</strong></p>
<p>If you have any problems, simply reply to this email and we'll get back to you as soon as we can.</p>
<p>
Expand Down

0 comments on commit 03ef0fc

Please sign in to comment.