Skip to content

Commit

Permalink
Merge branch 'main' into oltp
Browse files Browse the repository at this point in the history
  • Loading branch information
llun committed Sep 21, 2024
2 parents 01742d8 + 2f75a11 commit 3a67a1b
Show file tree
Hide file tree
Showing 7 changed files with 977 additions and 1,019 deletions.
151 changes: 80 additions & 71 deletions app/api/inbox/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ENTITY_TYPE_NOTE, ENTITY_TYPE_QUESTION } from '@llun/activities.schema'
import isMatch from 'lodash/isMatch'
import crypto from 'node:crypto'

import { StatusActivity } from '@/lib/activities/actions/status'
Expand All @@ -13,6 +14,7 @@ import {
CREATE_ANNOUNCE_JOB_NAME,
CREATE_NOTE_JOB_NAME,
CREATE_POLL_JOB_NAME,
DELETE_OBJECT_JOB_NAME,
UPDATE_NOTE_JOB_NAME,
UPDATE_POLL_JOB_NAME
} from '@/lib/jobs/names'
Expand All @@ -31,86 +33,93 @@ const CORS_HEADERS = [HttpMethod.enum.OPTIONS, HttpMethod.enum.POST]

export const OPTIONS = defaultOptions(CORS_HEADERS)

export const POST = ActivityPubVerifySenderGuard(async (request, context) => {
const { storage } = context
const body = await request.json()
const activity = (await compact(body)) as StatusActivity
export const getJobMessage = (activity: StatusActivity) => {
const deduplicationId = crypto
.createHash('sha256')
.update(JSON.stringify(activity.id))
.digest('hex')
switch (activity.type) {
case CreateAction: {
const jobName = ((name: string) => {
switch (name) {
case ENTITY_TYPE_NOTE:
return CREATE_NOTE_JOB_NAME
case ENTITY_TYPE_QUESTION:
return CREATE_POLL_JOB_NAME
default:
return null
}
})(activity.object.type)
if (!jobName) {
return apiErrorResponse(404)
}
await getQueue().publish({
id: deduplicationId,
name: jobName,
data: activity.object
})
return apiResponse(request, CORS_HEADERS, DEFAULT_202, 202)
if (
isMatch(activity, {
type: CreateAction,
object: { type: ENTITY_TYPE_NOTE }
})
) {
return {
id: deduplicationId,
name: CREATE_NOTE_JOB_NAME,
data: activity.object
}
case UpdateAction: {
switch (activity.object.type) {
case ENTITY_TYPE_QUESTION: {
await getQueue().publish({
id: deduplicationId,
name: UPDATE_POLL_JOB_NAME,
data: activity.object
})
break
}
case ENTITY_TYPE_NOTE: {
await getQueue().publish({
id: deduplicationId,
name: UPDATE_NOTE_JOB_NAME,
data: activity.object
})
break
}
}
return apiResponse(request, CORS_HEADERS, DEFAULT_202, 202)
}

if (
isMatch(activity, {
type: CreateAction,
object: { type: ENTITY_TYPE_QUESTION }
})
) {
return {
id: deduplicationId,
name: CREATE_POLL_JOB_NAME,
data: activity.object
}
case AnnounceAction: {
await getQueue().publish({
id: deduplicationId,
name: CREATE_ANNOUNCE_JOB_NAME,
data: activity
})
return apiResponse(request, CORS_HEADERS, DEFAULT_202, 202)
}

if (
isMatch(activity, {
type: UpdateAction,
object: { type: ENTITY_TYPE_QUESTION }
})
) {
return {
id: deduplicationId,
name: UPDATE_POLL_JOB_NAME,
data: activity.object
}
}

if (
isMatch(activity, {
type: UpdateAction,
object: { type: ENTITY_TYPE_NOTE }
})
) {
return {
id: deduplicationId,
name: UPDATE_NOTE_JOB_NAME,
data: activity.object
}
case UndoAction: {
switch (activity.object.type) {
case AnnounceAction: {
const statusId = activity.object.id
await storage.deleteStatus({ statusId })
break
}
}
return apiResponse(request, CORS_HEADERS, DEFAULT_202, 202)
}

if (isMatch(activity, { type: AnnounceAction })) {
return {
id: deduplicationId,
name: CREATE_ANNOUNCE_JOB_NAME,
data: activity
}
case DeleteAction: {
// TODO: Handle delete object type string
if (typeof activity.object === 'string') {
return apiResponse(request, CORS_HEADERS, DEFAULT_202, 202)
}
}

const id = activity.object.id
await storage.deleteStatus({ statusId: id })
return apiResponse(request, CORS_HEADERS, DEFAULT_202, 202)
if (
isMatch(activity, { type: UndoAction, object: { type: AnnounceAction } }) ||
isMatch(activity, { type: DeleteAction })
) {
return {
id: deduplicationId,
name: DELETE_OBJECT_JOB_NAME,
data: activity.object
}
default:
return apiErrorResponse(404)
}

return null
}

export const POST = ActivityPubVerifySenderGuard(async (request) => {
const body = await request.json()
const activity = (await compact(body)) as StatusActivity
const jobMessage = getJobMessage(activity)
if (!jobMessage) {
return apiErrorResponse(404)
}

await getQueue().publish(jobMessage)
return apiResponse(request, CORS_HEADERS, DEFAULT_202, 202)
})
1 change: 1 addition & 0 deletions lib/activities/actions/createStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface CreateStatus extends BaseActivity, ContextEntity {
interface CompactParams {
status: Status
}

export const compact = ({ status }: CompactParams) => {
const published = getISOTimeUTC(status.data.createdAt)
const context = { '@context': 'https://www.w3.org/ns/activitystreams' }
Expand Down
29 changes: 29 additions & 0 deletions lib/jobs/deleteObjectJob.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Tombstone } from '@llun/activities.schema'

import { getTracer } from '../utils/trace'
import { createJobHandle } from './createJobHandle'
import { DELETE_OBJECT_JOB_NAME } from './names'

export const deleteObjectJob = createJobHandle(
DELETE_OBJECT_JOB_NAME,
async (storage, message) => {
const data = message.data
if (typeof data === 'string') {
await getTracer().startActiveSpan('deleteUser', async (span) => {
span.setAttribute('actorId', data)
await storage.deleteActor({
actorId: data
})
})
return
}

const tombStone = Tombstone.parse(data)
await getTracer().startActiveSpan('deleteStatus', async (span) => {
span.setAttribute('statusId', tombStone.id)
await storage.deleteStatus({
statusId: tombStone.id
})
})
}
)
5 changes: 4 additions & 1 deletion lib/jobs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { JobHandle } from '../services/queue/type'
import { createAnnounceJob } from './createAnnounceJob'
import { createNoteJob } from './createNoteJob'
import { createPollJob } from './createPollJob'
import { deleteObjectJob } from './deleteObjectJob'
import {
CREATE_ANNOUNCE_JOB_NAME,
CREATE_NOTE_JOB_NAME,
CREATE_POLL_JOB_NAME,
DELETE_OBJECT_JOB_NAME,
UPDATE_NOTE_JOB_NAME,
UPDATE_POLL_JOB_NAME
} from './names'
Expand All @@ -17,5 +19,6 @@ export const JOBS: Record<string, JobHandle> = {
[UPDATE_NOTE_JOB_NAME]: updateNoteJob,
[CREATE_ANNOUNCE_JOB_NAME]: createAnnounceJob,
[CREATE_POLL_JOB_NAME]: createPollJob,
[UPDATE_POLL_JOB_NAME]: updatePollJob
[UPDATE_POLL_JOB_NAME]: updatePollJob,
[DELETE_OBJECT_JOB_NAME]: deleteObjectJob
}
1 change: 1 addition & 0 deletions lib/jobs/names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export const CREATE_NOTE_JOB_NAME = 'CreateNoteJob'
export const CREATE_POLL_JOB_NAME = 'CreatePollJob'
export const UPDATE_NOTE_JOB_NAME = 'UpdateNoteJob'
export const UPDATE_POLL_JOB_NAME = 'UpdatePollJob'
export const DELETE_OBJECT_JOB_NAME = 'DeleteObjectJob'
39 changes: 20 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
"migrate:make": "knex migrate:make"
},
"dependencies": {
"@aws-sdk/client-lambda": "^3.651.1",
"@aws-sdk/client-s3": "^3.651.1",
"@aws-sdk/s3-presigned-post": "^3.651.1",
"@aws-sdk/client-lambda": "^3.656.0",
"@aws-sdk/client-s3": "^3.654.0",
"@aws-sdk/s3-presigned-post": "^3.654.0",
"@aws-sdk/util-utf8-node": "^3.259.0",
"@date-fns/utc": "^2.1.0",
"@google-cloud/firestore": "^7.10.0",
"@google-cloud/opentelemetry-cloud-trace-exporter": "^2.3.0",
"@jmondi/oauth2-server": "^4.0.2",
"@llun/activities.schema": "^0.2.12",
"@llun/activities.schema": "^0.2.13",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/core": "1.25.1",
"@opentelemetry/exporter-jaeger": "1.25.1",
Expand All @@ -42,15 +42,15 @@
"@opentelemetry/sdk-trace-node": "1.25.1",
"@opentelemetry/semantic-conventions": "1.25.1",
"@popperjs/core": "^2.11.8",
"@upstash/qstash": "2.7.8",
"@upstash/qstash": "2.7.9",
"@vercel/otel": "^1.10.0",
"bcrypt": "^5.1.1",
"better-sqlite3": "^11.3.0",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"classnames": "^2.5.1",
"content-type": "^1.0.5",
"date-fns": "^3.6.0",
"date-fns": "^4.1.0",
"fluent-ffmpeg": "^2.1.3",
"got": "14.4.2",
"html-react-parser": "^5.1.16",
Expand All @@ -60,7 +60,7 @@
"lodash": "^4.17.21",
"marked": "^14.1.2",
"mime-types": "^2.1.35",
"next": "14.2.11",
"next": "14.2.13",
"next-auth": "4.24.7",
"nodemailer": "^6.9.15",
"peggy": "^4.0.3",
Expand All @@ -70,15 +70,16 @@
"react-modal": "^3.16.1",
"resend": "^4.0.0",
"sanitize-html": "^2.13.0",
"sass": "^1.78.0",
"sass": "^1.79.3",
"sharp": "0.33.5",
"zod": "^3.23.8"
},
"devDependencies": {
"@next/env": "^14.2.11",
"@next/env": "^14.2.13",
"@swc-node/register": "^1.10.9",
"@swc/core": "1.7.26",
"@swc/jest": "^0.2.36",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^16.0.1",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
Expand All @@ -91,33 +92,33 @@
"@types/lodash": "^4.17.7",
"@types/mime-types": "^2.1.4",
"@types/node": "^22.5.5",
"@types/nodemailer": "^6.4.15",
"@types/react": "18.3.5",
"@types/nodemailer": "^6.4.16",
"@types/react": "18.3.8",
"@types/react-dom": "18.3.0",
"@types/react-modal": "^3.16.3",
"@types/sanitize-html": "^2.13.0",
"@typescript-eslint/eslint-plugin": "^8.5.0",
"@typescript-eslint/parser": "^8.5.0",
"@typescript-eslint/eslint-plugin": "^8.6.0",
"@typescript-eslint/parser": "^8.6.0",
"dotenv-flow": "^4.1.0",
"eslint": "^9.10.0",
"eslint-config-next": "14.2.11",
"eslint": "^9.11.0",
"eslint-config-next": "14.2.13",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-jest": "^28.8.3",
"eslint-plugin-n": "^17.10.2",
"eslint-plugin-n": "^17.10.3",
"eslint-plugin-promise": "^7.1.0",
"eslint-plugin-unused-imports": "^4.1.4",
"firebase-tools": "^13.17.0",
"firebase-tools": "^13.18.0",
"jest": "^29.7.0",
"jest-extended": "^4.0.2",
"jest-fetch-mock": "^3.0.3",
"postcss": "^8.4.45",
"postcss": "^8.4.47",
"prettier": "3.3.3",
"typescript": "^5.6.2"
},
"resolutions": {
"eslint": "^8.57.0"
},
"packageManager": "yarn@4.4.1"
"packageManager": "yarn@4.5.0"
}
Loading

0 comments on commit 3a67a1b

Please sign in to comment.