├── Procfile ├── public ├── js │ └── main.js └── css │ └── main.css ├── views ├── new.erb ├── form.erb ├── flash.erb ├── header.erb ├── index.erb └── layout.erb ├── config.ru ├── .gitignore ├── Gemfile ├── LICENSE.txt ├── README.md ├── Gemfile.lock └── app.rb /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec thin start -p $PORT -e $RACK_ENV -------------------------------------------------------------------------------- /public/js/main.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | $(".alert").alert(); 3 | }); 4 | -------------------------------------------------------------------------------- /public/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 60px; 3 | padding-bottom: 40px; 4 | } 5 | -------------------------------------------------------------------------------- /views/new.erb: -------------------------------------------------------------------------------- 1 |
2 |

<%= @title %>

3 |
4 |
5 | 6 | <%= partial :form %> 7 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | ENV['RACK_ENV'] ||= 'development' 2 | 3 | require "rubygems" 4 | require "bundler/setup" 5 | require "./app.rb" 6 | 7 | use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public" 8 | 9 | run Oven::App 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | 15 | # YARD artifacts 16 | .yardoc 17 | _yardoc 18 | doc/ 19 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | # Auth 4 | gem 'sinatra_auth_github' 5 | 6 | # Helper 7 | gem 'sinatra-partial' 8 | gem 'sinatra-flash' 9 | 10 | # S3 11 | gem 'fog' 12 | 13 | # Property List 14 | gem 'plist' 15 | 16 | # App server 17 | gem 'thin' 18 | 19 | # HTML entities 20 | gem 'htmlentities', :require => 'htmlentities' 21 | 22 | # Development tools 23 | group :development do 24 | gem 'foreman' 25 | gem 'heroku' 26 | end 27 | -------------------------------------------------------------------------------- /views/form.erb: -------------------------------------------------------------------------------- 1 |
2 | Filename 3 | 4 | 5 | Plist Content 6 | 9 | Be sure that your plist is well-formatted. 10 | 11 | 12 |
13 | -------------------------------------------------------------------------------- /views/flash.erb: -------------------------------------------------------------------------------- 1 | <% if flash[:alert] %> 2 |
3 | 4 | <%= flash[:alert] %> 5 |
6 | <% end %> 7 | 8 | <% if flash[:error] %> 9 |
10 | 11 | <%= flash[:error] %> 12 |
13 | <% end %> 14 | 15 | <% if flash[:success] %> 16 |
17 | 18 | <%= flash[:success] %> 19 |
20 | <% end %> 21 | 22 | <% if flash[:info] %> 23 |
24 | 25 | <%= flash[:info] %> 26 |
27 | <% end %> 28 | -------------------------------------------------------------------------------- /views/header.erb: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /views/index.erb: -------------------------------------------------------------------------------- 1 |
2 |

Plists

3 | New Plist 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% @files.each do |f| %> 16 | 17 | 18 | 19 | 20 | 21 | <% end %> 22 | 23 |
FilenameLast ModifiedActions
<%= f.key %><%= f.last_modified %>Edit
24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Polydice, inc. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <%= partial :header %> 17 | 18 |
19 | 20 | <%= partial :flash %> 21 | 22 | <%= yield %> 23 | 24 |
25 | 26 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Oven 2 | 3 | Simple web app for plist files management. 4 | 5 | ## Intro 6 | 7 | For iOS development, there're tons of useful libraries that fetches plist file online to remotely configure your apps. 8 | 9 | To manage those plist files online for mobile developers can be a trouble, and in order to ease the burden, we created Oven for our in-house use but also open source for everyone else. 10 | 11 | ## Features 12 | 13 | * Simple file browser and manager for S3 14 | * Validation of plist format 15 | * GitHub authentication 16 | 17 | ## Installation 18 | 19 | Though it could be hosted in somewhere else, we recommended Heroku for Oven. 20 | 21 | ``` 22 | git clone git@github.com:polydice/oven.git 23 | cd oven 24 | heroku apps:create 25 | git push heroku master 26 | ``` 27 | 28 | Then you need to configure Oven with S3/GitHub credentials. 29 | 30 | The following environment variables are required for Oven: 31 | 32 | ``` 33 | AWS_ACCESS_KEY_ID 34 | AWS_SECRET_ACCESS_KEY 35 | AWS_REGION 36 | AWS_S3_BUCKET_NAME // The bucket name to put files 37 | 38 | GITHUB_AUTHENTICATION_TEAM_ID // (Optional) Only user in this team can use the app 39 | GITHUB_CLIENT_ID 40 | GITHUB_CLIENT_SECRET 41 | ``` 42 | 43 | To set these variables, use: 44 | 45 | ```heroku config:set KEY1=VALUE1 [KEY2=VALUE2 …]``` 46 | 47 | ## Cocoa Libraries 48 | 49 | To name a few libraries that make use of remote plist files: 50 | 51 | * [GroundControl](https://github.com/mattt/GroundControl) 52 | * [iVersion](https://github.com/nicklockwood/iVersion) 53 | 54 | ## License 55 | 56 | Oven is released under [MIT License](http://www.opensource.org/licenses/MIT) -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | addressable (2.3.2) 5 | builder (3.0.4) 6 | daemons (1.1.9) 7 | eventmachine (1.0.0) 8 | excon (0.16.8) 9 | faraday (0.8.4) 10 | multipart-post (~> 1.1) 11 | faraday_middleware (0.9.0) 12 | faraday (>= 0.7.4, < 0.9) 13 | fog (1.8.0) 14 | builder 15 | excon (~> 0.14) 16 | formatador (~> 0.2.0) 17 | mime-types 18 | multi_json (~> 1.0) 19 | net-scp (~> 1.0.4) 20 | net-ssh (>= 2.1.3) 21 | nokogiri (~> 1.5.0) 22 | ruby-hmac 23 | foreman (0.60.2) 24 | thor (>= 0.13.6) 25 | formatador (0.2.4) 26 | hashie (1.2.0) 27 | heroku (2.33.1) 28 | heroku-api (~> 0.3.6) 29 | launchy (>= 0.3.2) 30 | netrc (~> 0.7.7) 31 | rest-client (~> 1.6.1) 32 | rubyzip 33 | heroku-api (0.3.6) 34 | excon (~> 0.16.7) 35 | htmlentities (4.3.1) 36 | json (1.7.5) 37 | launchy (2.1.2) 38 | addressable (~> 2.3) 39 | mime-types (1.19) 40 | multi_json (1.5.0) 41 | multipart-post (1.1.5) 42 | net-scp (1.0.4) 43 | net-ssh (>= 1.99.1) 44 | net-ssh (2.6.2) 45 | netrc (0.7.7) 46 | nokogiri (1.5.5) 47 | oauth2 (0.5.2) 48 | faraday (~> 0.7) 49 | multi_json (~> 1.0) 50 | octokit (1.19.0) 51 | addressable (~> 2.2) 52 | faraday (~> 0.8) 53 | faraday_middleware (~> 0.9) 54 | hashie (~> 1.2) 55 | multi_json (~> 1.3) 56 | plist (3.1.0) 57 | rack (1.4.1) 58 | rack-protection (1.3.2) 59 | rack 60 | rest-client (1.6.7) 61 | mime-types (>= 1.16) 62 | ruby-hmac (0.4.0) 63 | rubyzip (0.9.9) 64 | sinatra (1.3.3) 65 | rack (~> 1.3, >= 1.3.6) 66 | rack-protection (~> 1.2) 67 | tilt (~> 1.3, >= 1.3.3) 68 | sinatra-flash (0.3.0) 69 | sinatra (>= 1.0.0) 70 | sinatra-partial (0.4.0) 71 | sinatra 72 | sinatra_auth_github (0.12.0) 73 | sinatra (~> 1.0) 74 | warden-github (~> 0.12.0) 75 | thin (1.5.0) 76 | daemons (>= 1.0.9) 77 | eventmachine (>= 0.12.6) 78 | rack (>= 1.0.0) 79 | thor (0.16.0) 80 | tilt (1.3.3) 81 | warden (1.2.1) 82 | rack (>= 1.0) 83 | warden-github (0.12.0) 84 | json (~> 1.5) 85 | oauth2 (~> 0.5.2) 86 | octokit (~> 1.19.0) 87 | rest-client (~> 1.6.1) 88 | warden (~> 1.0) 89 | yajl-ruby (~> 1.1) 90 | yajl-ruby (1.1.0) 91 | 92 | PLATFORMS 93 | ruby 94 | 95 | DEPENDENCIES 96 | fog 97 | foreman 98 | heroku 99 | htmlentities 100 | plist 101 | sinatra-flash 102 | sinatra-partial 103 | sinatra_auth_github 104 | thin 105 | -------------------------------------------------------------------------------- /app.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | require 'sinatra/partial' 3 | require 'sinatra/flash' 4 | require 'sinatra_auth_github' 5 | require 'fog' 6 | require 'plist' 7 | require 'htmlentities' 8 | 9 | module Oven 10 | class App < Sinatra::Base 11 | # For delete 12 | use Rack::MethodOverride 13 | 14 | # Session 15 | enable :sessions 16 | 17 | # Flash 18 | register Sinatra::Flash 19 | 20 | # Partial 21 | register Sinatra::Partial 22 | set :partial_template_engine, :erb 23 | 24 | # GitHub auth 25 | set :github_options, { 26 | :scopes => "user", 27 | :secret => ENV['GITHUB_CLIENT_SECRET'], 28 | :client_id => ENV['GITHUB_CLIENT_ID'], 29 | } 30 | register Sinatra::Auth::Github 31 | 32 | # Some helpers 33 | helpers do 34 | def title 35 | @title ? @title + " | Oven" : "Oven" 36 | end 37 | end 38 | 39 | # Fog 40 | def connection 41 | @connection ||= Fog::Storage.new({ 42 | :provider => 'AWS', 43 | :aws_access_key_id => ENV['AWS_ACCESS_KEY_ID'], 44 | :aws_secret_access_key => ENV['AWS_SECRET_ACCESS_KEY'], 45 | :region => ENV['AWS_REGION'] 46 | }) 47 | end 48 | 49 | def directory 50 | connection.directories.get(ENV['AWS_S3_BUCKET_NAME']) 51 | end 52 | 53 | # Check authentication 54 | before do 55 | github_team_authenticate!(ENV['GITHUB_AUTHENTICATION_TEAM_ID'].to_i) if ENV['GITHUB_AUTHENTICATION_TEAM_ID'] 56 | end 57 | 58 | get '/' do 59 | @files = directory.files 60 | erb :index 61 | end 62 | 63 | get '/plists/new' do 64 | @title = "New Plist" 65 | erb :new 66 | end 67 | 68 | get '/plists/:key/edit' do 69 | file = directory.files.get(params[:key]) 70 | @filename = params[:key] 71 | @content = file.body 72 | @encoded_content = HTMLEntities.new.encode(file.body) 73 | @title = "Edit Plist" 74 | 75 | erb :new 76 | end 77 | 78 | post '/plists' do 79 | @title = "New Plist" 80 | @filename = params[:filename] 81 | @content = params[:content] 82 | 83 | begin 84 | data = Plist::parse_xml(@content) 85 | rescue Exception => e 86 | data = nil 87 | end 88 | 89 | if !@filename =~ /.plist$/ 90 | flash[:error] = 'Your filename should end with ".plist" to be right format.' 91 | erb :new 92 | elsif data.nil? 93 | flash[:error] = 'Your plist content cannot be parsed. Please check again.' 94 | erb :new 95 | else 96 | file = directory.files.create( 97 | :content_type => "application/x-plist", 98 | :key => params[:filename], 99 | :body => params[:content], 100 | :public => true 101 | ) 102 | 103 | flash[:succss] = "The file has been created." 104 | redirect '/' 105 | end 106 | end 107 | 108 | delete '/plists/:key' do 109 | file = directory.files.get(params[:key]) 110 | file.destroy 111 | 112 | flash[:info] = "The file has been deleted." 113 | redirect '/' 114 | end 115 | 116 | get '/logout' do 117 | logout! 118 | redirect 'http://icook.tw/' 119 | end 120 | end 121 | end 122 | --------------------------------------------------------------------------------