deleting, view count, realname for articles
This commit is contained in:
parent
80b8a28c36
commit
c0214eef58
@ -22,21 +22,8 @@ const ArticleSchema = new Schema({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
ArticleSchema.virtual('author_user', {
|
|
||||||
ref: 'User',
|
|
||||||
localField: 'author',
|
|
||||||
foreignField: '_id',
|
|
||||||
justOne: true
|
|
||||||
});
|
|
||||||
|
|
||||||
ArticleSchema.methods.genSlug = title => title.toLowerCase().replace(/\W+/g, '-');
|
ArticleSchema.methods.genSlug = title => title.toLowerCase().replace(/\W+/g, '-');
|
||||||
|
|
||||||
ArticleSchema.pre('findOne', function (next) {
|
|
||||||
var article = this;
|
|
||||||
article.views++;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
ArticleSchema.pre('save', function (next) {
|
ArticleSchema.pre('save', function (next) {
|
||||||
var article = this;
|
var article = this;
|
||||||
// only gen the slug if title has been modified (or is new)
|
// only gen the slug if title has been modified (or is new)
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
<script context="module">
|
||||||
|
export async function preload({ user }, page)
|
||||||
|
{
|
||||||
|
const res = await this.fetch(`/me`);
|
||||||
|
user = await res.json();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Footer from '../components/Footer.svelte';
|
import Footer from '../components/Footer.svelte';
|
||||||
import Nav from '../components/Nav.svelte';
|
import Nav from '../components/Nav.svelte';
|
||||||
@ -5,20 +13,9 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
main {
|
main {
|
||||||
max-width: 100vw;
|
max-width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
footer {
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-align: center;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0 2px 5px #000;
|
|
||||||
padding: 1rem;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<Nav />
|
<Nav />
|
||||||
|
@ -4,9 +4,14 @@ export async function get(req, res, next) {
|
|||||||
// the `slug` parameter is available because
|
// the `slug` parameter is available because
|
||||||
// this file is called [slug].json.js
|
// this file is called [slug].json.js
|
||||||
const { slug } = req.params;
|
const { slug } = req.params;
|
||||||
const article = await Article.findOne({ slug }).populate('author_user');
|
const article = await Article.findOne({ slug }).populate({
|
||||||
|
path: 'author',
|
||||||
|
select: 'realname'
|
||||||
|
});
|
||||||
|
|
||||||
if (article) {
|
if (article) {
|
||||||
|
article.set({ views: article.views + 1 });
|
||||||
|
article.save();
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
});
|
});
|
||||||
@ -20,3 +25,20 @@ export async function get(req, res, next) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function del(req, res, next) {
|
||||||
|
const { slug } = req.params;
|
||||||
|
const article = await Article.findOneAndDelete({ slug });
|
||||||
|
|
||||||
|
if (article) {
|
||||||
|
res.writeHead(204);
|
||||||
|
res.end();
|
||||||
|
} else {
|
||||||
|
res.writeHead(404, {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
message: `Not found`
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -64,9 +64,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.article-meta {
|
div.article-meta {
|
||||||
margin: 0;
|
margin: 0 0 4rem 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.article-meta h1 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@ -79,6 +83,9 @@
|
|||||||
</figure>
|
</figure>
|
||||||
<div class="article-meta">
|
<div class="article-meta">
|
||||||
<h1 class="article-title">{article.title}</h1>
|
<h1 class="article-title">{article.title}</h1>
|
||||||
|
<p>Author: <strong>{article.author.realname}</strong></p>
|
||||||
|
<p>Published: <strong>{new Date(article.created_at).toLocaleString()}</strong></p>
|
||||||
|
<p>Views: <strong>{article.views}</strong></p>
|
||||||
</div>
|
</div>
|
||||||
{@html article.html}
|
{@html article.html}
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,12 +15,17 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { goto } from '@sapper/app';
|
||||||
export let articles;
|
export let articles;
|
||||||
|
|
||||||
function del(article)
|
async function del(article)
|
||||||
{
|
{
|
||||||
if (confirm(`Are you sure you want to delete "${article.title}"?`)) {
|
if (confirm(`Are you sure you want to delete "${article.title}"?`)) {
|
||||||
alert('ok');
|
await fetch(`/a/${article.slug}.json`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
const res = await fetch(`/a/all`);
|
||||||
|
articles = await res.json();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -31,7 +36,8 @@
|
|||||||
<ul>
|
<ul>
|
||||||
{#each articles as article}
|
{#each articles as article}
|
||||||
<li>
|
<li>
|
||||||
<strong>{article.title}</strong> by <strong>{article.author_user}</strong>
|
<strong>{article.title}</strong> by <strong>{article.author.realname}</strong>
|
||||||
|
({article.views} views)
|
||||||
<button on:click={() => { del(article) }}>Delete</button>
|
<button on:click={() => { del(article) }}>Delete</button>
|
||||||
</li>
|
</li>
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -212,7 +212,12 @@ express()
|
|||||||
}
|
}
|
||||||
const article = await new Article({ html, title, image, author: req.user._id });
|
const article = await new Article({ html, title, image, author: req.user._id });
|
||||||
await article.save();
|
await article.save();
|
||||||
res.redirect(`/a/${article.slug}`);
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
slug: article.slug
|
||||||
|
}));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.writeHead(500, {
|
res.writeHead(500, {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@ -225,7 +230,10 @@ express()
|
|||||||
)
|
)
|
||||||
|
|
||||||
.get('/a/all', async function (req, res, next) {
|
.get('/a/all', async function (req, res, next) {
|
||||||
let articles = await Article.find().populate('author_user');
|
let articles = await Article.find().sort({ created_at: 'desc' }).populate({
|
||||||
|
path: 'author',
|
||||||
|
select: 'realname'
|
||||||
|
});
|
||||||
articles.forEach(article => {
|
articles.forEach(article => {
|
||||||
article.slug = article.title.toLowerCase().replace(/\W+/g, '-');
|
article.slug = article.title.toLowerCase().replace(/\W+/g, '-');
|
||||||
article.html = article.html.replace(/^\t{3}/gm, '');
|
article.html = article.html.replace(/^\t{3}/gm, '');
|
||||||
@ -236,6 +244,22 @@ express()
|
|||||||
res.end(JSON.stringify(articles));
|
res.end(JSON.stringify(articles));
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.get('/me', function(req, res, next) {
|
||||||
|
if (req.user) {
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify(req.user));
|
||||||
|
} else {
|
||||||
|
res.writeHead(401, {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
message: `You are not logged in`
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
.use(compression({ threshold: 0 }))
|
.use(compression({ threshold: 0 }))
|
||||||
.use(sirv('static', { dev }))
|
.use(sirv('static', { dev }))
|
||||||
.use(sapper.middleware({
|
.use(sapper.middleware({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user