@@ -21,3 +21,4 @@ public/assets | |||||
.env | .env | ||||
.env.* | .env.* | ||||
node_modules/ | node_modules/ | ||||
neo4j/ |
@@ -39,6 +39,7 @@ gem 'will_paginate' | |||||
gem 'rack-attack' | gem 'rack-attack' | ||||
gem 'sidekiq' | gem 'sidekiq' | ||||
gem 'ledermann-rails-settings' | gem 'ledermann-rails-settings' | ||||
gem 'neography' | |||||
gem 'react-rails' | gem 'react-rails' | ||||
gem 'browserify-rails' | gem 'browserify-rails' | ||||
@@ -97,6 +97,7 @@ GEM | |||||
dotenv (= 2.1.1) | dotenv (= 2.1.1) | ||||
railties (>= 4.0, < 5.1) | railties (>= 4.0, < 5.1) | ||||
erubis (2.7.0) | erubis (2.7.0) | ||||
excon (0.53.0) | |||||
execjs (2.7.0) | execjs (2.7.0) | ||||
fabrication (2.15.2) | fabrication (2.15.2) | ||||
fast_blank (1.0.0) | fast_blank (1.0.0) | ||||
@@ -165,13 +166,21 @@ GEM | |||||
mime-types-data (3.2016.0521) | mime-types-data (3.2016.0521) | ||||
mimemagic (0.3.0) | mimemagic (0.3.0) | ||||
mini_portile2 (2.1.0) | mini_portile2 (2.1.0) | ||||
minitest (5.9.0) | |||||
minitest (5.9.1) | |||||
multi_json (1.12.1) | multi_json (1.12.1) | ||||
neography (1.8.0) | |||||
excon (>= 0.33.0) | |||||
json (>= 1.7.7) | |||||
multi_json (>= 1.3.2) | |||||
os (>= 0.9.6) | |||||
rake (>= 0.8.7) | |||||
rubyzip (>= 1.0.0) | |||||
nio4r (1.2.1) | nio4r (1.2.1) | ||||
nokogiri (1.6.8.1) | nokogiri (1.6.8.1) | ||||
mini_portile2 (~> 2.1.0) | mini_portile2 (~> 2.1.0) | ||||
oj (2.17.3) | oj (2.17.3) | ||||
orm_adapter (0.5.0) | orm_adapter (0.5.0) | ||||
os (0.9.6) | |||||
ostatus2 (1.0.1) | ostatus2 (1.0.1) | ||||
addressable (~> 2.4) | addressable (~> 2.4) | ||||
http (~> 2.0) | http (~> 2.0) | ||||
@@ -236,7 +245,7 @@ GEM | |||||
rake (>= 0.8.7) | rake (>= 0.8.7) | ||||
thor (>= 0.18.1, < 2.0) | thor (>= 0.18.1, < 2.0) | ||||
rainbow (2.1.0) | rainbow (2.1.0) | ||||
rake (11.2.2) | |||||
rake (11.3.0) | |||||
rdoc (4.2.2) | rdoc (4.2.2) | ||||
json (~> 1.4) | json (~> 1.4) | ||||
react-rails (1.8.2) | react-rails (1.8.2) | ||||
@@ -281,6 +290,7 @@ GEM | |||||
ruby-progressbar (~> 1.7) | ruby-progressbar (~> 1.7) | ||||
unicode-display_width (~> 1.0, >= 1.0.1) | unicode-display_width (~> 1.0, >= 1.0.1) | ||||
ruby-progressbar (1.8.1) | ruby-progressbar (1.8.1) | ||||
rubyzip (1.2.0) | |||||
safe_yaml (1.0.4) | safe_yaml (1.0.4) | ||||
sass (3.4.22) | sass (3.4.22) | ||||
sass-rails (5.0.6) | sass-rails (5.0.6) | ||||
@@ -370,6 +380,7 @@ DEPENDENCIES | |||||
letter_opener | letter_opener | ||||
link_header | link_header | ||||
lograge | lograge | ||||
neography | |||||
nokogiri | nokogiri | ||||
oj | oj | ||||
ostatus2 | ostatus2 | ||||
@@ -0,0 +1,15 @@ | |||||
import api from '../api'; | |||||
export const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST'; | |||||
export const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS'; | |||||
export const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL'; | |||||
export function fetchSuggestions() { | |||||
return (dispatch, getState) => { | |||||
api(getState).get('/api/v1/accounts/suggestions').then(response => { | |||||
console.log(response.data); | |||||
}).catch(error => { | |||||
console.error(error); | |||||
}); | |||||
}; | |||||
}; |
@@ -1,6 +1,6 @@ | |||||
class Api::V1::AccountsController < ApiController | class Api::V1::AccountsController < ApiController | ||||
before_action :doorkeeper_authorize! | before_action :doorkeeper_authorize! | ||||
before_action :set_account, except: :verify_credentials | |||||
before_action :set_account, except: [:verify_credentials, :suggestions] | |||||
respond_to :json | respond_to :json | ||||
def show | def show | ||||
@@ -19,6 +19,10 @@ class Api::V1::AccountsController < ApiController | |||||
@followers = @account.followers | @followers = @account.followers | ||||
end | end | ||||
def suggestions | |||||
@accounts = FollowSuggestion.get(current_user.account_id) | |||||
end | |||||
def statuses | def statuses | ||||
@statuses = @account.statuses.with_includes.with_counters.paginate_by_max_id(20, params[:max_id], params[:since_id]).to_a | @statuses = @account.statuses.with_includes.with_counters.paginate_by_max_id(20, params[:max_id], params[:since_id]).to_a | ||||
end | end | ||||
@@ -22,4 +22,28 @@ class Follow < ApplicationRecord | |||||
def title | def title | ||||
destroyed? ? "#{account.acct} is no longer following #{target_account.acct}" : "#{account.acct} started following #{target_account.acct}" | destroyed? ? "#{account.acct} is no longer following #{target_account.acct}" : "#{account.acct} started following #{target_account.acct}" | ||||
end | end | ||||
after_create :add_to_graph | |||||
after_destroy :remove_from_graph | |||||
private | |||||
def add_to_graph | |||||
neo = Neography::Rest.new | |||||
a = neo.create_unique_node('account_index', 'Account', account_id.to_s, account_id: account_id) | |||||
b = neo.create_unique_node('account_index', 'Account', target_account_id.to_s, account_id: target_account_id) | |||||
neo.create_unique_relationship('follow_index', 'Follow', id.to_s, 'follows', a, b) | |||||
rescue Neography::NeographyError => e | |||||
Rails.logger.error e | |||||
end | |||||
def remove_from_graph | |||||
neo = Neography::Rest.new | |||||
rel = neo.get_relationship_index('follow_index', 'Follow', id.to_s) | |||||
neo.delete_relationship(rel) | |||||
rescue Neography::NeographyError => e | |||||
Rails.logger.error e | |||||
end | |||||
end | end |
@@ -0,0 +1,7 @@ | |||||
class FollowSuggestion | |||||
def self.get(for_account_id) | |||||
neo = Neography::Rest.new | |||||
account_ids = neo.execute_query('START a=node:account_index(Account={id}) MATCH (a)-[:follows]->(b)-[:follows]->(c) WHERE a <> c AND NOT (a)-[:follows]->(c) RETURN DISTINCT c.account_id', id: for_account_id) | |||||
Account.where(id: account_ids['data'].first) unless account_ids.empty? | |||||
end | |||||
end |
@@ -0,0 +1,2 @@ | |||||
collection @accounts | |||||
extends('api/v1/accounts/show') |
@@ -22,8 +22,8 @@ module Mastodon | |||||
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] | ||||
# config.i18n.default_locale = :de | # config.i18n.default_locale = :de | ||||
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb') | |||||
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')] | |||||
# config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb') | |||||
# config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')] | |||||
config.active_job.queue_adapter = :sidekiq | config.active_job.queue_adapter = :sidekiq | ||||
@@ -0,0 +1,5 @@ | |||||
Neography.configure do |config| | |||||
config.protocol = "http" | |||||
config.server = ENV['NEO4J_HOST'] || 'localhost' | |||||
config.port = ENV['NEO4J_PORT'] || 7474 | |||||
end |
@@ -73,6 +73,7 @@ Rails.application.routes.draw do | |||||
collection do | collection do | ||||
get :relationships | get :relationships | ||||
get :verify_credentials | get :verify_credentials | ||||
get :suggestions | |||||
end | end | ||||
member do | member do | ||||