Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions products/backend/src/db/pay-crew2-schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// drizzle
import { pgTable, text, timestamp, index, uuid, uniqueIndex, integer } from 'drizzle-orm/pg-core';
import { pgTable, text, timestamp, index, uuid, uniqueIndex, integer, date } from 'drizzle-orm/pg-core';

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

The date import from 'drizzle-orm/pg-core' is added but should be verified that it's correctly used for the occurredAt field. Ensure that the date type correctly handles the YYYY-MM-DD format expected by the API validators and doesn't introduce timezone-related issues.

Copilot uses AI. Check for mistakes.
//auth-schema
import { user } from './auth-schema';

Expand All @@ -8,7 +8,7 @@ export const group = pgTable(
{
id: uuid('id').primaryKey(),
name: text('name').notNull(),
invite_id: text('invite_id').notNull().unique(),
inviteId: text('invite_id').notNull().unique(),

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

The column name is changed from invite_id (snake_case) to inviteId (camelCase) in the schema definition, but the actual database column name remains invite_id due to the second parameter in the text() function. This inconsistency between the schema property name and how it's referenced in the code could lead to confusion. Ensure all references in the codebase use the correct property name inviteId.

Suggested change
inviteId: text('invite_id').notNull().unique(),
invite_id: text('invite_id').notNull().unique(),

Copilot uses AI. Check for mistakes.
createdBy: text('created_by')
.references(() => user.id, { onDelete: 'set null' })
.notNull(),
Expand Down Expand Up @@ -58,7 +58,7 @@ export const debt = pgTable(
.references(() => user.id, { onDelete: 'cascade' }),
amount: integer('amount').notNull(),
description: text('description'),
occurredAt: timestamp('occurred_at').defaultNow().notNull(),
occurredAt: date('occurred_at').notNull(),

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

Changing the column type from timestamp to date removes time information from debt records. This is a breaking change that could result in data loss if existing records contain time information. Consider using a migration strategy to handle existing data appropriately.

Suggested change
occurredAt: date('occurred_at').notNull(),
occurredAt: timestamp('occurred_at').notNull(),

Copilot uses AI. Check for mistakes.
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at')
.defaultNow()
Expand Down
80 changes: 24 additions & 56 deletions products/backend/src/presentation/routes/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import {
type GetGroupDebtHistoryResponseSchemaType,
GetGroupInfoResponseMemberElementSchemaType,
registerGroupDebtRequestSchema,
registerGroupDebtResponseSchema,
type RegisterGroupDebtResponseSchemaType,
deleteGroupDebtRequestSchema,
} from 'validator';
// error schema
Expand Down Expand Up @@ -67,9 +65,6 @@ hono.openapi(createGroupSchema, async (c) => {
const loginUser = c.get('user');
const body = c.req.valid('json');

// 現在時刻の取得
const now = new Date();

// データベース接続
const db = drizzle({ connection: c.env.HYPERDRIVE });

Expand All @@ -79,29 +74,26 @@ hono.openapi(createGroupSchema, async (c) => {
.values({
id: crypto.randomUUID(),
name: body.group_name,
invite_id: `${crypto.randomUUID()}-${crypto.randomUUID()}`,
inviteId: `${crypto.randomUUID()}-${crypto.randomUUID()}`,
createdBy: loginUser.id,

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

The removal of createdAt and updatedAt timestamp assignments means these fields will rely solely on database defaults. If the database default is not set or fails, this could result in null values. Verify that the database schema has proper defaultNow() or equivalent defaults configured for these columns.

Suggested change
createdBy: loginUser.id,
createdBy: loginUser.id,
createdAt: new Date(),
updatedAt: new Date(),

Copilot uses AI. Check for mistakes.
createdAt: now,
updatedAt: now,
})
.returning({
id: group.id,
invite_id: group.invite_id,
inviteId: group.inviteId,
});

//* グループ作成者をgroupMembershipに挿入 *//
await db.insert(groupMembership).values({
id: crypto.randomUUID(),
groupId: result[0].id,
userId: loginUser.id,

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

Removing the joinedAt field assignment means this field will rely solely on the database default. If the database schema doesn't have a proper defaultNow() or equivalent default configured for this column, it could result in null values. Verify that the database schema has the appropriate default configured.

Suggested change
userId: loginUser.id,
userId: loginUser.id,
joinedAt: new Date(),

Copilot uses AI. Check for mistakes.
joinedAt: now,
});

// レスポンス
return c.json(
{
group_id: result[0].id,
invite_id: result[0].invite_id,
invite_id: result[0].inviteId,
} satisfies CreateGroupResponseSchemaType,
201
);
Expand Down Expand Up @@ -142,9 +134,6 @@ hono.openapi(joinGroupSchema, async (c) => {
const body = c.req.valid('json');
const user = c.get('user');

// 現在時刻の取得
const now = new Date();

// データベース接続
const db = drizzle({ connection: c.env.HYPERDRIVE });

Expand All @@ -154,7 +143,7 @@ hono.openapi(joinGroupSchema, async (c) => {
id: group.id,
})
.from(group)
.where(eq(group.invite_id, body.invite_id))
.where(eq(group.inviteId, body.invite_id))
.limit(1);

// invite_id が不正な場合はエラー
Expand Down Expand Up @@ -188,7 +177,6 @@ hono.openapi(joinGroupSchema, async (c) => {
id: crypto.randomUUID(),
groupId: groupData[0].id,
userId: user.id,

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

Removing the joinedAt field assignment means this field will rely solely on the database default. If the database schema doesn't have a proper defaultNow() or equivalent default configured for this column, it could result in null values. Verify that the database schema has the appropriate default configured.

Suggested change
userId: user.id,
userId: user.id,
joinedAt: new Date(),

Copilot uses AI. Check for mistakes.
joinedAt: now,
})
.returning({
groupId: groupMembership.groupId,
Expand Down Expand Up @@ -261,6 +249,7 @@ hono.openapi(getGroupInfoSchema, async (c) => {
const groupData = await db
.select({
name: group.name,
inviteId: group.inviteId,
createdBy: group.createdBy,
})
.from(group)
Expand Down Expand Up @@ -320,7 +309,9 @@ hono.openapi(getGroupInfoSchema, async (c) => {
return c.json(
{
group_name: groupData[0].name,
created_by:
invite_id: groupData[0].inviteId,
created_by_id: groupData[0].createdBy,
created_by_name:
createdByUserNameInfo[0].displayName !== null && createdByUserNameInfo[0].displayName.length > 0
? createdByUserNameInfo[0].displayName
: createdByUserNameInfo[0].name,
Expand Down Expand Up @@ -395,6 +386,8 @@ hono.openapi(getGroupDebtHistorySchema, async (c) => {
debtorId: debt.debtorId,
creditorId: debt.creditorId,
amount: debt.amount,
description: debt.description,
occurredAt: debt.occurredAt,
})
.from(debt)
.where(and(eq(debt.groupId, body.group_id), isNull(debt.deletedAt)));
Expand Down Expand Up @@ -438,6 +431,8 @@ hono.openapi(getGroupDebtHistorySchema, async (c) => {
? CreditorNameInfo[0].displayName
: CreditorNameInfo[0].name,
amount: debtEntry.amount,
description: debtEntry.description === null ? '' : debtEntry.description,
occurred_at: debtEntry.occurredAt,

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

The occurredAt field from the database is of type date, which is returned directly in the response. Depending on the database driver, this might be returned as a Date object rather than a string. The response schema expects a string matching the pattern /^\d{4}-\d{2}-\d{2}$/. Ensure that the date is properly formatted as a string (e.g., using toISOString().split('T')[0] or similar) before returning it in the response.

Copilot uses AI. Check for mistakes.
});
}

Expand Down Expand Up @@ -468,13 +463,8 @@ const registerGroupDebtSchema = route.createSchema(
},
},
responses: {
201: {
description: 'Created',
content: {
'application/json': {
schema: registerGroupDebtResponseSchema,
},
},
204: {
description: 'No Content',
},
},
},
Expand All @@ -485,9 +475,6 @@ hono.openapi(registerGroupDebtSchema, async (c) => {
const loginUser = c.get('user');
const body = c.req.valid('json');

// 現在時刻の取得
const now = new Date();

// データベース接続
const db = drizzle({ connection: c.env.HYPERDRIVE });

Expand All @@ -508,35 +495,17 @@ hono.openapi(registerGroupDebtSchema, async (c) => {
// NOTE: --- 共通化終了 ---

// body.group_id に貸し借り履歴を追加 (debt table)
const result = await db
.insert(debt)
.values({
id: crypto.randomUUID(),
groupId: body.group_id,
creditorId: body.creditor_id,
debtorId: body.debtor_id,
amount: body.amount,
description: typeof body.description === 'undefined' ? null : body.description,
occurredAt: body.occurred_at ? new Date(body.occurred_at) : now,
createdAt: now,
updatedAt: now,
})
.returning({
creditorId: debt.creditorId,
debtorId: debt.debtorId,
amount: debt.amount,
occurredAt: debt.occurredAt,
});
await db.insert(debt).values({
id: crypto.randomUUID(),
groupId: body.group_id,
creditorId: body.creditor_id,
debtorId: body.debtor_id,
amount: body.amount,
description: typeof body.description === 'undefined' ? null : body.description,
occurredAt: body.occurred_at,

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

The backend is setting occurredAt to body.occurred_at directly as a string, but the database schema expects a date type. This type mismatch may cause runtime errors depending on the database driver's handling. The date string should be converted to a proper Date object before insertion.

Suggested change
occurredAt: body.occurred_at,
occurredAt: new Date(body.occurred_at),

Copilot uses AI. Check for mistakes.
});

return c.json(
{
creditorId: result[0].creditorId,
debtorId: result[0].debtorId,
amount: result[0].amount,
occurredAt: result[0].occurredAt,
} satisfies RegisterGroupDebtResponseSchemaType,
201
);
return c.body(null, 204);
});

// TODO: 貸し借りの履歴の削除エンドポイントの登録
Expand Down Expand Up @@ -597,7 +566,6 @@ hono.openapi(deleteGroupDebtSchema, async (c) => {
.set({
deletedBy: loginUser.id,
deletedAt: now,

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

Removing the updatedAt field update on deletion is inconsistent with typical soft-delete patterns. When a record is soft-deleted (by setting deletedAt and deletedBy), it's common practice to also update the updatedAt timestamp to track when the deletion occurred. Consider whether this removal was intentional or if it could cause issues with audit trails or timestamp-based queries.

Suggested change
deletedAt: now,
deletedAt: now,
updatedAt: now,

Copilot uses AI. Check for mistakes.
updatedAt: now,
})
.where(and(eq(debt.id, body.debt_id), eq(debt.groupId, body.group_id), isNull(debt.deletedAt)));

Expand Down
1 change: 0 additions & 1 deletion products/backend/src/presentation/routes/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@ hono.openapi(infoUserRepaymentSchema, async (c) => {
.set({
deletedBy: loginUser.id,
deletedAt: now,
Comment on lines 298 to 300

Copilot AI Jan 10, 2026

Copy link

Choose a reason for hiding this comment

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

Removing the updatedAt field update on deletion is inconsistent with typical soft-delete patterns. When a record is soft-deleted (by setting deletedAt and deletedBy), it's common practice to also update the updatedAt timestamp to track when the deletion occurred. Consider whether this removal was intentional or if it could cause issues with audit trails or timestamp-based queries.

Copilot uses AI. Check for mistakes.
updatedAt: now,
})
.where(
or(
Expand Down
67 changes: 29 additions & 38 deletions products/frontend/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -4591,7 +4591,15 @@
"type": "string",
"format": "uuid"
},
"created_by": {
"invite_id": {
"type": "string",
"minLength": 1
},
"created_by_id": {
"type": "string",
"minLength": 1
},
"created_by_name": {
"type": "string",
"minLength": 1
},
Expand All @@ -4618,7 +4626,9 @@
},
"required": [
"group_name",
"created_by",
"invite_id",
"created_by_id",
"created_by_name",
"members"
]
}
Expand Down Expand Up @@ -5855,6 +5865,14 @@
"amount": {
"type": "number",
"minimum": 0
},
"description": {
"type": "string",
"minLength": 0
},
"occurred_at": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
}
},
"required": [
Expand All @@ -5863,7 +5881,9 @@
"debtor_name",
"creditor_id",
"creditor_name",
"amount"
"amount",
"description",
"occurred_at"
]
}
}
Expand Down Expand Up @@ -7077,53 +7097,24 @@
"type": "string"
},
"occurred_at": {
"type": "string"
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
}
},
"required": [
"group_id",
"creditor_id",
"debtor_id",
"amount"
"amount",
"occurred_at"
]
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"creditorId": {
"type": "string",
"minLength": 1
},
"debtorId": {
"type": "string",
"minLength": 1
},
"amount": {
"type": "number",
"minimum": 0
},
"occurredAt": {
"type": "string",
"format": "date"
}
},
"required": [
"creditorId",
"debtorId",
"amount",
"occurredAt"
]
}
}
}
"204": {
"description": "No Content"
},
"400": {
"description": "Bad Request",
Expand Down
22 changes: 9 additions & 13 deletions products/frontend/src/api/openapi.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,9 @@ export interface paths {
"application/json": {
/** Format: uuid */
group_name: string;
created_by: string;
invite_id: string;
created_by_id: string;
created_by_name: string;
members: {
user_id: string;
user_name: string;
Expand Down Expand Up @@ -578,6 +580,8 @@ export interface paths {
creditor_id: string;
creditor_name: string;
amount: number;
description: string;
occurred_at: string;
}[];
};
};
Expand Down Expand Up @@ -675,25 +679,17 @@ export interface paths {
debtor_id: string;
amount: number;
description?: string;
occurred_at?: string;
occurred_at: string;
};
};
};
responses: {
/** @description Created */
201: {
/** @description No Content */
204: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
creditorId: string;
debtorId: string;
amount: number;
/** Format: date */
occurredAt: string;
};
};
content?: never;
};
/** @description Bad Request */
400: {
Expand Down
Loading
Loading