mirror of
https://github.com/HackPlan/RootPanel.git
synced 2026-01-13 07:01:20 +08:00
refactor shadowsocks
This commit is contained in:
@@ -130,6 +130,8 @@
|
||||
apt-get install python-pip python-m2crypto
|
||||
pip install shadowsocks
|
||||
|
||||
mkdir /etc/shadowsocks
|
||||
|
||||
vi /etc/default/supervisor
|
||||
|
||||
ulimit -n 51200
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{async, path, harp, jade, temp, fs, _} = app.libs
|
||||
{async, path, harp, jade, tmp, fs, _, child_process} = app.libs
|
||||
{i18n, config, logger} = app
|
||||
|
||||
exports.plugins = {}
|
||||
@@ -176,7 +176,7 @@ exports.Plugin = class Plugin
|
||||
template_path = "#{__dirname}/../plugin/#{@NAME}/template/#{name}"
|
||||
|
||||
fs.readFile template_path, (err, template_file) ->
|
||||
callback _.template(template_file) view_data
|
||||
callback _.template(template_file.toString()) view_data
|
||||
|
||||
@writeConfigFile: (filename, content, callback) ->
|
||||
tmp.file
|
||||
@@ -190,5 +190,5 @@ exports.Plugin = class Plugin
|
||||
child_process.exec "sudo cp #{filepath} #{filename}", (err) ->
|
||||
logger.error err if err
|
||||
|
||||
fs.unlink path, ->
|
||||
fs.unlink filepath, ->
|
||||
callback()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{pluggable, config, utils} = app
|
||||
{Financials} = app.models
|
||||
|
||||
exports = module.exports = class ShadowSocksPlugin extends pluggable.Plugin
|
||||
@NAME: 'shadowsocks'
|
||||
@@ -26,31 +25,16 @@ if config.plugins.shadowsocks.green_style
|
||||
|
||||
exports.registerHook 'view.panel.widgets',
|
||||
generator: (req, callback) ->
|
||||
Financials.find
|
||||
account_id: account._id
|
||||
type: 'usage_billing'
|
||||
'payload.service': 'shadowsocks'
|
||||
, (err, financials) ->
|
||||
time_range =
|
||||
traffic_24hours: 24 * 3600 * 1000
|
||||
traffic_7days: 7 * 24 * 3600 * 1000
|
||||
traffic_30days: 30 * 24 * 3600 * 1000
|
||||
|
||||
result = {}
|
||||
|
||||
for name, range of time_range
|
||||
logs = _.filter financials, (i) ->
|
||||
return i.created_at.getTime() > Date.now() - range
|
||||
|
||||
result[name] = _.reduce logs, (memo, i) ->
|
||||
return memo + i.payload.traffic_mb
|
||||
, 0
|
||||
|
||||
shadowsocks.accountUsage req.account, (result) ->
|
||||
_.extend result,
|
||||
transfer_remainder: account.billing.balance / config.plugins.shadowsocks.price_bucket / (1000 * 1000 * 1000 / config.plugins.shadowsocks.billing_bucket)
|
||||
|
||||
exports.render 'widget', req, result, callback
|
||||
|
||||
exports.registerHook 'app.started',
|
||||
action: ->
|
||||
shadowsocks.initSupervisor ->
|
||||
|
||||
exports.registerServiceHook 'enable',
|
||||
filter: (req, callback) ->
|
||||
shadowsocks.initAccount req.account, callback
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
delete: (account, callback) ->
|
||||
queryIptablesInfo (iptables_info) ->
|
||||
port = account.attribute.plugin.shadowsocks.port
|
||||
|
||||
billing_traffic = iptables_info[port].bytes - account.attribute.plugin.shadowsocks.last_traffic_value
|
||||
billing_traffic = iptables_info[port].bytes if billing_traffic < 0
|
||||
billing_traffic += account.attribute.plugin.shadowsocks.pending_traffic
|
||||
|
||||
amount = billing_traffic / BILLING_BUCKET * config.plugins.shadowsocks.price_bucket
|
||||
|
||||
mAccount.update {_id: account._id},
|
||||
$unset:
|
||||
'attribute.plugin.shadowsocks': true
|
||||
$inc:
|
||||
'attribute.balance': -amount
|
||||
, ->
|
||||
async.parallel [
|
||||
(callback) ->
|
||||
child_process.exec "sudo iptables -D OUTPUT #{iptables_info[port].num}", callback
|
||||
|
||||
(callback) ->
|
||||
child_process.exec 'sudo iptables-save | sudo tee /etc/iptables.rules', callback
|
||||
|
||||
(callback) ->
|
||||
child_process.exec "sudo rm /etc/supervisor/conf.d/#{account.username}.conf", callback
|
||||
|
||||
(callback) ->
|
||||
child_process.exec 'sudo supervisorctl update', callback
|
||||
], ->
|
||||
if amount > 0
|
||||
mBalance.create account, 'service_billing', -amount,
|
||||
service: 'shadowsocks'
|
||||
traffic_mb: billing_traffic / BILLING_BUCKET * 100
|
||||
is_force: true
|
||||
, ->
|
||||
callback()
|
||||
else
|
||||
callback()
|
||||
|
||||
restart: (account, callback) ->
|
||||
config_content = _.template (fs.readFileSync path.join(__dirname, 'template/config.conf')).toString(), account.attribute.plugin.shadowsocks
|
||||
|
||||
pluggable.writeConfig "/etc/shadowsocks/#{account.username}.json", config_content, ->
|
||||
child_process.exec "sudo chmod +r /etc/shadowsocks/#{account.username}.json", ->
|
||||
config_content = _.template (fs.readFileSync path.join(__dirname, 'template/supervisor.conf')).toString(),
|
||||
account: account
|
||||
|
||||
pluggable.writeConfig "/etc/supervisor/conf.d/#{account.username}.conf", config_content, ->
|
||||
child_process.exec 'sudo supervisorctl update', ->
|
||||
callback()
|
||||
|
||||
restartAccount: (account, callback) ->
|
||||
child_process.exec "sudo supervisorctl restart shadowsocks-#{account.username}", ->
|
||||
callback()
|
||||
|
||||
monitoring: ->
|
||||
queryIptablesInfo (iptables_info) ->
|
||||
async.map _.values(iptables_info), (item, callback) ->
|
||||
{port, bytes} = item
|
||||
|
||||
mAccount.findOne
|
||||
'attribute.plugin.shadowsocks.port': port
|
||||
, (err, account) ->
|
||||
unless account
|
||||
return callback()
|
||||
|
||||
{pending_traffic, last_traffic_value} = account.attribute.plugin.shadowsocks
|
||||
|
||||
new_traffic = bytes - last_traffic_value
|
||||
|
||||
if new_traffic < 0
|
||||
new_traffic = bytes
|
||||
|
||||
new_pending_traffic = pending_traffic + new_traffic
|
||||
|
||||
billing_bucket = Math.floor pending_traffic / BILLING_BUCKET
|
||||
|
||||
new_pending_traffic -= billing_bucket * BILLING_BUCKET
|
||||
|
||||
if billing_bucket > 0
|
||||
amount = billing_bucket * config.plugins.shadowsocks.price_bucket
|
||||
|
||||
mAccount.update {_id: account._id},
|
||||
$set:
|
||||
'attribute.plugin.shadowsocks.pending_traffic': new_pending_traffic
|
||||
'attribute.plugin.shadowsocks.last_traffic_value': bytes
|
||||
$inc:
|
||||
'attribute.balance': -amount
|
||||
, (err) ->
|
||||
mBalance.create account, 'service_billing', -amount,
|
||||
service: 'shadowsocks'
|
||||
traffic_mb: billing_bucket * 100
|
||||
is_force: false
|
||||
, ->
|
||||
callback()
|
||||
else if pending_traffic != new_pending_traffic or last_traffic_value != bytes
|
||||
mAccount.update {_id: account._id},
|
||||
$set:
|
||||
'attribute.plugin.shadowsocks.pending_traffic': new_pending_traffic
|
||||
'attribute.plugin.shadowsocks.last_traffic_value': bytes
|
||||
, (err) ->
|
||||
callback()
|
||||
else
|
||||
callback()
|
||||
|
||||
, ->
|
||||
@@ -1,10 +1,37 @@
|
||||
{_, child_process} = app.libs
|
||||
{_, child_process, async, fs} = app.libs
|
||||
{logger, utils} = app
|
||||
{Account} = app.models
|
||||
{Account, Financials} = app.models
|
||||
|
||||
supervisor = require '../supervisor/supervisor'
|
||||
|
||||
BILLING_BUCKET = config.plugins.shadowsocks.billing_bucket
|
||||
|
||||
exports.generateConfigure = (users, options) ->
|
||||
exports.initSupervisor = (callback) ->
|
||||
supervisor.programsStatus (program_status) ->
|
||||
async.each config.plugins.shadowsocks.available_ciphers, (method, callback) ->
|
||||
program_name = "shadowsocks-#{method}"
|
||||
|
||||
if program_name in _.keys program_status
|
||||
return callback()
|
||||
|
||||
shadowsocks_config_file = "/etc/shadowsocks/#{method}.json"
|
||||
configure = exports.generateConfigure [],
|
||||
method: method
|
||||
|
||||
fs.writeFile shadowsocks_config_file, configure, ->
|
||||
supervisor.writeConfig {username: 'nobody'},
|
||||
command: "ssserver -c #{shadowsocks_config_file}"
|
||||
name: program_name
|
||||
autostart: true
|
||||
autorestart: true
|
||||
, ->
|
||||
supervisor.updateProgram {}, {program_name: program_name}, ->
|
||||
callback()
|
||||
|
||||
, ->
|
||||
callback()
|
||||
|
||||
exports.generateConfigure = (users, options = {}) ->
|
||||
configure =
|
||||
server: '0.0.0.0'
|
||||
local_port: 1080
|
||||
@@ -82,7 +109,181 @@ exports.initAccount = (account, callback) ->
|
||||
callback()
|
||||
|
||||
exports.deleteAccount = (account, callback) ->
|
||||
queryIptablesInfo (iptables_info) ->
|
||||
{port, method} = 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
|
||||
billing_traffic += account.pluggable.shadowsocks.pending_traffic
|
||||
|
||||
amount = billing_traffic / BILLING_BUCKET * config.plugins.shadowsocks.price_bucket
|
||||
|
||||
account.update
|
||||
$unset:
|
||||
'pluggable.shadowsocks': true
|
||||
$inc:
|
||||
'billing.balance': -amount
|
||||
, ->
|
||||
async.series [
|
||||
(callback) ->
|
||||
child_process.exec "sudo iptables -D OUTPUT #{iptables_info[port].num}", callback
|
||||
|
||||
(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
|
||||
|
||||
], ->
|
||||
if amount > 0
|
||||
Financials.create
|
||||
account_id: account._id
|
||||
type: 'usage_billing'
|
||||
amount: -amount
|
||||
payload:
|
||||
service: 'shadowsocks'
|
||||
traffic_mb: billing_traffic / (1000 * 1000)
|
||||
, ->
|
||||
callback()
|
||||
else
|
||||
callback()
|
||||
|
||||
exports.accountUsage = (account, callback) ->
|
||||
Financials.find
|
||||
account_id: account._id
|
||||
type: 'usage_billing'
|
||||
'payload.service': 'shadowsocks'
|
||||
, (err, financials) ->
|
||||
time_range =
|
||||
traffic_24hours: 24 * 3600 * 1000
|
||||
traffic_7days: 7 * 24 * 3600 * 1000
|
||||
traffic_30days: 30 * 24 * 3600 * 1000
|
||||
|
||||
result = {}
|
||||
|
||||
for name, range of time_range
|
||||
logs = _.filter financials, (i) ->
|
||||
return i.created_at.getTime() > Date.now() - range
|
||||
|
||||
result[name] = _.reduce logs, (memo, i) ->
|
||||
return memo + i.payload.traffic_mb
|
||||
, 0
|
||||
|
||||
callback result
|
||||
|
||||
exports.updateConfigure = (account, callback) ->
|
||||
{port, method, password} = account.pluggable.shadowsocks
|
||||
original_method = null
|
||||
|
||||
async.eachSeries config.plugins.shadowsocks.available_ciphers, (method, callback) ->
|
||||
shadowsocks_config_file = "/etc/shadowsocks/#{method}.json"
|
||||
|
||||
fs.readFile shadowsocks_config_file, (err, content) ->
|
||||
if port.toString() in _.keys JSON.parse(content).port_password
|
||||
original_method = method
|
||||
callback true
|
||||
else
|
||||
callback()
|
||||
|
||||
, ->
|
||||
async.series [
|
||||
(callback) ->
|
||||
shadowsocks_config_file = "/etc/shadowsocks/#{method}.json"
|
||||
|
||||
fs.readFile shadowsocks_config_file, (err, content) ->
|
||||
config = JSON.parse content
|
||||
config.port_password[port] = password
|
||||
|
||||
fs.writeFile shadowsocks_config_file, JSON.stringify(config), ->
|
||||
callback()
|
||||
|
||||
(callback) ->
|
||||
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) ->
|
||||
config = JSON.parse content
|
||||
delete config.port_password[port]
|
||||
|
||||
fs.writeFile shadowsocks_config_file, JSON.stringify(config), ->
|
||||
callback()
|
||||
|
||||
exports.monitoring = ->
|
||||
queryIptablesInfo (iptables_info) ->
|
||||
async.each _.values(iptables_info), (item, callback) ->
|
||||
{port, bytes} = item
|
||||
|
||||
Account.findOne
|
||||
'pluggable.shadowsocks.port': port
|
||||
, (err, account) ->
|
||||
unless account
|
||||
return callback()
|
||||
|
||||
{pending_traffic, last_traffic_value} = account.pluggable.shadowsocks
|
||||
|
||||
new_traffic = bytes - last_traffic_value
|
||||
|
||||
if new_traffic < 0
|
||||
new_traffic = bytes
|
||||
|
||||
new_pending_traffic = pending_traffic + new_traffic
|
||||
|
||||
billing_bucket = Math.floor pending_traffic / BILLING_BUCKET
|
||||
|
||||
new_pending_traffic -= billing_bucket * BILLING_BUCKET
|
||||
|
||||
if billing_bucket > 0
|
||||
amount = billing_bucket * config.plugins.shadowsocks.price_bucket
|
||||
|
||||
account.update
|
||||
$set:
|
||||
'pluggable.shadowsocks.pending_traffic': new_pending_traffic
|
||||
'pluggable.shadowsocks.last_traffic_value': bytes
|
||||
$inc:
|
||||
'billing.balance': -amount
|
||||
, (err) ->
|
||||
logger.error err if err
|
||||
|
||||
Financials.create
|
||||
account_id: account._id
|
||||
type: 'usage_billing'
|
||||
amount: -amount
|
||||
payload:
|
||||
service: 'shadowsocks'
|
||||
traffic_mb: (billing_bucket * BILLING_BUCKET) / 1000 * 1000
|
||||
, ->
|
||||
callback()
|
||||
|
||||
else if pending_traffic != new_pending_traffic or last_traffic_value != bytes
|
||||
account.update
|
||||
$set:
|
||||
'pluggable.shadowsocks.pending_traffic': new_pending_traffic
|
||||
'pluggable.shadowsocks.last_traffic_value': bytes
|
||||
, (err) ->
|
||||
callback()
|
||||
|
||||
else
|
||||
callback()
|
||||
|
||||
, ->
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"server": "0.0.0.0",
|
||||
"local_port": 1080,
|
||||
"port_password": {
|
||||
<% users.forEach(function(user) { %>
|
||||
"<%= user.port %>": "<%= %>"
|
||||
<% }); %>
|
||||
"8381": "foobar1",
|
||||
"8382": "foobar2",
|
||||
"8383": "foobar3",
|
||||
"8384": "foobar4"
|
||||
},
|
||||
"timeout": 60,
|
||||
"method": "aes-256-cfb",
|
||||
"workers": 2
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
[program:shadowsocks-<%= account.username %>]
|
||||
command=ssserver -c /etc/shadowsocks/<%= account.username %>.json
|
||||
autorestart=true
|
||||
user=nobody
|
||||
@@ -32,7 +32,6 @@ exports.updateProgram = (account, program, callback) ->
|
||||
|
||||
exports.writeConfig = (account, program, callback) ->
|
||||
SupervisorPlugin.renderTemplate 'program.conf',
|
||||
name_prefix: '@'
|
||||
account: account
|
||||
program: program
|
||||
, (configure) ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[program:<%= name_prefix %><%= account.username %>-<%= program.name %>]
|
||||
[program:<%= program.program_name %>]
|
||||
command = <%= program.command %>
|
||||
process_name = <%= program.name %>
|
||||
autostart = <%= program.autostart.toString() %>
|
||||
Reference in New Issue
Block a user