レンタルサーバ(xServer)でcoesite3レポジトリを利用する

# はじめに
[こちらの記事](https://qiita.com/k96mz/items/7d921fd5f88095b2f123)でcoesite3レポジトリがローカルでうまく動くか試してみましたが、ダメでした。おそらく、自己署名証明書はEntra IDでは使用できないのだろうと思われますので、レンタルサーバで試してみたいと思います。
レンタルサーバの環境については、[こちらの記事](https://qiita.com/k96mz/items/ba983b4d34e94aafa4c2)に記載しています。

# MySQLの設定

インストール
```
sudo dnf install -y @mysql
```
バージョン8.0.36がインストールされました。 

MySQL を起動 & 自動起動設定
```
sudo systemctl enable --now mysqld
```
enable --now は サービスの自動起動 も設定します。

状態確認
```
sudo systemctl status mysqld
```

MySQLにログイン
```
mysql -u root
```

セッション用のデータベースを作成
```sql
CREATE DATABASE coesite3_db;
```

MySQL ユーザーを作成
```sql
CREATE USER 'coesite3_user'@'%' IDENTIFIED BY 'coesite3pass';
```
今回はIPアドレスで絞りたくないので、「%」を指定しています。
※接続元を絞る場合は「%」ではなく、任意のIPアドレスを使用します。

セッション用データベースの権限を付与
```sql
GRANT ALL PRIVILEGES ON coesite3_db.* TO 'coesite3_user'@'%';
FLUSH PRIVILEGES;
```

データベースの設定が正しくできたか確認
```sql
SHOW DATABASES;
```
coesite3_db がデータベースに含まれています。


ユーザの設定が正しくできたか確認
```sql
SELECT user, host FROM mysql.user;
```
hostが%となっていることが確認できました。

変更を確実に反映するために MySQL を再起動します。
```sql
sudo systemctl restart mysqld
```

[こちらの記事](https://qiita.com/k96mz/items/7d921fd5f88095b2f123#%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%83%9A%E3%83%BC%E3%82%B8%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B)を参照して、エラーが発生した箇所について認証方式を確認しました。
```sql
SELECT user, host, plugin FROM mysql.user;
```

![スクリーンショット 2025-02-18 12.15.03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/4527910f-0339-46a2-8d5a-8e2e9304b4f5.jpeg)

plugin の列が mysql_native_password になっていたので、問題なさそうです。



# coesite3のクローン

```
git clone https://github.com/unvt/coesite3
```
としてレポジトリをローカルにクローンします。

```
npm install
```

# HTTPSのための設定

### .envの設定

sample.envを.envに名称変更して、さらに設定を変更します。
特に
OAUTH_REDIRECT_URI=https\://k96mz.com:3000/unvt/auth/callback
とします。unvtが含まれていることに注意が必要です。

さらにEntra IDにも上記URLをRedirect URIsに加えておきます。

MySQLの設定は以下としています。
MYSQL_USER=coesite3_user
MYSQL_PASSWORD=coesite3pass
MYSQL_DATABASE=coesite3_db

### default.hjsonの設定
以下の通りの設定とします。

 privkeyPath: ./key/privkey.pem
 fullchainPath: ./key/cert.pem
 port: 3000

証明書の場所もパスに合わせて変更し、さらにフォルダ及びファイルの所有者を変更します。

### デフォルトページを表示する
node app.js
として、
https://k96mz.com:3000/unvt/
にアクセスします。

エラーが出ています。
ClientAuthError: no_account_in_silent_request: Please pass an account object, silent flow is not supported without account information

acquireTokenSilent() を実行しようとしていますが、account オブジェクトが指定されていないということでした。確かに、routes/index.jsのコードを見てみると、まだサインインしていないのに、userAccountの値を取ろうとしています。

:::note info
default.hjsonのfullchainPath: ./key/cert.pem
の部分をfullchain.pemの名前を変更して、cert.pemとして試してみましたが、解決しませんでした。
別記事のcoesiteのコードでも試してみましたが、同様の結果でした。
:::

[こちらの記事](https://qiita.com/k96mz/items/6e80215e1b50f752248f#routesindexjs)に記載されている通り、index.jsのいらない部分をコメントアウトしてみます。

これで実行してみると、先程のエラーはなくなりました。

しかし、
https\://k96mz.com:3000/unvt/
の読み込みに対して、以下のエラーが発生しました。
(failed)net::ERR_HTTP2_PROTOCOL_ERROR	

# リバースプロキシでやってみる
app_reverse.js
を作成して、リバースプロキシとNode.js間は、http接続に切り替えました。
```
app.listen(8080, () => {
  console.log("running at port 8080 ...");
});
```

.envファイルは以下に変更しています。
OAUTH_REDIRECT_URI=https://k96mz.com/unvt/auth/callback

https://k96mz.com/unvt/
にアクセスします。

![スクリーンショット 2025-02-18 15.27.25.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/f86f8d5a-ae01-4765-8936-c81e1362c2fe.jpeg)

無事に表示されました。

この後、再度
routes/index.js
のコードをオリジナルのものにして試すと、問題なく動きました。
今から思えば、キャッシュが残っているときには、こちらのコードでないと、再度サインインしないといけなくなるので、これで正しいのかもしれません。

サインインしようとしましたが、以下のようなエラーが出ました。

![スクリーンショット 2025-02-18 17.05.41.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/9cbafc15-83f7-4223-a75a-54fa57118f64.jpeg)

PKCEが必要な認証フローに適切な設定がされていない 場合に発生するエラーらしいです。
PKCE (Proof Key for Code Exchange, ピクシー) は、OAuth 2.0 の 認可コードフロー において、コードインジェクション攻撃を防ぐためのセキュリティ強化手法らしいです。

Entra IDのページを見ていたら、以下の通り設定されていました。間違って登録したのかもしれません。とりあえず、削除して再度試してみます。

![スクリーンショット 2025-02-18 17.04.12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/3f49c5a1-891b-41bf-b620-a211fce19a5f.jpeg)

ようやく無事に表示されました。


![スクリーンショット 2025-02-18 17.45.42.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/831a2ff3-4fcc-435a-8bbb-c2f5b09d8365.jpeg)

### MySQLに保存されているか確認
MySQLにログイン
```
mysql -u root
```

データベースを選択
```sql
USE coesite3_db;
```

sessionsテーブルを表示
```sql
SELECT * FROM sessions;
```

以下のようにきちんと記録されていました。

![スクリーンショット 2025-02-18 17.23.48.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/b6a0ebde-0844-4c15-9701-0d4a76ebf693.jpeg)

# 地図を表示する

Simple Web Map APP
にアクセスしてみましたが、エラーが発生しました。

![スクリーンショット 2025-02-19 15.45.13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/162472f3-3b17-4121-892f-12aceb2ba53d.jpeg)

(failed)net::ERR_NAME_NOT_RESOLVED	
は、ブラウザが 指定されたドメイン名を解決できない（DNSルックアップに失敗した）ことを示しています。
おそらく、職場環境にあるサーバなので、外部からは接続できない設定になっているからだと思われます。

### style-std.jsonを修正する
style-std.json、style-prod.json、style-el.jsonのsprite、glyphs、tilesのルートURLを以下に変更します。

https\://k96mz.com/unvt/

### mbtilesファイルを持ってくる

職場サーバのcoesite3レポジトリから複数個のmbtilesファイルをコピーしてきます。



再度、Simple Web Map APPの地図をみてみます。無事に、HTTP/2で表示されました。

![スクリーンショット 2025-02-27 16.45.51.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/fcca0534-bbe9-47cf-9cfd-7df72cc3d3e9.jpeg)


Web Map APP (3 styles)（Contour Mapの標高タイル以外）については、問題なく表示されました。しかし、
Web Map APP - 3D (Testing)
については、標高pngファイルが読まれる時と読まれない時があります。

![スクリーンショット 2025-03-25 11.22.01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/88857c5d-345f-4c1f-91f3-c36d79374c07.jpeg)


## Geoportalで表示する

以下のとおり修正して、レイヤをGeoportalに登録できるようにします。

```javascript: app_reverse.js
app.use("/unvt/rest/services/esriIF", esriIFRouter);
```

```json: index.json
 "tiles": [
"https://k96mz.com/unvt/VT/zxy/UniteStreetMapVector/{z}/{x}/{y}.pbf"
 ],
  "maxLOD": 15, //Map Viewerで表示可能とするため
```

```json: style.json
"sprite": "https://k96mz.com/unvt/sprite-rev/sprite_S",
"glyphs": "https://k96mz.com/unvt/font/{fontstack}/{range}.pbf",
"url": "https://k96mz.com/unvt/rest/services/esriIF/UniteStreetMapVector/VectorTileServer",
```

node app_reverse.js 
として、以下にアクセスします。
https://k96mz.com/unvt/

以下のURLをMap Viewerで読み込んでみます。
https://k96mz.com/unvt/rest/services/esriIF/UniteStreetMapVector/VectorTileServer

レイヤ読み込みが出来ましたが、以下の通り、
Status Code: 401 Unauthorized
となります。

![スクリーンショット 2025-02-27 18.19.33.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3684675/d46f0ca3-251d-462e-ba6a-6f0c45984fbc.jpeg)

https\://k96mz.com/unvt/VT/zxy/UniteStreetMapVector/2/1/1.pbf
は読み込まれているので、ルーティングは問題ないように思います。

Geoportalでのタイル読み込みに関して、
Geoportalの認証と、Azure認証が同時？にある中で、参照するタイルが
https://geoportal.un.org/
以外のタイルを参照することができないのではないかと考えられます。


### タイルへのルーティングを追加して実験する
Azure認証がある場合には、タイルへのルーティングが、
https\://k96mz.com/unvt/VT/zxy/UniteStreetMapVector/{z}/{x}/{y}.pbf
となっているのが原因かもしれないと考えました。

esriIF.jsにタイルへのルーティングのコードを追加して試してみようかと思いましたが、これはpbfファイルの場合で、mbtilesファイルの場合はVT.jsを使用する必要があるので、どうしたら良いかよく分かりませんでした。

ひとまず、index.jsonにおいて、tilesに/rest/servicesをつけてました。
さらに、app_reverse_restservices.jsというファイルを作り、以下の設定としました。

app.use("/unvt/rest/services/VT", VTRouter);

しかし、結果はおなじく、レイヤ読み込みが出来ましたが、Status Code: 401 Unauthorized
が返ってきて、タイルが読み込めませんでした。


# まとめ
レンタルサーバでcoesite3レポジトリを利用してみました。MySQLを使用してユーザ認証がうまくいくことが確認されました。
リバースプロキシなしだと、index.jsを修正しないとエラーが発生することが分かりました。リバースプロキシを設定することで、うまくいったので、本番環境でもリバースプロキシを利用したほうが確実な気がします。
地図表示については、Geoportalでのタイル読み込みに関して、401エラーが返ってきて表示出来ませんでした。

# Reference
https://qiita.com/k96mz/items/9a191ee7881f7bf9f07e

https://qiita.com/k96mz/items/7d921fd5f88095b2f123

https://qiita.com/kaburankattara/items/842e2e1758ea00d19553