lens @ 869f50db2fbc24826762fb61d35e5f8de79296c1

feat: Handle login error
  1diff --git a/pkg/database/repository/base.go b/pkg/database/repository/base.go
  2new file mode 100644
  3index 0000000000000000000000000000000000000000..a9d69c990f2425f791b8e4ce2746a75452fcfb2f
  4--- /dev/null
  5+++ b/pkg/database/repository/base.go
  6@@ -0,0 +1,5 @@
  7+package repository
  8+
  9+import "errors"
 10+
 11+var ErrRecordNotFound = errors.New("record not found")
 12diff --git a/pkg/database/sql/user.go b/pkg/database/sql/user.go
 13index 6b1cf0fd16436f1cbce0e4c0c94d23d372ec31b9..2ec86229435c1508c25f5b67f98178a6cfd67647 100644
 14--- a/pkg/database/sql/user.go
 15+++ b/pkg/database/sql/user.go
 16@@ -2,6 +2,7 @@ package sql
 17 
 18 import (
 19 	"context"
 20+	"errors"
 21 
 22 	"golang.org/x/crypto/bcrypt"
 23 	"gorm.io/gorm"
 24@@ -82,7 +83,7 @@ 		WithContext(ctx).
 25 		Find(&users)
 26 
 27 	if result.Error != nil {
 28-		return nil, result.Error
 29+		return nil, wrapError(result.Error)
 30 	}
 31 
 32 	return users.ToModel(), nil
 33@@ -95,7 +96,7 @@ 		WithContext(ctx).
 34 		First(user)
 35 
 36 	if result.Error != nil {
 37-		return nil, result.Error
 38+		return nil, wrapError(result.Error)
 39 	}
 40 
 41 	return user, nil
 42@@ -113,7 +114,7 @@ 		Where("username = ?", username).
 43 		First(&userID)
 44 
 45 	if result.Error != nil {
 46-		return 0, result.Error
 47+		return 0, wrapError(result.Error)
 48 	}
 49 
 50 	return userID.ID, nil
 51@@ -131,7 +132,7 @@ 		Where("id = ?", id).
 52 		First(&userPassword)
 53 
 54 	if result.Error != nil {
 55-		return nil, result.Error
 56+		return nil, wrapError(result.Error)
 57 	}
 58 
 59 	return userPassword.Password, nil
 60@@ -150,7 +151,7 @@ 	result := self.db.
 61 		WithContext(ctx).
 62 		Create(user)
 63 	if result.Error != nil {
 64-		return 0, result.Error
 65+		return 0, wrapError(result.Error)
 66 	}
 67 
 68 	return user.Model.ID, nil
 69@@ -172,7 +173,7 @@ 		WithContext(ctx).
 70 		Omit("password").
 71 		Updates(user)
 72 	if result.Error != nil {
 73-		return result.Error
 74+		return wrapError(result.Error)
 75 	}
 76 
 77 	return nil
 78@@ -189,7 +190,7 @@ 	result := self.db.
 79 		WithContext(ctx).
 80 		Delete(user)
 81 	if result.Error != nil {
 82-		return result.Error
 83+		return wrapError(result.Error)
 84 	}
 85 	return nil
 86 }
 87@@ -203,7 +204,7 @@ 		Select("count(id) > 0").
 88 		Find(&exists)
 89 
 90 	if result.Error != nil {
 91-		return false, result.Error
 92+		return false, wrapError(result.Error)
 93 	}
 94 
 95 	return exists, nil
 96@@ -220,7 +221,7 @@ 		Where("id = ?", id).
 97 		First(&userPath)
 98 
 99 	if result.Error != nil {
100-		return "", result.Error
101+		return "", wrapError(result.Error)
102 	}
103 
104 	return userPath, nil
105@@ -233,5 +234,12 @@ 		Model(&User{}).
106 		Where("id = ?", id).
107 		Update("password", password)
108 
109-	return result.Error
110+	return wrapError(result.Error)
111+}
112+
113+func wrapError(err error) error {
114+	if errors.Is(err, gorm.ErrRecordNotFound) {
115+		return repository.ErrRecordNotFound
116+	}
117+	return err
118 }
119diff --git a/pkg/service/auth.go b/pkg/service/auth.go
120index 30e574a9690f2c33f6a89f08fa089f8776020102..2fc06e383f8f34d2e1ac843d1d48fb1930c6f80a 100644
121--- a/pkg/service/auth.go
122+++ b/pkg/service/auth.go
123@@ -21,6 +21,8 @@ 	userRepository repository.UserRepository
124 	key            []byte
125 }
126 
127+var InvalidLogin = errors.New("Invalid login")
128+
129 func NewAuthController(
130 	authRepository repository.AuthRepository,
131 	userRepository repository.UserRepository,
132@@ -35,17 +37,21 @@ }
133 
134 func (c *AuthController) Login(ctx context.Context, username, password []byte) ([]byte, error) {
135 	id, err := c.authRepository.GetIDByUsername(ctx, string(username))
136-	if err != nil {
137+	if errors.Is(err, repository.ErrRecordNotFound) {
138+		return nil, InvalidLogin
139+	} else if err != nil {
140 		return nil, err
141 	}
142 
143 	hashedPassword, err := c.authRepository.GetPassword(ctx, id)
144-	if err != nil {
145+	if errors.Is(err, repository.ErrRecordNotFound) {
146+		return nil, InvalidLogin
147+	} else if err != nil {
148 		return nil, err
149 	}
150 
151 	if err := bcrypt.CompareHashAndPassword(hashedPassword, password); err != nil {
152-		return nil, err
153+		return nil, InvalidLogin
154 	}
155 
156 	token := &Token{
157diff --git a/pkg/view/auth.go b/pkg/view/auth.go
158index 1b87235ed7a45f011cc695a650c793d3d493bc91..8d870352e8b7220c8df36582adaeeab7beb054b1 100644
159--- a/pkg/view/auth.go
160+++ b/pkg/view/auth.go
161@@ -2,6 +2,7 @@ package view
162 
163 import (
164 	"encoding/base64"
165+	"errors"
166 	"net/http"
167 
168 	"git.sr.ht/~gabrielgio/img/pkg/ext"
169@@ -45,6 +46,15 @@ 		password = []byte(r.FormValue("password"))
170 	)
171 
172 	auth, err := v.userController.Login(r.Context(), username, password)
173+
174+	if errors.Is(err, service.InvalidLogin) {
175+		templates.WritePageTemplate(w, &templates.LoginPage{
176+			Username: r.FormValue("username"),
177+			Err:      err.Error(),
178+		})
179+		return nil
180+	}
181+
182 	if err != nil {
183 		return err
184 	}
185diff --git a/scss/main.scss b/scss/main.scss
186index 887745238075345e54a2f47e69baa22d7e1beb31..532a38a801bf6cc40f6e57780c6fc35505883439 100644
187--- a/scss/main.scss
188+++ b/scss/main.scss
189@@ -1,13 +1,11 @@
190 $breakpoint: 360px;
191-
192+$radius: 0px;
193 $tablet: 480px;
194 $body-font-size: 1rem;
195-$radius-rounded: 0;
196 
197 $navbar-breakpoint: $breakpoint;
198 
199 $panel-item-border: 1px solid hsl(0, 0%, 93%);
200-$panel-radius: 0;
201 $panel-shadow: 0;
202 
203 $card-shadow: 0;
204@@ -17,7 +15,6 @@
205 $table-cell-padding: 0.5em;
206 $table-cell-border-width: 0;
207 
208-$tag-radius: 0;
209 $tag-delete-margin: 15px;
210 
211 $title-weight: normal;
212diff --git a/templates/login.qtpl b/templates/login.qtpl
213index 56394d001c32c59db586e49112fb57fd71ea0463..c68fb5f8dd68488cbf0e1e5b2bc150c5fcfe806a 100644
214--- a/templates/login.qtpl
215+++ b/templates/login.qtpl
216@@ -1,5 +1,8 @@
217 {% code
218-type LoginPage struct {}
219+type LoginPage struct {
220+        Username string
221+        Err string
222+    }
223 %}
224 
225 {% func (p *LoginPage) Title() %}Login{% endfunc %}
226@@ -9,7 +12,7 @@ <form action="/login" method="post">
227     <div class="field">
228         <label class="label">Username</label>
229         <div class="control">
230-            <input class="input" name="username" type="text">
231+            <input class="input" name="username" value="{%s p.Username %}" type="text">
232         </div>
233     </div>
234     <div class="field">
235@@ -18,9 +21,18 @@         <div class="control">
236             <input class="input" name="password" type="password">
237         </div>
238     </div>
239+    <div class="field is-grouped is-grouped-right">
240+        <input class="button" value="login" type="submit">
241+    </div>
242+    {% if p.Err != "" %}
243     <div class="field">
244-        <input class="button is-pulled-right" value="login" type="submit">
245+        <article class="message is-danger">
246+            <div class="message-body">
247+            {%s p.Err %}
248+            </div>
249+        </article>
250     </div>
251+    {% endif %}
252 </form>
253 {% endfunc %}
254 
255diff --git a/templates/register.qtpl b/templates/register.qtpl
256index 115edfeab3e33fe1f11b2dbf6afc9f3587e40a88..4d3c545dd718c85dc5e8395cdeb5e2694370745a 100644
257--- a/templates/register.qtpl
258+++ b/templates/register.qtpl
259@@ -25,8 +25,8 @@         <div class="control">
260             <input class="input" name="path" type="text">
261         </div>
262     </div>
263-    <div class="field">
264-        <input class="button is-pulled-right" value="Save" type="submit">
265+    <div class="field is-grouped is-grouped-right">
266+        <input class="button" value="Save" type="submit">
267     </div>
268 </form>
269 {% endfunc %}