Cloudflare Access で保護された Confluence に Markdown をアップロードする

# 目的
一部変更した `md_to_conf` ツールを使って以下のような構成を実現します。

https://github.com/kyouheicf/md_to_conf

```mermaid:
graph LR

subgraph PC [Local PC]
    md[Markdown file]
end

IdP

subgraph Cloudflare
    Access
    Domain["`your.confluence.com`"]
end

Origin[Confluence Server]

md --> |Upload|Access
Access <--> |Authentication|IdP
Access --> |cf-access-token|Domain --> |point to origin|Origin
```

# Confluence に Markdown をアップロードするには
以下の `md_to_conf` ツールが使えます。

https://github.com/RittmanMead/md_to_conf

以下の記事が参考になります。

マクロや HTML タグの変換を支援してくれる機能や不要なタグによるトラブルシューティングに関する記載があります。

https://qiita.com/reonyanarticle/items/b680e12afbe1e95841b9

# Cloudflare Access で保護された Confluence への対応

Cloudflare Access で保護されている場合、 同一ドメイン配下にある Confluence の API を経由して操作するためには `cloudflared` によって取得したアクセストークンを `cf-access-token` ヘッダとしてリクエストに付与する必要があります。

そのため、 `md2conf.py`の コードを以下のように変更します。

```diff_python
-       session.headers.update({'Authorization': 'Bearer ' + PA_TOKEN})
+       session.headers.update({'cf-access-token': '' + PA_TOKEN})
```

その後、以下のような手順で Confluence に Markdown をアップロードできます。

```shell:
export CONFLUENCE_ORGNAME='your.confluence.com'

cloudflared access login https://$CONFLUENCE_ORGNAME > /dev/null         

export CONFLUENCE_PERSONAL_ACCESS_TOKEN=$(cloudflared access token -app=https://${CONFLUENCE_ORGNAME})

python3 ~/md_to_conf/md2conf.py YOUR_MARKDOWN_FILE_FULLPATH.md '~USERNAME' -l DEBUG
```

# Markdown ファイルの取り扱い

Markdown ファイルの 1 行目が Confluence ページのタイトルとして扱われます。
または `--title` オプションで指定可能です。

```python:md2conf.py
...
PARSER.add_argument('--title', action='store', dest='title', default=None,
                    help='Set the title for the page, otherwise the title is going to be the first line in the markdown file')
...
```

Markdown ファイル名は、あくまでローカルファイルの指定のみに使用します。

```markdown:sample.md
Markdown Upload Test

# Test

Test

| a    | b    | c    |
| ---- | ---- | ---- |
| d    | e    | f    |
| g    | h    | i    |
| j    | k    | l    |
```

## 既存ページタイトルにマッチしなかった場合

新規ページが作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/ecf89432-b59c-de04-d6fb-362de293bc33.png)

## 既存ページタイトルにマッチする場合

指定の階層にアップロードしたい場合は、事前にページを作っておくことで、既存ページタイトルにマッチした場所へアップロードできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/2177a743-b01c-ca7c-c9fb-c7a831f31ba3.png)

# 参考：その他の変換
以下の要領で Confluence ページの見やすいマクロに変換する処理を追加で実装できます。

## ステータスマクロ

```markdown:status.md
| ROADMAP | SPEC | BLOG | BUG  | APPROVED |
| ------- | ---- | ---- | ---- | -------- |
| ROADMAP | SPEC | BLOG | BUG  | APPROVED |
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/0483fe11-18b7-92f2-9426-03df8c45c48b.png)

```diff_python
+    # ROADMAP Status Macro Converter
+    statuses = re.findall(r'<td>ROADMAP</td>', html)
+    if statuses:
+        for status in statuses:
+            html = html.replace(
+                status, '<td><ac:structured-macro ac:name="status" ac:schema-version="1"><ac:parameter ac:name="colour">Yellow</ac:parameter><ac:parameter ac:name="title">ROADMAP</ac:parameter></ac:structured-macro></td>')
```

## 絵文字マクロ

```markdown:emotion.md
| GOING  | WAIT      | DONE               |
| ------ | --------- | ------------------ |
| :star: | :pushpin: | :white_check_mark: |
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/1ebd64f5-2751-454c-48d9-3bc63ddadafa.png)

```diff_python
+    # DONE Status Macro Converter
+    statuses = re.findall(r'<td>:white_check_mark:</td>', html)
+    if statuses:
+        for status in statuses:
+            html = html.replace(
+                status, '<td><ac:emoticon ac:name="tick"/></td>')
```

## JIRA マクロ

```markdown:jira.md
| JIRA                                                         |
| ------------------------------------------------------------ |
| [[xxx-####] Kyouhei-test - Jira](https://jira.confluence.com/browse/xxx-####) |
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/6bd6585a-c416-75f8-569e-a8531d7092fe.png)

```diff_python
+    # Jira Macro Converter
+    links = re.findall(r'<a href=".+?">.+?</a>', html)
+    if links:
+        for link in links:
+            matches = re.search(r'<a href="(.+?)">(.+?)</a>', link)
+            ref = matches.group(1)
+            alt = matches.group(2)
+            jira_url = "jira." + '.'.join(ORGNAME.split('.')[-2:])
+            if urllib.parse.urlparse(ref).hostname == jira_url:
+                key = ref.rsplit('/', 1)[-1]
+                html = html.replace(
+                    link, '<ac:structured-macro ac:name="jiraissues"><ac:parameter ac:name="anonymous">true</ac:parameter><ac:parameter ac:name="key">%s</ac:parameter></ac:structured-macro>' % key)
```