Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
01cfd8e
refactor(articles): reorganize files and add architecture documentation
yamcodes Apr 17, 2025
3903bfe
Refactor articles and comments modules with schema and DTO separation
yamcodes Apr 17, 2025
6562e21
feat: add article mappers and domain interfaces, update architecture …
yamcodes Apr 19, 2025
da9309a
Add project structure docs and fix broken markdown link
yamcodes Apr 19, 2025
3948808
Add architecture diagram showing layer relationships and data flow
yamcodes Apr 19, 2025
d63fe60
refactor: consolidate article mappers and rename ArticleInDb to Artic…
yamcodes Apr 19, 2025
52cff00
Add ArticleDto schema with type validation for article data
yamcodes Apr 19, 2025
673bf6e
Reorder imports alphabetically across multiple files
yamcodes Apr 19, 2025
c75a113
Consolidate article schemas and add body field to article interface
yamcodes Apr 19, 2025
ace8438
Fix import order and reorganize imports in articles schema
yamcodes Apr 19, 2025
07d04ca
refactor: reorganize articles module and improve type safety
yamcodes Apr 19, 2025
87f3064
Refactor articles service to separate query params from options, fix …
yamcodes Apr 19, 2025
f65bb7e
refactor: improve articles feed filtering and add followed users lookup
yamcodes Apr 19, 2025
c3672aa
Refactor article response mapping and add domain model transformation
yamcodes Apr 19, 2025
07f0cb4
Refactor feed endpoint to handle pagination and add article response …
yamcodes Apr 19, 2025
61c7be5
refactor: replace toResponse with toDomain and fix author id access
yamcodes Apr 19, 2025
05a02a6
Add delete article endpoint and improve response handling in article …
yamcodes Apr 19, 2025
c119d2d
Merge branch 'main' into layered
yamcodes Jun 6, 2025
2a0352d
chore: update bun.lockb to reflect dependency changes
yamcodes Jun 6, 2025
86c3daf
chore: update bun.lockb to synchronize with dependency changes
yamcodes Jun 6, 2025
e7e810a
docs: update architecture documentation for clarity and formatting im…
yamcodes Jun 6, 2025
6b0d502
docs: enhance architecture documentation with clearer type convention…
yamcodes Jun 6, 2025
76889ad
refactor: streamline article response structure and enhance service m…
yamcodes Jun 6, 2025
752dff7
refactor: update article response mapping and enhance DTO exports
yamcodes Jun 6, 2025
ffe58a0
refactor: update article imports and enhance comment repository type …
yamcodes Jun 7, 2025
49f999f
refactor: update summary descriptions for articles, tags, and users
yamcodes Jun 7, 2025
7337557
refactor: reorganize article creation endpoint in articles controller
yamcodes Jun 7, 2025
c31c49c
refactor: update article feed query handling and remove unused DTO
yamcodes Jun 7, 2025
5b4fc90
fix: correct spelling of 'Registeration' to 'Registration' in users p…
yamcodes Jun 7, 2025
1fe5c27
feat: enhance articles endpoint description for clarity
yamcodes Jun 7, 2025
e46f93b
refactor: improve articles controller and DTO structure
yamcodes Jun 7, 2025
b4cb3d2
refactor: improve article domain mapping and tag handling
yamcodes Jun 7, 2025
d0fed98
refactor: optimize tag handling and article domain mapping
yamcodes Jun 7, 2025
f4426b0
refactor: update comment response structure and remove article mapping
yamcodes Jun 7, 2025
2cd3563
refactor: improve find method parameter handling in ArticlesRepository
yamcodes Jun 7, 2025
05b3823
refactor: streamline article query handling in ArticlesRepository
yamcodes Jun 7, 2025
64347a2
feat: enhance error handling and article slug generation
yamcodes Jun 7, 2025
8379741
refactor: improve follow/unfollow user functionality in profiles service
yamcodes Jun 7, 2025
031803b
refactor: improve follow/unfollow logic in profiles service
yamcodes Jun 7, 2025
3bae8b0
refactor: remove unnecessary logging in error handling
yamcodes Jun 7, 2025
cf7ca8e
refactor: enhance error handling in profiles service
yamcodes Jun 7, 2025
69fc970
refactor: update import paths and clean up article feed interface
yamcodes Jun 7, 2025
44bfb20
refactor: restore CommentsRepository and CommentsService imports in a…
yamcodes Jun 7, 2025
0f37339
refactor: update comment handling and type definitions
yamcodes Jun 7, 2025
5538df8
refactor: update article-feed-row interface to use ProfileFeed type
yamcodes Jun 7, 2025
fea27e6
Update src/errors.ts
yamcodes Jun 7, 2025
88f1cea
refactor: update error message in unfollow user logic
yamcodes Jun 7, 2025
8aaf4a6
refactor: simplify ProfilesService and remove UsersRepository dependency
yamcodes Jun 7, 2025
636614a
refactor: add TODO comment for future refactoring in generateProfileR…
yamcodes Jun 7, 2025
0212a89
refactor: enforce minimum length for tagList items in article DTOs
yamcodes Jun 7, 2025
50a52fb
refactor: update comments schema to improve data integrity and import…
yamcodes Jun 7, 2025
680038b
refactor: simplify ProfilesService initialization in articles module
yamcodes Jun 7, 2025
59bd5a7
refactor: update articles schema to include comments import
yamcodes Jun 7, 2025
b8ab437
refactor: update error messages in article creation and update methods
yamcodes Jun 7, 2025
a3b1d2b
refactor: remove unused slug generation methods from ArticlesRepository
yamcodes Jun 7, 2025
29e4cbd
feat: implement unique slug generation for articles
yamcodes Jun 7, 2025
6eaa6f1
refactor: enhance article creation and update methods
yamcodes Jun 7, 2025
897e395
refactor: add TODO comments for race condition handling in article me…
yamcodes Jun 7, 2025
e065c53
feat: enhance article filtering capabilities in repository
yamcodes Jun 7, 2025
edf6f2e
refactor: enforce minimum length for optional fields in ListArticlesQ…
yamcodes Jun 7, 2025
2b0d57c
refactor: update import paths for articles schema
yamcodes Jun 7, 2025
af86d2e
docs: update architecture and project structure documentation
yamcodes Jun 7, 2025
45e2957
docs: improve project structure documentation formatting
yamcodes Jun 7, 2025
239e7c4
docs: refine architecture and project structure documentation
yamcodes Jun 7, 2025
a741161
refactor: update comments schema and related imports
yamcodes Jun 8, 2025
aab8c39
feat: integrate comments controller into app module and enhance docum…
yamcodes Jun 8, 2025
cd7c66c
refactor: replace outdated comment DTOs with new interfaces
yamcodes Jun 8, 2025
ef3c2b2
refactor: streamline comments module and remove unused mappers
yamcodes Jun 8, 2025
c62c736
refactor: consolidate article mappers and update import paths
yamcodes Jun 8, 2025
64ab5be
refactor: enhance comment mapping functionality and update imports
yamcodes Jun 8, 2025
a5ed353
refactor: standardize import paths across article-related files
yamcodes Jun 8, 2025
a5ffc33
refactor: enhance comments module with article and tag services integ…
yamcodes Jun 8, 2025
444b4d0
refactor: update comment deletion logic and parameter validation
yamcodes Jun 8, 2025
a2cf974
refactor: optimize article filtering logic in repository
yamcodes Jun 8, 2025
74533af
refactor: optimize favorited and following checks in mappers
yamcodes Jun 8, 2025
a629b38
refactor: update comments schema to use integer type for IDs
yamcodes Jun 8, 2025
40d61fe
Merge branch 'layered-phase1-articles' into layered-phase2-comments
yamcodes Jun 8, 2025
f0065d7
Merge branch 'layered' of https://github.com/agnyz/the-bed-stack into…
yamcodes Jun 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { tagsPlugin } from '@tags/tags.plugin';
import { usersPlugin } from '@users/users.plugin';
import { Elysia } from 'elysia';
import { description, title, version } from '../package.json';
import { commentsController } from './comments/comments.controller';

// the file name is in the spirit of NestJS, where app module is the device in charge of putting together all the pieces of the app
// see: https://docs.nestjs.com/modules
Expand Down Expand Up @@ -60,6 +61,7 @@ export const setupApp = () => {
.use(usersPlugin)
.use(profilesPlugin)
.use(articlesController)
.use(commentsController)
.use(tagsPlugin),
);
};
70 changes: 2 additions & 68 deletions src/articles/articles.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { setupArticles } from '@articles/articles.module';
import { CommentResponseDto, CreateCommentDto } from '@comments/dto';
import { Elysia, t } from 'elysia';
import { Elysia } from 'elysia';
import {
ArticleFeedQueryDto,
ArticleResponseDto,
Expand All @@ -9,12 +8,7 @@ import {
ListArticlesQueryDto,
UpdateArticleDto,
} from './dto';
import {
toCommentResponse,
toCreateArticleInput,
toFeedResponse,
toResponse,
} from './mappers/articles.mapper';
import { toCreateArticleInput, toFeedResponse, toResponse } from './mappers';

export const articlesController = new Elysia().use(setupArticles).group(
'/articles',
Expand Down Expand Up @@ -177,66 +171,6 @@ export const articlesController = new Elysia().use(setupArticles).group(
},
},
)
.post(
'/:slug/comments',
async ({ body, params, store, request }) => {
const comment = await store.commentsService.createComment(
params.slug,
body.comment,
await store.authService.getUserIdFromHeader(request.headers),
);
return toCommentResponse(comment);
},
{
beforeHandle: app.store.authService.requireLogin,
body: CreateCommentDto,
response: CommentResponseDto,
detail: {
summary: 'Add Comments to an Article',
},
},
)
.get(
'/:slug/comments',
async ({ params, store, request }) => {
const userId = await store.authService.getOptionalUserIdFromHeader(
request.headers,
);
const comments = await store.commentsService.getComments(
params.slug,
userId === null ? undefined : userId,
);
return { comments: comments.map(toCommentResponse) };
},
{
response: t.Object({
comments: t.Array(CommentResponseDto),
}),
detail: {
summary: 'Get Comments from an Article',
},
},
)
.delete(
'/:slug/comments/:id',
async ({ params, store, request }) => {
await store.commentsService.deleteComment(
params.slug,
Number.parseInt(params.id, 10),
await store.authService.getUserIdFromHeader(request.headers),
);
},
{
beforeHandle: app.store.authService.requireLogin,
params: t.Object({
slug: t.String(),
id: t.String(),
}),
detail: {
summary: 'Delete Comment',
},
},
)
.post(
'/:slug/favorite',
async ({ params, store, request }) => {
Expand Down
8 changes: 0 additions & 8 deletions src/articles/articles.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { db } from '@/database.providers';
import { ArticlesRepository } from '@articles/articles.repository';
import { ArticlesService } from '@articles/articles.service';
import { AuthService } from '@auth/auth.service';
import { CommentsRepository } from '@comments/comments.repository';
import { CommentsService } from '@comments/comments.service';
import { ProfilesRepository } from '@profiles/profiles.repository';
import { ProfilesService } from '@profiles/profiles.service';
import { TagsRepository } from '@tags/tags.repository';
Expand All @@ -12,7 +10,6 @@ import { Elysia } from 'elysia';

export const setupArticles = () => {
const articlesRepository = new ArticlesRepository(db);
const commentsRepository = new CommentsRepository(db);
const profilesRepository = new ProfilesRepository(db);
const tagsRepositry = new TagsRepository(db);
const profilesService = new ProfilesService(profilesRepository);
Expand All @@ -22,14 +19,9 @@ export const setupArticles = () => {
profilesService,
tagsService,
);
const commentsService = new CommentsService(
commentsRepository,
profilesService,
);
const authService = new AuthService();
return new Elysia().state(() => ({
articlesService,
authService,
commentsService,
}));
};
4 changes: 2 additions & 2 deletions src/articles/articles.schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { users } from '@/users/users.model';
import { comments } from '@comments/schema';
import { comments } from '@comments/comments.schema';
import { articleTags } from '@tags/tags.model';
import { users } from '@users/users.model';
import { relations } from 'drizzle-orm';
import {
integer,
Expand Down
12 changes: 4 additions & 8 deletions src/articles/articles.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import { AuthorizationError, BadRequestError, ConflictError } from '@/errors';
import type { ProfilesService } from '@/profiles/profiles.service';
import { slugify } from '@/utils/slugify';
import type { ArticlesRepository } from '@articles/articles.repository';
import { AuthorizationError, BadRequestError, ConflictError } from '@errors';
import type { ProfilesService } from '@profiles/profiles.service';
import type { TagsService } from '@tags/tags.service';
import { slugify } from '@utils/slugify';
import { NotFoundError } from 'elysia';
import type {
CreateArticleInput,
IArticle,
IArticleFeed,
UpdateArticleInput,
} from './interfaces';
import {
toDomain,
toFeedDomain,
toNewArticleRow,
} from './mappers/articles.mapper';
import { toDomain, toFeedDomain, toNewArticleRow } from './mappers';

type FindFilters = {
tag?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/articles/interfaces/article-row.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Profile } from '@/profiles/profiles.schema';
import type { ArticleTag } from '@/tags/tags.schema';
import type { Profile } from '@profiles/profiles.schema';
import type { InferSelectModel } from 'drizzle-orm';
import type { articles, favoriteArticles } from '../articles.schema';

Expand Down
161 changes: 0 additions & 161 deletions src/articles/mappers/articles.mapper.ts

This file was deleted.

6 changes: 6 additions & 0 deletions src/articles/mappers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './to-domain.mapper';
export * from './to-response.mapper';
export * from './to-create-article-input.mapper';
export * from './to-new-article-row.mapper';
export * from './to-feed-domain.mapper';
export * from './to-feed-response.mapper';
13 changes: 13 additions & 0 deletions src/articles/mappers/to-create-article-input.mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { CreateArticleDto } from '../dto';
import type { CreateArticleInput } from '../interfaces';

export function toCreateArticleInput({
article,
}: CreateArticleDto): CreateArticleInput {
return {
title: article.title,
description: article.description,
body: article.body,
tagList: article.tagList ?? [],
};
}
34 changes: 34 additions & 0 deletions src/articles/mappers/to-domain.mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { ArticleRow, IArticle } from '../interfaces';

type ToDomainOptions = {
tagList?: string[];
currentUserId?: number;
};

export function toDomain(
article: ArticleRow,
{ tagList, currentUserId }: ToDomainOptions,
): IArticle {
return {
id: article.id,
slug: article.slug,
title: article.title,
description: article.description,
tagList: tagList ?? article.tags.map((t) => t.tagName),
createdAt: article.createdAt,
updatedAt: article.updatedAt,
favorited: article.favoritedBy.some(
(user) => user.userId === currentUserId,
),
favoritesCount: article.favoritedBy.length,
body: article.body,
author: {
username: article.author.username,
bio: article.author.bio,
image: article.author.image,
following: article.author.followers.some(
(follower) => follower.followerId === currentUserId,
),
},
};
}
Loading