AI Implementation feature(823): Landing Page and Login/Register Modal (#11)

This commit was merged in pull request #11.
This commit is contained in:
2026-07-21 18:34:45 +00:00
parent 8476e4a59f
commit 685a8bca84
51 changed files with 2354 additions and 226 deletions
@@ -0,0 +1,19 @@
import { Controller, Get } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { Public } from '../../common/decorators/public.decorator';
import { BlogService } from './blog.service';
import { PublicBlogList } from './dto/blog.dto';
@ApiTags('blog')
@Controller('api/v1/blog')
export class BlogController {
constructor(private readonly blog: BlogService) {}
@Public()
@Get('posts')
@ApiOperation({ summary: 'Public list of published blog posts' })
async listPosts(): Promise<PublicBlogList> {
const posts = await this.blog.listPublished();
return { posts };
}
}
+12
View File
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BlogPostEntity } from '../../database/entities/blog-post.entity';
import { BlogController } from './blog.controller';
import { BlogService } from './blog.service';
@Module({
imports: [TypeOrmModule.forFeature([BlogPostEntity])],
controllers: [BlogController],
providers: [BlogService],
})
export class BlogModule {}
+26
View File
@@ -0,0 +1,26 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BlogPostEntity } from '../../database/entities/blog-post.entity';
import { PublicBlogPost } from './dto/blog.dto';
@Injectable()
export class BlogService {
constructor(
@InjectRepository(BlogPostEntity)
private readonly posts: Repository<BlogPostEntity>,
) {}
async listPublished(): Promise<PublicBlogPost[]> {
const rows = await this.posts.find({
where: { status: 'published' },
order: { publishedAt: 'DESC' },
});
return rows.map((row) => ({
id: row.id,
title: row.title,
publishedAt: row.publishedAt ?? row.createdAt,
bodyMd: row.bodyMd ?? '',
}));
}
}
+14
View File
@@ -0,0 +1,14 @@
import { z } from 'zod';
export const PublicBlogPostSchema = z.object({
id: z.string(),
title: z.string(),
publishedAt: z.string(),
bodyMd: z.string(),
});
export type PublicBlogPost = z.infer<typeof PublicBlogPostSchema>;
export const PublicBlogListSchema = z.object({
posts: z.array(PublicBlogPostSchema),
});
export type PublicBlogList = z.infer<typeof PublicBlogListSchema>;