2025年12月28日日曜日

静的サイトジェネレーター Rspress の Quick start をやってみた

プロジェクト作成

node ➜ /workspaces/TIL/Rspress/firststep (rspress-firststep) $ npm create rspress@latest
Need to install the following packages:
create-rspress@1.47.0
Ok to proceed? (y) y


> npx
> create-rspress


  Create Rspress Project

  Project name or path
  rspress-firststep-project

  Select additional tools (Use <space> to select, <enter> to continue)
  Add Biome for code linting and formatting, Add ESLint for code linting, Add Prettier for code formatting

  Next steps ───────────────────╮

  cd rspress-firststep-project  │
  npm install                   │
  npm run dev                   │

├────────────────────────────────╯

  Done.

npm notice
npm notice New major version of npm available! 10.9.2 -> 11.7.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.7.0
npm notice To update run: npm install -g npm@11.7.0
npm notice

node ➜ /workspaces/TIL/Rspress/firststep (rspress-firststep) $ cd rspress-firststep-project/

以下の構成でファイルが生成される。

rspress-firststep-project
|-- README.md                       : Rspress プロジェクトの README
|-- biome.json                      : Biome 設定ファイル
|-- docs                            : ドキュメントルート
|   |-- _meta.json                  : ヘッダ情報が書いてある
|   |-- guide
|   |   |-- _meta.json
|   |   `-- index.md
|   |-- hello.md
|   |-- index.md
|   `-- public                      : 公開リソース置き場
|       |-- rspress-dark-logo.png
|       |-- rspress-icon.png
|       `-- rspress-light-logo.png
|-- eslint.config.mjs               : ESLint 設定ファイル
|-- package.json                    : npm 設定ファイル
|-- rspress.config.ts               : Rspress 設定ファイル
`-- tsconfig.json                   : TypeScript 設定ファイル

開発サーバーで実行

npm install
npm run dev

ブラウザで http://localhost:3000 にアクセスすると、生成されたページが表示される。

その他コマンド

その他、チェック、フォーマット、リント、プレビュー等のコマンドも定義されているので必要に応じて適宜使える。

node ➜ .../TIL/Rspress/firststep/rspress-firststep-project (rspress-firststep) $ npm run
Scripts available in rspress-firststep-project@1.0.0 via `npm run-script`:
  build
    rspress build
  check
    biome check --write
  dev
    rspress dev
  format
    prettier --write .
  lint
    eslint .
  preview
    rspress preview

ビルド

npm run build

doc_build に静的サイトが生成される。

Nginx にデプロイしてみる

docker 上の nginx にデプロイしてみる。

docker run -p 8081:80 -v $(pwd)/doc_build:/usr/share/nginx/html:ro nginx

ブラウザで http://localhost:8081 にアクセスすると、デプロイされたページが表示される。

参考資料

2025年12月13日土曜日

Yet another なガントチャート生成ツールを作った

CSV からガントチャートを生成する CLI ツール「ganttgen」を作りました。 この記事では、細かい仕様説明は省いて、実際に動かすところまでを紹介します。

始め方

実行バイナリのダウンロード

以下、 ganttgen の最新リリースページから、ご利用の OS に合わせた実行バイナリをダウンロードしてください。

https://github.com/mikoto2000/gantt-generator/releases/latest

CSV ファイルの準備

以下のような CSV ファイルを用意してください。ファイル名は sample.csv とします。

タスク名,開始,終了,期間,依存,実績開始,実績終了,実績期間,備考
#要件定義,,,,,,,,
タスク1,2025/12/1,,2d,,2025/12/11,2025/12/15,,"12/22 mikoto2000
まいった、終わらん
12/2 mikoto2000
病欠のため着手不能"
#設計,,,,,,,,要件定義は終わっていないが、決まったところから設計していく
タスク2,,,3d,タスク1,,,,"12/22 mikoto2000
テスト
12/2 mikoto2000
テテスト"
#製造,,,,,,,,着手未定
タスク3,,,4d,タスク2,,,,
#テスト,,,,,,,,着手未定
タスク4,,,5d,タスク3,,,,
タスク5,,,,,,,,着手未定

Excel 等の表計算ソフトで編集するのを推奨します。

See: https://github.com/mikoto2000/gantt-generator/blob/main/sample/sample.csv

祝日定義ファイルの準備

以下のような YAML ファイルを用意してください。ファイル名は sample_holiday.yaml とします。

# YAML で祝日を指定するサンプル。
# YYYY-MM-DD の文字列を配列で記述してください。
holidays:
  - 2024-06-10
  - 2024-09-16
  - 2024-09-23

See: https://github.com/mikoto2000/gantt-generator/blob/main/sample/sample_holiday.yaml

ガントチャートの生成・監視・再描画

前述の工程で用意した CSV ファイルと祝日定義ファイルを指定して、 ganttgen コマンドを実行してください。

ganttgen --livereload --holidays ./sample_holiday.yaml ./sample.csv -o sample.html

生成されたガントチャートは sample.html という名前で出力されます。ブラウザで開いて確認してください。

--livereload オプションを指定すると、CSV ファイルや祝日定義ファイルに変更があった場合に自動的にガントチャートが再生成されます。ブラウザのリロードは不要です。

参考資料

2025年12月8日月曜日

Vim のマクロで気持ち良くなった話 - Java POJO から SQL の INSERT 文を作る

SQL の INSERT 分の作成

こんな SQL を作りたい

MyBatis の書き方ですね。

INSERT INTO
TodoEntity
(
  id,
  title,
  done
)
VALUES
(
  #{id},
  #{title},
  #{done}
)

これに対応する Entity クラスが以下。

package dev.mikoto2000.todo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * TodoEntity
 */
@Data
@AllArgsConstructor
public class TodoEntity {
  private long id;
  private String title;
  private boolean done;
}

フィールドとカラムが 1 対 1 で対応しているので、マクロでどうとでもできるはず!

やってみた

という事でやってみた。頑張って動画からキーストロークを読み取ってください…(キーストロークを文章でまとめる気力が無かった…)

第 1 フェーズ: カラムの作成

次のようにカーソルを配置したうえで、

^WWvey<C-w><C-w>pa,<C-w><C-w>j をマクロで記録、 @q で繰り返し、最終行のカンマを消す。

第 2 フェーズ: 値の作成

次のようにカーソルを配置したうえで、

^WWvey<C-w><C-w>a#{<C-r>"}<C-w><C-w>j をマクロで記録、 @q で繰り返し、最終行のカンマを消す。

第 3 フェーズ: フォーマット

後は ggVG= でフォーマットを書ければ終了。

以下、同じようなことをしている動画。

こんな形でウィンドウをまたいだマクロでファイルを作っていくのも便利ですよ!

以上!

2025年10月15日水曜日

いまさら helix_five_rows_jis をビルドしようとしているあなたへ

2025/10/15 時点のビルド方法

  1. QMK Toolbox インストール

  2. QMK MSYS インストール(以降、QMK MSYS 上で作業)

  3. QMK Firmware をクローン

    • コミット: 9315172190 にスイッチ
  4. keyboards/helix/rev2/keymaps/keyboards/helix/rev2/keymapshelix_five_rows_jis をフォルダごとコピー

  5. command.h の編集

    diff --git a/tmk_core/common/command.h b/tmk_core/common/command.h
    index d9d89ba0f1..f44b3f7bc3 100644
    --- a/tmk_core/common/command.h
    +++ b/tmk_core/common/command.h
    @@ -16,7 +16,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
     #ifndef COMMAND_H
    -#define COMMAND
    +#define COMMAND_H
    
     /* FIXME: Add doxygen comments for the behavioral defines in here. */
  6. rules.mk の編集

    diff --git a/rules.mk b/rules.mk
    index 084b5db..3fb420c 100644
    --- a/rules.mk
    +++ b/rules.mk
    @@ -30,7 +30,7 @@ endef
     # you can edit follows 7 Variables
     #  jp: 以下の7つの変数を必要に応じて編集します。
     HELIX_ROWS = 5              # Helix Rows is 4 or 5
    -OLED_ENABLE = yes           # OLED_ENABLE
    +OLED_ENABLE = no           # OLED_ENABLE
     LOCAL_GLCDFONT = no         # use each keymaps "helixfont.h" insted of "common/glcdfont.c"
     LED_BACK_ENABLE = no        # LED backlight (Enable WS2812 RGB underlight.)
     LED_UNDERGLOW_ENABLE = no   # LED underglow (Enable WS2812 RGB underlight.)

残念ながら OLED はあきらめねばならない。

参考資料

2025年10月11日土曜日

コンテナ内で Codex を、 Plus アカウントとして使う

てこずったのでメモ。

前提

  • コンテナ内に codex の実行環境が整っていること
  • コンテナ内に curl がインストールされていること

手順

  1. コンテナ内で codex を起動
  2. 1. Sign in with ChatGPT Usage included with Plus, Pro, and Team plans を選択
  3. 表示された URL に、ホストのブラウザでアクセス
  4. ログイン/Codex にサインインする
  5. 「正常に接続できませんでした」となるので、ブラウザのアドレス欄をコピー
  6. コンテナ内で curl -L '<「5.」でコピーしたアドレス>' を実行
  7. コンテナ内で Codex にログイン完了

以上。

2025年8月25日月曜日

Vim のマクロで気持ち良くなった話 - Java フィールドから SQL の SELECT 定義を作成編

Java Bean でエンティティ的なものを作って、そのフィールド値を(ほぼ)全部 SELECT で撮ってきたいと思う場面は多いと思います。

ざっくりやってみたら意外とうまくいったので記録。

User.java

public class User {
  /**
   * 氏名
   */
  private String name;
  /**
   * グループ ID
   */
  private Integer groupId;
  /**
   * Role ID
   */
  private Long roleId;
  /**
   * 住所
   */
  private String address;
  /**
   * 電話番号
   */
  private String phoneNumber;
}

最初のコメントの始まりにカーソルを合わせ、以下キーストロークをうつと、フィールド名だけを抽出できる。

これを q に保存して @q で全部フィールド名だけを抽出する。

VjjdvWWhx$r,j

すると、こうなる。

public class User {
  name,
  groupId,
  roleId,
  address,
  phoneNumber,
}

後は lower snake case にすればいいので、対象のフィールドの行で以下キーストロークを実行。

^ve:s/\C\([A-Z]\)/_\1/g<Enter>veuj

これも q に記録して、対象行で @q していく。

すると以下のようになる。

public class User {
  name,
  group_id,
  role_Id,
  address,
  phone_number,
}

後は先頭と末尾を SELECT 文ににしてケツカンマを削除。

select
  name,
  group_id,
  role_Id,
  address,
  phone_number
from
  "user";

OK, 以上。

以下、似たようなことをやっている動画です。

2025年8月22日金曜日

Vim のマクロで気持ち良くなった話 - Java のフィールド定義編

エクセルのインタフェース仕様書とかに、こんな形で入力や出力のパラメーター情報が書かれていたりする。

これをいちいち手作業で Java のフィールドとして記載していくのは骨が折れるため、 Vim のマクロで何とかした。

論理名 物理名 データ型 …(略)
氏名 name String
住所 address String
電話番号 phoneNumber String

論理名からデータ型までの列をコピーして Vim に張り付けると、以下のようなテキストになる。

氏名  name    String
住所  address String
電話番号    phoneNumber String

氏名の行の先頭にカーソルを置き、以下のように入力していく。

i/**<Enter><BS> * <ESC>Ea<Enter>*/<Enter>private <ESC>Elvx<ESC>vexbPa <ESC>A;<ESC>j0

すると、氏名の行が以下のようになる。

/**
 * 氏名
 */
private String name;

このストロークを qq で記録しておき、 @q を 2 回実行すれば他の行も以下のようになる。

/**
 * 住所
 */
private String address;
/**
 * 電話番号
 */
private String phoneNumber;

うーん、便利。

以下、似たようなことをやっている動画です。

2025年8月15日金曜日

Ubuntu20.04 に Minikube をベアメタルインストールする

前提

  • Hyper-V を使用
  • OS: Ubuntu Server 20.04 LST
  • 標準インストール直後からの開始

ファイアウォールを OFF に

本当はまじめに穴あけしなきゃいけないのだけど、とりあえず動作確認という事で…

sudo ufw disable

Minikube の none ドライバ実行に必要なパッケージのインストール

conntrack

sudo apt install conntrack

containernetworking-plugins

CNI_PLUGIN_VERSION="v1.7.1"
CNI_PLUGIN_TAR="cni-plugins-linux-amd64-$CNI_PLUGIN_VERSION.tgz" # change arch if not on amd64
CNI_PLUGIN_INSTALL_DIR="/opt/cni/bin"

curl -LO "https://github.com/containernetworking/plugins/releases/download/$CNI_PLUGIN_VERSION/$CNI_PLUGIN_TAR"
sudo mkdir -p "$CNI_PLUGIN_INSTALL_DIR"
sudo tar -xf "$CNI_PLUGIN_TAR" -C "$CNI_PLUGIN_INSTALL_DIR"
rm "$CNI_PLUGIN_TAR"

crictl

DOWNLOAD_DIR="/usr/local/bin"
sudo mkdir -p "$DOWNLOAD_DIR"
CRICTL_VERSION="v1.33.0"
ARCH="amd64"
curl -L "https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-${ARCH}.tar.gz" | sudo tar -C $DOWNLOAD_DIR -xz

kubectl

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

Docker

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
           "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
             $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
               sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
               sudo apt-get update
 sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

cri-docker

curl -LO https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.18/cri-dockerd_0.3.18.3-0.ubuntu-focal_amd64.deb
sudo apt install -y ./cri-dockerd_0.3.18.3-0.ubuntu-focal_amd64.deb

Minikube

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
sudo dpkg -i minikube_latest_amd64.deb

OS 設定

何やっているか知らないけど、 minikube start --driver=none で怒られたので実行した。

sudo sysctl fs.protected_regular=0

Minikube クラスタの作成

sudo minikube start --driver=none

動作確認

mikoto@kubernetes:~$ sudo kubectl get nodes
NAME         STATUS   ROLES           AGE     VERSION
kubernetes   Ready    control-plane   7m51s   v1.33.1

mikoto@kubernetes:~$ sudo kubectl run hello-world --image=hello-world --restart=Never
pod/hello-world created

mikoto@kubernetes:~$ sudo kubectl get pod
NAME          READY   STATUS      RESTARTS   AGE
hello-world   0/1     Completed   0          23s


mikoto@kubernetes:~$ sudo kubectl logs pod/hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

OK.

参考資料

2025年8月14日木曜日

Ubuntu20.04 に kubeadm で Kubernetes を構築する

コントロールプレーンに接続できたりできなかったりと、よくわからない状態になってしまったが、とりあえず作業記録として残す。

前提

  • OS: Ubuntu Server 20.04
  • 標準インストール直後からの開始

必須パッケージのインストール

環境構築中に必要になる雑多パッケージをインストール。

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

swap の無効化

sudo swapoff -a
sudo sed -i '/\sswap\s/s/^/#/' /etc/fstab

containerd のインストール

sudo apt-get update
sudo apt-get install -y containerd

kubeadm のインストール

Kubernetes パッケージリポジトリの公開鍵をダウンロード

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

Kubernetes パッケージのリポジトリを登録

echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

インストール

kubelet, kubeadm, kubectl をインストールし、バージョンを固定する。

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

シングルノードクラスターの作成

sudo kubeadm init

root ユーザーでない自分んが kubectl を実行できるように設定

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

コントロールプレーンに Pod を配置できるように設定

シングルノード設定なので、コントロールプレーンに相乗りする。

デフォルトではセキュリティ上相乗りできないので、相乗りできるように設定する。

kubectl taint nodes --all node-role.kubernetes.io/control-plane-

動作確認

hello-world のイメージをデプロイしてみる。

mikoto@kubernetes:~$ sudo docker run -it --rm hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

良さそう。以上。

参考資料

2025年8月11日月曜日

Spring Security で API Key を使った認証を行う

Spring Initializr

これ

アプリの作成

Echo エンドポイントの作成

誰でも叩ける /echo と、 API Key が無いとたたけない /api/echo を用意する。

package dev.mikoto2000.springboot.security.apikey.firststep.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * EchoController
 */
@RestController
public class EchoController {

  @GetMapping("/echo")
  public String echo(
      @RequestParam String message) {
    return message;
  }

  @GetMapping("/api/echo")
  public String apiEcho(
      @RequestParam String message) {
    return message;
  }
}

セキュリティ設定

セキュリティフィルターの作成

properties に設定した API Key と等しいときだけ認証が通るフィルターを作成。

他プロジェクトで使いまわせるように clientName と同じロールを設定するようにしている。

package dev.mikoto2000.springboot.security.apikey.firststep.security;

import java.io.IOException;
import java.util.List;

import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class ApiKeyAuthFilter extends OncePerRequestFilter {

  private static final String API_KEY_HEADER = "x-api-key";

  private final String clientName;
  private final String apiKey;

  public ApiKeyAuthFilter(String clientName, String apiKey) {
    this.clientName = clientName;
    this.apiKey = apiKey;
  }

  @Override
  protected void doFilterInternal(HttpServletRequest request,
      HttpServletResponse response,
      FilterChain chain) throws IOException, ServletException {

    // CORSプリフライトは素通し(必要に応じて)
    if (HttpMethod.OPTIONS.matches(request.getMethod())) {
      chain.doFilter(request, response);
      return;
    }

    // 既に認証済みならスキップ
    Authentication current = SecurityContextHolder.getContext().getAuthentication();
    if (current != null && current.isAuthenticated()) {
      chain.doFilter(request, response);
      return;
    }

    // API Key による認証
    String requestApiKey = request.getHeader(API_KEY_HEADER);
    if (StringUtils.hasText(apiKey) && StringUtils.hasText(requestApiKey) && apiKey.equals(requestApiKey)) {
      // API クライアント用トークン作成
      var token = new UsernamePasswordAuthenticationToken(
          clientName,
          "N/A",
          List.of(new SimpleGrantedAuthority(String.format("ROLE_%s", clientName))));
      token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

      // SecurityContextHolder に認証済みトークンを設定
      SecurityContextHolder.getContext().setAuthentication(token);
    }

    // 次のフィルタへ
    chain.doFilter(request, response);
  }
}

SecurityConfig の作成

認証エラー時に相手に与える情報は少ない方が良いので空ボディを返すようにしている。

package dev.mikoto2000.springboot.security.apikey.firststep.security;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import jakarta.servlet.http.HttpServletResponse;

/**
 * SecurityConfig
 */
@Configuration
public class SecurityConfig {

  @Value("${security.api.client-role}")
  private String apiClientName;

  @Value("${security.api.key}")
  private String apiKey;

  @Bean
  public SecurityFilterChain apiChain(HttpSecurity http,
      AuthenticationEntryPoint emptyBody401EntryPoint) throws Exception {

    var apiKeyFilter = new ApiKeyAuthFilter(apiClientName, apiKey);

    http
        .securityMatcher("/api/**")
        .csrf(csrf -> csrf.disable())
        .cors(Customizer.withDefaults())
        .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .exceptionHandling(eh -> eh.authenticationEntryPoint(emptyBody401EntryPoint))
        .addFilterBefore(apiKeyFilter, UsernamePasswordAuthenticationFilter.class)
        .authorizeHttpRequests(auth -> auth
            .anyRequest().hasRole("TEST"));

    return http.build();
  }

  /**
   * 401 を本文なしで返す EntryPoint
   */
  @Bean
  public AuthenticationEntryPoint emptyBody401EntryPoint() {
    return (request, response, ex) -> {
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      // ここで Content-Type や Body は書かない(Content-Length: 0)
    };
  }
}

設定ファイル作成

spring.application.name=firststep
security.api.client-role=TEST
security.api.key=0123456789

動作確認

サーバー起動

./mvnw spring-boot:run

リクエスト発行

curl -v localhost:8080/echo?message=aaaaaaaaa
=> aaaaaaaaa

curl -v localhost:8080/api/echo?message=aaaaaaaaa
=> 401 error

curl -v localhost:8080/api/echo?message=aaaaaaaaa -H 'X-API-KEY: 0123456789'
=> aaaaaaaaa

2025年7月22日火曜日

Supabase に入門した

Use Supabase with React | Supabase Docs をベースに、 VanillaJS で DB に接続しに行ってみた。

前提

  • Supabase プロジェクト作成済み

プロジェクト情報のの確認

  1. プロジェクトページを開く
  2. 右下の情報をメモ
    • Project URL
    • API Key

テーブルの作成

プロジェクトページ -> SQL editor を選択。 表示された SQL エディターで SQL を実行していく。

テーブル作成

-- テーブル作成
create table instruments (
  id bigint primary key generated always as identity,
  name text not null
);
-- テストデータ挿入
insert into instruments (name)
values
  ('violin'),
  ('viola'),
  ('cello');

-- row level security 有効化
alter table instruments enable row level security;

RLS ポリシーの作成

テーブル内のデータを、アノニマスユーザーが読み取り可能に設定。

create policy "public can read instruments"
on public.instruments
for select to anon
using (true);
  • anon: アノニマスユーザー

Vite プロジェクトの作成

プロジェクト作成

node ➜ /workspaces/TIL/supabase (supabase-firststep) $ npm create vite@latest firststep
Need to install the following packages:
create-vite@7.0.3
Ok to proceed? (y) y


> npx
> create-vite firststep


  Select a framework:
  Vanilla

  Select a variant:
  TypeScript

  Scaffolding project in /workspaces/TIL/supabase/firststep...

  Done. Now run:

  cd firststep
  npm install
  npm run dev

Supabase パッケージのインストール

cd firststep && npm install @supabase/supabase-js

環境変数の設定

.env.local を作成し、そこにメモしたプロジェクト情報を指定。

VITE_SUPABASE_URL=<Project URL>
VITE_SUPABASE_ACCESS_TOKEN=<API Key>

実装

src/main.ts:

import { createClient } from "@supabase/supabase-js";

const supabase = createClient(import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_ANON_KEY);

const { data } = await supabase.from("instruments").select();

console.log(data);

const app = document.getElementById('app');

if (app) {
  // ルート要素作成
  const root = document.createElement('div');

  // タイトル追加
  const h1 = document.createElement('h1');
  h1.textContent = "Supabase Instruments";
  root.appendChild(h1);

  // リスト作成
  const ul = document.createElement('ul');
  data?.forEach(instrument => {
    const li = document.createElement('li');
    li.textContent = `id: ${instrument.id}, name: ${instrument.name}`;
    ul.appendChild(li);
  });
  root.appendChild(ul);

  // ルート要素をアプリケーションに追加
  app.appendChild(root);

}

動作確認

npm run dev して http://localhost:5173 にアクセスすると、挿入したデータが表示される。 OK.

参考資料