Add public timeline

This commit is contained in:
Jesse Wierzbinski 2023-10-01 14:07:29 -10:00
parent bff170d2e2
commit b7587f8d3f
9 changed files with 158 additions and 7 deletions

View file

@ -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: {

View 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()))
);
};

View file

@ -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)))

View file

@ -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>`),
{