···
1
+
import { NextRequest, NextResponse } from 'next/server'
2
+
import { getSession } from '@/lib/session'
3
+
import { getGlobalOAuthClient } from '@/lib/auth/client'
4
+
import { Agent } from '@atproto/api'
5
+
import { getPdsEndpoint } from '@atproto/common-web'
6
+
import { IdResolver } from '@atproto/identity'
7
+
import { TID } from '@atproto/common'
9
+
async function getSessionAgent(): Promise<{ agent: Agent; oauthSession: any } | null> {
11
+
const session = await getSession()
17
+
const client = await getGlobalOAuthClient()
18
+
const oauthSession = await client.restore(session.did)
20
+
if (!oauthSession) {
21
+
console.log('OAuth session restoration failed')
25
+
const agent = new Agent(oauthSession)
26
+
return { agent, oauthSession }
28
+
console.error('Session restore failed:', error)
33
+
// POST - Accept a change request and apply the proposed changes
34
+
export async function POST(request: NextRequest) {
36
+
console.log('POST /api/change-request/accept - Starting request')
38
+
const sessionResult = await getSessionAgent()
39
+
if (!sessionResult) {
40
+
console.log('No agent available - authentication required')
41
+
return NextResponse.json({ error: 'Authentication required' }, { status: 401 })
44
+
const { agent, oauthSession } = sessionResult
45
+
const body = await request.json()
46
+
const { changeRequestUri } = body
48
+
if (!changeRequestUri) {
49
+
return NextResponse.json({ error: 'changeRequestUri required' }, { status: 400 })
52
+
// Parse the change request URI to get DID and rkey
53
+
const uriParts = changeRequestUri.replace('at://', '').split('/')
54
+
const requesterDid = uriParts[0]
55
+
const changeRequestRkey = uriParts[2]
57
+
console.log('Fetching change request:', { requesterDid, changeRequestRkey })
59
+
// Resolve requester's DID to get their PDS
60
+
const resolver = new IdResolver()
61
+
const requesterDidDoc = await resolver.did.resolve(requesterDid)
63
+
if (!requesterDidDoc) {
64
+
return NextResponse.json({ error: 'Could not resolve requester DID' }, { status: 404 })
67
+
const requesterPdsEndpoint = getPdsEndpoint(requesterDidDoc)
68
+
if (!requesterPdsEndpoint) {
69
+
return NextResponse.json({ error: 'No PDS endpoint found for requester' }, { status: 404 })
72
+
// Create agent for requester's PDS to fetch the change request
73
+
const requesterAgent = new Agent({ service: requesterPdsEndpoint })
75
+
// Fetch the change request record
76
+
const changeRequestResponse = await requesterAgent.com.atproto.repo.getRecord({
78
+
collection: 'org.impactindexer.changeRequest',
79
+
rkey: changeRequestRkey
82
+
if (!changeRequestResponse.success) {
83
+
return NextResponse.json({ error: 'Change request not found' }, { status: 404 })
86
+
const changeRequestData = changeRequestResponse.data.value as any
88
+
// Verify that the current user is the target (project owner)
89
+
if (changeRequestData.targetDid !== agent.assertDid) {
90
+
return NextResponse.json({
91
+
error: 'Unauthorized - you can only accept change requests for your own projects'
95
+
console.log('Fetching proposed record data...')
97
+
// Fetch the proposed record data
98
+
const proposedUri = changeRequestData.proposedRecord
99
+
const proposedUriParts = proposedUri.replace('at://', '').split('/')
100
+
const proposedRkey = proposedUriParts[2]
102
+
const proposedResponse = await requesterAgent.com.atproto.repo.getRecord({
103
+
repo: requesterDid,
104
+
collection: 'org.impactindexer.status',
108
+
if (!proposedResponse.success) {
109
+
return NextResponse.json({ error: 'Proposed record not found' }, { status: 404 })
112
+
const proposedData = proposedResponse.data.value as any
114
+
console.log('Applying proposed changes to project owner record...')
116
+
// Check if the project owner has an existing record
117
+
let existingRecord = null
118
+
let rkey = TID.nextStr()
121
+
const listResponse = await agent.com.atproto.repo.listRecords({
122
+
repo: agent.assertDid,
123
+
collection: 'org.impactindexer.status',
127
+
if (listResponse.data.records.length > 0) {
128
+
existingRecord = listResponse.data.records[0]
129
+
rkey = existingRecord.uri.split('/').pop() || TID.nextStr()
132
+
console.log('No existing record found, will create new one')
135
+
// Create the updated record with proposed data, preserving timestamps appropriately
136
+
const now = new Date().toISOString()
137
+
const updatedRecord = {
138
+
$type: 'org.impactindexer.status',
139
+
displayName: proposedData.displayName,
140
+
description: proposedData.description,
141
+
website: proposedData.website,
142
+
fundingReceived: proposedData.fundingReceived,
143
+
fundingGivenOut: proposedData.fundingGivenOut,
144
+
annualBudget: proposedData.annualBudget,
145
+
teamSize: proposedData.teamSize,
146
+
sustainableRevenuePercent: proposedData.sustainableRevenuePercent,
147
+
categories: proposedData.categories,
148
+
impactMetrics: proposedData.impactMetrics,
149
+
geographicDistribution: proposedData.geographicDistribution,
150
+
createdAt: (existingRecord?.value as any)?.createdAt || now,
154
+
console.log('Final record to save:', updatedRecord)
156
+
// Remove undefined fields
157
+
Object.keys(updatedRecord).forEach(key => {
158
+
if ((updatedRecord as any)[key] === undefined) {
159
+
delete (updatedRecord as any)[key]
163
+
// Save the updated record to the project owner's repository
164
+
const response = await agent.com.atproto.repo.putRecord({
165
+
repo: agent.assertDid,
166
+
collection: 'org.impactindexer.status',
168
+
record: updatedRecord,
172
+
console.log('Successfully updated project owner record')
174
+
return NextResponse.json({
176
+
updatedRecordUri: response.data.uri,
177
+
updatedRecordCid: response.data.cid,
178
+
appliedChanges: updatedRecord
182
+
console.error('Accept change request failed:', error)
183
+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })