refactor pluggable

This commit is contained in:
jysperm
2014-11-29 01:04:50 +08:00
parent f83e6c9158
commit f0b985424b
11 changed files with 240 additions and 257 deletions

View File

@@ -89,6 +89,7 @@ app.i18n = require './core/i18n'
app.pluggable = require './core/pluggable'
app.models = {}
app.classes = {}
require './core/model/Account'
require './core/model/Financials'
@@ -117,8 +118,6 @@ app.express.use app.middleware.accountHelpers
app.express.set 'views', path.join(__dirname, 'core/view')
app.express.set 'view engine', 'jade'
app.express.get '/locale/:language?', app.i18n.downloadLocales
app.express.use '/account', require './core/router/account'
app.express.use '/billing', require './core/router/billing'
app.express.use '/ticket', require './core/router/ticket'
@@ -126,9 +125,10 @@ app.express.use '/coupon', require './core/router/coupon'
app.express.use '/admin', require './core/router/admin'
app.express.use '/panel', require './core/router/panel'
app.billing.initializePlans()
app.clusters.initializeNodes()
app.pluggable.initializePlugins()
app.billing.initPlans()
app.clusters.initNodes()
app.i18n.init()
app.pluggable.initPlugins()
app.express.get '/', (req, res) ->
unless res.headerSent

View File

@@ -186,10 +186,14 @@ exports.calcResourcesLimit = (plans) ->
return limit
exports.initializePlans = ->
exports.initPlans = ->
for name, info in config.plans
plan = new Plan info
exports.plans[name] = plan
exports.Plan = Plan = class Plan
info: null
name: null
constructor: (@info) ->
@name = @info.name

View File

@@ -3,8 +3,30 @@
exports.nodes = {}
exports.Node = Node = class Node
constructor: (@info) ->
info: null
name: null
exports.initializeNodes = ->
constructor: (@info) ->
@name = @info.name
writeConfigFile: (filename, content, options, callback) ->
unless callback
[options, callback] = [{}, options]
tmp.file
mode: options.mode ? 0o750
, (err, filepath, fd) ->
logger.error err if err
fs.writeSync fd, content, 0, 'utf8'
fs.closeSync fd
child_process.exec "sudo cp #{filepath} #{filename}", (err) ->
logger.error err if err
fs.unlink filepath, ->
callback()
exports.initNodes = ->
for name, info of config.nodes
exports[name] = new Node info

View File

@@ -5,33 +5,23 @@ i18n = exports
i18n.i18n_data = i18n_data = {}
i18n.init = (callback) ->
fs.readdir path.join(__dirname, 'locale'), (err, files) ->
logger.error err if err
i18n.init = ->
for filename in fs.readdirSync path.join(__dirname, 'locale')
language = path.basename filename, '.json'
i18n_data[language] = require path.join(__dirname, 'locale', filename)
config.i18n.available_language = _.union config.i18n.available_language, [language]
for filename in files
language = path.basename filename, '.json'
i18n_data[language] = require path.join(__dirname, 'locale', filename)
config.i18n.available_language = _.union config.i18n.available_language, [language]
i18n.initPlugin = (plugin) ->
for filename in fs.readdirSync path.join(__dirname, '../plugin', plugin.name, 'locale')
language = path.basename filename, '.json'
file_path = path.join __dirname, '../plugin', plugin.name, 'locale', filename
callback()
i18n.initPlugins = (plugin, callback) ->
fs.readdir path.join(__dirname, '../plugin', plugin.name, 'locale'), (err, files) ->
logger.error err if err
for filename in files
language = path.basename filename, '.json'
file_path = path.join __dirname, '../plugin', plugin.name, 'locale', filename
i18n_data[language] ?= {}
i18n_data[language]['plugins'] ?= {}
i18n_data[language]['plugins'][plugin.name] = require file_path
i18n_data[language] ?= {}
i18n_data[language]['plugins'] ?= {}
i18n_data[language]['plugins'][plugin.name] = require file_path
config.i18n.available_language = _.union config.i18n.available_language, [language]
config.i18n.available_language = _.union config.i18n.available_language, [language]
callback()
i18n.parseLanguageCode = parseLanguageCode = (language) ->
[lang, country] = language.replace('-', '_').split '_'

View File

@@ -1,34 +1,31 @@
{async, path, harp, jade, tmp, fs, _, child_process} = app.libs
{i18n, config, logger} = app
exports.plugins = {}
pluggable = exports
exports.hooks =
pluggable.plugins = {}
pluggable.components = {}
pluggable.hooks =
app:
# action: function
started: _.extend [],
global_event: true
started: []
# path: string
ignore_csrf: _.extend [],
global_event: true
ignore_csrf: []
model:
# model: string, field: string, type: string
type_enum: _.extend [],
global_event: true
type_enum: []
# model: string, action(schema, callback)
middleware: _.extend [],
global_event: true
middleware: []
account:
# filter: function(username, callback(is_allow))
username_filter: _.extend [],
global_event: true
username_filter: []
# filter: function(account, callback)
before_register: _.extend [],
global_event: true
before_register: []
# filter: function(account, callback)
resources_limit_changed: []
@@ -47,8 +44,7 @@ exports.hooks =
admin:
# generator: function(req, callback)
sidebars: _.extend [],
global_event: true
sidebars: []
panel:
# path
@@ -62,151 +58,120 @@ exports.hooks =
# type, filter: function(req, deposit_log, callback(l_details))
display_payment_details: []
service:
'service_name':
# filter: function(account, callback)
enable: []
# filter: function(account, callback)
disable: []
Plugin = class Plugin
info: null
name: null
config: null
plugin:
wiki:
# t_category, t_title, language, content_markdown
pages: []
exports.createHookPoint = (hook_name) ->
keys = hook_name.split '.'
pointer = exports.hooks
for item in keys
if pointer[item] == undefined
pointer[item] = {}
pointer = pointer[item]
exports.registerHook = (hook_name, plugin, payload) ->
keys = hook_name.split '.'
last_key = keys.pop()
pointer = exports.hooks
for item in keys
if pointer[item] == undefined
pointer[item] = {}
pointer = pointer[item]
else
pointer = pointer[item]
pointer[last_key] ?= []
pointer[last_key].push _.extend payload,
plugin_info: plugin
exports.selectHook = (account, hook_name) ->
keys = hook_name.split '.'
pointer = exports.hooks
for item in keys
if pointer[item] == undefined
pointer[item] = {}
pointer = pointer[item]
else
pointer = pointer[item]
return _.filter pointer, (hook) ->
if hook.plugin_info.type == 'extension'
return true
else if pointer.global_event or hook.always_notice
return true
else if !account
return false
else if hook.plugin_info.NAME in account.billing.services
return true
else
return false
exports.initializePlugin = (name) ->
plugin_path = "#{__dirname}/../plugin/#{name}"
plugin = require plugin_path
if fs.existsSync path.join(plugin_path, 'locale')
i18n.loadForPlugin plugin
if fs.existsSync path.join(plugin_path, 'static')
app.express.use harp.mount("/plugin/#{name}", path.join(plugin_path, 'static'))
exports.initializePlugins = ->
plugins = _.union config.plugin.available_extensions, config.plugin.available_services
for name in plugins
plugin = require "#{__dirname}/../plugin/#{name}"
if plugin.dependencies
for dependence in plugin.dependencies
unless dependence in plugins
throw new Error "#{name} is Dependent on #{dependence} but not load"
exports.plugins[name] = plugin
for name, plugin of exports.plugins
exports.initializePlugin name
exports.ComponentMeta = class ComponentMeta
constructor: (@info) ->
@name = @info.name
@config = config.plugins[@name] ? {}
exports.Plugin = class Plugin
@registerHook: (hook_name, payload) ->
return exports.registerHook hook_name, @, payload
registerComponent: (info) ->
component_meta = new ComponentMeta _.extend info,
plugin: @
@registerServiceHook: (hook_name, payload) ->
return @registerHook "service.#{@NAME}.#{hook_name}", payload
pluggable.components[info.name] = component_meta
@t: (req) ->
registerHook: (name, payload) ->
words = name.split '.'
last_word = words.pop()
hook_path = pluggable.selectHookPath words.join('.')
hook_path[last_word] ?= []
hook_path[last_word].push _.extend payload,
plugin: @
getTranslator: (languages) ->
return (name) =>
full_name = "plugins.#{@NAME}.#{name}"
if _.isArray languages
t = i18n.getTranslator languages
else
t = i18n.getTranslatorByReq languages
args = _.toArray arguments
args[0] = full_name
args[0] = "plugins.#{@name}.#{name}"
full_result = req.res.locals.t.apply @, args
full_result = t.apply @, args
unless full_result == full_name
return full_result
return req.res.locals.t.apply @, _.toArray(arguments)
return t.apply @, _.toArray(arguments)
@render: (template_name, req, view_data, callback) ->
template_path = "#{__dirname}/../plugin/#{@NAME}/view/#{template_name}.jade"
render: (name, req, view_data, callback) ->
template_path = path.join __dirname, '../plugin', @name, 'view', "#{name}.jade"
locals = _.extend _.clone(req.res.locals), view_data,
account: req.account
t: @t req
t: @getTranslator req
jade.renderFile template_path, locals, (err, html) ->
logger.error err if err
callback html
@renderTemplate: (name, view_data, callback) ->
template_path = "#{__dirname}/../plugin/#{@NAME}/template/#{name}"
renderTemplate: (name, view_data, callback) ->
template_path = path.join __dirname, '../plugin', @name, 'view', name
fs.readFile template_path, (err, template_file) ->
callback _.template(template_file.toString()) view_data
@writeConfigFile: (filename, content, options, callback) ->
unless callback
[options, callback] = [{}, options]
ComponentMeta = class ComponentMeta
info: null
name: null
tmp.file
mode: options.mode ? 0o750
, (err, filepath, fd) ->
logger.error err if err
constructor: (@info) ->
@name = @info.name
fs.writeSync fd, content, 0, 'utf8'
fs.closeSync fd
pluggable.selectHookPath = (name) ->
words = name.split '.'
child_process.exec "sudo cp #{filepath} #{filename}", (err) ->
logger.error err if err
hook_path = pluggable.hooks
fs.unlink filepath, ->
callback()
for word in words
hook_path[word] ?= {}
hook_path = hook_path[word]
return path
pluggable.selectHook = (name) ->
return pluggable.selectHookPath name
pluggable.initPlugin = (name) ->
plugin_path = path.join __dirname, '../plugin', name
plugin = require plugin_path
for path, payload in plugin.register_hooks ? {}
if payload.test and payload.test.apply @
plugin.registerHook path, payload
plugin.initialize()
if fs.existsSync path.join(plugin_path, 'locale')
i18n.initPlugin plugin, callback
if fs.existsSync path.join(plugin_path, 'static')
app.express.use harp.mount "/plugin/#{name}", path.join(plugin_path, 'static')
pluggable.initPlugins = ->
plugins_name = config.plugin.available_plugins
for name in plugins_name
plugin = require path.join __dirname, '../plugin', name
if plugin.dependencies
for dependence in plugin.dependencies
unless dependence in plugins_name
err = new Error "#{name} is Dependent on #{dependence} but not load"
logger.fatal err
throw err
exports.plugins[name] = plugin
for name in plugins_name
pluggable.initPlugin name
_.extend app.classes,
Plugin: Plguin
ComponentMeta: ComponentMeta

View File

@@ -2,75 +2,68 @@
{pluggable, config} = app
{requireAuthenticate} = app.middleware
{wrapAsync} = app.utils
exports = module.exports = class LinuxPlugin extends pluggable.Plugin
@NAME: 'linux'
@type: 'service'
{Plugin} = app.classes
linux = require './linux'
monitor = require './monitor'
exports.registerHook 'view.layout.menu_bar',
href: '/public/monitor/'
t_body: 'plugins.linux.server_monitor'
linuxPlugin = module.exports = new Plugin
name: 'linux'
exports.registerHook 'account.username_filter',
filter: (username, callback) ->
linux.getPasswdMap (passwd_map) ->
linux.getGroup (group_map) ->
if username in _.values passwd_map
callback false
else if username in _.values group_map
callback false
else
callback true
register_hooks:
'view.layout.menu_bar':
href: '/public/monitor/'
t_body: 'server_monitor'
exports.registerHook 'view.panel.styles',
path: '/plugin/linux/style/panel.css'
'account.username_filter':
filter: linux.isUsernameAvailable
exports.registerHook 'view.panel.widgets',
generator: (req, callback) ->
linux.getResourceUsageByAccount req.account, (resources_usage) ->
resources_usage ?=
username: req.account.username
cpu: 0
memory: 0
storage: 0
process: 0
'app.started':
test: -> @config.monitor_cycle
action: monitor.run
exports.render 'widget', req,
usage: resources_usage
, callback
initialize: ->
app.express.get '/public/monitor', requireAuthenticate, (req, res) ->
async.parallel
resources_usage: (callback) ->
linux.getResourceUsageByAccounts (result) ->
callback null, result
system: wrapAsync linux.getSystemInfo
storage: wrapAsync linux.getStorageInfo
process_list: wrapAsync linux.getProcessList
memory: wrapAsync linux.getMemoryInfo
exports.registerHook 'account.resources_limit_changed',
always_notice: true
filter: (account, callback) ->
linux.setResourceLimit account, callback
, (err, result) ->
logger.error err if err
exports.render 'monitor', req, result, (html) ->
res.send html
exports.registerServiceHook 'enable',
filter: (account, callback) ->
linux.createUser account, callback
linuxPlugin.registerComponent
name: 'linux'
exports.registerServiceHook 'disable',
filter: (account, callback) ->
linux.deleteUser account, callback
initialize: linux.createUser
destroy: linux.deleteUser
if config.plugins.linux.monitor_cycle
exports.registerHook 'app.started',
action: ->
monitor.run()
package: ->
unpacking: ->
app.express.get '/public/monitor', requireAuthenticate, (req, res) ->
async.parallel
resources_usage: (callback) ->
linux.getResourceUsageByAccounts (result) ->
callback null, result
system: wrapAsync linux.getSystemInfo
storage: wrapAsync linux.getStorageInfo
process_list: wrapAsync linux.getProcessList
memory: wrapAsync linux.getMemoryInfo
register_hooks:
'account.resources_limit_changed':
filter: linux.setResourceLimit
, (err, result) ->
logger.error err if err
exports.render 'monitor', req, result, (html) ->
res.send html
'view.panel.styles':
path: '/plugin/linux/style/panel.css'
'view.panel.widgets':
generator: (req, callback) ->
linux.getResourceUsageByAccount req.account, (resources_usage) ->
resources_usage ?=
username: req.account.username
cpu: 0
memory: 0
storage: 0
process: 0
exports.render 'widget', req,
usage: resources_usage
, callback

View File

@@ -73,6 +73,19 @@ exports.getGroup = (callback) ->
SETEX result, 120
, callback
# callback(is_available)
exports.isUsernameAvailable = (username, callback) ->
async.parallel
passwd: wrapAsync exports.getPasswdMap
group: wrapAsync exports.getGroup
, (err, result) ->
if username in _.values result.passwd
callback false
else if username in _.values result.group
callback false
else
callback true
exports.getMemoryInfo = (callback) ->
cache.try 'linux.getMemoryInfo', (SETEX) ->
fs.readFile '/proc/meminfo', (err, content) ->

View File

@@ -1,36 +1,31 @@
{jade, path, fs} = app.libs
{pluggable, config} = app
{Plugin} = app.classes
exports = module.exports = class RPVhostPlugin extends pluggable.Plugin
@NAME: 'rpvhost'
@type: 'extension'
rpvhostPlugin = module.exports = new Plugin
name: 'rpvhost'
exports.registerHook 'plugin.wiki.pages',
t_category: 'plugins.rpvhost.'
t_title: 'Terms.md'
language: 'zh_CN'
content_markdown: fs.readFileSync("#{__dirname}/wiki/Terms.md").toString()
register_hooks:
'view.layout.menu_bar':
href: 'http://blog.rpvhost.net'
target: '_blank'
t_body: 'official_blog'
exports.registerHook 'view.layout.menu_bar',
href: '//blog.rpvhost.net'
target: '_blank'
t_body: 'plugins.rpvhost.official_blog'
'billing.payment_methods':
widget_generator: (req, callback) ->
exports.render 'payment_method', req, {}, callback
exports.registerHook 'billing.payment_methods',
widget_generator: (req, callback) ->
exports.render 'payment_method', req, {}, callback
'view.pay.display_payment_details.taobao':
filter: (req, deposit_log, callback) ->
callback rpvhostPlugin.t(req) 'view.payment_details',
order_id: deposit_log.payload.order_id
exports.registerHook 'view.pay.display_payment_details',
type: 'taobao'
filter: (req, deposit_log, callback) ->
callback exports.t(req) 'view.payment_details',
order_id: deposit_log.payload.order_id
'view.layout.styles':
test: -> @config.green_style
path: '/plugin/rpvhost/style/green.css'
if config.plugins.rpvhost.green_style
exports.registerHook 'view.layout.styles',
path: '/plugin/rpvhost/style/green.css'
unless config.plugins.rpvhost.index_page == false
app.express.get '/', (req, res) ->
exports.render 'index', req, {}, (html) ->
res.send html
initialize: ->
unless @config.index_page == false
app.express.get '/', (req, res) ->
rpvhostPlugin.render 'index', req, {}, (html) ->
res.send html

View File

@@ -15,8 +15,7 @@ module.exports =
default_timezone: 'Asia/Shanghai'
plugin:
available_extensions: []
available_services: []
available_plugins: []
billing:
currency: 'CNY'

View File

@@ -15,8 +15,9 @@ module.exports =
default_timezone: 'Asia/Shanghai'
plugin:
available_extensions: ['bitcoin', 'wiki', 'rpvhost']
available_services: ['linux', 'supervisor', 'ssh']
available_plugins: [
'bitcoin', 'wiki', 'rpvhost','linux', 'supervisor', 'ssh'
]
billing:
currency: 'CNY'

View File

@@ -15,8 +15,9 @@ module.exports =
default_timezone: 'Asia/Shanghai'
plugin:
available_extensions: ['bitcoin', 'wiki', 'rpvhost']
available_services: ['linux', 'supervisor', 'shadowsocks']
available_plugins: [
'bitcoin', 'wiki', 'rpvhost', 'linux', 'supervisor', 'shadowsocks'
]
billing:
currency: 'CNY'