Introduce account document to user document

An account document is attached to a user document if an account of the
user is on the server. It may be missing if the user is on a remote server.
This commit is contained in:
Akihiko Odaki
2018-03-26 00:19:07 +09:00
parent a633f184ab
commit 19b9cb105d
70 changed files with 355 additions and 280 deletions

View File

@ -34,7 +34,7 @@ export default (req: express.Request) => new Promise<IAuthContext>(async (resolv
if (isNativeToken(token)) {
const user: IUser = await User
.findOne({ token: token });
.findOne({ 'account.token': token });
if (user === null) {
return reject('user not found');

View File

@ -225,7 +225,7 @@ class SigninContext extends Context {
}
} else {
// Compare password
const same = await bcrypt.compare(query, this.temporaryUser.password);
const same = await bcrypt.compare(query, this.temporaryUser.account.password);
if (same) {
this.bot.signin(this.temporaryUser);

View File

@ -110,11 +110,11 @@ class LineBot extends BotCore {
data: `showtl|${user.id}`
});
if (user.twitter) {
if (user.account.twitter) {
actions.push({
type: 'uri',
label: 'Twitterアカウントを見る',
uri: `https://twitter.com/${user.twitter.screen_name}`
uri: `https://twitter.com/${user.account.twitter.screen_name}`
});
}
@ -171,7 +171,7 @@ module.exports = async (app: express.Application) => {
if (session == null) {
const user = await User.findOne({
line: {
'account.line': {
user_id: sourceId
}
});
@ -181,7 +181,7 @@ module.exports = async (app: express.Application) => {
bot.on('signin', user => {
User.update(user._id, {
$set: {
line: {
'account.line': {
user_id: sourceId
}
}
@ -191,7 +191,7 @@ module.exports = async (app: express.Application) => {
bot.on('signout', user => {
User.update(user._id, {
$set: {
line: {
'account.line': {
user_id: null
}
}

View File

@ -2,7 +2,7 @@ import config from '../../conf';
export default function(res, user, redirect: boolean) {
const expires = 1000 * 60 * 60 * 24 * 365; // One Year
res.cookie('i', user.token, {
res.cookie('i', user.account.token, {
path: '/',
domain: `.${config.host}`,
secure: config.url.substr(0, 5) === 'https',

View File

@ -45,7 +45,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
// Fetch token
const session = await AuthSess
.findOne({ token: token });
.findOne({ 'account.token': token });
if (session === null) {
return rej('session not found');

View File

@ -32,7 +32,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
}, {
fields: {
data: false,
profile: false
'account.profile': false
}
});

View File

@ -31,7 +31,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
}, {
fields: {
data: false,
profile: false
'account.profile': false
}
});

View File

@ -22,7 +22,7 @@ module.exports = (params, user, _, isSecure) => new Promise(async (res, rej) =>
// Update lastUsedAt
User.update({ _id: user._id }, {
$set: {
last_used_at: new Date()
'account.last_used_at': new Date()
}
});
});

View File

@ -28,8 +28,8 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
await User.update(user._id, {
$set: {
two_factor_secret: user.two_factor_temp_secret,
two_factor_enabled: true
'account.two_factor_secret': user.two_factor_temp_secret,
'account.two_factor_enabled': true
}
});

View File

@ -14,7 +14,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
if (passwordErr) return rej('invalid password param');
// Compare password
const same = await bcrypt.compare(password, user.password);
const same = await bcrypt.compare(password, user.account.password);
if (!same) {
return rej('incorrect password');

View File

@ -11,7 +11,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
if (passwordErr) return rej('invalid password param');
// Compare password
const same = await bcrypt.compare(password, user.password);
const same = await bcrypt.compare(password, user.account.password);
if (!same) {
return rej('incorrect password');
@ -19,8 +19,8 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
await User.update(user._id, {
$set: {
two_factor_secret: null,
two_factor_enabled: false
'account.two_factor_secret': null,
'account.two_factor_enabled': false
}
});

View File

@ -22,7 +22,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
if (newPasswordErr) return rej('invalid new_password param');
// Compare password
const same = await bcrypt.compare(currentPassword, user.password);
const same = await bcrypt.compare(currentPassword, user.account.password);
if (!same) {
return rej('incorrect password');
@ -34,7 +34,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
await User.update(user._id, {
$set: {
password: hash
'account.password': hash
}
});

View File

@ -20,7 +20,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
if (passwordErr) return rej('invalid password param');
// Compare password
const same = await bcrypt.compare(password, user.password);
const same = await bcrypt.compare(password, user.account.password);
if (!same) {
return rej('incorrect password');
@ -31,7 +31,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
await User.update(user._id, {
$set: {
token: secret
'account.token': secret
}
});

View File

@ -29,12 +29,12 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re
// Get 'location' parameter
const [location, locationErr] = $(params.location).optional.nullable.string().pipe(isValidLocation).$;
if (locationErr) return rej('invalid location param');
if (location !== undefined) user.profile.location = location;
if (location !== undefined) user.account.profile.location = location;
// Get 'birthday' parameter
const [birthday, birthdayErr] = $(params.birthday).optional.nullable.string().pipe(isValidBirthday).$;
if (birthdayErr) return rej('invalid birthday param');
if (birthday !== undefined) user.profile.birthday = birthday;
if (birthday !== undefined) user.account.profile.birthday = birthday;
// Get 'avatar_id' parameter
const [avatarId, avatarIdErr] = $(params.avatar_id).optional.id().$;
@ -49,12 +49,12 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re
// Get 'is_bot' parameter
const [isBot, isBotErr] = $(params.is_bot).optional.boolean().$;
if (isBotErr) return rej('invalid is_bot param');
if (isBot != null) user.is_bot = isBot;
if (isBot != null) user.account.is_bot = isBot;
// Get 'auto_watch' parameter
const [autoWatch, autoWatchErr] = $(params.auto_watch).optional.boolean().$;
if (autoWatchErr) return rej('invalid auto_watch param');
if (autoWatch != null) user.settings.auto_watch = autoWatch;
if (autoWatch != null) user.account.settings.auto_watch = autoWatch;
await User.update(user._id, {
$set: {
@ -62,9 +62,9 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re
description: user.description,
avatar_id: user.avatar_id,
banner_id: user.banner_id,
profile: user.profile,
is_bot: user.is_bot,
settings: user.settings
'account.profile': user.account.profile,
'account.is_bot': user.account.is_bot,
'account.settings': user.account.settings
}
});

View File

@ -22,14 +22,14 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
if (valueErr) return rej('invalid value param');
const x = {};
x[`client_settings.${name}`] = value;
x[`account.client_settings.${name}`] = value;
await User.update(user._id, {
$set: x
});
// Serialize
user.client_settings[name] = value;
user.account.client_settings[name] = value;
const iObj = await pack(user, user, {
detail: true,
includeSecrets: true

View File

@ -26,7 +26,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
if (home) {
await User.update(user._id, {
$set: {
'client_settings.home': home
'account.client_settings.home': home
}
});
@ -38,7 +38,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
} else {
if (id == null && data == null) return rej('you need to set id and data params if home param unset');
const _home = user.client_settings.home;
const _home = user.account.client_settings.home;
const widget = _home.find(w => w.id == id);
if (widget == null) return rej('widget not found');
@ -47,7 +47,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
await User.update(user._id, {
$set: {
'client_settings.home': _home
'account.client_settings.home': _home
}
});

View File

@ -25,7 +25,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
if (home) {
await User.update(user._id, {
$set: {
'client_settings.mobile_home': home
'account.client_settings.mobile_home': home
}
});
@ -37,7 +37,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
} else {
if (id == null && data == null) return rej('you need to set id and data params if home param unset');
const _home = user.client_settings.mobile_home || [];
const _home = user.account.client_settings.mobile_home || [];
const widget = _home.find(w => w.id == id);
if (widget == null) return rej('widget not found');
@ -46,7 +46,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
await User.update(user._id, {
$set: {
'client_settings.mobile_home': _home
'account.client_settings.mobile_home': _home
}
});

View File

@ -30,7 +30,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
}, {
fields: {
data: false,
profile: false
'account.profile': false
}
});

View File

@ -30,7 +30,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
}, {
fields: {
data: false,
profile: false
'account.profile': false
}
});

View File

@ -12,7 +12,7 @@ import Post from '../../models/post';
* @return {Promise<any>}
*/
module.exports = (params, user) => new Promise(async (res, rej) => {
if (!user.is_pro) {
if (!user.account.is_pro) {
return rej('This endpoint is available only from a Pro account');
}

View File

@ -390,7 +390,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => {
});
// この投稿をWatchする
if (user.settings.auto_watch !== false) {
if (user.account.settings.auto_watch !== false) {
watch(user._id, reply);
}

View File

@ -100,7 +100,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
});
// この投稿をWatchする
if (user.settings.auto_watch !== false) {
if (user.account.settings.auto_watch !== false) {
watch(user._id, post);
}
});

View File

@ -116,7 +116,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
});
// この投稿をWatchする
if (user.settings.auto_watch !== false) {
if (user.account.settings.auto_watch !== false) {
watch(user._id, post);
}
});

View File

@ -30,7 +30,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
_id: {
$nin: followingIds
},
last_used_at: {
'account.last_used_at': {
$gte: new Date(Date.now() - ms('7days'))
}
}, {

View File

@ -11,7 +11,7 @@ import config from '../../conf';
const User = db.get<IUser>('users');
User.createIndex('username');
User.createIndex('token');
User.createIndex('account.token');
export default User;
@ -43,46 +43,48 @@ export type IUser = {
_id: mongo.ObjectID;
created_at: Date;
deleted_at: Date;
email: string;
followers_count: number;
following_count: number;
links: string[];
name: string;
password: string;
posts_count: number;
drive_capacity: number;
username: string;
username_lower: string;
token: string;
avatar_id: mongo.ObjectID;
banner_id: mongo.ObjectID;
data: any;
twitter: {
access_token: string;
access_token_secret: string;
user_id: string;
screen_name: string;
};
line: {
user_id: string;
};
description: string;
profile: {
location: string;
birthday: string; // 'YYYY-MM-DD'
tags: string[];
};
last_used_at: Date;
latest_post: IPost;
pinned_post_id: mongo.ObjectID;
is_bot: boolean;
is_pro: boolean;
is_suspended: boolean;
keywords: string[];
two_factor_secret: string;
two_factor_enabled: boolean;
client_settings: any;
settings: any;
account: {
email: string;
links: string[];
password: string;
token: string;
twitter: {
access_token: string;
access_token_secret: string;
user_id: string;
screen_name: string;
};
line: {
user_id: string;
};
profile: {
location: string;
birthday: string; // 'YYYY-MM-DD'
tags: string[];
};
last_used_at: Date;
is_bot: boolean;
is_pro: boolean;
two_factor_secret: string;
two_factor_enabled: boolean;
client_settings: any;
settings: any;
};
};
export function init(user): IUser {
@ -119,11 +121,11 @@ export const pack = (
const fields = opts.detail ? {
} : {
settings: false,
client_settings: false,
profile: false,
keywords: false,
domains: false
'account.settings': false,
'account.client_settings': false,
'account.profile': false,
'account.keywords': false,
'account.domains': false
};
// Populate the user if 'user' is ID
@ -158,26 +160,26 @@ export const pack = (
delete _user.latest_post;
// Remove private properties
delete _user.password;
delete _user.token;
delete _user.two_factor_temp_secret;
delete _user.two_factor_secret;
delete _user.account.password;
delete _user.account.token;
delete _user.account.two_factor_temp_secret;
delete _user.account.two_factor_secret;
delete _user.username_lower;
if (_user.twitter) {
delete _user.twitter.access_token;
delete _user.twitter.access_token_secret;
if (_user.account.twitter) {
delete _user.account.twitter.access_token;
delete _user.account.twitter.access_token_secret;
}
delete _user.line;
delete _user.account.line;
// Visible via only the official client
if (!opts.includeSecrets) {
delete _user.email;
delete _user.settings;
delete _user.client_settings;
delete _user.account.email;
delete _user.account.settings;
delete _user.account.client_settings;
}
if (!opts.detail) {
delete _user.two_factor_enabled;
delete _user.account.two_factor_enabled;
}
_user.avatar_url = _user.avatar_id != null

View File

@ -36,7 +36,7 @@ export default async (req: express.Request, res: express.Response) => {
}, {
fields: {
data: false,
profile: false
'account.profile': false
}
});
@ -48,12 +48,12 @@ export default async (req: express.Request, res: express.Response) => {
}
// Compare password
const same = await bcrypt.compare(password, user.password);
const same = await bcrypt.compare(password, user.account.password);
if (same) {
if (user.two_factor_enabled) {
if (user.account.two_factor_enabled) {
const verified = (speakeasy as any).totp.verify({
secret: user.two_factor_secret,
secret: user.account.two_factor_secret,
encoding: 'base32',
token: token
});

View File

@ -105,38 +105,40 @@ export default async (req: express.Request, res: express.Response) => {
// Create account
const account: IUser = await User.insert({
token: secret,
avatar_id: null,
banner_id: null,
created_at: new Date(),
description: null,
email: null,
followers_count: 0,
following_count: 0,
links: null,
name: name,
password: hash,
posts_count: 0,
likes_count: 0,
liked_count: 0,
drive_capacity: 1073741824, // 1GB
username: username,
username_lower: username.toLowerCase(),
profile: {
bio: null,
birthday: null,
blood: null,
gender: null,
handedness: null,
height: null,
location: null,
weight: null
},
settings: {
auto_watch: true
},
client_settings: {
home: homeData
account: {
token: secret,
email: null,
links: null,
password: hash,
profile: {
bio: null,
birthday: null,
blood: null,
gender: null,
handedness: null,
height: null,
location: null,
weight: null
},
settings: {
auto_watch: true
},
client_settings: {
home: homeData
}
}
});

View File

@ -39,10 +39,10 @@ module.exports = (app: express.Application) => {
if (userToken == null) return res.send('plz signin');
const user = await User.findOneAndUpdate({
token: userToken
'account.token': userToken
}, {
$set: {
twitter: null
'account.twitter': null
}
});
@ -126,7 +126,7 @@ module.exports = (app: express.Application) => {
const result = await twAuth.done(JSON.parse(ctx), req.query.oauth_verifier);
const user = await User.findOne({
'twitter.user_id': result.userId
'account.twitter.user_id': result.userId
});
if (user == null) {
@ -148,10 +148,10 @@ module.exports = (app: express.Application) => {
const result = await twAuth.done(JSON.parse(ctx), verifier);
const user = await User.findOneAndUpdate({
token: userToken
'account.token': userToken
}, {
$set: {
twitter: {
'account.twitter': {
access_token: result.accessToken,
access_token_secret: result.accessTokenSecret,
user_id: result.userId,

View File

@ -74,7 +74,7 @@ export default async function(request: websocket.request, connection: websocket.
// Update lastUsedAt
User.update({ _id: user._id }, {
$set: {
last_used_at: new Date()
'account.last_used_at': new Date()
}
});
break;

View File

@ -94,7 +94,7 @@ function authenticate(token: string): Promise<IUser> {
// Fetch user
const user: IUser = await User
.findOne({
token: token
'account.token': token
});
resolve(user);