hlfw.ca

webbing

Download patch

ref: 28dad660185dad7e96f5e4d78799ecac1b817277
parent: 0f6b28b1933ccf1de2ad1493d3f941b760c07cdd
author: halfwit <michaelmisch1985@gmail.com>
date: Thu Aug 6 05:35:43 PDT 2020

Add forms

--- a/README.md
+++ /dev/null
@@ -1,10 +1,0 @@
-# forms
-
-Contains form validation code
-
- - Any code that has validation scopes which are poorly defined, especially dates, must be tested with sanity checks.
-   - Dates for symptoms cannot have an onset in the future
-   - Birthdates cannot be older than 117 years
-   - binary choices, such as true false, must test to show other values cannot be entered
- 
- - The util has helper functions for writing your unit tests. See existing tests for canonical usage
--- a/doctor/application.go
+++ /dev/null
@@ -1,79 +1,0 @@
-package forms
-
-import (
-	"fmt"
-	"net/http"
-
-	"github.com/albrow/forms"
-	"github.com/olmaxmedical/plugins"
-	"github.com/olmaxmedical/router"
-	"golang.org/x/text/message"
-)
-
-func init() {
-	b := &router.Form{
-		Access:    router.GuestAuth,
-		Path:      "doctor/application",
-		Validator: application,
-		Redirect:  "/index.html",
-		After:     plugins.EmailForm | plugins.Countries | plugins.Services | plugins.FormToken,
-	}
-	router.AddPost(b)
-}
-
-func application(r *http.Request, p *message.Printer) []string {
-	var errors []string
-
-	data, err := forms.ParseMax(r, r.ContentLength)
-	if err != nil {
-		errors = append(errors, fmt.Sprintf("validation error %v", err))
-		return errors
-	}
-
-	val := data.Validator()
-	val.Require("gender").Message(p.Sprint("Please select a biological gender"))
-
-	if r.PostFormValue("gender") != "male" && r.PostFormValue("gender") != "female" {
-		val.AddError("gender", p.Sprint("Invalid selection for gender"))
-	}
-
-	val.RequireFile("cv").Message(p.Sprint("Empty or missing CV"))
-	val.AcceptFileExts("cv", "application/msword,applicationvnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf").Message(p.Sprint("unsupported filetype for cv"))
-	val.RequireFile("diploma").Message(p.Sprint("Empty or missing Diploma/Board Certification"))
-	val.AcceptFileExts("diploma", "application/msword,applicationvnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf").Message(p.Sprint("unsupported filetype for diploma"))
-
-	for i := 1; i < 12; i++ {
-		num := fmt.Sprintf("q%d", i)
-
-		sel, ok := r.Form[num]
-		if !ok {
-			val.AddError(num, p.Sprintf("No selection for question %d", i))
-			continue
-		}
-
-		if sel[0] == "Yes" || sel[0] == "yes" || sel[0] == "no" || sel[0] == "No" {
-			continue
-		}
-
-		val.AddError(num, p.Sprintf("Invalid selection for question %d", i))
-	}
-
-	val.Require("email").Message(p.Sprintf("Valid email required"))
-	val.MatchEmail("email").Message(p.Sprintf("Invalid email"))
-	val.Require("name").Message(p.Sprintf("Full name required"))
-	val.MinLength("name", 2).Message(p.Sprintf("Full name must be at least 2 characters"))
-
-	if r.PostFormValue("redFlag") != "on" {
-		val.AddError("redFlag", p.Sprint("Invalid selection for confirm element"))
-	}
-
-	if val.HasErrors() {
-		errors = append(errors, val.Messages()...)
-	}
-
-	r.Form["pagetitle"] = []string{"Application for doctor"}
-	r.Form["sendto"] = []string{"olmaxmedical@gmail.com"}
-	delete(r.Form, "redFlag")
-
-	return errors
-}
--- a/doctor/application_test.go
+++ /dev/null
@@ -1,94 +1,0 @@
-package forms
-
-import (
-	"bytes"
-	"errors"
-	"fmt"
-	"mime/multipart"
-	"net/http"
-	"testing"
-
-	"github.com/olmaxmedical/forms/util"
-	"golang.org/x/text/message"
-)
-
-func TestApplication(t *testing.T) {
-	fields := map[string]string{
-		"qs":      "no",
-		"gender":  "male",
-		"email":   "foo@bar.ca",
-		"name":    "Doctor Octopus",
-		"redFlag": "on",
-	}
-
-	if e := testApplication(t, fields); e != nil {
-		t.Error(e)
-	}
-
-	fields["gender"] = "pineapple"
-	if e := testApplication(t, fields); e == nil {
-		t.Error("invalid field accepted")
-	}
-
-	fields["gender"] = "male"
-	fields["email"] = "foo@bar@ca"
-	if e := testApplication(t, fields); e == nil {
-		t.Error("invalid field accepted")
-	}
-
-	fields["email"] = "foo@bar.ca"
-	fields["qs"] = "true"
-	if e := testApplication(t, fields); e == nil {
-		t.Error("invalid field accepted")
-	}
-}
-
-func testApplication(t *testing.T, fields map[string]string) error {
-	var reqBody bytes.Buffer
-
-	mpw := multipart.NewWriter(&reqBody)
-	files := map[string]string{
-		"cv":      "resume.pdf",
-		"diploma": "certificate.pdf",
-	}
-
-	for key, value := range files {
-		if e := util.WriteFile(mpw, key, "../resources/"+value); e != nil {
-			panic(e)
-		}
-	}
-
-	for key, value := range fields {
-		if key == "qs" {
-			for i := 0; i < 12; i++ {
-				key = fmt.Sprintf("q%d", i)
-				if e := mpw.WriteField(key, value); e != nil {
-					panic(e)
-				}
-			}
-			continue
-		}
-
-		if e := mpw.WriteField(key, value); e != nil {
-			panic(e)
-		}
-	}
-
-	request := util.BuildMultiRequest(mpw, &reqBody)
-	printer := message.NewPrinter(message.MatchLanguage("en"))
-	return runTest(request, printer)
-}
-
-func runTest(request *http.Request, printer *message.Printer) error {
-	for _, err := range application(request, printer) {
-		switch err {
-		case "unsupported filetype for cv":
-		case "unsupported filetype for diploma":
-		default:
-			return errors.New(err)
-		}
-
-	}
-
-	return nil
-}
--- a/doctor/profile.go
+++ /dev/null
@@ -1,53 +1,0 @@
-package forms
-
-import (
-	"net/http"
-	"time"
-
-	"github.com/albrow/forms"
-	"github.com/olmaxmedical/plugins"
-	"github.com/olmaxmedical/router"
-	"golang.org/x/text/message"
-)
-
-func init() {
-	b := &router.Form{
-		Access:    router.DoctorAuth,
-		Path:      "doctor/profile",
-		Validator: profile,
-		Redirect:  "/doctor/profile.html",
-		After:     plugins.FormToken | plugins.AddAppointment,
-	}
-	router.AddPost(b)
-}
-
-func profile(r *http.Request, p *message.Printer) []string {
-	var errors []string
-	data, err := forms.ParseMax(r, r.ContentLength)
-	if err != nil {
-		errors = append(errors, "Internal server error")
-		return errors
-	}
-	val := data.Validator()
-	val.Require("BTCperU").Message(p.Sprint("Please enter a rate (Bitcoin/15min)"))
-	bcu := data.GetFloat("BTCperU")
-	if 0.0 > bcu || bcu > 1.0 {
-		val.AddError("BTCperU", p.Sprint("BTC/15min rate out of range"))
-	}
-	val.Require("startDate").Message(p.Sprint("Start date required"))
-	_, err = time.Parse("2006-01-02T15:04:05", r.Form.Get("startDate"))
-	if err != nil {
-		val.AddError("startDate", p.Sprint("Invalid start-date entered"))
-	}
-
-	val.Require("endDate").Message(p.Sprint("End date required"))
-	_, err = time.Parse("2006-01-02T15:04:05", r.Form.Get("endDate"))
-	if err != nil {
-		val.AddError("endDate", p.Sprint("Invalid end-date entered"))
-	}
-
-	if val.HasErrors() {
-		errors = append(errors, val.Messages()...)
-	}
-	return errors
-}
--- a/doctor/profile_test.go
+++ /dev/null
@@ -1,31 +1,0 @@
-package forms
-
-import (
-	"net/url"
-	"testing"
-
-	"github.com/olmaxmedical/forms/util"
-)
-
-func TestProfile(t *testing.T) {
-	values := url.Values{}
-
-	values.Add("BTCperU", "0.1234")
-	values.Add("startDate", "2020-04-04T00:00:00")
-	values.Add("endDate", "2020-06-06T00:00:00")
-
-	if e := util.TestValues(values, profile); e != nil {
-		t.Error(e)
-	}
-
-	values.Set("BTCperU", "-1")
-	if e := util.TestValues(values, profile); e == nil {
-		t.Error("invalid BTC rate allowed")
-	}
-
-	values.Set("BTCperU", "0.1234")
-	values.Set("startDate", "1995-30-30T23:23:59")
-	if e := util.TestValues(values, profile); e == nil {
-		t.Error("invalid date allowed")
-	}
-}
--- /dev/null
+++ b/forms/doctor/application.go
@@ -1,0 +1,79 @@
+package forms
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/albrow/forms"
+	"github.com/olmaxmedical/plugins"
+	"github.com/olmaxmedical/router"
+	"golang.org/x/text/message"
+)
+
+func init() {
+	b := &router.Form{
+		Access:    router.GuestAuth,
+		Path:      "doctor/application",
+		Validator: application,
+		Redirect:  "/index.html",
+		After:     plugins.EmailForm | plugins.Countries | plugins.Services | plugins.FormToken,
+	}
+	router.AddPost(b)
+}
+
+func application(r *http.Request, p *message.Printer) []string {
+	var errors []string
+
+	data, err := forms.ParseMax(r, r.ContentLength)
+	if err != nil {
+		errors = append(errors, fmt.Sprintf("validation error %v", err))
+		return errors
+	}
+
+	val := data.Validator()
+	val.Require("gender").Message(p.Sprint("Please select a biological gender"))
+
+	if r.PostFormValue("gender") != "male" && r.PostFormValue("gender") != "female" {
+		val.AddError("gender", p.Sprint("Invalid selection for gender"))
+	}
+
+	val.RequireFile("cv").Message(p.Sprint("Empty or missing CV"))
+	val.AcceptFileExts("cv", "application/msword,applicationvnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf").Message(p.Sprint("unsupported filetype for cv"))
+	val.RequireFile("diploma").Message(p.Sprint("Empty or missing Diploma/Board Certification"))
+	val.AcceptFileExts("diploma", "application/msword,applicationvnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf").Message(p.Sprint("unsupported filetype for diploma"))
+
+	for i := 1; i < 12; i++ {
+		num := fmt.Sprintf("q%d", i)
+
+		sel, ok := r.Form[num]
+		if !ok {
+			val.AddError(num, p.Sprintf("No selection for question %d", i))
+			continue
+		}
+
+		if sel[0] == "Yes" || sel[0] == "yes" || sel[0] == "no" || sel[0] == "No" {
+			continue
+		}
+
+		val.AddError(num, p.Sprintf("Invalid selection for question %d", i))
+	}
+
+	val.Require("email").Message(p.Sprintf("Valid email required"))
+	val.MatchEmail("email").Message(p.Sprintf("Invalid email"))
+	val.Require("name").Message(p.Sprintf("Full name required"))
+	val.MinLength("name", 2).Message(p.Sprintf("Full name must be at least 2 characters"))
+
+	if r.PostFormValue("redFlag") != "on" {
+		val.AddError("redFlag", p.Sprint("Invalid selection for confirm element"))
+	}
+
+	if val.HasErrors() {
+		errors = append(errors, val.Messages()...)
+	}
+
+	r.Form["pagetitle"] = []string{"Application for doctor"}
+	r.Form["sendto"] = []string{"olmaxmedical@gmail.com"}
+	delete(r.Form, "redFlag")
+
+	return errors
+}
--- /dev/null
+++ b/forms/doctor/application_test.go
@@ -1,0 +1,94 @@
+package forms
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"mime/multipart"
+	"net/http"
+	"testing"
+
+	"github.com/olmaxmedical/forms/util"
+	"golang.org/x/text/message"
+)
+
+func TestApplication(t *testing.T) {
+	fields := map[string]string{
+		"qs":      "no",
+		"gender":  "male",
+		"email":   "foo@bar.ca",
+		"name":    "Doctor Octopus",
+		"redFlag": "on",
+	}
+
+	if e := testApplication(t, fields); e != nil {
+		t.Error(e)
+	}
+
+	fields["gender"] = "pineapple"
+	if e := testApplication(t, fields); e == nil {
+		t.Error("invalid field accepted")
+	}
+
+	fields["gender"] = "male"
+	fields["email"] = "foo@bar@ca"
+	if e := testApplication(t, fields); e == nil {
+		t.Error("invalid field accepted")
+	}
+
+	fields["email"] = "foo@bar.ca"
+	fields["qs"] = "true"
+	if e := testApplication(t, fields); e == nil {
+		t.Error("invalid field accepted")
+	}
+}
+
+func testApplication(t *testing.T, fields map[string]string) error {
+	var reqBody bytes.Buffer
+
+	mpw := multipart.NewWriter(&reqBody)
+	files := map[string]string{
+		"cv":      "resume.pdf",
+		"diploma": "certificate.pdf",
+	}
+
+	for key, value := range files {
+		if e := util.WriteFile(mpw, key, "../resources/"+value); e != nil {
+			panic(e)
+		}
+	}
+
+	for key, value := range fields {
+		if key == "qs" {
+			for i := 0; i < 12; i++ {
+				key = fmt.Sprintf("q%d", i)
+				if e := mpw.WriteField(key, value); e != nil {
+					panic(e)
+				}
+			}
+			continue
+		}
+
+		if e := mpw.WriteField(key, value); e != nil {
+			panic(e)
+		}
+	}
+
+	request := util.BuildMultiRequest(mpw, &reqBody)
+	printer := message.NewPrinter(message.MatchLanguage("en"))
+	return runTest(request, printer)
+}
+
+func runTest(request *http.Request, printer *message.Printer) error {
+	for _, err := range application(request, printer) {
+		switch err {
+		case "unsupported filetype for cv":
+		case "unsupported filetype for diploma":
+		default:
+			return errors.New(err)
+		}
+
+	}
+
+	return nil
+}
--- /dev/null
+++ b/forms/doctor/profile.go
@@ -1,0 +1,53 @@
+package forms
+
+import (
+	"net/http"
+	"time"
+
+	"github.com/albrow/forms"
+	"github.com/olmaxmedical/plugins"
+	"github.com/olmaxmedical/router"
+	"golang.org/x/text/message"
+)
+
+func init() {
+	b := &router.Form{
+		Access:    router.DoctorAuth,
+		Path:      "doctor/profile",
+		Validator: profile,
+		Redirect:  "/doctor/profile.html",
+		After:     plugins.FormToken | plugins.AddAppointment,
+	}
+	router.AddPost(b)
+}
+
+func profile(r *http.Request, p *message.Printer) []string {
+	var errors []string
+	data, err := forms.ParseMax(r, r.ContentLength)
+	if err != nil {
+		errors = append(errors, "Internal server error")
+		return errors
+	}
+	val := data.Validator()
+	val.Require("BTCperU").Message(p.Sprint("Please enter a rate (Bitcoin/15min)"))
+	bcu := data.GetFloat("BTCperU")
+	if 0.0 > bcu || bcu > 1.0 {
+		val.AddError("BTCperU", p.Sprint("BTC/15min rate out of range"))
+	}
+	val.Require("startDate").Message(p.Sprint("Start date required"))
+	_, err = time.Parse("2006-01-02T15:04:05", r.Form.Get("startDate"))
+	if err != nil {
+		val.AddError("startDate", p.Sprint("Invalid start-date entered"))
+	}
+
+	val.Require("endDate").Message(p.Sprint("End date required"))
+	_, err = time.Parse("2006-01-02T15:04:05", r.Form.Get("endDate"))
+	if err != nil {
+		val.AddError("endDate", p.Sprint("Invalid end-date entered"))
+	}
+
+	if val.HasErrors() {
+		errors = append(errors, val.Messages()...)
+	}
+	return errors
+}
--- /dev/null
+++ b/forms/doctor/profile_test.go
@@ -1,0 +1,31 @@
+package forms
+
+import (
+	"net/url"
+	"testing"
+
+	"github.com/olmaxmedical/forms/util"
+)
+
+func TestProfile(t *testing.T) {
+	values := url.Values{}
+
+	values.Add("BTCperU", "0.1234")
+	values.Add("startDate", "2020-04-04T00:00:00")
+	values.Add("endDate", "2020-06-06T00:00:00")
+
+	if e := util.TestValues(values, profile); e != nil {
+		t.Error(e)
+	}
+
+	values.Set("BTCperU", "-1")
+	if e := util.TestValues(values, profile); e == nil {
+		t.Error("invalid BTC rate allowed")
+	}
+
+	values.Set("BTCperU", "0.1234")
+	values.Set("startDate", "1995-30-30T23:23:59")
+	if e := util.TestValues(values, profile); e == nil {
+		t.Error("invalid date allowed")
+	}
+}
--- /dev/null
+++ b/forms/login.go
@@ -1,0 +1,39 @@
+package forms
+
+import (
+	"net/http"
+
+	"github.com/albrow/forms"
+	"github.com/olmaxmedical/plugins"
+	"github.com/olmaxmedical/router"
+	"golang.org/x/text/message"
+)
+
+func init() {
+	b := &router.Form{
+		Access:    router.GuestAuth,
+		Path:      "login",
+		Validator: login,
+		After:     plugins.ValidateLogin,
+		Redirect:  "/profile.html",
+	}
+	router.AddPost(b)
+}
+
+func login(r *http.Request, p *message.Printer) []string {
+	var errors []string
+	data, err := forms.Parse(r)
+	if err != nil {
+		errors = append(errors, p.Sprint("Internal server error"))
+		return errors
+	}
+	val := data.Validator()
+	val.Require("email").Message(p.Sprint("Username required"))
+	val.MatchEmail("email").Message(p.Sprint("User name must be a valid email"))
+	val.Require("pass").Message(p.Sprint("Password required"))
+	val.MinLength("pass", 8).Message(p.Sprint("Password must be at least 8 characters"))
+	if val.HasErrors() {
+		errors = append(errors, val.Messages()...)
+	}
+	return errors
+}
--- /dev/null
+++ b/forms/newpassword.go
@@ -1,0 +1,42 @@
+package forms
+
+import (
+	"net/http"
+
+	"github.com/albrow/forms"
+	"github.com/olmaxmedical/plugins"
+	"github.com/olmaxmedical/router"
+	"golang.org/x/text/message"
+)
+
+func init() {
+	b := &router.Form{
+		Access:    router.GuestAuth,
+		Path:      "newpassword",
+		Validator: newPassword,
+		Redirect:  "/login.html",
+		After:     plugins.ResetPassword | plugins.FormToken,
+	}
+	router.AddPost(b)
+}
+
+func newPassword(r *http.Request, p *message.Printer) []string {
+	var errors []string
+	data, err := forms.Parse(r)
+	if err != nil {
+		errors = append(errors, "Internal server error")
+		return errors
+	}
+	val := data.Validator()
+	val.Require("password").Message(p.Sprintf("Password required"))
+	val.MinLength("password", 8).Message(p.Sprintf("Password must be at least 8 characters"))
+	val.Require("reenter").Message(p.Sprintf("Re-enter same password"))
+	val.MinLength("reenter", 8).Message(p.Sprintf("Password must be at least 8 characters"))
+	if val.HasErrors() {
+		errors = append(errors, val.Messages()...)
+	}
+	if data.Get("reenter") != data.Get("password") {
+		errors = append(errors, p.Sprint("Passwords do not match"))
+	}
+	return errors
+}
--- /dev/null
+++ b/forms/patient/offer.go
@@ -1,0 +1,53 @@
+package forms
+
+import (
+	"net/http"
+	"time"
+
+	"github.com/albrow/forms"
+	"github.com/olmaxmedical/plugins"
+	"github.com/olmaxmedical/router"
+	"golang.org/x/text/message"
+)
+
+func init() {
+	b := &router.Form{
+		Access:    router.PatientAuth,
+		Path:      "patient/offer",
+		Validator: offer,
+		After:     plugins.Search | plugins.Services, //|plugins.Offer
+		Redirect:  "results.html",
+	}
+	router.AddPost(b)
+}
+
+func offer(r *http.Request, p *message.Printer) []string {
+	var errors []string
+	data, err := forms.ParseMax(r, r.ContentLength)
+	if err != nil {
+		errors = append(errors, p.Sprint("Internal server error"))
+		return errors
+	}
+	val := data.Validator()
+	val.Require("Amount").Message(p.Sprint("Please enter a target rate (Bitcoin/15min)"))
+	bcu := data.GetFloat("Amount")
+	if 0.0 > bcu || bcu > 1.0 {
+		val.AddError("Amount", p.Sprint("BTC/15min rate out of range"))
+	}
+	val.Require("startDate").Message(p.Sprint("Start date required"))
+	_, err = time.Parse("2006-01-02T15:04:05", r.Form.Get("startDate"))
+	if err != nil {
+		val.AddError("startDate", p.Sprint("Invalid start-date entered"))
+	}
+
+	val.Require("endDate").Message(p.Sprint("End date required"))
+	_, err = time.Parse("2006-01-02T15:04:05", r.Form.Get("endDate"))
+	if err != nil {
+		val.AddError("endDate", p.Sprint("Invalid end-date entered"))
+	}
+
+	if val.HasErrors() {
+		errors = append(errors, val.Messages()...)
+	}
+	return errors
+}
--- /dev/null
+++ b/forms/patient/offer_test.go
@@ -1,0 +1,31 @@
+package forms
+
+import (
+	"net/url"
+	"testing"
+
+	"github.com/olmaxmedical/forms/util"
+)
+
+func TestOffer(t *testing.T) {
+	values := url.Values{}
+
+	values.Add("Amount", "0.1234")
+	values.Add("startDate", "2020-04-04T00:00:00")
+	values.Add("endDate", "2020-06-06T00:00:00")
+
+	if e := util.TestValues(values, offer); e != nil {
+		t.Error(e)
+	}
+
+	values.Set("Amount", "-1")
+	if e := util.TestValues(values, offer); e == nil {
+		t.Error("invalid BTC rate allowed")
+	}
+
+	values.Set("Amount", "0.1234")
+	values.Set("startDate", "1995-30-30T23:23:59")
+	if e := util.TestValues(values, offer); e == nil {
+		t.Error("invalid date allowed")
+	}
+}
--- /dev/null
+++ b/forms/patient/profile.go
@@ -1,0 +1,35 @@
+package forms
+
+import (
+	"net/http"
+
+	"github.com/albrow/forms"
+	"github.com/olmaxmedical/router"
+	"golang.org/x/text/message"
+)
+
+func init() {
+	b := &router.Form{
+		Access:    router.PatientAuth,
+		Path:      "patient/profile",
+		Validator: profile,
+		After:     0,
+		Redirect:  "/patient/profile.html",
+	}
+	router.AddPost(b)
+}
+
+func profile(r *http.Request, p *message.Printer) []string {
+	var errors []string
+	data, err := forms.Parse(r)
+	if err != nil {
+		errors = append(errors, p.Sprint("Internal server error"))
+		return errors
+	}
+	val := data.Validator()
+	//
+	if val.HasErrors() {
+		errors = append(errors, val.Messages()...)
+	}
+	return errors
+}
--- /dev/null
+++ b/forms/patient/symptoms.go
@@ -1,0 +1,94 @@
+package forms
+
+import (
+	"net/http"
+	"time"
+
+	"github.com/albrow/forms"
+	"github.com/olmaxmedical/plugins"
+	"github.com/olmaxmedical/router"
+	"golang.org/x/text/message"
+)
+
+func init() {
+	b := &router.Form{
+		Access:    router.PatientAuth,
+		Path:      "patient/symptoms",
+		Validator: symptoms,
+		After:     plugins.EmailForm,
+		Redirect:  "patient/profile.html",
+	}
+	router.AddPost(b)
+}
+
+func symptoms(r *http.Request, p *message.Printer) []string {
+	var errors []string
+
+	data, err := forms.Parse(r)
+	if err != nil {
+		errors = append(errors, p.Sprint("Internal server error"))
+		return errors
+	}
+
+	val := data.Validator()
+
+	// NOTE(halfwit) This is the current record oldest person
+	// Anything older than this is most definitely invalid
+	oldest := time.Date(1901, 1, 1, 0, 0, 0, 0, time.UTC)
+
+	// NOTE(halfwit) There's potential that symptoms started that day
+	// and the client is in a different time zone, use our tomorrow as a gate
+	youngest := time.Now().Add(time.Hour * 24)
+
+	val.Require("bday").Message(p.Sprint("Birth date required"))
+	if d, e := time.Parse("2006-01-02T15:04:05", r.Form.Get("bday")); e != nil || oldest.After(d) || youngest.Before(d) {
+		val.AddError("bday", p.Sprint("Invalid birth date"))
+	}
+
+	val.Require("onset").Message(p.Sprint("Please enter the date and time your symptoms started"))
+	if d, e := time.Parse("2006-01-02T15:04:05", r.Form.Get("onset")); e != nil || oldest.After(d) || youngest.Before(d) {
+		val.AddError("bday", p.Sprint("Invalid date"))
+	}
+
+	val.Require("gender").Message(p.Sprint("Please select a biological gender"))
+	if r.PostFormValue("gender") != "male" && r.PostFormValue("gender") != "female" {
+		val.AddError("gender", p.Sprint("Invalid selection for gender"))
+	}
+
+	val.GreaterOrEqual("duration", 0).Message(p.Sprint("Invalid value entered for how long symptoms have lasted"))
+	val.Require("reason").Message(p.Sprint("Please provide the reason for visit"))
+	val.Require("location").Message(p.Sprint("Please list the area the symptom(s) appear"))
+	val.Require("characteristic").Message(p.Sprint("Please provide a description of your symptoms"))
+	val.Require("aggreAlevi").Message(p.Sprint("Please note anything which improves/worsens your symptoms"))
+
+	for _, i := range []string{
+		"feversChills",
+		"wtGainLoss",
+		"vision",
+		"lung",
+		"heart",
+		"bowel",
+		"renal",
+		"musSkel",
+		"neuro",
+		"psych",
+	} {
+		sel, ok := r.Form[i]
+		if !ok {
+			val.AddError(i, p.Sprintf("No selection for %s", i))
+			continue
+		}
+
+		if sel[0] == "Yes" || sel[0] == "yes" || sel[0] == "no" || sel[0] == "No" {
+			continue
+		}
+
+		val.AddError(i, p.Sprintf("Invalid selection for %s", i))
+	}
+
+	r.Form["pagetitle"] = []string{"Client symptoms"}
+	if val.HasErrors() {
+		errors = append(errors, val.Messages()...)
+	}
+	return errors
+}
--- /dev/null
+++ b/forms/patient/symptoms_test.go
@@ -1,0 +1,53 @@
+package forms
+
+import (
+	"net/url"
+	"testing"
+	"time"
+
+	"github.com/olmaxmedical/forms/util"
+)
+
+func TestSymptoms(t *testing.T) {
+	values := url.Values{}
+
+	values.Add("bday", "1990-01-01T01:01:01")
+	values.Add("onset", "2001-01-01T01:01:01")
+	values.Add("gender", "male")
+	values.Add("duration", "1")
+	values.Add("reason", "test")
+	values.Add("location", "test")
+	values.Add("characteristic", "test")
+	values.Add("aggreAlevi", "test")
+	for _, i := range []string{
+		"feversChills",
+		"wtGainLoss",
+		"vision",
+		"lung",
+		"heart",
+		"bowel",
+		"renal",
+		"musSkel",
+		"neuro",
+		"psych",
+	} {
+		values.Add(i, "yes")
+	}
+
+	if e := util.TestValues(values, symptoms); e != nil {
+		t.Error(e)
+	}
+
+	values.Set("bday", "1891-01-01T01:01:01")
+
+	if e := util.TestValues(values, symptoms); e == nil {
+		t.Error("forms parsing: invalid date accepted")
+	}
+
+	values.Set("bday", "1990-01-01T01:01:01")
+	values.Set("onset", time.Now().Add(time.Hour+48).String())
+
+	if e := util.TestValues(values, symptoms); e == nil {
+		t.Error("form parsing: invalid onset accepted")
+	}
+}
--- /dev/null
+++ b/forms/resetpassword.go
@@ -1,0 +1,37 @@
+package forms
+
+import (
+	"net/http"
+
+	"github.com/albrow/forms"
+	"github.com/olmaxmedical/plugins"
+	"github.com/olmaxmedical/router"
+	"golang.org/x/text/message"
+)
+
+func init() {
+	b := &router.Form{
+		Access:    router.GuestAuth,
+		Path:      "resetpassword",
+		Validator: reset,
+		Redirect:  "/login.html",
+		After:     plugins.ResetPassword,
+	}
+	router.AddPost(b)
+}
+
+func reset(r *http.Request, p *message.Printer) []string {
+	var errors []string
+	data, err := forms.Parse(r)
+	if err != nil {
+		errors = append(errors, "Internal server error")
+		return errors
+	}
+	val := data.Validator()
+	val.Require("email").Message(p.Sprintf("Valid email required"))
+	val.MatchEmail("email").Message(p.Sprintf("Invalid email"))
+	if val.HasErrors() {
+		errors = append(errors, val.Messages()...)
+	}
+	return errors
+}
--- /dev/null
+++ b/forms/signup.go
@@ -1,0 +1,43 @@
+package forms
+
+import (
+	"net/http"
+
+	"github.com/albrow/forms"
+	"github.com/olmaxmedical/plugins"
+	"github.com/olmaxmedical/router"
+	"golang.org/x/text/message"
+)
+
+func init() {
+	b := &router.Form{
+		Access:    router.GuestAuth,
+		Path:      "signup",
+		Validator: signin,
+		Redirect:  "/login.html",
+		After:     plugins.SendSignup,
+	}
+	router.AddPost(b)
+}
+
+func signin(r *http.Request, p *message.Printer) []string {
+	var errors []string
+	data, err := forms.Parse(r)
+	if err != nil {
+		errors = append(errors, "Internal server error")
+		return errors
+	}
+	val := data.Validator()
+	val.Require("fname").Message(p.Sprintf("First name required"))
+	val.MinLength("fname", 2).Message(p.Sprintf("First name must be at least 2 characters"))
+	val.Require("lname").Message(p.Sprintf("Last name required"))
+	val.MinLength("lname", 2).Message(p.Sprintf("Last name must be at least 2 characters"))
+	val.Require("email").Message(p.Sprintf("Valid email required"))
+	val.MatchEmail("email").Message(p.Sprintf("Invalid email"))
+	val.Require("pass").Message(p.Sprintf("Password required"))
+	val.MinLength("pass", 8).Message(p.Sprintf("Password must be at least 8 characters"))
+	if val.HasErrors() {
+		errors = append(errors, val.Messages()...)
+	}
+	return errors
+}
--- a/go.mod
+++ /dev/null
@@ -1,10 +1,0 @@
-module github.com/olmaxmedical/forms
-
-go 1.14
-
-require (
-	github.com/albrow/forms v0.3.4-0.20170215231405-c4277021bca2
-	github.com/olmaxmedical/plugins v0.0.1
-	github.com/olmaxmedical/router v0.0.1
-	golang.org/x/text v0.3.2
-)
--- a/go.sum
+++ /dev/null
@@ -1,34 +1,0 @@
-github.com/albrow/forms v0.3.4-0.20170215231405-c4277021bca2 h1:SnKbJhjY6YOX6cC0JL19DzxJ4nMZoTFqRJ006tgpclw=
-github.com/albrow/forms v0.3.4-0.20170215231405-c4277021bca2/go.mod h1:jvrM3b0gPuIRiY1E/KmKfPk2XXDEKj7yFB+g9g0BItQ=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/olmaxmedical/database v0.0.0/go.mod h1:BPYEBAP3GYeSeqg5hOaH7GC8+P/VnD2OuHYounbVjQs=
-github.com/olmaxmedical/database v0.0.1 h1:cuocVljXq7cPRS9HygFg2B/WSdzWAZEHrc5uMnN+A0A=
-github.com/olmaxmedical/database v0.0.1/go.mod h1:/5Tl6/p0jpvLpj4GaoFki3wRG/3b+ipNNhM5Dyi6Zf8=
-github.com/olmaxmedical/email v0.0.1 h1:bhOERmPiUmFJqC133s+FFXucSI3dNnfDKsboDYFEbkc=
-github.com/olmaxmedical/email v0.0.1/go.mod h1:bz6en9uc6h9fyu3MW2jTwYW19ZclQ22JkcIxsl3/epc=
-github.com/olmaxmedical/plugins v0.0.1 h1:fON9AAfH635gLKNbytrUdRoRCjhI0ZPP7VFzCVZDVWA=
-github.com/olmaxmedical/plugins v0.0.1/go.mod h1:bHkYv5oh6bk5y1jCZDO9Bk5IyYMPWgZDjMrEifxVgbU=
-github.com/olmaxmedical/router v0.0.1 h1:x3fZ9u00xwKxLvPj8Au0QhIxpmxRv+q2lVMdBmyRQjM=
-github.com/olmaxmedical/router v0.0.1/go.mod h1:28e377pByZCQMBAdERDBhX4wYeTsJgc6U4yOEiz7MsA=
-github.com/olmaxmedical/session v0.0.1 h1:2xdSjpEg89+ClRFLPp/gR3lNxl4JjPAUd2Hsds++FFs=
-github.com/olmaxmedical/session v0.0.1/go.mod h1:XOVyHL+cKa5t2fLDIJtFxwEzJOa3r1hUkwlL4aybdqA=
-github.com/pariz/gountries v0.0.0-20191029140926-233bc78cf5b5 h1:842t0ixg/A4my8/Q3oDNdHIsKYIx02NDlWVEhaiBToo=
-github.com/pariz/gountries v0.0.0-20191029140926-233bc78cf5b5/go.mod h1:U0ETmPPEsfd7CpUKNMYi68xIOL8Ww4jPZlaqNngcwqs=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/scorredoira/email v0.0.0-20191107070024-dc7b732c55da h1:hhmnjfzz7szp75AyXxn8tDfEA0oU4REQLmpuW6zNAOY=
-github.com/scorredoira/email v0.0.0-20191107070024-dc7b732c55da/go.mod h1:Q5ljvYIBpukMH+wgB8kcPV1i9NX8TqU++8GgBKq3pt0=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
--- a/login.go
+++ /dev/null
@@ -1,39 +1,0 @@
-package forms
-
-import (
-	"net/http"
-
-	"github.com/albrow/forms"
-	"github.com/olmaxmedical/plugins"
-	"github.com/olmaxmedical/router"
-	"golang.org/x/text/message"
-)
-
-func init() {
-	b := &router.Form{
-		Access:    router.GuestAuth,
-		Path:      "login",
-		Validator: login,
-		After:     plugins.ValidateLogin,
-		Redirect:  "/profile.html",
-	}
-	router.AddPost(b)
-}
-
-func login(r *http.Request, p *message.Printer) []string {
-	var errors []string
-	data, err := forms.Parse(r)
-	if err != nil {
-		errors = append(errors, p.Sprint("Internal server error"))
-		return errors
-	}
-	val := data.Validator()
-	val.Require("email").Message(p.Sprint("Username required"))
-	val.MatchEmail("email").Message(p.Sprint("User name must be a valid email"))
-	val.Require("pass").Message(p.Sprint("Password required"))
-	val.MinLength("pass", 8).Message(p.Sprint("Password must be at least 8 characters"))
-	if val.HasErrors() {
-		errors = append(errors, val.Messages()...)
-	}
-	return errors
-}
--- a/newpassword.go
+++ /dev/null
@@ -1,42 +1,0 @@
-package forms
-
-import (
-	"net/http"
-
-	"github.com/albrow/forms"
-	"github.com/olmaxmedical/plugins"
-	"github.com/olmaxmedical/router"
-	"golang.org/x/text/message"
-)
-
-func init() {
-	b := &router.Form{
-		Access:    router.GuestAuth,
-		Path:      "newpassword",
-		Validator: newPassword,
-		Redirect:  "/login.html",
-		After:     plugins.ResetPassword | plugins.FormToken,
-	}
-	router.AddPost(b)
-}
-
-func newPassword(r *http.Request, p *message.Printer) []string {
-	var errors []string
-	data, err := forms.Parse(r)
-	if err != nil {
-		errors = append(errors, "Internal server error")
-		return errors
-	}
-	val := data.Validator()
-	val.Require("password").Message(p.Sprintf("Password required"))
-	val.MinLength("password", 8).Message(p.Sprintf("Password must be at least 8 characters"))
-	val.Require("reenter").Message(p.Sprintf("Re-enter same password"))
-	val.MinLength("reenter", 8).Message(p.Sprintf("Password must be at least 8 characters"))
-	if val.HasErrors() {
-		errors = append(errors, val.Messages()...)
-	}
-	if data.Get("reenter") != data.Get("password") {
-		errors = append(errors, p.Sprint("Passwords do not match"))
-	}
-	return errors
-}
--- a/patient/offer.go
+++ /dev/null
@@ -1,53 +1,0 @@
-package forms
-
-import (
-	"net/http"
-	"time"
-
-	"github.com/albrow/forms"
-	"github.com/olmaxmedical/plugins"
-	"github.com/olmaxmedical/router"
-	"golang.org/x/text/message"
-)
-
-func init() {
-	b := &router.Form{
-		Access:    router.PatientAuth,
-		Path:      "patient/offer",
-		Validator: offer,
-		After:     plugins.Search | plugins.Services, //|plugins.Offer
-		Redirect:  "results.html",
-	}
-	router.AddPost(b)
-}
-
-func offer(r *http.Request, p *message.Printer) []string {
-	var errors []string
-	data, err := forms.ParseMax(r, r.ContentLength)
-	if err != nil {
-		errors = append(errors, p.Sprint("Internal server error"))
-		return errors
-	}
-	val := data.Validator()
-	val.Require("Amount").Message(p.Sprint("Please enter a target rate (Bitcoin/15min)"))
-	bcu := data.GetFloat("Amount")
-	if 0.0 > bcu || bcu > 1.0 {
-		val.AddError("Amount", p.Sprint("BTC/15min rate out of range"))
-	}
-	val.Require("startDate").Message(p.Sprint("Start date required"))
-	_, err = time.Parse("2006-01-02T15:04:05", r.Form.Get("startDate"))
-	if err != nil {
-		val.AddError("startDate", p.Sprint("Invalid start-date entered"))
-	}
-
-	val.Require("endDate").Message(p.Sprint("End date required"))
-	_, err = time.Parse("2006-01-02T15:04:05", r.Form.Get("endDate"))
-	if err != nil {
-		val.AddError("endDate", p.Sprint("Invalid end-date entered"))
-	}
-
-	if val.HasErrors() {
-		errors = append(errors, val.Messages()...)
-	}
-	return errors
-}
--- a/patient/offer_test.go
+++ /dev/null
@@ -1,31 +1,0 @@
-package forms
-
-import (
-	"net/url"
-	"testing"
-
-	"github.com/olmaxmedical/forms/util"
-)
-
-func TestOffer(t *testing.T) {
-	values := url.Values{}
-
-	values.Add("Amount", "0.1234")
-	values.Add("startDate", "2020-04-04T00:00:00")
-	values.Add("endDate", "2020-06-06T00:00:00")
-
-	if e := util.TestValues(values, offer); e != nil {
-		t.Error(e)
-	}
-
-	values.Set("Amount", "-1")
-	if e := util.TestValues(values, offer); e == nil {
-		t.Error("invalid BTC rate allowed")
-	}
-
-	values.Set("Amount", "0.1234")
-	values.Set("startDate", "1995-30-30T23:23:59")
-	if e := util.TestValues(values, offer); e == nil {
-		t.Error("invalid date allowed")
-	}
-}
--- a/patient/profile.go
+++ /dev/null
@@ -1,35 +1,0 @@
-package forms
-
-import (
-	"net/http"
-
-	"github.com/albrow/forms"
-	"github.com/olmaxmedical/router"
-	"golang.org/x/text/message"
-)
-
-func init() {
-	b := &router.Form{
-		Access:    router.PatientAuth,
-		Path:      "patient/profile",
-		Validator: profile,
-		After:     0,
-		Redirect:  "/patient/profile.html",
-	}
-	router.AddPost(b)
-}
-
-func profile(r *http.Request, p *message.Printer) []string {
-	var errors []string
-	data, err := forms.Parse(r)
-	if err != nil {
-		errors = append(errors, p.Sprint("Internal server error"))
-		return errors
-	}
-	val := data.Validator()
-	//
-	if val.HasErrors() {
-		errors = append(errors, val.Messages()...)
-	}
-	return errors
-}
--- a/patient/symptoms.go
+++ /dev/null
@@ -1,94 +1,0 @@
-package forms
-
-import (
-	"net/http"
-	"time"
-
-	"github.com/albrow/forms"
-	"github.com/olmaxmedical/plugins"
-	"github.com/olmaxmedical/router"
-	"golang.org/x/text/message"
-)
-
-func init() {
-	b := &router.Form{
-		Access:    router.PatientAuth,
-		Path:      "patient/symptoms",
-		Validator: symptoms,
-		After:     plugins.EmailForm,
-		Redirect:  "patient/profile.html",
-	}
-	router.AddPost(b)
-}
-
-func symptoms(r *http.Request, p *message.Printer) []string {
-	var errors []string
-
-	data, err := forms.Parse(r)
-	if err != nil {
-		errors = append(errors, p.Sprint("Internal server error"))
-		return errors
-	}
-
-	val := data.Validator()
-
-	// NOTE(halfwit) This is the current record oldest person
-	// Anything older than this is most definitely invalid
-	oldest := time.Date(1901, 1, 1, 0, 0, 0, 0, time.UTC)
-
-	// NOTE(halfwit) There's potential that symptoms started that day
-	// and the client is in a different time zone, use our tomorrow as a gate
-	youngest := time.Now().Add(time.Hour * 24)
-
-	val.Require("bday").Message(p.Sprint("Birth date required"))
-	if d, e := time.Parse("2006-01-02T15:04:05", r.Form.Get("bday")); e != nil || oldest.After(d) || youngest.Before(d) {
-		val.AddError("bday", p.Sprint("Invalid birth date"))
-	}
-
-	val.Require("onset").Message(p.Sprint("Please enter the date and time your symptoms started"))
-	if d, e := time.Parse("2006-01-02T15:04:05", r.Form.Get("onset")); e != nil || oldest.After(d) || youngest.Before(d) {
-		val.AddError("bday", p.Sprint("Invalid date"))
-	}
-
-	val.Require("gender").Message(p.Sprint("Please select a biological gender"))
-	if r.PostFormValue("gender") != "male" && r.PostFormValue("gender") != "female" {
-		val.AddError("gender", p.Sprint("Invalid selection for gender"))
-	}
-
-	val.GreaterOrEqual("duration", 0).Message(p.Sprint("Invalid value entered for how long symptoms have lasted"))
-	val.Require("reason").Message(p.Sprint("Please provide the reason for visit"))
-	val.Require("location").Message(p.Sprint("Please list the area the symptom(s) appear"))
-	val.Require("characteristic").Message(p.Sprint("Please provide a description of your symptoms"))
-	val.Require("aggreAlevi").Message(p.Sprint("Please note anything which improves/worsens your symptoms"))
-
-	for _, i := range []string{
-		"feversChills",
-		"wtGainLoss",
-		"vision",
-		"lung",
-		"heart",
-		"bowel",
-		"renal",
-		"musSkel",
-		"neuro",
-		"psych",
-	} {
-		sel, ok := r.Form[i]
-		if !ok {
-			val.AddError(i, p.Sprintf("No selection for %s", i))
-			continue
-		}
-
-		if sel[0] == "Yes" || sel[0] == "yes" || sel[0] == "no" || sel[0] == "No" {
-			continue
-		}
-
-		val.AddError(i, p.Sprintf("Invalid selection for %s", i))
-	}
-
-	r.Form["pagetitle"] = []string{"Client symptoms"}
-	if val.HasErrors() {
-		errors = append(errors, val.Messages()...)
-	}
-	return errors
-}
--- a/patient/symptoms_test.go
+++ /dev/null
@@ -1,53 +1,0 @@
-package forms
-
-import (
-	"net/url"
-	"testing"
-	"time"
-
-	"github.com/olmaxmedical/forms/util"
-)
-
-func TestSymptoms(t *testing.T) {
-	values := url.Values{}
-
-	values.Add("bday", "1990-01-01T01:01:01")
-	values.Add("onset", "2001-01-01T01:01:01")
-	values.Add("gender", "male")
-	values.Add("duration", "1")
-	values.Add("reason", "test")
-	values.Add("location", "test")
-	values.Add("characteristic", "test")
-	values.Add("aggreAlevi", "test")
-	for _, i := range []string{
-		"feversChills",
-		"wtGainLoss",
-		"vision",
-		"lung",
-		"heart",
-		"bowel",
-		"renal",
-		"musSkel",
-		"neuro",
-		"psych",
-	} {
-		values.Add(i, "yes")
-	}
-
-	if e := util.TestValues(values, symptoms); e != nil {
-		t.Error(e)
-	}
-
-	values.Set("bday", "1891-01-01T01:01:01")
-
-	if e := util.TestValues(values, symptoms); e == nil {
-		t.Error("forms parsing: invalid date accepted")
-	}
-
-	values.Set("bday", "1990-01-01T01:01:01")
-	values.Set("onset", time.Now().Add(time.Hour+48).String())
-
-	if e := util.TestValues(values, symptoms); e == nil {
-		t.Error("form parsing: invalid onset accepted")
-	}
-}
--- a/resetpassword.go
+++ /dev/null
@@ -1,37 +1,0 @@
-package forms
-
-import (
-	"net/http"
-
-	"github.com/albrow/forms"
-	"github.com/olmaxmedical/plugins"
-	"github.com/olmaxmedical/router"
-	"golang.org/x/text/message"
-)
-
-func init() {
-	b := &router.Form{
-		Access:    router.GuestAuth,
-		Path:      "resetpassword",
-		Validator: reset,
-		Redirect:  "/login.html",
-		After:     plugins.ResetPassword,
-	}
-	router.AddPost(b)
-}
-
-func reset(r *http.Request, p *message.Printer) []string {
-	var errors []string
-	data, err := forms.Parse(r)
-	if err != nil {
-		errors = append(errors, "Internal server error")
-		return errors
-	}
-	val := data.Validator()
-	val.Require("email").Message(p.Sprintf("Valid email required"))
-	val.MatchEmail("email").Message(p.Sprintf("Invalid email"))
-	if val.HasErrors() {
-		errors = append(errors, val.Messages()...)
-	}
-	return errors
-}
binary files a/resources/certificate.pdf /dev/null differ
binary files a/resources/resume.pdf /dev/null differ
--- a/signup.go
+++ /dev/null
@@ -1,43 +1,0 @@
-package forms
-
-import (
-	"net/http"
-
-	"github.com/albrow/forms"
-	"github.com/olmaxmedical/plugins"
-	"github.com/olmaxmedical/router"
-	"golang.org/x/text/message"
-)
-
-func init() {
-	b := &router.Form{
-		Access:    router.GuestAuth,
-		Path:      "signup",
-		Validator: signin,
-		Redirect:  "/login.html",
-		After:     plugins.SendSignup,
-	}
-	router.AddPost(b)
-}
-
-func signin(r *http.Request, p *message.Printer) []string {
-	var errors []string
-	data, err := forms.Parse(r)
-	if err != nil {
-		errors = append(errors, "Internal server error")
-		return errors
-	}
-	val := data.Validator()
-	val.Require("fname").Message(p.Sprintf("First name required"))
-	val.MinLength("fname", 2).Message(p.Sprintf("First name must be at least 2 characters"))
-	val.Require("lname").Message(p.Sprintf("Last name required"))
-	val.MinLength("lname", 2).Message(p.Sprintf("Last name must be at least 2 characters"))
-	val.Require("email").Message(p.Sprintf("Valid email required"))
-	val.MatchEmail("email").Message(p.Sprintf("Invalid email"))
-	val.Require("pass").Message(p.Sprintf("Password required"))
-	val.MinLength("pass", 8).Message(p.Sprintf("Password must be at least 8 characters"))
-	if val.HasErrors() {
-		errors = append(errors, val.Messages()...)
-	}
-	return errors
-}