Skip to content

Commit 1e41b43

Browse files
committed
more dev friendly
- backend automatically seeds data - dev admin users password not automatically generated - postgres without mTLS possible
1 parent 3122569 commit 1e41b43

File tree

5 files changed

+106
-221
lines changed

5 files changed

+106
-221
lines changed

.env

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
ENV="production"
2+
13
PUBLIC_URL=""
24
PEPPER_KEY=""
35

README.md

+28-169
Original file line numberDiff line numberDiff line change
@@ -17,190 +17,49 @@ docker compose up -d && docker compose logs -f
1717
#### frontend
1818
```bash
1919
cd frontend
20-
npm i # on initial startup
21-
npm run codegen # on initial startup and when queries or mutations are changed
20+
npm i
21+
npm run codegen
2222
npm run dev
2323
```
2424

2525
#### backend
26+
> [!IMPORTANT]
27+
> In development, the backend creates some example data and creates an admin user:
28+
29+
> Password: `admin`
2630
```bash
2731
cd server
28-
go generate ./... # on initial startup and when schema.graphql is changed
32+
go generate ./...
2933
go run server.go
3034
```
3135

32-
#### example data
33-
1. visit [localhost:8080/playground](http://localhost:8080/playground)
34-
2. get a session id for the generated admin user by doing a login query
35-
```
36-
{
37-
login (
38-
39-
password: "<generated password from backend logs>"
40-
)
41-
}
42-
```
43-
3. in the playground go to the headers tab and paste the session id
44-
```
45-
{
46-
"SID": "<session id>"
47-
}
48-
```
49-
<details>
50-
<summary>4. trigger the following mutation</summary>
36+
## env vars
5137

52-
mutation exampleData {
53-
tutor1: addUser(user: {mail: "[email protected]", fn: "Tutorin", sn: "One", password: "test1"})
54-
tutor2: addUser(user: {mail: "[email protected]", fn: "Tutor", sn: "Two", password: "test2"})
55-
mmk: addBuilding(
56-
building: {name: "Mathematikon", street: "INF", number: "205", city: "Heidelberg", zip: "69115", latitude: 49.417493, longitude: 8.675197, zoomLevel: 17}
57-
) {
58-
ID
59-
}
60-
kip: addBuilding(
61-
building: {name: "Kirchhoff-Institut für Physik", street: "INF", number: "227", city: "Heidelberg", zip: "69115", latitude: 49.4162501, longitude: 8.6694734, zoomLevel: 17}
62-
) {
63-
ID
64-
}
65-
sr1: addRoom(
66-
room: {number: "101", name: "SR 1", capacity: 20, floor: 2, buildingID: 1}
67-
) {
68-
number
69-
}
70-
sr2: addRoom(room: {number: "2.141", capacity: 35, buildingID: 1}) {
71-
number
72-
}
73-
sr3: addRoom(
74-
room: {number: "503", name: "Labor 1", capacity: 30, floor: 5, buildingID: 2}
75-
) {
76-
number
77-
}
78-
mathe: addLabel(label: {name: "Mathe", color: "#87cefa", kind: TOPIC}) {
79-
name
80-
}
81-
info: addLabel(label: {name: "Informatik", color: "#FFE31A", kind: TOPIC}) {
82-
name
83-
}
84-
allg: addLabel(label: {name: "Allgemein", color: "#5D737E", kind: TOPIC}) {
85-
name
86-
}
87-
tutorial: addLabel(
88-
label: {name: "Tutorium", color: "#ABBA7C", kind: EVENT_TYPE}
89-
) {
90-
name
91-
}
92-
vl: addLabel(label: {name: "Vorlesung", color: "#ffbf00", kind: EVENT_TYPE}) {
93-
name
94-
}
95-
vk: addEvent(
96-
event: {title: "Vorkurs 2025", description: "Lorem Ipsum", from: "2030-02-21T00:00:00Z", to: "2030-02-24T00:00:00Z", needsTutors: true}
97-
) {
98-
ID
99-
}
100-
pvk: addEvent(
101-
event: {title: "Programmiervorkurs 2025", description: "Lorem Ipsum", from: "2030-02-21T00:00:00Z", to: "2030-02-24T00:00:00Z", needsTutors: true}
102-
) {
103-
ID
104-
}
105-
alda: addEvent(
106-
event: {title: "Algorithmen und Datenstrukturen", description: "Lorem Ipsum dolor sit amed", topicName: "Informatik", typeName: "Tutorium", needsTutors: true, from: "2030-02-21T00:00:00Z", to: "2030-02-21T01:00:00Z", umbrellaID: 1}
107-
) {
108-
ID
109-
}
110-
ana: addEvent(
111-
event: {title: "Analysis", description: "Lorem Ipsum dolor sit amed", topicName: "Mathe", typeName: "Vorlesung", needsTutors: true, from: "2030-02-28T00:00:00Z", to: "2030-02-28T02:00:00Z", umbrellaID: 1}
112-
) {
113-
ID
114-
}
115-
sr1vk: addRoomAvailabilityForEvent(
116-
availability: {roomNumber: "101", buildingID: 1, eventID: 3}
117-
) {
118-
number
119-
}
120-
sr2vk: addRoomAvailabilityForEvent(
121-
availability: {roomNumber: "2.141", buildingID: 1, eventID: 3}
122-
) {
123-
number
124-
}
125-
sr3pvk: addRoomAvailabilityForEvent(
126-
availability: {roomNumber: "503", buildingID: 2, eventID: 5}
127-
) {
128-
number
129-
}
130-
t1sr1: addEventAssignmentForTutor(
131-
assignment: {eventID: 3, userMail: "[email protected]", roomNumber: "101", buildingID: 1}
132-
) {
133-
ID
134-
}
135-
t2sr1: addEventAssignmentForTutor(
136-
assignment: {eventID: 3, userMail: "[email protected]", roomNumber: "101", buildingID: 1}
137-
) {
138-
ID
139-
}
140-
t2sr2: addEventAssignmentForTutor(
141-
assignment: {eventID: 3, userMail: "[email protected]", roomNumber: "2.141", buildingID: 1}
142-
) {
143-
ID
144-
}
145-
t1vk: addTutorAvailabilityForEvent(
146-
availability: {userMail: "[email protected]", eventID: [3, 4]}
147-
) {
148-
mail
149-
}
150-
t2vk: addTutorAvailabilityForEvent(
151-
availability: {userMail: "[email protected]", eventID: [3, 4]}
152-
) {
153-
mail
154-
}
155-
addForm(
156-
form: {title: "Beispielregistrierung", description: "Lorem Ipsum dolor sit amed", questions: [{title: "Wie viel Programmiererfahrung hast du?", type: SCALE, required: true, answers: [{title: "Keine", points: 8}, {title: "Ich arbeite an eigenen Projekten", points: 0}]}, {title: "Welche der folgenden Konzepte kennst du noch nicht?", type: MULTIPLE_CHOICE, required: false, answers: [{title: "Variablen", points: 5}, {title: "If-Bedingungen", points: 4}, {title: "For/While-Schleifen", points: 3}, {title: "Klassen", points: 1}]}]}
157-
) {
158-
eventID
159-
}
160-
s1: addSetting(setting: {key: "copyright-notice", value: "Copyright © 2024, Fachschaft MathPhysInfo. All rights reserved.", type: STRING}) { key }
161-
s2: addSetting(setting: {key: "email-greeting", value: "Hey", type: STRING}) { key }
162-
s3: addSetting(setting: {key: "email-signature", value: "Dein", type: STRING}) { key }
163-
s4: addSetting(setting: {key: "email-name", value: "Fachschaft MPI", type: STRING}) { key }
164-
s5: addSetting(setting: {key: "email-confirm-subject", value: "Bestätige deine Registrierung", type: STRING}) { key }
165-
s6: addSetting(setting: {key: "email-confirm-intro", value: "Bitte bestätige deine Registrierung.", type: STRING}) { key }
166-
s7: addSetting(setting: {key: "email-confirm-button-instruction", value: "Klicke auf den Button", type: STRING}) { key }
167-
s8: addSetting(setting: {key: "email-confirm-button-text", value: "Bestätigen", type: STRING}) { key }
168-
s9: addSetting(setting: {key: "email-availability-subject", value: "Verfügbarkeit für Tutorien", type: STRING}) { key }
169-
s10: addSetting(setting: {key: "email-availability-intro", value: "Bitte gib deine Verfügbarkeiten an.", type: STRING}) { key }
170-
s11: addSetting(setting: {key: "email-availability-outro", value: "Danke für deine Rückmeldung!", type: STRING}) { key }
171-
s12: addSetting(setting: {key: "email-assignment-subject", value: "Zuteilung zu Tutorien", type: STRING}) { key }
172-
s13: addSetting(setting: {key: "email-assignment-event-title", value: "Veranstaltung", type: STRING}) { key }
173-
s14: addSetting(setting: {key: "email-assignment-kind-title", value: "Art", type: STRING}) { key }
174-
s15: addSetting(setting: {key: "email-assignment-date-title", value: "Datum", type: STRING}) { key }
175-
s16: addSetting(setting: {key: "email-assignment-time-title", value: "Uhrzeit", type: STRING}) { key }
176-
s17: addSetting(setting: {key: "email-assignment-room-title", value: "Raum", type: STRING}) { key }
177-
s18: addSetting(setting: {key: "email-assignment-building-title", value: "Gebäude", type: STRING}) { key }
178-
s19: addSetting(setting: {key: "email-assignment-intro", value: "Hier ist deine Zuteilung", type: STRING}) { key }
179-
s20: addSetting(setting: {key: "email-assignment-outro", value: "Viel Erfolg!", type: STRING}) { key }
180-
}
181-
</details>
38+
### required
18239

183-
### troubleshooting
184-
If you lost your admin password, delete the database `server/pepp.db` and rerun the server. The admin password is only shown on initial startup in your server logs.
40+
| Key | Description |
41+
| - | - |
42+
| `PUBLIC_URL` | The domain under which pepp is deployed |
43+
| `PEPPER_KEY` | Generate a random 64 characters long string for password security |
44+
| `SMTP_HOST` | E-Mail provider, e.g. `smtp.example.de` |
45+
| `SMTP_USER` | E.g. `[email protected]` |
46+
| `SMTP_PASSWORD` | The password to log into the SMTP Server |
47+
| `SMTP_PORT` | Mostly `465` |
48+
| `FROM_ADDRESS` | Address from which mails are send, e.g. `[email protected]` |
18549

186-
## env vars
50+
### optional
18751

18852
| Key | Description |
18953
| - | - |
190-
| `LOG_LEVEL` (optional) | Default is `Info`. Set to `Debug` for more information |
191-
| `PUBLIC_URL` (required) | The domain under which pepp is deployed |
192-
| `PEPPER_KEY` (required) | Generate a random 64 characters long string for password security |
193-
| `ADMIN_USER` (optional) | Default is `[email protected]`. Generated on initial startup |
194-
| `POSTGRES_HOST` (optional) | When given tries to connect. Creates a SQLite per default |
195-
| `POSTGRES_PASSWORD` (optional) | Required if `POSTGRES_HOST` is given |
196-
| `POSTGRES_PORT` (optional) | Required if `POSTGRES_HOST` is given |
197-
| `POSTGRES_USER` (optional) | Required if `POSTGRES_HOST` is given |
198-
| `POSTGRES_DB` (optional) | Required if `POSTGRES_HOST` is given |
199-
| `SMTP_HOST` (required) | E-Mail provider, e.g. `smtp.example.de` |
200-
| `SMTP_USER` (required) | E.g. `[email protected]` |
201-
| `SMTP_PASSWORD` (required) | The password to log into the SMTP Server |
202-
| `SMTP_PORT` (required) | Mostly `465` |
203-
| `FROM_ADDRESS` (required) | Address from which mails are send, e.g. `[email protected]` |
54+
| `LOG_LEVEL` | Default is `Info`. Set to `Debug` for more information |
55+
| `ENV` | Set to `Production` on deployment |
56+
| `ENABLE_TRACING` | Generate a random 64 characters long string for password security |
57+
| `ADMIN_USER` | Default is `[email protected]`. Generated on initial startup |
58+
| `POSTGRES_HOST` | When given tries to connect. Creates a SQLite per default |
59+
| `POSTGRES_PASSWORD` | Required if `POSTGRES_HOST` is given |
60+
| `POSTGRES_PORT` | Required if `POSTGRES_HOST` is given |
61+
| `POSTGRES_USER` | Required if `POSTGRES_HOST` is given |
62+
| `POSTGRES_DB` | Required if `POSTGRES_HOST` is given |
20463

20564
## contributions
20665
1. [create an issue](https://github.com/FachschaftMathPhysInfo/pepp/issues/new)

server/db/init.go

+42-36
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,39 @@ import (
2020
)
2121

2222
var (
23-
db *bun.DB
24-
sqldb *sql.DB
25-
err error
23+
db *bun.DB
24+
sqldb *sql.DB
25+
err error
26+
relations = []interface{}{
27+
(*models.ApplicationToQuestion)(nil),
28+
(*models.EventToUserAssignment)(nil),
29+
(*models.UserToEventAvailability)(nil),
30+
(*models.UserToEventRegistration)(nil),
31+
(*models.RoomToEventAvailability)(nil)}
32+
33+
tables = []interface{}{
34+
(*models.Label)(nil),
35+
(*models.Event)(nil),
36+
(*models.User)(nil),
37+
(*models.Building)(nil),
38+
(*models.Room)(nil),
39+
(*models.Form)(nil),
40+
(*models.Question)(nil),
41+
(*models.Answer)(nil),
42+
(*models.Application)(nil),
43+
(*models.Setting)(nil)}
2644
)
2745

2846
func Init(ctx context.Context, tracer *trace.TracerProvider) (*bun.DB, *sql.DB, error) {
29-
switch os.Getenv("DATABASE_TYPE") {
30-
case "PostgreSQL":
47+
if os.Getenv("POSTGRES_HOST") != "" {
48+
log.Info("connecting to postgres instance...")
3149
sqldb, err = connectTCPSocket()
3250
if err != nil {
3351
log.Panic("postgres connection failed: ", err)
3452
}
3553
db = bun.NewDB(sqldb, pgdialect.New())
36-
default:
37-
log.Info("no database type specified, using default sqlite")
54+
} else {
55+
log.Info("no db host specified: connecting to default sqlite...")
3856
sqldb, err = sql.Open(sqliteshim.ShimName, "./pepp.db")
3957
if err != nil {
4058
log.Panic("sqlite creation failed: ", err)
@@ -55,25 +73,6 @@ func Init(ctx context.Context, tracer *trace.TracerProvider) (*bun.DB, *sql.DB,
5573
))
5674
}
5775

58-
relations := []interface{}{
59-
(*models.ApplicationToQuestion)(nil),
60-
(*models.EventToUserAssignment)(nil),
61-
(*models.UserToEventAvailability)(nil),
62-
(*models.UserToEventRegistration)(nil),
63-
(*models.RoomToEventAvailability)(nil)}
64-
65-
tables := []interface{}{
66-
(*models.Label)(nil),
67-
(*models.Event)(nil),
68-
(*models.User)(nil),
69-
(*models.Building)(nil),
70-
(*models.Room)(nil),
71-
(*models.Form)(nil),
72-
(*models.Question)(nil),
73-
(*models.Answer)(nil),
74-
(*models.Application)(nil),
75-
(*models.Setting)(nil)}
76-
7776
for _, relation := range relations {
7877
db.RegisterModel(relation)
7978
}
@@ -86,22 +85,21 @@ func Init(ctx context.Context, tracer *trace.TracerProvider) (*bun.DB, *sql.DB,
8685
log.Panic("unable to insert basic connetcion relations in DB: ", err)
8786
}
8887

89-
if err := initAdminUser(ctx); err != nil {
90-
log.Panic("error crating admin user: ", err)
91-
}
92-
9388
return db, sqldb, nil
9489
}
9590

96-
func initAdminUser(ctx context.Context) error {
91+
func InitAdminUser(ctx context.Context, db *bun.DB) error {
9792
mail := os.Getenv("ADMIN_USER")
9893
if mail == "" {
9994
10095
}
10196

102-
password, err := auth.GenerateSalt(32)
103-
if err != nil {
104-
return err
97+
password := "admin"
98+
if os.Getenv("ENV") == "Production" {
99+
password, err = auth.GenerateSalt(32)
100+
if err != nil {
101+
return err
102+
}
105103
}
106104

107105
hash, salt, err := auth.Hash(password)
@@ -159,15 +157,23 @@ func connectTCPSocket() (*sql.DB, error) {
159157
dbUser = mustGetenv("POSTGRES_USER")
160158
dbPwd = mustGetenv("POSTGRES_PASSWORD")
161159
dbName = mustGetenv("POSTGRES_DB")
160+
dbURI string
162161
)
163162

164-
dbURI := fmt.Sprintf("host=%s user=%s password=%s database=%s sslmode=verify-full sslrootcert=root.crt sslcert=client.crt sslkey=client.key",
165-
dbHost, dbUser, dbPwd, dbName)
163+
if _, err := os.Stat("client.crt"); err == nil {
164+
dbURI = fmt.Sprintf("host=%s user=%s password=%s database=%s sslmode=verify-full sslrootcert=root.crt sslcert=client.crt sslkey=client.key",
165+
dbHost, dbUser, dbPwd, dbName)
166+
} else {
167+
168+
dbURI = fmt.Sprintf("host=%s user=%s password=%s database=%s",
169+
dbHost, dbUser, dbPwd, dbName)
170+
}
166171

167172
dbPool, err := sql.Open("postgres", dbURI)
168173
if err != nil {
169174
return nil, fmt.Errorf("sql.Open: %w", err)
170175
}
176+
171177
if err = dbPool.Ping(); err != nil {
172178
log.Fatalf("DB unreachable: %s", err)
173179
}

server/db/seed.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"github.com/uptrace/bun"
1212
)
1313

14-
func seedData(ctx context.Context, db *bun.DB) error {
14+
func SeedData(ctx context.Context, db *bun.DB) error {
1515
users := []*models.User{
1616
{Mail: "[email protected]", Fn: "Tutorin", Sn: "One", Confirmed: true},
1717
{Mail: "[email protected]", Fn: "Tutor", Sn: "Two", Confirmed: true},
@@ -239,8 +239,6 @@ func insertData[T any](ctx context.Context, db *bun.DB, model T, data []T, descr
239239
return fmt.Errorf("%s: %s", description, err)
240240
}
241241
log.Infof("%s seeded successfully\n", description)
242-
} else {
243-
log.Infof("%s already exists, skipping seed\n", description)
244242
}
245243
return nil
246244
}

0 commit comments

Comments
 (0)