refactoring

Resolve #7779
This commit is contained in:
syuilo
2021-11-12 02:02:25 +09:00
parent 037837b551
commit 0e4a111f81
1714 changed files with 20803 additions and 11751 deletions

View File

@@ -0,0 +1,156 @@
import $ from 'cafy';
import { ID } from '@/misc/cafy-id';
import define from '../../../define';
import { ReversiGames } from '@/models/index';
import { makePaginationQuery } from '../../../common/make-pagination-query';
import { Brackets } from 'typeorm';
export const meta = {
tags: ['games'],
params: {
limit: {
validator: $.optional.num.range(1, 100),
default: 10
},
sinceId: {
validator: $.optional.type(ID),
},
untilId: {
validator: $.optional.type(ID),
},
my: {
validator: $.optional.bool,
default: false
}
},
res: {
type: 'array' as const,
optional: false as const, nullable: false as const,
items: {
type: 'object' as const,
optional: false as const, nullable: false as const,
properties: {
id: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'id'
},
createdAt: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'date-time'
},
startedAt: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'date-time'
},
isStarted: {
type: 'boolean' as const,
optional: false as const, nullable: false as const
},
isEnded: {
type: 'boolean' as const,
optional: false as const, nullable: false as const
},
form1: {
type: 'any' as const,
optional: false as const, nullable: true as const
},
form2: {
type: 'any' as const,
optional: false as const, nullable: true as const
},
user1Accepted: {
type: 'boolean' as const,
optional: false as const, nullable: false as const,
default: false
},
user2Accepted: {
type: 'boolean' as const,
optional: false as const, nullable: false as const,
default: false
},
user1Id: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'id'
},
user2Id: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'id'
},
user1: {
type: 'object' as const,
optional: false as const, nullable: false as const,
ref: 'User'
},
user2: {
type: 'object' as const,
optional: false as const, nullable: false as const,
ref: 'User'
},
winnerId: {
type: 'string' as const,
optional: false as const, nullable: true as const,
format: 'id'
},
winner: {
type: 'object' as const,
optional: false as const, nullable: true as const,
ref: 'User'
},
surrendered: {
type: 'string' as const,
optional: false as const, nullable: true as const,
format: 'id'
},
black: {
type: 'number' as const,
optional: false as const, nullable: true as const
},
bw: {
type: 'string' as const,
optional: false as const, nullable: false as const
},
isLlotheo: {
type: 'boolean' as const,
optional: false as const, nullable: false as const,
},
canPutEverywhere: {
type: 'boolean' as const,
optional: false as const, nullable: false as const
},
loopedBoard: {
type: 'boolean' as const,
optional: false as const, nullable: false as const
}
}
}
}
};
export default define(meta, async (ps, user) => {
const query = makePaginationQuery(ReversiGames.createQueryBuilder('game'), ps.sinceId, ps.untilId)
.andWhere('game.isStarted = TRUE');
if (ps.my && user) {
query.andWhere(new Brackets(qb => { qb
.where('game.user1Id = :userId', { userId: user.id })
.orWhere('game.user2Id = :userId', { userId: user.id });
}));
}
// Fetch games
const games = await query.take(ps.limit!).getMany();
return await Promise.all(games.map((g) => ReversiGames.pack(g, user, {
detail: false
})));
});

View File

@@ -0,0 +1,168 @@
import $ from 'cafy';
import { ID } from '@/misc/cafy-id';
import Reversi from '../../../../../../games/reversi/core';
import define from '../../../../define';
import { ApiError } from '../../../../error';
import { ReversiGames } from '@/models/index';
export const meta = {
tags: ['games'],
params: {
gameId: {
validator: $.type(ID),
},
},
errors: {
noSuchGame: {
message: 'No such game.',
code: 'NO_SUCH_GAME',
id: 'f13a03db-fae1-46c9-87f3-43c8165419e1'
},
},
res: {
type: 'array' as const,
optional: false as const, nullable: false as const,
items: {
type: 'object' as const,
optional: false as const, nullable: false as const,
properties: {
id: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'id'
},
createdAt: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'date-time'
},
startedAt: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'date-time'
},
isStarted: {
type: 'boolean' as const,
optional: false as const, nullable: false as const
},
isEnded: {
type: 'boolean' as const,
optional: false as const, nullable: false as const
},
form1: {
type: 'any' as const,
optional: false as const, nullable: true as const
},
form2: {
type: 'any' as const,
optional: false as const, nullable: true as const
},
user1Accepted: {
type: 'boolean' as const,
optional: false as const, nullable: false as const,
default: false
},
user2Accepted: {
type: 'boolean' as const,
optional: false as const, nullable: false as const,
default: false
},
user1Id: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'id'
},
user2Id: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'id'
},
user1: {
type: 'object' as const,
optional: false as const, nullable: false as const,
ref: 'User'
},
user2: {
type: 'object' as const,
optional: false as const, nullable: false as const,
ref: 'User'
},
winnerId: {
type: 'string' as const,
optional: false as const, nullable: true as const,
format: 'id'
},
winner: {
type: 'object' as const,
optional: false as const, nullable: true as const,
ref: 'User'
},
surrendered: {
type: 'string' as const,
optional: false as const, nullable: true as const,
format: 'id'
},
black: {
type: 'number' as const,
optional: false as const, nullable: true as const
},
bw: {
type: 'string' as const,
optional: false as const, nullable: false as const
},
isLlotheo: {
type: 'boolean' as const,
optional: false as const, nullable: false as const,
},
canPutEverywhere: {
type: 'boolean' as const,
optional: false as const, nullable: false as const
},
loopedBoard: {
type: 'boolean' as const,
optional: false as const, nullable: false as const
},
board: {
type: 'array' as const,
optional: false as const, nullable: false as const,
items: {
type: 'any' as const,
optional: false as const, nullable: false as const
}
},
turn: {
type: 'any' as const,
optional: false as const, nullable: false as const
}
}
}
}
};
export default define(meta, async (ps, user) => {
const game = await ReversiGames.findOne(ps.gameId);
if (game == null) {
throw new ApiError(meta.errors.noSuchGame);
}
const o = new Reversi(game.map, {
isLlotheo: game.isLlotheo,
canPutEverywhere: game.canPutEverywhere,
loopedBoard: game.loopedBoard
});
for (const log of game.logs) {
o.put(log.color, log.pos);
}
const packed = await ReversiGames.pack(game, user);
return Object.assign({
board: o.board,
turn: o.turn
}, packed);
});

View File

@@ -0,0 +1,67 @@
import $ from 'cafy';
import { ID } from '@/misc/cafy-id';
import { publishReversiGameStream } from '@/services/stream';
import define from '../../../../define';
import { ApiError } from '../../../../error';
import { ReversiGames } from '@/models/index';
export const meta = {
tags: ['games'],
requireCredential: true as const,
params: {
gameId: {
validator: $.type(ID),
}
},
errors: {
noSuchGame: {
message: 'No such game.',
code: 'NO_SUCH_GAME',
id: 'ace0b11f-e0a6-4076-a30d-e8284c81b2df'
},
alreadyEnded: {
message: 'That game has already ended.',
code: 'ALREADY_ENDED',
id: '6c2ad4a6-cbf1-4a5b-b187-b772826cfc6d'
},
accessDenied: {
message: 'Access denied.',
code: 'ACCESS_DENIED',
id: '6e04164b-a992-4c93-8489-2123069973e1'
},
}
};
export default define(meta, async (ps, user) => {
const game = await ReversiGames.findOne(ps.gameId);
if (game == null) {
throw new ApiError(meta.errors.noSuchGame);
}
if (game.isEnded) {
throw new ApiError(meta.errors.alreadyEnded);
}
if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) {
throw new ApiError(meta.errors.accessDenied);
}
const winnerId = game.user1Id === user.id ? game.user2Id : game.user1Id;
await ReversiGames.update(game.id, {
surrendered: user.id,
isEnded: true,
winnerId: winnerId
});
publishReversiGameStream(game.id, 'ended', {
winnerId: winnerId,
game: await ReversiGames.pack(game.id, user)
});
});

View File

@@ -0,0 +1,58 @@
import define from '../../../define';
import { ReversiMatchings } from '@/models/index';
export const meta = {
tags: ['games'],
requireCredential: true as const,
res: {
type: 'array' as const,
optional: false as const, nullable: false as const,
items: {
type: 'object' as const,
optional: false as const, nullable: false as const,
properties: {
id: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'id'
},
createdAt: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'date-time'
},
parentId: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'id'
},
parent: {
type: 'object' as const,
optional: false as const, nullable: false as const,
ref: 'User'
},
childId: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'id'
},
child: {
type: 'object' as const,
optional: false as const, nullable: false as const,
ref: 'User'
}
}
}
}
};
export default define(meta, async (ps, user) => {
// Find session
const invitations = await ReversiMatchings.find({
childId: user.id
});
return await Promise.all(invitations.map((i) => ReversiMatchings.pack(i, user)));
});

View File

@@ -0,0 +1,108 @@
import $ from 'cafy';
import { ID } from '@/misc/cafy-id';
import { publishMainStream, publishReversiStream } from '@/services/stream';
import { eighteight } from '../../../../../games/reversi/maps';
import define from '../../../define';
import { ApiError } from '../../../error';
import { getUser } from '../../../common/getters';
import { genId } from '@/misc/gen-id';
import { ReversiMatchings, ReversiGames } from '@/models/index';
import { ReversiGame } from '@/models/entities/games/reversi/game';
import { ReversiMatching } from '@/models/entities/games/reversi/matching';
export const meta = {
tags: ['games'],
requireCredential: true as const,
params: {
userId: {
validator: $.type(ID),
},
},
errors: {
noSuchUser: {
message: 'No such user.',
code: 'NO_SUCH_USER',
id: '0b4f0559-b484-4e31-9581-3f73cee89b28'
},
isYourself: {
message: 'Target user is yourself.',
code: 'TARGET_IS_YOURSELF',
id: '96fd7bd6-d2bc-426c-a865-d055dcd2828e'
},
}
};
export default define(meta, async (ps, user) => {
// Myself
if (ps.userId === user.id) {
throw new ApiError(meta.errors.isYourself);
}
// Find session
const exist = await ReversiMatchings.findOne({
parentId: ps.userId,
childId: user.id
});
if (exist) {
// Destroy session
ReversiMatchings.delete(exist.id);
// Create game
const game = await ReversiGames.save({
id: genId(),
createdAt: new Date(),
user1Id: exist.parentId,
user2Id: user.id,
user1Accepted: false,
user2Accepted: false,
isStarted: false,
isEnded: false,
logs: [],
map: eighteight.data,
bw: 'random',
isLlotheo: false
} as Partial<ReversiGame>);
publishReversiStream(exist.parentId, 'matched', await ReversiGames.pack(game, { id: exist.parentId }));
const other = await ReversiMatchings.count({
childId: user.id
});
if (other == 0) {
publishMainStream(user.id, 'reversiNoInvites');
}
return await ReversiGames.pack(game, user);
} else {
// Fetch child
const child = await getUser(ps.userId).catch(e => {
if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
throw e;
});
// 以前のセッションはすべて削除しておく
await ReversiMatchings.delete({
parentId: user.id
});
// セッションを作成
const matching = await ReversiMatchings.save({
id: genId(),
createdAt: new Date(),
parentId: user.id,
childId: child.id
} as ReversiMatching);
const packed = await ReversiMatchings.pack(matching, child);
publishReversiStream(child.id, 'invited', packed);
publishMainStream(child.id, 'reversiInvited', packed);
return;
}
});

View File

@@ -0,0 +1,14 @@
import define from '../../../../define';
import { ReversiMatchings } from '@/models/index';
export const meta = {
tags: ['games'],
requireCredential: true as const
};
export default define(meta, async (ps, user) => {
await ReversiMatchings.delete({
parentId: user.id
});
});