さて先日つくった Nextcloud + QOwnNotes のノートアプリ環境ですが、若干の不満はありますが、まずまずって感じです。
スマホだと(すくなくとも いPhone では) Evernote のような Webクリッピング機能がないことです。iPhone でブラウジング中に、このページちょっと見返す必要があるかもって時に、手数が多いのが面倒です。(デスクトップだとPluginがあるっぽい)
これを解消するために n8n で メモする URLを受取りマークダウンで保存するフローを作ります。
前回 の iPhone ショートカットアプリから POST リクエストする仕組みを使い、
ブラウザで共有 > ショートカットアプリで POST > n8n でマークダウン作成 > NextCloud ファイル保存
の流れができ URL を簡単にメモできるようにします。
NextCloud
n8n から NextCloud へのアクセスは公式のノードがありますが今回はあえて使いません。公式のノードでは NextCloud にアップロードしてファイルを作成するという正しい方法でおこなっています。
自分の環境では NextCloud がコンテナとして動作しているので万が一止まっていると、メモしたいものが消えてしまうので、直接ローカルファイルシステムに書き込んでいます。ローカルのフォルダを docker にマウントしてみえるようにしています。
この方法には1つ欠点があって、NextCloud からすると勝手に追加されたファイルを認識できないので、 occ コマンドでフォルダをスキャンしてやって初めて NextCloud の管理下に入れることができる。今回は systemd タイマーで解決することにした。
n8n
n8n のフローはごくシンプルです。
- Webhook
- Code
- Read・Wrie Files from Disk
の3つのノードを使います。ただ順番につなげるだけです。

Webhook
HTTP Mothod: POST
に設定します。[Test URL]、[Production URL] の違いですが、編集画面でデバッグ実行できるのが [Test URL] です。Production の方は Execution ログには残りますが、途中で動作を止めたり、編集できないので注意。
これに気づかず [Test URL] のまま運用して保存(つまり n8n のフローが実行)されないことにしばらくきづきませんでした。
メッセージbody には以下のような JSON スキーマを期待しています。
{
"tilte": "ページタイトル",
"url": "メモしたいURL"
}Code
キャプチャでは Generate Markdown という名前に変更していますが、それはお好みで。
// Get all input items
const allItems = $input.all();
// First item should have the HTML data from HTTP Request
const httpResponse = allItems[0].json;
const url = $('Webhook').first().json.body.url
const title = $('Webhook').first().json.body.title
// Generate filename with timestamp
const now = new Date($now);
const yyyy = now.getFullYear();
const mm = String(now.getMonth() + 1).padStart(2, '0');
const dd = String(now.getDate()).padStart(2, '0');
const hh = String(now.getHours()).padStart(2, '0');
const min = String(now.getMinutes()).padStart(2, '0');
const ss = String(now.getSeconds()).padStart(2, '0');
const filename = `${yyyy}${mm}${dd}-${hh}${min}${ss}.md`;
// Generate markdown content
const markdown = `title: ${title}\nurl: ${url}\n`;
// Return data with binary content
return [{
json: {
filename: filename,
url: url,
title: title
},
binary: {
data: {
data: Buffer.from(markdown, 'utf8').toString('base64'),
mimeType: 'text/markdown',
fileName: filename
}
}
}];POST リクエストから JSON 取り出して、日時ベースのファイル名を決定し、マークダウン本文を構築して、次のノードにそれらを流しています。
Read / Write Files from Disk
Operation: Write File to Disk
File Path and Name: /保存したいパス/{{ $json.filename }}
$json.filename は前のノードから渡されたファイル名を指定している。
systemd timer
先述のとおり、 occ コマンドでスキャンする必要があるので、systemd タイマーを書く。もちろん cron でもいい。ファイル書くのは面倒だけど、こういうのこそ生成 AI が得意なので適当に下書きしてもらってなおすと楽。
cron は実行間隔の書き方を永遠に覚えられる気がしないので、その点だけが systemd timer が好きな理由。
[Unit]
Description=Nextcloud scan Notes folder
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
User=実行するユーザー名
ExecStart=/usr/bin/docker exec -u www-data nextcloud php occ files:scan --path="/保存先のフォルダ"
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target[Unit]
Description=Scan Nextcloud Notes/zzWorking folder every 5 minutes
Requires=nextcloud-scan.service
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
AccuracySec=1min
[Install]
WantedBy=timers.targetあとは
% sudo systemd daemon-reload
% sudo systemd enable nextcloud-scan.service
% sudo systemd start nextcloud-scan.serviceで実行する。
コメントを残す