initial commit

This commit is contained in:
itwrx
2025-05-15 08:01:35 -05:00
commit 74e9056c49
68 changed files with 2942 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
#[Copyright 2025 ITwrx.
This file is part of Simple Site Manager.
Simple Site Manager is released under the GNU Affero General Public License 3.0.
See COPYING or <https://www.gnu.org/licenses/> for details.]#
import guildenstern/httpserver
import nimword, jsony
import std/[base64, sysrand]
import "../helpers/form", "../helpers/validation", "../helpers/auth", "../helpers/global", "../models/user", "../models/session"
proc loginPostHandler*() =
try:
var cookieHeader1, cookieHeader2: string
let email = formInput("email")
let password = formInput("password")
#validate form inputs
vInput("email", @["required", "string", "email", "max:75"])
vInput("password", @["required", "string", "min_complexity", "max:100"])
var csrfTokenInput = formInput("csrf_token")
#create formResult and redirect on validation errors.
if formErrors.len > 0:
addFormOldInput("email", formInput("email"))
#discard assignErrorFR(formErrors, formOldInputs, csrfTokenInput)
discard assignErrorFR(formErrors, formOldInputs)
let frJson = formResult.toJson()
#let cookieHeader = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "path=/;"
if APP_MODE == "dev":
cookieHeader1 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "path=/;" & "SameSite=Lax;"
else:
cookieHeader1 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "Secure=true;" & "path=/;" & "SameSite=Lax;"
reply(Http303, [locationBack(), cookieHeader1])
else:
#inputs pass validation rules. let's see if the creds supplied are valid.
let registeredUser = getUserByEmail(email)
if registeredUser.email.len > 0:
if password.isValidPassword(registeredUser.password):
#create sessionId
var sessionId = $urandom(32)
sessionId = base64.encode(sessionId)
#create session
var userSession: Session
userSession.sessionId = sessionId
userSession.userId = registeredUser.id
userSession.csrfToken = newCsrfToken()
createUserSession(userSession)
discard assignLoginSuccessFR()
let frJson = formResult.toJson()
#redirect, and set session cookie.
#will probably need to detect requested url for redirect (with static fallback) instead of just static location.
#Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly
#dev
#let cookieHeader1 = "Set-Cookie: " & APP_NAME & "_session=" & sessionId & ";" & "HttpOnly;" & "SameSite=Lax;"
#let cookieHeader2 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "path=/;" & "SameSite=Lax;"
#prod
#let cookieHeader1 = "Set-Cookie: " & APP_NAME & "_session=" & sessionId & ";" & "HttpOnly;" & "Secure=true;" & "SameSite=Lax;"
#let cookieHeader2 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "Secure=true;" & "path=/;" & "SameSite=Lax;"
if APP_MODE == "dev":
cookieHeader1 = "Set-Cookie: " & APP_NAME & "_session=" & sessionId & ";" & "HttpOnly;" & "SameSite=Lax;"
cookieHeader2 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "path=/;" & "SameSite=Lax;"
else:
cookieHeader1 = "Set-Cookie: " & APP_NAME & "_session=" & sessionId & ";" & "HttpOnly;" & "Secure=true;" & "SameSite=Lax;"
cookieHeader2 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "Secure=true;" & "path=/;" & "SameSite=Lax;"
#headers are just an openarray of strings.
reply(Http303, [location("/"), cookieHeader1, cookieHeader2])
else:
#password supplied did not match password in DB for supplied email address. redirect back to login with creds error.
#create a formError.
formError.fieldName = "password"
formError.fieldMessage = "Entered password is not valid."
#add it to formErrors seq.
formErrors.add(formError)
formResult.message = "Authentication error."
formResult.messageClass = "text-red-500"
formResult.errors = toJson(formErrors)
formResult.oldInputs = getOldInputJson()
let frJson = formResult.toJson()
#let cookieHeader = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;"
#let cookieHeader = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "path=/;"
if APP_MODE == "dev":
cookieHeader1 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "path=/;" & "SameSite=Lax;"
else:
cookieHeader1 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "Secure=true;" & "path=/;" & "SameSite=Lax;"
reply(Http303, [locationBack(), cookieHeader1])
else:
formError.fieldName = "email"
formError.fieldMessage = "No user found with supplied email address."
#add it to formErrors seq.
formErrors.add(formError)
formResult.message = "Authentication error."
formResult.messageClass = "text-red-500"
formResult.errors = toJson(formErrors)
formResult.oldInputs = getOldInputJson()
let frJson = formResult.toJson()
#let cookieHeader = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "path=/;"
if APP_MODE == "dev":
cookieHeader1 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "path=/;" & "SameSite=Lax;"
else:
cookieHeader1 = "Set-Cookie: form_result=" & frJson & ";" & "HttpOnly;" & "Secure=true;" & "path=/;" & "SameSite=Lax;"
reply(Http303, [locationBack(), cookieHeader1])
except Exception as e:
echo e.msg
#reply(Http500)
discard assignGeneralErrorFR(e.msg)
setFR()
reply(Http303, [location("/500")])

View File

@@ -0,0 +1,196 @@
#[Copyright 2024 ITwrx.
This file is part of Simple Site Manager.
Simple Site Manager is released under the GNU Affero General Public License 3.0.
See COPYING or <https://www.gnu.org/licenses/> for details.]#
import guildenstern/httpserver, jsony
import "../helpers/form", "../helpers/global", "../helpers/validation", "../helpers/auth"
import "../models/reminder"
proc reminderCreatePostHandler*() =
#try to save to DB.
try:
#attempt validation first.
vInput("title", @["required", "string", "max:150"])
#vInput("subject", @["string", "max:175"])
vInput("message", @["required", "string", "max:255"])
vInput("notify_via", @["required", "string", "max:5"])
vInput("repeats", @["required", "integer", "max:1"])
vInput("repeat_freq", @["required_with:repeats", "string", "max:10"])
vInput("weekly_on", @["required_when:repeat_freq:equals:week", "string", "max:10"])
vInput("monthly_on_day", @["required_when:repeat_freq:equals:month:without:monthly_on_week", "integer", "max:31"])
vInput("monthly_on_weekday", @["required_with:monthly_on_week", "string", "max:10"])
vInput("monthly_on_week", @["required_when:repeat_freq:equals:month:without:monthly_on_day", "string", "max:4"])
vInput("yearly_on_month", @["required_when:repeat_freq:equals:year:without:yearly_on_week", "string", "max:12"])
vInput("yearly_on_day", @["required_with:yearly_on_month", "integer", "max:31"])
vInput("yearly_on_week", @["required_when:repeat_freq:equals:year:without:yearly_on_month", "string", "max:4"])
vInput("yearly_on_weekday", @["required_with:yearly_on_week", "string", "max:10"])
vInput("yearly_on_month2", @["required_with:yearly_on_week", "string", "max:12"])
vInput("send_date", @["required", "string", "max:10"])
vInput("send_time_hr", @["required", "integer", "max:12"])
vInput("send_time_min", @["required", "integer", "max:60"])
vInput("send_time_am_pm", @["required", "string", "max:2"])
#create formResult and redirect on validation errors.
if formErrors.len > 0:
addFormOldInput("title", formInput("title"))
#addFormOldInput("subject", formInput("subject"))
addFormOldInput("message", formInput("message"))
addFormOldInput("notify_via", formInput("notify_via"))
addFormOldInput("repeats", formInput("repeats"))
addFormOldInput("repeat_freq", formInput("repeat_freq"))
addFormOldInput("weekly_on", formInput("weekly_on"))
addFormOldInput("monthly_on_day", formInput("monthly_on_day"))
addFormOldInput("monthly_on_weekday", formInput("monthly_on_weekday"))
addFormOldInput("monthly_on_week", formInput("monthly_on_week"))
addFormOldInput("yearly_on_month", formInput("yearly_on_month"))
addFormOldInput("yearly_on_day", formInput("yearly_on_day"))
addFormOldInput("yearly_on_week", formInput("yearly_on_week"))
addFormOldInput("yearly_on_weekday", formInput("yearly_on_weekday"))
addFormOldInput("yearly_on_month2", formInput("yearly_on_month2"))
addFormOldInput("send_date", formInput("send_date"))
addFormOldInput("send_time_hr", formInput("send_time_hr"))
addFormOldInput("send_time_min", formInput("send_time_min"))
addFormOldInput("send_time_am_pm", formInput("send_time_am_pm"))
discard assignErrorFR(formErrors, formOldInputs)
setFR()
reply(Http303, [locationBack()])
#validation passed. Save posted data to db.
else:
var reminder: Reminder
reminder.title = formInput("title")
#reminder.subject = formInput("subject")
reminder.message = formInput("message")
reminder.notifyVia = formInput("notify_via")
reminder.repeats = formInputInt("repeats")
reminder.repeatFreq = formInput("repeat_freq")
reminder.weeklyOn = formInput("weekly_on")
reminder.monthlyOnDay = formInputInt("monthly_on_day")
reminder.monthlyOnWeekday = formInput("monthly_on_weekday")
reminder.monthlyOnWeek = formInput("monthly_on_week")
reminder.yearlyOnMonth = formInput("yearly_on_month")
reminder.yearlyOnDay = formInputInt("yearly_on_day")
reminder.yearlyOnWeek = formInput("yearly_on_week")
reminder.yearlyOnWeekday = formInput("yearly_on_weekday")
reminder.yearlyOnMonth2 = formInput("yearly_on_month2")
reminder.sendDate = formInput("send_date")
reminder.sendTimeHr = formInputInt("send_time_hr")
reminder.sendTimeMin = formInputInt("send_time_min")
reminder.sendTimeAmPm = formInput("send_time_am_pm")
let reminderId = createReminder(reminder)
clearAllReminders()
discard assignCEEditSuccessFR()
setFR()
reply(Http303, [location("/")])
except CatchableError as e:
echo e.msg
#reply(Http500)
discard assignGeneralErrorFR(e.msg)
setFR()
reply(Http303, [location("/500")])
proc reminderUpdatePostHandler*() =
try:
#var origin = http.headers.getOrDefault("origin")
#attempt validation first.
vInput("title", @["required", "string", "max:150"])
#vInput("subject", @["string", "max:175"])
vInput("message", @["required", "string", "max:255"])
vInput("notify_via", @["required", "string", "max:5"])
vInput("repeats", @["required", "integer", "max:1"])
vInput("repeat_freq", @["required_with:repeats", "string", "max:10"])
vInput("weekly_on", @["required_when:repeat_freq:equals:week", "string", "max:10"])
vInput("monthly_on_day", @["required_when:repeat_freq:equals:month:without:monthly_on_week", "must_unset_with:monthly_on_week", "must_unset_with:monthly_on_weekday", "integer", "max:31"])
vInput("monthly_on_weekday", @["required_with:monthly_on_week", "must_unset_with:monthly_on_day", "string", "max:10"])
vInput("monthly_on_week", @["required_when:repeat_freq:equals:month:without:monthly_on_day", "must_unset_with:monthly_on_day", "string", "max:4"])
vInput("yearly_on_month", @["required_when:repeat_freq:equals:year:without:yearly_on_week", "must_unset_with:yearly_on_week", "must_unset_with:yearly_on_weekday", "must_unset_with:yearly_on_month2", "string", "max:12"])
vInput("yearly_on_day", @["required_with:yearly_on_month", "must_unset_with:yearly_on_week", "must_unset_with:yearly_on_weekday", "must_unset_with:yearly_on_month2", "integer", "max:31"])
vInput("yearly_on_week", @["required_when:repeat_freq:equals:year:without:yearly_on_month", "must_unset_with:yearly_on_month", "must_unset_with:yearly_on_day", "string", "max:4"])
vInput("yearly_on_weekday", @["required_with:yearly_on_week", "must_unset_with:yearly_on_month", "must_unset_with:yearly_on_day", "string", "max:10"])
vInput("yearly_on_month2", @["required_with:yearly_on_week", "must_unset_with:yearly_on_month", "must_unset_with:yearly_on_day", "string", "max:12"])
vInput("send_date", @["required", "string", "max:10", "future_datetime"])
vInput("send_time_hr", @["required", "integer", "max:12"])
vInput("send_time_min", @["required", "integer", "max:60"])
vInput("send_time_am_pm", @["required", "string", "max:2"])
#create formResult and redirect on validation errors.
if formErrors.len > 0:
#since validation failed we better keep add the old inputs.
addFormOldInput("title", formInput("title"))
#addFormOldInput("subject", formInput("subject"))
addFormOldInput("message", formInput("message"))
addFormOldInput("notify_via", formInput("notify_via"))
addFormOldInput("repeats", formInput("repeats"))
addFormOldInput("repeat_freq", formInput("repeat_freq"))
addFormOldInput("weekly_on", formInput("weekly_on"))
addFormOldInput("monthly_on_day", formInput("monthly_on_day"))
addFormOldInput("monthly_on_weekday", formInput("monthly_on_weekday"))
addFormOldInput("monthly_on_week", formInput("monthly_on_week"))
addFormOldInput("yearly_on_month", formInput("yearly_on_month"))
addFormOldInput("yearly_on_day", formInput("yearly_on_day"))
addFormOldInput("yearly_on_week", formInput("yearly_on_week"))
addFormOldInput("yearly_on_weekday", formInput("yearly_on_weekday"))
addFormOldInput("yearly_on_month2", formInput("yearly_on_month2"))
addFormOldInput("send_date", formInput("send_date"))
addFormOldInput("send_time_hr", formInput("send_time_hr"))
addFormOldInput("send_time_min", formInput("send_time_min"))
addFormOldInput("send_time_am_pm", formInput("send_time_am_pm"))
discard assignErrorFR(formErrors, formOldInputs)
setFR()
reply(Http303, [locationBack()])
#validation passed. Save posted data to db and redirect back with success message.
else:
var reminder: Reminder
reminder.id = formInputInt("reminder_id")
reminder.title = formInput("title")
#reminder.subject = formInput("subject")
reminder.message = formInput("message")
reminder.notifyVia = formInput("notify_via")
reminder.repeats = formInputInt("repeats")
reminder.repeatFreq = formInput("repeat_freq")
reminder.weeklyOn = formInput("weekly_on")
reminder.monthlyOnDay = formInputInt("monthly_on_day")
reminder.monthlyOnWeekday = formInput("monthly_on_weekday")
reminder.monthlyOnWeek = formInput("monthly_on_week")
reminder.yearlyOnMonth = formInput("yearly_on_month")
reminder.yearlyOnDay = formInputInt("yearly_on_day")
reminder.yearlyOnWeek = formInput("yearly_on_week")
reminder.yearlyOnWeekday = formInput("yearly_on_weekday")
reminder.yearlyOnMonth2 = formInput("yearly_on_month2")
reminder.sendDate = formInput("send_date")
reminder.sendTimeHr = formInputInt("send_time_hr")
reminder.sendTimeMin = formInputInt("send_time_min")
reminder.sendTimeAmPm = formInput("send_time_am_pm")
updateReminder(reminder)
clearAllReminders()
discard assignCEEditSuccessFR()
setFR()
reply(Http303, [location("/")])
except CatchableError as e:
echo e.msg
#reply(Http500)
discard assignGeneralErrorFR(e.msg)
setFR()
reply(Http303, [location("/500")])
proc reminderDeletePostHandler*() =
try:
vInput("reminder_id", @["integer", "max:200000"])
#create formResult and redirect on validation errors.
if formErrors.len > 0:
#since validation failed we better add the old inputs.
discard assignErrorFR(formErrors, formOldInputs)
setFR()
reply(Http303, [locationBack()])
#validation passed. Save posted data to db and redirect back with success message.
else:
let reminderId = formInputInt("reminder_id")
deleteReminder(reminderId)
clearAllReminders()
discard assignCEDeleteSuccessFR()
setFR()
reply(Http303, [location("/")])
except CatchableError as e:
echo e.msg
#reply(Http500)
discard assignGeneralErrorFR(e.msg)
setFR()
reply(Http303, [location("/500")])

View File

@@ -0,0 +1,18 @@
#[Copyright 2025 ITwrx.
This file is part of Simple Site Manager.
Simple Site Manager is released under the GNU Affero General Public License 3.0.
See COPYING or <https://www.gnu.org/licenses/> for details.]#
import guildenstern/httpserver, "../helpers/reminder", "../helpers/form"
proc sendRemindersPostHandler*() =
try:
if formInput("send_reminders_key") == "wpsU5CY1Tn5PkMjN6OBC7cdPVZbaW3x":
sendReminders()
reply(Http200)
else:
echo "'send_reminders_key' not valid"
reply(Http403)
except CatchableError as e:
echo e.msg
reply(Http500)

View File

@@ -0,0 +1,18 @@
#[Copyright 2025 ITwrx.
This file is part of Simple Site Manager.
Simple Site Manager is released under the GNU Affero General Public License 3.0.
See COPYING or <https://www.gnu.org/licenses/> for details.]#
import guildenstern/httpserver, "../models/user_session", "../helpers/form"
proc userSessionDeletePostHandler*() =
try:
if formInput("delete_user_sessions_key") == "XR5yLeigb4PFXOZBh3PBOuQXc8d7NE6":
deleteUserSessions()
reply(Http200)
else:
echo "'delete_user_sessions_key' not valid"
reply(Http403)
except CatchableError as e:
echo e.msg
reply(Http500)