diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 2e4d283..0000000 --- a/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.idea -test.js - -node_modules -npm-debug.log diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..793a996 --- /dev/null +++ b/Gemfile @@ -0,0 +1,14 @@ +source "https://rubygems.org" + +gem "sinatra" +gem "sinatra-contrib" +gem "puma" +gem "jwt" +gem "dotenv" +gem "rake", "~> 12.0" +gem "oj", "~> 3.11.3", platform: :ruby +gem "redis" +gem "bcrypt" +# gem "mongo" +# gem "bson" +gem "mongoid" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..41857fd --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,72 @@ +GEM + remote: https://rubygems.org/ + specs: + activemodel (6.1.4.1) + activesupport (= 6.1.4.1) + activesupport (6.1.4.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + bcrypt (3.1.16) + bson (4.12.1) + concurrent-ruby (1.1.9) + dotenv (2.7.6) + i18n (1.8.10) + concurrent-ruby (~> 1.0) + jwt (2.2.3) + minitest (5.14.4) + mongo (2.15.1) + bson (>= 4.8.2, < 5.0.0) + mongoid (7.3.3) + activemodel (>= 5.1, < 6.2) + mongo (>= 2.10.5, < 3.0.0) + ruby2_keywords (~> 0.0.5) + multi_json (1.15.0) + mustermann (1.1.1) + ruby2_keywords (~> 0.0.1) + nio4r (2.5.8) + oj (3.11.8) + puma (5.4.0) + nio4r (~> 2.0) + rack (2.2.3) + rack-protection (2.1.0) + rack + rake (12.3.3) + redis (4.4.0) + ruby2_keywords (0.0.5) + sinatra (2.1.0) + mustermann (~> 1.0) + rack (~> 2.2) + rack-protection (= 2.1.0) + tilt (~> 2.0) + sinatra-contrib (2.1.0) + multi_json + mustermann (~> 1.0) + rack-protection (= 2.1.0) + sinatra (= 2.1.0) + tilt (~> 2.0) + tilt (2.0.10) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) + zeitwerk (2.4.2) + +PLATFORMS + x86_64-darwin-20 + +DEPENDENCIES + bcrypt + dotenv + jwt + mongo + mongoid + oj (~> 3.11.3) + puma + rake (~> 12.0) + redis + sinatra + sinatra-contrib + +BUNDLED WITH + 2.2.27 diff --git a/Makefile b/Makefile deleted file mode 100644 index 2ebf086..0000000 --- a/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -test: - @./node_modules/.bin/mocha \ - --reporter list - -.PHONY: test \ No newline at end of file diff --git a/README.md b/README.md index e305169..92ea0c0 100644 --- a/README.md +++ b/README.md @@ -1,367 +1,45 @@ -WHMCS Node Module -========= +APP >> Redis +{user/pass, backend: [{endpoint, api_key, api_secret},{endpoint, api_key, api_secret}]} -WHMCS' API Node client. +### TODO -## Pre-requisites +# registration >> portal >> enter endpoint/api credentials for whmcs/ubersmith -Install [Node.js](https://nodejs.org/en/) version 12.0.0 or higher. +get '/' do +if login do +erb: portal page +else +erb: login page +end -## Installation +get '/register' do +If email = registered do +redirect '/login' +else +erb: registration +end +end -``` -npm install whmcs -``` - -## Usage - -First you need to instantiate it. - -You can instantiate with api credentials: - -```javascript -const config = { - apiIdentifier: '', - apiSecret: '', - accesskey: '', //optional. use it to bypass IP restrictions - serverUrl: 'http://127.0.0.1', - userAgent: '' -}; - -const wclient = new WHMCS(config); -``` +#### redis -Or you can instantiate with administrator username and MD5 hashed password: +`docker run -d -v /myredis/conf:/usr/local/etc/redis --name redis redis redis-server /usr/local/etc/redis/redis.conf` -```javascript -const config = { - username: process.env.WHMCS_USER || '', - password: process.env.WHMCS_PASSWORD || '', - accesskey: '', //optional. use it to bypass IP restrictions - serverUrl: 'http://127.0.0.1', - userAgent: '' -}; - -const whmcs = new WHMCS(config); ``` - -With the created WHMCS client, you should get the category/module you want (see [available modules](#Implemented-functions)) and call the method you need. - -For example, you can fetch 10 users like this: - -```javascript -const parameters = { - limitstart: 0, - limitnum: 10 -}; - -whmcs.users.getUsers(parameters) - .then(details => { - ... - }) - .catch(err => { - ... - }); +docker run \ +-p 6379:6379 \ +-v /home/db/redis/data:/data:rw \ +-v $PWD/conf/redis.conf:/etc/redis/redis.conf:ro \ +--privileged=true \ +--name redis \ +-d redis redis-server /etc/redis/redis.conf ``` -If you want/need to, you can use callbacks instead: - -```javascript -const parameters = { - limitstart: 0, - limitnum: 10 -}; - -whmcs.users.getUsers(parameters, function (err, details) { - ... -}); ``` - -## Module architecture - -This node-js module follows [WHMCS' API Index](https://developers.whmcs.com/api/api-index/) structure, which have functions organized by categories. Every category is represented here by a module Object, and each module has public methods representing WHMCS' functions. -
-When WHMCS' function requires parameters, they must be grouped in a JSON Object and then can be set as the first argument of the method. If no parameters are required, this argument must not be set. The "callback" argument is optional, and when not provided the method returns a Promise. This pattern is shared all across the modules public methods. -
- -## Implemented functions - -The [WHMCS' API Index](https://developers.whmcs.com/api/api-index/) can be updated anytime, so you must check below which functions are implemented (expand to show the function list). - -
- Addons - - - UpdateClientAddon: updateClientAddon(parameters, [callback]) -
- -
- Affiliates - - - AffiliateActivate: affiliateActivate(parameters, [callback]) - - GetAffiliates: getAffiliates(parameters, [callback]) -
- -
- Authentication - - - CreateOAuthCredential: createOAuthCredential(parameters, [callback]) - - CreateSsoToken: createSsoToken(parameters, [callback]) - - DeleteOAuthCredential: deleteOAuthCredential(parameters, [callback]) - - ListOAuthCredentials: listOAuthCredentials(parameters, [callback]) - - UpdateOAuthCredential: updateOAuthCredential(parameters, [callback]) - - ValidateLogin: validateLogin(parameters, [callback]) -
- -
- Billing - - - AcceptQuote: acceptQuote(parameters, [callback]) - - AddBillableItem: addBillableItem(parameters, [callback]) - - AddCredit: addCredit(parameters, [callback]) - - AddInvoicePayment: addInvoicePayment(parameters, [callback]) - - AddPayMethod: addPayMethod(parameters, [callback]) - - AddTransaction: addTransaction(parameters, [callback]) - - ApplyCredit: applyCredit(parameters, [callback]) - - CapturePayment: capturePayment(parameters, [callback]) - - CreateInvoice: createInvoice(parameters, [callback]) - - CreateQuote: createQuote(parameters, [callback]) - - DeletePayMethod: deletePayMethod(parameters, [callback]) - - DeleteQuote: deleteQuote(parameters, [callback]) - - GenInvoices: genInvoices(parameters, [callback]) - - GetCredits: getCredits(parameters, [callback]) - - GetInvoice: getInvoice(parameters, [callback]) - - GetInvoices: getInvoices(parameters, [callback]) - - GetPayMethods: getPayMethods(parameters, [callback]) - - GetQuotes: getQuotes(parameters, [callback]) - - GetTransactions: getTransactions(parameters, [callback]) - - SendQuote: sendQuote(parameters, [callback]) - - UpdateInvoice: updateInvoice(parameters, [callback]) - - UpdatePayMethod: updatePayMethod(parameters, [callback]) - - UpdateQuote: updateQuote(parameters, [callback]) - - UpdateTransaction: updateTransaction(parameters, [callback]) -
- -
- Client - - - AddClient: addClient(parameters, [callback]) - - AddContact: addContact(parameters, [callback]) - - CloseClient: closeClient(parameters, [callback]) - - DeleteClient: deleteClient(parameters, [callback]) - - DeleteContact: deleteContact(parameters, [callback]) - - GetCancelledPackages: getCancelledPackages(parameters, [callback]) - - GetClientGroups: getClientGroups([callback]) - - GetClientPassword: getClientPassword(parameters, [callback]) - - GetClients: getClients(parameters, [callback]) - - GetClientsAddons: getClientsAddons(parameters, [callback]) - - GetClientsDetails: getClientsDetails(parameters, [callback]) - - GetClientsDomains: getClientsDomains(parameters, [callback]) - - GetClientsProducts: getClientsProducts(parameters, [callback]) - - GetContacts: getContacts(parameters, [callback]) - - GetEmails: getEmails(parameters, [callback]) - - UpdateClient: updateClient(parameters, [callback]) - - UpdateContact: updateContact(parameters, [callback]) -
- -
- Domains - - - CreateOrUpdateTLD: createOrUpdateTLD(parameters, [callback]) - - DomainGetLockingStatus: domainGetLockingStatus(parameters, [callback]) - - DomainGetNameservers: domainGetNameservers(parameters, [callback]) - - DomainGetWhoisInfo: domainGetWhoisInfo(parameters, [callback]) - - DomainRegister: domainRegister(parameters, [callback]) - - DomainRelease: domainRelease(parameters, [callback]) - - DomainRenew: domainRenew(parameters, [callback]) - - DomainRequestEPP: domainRequestEPP(parameters, [callback]) - - DomainToggleIdProtect: domainToggleIdProtect(parameters, [callback]) - - DomainTransfer: domainTransfer(parameters, [callback]) - - DomainUpdateLockingStatus: domainUpdateLockingStatus(parameters, [callback]) - - DomainUpdateNameservers: domainUpdateNameservers(parameters, [callback]) - - DomainUpdateWhoisInfo: domainUpdateWhoisInfo(parameters, [callback]) - - DomainWhois: domainWhois(parameters, [callback]) - - GetTLDPricing: getTLDPricing(parameters, [callback]) - - UpdateClientDomain: updateClientDomain(parameters, [callback]) -
- -
- Module - - - ActivateModule: activateModule(parameters, [callback]) - - DeactivateModule: deactivateModule(parameters, [callback]) - - GetModuleConfigurationParameters: getModuleConfigurationParameters(parameters, [callback]) - - GetModuleQueue: getModuleQueue(parameters, [callback]) - - UpdateModuleConfiguration: updateModuleConfiguration(parameters, [callback]) -
- -
- Module - - - AcceptOrder: acceptOrder(parameters, [callback]) - - AddOrder: addOrder(parameters, [callback]) - - CancelOrder: cancelOrder(parameters, [callback]) - - DeleteOrder: deleteOrder(parameters, [callback]) - - FraudOrder: fraudOrder(parameters, [callback]) - - GetOrders: getOrders(parameters, [callback]) - - GetOrderStatuses: getOrderStatuses([callback]) - - GetProducts: getProducts(parameters, [callback]) - - GetPromotions: getPromotions(parameters, [callback]) - - OrderFraudCheck: orderFraudCheck(parameters, [callback]) - - PendingOrder: pendingOrder(parameters, [callback]) -
- -
- Products - - - AddProduct: addProduct(parameters, [callback]) -
- -
- Project Management - - - AddProjectMessage: addProjectMessage(parameters, [callback]) - - AddProjectTask: addProjectTask(parameters, [callback]) - - CreateProject: createProject(parameters, [callback]) - - DeleteProjectTask: deleteProjectTask(parameters, [callback]) - - EndTaskTimer: endTaskTimer(parameters, [callback]) - - GetProject: getProject(parameters, [callback]) - - GetProjects: getProjects(parameters, [callback]) - - StartTaskTimer: startTaskTimer(parameters, [callback]) - - UpdateProject: updateProject(parameters, [callback]) - - UpdateProjectTask: updateProjectTask(parameters, [callback]) -
- -
- Servers - - - GetHealthStatus: getHealthStatus(parameters, [callback]) - - GetServers: getServers(parameters, [callback]) -
- - -
- Service - - - ModuleChangePackage: moduleChangePackage(parameters, [callback]) - - ModuleChangePw: moduleChangePw(parameters, [callback]) - - ModuleCreate: moduleCreate(parameters, [callback]) - - ModuleCustom: moduleCustom(parameters, [callback]) - - ModuleSuspend: moduleSuspend(parameters, [callback]) - - ModuleTerminate: moduleTerminate(parameters, [callback]) - - ModuleUnsuspend: moduleUnsuspend(parameters, [callback]) - - UpdateClientProduct: updateClientProduct(parameters, [callback]) - - UpgradeProduct: upgradeProduct(parameters, [callback]) -
- -
- Support - - - AddAnnouncement: addAnnouncement(parameters, [callback]) - - AddCancelRequest: addCancelRequest(parameters, [callback]) - - AddClientNote: addClientNote(parameters, [callback]) - - AddTicketNote: addTicketNote(parameters, [callback]) - - AddTicketReply: addTicketReply(parameters, [callback]) - - blockTicketSender: blockTicketSender(parameters, [callback]) - - DeleteAnnouncement: deleteAnnouncement(parameters, [callback]) - - DeleteTicket: deleteTicket(parameters, [callback]) - - DeleteTicketNote: deleteTicketNote(parameters, [callback]) - - DeleteTicketReply: deleteTicketReply(parameters, [callback]) - - GetAnnouncements: getAnnouncements(parameters, [callback]) - - MergeTicket: mergeTicket(parameters, [callback]) - - OpenTicket: openTicket(parameters, [callback]) - - UpdateTicket: updateTicket(parameters, [callback]) - - UpdateTicketReply: updateTicketReply(parameters, [callback]) -
- - -
- System - - - AddBannedIp: addBannedIp(parameters, [callback]) - - DecryptPassword: decryptPassword(parameters, [callback]) - - EncryptPassword: encryptPassword(parameters, [callback]) - - GetActivityLog: getActivityLog(parameters, [callback]) - - GetAdminDetails: getAdminDetails([callback]) - - GetAdminUsers: getAdminUsers(parameters, [callback]) - - GetAutomationLog: getAutomationLog(parameters, [callback]) - - GetConfigurationValue: getConfigurationValue(parameters, [callback]) - - GetCurrencies: getCurrencies([callback]) - - GetEmailTemplates: getEmailTemplates(parameters, [callback]) - - GetPaymentMethods: getPaymentMethods([callback]) - - GetStaffOnline: getStaffOnline([callback]) - - GetStats: getStats(parameters, [callback]) - - GetToDoItems: getToDoItems(parameters, [callback]) - - GetToDoItemStatuses: getToDoItemStatuses([callback]) - - LogActivity: logActivity(parameters, [callback]) - - SendAdminEmail: sendAdminEmail(parameters, [callback]) - - SendEmail: sendEmail(parameters, [callback]) - - SetConfigurationValue: setConfigurationValue(parameters, [callback]) - - TriggerNotificationEvent: triggerNotificationEvent(parameters, [callback]) - - UpdateAdminNotes: updateAdminNotes(parameters, [callback]) - - UpdateAnnouncement: updateAnnouncement(parameters, [callback]) - - UpdateToDoItem: updateToDoItem(parameters, [callback]) - - WhmcsDetails: whmcsDetails([callback]) -
- -
- Tickets - - - GetSupportDepartments: getSupportDepartments(parameters, [callback]) - - GetSupportStatuses: getSupportStatuses(parameters, [callback]) - - GetTicket: getTicket(parameters, [callback]) - - GetTicketAttachment: getTicketAttachment(parameters, [callback]) - - GetTicketCounts: getTicketCounts(parameters, [callback]) - - GetTicketNotes: getTicketNotes(parameters, [callback]) - - GetTicketPredefinedCats: getTicketPredefinedCats([callback]) - - GetTicketPredefinedReplies: getTicketPredefinedReplies(parameters, [callback]) - - GetTickets: getTickets(parameters, [callback]) -
- -
- Users - - - AddUser: addUser(parameters, [callback]) - - CreateClientInvite: createClientInvite(parameters, [callback]) - - DeleteUserClient: deleteUserClient(parameters, [callback]) - - GetPermissionsList: getPermissionsList([callback]) - - GetUserPermissions: getUserPermissions(parameters, [callback]) - - GetUsers: getUsers(parameters, [callback]) - - ResetPassword: resetPassword(parameters, [callback]) - - UpdateUser: updateUser(parameters, [callback]) - - UpdateUserPermissions: updateUserPermissions(parameters, [callback]) -
- - -## Custom API functions - -It is possible to call custom functions using the method callApi: - -```javascript -const parameters = { - paramx: 'x', - paramy: 'y' -}; - -whmcs.callApi('CustomFunctionName', parameters) - .then(details => { - ... - }) - .catch(err => { - ... - }); +docker run \ +-p 6379:6379 \ +-v /home/db/redis/data:/data:rw \ +-v /home/db/redis/conf/redis.conf:/etc/redis/redis.conf:ro \ +--privileged=true \ +--name redis \ +-d redis redis-server /etc/redis/redis.conf ``` - -## Tests - -Tests are implemented using `mocha` and `chai`. Run them with `npm test`. - - -## License - -Pedro Dias - [@pedromdias](https://twitter.com/pedromdias) - -Licensed under the Apache license, version 2.0 (the "license"); You may not use this file except in compliance with the license. You may obtain a copy of the license at: - -http://www.apache.org/licenses/LICENSE-2.0.html - -Unless required by applicable law or agreed to in writing, software distributed under the license is distributed on an "as is" basis, without warranties or conditions of any kind, either express or implied. See the license for the specific language governing permissions and limitations under the license. diff --git a/app.rb b/app.rb new file mode 100644 index 0000000..50d46a3 --- /dev/null +++ b/app.rb @@ -0,0 +1,70 @@ +require "sinatra" +require "dotenv/load" +require "sinatra/json" +require "sinatra/namespace" +require "sinatra/reloader" if development? + +require "redis" +require "bcrypt" +# require "json" +# require "bson" +require "mongoid" + +configure :production, :development do + enable :logging + enable :reloader + + set :server, :puma + set :show_exceptions, false + set :default_content_type, "application/json" + + # Session Middleware + # set :sessions, :expire_after => 60 * 60 * 1 # 1 hour in seconds + # set :session_store, Rack::Session::Pool, :key => "session_id", + # :expire_after => 60 * 60 * 1, # 1 hour in seconds + # :cookie_only => false, + # :defer => true + # set :session_secret, ENV["SESSION_SECRET"] + # + # use Rack::Session::Cookie middleware instead of defualt 'enable :sessions' if you need to set additional parameters [pool] => save to memory, lost data after restart [cookie] => With Cookie instead you will have restart-persistent sessions at the price of marshaling. + # + # use Rack::Session::Pool, :expire_after => 60 * 60 * 1 # 1 hour in seconds + use Rack::Session::Cookie, :path => "/", + :httponly => true, + :expire_after => 60 * 60 * 24 * 1, # 5 days in seconds + :secret => ENV["SESSION_SECRET"] + + # eable protection + use Rack::Protection, :except => :path_traversal + + # connect to db + REDIS = Redis.new(url: ENV["REDIS_URI"]) + # DB Setup + Mongoid.load!(File.join(File.dirname(__FILE__), "config", "mongo.yml"), :production) +end + +before do + # Switch to parameter based session management if the client is an ios device + if env["HTTP_USER_AGENT"] =~ /iOS/ + session.options[:cookie_only] = false + session.options[:defer] = true + end +end + +def test(params) + { "test from app": params.to_s }.to_json +end + +# load helper +require "./helper" + +# Routing +%w[route lib models].each { |path| Dir[File.join(File.dirname(__FILE__), path, "*.rb")].each { |file| require file } } + +# Error handlers +error 404 do + halt(404, { code: 404, error: "[client] Not found" }.to_json) +end +error 500 do + halt(500, { code: 500, error: "[Server] Not found" }.to_json) +end diff --git a/config/config.ru b/config/config.ru new file mode 100644 index 0000000..91196af --- /dev/null +++ b/config/config.ru @@ -0,0 +1,3 @@ +# Encoding.default_external = "utf-8" +require "./app" +run Sinatra::Application diff --git a/config/mongo.yml b/config/mongo.yml new file mode 100644 index 0000000..20f3a82 --- /dev/null +++ b/config/mongo.yml @@ -0,0 +1,18 @@ +development: + clients: + default: + database: <%= ENV['MONGO_DB'] %> + hosts: + - <%= ENV['MONGO_URL'] %> + user: <%= ENV['MONGO_USER'] %> + password: <%= ENV['MONGO_PASS'] %> + # options: + # skip_version_check: true + # safe: true +production: + clients: + default: + uri: <%= ENV['MONGO_URI'] %> + options: + skip_version_check: true + safe: true diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..f3c802f --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,7 @@ +workers Integer(ENV["WEB_CONCURRENCY"] || 2) +threads_count = Integer(ENV["THREAD_COUNT"] || 10) +threads threads_count, threads_count + +rackup DefaultRackup +port ENV["PORT"] || 4567 +environment ENV["RACK_ENV"] || "development" diff --git a/helper.rb b/helper.rb new file mode 100644 index 0000000..8609c96 --- /dev/null +++ b/helper.rb @@ -0,0 +1,117 @@ +helpers do + def hash_password(password) + BCrypt::Password.create(password).to_s + end + + def test_password(password, hash) + BCrypt::Password.new(hash) == password + end + + def register(usersname, email, password) + password_salt = BCrypt::Engine.generate_salt + password_hash = BCrypt::Engine.hash_secret(password, password_salt) + + @user = User.new(:username => username.downcase, + :email => email.downcase, + :password_hash => password_hash, + :password_salt => password_salt) + @user.save + if @user.save! + return { "result": "success", "message": "User registered successfuly" } + else + return { "result": "error", "message": "Error, please try again." } + end + end + + # def self.authenticate(username, password) + # user = User.username(username).first + # if user && user.passwordhash == Digest::SHA1.hexdigest(password) + # return user + # else + # return nil + # end + # end + + # def login(email, password) + # @user = User.find_by({ :username => username }) + # # method alias: == vs is_passowrd? + # if BCrypt::Password.new(@user.password_hash).is_password?(password) + # session[:id] = @user.id + # session[:username] = @user.username + # return redirect "/", flash[:notice] = "Successfully login..." + # else + # return redirect back, flash[:error] = "Error, please try again." + # end + # end + + ## Jwt + # + # protected just does a redirect if we don't have a valid token + # + def protected! + return if authorized? + redirect to("/login") + end + + # helper to extract the token from the session, header or request param + # if we are building an api, we would obviously want to handle header or request param + def extract_token + # check for the access_token header + token = request.env["access_token"] + + if token + return token + end + + # or the form parameter _access_token + token = request["access_token"] + + if token + return token + end + + # or check the session for the access_token + token = session["access_token"] + + if token + return token + end + + return nil + end + + # check the token to make sure it is valid with our public key + def authorized? + @token = extract_token + + if @token.nil? + session["message"] = "No JWT found in session. Please log in." + return false + end + + begin + @payload, @header = JWT.decode(@token, settings.verify_key, true, { algorithm: "HS256" }) + + @exp = @header["exp"] + + # check to see if the exp is set (we don't accept forever tokens) + if @exp.nil? + session["message"] = "No exp set on JWT token." + return false + end + + @exp = Time.at(@exp.to_i) + + # make sure the token hasn't expired + if Time.now > @exp + session["message"] = "JWT token expired." + return false + end + + @user_id = @payload["user_id"] + rescue JWT::DecodeError => e + session["message"] = "JWT decode error: #{e.message}" + return false + end + end +end diff --git a/lib/color.rb b/lib/color.rb new file mode 100644 index 0000000..594c954 --- /dev/null +++ b/lib/color.rb @@ -0,0 +1,29 @@ +class String + def black; "\e[30m#{self}\e[0m" end + def red; "\e[31m#{self}\e[0m" end + def green; "\e[32m#{self}\e[0m" end + def brown; "\e[33m#{self}\e[0m" end + def blue; "\e[34m#{self}\e[0m" end + def magenta; "\e[35m#{self}\e[0m" end + def cyan; "\e[36m#{self}\e[0m" end + def gray; "\e[37m#{self}\e[0m" end + + def bg_black; "\e[40m#{self}\e[0m" end + def bg_red; "\e[41m#{self}\e[0m" end + def bg_green; "\e[42m#{self}\e[0m" end + def bg_brown; "\e[43m#{self}\e[0m" end + def bg_blue; "\e[44m#{self}\e[0m" end + def bg_magenta; "\e[45m#{self}\e[0m" end + def bg_cyan; "\e[46m#{self}\e[0m" end + def bg_gray; "\e[47m#{self}\e[0m" end + + def bold; "\e[1m#{self}\e[22m" end + def italic; "\e[3m#{self}\e[23m" end + def underline; "\e[4m#{self}\e[24m" end + def blink; "\e[5m#{self}\e[25m" end + def reverse_color; "\e[7m#{self}\e[27m" end + + def no_colors + self.gsub /\e\[\d+m/, "" + end +end diff --git a/lib/jwt.rb b/lib/jwt.rb new file mode 100644 index 0000000..793fa92 --- /dev/null +++ b/lib/jwt.rb @@ -0,0 +1,76 @@ +# To connect this middleware.rb file to your sinatra app +# add 'use JWTAuthorization' as one of your first lines in +# your Application class. +# e.g. +# require 'middlewares.rb' +# class Application < Sinatra::Base +# use JWTAuthorization +# ... +# end + +# require "sinatra/json" +require "jwt" + +# JWT settings + +# REMOVE rsa encryption +# signing_key_path = File.expand_path("../app.rsa", __FILE__) +# verify_key_path = File.expand_path("../app.rsa.pub", __FILE__) + +# signing_key = "" +# verify_key = "" + +# File.open(signing_key_path) do |file| +# signing_key = OpenSSL::PKey.read(file) +# end + +# File.open(verify_key_path) do |file| +# verify_key = OpenSSL::PKey.read(file) +# end + +# set :signing_key, ENV["JWT_KEY"] +# set :verify_key, ENV["JWT_SECRET"] + +# use JwtAuth + +# class JWTAuthorization +# def initialize(app) +# @app = app +# end + +# def call(env) +# begin +# # env.fetch gets http header + +# # bearer = env.fetch('HTTP_AUTHORIZATION', '').split(' ')[1] # also work +# bearer = env.fetch("HTTP_AUTHORIZATION").slice(7..-1) # gets JWT token +# # key = OpenSSL::PKey::RSA.new ENV["PUBLIC_KEY"] # read public key pem file +# key = ENV["JWT_KEY"] +# # payload = JWT.decode bearer, key, true, { algorithm: "RS256" } # decode and verify token with pub key +# claims = payload.first + +# payload, header = JWT.decode bearer, ENV["JWT_SECRET"], true, { algorithm: "HS256" } +# claims = payload.first +# # current_user is defined by env[:user]. +# # useful to define current_user if you are using pundit gem +# if claims["iss"] == "user" +# env[:user] = User.find_by_email(claims["email"]) +# end + +# # access your claims here... + +# @app.call env +# rescue JWT::ExpiredSignature +# [403, { "Content-Type" => "text/plain" }, ["The token has expired."]] +# rescue JWT::DecodeError +# [401, { "Content-Type" => "text/plain" }, ["A token must be passed."]] +# rescue JWT::InvalidIssuerError +# [403, { "Content-Type" => "text/plain" }, ["The token does not have a valid issuer."]] +# rescue JWT::InvalidIatError +# [403, { "Content-Type" => "text/plain" }, ['The token does not have a valid "issued at" time.']] +# # useful only if using pundit gem +# rescue Pundit::NotAuthorizedError +# [401, { "Content-Type" => "text/plain" }, ["Unauthorized access."]] +# end +# end +# end diff --git a/lib/ubersmith.rb b/lib/ubersmith.rb new file mode 100755 index 0000000..30e6e50 --- /dev/null +++ b/lib/ubersmith.rb @@ -0,0 +1,160 @@ +require 'mechanize' +require 'json' +# This gem provides programmatic access to the Ubersmith API from ruby. To use it create +# an instance of the Ubersmith::API class using the url, username, and api token for +# the Ubersmith server. +# +# require 'ubersmithrb' +# api = Ubersmith::API.new(myurl, myuser, mytoken) +# result = api.client.create({:first_name => "Test", :last_name => "User"}) +# puts result.message +module Ubersmith + + # This class serves as the API handle for communicating with an ubersmith server. + # It contains accessors for communicating with each of the ubersmith API + # modules. When instantiated it requires the API URL to connect to and the user + # name and API token to use. For documentation of the Ubersmith API calls + # available see http://www.ubersmith.com/kbase/index.php?_m=downloads&_a=view&parentcategoryid=2 + class API + + # Initializer method. Accepts API URL, username, and API token as parameters. + def initialize(url, user, token) + @url = url + @user = user + @token = token + @modules = {} + [:uber, :client, :device, :order, :sales, :support].each do |mod| + @modules[mod] = Ubersmith::Command.new(mod, url, user, token) + end + end + + # This accessor is used for calling API methods under the uber module. + def uber + @modules[:uber] + end + + # This accessor is used for calling API methods under the client module. + def client + @modules[:client] + end + + # This accessor is used for calling API methods under the device module. + def device + @modules[:device] + end + + # This accessor is used for calling API methods under the order module. + def order + @modules[:order] + end + + # This accessor is used for calling API methods under the sales module. + def sales + @modules[:sales] + end + + # This accessor is used for calling API methods under the support module. + def support + @modules[:support] + end + end + + + # This class is the command handler for sending API commands and processing the + # response. Each instance of the handler is given a module name and method calls + # made to instances of this class will then be delegated to that API module. + class Command + def initialize(mod, url, user, token) + @modname = mod + @url = url + @user = user + @token = token + @agent = Mechanize.new + end + + # Returns a formatted API command call URL. + def command_url(cmd) + "#{@url}?method=#{@modname.to_s}.#{cmd}" + end + + # This class uses method_missing for handling delegation to the API method. + # When making a call from this class call the method as you would a normal + # ruby method. Parameters passed should be a hash of all the fields to + # send to the API. Consult the ubersmith API docs for the necessary fields + # for each API call. + def method_missing(sym, *args) + cmd = command_url(sym) + @agent.add_auth(cmd, @user, @token) + resp = nil + begin + a = args.first +# puts "#{cmd}(#{a.inspect})" + page = (a.nil?) ? @agent.get(cmd) : @agent.post(cmd, a) +# puts "#{page.content}" + resp = Ubersmith::Response.new(JSON.parse(page.content)) + rescue Exception => e + if !page.nil? and !page.content.nil? and page.content.include?("PDF") + resp = Ubersmith::Response.new({'status' => true, 'data' => page.content}) + elsif !page.nil? and !page.content.nil? and page.content.include?("HTML") + resp = Ubersmith::Response.new({'status' => true, 'data' => page.content}) + else + resp = Ubersmith::Response.new({'status' => false, 'error_code' => 500, 'error_message' => e.message}) + end + end + resp + end + end + + # This class represents a response from the API. It maintains the result status + # and any error codes and messages in addition to the data portion of the API response. + class Response + def initialize(raw = {'status' => false, 'error_code' => '999', 'error_message' => 'No response'}) + @response = raw + end + + # Returns true if the API call was successful. + def ok? + @response['status'] == true + end + + # Returns true if the API had an error. + def error? + @response['status'] != true + end + + # Return an message string. OK if successful, or error code and message if error. + def message + (ok?) ? "OK" : "(#{error_code}): #{error_message}" + end + + # Returns the error code if one was given. + def error_code + @response['error_code'] + end + + # Returns the error message if one was given. + def error_message + @response['error_message'] + end + + # Returns parsed data result from the API call. For many API calls this will + # be a nested hash data structure. For some it may be a scalar value. Consult + # the Ubersmith API docs for the value to expect for each call. + def data + @response['data'] + end + + # This class provides a convenience means via method_missing to delegate to the + # data results of the response so that the main object can be treated like the + # data directly. + def method_missing(sym, *args) + if args.empty? + @response['data'].send(sym) + else + @response['data'].send(sym, *args) + end + end + + end + +end diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index 22de59c..0000000 --- a/lib/utils.js +++ /dev/null @@ -1,23 +0,0 @@ -const hasOwnProperty = Object.prototype.hasOwnProperty; - -module.exports = { - /** - * Extend properties of one object with one or more Objects - * Copied from Underscore - http://underscorejs.org/ - * @param obj Object - * @returns Object - */ - extend: function (obj) { - if (typeof obj !== 'object') return obj; - let source, prop; - for (let i = 1, length = arguments.length; i < length; i++) { - source = arguments[i]; - for (prop in source) { - if (hasOwnProperty.call(source, prop)) { - obj[prop] = source[prop]; - } - } - } - return obj; - } -}; diff --git a/lib/whmcs.rb b/lib/whmcs.rb new file mode 100644 index 0000000..aced6a5 --- /dev/null +++ b/lib/whmcs.rb @@ -0,0 +1,152 @@ +require "active_support" +require "active_support/core_ext/object/blank" +require "net/http" + +# Dir[File.join(__dir__, 'whmcs', '*.rb')].each { |file| require file } +Dir[File.join(File.dirname(__FILE__), "whmcs", "*.rb")].each { |file| require file } + +if RUBY_ENGINE == "ruby" and not ENV["DISABLE_OJ"] + require "oj" + Oj.mimic_JSON +end + +module Whmcs + class Error < StandardError; end + + ## + # Define settings for the user to provide + # + # The order of the fields is determined by their position in the list. + # + # Fields: + # + # name: must be lower case, no numbers, spaces, or special characters (except _). + # label: The name of the field shown in the UI + # description: Displayed under the field to help the user + # field_type: + # * string | Short text field + # * text | Textarea + # * password | Will store result encrypted and use a password field on the UI + # * checkbox | Will display a checkbox and store it as a boolean value + # * dropdown | Presents a list of values + # + @settings = [ + { + name: "endpoint", + label: "WHMCS URL", + description: "example: whmcs.mysite.com", + field_type: "string", + default: "", + }, + { + name: "identifier", + label: "API Key", + description: "", + field_type: "string", + default: "", + }, + { + name: "secret", + label: "API Secret", + description: "", + field_type: "password", + default: "", + }, + { + name: "accesskey", + label: "API Access Key", + description: "(optional) access key to bypass WHMCS api IP restrictions", + field_type: "password", + default: "", + }, + { + name: "due_date", + label: "Invoice Due Day of Month", + description: "What day of the month should the invocie be due? All invoices are generated on the 1st of the month.", + field_type: "string", + default: "1", + }, + { + name: "invoice_on", + label: "When to generate invoices", + description: "By default, we will invoice on the due date. Alternatively, you can set this to nextcron.", + field_type: "string", + default: "duedate", + }, + ] + + @config = @settings.map { |i| { i[:name].to_sym => nil } }.reduce Hash.new, :merge + @valid_config_keys = @config.keys + + class << self + def config + @config + end + + def settings + @settings + end + + def configure(opts = {}) + opts.each { |k, v| @config[k.to_sym] = v if @valid_config_keys.include? k.to_sym } + end + + ## + # Test the connection to the integration + # + # Should return boolean (true|false) + # + def test_connection! + Whmcs::Base.new.remote("GetHealthStatus").kind_of? Net::HTTPSuccess + end + + ## + # Load WHMCS version + # + # `{"result"=>"success", "whmcs"=>{"version"=>"8.2.0", "canonicalversion"=>"8.2.0-release.1"}}` + # + # @return Gem::Version + def whmcs_version + Gem::Version.new Oj.load(Whmcs::Base.new.remote("WhmcsDetails").body)["whmcs"]["version"] + rescue + Gem::Version.new "0.0" + end + + def call(api, *params) + response = Whmcs::Base.new.remote(api, *params) + # return utf-8 encoded string + # puts "#{response.each_header { |h| p "#{h} => #{response[h]}" }}" + # puts response.body.encode(Encoding::ISO_8859_1).encode(Encoding::UTF_8) + + if response.kind_of? Net::HTTPSuccess + Oj.load(response.body, { symbol_keys: true, mode: :object }) + elsif response.kind_of? Net::HTTPForbidden + Oj.load(response.body, { symbol_keys: true, mode: :object }) + else + nil + end + end + + ## + # + # Authentication + # + # action: ValidateLogin + # email: string + # password2: string + # { + # "result": "success", + # "userid": "1", + # "passwordhash": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + # "twoFactorEnabled": "false" + # } + def login(email, password) + response = Whmcs::Base.new.remote("ValidateLogin", { email: email, password2: password }) + if response.kind_of? Net::HTTPSuccess + Oj.load(response.body, { symbol_keys: true, mode: :object }) + else + nil + end + end + end +end diff --git a/lib/whmcs/base.rb b/lib/whmcs/base.rb new file mode 100644 index 0000000..6da666c --- /dev/null +++ b/lib/whmcs/base.rb @@ -0,0 +1,47 @@ +module Whmcs + ## + # Base class for our plugin + class Base + def initialize(args = {}) + args.each do |key, value| + send("#{key}=", value) + end + end + + ## + # Initiate remote api calls to WHMCS + def remote(action, opts = {}) + raise Whmcs::Error.new "Plugin not configured" unless configured? + + endpoint = "https://#{sanitized_endpoint}/includes/api.php" + params = { + action: action, + identifier: Whmcs.config[:identifier], + secret: Whmcs.config[:secret], + responsetype: "json", + } + unless Whmcs.config[:accesskey].blank? + params[:accesskey] = Whmcs.config[:accesskey] + end + params.merge!(opts) unless opts.empty? + # Faraday.post(endpoint, params) + Net::HTTP.post_form(URI(endpoint), params) + end + + private + + ## + # Perform some basic sanity checks on the endpoint + def sanitized_endpoint + return nil if Whmcs.config[:endpoint].blank? + e = Whmcs.config[:endpoint].split("://").last # remove all protocol definitions + e[-1] == "/" ? e[0..-2] : e # Remove trailing '/'. + end + + ## + # Simple sanity check to make sure our required parameters are not blank + def configured? + !(Whmcs.config[:identifier].blank? || Whmcs.config[:secret].blank? || Whmcs.config[:endpoint].blank?) + end + end +end diff --git a/lib/whmcs/hooks.rb b/lib/whmcs/hooks.rb new file mode 100644 index 0000000..fed01a0 --- /dev/null +++ b/lib/whmcs/hooks.rb @@ -0,0 +1,67 @@ +module Whmcs + ## + # Hooks allow you to extend your module into different aspects of ComputeStacks. + # + # Available Hooks: + # + # user_created, user_updated: + # * params: User model. This allows you to access the user object and make changes to the user + # + # process_usage: + # * params: Array of Hashes | aggregated usage data for the month. This will be triggered on the last day of the month. + # + class Hooks < Base + + attr_accessor :errors + + def initialize + self.errors = [] + end + + + ## + # UserCreated hook + # + # This hook receives the full user Model from ComputeStacks. You can make changes to the user using this. + # + def user_created(model) + success, errors = Whmcs::Service.link_user_by_username model + return true if success + self.errors += errors + false + end + + ## + # ProcessUsage Hook + # + # Take the raw aggregated billing data from ComputeStacks and process it + # + def process_usage(raw_data) + usage = Whmcs::Usage.new(raw_data) + return true if usage.billable_items.empty? + result = usage.save + self.errors += usage.errors + errors.empty? && result + end + + ## + # User Updated + # + # Update the username field of a WHMCS service + # + def user_updated(model) + return true if model.labels.empty? || model.labels.dig('whmcs', 'service_id').nil? + service = Whmcs::Service.find(model.labels['whmcs']['service_id'].to_i) + if service.nil? + self.errors << "Unknown service, unable to update." + return false + end + return true if model.email == service.username + service.username = model.email + result = service.save + self.errors += service.errors + errors.empty? && result + end + + end +end \ No newline at end of file diff --git a/lib/whmcs/service.rb b/lib/whmcs/service.rb new file mode 100644 index 0000000..eabd453 --- /dev/null +++ b/lib/whmcs/service.rb @@ -0,0 +1,171 @@ +module Whmcs + class Service < Base + + attr_accessor :id, + :client_id, + :product_id, + :username, + :status, + :p_module, + :errors + + def initialize(args = {}) + self.errors = [] + super + end + + # Set service username + def save + return false unless valid? + response = remote('UpdateClientProduct', { 'serviceid' => id, 'serviceusername' => username }) + begin + result = Oj.load(response.body, { symbol_keys: true, mode: :object }) + self.errors << result[:message] unless result[:result] == 'success' + rescue + # probably a failure anyways + self.errors << "Fatal error reporting usage to WHMCS." + end + errors.empty? + end + + class << self + + ## + # Load service + def find(id) + response = Whmcs::Base.new.remote('GetClientsProducts', { 'serviceid' => id }) + return nil unless response.kind_of? Net::HTTPSuccess + result = Oj.load(response.body, { symbol_keys: true, mode: :object }) + return nil unless result[:totalresults].to_i == 1 + service = result[:products][:product][0] + new( + id: service[:id], + client_id: service[:clientid], + product_id: service[:pid], + username: service[:username], + p_module: product_module(service[:pid]), + status: service[:status].downcase + ) + end + + ## + # Find a user given their product/service username + def find_all_by_username(username) + services = [] + start_number = 0 + loop_until = Time.now + 300 # 5 minutes from now + while loop_until >= Time.now do + response = Whmcs::Base.new.remote('GetClientsProducts', { limitstart: start_number, username2: username }) + break unless response.kind_of? Net::HTTPSuccess + result = Oj.load(response.body, { symbol_keys: true, mode: :object }) + break if result[:totalresults].zero? + + result[:products][:product].each do |i| + services << new( + id: i[:id], + product_id: i[:pid], + client_id: i[:clientid], + status: i[:status], + username: i[:username], + p_module: Whmcs::Service.product_module(i[:pid]) + ) + end + + break unless (result[:startnumber] + result[:numreturned]) < result[:totalresults] + start_number = result[:startnumber] + result[:numreturned] + end + services + end + + ## + # Determine the product module name given a productid + def product_module(pid) + response = Whmcs::Base.new.remote('GetProducts', { pid: pid }) + return nil unless response.kind_of? Net::HTTPSuccess + result = Oj.load(response.body, { symbol_keys: true, mode: :object }) + return nil unless result[:result] == 'success' + result[:products][:product].each do |i| + return i[:module] if i[:module] + end + nil + end + + ## + # Link a user given their Username + # + # Triggered by `user_created` hook to link a CS user with their WHMCS account. + # + # This method is passed the model from ComputeStacks, allowing us to save our own labels. + # + def link_user_by_username(model) + return true, [] if model.labels.empty? || model.labels['cpanel'].nil? + candidates = [] + model.labels['cpanel'].each_key do |server| + items = Whmcs::Service.find_all_by_username(model.labels['cpanel'][server]) + items.each do |i| + candidates << { + client_id: i.client_id, + service_id: i.id, + p_module: i.p_module + } unless candidates.map { |i| i[:client_id] }.include?(i.client_id) + end + end + + if candidates.empty? + return false, ["No candidates found"] + end + + if candidates.count > 1 + return false, ["Multiple candidates found (found: #{candidates.count}"] + end + + candidate = candidates.first + + # Load the WHMCS user + whmcs_user = Whmcs::User.find_by_id candidate[:client_id] + + return false, ["Missing WHMCS user"] if whmcs_user.nil? + + labels = model.labels + labels.merge!( + 'whmcs' => { + 'client_id' => candidate[:client_id], + 'service_id' => candidate[:p_module] == 'computestacks' ? candidate[:service_id] : nil + } + ) + + unless whmcs_user.nil? + model.active = true + + # `requested_email` means that we will try to give this email, otherwise we will create a generated one + model.requested_email = whmcs_user.email + + model.fname = whmcs_user.fname + model.lname = whmcs_user.lname + model.address1 = whmcs_user.address1 + model.address2 = whmcs_user.address2 + model.city = whmcs_user.city + model.state = whmcs_user.state + model.zip = whmcs_user.zip + model.country = whmcs_user.country + model.phone = whmcs_user.phone + model.labels = labels + + unless model.save + return false, model.errors.full_messages + end + + end + return true, [] + end + end + + private + + def valid? + self.errors << "Missing service id" if self.id.blank? + errors.empty? + end + + end +end \ No newline at end of file diff --git a/lib/whmcs/system.rb b/lib/whmcs/system.rb new file mode 100644 index 0000000..b07863e --- /dev/null +++ b/lib/whmcs/system.rb @@ -0,0 +1,35 @@ +module Whmcs + class System < Base + + class << self + ## + # GetAdminUsers: String + # roleid: int + # email: String + # include_disabled: bool + ## + # result: "success" || "error" + # count: int + # admin_users: array + def find_all_by_role_id(params) + admin_users = [] + start_number = 0 + loop_until = Time.now + 300 # 5 minutes from now + while loop_until >= Time.now + response = Whmcs::Base.new.remote("GetAdminUsers", params) + break unless response.kind_of? Net::HTTPSuccess + result = Oj.load(response.body, { symbol_keys: true, mode: :object }) + break if result[:count].zero? + end + end + end + + private + + def valid? + self.errors << "Missing service id" if self.id.blank? + errors.empty? + end + + end +end \ No newline at end of file diff --git a/lib/whmcs/usage.rb b/lib/whmcs/usage.rb new file mode 100644 index 0000000..2b8649e --- /dev/null +++ b/lib/whmcs/usage.rb @@ -0,0 +1,121 @@ +module Whmcs + ## + # Process usage data from ComputeStacks + class Usage < Base + + attr_accessor :billable_items, + :errors + + def initialize(raw_data = []) + self.billable_items = [] + self.errors = [] + aggregate_usage!(raw_data) unless raw_data.empty? + end + + ## + # Create billable items in WHMCS + def save + t = Time.new(Time.now.utc.year,Time.now.utc.month)+45*24*3600 + due_date = Time.new(t.year, t.month, due_date_day).strftime('%Y-%m-%d') + # Generate Billable Items + billable_items.each do |i| + # Intentionally not including this so we can capture the exception in CS. + response = remote('AddBillableItem', billabe_by_version(i, due_date)) + begin + result = Oj.load(response.body, { symbol_keys: true, mode: :object }) + self.errors << result[:message] unless result[:result] == 'success' + rescue + # probably a failure anyways + self.errors << "Fatal error reporting usage to WHMCS." + end + end + errors.empty? + end + + private + + def billabe_by_version(i, due_date) + if Whmcs.whmcs_version >= Gem::Version.new('8.2.0') + { + 'clientid' => i[:client_id], + 'description' => i[:product], + 'amount' => i[:total], + 'invoiceaction' => invoice_generate_on, + 'duedate' => due_date, + 'quantity' => i[:qty], + 'unit' => 'quantity' + } + else + { + 'clientid' => i[:client_id], + 'description' => i[:product], + 'amount' => i[:total], + 'invoiceaction' => invoice_generate_on, + 'duedate' => due_date, + 'hours' => i[:qty] + } + end + end + + def due_date_day + default_due_date = Whmcs.settings.select{ |i| i[:name] == 'due_date' }[0][:default].to_i + default_due_date = default_due_date.zero? ? 1 : default_due_date + Whmcs.config[:due_date].to_i.zero? ? default_due_date : Whmcs.config[:due_date].to_i + end + + def invoice_generate_on + generate_on_default = Whmcs.settings.select{ |i| i[:name] == 'invoice_on' }[0][:default] + %w(duedate nextcron).include?(Whmcs.config[:invoice_on]) ? Whmcs.config[:invoice_on] : generate_on_default + end + + ## + # Format data provided by ComputeStacks, for WHMCS + def aggregate_usage!(data) + # Collect all WHMCS users IDs + user_ids = [] + data.each do |i| + # clientid = i.dig(:user, :labels, 'whmcs', 'client_id') + # if clientid.blank? && i.dig(:user, :labels, 'whmcs', 'service_id') + # s = Whmcs::Service.find(i.dig(:user, :labels, 'whmcs', 'service_id')) + # client_id = s.client_id if s + # end + whmcs_cid = i.dig(:user, :labels, 'whmcs', 'client_id') + whmcs_sid = i.dig(:user, :labels, 'whmcs', 'service_id') + clientid = if whmcs_cid.blank? + whmcs_sid.blank? ? nil : Whmcs::Service.find(i.dig(:user, :labels, 'whmcs', 'service_id'))&.client_id + else + i.dig(:user, :labels, 'whmcs', 'client_id') + end + clientid = i.dig(:user, :external_id) if clientid.blank? + next if clientid.blank? + clientid = clientid.to_i # ensure we always have an int + user_ids << clientid unless user_ids.include?(clientid) + # Now set our copy of the data to include the clientid as the external id. We will use this later to verify the output + i[:user][:external_id] = clientid + end + + billables = [] + user_ids.each do |i| + products = [] + data.each do |item| + if item[:user] && item[:user][:external_id] == i + products << item[:product][:name] unless products.include?(item[:product][:name]) + end + end + products.each do |p| + total = 0.0 + qty = 0.0 + data.each do |item| + next if item[:user].nil? || item[:user][:external_id] != i + next unless item[:product][:name] == p + total += item[:total] + qty += item[:qty] + end + billables << { total: total.round(2), qty: qty , client_id: i, product: p } unless total.zero? + end + end + self.billable_items = billables + end + + end +end \ No newline at end of file diff --git a/lib/whmcs/user.rb b/lib/whmcs/user.rb new file mode 100644 index 0000000..9afc411 --- /dev/null +++ b/lib/whmcs/user.rb @@ -0,0 +1,70 @@ +module Whmcs + ## + # WHMCS User + class User < Base + attr_accessor :id, + :uuid, + :fname, + :lname, + :company, + :email, + :address1, + :address2, + :city, + :state, + :zip, + :country, + :phone + + class << self + + ## + # Find a WHMCS user by their ID + def find_by_id(clientid) + response = Whmcs::Base.new.remote('GetClientsDetails', {clientid: clientid}) + if response.kind_of? Net::HTTPSuccess + result = Oj.load(response.body, { symbol_keys: true, mode: :object }) + if result[:result] == 'success' && result[:client] + new_from_response(result[:client]) + end + else + nil + end + end + + ## + # Find a WHMCS user by their email + def find_by_email(clientemail) + response = Whmcs::Base.new.remote('GetClientsDetails', {email: clientemail}) + if response.kind_of? Net::HTTPSuccess + result = Oj.load(response.body, { symbol_keys: true, mode: :object }) + if result[:result] == 'success' && result[:client] + new_from_response(result[:client]) + end + else + nil + end + end + + ## + # Create a Whmcs::User object from the WHMCS response + def new_from_response(data) + new( + id: data[:id], + uuid: data[:uuid], + fname: data[:firstname], + lname: data[:lastname], + email: data[:email], + address1: data[:address1], + address2: data[:address2], + city: data[:city], + state: data[:fullstate], + zip: data[:postcode], + country: data[:countrycode].blank? ? data[:country] : data[:countrycode], + phone: data[:phonenumberformatted].blank? ? data[:phonenumber] : data[:phonenumberformatted] + ) + end + + end + end +end \ No newline at end of file diff --git a/lib/whmcs/version.rb b/lib/whmcs/version.rb new file mode 100644 index 0000000..eb428e5 --- /dev/null +++ b/lib/whmcs/version.rb @@ -0,0 +1,3 @@ +module Whmcs + VERSION = "1.0.0" +end \ No newline at end of file diff --git a/lib/whmcshttpclient.js b/lib/whmcshttpclient.js deleted file mode 100644 index d698e7d..0000000 --- a/lib/whmcshttpclient.js +++ /dev/null @@ -1,137 +0,0 @@ -const utils = require('./utils'), - axios = require('axios'), - xml2js = require('xml2js'), - HttpsProxyAgent = require('https-proxy-agent'), - querystring = require('querystring'); - -class WhmcsHttpClient { - #apiSecret = null; - #apiIdentifier = null; - #username = null; - #password = null; - #accessKey = null; - #serverUrl = null; - #Promise = global.Promise; - #responseType = 'json'; - #userAgent = null; - #proxyUrl = null; - #axiosInstance = null; - #timeout = 0; - - constructor(configs = {}) { - this.#username = (configs.username != null) ? configs.username : this.#username; - this.#password = (configs.password != null) ? configs.password : this.#password; - this.#serverUrl = (configs.serverUrl != null) ? configs.serverUrl : this.#serverUrl; - this.#timeout = (configs.timeout != null) ? configs.timeout : this.#timeout; - this.#apiIdentifier = (configs.apiIdentifier != null) ? configs.apiIdentifier : this.#apiIdentifier; - this.#apiSecret = (configs.apiSecret != null) ? configs.apiSecret : this.#apiSecret; - this.#accessKey = (configs.accessKey != null) ? configs.accessKey : this.#accessKey; - this.#Promise = (configs.Promise != null) ? configs.Promise : this.#Promise; - this.#responseType = (configs.responseType != null) ? configs.responseType : this.#responseType; - this.#userAgent = (configs.userAgent != null) ? configs.userAgent : this.#userAgent; - this.#proxyUrl = (configs.proxyUrl != null) ? configs.proxyUrl : this.#proxyUrl; - - if (this.#Promise != null && typeof this.#Promise.resolve !== 'function') { - throw new Error('Invalid promise library.'); - } - - let axiosConf = { - baseURL: this.#serverUrl, - timeout: this.#timeout, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }; - - if (this.#userAgent != null) { - axiosConf.headers['User-Agent'] = this.#userAgent; - } - - if (this.#proxyUrl != null) { - serverOptions.httpsAgent = new HttpsProxyAgent(this.#proxyUrl); - } - - this.#axiosInstance = axios.create(axiosConf); - } - - /** - * Executes a WHMCS' API action with given parameters. - * WHMCS' official action list available here: https://developers.whmcs.com/api/api-index/ - * @param {String} action Command name - * @param {Object} parameters Request parameters (JSON Object) - * @param {*} callback Optional callback. If not set the method returns a Promise - */ - callApi(action, parameters, callback) { - const _this = this; - let bodyParams = { - action: action, - username: this.#username, - password: this.#password, - identifier: this.#apiIdentifier, - secret: this.#apiSecret, - accesskey: this.#accessKey, - responsetype: this.#responseType - }; - - if (typeof parameters === 'function' && callback == null) { - callback = parameters; - } else { - bodyParams = utils.extend(bodyParams, parameters); - } - - function startRequest(callback) { - _this.#post(bodyParams, function (err, data, httpStatusCode) { - if (err) { - callback(err); - } else if (data.result && data.result == 'error' || data.status && data.status == 'error') { - let whmcsError = new Error('WHMCS Error (' + httpStatusCode + '): ' + data.message); - callback(whmcsError); - } else { - callback(null, data); - } - }); - } - - if (callback === undefined) { - return new this.#Promise(function (resolve, reject) { - startRequest(function (err, data) { - if (err) { - reject(err); - } else { - resolve(data); - } - }); - }); - } else { - startRequest(callback); - } - } - - #post(bodyParams, callback) { - const qs = querystring.stringify(bodyParams); - this.#axiosInstance.post('/includes/api.php', qs) - .then(resp => { - if (resp.headers['content-type'].indexOf('application/xml') > -1) { - xml2js.parseString(resp.data, { - 'explicitArray': false - }, function (err, parsedXml) { - if (err) { - callback(new Error('Error parsing xml')); - } else if (!parsedXml) { - callback(new Error('Empty HTTP response')); - } else if (!parsedXml.whmcsapi || !parsedXml.whmcsapi.result) { - callback(new Error('Unexpected XML response')); - } else { - callback(null, parsedXml.whmcsapi, resp.status); - } - }); - } else { - callback(null, resp.data, resp.status); - } - }).catch(err => { - callback(err); - }); - } -} - -module.exports = WhmcsHttpClient; \ No newline at end of file diff --git a/models/people.rb b/models/people.rb new file mode 100644 index 0000000..a31f641 --- /dev/null +++ b/models/people.rb @@ -0,0 +1,17 @@ +# Models::People +class People + include Mongoid::Document + include Mongoid::Attributes::Dynamic + include Mongoid::Timestamps + + field :full_name, type: String + field :company_name, type: String + field :address1, type: String + field :postal_code, type: String + field :city, type: String + field :country, type: String + field :mobile, type: String + field :phone, type: String + field :fax, type: String + field :activity, type: String +end diff --git a/models/user.rb b/models/user.rb new file mode 100644 index 0000000..d96c9c6 --- /dev/null +++ b/models/user.rb @@ -0,0 +1,13 @@ +# Models::User +class User + include Mongoid::Document + include Mongoid::Timestamps + + field :username, type: String + field :email, type: String + field :passwordhash, type: String + field :salt, type: String + field :access_token, type: String + field :refresh_token, type: String + field :admin, type: Boolean +end diff --git a/modules/addons.js b/modules/addons.js deleted file mode 100644 index 69918a6..0000000 --- a/modules/addons.js +++ /dev/null @@ -1,21 +0,0 @@ -class Addons { - /** - * Creates a new Addons object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Updates a Client Addon. - * https://developers.whmcs.com/api-reference/updateclientaddon/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateClientAddon(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateClientAddon', parameters, callback); - }; -} - -module.exports = Addons; diff --git a/modules/affiliates.js b/modules/affiliates.js deleted file mode 100644 index 39bdd5c..0000000 --- a/modules/affiliates.js +++ /dev/null @@ -1,31 +0,0 @@ -class Affiliates { - /** - * Creates a new Affiliates object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Activate affiliate referrals for a client. - * https://developers.whmcs.com/api-reference/affiliateactivate/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - affiliateActivate(parameters, callback) { - return this.whmcsHttpClient.callApi('AffiliateActivate', parameters, callback); - }; - - /** - * Obtain an array of affiliates - * https://developers.whmcs.com/api-reference/getaffiliates/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getAffiliates(parameters, callback) { - return this.whmcsHttpClient.callApi('GetAffiliates', parameters, callback); - }; -} - -module.exports = Affiliates; diff --git a/modules/authentication.js b/modules/authentication.js deleted file mode 100644 index ee15c0a..0000000 --- a/modules/authentication.js +++ /dev/null @@ -1,72 +0,0 @@ -class Authentication { - /** - * Creates a new Authentication object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Create an OAuth Credential - * https://developers.whmcs.com/api-reference/createoauthcredential/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - createOAuthCredential(parameters, callback) { - return this.whmcsHttpClient.callApi('CreateOAuthCredential', parameters, callback); - }; - - /** - * Create a single use, client or user single sign-on access token. - * https://developers.whmcs.com/api-reference/createssotoken/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - createSsoToken(parameters, callback) { - return this.whmcsHttpClient.callApi('CreateSsoToken', parameters, callback); - }; - - /** - * Removes OAuth Credential record. This action cannot be undone. - * https://developers.whmcs.com/api-reference/deleteoauthcredential/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteOAuthCredential(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteOAuthCredential', parameters, callback); - }; - - /** - * List OAuth Credentials matching passed criteria. - * https://developers.whmcs.com/api-reference/listoauthcredentials/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - listOAuthCredentials(parameters, callback) { - return this.whmcsHttpClient.callApi('ListOAuthCredentials', parameters, callback); - }; - - /** - * Updates a given OAuth API Client Credential. - * https://developers.whmcs.com/api-reference/updateoauthcredential/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateOAuthCredential(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateOAuthCredential', parameters, callback); - }; - - /** - * Validate user login credentials. - * https://developers.whmcs.com/api-reference/validatelogin/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - validateLogin(parameters, callback) { - return this.whmcsHttpClient.callApi('ValidateLogin', parameters, callback); - }; - -} - -module.exports = Authentication; diff --git a/modules/billing.js b/modules/billing.js deleted file mode 100644 index 3df9963..0000000 --- a/modules/billing.js +++ /dev/null @@ -1,252 +0,0 @@ -class Billing { - /** - * Creates a new Billing object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Accepts a quote. - * https://developers.whmcs.com/api-reference/acceptquote/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - acceptQuote(parameters, callback) { - return this.whmcsHttpClient.callApi('AcceptQuote', parameters, callback); - }; - - /** - * Adds a Billable Item. - * https://developers.whmcs.com/api-reference/addbillableitem/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addBillableItem(parameters, callback) { - return this.whmcsHttpClient.callApi('AddBillableItem', parameters, callback); - }; - - /** - * Adds credit to a given client. - * https://developers.whmcs.com/api-reference/addcredit/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addCredit(parameters, callback) { - return this.whmcsHttpClient.callApi('AddCredit', parameters, callback); - }; - - /** - * Adds payment to a given invoice. - * https://developers.whmcs.com/api-reference/addinvoicepayment/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addInvoicePayment(parameters, callback) { - return this.whmcsHttpClient.callApi('AddInvoicePayment', parameters, callback); - }; - - /** - * Add a Pay Method to a given client. - * https://developers.whmcs.com/api-reference/addpaymethod/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addPayMethod(parameters, callback) { - return this.whmcsHttpClient.callApi('AddPayMethod', parameters, callback); - }; - - /** - * Add a transaction to the system. - * https://developers.whmcs.com/api-reference/addtransaction/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addTransaction(parameters, callback) { - return this.whmcsHttpClient.callApi('AddTransaction', parameters, callback); - }; - - /** - * Applies the Client’s Credit to an invoice. - * https://developers.whmcs.com/api-reference/applycredit/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - applyCredit(parameters, callback) { - return this.whmcsHttpClient.callApi('ApplyCredit', parameters, callback); - }; - - /** - * Attempt to capture a payment on an unpaid CC Invoice. - * https://developers.whmcs.com/api-reference/capturepayment/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - capturePayment(parameters, callback) { - return this.whmcsHttpClient.callApi('CapturePayment', parameters, callback); - }; - - /** - * Create an invoice using the provided parameters. - * https://developers.whmcs.com/api-reference/createinvoice/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - createInvoice(parameters, callback) { - return this.whmcsHttpClient.callApi('CreateInvoice', parameters, callback); - }; - - /** - * Creates a new quote. - * https://developers.whmcs.com/api-reference/createquote/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - createQuote(parameters, callback) { - return this.whmcsHttpClient.callApi('CreateQuote', parameters, callback); - }; - - /** - * Delete a Pay Method. - * https://developers.whmcs.com/api-reference/deletepaymethod/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deletePayMethod(parameters, callback) { - return this.whmcsHttpClient.callApi('DeletePayMethod', parameters, callback); - }; - - /** - * Deletes a quote. - * https://developers.whmcs.com/api-reference/deletequote/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteQuote(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteQuote', parameters, callback); - }; - - /** - * Generate any invoices that are due to be generated. - * https://developers.whmcs.com/api-reference/geninvoices/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - genInvoices(parameters, callback) { - return this.whmcsHttpClient.callApi('GenInvoices', parameters, callback); - }; - - /** - * Obtain the Credit Log for a Client Account. - * https://developers.whmcs.com/api-reference/getcredits/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getCredits(parameters, callback) { - return this.whmcsHttpClient.callApi('GetCredits', parameters, callback); - }; - - /** - * Retrieve a specific invoice. - * https://developers.whmcs.com/api-reference/getinvoice/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getInvoice(parameters, callback) { - return this.whmcsHttpClient.callApi('GetInvoice', parameters, callback); - }; - - /** - * Retrieve a list of invoices. - * https://developers.whmcs.com/api-reference/getinvoices/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getInvoices(parameters, callback) { - return this.whmcsHttpClient.callApi('GetInvoices', parameters, callback); - }; - - /** - * Obtain the Pay Methods associated with a provided client id. - * https://developers.whmcs.com/api-reference/getpaymethods/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getPayMethods(parameters, callback) { - return this.whmcsHttpClient.callApi('GetPayMethods', parameters, callback); - }; - - /** - * Obtain quotes matching the passed criteria. - * https://developers.whmcs.com/api-reference/getquotes/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getQuotes(parameters, callback) { - return this.whmcsHttpClient.callApi('GetQuotes', parameters, callback); - }; - - /** - * Obtain transactions matching the passed criteria. - * https://developers.whmcs.com/api-reference/gettransactions/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getTransactions(parameters, callback) { - return this.whmcsHttpClient.callApi('GetTransactions', parameters, callback); - }; - - /** - * Send a quote to the associated client. - * https://developers.whmcs.com/api-reference/sendquote/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - sendQuote(parameters, callback) { - return this.whmcsHttpClient.callApi('SendQuote', parameters, callback); - }; - - /** - * Update an invoice using the provided parameters. - * https://developers.whmcs.com/api-reference/updateinvoice/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateInvoice(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateInvoice', parameters, callback); - }; - - /** - * Update a Credit Card Pay Method. - * https://developers.whmcs.com/api-reference/updatepaymethod/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updatePayMethod(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdatePayMethod', parameters, callback); - }; - - /** - * Updates an existing quote. - * https://developers.whmcs.com/api-reference/updatequote/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - - updateQuote(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateQuote', parameters, callback); - }; - - /** - * Updates a transaction in the system. - * https://developers.whmcs.com/api-reference/updatetransaction/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateTransaction(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateTransaction', parameters, callback); - }; -} - -module.exports = Billing; \ No newline at end of file diff --git a/modules/client.js b/modules/client.js deleted file mode 100644 index 0858a86..0000000 --- a/modules/client.js +++ /dev/null @@ -1,180 +0,0 @@ -class Client { - /** - * Creates a new Client object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Adds a client. - * https://developers.whmcs.com/api-reference/addclient/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addClient(parameters, callback) { - return this.whmcsHttpClient.callApi('AddClient', parameters, callback); - }; - - /** - * Adds a contact to a client account. - * https://developers.whmcs.com/api-reference/addcontact/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addContact(parameters, callback) { - return this.whmcsHttpClient.callApi('AddContact', parameters, callback); - }; - - /** - * Close a Client. - * https://developers.whmcs.com/api-reference/closeclient/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - closeClient(parameters, callback) { - return this.whmcsHttpClient.callApi('CloseClient', parameters, callback); - }; - - /** - * Deletes a client. - * https://developers.whmcs.com/api-reference/deleteclient/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteClient(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteClient', parameters, callback); - }; - - /** - * Deletes a contact. - * https://developers.whmcs.com/api-reference/deletecontact/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteContact(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteContact', parameters, callback); - }; - - /** - * Obtain an array of cancellation requests. - * https://developers.whmcs.com/api-reference/getcancelledpackages/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getCancelledPackages(parameters, callback) { - return this.whmcsHttpClient.callApi('GetCancelledPackages', parameters, callback); - }; - - /** - * Obtain an array of client groups. - * https://developers.whmcs.com/api-reference/getclientgroups/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getClientGroups(callback) { - return this.whmcsHttpClient.callApi('GetClientGroups', callback); - }; - - /** - * Obtain the encrypted client password. - * https://developers.whmcs.com/api-reference/getclientpassword/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getClientPassword(parameters, callback) { - return this.whmcsHttpClient.callApi('GetClientPassword', parameters, callback); - }; - - /** - * Obtain the Clients that match passed criteria. - * https://developers.whmcs.com/api-reference/getclients/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getClients(parameters, callback) { - return this.whmcsHttpClient.callApi('GetClients', parameters, callback); - }; - - /** - * Obtain the Clients Product Addons that match passed criteria. - * https://developers.whmcs.com/api-reference/getclientsaddons/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getClientsAddons(parameters, callback) { - return this.whmcsHttpClient.callApi('GetClientsAddons', parameters, callback); - }; - - /** - * Obtain the Clients Details for a specific client. - * https://developers.whmcs.com/api-reference/getclientsdetails/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getClientsDetails(parameters, callback) { - return this.whmcsHttpClient.callApi('GetClientsDetails', parameters, callback); - }; - - /** - * Obtain a list of Client Purchased Domains matching the provided criteria. - * https://developers.whmcs.com/api-reference/getclientsdomains/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getClientsDomains(parameters, callback) { - return this.whmcsHttpClient.callApi('GetClientsDomains', parameters, callback); - }; - - /** - * Obtain a list of Client Purchased Products matching the provided criteria. - * https://developers.whmcs.com/api-reference/getclientsproducts/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getClientsProducts(parameters, callback) { - return this.whmcsHttpClient.callApi('GetClientsProducts', parameters, callback); - }; - - /** - * Obtain the Client Contacts that match passed criteria. - * https://developers.whmcs.com/api-reference/getcontacts/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getContacts(parameters, callback) { - return this.whmcsHttpClient.callApi('GetContacts', parameters, callback); - }; - - /** - * Obtain a list of emails sent to a specific Client ID. - * https://developers.whmcs.com/api-reference/getemails/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getEmails(parameters, callback) { - return this.whmcsHttpClient.callApi('GetEmails', parameters, callback); - }; - - /** - * Updates a client with the passed parameters. - * https://developers.whmcs.com/api-reference/updateclient/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateClient(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateClient', parameters, callback); - }; - - /** - * Updates a contact with the passed parameters. - * https://developers.whmcs.com/api-reference/updatecontact/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateContact(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateContact', parameters, callback); - }; -} - -module.exports = Client; \ No newline at end of file diff --git a/modules/domains.js b/modules/domains.js deleted file mode 100644 index cf8106a..0000000 --- a/modules/domains.js +++ /dev/null @@ -1,171 +0,0 @@ -class Domains { - /** - * Creates a new Domains object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Create or Update a TLD Extension. - * https://developers.whmcs.com/api-reference/createorupdatetld/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - createOrUpdateTLD(parameters, callback) { - return this.whmcsHttpClient.callApi('CreateOrUpdateTLD', parameters, callback); - }; - - /** - * Obtains the current lock status of the domain. - * https://developers.whmcs.com/api-reference/domaingetlockingstatus/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainGetLockingStatus(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainGetLockingStatus', parameters, callback); - }; - - /** - * Obtains the current nameservers for the domain. - * https://developers.whmcs.com/api-reference/domaingetnameservers/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainGetNameservers(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainGetNameservers', parameters, callback); - }; - - /** - * Obtains the current whois information for the domain. - * https://developers.whmcs.com/api-reference/domaingetwhoisinfo/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainGetWhoisInfo(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainGetWhoisInfo', parameters, callback); - }; - - /** - * Sends the Register command to the registrar for the domain. - * https://developers.whmcs.com/api-reference/domainregister/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainRegister(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainRegister', parameters, callback); - }; - - /** - * Sends the Release command to the registrar for the domain to a new tag. - * https://developers.whmcs.com/api-reference/domainrelease/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainRelease(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainRelease', parameters, callback); - }; - - /** - * Sends the Renew command to the registrar for the domain. - * https://developers.whmcs.com/api-reference/domainrenew/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainRenew(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainRenew', parameters, callback); - }; - - /** - * Sends the Request EPP command to the registrar for the domain. - * https://developers.whmcs.com/api-reference/domainrequestepp/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainRequestEPP(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainRequestEPP', parameters, callback); - }; - - /** - * Sends the Toggle ID Protect command to the registrar for the domain. - * https://developers.whmcs.com/api-reference/domaintoggleidprotect/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainToggleIdProtect(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainToggleIdProtect', parameters, callback); - }; - - /** - * Sends the Transfer command to the registrar for the domain. - * https://developers.whmcs.com/api-reference/domaintransfer/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainTransfer(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainTransfer', parameters, callback); - }; - - /** - * Sends the Update Lock command to the registrar for the domain. - * https://developers.whmcs.com/api-reference/domainupdatelockingstatus/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainUpdateLockingStatus(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainUpdateLockingStatus', parameters, callback); - }; - - /** - * Sends the Save Nameservers command to the registrar for the domain. - * https://developers.whmcs.com/api-reference/domainupdatenameservers/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainUpdateNameservers(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainUpdateNameservers', parameters, callback); - }; - - /** - * Sends the Save Whois command to the registrar for the domain. - * https://developers.whmcs.com/api-reference/domainupdatewhoisinfo/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainUpdateWhoisInfo(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainUpdateWhoisInfo', parameters, callback); - }; - - /** - * Retrieve domain whois information. - * https://developers.whmcs.com/api-reference/domainwhois/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - domainWhois(parameters, callback) { - return this.whmcsHttpClient.callApi('DomainWhois', parameters, callback); - }; - - /** - * Retrieve TLD pricing. - * https://developers.whmcs.com/api-reference/gettldpricing/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getTLDPricing(parameters, callback) { - return this.whmcsHttpClient.callApi('GetTLDPricing', parameters, callback); - }; - - /** - * Updates a Client Domain. - * https://developers.whmcs.com/api-reference/updateclientdomain/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateClientDomain(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateClientDomain', parameters, callback); - }; -} - -module.exports = Domains; diff --git a/modules/index.js b/modules/index.js deleted file mode 100644 index 8047786..0000000 --- a/modules/index.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - Orders: require('./orders.js'), - Billing: require('./billing.js'), - Module: require('./module.js'), - Support: require('./support.js'), - System: require('./system.js'), - Client: require('./client.js'), - Products: require('./products.js'), - ProjectManagement: require('./projectmanagement.js'), - Users: require('./users.js'), - Affiliates: require('./affiliates.js'), - Authentication: require('./authentication.js'), - Domains: require('./domains.js'), - Servers: require('./servers.js'), - Tickets: require('./tickets.js'), - Service: require('./service.js'), - Addons: require('./addons.js') -} diff --git a/modules/module.js b/modules/module.js deleted file mode 100644 index 0c9b72f..0000000 --- a/modules/module.js +++ /dev/null @@ -1,61 +0,0 @@ -class Module { - /** - * Creates a new Module object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Activates a given module. - * https://developers.whmcs.com/api-reference/activatemodule/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - activateModule(parameters, callback) { - return this.whmcsHttpClient.callApi('ActivateModule', parameters, callback); - }; - - /** - * Deactivates a given module. - * https://developers.whmcs.com/api-reference/deactivatemodule/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deactivateModule(parameters, callback) { - return this.whmcsHttpClient.callApi('DeactivateModule', parameters, callback); - }; - - /** - * Obtains the Module Configuration Parameters. - * https://developers.whmcs.com/api-reference/getmoduleconfigurationparameters/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getModuleConfigurationParameters(parameters, callback) { - return this.whmcsHttpClient.callApi('GetModuleConfigurationParameters', parameters, callback); - }; - - /** - * Obtains the Module Queue for Incomplete Failed Actions. - * https://developers.whmcs.com/api-reference/getmodulequeue/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getModuleQueue(parameters, callback) { - return this.whmcsHttpClient.callApi('GetModuleQueue', parameters, callback); - }; - - /** - * Updates a given module. - * https://developers.whmcs.com/api-reference/updatemoduleconfiguration/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateModuleConfiguration(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateModuleConfiguration', parameters, callback); - }; -} - -module.exports = Module; \ No newline at end of file diff --git a/modules/orders.js b/modules/orders.js deleted file mode 100644 index bbd94ba..0000000 --- a/modules/orders.js +++ /dev/null @@ -1,120 +0,0 @@ -class Orders { - /** - * Creates a new Orders object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Accepts a pending order. - * https://developers.whmcs.com/api-reference/acceptorder/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - acceptOrder(parameters, callback) { - return this.whmcsHttpClient.callApi('AcceptOrder', parameters, callback); - }; - - /** - * Adds an order to a client. - * https://developers.whmcs.com/api-reference/addorder/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addOrder(parameters, callback) { - return this.whmcsHttpClient.callApi('AddOrder', parameters, callback); - }; - - /** - * Cancel a Pending Order. - * https://developers.whmcs.com/api-reference/cancelorder/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - cancelOrder(parameters, callback) { - return this.whmcsHttpClient.callApi('CancelOrder', parameters, callback); - }; - - /** - * Deletes a cancelled or fraud order. - * https://developers.whmcs.com/api-reference/deleteorder/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteOrder(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteOrder', parameters, callback); - }; - - /** - * Marks an order as fraudulent. - * https://developers.whmcs.com/api-reference/fraudorder/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - fraudOrder(parameters, callback) { - return this.whmcsHttpClient.callApi('FraudOrder', parameters, callback); - }; - - /** - * Obtain orders matching the passed criteria. - * https://developers.whmcs.com/api-reference/getorders/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getOrders(parameters, callback) { - return this.whmcsHttpClient.callApi('GetOrders', parameters, callback); - }; - - /** - * Retrieve Order Status and number in those statuses. - * https://developers.whmcs.com/api-reference/getorderstatuses/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getOrderStatuses(callback) { - return this.whmcsHttpClient.callApi('GetOrderStatuses', callback); - }; - - /** - * Retrieve configured products matching provided criteria. - * https://developers.whmcs.com/api-reference/getproducts/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getProducts(parameters, callback) { - return this.whmcsHttpClient.callApi('GetProducts', parameters, callback); - }; - - /** - * Obtain promotions matching the passed criteria. - * https://developers.whmcs.com/api-reference/getpromotions/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getPromotions(parameters, callback) { - return this.whmcsHttpClient.callApi('GetPromotions', parameters, callback); - }; - - /** - * Run a fraud check on a passed Order ID using the active fraud module. - * https://developers.whmcs.com/api-reference/orderfraudcheck/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - orderFraudCheck(parameters, callback) { - return this.whmcsHttpClient.callApi('OrderFraudCheck', parameters, callback); - }; - - /** - * Sets an order, and all associated order items to Pending status. - * https://developers.whmcs.com/api-reference/pendingorder/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - pendingOrder(parameters, callback) { - return this.whmcsHttpClient.callApi('PendingOrder', parameters, callback); - }; -} - -module.exports = Orders; \ No newline at end of file diff --git a/modules/products.js b/modules/products.js deleted file mode 100644 index 1bdf16a..0000000 --- a/modules/products.js +++ /dev/null @@ -1,20 +0,0 @@ -class Products { - /** - * Creates a new Products object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Adds a product to the system to be available for purchase. - * https://developers.whmcs.com/api-reference/addproduct/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addProduct(parameters, callback) { - return this.whmcsHttpClient.callApi('AddProduct', parameters, callback); - }; -} -module.exports = Products; diff --git a/modules/projectmanagement.js b/modules/projectmanagement.js deleted file mode 100644 index bf09b93..0000000 --- a/modules/projectmanagement.js +++ /dev/null @@ -1,111 +0,0 @@ -class ProjectManagement { - /** - * Creates a new ProjectManagement object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Adds a Message to a project. - * https://developers.whmcs.com/api-reference/addprojectmessage/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addProjectMessage(parameters, callback) { - return this.whmcsHttpClient.callApi('AddProjectMessage', parameters, callback); - }; - - /** - * Adds a Task to a project. - * https://developers.whmcs.com/api-reference/addprojecttask/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addProjectTask(parameters, callback) { - return this.whmcsHttpClient.callApi('AddProjectTask', parameters, callback); - }; - - /** - * Creates a new project. - * https://developers.whmcs.com/api-reference/createproject/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - createProject(parameters, callback) { - return this.whmcsHttpClient.callApi('CreateProject', parameters, callback); - }; - - /** - * Deletes a task associated with a project. - * https://developers.whmcs.com/api-reference/deleteprojecttask/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteProjectTask(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteProjectTask', parameters, callback); - }; - - /** - * Ends a started timer for a project. - * https://developers.whmcs.com/api-reference/endtasktimer/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - endTaskTimer(parameters, callback) { - return this.whmcsHttpClient.callApi('EndTaskTimer', parameters, callback); - }; - - /** - * Retrieve a specific Project. - * https://developers.whmcs.com/api-reference/getproject/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getProject(parameters, callback) { - return this.whmcsHttpClient.callApi('GetProject', parameters, callback); - }; - - /** - * Obtain orders matching the passed criteria. - * https://developers.whmcs.com/api-reference/getprojects/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getProjects(parameters, callback) { - return this.whmcsHttpClient.callApi('GetProjects', parameters, callback); - }; - - /** - * Starts a timer for a project. - * https://developers.whmcs.com/api-reference/starttasktimer/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - startTaskTimer(parameters, callback) { - return this.whmcsHttpClient.callApi('StartTaskTimer', parameters, callback); - }; - - /** - * Updates a project. - * https://developers.whmcs.com/api-reference/updateproject/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateProject(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateProject', parameters, callback); - }; - - /** - * Adds a Task to a project. - * https://developers.whmcs.com/api-reference/updateprojecttask/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateProjectTask(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateProjectTask', parameters, callback); - }; -} - -module.exports = ProjectManagement; diff --git a/modules/servers.js b/modules/servers.js deleted file mode 100644 index 85b1cdb..0000000 --- a/modules/servers.js +++ /dev/null @@ -1,31 +0,0 @@ -class Servers { - /** - * Creates a new Servers object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Get health status. - * https://developers.whmcs.com/api-reference/gethealthstatus/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getHealthStatus(parameters, callback) { - return this.whmcsHttpClient.callApi('GetHealthStatus', parameters, callback); - }; - - /** - * Get servers. - * https://developers.whmcs.com/api-reference/getservers/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getServers(parameters, callback) { - return this.whmcsHttpClient.callApi('GetServers', parameters, callback); - }; -} - -module.exports = Servers; diff --git a/modules/service.js b/modules/service.js deleted file mode 100644 index e9bedba..0000000 --- a/modules/service.js +++ /dev/null @@ -1,101 +0,0 @@ -class Service { - /** - * Creates a new Service object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Runs a change package action for a given service. - * https://developers.whmcs.com/api-reference/modulechangepackage/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - moduleChangePackage(parameters, callback) { - return this.whmcsHttpClient.callApi('ModuleChangePackage', parameters, callback); - }; - - /** - * Runs a change password action for a given service. - * https://developers.whmcs.com/api-reference/modulechangepw/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - moduleChangePw(parameters, callback) { - return this.whmcsHttpClient.callApi('ModuleChangePw', parameters, callback); - }; - - /** - * Runs the module create action for a given service. - * https://developers.whmcs.com/api-reference/modulecreate/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - moduleCreate(parameters, callback) { - return this.whmcsHttpClient.callApi('ModuleCreate', parameters, callback); - }; - - /** - * Runs a custom module action for a given service. - * https://developers.whmcs.com/api-reference/modulecustom/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - moduleCustom(parameters, callback) { - return this.whmcsHttpClient.callApi('ModuleCustom', parameters, callback); - }; - - /** - * Runs the module suspend action for a given service. - * https://developers.whmcs.com/api-reference/modulesuspend/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - moduleSuspend(parameters, callback) { - return this.whmcsHttpClient.callApi('ModuleSuspend', parameters, callback); - }; - - /** - * Runs a terminate action for a given service. - * https://developers.whmcs.com/api-reference/moduleterminate/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - moduleTerminate(parameters, callback) { - return this.whmcsHttpClient.callApi('ModuleTerminate', parameters, callback); - }; - - /** - * Runs an unsuspend action for a given service. - * https://developers.whmcs.com/api-reference/moduleunsuspend/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - moduleUnsuspend(parameters, callback) { - return this.whmcsHttpClient.callApi('ModuleUnsuspend', parameters, callback); - }; - - /** - * Updates a Client Service. - * https://developers.whmcs.com/api-reference/updateclientproduct/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateClientProduct(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateClientProduct', parameters, callback); - }; - - /** - * Upgrade, or calculate an upgrade on, a product. - * https://developers.whmcs.com/api-reference/upgradeproduct/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - upgradeProduct(parameters, callback) { - return this.whmcsHttpClient.callApi('UpgradeProduct', parameters, callback); - }; -} - -module.exports = Service; diff --git a/modules/support.js b/modules/support.js deleted file mode 100644 index 7a90d49..0000000 --- a/modules/support.js +++ /dev/null @@ -1,161 +0,0 @@ -class Support { - /** - * Creates a new Support object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Adds an announcement. - * https://developers.whmcs.com/api-reference/addannouncement/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addAnnouncement(parameters, callback) { - return this.whmcsHttpClient.callApi('AddAnnouncement', parameters, callback); - }; - - /** - * Adds a Cancellation Request. - * https://developers.whmcs.com/api-reference/addcancelrequest/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addCancelRequest(parameters, callback) { - return this.whmcsHttpClient.callApi('AddCancelRequest', parameters, callback); - }; - - /** - * Adds a Client Note. - * https://developers.whmcs.com/api-reference/addclientnote/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addClientNote(parameters, callback) { - return this.whmcsHttpClient.callApi('AddClientNote', parameters, callback); - }; - - /** - * Add a note to a ticket by Ticket ID or Ticket Number. - * https://developers.whmcs.com/api-reference/addticketnote/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addTicketNote(parameters, callback) { - return this.whmcsHttpClient.callApi('AddTicketNote', parameters, callback); - }; - - /** - * Add a reply to a ticket by Ticket ID. - * https://developers.whmcs.com/api-reference/addticketreply/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addTicketReply(parameters, callback) { - return this.whmcsHttpClient.callApi('AddTicketReply', parameters, callback); - }; - - /** - * Blocks a ticket sender. - * https://developers.whmcs.com/api-reference/blockticketsender/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - blockTicketSender(parameters, callback) { - return this.whmcsHttpClient.callApi('BlockTicketSender', parameters, callback); - }; - - /** - * Delete an announcement. - * https://developers.whmcs.com/api-reference/deleteannouncement/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteAnnouncement(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteAnnouncement', parameters, callback); - }; - - /** - * Deletes a ticket. - * https://developers.whmcs.com/api-reference/deleteticket/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteTicket(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteTicket', parameters, callback); - }; - - /** - * Deletes a ticket note. - * https://developers.whmcs.com/api-reference/deleteticketnote/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteTicketNote(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteTicketNote', parameters, callback); - }; - - /** - * Deletes a ticket reply. - * https://developers.whmcs.com/api-reference/deleteticketreply/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteTicketReply(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteTicketReply', parameters, callback); - }; - - /** - * Obtain an array of announcements. - * https://developers.whmcs.com/api-reference/getannouncements/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getAnnouncements(parameters, callback) { - return this.whmcsHttpClient.callApi('GetAnnouncements', parameters, callback); - }; - - /** - * Merge tickets. - * https://developers.whmcs.com/api-reference/mergeticket/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - mergeTicket(parameters, callback) { - return this.whmcsHttpClient.callApi('MergeTicket', parameters, callback); - }; - - /** - * Open a new ticket. - * https://developers.whmcs.com/api-reference/openticket/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - openTicket(parameters, callback) { - return this.whmcsHttpClient.callApi('OpenTicket', parameters, callback); - }; - - /** - * Updates an existing ticket. - * https://developers.whmcs.com/api-reference/updateticket/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateTicket(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateTicket', parameters, callback); - }; - - /** - * Updates a ticket reply message. - * https://developers.whmcs.com/api-reference/updateticketreply/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateTicketReply(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateTicketReply', parameters, callback); - }; -} - -module.exports = Support; diff --git a/modules/system.js b/modules/system.js deleted file mode 100644 index 98d517a..0000000 --- a/modules/system.js +++ /dev/null @@ -1,245 +0,0 @@ -class System { - /** - * Creates a new System object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Adds an IP to the ban list. - * https://developers.whmcs.com/api-reference/addbannedip/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addBannedIp(parameters, callback) { - return this.whmcsHttpClient.callApi('AddBannedIp', parameters, callback); - }; - - /** - * Decrypt an encrypted string. - * https://developers.whmcs.com/api-reference/decryptpassword/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - decryptPassword(parameters, callback) { - return this.whmcsHttpClient.callApi('DecryptPassword', parameters, callback); - }; - - /** - * Encrypt a string. - * https://developers.whmcs.com/api-reference/encryptpassword/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - encryptPassword(parameters, callback) { - return this.whmcsHttpClient.callApi('EncryptPassword', parameters, callback); - }; - - /** - * Obtain the Activity Log that matches passed criteria. - * https://developers.whmcs.com/api-reference/getactivitylog/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getActivityLog(parameters, callback) { - return this.whmcsHttpClient.callApi('GetActivityLog', parameters, callback); - }; - - /** - * Obtain the details for the current Admin User. - * https://developers.whmcs.com/api-reference/getadmindetails/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getAdminDetails(callback) { - return this.whmcsHttpClient.callApi('GetAdminDetails', callback); - }; - - /** - * Retrieve a list of administrator user accounts. - * https://developers.whmcs.com/api-reference/getadminusers/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getAdminUsers(parameters, callback) { - return this.whmcsHttpClient.callApi('GetAdminUsers', parameters, callback); - }; - - /** - * Get Automation Task Log. - * https://developers.whmcs.com/api-reference/getautomationlog/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getAutomationLog(parameters, callback) { - return this.whmcsHttpClient.callApi('GetAutomationLog', parameters, callback); - }; - - /** - * Retrieve a System Configuration Value. - * https://developers.whmcs.com/api-reference/getconfigurationvalue/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getConfigurationValue(parameters, callback) { - return this.whmcsHttpClient.callApi('GetConfigurationValue', parameters, callback); - }; - - /** - * Obtain the Currencies configured in the System. - * https://developers.whmcs.com/api-reference/getcurrencies/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getCurrencies(callback) { - return this.whmcsHttpClient.callApi('GetCurrencies', callback); - }; - - /** - * Obtain a list of email templates from the system. - * https://developers.whmcs.com/api-reference/getemailtemplates/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getEmailTemplates(parameters, callback) { - return this.whmcsHttpClient.callApi('GetEmailTemplates', parameters, callback); - }; - - /** - * Retrieve Activated Payment Methods. - * https://developers.whmcs.com/api-reference/getpaymentmethods/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getPaymentMethods(callback) { - return this.whmcsHttpClient.callApi('GetPaymentMethods', callback); - }; - - /** - * Retrieve a list of currently logged in admin users. - * https://developers.whmcs.com/api-reference/getstaffonline/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getStaffOnline(callback) { - return this.whmcsHttpClient.callApi('GetStaffOnline', callback); - }; - - /** - * Get business performance metrics and statistics. - * https://developers.whmcs.com/api-reference/getstats/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getStats(parameters, callback) { - return this.whmcsHttpClient.callApi('GetStats', parameters, callback); - }; - - /** - * Get To-Do List Items. - * https://developers.whmcs.com/api-reference/gettodoitems/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getToDoItems(parameters, callback) { - return this.whmcsHttpClient.callApi('GetToDoItems', parameters, callback); - }; - - /** - * Obtain To Do item statuses and counts. - * https://developers.whmcs.com/api-reference/gettodoitemstatuses/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getToDoItemStatuses(callback) { - return this.whmcsHttpClient.callApi('GetToDoItemStatuses', callback); - }; - - /** - * Creates an activity log entry. - * https://developers.whmcs.com/api-reference/logactivity/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - logActivity(parameters, callback) { - return this.whmcsHttpClient.callApi('LogActivity', parameters, callback); - }; - - /** - * Send an Admin Email Notification. - * https://developers.whmcs.com/api-reference/sendadminemail/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - sendAdminEmail(parameters, callback) { - return this.whmcsHttpClient.callApi('SendAdminEmail', parameters, callback); - }; - - /** - * Send a client Email Notification. - * https://developers.whmcs.com/api-reference/sendemail/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - sendEmail(parameters, callback) { - return this.whmcsHttpClient.callApi('SendEmail', parameters, callback); - }; - - /** - * Set a System Configuration Value via the local API only. - * https://developers.whmcs.com/api-reference/setconfigurationvalue/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - setConfigurationValue(parameters, callback) { - return this.whmcsHttpClient.callApi('SetConfigurationValue', parameters, callback); - }; - - /** - * Trigger a Custom Notification Event. - * https://developers.whmcs.com/api-reference/triggernotificationevent/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - triggerNotificationEvent(parameters, callback) { - return this.whmcsHttpClient.callApi('TriggerNotificationEvent', parameters, callback); - }; - - /** - * Update the admin notes. - * https://developers.whmcs.com/api-reference/updateadminnotes/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateAdminNotes(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateAdminNotes', parameters, callback); - }; - - /** - * Update a specific announcement. - * https://developers.whmcs.com/api-reference/updateannouncement/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateAnnouncement(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateAnnouncement', parameters, callback); - }; - - /** - * Update To-Do Item. - * https://developers.whmcs.com/api-reference/updatetodoitem/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateToDoItem(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateToDoItem', parameters, callback); - }; - - /** - * Obtain details pertaining to the current WHMCS installation. - * https://developers.whmcs.com/api-reference/whmcsdetails/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - whmcsDetails(callback) { - return this.whmcsHttpClient.callApi('WhmcsDetails', callback); - }; -} - -module.exports = System; \ No newline at end of file diff --git a/modules/tickets.js b/modules/tickets.js deleted file mode 100644 index 1bcb1e6..0000000 --- a/modules/tickets.js +++ /dev/null @@ -1,100 +0,0 @@ -class Tickets { - /** - * Creates a new Tickets object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Get the support departments and associated ticket counts. - * https://developers.whmcs.com/api-reference/getsupportdepartments/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getSupportDepartments(parameters, callback) { - return this.whmcsHttpClient.callApi('GetSupportDepartments', parameters, callback); - }; - - /** - * Get the support statuses and number of tickets in each status. - * https://developers.whmcs.com/api-reference/getsupportstatuses/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getSupportStatuses(parameters, callback) { - return this.whmcsHttpClient.callApi('GetSupportStatuses', parameters, callback); - }; - - /** - * Obtain a specific ticket - * https://developers.whmcs.com/api-reference/getticket/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getTicket(parameters, callback) { - return this.whmcsHttpClient.callApi('GetTicket', parameters, callback); - }; - - /** - * Retrieve a single attachment. - * https://developers.whmcs.com/api-reference/getticketattachment/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getTicketAttachment(parameters, callback) { - return this.whmcsHttpClient.callApi('GetTicketAttachment', parameters, callback); - }; - - /** - * Get ticket counts. - * https://developers.whmcs.com/api-reference/getticketcounts/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getTicketCounts(parameters, callback) { - return this.whmcsHttpClient.callApi('GetTicketCounts', parameters, callback); - }; - - /** - * Obtain a specific ticket notes. - * https://developers.whmcs.com/api-reference/getticketnotes/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getTicketNotes(parameters, callback) { - return this.whmcsHttpClient.callApi('GetTicketNotes', parameters, callback); - }; - - /** - * Obtain the Predefined Ticket Reply Categories. - * https://developers.whmcs.com/api-reference/getticketpredefinedcats/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getTicketPredefinedCats(callback) { - return this.whmcsHttpClient.callApi('GetTicketPredefinedCats', callback); - }; - - /** - * Obtain the Predefined Ticket Replies. - * https://developers.whmcs.com/api-reference/getticketpredefinedreplies/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getTicketPredefinedReplies(parameters, callback) { - return this.whmcsHttpClient.callApi('GetTicketPredefinedReplies', parameters, callback); - }; - - /** - * Obtain tickets matching the passed criteria. - * https://developers.whmcs.com/api-reference/gettickets/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getTickets(parameters, callback) { - return this.whmcsHttpClient.callApi('GetTickets', parameters, callback); - }; -} - -module.exports = Tickets; diff --git a/modules/users.js b/modules/users.js deleted file mode 100644 index 04ae34d..0000000 --- a/modules/users.js +++ /dev/null @@ -1,100 +0,0 @@ -class Users { - /** - * Creates a new Users object - * @param {WhmcsHttpClient} whmcsHttpClient - */ - constructor(whmcsHttpClient) { - this.whmcsHttpClient = whmcsHttpClient; - } - - /** - * Add a user. - * https://developers.whmcs.com/api-reference/adduser/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - addUser(parameters, callback) { - return this.whmcsHttpClient.callApi('AddUser', parameters, callback); - }; - - /** - * Send an invite to manage a client. - * https://developers.whmcs.com/api-reference/createclientinvite/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - createClientInvite(parameters, callback) { - return this.whmcsHttpClient.callApi('CreateClientInvite', parameters, callback); - }; - - /** - * Delete relationship between user and client. - * https://developers.whmcs.com/api-reference/deleteuserclient/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - deleteUserClient(parameters, callback) { - return this.whmcsHttpClient.callApi('DeleteUserClient', parameters, callback); - }; - - /** - * Retrieve a list of permissions that can be used when creating a user. - * https://developers.whmcs.com/api-reference/getpermissionslist/ - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getPermissionsList(callback) { - return this.whmcsHttpClient.callApi('GetPermissionsList', callback); - }; - - /** - * Provide the permissions of a user for a client. - * https://developers.whmcs.com/api-reference/getuserpermissions/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getUserPermissions(parameters, callback) { - return this.whmcsHttpClient.callApi('GetUserPermissions', parameters, callback); - }; - - /** - * Obtain the Users that match passed criteria. - * https://developers.whmcs.com/api-reference/getusers/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - getUsers(parameters, callback) { - return this.whmcsHttpClient.callApi('GetUsers', parameters, callback); - }; - - /** - * Starts the password reset process for a user. - * https://developers.whmcs.com/api-reference/resetpassword/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - resetPassword(parameters, callback) { - return this.whmcsHttpClient.callApi('ResetPassword', parameters, callback); - }; - - /** - * Update a user. - * https://developers.whmcs.com/api-reference/updateuser/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateUser(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateUser', parameters, callback); - }; - - /** - * Update the permissions of a user for a client. - * https://developers.whmcs.com/api-reference/updateuserpermissions/ - * @param {Object} parameters Request parameters - * @param {Function} callback Optional callback. If not set the method returns a Promise - */ - updateUserPermissions(parameters, callback) { - return this.whmcsHttpClient.callApi('UpdateUserPermissions', parameters, callback); - }; -} - -module.exports = Users; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 969fe36..0000000 --- a/package-lock.json +++ /dev/null @@ -1,2341 +0,0 @@ -{ - "name": "whmcs", - "version": "1.2.2", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "whmcs", - "version": "1.2.2", - "dependencies": { - "axios": "^0.21.1", - "bluebird": "^3.7.2", - "https-proxy-agent": "^5.0.0", - "xml2js": "0.4.x" - }, - "devDependencies": { - "chai": "~4.2.0", - "mocha": "^7.1.0" - }, - "engines": { - "node": ">= 12.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", - "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", - "dependencies": { - "follow-redirects": "^1.10.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.1.1" - } - }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/es-abstract": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", - "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", - "dependencies": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.0", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", - "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.0.0", - "string.prototype.trimright": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "dependencies": { - "is-buffer": "~2.0.3" - }, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dependencies": { - "has": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dependencies": { - "has-symbols": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "node_modules/mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", - "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==", - "dev": true, - "dependencies": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.3", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "dependencies": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dependencies": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "dependencies": { - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", - "dependencies": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", - "dependencies": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dependencies": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/xml2js": { - "version": "0.4.22", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz", - "integrity": "sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==", - "dependencies": { - "sax": ">=0.6.0", - "util.promisify": "~1.0.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, - "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "dependencies": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - } - }, - "dependencies": { - "agent-base": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", - "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", - "requires": { - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", - "requires": { - "follow-redirects": "^1.10.0" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "es-abstract": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", - "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.0", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", - "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.0.0", - "string.prototype.trimright": "^2.0.0" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - } - }, - "follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "requires": { - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "requires": { - "has": "^1.0.1" - } - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "requires": { - "has-symbols": "^1.0.0" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mocha": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", - "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==", - "dev": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.3", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xml2js": { - "version": "0.4.22", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz", - "integrity": "sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==", - "requires": { - "sax": ">=0.6.0", - "util.promisify": "~1.0.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" - }, - "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index a2be5a7..0000000 --- a/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "whmcs", - "description": "WHMCS API client.", - "version": "1.2.2", - "author": "Pedro Dias ", - "maintainers": [ - "apocas " - ], - "repository": { - "type": "git", - "url": "http://github.com/apocas/node-whmcs.git" - }, - "keywords": [ - "whmcs", - "hosting" - ], - "dependencies": { - "axios": "^0.21.1", - "bluebird": "^3.7.2", - "https-proxy-agent": "^5.0.0", - "xml2js": "0.4.x" - }, - "devDependencies": { - "chai": "~4.2.0", - "mocha": "^7.1.0" - }, - "scripts": { - "test": "mocha --timeout 10000" - }, - "main": "./whmcs", - "engines": { - "node": ">= 12.0" - } -} diff --git a/rake/list_routes.rb b/rake/list_routes.rb new file mode 100644 index 0000000..e7f34b7 --- /dev/null +++ b/rake/list_routes.rb @@ -0,0 +1,12 @@ +# desc 'List defined routes' +# task :routes do +# require '../app' + +# Shoebox::Server.routes.map do |method, routes| +# routes.map { |r| r.first.to_s }.map do |route| +# "#{method.rjust(7, ' ')} #{route}" +# end +# end.flatten.sort.each do |route| +# puts route +# end +# end \ No newline at end of file diff --git a/redis.sh b/redis.sh new file mode 100644 index 0000000..e348a3f --- /dev/null +++ b/redis.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +REDIS_CONFIG=' +bind 0.0.0.0 +port 6379 +cluster-enabled yes +cluster-config-file nodes.conf +cluster-node-timeout 5000 +requirepass E8OPi07dPfY! +appendonly yes' + +network=redis-net + +docker network create --driver overlay --attachable $network +# docker #1 +sleep 2 +docker service create --name redis \ + --network $network \ + --replicas=6 \ + --constraint node.labels.type==bare-metal \ + -e REDIS_CONFIG="$REDIS_CONFIG" \ + -e REDIS_CONFIG_FILE="/usr/local/etc/redis/redis.conf" \ + redis:latest sh -c 'mkdir -p $(dirname $REDIS_CONFIG_FILE) && echo "$REDIS_CONFIG" > $REDIS_CONFIG_FILE && cat $REDIS_CONFIG_FILE && redis-server $REDIS_CONFIG_FILE' + + +sleep 2 +docker service ps redis --no-trunc + +# run the redis-trib.rb script (the docker inspect runs on the host and the echo output is passed the along to the ruby container) +# docker run -it --rm --net redis-net ruby sh -c "\ +# gem install redis --version 3.2 \ +# && wget http://download.redis.io/redis-stable/src/redis-trib.rb \ +# && ruby redis-trib.rb create --replicas 1 \ +# \$(getent hosts tasks.redis | awk '{print \$1 \":6379\"}') " + +# this is WORKING: +# +# get the IP address of each redis container +# +# docker network inspect redis-net | grep -i -E "name|ipv4address" +# enter any of redis container +# +# docker exec -it redis.1.ftk1dswtspr68vn5okz7psiwb bash +# redis-cli -a E8OPi07dPfY! --cluster create 10.0.7.19:6379 10.0.7.14:6379 10.0.7.15:6379 10.0.7.16:6379 10.0.7.17:6379 10.0.7.18:6379 --cluster-replicas 1 + + +# test the redis cluster +# docker exec -it redis.1 /bin/sh +# redis-cli -a E8OPi07dPfY! --cluster info +# docker run -it --rm --net redis_network redis redis-cli -c -h redis -p 6379 \ No newline at end of file diff --git a/route/api/addons.rb b/route/api/addons.rb new file mode 100644 index 0000000..dc3fafe --- /dev/null +++ b/route/api/addons.rb @@ -0,0 +1,19 @@ +namespace "/api/v1/addons" do + # UpdateClientAddon + get "/update" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpdateClientAddon", { :id => payload[:id].to_i, + :status => payload[:status], + :terminationDate => payload[:terminationDate], + :addonid => payload[:addonid], + :name => payload[:name], + :setupfee => payload[:setupfee], + :recurring => payload[:recurring], + :billingcycle => payload[:billingcycle], + :nextduedate => payload[:nextduedate], + :notes => payload[:notes], + :autorecalc => payload[:autorecalc] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end +end diff --git a/route/api/affiliates.rb b/route/api/affiliates.rb new file mode 100644 index 0000000..9128d45 --- /dev/null +++ b/route/api/affiliates.rb @@ -0,0 +1,28 @@ +namespace "/api/v1" do + # GetAffiliates + get "/affiliate" do + logger.info "Get affiliate" + + response = Whmcs.call("GetAffiliates", { :limitstart => params[:limitstart].to_i, + :limitnum => params[:limitnum].to_i, + :userid => params[:userid].to_i, + :visitors => params[:visitors].to_i, + :paytype => params[:paytype], + :payamount => params[:payamount].to_f, + :onetime => params[:onetime].to_i, + :balance => params[:balance].to_f, + :withdrawn => params[:withdrawn].to_f }) + logger.info "[#{response[:result].upcase}] loading data" + + return json(response) + end + # AffiliateActivate + get "/affiliate/activate" do + logger.info "Activate affiliate" + + response = Whmcs.call("AffiliateActivate", { :userid => params[:limitstart] }) + logger.info "[#{response[:result].upcase}] loading data" + + return json(response) + end +end diff --git a/route/api/authentication.rb b/route/api/authentication.rb new file mode 100644 index 0000000..07e7ed3 --- /dev/null +++ b/route/api/authentication.rb @@ -0,0 +1,110 @@ +namespace "/api/v1" do + # ListOAuthCredentials + get "/oauth" do + # set default parmas + response = Whmcs.call("ListOAuthCredentials", { :grantType => params[:grantType].to_s, + :sortField => params[:sortField].to_s, + :sortOrder => params[:sortOrder] || "DESC", # ASC or DESC + :limit => params[:limit].to_i }) + logger.info "[#{response[:result].upcase}]" + + return json(response) + end + + # CreateOAuthCredential + post "/oauth/create" do + payload = JSON.parse(request.body.read.to_s) + + response = Whmcs.call("CreateOAuthCredential", { :grantType => payload[:grantType].to_s, + :scope => payload[:scope].to_s, + :name => payload[:name].to_s, + :serviceId => payload[:serviceId].to_i, + :description => payload[:description].to_s, + :logoUri => payload[:logoUri].to_s, + :redirectUri => payload[:redirectUri].to_s }) + + return json(response) + end + + # UpdateOAuthCredential + post "/oauth/update" do + payload = JSON.parse(request.body.read.to_s) + + response = Whmcs.call("UpdateOAuthCredential", { :credentialId => payload[:credentialId].to_i, + :clientApiIdentifier => payload[:clientApiIdentifier].to_s, + :name => payload[:name].to_s, + :description => payload[:description].to_s, + :grantType => payload[:grantType].to_s, + :scope => payload[:scope].to_s, + :serviceId => payload[:serviceId].to_i, + :logoUri => payload[:logoUri], + :redirectUri => payload[:redirectUri].to_s, + :resetSecret => payload[:resetSecret] }) + + return json(response) + end + # DeleteOAuthCredential + post "/oauth/delete" do + payload = JSON.parse(request.body.read.to_s) + + response = Whmcs.call("DeleteOAuthCredential", { :credentialId => payload[:credentialId].to_i }) + + return json(response) + end + + # CreateSsoToken + post "/sso/create" do + payload = JSON.parse(request.body.read.to_s) + + response = Whmcs.call("CreateSsoToken", { :client_id => payload[:client_id].to_i, + :user_id => payload[:user_id].to_i, + :destination => payload[:destination].to_s, + :service_id => payload[:service_id].to_i, + :domain_id => payload[:domain_id].to_i, + :sso_redirect_path => payload[:sso_redirect_path].to_s }) + + return json(response) + end + # ValidateLogin + # + post "/validatelogin" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Login #{payload}" + response = Whmcs.login(payload["email"], payload["password"]) + session[:userid] = response[:userid] + session[:passwordhash] = response[:passwordhash] + + if response[:result] == "success" && response[:passwordhash].present? + # jwt authentication + # headers = { + # exp: Time.now.to_i + 60, #expire in 60 seconds + # } + # @token = JWT.encode({ user_id: response[:userid] }, settings.signing_key, "RS256", headers) + # @token = JWT.encode({ :user_id => response[:userid], :passwordhash => response[:passwordhash] }, settings.signing_key, "HS256", headers) + response[:access_token] = @token + logger.info "Login #{response} " + + return json(response) + else + return { "error": "failed to login" } + end + + # def token(user) + # JWT.encode payload(user), ENV["JWT_SECRET"], "HS256" + # end + + # def payload(user) + # { + # exp: Time.now.to_i + 60 * 60, + # iat: Time.now.to_i, + # iss: ENV["JWT_ISSUER"], + # user: { + # userid: userid, + # passwordhash: passwordhash, + # }, + # } + # end + end +end diff --git a/route/api/billing.rb b/route/api/billing.rb new file mode 100644 index 0000000..9dceb22 --- /dev/null +++ b/route/api/billing.rb @@ -0,0 +1,380 @@ +namespace "/api/v1/billing" do + # AcceptQuote + get "/quote/accept" do + response = Whmcs.call("AcceptQuote", { :quoteid => params[:quoteid].to_i }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # AddBillableItem + post "/billableitem/add" do + payload = JSON.parse(request.body.read.to_s) + + logger.info "Add Billable Item: #{payload}" + response = Whmcs.call("AddBillableItem", { :clientid => payload[:clientid].to_i, + :description => payload[:description].to_s, + :amount => payload[:amount].to_f, + :quantity => payload[:quantity].to_f, + :unit => payload[:unit].to_s, + :invoiceaction => payload[:invoiceaction].to_s, # One of ‘noinvoice’, ‘nextcron’, ‘nextinvoice’, ‘duedate’, or ‘recur’. + :recur => payload[:recur].to_i, + :recurcycle => payload[:recurcycle].to_s, + :recurfor => payload[:recurfor].to_i, + :duedate => payload[:duedate].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # AddCredit + post "/credit/add" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("AddCredit", { :clientid => payload[:clientid].to_i, + :description => payload[:description].to_s, + :amount => payload[:amount].to_f, + :date => payload[:date].to_s, + :adminid => payload[:adminid].to_i, + :type => payload[:type].to_s }) + + logger.info "Credit Added: #{response}" + return json(response) + end + + # AddInvoicePayment + post "/invoice/pay" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("AddInvoicePayment", { :invoiceid => payload[:invoiceid].to_i, + :transid => payload[:transid].to_s, + :gateway => payload[:gateway].to_s, + :date => payload[:date].to_s, + :amount => payload[:amount].to_f, + :fees => payload[:fees].to_f, + :noemail => payload[:noemail] }) + + logger.info "Invoice paid: #{response}" + return json(response) + end + + # AddPayMethod + post "/payment/add" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("AddPayMethod", { :clientid => payload[:clientid].to_i, + :type => payload[:type].to_s, + :description => payload[:description].to_s, + :gateway_module_name => payload[:gateway_module_name].to_s, + :card_number => payload[:card_number].to_s, + :card_expiry => payload[:card_expiry].to_s, + :card_start => payload[:card_start].to_s, + :card_issue_number => payload[:card_issue_number].to_i, + :bank_name => payload[:bank_name].to_s, + :bank_account_type => payload[:bank_account_type].to_s, + :bank_code => payload[:bank_code].to_s, + :bank_account => payload[:bank_account].to_s, + :set_as_default => payload[:set_as_default] }) + + logger.info "Payment gateway added: #{response}" + return json(response) + end + + # UpdatePayMethod + post "/payment/update" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpdatePayMethod", { :clientid => payload[:clientid].to_i, + :paymethodid => payload[:paymethodid].to_i, + :card_number => payload[:card_number].to_s, + :card_expiry => payload[:card_expiry].to_s, + :card_start => payload[:card_start].to_s, + :card_issue_number => payload[:card_issue_number].to_i, + :bank_name => payload[:bank_name].to_s, + :bank_account_type => payload[:bank_account_type].to_s, + :bank_code => payload[:bank_code].to_s, + :bank_account => payload[:bank_account].to_s, + :set_as_default => payload[:set_as_default] }) + + logger.info "Payment gateway updated: #{response}" + return json(response) + end + + # GetTransactions + get "/transactions" do + response = Whmcs.call("GetTransactions", { :invoiceid => params[:invoiceid].to_i, + :clientid => params[:clientid].to_i, + :transid => params[:transid].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # AddTransaction + post "/transaction/add" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("AddTransaction", { :paymentmethod => payload[:paymentmethod].to_s, + :userid => payload[:userid].to_i, + :invoiceid => payload[:invoiceid].to_i, + :transid => payload[:transid].to_s, + :date => payload[:date].to_s, + :currencyid => payload[:currencyid].to_i, + :description => payload[:description].to_s, + :amountin => payload[:amountin].to_f, + :fees => payload[:fees].to_f, + :amountout => payload[:amountout].to_f, + :rate => payload[:rate].to_f, + :credit => payload[:credit], + :allowduplicatetransid => payload[:allowduplicatetransid] }) + + logger.info "Payment transaction added: #{response}" + return json(response) + end + + # UpdateTransaction + post "/transaction/update" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpdateTransaction", { :transactionid => payload[:transactionid].to_i, + :refundid => payload[:refundid].to_i, + :userid => payload[:userid].to_i, + :invoiceid => payload[:invoiceid].to_i, + :transid => payload[:transid].to_s, + :date => payload[:date].to_s, + :gateway => payload[:gateway].to_s, + :currency => payload[:currency].to_i, + :description => payload[:description].to_s, + :amountin => payload[:amountin].to_f, + :fees => payload[:fees].to_f, + :amountout => payload[:amountout].to_f, + :rate => payload[:rate].to_f, + :credit => payload[:credit] }) + + logger.info "Payment transaction updated: #{response}" + return json(response) + end + + # GetCredits + get "/credits" do + response = Whmcs.call("GetCredits", { :clientid => params[:clientid].to_i }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # ApplyCredit + post "/credit/add" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("ApplyCredit", { :invoiceid => payload[:invoiceid].to_i, + :amount => payload[:amount].to_f, + :noemail => payload[:noemail] }) + + logger.info "Account credit added: #{response}" + return json(response) + end + + # CapturePayment + post "/payment/capture" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("CapturePayment", { :invoiceid => payload[:invoiceid].to_i, + :cvv => payload[:cvv] }) + + logger.info "Capture Payment: #{response}" + return json(response) + end + + # GetPayMethods + # + # Obtain the Pay Methods associated with a provided client id. + get "/payments" do + response = Whmcs.call("GetPayMethods", { :clientid => params[:clientid].to_i, + :paymethodid => params[:paymethodid].to_i, + :type => params[:type].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # DeletePayMethod + post "/payment/delete" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("DeletePayMethod", { :clientid => payload[:clientid].to_i, + :paymethodid => payload[:paymethodid].to_i, + :failonremotefailure => payload[:failonremotefailure] }) + + logger.info "Payment Deleted: #{response}" + return json(response) + end + + # GetInvoice + get "/invoice" do + response = Whmcs.call("GetInvoice", { :invoiceid => params[:invoiceid].to_i }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # GetInvoices + get "/invoices" do + response = Whmcs.call("GetInvoice", { :limitstart => params[:limitstart].to_i || 0, + :limitnum => params[:limitnum].to_i || 25, + :userid => params[:userid].to_i, + :status => params[:status].to_s, + :orderby => params[:orderby].to_s || "id", + :order => params[:order].to_s || "DESC" }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # CreateInvoice + post "/invoice/create" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("CreateInvoice", { :userid => payload[:userid].to_i, + :status => payload[:status].to_s, + :draft => payload[:draft], + :sendinvoice => payload[:sendinvoice], + :paymentmethod => payload[:paymentmethod].to_s, + :taxrate => payload[:taxrate].to_f, + :taxrate2 => payload[:taxrate2].to_f, + :date => payload[:date].to_s, + :duedate => payload[:duedate].to_s, + :notes => payload[:notes].to_s, + :itemdescriptionx => payload[:itemdescriptionx].to_s, + :itemamountx => payload[:itemamountx].to_f, + :itemtaxedx => payload[:itemtaxedx], + :autoapplycredit => payload[:autoapplycredit] }) + + logger.info "Invoice Created: #{response}" + return json(response) + end + + # GenInvoices + post "/invoice/generate" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("GenInvoices", { :noemails => payload[:noemails], + :clientid => payload[:clientid].to_i, + :serviceids => payload[:serviceids].to_a, + :domainids => payload[:domainids].to_a, + :addonids => payload[:addonids].to_a }) + + logger.info "Invoice Generated: #{response}" + return json(response) + end + + # UpdateInvoice + post "/invoice/update" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpdateInvoice", { :invoiceid => payload[:invoiceid].to_i, + :status => payload[:status].to_s, + :paymentmethod => payload[:paymentmethod].to_s, + :taxrate => payload[:taxrate].to_f, + :taxrate2 => payload[:taxrate2].to_f, + :credit => payload[:credit].to_f, + :date => payload[:date].to_s, + :duedate => payload[:duedate].to_s, + :datepaid => payload[:datepaid].to_s, + :notes => payload[:notes].to_s, + :itemdescriptionx => payload[:itemdescriptionx], + :itemamount => payload[:itemamount], + :itemtaxed => payload[:itemtaxed], + :newitemdescription => payload[:newitemdescription], + :newitemamount => payload[:newitemamount], + :newitemtaxed => payload[:newitemtaxed], + :deletelineids => payload[:deletelineids], + :publish => payload[:publish], + :publishandsendemail => payload[:publishandsendemail] }) + + logger.info "Invoice Created: #{response}" + return json(response) + end + + # GetQuotes + get "/quotes" do + response = Whmcs.call("GetQuotes", { :limitstart => params[:limitstart].to_i || 0, + :limitnum => params[:limitnum].to_i || 25, + :quoteid => params[:quoteid].to_i, + :userid => params[:userid].to_i, + :subject => params[:subject].to_s, + :stage => params[:stage].to_s, + :datecreated => params[:datecreated].to_s, + :lastmodified => params[:lastmodified].to_s, + :validuntil => params[:validuntil].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # SendQuote + get "/quote/send" do + response = Whmcs.call("SendQuote", { :quoteid => params[:quoteid].to_i }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # CreateQuote + post "/quote/create" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("CreateQuote", { :subject => payload[:subject].to_s || " ", + :stage => payload[:stage].to_s || "Draft", + :validuntil => payload[:validuntil], + :datecreated => payload[:datecreated], + :lineitems => payload[:lineitems].to_a, + :userid => payload[:userid].to_i, + :firstname => payload[:firstname].to_s, + :lastname => payload[:lastname].to_s, + :companyname => payload[:companyname].to_s, + :email => payload[:email].to_s, + :address1 => payload[:address1].to_s, + :address2 => payload[:address2].to_f, + :city => payload[:city].to_s, + :state => payload[:state].to_s.upcase, + :postcode => payload[:postcode].to_s, + :country => payload[:country].to_s, + :phonenumber => payload[:phonenumber].to_s, + :tax_id => payload[:tax_id].to_s, + :currency => payload[:currency].to_i, + :proposal => payload[:proposal].to_s, + :customernotes => payload[:customernotes].to_s, + :adminnotes => payload[:adminnotes].to_s }) + + logger.info "Invoice Created: #{response}" + return json(response) + end + + # UpdateQuote + post "/quote/udpate" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpdateQuote", { :quoteid => payload[:quoteid].to_i, + :subject => payload[:subject].to_s || " ", + :stage => payload[:stage].to_s || "Draft", + :validuntil => payload[:validuntil], + :datecreated => payload[:datecreated], + :lineitems => payload[:lineitems].to_a, + :userid => payload[:userid].to_i, + :firstname => payload[:firstname].to_s, + :lastname => payload[:lastname].to_s, + :companyname => payload[:companyname].to_s, + :email => payload[:email].to_s, + :address1 => payload[:address1].to_s, + :address2 => payload[:address2].to_f, + :city => payload[:city].to_s, + :state => payload[:state].to_s.upcase, + :postcode => payload[:postcode].to_s, + :country => payload[:country].to_s, + :phonenumber => payload[:phonenumber].to_s, + :tax_id => payload[:tax_id].to_s, + :currency => payload[:currency].to_i, + :proposal => payload[:proposal].to_s, + :customernotes => payload[:customernotes].to_s, + :adminnotes => payload[:adminnotes].to_s }) + + logger.info "Invoice Created: #{response}" + return json(response) + end + + # DeleteQuote + post "/quote/delete" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("DeleteQuote", { :quoteid => payload[:quoteid].to_i }) + + logger.info "Payment Deleted: #{response}" + return json(response) + end +end diff --git a/route/api/client.rb b/route/api/client.rb new file mode 100644 index 0000000..251ca79 --- /dev/null +++ b/route/api/client.rb @@ -0,0 +1,198 @@ +namespace "/api/v1" do + # GetClients + get "/clients" do + # set default parmas + response = Whmcs.call("GetClients", { :limitstart => params[:limitstart] || 0, + :limitnum => params[:limitnum] || 25, + :sorting => params[:sorting] || "ASC", + # ‘Active’, ‘Inactive’, or ‘Closed’. + :status => params[:status], + # email, firstname, lastname, fullname or companyname + :search => params[:search], + # id, firstname, lastname, companyname, email, groupid, datecreated, status + :orderby => params[:orderby] || "id" }) + + logger.info "[#{response[:result].upcase}] loading clients data" + + return json(response) + end + + # GetClientGroups + get "/clients/groups" do + # set default parmas + response = Whmcs.call("GetClientGroups") + logger.info "[#{response}]" + + return json(response) + end + + # GetClientsDetails + get "/clients/details" do + # set default parmas + response = Whmcs.call("GetClientsDetails", { :clientid => params[:clientid].to_i, + :email => params[:email].to_s, + :stats => params[:stats] || true }) + logger.info "[#{response}]" + + return json(response) + end + + # GetClientPassword + get "/clients/password" do + # set default parmas + response = Whmcs.call("GetClientPassword", { :userid => params[:userid].to_i, + :email => params[:email].to_s }) + logger.info "[#{response}]" + + return json(response) + end + + # GetClientsAddons + get "/addons" do + # set default parmas + response = Whmcs.call("GetClientsAddons", { :serviceid => params[:serviceid].to_i, + :clientid => params[:clientid].to_i, + :addonid => params[:addonid].to_i }) + logger.info "[#{response}]" + + return json(response) + end + + # GetClientsDomains + get "/clients/domains" do + # set default parmas + response = Whmcs.call("GetClientsDomains", { :limitstart => params[:limitstart].to_i, + :limitnum => params[:limitnum].to_i, + :clientid => params[:clientid].to_i, + :domainid => params[:domainid].to_i, + :domain => params[:domain].to_s }) + + logger.info "[#{response}]" + + return json(response) + end + + # GetContacts + get "/clients/contacts" do + response = Whmcs.call("GetContacts", { :limitstart => params[:limitstart] || 0, + :limitnum => params[:limitnum] || 25, + :userid => params[:userid].to_i, + :firstname => params[:firstname].to_s, + :lastname => params[:lastname].to_s, + :companyname => params[:companyname].to_s, + :email => params[:email].to_s, + :address1 => params[:address1].to_s, + :address2 => params[:address2].to_s, + :city => params[:city].to_s, + :state => params[:state].to_s.upcase, + :postcode => params[:postcode].to_s, + :country => params[:country].to_s, + :phonenumber => params[:phonenumber].to_s.strip }) + logger.info "[#{response[:result].upcase}] loading client contacts data" + + return json(response) + end + + # GetEmails + get "/emails" do + response = Whmcs.call("GetEmails", { :clientid => params[:clientid].to_i, + :limitstart => params[:limitstart] || 0, + :limitnum => params[:limitnum] || 25, + :date => params[:date].to_s, + :subject => params[:subject].to_s }) + + return json(response) + end + + # GetClientsProducts + get "/clients/products" do + response = Whmcs.call("GetClientsProducts", { :clientid => params[:clientid].to_i, + :limitstart => params[:limitstart] || 0, + :limitnum => params[:limitnum] || 25, + :serviceid => params[:serviceid].to_i, + :pid => params[:pid].to_i, + :domain => params[:domain].to_s, + :username2 => params[:username].to_s }) + + return json(response) + end + + # GetCancelledPackages + get "/packages/cancelled" do + response = Whmcs.call("GetCancelledPackages", { :limitstart => params[:limitstart] || 0, + :limitnum => params[:limitnum] || 25 }) + + return json(response) + end + + # UpdateClient + post "/clients/update" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpdateClient", { :clientid => payload[:clientid].to_i, + :clientemail => payload[:clientemail].to_s, + :firstname => payload[:firstname].to_s, + :lastname => payload[:lastname].to_f, + :companyname => payload[:companyname].to_s, + :email => payload[:email].to_s, + :address1 => payload[:address1].to_s, + :address2 => payload[:address2].to_s, + :city => payload[:city].to_s, + :state => payload[:state].to_s, + :postcode => payload[:postcode].to_s, + :country => payload[:country], + :phonenumber => payload[:phonenumber], + :tax_id => payload[:tax_id].to_s, + :currency => payload[:currency], + :groupid => payload[:groupid], + :customfields => payload[:customfields], + :language => payload[:language], + :clientip => payload[:clientip], + :notes => payload[:notes], + :status => payload[:status], + :paymentmethod => payload[:paymentmethod], + :email_preferences[general] => payload[:email_preferences][:general], + :email_preferences[product] => payload[:email_preferences][:product], + :email_preferences[domain] => payload[:email_preferences][:domain], + :email_preferences[invoice] => payload[:email_preferences][:invoice], + :email_preferences[support] => payload[:email_preferences][:support], + :email_preferences[affiliate] => payload[:email_preferences][:affiliate], + :marketingoptin => payload[:marketingoptin], + :clearcreditcard => payload[:clearcreditcard], + :skipvalidation => payload[:skipvalidation], + :latefeeoveride => payload[:latefeeoveride], + :overideduenotices => payload[:overideduenotices], + :taxexempt => payload[:taxexempt], + :separateinvoices => payload[:separateinvoices], + :disableautocc => payload[:disableautocc], + :overrideautoclose => payload[:overrideautoclose] }) + + logger.info "Invoice Created: #{response}" + return json(response) + end + + # UpdateContact + post "/clients/contact/update" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpdateContact", { :contactid => payload[:contactid].to_i, + :firstname => payload[:firstname].to_s, + :lastname => payload[:lastname].to_f, + :companyname => payload[:companyname].to_s, + :email => payload[:email].to_s, + :address1 => payload[:address1].to_s, + :address2 => payload[:address2].to_s, + :city => payload[:city].to_s, + :state => payload[:state].to_s, + :postcode => payload[:postcode].to_s, + :country => payload[:country], + :phonenumber => payload[:phonenumber], + :email_preferences[general] => payload[:email_preferences][:general], + :email_preferences[product] => payload[:email_preferences][:product], + :email_preferences[domain] => payload[:email_preferences][:domain], + :email_preferences[invoice] => payload[:email_preferences][:invoice], + :email_preferences[support] => payload[:email_preferences][:support], + :email_preferences[affiliate] => payload[:email_preferences][:affiliate] }) + + logger.info "Invoice Created: #{response}" + return json(response) + end +end diff --git a/route/api/domains.rb b/route/api/domains.rb new file mode 100644 index 0000000..e69de29 diff --git a/route/api/module.rb b/route/api/module.rb new file mode 100644 index 0000000..a2559ca --- /dev/null +++ b/route/api/module.rb @@ -0,0 +1,54 @@ +namespace "/api/v1/module" do + + # GetModuleQueue + get "/module/queue" do + response = Whmcs.call("GetModuleQueue", { :relatedId => params[:relatedId].to_i, + :serviceType => params[:serviceType].to_s, + :moduleName => params[:moduleName].to_s, + :moduleAction => params[:moduleAction].to_s, + :since => params[:since].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # ActivateModule + get "/module/activate" do + response = Whmcs.call("ActivateModule", { :moduleType => params[:moduleType].to_s, + :moduleName => params[:moduleName].to_s, + :parameters => params[:parameters].to_a }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # DeactivateModule + get "/module/deactivate" do + response = Whmcs.call("ActivateModule", { :moduleType => params[:moduleType].to_s, + :moduleName => params[:moduleName].to_s, + :newGateway => params[:newGateway].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # GetModuleConfigurationParameters + get "/module/configuration" do + response = Whmcs.call("GetModuleConfigurationParameters", { :moduleType => params[:moduleType].to_s, + :moduleName => params[:moduleName].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # UpdateModuleConfiguration + post "/module/configuration/update" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpdateModuleConfiguration", { :moduleType => payload[:moduleType].to_s, + :moduleName => payload[:moduleName].to_s, + :parameters => payload[:parameters].to_a }) + + logger.info "Module Configuration Updated: #{response}" + return json(response) + end +end diff --git a/route/api/orders.rb b/route/api/orders.rb new file mode 100644 index 0000000..1fc7ed4 --- /dev/null +++ b/route/api/orders.rb @@ -0,0 +1,160 @@ +namespace "/api/v1" do + # GetOrders + get "/orders" do + # set default parmas + response = Whmcs.call("GetOrders", { :limitstart => params[:limitstart] || 0, + :limitnum => params[:limitnum] || 25, + :id => params[:id] || "", + :userid => params[:userid] || "", + :requestor_id => params[:requestor_id] || "", + :status => params[:status] || "Active" # ‘Active’, ‘Inactive’, or ‘Closed’. + }) + logger.info "[#{response[:result].upcase}] loading clients data" + + return json(response) + end + + # GetOrderStatuses + get "/orders/status" do + # set default parmas + response = Whmcs.call("GetOrderStatuses") + logger.info "[#{response[:result].upcase}] loading clients data" + + return json(response) + end + + # GetProducts + # + # Retrieve configured products matching provided criteria + get "/products" do + # set default parmas + response = Whmcs.call("GetProducts", { :pid => params[:pid].to_i, + :gid => params[:gid].to_i, + :module => params[:module] }) + logger.info "[#{response[:result].upcase}] loading clients data" + + return json(response) + end + + # AcceptOrder + get "/orders/accept" do + # set default parmas + response = Whmcs.call("AcceptOrder", { :orderid => params[:orderid], + :serverid => params[:serverid].to_i, + :serviceusername => params[:serviceusername] || "", + :servicepassword => params[:servicepassword] || "", + :registrar => params[:registrar] || "", + :sendregistrar => params[:sendregistrar], + :autosetup => params[:autosetup], + :sendemail => params[:sendemail] }) + logger.info "[#{response[:result].upcase}] Order accepted" + + return json(response) + end + + # AddOrder + post "/order/add" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Add order: #{payload}" + response = Whmcs.call("AddOrder", { :clientid => payload["clientid"].to_i, + :paymentmethod => payload["paymentmethod"] || "paypal", + :pid => payload["pid"] || [], + :domain => payload["domain"] || [], + :billingcycle => payload["billingcycle"] || [], + :domaintype => payload["domaintype"] || [], + :hidden => payload["hidden"], + :regperiod => payload["regperiod"] || [], + :idnlanguage => payload["idnlanguage"] || [], + :eppcode => payload["eppcode"], + :nameserver1 => payload["nameserver1"], + :nameserver2 => payload["nameserver2"], + :nameserver3 => payload["nameserver3"], + :nameserver4 => payload["nameserver4"], + :nameserver5 => payload["nameserver5"], + :customfields => payload["customfields"], + :configoptions => payload["configoptions"], + :priceoverride => payload["priceoverride"], + :promocode => payload["promocode"], + :promooverride => payload["promooverride"], + :affid => payload["affid"], + :noinvoice => payload["noinvoice"], + :noinvoiceemail => payload["noinvoiceemail"], + :noemail => payload["noemail"], + :addons => payload["addons"] || [], + :hostname => payload["hostname"], + :ns1prefix => payload["ns1prefix"], + :ns2prefix => payload["ns2prefix"], + :rootpw => payload["rootpw"], + :contactid => payload["contactid"], + :dnsmanagement => payload["dnsmanagement"], + :domainfields => payload["domainfields"], + :emailforwarding => payload["emailforwarding"], + :idprotection => payload["idprotection"], + :domainpriceoverride => payload["domainpriceoverride"], + :domainrenewoverride => payload["domainrenewoverride"], + :domainrenewals => payload["domainrenewals"], + :clientip => payload["clientip"].to_s, + :addonid => payload["addonid"].to_i, + :serviceid => payload["serviceid"].to_i, + :addonids => payload["addonids"] || [], + :serviceids => payload["serviceids"] || [] }) + session[:userid] = response[:user_id] + logger.info "User updated: #{response}" + return json(response) + end + + # CancelOrder + get "/orders/cancel" do + # set default parmas + response = Whmcs.call("CancelOrder", { :orderid => params[:orderid], + :cancelsub => params[:cancelsub], + :noemail => params[:noemail] }) + logger.info "[#{response[:result].upcase}] Order accepted" + + return json(response) + end + + # DeleteOrder + get "/orders/delete" do + response = Whmcs.call("DeleteOrder", { :orderid => params[:orderid] }) + logger.info "[#{response[:result].upcase}] Order accepted" + + return json(response) + end + + # FraudOrder + get "/orders/fraud" do + response = Whmcs.call("FraudOrder", { :orderid => params[:orderid], + :cancelsub => params[:cancelsub] }) + logger.info "[#{response[:result].upcase}] Order accepted" + + return json(response) + end + + # OrderFraudCheck + get "/orders/fraud/check" do + response = Whmcs.call("OrderFraudCheck", { :orderid => params[:orderid], + :ipaddress => params[:ipaddress].to_s }) + logger.info "[#{response[:result].upcase}] Fraud check - IP: #{response[:ipaddress]}" + + return json(response) + end + + # PendingOrder + get "/orders/pending" do + response = Whmcs.call("PendingOrder", { :orderid => params[:orderid] }) + logger.info "[#{response[:result].upcase}] Fraud check - IP: #{response[:ipaddress]}" + + return json(response) + end + + # GetPromotions + get "/promotions" do + response = Whmcs.call("GetPromotions", { :code => params[:code] }) + logger.info "[#{response[:result].upcase}] Coupon code applied" + + return json(response) + end +end diff --git a/route/api/products.rb b/route/api/products.rb new file mode 100644 index 0000000..2e93203 --- /dev/null +++ b/route/api/products.rb @@ -0,0 +1,48 @@ +namespace "/api/v1/products" do + # GetProducts + get "/" do + response = Whmcs.call("GetProducts", { :pid => params[:pid].to_i, # Can be a list of ids comma separated + :gid => params[:gid].to_i, + :module => params[:module] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # AddProduct + post "/add" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Add product: #{payload}" + response = Whmcs.call("AddProduct", { :name => payload[:name], + :gid => payload[:gid].to_i, + :type => payload[:type], + :stockcontrol => payload[:stockcontrol], + :qty => payload[:qty], + :paytype => payload[:paytype], + :hidden => payload[:hidden], + :showdomainoptions => payload[:showdomainoptions], + :tax => payload[:tax], + :isFeatured => payload[:isFeatured], + :proratabilling => payload[:proratabilling], + :description => payload[:description], + :welcomeemail => payload[:welcomeemail], + :proratadate => payload[:proratadate], + :proratachargenextmonth => payload[:proratachargenextmonth], + :subdomain => payload[:subdomain], + :autosetup => payload[:autosetup], + :module => payload[:module], + :servergroupids => payload[:servergroupids], + :configoption1 => payload[:configoption1], + :configoption2 => payload[:configoption2], + :configoption3 => payload[:configoption3], + :configoption4 => payload[:configoption4], + :configoption5 => payload[:configoption5], + :configoption6 => payload[:configoption6], + :order => payload[:order].to_i, + :pricing => payload[:pricing].to_a }) + + logger.info "User updated: #{response}" + return json(response) + end +end diff --git a/route/api/project_management.rb b/route/api/project_management.rb new file mode 100644 index 0000000..823f440 --- /dev/null +++ b/route/api/project_management.rb @@ -0,0 +1,148 @@ +namespace "/api/v1" do + # GetProject + get "/project" do + response = Whmcs.call("GetProject", { :projectid => params[:projectid].to_i }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # GetProjects + get "/projects" do + response = Whmcs.call("GetProjects", { :limitstart => params[:limitstart].to_i || 0, + :limitnum => params[:limitnum].to_i || 25, + :userid => params[:userid].to_i, + :title => params[:title].to_s, + :ticketids => params[:ticketids].to_s, + :invoiceids => params[:invoiceids].to_s, + :notes => params[:notes].to_s, + :adminid => params[:adminid].to_i, + :status => params[:status].to_s, + :createdfrom => params[:createdfrom].to_s, + :duedate => params[:duedate].to_s, + :completed => params[:completed], + :lastmodified => params[:lastmodified].to_s }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # CreateProject + post "/project/create" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Create Project: #{payload}" + response = Whmcs.call("CreateProject", { :title => payload[:title], + :adminid => payload[:adminid].to_i, + :userid => payload[:userid].to_i, + :status => payload[:status], + # The date of the project in Y-m-d format. + :created => payload[:created].to_s, + :duedate => payload[:duedate].to_s, + :completed => payload[:completed], + :ticketids => payload[:ticketids].to_s, + :invoiceids => payload[:invoiceids].to_s }) + + logger.info "Project Created: #{response}" + return json(response) + end + + # UpdateProject + post "/project/update" do + payload = JSON.parse(request.body.read.to_s) + + logger.info "Update Project: #{payload}" + response = Whmcs.call("UpdateProject", { :projectid => payload[:projectid].to_i, + :adminid => payload[:adminid].to_i, + :userid => payload[:userid].to_i, + :status => payload[:status], + # The date of the project in Y-m-d format. + :created => payload[:created].to_s, + :duedate => payload[:duedate].to_s, + :completed => payload[:completed], + :title => payload[:title], + :ticketids => payload[:ticketids].to_s, + :invoiceids => payload[:invoiceids].to_s, + :notes => payload[:notes].to_s }) + + logger.info "Project updated: #{response}" + return json(response) + end + + # AddProjectMessage + post "/project/message/add" do + payload = JSON.parse(request.body.read.to_s) + + logger.info "Add message to Project: #{payload}" + response = Whmcs.call("AddProjectMessage", { :projectid => payload[:projectid].to_i, + :message => payload[:message].to_s, + :adminid => payload[:adminid].to_i }) + + logger.info "Message added: #{response}" + return json(response) + end + + # AddProjectTask + post "/task/add" do + payload = JSON.parse(request.body.read.to_s) + + logger.info "Add Project Task: #{payload}" + response = Whmcs.call("AddProjectTask", { :projectid => payload[:projectid].to_i, + :duedate => payload[:duedate].to_s, + :adminid => payload[:adminid].to_i, + :task => payload[:task].to_s, + :notes => payload[:notes].to_s, + :completed => payload[:completed], + :billed => payload[:billed] }) + + logger.info "Task added: #{response}" + return json(response) + end + + # UpdateProjectTask + post "/task/update" do + payload = JSON.parse(request.body.read.to_s) + + logger.info "Update Project Task: #{payload}" + response = Whmcs.call("UpdateProjectTask", { :taskid => payload[:taskid].to_i, + :projectid => payload[:projectid].to_i, + :duedate => payload[:duedate].to_s, + :adminid => payload[:adminid].to_i, + :task => payload[:task].to_s, + :notes => payload[:notes].to_s, + :completed => payload[:completed] }) + + logger.info "Task updated: #{response}" + return json(response) + end + + # DeleteProjectTask + get "/task/delete" do + response = Whmcs.call("DeleteProjectTask", { :projectid => params[:projectid].to_i, + :taskid => params[:taskid].to_i }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # StartTaskTimer + get "/timer/start" do + response = Whmcs.call("StartTaskTimer", { :timerid => params[:timerid].to_i, + :projectid => params[:projectid].to_i, + :adminid => params[:adminid].to_i, + #The start time as a unix time stamp.Defaults to time() if not provided + :start_time => params[:start_time].to_i, + :end_time => params[:end_time].to_i }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # EndTaskTimer + get "/timer/end" do + response = Whmcs.call("EndTaskTimer", { :timerid => params[:timerid].to_i, + :projectid => params[:projectid].to_i, + :adminid => params[:adminid].to_i, + #The start time as a unix time stamp.Defaults to time() if not provided + :end_time => params[:end_time].to_i }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end +end diff --git a/route/api/servers.rb b/route/api/servers.rb new file mode 100644 index 0000000..bad73ff --- /dev/null +++ b/route/api/servers.rb @@ -0,0 +1,16 @@ +namespace "/api/v1/servers" do + # GetServers + get "/" do + response = Whmcs.call("GetServers", { :serviceId => "", + :addonId => "", + :fetchStatus => true }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # GetHealthStatus + get "/health/status" do + response = Whmcs.call("GetHealthStatus", { :fetchStatus => true }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end +end diff --git a/route/api/service.rb b/route/api/service.rb new file mode 100644 index 0000000..7aa498c --- /dev/null +++ b/route/api/service.rb @@ -0,0 +1,112 @@ +namespace "/api/v1/service" do + # TODO + get "/:id" do |id| + res = Whmcs::Service.find(id) + puts res + return json(res) + end + # TODO + delete "/:id" do |id| + service.destroy if service + status 204 + end + + # ModuleChangePackage + get "/module/change/package" do + response = Whmcs.call("ModuleChangePackage", { :serviceid => params[:serviceid].to_i }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # ModuleChangePw + get "/module/change/password" do + response = Whmcs.call("ModuleChangePw", { :serviceid => params[:serviceid].to_i, + :servicepassword => params[:servicepassword].to_s }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # ModuleCreate + get "/module/create" do + response = Whmcs.call("ModuleCreate", { :serviceid => params[:serviceid].to_i }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # ModuleCustom + get "/module/custom" do + response = Whmcs.call("ModuleCustom", { :serviceid => params[:serviceid].to_i, + :func_name => params[:func_name].to_s }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # ModuleSuspend + get "/module/suspend" do + response = Whmcs.call("ModuleSuspend", { :serviceid => params[:serviceid].to_i, + :suspendreason => params[:suspendreason].to_s }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # ModuleUnsuspend + get "/module/unsuspend" do + response = Whmcs.call("ModuleUnsuspend", { :serviceid => params[:serviceid].to_i }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # ModuleTerminate + get "/module/terminate" do + response = Whmcs.call("ModuleTerminate", { :serviceid => params[:serviceid].to_i }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # UpdateClientProduct + post "/updateclientproduct" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpdateClientProduct", { :serviceid => payload[:serviceid].to_i, + :pid => payload[:pid].to_i, + :serverid => payload[:serverid], + :regdate => payload[:regdate], + :nextduedate => payload[:nextduedate], + :terminationdate => payload[:terminationdate], + :domain => payload[:domain], + :firstpaymentamount => payload[:firstpaymentamount], + :recurringamount => payload[:recurringamount], + :paymentmethod => payload[:paymentmethod], + :billingcycle => payload[:billingcycle], + :subscriptionid => payload[:subscriptionid], + :status => payload[:status], + :notes => payload[:notes], + :serviceusername => payload[:serviceusername], + :servicepassword => payload[:servicepassword], :overideautosuspend => payload[:overideautosuspend], + :overidesuspenduntil => payload[:overidesuspenduntil], + :ns1 => payload[:ns1], + :ns2 => payload[:ns2], + :dedicatedip => payload[:dedicatedip], + :assignedips => payload[:assignedips], + :diskusage => payload[:diskusage], + :disklimit => payload[:disklimit], + :bwusage => payload[:bwusage], + :bwlimit => payload[:bwlimit], + :suspendreason => payload[:suspendreason], + :promoid => payload[:promoid], + :unset => payload[:unset], + :autorecalc => payload[:autorecalc], + :customfields => payload[:customfields], + :configoptions => payload[:configoptions] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # UpgradeProduct + post "/upgradeproduct" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("UpgradeProduct", { :serviceid => payload[:serviceid].to_i, + :calconly => payload[:calconly], + :paymentmethod => payload[:paymentmethod], + :type => payload[:type], + :newproductid => payload[:newproductid].to_i, + :newproductbillingcycle => payload[:newproductbillingcycle], + :promocode => payload[:promocode], + :configoptions => payload[:configoptions] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end +end diff --git a/route/api/support.rb b/route/api/support.rb new file mode 100644 index 0000000..f8d40eb --- /dev/null +++ b/route/api/support.rb @@ -0,0 +1,67 @@ +namespace "/api/v1/support" do + + # GetAnnouncements + get "/announcement" do + logger.info "loading announcement data" + + response = Whmcs.call("GetAnnouncements", { :limitstart => params[:limitstart], + :limitnum => params[:limitnum] }) + logger.info "[#{response[:result].upcase}] loading data" + + return json(response) + end + + # AddAnnouncement + post "/announcement/add" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Adding announcement #{payload}" + response = Whmcs.call("AddAnnouncement", { :date => payload[:date], + :title => payload[:title], + :announcement => payload[:announcement], + :published => payload[:published] }) + session[:announcement] = response[:announcement] + logger.info "Announcement added: #{response}" + return json(response) + end + + # UpdateAnnouncement + post "/announcement/update" do + payload = JSON.parse(request.body.read.to_s) + + response = Whmcs.call("UpdateAnnouncement", { :announcementid => payload[:announcementid], + :date => payload[:date], + :title => payload[:title], + :announcement => payload[:announcement], + :published => payload[:published] # 1 || 0 + }) + session[:announcement] = response[:announcement] + logger.info "Announcement updated: #{response}" + return json(response) + end + + # DeleteAnnouncement + post "/announcement/delete" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Pending remove #{payload}" + response = Whmcs.call("AddAnnouncement", { :announcementid => payload[:announcementid] }) + session[:announcement] = response[:announcement] + logger.info "Accnouncement removed: #{response}" + return json(response) + end + + # OpenTicket + post "/ticket/open" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Pending remove #{payload}" + response = Whmcs.call("AddAnnouncement", { :announcementid => payload[:announcementid] }) + session[:announcement] = response[:announcement] + logger.info "Accnouncement #{response} removed" + return json(response) + end +end diff --git a/route/api/system.rb b/route/api/system.rb new file mode 100644 index 0000000..8e4f72d --- /dev/null +++ b/route/api/system.rb @@ -0,0 +1,148 @@ +namespace "/api/v1/system" do + # GetAdminUsers + # + # NOT ALLOWED + get "/getadminusers" do + response = Whmcs.call("GetAdminUsers", { :roleid => params[:roleid].to_i, + :email => params[:email].to_s, + :include_disabled => params[:include_disabled] }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # GetAdminDetails + get "/getadmindetails" do + response = Whmcs.call("GetAdminDetails") + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + # GetStats + get "/getstats" do + response = Whmcs.call("GetStats", { :timeline_days => params[:timeline_days].to_i }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # GetStaffOnline + # + # Retrieve a list of currently logged in admin users. + get "/getstaffonline" do + response = Whmcs.call("GetStaffOnline") + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # GetPaymentMethods + # + # Retrieve Activated Payment Methods + get "/getpaymentmethods" do + response = Whmcs.call("GetPaymentMethods") + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # GetCurrencies + get "/getcurrencies" do + response = Whmcs.call("GetCurrencies") + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # GetEmailTemplates + get "/getemailtemplates" do + response = Whmcs.call("GetEmailTemplates", { :type => params[:type].to_s, + :language => params[:language].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # GetConfigurationValue + get "/setting" do + response = Whmcs.call("GetConfigurationValue", { :setting => params[:setting].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # SetConfigurationValue + post "/setting/add" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("LogActivity", { :setting => payload[:setting].to_s, + :value => payload[:value].to_s }) + + logger.info "Setting added: #{response}" + return json(response) + end + + # LogActivity + post "/logs/add" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("LogActivity", { :clientid => payload[:clientid], + :description => payload[:description].to_s }) + + logger.info "log added: #{response}" + return json(response) + end + + # GetAutomationLog + get "/logs/automation" do + response = Whmcs.call("GetAutomationLog", { :startdate => params[:startdate].to_s, + :enddate => params[:enddate].to_s, + :namespace => params[:namespace].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # GetActivityLog + get "/logs/activity" do + response = Whmcs.call("GetActivityLog", { :limitstart => params[:limitstart].to_i || 0, + :limitnum => params[:limitnum].to_i || 25, + :userid => params[:userid].to_i, + :date => params[:date].to_s, + :user => params[:user].to_s, + :description => params[:description].to_s, + :ipaddress => params[:ipaddress].to_s }) + + logger.info "[#{response[:result].upcase}] - #{response[:message]}." + return json(response) + end + + # EncryptPassword + post "/password/encrypt" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("EncryptPassword", { :password2 => payload[:password].to_s }) + + logger.info "Password Encrypted: #{response}" + return json(response) + end + + # DecryptPassword + post "/password/decrypt" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("DecryptPassword", { :password2 => payload[:password].to_s }) + + logger.info "Password Decrypted: #{response}" + return json(response) + end + + # AddBannedIp + post "/blacklist" do + payload = JSON.parse(request.body.read.to_s) + response = Whmcs.call("AddBannedIp", { :ip => payload[:ip].to_s, + :reason => payload[:reason].to_s, + :days => payload[:days].to_i, + :expires => payload[:expires] #YYYY-MM-DD HH:MM:SS + }) + + logger.info "Added IP to Blacklist: #{response}" + return json(response) + end +end diff --git a/route/api/tickets.rb b/route/api/tickets.rb new file mode 100644 index 0000000..ad18b43 --- /dev/null +++ b/route/api/tickets.rb @@ -0,0 +1,76 @@ +namespace "/api/v1" do + + # GetSupportDepartments + get "/departments" do + response = Whmcs.call("GetSupportDepartments", { :ignore_dept_assignments => params[:ignore_dept_assignments] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # GetSupportStatuses + get "/status" do + response = Whmcs.call("GetSupportStatuses", { :deptid => params[:deptid] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # GetTicket + get "/ticket" do + response = Whmcs.call("GetTicket", { :ticketnum => params[:ticketnum], + :ticketid => params[:ticketid], + :repliessort => params[:repliessort] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # GetTickets + get "/tickets" do + response = Whmcs.call("GetTickets", { :limitstart => params[:limitstart], + :limitnum => params[:limitnum], + :deptid => params[:deptid], + :clientid => params[:clientid], + :email => params[:email], + :status => params[:status], + :subject => params[:subject], + :ignore_dept_assignments => params[:ignore_dept_assignments] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # GetTicketAttachment + get "/ticket/attachment" do + response = Whmcs.call("GetTicketAttachment", { :relatedid => params[:relatedid], + :type => params[:type], + :index => params[:index] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # GetTicketCounts + get "/ticket/counts" do + response = Whmcs.call("GetTicketCounts", { :ignoreDepartmentAssignments => params[:ignoreDepartmentAssignments], + :includeCountsByStatus => params[:includeCountsByStatus] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # GetTicketNotes + get "/ticket/notes" do + response = Whmcs.call("GetTicketNotes", { :ticketid => params[:ticketid] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # GetTicketPredefinedCats + get "/ticket/predefined/cats" do + response = Whmcs.call("GetTicketPredefinedCats") + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # GetTicketPredefinedReplies + get "/ticket/predefined/replies" do + response = Whmcs.call("GetTicketPredefinedReplies", { :catid => params[:catid] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end +end diff --git a/route/api/users.rb b/route/api/users.rb new file mode 100644 index 0000000..68a0ea7 --- /dev/null +++ b/route/api/users.rb @@ -0,0 +1,112 @@ +namespace "/api/v1/users" do + # GetUsers + get "/" do + logger.info "loading users data" + + response = Whmcs.call("GetUsers", { :limitstart => params[:limitstart], + :limitnum => params[:limitnum], + :sorting => params[:sorting], + :search => params[:search] }) + logger.info "[#{response[:result].upcase}] loading data" + + return json(response) + end + + # CreateClientInvite + get "/invite" do + logger.info "loading users data" + + response = Whmcs.call("CreateClientInvite", { :limitstart => params[:limitstart], + :limitnum => params[:limitnum], + :sorting => params[:sorting], + :search => params[:search] }) + logger.info "[#{response[:result].upcase}] loading data" + + return json(response) + end + + # AddUser + post "/add" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "AddUser: #{payload}" + response = Whmcs.call("AddUser", { :firstname => payload["firstname"], + :lastname => payload["lastname"], + :email => payload["email"], + :password2 => payload["password2"], + :language => payload["language"] }) + session[:userid] = response[:user_id] + logger.info "User Added: #{response}" + return json(response) + end + + # DeleteUserClient + # Delete relationship between user and client. + post "/delete" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Delete User Client relationship: #{payload}" + response = Whmcs.call("DeleteUserClient", { :user_id => payload["user_id"], + :client_id => payload["client_id"] }) + session[:userid] = response[:user_id] + logger.info "relationship deleted: #{response}" + return json(response) + end + + # GetPermissionsList + get "/permissionlist" do + response = Whmcs.call("GetPermissionsList") + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # GetUserPermissions + get "/permissions" do + response = Whmcs.call("GetUserPermissions", { :userid => params[:userid], + :client_id => params[:client_id] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # ResetPassword + post "/resetpassword" do + payload = JSON.parse(request.body.read.to_s) + logger.info "Password Reset: #{payload}" + response = Whmcs.call("ResetPassword", { :id => payload[:id], + :email => payload[:email] }) + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + + # UpdateUser + post "/update" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Update User: #{payload}" + response = Whmcs.call("UpdateUser", { :user_id => payload["user_id"], + :firstname => payload["firstname"], + :lastname => payload["lastname"], + :email => payload["email"], + :language => payload["language"] }) + session[:userid] = response[:user_id] + logger.info '[#{response[:result].upcase}] - #{response[:message]}.' + return json(response) + end + # UpdateUserPermissions + # + # Update the permissions of a user for a client. + post "/updatepermission" do + # read request body + payload = JSON.parse(request.body.read.to_s) + + logger.info "Update User permission: #{payload}" + response = Whmcs.call("UpdateUserPermissions", { :user_id => payload["user_id"], + :client_id => payload["client_id"], + :permissions => payload["permissions"] }) + session[:userid] = response[:user_id] + logger.info "User updated: #{response}" + return json(response) + end +end diff --git a/route/portal.rb b/route/portal.rb new file mode 100644 index 0000000..b35c355 --- /dev/null +++ b/route/portal.rb @@ -0,0 +1,43 @@ +# Endpoints +get "/" do + return test("params") +end + +get "/debug" do + return session.inspect +end + +get "/test/:key" do + result = REDIS.get(params[:key]) + + return json({ "username": result }) +end + +get "/logout" do + session["access_token"] = nil + session.clear + + redirect to("/") +end + +get "/register" do +end + +namespace "/api/v1" do + before do + content_type "application/json" + expires 500, :public, :must_revalidate + + # set global variables @payload + # @payload = JSON.parse(request.body.read.to_s) + config = { + 'endpoint': ENV["ENDPOINT"], + 'identifier': ENV["IDENTIFIER"], + 'secret': ENV["SECRET"], + } + + Whmcs.configure(config) + + Dir[File.join(File.dirname(__FILE__), "api", "*.rb")].each { |file| require file } + end +end diff --git a/test/addons.js b/test/addons.js deleted file mode 100644 index 2fede76..0000000 --- a/test/addons.js +++ /dev/null @@ -1,21 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Addons"', function () { - - it('should update client addon', function (done) { - let opts = { - id: 1 - }; - conf.whmcs.addons.updateClientAddon(opts, function (err, details) { - if (err && err.message.indexOf('Addon ID Not Found') > -1) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - -}); \ No newline at end of file diff --git a/test/affiliates.js b/test/affiliates.js deleted file mode 100644 index af66c5b..0000000 --- a/test/affiliates.js +++ /dev/null @@ -1,44 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Affiliates"', function () { - - it('should get referrals', function (done) { - let opts = { - limitstart: 0, - limitnum: 25 - }; - conf.whmcs.affiliates.getAffiliates(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('affiliates').to.be.an('object'); - done(); - }); - }); - - it('should activate and get referrals by client id', function (done) { - let opts = { - userid: conf.demoClientId - }; - conf.whmcs.affiliates.affiliateActivate(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - - let opts = { - userid: conf.demoClientId - }; - conf.whmcs.affiliates.getAffiliates(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('affiliates').to.be.an('object'); - expect(details.affiliates).to.have.a.property('affiliate').to.be.an('array'); - let a = details.affiliates.affiliate.map(function (affiliate) { - return affiliate.clientid; - }); - expect(a).includes(conf.demoClientId); - done(); - }); - }); - }); - -}); \ No newline at end of file diff --git a/test/authentication.js b/test/authentication.js deleted file mode 100644 index ec37594..0000000 --- a/test/authentication.js +++ /dev/null @@ -1,72 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Authentication"', function () { - - it('should validate user login credential', function (done) { - let opts = { - email: conf.demoUserDetails.email, - password2: conf.demoUserDetails.password2 - }; - conf.whmcs.authentication.validateLogin(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should create, update, get and delete an OAuth credential', function (done) { - this.timeout(30000); - let opts = { - email: conf.demoUserDetails.email, - grantType: 'authorization_code', - scope: 'clientarea:sso', - name: 'oauthtest' - }; - conf.whmcs.authentication.createOAuthCredential(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('credentialId'); - let credentialId = details.credentialId; - - let opts = { - credentialId: credentialId, - scope: 'clientarea:billing_info' - }; - conf.whmcs.authentication.updateOAuthCredential(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - - conf.whmcs.authentication.listOAuthCredentials(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('clients').to.be.an('array'); - let c = details.clients.map(function (client) { - return client.credentialId; - }); - expect(c).includes(credentialId); - - let opts = { - credentialId: credentialId - }; - conf.whmcs.authentication.deleteOAuthCredential(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - }); - }); - }); - - it('should create a SSO token', function (done) { - let opts = { - client_id: conf.demoClientId - }; - conf.whmcs.authentication.createSsoToken(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); -}); \ No newline at end of file diff --git a/test/billing.js b/test/billing.js deleted file mode 100644 index dc137a0..0000000 --- a/test/billing.js +++ /dev/null @@ -1,466 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Billing"', function () { - - describe('Quote', function () { - let demoQuoteId; - - it('should get quotes', function (done) { - let opts = { - limitstart: 0, - limitnum: 1 - }; - - conf.whmcs.billing.getQuotes(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('quotes').to.be.an('object'); - expect(details.quotes).to.have.a.property('quote').to.be.an('array'); - done(); - }); - }); - - it('should create a quote', function (done) { - let opts = { - subject: 'test quote', - stage: 'Draft', - validuntil: '01-01-2099', - userid: conf.demoClientId - }; - - let items = { - 'lineitems[0]': { - desc: 'quote description', - qty: 1, - up: 10, - taxable: false - } - }; - - opts['lineitems'] = Buffer.from(conf.serialize(items), 'ascii').toString('base64'); - - conf.whmcs.billing.createQuote(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('quoteid').to.not.be.null; - demoQuoteId = details.quoteid; - done(); - }); - }); - - it('should get a quote by id', function (done) { - if (demoQuoteId == undefined) { - this.skip(); - } - let opts = { - quoteid: demoQuoteId - }; - - conf.whmcs.billing.getQuotes(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('quotes').to.be.an('object'); - expect(details.quotes).to.have.a.property('quote').to.be.an('array').to.have.lengthOf(1); - done(); - }); - }); - - it('should update a quote', function (done) { - if (demoQuoteId == undefined) { - this.skip(); - } - let opts = { - quoteid: demoQuoteId, - subject: 'this is an updated quote' - }; - - conf.whmcs.billing.updateQuote(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should accept a quote', function (done) { - this.timeout(30000); - if (demoQuoteId == undefined) { - this.skip(); - } - let opts = { - quoteid: demoQuoteId, - }; - - conf.whmcs.billing.acceptQuote(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('invoiceid'); - done(); - }); - }); - - it('should send a quote', function (done) { - if (demoQuoteId == undefined) { - this.skip(); - } - let opts = { - quoteid: demoQuoteId, - }; - - conf.whmcs.billing.sendQuote(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should delete a quote', function (done) { - if (demoQuoteId == undefined) { - this.skip(); - } - let opts = { - quoteid: demoQuoteId, - }; - - conf.whmcs.billing.deleteQuote(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - }); - - it('should add a billable item', function (done) { - let opts = { - clientid: conf.demoClientId, - description: 'this is a billable item', - amount: '10.00' - }; - - conf.whmcs.billing.addBillableItem(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('billableid').to.not.be.null; - done(); - }); - }); - - it('should create an invoice', function (done) { - this.timeout(30000); - let opts = { - userid: conf.demoClientId, - itemdescription0: 'this is a test invoice', - itemamount0: 1, - autoapplycredit: false - }; - - conf.whmcs.billing.createInvoice(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('invoiceid').to.not.be.null; - done(); - }); - }); - - it('should get invoices', function (done) { - let opts = { - limitstart: 0, - limitnum: 1 - }; - - conf.whmcs.billing.getInvoices(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('invoices').to.be.an('object'); - expect(details.invoices).to.have.a.property('invoice').to.be.an('array'); - done(); - }); - }); - - describe('Invoice', function () { - let demoInvoiceId; - - before(function (done) { - let opts = { - userid: conf.demoClientId, - itemdescription0: 'this is a test invoice', - itemamount0: 1, - autoapplycredit: false - }; - - conf.whmcs.billing.createInvoice(opts, function (err, details) { - if (err) { - throw err; - } else if (details.invoiceid == undefined) { - throw new Error('Cannot create an invoice. Cannot proceed.'); - } else { - demoInvoiceId = details.invoiceid; - done(); - } - }); - }); - - it('should update an invoice', function (done) { - let opts = { - invoiceid: demoInvoiceId, - 'itemdescription[0]': 'this is an updated invoice', - 'itemamount[0]': 1, - 'itemtaxed[0]': false - }; - - conf.whmcs.billing.updateInvoice(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('invoiceid').to.equal(demoInvoiceId); - done(); - }); - }); - - it('should get an invoice', function (done) { - let opts = { - invoiceid: demoInvoiceId - }; - - conf.whmcs.billing.getInvoice(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('items').to.be.an('object'); - expect(details.items).to.have.a.property('item').to.be.an('array').to.have.lengthOf(1); - done(); - }); - }); - - it('should add a payment to an invoice', function (done) { - let opts = { - invoiceid: demoInvoiceId, - transid: 1234, - amount: 0.01, - gateway: 'paypal' - }; - - conf.whmcs.billing.addInvoicePayment(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should apply credit to an invoice', function (done) { - let opts = { - invoiceid: demoInvoiceId, - amount: 0.01, - noemail: true - }; - - conf.whmcs.billing.applyCredit(opts, function (err, details) { - if (err && err.message.indexOf('Amount exceeds customer credit balance') > -1) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('invoicepaid').to.not.be.null; - done(); - } - }); - }); - - it('should capture payment on an unpaid invoice', function (done) { - let opts = { - invoiceid: demoInvoiceId - }; - - conf.whmcs.billing.capturePayment(opts, function (err, details) { - if (err && err.message.indexOf("Payment Attempt Failed") > -1) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - }); - - describe('Pay Method', function () { - let demoPaymentMethodId; - - it('should add a pay method to given client', function (done) { - let opts = { - clientid: conf.demoClientId, - type: 'BankAccount', - bank_code: '123456789', - bank_code: '1234', - bank_account: '999999999' - }; - - conf.whmcs.billing.addPayMethod(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('paymethodid').to.not.be.null; - demoPaymentMethodId = details.paymethodid; - done(); - }); - }); - - it('should get pay methods associated with client id', function (done) { - if (demoPaymentMethodId == undefined) { - this.skip(); - } else { - let opts = { - clientid: conf.demoClientId, - paymethodid: demoPaymentMethodId - }; - - conf.whmcs.billing.getPayMethods(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('paymethods').to.be.an('array').to.have.lengthOf(1); - done(); - }); - } - }); - - it('should update a pay method', function (done) { - if (demoPaymentMethodId == undefined) { - this.skip(); - } else { - let opts = { - clientid: conf.demoClientId, - paymethodid: demoPaymentMethodId - }; - - conf.whmcs.billing.updatePayMethod(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('paymethodid').to.not.be.null; - done(); - }); - } - }); - - it('should delete a pay method', function (done) { - if (demoPaymentMethodId == undefined) { - this.skip(); - } else { - let opts = { - clientid: conf.demoClientId, - paymethodid: demoPaymentMethodId - }; - - conf.whmcs.billing.deletePayMethod(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('paymethodid').to.not.be.null; - done(); - }); - } - }); - }); - - it('should generate invoices that are due to be generated', function (done) { - this.timeout(30000); - let opts = { - clientid: conf.demoClientId - }; - conf.whmcs.billing.genInvoices(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('numcreated').to.not.be.null; - done(); - }); - }); - - describe('Credit', function () { - it('should add credit', function (done) { - let opts = { - clientid: conf.demoClientId, - description: 'this is a credit test', - amount: 1 - }; - - conf.whmcs.billing.addCredit(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('newbalance').to.not.be.null; - done(); - }); - }); - - it('should get credits', function (done) { - let opts = { - clientid: conf.demoClientId - }; - - conf.whmcs.billing.getCredits(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('credits').to.be.an('object'); - expect(details.credits).to.have.a.property('credit').to.be.an('array').to.have.lengthOf(1); - done(); - }); - }); - }); - - describe('Transaction', function () { - let demoPaymentMethod, demoTransactionId; - - before(function (done) { - conf.whmcs.system.getPaymentMethods(function (err, details) { - if (err) { - throw err; - } else if (!details.paymentmethods || !details.paymentmethods.paymentmethod || !details.paymentmethods.paymentmethod[0]) { - throw new Error('Payment methods do not exist. You must create a new payment method first.'); - } else { - demoPaymentMethod = details.paymentmethods.paymentmethod[0].module; - done(); - } - }); - }); - - it('should add transaction', function (done) { - let opts = { - paymentmethod: demoPaymentMethod, - userid: conf.demoClientId - }; - - conf.whmcs.billing.addTransaction(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should get transactions', function (done) { - let opts = { - clientid: conf.demoClientId - }; - - conf.whmcs.billing.getTransactions(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('transactions').to.be.an('object'); - expect(details.transactions).to.have.a.property('transaction').to.be.an('array').to.have.length.greaterThan(0); - demoTransactionId = details.transactions.transaction[0]; - done(); - }); - }); - - it('should update a transaction', function (done) { - if (demoTransactionId == undefined) { - this.skip(); - } else { - let opts = { - transactionid: demoTransactionId - }; - - conf.whmcs.billing.updateTransaction(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - }); - - -}); \ No newline at end of file diff --git a/test/client.js b/test/client.js deleted file mode 100644 index 551e182..0000000 --- a/test/client.js +++ /dev/null @@ -1,355 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Client"', function () { - - it('should create a client, create a contact, close the client and then delete both of them', function (done) { - this.timeout(30000); - let opts = { - firstname: 'Major', - lastname: 'Tom', - email: 'majortom@john.doe', - address1: 'Mars', - city: 'Phobos', - state: 'Crater', - postcode: '9999-999', - country: 'US', - phonenumber: '10987654321', - password2: 'liftoff' - }; - conf.whmcs.client.addClient(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('owner_id'); - expect(details).to.have.a.property('clientid'); - let clientId = details.clientid; - - let opts = { - clientid: clientId, - firstname: 'Ground', - lastname: 'Control', - email: 'groundcontrol@john.doe', - address1: 'Earth', - city: 'Phobos', - state: 'Crater', - postcode: '9999-999', - country: 'US', - phonenumber: '911911911' - } - conf.whmcs.client.addContact(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('contactid'); - let contactId = details.contactid; - - let opts = { - contactid: contactId - } - conf.whmcs.client.deleteContact(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - - let opts = { - clientid: clientId - } - conf.whmcs.client.closeClient(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - - let opts = { - clientid: clientId, - deleteusers: true, - deletetransactions: true - } - conf.whmcs.client.deleteClient(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - }); - }); - }); - }); - - it('should get cancellation requests', function (done) { - let opts = { - limitstart: 0, - limitnum: 25 - }; - conf.whmcs.client.getCancelledPackages(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('packages'); - done(); - }); - }); - - it('should get client groups', function (done) { - conf.whmcs.client.getClientGroups(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('groups').to.be.an.an('object'); - expect(details.groups).to.have.a.property('group'); - expect(details.groups.group).to.be.an('array'); - done(); - }); - }); - - it('should get the encrypted password, by user id', function (done) { - let opts = { - userid: conf.demoClientId - }; - conf.whmcs.client.getClientPassword(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('password').to.be.a('string'); - done(); - }); - }); - - it('should get the encrypted password, by user email address', function (done) { - let opts = { - email: conf.demoUserDetails.email - }; - conf.whmcs.client.getClientPassword(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('password').to.be.a('string'); - done(); - }); - }); - - it('should get clients', function (done) { - let opts = { - limitstart: 0, - limitnum: 25 - }; - conf.whmcs.client.getClients(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('clients').to.be.an.an('object'); - expect(details.clients).to.have.a.property('client'); - expect(details.clients.client).to.be.an('array').to.have.length.above(0); - done(); - }); - }); - - it('should get clients by email', function (done) { - let opts = { - search: conf.demoUserDetails.email - }; - conf.whmcs.client.getClients(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('numreturned').to.equal(1); - expect(details).to.have.a.property('clients').to.be.an.an('object'); - expect(details.clients).to.have.a.property('client'); - expect(details.clients.client).to.be.an('array').to.have.lengthOf(1); - done(); - }); - }); - - it('should get client addons', function (done) { - let opts = { - clientid: conf.demoClientId - }; - conf.whmcs.client.getClientsAddons(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should get client details by id', function (done) { - let opts = { - clientid: conf.demoClientId - }; - conf.whmcs.client.getClientsDetails(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('client').to.be.an.an('object'); - done(); - }); - }); - - it('should get client details by email address', function (done) { - let opts = { - email: conf.demoUserDetails.email - }; - conf.whmcs.client.getClientsDetails(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('client').to.be.an.an('object'); - done(); - }); - }); - - it('should get clients domains', function (done) { - let opts = { - limitstart: 0, - limitnum: 25 - }; - conf.whmcs.client.getClientsDomains(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('domains').to.be.an.an('object'); - expect(details.domains).to.have.a.property('domain'); - expect(details.domains.domain).to.be.an('array'); - done(); - }); - }); - - it('should get client domains by client id', function (done) { - let opts = { - clientid: conf.demoClientId - }; - conf.whmcs.client.getClientsDomains(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should get clients products', function (done) { - this.timeout(30000); - let opts = { - limitstart: 0, - limitnum: 25 - }; - conf.whmcs.client.getClientsProducts(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('products').to.be.an.an('object'); - expect(details.products).to.have.a.property('product'); - expect(details.products.product).to.be.an('array'); - done(); - }); - }); - - it('should get clients products by client id', function (done) { - let opts = { - clientid: conf.demoClientId - }; - conf.whmcs.client.getClientsProducts(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should get clients contacts', function (done) { - let opts = { - limitstart: 0, - limitnum: 25 - }; - conf.whmcs.client.getContacts(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('contacts').to.be.an.an('object'); - expect(details.contacts).to.have.a.property('contact'); - expect(details.contacts.contact).to.be.an('array'); - done(); - }); - }); - - it('should get clients contacts by client id', function (done) { - let opts = { - userid: conf.demoClientId - }; - conf.whmcs.client.getContacts(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('contacts').to.be.an.an('object'); - expect(details.contacts).to.have.a.property('contact'); - expect(details.contacts.contact).to.be.an('array'); - done(); - }); - }); - - it('should get clients emails', function (done) { - let opts = { - limitstart: 0, - limitnum: 25, - clientid: conf.demoClientId - }; - conf.whmcs.client.getEmails(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('emails').to.be.an.an('object'); - expect(details.emails).to.have.a.property('email'); - expect(details.emails.email).to.be.an('array'); - done(); - }); - }); - - it('should update client by clientid', function (done) { - let opts = { - clientid: conf.demoClientId, - lastname: 'updated1' - }; - conf.whmcs.client.updateClient(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - - let opts = { - clientid: conf.demoClientId - }; - conf.whmcs.client.getClientsDetails(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('client').to.be.an.an('object'); - expect(details.client).to.have.a.property('lastname').to.equal('updated1'); - done(); - }); - }); - }); - - it('should update client by email', function (done) { - let opts = { - clientemail: conf.demoUserDetails.email, - lastname: 'updated2' - }; - conf.whmcs.client.updateClient(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - - let opts = { - clientid: conf.demoClientId - }; - conf.whmcs.client.getClientsDetails(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('client').to.be.an.an('object'); - expect(details.client).to.have.a.property('lastname').to.equal('updated2'); - done(); - }); - }); - }); - - it('should update contact by contact id', function (done) { - let opts = { - contactid: conf.demoContactId, - lastname: 'newlastname' - }; - conf.whmcs.client.updateContact(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - - let opts = { - userid: conf.demoClientId, - email: conf.demoContactDetails.email - }; - conf.whmcs.client.getContacts(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('contacts').to.be.an.an('object'); - expect(details.contacts).to.have.a.property('contact'); - expect(details.contacts.contact).to.be.an('array').to.have.lengthOf(1); - expect(details.contacts.contact[0]).to.have.a.property('lastname').to.equal('newlastname'); - done(); - }); - }); - }); - -}); \ No newline at end of file diff --git a/test/conf.js b/test/conf.js deleted file mode 100644 index d2f3fa5..0000000 --- a/test/conf.js +++ /dev/null @@ -1,209 +0,0 @@ -const WHMCS = require('../whmcs'); - -let config = { - apiIdentifier: process.env.WHMCS_API_IDENTIFIER || 'apiIdentifier', - apiSecret: process.env.WHMCS_API_SECRET || 'apiSecret', - serverUrl: process.env.WHMCS_URL || 'http://192.168.1.1', - userAgent: process.env.WHMCS_USERAGENT || 'node-whmcs' -}; - -const whmcs = new WHMCS(config); - -let userDetails = { - firstname: 'John', - lastname: 'Doe', - email: 'johndoe@john.doe', - address1: 'Mars', - city: 'Phobos', - state: 'Crater', - postcode: '9999-999', - country: 'US', - phonenumber: '123456789', - password2: '123qwe' -}; - -let contactDetails = { - firstname: 'Ground', - lastname: 'Control', - email: 'groundcontrol@john.doe', - address1: 'Earth', - city: 'Phobos', - state: 'Crater', - postcode: '9999-999', - country: 'US', - phonenumber: '911911911' -} - -function initialize(done) { - console.log('Preparing the test environment. Please wait...'); - - whmcs.client.addClient(userDetails, function (err, res) { - if (err) { - done(err); - } else { - module.exports.demoUserId = res.owner_id; - module.exports.demoClientId = res.clientid; - - contactDetails.clientid = res.clientid; - whmcs.client.addContact(contactDetails, function (err, details) { - if (err) { - done(err); - } else { - module.exports.demoContactId = details.contactid; - console.log('Test environment initialization complete.'); - done(); - } - }); - } - }); -} - -function rollback(done) { - console.log('Removing the temporary data. Please wait...'); - - let opts = { - clientid: module.exports.demoClientId, - deleteusers: true, - deletetransactions: true - }; - whmcs.client.deleteClient(opts, function (err, res) { - done(err); - }); -} - -before(function (done) { - this.timeout(60000); - initialize(done); -}); - -after(function (done) { - this.timeout(60000); - rollback(done); -}); - -function serialize(mixed_value) { - // http://kevin.vanzonneveld.net - // + original by: Arpad Ray (mailto:arpad@php.net) - // + improved by: Dino - // + bugfixed by: Andrej Pavlovic - // + bugfixed by: Garagoth - // + input by: DtTvB (http://dt.in.th/2008-09-16.string-length-in-bytes.html) - // + bugfixed by: Russell Walker (http://www.nbill.co.uk/) - // + bugfixed by: Jamie Beck (http://www.terabit.ca/) - // + input by: Martin (http://www.erlenwiese.de/) - // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net/) - // + improved by: Le Torbi (http://www.letorbi.de/) - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net/) - // + bugfixed by: Ben (http://benblume.co.uk/) - // % note: We feel the main purpose of this function should be to ease the transport of data between php & js - // % note: Aiming for PHP-compatibility, we have to translate objects to arrays - // * example 1: serialize(['Kevin', 'van', 'Zonneveld']); - // * returns 1: 'a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}' - // * example 2: serialize({firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'}); - // * returns 2: 'a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}' - let val, key, okey, - ktype = '', - vals = '', - count = 0, - _utf8Size = function (str) { - let size = 0, - i = 0, - l = str.length, - code = ''; - for (i = 0; i < l; i++) { - code = str.charCodeAt(i); - if (code < 0x0080) { - size += 1; - } else if (code < 0x0800) { - size += 2; - } else { - size += 3; - } - } - return size; - }, - _getType = function (inp) { - let match, key, cons, types, type = typeof inp; - - if (type === 'object' && !inp) { - return 'null'; - } - if (type === 'object') { - if (!inp.constructor) { - return 'object'; - } - cons = inp.constructor.toString(); - match = cons.match(/(\w+)\(/); - if (match) { - cons = match[1].toLowerCase(); - } - types = ['boolean', 'number', 'string', 'array']; - for (key in types) { - if (cons == types[key]) { - type = types[key]; - break; - } - } - } - return type; - }, - type = _getType(mixed_value); - - switch (type) { - case 'function': - val = ''; - break; - case 'boolean': - val = 'b:' + (mixed_value ? '1' : '0'); - break; - case 'number': - val = (Math.round(mixed_value) == mixed_value ? 'i' : 'd') + ':' + mixed_value; - break; - case 'string': - val = 's:' + _utf8Size(mixed_value) + ':"' + mixed_value + '"'; - break; - case 'array': - case 'object': - val = 'a'; - /* - if (type === 'object') { - let objname = mixed_value.constructor.toString().match(/(\w+)\(\)/); - if (objname == undefined) { - return; - } - objname[1] = this.serialize(objname[1]); - val = 'O' + objname[1].substring(1, objname[1].length - 1); - } - */ - - for (key in mixed_value) { - if (mixed_value.hasOwnProperty(key)) { - ktype = _getType(mixed_value[key]); - if (ktype === 'function') { - continue; - } - - okey = (key.match(/^[0-9]+$/) ? parseInt(key, 10) : key); - vals += serialize(okey) + serialize(mixed_value[key]); - count++; - } - } - val += ':' + count + ':{' + vals + '}'; - break; - default: - // if the JS object has a property which contains a null value, the string cannot be unserialized by PHP - val = 'N'; - break; - } - if (type !== 'object' && type !== 'array') { - val += ';'; - } - return val; -}; - -module.exports = { - whmcs: whmcs, - demoUserDetails: userDetails, - demoContactDetails: contactDetails, - serialize: serialize -}; diff --git a/test/domains.js b/test/domains.js deleted file mode 100644 index aa69390..0000000 --- a/test/domains.js +++ /dev/null @@ -1,320 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -function isRegistrarError(msg) { - let errorMessages = ['Registrar Error Message', 'Registrar Function Not Supported', 'No response from API command']; - - for (let i = 0; i < errorMessages.length; i++) { - if (msg.indexOf(errorMessages[i]) > -1) { - return true; - } - } - - return false; -}; - -describe('Module "Domains"', function () { - - it('should create or update tld', function (done) { - let opts = { - extension: '.com' - }; - conf.whmcs.domains.createOrUpdateTLD(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('extension').to.be.equal(opts.extension); - done(); - }); - }); - - it('should get tld pricing', function (done) { - this.timeout(30000); - - let opts = { - clientid: conf.demoClientId - }; - conf.whmcs.domains.getTLDPricing(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('pricing').to.be.an('object'); - done(); - }); - }); - - describe('Domain', function () { - let demoPaymentMethod, demoDomainId, demoOrderId; - - before(function (done) { - this.timeout(30000); - conf.whmcs.system.getPaymentMethods(function (err, details) { - if (err) { - throw err; - } else if (!details.paymentmethods || !details.paymentmethods.paymentmethod || !details.paymentmethods.paymentmethod[0]) { - throw new Error('Payment methods do not exist. You must create a new payment method first.'); - } else { - demoPaymentMethod = details.paymentmethods.paymentmethod[0].module; - - let opts = { - clientid: conf.demoClientId, - paymentmethod: demoPaymentMethod, - 'domain[0]': 'domaintest.com', - 'domaintype[0]': 'register', - 'regperiod[0]': 1 - }; - conf.whmcs.orders.addOrder(opts, function (err, details) { - if (err) { - throw err; - } else if (!details.orderid) { - throw new Error('Cannot get the order id. Cannot proceed.'); - } else { - demoOrderId = details.orderid; - } - - let opts = { - domain: 'domaintest.com', - limitstart: 0, - limitnum: 1 - }; - conf.whmcs.client.getClientsDomains(opts, function (err, details) { - if (err) { - throw err; - } else if (!details.domains || !details.domains.domain || details.domains.domain.length === 0) { - throw new Error('Cannot get the domain. Cannot proceed.'); - } else { - demoDomainId = details.domains.domain[0].id; - done(); - } - }); - }); - } - }); - }); - - it('should get locking status', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainGetLockingStatus(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('lockstatus').to.not.be.null; - done(); - }); - }); - - it('should get nameservers', function (done) { - this.timeout(60000); - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainGetNameservers(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('ns1'); - done(); - } - }); - }); - - it('should get whois information', function (done) { - this.timeout(60000); - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainGetWhoisInfo(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should send a register command to command to registrar module', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainRegister(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should send a release command to registrar module', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainRelease(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should send a renew command to registrar module', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainRenew(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should send an epp command to registrar module', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainRequestEPP(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should send the toggle ID protect command to registrar module', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainToggleIdProtect(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should send the domain transfer command to registrar module', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainTransfer(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should send the update lock command to registrar module', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainUpdateLockingStatus(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should send the save nameservers command to registrar module', function (done) { - let opts = { - domainid: demoDomainId, - ns1: 'ns1.domaintest.com', - ns2: 'ns2.domaintest.com' - }; - - conf.whmcs.domains.domainUpdateNameservers(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should send the save whois command to registrar module', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainUpdateWhoisInfo(opts, function (err, details) { - expect(err).to.not.be.null; - expect(err).to.have.a.property('message'); - expect(err.message).to.have.string('XML Required') - done(); - }); - }); - - it('should retrieve whois information', function (done) { - let opts = { - domainid: demoDomainId - }; - - conf.whmcs.domains.domainWhois(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('status'); - done(); - } - }); - }); - - it('should update a domain', function (done) { - let opts = { - domainid: demoDomainId, - idprotection: false - }; - - conf.whmcs.domains.updateClientDomain(opts, function (err, details) { - if (err && isRegistrarError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - }); - -}); \ No newline at end of file diff --git a/test/internal.js b/test/internal.js deleted file mode 100644 index c3c8f97..0000000 --- a/test/internal.js +++ /dev/null @@ -1,127 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'), - WHMCS = require('../whmcs'), - Bluebird = require('bluebird'); - -describe('Internal', function () { - it('should call an action by name', function (done) { - let opts = { - limitstart: 0, - limitnum: 1 - } - conf.whmcs.callApi('GetActivityLog', opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should handle native promises', function (done) { - let opts = { - limitstart: 0, - limitnum: 1 - }; - - conf.whmcs.system.getActivityLog(opts) - .then(function (details) { - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }) - .catch(function (err) { - expect(err).to.be.null; - done(); - }); - }); - - it('should handle custom promises library', function (done) { - let config = { - apiIdentifier: process.env.WHMCS_API_IDENTIFIER || 'apiIdentifier', - apiSecret: process.env.WHMCS_API_SECRET || 'apiSecret', - serverUrl: process.env.WHMCS_URL || 'http://192.168.1.1', - userAgent: process.env.WHMCS_USERAGENT || 'node-whmcs', - Promise: Bluebird, - }; - - let whmcs = new WHMCS(config); - - let opts = { - limitstart: 0, - limitnum: 1 - }; - - whmcs.system.getActivityLog(opts) - .then(function (details) { - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }) - .catch(function (err) { - expect(err).to.be.null; - done(); - }); - }); - - it('should throw an error if Promise library is invalid', function () { - let config = { - apiIdentifier: process.env.WHMCS_API_IDENTIFIER || 'apiIdentifier', - apiSecret: process.env.WHMCS_API_SECRET || 'apiSecret', - serverUrl: process.env.WHMCS_URL || 'http://192.168.1.1', - userAgent: process.env.WHMCS_USERAGENT || 'node-whmcs', - Promise: {} - }; - - let fn = function () { - return new WHMCS(config); - }; - - expect(fn).to.throw(Error, 'Invalid promise library.'); - }); - - it('should handle XML response', function (done) { - let config = { - apiIdentifier: process.env.WHMCS_API_IDENTIFIER || 'apiIdentifier', - apiSecret: process.env.WHMCS_API_SECRET || 'apiSecret', - serverUrl: process.env.WHMCS_URL || 'http://192.168.1.1', - userAgent: process.env.WHMCS_USERAGENT || 'node-whmcs', - responseType: 'xml' - }; - - let whmcs = new WHMCS(config); - - let opts = { - limitstart: 0, - limitnum: 1 - }; - - whmcs.system.getActivityLog(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should authenticate with username and password', function (done) { - let config = { - username: process.env.WHMCS_USER || 'username', - password: process.env.WHMCS_PASSWORD || 'password', - serverUrl: process.env.WHMCS_URL || 'http://192.168.1.1', - userAgent: process.env.WHMCS_USERAGENT || 'node-whmcs' - }; - - let whmcs = new WHMCS(config); - - let opts = { - limitstart: 0, - limitnum: 1 - }; - - whmcs.system.getActivityLog(opts) - .then(function (details) { - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }) - .catch(function (err) { - expect(err).to.be.null; - done(); - }); - }); -}); \ No newline at end of file diff --git a/test/module.js b/test/module.js deleted file mode 100644 index 6113f54..0000000 --- a/test/module.js +++ /dev/null @@ -1,73 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Module"', function () { - - it('should get the module queue', function (done) { - conf.whmcs.module.getModuleQueue(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('queue').to.be.an('array'); - done(); - }); - }); - - it('should get the module configuration parameters', function (done) { - let opts = { - moduleType: 'gateway', - moduleName: 'paypal' - }; - - conf.whmcs.module.getModuleConfigurationParameters(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('parameters').to.be.an('array'); - done(); - }); - }); - - it('should activate a module', function (done) { - let opts = { - moduleType: 'gateway', - moduleName: 'paypal' - }; - - conf.whmcs.module.activateModule(opts, function (err, details) { - if (err && err.message.indexOf('Failed to activate: Module already active') > -1) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should update module configuration parameters', function (done) { - let opts = { - moduleType: 'gateway', - moduleName: 'paypal' - }; - - conf.whmcs.module.updateModuleConfiguration(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should deactivate a module', function (done) { - let opts = { - moduleType: 'gateway', - moduleName: 'paypal', - newGateway: 'paypal' - }; - - conf.whmcs.module.deactivateModule(opts, function (err, details) { - expect(err).to.have.a.property('message'); - expect(err.message).to.have.string('Module deactivation not supported by module type'); - done(); - }); - }); - -}); \ No newline at end of file diff --git a/test/orders.js b/test/orders.js deleted file mode 100644 index 2ca89f4..0000000 --- a/test/orders.js +++ /dev/null @@ -1,228 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Orders"', function () { - - it('should get orders', function (done) { - let opts = { - limitstart: 0, - limitnum: 1 - }; - - conf.whmcs.orders.getOrders(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('orders').to.be.an('object'); - expect(details.orders).to.have.a.property('order').to.be.an('array'); - done(); - }); - }); - - it('should get order statuses', function (done) { - conf.whmcs.orders.getOrderStatuses(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('statuses').to.be.an('object'); - expect(details.statuses).to.have.a.property('status').to.be.an('array'); - done(); - }); - }); - - describe('Product', function () { - let demoPid; - - before(function (done) { - let opts = { - name: 'Test product', - gid: 1, - type: 'hostingaccount' - }; - conf.whmcs.products.addProduct(opts, function (err, details) { - if (err) { - throw err; - } else if (details.pid == undefined) { - throw new Error('Cannot get the created product ID. Cannot proceed.'); - } else { - demoPid = details.pid; - done(); - } - }); - }); - - it('should get products', function (done) { - let opts = { - pid: demoPid - }; - - conf.whmcs.orders.getProducts(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('products').to.be.an('object'); - expect(details.products).to.have.a.property('product').to.be.an('array'); - done(); - }); - }); - }); - - it('should get promotions', function (done) { - conf.whmcs.orders.getPromotions(function (err, details) { - expect(details).to.have.a.property('result').to.equal('success'); - if(details.totalResults > 0) { - expect(details).to.have.a.property('promotions').to.be.an('object'); - expect(details.promotions).to.have.a.property('promotion').to.be.an('array'); - } - done(); - }); - }); - - describe('Order', function () { - let demoPaymentMethod, demoOrderId; - - before(function (done) { - conf.whmcs.system.getPaymentMethods(function (err, details) { - if (err) { - throw err; - } else if (!details.paymentmethods || !details.paymentmethods.paymentmethod || !details.paymentmethods.paymentmethod[0]) { - throw new Error('Payment methods do not exist. You must create a new payment method first.'); - } else { - demoPaymentMethod = details.paymentmethods.paymentmethod[0].module; - done(); - } - }); - }); - - it('should add an order', function (done) { - let opts = { - clientid: conf.demoClientId, - paymentmethod: demoPaymentMethod, - 'domain[0]': 'domaintest.com', - 'domaintype[0]': 'register', - 'regperiod[0]': 1 - }; - conf.whmcs.orders.addOrder(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('orderid'); - demoOrderId = details.orderid; - done(); - }); - }); - - it('should mark the order as fraudulent', function (done) { - if (!demoOrderId) { - this.skip(); - } else { - let opts = { - orderid: demoOrderId - }; - conf.whmcs.orders.fraudOrder(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - - it('should run a fraud check', function (done) { - if (!demoOrderId) { - this.skip(); - } else { - let opts = { - orderid: demoOrderId - }; - conf.whmcs.orders.orderFraudCheck(opts, function (err, details) { - if (err && err.message.indexOf('No Active Fraud Module') > -1) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - } - }); - - it('should set an order to pending', function (done) { - if (!demoOrderId) { - this.skip(); - } else { - let opts = { - orderid: demoOrderId - }; - conf.whmcs.orders.pendingOrder(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - - it('should cancel an order', function (done) { - if (!demoOrderId) { - this.skip(); - } else { - let opts = { - orderid: demoOrderId - }; - conf.whmcs.orders.cancelOrder(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - - describe('Order accept', function () { - before(function (done) { - let opts = { - clientid: conf.demoClientId, - paymentmethod: demoPaymentMethod, - 'domain[0]': 'domaintest.com', - 'domaintype[0]': 'register', - 'regperiod[0]': 1 - }; - conf.whmcs.orders.addOrder(opts, function (err, details) { - if (err) { - throw err; - } else if (details.orderid == undefined) { - throw new Error('Cannot get the order ID. Cannot proceed.'); - } else { - demoOrderId = details.orderid; - done(); - } - }); - }); - - it('should accept an order', function (done) { - if (!demoOrderId) { - this.skip(); - } else { - let opts = { - orderid: demoOrderId - }; - conf.whmcs.orders.acceptOrder(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - }); - - it('should delete an order', function (done) { - if (!demoOrderId) { - this.skip(); - } else { - let opts = { - orderid: demoOrderId - }; - conf.whmcs.orders.deleteOrder(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - }); - -}); \ No newline at end of file diff --git a/test/products.js b/test/products.js deleted file mode 100644 index 5cf8181..0000000 --- a/test/products.js +++ /dev/null @@ -1,20 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Products"', function () { - - it('should create a new product', function (done) { - let opts = { - name: 'Test product', - gid: 1, - type: 'hostingaccount', - - }; - conf.whmcs.products.addProduct(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('pid'); - done(); - }); - }); -}); \ No newline at end of file diff --git a/test/projectmanagement.js b/test/projectmanagement.js deleted file mode 100644 index 52b0268..0000000 --- a/test/projectmanagement.js +++ /dev/null @@ -1,230 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Project Management"', function () { - let demoProjectId, demoTaskId, demoTimerId; - - before(function (done) { - const _this = this; - let opts = { - title: 'check if feature is enabled', - adminid: 1 - }; - - conf.whmcs.projectManagement.createProject(opts, function (err, details) { - if (err && err.message && err.message.indexOf('Project Management is not active.') > -1) { - console.log('Project Management is not active. Some tests will be skipped.'); - _this.skip(); - } else { - done(); - } - }); - }); - - it('should create a project', function (done) { - let opts = { - title: 'untitled project', - adminid: 1 - }; - - conf.whmcs.projectManagement.createProject(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('projectid'); - demoProjectId = details.projectid; - done(); - }); - }); - - it('should get a project by id', function (done) { - let opts = { - projectid: demoProjectId - }; - conf.whmcs.projectManagement.getProject(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('projectinfo').to.be.an.an('object'); - done(); - }); - }); - - it('should get projects', function (done) { - let opts = { - limitstart: 0, - limitnum: 1 - }; - conf.whmcs.projectManagement.getProjects(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('projects').to.be.an.an('array'); - done(); - }); - }); - - it('should get projects by clientid', function (done) { - let opts = { - limitstart: 0, - limitnum: 1, - userid: conf.demoClientId - }; - conf.whmcs.projectManagement.getProjects(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('projects').to.be.an.an('array'); - done(); - }); - }); - - it('should update a project', function (done) { - let opts = { - projectid: demoProjectId, - title: 'space oddity' - }; - conf.whmcs.projectManagement.updateProject(opts, function (err, details) { - expect(err).to.be.null; - - let opts = { - projectid: demoProjectId - }; - conf.whmcs.projectManagement.getProject(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('projectinfo').to.be.an.an('object'); - expect(details.projectinfo).to.have.a.property('title').to.equal('space oddity'); - done(); - }); - }); - }); - - it('should add a message to a project', function (done) { - let opts = { - projectid: demoProjectId, - message: 'can you hear me major tom?' - }; - conf.whmcs.projectManagement.addProjectMessage(opts, function (err, details) { - expect(err).to.be.null; - - let opts = { - projectid: demoProjectId - }; - conf.whmcs.projectManagement.getProject(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('messages').to.be.an.an('object'); - expect(details.messages).to.have.a.property('message').to.be.an('array').to.have.lengthOf(1); - expect(details.messages.message[0]).to.have.a.property('message').to.equal('can you hear me major tom?'); - done(); - }); - }); - }); - - it('should add a task to a project', function (done) { - let opts = { - projectid: demoProjectId, - duedate: '1969-07-11', - task: 'leave the capsule' - }; - conf.whmcs.projectManagement.addProjectTask(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - - let opts = { - projectid: demoProjectId - }; - conf.whmcs.projectManagement.getProject(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('tasks').to.be.an.an('object'); - expect(details.tasks).to.have.a.property('task').to.be.an('array').to.have.lengthOf(1); - expect(details.tasks.task[0]).to.have.a.property('task').to.equal('leave the capsule'); - demoTaskId = details.tasks.task[0].id; - done(); - }); - }); - }); - - it('should update a project task', function (done) { - let opts = { - taskid: demoTaskId, - task: 'step through the door' - }; - conf.whmcs.projectManagement.updateProjectTask(opts, function (err, details) { - expect(err).to.be.null; - - let opts = { - projectid: demoProjectId - }; - conf.whmcs.projectManagement.getProject(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('tasks').to.be.an.an('object'); - expect(details.tasks).to.have.a.property('task').to.be.an('array').to.have.lengthOf(1); - expect(details.tasks.task[0]).to.have.a.property('task').to.equal('step through the door'); - done(); - }); - }); - }); - - it('should start a project task timer', function (done) { - let opts = { - taskid: demoTaskId, - projectid: demoProjectId - }; - conf.whmcs.projectManagement.startTaskTimer(opts, function (err, details) { - expect(err).to.be.null; - - let opts = { - projectid: demoProjectId - }; - conf.whmcs.projectManagement.getProject(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('tasks').to.be.an.an('object'); - expect(details.tasks).to.have.a.property('task').to.be.an('array').to.have.lengthOf(1); - expect(details.tasks.task[0]).to.have.a.property('timelogs').to.be.an.an('object'); - expect(details.tasks.task[0].timelogs).to.have.a.property('timelog').to.be.an.an('array').to.have.lengthOf(1); - expect(details.tasks.task[0].timelogs.timelog[0]).to.have.a.property('starttime'); - expect(details.tasks.task[0].timelogs.timelog[0]).to.have.a.property('endtime'); - demoTimerId = details.tasks.task[0].timelogs.timelog[0].id; - done(); - }); - }); - }); - - it('should end a project task timer', function (done) { - let opts = { - timerid: demoTimerId, - projectid: demoProjectId - }; - conf.whmcs.projectManagement.endTaskTimer(opts, function (err, details) { - expect(err).to.be.null; - - let opts = { - projectid: demoProjectId - }; - conf.whmcs.projectManagement.getProject(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('tasks').to.be.an.an('object'); - expect(details.tasks).to.have.a.property('task').to.be.an('array').to.have.lengthOf(1); - expect(details.tasks.task[0]).to.have.a.property('timelogs').to.be.an.an('object'); - expect(details.tasks.task[0].timelogs).to.have.a.property('timelog').to.be.an.an('array').to.have.lengthOf(1); - expect(details.tasks.task[0].timelogs.timelog[0]).to.have.a.property('starttime'); - expect(details.tasks.task[0].timelogs.timelog[0]).to.have.a.property('endtime'); - done(); - }); - }); - }); - - it('should delete a project task', function (done) { - let opts = { - projectid: demoProjectId, - taskid: demoTaskId - }; - conf.whmcs.projectManagement.deleteProjectTask(opts, function (err, details) { - expect(err).to.be.null; - - let opts = { - projectid: demoProjectId - }; - conf.whmcs.projectManagement.getProject(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.not.have.a.property('tasks'); - done(); - }); - }); - }); -}); \ No newline at end of file diff --git a/test/servers.js b/test/servers.js deleted file mode 100644 index 61ee2ef..0000000 --- a/test/servers.js +++ /dev/null @@ -1,22 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Servers"', function () { - - it('should get servers', function (done) { - conf.whmcs.servers.getServers(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should get health status', function (done) { - conf.whmcs.servers.getHealthStatus(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - -}); \ No newline at end of file diff --git a/test/service.js b/test/service.js deleted file mode 100644 index 8296c81..0000000 --- a/test/service.js +++ /dev/null @@ -1,195 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -function isModuleNotAssignedError(msg) { - return msg.indexOf('Service not assigned to a module') > -1; -} - -describe('Module "Service"', function () { - let demoPid, demoPaymentMethod, demoOrderId, demoServiceId; - - before(function (done) { - this.timeout(30000); - - let opts = { - name: 'Test product', - gid: 1, - type: 'hostingaccount', - - }; - conf.whmcs.products.addProduct(opts, function (err, details) { - if (err) { - throw err; - } else if (details.pid == undefined) { - throw new Error('Cannot get the pid. Cannot proceed.'); - } else { - demoPid = details.pid; - - conf.whmcs.system.getPaymentMethods(function (err, details) { - if (err) { - throw err; - } else if (!details.paymentmethods || !details.paymentmethods.paymentmethod || !details.paymentmethods.paymentmethod[0]) { - throw new Error('Payment methods do not exist. You must create a new payment method first.'); - } else { - demoPaymentMethod = details.paymentmethods.paymentmethod[0].module; - - let opts = { - clientid: conf.demoClientId, - paymentmethod: demoPaymentMethod, - 'pid[0]': demoPid, - 'domain[0]': 'hostingtest.com', - 'billingcycle[0]': 'monthly', - 'priceoverride[0]': 1 - }; - conf.whmcs.orders.addOrder(opts, function (err, details) { - if (err) { - throw err; - } else if (!details.orderid) { - throw new Error('Cannot get the order id. Cannot proceed.'); - } else { - demoOrderId = details.orderid; - } - - let opts = { - domain: 'hostingtest.com', - limitstart: 0, - limitnum: 1 - }; - conf.whmcs.client.getClientsProducts(opts, function (err, details) { - if (err) { - throw err; - } else if (!details.products || !details.products.product || details.products.product.length === 0) { - throw new Error('Cannot get the product. Cannot proceed.'); - } else { - demoServiceId = details.products.product[0].id; - done(); - } - }); - }); - } - }); - } - }); - - - }); - - it('should update a client service', function (done) { - let opts = { - serviceid: demoServiceId, - notes: 'this service was updated' - }; - conf.whmcs.service.updateClientProduct(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should run the module create', function (done) { - let opts = { - serviceid: demoServiceId - }; - conf.whmcs.service.moduleCreate(opts, function (err, details) { - if (err && isModuleNotAssignedError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should run the change package action', function (done) { - let opts = { - serviceid: demoServiceId - }; - conf.whmcs.service.moduleChangePackage(opts, function (err, details) { - if (err && isModuleNotAssignedError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should run the change pw action', function (done) { - let opts = { - serviceid: demoServiceId - }; - conf.whmcs.service.moduleChangePw(opts, function (err, details) { - if (err && isModuleNotAssignedError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should run a custom module action', function (done) { - let opts = { - serviceid: demoServiceId, - func_name: 'test' - }; - conf.whmcs.service.moduleCustom(opts, function (err, details) { - if (err && isModuleNotAssignedError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should run the module suspend action', function (done) { - let opts = { - serviceid: demoServiceId - }; - conf.whmcs.service.moduleSuspend(opts, function (err, details) { - if (err && isModuleNotAssignedError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should run the module unsuspend action', function (done) { - let opts = { - serviceid: demoServiceId - }; - conf.whmcs.service.moduleUnsuspend(opts, function (err, details) { - if (err && isModuleNotAssignedError(err.message)) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should update or calculate an upgrade on a product', function (done) { - let opts = { - serviceid: demoServiceId, - type: 'product', - calconly: true, - newproductid: demoPid, - newproductbillingcycle: 'monthly' - }; - conf.whmcs.service.upgradeProduct(opts, function (err, details) { - expect(err).to.have.a.property('message'); - expect(err.message).to.have.string('Invalid Billing Cycle Requested'); - done(); - }); - }); - -}); \ No newline at end of file diff --git a/test/support.js b/test/support.js deleted file mode 100644 index d87df7b..0000000 --- a/test/support.js +++ /dev/null @@ -1,366 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Support"', function () { - describe('Announcement', function () { - let demoAnnouncementId; - - it('should add an announcement', function (done) { - let opts = { - date: '1969-07-11', - title: 'There\'s something wrong', - announcement: 'Your circuit\'s dead' - }; - - conf.whmcs.support.addAnnouncement(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('announcementid'); - demoAnnouncementId = details.announcementid; - done(); - }); - }); - - it('should get announcements', function (done) { - let opts = { - limitstart: 0, - limitnum: 1 - }; - - conf.whmcs.support.getAnnouncements(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('announcements').to.be.an.an('object'); - expect(details.announcements).to.have.a.property('announcement').to.be.an('array').to.have.length.above(0); - done(); - }); - }); - - it('should delete an announcement', function (done) { - if (demoAnnouncementId == undefined) { - this.skip(); - } else { - let opts = { - announcementid: demoAnnouncementId - }; - - conf.whmcs.support.deleteAnnouncement(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - - }); - - it('should add a cancel request', function (done) { - let opts = { - serviceid: 1 - }; - - conf.whmcs.support.addCancelRequest(opts, function (err, details) { - if (err && err.message.indexOf('Existing Cancellation Request Exists') > -1) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should add a client note', function (done) { - let opts = { - userid: conf.demoClientId, - notes: 'Planet Earth is blue and there\'s nothing I can do' - }; - - conf.whmcs.support.addClientNote(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - describe('Ticket', function () { - let demoTicketId; - - it('should open a ticket', function (done) { - let opts = { - deptid: 1, - clientid: conf.demoClientId, - subject: 'this is a subject', - message: 'this is a message' - }; - - conf.whmcs.support.openTicket(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('id'); - expect(details).to.have.a.property('tid'); - expect(details).to.have.a.property('c'); - demoTicketId = details.id; - done(); - }); - }); - - it('should add a note to the ticket', function (done) { - if (demoTicketId == undefined) { - this.skip(); - } else { - let opts = { - message: 'this is a ticket note', - ticketid: demoTicketId - }; - - conf.whmcs.support.addTicketNote(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - - it('should add a reply to the ticket', function (done) { - if (demoTicketId == undefined) { - this.skip(); - } else { - let opts = { - ticketid: demoTicketId, - clientid: conf.demoClientId, - message: 'this is a new reply' - }; - - conf.whmcs.support.addTicketReply(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - - it('should block a ticket sender', function (done) { - if (demoTicketId == undefined) { - this.skip(); - } else { - let opts = { - ticketid: demoTicketId - }; - - conf.whmcs.support.blockTicketSender(opts, function (err, details) { - if (err && err.message.indexOf('A Client Cannot Be Blocked') > -1) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - } - }); - - it('should update a ticket', function (done) { - if (demoTicketId == undefined) { - this.skip(); - } else { - let opts = { - ticketid: demoTicketId, - subject: 'this is an updated ticket' - }; - - conf.whmcs.support.updateTicket(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('ticketid').to.equal(demoTicketId); - done(); - }); - } - }); - - it('should create another ticket and merge it', function (done) { - if (demoTicketId == undefined) { - this.skip(); - } else { - let opts = { - deptid: 1, - clientid: conf.demoClientId, - subject: 'this is another subject', - message: 'this is another message' - }; - - conf.whmcs.support.openTicket(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('id'); - expect(details).to.have.a.property('tid'); - expect(details).to.have.a.property('c'); - - let opts = { - ticketid: demoTicketId, - mergeticketids: details.id, - newsubject: 'this is a merged ticket' - }; - - conf.whmcs.support.mergeTicket(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('ticketid').to.equal(demoTicketId); - done(); - }); - }); - } - }); - - it('should delete a ticket', function (done) { - if (demoTicketId == undefined) { - this.skip(); - } else { - let opts = { - ticketid: demoTicketId - }; - - conf.whmcs.support.deleteTicket(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - } - }); - }); - - describe('Ticket reply', function () { - let demoTicketId, demoReplyId; - - before(function (done) { - let opts = { - deptid: 1, - clientid: conf.demoClientId, - subject: 'this is a subject', - message: 'this is a message' - }; - - conf.whmcs.support.openTicket(opts, function (err, details) { - if (err) { - throw (err); - } else { - demoTicketId = details.id; - - let opts = { - ticketid: demoTicketId, - clientid: conf.demoClientId, - message: 'this is a new reply' - }; - - conf.whmcs.support.addTicketReply(opts, function (err, details) { - if (err) { - throw err; - } else { - let opts = { - ticketid: demoTicketId - }; - - conf.whmcs.tickets.getTicket(opts, function (err, details) { - if (err) { - throw err; - } else if (!details || !details.replies || !details.replies.reply || !details.replies.reply[0]) { - throw new Error('Ticket must have a reply. Cannot proceed.'); - } else { - demoReplyId = details.replies.reply[0].replyid; - done(); - } - }); - } - }); - } - }); - - it('should update a ticket reply', function (done) { - let opts = { - replyid: demoReplyId, - //ticketid: demoTicketId, - message: 'this is an updated reply' - }; - - conf.whmcs.support.updateTicketReply(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should delete a ticket reply', function (done) { - let opts = { - //ticketid: demoTicketId, - replyid: demoReplyId - }; - - conf.whmcs.support.deleteTicketReply(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - }); - }); - - describe('Ticket note', function () { - let demoTicketId, demoNoteId; - - before(function (done) { - let opts = { - deptid: 1, - clientid: conf.demoClientId, - subject: 'this is a subject', - message: 'this is a message' - }; - - conf.whmcs.support.openTicket(opts, function (err, details) { - if (err) { - throw (err); - } else { - demoTicketId = details.id; - - let opts = { - message: 'this is a ticket note', - ticketid: demoTicketId - }; - - conf.whmcs.support.addTicketNote(opts, function (err, details) { - if (err) { - throw err; - } else { - let opts = { - ticketid: demoTicketId - }; - - conf.whmcs.tickets.getTicket(opts, function (err, details) { - if (err) { - throw err; - } else if (!details || !details.notes || !details.notes.note || !details.notes.note[0]) { - throw new Error('Ticket must have a note. Cannot proceed.'); - } else { - demoNoteId = details.notes.note[0].noteid; - done(); - } - }); - } - }); - } - }); - }); - - it('should delete a ticket note', function (done) { - let opts = { - noteid: demoNoteId - }; - - conf.whmcs.support.deleteTicketNote(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - }); - -}); \ No newline at end of file diff --git a/test/system.js b/test/system.js deleted file mode 100644 index 489695d..0000000 --- a/test/system.js +++ /dev/null @@ -1,295 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "System"', function () { - - it('should add an ip to ban list', function (done) { - let opts = { - ip: '1.2.3.4', - reason: 'just because', - days: 1 - }; - - conf.whmcs.system.addBannedIp(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - describe('Password encryption', function () { - let demoPassword = 'n2w47bVW#QABW63vVw', - encryptedPassword; - - it('should encrypt a password', function (done) { - let opts = { - password2: demoPassword - }; - - conf.whmcs.system.encryptPassword(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('password'); - encryptedPassword = details.password; - done(); - }); - }); - - it('should decrypt a password', function (done) { - if (!encryptedPassword) { - this.skip(); - } else { - let opts = { - password2: encryptedPassword - }; - - conf.whmcs.system.decryptPassword(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('password').to.equal(demoPassword); - done(); - }); - } - }); - }); - - it('should get activity log', function (done) { - let opts = { - limitstart: 0, - limitnum: 1 - }; - - conf.whmcs.system.getActivityLog(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('activity').to.be.an('object'); - expect(details.activity).to.have.a.property('entry').to.be.an('array'); - done(); - }); - }); - - it('should get admin details', function (done) { - conf.whmcs.system.getAdminDetails(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('adminid'); - done(); - }); - }); - - it('should get admin users', function (done) { - conf.whmcs.system.getAdminUsers(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('admin_users').to.be.an('array'); - done(); - }); - }); - - it('should get automation log', function (done) { - conf.whmcs.system.getAutomationLog(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('currentDatetime'); - done(); - }); - }); - - it('should get configuration value', function (done) { - let opts = { - setting: 'Language' - }; - conf.whmcs.system.getConfigurationValue(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('setting'); - done(); - }); - }); - - it('should get currencies', function (done) { - conf.whmcs.system.getCurrencies(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('currencies').to.be.an.an('object'); - expect(details.currencies).to.have.a.property('currency').to.be.an.an('array'); - done(); - }); - }); - - it('should get email templates', function (done) { - conf.whmcs.system.getEmailTemplates(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('emailtemplates').to.be.an.an('object'); - expect(details.emailtemplates).to.have.a.property('emailtemplate').to.be.an.an('array'); - done(); - }); - }); - - it('should get payment methods', function (done) { - conf.whmcs.system.getPaymentMethods(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('paymentmethods').to.be.an.an('object'); - expect(details.paymentmethods).to.have.a.property('paymentmethod').to.be.an.an('array'); - done(); - }); - }); - - it('should get staff online', function (done) { - conf.whmcs.system.getStaffOnline(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('staffonline').to.be.an.an('object'); - expect(details.staffonline).to.have.a.property('staff').to.be.an.an('array'); - done(); - }); - }); - - it('should get stats', function (done) { - conf.whmcs.system.getStats(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('income_today'); - done(); - }); - }); - - it('should get todo items', function (done) { - conf.whmcs.system.getToDoItems(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should get todo item statuses', function (done) { - conf.whmcs.system.getToDoItemStatuses(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('todoitemstatuses').to.be.an.an('object'); - expect(details.todoitemstatuses).to.have.a.property('status').to.be.an.an('array'); - done(); - }); - }); - - it('should create a log activity', function (done) { - let opts = { - clientid: conf.demoClientId, - description: 'log activity test' - }; - conf.whmcs.system.logActivity(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should send an admin email notification', function (done) { - let opts = { - customsubject: 'notification test', - custommessage: 'this is a notification test' - }; - conf.whmcs.system.sendAdminEmail(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should send a client email notification', function (done) { - let opts = { - id: conf.demoClientId, - customsubject: 'notification test', - custommessage: 'this is a notification test', - customtype: 'general' - }; - conf.whmcs.system.sendEmail(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should set a configuration value', function (done) { - let opts = { - setting: 'CompanyName', - value: 'My company' - }; - conf.whmcs.system.setConfigurationValue(opts, function (err, details) { - if (err && err.message.indexOf('API Command Restricted to Internal API') > -1) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - it('should trigger a custom notification event', function (done) { - let opts = { - title: 'Notification test', - message: 'this is a custom notification', - notification_identifier: 'test' - }; - conf.whmcs.system.triggerNotificationEvent(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should update admin notes', function (done) { - let opts = { - notes: 'This is a note' - }; - conf.whmcs.system.updateAdminNotes(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - describe('Announcements', function () { - let demoAnnouncementId; - - before(function (done) { - let opts = { - date: '1969-07-11', - title: 'There\'s something wrong', - announcement: 'Your circuit\'s dead' - }; - - conf.whmcs.support.addAnnouncement(opts, function (err, details) { - if (err) { - throw err; - } else { - demoAnnouncementId = details.announcementid; - done(); - } - }); - }); - - it('should update an announcement', function (done) { - let opts = { - announcementid: demoAnnouncementId, - title: 'Can you hear me Major Tom?' - }; - conf.whmcs.system.updateAnnouncement(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - }); - - it('should get whmcs details', function (done) { - conf.whmcs.system.whmcsDetails(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('whmcs'); - done(); - }); - }); -}); \ No newline at end of file diff --git a/test/tickets.js b/test/tickets.js deleted file mode 100644 index 5cff786..0000000 --- a/test/tickets.js +++ /dev/null @@ -1,133 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Tickets"', function () { - - it('should get support departments', function (done) { - conf.whmcs.tickets.getSupportDepartments(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('departments').to.be.an('object'); - expect(details.departments).to.have.a.property('department').to.be.an('array'); - done(); - }); - }); - - it('should get support statuses', function (done) { - conf.whmcs.tickets.getSupportStatuses(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('statuses').to.be.an('object'); - expect(details.statuses).to.have.a.property('status').to.be.an('array'); - done(); - }); - }); - - it('should get support statuses', function (done) { - conf.whmcs.tickets.getTicketCounts(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('allActive'); - done(); - }); - }); - - it('should get predefined cats', function (done) { - conf.whmcs.tickets.getTicketPredefinedCats(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('totalresults'); - done(); - }); - }); - - it('should get predefined replies', function (done) { - conf.whmcs.tickets.getTicketPredefinedCats(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('totalresults'); - done(); - }); - }); - - describe('Ticket', function () { - let demoTicketId; - - before(function (done) { - let opts = { - deptid: 1, - clientid: conf.demoClientId, - subject: 'this is a subject', - message: 'this is a message' - }; - - conf.whmcs.support.openTicket(opts, function (err, details) { - if (err) { - throw err; - } else { - demoTicketId = details.id; - done(); - } - }); - }); - - it('should get tickets', function (done) { - let opts = { - limitstart: 0, - limitnum: 1 - }; - - conf.whmcs.tickets.getTickets(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should get ticket notes', function (done) { - let opts = { - ticketid: demoTicketId - }; - - conf.whmcs.tickets.getTicketNotes(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('notes').to.be.an('object'); - expect(details.notes).to.have.a.property('note').to.be.an('array'); - done(); - }); - }); - - it('should get a ticket by id', function (done) { - let opts = { - ticketid: demoTicketId - }; - - conf.whmcs.tickets.getTicket(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - expect(details).to.have.a.property('id').to.equal(demoTicketId); - done(); - }); - }); - - it('should get ticket attachment', function (done) { - let opts = { - relatedid: demoTicketId, - type: 'ticket', - index: 0 - }; - - conf.whmcs.tickets.getTicketAttachment(opts, function (err, details) { - if (err && err.message.indexOf('No Attachments Found') > -1) { - done(); - } else { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - } - }); - }); - - }); -}); \ No newline at end of file diff --git a/test/users.js b/test/users.js deleted file mode 100644 index 2c64cf3..0000000 --- a/test/users.js +++ /dev/null @@ -1,123 +0,0 @@ -const expect = require('chai').expect, - conf = require('./conf'); - -describe('Module "Users"', function () { - it('should unlink an user from client', function (done) { - done(); - }); - - it('should unlink a client from user', function (done) { - done(); - }); - - it('should send an invite to manage a client', function (done) { - let opts = { - client_id: conf.demoClientId, - email: 'johndoefriend@john.doe', - permissions: 'products,domains' - } - conf.whmcs.users.createClientInvite(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should get a list of permissions that can be used when creating a user', function (done) { - conf.whmcs.users.getPermissionsList(function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('permissions').to.be.an('object'); - expect(details.permissions).to.have.a.property('permission').to.be.an('array').to.have.length.above(0); - done(); - }); - }); - - it('should get the permissions of an user, for a client', function (done) { - let opts = { - user_id: conf.demoUserId, - client_id: conf.demoClientId - }; - conf.whmcs.users.getUserPermissions(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should get users according to search limit', function (done) { - let opts = { - limitstart: 0, - limitnum: 25 - }; - conf.whmcs.users.getUsers(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('users').to.be.an('array') - expect(details.users).to.have.length.above(0); - expect(details.users).to.have.length.below(26); - done(); - }); - }); - - it('should start the password reset process for an user, by user id', function (done) { - let opts = { - id: conf.demoUserId - }; - conf.whmcs.users.resetPassword(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should start the password reset process for an user, by email', function (done) { - let opts = { - email: conf.demoUserDetails.email - }; - conf.whmcs.users.resetPassword(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should update an user', function (done) { - let opts = { - user_id: conf.demoUserId, - lastname: 'updated lastname' - }; - conf.whmcs.users.updateUser(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('result').to.equal('success'); - done(); - }); - }); - - it('should update the permissions of an user, for a client', function (done) { - //TODO - // let opts = { - // user_id: xxx - // client_id: xxx - // permissions: 'domains,products' - // }; - // conf.whmcs.users.updateUser(opts, function (err, details) { - // expect(err).to.be.null; - // expect(details).to.have.a.property('result').to.equal('success'); - // done(); - // }); - done(); - }); - - it('should get an user by email', function (done) { - let opts = { - search: 'johndoe@john.doe' - } - conf.whmcs.users.getUsers(opts, function (err, details) { - expect(err).to.be.null; - expect(details).to.have.a.property('numreturned').to.equal(1); - expect(details).to.have.a.property('users'); - expect(details.users).to.be.an('array'); - expect(details.users).to.have.lengthOf(1); - done(); - }); - }); -}); \ No newline at end of file diff --git a/whmcs.js b/whmcs.js deleted file mode 100644 index eba69f4..0000000 --- a/whmcs.js +++ /dev/null @@ -1,41 +0,0 @@ -const WhmcsHttpClient = require('./lib/whmcshttpclient'); -const modules = require('./modules/index'); - -class WHMCS { - /** - * Creates a WHMCS object - * @param {Object} options Configuration parameters (key-value pairs) - */ - constructor(options) { - this.whmcsHttpClient = new WhmcsHttpClient(options); - - this.orders = new modules.Orders(this.whmcsHttpClient); - this.billing = new modules.Billing(this.whmcsHttpClient); - this.module = new modules.Module(this.whmcsHttpClient); - this.support = new modules.Support(this.whmcsHttpClient); - this.system = new modules.System(this.whmcsHttpClient); - this.client = new modules.Client(this.whmcsHttpClient); - this.products = new modules.Products(this.whmcsHttpClient); - this.projectManagement = new modules.ProjectManagement(this.whmcsHttpClient); - this.users = new modules.Users(this.whmcsHttpClient); - this.affiliates = new modules.Affiliates(this.whmcsHttpClient); - this.authentication = new modules.Authentication(this.whmcsHttpClient); - this.domains = new modules.Domains(this.whmcsHttpClient); - this.servers = new modules.Servers(this.whmcsHttpClient); - this.tickets = new modules.Tickets(this.whmcsHttpClient); - this.service = new modules.Service(this.whmcsHttpClient); - this.addons = new modules.Addons(this.whmcsHttpClient); - } - - /** - * Executes an action in WHMCS. You can use this to execute an action that is not defined in the pre-loaded modules. - * @param {String} action Command name - * @param {Object} parameters Request parameters (key-value pairs) - * @param {Function} callback Optional callback. If not set the method returns a Promise. - */ - callApi(action, parameters, callback) { - return this.whmcsHttpClient.callApi(action, parameters, callback); - }; -} - -module.exports = WHMCS; \ No newline at end of file