app.rb 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. require 'flickr/login'
  2. require 'flickraw'
  3. require 'pry'
  4. require 'sequel_enum'
  5. require 'sinatra'
  6. require 'sinatra/config_file'
  7. require 'sinatra/json'
  8. require 'sinatra/reloader'
  9. require 'sinatra/sequel'
  10. config_file 'config.yml'
  11. enable :sessions
  12. set :database, 'sqlite://db.sqlite3'
  13. FlickRaw.api_key = settings.flickr_api_key
  14. FlickRaw.shared_secret = settings.flickr_shared_secret
  15. migration 'create users, licenses, and photos tables' do
  16. database.create_table :users do
  17. column :nsid, String, primary_key: true
  18. column :username, String
  19. column :fullname, String
  20. # column :json, 'text'
  21. # foreign_key :show_license_id, :licenses, null: true, on_delete: :set_null, on_update: :restrict
  22. # column :show_privacy, Integer, default: 0, null: false
  23. # column :show_ignored, 'boolean', default: true, null: false
  24. end
  25. database.create_table :licenses do
  26. column :id, Integer, primary_key: true
  27. column :name, String
  28. column :url, String
  29. end
  30. database.create_table :photos do
  31. column :id, Integer, primary_key: true
  32. # foreign_key :user_id, :users, on_delete: :cascade, on_update: :restrict
  33. foreign_key :owner, :users
  34. # foreign_key :license_id, :licenses, on_delete: :cascade, on_update: :restrict
  35. foreign_key :license, :licenses
  36. column :json, 'text'
  37. column :ignore, 'boolean'
  38. # column :public, 'boolean'
  39. # column :friend, 'boolean'
  40. # column :family, 'boolean'
  41. end
  42. end
  43. migration 'rename photo owner and license columns to user_id and license_id' do
  44. database.alter_table :photos do
  45. drop_foreign_key [:owner]
  46. rename_column :owner, :user_id
  47. add_foreign_key [:user_id], :users
  48. drop_foreign_key [:license]
  49. rename_column :license, :license_id
  50. add_foreign_key [:license_id], :licenses
  51. end
  52. end
  53. migration 'add on delete and update constraints to photo user_id and license_id' do
  54. database.alter_table :photos do
  55. drop_foreign_key [:user_id]
  56. add_foreign_key [:user_id], :users, on_delete: :cascade, on_update: :restrict
  57. drop_foreign_key [:license_id]
  58. add_foreign_key [:license_id], :licenses, on_delete: :cascade, on_update: :restrict
  59. end
  60. end
  61. migration 'add not null constraint to photo user_id and license_id' do
  62. database.alter_table :photos do
  63. set_column_not_null :user_id
  64. set_column_not_null :license_id
  65. end
  66. end
  67. migration 'add json field to user' do
  68. database.alter_table :users do
  69. add_column :json, 'text'
  70. end
  71. database.select(:nsid).from(:users).select_map(:nsid).each do |nsid|
  72. info = OpenStruct.new(flickr.people.getInfo(user_id: nsid).to_hash)
  73. info.timezone = info.timezone.to_hash
  74. info.photos = info.photos.to_hash
  75. database.from(:users).where(nsid: nsid).update(json: info.to_h.to_json)
  76. end
  77. end
  78. migration 'add show license/privacy/ignored fields to user' do
  79. database.alter_table :users do
  80. add_foreign_key :show_license_id, :licenses, null: true, on_delete: :set_null, on_update: :restrict
  81. add_column :show_privacy, Integer, default: 0, null: false
  82. add_column :show_ignored, 'boolean', default: true, null: false
  83. end
  84. end
  85. migration 'add public/friend/family fields to photos' do
  86. database.alter_table :photos do
  87. add_column :public, 'boolean'
  88. add_column :friend, 'boolean'
  89. add_column :family, 'boolean'
  90. end
  91. database.select(:id, :json).from(:photos).select_map([:id, :json]).each do |id, json|
  92. photo = OpenStruct.new(JSON.parse(json))
  93. database.from(:photos).where(id: id).update(public: photo.ispublic != 0, friend: photo.isfriend != 0, family: photo.isfamily != 0)
  94. end
  95. end
  96. class User < Sequel::Model
  97. plugin :enum
  98. one_to_many :photos
  99. many_to_one :show_license, class: :License
  100. enum :show_privacy, [:all, :public, :friends_family, :friends, :family, :private]
  101. unrestrict_primary_key
  102. def flickraw
  103. @flickraw ||= OpenStruct.new(JSON.parse(json))
  104. end
  105. def buddyicon
  106. if flickraw.iconserver.to_i > 0
  107. "https://farm#{flickraw.iconfarm}.staticflickr.com/#{flickraw.iconserver}/buddyicons/#{nsid}.jpg"
  108. else
  109. "https://www.flickr.com/images/buddyicon.gif"
  110. end
  111. end
  112. def photosurl
  113. flickraw.photosurl
  114. end
  115. end
  116. class License < Sequel::Model
  117. one_to_many :photos
  118. unrestrict_primary_key
  119. def as_json(*)
  120. {
  121. id: id,
  122. name: name,
  123. url: url,
  124. }
  125. end
  126. def to_json(*args)
  127. as_json.to_json(*args)
  128. end
  129. end
  130. class Photo < Sequel::Model
  131. many_to_one :user
  132. many_to_one :license
  133. unrestrict_primary_key
  134. def flickraw
  135. @flickraw ||= OpenStruct.new(JSON.parse(json))
  136. end
  137. def as_json(*)
  138. {
  139. id: id,
  140. license: license_id,
  141. ignore: ignore,
  142. img: FlickRaw.url_q(flickraw),
  143. url: FlickRaw.url_photopage(flickraw),
  144. title: flickraw.title,
  145. public: public,
  146. friend: friend,
  147. family: family,
  148. }
  149. end
  150. def to_json(*args)
  151. as_json.to_json(*args)
  152. end
  153. end
  154. helpers Flickr::Login::Helpers
  155. helpers do
  156. def flickr
  157. unless @flickr
  158. @flickr = FlickRaw::Flickr.new(api_key: settings.flickr_api_key, shared_secret: settings.flickr_shared_secret)
  159. @flickr.access_token, @flickr.access_secret = flickr_access_token
  160. end
  161. @flickr
  162. end
  163. end
  164. before do
  165. redirect to('/login?perms=write') unless flickr_user
  166. @user = User.find_or_create(nsid: flickr_user[:user_nsid]) do |user|
  167. user.username = flickr_user[:username]
  168. user.fullname = flickr_user[:fullname]
  169. info = OpenStruct.new(flickr.people.getInfo(user_id: user.nsid).to_hash)
  170. info.timezone = info.timezone.to_hash
  171. info.photos = info.photos.to_hash
  172. user.json = info.to_h.to_json
  173. user.show_license = nil
  174. user.show_privacy = :all
  175. user.show_ignored = true
  176. end
  177. end
  178. get '/' do
  179. @licenses = License.all
  180. @licenses = flickr.photos.licenses.getInfo.map do |flickr_license|
  181. License.create do |license|
  182. license.id = flickr_license.id
  183. license.name = flickr_license.name
  184. license.url = flickr_license.url
  185. end
  186. end if @licenses.count == 0
  187. @show_licenses = [
  188. OpenStruct.new(id: nil, name: 'show photos with any license'),
  189. ] + @licenses
  190. @show_privacies = {
  191. all: 'show public and private photos',
  192. public: 'show only public photos',
  193. friends_family: 'show photos visible to friends and family',
  194. friends: 'show photos visible to only friends',
  195. family: 'show photos visible to only family',
  196. private: 'show completely private photos',
  197. }
  198. @show_ignoreds = {
  199. true => 'show ignored photos',
  200. false => 'hide ignored photos',
  201. }
  202. erb :index
  203. end
  204. get '/logout' do
  205. flickr_clear
  206. redirect to('/')
  207. end
  208. get %r{/photos/([1-8])} do |page|
  209. @user.photos_dataset.delete if params['reload'] == 'true'
  210. page, per_page = page.to_i, 500
  211. photos = @user.photos_dataset.reverse(:id).limit(per_page, (page - 1) * per_page).all
  212. begin
  213. photos = flickr.photos.search(user_id: :me, extras: 'license', per_page: per_page, page: page).map do |flickr_photo|
  214. Photo.create do |photo|
  215. photo.id = flickr_photo.id
  216. photo.user = @user
  217. photo.license_id = flickr_photo.license
  218. photo.json = flickr_photo.to_hash.to_json
  219. photo.ignore = false
  220. photo.public = flickr_photo.ispublic != 0
  221. photo.friend = flickr_photo.isfriend != 0
  222. photo.family = flickr_photo.isfamily != 0
  223. end
  224. end if photos.count == 0
  225. rescue FlickRaw::Error => e
  226. halt 422, json(error: e.message)
  227. rescue Sequel::UniqueConstraintViolation
  228. # sometimes the Flickr API will just keep repeating the same results for subsequent pages
  229. end
  230. halt json path: "/photos/#{page + 1}", photos: photos if photos.count == per_page && page < 8
  231. json photos: photos
  232. end
  233. post '/user' do
  234. halt 422, json(error: 'Missing required parameter(s)') unless %w(show_license show_privacy show_ignored).any? {|param| params[param]}
  235. show_license_id = params['show_license']
  236. if show_license_id
  237. if show_license_id.empty?
  238. show_license = nil
  239. else
  240. show_license_id = show_license_id.to_i
  241. show_license = License[show_license_id]
  242. halt 422, json(error: "Could not find license with ID: #{show_license_id.inspect}") unless show_license
  243. end
  244. @user.show_license = show_license
  245. @user.save
  246. end
  247. show_privacy = params['show_privacy']
  248. if show_privacy
  249. @user.show_privacy = show_privacy
  250. begin
  251. @user.save
  252. rescue Sequel::NotNullConstraintViolation
  253. halt 422, json(error: "Invalid privacy value: #{show_privacy.inspect}")
  254. end
  255. end
  256. show_ignored = params['show_ignored']
  257. if show_ignored
  258. @user.show_ignored = show_ignored == 'true'
  259. @user.save
  260. end
  261. status 204
  262. end
  263. post '/photos' do
  264. ignore = params['ignore'] == 'true'
  265. ids = params['photos'] && params['photos'].is_a?(Array) ? params['photos'] : [params['photo']]
  266. photos = @user.photos_dataset.where(id: ids)
  267. halt 404, json(error: "Could not find photo(s) with ID(s): #{ids.map(&:to_i) - photos.map(:id)}") unless photos.count == ids.size
  268. photos.update(ignore: ignore)
  269. status 204
  270. end
  271. post '/photos/*' do |id|
  272. photo = @user.photos_dataset.where(id: id).first
  273. halt 404, json(error: "Could not find photo with ID: #{id}") unless photo
  274. license_id = params['license'] && params['license'].to_i
  275. license = License[license_id]
  276. halt 422, json(error: "Could not find license with ID: #{license_id.inspect}") unless license
  277. case @user.show_privacy
  278. when :all
  279. # no need to check privacy
  280. when :public
  281. halt 422, json(error: 'Could not change license of non public photo') unless photo.public
  282. when :friends_family
  283. halt 422, json(error: 'Could not change license of non friends and family photo') unless photo.friend && photo.family
  284. when :friends
  285. halt 422, json(error: 'Could not change license of non only friends photo') unless photo.friend && !photo.family
  286. when :family
  287. halt 422, json(error: 'Could not change license of non only family photo') unless !photo.friend && photo.family
  288. when :private
  289. halt 422, json(error: 'Could not change license of non private photo') unless !photo.public && !photo.friend && !photo.family
  290. end
  291. halt 422, json(error: 'Could not change license of photo with unselected license') if @user.show_license && @user.show_license != photo.license
  292. halt 422, json(error: 'Could not change license of ignored photo') if photo.ignore
  293. begin
  294. flickr.photos.licenses.setLicense(photo_id: photo.id, license_id: license.id)
  295. rescue FlickRaw::Error => e
  296. halt 422, json(error: e.message)
  297. end
  298. photo.update(license: license)
  299. status 204
  300. end