mirror of
https://github.com/HackPlan/RootPanel.git
synced 2026-03-25 15:14:04 +08:00
refactor pluggable
This commit is contained in:
10
app.coffee
10
app.coffee
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 '_'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,8 +15,7 @@ module.exports =
|
||||
default_timezone: 'Asia/Shanghai'
|
||||
|
||||
plugin:
|
||||
available_extensions: []
|
||||
available_services: []
|
||||
available_plugins: []
|
||||
|
||||
billing:
|
||||
currency: 'CNY'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user