Skip to content
Open
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
2 changes: 2 additions & 0 deletions atlas/migrations/20241126073229_add_column_users_email.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Modify "users" table
ALTER TABLE "public"."users" ADD COLUMN "email" character varying(255) NULL, ADD COLUMN "email_verified" boolean NULL DEFAULT false;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Modify "users" table
ALTER TABLE "public"."users" ALTER COLUMN "email" SET NOT NULL, ALTER COLUMN "email_verified" SET NOT NULL;
-- Create index "email" to table: "users"
CREATE UNIQUE INDEX "email" ON "public"."users" ("email");
4 changes: 3 additions & 1 deletion atlas/migrations/atlas.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
h1:33BOoFIDyYBMfpcF5tQDv+RBNRH0fTenBPHUnlnSCaI=
h1:5aYCq2wr+qJ4ckP53Fj+f/l0JFFOQG7C9ictTx6NvPo=
20241021163015_create_user.sql h1:gCgeqsR5gC7HN2P2UeGrpMNAW5b9zJNSwPtURtwws1A=
20241021163211_create_client.sql h1:Zk3Nd8qUAnS7v7eIvNaADHK8D0RqIcpkCjUT/Guxt1o=
20241021163446_create_redirect_uri.sql h1:K1C6Q4XOQKbChyejJ/M7F3JHVOgu6DTweXnnX/fOjJ0=
20241024084524_create_approvals.sql h1:7ar7WTyOGflX99Dpm5JiiCInKNrvPk+3UM6yHC+o4Sw=
20241112154715_create_auth_codes.sql h1:GV/eitiRSfHDM1t1YDxKNKsNsrv35QPNSXDZPPKA+Ns=
20241126073229_add_column_users_email.sql h1:st3u0Yl/fpXgr3JHyeT2/PHo5gywJY7lxm5l12Wd7u8=
20241126073311_change_user_email_not_null.sql h1:crChtzneQLh7UXWKfEezju1d8/mAK+JzzfFgOHkq6MM=
11 changes: 11 additions & 0 deletions atlas/schema.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ table "users" {
column "id" {
type = bigserial
}
column "email" {
type = varchar(255)
}
column "email_verified" {
type = boolean
default = false
}
column "name" {
type = varchar(255)
}
Expand All @@ -24,6 +31,10 @@ table "users" {
columns = [column.name]
unique = true
}
index "email" {
columns = [column.email]
unique = true
}
}
table "clients" {
schema = schema.public
Expand Down
20 changes: 19 additions & 1 deletion frontend/src/app/member/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,26 @@ export default function Page() {
return (
<Box sx={{ '> *': { margin: 2 } }}>
<Typography mb={4} variant="h4">
ダッシュボード - {user.name}
ダッシュボード
</Typography>
<Box p={4} borderRadius={2} maxWidth={800} bgcolor={blueGrey[50]}>
<Box display="flex" alignItems="center" mb={1}>
<Typography mr={3} variant="h5">
Account
</Typography>
</Box>
<Box bgcolor="white">
<Typography p={2} variant="h6">
ID: {user.id}
</Typography>
<Typography p={2} variant="h6">
Name: {user.name}
</Typography>
<Typography p={2} variant="h6">
Email: {user.email}
</Typography>
</Box>
</Box>
<Box p={4} borderRadius={2} maxWidth={800} bgcolor={blueGrey[50]}>
<Box display="flex" alignItems="center" mb={1}>
<Typography mr={3} variant="h5">
Expand Down
31 changes: 30 additions & 1 deletion frontend/src/components/SignupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Controller, useForm } from 'react-hook-form'

type SignupInput = {
name: string
email: string
password: string
passwordConfirmation: string
}
Expand All @@ -20,20 +21,31 @@ export const SignupForm = ({ next }: SignupFormProps): JSX.Element => {
const router = useRouter()
const query = useSearchParams()
const { control, handleSubmit, setError } = useForm<SignupInput>({
defaultValues: { name: '', password: '', passwordConfirmation: '' },
defaultValues: {
name: '',
email: '',
password: '',
passwordConfirmation: '',
},
})
const submit = useCallback(
async (data: SignupInput) => {
const { error } = await client.POST('/users/signup', {
body: {
name: data.name,
email: data.email,
password: data.password,
password_confirmation: data.passwordConfirmation,
},
})
if (!!error) {
if (error.error === 'name_already_used') {
setError('name', { message: error.error })
} else if (
error.error === 'email_already_used' ||
error.error === 'email_format_invalid'
) {
setError('email', { message: error.error_description })
} else if (error.error === 'password_length_not_enough') {
setError('password', { message: error.error_description })
} else if (error.error === 'password_confirmation_not_match') {
Expand Down Expand Up @@ -76,6 +88,23 @@ export const SignupForm = ({ next }: SignupFormProps): JSX.Element => {
)}
/>
</Box>
<Box>
<Controller
name="email"
control={control}
render={({ field, fieldState }) => (
<TextField
label="Email"
variant="outlined"
fullWidth
required
error={fieldState.invalid}
helperText={fieldState.error?.message}
{...field}
/>
)}
/>
</Box>
<Box>
<Controller
name="password"
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/utils/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,10 @@ export interface components {
/** Format: int64 */
id: number
name: string
email: string
}
'Users.ReqSignup': {
email: string
name: string
password: string
password_confirmation: string
Expand All @@ -287,6 +289,8 @@ export interface components {
'Users.SignupErr':
| 'name_length_not_enough'
| 'name_already_used'
| 'email_already_used'
| 'email_format_invalid'
| 'password_length_not_enough'
| 'password_confirmation_not_match'
}
Expand Down Expand Up @@ -789,7 +793,6 @@ export interface operations {
/** @description There is no content to send for this request, but the headers may be useful. */
204: {
headers: {
'set-cookie': string
[name: string]: unknown
}
content?: never
Expand Down
82 changes: 40 additions & 42 deletions internal/api/gen.go

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

5 changes: 3 additions & 2 deletions internal/api/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ func (s *Server) SessionInterfaceMe(ctx context.Context, request SessionInterfac
}
return &SessionInterfaceMe200JSONResponse{
User: &User{
Id: int64(session.User.ID),
Name: session.User.Name,
Id: int64(session.User.ID),
Name: session.User.Name,
Email: session.User.Email,
},
}, nil
}
15 changes: 3 additions & 12 deletions internal/api/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
)

func (s *Server) UsersInterfaceSignup(ctx context.Context, request UsersInterfaceSignupRequestObject) (UsersInterfaceSignupResponseObject, error) {
user, err := s.UserUsecase.SignUp(
request.Body.Name, request.Body.Password, request.Body.PasswordConfirmation,
_, err := s.UserUsecase.SignUp(
request.Body.Email, request.Body.Name, request.Body.Password, request.Body.PasswordConfirmation,
)
if errors.Is(err, domain.ErrNameLengthNotEnough) {
s.logger.Infof("name length not enough: %v", err)
Expand Down Expand Up @@ -42,14 +42,5 @@ func (s *Server) UsersInterfaceSignup(ctx context.Context, request UsersInterfac
s.logger.Errorf("failed to signup: %v", err)
return nil, err
}
cookie, err := Login(ctx, user)
if err != nil {
s.logger.Errorf("failed to login: %v", err)
return nil, err
}
return &UsersInterfaceSignup204Response{
Headers: UsersInterfaceSignup204ResponseHeaders{
SetCookie: cookie.String(),
},
}, nil
return &UsersInterfaceSignup204Response{}, nil
}
2 changes: 1 addition & 1 deletion internal/domain/oauth/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ type userRepo struct{ Users []*domain.User }
var _ domain.IUserRepo = &userRepo{}

// Create implements domain.IUserRepo.
func (u *userRepo) Create(name string, encryptedPassword string) error {
func (u *userRepo) Create(email, name, encryptedPassword string) error {
panic("unimplemented")
}

Expand Down
3 changes: 2 additions & 1 deletion internal/domain/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
type UserID int64
type User struct {
ID UserID
Email string
Name string
EncryptedPassword string
CreatedAt time.Time
Expand All @@ -19,7 +20,7 @@ type User struct {
type IUserRepo interface {
FindByID(id UserID) (*User, error)
FindByName(name string) (*User, error)
Create(name, encryptedPassword string) error
Create(email, name, encryptedPassword string) error
}

type UserService struct {
Expand Down
6 changes: 3 additions & 3 deletions internal/infrastructure/gateway/auth_code_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ func TestAuthCodeCreate(t *testing.T) {
authTime := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
clientID := "test_client_id"
userID := int64(1)
query.User.Create(&model.User{ID: userID, Name: "test", EncryptedPassword: "test"})
query.User.Create(&model.User{ID: 2, Name: "test2", EncryptedPassword: "test2"})
query.User.Create(&model.User{ID: userID, Email: "test1@example.com", Name: "test", EncryptedPassword: "test"})
query.User.Create(&model.User{ID: 2, Email: "test2@example.com", Name: "test2", EncryptedPassword: "test2"})
query.Client.Create(&model.Client{
ID: clientID,
EncryptedSecret: "",
Expand Down Expand Up @@ -89,7 +89,7 @@ func TestAuthCodeFind(t *testing.T) {
clientID := "test_client_id"
userID := int64(1)
value := "test_value"
query.User.Create(&model.User{ID: userID, Name: "test", EncryptedPassword: "test"})
query.User.Create(&model.User{ID: userID, Email: "test1@example.com", Name: "test", EncryptedPassword: "test"})
query.Client.Create(&model.Client{ID: clientID, EncryptedSecret: "", UserID: 1})
err := authCodeRepo.Create(value, oauth.ClientID(clientID), domain.UserID(userID), scopes, expiresAt, authTime, "test_redirect_uri")
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions internal/infrastructure/gateway/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ func TestClientFindWithUserID(t *testing.T) {
const user2ID = 45329
const clientID = "client1"
initialUsers := []*model.User{
{Name: "test1", ID: userID, EncryptedPassword: "password"},
{Name: "test2", ID: user2ID, EncryptedPassword: "password"},
{Name: "test1", Email: "test1@example.com", ID: userID, EncryptedPassword: "password"},
{Name: "test2", Email: "test2@example.com", ID: user2ID, EncryptedPassword: "password"},
}
suites := []struct {
name string
Expand Down Expand Up @@ -283,9 +283,9 @@ func TestClientList(t *testing.T) {
const user2ID = 45329
const user3ID = 45330
initialUsers := []*model.User{
{Name: "with clients", ID: userID, EncryptedPassword: "password"},
{Name: "with no client", ID: user2ID, EncryptedPassword: "password"},
{Name: "dummy user", ID: user3ID, EncryptedPassword: "password"},
{Name: "with clients", ID: userID, Email: "test1@example.com", EncryptedPassword: "password"},
{Name: "with no client", ID: user2ID, Email: "test2@example.com", EncryptedPassword: "password"},
{Name: "dummy user", ID: user3ID, Email: "test3@example.com", EncryptedPassword: "password"},
}
initialClients := []*oauth.ClientInput{
{ID: "with two uris", UserID: userID, RedirectURIs: []string{"http://example.com", "http://example1.com"}},
Expand Down
Loading
Loading