-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
93 changed files
with
5,517 additions
and
616 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,13 @@ | ||
# This env file should be checked into source control | ||
# This is the place for default values that should be used in all environments | ||
APP_URL= | ||
|
||
DATABASE_URL= | ||
|
||
# Mail drivers: SENDGRID, SMTP, PREVIEW | ||
MAIL_DRIVER=PREVIEW | ||
MAIL_HOST= | ||
MAIL_PORT= | ||
MAIL_USER= | ||
MAIL_PASS= | ||
MAIL_FROM_EMAIL= | ||
MAIL_FROM_NAME= | ||
MAIL_SENDGRID_KEY= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
[![Blitz.js](https://raw.githubusercontent.com/blitz-js/art/master/github-cover-photo.png)](https://blitzjs.com) | ||
# **Blitz.js Jobs** | ||
|
||
This is a [Blitz.js](https://github.com/blitz-js/blitz) app. | ||
|
||
# **name** | ||
The first-ever [Blitz.js job board](https://blitz-jobs.com/). This project is non-commercial, meaning that anyone can post jobs for free. | ||
|
||
## Getting Started | ||
|
||
|
@@ -19,26 +17,38 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the | |
Ensure the `.env.local` file has required environment variables: | ||
|
||
``` | ||
APP_URL=http://localhost:3000 | ||
DATABASE_URL=postgresql://<YOUR_DB_USERNAME>@localhost:5432/jobboard | ||
MAIL_DRIVER=PREVIEW | ||
MAIL_FROM_EMAIL="[email protected]" | ||
MAIL_FROM_NAME="Blitz.js Jobs" | ||
``` | ||
|
||
Ensure the `.env.test.local` file has required environment variables: | ||
|
||
``` | ||
DATABASE_URL=postgresql://<YOUR_DB_USERNAME>@localhost:5432/jobboard_test | ||
``` | ||
The environment variable `MAIL_DRIVER` can be equal to one of these: | ||
|
||
## Tests | ||
- `SMTP` - Basic SMTP driver. Requires these environment variables to be set: | ||
- `MAIL_HOST` | ||
- `MAIL_PORT` | ||
- `MAIL_USER` | ||
- `MAIL_PASS` | ||
- `SENDGRID` - Sendgrid driver. Requires these environment variables to be set: | ||
- `MAIL_SENDGRID_KEY` | ||
- `PREVIEW` - Browser preview driver. | ||
|
||
Runs your tests using Jest. | ||
Usage example: | ||
|
||
``` | ||
blitz test | ||
or | ||
yarn test | ||
``` | ||
```js | ||
import { mail } from "app/mail/mail" | ||
|
||
Blitz comes with a test setup using [Jest](https://jestjs.io/) and [react-testing-library](https://testing-library.com/). | ||
mail.send({ | ||
subject: "Please confirm your email", | ||
to: "[email protected]", | ||
view: "auth/mail/user-confirmation", | ||
variables: { confirmationUrl: "https://confirmation.url/" }, | ||
}) | ||
``` | ||
|
||
## Commands | ||
|
||
|
@@ -58,113 +68,12 @@ Blitz comes with a powerful CLI that is designed to make development easy and fa | |
|
||
You can read more about it on the [CLI Overview](https://blitzjs.com/docs/cli-overview) documentation. | ||
|
||
## What's included? | ||
|
||
Here is the structure of your app. | ||
|
||
``` | ||
jobboard | ||
├── app | ||
│ |── auth | ||
│ │ ├── components | ||
│ │ │ └── LoginForm.tsx | ||
│ │ ├── mutations | ||
│ │ │ ├── login.ts | ||
│ │ │ ├── logout.ts | ||
│ │ │ └── signup.ts | ||
│ │ └── pages | ||
│ │ ├── login.tsx | ||
│ │ └── signup.tsx | ||
│ ├── auth-utils.ts | ||
│ ├── validations.ts | ||
│ ├── components | ||
│ │ ├── Form.tsx | ||
│ │ └── LabeledTextField.tsx | ||
│ ├── hooks | ||
│ │ └── useCurrentUser.ts | ||
│ ├── layouts | ||
│ │ └── Layout.tsx | ||
│ │── pages | ||
│ │ ├── _app.tsx | ||
│ │ ├── _document.tsx | ||
│ │ ├── 404.tsx | ||
│ │ ├── index.tsx | ||
│ │ └── index.test.tsx | ||
│ └── users | ||
│ │ └── queries | ||
│ │ └── getCurrentUser.ts | ||
├── db | ||
│ ├── migrations | ||
│ ├── index.ts | ||
│ └── schema.prisma | ||
├── integrations | ||
├── node_modules | ||
├── public | ||
│ ├── favicon.ico | ||
│ └── logo.png | ||
├── test | ||
│ ├── __mocks__ | ||
│ │ └── fileMock.js | ||
│ ├── setup.ts | ||
│ └── utils.tsx | ||
├── utils | ||
├── .env | ||
├── .eslintrc.js | ||
├── .gitignore | ||
├── .npmrc | ||
├── .prettierignore | ||
├── babel.config.js | ||
├── blitz.config.js | ||
├── jest.config.js | ||
├── package.json | ||
├── README.md | ||
├── tsconfig.json | ||
└── yarn.lock | ||
``` | ||
|
||
These files are: | ||
|
||
- The `app/` directory is a container for most of your project. This is where you’ll put any pages or API routes. | ||
|
||
- `db`/ is where your database configuration goes. If you’re writing models or checking migrations, this is where to go. | ||
|
||
- `node_modules/` is where your “dependencies” are stored. This directory is updated by your package manager, so you don’t have to worry too much about it. | ||
|
||
- `public/` is a directory where you will put any static assets. If you have images, files, or videos which you want to use in your app, this is where to put them. | ||
|
||
- `test/` is a directory where you can put your unit and integration tests. | ||
|
||
- `utils/` is a good place to put any shared utility files which you might use across different sections of your app. | ||
|
||
- `.babelrc.js`, `.env`, etc. ("dotfiles") are configuration files for various bits of JavaScript tooling. | ||
|
||
- `blitz.config.js` is for advanced custom configuration of Blitz. It extends [`next.config.js`](https://nextjs.org/docs/api-reference/next.config.js/introduction). | ||
|
||
- `jest.config.js` contains config for Jest tests. You can [customize it if needed](https://jestjs.io/docs/en/configuration). | ||
|
||
- `package.json` contains information about your dependencies and devDependencies. If you’re using a tool like `npm` or `yarn`, you won’t have to worry about this much. | ||
|
||
- `tsconfig.json` is our recommended setup for TypeScript. | ||
|
||
You can read more about it in the [File Structure](https://blitzjs.com/docs/file-structure) section of the documentation. | ||
|
||
## Learn more | ||
|
||
Read the [Blitz.js Documentation](https://blitzjs.com/docs/getting-started) to learn more. | ||
|
||
### The Blitz.js Manifesto | ||
|
||
Read the [Blitz Manifesto](https://blitzjs.com/docs/manifesto) to learn the Blitz foundational principles. | ||
|
||
Blitz is built on Next.js. For more info on this see [Why use Blitz instead of Next.js](https://blitzjs.com/docs/why-blitz) | ||
## To do | ||
|
||
## Get in touch | ||
Contributions and suggestions of any kind are very welcome. | ||
|
||
The Blitz community is warm, safe, diverse, inclusive, and fun! Feel free to reach out to us in any of our communication channels. | ||
Here is a list of things that need to be developed: | ||
|
||
- [Website](https://blitzjs.com/) | ||
- [Slack](https://slack.blitzjs.com/) | ||
- [Report an issue](https://github.com/blitz-js/blitz/issues/new/choose) | ||
- [Forum discussions](https://github.com/blitz-js/blitz/discussions) | ||
- [Sponsors and donations](https://github.com/blitz-js/blitz#sponsors-and-donations) | ||
- [Contributing Guide](https://blitzjs.com/docs/contributing) | ||
- [ ] Forgot password system | ||
- [ ] Tests | ||
- [ ] Captcha (login, signup, post job) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Role } from "app/users/role" | ||
import { IdInput, IdInputType } from "app/validations" | ||
import { SessionContext } from "blitz" | ||
import db from "db" | ||
|
||
export default async function publishJob( | ||
input: IdInputType, | ||
ctx: { session?: SessionContext } = {} | ||
) { | ||
ctx.session!.authorize(Role.ADMIN) | ||
|
||
const { id } = IdInput.parse(input) | ||
|
||
await db.job.update({ | ||
where: { | ||
id, | ||
}, | ||
data: { | ||
publishedAt: new Date(), | ||
}, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import React, { Suspense } from "react" | ||
import { BlitzPage, GetServerSideProps } from "blitz" | ||
import { AppLayout } from "app/layouts/AppLayout" | ||
import { JobsList } from "app/jobs/components/JobsList" | ||
import { Loading } from "app/components/Loading" | ||
import { ensureHasRole } from "app/guards/ensureHasRole" | ||
import { Role } from "app/users/role" | ||
import getUnpublishedJobs from "app/admin/queries/getUnpublishedJobs" | ||
|
||
export const getServerSideProps: GetServerSideProps = async (context) => | ||
await ensureHasRole({ ...context, role: Role.ADMIN }) | ||
|
||
const Admin: BlitzPage = () => { | ||
return ( | ||
<React.Fragment> | ||
<div> | ||
<h3 className="text-lg font-medium leading-6 text-gray-900">Manage All Jobs</h3> | ||
<p className="mt-1 text-sm leading-5 text-gray-500"> | ||
Here you can publish all the submitted jobs. | ||
</p> | ||
</div> | ||
<Suspense | ||
fallback={ | ||
<div className="flex items-end justify-center h-12"> | ||
<Loading className="w-5 h-5 text-indigo-600" /> | ||
</div> | ||
} | ||
> | ||
<JobsList query={getUnpublishedJobs} withAdminActions /> | ||
</Suspense> | ||
</React.Fragment> | ||
) | ||
} | ||
|
||
Admin.getLayout = (page) => <AppLayout title="Dashboard">{page}</AppLayout> | ||
|
||
export default Admin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { PaginationInput, PaginationInputType } from "app/validations" | ||
import db from "db" | ||
|
||
export default async function getUnpublishedJobs(input: PaginationInputType) { | ||
const { skip, take } = PaginationInput.parse(input) | ||
|
||
const jobs = await db.job.findMany({ | ||
skip, | ||
take, | ||
where: { | ||
publishedAt: null, | ||
}, | ||
}) | ||
|
||
const count = await db.job.count() | ||
|
||
const hasMore = typeof take === "number" ? skip + take < count : false | ||
|
||
const nextPage = hasMore ? { take, skip: skip + take! } : null | ||
|
||
return { | ||
jobs, | ||
nextPage, | ||
hasMore, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,62 @@ | ||
import { AuthenticationError } from "blitz" | ||
import SecurePassword from "secure-password" | ||
import db from "db" | ||
import db, { User } from "db" | ||
import { EmailUsedError } from "app/errors/emailUsed" | ||
import { UnconfirmedEmailError } from "app/errors/unconfirmedEmail" | ||
|
||
const SP = new SecurePassword() | ||
|
||
export const hashPassword = async (password: string) => { | ||
const hashedBuffer = await SP.hash(Buffer.from(password)) | ||
|
||
return hashedBuffer.toString("base64") | ||
} | ||
|
||
export const verifyPassword = async (hashedPassword: string, password: string) => { | ||
try { | ||
return await SP.verify(Buffer.from(password), Buffer.from(hashedPassword, "base64")) | ||
} catch (error) { | ||
console.error(error) | ||
|
||
return false | ||
} | ||
} | ||
|
||
export const ensureUserConfirmed = (user: Pick<User, "confirmedAt">) => { | ||
if (!user.confirmedAt) { | ||
throw new UnconfirmedEmailError() | ||
} | ||
} | ||
|
||
export const ensureUserEmailNotUsed = async (email: string) => { | ||
const user = await db.user.findOne({ where: { email } }) | ||
|
||
if (!!user) { | ||
throw new EmailUsedError({ email }) | ||
} | ||
} | ||
|
||
export const authenticateUser = async (email: string, password: string) => { | ||
const user = await db.user.findOne({ where: { email } }) | ||
|
||
if (!user || !user.hashedPassword) throw new AuthenticationError() | ||
if (!user || !user.hashedPassword) { | ||
throw new AuthenticationError() | ||
} | ||
|
||
switch (await verifyPassword(user.hashedPassword, password)) { | ||
case SecurePassword.VALID: | ||
break | ||
case SecurePassword.VALID_NEEDS_REHASH: | ||
// Upgrade hashed password with a more secure hash | ||
const improvedHash = await hashPassword(password) | ||
|
||
await db.user.update({ where: { id: user.id }, data: { hashedPassword: improvedHash } }) | ||
|
||
break | ||
default: | ||
throw new AuthenticationError() | ||
} | ||
|
||
const { hashedPassword, ...rest } = user | ||
|
||
return rest | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React, { PropsWithoutRef } from "react" | ||
|
||
export const AuthHeading = React.forwardRef< | ||
HTMLHeadingElement, | ||
PropsWithoutRef<JSX.IntrinsicElements["h2"]> | ||
>(({ children, ...props }, ref) => ( | ||
<h2 | ||
ref={ref} | ||
className="mt-6 text-3xl font-extrabold leading-9 text-center text-gray-900" | ||
{...props} | ||
> | ||
{children} | ||
</h2> | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import React, { PropsWithoutRef } from "react" | ||
|
||
export const AuthSubheading = React.forwardRef< | ||
HTMLHeadingElement, | ||
PropsWithoutRef<JSX.IntrinsicElements["p"]> | ||
>(({ children, ...props }, ref) => ( | ||
<p ref={ref} className="mt-2 text-sm leading-5 text-center text-gray-600" {...props}> | ||
{children} | ||
</p> | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React, { PropsWithoutRef } from "react" | ||
|
||
export const AuthWrapper = React.forwardRef< | ||
HTMLDivElement, | ||
PropsWithoutRef<JSX.IntrinsicElements["h2"]> | ||
>(({ children, ...props }, ref) => ( | ||
<div | ||
ref={ref} | ||
className="flex flex-col justify-center min-h-screen py-12 bg-gray-50 sm:px-6 lg:px-8" | ||
{...props} | ||
> | ||
{children} | ||
</div> | ||
)) |
Oops, something went wrong.