from datetime import timedelta from flask import Flask, session, jsonify from flask_restful import Api from api import api_errors from api.resources.spotify_mark_signed import SpotifyMarkSigned from api.resources.admin.soundcloud import SoundCloudTrackAdmin from tracker.json_utils import JSONEncoder, dumps from .resources import * app = Flask(__name__) app.secret_key = "NM2abnFDeSRh3jDNqqQyVqSnwyxRWpD3vgvD5gg8Cd3Rd" app.config["RESTFUL_JSON"] = { "separators": config.api_pretty_json and (", ", ": ") or (",", ":"), "indent": config.api_pretty_json and 2 or None, "cls": JSONEncoder, } app.config["PROPAGATE_EXCEPTIONS"] = True if not config.api_use_debug: app.config["SESSION_COOKIE_SAMESITE"] = "none" app.config["SESSION_COOKIE_SECURE"] = True if config.api_session_expiry_seconds > 0: app.config["PERMANENT_SESSION_LIFETIME"] = timedelta( seconds=config.api_session_expiry_seconds ) app.config["SESSION_REFRESH_EACH_REQUEST"] = True api = Api(app) # This should add the default loggers to the app logger. from tracker.db import Session as db from tracker.db import ROSession from .resources.login import init_app from api.resources import admin, cerebro, charts, queries init_app(app) if config.enable_aws_xray: print("Configuring xray") from aws_xray_sdk.core import xray_recorder from aws_xray_sdk.ext.flask.middleware import XRayMiddleware from aws_xray_sdk.core.sampling.local.sampler import LocalSampler sample_everything_but_ping = { "version": 1, "rules": [ { "description": "Ping", "service_name": "*", "http_method": "*", "url_path": "/api/ping", "fixed_target": 0, "rate": 0, } ], "default": {"fixed_target": 1, "rate": 1}, } xray_recorder.configure( sampling=True, sampler=LocalSampler(rules=sample_everything_but_ping), service="whtlst-api", ) # class FakeEmitter(): # def send_entity(self, entity): # print("SEND", entity) # def set_daemon_address(self, addr): # print("WOULD SET ADDRESS", addr) # # xray_recorder.emitter = FakeEmitter() XRayMiddleware(app, xray_recorder) else: print("Xray disabled") api.add_resource(Ping, "/api/ping") api.add_resource(Login, "/api/login") api.add_resource(SoundCloudTrackSearch, "/api/soundcloud_tracks") api.add_resource(SoundCloudTrackDetails, "/api/soundcloud_tracks/") api.add_resource(SoundCloudAnalyzer, "/api/soundcloud_analyzer") api.add_resource(YouTubeAnalyzer, "/api/youtube_analyzer") api.add_resource(YouTubeVidByYtid, "/api/youtube_videos_list") api.add_resource(SpotifyAnalyzer, "/api/spotify_analyzer") api.add_resource(InstagramAnalyzer, "/api/instagram_analyzer") api.add_resource(SoundCloudTrackByScid, "/api/soundcloud_tracks_list") api.add_resource(SoundCloudTrackAdmin, "/api/soundcloud_track_admin") api.add_resource(SpotifyTracksById, "/api/spotify_tracks_list") api.add_resource(Spotify, "/api/spotify") api.add_resource(SpotifyTrack, "/api/spotify_tracks/") api.add_resource(SpotifyMarkSigned, "/api/spotify//mark_signed") api.add_resource(SampleSpotify, "/api/sample_spotify") api.add_resource( SpotifyTrackTags, "/api/spotify/tracks//tags/" ) api.add_resource(YouTube, "/api/youtube") api.add_resource(YouTubeVideoDetail, "/api/ytvideos/") api.add_resource(TwitterActivity, "/api/twitter_by_day") api.add_resource(SoundCloudActivity, "/api/soundcloud_by_day") api.add_resource(InstagramActivity, "/api/instagram_by_day") api.add_resource(Artist, "/api/artists//") api.add_resource(ArtistBrains, "/api/artists///brains") api.add_resource(ArtistManualCache, "/api/artist_manual_cache") api.add_resource(ArtistByIds, "/api/artists_by_id") api.add_resource(Search, "/api/search") api.add_resource(Influencer, "/api/influencers//") api.add_resource(PublishedSpreadsheets, "/api/admin/published_spreadsheets") api.add_resource(SoundcloudAdmin, "/api/admin/sc_resolve") # deprecated: api.add_resource( MyWhitelist, "/api/my_whitelist", "/api/my_whitelist/", "/api/my_whitelist//", ) api.add_resource( MyWhitelists, "/api/MyWhitelists", ) api.add_resource( ManageWhitelist, "/api/ManageWhitelist//", "/api/ManageWhitelist///", ) api.add_resource(ManageArtistSources, "/api/artist_sources/") api.add_resource(UserData, "/api/userdata") api.add_resource( UsersHiddenArtist, "/api/users/hidden_artists//" ) api.add_resource(MyHiddenArtists, "/api/users/my_hidden_artists") api.add_resource(Echo, "/echo") api.add_resource(DbCheck, "/api/dbcheck") api.add_resource(SpyStreams, "/api/admin/_spy_streams/") api.add_resource(ScProfile, "/api/profiles/sc") api.add_resource(InProfile, "/api/profiles/in") api.add_resource(SpyProfile, "/api/profiles/spy") api.add_resource(TwProfile, "/api/profiles/tw") api.add_resource(YtProfile, "/api/profiles/yt") api.add_resource(ScRecommendedInfluencers, "/api/recommended_influencers/sc") api.add_resource(TikToks, "/api/tiktoks", "/api/trending_tiktoks") api.add_resource(TikTokSounds, "/api/tiktok_sounds") api.add_resource(TikTokScoutings, "/api/tiktok_scoutings") admin.register_urls(api, "/api/admin") cerebro.register_urls(api, "/api/cerebro") charts.register_urls(api, "/api/charts") api.add_resource(TestThreading, "/api/testthreading") @app.errorhandler(Exception) def json_error(e): print("\n\nA JsonException was caught.\n\n") print(str(e)) print(type(e)) import traceback import sys traceback.print_exc() status = getattr(e, "status", 500) or 500 try: jsontext = dumps(e) except Exception: jsontext = dumps({"info": str(e), "status": status}) if config.api_enable_rollbar: current_user = flask_login.current_user if current_user: anon_id = current_user.anon_id() else: anon_id = "na" try: rollbar.report_exc_info( sys.exc_info(), request, level="error", extra_data=dict(anon_id=anon_id, githash=app.config.get("GIT_HASH")), ) except Exception as rollee: print("Failed to send error to rollbar {}".format(rollee)) return app.response_class(jsontext, mimetype="application/json", status=status) @app.errorhandler(api_errors.IncorrectLoginError) def need_to_login(e: api_errors.IncorrectLoginError): return app.response_class(dumps(e), mimetype="application/json", status=403) @app.errorhandler(api_errors.UnauthenticatedError) def need_to_login(e): return app.response_class(dumps(e), mimetype="application/json", status=401) @app.errorhandler(api_errors.SessionExpiredError) def need_to_login(e): return app.response_class(dumps(e), mimetype="application/json", status=401) @app.errorhandler(api_errors.NotFoundError) def need_to_login(e): return app.response_class(dumps(e), mimetype="application/json", status=404) @app.errorhandler(404) def four_o_four_error(e): return app.response_class( dumps(api_errors.NotFoundError()), mimetype="application/json", status=404 ) from flask import request import rollbar @app.before_first_request def add_monitoring(): if config.api_enable_rollbar: rollbar.init(config.rollbar_secret) rollbar.report_message( "Rollbar configured in API", level="info", extra_data=dict(githash=app.config.get("GIT_HASH")), ) @app.before_request def check_if_session_should_modify(): if ( not request.args.get("_ignoreForSession") or request.args["_ignoreForSession"] != "1" ): session.modified = True else: print("Request won't refresh session") @app.after_request def after_request(response): cors_origin = request.headers.get("Origin") if cors_origin: # In order to allow credentials in a CORS request (ie. having the browser pass cookies) # the -Allow-Origin header cannot by '*'. Here we can just return the origin. # we may want to whitelist only certain ones. response.headers.add("Access-Control-Allow-Origin", cors_origin) response.headers.add("Access-Control-Allow-Credentials", "true") else: response.headers.add("Access-Control-Allow-Origin", "*") response.headers.add( "Access-Control-Allow-Headers", "Origin, Content-Type, Authorization" ) response.headers.add("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE") response.headers.add("Access-Control-Max-Age", "600") try: db.close() except Exception as e: app.logger.exception("Error closing database") return response @app.teardown_appcontext def shutdown_session(exception=None): db.remove() ROSession.remove()