See also: API documentation
PressWord Software believes they can do blogging better. They've hired your freelance team to build out the BlogPress platform and prove it.
Summary of requirements (15 total)
You are essentially building headless CMS software as a service, which hosts bloggers and their blogs.
The app supports three user types: guests, bloggers, and administrators. Authentication occurs via the API. Users interact with content via each blogger's unique blog, which is a sort of self-contained miniature website that can be modified and extended by the blogger. Each user's blog is reachable via a unique URL. While all users can view blogs and bloggers can only manage their own unique blogs, administrators can view and manage any blog in the system.
The app has at least five views: Home, Blog, Builder, Admin, and Auth. The Home view is the landing page of your app. The Blog view renders, depending on the URL, a specific page from a specific user's blog. The Builder view allows an authenticated user to manage their specific blog. The Admin view allows administrators to manage any blog in the system as well as manage other users and create new administrators. The Auth view is used for handling user authentication.
Note that time-based data in the API is represented as the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC. See the API documentation for details. Further note that you must use the API to complete this problem statement, including interacting with data from other chapters, though you may consider a hybrid approach where you have your own database storing non-API data.
We're looking for feedback! If you have any opinions or ideas, contact us on Slack.
🚧 🚧 To avoid disqualification, please take note of the following:
- Unlike PS2, PS1 is a "chapter-wide problem statement". That is: all students, coaches, and coordinators in the chapter can teach to, talk about, and collaborate on a solution. And then, when the conference comes around, your chapter sends your best five students to finish the job.
- Your solution’s landing page must be available at
http://127.0.0.1:3000
(localhost) on your team's AWS WorkSpace. Judges must not have to type in anything other thanhttp://127.0.0.1:3000
to reach your app. - Your solution’s source code must be located at
%USERPROFILE%\Desktop\source
on your team's AWS WorkSpace. You can have other files located elsewhere, so long as they are also visible from the aforesaid directory. - HTTP requests to the API must be sent with an
Authorization
header (Key
header is deprecated). See the API documentation for details.
Your app will support 3 types of users: guest
, blogger
, and
administrator
.
- Are unauthenticated users (i.e. don't have to login)
- Can access the Auth view
- Can access the Blog view
- Are authenticated users (i.e. users that have already logged in)
- Can access their specific Builder view
- Can access all the views
guest
s can access
- Are authenticated users (i.e. users that have already logged in)
- Can access the Admin view
- Can access every other view
- Do not have a blog associated with their account
Home view: display the BlogPress landing page featuring the hard sell.
This is the "home page" of your service, and is the first page users will land on when they navigate to your app. The purpose of this view is to sell the viewer on the awesomeness of your application/solution while introducing them to your service's features.
A good example would be WordPress's landing page.
Blog (Head) view: display a user's blog.
This view is responsible for rendering a specific blogger
's blog (specifically
its pages). From this view, users can view any
of the blog's pages rendered as Markdown.
This view must be accessible by any user (including guest
s) via unique URL.
That is: each user's blog must have a unique URL that leads to that blog. For
example, if your solution is located at http://localhost:3000, the
root page of a blog named "cool-blogio" might be found at
http://localhost:3000/blogs/cool-blogio, assuming a potential URL scheme of
{app-url}/blogs/{blog-name}
. A page named
"about-us" from the same blog might be found at
http://localhost:3000/blogs/cool-blogio/about-us.
Additionally, this view will always display a blog-specific standard navigation element.
Builder (Body) view: manage a user's blog.
This view allows authenticated users to manage their specific blog.
From this view, authenticated users can:
- Create new blog pages
- Delete existing blog pages (except the "home" page)
- Edit the Markdown contents of existing pages
- View a per-page breakdown of (1) the total number of views and (2) the total number of active (non-expired) viewers
- View the cumulative total number of views for the entire blog (sum of all page views)
- Select which pages will be listed in the blog's navigation element
Every blog, when it is first created along with the user account, will have a
default "home" page created for them by the API. This page will be considered
their blog's root page. This default home page will also be
added to the new blog's navigation element by default,
which the blogger
can change later.
When authoring a page, the user must be able to see a preview before they submit it (example). Since pages are authored in Markdown, your app will have to use a library to render the Markdown into HTML, which you can then display to your users. Research and explore which Markdown library is best for your purposes. Here are some suggestions to start with.
Each blog must have a "root" (aka home or landing) page. It is a page just
like any other page, except its name does not need to be entered as part of the
blog's URL to visit it and it cannot be deleted by the user. For example, if
your solution is located at http://localhost:3000, the root page of a blog named
"cool-blogio" would be rendered at http://localhost:3000/blogs/cool-blogio,
assuming a URL scheme of {app-url}/blogs/{blog-name}
.
Given these constraints, blog names must be unique and page names must be unique
per blogger
account. This is enforced at the API level.
Each blog has its own navigation element, which is just a list of at most five pages/URIs that will always appear at the top of the Blog view.
See requirement 8 for more details.
Admin view: view statistics and manage any user blog or account.
This view allows administrator
s to manage existing blogs and accounts.
Specifically, administrator
s can:
- Create, edit, and delete any blog's pages
- Ban
blogger
users (prevents them from being able to login) - Unban
blogger
users - Create new
administrator
accounts - View generic statistics about the system including:
- Total number of users
- Total number of blogs
- Total number of pages
- View a list of users, the names of their blogs, and related statistics
including:
- If the user is banned or not
- Cumulative total number of views for the entire blog (sum of all page views)
- Total number of views per page (this data can be hidden behind a drop-down or some other "focus" mechanic if desired)
Additionally, the list of users and associated statistics can be sorted in the UI by:
- Username / email address
- Banned status
- Total number of views across the entire blog
Reminder: consider caching where appropriate.
Auth view: register new users and authenticate existing users.
guest
users can use this view to authenticate (login) using an existing
email and password. This sensitive information is referred to as a user's
credentials.
guest
users can also use this view to register new credentials and create new
blogger
accounts.
Your app must use
the API
to authenticate the guest
user instead of retrieving the user's credentials
from a local database. Your app will do this by sending the API a
digest value derived
from the username, email address, and password (username + email + password)
provided. See
the API documentation
for more details.
If authed
, the user can choose to logout, after which your app will treat
them like any other guest
user. Logging a user out does not require a call to
the API.
There is an open registration feature guest
users can use to register a new
account. When they do, they must provide at least the following where required:
- Desired username <required>
- Must be lowercase alphanumeric (dashes and underscores are also allowed).
- Email address <required>
- Password <required>
- Password strength must be indicated as well. Weak passwords will be rejected. A weak password is ≤10 characters. A moderate password is 11-17 characters. A strong password is above 17 characters.
- Blog name <required for non-administrators>
- The answer to a simple CAPTCHA challenge of some type <required>
- Example:
what is 2+2=?
- Teams must not call out to any API for this. Teams must create the CAPTCHA manually.
- Example:
Your app must use the API to create the new user instead of storing the user's information locally.
- Usernames, email addresses, and blog names must be unique. That is: no two users can have the same username, email address, or blog name. This is enforced at the API level.
guest
users will be prevented from logging in for 1 hour after 3 failed login attempts.guest
users will always see how many attempts they have left.guest
users will have the option to use remember me functionality to maintain long-running authentication state. That is: if aguest
logs in and wants to be "remembered," they will not be logged out until they manually log out.- You must use the API to create new users, authenticate and store their credentials, and track any relevant metadata. See the API documentation for details on what the API will store for you. Anything not storable at the API level can be stored locally by your solution.
If a user does not remember their password, they can use email to recover their account.
If a user has forgotten their login credentials, they will be able to recover their account by clicking a link in the recovery email sent to their address. Your app will then allow them to set a new password.
The app must not actually send emails out onto the internet. The sending of emails can be simulated however you want, including outputting the would-be email to the console. The app will not use an external API or service for this. For full points, ensure you document how recovery emails are simulated when submitting your solution.
A navigation element containing the BDPA logo, the blog name, the blog's navigation links, and a subset of user data is permanently visible to users when rendering the Blog view.
In the Blog view, a navigation element is permanently visible containing:
- The BDPA logo (downloadable here)
- The name of the blog currently being rendered
- Said blog's navigation links
- If the current user is authenticated: the username/email of the current user and a logout link
- If the current user is unauthenticated: login and register links
- If the user is an
administrator
: a link to the Admin view - If the user is a
blogger
(oradministrator
): a link to the Builder view for the currently rendered blog
Hint: keep maximum interoperability with other teams’ solutions in mind when deciding on your app’s URL scheme and how you’ll handle navigation link
href
s and HTTP 404 errors.
User-specific Blog and Builder views must be reachable from RESTful URLs.
For example, given a potential URL scheme of
{app-url}/({blog-name}|auth|builder)/{?blog-page}
, a user with a blog
named "cool-blogio" could have their
Blog view reachable at http://localhost:3000/cool-blogio
(root page) and their Builder view at
http://localhost:3000/builder/cool-blogio. If their blog has "home"
(root page), "about," and "contact"
pages, those would be reachable at
http://localhost:3000/cool-blogio/home, http://localhost:3000/cool-blogio/about,
and http://localhost:3000/cool-blogio/contact respectively. Further, the Auth
view might be available at http://localhost:3000/auth and the Home view at
http://localhost:3000.
With this potential URL scheme, no user can create a blog named "auth" or "builder".
You can come up with any RESTful URL scheme you want. You do not have to use the example scheme described above. Though remember that the input limits you place on your solution's frontend (like not allowing certain blog names) might not exist in other teams' solutions.
Blog view must asynchronously update number of views and number of active viewers (sessions). Builder and Admin views must asynchronously report this information.
When the Blog view loads a specific blog's page, your app must do two things:
- Increment the (monotonic) total number of views associated with that blog's page
- Add or renew an "active session" entry associated with that user
Active sessions represent users (guest
or otherwise) that are currently
interacting with a blog's page. When registering a new active session, you'll
receive a
session_id
that you can use to
renew
that session every so often or
manually expire (delete)
it.
Clients should regularly renew active sessions for as long as the user is viewing a blog's page. Once the client navigates away from the blog page, the session should no longer be renewed.
As for the Admin and Builder views, they must display statistics about the total number of views and total number of active (non-expired) users of each of the current blog's pages. The updated totals must eventually appear within these two views without the page refreshing or the user doing anything extra, like pressing a refresh button.
This type of automatic updating/revalidating of data is called asynchronous or "ajaxian" since it occurs outside the usual synchronous event flow. There are many solutions, including interval revalidation, focus revalidation, and visibility-based revalidation (i.e. updating data only for elements that are currently visible). Another solution is to use frontend timers to regularly check a source for new data every now and then.
The app will be performant.
The amount of time the application takes to load and sort data, display information, and act on input is progressively penalized. That is: the teams with the fastest load times will earn the most points and the teams with the slowest load times will earn zero points from this requirement.
Average (median) is used to calculate load times. Measurements will include initial page load times and, depending on the other requirements, various other frontend UI response times.
Tail latencies (e.g. on startup with a cold cache) are ignored. Only averages are considered.
FOUC may also be penalized.
To maximize performance, consider caching (see also) the result of data processing operations and using range queries to retrieve only the data your app hasn't yet processed.
Results and lists of items displayed in the frontend UI will be paginated where appropriate.
Pagination is the strategy of showing a limited number of a large set of results and providing a navigation element where users can switch to different "pages" of that large set. A Google search result (which has multiple pages) is a good example of pagination. Infinite scroll, a specific pagination implementation used by the likes of Facebook/Instagram and Twitter, is another good example.
Security: no XSS, SQLI, insecure database, or other trivial security vulnerabilities.
The app will use modern software engineering practices that protect from common XSS, SQL injection, and other security vulnerabilities. Specifically: form inputs and the like will not be vulnerable to SQL injection attacks. User-generated outputs will not be vulnerable to XSS or similar attacks.
As for database security, any passwords present in the database must be hashed (not encrypted). We recommend using a salted SHA-256 hash construction or something similar. You don't need to do anything fancy. There are many tutorials for how to safely store passwords and other credentials in a database.
Passwords stored in your database in cleartext, hashed incorrectly, or re-encoded (e.g. with base64) will earn your team zero points for this requirement.
The app will fail gracefully when exceptional conditions are encountered.
This includes handling API errors during fetch, login errors, random exceptions, showing spinners when content needs to load, etc.
Every so often, the API will respond with an
HTTP 555
error instead of fulfilling a request. Further, API requests and responses will be manipulated by the judges in an attempt to break the app. If at any time a user is presented with a non-app error page or a completely blank screen for more than a second or so, your solution may lose points on this requirement.
The frontend UI will be responsive to mobile, tablet, and desktop viewports.
The app will be pleasant to the eye when viewed on a smartphone, tablet, and a desktop viewport. The design and functionality will not "break" across these viewports nor will the app become non-functional.
Judges will view and interact with the app through emulated phone and tablet viewports. If the app breaks when viewed, it will lose points on this and other requirements. We recommend using mobile-first software design principles.