DEVELOPMENT ENVIRONMENT

~liljamo/felu

0ef60cda78aa255496bee7d1b212f31016a78dd9 — Jonni Liljamo 1 year, 1 month ago b18df32
feat: components in multiple files, and i think all endpoints initialized
15 files changed, 603 insertions(+), 243 deletions(-)

R components.templ => components/base.templ
R components_templ.go => components/base_templ.go
A components/index.templ
A components/index_templ.go
A components/login.templ
A components/login_templ.go
A components/manage.templ
A components/manage_templ.go
A components/manageadmin.templ
A components/manageadmin_templ.go
M db/users.go
M felu.go
M handlers/login.go
A middlewares/admin.go
M middlewares/auth.go
R components.templ => components/base.templ +6 -40
@@ 1,4 1,4 @@
package main
package components

import "git.src.quest/~skye/felu-ddns/config"



@@ 6,7 6,7 @@ func serviceName() string {
	return config.FeluConfig.ServiceName
}

templ baseBase(title string) {
templ BaseBase(title string) {
	<!DOCTYPE html>
	<html>
		<head>


@@ 27,8 27,8 @@ templ baseBase(title string) {
	</html>
}

templ base(title string) {
	@baseBase(title) {
templ Base(title string) {
	@BaseBase(title) {
		<div class="flex flex-col w-full items-center p-4">
			<div class="flex flex-col items-center gap-4">
				<a class="text-4xl" href="/">{ serviceName() }</a>


@@ 40,8 40,8 @@ templ base(title string) {
	}
}

templ manageBase(title string) {
	@baseBase(title) {
templ ManageBase(title string) {
	@BaseBase(title) {
		<div class="flex flex-col w-full items-center p-4">
			<div class="flex w-full max-w-5xl items-center gap-4">
				<a href="/manage">{ serviceName() }</a>


@@ 52,37 52,3 @@ templ manageBase(title string) {
		</div>
	}
}

templ index() {
	@base("Index") {
		<div class="bg-rose-200">
			have info about the service here,
			with a login button somewhere.
			<br/>and also something something, basic index page info, bla bla
		</div>
		<!-- some kind of if logged_in thing, and then show a "manage" button instead of a login button -->
	}
}

templ pageLogin() {
	@manageBase("Login") {
		<div class="bg-lime-200">
			<form class="flex flex-col p-2 gap-2" hx-post="/auth/login" hx-target="#login_error">
				<label for="login_email">Email</label>
				<input class="border" type="text" placeholder="..." name="email" id="login_email"/>
				<label for="login_password">Password</label>
				<input class="border" type="password" placeholder="..." name="password" id="login_password"/>
				<div class="text-rose-600 text-center" id="login_error"></div>
				<button class="border p-1" type="submit">Login</button>
			</form>
		</div>
	}
}

templ pageManage() {
	@manageBase("Manage") {
		<div>
			something something manaag
		</div>
	}
}

R components_templ.go => components/base_templ.go +6 -195
@@ 1,6 1,6 @@
// Code generated by templ@(devel) DO NOT EDIT.

package main
package components

//lint:file-ignore SA4006 This context is only used if a nested component is present.



@@ 15,7 15,7 @@ func serviceName() string {
	return config.FeluConfig.ServiceName
}

func baseBase(title string) templ.Component {
func BaseBase(title string) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {


@@ 74,7 74,7 @@ func baseBase(title string) templ.Component {
	})
}

func base(title string) templ.Component {
func Base(title string) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {


@@ 119,7 119,7 @@ func base(title string) templ.Component {
			}
			return err
		})
		err = baseBase(title).Render(templ.WithChildren(ctx, var_6), templBuffer)
		err = BaseBase(title).Render(templ.WithChildren(ctx, var_6), templBuffer)
		if err != nil {
			return err
		}


@@ 130,7 130,7 @@ func base(title string) templ.Component {
	})
}

func manageBase(title string) templ.Component {
func ManageBase(title string) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {


@@ 175,196 175,7 @@ func manageBase(title string) templ.Component {
			}
			return err
		})
		err = baseBase(title).Render(templ.WithChildren(ctx, var_9), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

func index() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_11 := templ.GetChildren(ctx)
		if var_11 == nil {
			var_11 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_12 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div class=\"bg-rose-200\">")
			if err != nil {
				return err
			}
			var_13 := `have info about the service here,`
			_, err = templBuffer.WriteString(var_13)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString(" ")
			if err != nil {
				return err
			}
			var_14 := `with a login button somewhere.`
			_, err = templBuffer.WriteString(var_14)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString(" <br>")
			if err != nil {
				return err
			}
			var_15 := `and also something something, basic index page info, bla bla`
			_, err = templBuffer.WriteString(var_15)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</div> <!--")
			if err != nil {
				return err
			}
			var_16 := ` some kind of if logged_in thing, and then show a "manage" button instead of a login button `
			_, err = templBuffer.WriteString(var_16)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("-->")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = base("Index").Render(templ.WithChildren(ctx, var_12), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

func pageLogin() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_17 := templ.GetChildren(ctx)
		if var_17 == nil {
			var_17 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_18 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div class=\"bg-lime-200\"><form class=\"flex flex-col p-2 gap-2\" hx-post=\"/auth/login\" hx-target=\"#login_error\"><label for=\"login_email\">")
			if err != nil {
				return err
			}
			var_19 := `Email`
			_, err = templBuffer.WriteString(var_19)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</label><input class=\"border\" type=\"text\" placeholder=\"...\" name=\"email\" id=\"login_email\"><label for=\"login_password\">")
			if err != nil {
				return err
			}
			var_20 := `Password`
			_, err = templBuffer.WriteString(var_20)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</label><input class=\"border\" type=\"password\" placeholder=\"...\" name=\"password\" id=\"login_password\"><div class=\"text-rose-600 text-center\" id=\"login_error\"></div><button class=\"border p-1\" type=\"submit\">")
			if err != nil {
				return err
			}
			var_21 := `Login`
			_, err = templBuffer.WriteString(var_21)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</button></form></div>")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = manageBase("Login").Render(templ.WithChildren(ctx, var_18), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

func pageManage() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_22 := templ.GetChildren(ctx)
		if var_22 == nil {
			var_22 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_23 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div>")
			if err != nil {
				return err
			}
			var_24 := `something something manaag`
			_, err = templBuffer.WriteString(var_24)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</div>")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = manageBase("Manage").Render(templ.WithChildren(ctx, var_23), templBuffer)
		err = BaseBase(title).Render(templ.WithChildren(ctx, var_9), templBuffer)
		if err != nil {
			return err
		}

A components/index.templ => components/index.templ +12 -0
@@ 0,0 1,12 @@
package components

templ Index() {
	@Base("Index") {
		<div class="bg-rose-200">
			have info about the service here,
			with a login button somewhere.
			<br/>and also something something, basic index page info, bla bla
		</div>
		<!-- some kind of if logged_in thing, and then show a "manage" button instead of a login button -->
	}
}

A components/index_templ.go => components/index_templ.go +85 -0
@@ 0,0 1,85 @@
// Code generated by templ@(devel) DO NOT EDIT.

package components

//lint:file-ignore SA4006 This context is only used if a nested component is present.

import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"

func Index() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_1 := templ.GetChildren(ctx)
		if var_1 == nil {
			var_1 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_2 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div class=\"bg-rose-200\">")
			if err != nil {
				return err
			}
			var_3 := `have info about the service here,`
			_, err = templBuffer.WriteString(var_3)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString(" ")
			if err != nil {
				return err
			}
			var_4 := `with a login button somewhere.`
			_, err = templBuffer.WriteString(var_4)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString(" <br>")
			if err != nil {
				return err
			}
			var_5 := `and also something something, basic index page info, bla bla`
			_, err = templBuffer.WriteString(var_5)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</div> <!--")
			if err != nil {
				return err
			}
			var_6 := ` some kind of if logged_in thing, and then show a "manage" button instead of a login button `
			_, err = templBuffer.WriteString(var_6)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("-->")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = Base("Index").Render(templ.WithChildren(ctx, var_2), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

A components/login.templ => components/login.templ +16 -0
@@ 0,0 1,16 @@
package components

templ Login() {
	@ManageBase("Login") {
		<div class="bg-lime-200">
			<form class="flex flex-col p-2 gap-2" hx-post="/auth/login" hx-target="#login_error">
				<label for="login_email">Email</label>
				<input class="border" type="text" placeholder="..." name="email" id="login_email"/>
				<label for="login_password">Password</label>
				<input class="border" type="password" placeholder="..." name="password" id="login_password"/>
				<div class="text-rose-600 text-center" id="login_error"></div>
				<button class="border p-1" type="submit">Login</button>
			</form>
		</div>
	}
}

A components/login_templ.go => components/login_templ.go +76 -0
@@ 0,0 1,76 @@
// Code generated by templ@(devel) DO NOT EDIT.

package components

//lint:file-ignore SA4006 This context is only used if a nested component is present.

import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"

func Login() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_1 := templ.GetChildren(ctx)
		if var_1 == nil {
			var_1 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_2 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div class=\"bg-lime-200\"><form class=\"flex flex-col p-2 gap-2\" hx-post=\"/auth/login\" hx-target=\"#login_error\"><label for=\"login_email\">")
			if err != nil {
				return err
			}
			var_3 := `Email`
			_, err = templBuffer.WriteString(var_3)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</label><input class=\"border\" type=\"text\" placeholder=\"...\" name=\"email\" id=\"login_email\"><label for=\"login_password\">")
			if err != nil {
				return err
			}
			var_4 := `Password`
			_, err = templBuffer.WriteString(var_4)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</label><input class=\"border\" type=\"password\" placeholder=\"...\" name=\"password\" id=\"login_password\"><div class=\"text-rose-600 text-center\" id=\"login_error\"></div><button class=\"border p-1\" type=\"submit\">")
			if err != nil {
				return err
			}
			var_5 := `Login`
			_, err = templBuffer.WriteString(var_5)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</button></form></div>")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = ManageBase("Login").Render(templ.WithChildren(ctx, var_2), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

A components/manage.templ => components/manage.templ +18 -0
@@ 0,0 1,18 @@
package components

templ Manage() {
	@ManageBase("Manage") {
		<div>
			something something manaag
			<br/>have the domains of a user listed here, with also the edit stuff and all that
		</div>
	}
}

templ ManageSettings() {
	@ManageBase("Settings") {
		<div>
			user settings here, like updating email and password
		</div>
	}
}

A components/manage_templ.go => components/manage_templ.go +115 -0
@@ 0,0 1,115 @@
// Code generated by templ@(devel) DO NOT EDIT.

package components

//lint:file-ignore SA4006 This context is only used if a nested component is present.

import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"

func Manage() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_1 := templ.GetChildren(ctx)
		if var_1 == nil {
			var_1 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_2 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div>")
			if err != nil {
				return err
			}
			var_3 := `something something manaag`
			_, err = templBuffer.WriteString(var_3)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString(" <br>")
			if err != nil {
				return err
			}
			var_4 := `have the domains of a user listed here, with also the edit stuff and all that`
			_, err = templBuffer.WriteString(var_4)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</div>")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = ManageBase("Manage").Render(templ.WithChildren(ctx, var_2), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

func ManageSettings() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_5 := templ.GetChildren(ctx)
		if var_5 == nil {
			var_5 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_6 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div>")
			if err != nil {
				return err
			}
			var_7 := `user settings here, like updating email and password`
			_, err = templBuffer.WriteString(var_7)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</div>")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = ManageBase("Settings").Render(templ.WithChildren(ctx, var_6), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

A components/manageadmin.templ => components/manageadmin.templ +26 -0
@@ 0,0 1,26 @@
package components

templ ManageAdmin() {
	@ManageBase("ManageAdmin") {
		<div>
			something something admin page
			<br/>probs have like stats and that kinda stuff here
		</div>
	}
}

templ ManageAdminUsers() {
	@ManageBase("ManageAdmin") {
		<div>
			list of users here, with also the ability to add new users
		</div>
	}
}

templ ManageAdminDomains() {
	@ManageBase("ManageAdmin") {
		<div>
			list of all domains on the instance, with owner details and such
		</div>
	}
}

A components/manageadmin_templ.go => components/manageadmin_templ.go +163 -0
@@ 0,0 1,163 @@
// Code generated by templ@(devel) DO NOT EDIT.

package components

//lint:file-ignore SA4006 This context is only used if a nested component is present.

import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"

func ManageAdmin() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_1 := templ.GetChildren(ctx)
		if var_1 == nil {
			var_1 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_2 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div>")
			if err != nil {
				return err
			}
			var_3 := `something something admin page`
			_, err = templBuffer.WriteString(var_3)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString(" <br>")
			if err != nil {
				return err
			}
			var_4 := `probs have like stats and that kinda stuff here`
			_, err = templBuffer.WriteString(var_4)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</div>")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = ManageBase("ManageAdmin").Render(templ.WithChildren(ctx, var_2), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

func ManageAdminUsers() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_5 := templ.GetChildren(ctx)
		if var_5 == nil {
			var_5 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_6 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div>")
			if err != nil {
				return err
			}
			var_7 := `list of users here, with also the ability to add new users`
			_, err = templBuffer.WriteString(var_7)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</div>")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = ManageBase("ManageAdmin").Render(templ.WithChildren(ctx, var_6), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

func ManageAdminDomains() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		templBuffer, templIsBuffer := w.(*bytes.Buffer)
		if !templIsBuffer {
			templBuffer = templ.GetBuffer()
			defer templ.ReleaseBuffer(templBuffer)
		}
		ctx = templ.InitializeContext(ctx)
		var_8 := templ.GetChildren(ctx)
		if var_8 == nil {
			var_8 = templ.NopComponent
		}
		ctx = templ.ClearChildren(ctx)
		var_9 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
			templBuffer, templIsBuffer := w.(*bytes.Buffer)
			if !templIsBuffer {
				templBuffer = templ.GetBuffer()
				defer templ.ReleaseBuffer(templBuffer)
			}
			_, err = templBuffer.WriteString("<div>")
			if err != nil {
				return err
			}
			var_10 := `list of all domains on the instance, with owner details and such`
			_, err = templBuffer.WriteString(var_10)
			if err != nil {
				return err
			}
			_, err = templBuffer.WriteString("</div>")
			if err != nil {
				return err
			}
			if !templIsBuffer {
				_, err = io.Copy(w, templBuffer)
			}
			return err
		})
		err = ManageBase("ManageAdmin").Render(templ.WithChildren(ctx, var_9), templBuffer)
		if err != nil {
			return err
		}
		if !templIsBuffer {
			_, err = templBuffer.WriteTo(w)
		}
		return err
	})
}

M db/users.go => db/users.go +18 -3
@@ 51,11 51,12 @@ func CreateAdmin(email string, pwd string) error {
}

type User struct {
	Ulid  string
	Email string
	Ulid    string
	Email   string
	IsAdmin bool
}

func FetchUser(email string, pwd string) (*User, error) {
func FetchUserWithCreds(email string, pwd string) (*User, error) {
	user := User{ Email: email }
	var encodedPwd string
	err := DBConn.QueryRow(`SELECT ulid, pwd FROM users WHERE email = $1`,


@@ 77,3 78,17 @@ func FetchUser(email string, pwd string) (*User, error) {

	return &user, nil
}

func FetchUserWithUlid(ulid string) (*User, error) {
	user := User{ Ulid: ulid }
	err := DBConn.QueryRow(`SELECT email, is_admin FROM users WHERE ulid = $1`,
		ulid).Scan(&user.Email, &user.IsAdmin)
	if err == sql.ErrNoRows {
		return nil, errors.New("User not found")
	}
	if err != nil {
		return nil, errors.New("User query failed")
	}

	return &user, nil
}

M felu.go => felu.go +22 -4
@@ 13,6 13,7 @@ import (
	"strconv"
	"time"

	"git.src.quest/~skye/felu-ddns/components"
	"git.src.quest/~skye/felu-ddns/config"
	"git.src.quest/~skye/felu-ddns/db"
	"git.src.quest/~skye/felu-ddns/dns"


@@ 63,11 64,11 @@ func setupFrontendRouter() *gin.Engine {
	r.HTMLRender = &TemplRender{}

	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "", index())
		c.HTML(http.StatusOK, "", components.Index())
	})

	r.GET("/login", middlewares.SessionExists(sessionManager), func(c *gin.Context) {
		c.HTML(http.StatusOK, "", pageLogin())
		c.HTML(http.StatusOK, "", components.Login())
	})

	auth := r.Group("/auth")


@@ 81,9 82,26 @@ func setupFrontendRouter() *gin.Engine {
	manage := r.Group("/manage").Use(middlewares.SessionExists(sessionManager))
	{
		manage.GET("/", func(c *gin.Context) {
			c.HTML(http.StatusOK, "", pageManage())
			c.HTML(http.StatusOK, "", components.Manage())
		})
		manage.GET("/settings", func(c *gin.Context) {
			c.HTML(http.StatusOK, "", components.ManageSettings())
		})
	}
	manageAdmin := r.Group("/manage/admin").Use(
		middlewares.SessionExists(sessionManager),
		middlewares.AdminOnly(),
	)
	{
		manageAdmin.GET("/", func(c *gin.Context) {
			c.HTML(http.StatusOK, "", components.ManageAdmin())
		})
		manageAdmin.GET("/users", func(c *gin.Context) {
			c.HTML(http.StatusOK, "", components.ManageAdminUsers())
		})
		manageAdmin.GET("/domains", func(c *gin.Context) {
			c.HTML(http.StatusOK, "", components.ManageAdminDomains())
		})
		manage.GET("/settings")
	}

	return r

M handlers/login.go => handlers/login.go +1 -1
@@ 28,7 28,7 @@ func AuthLogin(sm *scs.SessionManager, c *gin.Context) {
		return
	}

	user, err := db.FetchUser(data.Email, data.Password)
	user, err := db.FetchUserWithCreds(data.Email, data.Password)
	if err != nil {
		c.String(http.StatusUnauthorized, err.Error())
		return

A middlewares/admin.go => middlewares/admin.go +37 -0
@@ 0,0 1,37 @@
/*
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * This file is licensed under AGPL-3.0-or-later, see NOTICE and LICENSE for
 * more information.
 */
package middlewares

import (
	"net/http"

	"git.src.quest/~skye/felu-ddns/db"
	"github.com/gin-gonic/gin"
)

func AdminOnly() gin.HandlerFunc {
	return func(c *gin.Context) {
		user_id, exists := c.Get("user_id")
		if exists {
			user, err := db.FetchUserWithUlid(user_id.(string))
			if err == nil {
				if user.IsAdmin {
					c.Next()
				} else {
					c.Redirect(http.StatusTemporaryRedirect, "/manage")
					c.Abort()
				}
			} else {
				c.String(http.StatusInternalServerError, "This should not be possible, but don't quote me on that, S01E02")
				c.Abort()
			}
		} else {
			c.String(http.StatusInternalServerError, "This should not be possible, but don't quote me on that, S01E01")
			c.Abort()
		}
	}
}

M middlewares/auth.go => middlewares/auth.go +2 -0
@@ 21,6 21,8 @@ func SessionExists(sm *scs.SessionManager) gin.HandlerFunc {
				c.Redirect(http.StatusTemporaryRedirect, "/manage")
				c.Abort()
			} else {
				// Set user_id in context, if needed later (e.g. AdminOnly middleware)
				c.Set("user_id", user_id)
				// TODO: Validate in db?
				c.Next()
			}