Move account sync to inbox processing and reduce timeout to 3 hours, add stub route for /api/v2/, change inbox and outbox processing to stop using fs_list and remove file locking that was causing significant delays, start using struct status_ref to include the id URL and not just the local status id

master
teknomunk 3 months ago
parent 55a6a6c05c
commit d714135f06

@ -61,6 +61,7 @@ static void process_fetch_once()
printf( "new count = %d\n", ffdb_trie_count(STUBS) );
}
/*
static void process_account_sync_once()
{
for( int i = 0; i < 10; ++i ) {
@ -81,12 +82,13 @@ static void process_account_sync_once()
return;
}
}
*/
void process_fetch()
{
while(true) {
process_fetch_once();
process_account_sync_once();
//process_account_sync_once();
for( int i = 0; i < 30; ++i ) {
if( terminate ) { return; };

@ -56,7 +56,8 @@ bool route_announce( struct ap_object* act )
s->url = strdup(act->id);
s->published = act->published;
s->repost_id = original_post->id;
s->repost.url = strdup(act->object.ref);
s->repost.id = original_post->id;
s->account_id = actor_account->id;
s->sensitive = act->sensitive;
status_save_new(s);

@ -14,6 +14,7 @@
#include "controller/did.h"
#include "controller/inbox.h"
#include "controller/mastodon_api.h"
#include "controller/mastodon_api/v2.h"
#include "controller/nodeinfo.h"
#include "controller/oauth.h"
#include "controller/owner.h"
@ -227,8 +228,12 @@ bool route_request( struct http_request* req )
}
if( g_server->configured ) {
if( http_request_route( req, "/api/v1/" ) ) {
return route_mastodon_api( req );
if( http_request_route( req, "/api/" ) ) {
if( http_request_route( req, "v1/" ) ) {
return route_mastodon_api( req );
} else if( http_request_route( req, "v2/" ) ) {
return route_mastodon_api_v2( req );
}
} else if( http_request_route( req, "/api/pleroma" ) ) {
return route_pleroma_api2( req );
} else if( http_request_route( req, "/admin" ) ) {

@ -0,0 +1,7 @@
#include "v2.h"
bool route_mastodon_api_v2( struct http_request* req )
{
return false;
}

@ -0,0 +1,8 @@
#pragma once
#include <stdbool.h>
struct http_request;
bool route_mastodon_api_v2( struct http_request* req );

@ -28,6 +28,7 @@
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
#include <dirent.h>
static bool blacklisted( struct outbox_envelope* env )
{
@ -164,7 +165,7 @@ static bool delivery_succeeded( long status_code )
return false;
}
static bool process_envelope( struct outbox_envelope* env )
static bool process_envelope( struct outbox_envelope* env, bool fast_only )
{
bool result = false;
char* postdata = NULL;
@ -198,13 +199,12 @@ static bool process_envelope( struct outbox_envelope* env )
p = peer_create_from_domain( domain );
// Handle tor delivery
//*
if( p->tor_hidden_service ) {
char* path = strchr( inbox + 9, '/' );
tor_inbox = aformat( "http://%s%s", p->tor_hidden_service, path );
printf( "Using TOR inbox: %s\n", tor_inbox );
}
//*/
struct fetch_data fd;
fd.p = p;
@ -253,54 +253,62 @@ static bool process_envelope( struct outbox_envelope* env )
// Force null termination
postdata = realloc( postdata, size + 1 );
postdata[size] = '\0';
printf( "post: %s\n", postdata );
//printf( "post: %s\n", postdata );
long status_code;
if( !p->admin_disable_tor ) {
// Try TOR Hidden Service delivery
if( tor_inbox ) {
status_code = submit_activity( postdata, inbox, tor_inbox, keys, &fd, true );
if( !fast_only || ( time(NULL) - p->last_hidden_service_delivery < (3600*24*30) ) ) {
if( !p->admin_disable_tor ) {
// Try TOR Hidden Service delivery
if( tor_inbox ) {
status_code = submit_activity( postdata, inbox, tor_inbox, keys, &fd, true );
if( delivery_succeeded(status_code) ) {
p->last_hidden_service_delivery = time(NULL);
goto success;
}
}
// Try TOR delivery
status_code = submit_activity( postdata, inbox, inbox, keys, &fd, true );
if( delivery_succeeded(status_code) ) {
p->last_hidden_service_delivery = time(NULL);
p->last_tor_delivery = time(NULL);
goto success;
}
}
}
// Try TOR delivery
status_code = submit_activity( postdata, inbox, inbox, keys, &fd, true );
if( !fast_only || ( time(NULL) - p->last_successful_delivery < (3600*24*30 ) ) ) {
// Try clearnet delivery
status_code = submit_activity( postdata, inbox, inbox, keys, &fd, false );
if( delivery_succeeded(status_code) ) {
p->last_tor_delivery = time(NULL);
p->last_successful_delivery = time(NULL);
goto success;
}
}
// Try clearnet delivery
status_code = submit_activity( postdata, inbox, inbox, keys, &fd, false );
// delivery failed
printf( "\nServer returned status code %ld\n", status_code );
if( env->retry_after ) {
env->retries += 1;
}
if( p ) {
p->last_failed_delivery = time(NULL);
// delivery failed
printf( "\nServer returned status code %ld\n", status_code );
if( env->retry_after ) {
env->retries += 1;
}
if( p ) {
p->last_failed_delivery = time(NULL);
if( p->last_successful_delivery - p->last_failed_delivery > 60*60*24*7 ) {
// At least one week since last successful delivery
printf( "Over a week since last successful delivery, marking for discard.\n" );
env->retries = g_server->outbox_discard_limit;
}
if( p->last_successful_delivery - p->last_failed_delivery > 60*60*24*7 ) {
// At least one week since last successful delivery
printf( "Over a week since last successful delivery, marking for discard.\n" );
env->retries = g_server->outbox_discard_limit;
peer_save(p);
}
peer_save(p);
}
if( env->retries >= g_server->outbox_discard_limit ) {
// Force discard after 10 delivery attempts
// TODO: change this to a configuration option
goto discard;
if( env->retries >= g_server->outbox_discard_limit ) {
// Force discard after 10 delivery attempts
// TODO: change this to a configuration option
goto discard;
}
env->retry_after = time(NULL) + 60 * ( env->retries + 1 ) * ( env->retries + 1 );
outbox_envelope_save(env);
}
env->retry_after = time(NULL) + 60 * ( env->retries + 1 ) * ( env->retries + 1 );
outbox_envelope_save(env);
goto failed;
@ -333,33 +341,36 @@ discard:
goto cleanup;
}
static bool process_pending()
static bool process_pending( bool fast_only )
{
int head = fs_list_get("data/outbox/HEAD");
int tail = fs_list_get("data/outbox/TAIL");
DIR* d = opendir("data/outbox");
if( !d ) { return false; }
struct dirent* entry;
bool result = false;
for( int i = head; i > tail; --i ) {
while( (entry=readdir(d)) ) {
char* remain;
int i = strtol(entry->d_name,&remain,10);
if( 0 != strcmp( remain, ".json" ) ) { continue; }
struct outbox_envelope* env = outbox_envelope_from_id( i );
if( !env ) {
if( i == tail+1 ) {
// Envelope already sent, advance tail
fs_list_set( "data/outbox/TAIL", i );
tail += 1;
result = true;
}
} else {
if( env ) {
if( env->sent ) {
outbox_envelope_delete(env);
} else if( process_envelope(env) ) {
printf( "Done with outbox/%d.json\n", i );
} else if( process_envelope(env, fast_only ) ) {
printf( "Envelope data/outbox/%d.json sent\n", i );
outbox_envelope_delete(env);
return true;
env = NULL;
}
}
outbox_envelope_free(env);
fflush(stdout);
}
closedir(d);
return result;
}
bool cleanup_box( const char* box );
@ -371,8 +382,8 @@ void process_outbox()
while( !terminate ) {
bool activity = false;
activity |= process_pending();
//activity |= cleanup_box("data/outbox");
activity |= process_pending(true);
activity |= process_pending(false);
if( !activity ) {
fflush(stdout);

@ -78,7 +78,8 @@ succeeded:
goto cleanup;
cleanup:
if( a ) {
a->next_update = time(NULL) + (60*60*24*3) + (rand() % (60*60*24*2)); // Next update in 3-5 days
//a->next_update = time(NULL) + (60*60*24*1) + (rand() % (60*60*24*2)); // Next update in 1-3 days
a->next_update = time(NULL) + (60*60*3); // 3 hours until next update
printf( "Account saved.\n" );
account_save(a);
account_index_webfinger(a);

@ -245,15 +245,21 @@ struct ap_object* activity_create_Note( struct status* s )
act->source.content = strdup(s->source);
}
act->content.content = strdup(status_render_source(s,g_server->domain));
if( s->in_reply_to ) {
struct status* s_in_reply_to = status_from_id( s->in_reply_to );
if( s_in_reply_to ) {
act->in_reply_to = strdup( s_in_reply_to->url );
status_free(s_in_reply_to);
if( s->in_reply_to.id ) {
if( s->in_reply_to.url ) {
act->in_reply_to = strdup( s->in_reply_to.url );
} else {
struct status* s_in_reply_to = status_from_id( s->in_reply_to.id );
if( s_in_reply_to ) {
act->in_reply_to = strdup( s_in_reply_to->url );
status_free(s_in_reply_to);
}
}
}
if( s->quote_id ) {
struct status* s_quote_post = status_from_id( s->quote_id );
if( s->quote.url ) {
act->quote_url = strdup(s->quote.url);
} else if( s->quote.id ) {
struct status* s_quote_post = status_from_id( s->quote.id );
if( s_quote_post ) {
act->quote_url = strdup( s_quote_post->url );
status_free(s_quote_post);

@ -90,7 +90,7 @@ static bool do_fetch_tor( const char* uri, struct fetch_data* fd, const char* re
HTTP_REQ_RESULT_STATUS, &status_code,
HTTP_RES_HEADER_CALLBACK, fetch_handle_header, (void*)fd,
HTTP_REQ_PROXY, proxy,
HTTP_REQ_TIMEOUT, (void*)5,
HTTP_REQ_TIMEOUT, (void*)10,
NULL,
};
printf( "GET %s\n", uri );

@ -47,7 +47,9 @@ void mark_post( struct bitmap* b, int status_id, bool force )
if( s->published > time(NULL) - 3600 * 24 * 2 ) { // keep all posts in the last 2 days
e->marked = true;
} else if( s->account_id == owner_account_id ) { // Keep all owner posts
if( s->repost_id ) {
if( s->repost.id ) {
e->marked = true;
} else if( s->quote.id ) {
e->marked = true;
}
} else if( s->bookmarked ) {
@ -64,11 +66,14 @@ void mark_post( struct bitmap* b, int status_id, bool force )
}
// Flood fill to keep all related posts alive
if( s->repost_id ) {
mark_post( b, s->repost_id, true );
if( s->repost.id ) {
mark_post( b, s->repost.id, true );
}
if( s->in_reply_to.id ) {
mark_post( b, s->in_reply_to.id, true );
}
if( s->in_reply_to ) {
mark_post( b, s->in_reply_to, true );
if( s->quote.id ) {
mark_post( b, s->quote.id, true );
}
for( int i = 0; i < s->replies.count; ++i ) {
mark_post( b, s->replies.items[i], true );

@ -6,7 +6,6 @@
#include "json/layout.h"
#include "http/server/request.h"
#include "http/server/header.h"
#include "ffdb/fs_list.h"
#include "collections/collection.h"
#include "collections/array.h"
@ -89,6 +88,7 @@ bool envelope_create_from_request( struct http_request* req )
memset( &env, 0, sizeof(env) );
// Read body in
printf("reading body..." ); fflush(stdout);
FILE* body = http_request_get_request_data(req);
size_t s;
FILE* mem = open_memstream(&env.body, &s);
@ -96,6 +96,7 @@ bool envelope_create_from_request( struct http_request* req )
fclose(mem);
env.body = realloc(env.body,s+1);
env.body[s] = '\0';
printf( "done.\n" );
// Create timestamp
uint64_t time_ns;
@ -120,7 +121,8 @@ bool envelope_create_from_request( struct http_request* req )
}
// Get a space in the inbox
int head = fs_list_inc( "data/inbox/HEAD" );
int head = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
printf( "using head=%d\n", head );
// Setup filenames
char filename[512];

@ -1,12 +1,12 @@
#include "outbox_envelope.h"
#include "json/layout.h"
#include "ffdb/fs_list.h"
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#define OBJ_TYPE struct outbox_envelope
static struct json_object_field layout[] = {
@ -50,7 +50,11 @@ void outbox_envelope_free( struct outbox_envelope* env )
void outbox_envelope_save( struct outbox_envelope* env )
{
if( env->id == 0 ) {
int id = fs_list_inc( "data/outbox/HEAD" );
struct timespec ts;
clock_gettime( CLOCK_REALTIME, &ts );
int id = ts.tv_sec * 1000 + ts.tv_nsec / (100*1000*1000) + (rand() % 100);
//int id = fs_list_inc( "data/outbox/HEAD" );
env->id = id;
}
@ -78,11 +82,6 @@ struct outbox_envelope* outbox_envelope_from_id( int id )
return env;
}
struct outbox_envelope* outbox_envelope_load_next()
{
return outbox_envelope_from_id( fs_list_get( "data/outbox/TAIL" ) + 1 );
}
void outbox_envelope_delete( struct outbox_envelope* env )
{
char filename[512];

@ -21,7 +21,6 @@ void outbox_envelope_free( struct outbox_envelope* env );
struct outbox_envelope* outbox_envelope_from_id( int id );
void outbox_envelope_save( struct outbox_envelope* env );
//struct outbox_envelope* outbox_envelope_load_next();
void outbox_envelope_delete( struct outbox_envelope* env );
struct outbox_envelope_list

@ -54,10 +54,58 @@ static struct json_object_field status_layout[] = {
JSON_FIELD_ENUM( visibility, visibility_enum, false ),
JSON_FIELD_DATETIME( published, false ),
JSON_FIELD_INTEGER( in_reply_to, false ),
JSON_FIELD_INTEGER( quote_id, false ),
JSON_FIELD_INTEGER( repost_id, false ),
JSON_FIELD_INTEGER( root_status_id, false ),
{
.key = "in_reply_to",
.offset = offsetof( OBJ_TYPE, in_reply_to.id ),
.required = false,
.type = &json_field_integer,
},
{
.key = "in_reply_to_url",
.offset = offsetof( OBJ_TYPE, in_reply_to.url ),
.required = false,
.type = &json_field_string,
},
{
.key = "quote_id",
.offset = offsetof( OBJ_TYPE, quote.id ),
.required = false,
.type = &json_field_integer,
},
{
.key = "quote_url",
.offset = offsetof( OBJ_TYPE, quote.url ),
.required = false,
.type = &json_field_string,
},
{
.key = "repost_id",
.offset = offsetof( OBJ_TYPE, repost.id ),
.required = false,
.type = &json_field_integer,
},
{
.key = "repost_url",
.offset = offsetof( OBJ_TYPE, repost.url ),
.required = false,
.type = &json_field_string,
},
{
.key = "root_status_id",
.offset = offsetof( OBJ_TYPE, root_status.id ),
.required = false,
.type = &json_field_integer,
},
{
.key = "root_status_url",
.offset = offsetof( OBJ_TYPE, root_status.url ),
.required = false,
.type = &json_field_string,
},
JSON_FIELD_INTEGER( reposted_status_id, false ),
JSON_FIELD_ARRAY_OF_STRINGS( media, false ),
@ -137,6 +185,7 @@ struct status* status_from_id( unsigned int id )
if( id == 0 ) { return NULL; }
struct status* s = NULL;
bool needs_save = false;
FILE* f = open_status_data_file( id, "r" );
if( !f ) { return NULL; }
@ -175,18 +224,53 @@ struct status* status_from_id( unsigned int id )
free( s->media.items );
memset( &s->media, 0, sizeof(s->media) );
status_save(s);
needs_save = true;
}
// Fix source field
if( !s->source ) {
s->source = strdup("");
}
// Fix remote field
if( s->account_id == owner_account_id ) {
s->remote =false;
}
// Fix url field
if( !s->remote && !s->url ) {
s->url = aformat( "https://%s/note/%d", g_server->domain, s->id );
}
if( s->account_id == owner_account_id ) {
s->remote =false;
// Fill in reference urls
if( s->in_reply_to.id && !s->in_reply_to.url ) {
struct status* in_reply_to = status_from_id( s->in_reply_to.id );
if( in_reply_to && in_reply_to->url ) {
s->in_reply_to.url = strdup(in_reply_to->url);
needs_save = true;
}
status_free(in_reply_to);
}
if( s->quote.id && !s->quote.url ) {
struct status* quote = status_from_id( s->quote.id );
if( quote && quote->url ) {
s->quote.url = strdup(quote->url);
needs_save = true;
}
status_free(quote);
}
if( s->root_status.id && ( s->root_status.id != s->id ) && !s->root_status.url ) {
struct status* root_status = status_from_id( s->root_status.id );
if( root_status && root_status->url ) {
s->root_status.url = strdup(root_status->url);
needs_save = true;
}
status_free(root_status);
}
// Save changes
if( needs_save ) {
status_save(s);
}
return s;
@ -198,7 +282,8 @@ struct status* status_new_repost( struct status* s, struct account* a )
memset(repost,0,sizeof(*repost));
repost->account_id = a->id;
repost->repost_id = s->id;
repost->repost.id = s->id;
repost->repost.url = strdup(s->url);
repost->published = time(NULL);
status_add_repost( s, repost );
@ -382,6 +467,13 @@ bool status_sync_from_activity_pub( struct status* s, struct ap_object* act )
goto failed;
}
// Update account if it should be
if( time(NULL) >= a->next_update ) {
printf( "* Updating account %s\n", a->account_url );
account_sync_from_activity_pub( a->id, true );
}
// Handle post visibility
bool is_public = false;
for( int i = 0; i < act->to.count; ++i ) {
@ -534,7 +626,8 @@ bool status_sync_from_activity_pub( struct status* s, struct ap_object* act )
if( parent_id ) {
status_make_reply_to( s, parent_id );
s->in_reply_to = parent_id;
s->in_reply_to.id = parent_id;
s->in_reply_to.url = strdup(act->in_reply_to);
}
//printf( "Status %d has been marked as a reply to %d (%s)\n", s->id, parent_id, act->in_reply_to );
}
@ -738,7 +831,10 @@ void status_assign_local_id( struct status* s )
int head = fs_list_inc( "data/statuses/HEAD" );
s->id = head;
s->root_status_id = s->id;
s->root_status.id = s->id;
if( s->url ) {
s->root_status.url = strdup(s->url);
}
}
bool status_save_new( struct status* s )
@ -814,6 +910,11 @@ void status_free( struct status* s )
}
free(s->reacts.items);
free(s->in_reply_to.url );
free(s->quote.url);
free(s->repost.url);
free(s->root_status.url);
free(s->likes.items);
free(s->replies.items);
free(s->reposts.items);
@ -890,13 +991,16 @@ void status_make_reply_to( struct status* s, int in_reply_to_id )
// Add this status to the other
struct status* in_reply_to = status_from_id( in_reply_to_id );
if( !in_reply_to ) {
s->in_reply_to = 0;
s->in_reply_to.id = 0;
return;
}
// Setup this status's reply fields
s->in_reply_to = in_reply_to_id;
s->root_status_id = in_reply_to->root_status_id;
s->in_reply_to.id = in_reply_to_id;
s->root_status.id = in_reply_to->root_status.id;
if( in_reply_to->root_status.url ) {
s->root_status.url = strdup( in_reply_to->root_status.url );
}
// Record in parent as reply
status_add_reply( in_reply_to, s );
@ -917,12 +1021,13 @@ void status_make_quote_of( struct status* s, int id_quote_of )
{
struct status* quoted_post = status_from_id( id_quote_of );
if( !quoted_post ) {
s->quote_id = 0;
s->quote.id = 0;
return;
}
// Set quoted id
s->quote_id = id_quote_of;
s->quote.id = id_quote_of;
s->quote.url = strdup(quoted_post->url);
// Record quote in parent
status_add_quote( quoted_post, s );
@ -953,7 +1058,7 @@ void status_get_context( struct status* s, void* ancestors_ptr, void* replies_pt
memset(replies,0,sizeof(*replies));
struct status* parent = NULL;
for( int i = s->in_reply_to; i != 0; i = parent->in_reply_to ) {
for( int i = s->in_reply_to.id; i != 0; i = parent->in_reply_to.id ) {
parent = status_from_id( i );
if( !parent ) { break; }
array_append( ancestors, sizeof(parent), &parent );
@ -991,8 +1096,8 @@ void status_get_quotes( struct status* s, void* quotes_ptr )
void status_add_react( struct status* s, const char* react, struct account* a )
{
if( s->repost_id ) {
struct status* reposted_status = status_from_id( s->repost_id );
if( s->repost.id ) {
struct status* reposted_status = status_from_id( s->repost.id );
status_add_react(reposted_status,react,a);
status_free(reposted_status);
return;
@ -1050,8 +1155,8 @@ done:
}
void status_remove_react( struct status* s, const char* react, struct account* a )
{
if( s->repost_id ) {
struct status* reposted_status = status_from_id( s->repost_id );
if( s->repost.id ) {
struct status* reposted_status = status_from_id( s->repost.id );
status_remove_react(reposted_status,react,a);
status_free(reposted_status);
return;
@ -1097,8 +1202,8 @@ update_entry:
}
void status_add_like( struct status* s, struct account* a )
{
if( s->repost_id ) {
struct status* reposted_status = status_from_id( s->repost_id );
if( s->repost.id ) {
struct status* reposted_status = status_from_id( s->repost.id );
status_add_like(reposted_status,a);
status_free(reposted_status);
return;
@ -1136,8 +1241,8 @@ void status_add_like( struct status* s, struct account* a )
}
void status_remove_like( struct status* s, struct account* a )
{
if( s->repost_id ) {
struct status* reposted_status = status_from_id( s->repost_id );
if( s->repost.id ) {
struct status* reposted_status = status_from_id( s->repost.id );
status_remove_like(reposted_status,a);
status_free(reposted_status);
return;

@ -17,6 +17,8 @@ enum
status_visibility_direct = 3,
};
#include "model/status/ref.h"
struct status
{
unsigned int id;
@ -36,10 +38,10 @@ struct status
time_t published;
int visibility;
int in_reply_to;
int quote_id;
int repost_id;
int root_status_id;
struct status_ref in_reply_to;
struct status_ref quote;
struct status_ref repost;
struct status_ref root_status;
int reposted_status_id; // if this post was reposted, this will be the id of that status
struct {

@ -0,0 +1,11 @@
#pragma once
struct status_ref
{
int id;
char* url;
};
extern struct json_field_type status_ref_type;
void status_ref_free_composite( struct status_ref* ref );

@ -60,7 +60,7 @@ bool owner_favorited_callback( void* field_data, bool is_read, bool* value )
static bool write_in_reply_to( struct json_writer* jw, const char* field_name, void* field_data, struct json_reflection* layout_field_data, int offset )
{
struct status* s = field_data;
struct status* in_reply_to = status_from_id( s->in_reply_to );
struct status* in_reply_to = status_from_id( s->in_reply_to.id );
if( in_reply_to ) {
json_write_field_name(jw,"in_reply_to_account_id");
fprintf( jw->f, "\"%d\"", in_reply_to->account_id );
@ -320,7 +320,7 @@ bool context_url_callback( void* field_data, bool is_read, char** res )
{
struct status* s = field_data;
if( !is_read ) {
*res = aformat( "https://%s/contexts/%d", g_server->domain, s->root_status_id );
*res = aformat( "https://%s/contexts/%d", g_server->domain, s->root_status.id );
return true;
}
return false;
@ -336,9 +336,9 @@ bool quote_url_callback( void* field_data, bool is_read, char** val )
if( is_read ) { return false; }
struct status* s = field_data;
if( s->quote_id == 0 ) { return false; }
if( s->quote.id == 0 ) { return false; }
struct status* quoted_post = status_from_id(s->quote_id);
struct status* quoted_post = status_from_id(s->quote.id);
if( !quoted_post ) { return false; }
*val = strdup(quoted_post->url );
status_free(quoted_post);
@ -350,7 +350,7 @@ bool quote_visible_callback( void* field_data, bool is_read, bool* val )
struct status* s = field_data;
*val = ( s->quote_id != 0 );
*val = ( s->quote.id != 0 );
return true;
}
@ -380,7 +380,7 @@ static struct json_object_field pleroma_layout[] = {
},
{
.key = "conversation_id",
.offset = offsetof( OBJ_TYPE, root_status_id ),
.offset = offsetof( OBJ_TYPE, root_status.id ),
.type = &json_field_integer,
},
JSON_FIELD_FIXED_NULL( direct_conversation_id ), // Maybe this is breaking things in the home feed?
@ -409,7 +409,7 @@ static struct json_object_field pleroma_layout[] = {
JSON_FIELD_FIXED_NULL( pinned_at ),
{
.key = "quote",
.offset = offsetof( OBJ_TYPE, quote_id ),
.offset = offsetof( OBJ_TYPE, quote.id ),
.type = &Status_reference_type,
},
{
@ -526,7 +526,7 @@ struct json_object_field api_Status_layout[] = {
},
{
.key = "reblog",
.offset = offsetof(OBJ_TYPE, repost_id),
.offset = offsetof(OBJ_TYPE, repost.id),
.type = &Status_reference_type,
},
{

Loading…
Cancel
Save