diff --git a/apps/gifted-subs/src/app/giftedsubs/giftedsubs.component.ts b/apps/gifted-subs/src/app/giftedsubs/giftedsubs.component.ts index efbb359..09ad43d 100644 --- a/apps/gifted-subs/src/app/giftedsubs/giftedsubs.component.ts +++ b/apps/gifted-subs/src/app/giftedsubs/giftedsubs.component.ts @@ -27,8 +27,6 @@ import { import { merge } from 'rxjs/observable/merge'; import { fromEvent } from 'rxjs/observable/fromEvent'; -import { saveAs } from 'file-saver'; - import { GiftedSubsService } from '../services/giftedsubs.service'; import { GiftedSubsDataSource } from '../services/giftedsubs.datasource'; diff --git a/apps/gifted-subs/src/app/services/giftedsubs.service.ts b/apps/gifted-subs/src/app/services/giftedsubs.service.ts index 3635d24..7707e8b 100644 --- a/apps/gifted-subs/src/app/services/giftedsubs.service.ts +++ b/apps/gifted-subs/src/app/services/giftedsubs.service.ts @@ -20,7 +20,7 @@ export class GiftedSubsService { filter = '', sortBy = 'time', sortOrder = 'desc', pageNumber = 0, pageSize = 20): Observable { - return this.http.get('/gifted-subs/api/gifted-subs', { + return this.http.get('/twitch-subs/api/gifted-subs', { observe: 'response', params: new HttpParams() .set('filter', filter) diff --git a/docker-compose.yaml.example b/docker-compose.yaml.example index 3cb178a..ac07757 100644 --- a/docker-compose.yaml.example +++ b/docker-compose.yaml.example @@ -10,24 +10,11 @@ services: ports: - 127.0.0.1:8080:80 depends_on: - - gifted-subs-api - quotes-api - twitch-cache-api + - twitch-subs-api - cms - # Gifted subs API service with /data/gifted-subs mounted as database storage - # SECRET_KEY is needed for API key validation - gifted-subs-api: - build: - context: ./gifted-subs-api - volumes: - - /data/gifted-subs:/gifted-subs - environment: - - SQLALCHEMY_DATABASE_URI=sqlite:////gifted-subs/gifted-subs.db - - SECRET_KEY=__SECRET_KEY__ - expose: - - 5000 - # Quotes API service with /data/quotes mounted as database storage # SECRET_KEY is needed for API key validation quotes-api: @@ -58,6 +45,19 @@ services: expose: - 5000 + # Twitch subs API service with /data/twitch-subs mounted as database storage + # SECRET_KEY is needed for API key validation + twitch-subs-api: + build: + context: ./twitch-subs-api + volumes: + - /data/twitch-subs:/twitch-subs + environment: + - SQLALCHEMY_DATABASE_URI=sqlite:////twitch-subs/twitch-subs.db + - SECRET_KEY=__SECRET_KEY__ + expose: + - 5000 + # CMS service with /data/grav mounted as user data storage cms: build: diff --git a/nginx/nginx.conf b/nginx/nginx.conf index f8170b7..6a9cdb2 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -64,15 +64,6 @@ http { root /twitch-logs; } - location ^~ /gifted-subs/api/ { - rewrite ^/gifted-subs/api(/.*)$ $1 break; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_http_version 1.1; - tcp_nodelay on; - proxy_pass http://gifted-subs-api:5000/; - } - location ^~ /quotes/api/ { rewrite ^/quotes/api(/.*)$ $1 break; proxy_set_header Host $host; @@ -91,6 +82,15 @@ http { proxy_pass http://twitch-cache-api:5000/; } + location ^~ /twitch-subs/api/ { + rewrite ^/twitch-subs/api(/.*)$ $1 break; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_http_version 1.1; + tcp_nodelay on; + proxy_pass http://twitch-subs-api:5000/; + } + location ~ /\.ht { deny all; } diff --git a/gifted-subs-api/.gitignore b/twitch-subs-api/.gitignore similarity index 100% rename from gifted-subs-api/.gitignore rename to twitch-subs-api/.gitignore diff --git a/gifted-subs-api/Dockerfile b/twitch-subs-api/Dockerfile similarity index 100% rename from gifted-subs-api/Dockerfile rename to twitch-subs-api/Dockerfile diff --git a/gifted-subs-api/app.py b/twitch-subs-api/app.py similarity index 58% rename from gifted-subs-api/app.py rename to twitch-subs-api/app.py index 179d84e..d870415 100644 --- a/gifted-subs-api/app.py +++ b/twitch-subs-api/app.py @@ -10,7 +10,7 @@ import itsdangerous import sqlalchemy import sqlalchemy.engine -from db import db, GiftedSub +from db import db, RegularSub, GiftedSub app = flask.Flask(__name__) @@ -36,6 +36,15 @@ login_manager.init_app(app) api = flask_restful.Api(app) +regular_sub_fields = { + 'id': flask_restful.fields.Integer(), + 'user': flask_restful.fields.String(), + 'rank': flask_restful.fields.Integer(), + 'time': flask_restful.fields.DateTime(dt_format='iso8601'), + 'created_at': flask_restful.fields.DateTime(dt_format='iso8601'), + 'updated_at': flask_restful.fields.DateTime(dt_format='iso8601'), +} + gifted_sub_fields = { 'id': flask_restful.fields.Integer(), 'giver': flask_restful.fields.String(), @@ -46,13 +55,18 @@ gifted_sub_fields = { } +regular_sub_parser = flask_restful.reqparse.RequestParser() +regular_sub_parser.add_argument('id', type=int) +regular_sub_parser.add_argument('user', type=str, required=True) +regular_sub_parser.add_argument('rank', type=int, required=True) +regular_sub_parser.add_argument('time', type=flask_restful.inputs.datetime_from_iso8601, required=True) + gifted_sub_parser = flask_restful.reqparse.RequestParser() gifted_sub_parser.add_argument('id', type=int) gifted_sub_parser.add_argument('giver', type=str, required=True) gifted_sub_parser.add_argument('receiver', type=str, required=True) gifted_sub_parser.add_argument('time', type=flask_restful.inputs.datetime_from_iso8601, required=True) - filter_parser = flask_restful.reqparse.RequestParser() filter_parser.add_argument('filter', type=str) filter_parser.add_argument('older_than', type=flask_restful.inputs.datetime_from_iso8601) @@ -65,7 +79,7 @@ filter_parser.add_argument('page_size', type=int) @login_manager.request_loader def load_user(request): - key = request.headers.get('X-Gifted-Subs-API-Key') + key = request.headers.get('X-Twitch-Subs-API-Key') if not key: return None s = itsdangerous.TimedJSONWebSignatureSerializer(app.config['SECRET_KEY']) @@ -77,6 +91,100 @@ def load_user(request): return None +class RegularSubResource(flask_restful.Resource): + @flask_restful.marshal_with(regular_sub_fields) + def get(self, id): + q = db.session.query(RegularSub).filter(RegularSub.id == id) + regular_sub = q.first() + if not regular_sub: + flask_restful.abort(404, message='Regular sub {0} does not exist'.format(id)) + return regular_sub, 200 + + @flask_login.login_required + @flask_restful.marshal_with(regular_sub_fields) + def put(self, id): + args = regular_sub_parser.parse_args() + now = sqlalchemy.func.now() + q = db.session.query(RegularSub).filter(RegularSub.id == id) + regular_sub = q.first() + if not regular_sub: + regular_sub = RegularSub(id=id, created_at=now) + regular_sub.user = args['user'] + regular_sub.rank = args['rank'] + regular_sub.time = args['time'] + regular_sub.updated_at = now + db.session.add(regular_sub) + db.session.commit() + return regular_sub, 200 + + @flask_login.login_required + def delete(self, id): + q = db.session.query(RegularSub).filter(RegularSub.id == id) + regular_sub = q.first() + if not regular_sub: + flask_restful.abort(404, message='Regular sub {0} does not exist'.format(id)) + db.session.delete(regular_sub) + db.session.commit() + return None, 204 + + +class RegularSubsResource(flask_restful.Resource): + @flask_restful.marshal_with(regular_sub_fields) + def get(self): + args = filter_parser.parse_args() + q = db.session.query(RegularSub) + if args['filter']: + q = q.filter(RegularSub.user.ilike('%{}%'.format(args['filter']))) + if args['older_than']: + q = q.filter(RegularSub.time < args['older_than']) + if args['newer_than']: + q = q.filter(RegularSub.time > args['newer_than']) + count = q.count() + if args['sort_order'] == 'random': + q = q.order_by(sqlalchemy.func.random()) + elif args['sort_by']: + col = getattr(RegularSub, args['sort_by'], None) + if col: + if args['sort_order']: + order_by = getattr(col, args['sort_order'], None) + if order_by: + q = q.order_by(order_by()) + else: + q = q.order_by(col) + if args['page_size']: + q = q.limit(args['page_size']) + if args['page_number'] and args['page_size']: + q = q.offset(args['page_number'] * args['page_size']) + regular_subs = q.all() + return regular_subs, 200, {'X-Total-Count': count} + + @flask_login.login_required + @flask_restful.marshal_with(regular_sub_fields) + def post(self): + args = regular_sub_parser.parse_args() + if not args['user']: + flask_restful.abort(400, message='Missing required parameter user') + if not args['rank']: + flask_restful.abort(400, message='Missing required parameter rank') + if not args['time']: + flask_restful.abort(400, message='Missing required parameter time') + now = sqlalchemy.func.now() + q = db.session.query(RegularSub).filter((RegularSub.user == args['user']) &\ + (RegularSub.rank == args['rank']) &\ + (sqlalchemy.func.DATE(RegularSub.time) == args['time'].date())) + regular_sub = q.first() + if not regular_sub: + regular_sub = RegularSub(created_at=now) + regular_sub.user = args['user'] + regular_sub.rank = args['rank'] + regular_sub.time = args['time'] + regular_sub.updated_at = now + db.session.add(regular_sub) + db.session.commit() + url = api.url_for(RegularSubResource, id=regular_sub.id, _external=True, _scheme='https') + return regular_sub, 201, {'Location': url} + + class GiftedSubResource(flask_restful.Resource): @flask_restful.marshal_with(gifted_sub_fields) def get(self, id): @@ -172,6 +280,8 @@ class GiftedSubsResource(flask_restful.Resource): return gifted_sub, 201, {'Location': url} +api.add_resource(RegularSubResource, '/regular-subs/') +api.add_resource(RegularSubsResource, '/regular-subs') api.add_resource(GiftedSubResource, '/gifted-subs/') api.add_resource(GiftedSubsResource, '/gifted-subs') diff --git a/gifted-subs-api/db.py b/twitch-subs-api/db.py similarity index 56% rename from gifted-subs-api/db.py rename to twitch-subs-api/db.py index bc9c321..069155f 100644 --- a/gifted-subs-api/db.py +++ b/twitch-subs-api/db.py @@ -4,6 +4,17 @@ import flask_sqlalchemy db = flask_sqlalchemy.SQLAlchemy(session_options=dict(autoflush=False)) +class RegularSub(db.Model): + __tablename__ = 'regularsubs' + + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + user = db.Column(db.String) + rank = db.Column(db.Integer) + time = db.Column(db.DateTime) + created_at = db.Column(db.DateTime) + updated_at = db.Column(db.DateTime) + + class GiftedSub(db.Model): __tablename__ = 'giftedsubs' diff --git a/gifted-subs-api/generate_api_key.py b/twitch-subs-api/generate_api_key.py similarity index 100% rename from gifted-subs-api/generate_api_key.py rename to twitch-subs-api/generate_api_key.py diff --git a/gifted-subs-api/requirements.txt b/twitch-subs-api/requirements.txt similarity index 100% rename from gifted-subs-api/requirements.txt rename to twitch-subs-api/requirements.txt