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
|
||||
/cypress/screenshots/
|
||||
/__sapper__/
|
||||
.env
|
||||
|
@ -1,12 +1,15 @@
|
||||
# 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
|
||||
|
||||
Requires Node.js
|
||||
Requires Node.js and SQLite
|
||||
|
||||
Set up `.env.example` as `.env`
|
||||
|
||||
```sh
|
||||
sqlite3 storage/howfeed.db < storage/init.sql
|
||||
npm i
|
||||
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",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
@ -12,9 +12,18 @@
|
||||
"test": "run-p --race dev cy:run"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^4.0.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"compression": "^1.7.1",
|
||||
"polka": "next",
|
||||
"sirv": "^0.4.0"
|
||||
"connect-flash": "^0.1.1",
|
||||
"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": {
|
||||
"npm-run-all": "^4.1.5",
|
||||
@ -22,5 +31,9 @@
|
||||
"svelte": "^3.0.0",
|
||||
"svelte-loader": "^2.9.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>
|
||||
nav {
|
||||
font-weight: bold;
|
||||
@ -11,6 +7,7 @@
|
||||
z-index: 100;
|
||||
background-color: #fff;
|
||||
top: 0;
|
||||
box-shadow: 0 -2px 5px #000;
|
||||
}
|
||||
div.items {
|
||||
margin: 0;
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
p {
|
||||
margin: 1em auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
h1 {
|
||||
@ -28,13 +28,13 @@
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<title>{status}</title>
|
||||
<title>Error {status} | HOWFEED.BIZ</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>{status}</h1>
|
||||
|
||||
<p>{error.message}</p>
|
||||
|
||||
{#if dev && error.stack}
|
||||
<pre>{error.stack}</pre>
|
||||
{/if}
|
||||
<div class="content">
|
||||
<h1>Error {status}</h1>
|
||||
<p>{error.message}</p>
|
||||
{#if dev && error.stack}
|
||||
<pre>{error.stack}</pre>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -1,13 +1,10 @@
|
||||
<script>
|
||||
import Nav from '../components/Nav.svelte';
|
||||
|
||||
export let segment;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
main {
|
||||
max-width: 100vw;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
footer {
|
||||
@ -17,12 +14,13 @@
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 5px #000;
|
||||
padding: 1rem;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<Nav {segment}/>
|
||||
<Nav />
|
||||
|
||||
<main>
|
||||
<slot></slot>
|
||||
|
@ -52,11 +52,10 @@
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
margin: 8rem;
|
||||
padding: 2rem;
|
||||
@media (min-width: 640px) {
|
||||
.content {
|
||||
width: 75vw;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@media (min-width: 640px) {
|
||||
div.content {
|
||||
div.homepage {
|
||||
padding-top: 5rem !important;
|
||||
}
|
||||
h1.welcome {
|
||||
@ -57,8 +57,9 @@
|
||||
z-index: 0;
|
||||
filter: blur(5px);
|
||||
}
|
||||
div.content {
|
||||
div.homepage {
|
||||
padding-top: 8rem;
|
||||
padding-bottom: 4rem;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
margin: 0 auto;
|
||||
@ -85,7 +86,7 @@
|
||||
</svelte:head>
|
||||
|
||||
<div class="background"></div>
|
||||
<div class="content">
|
||||
<div class="homepage">
|
||||
<h1 class="welcome">Welcome</h1>
|
||||
<h2 class="desc">Find an Article</h2>
|
||||
<div class="article-list">
|
||||
|
@ -1,17 +1,60 @@
|
||||
import sirv from 'sirv';
|
||||
import polka from 'polka';
|
||||
import express from 'express';
|
||||
import session from 'express-session';
|
||||
import compression from 'compression';
|
||||
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';
|
||||
|
||||
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(
|
||||
session({
|
||||
secret: SESSION_SECRET,
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
cookie: { secure: true }
|
||||
}),
|
||||
compression({ threshold: 0 }),
|
||||
sirv('static', { dev }),
|
||||
sapper.middleware()
|
||||
)
|
||||
)
|
||||
.post('/cms/login', passport.authenticate('local', {
|
||||
successRedirect: '/cms',
|
||||
failureRedirect: '/cms/login',
|
||||
failureFlash: true,
|
||||
}))
|
||||
.listen(PORT, err => {
|
||||
if (err) console.log('error', err);
|
||||
});
|
||||
|
@ -36,7 +36,10 @@ code {
|
||||
@media (min-width: 640px) {
|
||||
body {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
margin: 8rem auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
div.article-list {
|
||||
@ -77,3 +80,9 @@ div.article-meta {
|
||||
.article-date {
|
||||
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