const path = require('path');
const md5 = require('blueimp-md5');
nv_create_dirs('./nv-content/data/');
const db_users = new NV_Database(path.join(process.cwd(),"./nv-content/data/users.json"),{
columns: ['name','password','created_time','power','email','url'],
index: 'id'
});
nvdb.users = db_users;
global.get_user_role = (power)=>{
return power >= 10 ? "管理员"
: power >= 8 ? "编辑"
: power >= 6 ? "贡献者"
: power >= 3 ? "读者"
: power >= 1 ? "演示用户"
: "未注册";
}
var is_error_username = name=>{
if (!name) {return new NV_Error('请输入账号');}
if (typeof(name) !== 'string') { return new NV_Error('用户名类型错误'); }
if (name[0]==' ' || name[name.length - 1]== ' '
|| name[0]==' ' || name[name.length - 1]== ' ') {
return new NV_Error('用户名前后不应包含空格');
}
if ('@#$*?'
.split('')
.some( char => name.includes(char) )) {
return new NV_Error('用户名不能使用特殊字符');
}
}
global.nv_count_admin = ()=>{
return db_users.find(r=>r.power == 10).length;
}
global.nv_insert_user = (args)=>{
// 调用相关的action
args = apply_filters('pre_insert_user_data', args);
const {name,password,power,email,url} = args;
if (!name || !password) {
return new NV_Error('参数错误');
}
if (password.length < 6) {
return new NV_Error('密码至少6位');
}
var error_username = is_error_username(name);
if (error_username) {
return error_username;
}
if ( get_user_by_name(name) ) {
return new NV_Error(`用户名已存在`);
}
if ( email && get_user_by_email(email) ) {
return new NV_Error(`邮箱已存在`);
}
var userdata = {
name,
created_time: new Date().getTime(),
power: parseInt(power) || 3,
email: email || "",
url: url || ""
}
var user_id = db_users.insert({
...userdata,
password: md5(password),
});
// 设置上次登录时间
update_user_meta(user_id,'last_logon_date',null);
//发送邮件
if ( get_option('nv_user_register_notify') ) {
var user = get_user_by_id(user_id);
send_mail_to_admin('新用户加入',[
'恭喜!又一个新用户加入了您的站点:',
`账号:${name}<br>邮箱:${email}<br>角色:${user.role}`,
`<span class="small">您收到此信息是因为后台启用了新用户注册时通知管理员功能。</span>`
])
.then(()=>{})
.catch(err=>{console.log('新增用户,发送邮件通知管理员失败:',err)})
}
do_action( 'user_register', user_id, userdata );
return user_id;
}
global.nv_update_user = (args)=>{
// 调用相关的action
args = apply_filters('pre_update_user_data', args);
args.id = parseInt(args.id);
const {id,name,email} = args;
if (!id) {
return new NV_Error('参数错误');
}
if (name) {
var error_username = is_error_username(name);
if (error_username) {
return error_username;
}
//尝试修改登录名,要判定登录名是否重复
var user = db_users.find(r=>r.id !== id && r.name == name)[0];
if (user) {
return new NV_Error(`用户名已存在,请更换后重试`);
}
}
if (email) {
//尝试修改邮箱,要判定邮箱是否重复
var user = db_users.find(r=>r.id !== id && r.email == email)[0];
if (user) {
return new NV_Error(`邮箱已存在,请更换后重试`);
}
}
if (args.password) {
if (args.password.length < 6) {
return new NV_Error('密码至少6位');
}
args.password = md5(args.password);
} else {
delete args.password;
}
delete args.id;
delete args.created_time;
db_users.setColumn(r=>r.id==id,{
...args
});
// 调用相关的action
do_action('update_user', id, args);
return id;
}
global.nv_delete_user = (id)=>{
id = parseInt(id);
if (!id) {
return new NV_Error('参数错误');
}
// 调用相关的action
do_action('before_delete_user', id);
//删除用户前,先删除用户头像(前提是确实是头像,并且有且仅有自己一人使用)
var media_id = get_user_meta(id,'avatar_id');
if (media_id) {
var is_avatar = get_media_meta(media_id, 'is_avatar');
// 查询有多少人在使用这个 media_id 的头像
var result = nvdb.usermeta.find(r=>r.key=="avatar_id" && r.value==media_id);
if (is_avatar && result.length == 1) {
nv_delete_media(media_id)
}
}
db_users.drop(r=>r.id==id);
delete_user_meta(id)
//删除用户时,将评论的用户ID设为-1
nvdb.comments.setColumn(r=>r.user_id == id,{
user_id: -1
})
// 调用相关的action
do_action('after_delete_user', id);
return id;
}
global.get_user_by_id = (id)=>{
id = parseInt(id);
var user = db_users.find(r=>r.id == id)[0];
if (user) {
delete user.password;
user.role = get_user_role(user.power);
return apply_filters('get_user',user,user.id);
}
return false;
}
global.get_user_by_name = (name)=>{
var user = db_users.find(r=>r.name == name)[0];
if (user) {
delete user.password;
user.role = get_user_role(user.power);
return apply_filters('get_user',user,user.id);
}
return false;
}
global.get_user_by_email = (email)=>{
var user = db_users.find(r=>r.email == email)[0];
if (user) {
delete user.password;
user.role = get_user_role(user.power);
return apply_filters('get_user',user,user.id);
}
return false;
}
global.nv_user_login = (name,password)=>{
if (!name) { return new NV_Error('请输入账号'); }
// name应该可以是用户名、邮箱。而不仅仅是用户名登录
var user = db_users.find(r=> (r.name == name || r.email == name) && r.password == md5(password))[0];
if (user) {
delete user.password;
user.role = get_user_role(user.power);
return apply_filters('get_user',user,user.id);
}
return new NV_Error('用户名或密码错误');
}
var nonce_store = {};
global.nv_create_nonce = (user_id)=>{
var user = db_users.find(r=>r.id == user_id)[0] || false;
const { v4: uuidv4 } = require('uuid');
const uuid = uuidv4();
nonce_store[uuid] = {
id: user.id || 0,
password: user.password || "",
deprecated: new Date().getTime() + 24*60*60*1000
}
nv_set_user_last_logon_date(user_id);
return uuid;
}
global.nv_revoke_nonce = (uuid)=>{
delete nonce_store[uuid];
}
global.nv_verify_nonce = (uuid)=>{
// verify 不会给nonce延期
var nonce = nonce_store[uuid];
if (!nonce) {
return false;
} else if (new Date().getTime() >= nonce.deprecated) {
//已过期
delete nonce_store[uuid];
return false;
} else {
return true;
}
}
global.nv_set_user_last_logon_date = (user_id)=>{
if (!user_id) {return;}
// 注意,精度是Date。如果不是当天才重新设置。是当天,则不重新设置!
var last_logon_date = get_user_meta(user_id, 'last_logon_date') || 0;
// 如果上次的时间戳不是在今天,那么记录新的时间戳
if (new Date(last_logon_date).toDateString() !== new Date().toDateString()) {
update_user_meta(user_id,'last_logon_date', new Date().getTime() )
}
}
global.get_user_by_nonce = (uuid)=>{
// 通过nonce查找用户
// 会验证用户密码是否和创建nonce时一致,若一致,会给nonce延期24小时
var nonce = nonce_store[uuid];
if (!nonce) {
return new NV_Error('登录信息已失效,请重新登录',601);
}
else if ( new Date().getTime() >= nonce.deprecated ) {
//已过期
delete nonce_store[uuid];
return new NV_Error('登录信息已过期,请重新登录',602);
}
else {
var user = db_users.find(r=>r.id == nonce.id)[0];
if (user) {
if (user.password == nonce.password) {
//nvnonce检查成功
//重新设置用户最后登录时间
nv_set_user_last_logon_date(user.id);
//重新定义nvnonce时间
nonce_store[uuid].deprecated = new Date().getTime() + 24*60*60*1000;
delete user.password;
user.role = get_user_role(user.power);
return apply_filters('get_user',user,user.id);
} else {
//登录期间修改过密码
delete nonce_store[uuid];
return new NV_Error('用户已修改密码,请重新登录',603);
}
} else {
//用户已删除
delete nonce_store[uuid];
return new NV_Error('用户不存在,请重新登录',604);
}
}
}
// 每24小时定时清除过期的nonce
setInterval(()=>{
var now = new Date().getTime();
for (var uuid in nonce_store) {
if (now > nonce_store[uuid].deprecated) {
delete nonce_store[uuid];
}
}
}, 24*60*60*1000);
/*global.get_user_list = (filter)=>{
console.log('DEPRECATED:过时的方法!get_user_list 方法将在未来移除')
if (!filter) { filter = r=>true; }
return db_users.find(filter).map(user=>{
delete user.password;
user.role = get_user_role(user.power);
return apply_filters('get_user',user,user.id);
});
}*/
global.query_users = (args={}) => {
/*args = {
include: [user_id], // 可以id、name、email
exclude: [user_id], // 可以id、name、email
keyword: String, // name、email相似的
power: Number, // 0~10
meta_query: {
relation: String, AND /OR //默认 AND
opts: [
{
key: String, metaKey
value: String / Array, Array可以用IN /NOT IN/BETWEEN/NOT BETWEEN来compare
compare: String, =/!=/>/>=/</<=/LIKE/NOT LIKE/IN/NOT IN/BETWEEN/NOT BETWEEN
}
]
},
orderby: rand,name,power,email,url,created_time //默认:power
order: ASC/DESC, 默认:ASC
users_per_page: Number,
current_page: Number
}*/
var users = db_users.find(r=>{
var result = [];
if (args.include) {
if ( !(args.include.includes(r.id) || args.include.includes(r.name) || args.include.includes(r.email) ) ) {
return false;
}
}
if (args.exclude) {
if ( args.exclude.includes(r.id) || args.exclude.includes(r.name) || args.exclude.includes(r.email) ) {
return false;
}
}
if (args.keyword) {
if ( !(r.name.includes(args.keyword) || r.email.includes(args.keyword) ) ) {
return false;
}
}
if (args.power) {
if ( r.power !== args.power ) {
return false;
}
}
return true;
})
if (args.meta_query) {
var meta_queried_user_ids = [];
//先根据opts分别获取user_id
var meta_query_result = [ /*[user_ids],[user_ids]*/ ]
args.meta_query.opts.forEach(({key,value,compare})=>{
var user_ids = nvdb.usermeta.find(r=>{
if (r.key !== key) {return false;}
var user = users.find(user=>user.id == r.user_id);
if (!user) {return;}
if (!user.metas) { user.metas = {} }
user.metas[key] = r.value;
try {
switch (compare) {
case '=': return r.value == value;
case '!=': return r.value !== value;
case '>': return r.value > value;
case '>=': return r.value >= value;
case '<=': return r.value <= value;
case 'LIKE': return r.value.toString().includes(value);
case 'NOT LIKE': return !r.value.toString().includes(value);
case 'IN': return value.includes(r.value);
case 'NOT IN': return !value.includes(r.value);
case 'BETWEEN': return r.value >= value[0] && r.value <=value[1];
case 'NOT BETWEEN': return r.value >= value[1] || r.value <=value[0];
default: return true;
}
} catch(e) {
console.log(e,`比较类型:${compare}`,`原始数据:`,r)
return false;
}
}).map(r=>r.user_id)
meta_query_result.push(user_ids)
})
//根据relation将每次获取的term_id进行交集或并集
if (args.meta_query.relation == 'OR') {
// 取并集
meta_queried_user_ids = meta_query_result.reduce( (arr1,arr2)=> arr1.concat(arr2.filter(v => !arr1.includes(v))) )
} else {
// 取交集
meta_queried_user_ids = meta_query_result.reduce( (arr1,arr2)=> arr1.filter(v => arr2.includes(v)) )
}
//最后根据最终交或并结果的[user_id]来过滤posts
var meta_user_intersections = [];
users.forEach(user=>{
if ( meta_queried_user_ids.includes(user.id) ) {
meta_user_intersections.push(user)
}
})
users = meta_user_intersections;
}
// 排序
if (args.orderby == 'rand') {
users.sort(() => Math.random() - 0.5);
} else {
if (!args.orderby) {
users.sort((user1,user2)=>{
return user2.power - user1.power;
})
} else {
users.sort((user1,user2)=>{
if ( typeof( user1[args.orderby] || (user1.metas||{})[args.orderby] ) == 'string' ) {
var diff = (user1[args.orderby] || (user1.metas||{})[args.orderby]).localeCompare( user2[args.orderby] || (user2.metas||{})[args.orderby] )
} else {
var diff = (user1[args.orderby] || (user1.metas||{})[args.orderby]) - (user2[args.orderby] || (user2.metas||{})[args.orderby]);
}
return (args.order || 'ASC') == "ASC" ? diff : -diff;
})
}
}
//分页(和文章、评论不同的是:如果没有分页参数,那么返回全部terms。而不是默认分页个数)
if (args.current_page !== undefined || args.users_per_page !== undefined) {
var current_page = parseInt(args.current_page) || 0;
if (current_page <= 0) {current_page = 1}
var users_per_page = parseInt(args.users_per_page) || 0;
if (users_per_page <= 0) {users_per_page = 10}
var users_start_index = ( current_page - 1 ) * users_per_page;
var usersPaged = users.slice(users_start_index, users_start_index + users_per_page);
} else {
var usersPaged = users;
}
// 将tree展开,理由:方便写api接口的时候对每个user数据处理。否则循环起来不方便
var expanded = [];
var expander = arr => {
arr.forEach(item=>{
var {children} = item;
if (children) {
delete item.children;
if (children.length) {
expanded.push(item);
return expander(children)
}
}
expanded.push(item)
})
}
expander(usersPaged)
// 输出前过滤数据
usersPaged = usersPaged.map(user=>{
delete user.password;
// user.role = get_user_role(user.power);
// return apply_filters('get_user',user,user.id);
return user;
})
//有分页,返回时带上分页信息。否则仅返回数据
if (args.current_page !== undefined || args.users_per_page !== undefined) {
return {
data: usersPaged,
pagination: {
current_page,
users_per_page,
total: users.length
}
}
} else {
return usersPaged;
}
}
global.update_user_meta = (user_id,key,value) => {
return update_metadata('user',user_id,key,value);
}
global.get_user_meta = (user_id,key)=>{
return get_metadata('user',user_id,key);
}
global.delete_user_meta = (user_id,key) => {
return delete_metadata('user',user_id,key);
}