マストドドン探索日記

1 Star2 Stars3 Stars4 Stars5 Stars (まだ評価されていません)
Loading...

はじめに

リポジトリはfriends.nicoを使ってます。

docker-composeによるコンテナの起ち上げとユーザー認証まで

$ # イメージのビルド
$ docker-compose build
$ # データベースの初期化とアセットのプリコンパイル
$ docker-compose run --rm web rails db:migrate
$ docker-compose run --rm web rails assets:precompile
$ # コンテナのアップ
$ docker-compose up

ブラウザからアカウントを新規登録する。
Ctrl+Cでコンテナをdownさせる。

$ # アカウントの認証
$ docker-compose run --rm web rails mastodon:confirm_email USER_EMAIL=登録したメールアドレス
$ # アカウントに管理者権限を付与
$ docker-compose run --rm web rails mastodon:make_admin USERNAME=登録したユーザー名

docker-compose up後にブラウザからログインする。ログインできたら開発可能。
通知欄には「まだ通知がありません。他の人とふれ合って会話を始めましょう。」とあるが、ひとり旅に会話相手はいないので「無茶言うなよ」とツッコみたくなる。

Rails

マストドドン Rails

React

マストドドン React

探索

最近話題のマストドンを探索してみることにした。

マストドンのバージョン

マストドンのバージョンの確認方法はTODOとしておこう。

webコンテナにアタッチ

アップしたwebコンテナにアタッチして「動いているマストドン」の中へ侵入してみることに。入ると私は/mastodonへと誘導された。おそらくDockerfileのWORKDIRの仕業だろう。

$ docker-compose exec web /bin/sh
/mastodon # 

rake routes

マストドンはRailsで作られている。とにもかくにも私はrake routesを唱えてみることにした。

                                             Prefix Verb     URI Pattern                                                    Controller#Action
sidekiq          /sidekiq                                                       Sidekiq::Web
pghero          /pghero                                                        PgHero::Engine
GET      /oauth/authorize/:code(.:format)                               oauth/authorizations#show
oauth_authorization GET      /oauth/authorize(.:format)                                     oauth/authorizations#new
DELETE   /oauth/authorize(.:format)                                     oauth/authorizations#destroy
POST     /oauth/authorize(.:format)                                     oauth/authorizations#create
oauth_token POST     /oauth/token(.:format)                                         doorkeeper/tokens#create
oauth_revoke POST     /oauth/revoke(.:format)                                        doorkeeper/tokens#revoke
oauth_applications GET      /oauth/applications(.:format)                                  doorkeeper/applications#index
POST     /oauth/applications(.:format)                                  doorkeeper/applications#create
new_oauth_application GET      /oauth/applications/new(.:format)                              doorkeeper/applications#new
edit_oauth_application GET      /oauth/applications/:id/edit(.:format)                         doorkeeper/applications#edit
oauth_application GET      /oauth/applications/:id(.:format)                              doorkeeper/applications#show
PATCH    /oauth/applications/:id(.:format)                              doorkeeper/applications#update
PUT      /oauth/applications/:id(.:format)                              doorkeeper/applications#update
DELETE   /oauth/applications/:id(.:format)                              doorkeeper/applications#destroy
oauth_authorized_applications GET      /oauth/authorized_applications(.:format)                       oauth/authorized_applications#index
oauth_authorized_application DELETE   /oauth/authorized_applications/:id(.:format)                   oauth/authorized_applications#destroy
oauth_token_info GET      /oauth/token/info(.:format)                                    doorkeeper/token_info#show
host_meta GET      /.well-known/host-meta(.:format)                               well_known/host_meta#show {:format=>"xml"}
webfinger GET      /.well-known/webfinger(.:format)                               well_known/webfinger#show
manifest GET      /manifest(.:format)                                            manifests#show {:format=>"json"}
user_niconico_omniauth_authorize GET|POST /auth/auth/niconico(.:format)                                  auth/omniauth_callbacks#passthru
user_niconico_omniauth_callback GET|POST /auth/auth/niconico/callback(.:format)                         auth/omniauth_callbacks#niconico
new_user_password GET      /auth/password/new(.:format)                                   auth/passwords#new
edit_user_password GET      /auth/password/edit(.:format)                                  auth/passwords#edit
user_password PATCH    /auth/password(.:format)                                       auth/passwords#update
PUT      /auth/password(.:format)                                       auth/passwords#update
POST     /auth/password(.:format)                                       auth/passwords#create
cancel_user_registration GET      /auth/cancel(.:format)                                         auth/registrations#cancel
new_user_registration GET      /auth/sign_up(.:format)                                        auth/registrations#new
edit_user_registration GET      /auth/edit(.:format)                                           auth/registrations#edit
user_registration PATCH    /auth(.:format)                                                auth/registrations#update
PUT      /auth(.:format)                                                auth/registrations#update
DELETE   /auth(.:format)                                                auth/registrations#destroy
POST     /auth(.:format)                                                auth/registrations#create
new_user_confirmation GET      /auth/confirmation/new(.:format)                               auth/confirmations#new
user_confirmation GET      /auth/confirmation(.:format)                                   auth/confirmations#show
POST     /auth/confirmation(.:format)                                   auth/confirmations#create
new_user_session GET      /auth/sign_in(.:format)                                        auth/sessions#new
user_session POST     /auth/sign_in(.:format)                                        auth/sessions#create
destroy_user_session DELETE   /auth/sign_out(.:format)                                       auth/sessions#destroy
GET      /users/:username(.:format)                                     redirect(301, /@%{username}) {:format=>:html}
embed_account_stream_entry GET      /users/:account_username/updates/:id/embed(.:format)           stream_entries#embed
account_stream_entry GET      /users/:account_username/updates/:id(.:format)                 stream_entries#show
account_remote_follow GET      /users/:account_username/remote_follow(.:format)               remote_follow#new
POST     /users/:account_username/remote_follow(.:format)               remote_follow#create
account_followers GET      /users/:account_username/followers(.:format)                   follower_accounts#index
account_following_index GET      /users/:account_username/following(.:format)                   following_accounts#index
account_follow POST     /users/:account_username/follow(.:format)                      account_follow#create
account_unfollow POST     /users/:account_username/unfollow(.:format)                    account_unfollow#create
account GET      /users/:username(.:format)                                     accounts#show
short_account GET      /@:username(.:format)                                          accounts#show
short_account_status GET      /@:account_username/:id(.:format)                              statuses#show
settings_profile GET      /settings/profile(.:format)                                    settings/profiles#show
PATCH    /settings/profile(.:format)                                    settings/profiles#update
PUT      /settings/profile(.:format)                                    settings/profiles#update
settings_preferences GET      /settings/preferences(.:format)                                settings/preferences#show
PATCH    /settings/preferences(.:format)                                settings/preferences#update
PUT      /settings/preferences(.:format)                                settings/preferences#update
settings_import GET      /settings/import(.:format)                                     settings/imports#show
POST     /settings/import(.:format)                                     settings/imports#create
settings_export GET      /settings/export(.:format)                                     settings/exports#show
settings_exports_follows GET      /settings/exports/follows(.:format)                            settings/exports/following_accounts#index {:format=>:csv}
settings_exports_blocks GET      /settings/exports/blocks(.:format)                             settings/exports/blocked_accounts#index {:format=>:csv}
settings_exports_mutes GET      /settings/exports/mutes(.:format)                              settings/exports/muted_accounts#index {:format=>:csv}
settings_two_factor_authentication GET      /settings/two_factor_authentication(.:format)                  settings/two_factor_authentications#show
DELETE   /settings/two_factor_authentication(.:format)                  settings/two_factor_authentications#destroy
POST     /settings/two_factor_authentication(.:format)                  settings/two_factor_authentications#create
settings_two_factor_authentication_recovery_codes POST     /settings/two_factor_authentication/recovery_codes(.:format)   settings/two_factor_authentication/recovery_codes#create
new_settings_two_factor_authentication_confirmation GET      /settings/two_factor_authentication/confirmation/new(.:format) settings/two_factor_authentication/confirmations#new
settings_two_factor_authentication_confirmation POST     /settings/two_factor_authentication/confirmation(.:format)     settings/two_factor_authentication/confirmations#create
settings_follower_domains GET      /settings/follower_domains(.:format)                           settings/follower_domains#show
PATCH    /settings/follower_domains(.:format)                           settings/follower_domains#update
PUT      /settings/follower_domains(.:format)                           settings/follower_domains#update
settings_delete GET      /settings/delete(.:format)                                     settings/deletes#show
DELETE   /settings/delete(.:format)                                     settings/deletes#destroy
settings_oauth GET      /settings/oauth(.:format)                                      settings/oauths#show
PATCH    /settings/oauth(.:format)                                      settings/oauths#update
PUT      /settings/oauth(.:format)                                      settings/oauths#update
DELETE   /settings/oauth(.:format)                                      settings/oauths#destroy
medium GET      /media/:id(.:format)                                           media#show
tag GET      /tags/:id(.:format)                                            tags#show
authorize_follow GET      /authorize_follow(.:format)                                    authorize_follows#show
POST     /authorize_follow(.:format)                                    authorize_follows#create
admin_subscriptions GET      /admin/subscriptions(.:format)                                 admin/subscriptions#index
admin_domain_blocks GET      /admin/domain_blocks(.:format)                                 admin/domain_blocks#index
POST     /admin/domain_blocks(.:format)                                 admin/domain_blocks#create
new_admin_domain_block GET      /admin/domain_blocks/new(.:format)                             admin/domain_blocks#new
admin_domain_block GET      /admin/domain_blocks/:id(.:format)                             admin/domain_blocks#show
DELETE   /admin/domain_blocks/:id(.:format)                             admin/domain_blocks#destroy
edit_admin_settings GET      /admin/settings/edit(.:format)                                 admin/settings#edit
admin_settings PATCH    /admin/settings(.:format)                                      admin/settings#update
PUT      /admin/settings(.:format)                                      admin/settings#update
admin_instances GET      /admin/instances(.:format)                                     admin/instances#index
admin_report_reported_status PATCH    /admin/reports/:report_id/reported_statuses/:id(.:format)      admin/reported_statuses#update
PUT      /admin/reports/:report_id/reported_statuses/:id(.:format)      admin/reported_statuses#update
DELETE   /admin/reports/:report_id/reported_statuses/:id(.:format)      admin/reported_statuses#destroy
admin_reports GET      /admin/reports(.:format)                                       admin/reports#index
admin_report GET      /admin/reports/:id(.:format)                                   admin/reports#show
PATCH    /admin/reports/:id(.:format)                                   admin/reports#update
PUT      /admin/reports/:id(.:format)                                   admin/reports#update
subscribe_admin_account POST     /admin/accounts/:id/subscribe(.:format)                        admin/accounts#subscribe
unsubscribe_admin_account POST     /admin/accounts/:id/unsubscribe(.:format)                      admin/accounts#unsubscribe
redownload_admin_account POST     /admin/accounts/:id/redownload(.:format)                       admin/accounts#redownload
admin_account_reset POST     /admin/accounts/:account_id/reset(.:format)                    admin/resets#create
admin_account_silence DELETE   /admin/accounts/:account_id/silence(.:format)                  admin/silences#destroy
POST     /admin/accounts/:account_id/silence(.:format)                  admin/silences#create
admin_account_suspension DELETE   /admin/accounts/:account_id/suspension(.:format)               admin/suspensions#destroy
POST     /admin/accounts/:account_id/suspension(.:format)               admin/suspensions#create
admin_account_confirmation POST     /admin/accounts/:account_id/confirmation(.:format)             admin/confirmations#create
admin_accounts GET      /admin/accounts(.:format)                                      admin/accounts#index
admin_account GET      /admin/accounts/:id(.:format)                                  admin/accounts#show
admin_user_two_factor_authentication DELETE   /admin/users/:user_id/two_factor_authentication(.:format)      admin/two_factor_authentications#destroy
admin GET      /admin(.:format)                                               redirect(302, /admin/settings/edit)
api_subscription GET      /api/subscriptions/:id(.:format)                               api/subscriptions#show
api POST     /api/subscriptions/:id(.:format)                               api/subscriptions#update
api_push POST     /api/push(.:format)                                            api/push#update
api_salmon POST     /api/salmon/:id(.:format)                                      api/salmon#update
api_oembed GET      /api/oembed(.:format)                                          api/oembed#show
api_activitypub_outbox GET      /api/activitypub/users/:id/outbox(.:format)                    api/activitypub/outbox#show
api_activitypub_status GET      /api/activitypub/statuses/:id(.:format)                        api/activitypub/activities#show_status
api_activitypub_note GET      /api/activitypub/notes/:id(.:format)                           api/activitypub/notes#show
api_v1_status_reblogged_by_index GET      /api/v1/statuses/:status_id/reblogged_by(.:format)             api/v1/statuses/reblogged_by_accounts#index
api_v1_status_favourited_by_index GET      /api/v1/statuses/:status_id/favourited_by(.:format)            api/v1/statuses/favourited_by_accounts#index
api_v1_status_reblog POST     /api/v1/statuses/:status_id/reblog(.:format)                   api/v1/statuses/reblogs#create
api_v1_status_unreblog POST     /api/v1/statuses/:status_id/unreblog(.:format)                 api/v1/statuses/reblogs#destroy
api_v1_status_favourite POST     /api/v1/statuses/:status_id/favourite(.:format)                api/v1/statuses/favourites#create
api_v1_status_unfavourite POST     /api/v1/statuses/:status_id/unfavourite(.:format)              api/v1/statuses/favourites#destroy
api_v1_status_mute POST     /api/v1/statuses/:status_id/mute(.:format)                     api/v1/statuses/mutes#create
api_v1_status_unmute POST     /api/v1/statuses/:status_id/unmute(.:format)                   api/v1/statuses/mutes#destroy
context_api_v1_status GET      /api/v1/statuses/:id/context(.:format)                         api/v1/statuses#context
card_api_v1_status GET      /api/v1/statuses/:id/card(.:format)                            api/v1/statuses#card
api_v1_statuses POST     /api/v1/statuses(.:format)                                     api/v1/statuses#create
api_v1_status GET      /api/v1/statuses/:id(.:format)                                 api/v1/statuses#show
DELETE   /api/v1/statuses/:id(.:format)                                 api/v1/statuses#destroy
api_v1_timelines_home GET      /api/v1/timelines/home(.:format)                               api/v1/timelines/home#show
api_v1_timelines_public GET      /api/v1/timelines/public(.:format)                             api/v1/timelines/public#show
api_v1_timelines_tag GET      /api/v1/timelines/tag/:id(.:format)                            api/v1/timelines/tag#show
api_v1_streaming_index GET      /api/v1/streaming(.:format)                                    api/v1/streaming#index
api_v1_search GET      /api/v1/search(.:format)                                       api/v1/search#index
api_v1_follows POST     /api/v1/follows(.:format)                                      api/v1/follows#create
api_v1_media POST     /api/v1/media(.:format)                                        api/v1/media#create
api_v1_apps POST     /api/v1/apps(.:format)                                         api/v1/apps#create
api_v1_blocks GET      /api/v1/blocks(.:format)                                       api/v1/blocks#index
api_v1_mutes GET      /api/v1/mutes(.:format)                                        api/v1/mutes#index
api_v1_favourites GET      /api/v1/favourites(.:format)                                   api/v1/favourites#index
api_v1_reports GET      /api/v1/reports(.:format)                                      api/v1/reports#index
POST     /api/v1/reports(.:format)                                      api/v1/reports#create
api_v1_instance GET      /api/v1/instance(.:format)                                     api/v1/instances#show
api_v1_domain_blocks GET      /api/v1/domain_blocks(.:format)                                api/v1/domain_blocks#show
DELETE   /api/v1/domain_blocks(.:format)                                api/v1/domain_blocks#destroy
POST     /api/v1/domain_blocks(.:format)                                api/v1/domain_blocks#create
authorize_api_v1_follow_request POST     /api/v1/follow_requests/:id/authorize(.:format)                api/v1/follow_requests#authorize
reject_api_v1_follow_request POST     /api/v1/follow_requests/:id/reject(.:format)                   api/v1/follow_requests#reject
api_v1_follow_requests GET      /api/v1/follow_requests(.:format)                              api/v1/follow_requests#index
clear_api_v1_notifications POST     /api/v1/notifications/clear(.:format)                          api/v1/notifications#clear
dismiss_api_v1_notifications POST     /api/v1/notifications/dismiss(.:format)                        api/v1/notifications#dismiss
api_v1_notifications GET      /api/v1/notifications(.:format)                                api/v1/notifications#index
api_v1_notification GET      /api/v1/notifications/:id(.:format)                            api/v1/notifications#show
api_v1_accounts_verify_credentials GET      /api/v1/accounts/verify_credentials(.:format)                  api/v1/accounts/credentials#show
api_v1_accounts_update_credentials PATCH    /api/v1/accounts/update_credentials(.:format)                  api/v1/accounts/credentials#update
api_v1_accounts_search GET      /api/v1/accounts/search(.:format)                              api/v1/accounts/search#show
api_v1_accounts_relationships GET      /api/v1/accounts/relationships(.:format)                       api/v1/accounts/relationships#index
api_v1_account_statuses GET      /api/v1/accounts/:account_id/statuses(.:format)                api/v1/accounts/statuses#index
api_v1_account_followers GET      /api/v1/accounts/:account_id/followers(.:format)               api/v1/accounts/follower_accounts#index
api_v1_account_following_index GET      /api/v1/accounts/:account_id/following(.:format)               api/v1/accounts/following_accounts#index
follow_api_v1_account POST     /api/v1/accounts/:id/follow(.:format)                          api/v1/accounts#follow
unfollow_api_v1_account POST     /api/v1/accounts/:id/unfollow(.:format)                        api/v1/accounts#unfollow
block_api_v1_account POST     /api/v1/accounts/:id/block(.:format)                           api/v1/accounts#block
unblock_api_v1_account POST     /api/v1/accounts/:id/unblock(.:format)                         api/v1/accounts#unblock
mute_api_v1_account POST     /api/v1/accounts/:id/mute(.:format)                            api/v1/accounts#mute
unmute_api_v1_account POST     /api/v1/accounts/:id/unmute(.:format)                          api/v1/accounts#unmute
api_v1_account GET      /api/v1/accounts/:id(.:format)                                 api/v1/accounts#show
api_web_settings PATCH    /api/web/settings(.:format)                                    api/web/settings#update
PUT      /api/web/settings(.:format)                                    api/web/settings#update
web GET      /web(/*any)(.:format)                                          home#index
about GET      /about(.:format)                                               about#show
about_more GET      /about/more(.:format)                                          about#more
terms GET      /terms(.:format)                                               about#terms
root GET      /                                                              home#index
/*unmatched_route                                              application#raise_not_found
Routes for PgHero::Engine:
index_usage GET  (/:database)/index_usage(.:format)               pg_hero/home#index_usage
space GET  (/:database)/space(.:format)                     pg_hero/home#space
live_queries GET  (/:database)/live_queries(.:format)              pg_hero/home#live_queries
queries GET  (/:database)/queries(.:format)                   pg_hero/home#queries
system GET  (/:database)/system(.:format)                    pg_hero/home#system
cpu_usage GET  (/:database)/cpu_usage(.:format)                 pg_hero/home#cpu_usage
connection_stats GET  (/:database)/connection_stats(.:format)          pg_hero/home#connection_stats
replication_lag_stats GET  (/:database)/replication_lag_stats(.:format)     pg_hero/home#replication_lag_stats
load_stats GET  (/:database)/load_stats(.:format)                pg_hero/home#load_stats
explain GET  (/:database)/explain(.:format)                   pg_hero/home#explain
tune GET  (/:database)/tune(.:format)                      pg_hero/home#tune
connections GET  (/:database)/connections(.:format)               pg_hero/home#connections
maintenance GET  (/:database)/maintenance(.:format)               pg_hero/home#maintenance
kill POST (/:database)/kill(.:format)                      pg_hero/home#kill
kill_long_running_queries POST (/:database)/kill_long_running_queries(.:format) pg_hero/home#kill_long_running_queries
kill_all POST (/:database)/kill_all(.:format)                  pg_hero/home#kill_all
enable_query_stats POST (/:database)/enable_query_stats(.:format)        pg_hero/home#enable_query_stats
POST (/:database)/explain(.:format)                   pg_hero/home#explain
reset_query_stats POST (/:database)/reset_query_stats(.:format)         pg_hero/home#reset_query_stats
system_stats GET  (/:database)/system_stats(.:format)              redirect(301, system)
query_stats GET  (/:database)/query_stats(.:format)               redirect(301, queries)
indexes GET  (/:database)/indexes(.:format)                   redirect(301, index_usage)
root GET  /(:database)(.:format)                           pg_hero/home#index

かなり長いルーティング情報が出力された。私はリダイレクトを忘れたことを後悔し、/tmpの巻き物に出力を保存後、ダンジョン外へとコピーした。

home#index

出力されたルーティングを見ると、roothome#indexになっている。マストドンはhomeコントローラのindexアクションから始まるようだ。
ビューのファイルはerbではなくhamlが使われている。Haml?私のスキルシートには見当たらないので解読はあきらめた。

Hamlの書き方

/about

ダンジョンからいったん出た私は、/aboutのメッセージに注目してみることにした。
/aboutのメッセージはconfig/locales/ja.ymlに日本語が書かれている。
config/locales/ja.ymlには他にもメッセージが定義されているが、たぶんRailsの国際化のためのファイル?

ためしにホスト側で「Mastodon は自由でオープンソースなソーシャルネットワークです。」を「Mastodon はふええええええええ!自由でオープンソースなソーシャルネットワークです。」に変更してみる。
その後、

$ docker-compose build
$ docker-compose up

を行なうと変更が反映されているのがわかった。
ダンジョン内からviを使って編集を試みたが、日本語は豆腐になっていた。

main.js

マストドンの特徴とも言うべきReactの部分を探索してみることに。
app/javascript/mastodon以下のmain.jsmain関数がReactのエントリーポイント?console.logとreturnでmain関数の途中から抜けたら画面が構築されなくなった。たぶんエントリーポイントだろう。

mastodon/containers/mastodon

main関数内。

const Mastodon = require('mastodon/containers/mastodon').default;
...
ReactDOM.render(<Mastodon {...props} />, mountNode);

に注目すると、mastodon/containers/mastodonrequireして<Mastodon />タグを描画している。このrequire先はapp/javascript/mastodon/containers/mastodon.jsだろう(多分)。
Reactの仕様がよくわかっていないのでパスがどう解決されるかは推測。推測って大事。

app/javascript/mastodon/containers/mastodon.js

まがまがしいimport文が並ぶ。ここはエントリーポイントから呼び出されているマストドンのオリジナリティなコンテナーの中だ。まがまがしいといえば

export default class Mastodon extends React.PureComponent {

私はこの宣言に遭遇したときC++をすこし思い出した。よくよく見れば理解できる構文だが、パッと見はまがまがしく感じた。おそるおそるまたいで関数群をくぐり抜けて行くと・・・

  render () {                                                                        
const { locale } = this.props;                                                   
return (                                                                         
<IntlProvider locale={locale} messages={messages}>                             
<Provider store={store}>                                                     
<BrowserRouter basename='/web'>                                            
<ScrollContext>                                                          
<Route path='/' component={UI} />                                      
</ScrollContext>                                                         
</BrowserRouter>                                                           
</Provider>                                                                  
</IntlProvider>                                                                
);                                                                               
}                                                                                  

renderという描画を担当する関数らしきものに遭遇した。メインルーチンからはこのコンテナだけが描画されているので、このコンテナが描画するものがマストドンの全てということになりそうだが・・・私は実験してみることにした。

しかし実験は失敗した。いくつか描画するタグを消してみたが、エラーになってしまった。予想では部分的に描画されないUIが表示されると思ったのだが。予想は外れたらしい。

import文の

import Route from 'react-router-dom/Route';
import ScrollContext from 'react-router-scroll/lib/ScrollBehaviorContext';
import UI from '../features/ui';

に注目するとRouteScrollContextはReactの部品のようだ。UIはオリジナルだろうか。私は現在のMastodonコンテナーからUIコンテナーへと進んでみることにした。

参照

https://ai-create.net/magazine/2017/04/15/mastodon%E3%82%92docker%E3%81%A7%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%81%AB%E6%A7%8B%E7%AF%89%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F%EF%BC%81-%E3%81%AE%E3%81%A7%E3%80%81%E3%81%9D%E3%81%AE%E6%96%B9/


1 Star2 Stars3 Stars4 Stars5 Stars (まだ評価されていません)
Loading...
      この投稿は審査処理中  | 元のサイトへ