server-start
This commit is contained in:
parent
53e0a7c3e4
commit
9e69f506db
|
@ -1,5 +1,6 @@
|
||||||
[0.2.9]
|
[0.2.9]
|
||||||
|
- starlette is back
|
||||||
|
- auth middleware
|
||||||
- create first chat with member by id = 1 if empty smembers chats_by_author/author_id
|
- create first chat with member by id = 1 if empty smembers chats_by_author/author_id
|
||||||
|
|
||||||
[0.2.8]
|
[0.2.8]
|
||||||
|
@ -18,4 +19,5 @@
|
||||||
- auth service connection
|
- auth service connection
|
||||||
|
|
||||||
[0.2.5]
|
[0.2.5]
|
||||||
- dummy isolation
|
- dummy isolation
|
||||||
|
- aiohttp version
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
FROM python:slim
|
FROM python:slim
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
ADD nginx.conf.sigil ./
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
COPY . .
|
COPY . .
|
||||||
EXPOSE 8080
|
CMD ["python", "server.py"]
|
||||||
CMD ["python", "main.py"]
|
|
||||||
|
|
53
main.py
53
main.py
|
@ -1,30 +1,47 @@
|
||||||
from aiohttp import web
|
import os
|
||||||
from ariadne import make_executable_schema, load_schema_from_path
|
from os.path import exists
|
||||||
|
from ariadne import load_schema_from_path, make_executable_schema
|
||||||
from ariadne.asgi import GraphQL
|
from ariadne.asgi import GraphQL
|
||||||
|
from starlette.applications import Starlette
|
||||||
|
from starlette.middleware import Middleware
|
||||||
|
from starlette.middleware.authentication import AuthenticationMiddleware
|
||||||
|
from starlette.middleware.sessions import SessionMiddleware
|
||||||
|
|
||||||
|
from services.auth import JWTAuthenticate
|
||||||
from services.redis import redis
|
from services.redis import redis
|
||||||
from resolvers import resolvers
|
from resolvers import resolvers
|
||||||
|
from settings import DEV_SERVER_PID_FILE_NAME, SENTRY_DSN, SESSION_SECRET_KEY, MODE
|
||||||
|
|
||||||
type_defs = load_schema_from_path("inbox.graphql")
|
schema = make_executable_schema(load_schema_from_path("schema.graphql"), resolvers) # type: ignore
|
||||||
schema = make_executable_schema(type_defs, resolvers)
|
|
||||||
|
middleware = [
|
||||||
|
Middleware(AuthenticationMiddleware, backend=JWTAuthenticate()),
|
||||||
|
Middleware(SessionMiddleware, secret_key=SESSION_SECRET_KEY),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
async def on_startup(_app):
|
async def start_up():
|
||||||
await redis.connect()
|
if MODE == "dev":
|
||||||
|
if exists(DEV_SERVER_PID_FILE_NAME):
|
||||||
|
await redis.connect()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
with open(DEV_SERVER_PID_FILE_NAME, "w", encoding="utf-8") as f:
|
||||||
|
f.write(str(os.getpid()))
|
||||||
|
else:
|
||||||
|
await redis.connect()
|
||||||
|
try:
|
||||||
|
import sentry_sdk
|
||||||
|
|
||||||
|
sentry_sdk.init(SENTRY_DSN)
|
||||||
|
except Exception as e:
|
||||||
|
print("[sentry] init error")
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
async def on_cleanup(_app):
|
async def shutdown():
|
||||||
await redis.disconnect()
|
await redis.disconnect()
|
||||||
|
|
||||||
|
|
||||||
# Run the aiohttp server
|
app = Starlette(debug=True, on_startup=[start_up], on_shutdown=[shutdown])
|
||||||
if __name__ == "__main__":
|
app.mount("/", GraphQL(schema, debug=True))
|
||||||
app = web.Application()
|
|
||||||
app.on_startup.append(on_startup)
|
|
||||||
app.on_cleanup.append(on_cleanup)
|
|
||||||
app.router.add_route(
|
|
||||||
"*",
|
|
||||||
"/graphql",
|
|
||||||
GraphQL(schema),
|
|
||||||
)
|
|
||||||
web.run_app(app)
|
|
||||||
|
|
226
nginx.conf.sigil
Normal file
226
nginx.conf.sigil
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
{{ range $port_map := .PROXY_PORT_MAP | split " " }}
|
||||||
|
{{ $port_map_list := $port_map | split ":" }}
|
||||||
|
{{ $scheme := index $port_map_list 0 }}
|
||||||
|
{{ $listen_port := index $port_map_list 1 }}
|
||||||
|
{{ $upstream_port := index $port_map_list 2 }}
|
||||||
|
|
||||||
|
map $http_origin $allow_origin {
|
||||||
|
~^https?:\/\/((.*\.)?localhost(:\d+)?|discoursio-webapp(-(.*))?\.vercel\.app|(.*\.)?discours\.io)$ $http_origin;
|
||||||
|
default "";
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if eq $scheme "http" }}
|
||||||
|
server {
|
||||||
|
listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }};
|
||||||
|
listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }};
|
||||||
|
{{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }}
|
||||||
|
access_log {{ $.NGINX_ACCESS_LOG_PATH }}{{ if and ($.NGINX_ACCESS_LOG_FORMAT) (ne $.NGINX_ACCESS_LOG_PATH "off") }} {{ $.NGINX_ACCESS_LOG_FORMAT }}{{ end }};
|
||||||
|
error_log {{ $.NGINX_ERROR_LOG_PATH }};
|
||||||
|
{{ if (and (eq $listen_port "80") ($.SSL_INUSE)) }}
|
||||||
|
include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf;
|
||||||
|
location / {
|
||||||
|
return 301 https://$host:{{ $.PROXY_SSL_PORT }}$request_uri;
|
||||||
|
}
|
||||||
|
{{ else }}
|
||||||
|
location / {
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_min_length 1100;
|
||||||
|
gzip_buffers 4 32k;
|
||||||
|
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
|
||||||
|
proxy_pass http://{{ $.APP }}-{{ $upstream_port }};
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_read_timeout {{ $.PROXY_READ_TIMEOUT }};
|
||||||
|
proxy_buffer_size {{ $.PROXY_BUFFER_SIZE }};
|
||||||
|
proxy_buffering {{ $.PROXY_BUFFERING }};
|
||||||
|
proxy_buffers {{ $.PROXY_BUFFERS }};
|
||||||
|
proxy_busy_buffers_size {{ $.PROXY_BUSY_BUFFERS_SIZE }};
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $http_connection;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Forwarded-For {{ $.PROXY_X_FORWARDED_FOR }};
|
||||||
|
proxy_set_header X-Forwarded-Port {{ $.PROXY_X_FORWARDED_PORT }};
|
||||||
|
proxy_set_header X-Forwarded-Proto {{ $.PROXY_X_FORWARDED_PROTO }};
|
||||||
|
proxy_set_header X-Request-Start $msec;
|
||||||
|
{{ if $.PROXY_X_FORWARDED_SSL }}proxy_set_header X-Forwarded-Ssl {{ $.PROXY_X_FORWARDED_SSL }};{{ end }}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if $.CLIENT_MAX_BODY_SIZE }}client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }};{{ end }}
|
||||||
|
include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf;
|
||||||
|
|
||||||
|
error_page 400 401 402 403 405 406 407 408 409 410 411 412 413 414 415 416 417 418 420 422 423 424 426 428 429 431 444 449 450 451 /400-error.html;
|
||||||
|
location /400-error.html {
|
||||||
|
root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors;
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_page 404 /404-error.html;
|
||||||
|
location /404-error.html {
|
||||||
|
root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors;
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_page 500 501 502 503 504 505 506 507 508 509 510 511 /500-error.html;
|
||||||
|
location /500-error.html {
|
||||||
|
root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors;
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
|
}
|
||||||
|
{{ else if eq $scheme "https"}}
|
||||||
|
server {
|
||||||
|
listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl {{ if eq $.HTTP2_SUPPORTED "true" }}http2{{ else if eq $.SPDY_SUPPORTED "true" }}spdy{{ end }};
|
||||||
|
listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} ssl {{ if eq $.HTTP2_SUPPORTED "true" }}http2{{ else if eq $.SPDY_SUPPORTED "true" }}spdy{{ end }};
|
||||||
|
{{ if $.SSL_SERVER_NAME }}server_name {{ $.SSL_SERVER_NAME }}; {{ end }}
|
||||||
|
{{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }}
|
||||||
|
access_log {{ $.NGINX_ACCESS_LOG_PATH }}{{ if and ($.NGINX_ACCESS_LOG_FORMAT) (ne $.NGINX_ACCESS_LOG_PATH "off") }} {{ $.NGINX_ACCESS_LOG_FORMAT }}{{ end }};
|
||||||
|
error_log {{ $.NGINX_ERROR_LOG_PATH }};
|
||||||
|
|
||||||
|
ssl_certificate {{ $.APP_SSL_PATH }}/server.crt;
|
||||||
|
ssl_certificate_key {{ $.APP_SSL_PATH }}/server.key;
|
||||||
|
ssl_protocols TLSv1.2 {{ if eq $.TLS13_SUPPORTED "true" }}TLSv1.3{{ end }};
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
|
||||||
|
keepalive_timeout 70;
|
||||||
|
{{ if and (eq $.SPDY_SUPPORTED "true") (ne $.HTTP2_SUPPORTED "true") }}add_header Alternate-Protocol {{ $.PROXY_SSL_PORT }}:npn-spdy/2;{{ end }}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_min_length 1100;
|
||||||
|
gzip_buffers 4 32k;
|
||||||
|
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
|
||||||
|
proxy_pass http://{{ $.APP }}-{{ $upstream_port }};
|
||||||
|
{{ if eq $.HTTP2_PUSH_SUPPORTED "true" }}http2_push_preload on; {{ end }}
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_read_timeout {{ $.PROXY_READ_TIMEOUT }};
|
||||||
|
proxy_buffer_size {{ $.PROXY_BUFFER_SIZE }};
|
||||||
|
proxy_buffering {{ $.PROXY_BUFFERING }};
|
||||||
|
proxy_buffers {{ $.PROXY_BUFFERS }};
|
||||||
|
proxy_busy_buffers_size {{ $.PROXY_BUSY_BUFFERS_SIZE }};
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $http_connection;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Forwarded-For {{ $.PROXY_X_FORWARDED_FOR }};
|
||||||
|
proxy_set_header X-Forwarded-Port {{ $.PROXY_X_FORWARDED_PORT }};
|
||||||
|
proxy_set_header X-Forwarded-Proto {{ $.PROXY_X_FORWARDED_PROTO }};
|
||||||
|
proxy_set_header X-Request-Start $msec;
|
||||||
|
{{ if $.PROXY_X_FORWARDED_SSL }}proxy_set_header X-Forwarded-Ssl {{ $.PROXY_X_FORWARDED_SSL }};{{ end }}
|
||||||
|
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
add_header 'Access-Control-Allow-Origin' '$allow_origin' always;
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||||
|
#
|
||||||
|
# Custom headers and headers various browsers *should* be OK with but aren't
|
||||||
|
#
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
|
||||||
|
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||||
|
#
|
||||||
|
# Tell client that this pre-flight info is valid for 20 days
|
||||||
|
#
|
||||||
|
add_header 'Access-Control-Max-Age' 1728000;
|
||||||
|
add_header 'Content-Type' 'text/plain; charset=utf-8';
|
||||||
|
add_header 'Content-Length' 0;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request_method = 'POST') {
|
||||||
|
add_header 'Access-Control-Allow-Origin' '$allow_origin' always;
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
|
||||||
|
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
|
||||||
|
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request_method = 'GET') {
|
||||||
|
add_header 'Access-Control-Allow-Origin' '$allow_origin' always;
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
|
||||||
|
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
|
||||||
|
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if $.CLIENT_MAX_BODY_SIZE }}client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }};{{ end }}
|
||||||
|
include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf;
|
||||||
|
|
||||||
|
error_page 400 401 402 403 405 406 407 408 409 410 411 412 413 414 415 416 417 418 420 422 423 424 426 428 429 431 444 449 450 451 /400-error.html;
|
||||||
|
location /400-error.html {
|
||||||
|
root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors;
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_page 404 /404-error.html;
|
||||||
|
location /404-error.html {
|
||||||
|
root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors;
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_page 500 501 503 504 505 506 507 508 509 510 511 /500-error.html;
|
||||||
|
location /500-error.html {
|
||||||
|
root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors;
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_page 502 /502-error.html;
|
||||||
|
location /502-error.html {
|
||||||
|
root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors;
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{ else if eq $scheme "grpc"}}
|
||||||
|
{{ if eq $.GRPC_SUPPORTED "true"}}{{ if eq $.HTTP2_SUPPORTED "true"}}
|
||||||
|
server {
|
||||||
|
listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} http2;
|
||||||
|
listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} http2;
|
||||||
|
{{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }}
|
||||||
|
access_log {{ $.NGINX_ACCESS_LOG_PATH }}{{ if and ($.NGINX_ACCESS_LOG_FORMAT) (ne $.NGINX_ACCESS_LOG_PATH "off") }} {{ $.NGINX_ACCESS_LOG_FORMAT }}{{ end }};
|
||||||
|
error_log {{ $.NGINX_ERROR_LOG_PATH }};
|
||||||
|
location / {
|
||||||
|
grpc_pass grpc://{{ $.APP }}-{{ $upstream_port }};
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if $.CLIENT_MAX_BODY_SIZE }}client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }};{{ end }}
|
||||||
|
include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf;
|
||||||
|
}
|
||||||
|
{{ end }}{{ end }}
|
||||||
|
{{ else if eq $scheme "grpcs"}}
|
||||||
|
{{ if eq $.GRPC_SUPPORTED "true"}}{{ if eq $.HTTP2_SUPPORTED "true"}}
|
||||||
|
server {
|
||||||
|
listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl http2;
|
||||||
|
listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} ssl http2;
|
||||||
|
{{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }}
|
||||||
|
access_log {{ $.NGINX_ACCESS_LOG_PATH }}{{ if and ($.NGINX_ACCESS_LOG_FORMAT) (ne $.NGINX_ACCESS_LOG_PATH "off") }} {{ $.NGINX_ACCESS_LOG_FORMAT }}{{ end }};
|
||||||
|
error_log {{ $.NGINX_ERROR_LOG_PATH }};
|
||||||
|
|
||||||
|
ssl_certificate {{ $.APP_SSL_PATH }}/server.crt;
|
||||||
|
ssl_certificate_key {{ $.APP_SSL_PATH }}/server.key;
|
||||||
|
ssl_protocols TLSv1.2 {{ if eq $.TLS13_SUPPORTED "true" }}TLSv1.3{{ end }};
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
grpc_pass grpc://{{ $.APP }}-{{ $upstream_port }};
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ if $.CLIENT_MAX_BODY_SIZE }}client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }};{{ end }}
|
||||||
|
include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf;
|
||||||
|
}
|
||||||
|
{{ end }}{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if $.DOKKU_APP_WEB_LISTENERS }}
|
||||||
|
{{ range $upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }}
|
||||||
|
upstream {{ $.APP }}-{{ $upstream_port }} {
|
||||||
|
{{ range $listeners := $.DOKKU_APP_WEB_LISTENERS | split " " }}
|
||||||
|
{{ $listener_list := $listeners | split ":" }}
|
||||||
|
{{ $listener_ip := index $listener_list 0 }}
|
||||||
|
server {{ $listener_ip }}:{{ $upstream_port }};{{ end }}
|
||||||
|
}
|
||||||
|
{{ end }}{{ end }}
|
|
@ -1,5 +1,8 @@
|
||||||
aiohttp
|
starlette
|
||||||
aioredis
|
aioredis
|
||||||
ariadne
|
ariadne
|
||||||
sqlalchemy
|
sqlalchemy
|
||||||
gql
|
gql
|
||||||
|
pydantic
|
||||||
|
httpx
|
||||||
|
uvicorn
|
||||||
|
|
59
server.py
Normal file
59
server.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import sys
|
||||||
|
import uvicorn
|
||||||
|
from settings import PORT
|
||||||
|
|
||||||
|
|
||||||
|
def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
|
||||||
|
print("%s: %s" % (exception_type.__name__, exception))
|
||||||
|
|
||||||
|
|
||||||
|
log_settings = {
|
||||||
|
"version": 1,
|
||||||
|
"disable_existing_loggers": True,
|
||||||
|
"formatters": {
|
||||||
|
"default": {
|
||||||
|
"()": "uvicorn.logging.DefaultFormatter",
|
||||||
|
"fmt": "%(levelprefix)s %(message)s",
|
||||||
|
"use_colors": None,
|
||||||
|
},
|
||||||
|
"access": {
|
||||||
|
"()": "uvicorn.logging.AccessFormatter",
|
||||||
|
"fmt": '%(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"handlers": {
|
||||||
|
"default": {
|
||||||
|
"formatter": "default",
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"stream": "ext://sys.stderr",
|
||||||
|
},
|
||||||
|
"access": {
|
||||||
|
"formatter": "access",
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"stream": "ext://sys.stdout",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"uvicorn": {"handlers": ["default"], "level": "INFO"},
|
||||||
|
"uvicorn.error": {"level": "INFO", "handlers": ["default"], "propagate": True},
|
||||||
|
"uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local_headers = [
|
||||||
|
("Access-Control-Allow-Methods", "GET, POST, OPTIONS, HEAD"),
|
||||||
|
("Access-Control-Allow-Origin", "https://localhost:3000"),
|
||||||
|
(
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization",
|
||||||
|
),
|
||||||
|
("Access-Control-Expose-Headers", "Content-Length,Content-Range"),
|
||||||
|
("Access-Control-Allow-Credentials", "true"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.excepthook = exception_handler
|
||||||
|
uvicorn.run(
|
||||||
|
"main:app", host="0.0.0.0", port=PORT, proxy_headers=True, server_header=True
|
||||||
|
)
|
|
@ -1,19 +1,38 @@
|
||||||
|
from typing import Optional
|
||||||
|
from pydantic import BaseModel
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from gql.transport import aiohttp
|
from starlette.authentication import AuthenticationBackend
|
||||||
import aiohttp
|
from starlette.requests import HTTPConnection
|
||||||
import json
|
from graphql.error import GraphQLError
|
||||||
|
from httpx import AsyncClient
|
||||||
from services.db import local_session
|
from services.db import local_session
|
||||||
from settings import AUTH_URL
|
from settings import AUTH_URL
|
||||||
from orm.author import Author
|
from orm.author import Author
|
||||||
from graphql.error import GraphQLError
|
|
||||||
|
|
||||||
|
|
||||||
class BaseHttpException(GraphQLError):
|
class AuthUser(BaseModel):
|
||||||
code = 500
|
user_id: Optional[int]
|
||||||
message = "500 Server error"
|
username: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class Unauthorized(BaseHttpException):
|
class AuthCredentials(BaseModel):
|
||||||
|
user_id: Optional[int] = None
|
||||||
|
scopes: Optional[dict] = {}
|
||||||
|
logged_in: bool = False
|
||||||
|
error_message: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class JWTAuthenticate(AuthenticationBackend):
|
||||||
|
async def authenticate(self, request: HTTPConnection):
|
||||||
|
scopes = {} # TODO: integrate await user.get_permission
|
||||||
|
logged_in, user_id = await check_auth(request)
|
||||||
|
return (
|
||||||
|
AuthCredentials(user_id=user_id, scopes=scopes, logged_in=logged_in),
|
||||||
|
AuthUser(user_id=user_id, username=""),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Unauthorized(GraphQLError):
|
||||||
code = 401
|
code = 401
|
||||||
message = "401 Unauthorized"
|
message = "401 Unauthorized"
|
||||||
|
|
||||||
|
@ -26,16 +45,14 @@ async def check_auth(req):
|
||||||
else {"query": "{ session { user { id } } }"}
|
else {"query": "{ session { user { id } } }"}
|
||||||
)
|
)
|
||||||
headers = {"Authorization": token, "Content-Type": "application/json"}
|
headers = {"Authorization": token, "Content-Type": "application/json"}
|
||||||
async with aiohttp.ClientSession(headers=headers) as session:
|
async with AsyncClient() as client:
|
||||||
async with session.post(AUTH_URL, data=json.dumps(gql)) as response:
|
response = await client.post(AUTH_URL, headers=headers, data=gql)
|
||||||
if response.status != 200:
|
if response.status_code != 200:
|
||||||
return False, None
|
return False, None
|
||||||
r = await response.json()
|
r = response.json()
|
||||||
user_id = (
|
user_id = r.get("data", {}).get("session", {}).get("user", {}).get("id", None)
|
||||||
r.get("data", {}).get("session", {}).get("user", {}).get("id", None)
|
is_authenticated = user_id is not None
|
||||||
)
|
return is_authenticated, user_id
|
||||||
is_authenticated = user_id is not None
|
|
||||||
return is_authenticated, user_id
|
|
||||||
|
|
||||||
|
|
||||||
def author_id_by_user_id(user_id):
|
def author_id_by_user_id(user_id):
|
||||||
|
|
|
@ -9,3 +9,8 @@ DB_URL = (
|
||||||
REDIS_URL = environ.get("REDIS_URL") or "redis://127.0.0.1"
|
REDIS_URL = environ.get("REDIS_URL") or "redis://127.0.0.1"
|
||||||
API_BASE = environ.get("API_BASE") or ""
|
API_BASE = environ.get("API_BASE") or ""
|
||||||
AUTH_URL = environ.get("AUTH_URL") or ""
|
AUTH_URL = environ.get("AUTH_URL") or ""
|
||||||
|
MODE = environ.get("MODE") or "production"
|
||||||
|
SENTRY_DSN = environ.get("SENTRY_DSN")
|
||||||
|
SESSION_SECRET_KEY = environ.get("SESSION_SECRET_KEY") or "!secret"
|
||||||
|
DEV_SERVER_PID_FILE_NAME = "dev-server.pid"
|
||||||
|
SESSION_TOKEN_HEADER = "Authorization"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user