2021-08-04 15:35:10 +00:00
from files . mail import *
from files . __main__ import app , limiter
from files . helpers . alerts import *
2021-08-28 12:46:34 +00:00
from files . classes . award import AWARDS
from sqlalchemy import func
2021-09-09 11:02:06 +00:00
from os import path
2021-09-24 18:37:43 +00:00
import calendar
import matplotlib . pyplot as plt
2021-07-21 01:12:26 +00:00
2021-08-05 14:41:32 +00:00
site = environ . get ( " DOMAIN " ) . strip ( )
2021-08-19 05:40:23 +00:00
site_name = environ . get ( " SITE_NAME " ) . strip ( )
2021-07-25 21:59:20 +00:00
2021-09-09 11:02:06 +00:00
@app.get ( ' /rules ' )
@auth_desired
def static_rules ( v ) :
2021-09-12 01:41:48 +00:00
if not path . exists ( f ' ./ { site_name } rules.html ' ) :
2021-09-09 11:02:06 +00:00
if v and v . admin_level == 6 :
return render_template ( ' norules.html ' , v = v )
else :
abort ( 404 )
2021-09-12 01:41:48 +00:00
with open ( f ' ./ { site_name } rules.html ' , ' r ' ) as f :
2021-09-09 11:02:06 +00:00
rules = f . read ( )
return render_template ( ' rules.html ' , rules = rules , v = v )
2021-08-07 12:20:00 +00:00
@app.get ( " /stats " )
2021-09-24 21:57:17 +00:00
@auth_required
2021-08-07 12:20:00 +00:00
def participation_stats ( v ) :
now = int ( time . time ( ) )
day = now - 86400
2021-09-27 21:46:35 +00:00
data = { " valid_users " : g . db . query ( User . id ) . count ( ) ,
" private_users " : g . db . query ( User . id ) . options ( lazyload ( ' * ' ) ) . filter_by ( is_private = True ) . count ( ) ,
" banned_users " : g . db . query ( User . id ) . options ( lazyload ( ' * ' ) ) . filter ( User . is_banned > 0 ) . count ( ) ,
" verified_email_users " : g . db . query ( User . id ) . options ( lazyload ( ' * ' ) ) . filter_by ( is_activated = True ) . count ( ) ,
2021-09-08 07:19:30 +00:00
" total_coins " : g . db . query ( func . sum ( User . coins ) ) . scalar ( ) ,
2021-09-27 21:46:35 +00:00
" signups_last_24h " : g . db . query ( User . id ) . options ( lazyload ( ' * ' ) ) . filter ( User . created_utc > day ) . count ( ) ,
" total_posts " : g . db . query ( Submission . id ) . count ( ) ,
2021-08-07 12:20:00 +00:00
" posting_users " : g . db . query ( Submission . author_id ) . distinct ( ) . count ( ) ,
2021-09-27 21:46:35 +00:00
" listed_posts " : g . db . query ( Submission . id ) . options ( lazyload ( ' * ' ) ) . filter_by ( is_banned = False ) . filter ( Submission . deleted_utc == 0 ) . count ( ) ,
" removed_posts " : g . db . query ( Submission . id ) . options ( lazyload ( ' * ' ) ) . filter_by ( is_banned = True ) . count ( ) ,
" deleted_posts " : g . db . query ( Submission . id ) . options ( lazyload ( ' * ' ) ) . filter ( Submission . deleted_utc > 0 ) . count ( ) ,
" posts_last_24h " : g . db . query ( Submission . id ) . options ( lazyload ( ' * ' ) ) . filter ( Submission . created_utc > day ) . count ( ) ,
" total_comments " : g . db . query ( Comment . id ) . count ( ) ,
2021-08-07 12:20:00 +00:00
" commenting_users " : g . db . query ( Comment . author_id ) . distinct ( ) . count ( ) ,
2021-09-27 21:46:35 +00:00
" removed_comments " : g . db . query ( Comment . id ) . options ( lazyload ( ' * ' ) ) . filter_by ( is_banned = True ) . count ( ) ,
" deleted_comments " : g . db . query ( Comment . id ) . options ( lazyload ( ' * ' ) ) . filter ( Comment . deleted_utc > 0 ) . count ( ) ,
" comments_last_24h " : g . db . query ( Comment . id ) . options ( lazyload ( ' * ' ) ) . filter ( Comment . created_utc > day ) . count ( ) ,
" post_votes " : g . db . query ( Vote . id ) . count ( ) ,
2021-08-07 12:20:00 +00:00
" post_voting_users " : g . db . query ( Vote . user_id ) . distinct ( ) . count ( ) ,
2021-09-27 21:46:35 +00:00
" comment_votes " : g . db . query ( CommentVote . id ) . count ( ) ,
2021-08-07 12:20:00 +00:00
" comment_voting_users " : g . db . query ( CommentVote . user_id ) . distinct ( ) . count ( ) ,
2021-09-27 21:46:35 +00:00
" total_awards " : g . db . query ( AwardRelationship . id ) . count ( ) ,
" awards_given " : g . db . query ( AwardRelationship . id ) . options ( lazyload ( ' * ' ) ) . filter ( or_ ( AwardRelationship . submission_id != None , AwardRelationship . comment_id != None ) ) . count ( )
2021-08-07 12:20:00 +00:00
}
return render_template ( " admin/content_stats.html " , v = v , title = " Content Statistics " , data = data )
2021-09-24 18:37:43 +00:00
@app.get ( " /chart " )
@auth_required
def chart ( v ) :
file = cached_chart ( )
return send_file ( f " ../ { file } " )
@cache.memoize ( timeout = 86400 )
def cached_chart ( ) :
days = int ( request . values . get ( " days " , 25 ) )
now = time . gmtime ( )
midnight_this_morning = time . struct_time ( ( now . tm_year ,
now . tm_mon ,
now . tm_mday ,
0 ,
0 ,
0 ,
now . tm_wday ,
now . tm_yday ,
0 )
)
today_cutoff = calendar . timegm ( midnight_this_morning )
day = 3600 * 24
day_cutoffs = [ today_cutoff - day * i for i in range ( days ) ]
day_cutoffs . insert ( 0 , calendar . timegm ( now ) )
daily_times = [ time . strftime ( " %d " , time . gmtime ( day_cutoffs [ i + 1 ] ) ) for i in range ( len ( day_cutoffs ) - 1 ) ] [ 2 : ] [ : : - 1 ]
2021-09-27 21:46:35 +00:00
daily_signups = [ g . db . query ( User . id ) . options ( lazyload ( ' * ' ) ) . filter ( User . created_utc < day_cutoffs [ i ] , User . created_utc > day_cutoffs [ i + 1 ] ) . count ( ) for i in range ( len ( day_cutoffs ) - 1 ) ] [ 2 : ] [ : : - 1 ]
2021-09-24 18:37:43 +00:00
2021-09-27 21:46:35 +00:00
post_stats = [ g . db . query ( Submission . id ) . options ( lazyload ( ' * ' ) ) . filter ( Submission . created_utc < day_cutoffs [ i ] , Submission . created_utc > day_cutoffs [ i + 1 ] , Submission . is_banned == False ) . count ( ) for i in range ( len ( day_cutoffs ) - 1 ) ] [ 2 : ] [ : : - 1 ]
2021-09-24 18:37:43 +00:00
2021-09-27 21:46:35 +00:00
comment_stats = [ g . db . query ( Comment . id ) . options ( lazyload ( ' * ' ) ) . filter ( Comment . created_utc < day_cutoffs [ i ] , Comment . created_utc > day_cutoffs [ i + 1 ] , Comment . is_banned == False , Comment . author_id != 1 ) . count ( ) for i in range ( len ( day_cutoffs ) - 1 ) ] [ 2 : ] [ : : - 1 ]
2021-09-24 18:37:43 +00:00
# create multiple charts
signup_chart = plt . subplot2grid ( ( 20 , 4 ) , ( 0 , 0 ) , rowspan = 5 , colspan = 4 )
posts_chart = plt . subplot2grid ( ( 20 , 4 ) , ( 7 , 0 ) , rowspan = 5 , colspan = 4 )
comments_chart = plt . subplot2grid ( ( 20 , 4 ) , ( 14 , 0 ) , rowspan = 5 , colspan = 4 )
signup_chart . grid ( ) , posts_chart . grid ( ) , comments_chart . grid ( )
signup_chart . plot (
daily_times ,
daily_signups ,
color = ' red ' )
posts_chart . plot (
daily_times ,
post_stats ,
color = ' green ' )
comments_chart . plot (
daily_times ,
comment_stats ,
color = ' gold ' )
signup_chart . set_ylabel ( " Signups " )
posts_chart . set_ylabel ( " Posts " )
comments_chart . set_ylabel ( " Comments " )
comments_chart . set_xlabel ( " Time (UTC) " )
signup_chart . legend ( loc = ' upper left ' , frameon = True )
posts_chart . legend ( loc = ' upper left ' , frameon = True )
comments_chart . legend ( loc = ' upper left ' , frameon = True )
file = " chart.png "
plt . savefig ( file )
plt . clf ( )
return file
2021-09-21 21:08:20 +00:00
@app.get ( " /patrons " )
2021-09-16 11:08:06 +00:00
@app.get ( " /paypigs " )
2021-07-30 15:14:41 +00:00
@auth_desired
def patrons ( v ) :
2021-08-28 12:46:34 +00:00
query = g . db . query (
User . id , User . username , User . patron , User . namecolor ,
AwardRelationship . kind . label ( ' last_award_kind ' ) , func . count ( AwardRelationship . id ) . label ( ' last_award_count ' )
2021-08-28 12:58:59 +00:00
) . filter ( AwardRelationship . submission_id == None , AwardRelationship . comment_id == None , User . patron > 0 ) \
2021-08-28 12:46:34 +00:00
. group_by ( User . username , User . patron , User . id , User . namecolor , AwardRelationship . kind ) \
2021-08-28 12:58:59 +00:00
. order_by ( User . patron . desc ( ) , AwardRelationship . kind . desc ( ) ) \
2021-08-28 12:46:34 +00:00
. join ( User ) . all ( )
result = { }
for row in ( r . _asdict ( ) for r in query ) :
user_id = row [ ' id ' ]
if user_id not in result :
result [ user_id ] = row
result [ user_id ] [ ' awards ' ] = { }
2021-08-28 12:55:13 +00:00
kind = row [ ' last_award_kind ' ]
if kind in AWARDS . keys ( ) :
result [ user_id ] [ ' awards ' ] [ kind ] = ( AWARDS [ kind ] , row [ ' last_award_count ' ] )
2021-08-28 12:46:34 +00:00
2021-08-28 12:58:59 +00:00
return render_template ( " patrons.html " , v = v , result = result )
2021-07-30 15:14:41 +00:00
2021-08-18 15:32:27 +00:00
@app.get ( " /admins " )
2021-07-25 21:59:20 +00:00
@auth_desired
2021-08-18 15:32:27 +00:00
def admins ( v ) :
2021-09-17 08:29:05 +00:00
admins = g . db . query ( User ) . options ( lazyload ( ' * ' ) ) . filter_by ( admin_level = 6 ) . order_by ( User . coins . desc ( ) ) . all ( )
2021-08-18 15:32:27 +00:00
return render_template ( " admins.html " , v = v , admins = admins )
2021-07-25 21:59:20 +00:00
2021-09-19 21:06:48 +00:00
@app.get ( " /log " )
@auth_desired
def log ( v ) :
2021-09-21 19:31:45 +00:00
page = int ( request . args . get ( " page " , 1 ) )
2021-07-25 21:59:20 +00:00
2021-09-21 19:31:45 +00:00
if v and v . admin_level == 6 : actions = g . db . query ( ModAction ) . order_by ( ModAction . id . desc ( ) ) . offset ( 25 * ( page - 1 ) ) . limit ( 26 ) . all ( )
else : actions = g . db . query ( ModAction ) . filter ( ModAction . kind != " shadowban " , ModAction . kind != " unshadowban " , ModAction . kind != " club " , ModAction . kind != " unclub " ) . order_by ( ModAction . id . desc ( ) ) . offset ( 25 * ( page - 1 ) ) . limit ( 26 ) . all ( )
2021-07-25 21:59:20 +00:00
2021-09-21 19:31:45 +00:00
next_exists = len ( actions ) == 26
actions = actions [ : 25 ]
2021-07-25 21:59:20 +00:00
2021-09-21 19:31:45 +00:00
return render_template ( " log.html " , v = v , actions = actions , next_exists = next_exists , page = page )
2021-07-25 21:59:20 +00:00
2021-07-30 08:28:18 +00:00
@app.get ( " /log/<id> " )
2021-07-25 21:59:20 +00:00
@auth_desired
2021-07-30 08:28:18 +00:00
def log_item ( id , v ) :
2021-07-25 21:59:20 +00:00
2021-07-30 08:28:18 +00:00
try : id = int ( id )
2021-07-31 06:59:18 +00:00
except :
try : id = int ( id , 36 )
except : abort ( 404 )
2021-07-30 08:28:18 +00:00
2021-09-17 08:29:05 +00:00
action = g . db . query ( ModAction ) . options ( lazyload ( ' * ' ) ) . filter_by ( id = id ) . first ( )
2021-07-25 21:59:20 +00:00
if not action :
abort ( 404 )
if request . path != action . permalink :
return redirect ( action . permalink )
2021-07-31 05:59:25 +00:00
return render_template ( " log.html " ,
2021-07-25 21:59:20 +00:00
v = v ,
actions = [ action ] ,
next_exists = False ,
page = 1 ,
action = action
)
2021-07-27 22:31:28 +00:00
@app.get ( " /assets/favicon.ico " )
2021-07-21 01:12:26 +00:00
def favicon ( ) :
2021-09-13 15:58:52 +00:00
return send_file ( f " ./assets/images/ { site_name } /icon.webp " )
2021-07-21 01:12:26 +00:00
2021-07-31 04:32:42 +00:00
@app.get ( " /api " )
2021-07-21 01:12:26 +00:00
@auth_desired
2021-07-31 04:32:42 +00:00
def api ( v ) :
return render_template ( " api.html " , v = v )
2021-07-21 01:12:26 +00:00
2021-07-27 22:31:28 +00:00
@app.get ( " /contact " )
2021-10-02 11:34:37 +00:00
@auth_required
2021-07-21 01:12:26 +00:00
def contact ( v ) :
2021-08-23 17:48:55 +00:00
2021-07-21 01:12:26 +00:00
return render_template ( " contact.html " , v = v )
2021-07-27 22:31:28 +00:00
@app.post ( " /contact " )
2021-10-02 11:34:37 +00:00
@auth_required
2021-07-21 01:12:26 +00:00
def submit_contact ( v ) :
2021-09-19 13:11:34 +00:00
message = f ' This message has been sent automatically to all admins via https:// { site } /contact, user email is " { v . email } " \n \n Message: \n \n ' + request . values . get ( " message " , " " )
2021-07-21 01:12:26 +00:00
send_admin ( v . id , message )
2021-09-16 17:02:58 +00:00
g . db . commit ( )
2021-07-21 01:12:26 +00:00
return render_template ( " contact.html " , v = v , msg = " Your message has been sent. " )
@app.route ( ' /archives ' )
@limiter.exempt
def archivesindex ( ) :
return redirect ( " /archives/index.html " )
@app.route ( ' /archives/<path:path> ' )
@limiter.exempt
def archives ( path ) :
resp = make_response ( send_from_directory ( ' /archives ' , path ) )
if request . path . endswith ( ' .css ' ) : resp . headers . add ( " Content-Type " , " text/css " )
return resp
@app.route ( ' /assets/<path:path> ' )
@limiter.exempt
def static_service ( path ) :
resp = make_response ( send_from_directory ( ' ./assets ' , path ) )
2021-09-13 15:58:52 +00:00
if request . path . endswith ( ' .webp ' ) or request . path . endswith ( ' .gif ' ) or request . path . endswith ( ' .ttf ' ) or request . path . endswith ( ' .woff ' ) or request . path . endswith ( ' .woff2 ' ) :
2021-08-23 10:00:05 +00:00
resp . headers . remove ( " Cache-Control " )
2021-08-31 14:55:34 +00:00
resp . headers . add ( " Cache-Control " , " public, max-age=2628000 " )
2021-08-23 10:00:05 +00:00
2021-07-21 01:12:26 +00:00
return resp
2021-07-27 22:31:28 +00:00
@app.get ( " /robots.txt " )
2021-07-21 01:12:26 +00:00
def robots_txt ( ) :
return send_file ( " ./assets/robots.txt " )
2021-07-27 22:31:28 +00:00
@app.get ( " /settings " )
2021-07-21 01:12:26 +00:00
@auth_required
def settings ( v ) :
2021-08-23 17:48:55 +00:00
2021-07-21 01:12:26 +00:00
return redirect ( " /settings/profile " )
2021-07-27 22:31:28 +00:00
@app.get ( " /settings/profile " )
2021-07-21 01:12:26 +00:00
@auth_required
def settings_profile ( v ) :
2021-08-23 17:48:55 +00:00
2021-07-21 01:12:26 +00:00
return render_template ( " settings_profile.html " ,
v = v )
2021-07-27 22:31:28 +00:00
@app.get ( " /badges " )
2021-07-21 01:12:26 +00:00
@auth_desired
def badges ( v ) :
2021-08-23 17:48:55 +00:00
2021-07-21 01:12:26 +00:00
2021-08-20 05:55:53 +00:00
badges = g . db . query ( BadgeDef ) . all ( )
return render_template ( " badges.html " , v = v , badges = badges )
2021-07-21 01:12:26 +00:00
2021-07-27 22:31:28 +00:00
@app.get ( " /blocks " )
2021-07-21 01:12:26 +00:00
@auth_desired
def blocks ( v ) :
2021-08-23 17:48:55 +00:00
2021-07-21 01:12:26 +00:00
blocks = g . db . query ( UserBlock ) . all ( )
users = [ ]
targets = [ ]
for x in blocks :
users . append ( get_account ( x . user_id ) )
targets . append ( get_account ( x . target_id ) )
return render_template ( " blocks.html " , v = v , users = users , targets = targets )
2021-07-27 22:31:28 +00:00
@app.get ( " /banned " )
2021-07-21 01:12:26 +00:00
@auth_desired
def banned ( v ) :
2021-08-23 17:48:55 +00:00
2021-07-21 01:12:26 +00:00
2021-09-17 08:29:05 +00:00
users = [ x for x in g . db . query ( User ) . options ( lazyload ( ' * ' ) ) . filter ( User . is_banned > 0 , User . unban_utc == 0 ) . all ( ) ]
2021-07-21 01:12:26 +00:00
return render_template ( " banned.html " , v = v , users = users )
2021-07-27 22:31:28 +00:00
@app.get ( " /formatting " )
2021-07-21 01:12:26 +00:00
@auth_desired
def formatting ( v ) :
2021-08-23 17:48:55 +00:00
2021-07-21 01:12:26 +00:00
return render_template ( " formatting.html " , v = v )
@app.route ( " /service-worker.js " )
def serviceworker ( ) :
2021-09-19 12:46:30 +00:00
with open ( " files/assets/js/service-worker.js " , " r " ) as f : return Response ( f . read ( ) , mimetype = ' application/javascript ' )
2021-07-21 01:12:26 +00:00
2021-07-27 22:31:28 +00:00
@app.get ( " /settings/security " )
2021-07-21 01:12:26 +00:00
@auth_required
def settings_security ( v ) :
2021-08-23 17:48:55 +00:00
2021-07-21 01:12:26 +00:00
return render_template ( " settings_security.html " ,
v = v ,
mfa_secret = pyotp . random_base32 ( ) if not v . mfa_secret else None ,
2021-09-19 13:11:34 +00:00
error = request . values . get ( " error " ) or None ,
msg = request . values . get ( " msg " ) or None
2021-07-21 01:12:26 +00:00
)
2021-07-27 22:31:28 +00:00
@app.post ( " /dismiss_mobile_tip " )
2021-07-21 01:12:26 +00:00
def dismiss_mobile_tip ( ) :
session [ " tooltip_last_dismissed " ] = int ( time . time ( ) )
session . modified = True
return " " , 204