add auth token cleanup logic
This commit is contained in:
@@ -119,6 +119,13 @@ async function verifyAuthToken(cookies: Bun.CookieMap): Promise<User | null> {
|
|||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startBackgroundAuthTokenCleanup() {
|
||||||
|
const query = db.query("DELETE FROM auth_tokens WHERE expires_at_unix_ms < $time")
|
||||||
|
setInterval(() => {
|
||||||
|
query.run({ time: new Date().valueOf() })
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
|
||||||
async function signUp(request: Bun.BunRequest<"/api/sign-up">) {
|
async function signUp(request: Bun.BunRequest<"/api/sign-up">) {
|
||||||
const body = await request.json().catch(() => {
|
const body = await request.json().catch(() => {
|
||||||
throw new HttpError(500)
|
throw new HttpError(500)
|
||||||
@@ -199,4 +206,4 @@ async function logout(request: Bun.BunRequest<"/api/logout">, user: User): Promi
|
|||||||
return new Response(undefined, { status: 204 })
|
return new Response(undefined, { status: 204 })
|
||||||
}
|
}
|
||||||
|
|
||||||
export { authenticated, signUp, login, logout }
|
export { authenticated, signUp, login, logout, startBackgroundAuthTokenCleanup }
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import type { Bookmark, BookmarkTag } from "@markone/core/bookmark"
|
import type { Bookmark, BookmarkId, BookmarkTag } from "@markone/core/bookmark"
|
||||||
import type { User } from "@markone/core/user"
|
import type { User } from "@markone/core/user"
|
||||||
import { Readability } from "@mozilla/readability"
|
import { Readability } from "@mozilla/readability"
|
||||||
import { JSDOM } from "jsdom"
|
import { JSDOM } from "jsdom"
|
||||||
@@ -72,6 +72,14 @@ function findBookmark(id: string, user: User): Bookmark | null {
|
|||||||
return bookmark
|
return bookmark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteBookmark(id: BookmarkId, user: User) {
|
||||||
|
db.query("DELETE FROM bookmarks WHERE user_id = $userId AND id = $id").run({
|
||||||
|
id,
|
||||||
|
userId: user.id,
|
||||||
|
})
|
||||||
|
db.query("DELETE FROM bookmark_tags WHERE bookmark_id = ?").run(id)
|
||||||
|
}
|
||||||
|
|
||||||
async function cacheContent(url: string): Promise<CachedContent | null> {
|
async function cacheContent(url: string): Promise<CachedContent | null> {
|
||||||
const res = await fetch(url).catch(() => {
|
const res = await fetch(url).catch(() => {
|
||||||
throw new LinkUnreachable()
|
throw new LinkUnreachable()
|
||||||
@@ -162,6 +170,7 @@ function findBookmarkTags(bookmark: Bookmark): BookmarkTag[] {
|
|||||||
export {
|
export {
|
||||||
insertDemoBookmarks,
|
insertDemoBookmarks,
|
||||||
insertBookmark,
|
insertBookmark,
|
||||||
|
deleteBookmark,
|
||||||
findBookmark,
|
findBookmark,
|
||||||
findBookmarkCachedContent,
|
findBookmarkCachedContent,
|
||||||
cacheContent,
|
cacheContent,
|
||||||
|
@@ -10,6 +10,7 @@ import {
|
|||||||
UnsupportedLink,
|
UnsupportedLink,
|
||||||
assignTagsToBookmark,
|
assignTagsToBookmark,
|
||||||
cacheContent,
|
cacheContent,
|
||||||
|
deleteBookmark,
|
||||||
findBookmark,
|
findBookmark,
|
||||||
findBookmarkCachedContent,
|
findBookmarkCachedContent,
|
||||||
findBookmarkTags,
|
findBookmarkTags,
|
||||||
@@ -22,6 +23,7 @@ const BOOKMARK_PAGINATION_LIMIT = 100
|
|||||||
const ListUserBookmarksParams = type({
|
const ListUserBookmarksParams = type({
|
||||||
limit: ["number", "=", BOOKMARK_PAGINATION_LIMIT],
|
limit: ["number", "=", BOOKMARK_PAGINATION_LIMIT],
|
||||||
skip: ["number", "=", 0],
|
skip: ["number", "=", 0],
|
||||||
|
"tags?": "string",
|
||||||
})
|
})
|
||||||
|
|
||||||
const AddBookmarkRequestBody = type({
|
const AddBookmarkRequestBody = type({
|
||||||
@@ -36,36 +38,53 @@ const AddTagRequestBody = type({
|
|||||||
})
|
})
|
||||||
|
|
||||||
async function listUserBookmarks(request: Bun.BunRequest<"/api/bookmarks">, user: User) {
|
async function listUserBookmarks(request: Bun.BunRequest<"/api/bookmarks">, user: User) {
|
||||||
const queryParams = ListUserBookmarksParams(request.params)
|
const { searchParams } = new URL(request.url)
|
||||||
|
const queryParams = ListUserBookmarksParams(Object.fromEntries(searchParams))
|
||||||
if (queryParams instanceof type.errors) {
|
if (queryParams instanceof type.errors) {
|
||||||
throw new HttpError(400, "", queryParams.summary)
|
throw new HttpError(400, "", queryParams.summary)
|
||||||
}
|
}
|
||||||
|
|
||||||
const listBookmarksQuery = db.query(
|
let results: Bookmark[]
|
||||||
`
|
if (queryParams.tags) {
|
||||||
SELECT bookmarks.id, bookmarks.title, bookmarks.url FROM bookmarks
|
const tagNames = queryParams.tags.split(",")
|
||||||
WHERE bookmarks.user_id = $userId
|
|
||||||
ORDER BY bookmarks.id DESC
|
|
||||||
LIMIT $limit OFFSET $skip
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
|
|
||||||
const results = listBookmarksQuery.all({
|
const tagIdsQuery = db.query<{ id: string }, string[]>(
|
||||||
userId: user.id,
|
`SELECT id FROM tags WHERE name IN (${Array(tagNames.length).fill("?").join(",")})`,
|
||||||
limit: queryParams.limit,
|
)
|
||||||
skip: queryParams.skip,
|
|
||||||
})
|
const tagIds = tagIdsQuery.all(...tagNames).map(({ id }) => id)
|
||||||
|
|
||||||
|
const query = db.query(`
|
||||||
|
SELECT bookmarks.id, bookmarks.title, bookmarks.url FROM bookmarks
|
||||||
|
INNER JOIN bookmark_tags
|
||||||
|
ON bookmark_tags.bookmark_id = bookmarks.id
|
||||||
|
WHERE bookmarks.user_id = ? AND bookmark_tags.tag_id IN (${Array(tagIds.length).fill("?").join(",")})
|
||||||
|
ORDER BY bookmarks.id DESC
|
||||||
|
LIMIT ? OFFSET ?
|
||||||
|
`)
|
||||||
|
|
||||||
|
results = query.all(...[user.id, ...tagIds, queryParams.limit, queryParams.skip]) as Bookmark[]
|
||||||
|
} else {
|
||||||
|
const query = db.query(`
|
||||||
|
SELECT bookmarks.id, bookmarks.title, bookmarks.url FROM bookmarks
|
||||||
|
WHERE bookmarks.user_id = $userId
|
||||||
|
ORDER BY bookmarks.id DESC
|
||||||
|
LIMIT $limit OFFSET $skip
|
||||||
|
`)
|
||||||
|
|
||||||
|
results = query.all({
|
||||||
|
userId: user.id,
|
||||||
|
limit: queryParams.limit,
|
||||||
|
skip: queryParams.skip,
|
||||||
|
}) as Bookmark[]
|
||||||
|
}
|
||||||
|
|
||||||
return Response.json(results, { status: 200 })
|
return Response.json(results, { status: 200 })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteUserBookmark(request: Bun.BunRequest<"/api/bookmarks/:id">, user: User) {
|
async function deleteUserBookmark(request: Bun.BunRequest<"/api/bookmarks/:id">, user: User) {
|
||||||
if (user.id !== DEMO_USER.id) {
|
if (user.id !== DEMO_USER.id) {
|
||||||
const deleteBookmarkQuery = db.query("DELETE FROM bookmarks WHERE user_id = $userId AND id = $id")
|
deleteBookmark(request.params.id, user)
|
||||||
const tx = db.transaction(() => {
|
|
||||||
deleteBookmarkQuery.run({ userId: user.id, id: request.params.id })
|
|
||||||
})
|
|
||||||
tx()
|
|
||||||
}
|
}
|
||||||
return Response.json(undefined, { status: 204 })
|
return Response.json(undefined, { status: 204 })
|
||||||
}
|
}
|
||||||
@@ -94,7 +113,7 @@ async function addBookmark(request: Bun.BunRequest<"/api/bookmarks">, user: User
|
|||||||
|
|
||||||
const tagNames = new Set(body.tags)
|
const tagNames = new Set(body.tags)
|
||||||
for (const tag of tagNames) {
|
for (const tag of tagNames) {
|
||||||
if (/[\s#]/g.test(tag)) {
|
if (tag.length === 0 || /[\s#]/g.test(tag)) {
|
||||||
throw new HttpError(400, "InvalidTag", "Tags cannot contain '#' or whitespaces")
|
throw new HttpError(400, "InvalidTag", "Tags cannot contain '#' or whitespaces")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,9 +135,9 @@ async function addBookmark(request: Bun.BunRequest<"/api/bookmarks">, user: User
|
|||||||
|
|
||||||
if (tagNames.size > 0) {
|
if (tagNames.size > 0) {
|
||||||
const tagQuery = db.query<Partial<BookmarkTag>, string[]>(`
|
const tagQuery = db.query<Partial<BookmarkTag>, string[]>(`
|
||||||
SELECT id, name FROM tags
|
SELECT id, name FROM tags
|
||||||
WHERE user_id = ? AND name IN (${Array(tagNames.size).fill("?").join(",")})
|
WHERE user_id = ? AND name IN (${Array(tagNames.size).fill("?").join(",")})
|
||||||
`)
|
`)
|
||||||
const tags = tagQuery.all(user.id, ...tagNames)
|
const tags = tagQuery.all(user.id, ...tagNames)
|
||||||
|
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
@@ -128,8 +147,12 @@ async function addBookmark(request: Bun.BunRequest<"/api/bookmarks">, user: User
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdTags = insertTags([...tagNames], user)
|
if (tagNames.size > 0) {
|
||||||
assignTagsToBookmark(createdTags, bookmark)
|
const createdTags = insertTags([...tagNames], user)
|
||||||
|
bookmark.tags.push(...createdTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
assignTagsToBookmark(bookmark.tags, bookmark)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.json(bookmark, { status: 200 })
|
return Response.json(bookmark, { status: 200 })
|
||||||
|
@@ -4,7 +4,7 @@ import { ulid } from "ulid"
|
|||||||
import { db } from "~/database.ts"
|
import { db } from "~/database.ts"
|
||||||
|
|
||||||
function insertTags(names: string[], user: User): BookmarkTag[] {
|
function insertTags(names: string[], user: User): BookmarkTag[] {
|
||||||
console.log(names)
|
console.log("======== insert tags", names)
|
||||||
const insertTags = db.query(`
|
const insertTags = db.query(`
|
||||||
INSERT INTO tags (id, name, user_id)
|
INSERT INTO tags (id, name, user_id)
|
||||||
VALUES ${Array(names.length).fill("(?,?,?)").join(",")}
|
VALUES ${Array(names.length).fill("(?,?,?)").join(",")}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { authenticated, login, logout, signUp } from "./auth/auth.ts"
|
import { authenticated, login, logout, signUp, startBackgroundAuthTokenCleanup } from "./auth/auth.ts"
|
||||||
import { startBackgroundSessionCleanup } from "./auth/session.ts"
|
import { startBackgroundSessionCleanup } from "./auth/session.ts"
|
||||||
import { insertDemoBookmarks } from "./bookmark/bookmark.ts"
|
import { insertDemoBookmarks } from "./bookmark/bookmark.ts"
|
||||||
import {
|
import {
|
||||||
@@ -19,6 +19,7 @@ async function main() {
|
|||||||
const user = await createDemoUser()
|
const user = await createDemoUser()
|
||||||
insertDemoBookmarks(user)
|
insertDemoBookmarks(user)
|
||||||
startBackgroundSessionCleanup()
|
startBackgroundSessionCleanup()
|
||||||
|
startBackgroundAuthTokenCleanup()
|
||||||
|
|
||||||
Bun.serve({
|
Bun.serve({
|
||||||
routes: {
|
routes: {
|
||||||
|
Reference in New Issue
Block a user