|
|
|
|
@ -2,26 +2,30 @@ package MJB::Web::Controller::Admin; |
|
|
|
|
use Mojo::Base 'Mojolicious::Controller', -signatures; |
|
|
|
|
use Try::Tiny; |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# GET /admin | show_admin |
|
|
|
|
# |
|
|
|
|
# Redirect to the people listing page. |
|
|
|
|
#== |
|
|
|
|
sub index ( $c ) { |
|
|
|
|
$c->redirect_to( 'show_admin_people' ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
# POST /admin |
|
|
|
|
#== |
|
|
|
|
# POST /admin | do_admin_become |
|
|
|
|
# uid | A user id, that the admin would like to become |
|
|
|
|
# bid | A blog id, that the admin will go to the manage page for |
|
|
|
|
# url | A URL to return to when the admin logs out of the user account |
|
|
|
|
# |
|
|
|
|
# An admin may inpersonate any other user for technical support purposes, |
|
|
|
|
# this code is called to become another user. Sign out to become your origional |
|
|
|
|
# user again. |
|
|
|
|
# |
|
|
|
|
# INPUT: |
|
|
|
|
# uid | A user id |
|
|
|
|
# bid | A blog id belonging to the user |
|
|
|
|
# url | A URL to return to when the admin logs out of the user account |
|
|
|
|
# |
|
|
|
|
# When given a uid, become that user and go to the user's dashboard. |
|
|
|
|
# |
|
|
|
|
# When given a uid and a bid that the user owns, become that user |
|
|
|
|
# and go to the blog's dashboard. |
|
|
|
|
# |
|
|
|
|
#== |
|
|
|
|
sub do_admin_become ( $c ) { |
|
|
|
|
my ( $uid, $bid, $url ) = ( $c->param('uid'), $c->param('bid'), $c->param('url') ); |
|
|
|
|
|
|
|
|
|
@ -38,15 +42,31 @@ sub do_admin_become ( $c ) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# GET /admin/people | show_admin_people |
|
|
|
|
# |
|
|
|
|
# This route shows people, users, who exist on this system. |
|
|
|
|
#== |
|
|
|
|
sub people ( $c ) { |
|
|
|
|
my $people = $c->stash->{people} = [ $c->db->people->all ]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# GET /admin/person/:id | show_admin_person, { id => ? } |
|
|
|
|
# |
|
|
|
|
# This route shows a given person, their blogs, and notes about them. |
|
|
|
|
#== |
|
|
|
|
sub person ( $c ) { |
|
|
|
|
my $profile = $c->stash->{profile} = $c->db->person( $c->param('id') ); |
|
|
|
|
my $notes = $c->stash->{notes} = [ $profile->person_note_people->all ]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/person/:id/note | do_admin_person_note { id => person.id } |
|
|
|
|
# content | The content of the message about this user. |
|
|
|
|
# |
|
|
|
|
# This route makes a note about a person on their profile page. |
|
|
|
|
#== |
|
|
|
|
sub do_person_note ( $c ) { |
|
|
|
|
my $profile = $c->db->person( $c->param('id') ); |
|
|
|
|
my $content = $c->param('content'); |
|
|
|
|
@ -58,24 +78,35 @@ sub do_person_note ( $c ) { |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Added note to this account." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_person', { id => $profile->id } ) ); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# GET /admin/blogs | show_admin_blogs |
|
|
|
|
# |
|
|
|
|
# This route lists blogs that are hosted on this system. |
|
|
|
|
#== |
|
|
|
|
sub blogs ( $c ) { |
|
|
|
|
my $blogs = $c->stash->{blogs} = [ $c->db->blogs->all ]; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sub servers ( $c ) { |
|
|
|
|
my $servers = $c->stash->{servers} = [ $c->db->servers->all ]; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# GET /admin/invites | show_admin_invites |
|
|
|
|
# |
|
|
|
|
# This route shows the invites that currently exist and can be used. |
|
|
|
|
#== |
|
|
|
|
sub invites ( $c ) { |
|
|
|
|
my $invites = $c->stash->{invites} = [ $c->db->invites->all ]; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/invite | do_admin_invite |
|
|
|
|
# code | The invite code, this is case-sensitive. |
|
|
|
|
# is_multi_use | When true, the code may be used more than once. |
|
|
|
|
# |
|
|
|
|
# This route adds an invite code. |
|
|
|
|
#== |
|
|
|
|
sub do_invite ( $c ) { |
|
|
|
|
my $code = $c->param('code'); |
|
|
|
|
my $is_multi_use = $c->param('is_multi_use' ); |
|
|
|
|
@ -88,29 +119,50 @@ sub do_invite ( $c ) { |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} catch { |
|
|
|
|
$c->flash( error_message => "Invite Code could not be created: $_" ); |
|
|
|
|
push @{$c->stash->{errors}}, "Invite code could not be created: $_"; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_invites' ) ); |
|
|
|
|
return; |
|
|
|
|
return $c->redirect_error( 'show_admin_invites' ) |
|
|
|
|
if $c->stash->{errors}; |
|
|
|
|
|
|
|
|
|
return $c->redirect_success( 'show_admin_invites', "Added $code to invites." ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/invite/remove | do_admin_invite_remove |
|
|
|
|
# iid | The id of the invite code to delete |
|
|
|
|
# |
|
|
|
|
# This route deletes an invite code. |
|
|
|
|
#== |
|
|
|
|
sub do_invite_remove ( $c ) { |
|
|
|
|
my $invite = $c->db->invite($c->param('iid')); |
|
|
|
|
|
|
|
|
|
if ( ! $invite ) { |
|
|
|
|
$c->flash( error_message => "Invite could not be removed, because it doesn't exist?" ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_invites' ) ); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
return $c->redirect_error( 'show_admin_invites', {}, [ 'The invite does not exist' ] ) |
|
|
|
|
unless $invite; |
|
|
|
|
|
|
|
|
|
my $code = $invite->code; |
|
|
|
|
$invite->delete; |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Removed $code from invite pool." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_invites' ) ); |
|
|
|
|
return $c->redirect_success( 'show_admin_invites', "Removed $code from invite pool." ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# GET /admin/servers | show_admin_servers |
|
|
|
|
# |
|
|
|
|
# This route shows servers that blogs are hosted on, and deployed to. |
|
|
|
|
#== |
|
|
|
|
sub servers ( $c ) { |
|
|
|
|
my $servers = $c->stash->{servers} = [ $c->db->servers->all ]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/server | do_admin_server |
|
|
|
|
# server_fqdn | The domain name to use for the server, builder and certbot |
|
|
|
|
# servers should have ssh access to these servers. |
|
|
|
|
# |
|
|
|
|
# This route adds a server to the pool for deployment. These servers are used by |
|
|
|
|
# the certbot and builder servers to deploy ssl certs and blogs to. |
|
|
|
|
#== |
|
|
|
|
sub do_server ( $c ) { |
|
|
|
|
my $fqdn = $c->param('server_fqdn'); |
|
|
|
|
|
|
|
|
|
@ -119,36 +171,53 @@ sub do_server ( $c ) { |
|
|
|
|
$c->db->servers->create({ hostname => $fqdn }); |
|
|
|
|
}); |
|
|
|
|
} catch { |
|
|
|
|
$c->flash( error_message => "Server could not be created: $_" ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_servers' ) ); |
|
|
|
|
return; |
|
|
|
|
push @{$c->stash->{errors}}, "Server could not be created: $_"; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Added $fqdn to server pool." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_servers' ) ); |
|
|
|
|
return $c->redirect_error( 'show_admin_servers' ) |
|
|
|
|
if $c->stash->{errors}; |
|
|
|
|
|
|
|
|
|
return $c->redirect_success( 'show_admin_servers', "Added $fqdn to server pool." ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/server/remove | do_admin_server_remove |
|
|
|
|
# sid | The id of the server to remove |
|
|
|
|
# |
|
|
|
|
# This route removes a server from the rotation used for deploying blogs/ssl certs. |
|
|
|
|
#== |
|
|
|
|
sub do_server_remove ( $c ) { |
|
|
|
|
my $server = $c->db->server($c->param('sid')); |
|
|
|
|
|
|
|
|
|
if ( ! $server ) { |
|
|
|
|
$c->flash( error_message => "Server could not be removed, because it doesn't exist?" ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_servers' ) ); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
return $c->redirect_error( 'show_admin_servers', {}, [ 'The server does not exist' ] ) |
|
|
|
|
unless $server; |
|
|
|
|
|
|
|
|
|
my $hostname = $server->hostname; |
|
|
|
|
$server->delete; |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Removed $hostname from server pool." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_servers' ) ); |
|
|
|
|
return $c->redirect_success( 'show_admin_servers', "Removed $hostname to server pool." ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# GET /admin/domains | show_admin_domains |
|
|
|
|
# |
|
|
|
|
# This route shows domains that users can host their blogs under. |
|
|
|
|
#== |
|
|
|
|
sub domains ( $c ) { |
|
|
|
|
my $domains = $c->stash->{domains} = [ $c->db->hosted_domains->all ]; |
|
|
|
|
|
|
|
|
|
$c->stash->{domains} = [ $c->db->hosted_domains->all ]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/domain | do_admin_domain |
|
|
|
|
# domain_fqdn | The fully qualified domain name we will use for hosting (i.e. foobar.net) |
|
|
|
|
# ssl_challenge | The challenge type -- dns-linode or http to use for validating domains |
|
|
|
|
# |
|
|
|
|
# This route will add a domain to use for hosting. For example, if one adds foobar.net, then |
|
|
|
|
# users will be able to host blogs like myblog.foobar.net. |
|
|
|
|
# |
|
|
|
|
# http challenges will use the certbot server and /.well-known/ forwarding for creation/updating w/ --standalone |
|
|
|
|
# dns-linode challenges will use the --dns-linode plugin and credentials expected to be done with ansible. |
|
|
|
|
#== |
|
|
|
|
sub do_domain ( $c ) { |
|
|
|
|
my $fqdn = $c->param('domain_fqdn'); |
|
|
|
|
my $ssl = $c->param('ssl_challenge'); |
|
|
|
|
@ -158,35 +227,44 @@ sub do_domain ( $c ) { |
|
|
|
|
$c->db->hosted_domains->create({ name => $fqdn, letsencrypt_challenge => $ssl }); |
|
|
|
|
}); |
|
|
|
|
} catch { |
|
|
|
|
$c->flash( error_message => "domain could not be created: $_" ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_domains' ) ); |
|
|
|
|
return; |
|
|
|
|
push @{$c->stash->{errors}}, "Domain could not be created: $_"; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return $c->redirect_error( 'show_admin_domains' ) |
|
|
|
|
if $c->stash->{errors}; |
|
|
|
|
|
|
|
|
|
if ( $ssl eq 'dns-linode' ) { |
|
|
|
|
$c->minion->enqueue( 'mk_wildcard_ssl', [ $domain->id ], { queue => 'certbot' } ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Added $fqdn to domain pool." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_domains' ) ); |
|
|
|
|
return $c->redirect_success( 'show_admin_domains', "Added $fqdn to domain pool." ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/domain/remove | do_admin_domain_remove |
|
|
|
|
# did | The ID for the domain to remove. |
|
|
|
|
# |
|
|
|
|
# This route will remove a hosted domain by its ID. |
|
|
|
|
#== |
|
|
|
|
sub do_domain_remove ( $c ) { |
|
|
|
|
my $domain = $c->db->hosted_domain($c->param('did')); |
|
|
|
|
|
|
|
|
|
if ( ! $domain ) { |
|
|
|
|
$c->flash( error_message => "domain could not be removed, because it doesn't exist?" ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_domains' ) ); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
return $c->redirect_error( 'show_admin_domains', {}, [ "That domain doesn't seem to exist." ] ) |
|
|
|
|
unless $domain; |
|
|
|
|
|
|
|
|
|
my $hostname = $domain->name; |
|
|
|
|
$domain->delete; |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Removed $hostname from domain pool." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_domains' ) ); |
|
|
|
|
return $c->redirect_success( 'show_admin_domains', "Removed $hostname from domain pool." ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/update_ssl | do_admin_update_ssl |
|
|
|
|
# |
|
|
|
|
# This route will schedule a job for update_ssl SSL certs on |
|
|
|
|
# the certbot server and then sync them with the webserver. |
|
|
|
|
# certbot server to the webservers. |
|
|
|
|
#== |
|
|
|
|
sub do_update_ssl ( $c ) { |
|
|
|
|
my $id = $c->minion->enqueue( 'update_ssl_certs', [ ], { |
|
|
|
|
queue => 'certbot', |
|
|
|
|
@ -194,10 +272,15 @@ sub do_update_ssl ( $c ) { |
|
|
|
|
}); |
|
|
|
|
$c->db->admin_jobs->create({ minion_job_id => $id }); |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Scheduled job to update SSL certs." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_jobs' ) ); |
|
|
|
|
return $c->redirect_success( 'show_admin_jobs', 'Scheduled job to update SSL certs.' ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/sync_ssl | do_admin_sync_ssl |
|
|
|
|
# |
|
|
|
|
# This route will schedule a job for syncing SSL certs from the |
|
|
|
|
# certbot server to the webservers. |
|
|
|
|
#== |
|
|
|
|
sub do_sync_ssl ( $c ) { |
|
|
|
|
my $id = $c->minion->enqueue( 'sync_ssl_certs', [ ], { |
|
|
|
|
queue => 'certbot', |
|
|
|
|
@ -205,58 +288,61 @@ sub do_sync_ssl ( $c ) { |
|
|
|
|
}); |
|
|
|
|
$c->db->admin_jobs->create({ minion_job_id => $id }); |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Scheduled job to sync SSL certs." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_jobs' ) ); |
|
|
|
|
return $c->redirect_success( 'show_admin_jobs', 'Scheduled job to sync SSL certs.' ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/alert/read | do_admin_alert_read |
|
|
|
|
# nid | The ID for the system_note |
|
|
|
|
# |
|
|
|
|
# This route will mark a system_note as read when given the note id. |
|
|
|
|
#== |
|
|
|
|
sub do_alert_read ( $c ) { |
|
|
|
|
my $note = $c->db->system_note( $c->param('nid') ); |
|
|
|
|
|
|
|
|
|
if ( ! $note ) { |
|
|
|
|
$c->flash( error_message => "Note could not be marked as read, because it doesn't exist?" ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_alerts' ) ); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
return $c->redirect_error( 'show_admin_alerts', {}, [ "That note doesn't seem to exist." ] ) |
|
|
|
|
unless $note; |
|
|
|
|
|
|
|
|
|
$note->is_read( 1 ); |
|
|
|
|
$note->update; |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Note marked as read." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_alerts' ) ); |
|
|
|
|
return $c->redirect_success( 'show_admin_alerts', 'Note marked as read.' ); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/alert/unread | do_admin_alert_unread |
|
|
|
|
# nid | The ID for the system_note |
|
|
|
|
# |
|
|
|
|
# This route will mark a system_note as unread when given the note id. |
|
|
|
|
#== |
|
|
|
|
sub do_alert_unread ( $c ) { |
|
|
|
|
my $note = $c->db->system_note( $c->param('nid') ); |
|
|
|
|
|
|
|
|
|
if ( ! $note ) { |
|
|
|
|
$c->flash( error_message => "Note could not be marked as unread, because it doesn't exist?" ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_alerts' ) ); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
return $c->redirect_error( 'show_admin_alerts', {}, [ "That note doesn't seem to exist." ] ) |
|
|
|
|
unless $note; |
|
|
|
|
|
|
|
|
|
$note->is_read( 0 ); |
|
|
|
|
$note->update; |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Note marked as read." ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_alerts' ) ); |
|
|
|
|
|
|
|
|
|
return $c->redirect_success( 'show_admin_alerts', 'Note marked as unread.' ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#== |
|
|
|
|
# POST /admin/alert/remove | do_admin_alert_remove |
|
|
|
|
# nid | The ID for the system_note |
|
|
|
|
# |
|
|
|
|
# This route will delete a system_note when given the note id. |
|
|
|
|
#== |
|
|
|
|
sub do_alert_remove ( $c ) { |
|
|
|
|
my $note = $c->db->system_note( $c->param('nid') ); |
|
|
|
|
|
|
|
|
|
if ( ! $note ) { |
|
|
|
|
$c->flash( error_message => "Note could not be removed, because it doesn't exist?" ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_alerts' ) ); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
return $c->redirect_error( 'show_admin_alerts', {}, [ "That note doesn't seem to exist." ] ) |
|
|
|
|
unless $note; |
|
|
|
|
|
|
|
|
|
$note->delete; |
|
|
|
|
|
|
|
|
|
$c->flash( confirmation => "Note removed" ); |
|
|
|
|
$c->redirect_to( $c->url_for( 'show_admin_alerts' ) ); |
|
|
|
|
|
|
|
|
|
return $c->redirect_success( 'show_admin_alerts', 'Note removed.' ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
1; |
|
|
|
|
|