Open komagata opened 8 months ago
@komagata お疲れ様です。 実装の方針について確認させていただきたいです🙏
luckrya/markdown-it-link-to-card
リンクカードの実装に使うこちらのnpmなんですが、使われている言語がTypeScript
なためそのまま使えなさそうです。
下記のAPI(JavaScript
)の方で実装するという形で良かったのでしょうか?
不安に思ったので確認させていただきたいです🙇♂️
import MarkdownIt from "markdown-it";
import { linkToCardPlugin } from "@luckrya/markdown-it-link-to-card";
import type { LinkToCardPluginOptions } from "@luckrya/markdown-it-link-to-card";
const md = MarkdownIt({ html: true }).use<LinkToCardPluginOptions>(
linkToCardPlugin,
{
// options
size: "small",
}
);
const rendered = md.render(`
# Home
...
### Reference
- [github](https://github.com)
- [bing](https://cn.bing.com/)
- [知乎 - 发现页](https://www.zhihu.com/explore)
- [markdown-it-link-to-card](https://github.com/luckrya/markdown-it-link-to-card)
<br />
- [github](@:https://github.com)
- [bing](@:https://cn.bing.com)
- [知乎 - 发现页](@:https://www.zhihu.com/explore)
- [markdown-it-link-to-card](@:https://github.com/luckrya/markdown-it-link-to-card)
`);
import { generateCard } from "@luckrya/markdown-it-link-to-card";
generateCard("https://github.com", {
linkTitle: "Github Home",
showTitle: true,
size: "small",
}).then(({ dom }) => {
// card dom fragment
console.log(dom);
});
@mousu-a 後からこのIssueを追った人にもわかるようにGitHub外でのやりとり(ミーティングやDiscordや口頭など)で決まったことがあればこちらにメモとして残すようにしてみてください〜。
後からこのIssueを追った人にもわかるようにGitHub外でのやりとり(ミーティングやDiscordや口頭など)で決まったことがあればこちらにメモとして残すようにしてみてください〜。
ありがとうございます🙏
以下ミーティングで決まった方針です!ログとして残します。
リンクカードの実装方針に不安があり、komagataさんに相談させていただいた。
上にあるように、リンクカードの実装使うnpmがTypeScriptで書かれていたため、Usageとして例に上がっているこちらのコードではなく、 https://github.com/luckrya/markdown-it-link-to-card?tab=readme-ov-file#usage
APIとして提示されているこちらのコードを使って実装するものだと思い込んでいました。 https://github.com/luckrya/markdown-it-link-to-card?tab=readme-ov-file#api
そちらをミーティングにてkomagataさんに相談させていただいたところ、「なにか他に別の手段がありそう、探してみてほしい。というよりこの場合のAPIというのが(何に使うのか?)よくわからない」とのこと。 それとチーム開発メンバーのAntiSatoriさんから「TypeScriptは実行時にJavaScriptにコンパイルされるため、TypeScriptで書かれたコードでも問題はなさそう」とのご指摘をいただいたので、 現在は「TypeScriptで書いた(Usageにあるような)リンクカード関連のコードをJavaScriptのファイルでimportして使う」形で実装を進めています。
こちらご紹介いただいたnpmがTypeScriptなのですが、komagataさんより「TypeScriptを使ってしまうと、RailsエンジニアコースでもTypeScriptのカリキュラムを作る必要があり、あえて導入しないようにしているので、TypeScriptはbootcampに導入しない方向で進めていただければありがたいです〜。」とのことです。
TypeScriptを使わずにnpmを使って導入するという方向で進めていきます🙏
@komagata
お疲れ様です! リンクカードの実装、ようやく方向性が見えてきたのですが、npmの問題点が見つかりました。それについての対処法について、お考えをお聞きしたいです。
XHR
を使っています)JSと外部サイトとのリクエストの間にRailsのコントローラーを挟むことでCORSによるブロックが発生しないようにする想定です。(レスポンスヘッダーにAccess-Control-Allow-Origin
の設定を組み込めるためCORSによるブロックが発生しなくなります)
http://localhost:3000/api/linkcard/index?url=
をつけて、リクエストをRailsのコントローラーに送るようにする。http://localhost:3000/api/linkcard/index?url=リンク
# コード例
# JS → Railsコントローラー → 外部サイトの順にリクエスト
class API::LinkcardController < ApplicationController
def index
uri = URI.parse(params['url'])
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
headers = { 'Content-Type' => 'application/json' } response = http.get(uri.path, headers)
render json: response.body, status: :ok end end
ザックリこんな方針で進めています。 このまま進めて良いものかと思い質問させていただきました。
気になるところなどあればお考えをお聞きしたいです! 大丈夫そうであればこのまま進めたいと思います!
こちらの日報でこれらの問題について、メンターのyuuuさんにお話を伺っていました。 共有させていただきます🙏
@mousu-a まず、この npmを使う人ほぼ全員がCROSの問題にあたるとおもいます。 そこをみんながどうしてるのかを調べるのがよさそうです。
@komagata
一通り調べたつもりですが、確実にYESと言えるほど理解度が高くないのでもう少し調べてみます🙏
@komagata
お疲れ様です。一通り調べましたがやはりサーバーサイドを間に通してリクエストをするのが良さそうだと思うのですがいかがでしょうか。以下詳細になります。
調べましたが、やはりこのnpmでCORS関連の記述は出てこないようです。
そもそもこのnpmのドキュメントが本家のREADME以外に全くないという感じです。(READMEにも書いておらず、使っている人も見つかりませんでした😓)
コードを見てもそれらしき記述は見つかりませんでした。
どうやらnpmのコードはクライアントサイドの実装しかないようです。
それにリクエストにXHR
を使っているので、クライアントサイドの方で(CORS対策について)出来ることはないという認識です。(cookieを必要としない限り)
こんなサイトがありましたが、この中では予め用意したデータを使ってリンクカードを生成していたので参考にならずじまいでした。。(外部サイトにリクエストを送っていないのでそもそも前提条件が違いました)
主にこの二つがありそうです。 クライアントサイドで完結させる手段としては、間にプロキシサーバーを用意し、そこにリクエストを送りプロキシサーバーから外部サイトへリクエストを送るようにすることでCORSを避ける、という手段もあるようです。 サーバーサイドに関しては上のコメントで述べている通りです。
ただ結局は、サーバーサイドかサーバーサイドの代わりになるものを用意してそれを間に挟もう、という試みなのは変わらなさそうです。
この場合Railsのコントローラーを間に挟むのは下策でしょうか?ご意見を伺いたいです🙇♂️
@mousu-a まずパラメーターにURLを渡してどのページでも表示するというのはセキュリティ上危険なのでできないです。
それだったらリンクカードをrailsで表示するページをつくって、別のページからはIFRAMEで表示する方がいいとおもいます。 ただそれだとnpmを使う意味(便利さ)がないので、ほかのnpmを探してみるのはいかがでしょうか。
@komagata
質問が2つあります🙏ご意見を伺いたいです🙇♂️
まずパラメーターにURLを渡してどのページでも表示するというのはセキュリティ上危険なのでできないです。
ほかのnpmを探してみるのはいかがでしょうか。
これらの理由から、必ずブラウザ用にコンパイルされたJSから外部サイトへリクエストを送る必要がある(そしてその場合CORSが発生する)、という認識です。 なので、ブラウザ用のJS→サーバー(Railsなど)→外部サイト の流れでリクエストを送ることは必須になるものと思っているのですがいかがでしょうか?
Zenn
のリンクカードなどでは外部サービス(Cloud Functions)を使って外部サイトへのアクセスをしているみたいです。
Zennのカードリンクのレスポンスが遅い件
Qiita
やX
は見つかりませんでした。。
認識が間違っているところなどあればご指摘いただけますと幸いです🙇♂️
@mousu-a
リクエストを受け取ったRailsのコントローラーで、「リンクかどうか?」を正規表現でチェックする、危ない記号である「<>, ”’, :;」これらをサニタイズする、などをしてもダメなのでしょうか?
はい。 同じサイト内だったらいいですが、外部のサイトをそのまま出すのは相当な理由がない限りNGです。
CORSの問題はnpmを変えてどうにかなるものでなく、ブラウザのJSから外部サイトへリクエストを送る時点で必ず発生してしまいます。 Node.jsで外部サイトへリクエストを送ればCORSは発生しませんが、それですとbootcampの既存のMarkdownItPlugin(ブラウザ用のJS)との互換性が保てません。。(webpackerによりブラウザ用にJSファイルがコンパイルされているため、ブラウザ用のJSファイルからNode.jsで書かれたJSファイルをimportすることができません)
同じような実装の似た他のnpmを使ってほしいと思っているわけではないです。
QiitaやXは見つかりませんでした。。
なるほどです。
Railsのコントローラーを挟む場合の実装方法は一例考えがありますが、勉強になるとおもうので @mousu-a さんもサイトにリンクカード的なものを追加したい場合、どのような実装がいいのか考えてみてください。
@mousu-a すみません、ちょっとややこしいですよね。 仕様を作ってみたのでこちらで進めていただければありがたいです。仕様に不明点などありましたらお気軽におっしゃっていただければと思います。
リンクカード · fjordllc/bootcamp Wiki
@machida リンクカードのHTMLや表示について問題点あればおっしゃっていただければとおもいます~。
@komagata うおお〜😭 すみません、お忙しい中ありがとうございます!
仕様に不明点などありましたらお気軽におっしゃっていただければと思います。
ありがとうございます!わからないところあれば質問させていただきます🙇♂️
@komagata
お疲れ様です。すみません、早速ですが質問が2つあります。 リンクカード実装の方向性について確認させていただきたいです🙏
リンクカードの実装は、テキスト中に[TITLE](URL)
があれば該当箇所をリンクカードのHTMLに置換する、という形で宜しいのでしょうか?
適用前:
テキストテキスト
[TITLE](URL)
テキストテキスト
適用後:
<p>テキストテキスト</p>
<div class="link-card">
<div class="link-card__title">
<a href="URL" target="_blank">TITLE</a>
</div>
<div class="link-card__description">DESCRIPTION</div>
<div class="link-card__favicon"><img src="FAVICON" /></div>
<div class="link-card__site-title">
<a href="SITE URL" target="_blank">SITE TITLE</a>
</div>
<div class="link-card__image"><img src="IMAGE" /></div>
</div>
<p>テキストテキスト</p>
リンクカードwikiにて、
markdownでの記法はFBCで使っているmarkdown-itのプラグインとして実装する。
とありますが、md.use(Plugin)
のような形で使えるよう実装してほしい、ということでしょうか?
それともマークダウンを適用する流れで一緒に適用出来るように実装してほしい、ということでしょうか?
例:
# markdown-initializer.js
# md.use(Plugin)の場合
…
md.use(MarkdownItEmoji)
# マークダウンを適用する流れで一緒に適用する場合(例として既存の実装をピックアップしました🙏)
…
MarkdownItTaskListsInitializer.initialize()
よろしくお願いいたします🙇♂️
@mousu-a
質問1
NOです。
下記の「リンクカード記法」の部分を参照してください。
質問2
markdown-itのプラグインとは何なのか、markdown-itの公式サイトなどを参考に調べてみてください。
markdown-it
を勉強していけば理解できると思う@komagata お疲れ様です! すみません、実装の仕方について確認させていただきたいです。 リンクカードの実装についてですが、このような認識で合っていますでしょうか?
// markdown-initializer.js
import MarkdownItLinkcard from 'markdown-it-linkcard'
~
md.use(MarkdownItLinkcard)
~
// markdown-it-linkcard.js
export default (md) => {
~リンクカードの実装~
}
// markdown-initializer.js
import MarkDownItContainerDetails from 'markdown-it-container-details'
~
md.use(MarkDownItContainerDetails)
~
// markdown-it-container-details.js
export default (md) => {
~プラグインの実装~
}
@mousu-a linkはcontainer(block要素)じゃなくてinline要素なので違うと思います。
import MarkdownItLinkcard from 'markdown-it-linkcard'
これは下記がいいとおもいます。
import MarkdownItLinkCard from 'markdown-it-link-card'
別の点で、これも、
import MarkdownItContainerDetails from 'markdown-it-container-details'
こちらの間違いかなとおもいます。
@komagata 質問させていただきたいです。 リンクカードの生成条件についての確認になります。
①1行に@[TITLE](URL)
のみ存在する場合(記法の前後に文字が存在しない)
aaa
@[TITLE](URL)
aaa
②@[TITLE](URL)
の前後に文字がある場合(文の中に記法が存在する場合)
aaa @[TITLE](URL) aaa
リンクカード生成にあたり、この2つが状況として考えられると思います。
自分の認識としては、 ①の場合のみリンクカードを生成する。 ②の場合は"表示できない場合"として扱い、普通のリンクとして表示する。 という風に考えているのですがいかがでしょうか。
komagataさんのご意見を伺いたいです🙇♂️
ZENNでは1行にURLのみ存在する場合(①の場合)のみリンクカードを生成するという形になっています。
①1行に@[title](url)
のみ存在する場合
②@[title](url)
の前後に文字がある場合
どちらの場合でもリンクカードを作成するように実装する
上記のいずれの状況もユーザーにとってハイコンテキストなため、ユーザーからは見えないのが望ましい。
:::details ネタバレ注意! @TITLE :::
マークダウン記法がネストしている状態でもリンクカードは生成する。
@komagata
お疲れ様です。 質問があります!2つあります。 よろしくお願いします🙏
メタデータAPIのこちらのレスポンスですが、これらのkey
はogpのproperty値を参考にしているのでしょうか?
{
site_title: "Moeny Forward Developers Blog",
site_url: "https://xxx.com",
favicon: "https://xxx.com/favicon.ico",
url: "https://xxx.com/xxxx",
title: "ChatGPTのAPIがオープンになったので(略)",
description: "xxxxxxx",
image: "https://xxx.com/xxx.png"
}
OGP
<meta property="og:url" content=" ページの URL" />
<meta property="og:type" content=" ページの種類" />
<meta property="og:title" content=" ページの タイトル" />
<meta property="og:description" content=" ページの説明文" />
<meta property="og:site_name" content="サイト名" />
<meta property="og:image" content=" サムネイル画像の URL" />
もしそうであれば、差し支えなければsite_title
をsite_name
に変更させていただきたいのですがどうでしょうか。
というのも、レスポンスのkeyとogpのproperty値がほとんど一致しているためそれに即した実装を考えているのですがsite_title
だけ違うためちょっと引っかかってしまうな、というところで質問いたしました。
まだ未完成ではありますが、このような実装を考えています。
# リクエスト
uri = Addressable::URI.parse(url).normalize
res = Net::HTTP.get_response(uri)
# nokogiriでdoc化
doc = Nokogiri::HTML(res.body)
# OGPのmetatagを抽出
og_meta_tags = doc.css('meta[property^="og:"]')
# レスポンスを生成
js_response = { site_title: '', site_url: '', favicon: '', url: '', title: '', description: '', image: '' }
og_meta_tags.each do |meta|
js_response.each do |key, _value|
# レスポンスのkeyと、metadataのpropertyが一致したら contentの値をvalueにセット
js_response[key] = meta['content'] if meta['property'] == "og:#{key}"
end
end
@mousu-a 厳密な仕様って感じではないので、やりやすいように変えてしまって大丈夫です〜
TweetのURLだけ特別仕様として実装する
@machida お疲れ様です!先日のMTGでお話していたTweetのリンクカード(埋め込みTweet)のデザインについてお聞きしたいです。 お手すきの際にご確認をお願いいたします🙇♂️
現状Tweetのリンクカードだけ、このようにTwitterのoEmbedAPIを使用しデザインが特別仕様となっています。 Tweetのリンクカードも他のリンクカードとデザインを統一する形にした方がいいでしょうか?
oEmbedAPIを通してTweetURLにリクエストを送ることで、このようなデータを取得することができます。
レスポンスのボディ
{"url":"https:\/\/twitter.com\/mousuFBC\/status\/1799277839138926685","author_name":"mousu","author_url":"https:\/\/twitter.com\/mousuFBC","html":"\u003Cblockquote class=\"twitter-tweet\"\u003E\u003Cp lang=\"ja\" dir=\"ltr\"\u003EGem ViewComponentを勉強中!\u003Cbr\u003Eまだフワフワした全体像が掴めただけで何もわかっていない\uD83D\uDE05\u003Cbr\u003E焦らずやっていくぞ\uD83D\uDCAA\u003C\/p\u003E— mousu (@mousuFBC) \u003Ca href=\"https:\/\/twitter.com\/mousuFBC\/status\/1799277839138926685?ref_src=twsrc%5Etfw\"\u003EJune 8, 2024\u003C\/a\u003E\u003C\/blockquote\u003E\n\u003Cscript async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"\u003E\u003C\/script\u003E\n\n","width":550,"height":null,"type":"rich","cache_age":"3153600000","provider_name":"Twitter","provider_url":"https:\/\/twitter.com","version":"1.0"}
レスポンスのボディからHTML部分だけを抽出したもの
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Gem ViewComponentを勉強中!<br>まだフワフワした全体像が掴めただけで何もわかっていない😅<br>焦らずやっていくぞ💪</p>— mousu (@mousuFBC) <a href="https://twitter.com/mousuFBC/status/1799277839138926685?ref_src=twsrc%5Etfw">June 8, 2024</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
↑のHTML中のscriptタグを実行することで、blockquoteタグがiframeタグに置き代わり、埋め込みツイートのデザインが適用されています。
Tweetのリンクカードも他のリンクカードとデザインを統一する場合、リンクカードのmetadataが必要となります。
{
site_name: "Moeny Forward Developers Blog",
site_url: "https://xxx.com",
favicon: "https://xxx.com/favicon.ico",
url: "https://xxx.com/xxxx",
title: "ChatGPTのAPIがオープンになったので(略)",
description: "xxxxxxx",
image: "https://xxx.com/xxx.png"
}
前述したAPIを介したリクエストでは取得出来るmetadataが少なく、リンクカードとして成立させにくい、という問題があります。
具体的にはfavicon
, site_name
, title
, image
が足りていません。ただ他のもので代替することはできそうです。
favicon
はhttps://publish.twitter.com/oembed?url=https://twitter.com
に追加でリクエストすれば取って来れそうです。site_name
は#{author_name}さんのTweet
のようにすれば良さそうです。title
はdescriptionと同じで良さそうです。image
(Tweetしたユーザーのicon)はまだ取れる方法を見つけられていません😢ZENNではoEmbedAPIを使いTweetのリンクカード(埋め込みTweet)を特別仕様としています。
以上のことを踏まえて、Tweetのリンクカード(埋め込みTweet)も他のリンクカードとデザインを統一させるかどうかをお聞きしたいです🙏
@mousu-a
Twitterのデザインは別にし、TwitterはTwitterに適した見た目にしたいと思います。もし、Twitterは何もしなかったら勝手にデザインが入るようになっているのであれば、それを確認したいです。
リンクカードのデザインは僕の方でやります。
確認、デザインがしやすいように、リンクカード、Twitterを入れた投稿をfixtureに入れておいてください。それをcommitしたら、メンションをお願いします。実際に手元でコードを見て確認します。
よろしくお願いします🙏
@machida まだテストやリファクタリングは出来ていないのですが、macihdaさんの確認のために一度commit、プルリクを作ってお見せした方がいいでしょうか? (実装に変更があれば手戻りが発生してしまうのかなと思った次第です)
@mousu-a キリのいいところで大丈夫ですー どこをキリのいいところとするかで迷ったら@komagataさんと相談してください🙏
@komagata
お疲れ様です。 1つ質問をさせていただきたいです。
リンクカード記法の[TITLE]
、(URL)
の値、
@[TITLE](URL)
metadata(レスポンス)のtitle
、url
の値
{
site_name: "Moeny Forward Developers Blog",
site_url: "https://xxx.com",
favicon: "https://xxx.com/favicon.ico",
url: "https://xxx.com/xxxx",
title: "ChatGPTのAPIがオープンになったので(略)",
description: "xxxxxxx",
image: "https://xxx.com/xxx.png"
}
この2つは同じものと考えてもいいのでしょうか?
同じものなのであれば、今回実装するAPIコントローラーのレスポンスは上記wikiの通り用意し、今回は(リンクカードの実装では)レスポンスのtitle
、url
を使わずにリンクカード記法のTITLE
とURL
を使う、という風な実装を考えています。
というのも、存在しないサイトのリンクカードを生成する時、リンクカード記法のTITLE
とURL
のみをmetadataとして用いてリンクカードを生成したいからです。
(存在しないサイトのリンクカードを生成する場合、空のレスポンスが返るため)
📝
リンクカードの仕様を@[TITLE](URL)
→ @[card](URL)
という記法に変更。リンクカードのtitle
はmetadataAPIのレスポンスのtitle
を使う。
@komagata すみません、こちらで質問させていただいたことと同じなのですが、具体的な内容がわからないメモの残し方をしてしまい、リンクカードの具体的なイメージについてもう一度お聞きしたいです🙇
@[card](url)
の前後に文字がある場合でもリンクカードを生成するという仕様になっていますが、具体的なイメージとしてはどちらでしたでしょうか?
aaa リンクカード記法 bbb
↓
aaa
リンクカード
bbb
aaa リンクカード記法 bbb
↓
リンクカード
Markdown-it のプラグインとして実装する際に、inline
のrule
として実装すると例1のようになります。
block
のrule
として実装すると例2のようになります。
個人的には例1(inline
のrule
としてプラグインを実装する)が正しい形なのかなという認識です。
お手間をとらせてしまい申し訳ありません🙇 よろしくお願いいたします。
@mousu-a @komagata
仕様を変更させてください。
inlineで @[card](URL)
がある場合、つまり、@[card](URL)
この前後に文字がある場合はリンクカードにする、という仕様は無しにしてください。
理由は、inline の場合、p や li の中にリンクカードが生成されるのは、マークアップ的にinvaridなHTMLになるからです。
あああ @[card](URL) あああ
の場合は、
<p>あああ</p>
[リンクカード]
<p>あああ</p>
という表示になるなら HTML は validにはなるので、そこまでしてあげたらユーザーには優しいですが、そもそも変換前の Markdown のコードの時点で invalid な HTML であるため、このように変更してあげる気遣いは不要です。
あああ @[card](URL) あああ
このコードの場合、現在のコード(machidaのPR)では以下のように出力されます。
<p>あああ @<a href="URL">card</a> あああ</p>
このままでいいと思います。 Zennもこのようになります。
あああ
@[card](URL)
あああ
このコードの場合は、
<p>あああ
<br>
[リンクカード]
<br>
あああ</p>
というHTMLに変換されるのも、invalidなHTMLなのでNGです。
<p>あああ</p>
[リンクカード]
<p>あああ</p>
このように変換されると、 HTML は valid にはなるので、そこまでしてあげたらユーザーには優しいですが、そもそも変換前の Markdown のコードの時点で invalid な HTML であるため、このように変更してあげなくてもいいとは思います。
ただ、Zennではこのように変換されるようにはなっていました(FBCがそこまでやってあげなくてもいいとは思いますが、簡単にできるならやってあげたい)。
現在のコード(machidaのPR)では、
<p>あああ
<br>
@<a href="URL">card</a>
<br>
あああ</p>
このように表示されます。
少し別の話になりますが、 現在の mousu さんのコードは、
あああ
@[card](URL)
あああ
の場合、
<p>あああ</p>
<p>[リンクカード]</p>
<p>あああ</p>
このように出力されます。 p の中に[リンクカード]があるので、これも invalid な HTML です。
僕の PR で[リンクカード]を囲うpを削除するコードを含めています。 以下のように出力されます。
<p>あああ</p>
[リンクカード]
<p>あああ</p>
機能を修正する場合は、差分が出てマージが難しくなるので https://github.com/fjordllc/bootcamp/pull/8143 こちらをマージした上で作業をお願いします。
↓こういうやつが欲しい。
ブログや日報など、Markdownエディターを使ってるところで使えるようにする。
下記のnpmを試してみてください〜(書式も下記のnpmに従います)
https://www.npmjs.com/package/@luckrya/markdown-it-link-to-card