20210507のMySQLに関する記事は4件です。

Spring Data R2DBCでMySQLのJSONを取り扱う

Spring Data R2DBC + r2dbc-mysqlでJSONを取り扱うときにハマったので備忘録として書き残しておきます。 結論としては、Spring Data R2DBCのCustom Conversionsを利用しました。 なお、本記事ではコードを原則Kotlinで記述しています。 課題 下記のようなSQLで作成されるテーブルをMySQL上に持つとします。 create table article ( id bigint unsigned auto_increment, category_ids json not null, constraint article_pk primary key (id) ); --- data exmaple INSERT INTO article VALUES (null, json_array(1, 2)); このテーブルは「記事」エンティティに関するテーブルで、カテゴリIDのリストを保持したカラムを持ちます。 そして、このテーブルに対応する「記事」エンティティを次のように定義し、ReactiveCrudRipositoryインタフェースを追加します。 Article.kt data class Article( @Id val Id: Long? = null, val categoryIds: List<Int>, ) ArticleRepository.kt interface ArticleRepository : ReactiveCrudRepository<Article, Long> これで、articleRepository.findById(1)のようにしたら問題なくエンティティを取得できそうな気もしますが、下記のようなエラーが発生します。 Failed to convert from type [java.lang.String] to type [java.lang.Integer] for value ‘[1]’; nested exception is java.lang.NumberFormatException: For input string: “[1]” 原因 色々調べてみたところ、r2dbc-mysqlでは現在、JSONタイプをサポートせず、VARCHARとしてパススルーさせているようです。 ドライバ「側」が複数のライブラリ等に対するサポートを行うのは望ましくないからという考えからそのような実装になっているようですね。 JSON mapping is typically an application-level concern (JSON-P, JSON-B) because all transformations and bindings are reflected on application or client-level. Having support directly in the driver comes with several drawbacks as drivers need to either commit to a single library or provide support for multiple implementations (Jackso, GSON, Apache Johnzon). This isn't typically the thing you want to deal with. 該当のIssueでは、Custom Codecsを提供するとのことでしたので、リファレンスをみつつ実装するかーと思ったのですが・・・ 2020/5/7時点の最新版であるR2DBC MySQL 0.8.2.RELEASEにおいてはCustom Codecインタフェースが公開されていない不具合が解消されていないようで、まだこの方法は利用できないみたい。 対策 結局、冒頭で述べた通りですがSpring Data R2DBCのCustom Conversionsという機能を利用することにしました。 まず、こちらを参考にReadingConverterとWritingConverterを実装します。 ReadingConverterでは、取得したRowから該当するカラムのデータを取り出し、エンティティを戻り値としています。 この際に、categoryIdsはJacksonによってList<Int>に変換をしています。 @ReadingConverter class ArticleReadConverter : Converter<Row, Article> { override fun convert(source: Row): Article { val id = source.get("id", Long::class.java) val categoryIdsString = source.get("category_ids", String::class.java) val mapper = ObjectMapper() val categoryIds = mapper.readValue(categoryIdsString, object : TypeReference<List<Int>>() {}) return Article(id = id, code = code!!, name = name!!, categoryIds = categoryIds) } } WritingConverterでは、ArticleをOutboundRowに変換しています。 この際、公式のドキュメントではSettableValue.fromメソッドを利用していますが、現在こちらのメソッドは非推奨になっているのでParameter.from等を使いましょう。 @WritingConverter class ArticleWriteConverter : Converter<Article, OutboundRow> { override fun convert(source: Article): OutboundRow { val row = OutboundRow() with(row) { put("id", Parameter.fromOrEmpty(source.id, Long::class.java)) put("category_ids", Parameter.from(ObjectMapper().writeValueAsString(source.categoryIds))) } return row } } 最後に、AbstractR2dbcConfigurationクラスを継承した設定クラスを実装し、getCustomConvertersメソッドで実装したReadingConverterとWritingConverterを登録します。 @Configuration class R2dbcConfiguration( private val connectionFactory: ConnectionFactory ) : AbstractR2dbcConfiguration() { override fun connectionFactory() = connectionFactory override fun getCustomConverters() = mutableListOf<Any>(ArticleReadConverter(), ArticleWriteConverter()) } これで、「記事」エンティティを取得したり永続化したりできるようになりました! Appendix spring-data-r2dbc - Mapping Spring Data R2DBC - Reference Documentation #17.3. Mapping Configuration 下は、StackOverflowで質問して、結局自己解決したのでセルフ回答したやつ。 最後までお読みいただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Django3のmakemigrationsの小技

概要 今、開発しているdjangoのprojectのmigration時に、models.pyのコートを書いては消し、としているので、よくデータベースを消すはめになります そこで、自分で調べてみて、簡単にmigrationの変更を削除、更新できるようにしたいと考え、忘備録として記していこうと思います。また私の環境で find . -path "*/migrations/*.py" -not -name "__init__.py" -delete find . -path "*/migrations/*.pyc" -delete と書いたら、Djangoを再インストールするハメになったので。このコマンドは使わないようにします。 開発環境 django==3.2.1 python==3.9.1 データベース==MySQL きっかけ 今回、私はaccountsというアプリケーションの中のmodelsに、UserというAbstractBaseUserをオーバーライドさせたmodelsを作成したのですが、primary_keyを設定して、makemigrationsしてしまったので、こんなのがターミナルに毎回出てきてしまいます。 WARNINGS: accounts.User: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'. これを消すために、migrateして、データベースに保存されてしまったprimary_keyの値をなかったことにしたいと思います。 migrateのコマンドまとめ makemigrations python manage.py makemigrations (アプリケーション名)   migrate python manage.py migrate (アプリケーション名) migrationした時のnameを変更 python manage.py makemigrations --name (付けたい名前) migrateの履歴を見る python manage.py showmigrations (アプリケーション名) [ ]=migrateしてない [ X ]=migrate済み migrateの巻き戻し python manage.py migrate (ファイル名) ファイル名というのは0001や0002から始まるファイルを指しています。指定したファイル名の直前まで、巻き戻すことになります。 migrateを全てやり直し python manage.py migrate (アプリケーション名) zero 履歴からも削除 migrationファイルを削除 私の場合 python manage.py showmigrations accounts 結果 accounts [X] 0001_initial []の中のXはmigrateした際に、表示されます。(makemigrations時には空のままです。) migrate時にデータベースの方にmodelが保存されるようです。つまり、このXが消えれば、データベースの変更がなくなるらしい。なので、次のコマンドを使って、migrateをなかったことにしてみます。 python manage.py migrate accounts zero 結果 accounts [ ] 0001_initial となりました。 これを完全に消すためには、アプリケーション内のmigrationsと__pychahe__の中にある0001から始まるファイルを消します。 この後、使っていたデータベースも削除して、makemigrations,migrateを行いましたが、warningは消えませんでした。。。 とりあえず、errorにはなっていないので、このまま進めていこうと思います。 このwarningについて、知見のある方は連絡をしていただけると助かります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【なんだろう...】ソーシャル匿名掲示板を作った件Part.1【ちょっと使ってみてもらっていいですか?】

7秒で読める忙しい人用まとめ ここはすばらしいインターネッツですね 趣味でソーシャル匿名掲示板fora.socialを作りました。 アルファテスト中です。 ログイン不要でコメントができるのでぜひ記念カキコしてみてください。 サーバーよわよわなんで、アクセス集中するとサーバーダウンするかもです。 レスポンシンブ(スマホから見るやつ)は一応対応してますけど、完全対応ではないのでちょっと使いにくいかもです。 また、予告なくサイトを一時閉鎖する可能性があるのでご了承ください。 この記事では、 「なぜ今更匿名掲示板を作ったか」 「実装についてあれこれ」 「セキュリティ対策について」 「機能説明」 「今後について」 などざっくりと説明していきます。 1分で読めるまとめ 初カキコ…ども…ソーシャル匿名掲示板fora.socialを作りました... 俺みたいな、いまさら匿名掲示板作る腐れ野郎、他に、いますかっていねーか、はは 今日のyoutubeの急上昇ランキング マインクラフトやAPEXがどうとか 芸能人が○○してみた とか ま、それが普通ですわな そして俺は配信者勢いランキングを見て、呟くんすわ うんこちゃんと、兎田ぺこらどっちにしようかな え、どっちもやってない?それじゃあ it’a Abema TV.呪術廻戦でも見るか 好きな音楽 YOASOBI 尊敬する人間 にしむらひろゆき氏(愛してるって言うのはNO) なんつってる間に4時っすよ(笑) あ~あ、コロナ早く収まってほしいね、これ ちなみにくだらないノリはまだ続きますよ。 現在fora.socialはアルファテスト中です。 なんで「ソーシャル」なのかと言うと、スレッドがいいね数やコメント数に従ってランキング化されたり、各スレッドの人気コメントがスレッド一覧に表示されたりする部分がソーシャル?ということです。文字だと伝わりにくいですね。 あとは、「匿名掲示板」だけだと新しい響きじゃないし、パンチが弱いと思ったんです。 匿名掲示板の名の通り、ログイン不要でスレ建てやコメントの投稿ができます。 【トップページの画面。デフォルトでは全板総合の毎時ランキングです。】 【各トピックの詳細画面。画面下部のボタンからコメントできます。】 左上のボタンでカテゴリ移動ができます 全板総合、カテゴリ内、板内で、各種ソートが選べます。 板ごとにスレ投稿画面へ遷移できます スレ投稿画面では、「文字(1024文字以内)」「リンク」「画像(5MBまで)」「動画(youtubeとニコニコ動画のURLを貼ってください)」張り付けることができます ログインするとマイページから板の作成ができます いわゆる「ポートフォリオ?」というものではないです。作ってみようと思い立って、勢いで作ってみちゃった系です。 現在ユーザー登録(メアド不要)すると、板が二つまで作れる設定となっておりますので、是非板を作成してみてください!その他ご意見、ご感想お待ちしております。→運営への意見スレ はじめに うそはうそであると見抜ける人でないと(掲示板を使うのは)難しい くぅ~疲れましたw これにて一旦動くものができたみたいです! 実は、某Nchのトラフィックを見たら、じゃあ、自分も作ってみようというのが始まりでした 本当はweb系の技術なんてなかったのですが← やる気を無駄にするわけには行かないので流行りのネタで挑んでみた所存ですw 以下、ぼく達のみんなへのメッセジをどぞ 戦略家ぼく「みんな、見てくれてありがとう!ちょっと1000番煎じ感もあるけど・・・気にしないでね!」 フロントエンドぼく「いやーありがと!jqueryのすばらしさは二十分に伝わったかな?」 バックエンドぼく「見てくれたのは嬉しいけど、可用性がオワってるのが恥ずかしいわね・・・早めにAWSに移行しないときつくない?」 デザイナーぼく「見てくれありがとな!正直、全部Bootstrapだよ!角は丸くしろってジョブズも言ってたよ!」 マーケターぼく「・・・Qiitaに記事書いただけだけど、ありがと」ファサ では、 戦略家ぼく、フロントエンドぼく、バックエンドぼく、デザイナーぼく、マーケターぼく、俺「皆さんありがとうございました!」 終 戦略家ぼく、フロントエンドぼく、バックエンドぼく、デザイナーぼく、マーケターぼく「って、なんで俺くんが!?改めまして、ありがとうございました!」 本当の本当に終わり 閑話休題~名前について ソーシャル匿名掲示板を作りました。その名も、fora.socialです。 ダサいとか言ったら異界送りにします。 キマリ、ユウナ、マモル foraでもfora.socialでもどっちの呼び方でもいいんですが、ドメイン名も含めた方がオシャレじゃないですか。(musical.lyとかyoutu.beみたいなのが頭に残っていたんですよ。ちなみにmusical.lyはtiktokの前身のサイトです。https://www.musical.ly へ飛ぶとtiktokへリダイレクトされると思います。) はい、本当はfora.comとかfora.net、fora.siteというドメインが埋まっていて、有名なドメインが取れなかっただけです。 名前の命名も、匿名掲示板という性質上、本当はもっとアンダーグラウンドっぽい名前にしようかと思ったんです。ですが、アンダーグラウンドの雰囲気にし過ぎてしまうのはリスク高いな~と思い、この名前にしました。(2chも訴訟問題とか散々ありましたね)といっても、「聖なる雰囲気」というか、ちゃんとした雰囲気にし過ぎてしまうのもそれはそれで面白くないですよね。名前変えるかもしれないです。 ちなみにforaという言葉は、古代ローマ都市の公共広場、フォルム(forum)の複数形(fora)です。いろいろな人々が広場に集まって談笑や議論している姿を想像して、まさにfora.socialの行く末になって欲しいと思って命名しました。 ただ、日本的な名前にしたいなーという思いもあります。サイト内でも英語はできるだけ使わないようにしていますので。 「亀山社中」。。とか?意味不明ですかね。「にっぽん昔ばなし」とか、意味不明ですね。 「よもやまばなし」「よもやま」「四方八方」「四方山」「にっぽんよもやまばなし」 よもやまから離れられなくなってきました 「よろず報知」 とか 「瓦版(かわらばん)」 とかも面白いですね。ただオシャレな名前じゃないと若い人集まらなさそうです。 卍インターネッツ ...意味不明ですね うーん。難しいですね。 人々の話のログを整理し、残していくという意味で、「アカーシャクロニクル」とか「アカシックレコード」とか厨二病みたいな名前も考えました。かっこいいですけど宗教的な意味合いに曲解してしまわれそうで微妙ですかね 「人々が集まっていろいろと議論する場所」みたいないい感じの言葉ないですかね。 いい名前があれば募集中です。 個人的にfora.socialという名前は匿名掲示板としてクールすぎる気がしているんですよ なんだかんだバランス取れている気もしますけど ドメインの設定とかもめんどくさいので、とりあえずこの名前にしてます。 これでいいさ。20年…待たせたからな。もう おまえたちの時代だ! 戦略編 戦略家ぼく(数か月前)「匿名掲示板なんて3日でできるだろwよゆーよゆー」 そういえば、2ch(5chという名前になったことすら知りませんでした)やそのまとめサイトをあまり見なくなり、久しく経った気がします。TwitterやInstagram、Facebook、Youtubeといったアメリカ製のサービスが日本でも猛威を振るい、インターネッツ上での盛り上がりをごっそり奪い取っていってしまったのでしょうか。 なんとなく感じる物足りない感 今、何が流行ってるんだ? もっぱらYoutubeでお気に入りYoutuberの動画やライブ配信を見る日々。ニコニコ動画すらも見なくなりました。 何か物足りない。急上昇ランキングは格闘家しかいないし、ゲームはマイクラとAPEXしか世界に存在しないかのようです。 フラッシュ倉庫で赤い部屋やウォーリーを探さないでを見て眠れなくなったり、2chでポケモンのコイルを人気投票1位にしようとお祭りになったり、ニコニコ動画で謎のインディーズゲーム「東方」が盛り上がったり、パソコンに歌を歌わせる「ボーカロイド」が生まれたり.....。懐かしきインターネッツの日々が思い出されます。 何か足りない。困っちまうこれは誰かのせい。あてもなく混乱するエイデイ そんな日々の中で、創設から20年以上もたつ2ch(5ch)は、日本でもいまだにTOP50以内の優秀なトランザクション量を誇るとひょんなことから知りました。20年もたつのに、、、ぐぎぎ、、、、、。 3日間くらい考えて、匿名掲示板というもののインターネッツにおける役割について興味を持ち始めた戦略家ぼくは、匿名掲示板について調査を開始しました。匿名掲示板の定義を厳密に行うのは難しいですが、「インターネットにおける人格」というのは面白い研究分野であることがわかりました。4chan創業者、クリストファーブール氏の発言なども結構興味深かったです。 そして、暇な時間に匿名掲示板に類すると思われるサービスを調査という名のROMを続けていたところ、自分もじゃあ作ってみようかと、思い始めてしまったです.....。 調査結果 以下のような部分をどうするかで、サイトの方向性が決まるような気がします。 コメントのデフォルトの表示順 連番(2ch、Dcard)なのか、人気順か。スレの「流れ」を生み出すには、連番の必要がある。カスタムで新着順に表示できたとしても(youtube,yahooニュース,redditなど)、多くの人はわざわざカスタムで新着順にしてコメント欄を見るというめんどくさいことはしないため、スレの流れは生まれにくい。デフォルトの表示がどうなっているかが重要。 スレッド一覧画面の順 スレッドフロート方式(2ch)、人気順(reddit)、ユーザーへのサジェスト(youtubeのアルゴリズムみたいな)。 コメントの通報と削除、書き込み制限の仕組み コメント/スレッド作成の権限(ログインの要不要)-気軽にコメントできるかどうか。→気軽にコメントできる方が盛り上がりやすいが、削除・通報の仕組みや荒らしスパム対策をしっかり行う必要がある コメントに表示されるID-ユーザー名が表示される(reddit,youtube)のか。その場限りのID(2ch)か、完全に何もないか(爆サイ、がるちゃん) 中央集権かモデレータ主導か タイムスタンプとその表示形式→スレの流れが生まれるかどうかに影響する 以下は、各サービスをあっさ~く使ってみての、完全に個人の主観的感想ですので、実際のコアユーザーの意見と異なると思います。 匿名掲示板に類する有名なサービス 日本 2ch-日本最大規模の匿名掲示板。スレッドに「雰囲気」というか「流れ」がある。 5ch-2chと色々あったらしい。2chのコメントは5chに転送されている。エッティな広告が多い。 ガールズちゃんねる-通称ガルチャン。男がいると「こいつ男だろ」 と言われる。 はてな匿名ダイアリー-通称増田。独自の文化を生成。 したらば-2ch型の掲示板をレンタルできるサービス。中央集権型の2chとは違い、各板に管理人がいる。2chの避難所だったりする。 mixiコミュニティ-いまでも一定のユーザーがいる。この中ではかなりマイルド?なコミュニティ lobi-ゲームコミュニティ。若い人も多く、秩序が保たれている印象。 Yahoo知恵袋-質問形式の知恵共有サービス。ベストアンサーへのインセンティブ設計がよくできてる。 ヤフーニュースのコメント欄-真面目系のコメントが多い。コメントの「流れ」があまりなく、単発のコメントの集合体。 Youtubeのコメント欄-あくまでも動画サイトであり、動画についての話題しかない。コメントの「流れ」があまりなく、単発のコメントの集合体。だが、youtube自体の規模が巨大なため、コメントも多い。有名な動画は日本語と英語が入り混じっている。動画によっては掲示板っぽい使われ方がされている場合もあり。PV数からしたら、視聴者のコメント「率」はかなり低い 爆サイ-ローカルクチコミサイト。ID表示がなく、雰囲気はかなりフリーダム。 各種まとめサイト 匿名掲示板に類する有名なサービス 世界 reddit-おそらく英語圏最大の掲示板サイト。世界TOP10のトラフィックを誇る。掲示板の運営はモデレータによって行われており、サイトの構造はしたらばに近い。karmaというポイントシステムがあり、ポストに対するインセンティブがある。ログイン必須。 digg-ソーシャルニュースサイト。一時はredditと双璧を成したっぽいが、今は掲示板というよりもニュースサイトとしての側面が強い。 4chan-日本のふたば☆ちゃんねるに影響されて作られた英語圏の画像掲示板。登録システムが存在しなく、2chに近い。投稿が一定期間で削除されるという性質上、英語圏で最もアンダーグラウンドな雰囲気漂う匿名掲示板。 PTT (台湾)-台湾の2ch 巴哈姆特:バハムート-台湾のゲームコミュニティ。台湾におけるゲーム、アニメ、漫画の発信地である。 Dcard-台湾の大学生専用のコミュニティサイト。台湾有数のSNSとしても有名。ユーザーが大学生という性質上、この手のサイトにしては珍しくアングラ感はあまりなく、女性ユーザーが多い。メイクや化粧、恋愛相談の話題も多くなされている印象。 Plurk-台湾のサブカル系SNS。肉が歩いている。変なタコみたいなキャラクターが特徴。twitterと共存している?もともとはカナダで開発された? craygslist-アメリカ最大のクラシファイドサイト。シンプルなUIながら、月間10億PV以上を誇る。 slashdots-テクノロジー系の匿名掲示板。日本では「スラド」として知られている。 tumblr twitter 世界と言っておきながら、アメリカと台湾しかないですが。中国にもいろいろあるのでしょうが、あまり調べられませんでした。 いろいろと匿名掲示板に類するサービスを調べていくうちに、思ったこと 日本の匿名掲示板系のサービスは英語圏のものに比べてシステムが古い傾向 台湾と比べても、経済規模は日本の方が上なのに、台湾の方がシステムが優れている印象 しかし、日本の各掲示板にも、一定のユーザーがいて、独自のコミュニティを形成している。=匿名掲示板というコミュニケーションの形は、古今東西問わず一定の需要があるのでは? なぜ英語圏の素晴らしいサイト(主にreddit)を使わず、日本の古いサイトを使い続ける人が多いのか?→言語、文化が違うから。+redditが本気で日本を狙っていないから?日本向けのチューニング(翻訳やマーケティング)が細かいところまで行き届いていなくて、「日本人ですけどredditはん、使わせてもらいまっせ。おおきに~」みたいな微妙な居心地の悪さを感じ、なんというか「おれたちのコミュニティ」みたいなアットホーム感がない。わざわざスレッドにご丁寧に「日本語」みたいなタグがついてても拍子抜けしちゃうでしょ。「ここはおれたちの居場所じゃねえ」って だいたい、サブレってなんですか。鳩のあれですか?板って呼んで、くれないか コメントの匿名性と、サイトの秩序というものに相関がある。コメントの匿名性が高いほど秩序を保つことが難しくなるが、思いもよらない化学反応的な面白さが生み出されることもある。→創発現象 中央集権型のサービスと、モデレータ主導型のサービスがある 匿名掲示板というサービスの性質上、自然と男性の割合が多くなる傾向にある気がする。 日本では匿名掲示板系のサービスは、ユーザーの高齢化が進んでいる印象。若い人は匿名掲示板でやり取りするという概念がそもそも無い。twitter、instagramまたはyoutubeのコメント欄が3大若い人のコメントが多いサイトか。しかしtwitterやinstagramはリアルの友達と繋がっていることもあり、息苦しく感じる人も少なくない→何か言いたいことがあるけど内に秘めたまま言えず、ストレスを抱え込む人がわりと多いのではないか? 匿名ということもあり、トラブルの際どうするかのルール・線引き(権利、責任)をきちんと行う必要がある。 コミュニティが廃れてしまう原因の大きなものとして、荒らしやスパムの存在がでかい 以上を踏まえ、作る方向性 「匿名」掲示板である。→コメント/スレッドの作成や、スレッドやコメントへの「いいね」はログインしなくてもできるように。 サイト全体の構成→「サイト全体」にユーザーが付くのではなく、「板」に対するユーザーがつく(=板ごとに文化が生成される)ように、板へのフォロー機能を実装。(ログインユーザーのみ) 秩序を保てる掲示板システム→スレッド内にはID制度(IPアドレス×スレッド×日づけのため、スレッドが変わればIDが違くなる。)を導入し、自作自演をしにくくした。ユーザー同士のチャットはできず、ユーザーフォローはできないようにすることで、個人VS個人の争いができないようにした。各スレッドやコメントに報告ボタンを付け、ログイン不要で規約違反や不快なコメントの報告ができる。(報告はIPアドレスに紐づいているので連投はできない。)多く報告されたコメントやスレッドは自動的に削除(非表示)される(いいね数が多いものはその分たくさん報告される必要あり)。また、そのコメントやスレッドを投稿したIPアドレスを内部的に管理し、一定期間で多く報告がされたIPアドレスを、板ごとに一定期間書き込み制限とする。(IPアドレス×板×一日くらいの期間で書き込み制限がかかる。) モデレータ主導型サービスにしたい→アカウントを持つユーザーが板とそのルールを作成、スレッドやコメントの削除権限を付与。 若い人でも使えるように→動画やリンクの埋め込みを可能とし、見た目的にもとっつきやすく。また、ランキング機能の充実によりトレンドを知りたい若い人のニーズにもこたえる。さらに、PWAの技術でスマホからでもアプリライクなアクセスを可能に。 インセンティブ→ログインしていると、もらったいいね数が蓄積していく。(redditのkarmaに近い)ただ、板のモデレータのインセンティブ設計が難しい。。。。「自分の考えた板が盛り上がると嬉しい」だけだとインセンティブとして弱いので、考える必要がある。wikipediaみたいに「モデレータコミュニティ」みたいなのを作って発展させていければ。。。? スレッドの整理と蓄積-Googleは「検索」型サービス。目的があって、使うサイト。匿名掲示板は「探索」型。探索型サイトとして最善のやり方で、スレッドやコメント情報の整理と蓄積を目指す→カテゴリによる分類、タグによるメタ情報整理、PV数といいね数による人気度の数値化(←twitterは過去情報は蓄積されないし、整理もされない。最大の違い。twitterはフロー型。foraはストックを目指す)詳しくはフォークソノミーのwikiとか読んでみると結構おもしろいですよ 実装編 ジェバンニが一晩でやってくれました うそです。バックエンドぼくが一生懸命、数か月頑張ってくれました。 まずは技術的な仕様をご紹介します。 ざっくり技術的な仕様 バックエンド python3.7.5 Django2.2.17 (pythonフレームワーク。元はニュース系のサイトを管理するために作られたフレームワークらしい。instagramやpinterestのバックエンドはdjangoらしいと聞いてdjangoを選択。テンプレート(html生成)機能が標準で搭載されているのも良い。) フロントエンド 主にDjango標準のテンプレート機能。モダンなjsフレームワークは使っておりません。reactとかvueの勉強がめんどくさかったわけではないですよ・・・・。 js系 9割jquery、その他lightbox.jsとかlazyload.jsとか便利系ちょこちょこ css系 bootstrap アイコンはfontawesome DBサーバー-MySQL on pythonanywhere django標準のインストールだとsqlite3ってのが入っていると思いますが、さすがにsqlite3で運用は厳しいんじゃないかと。 バックエンドサーバー-pythonanywhere djangoならAWSよりも手軽にデプロイできると思います。手軽さと、趣味で作ってみましたってことでpythonanywhereを選びました。バッチ処理とかも簡単にできます。課金するとストレージ容量だったり、サーバーのスペックをある程度強化できます。ただ、細かいDBの(水平・垂直)分割とかの設定は難しいみたいなので、スケーリングを考えるといずれAWSかその他のホスティングサービスの方がいいと思います。あとはネット上にpythonanywhereの日本語の情報がめちゃ少ないので、pythonanywhereのforumのQ&Aとかは結構見てました。 ストレージ-pythonanywhere ドメイン-お名前.com Djangoあれこれ →気が向いたら書きます。 フルスタックMVCフレームワーク REST化も簡単 テンプレートタグとフィルター バッチ 優秀なORマッパーで、SQL文知らなくても使える セキュアなユーザー管理システム adminサイト標準搭載 豊富なmiddlewareでセキュリティ対策もやりやすい pythonanywhereあれこれ →気が向いたら書きます。 実はAWSのEC2? デプロイが簡単 監理サイトが使いやすい 無料モードもあり お名前コムあれこれ →気が向いたら書きます。 PWA(progressive web application) これは、webサイトをスマホアプリのように振舞わせることができる手法です。 一応iOSの方は、上記サイトにsafariでアクセスしてもらって、「ホーム画面へ追加」を押すと、アプリっぽい動作をするものがスマホで使えます。(スタンドアロンで起動します) manifest.jsonとserviceworkerを設定するのですが、気が向いたら書きます。(わたしも深くは理解してないですが) ページの表示スピードを速くしたい ハイパフォーマンスwebサイトこのあたりを読んだりしましたが、、そこまでチューニングできていないです。 問題はフロントエンドだ cssはインラインに書くな→レンダリングの阻害 jsとcssの場所に注意しろ CDNを使え gzip化しろ facebookとyoutubeに気を付けろ 画像のlazyload stdimageの活用 django DBの仕様ー外部キーにはインデックスがデフォルトで付いている djangoのキャッシングフレームワークは優秀だが、使いどころには気を付けろ セキュリティ対策編 webアプリケーションにはセキュリティ対策が必須です。特に掲示板のようなサービスは、気を付けなければいけないですね セキュリティ対策については、このあたりのサイトを参考にさせていただきました。 IPA 安全なウェブサイトの作り方 現場で使えるdjangoのセキュリティ対策 Djangoで開発する際にセキュリティについて気をつけること GoogleSearchconsoleと、webサイトの脆弱性診断サイトgredでの結果は次のようになりました パスワード系 パスワード不正取得 →ブルートフォースアタック対策を行う必要があります。 ログイン機能には、django-allauthを使っています 詳しくはこの辺りでhttps://qiita.com/jansnap/items/62f0de512bc8771ebd58 パスワードのバリデーションは、settings.pyの以下のコードで行われてます。 AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] ユーザー登録の際には、ある程度のパスワードの複雑さが必要です。推測されやすいパスワードの登録はできません。 ログイン制限 ログイン画面でパスワードを一定回数間違えると、一定時間ログイン制限がかかります。 なんでその部分だけ英語なの?というと、django-alltuhをインストールして、標準のテンプレートを使うとこうなるからですね。日本語にする方法はちょっとよくわかりません。いずれ対応します。 パスワード漏洩 データベースにパスワードが平文、あるいは容易に復号可能な状態で保存されている場合、データベースからデータが流出した場合に、不正アクセスされるので、対策の必要がある。 結論から言うと、foraでパスワードは平文で保存されていません。入力されたパスワードとランダムな文字列を結合し、ハッシュ関数を一定回数繰り返し使ってハッシュ化してからデータベースに保存しているため、複合ができないです。たとえデータベースからパスワードが流出しても元のパスワードを流出データから知ることはできません。 パスワードはこんな感じで保存されています ちょっと詳細 foraで使っているユーザーモデルは、AbstractUserを継承しています。 from django.contrib.auth.models import AbstractUser class CustomUser(AbstractUser): django/contrib/auth/models.pyをみてみるとAbstractUserはAbstractBaseUserを継承していて from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager class AbstractUser(AbstractBaseUser, PermissionsMixin): django/contrib/auth/base_user.pyをみてみると from django.contrib.auth.hashers import ( check_password, is_password_usable, make_password, ) class AbstractBaseUser(models.Model): password = models.CharField(_('password'), max_length=128) # Stores the raw password if set_password() is called so that it can # be passed to password_changed() after the model is saved. _password = None def set_password(self, raw_password): self.password = make_password(raw_password) self._password = raw_password def check_password(self, raw_password): """ Return a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes. """ def setter(raw_password): self.set_password(raw_password) # Password hash upgrades shouldn't be considered password changes. self._password = None self.save(update_fields=["password"]) return check_password(raw_password, self.password, setter) 入力されたパスワードをmake_password関数で何らかの処理をして、結果をpasswordテーブルに保存しているようです 入力された生のパスワードは、self._password = Noneの処理で消されているようですね。 以下の処理は、Djangoはパスワードをどうやって保存しているのかと同じですので、気になる方はこちらをチェックしてみてください。 認証系 cookieとセッション(セッションハイジャック) →セッションハイジャックされないようにする必要がある Djangoでは、セッション管理はCookieベースであり、URLにcookieの埋め込みはしません。また、セッションIDはランダムな文字列として生成されてます。 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', ] また、settings.pyに以下の設定をすることで、cookieにsecure属性を付与しています SESSION_COOKIE_SECURE = True アクセス制御 →非公開情報を第三者に見られないようにする デコレータの活用 from django.contrib.auth.decorators import login_required @login_required def hoge(request): 管理サイトのURLをデフォルトから変える https://www.fora.social/admin/ にアクセスすると debug=Falseにする インジェクション SQLインジェクション DjangoのORマッパーはセキュアなSQLを組み立てるため、Djangoを普通に使っている限り心配する必要はないかと HTTPヘッダインジェクション setting.pyのALLOWED_HOSTSでホワイトリストを設定してます クロスサイト系 XSS(クロスサイトスクリプティング) →画面への出力処理にスクリプトを埋め込める脆弱性がある場合、利用者のブラウザで不正なスクリプトが実行される危険性がある djangoのテンプレートを使っている場合基本的にはオートエスケープ機能が働いていて、XSSの心配はないです。ただし、以下のような記法は注意する必要があります。 {{ hogepiyo | safe }} この「safe」フィルタは、文字列に対して、さらなるエスケープが必要でないことをマークするのに使います。 ユーザーが投稿可能なフィールドの出力に「safe」を付けると、 <script>alert(1)</script> などがエスケープされずにそのままhtmlの要素としてブラウザでレンダリングされるため、悪意あるユーザーにスクリプトを埋め込まれる危険性があります。 foraではいろいろとカスタムテンプレートフィルタを作っていて、板や、コメントやスレッドの出力画面では、{% autoescape off %} や、safeフィルタの使用は避けるよう注意をしています。 import html text = html.escape(text) CSRF(クロスサイトリクエストフォージェリ) MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', ] 上記のミドルウェアを読み込み、POSTのフォーム要素には{% csrf_token %}をつけると、django側でCSRF検証をしてくれます。 一部、ajaxのPOSTを使っていますが、下記のようにcsrf_tokenを生成すれば問題ありません function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); (中略) $.ajax({ type: 'POST', data: { 'data': data, }, beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRFToken', csrftoken); xhr.overrideMimeType('application/json;charset=Shift_JIS'); }, クリックジャッキング →攻撃対象サイトの上にiframeを重ねて、わからないように誘導 XFrameOptionsMiddlewareをいれる。(デフォルトで入ってますけど) MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] その他 ファイルアップロード時に気を付けること POSTフォームはform.is_valid()つかう foraでは、modelsでvalidationを設定 models.py def validate_image(fieldfile_obj): filesize = fieldfile_obj.file.size megabyte_limit = 5.0 if filesize > megabyte_limit*1024*1024: raise ValidationError("アップロード可能なのは、 %sMB以下のファイルです。" % str(megabyte_limit)) image = StdImageField(blank=True,null=True, validators=[validate_image,FileExtensionValidator(['jpg','jpeg','png','gif', ])],variations={ 'thumbnail': (50, 50, True), 'medium': (150, 150), }) https化 pythonanywhereではワンクリックで対応可能(認証局はlets enclyptですけど) Djangoのセキュリティチェック機能 manage.py check --deploy コマンドでセキュリティチェックができます。 いくつかWARNIGSが出ているので対応します。 #security.W004 SECURE_HSTS_SECONDS = 315360000 SECURE_HSTS_INCLUDE_SUBDOMAINS = True #security.W006 SECURE_CONTENT_TYPE_NOSNIFF = True #security.W007 SECURE_BROWSER_XSS_FILTER = True #security.W008 SECURE_SSL_REDIRECT = True #security.W016 CSRF_COOKIE_SECURE = True #security.W019 X_FRAME_OPTIONS = 'DENY' #security.W021 SECURE_HSTS_PRELOAD = True コメントの投稿制限 連投防止 機能編(サイトの仕様) サイトの構造 サイトの構造はこんな感じです。(ER図とか画面遷移図、アーキテクチャの細かい説明はめんどくさいので割愛します....) 権限 板にスレッドの投稿を行うことができ、スレッドにコメントの投稿を行うことができます。 板の作成は誰でもできますが、ログインが必要です。スレッドやコメントの投稿は、ログイン不要です。スレッドやコメントは、内部的にはipアドレスと紐づいていますが、他のユーザーからipアドレスを識別することはできません。 ログイン機能 メール登録→任意です。一応ちゃんと登録できます。メールアドレスを登録しておくと、パスワードを紛失したときにアカウントの復活ができます。support@fora.socialから自動で返信が来ると思います。 板 モデレータが作成→現在ログインすると、2つまで板が作成できます。 モデレータの権限→自分の作った板のスレッドやコメントで、板の雰囲気にそぐわないものがあった場合、モデレータによって削除ができます。 スレッドの削除 コメントの削除 通報 ログインしていなくても、報告ができます。 報告画面 報告後(新規報告の場合) 報告後(すでに報告済みの場合) 多数報告されたスレッドやコメントは、自動的に削除になります。(いいね数が多いスレッドやコメントの場合、削除されるためにはその分たくさんの報告が必要になります。) その他、お問い合わせはこちらまで(対応遅いかもしれません) 書き込み制限 板ごとに設定されます。 書き込んだコメントやスレッドが、一日の中で多く報告されるか、またはモデレータによって削除にされた場合、その板内で一定期間書き込み制限となります。 スレッド投稿しようとするとこんな画面に コメント投稿しようとするとこんな画面に 一日以内に解除されます。 その他、fora全体に書き込み禁止措置をすることもできますが、詳細はまた今度。 30秒以内に連続で同じスレッド内でコメントを投稿しようとすると、以下のようになります スレの終了 コメントが1000を超えると、それ以上書き込めなくなります。 スレの終了については、一定期間書き込みが無かったら強制終了するような機能の実装をするかもしれないですが、まだ検討中です。 コメント コメントにはIDがつきます。このIDは、日×スレッド×ipアドレスに基づいて計算されています。IDからipアドレスの復号はできません。 「名無し」の名前は板ごとに設定されています。 今後について 機能 機能的には、ベースのものは一通り実装済みかなと思っています。 が、板のライフサイクルみたいなところはちゃんとしなきゃなと思ってます。具体的には、合併と分割、板の終了ですね。 「過疎板の合併」みたいなことができればいいなと考えているんですよね。合併のプロセスはなにもアイデアありませんが。 板の分割は、どのスレッドをどのように割り振るか難しいので厳しそうですけど(というか板の分割は、板内で抗争?が発生して自然発生するのが自然なのでしょうね)。 板の終了に関しては、一定期間スレッドが生成されない板に関して、なんらかの処理をしなきゃと思ってます 結局ユーザーに比べて板が多すぎると人が分散しちゃうんで、それは避けたいですね。サイト全体として適切な盛り上がりを維持できる板のライフサイクル設計にしたいですね 板に関しては、板ごとに名前の入力のON/OFFを可能にしたりとか、IDの表示をなくしたりとか、ログインユーザー(承認ユーザー)のみ書き込み可能にしたりとか、ランキングに載せないように(URLを知ってる人しか使えない)ようにしたりとか、いろいろ板ごとになバリエーションが選択できるようにしてもおもしろいかもしれませんね。 板の話題によって、ニーズも変わってきそうですし。 あとはスレッド内のまとめ機能ですね。まだたくさんコメントがあるスレッドが無いので、あまり考えられてないですが。各コメントの「いいね数」をなんらかのアルゴリズムで弄れば簡単にできそうですね。 サーバー移行(スケーリングについて) 本格的に運用するのであれば、pythonanywhereはスケーリングが難しそうなため、AWSへ移行の必要がありそうです。そのさいに、データベースを一旦リセットする可能性があります。 本格的に運用するのかどうかは未定ですけどね。 DBについて やはり本格的に勉強する必要がありそうです。分割とか、シャーディング?レプリケーション?redisとmemcachedってなんぞや?とか、いろいろわからないことがあります。たぶんコメントテーブルとかいいねテーブルはシャーディングみたいのしないといけないんだろうなあ。 https://techplay.jp/column/239 こういうの見ると、foraのサーバー、DBアーキテクチャはほんとへなちょこなんだなと思います SEO対策 →そのうち 各種監視ツール munin(未導入) pingdom(未導入) PagerDuty(未導入) GoogleSearchConsole(導入済み) GoogleAnalytics(導入済み) ここまで読んでいただきありがとうございます。 ここはすばらしいインターネッツですね fora.social
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

2chとredditの中間みたいな匿名掲示板を作ってみた話

5/14更新 ・読みやすいように文章を少し削りました。 ・今はWebSocket&redisでリアルタイムでページを見てる人数(板毎など)を表示したいなーと思ってるのですが、pythonanywhwreだとwebsocketに対応していないので、AWSに移行しようかなと思ってるのですが従量課金なのがちょっとなと思ってます。気軽にWebsocket試せないですかね。→Pusherというサービスを発見しました。 7秒で読める忙しい人用まとめ 趣味でソーシャル匿名掲示板fora.socialを作っています。 アルファテスト中です。 レスポンシンブ(スマホから見るやつ)は一応対応してますけど、完全対応ではないのでちょっと使いにくいかもです。 また、予告なくサイトを一時閉鎖する可能性があるのでご了承ください。 この記事では、 「なぜ今更匿名掲示板を作ったか」 「実装についてあれこれ」 「セキュリティ対策について」 「機能説明」 「今後について」 などざっくりと説明していきます。 1分で読めるまとめ 現在ソーシャル匿名掲示板のfora.socialはアルファテスト中です。 なぜ「ソーシャル」なのかと言うと、スレッドがいいね数やコメント数に従ってランキング化されたり、各スレッドの人気コメントがスレッド一覧に表示されたりする部分がソーシャル?ということです。文字だと伝わりにくいですね。 あとは、「匿名掲示板」だけだと新しい響きじゃないし、パンチが弱いと思ったんです。 匿名掲示板の名の通り、ログイン不要でスレ建てやコメントの投稿ができます。 【トップページの画面。デフォルトでは全板総合の毎時ランキングです。】 【各トピックの詳細画面。画面下部のボタンからコメントできます。】 左上のボタンでカテゴリ移動ができます 全板総合、カテゴリ内、板内で、各種ソートが選べます。 板ごとにスレ投稿画面へ遷移できます スレ投稿画面では、「文字(1024文字以内)」「リンク」「画像(5MBまで)」「動画(youtubeとニコニコ動画のURLを貼ってください)」張り付けることができます ログインするとマイページから板の作成ができます いわゆる「ポートフォリオ?」というものではないです。作ってみようと思い立って、勢いで作ってみちゃった系です。 現在ユーザー登録(メアド不要)すると、板が二つまで作れる設定となっておりますので、是非板を作成してみてください!その他ご意見、ご感想お待ちしております。 戦略編 そういえば、2ch(5chという名前になったことすら知りませんでした)やそのまとめサイトをあまり見なくなり、久しく経った気がします。TwitterやInstagram、Facebook、Youtubeといったアメリカ製のサービスが日本でも猛威を振るい、盛り上がりをごっそり奪い取っていってしまったのでしょうか。 なんとなく感じる物足りない感 今、何が流行ってるんだ? もっぱらYoutubeでお気に入りYoutuberの動画やライブ配信を見る日々。ニコニコ動画すらも見なくなりました。 何か物足りない。急上昇ランキングは格闘家しかいないし、ゲームはマイクラとAPEXしか世界に存在しないかのようです。 フラッシュ倉庫で赤い部屋やウォーリーを探さないでを見て眠れなくなったり、2chでポケモンのコイルを人気投票1位にしようとお祭りになったり、ニコニコ動画で謎のインディーズゲーム「東方」が盛り上がったり、パソコンに歌を歌わせる「ボーカロイド」が生まれたり.....。懐かしき日々が思い出されます。 そんな日々の中で、創設から20年以上もたつ2ch(5ch)は、日本でもいまだにTOP50以内の優秀なトランザクション量を誇るとひょんなことから知りました。 3日間くらい考えて、匿名掲示板というもののネットにおける役割について興味を持ち始め、匿名掲示板について調査を開始しました。匿名掲示板の定義を厳密に行うのは難しいですが、「インターネットにおける人格」というのは面白い研究分野であることがわかりました。4chan創業者、クリストファーブール氏の発言なども結構興味深かったです。 そして、暇な時間に匿名掲示板に類すると思われるサービスを調査という名のROMを続けていたところ、自分もじゃあ作ってみようかと、思い始めてしまったです.....。 調査結果 以下のような部分をどうするかで、サイトの方向性が決まるような気がします。 コメントのデフォルトの表示順 連番(2ch、Dcard)なのか、人気順か。スレの「流れ」を生み出すには、連番の必要がある。カスタムで新着順に表示できたとしても(youtube,yahooニュース,redditなど)、多くの人はわざわざカスタムで新着順にしてコメント欄を見るというめんどくさいことはしないため、スレの流れは生まれにくい。デフォルトの表示がどうなっているかが重要。 スレッド一覧画面の順 スレッドフロート方式(2ch)、人気順(reddit)、ユーザーへのサジェスト(youtubeのアルゴリズムみたいな)。 コメントの通報と削除、書き込み制限の仕組み コメント/スレッド作成の権限(ログインの要不要)-気軽にコメントできるかどうか。→気軽にコメントできる方が盛り上がりやすいが、削除・通報の仕組みや荒らしスパム対策をしっかり行う必要がある コメントに表示されるID-ユーザー名が表示される(reddit,youtube)のか。その場限りのID(2ch)か、完全に何もないか(爆サイ、がるちゃん) 中央集権かモデレータ主導か タイムスタンプとその表示形式→スレの流れが生まれるかどうかに影響する 以下は、各サービスをあっさ~く使ってみての、完全に個人の主観的感想ですので、実際のコアユーザーの意見と異なると思います。 匿名掲示板に類する有名なサービス 日本 2ch-日本最大規模の匿名掲示板。スレッドに「雰囲気」というか「流れ」がある。 5ch-2chと色々あったらしい。2chのコメントは5chに転送されている。 ガールズちゃんねる-通称ガルチャン。 はてな匿名ダイアリー-通称増田。独自の文化を生成。 したらば-2ch型の掲示板をレンタルできるサービス。中央集権型の2chとは違い、各板に管理人がいる。2chの避難所だったりする。 mixiコミュニティ-いまでも一定のユーザーがいる。この中ではかなりマイルド?なコミュニティ lobi-ゲームコミュニティ。若い人も多く、秩序が保たれている印象。 Yahoo知恵袋-質問形式の知恵共有サービス。ベストアンサーへのインセンティブ設計がよくできてる。 ヤフーニュースのコメント欄-真面目系のコメントが多い。コメントの「流れ」があまりなく、単発のコメントの集合体。 Youtubeのコメント欄-あくまでも動画サイトであり、動画についての話題しかない。コメントの「流れ」があまりなく、単発のコメントの集合体。だが、youtube自体の規模が巨大なため、コメントも多い。有名な動画は日本語と英語が入り混じっている。動画によっては掲示板っぽい使われ方がされている場合もあり。PV数からしたら、視聴者のコメント「率」はかなり低い 爆サイ-ローカルクチコミサイト。ID表示がなく、雰囲気はかなりフリーダム。 各種まとめサイト 匿名掲示板に類する有名なサービス 世界 reddit-おそらく英語圏最大の掲示板サイト。世界TOP10のトラフィックを誇る。掲示板の運営はモデレータによって行われており、サイトの構造はしたらばに近い。karmaというポイントシステムがあり、ポストに対するインセンティブがある。ログイン必須。 digg-ソーシャルニュースサイト。一時はredditと双璧を成したっぽいが、今は掲示板というよりもニュースサイトとしての側面が強い。 4chan-日本のふたば☆ちゃんねるに影響されて作られた英語圏の画像掲示板。登録システムが存在しなく、2chに近い。投稿が一定期間で削除されるという性質上、英語圏で最もアンダーグラウンドな雰囲気漂う匿名掲示板。 PTT (台湾)-台湾の2ch 巴哈姆特:バハムート-台湾のゲームコミュニティ。台湾におけるゲーム、アニメ、漫画の発信地である。 Dcard-台湾の大学生専用のコミュニティサイト。台湾有数のSNSとしても有名。ユーザーが大学生という性質上、この手のサイトにしては珍しくアングラ感はあまりなく、女性ユーザーが多い。メイクや化粧、恋愛相談の話題も多くなされている印象。 Plurk-台湾のサブカル系SNS。肉が歩いている。変なタコみたいなキャラクターが特徴。twitterと共存している?もともとはカナダで開発された? craygslist-アメリカ最大のクラシファイドサイト。シンプルなUIながら、月間10億PV以上を誇る。 slashdots-テクノロジー系の匿名掲示板。日本では「スラド」として知られている。 tumblr twitter 世界と言っておきながら、アメリカと台湾しかないですが。中国にもいろいろあるのでしょうが、あまり調べられませんでした。 いろいろと匿名掲示板に類するサービスを調べていくうちに、思ったこと 日本の匿名掲示板系のサービスは英語圏のものに比べてシステムが古い傾向 台湾と比べても、経済規模は日本の方が上なのに、台湾の方がシステムが優れている印象 しかし、日本の各掲示板にも、一定のユーザーがいて、独自のコミュニティを形成している。=匿名掲示板というコミュニケーションの形は、古今東西問わず一定の需要があるのでは? なぜ英語圏の素晴らしいサイト(主にreddit)を使わず、日本の古いサイトを使い続ける人が多いのか?→言語、文化が違うから。+redditが本気で日本を狙っていないから?日本向けのチューニング(翻訳やマーケティング)が細かいところまで行き届いていなくて、「日本人ですけどredditはん、使わせてもらいまっせ。おおきに~」みたいな微妙な居心地の悪さを感じ、なんというか「おれたちのコミュニティ」みたいなアットホーム感がない。わざわざスレッドにご丁寧に「日本語」みたいなタグがついてても拍子抜けしちゃうでしょ。「ここはおれたちの居場所じゃねえ」って だいたい、サブレってなんですか。鳩のあれですか?板って呼んで、くれないか コメントの匿名性と、サイトの秩序というものに相関がある。コメントの匿名性が高いほど秩序を保つことが難しくなるが、思いもよらない化学反応的な面白さが生み出されることもある。→創発現象 中央集権型のサービスと、モデレータ主導型のサービスがある 匿名掲示板というサービスの性質上、自然と男性の割合が多くなる傾向にある気がする。 日本では匿名掲示板系のサービスは、ユーザーの高齢化が進んでいる印象。若い人は匿名掲示板でやり取りするという概念がそもそも無い。twitter、instagramまたはyoutubeのコメント欄が3大若い人のコメントが多いサイトか。しかしtwitterやinstagramはリアルの友達と繋がっていることもあり、息苦しく感じる人も少なくない→何か言いたいことがあるけど内に秘めたまま言えず、ストレスを抱え込む人がわりと多いのではないか? 匿名ということもあり、トラブルの際どうするかのルール・線引き(権利、責任)をきちんと行う必要がある。 コミュニティが廃れてしまう原因の大きなものとして、荒らしやスパムの存在がでかい 以上を踏まえ、作る方向性 「匿名」掲示板である。→コメント/スレッドの作成や、スレッドやコメントへの「いいね」はログインしなくてもできるように。 サイト全体の構成→「サイト全体」にユーザーが付くのではなく、「板」に対するユーザーがつく(=板ごとに文化が生成される)ように、板へのフォロー機能を実装。(ログインユーザーのみ) 秩序を保てる掲示板システム→スレッド内にはID制度(IPアドレス×スレッド×日づけのため、スレッドが変わればIDが違くなる。)を導入し、自作自演をしにくくした。ユーザー同士のチャットはできず、ユーザーフォローはできないようにすることで、個人VS個人の争いができないようにした。各スレッドやコメントに報告ボタンを付け、ログイン不要で規約違反や不快なコメントの報告ができる。(報告はIPアドレスに紐づいているので連投はできない。)多く報告されたコメントやスレッドは自動的に削除(非表示)される(いいね数が多いものはその分たくさん報告される必要あり)。また、そのコメントやスレッドを投稿したIPアドレスを内部的に管理し、一定期間で多く報告がされたIPアドレスを、板ごとに一定期間書き込み制限とする。(IPアドレス×板×一日くらいの期間で書き込み制限がかかる。) モデレータ主導型サービスにしたい→アカウントを持つユーザーが板とそのルールを作成、スレッドやコメントの削除権限を付与。 若い人でも使えるように→動画やリンクの埋め込みを可能とし、見た目的にもとっつきやすく。また、ランキング機能の充実によりトレンドを知りたい若い人のニーズにもこたえる。さらに、PWAの技術でスマホからでもアプリライクなアクセスを可能に。 インセンティブ→ログインしていると、もらったいいね数が蓄積していく。(redditのkarmaに近い)ただ、板のモデレータのインセンティブ設計が難しい。。。。「自分の考えた板が盛り上がると嬉しい」だけだとインセンティブとして弱いので、考える必要がある。wikipediaみたいに「モデレータコミュニティ」みたいなのを作って発展させていければ。。。? スレッドの整理と蓄積-Googleは「検索」型サービス。目的があって、使うサイト。匿名掲示板は「探索」型。探索型サイトとして最善のやり方で、スレッドやコメント情報の整理と蓄積を目指す→カテゴリによる分類、タグによるメタ情報整理、PV数といいね数による人気度の数値化(←twitterは過去情報は蓄積されないし、整理もされない。最大の違い。twitterはフロー型。foraはストックを目指す)詳しくはフォークソノミーのwikiとか読んでみると結構おもしろいですよ 実装編 まずは技術的な仕様をご紹介します。 ざっくり技術的な仕様 バックエンド python3.7.5 Django2.2.17 (pythonフレームワーク。元はニュース系のサイトを管理するために作られたフレームワークらしい。instagramやpinterestのバックエンドはdjangoらしいと聞いてdjangoを選択。テンプレート(html生成)機能が標準で搭載されているのも良い。) フロントエンド 主にDjango標準のテンプレート機能。モダンなjsフレームワークは使っておりません。reactとかvueの勉強がめんどくさかったわけではないですよ・・・・。 js系 9割jquery、その他lightbox.jsとかlazyload.jsとか便利系ちょこちょこ css系 bootstrap アイコンはfontawesome DBサーバー-MySQL on pythonanywhere django標準のインストールだとsqlite3ってのが入っていると思いますが、さすがにsqlite3で運用は厳しいんじゃないかと。 バックエンドサーバー-pythonanywhere djangoならAWSよりも手軽にデプロイできると思います。手軽さと、趣味で作ってみましたってことでpythonanywhereを選びました。バッチ処理とかも簡単にできます。課金するとストレージ容量だったり、サーバーのスペックをある程度強化できます。ただ、細かいDBの(水平・垂直)分割とかの設定は難しいみたいなので、スケーリングを考えるといずれAWSかその他のホスティングサービスの方がいいと思います。あとはネット上にpythonanywhereの日本語の情報がめちゃ少ないので、pythonanywhereのforumのQ&Aとかは結構見てました。 ストレージ-pythonanywhere ドメイン-お名前.com Djangoあれこれ →気が向いたら書きます。 フルスタックMVCフレームワーク REST化も簡単 テンプレートタグとフィルター バッチ 優秀なORマッパーで、SQL文知らなくても使える セキュアなユーザー管理システム adminサイト標準搭載 豊富なmiddlewareでセキュリティ対策もやりやすい pythonanywhereあれこれ →気が向いたら書きます。 実はAWSのEC2? デプロイが簡単 監理サイトが使いやすい 無料モードもあり お名前コムあれこれ →気が向いたら書きます。 PWA(progressive web application) これは、webサイトをスマホアプリのように振舞わせることができる手法です。 一応iOSの方は、上記サイトにsafariでアクセスしてもらって、「ホーム画面へ追加」を押すと、アプリっぽい動作をするものがスマホで使えます。(スタンドアロンで起動します) manifest.jsonとserviceworkerを設定するのですが、気が向いたら書きます。(わたしも深くは理解してないですが) ページの表示スピードを速くしたい ハイパフォーマンスwebサイトこのあたりを読んだりしましたが、、そこまでチューニングできていないです。 問題はフロントエンドだ cssはインラインに書くな→レンダリングの阻害 jsとcssの場所に注意しろ CDNを使え gzip化しろ facebookとyoutubeに気を付けろ 画像のlazyload stdimageの活用 django DBの仕様ー外部キーにはインデックスがデフォルトで付いている djangoのキャッシングフレームワークは優秀だが、使いどころには気を付けろ セキュリティ対策編 webアプリケーションにはセキュリティ対策が必須です。特に掲示板のようなサービスは、気を付けなければいけないですね セキュリティ対策については、このあたりのサイトを参考にさせていただきました。 IPA 安全なウェブサイトの作り方 現場で使えるdjangoのセキュリティ対策 Djangoで開発する際にセキュリティについて気をつけること GoogleSearchconsoleと、webサイトの脆弱性診断サイトgredでの結果は次のようになりました パスワード系 パスワード不正取得 →ブルートフォースアタック対策を行う必要があります。 ログイン機能には、django-allauthを使っています 詳しくはこの辺りでhttps://qiita.com/jansnap/items/62f0de512bc8771ebd58 パスワードのバリデーションは、settings.pyの以下のコードで行われてます。 AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] ユーザー登録の際には、ある程度のパスワードの複雑さが必要です。推測されやすいパスワードの登録はできません。 ログイン制限 ログイン画面でパスワードを一定回数間違えると、一定時間ログイン制限がかかります。 なんでその部分だけ英語なの?というと、django-alltuhをインストールして、標準のテンプレートを使うとこうなるからですね。日本語にする方法はちょっとよくわかりません。いずれ対応します。 パスワード漏洩 データベースにパスワードが平文、あるいは容易に復号可能な状態で保存されている場合、データベースからデータが流出した場合に、不正アクセスされるので、対策の必要がある。 結論から言うと、foraでパスワードは平文で保存されていません。入力されたパスワードとランダムな文字列を結合し、ハッシュ関数を一定回数繰り返し使ってハッシュ化してからデータベースに保存しているため、複合ができないです。たとえデータベースからパスワードが流出しても元のパスワードを流出データから知ることはできません。 パスワードはこんな感じで保存されています ちょっと詳細 foraで使っているユーザーモデルは、AbstractUserを継承しています。 from django.contrib.auth.models import AbstractUser class CustomUser(AbstractUser): django/contrib/auth/models.pyをみてみるとAbstractUserはAbstractBaseUserを継承していて from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager class AbstractUser(AbstractBaseUser, PermissionsMixin): django/contrib/auth/base_user.pyをみてみると from django.contrib.auth.hashers import ( check_password, is_password_usable, make_password, ) class AbstractBaseUser(models.Model): password = models.CharField(_('password'), max_length=128) # Stores the raw password if set_password() is called so that it can # be passed to password_changed() after the model is saved. _password = None def set_password(self, raw_password): self.password = make_password(raw_password) self._password = raw_password def check_password(self, raw_password): """ Return a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes. """ def setter(raw_password): self.set_password(raw_password) # Password hash upgrades shouldn't be considered password changes. self._password = None self.save(update_fields=["password"]) return check_password(raw_password, self.password, setter) 入力されたパスワードをmake_password関数で何らかの処理をして、結果をpasswordテーブルに保存しているようです 入力された生のパスワードは、self._password = Noneの処理で消されているようですね。 以下の処理は、Djangoはパスワードをどうやって保存しているのかと同じですので、気になる方はこちらをチェックしてみてください。 認証系 cookieとセッション(セッションハイジャック) →セッションハイジャックされないようにする必要がある Djangoでは、セッション管理はCookieベースであり、URLにcookieの埋め込みはしません。また、セッションIDはランダムな文字列として生成されてます。 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', ] また、settings.pyに以下の設定をすることで、cookieにsecure属性を付与しています SESSION_COOKIE_SECURE = True アクセス制御 →非公開情報を第三者に見られないようにする デコレータの活用 from django.contrib.auth.decorators import login_required @login_required def hoge(request): 管理サイトのURLをデフォルトから変える https://www.fora.social/admin/ にアクセスすると debug=Falseにする インジェクション SQLインジェクション DjangoのORマッパーはセキュアなSQLを組み立てるため、Djangoを普通に使っている限り心配する必要はないかと HTTPヘッダインジェクション setting.pyのALLOWED_HOSTSでホワイトリストを設定してます クロスサイト系 XSS(クロスサイトスクリプティング) →画面への出力処理にスクリプトを埋め込める脆弱性がある場合、利用者のブラウザで不正なスクリプトが実行される危険性がある djangoのテンプレートを使っている場合基本的にはオートエスケープ機能が働いていて、XSSの心配はないです。ただし、以下のような記法は注意する必要があります。 {{ hogepiyo | safe }} この「safe」フィルタは、文字列に対して、さらなるエスケープが必要でないことをマークするのに使います。 ユーザーが投稿可能なフィールドの出力に「safe」を付けると、 <script>alert(1)</script> などがエスケープされずにそのままhtmlの要素としてブラウザでレンダリングされるため、悪意あるユーザーにスクリプトを埋め込まれる危険性があります。 foraではいろいろとカスタムテンプレートフィルタを作っていて、板や、コメントやスレッドの出力画面では、{% autoescape off %} や、safeフィルタの使用は避けるよう注意をしています。 import html text = html.escape(text) CSRF(クロスサイトリクエストフォージェリ) MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', ] 上記のミドルウェアを読み込み、POSTのフォーム要素には{% csrf_token %}をつけると、django側でCSRF検証をしてくれます。 一部、ajaxのPOSTを使っていますが、下記のようにcsrf_tokenを生成すれば問題ありません function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); (中略) $.ajax({ type: 'POST', data: { 'data': data, }, beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRFToken', csrftoken); xhr.overrideMimeType('application/json;charset=Shift_JIS'); }, クリックジャッキング →攻撃対象サイトの上にiframeを重ねて、わからないように誘導 XFrameOptionsMiddlewareをいれる。(デフォルトで入ってますけど) MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] その他 ファイルアップロード時に気を付けること POSTフォームはform.is_valid()つかう foraでは、modelsでvalidationを設定 models.py def validate_image(fieldfile_obj): filesize = fieldfile_obj.file.size megabyte_limit = 5.0 if filesize > megabyte_limit*1024*1024: raise ValidationError("アップロード可能なのは、 %sMB以下のファイルです。" % str(megabyte_limit)) image = StdImageField(blank=True,null=True, validators=[validate_image,FileExtensionValidator(['jpg','jpeg','png','gif', ])],variations={ 'thumbnail': (50, 50, True), 'medium': (150, 150), }) https化 pythonanywhereではワンクリックで対応可能(認証局はlets enclyptですけど) Djangoのセキュリティチェック機能 manage.py check --deploy コマンドでセキュリティチェックができます。 いくつかWARNIGSが出ているので対応します。 #security.W004 SECURE_HSTS_SECONDS = 315360000 SECURE_HSTS_INCLUDE_SUBDOMAINS = True #security.W006 SECURE_CONTENT_TYPE_NOSNIFF = True #security.W007 SECURE_BROWSER_XSS_FILTER = True #security.W008 SECURE_SSL_REDIRECT = True #security.W016 CSRF_COOKIE_SECURE = True #security.W019 X_FRAME_OPTIONS = 'DENY' #security.W021 SECURE_HSTS_PRELOAD = True コメントの投稿制限 連投防止 機能編(サイトの仕様) サイトの構造 サイトの構造はこんな感じです。(ER図とか画面遷移図、アーキテクチャの細かい説明はめんどくさいので割愛します....) 権限 板にスレッドの投稿を行うことができ、スレッドにコメントの投稿を行うことができます。 板の作成は誰でもできますが、ログインが必要です。スレッドやコメントの投稿は、ログイン不要です。スレッドやコメントは、内部的にはipアドレスと紐づいていますが、他のユーザーからipアドレスを識別することはできません。 ログイン機能 メール登録→任意です。一応ちゃんと登録できます。メールアドレスを登録しておくと、パスワードを紛失したときにアカウントの復活ができます。support@fora.socialから自動で返信が来ると思います。 板 モデレータが作成→現在ログインすると、2つまで板が作成できます。 モデレータの権限→自分の作った板のスレッドやコメントで、板の雰囲気にそぐわないものがあった場合、モデレータによって削除ができます。 スレッドの削除 コメントの削除 通報 ログインしていなくても、報告ができます。 報告画面 報告後(新規報告の場合) 報告後(すでに報告済みの場合) 多数報告されたスレッドやコメントは、自動的に削除になります。(いいね数が多いスレッドやコメントの場合、削除されるためにはその分たくさんの報告が必要になります。) その他、お問い合わせはこちらまで(対応遅いかもしれません) 書き込み制限 板ごとに設定されます。 書き込んだコメントやスレッドが、一日の中で多く報告されるか、またはモデレータによって削除にされた場合、その板内で一定期間書き込み制限となります。 スレッド投稿しようとするとこんな画面に コメント投稿しようとするとこんな画面に 一日以内に解除されます。 その他、fora全体に書き込み禁止措置をすることもできますが、詳細はまた今度。 30秒以内に連続で同じスレッド内でコメントを投稿しようとすると、以下のようになります スレの終了 コメントが1000を超えると、それ以上書き込めなくなります。 また、24時間書き込みが無かった板も、それ以上書き込めなくなります。 コメント コメントにはIDがつきます。このIDは、日×スレッド×ipアドレスに基づいて計算されています。IDからipアドレスの復号はできません。 「名無し」の名前は板ごとに設定されています。 今後について 機能 機能的には、ベースのものは一通り実装済みかなと思っています。 が、板のライフサイクルみたいなところはちゃんとしなきゃなと思ってます。具体的には、合併と分割、板の終了ですね。 「過疎板の合併」みたいなことができればいいなと考えているんですよね。合併のプロセスはなにもアイデアありませんが。 板の分割は、どのスレッドをどのように割り振るか難しいので厳しそうですけど(というか板の分割は、板内で抗争?が発生して自然発生するのが自然なのでしょうね)。 板の終了に関しては、一定期間スレッドが生成されない板に関して、なんらかの処理をしなきゃと思ってます 結局ユーザーに比べて板が多すぎると人が分散しちゃうんで、それは避けたいですね。サイト全体として適切な盛り上がりを維持できる板のライフサイクル設計にしたいですね 板に関しては、板ごとに名前の入力のON/OFFを可能にしたりとか、IDの表示をなくしたりとか、ログインユーザー(承認ユーザー)のみ書き込み可能にしたりとか、ランキングに載せないように(URLを知ってる人しか使えない)ようにしたりとか、いろいろ板ごとになバリエーションが選択できるようにしてもおもしろいかもしれませんね。 板の話題によって、ニーズも変わってきそうですし。 あとはスレッド内のまとめ機能ですね。まだたくさんコメントがあるスレッドが無いので、あまり考えられてないですが。各コメントの「いいね数」をなんらかのアルゴリズムで弄れば簡単にできそうですね。 サーバー移行(スケーリングについて) 本格的に運用するのであれば、pythonanywhereはスケーリングが難しそうなため、AWSへ移行の必要がありそうです。そのさいに、データベースを一旦リセットする可能性があります。 本格的に運用するのかどうかは未定ですけどね。 DBについて やはり本格的に勉強する必要がありそうです。分割とか、シャーディング?レプリケーション?redisとmemcachedってなんぞや?とか、いろいろわからないことがあります。たぶんコメントテーブルとかいいねテーブルはシャーディングみたいのしないといけないんだろうなあ。 https://techplay.jp/column/239 こういうの見ると、foraのサーバー、DBアーキテクチャはほんとへなちょこなんだなと思います SEO対策 →そのうち 各種監視ツール munin(未導入) pingdom(未導入) PagerDuty(未導入) GoogleSearchConsole(導入済み) GoogleAnalytics(導入済み) ここまで読んでいただきありがとうございます。 fora.social
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む