diff --git a/Gemfile b/Gemfile index 2da928a..41ce969 100644 --- a/Gemfile +++ b/Gemfile @@ -16,3 +16,6 @@ gem 'capistrano-npm' gem 'thin' gem 'rabl' gem 'oj' +gem 'zip-zip' +gem 'ruby_apk' +gem 'nokogiri' diff --git a/Gemfile.lock b/Gemfile.lock index d9097a3..3b09f6f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -44,12 +44,15 @@ GEM jmespath (1.0.2) multi_json (~> 1.0) json (1.8.3) + mini_portile (0.6.2) minitest (5.8.0) multi_json (1.11.2) mysql2 (0.3.20) net-scp (1.2.1) net-ssh (>= 2.6.5) net-ssh (2.9.2) + nokogiri (1.6.6.2) + mini_portile (~> 0.6.0) oj (2.13.0) rabl (0.11.6) activesupport (>= 2.3.14) @@ -60,6 +63,9 @@ GEM rack (>= 1.0) rake (10.4.2) rmagick (2.15.4) + ruby_apk (0.7.0) + rubyzip + rubyzip (1.1.7) sinatra (1.4.6) rack (~> 1.4) rack-protection (~> 1.4) @@ -86,6 +92,8 @@ GEM tilt (2.0.1) tzinfo (1.2.2) thread_safe (~> 0.1) + zip-zip (0.3) + rubyzip (>= 1.0.0) PLATFORMS ruby @@ -99,11 +107,14 @@ DEPENDENCIES capistrano-npm capistrano-rvm mysql2 + nokogiri oj rabl rake rmagick + ruby_apk sinatra sinatra-activerecord sinatra-contrib thin + zip-zip diff --git a/app.rb b/app.rb index bcf8ab3..4ccb031 100644 --- a/app.rb +++ b/app.rb @@ -39,12 +39,3 @@ def success get '/' do send_file File.join(settings.public_folder, 'index.html') end - -get '/app_release' do - @app_release = AppRelease.first - if @app_release - json @app_release - else - json version_code: 0 - end -end diff --git a/config.yml b/config.yml index 66b804c..3322f03 100644 --- a/config.yml +++ b/config.yml @@ -12,9 +12,11 @@ aws: s3: bucket: 'image.oneplusapp.com' + storage_bucket: 'download.oneplusapp.com' cdn: hosts: ['http://image.oneplusapp.com/'] + storage_host: 'http://download.oneplusapp.com/' yunba: app_key: '561b7f3d860409b810e0d11a' diff --git a/controllers/file.rb b/controllers/file.rb index 50c6638..2326c20 100644 --- a/controllers/file.rb +++ b/controllers/file.rb @@ -5,10 +5,13 @@ credentials: Aws::Credentials.new(settings.aws[:access_key_id], settings.aws[:secret_access_key]) ) -def upload_file_to_s3(uploaded_file) +def upload_file_to_s3(uploaded_file, options = {}) s3 = Aws::S3::Client.new - key = "#{Time.zone.now.strftime('%Y-%m-%d')}/#{SecureRandom.uuid}#{File.extname(uploaded_file[:filename])}" - s3.put_object(acl: 'public-read-write', bucket: settings.s3[:bucket], key: key, + options = { + key: "#{Time.zone.now.strftime('%Y-%m-%d')}/#{SecureRandom.uuid}#{File.extname(uploaded_file[:filename])}", + bucket: settings.s3[:bucket] + }.merge(options) + s3.put_object(acl: 'public-read-write', bucket: options[:bucket], key: options[:key], content_type: uploaded_file[:type], body: uploaded_file[:tempfile]) - key + options[:key] end diff --git a/controllers/release.rb b/controllers/release.rb new file mode 100644 index 0000000..e110d92 --- /dev/null +++ b/controllers/release.rb @@ -0,0 +1,30 @@ +require 'ruby_apk' +require 'nokogiri' + +get '/app_release/android' do + @app_release = AppRelease.first + if @app_release + json @app_release + else + json version_code: 0 + end +end + +post '/app_release/android' do + @app_release = AppRelease.first_or_initialize + @app_release.message = params[:message] + + if params[:archive] + apk = Android::Apk.new(params[:archive][:tempfile].path) + manifest = apk.manifest + xml = Nokogiri::XML(manifest.to_xml) + version_code = xml.root['android:versionCode'].to_i + @app_release.version_code = version_code + + path = upload_file_to_s3(params[:archive], key: params[:archive][:filename], bucket: settings.s3[:storage_bucket]) + @app_release.download_url = settings.cdn[:storage_host] + path + end + + @app_release.save + json @app_release +end diff --git a/public/css/admin.css b/public/css/admin.css index 9d90215..365c96e 100644 --- a/public/css/admin.css +++ b/public/css/admin.css @@ -29,3 +29,7 @@ img.avatar { .thumbnail.recommended { border: 2px solid #FEB724; } + +*[hidden] { + display: none !important; +} diff --git a/public/js/admin.jsx b/public/js/admin.jsx index aa633ec..dc4e81a 100644 --- a/public/js/admin.jsx +++ b/public/js/admin.jsx @@ -10,6 +10,7 @@ const Users = require('./components/users') const User = require('./components/user') const Post = require('./components/post') const Feedbacks = require('./components/feedbacks') +const AndroidRelease = require('./components/androidRelease.jsx') var Routes = ( @@ -18,6 +19,7 @@ var Routes = ( + ) diff --git a/public/js/components/androidRelease.jsx b/public/js/components/androidRelease.jsx new file mode 100644 index 0000000..71517b7 --- /dev/null +++ b/public/js/components/androidRelease.jsx @@ -0,0 +1,85 @@ +const React = require('react') +const {FormControls, Col, Input, Panel, Button} = require('react-bootstrap') +const $ = require('jquery') +const assign = require('object-assign') + +module.exports = React.createClass({ + getInitialState: function() { + return {release: {}, editing: false} + }, + + componentDidMount: function() { + this.loadReleaseFromServer() + }, + + loadReleaseFromServer: function() { + var url = '/app_release/android' + $.ajax({ + url: url, + dataType: 'json', + cache: false, + success: function(data) { + this.setState({release: data, editing: false}) + }.bind(this), + error: function(xhr, status, err) { + console.error(url, status, err.toString()) + }.bind(this) + }) + }, + + edit: function() { + this.setState({editing: true}) + }, + + submitNewRelease: function() { + var url = '/app_release/android' + + var data = new FormData() + var message = this.refs.message.getValue() + var archive = this.refs.archive.getInputDOMNode().files[0] + data.append('message', message) + if (archive) { + data.append('archive', archive) + } + $.ajax({ + url: url, + method: 'POST', + dataType: 'json', + processData: false, + contentType: false, + data: data, + success: function(data) { + this.setState({release: data, editing: false}) + }.bind(this), + error: function(xhr, status, err) { + console.error(url, status, err.toString()) + }.bind(this) + }) + }, + + changeMessage: function() { + this.setState({release: assign(this.state.release, {message: event.target.value})}) + }, + + render: function() { + return ( + +
+ + + +
+ + + + + +
+ +
+ ) + } +}) diff --git a/public/js/components/appNav.jsx b/public/js/components/appNav.jsx index da59c7c..f5ae4b1 100644 --- a/public/js/components/appNav.jsx +++ b/public/js/components/appNav.jsx @@ -15,6 +15,9 @@ module.exports = React.createClass({ Feedbacks + + Android Release +