This commit is contained in:
syuilo 2018-04-02 04:01:34 +09:00
parent ad57e36d0d
commit e8bde94e5b
9 changed files with 47 additions and 40 deletions

View file

@ -1,8 +1,8 @@
import config from '../../../../conf'; import config from '../../../../conf';
import { IRemoteAccount } from '../../../../models/user'; import { IRemoteUser } from '../../../../models/user';
export default ({ username }, { account }) => ({ export default ({ username }, followee: IRemoteUser) => ({
type: 'Follow', type: 'Follow',
actor: `${config.url}/@${username}`, actor: `${config.url}/@${username}`,
object: (account as IRemoteAccount).uri object: followee.account.uri
}); });

View file

@ -1,10 +1,10 @@
import config from '../../../../conf'; import config from '../../../../conf';
import { extractPublic } from '../../../../crypto_key'; import { extractPublic } from '../../../../crypto_key';
import { ILocalAccount } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
export default ({ username, account }) => ({ export default (user: ILocalUser) => ({
id: `${config.url}/@${username}/publickey`, id: `${config.url}/@${user.username}/publickey`,
type: 'Key', type: 'Key',
owner: `${config.url}/@${username}`, owner: `${config.url}/@${user.username}`,
publicKeyPem: extractPublic((account as ILocalAccount).keypair) publicKeyPem: extractPublic(user.account.keypair)
}); });

View file

@ -1,4 +1,4 @@
import { ILocalAccount, IUser } from '../../models/user'; import { IUser, isLocalUser } from '../../models/user';
import getAcct from './get-acct'; import getAcct from './get-acct';
/** /**
@ -9,8 +9,8 @@ export default function(user: IUser): string {
let string = `${user.name} (@${getAcct(user)})\n` + let string = `${user.name} (@${getAcct(user)})\n` +
`${user.postsCount}投稿、${user.followingCount}フォロー、${user.followersCount}フォロワー\n`; `${user.postsCount}投稿、${user.followingCount}フォロー、${user.followersCount}フォロワー\n`;
if (user.host === null) { if (isLocalUser(user)) {
const account = user.account as ILocalAccount; const account = user.account;
string += `場所: ${account.profile.location}、誕生日: ${account.profile.birthday}\n`; string += `場所: ${account.profile.location}、誕生日: ${account.profile.birthday}\n`;
} }

View file

@ -39,7 +39,7 @@ export function isValidBirthday(birthday: string): boolean {
return typeof birthday == 'string' && /^([0-9]{4})\-([0-9]{2})-([0-9]{2})$/.test(birthday); return typeof birthday == 'string' && /^([0-9]{4})\-([0-9]{2})-([0-9]{2})$/.test(birthday);
} }
export type ILocalAccount = { type ILocalAccount = {
keypair: string; keypair: string;
email: string; email: string;
links: string[]; links: string[];
@ -69,7 +69,7 @@ export type ILocalAccount = {
settings: any; settings: any;
}; };
export type IRemoteAccount = { type IRemoteAccount = {
inbox: string; inbox: string;
uri: string; uri: string;
publicKey: { publicKey: {
@ -78,7 +78,7 @@ export type IRemoteAccount = {
}; };
}; };
export type IUser = { type IUserBase = {
_id: mongo.ObjectID; _id: mongo.ObjectID;
createdAt: Date; createdAt: Date;
deletedAt: Date; deletedAt: Date;
@ -97,13 +97,19 @@ export type IUser = {
pinnedPostId: mongo.ObjectID; pinnedPostId: mongo.ObjectID;
isSuspended: boolean; isSuspended: boolean;
keywords: string[]; keywords: string[];
host: string;
hostLower: string; hostLower: string;
account: ILocalAccount | IRemoteAccount;
}; };
export type ILocalUser = IUser & { account: ILocalAccount }; export type IUser = ILocalUser | IRemoteUser;
export type IRemoteUser = IUser & { account: IRemoteAccount };
export interface ILocalUser extends IUserBase { host: null; account: ILocalAccount; }
export interface IRemoteUser extends IUserBase { host: string; account: IRemoteAccount; }
export const isLocalUser = (user: any): user is ILocalUser =>
user.host === null;
export const isRemoteUser = (user: any): user is IRemoteUser =>
!isLocalUser(user);
export function init(user): IUser { export function init(user): IUser {
user._id = new mongo.ObjectID(user._id); user._id = new mongo.ObjectID(user._id);

View file

@ -1,7 +1,7 @@
import { request } from 'https'; import { request } from 'https';
import { sign } from 'http-signature'; import { sign } from 'http-signature';
import { URL } from 'url'; import { URL } from 'url';
import User, { ILocalAccount, IRemoteAccount, pack as packUser } from '../../models/user'; import User, { isLocalUser, pack as packUser, ILocalUser } from '../../models/user';
import Following from '../../models/following'; import Following from '../../models/following';
import event from '../../common/event'; import event from '../../common/event';
import notify from '../../common/notify'; import notify from '../../common/notify';
@ -10,7 +10,7 @@ import render from '../../common/remote/activitypub/renderer/follow';
import config from '../../conf'; import config from '../../conf';
export default ({ data }, done) => Following.findOne({ _id: data.following }).then(({ followerId, followeeId }) => { export default ({ data }, done) => Following.findOne({ _id: data.following }).then(({ followerId, followeeId }) => {
const promisedFollower = User.findOne({ _id: followerId }); const promisedFollower: Promise<ILocalUser> = User.findOne({ _id: followerId });
const promisedFollowee = User.findOne({ _id: followeeId }); const promisedFollowee = User.findOne({ _id: followeeId });
return Promise.all([ return Promise.all([
@ -38,7 +38,7 @@ export default ({ data }, done) => Following.findOne({ _id: data.following }).th
.then(packed => event(follower._id, 'follow', packed)); .then(packed => event(follower._id, 'follow', packed));
let followeeEvent; let followeeEvent;
if (followee.host === null) { if (isLocalUser(followee)) {
followeeEvent = packUser(follower, followee) followeeEvent = packUser(follower, followee)
.then(packed => event(followee._id, 'followed', packed)); .then(packed => event(followee._id, 'followed', packed));
} else { } else {
@ -49,7 +49,7 @@ export default ({ data }, done) => Following.findOne({ _id: data.following }).th
port, port,
pathname, pathname,
search search
} = new URL((followee.account as IRemoteAccount).inbox); } = new URL(followee.account.inbox);
const req = request({ const req = request({
protocol, protocol,
@ -72,7 +72,7 @@ export default ({ data }, done) => Following.findOne({ _id: data.following }).th
sign(req, { sign(req, {
authorizationHeaderName: 'Signature', authorizationHeaderName: 'Signature',
key: (follower.account as ILocalAccount).keypair, key: follower.account.keypair,
keyId: `acct:${follower.username}@${config.host}` keyId: `acct:${follower.username}@${config.host}`
}); });

View file

@ -1,8 +1,9 @@
import * as bodyParser from 'body-parser'; import * as bodyParser from 'body-parser';
import * as express from 'express'; import * as express from 'express';
import { parseRequest, verifySignature } from 'http-signature'; import { parseRequest, verifySignature } from 'http-signature';
import User, { IRemoteAccount } from '../../models/user'; import User, { IRemoteUser } from '../../models/user';
import queue from '../../queue'; import queue from '../../queue';
import parseAcct from '../../common/user/parse-acct';
const app = express(); const app = express();
app.disable('x-powered-by'); app.disable('x-powered-by');
@ -36,13 +37,13 @@ app.post('/@:user/inbox', async (req, res) => {
}; };
} }
const user = await User.findOne(query); const user = await User.findOne(query) as IRemoteUser;
if (user === null) { if (user === null) {
return res.sendStatus(401); return res.sendStatus(401);
} }
if (!verifySignature(parsed, (user.account as IRemoteAccount).publicKey.publicKeyPem)) { if (!verifySignature(parsed, user.account.publicKey.publicKeyPem)) {
return res.sendStatus(401); return res.sendStatus(401);
} }

View file

@ -1,7 +1,7 @@
import * as EventEmitter from 'events'; import * as EventEmitter from 'events';
import * as bcrypt from 'bcryptjs'; import * as bcrypt from 'bcryptjs';
import User, { ILocalAccount, IUser, init as initUser } from '../../../models/user'; import User, { IUser, init as initUser, ILocalUser } from '../../../models/user';
import getPostSummary from '../../../common/get-post-summary'; import getPostSummary from '../../../common/get-post-summary';
import getUserSummary from '../../../common/user/get-summary'; import getUserSummary from '../../../common/user/get-summary';
@ -198,7 +198,7 @@ abstract class Context extends EventEmitter {
} }
class SigninContext extends Context { class SigninContext extends Context {
private temporaryUser: IUser = null; private temporaryUser: ILocalUser = null;
public async greet(): Promise<string> { public async greet(): Promise<string> {
return 'まずユーザー名を教えてください:'; return 'まずユーザー名を教えてください:';
@ -207,14 +207,14 @@ class SigninContext extends Context {
public async q(query: string): Promise<string> { public async q(query: string): Promise<string> {
if (this.temporaryUser == null) { if (this.temporaryUser == null) {
// Fetch user // Fetch user
const user: IUser = await User.findOne({ const user = await User.findOne({
usernameLower: query.toLowerCase(), usernameLower: query.toLowerCase(),
host: null host: null
}, { }, {
fields: { fields: {
data: false data: false
} }
}); }) as ILocalUser;
if (user === null) { if (user === null) {
return `${query}というユーザーは存在しませんでした... もう一度教えてください:`; return `${query}というユーザーは存在しませんでした... もう一度教えてください:`;
@ -225,7 +225,7 @@ class SigninContext extends Context {
} }
} else { } else {
// Compare password // Compare password
const same = await bcrypt.compare(query, (this.temporaryUser.account as ILocalAccount).password); const same = await bcrypt.compare(query, this.temporaryUser.account.password);
if (same) { if (same) {
this.bot.signin(this.temporaryUser); this.bot.signin(this.temporaryUser);

View file

@ -5,9 +5,9 @@ import $ from 'cafy';
import deepEqual = require('deep-equal'); import deepEqual = require('deep-equal');
import html from '../../../../common/text/html'; import html from '../../../../common/text/html';
import parse from '../../../../common/text/parse'; import parse from '../../../../common/text/parse';
import { default as Post, IPost, isValidText, isValidCw } from '../../../../models/post'; import Post, { IPost, isValidText, isValidCw } from '../../../../models/post';
import { default as User, ILocalAccount, IUser } from '../../../../models/user'; import User, { ILocalUser } from '../../../../models/user';
import { default as Channel, IChannel } from '../../../../models/channel'; import Channel, { IChannel } from '../../../../models/channel';
import Following from '../../../../models/following'; import Following from '../../../../models/following';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import DriveFile from '../../../../models/drive-file'; import DriveFile from '../../../../models/drive-file';
@ -29,7 +29,7 @@ import config from '../../../../conf';
* @param {any} app * @param {any} app
* @return {Promise<any>} * @return {Promise<any>}
*/ */
module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { module.exports = (params, user: ILocalUser, app) => new Promise(async (res, rej) => {
// Get 'text' parameter // Get 'text' parameter
const [text, textErr] = $(params.text).optional.string().pipe(isValidText).$; const [text, textErr] = $(params.text).optional.string().pipe(isValidText).$;
if (textErr) return rej('invalid text'); if (textErr) return rej('invalid text');
@ -400,7 +400,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => {
}); });
// この投稿をWatchする // この投稿をWatchする
if ((user.account as ILocalAccount).settings.autoWatch !== false) { if (user.account.settings.autoWatch !== false) {
watch(user._id, reply); watch(user._id, reply);
} }

View file

@ -1,7 +1,7 @@
import * as express from 'express'; import * as express from 'express';
import * as bcrypt from 'bcryptjs'; import * as bcrypt from 'bcryptjs';
import * as speakeasy from 'speakeasy'; import * as speakeasy from 'speakeasy';
import { default as User, ILocalAccount, IUser } from '../../../models/user'; import User, { ILocalUser } from '../../../models/user';
import Signin, { pack } from '../../../models/signin'; import Signin, { pack } from '../../../models/signin';
import event from '../../../common/event'; import event from '../../../common/event';
import signin from '../common/signin'; import signin from '../common/signin';
@ -31,7 +31,7 @@ export default async (req: express.Request, res: express.Response) => {
} }
// Fetch user // Fetch user
const user: IUser = await User.findOne({ const user = await User.findOne({
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
host: null host: null
}, { }, {
@ -39,7 +39,7 @@ export default async (req: express.Request, res: express.Response) => {
data: false, data: false,
'account.profile': false 'account.profile': false
} }
}); }) as ILocalUser;
if (user === null) { if (user === null) {
res.status(404).send({ res.status(404).send({
@ -48,7 +48,7 @@ export default async (req: express.Request, res: express.Response) => {
return; return;
} }
const account = user.account as ILocalAccount; const account = user.account;
// Compare password // Compare password
const same = await bcrypt.compare(password, account.password); const same = await bcrypt.compare(password, account.password);