Chg: exactOptionalPropertyTypesをfalseに変更 / Chg(Security): トークンが不正な場合のエラーを全てtoken_invalidに変更 / New: channelテーブル・リポジトリ / Chg: configテーブルのvalueをstringに / Chg: configテーブルのlengthを4096に / New: messageテーブル / Chg: 安全のためuserテーブルのOptionalPropsにidを追加 / New: channel/createエンドポイント / New: channel/listエンドポイント / New: channel/editエンドポイント / Enhance: primary/signupエンドポイントの重複エラーの実装で末尾カンマなどの改善 / Chg: setup/initializationのdescriptionに最大文字数4096を制定 / Chg: serverInfoをdefault exportからexportに変更 / New: フロントエンドでmeを読み込み / New: フロントエンドでchannelを読み込み / New: client.tsでトークンがある場合はトークンを指定 / Chg: clientをrefに / Del: IndexedDBからserverテーブルを削除 / Fix: Dexieのclassに命名 / Feat: フロントエンドでのサインインページ / Fix: L.jsで任意のbodyがあるエンドポイントが定義できない問題を修正 / Del: L.jsのserver-infoでの不要なimportを削除 / Fix: L.jsでトークンのエラーを追加 / Fix: L.jsのUserSchemaにlastUsedAtを追加

This commit is contained in:
2026-03-30 11:37:57 +09:00
parent 6b54ae4306
commit d129c95aa4
33 changed files with 683 additions and 39 deletions
@@ -0,0 +1,43 @@
import generateUniqueId from "@/lib/id";
import { Entity, EntityRepositoryType, Index, ManyToOne, OptionalProps, PrimaryKey, Property } from "@mikro-orm/core";
import { UserEntity } from "@/modules/entities/User";
import { ChannelRepository } from "@/modules/repositories/Channel";
@Entity({
tableName: "channel",
repository: () => ChannelRepository,
})
export class ChannelEntity {
[EntityRepositoryType]?: ChannelRepository;
[OptionalProps]?: "id" | "createdAt";
@PrimaryKey({
type: "string",
length: 10,
onCreate: () => generateUniqueId(),
})
id!: string;
@Property({
type: "string",
length: 20,
unique: true,
})
@Index()
name!: string;
@Property({
type: "string",
length: 4096,
})
description!: string;
@ManyToOne(() => UserEntity)
createdBy!: UserEntity;
@Property({
type: "datetime",
onCreate: () => new Date(),
})
createdAt!: Date;
}
@@ -11,6 +11,9 @@ export class ConfigEntity {
@PrimaryKey({ type: "string" })
name!: string;
@Property({ type: "text" })
@Property({
type: "string",
length: 4096,
})
value!: string;
}
@@ -0,0 +1,36 @@
import generateUniqueId from "@/lib/id";
import { Entity, Index, ManyToOne, OptionalProps, PrimaryKey, Property } from "@mikro-orm/core";
import { UserEntity } from "@/modules/entities/User";
import { ChannelEntity } from "@/modules/entities/Channel";
@Entity({
tableName: "message",
})
export class MessageEntity {
[OptionalProps]?: "id" | "createdAt";
@PrimaryKey({
type: "string",
length: 10,
onCreate: () => generateUniqueId(),
})
id!: string;
@Property({
type: "string",
length: 4096,
})
message!: string;
@ManyToOne(() => ChannelEntity)
channel!: ChannelEntity;
@ManyToOne(() => UserEntity)
createdBy!: UserEntity;
@Property({
type: "datetime",
onCreate: () => new Date(),
})
createdAt!: Date;
}
@@ -8,7 +8,7 @@ import generateUniqueId from "@/lib/id";
})
export class UserEntity {
[EntityRepositoryType]?: UserRepository;
[OptionalProps]?: "profile" | "isSuspended" | "createdAt";
[OptionalProps]?: "id" | "profile" | "isSuspended" | "createdAt";
@PrimaryKey({
type: "string",
@@ -0,0 +1,65 @@
import { EntityRepository } from "@mikro-orm/postgresql";
import type { ChannelEntity } from "@/modules/entities/Channel";
import { ErrorBase } from "@/errors";
import z from "zod/v3";
import { UserRepository } from "@/modules/repositories/User";
export class ChannelRepository extends EntityRepository<ChannelEntity> {
public static schema = z.object({
name: z.string().trim().min(1).max(20),
description: z.string().trim().min(1).max(4096),
userid: UserRepository.schema.shape.userid,
});
async findChannel(limit: number = 20, sinceData?: string) {
let since = sinceData ?? new Date();
if (
sinceData &&
!isNaN(new Date(sinceData).getTime())
) {
const itChannel = await this.findOne({ id: sinceData });
if (!itChannel) {
return ErrorBase({
bad: "client",
code: "channel_not_found",
message: "対象のチャンネルが見つかりませんでした。",
});
}
since = itChannel.createdAt;
}
const findResult = await this.find({
createdAt: {
$lt: since,
},
}, {
orderBy: {
createdAt: "DESC",
},
limit: limit,
});
return findResult ?? [];
}
async createChannel(data: z.infer<typeof ChannelRepository.schema>) {
const channel = this.create({
...data,
createdBy: data.userid,
});
await this.em.persist(channel).flush();
return channel.id;
}
async editChannel(
target: ChannelEntity,
data: Partial<Omit<z.infer<typeof ChannelRepository.schema>, "userid">>
) {
await this.nativeUpdate(target, data);
return target.id;
}
}
@@ -52,9 +52,9 @@ export class TokenRepository extends EntityRepository<TokenEntity> {
)
return ErrorBase({
bad: "client",
code: "token_length_wrong",
message: "トークンの文字数が不正です。",
});
code: "token_invalid",
message: "トークンが不正です。",
})
const token = await this.findOne({ name: tokenArr[0] }, { populate: ["user"] });
if (!token)