Compare commits

..

No commits in common. "cce480bad7bb8f5947ad4b0cb549859ff632c456" and "f1669d6e83f0d8be9e6ef829e135c348fdc9654c" have entirely different histories.

7 changed files with 40 additions and 123 deletions

4
.gitignore vendored
View File

@ -2,7 +2,3 @@ etc/keys/*
etc/nginx.conf
public/config.php
# Vim
~*
.*.sw*

18
bin/addstatus.php Normal file
View File

@ -0,0 +1,18 @@
<?php
require_once('./public/config.php');
$uuid = shell_exec('uuidgen');
// Have the user write the content to a temporary file
$temp_file_name = sys_get_temp_dir() . '/' . $uuid;
shell_exec("vim $temp_file_name");
$content = file_get_contents($temp_file_name);
// Clean up
unlink($temp_file_name);
$object_id = 'https://' . HOSTAS_DOMAIN . "/api/v1/object/$uuid";
$preferred_username = readline('Author username: ');

View File

@ -40,12 +40,6 @@ CREATE TABLE IF NOT EXISTS activity (
instrument TEXT, -- Object with which the activity was performed
FOREIGN KEY (objectId) REFERENCES object(id),
FOREIGN KEY (actor) REFERENCES object(id),
FOREIGN KEY (object) REFERENCES object(id),
FOREIGN KEY (origin) REFERENCES object(id),
FOREIGN KEY (target) REFERENCES object(id),
FOREIGN KEY (result) REFERENCES object(id),
FOREIGN KEY (instrument) REFERENCES object(id),
PRIMARY KEY (objectId)
);

View File

@ -10,5 +10,3 @@ define('HOSTAS_UNIX_USER', 'www-data');
define('HOSTAS_ACCESS_LIST', array(
'example.org' => HOSTAS_ACCESS_LEVEL->trusted
));
define('HOSTAS_MAX_POSTS_PER_PAGE', 50);

View File

@ -47,57 +47,33 @@ function get_actor(string $preferred_username) {
die();
}
/** /api/v1/actor/:preferred_username/outbox?before=:before_date_iso&limit=:limit
* Returns an OrderedCollectionPage of at most `limit` pages published before
* `before_date_iso`.
*
* Parameters:
* before ISO timestamp string. Only posts published before this time are included
* limit Integer. Number of posts to include in the result. Default: 20. Max: HOSTAS_MAX_POSTS_PER_PAGE
*/
function get_actor_outbox_paged(string $preferred_username) {
function get_actor_outbox(string $preferred_username) {
header('Content-Type: application/activity+json');
$before = array_key_exists('before', $_GET) ? $_GET['before'] : date(DATE_ISO8601, time());
$after = array_key_exists('after', $_GET) ? $_GET['after'] : date(DATE_ISO8601, 0);
$limit = array_key_exists('limit', $_GET) ? $_GET['limit'] : 20;
// Parameters in $_GET with a + character will get replaced with whitespace,
// so we've got to change it back
$before = str_replace(" ", "+", $before);
$after = str_replace(" ", "+", $after);
// Cap the limit at the configured maximum
if ($limit > HOSTAS_MAX_POSTS_PER_PAGE) {
$limit = HOSTAS_MAX_POSTS_PER_PAGE;
}
$conn = new SQLite3(HOSTAS_DATABASE_PATH);
$actor = get_actor_or_exit($conn, $preferred_username);
$sql_query = <<<'EOT'
select object.id as object_id, object.type as object_type,
$create_activities_result = prepare_and_execute($conn,
"select object.id as object_id, object.type as object_type,
activity.actor as activity_actor, post.id as post_id,
post.published as post_published, post.type as post_type,
post.content as post_content, post.published as post_published
post.content as post_content
from object
join activity on activity.objectId = object.id
join object as post on activity.object = post.id
where
activity_actor = :actor_id
and post_published < :before
and post_published > :after
order by object.published desc
limit :limit
EOT;
$create_activities_result = prepare_and_execute($conn, $sql_query,
array(
':actor_id' => $actor['id'],
':before' => strtotime($before),
':after' => strtotime($after),
':limit' => $limit
)
where activity.actor = :actor_id
order by object.published",
/*
"select object.id as object_id, object.type as object_type,
post.id as post_id, post.type as post.type, post.published as post_published,
post.url as post_url, post.content as post_content
from object
join activity on activity.objectId = object.id
join object as post on activity.object = post.id
where activity.actor = :actor_id
order by object.published",
*/
array(':actor_id' => $actor['id'])
);
$total_items = 0;
@ -123,64 +99,12 @@ function get_actor_outbox_paged(string $preferred_username) {
));
}
$clean_uri = 'https://' . HOSTAS_DOMAIN . strtok($_SERVER['REQUEST_URI'], '?');
$response = array(
'@context' => HOSTAS_CONTEXT,
'id' => 'https://' . HOSTAS_DOMAIN . $_SERVER['REQUEST_URI'],
'type' => 'OrderedCollectionPage',
'partOf' => 'https://' . HOSTAS_DOMAIN . $clean_uri,
'orderedItems' => $ordered_items,
);
if ($total_items === 0) {
// There may be previous items, so we can reuse the before param. We
// need to do some pre-processing to avoid characters getting escaped
// from the URL
$latest_time = str_replace(" ", "+", $before);
$response['prev'] = $clean_uri . "?after=$latest_time";
} else {
$earliest_time = $ordered_items[array_key_last($ordered_items)]['published'];
$latest_time = $ordered_items[0]['published'];
$response['prev'] = $clean_uri . "?after=$latest_time";
$response['next'] = $clean_uri . "?before=$earliest_time";
}
echo json_encode($response);
die();
}
function get_actor_outbox(string $preferred_username) {
header('Content-Type: application/activity+json');
$conn = new SQLite3(HOSTAS_DATABASE_PATH);
$actor = get_actor_or_exit($conn, $preferred_username);
$sql_query = <<<'EOT'
select count(post.id) as post_count, min(post.published)
from object
join activity on activity.objectId = object.id
join object as post on activity.object = post.id
where activity.actor = :actor_id
EOT;
$items_count_result = prepare_and_execute($conn, $sql_query,
array(':actor_id' => $actor['id'])
);
$items_count_row = $items_count_result->fetchArray(SQLITE3_ASSOC);
$total_items = $items_count_row['post_count'];
$collection_id = "https://" . HOSTAS_DOMAIN . "/api/v1/actor/$preferred_username/outbox";
echo json_encode(array(
'@context' => HOSTAS_CONTEXT,
'id' => $collection_id,
'id' => "https://" . HOSTAS_DOMAIN . "/api/v1/actor/$preferred_username/outbox",
'type' => 'OrderedCollection',
'totalItems' => $total_items,
'first' => $collection_id . '?before=' . date(DATE_ISO8601, time()),
'last' => $collection_id . '?after=' . date(DATE_ISO8601, 0),
'orderedItems' => $ordered_items,
));
die();
@ -194,15 +118,8 @@ switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
if (sizeof(REQUEST_PATH) === 3) {
if (REQUEST_PATH[2] === 'inbox') get_actor_inbox(REQUEST_PATH[1]);
else if (REQUEST_PATH[2] === 'outbox') {
if (array_key_exists('before', $_GET) || array_key_exists('after', $_GET)) {
get_actor_outbox_paged(REQUEST_PATH[1]);
} else {
get_actor_outbox(REQUEST_PATH[1]);
}
}
else if (REQUEST_PATH[2] === 'outbox') get_actor_outbox(REQUEST_PATH[1]);
}
get_actor(REQUEST_PATH[1]);
break;
default:

View File

@ -19,8 +19,5 @@ function sql_fetch_actor($conn, string $preferred_username) {
array(':object_id' => $object_id)
);
// fetchArray returns false when there's no array; we're rather have it be
// explicitly null
$actor = $result->fetchArray(SQLITE3_ASSOC);
return $actor !== false ? $actor : null;
return $result->fetchArray(SQLITE3_ASSOC);
}

View File

@ -1,12 +1,9 @@
<?php
require_once($_SERVER['DOCUMENT_ROOT'] . '/config.php');
// $clean_uri is the request URI with any query strings removed
$clean_uri = strtok($_SERVER['REQUEST_URI'], '?');
// e.g. /api/v1/test/request/ -> ['', 'api', 'v1', 'test', 'request'].
// For convenience later, we take a slice that excludes ['', 'api', 'v1'].
define('REQUEST_PATH', array_slice(explode('/', $clean_uri), offset: 3));
define('REQUEST_PATH', array_slice(explode('/', $_SERVER['REQUEST_URI']), offset: 3));
switch (REQUEST_PATH[0]) {
case 'actor': // /api/v1/actor