Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/database/postgres/news.ts
5614 views
1
/*
2
* This file is part of CoCalc: Copyright © 2023 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import {
7
Channel,
8
EVENT_CHANNEL,
9
NewsItem,
10
NewsPrevNext,
11
RecentHeadline,
12
} from "@cocalc/util/types/news";
13
import { LRUQueryCache } from "./util";
14
15
const C = new LRUQueryCache({ ttl_s: 10 * 60 });
16
17
export function clearCache(): void {
18
C.clear();
19
}
20
21
// We exclude hidden and future news items and items from the events channel to keep user's news
22
// feed clear
23
const Q_FEED = `
24
SELECT
25
id, channel, title, text, url,
26
extract(epoch from date::timestamp)::integer as date,
27
extract(epoch from until::timestamp)::integer as until
28
FROM news
29
WHERE news.date <= NOW()
30
AND hide IS NOT TRUE
31
AND channel != '${EVENT_CHANNEL}'
32
AND (until IS NULL OR until > NOW())
33
ORDER BY date DESC
34
LIMIT 100`;
35
36
export async function getFeedData(): Promise<NewsItem[]> {
37
return await C.query(Q_FEED);
38
}
39
40
// ::timestamptz because if your server is not in UTC, it will be converted to UTC
41
// and the UTC epoch timestamp will be used in the browser client as the basis, adding your TZ offset
42
const Q_BY_ID = `
43
SELECT
44
id, channel, title, text, url, hide, tags,
45
extract(epoch from date::timestamptz)::INTEGER as date,
46
extract(epoch from until::timestamptz)::INTEGER as until
47
FROM news
48
WHERE id = $1`;
49
50
// This is used for editing a news item
51
export async function getNewsItem(
52
id: number,
53
cached = true,
54
): Promise<NewsItem | null> {
55
return await C.queryOne<NewsItem>(Q_BY_ID, [id], cached);
56
}
57
58
const Q_BY_ID_USER = `
59
SELECT
60
id, channel, title, text, url, hide, tags, history,
61
date >= NOW() as future,
62
until IS NOT NULL AND until <= NOW() as expired,
63
extract(epoch from date::timestamptz)::INTEGER as date,
64
extract(epoch from until::timestamptz)::INTEGER as until
65
FROM news
66
WHERE id = $1`;
67
68
const Q_NEXT = `
69
SELECT id, title
70
FROM news
71
WHERE date >= (SELECT date FROM news WHERE id = $1)
72
AND id != $1
73
AND hide IS NOT TRUE
74
AND date < NOW()
75
AND channel != '${EVENT_CHANNEL}'
76
AND (until IS NULL OR until > NOW())
77
ORDER BY date ASC, id ASC
78
LIMIT 1`;
79
80
const Q_PREV = `
81
SELECT id, title
82
FROM news
83
WHERE date <= (SELECT date FROM news WHERE id = $1)
84
AND id != $1
85
AND hide IS NOT TRUE
86
AND date < NOW()
87
AND channel != '${EVENT_CHANNEL}'
88
AND (until IS NULL OR until > NOW())
89
ORDER BY date DESC, id DESC
90
LIMIT 1`;
91
92
// This is used for displaying one news item (and next/prev ones) to a user
93
export async function getNewsItemUserPrevNext(id: number): Promise<{
94
news: NewsItem | null;
95
prev: NewsPrevNext | null;
96
next: NewsPrevNext | null;
97
}> {
98
const [news, prev, next] = await Promise.all([
99
C.queryOne<NewsItem>(Q_BY_ID_USER, [id]),
100
C.queryOne<NewsPrevNext>(Q_PREV, [id]),
101
C.queryOne<NewsPrevNext>(Q_NEXT, [id]),
102
]);
103
return { news, prev, next };
104
}
105
106
export async function getNewsItemUser(id: number): Promise<NewsItem | null> {
107
return await C.queryOne<NewsItem>(Q_BY_ID_USER, [id]);
108
}
109
110
const Q_INDEX = `
111
SELECT
112
id, channel, title, text, url, hide, tags,
113
date >= NOW() as future,
114
until IS NOT NULL AND until <= NOW() as expired,
115
extract(epoch from date::timestamptz)::INTEGER as date,
116
extract(epoch from until::timestamptz)::INTEGER as until
117
FROM news
118
WHERE channel <> '${EVENT_CHANNEL}'
119
ORDER BY date DESC
120
LIMIT $1
121
OFFSET $2`;
122
123
export async function getIndex(
124
limit: number,
125
offset: number,
126
): Promise<NewsItem[]> {
127
return await C.query(Q_INDEX, [limit, offset]);
128
}
129
130
// get the most recent news item (excluding events)
131
const Q_MOST_RECENT = `
132
SELECT
133
id, channel, title, tags,
134
extract(epoch from date::timestamptz)::INTEGER as date,
135
extract(epoch from until::timestamptz)::INTEGER as until
136
FROM news
137
WHERE date <= NOW()
138
AND hide IS NOT TRUE
139
AND channel != '${EVENT_CHANNEL}'
140
AND (until IS NULL OR until > NOW())
141
ORDER BY date DESC
142
LIMIT 1`;
143
144
export async function getMostRecentNews(): Promise<RecentHeadline | null> {
145
return await C.queryOne<RecentHeadline>(Q_MOST_RECENT);
146
}
147
148
const Q_RECENT = `
149
SELECT
150
id, channel, title, tags,
151
extract(epoch from date::timestamptz)::INTEGER as date,
152
extract(epoch from until::timestamptz)::INTEGER as until
153
FROM news
154
WHERE date <= NOW()
155
AND channel != '${EVENT_CHANNEL}'
156
AND hide IS NOT TRUE
157
AND (until IS NULL OR until > NOW())
158
ORDER BY date DESC
159
LIMIT $1`;
160
161
// of the last n picked by Q_RECENT, select one deterministically different every 10 minutes
162
export async function getRecentHeadlines(
163
n: number,
164
): Promise<RecentHeadline[] | null> {
165
const headlines = await C.query(Q_RECENT, [n]);
166
if (headlines.length === 0) return null;
167
return headlines;
168
}
169
170
// Query upcoming events from a particular channel
171
const Q_UPCOMING_NEWS_CHANNEL_ITEMS = `
172
SELECT
173
id, channel, title, text, url, tags,
174
extract(epoch from date::timestamp)::integer as date,
175
extract(epoch from until::timestamp)::integer as until
176
FROM news
177
WHERE date >= NOW()
178
AND channel = $1
179
AND hide IS NOT TRUE
180
AND (until IS NULL OR until > NOW())
181
ORDER BY date
182
LIMIT 100`;
183
184
export async function getUpcomingNewsChannelItems(
185
channel: Channel,
186
): Promise<NewsItem[]> {
187
return await C.query(Q_UPCOMING_NEWS_CHANNEL_ITEMS, [channel]);
188
}
189
190
// Query past events from a particular channel
191
const Q_PAST_NEWS_CHANNEL_ITEMS = `
192
SELECT
193
id, channel, title, text, url, tags,
194
extract(epoch from date::timestamp)::integer as date,
195
extract(epoch from until::timestamp)::integer as until
196
FROM news
197
WHERE date <= NOW()
198
AND channel = $1
199
AND hide IS NOT TRUE
200
AND (until IS NULL OR until > NOW())
201
ORDER BY date DESC
202
LIMIT 100`;
203
204
export async function getPastNewsChannelItems(
205
channel: Channel,
206
): Promise<NewsItem[]> {
207
return await C.query(Q_PAST_NEWS_CHANNEL_ITEMS, [channel]);
208
}
209
210