teknomunk 5 months ago
commit 33ab478771

1
.gitignore vendored

@ -13,3 +13,4 @@ apogee
apogee.debug
/*.log
/*.json
obj/

3
.gitmodules vendored

@ -34,3 +34,6 @@
[submodule "src/util"]
path = src/util
url = https://gitea.polaris-1.work/teknomunk/util.git
[submodule "assets/Soapbox"]
path = assets/soapbox
url = https://gitea.polaris-1.work/teknomunk/Soapbox.git

@ -16,7 +16,41 @@ USE AT YOUR OWN RISK.
* ActivityPub federation - follow interesting people on the many instances of the fediverse.
# Dependencies
## Runtime
* libCURL
* OpenSSL
* Globaly resolvable domain name
* Tor
## Source Install/Development
* C Compiler (test with GCC)
* Ruby (hopefully temporary)
# Source Installation Instructions
Ensure you have system packages for the dependencies installed, then run the following code in
a terminal window:
````
git clone https://gitea.polaris-1.work/teknomunk/apogee.git
cd apogee
./build.sh
./apogee --listen=$PORT
````
If everything worked correctly, you should see something similar to the following:
````
Starting Apogee ActivityPub server...
Using port 21630 for web server
Starting section 6 (tor)
Starting section 0 (webserver)
Starting section 1 (inbox)
Starting section 2 (outbox)
Starting section 5 (fetch)
````
The listening port is chosen at random each time the program is run until setup, then the configured
port is used. Open a browser and navigate to http://${hostnameOrIPAddress}:${PORT}/, then fill in the
required information.

@ -0,0 +1 @@
Subproject commit c667899bd898056810acfe349da59491198fbb1f

@ -2,6 +2,8 @@
set -e
git submodule update --init --recursive
rm debug release 2>/dev/null || true
find src | grep -E "\.template$" | while read FILE; do
ruby tools/builder/tools/embed.rb "$FILE"

@ -0,0 +1,191 @@
#include "controller/admin.h"
// Model
#include "src/model/server.h"
#include "src/model/owner.h"
#include "src/model/account.h"
#include "src/model/crypto/keys.h"
// View
// Controller
#include "src/controller/api/client_apps.h"
// Submodules
#include "form.h"
#include "format.h"
#include "http/server/request.h"
#include "ffdb/fs_list.h"
// Platform Headers
#include <string.h>
#include <stdlib.h>
const char* view_checkbox( bool value )
{
return value ? "checked" : "";
}
bool route_admin_request( struct http_request* req )
{
// TODO: authenticate
if( !check_authentication_header(req) ) {
printf( "User-Agent: %s\n", http_request_get_header(req,"user-agent") );
http_request_send_headers( req, 401, "text/plain", true );
FILE* f = http_request_get_response_body( req );
fprintf( f, "Not authorized to use this endpoint.\n" );
return true;
}
if( http_request_route_term( req, "/server-setup" ) ) {
return handle_admin_server_setup(req);
}
return false;
}
// Special: /, step=1
bool handle_admin_initial_owner_setup( struct http_request* req )
{
if( http_request_route_method( req, "POST" ) ) {
// TODO: handle post
FILE* body = http_request_get_request_data(req);
struct form_parser* fp = form_pull_parser_new(body);
if( !fp ) { goto show_owner_setup; }
struct owner* o = owner_new();
// Create owner account
struct account* owner = account_new();
owner->id = owner_account_id;
owner->server = strdup(g_server->domain);
owner->account_url = aformat("https://%s/owner/actor", g_server->domain );
owner->banner = aformat("https://%s/owner/banner.blob", g_server->domain );
owner->avatar.url = aformat("https://%s/owner/avatar.blob", g_server->domain );
owner->avatar.static_url = aformat("https://%s/owner/avatar.blob", g_server->domain );
owner->note = strdup("");
account_save(owner);
// Create home timeline account
{
struct account* home = account_new();
home->id = home_timeline_id;
home->handle = strdup("%home-timeline");
home->server = strdup("localhost");
account_save(home);
account_free(home);
}
// Create public timeline account
{
struct account* public = account_new();
public->id = public_timeline_id;
public->handle = strdup("%public-timeline");
public->server = strdup("localhost");
account_save(public);
account_free(public);
}
fs_list_set( "data/accounts/HEAD", 3 );
// Create RSA public/private keys
struct crypto_keys* keys = crypto_keys_new();
crypt_keys_generate(keys);
crypto_keys_save_public(keys,"data/owner/public.pem");
crypto_keys_save_private(keys,"data/owner/private.pem");
crypto_keys_free(keys);
bool success = false;
char* password = NULL;
char* confirm = NULL;
char* key = NULL;
while( (key=form_pull_parser_read_key(fp)) ) {
if( 0 == strcmp(key,"password") ) {
password = strdup(form_pull_parser_read_value(fp));
} else if( 0 == strcmp(key,"confirm") ) {
confirm = strdup(form_pull_parser_read_value(fp));
} else if( 0 == strcmp(key,"handle") ) {
owner->handle = strdup(form_pull_parser_read_value(fp));
owner->display_name = strdup(owner->handle);
account_save(owner);
}
}
if( owner->handle && *owner->handle && password && confirm && ( 0 == strcmp(password,confirm) ) ) {
owner_set_password( o, password );
success = true;
}
form_pull_parser_release(fp);
if( success ) {
owner_save(o);
}
owner_free(o);
account_free(owner);
if( success ) {
// TODO: generate crypto keys
// Advance wizard to next step
g_server->configured = true;
app_args_save();
http_request_begin_send_headers( req, 302, false );
http_request_send_header( req, "Location", "/?complete" );
http_request_end_send_headers( req, false );
return true;
}
}
show_owner_setup:
http_request_send_headers( req, 200, "text/html", true );
FILE* f = http_request_get_response_body( req );
#include "view/admin/owner-setup.html.inc"
return true;
}
// Route: /admin/server-setup
// Special: /, step=0 (when server hasn't been configured)
bool handle_admin_server_setup( struct http_request* req )
{
if( http_request_route_method( req, "POST" ) ) {
// TODO: handle post
FILE* body = http_request_get_request_data(req);
struct form_parser* fp = form_pull_parser_new(body);
if( !fp ) { return false; }
app_args_load_from_form( g_server, fp );
form_pull_parser_release(fp);
// Advance wizard to next step
if( g_server->setup_wizard_step == 0 ) {
g_server->setup_wizard_step = 1;
}
app_args_save();
app_args_load();
// Redirect
http_request_begin_send_headers( req, 302, false );
http_request_send_header( req, "Location", "/?account" );
http_request_end_send_headers( req, false );
return true;
} else {
http_request_send_headers( req, 200, "text/html", true );
FILE* f = http_request_get_response_body( req );
#include "view/admin/server-setup.html.inc"
return true;
}
}
bool handle_admin_server_setup_wizard( struct http_request* req )
{
switch(g_server->setup_wizard_step) {
case 0: return handle_admin_server_setup(req);
case 1: return handle_admin_initial_owner_setup(req);
}
return false;
}

@ -0,0 +1,10 @@
#pragma once
#include <stdbool.h>
struct http_request;
bool route_admin_request( struct http_request* req );
bool handle_admin_server_setup( struct http_request* req );
bool handle_admin_server_setup_wizard( struct http_request* req );

@ -92,6 +92,8 @@ bool check_bearer_token( const char* auth_token )
client_app_free(app);
return false;
}
app->last_used = time(NULL);
client_app_save(app);
client_app_free(app);
return true;

@ -27,6 +27,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
extern bool terminate;
@ -697,6 +698,8 @@ bool cleanup_box( const char* box )
void process_inbox()
{
mkdir( "data/inbox", 0750 );
while( !terminate ) {
bool activity = false;
activity |= process_one();

@ -17,6 +17,7 @@
#include "controller/inbox.h"
#include "controller/activity_pub.h"
#include "controller/api/emoji.h"
#include "controller/admin.h"
// Standard Library
#include <string.h>
@ -169,6 +170,9 @@ bool route_request( struct http_request* req )
char onion_location[512];
snprintf( onion_location,512, "http://%s%s", g_server->tor_hidden_service, http_request_get_full_path(req) );
http_request_add_header( req, "Onion-Location", onion_location );
#define SOURCE_URL "https://gitea.polaris-1.work/teknomunk/apogee"
#define LICENSE_URL "https://www.gnu.org/licenses/agpl-3.0.html"
http_request_add_header( req, "Link", "<" LICENSE_URL ">; rel=\"license\", <" SOURCE_URL ">; rel=\"source\"" );
bool inner( struct http_request* req ) {
if( http_request_route( req, "/oauth" ) ) {
@ -196,6 +200,13 @@ bool route_request( struct http_request* req )
}
} else if( http_request_route( req, "/nodeinfo" ) ) {
return route_nodeinfo(req);
} else if( http_request_route_term( req, "/instance/soapbox.json" ) ) {
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
#include "view/soapbox.json.inc"
return true;
} else {
return route_asset(req);
}
@ -203,16 +214,26 @@ bool route_request( struct http_request* req )
return false;
}
if( http_request_route( req, "/api/v1/" ) ) {
return route_mastodon_api( req );
} else if( http_request_route( req, "/api/pleroma" ) ) {
return route_pleroma_api2( req );
} else if( inner( req ) ) {
return true;
if( g_server->configured ) {
if( http_request_route( req, "/api/v1/" ) ) {
return route_mastodon_api( req );
} else if( http_request_route( req, "/api/pleroma" ) ) {
return route_pleroma_api2( req );
} else if( http_request_route( req, "/admin" ) ) {
return route_admin_request( req );
} else if( inner( req ) ) {
return true;
} else {
return send_asset( req, "assets/soapbox/index.html" );
}
return false;
} else {
return send_asset( req, "assets/soapbox/index.html" );
}
if( http_request_route_term( req, "/" ) || http_request_route( req, "/?") ) {
return handle_admin_server_setup_wizard( req );
}
return false;
return false;
}
}

@ -27,6 +27,7 @@
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
static bool blacklisted( struct outbox_envelope* env )
{
@ -366,6 +367,8 @@ bool cleanup_box( const char* box );
extern bool terminate;
void process_outbox()
{
mkdir( "data/outbox", 0750 );
while( !terminate ) {
bool activity = false;
activity |= process_pending();

@ -1 +1 @@
Subproject commit c57a8bb19fedfb717ac0ae498b2e121f96d3507f
Subproject commit b4edcf9a332b0bc59b9f526889cd3059c77aaa5f

@ -13,6 +13,7 @@
#include <curl/curl.h>
#include <stdlib.h>
#include <time.h>
bool terminate = false;
@ -23,6 +24,7 @@ void handle_ctrl_c(int)
int main( int argc, char* argv[], char* envp[] )
{
srand( time(NULL) );
curl_global_init(CURL_GLOBAL_DEFAULT);
model_init();
@ -36,6 +38,7 @@ int main( int argc, char* argv[], char* envp[] )
int code = 0;
if( g_server->section == -1 ) {
printf( "Starting Apogee ActivityPub server...\n" );
printf( "Using port %d for web server\n", g_server->http_settings.bind_port );
process_start_section(6);
// Make sure we have a hidden service hostname

@ -2,8 +2,25 @@
#include "model/status.h"
#include "model/peer.h"
#include <sys/stat.h>
void model_init()
{
mkdir( "data", 0750 );
mkdir( "data/accounts", 0750 );
mkdir( "data/activities", 0750 );
mkdir( "data/bookmarks", 0750 );
mkdir( "data/cache", 0750 );
mkdir( "data/client_apps", 0750 );
mkdir( "data/config", 0750 );
mkdir( "data/crypto", 0750 );
mkdir( "data/emoji", 0750 );
mkdir( "data/indempotency", 0750 );
mkdir( "data/media", 0750 );
mkdir( "data/notices", 0750 );
mkdir( "data/owner", 0750 );
mkdir( "data/webfinger", 0750 );
status_model_init();
peer_model_init();
}

@ -236,12 +236,17 @@ static struct account* account_load_from_id( int id, int recurse_limit )
return NULL;
}
// Handle account migration
if( a->replaced_by && a->replaced_by != a->id ) {
int new_id = a->replaced_by;
account_free(a);
return account_load_from_id( new_id, recurse_limit - 1 );
}
// Sanity checks
if( !a->note ) {
a->note = strdup("");
}
if( !a->banner ) {
a->banner = aformat( "https://%s/server/default-banner.blob", g_server->domain );
}

@ -18,6 +18,7 @@ static struct json_object_field client_app_layout[] = {
JSON_FIELD_STRING( auth_code, false ),
JSON_FIELD_STRING( access_token, false ),
JSON_FIELD_STRING( redirect_uri, false ),
JSON_FIELD_DATETIME( last_used, false ),
{
.key = "secret",
.offset = offsetof( struct client_app, client.secret ),
@ -69,6 +70,7 @@ struct client_app* client_app_new( const char* client_name )
app->auth_code = NULL;
app->redirect_uri = NULL;
app->access_token = NULL;
app->last_used = time(NULL);
client_app_save(app);
return app;

@ -1,5 +1,7 @@
#pragma once
#include <time.h>
struct client_app
{
struct {
@ -11,6 +13,7 @@ struct client_app
char* redirect_uri;
char* auth_code;
char* access_token;
time_t last_used;
// auth_expires
};

@ -66,6 +66,41 @@ bool crypto_keys_load_public( struct crypto_keys* keys, const char* filename )
return !!keys->pubkey;
}
void crypt_keys_generate( struct crypto_keys* keys )
{
if( keys->privkey ) {
EVP_PKEY_free( keys->privkey );
keys->privkey = NULL;
}
if( keys->pubkey ) {
EVP_PKEY_free( keys->pubkey );
keys->pubkey = NULL;
}
EVP_PKEY_CTX* pkey_context = EVP_PKEY_CTX_new_id( EVP_PKEY_RSA, NULL );
EVP_PKEY_keygen_init( pkey_context );
EVP_PKEY_CTX_set_rsa_keygen_bits( pkey_context, 2048 );
EVP_PKEY_keygen( pkey_context, &keys->privkey );
keys->pubkey = keys->privkey;
EVP_PKEY_up_ref(keys->pubkey);
}
void crypto_keys_save_public( struct crypto_keys* keys, const char* filename )
{
FILE* f = fopen( filename, "w");
if( !f ) { return; }
PEM_write_PUBKEY( f, keys->pubkey );
fclose(f);
}
void crypto_keys_save_private( struct crypto_keys* keys, const char* filename )
{
FILE* f = fopen( filename, "w");
if( !f ) { return; }
PEM_write_PrivateKey( f, keys->privkey, NULL, NULL, 0, NULL, NULL );
fclose(f);
}
char* crypto_keys_sign( struct crypto_keys* keys, void* data, unsigned int size )
{

@ -10,6 +10,9 @@ void crypto_keys_free( struct crypto_keys* keys );
bool crypto_keys_load_public ( struct crypto_keys* keys, const char* filename );
bool crypto_keys_load_private( struct crypto_keys* keys, const char* filename );
void crypt_keys_generate( struct crypto_keys* keys );
void crypto_keys_save_public( struct crypto_keys* keys, const char* filename );
void crypto_keys_save_private( struct crypto_keys* keys, const char* filename );
char* crypto_keys_sign( struct crypto_keys* keys, void* data, unsigned int size );
bool crypto_keys_verify( struct crypto_keys* keys, void* data, unsigned int size, char* signature );

@ -3,6 +3,7 @@
#include "json/json.h"
#include "json/layout.h"
#include "sha256/sha256.h"
#include "form.h"
#include <stdlib.h>
#include <stdio.h>
@ -32,10 +33,7 @@ struct owner* owner_new()
{
struct owner* o = allocate(sizeof(struct owner));
if( !json_read_object_layout_from_file( "data/owner.json", owner_layout, o ) ) {
owner_free(o);
return NULL;
}
json_read_object_layout_from_file( "data/owner.json", owner_layout, o );
return o;
}
@ -46,15 +44,24 @@ void owner_free( struct owner* o )
free(o->password_hash);
free(o);
}
void owner_save( struct owner* o )
{
json_write_object_layout_to_file( "data/owner.json", "\t", owner_layout, o );
}
bool owner_check_password( struct owner* o, const char* passwd )
{
char buffer[512];
snprintf( buffer, 512, "%s:%s", o->password_salt, passwd );
printf( "passwd='%s'\n", passwd );
char hash[65] = "";
sha256_easy_hash_hex( buffer, strlen(buffer), hash );
printf( "hash=%s\n", hash );
printf( "expt=%s\n", o->password_hash );
return 0 == strcmp(hash,o->password_hash);
}
@ -64,6 +71,7 @@ void owner_set_password( struct owner* o, const char* passwd )
free(o->password_hash);
char* new_salt = o->password_salt = malloc(65);
memset(new_salt,0,65);
for( int i = 0; i < 64; ++i ) {
new_salt[i] = 'a' + (rand()%26);
}
@ -73,6 +81,10 @@ void owner_set_password( struct owner* o, const char* passwd )
snprintf( buffer, 512, "%s:%s", new_salt, passwd );
char* new_hash = o->password_hash = malloc(65);
memset(new_hash,0,65);
sha256_easy_hash_hex( buffer, strlen(buffer), new_hash );
o->password_hash = new_hash;
o->password_salt = new_salt;
}

@ -15,4 +15,5 @@ void owner_free( struct owner* );
bool owner_check_password( struct owner* o, const char* passwd );
void owner_set_password( struct owner* o, const char* passwd );
struct form_pull_parser;

@ -1,17 +1,20 @@
#include "server.h"
#include "json/layout.h"
#include "controller/cli.h"
#include "process.h"
// Submodules
#include "json/layout.h"
#include "form.h"
// Platform libraries
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define OBJ_TYPE struct app_args
static struct json_object_field app_args_layout[] = {
JSON_FIELD_STRING( domain, true ),
JSON_FIELD_STRING( icann_domain, false ),
JSON_FIELD_STRING( user_agent, false ),
{
.key = "addr",
@ -29,6 +32,10 @@ static struct json_object_field app_args_layout[] = {
JSON_FIELD_INTEGER( outbox_discard_limit, false ),
JSON_FIELD_BOOL( develop, false ),
JSON_FIELD_BOOL( disabled, false ),
JSON_FIELD_BOOL( configured, false ),
JSON_FIELD_INTEGER( setup_wizard_step, false ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
@ -42,6 +49,9 @@ void app_args_refresh_tor_hidden_service()
g_server->tor_hidden_service = NULL;
FILE* f = fopen("data/tor/hidden_service/hostname","r");
if( !f ) {
return;
}
size_t n;
getline( &g_server->tor_hidden_service, &n, f );
n = strlen( g_server->tor_hidden_service );
@ -50,28 +60,42 @@ void app_args_refresh_tor_hidden_service()
}
fclose(f);
}
struct app_args* app_args_new( int argc, char** argv )
void app_args_load()
{
struct app_args* args = (struct app_args*)malloc( sizeof(struct app_args) );
memset(args,0,sizeof(*args));
if( !args ) { return NULL; }
g_server->http_settings.bind_port = 9000 + ( rand() % 41000 );
g_server->http_settings.bind_address = strdup("0.0.0.0");
g_server->tor_socks_port = ( rand() % 41000 ) + 9000;
g_server->section = -1;
g_server->outbox_discard_limit = 5;
g_server->user_agent = strdup( "Apogee/0.1" );
g_server->disabled = true;
g_server = args;
json_read_object_layout_from_file( "data/server.json", app_args_layout, g_server );
args->http_settings.bind_port = 9053;
args->http_settings.bind_address = strdup("0.0.0.0");
args->tor_socks_port = 9123;
args->section = -1;
args->outbox_discard_limit = 5;
args->user_agent = strdup( "Apogee/0.1" );
app_args_refresh_tor_hidden_service();
json_read_object_layout_from_file( "data/server.json", app_args_layout, args );
if( g_server->icann_domain ) {
g_server->domain = strdup(g_server->icann_domain);
} else if( g_server->tor_hidden_service ) {
g_server->domain = strdup(g_server->tor_hidden_service);
}
app_args_refresh_tor_hidden_service();
printf( "Using domain: %s\n", g_server->domain );
}
struct app_args* app_args_new( int argc, char** argv )
{
if( !g_server ) {
struct app_args* args = (struct app_args*)malloc( sizeof(struct app_args) );
memset(args,0,sizeof(*args));
if( !args ) { return NULL; }
g_server = args;
}
app_args_load();
if( ( argc > 1 ) && ( 0 != strncmp(argv[1],"--",2) ) ) {
handle_command( argv, argc );
free(args);
return NULL;
}
@ -79,16 +103,22 @@ struct app_args* app_args_new( int argc, char** argv )
const char* arg = argv[i];
// Debug flag
if( 0 == strcmp(argv[i],"--debug") ) { args->debug = true; goto next_arg; }
if( 0 == strcmp(argv[i],"--debug") ) { g_server->debug = true; goto next_arg; }
// Sections by number
if( sscanf(arg,"--section=%d",&args->section) ) { goto next_arg; }
if( sscanf(arg,"--section=%d",&g_server->section) ) { goto next_arg; }
// Override server port
if( sscanf(arg,"--listen=%d",&g_server->http_settings.bind_port) ) {
printf( "Listening on port %d\n", g_server->http_settings.bind_port );
goto next_arg;
}
// Sections by name
if( ( argv[i][0] == '-' ) && ( argv[i][1] == '-' ) ) {
for( int i = 0; i <= process_get_max_section(); ++i ) {
if( strcmp( &argv[i][2], process_get_section_name(i) ) ) {
args->section = i;
g_server->section = i;
goto next_arg;
}
}
@ -96,27 +126,74 @@ struct app_args* app_args_new( int argc, char** argv )
// Development
if( 0 == strcmp(argv[i],"--devel") ) {
args->section = 100;
g_server->section = 100;
goto next_arg;
}
// Unknown argument
printf( "Unknown argument: %s\n", argv[i] );
free(args);
return NULL;
next_arg:;
}
return args;
return g_server;
}
void app_args_release( struct app_args* args )
{
free(args->http_settings.bind_address);
free(args->domain);
free(args->icann_domain);
free(args->tor_hidden_service);
free(args->user_agent);
free(args);
}
void app_args_save()
{
// Save to disk
json_write_object_layout_to_file( "data/server.json", "\t", app_args_layout, g_server );
}
void app_args_load_from_form( struct app_args* args, struct form_parser* fp )
{
char* key = NULL;
while( (key=form_pull_parser_read_key(fp)) != NULL ) {
for( struct json_object_field* jof = app_args_layout; jof->key; jof += 1 ) {
intptr_t field_offset = (intptr_t)args + jof->offset;
if( strcmp(jof->key, key) == 0 ) {
if( jof->type == &json_field_string ) {
const char* value = form_pull_parser_read_value(fp);
char** field = (char**)field_offset;
printf( "%s: %s\n", jof->key, value );
free( *field );
*field = NULL;
if( value ) {
*field = strdup(value);
}
} else if( jof->type == &json_field_integer ) {
const char* value = form_pull_parser_read_value(fp);
printf( "%s: %s\n", jof->key, value );
int* field = (int*)field_offset;
sscanf( value, "%d", field );
} else if( jof->type == &json_field_bool ) {
const char* value = form_pull_parser_read_value(fp);
printf( "%s: %s\n", jof->key, value );
bool* field = (bool*)field_offset;
if( strcmp(value,"on") == 0 ) {
*field = true;
} else {
*field = false;
}
} else {
printf( "Unknown type for field %s\n", jof->key );
}
}
}
}
}

@ -11,6 +11,7 @@ struct app_args
struct http_server_args http_settings;
//char* addr;
char* domain;
char* icann_domain;
char* tor_hidden_service;
char* user_agent;
bool debug;
@ -21,9 +22,17 @@ struct app_args
int outbox_discard_limit;
bool develop;
bool disabled;
bool configured;
int setup_wizard_step;
};
struct app_args* app_args_new( int argc, char** argv );
void app_args_load();
void app_args_release( struct app_args* args );
void app_args_save();
struct form_parser;
void app_args_load_from_form( struct app_args* args, struct form_parser* fp );
extern struct app_args* g_server;

@ -130,6 +130,8 @@ static void redirect_io( int section )
const char* section_name_str = process_get_section_name(section);
if( !section_name_str ) { return; }
mkdir( "data/logs", 0750 );
char filename[512];
snprintf( filename,512, "data/logs/%s.log", section_name_str );

@ -1 +1 @@
Subproject commit e133427bb7da0224fc365252406024c207418863
Subproject commit 74376d45529aa0b5e845cddd00005ee2aedcc66f

@ -0,0 +1,34 @@
<html>%(/*
vim: filetype=html
*/)
<body>
<h1>Welcome to Apogee</h1>
<p>
Apogee is a single-user Activity Pub federated server intended for self-hosting.
</p>
<h2>Account Settings</h2>
<form method="POST">
<table width='100%%'>
<tr>
<td width="15%%"><b>Username:</b></td>
<td><input name="handle" type="text" /></td>
<td></td>
</tr>
<tr>
<td><b>Password:</b></td>
<td><input name="password" type="password" /></td>
<td></td>
</tr>
<tr>
<td><b>Confirm Password:</b></td>
<td><input name="confirm" type="password" /></td>
<td></td>
</tr>
</table>
<br/>
<input type="submit" />
</form>
</body>
</html>

@ -0,0 +1,77 @@
<html>%(/*
vim: filetype=html
*/)
<body>
<h1>Welcome to Apogee</h1>
<p>
Apogee is a single-user Activity Pub federated server intended for self-hosting.
</p>
<h2>Server Settings</h2>
<form method="POST">
<h3>HTTP</h3>
<table width='100%%'>
<tr>
<td width="15%%"><b>ICANN Domain Name:</b></td>
<td><input name="icann_domain" type="text" value="%s{ g_server->icann_domain ? g_server->icann_domain : "" }"/></td>
<td></td>
</tr>
<tr>
<td><b>Listen Address:</b></td>
<td><input name="address" type="text" value="%s{ g_server->http_settings.bind_address ? g_server->http_settings.bind_address : "0.0.0.0" }"/></td>
<td></td>
</tr>
<tr>
<td><b>Listen Port:</b></td>
<td><input name="port" type="text" value="%d{ g_server->http_settings.bind_port }" /></td>
<td></td>
</tr>
<tr>
<td><b>User Agent:</b></td>
<td><input name="useragent" type="text" value="%s{ g_server->user_agent }" /></td>
<td></td>
</tr>
</table>
<h3>Tor</h3>
<table width='100%%'>
<tr>
<td width="15%%"><b>Disable:<b></td>
<td><input name="disable_tor" type="checkbox" %s{ view_checkbox(g_server->disable_tor) }/></td>
<td></td>
</tr>
<tr>
<td><b>SOCKS Port:<b></td>
<td><input name="socks_port" type="text" value="%d{ g_server->tor_socks_port }" /></td>
<td></td>
</tr>
</table>
<h3>Other</h3>
<table width='100%%'>
<tr>
<td width="15%%"><b>Outbox Discard Limit:</b></td>
<td><input name="outbox_discard_limit" type="text" value="%d{ g_server->outbox_discard_limit }" /></td>
<td></td>
</tr>
<tr>
<td><b>Debug Mode:</b></td>
<td><input name="debug" type="checkbox" %s{ view_checkbox(g_server->debug) }/></td>
<td>Greatly increases server verbosity</td>
</tr>
<tr>
<td><b>Developer Mode:</b></td>
<td><input name="develop" type="checkbox" %s{ view_checkbox(g_server->develop) }/></td>
<td>Change server behavior to suit developers</td>
</tr>
<tr>
<td><b>Server Disabled:</b></td>
<td><input name="disabled" type="checkbox" %s{ view_checkbox( g_server->disabled ) }/></td>
<td>Turn the entire server off</td>
</tr>
</table>
<br/>
<input type="submit" />
</form>
</body>
</html>

@ -0,0 +1,33 @@
{
"logo":"/instance/images/logo.png",
"brandColor": "#0482d8",
"promoPanel":{
"items":[
{
"icon": "area-chart",
"text": "Site stats",
"url": "https://fediverse.network/%s{ g_server->domain }"
},
{
"icon": "admin",
"text": "Server Settings",
"url": "/admin/server-settings"
}
]
},
"defaultSettings": {
"autoPlayGif": false,
"themeMode": "light"
},
"copyright": "♡2020. Copying is an act of love. Please copy and share.",
"navlinks": {
"homeFooter":[
{ "title": "About", "url": "/about" },
{ "title": "Terms of Service", "url": "/about/tos" },
{ "title": "Privacy Policy", "url": "/about/privacy" },
{ "title": "DMCA", "url": "/about/dmca" },
{ "title": "Source Code", "url": "/about#opensource" }
]
},
"allowedEmoji": ["🤔", "😂", "😭", "😡", "🥰", "👍🏼", "👎🏼", "🔥", "😩", "🍆"]
}
Loading…
Cancel
Save