From 5d07426e23118acc8c57cc0a30b198baa1a9dbe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AC=B4=EB=9D=BC=EC=BF=A0=EB=AA=A8?= Date: Sun, 6 Oct 2024 22:09:08 +0900 Subject: [PATCH] wip --- src/backend/controllers/questions.ts | 104 ++++++++++++++++++++++++ src/backend/controllers/session.ts | 1 + src/backend/models/entities/question.ts | 71 ++++++++++++++++ src/backend/models/index.ts | 4 +- src/common/functions/create-gacha.ts | 14 +++- src/common/functions/format.ts | 4 +- src/common/types/question.ts | 13 +++ 7 files changed, 204 insertions(+), 7 deletions(-) create mode 100644 src/backend/controllers/questions.ts create mode 100644 src/backend/models/entities/question.ts create mode 100644 src/common/types/question.ts diff --git a/src/backend/controllers/questions.ts b/src/backend/controllers/questions.ts new file mode 100644 index 0000000..ccb7063 --- /dev/null +++ b/src/backend/controllers/questions.ts @@ -0,0 +1,104 @@ +/** + * バージョン情報など、サーバーのメタデータを返すAPI + * @author Xeltica + */ + +import { BadRequestError, Body, CurrentUser, Delete, Get, JsonController, NotFoundError, OnUndefined, Param, Post, Put } from 'routing-controllers'; +import { IUser } from '../../common/types/user.js'; +import { Questions } from '../models/index.js'; +import { AnnounceCreate } from './body/announce-create.js'; +import { AnnounceUpdate } from './body/announce-update.js'; +import { IdProp } from './body/id-prop.js'; + +@JsonController('/questions') +export class AnnouncementController { + @Get() get() { + const query = Questions.createQueryBuilder('announcement') + .orderBy('"announcement"."createdAt"', 'DESC'); + + return query.getMany(); + } + + @OnUndefined(204) + @Post() async post(@CurrentUser({ required: true }) user: IUser, @Body({required: true}) {title, body}: AnnounceCreate) { + if (!user.isAdmin) { + throw new BadRequestError('Not an Admin'); + } + if (!title || !body) { + throw new BadRequestError(); + } + await Questions.insert({ + createdAt: new Date(), + title, + body, + }); + } + + @OnUndefined(204) + @Put() async update(@CurrentUser({ required: true }) user: IUser, @Body() {id, title, body}: AnnounceUpdate) { + if (!user.isAdmin) { + throw new BadRequestError('Not an Admin'); + } + if (!id || !title || !body) { + throw new BadRequestError(); + } + if (!(await Questions.findOne(id))) { + throw new NotFoundError(); + } + + await Questions.update(id, { + title, + body, + }); + } + + @OnUndefined(204) + @Post('/like/:id') async like(@CurrentUser({ required: true }) user: IUser, @Param('id') id: string) { + if (!user.isAdmin) { + throw new BadRequestError('Not an Admin'); + } + const idNumber = Number(id); + if (isNaN(idNumber)) { + throw new NotFoundError(); + } + if (!id) { + throw new BadRequestError(); + } + + const announcement = await Questions.findOne(Number(idNumber)); + + if (!announcement) { + throw new NotFoundError(); + } + + await Questions.update(id, { + like: announcement.like + 1, + }); + + return announcement.like + 1; + } + + @Delete() async delete(@CurrentUser({ required: true }) user: IUser, @Body() {id}: IdProp) { + if (!user.isAdmin) { + throw new BadRequestError('Not an Admin'); + } + + if (!id) { + throw new BadRequestError(); + } + + await Questions.delete(id); + } + + @Get('/:id') async getDetail(@Param('id') id: string) { + const idNumber = Number(id); + if (isNaN(idNumber)) { + throw new NotFoundError(); + } + const announcement = await Questions.findOne(idNumber); + if (!announcement) { + throw new NotFoundError(); + } + return announcement; + } +} diff --git a/src/backend/controllers/session.ts b/src/backend/controllers/session.ts index d957607..92473c4 100644 --- a/src/backend/controllers/session.ts +++ b/src/backend/controllers/session.ts @@ -40,6 +40,7 @@ export class SessionController { if (setting.notificationIcon !== undefined) s.notificationIcon = setting.notificationIcon; if (setting.useRanking !== undefined) s.useRanking = setting.useRanking; if (setting.appendHashtag !== undefined) s.appendHashtag = setting.appendHashtag; + if se if (Object.keys(s).length === 0) return; await updateUser(user.username, user.host, s); } diff --git a/src/backend/models/entities/question.ts b/src/backend/models/entities/question.ts new file mode 100644 index 0000000..81a74d3 --- /dev/null +++ b/src/backend/models/entities/question.ts @@ -0,0 +1,71 @@ +import { Entity, PrimaryGeneratedColumn, Column, Index, JoinColumn, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { IQuestion } from '../../../common/types/question.js'; + +@Entity() +export class Question implements IQuestion { + @PrimaryGeneratedColumn() + public id: number; + + @Index() + @Column({ + type: 'number' as const, + nullable: true, + comment: 'The ID of reply target.', + }) + public recipientId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public recipient: User; + + @Index() + @Column({ + type: 'number' as const, + nullable: true, + comment: 'The ID of reply target.', + }) + public senderId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public sender: User | null; + + @Column({ + type: 'timestamp without time zone', + }) + public createdAt: Date; + + @Column({ + type: 'varchar', + length: 8192, + }) + public question: string; + + @Column({ + type: 'timestamp without time zone', + }) + public repliedAt: Date; + + @Column({ + type: 'varchar', + length: 8192, + }) + public reply: string; + + @Column({ + type: 'boolean', + default: false, + }) + public isDeleted: boolean; + + @Column({ + type: 'boolean', + default: false, + }) + public isNSFW: boolean; +} diff --git a/src/backend/models/index.ts b/src/backend/models/index.ts index 5008257..486ff9a 100644 --- a/src/backend/models/index.ts +++ b/src/backend/models/index.ts @@ -1,8 +1,10 @@ import { User } from './entities/user.js'; import { UsedToken } from './entities/used-token.js'; -import { getRepository } from 'typeorm'; import { Announcement } from './entities/announcement.js'; +import { Question } from './entities/question.js'; +import { getRepository } from 'typeorm'; export const Users = getRepository(User); export const UsedTokens = getRepository(UsedToken); export const Announcements = getRepository(Announcement); +export const Questions = getRepository(Question); diff --git a/src/common/functions/create-gacha.ts b/src/common/functions/create-gacha.ts index d5fe223..9463ba0 100644 --- a/src/common/functions/create-gacha.ts +++ b/src/common/functions/create-gacha.ts @@ -1,11 +1,17 @@ const allTexts: string[] = [ '아무말 빔----', '휴대폰용 보험은 가입하셨나요?', + '아, 뭐하려고 했더라...?', + '여기 재밌는 이야기가 있어요! https://youtu.be/dQw4w9WgXcQ', + '모니터를 청소하려고 물을 뿌렸더니, 아무것도 보이지 않게 됐어요...' ]; -const getRandomText = () => allTexts[Math.floor(Math.random() * allTexts.length)]; +function getRandomText(texts: string[]): string { + if (texts.length < 1) texts = allTexts; + return texts[Math.floor(Math.random() * texts.length)]; +} -export const createGacha = () => { - const result = getRandomText(); +export function createGacha(texts: string[]): string { + const result = getRandomText(texts); return result; -}; +} diff --git a/src/common/functions/format.ts b/src/common/functions/format.ts index 5130929..11909be 100644 --- a/src/common/functions/format.ts +++ b/src/common/functions/format.ts @@ -25,7 +25,7 @@ export const variables: Record = { username: (_, user) => String(user.username), host: (_, user) => String(user.host), rating: (_, user) => String(user.rating), - gacha: () => createGacha(), + gacha: () => createGacha(user.gachaTexts), }; const variableRegex = /\{([a-zA-Z0-9_]+?)}/g; @@ -47,7 +47,7 @@ export const format = (user: IUser, count: Count): string => { return !v ? m : typeof v === 'function' ? v(score, user) : v; }); if (user.appendHashtag) { - result = result + '\n\n#misshaialert'; + result = result + '\n\nPowered by #misshaialert'; } return result; }; diff --git a/src/common/types/question.ts b/src/common/types/question.ts new file mode 100644 index 0000000..2a87818 --- /dev/null +++ b/src/common/types/question.ts @@ -0,0 +1,13 @@ +import { IUser } from './user.js'; + +export interface IQuestion { + id: number; + recipient: IUser; + sender: IUser | null; + createdAt: Date; + question: string; + repliedAt: Date; + reply: string; + isDeleted: boolean; + isNSFW: boolean; +}