diff --git a/core/pluggable.coffee b/core/pluggable.coffee index 9db313e..ca05a22 100644 --- a/core/pluggable.coffee +++ b/core/pluggable.coffee @@ -45,6 +45,11 @@ exports.hooks = # path styles: [] + admin: + # generator: function(req, callback) + sidebars: _.extend [], + global_event: true + panel: # path scripts: [] diff --git a/core/router/admin.coffee b/core/router/admin.coffee index e2f69c3..ba733cc 100644 --- a/core/router/admin.coffee +++ b/core/router/admin.coffee @@ -9,9 +9,15 @@ exports.use requireAdminAuthenticate exports.get '/', (req, res) -> Account.find {}, (err, accounts) -> - return res.render 'admin', - accounts: accounts - coupon_code_types: _.keys config.coupons_meta + async.map pluggable.selectHook(null, 'view.admin.sidebars'), (hook, callback) -> + hook.generator req, (html) -> + callback null, html + + , (err, sidebars_html) -> + res.render 'admin', + accounts: accounts + sidebars_html: sidebars_html + coupon_code_types: _.keys config.coupons_meta exports.get '/account_details', (req, res) -> Account.findById req.query.account_id, (err, account) -> diff --git a/core/view/admin.jade b/core/view/admin.jade index abb489e..ede94ab 100644 --- a/core/view/admin.jade +++ b/core/view/admin.jade @@ -111,6 +111,9 @@ block main button.btn.btn-success.action-confirm-payment(type='button')= t('common.create') prepend sidebar + for sidebar_html in sidebars_html + != sidebar_html + .row a.btn.btn-lg.btn-success(href='/admin/ticket/')= t('ticket.ticket_list') diff --git a/plugin/shadowsocks/index.coffee b/plugin/shadowsocks/index.coffee index acae322..5627ad8 100644 --- a/plugin/shadowsocks/index.coffee +++ b/plugin/shadowsocks/index.coffee @@ -1,5 +1,6 @@ {_, fs} = app.libs {pluggable, config, utils} = app +{Financials} = app.models exports = module.exports = class ShadowSocksPlugin extends pluggable.Plugin @NAME: 'shadowsocks' @@ -31,6 +32,32 @@ exports.registerHook 'view.panel.widgets', exports.render 'widget', req, result, callback +exports.registerHook 'view.admin.sidebars', + generator: (req, callback) -> + Financials.find + type: 'usage_billing' + 'payload.service': 'shadowsocks' + created_at: + $gte: new Date Date.now() - 30 * 24 * 3600 * 1000 + , (err, financials) -> + time_range = + traffic_24hours: 24 * 3600 * 1000 + traffic_3days: 3 * 24 * 3600 * 1000 + traffic_7days: 7 * 24 * 3600 * 1000 + traffic_30days: 30 * 24 * 3600 * 1000 + + traffic_result = {} + + for name, range of time_range + logs = _.filter financials, (i) -> + return i.created_at.getTime() > Date.now() - range + + traffic_result[name] = _.reduce logs, (memo, i) -> + return memo + i.payload.traffic_mb + , 0 + + exports.render 'admin/sidebar', req, traffic_result, callback + exports.registerHook 'app.started', action: -> shadowsocks.initSupervisor -> diff --git a/plugin/shadowsocks/locale/en.json b/plugin/shadowsocks/locale/en.json index 112f2c8..777e5a7 100644 --- a/plugin/shadowsocks/locale/en.json +++ b/plugin/shadowsocks/locale/en.json @@ -4,8 +4,10 @@ "remote_port": "Port", "reset": "Reset", "transfer": "Traffic", + "method": "method", "transfer_remainder": "About __traffic__ G remainder", "24hours_ago": "24 hours ago", + "3days_ago": "3 days ago", "7days_ago": "7 days ago", "30days_ago": "30 days ago" } diff --git a/plugin/shadowsocks/locale/zh_CN.json b/plugin/shadowsocks/locale/zh_CN.json index ff44273..bb0ef56 100644 --- a/plugin/shadowsocks/locale/zh_CN.json +++ b/plugin/shadowsocks/locale/zh_CN.json @@ -4,8 +4,10 @@ "remote_port": "端口", "reset": "重置", "transfer": "流量", + "method": "加密算法", "transfer_remainder": "余额折合约 __traffic__ G 流量", "24hours_ago": "过去 24 小时", + "3days_ago": "过去 3 天", "7days_ago": "过去 7 天", "30days_ago": "过去 30 天" } diff --git a/plugin/shadowsocks/router.coffee b/plugin/shadowsocks/router.coffee index ed4f02c..67af8fa 100644 --- a/plugin/shadowsocks/router.coffee +++ b/plugin/shadowsocks/router.coffee @@ -1,4 +1,4 @@ -{utils} = app +{utils, config} = app {markdown, fs, path, express} = app.libs {requireInService} = app.middleware @@ -15,20 +15,19 @@ exports.post '/reset_password', (req, res) -> $set: 'pluggable.shadowsocks.password': password , -> - req.account.pluggable.shadowsocks.password = password - - shadowsocks.updateConfigure req.account, -> + shadowsocks.updateConfigure -> res.json {} exports.post '/switch_method', (req, res) -> unless req.body.method in config.plugins.shadowsocks.available_ciphers return res.error 'invalid_method' + if req.body.method == req.account.pluggable.shadowsocks.method + return res.error 'already_in_method' + req.account.update $set: - 'pluggable.shadowsocks.method': method + 'pluggable.shadowsocks.method': req.body.method , -> - req.account.pluggable.shadowsocks.method = method - - shadowsocks.updateConfigure req.account, -> + shadowsocks.updateConfigure -> res.json {} diff --git a/plugin/shadowsocks/shadowsocks.coffee b/plugin/shadowsocks/shadowsocks.coffee index 6aea27c..66edecc 100644 --- a/plugin/shadowsocks/shadowsocks.coffee +++ b/plugin/shadowsocks/shadowsocks.coffee @@ -16,14 +16,14 @@ exports.initSupervisor = (callback) -> if program_name in _.pluck program_status, 'name' return callback() - shadowsocks_config_file = "/etc/shadowsocks/#{method}.json" configure = exports.generateConfigure [], method: method - ShadowsocksPlugin.writeConfigFile shadowsocks_config_file, configure, {mode: 0o755}, -> + filename = "/etc/shadowsocks/#{method}.json" + ShadowsocksPlugin.writeConfigFile filename, configure, {mode: 0o755}, -> supervisor.writeConfig {username: 'nobody'}, program_name: program_name - command: "ssserver -c #{shadowsocks_config_file}" + command: "ssserver -c #{filename}" name: program_name autostart: true autorestart: true @@ -109,12 +109,12 @@ exports.initAccount = (account, callback) -> child_process.exec "sudo iptables -I OUTPUT -p tcp --sport #{port}", -> child_process.exec 'sudo iptables-save | sudo tee /etc/iptables.rules', -> - exports.updateConfigure account, -> + exports.updateConfigure -> callback() exports.deleteAccount = (account, callback) -> exports.queryIptablesInfo (iptables_info) -> - {port, method} = account.pluggable.shadowsocks + {port} = account.pluggable.shadowsocks billing_traffic = iptables_info[port].bytes - account.pluggable.shadowsocks.last_traffic_value billing_traffic = iptables_info[port].bytes if billing_traffic < 0 @@ -136,10 +136,7 @@ exports.deleteAccount = (account, callback) -> child_process.exec 'sudo iptables-save | sudo tee /etc/iptables.rules', callback (callback) -> - exports.deleteAccountConfigure account, callback - - (callback) -> - supervisor.updateProgram {}, {program_name: "shadowsocks-#{method}"}, callback + exports.updateConfigure callback ], -> if amount > 0 @@ -178,60 +175,24 @@ exports.accountUsage = (account, callback) -> callback result -exports.updateConfigure = (account, callback) -> - {port, method, password} = account.pluggable.shadowsocks - original_method = null - +exports.updateConfigure = (callback) -> async.eachSeries config.plugins.shadowsocks.available_ciphers, (method, callback) -> - shadowsocks_config_file = "/etc/shadowsocks/#{method}.json" + Account.find + 'pluggable.shadowsocks.method': method + , (err, accounts) -> + users = _.map accounts, (account) -> + return account.pluggable.shadowsocks - fs.readFile shadowsocks_config_file, (err, content) -> - if port.toString() in _.keys JSON.parse(content).port_password - original_method = method - callback true - else - callback() + configure = exports.generateConfigure users, + method: method - , -> - async.series [ - (callback) -> - shadowsocks_config_file = "/etc/shadowsocks/#{method}.json" - - fs.readFile shadowsocks_config_file, (err, content) -> - configure = JSON.parse content - configure.port_password[port] = password - - ShadowsocksPlugin.writeConfigFile shadowsocks_config_file, JSON.stringify(configure), {mode: 0o755}, -> - callback() - - (callback) -> + filename = "/etc/shadowsocks/#{method}.json" + ShadowsocksPlugin.writeConfigFile filename, configure, {mode: 0o755}, -> supervisor.updateProgram {}, {program_name: "shadowsocks-#{method}"}, -> callback() - (callback) -> - if original_method == original_method - return callback() - - account = account.toObject() - account.pluggable.shadowsocks.method = original_method - - exports.deleteAccountConfigure account, -> - supervisor.updateProgram {}, {program_name: "shadowsocks-#{original_method}"}, -> - callback() - - ], -> - callback() - -exports.deleteAccountConfigure = (account, callback) -> - {port, method} = account.pluggable.shadowsocks - shadowsocks_config_file = "/etc/shadowsocks/#{method}.json" - - fs.readFile shadowsocks_config_file, (err, content) -> - configure = JSON.parse content - delete configure.port_password[port] - - fs.writeFile shadowsocks_config_file, JSON.stringify(configure), -> - callback() + , -> + callback() exports.monitoring = -> exports.queryIptablesInfo (iptables_info) -> diff --git a/plugin/shadowsocks/static/script/panel.coffee b/plugin/shadowsocks/static/script/panel.coffee index 6511339..b4e6629 100644 --- a/plugin/shadowsocks/static/script/panel.coffee +++ b/plugin/shadowsocks/static/script/panel.coffee @@ -1,11 +1,11 @@ $ -> - $('#widget-shadowsocks .action-reset-password').click -> + $('.widget-shadowsocks .action-reset-password').click -> if window.confirm 'Are you sure?' - $.post '/plugin/shadowsocks/reset_password/', {} - .fail (jqXHR) -> - if jqXHR.responseJSON?.error - alert jqXHR.responseJSON.error - else - alert jqXHR.statusText - .success -> + request '/plugin/shadowsocks/reset_password/', {}, -> location.reload() + + $('.widget-shadowsocks .action-switch-method').click -> + request '/plugin/shadowsocks/switch_method/', + method: $('.widget-shadowsocks .input-method').val() + , -> + alert 'Success' diff --git a/plugin/shadowsocks/view/admin/sidebar.jade b/plugin/shadowsocks/view/admin/sidebar.jade new file mode 100644 index 0000000..e2edab0 --- /dev/null +++ b/plugin/shadowsocks/view/admin/sidebar.jade @@ -0,0 +1,15 @@ +.row + table.table.table-hover + tbody + tr + td #{(traffic_24hours / 1000).toFixed(1)}G + td= t('24hours_ago') + tr + td #{(traffic_3days / 1000).toFixed(1)}G + td= t('3days_ago') + tr + td #{(traffic_7days / 1000).toFixed(1)}G + td= t('7days_ago') + tr + td #{(traffic_30days / 1000).toFixed(1)}G + td= t('30days_ago') diff --git a/plugin/shadowsocks/view/widget.jade b/plugin/shadowsocks/view/widget.jade index 86d650f..ce89c8f 100644 --- a/plugin/shadowsocks/view/widget.jade +++ b/plugin/shadowsocks/view/widget.jade @@ -13,6 +13,13 @@ input.form-control(type='text', value=account.pluggable.shadowsocks.password, disabled) span.input-group-btn button.btn.btn-default.action-reset-password(type='button')= t('reset') + .input-group + span.input-group-addon= t('method') + select.input-method.form-control(style='-webkit-appearance: none;') + for method in config.plugins.shadowsocks.available_ciphers + option= method + span.input-group-btn + button.btn.btn-default.action-switch-method(type='button')= t('common.change') .col-md-6 .panel.panel-default diff --git a/plugin/wiki/index.coffee b/plugin/wiki/index.coffee index abedb9c..8ca7d72 100644 --- a/plugin/wiki/index.coffee +++ b/plugin/wiki/index.coffee @@ -9,7 +9,7 @@ exports.registerHook 'view.layout.menu_bar', href: '/wiki/' t_body: 'plugins.wiki.' -unless config.plugins.wiki.disable_default_wiki +unless config.plugins.wiki?.disable_default_wiki wiki_path = "#{__dirname}/../../WIKI" for category_name in fs.readdirSync(wiki_path)