Skip to content

Commit 317f40a

Browse files
feat(backend): fix login, fetch users from config
Signed-off-by: roman-kiselenko <roman.kiselenko.dev@gmail.com>
1 parent d08c2b9 commit 317f40a

11 files changed

Lines changed: 115 additions & 147 deletions

File tree

AGENTS.md

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# AGENTS.md file for smolgit
22

33
## Overview
4+
45
smolgit is a minimalist Git server written in Go with a React/TS frontend. It exposes a REST API over HTTP and an SSH interface.
56

67
## Directory layout
8+
79
- `cmd/` – main entry point.
810
- `pkg/` – core libraries: ssh, git, config, model, route, etc.
911
- `frontend/` – React/TS UI, built into `dist/` and embedded via `go:embed`.
@@ -13,22 +15,23 @@ smolgit is a minimalist Git server written in Go with a React/TS frontend. It ex
1315

1416
## Build & Run
1517

16-
| Target | Description | Command |
17-
|--------|-------------|---------|
18-
| `build` | Compile Go binary into `bin/smolgit` | `make build` |
19-
| `build-frontend` | Build React assets into `dist/` | `make build-frontend` |
20-
| `build-docker` | Build Docker image | `make build-docker` |
21-
| `run` | Run compiled binary locally | `make run` |
22-
| `run-docker` | Run smolgit in a container | `make run-docker` |
23-
| `config` | Generate default config to stdout | `make config` |
24-
| `config-docker` | Generate config for Docker | `make config-docker` |
25-
| `clean` | Remove build artifacts | `make clean` |
26-
| `lint` | Run golangci-lint on Go code | `make lint` |
27-
| `test` | Run Go unit tests | `make test` |
28-
| `integration-test` | Run Bats integration tests | `make integration-test` |
29-
| `help` | Show available make targets | `make help` |
18+
| Target | Description | Command |
19+
| ------------------ | ------------------------------------ | ----------------------- |
20+
| `build` | Compile Go binary into `bin/smolgit` | `make build` |
21+
| `build-frontend` | Build React assets into `dist/` | `make build-frontend` |
22+
| `build-docker` | Build Docker image | `make build-docker` |
23+
| `run` | Run compiled binary locally | `make run` |
24+
| `run-docker` | Run smolgit in a container | `make run-docker` |
25+
| `config` | Generate default config to stdout | `make config` |
26+
| `config-docker` | Generate config for Docker | `make config-docker` |
27+
| `clean` | Remove build artifacts | `make clean` |
28+
| `lint` | Run golangci-lint on Go code | `make lint` |
29+
| `test` | Run Go unit tests | `make test` |
30+
| `integration-test` | Run Bats integration tests | `make integration-test` |
31+
| `help` | Show available make targets | `make help` |
3032

3133
### Docker usage
34+
3235
```sh
3336
# Build image
3437
make build-docker
@@ -52,35 +55,41 @@ A minimal example:
5255

5356
```yaml
5457
log:
58+
# Color log output
5559
color: true
60+
# Log as json
5661
json: false
62+
# Log level (INFO, DEBUG, TRACE, WARN)
5763
level: DEBUG
5864
server:
59-
disabled: false
60-
jwt_key: "your-secret-key"
65+
jwt_key: "super-salt"
6166
auth_disabled: false
67+
# Disable web server
68+
disabled: false
69+
# Web server address
6270
addr: ":3080"
71+
# Navbar brand string
6372
brand: "smolgit"
64-
auth:
65-
enabled: false
66-
accounts:
67-
- login: user
68-
password: pass
6973
ssh:
74+
# SSH server address
7075
addr: ":3081"
7176
git:
72-
path: /tmp/smolgit
77+
# Folder to save git repositories
78+
path: ./tmp
79+
# Base for clone string formating
80+
# (e.g. ssh://git@my-git-server.lan/myuser/project.git)
7381
base: "git@my-git-server.lan"
7482
users:
7583
- name: "bob"
76-
permissions: "*"
84+
password: "$2y$05$US7wXbew8P9d2h8qL3aC6OMhVcwO.1W6U.hVFBGNj9o9YQO.cSqd2" # htpasswd -nbB admin MySecret123
85+
role: "admin"
7786
keys:
78-
- ssh-rsa AAAAB3...
87+
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQ... developer@mail.com
7988
```
8089
81-
* `server.jwt_key` is mandatory – without it the server will refuse to start.
82-
* `git.path` can point to a local `git` binary; if omitted the binary defaults to `/usr/local/bin/git`.
83-
* `git.users` controls SSH access; each user has a list of public keys and a permissions string.
90+
- `server.jwt_key` is mandatory – without it the server will refuse to start.
91+
- `git.path` can point to a local `git` binary; if omitted the binary defaults to `/usr/local/bin/git`.
92+
- `git.users` controls SSH access; each user has a list of public keys and a role string.
8493

8594
## Frontend
8695

@@ -104,11 +113,13 @@ pnpm run lint
104113
## Testing
105114

106115
### Go unit tests
116+
107117
```sh
108118
make test
109119
```
110120

111121
### Bats integration tests
122+
112123
```sh
113124
make integration-test
114125
```
@@ -118,22 +129,23 @@ The Bats tests start a temporary smolgit instance, exercise the CLI, and verify
118129
### CI
119130

120131
GitHub Actions runs:
121-
* `make build`
122-
* `make lint`
123-
* `make config`
124-
* `make integration-test`
132+
133+
- `make build`
134+
- `make lint`
135+
- `make config`
136+
- `make integration-test`
125137

126138
See `.github/workflows/test.yml` for details.
127139

128140
## Naming & Coding Conventions
129141

130-
| Area | Convention |
131-
|------|------------|
132-
| Go packages | lowercase, no underscores |
142+
| Area | Convention |
143+
| -------------- | ------------------------------------------------------ |
144+
| Go packages | lowercase, no underscores |
133145
| Go identifiers | camelCase for variables, PascalCase for exported types |
134-
| Go structs | Tags use `koanf` style |
135-
| Frontend | PascalCase components, camelCase props |
136-
| Paths | Use relative paths in imports (e.g. `smolgit/pkg/git`) |
146+
| Go structs | Tags use `koanf` style |
147+
| Frontend | PascalCase components, camelCase props |
148+
| Paths | Use relative paths in imports (e.g. `smolgit/pkg/git`) |
137149

138150
## Gotchas
139151

@@ -154,7 +166,9 @@ make test
154166
before pushing. Use the PR title format `[smolgit] <Title>`.
155167

156168
## Commit Messages
169+
157170
Follow Conventional Commits:
171+
158172
- `feat`: A new feature
159173
- `fix`: A bug fix
160174
- `docs`: Documentation only changes

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ ADD . ./
1010

1111
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -v -o /build/smolgit
1212

13-
FROM --platform=$BUILDPLATFORM alpine/git:v2.49.0 AS runner
13+
FROM alpine/git:v2.49.0 AS runner
1414

1515
COPY --from=builder /build/smolgit /usr/bin/smolgit
1616

README.md

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,10 @@ log:
6464
# Log level (INFO, DEBUG, TRACE, WARN)
6565
level: DEBUG
6666
server:
67+
jwt_key: "super-salt"
68+
auth_disabled: false
6769
# Disable web server
6870
disabled: false
69-
# Enable basic http auth
70-
auth:
71-
enabled: false
72-
# Credentials for basic auth
73-
accounts:
74-
- login: user2
75-
password: bar
76-
- login: user1
77-
password: foo
7871
# Web server address
7972
addr: ":3080"
8073
# Navbar brand string
@@ -84,21 +77,16 @@ ssh:
8477
addr: ":3081"
8578
git:
8679
# Folder to save git repositories
87-
path: /tmp/smolgit
80+
path: ./tmp
8881
# Base for clone string formating
8982
# (e.g. ssh://git@my-git-server.lan/myuser/project.git)
9083
base: "git@my-git-server.lan"
9184
users:
92-
# User name used for folder in git.path
9385
- name: "bob"
94-
# Permissions, wildcard or regex
95-
# User to check access for other repositories
96-
# '*' - access for all repositories
97-
# 'admin' - access for admin's repositories
98-
# '(admin|billy)' - access for admin's and billy's repositories
99-
permissions: "*"
86+
password: "$2y$05$US7wXbew8P9d2h8qL3aC6OMhVcwO.1W6U.hVFBGNj9o9YQO.cSqd2" # htpasswd -nbB admin MySecret123
87+
role: "admin"
10088
keys:
101-
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCq9rD9b8tYyuSLsTECHCn... developer@mail.com
89+
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQ... developer@mail.com
10290
```
10391
10492
cli options:

cmd/app.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ func (a *App) initWebServer(staticFiles embed.FS) error {
125125
"message": "pong",
126126
})
127127
})
128+
router.GET("/healthz", func(c *gin.Context) {
129+
c.JSON(http.StatusOK, gin.H{
130+
"message": "ok",
131+
})
132+
})
128133
router.GET("/api/auth_disabled", func(c *gin.Context) {
129134
c.JSON(http.StatusOK, gin.H{
130135
"message": a.Config.ServiceAuthDisabled,
@@ -142,6 +147,7 @@ func (a *App) initWebServer(staticFiles embed.FS) error {
142147
})
143148

144149
router.GET("/api/repos", r.Repos)
150+
router.POST("/api/login", r.Login)
145151
router.GET("/api/repos/files/:user/:path", r.Files)
146152

147153
addr := a.Config.ServerAddr

frontend/src/components/pages/Start.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function StartPage() {
2828

2929
const interval = setInterval(() => {
3030
fetchData();
31-
}, 2000);
31+
}, 5000);
3232

3333
return () => clearInterval(interval);
3434
}, [fetchData]);

frontend/src/context/AuthProvider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
8383

8484
return (
8585
<AuthContext.Provider
86-
value={{ user, token, login, logout, isAuthenticated: true, AuthDisabled: !!authDisabled }}
86+
value={{ user, token, login, logout, isAuthenticated: !!token, AuthDisabled: !!authDisabled }}
8787
>
8888
{children}
8989
</AuthContext.Provider>

frontend/src/store/repositories.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ export const repositoriesState = hookstate<{ repos: object[] }>({
88

99
export async function getRepos(query: string) {
1010
try {
11-
let { repos } = await call<any[]>('repos');
11+
let { items } = await call<any[]>('repos');
1212
if (query !== '') {
13-
repos = repos.filter((c) => {
13+
items = items.filter((c) => {
1414
return String(c.path || '')
1515
.toLowerCase()
1616
.includes(query.toLowerCase());
1717
});
1818
}
19-
repositoriesState.repos.set(repos);
19+
repositoriesState.repos.set(items);
2020
} catch (error: any) {
2121
toast.error('Error! Cant load repos\n' + error.message);
2222
console.error('Error! Cant load repos\n' + error.message);

pkg/config/config.TEMPLATE.yaml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@ git:
2626
users:
2727
# User name used for folder in git.path
2828
- name: "bob"
29-
# Permissions, wildcard or regex
30-
# User to check access for other repositories
31-
# '*' - access for all repositories
32-
# 'admin' - access for admin's repositories
33-
# '(admin|billy)' - access for admin's and billy's repositories
34-
permissions: "*"
29+
password: "" # htpasswd -nbB admin MySecret123
30+
role: "admin"
3531
keys:
3632
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCq9rD9b8tYyuSLsTECHCn... developer@mail.com

pkg/config/config.go

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
_ "embed"
66
"errors"
77
"html/template"
8+
"strings"
89

910
"smolgit/pkg/model"
1011
)
@@ -19,48 +20,34 @@ type Config struct {
1920
ServerAddr string `koanf:"server.addr"`
2021
ServerAuthAccounts []map[string]string `koanf:"server.auth.accounts"`
2122
ServerBrand string `koanf:"server.brand"`
22-
Users []User `koanf:"users"`
23+
Users []model.User `koanf:"git.users"`
2324
SSHAddr string `koanf:"ssh.addr"`
2425
GitPath string `koanf:"git.path"`
2526
GitBase string `koanf:"git.base"`
2627
Version string
2728
}
2829

29-
type User struct {
30-
Username string `yaml:"username"`
31-
Password string `yaml:"password"`
32-
Role string `yaml:"role"`
33-
}
34-
3530
type Users struct {
36-
Users map[string]User
31+
Users map[string]model.User
3732
}
3833

3934
func (c *Config) FindUserByKey(key string) (model.User, error) {
40-
// for _, u := range c.GitUsers {
41-
// for _, k := range u["keys"].([]interface{}) {
42-
// p, _ := u["permissions"].(string)
43-
// if strings.HasPrefix(k.(string), key) {
44-
// return model.User{
45-
// Name: u["name"].(string),
46-
// Permissions: p,
47-
// }, nil
48-
// }
49-
// }
50-
// }
35+
for _, u := range c.Users {
36+
for _, k := range u.Keys {
37+
if strings.HasPrefix(k, key) {
38+
return u, nil
39+
}
40+
}
41+
}
5142
return model.User{}, errors.New("user not found")
5243
}
5344

5445
func (c *Config) FindUserByName(name string) (model.User, error) {
55-
// for _, u := range c.GitUsers {
56-
// if u["name"].(string) == name {
57-
// p, _ := u["permissions"].(string)
58-
// return model.User{
59-
// Name: u["name"].(string),
60-
// Permissions: p,
61-
// }, nil
62-
// }
63-
// }
46+
for _, u := range c.Users {
47+
if u.Name == name {
48+
return u, nil
49+
}
50+
}
6451
return model.User{}, errors.New("user not found")
6552
}
6653

pkg/model/model.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66

77
type User struct {
88
Name string `json:"name,omitempty"`
9+
Password string `json:"password,omitempty"`
10+
Role string `json:"role,omitempty"`
911
Permissions string `json:"permissions,omitempty"`
1012
Repos []*Repository `json:"repos,omitempty"`
1113
Keys []string `json:"keys,omitempty"`

0 commit comments

Comments
 (0)