Skip to content

Commit 1cf90b2

Browse files
committed
Merge branch 'feature'
2 parents f4481a5 + ac5e34e commit 1cf90b2

File tree

83 files changed

+2807
-378
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+2807
-378
lines changed

velog-backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"eslint-config-airbnb-base": "^12.1.0",
3030
"eslint-plugin-flowtype": "^2.39.1",
3131
"eslint-plugin-import": "^2.8.0",
32-
"flow-bin": "0.72.0",
32+
"flow-bin": "0.75",
3333
"onchange": "^3.3.0",
3434
"pg-hstore": "^2.3.2",
3535
"pg-native": "^2.2.0",

velog-backend/src/database/models/Series.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,32 @@ import Sequelize from 'sequelize';
22
import db from 'database/db';
33
import { primaryUUID } from 'lib/common';
44
import { User } from 'database/models';
5+
import pick from 'lodash/pick';
56

67
const Series = db.define(
78
'series',
89
{
910
id: primaryUUID,
1011
fk_user_id: Sequelize.UUID,
1112
name: Sequelize.STRING,
12-
description: Sequelize.STRING,
13+
description: Sequelize.TEXT,
14+
thumbnail: Sequelize.STRING,
15+
url_slug: Sequelize.STRING,
1316
},
1417
{
1518
indexes: [
1619
{
1720
fields: ['fk_user_id'],
1821
},
22+
{
23+
fields: ['created_at'],
24+
},
25+
{
26+
fields: ['updated_at'],
27+
},
28+
{
29+
fields: ['fk_user_id', 'url_slug'],
30+
},
1931
],
2032
},
2133
);
@@ -28,4 +40,22 @@ Series.associate = () => {
2840
});
2941
};
3042

43+
export const serializeSeries = data => ({
44+
...pick(data, [
45+
'id',
46+
'name',
47+
'description',
48+
'thumbnail',
49+
'url_slug',
50+
'created_at',
51+
'updated_at',
52+
]),
53+
user: {
54+
id: data.user.id,
55+
short_bio: data.user.user_profile.short_bio,
56+
username: data.user.username,
57+
thumbnail: data.user.user_profile.thumbnail,
58+
},
59+
});
60+
3161
export default Series;

velog-backend/src/database/models/SeriesPosts.js

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @flow
12
import Sequelize from 'sequelize';
23
import db from 'database/db';
34
import { primaryUUID } from 'lib/common';
@@ -26,20 +27,47 @@ const SeriesPosts = db.define(
2627
);
2728

2829
SeriesPosts.associate = () => {
29-
Post.belongsTo(Series, {
30+
SeriesPosts.belongsTo(Post, {
3031
onDelete: 'CASCADE',
31-
onUpdate: 'Restrict',
32-
through: {
33-
model: SeriesPosts,
34-
},
32+
onUpdate: 'restrict',
3533
foreignKey: 'fk_post_id',
3634
});
37-
Series.belongsTo(Post, {
35+
SeriesPosts.belongsTo(Series, {
3836
onDelete: 'CASCADE',
39-
onUpdate: 'Restrict',
40-
through: {
41-
model: SeriesPosts,
42-
},
37+
onUpdate: 'restrict',
4338
foreignKey: 'fk_series_id',
4439
});
4540
};
41+
42+
SeriesPosts.append = async (
43+
seriesId: string,
44+
postId: string,
45+
userId: string,
46+
) => {
47+
// list all series post
48+
const seriesPosts = await SeriesPosts.findAll({
49+
where: {
50+
fk_series_id: seriesId,
51+
},
52+
include: [Post],
53+
order: [['index', 'ASC']],
54+
});
55+
const nextIndex =
56+
seriesPosts.length === 0
57+
? 1
58+
: seriesPosts[seriesPosts.length - 1].index + 1;
59+
// check already added
60+
const exists = seriesPosts.find(sp => sp.fk_post_id === postId);
61+
if (exists) {
62+
return exists;
63+
}
64+
const sp = await SeriesPosts.build({
65+
fk_user_id: userId,
66+
index: nextIndex,
67+
fk_post_id: postId,
68+
fk_series_id: seriesId,
69+
}).save();
70+
return sp;
71+
};
72+
73+
export default SeriesPosts;

velog-backend/src/database/models/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@ export { default as UrlSlugHistory } from './UrlSlugHistory';
2121
export { default as EmailCert } from './EmailCert';
2222
export { default as UserMeta } from './UserMeta';
2323
export { default as UserImage } from './UserImage';
24+
export { default as Series } from './Series';
25+
export { default as SeriesPosts } from './SeriesPosts';
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @flow
2+
import Sequelize from 'sequelize';
3+
import db from 'database/db';
4+
5+
type SeriesPostCountRow = {
6+
id: string,
7+
count: number,
8+
};
9+
10+
export const getSeriesPostCountList = async (
11+
seriesIds: string[],
12+
): Promise<SeriesPostCountRow[]> => {
13+
const matches = `(${seriesIds.map(seriesId => `'${seriesId}'`).join(', ')})`;
14+
const query = `
15+
select series.id, COUNT(series_posts.fk_post_id) FROM series
16+
left join series_posts on series.id = series_posts.fk_series_id
17+
where series.id IN ${matches}
18+
group by series.id
19+
`;
20+
try {
21+
const rows = await db.query(query, {
22+
type: Sequelize.QueryTypes.SELECT,
23+
});
24+
return rows;
25+
} catch (e) {
26+
throw e;
27+
}
28+
};

velog-backend/src/database/sync.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import {
2222
EmailCert,
2323
UserMeta,
2424
UserImage,
25+
Series,
26+
SeriesPosts,
2527
} from './models';
2628
import * as views from './views';
2729

@@ -47,6 +49,8 @@ export function associate() {
4749
EmailCert.associate();
4850
UserMeta.associate();
4951
UserImage.associate();
52+
Series.associate();
53+
SeriesPosts.associate();
5054
}
5155
export default function sync() {
5256
associate();

velog-backend/src/lib/redisClient.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ class RedisClient {
1515
}
1616
connect() {
1717
const p = new Promise((resolve, reject) => {
18-
const { REDIS_HOST, REDIS_PASS } = process.env;
1918
const client = redis.createClient({
2019
host: process.env.REDIS_HOST || '',
2120
password: process.env.REDIS_PASS || '',

velog-backend/src/router/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import common from './common';
1313
import sitemaps from './sitemaps';
1414
import internal from './internal';
1515
import atom from './atom';
16-
import search from './search/search';
16+
import search from './search';
17+
import series from './series';
1718

1819
const router: Router = new Router();
1920

@@ -28,6 +29,7 @@ router.use('/sitemaps', sitemaps.routes());
2829
router.use('/internal', internal.routes());
2930
router.use('/atom', atom.routes());
3031
router.use('/search', search.routes());
32+
router.use('/series', series.routes());
3133

3234
router.get('/check', (ctx: Context) => {
3335
console.log('avoiding cold start...');

velog-backend/src/router/posts/post/post.ctrl.js

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
PostsTags,
1818
PostsCategories,
1919
Category,
20+
Series,
21+
SeriesPosts,
2022
} from 'database/models';
2123
import redisClient from 'lib/redisClient';
2224
import UrlSlugHistory from 'database/models/UrlSlugHistory';
@@ -72,6 +74,7 @@ export const updatePost = async (ctx: Context): Promise<*> => {
7274
is_temp: boolean,
7375
meta: any,
7476
is_private: boolean,
77+
series_id: ?string,
7578
};
7679

7780
const schema = Joi.object().keys({
@@ -92,6 +95,7 @@ export const updatePost = async (ctx: Context): Promise<*> => {
9295
.max(130),
9396
meta: Joi.object(),
9497
is_private: Joi.boolean(),
98+
series_id: Joi.string().allow(null),
9599
});
96100

97101
if (!validateSchema(ctx, schema)) {
@@ -108,6 +112,7 @@ export const updatePost = async (ctx: Context): Promise<*> => {
108112
is_temp: isTemp,
109113
meta,
110114
is_private,
115+
series_id,
111116
}: BodySchema = (ctx.request.body: any);
112117

113118
const stringsToCheck = [title, body, ...tags];
@@ -180,8 +185,8 @@ export const updatePost = async (ctx: Context): Promise<*> => {
180185
thumbnail,
181186
is_temp: isTemp,
182187
meta,
183-
is_private: (is_private || false),
184-
released_at: (!isTemp && ctx.post.is_temp) ? new Date() : undefined,
188+
is_private: is_private || false,
189+
released_at: !isTemp && ctx.post.is_temp ? new Date() : undefined,
185190
};
186191

187192
Object.keys(updateQuery).forEach((key) => {
@@ -190,6 +195,48 @@ export const updatePost = async (ctx: Context): Promise<*> => {
190195
}
191196
});
192197

198+
// Update Series
199+
let series = null;
200+
try {
201+
const seriesPost = await SeriesPosts.findOne({
202+
where: { fk_post_id: id },
203+
include: [Series],
204+
});
205+
// Check Series Validity
206+
if (series_id) {
207+
const nextSeries = await Series.findById(series_id);
208+
if (!nextSeries) {
209+
ctx.status = 404;
210+
ctx.body = {
211+
name: 'INVALID_SERIES',
212+
};
213+
}
214+
if (nextSeries.fk_user_id !== ctx.user.id) {
215+
ctx.status = 403;
216+
return;
217+
}
218+
series = {
219+
id: nextSeries.id,
220+
name: nextSeries.name,
221+
};
222+
}
223+
224+
if (seriesPost) {
225+
if (seriesPost.series.id !== series_id) {
226+
seriesPost.destroy();
227+
if (!series_id) {
228+
series = null;
229+
} else {
230+
await SeriesPosts.append(series_id, id, ctx.user.id);
231+
}
232+
}
233+
} else {
234+
await SeriesPosts.append(series_id, id, ctx.user.id);
235+
}
236+
} catch (e) {
237+
ctx.throw(500, e);
238+
}
239+
193240
// Update Tags
194241
if (tags) {
195242
// Check which tags to remove or add
@@ -254,7 +301,7 @@ export const updatePost = async (ctx: Context): Promise<*> => {
254301
await ctx.post.update(updateQuery);
255302
const post = await Post.readPostById(id);
256303
const serialized = serializePost(post);
257-
ctx.body = serialized;
304+
ctx.body = { ...serialized, series };
258305
} catch (e) {
259306
ctx.throw(500, e);
260307
}
@@ -264,9 +311,26 @@ export const updatePost = async (ctx: Context): Promise<*> => {
264311
export const readPost = async (ctx: Context): Promise<*> => {
265312
const { id } = ctx.params;
266313
try {
267-
const post = await Post.readPostById(id);
314+
const [post, seriesPost] = await Promise.all([
315+
Post.readPostById(id),
316+
SeriesPosts.findOne({
317+
include: [Series],
318+
where: {
319+
fk_post_id: id,
320+
},
321+
}),
322+
]);
323+
268324
const serialized = serializePost(post);
269-
ctx.body = serialized;
325+
ctx.body = {
326+
...serialized,
327+
series: seriesPost
328+
? {
329+
id: seriesPost.series.id,
330+
name: seriesPost.series.name,
331+
}
332+
: null,
333+
};
270334
} catch (e) {
271335
ctx.throw(500, e);
272336
}

0 commit comments

Comments
 (0)