1diff --git a/README.md b/README.md
2index 6103dbfc3432ba7615a19ab7d4026389bd854e9b..9ff9bfd53f0a3058505d4b70861d40120fb3537d 100644
3--- a/README.md
4+++ b/README.md
5@@ -13,3 +13,5 @@ * Alpine package and demo site
6 * Single image viewer and show exif info (not sure how yet)
7 * User base root folder
8 * Albuns
9+* Testing. Since I still on initial iteration phases I'm not adding as many
10+ testing as I'd like to. Once I set on most of the design I'll add testing.
11diff --git a/cmd/server/main.go b/cmd/server/main.go
12index 4ca39de019df146d6b4348036a23abaa1087d090..d7c2fd64ce0eaf97d3afc4e3d3d6337ac5995109 100644
13--- a/cmd/server/main.go
14+++ b/cmd/server/main.go
15@@ -103,7 +103,7 @@ // view
16 for _, v := range []view.View{
17 view.NewAuthView(userController),
18 view.NewFileSystemView(*fileSystemController, settingsRepository),
19- view.NewSettingsView(settingsRepository),
20+ view.NewSettingsView(settingsRepository, userRepository),
21 view.NewMediaView(mediaRepository),
22 } {
23 v.SetMyselfIn(extRouter)
24diff --git a/pkg/components/auth/controller.go b/pkg/components/auth/controller.go
25index 4da607129965146d0a5cef01599d464e421f5875..a81a1c0f802acc5a4c90bcc400d2d9f913c73b9b 100644
26--- a/pkg/components/auth/controller.go
27+++ b/pkg/components/auth/controller.go
28@@ -41,17 +41,3 @@ Username: string(username),
29 }
30 return ext.WriteToken(token, c.key)
31 }
32-
33-func (c *Controller) Register(ctx context.Context, username, password []byte) error {
34- hash, err := bcrypt.GenerateFromPassword(password, bcrypt.MinCost)
35- if err != nil {
36- return err
37- }
38-
39- _, err = c.repository.Create(ctx, &CreateUser{
40- Username: string(username),
41- Password: hash,
42- })
43-
44- return err
45-}
46diff --git a/pkg/components/auth/model.go b/pkg/components/auth/model.go
47index e46ef4979fc10a82e1822ab6a692c19774093525..dd6ce5036b91127e13e37c4e57b7baa13b486a1a 100644
48--- a/pkg/components/auth/model.go
49+++ b/pkg/components/auth/model.go
50@@ -3,30 +3,8 @@
51 import "context"
52
53 type (
54- // TODO: move to user later
55- User struct {
56- ID uint
57- Username string
58- Name string
59- }
60-
61- // TODO: move to user later
62- UpdateUser struct {
63- Username string
64- Name string
65- }
66-
67- // TODO: move to user later
68- CreateUser struct {
69- Username string
70- Name string
71- Password []byte
72- }
73-
74 Repository interface {
75 GetIDByUsername(ctx context.Context, username string) (uint, error)
76 GetPassword(ctx context.Context, id uint) ([]byte, error)
77- // TODO: move to user later
78- Create(ctx context.Context, createUser *CreateUser) (uint, error)
79 }
80 )
81diff --git a/pkg/components/user/controller.go b/pkg/components/user/controller.go
82new file mode 100644
83index 0000000000000000000000000000000000000000..a00006b65468ecad731002a1e2c54884759656ed
84--- /dev/null
85+++ b/pkg/components/user/controller.go
86@@ -0,0 +1 @@
87+package user
88diff --git a/pkg/components/user/model.go b/pkg/components/user/model.go
89new file mode 100644
90index 0000000000000000000000000000000000000000..f957c39a95fb49179c319674a6c31827bebb695c
91--- /dev/null
92+++ b/pkg/components/user/model.go
93@@ -0,0 +1,33 @@
94+package user
95+
96+import "context"
97+
98+type (
99+ User struct {
100+ ID uint
101+ Username string
102+ Name string
103+ IsAdmin bool
104+ Path string
105+ }
106+
107+ UpdateUser struct {
108+ Username string
109+ Name string
110+ Password *string
111+ }
112+
113+ CreateUser struct {
114+ Username string
115+ Name string
116+ Password string
117+ IsAdmin bool
118+ Path string
119+ }
120+
121+ Repository interface {
122+ List(ctx context.Context) ([]*User, error)
123+ Create(ctx context.Context, createUser *CreateUser) error
124+ Update(ctx context.Context, id uint, updateUser *UpdateUser) error
125+ }
126+)
127diff --git a/pkg/database/sql/user.go b/pkg/database/sql/user.go
128index d449b05078914829508c79dd8da8583b7f66cbc9..2d74162713c10e1ed292a1dc116404e862f77bd3 100644
129--- a/pkg/database/sql/user.go
130+++ b/pkg/database/sql/user.go
131@@ -7,7 +7,7 @@ "golang.org/x/crypto/bcrypt"
132 "gorm.io/gorm"
133
134 "git.sr.ht/~gabrielgio/img/pkg/components/auth"
135- user "git.sr.ht/~gabrielgio/img/pkg/components/auth"
136+ "git.sr.ht/~gabrielgio/img/pkg/components/user"
137 )
138
139 type (
140@@ -16,6 +16,8 @@ gorm.Model
141 Username string
142 Name string
143 Password string
144+ IsAdmin bool
145+ Path string
146 }
147
148 Users []*User
149@@ -26,6 +28,7 @@ }
150 )
151
152 var _ auth.Repository = &UserRepository{}
153+var _ user.Repository = &UserRepository{}
154
155 func NewUserRepository(db *gorm.DB) *UserRepository {
156 return &UserRepository{
157@@ -38,6 +41,8 @@ return &user.User{
158 ID: self.Model.ID,
159 Name: self.Name,
160 Username: self.Username,
161+ Path: self.Path,
162+ IsAdmin: self.IsAdmin,
163 }
164 }
165
166@@ -63,6 +68,8 @@ if !exists {
167 hash, _ := bcrypt.GenerateFromPassword([]byte("admin"), bcrypt.MinCost)
168 self.db.Save(&User{
169 Username: "admin",
170+ Path: "/",
171+ IsAdmin: true,
172 Password: string(hash),
173 })
174 }
175@@ -130,7 +137,7 @@
176 return userPassword.Password, nil
177 }
178
179-func (self *UserRepository) Create(ctx context.Context, createUser *user.CreateUser) (uint, error) {
180+func (self *UserRepository) Create(ctx context.Context, createUser *user.CreateUser) error {
181 user := &User{
182 Username: createUser.Username,
183 Name: createUser.Name,
184@@ -141,10 +148,10 @@ result := self.db.
185 WithContext(ctx).
186 Create(user)
187 if result.Error != nil {
188- return 0, result.Error
189+ return result.Error
190 }
191
192- return user.Model.ID, nil
193+ return nil
194 }
195
196 func (self *UserRepository) Update(ctx context.Context, id uint, update *user.UpdateUser) error {
197diff --git a/pkg/view/auth.go b/pkg/view/auth.go
198index 5c83eba0225998cda97795f1747f3d73894e9f4d..d44424d64290d5877761bcc1229b9244551ca08b 100644
199--- a/pkg/view/auth.go
200+++ b/pkg/view/auth.go
201@@ -64,23 +64,6 @@ }
202 return nil
203 }
204
205-func (v *AuthView) RegisterView(ctx *fasthttp.RequestCtx) error {
206- return img.Render[interface{}](ctx, "register.html", nil)
207-}
208-
209-func (v *AuthView) Register(ctx *fasthttp.RequestCtx) error {
210- username := ctx.FormValue("username")
211- password := ctx.FormValue("password")
212-
213- err := v.userController.Register(ctx, username, password)
214- if err != nil {
215- return err
216- }
217-
218- ctx.Redirect("/login", 307)
219- return nil
220-}
221-
222 func Index(ctx *fasthttp.RequestCtx) {
223 ctx.Redirect("/login", 307)
224 }
225@@ -88,9 +71,6 @@
226 func (v *AuthView) SetMyselfIn(r *ext.Router) {
227 r.GET("/login", v.LoginView)
228 r.POST("/login", v.Login)
229-
230- r.GET("/register", v.RegisterView)
231- r.POST("/register", v.Register)
232
233 r.GET("/logout", v.Logout)
234 r.POST("/logout", v.Logout)
235diff --git a/pkg/view/settings.go b/pkg/view/settings.go
236index 746dee4df99001f92e4b6703fee352ca4cb76998..e5acb1bbc57cfcb0325646114b7e95342e21c53a 100644
237--- a/pkg/view/settings.go
238+++ b/pkg/view/settings.go
239@@ -5,28 +5,50 @@ "github.com/valyala/fasthttp"
240
241 "git.sr.ht/~gabrielgio/img"
242 "git.sr.ht/~gabrielgio/img/pkg/components/settings"
243+ "git.sr.ht/~gabrielgio/img/pkg/components/user"
244 "git.sr.ht/~gabrielgio/img/pkg/ext"
245 )
246
247-type SettingsView struct {
248- // there is not need to create a controller for this
249- repository settings.Repository
250-}
251+type (
252+ SettingsView struct {
253+ // there is not need to create a controller for this
254+ settingsRepository settings.Repository
255+ userRepository user.Repository
256+ }
257+
258+ SettingsPage struct {
259+ Settings *settings.Settings
260+ Users []*user.User
261+ }
262+)
263
264-func NewSettingsView(respository settings.Repository) *SettingsView {
265+func NewSettingsView(
266+ settingsRespository settings.Repository,
267+ userRepository user.Repository,
268+) *SettingsView {
269 return &SettingsView{
270- repository: respository,
271+ settingsRepository: settingsRespository,
272+ userRepository: userRepository,
273 }
274 }
275
276 func (self *SettingsView) Index(ctx *fasthttp.RequestCtx) error {
277- s, err := self.repository.Load(ctx)
278+ s, err := self.settingsRepository.Load(ctx)
279+ if err != nil {
280+ return err
281+ }
282+
283+ users, err := self.userRepository.List(ctx)
284 if err != nil {
285 return err
286 }
287- return img.Render(ctx, "settings.html", &img.HTMLView[*settings.Settings]{
288+
289+ return img.Render(ctx, "settings.html", &img.HTMLView[*SettingsPage]{
290 Title: "Settings",
291- Data: s,
292+ Data: &SettingsPage{
293+ Settings: s,
294+ Users: users,
295+ },
296 })
297 }
298
299@@ -36,7 +58,7 @@ showMode = string(ctx.FormValue("showMode")) == "on"
300 showOwner = string(ctx.FormValue("showOwner")) == "on"
301 )
302
303- err := self.repository.Save(ctx, &settings.Settings{
304+ err := self.settingsRepository.Save(ctx, &settings.Settings{
305 ShowMode: showMode,
306 ShowOwner: showOwner,
307 })
308diff --git a/scss/main.scss b/scss/main.scss
309index 033315f51b4c6b75ed6da68eb9d5faa25656409b..7028622f93c495f755a63365a8093ba4cc8c1264 100644
310--- a/scss/main.scss
311+++ b/scss/main.scss
312@@ -34,15 +34,20 @@
313 .input, .button{
314 border-radius: 0;
315 }
316-
317-.file-row {
318+.wide-column {
319 width: 100%;
320+}
321+.is-mono {
322 font-family: monospace;
323 }
324
325 nav {
326 border-bottom: 1px solid;
327 margin: auto;
328+}
329+
330+a.is-danger {
331+ color: $danger
332 }
333
334 .container {
335diff --git a/templates/fs.html b/templates/fs.html
336index 608289d971d4f83e1c9b5155f63f6059084b5b85..a44d78f441b53a2e1229120696a4c081908679dd 100644
337--- a/templates/fs.html
338+++ b/templates/fs.html
339@@ -11,7 +11,7 @@ </div>
340 </div>
341 {{range .Data.Page.Files}}
342 <div class="panel-block">
343- <div class="columns file-row is-gapless is-mobile">
344+ <div class="columns wide-column is-mono is-gapless is-mobile">
345 <div class="column">
346 {{if $.Data.ShowMode}}{{.Info.Mode}} {{end}}
347 {{if $.Data.ShowOwner}}{{.Info.Sys.Gid}}:{{.Info.Sys.Uid}} {{end}}
348diff --git a/templates/settings.html b/templates/settings.html
349index 2aa1a80ea944113afeac78ecc08cac704956187d..8c08773338919637ec0c3cb18e2b050f22945a66 100644
350--- a/templates/settings.html
351+++ b/templates/settings.html
352@@ -7,7 +7,7 @@ <form action="/settings/", method="post">
353 <div class="field">
354 <div class="control">
355 <label class="checkbox">
356- <input type="checkbox" id="showMode" name="showMode" {{if .Data.ShowMode}}checked{{end}}>
357+ <input type="checkbox" id="showMode" name="showMode" {{if .Data.Settings.ShowMode}}checked{{end}}>
358 Show File Modes
359 </label>
360 </div>
361@@ -15,7 +15,7 @@ </div>
362 <div class="field">
363 <div class="control">
364 <label class="checkbox">
365- <input type="checkbox" id="showOwner" name="showOwner" {{if .Data.ShowOwner}}checked{{end}}>
366+ <input type="checkbox" id="showOwner" name="showOwner" {{if .Data.Settings.ShowOwner}}checked{{end}}>
367 Show File Owner
368 </label>
369 </div>
370@@ -26,38 +26,19 @@ </div>
371 </form>
372 </div>
373 <div class="column">
374- <div class="table-container">
375- <table class="table">
376- <thead>
377- <tr>
378- <th>Username</th>
379- <th>Path</th>
380- <th>Is Admin?</th>
381- <th>Action</th>
382- </tr>
383- </thead>
384- <tbody>
385- <tr>
386- <td>gabrielgio</td>
387- <td>/home/gabrielgio</td>
388- <td>yes</td>
389- <td>
390- <a href="#" class="button is-small">Edit</button>
391- <a href="#" class="button is-small is-danger">Delete</button>
392- </td>
393- </tr>
394- <tr>
395- <td>beatriz</td>
396- <td>/home/beatriz</td>
397- <td>no</td>
398- <td>
399- <a href="#" class="button is-small">Edit</button>
400- <a href="#" class="button is-small is-danger">Delete</button>
401- </td>
402- </tr>
403- </tbody>
404- </table>
405+ {{range .Data.Users}}
406+ <div class="panel-block">
407+ <div class="columns wide-column is-gapless is-mobile">
408+ <div class="column">
409+ {{.Username}}
410+ </div>
411+ <div class="column">
412+ {{.Path}}
413+ </div>
414+ <div class="column has-text-right"><a href="#">Edit</a> / <a href="#" class="is-danger">Delete</a></div>
415+ </div>
416 </div>
417+ {{end}}
418 </div>
419 </div>
420 {{end}}