tentd 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +9 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/Rakefile +8 -0
- data/bin/tent-server +3 -0
- data/lib/tentd.rb +31 -0
- data/lib/tentd/api.rb +58 -0
- data/lib/tentd/api/apps.rb +196 -0
- data/lib/tentd/api/authentication_finalize.rb +12 -0
- data/lib/tentd/api/authentication_lookup.rb +27 -0
- data/lib/tentd/api/authentication_verification.rb +50 -0
- data/lib/tentd/api/authorizable.rb +21 -0
- data/lib/tentd/api/authorization.rb +14 -0
- data/lib/tentd/api/core_profile_data.rb +45 -0
- data/lib/tentd/api/followers.rb +218 -0
- data/lib/tentd/api/followings.rb +241 -0
- data/lib/tentd/api/groups.rb +161 -0
- data/lib/tentd/api/middleware.rb +32 -0
- data/lib/tentd/api/posts.rb +373 -0
- data/lib/tentd/api/profile.rb +78 -0
- data/lib/tentd/api/router.rb +123 -0
- data/lib/tentd/api/router/caching_headers.rb +49 -0
- data/lib/tentd/api/router/extract_params.rb +88 -0
- data/lib/tentd/api/router/serialize_response.rb +38 -0
- data/lib/tentd/api/user_lookup.rb +10 -0
- data/lib/tentd/core_ext/hash/slice.rb +29 -0
- data/lib/tentd/datamapper/array_property.rb +23 -0
- data/lib/tentd/datamapper/query.rb +19 -0
- data/lib/tentd/json_patch.rb +181 -0
- data/lib/tentd/model.rb +30 -0
- data/lib/tentd/model/app.rb +68 -0
- data/lib/tentd/model/app_authorization.rb +113 -0
- data/lib/tentd/model/follower.rb +105 -0
- data/lib/tentd/model/following.rb +100 -0
- data/lib/tentd/model/group.rb +24 -0
- data/lib/tentd/model/mention.rb +19 -0
- data/lib/tentd/model/notification_subscription.rb +56 -0
- data/lib/tentd/model/permissible.rb +227 -0
- data/lib/tentd/model/permission.rb +28 -0
- data/lib/tentd/model/post.rb +178 -0
- data/lib/tentd/model/post_attachment.rb +27 -0
- data/lib/tentd/model/post_version.rb +64 -0
- data/lib/tentd/model/profile_info.rb +80 -0
- data/lib/tentd/model/random_public_id.rb +46 -0
- data/lib/tentd/model/serializable.rb +58 -0
- data/lib/tentd/model/type_properties.rb +36 -0
- data/lib/tentd/model/user.rb +39 -0
- data/lib/tentd/model/user_scoped.rb +14 -0
- data/lib/tentd/notifications.rb +13 -0
- data/lib/tentd/notifications/girl_friday.rb +30 -0
- data/lib/tentd/notifications/sidekiq.rb +50 -0
- data/lib/tentd/tent_type.rb +20 -0
- data/lib/tentd/tent_version.rb +41 -0
- data/lib/tentd/version.rb +3 -0
- data/spec/fabricators/app_authorizations_fabricator.rb +5 -0
- data/spec/fabricators/apps_fabricator.rb +11 -0
- data/spec/fabricators/followers_fabricator.rb +14 -0
- data/spec/fabricators/followings_fabricator.rb +17 -0
- data/spec/fabricators/groups_fabricator.rb +3 -0
- data/spec/fabricators/mentions_fabricator.rb +3 -0
- data/spec/fabricators/notification_subscriptions_fabricator.rb +4 -0
- data/spec/fabricators/permissions_fabricator.rb +1 -0
- data/spec/fabricators/post_attachments_fabricator.rb +8 -0
- data/spec/fabricators/post_versions_fabricator.rb +12 -0
- data/spec/fabricators/posts_fabricator.rb +12 -0
- data/spec/fabricators/profile_infos_fabricator.rb +30 -0
- data/spec/integration/api/apps_spec.rb +466 -0
- data/spec/integration/api/followers_spec.rb +535 -0
- data/spec/integration/api/followings_spec.rb +688 -0
- data/spec/integration/api/groups_spec.rb +207 -0
- data/spec/integration/api/posts_spec.rb +874 -0
- data/spec/integration/api/profile_spec.rb +285 -0
- data/spec/integration/api/router_spec.rb +102 -0
- data/spec/integration/model/app_authorization_spec.rb +59 -0
- data/spec/integration/model/app_spec.rb +63 -0
- data/spec/integration/model/follower_spec.rb +344 -0
- data/spec/integration/model/following_spec.rb +97 -0
- data/spec/integration/model/group_spec.rb +39 -0
- data/spec/integration/model/notification_subscription_spec.rb +145 -0
- data/spec/integration/model/post_spec.rb +658 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/expect_server.rb +3 -0
- data/spec/support/json_request.rb +54 -0
- data/spec/support/with_constant.rb +23 -0
- data/spec/support/with_warnings.rb +6 -0
- data/spec/unit/api/authentication_finalize_spec.rb +45 -0
- data/spec/unit/api/authentication_lookup_spec.rb +65 -0
- data/spec/unit/api/authentication_verification_spec.rb +50 -0
- data/spec/unit/api/authorizable_spec.rb +50 -0
- data/spec/unit/api/authorization_spec.rb +44 -0
- data/spec/unit/api/caching_headers_spec.rb +121 -0
- data/spec/unit/core_profile_data_spec.rb +64 -0
- data/spec/unit/json_patch_spec.rb +407 -0
- data/spec/unit/tent_type_spec.rb +28 -0
- data/spec/unit/tent_version_spec.rb +68 -0
- data/tentd.gemspec +36 -0
- metadata +435 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
module TentD
|
|
2
|
+
class API
|
|
3
|
+
class Groups
|
|
4
|
+
include Router
|
|
5
|
+
|
|
6
|
+
class AuthorizeRead < Middleware
|
|
7
|
+
def action(env)
|
|
8
|
+
authorize_env!(env, :read_groups)
|
|
9
|
+
env
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class AuthorizeWrite < Middleware
|
|
14
|
+
def action(env)
|
|
15
|
+
authorize_env!(env, :write_groups)
|
|
16
|
+
env
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class GetActualId < Middleware
|
|
21
|
+
def action(env)
|
|
22
|
+
[:group_id, :before_id, :since_id].each do |id_key|
|
|
23
|
+
if env.params[id_key]
|
|
24
|
+
if g = Model::Group.first(:public_id => env.params[id_key])
|
|
25
|
+
env.params[id_key] = g.id
|
|
26
|
+
else
|
|
27
|
+
env.params[id_key] = nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
env
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class GetCount < Middleware
|
|
36
|
+
def action(env)
|
|
37
|
+
env.params.return_count = true
|
|
38
|
+
env
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class GetAll < Middleware
|
|
43
|
+
def action(env)
|
|
44
|
+
conditions = {}
|
|
45
|
+
conditions[:id.lt] = env.params.before_id if env.params.before_id
|
|
46
|
+
conditions[:id.gt] = env.params.since_id if env.params.since_id
|
|
47
|
+
conditions[:limit] = [env.params.limit.to_i, MAX_PER_PAGE].min if env.params.limit
|
|
48
|
+
conditions[:limit] ||= PER_PAGE
|
|
49
|
+
if env.params.return_count
|
|
50
|
+
env.response = Model::Group.count(conditions)
|
|
51
|
+
else
|
|
52
|
+
conditions[:order] = :id.desc
|
|
53
|
+
if conditions[:limit] == 0
|
|
54
|
+
env.response = []
|
|
55
|
+
else
|
|
56
|
+
env.response = Model::Group.all(conditions)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
env
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class GetOne < Middleware
|
|
64
|
+
def action(env)
|
|
65
|
+
if group = Model::Group.first(:id => env.params[:group_id])
|
|
66
|
+
env['response'] = group
|
|
67
|
+
end
|
|
68
|
+
env
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class Update < Middleware
|
|
73
|
+
def action(env)
|
|
74
|
+
if group = Model::Group.first(:id => env.params[:group_id])
|
|
75
|
+
group.update(:name => env.params.data.name)
|
|
76
|
+
env['response'] = group.reload
|
|
77
|
+
end
|
|
78
|
+
env
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class Create < Middleware
|
|
83
|
+
def action(env)
|
|
84
|
+
group_attributes = env.params[:data]
|
|
85
|
+
if group = Model::Group.create(group_attributes)
|
|
86
|
+
env.response = group
|
|
87
|
+
env.notify_action = 'create'
|
|
88
|
+
env.notify_instance = group
|
|
89
|
+
end
|
|
90
|
+
env
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class Destroy < Middleware
|
|
95
|
+
def action(env)
|
|
96
|
+
if (group = Model::Group.first(:id => env.params.group_id)) && group.destroy
|
|
97
|
+
env.response = ''
|
|
98
|
+
env.notify_action = 'delete'
|
|
99
|
+
env.notify_instance = group
|
|
100
|
+
end
|
|
101
|
+
env
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class Notify < Middleware
|
|
106
|
+
def action(env)
|
|
107
|
+
return env unless group = env.notify_instance
|
|
108
|
+
post = Model::Post.create(
|
|
109
|
+
:type => 'https://tent.io/types/post/group/v0.1.0',
|
|
110
|
+
:entity => env['tent.entity'],
|
|
111
|
+
:content => {
|
|
112
|
+
:id => group.public_id,
|
|
113
|
+
:name => group.name,
|
|
114
|
+
:action => env.notify_action
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
Notifications.trigger(:type => post.type.uri, :post_id => post.id)
|
|
118
|
+
env
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
get '/groups' do |b|
|
|
123
|
+
b.use AuthorizeRead
|
|
124
|
+
b.use GetActualId
|
|
125
|
+
b.use GetAll
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
get '/groups/count' do |b|
|
|
129
|
+
b.use AuthorizeRead
|
|
130
|
+
b.use GetActualId
|
|
131
|
+
b.use GetCount
|
|
132
|
+
b.use GetAll
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
get '/groups/:group_id' do |b|
|
|
136
|
+
b.use AuthorizeRead
|
|
137
|
+
b.use GetActualId
|
|
138
|
+
b.use GetOne
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
put '/groups/:group_id' do |b|
|
|
142
|
+
b.use AuthorizeWrite
|
|
143
|
+
b.use GetActualId
|
|
144
|
+
b.use Update
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
post '/groups' do |b|
|
|
148
|
+
b.use AuthorizeWrite
|
|
149
|
+
b.use Create
|
|
150
|
+
b.use Notify
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
delete '/groups/:group_id' do |b|
|
|
154
|
+
b.use AuthorizeWrite
|
|
155
|
+
b.use GetActualId
|
|
156
|
+
b.use Destroy
|
|
157
|
+
b.use Notify
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'hashie'
|
|
2
|
+
|
|
3
|
+
module TentD
|
|
4
|
+
class API
|
|
5
|
+
class Middleware
|
|
6
|
+
include Authorizable
|
|
7
|
+
|
|
8
|
+
def initialize(app)
|
|
9
|
+
@app = app
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(env)
|
|
13
|
+
env = Hashie::Mash.new(env) unless env.kind_of?(Hashie::Mash)
|
|
14
|
+
response = action(env)
|
|
15
|
+
response.kind_of?(Hash) ? @app.call(response) : response
|
|
16
|
+
rescue Unauthorized
|
|
17
|
+
[403, {}, ['Unauthorized']]
|
|
18
|
+
rescue DataMapper::SaveFailureError, DataObjects::IntegrityError
|
|
19
|
+
[422, {}, ['Invalid Attributes']]
|
|
20
|
+
rescue Exception => e
|
|
21
|
+
if ENV['RACK_ENV'] == 'test'
|
|
22
|
+
raise
|
|
23
|
+
elsif defined?(Airbrake)
|
|
24
|
+
Airbrake.notify_or_ignore(e, :rack_env => env)
|
|
25
|
+
else
|
|
26
|
+
puts $!.inspect, $@
|
|
27
|
+
end
|
|
28
|
+
[500, {}, ['Internal Server Error']]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
module TentD
|
|
2
|
+
class API
|
|
3
|
+
class Posts
|
|
4
|
+
include Router
|
|
5
|
+
|
|
6
|
+
class GetActualId < Middleware
|
|
7
|
+
def action(env)
|
|
8
|
+
id_mapping = [:post_id, :since_id, :before_id].select { |key| env.params.has_key?(key) }.inject({}) { |memo, key|
|
|
9
|
+
memo[env.params[key]] = key
|
|
10
|
+
env.params[key] = nil
|
|
11
|
+
memo
|
|
12
|
+
}
|
|
13
|
+
posts = Model::Post.all(:public_id => id_mapping.keys, :fields => [:id, :public_id])
|
|
14
|
+
posts.each do |post|
|
|
15
|
+
key = id_mapping[post.public_id]
|
|
16
|
+
env.params[key] = post.id
|
|
17
|
+
end
|
|
18
|
+
env
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class GetOne < Middleware
|
|
23
|
+
def action(env)
|
|
24
|
+
if authorize_env?(env, :read_posts)
|
|
25
|
+
conditions = { :id => env.params.post_id }
|
|
26
|
+
unless env.current_auth.post_types.include?('all')
|
|
27
|
+
conditions[:type_base] = env.current_auth.post_types.map { |t| TentType.new(t).base }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if env.params.version
|
|
31
|
+
conditions[:fields] = [:id]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
post = Model::Post.first(conditions)
|
|
35
|
+
else
|
|
36
|
+
post = Model::Post.find_with_permissions(env.params.post_id, env.current_auth)
|
|
37
|
+
end
|
|
38
|
+
if post
|
|
39
|
+
if env.params.version
|
|
40
|
+
if post_version = post.versions.first(:version => env.params.version.to_i)
|
|
41
|
+
post = post_version
|
|
42
|
+
else
|
|
43
|
+
post = nil
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
env.response = post
|
|
48
|
+
end
|
|
49
|
+
env
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class GetCount < Middleware
|
|
54
|
+
def action(env)
|
|
55
|
+
env.params.return_count = true
|
|
56
|
+
env
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class GetFeed < Middleware
|
|
61
|
+
def action(env)
|
|
62
|
+
if authorize_env?(env, :read_posts)
|
|
63
|
+
conditions = {}
|
|
64
|
+
non_public_conditions = {}
|
|
65
|
+
conditions[:id.gt] = env.params.since_id if env.params.since_id
|
|
66
|
+
conditions[:id.lt] = env.params.before_id if env.params.before_id
|
|
67
|
+
conditions[:published_at.gt] = Time.at(env.params.since_time.to_i) if env.params.since_time
|
|
68
|
+
conditions[:published_at.lt] = Time.at(env.params.before_time.to_i) if env.params.before_time
|
|
69
|
+
conditions[:entity] = env.params.entity if env.params.entity
|
|
70
|
+
if env.params.mentioned_post && env.params.mentioned_entity
|
|
71
|
+
conditions[:mentions] = {
|
|
72
|
+
:mentioned_post_id => env.params.mentioned_post,
|
|
73
|
+
:entity => env.params.mentioned_entity
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
if env.params.post_types
|
|
77
|
+
types = env.params.post_types.split(',').map { |t| TentType.new(URI.unescape(t)) }
|
|
78
|
+
|
|
79
|
+
non_public_conditions[:type_base] = types.select do |type|
|
|
80
|
+
env.current_auth.post_types.include?('all') ||
|
|
81
|
+
env.current_auth.post_types.include?(type.uri)
|
|
82
|
+
end.map(&:base)
|
|
83
|
+
non_public_conditions[:type_base] = nil unless non_public_conditions[:type_base].any?
|
|
84
|
+
|
|
85
|
+
conditions[:type_base] = types.map(&:base)
|
|
86
|
+
|
|
87
|
+
elsif !env.current_auth.post_types.include?('all')
|
|
88
|
+
non_public_conditions[:type_base] = env.current_auth.post_types.map { |t| TentType.new(t).base }
|
|
89
|
+
end
|
|
90
|
+
if env.params.limit
|
|
91
|
+
conditions[:limit] = [env.params.limit.to_i, TentD::API::MAX_PER_PAGE].min
|
|
92
|
+
else
|
|
93
|
+
conditions[:limit] = TentD::API::PER_PAGE
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if env.params.return_count
|
|
97
|
+
conditions[:original] = true
|
|
98
|
+
env.response = Model::Post.all(conditions.merge(non_public_conditions))
|
|
99
|
+
env.response += Model::Post.all(conditions.merge(:public => true))
|
|
100
|
+
env.response = env.response.count
|
|
101
|
+
else
|
|
102
|
+
conditions[:order] = :published_at.desc
|
|
103
|
+
if conditions[:limit] == 0
|
|
104
|
+
env.response = []
|
|
105
|
+
else
|
|
106
|
+
env.response = Model::Post.all(conditions.merge(non_public_conditions))
|
|
107
|
+
env.response += Model::Post.all(conditions.merge(:public => true))
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
env.response = Model::Post.fetch_with_permissions(env.params, env.current_auth)
|
|
112
|
+
end
|
|
113
|
+
env
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
class CreatePost < Middleware
|
|
118
|
+
def action(env)
|
|
119
|
+
authorize_post!(env)
|
|
120
|
+
set_app_details(env.params.data)
|
|
121
|
+
set_publicity(env.params.data)
|
|
122
|
+
parse_times(env.params.data)
|
|
123
|
+
data = env.params[:data].slice(*whitelisted_attributes(env))
|
|
124
|
+
data.public_id = env.params.data.id if env.params.data.id
|
|
125
|
+
post = Model::Post.create(data)
|
|
126
|
+
post.assign_permissions(env.params.data.permissions) if post.original
|
|
127
|
+
env['response'] = post
|
|
128
|
+
env
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def authorize_post!(env)
|
|
134
|
+
post = env.params.data
|
|
135
|
+
if auth_is_publisher?(env.current_auth, post)
|
|
136
|
+
post.following_id = env.current_auth.id if env.current_auth.kind_of?(Model::Following)
|
|
137
|
+
env.authorized_scopes << :write_posts
|
|
138
|
+
elsif anonymous_publisher?(env.current_auth, post) && post != env['tent.entity']
|
|
139
|
+
env.authorized_scopes << :write_posts
|
|
140
|
+
elsif env.authorized_scopes.include?(:import_posts)
|
|
141
|
+
post.entity ||= env['tent.entity']
|
|
142
|
+
post.app ||= env.current_auth.app
|
|
143
|
+
if post.following_id && following = Model::Following.first(:public_id => post.following_id)
|
|
144
|
+
post.following_id = following.id
|
|
145
|
+
end
|
|
146
|
+
elsif env.current_auth.respond_to?(:app)
|
|
147
|
+
post.entity = env['tent.entity']
|
|
148
|
+
post.app = env.current_auth.app
|
|
149
|
+
post.following_id = nil
|
|
150
|
+
end
|
|
151
|
+
post.original = post.entity == env['tent.entity']
|
|
152
|
+
authorize_env!(env, :write_posts)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def whitelisted_attributes(env)
|
|
156
|
+
attrs = Model::Post.write_attributes
|
|
157
|
+
attrs += [:app_id] if env.current_auth.respond_to?(:app)
|
|
158
|
+
attrs += [:received_at] if env.authorized_scopes.include?(:import_posts)
|
|
159
|
+
attrs
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def auth_is_publisher?(auth, post)
|
|
163
|
+
auth.respond_to?(:entity) && auth.entity == post.entity
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def anonymous_publisher?(auth, post)
|
|
167
|
+
!auth && post.entity && !Model::Following.first(:entity => post.entity, :fields => [:id])
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def set_app_details(post)
|
|
171
|
+
if app = post.delete('app')
|
|
172
|
+
post.app_url = app.url
|
|
173
|
+
post.app_name = app.name
|
|
174
|
+
post.app_id = app.id if app.id
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def set_publicity(post)
|
|
179
|
+
post.public = post.permissions.public if post.permissions
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def parse_times(post)
|
|
183
|
+
post.published_at = Time.at(post.published_at.to_i) if post.published_at
|
|
184
|
+
post.received_at = Time.at(post.received_at.to_i) if post.received_at
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def assign_permissions(post, permissions)
|
|
188
|
+
return unless post.original && permissions
|
|
189
|
+
if permissions.groups && permissions.groups.kind_of?(Array)
|
|
190
|
+
permissions.groups.each do |g|
|
|
191
|
+
next unless g.id
|
|
192
|
+
group = Model::Group.first(:public_id => g.id, :fields => [:id])
|
|
193
|
+
post.permissions.create(:group => group) if group
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
if permissions.entities && permissions.entities.kind_of?(Hash)
|
|
198
|
+
permissions.entities.each do |entity,visible|
|
|
199
|
+
next unless visible
|
|
200
|
+
followers = Model::Follower.all(:entity => entity, :fields => [:id])
|
|
201
|
+
followers.each do |follower|
|
|
202
|
+
post.permissions.create(:follower_access => follower)
|
|
203
|
+
end
|
|
204
|
+
followings = Model::Following.all(:entity => entity, :fields => [:id])
|
|
205
|
+
followings.each do |following|
|
|
206
|
+
post.permissions.create(:following => following)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
class Update < Middleware
|
|
214
|
+
def action(env)
|
|
215
|
+
authorize_env!(env, :write_posts)
|
|
216
|
+
if post = TentD::Model::Post.first(:id => env.params.post_id)
|
|
217
|
+
version = post.latest_version(:fields => [:id])
|
|
218
|
+
post.update(env.params.data.slice(:content, :licenses, :mentions, :views))
|
|
219
|
+
|
|
220
|
+
if env.params.attachments.kind_of?(Array)
|
|
221
|
+
Model::PostAttachment.all(:post_id => post.id).update(:post_id => nil, :post_version_id => version.id)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
env.response = post
|
|
225
|
+
end
|
|
226
|
+
env
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
class Destroy < Middleware
|
|
231
|
+
def action(env)
|
|
232
|
+
authorize_env!(env, :write_posts)
|
|
233
|
+
if (post = TentD::Model::Post.first(:id => env.params.post_id)) && post.destroy
|
|
234
|
+
raise Unauthorized unless post.original
|
|
235
|
+
env.response = ''
|
|
236
|
+
env.notify_deleted_post = post
|
|
237
|
+
end
|
|
238
|
+
env
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
class CreateAttachments < Middleware
|
|
243
|
+
def action(env)
|
|
244
|
+
return env unless env.params.attachments.kind_of?(Array) && env.response
|
|
245
|
+
post = env.response
|
|
246
|
+
version = post.latest_version(:fields => [:id])
|
|
247
|
+
env.params.attachments.each do |attachment|
|
|
248
|
+
Model::PostAttachment.create(:post => post,
|
|
249
|
+
:post_version => version,
|
|
250
|
+
:type => attachment.type,
|
|
251
|
+
:category => attachment.name, :name => attachment.filename,
|
|
252
|
+
:data => Base64.encode64(attachment.tempfile.read), :size => attachment.tempfile.size)
|
|
253
|
+
end
|
|
254
|
+
env.response.reload
|
|
255
|
+
env
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
class Notify < Middleware
|
|
260
|
+
def action(env)
|
|
261
|
+
if deleted_post = env.notify_deleted_post
|
|
262
|
+
post = Model::Post.create(
|
|
263
|
+
:type => 'https://tent.io/types/post/delete/v0.1.0',
|
|
264
|
+
:entity => env['tent.entity'],
|
|
265
|
+
:original => true,
|
|
266
|
+
:content => {
|
|
267
|
+
:id => deleted_post.public_id
|
|
268
|
+
}
|
|
269
|
+
)
|
|
270
|
+
Model::Permission.copy(deleted_post, post)
|
|
271
|
+
else
|
|
272
|
+
return env unless (post = env.response) && post.kind_of?(Model::Post)
|
|
273
|
+
end
|
|
274
|
+
Notifications.trigger(:type => post.type.uri, :post_id => post.id)
|
|
275
|
+
env
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
class GetAttachment < Middleware
|
|
280
|
+
def action(env)
|
|
281
|
+
return env unless env.response
|
|
282
|
+
type = env['HTTP_ACCEPT'].split(/;|,/).first if env['HTTP_ACCEPT']
|
|
283
|
+
attachment = env.response.attachments.first(:type => type, :name => env.params.attachment_name, :fields => [:data])
|
|
284
|
+
if attachment
|
|
285
|
+
env.response = Base64.decode64(attachment.data)
|
|
286
|
+
env['response.type'] = type
|
|
287
|
+
else
|
|
288
|
+
env.response = nil
|
|
289
|
+
end
|
|
290
|
+
env
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
class ConfirmFollowing < Middleware
|
|
295
|
+
def action(env)
|
|
296
|
+
if Model::Following.first(:public_id => env.params.following_id)
|
|
297
|
+
[200, { 'Content-Type' => 'text/plain' }, [env.params.challenge]]
|
|
298
|
+
else
|
|
299
|
+
[404, {}, []]
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
class TriggerUpdates < Middleware
|
|
305
|
+
def action(env)
|
|
306
|
+
post = env.response
|
|
307
|
+
if post && post.following && post.following == env.current_auth
|
|
308
|
+
case post.type.base
|
|
309
|
+
when 'https://tent.io/types/post/profile'
|
|
310
|
+
Notifications.update_following_profile(:following_id => post.following.id)
|
|
311
|
+
when 'https://tent.io/types/post/delete'
|
|
312
|
+
if deleted_post = Model::Post.first(:public_id => post.content.id, :following_id => env.current_auth.id)
|
|
313
|
+
deleted_post.destroy
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
env
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
get '/posts/count' do |b|
|
|
322
|
+
b.use GetCount
|
|
323
|
+
b.use GetFeed
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
get '/posts/:post_id' do |b|
|
|
327
|
+
b.use GetActualId
|
|
328
|
+
b.use GetOne
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
get '/posts/:post_id/attachments/:attachment_name' do |b|
|
|
332
|
+
b.use GetActualId
|
|
333
|
+
b.use GetOne
|
|
334
|
+
b.use GetAttachment
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
get '/posts' do |b|
|
|
338
|
+
b.use GetActualId
|
|
339
|
+
b.use GetFeed
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
post '/posts' do |b|
|
|
343
|
+
b.use CreatePost
|
|
344
|
+
b.use CreateAttachments
|
|
345
|
+
b.use Notify
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
post '/notifications/:following_id' do |b|
|
|
349
|
+
b.use CreatePost
|
|
350
|
+
b.use CreateAttachments
|
|
351
|
+
b.use Notify
|
|
352
|
+
b.use TriggerUpdates
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
get '/notifications/:following_id' do |b|
|
|
356
|
+
b.use ConfirmFollowing
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
put '/posts/:post_id' do |b|
|
|
360
|
+
b.use GetActualId
|
|
361
|
+
b.use Update
|
|
362
|
+
b.use CreateAttachments
|
|
363
|
+
b.use Notify
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
delete '/posts/:post_id' do |b|
|
|
367
|
+
b.use GetActualId
|
|
368
|
+
b.use Destroy
|
|
369
|
+
b.use Notify
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|