Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
8c4d22e
feat(backend): add email verification functionality with token manage…
yupix May 20, 2026
88ec75f
refactor(backend): SeaORMのv2で推奨されているエラーハンドリングに変更
yupix May 20, 2026
f184165
refactor(backend): トークンの更新をLuaSccriptで原始的に実行するように
yupix May 20, 2026
835e83f
feat(backend): add urlencoding dependency and use it for token encodi…
yupix May 20, 2026
7c5e840
feat(backend): implement verification email outbox for transactional …
yupix May 20, 2026
b691478
chore(front): openapi.jsonからapi呼び出し用の関数を生成
yupix May 20, 2026
46c9b87
feat(backend): add email_verification_app_url to settings with valida…
yupix May 20, 2026
9b59eaf
feat(backend): enhance verification email outbox with processing stat…
yupix May 21, 2026
3c68222
refactor(backend): streamline email worker processing logic and impro…
yupix May 21, 2026
883f35e
feat(backend): introduce centralized error handling with AppError and…
yupix May 21, 2026
1371d4b
feat(backend): タイミング攻撃の対策
yupix May 21, 2026
4632626
feat(backend): implement verification email job processing with Apali…
yupix May 21, 2026
8c3837c
feat(backend): enhance personal_tokens model with ToSchema and remove…
yupix May 21, 2026
2db4f55
feat(backend): implement user registration and email verification flo…
yupix May 21, 2026
43e5187
feat(backend): update personal_tokens model to include ScopeList for …
yupix May 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"tamasfe.even-better-toml",
"bradlc.vscode-tailwindcss",
"vue.volar",
"github.vscode-github-actions"
"github.vscode-github-actions",
"bruno-api-client.bruno"
]
}
7 changes: 7 additions & 0 deletions apps/backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# 未指定時は info,sqlx=warn(2秒/30秒ポーリングの空 SELECT をログに出さない)
# RUST_LOG=info,sqlx=warn
database_url=postgresql://username:password@host:port/db_name
redis_url=redis://127.0.0.1:6379
sentry_dsn=
Expand All @@ -6,3 +8,8 @@ smtp_port=587
smtp_username=your_smtp_username
smtp_password=your_smtp_password
smtp_from=no-reply@example.com
# 必須。認証メールのリンク先(本番では実際のフロント URL を指定)
email_verification_app_url=http://localhost:3000
# 認証メール Apalis ワーカー並列度(本番では 2 以上を検討)
# verification_email_worker_concurrency=2
# ジョブ管理 UI: http://localhost:3400/ (/jobs は / へリダイレクト。本番はネットワーク制限を推奨)
187 changes: 187 additions & 0 deletions apps/backend/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions apps/backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,21 @@ sentry = { version = "0.48.2", features = ["tower-axum-matched-path"] }
tower = "0.5.2"
argon2 = "0.5.3"
anyhow = "1.0.102"
chrono = { version = "0.4", default-features = false, features = ["clock"] }
thiserror = "2.0.10"
base64 = "0.22.1"
redis_pool = "0.10.0"
redis = { version = "1.2.1", features = ["tokio-comp"] }
url = "2.5"
urlencoding = "2.1"
hmac = "0.13"
sha2 = "0.11"
rand = "0.10"
subtle = "2.4"
strum = { version = "0.28.0", features = ["derive"] }
strum_macros = "0.28"
lettre = { version = "0.11", default-features = false, features = ["builder", "hostname", "smtp-transport","tokio1", "tokio1-rustls-tls"] }
apalis = { version = "1.0.0-rc.9", features = ["limit", "retry", "tracing"] }
apalis-postgres = { version = "1.0.0-rc.8", default-features = false, features = ["migrate", "tokio-comp", "time"] }
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres", "time"] }
apalis-board = { version = "1.0.0-rc.8", features = ["axum", "ui", "events"] }
Comment on lines +51 to +54
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

LOCK_FILE="$(fd -HI '^Cargo.lock$' | head -n1 || true)"
if [ -z "${LOCK_FILE}" ]; then
  echo "Cargo.lock が見つかりません。"
  exit 0
fi

python - <<'PY'
import pathlib, tomllib
lock = pathlib.Path("Cargo.lock")
data = tomllib.loads(lock.read_text())
targets = {"apalis", "apalis-postgres", "apalis-board"}

rows = [(p["name"], p["version"]) for p in data.get("package", []) if p.get("name") in targets]
if not rows:
    print("対象クレートが Cargo.lock に見つかりませんでした。")
    raise SystemExit(0)

print("Resolved versions:")
for name, version in sorted(rows):
    print(f"  {name}: {version}")

from collections import defaultdict
vers = defaultdict(set)
for n, v in rows:
    vers[n].add(v)

multi = {k: sorted(v) for k, v in vers.items() if len(v) > 1}
if multi:
    print("\n同一クレートで複数バージョンが解決されています:")
    for k, v in multi.items():
        print(f"  {k}: {v}")
PY

Repository: TeamBlackCrystal/task

Length of output: 602


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate any Cargo.lock in repo (limit output)
echo "Searching for Cargo.lock files..."
fd -HI 'Cargo.lock$' . || true

# Also try common locations under apps/backend
echo
echo "Searching under apps/backend..."
fd -HI 'Cargo.lock$' apps/backend . || true

# If any are found, check versions from the first one
LOCKS=()
while IFS= read -r f; do LOCKS+=("$f"); done < <(fd -HI 'Cargo.lock$' . || true)

if [ ${`#LOCKS`[@]} -eq 0 ]; then
  echo
  echo "No Cargo.lock found anywhere in repo."
  exit 0
fi

LOCK_FILE="${LOCKS[0]}"
echo
echo "Using lock file: $LOCK_FILE"

python3 - <<PY
import pathlib, tomllib, sys
lock_path = pathlib.Path("$LOCK_FILE")
data = tomllib.loads(lock_path.read_text())
targets = {"apalis", "apalis-postgres", "apalis-board"}
rows=[]
for p in data.get("package", []):
    n=p.get("name")
    if n in targets:
        rows.append((n, p.get("version")))
if not rows:
    print("対象クレートが Cargo.lock に見つかりませんでした。")
    sys.exit(0)

print("Resolved versions (from Cargo.lock):")
for name, version in sorted(rows):
    print(f"  {name}: {version}")

from collections import defaultdict
vers=defaultdict(set)
for n,v in rows:
    vers[n].add(v)

multi={k:sorted(list(v)) for k,v in vers.items() if len(v)>1}
if multi:
    print("\\n同一クレートで複数バージョンが解決されています:")
    for k,v in multi.items():
        print(f"  {k}: {v}")
else:
    print("\\napalis 系は単一バージョンで解決されています。")
PY

Repository: TeamBlackCrystal/task

Length of output: 390


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate any Cargo.lock in repo (limit output)
echo "Searching for Cargo.lock files..."
fd -HI 'Cargo.lock$' . || true

# Also try common locations under apps/backend
echo
echo "Searching under apps/backend..."
fd -HI 'Cargo.lock$' apps/backend . || true

# If any are found, check versions from the first one
LOCKS=()
while IFS= read -r f; do LOCKS+=("$f"); done < <(fd -HI 'Cargo.lock$' . || true)

if [ ${`#LOCKS`[@]} -eq 0 ]; then
  echo
  echo "No Cargo.lock found anywhere in repo."
  exit 0
fi

LOCK_FILE="${LOCKS[0]}"
echo
echo "Using lock file: $LOCK_FILE"

python3 - <<PY
import pathlib, tomllib, sys
lock_path = pathlib.Path("$LOCK_FILE")
data = tomllib.loads(lock_path.read_text())
targets = {"apalis", "apalis-postgres", "apalis-board"}
rows=[]
for p in data.get("package", []):
    n=p.get("name")
    if n in targets:
        rows.append((n, p.get("version")))
if not rows:
    print("対象クレートが Cargo.lock に見つかりませんでした。")
    sys.exit(0)

print("Resolved versions (from Cargo.lock):")
for name, version in sorted(rows):
    print(f"  {name}: {version}")

from collections import defaultdict
vers=defaultdict(set)
for n,v in rows:
    vers[n].add(v)

multi={k:sorted(list(v)) for k,v in vers.items() if len(v)>1}
if multi:
    print("\\n同一クレートで複数バージョンが解決されています:")
    for k,v in multi.items():
        print(f"  {k}: {v}")
else:
    print("\\napalis 系は単一バージョンで解決されています。")
PY

Repository: TeamBlackCrystal/task

Length of output: 390


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Identify lock files explicitly (avoid process substitution issues)
LOCKS=(
  "apps/backend/Cargo.lock"
  "apps/backend/migration/Cargo.lock"
)

PY_SCRIPT=$(cat <<'PY'
import pathlib, tomllib, sys
targets = {"apalis", "apalis-postgres", "apalis-board"}

def check(lock_path: str):
    p = pathlib.Path(lock_path)
    data = tomllib.loads(p.read_text())
    rows=[]
    for pkg in data.get("package", []):
        n = pkg.get("name")
        if n in targets:
            rows.append((n, pkg.get("version")))
    if not rows:
        print(f"{lock_path}: 対象クレートが Cargo.lock に見つかりませんでした。")
        return
    print(f"{lock_path}: Resolved versions:")
    for name, version in sorted(rows):
        print(f"  {name}: {version}")
    from collections import defaultdict
    vers=defaultdict(set)
    for n,v in rows:
        vers[n].add(v)
    multi={k:sorted(list(v)) for k,v in vers.items() if len(v)>1}
    if multi:
        print("  同一クレートで複数バージョンが解決されています:")
        for k,v in multi.items():
            print(f"    {k}: {v}")
    else:
        print("  apalis 系は単一バージョンで解決されています。")
    print()

for path in sys.argv[1:]:
    check(path)
PY
)

python3 - <<PY
$PY_SCRIPT
PY "${LOCKS[@]}"

Repository: TeamBlackCrystal/task

Length of output: 346


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import pathlib, tomllib

targets = {"apalis", "apalis-postgres", "apalis-board"}
lock_paths = ["apps/backend/Cargo.lock", "apps/backend/migration/Cargo.lock"]

def check(lock_path: str):
    p = pathlib.Path(lock_path)
    if not p.exists():
        print(f"{lock_path}: Cargo.lock が存在しません")
        return
    data = tomllib.loads(p.read_text())
    rows=[]
    for pkg in data.get("package", []):
        n = pkg.get("name")
        if n in targets:
            rows.append((n, pkg.get("version")))
    if not rows:
        print(f"{lock_path}: 対象クレートが Cargo.lock に見つかりませんでした。")
        return

    print(f"{lock_path}: Resolved versions:")
    for name, version in sorted(rows):
        print(f"  {name}: {version}")

    from collections import defaultdict
    vers=defaultdict(set)
    for n,v in rows:
        vers[n].add(v)

    multi={k:sorted(list(v)) for k,v in vers.items() if len(v)>1}
    if multi:
        print("  同一クレートで複数バージョンが解決されています:")
        for k,v in multi.items():
            print(f"    {k}: {v}")
    else:
        print("  apalis 系は単一バージョンで解決されています。")
    print()

for lp in lock_paths:
    check(lp)
PY

Repository: TeamBlackCrystal/task

Length of output: 1458


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== migration/Cargo.lock line 780-840 =="
nl -ba apps/backend/migration/Cargo.lock | sed -n '780,840p'

Repository: TeamBlackCrystal/task

Length of output: 166


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== migration/Cargo.lock line 780-840 =="
sed -n '780,840p' apps/backend/migration/Cargo.lock

Repository: TeamBlackCrystal/task

Length of output: 1717


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import pathlib, tomllib

lock_path = pathlib.Path("apps/backend/Cargo.lock")
data = tomllib.loads(lock_path.read_text())

targets = {("apalis", "1.0.0-rc.9"),
            ("apalis-postgres", "1.0.0-rc.8"),
            ("apalis-board", "1.0.0-rc.8")}

# Build quick lookup
pkgs = {(p.get("name"), p.get("version")): p for p in data.get("package", [])}

def show(name, ver):
    p = pkgs.get((name, ver))
    if not p:
        print(f"Not found: {name} {ver}")
        return
    deps = p.get("dependencies", [])
    print(f"\n{name} {ver} dependencies ({len(deps)}):")
    for d in deps:
        print(f"  {d}")

show("apalis", "1.0.0-rc.9")
show("apalis-postgres", "1.0.0-rc.8")
show("apalis-board", "1.0.0-rc.8")
PY

Repository: TeamBlackCrystal/task

Length of output: 1964


Apalis 系の RC を揃えるか、Cargo.lock での解決(重複解決の有無)を確認

apps/backend/Cargo.lock では apalis = 1.0.0-rc.9apalis-postgres = 1.0.0-rc.8apalis-board = 1.0.0-rc.8 が解決されており、少なくとも同一クレート名としての apalis の重複解決は確認できませんでした。それでも rc.8 / rc.9 間の API 互換性で問題が出る可能性があるため、可能なら apalis-family の RC を揃えるか、apps/backend/migration/Cargo.lock 側でも同様に apalis が重複解決していないことを確認してください。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/backend/Cargo.toml` around lines 51 - 54, The apalis crate versions are
inconsistent (apalis = rc.9 vs apalis-postgres/apalis-board = rc.8); update the
Cargo.toml so the apalis-family crates use the same RC (e.g., set
apalis-postgres and apalis-board to "1.0.0-rc.9" or pin apalis to "1.0.0-rc.8"),
then run cargo update to regenerate Cargo.lock and verify no duplicate
resolutions; additionally check apps/backend/migration/Cargo.lock to ensure it
doesn't introduce a different apalis RC and reconcile it if needed.

36 changes: 36 additions & 0 deletions apps/backend/bruno/01-register-verify/01-register.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
meta {
name: 1. 新規登録
type: http
seq: 1
}

post {
url: {{baseUrl}}/v1/auth/register
body: json
auth: none
}

headers {
content-type: application/json
}

body:json {
{
"username": "{{username}}",
"email": "{{email}}",
"password": "{{password}}"
}
}

vars:pre-request {
baseUrl: http://localhost:3400
username: yupix
email: "{{email}}"
password: "{{password}}"
}

tests {
test("should return 201", function() {
expect(res.status).to.equal(201);
});
}
Loading