Skip to content

Commit

Permalink
feat: working location upload with images
Browse files Browse the repository at this point in the history
  • Loading branch information
jmcdo29 committed Nov 7, 2023
1 parent b0bc1a0 commit ace0e35
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 18 deletions.
2 changes: 1 addition & 1 deletion libs/server/common/src/lib/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ValibotDto } from "./valibot.dto";

export const FileSchema = object({
fieldname: string(),
originalName: string(),
originalname: string(),
mimetype: string(),
size: number(),
buffer: instance(Buffer),
Expand Down
14 changes: 10 additions & 4 deletions libs/server/common/src/lib/valibot-to-openapi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Type } from "@nestjs/common";
import { ObjectSchema } from "valibot";
import { ObjectSchema, OptionalSchema } from "valibot";

// biome-ignore lint/suspicious/noExplicitAny: valibot needs any here
type Object = ObjectSchema<Record<string, any>>;

const getType = (schema: {
schema: string;
Expand Down Expand Up @@ -44,9 +47,12 @@ const getType = (schema: {
throw new Error(`Unknown Type: "${schema.schema}"`);
}
};

// biome-ignore lint/suspicious/noExplicitAny: valibot needs any here
export const schemaToOpenAPI = (schema: ObjectSchema<Record<string, any>>) => {
export const schemaToOpenAPI = (
schema: Object | OptionalSchema<Object>,
): Record<string, unknown> => {
if (schema.schema === "optional") {
return { required: false, ...schemaToOpenAPI(schema.wrapped) };
}
const valibotSchema = schema.object;
const schemaObj: Record<string, unknown> = {};
const schemaKeys = Object.keys(valibotSchema);
Expand Down
8 changes: 5 additions & 3 deletions libs/server/common/src/lib/valibot.dto.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { TypeschemaDto } from "@nest-lab/typeschema";
import { ObjectSchema } from "valibot";
import { ObjectSchema, OptionalSchema, OptionalSchemaAsync } from "valibot";
import { schemaToOpenAPI } from "./valibot-to-openapi";

// biome-ignore lint/suspicious/noExplicitAny: valibot requires any here
export const ValibotDto: <T extends ObjectSchema<Record<string, any>>>(
// biome-ignore lint/suspicious/noExplicitAny: Valibot required any here
type Object = ObjectSchema<Record<string, any>>;

export const ValibotDto: <T extends Object | OptionalSchema<Object>,>(
schema: T,
) => ReturnType<typeof TypeschemaDto<T>> = (schema) => {
class DtoSchema extends TypeschemaDto(schema) {
Expand Down
16 changes: 15 additions & 1 deletion libs/server/location/src/lib/location.repository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Injectable } from "@nestjs/common";
import { File } from "@unteris/server/common";
import { Database, InjectKysely } from "@unteris/server/kysely";
import {
Location,
Expand Down Expand Up @@ -29,10 +30,23 @@ export class LocationRepository {

async createLocation(
location: Insertable<Database["location"]>,
file?: string,
): Promise<Location> {
let fileId;
if (file) {
const result = await this.db
.insertInto("image")
.values({
originalUrl: file,
type: "location_image",
})
.returning(["id"])
.execute();
fileId = result[0].id;
}
return this.db
.insertInto("location")
.values(location)
.values({ ...location, imageId: fileId })
.returningAll()
.executeTakeFirstOrThrow();
}
Expand Down
4 changes: 4 additions & 0 deletions libs/server/location/src/lib/models/image-file.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { FileSchema, ValibotDto } from "@unteris/server/common";
import { optional } from "valibot";

export class ImageFile extends ValibotDto(optional(FileSchema)) {}
3 changes: 3 additions & 0 deletions libs/server/location/src/lib/models/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export * from "./by-type-query.dto";
export * from "./image-file.dto";
export * from "./location-creation.dto";
export * from "./location-update.dto";
13 changes: 8 additions & 5 deletions libs/server/location/src/lib/serer-location.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@ import {
getSchemaPath,
} from "@nestjs/swagger";
import { Action, Castle, CastleGuard, Subject } from "@unteris/server/castle";
import { FileDto, IdParamDto, OverviewObjectDto } from "@unteris/server/common";
import { IdParamDto, OverviewObjectDto } from "@unteris/server/common";
import { SkipSessionCheck } from "@unteris/server/session";
import { locationRoute } from "@unteris/shared/types";
import { ByTypeQueryDto } from "./models";
import { LocationCreationDto } from "./models/location-creation.dto";
import { LocationUpdateDto } from "./models/location-update.dto";
import {
ByTypeQueryDto,
ImageFile,
LocationCreationDto,
LocationUpdateDto,
} from "./models";
import { ServerLocationService } from "./server-location.service";

@ApiTags("Location")
Expand Down Expand Up @@ -77,7 +80,7 @@ export class ServerLocationController {
@UseGuards(CastleGuard)
@Castle([Action.Create, Subject.Location])
@Post("new")
create(@Body() body: LocationCreationDto, @UploadedFile() file?: FileDto) {
create(@Body() body: LocationCreationDto, @UploadedFile() file?: ImageFile) {
return this.service.createLocation(body.data, file?.data ?? undefined);
}

Expand Down
9 changes: 8 additions & 1 deletion libs/server/location/src/lib/server-location.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { Module } from "@nestjs/common";
import { ServerCastleModule } from "@unteris/server/castle";
import { ServerFileStorageModule } from "@unteris/server/file-storage";
import { ServerImageClientModule } from "@unteris/server/image-client";
import { KyselyModule } from "@unteris/server/kysely";
import { LocationRepository } from "./location.repository";
import { ServerLocationController } from "./serer-location.controller";
import { ServerLocationService } from "./server-location.service";

@Module({
imports: [KyselyModule, ServerCastleModule],
imports: [
KyselyModule,
ServerCastleModule,
ServerImageClientModule,
ServerFileStorageModule,
],
controllers: [ServerLocationController],
providers: [ServerLocationService, LocationRepository],
exports: [ServerLocationService],
Expand Down
18 changes: 16 additions & 2 deletions libs/server/location/src/lib/server-location.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import { File } from "@unteris/server/common";
import { ServerFileStorageService } from "@unteris/server/file-storage";
import { ServerImageClientService } from "@unteris/server/image-client";
import { Database } from "@unteris/server/kysely";
import {
Location,
Expand All @@ -11,7 +13,11 @@ import { LocationRepository } from "./location.repository";

@Injectable()
export class ServerLocationService {
constructor(private readonly locationRepo: LocationRepository) {}
constructor(
private readonly locationRepo: LocationRepository,
private readonly imageService: ServerImageClientService,
private readonly fileService: ServerFileStorageService,
) {}

async getByType(type: Location["type"]): Promise<OverviewObject[]> {
return this.locationRepo.getByType(type);
Expand All @@ -29,7 +35,15 @@ export class ServerLocationService {
location: Insertable<Database["location"]>,
file?: File,
): Promise<Location> {
const result = this.locationRepo.createLocation(location);
let filePath;
if (file) {
filePath = file.originalname;
await this.fileService.writeFileToStore(filePath, file.buffer);
}
const result = await this.locationRepo.createLocation(location, filePath);
if (result.imageId) {
this.imageService.sendImageIdForProcessing(result.imageId);
}
return result;
}

Expand Down
2 changes: 1 addition & 1 deletion libs/shared/types/src/lib/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Output, enumType, nullable, object, string, ulid } from "valibot";

export const ImageSchema = object({
id: string([ulid()]),
type: enumType(["deity_avatar", "user_avatar"]),
type: enumType(["deity_avatar", "user_avatar", "location_image"]),
originalUrl: string(),
smallUrl: nullable(string()),
mediumUrl: nullable(string()),
Expand Down

0 comments on commit ace0e35

Please sign in to comment.