From 0b37979915ff4e3d8666d8cb3cb140f69d525810 Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Thu, 19 Dec 2019 02:59:43 +0900 Subject: [PATCH 1/5] wip --- src/cliUtils/addDummyDataForTest.ts | 6 +- src/db/entities/user.ts | 13 ++- ...7031553-AddSubAccountColumnToUsersTable.ts | 80 +++++++++++++++++++ src/db/repositories/user.ts | 2 +- src/routers/api/v1/posts.ts | 4 +- src/routers/web/login.ts | 2 +- src/routers/web/register.ts | 1 + src/routers/web/settings/index.ts | 2 + src/routers/web/settings/subAccounts.ts | 17 ++++ 9 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts create mode 100644 src/routers/web/settings/subAccounts.ts diff --git a/src/cliUtils/addDummyDataForTest.ts b/src/cliUtils/addDummyDataForTest.ts index af75353ab..0064c7036 100644 --- a/src/cliUtils/addDummyDataForTest.ts +++ b/src/cliUtils/addDummyDataForTest.ts @@ -37,7 +37,7 @@ async function main() { for (const name of Object.keys(users) as (keyof typeof users)[]) { users[name].name = name - users[name].screenName = name + users[name].displayScreenName = name users[name].encryptedPassword = "!dummy" if (name === "chihiro" || name === "producer") { users[name].canMakeInviteCode = true @@ -64,7 +64,7 @@ async function main() { const token = new AccessToken() token.application = app token.user = user - token.token = user.screenName + token.token = user.displayScreenName return token }) ) @@ -73,7 +73,7 @@ async function main() { const token = new AccessToken() token.application = app token.user = user - token.token = "revoked." + user.screenName + token.token = "revoked." + user.displayScreenName token.revokedAt = new Date(0) return token }) diff --git a/src/db/entities/user.ts b/src/db/entities/user.ts index ccddd101f..b78da1526 100644 --- a/src/db/entities/user.ts +++ b/src/db/entities/user.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from "typeorm" +import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, OneToMany, ManyToOne } from "typeorm" import { Matches, MaxLength } from "class-validator" import { EntityWithTimestamps } from "../../utils/timestampColumns" import { InviteCode } from "./inviteCode" @@ -17,6 +17,17 @@ export class User extends EntityWithTimestamps { @Matches(/^[0-9A-Za-z_]{1,20}$/) screenName!: string + @Column({ name: "display_screen_name", nullable: false }) + displayScreenName!: string + + @ManyToOne(type => User) + @JoinColumn({ name: "owner_id", referencedColumnName: "id" }) + owner!: User | null + + @Column({ name: "owner_screen_name", type: "citext", nullable: true }) + @Matches(/^[0-9A-Za-z_]{1,20}$/) + ownerScreenName!: string | null + @Column({ name: "encrypted_password", nullable: false }) encryptedPassword!: string diff --git a/src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts b/src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts new file mode 100644 index 000000000..761276f51 --- /dev/null +++ b/src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts @@ -0,0 +1,80 @@ +import { MigrationInterface, QueryRunner, TableColumn, TableIndex, TableForeignKey, TableCheck } from "typeorm" + +export class AddSubAccountColumnToUsersTable1576567031553 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumns("users", [ + new TableColumn({ name: "owner_id", type: "int", isNullable: true }), + new TableColumn({ name: "owner_screen_name", type: "citext", isNullable: true }), + new TableColumn({ name: "display_screen_name", type: "citext", isNullable: true }), + ]) + await queryRunner.dropIndex("users", "UQ:users:screen_name") + await queryRunner.createIndices("users", [ + new TableIndex({ + name: "UQ:users:screen_name", + columnNames: ["screen_name"], + where: "owner_screen_name IS NULL", + isUnique: true, + }), + new TableIndex({ + name: "UQ:users:screen_name:owner_screen_name", + columnNames: ["screen_name", "owner_screen_name"], + where: "owner_screen_name IS NOT NULL", + isUnique: true, + }), + new TableIndex({ + name: "UQ:users:display_screen_name", + columnNames: ["display_screen_name"], + isUnique: true, + }), + ]) + await queryRunner.query("UPDATE users SET display_screen_name = screen_name") + await queryRunner.query("ALTER TABLE users ALTER COLUMN display_screen_name SET NOT NULL") + await queryRunner.createCheckConstraint( + "users", + new TableCheck({ + name: "CHK:users:display_screen_name", + columnNames: ["display_screen_name"], + expression: "display_screen_name = COALESCE(screen_name || '.' || owner_screen_name, screen_name)", + }) + ) + await queryRunner.createForeignKeys("users", [ + new TableForeignKey({ + name: "FK:users:owner_id::users:id", + columnNames: ["owner_id"], + referencedTableName: "users", + referencedColumnNames: ["id"], + onDelete: "RESTRICT", + onUpdate: "CASCADE", + }), + new TableForeignKey({ + name: "FK:users:owner_screen_name::users:screen_name", + columnNames: ["owner_screen_name"], + referencedTableName: "users", + referencedColumnNames: ["display_screen_name"], + onDelete: "RESTRICT", + onUpdate: "CASCADE", + }), + ]) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropForeignKey("users", "FK:users:owner_id::users:id") + await queryRunner.dropForeignKey("users", "FK:users:owner_screen_name::users:screen_name") + await queryRunner.dropIndex("users", "UQ:users:screen_name") + await queryRunner.dropIndex("users", "UQ:users:screen_name:owner_screen_name") + await queryRunner.dropIndex("users", "UQ:users:display_screen_name") + await queryRunner.dropCheckConstraint("users", "CHK:users:display_screen_name") + await queryRunner.dropColumn("users", "owner_id") + await queryRunner.dropColumn("users", "owner_screen_name") + await queryRunner.dropColumn("users", "screen_name") + await queryRunner.renameColumn("users", "display_screen_name", "screen_name") + await queryRunner.createIndex( + "users", + new TableIndex({ + name: "UQ:users:screen_name", + columnNames: ["screen_name"], + isUnique: true, + }) + ) + } +} diff --git a/src/db/repositories/user.ts b/src/db/repositories/user.ts index 1ab940371..6ec411f73 100644 --- a/src/db/repositories/user.ts +++ b/src/db/repositories/user.ts @@ -16,7 +16,7 @@ export class UserRepository extends Repository { return { id: user.id, name: user.name, - screenName: user.screenName, + screenName: user.displayScreenName, postsCount: user.postsCount, createdAt: user.createdAt, updatedAt: user.updatedAt, diff --git a/src/routers/api/v1/posts.ts b/src/routers/api/v1/posts.ts index 72cdaeb2b..6c41ce3e7 100644 --- a/src/routers/api/v1/posts.ts +++ b/src/routers/api/v1/posts.ts @@ -111,14 +111,14 @@ router.post("/", koaBody(), async ctx => { .createQueryBuilder("subscription") .where("subscription.revokedAt IS NULL") .innerJoin("subscription.user", "users") - .andWhere("users.screenName = ANY(:lusers)", { lusers: mentions }) + .andWhere("users.displayScreenName = ANY(:lusers)", { lusers: mentions }) .getMany() const payload = { post: { user: { id: post.user.id, name: post.user.name, - screenName: post.user.screenName, + screenName: post.user.displayScreenName, icon: icon, }, text: post.text, diff --git a/src/routers/web/login.ts b/src/routers/web/login.ts index 183eb6c28..03d592734 100644 --- a/src/routers/web/login.ts +++ b/src/routers/web/login.ts @@ -22,7 +22,7 @@ router.post("/", koaBody(), checkReCaptcha, async ctx => { password: $.string.compose($length({ min: 8 })), }).transformOrThrow(ctx.request.body) const user = await getRepository(User).findOne({ - screenName: body.screen_name, + displayScreenName: body.screen_name, }) if (user == null) return ctx.throw(400, "そんなユーザーいない") const checkPasswordResult = await bcrypt.compare(body.password, user.encryptedPassword) diff --git a/src/routers/web/register.ts b/src/routers/web/register.ts index 45436534f..4eb27608d 100644 --- a/src/routers/web/register.ts +++ b/src/routers/web/register.ts @@ -24,6 +24,7 @@ router.post("/", koaBody(), checkReCaptcha, async ctx => { const user = new User() user.name = body.name user.screenName = body.screen_name + user.displayScreenName = body.screen_name user.encryptedPassword = await bcrypt.hash(body.password, 14) const repo = getRepository(User) const res = await repo.insert(user).catch(e => { diff --git a/src/routers/web/settings/index.ts b/src/routers/web/settings/index.ts index 9e29ebe20..936ccd554 100644 --- a/src/routers/web/settings/index.ts +++ b/src/routers/web/settings/index.ts @@ -2,11 +2,13 @@ import Router from "koa-router" import { WebRouterState, WebRouterCustom } from ".." import myDevelopedApplicationsRouter from "./myDevelopedApplications" import inviteCodesRouter from "./inviteCodes" +import subAccountsRouter from "./subAccounts" const router = new Router() router.use("/my_developed_applications", myDevelopedApplicationsRouter.routes()) router.use("/invite_codes", inviteCodesRouter.routes()) +router.use("/sub_accounts", subAccountsRouter.routes()) router.get("/", async ctx => { ctx.render("settings/index") diff --git a/src/routers/web/settings/subAccounts.ts b/src/routers/web/settings/subAccounts.ts new file mode 100644 index 000000000..da2415373 --- /dev/null +++ b/src/routers/web/settings/subAccounts.ts @@ -0,0 +1,17 @@ +import Router = require("koa-router") +import { WebRouterState, WebRouterCustom } from ".." +import { getRepository } from "typeorm" +import { User } from "../../../db/entities/user" + +const router = new Router() + +router.get("/", async ctx => { + const session = ctx.state.session + if (session == null) throw "please login" + const subAccounts = await getRepository(User).find({ + owner: session.user, + }) + ctx.render("settings/sub_accounts/index", { subAccounts }) +}) + +export default router From 3c231a54d99e1e14b937a37caa640a11662736f1 Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Thu, 19 Dec 2019 03:04:10 +0900 Subject: [PATCH 2/5] fi --- src/cliUtils/addDummyDataForTest.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cliUtils/addDummyDataForTest.ts b/src/cliUtils/addDummyDataForTest.ts index 0064c7036..0195aa400 100644 --- a/src/cliUtils/addDummyDataForTest.ts +++ b/src/cliUtils/addDummyDataForTest.ts @@ -37,6 +37,7 @@ async function main() { for (const name of Object.keys(users) as (keyof typeof users)[]) { users[name].name = name + users[name].screenName = name users[name].displayScreenName = name users[name].encryptedPassword = "!dummy" if (name === "chihiro" || name === "producer") { From 6f391d6ed2d3233121b4f9c3e0ec721d696b5773 Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Thu, 19 Dec 2019 03:22:45 +0900 Subject: [PATCH 3/5] test --- ...7031553-AddSubAccountColumnToUsersTable.ts | 80 ------------------- 1 file changed, 80 deletions(-) delete mode 100644 src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts diff --git a/src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts b/src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts deleted file mode 100644 index 761276f51..000000000 --- a/src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn, TableIndex, TableForeignKey, TableCheck } from "typeorm" - -export class AddSubAccountColumnToUsersTable1576567031553 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumns("users", [ - new TableColumn({ name: "owner_id", type: "int", isNullable: true }), - new TableColumn({ name: "owner_screen_name", type: "citext", isNullable: true }), - new TableColumn({ name: "display_screen_name", type: "citext", isNullable: true }), - ]) - await queryRunner.dropIndex("users", "UQ:users:screen_name") - await queryRunner.createIndices("users", [ - new TableIndex({ - name: "UQ:users:screen_name", - columnNames: ["screen_name"], - where: "owner_screen_name IS NULL", - isUnique: true, - }), - new TableIndex({ - name: "UQ:users:screen_name:owner_screen_name", - columnNames: ["screen_name", "owner_screen_name"], - where: "owner_screen_name IS NOT NULL", - isUnique: true, - }), - new TableIndex({ - name: "UQ:users:display_screen_name", - columnNames: ["display_screen_name"], - isUnique: true, - }), - ]) - await queryRunner.query("UPDATE users SET display_screen_name = screen_name") - await queryRunner.query("ALTER TABLE users ALTER COLUMN display_screen_name SET NOT NULL") - await queryRunner.createCheckConstraint( - "users", - new TableCheck({ - name: "CHK:users:display_screen_name", - columnNames: ["display_screen_name"], - expression: "display_screen_name = COALESCE(screen_name || '.' || owner_screen_name, screen_name)", - }) - ) - await queryRunner.createForeignKeys("users", [ - new TableForeignKey({ - name: "FK:users:owner_id::users:id", - columnNames: ["owner_id"], - referencedTableName: "users", - referencedColumnNames: ["id"], - onDelete: "RESTRICT", - onUpdate: "CASCADE", - }), - new TableForeignKey({ - name: "FK:users:owner_screen_name::users:screen_name", - columnNames: ["owner_screen_name"], - referencedTableName: "users", - referencedColumnNames: ["display_screen_name"], - onDelete: "RESTRICT", - onUpdate: "CASCADE", - }), - ]) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropForeignKey("users", "FK:users:owner_id::users:id") - await queryRunner.dropForeignKey("users", "FK:users:owner_screen_name::users:screen_name") - await queryRunner.dropIndex("users", "UQ:users:screen_name") - await queryRunner.dropIndex("users", "UQ:users:screen_name:owner_screen_name") - await queryRunner.dropIndex("users", "UQ:users:display_screen_name") - await queryRunner.dropCheckConstraint("users", "CHK:users:display_screen_name") - await queryRunner.dropColumn("users", "owner_id") - await queryRunner.dropColumn("users", "owner_screen_name") - await queryRunner.dropColumn("users", "screen_name") - await queryRunner.renameColumn("users", "display_screen_name", "screen_name") - await queryRunner.createIndex( - "users", - new TableIndex({ - name: "UQ:users:screen_name", - columnNames: ["screen_name"], - isUnique: true, - }) - ) - } -} From a3bed1653d9aaa2a5c21655bd2b0fbe5b57f9c50 Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Thu, 19 Dec 2019 17:29:34 +0900 Subject: [PATCH 4/5] Revert "test" This reverts commit 6f391d6ed2d3233121b4f9c3e0ec721d696b5773. --- ...7031553-AddSubAccountColumnToUsersTable.ts | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts diff --git a/src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts b/src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts new file mode 100644 index 000000000..761276f51 --- /dev/null +++ b/src/db/migrations/1576567031553-AddSubAccountColumnToUsersTable.ts @@ -0,0 +1,80 @@ +import { MigrationInterface, QueryRunner, TableColumn, TableIndex, TableForeignKey, TableCheck } from "typeorm" + +export class AddSubAccountColumnToUsersTable1576567031553 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumns("users", [ + new TableColumn({ name: "owner_id", type: "int", isNullable: true }), + new TableColumn({ name: "owner_screen_name", type: "citext", isNullable: true }), + new TableColumn({ name: "display_screen_name", type: "citext", isNullable: true }), + ]) + await queryRunner.dropIndex("users", "UQ:users:screen_name") + await queryRunner.createIndices("users", [ + new TableIndex({ + name: "UQ:users:screen_name", + columnNames: ["screen_name"], + where: "owner_screen_name IS NULL", + isUnique: true, + }), + new TableIndex({ + name: "UQ:users:screen_name:owner_screen_name", + columnNames: ["screen_name", "owner_screen_name"], + where: "owner_screen_name IS NOT NULL", + isUnique: true, + }), + new TableIndex({ + name: "UQ:users:display_screen_name", + columnNames: ["display_screen_name"], + isUnique: true, + }), + ]) + await queryRunner.query("UPDATE users SET display_screen_name = screen_name") + await queryRunner.query("ALTER TABLE users ALTER COLUMN display_screen_name SET NOT NULL") + await queryRunner.createCheckConstraint( + "users", + new TableCheck({ + name: "CHK:users:display_screen_name", + columnNames: ["display_screen_name"], + expression: "display_screen_name = COALESCE(screen_name || '.' || owner_screen_name, screen_name)", + }) + ) + await queryRunner.createForeignKeys("users", [ + new TableForeignKey({ + name: "FK:users:owner_id::users:id", + columnNames: ["owner_id"], + referencedTableName: "users", + referencedColumnNames: ["id"], + onDelete: "RESTRICT", + onUpdate: "CASCADE", + }), + new TableForeignKey({ + name: "FK:users:owner_screen_name::users:screen_name", + columnNames: ["owner_screen_name"], + referencedTableName: "users", + referencedColumnNames: ["display_screen_name"], + onDelete: "RESTRICT", + onUpdate: "CASCADE", + }), + ]) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropForeignKey("users", "FK:users:owner_id::users:id") + await queryRunner.dropForeignKey("users", "FK:users:owner_screen_name::users:screen_name") + await queryRunner.dropIndex("users", "UQ:users:screen_name") + await queryRunner.dropIndex("users", "UQ:users:screen_name:owner_screen_name") + await queryRunner.dropIndex("users", "UQ:users:display_screen_name") + await queryRunner.dropCheckConstraint("users", "CHK:users:display_screen_name") + await queryRunner.dropColumn("users", "owner_id") + await queryRunner.dropColumn("users", "owner_screen_name") + await queryRunner.dropColumn("users", "screen_name") + await queryRunner.renameColumn("users", "display_screen_name", "screen_name") + await queryRunner.createIndex( + "users", + new TableIndex({ + name: "UQ:users:screen_name", + columnNames: ["screen_name"], + isUnique: true, + }) + ) + } +} From 03732013af0d76afa3e60537b3296844a8cff30f Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Thu, 19 Dec 2019 17:33:07 +0900 Subject: [PATCH 5/5] remove EnableCITextExtension1555355416048 migration --- .../1555355416048-EnableCITextExtension.ts | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 src/db/migrations/1555355416048-EnableCITextExtension.ts diff --git a/src/db/migrations/1555355416048-EnableCITextExtension.ts b/src/db/migrations/1555355416048-EnableCITextExtension.ts deleted file mode 100644 index 757a2aeb2..000000000 --- a/src/db/migrations/1555355416048-EnableCITextExtension.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {MigrationInterface, QueryRunner} from "typeorm"; - -export class EnableCITextExtension1555355416048 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE EXTENSION CITEXT") - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query("DROP EXTENSION CITEXT") - } -}