db, starting cms pages
This commit is contained in:
parent
71e7b0f54f
commit
d4f976cd43
1
.env.example
Normal file
1
.env.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
SESSION_SECRET=
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@
|
|||||||
yarn-error.log
|
yarn-error.log
|
||||||
/cypress/screenshots/
|
/cypress/screenshots/
|
||||||
/__sapper__/
|
/__sapper__/
|
||||||
|
.env
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
# HowFeed.biz
|
# HowFeed.biz
|
||||||
|
|
||||||
A satirical blog and lightweight CMS that runs on [Sapper](https://sapper.svelte.dev).
|
A satirical blog with its own lightweight CMS, which all runs on [Sapper](https://sapper.svelte.dev).
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
Requires Node.js
|
Requires Node.js and SQLite
|
||||||
|
|
||||||
|
Set up `.env.example` as `.env`
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
sqlite3 storage/howfeed.db < storage/init.sql
|
||||||
npm i
|
npm i
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
857
package-lock.json
generated
857
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "HowFeed.biz",
|
"name": "howfeed",
|
||||||
"description": "HowFeed.biz",
|
"description": "HowFeed.biz",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -12,9 +12,18 @@
|
|||||||
"test": "run-p --race dev cy:run"
|
"test": "run-p --race dev cy:run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bcrypt": "^4.0.1",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
"compression": "^1.7.1",
|
"compression": "^1.7.1",
|
||||||
"polka": "next",
|
"connect-flash": "^0.1.1",
|
||||||
"sirv": "^0.4.0"
|
"cookie-parser": "^1.4.5",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"express-session": "^1.17.1",
|
||||||
|
"passport": "^0.4.1",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
|
"sirv": "^0.4.0",
|
||||||
|
"sqlite3": "^4.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
@ -22,5 +31,9 @@
|
|||||||
"svelte": "^3.0.0",
|
"svelte": "^3.0.0",
|
||||||
"svelte-loader": "^2.9.0",
|
"svelte-loader": "^2.9.0",
|
||||||
"webpack": "^4.7.0"
|
"webpack": "^4.7.0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git@github.com:scoliono/howfeed.git"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
<script>
|
|
||||||
export let segment;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
nav {
|
nav {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -11,6 +7,7 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
box-shadow: 0 -2px 5px #000;
|
||||||
}
|
}
|
||||||
div.items {
|
div.items {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 1em auto;
|
margin: 1em auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
h1 {
|
h1 {
|
||||||
@ -28,13 +28,13 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{status}</title>
|
<title>Error {status} | HOWFEED.BIZ</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<h1>{status}</h1>
|
<div class="content">
|
||||||
|
<h1>Error {status}</h1>
|
||||||
<p>{error.message}</p>
|
<p>{error.message}</p>
|
||||||
|
{#if dev && error.stack}
|
||||||
{#if dev && error.stack}
|
<pre>{error.stack}</pre>
|
||||||
<pre>{error.stack}</pre>
|
{/if}
|
||||||
{/if}
|
</div>
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import Nav from '../components/Nav.svelte';
|
import Nav from '../components/Nav.svelte';
|
||||||
|
|
||||||
export let segment;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
main {
|
main {
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
background: #fff;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
footer {
|
footer {
|
||||||
@ -17,12 +14,13 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
box-shadow: 0 2px 5px #000;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<Nav {segment}/>
|
<Nav />
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
@ -52,11 +52,10 @@
|
|||||||
margin: 0 0 0.5em 0;
|
margin: 0 0 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
@media (min-width: 640px) {
|
||||||
position: absolute;
|
.content {
|
||||||
background: #fff;
|
width: 75vw;
|
||||||
margin: 8rem;
|
}
|
||||||
padding: 2rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
figure.article-image {
|
figure.article-image {
|
||||||
|
33
src/routes/cms/login.svelte
Normal file
33
src/routes/cms/login.svelte
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<script>
|
||||||
|
let username, password;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
input {
|
||||||
|
display: block;
|
||||||
|
padding: 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
width: 70%;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
button[type=submit] {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
max-width: 40rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Login | HOWFEED.BIZ</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<h1>Login</h1>
|
||||||
|
<form method="POST" action="/cms/login">
|
||||||
|
<input required type="text" name="username" bind:value={username} placeholder="Username">
|
||||||
|
<input required type="password" name="password" bind:value={password} placeholder="Password">
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
34
src/routes/cms/register.svelte
Normal file
34
src/routes/cms/register.svelte
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script>
|
||||||
|
let username, password, password_confirm;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
input {
|
||||||
|
display: block;
|
||||||
|
padding: 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
width: 70%;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
button[type=submit] {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
div.content {
|
||||||
|
max-width: 40rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Register | HOWFEED.BIZ</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<h1>Register</h1>
|
||||||
|
<form method="POST" action="/cms/register">
|
||||||
|
<input required type="text" name="username" bind:value={username} placeholder="Username">
|
||||||
|
<input required type="password" name="password" bind:value={password} placeholder="Password">
|
||||||
|
<input required type="password" name="password_confirm" bind:value={password_confirm} placeholder="Confirm Password">
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
@ -30,7 +30,7 @@
|
|||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
}
|
}
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
div.content {
|
div.homepage {
|
||||||
padding-top: 5rem !important;
|
padding-top: 5rem !important;
|
||||||
}
|
}
|
||||||
h1.welcome {
|
h1.welcome {
|
||||||
@ -57,8 +57,9 @@
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
filter: blur(5px);
|
filter: blur(5px);
|
||||||
}
|
}
|
||||||
div.content {
|
div.homepage {
|
||||||
padding-top: 8rem;
|
padding-top: 8rem;
|
||||||
|
padding-bottom: 4rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@ -85,7 +86,7 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="background"></div>
|
<div class="background"></div>
|
||||||
<div class="content">
|
<div class="homepage">
|
||||||
<h1 class="welcome">Welcome</h1>
|
<h1 class="welcome">Welcome</h1>
|
||||||
<h2 class="desc">Find an Article</h2>
|
<h2 class="desc">Find an Article</h2>
|
||||||
<div class="article-list">
|
<div class="article-list">
|
||||||
|
@ -1,17 +1,60 @@
|
|||||||
import sirv from 'sirv';
|
import sirv from 'sirv';
|
||||||
import polka from 'polka';
|
import express from 'express';
|
||||||
|
import session from 'express-session';
|
||||||
import compression from 'compression';
|
import compression from 'compression';
|
||||||
import * as sapper from '@sapper/server';
|
import * as sapper from '@sapper/server';
|
||||||
|
import sqlite3 from 'sqlite3';
|
||||||
|
import passport from 'passport';
|
||||||
|
import { Strategy } from 'passport-local';
|
||||||
|
import bcrypt from 'bcrypt';
|
||||||
|
|
||||||
const { PORT, NODE_ENV } = process.env;
|
require('dotenv').config();
|
||||||
|
|
||||||
|
const { PORT, NODE_ENV, SESSION_SECRET } = process.env;
|
||||||
const dev = NODE_ENV === 'development';
|
const dev = NODE_ENV === 'development';
|
||||||
|
|
||||||
polka() // You can also use Express
|
const db = new sqlite3.Database('./storage/howfeed.db', (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err.message);
|
||||||
|
} else {
|
||||||
|
console.log('Connected to the database.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
passport.use(new Strategy((username, password, done) => {
|
||||||
|
const sql = `SELECT password FROM users WHERE username = ?`;
|
||||||
|
db.get(sql, [username], async (err, row) => {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
if (!row) {
|
||||||
|
return done(null, false, { message: 'Incorrect username.' });
|
||||||
|
}
|
||||||
|
const validPass = await bcrypt.compare(password, row.password);
|
||||||
|
if (!validPass) {
|
||||||
|
return done(null, false, { message: 'Incorrect password.' });
|
||||||
|
}
|
||||||
|
return done(null, row);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
express()
|
||||||
.use(
|
.use(
|
||||||
|
session({
|
||||||
|
secret: SESSION_SECRET,
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: true,
|
||||||
|
cookie: { secure: true }
|
||||||
|
}),
|
||||||
compression({ threshold: 0 }),
|
compression({ threshold: 0 }),
|
||||||
sirv('static', { dev }),
|
sirv('static', { dev }),
|
||||||
sapper.middleware()
|
sapper.middleware()
|
||||||
)
|
)
|
||||||
|
.post('/cms/login', passport.authenticate('local', {
|
||||||
|
successRedirect: '/cms',
|
||||||
|
failureRedirect: '/cms/login',
|
||||||
|
failureFlash: true,
|
||||||
|
}))
|
||||||
.listen(PORT, err => {
|
.listen(PORT, err => {
|
||||||
if (err) console.log('error', err);
|
if (err) console.log('error', err);
|
||||||
});
|
});
|
||||||
|
@ -36,7 +36,10 @@ code {
|
|||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
body {
|
body {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
.content {
|
||||||
|
margin: 8rem auto !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.article-list {
|
div.article-list {
|
||||||
@ -77,3 +80,9 @@ div.article-meta {
|
|||||||
.article-date {
|
.article-date {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background: #fff;
|
||||||
|
margin: 8rem 0;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
1
storage/.gitignore
vendored
Normal file
1
storage/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.db
|
12
storage/init.sql
Normal file
12
storage/init.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
PRAGMA foreign_keys = ON;
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
username TEXT PRIMARY KEY,
|
||||||
|
password TEXT NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS articles (
|
||||||
|
title TEXT PRIMARY KEY,
|
||||||
|
created_at TEXT NOT NULL,
|
||||||
|
html BLOB NOT NULL,
|
||||||
|
author INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (author) REFERENCES users (username)
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user