Skip to content

Commit

Permalink
update content lists by accountId
Browse files Browse the repository at this point in the history
  • Loading branch information
orlein committed Nov 28, 2024
1 parent d34a6f4 commit 6218205
Show file tree
Hide file tree
Showing 18 changed files with 451 additions and 40 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@oz-adv/backend",
"version": "0.0.2 (2024-11-28.005)",
"version": "0.0.2 (2024-11-28.006)",
"description": "Backend for the Oz-Adv project",
"type": "module",
"scripts": {
Expand Down
15 changes: 15 additions & 0 deletions src/account/account-api-live.mts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ export const AccountApiLive = HttpApiBuilder.group(Api, 'account', (handlers) =>
accountService.signIn(payload).pipe(withSystemActor),
)
.handle('me', () => CurrentAccount)
.handle('findPosts', ({ path, urlParams }) =>
accountService.findPosts(urlParams, path.accountId),
)
.handle('findComments', ({ path, urlParams }) =>
accountService.findComments(urlParams, path.accountId),
)
.handle('findChallenges', ({ path, urlParams }) =>
accountService.findChallenges(urlParams, path.accountId),
)
.handle('findChallengeEvents', ({ path, urlParams }) =>
accountService.findAllChallengeEvents(urlParams, path.accountId),
)
.handle('findLikes', ({ path, urlParams }) =>
accountService.findAllLikes(urlParams, path.accountId),
)
.handle('invalidate', ({ headers }) =>
accountService.invalidate(headers['refresh-token']),
);
Expand Down
118 changes: 114 additions & 4 deletions src/account/account-api.mts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { Authentication } from '@/auth/authentication.mjs';
import { Unauthorized } from '@/auth/error-403.mjs';
import { ChallengeEventView } from '@/challenge-event/challenge-event-schema.mjs';
import { ChallengeView } from '@/challenge/challenge-schema.mjs';
import { CommentView } from '@/comment/comment-schema.mjs';
import {
GeneratingSaltError,
HashingPasswordError,
} from '@/crypto/crypto-error.mjs';
import { VerifyTokenError } from '@/crypto/token-error.mjs';
import { ServerError } from '@/misc/common-error.mjs';
import { FindManyResultSchema } from '@/misc/find-many-result-schema.mjs';
import { FindManyUrlParams } from '@/misc/find-many-url-params-schema.mjs';
import { PostView } from '@/post/post-schema.mjs';
import { Tag } from '@/tag/tag-schema.mjs';
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from '@effect/platform';
import { Schema } from 'effect';
import {
Expand All @@ -16,7 +23,6 @@ import {
import { Account, AccountId } from './account-schema.mjs';
import { SignIn } from './sign-in-schema.mjs';
import { SignUp } from './sign-up-schema.mjs';
import { Tag } from '@/tag/tag-schema.mjs';

export class AccountApi extends HttpApiGroup.make('account')
.add(
Expand All @@ -26,8 +32,8 @@ export class AccountApi extends HttpApiGroup.make('account')
accountId: AccountId,
}),
)
.addSuccess(Account.json)
.addError(AccountNotFound)
.addSuccess(Account.json)
.annotateContext(
OpenApi.annotations({
title: '계정 조회',
Expand All @@ -51,14 +57,118 @@ export class AccountApi extends HttpApiGroup.make('account')
.annotateContext(
OpenApi.annotations({
title: '계정 태그 조회',
description:
'계정의 태그를 조회합니다. 계정이 존재하지 않는 경우 404를 반환합니다. 다른 사람의 계정을 조회할 수 있습니다. 로그인하지 않아도 사용할 수 있습니다.',
description: '내 계정의 태그를 조회합니다.',
override: {
summary: '(사용가능) 계정 태그 조회',
},
}),
),
)
.add(
HttpApiEndpoint.get('findChallenges', '/:accountId/challenges')
.setPath(
Schema.Struct({
accountId: AccountId,
}),
)
.setUrlParams(FindManyUrlParams)
.addError(AccountNotFound)
.addSuccess(FindManyResultSchema(ChallengeView))
.annotateContext(
OpenApi.annotations({
title: '작성한 챌린지 조회',
description:
'이 사용자가 작성한 챌린지를 조회합니다. 계정이 존재하지 않는 경우 404를 반환합니다. 다른 사람의 계정을 조회할 수 있습니다. 로그인하지 않아도 사용할 수 있습니다.',
override: {
summary: '(사용가능) 작성한 챌린지 조회',
},
}),
),
)
.add(
HttpApiEndpoint.get('findPosts', '/:accountId/posts')
.setPath(
Schema.Struct({
accountId: AccountId,
}),
)
.setUrlParams(FindManyUrlParams)
.addError(AccountNotFound)
.addSuccess(FindManyResultSchema(PostView))
.annotateContext(
OpenApi.annotations({
title: '작성한 게시글 조회',
description:
'이 사용자가 작성한 포스트를 조회합니다. 계정이 존재하지 않는 경우 404를 반환합니다. 다른 사람의 계정을 조회할 수 있습니다. 로그인하지 않아도 사용할 수 있습니다.',
override: {
summary: '(사용가능) 작성한 게시글 조회',
},
}),
),
)
.add(
HttpApiEndpoint.get('findLikes', '/:accountId/likes')
.setPath(
Schema.Struct({
accountId: AccountId,
}),
)
.setUrlParams(FindManyUrlParams)
.addError(AccountNotFound)
.addSuccess(FindManyResultSchema(Schema.Any))
.annotateContext(
OpenApi.annotations({
title: '좋아요한 이력 조회',
description:
'이 사용자가 좋아요한 내용들을 조회합니다. 계정이 존재하지 않는 경우 404를 반환합니다. 다른 사람의 계정을 조회할 수 있습니다. 로그인하지 않아도 사용할 수 있습니다.',
override: {
summary: '(사용가능) 좋아요한 이력 조회',
},
}),
),
)
.add(
HttpApiEndpoint.get('findComments', '/:accountId/comments')
.setPath(
Schema.Struct({
accountId: AccountId,
}),
)
.setUrlParams(FindManyUrlParams)
.addError(AccountNotFound)
.addSuccess(FindManyResultSchema(CommentView.json))
.annotateContext(
OpenApi.annotations({
title: '댓글한 이력 조회',
description:
'이 사용자가 댓글단 내용을 조회합니다. 계정이 존재하지 않는 경우 404를 반환합니다. 다른 사람의 계정을 조회할 수 있습니다. 로그인하지 않아도 사용할 수 있습니다.',
override: {
summary: '(사용가능) 댓글한 이력 조회',
},
}),
),
)
.add(
HttpApiEndpoint.get('findChallengeEvents', '/:accountId/challenge-events')
.setPath(
Schema.Struct({
accountId: AccountId,
}),
)
.setUrlParams(FindManyUrlParams)
.addError(AccountNotFound)
.addSuccess(FindManyResultSchema(ChallengeEventView.json))
.annotateContext(
OpenApi.annotations({
title: '참여한 챌린지 이벤트 조회',
description:
'이 사용자가 참여한 챌린지 이벤트를 조회합니다. 계정이 존재하지 않는 경우 404를 반환합니다. 다른 사람의 계정을 조회할 수 있습니다. 로그인하지 않아도 사용할 수 있습니다.',
override: {
summary: '(사용가능) 참여한 챌린지 이벤트 조회',
},
}),
),
)
.add(
HttpApiEndpoint.patch('updateById', '/:accountId')
.setPath(
Expand Down
36 changes: 29 additions & 7 deletions src/account/account-policy.mts
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
import { policy } from '@/auth/authorization.mjs';
import { Effect, Layer } from 'effect';
import { AccountRepo } from './account-repo.mjs';
import { AccountId } from './account-schema.mjs';
import { policy } from '@/auth/authorization.mjs';

const make = Effect.gen(function* () {
const accountRepo = yield* AccountRepo;

const canUpdate = (toUpdate: AccountId) =>
policy('account', 'update', (actor) =>
Effect.succeed(actor.id === toUpdate || actor.role === 'admin'),
);

// const canRead = (toRead: AccountId) =>
// policy('account', 'read', (actor) =>
// Effect.succeed(actor.id === toRead || actor.role === 'admin'),
// );
const canRead = (toRead: AccountId) =>
policy(
'account',
'read',
(actor) =>
Effect.gen(function* () {
if (actor.id === toRead || actor.role === 'admin') {
return yield* Effect.succeed(true);
}
const isPrivate = yield* accountRepo.with(toRead, (account) =>
Effect.succeed(account.isPrivate),
);

if (isPrivate) {
return yield* Effect.succeed(false);
}

return false;
}),
'대상의 계정이 비공개상태이거나, 유효한 권한이 없습니다.',
);

const canReadSensitive = (toRead: AccountId) =>
policy('account', 'readSensitive', (actor) =>
Expand All @@ -20,7 +40,7 @@ const make = Effect.gen(function* () {

return {
canUpdate,
// canRead,
canRead,
canReadSensitive,
} as const;
});
Expand All @@ -29,5 +49,7 @@ export class AccountPolicy extends Effect.Tag('Account/AccountPolicy')<
AccountPolicy,
Effect.Effect.Success<typeof make>
>() {
static Live = Layer.effect(AccountPolicy, make);
static layer = Layer.effect(AccountPolicy, make);

static Live = this.layer.pipe(Layer.provide(AccountRepo.Live));
}
49 changes: 49 additions & 0 deletions src/account/account-service.mts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,22 @@ import { Account, AccountId } from './account-schema.mjs';
import { SignIn } from './sign-in-schema.mjs';
import { SignUp } from './sign-up-schema.mjs';
import { VerifyTokenError } from '@/crypto/token-error.mjs';
import { ChallengeEventService } from '@/challenge-event/challenge-event-service.mjs';
import { CommentService } from '@/comment/comment-service.mjs';
import { LikeService } from '@/like/like-service.mjs';
import { PostService } from '@/post/post-service.mjs';
import { FindManyUrlParams } from '@/misc/find-many-url-params-schema.mjs';
import { ChallengeService } from '@/challenge/challenge-service.mjs';

const make = Effect.gen(function* () {
const accountRepo = yield* AccountRepo;
const cryptoService = yield* CryptoService;
const tokenService = yield* TokenService;
const postService = yield* PostService;
const commentService = yield* CommentService;
const challengeService = yield* ChallengeService;
const challengeEventService = yield* ChallengeEventService;
const likeService = yield* LikeService;

const signUp = (signUp: SignUp) => {
const program = Effect.gen(function* () {
Expand Down Expand Up @@ -216,6 +227,34 @@ const make = Effect.gen(function* () {
}),
);

const findPosts = (params: FindManyUrlParams, accountId: AccountId) =>
postService
.findPosts(params, accountId)
.pipe(Effect.withSpan('AccountService.findPosts'));

const findComments = (params: FindManyUrlParams, accountId: AccountId) =>
commentService
.findAllPossiblyByAccountId(params, accountId)
.pipe(Effect.withSpan('AccountService.findPosts'));

const findChallenges = (params: FindManyUrlParams, accountId: AccountId) =>
challengeService
.findChallenges(params, accountId)
.pipe(Effect.withSpan('AccountService.findChallenges'));

const findAllChallengeEvents = (
params: FindManyUrlParams,
accountId: AccountId,
) =>
challengeEventService
.findChallengeEvents(params, accountId)
.pipe(Effect.withSpan('AccountService.findAllChallengeEvents'));

const findAllLikes = (params: FindManyUrlParams, accountId: AccountId) =>
likeService
.findAllLikes(params, accountId)
.pipe(Effect.withSpan('AccountService.findAllLikes'));

return {
signUp,
signIn,
Expand All @@ -226,6 +265,11 @@ const make = Effect.gen(function* () {
updateAccountById,
embellishAccount,
invalidate,
findPosts,
findComments,
findChallenges,
findAllChallengeEvents,
findAllLikes,
} as const;
});

Expand All @@ -239,6 +283,11 @@ export class AccountService extends Effect.Tag('AccountService')<
Layer.provide(AccountRepo.Live),
Layer.provide(CryptoService.Live),
Layer.provide(TokenService.Live),
Layer.provide(PostService.Live),
Layer.provide(CommentService.Live),
Layer.provide(LikeService.Live),
Layer.provide(ChallengeService.Live),
Layer.provide(ChallengeEventService.Live),
);

static Test = this.layer.pipe(Layer.provideMerge(SqlTest));
Expand Down
13 changes: 7 additions & 6 deletions src/api.mts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ export class Api extends HttpApi.empty
OpenApi.annotations({
title: '오즈 6기 심화반 챌린지 서비스를 위한 백엔드',
description: `최신변경점:
* 내가, 혹은 다른 누군가가 만든 챌린지 목록을 가져오는 기능 (2024-11-28.006)
* 내가, 혹은 다른 누군가가 참여중인 챌린지 목록을 가져오는 기능 (2024-11-28.006)
* 내가, 혹은 다른 누군가가 참여중인 챌린지 이벤트를 가져오는 기능 (2024-11-28.006)
* 내가, 혹은 다른 누군가가 쓴 글 목록을 가져오는 기능 (2024-11-28.006)
* 내가, 혹은 다른 누군가가 좋아요/싫어요한 글 목록을 가져오는 기능 (2024-11-28.006)
* 내가, 혹은 다른 누군가가 쓴 댓글 목록을 가져오는 기능 (2024-11-28.006)
* 챌린지 / 게시글의 태그를 삭제하는 기능 (2024-11-28.005)
* 게시글 삭제처리 softDelete로 변경 (2024-11-28.004)
* 챌린지 태그연결 api 위치변경: tag -> challenge (2024-11-28.003)
Expand All @@ -48,12 +54,7 @@ export class Api extends HttpApi.empty
* [유저가 참여한 챌린지]와 [유저가 쓴 글], [유저가 만든 챌린지]의 태그를 찾아, 그를 프로필에 표시할 수 있게 지원하는 기능 (2024-11-27.001)
예정변경점:
* 내가 만든 챌린지 목록을 가져오는 기능
* 내가 참여중인 챌린지 목록을 가져오는 기능
* 내가 참여중인 챌린지 이벤트를 가져오는 기능
* 내가 쓴 글 목록을 가져오는 기능
* 내가 좋아요/싫어요한 글 목록을 가져오는 기능
* 내가 쓴 댓글 목록을 가져오는 기능
* 버그 리포트시 대응예정
`,
version: version,
override: {},
Expand Down
Loading

0 comments on commit 6218205

Please sign in to comment.