├── 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 |
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 |
5 |
6 |
7 |
8 |
9 | Filename
10 | Last Modified
11 | Actions
12 |
13 |
14 |
15 | <% @files.each do |f| %>
16 |
17 | <%= f.key %>
18 | <%= f.last_modified %>
19 | Edit Delete
20 |
21 | <% end %>
22 |
23 |
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 |
27 | © Polydice, Inc. 2013
28 |
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 |
--------------------------------------------------------------------------------