Improve post query performance

This commit is contained in:
Ben Visness 2021-04-26 20:49:46 -05:00
parent 1d9ad49db4
commit 649f353b8c
20 changed files with 771 additions and 50 deletions

1
oldstaticpages/TODO.txt Normal file
View File

@ -0,0 +1 @@
// TODO: Delete this folder!

50
oldstaticpages/about.txt Normal file
View File

@ -0,0 +1,50 @@
<p>
We're a small group of programmers and idealists who are trying to make
programming better for everyone. And we think the best way to do that is by
collecting the best, down-to-earth programming projects in one place and
building a community of the most talented programmers and most eager learners we
can find.
</p>
<h2 class="realname">Abner Coimbre</h2>
<p class="bio">
Abner Coimbre started Handmade Network when rallying the Handmade Hero community around a common goal. He leads the policies, initiatives, and public facing of the network. Abner became NASA's Kennedy Intern of the Year in 2015 and was named to Kennedys 2016 Top 10 Innovators. In 2017 Abner moved to Seattle, WA to work for Jonathan Blow's studio Thekla Inc.
</p>
<p>Twitter: <a target="_blank" href="https://twitter.com/AbnerCoimbre">@AbnerCoimbre</a></p>
<br/>
<h2 class="realname">Jeroen van Rijn</h2>
<p class="bio">
Jeroen is just this guy, you know. He's a veteran programmer who at one time found himself chin deep in webdev and
decided the best thing for his sanity was to retire from it. Then Abner proposed what would become Handmade Network and
he decided to dust off his keyboard and join the fray, only to find the keyboard wasn't particularly dusty yet. Some
keys were getting stuck or unresponsive, sure, but a replacement keyboard soon fixed that. When not wrangling RSI, he
develops the backend software that makes the website tick. <br/><br/> Apart from this he has a keen interest in low-level
programming, optimisation, compiler writing and games development, among other subjects. When the site software is
sufficiently mature, he plans to do an educational series on the Handmade Network, passing on some of the 30 years of
collected knowledge he's gathered.
</p>
<p>Twitter: <a target="_blank" href="https://twitter.com/J_vanRijn">@J_vanRijn</a></p>
<br/>
<h2 target="_blank" class="realname">Matt Mascarenhas</h2>
<p class="bio">
Matt is a lifelong computer user, music player and book reader. In contrast to everyone else on the Handmade Network
team, his education only touched on computer programming, having culminated with a degree in English language and
literature. Or, rather, it would have culminated there had Casey not begun his educational programming project Handmade
Hero.
<br/><br/>
Within the Handmade Network, Matt has become the person to ask if you want to know if / when Casey covered a particular
topic on Handmade Hero at least until the annotation system receives its long-expected overhaul, at which point he'll
become partially redundant and will be the first port of call should you have any issues with the Handmade Network
website.
<br/><br/>
While not annotating or grepping the annotations for answers to people's questions, Matt practises programming in C with
a view to developing a bunch of software to play, to run your console-based applications, for doing linguistic
research and for staging theatrical productions writes audio and story for games, and evangelises his Arch Linux
system, taking full credit for Abner's discovery of the beautiful distribution.
</p>
<p>Twitter: <a target="_blank" href="https://twitter.com/miblo_">@miblo_</a></p>
<br/>
<h2 class="realname">Andrew Chronister</h2>
<p class="bio">
Andrew is co-developer of Handmade Network and a student of computer engineering at the University of Washington. When he's not coding away at his project of the month, he can be found doing pointless math and cracking bad jokes on niche experimental decentralized social networks.
</p>
<p>Mastodon: <a target="_blank" href="https://cybre.space/@chr">@chr@cybre.space</a></p>

View File

@ -0,0 +1,16 @@
<p>If you are experiencing technical issues with the site, please send a detailed email to
<a href="mailto:team@handmadedev.org">team@handmadedev.org</a>
with a description of the problem, and we'll do our best to address it in a
timely manner.
</p>
<p>For administrative issues, such as moderation disputes or content
revisions, contact any of the staff by email:
</p>
<ul>
<li><a href="mailto:team@handmadedev.org">team@handmadedev.org</a></li>
<li><a href="mailto:ryan@handmadedev.org">ryan@handmadedev.org</a></li>
<li><a href="mailto:ben@handmadedev.org">ben@handmadedev.org</a></li>
<li><a href="mailto:asaf@handmadedev.org">asaf@handmadedev.org</a></li>
<li><a href="mailto:alex@handmadedev.org">alex@handmadedev.org</a></li>
</ul>

View File

@ -0,0 +1,26 @@
<p>
The Handmade community strives to create an environment conducive to
innovation, education, and constructive discussion. To that end, we expect
members of the site to respect a basic set of principles to maintain civil
discourse and create an inclusive environment. It is up to the discretion
of the moderators to determine when these guidelines are being met, and
they are permitted to act accordingly. If you feel an action has been made
against you unreasonably, please <a href="/contact">contact</a> an admin
privately with your grievance and we will review the decision.
</p>
<p>
Some loose principles to follow while on handmade.network:
<ul>
<li>We're mostly business, with a bit of fun. Discussion should primarily be about software development, and in
particular about the relevant project or topic given in the title where you are discussing. Overt advertising
(beyond reasonably relevant mention or reference) in other projects will not be tolerated.</p>
<li>Support arguments with evidence and rational discussion. Don't resort to ad hominem attacks or personality
judgements.</li>
<li>Differing opinions are valuable and should be respected. Not everyone sees eye-to-eye on every
matter, but we're all trying to write useful software. Try to keep an open mind.</li>
<li>Language has meaning, and can be used in destructive ways. Be aware that what you say may not seem
injurious to you, but might make someone else's experience on the site tangibly worse. If you are asked to
reconsider your conduct, please exercise some introspection. We want everyone to feel welcome here.</p>
</ul>
</p>

View File

@ -0,0 +1,23 @@
<p>
The Handmade Network IRC and all channels hosted on it follow the same <a href="/guidelines">basic set of rules</a> as
the rest of the site. You are still using Handmade Network-provided utilities and interacting with the same community,
even if you are not doing so through a web browser pointed at the site. It is up to the channel and server operators to
decide how to best maintain a civil and constructive chat environment, and they will act as they see fit to maintain it.
</p>
<p>
Additional notes:
<ul>
<li>Spamming, repetitive arguments that clog up the chat, or refusal to cooperate with a reasonable request from
a moderator may result in a ban from the chat at the moderator's sole discretion.</li>
<li>Excessive join/part messages annoy some IRC users. If your internet is intermittent and you aren't actively
talking in chat, consider closing your client until connection issues are resolved, or use a repeater. Full logs
on public channels will be made available in the near future.</li>
<li>Abusive behavior will not be tolerated. If it is determined that a user or group of users are participating
in targeted abuse (such as serial trolling/flaming and/or making serious threats), they will be banned
immediately.</li>
</ul>
</p>

View File

@ -0,0 +1,20 @@
<br/>
<p> Modern computer hardware is amazing. Manufacturers have orchestrated billions of pieces of silicon into terrifyingly complex and efficient structures that sweep electrons through innumerable tangled paths, branchings, and reunions with the sole purpose of performing computations at more than a billion times per second. This awe-inspiring piece of computational wizardry has at its disposal multiple billions of uniquely addressible silicon plates where it can store the results of millions of computations in an array of several vanishingly small chips. All of this hardware, though each component often sits no further than 7 or 8 centimeters away from the others, cycles so fast that the speed of light, a physical law of the universe, limits the rate at which they communicate with each other.
</p><p><span class="big">So why is software still slow?</span>
</p><p>Why does it take your operating system 10 seconds, 30 seconds, a minute to boot up? Why does your word processor freeze when you save a document on the cloud? Why does your web browser take 3, 4, 10 seconds to load a web page? Why does your phone struggle to keep more than a few apps open at a time? And why does each update somehow make the problem worse?
</p><p><span class="big">We made it slow</span>.
</p><p>Not necessarily you, not necessarily me, not necessarily any single person in particular. But we, the software development community, made it slow by ignoring the fundamental reality of our occupation. We write code, code that runs on computers. Real computers, with central processing units and random access memory and hard disk drives and display buffers. Real computers, with integer and bitwise math and floating point units and L2 caches, with threads and cores and a tenuous little network connection to a million billion other computers. Real computers not built for ease of human understanding but for blindingly, incomprehensibly fast speed.
</p><p><span class="big">A lot of us have forgotten that</span>.
</p><p>In our haste to get our products, our projects, the works of our hands and minds, to as many people as possible, we take shortcuts. We make assumptions. We generalize, and abstract, and assume that just because these problems have been solved before that they never need to be solved again. We build abstraction layers, then forget we built them and build more on top.
</p><p>And it's true that many of us think we do not have the time, the money, the mental bandwidth to always consider these things in detail. The deadline is approaching or the rent is due or we have taxes to fill out and a manager on our back and someone asking us why we always spend so much time at the office, and we just have to stick the library or virtual machine or garbage collector in there to cover up the places we can't think through right now.
</p><p>Others of us were never taught to think about the computer itself. We learned about objects and classes and templates and how to make our code clean and pretty. We learned how to write code to make the client or the manager or the teacher happy, but made the processor churn. And because we did, that amazing speed we'd been granted was wasted, by us, in a death by a thousand abstraction layers.
</p><p><span class="big">But some of us aren't satisfied with that.</span>
</p><p>Some of us take a few extra steps into the covered territory, the wheels sitting, motionless, in a pile behind us, examine their designs and decide there is a better way. The more experienced among us remember how software used to be, the potential that we know exists for computer programs to be useful, general, <em>and</em> efficient. Others of us got fed up with the tools we were expected to use without complaint, but which failed us time and time again. Some of us are just curious and don't know what's good for us. Don't trust what we've been told is good for us.
</p><p>We sat down and looked at our hardware, and examined our data, and thought about how to use the one to transform the other. We tinkered, and measured, and read, and compared, and wrote, and refined, and modified, and measured again, over and over, until we found we had built the same thing, but 10 times faster and incomparably more useful to the people we designed it for. And we had built it by hand.
</p><p>That is what Handmade means. It's not a technique or a language or a management strategy, it isn't a formula or a library or an abstraction. It's an idea. The idea that we can build software that works with the computer, not against it. The idea that sometimes an individual programmer can be more productive than a large team, that a small group can do more than an army of software engineers and *do it better*. The idea that programming is about transforming data and we wield the code, the tool we use to bend that data to our will.
</p><p> It doesn't require a degree, or a dissertation, or a decade of experience. You don't need an
expensive computer or a certificate or even prior knowledge. All you need is an open mind and a sense of
curiosity. We'll help you with the rest.
</p><p><span class="big">Will you join us?</span>
</p><p>Will you build your software by hand?</p>

View File

@ -0,0 +1,98 @@
<p>The Handmade Network provides a place for project owners such as yourself
to showcase and record the development of their projects, and for
users to discover, follow and discuss these projects. This Monthly
Update Policy applies to your project if it doesnt fit into a category listed under
“When the Monthly Update Policy Doesnt Apply” below, and is
intended to encourage development and guard against your active or
complete project having to share web space with abandoned ones.</p>
<h2>What Constitutes an Update</h2>
<p>Outlined
are the ways a project owner, or a well-meaning member of the
projects community, may contribute to a monthly update.</p>
<h3>Blog Post</h3>
<p>The most straightforward way to update the community with your progress
is with a simple blog post within the network. The length of the post
is not measured, as the network understands an individual may have
difficult months. Therefore, anything in the spectrum of a quick
message saying you had no progress, to a summary of a chat
discussion, all the way up to a full-blown detailed development log
would be considered a monthly update. Even with posts detailing your
inability to work for a month, looking back at your own development
history is an important exercise.</p>
<h3>Forum Activity</h3>
<p>Any activity in your projects forum would satisfy the monthly update
policy. This includes contributions to any forum discussions from you
as the project owner and from members of the community. </p>
<h3>Link to External Update</h3>
<p>YouTube videos, GitHub / GitLab repository links (e.g. to a recent commit),
Reddit thread, Twitter threads discussing the projects current
state, your own personal blog posts, are a few examples of what would
work here. Such updates must be linked somewhere in your project page, blog, or
forums in order to qualify. As a reminder, having
a hyperlink in to your external blog in your signature or profile
links that visitors are expected to follow up on without further
prompting from you doesnt count. </p>
<h2> When the Monthly Update Policy Doesnt Apply</h2>
<h3>Grace Period</h3>
<p>New projects are not required to provide updates until the month
following their approval. For example, if a project is posted in the
middle of March, they are not required to post any further updates
for March the monthly update policy will go in effect for that
project in April.</p>
<h3>Personal Circumstances</h3>
<p>If the developer foresees not being able to provide a timely update this
month, or possibly even for a number of consecutive months, we invite
them to post a short update on their project blog letting their
audience know theyll be taking a leave of absence. A reason need
not be supplied. Should you feel uncomfortable in doing so, an email
to <a href="mailto:webmaster@handmadedev.org">webmaster@handmadedev.org</a>
in private will let us flag the project as in hiatus and it will let
us deflect incoming questions about the projects status in your
stead.</p>
<p>Although a blog post explaining the leave of absence will suffice, such an
e-mail is still appreciated, of course.</p>
<h3>Project Completion</h3>
<p>When the project owner is confident the project has entered a stage of
completion, a stage of maintenance with no further features, or it
has changed identity such that it is no longer considered the
original project, the monthly update would no longer be necessary.
Please advise us of this so that we may update the
status of your project, both for our bookkeeping, and for the
purposes of sorting projects by status.</p>
<h2>What happens if the Project is Inactive</h2>
<h3>First Strike</h3>
<p>If for any month there was no activity, without having notified the
network, the project owner will receive an e-mail within the first
week of the following month. No further action will be taken for that
month.</p>
<h3>Second Strike and Beyond</h3>
<p>If for any subsequent month there is no activity, without having notified
the network, the project will receive an e-mail within the first week
of the following month and will be flagged as in-hiatus until
activity resumes. This will always happen for any inactive month
following the first strike of the year. At the start of a new year,
the strikes are reset, applying the first one first.</p>
<p>The project, while in hiatus, will remain as part of the project listings
but may be pushed down the list, and may not be considered as a
featured project while in this status. If it was a featured project
at the time of being flagged due to a strike, it will be removed from
the featured list.</p>
<h3>Three Months Inactivity</h3>
<p>If the project has been in hiatus for three months, without having
notified the network, it is considered dead and removed from the
project listings. E-mail <a href="mailto:webmaster@handmadedev.org">webmaster@handmadedev.org</a>
to re-instate it the first time this occurs.</p>
<p>The second time a project reaches this status, without having notified
the network, it will be considered permanently dead. At this point,
the project will need to be re-submitted.</p>
<h2>How to be Featured</h2>
<p>More active projects have a better chance at being noticed by members and
staff alike. Popular projects are more likely to receive votes
towards a monthly community featured-project slot, though community
interest can be piqued even by small or new projects if the project
is producing interesting content. Community-chosen featured-project
slots make up to 3 of the possible highlighted projects -- there are
also up to 3 staff-pick featured slots each month. The recipients of
these slots are at the discretion of the site staff, but you can make
your project shine by frequently and consistently reporting progress
on your project and interacting with the community.</p>

21
oldstaticpages/press.txt Normal file
View File

@ -0,0 +1,21 @@
<p>
To refer to the organization developing this website, please use "Handmade
Dev". To refer to this website directly, use "the Handmade Network" or
"handmade.network" (with that capitalisation). Handmade Dev is not yet an
official organization, but we are working on filing paperwork to register as a
501(c) Non-Profit.
</p>
<p>
If you wish to include an official logo for the website in an external piece of
work (news article, linkback, credit, etc), please use one of the following
logos, sized as appropriate. The variations with transparent backgrounds may be
recolored as appropriate for the surrounding context. If you would like higher
resolution images or vector outlines, please email one of the staff with your request.
</p>
<img src="/static/press/hmd_logo_color.png" width="300">
<img src="/static/press/hmd_logo.png" width="300" style="background-color: #aaa; padding: 20px;">
<img src="/static/press/hmn_logo_color.png" width="300">
<img src="/static/press/hmn_logo.png" width="300" style="background-color: #aaa; padding: 20px;">

View File

@ -0,0 +1,199 @@
<p class="western" style="margin-bottom: 0in" align="CENTER"><b>Project
Submission Guidelines</b></p>
<p class="western" style="margin-bottom: 0in"><br>
</p>
<p class="western" style="margin-bottom: 0in">At Handmade Network, we
pride ourselves on being open to hosting all sorts of projects, from
low level tools to games to conscientious web applications to
innumerable other carefully crafted varieties of software. With this
in mind, there are certain standards of content quality that we
expect projects hosted on the site to meet, and certain
characteristics that we believe projects should uphold in order to
best contribute to the community of software development we are
attempting to cultivate.
</p>
<p class="western" style="margin-bottom: 0in"><br>
</p>
<p class="western" style="margin-bottom: 0in">This document outlines
these standards and characteristics as specifically as possible,
allowing for exceptions both in favor and against project approval if
unanticipated circumstances arise. All project approvals or
rejections will be accompanied by a justification, and we expect to
be held accountable if the provided justification is insufficiently
backed by these guidelines.</p>
<p class="western" style="margin-bottom: 0in"><br>
</p>
<p class="western" style="margin-bottom: 0in"><b>Content Quality</b></p>
<p class="western" style="margin-bottom: 0in; font-weight: normal">A
high-quality Handmade Network project submission will:</p>
<ol>
<li><p class="western" style="margin-bottom: 0in"><b>Have a relevant
and informative blurb</b><span style="font-weight: normal"> which
gives those browsing project listings a basic idea of the project's
purpose and value. The 'elevator pitch', if you will.</span></p>
</li><li><p class="western" style="margin-bottom: 0in"><b>Have a thorough
description</b><span style="font-weight: normal">, which will give
visitors several vital pieces of information in understanding the
project:</span></p>
<ol type="i">
<li><p class="western" style="margin-bottom: 0in; font-weight: normal">
The background / impetus for the projects creation</p>
</li><li><p class="western" style="margin-bottom: 0in; font-weight: normal">
The project's short-term and long-term goals</p>
</li><li><p class="western" style="margin-bottom: 0in; font-weight: normal">
The current status of the project</p>
</li><li><p class="western" style="margin-bottom: 0in"><span style="font-weight: normal">The
road-map to reaching these goals, both short- and long-term</span></p>
</li></ol>
</li></ol>
<p class="western" style="margin-bottom: 0in; font-weight: normal"> Excellent
descriptions will also make use of (BBCode) markup to make it easier
for readers to scan for relevant information.</p>
<ol start="3">
<li><p class="western" style="margin-bottom: 0in"><b>Provide links
to other relevant websites</b> where visitors may find out more
about project development or the current activity of the author(s).
Common examples:</p>
<ol type="i">
<li><p class="western" style="margin-bottom: 0in">A YouTube channel
where the author(s) showcase latest features or record development
logs</p>
</li><li><p class="western" style="margin-bottom: 0in">An external
project homepage where new development builds can be found</p>
</li><li><p class="western" style="margin-bottom: 0in">An external
source-code hosting site where the project's source code is
available</p>
</li><li><p class="western" style="margin-bottom: 0in">A social media
page for the project or the author(s) showing development progress</p>
</li></ol>
</li><li><p class="western" style="margin-bottom: 0in">P<b>rovide several
screenshots showcasing the current state of project development</b>,
if the project is visually oriented. Examples:</p>
<ol type="i">
<li><p class="western" style="margin-bottom: 0in">A project with a
graphical user interface should show what a typical user will see
when interacting with the running program</p>
</li><li><p class="western" style="margin-bottom: 0in">Same goes for
command line tool with a curses-like TUI</p>
</li><li><p class="western" style="margin-bottom: 0in">A game or game
engine should provide several screenshots to give visitors an idea
of what makes it unique</p>
</li><li><p class="western" style="margin-bottom: 0in">Visually-oriented
projects with support for multiple platforms should provide at
least one screenshot of the project running on each platform</p>
</li></ol>
</li><li><p class="western" style="margin-bottom: 0in"><b>Where possible,
provide current builds of the project</b> that visitors can run to
see the state of the project.</p>
</li><li><p class="western" style="margin-bottom: 0in"><b>Customize the
project page with appropriate colors or background images</b> to
give the project a sense of visual identity.</p>
</li></ol>
<p class="western" style="margin-bottom: 0in"><br>
</p>
<p class="western" style="margin-bottom: 0in">Providing as much
information about your project as possible will help us decide
whether it meets the qualifications listed below, and whether
community members will be invested in your project's success.</p>
<p class="western" style="margin-bottom: 0in"><br>
</p>
<p class="western" style="margin-bottom: 0in"><b>Acceptable Projects</b></p>
<p class="western" style="margin-bottom: 0in; font-weight: normal">For
us to be willing to approve a project on the site, it should:</p>
<ol>
<li><p class="western" style="margin-bottom: 0in"><b>Be a
legitimate, unique, and original work.</b><span style="font-weight: normal">
We unequivocally will not accept projects which are blatantly
advertisements for unrelated websites or companies, projects which
are indistinguishable from others on the site, projects which are
incorrectly attributed to someone other than the actual creator. </span>
</p>
</li><li><p class="western" style="margin-bottom: 0in"><b>Have a
meaningful amount of development work planned or completed above its
dependencies, parent project, or previous incarnation.</b><span style="font-weight: normal">
We will not accept projects which cannot prove themselves to be more
than a thin layer of “glue code” above several libraries, or
which are barely-modified forks of other projects.</span></p>
</li><li><p class="western" style="margin-bottom: 0in"><b>Be the
development effort of an individual or small team, organization or
company.</b><span style="font-weight: normal"> We wish to keep the
focus of this site on projects which highlight the inspiring work of
small developers, projects which provide a high ratio of value-added
to man-hours worked, and the exploration of software creation as a
craft. We will refrain from defining “small” for the purposes of
this guideline, and instead give examples of approvals and
rejections:</span></p>
<ol type="i">
<li><p class="western" style="margin-bottom: 0in; font-weight: normal">
A commercial compression tool developed by a small team of 2-4
programmers and a few miscellaneous staff under the umbrella of an
LLC would be approved.</p>
</li><li><p class="western" style="margin-bottom: 0in; font-weight: normal">
A suite of productivity software developed by a large multinational
corporation would be rejected, as it is not in line with the
intended focus of the site, and such a corporation is likely
already receiving widespread exposure elsewhere and may have their
own extensive user community.</p>
</li><li><p class="western" style="margin-bottom: 0in; font-weight: normal">
An existing, well-established open-source project with a
contributor count at the time of submission in the thousands may be
approved or rejected depending on other factors like the age of the
project, the number of current contributors, the content to be
provided on Handmade Network vs the project's homepage, and the
current state of development of the project.</p>
</li></ol>
</li><li><p class="western" style="margin-bottom: 0in"><b>Enrich to the
community </b><span style="font-weight: normal">by releasing
finished projects for community members to use or purchase and
providing regular updates that inform and educate community members.
We heavily encourage activities such as:</span></p>
<ol type="i">
<li><p class="western" style="margin-bottom: 0in; font-weight: normal">
Writing or recording logs/articles explaining decisions/trade-offs
made during development and their rationale, theory behind a piece
of code, information discovered about an API or dependency.</p>
</li><li><p class="western" style="margin-bottom: 0in; font-weight: normal">
Releasing demos, samples, or pre-release versions demonstrating the
mechanics or evolution of a particular feature</p>
</li><li><p class="western" style="margin-bottom: 0in"><span style="font-weight: normal">Responding
to questions about functionality in comments/forums</span><b>.</b></p>
</li></ol>
</li></ol>
<p class="western" style="margin-bottom: 0in"><b> </b><span style="font-weight: normal">For
more information on this, please see our <a href="/monthly-update-policy">Monthly Update Policy</a>
requirements and suggestions.</span></p>
<ol start="5">
<li><p class="western" style="margin-bottom: 0in"><b>Have a clear,
achievable goal or set of goals</b><span style="font-weight: normal">,
and </span><b>provide lasting value to its intended audience.</b><span style="font-weight: normal">
We want to discourage projects from falling into development limbo
and slowly dying off. We also want to encourage project owners to
support their projects past release, so that they might have
continuing usefulness as the dependencies and platforms they rely on
continue to develop.</span></p>
</li></ol>
<p class="western" style="margin-bottom: 0in"><br>
</p>
<p class="western" style="margin-bottom: 0in"><span style="font-weight: normal">Additionally,
there are some criteria that we guarantee will </span><b>not</b><span style="font-weight: normal">
be considered in approving or rejecting a project:</span></p>
<ol>
<li><p class="western" style="margin-bottom: 0in"><b>Implementation
language</b><span style="font-weight: normal">. A language is merely
a tool, and worthwhile software can be created in any language or
environment.</span></p>
</li><li><p class="western" style="margin-bottom: 0in"><b>Use (or not) of
libraries</b><span style="font-weight: normal">. While we are
supportive of efforts to develop software that makes minimal use of
libraries, and to write code that supplants existing libraries, this
is by no means a requirement of projects hosted on the site. We
merely ask, as stated above, that projects perform some significant
work above that which its dependencies perform on their own.</span></p>
</li><li><p class="western" style="margin-bottom: 0in"><b>License,
monetization, and source-code provision</b><span style="font-weight: normal">.
We do not require projects be open-source, free software, etc. While
we are very supportive of these movements and the principles behind
them, we also understand the need to make a living off of
development work, and wish to make it possible for any project to
contribute to and benefit from our software development community.</span></p>
</li></ol>

View File

@ -9,8 +9,8 @@ set -eou pipefail
# TODO(opensource): We should adapt Asaf's seedfile command and then delete this.
THIS_PATH=$(pwd)
#BETA_PATH='/mnt/c/Users/bvisn/Developer/handmade/handmade-beta'
BETA_PATH='/Users/benvisness/Developer/handmade/handmade-beta'
BETA_PATH='/mnt/c/Users/bvisn/Developer/handmade/handmade-beta'
# BETA_PATH='/Users/benvisness/Developer/handmade/handmade-beta'
cd $BETA_PATH
docker-compose down -v
@ -22,5 +22,5 @@ cd $THIS_PATH
go run src/main.go migrate 2021-03-10T05:16:21Z
cd $BETA_PATH
#./scripts/db_import -d -n hmn_two -a ./dbdumps/hmn_pg_dump_2020-11-10
./scripts/db_import -d -n hmn_two -a ./dbdumps/hmn_pg_dump_2021-04-25
./scripts/db_import -d -n hmn_two -a ./dbdumps/hmn_pg_dump_2021-04-26
# ./scripts/db_import -d -n hmn_two -a ./dbdumps/hmn_pg_dump_2021-04-25

View File

@ -317,18 +317,21 @@ func SeedFromFile(seedFile string, afterMigration types.MigrationVersion) {
)
// NOTE(asaf): We have to use the low-level API of pgconn, because the pgx Exec always wraps the query in a transaction.
lowLevelConn, err := pgconn.Connect(ctx, template1DSN)
if err != nil {
panic(fmt.Errorf("failed to connect to db: %w", err))
}
defer lowLevelConn.Close(ctx)
result := lowLevelConn.ExecParams(ctx, "DROP DATABASE hmn", nil, nil, nil, nil)
result := lowLevelConn.ExecParams(ctx, fmt.Sprintf("DROP DATABASE %s", config.Config.Postgres.DbName), nil, nil, nil, nil)
_, err = result.Close()
pgErr, isPgError := err.(*pgconn.PgError)
if err != nil {
if !isPgError || pgErr.SQLState() != "3D000" { // NOTE(asaf): 3D000 means "Database does not exist"
if !(isPgError && pgErr.SQLState() == "3D000") { // NOTE(asaf): 3D000 means "Database does not exist"
panic(fmt.Errorf("failed to drop db: %w", err))
}
}
result = lowLevelConn.ExecParams(ctx, "CREATE DATABASE hmn", nil, nil, nil, nil)
result = lowLevelConn.ExecParams(ctx, fmt.Sprintf("CREATE DATABASE %s", config.Config.Postgres.DbName), nil, nil, nil, nil)
_, err = result.Close()
if err != nil {
panic(fmt.Errorf("failed to create db: %w", err))

View File

@ -1,6 +1,7 @@
package migrations
import (
"context"
"time"
"git.handmade.network/hmn/hmn/src/migration/types"

View File

@ -0,0 +1,90 @@
package migrations
import (
"context"
"fmt"
"time"
"git.handmade.network/hmn/hmn/src/migration/types"
"git.handmade.network/hmn/hmn/src/oops"
"github.com/jackc/pgx/v4"
)
func init() {
registerMigration(DeleteStaticPages{})
}
type DeleteStaticPages struct{}
func (m DeleteStaticPages) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2021, 4, 26, 0, 0, 0, 0, time.UTC))
}
func (m DeleteStaticPages) Name() string {
return "DeleteStaticPages"
}
func (m DeleteStaticPages) Description() string {
return "Delete static page posts"
}
func (m DeleteStaticPages) Up(ctx context.Context, tx pgx.Tx) error {
res, err := tx.Exec(ctx,
`
ALTER TABLE handmade_project
DROP static_id;
`,
)
if err != nil {
return oops.New(err, "failed to drop static_id column")
}
res, err = tx.Exec(ctx,
`
DELETE FROM handmade_post
WHERE id IN (
SELECT post.id
FROM
handmade_post AS post
LEFT JOIN handmade_thread AS thread ON post.thread_id = thread.id
LEFT JOIN handmade_category AS threadcat ON thread.category_id = threadcat.id
LEFT JOIN handmade_category AS postcat ON post.category_id = postcat.id
WHERE
threadcat.kind = 3
OR postcat.kind = 3
);
`,
)
if err != nil {
return oops.New(err, "failed to delete the posts")
}
fmt.Printf("Deleted %v static pages.\n", res.RowsAffected())
_, err = tx.Exec(ctx,
`
DELETE FROM handmade_thread
WHERE id IN (
SELECT thread.id
FROM
handmade_thread AS thread
JOIN handmade_category AS cat ON thread.category_id = cat.id
WHERE
cat.kind = 3
);
`,
)
if err != nil {
return oops.New(err, "failed to delete the threads")
}
_, err = tx.Exec(ctx, `DELETE FROM handmade_category WHERE kind = 3`)
if err != nil {
return oops.New(err, "failed to delete the categories")
}
return nil
}
func (m DeleteStaticPages) Down(ctx context.Context, tx pgx.Tx) error {
panic("Implement me")
}

View File

@ -0,0 +1,53 @@
package migrations
import (
"context"
"time"
"git.handmade.network/hmn/hmn/src/migration/types"
"git.handmade.network/hmn/hmn/src/oops"
"github.com/jackc/pgx/v4"
)
func init() {
registerMigration(FixPostCategoryId{})
}
type FixPostCategoryId struct{}
func (m FixPostCategoryId) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2021, 4, 26, 0, 0, 0, 1, time.UTC))
}
func (m FixPostCategoryId) Name() string {
return "FixPostCategoryId"
}
func (m FixPostCategoryId) Description() string {
return "Copy category id from thread"
}
func (m FixPostCategoryId) Up(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
UPDATE handmade_post
SET (category_id) = (
SELECT thread.category_id
FROM
handmade_thread AS thread
JOIN handmade_post AS post ON post.thread_id = thread.id
WHERE
post.id = handmade_post.id
)
`,
)
if err != nil {
return oops.New(err, "failed to migrate data from categories")
}
return nil
}
func (m FixPostCategoryId) Down(ctx context.Context, tx pgx.Tx) error {
panic("Implement me")
}

View File

@ -0,0 +1,124 @@
package migrations
import (
"context"
"time"
"git.handmade.network/hmn/hmn/src/migration/types"
"git.handmade.network/hmn/hmn/src/oops"
"github.com/jackc/pgx/v4"
)
func init() {
registerMigration(AddCommonFieldsToPosts{})
}
type AddCommonFieldsToPosts struct{}
func (m AddCommonFieldsToPosts) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2021, 4, 26, 23, 57, 20, 0, time.UTC))
}
func (m AddCommonFieldsToPosts) Name() string {
return "AddCommonFieldsToPosts"
}
func (m AddCommonFieldsToPosts) Description() string {
return "Adds project and category info directly to posts for more efficient queries"
}
func (m AddCommonFieldsToPosts) Up(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
ALTER TABLE handmade_post
ADD category_kind INT,
ADD project_id INT REFERENCES handmade_project (id) ON DELETE RESTRICT;
`,
)
if err != nil {
return oops.New(err, "failed to add columns")
}
_, err = tx.Exec(ctx,
`
UPDATE handmade_post
SET (category_id, category_kind, project_id) = (
SELECT cat.id, cat.kind, cat.project_id
FROM
handmade_category AS cat
JOIN handmade_thread AS thread ON thread.category_id = cat.id
JOIN handmade_post AS post ON post.thread_id = thread.id
WHERE
post.id = handmade_post.id
)
`,
)
if err != nil {
return oops.New(err, "failed to migrate data from categories")
}
_, err = tx.Exec(ctx,
`
CREATE FUNCTION category_id_for_thread(int) returns int as $$
SELECT thread.category_id
FROM handmade_thread AS thread
WHERE thread.id = $1
$$ LANGUAGE SQL;
CREATE FUNCTION category_kind_for_post(int) returns int as $$
SELECT cat.kind
FROM
handmade_post AS post
JOIN handmade_thread AS thread ON post.thread_id = thread.id
JOIN handmade_category AS cat ON thread.category_id = cat.id
WHERE post.id = $1
$$ LANGUAGE SQL;
CREATE FUNCTION project_id_for_post(int) returns int as $$
SELECT cat.project_id
FROM
handmade_post AS post
JOIN handmade_thread AS thread ON post.thread_id = thread.id
JOIN handmade_category AS cat ON thread.category_id = cat.id
WHERE post.id = $1
$$ LANGUAGE SQL;
ALTER TABLE handmade_post
ALTER category_kind SET NOT NULL,
ALTER thread_id SET NOT NULL,
ALTER project_id SET NOT NULL,
ADD CONSTRAINT post_category_id_from_thread CHECK (
category_id_for_thread(thread_id) = category_id
),
ADD CONSTRAINT post_category_kind_from_category CHECK (
category_kind_for_post(id) = category_kind
),
ADD CONSTRAINT post_project_id_from_category CHECK (
project_id_for_post(id) = project_id
);
`,
)
if err != nil {
return oops.New(err, "failed to add constraints")
}
_, err = tx.Exec(ctx,
`
CREATE INDEX post_project_id ON handmade_post (project_id);
CREATE INDEX post_category_kind ON handmade_post (category_kind);
CREATE INDEX post_postdate ON handmade_post (postdate DESC);
CREATE INDEX clri_user_id ON handmade_categorylastreadinfo (user_id);
CREATE INDEX tlri_user_id ON handmade_threadlastreadinfo (user_id);
`,
)
if err != nil {
return oops.New(err, "failed to create indexes")
}
return nil
}
func (m AddCommonFieldsToPosts) Down(ctx context.Context, tx pgx.Tx) error {
panic("Implement me")
}

View File

@ -12,8 +12,11 @@ type Post struct {
AuthorID *int `db:"author_id"`
CategoryID int `db:"category_id"`
ParentID *int `db:"parent_id"`
ThreadID *int `db:"thread_id"` // TODO: This is only null for posts that are actually static pages. Which probably shouldn't be posts anyway. Plz make not null thanks
ThreadID int `db:"thread_id"`
CurrentID int `db:"current_id"`
ProjectID int `db:"project_id"`
CategoryType CategoryType `db:"category_kind"`
Depth int `db:"depth"`
Slug string `db:"slug"`

View File

@ -269,12 +269,10 @@
<div class="flex items-start ph3 pv2 {{ if .Unread }}unread{{ else }}read{{ end }}">
<img class="avatar-icon mr2" src="{{ .User.AvatarUrl }}">
<div class="flex-grow-1">
<div class="overflow-hidden">
<div class="title nowrap truncate"><a href="{{ .Url }}">{{ .Title }}</a></div>
<div class="title mb1"><a href="{{ .Url }}">{{ .Title }}</a></div>
<div class="details">
<a class="user" href="{{ .User.ProfileUrl }}">{{ .User.Name }}</a> &mdash; <span class="datetime">{{ relativedate .Date }}</span>
</div>
</div>
<div class="overflow-hidden mh-5 mt2 relative">
<div>
{{ .Content }}

View File

@ -11,9 +11,9 @@ func PostUrl(post models.Post, catType models.CategoryType, subdomain string) st
switch catType {
// TODO: All the relevant post types. Maybe it doesn't make sense to lump them all together here.
case models.CatTypeBlog:
return hmnurl.ProjectUrl(fmt.Sprintf("blogs/p/%d/e/%d", *post.ThreadID, post.ID), nil, subdomain)
return hmnurl.ProjectUrl(fmt.Sprintf("blogs/p/%d/e/%d", post.ThreadID, post.ID), nil, subdomain)
case models.CatTypeForum:
return hmnurl.ProjectUrl(fmt.Sprintf("forums/t/%d/p/%d", *post.ThreadID, post.ID), nil, subdomain)
return hmnurl.ProjectUrl(fmt.Sprintf("forums/t/%d/p/%d", post.ThreadID, post.ID), nil, subdomain)
}
return ""

View File

@ -29,9 +29,8 @@ func Feed(c *RequestContext) ResponseData {
SELECT COUNT(*)
FROM
handmade_post AS post
JOIN handmade_category AS cat ON cat.id = post.category_id
WHERE
cat.kind IN ($1, $2, $3, $4)
post.category_kind IN ($1, $2, $3, $4)
AND NOT moderated
`,
models.CatTypeForum,
@ -77,7 +76,6 @@ func Feed(c *RequestContext) ResponseData {
type feedPostQuery struct {
Post models.Post `db:"post"`
Thread models.Thread `db:"thread"`
Cat models.Category `db:"cat"`
Proj models.Project `db:"proj"`
User models.User `db:"auth_user"`
ThreadLastReadTime *time.Time `db:"tlri.lastread"`
@ -89,19 +87,18 @@ func Feed(c *RequestContext) ResponseData {
FROM
handmade_post AS post
JOIN handmade_thread AS thread ON thread.id = post.thread_id
JOIN handmade_category AS cat ON cat.id = thread.category_id
JOIN handmade_project AS proj ON proj.id = cat.project_id
JOIN handmade_project AS proj ON proj.id = post.project_id
LEFT OUTER JOIN handmade_threadlastreadinfo AS tlri ON (
tlri.thread_id = thread.id
tlri.thread_id = post.thread_id
AND tlri.user_id = $1
)
LEFT OUTER JOIN handmade_categorylastreadinfo AS clri ON (
clri.category_id = cat.id
clri.category_id = post.category_id
AND clri.user_id = $1
)
LEFT OUTER JOIN auth_user ON post.author_id = auth_user.id
WHERE
cat.kind IN ($2, $3, $4, $5)
post.category_kind IN ($2, $3, $4, $5)
AND post.moderated = FALSE
AND post.thread_id IS NOT NULL
ORDER BY postdate DESC
@ -131,7 +128,8 @@ func Feed(c *RequestContext) ResponseData {
hasRead = true
}
parents := postResult.Cat.GetHierarchy(c.Context(), c.Conn)
var parents []models.Category
// parents := postResult.Cat.GetHierarchy(c.Context(), c.Conn)
logging.Debug().Interface("parents", parents).Msg("")
var breadcrumbs []templates.Breadcrumb
@ -148,7 +146,7 @@ func Feed(c *RequestContext) ResponseData {
postItems = append(postItems, templates.PostListItem{
Title: postResult.Thread.Title,
Url: templates.PostUrl(postResult.Post, postResult.Cat.Kind, postResult.Proj.Subdomain()),
Url: templates.PostUrl(postResult.Post, postResult.Post.CategoryType, postResult.Proj.Subdomain()),
User: templates.UserToTemplate(&postResult.User),
Date: postResult.Post.PostDate,
Breadcrumbs: breadcrumbs,

View File

@ -77,7 +77,6 @@ func Index(c *RequestContext) ResponseData {
type projectPostQuery struct {
Post models.Post `db:"post"`
Thread models.Thread `db:"thread"`
Cat models.Category `db:"cat"`
User models.User `db:"auth_user"`
ThreadLastReadTime *time.Time `db:"tlri.lastread"`
CatLastReadTime *time.Time `db:"clri.lastread"`
@ -87,22 +86,20 @@ func Index(c *RequestContext) ResponseData {
SELECT $columns
FROM
handmade_post AS post
JOIN handmade_thread AS thread ON thread.id = post.thread_id
JOIN handmade_category AS cat ON cat.id = thread.category_id
LEFT OUTER JOIN handmade_threadlastreadinfo AS tlri ON (
tlri.thread_id = thread.id
JOIN handmade_thread AS thread ON post.thread_id = thread.id
LEFT JOIN handmade_threadlastreadinfo AS tlri ON (
tlri.thread_id = post.thread_id
AND tlri.user_id = $1
)
LEFT OUTER JOIN handmade_categorylastreadinfo AS clri ON (
clri.category_id = cat.id
LEFT JOIN handmade_categorylastreadinfo AS clri ON (
clri.category_id = post.category_id
AND clri.user_id = $1
)
LEFT OUTER JOIN auth_user ON post.author_id = auth_user.id
LEFT JOIN auth_user ON post.author_id = auth_user.id
WHERE
cat.project_id = $2
AND cat.kind IN ($3, $4, $5, $6)
post.project_id = $2
AND post.category_kind IN ($3, $4, $5, $6)
AND post.moderated = FALSE
AND post.thread_id IS NOT NULL
ORDER BY postdate DESC
LIMIT $7
`,
@ -133,7 +130,7 @@ func Index(c *RequestContext) ResponseData {
}
featurable := (!proj.IsHMN() &&
projectPost.Cat.Kind == models.CatTypeBlog &&
projectPost.Post.CategoryType == models.CatTypeBlog &&
projectPost.Post.ParentID == nil &&
landingPageProject.FeaturedPost == nil)
@ -159,7 +156,7 @@ func Index(c *RequestContext) ResponseData {
landingPageProject.FeaturedPost = &LandingPageFeaturedPost{
Title: projectPost.Thread.Title,
Url: templates.PostUrl(projectPost.Post, projectPost.Cat.Kind, proj.Subdomain()),
Url: templates.PostUrl(projectPost.Post, projectPost.Post.CategoryType, proj.Subdomain()),
User: templates.UserToTemplate(&projectPost.User),
Date: projectPost.Post.PostDate,
Unread: !hasRead,
@ -168,7 +165,7 @@ func Index(c *RequestContext) ResponseData {
} else {
landingPageProject.Posts = append(landingPageProject.Posts, templates.PostListItem{
Title: projectPost.Thread.Title,
Url: templates.PostUrl(projectPost.Post, projectPost.Cat.Kind, proj.Subdomain()),
Url: templates.PostUrl(projectPost.Post, projectPost.Post.CategoryType, proj.Subdomain()),
User: templates.UserToTemplate(&projectPost.User),
Date: projectPost.Post.PostDate,
Unread: !hasRead,