From 9a6ebf3b65b789952fe9e1be4db019538711325b Mon Sep 17 00:00:00 2001 From: bohung Date: Tue, 16 Feb 2021 16:49:58 +0800 Subject: [PATCH] Add copy site feature. --- .../admin/site_panel_controller.rb | 14 +- app/models/site_construct.rb | 13 ++ app/views/admin/site_panel/_form.html.erb | 10 + .../site_panel/_sites_list_table.html.erb | 5 +- config/locales/en.yml | 1 + config/locales/zh_tw.yml | 1 + lib/tasks/copy_site.rake | 181 ++++++++++++++++++ lib/tasks/create_site.rake | 7 +- 8 files changed, 222 insertions(+), 10 deletions(-) create mode 100644 lib/tasks/copy_site.rake diff --git a/app/controllers/admin/site_panel_controller.rb b/app/controllers/admin/site_panel_controller.rb index 861d66c..275f471 100644 --- a/app/controllers/admin/site_panel_controller.rb +++ b/app/controllers/admin/site_panel_controller.rb @@ -83,8 +83,14 @@ class Admin::SitePanelController < OrbitAdminController db_name = site_construct.db_name path = site_construct.path site_construct_id = site_construct.id.to_s - Thread.new do - system("rake create_site:create_site['#{git_template_url}','#{git_extension_url}','#{git_url}','#{ip}','#{user}','#{password}','#{site_name}','#{domain_name}','#{port}','#{db_name}','#{path}','#{site_construct_id}']") + if params[:site_construct][:copy_id].blank? + Thread.new do + system("rake create_site:create_site['#{git_template_url}','#{git_extension_url}','#{git_url}','#{ip}','#{user}','#{password}','#{site_name}','#{domain_name}','#{port}','#{db_name}','#{path}','#{site_construct_id}']") + end + else + Thread.new do + system("rake create_site:copy_site['#{ip}','#{user}','#{password}','#{site_name}','#{domain_name}','#{port}','#{db_name}','#{path}','#{site_construct_id}','#{params[:site_construct][:copy_id]}']") + end end redirect_to "#{admin_site_panel_sites_list_path}?id=#{site_construct_id}" end @@ -162,7 +168,9 @@ class Admin::SitePanelController < OrbitAdminController private def site_construct_params - params.require(:site_construct).permit! + site_construct_params = params.require(:site_construct).permit! + site_construct_params = site_construct_params.except(:copy_id) + return site_construct_params end def site_server_params params.require(:site_server).permit! diff --git a/app/models/site_construct.rb b/app/models/site_construct.rb index eddb422..5c9344c 100644 --- a/app/models/site_construct.rb +++ b/app/models/site_construct.rb @@ -38,4 +38,17 @@ class SiteConstruct record.path = "/home/rulingcom/#{dir_path}" end end + def get_domain_name + scheme = "" + extra_port = "" + if self.port == "443" + scheme = "https://" + else + scheme = "http://" + if self.port != "80" + extra_port = ":#{self.port}" + end + end + return (scheme + self.domain_name + extra_port) + end end \ No newline at end of file diff --git a/app/views/admin/site_panel/_form.html.erb b/app/views/admin/site_panel/_form.html.erb index 49078f8..01f91d4 100644 --- a/app/views/admin/site_panel/_form.html.erb +++ b/app/views/admin/site_panel/_form.html.erb @@ -20,6 +20,16 @@
+
+ <% copy_source = SiteConstruct.find(params[:copy_id]) rescue nil %> + <% if copy_source %> + + + <%= f.hidden_field :copy_id, :value => params[:copy_id] %> + <% end %> +
<%= f.label :server_type ,"Server", :class => "control-label muted" %>
diff --git a/app/views/admin/site_panel/_sites_list_table.html.erb b/app/views/admin/site_panel/_sites_list_table.html.erb index 3b94737..9588ddf 100644 --- a/app/views/admin/site_panel/_sites_list_table.html.erb +++ b/app/views/admin/site_panel/_sites_list_table.html.erb @@ -30,7 +30,10 @@ <% end%> Exec commands " title="See <%=site.domain_name%> detail" class="btn btn-primary see_detail" data-id="<%=site.id.to_s%>">Detail - " title="Delete <%=site.domain_name%> from list" class="btn btn-primary see_detail" data-id="<%=site.id.to_s%>">Delete from list + " title="Delete <%=site.domain_name%> from list" class="btn btn-primary" data-id="<%=site.id.to_s%>">Delete from list + <% if site.status == "finish" || site.status == "closed" %> + " title="Copy site to another site." class="btn btn-primary">Copy site + <% end %> <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 4c47224..dd51bbd 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,5 +1,6 @@ en: client_management: + copy_source: Copy Source see_sites: See sites on the server client_management: Client Management my_sites: My Sites diff --git a/config/locales/zh_tw.yml b/config/locales/zh_tw.yml index 59e304a..a637a5b 100644 --- a/config/locales/zh_tw.yml +++ b/config/locales/zh_tw.yml @@ -1,5 +1,6 @@ zh_tw: client_management: + copy_source: 複製來源 see_sites: 查看主機上的網站 client_management: Client Management my_sites: My Sites diff --git a/lib/tasks/copy_site.rake b/lib/tasks/copy_site.rake new file mode 100644 index 0000000..45fa1c0 --- /dev/null +++ b/lib/tasks/copy_site.rake @@ -0,0 +1,181 @@ +require 'net/ssh' +require 'pathname' +namespace :create_site do + desc "Copy Site from another site" + task :copy_site,[:ip,:user,:password,:site_name,:domain_name,:port,:db_name,:path,:site_construct_id,:template_site_construct_id] => :environment do |task,args| + @password = args.password + template_site = SiteConstruct.find(args.template_site_construct_id) + if args.site_construct_id.blank? + @site_construct = SiteConstruct.new + @site_construct.server_type = (SiteServer.where(:ip=>args.ip).first.server_name rescue args.ip) + @site_construct.site_name = args.site_name + @site_construct.domain_name = args.domain_name + @site_construct.db_name = args.db_name + @site_construct.port = args.port + @site_construct.path = args.path + @site_construct.school_name = args.site_name.split(/[-_]/) + @site_construct.user_id = User.first.id.to_s + @site_construct.save + else + @site_construct = SiteConstruct.find(args.site_construct_id) + end + begin + @site_construct.update(:status=>"creating") + @site_construct.update!(:infos=>[]) + begin + Net::SSH.start(args.ip , args.user , password: args.password) do |ssh| + end + rescue Net::SSH::HostKeyMismatch + system("ssh-keygen -f \"$HOME/.ssh/known_hosts\" -R #{args.ip}") + rescue Errno::ENOTTY + system("ssh-add \"$HOME/.ssh/id_rsa\"") + end + Net::SSH.start(args.ip , args.user , password: args.password) do |ssh| + update_infos("setting nginx for #{args.site_name}") + nginx_setting_texts = ('upstream '+args.site_name+'_sock {\n'+ + ' server unix:'+args.path+'/'+args.site_name+'/tmp/unicorn.sock;\n'+ + '}\n'+ + 'server {\n'+ + ' listen '+args.port+';\n\n'+ + ' root '+args.path+'/'+args.site_name+'/public;\n\n'+ + 'server_name '+args.domain_name+';\n\n'+ + ' client_max_body_size 500m;\n\n'+ + ' location / {\n'+ + ' try_files \$uri \$uri/index.html \$uri.html @app;\n'+ + ' }\n\n'+ + ' location @app {\n'+ + ' proxy_redirect off;\n'+ + ' proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;\n'+ + ' proxy_set_header Host \$http_host;\n'+ + ' proxy_connect_timeout 360;\n'+ + 'proxy_pass http://'+args.site_name+'_sock;\n'+ + ' }\n'+ + '}') + exec_ssh_command_by_sudo_for_create(ssh,"touch /etc/nginx/orbit_sites/#{args.site_name}") + exec_ssh_command_by_sudo_for_create(ssh,"sh -c \"echo '#{nginx_setting_texts}' > /etc/nginx/orbit_sites/#{args.site_name}\"") + update_infos("restarting nginx") + exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' service nginx restart") + update_infos("finish restarting nginx") + exec_ssh_command_by_sudo_for_create(ssh,"mkdir -p #{args.path}") + + update_infos("copying site's files for #{args.site_name}") + exec_ssh_command_for_create(ssh,"sudo -p 'sudo password:' apt-get install -y rsync") + if @site_construct.server_type == template_site.server_type + exec_ssh_command_for_create(ssh,"rsync -arv --delete --exclude={tmp/cache/,tmp/unicorn.sock,tmp/pids/*} #{template_site.path}/#{template_site.site_name}/ #{args.path}/#{args.site_name}/",true) + else + server = SiteServer.where(:server_name => template_site.server_type).first + exec_ssh_command_for_create(ssh,"sudo -p 'sudo password:' apt-get install -y sshpass") + outputs = exec_ssh_command_for_create(ssh,"sshpass -p \"#{server.password}\" rsync -arv --delete --exclude={tmp/cache/,tmp/unicorn.sock,tmp/pids/*} #{server.account}@#{server.ip}:#{template_site.path}/#{template_site.site_name}/ #{args.path}/#{args.site_name}/",true) + if outputs.join("\n").include?("Host key verification failed") + exec_ssh_command_by_sudo_for_create(session,"ssh-keygen -f \"$HOME/.ssh/known_hosts\" -R #{server.ip}") + exec_ssh_command_by_sudo_for_create(session,"ssh-keyscan -H #{server.ip} >> ~/.ssh/known_hosts") + exec_ssh_command_for_create(ssh,"sshpass -p \"#{server.password}\" rsync -arv --delete --exclude={tmp/cache/,tmp/unicorn.sock,tmp/pids/*} #{server.account}@#{server.ip}:#{template_site.path}/#{template_site.site_name}/ #{args.path}/#{args.site_name}/",true) + end + end + update_infos("Finish copying site's files for #{args.site_name}!") + + exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' chown #{args.user}:#{args.user} #{args.path}/#{args.site_name} -R") + exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' chmod 777 #{args.path}/#{args.site_name} -R") + db_setting_text = ssh.exec!("cat #{args.path}/#{args.site_name}/config/mongoid.yml") + update_infos("setting dbname to #{args.db_name}") + exec_ssh_command_by_sudo_for_create(ssh,"sh -c \"echo '#{db_setting_text.gsub(template_site.db_name,args.db_name)}' > #{args.path}/#{args.site_name}/config/mongoid.yml\"",true) + + update_infos("copying db from #{template_site.db_name} to #{args.db_name}") + if @site_construct.server_type == template_site.server_type + exec_ssh_command_by_sudo_for_create(ssh,"mongodump -d #{template_site.db_name} -o #{args.path}/#{args.site_name}/dump_xxxx") + exec_ssh_command_by_sudo_for_create(ssh,"mongorestore --drop -d #{args.db_name} #{args.path}/#{args.site_name}/dump_xxxx/#{template_site.db_name}") + exec_ssh_command_by_sudo_for_create(ssh,"rm -rf #{args.path}/#{args.site_name}/dump_xxxx") + else + exec_ssh_command_by_sudo_for_create(ssh,"sshpass -p '#{server.password}' ssh #{server.account}@#{server.ip} 'mongodump -d #{template_site.db_name} -o #{template_site.path}/#{template_site.site_name}/dump_xxxx'") + exec_ssh_command_for_create(ssh,"sshpass -p \"#{server.password}\" rsync -arv --delete #{server.account}@#{server.ip}:#{template_site.path}/#{template_site.site_name}/dump_xxxx/ #{args.path}/#{args.site_name}/dump_xxxx/") + exec_ssh_command_by_sudo_for_create(ssh,"mongorestore --drop -d #{args.db_name} #{args.path}/#{args.site_name}/dump_xxxx/#{template_site.db_name}") + exec_ssh_command_by_sudo_for_create(ssh,"rm -rf #{args.path}/#{args.site_name}/dump_xxxx") + exec_ssh_command_by_sudo_for_create(ssh,"sshpass -p '#{server.password}' ssh #{server.account}@#{server.ip} 'rm -rf #{template_site.path}/#{template_site.site_name}/dump_xxxx'") + end + update_infos("Finish copying database!") + + update_infos("execing bundle install...") + exec_ssh_command_by_sudo_for_create(ssh,"bash -l -c 'cd #{args.path}/#{args.site_name}\nbundle install'",true) + exec_ssh_command_by_sudo_for_create(ssh,"bash -l -c 'cd #{args.path}/#{args.site_name}\nbundle install'") + update_infos("finish execing bundle install") + update_infos("starting #{args.site_name} web server to development") + outputs = exec_ssh_command_by_sudo_for_create(ssh,"bash -l -c 'cd #{args.path}/#{args.site_name}\nkill -s TERM `fuser tmp/unicorn.sock`\nsudo -p \"sudo password:\" kill -s TERM `sudo -p \"sudo password:\" fuser tmp/unicorn.sock`\nsudo -p \"sudo password:\" rm -f tmp/pids/unicorn.pid\nbundle exec unicorn_rails -c config/unicorn.rb -D -E development\n'") + if outputs.include? "not writable" + exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' chown #{args.user}:#{args.user} #{args.path}/#{args.site_name} -R") + exec_ssh_command_by_sudo_for_create(ssh,"sudo -p 'sudo password:' chmod 777 #{args.path}/#{args.site_name} -R") + end + update_infos("finish creating #{args.site_name}") + exec_ssh_command_by_sudo_for_create(ssh,"chmod 777 #{args.path}/#{args.site_name} -R") + @site_construct.update(:status =>"finish") + puts "finish creating #{args.site_name} on #{args.ip}" + end + rescue =>e + @site_construct.update(:status =>"error",:infos=>@site_construct.infos.push("#{e}")) + end + end + def exec_ssh_command_by_sudo(session,command) + output = session.exec!("echo '#{@password}' | sudo -S #{command}") + # output = session.exec!("echo '#{@password}' | sudo -S -s #{command}") + if output.include?("sudo:") && output.include?("command not found") + output = session.exec!(command) + end + return output + end + def update_thread_infos_for_exec(info,update_last=false) + if update_last + @thread.status["infos"][-1] += info + else + @thread.status["infos"] = @thread.status["infos"].push(info) + end + @thread.save! + return @thread.status["infos"] + end + def exec_ssh_command_for_create(session,command,update=false) + outputs = [] + session.open_channel do |channel| + channel.request_pty do |channel, success| + channel.exec(command) do |ch, success| + abort "could not execute command: #{command}" unless success + channel.on_data do |ch, data| + outputs.push(data) + print "#{data}" + if update + if data.include? "\n" + update_infos_for_exec(data) + else + update_infos_for_exec(data,true) + end + end + if data.to_s.include?("sudo password:") || data.to_s.include?("Password:") + channel.send_data "#{@password}\n" + end + end + end + end + end + session.loop + return outputs + end + def exec_ssh_command_by_sudo_for_create(session,command,update=false) + outputs = exec_ssh_command_for_create(session,command,update) + if outputs.join("\n").include?("Permission denied") || outputs.join("\n").include?("Operation not permitted") + outputs = exec_ssh_command_for_create(session,"sudo -p 'sudo password:' #{command}",update) + end + return outputs.join("\n") + end + def update_infos_for_exec(info,update_last=false) + if update_last && !@site_construct.infos.empty? + @site_construct.infos[-1] += info + else + @site_construct.infos = @site_construct.infos.push(info) + end + @site_construct.save! + return @site_construct.infos + end + def update_infos(info) + puts info + @site_construct.infos = @site_construct.infos.push(info) + @site_construct.save! + return @site_construct.infos + end +end \ No newline at end of file diff --git a/lib/tasks/create_site.rake b/lib/tasks/create_site.rake index 23c4dda..0c676b5 100644 --- a/lib/tasks/create_site.rake +++ b/lib/tasks/create_site.rake @@ -4,7 +4,6 @@ namespace :create_site do desc "Create Site Script" task :create_site,[:git_template_url,:git_extension_url,:git_url,:ip,:user,:password,:site_name,:domain_name,:port,:db_name,:path,:site_construct_id] => :environment do |task,args| @password = args.password - puts args if args.site_construct_id.blank? @site_construct = SiteConstruct.new @site_construct.server_type = (SiteServer.where(:ip=>args.ip).first.server_name rescue args.ip) @@ -56,11 +55,7 @@ namespace :create_site do update_infos("restarting nginx") exec_ssh_command_by_sudo_for_create(ssh,"service nginx restart") update_infos("finish restarting nginx") - dir = Pathname.new(args.path) - while dir.to_s != '/' do - exec_ssh_command_by_sudo_for_create(ssh,"mkdir #{dir}") - dir = dir.dirname - end + exec_ssh_command_by_sudo_for_create(ssh,"mkdir -p #{args.path}") update_infos("cloning orbit4-5 from #{args.git_url} to #{args.path}/#{args.site_name}") exec_ssh_command_by_sudo_for_create(ssh,"git clone #{args.git_url} #{args.path}/#{args.site_name}") exec_ssh_command_by_sudo_for_create(ssh,"bash -l -c 'cd #{args.path}/#{args.site_name} && git submodule add #{args.git_template_url} app/templates/default-theme'")