OmniAICreator/Qiita-1.07M huggingface dataset:
【Cisco_設定】C891FJK9の任意のポートを自動再起動

自宅ルータのWANポートのアドレスがDHCP取得のためIPの切り替わりの時などに通信断が続くことがあるため、IP SLA/EEMの機能により監視と自動設定変更を実施させる。

8.8.8.8へのPING監視を実施してダウンの発生から継続時間により任意のポートを再起動させている。以下のコマンドにより設定が実施できた。

※%TRACK-6-STATE: 200 ip sla 1 reachability Up -> Downのログが出力されるとtrack 200が実施　　　される。

※%TRACK-6-STATE: 200 ip sla 1 reachability Down -> Upのログが出力されるとtrack 200が再開される

ip sla 1
icmp-echo x.x.x.x
timeout 1000
frequency 720
!
ip sla 2
icmp-echo x.x.x.x
timeout 1000
frequency 300
!
ip sla schedule 1 life forever start-time after 00:10:00
ip sla schedule 2 life forever start-time after 00:05:00
!
track 100 ip sla 1 reachability
track 200 ip sla 2 reachability
!
event manager applet RELOAD
event track 100 state down
action 1.0 reload
!
event manager applet LINK-UP-DOWN
event track 200 state down
action 1.0 cli command “enable”
action 2.0 cli command “config t”
action 3.0 cli command “interface xxxxx”
action 4.0 cli command “shutdown”
action 5.0 cli command “no shutdown”
action 6.0 cli command “end”

※x.x.x.xは監視先のIPを設定

◇　icmp-echo 200.1.1.1 source-interface GigabitEthernet0/1
　⇒　ICMP Echoリクエストを送信する宛先IPアドレスと送信元インターフェースの指定

　◇　timeout 5000
　⇒　ICMP Echoリクエストに対する応答時間をミリ秒で指定。5000 = 5秒。

　◇　frequency 10
　⇒　ICMP Echoリクエストを送信する間隔を秒で指定。10 = 10秒。

　◇　ip sla schedule 1 life forever start-time now
　⇒　指定したIP SLA番号に、実行スケジュールの開始時間と有効期限を指定。通常、上記のように設定する。

　◇　track 101 ip sla 1 reachability
　⇒　監視するオブジェクトの指定と定義されたIP SLA番号を割り当てる。
【Cisco_ログ】show techの内容(C891FJ-K9)

C891FJ-K9でshow techを取得した際に表示されるログの一覧

show version
show running-config
show stacks
show interfaces
show interfaces history
show controllers
show user
show file systems
show file descriptors
show flash:all
show data-corruption
show memory statistics
show process memory
show process cpu
show process cpu history
show diag
show platform
show environment all
show controllers t1
show controllers e1
show controllers j1
show crypto engine configuration
show crypto engine accelerator statistic
show crypto engine accel ring packet
show crypto engine accel ring pool
show crypto engine accel ring control
show cdp neighbor
show cdp neighbor detail
show ip nbar version
show webvpn session context all
show webvpn stats detail
show webvpn stats context all
show webvpn nbns context all
show webvpn stats cifs
show webvpn stats sock
show raw reclaimed
show crypto key mypubkey rsa
show crypto call admission statistics
show crypto gdoi
show crypto gdoi rekey sa
show crypto gdoi rekey sa detail
show crypto gdoi gm
show crypto gdoi gm acl
show crypto gdoi gm pubkey
show crypto gdoi gm rekey detail
show crypto gdoi gm replay
show crypto gdoi ipsec sa
show crypto gdoi ks
show crypto gdoi ks acl
show crypto gdoi ks coop
show crypto gdoi ks coop version
show crypto gdoi ks identifier detail
show crypto gdoi ks member
show crypto gdoi ks policy
show crypto gdoi ks rekey
show crypto gdoi ks replay
show crypto gdoi diagnose events
show crypto gdoi diagnose errors
show license udi
show license udi standby
show license feature
show license feature standby
show license file
show license file standby
show license detail
show license detail standby
show license status
show license status standby
show license statistics
show license statistics standby
show buffers
show buffers usage
show inventory
show region
show dmvpn detail
show crypto ipsec client ezvpn
show ip nat statistics
show ip nat translations
show crypto map
show access-list
show crypto isakmp policy
show crypto ipsec transform
show crypto ipsec profile
show crypto isakmp sa
show crypto engine connection active
show crypto ipsec sa
show modem version
show modem
show modem summary
show modem call-stats
【Cisco_ログ】IOS XE Software, Ver17.03.04以降のライセンス確認方法

IOS XE Software, Version 17.03.04以降(C9300など)では、ライセンスの確認の仕方が変わります。従来の考え方と違いSLPになったことによりレポーティングがされてればOKとのことで確認方法を以下に記載。

show license statusのUsage Reporting内のLast ACK receivedなどの情報が、日付で更新されていればレポーティングされているとのこと。

------------------ show license status ------------------

Utility:
Status: DISABLED

Smart Licensing Using Policy:
Status: ENABLED

Data Privacy:
Sending Hostname: yes
Callhome hostname privacy: DISABLED
Smart Licensing hostname privacy: DISABLED
Version privacy: DISABLED

Transport:
Type: Transport Off

Policy:
Policy in use: Merged from multiple sources.
Reporting ACK required: yes (CISCO default)
Unenforced/Non-Export Perpetual Attributes:
First report requirement (days): 365 (CISCO default)
Reporting frequency (days): 0 (CISCO default)
Report on change (days): 90 (CISCO default)
Unenforced/Non-Export Subscription Attributes:
First report requirement (days): 90 (CISCO default)
Reporting frequency (days): 90 (CISCO default)
Report on change (days): 90 (CISCO default)
Enforced (Perpetual/Subscription) License Attributes:
First report requirement (days): 0 (CISCO default)
Reporting frequency (days): 0 (CISCO default)
Report on change (days): 0 (CISCO default)
Export (Perpetual/Subscription) License Attributes:
First report requirement (days): 0 (CISCO default)
Reporting frequency (days): 0 (CISCO default)
Report on change (days): 0 (CISCO default)

Miscellaneous:
Custom Id:

Usage Reporting:
Last ACK received: Jan 27 12:51:24 2022 jst
Next ACK deadline: Apr 27 12:51:24 2022 jst
Reporting push interval: 30 days
Next ACK push check:
Next report push: Jul 24 10:54:13 2022 jst
Last report push:
Last report file write:

Trust Code Installed:

上記の際、show license statusではIn Activeと表示されているが問題ない。あくまで、上記コマンドの結果が大事である。
【Cisco_設定】従属APをMEに変換(1800シリーズ)

従属APのOSをME用に変換する際は、ME用のOSを用意して下記のコマンドでダウンロードする

【必要ファイル】
●ME用のtarファイル
●ME用のZIPファイル　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　(ZIPも使用できるが解凍後のフォルダから適用できるファイルを選択する必要がある)

ap-type mobility-express tftp://tftpサーバアドレス/OSファイル名

【実施例】
ap-type mobility-express tftp://192.168.1.100/AIR-AP1830-K9-8-5-171-0.tar

※上記の内容はAIR-AP1832I-Q-K9にて検証済み
【Cisco_設定】Catalyst 9100アクセスポイントバージョンアップ

バージョンアップに必要となるファイルは、ap1g7になります。

APにコンソールで接続し以下のコマンドを実施。TFTPサーバも用意します。

ap-type ewc-ap tftp://192.168.1.25/ap1g7 

EWCのOSもバージョンアップも同時にする場合は、以下のコマンドを実施

ap-type ewc-ap tftp://192.168.1.25/ap1g7 tftp://192.168.1.25/C9800-AP-iosxe-wlc.bin
【Cisco_設定】EWCから従属APのOSへの切り替え

EWCとして起動したC9115AXE-EWC-Q(EWC付きAPシリーズ含む)を従属APとして使用する方法を記載

①ハードウェアリセットを実施する(write eraseなどのソフトウェアリセットでは再起動後にAPのOSが起動せずEWCとしてしか起動しないため)

②初期化後、APにログインしap-type capwapを実施する。

③再起動後に従属APとして起動する。

※OSを従属AP用にインストールしなおしても問題ない。
【Cisco_仕様】従属APのIPがDHCPに変更される

帰属しているWLCが起動されておらず環境内にDHCPサーバが存在すると、固定IPを設定していてもDHCPパケットを受け取りDHCPにより割り振られたIPに変更されることがある。

初期化されているWLCに直接つないでもDHCPのIPに変更されてしまう。おそらく、初期設定をしていないためDHCPパケットを自動的に排出してしまうためと思われる。

参考https://community.cisco.com/t5/%E3%83%AF%E3%82%A4%E3%83%A4%E3%83%AC%E3%82%B9-%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88/static-ip-%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%9F%E9%9B%86%E4%B8%AD%E7%AE%A1%E7%90%86%E5%9E%8B-ap-%E3%81%AE-ip-%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%81%8C%E5%A4%89%E3%82%8F%E3%82%8B%E4%BA%8B%E8%B1%A1%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/ta-p/3134274
【Cisco_設定】EWCからAPへのログインを有効化

EWCからAPへのログインを実施する際は、「wireless ewc-ap ap shell username cisco」コマンドを実施するのだがログインするためにはあらかじめログイン情報を設定する必要があります。

EWC(config)#ap profile default-ap-profile　※デフォルトのAPプロファイルの設定モード
EWC(config-ap-profile)#mgmtuser username cisco password 0 Cisco456 secret 0 Cisco789
※APのログインユーザ名とパスワード、特権EXECパスワードの設定

参考URL:iDATEN(韋駄天)｜ Wireless 第41回[2022年03月14更新]「Catalyst9100シリーズ 外部DHCPサーバが無い環境にEWC-APを導入してみた」
文字列はなぜ.equalsで比較するのか。参照型の変数について分かりやすく説明してみる。

### さっそくいってみよう
結論だけ知りたい方は[変数AとBを比較してみましょう](#変数aとbを比較してみましょう)にとんでください
まず初めに、参照型の代表であるStringの変数を宣言してみましょう。

```
String A = "あいうえお";
```
この時何が起きているかというと、
Aロッカーに "あいうえお" という文字列が格納された。と考えてください。
(文字列だとわかりにくい場合は、サッカーボールでもカバンでもなんでも構いません。
わかりやすいものでイメージしてみてください。)
そしてそのAロッカーを開けるための鍵が、変数Aになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3511376/02920c11-f7cc-d99f-ece9-49bc346afbb9.png)
この図を見てわかる通り、変数Aに直接 "あいうえお" が入っているわけではありません。
変数Aにはロッカー番号が入っているため、参照型と呼ばれるわけですね。

それではここで、もう一つ変数を宣言してみましょう。
```
String B = "あいうえお";
```
Bロッカーとその鍵が増えました。中身は "あいうえお" です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3511376/b0e8371e-314e-87ec-e19a-a56eb60cb2d8.png)



### 変数AとBを比較してみましょう
```
String A = "あいうえお";
String B = "あいうえお";

System.out.println(A == B); /* False */
System.out.println(A.equals(B)); /* True */
```
#### 「==」の場合
「==」ではロッカー番号を比較するため、結果はfalseになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3511376/1e105b87-2539-3881-ecf0-c0e5e5327a05.png)

#### 「.equals」の場合
「.equals」ではロッカーの中身を比較するため、結果はTrueになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3511376/e8366ed3-5d29-d5fa-cae7-c37031b29345.png)

以上、今日はこの辺でおしまいです。
代入についても、動きが理解できると面白いので次回書いてみようと思います。
nuxt config における hooksとは？

Nuxt.jsの設定ファイルである`nuxt.config.js`における「hooks」は、Nuxt.jsのライフサイクルイベントに対してカスタムのコードをフックするための仕組みです。ライフサイクルイベントは、アプリケーションの起動やページの生成など、アプリケーションのさまざまな段階で発生するイベントです。

「hooks」を使用することで、これらのイベントが発生したときに特定の処理を追加できます。これにより、アプリケーションの動作をカスタマイズしたり、外部モジュールを統合したりすることができます。

`nuxt.config.js`内で「hooks」を使用する場合、以下のような構造を持ちます：

```javascript
export default {
  hooks: {
    hookName: (arg1, arg2, ...) => {
      // イベントが発生したときに実行される処理
    },
    // 他の hooks も定義可能
  }
}
```

ここで、`hookName`は特定のライフサイクルイベントを指します。例えば、`hooks: { 'build:done': () => {} }`のように指定することで、ビルドが完了した際にカスタムの処理を実行することができます。

一部の一般的な`hookName`は以下の通りですが、詳細な情報はNuxt.jsの公式ドキュメントを参照してください。

- `build:before`
- `build:done`
- `render:before`
- `render:done`
- `generate:before`
- `generate:done`
- `modules:before`
- `modules:done`
- など

これらのライフサイクルイベントを活用することで、Nuxt.jsアプリケーションの挙動をカスタマイズし、必要な処理を追加することが可能です。
Laravel10でrpm run devを実行するとnet::ERR_ADDRESS_INVALIDが発生する

## 概要
Laravel10 + Breeze（react）を使用。
ある時、`npm run dev`を実行し、画面を確認すると下記のエラーがコンソールに表示されていた。
`GET http://[::]:5173/@vite/client net::ERR_ADDRESS_INVALID`

bashにはこんなエラーが...
`Port 5173 is in use, trying another one...`

## 対処方法
自分は`vite.config.js`にclientPortを追加することで解決できました。

```
export default defineConfig({
    server: {
        hmr: {
            clientPort: 5173,　←ここを追加
            host: 'localhost',
        }
    },
});
```
Nuxt3で採用されているサーバーエンジン nitroとは？

「Nitro」は、Nuxt.jsの新しいバージョンである「Nuxt 3」で採用されているサーバーエンジンです。Nuxt 3は、Nuxt.jsチームによって開発された、モダンなウェブアプリケーションを構築するためのフレームワークです。Nuxt 3は、従来のNuxt.jsとは異なるアーキテクチャを持ち、高速なサーバーサイドレンダリングとパフォーマンスの向上を重視しています。

Nitroは、Nuxt 3の中核を成すサーバーエンジンで、以下の特徴を持っています

### Nitroの５つの特徴

‌

1. **高速なサーバーサイドレンダリング（SSR）**: Nitroは、高速なSSRを提供することを目指して設計されています。事前レンダリングとクライアントサイドレンダリングの組み合わせにより、快適なユーザーエクスペリエンスを提供します。
2. **インクリメンタルなデータ取得**: Nitroでは、コンポーネントのデータ取得を必要な箇所に適用できる「インクリメンタルデータフェッチ」がサポートされています。これにより、必要なデータだけを取得して効率的にページを更新することができます。
3. **モジュールベースのアーキテクチャ**: Nuxt 3では、機能ごとにモジュールを作成し、それらのモジュールを組み合わせてアプリケーションを構築するアーキテクチャが採用されています。これにより、アプリケーションの構造がシンプルでメンテナンスしやすくなります。
4. **TypeScriptサポート**: Nitroは、TypeScriptをサポートしており、型安全性を向上させながら開発することができます。
5. **プラグインシステム**: Nuxt 3はプラグインシステムを提供し、アプリケーションに機能を追加するためのカスタマイズが容易です。

Nuxt 3は、従来のNuxt.jsとは異なる設計とアーキテクチャを持ち、開発者が高速なウェブアプリケーションを構築するためのツールとして注目されています。詳細な情報やドキュメントは、公式のNuxt 3ウェブサイトを確認することをおすすめします。
WSL2用に内蔵ストレージをext4フォーマットしてマウントする

# 背景
- WSL2のディスクイメージがストレージを逼迫しているので、内蔵SSDを増設してWSL2（Ubuntu 20.04）から読み書きしたい
- WSL2からストレージを利用するには、ディスクイメージを作る方法と直接マウントする方法の2つがあるが、SSDをext4フォーマットして直接マウントすることにした
  - ちなみにext4にフォーマットせずともNTFSのままでマウントすることもできる
    - 今回は大容量データの読み書きが発生するのでパフォーマンス優先でext4とした
  - 手順は公式ドキュメントに詳しく書かれている
  - https://learn.microsoft.com/ja-jp/windows/wsl/wsl2-mount-disk

# 作業手順
## おおまかな流れ

1. Windows上でパーティションを分割（optional）
1. PowerShellから、WSL2にパーティションをマウント
1. WSL2上でext4にフォーマット、マウント設定

## Windows上でパーティションを分割（optional）
SSDやHDDを分割して使う必要がなければこの手順はスキップして良い。  

今回の場合、8TBのSSDに4TBのパーティションを作成した。
Windowsの『ディスクの管理』を起動し、新規GPTパーティションを作成する。容量はお好きに。  
筆者は癖でNTFSでフォーマットしてドライブレターを割り当てたが、後でext4フォーマットする際に消し飛ぶのでその必要はなかった。

## PowerShellから、WSL2にパーティションをマウント
まずはPowerShellから次を実行して `DeviceID` を調べる
  ```powershell
  GET-CimInstance -query "SELECT * from Win32_DiskDrive"
  ```
結果の例：ここでは `\\.\PHYSICALDRIVE0` というのがDeviceID


    ```
    DeviceID           Caption                 Partitions Size          Model
    --------           -------                 ---------- ----          -----
    （中略）
    \\.\PHYSICALDRIVE0 Samsung SSD 870 QVO 8TB 1          8001560609280 Samsung SSD 870 QVO 8TB
    ```

続いて、ストレージをWSL2にマウントする。これでドライブがWSL2から見えるようになるはず。ここで権限周りで怒られる場合があり、その場合はPowerShellを管理者権限で起動（PowerShellのアイコンを右クリック→管理者として実行）してリトライする。


```powershell
wsl --mount <YourDeviceID> --bare
例）wsl --mount \\.\PHYSICALDRIVE0 --bare
```

WSL2のターミナルを開き、ドライブが見えるか確認する。ここでパーティション名を確認しておく。
```bash
lsblk
```

結果例： この場合はsde2が新しく作ったパーティションとわかる
  ```
  （中略）
  sde      8:64   0   7.3T  0 disk
  ├─sde1   8:65   0    16M  0 part
  └─sde2   8:66   0   3.7T  0 part
  ```

## WSL2上でext4にフォーマット、マウント設定

- ここからはWSL2上での作業になる
- `e2fsprogs` パッケージをインストール
    ```bash
    sudo apt update
    sudo apt install e2fsprogs
    ```
- フォーマットを実行
    ```bash
    sudo mkfs.ext4 /dev/sde2		
    ```
- マウントポイントを設定
    ```bash
    sudo mkdir /mnt/data  # マウントポイントの作成
    sudo mount /dev/sde2 /mnt/data
    ```

- 自動マウント設定
    - 先にパーティションのUUIDを調べておく
    ```bash
    sudo blkid
    ```
    - 結果例
        - 今回であれば `/dev/sde2: UUID="f764fdca-f2ba-408b-a790-a1f56120b2fb"`がそれ
    ```bash
    /dev/sde2: UUID="f764fdca-f2ba-408b-a790-a1f56120b2fb" TYPE="ext4" PARTLABEL="Basic data partition" PARTUUID="3329384f-efc0-496c-ae8a-6f7d54f620c5"
    /dev/loop0: UUID="2021-12-21-15-08-50-00" LABEL="wsl-cli-bundle" TYPE="iso9660"
    /dev/loop1: UUID="2021-12-21-15-17-09-00" LABEL="LinuxKit" TYPE="iso9660"
    /dev/sda: UUID="3255683f-53a2-4fdf-91cf-b4c1041e2a62" TYPE="ext4"
    ```

    - fstabに自動マウントのための設定を追記    
    ```bash
    sudo vi /etc/fstab
    # ファイルの末尾に以下の行を追加して保存
    UUID=<フォーマットしたパーティションのUUID> /mnt/new_ext4_partition ext4 defaults 0 0
    # 例
    UUID=f764fdca-f2ba-408b-a790-a1f56120b2fb /mnt/data ext4 defaults 0 0
    ```

- 完了です。お疲れ様でした。

すべての作業が完了した後で、該当パーティションはWindowsからはこのように見える
![スクリーンショット 2023-08-15 23.41.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162798/291a9ffa-8142-8eee-a18b-22dbd74c0869.png)
【Zod】確認用パスワードが一致していることを確認する

## 確認方法

以下のようにすることでパスワードと確認用パスワードが一致していることを確認できます。

```js
export const changePasswordSchema = z.object({
    password: signUpSchema.shape.password,
    passwordConfirmation: z.string()
  })
  .refine((data) => data.password === data.passwordConfirmation, {
    message: 'パスワードが一致しません',
    path: ['passwordConfirmation'],
  })
```
structや定義した内容のjson:"id"などはどのような意味を持つのか

## structとは
構造体を決定するのに用いる。今回の場合だと、ユーザー情報の構造体を定義するのに用いている。

### 上記の内容からユーザーの情報を定義してみる
``` user.go
package model

import "time"

// ユーザーの構造体を定義
type User struct {
	ID        uint      `json:"id" gorm:"primaryKey"`
	Email     string    `json:"email" gorm:"unique"`
	Password  string    `json:"password"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}
```

[こちら](https://qiita.com/NaoyukiSato/items/6dcd26725d01d0b6b2fa)の記事によると
>投げ入れられるjson名をそのままGoの世界で扱いたくないということはよくあります。

ということがあるみたいです。理由は上記の記事に記述されてます。(こちらにも記述しておきます)

>e.g. json名をGoの世界でそのまま使いたくないとき
>* プロジェクトの命名規則と異なる。
>* すでにnameという変数は使われていて名前がバッティングする。
>* nameってなんの名前のこと指してるのかわからない。わかりやすいフィールド名にしたい。

また、コード内の`json:"id" gorm:"primaryKey"`の部分は、上記の構造体をjsonに変換した時に自動的にフィールド名を小文字に変換してくれる書き方です。

## 参考サイト
https://qiita.com/NaoyukiSato/items/6dcd26725d01d0b6b2fa
Node.js上のCLIツールで文字列をクリップボードにコピーさせる

# 概要

個人的な用途で、Node.jsのCLIツールを作っていました。求める機能としては、「標準入力した複数行の文字列を、(若干加工した後)1行ずつ逐次クリップボードにコピーさせる」というものでした。クリップボードへのアクセス以外については比較的苦しむことなく実装できたのですが、クリップボードへのアクセスを行う方法について簡単に調べたところ、殆どの記事で「このnpmパッケージを使え」みたいなことが書かれており……「その部分除けば50行もないコードの為にpackage.json書けとでも……？！」という気分になった結果、どうにかNode.jsに付属している機能だけで何とかすることができたので、それについて書いていきたいと思います。

# コード

完成形としては以下のようなものになりました。

```javascript
const {exec} = require('node:child_process');
function clip(str) {
  exec(`echo ${str} | clip`);// ※Windows以外の環境ではここをいじる必要がある
}

const rl = require('node:readline/promises')
function createReader() {
  return rl.createInterface({
    input: process.stdin,
    output: process.stdout
  });
}

async function main() {
  const chunk = [];
  console.log('Please input.');

  for await (const inputstr of createReader()) {
    if (!inputstr) break;// 空行で脱出
    chunk.push(inputstr);
  }

  for (str of chunk) {
    clip(str);
    const waiter = createReader();
    await waiter.question(`copied: ${str}`);
    waiter.close();
  }
}

main();
```

# 解説

……おおよそ要件通りと言えばそうです。
`readline`については、割と見れば分かるでしょうし、他にも有益な記事が多数あると思うので、割愛します。

クリップボードへのコピー機能については、苦戦した割にコードとしてはシンプルにまとまりました。
要は、「クリップボードへのコピーを行うプロセスを起動する」という方法で実現させています。コード内では、Windowsのcmd.exe上で実行する際のコマンドが書かれています。もし他の環境で実行する場合は、ここがその環境に応じたコマンドに変えられる必要があります。

# 最後に

色々分からないことがありながら捻り出したコードなので、もっといい方法があるかもしれません。その時は……もっと良い方法について記事を書いていただければと思います。
ここまで読んで下さり、ありがとうございます！
[OCI] クロスリージョンレプリケーションをアーカイブストレージに保存する

OCIを利用するにあたり、バックアップデータなどの遠隔地保管としてObject Storageのクロスリージョンレプリケーションを利用するケースは多いです。
そしてその際に、保存先のストレージ層を「アーカイブ」にして、コストを節約するケースも多く見受けられます。

しかし、Object Storageのクロスリージョンレプリケーション先をアーカイブ・ストレージにしている解説記事がぱっとみ、見当たらなかったので今回記事にしてみます。

※ アーカイブ・ストレージを使用する際の制約事項にはご注意ください。
・アーカイブ・ストレージからオブジェクトをダウンロードするには、まずオブジェクトをリストアする必要があります。
・アーカイブ層の最小ストレージ保持期間は90日です。

# 1. ソース・バケットの作成
レプリケーション元となる東京リージョンで、バケットを作成します。
デフォルト・ストレージ層は[標準]を選択します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2357379/7a7f9a6b-93a6-a32d-5d95-8eaa20b1fef2.png)

# 2. 宛先バケットの作成
レプリケーション先となる大阪リージョンで、バケットを作成します。
デフォルト・ストレージ層は[アーカイブ]を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2357379/0ed6bfe3-4c9d-36ec-4764-7937d9f386ef.png)

# 3. レプリケーション・ポリシーの作成
先程作成した、ソース・バケット[bucket_tokyo_source01] の詳細画面から、レプリケーション・ポリシーを作成します。
宛先リージョンは[Japan Central(Osaka)]で、宛先バケットに先程大阪リージョンに作成した、[bucket_osaka_target01]を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2357379/3f610942-df37-528b-2a38-ea20b10234f6.png)

レプリケーション・ポリシーが作成され、ステータスがActiveになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2357379/72094700-4ac4-83df-8a07-3843e9a5e5c7.png)


# 4. 検証
ここまでで必要なリソースの作成と設定が完了したので、ファイルをアップロードして動作を確かめてみます。
今回は4k画質の画像ファイル(4.96MiB)をアップロードしてみます。

東京リージョンのソースバケットに画像ファイルをアップロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2357379/401e243a-f941-191b-6f9f-330428be7bed.png)

アップロードされました。ソース・バケットのストレージ層は[標準]です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2357379/6a624fde-ccf9-4844-4426-d613932bcbaf.png)

では、大阪の宛先バケットを確認してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2357379/daf62200-448d-72ba-757a-259c26b13943.png)

無事にアーカイブ層としてレプリケーションされていました。
レプリケーションは非同期ですが、今回はファイルサイズが小さかったのもあってか数秒でレプリケートされていますね。

もともとクロスリージョンレプリケーションも簡単に構成はできますが、
宛先としてアーカイブ・ストレージを使用した場合も簡単に構成することができることがわかりました。

# 参考
[レプリケーションの使用](https://docs.oracle.com/ja-jp/iaas/Content/Object/Tasks/usingreplication.htm)
[アーカイブ・ストレージの概要](https://docs.oracle.com/ja-jp/iaas/Content/Archive/Concepts/archivestorageoverview.htm)
[OCI技術資料 : オブジェクト・ストレージ 概要](https://speakerdeck.com/ocise/ociji-shu-zi-liao-obuziekutosutorezi-gai-yao)
【LIFF】LIFF Inspectorを使ってローカル開発環境で実機デバッグする方法

## 概要

LIFFで開発をしている際に、ブラウザで動作確認をしても正常に動いているけど、

実機で確認すると、エラーが出る。。。といった場合に、

「LIFFを実機で動かしてその内容をデバッグをしたい。」という時のデバッグ方法を記載します。

LIFFプラグインのLIFF Inspectorを使います。

## LIFF Inspectorとは
>LIFF Inspectorは、LIFFアプリをデバッグするためのLIFFプラグインです。LIFF Inspectorを使うと、LIFFアプリを実行している端末とは別のPC上のChrome DevTools (opens new window)を使って、LIFFアプリをデバッグできます。

引用：https://developers.line.biz/ja/docs/liff/liff-plugin/#liff-inspector


## 前提

基本的には、LIFF InspectorのREADME通りに進めればデバッグができるのですが、
最後の「HTTPS で LIFF Inspector Server を起動する」で既に、
ngrokをLIFFのフロントを立ち上げているローカルサーバで使用している場合、
どのようにするか困った為、おすすめのツールと、具体的な方法を記載します。

[ngrok](https://ngrok.com/)と[localtunnel](https://github.com/localtunnel/localtunnel)というツールを使用します。

## 方法（一例）

#### 1,フロントのlocalhostにhttpsでアクセスできるようにする(localtunnel)

```sh
$ yarn dev
yarn run v1.22.19
$ vite

  VITE v4.4.6  ready in 1359 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h to show help
```

```sh
$ npx localtunnel --port 5173
your url is: https://full-pears-study.loca.lt
```


#### 2,HTTPSでLIFF Inspector Server を起動する（ngrok）

```sh
$ npx @line/liff-inspector
Debugger listening on ws://192.168.11.3:9222

You need to serve this server over SSL/TLS
For help, see: https://github.com/line/liff-inspector#important-liff-inspector-server-need-to-be-served-over-ssltls
```

LIFF Inspector Plugin を LIFF アプリにインストールする

```sh
$ npm install @line/liff-inspector
```

liff.initの前にliff.useでLIFF Inspectorを読み込む
```typescript
import liff from '@line/liff';
import LIFFInspectorPlugin from '@line/liff-inspector';

liff.use(new LIFFInspectorPlugin());

const initializeLiff = async () => {
  await liff.init({ liffId: import.meta.env.VITE_LIFF_ID || "" });
  if (!liff.isLoggedIn()) {
    liff.login();
  }
};
```

ngrok を使い LIFF Inspector Server を外部公開する
```sh
$ ngrok http 9222
```

下記のコマンドを実行して表示されるngrokのURLをコピーする（wss://~）
```sh
$ node -e "const res=$(curl -s -sS http://127.0.0.1:4040/api/tunnels); const url=new URL(res.tunnels[0].public_url); console.log('wss://'+url.host);"

↓
wss://sample.ngrok-free.app
```

#### 3,LINE Developperコンソールの該当のLIFFのエンドポイントを変更する。
```
エンドポイントURL：  フロントのlocalhostのHTTPSのURL + ?li.origin= + LIFF 
Inspector ServerのHTTPSのURL

例：https://sample.loca.lt?li.origin=wss://sample.ngrok-free.app
```


LIFFURLに実機でアクセスし、
下記のようにDevTools URLがアクセス時に表示され、
DevTools URLを開くとデベロッパーツールが表示されるので、
そこで実機でのデバッグ内容が表示されれば成功です。

```sh
Mac-Book-Air:LINEDC_LIFF_study yuuki$ npx @line/liff-inspector
Debugger listening on ws://192.168.11.3:9222

You need to serve this server over SSL/TLS
For help, see: https://github.com/line/liff-inspector#important-liff-inspector-server-need-to-be-served-over-ssltls

connection from client, id: 123456789-TESTEST
DevTools URL: devtools://devtools/bundled/inspector.html?wss=65b5-2404-7a85-c940-8500-6dc2-2939-326a-5798.ngrok-free.app/?hi_id=2000193345-NRjvXBmV
```

## 参考文献

この記事は以下の情報を参考にして執筆しました。

- [liff-inspector/README_ja.md](https://github.com/line/liff-inspector/blob/main/README_ja.md)
Bitbucket Pipelinesでgit cloneする

# 前提

走り書きです。
スクショやbitbucket-pipelines.ymlの書き方はまた追々…

- リポジトリA
- リポジトリB
の2つのリポジトリがあり、リポジトリAでPipelinesを実行するときに、リポジトリBをcloneしたい。
（sub-modulesでの利用ではなく）

# リポジトリAの設定

1. `リポジトリの設定/パイプライン/設定`　ページへ
1. `Enable Pipelines`を有効にする
1. `リポジトリの設定/パイプライン/SSH Keys`　ページへ
1. `Generate Keys`　ボタンを押下して生成
1. `Copy public key`　ボタンを押下してコピーする


# リポジトリBの設定

1. `リポジトリの設定/セキュリティ/アクセスキー`　ページへ
1. `Add key`　ボタンを押下しコピーした公開鍵を貼り付け、`label`は`pipelines`などとして、`Add SSH key`を押下


# リポジトリAでPipelinesを実行

無事cloneできる
[Go]ポインタ

# 概要

* [前回](https://qiita.com/hasesiu/items/e488c1e8d9c73f2c7c44)の続き
* 今回はポインタについて以下を学習
    * 変数からポインタの参照方法や、ポインタから値の参照法について
    * ポインタとミュータブルの関係について
    * マップやスライスが値渡しでない理由

参考

https://www.oreilly.co.jp/books/9784814400041/

# ポインタについて

* ポインタは参照先のアドレス値のこと。
* リテラル値とは別のメモリ空間で管理されている
* C言語とは異なり、ポインタの操作はデフォルトでは許可されていない

# ポインタ値の参照、逆参照

* ポインタ値は、`&変数`で取得する
* ポインタ値が参照している実際の値は、`*ポインタ変数`で取得する
* ポインタ型の変数をvarで宣言するには`*型`で行う
* ポインタの参照先がnilの場合、値は設定できない。そのため、ゼロ値と値なしを区別する際に利用することもある。（MapのカンマOkイディオムを使うほうがよいが。）

```go
	x := "hello"
	ponterX := &x //ポインタ値の取得
	x2 := *ponterX // ポインタ値から値の取得（逆参照）
	fmt.Println(ponterX) //0x1400010a230（アドレス）
	fmt.Println(x2) // hello
	
	var pointer *int // ポインタ型の宣言。初期値nil
	fmt.Println(pointer) // nil
	//fmt.Println(*pointer) // パニック(nilの参照先はNG）
```

# ポインタを利用することでミュータブルになる

* 基本形や構造体は値渡しであるが、ポインタ値を渡すと、アドレスが渡されることになる。
* つまり、ポインタの参照先の値を変更すると、呼び出し先での変更が呼び出し元に反映される。
* なお、ポインタ渡しの場合もポインタ値のコピーを渡しているため、ポインタ値を変更しても呼び出し先の値は変わらない。

```go
func main() {
	val := 10
	mutableFunc(&val) // ポインタ（アドレス）を渡す
	fmt.Println("main",val) // 更新される→1000
}

func mutableFunc(pointer *int) {
	*pointer = 1000 // ポインタ（アドレス）参照先の値を変更
	fmt.Println("func",*pointer) // 1000

	// 以下の場合は変更されない
	//val := 1000
	//pointer = &val	// ポインタ（アドレス）を変更しているので元の値は変更されない。 
}
```

# マップとスライスの受け渡し

関数にマップやスライスを渡した場合、以下の動作となる。
* マップについては、構造体へのポインタのコピーを渡しているため、呼び出し元のマップも変更される。
* スライスについては、**内容は変更できるが、サイズは変えられない。**
    * スライスが渡されると、「サイズ」、「キャパシティ」、「ポインタ」のコピーが作成される。
    * スライス内の値を変える場合、ポインタの参照先の値が変わるため、呼び出し元へ反映される
    * サイズとキャパシティの変更の場合は、呼び出し元のスライスは変更されない。
        * この場合、新しいスライスが作成され、ポインタも別の値となる。

# 所感

* Goにおけるポインタの文法や、動作については理解できた
* しかし、その必要性についてはよくわからなかった。
    * 元々、C言語にはあったが、難解かつ値の状態が追いづらくなるため、Java等で採用されなくなったと記憶している。
    * なぜGo言語でポインタの概念が復活したのか。
    * ガベージコレクションの効率化や、Json、インターフェース利用時に必要となるらしいので、後々理解できるか。。
* ポインタについては後の単元を学習した後、再度復習したほうがよさそう。
データベースの基礎

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/ae32e338-0e61-ba23-b86a-a10d76ee94b4.png)
　データベースは、データを蓄えてさまざまなアプリケーションからアクセスできるようにする仕組みです。データベースを利用して、データを一元管理することで、古いデータがどれで、新しいデータがどれかといったような混乱がなくなりますし、同じデータがいくつも記録されている効率の悪いこともなくなります。また、データの機密保持もしやすくなります。

１：データベースの目的
　データを一元管理することが目的です。

①独立性の確保：アプリケーションごとにデータファイルを持つ必要がなくなり、アプリケーションの変更がデータに影響しなくなる。

②整合性の確保：さまざまなアプリケーションでデータを共有する小tが容易になる。新しいデータと旧データが混在することを防ぐ。

③データの機密保持：一貫したアクセス制御を行える。
FileMaker Proで特許情報取得APIを使ってみる

## はじめに
特許情報取得APIってのは、特許庁が提供してくれてる特許情報を取得するためのなんかすごいやつです。じゃあ、FileMakerで特許案件を管理するのに使いたいですね。APIを使うには、cURLオプションっていうのを使うらしい。よーしやってみよう。

## API利用登録
特許庁のAPI情報提供サイト「利用の手引き」をみて、メールで申し込もう。数日かかるよ。
ID、パスワード、トークン取得パスがもらえます。

https://ip-data.jpo.go.jp/pages/top.html

## スクリプト例 (簡易版)

特願2000-123456のJ-PlatPatの固定アドレスを得て、そのページを表示するスクリプト。

目的は、特許情報(固定アドレス)を得ること。そのためにはアクセストークンが必要。アクセストークンは、IDとパスワードを使って指定されたURL(トークン取得パス)にアクセスするともらえる。アクセストークンを持って、欲しい情報ごとに決まったURLにアクセスすると、目的の特許情報を得られる。
このアクセストークンは1時間有効。リフレッシュトークンを使うと8時間以内ならアクセストークンを再取得できる。以下のスクリプトは、トークンの有効時間を全く考えていない、という意味で簡易版です。エラー処理もしてないしな。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1140682/c1a919f8-b95a-2b4f-96b2-682ef4fd6497.png)

<details><summary> 「テキストを挿入」の詳細</summary>

```
テキストを挿入(アクセストークンを取得)
-X POST -H "Content-Type: application/x-www-form-urlencoded" --data-urlencode "grant_type=password" --data-urlencode "username=__ID__" --data-urlencode "password=__pass__"
```

```
テキストを挿入(特許情報を取得)
-X GET -H "Authorization: Bearer __access_token__"
```

```
おまけ：テキストを挿入(リフレッシュトークンを使ってアクセストークンの再取得)
-X POST -H "Content-Type: application/x-www-form-urlencoded" --data-urlencode "grant_type=refresh_token" --data-urlencode "refresh_token=__refresh_token__"
```

※cURLオプションは、「利用の手引き」に書いてあったcURLコマンドを、以下のURLの動画を参考に修正したものです
https://www.youtube.com/watch?v=4c3_S0XYyFk

</details>


## 実行結果
スクリプトを実行すると、J-PlatPatの「特願2000-123456」のページにアクセスできた！わーい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1140682/a4e9974c-e175-7461-b553-7fe6cee06d78.png)

## APIの種類
URLを変えると得られる特許情報も変わります。URLはこちら: [特許情報取得API](https://ip-data.jpo.go.jp/api_guide/api_reference.html)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1140682/e6ba3cbc-9fd8-be26-c532-931327c35bcd.png)
得られた情報でなんじゃこりゃっていうのがあったら特許情報標準データの仕様書を見に行こう。
例えば引用文献の番号はJPA 430123456って返ってくる。公報を見慣れた方なら、なんじゃこりゃと感じる番号ですね。これを「07_引用文献マスタ」を見て解読すると、JPA 430123456のうち、430は平成30年を示していて、特開2018-123456だとわかるよ！

https://www.jpo.go.jp/system/laws/sesaku/data/keikajoho/index.html

## まとめ
わーいいろいろ遊べそう。ただし、まだお試し期間中のようです。上限アクセス回数がAPIごとに30~400回くらいになっているので、使い過ぎに注意。そして、アクセストークンの有効時間はまったく考慮してないので、その辺はリフレッシュトークンを使って、うまいことあそんでね。エラー処理もしよー。

さらに、外国案件の情報にアクセスできるAPIもあるみたい。それはOPD-API、ワン・ポータル・ドシエを通じて外国庁で提供されている五大特許庁（日本国特許庁、米国特許商標庁、欧州特許庁、中国国家知識産権局、韓国特許庁）の審査経過情報が得られるってものです。そちらは利用申込の上限に達してしまって登録できない状態です(2023年8月時点)。また登録できたら遊んでみよー！

※ ふわふわ知識で書いているので、間違っているとこあればご指摘くださいませ！
【Python】クラスについて

# 【Python】クラスについて


<h3>クラスとはオブジェクトの設計図のこと</h3>

どの様なデータ（属性）を持ち、どのような動作（メソッド）にするか定義するものがクラス。
属性　＝　どのようなモノにするか（おもちゃだと、色・大きさ。。）
メソッド　＝　どのような動きをするか（歩く・しゃがむ・走る。。。）
設計図を元におもちゃを作る。
クラス（設計図）を元にインスタンス化（おもちゃを完成させる）する。



```py
class クラス名:
    def メソッド名(self, 引数,...):
        # 処理

    def メソッド名(self, 引数,...):
        # 処理
```
クラスが持っている関数は、`メソッド`と言います。
`メソッド`を書く時は、必ず最初の引数は`self`を書いてください。
selfにはクラスから作られた実態が渡されます。
selfは自分自身(インスタンス)という意味になります。
インスタンス変数は、メソッドの中で定義します。


```py 
    # クラス変数
    変数名=値
    def__init__(self, 引数,...):
      
       self.インスタンス変数名 = 引数   

  
    def メソッド名(self, 引数,...):
        # 引数
```
`変数名=値はクラス変数`と言います。クラスで共通した値を持つクラス変数です。
`__init__`は「コンストラクタ」又は、「イニシャライザ」と呼ばれます。
初期化するものです。
これは、インスタンス作成時に1度だけ呼び出されるメソッドです。
クラスが持っている変数「インスタンス変数」に、値を代入するのに使います。

インスタンス変数とクラス変数を`属性`と言います。


クラスのインスタンス生成方法

```py
インスタンス名 = クラス名(引数1, ...):
```

引数1は、イニシャライザに渡す引数

インスタンスからメソッドを呼び出す方法

```py
インスタンス = メソッド名(引数1, ...)
インスタンス.インスタンス変数名
インスタンス.クラス変数名
```
ChatGPTにPythonのインスタンス変数とは？と質問した答え

Pythonのクラス内で定義された変数は、そのクラスのインスタンスごとに異なる値を持つことができる変数です。これらの変数は「インスタンス変数」と呼ばれます。インスタンス変数は、各インスタンスごとに固有の属性を持たせるために使用されます。

以下は、Pythonでインスタンス変数を使用する例です：
```py:参考に…
class Person:
    def __init__(self, name, age):
        self.name = name   # インスタンス変数
        self.age = age     # インスタンス変数

# Personクラスのインスタンスを作成
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)

# インスタンス変数にアクセス
print(person1.name)  # 出力: Alice
print(person2.age)   # 出力: 25
```
上記の例では、Person クラスのインスタンス変数として name と age を定義しています。これにより、person1 インスタンスと person2 インスタンスはそれぞれ異なる name と age を持つことができます。

インスタンス変数は、self キーワードを使用してクラス内のメソッドからアクセスできます。上記の例では、__init__ メソッドで self.name と self.age がインスタンス変数として設定されています。

インスタンス変数はクラス内のどのメソッドからもアクセス可能であり、インスタンスごとに個別の値を保持することができます。
Javascriptにおけるデコレータとは？

JavaScriptにおけるデコレータは、関数やクラス、メソッドなどの動作を修飾するための仕組みです。デコレータは、他の関数やクラスに適用され、その処理を変更、拡張、またはラップするために使用されます。これにより、コードの再利用性やメンテナンス性を向上させることができます。

デコレータは、通常は関数として定義され、対象となる関数やクラスの定義の前に「@デコレータ名」の形式で記述されます。デコレータが適用された対象に対して、あらかじめ定義された処理が自動的に適用されるという仕組みです。

### JavaScriptにおけるデコレータのサンプル

```javascript
function logDecorator(target, name, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args) {
    console.log(`Calling ${name} with arguments: ${args}`);
    const result = originalMethod.apply(this, args);
    console.log(`${name} returned: ${result}`);
    return result;
  };
  return descriptor;
}

class Calculator {
  @logDecorator
  add(a, b) {
    return a + b;
  }
}

const calculator = new Calculator();
console.log(calculator.add(2, 3)); // This will log the arguments and result of the add method.
```

上記の例では、`logDecorator` デコレータが `add` メソッドに適用されています。デコレータはメソッドが呼び出されるたびに、メソッドの引数と戻り値をログ出力する処理を追加します。

デコレータは、ログ出力以外にも、認証、キャッシュ、パフォーマンス計測などの用途に使用されることがあります。一般的には、コードの再利用性と可読性を高めるために、特定の横断的な機能を関数やクラスに適用する際に利用されます。

‌

### Decorator におけるdescriptorとは？

デコレータにおける `descriptor` は、JavaScriptで関数やメソッドのプロパティを表すオブジェクトです。`descriptor` オブジェクトは、特定のプロパティの属性や動作を定義します。

`descriptor` オブジェクトは、通常以下のプロパティを持ちます。

1. `value`: プロパティが関数の場合、関数本体がここに格納されます。
2. `writable`: プロパティが書き込み可能かどうかを示すブール値です。
3. `enumerable`: プロパティが列挙可能かどうかを示すブール値です。
4. `configurable`: プロパティが変更可能かどうかを示すブール値です。
5. `get`: ゲッターメソッドを指定します。
6. `set`: セッターメソッドを指定します。

デコレータを使用する際に、デコレータ関数は通常、デコレートされる対象（関数やメソッド、クラス）の動作を変更するために `descriptor` オブジェクトを受け取り、そのプロパティを操作します。特にメソッドデコレーションの場合、`descriptor.value` に元のメソッドの関数本体が格納されており、デコレータはこの関数を変更、拡張、または置き換えるために `descriptor.value` を操作します。

先ほどの例でも示したように、デコレータが関数やメソッドの処理を変更する場合、デコレータ関数は `descriptor` オブジェクトを引数として受け取り、その中のプロパティを操作して新しい動作を定義します。
URL、FQDN、ホスト名、ドメイン名

## わかっているようでわかっていない用語

URL
https://www.xxx.com/index.html

FQDN
www.xxx.com

ホスト名
www

ドメイン名
xxx.com

「ホスト名＋ドメイン名」＝「FQDN」

https://itmanabi.com/fqdn-url/
UniRxでuGUIのドラッグ&ドロップの実装

# コード

色々な実装方法があったのですが，自分なりに一番簡単なものを備忘録として書き留めます

この三行だけ

```c#
trigger.OnDragAsObservable()
    .Subscribe(e => transform.position = e.pointerCurrentRaycast.worldPosition)
    .AddTo(this);
```

また，汎用性を上げるためにドラッグ&ドロップの中止ができるようにする

```c#
private bool isDraggable = true;

trigger.OnDragAsObservable()
    .TakeWhile(_ => isDraggable)
    .RepeatUntilDestroy(this)
    .Subscribe(e => transform.position = e.pointerCurrentRaycast.worldPosition)
    .AddTo(this);
```

テストとしてドラッグ開始から1秒後に停止してみる

```c#
trigger.OnBeginDragAsObservable()
    .SelectMany(_ => Observable.Timer(TimeSpan.FromSeconds(1)))
    .RepeatUntilDestroy(this)
    .Subscribe(_ => isDraggable = false)
    .AddTo(this);
```
SAP on Azure デプロイ自動化フレームワーク - SAPシステムの作成（仮想マシン作成）

# ブログ内容
私のブログを参考にして構築する場合は、赤点線枠のリソースが作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3521211/2dfa633c-b0e2-176d-1afb-6ba97623ca31.png)
作業内容として、以下作業を前回用意したコントロールプレーン側から実施して仮想マシンを作成していきます。

+ Terraformの変数ファイル（.tfvars）の修正
+ 仮想マシン作成

# 実施内容及び、作成されるリソースについて
Terraformを利用して仮想マシンを作成します。
作成されるリソースは以下になります。

■ 仮想マシン（計5台）
+ PAS：Primary Application server 
+ AAS：Additional Application Server
+ ASCS：ABAP Central Services
+ WDP：Web Dispatcher
+ DB：Database Server

■ その他
+ 近接配置グループ×2（ゾーン1用、ゾーン2用）
+ 可用性セット×2（ゾーン1用、ゾーン2用）
+ ロードバランサー×3（AP用、DB用、Web用）
+ アプリケーションセキュリティーグループ×3（AP用、DB用、Web用）
+ ストレージアカウント（sapmnt用）
+ プライベートエンドポイント（sapmnt用）

:::note info
今回は、デフォルト設定と同じ構成で作成します。
:::

# Terraformの変数ファイル（.tfvars）の修正
SAPシステムの仮想マシンを作成する際に利用するファイルは「DEV-JAEA-SAP01-X00.tfvars」になります。
以下URLを確認し必要なパラメータの修正や追加をしていきます。
※ ファイルの配置については前回のブログで用意しているので、項目を省いています！

https://learn.microsoft.com/ja-jp/azure/sap/automation/configure-system

<details><summary>今回DEV-JAEA-SAP01-X00.tfvarsで修正した項目</summary>

```rb
environment = "DEV"
location = "japaneast"
scs_server_use_ppg = false
application_server_use_ppg = false
webdispatcher_server_use_ppg = false
```
</details>

# 仮想マシン作成
作成する際に利用するコマンドは以下になります。
サンプルコマンドを書き換えて実行します。

<details><summary>サンプルコマンド</summary>

```rb
az logout
az login --service-principal -u "appId" -p "password" --tenant "xxxxxxxxxx"
cd ~/Azure_SAP_Automated_Deployment/WORKSPACES

export subscriptionId="xxxxxxxxxx"
export         spn_id="サービスプリンシパルのappId"
export     spn_secret="サービスプリンシパルのpassword"
export      tenant_id="サービスプリンシパルのテナントid"
export       deployer_env_code="DEMO"
export            sap_env_code="DEV"
export    region_code="JAEA"
export DEPLOYMENT_REPO_PATH="${HOME}/Azure_SAP_Automated_Deployment/sap-automation"
export CONFIG_REPO_PATH="${HOME}/Azure_SAP_Automated_Deployment/WORKSPACES"
export SAP_AUTOMATION_REPO_PATH="${HOME}/Azure_SAP_Automated_Deployment/sap-automation"
export ARM_SUBSCRIPTION_ID="${subscriptionId}"
export ARM_TENANT_ID="${tenant_id}"
export ARM_CLIENT_ID="${spn_id}"
export ARM_CLIENT_SECRET="${spn_secret}"

cd ~/Azure_SAP_Automated_Deployment/WORKSPACES/SYSTEM/DEV-JAEA-SAP01-X00

${DEPLOYMENT_REPO_PATH}/deploy/scripts/installer.sh          \
        --parameterfile DEV-JAEA-SAP01-X00.tfvars           \
        --type sap_system           \
        --state_subscription "${subscriptionId}"     \
        --deployer_tfstate_key "${deployer_env_code}-${region_code}-DEP00-INFRASTRUCTURE.terraform.tfstate" \
        --landscape_tfstate_key "${sap_env_code}-${region_code}-SAP01-INFRASTRUCTURE.terraform.tfstate" \
        --auto-approve
```
</details>

実行に20分くらい時間がかかります。
結果ですが、作成の途中で赤文字のエラーが表示されていなければ問題なくリソースが作成できています。
リソースグループ「DEV-JAEA-SAP01-X00」が作成されており、それに紐づくリソースも作成されていることが確認できます。

# 所感
今回の検証では、以下分かりました。

+ 「scs_server_use_ppg = true」、「application_server_use_ppg = true」、「webdispatcher_server_use_ppg = true」の状態では、仮想マシンの起動に失敗しエラーとなる。
+ 近接配置グループの設定を「false」にしても近接配置グループのリソースは作成される。


:::note info
SAPシステムの構成については別構成verを弊社ブログで投稿予定です。
その際に、リソース作成の流れや仕組みをもう少し深堀した形で、情報をお届けできたらなと思います。
:::



# 次回予告
SAPをインストールするため、インストールメディアを準備します。
次回「BOMファイル準備＆配置」の記事を投稿します！
DHCPリレーエージェントが必要な理由

## DHCP DISCOVER
DISCOVER自体は、ブロードキャストなのでセグメントをまたぐことができない。
そのためにルータにリレーエージェントの設定が必要になる。

giaddr勉強になった。
この情報があるため、DHCPサーバが割り振るクライアントのIPアドレスのセグメント情報がわかる→適切なIPアドレスが払い出される。

https://milestone-of-se.nesuke.com/l7protocol/dhcp/dhcp-relay-packet-format-and-capture/
Virtualenvが動かなかったのでpyランチャーとvenvで何とかした話

ある日、virtualenvを入れようとしたら… こうなった
```
python -m virtualenv venv --python 3.8.5
PEP-514 violation in Windows Registry at HKEY_LOCAL_MACHINE/PythonCore/3.11/InstallPath error
```

原因は存在しないレジストリキーを参照しようとしてエラーが出ている模様。

ここらへんも読んだけど解決には至らず

https://peps.python.org/pep-0514/

で、venvを代用しようと（この時点でホントはやりたくなかった）していたら以下を発見。

https://pi-trade.tech/programing-learning/python-venv-how-to/

※やりたくなかった理由は以下

[https://mycodingjp.blogspot.com/2018/12/python-venv-virtualenv.html#:~:text=一方で、virtualenv　は、Pythonのバージョン自身も管理できる。](https://mycodingjp.blogspot.com/2018/12/python-venv-virtualenv.html#:~:text=%E4%B8%80%E6%96%B9%E3%81%A7%E3%80%81virtualenv%E3%80%80%E3%81%AF%E3%80%81Python%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E8%87%AA%E8%BA%AB%E3%82%82%E7%AE%A1%E7%90%86%E3%81%A7%E3%81%8D%E3%82%8B%E3%80%82)

で、Pythonランチャーとvenvでvirtualenvの代用とすることにした。

参考

https://gammasoft.jp/blog/find-out-python-launcher/

手順

1. WindowsのPythonをインストール（MSStore版ではランチャーが入らなかった）

https://www.python.jp/install/windows/install.html

※バージョンは3.11.1

※パスを通すチェックは不要（ランチャー使いたいだけなので）

1. Powershellを開いて「py」と入力して実行してインタプリタが起動するのを確認

1. 仮想環境を作成する。対象のディレクトリで py -m venv [env_name] と入力
2. バージョンを指定して仮想環境を作りたい場合は py [-version] -m venv [env_name] と入力
    
    Ex: py -3.9 -m venv venv みたいな感じ
    
3. 仮想ディレクトリに入って python -V を入力して、指定のバージョンが入っていればOK。

windowsユーザーに限るけど、標準で使えるvenvをバージョン管理できるのでかなり便利になった。
BigQueryにGSSの日時型カラムをDATETIME型で読み込む

BigQueryではGoogleスプレッドシート（GSS）を読み込んでテーブルを作成することができます。
ですが、GSSの日時型カラムをBigQueryでDATETIME型としてそのまま読み込むとエラーが発生します。
この解決に時間がかかったので備忘として残します。

## 結論
GSS側で日時型カラムを**文字列型**`YYYY-MM-DD hh:mm:ss`に変換してからBigQueryに読み込めばOK


## エラー状況
例えば、次のようなGSSをBigQueryに読み込みたいとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341655/c47bec2c-b39b-c331-647f-85c8fcb938f1.png)

GSSをBigQueryに読み込むには、「テーブルを作成」において次のように指定します。
ポイントとしては、日付型カラムの読み込み先をDATETIME型で指定している点です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341655/143b5391-9859-3c93-ec66-b6e8ad362916.png)

このまま「テーブルを作成」ボタンを押した段階ではエラーは出ません。しかし、テーブルをSQL内で呼び出したときにエラーが吐き出されます。
```
Error while reading table: my_project.sample_dataset.sample_table, error message:
Could not convert value to datetime. Row 1; Col 0. 
File: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
このような挙動となるのは、「テーブルを作成」を押した段階ではGSSを参照するという設定をしただけであり、SELECT句を走らせたときに初めてGSS上のデータが実際に読み込まれるからです。

## 解決策
このエラーを回避するには、GSS側で日時型カラムを**文字列型**`YYYY-MM-DD hh:mm:ss`に変換する必要があります。

新規シートを作り、文字列型カラムを変換のように変換します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341655/40415ba5-2ca0-624f-afd5-23084dcea9b2.png)

ポイントは次の通りです。
- ARRAYFORMULA関数を使えば1番上のセルに関数を書くだけで済み、レコード追加にも対応できる。
- 日時型を文字列に変換するにはTEXT関数を使う。第２引数は```yyyy-mm-dd hh:mm:ss;;```とする。
末尾に```;;```を付けないと、空欄を0として認識して変換してしまいます。

【失敗例】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341655/93785cc4-1ea6-3cb1-1548-f46adcc5f91a.png)

日付型以外のカラムは、ARRAYFORMULA関数だけを適用すればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341655/f2ebfbe4-51a1-c65e-9184-641eee4bb99d.png)

以上のようにGSS側で変換を行えば、無事に日付型カラムをDATETIME型で読み込むことができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341655/a64b83b8-4bd1-dc8f-579d-50b9c353439e.png)
NextjsをFirebaseにデプロイするとTypeError: Cannot read properties of null (reading 'endsWith')

# はじめに

Nextjsを初めてFirebaseにデプロイする際につまづいたのでまとめます
よく起こりそうなイメージですが、ネットにすぐ解決できる記事が見当たりませんでした

# 問題

```
$ firebase deploy
```

をすると以下のエラーが発生しました

```
[debug] [2023-08-15T12:51:24.334Z] TypeError: Cannot read properties of null (reading 'endsWith')
    at ɵcodegenPublicDirectory (/home/watanabejin/.cache/firebase/tools/lib/node_modules/firebase-tools/lib/frameworks/next/index.js:206:56)
    at async prepareFrameworks (/home/watanabejin/.cache/firebase/tools/lib/node_modules/firebase-tools/lib/frameworks/index.js:294:13)
    at async deploy (/home/watanabejin/.cache/firebase/tools/lib/node_modules/firebase-tools/lib/deploy/index.js:53:13)
[error] 
```

# 解決方法

`firebase-tools`が古かったのでアップデートしたら治りました

```
$ sudo rm /usr/local/bin/firebase
$ sudo npm install -g firebase-tools
```

# おわりに

Nextjsだと無料枠ではできないようでした
どれくらい課金されるのか不安ではありますが、引き続き開発を進めていきます
パラメータストアに登録されている値をTerraform内で取得し活用する

# はじめに
あらかじめパラメータストアに登録されている秘匿情報を用いてTerraformでAWS環境を構築する、という事は多々あると思います。
公式ドキュメントに記載があるのですが、具体的な例を交えた端的な活用方法が余り見当たらないので、残しときます。

# RDSのユーザー名、パスワードを登録する
以下の様なパラメータがパラメータストアに登録されています。
* `/dev/rds/username`: データベースユーザー名
* `/dev/rds/password`: データベースパスワード

[Twelve-Factor Appの3.設定](https://12factor.net/ja/config)に従がって環境毎にパラメータを分けたい為、[階層パラメータ](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-paramstore-hierarchies.html)を用いて先頭に環境名(`/dev`)を付けています。

# パラメータを取得しRDSを構築する
いきなりTerraformのコードです。
```tf:main.tf
# 環境はvar.envで切り替える。今回は var.env=dev
# parameter store
data "aws_ssm_parameter" "db_username" {
  name = "/${var.env}/rds/username"
}
data "aws_ssm_parameter" "db_password" {
  name = "/${var.env}/rds/password"
}

# RDS Setting
resource "aws_db_instance" "db" {
  db_name           = "sample_db"
  identifier        = "sample-${var.env}-db"
  allocated_storage = 20
  storage_type      = "standard"
  engine            = "mysql"
  engine_version    = "5.7"
  instance_class    = "db.t3.small"
  username          = data.aws_ssm_parameter.db_username.value # ココで呼ぶ！
  password          = data.aws_ssm_parameter.db_password.value # ココで呼ぶ！
...略
}
```
パッとみて分かるものがないな、と思ったので記事にしました。
オープンソースアプリフレームワークの "Skeet" 開発コミュニティ が Solana ブロックチェーンのバリデーターになりました

![SkeetSolanaValidator.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2780317/515474e1-6558-0c3d-db4e-4c2e293e49a6.png)

gm! ELSOUL LABO B.V. Co-founderのkishi.solです。

普段開発しているオープンソースアプリフレームワーク "Skeet" の開発コミュニティが Solana ブロックチェーンのバリデーターになったことを報告させていただきます。

Skeet は GCP (Google Cloud) と Firebase 上にフルスタックアプリを構築できるオープンソースの TypeScript 製サーバーレスフレームワークです。

API サーバーから Web・iOS・Android アプリまでを TypeScript で一貫して超速開発することができます。

Skeet は Solana ブロックチェーン を活用した dApps や Solana Mobile Stack の Web3 モバイル dApp 開発にも対応しています。

この度、Skeet 開発コミュニティは Solana ブロックチェーンのバリデーターとなりました。

Solana Validators (Skeet Framework): https://www.validators.app/validators/EmmaLKkBBjvwqdL38cC5nv7SQunQr8g1iL9JeZ2bAk2D?locale=en&network=testnet

## Solana バリデーターとしての Skeet の役割とビジョン
![SkeetSolanaValidatorDetail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2780317/02951111-6646-e20e-3f8a-19bb87a351b1.png)


Solana ブロックチェーンはその高速性と低い取引コストで注目されており、多くのプロジェクトがこのプラットフォームを選択しています。Skeet チームは Solana のバリデーターとして活動することで、ブロックチェーンネットワークの安全性や効率性を高めるとともに、トランザクションの承認やブロックの生成に参加します。

この役割を通じて、Skeet チームの Solana に対する知識や技術的な理解はより一層深まります。これは、Solana を活用した Web3 やブロックチェーンの開発で、より革新的でユーザーフレンドリーなフレームワークの提供を意味します。Skeet の開発者は、これらの知識を活かしてフレームワークの機能を充実させ、Web3 アプリ・ dApps の開発者にとって魅力的な選択肢として Skeet を位置付けることを目指しています。

さらに、オープンソースとしての Skeet の哲学は、技術の民主化と共有を重視しています。このバリデーターとしての役割は、ブロックチェーン技術の普及と共有を推進する大きなステップであり、コミュニティ全体の成長と発展を促進するものと考えています。

最後に、Solana バリデーターとして得られる報酬は、Skeet フレームワークのさらなる開発やコミュニティの運営に役立てられます。これにより、Skeet はより持続可能で、成長し続けるオープンソースコミュニティとしての立場を強化することができます。Skeet チームは、この新たな取り組みを通じて、ブロックチェーン技術の未来を共に築いていくパートナーシップを広げていくことを心より楽しみにしています。

Solana 公式ウェブサイト: https://solana.com/

Skeet 公式ドキュメント: https://skeet.dev/ja/
Azure SQL DB へ Data Factory から接続する with Managed ID

# やりたいこと
Azure の SQL Server/DB に Data Factory の System Assigned Managed ID で接続する方法をいろいろと調べて実現できたので、その内容をまとめます。

# 前提
* Azure のリソースは Terraform で作成する
* SQL Server の管理者として AAD で作成したグループを登録する
    * 管理者としてアクセスできないと設定できないので

# Terraform コード (参考)
実際のコードを一部改変しているため、正しく動作するかは未検証。参考程度に。

``` main.tf
# Resource Group
resource "azurerm_resource_group" "my_rg" {
  name = "rg-aad-connection"
  location = "Japan East"
}

# AAD Group
resource "azuread_group" "grp_admin" {
  display_name     = "grp-aad-connection-admin'
  owners           = "user1@sample.co.jp"
  members          = "user1@sample.co.jp"
  security_enabled = true
}

# SQL Server
resource "azurerm_mssql_server" "sql" {
  name                          = "sql-aad-connection"
  resource_group_name           = azurerm_resource_group.my_rg.name
  location                      = azurerm_resource_group.my_rg.location
  version                       = "12.0"
  administrator_login           = "sqladmin"
  administrator_login_password  = "YOUR_SQLADMIN_PASSWORD"

  azuread_administrator {
    azuread_authentication_only = false
    login_username              = "AzureAD Admin"
    object_id                   = azuread_group.grp_admin.object_id
  }
}

# SQL DB
resource "azurerm_mssql_database" "sqldb" {
  name                        = "sqldb-aad-connection"
  server_id                   = azurerm_mssql_server.sql.id
  auto_pause_delay_in_minutes = 60
  collation                   = Japanese_CI_AS
  max_size_gb                 = 2
  min_capacity                = 1
  read_scale                  = false
  sku_name                    = "GP_S_Gen5_2"
  storage_account_type        = "Local"
  zone_redundant              = false
}

# Data Factory
esource "azurerm_data_factory" "adf" {
  name                = "adf-aad-connection"
  resource_group_name = azurerm_resource_group.my_rg.name
  location            = azurerm_resource_group.my_rg.location

  identity {
    type = "SystemAssigned"
  }
}
```

SQL Server のリソース定義の `azuread_administrator` ブロックによってサーバ管理者を設定している。
デフォルトだと、SQL 認証 (sqladmin) とこの AAD グループしか DB にログインできないので Data Factory から接続するには、Managed ID を DB のユーザとして作成する必要がある。

# SQL DB 上での AAD ユーザ作成とロール割当
以下の公式ドキュメントを参考に行った。

https://learn.microsoft.com/ja-jp/azure/data-factory/connector-azure-sql-database?tabs=data-factory#managed-identity

DB へ接続して以下のクエリを実行する (`db_datareader` は冗長かもしれない)。

```
# ユーザ作成
CREATE USER [adf-aad-connection] FROM EXTERNAL PROVIDER;

# ロール割当
ALTER ROLE db_datareader ADD MEMBER [adf-aad-connection];
ALTER ROLE db_datawriter ADD MEMBER [adf-aad-connection];
ALTER ROLE db_ddladmin ADD MEMBER [adf-aad-connection];
```

以上を行った後に、Data Factory Studio 上で、SQL DB への Linked Service を作成して、Test Connection してみると接続される！

# ということで
SQL DB へ Data Factory から Managed ID で接続しました。今回は Data Factory からでしたが、他のリソース (Web Apps や Functions App など) でも同様に接続できるので、パスワードの管理などに悩まなくて済みそうです。
RBAC とは別のレイヤでの話になるので最初は？となりましたが、通常の SQL DB でのユーザ管理と一緒ですね。このあたりも全部 Azure 上の設定で完結できると良いなと個人的には思います。


以上です。
URLパラメーターを直接変更されてアクセスされるのを防ぐ

あまりいい例ではないかもしれないですが…adminが数人いる場合

⚫︎編集画面のrouting

```php
Route::get('/{id}/edit', 'AdminController@edit')->name('admin.edit');
```

id = 1 のadminが、直接 /2/edit とURLを編集してアクセスしたときに、id = 2 のadminのデータを編集できてしまうとまずいですよね。
そのようにアクセスされた場合には404エラーを出すようにしたいと思います。

```php:AdminController.php
public function __construct()
{
    $this->middleware(function ($request, $next) {
        // URLで入力されているパラメーターを取得
        $requestId = (int)$request->route()->parameter('id');
        if (!is_null($requestId)) {
            // Auth::id()でログインしているidを取得
            $adminId = Auth::id();
            if ($requestId !== $adminId) {
                abort('404');
            }
        }
        return $next($request);
    });
}

public function edit (int $id)
{
	// hogehoge
}
```

コンストラクタで「コントローラミドルウェア」というものを使用します。

⚫︎参考URL
https://readouble.com/laravel/9.x/ja/controllers.html

> コントローラでは、クロージャを使用したミドルウェアの登録もできます。これにより、ミドルウェアクラス全体を定義せずに、単一のコントローラ用のインラインミドルウェアを便利に定義できます。
> 
- URLパラメーターのIDは文字列で取得
- Auth::id() で取得するIDはintで取得

という違いがあるので、URLパラメーターのIDをint型に変換してます。

あとは取得した2つのIDが違っていればabort(’404’)で404エラーを出します。