Skip to content

Commit

Permalink
(FE) Apply URL Spec (#98)
Browse files Browse the repository at this point in the history
* Change login page url

* Add new index page

* Add login button to main header

* Change height of main layout

* Change workspace url spec

* Change redirect url after login

* Change url of workspace

* Change component name from `Editor` to `Document`

* Change `injectProtectedRoute` implementation to support protecting children

* Change documentSlug to documentId in URL

* Delete share mode in `DocumentIndex`

* Change API path for document

* Change document page url

* Change cleanup code for docs

* Remove share mode in share modal

* Add document share page

* Componentize DocumentView

* Add share mode

* Fix mode button background padding

* Change document page to use `useYorkieDocument`

* Add tooltip to avatar

* Add back button to DocumentHeader

* Remove slug in document db

* Fix lint

* Add check path to API

* Fix formatting

* Add name conflict checking API

* Fix formatting

* Remove default nickname

* Add API for changing nickname

* Change findOptions for user workspaces

* Fix lint

* Add Change nickname modal

* Add name conflict checking on workspace

* Add conflict checking to CreateModal

* Add queryInvalidation on creating workspace

* Move to the note page when created
  • Loading branch information
devleejb authored Jan 24, 2024
1 parent 371b0b3 commit 52f7a08
Show file tree
Hide file tree
Showing 57 changed files with 891 additions and 439 deletions.
3 changes: 1 addition & 2 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
socialProvider String @map("social_provider")
socialUid String @unique @map("social_uid")
nickname String
nickname String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
userWorkspaceList UserWorkspace[]
Expand Down Expand Up @@ -52,7 +52,6 @@ model Document {
id String @id @default(auto()) @map("_id") @db.ObjectId
yorkieDocumentId String @map("yorkie_document_id")
title String
slug String
content String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
Expand Down
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { WorkspacesModule } from "./workspaces/workspaces.module";
import { WorkspaceUsersModule } from "./workspace-users/workspace-users.module";
import { WorkspaceDocumentsModule } from "./workspace-documents/workspace-documents.module";
import { DocumentsModule } from "./documents/documents.module";
import { CheckModule } from "./check/check.module";

@Module({
imports: [
Expand All @@ -19,6 +20,7 @@ import { DocumentsModule } from "./documents/documents.module";
WorkspaceUsersModule,
WorkspaceDocumentsModule,
DocumentsModule,
CheckModule,
],
controllers: [],
providers: [
Expand Down
3 changes: 1 addition & 2 deletions backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ export class AuthController {
async login(@Req() req: LoginRequest): Promise<HttpRedirectResponse> {
const user = await this.usersService.findOrCreate(
req.user.socialProvider,
req.user.socialUid,
req.user.nickname
req.user.socialUid
);

const accessToken = this.jwtService.sign({ sub: user.id, nickname: user.nickname });
Expand Down
13 changes: 0 additions & 13 deletions backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,4 @@ import { UsersService } from "src/users/users.service";
@Injectable()
export class AuthService {
constructor(private usersService: UsersService) {}

async issueJwtToken(socialProvider: string, socialUid: string, nickname: string) {
const user = await this.usersService.findOrCreate(socialProvider, socialUid, nickname);

if (user) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { socialProvider: _socaialProvider, socialUid: _social, ...result } = user;

return result;
}

return null;
}
}
18 changes: 18 additions & 0 deletions backend/src/check/check.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from "@nestjs/testing";
import { CheckController } from "./check.controller";

describe("CheckController", () => {
let controller: CheckController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [CheckController],
}).compile();

controller = module.get<CheckController>(CheckController);
});

it("should be defined", () => {
expect(controller).toBeDefined();
});
});
26 changes: 26 additions & 0 deletions backend/src/check/check.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Body, Controller, Post } from "@nestjs/common";
import { CheckService } from "./check.service";
import { CheckNameConflictDto } from "./dto/check-name-conflict.dto";
import { CheckNameConflicReponse } from "./types/check-name-conflict-response.type";
import { Public } from "src/utils/decorators/auth.decorator";
import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from "@nestjs/swagger";

@ApiTags("Check")
@Controller("check")
export class CheckController {
constructor(private checkService: CheckService) {}

@Public()
@Post("name-conflict")
@ApiOperation({
summary: "Check Whether The Name Conflicts with Username or Title of Workspace.",
description: "If the name is conflict, it returns true.",
})
@ApiBody({ type: CheckNameConflictDto })
@ApiOkResponse({ type: CheckNameConflicReponse })
async checkNameConflict(
@Body() checkNameConflictDto: CheckNameConflictDto
): Promise<CheckNameConflicReponse> {
return this.checkService.checkNameConflict(checkNameConflictDto.name);
}
}
10 changes: 10 additions & 0 deletions backend/src/check/check.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from "@nestjs/common";
import { CheckService } from "./check.service";
import { CheckController } from "./check.controller";
import { PrismaService } from "src/db/prisma.service";

@Module({
providers: [CheckService, PrismaService],
controllers: [CheckController],
})
export class CheckModule {}
18 changes: 18 additions & 0 deletions backend/src/check/check.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from "@nestjs/testing";
import { CheckService } from "./check.service";

describe("CheckService", () => {
let service: CheckService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [CheckService],
}).compile();

service = module.get<CheckService>(CheckService);
});

it("should be defined", () => {
expect(service).toBeDefined();
});
});
27 changes: 27 additions & 0 deletions backend/src/check/check.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Injectable } from "@nestjs/common";
import { PrismaService } from "src/db/prisma.service";
import { CheckNameConflicReponse } from "./types/check-name-conflict-response.type";
import slugify from "slugify";

@Injectable()
export class CheckService {
constructor(private prismaService: PrismaService) {}

async checkNameConflict(name: string): Promise<CheckNameConflicReponse> {
const slug = slugify(name, { lower: true });
const conflictUserList = await this.prismaService.user.findMany({
where: {
OR: [{ nickname: name }, { nickname: slug }],
},
});
const conflictWorkspaceList = await this.prismaService.workspace.findMany({
where: {
OR: [{ title: name }, { title: slug }],
},
});

return {
conflict: Boolean(conflictUserList.length + conflictWorkspaceList.length),
};
}
}
6 changes: 6 additions & 0 deletions backend/src/check/dto/check-name-conflict.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";

export class CheckNameConflictDto {
@ApiProperty({ type: String, description: "Name to check conflict" })
name: string;
}
6 changes: 6 additions & 0 deletions backend/src/check/types/check-name-conflict-response.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";

export class CheckNameConflicReponse {
@ApiProperty({ type: Boolean, description: "Whether the name is conflict" })
conflict: boolean;
}
23 changes: 1 addition & 22 deletions backend/src/documents/documents.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Controller, Get, Param, Query, Req } from "@nestjs/common";
import { Controller, Get, Query } from "@nestjs/common";
import { DocumentsService } from "./documents.service";
import { Public } from "src/utils/decorators/auth.decorator";
import {
ApiFoundResponse,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
Expand All @@ -12,8 +11,6 @@ import {
} from "@nestjs/swagger";
import { FindDocumentFromSharingTokenResponse } from "./types/find-document-from-sharing-token-response.type";
import { HttpExceptionResponse } from "src/utils/types/http-exception-response.type";
import { FindDocumentResponse } from "./types/find-document-response.type";
import { AuthroizedRequest } from "src/utils/types/req.type";

@ApiTags("Documents")
@Controller("documents")
Expand Down Expand Up @@ -41,22 +38,4 @@ export class DocumentsController {
): Promise<FindDocumentFromSharingTokenResponse> {
return this.documentsService.findDocumentFromSharingToken(token);
}

@Get(":document_slug")
@ApiOperation({
summary: "Retrieve a Document in the Workspace",
description: "If the user has the access permissions, return a document.",
})
@ApiFoundResponse({ type: FindDocumentResponse })
@ApiNotFoundResponse({
type: HttpExceptionResponse,
description:
"The workspace or document does not exist, or the user lacks the appropriate permissions.",
})
async findOne(
@Req() req: AuthroizedRequest,
@Param("document_slug") documentSlug: string
): Promise<FindDocumentResponse> {
return this.documentsService.findOneBySlug(req.user.id, documentSlug);
}
}
21 changes: 0 additions & 21 deletions backend/src/documents/documents.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,4 @@ export class DocumentsService {
role,
};
}

async findOneBySlug(userId: string, documentSlug: string) {
try {
const document = await this.prismaService.document.findFirstOrThrow({
where: {
slug: documentSlug,
},
});

await this.prismaService.userWorkspace.findFirstOrThrow({
where: {
userId,
workspaceId: document.workspaceId,
},
});

return document;
} catch (e) {
throw new NotFoundException();
}
}
}
2 changes: 0 additions & 2 deletions backend/src/documents/types/document-domain.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ export class DocumentDomain {
yorkieDocumentId: string;
@ApiProperty({ type: String, description: "Title of the document" })
title: string;
@ApiProperty({ type: String, description: "Slug of the document" })
slug: string;
@ApiProperty({ type: String, description: "Content of the document", required: false })
content?: string;
@ApiProperty({ type: Date, description: "Created date of the document" })
Expand Down
3 changes: 0 additions & 3 deletions backend/src/documents/types/find-document-response.type.ts

This file was deleted.

6 changes: 6 additions & 0 deletions backend/src/users/dto/change-nickname.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";

export class ChangeNicknameDto {
@ApiProperty({ type: String, description: "Nickname of user to update" })
nickname: string;
}
8 changes: 4 additions & 4 deletions backend/src/users/types/user-domain.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { ApiProperty } from "@nestjs/swagger";
export class UserDomain {
@ApiProperty({ type: String, description: "ID of user" })
id: string;
@ApiProperty({ type: String, description: "Nickname of user" })
nickname: string;
@ApiProperty({ type: String, description: "Last worksace slug of user" })
lastWorkspaceSlug: string;
@ApiProperty({ type: String, description: "Nickname of user", required: false })
nickname?: string;
@ApiProperty({ type: String, description: "Last worksace slug of user", required: false })
lastWorkspaceSlug?: string;
@ApiProperty({ type: Date, description: "Created date of user" })
createdAt: Date;
@ApiProperty({ type: Date, description: "Updated date of user" })
Expand Down
29 changes: 27 additions & 2 deletions backend/src/users/users.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { Controller, Get, Req } from "@nestjs/common";
import { ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags } from "@nestjs/swagger";
import { Body, Controller, Get, Put, Req } from "@nestjs/common";
import {
ApiBearerAuth,
ApiBody,
ApiConflictResponse,
ApiOkResponse,
ApiOperation,
ApiTags,
} from "@nestjs/swagger";
import { UsersService } from "./users.service";
import { AuthroizedRequest } from "src/utils/types/req.type";
import { FindUserResponse } from "./types/find-user-response.type";
import { ChangeNicknameDto } from "./dto/change-nickname.dto";

@ApiTags("Users")
@ApiBearerAuth()
Expand All @@ -19,4 +27,21 @@ export class UsersController {
async findOne(@Req() req: AuthroizedRequest): Promise<FindUserResponse> {
return this.usersService.findOne(req.user.id);
}

@Put("")
@ApiOperation({
summary: "Change the Nickname of the User",
description: "Change the nickname of the user",
})
@ApiBody({
type: ChangeNicknameDto,
})
@ApiOkResponse()
@ApiConflictResponse({ description: "The nickname conflicts" })
async changeNickname(
@Req() req: AuthroizedRequest,
@Body() changeNicknameDto: ChangeNicknameDto
): Promise<void> {
return this.usersService.changeNickname(req.user.id, changeNicknameDto.nickname);
}
}
3 changes: 2 additions & 1 deletion backend/src/users/users.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Module } from "@nestjs/common";
import { UsersService } from "./users.service";
import { PrismaService } from "src/db/prisma.service";
import { UsersController } from "./users.controller";
import { CheckService } from "src/check/check.service";

@Module({
providers: [UsersService, PrismaService],
providers: [UsersService, PrismaService, CheckService],
exports: [UsersService],
controllers: [UsersController],
})
Expand Down
Loading

0 comments on commit 52f7a08

Please sign in to comment.