Processed some TODOs
This commit is contained in:
parent
c59b58daf0
commit
5fa0a3a7c2
|
@ -32,7 +32,7 @@ type User struct {
|
||||||
Bio string `db:"bio"`
|
Bio string `db:"bio"`
|
||||||
Blurb string `db:"blurb"`
|
Blurb string `db:"blurb"`
|
||||||
Signature string `db:"signature"`
|
Signature string `db:"signature"`
|
||||||
Avatar *string `db:"avatar"` // TODO: Image field stuff?
|
Avatar *string `db:"avatar"`
|
||||||
|
|
||||||
DarkTheme bool `db:"darktheme"`
|
DarkTheme bool `db:"darktheme"`
|
||||||
Timezone string `db:"timezone"`
|
Timezone string `db:"timezone"`
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
{{ template "pagination.html" .Pagination }}
|
{{ template "pagination.html" .Pagination }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/* TODO: Breadcrumbs, or some other link back to the blog index */}}
|
|
||||||
{{ if .Posts }}
|
{{ if .Posts }}
|
||||||
{{ range .Posts }}
|
{{ range .Posts }}
|
||||||
<div class="flex items-start ph3 pv3 background-even">
|
<div class="flex items-start ph3 pv3 background-even">
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
{{ template "base.html" . }}
|
{{ template "base.html" . }}
|
||||||
|
|
||||||
{{ define "extrahead" }}
|
|
||||||
{{/* TODO
|
|
||||||
<script type="text/javascript" src="{% static 'util.js' %}?v={% cachebust %}"></script>
|
|
||||||
*/}}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="content-block">
|
<div class="content-block">
|
||||||
<div class="optionbar">
|
<div class="optionbar">
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
<a class="login" id="login-link" href="{{ .LoginPageUrl }}">Log in</a>
|
<a class="login" id="login-link" href="{{ .LoginPageUrl }}">Log in</a>
|
||||||
<div id="login-popup">
|
<div id="login-popup">
|
||||||
<form action="{{ .Header.LoginActionUrl }}" method="post">
|
<form action="{{ .Header.LoginActionUrl }}" method="post">
|
||||||
{{/* TODO: CSRF */}}
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th><label>Username:</label></th>
|
<th><label>Username:</label></th>
|
||||||
|
@ -21,8 +20,8 @@
|
||||||
<th><label>Password:</label></th>
|
<th><label>Password:</label></th>
|
||||||
<td><input type="password" name="password" class="textbox password" value="" /></td>
|
<td><input type="password" name="password" class="textbox password" value="" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/* TODO: Forgot password flow? Or just on standalone page? */}}
|
|
||||||
</table>
|
</table>
|
||||||
|
<a class="db" style="padding: 0.5rem 0;" href="{{ .Header.ForgotPasswordUrl }}">Forgot your password?</a>
|
||||||
<input type="hidden" name="redirect" value="{{ $.CurrentUrl }}">
|
<input type="hidden" name="redirect" value="{{ $.CurrentUrl }}">
|
||||||
<div class="pt2">
|
<div class="pt2">
|
||||||
<input type="submit" value="Log In" />
|
<input type="submit" value="Log In" />
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
let currentYear = null;
|
let currentYear = null;
|
||||||
for (let i = 0; i < showcaseItems.length; i++) {
|
for (let i = 0; i < showcaseItems.length; i++) {
|
||||||
const item = showcaseItems[i];
|
const item = showcaseItems[i];
|
||||||
const date = new Date(item.date * 1000); // TODO(asaf): Verify that this is still correct with our new JSON marshalling
|
const date = new Date(item.date * 1000);
|
||||||
|
|
||||||
if (date.getMonth() !== currentMonth || date.getFullYear() !== currentYear) {
|
if (date.getMonth() !== currentMonth || date.getFullYear() !== currentYear) {
|
||||||
if (currentMonthElements.length > 0) {
|
if (currentMonthElements.length > 0) {
|
||||||
|
|
|
@ -249,12 +249,3 @@ var HMNTemplateFuncs = template.FuncMap{
|
||||||
return snippet.Type == TimelineTypeSnippetYoutube
|
return snippet.Type == TimelineTypeSnippetYoutube
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(asaf): Delete these?
|
|
||||||
type ErrInvalidHexColor struct {
|
|
||||||
color string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ErrInvalidHexColor) Error() string {
|
|
||||||
return fmt.Sprintf("invalid hex color: %s", e.color)
|
|
||||||
}
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ type Header struct {
|
||||||
UserSettingsUrl string
|
UserSettingsUrl string
|
||||||
LoginActionUrl string
|
LoginActionUrl string
|
||||||
LogoutActionUrl string
|
LogoutActionUrl string
|
||||||
|
ForgotPasswordUrl string
|
||||||
RegisterUrl string
|
RegisterUrl string
|
||||||
HMNHomepageUrl string
|
HMNHomepageUrl string
|
||||||
ProjectHomepageUrl string
|
ProjectHomepageUrl string
|
||||||
|
|
|
@ -18,11 +18,6 @@ import (
|
||||||
"git.handmade.network/hmn/hmn/src/templates"
|
"git.handmade.network/hmn/hmn/src/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(asaf): Add a middleware that guarantees the certain handlers will take at least X amount of time.
|
|
||||||
// Will be relevant for:
|
|
||||||
// * Login POST
|
|
||||||
// * Register POST
|
|
||||||
|
|
||||||
var UsernameRegex = regexp.MustCompile(`^[0-9a-zA-Z][\w-]{2,29}$`)
|
var UsernameRegex = regexp.MustCompile(`^[0-9a-zA-Z][\w-]{2,29}$`)
|
||||||
|
|
||||||
type LoginPageData struct {
|
type LoginPageData struct {
|
||||||
|
@ -46,7 +41,6 @@ func LoginPage(c *RequestContext) ResponseData {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Login(c *RequestContext) ResponseData {
|
func Login(c *RequestContext) ResponseData {
|
||||||
// TODO: Update this endpoint to give uniform responses on errors and to be resilient to timing attacks.
|
|
||||||
if c.CurrentUser != nil {
|
if c.CurrentUser != nil {
|
||||||
return RejectRequest(c, "You are already logged in.")
|
return RejectRequest(c, "You are already logged in.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,6 @@ func Forum(c *RequestContext) ResponseData {
|
||||||
|
|
||||||
for _, sfNode := range subforumNodes {
|
for _, sfNode := range subforumNodes {
|
||||||
c.Perf.StartBlock("SQL", "Fetch count of subforum threads")
|
c.Perf.StartBlock("SQL", "Fetch count of subforum threads")
|
||||||
// TODO(asaf): [PERF] [MINOR] Consider replacing querying count per subforum with a single query for all subforums with GROUP BY.
|
|
||||||
numThreads, err := db.QueryInt(c.Context(), c.Conn,
|
numThreads, err := db.QueryInt(c.Context(), c.Conn,
|
||||||
`
|
`
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
|
@ -207,7 +206,6 @@ func Forum(c *RequestContext) ResponseData {
|
||||||
c.Perf.EndBlock()
|
c.Perf.EndBlock()
|
||||||
|
|
||||||
c.Perf.StartBlock("SQL", "Fetch subforum threads")
|
c.Perf.StartBlock("SQL", "Fetch subforum threads")
|
||||||
// TODO(asaf): [PERF] [MINOR] Consider batching these.
|
|
||||||
itThreads, err := db.Query(c.Context(), c.Conn, threadQueryResult{},
|
itThreads, err := db.Query(c.Context(), c.Conn, threadQueryResult{},
|
||||||
`
|
`
|
||||||
SELECT $columns
|
SELECT $columns
|
||||||
|
@ -261,7 +259,7 @@ func Forum(c *RequestContext) ResponseData {
|
||||||
|
|
||||||
baseData := getBaseData(c)
|
baseData := getBaseData(c)
|
||||||
baseData.Title = c.CurrentProject.Name + " Forums"
|
baseData.Title = c.CurrentProject.Name + " Forums"
|
||||||
baseData.Breadcrumbs = []templates.Breadcrumb{ // TODO(ben): This is wrong; it needs to account for subforums.
|
baseData.Breadcrumbs = []templates.Breadcrumb{
|
||||||
{
|
{
|
||||||
Name: c.CurrentProject.Name,
|
Name: c.CurrentProject.Name,
|
||||||
Url: hmnurl.BuildProjectHomepage(c.CurrentProject.Slug),
|
Url: hmnurl.BuildProjectHomepage(c.CurrentProject.Slug),
|
||||||
|
|
|
@ -140,11 +140,21 @@ func (c *RequestContext) URL() *url.URL {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RequestContext) FullUrl() string {
|
func (c *RequestContext) FullUrl() string {
|
||||||
var scheme string // TODO(asaf): BEFORE RELEASE!! Fetch scheme from X-Forwarded-* headers or Forwarded header
|
var scheme string
|
||||||
if c.Req.TLS != nil {
|
|
||||||
scheme = "https://"
|
if scheme == "" {
|
||||||
} else {
|
proto, hasProto := c.Req.Header["X-Forwarded-Proto"]
|
||||||
scheme = "http://"
|
if hasProto {
|
||||||
|
scheme = fmt.Sprintf("%s://", proto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if scheme == "" {
|
||||||
|
if c.Req.TLS != nil {
|
||||||
|
scheme = "https://"
|
||||||
|
} else {
|
||||||
|
scheme = "http://"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return scheme + c.Req.Host + c.Req.URL.String()
|
return scheme + c.Req.Host + c.Req.URL.String()
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
|
||||||
// NOTE(asaf): HMN-only routes:
|
// NOTE(asaf): HMN-only routes:
|
||||||
mainRoutes.GET(hmnurl.RegexOldHome, Index)
|
mainRoutes.GET(hmnurl.RegexOldHome, Index)
|
||||||
|
|
||||||
mainRoutes.POST(hmnurl.RegexLoginAction, Login)
|
mainRoutes.POST(hmnurl.RegexLoginAction, securityTimerMiddleware(time.Millisecond*100, Login)) // TODO(asaf): Adjust this after launch
|
||||||
mainRoutes.GET(hmnurl.RegexLogoutAction, Logout)
|
mainRoutes.GET(hmnurl.RegexLogoutAction, Logout)
|
||||||
mainRoutes.GET(hmnurl.RegexLoginPage, LoginPage)
|
mainRoutes.GET(hmnurl.RegexLoginPage, LoginPage)
|
||||||
|
|
||||||
|
@ -297,6 +297,7 @@ func getBaseData(c *RequestContext) templates.BaseData {
|
||||||
UserSettingsUrl: hmnurl.BuildUserSettings(""),
|
UserSettingsUrl: hmnurl.BuildUserSettings(""),
|
||||||
LoginActionUrl: hmnurl.BuildLoginAction(c.FullUrl()),
|
LoginActionUrl: hmnurl.BuildLoginAction(c.FullUrl()),
|
||||||
LogoutActionUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
|
LogoutActionUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
|
||||||
|
ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(),
|
||||||
RegisterUrl: hmnurl.BuildRegister(),
|
RegisterUrl: hmnurl.BuildRegister(),
|
||||||
HMNHomepageUrl: hmnurl.BuildHomepage(),
|
HMNHomepageUrl: hmnurl.BuildHomepage(),
|
||||||
ProjectHomepageUrl: hmnurl.BuildProjectHomepage(c.CurrentProject.Slug),
|
ProjectHomepageUrl: hmnurl.BuildProjectHomepage(c.CurrentProject.Slug),
|
||||||
|
|
Loading…
Reference in New Issue