Skip to content

Commit 880a8ee

Browse files
committed
chore: migrate genesis backend into repository
move frontend files to src fix urls in index.html
1 parent 8b56be3 commit 880a8ee

51 files changed

Lines changed: 2752 additions & 83 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.env.example‎

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
# Frontend configuration for during development
1+
# ===============================================
2+
# Frontend configuration
3+
# ===============================================
4+
25
# Backend host, usually /api if hosted locally and used via proxy
36
# If omitted and OCULAR_HYBRID_MODE is not set to true, authentication will be required to use the app
47
OCULAR_GENESIS_HOST=/api
@@ -10,3 +13,59 @@ OCULAR_HYBRID_MODE=
1013
# Optional, pre-filled login credentials for local testing
1114
OCULAR_TEST_USERNAME=admin
1215
OCULAR_TEST_PASSWORD=hgEiPCZP
16+
17+
# ===============================================
18+
# Backend configuration
19+
# ===============================================
20+
21+
# Database location
22+
GENESIS_DB_PATH=.data
23+
24+
# JWT secret known only to your token generator
25+
GENESIS_JWT_SECRET=84d62c48dd30ca7caf6561252c5bd919
26+
27+
# JWT expiration in minutes
28+
GENESIS_JWT_TOKEN_EXPIRATION=120000
29+
30+
# If the cookie should be allowed to be sent over http
31+
# Dangerous, it's best to run it behind a reverse proxy with https
32+
GENESIS_JWT_COOKIE_ALLOW_HTTP=true
33+
34+
# Gin mode, either test, release or debug
35+
GENESIS_GIN_MODE=test
36+
37+
# Zap logger, either production or development
38+
GENESIS_LOG_MODE=development
39+
40+
# Port to listen on
41+
GENESIS_PORT=8080
42+
43+
# Base url to listen for requests
44+
GENESIS_BASE_URL=/
45+
46+
# Users to be created on start up.
47+
# Use ! as suffix for the username to indicate that this user should be created as an admin.
48+
# Admins can add, remove and edit users.
49+
GENESIS_CREATE_USERS=admin!:hgEiPCZP
50+
51+
# Allowed username pattern
52+
GENESIS_USERNAME_PATTERN=^[\w]{0,32}$
53+
54+
# Allowed key pattern
55+
GENESIS_KEY_PATTERN=^[\w]{0,32}$
56+
57+
# Maximum size of each key in kilobytes
58+
GENESIS_DATA_MAX_SIZE=512
59+
60+
# Maximum amount of datasets per user
61+
GENESIS_KEYS_PER_USER=3
62+
63+
# Maximum login attempts before temporary lockout for a user.
64+
# They reset after a successful login and are not persistent across restarts.
65+
# Setting it to 0 disables this feature.
66+
GENESIS_LOGIN_MAX_ATTEMPTS=5
67+
68+
# Durations for temporary lockouts after failed login attempts, capped at the last value.
69+
# This is reset after a successful login for each individual user.
70+
# Leaving this empty disables temporary lockouts.
71+
GENESIS_LOGIN_LOCKOUT_DURATIONS=3s,15s,1m,5m,10m,15m

‎.env.genesis‎

Lines changed: 0 additions & 17 deletions
This file was deleted.

‎.github/CONTRIBUTING.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Make sure you have the following installed:
1919

2020
- [Node.js](https://nodejs.org/en/) (LTS version)
2121
- [pnpm](https://pnpm.io/) (for package management)
22-
- [Docker](https://www.docker.com/) (leave out if you want to work only on the frontend)
22+
- [GoLang](https://golang.org/) (leave out if you want to work only on the frontend)
2323

2424
After you've installed the required software, follow these steps:
2525

‎.github/workflows/test.yml‎

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
name: Test
22

33
on:
4-
pull_request:
54
push:
65
tags-ignore:
76
- '**'
@@ -10,11 +9,10 @@ on:
109

1110
env:
1211
HUSKY: 0
13-
GENESIS_VERSION: v1.6.1
1412

1513
jobs:
16-
test:
17-
name: Run unit, lint and e2e tests
14+
test_ocular:
15+
name: Run ocular tests
1816
runs-on: ubuntu-24.04
1917
steps:
2018
- name: Checkout repository
@@ -23,6 +21,11 @@ jobs:
2321
- name: Setup repository
2422
uses: ./.github/workflows/actions/setup-repository
2523

24+
- name: Set up Go
25+
uses: actions/setup-go@v6
26+
with:
27+
go-version: 1.26
28+
2629
- name: Setup ESLint cache
2730
uses: actions/cache@v5
2831
with:
@@ -41,11 +44,16 @@ jobs:
4144
OCULAR_GENESIS_HOST: /api
4245
OCULAR_HYBRID_MODE: true
4346

47+
- name: Prepare .env
48+
run: cp .env.example .env
49+
50+
- name: Install go dependencies
51+
run: go -C genesis mod download
52+
4453
- name: Start genesis and ocular
4554
run: |
46-
docker pull ghcr.io/simonwep/genesis:latest
47-
docker run -p 8080:8080 -v "./.data:/app/.data" --env-file .env.genesis ghcr.io/simonwep/genesis:${{ env.GENESIS_VERSION }} start &
48-
pnpm vite preview &
55+
pnpm dev:backend >genesis.log 2>&1 &
56+
pnpm vite preview >ocular.log 2>&1 &
4957
5058
- name: Wait for genesis to be healthy
5159
uses: ./.github/workflows/actions/wait-for-health-check
@@ -71,3 +79,32 @@ jobs:
7179
name: test-results
7280
path: test-results/
7381
retention-days: 7
82+
83+
- name: Upload service logs
84+
uses: actions/upload-artifact@v7
85+
if: ${{ failure() }}
86+
with:
87+
name: service-logs
88+
path: "*.log"
89+
retention-days: 7
90+
91+
test_genesis:
92+
name: Run genesis tests
93+
runs-on: ubuntu-latest
94+
steps:
95+
- name: Checkout repository
96+
uses: actions/checkout@v6
97+
98+
- name: Set up Go
99+
uses: actions/setup-go@v6
100+
with:
101+
go-version: 1.26
102+
103+
- name: Install dependencies
104+
run: go -C genesis mod download
105+
106+
- name: Build
107+
run: go -C genesis build -v ./...
108+
109+
- name: Test
110+
run: go -C genesis test -v ./...

‎docker/Dockerfile‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
FROM ghcr.io/simonwep/genesis:v1.6.1 AS genesis
1+
FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS genesis
2+
3+
ARG TARGETOS
4+
ARG TARGETARCH
5+
6+
WORKDIR /app
7+
COPY genesis/go.mod genesis/go.sum ./genesis/
8+
9+
RUN cd genesis && \
10+
go mod download
11+
12+
COPY genesis ./genesis
13+
14+
RUN cd genesis && \
15+
GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o genesis
216

317
FROM --platform=$BUILDPLATFORM node:24-alpine AS frontend
418

@@ -41,7 +55,7 @@ ENV GENESIS_KEYS_PER_USER='2'
4155
ENV GENESIS_LOGIN_MAX_ATTEMPTS='5'
4256
ENV GENESIS_LOGIN_LOCKOUT_DURATIONS='1m,5m,15m,30m,1h'
4357

44-
COPY --from=genesis /app/genesis /usr/local/bin/genesis
58+
COPY --from=genesis /app/genesis/genesis /usr/local/bin/genesis
4559
COPY --from=frontend /app/dist /usr/share/caddy
4660

4761
COPY ./docker/Caddyfile /etc/caddy/Caddyfile

‎genesis/.env.test‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
GENESIS_DB_PATH=.data
2+
GENESIS_JWT_SECRET=iD5kvlPDwvrlKZLecoV1lhGN9Em8mDFZ
3+
GENESIS_JWT_TOKEN_EXPIRATION=120960
4+
GENESIS_GIN_MODE=test
5+
GENESIS_LOG_MODE=development
6+
GENESIS_PORT=8080
7+
GENESIS_CREATE_USERS=foo:hgEiPCZP,baz:8d7f6g5h,bar!:EczUR8dn
8+
GENESIS_USERNAME_PATTERN=^[\w]{0,32}$
9+
GENESIS_KEY_PATTERN=^[\w]{0,32}$
10+
GENESIS_DATA_MAX_SIZE=1
11+
GENESIS_KEYS_PER_USER=3
12+
GENESIS_LOGIN_MAX_ATTEMPTS=5
13+
GENESIS_LOGIN_LOCKOUT_DURATIONS=2s,5s,10s

‎genesis/commands/app.go‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package commands
2+
3+
import (
4+
"github.com/simonwep/genesis/core"
5+
"github.com/simonwep/genesis/routes"
6+
"github.com/urfave/cli/v2"
7+
)
8+
9+
func Start(*cli.Context) error {
10+
router := routes.SetupRoutes()
11+
core.InitializeUsers()
12+
13+
if err := router.SetTrustedProxies(nil); err != nil {
14+
return err
15+
} else if err = router.Run("0.0.0.0:" + core.Config.AppPort); err != nil {
16+
return err
17+
}
18+
19+
return nil
20+
}

‎genesis/commands/user.go‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package commands
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/simonwep/genesis/core"
9+
"github.com/urfave/cli/v2"
10+
)
11+
12+
func ListUsers(_ *cli.Context) error {
13+
if users, err := core.GetAllUsers(); err != nil {
14+
return err
15+
} else {
16+
17+
// Print users
18+
for _, user := range users {
19+
fmt.Printf("Name: %v, Admin: %v\n", user.Name, user.Admin)
20+
}
21+
}
22+
23+
return nil
24+
}
25+
26+
func RemoveUser(ctx *cli.Context) error {
27+
return core.DeleteUser(ctx.Args().Get(0))
28+
}
29+
30+
func AddUser(ctx *cli.Context) error {
31+
username, password := ctx.Args().Get(0), ctx.Args().Get(1)
32+
admin := strings.HasSuffix(username, "!")
33+
34+
err := core.CreateUser(core.User{
35+
Name: strings.TrimSuffix(username, "!"),
36+
Admin: admin,
37+
Password: password,
38+
})
39+
40+
if errors.Is(err, core.ErrUserAlreadyExists) {
41+
fmt.Println("User already exists")
42+
return nil
43+
}
44+
45+
return err
46+
}
47+
48+
func UpdateUser(ctx *cli.Context) error {
49+
username := ctx.Args().Get(0)
50+
newPassword := ctx.String("password")
51+
52+
if newPassword == "" {
53+
fmt.Println("No password provided")
54+
return nil
55+
}
56+
57+
err := core.UpdateUser(username, core.PartialUser{
58+
Password: &newPassword,
59+
})
60+
61+
if errors.Is(err, core.ErrUserNotFound) {
62+
fmt.Println("User not found")
63+
return nil
64+
}
65+
66+
return err
67+
}

‎genesis/core/auth.go‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package core
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/golang-jwt/jwt/v5"
8+
"github.com/google/uuid"
9+
)
10+
11+
type JWTClaim struct {
12+
User string `json:"user"`
13+
jwt.RegisteredClaims
14+
}
15+
16+
func CreateAuthToken(user *User) (string, error) {
17+
return jwt.NewWithClaims(jwt.SigningMethodHS256, JWTClaim{
18+
User: user.Name,
19+
RegisteredClaims: jwt.RegisteredClaims{
20+
ExpiresAt: jwt.NewNumericDate(time.Now().Add(Config.JWTExpiration)),
21+
ID: uuid.NewString(),
22+
},
23+
}).SignedString(Config.JWTSecret)
24+
}
25+
26+
func ParseAuthToken(token string) (*JWTClaim, error) {
27+
var claims JWTClaim
28+
29+
_, err := jwt.ParseWithClaims(token, &claims,
30+
func(t *jwt.Token) (interface{}, error) {
31+
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
32+
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
33+
}
34+
return Config.JWTSecret, nil
35+
},
36+
jwt.WithValidMethods([]string{"HS256"}),
37+
)
38+
39+
if len(claims.ID) != 0 {
40+
blacklisted, err := IsTokenBlacklisted(claims.ID)
41+
42+
if blacklisted || err != nil {
43+
return nil, err
44+
}
45+
}
46+
47+
return &claims, err
48+
}

0 commit comments

Comments
 (0)