mirror of
https://git.sr.ht/~cadence/bibliogram
synced 2025-12-14 10:35:07 +00:00
Add request quota system
This commit is contained in:
@@ -57,7 +57,7 @@ module.exports = [
|
||||
route: `/u/(${constants.external.username_regex})/(rss|atom)\\.xml`, methods: ["GET"], code: ({fill}) => {
|
||||
const kind = fill[1]
|
||||
if (constants.feeds.enabled) {
|
||||
return fetchUser(fill[0], constants.symbols.fetch_context.RSS).then(async user => {
|
||||
return fetchUser(fill[0], constants.symbols.fetch_context.RSS).then(async ({user}) => {
|
||||
const feed = await user.timeline.fetchFeed()
|
||||
if (constants.feeds.feed_message.enabled) {
|
||||
addAnnouncementFeedItem(feed)
|
||||
|
||||
@@ -6,6 +6,7 @@ const {render, redirect, getStaticURL} = require("pinski/plugins")
|
||||
const {pugCache} = require("../passthrough")
|
||||
const {getSettings} = require("./utils/getsettings")
|
||||
const {getSettingsReferrer} = require("./utils/settingsreferrer")
|
||||
const quota = require("../../lib/quota")
|
||||
|
||||
/** @param {import("../../lib/structures/TimelineEntry")} post */
|
||||
function getPageTitle(post) {
|
||||
@@ -66,21 +67,38 @@ module.exports = [
|
||||
}
|
||||
},
|
||||
{
|
||||
route: `/u/(${constants.external.username_regex})(/channel)?`, methods: ["GET"], code: ({req, url, fill}) => {
|
||||
route: `/u/(${constants.external.username_regex})(/channel)?`, methods: ["GET"], code: async ({req, url, fill}) => {
|
||||
const username = fill[0]
|
||||
const type = fill[1] ? "igtv" : "timeline"
|
||||
|
||||
if (username !== username.toLowerCase()) { // some capital letters
|
||||
return Promise.resolve(redirect(`/u/${username.toLowerCase()}`, 301))
|
||||
return redirect(`/u/${username.toLowerCase()}`, 301)
|
||||
}
|
||||
|
||||
const settings = getSettings(req)
|
||||
const params = url.searchParams
|
||||
return fetchUser(username).then(async user => {
|
||||
|
||||
try {
|
||||
if (quota.remaining(req) === 0) {
|
||||
throw constants.symbols.QUOTA_REACHED
|
||||
}
|
||||
|
||||
const {user, quotaUsed} = await fetchUser(username)
|
||||
let remaining = quota.add(req, quotaUsed)
|
||||
|
||||
const selectedTimeline = user[type]
|
||||
let pageNumber = +params.get("page")
|
||||
if (isNaN(pageNumber) || pageNumber < 1) pageNumber = 1
|
||||
await selectedTimeline.fetchUpToPage(pageNumber - 1)
|
||||
const pageIndex = pageNumber - 1
|
||||
|
||||
const pagesNeeded = pageNumber - selectedTimeline.pages.length
|
||||
if (pagesNeeded > remaining) {
|
||||
throw constants.symbols.QUOTA_REACHED
|
||||
}
|
||||
|
||||
const quotaUsed2 = await selectedTimeline.fetchUpToPage(pageIndex)
|
||||
remaining = quota.add(req, quotaUsed2)
|
||||
|
||||
const followerCountsAvailable = !(user.constructor.name === "ReelUser" && user.following === 0 && user.followedBy === 0)
|
||||
return render(200, "pug/user.pug", {
|
||||
url,
|
||||
@@ -90,9 +108,10 @@ module.exports = [
|
||||
followerCountsAvailable,
|
||||
constants,
|
||||
settings,
|
||||
settingsReferrer: getSettingsReferrer(req.url)
|
||||
settingsReferrer: getSettingsReferrer(req.url),
|
||||
remaining
|
||||
})
|
||||
}).catch(error => {
|
||||
} catch (error) {
|
||||
if (error === constants.symbols.NOT_FOUND || error === constants.symbols.ENDPOINT_OVERRIDDEN) {
|
||||
return render(404, "pug/friendlyerror.pug", {
|
||||
statusCode: 404,
|
||||
@@ -119,10 +138,21 @@ module.exports = [
|
||||
}
|
||||
} else if (error === constants.symbols.extractor_results.AGE_RESTRICTED) {
|
||||
return render(403, "pug/age_gated.pug", {settings})
|
||||
} else if (error === constants.symbols.QUOTA_REACHED) {
|
||||
return render(429, "pug/friendlyerror.pug", {
|
||||
title: "Quota reached",
|
||||
statusCode: 429,
|
||||
message: "Quota reached",
|
||||
explanation:
|
||||
"Each person has a limited number of requests to Bibliogram."
|
||||
+"\nYou have reached that limit."
|
||||
+"\nWait a while to for your counter to reset.\n",
|
||||
withInstancesLink: true
|
||||
})
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -139,11 +169,27 @@ module.exports = [
|
||||
let type = url.searchParams.get("type")
|
||||
if (!["timeline", "igtv"].includes(type)) type = "timeline"
|
||||
|
||||
const settings = getSettings(req)
|
||||
return fetchUser(username).then(async user => {
|
||||
try {
|
||||
if (quota.remaining(req) === 0) {
|
||||
throw constants.symbols.QUOTA_REACHED
|
||||
}
|
||||
|
||||
const settings = getSettings(req)
|
||||
|
||||
const {user, quotaUsed} = await fetchUser(username)
|
||||
const remaining = quota.add(req, quotaUsed)
|
||||
|
||||
const pageIndex = pageNumber - 1
|
||||
const selectedTimeline = user[type]
|
||||
await selectedTimeline.fetchUpToPage(pageIndex)
|
||||
|
||||
const pagesNeeded = pageNumber - selectedTimeline.pages.length
|
||||
if (pagesNeeded > remaining) {
|
||||
throw constants.symbols.QUOTA_REACHED
|
||||
}
|
||||
|
||||
const quotaUsed2 = await selectedTimeline.fetchUpToPage(pageIndex)
|
||||
quota.add(req, quotaUsed2)
|
||||
|
||||
if (selectedTimeline.pages[pageIndex]) {
|
||||
return render(200, "pug/fragments/timeline_page.pug", {page: selectedTimeline.pages[pageIndex], selectedTimeline, type, pageIndex, user, url, settings})
|
||||
} else {
|
||||
@@ -153,7 +199,7 @@ module.exports = [
|
||||
content: "That page does not exist."
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
} catch (error) {
|
||||
if (error === constants.symbols.NOT_FOUND || error === constants.symbols.ENDPOINT_OVERRIDDEN) {
|
||||
return render(404, "pug/friendlyerror.pug", {
|
||||
statusCode: 404,
|
||||
@@ -163,10 +209,12 @@ module.exports = [
|
||||
})
|
||||
} else if (error === constants.symbols.INSTAGRAM_DEMANDS_LOGIN || error === constants.symbols.RATE_LIMITED) {
|
||||
return render(503, "pug/fragments/timeline_loading_blocked.pug")
|
||||
} else if (error === constants.symbols.QUOTA_REACHED) {
|
||||
return render(429, "pug/fragments/timeline_quota_reached.pug")
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -65,7 +65,7 @@ module.exports = [
|
||||
delete saved.updated_version
|
||||
return Promise.resolve(replyWithUserData(saved))
|
||||
} else {
|
||||
return collectors.fetchUser(username, constants.symbols.fetch_context.ASSISTANT).then(user => {
|
||||
return collectors.fetchUser(username, constants.symbols.fetch_context.ASSISTANT).then(({user}) => {
|
||||
return replyWithUserData({
|
||||
username: user.data.username,
|
||||
user_id: user.data.id,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {ElemJS, q} from "./elemjs/elemjs.js"
|
||||
import {quota} from "./quota.js"
|
||||
|
||||
class FreezeWidth extends ElemJS {
|
||||
freeze(text) {
|
||||
@@ -79,6 +80,7 @@ class NextPage extends FreezeWidth {
|
||||
const type = this.element.getAttribute("data-type")
|
||||
|
||||
return fetch(`/fragment/user/${this.element.getAttribute("data-username")}/${this.nextPageNumber}?type=${type}`).then(res => res.text()).then(text => {
|
||||
quota.change(-1)
|
||||
q("#next-page-container").remove()
|
||||
this.observer.disconnect()
|
||||
q("#timeline").insertAdjacentHTML("beforeend", text)
|
||||
|
||||
18
src/site/html/static/js/quota.js
Normal file
18
src/site/html/static/js/quota.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import {ElemJS, q} from "./elemjs/elemjs.js"
|
||||
|
||||
class Quota extends ElemJS {
|
||||
constructor() {
|
||||
super(q("#quota"))
|
||||
this.value = +this.element.textContent
|
||||
}
|
||||
|
||||
change(difference) {
|
||||
this.value += difference
|
||||
this.value = Math.max(0, this.value)
|
||||
this.text(this.value)
|
||||
}
|
||||
}
|
||||
|
||||
const quota = new Quota()
|
||||
|
||||
export {quota}
|
||||
7
src/site/pug/fragments/timeline_quota_reached.pug
Normal file
7
src/site/pug/fragments/timeline_quota_reached.pug
Normal file
@@ -0,0 +1,7 @@
|
||||
.error-fragment
|
||||
.message Quota reached
|
||||
.explanation.
|
||||
Each person has a limited number of requests to Bibliogram.
|
||||
You have reached that limit. You cannot load any more data on this instance.
|
||||
Your quota will reset automatically after some time has passed.
|
||||
Or, you could try #[a(href="https://git.sr.ht/~cadence/bibliogram-docs/tree/master/docs/Instances.md") browsing Bibliogram on another instance.]
|
||||
@@ -95,6 +95,8 @@ html
|
||||
.links
|
||||
a(href="/")= ll.t_home
|
||||
a(href=settingsReferrer)= ll.t_settings
|
||||
if constants.quota.enabled
|
||||
.quota Quota left: #[span#quota= remaining]
|
||||
|
||||
- const hasPosts = !user.data.is_private && selectedTimeline.pages.length && selectedTimeline.pages[0].length
|
||||
.timeline-section
|
||||
|
||||
@@ -163,6 +163,9 @@ body
|
||||
> *
|
||||
margin: 5px 8px
|
||||
|
||||
.quota
|
||||
margin: 15px 0px
|
||||
|
||||
.bibliogram-meta
|
||||
margin: 20px 10px
|
||||
border-top: map-get($theme, "edge-context-divider")
|
||||
|
||||
Reference in New Issue
Block a user