Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lillian committed Dec 17, 2023
0 parents commit ca6e24e
Show file tree
Hide file tree
Showing 44 changed files with 2,536 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

# Devenv
.devenv*
devenv.local.nix

# direnv
.direnv

# pre-commit
.pre-commit-config.yaml

1 change: 1 addition & 0 deletions cmd/apply/.todo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh [email protected] CLI
1 change: 1 addition & 0 deletions cmd/memberizer/.todo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh [email protected] CLI
1 change: 1 addition & 0 deletions cmd/passwdweb/.todo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Web API just for reset password requests - so main web UI does not keep LDAP admin credentials
27 changes: 27 additions & 0 deletions cmd/web/api/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package api

import (
"net/http"

"github.com/go-chi/chi/v5"
)

// todo: service (ldap/db) auth

func RpcRouter() chi.Router {
r := chi.NewRouter()

r.Get("/verify_card", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
})

return r
}

func RestRouter() chi.Router {
r := chi.NewRouter()

// db crud I guess

return r
}
20 changes: 20 additions & 0 deletions cmd/web/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"log"
"members-platform/cmd/web/api"
"members-platform/cmd/web/ui"
"members-platform/internal/db"
"net/http"
)

func main() {
if err := db.Connect(true); err != nil {
log.Fatalln(err)
}
r := ui.Router()
r.Mount("/rpc", api.RpcRouter())
r.Mount("/rest", api.RestRouter())
log.Println("starting web server")
http.ListenAndServe(":18884", r)
}
22 changes: 22 additions & 0 deletions cmd/web/ui/htmx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ui

import (
"log"
"net/http"
)

// todo: integrate this with auth
// todo: check HX-Current-URL and add title to component-only request
func MaybeHtmxComponent(rw http.ResponseWriter, r *http.Request, page string, data any) {
if r.Header.Get("HX-Request") == "true" {
log.Println("got htmx request!")
if err := Page(rw, page, data); err != nil {
log.Println(err)
}
return
}

if err := PageWithShell(r.Context(), rw, page, data); err != nil {
log.Println(err)
}
}
11 changes: 11 additions & 0 deletions cmd/web/ui/pages/apply-success.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- imported, need to replace this with tailwind styles -->
<div class="jumbotron">
<h1>You're good to go!</h1>
<p class="lead">
Thanks for applying to become a member. Operations and the membership have been notified and the process is underway.
</p>
<p class="lead">
We suggest you pay your dues now to prevent any hiccups during memberization:
</p>
<p><a class="btn btn-lg btn-success" href="https://hacklab.to/membership/dues/" role="button">Take me to the dues page</a></p>
</div>
2 changes: 2 additions & 0 deletions cmd/web/ui/pages/apply.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<!-- todo: ask for APPLY_PASSWORD env variable, add to session -->
<p>TODO: put form here</p>
9 changes: 9 additions & 0 deletions cmd/web/ui/pages/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{ if IsMemberLoggedIn .Ctx.AuthLevel }}
<p>Hello, {{ .Ctx.CurrentUsername }}! We don't have much here yet.</p>
<p>Do you want to look at <a class="underline text-blue-600 hover:text-blue-800" href="https://wiki.hacklab.to">the wiki</a>?</p>
{{ else }}
<p>Hello world</p>
<p>more text should go here eventually</p>
<br/>
If you are a current Hacklab member, you can <a class="underline text-blue-600 hover:text-blue-800" href="/login/">log in here</a>. Otherwise, why not <a class="underline text-blue-600 hover:text-blue-800" href="/apply/">become a member</a>?
{{ end }}
44 changes: 44 additions & 0 deletions cmd/web/ui/pages/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{{ if IsLoggedOut .Ctx.AuthLevel }}
<form class="w-full max-w-sm" hx-post="/login/" method="post">
<h2 class="text-xl">Log In</h2>
<br>
<div class="md:flex md:items-center mb-6">
<div class="md:w-1/3">
<label class="block text-gray-500 font-bold md:text-right mb-1 md:mb-0 pr-4" for="inline-full-name">
Username
</label>
</div>
<div class="md:w-2/3">
<input class="bg-gray-200 appearance-none border-2 border-gray-200 rounded w-full py-2 px-4 text-gray-700 leading-tight focus:outline-none focus:bg-white" id="inline-full-name" type="text" name="username">
</div>
</div>
<div class="md:flex md:items-center mb-6">
<div class="md:w-1/3">
<label class="block text-gray-500 font-bold md:text-right mb-1 md:mb-0 pr-4" for="inline-password">
Password
</label>
</div>
<div class="md:w-2/3">
<input class="bg-gray-200 appearance-none border-2 border-gray-200 rounded w-full py-2 px-4 text-gray-700 leading-tight focus:outline-none focus:bg-white" id="inline-password" type="password" name="password">
</div>
</div>
<div class="md:flex md:items-center">
<div class="md:w-1/3"></div>
<div class="md:w-2/3">
<button class="shadow bg-blue-500 hover:bg-blue-400 focus:shadow-outline focus:outline-none text-white font-bold py-2 px-4 rounded" type="submit">
Log in
</button>
<a class="inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" href="/passwd/">
Forgot Password?
</a>
</div>
</div>
{{ if .Data }}
{{ if .Data.Error }}
<p class="text-red-500">Error: {{ .Data.Error }}</p>
{{ end }}
{{ end }}
</form>
{{ else }}
<p>You are already logged in! <a class="inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" href="/">Go Home?</a></p>
{{ end }}
30 changes: 30 additions & 0 deletions cmd/web/ui/pages/passwd.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{{ if IsMemberLoggedIn .Ctx.AuthLevel }}
<p>todo: change password page</p>
{{ else }}
<form class="w-full max-w-sm" hx-post="/passwd/" method="post">
<h2 class="text-xl">Reset your password</h2>
<br>
<input type="hidden" id="type" value="reset" />
<div class="md:flex md:items-center mb-6">
<div class="md:w-1/3">
<label class="block text-gray-500 font-bold md:text-right mb-1 md:mb-0 pr-4" for="inline-full-name">
Username
</label>
</div>
<div class="md:w-2/3">
<input class="bg-gray-200 appearance-none border-2 border-gray-200 rounded w-full py-2 px-4 text-gray-700 leading-tight focus:outline-none focus:bg-white" id="inline-full-name" type="text" name="username">
</div>
</div>
<div class="md:flex md:items-center">
<div class="md:w-1/3"></div>
<div class="md:w-2/3">
<button class="shadow bg-blue-500 hover:bg-blue-400 focus:shadow-outline focus:outline-none text-white font-bold py-2 px-4 rounded" type="submit">
Submit
</button>
</div>
</div>
{{ if .Error }}
<p class="text-red-500">Error: {{ .Error }}</p>
{{ end }}
</form>
{{ end }}
123 changes: 123 additions & 0 deletions cmd/web/ui/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package ui

import (
"encoding/base64"
"log"
"members-platform/internal/auth"
"members-platform/static"
"net/http"
"strings"
"time"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)

func Router() chi.Router {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(auth.AuthenticateHTTP)

registerStaticRoutes(r)
registerStaticPages(r)

// todo: this needs to be POST with CSRF
r.Get("/logout/", func(rw http.ResponseWriter, r *http.Request) {
http.SetCookie(rw, &http.Cookie{
Name: "HL-Session",
Value: "",
Path: "/",
Expires: time.Now().UTC(),
})
http.Redirect(rw, r, "/", http.StatusTemporaryRedirect)
})

r.Post("/login/", func(rw http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
log.Println(err)
}
username := r.Form.Get("username")
password := r.Form.Get("password")
ok, err := auth.AuthenticateUser(username, password)
if err != nil {
MaybeHtmxComponent(rw, r, "login", shit{Error: err.Error()})
return
}
if !ok {
MaybeHtmxComponent(rw, r, "login", shit{Error: "Invalid username or password"})
return
}

http.SetCookie(rw, &http.Cookie{
Name: "HL-Session",
Value: "Basic " + base64.StdEncoding.EncodeToString([]byte(strings.Join([]string{username, password}, ":"))),
HttpOnly: true,
Path: "/",
})

// htmx this is stupid
if r.Header.Get("HX-Request") == "true" {
rw.Header().Set("HX-Location", "/")
} else {
http.Redirect(rw, r, "/", http.StatusFound)
}
})

r.Post("/passwd/", func(rw http.ResponseWriter, r *http.Request) {
token := auth.CreateResetToken("lillian")
_ = auth.SendResetEmail("[email protected]", "lillian", token)

// todo: don't, obviously
err := auth.DoChangePassword(
"uid=lilliantest,ou=people,dc=hacklab,dc=to",
"NotAPassword!!",
"uid=lilliantest,ou=people,dc=hacklab,dc=to",
"newpass1234",
)
data := shit{Error: "ok"}
if err != nil {
data.Error = err.Error()
}
MaybeHtmxComponent(rw, r, "passwd-reset", data)
})

return r
}

func registerStaticRoutes(r chi.Router) {
r.Get("/favicon.ico", func(rw http.ResponseWriter, r *http.Request) {
r.URL.Path = "/static/favicon.ico"
static.Server().ServeHTTP(rw, r)
})

r.Get("/robots.txt", func(rw http.ResponseWriter, _ *http.Request) {
rw.Write([]byte("User-Agent: *\nDisallow: *\nDisallow: /ban-me/admin.php"))
})

r.Get("/static/*", static.Server())
}

func registerStaticPages(r chi.Router) {
pathPages := map[string]string{
"/": "index",
"/login/": "login",
"/passwd/": "passwd",
"/apply/": "apply",
}

for k, v := range pathPages {
// ???
p := k
q := v
r.Get(p, func(rw http.ResponseWriter, r *http.Request) {
if err := PageWithShell(r.Context(), rw, q, nil); err != nil {
log.Println(err)
}
})
}
}

type shit struct {
Error string
}
56 changes: 56 additions & 0 deletions cmd/web/ui/shell.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>{{ .Data.Title }}</title>
<link rel="stylesheet" type="text/css" href="/static/tailwind.css"/>
<link rel="stylesheet" type="text/css" href="/static/style.css"/>
<script src="/static/htmx.min.js"></script>
</head>
<body hx-boost="true" class="bg-white dark:bg-gray-900 dark:text-white">
<nav class="border-gray-200">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<a href="/" class="flex items-center space-x-3 rtl:space-x-reverse">
<img src="/static/HackStencil_horizontal.svg" class="h-8" alt="Hacklab.to Logo" />
</a>
<ul class="font-medium flex flex-col p-4 md:p-0 mt-4 border border-gray-100 rounded-lg bg-gray-50 md:flex-row md:space-x-8 rtl:space-x-reverse md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700">
{{ if IsLoggedOut .Ctx.AuthLevel }}
<li>
<a href="/apply/" class="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Apply</a>
</li>
<li>
<a href="/login/" class="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Login</a>
</li>
{{ end }}
{{ if IsApplicantLoggedIn .Ctx.AuthLevel }}
<li>
<a href="/apply/" class="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Apply</a>
</li>
{{ end }}
{{ if IsMemberLoggedIn .Ctx.AuthLevel }}
<li>
<a href="/logout/" class="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Log Out</a>
</li>
{{ end }}
</ul>
<input id="menu" data-collapse-toggle="navbar-default" type="checkbox" aria-controls="navbar-default" aria-expanded="false" style="display: none" />
<label for="menu" class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600" >
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/>
</svg>
</label>
</div>
</nav>

<div class="wrapper">
<div class="wrapper-inner">
{{ .Data.UnsafeInnerHTML }}
</div>
</div>

<footer class="p-2 text-sm border-gray-200 flex flex-wrap justify-between">
<p>&copy; <a class="text-blue-600 hover:text-blue-800" href="https://hacklab.to">Hacklab.to</a> {{ .Data.CurrentYearForCopyright }} | Bug/problem? Email <code>operations[at]hacklab.to</code></p>
<a class="text-blue-600 hover:text-blue-800" href="https://github.com/hacklabto/members-platform">Source code</a>
</footer>
</body>
Loading

0 comments on commit ca6e24e

Please sign in to comment.