Authored by 姜枫

添加用户管理

... ... @@ -5,6 +5,7 @@ import ServerModel from './server';
import BuildingModel from './building';
import ProjectModel from './project';
import DeployModel from './deploy';
import UserModel from './user';
shelljs.mkdir('-p', config.dbDir);
... ... @@ -12,10 +13,12 @@ const Server = new ServerModel();
const Building = new BuildingModel();
const Project = new ProjectModel();
const DeployInfo = new DeployModel();
const User = new UserModel();
export {
Server,
Building,
Project,
DeployInfo
DeployInfo,
User
};
\ No newline at end of file
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/8/16
*/
'use strict';
import Model from './model';
class User extends Model {
constructor() {
super('users');
}
findByUsername(username) {
return this.findOne({
username: username
});
}
}
export default User;
\ No newline at end of file
... ...
'use strict';
import Router from 'koa-router';
import md5 from 'md5';
import {User} from '../../models';
let r = new Router();
... ... @@ -12,9 +14,11 @@ const login = {
let username = ctx.request.body.username;
let password = ctx.request.body.password;
if (username === 'yoho' && password === 'yoho9646') {
let user = await User.findByUsername(username);
if (user && password && user.password === md5(password)) {
ctx.session = {
user: 'yoho'
user: user
};
ctx.redirect('/projects');
ctx.status = 301;
... ...
... ... @@ -18,6 +18,7 @@ let r = new Router();
const colors = ['primary', 'success', 'info', 'warning', 'danger', 'success-alt', 'info-alt', 'warning-alt', 'danger-alt', 'primary-head', 'success-head', 'danger-head'];
const envs = {
production: '线上环境',
preview: '灰度环境',
test: '测试环境'
};
... ... @@ -88,6 +89,7 @@ const p = {
}
}
});
},
edit_page: async(ctx, next) => {
let id = ctx.query.id;
... ...
... ... @@ -10,6 +10,7 @@ const r = new Router;
const envs = {
production: '线上环境',
preview: '灰度环境',
test: '测试环境'
};
... ...
/**
* 用户管理
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/8/16
*/
'use strict';
import Router from 'koa-router';
import md5 from 'md5';
import {User} from '../../models';
const r = new Router;
const user = {
async setting_page(ctx) {
let users = await User.findAll();
await ctx.render('action/users', {
users: users
});
},
async new_page(ctx) {
await ctx.render('action/users_form');
},
async edit_page(ctx) {
let id = ctx.query.id;
let user = await User.findById(id);
user.password = "******";
await ctx.render('action/users_form', user);
},
async save(ctx) {
let {_id, username, password, role, state} = ctx.request.body;
let user = {
username: username,
role: role,
state: state
};
if (password !== '******') {
user.password = md5(password);
}
if (_id) {
await User.update({
_id: _id
}, {
$set: user
});
} else {
await User.insert(user);
}
ctx.redirect('/users/setting');
ctx.status = 301;
},
async del(ctx) {
let id = ctx.request.body.id;
await User.removeById(id);
ctx.body = {
code: 200
};
}
};
r.get('/setting', user.setting_page);
r.get('/new', user.new_page);
r.get('/edit', user.edit_page);
r.post('/save', user.save);
r.post('/del', user.del);
export default r;
\ No newline at end of file
... ...
... ... @@ -19,6 +19,16 @@ app.use(hbs({
helpers: helpers
}));
const mastersUrl = [
'/projects/new',
'/projects/edit',
'/projects/save',
'/projects/build',
'/projects/deploy',
'/servers',
'/users'
];
app.use(async(ctx, next) => {
ctx.locals = {
title: 'Yoho Node.js 持续集成平台'
... ... @@ -27,8 +37,23 @@ app.use(async(ctx, next) => {
if (pjax) {
ctx.locals.layout = null;
}
await next();
if (ctx.session && ctx.session.user ) {
ctx.locals.is_master = ctx.session.user.role === '1000';
ctx.locals.current_user = ctx.session.user;
}
let needMaster = mastersUrl.some(u => {
return ctx.request.path.indexOf(u) === 0;
});
if (needMaster) {
if (ctx.locals.is_master) {
await next();
}
} else {
await next();
}
// if (pjax && ctx.status == 301) {
// let location = ctx.response.get('Location');
// ctx.response.set('X-PJAX-URL', ctx.origin + location);
... ...
... ... @@ -6,6 +6,7 @@ import projects from './actions/projects';
import servers from './actions/servers';
import login from './actions/login';
import monitor from './actions/monitor';
import users from './actions/users';
const noAuth = new Router();
const base = new Router();
... ... @@ -28,6 +29,7 @@ export default function (app) {
base.use('/projects', projects.routes(), projects.allowedMethods());
base.use('/servers', servers.routes(), servers.allowedMethods());
base.use('/monitor', monitor.routes(), monitor.allowedMethods());
base.use('/users', users.routes(), users.allowedMethods());
base.use('', index.routes(), index.allowedMethods());
... ...
... ... @@ -56,8 +56,13 @@
<div class="col-sm-8 col-md-9">
<div class="panel">
<div class="panel-heading">
<div class="pull-right">
<a class="btn btn-success btn-rounded mr20 realtime-btn">开启实时加载</a>
</div>
<h4 class="panel-title">日志</h4>
<p>可通过滚动鼠标加载</p>
</div><!-- panel-heading -->
<div class="panel-body yoho-log-dark">
<div class="results-list ">
... ... @@ -214,6 +219,7 @@
return;
} else {
loading = true;
var scH = $logWrap[0].scrollHeight;
$.get('/monitor/log/query', {
influxName: influxName,
ip: ip,
... ... @@ -227,20 +233,35 @@
$(data).each(function() {
appendLog(this);
});
$logWrap.scrollTop(scH);
});
}
}
var scrollH, scrollTop = 0;
$logWrap.scroll(function(e) {
$logWrap.scroll(function() {
scrollH = $(this)[0].scrollHeight;
scrollTop = $(this)[0].scrollTop;
if (scrollTop < 10) {
loadOld();
} else if (scrollTop + $logWrap.height() + 50 >= scrollH) {
} else if (scrollTop + $logWrap.height() + 40 >= scrollH) {
loadNew();
}
});
var sc;
$('.realtime-btn').click(function() {
if ($(this).hasClass('active')) {
clearInterval(sc);
$(this).removeClass('active');
$(this).text('开启实时加载');
} else {
$(this).addClass('active');
$(this).text('关闭实时加载');
sc = setInterval(loadNew, 2000);
}
});
});
</script>
\ No newline at end of file
... ...
... ... @@ -83,12 +83,13 @@
<div class="col-sm-12">
<h5 class="lg-title mb10">环境配置</h5>
<ul class="nav nav-tabs nav-primary">
<li class="active"><a href="#home4" data-toggle="tab"><strong>线上环境</strong></a></li>
<li><a href="#profile4" data-toggle="tab"><strong>测试环境</strong></a></li>
<li class="active"><a href="#production4" data-toggle="tab"><strong>线上环境</strong></a></li>
<li><a href="#preview4" data-toggle="tab"><strong>灰度环境</strong></a></li>
<li><a href="#test4" data-toggle="tab"><strong>测试环境</strong></a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content tab-content-primary mb30">
<div class="tab-pane active" id="home4">
<div class="tab-pane active" id="production4">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
... ... @@ -126,8 +127,48 @@
</div>
</div>
</div><!-- tab-pane -->
<div class="tab-pane" id="profile4">
<div class="tab-pane" id="preview4">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="control-label">目标服务器</label>
<div class="col-sm-12">
{{#each servers.preview}}
<div class="checkbox inline-block mr10">
<label>
<input type="checkbox"
name="deploy[preview][target][{{@index}}]"
value="{{host}}"
{{#if checked}}checked=""{{/if}}> {{host}}
</label>
</div>
{{/each}}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">对应Git分支</label>
<input type="text" value="{{project.deploy.preview.branchName}}"
name="deploy[preview][branchName]" value="" placeholder="master"
class="form-control">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="control-label">测试URL</label>
<input type="text" value="{{project.deploy.preview.testUrl}}"
name="deploy[preview][testUrl]" placeholder="ex: http://{host}:8080/test"
class="form-control">
</div>
</div>
</div>
</div><!-- tab-pane -->
<div class="tab-pane" id="test4">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
... ...
... ... @@ -19,8 +19,10 @@
<div class="panel panel-default" data-env='{{deploy.env}}'>
<div class="panel-heading">
<div class="pull-right">
<a class="btn btn-info btn-rounded mr5 log-btn"><i class="fa fa-eye"></i> 查看构建日志</a>
<a class="btn btn-success btn-rounded mr20 build-btn"><i class="glyphicon glyphicon-plus"></i> 新增构建</a>
{{#if is_master}}
<a class="btn btn-info btn-rounded mr5 log-btn"><i class="fa fa-eye"></i> 查看构建日志</a>
<a class="btn btn-success btn-rounded mr20 build-btn"><i class="glyphicon glyphicon-plus"></i> 新增构建</a>
{{/if}}
<a href="" class="tooltips panel-minimize"><i class="fa fa-minus"></i></a>
</div>
<h4 class="panel-title">{{deploy.name}}</h4>
... ... @@ -29,18 +31,18 @@
<div class="panel-body">
<table id="table-{{deploy.env}}" class="table table-striped table-bordered building-table">
<thead>
<tr>
<th>构建版本</th>
<th>分支名称</th>
<th>状态</th>
<th>构建时间</th>
<th>操作</th>
</tr>
<tr>
<th>构建版本</th>
<th>分支名称</th>
<th>状态</th>
<th>构建时间</th>
<th>操作</th>
</tr>
</thead>
</table>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4>服务器信息</h4>
... ... @@ -49,29 +51,32 @@
<div class="panel-body">
<div class="row">
{{#each targets}}
<div class="col-md-4" id="d-{{hostFm}}">
<div class="panel panel-info noborder">
<div class="panel-heading noborder">
<div class="panel-btns">
</div><!-- panel-btns -->
<div class="panel-icon"><i class="fa fa-cloud" style="padding-left:12px;"></i></div>
<div class="media-body">
<h2 class="nomargin">{{host}}</h2>
<h5 class="md-title mt5">当前运行版本:&nbsp;<code>{{#if info}}{{info.building}}{{^}}未知部署{{/if}}</code></h5>
</div><!-- media-body -->
<hr class="mt10 mb10">
<div class="clearfix mt5">
<div class="col-xs-6 project-env" data-env="test">
<h5 class="md-title mt10">当前状态</h5>
<span class="label label-success deploy-log-btn" data-host="{{host}}"><i class="fa fa-spinner fa-spin fa-fw margin-bottom"></i> <b>{{#if info}}{{info.state}}{{^}}未知部署{{/if}}</b></span>
</div>
<div class="col-xs-6">
<div class="col-md-4" id="d-{{hostFm}}">
<div class="panel panel-info noborder">
<div class="panel-heading noborder">
<div class="panel-btns">
</div><!-- panel-btns -->
<div class="panel-icon"><i class="fa fa-cloud" style="padding-left:12px;"></i></div>
<div class="media-body">
<h2 class="nomargin">{{host}}</h2>
<h5 class="md-title mt5">当前运行版本:&nbsp;<code>{{#if info}}{{info.building}}{{^}}
未知部署{{/if}}</code></h5>
</div><!-- media-body -->
<hr class="mt10 mb10">
<div class="clearfix mt5">
<div class="col-xs-6 project-env" data-env="test">
<h5 class="md-title mt10">当前状态</h5>
<span class="label label-success deploy-log-btn" data-host="{{host}}"><i
class="fa fa-spinner fa-spin fa-fw margin-bottom"></i> <b>{{#if info}}{{info.state}}{{^}}
未知部署{{/if}}</b></span>
</div>
<div class="col-xs-6">
</div>
</div>
</div>
</div><!-- panel-body -->
</div><!-- panel -->
</div>
</div><!-- panel-body -->
</div><!-- panel -->
</div>
{{/each}}
</div>
</div>
... ... @@ -79,99 +84,103 @@
</div>
<div id="log-area" class="panel panel-default panel-alt" style="display:none;height: 100%;">
<div class="panel-body nopadding" style="height: 100%;">
<div class="panel-body nopadding" style="height: 100%;">
<textarea name="code" id="code" style="height: 100%;"></textarea>
</div>
</div>
<script>
$(function(){
$(function() {
var tables = {};
$('.building-table').each(function(){
$('.building-table').each(function() {
var env = $(this).parents('.panel').data('env');
tables[env] = $(this).DataTable({
responsive: true,
ajax: '/projects/{{project._id}}/buildings?env=' + env,
columns: [
{ data: "buildTime"},
{ data: "branch"},
{ data: "state"},
{ data: "updatedAt"},
{ data: "_id" }
{data: "buildTime"},
{data: "branch"},
{data: "state"},
{data: "updatedAt"},
{data: "_id"}
],
order: [[0, 'desc']],
columnDefs: [{
render: function(data, type, row){
render: function(data, type, row) {
var color = 'warning';
if(data == 'success'){
if (data == 'success') {
color = 'success';
}else if(data == 'fail'){
} else if (data == 'fail') {
color = 'danger';
}
var html = '<span id="b-'+row._id+'" class="label label-' + color + '">';
if(data != 'success' && data != 'fail'){
var html = '<span id="b-' + row._id + '" class="label label-' + color + '">';
if (data != 'success' && data != 'fail') {
html += '<i class="fa fa-spinner fa-spin fa-fw margin-bottom"></i>';
}
}
html += data + '</span>';
return html;
},
targets: 2
}, {
render: function(data, type, row){
render: function(data, type, row) {
var disabled = row.state !== 'success';
return '<button '+(disabled? 'disabled' : '')+' class="btn btn-success btn-xs deploy-btn" data-id="'+data+'" data-build='+row.buildTime+'>分发</button>';
{{#if is_master}}
return '<button ' + (disabled ? 'disabled' : '') + ' class="btn btn-success btn-xs deploy-btn" data-id="' + data + '" data-build=' + row.buildTime + '>分发</button>';
{{^}}
return '';
{{/if}}
},
targets: 4
}]
});
$(this).on( 'draw.dt', function () {
$('.deploy-btn').click(function(){
var id = $(this).data('id');
var build = $(this).data('build');
layer.confirm('确定发布版本<code>' + build + '</code>吗?', {
btn: ['确定', '取消']
}, function(){
doDeploy(id);
});
$(this).on('draw.dt', function() {
$('.deploy-btn').click(function() {
var id = $(this).data('id');
var build = $(this).data('build');
layer.confirm('确定发布版本<code>' + build + '</code>吗?', {
btn: ['确定', '取消']
}, function() {
doDeploy(id);
});
} );
});
});
});
function doDeploy(build){
function doDeploy(build) {
$.post('/projects/deploy/' + build, function(ret) {
if(ret.code == 200){
if (ret.code == 200) {
layer.msg('正在分发中');
}else{
} else {
layer.msg(ret.msg, {icon: 5});
}
});
}
$('.build-btn').click(function(){
$('.build-btn').click(function() {
var env = $(this).parents('.panel').data('env');
var i = layer.prompt({
var i = layer.prompt({
title: '请输入需要构建的分支,默认为 {{deploy.branchName}}'
}, function(branch){
}, function(branch) {
branch = branch || '{{deploy.branchName}}';
$.post('/projects/build/{{project._id}}', {env: env, branch: branch}, function(ret){
if(ret.code == 200) {
$.post('/projects/build/{{project._id}}', {env: env, branch: branch}, function(ret) {
if (ret.code == 200) {
tables[env].ajax.reload();
layer.close(i);
}
});
});
});
$('.rollback-btn').click(function(){
$('.rollback-btn').click(function() {
layer.prompt({
title: '请输入回滚到哪个版本'
}, function(build){
doDeploy(build);
}, function(build) {
doDeploy(build);
});
});
... ... @@ -181,17 +190,17 @@
});
var tag;
$('.log-btn').click(function(){
$('.log-btn').click(function() {
viewLog('');
});
$('.deploy-log-btn').click(function(){
$('.deploy-log-btn').click(function() {
var host = $(this).data('host');
viewLog(host);
});
function viewLog(tag){
function viewLog(tag) {
tag = tag;
$('#log-area').show();
cm.setValue("");
... ... @@ -201,46 +210,46 @@
title: '实时日志',
content: $('#log-area'),
area: ['1000px', '600px'],
maxmin:true,
cancel: function(){
maxmin: true,
cancel: function() {
$('#log-area').hide();
}
});
}
var ws = io();
ws.on('connect', function(){
ws.on('/building/{{project._id}}', function(data){
ws.on('connect', function() {
ws.on('/building/{{project._id}}', function(data) {
console.log(data);
$('#b-' + data.bid).text(data.state);
if(data.state == 'success'){
if (data.state == 'success') {
$('#b-' + data.bid).removeClass('label-warning').addClass('label-success')
$('#b-' + data.bid).parent().parent().find('.deploy-btn').removeAttr('disabled');
$('#b-' + data.bid).find('i').remove();
}else if(data.state == 'fail'){
} else if (data.state == 'fail') {
$('#b-' + data.bid).removeClass('label-warning').addClass('label-danger');
$('#b-' + data.bid).find('i').remove();
}
});
// ws.on('/building/{{project._id}}/log', function(data){
// if(tag == '') {
// cm.replaceRange("> " + data + "\n", {line: Infinity});
// }
// });
ws.on('/deploy/{{project._id}}', function(data){
// ws.on('/building/{{project._id}}/log', function(data){
// if(tag == '') {
// cm.replaceRange("> " + data + "\n", {line: Infinity});
// }
// });
ws.on('/deploy/{{project._id}}', function(data) {
console.log(data);
$('#d-' + data.host.replace(/\./g, '-')).find('b').text(data.state);
});
// ws.on('/deploy/{{project._id}}/log', function(data){
// if(tag == data.host){
// cm.replaceRange("> " +data.msg+ "\n", {line: Infinity});
// }
// });
// ws.on('/deploy/{{project._id}}/log', function(data){
// if(tag == data.host){
// cm.replaceRange("> " +data.msg+ "\n", {line: Infinity});
// }
// });
});
ws.on('error', function(){
ws.on('error', function() {
console.log('connect fail');
});
});
... ...
... ... @@ -18,59 +18,68 @@
<div class="contentpanel servers-page">
<div class="row row-stat">
{{#each projects}}
<div class="col-md-4">
<div class="panel panel-{{color}} noborder">
<div class="panel-heading noborder">
<div class="panel-btns">
<a href="/projects/edit?id={{_id}}" class="tooltips" title="设置"><i class="fa fa-gear"></i></a>
</div><!-- panel-btns -->
<div class="panel-icon"><i class="fa fa-git" style="padding-left:12px;"></i></div>
<div class="media-body">
<h1 class="nomargin">{{name}}</h1>
<h5 class="md-title mt5">{{subname}}&nbsp;</h5>
</div><!-- media-body -->
<hr>
<div class="clearfix mt20">
<div class="col-xs-6 project-env" data-id="{{_id}}" data-env="production">
<h5 class="md-title nomargin">线上环境</h5>
<h4 class="nomargin">{{deploy.production.target.length}}</h4>
<div class="col-md-4">
<div class="panel panel-{{color}} noborder">
<div class="panel-heading noborder">
<div class="panel-btns">
{{#if @root.is_master}}
<a href="/projects/edit?id={{_id}}" class="tooltips" title="设置"><i
class="fa fa-gear"></i></a>
{{/if}}
</div><!-- panel-btns -->
<div class="panel-icon"><i class="fa fa-git" style="padding-left:12px;"></i></div>
<div class="media-body">
<h1 class="nomargin">{{name}}</h1>
<h5 class="md-title mt5">{{subname}}&nbsp;</h5>
</div><!-- media-body -->
<hr>
<div class="clearfix mt20">
<div class="col-xs-4 project-env" data-id="{{_id}}" data-env="production">
<h5 class="md-title nomargin">线上环境</h5>
<h4 class="nomargin">{{deploy.production.target.length}}</h4>
</div>
<div class="col-xs-4 project-env" data-id="{{_id}}" data-env="preview">
<h5 class="md-title nomargin">灰度环境</h5>
<h4 class="nomargin">{{deploy.preview.target.length}}</h4>
</div>
<div class="col-xs-4 project-env" data-id="{{_id}}" data-env="test">
<h5 class="md-title nomargin">测试环境</h5>
<h4 class="nomargin">{{deploy.test.target.length}}</h4>
</div>
</div>
<div class="col-xs-6 project-env" data-id="{{_id}}" data-env="test">
<h5 class="md-title nomargin">测试环境</h5>
<h4 class="nomargin">{{deploy.test.target.length}}</h4>
</div>
</div>
</div><!-- panel-body -->
</div><!-- panel -->
</div><!-- col-md-4 -->
</div><!-- panel-body -->
</div><!-- panel -->
</div><!-- col-md-4 -->
{{/each}}
<div class="col-md-4">
<div class="panel panel-default noborder">
<div class="panel-heading noborder">
<div style="text-align: center; font-size: 97px;">
<a href="/projects/new" class="">
<i class="fa fa-plus"></i>
</a>
{{#if is_master}}
<div class="col-md-4">
<div class="panel panel-default noborder">
<div class="panel-heading noborder">
<div style="text-align: center; font-size: 97px;">
<a href="/projects/new" class="">
<i class="fa fa-plus"></i>
</a>
</div>
</div>
</div>
</div>
</div>
{{/if}}
</div><!-- row -->
</div>
<script>
$(function() {
$('.servers-page').pjax('a', '#pjax-container');
$('.project-env').click(function(){
var id = $(this).data('id');
var env = $(this).data('env');
$.pjax({
url: '/projects/' + id + '?env=' + env,
container: '#pjax-container'
});
});
$('.servers-page').pjax('a', '#pjax-container');
$('.project-env').click(function() {
var id = $(this).data('id');
var env = $(this).data('env');
$.pjax({
url: '/projects/' + id + '?env=' + env,
container: '#pjax-container'
});
});
});
</script>
\ No newline at end of file
... ...
... ... @@ -73,7 +73,14 @@
</div>
</div>
</div>
<div class="col-sm-4">
<div class="col-sm-2">
<label class="control-label">&nbsp;</label>
<div class="rdio rdio-primary">
<input type="radio" name="env" id="previewEnv" value="preview" {{#equals env 'preview'}}checked="checked"{{/equals}}>
<label for="previewEnv">灰度环境</label>
</div>
</div>
<div class="col-sm-2">
<label class="control-label">&nbsp;</label>
<div class="rdio rdio-primary">
<input type="radio" name="env" id="testEnv" value="test" {{#equals env 'test'}}checked="checked"{{/equals}}>
... ...
... ... @@ -46,7 +46,7 @@
<td>{{host}}</td>
<td>{{username}}</td>
<td>{{port}}</td>
<td><span class="label label-primary">{{envName}}</span></td>
<td><span class="label label-primary {{env}}">{{envName}}</span></td>
<td>{{deployDir}}</td>
<td data-id='{{_id}}'>
<button class="btn btn-success btn-xs server-edit">修改</button> &nbsp;
... ...
<div class="pageheader">
<div class="media">
<div class="pageicon pull-left">
<i class="fa fa-th-list"></i>
</div>
<div class="media-body">
<ul class="breadcrumb">
<li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
<li><a href="">系统设置</a></li>
<li>用户管理</li>
</ul>
<h4>用户管理</h4>
</div>
</div>
<!-- media -->
</div>
<!-- pageheader -->
<div class="contentpanel">
<div class="panel panel-primary-head">
<div class="panel-heading">
<div class="pull-right">
<a id="new-page" href="/users/new" class="btn btn-success btn-rounded"><i class="glyphicon glyphicon-plus"></i> 新增用户</a>
</div>
<h4 class="panel-title">用户管理</h4>
<p>&nbsp;</p>
</div>
<!-- panel-heading -->
<table id="basicTable" class="table table-striped table-bordered responsive">
<thead class="">
<tr>
<th>用户名</th>
<th>角色</th>
<th>状态</th>
<th></th>
</tr>
</thead>
<tbody>
{{#each users}}
<tr>
<td>{{username}}</td>
<td>{{#equals role '1000'}}<span class="label label-primary">运维</span>{{^}}<span class="label label-success">开发</span>{{/equals}}</td>
<td>{{#equals state '1'}}<span class="label label-success">启用</span>{{^}}<span class="label label-danger">禁用</span>{{/equals}}</td>
<td data-id='{{_id}}'>
<button class="btn btn-success btn-xs server-edit">修改</button> &nbsp;
<button class="btn btn-danger btn-xs server-del">删除</button>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<!-- panel -->
</div>
<script>
$(function() {
$("#basicTable").DataTable({
responsive: true
});
$(document).pjax('#new-page', '#pjax-container')
$('.server-del').click(function(){
var id = $(this).parent().data('id');
$.post('/users/del', {id: id}, function(ret){
if(ret.code == 200){
var i = layer.alert('操作成功', function(){
layer.close(i);
$.pjax.reload('#pjax-container');
});
}
});
});
$('.server-edit').click(function(){
var id = $(this).parent().data('id');
$.pjax({
url: '/users/edit?id=' + id,
container: '#pjax-container'
});
});
});
</script>
\ No newline at end of file
... ...
<div class="pageheader">
<div class="media">
<div class="pageicon pull-left">
<i class="fa fa-th-list"></i>
</div>
<div class="media-body">
<ul class="breadcrumb">
<li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
<li><a href="">服务器管理</a></li>
<li>用户信息</li>
</ul>
<h4>用户信息</h4>
</div>
</div>
<!-- media -->
</div>
<!-- pageheader -->
<div class="panel panel-default">
<div class="panel-heading">
<p>&nbsp;</p>
</div>
<form action="/users/save" method="POST" data-pjax>
<input type="hidden" name="_id" value="{{_id}}">
<div class="panel-body">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">Username</label>
<input type="text" name="username" value="{{username}}" class="form-control" placeholder="用户名">
</div>
<!-- form-group -->
</div>
<!-- col-sm-6 -->
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">Password</label>
<input type="text" name="password" value="{{password}}" class="form-control" placeholder="密码">
</div>
<!-- form-group -->
</div>
<!-- col-sm-6 -->
</div>
<!-- row -->
<div class="row">
<div class="col-sm-2">
<div class="form-group">
<label class="control-label">Role</label>
<div class="rdio rdio-primary">
<input type="radio" name="role" id="role1" value="1000" {{#equals role '1000'}}checked="checked"{{/equals}}>
<label for="role1">运维</label>
</div>
</div>
</div>
<div class="col-sm-4">
<label class="control-label">&nbsp;</label>
<div class="rdio rdio-primary">
<input type="radio" name="role" id="role2" value="2000" {{#equals role '2000'}}checked="checked"{{/equals}}>
<label for="role2">开发</label>
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label class="control-label">状态</label>
<div class="rdio rdio-primary">
<input type="radio" name="state" id="state1" value="1" {{#equals state '1'}}checked="checked"{{/equals}}>
<label for="state1">启用</label>
</div>
</div>
</div>
<div class="col-sm-4">
<label class="control-label">&nbsp;</label>
<div class="rdio rdio-primary">
<input type="radio" name="state" id="state2" value="0" {{#equals state '0'}}checked="checked"{{/equals}}>
<label for="state2">禁用</label>
</div>
</div>
</div>
<!-- row -->
</div>
<!-- panel-body -->
<div class="panel-footer">
<button type="submit" class="btn btn-primary">保存</button>
<button type="button" class="btn btn-default ml20 go-back">取消</button>
</div>
</form>
<!-- panel-footer -->
</div>
<script>
$(function(){
$(document.body).off().on('submit', 'form[data-pjax]', function(event) {
event.preventDefault(); // stop default submit behavior
$.pjax.submit(event, '#pjax-container', {
type: 'POST'
});
});
$('.go-back').click(function() {
window.history.go(-1);
});
});
</script>
... ...
... ... @@ -4,7 +4,7 @@
<img class="img-circle" src="/images/photos/profile.png" alt="">
</a>
<div class="media-body">
<h4 class="media-heading">Admin</h4>
<h4 class="media-heading">{{current_user.username}}</h4>
<small class="text-muted">yoho-ci</small>
</div>
</div>
... ... @@ -13,17 +13,20 @@
<h5 class="leftpanel-title">Navigation</h5>
<ul class="nav nav-pills nav-stacked nav-menu">
<li class="active"><a href="/index"><i class="fa fa-home"></i> <span>Dashboard</span></a></li>
<li><a href="/projects"><i class="glyphicon glyphicon-th"></i> <span>Projects</span></a></li>
<li><a href="/projects"><i class="glyphicon glyphicon-th"></i> <span>项目</span></a></li>
<li class="parent"><a href=""><i class="fa fa-gears"></i> <span>监控中心</span></a>
<ul class="children">
<li><a href="/monitor/log">实时日志</a></li>
</ul>
</li>
<li class="parent"><a href=""><i class="fa fa-gears"></i> <span>Settings</span></a>
{{#if is_master}}
<li class="parent"><a href=""><i class="fa fa-gears"></i> <span>系统配置</span></a>
<ul class="children">
<li><a href="/servers/setting">Servers</a></li>
<li><a href="/servers/setting">服务器配置</a></li>
<li><a href="/users/setting">用户管理</a></li>
</ul>
</li>
{{/if}}
</ul>
</div>
\ No newline at end of file
... ...
... ... @@ -47,6 +47,7 @@
"koa-session": "^3.3.1",
"koa-static": "^3.0.0",
"lodash": "^4.13.1",
"md5": "^2.1.0",
"moment": "^2.13.0",
"nedb": "^1.8.0",
"nedb-promise": "^2.0.0",
... ...
... ... @@ -79,4 +79,16 @@ body .mainwrapper .leftpanel {
.yoho-log-dark .host {
width: 140px;
display: inline-block;
}
.label.production {
background-color: #428bca;
}
.label.preview {
background-color: #F5A503;
}
.label.test {
background-color: #5cb85c;
}
\ No newline at end of file
... ...