Skip to content

Commit 5f589f9

Browse files
committed
inject defaults + cleanup
1 parent 9b46ea7 commit 5f589f9

File tree

8 files changed

+213
-86
lines changed

8 files changed

+213
-86
lines changed

backend/app/api/middleware.go

+41-14
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,21 @@ func mwStripTrailingSlash(next http.Handler) http.Handler {
8787
})
8888
}
8989

90+
type StatusRecorder struct {
91+
http.ResponseWriter
92+
Status int
93+
}
94+
95+
func (r *StatusRecorder) WriteHeader(status int) {
96+
r.Status = status
97+
r.ResponseWriter.WriteHeader(status)
98+
}
99+
90100
func (a *app) mwStructLogger(next http.Handler) http.Handler {
91101
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
102+
record := &StatusRecorder{ResponseWriter: w, Status: http.StatusOK}
103+
next.ServeHTTP(record, r)
104+
92105
scheme := "http"
93106
if r.TLS != nil {
94107
scheme = "https"
@@ -101,33 +114,47 @@ func (a *app) mwStructLogger(next http.Handler) http.Handler {
101114
Str("url", url).
102115
Str("method", r.Method).
103116
Str("remote_addr", r.RemoteAddr).
104-
Msgf("[%s] %s", r.Method, url)
105-
next.ServeHTTP(w, r)
117+
Int("status", record.Status).
118+
Msg(url)
106119
})
107120
}
108121

109122
func (a *app) mwSummaryLogger(next http.Handler) http.Handler {
110-
bold := func(s string) string {
111-
return "\033[1m" + s + "\033[0m"
112-
}
113-
114-
pink := func(s string) string {
115-
return "\033[35m" + s + "\033[0m"
116-
}
117-
118-
aqua := func(s string) string {
119-
return "\033[36m" + s + "\033[0m"
123+
bold := func(s string) string { return "\033[1m" + s + "\033[0m" }
124+
orange := func(s string) string { return "\033[33m" + s + "\033[0m" }
125+
aqua := func(s string) string { return "\033[36m" + s + "\033[0m" }
126+
red := func(s string) string { return "\033[31m" + s + "\033[0m" }
127+
green := func(s string) string { return "\033[32m" + s + "\033[0m" }
128+
129+
fmtCode := func(code int) string {
130+
switch {
131+
case code >= 500:
132+
return red(fmt.Sprintf("%d", code))
133+
case code >= 400:
134+
return orange(fmt.Sprintf("%d", code))
135+
case code >= 300:
136+
return aqua(fmt.Sprintf("%d", code))
137+
default:
138+
return green(fmt.Sprintf("%d", code))
139+
}
120140
}
121141

122142
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
143+
record := &StatusRecorder{ResponseWriter: w, Status: http.StatusOK}
144+
next.ServeHTTP(record, r) // Blocks until the next handler returns.
145+
123146
scheme := "http"
124147
if r.TLS != nil {
125148
scheme = "https"
126149
}
127150

128151
url := fmt.Sprintf("%s://%s%s %s", scheme, r.Host, r.RequestURI, r.Proto)
129152

130-
log.Info().Msgf("%s %s", bold(pink("["+r.Method+"]")), aqua(url))
131-
next.ServeHTTP(w, r)
153+
log.Info().
154+
Msgf("%s %s %s",
155+
bold(orange(""+r.Method+"")),
156+
aqua(url),
157+
bold(fmtCode(record.Status)),
158+
)
132159
})
133160
}

backend/internal/services/service_user.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type UserService struct {
2323
repos *repo.AllRepos
2424
}
2525

26+
// RegisterUser creates a new user and group in the data with the provided data. It also bootstraps the user's group
27+
// with default Labels and Locations.
2628
func (svc *UserService) RegisterUser(ctx context.Context, data types.UserRegistration) (*types.UserOut, error) {
2729
group, err := svc.repos.Groups.Create(ctx, data.GroupName)
2830
if err != nil {
@@ -38,7 +40,26 @@ func (svc *UserService) RegisterUser(ctx context.Context, data types.UserRegistr
3840
GroupID: group.ID,
3941
}
4042

41-
return mappers.ToOutUser(svc.repos.Users.Create(ctx, usrCreate))
43+
usr, err := svc.repos.Users.Create(ctx, usrCreate)
44+
if err != nil {
45+
return &types.UserOut{}, err
46+
}
47+
48+
for _, label := range defaultLabels() {
49+
_, err := svc.repos.Labels.Create(ctx, group.ID, label)
50+
if err != nil {
51+
return &types.UserOut{}, err
52+
}
53+
}
54+
55+
for _, location := range defaultLocations() {
56+
_, err := svc.repos.Locations.Create(ctx, group.ID, location)
57+
if err != nil {
58+
return &types.UserOut{}, err
59+
}
60+
}
61+
62+
return mappers.ToOutUser(usr, nil)
4263
}
4364

4465
// GetSelf returns the user that is currently logged in based of the token provided within
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package services
2+
3+
import "github.com/hay-kot/content/backend/internal/types"
4+
5+
func defaultLocations() []types.LocationCreate {
6+
return []types.LocationCreate{
7+
{
8+
Name: "Living Room",
9+
},
10+
{
11+
Name: "Garage",
12+
},
13+
{
14+
Name: "Kitchen",
15+
},
16+
{
17+
Name: "Bedroom",
18+
},
19+
{
20+
Name: "Bathroom",
21+
},
22+
{
23+
Name: "Office",
24+
},
25+
{
26+
Name: "Attic",
27+
},
28+
{
29+
Name: "Basement",
30+
},
31+
}
32+
}
33+
34+
func defaultLabels() []types.LabelCreate {
35+
return []types.LabelCreate{
36+
{
37+
Name: "Appliances",
38+
},
39+
{
40+
Name: "IOT",
41+
},
42+
{
43+
Name: "Electronics",
44+
},
45+
{
46+
Name: "Servers",
47+
},
48+
{
49+
Name: "General",
50+
},
51+
{
52+
Name: "Important",
53+
},
54+
}
55+
}

frontend/composables/use-api.ts

+18-10
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
1-
import { PublicApi } from "~~/lib/api/public";
2-
import { UserApi } from "~~/lib/api/user";
3-
import { Requests } from "~~/lib/requests";
4-
import { useAuthStore } from "~~/stores/auth";
1+
import { PublicApi } from '~~/lib/api/public';
2+
import { UserApi } from '~~/lib/api/user';
3+
import { Requests } from '~~/lib/requests';
4+
import { useAuthStore } from '~~/stores/auth';
55

6-
async function ApiDebugger(r: Response) {
6+
function ApiDebugger(r: Response) {
77
console.table({
8-
"Request Url": r.url,
9-
"Response Status": r.status,
10-
"Response Status Text": r.statusText,
8+
'Request Url': r.url,
9+
'Response Status': r.status,
10+
'Response Status Text': r.statusText,
1111
});
1212
}
1313

1414
export function usePublicApi(): PublicApi {
15-
const requests = new Requests("", "", {}, ApiDebugger);
15+
const requests = new Requests('', '', {});
1616
return new PublicApi(requests);
1717
}
1818

1919
export function useUserApi(): UserApi {
2020
const authStore = useAuthStore();
21-
const requests = new Requests("", () => authStore.token, {}, ApiDebugger);
21+
22+
const requests = new Requests('', () => authStore.token, {});
23+
requests.addResponseInterceptor(ApiDebugger);
24+
requests.addResponseInterceptor(r => {
25+
if (r.status === 401) {
26+
authStore.clearSession();
27+
}
28+
});
29+
2230
return new UserApi(requests);
2331
}

frontend/lib/requests/requests.ts

+14-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ export enum Method {
55
DELETE = 'DELETE',
66
}
77

8+
export type RequestInterceptor = (r: Response) => void;
9+
export type ResponseInterceptor = (r: Response) => void;
10+
811
export interface TResponse<T> {
912
status: number;
1013
error: boolean;
@@ -16,22 +19,24 @@ export class Requests {
1619
private baseUrl: string;
1720
private token: () => string;
1821
private headers: Record<string, string> = {};
19-
private logger?: (response: Response) => void;
22+
private responseInterceptors: ResponseInterceptor[] = [];
23+
24+
addResponseInterceptor(interceptor: ResponseInterceptor) {
25+
this.responseInterceptors.push(interceptor);
26+
}
27+
28+
private callResponseInterceptors(response: Response) {
29+
this.responseInterceptors.forEach(i => i(response));
30+
}
2031

2132
private url(rest: string): string {
2233
return this.baseUrl + rest;
2334
}
2435

25-
constructor(
26-
baseUrl: string,
27-
token: string | (() => string) = '',
28-
headers: Record<string, string> = {},
29-
logger?: (response: Response) => void
30-
) {
36+
constructor(baseUrl: string, token: string | (() => string) = '', headers: Record<string, string> = {}) {
3137
this.baseUrl = baseUrl;
3238
this.token = typeof token === 'string' ? () => token : token;
3339
this.headers = headers;
34-
this.logger = logger;
3540
}
3641

3742
public get<T>(url: string): Promise<TResponse<T>> {
@@ -73,10 +78,7 @@ export class Requests {
7378
}
7479

7580
const response = await fetch(this.url(url), args);
76-
77-
if (this.logger) {
78-
this.logger(response);
79-
}
81+
this.callResponseInterceptors(response);
8082

8183
const data: T = await (async () => {
8284
if (response.status === 204) {

frontend/pages/home.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
</script>
4545

4646
<template>
47-
<BaseContainer class="space-y-16">
47+
<BaseContainer class="space-y-16 pb-16">
4848
<section aria-labelledby="profile-overview-title" class="mt-8">
4949
<div class="overflow-hidden rounded-lg bg-white shadow">
5050
<h2 class="sr-only" id="profile-overview-title">Profile Overview</h2>

0 commit comments

Comments
 (0)