mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 13:59:16 +01:00
Add public timeline
This commit is contained in:
parent
bff170d2e2
commit
b7587f8d3f
9 changed files with 158 additions and 7 deletions
|
|
@ -15,6 +15,7 @@ export default async (
|
|||
const {
|
||||
limit,
|
||||
exclude_reblogs,
|
||||
pinned,
|
||||
}: {
|
||||
max_id?: string;
|
||||
since_id?: string;
|
||||
|
|
@ -23,6 +24,7 @@ export default async (
|
|||
only_media?: boolean;
|
||||
exclude_replies?: boolean;
|
||||
exclude_reblogs?: boolean;
|
||||
// TODO: Add with_muted
|
||||
pinned?: boolean;
|
||||
tagged?: string;
|
||||
} = matchedRoute.query;
|
||||
|
|
@ -33,6 +35,10 @@ export default async (
|
|||
|
||||
if (!user) return errorResponse("User not found", 404);
|
||||
|
||||
if (pinned) {
|
||||
// TODO: Add pinned statuses
|
||||
}
|
||||
|
||||
// TODO: Check if status can be seen by this user
|
||||
const statuses = await Status.find({
|
||||
where: {
|
||||
|
|
|
|||
89
server/api/api/v1/timelines/public.ts
Normal file
89
server/api/api/v1/timelines/public.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { RawObject } from "~database/entities/RawObject";
|
||||
|
||||
/**
|
||||
* Fetch public timeline statuses
|
||||
*/
|
||||
export default async (req: Request): Promise<Response> => {
|
||||
const {
|
||||
local,
|
||||
limit = 20,
|
||||
max_id,
|
||||
min_id,
|
||||
only_media,
|
||||
remote,
|
||||
since_id,
|
||||
} = await parseRequest<{
|
||||
local?: boolean;
|
||||
only_media?: boolean;
|
||||
remote?: boolean;
|
||||
max_id?: string;
|
||||
since_id?: string;
|
||||
min_id?: string;
|
||||
limit?: number;
|
||||
}>(req);
|
||||
|
||||
if (limit < 1 || limit > 40) {
|
||||
return errorResponse("Limit must be between 1 and 40", 400);
|
||||
}
|
||||
|
||||
let query = RawObject.createQueryBuilder("object")
|
||||
.where("object.data->>'type' = 'Note'")
|
||||
.andWhere("CAST(object.data->>'to' AS jsonb) @> CAST(:to AS jsonb)", {
|
||||
to: JSON.stringify([
|
||||
"https://www.w3.org/ns/activitystreams#Public",
|
||||
]),
|
||||
})
|
||||
.orderBy("object.data->>'published'", "DESC")
|
||||
.take(limit);
|
||||
|
||||
if (max_id) {
|
||||
const maxPost = await RawObject.findOneBy({ id: max_id });
|
||||
if (maxPost) {
|
||||
query = query.andWhere("object.data->>'published' < :max_date", {
|
||||
max_date: maxPost.data.published,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (min_id) {
|
||||
const minPost = await RawObject.findOneBy({ id: min_id });
|
||||
if (minPost) {
|
||||
query = query.andWhere("object.data->>'published' > :min_date", {
|
||||
min_date: minPost.data.published,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (since_id) {
|
||||
const sincePost = await RawObject.findOneBy({ id: since_id });
|
||||
if (sincePost) {
|
||||
query = query.andWhere("object.data->>'published' >= :since_date", {
|
||||
since_date: sincePost.data.published,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (only_media) {
|
||||
query = query.andWhere("object.data->'attachment' IS NOT NULL");
|
||||
}
|
||||
|
||||
if (local) {
|
||||
query = query.andWhere("object.data->>'actor' LIKE :actor", {
|
||||
actor: `%${new URL(req.url).hostname}%`,
|
||||
});
|
||||
}
|
||||
|
||||
if (remote) {
|
||||
query = query.andWhere("object.data->>'actor' NOT LIKE :actor", {
|
||||
actor: `%${new URL(req.url).hostname}%`,
|
||||
});
|
||||
}
|
||||
|
||||
const objects = await query.getMany();
|
||||
|
||||
return jsonResponse(
|
||||
await Promise.all(objects.map(async object => await object.toAPI()))
|
||||
);
|
||||
};
|
||||
|
|
@ -12,7 +12,7 @@ export default async (
|
|||
req: Request,
|
||||
matchedRoute: MatchedRoute
|
||||
): Promise<Response> => {
|
||||
const scopes = (matchedRoute.query.scopes || "")
|
||||
const scopes = (matchedRoute.query.scope || "")
|
||||
.replaceAll("+", " ")
|
||||
.split(" ");
|
||||
const redirect_uri = matchedRoute.query.redirect_uri;
|
||||
|
|
@ -21,18 +21,18 @@ export default async (
|
|||
|
||||
const formData = await req.formData();
|
||||
|
||||
const username = formData.get("username")?.toString() || null;
|
||||
const email = formData.get("email")?.toString() || null;
|
||||
const password = formData.get("password")?.toString() || null;
|
||||
|
||||
if (response_type !== "code")
|
||||
return errorResponse("Invalid response type (try 'code')", 400);
|
||||
|
||||
if (!username || !password)
|
||||
if (!email || !password)
|
||||
return errorResponse("Missing username or password", 400);
|
||||
|
||||
// Get user
|
||||
const user = await User.findOneBy({
|
||||
username,
|
||||
email,
|
||||
});
|
||||
|
||||
if (!user || !(await Bun.password.verify(password, user.password)))
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default async (
|
|||
(await html.text())
|
||||
.replace(
|
||||
"{{URL}}",
|
||||
`/auth/login?redirect_uri=${matchedRoute.query.redirect_uri}&response_type=${matchedRoute.query.response_type}&client_id=${matchedRoute.query.client_id}&scopes=${matchedRoute.query.scopes}`
|
||||
`/auth/login?redirect_uri=${matchedRoute.query.redirect_uri}&response_type=${matchedRoute.query.response_type}&client_id=${matchedRoute.query.client_id}&scope=${matchedRoute.query.scope}`
|
||||
)
|
||||
.replace("{{STYLES}}", `<style>${await css.text()}</style>`),
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue