├── .gitignore
├── MIT-LICENSE
├── README.md
├── Rakefile
├── backend
├── Gemfile
├── Rakefile
├── app
│ ├── admin
│ │ ├── admin_user.rb
│ │ ├── api_token.rb
│ │ ├── blogs.rb
│ │ ├── cloudflares.rb
│ │ ├── dashboard.rb
│ │ ├── domains.rb
│ │ ├── locales.rb
│ │ ├── monitor.rb
│ │ ├── permissions.rb
│ │ ├── proxy.rb
│ │ ├── servers.rb
│ │ ├── settings.rb
│ │ └── templates.rb
│ ├── assets
│ │ ├── images
│ │ │ ├── icons
│ │ │ │ ├── arrows.svg
│ │ │ │ └── interface.svg
│ │ │ └── wordpress
│ │ │ │ ├── favicon.ico
│ │ │ │ └── logo.png
│ │ ├── javascripts
│ │ │ └── active_admin
│ │ │ │ ├── amz.js
│ │ │ │ └── components
│ │ │ │ └── copy_text.js
│ │ └── stylesheets
│ │ │ └── active_admin
│ │ │ ├── _amz.scss
│ │ │ └── components
│ │ │ ├── _status_tags.scss
│ │ │ └── _table.scss
│ ├── models
│ │ ├── admin_user_decorator.rb
│ │ ├── api_token.rb
│ │ ├── blog.rb
│ │ ├── cloudflare.rb
│ │ ├── domain.rb
│ │ ├── locale.rb
│ │ ├── monitor.rb
│ │ ├── proxy.rb
│ │ ├── server.rb
│ │ ├── tag.rb
│ │ ├── tags_blog.rb
│ │ └── template.rb
│ └── views
│ │ └── admin
│ │ ├── blogs
│ │ ├── install.html.arb
│ │ ├── login.html.erb
│ │ └── migration.html.erb
│ │ ├── import.html.erb
│ │ └── proxies
│ │ └── upload.html.arb
├── config
│ └── locales
│ │ └── en.yml
├── lib
│ ├── active_admin_import.rb
│ ├── active_admin_import
│ │ ├── authorization.rb
│ │ ├── dsl.rb
│ │ ├── import_result.rb
│ │ ├── importer.rb
│ │ ├── model.rb
│ │ ├── options.rb
│ │ └── version.rb
│ ├── wordpress
│ │ ├── backend.rb
│ │ └── backend
│ │ │ ├── authorization.rb
│ │ │ ├── batch
│ │ │ └── dsl.rb
│ │ │ ├── controller
│ │ │ └── active_admin_base.rb
│ │ │ ├── engine.rb
│ │ │ ├── paranoia
│ │ │ ├── authorization.rb
│ │ │ ├── core.rb
│ │ │ └── dsl.rb
│ │ │ └── state_machine
│ │ │ └── dsl.rb
│ └── wordpress_backend.rb
├── test
│ ├── backend_test.rb
│ └── test_helper.rb
└── wordpress_backend.gemspec
├── core
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── app
│ ├── controllers
│ │ └── wordpress
│ │ │ ├── api_controller.rb
│ │ │ ├── base_controller.rb
│ │ │ ├── home_controller.rb
│ │ │ └── server_controller.rb
│ ├── helpers
│ │ └── wordpress
│ │ │ └── base_helper.rb
│ ├── jobs
│ │ └── wordpress
│ │ │ ├── blog_check_online_job.rb
│ │ │ ├── blog_install_job.rb
│ │ │ ├── blog_job.rb
│ │ │ ├── blog_reset_password_job.rb
│ │ │ ├── domain_import_job.rb
│ │ │ ├── domain_job.rb
│ │ │ ├── proxy_check_job.rb
│ │ │ ├── proxy_install_job.rb
│ │ │ ├── server_job.rb
│ │ │ ├── template_install_job.rb
│ │ │ ├── template_job.rb
│ │ │ ├── template_reset_password_job.rb
│ │ │ └── template_tar_job.rb
│ ├── models
│ │ ├── concerns
│ │ │ └── wordpress
│ │ │ │ ├── number_generator.rb
│ │ │ │ ├── routeable.rb
│ │ │ │ └── validates.rb
│ │ └── wordpress
│ │ │ ├── api_token.rb
│ │ │ ├── app_configuration.rb
│ │ │ ├── auth_configuration.rb
│ │ │ ├── base.rb
│ │ │ ├── blog.rb
│ │ │ ├── blog
│ │ │ ├── mysql_connect.rb
│ │ │ ├── scope.rb
│ │ │ ├── state_machine.rb
│ │ │ └── wp_config.rb
│ │ │ ├── cloudflare.rb
│ │ │ ├── cloudflare
│ │ │ └── preference.rb
│ │ │ ├── domain.rb
│ │ │ ├── locale.rb
│ │ │ ├── monitor.rb
│ │ │ ├── monitor
│ │ │ └── state_machine.rb
│ │ │ ├── preference.rb
│ │ │ ├── preferences
│ │ │ ├── cloudflare.rb
│ │ │ ├── configuration.rb
│ │ │ ├── preferable.rb
│ │ │ ├── preferable_class_methods.rb
│ │ │ └── scoped_cloudflare.rb
│ │ │ ├── proxy.rb
│ │ │ ├── server.rb
│ │ │ ├── tag.rb
│ │ │ ├── tags_blog.rb
│ │ │ └── template.rb
│ └── views
│ │ └── wordpress
│ │ ├── api
│ │ └── code.html.erb
│ │ └── server
│ │ ├── install_os7_apache_php74.html.erb
│ │ ├── install_os7_mysqlv8.html.erb
│ │ ├── install_os8_apache_php74.html.erb
│ │ └── install_os8_mysqlv8.html.erb
├── bin
│ ├── rails
│ └── test
├── config
│ ├── initializers
│ │ └── assets.rb
│ ├── locales
│ │ └── zh-CN.yml
│ └── routes.rb
├── db
│ └── migrate
│ │ ├── 20200707064855_create_wordpress_blogs.rb
│ │ ├── 20200707071606_create_wordpress_cloudflares.rb
│ │ ├── 20200707074130_create_wordpress_templates.rb
│ │ ├── 20200707074205_create_wordpress_locales.rb
│ │ ├── 20200707074412_create_wordpress_servers.rb
│ │ ├── 20200707081610_create_wordpress_tags.rb
│ │ ├── 20200707081940_create_wordpress_api_tokens.rb
│ │ ├── 20200707082208_create_wordpress_proxies.rb
│ │ ├── 20200707091733_create_wordpress_tags_blogs.rb
│ │ ├── 20200707095329_create_wordpress_domains.rb
│ │ ├── 20200707125708_add_time_zone_to_admin_user.rb
│ │ ├── 20200708072401_create_wordpress_preferences.rb
│ │ ├── 20200709150917_add_domain_to_cloudflare.rb
│ │ ├── 20200709160438_add_name_to_admin_user.rb
│ │ ├── 20200717035207_add_directory_to_proxy.rb
│ │ ├── 20200717132029_remove_domain_to_server.rb
│ │ ├── 20200724094508_add_download_url_to_blogs.rb
│ │ ├── 20200726082042_add_trackable_to_admin_user.rb
│ │ ├── 20200728095718_add_user_id_to_cloudflares.rb
│ │ ├── 20200728124406_add_zone_id_to_cloudflares.rb
│ │ ├── 20200729052427_add_account_id_to_cloudflares.rb
│ │ ├── 20200729084505_add_zone_id_to_domains.rb
│ │ └── 20200730071621_create_wordpress_monitors.rb
├── lib
│ ├── generators
│ │ └── wordpress
│ │ │ ├── install
│ │ │ └── install_generator.rb
│ │ │ └── templates
│ │ │ ├── initializer.tt
│ │ │ ├── sidekiq.tt
│ │ │ └── sidekiq.yml
│ ├── tasks
│ │ ├── wordpress_sidekiq_task.rake
│ │ └── wordpress_tasks.rake
│ ├── wordpress
│ │ ├── auth
│ │ │ └── devish.rb
│ │ ├── core.rb
│ │ └── core
│ │ │ ├── engine.rb
│ │ │ ├── helpers
│ │ │ ├── apache.rb
│ │ │ ├── cloudflare_api.rb
│ │ │ ├── mysql.rb
│ │ │ └── mysql2_client.rb
│ │ │ ├── routes.rb
│ │ │ ├── token_generator.rb
│ │ │ └── version.rb
│ └── wordpress_core.rb
├── test
│ ├── dummy
│ │ ├── .ruby-version
│ │ ├── Rakefile
│ │ ├── app
│ │ │ ├── assets
│ │ │ │ ├── config
│ │ │ │ │ └── manifest.js
│ │ │ │ ├── images
│ │ │ │ │ └── .keep
│ │ │ │ └── stylesheets
│ │ │ │ │ └── application.css
│ │ │ ├── channels
│ │ │ │ └── application_cable
│ │ │ │ │ ├── channel.rb
│ │ │ │ │ └── connection.rb
│ │ │ ├── controllers
│ │ │ │ ├── application_controller.rb
│ │ │ │ └── concerns
│ │ │ │ │ └── .keep
│ │ │ ├── helpers
│ │ │ │ └── application_helper.rb
│ │ │ ├── javascript
│ │ │ │ └── packs
│ │ │ │ │ └── application.js
│ │ │ ├── jobs
│ │ │ │ └── application_job.rb
│ │ │ ├── mailers
│ │ │ │ └── application_mailer.rb
│ │ │ ├── models
│ │ │ │ ├── application_record.rb
│ │ │ │ └── concerns
│ │ │ │ │ └── .keep
│ │ │ └── views
│ │ │ │ └── layouts
│ │ │ │ ├── application.html.erb
│ │ │ │ ├── mailer.html.erb
│ │ │ │ └── mailer.text.erb
│ │ ├── bin
│ │ │ ├── rails
│ │ │ ├── rake
│ │ │ └── setup
│ │ ├── config.ru
│ │ ├── config
│ │ │ ├── application.rb
│ │ │ ├── boot.rb
│ │ │ ├── cable.yml
│ │ │ ├── database.yml
│ │ │ ├── environment.rb
│ │ │ ├── environments
│ │ │ │ ├── development.rb
│ │ │ │ ├── production.rb
│ │ │ │ └── test.rb
│ │ │ ├── initializers
│ │ │ │ ├── application_controller_renderer.rb
│ │ │ │ ├── assets.rb
│ │ │ │ ├── backtrace_silencers.rb
│ │ │ │ ├── content_security_policy.rb
│ │ │ │ ├── cookies_serializer.rb
│ │ │ │ ├── filter_parameter_logging.rb
│ │ │ │ ├── inflections.rb
│ │ │ │ ├── mime_types.rb
│ │ │ │ └── wrap_parameters.rb
│ │ │ ├── locales
│ │ │ │ └── en.yml
│ │ │ ├── puma.rb
│ │ │ ├── routes.rb
│ │ │ ├── spring.rb
│ │ │ └── storage.yml
│ │ ├── lib
│ │ │ └── assets
│ │ │ │ └── .keep
│ │ ├── log
│ │ │ ├── .keep
│ │ │ └── development.log
│ │ ├── public
│ │ │ ├── 404.html
│ │ │ ├── 422.html
│ │ │ ├── 500.html
│ │ │ ├── apple-touch-icon-precomposed.png
│ │ │ ├── apple-touch-icon.png
│ │ │ └── favicon.ico
│ │ ├── storage
│ │ │ └── .keep
│ │ └── tmp
│ │ │ ├── .keep
│ │ │ ├── development_secret.txt
│ │ │ ├── pids
│ │ │ └── .keep
│ │ │ └── storage
│ │ │ └── .keep
│ ├── fixtures
│ │ └── wordpress
│ │ │ ├── api_tokens.yml
│ │ │ ├── blogs.yml
│ │ │ ├── cloudflares.yml
│ │ │ ├── domains.yml
│ │ │ ├── locales.yml
│ │ │ ├── monitors.yml
│ │ │ ├── preferences.yml
│ │ │ ├── proxies.yml
│ │ │ ├── servers.yml
│ │ │ ├── tags.yml
│ │ │ ├── tags_blogs.yml
│ │ │ └── templates.yml
│ ├── models
│ │ └── wordpress
│ │ │ ├── api_token_test.rb
│ │ │ ├── blog_test.rb
│ │ │ ├── cloudflare_test.rb
│ │ │ ├── domain_test.rb
│ │ │ ├── locale_test.rb
│ │ │ ├── monitor_test.rb
│ │ │ ├── preference_test.rb
│ │ │ ├── proxy_test.rb
│ │ │ ├── server_test.rb
│ │ │ ├── tag_test.rb
│ │ │ ├── tags_blog_test.rb
│ │ │ └── template_test.rb
│ ├── test_helper.rb
│ └── wordpress_test.rb
└── wordpress_core.gemspec
├── lib
└── wordpress.rb
└── wordpress.gemspec
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | log/*.log
3 | pkg/
4 | test/dummy/db/*.sqlite3
5 | test/dummy/db/*.sqlite3-journal
6 | test/dummy/db/*.sqlite3-*
7 | test/dummy/log/*.log
8 | test/dummy/storage/
9 | test/dummy/tmp/
10 | .DS_Store
11 | /*/node_modules
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2020
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cloud_wordpress is use for wordpress site group management
2 |
3 | * Is activeadmin based;
4 | * WordPress blog domain name connects to Cloudflare Partner;
5 | * WordPress host auto install;
6 | * WordPress host distributed management;
7 | * Clone an existing blog when create a new WordPress;
8 | * Multi-language WordPress management;
9 | * php proxy forwarding;
10 | * Admin user role;
11 |
12 |
13 | ## Usage
14 | How to use my plugin.
15 |
16 | ## Installation
17 | Add this line to your application's Gemfile:
18 |
19 | ```ruby
20 | gem "wordpress", github: "seadfeng/cloud_wordpress"
21 | gem "active_admin_role", github: "seadfeng/active_admin_role"
22 | ```
23 |
24 | And then execute:
25 | ```bash
26 | $ bundle install
27 | $ rails webpacker:install
28 | $ rake db:create
29 | $ rails g active_admin:install
30 | $ rails g wordpress:install
31 | $ rake db:migrate
32 | $ rake db:seed
33 | $ rake wordpress:init
34 | $ rake assets:precompile
35 | ```
36 |
37 | ## Update
38 |
39 | ```bash
40 | $ rake railties:install:migrations FROM=wordpress
41 | $ rake db:migrate
42 | ```
43 |
44 | ## Application Config
45 |
46 | ```ruby
47 | # config/application.rb
48 | config.i18n.default_locale = :"zh-CN"
49 | config.active_job.default_url_options = { host: "demo.cloudwp.xyz" }
50 | Rails.application.routes.default_url_options[:host] = "demo.cloudwp.xyz"
51 | ```
52 |
53 | ## Sidekiq Config
54 | ```ruby
55 | # config/initializers/sidekiq.rb
56 | Sidekiq.configure_server do |config|
57 | config.redis = { url: 'redis://127.0.0.1:6379/2' }
58 | end
59 | Sidekiq.configure_client do |config|
60 | config.redis = { url: 'redis://127.0.0.1:6379/2' }
61 | end
62 | ```
63 |
64 | ## Wordpress Host System dependence
65 | Centos 8.0+
66 |
67 | ## System dependence
68 | rails 6.0+
69 | mysql 8.0+
70 |
71 | ## Dependencies
72 |
73 | [activeadmin](https://github.com/activeadmin/activeadmin)
74 |
75 | [activeadmin_addons](https://github.com/platanus/activeadmin_addons)
76 |
77 | [active_admin_role](https://github.com/seadfeng/active_admin_role)
78 |
79 | [active_admin_import](https://github.com/activeadmin-plugins/active_admin_import)
80 |
81 |
82 | ## Sidekiq Service
83 |
84 | https://github.com/mperham/sidekiq/blob/07c0e1f4e60298deeab70999f6a33c86959f196a/examples/systemd/sidekiq.service
85 |
86 | ## Webpacker Using in Rails engines
87 |
88 | https://github.com/rails/webpacker/blob/master/docs/engines.md
89 |
90 | ## License
91 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
92 |
93 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | begin
2 | require 'bundler/setup'
3 | rescue LoadError
4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5 | end
6 |
7 | require 'rdoc/task'
8 |
9 | RDoc::Task.new(:rdoc) do |rdoc|
10 | rdoc.rdoc_dir = 'rdoc'
11 | rdoc.title = 'Amz'
12 | rdoc.options << '--line-numbers'
13 | rdoc.rdoc_files.include('README.md')
14 | rdoc.rdoc_files.include('lib/**/*.rb')
15 | end
16 |
17 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18 | load 'rails/tasks/engine.rake'
19 |
20 | load 'rails/tasks/statistics.rake'
21 |
22 | require 'bundler/gem_tasks'
23 |
24 | require 'rake/testtask'
25 |
26 | Rake::TestTask.new(:test) do |t|
27 | t.libs << 'test'
28 | t.pattern = 'test/**/*_test.rb'
29 | t.verbose = false
30 | end
31 |
32 | task default: :test
33 |
--------------------------------------------------------------------------------
/backend/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | # Declare your gem's dependencies in wordpress_backend.gemspec.
5 | # Bundler will treat runtime dependencies like base dependencies, and
6 | # development dependencies will be added by default to the :development group.
7 | gemspec
8 |
9 | # Declare any dependencies that are still in development here instead of in
10 | # your gemspec. These might include edge Rails or gems from your path or
11 | # Git. Remember to move these dependencies to your gemspec before releasing
12 | # your gem to rubygems.org.
13 |
14 | # To use a debugger
15 | # gem 'byebug', group: [:development, :test]
16 |
--------------------------------------------------------------------------------
/backend/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rake/testtask"
3 |
4 | Rake::TestTask.new(:test) do |t|
5 | t.libs << "test"
6 | t.libs << "lib"
7 | t.test_files = FileList["test/**/*_test.rb"]
8 | end
9 |
10 | task :default => :test
11 |
--------------------------------------------------------------------------------
/backend/app/admin/admin_user.rb:
--------------------------------------------------------------------------------
1 | if defined?(ActiveAdmin) && defined?(AdminUser)
2 | ActiveAdmin.register AdminUser do
3 | config.comments = false
4 | menu priority: 100
5 | init_controller
6 | begin
7 | role_changeable
8 | rescue => exception
9 | nil
10 | end
11 |
12 | permit_params :email, :password, :password_confirmation, :first_name, :last_name, :time_zone
13 |
14 | controller do
15 | def update
16 | params[:admin_user][:password] = resource.password if params[:admin_user][:password].blank?
17 | params[:admin_user][:password_confirmation] = resource.password_confirmation if params[:admin_user][:password_confirmation].blank?
18 | super
19 | end
20 | end
21 |
22 | index download_links: false do
23 | selectable_column
24 | id_column
25 | column :full_name
26 | column :email
27 | column :current_sign_in_at
28 | column :sign_in_count
29 | column :created_at
30 | actions
31 | end
32 |
33 | filter :email
34 | filter :current_sign_in_at
35 | filter :sign_in_count
36 | filter :created_at
37 |
38 | form do |f|
39 | f.inputs do
40 | f.input :email
41 | f.input :first_name
42 | f.input :last_name
43 | f.input :time_zone , as: :select, collection: TZInfo::Timezone.all_country_zone_identifiers
44 | f.input :password
45 | f.input :password_confirmation
46 | end
47 | f.actions
48 | end
49 |
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/backend/app/admin/api_token.rb:
--------------------------------------------------------------------------------
1 | ActiveAdmin.register Wordpress::ApiToken, as: "ApiToken" do
2 | permit_params :name, :key
3 | active_admin_paranoia
4 | menu priority: 80, parent: "Settings"
5 |
6 | member_action :download_code, method: :put do
7 | options = {
8 | 'X-Auth-Key' => resource.key,
9 | }
10 | client = RestClient.get( wordpress.api_code_url, options )
11 | send_data client.body, :disposition => "attachment; filename=index.php", :type => 'text/html; charset=utf-8; header=present'
12 | end
13 |
14 | index download_links: false do
15 | selectable_column
16 | id_column
17 | column :name
18 | column :download do |source|
19 | link_to "index.php", download_code_admin_api_token_path(source), method: :put
20 | end
21 | column :key
22 | column :created_at
23 | column :updated_at
24 | actions
25 | end
26 |
27 | filter :name
28 |
29 | form do |f|
30 | f.inputs I18n.t("active_admin.api_token.form" , default: "API") do
31 | f.input :name
32 | end
33 | f.actions
34 | end
35 | end
--------------------------------------------------------------------------------
/backend/app/admin/cloudflares.rb:
--------------------------------------------------------------------------------
1 | ActiveAdmin.register Wordpress::Cloudflare, as: "Cloudflare" do
2 | init_controller
3 | actions :all
4 | # batch_action :destroy, false
5 | menu priority: 60 , parent: "Settings"
6 | permit_params :api_user, :name, :api_token ,:description , :domain
7 | active_admin_paranoia
8 |
9 |
10 | controller do
11 | def update
12 | params[:cloudflare][:api_token] = resource.api_token if params[:cloudflare][:api_token].blank?
13 | super
14 | end
15 | end
16 |
17 | action_item :rsync, only: :show do
18 | link_to(
19 | resource.rsynced? ? I18n.t('active_admin.rsync_info', default: "更新信息") : I18n.t('active_admin.get_info', default: "获取信息") ,
20 | rsync_admin_cloudflare_path(resource),
21 | method: :put
22 | )
23 | end
24 |
25 | member_action :rsync, method: :put do
26 | if (resource.rsync_user_id && resource.rsync_zone_id && resource.rsync_account_id)
27 | options = { notice: I18n.t('active_admin.updated', default: "更新成功") }
28 | else
29 | options = { notice: I18n.t('active_admin.update_failed', default: "更新失败") }
30 | end
31 | redirect_back({ fallback_location: ActiveAdmin.application.root_to }.merge(options))
32 | end
33 |
34 | index download_links: false do
35 | selectable_column
36 | id_column
37 | column :user_id do |source|
38 | if (source.user_id.blank? || source.zone_id.blank?)
39 | link_to t("active_admin.cloudflare.get_user_id" , default: "手工获取") , rsync_admin_cloudflare_path(source), method: :put
40 | else
41 | source.user_id
42 | end
43 | end
44 | column :name
45 | column :remaining
46 | column :domain
47 | column :api_user
48 | column :description
49 | column :created_at
50 | column :updated_at
51 | actions
52 | end
53 |
54 | filter :name
55 | filter :code
56 | filter :created_at
57 | filter :updated_at
58 |
59 | form do |f|
60 | f.inputs I18n.t("active_admin.cloudflare.form" , default: "Cloudflare") do
61 | f.input :name, hint: "根据自己命名习惯起名"
62 | f.input :domain, hint: "Cloudflare必须有解析权限的域名,用于博客的二级域名"
63 | f.input :api_user , hint: "Cloudflare登陆账户名"
64 | f.input :api_token , as: :password , hint: raw("Cloudflare Api Key => Global API Key => View
快捷链接" )
65 | f.input :description
66 | end
67 | f.actions
68 | end
69 |
70 | show do
71 | panel t('active_admin.details', model: resource_class.to_s.titleize) do
72 | attributes_table_for resource do
73 | row :api_user
74 | row :account_id
75 | row :zone_id
76 | row :user_id
77 | row :domain
78 | row :name
79 | row :description
80 | row :created_at
81 | row :updated_at
82 | end
83 | end
84 | end
85 |
86 |
87 | end
88 |
89 |
--------------------------------------------------------------------------------
/backend/app/admin/dashboard.rb:
--------------------------------------------------------------------------------
1 | ActiveAdmin.register_page "Dashboard" do
2 | init_controller
3 | menu priority: 0, label: proc{ I18n.t("active_admin.dashboard") }
4 |
5 | content title: proc{ I18n.t("active_admin.dashboard") } do
6 | # Groupdate.time_zone = "Beijing"
7 | Groupdate.day_start = 0
8 |
9 | columns do
10 | blogs = Wordpress::Blog.published
11 | blogs = blogs.where(admin_user_id: current_admin_user.id) unless current_admin_user.admin?
12 | days_90 = blogs.where( "published_at >= ?", 3.months.ago )
13 | column do
14 | panel "90天发布 - #{days_90.count}个" do
15 | div line_chart days_90.group_by_day(:published_at ).count
16 | end
17 | end
18 | if current_admin_user.admin?
19 | column do
20 | panel "博客主机分布" do
21 | div pie_chart Wordpress::Blog.all.joins(:server).group("#{Wordpress::Server.table_name}.name").order("count_all desc").count
22 | end
23 | end
24 | end
25 | end #columns
26 | end # content
27 | end
28 |
--------------------------------------------------------------------------------
/backend/app/admin/domains.rb:
--------------------------------------------------------------------------------
1 | if defined?(ActiveAdmin) && defined?(Wordpress::Domain)
2 | ActiveAdmin.register Wordpress::Domain, as: "Domain" do
3 | init_controller
4 | permit_params :name , :description
5 | menu priority: 6
6 | active_admin_paranoia
7 |
8 | scope :active
9 | scope :not_use
10 | scope :cloudflare
11 | scope :unuse_cloudflare
12 |
13 | active_admin_import validate: true,
14 | template_object: ActiveAdminImport::Model.new(
15 | hint: I18n.t("active_admin_import.domain.import.hint" , default: "CSV: ,\"Name\",\"Description\"
示例:
下载CSV文件"),
16 | ),
17 | headers_rewrites: { :'Description' => :description, :'Name' => :name }
18 |
19 |
20 | collection_action :import_csv, method: :get do
21 | send_data "Name,Description\r\n,", :disposition => "attachment; filename=domains.csv"
22 | end
23 |
24 |
25 | member_action :rsync_cloudflare_zone, method: :put do
26 | resource.rsync_cloudflare_zone
27 | options = { notice: I18n.t('active_admin.processing', default: "正在受理") }
28 | redirect_back({ fallback_location: ActiveAdmin.application.root_to }.merge(options))
29 | end
30 |
31 | action_item :rsync_cloudflare_zone, only: :show do
32 | if Wordpress::Config.cfp_enable
33 | link_to(
34 | I18n.t('active_admin.rsync_cloudflare_zone', default: "同步Cloudflare Partner"),
35 | rsync_cloudflare_zone_admin_domain_path(resource) ,
36 | method: :put
37 | )
38 | end
39 | end
40 |
41 | # action_item :import_cfp, only: [:index] do
42 | # if Wordpress::Config.cfp_enable
43 | # link_to(
44 | # I18n.t('active_admin.import_cfp', default: "导入Cloudflare Partner域名"),
45 | # import_cfp_admin_domains_path ,
46 | # method: "put"
47 | # )
48 | # end
49 | # end
50 |
51 | index do
52 | selectable_column
53 | id_column
54 | column :blogs do |source|
55 | ul do
56 | source.blogs.each do |blog|
57 | li auto_link blog
58 | end
59 | end
60 | end
61 | column :name
62 | column :description
63 | column :state
64 | column :cloudflare do |source|
65 | status_tag !!source.zone_id ? "Yes" : "No"
66 | end
67 | column :created_at
68 | column :updated_at
69 | actions
70 | end
71 |
72 | filter :name
73 | filter :state
74 |
75 | form do |f|
76 | f.inputs I18n.t("active_admin.domains.form" , default: "域名") do
77 | f.input :name, hint: "根域名"
78 | f.input :description
79 | end
80 | f.actions
81 | end
82 | end
83 | end
--------------------------------------------------------------------------------
/backend/app/admin/locales.rb:
--------------------------------------------------------------------------------
1 | ActiveAdmin.register Wordpress::Locale, as: "Locale" do
2 | init_controller
3 | actions :all, except: [:destroy]
4 | batch_action :destroy, false
5 | menu priority: 60 , parent: "Settings"
6 | permit_params :code, :name , :position
7 |
8 |
9 | index download_links: false do
10 | selectable_column
11 | id_column
12 | column :name
13 | column :code
14 | column :position
15 | column :created_at
16 | column :updated_at
17 | actions
18 | end
19 |
20 | filter :name
21 | filter :code
22 | filter :created_at
23 | filter :updated_at
24 |
25 | end
26 |
27 |
--------------------------------------------------------------------------------
/backend/app/admin/monitor.rb:
--------------------------------------------------------------------------------
1 | ActiveAdmin.register Wordpress::Monitor, as: "Monitor" do
2 | init_controller
3 | actions :all, except: [:edit, :new, :update]
4 | menu priority: 100
5 |
6 | filter :created_at
7 | filter :updated_at
8 |
9 | index do
10 | selectable_column
11 | id_column
12 | tag_column :state, machine: :state
13 | column :blog
14 | column :action
15 | column :queued_at
16 | column :completed_at
17 | column :created_at
18 | column :updated_at
19 | actions
20 | end
21 | end
--------------------------------------------------------------------------------
/backend/app/admin/permissions.rb:
--------------------------------------------------------------------------------
1 | ActiveAdmin.register ::ActiveAdmin::Permission, as: "Permission" do
2 | menu priority: 100, parent: "Admin Users"
3 | actions :index
4 |
5 | filter :state, as: :select, collection: controller.resource_class.states
6 |
7 | filter :managed_resource_action_equals, as: :select,
8 | label: ::ActiveAdmin::ManagedResource.human_attribute_name(:action),
9 | collection: -> { ::ActiveAdmin::ManagedResource.distinct.order(:action).pluck(:action) }
10 |
11 | filter :managed_resource_name_equals, as: :select,
12 | label: ::ActiveAdmin::ManagedResource.human_attribute_name(:name),
13 | collection: -> { ::ActiveAdmin::ManagedResource.distinct.pluck(:name).sort }
14 |
15 | filter :managed_resource_class_name_equals, as: :select,
16 | label: ::ActiveAdmin::ManagedResource.human_attribute_name(:class_name),
17 | collection: -> { ::ActiveAdmin::ManagedResource.distinct.order(:class_name).pluck(:class_name) }
18 |
19 | scope :all, default: true
20 |
21 | controller.resource_class.manageable_roles.each_key(&method(:scope))
22 |
23 | controller.resource_class.states.each_key do |state|
24 | batch_action state do |ids|
25 | resource_class.clear_cache
26 | resource_class.where(id: ids).update_all(state: resource_class.states[state])
27 | redirect_back fallback_location: admin_root_url, notice: t("views.permission.notice.state_changed", state: state)
28 | end
29 | end
30 |
31 | collection_action :reload, method: :post do
32 | ::ActiveAdmin::ManagedResource.reload
33 | redirect_back(fallback_location: admin_root_url, notice: t("views.permission.notice.reloaded"))
34 | end
35 |
36 | action_item :reload do
37 | link_to t("views.permission.action_item.reload"), reload_admin_permissions_path, method: :post
38 | end
39 |
40 | includes :managed_resource
41 |
42 | index do
43 | selectable_column
44 | column :role
45 | column(:state) do |record|
46 | status_tag(record.state, class: record.can? ? "completed ok" : "null", label: record.state)
47 | end
48 | column :action
49 | column :name
50 | column :class_name
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/backend/app/admin/proxy.rb:
--------------------------------------------------------------------------------
1 | ActiveAdmin.register Wordpress::Proxy, as: "Proxy" do
2 | permit_params :host, :port, :user, :password, :name, :description ,:connection_type, :installed_at
3 | batch_action :destroy, false
4 | actions :all, except: [:destroy]
5 | menu priority: 80
6 |
7 | controller do
8 | def update
9 | params[:proxy][:password] = resource.password if params[:proxy][:password].blank?
10 | super
11 | end
12 | end
13 |
14 | index download_links: false do
15 | selectable_column
16 | id_column
17 | column :host
18 | column :connection_type
19 | column :user
20 | column :name
21 | column :status
22 | column :directory
23 | # column :install do |post|
24 | # if post.installed
25 | # span "已安装" , style: "background-color: #5cb85c;display:block;min-width:35px;text-align:center"
26 | # else
27 | # link_to I18n.t("active_admin.proxy.dns" , default: "安装") , install_admin_proxy_path(post) , method: :put , class: "status_tag yes" ,style: "color:#FFF"
28 | # end
29 | # end
30 | column :installed_at
31 | column :created_at
32 | column :updated_at
33 | actions
34 | end
35 |
36 |
37 | action_item :install, only: [:show] do
38 | unless resource.installed?
39 | link_to(
40 | I18n.t('active_admin.install', default: "安装Apache+PHP"),
41 | install_admin_proxy_path(resource),
42 | method: "put"
43 | )
44 | end
45 | end
46 |
47 | action_item :upload, only: [:show] do
48 | if resource.installed?
49 | link_to(
50 | I18n.t('active_admin.upload', default: "上传"),
51 | upload_admin_proxy_path(resource),
52 | method: "put"
53 | )
54 | end
55 | end
56 |
57 |
58 | member_action :upload, method: :put do
59 |
60 | end
61 |
62 | member_action :do_upload, method: :put do
63 | api_id = params["api_id"]
64 | api = ApiToken.find( api_id )
65 | if api && resource.push_code(api)
66 | redirect_to admin_proxy_path(resource), notice: "已推送"
67 | else
68 | redirect_to admin_proxy_path(resource), notice: "推送失败"
69 | end
70 |
71 | end
72 |
73 |
74 | member_action :install, method: :put do
75 | resource.install
76 | redirect_back(fallback_location: admin_proxies_path, notice: "已推送安装指令,预计3~7分钟安装完成" )
77 | end
78 |
79 | member_action :test, method: :put do
80 | if resource.test_connection
81 | options = { notice: I18n.t('active_admin.connection_succeeded', default: "连接成功") }
82 | else
83 | options = { alert: I18n.t('active_admin.connection_failed', default: "连接失败") }
84 | end
85 | redirect_back({ fallback_location: ActiveAdmin.application.root_to }.merge(options))
86 | end
87 |
88 | action_item :test, only: [:show , :edit] do
89 | link_to(
90 | I18n.t('active_admin.test_connection', default: "连接测试"),
91 | test_admin_proxy_path(resource),
92 | method: "put"
93 | )
94 | end
95 |
96 | form do |f|
97 | f.inputs I18n.t("active_admin.proxy.form" , default: "代理") do
98 | f.input :host , hint: "安装脚本只支持:Centos 7/8"
99 | f.input :name
100 | f.input :connection_type, as: :select, collection: Wordpress::Proxy::CONNECTION_TYPES
101 | f.input :port
102 | f.input :user
103 | f.input :password
104 | f.input :directory, hint: "不设置默认:/var/www/html/"
105 | f.input :description
106 | f.input :installed_at, as: :date_time_picker
107 | end
108 | f.actions
109 | end
110 |
111 | end
112 |
--------------------------------------------------------------------------------
/backend/app/assets/images/icons/arrows.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
41 |
--------------------------------------------------------------------------------
/backend/app/assets/images/icons/interface.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
51 |
--------------------------------------------------------------------------------
/backend/app/assets/images/wordpress/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seadfeng/cloud_wordpress/b89f027ce3c7a82972d8279e70faf8ffee96749a/backend/app/assets/images/wordpress/favicon.ico
--------------------------------------------------------------------------------
/backend/app/assets/images/wordpress/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seadfeng/cloud_wordpress/b89f027ce3c7a82972d8279e70faf8ffee96749a/backend/app/assets/images/wordpress/logo.png
--------------------------------------------------------------------------------
/backend/app/assets/javascripts/active_admin/amz.js:
--------------------------------------------------------------------------------
1 | //= require ./components/copy_text
--------------------------------------------------------------------------------
/backend/app/assets/javascripts/active_admin/components/copy_text.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 | $('.row-mysql_password td, .row-wordpress_password td, .row-mysql_host td, .row-mysql_user td').each(function(){
3 | $(this).attr('title', "可复制");
4 | $(this).click(function(){
5 | $('.row td.active').removeClass('active');
6 | $(this).addClass('active');
7 | var tempInput = document.createElement("input");
8 | tempInput.value = $(this).text();
9 | document.body.appendChild(tempInput);
10 | tempInput.select();
11 | tempInput.setSelectionRange(0, 99999);
12 | document.execCommand("copy");
13 | document.body.removeChild(tempInput);
14 | })
15 | })
16 | })
--------------------------------------------------------------------------------
/backend/app/assets/stylesheets/active_admin/_amz.scss:
--------------------------------------------------------------------------------
1 | @import "active_admin/components/status_tags";
2 | @import "active_admin/components/table";
--------------------------------------------------------------------------------
/backend/app/assets/stylesheets/active_admin/components/_status_tags.scss:
--------------------------------------------------------------------------------
1 | .status_tag {
2 | &.installed,
3 | &.open,
4 | &.done { background: #29c7b3; }
5 | &.complete,
6 | &.published { background: #7DB942; }
7 | &.draft,
8 | &.processing { background: orange; }
9 | }
--------------------------------------------------------------------------------
/backend/app/assets/stylesheets/active_admin/components/_table.scss:
--------------------------------------------------------------------------------
1 | .index_table {
2 | .alert {
3 | color: #ff0000;
4 | }
5 | }
6 | .attributes_table {
7 | td.active {
8 | background: #57ab6a63;
9 | }
10 | }
11 | .lds-ellipsis {
12 | display: block;
13 | position: relative;
14 | margin: 0 auto;
15 | width: 100px;;
16 | height: 100px;;
17 | div {
18 | position: absolute;
19 | top: 33px;
20 | width: 13px;
21 | height: 13px;
22 | border-radius: 50%;
23 | background: #63636363;
24 | animation-timing-function: cubic-bezier(0, 1, 1, 0);
25 | }
26 | div:nth-child(1) {
27 | left: 8px;
28 | animation: lds-ellipsis1 0.6s infinite;
29 | }
30 | div:nth-child(2) {
31 | left: 8px;
32 | animation: lds-ellipsis2 0.6s infinite;
33 | }
34 | div:nth-child(3) {
35 | left: 32px;
36 | animation: lds-ellipsis2 0.6s infinite;
37 | }
38 | div:nth-child(4) {
39 | left: 56px;
40 | animation: lds-ellipsis3 0.6s infinite;
41 | }
42 | }
43 |
44 | @keyframes lds-ellipsis1 {
45 | 0% {
46 | transform: scale(0);
47 | }
48 | 100% {
49 | transform: scale(1);
50 | }
51 | }
52 | @keyframes lds-ellipsis3 {
53 | 0% {
54 | transform: scale(1);
55 | }
56 | 100% {
57 | transform: scale(0);
58 | }
59 | }
60 | @keyframes lds-ellipsis2 {
61 | 0% {
62 | transform: translate(0, 0);
63 | }
64 | 100% {
65 | transform: translate(24px, 0);
66 | }
67 | }
--------------------------------------------------------------------------------
/backend/app/models/admin_user_decorator.rb:
--------------------------------------------------------------------------------
1 | class AdminUserDecorator
2 | if defined?(AdminUser)
3 | AdminUser.class_eval do
4 | devise :trackable
5 | validates :first_name, presence: true
6 | validates :last_name, presence: true
7 |
8 | def full_name
9 | "#{first_name} #{last_name}"
10 | end
11 |
12 | def display_name
13 | "#{self.full_name} - #{role}"
14 | end
15 | end
16 | end
17 | end
--------------------------------------------------------------------------------
/backend/app/models/api_token.rb:
--------------------------------------------------------------------------------
1 | class ApiToken < Wordpress::ApiToken
2 |
3 | end
--------------------------------------------------------------------------------
/backend/app/models/blog.rb:
--------------------------------------------------------------------------------
1 | class Blog < Wordpress::Blog
2 |
3 | end
--------------------------------------------------------------------------------
/backend/app/models/cloudflare.rb:
--------------------------------------------------------------------------------
1 | class Cloudflare < Wordpress::Cloudflare
2 |
3 | end
--------------------------------------------------------------------------------
/backend/app/models/domain.rb:
--------------------------------------------------------------------------------
1 | class Domain < Wordpress::Domain
2 |
3 | end
4 |
--------------------------------------------------------------------------------
/backend/app/models/locale.rb:
--------------------------------------------------------------------------------
1 | class Locale < Wordpress::Locale
2 |
3 | end
--------------------------------------------------------------------------------
/backend/app/models/monitor.rb:
--------------------------------------------------------------------------------
1 | class Monitor < Wordpress::Monitor
2 |
3 | end
4 |
--------------------------------------------------------------------------------
/backend/app/models/proxy.rb:
--------------------------------------------------------------------------------
1 | class Proxy < Wordpress::Proxy
2 |
3 | end
4 |
--------------------------------------------------------------------------------
/backend/app/models/server.rb:
--------------------------------------------------------------------------------
1 | class Server < Wordpress::Server
2 |
3 | end
4 |
--------------------------------------------------------------------------------
/backend/app/models/tag.rb:
--------------------------------------------------------------------------------
1 | class Tag < Wordpress::Tag
2 |
3 | end
4 |
--------------------------------------------------------------------------------
/backend/app/models/tags_blog.rb:
--------------------------------------------------------------------------------
1 | class TagsBlog < Wordpress::TagsBlog
2 |
3 | end
4 |
--------------------------------------------------------------------------------
/backend/app/models/template.rb:
--------------------------------------------------------------------------------
1 | class Template < Wordpress::Template
2 |
3 | end
4 |
--------------------------------------------------------------------------------
/backend/app/views/admin/blogs/install.html.arb:
--------------------------------------------------------------------------------
1 |
2 |
3 | panel "选择模版" do
4 | table_for resource.templates do
5 | column :id
6 | column :locale
7 | column :name
8 | column :install do |template|
9 | link_to I18n.t('active_admin.install', default: "安装"), do_install_admin_blog_path(resource,template_id: template.id) ,method: "put"
10 | end
11 | end
12 | end
--------------------------------------------------------------------------------
/backend/app/views/admin/blogs/login.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
2 | <%= raw(@active_admin_import_model.hint) %> 3 |
4 | <%= semantic_form_for @active_admin_import_model, url: {action: :do_import}, html: {multipart: true} do |f| %> 5 | <%= f.inputs name: t("active_admin_import.details") do %> 6 | <%= f.input :file, as: :file %> 7 | <% end %> 8 | <%= f.actions do %> 9 | <%= f.action :submit, label: t("active_admin_import.import_btn"), button_html: {data: {disable_with: t("active_admin_import.import_btn_disabled")}} %> 10 | <% end %> 11 | <% end %> -------------------------------------------------------------------------------- /backend/app/views/admin/proxies/upload.html.arb: -------------------------------------------------------------------------------- 1 | panel "选择Api" do 2 | table_for ApiToken.all do 3 | column :id 4 | column :name 5 | column :key 6 | column :upload do |source| 7 | link_to "上传", do_upload_admin_proxy_path(source, api_id: source.id), method: :put 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /backend/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | active_admin: 3 | send_job: "#%{id} %{name} Send to Job" 4 | successfully_updated: "Successfully updated %{name}" 5 | successfully_created: "Successfully created %{name}" 6 | create_failure: "%{name} create failure" 7 | import: "Import" 8 | filters: 9 | store: "Store" 10 | asin: "ASIN" 11 | batch_actions: 12 | delete_confirmation: "Are you sure you want to archive these %{plural_model}?" 13 | succesfully_destroyed: 14 | one: "Successfully archived 1 %{model}" 15 | other: "Successfully archived %{count} %{plural_model}" 16 | labels: 17 | destroy: "Archive" 18 | active_admin_paranoia: 19 | batch_actions: 20 | restore_confirmation: "Are you sure you want to restore these %{plural_model}?" 21 | succesfully_restored: 22 | one: "Successfully restored 1 %{model}" 23 | other: "Successfully restored %{count} %{plural_model}" 24 | archived: "Archived" 25 | non_archived: "Non Archived" 26 | restore_model: "Restore %{model}" 27 | restore: "Restore" 28 | restore_confirmation: "Are you sure you want to restore this?" 29 | something_wrong: "Something went wrong. Please try again" 30 | active_admin_import: 31 | file: 'File' 32 | file_error: "Error: %{message}" 33 | file_format_error: "You can import only valid csv file" 34 | file_empty_error: "You can't import empty file" 35 | no_file_error: "Please, select file to import" 36 | details: "Please, select file to import" 37 | imported: 38 | one: "Successfully imported 1 %{model}" 39 | other: "Successfully imported %{count} %{plural_model}" 40 | failed: 41 | one: "Failed to import 1 %{model}: %{message}" 42 | other: "Failed to import %{count} %{plural_model}: %{message}" 43 | import_model: "Import %{plural_model}" 44 | import_btn: "Import" 45 | import_btn_disabled: "Wait..." 46 | csv_options: "CSV options" 47 | col_sep: 'Col sep' 48 | row_sep: 'Row sep' 49 | quote_char: 'Quote char' -------------------------------------------------------------------------------- /backend/lib/active_admin_import.rb: -------------------------------------------------------------------------------- 1 | require 'activerecord-import' 2 | require 'active_admin' 3 | require 'active_admin_import/version' 4 | require 'active_admin_import/import_result' 5 | require 'active_admin_import/options' 6 | require 'active_admin_import/dsl' 7 | require 'active_admin_import/importer' 8 | require 'active_admin_import/model' 9 | require 'active_admin_import/authorization' 10 | ::ActiveAdmin::DSL.send(:include, ActiveAdminImport::DSL) -------------------------------------------------------------------------------- /backend/lib/active_admin_import/authorization.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module ActiveAdminImport 3 | # Default Authorization permission for ActiveAdminImport 4 | module Authorization 5 | IMPORT = :import 6 | end 7 | 8 | Auth = Authorization 9 | end 10 | -------------------------------------------------------------------------------- /backend/lib/active_admin_import/import_result.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module ActiveAdminImport 3 | class ImportResult 4 | attr_reader :failed, :total 5 | 6 | def initialize 7 | @failed = [] 8 | @total = 0 9 | end 10 | 11 | def add(result, qty) 12 | @failed += result.failed_instances 13 | @total += qty 14 | end 15 | 16 | def imported_qty 17 | total - failed.count 18 | end 19 | 20 | def imported? 21 | imported_qty > 0 22 | end 23 | 24 | def failed? 25 | failed.any? 26 | end 27 | 28 | def empty? 29 | total == 0 30 | end 31 | 32 | def failed_message(options = {}) 33 | limit = options[:limit] || failed.count 34 | failed.first(limit).map do |record| 35 | errors = record.errors 36 | failed_values = errors.keys.map do |key| 37 | key == :base ? nil : record.public_send(key) 38 | end 39 | errors.full_messages.zip(failed_values).map { |ms| ms.compact.join(' - ') }.join(', ') 40 | end.join(' ; ') 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /backend/lib/active_admin_import/options.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module ActiveAdminImport 3 | module Options 4 | VALID_OPTIONS = [ 5 | :back, 6 | :csv_options, 7 | :validate, 8 | :batch_size, 9 | :batch_transaction, 10 | :before_import, 11 | :after_import, 12 | :before_batch_import, 13 | :after_batch_import, 14 | :on_duplicate_key_update, 15 | :timestamps, 16 | :ignore, 17 | :template, 18 | :template_object, 19 | :resource_class, 20 | :resource_label, 21 | :plural_resource_label, 22 | :error_limit, 23 | :headers_rewrites, 24 | :if 25 | ].freeze 26 | 27 | def self.options_for(config, options = {}) 28 | unless options.key? :template_object 29 | options[:template_object] = ActiveAdminImport::Model.new 30 | end 31 | 32 | { 33 | back: { action: :import }, 34 | csv_options: {}, 35 | template: 'admin/import', 36 | resource_class: config.resource_class, 37 | resource_label: config.resource_label, 38 | plural_resource_label: config.plural_resource_label, 39 | error_limit: 5, 40 | headers_rewrites: {}, 41 | if: true 42 | }.deep_merge(options) 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /backend/lib/active_admin_import/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ActiveAdminImport 4 | VERSION = '4.1.1' 5 | end 6 | -------------------------------------------------------------------------------- /backend/lib/wordpress/backend.rb: -------------------------------------------------------------------------------- 1 | require "activeadmin" 2 | require "activeadmin_addons" 3 | require "active_admin_role" 4 | require "devise" 5 | require "draper" 6 | require "pundit" 7 | require "enumerize" 8 | require "chartkick" 9 | require "groupdate" 10 | 11 | require "wordpress/backend/authorization" 12 | require "wordpress/backend/engine" 13 | require "wordpress/backend/paranoia/core" 14 | require "wordpress/backend/batch/dsl" 15 | require "wordpress/backend/state_machine/dsl" 16 | require "wordpress/backend/controller/active_admin_base" 17 | -------------------------------------------------------------------------------- /backend/lib/wordpress/backend/authorization.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module Backend 3 | module Authorization 4 | CREATE_TICKET = :create_ticket 5 | PUBLISH = :publish 6 | DONE = :done 7 | IMPORT = :import 8 | end 9 | Auth = Authorization 10 | end 11 | end 12 | ::ActiveAdmin::Authorization.send(:include, Wordpress::Backend::Authorization) -------------------------------------------------------------------------------- /backend/lib/wordpress/backend/batch/dsl.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module Backend 3 | module Batch 4 | module DSL 5 | def batch_action_model( model, action_options = {} ) 6 | model = model.to_s.camelize.constantize 7 | model.all.each do |item| 8 | batch_action item.name, action_options do |ids| 9 | i = 0 10 | batch_action_collection.find(ids).each do |source| 11 | target = model.find_by(id: item.id) 12 | i += 1 if source.batch_action(target) 13 | end 14 | options = { notice: I18n.t('active_admin.batch_action.succesfully_updated', count: i, model: resource_class.to_s.camelize.constantize.model_name, plural_model: resource_class.to_s.downcase.pluralize) } 15 | redirect_back({ fallback_location: ActiveAdmin.application.root_to }.merge(options)) 16 | end 17 | end 18 | end 19 | end 20 | end 21 | end 22 | end 23 | 24 | ::ActiveAdmin::DSL.send(:include, Wordpress::Backend::Batch::DSL) 25 | -------------------------------------------------------------------------------- /backend/lib/wordpress/backend/controller/active_admin_base.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module Backend 3 | module Controller 4 | module Base 5 | def init_controller 6 | controller do 7 | before_action :set_time_zone, if: :current_admin_user 8 | private 9 | def set_time_zone 10 | Time.zone = current_admin_user.time_zone if current_admin_user.time_zone.present? 11 | end 12 | end 13 | sidebar :time_now, only: [:index] do 14 | dl do 15 | dt Time.zone 16 | dd Time.current 17 | end 18 | end 19 | end 20 | end 21 | end 22 | end 23 | end 24 | ::ActiveAdmin::DSL.send(:include, Wordpress::Backend::Controller::Base) 25 | -------------------------------------------------------------------------------- /backend/lib/wordpress/backend/engine.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module Backend 3 | class Engine < ::Rails::Engine 4 | 5 | initializer "active_admin.load_app_path" do 6 | app_admin = File.expand_path("../../../app/admin", __dir__) 7 | ActiveAdmin.application.load_paths += Dir[app_admin] 8 | end 9 | 10 | initializer "active_admin.memu" do 11 | ActiveAdmin.setup do |config| 12 | config.namespace :admin do |admin| 13 | admin.build_menu :default do |menu| 14 | menu.add label: I18n.t("active_admin.sidekiq", default: "Sidekiq"), url: "/sidekiq", html_options: { target: :blank }, priority: 1000 15 | end 16 | end 17 | config.default_per_page = [20, 50, 100] 18 | config.download_links = [:csv, :json] 19 | end 20 | end 21 | 22 | def self.activate 23 | Dir.glob(File.join(File.dirname(__FILE__), '../../../app/**/*_decorator*.rb')).each do |c| 24 | Rails.configuration.cache_classes ? require(c) : load(c) 25 | end 26 | end 27 | 28 | config.to_prepare(&method(:activate).to_proc) 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /backend/lib/wordpress/backend/paranoia/authorization.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module Backend 3 | module Paranoia 4 | # Default Authorization permission for ActiveAdmin::Paranoia 5 | module Authorization 6 | RESTORE = :restore 7 | PERMANENT_DELETE = :permanent_delete 8 | end 9 | 10 | Auth = Authorization 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /backend/lib/wordpress/backend/paranoia/core.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'wordpress/backend/paranoia/dsl' 3 | require 'wordpress/backend/paranoia/authorization' 4 | ::ActiveAdmin::DSL.send(:include, Wordpress::Backend::Paranoia::DSL) -------------------------------------------------------------------------------- /backend/lib/wordpress/backend/state_machine/dsl.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module Backend 3 | module StateMachine 4 | module DSL 5 | 6 | # 7 | # Easily tie into a state_machine action 8 | # 9 | # @param [Symbol] state machine event, ie: :publish 10 | # @param [Hash] options 11 | # - permission [Symbol] permission to check authorization against 12 | # - http_verb [Symbol] :put, :post, :get, etc 13 | # 14 | # Will call "resource.publish!", if "resource.can_publish?" returns true 15 | # 16 | 17 | def state_action(action, options={}, &controller_action) 18 | singular = config.resource_name.singular 19 | plural = config.resource_name.plural 20 | 21 | options[:permission] ||= controller.new.send(:action_to_permission, action) 22 | confirmation = options.fetch(:confirm, false) 23 | if confirmation == true 24 | default = "Are you sure you want to #{action.to_s.humanize.downcase}?" 25 | confirmation = ->{ I18n.t(:confirm, scope: "#{plural}.#{action}", default: default) } 26 | end 27 | 28 | http_verb = options.fetch :http_verb, :put 29 | 30 | action_item_args = if ActiveAdmin::VERSION.start_with?('0.') 31 | [{ only: :show }] 32 | else 33 | ["state_action_#{action}", { only: :show }] 34 | end 35 | action_item(*action_item_args) do 36 | if resource.send("can_#{action}?") && authorized?(options[:permission], resource) 37 | path = resource_path << "/#{action}" 38 | label = I18n.t("#{plural}.#{action}.label", default: action.to_s.titleize) 39 | 40 | link_options = {} 41 | if confirmation.is_a?(Proc) 42 | link_options[:data] ||= {} 43 | link_options[:data][:confirm] = instance_exec(&confirmation) 44 | end 45 | 46 | link_options[:class] = "btn btn-large" 47 | link_options[:method] = http_verb 48 | 49 | link_to label, path, link_options 50 | end 51 | end 52 | 53 | unless block_given? 54 | controller_action = -> do 55 | begin 56 | resource.send("#{action}!") 57 | rescue => e 58 | flash[:error] = t("#{plural}.#{action}.flash.errors", default: e.message) 59 | redirect_to smart_resource_url and return 60 | end 61 | flash[:notice] = t("#{plural}.#{action}.flash.success") 62 | redirect_to smart_resource_url 63 | end 64 | end 65 | 66 | member_action action, method: http_verb, &controller_action 67 | end 68 | end 69 | end 70 | end 71 | end 72 | 73 | ::ActiveAdmin::DSL.send(:include, Wordpress::Backend::StateMachine::DSL) 74 | -------------------------------------------------------------------------------- /backend/lib/wordpress_backend.rb: -------------------------------------------------------------------------------- 1 | require 'active_admin_import' 2 | require 'wordpress/backend' -------------------------------------------------------------------------------- /backend/test/backend_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class BackendTest < Minitest::Test 4 | def test_that_it_has_a_version_number 5 | refute_nil ::Backend::VERSION 6 | end 7 | 8 | def test_it_does_something_useful 9 | assert false 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /backend/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) 2 | require "backend" 3 | 4 | require "minitest/autorun" 5 | -------------------------------------------------------------------------------- /backend/wordpress_backend.gemspec: -------------------------------------------------------------------------------- 1 | # Maintain your gem's version: 2 | require_relative '../core/lib/wordpress/core/version' 3 | 4 | Gem::Specification.new do |spec| 5 | spec.name = "wordpress_backend" 6 | spec.version = Wordpress::VERSION 7 | spec.authors = ["Sead Feng"] 8 | spec.email = ["seadfeng@gmail.com"] 9 | spec.homepage = "https://github.com/seadfeng/cloud_wordpress" 10 | spec.summary = "Cloud Wordpress Core" 11 | spec.description = "Wordpress Core" 12 | spec.license = "MIT" 13 | 14 | 15 | spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] 16 | spec.require_path = 'lib' 17 | spec.requirements << 'none' 18 | 19 | spec.add_dependency "wordpress_core", spec.version 20 | spec.add_dependency "activeadmin" 21 | spec.add_dependency "activeadmin_addons" 22 | 23 | spec.add_dependency 'activerecord-import', '~> 0.27' 24 | spec.add_dependency "rchardet" 25 | 26 | spec.add_dependency "active_admin_role" 27 | spec.add_dependency "devise" 28 | spec.add_dependency "draper" 29 | spec.add_dependency "pundit" 30 | spec.add_dependency "chartkick" 31 | spec.add_dependency "groupdate" 32 | 33 | spec.add_dependency "enumerize", '~> 2.3.1' 34 | end 35 | -------------------------------------------------------------------------------- /core/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | # Declare your gem's dependencies in wordpress_core.gemspec. 5 | # Bundler will treat runtime dependencies like base dependencies, and 6 | # development dependencies will be added by default to the :development group. 7 | gemspec 8 | 9 | # Declare any dependencies that are still in development here instead of in 10 | # your gemspec. These might include edge Rails or gems from your path or 11 | # Git. Remember to move these dependencies to your gemspec before releasing 12 | # your gem to rubygems.org. 13 | 14 | # To use a debugger 15 | # gem 'byebug', group: [:development, :test] -------------------------------------------------------------------------------- /core/Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | require 'rdoc/task' 8 | 9 | RDoc::Task.new(:rdoc) do |rdoc| 10 | rdoc.rdoc_dir = 'rdoc' 11 | rdoc.title = 'Wordpress' 12 | rdoc.options << '--line-numbers' 13 | rdoc.rdoc_files.include('README.md') 14 | rdoc.rdoc_files.include('lib/**/*.rb') 15 | end 16 | 17 | require 'bundler/gem_tasks' 18 | 19 | require 'rake/testtask' 20 | 21 | Rake::TestTask.new(:test) do |t| 22 | t.libs << 'test' 23 | t.pattern = 'test/**/*_test.rb' 24 | t.verbose = false 25 | end 26 | 27 | task default: :test 28 | -------------------------------------------------------------------------------- /core/app/controllers/wordpress/api_controller.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class ApiController < Wordpress::BaseController 3 | before_action :load_data 4 | 5 | def show 6 | auth_domain = request.headers["X-Auth-Domain"] 7 | request_uri = request.headers["Request-Uri"] 8 | forwarded_proto = request.headers["X-Forwarded-Proto"] 9 | subdomain = "#{auth_domain}".gsub!(/(.*)\.[^\.]*\.[^\.]*/,'\1') 10 | 11 | if subdomain.nil? 12 | root_domain = auth_domain 13 | else 14 | root_domain = auth_domain.gsub(/#{subdomain}\./,'') 15 | end 16 | 17 | domain = Wordpress::Domain.cache_by_name(root_domain) 18 | 19 | if @api && domain && blog = domain.blog_cache_by_subname(subdomain) 20 | uri = "#{blog.cloudflare_origin}#{request_uri}" 21 | 22 | if blog.published? 23 | @headers = { 24 | 'X-Forwarded-Host' => blog.origin, 25 | 'X-Forwarded-Proto' => forwarded_proto, 26 | 'User-Agent' => request.headers["User-Agent"], 27 | 'Cache-Control'=> request.headers["Cache-Control"], 28 | } 29 | 30 | client = rest_client(uri, request.method, request.query_parameters.to_json) 31 | if client 32 | response.status = client.code 33 | body = client.body 34 | if request_uri =~ /(\.jpg|\.jpeg)/i 35 | send_data(body, disposition:'inline', :type => 'image/jpeg') 36 | elsif request_uri =~ /(\.png)/i 37 | send_data(body, disposition:'inline', :type => 'image/png') 38 | elsif request_uri =~ /(\.gif)/i 39 | send_data(body, disposition:'inline', :type => 'image/gif') 40 | elsif request_uri =~ /(\.jpg)/i 41 | send_data(body, disposition:'inline', :type => 'image/jpg') 42 | elsif request_uri =~ /(\.ico)/i 43 | send_data(body, disposition:'inline', :type => 'image/x-icon') 44 | elsif request_uri =~ /(\.svg)/i 45 | send_data(body, disposition:'inline', :type => 'image/svg+xml') 46 | elsif request_uri =~ /(robots\.txt)/i 47 | render inline: "User-agent: *" 48 | else 49 | render inline: body 50 | end 51 | else 52 | render_404 53 | end 54 | # render inline: "#{@api.key}, #{request.method}, #{params}" 55 | else 56 | render_404 57 | end 58 | else 59 | render_404 60 | end 61 | 62 | end 63 | 64 | def code 65 | render layout: false, content_type: 'text/plain', locals: { api_url: wordpress.api_url, auth_key: @api.key } 66 | end 67 | 68 | private 69 | 70 | def rest_client( url, method, *options ) 71 | options = options.first || {} 72 | begin 73 | if method === "POST" 74 | RestClient.post url, options, @headers 75 | elsif method === "PUT" 76 | RestClient.put url, options, @headers 77 | else 78 | RestClient.get url, @headers 79 | end 80 | rescue RestClient::ExceptionWithResponse => e 81 | case e.http_code 82 | when 301, 302, 307 83 | e.response.follow_redirection 84 | else 85 | raise 86 | end 87 | end 88 | end 89 | 90 | def render_404 91 | response.status = 404 92 | body = "404" 93 | render inline: body 94 | end 95 | 96 | def load_data 97 | auth_key = request.headers["X-Auth-Key"] 98 | @api = Wordpress::ApiToken.api_token_cache(auth_key) 99 | if @api.blank? 100 | return render_404 101 | end 102 | end 103 | 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /core/app/controllers/wordpress/base_controller.rb: -------------------------------------------------------------------------------- 1 | 2 | module Wordpress 3 | class BaseController < ApplicationController 4 | 5 | respond_to :html 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /core/app/controllers/wordpress/home_controller.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class HomeController < Wordpress::BaseController 3 | 4 | def index 5 | puts Wordpress::Config 6 | render inline: "Ok" 7 | end 8 | 9 | end 10 | end -------------------------------------------------------------------------------- /core/app/controllers/wordpress/server_controller.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class ServerController < Wordpress::BaseController 3 | 4 | def index 5 | if params[:os] == "v7" 6 | install_os7_apache_php74 7 | elsif params[:os] == "v8" 8 | install_os8_apache_php74 9 | else 10 | render inline: "# v7 or v8", layout: false, content_type: 'text/plain' 11 | end 12 | end 13 | 14 | def mysql 15 | if params[:os] == "v7" 16 | install_os7_mysqlv8 17 | elsif params[:os] == "v8" 18 | install_os8_mysqlv8 19 | else 20 | render inline: "# v7 or v8", layout: false, content_type: 'text/plain' 21 | end 22 | end 23 | 24 | private 25 | 26 | def install_os7_mysqlv8 27 | render "wordpress/server/install_os7_mysqlv8", layout: false, content_type: 'text/plain' 28 | end 29 | 30 | def install_os8_mysqlv8 31 | render "wordpress/server/install_os8_mysqlv8", layout: false, content_type: 'text/plain' 32 | end 33 | 34 | def install_os7_apache_php74 35 | render "wordpress/server/install_os7_apache_php74", layout: false, content_type: 'text/plain' 36 | end 37 | 38 | def install_os8_apache_php74 39 | render "wordpress/server/install_os8_apache_php74", layout: false, content_type: 'text/plain' 40 | end 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /core/app/helpers/wordpress/base_helper.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module BaseHelper 3 | end 4 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/blog_check_online_job.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class BlogCheckOnlineJob < ApplicationJob 3 | queue_as :default 4 | sidekiq_options retry: 3 5 | attr_reader :blog 6 | 7 | def perform(blog) 8 | begin 9 | cli = RestClient.get blog.online_origin 10 | blog.update_attribute(:status, cli.code) 11 | rescue RestClient::ExceptionWithResponse => e 12 | blog.update_attribute(:status, blog.http_code) if blog.http_code 13 | case e.http_code 14 | when 301, 302, 307 15 | e.response.follow_redirection 16 | else 17 | raise 18 | end 19 | end 20 | end 21 | 22 | private 23 | 24 | def log_file 25 | File.open('log/wordpress_check_job.log', File::WRONLY | File::APPEND | File::CREAT) 26 | end 27 | 28 | end 29 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/blog_job.rb: -------------------------------------------------------------------------------- 1 | require 'wordpress/core/helpers/apache' 2 | require 'wordpress/core/helpers/mysql' 3 | module Wordpress 4 | class BlogJob < ApplicationJob 5 | queue_as :wordpress 6 | sidekiq_options retry: 3 7 | attr_reader :blog 8 | 9 | def perform(blog) 10 | RestClient.get url 11 | end 12 | 13 | private 14 | 15 | def log_file 16 | # To create new (and to remove old) logfile, add File::CREAT like; 17 | # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) 18 | File.open('log/wordpress_job.log', File::WRONLY | File::APPEND | File::CREAT) 19 | end 20 | 21 | end 22 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/blog_reset_password_job.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class BlogResetPasswordJob < Wordpress::BlogJob 3 | 4 | def perform(blog) 5 | logger = Logger.new(log_file) 6 | begin 7 | server = blog.server 8 | mysql_info = { 9 | # user: blog.mysql_user, 10 | # user_host: server.mysql_host_user , 11 | # user_password: blog.mysql_password, 12 | database: blog.mysql_db, 13 | collection_user: blog.mysql_user, 14 | collection_password: blog.mysql_password, 15 | collection_host: server.mysql_host 16 | } 17 | mysql = Wordpress::Core::Helpers::Mysql.new(mysql_info) 18 | Net::SSH.start( server.host, server.host_user, :password => server.host_password, :port => server.host_port) do |ssh| 19 | logger.info("ssh connected") 20 | channel = ssh.open_channel do |ch| 21 | ch.exec "#{mysql.only_update_password(blog.password, blog.user)}" do |ch, success| 22 | logger.info("#{mysql.only_update_password(blog.password, blog.user)}") 23 | ch.on_data do |c, data| 24 | $stdout.print data 25 | if /^#{mysql_info[:database]}$/.match(data) 26 | logger.info("Database checked: #{mysql_info[:database]}") 27 | logger.info("Password has Updated!") 28 | end 29 | end 30 | end 31 | end 32 | channel.wait 33 | end 34 | rescue Exception, ActiveJob::DeserializationError => e 35 | logger.error("Blog Id:#{blog.id} ================") 36 | logger.error(I18n.t('active_admin.active_job', message: e.message, default: "ActiveJob: #{e.message}")) 37 | logger.error(e.backtrace.join("\n")) 38 | nil 39 | end 40 | 41 | end 42 | 43 | private 44 | 45 | def log_file 46 | # To create new (and to remove old) logfile, add File::CREAT like; 47 | # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) 48 | File.open('log/wordpress_reset_password_job.log', File::WRONLY | File::APPEND | File::CREAT) 49 | end 50 | 51 | end 52 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/domain_import_job.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class DomainImportJob < ApplicationJob 3 | queue_as :default 4 | sidekiq_options retry: 3 5 | attr_reader :page, :total_pages, :next_page, :result 6 | 7 | def perform(page) 8 | @page = page 9 | cloudflare = { 10 | api_user: Wordpress::Config.api_user, 11 | api_token: Wordpress::Config.api_token, 12 | } 13 | cloudflare_api = Wordpress::Core::Helpers::CloudflareApi.new(cloudflare) 14 | body = cloudflare_api.list_all_zone(page) 15 | if body["success"] 16 | @total_pages = body["result_info"]["total_pages"] 17 | @result = body["result"] 18 | @result.each do |zone| 19 | Domain.find_or_create( name: zone["name"] ) 20 | end 21 | if page < @total_pages 22 | @next_page = @total_pages - page 23 | Wordpress::DomainImportJob.perform_later(@next_page) 24 | end 25 | end 26 | end 27 | 28 | private 29 | 30 | def log_file 31 | # To create new (and to remove old) logfile, add File::CREAT like; 32 | # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) 33 | File.open('log/domain_job.log', File::WRONLY | File::APPEND | File::CREAT) 34 | end 35 | 36 | end 37 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/domain_job.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class DomainJob < ApplicationJob 3 | queue_as :default 4 | sidekiq_options retry: 3 5 | attr_reader :domain, :options, :config 6 | 7 | def perform(domain, *options) 8 | @domain = domain 9 | @config = Wordpress::Config 10 | @options = options.first || {} 11 | if (@options[:action] == "find_or_create_zone" || @options[:action] == :find_or_create_zone) 12 | find_or_create_zone 13 | elsif ( @options[:action] == "find_zone" || @options[:action] == :find_zone) 14 | find_zone 15 | elsif ( @options[:action] == "create_zone" || @options[:action] == :create_zone) 16 | create_zone 17 | end 18 | end 19 | 20 | private 21 | 22 | def find_or_create_zone 23 | return find_zone if find_zone 24 | if create_zone 25 | find_zone 26 | end 27 | end 28 | 29 | def find_zone 30 | if config.cfp_enable 31 | api = cloudflare_api 32 | zone_id = api.find_zone( domain.name ) 33 | domain.update_attribute(:zone_id, zone_id) unless zone_id.blank? 34 | end 35 | end 36 | 37 | def create_zone 38 | data = { 39 | :domain => domain.name, 40 | :submit => '' 41 | } 42 | 43 | url = "#{config.cfp_site}/?action=add" 44 | 45 | cookies = { 46 | :user_api_key => config.cfp_token, 47 | :user_key => config.cfp_user_key, 48 | :cloudflare_email => config.cfp_user 49 | } 50 | client = RestClient::Request.execute url: url, method: :post, payload: data, :cookies => cookies, :headers => { :"Content-Type" => "application/x-www-form-urlencoded" } 51 | if client && client.code == 200 52 | if /Go to console/.match( client.body ) 53 | ActiveAdmin::Comment.create( 54 | resource_type: "Wordpress::Domain", 55 | resource_id: domain.id, 56 | author_type: "AdminUser", 57 | author_id: AdminUser.where(role: "admin")&.first&.id, 58 | body: "成功添加域名到Cloudflare Partner", 59 | namespace: "admin" 60 | ) 61 | else 62 | ActiveAdmin::Comment.create( 63 | namespace: "admin", 64 | resource_type: "Wordpress::Domain", 65 | resource_id: domain.id, 66 | author_type: "AdminUser", 67 | author_id: AdminUser.where(role: "admin")&.first&.id, 68 | body: "CloudFlare is already activated for \"#{domain.name}\" under a different account. If you want to enable CloudFlare through this partner, please log in to your CloudFlare account and choose \"Disconnect\" on your CloudFlare DNS Settings page." 69 | ) 70 | end 71 | end 72 | end 73 | 74 | 75 | def cloudflare_api 76 | cfp_cloudflare = { 77 | api_user: config.cfp_user, 78 | api_token: config.cfp_token 79 | } 80 | Wordpress::Core::Helpers::CloudflareApi.new(cfp_cloudflare) 81 | end 82 | 83 | def log_file 84 | # To create new (and to remove old) logfile, add File::CREAT like; 85 | # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) 86 | File.open('log/domain_job.log', File::WRONLY | File::APPEND | File::CREAT) 87 | end 88 | 89 | end 90 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/proxy_check_job.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class ProxyCheckJob < ApplicationJob 3 | queue_as :wordpress 4 | sidekiq_options retry: 3 5 | attr_reader :proxy 6 | 7 | def perform(proxy) 8 | begin 9 | if proxy.connection_type == "SSH" 10 | Net::SSH.start(proxy.host, proxy.user, :password => proxy.password, :port => proxy.port ) do |ssh| 11 | proxy.status = 1 12 | proxy.save 13 | end 14 | else 15 | proxy.status = 0 16 | proxy.save 17 | nil 18 | end 19 | rescue Exception => e 20 | proxy.status = 0 21 | proxy.save 22 | logger = Logger.new(log_file) 23 | logger.error("Proxy Id:#{proxy.id} ================") 24 | logger.error(I18n.t('active_admin.active_job', message: e.message, default: "ActiveJob: #{e.message}")) 25 | logger.error(e.backtrace.join("\n")) 26 | if /fingerprint/.match(e.message) 27 | system("sed -i \"/#{proxy.host}/d\" .ssh/known_hosts") 28 | end 29 | nil 30 | end 31 | end 32 | 33 | private 34 | 35 | def log_file 36 | File.open('log/proxy_job.log', File::WRONLY | File::APPEND | File::CREAT) 37 | end 38 | 39 | end 40 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/proxy_install_job.rb: -------------------------------------------------------------------------------- 1 | 2 | module Wordpress 3 | class ProxyInstallJob < ApplicationJob 4 | include Wordpress::Routeable 5 | queue_as :wordpress 6 | sidekiq_options retry: 3 7 | attr_reader :proxy, :host 8 | 9 | def perform(proxy,host = nil) 10 | @host = host 11 | logger = Logger.new(log_file) 12 | logger.info("Proxy Id:#{proxy.id} ================") 13 | begin 14 | Net::SSH.start(proxy.host, proxy.user, :password => proxy.password, :port => proxy.port) do |ssh| 15 | logger.info("SSH connected") 16 | centos_ver = 0 17 | channela = ssh.open_channel do |ch| 18 | ch.exec "rpm --eval '%{centos_ver}'" do |ch, success| 19 | if success 20 | ch.on_data do |c, data| 21 | $stdout.print data 22 | centos_ver = data 23 | end 24 | end 25 | end 26 | end 27 | channela.wait 28 | logger.info("Centos #{centos_ver}") 29 | ssh_exec = "" 30 | if centos_ver.to_i == 7 31 | ssh_exec = "curl -o- -L #{server_url("v7")} | sh" 32 | elsif centos_ver.to_i == 8 33 | ssh_exec = "curl -o- -L #{server_url("v8")} | sh" 34 | end 35 | unless ssh_exec.blank? 36 | channel = ssh.open_channel do |ch| 37 | logger.info("SSH Exec:#{ssh_exec}") 38 | ch.exec ssh_exec do |ch, success| 39 | if success 40 | ch.on_data do |c, data| 41 | $stdout.print data 42 | if /Install OK/.match(data) 43 | logger.info("Install OK") 44 | proxy.installed_at = Time.now 45 | proxy.save 46 | end 47 | end 48 | end 49 | end 50 | end 51 | channel.wait 52 | else 53 | nil 54 | end 55 | end 56 | rescue Exception, ActiveJob::DeserializationError => e 57 | logger.error("Proxy Id:#{proxy.id} ================") 58 | logger.error(I18n.t('active_admin.active_job', message: e.message, default: "ActiveJob: #{e.message}")) 59 | logger.error(e.backtrace.join("\n")) 60 | nil 61 | end 62 | end 63 | 64 | protected 65 | 66 | def default_url_options 67 | host || Rails.application.config.active_job.default_url_options || {} 68 | end 69 | 70 | private 71 | 72 | def log_file 73 | # To create new (and to remove old) logfile, add File::CREAT like; 74 | # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) 75 | File.open('log/proxy_install_job.log', File::WRONLY | File::APPEND | File::CREAT) 76 | end 77 | 78 | end 79 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/server_job.rb: -------------------------------------------------------------------------------- 1 | 2 | module Wordpress 3 | class ServerJob < ApplicationJob 4 | include Wordpress::Routeable 5 | queue_as :wordpress 6 | sidekiq_options retry: 3 7 | attr_reader :server, :host 8 | 9 | def perform(server,host = nil) 10 | @host = host 11 | logger = Logger.new(log_file) 12 | logger.info("Sever Id:#{server.id} ================") 13 | begin 14 | Net::SSH.start(server.host, server.host_user, :password => server.host_password, :port => server.host_port) do |ssh| 15 | logger.info("SSH connected") 16 | centos_ver = 0 17 | channela = ssh.open_channel do |ch| 18 | ch.exec "rpm --eval '%{centos_ver}'" do |ch, success| 19 | if success 20 | ch.on_data do |c, data| 21 | $stdout.print data 22 | centos_ver = data 23 | end 24 | end 25 | end 26 | end 27 | channela.wait 28 | logger.info("Centos #{centos_ver}") 29 | ssh_exec = "" 30 | if centos_ver.to_i == 7 31 | ssh_exec = "curl -o- -L #{server_url("v7")} | sh" 32 | elsif centos_ver.to_i == 8 33 | ssh_exec = "curl -o- -L #{server_url("v8")} | sh" 34 | end 35 | unless ssh_exec.blank? 36 | channel = ssh.open_channel do |ch| 37 | logger.info("SSH Exec:#{ssh_exec}") 38 | ch.exec ssh_exec do |ch, success| 39 | if success 40 | ch.on_data do |c, data| 41 | $stdout.print data 42 | if /Install OK/.match(data) 43 | logger.info("Install OK") 44 | server.installed = 1 45 | server.save 46 | end 47 | end 48 | end 49 | end 50 | end 51 | channel.wait 52 | else 53 | nil 54 | end 55 | end 56 | rescue Exception, ActiveJob::DeserializationError => e 57 | logger.error("Sever Id:#{server.id} ================") 58 | logger.error(I18n.t('active_admin.active_job', message: e.message, default: "ActiveJob: #{e.message}")) 59 | logger.error(e.backtrace.join("\n")) 60 | nil 61 | end 62 | end 63 | 64 | protected 65 | 66 | def default_url_options 67 | host || Rails.application.config.active_job.default_url_options || {} 68 | end 69 | 70 | private 71 | 72 | def log_file 73 | # To create new (and to remove old) logfile, add File::CREAT like; 74 | # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) 75 | File.open('log/server_job.log', File::WRONLY | File::APPEND | File::CREAT) 76 | end 77 | 78 | end 79 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/template_job.rb: -------------------------------------------------------------------------------- 1 | require 'wordpress/core/helpers/mysql' 2 | module Wordpress 3 | class TemplateJob < ApplicationJob 4 | queue_as :wordpress 5 | sidekiq_options retry: 3 6 | attr_reader :template 7 | 8 | def perform(template) 9 | @template = template 10 | end 11 | 12 | private 13 | 14 | def log_file 15 | # To create new (and to remove old) logfile, add File::CREAT like; 16 | # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) 17 | File.open('log/template_job.log', File::WRONLY | File::APPEND | File::CREAT) 18 | end 19 | 20 | end 21 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/template_reset_password_job.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class TemplateResetPasswordJob < Wordpress::TemplateJob 3 | 4 | def perform(template) 5 | 6 | begin 7 | config = Wordpress::Config 8 | mysql_info = { user: template.mysql_user, 9 | user_host: config.template_mysql_host , 10 | user_password: template.mysql_password, 11 | database: template.mysql_user, 12 | collection_user: template.mysql_user, 13 | collection_password: template.mysql_password, 14 | collection_host: config.template_mysql_connection_host } 15 | mysql = Wordpress::Core::Helpers::Mysql.new(mysql_info) 16 | 17 | logger = Logger.new(log_file) 18 | logger.info("Template Id:#{template.id} --------") 19 | 20 | Net::SSH.start( config.template_host, config.template_host_user, :password => config.template_host_password, :port => config.template_host_port) do |ssh| 21 | 22 | logger.info("ssh connected") 23 | 24 | channel = ssh.open_channel do |ch| 25 | ch.exec "#{mysql.only_update_password(template.wordpress_password, template.wordpress_user)}" do |ch, success| 26 | ch.on_data do |c, data| 27 | $stdout.print data 28 | logger.info("Database checked: #{mysql_info[:database]}") if /^#{mysql_info[:database]}$/.match(data) 29 | end 30 | end 31 | end 32 | channel.wait 33 | 34 | end 35 | 36 | rescue Exception, ActiveJob::DeserializationError => e 37 | logger = Logger.new(log_file) 38 | logger.error("Template Id:#{template.id} ================") 39 | logger.error(I18n.t('active_admin.active_job', message: e.message, default: "ActiveJob: #{e.message}")) 40 | logger.error(e.backtrace.join("\n")) 41 | nil 42 | end 43 | 44 | end 45 | 46 | private 47 | 48 | def log_file 49 | # To create new (and to remove old) logfile, add File::CREAT like; 50 | # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) 51 | File.open('log/template_reset_password_job.log', File::WRONLY | File::APPEND | File::CREAT) 52 | end 53 | 54 | end 55 | end -------------------------------------------------------------------------------- /core/app/jobs/wordpress/template_tar_job.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class TemplateTarJob < Wordpress::TemplateJob 3 | 4 | def perform(template) 5 | logger = Logger.new(log_file) 6 | begin 7 | logger.info("Template Id:#{template.id} ================") 8 | config = Wordpress::Config 9 | directory = "#{config.template_directory}/#{template.id}" 10 | mysql_info = { 11 | database: template.database, 12 | collection_user: template.mysql_user, 13 | collection_password: template.mysql_password, 14 | collection_host: config.template_mysql_connection_host 15 | } 16 | mysql = Wordpress::Core::Helpers::Mysql.new(mysql_info) 17 | Net::SSH.start( config.template_host, config.template_host_user, :password => config.template_host_password, :port => config.template_host_port ) do |ssh| 18 | logger.info("ssh connected") 19 | channel = ssh.open_channel do |ch| 20 | ssh_exec = "cd #{directory} && #{mysql.dump_mysql} && tar cjf #{template.template_tar_file} #{mysql_info[:database]}.sql wordpress" 21 | logger.info("#{ssh_exec}") 22 | ch.exec ssh_exec do |ch, success| 23 | ch.on_data do |c, data| 24 | $stdout.print data 25 | end 26 | end 27 | end 28 | channel.wait 29 | end 30 | rescue Exception, ActiveJob::DeserializationError => e 31 | logger.error("Template Id:#{template.id} ================") 32 | logger.error(I18n.t('active_admin.active_job', message: e.message, default: "ActiveJob: #{e.message}")) 33 | logger.error(e.backtrace.join("\n")) 34 | nil 35 | end 36 | 37 | end 38 | 39 | private 40 | 41 | def log_file 42 | # To create new (and to remove old) logfile, add File::CREAT like; 43 | # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) 44 | File.open('log/template_tar_job.log', File::WRONLY | File::APPEND | File::CREAT) 45 | end 46 | 47 | end 48 | end -------------------------------------------------------------------------------- /core/app/models/concerns/wordpress/number_generator.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class NumberGenerator < Module 3 | extend ActiveSupport::Concern 4 | BASE = 10 5 | DEFAULT_LENGTH = 9 6 | 7 | attr_accessor :prefix, :length 8 | 9 | def initialize(options) 10 | @prefix = options.fetch(:prefix) 11 | @length = options.fetch(:length, DEFAULT_LENGTH) 12 | @letters = options[:letters] 13 | end 14 | 15 | def included(host) 16 | generator_method = method(:generate_permalink) 17 | generator_instance = self 18 | 19 | host.class_eval do 20 | validates_uniqueness_of :number, presence: true 21 | before_validation do |instance| 22 | instance.number ||= generator_method.call(host) 23 | end 24 | 25 | define_singleton_method(:number_generator) { generator_instance } 26 | end 27 | end 28 | 29 | private 30 | 31 | def generate_permalink(host) 32 | length = @length 33 | 34 | loop do 35 | candidate = new_candidate(length) 36 | return candidate unless host.exists?(number: candidate) 37 | 38 | # If over half of all possible options are taken add another digit. 39 | length += 1 if host.count > Rational(BASE**length, 2) 40 | end 41 | end 42 | 43 | def new_candidate(length) 44 | characters = @letters ? 36 : 10 45 | @prefix + SecureRandom.random_number(characters**length).to_s(characters).rjust(length, '0').upcase 46 | end 47 | end 48 | end -------------------------------------------------------------------------------- /core/app/models/concerns/wordpress/routeable.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module Routeable 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | include Wordpress::Core::Engine.routes.url_helpers 7 | end 8 | 9 | protected 10 | 11 | def default_url_options 12 | Rails.application.routes.default_url_options || {} 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /core/app/models/concerns/wordpress/validates.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module Validates 3 | extend ActiveSupport::Concern 4 | 5 | class EmailValidator < ActiveModel::EachValidator 6 | def validate_each(record, attribute, value) 7 | unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i 8 | record.errors[attribute] << (options[:message] || "is not an email") 9 | end 10 | end 11 | end 12 | 13 | class UrlValidator < ActiveModel::EachValidator 14 | def validate_each(record, attribute, value) 15 | unless value =~ URI::regexp 16 | record.errors[attribute] << (options[:message] || "is not an url") 17 | end 18 | end 19 | end 20 | 21 | class DomainValidator < ActiveModel::EachValidator 22 | def validate_each(record, attribute, value) 23 | unless value =~ /\A([^\/]*\.[^\/]*)\z/i 24 | record.errors[attribute] << (options[:message] || I18n.t("activerecord.errors.models.site.attributes.origin.validator" , default: "域名有误")) 25 | end 26 | end 27 | end 28 | 29 | class RedirectUrlValidator < ActiveModel::EachValidator 30 | def validate_each(record, attribute, value) 31 | unless value =~ /\A(^\/.*)\z/i 32 | record.errors[attribute] << (options[:message] || I18n.t("activerecord.errors.models.site.attributes.redirect_url.validator" , default: "必须'/'开头的站内链接")) 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /core/app/models/wordpress/api_token.rb: -------------------------------------------------------------------------------- 1 | 2 | module Wordpress 3 | class ApiToken < Wordpress::Base 4 | include Wordpress::Core::TokenGenerator 5 | acts_as_paranoid 6 | before_validation :set_token 7 | 8 | with_options presence: true do 9 | validates_uniqueness_of :key, allow_blank: false 10 | validates :name 11 | end 12 | 13 | after_commit :clear_cache 14 | 15 | 16 | def self.api_token_cache(token) 17 | # find_api_token = ApiToken.find_by_key(token) 18 | # return nil if find_api_token.blank? 19 | find_api_token = Rails.cache.fetch("api_token_key_#{token}") do 20 | ApiToken.find_by_key(token) 21 | end 22 | 23 | if find_api_token.blank? 24 | Rails.cache.delete( "api_token_key_#{token}" ) 25 | else 26 | find_api_token 27 | end 28 | 29 | end 30 | 31 | def clear_cache 32 | Rails.cache.delete( "api_token_key_#{self.key}" ) 33 | end 34 | 35 | private 36 | 37 | def set_token 38 | self.key = generate_token if self.key.blank? 39 | end 40 | 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /core/app/models/wordpress/app_configuration.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class AppConfiguration < Preferences::Configuration 3 | 4 | preference :server_directory, :string, default: '/home/deploy/wwwroot/' 5 | 6 | preference :template_origin, :string, default: 'http://localhost/' 7 | preference :template_host, :string, default: '127.0.0.1' 8 | preference :template_host_port, :integer, default: 22 9 | preference :template_host_user, :string, default: 'root' 10 | preference :template_host_password, :string, default: '' 11 | preference :template_directory, :string, default: '/home/deploy/wwwroot/' 12 | 13 | # Mysql 14 | preference :template_mysql_connection_host, :string, default: '127.0.0.1' 15 | preference :template_mysql_host, :string, default: '127.0.0.1' 16 | preference :template_mysql_host_port, :integer, default: 3306 17 | preference :template_mysql_host_user, :string, default: '' 18 | preference :template_mysql_host_password, :string, default: '' 19 | 20 | ## Cloudflare Partner User Api 21 | 22 | preference :cfp_site, :string, default: 'https://dns.advertcn.com' 23 | preference :cfp_user, :string, default: '' 24 | preference :cfp_token, :string, default: '' 25 | preference :cfp_user_key, :string, default: '' 26 | preference :cfp_account_id, :string, default: '' 27 | preference :cfp_user_id, :string, default: '' 28 | preference :cfp_all_in_one_cname, :string, default: '' 29 | preference :cfp_enable, :boolean, default: 0 30 | 31 | 32 | 33 | 34 | end 35 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/auth_configuration.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class AuthConfiguration < Preferences::Configuration 3 | 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /core/app/models/wordpress/base.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Base < ApplicationRecord 3 | include Wordpress::Preferences::Preferable 4 | serialize :preferences, Hash 5 | 6 | after_initialize do 7 | if has_attribute?(:preferences) && !preferences.nil? 8 | self.preferences = default_preferences.merge(preferences) 9 | end 10 | end 11 | 12 | self.abstract_class = true 13 | 14 | def self.belongs_to_required_by_default 15 | false 16 | end 17 | 18 | def self.wordpress_base_scopes 19 | where(nil) 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/blog/mysql_connect.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Blog < Wordpress::Base 3 | module MysqlConnect 4 | extend ActiveSupport::Concern 5 | included do 6 | def post_publish_count 7 | mysql2.post_status('publish') 8 | end 9 | 10 | private 11 | 12 | def mysql2 13 | Wordpress::Core::Helpers::Mysql2Client.new( 14 | :host => server.mysql_host, 15 | :password => server.mysql_password, 16 | :username => server.mysql_user, 17 | :database => self.installed? ? self.mysql_db : nil , 18 | :port => server.mysql_port 19 | ) 20 | end 21 | 22 | end 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/blog/scope.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Blog < Wordpress::Base 3 | module Scope 4 | extend ActiveSupport::Concern 5 | included do 6 | scope :by_states, lambda { |status| where( "#{Blog.table_name}.state": status) unless status.blank? } 7 | scope :processing, -> { by_states("processing") } 8 | scope :pending, -> { by_states("pending") } 9 | scope :installed, -> { by_states("installed") } 10 | scope :published, -> { by_states("published") } 11 | scope :done, -> { by_states("done") } 12 | 13 | scope :ssl_on, -> { where("#{Blog.quoted_table_name}.use_ssl = ?", 1 ) } 14 | scope :ssl_off, -> { where("#{Blog.quoted_table_name}.use_ssl = ?", 0 ) } 15 | scope :published_today , -> { where("#{Blog.quoted_table_name}.published_at >= ?", Date.today ) } 16 | scope :published_month , -> { where("#{Blog.quoted_table_name}.published_at >= ?", Date.today - 30 ) } 17 | scope :cname_null, -> { where("#{Blog.quoted_table_name}.cname is NOT NULL" ) } 18 | end 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/blog/state_machine.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Blog < Wordpress::Base 3 | module StateMachine 4 | extend ActiveSupport::Concern 5 | included do 6 | extend Enumerize 7 | enumerize :state, in: [:pending, :pending_migration, :processing, :installed, :done, :published], default: :pending 8 | state_machine :state, initial: :pending do 9 | # before_transition [:pending ] => :processing, :do => :send_job 10 | before_transition [:processing ] => :installed, :do => :touch_installed_at 11 | before_transition [:done ] => :published, :do => :touch_published_at 12 | 13 | event :install do 14 | transition [:pending, :pending_migration] => :processing 15 | end 16 | 17 | event :processed do 18 | transition [:processing] => :installed 19 | end 20 | 21 | event :has_done do 22 | transition [:installed] => :done 23 | end 24 | 25 | event :error do 26 | transition [:processing] => :pending 27 | end 28 | 29 | event :publish do 30 | transition [:done] => :published 31 | end 32 | 33 | state :pending 34 | state :pending_migration 35 | 36 | state :processing do 37 | validate :validate_server_and_cloudflare 38 | end 39 | state :installed 40 | state :done 41 | state :published do 42 | validate :validate_published 43 | end 44 | end 45 | 46 | def validate_server_and_cloudflare 47 | errors.add(:state, :cannot_install_if_none_server) if server.blank? 48 | errors.add(:state, :cannot_install_if_none_cloudflare) if cloudflare.blank? 49 | end 50 | 51 | def validate_published 52 | errors.add(:state, :cannot_published_if_none_domain) if domain.blank? 53 | end 54 | 55 | private 56 | 57 | # def send_job 58 | # Wordpress::BlogInstallJob.perform_later(self) 59 | # end 60 | 61 | def touch_installed_at 62 | update_attribute(:installed_at, Time.current) 63 | self.set_dns 64 | end 65 | 66 | def touch_published_at 67 | update_attribute(:published_at, Time.current) 68 | self.set_online_dns 69 | # self.create_online_virtual_host 70 | end 71 | end 72 | end 73 | end 74 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/blog/wp_config.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Blog < Wordpress::Base 3 | module WpConfig 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | def update_wp_config 8 | logger = Logger.new(log_file) 9 | begin 10 | Net::SSH.start( server.host, server.host_user, :password => server.host_password) do |ssh| 11 | logger.info("Blog Id:#{self.id} Start! ********************************/") 12 | logger.info("SSH Connected: #{server.host}") 13 | ssh_str = " 14 | #{config_replace_db} 15 | #{config_replace_user} 16 | #{config_replace_db_password} 17 | #{config_replace_db_host} 18 | grep '#{self.mysql_password}' #{directory_wordpress_config} 19 | " 20 | channel = ssh.open_channel do |ch| 21 | puts ssh_str 22 | ch.exec ssh_str do |ch, success| 23 | ch.on_data do |c, data| 24 | $stdout.print data 25 | if /#{self.mysql_password}/.match(data) 26 | logger.info("wp-config.php updated!") 27 | end 28 | end 29 | end 30 | end 31 | channel.wait 32 | logger.info("Blog Id:#{self.id} End! ********************************/") 33 | end 34 | rescue Exception => e 35 | logger.error("Blog Id:#{self.id} ================") 36 | logger.error(e.backtrace.join("\n")) 37 | nil 38 | end 39 | end 40 | 41 | private 42 | 43 | def config_replace_db 44 | "sed -i \"/'DB_NAME'/ c define( 'DB_NAME', '#{mysql_db}' );\" #{directory_wordpress_config}" 45 | end 46 | 47 | def config_replace_user 48 | "sed -i \"/'DB_USER'/ c define( 'DB_USER', '#{self.mysql_user}' );\" #{directory_wordpress_config}" 49 | end 50 | 51 | def config_replace_db_password 52 | "sed -i \"/'DB_PASSWORD'/ c define( 'DB_PASSWORD', '#{self.mysql_password}' );\" #{directory_wordpress_config}" 53 | end 54 | 55 | def config_replace_db_host 56 | "sed -i \"/'DB_HOST'/ c define( 'DB_HOST', '#{server.mysql_host}' );\" #{directory_wordpress_config}" 57 | end 58 | 59 | def directory_wordpress 60 | "#{directory}/wordpress" 61 | end 62 | 63 | def directory_wordpress_config 64 | "#{directory_wordpress}/wp-config.php" 65 | end 66 | 67 | end 68 | end 69 | end 70 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/cloudflare.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Cloudflare < Wordpress::Base 3 | include Wordpress::Cloudflare::Preference 4 | include Validates 5 | acts_as_paranoid 6 | 7 | 8 | has_many :blogs 9 | 10 | scope :active, ->{ where("#{Cloudflare.quoted_table_name}.remaining > 0 and #{Cloudflare.quoted_table_name}.account_id is not null")} 11 | after_commit :clear_cache 12 | 13 | with_options presence: true do 14 | validates_uniqueness_of :api_user, case_sensitive: true, allow_blank: false, scope: :domain 15 | validates :api_user, :api_token, :name , :domain 16 | validates :domain, domain: true 17 | end 18 | before_validation :check_domain, if: :domain_changed? , on: :update 19 | 20 | after_create :rsync_user_id 21 | after_create :rsync_account_id 22 | after_create :rsync_zone_id 23 | 24 | def rsynced? 25 | user_id && zone_id && account_id 26 | end 27 | 28 | def rsync_user_id 29 | cloudflare_api = Wordpress::Core::Helpers::CloudflareApi.new(self) 30 | get_user_id = cloudflare_api.get_user_id 31 | update_attribute(:user_id, get_user_id) if get_user_id 32 | end 33 | 34 | def rsync_account_id 35 | cloudflare_api = Wordpress::Core::Helpers::CloudflareApi.new(self) 36 | get_account_id = cloudflare_api.get_account_id 37 | update_attribute(:account_id, get_account_id) if get_account_id 38 | end 39 | 40 | def rsync_zone_id 41 | cloudflare_api = Wordpress::Core::Helpers::CloudflareApi.new(self) 42 | rsync_zone_id = cloudflare_api.find_or_create_zone(self.domain, self.user_id ) 43 | update_attribute(:zone_id, rsync_zone_id) if rsync_zone_id 44 | end 45 | 46 | def self.cloudflare_cache(cloudflare_id) 47 | return nil if cloudflare_id.nil? 48 | Rails.cache.fetch("cloudflare_key_#{cloudflare_id}") do 49 | Cloudflare.find(cloudflare_id) 50 | end 51 | end 52 | 53 | def clear_cache 54 | Rails.cache.delete( "cloudflare_key_#{self.id}" ) 55 | end 56 | 57 | private 58 | 59 | def check_domain 60 | errors.add(:domain, :cannot_change_if_has_blogs) if blogs.any? 61 | end 62 | 63 | 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /core/app/models/wordpress/cloudflare/preference.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Cloudflare < Wordpress::Base 3 | module Preference 4 | extend ActiveSupport::Concern 5 | included do 6 | # preference :reviews_per_page, :integer, default: 12 7 | 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /core/app/models/wordpress/domain.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Domain < Wordpress::Base 3 | include Validates 4 | acts_as_paranoid 5 | has_many :blogs 6 | 7 | with_options presence: true do 8 | validates_uniqueness_of :name, case_sensitive: true, allow_blank: false 9 | validates :name 10 | end 11 | 12 | validates :name, domain: true 13 | 14 | scope :active, ->{ joins(:blogs) } 15 | scope :cloudflare, ->{ where("#{Domain.quoted_table_name}.zone_id is not null") } 16 | scope :unuse_cloudflare, ->{ where("#{Domain.quoted_table_name}.zone_id is null") } 17 | scope :not_use, -> { where("#{Domain.quoted_table_name}.id not in (?)", active.ids) } 18 | 19 | after_commit :clear_cache 20 | 21 | def cloudflare? 22 | !!zone_id 23 | end 24 | 25 | def self.cache_by_name(domain) 26 | find_domain = Rails.cache.fetch("domain_key_#{domain}") do 27 | Domain.find_by_name(domain) 28 | end 29 | 30 | if find_domain.blank? 31 | Rails.cache.delete( "domain_key_#{domain}" ) 32 | else 33 | find_domain 34 | end 35 | end 36 | 37 | 38 | def blog_cache_by_subname(subdomain) 39 | cnames = [] 40 | 41 | if subdomain.blank? 42 | cnames.push(nil) 43 | cnames.push('') 44 | cnames.push('@') 45 | else 46 | cnames.push(subdomain) 47 | end 48 | 49 | find_blog = Rails.cache.fetch("blog_key_#{self.name}_#{subdomain}") do 50 | find_blogs = blogs.where(cname: cnames ) 51 | find_blogs.last if find_blogs.any? 52 | end 53 | 54 | if find_blog.blank? 55 | Rails.cache.delete( "blog_key_#{self.name}_#{subdomain}" ) 56 | else 57 | find_blog 58 | end 59 | end 60 | 61 | def clear_cache 62 | Rails.cache.delete( "domain_key_#{self.name}" ) 63 | end 64 | 65 | def rsync_cloudflare_zone 66 | # Wordpress::DomainJob.perform_later(self, { action: "create_zone" } ) 67 | Wordpress::DomainJob.perform_later(self, { action: "find_or_create_zone" } ) 68 | # Wordpress::DomainJob.perform_later(self, { action: "find_or_create_zone" } ) 69 | end 70 | 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /core/app/models/wordpress/locale.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Locale < Wordpress::Base 3 | has_many :blogs 4 | has_many :templates 5 | 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /core/app/models/wordpress/monitor.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Monitor < Wordpress::Base 3 | include Wordpress::Monitor::StateMachine 4 | belongs_to :blog 5 | 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /core/app/models/wordpress/monitor/state_machine.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Monitor < Wordpress::Base 3 | module StateMachine 4 | extend ActiveSupport::Concern 5 | included do 6 | extend Enumerize 7 | enumerize :state, in: [:pending, :queue, :completed ], default: :pending 8 | 9 | state_machine :state, initial: :pending do 10 | before_transition [:pending ] => :queue, :do => :touch_queued_at 11 | before_transition [:queue ] => :completed, :do => :touch_completed_at 12 | event :processing do 13 | transition [:pending] => :queue 14 | end 15 | event :complete do 16 | transition [:queue] => :completed 17 | end 18 | end 19 | private 20 | 21 | def touch_queued_at 22 | update_attribute(:queued_at, Time.current) 23 | end 24 | 25 | def touch_completed_at 26 | update_attribute(:completed_at, Time.current) 27 | end 28 | end 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/preference.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Preference < Wordpress::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /core/app/models/wordpress/preferences/cloudflare.rb: -------------------------------------------------------------------------------- 1 | # Use singleton class Wordpress::Preferences::Cloudflare.instance to access 2 | # 3 | # StoreInstance has a persistence flag that is on by default, 4 | # but we disable database persistence in testing to speed up tests 5 | # 6 | 7 | require 'singleton' 8 | 9 | DB_EXCEPTIONS = if defined? PG 10 | [PG::ConnectionBad, ActiveRecord::NoDatabaseError] 11 | elsif defined? Mysql2 12 | [Mysql2::Error::ConnectionError, ActiveRecord::NoDatabaseError] 13 | else 14 | [ActiveRecord::ConnectionNotEstablished, ActiveRecord::NoDatabaseError] 15 | end 16 | 17 | module Wordpress::Preferences 18 | class CloudflareInstance 19 | attr_accessor :persistence 20 | 21 | def initialize 22 | @cache = Rails.cache 23 | @persistence = true 24 | end 25 | 26 | def set(key, value) 27 | @cache.write(key, value) 28 | persist(key, value) 29 | end 30 | alias []= set 31 | 32 | def exist?(key) 33 | @cache.exist?(key) || 34 | should_persist? && Wordpress::Preference.where(key: key).exists? 35 | end 36 | 37 | def get(key) 38 | # return the retrieved value, if it's in the cache 39 | # use unless nil? incase the value is actually boolean false 40 | # 41 | unless (val = @cache.read(key)).nil? 42 | return val 43 | end 44 | 45 | if should_persist? 46 | # If it's not in the cache, maybe it's in the database, but 47 | # has been cleared from the cache 48 | 49 | # does it exist in the database? 50 | val = if preference = Wordpress::Preference.find_by(key: key) 51 | # it does exist 52 | preference.value 53 | else 54 | # use the fallback value 55 | yield 56 | end 57 | 58 | # Cache either the value from the db or the fallback value. 59 | # This avoids hitting the db with subsequent queries. 60 | @cache.write(key, val) 61 | 62 | return val 63 | else 64 | yield 65 | end 66 | end 67 | alias fetch get 68 | 69 | def delete(key) 70 | @cache.delete(key) 71 | destroy(key) 72 | end 73 | 74 | def clear_cache 75 | @cache.clear 76 | end 77 | 78 | private 79 | 80 | def persist(cache_key, value) 81 | return unless should_persist? 82 | 83 | preference = Wordpress::Preference.where(key: cache_key).first_or_initialize 84 | preference.value = value 85 | preference.save 86 | end 87 | 88 | def destroy(cache_key) 89 | return unless should_persist? 90 | 91 | preference = Wordpress::Preference.find_by(key: cache_key) 92 | preference&.destroy 93 | end 94 | 95 | def should_persist? 96 | @persistence && Wordpress::Preference.table_exists? 97 | rescue *DB_EXCEPTIONS # this is fix to make Deploy To Heroku button work 98 | false 99 | end 100 | end 101 | 102 | class Cloudflare < CloudflareInstance 103 | include Singleton 104 | end 105 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/preferences/configuration.rb: -------------------------------------------------------------------------------- 1 | # This takes the preferrable methods and adds some 2 | # syntatic sugar to access the preferences 3 | # 4 | # class App < Configuration 5 | # preference :color, :string 6 | # end 7 | # 8 | # a = App.new 9 | # 10 | # setters: 11 | # a.color = :blue 12 | # a[:color] = :blue 13 | # a.set :color = :blue 14 | # a.preferred_color = :blue 15 | # 16 | # getters: 17 | # a.color 18 | # a[:color] 19 | # a.get :color 20 | # a.preferred_color 21 | # 22 | # 23 | module Wordpress::Preferences 24 | class Configuration 25 | include Wordpress::Preferences::Preferable 26 | 27 | def configure 28 | yield(self) if block_given? 29 | end 30 | 31 | def preferences 32 | ::Wordpress::Preferences::ScopedCloudflare.new(self.class.name.underscore) 33 | end 34 | 35 | def reset 36 | preferences.each do |name, _value| 37 | set_preference name, preference_default(name) 38 | end 39 | end 40 | 41 | alias [] get_preference 42 | alias []= set_preference 43 | 44 | alias get get_preference 45 | 46 | def set(*args) 47 | options = args.extract_options! 48 | options.each do |name, value| 49 | set_preference name, value 50 | end 51 | 52 | set_preference args[0], args[1] if args.size == 2 53 | end 54 | 55 | def method_missing(method, *args) 56 | name = method.to_s.delete('=') 57 | if has_preference? name 58 | if method.to_s =~ /=$/ 59 | set_preference(name, args.first) 60 | else 61 | get_preference name 62 | end 63 | else 64 | super 65 | end 66 | end 67 | end 68 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/preferences/preferable_class_methods.rb: -------------------------------------------------------------------------------- 1 | module Wordpress::Preferences 2 | module PreferableClassMethods 3 | def preference(name, type, *args) 4 | options = args.extract_options! 5 | options.assert_valid_keys(:default) 6 | default = options[:default] 7 | default = -> { options[:default] } unless default.is_a?(Proc) 8 | 9 | # cache_key will be nil for new objects, then if we check if there 10 | # is a pending preference before going to default 11 | define_method preference_getter_method(name) do 12 | preferences.fetch(name) do 13 | default.call 14 | end 15 | end 16 | 17 | define_method preference_setter_method(name) do |value| 18 | value = convert_preference_value(value, type) 19 | preferences[name] = value 20 | 21 | # If this is an activerecord object, we need to inform 22 | # ActiveRecord::Dirty that this value has changed, since this is an 23 | # in-place update to the preferences hash. 24 | preferences_will_change! if respond_to?(:preferences_will_change!) 25 | end 26 | 27 | define_method preference_default_getter_method(name), &default 28 | 29 | define_method preference_type_getter_method(name) do 30 | type 31 | end 32 | end 33 | 34 | def preference_getter_method(name) 35 | "preferred_#{name}".to_sym 36 | end 37 | 38 | def preference_setter_method(name) 39 | "preferred_#{name}=".to_sym 40 | end 41 | 42 | def preference_default_getter_method(name) 43 | "preferred_#{name}_default".to_sym 44 | end 45 | 46 | def preference_type_getter_method(name) 47 | "preferred_#{name}_type".to_sym 48 | end 49 | end 50 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/preferences/scoped_cloudflare.rb: -------------------------------------------------------------------------------- 1 | module Wordpress::Preferences 2 | class ScopedCloudflare 3 | def initialize(prefix, suffix = nil) 4 | @prefix = prefix 5 | @suffix = suffix 6 | end 7 | 8 | def cloudflare 9 | Wordpress::Preferences::Cloudflare.instance 10 | end 11 | 12 | def fetch(key, &block) 13 | cloudflare.fetch(key_for(key), &block) 14 | end 15 | 16 | def []=(key, value) 17 | cloudflare[key_for(key)] = value 18 | end 19 | 20 | def delete(key) 21 | cloudflare.delete(key_for(key)) 22 | end 23 | 24 | private 25 | 26 | def key_for(key) 27 | [rails_cache_id, @prefix, key, @suffix].compact.join('/') 28 | end 29 | 30 | def rails_cache_id 31 | ENV['RAILS_CACHE_ID'] 32 | end 33 | end 34 | end -------------------------------------------------------------------------------- /core/app/models/wordpress/proxy.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Proxy < Wordpress::Base 3 | include Wordpress::Routeable 4 | acts_as_paranoid 5 | CONNECTION_TYPES = %W( SSH SFTP ) 6 | 7 | with_options presence: true do 8 | validates_uniqueness_of :host, allow_blank: false, scope: :user 9 | validates :host, :user , :connection_type, :port , :password 10 | end 11 | 12 | def push_code(api) 13 | if self.connection_type == "SSH" 14 | Net::SSH.start(self.host, self.user, :password => self.password, :port => self.port) do |ssh| 15 | channel = ssh.open_channel do |ch| 16 | ch.exec "rm #{rootpath}/index.php && wget -c --header 'X-Auth-Key: #{api.key}' #{api_code_url} -O #{rootpath}/index.php" do |ch, success| 17 | if success 18 | ch.on_data do |c, data| 19 | $stdout.print data 20 | end 21 | end 22 | end 23 | end 24 | channel.wait 25 | end 26 | else 27 | 28 | end 29 | end 30 | 31 | def rootpath 32 | directory.blank? ? "/var/www/html/" : directory 33 | end 34 | 35 | def test_connection 36 | Wordpress::ProxyCheckJob.perform_now(self) 37 | end 38 | 39 | def install 40 | Wordpress::ProxyInstallJob.perform_later(self) 41 | end 42 | 43 | def installed? 44 | !!installed_at 45 | end 46 | 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /core/app/models/wordpress/server.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Server < Wordpress::Base 3 | acts_as_paranoid 4 | include Validates 5 | belongs_to :cloudflare 6 | has_many :blogs 7 | scope :active, ->{ left_joins(:blogs) 8 | .select("COUNT( #{Blog.quoted_table_name}.id) AS blog_count, #{Server.quoted_table_name}.*").distinct 9 | .group("#{Server.quoted_table_name}.id") 10 | .where("#{Server.quoted_table_name}.host_status = 1 and #{Server.quoted_table_name}.mysql_status = 1") 11 | .having("blog_count < #{Server.quoted_table_name}.max_size") 12 | } 13 | 14 | 15 | with_options presence: true do 16 | validates_uniqueness_of :host, case_sensitive: true, allow_blank: false 17 | # validates :domain, domain: true 18 | validates :cloudflare, :host, :host_user, :host_password, :mysql_host, :mysql_host_user, :mysql_password, :mysql_user 19 | end 20 | 21 | before_validation :check_host, if: :host_password_changed?, on: :update 22 | before_validation :check_hosts, if: :host_changed? , on: :update 23 | before_validation :check_cloudflares, if: :cloudflare_id_changed? , on: :update 24 | 25 | def cname 26 | "server#{self.id}.#{cloudflare.domain}" 27 | end 28 | 29 | def check_cloudflares 30 | errors.add(:cloudflare_id, :cannot_change_if_has_blogs) if blogs.any? 31 | end 32 | 33 | def check_hosts 34 | errors.add(:host, :cannot_change_if_has_blogs) if blogs.any? 35 | end 36 | 37 | def check_host 38 | begin 39 | Net::SSH.start(self.host, self.host_user, :password => self.host_password, :port => self.host_port) do |ssh| 40 | update_attribute(:host_status, 1) 41 | end 42 | rescue Exception => e 43 | if /fingerprint/.match(e.message) 44 | system("sed -i \"/#{self.host}/d\" .ssh/known_hosts") 45 | end 46 | update_attribute(:host_status, 0) 47 | nil 48 | end 49 | end 50 | 51 | def set_dns 52 | rootdomain = cloudflare.domain 53 | cloudflare_api = Wordpress::Core::Helpers::CloudflareApi.new(cloudflare) 54 | proxied = true 55 | update_attribute(:dns_status, 1) if cloudflare_api.create_or_update_dns_a( self.cname, self.host , proxied) 56 | end 57 | 58 | def check_mysql 59 | 60 | mysql_info = { 61 | collection_user: self.mysql_user, 62 | collection_password: self.mysql_password, 63 | collection_host: self.mysql_host 64 | } 65 | mysql = Wordpress::Core::Helpers::Mysql.new(mysql_info) 66 | begin 67 | ok_status = false 68 | Net::SSH.start(self.host, self.host_user, :password => self.host_password, :port => self.host_port) do |ssh| 69 | channel = ssh.open_channel do |ch| 70 | ch.exec "echo 'show databases;' | #{mysql.collection}" do |ch, success| 71 | ch.on_data do |c, data| 72 | # $stdout.print data 73 | ok_status = true if /^mysql$/.match(data) 74 | end 75 | end 76 | end 77 | channel.wait 78 | if ok_status 79 | self.mysql_status = 1 80 | else 81 | self.mysql_status = 0 82 | end 83 | self.save 84 | ok_status 85 | end 86 | rescue Exception => e 87 | self.mysql_status = 0 88 | self.save 89 | nil 90 | end 91 | end 92 | 93 | def install 94 | Wordpress::ServerJob.perform_later(self) 95 | end 96 | 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /core/app/models/wordpress/tag.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class Tag < Wordpress::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /core/app/models/wordpress/tags_blog.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | class TagsBlog < Wordpress::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /core/app/models/wordpress/template.rb: -------------------------------------------------------------------------------- 1 | class UrlValidator < ActiveModel::EachValidator 2 | def validate_each(record, attribute, value) 3 | unless value =~ /\A(https?:\/\/[^\/]*\.[^\/]*\/.*\.gz)\z/i 4 | record.errors[attribute] << (options[:message] || I18n.t("activerecord.errors.models.site.attributes.origin.validator" , default: "文件包必须gz格式")) 5 | end 6 | end 7 | end 8 | 9 | module Wordpress 10 | class Template < Wordpress::Base 11 | acts_as_paranoid 12 | belongs_to :locale 13 | 14 | with_options presence: true do 15 | validates :mysql_password, :install_url, :locale, :name 16 | validates_uniqueness_of :name, case_sensitive: true, allow_blank: false 17 | 18 | end 19 | 20 | validates :install_url, url: true 21 | 22 | before_validation :set_mysql_password 23 | before_validation :set_wordpress_admin_user 24 | before_validation :tar_later, if: :installed_changed? , on: :update 25 | after_create :set_mysql_user 26 | after_create :send_install_job 27 | 28 | 29 | def database 30 | self.mysql_user 31 | end 32 | 33 | def set_mysql_user 34 | update_attribute(:mysql_user, "wp_template_#{self.id}") 35 | end 36 | 37 | def set_mysql_password 38 | self.mysql_password = random_password if mysql_password.blank? 39 | end 40 | 41 | def set_wordpress_admin_user 42 | self.wordpress_user = "admin" 43 | self.wordpress_password = random_password if wordpress_password.blank? 44 | end 45 | 46 | def origin 47 | "#{Wordpress::Config.template_origin}/#{self.id}" 48 | end 49 | 50 | def origin_wordpress 51 | "#{origin}/wordpress" 52 | end 53 | 54 | def down_url 55 | "#{origin}/#{template_tar_file}" 56 | end 57 | 58 | def template_tar_file 59 | "wp-#{self.id}.tar.bz2" 60 | end 61 | 62 | def reset_password 63 | update_attribute(:wordpress_password, random_password) 64 | Wordpress::TemplateResetPasswordJob.perform_later(self) 65 | end 66 | 67 | def send_install_job 68 | Wordpress::TemplateInstallJob.perform_later(self) 69 | end 70 | 71 | def tar_later 72 | Wordpress::TemplateTarJob.perform_later(self) 73 | end 74 | 75 | def tar_now 76 | Wordpress::TemplateTarJob.perform_now(self) 77 | end 78 | 79 | private 80 | 81 | def random_password 82 | random = SecureRandom.urlsafe_base64(nil, false) 83 | "i-#{random}" 84 | end 85 | 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /core/app/views/wordpress/server/install_os7_apache_php74.html.erb: -------------------------------------------------------------------------------- 1 | cat /etc/DIR_COLORS | sed 's/01;34/01;36/' > ~/.dir_colors 2 | rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY* 3 | yum -y install epel-release 4 | #yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 5 | yum -y install https://rpms.remirepo.net/enterprise/remi-release-7.rpm 6 | yum -y install yum-utils 7 | yum-config-manager --enable remi-php74 8 | 9 | yum update -y 10 | yum groupinstall "Development Tools" -y 11 | yum install ncurses-devel -y 12 | yum install wget telnet -y 13 | yum install -y libxslt libxslt-devel libxml2 libxml2-devel libicu-devel 14 | yum -y install expect 15 | yum install ImageMagick -y 16 | yum install ImageMagick-devel -y 17 | yum -y install mysql mysql-devel 18 | yum -y install php-mysql php-pear php-xml php-cli php-imap php-gd php-pdo php-devel php-mbstring php-common php-ldap php php-imagick 19 | yum -y install libmcrypt libmcrypt-devel readline-devel php-mcrypt php-gd php-xml php-mbstring php-ldap php-pear php-xmlrpc php-curl php-memcache php-sockets php-ioncube-loader php-bcmath 20 | yum -y install httpd httpd-manual httpd-devel 21 | yum -y install mod_ssl mod_perl mod_fcgid mod_php 22 | 23 | sed -i 's/AllowOverride None/AllowOverride All/g' /etc/httpd/conf/httpd.conf 24 | sed -i 's/enforcing/disabled/g' /etc/selinux/config 25 | setenforce 0 26 | 27 | systemctl start httpd.service 28 | systemctl enable httpd.service 29 | 30 | systemctl start firewalld 31 | systemctl enable firewalld.service 32 | 33 | firewall-cmd --permanent --zone=public --add-service=http 34 | firewall-cmd --permanent --zone=public --add-service=https 35 | firewall-cmd --permanent --add-port=443/tcp 36 | firewall-cmd --permanent --add-port=80/tcp 37 | firewall-cmd --reload 38 | 39 | if [ ! -d /etc/httpd/conf.d/vhost/ ];then 40 | mkdir /etc/httpd/conf.d/vhost/ -p 41 | echo 'IncludeOptional conf.d/vhost/*.conf' >> /etc/httpd/conf/httpd.conf 42 | echo 'SetEnvIf Ssl-Offloaded 1 HTTPS=on' >> /etc/httpd/conf/httpd.conf 43 | echo 'SetEnvIf Server-Https SSL HTTPS=on' >> /etc/httpd/conf/httpd.conf 44 | echo 'SetEnvIf X-Forwarded-Proto https HTTPS=on' >> /etc/httpd/conf/httpd.conf 45 | fi 46 | 47 | systemctl restart httpd.service 48 | 49 | echo 'Install OK' 50 | -------------------------------------------------------------------------------- /core/app/views/wordpress/server/install_os7_mysqlv8.html.erb: -------------------------------------------------------------------------------- 1 | cat /etc/DIR_COLORS | sed 's/01;34/01;36/' > ~/.dir_colors 2 | 3 | rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 4 | rpm -Uvh http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm 5 | 6 | yum update -y 7 | yum groupinstall "Development Tools" -y 8 | yum install ncurses-devel -y 9 | yum -y install mysql mysql-server mysql-devel 10 | 11 | 12 | service mysqld start 13 | systemctl enable mysqld.service 14 | 15 | 16 | # Set you localehost root password 17 | mysql_secure_installation 18 | 19 | 20 | # Time zone support 21 | mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -uroot -p mysql 22 | 23 | ## create mysql root user for your private network host 24 | # CREATE USER 'root'@'10.10.10.%' IDENTIFIED BY 'password'; 25 | # GRANT ALL PRIVILEGES ON * .* TO 'root'@'10.10.10.%'; 26 | # Change to your host 10.10.10.% and password 27 | echo "CREATE USER 'root'@'10.10.10.%' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON * .* TO 'root'@'10.10.10.%';" | mysql -uroot -p 28 | 29 | 30 | systemctl start firewalld 31 | systemctl enable firewalld.service 32 | firewall-cmd --permanent --add-port=3306/tcp 33 | firewall-cmd --reload 34 | -------------------------------------------------------------------------------- /core/app/views/wordpress/server/install_os8_apache_php74.html.erb: -------------------------------------------------------------------------------- 1 | cat /etc/DIR_COLORS | sed 's/01;34/01;36/' > ~/.dir_colors 2 | 3 | yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm 4 | yum -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm 5 | 6 | dnf -y install dnf-utils 7 | dnf module install php:remi-7.4 -y 8 | 9 | dnf install php php-cli php-common -y 10 | 11 | yum update -y 12 | yum groupinstall "Development Tools" -y 13 | yum install ncurses-devel -y 14 | yum install wget telnet -y 15 | yum install -y libxslt libxslt-devel libxml2 libxml2-devel libicu-devel 16 | yum -y install expect 17 | yum install ImageMagick -y 18 | yum install ImageMagick-devel -y 19 | yum -y install mysql mysql-devel 20 | yum -y install php-mysql php-pear php-xml php-cli php-imap php-gd php-pdo php-devel php-mbstring php-common php-ldap php php-imagick 21 | yum -y install libmcrypt libmcrypt-devel readline-devel php-mcrypt php-gd php-xml php-mbstring php-ldap php-pear php-xmlrpc php-curl php-memcache php-sockets php-ioncube-loader php-bcmath 22 | yum -y install httpd httpd-manual httpd-devel 23 | yum -y install mod_ssl mod_perl mod_fcgid mod_php 24 | 25 | 26 | sed -i '/mod_mpm_prefork/ c LoadModule mpm_prefork_module modules/mod_mpm_prefork.so' /etc/httpd/conf.modules.d/*-mpm.conf 27 | sed -i '/mod_mpm_event/ c #LoadModule mpm_event_module modules/mod_mpm_event.so' /etc/httpd/conf.modules.d/*-mpm.conf 28 | 29 | sed -i 's/AllowOverride None/AllowOverride All/g' /etc/httpd/conf/httpd.conf 30 | sed -i 's/enforcing/disabled/g' /etc/selinux/config 31 | setenforce 0 32 | 33 | systemctl start httpd.service 34 | systemctl enable httpd.service 35 | 36 | systemctl start firewalld 37 | systemctl enable firewalld.service 38 | 39 | firewall-cmd --permanent --zone=public --add-service=http 40 | firewall-cmd --permanent --zone=public --add-service=https 41 | firewall-cmd --permanent --add-port=443/tcp 42 | firewall-cmd --permanent --add-port=80/tcp 43 | firewall-cmd --reload 44 | 45 | if [ ! -d /etc/httpd/conf.d/vhost/ ];then 46 | mkdir /etc/httpd/conf.d/vhost/ -p 47 | echo 'IncludeOptional conf.d/vhost/*.conf' >> /etc/httpd/conf/httpd.conf 48 | echo 'SetEnvIf Ssl-Offloaded 1 HTTPS=on' >> /etc/httpd/conf/httpd.conf 49 | echo 'SetEnvIf Server-Https SSL HTTPS=on' >> /etc/httpd/conf/httpd.conf 50 | echo 'SetEnvIf X-Forwarded-Proto https HTTPS=on' >> /etc/httpd/conf/httpd.conf 51 | fi 52 | 53 | systemctl restart httpd.service 54 | 55 | echo 'Install OK' -------------------------------------------------------------------------------- /core/app/views/wordpress/server/install_os8_mysqlv8.html.erb: -------------------------------------------------------------------------------- 1 | cat /etc/DIR_COLORS | sed 's/01;34/01;36/' > ~/.dir_colors 2 | 3 | rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm 4 | 5 | yum update -y 6 | yum groupinstall "Development Tools" -y 7 | yum install ncurses-devel -y 8 | yum -y install mysql mysql-server mysql-devel 9 | 10 | 11 | service mysqld start 12 | systemctl enable mysqld.service 13 | 14 | 15 | # Set you localehost root password 16 | mysql_secure_installation 17 | 18 | 19 | # Time zone support 20 | mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -uroot -p mysql 21 | 22 | ## create mysql root user for your private network host 23 | # CREATE USER 'root'@'10.10.10.%' IDENTIFIED BY 'password'; 24 | # GRANT ALL PRIVILEGES ON * .* TO 'root'@'10.10.10.%'; 25 | # Change to your host 10.10.10.% and password 26 | echo "CREATE USER 'root'@'10.10.10.%' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON * .* TO 'root'@'10.10.10.%';" | mysql -u root -p 27 | 28 | 29 | systemctl start firewalld 30 | systemctl enable firewalld.service 31 | firewall-cmd --permanent --add-port=3306/tcp 32 | firewall-cmd --reload 33 | -------------------------------------------------------------------------------- /core/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails gems 3 | # installed from the root of your application. 4 | 5 | ENGINE_ROOT = File.expand_path('..', __dir__) 6 | ENGINE_PATH = File.expand_path('../lib/wordpress/core/engine', __dir__) 7 | APP_PATH = File.expand_path('../test/dummy/config/application', __dir__) 8 | 9 | # Set up gems listed in the Gemfile. 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 11 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 12 | 13 | require 'rails/all' 14 | require 'rails/engine/commands' 15 | -------------------------------------------------------------------------------- /core/bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $: << File.expand_path("../test", __dir__) 3 | 4 | require "bundler/setup" 5 | require "rails/plugin/test" 6 | -------------------------------------------------------------------------------- /core/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.assets.precompile += %w( 2 | favicon.ico 3 | icons/arrows.svg 4 | icons/interface.svg 5 | wordpress/favicon.ico 6 | wordpress/logo.png 7 | wordpress/application.css 8 | ) -------------------------------------------------------------------------------- /core/config/routes.rb: -------------------------------------------------------------------------------- 1 | Wordpress::Core::Engine.add_routes do 2 | if defined? authenticate 3 | require 'sidekiq/web' 4 | authenticate :admin_user, lambda { |u| u.admin? } do 5 | mount Sidekiq::Web => '/sidekiq' 6 | end 7 | end 8 | match '/api/v1', to: 'api#show', as: :api, via: [:get, :post] 9 | get '/api/v1/code', to: 'api#code', as: :api_code 10 | get '/server/install/*os', to: 'server#index' , as: :server 11 | get '/server/mysql/install/*os', to: 'server#mysql' , as: :server_mysql 12 | root to: 'home#index' 13 | 14 | end 15 | Wordpress::Core::Engine.draw_routes -------------------------------------------------------------------------------- /core/db/migrate/20200707064855_create_wordpress_blogs.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressBlogs < ActiveRecord::Migration[6.0] 2 | def up 3 | create_table :wordpress_blogs do |t| 4 | t.belongs_to :admin_user 5 | t.belongs_to :server 6 | t.belongs_to :locale 7 | t.belongs_to :cloudflare 8 | t.belongs_to :domain 9 | 10 | ## Site Info 11 | t.string :cname, default: "@" 12 | t.string :name 13 | t.text :description 14 | t.string :number 15 | t.integer :post , null: false, default: 1 16 | t.boolean :use_ssl, null: false, default: 1 17 | t.boolean :dns_status, null: false, default: 0 18 | t.string :state 19 | t.string :status 20 | 21 | 22 | ## Blog Administrator 23 | t.string :user, null: false, default: "admin" 24 | t.string :password, null: false, default: "" 25 | 26 | t.string :mysql_user, null: false, default: "" 27 | t.string :mysql_password, null: false, default: "" 28 | 29 | ## 30 | t.boolean :installed, null: false, default: 0 31 | t.boolean :published, null: false, default: 0 32 | 33 | t.datetime :installed_at 34 | t.datetime :published_at 35 | t.datetime :deleted_at 36 | 37 | t.timestamps 38 | end 39 | add_index :wordpress_blogs, [:number ], name: 'index_blogs_by_number', unique: true 40 | 41 | end 42 | 43 | def down 44 | drop_table :wordpress_blogs 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /core/db/migrate/20200707071606_create_wordpress_cloudflares.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressCloudflares < ActiveRecord::Migration[6.0] 2 | def up 3 | create_table :wordpress_cloudflares do |t| 4 | t.string :name, default: '', null: false 5 | t.text :description 6 | t.string :api_token, default: '', null: false 7 | t.string :api_user, default: '', null: false 8 | t.integer :remaining, default: 3500, null: false 9 | t.datetime :deleted_at 10 | t.timestamps 11 | end 12 | end 13 | 14 | def down 15 | drop_table :wordpress_cloudflares 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /core/db/migrate/20200707074130_create_wordpress_templates.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressTemplates < ActiveRecord::Migration[6.0] 2 | def up 3 | create_table :wordpress_templates do |t| 4 | t.belongs_to :locale 5 | t.string :install_url, null: false, default: "" 6 | t.string :name, null: false, default: "" 7 | t.string :description 8 | 9 | t.string :wordpress_user, null: false, default: "admin" 10 | t.string :wordpress_password, null: false, default: "" 11 | 12 | t.string :mysql_user, null: false, default: "" 13 | t.string :mysql_password, null: false, default: "" 14 | 15 | t.boolean :installed, null: false, default: 0 16 | 17 | t.datetime :deleted_at 18 | t.timestamps 19 | end 20 | end 21 | def down 22 | drop_table :wordpress_templates 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /core/db/migrate/20200707074205_create_wordpress_locales.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressLocales < ActiveRecord::Migration[6.0] 2 | 3 | def up 4 | create_table :wordpress_locales do |t| 5 | t.string :name, null: false, default: "" 6 | t.string :code, null: false, default: "" 7 | t.integer :position, null: false, default: 0 8 | t.timestamps 9 | t.index ["code"], name: "index_locales_on_code", unique: true 10 | t.index ["name"], name: "index_locales_on_name", unique: true 11 | end 12 | end 13 | 14 | def down 15 | drop_table :wordpress_locales 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /core/db/migrate/20200707074412_create_wordpress_servers.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressServers < ActiveRecord::Migration[6.0] 2 | def up 3 | create_table :wordpress_servers do |t| 4 | t.belongs_to :cloudflare 5 | t.integer :max_size, null: false, default: 200 6 | t.string :name 7 | t.text :description 8 | t.datetime :deleted_at 9 | 10 | ## Domain 11 | t.string :domain, null: false, default: "" 12 | 13 | ## Host 14 | t.string :host, null: false, default: "" 15 | t.integer :host_port, null: false, default: 22 16 | t.string :host_user, null: false, default: "" 17 | t.string :host_password, null: false, default: "" 18 | t.boolean :host_status, null: false, default: 0 19 | t.boolean :dns_status, null: false, default: 0 20 | t.boolean :installed, null: false, default: 0 21 | 22 | ## Mysql 23 | t.string :mysql_host, null: false, default: "" 24 | t.string :mysql_host_user, null: false, default: "" 25 | t.integer :mysql_port, null: false, default: 3306 26 | t.string :mysql_user, null: false, default: "" 27 | t.string :mysql_password, null: false, default: "" 28 | t.boolean :mysql_status 29 | 30 | t.timestamps 31 | t.index ["host"], name: "index_servers_on_host", unique: true 32 | 33 | end 34 | end 35 | 36 | def down 37 | drop_table :wordpress_servers 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /core/db/migrate/20200707081610_create_wordpress_tags.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressTags < ActiveRecord::Migration[6.0] 2 | def up 3 | create_table :wordpress_tags do |t| 4 | t.string :name, null: false, default: '' 5 | t.timestamps 6 | end 7 | end 8 | 9 | def down 10 | drop_table :wordpress_tags 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /core/db/migrate/20200707081940_create_wordpress_api_tokens.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressApiTokens < ActiveRecord::Migration[6.0] 2 | def up 3 | create_table :wordpress_api_tokens do |t| 4 | t.string :name, null: false, default: "" 5 | t.string :key, null: false, default: "" 6 | t.datetime :deleted_at 7 | t.timestamps 8 | end 9 | end 10 | 11 | def down 12 | drop_table :wordpress_api_tokens 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /core/db/migrate/20200707082208_create_wordpress_proxies.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressProxies < ActiveRecord::Migration[6.0] 2 | def up 3 | create_table :wordpress_proxies do |t| 4 | t.string :name 5 | t.string :host, null: false, default: "" 6 | t.string :connection_type, null: false, default: "" 7 | t.integer :port, null: false, default: 22 8 | t.string :user, null: false, default: "" 9 | t.string :password, null: false, default: "" 10 | t.text :description 11 | t.boolean :status, default: 0 12 | 13 | t.datetime :deleted_at 14 | t.datetime :uploaded_at 15 | t.datetime :installed_at 16 | 17 | t.timestamps 18 | t.index ["host","user"], name: "index_php_proxies_on_host_and_user", unique: true 19 | end 20 | end 21 | 22 | def down 23 | drop_table :wordpress_proxies 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /core/db/migrate/20200707091733_create_wordpress_tags_blogs.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressTagsBlogs < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :wordpress_tags_blogs do |t| 4 | t.belongs_to :tag 5 | t.belongs_to :blog 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /core/db/migrate/20200707095329_create_wordpress_domains.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressDomains < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :wordpress_domains do |t| 4 | t.string :name , null: false, default: '' 5 | t.text :description 6 | t.string :state 7 | 8 | t.datetime :deleted_at 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /core/db/migrate/20200707125708_add_time_zone_to_admin_user.rb: -------------------------------------------------------------------------------- 1 | class AddTimeZoneToAdminUser < ActiveRecord::Migration[6.0] 2 | def self.up 3 | add_column :admin_users, :time_zone, :string 4 | end 5 | def self.down 6 | remove_column :admin_users, :time_zone 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /core/db/migrate/20200708072401_create_wordpress_preferences.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressPreferences < ActiveRecord::Migration[6.0] 2 | def up 3 | create_table :wordpress_preferences do |t| 4 | t.string :name, limit: 100 5 | t.references :owner, polymorphic: true 6 | t.text :value 7 | t.string :key 8 | t.string :value_type 9 | t.timestamps 10 | end 11 | add_index :wordpress_preferences, [:key], name: 'index_wordpress_preferences_on_key', unique: true 12 | 13 | end 14 | 15 | def down 16 | drop_table :wordpress_preferences 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /core/db/migrate/20200709150917_add_domain_to_cloudflare.rb: -------------------------------------------------------------------------------- 1 | class AddDomainToCloudflare < ActiveRecord::Migration[6.0] 2 | def self.up 3 | add_column :wordpress_cloudflares, :domain, :string 4 | end 5 | def self.down 6 | remove_column :wordpress_cloudflares, :domain 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /core/db/migrate/20200709160438_add_name_to_admin_user.rb: -------------------------------------------------------------------------------- 1 | class AddNameToAdminUser < ActiveRecord::Migration[6.0] 2 | def up 3 | add_column :admin_users, :first_name, :string 4 | add_column :admin_users, :last_name, :string 5 | end 6 | 7 | def down 8 | remove_column :admin_users, :first_name 9 | remove_column :admin_users, :last_name 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /core/db/migrate/20200717035207_add_directory_to_proxy.rb: -------------------------------------------------------------------------------- 1 | class AddDirectoryToProxy < ActiveRecord::Migration[6.0] 2 | def up 3 | add_column :wordpress_proxies, :directory, :string 4 | end 5 | 6 | def down 7 | remove_column :wordpress_proxies, :directory 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /core/db/migrate/20200717132029_remove_domain_to_server.rb: -------------------------------------------------------------------------------- 1 | class RemoveDomainToServer < ActiveRecord::Migration[6.0] 2 | 3 | def up 4 | remove_column :wordpress_servers, :domain 5 | end 6 | 7 | def down 8 | add_column :wordpress_servers, :domain, :string, null: false, default: "" 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /core/db/migrate/20200724094508_add_download_url_to_blogs.rb: -------------------------------------------------------------------------------- 1 | class AddDownloadUrlToBlogs < ActiveRecord::Migration[6.0] 2 | 3 | def up 4 | add_column :wordpress_blogs, :download_url, :string 5 | end 6 | 7 | def down 8 | remove_column :wordpress_blogs, :download_url 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /core/db/migrate/20200726082042_add_trackable_to_admin_user.rb: -------------------------------------------------------------------------------- 1 | class AddTrackableToAdminUser < ActiveRecord::Migration[6.0] 2 | def up 3 | add_column :admin_users, :sign_in_count, :integer , default: 0, null: false 4 | add_column :admin_users, :current_sign_in_at, :datetime 5 | add_column :admin_users, :last_sign_in_at, :datetime 6 | add_column :admin_users, :current_sign_in_ip, :string 7 | add_column :admin_users, :last_sign_in_ip, :string 8 | end 9 | 10 | def down 11 | remove_column :admin_users, :sign_in_count 12 | remove_column :admin_users, :current_sign_in_at 13 | remove_column :admin_users, :last_sign_in_at 14 | remove_column :admin_users, :current_sign_in_ip 15 | remove_column :admin_users, :last_sign_in_ip 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /core/db/migrate/20200728095718_add_user_id_to_cloudflares.rb: -------------------------------------------------------------------------------- 1 | class AddUserIdToCloudflares < ActiveRecord::Migration[6.0] 2 | def self.up 3 | add_column :wordpress_cloudflares, :user_id, :string 4 | end 5 | def self.down 6 | remove_column :wordpress_cloudflares, :user_id 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /core/db/migrate/20200728124406_add_zone_id_to_cloudflares.rb: -------------------------------------------------------------------------------- 1 | class AddZoneIdToCloudflares < ActiveRecord::Migration[6.0] 2 | def self.up 3 | add_column :wordpress_cloudflares, :zone_id, :string 4 | end 5 | def self.down 6 | remove_column :wordpress_cloudflares, :zone_id 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /core/db/migrate/20200729052427_add_account_id_to_cloudflares.rb: -------------------------------------------------------------------------------- 1 | class AddAccountIdToCloudflares < ActiveRecord::Migration[6.0] 2 | def self.up 3 | add_column :wordpress_cloudflares, :account_id, :string 4 | end 5 | def self.down 6 | remove_column :wordpress_cloudflares, :account_id 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /core/db/migrate/20200729084505_add_zone_id_to_domains.rb: -------------------------------------------------------------------------------- 1 | class AddZoneIdToDomains < ActiveRecord::Migration[6.0] 2 | def self.up 3 | add_column :wordpress_domains, :zone_id, :string 4 | end 5 | def self.down 6 | remove_column :wordpress_domains, :zone_id 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /core/db/migrate/20200730071621_create_wordpress_monitors.rb: -------------------------------------------------------------------------------- 1 | class CreateWordpressMonitors < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :wordpress_monitors do |t| 4 | t.belongs_to :blog 5 | t.string :state 6 | t.string :action 7 | t.datetime :queued_at 8 | t.datetime :completed_at 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /core/lib/generators/wordpress/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators' 2 | 3 | # require 'amz/core' 4 | 5 | module Wordpress 6 | module Generators 7 | class InstallGenerator < Rails::Generators::Base 8 | source_root File.expand_path("../templates", __dir__) 9 | 10 | def add_stylesheets 11 | file_path = 'app/assets/stylesheets/active_admin' 12 | begin 13 | append_file("#{file_path}.scss", css_assets) 14 | rescue 15 | append_file("#{file_path}.css.scss", css_assets) 16 | end 17 | end 18 | 19 | def add_javascripts 20 | file_path = 'app/assets/javascripts/active_admin' 21 | append_file("#{file_path}.js", js_assets) 22 | end 23 | 24 | def install_active_admin_role 25 | run 'rails g active_admin_role:install' 26 | end 27 | 28 | def install_activeadmin_addons 29 | run 'rails active_storage:install' 30 | run 'rails g activeadmin_addons:install' 31 | end 32 | 33 | def add_migrations 34 | run 'bundle exec rake railties:install:migrations FROM=wordpress' 35 | end 36 | 37 | def copy_initializer_file 38 | template "initializer.tt", "config/initializers/wordpress.rb" 39 | template "sidekiq.tt", "config/initializers/sidekiq.rb" 40 | template "sidekiq.yml", "config/sidekiq.yml" 41 | end 42 | 43 | def notify_about_routes 44 | insert_into_file(File.join('config', 'routes.rb'), 45 | after: "Rails.application.routes.draw do\n") do 46 | <<-ROUTES.strip_heredoc.indent!(2) 47 | # This line mounts Am z's routes at the root of your application. 48 | # This means, any requests to URLs such as /products, will go to 49 | # Amz::ProductsController. 50 | # If you would like to change where this engine is mounted, simply change the 51 | # :at option to something different. 52 | # 53 | # We ask that you don't use the :as option here, as Amz relies on it being 54 | # the default of "spree". 55 | mount Wordpress::Core::Engine, at: '/' 56 | ROUTES 57 | end 58 | 59 | unless options[:quiet] 60 | puts '*' * 50 61 | puts "We added the following line to your application's config/routes.rb file:" 62 | puts ' ' 63 | puts " mount Wordpress::Core::Engine, at: '/'" 64 | end 65 | end 66 | 67 | def ck_js 68 | insert_into_file(File.join('config/initializers', 'active_admin.rb'), after: "ActiveAdmin.setup do |config|\n") do 69 | <<-EOF 70 | config.register_javascript 'chartkick' 71 | config.register_javascript 'Chart.bundle' 72 | config.favicon = 'wordpress/favicon.ico' 73 | config.site_title_image = 'wordpress/logo.png' 74 | EOF 75 | end 76 | end 77 | 78 | def install_js_packages 79 | packages = " chartkick" 80 | packages += " chart.js" 81 | run "yarn add #{packages}" 82 | end 83 | 84 | private 85 | 86 | def js_assets 87 | to_add = "//= require active_admin/amz\n" 88 | end 89 | 90 | def coffee_assets 91 | to_add = "#= require active_admin/amz\n" 92 | end 93 | 94 | def css_assets 95 | "@import 'active_admin/amz';\n" 96 | end 97 | 98 | end 99 | end 100 | end -------------------------------------------------------------------------------- /core/lib/generators/wordpress/templates/initializer.tt: -------------------------------------------------------------------------------- 1 | Wordpress.configure do |config| 2 | 3 | end -------------------------------------------------------------------------------- /core/lib/generators/wordpress/templates/sidekiq.tt: -------------------------------------------------------------------------------- 1 | Sidekiq.configure_server do |config| 2 | config.redis = { url: 'redis://127.0.0.1:6379/12' } 3 | #config.log_formatter = Sidekiq::Logger::Formatters::JSON.new 4 | end 5 | Sidekiq.configure_client do |config| 6 | config.redis = { url: 'redis://127.0.0.1:6379/12' } 7 | end -------------------------------------------------------------------------------- /core/lib/generators/wordpress/templates/sidekiq.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :verbose: false 3 | :pidfile: ./tmp/pids/sidekiq.pid 4 | :logfile: ./log/sidekiq.log 5 | :concurrency: 4 6 | :queues: 7 | - default 8 | - mailers 9 | - wordpress 10 | -------------------------------------------------------------------------------- /core/lib/tasks/wordpress_sidekiq_task.rake: -------------------------------------------------------------------------------- 1 | namespace :sidekiq do 2 | sidekiq_pid_file = Rails.root+'/tmp/pids/sidekiq.pid' 3 | 4 | desc "Sidekiq stop" 5 | task :stop do 6 | puts "#### Trying to stop Sidekiq Now !!! ####" 7 | if File.exist?(sidekiq_pid_file) 8 | puts "Stopping sidekiq now #PID-#{File.readlines(sidekiq_pid_file).first}..." 9 | system "sidekiqctl stop #{Rails.root}/tmp/pids/sidekiq.pid" # stops sidekiq process here 10 | else 11 | puts "--- Sidekiq Not Running !!!" 12 | end 13 | end 14 | 15 | desc "Sidekiq start" 16 | task :start do 17 | puts "Starting Sidekiq..." 18 | system "bundle exec sidekiq -e#{Rails.env} -C #{Rails.root}/config/sidekiq.yml" # starts sidekiq process here 19 | sleep(2) 20 | puts "Sidekiq started #PID-#{File.readlines(sidekiq_pid_file).first}." 21 | end 22 | 23 | desc "Sidekiq restart" 24 | task :restart do 25 | puts "#### Trying to restart Sidekiq Now !!! ####" 26 | Rake::Task['sidekiq:stop'].invoke 27 | Rake::Task['sidekiq:start'].invoke 28 | puts "#### Sidekiq restarted successfully !!! ####" 29 | end 30 | end -------------------------------------------------------------------------------- /core/lib/tasks/wordpress_tasks.rake: -------------------------------------------------------------------------------- 1 | # desc "Explaining what the task does" 2 | # task :wordpress_core do 3 | # # Task goes here 4 | # end 5 | namespace :wordpress do 6 | desc "Initial" 7 | task :init => :environment do 8 | unless Wordpress::Locale.first 9 | Wordpress::Locale.create(name: 'English (United States)', code: 'en-US', position: 1) 10 | Wordpress::Locale.create(name: 'English (United Kingdom)', code: 'en-GB', position: 2) 11 | Wordpress::Locale.create(name: 'English (Australia)', code: 'en-AU', position: 3) 12 | Wordpress::Locale.create(name: 'English (Canada)', code: 'en-CA', position: 4) 13 | Wordpress::Locale.create(name: 'English (India)', code: 'en-IN', position: 4) 14 | Wordpress::Locale.create(name: 'Spanish (Spain)', code: 'es-ES', position: 4) 15 | end 16 | end 17 | 18 | task :monitor_blog => :environment do 19 | blogs = Blog.publishedBlog.published 20 | puts "Monitor BLog Job #{blogs.size}" 21 | blogs.each do |blog| 22 | blog.monitors.create(action: blog.check_online_job_class_name) 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /core/lib/wordpress/auth/devish.rb: -------------------------------------------------------------------------------- 1 | require 'devise' 2 | require 'devise-encryptable' 3 | Devise.secret_key = SecureRandom.hex(50) 4 | 5 | module Wordpress 6 | module Auth 7 | mattr_accessor :default_secret_key 8 | 9 | def self.config 10 | yield(Wordpress::Auth::Config) 11 | end 12 | 13 | end 14 | end 15 | 16 | Wordpress::Auth.default_secret_key = Devise.secret_key 17 | -------------------------------------------------------------------------------- /core/lib/wordpress/core.rb: -------------------------------------------------------------------------------- 1 | require "wordpress/core/engine" 2 | require 'rails/all' 3 | require 'paranoia' 4 | require 'sidekiq' 5 | require 'rest-client' 6 | require 'mysql2' 7 | require 'state_machine' 8 | require 'net/ssh' 9 | require 'net/sftp' 10 | require 'wordpress/auth/devish' 11 | 12 | #helpers 13 | require 'wordpress/core/helpers/cloudflare_api' 14 | require "wordpress/core/helpers/mysql" 15 | require "wordpress/core/helpers/mysql2_client" 16 | require "wordpress/core/helpers/apache" 17 | 18 | module Wordpress 19 | ROOT_PATH = Pathname.new(File.join(__dir__, "../../")) 20 | 21 | # Used to configure Wordpress. 22 | # 23 | # Example: 24 | # 25 | # Wordpress.configure do |config| 26 | # config.track_inventory_levels = false 27 | # end 28 | # 29 | # This method is defined within the core gem on purpose. 30 | # Some people may only wish to use the Core part of Wordpress. 31 | def self.configure 32 | yield(Wordpress::Config) 33 | end 34 | 35 | def self.config 36 | yield(Wordpress::Config) 37 | end 38 | 39 | module Core 40 | autoload :TokenGenerator, 'wordpress/core/token_generator' 41 | end 42 | 43 | end 44 | 45 | -------------------------------------------------------------------------------- /core/lib/wordpress/core/engine.rb: -------------------------------------------------------------------------------- 1 | 2 | module Wordpress 3 | module Core 4 | class Engine < ::Rails::Engine 5 | Environment = Struct.new( 6 | :preferences, 7 | ) 8 | 9 | isolate_namespace Wordpress 10 | engine_name 'wordpress' 11 | 12 | initializer 'wordpress.environment', before: :load_config_initializers do |app| 13 | app.config.wordpress = Environment.new( Wordpress::AppConfiguration.new ) 14 | Wordpress::Config = app.config.wordpress.preferences 15 | end 16 | 17 | initializer "wordpress.active_job" do |app| 18 | app.config.active_job.queue_adapter = :sidekiq 19 | end 20 | 21 | initializer "wordpress.auth.environment", before: :load_config_initializers do |_app| 22 | Wordpress::Auth::Config = Wordpress::AuthConfiguration.new 23 | end 24 | 25 | initializer "wordpress_auth_devise.check_secret_token" do 26 | if Wordpress::Auth.default_secret_key == Devise.secret_key 27 | puts "[WARNING] You are not setting Devise.secret_key within your application!" 28 | puts "You must set this in config/initializers/devise.rb. Here's an example:" 29 | puts " " 30 | puts %{Devise.secret_key = "#{SecureRandom.hex(50)}"} 31 | end 32 | end 33 | 34 | end 35 | end 36 | end 37 | require 'wordpress/core/routes' 38 | -------------------------------------------------------------------------------- /core/lib/wordpress/core/helpers/apache.rb: -------------------------------------------------------------------------------- 1 | module Wordpress 2 | module Core 3 | module Helpers 4 | class Apache 5 | attr_reader :ssh_info, :virtual 6 | 7 | def initialize( virtual ) 8 | @virtual = virtual 9 | end 10 | 11 | def create_virtual_host(wordpress = '/wordpress/', server_alias = nil) 12 | vhost_file = "/etc/httpd/conf.d/vhost/#{conf_file_name}" 13 | ssh = " 14 | mkdir /etc/httpd/conf.d/vhost -p 15 | echo \"#{virtual_host(wordpress)}\" > #{vhost_file} 16 | service httpd restart 17 | echo 'Restart OK' 18 | " 19 | end 20 | 21 | def mkdir_directory 22 | "mkdir #{virtual[:directory]} -p" 23 | end 24 | 25 | def download_and_install(options) 26 | wordpress_config = "wordpress/wp-config.php" 27 | " 28 | #{mkdir_directory} 29 | if [ ! -f \"#{virtual[:directory]}/#{options[:template][:file_name]}\" ];then 30 | cd #{virtual[:directory]} && wget #{virtual[:wordpress_down_url]} && tar xf #{options[:template][:file_name]} && chown apache:apache ./ -R 31 | sed -i \"s/#{ options[:template][:mysql_user]}/#{options[:blog][:mysql_user]}/g\" #{wordpress_config} 32 | sed -i \"s/#{ options[:template][:mysql_password]}/#{options[:blog][:mysql_password]}/g\" #{wordpress_config} 33 | sed -i \"s/#{ options[:template][:mysql_host] }/#{options[:blog][:mysql_host]}/g\" #{wordpress_config} 34 | sed -i \"/'WP_DEBUG'/a\\\if (\\\$_SERVER['HTTP_X_FORWARDED_HOST']) { \\\$scheme = 'http://'; if (\\\$_SERVER['HTTPS']=='on') { \\\$scheme = 'https://' ;} \\\$home = \\\$scheme.\\\$_SERVER['HTTP_X_FORWARDED_HOST']; \\\$siteurl = \\\$scheme.\\\$_SERVER['HTTP_X_FORWARDED_HOST']; define('WP_HOME', \\\$home); define('WP_SITEURL', \\\$siteurl); }\" #{wordpress_config} 35 | sed -i \"/'WP_DEBUG'/a\\\if (\\\$_SERVER['HTTP_X_FORWARDED_PROTO']=='https') { \\\$_SERVER['HTTPS'] = 'on'; }\" #{wordpress_config} 36 | sed -i \"s/#{options[:template][:id]}\\\/wordpress\\\///g\" #{virtual[:directory]}/wordpress/.htaccess 37 | fi 38 | " 39 | end 40 | 41 | private 42 | 43 | 44 | def conf_file_name 45 | "#{virtual[:server_name]}.conf" 46 | end 47 | 48 | def virtual_host(wordpress, server_alias = nil) 49 | server_alias = "ServerAlias #{server_alias}" unless server_alias.blank? 50 | " 51 |You may have mistyped the address or the page may have moved.
63 |If you are the application owner check the logs for more information.
65 |Maybe you tried to change something you didn't have access to.
63 |If you are the application owner check the logs for more information.
65 |If you are the application owner check the logs for more information.
64 |