🐈

立て替えの取引回数を最小限にするtatekaeというサービスを作成しました!

2024/09/26に公開

背景

僕を含めた友達でよく旅行に行くのですが、旅行に付きまとうのは代金の立て替えだと思います。
飛行機代を建て替えたり、居酒屋代を建て替えたりと色々立て替えることが多いですが、必ずしも一人が建て替え続けることは少ないと思います。
また個々に建て替えたりと、旅行のたびに請求したりされたりで面倒だったので、作成に至りました。

repository:
https://github.com/ei-sugimoto/tatekae

使い方

立て替えの取引を他人見られても問題ない人やデモを見てみたい人は、下記のURL(自宅サーバ)に公開しています。時々閉じていたりするので、注意してもらえるとありがたいです。
https://tatekae-web.ei-sugimoto.uk/

起動方法

k8sなどに3つコンテナを作成します。dbは、mysqlのimageを使用し、apiとwebはghcrから取得してください。

  • docker compose
docker-compose.yml
services:
  db:
    image: mysql:8.1
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: mydb
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - db:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 5s
      retries: 5

  api:
    image: ghcr.io/ei-sugimoto/tatekae/api:latest
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=xxx
      - DB_USER=xxx
      - DB_PASS=xxx
      - DB_NAME=xxx
      - DB_PORT=3306
    depends_on:
      db:
        condition: service_healthy
    tty: true

  web:
    image: ghcr.io/ei-sugimoto/tatekae/api:latest
    ports:
      - "3000:80"
    depends_on:
      - api
      - db
    tty: true
    working_dir: /workspace/app
    stdin_open: true
  • k8s
tatekae-deploy.yml
apiVersion: v1
kind: Namespace
metadata:
  name: tatekae

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-pvc
  namespace: tatekae
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: Service
metadata:
  name: db
  namespace: tatekae
spec:
  ports:
    - port: 3306
  selector:
    app: db

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: db
  namespace: tatekae
spec:
  replicas: 1
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
    spec:
      containers:
        - name: db
          image: mysql:8.1
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: 'xxxx'
            - name: MYSQL_DATABASE
              value: 'xxxx'
            - name: MYSQL_USER
              value: 'xxxx'
            - name: MYSQL_PASSWORD
              value: 'xxxx'
          volumeMounts:
            - name: db-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: db-storage
          persistentVolumeClaim:
            claimName: db-pvc

---
apiVersion: v1
kind: Service
metadata:
  name: api
  namespace: tatekae
spec:
  ports:
    - port: 8080
  selector:
    app: api

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: tatekae
spec:
  replicas: 1
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: ghcr.io/ei-sugimoto/tatekae/api:latest
          ports:
            - containerPort: 8080
          env:
            - name: DB_HOST
              value: 'xxxx'
            - name: DB_USER
              value: 'xxxx'
            - name: DB_PASS
              value: 'xxxx'
            - name: DB_NAME
              value: 'xxxx'
            - name: DB_PORT
              value: '3306'
          volumeMounts:
            - name: api-storage
              mountPath: /app
      volumes:
        - name: api-storage
          emptyDir: {}

---
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: tatekae
spec:
  ports:
    - port: 3000
      targetPort: 80
  selector:
    app: web

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: tatekae
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: ghcr.io/ei-sugimoto/tatekae/web:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: web-storage
              mountPath: /workspace/app
      volumes:
        - name: web-storage
          emptyDir: {}

サイト内説明

  1. ユーザにログインまたは登録
    まずは、ユーザを作成またはログインをしましょう!
    login
  2. プロジェクトを作成および参加
    旅行毎にプロジェクトを作成しましょう!作成したら、Joinボタンを押して参加しましょう。
    join
  3. 請求を作成する
    bill

赤がプロジェクト名、青が参加しているメンバー、黄が請求を作成するところです。
create
参加しているメンバーが請求先一覧に表示されるので、建て替えた金額を入力し、請求先を作成しましょう!
list
作成が完了すると、作成した請求の一覧と、取引回数を最小限した結果を示すsummarizeが表示されます。

技術スタック

api

  1. ent
    entを使用してschemaからテーブルを作成しています。また、SQLに型の注釈がつくので非常に簡単です。
  2. connect-go
    gprc通信およびwebからのhttp通信の両方に対応でき、protobufでエンドポイントやリクエストレスポンスを定義できるconnectを使用しました。
    これにより、リクエスト等にも型の注釈がつくので、楽に開発できました。

web

  1. vite+react
    ビルドツールとしてvite、また純粋なreactを採用しました。vueとreactは迷いましたが、最近使っているreactを選びました。

other

  1. cloudflared tunnel
    サクッと自宅サーバを構築できます。デモサイトもtunnelを作成して、自宅サーバとして公開しています。このtunnel自体もコンテナを起動することで簡単に作成できるのでまじで便利です!!

今後追加していきたい機能

  • プロジェクトをpublicかprivateかを選べる
    現状では、すべてのプロジェクトが見れるようになっているので、招待制のプロジェクトも作成できるようにしていきたいです。
  • UIを作りこむ
    まだ、1週間程度しか開発していないので、UIが微妙です。ここは少しずつ作りこんでいきたい。
GitHubで編集を提案

Discussion