日常生活的交流与学习

首页 新随笔 联系 管理

E:\song\agv_fastapi_socket2\fastapi-socketio-example-main\app.py

import os
import pathlib
import secrets
import time
from typing import Optional

import socketio
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.param_functions import Cookie, Depends
from fastapi.params import Form
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from starlette.middleware.sessions import SessionMiddleware
from starlette.requests import Request
from starlette.responses import RedirectResponse, Response

SECRET_KEY = os.environ.get("SECRET_KEY", "ef4ac4e2a33e4d9e0bb34200349e3544")

templates = Jinja2Templates(directory=pathlib.Path(__file__).parent / "templates")

fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": "fakehashedsecret",
        "disabled": False,
    },
    "alice": {
        "username": "alice",
        "full_name": "Alice Wonderson",
        "email": "alice@example.com",
        "hashed_password": "fakehashedsecret2",
        "disabled": True,
    },
}


class RequiresLoginException(Exception):
    pass


app = FastAPI()
# socketio
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
app.mount("/ws", socketio.ASGIApp(sio))

app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000/"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.mount(
    "/static",
    StaticFiles(directory=pathlib.Path(__file__).parent / "templates"),
    name="static",
)


# This is not really required for simple use case, but if we have a lot views
# and want to protect them, a common redirect logic is convenient.
@app.exception_handler(RequiresLoginException)
async def exception_handler(*args, **kwargs) -> Response:
    return RedirectResponse(url="/", status_code=303)


def verify_session_id(request: Request, session_id: Optional[str] = Cookie(...)):
    """Verify the session_id in the fake db.
    If it doesn't exist raise an exception to redirect to Login page"""
    username = request.session.get(session_id)
    if username not in fake_users_db:
        # raise an exception so that we can redirect to the login
        # if there's no `session_id`` passed or wrong `session_id`` is given
        raise RequiresLoginException
    return username


@app.get("/view")
async def view(request: Request, username: str = Depends(verify_session_id)):
    await sio.emit("message", "hello universe")
    return templates.TemplateResponse(
        "view.html",
        {
            "request": request,
            "current_user": username,
            "start_time": request.session.get("start_time", int(time.time())),
            "PORT": os.environ.get("PORT", 8000),
        },
    )


@app.get("/")
def index(request: Request):
    # if there's some session, the user may likely be logged in
    # try redirecting to the /view
    if request.session:
        return RedirectResponse(url="/view", status_code=303)
    return templates.TemplateResponse("index.html", {"request": request})


@app.post("/login")
async def login(request: Request, username: str = Form(...), password: str = Form(...)):
    """Get `username` and `password` from form data and authenticate the user
    If username doesn't exist, redirect to Login page.
    Else continue to `/view` page
    """
    # for simplicity we will only check the `username`` exists
    # we can add a `password` check if required
    if username not in fake_users_db:
        response = RedirectResponse(url="/", status_code=303)
        return response
    # why we need to set the status_code to `303` can be seen in the below git issue comment
    # https://github.com/encode/starlette/issues/632#issuecomment-527258349
    response = RedirectResponse(url="/view", status_code=303)
    session_id = secrets.token_hex(16)
    request.session.update(
        {
            session_id: username,
            "start_time": int(time.time()),
            "username": username,
        }
    )
    response.set_cookie("session_id", session_id)
    return response


@app.get("/logout", name="logout")
async def logout(request: Request, username: str = Depends(verify_session_id)):
    """Logout and redirect to Login screen"""
    request.session.clear()
    response = RedirectResponse(url="/", status_code=303)
    response.set_cookie("session_id", None)
    await sio.emit("logout", username)
    return response


@sio.event
async def connect(sid, environ):
    print('connect')
    session = environ["asgi.scope"]["session"]
    await sio.emit("new user", session)


@sio.event
async def message(sid, data):
    await sio.emit("message", data, room=sid)

E:\song\agv_fastapi_socket2\fastapi-socketio-example-main\main.py


import uvicorn
if __name__ == '__main__':
    uvicorn.run("app:app", host='127.0.0.1', port=8000, reload=True)

E:\song\agv_fastapi_socket2\fastapi-socketio-example-main\templates\index.html

<html>

<head>
    <title>Index</title>
    <style>
    </style>
</head>

<body>
    <div class="form-center">
        <form action="/login" method="post">
            <input type="text" name="username" placeholder="username" required>
            <br/> <br/>
            <input type="password" name="password" placeholder="password" required>
            <br/> <br/>
            <input type="submit" value="Login">
        </form>
    </div>
</body>

</html>```
# `E:\song\agv_fastapi_socket2\fastapi-socketio-example-main\templates\view.html`

```html
<html>

<head>
    <title>View</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.4.0/socket.io.js" integrity="sha512-nYuHvSAhY5lFZ4ixSViOwsEKFvlxHMU2NHts1ILuJgOS6ptUmAGt/0i5czIgMOahKZ6JN84YFDA+mCdky7dD8A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
    <p>Hello {{ current_user }}</p>
    <span id='ct'></span>
    <br/>
    <button type="button"><a id="logoutBtn" href="{{ url_for('logout') }}" style="text-decoration: none;">Logout</a></button>
    <br />
    <i>Note</i> <br/>
    This msg box is just a convenient way if the web socket works [for testing]
    <br />
    <input id="textInput" placeholder="message">
    <button id="sendBtn">Send</button>

    <ul>
        
    </ul>

</body>
<script defer type="text/javascript">
    const startTime = {{ start_time }}
    const currentUser = '{{ current_user }}'
    const PORT = '{{ PORT }}'
    function display_c() {
        var refresh = 1000;
        mytime = setTimeout('display_ct()', refresh)
    }

    function display_ct() {
        var x = new Date()
        document.getElementById('ct').innerHTML = x;
        display_c();
    }

    // Support TLS-specific URLs, when appropriate.
        if (window.location.protocol == "https:") {
            var ws_scheme = "wss://";
        } else {
            var ws_scheme = "ws://"
        };

    const socket = io(ws_scheme + location.host, {path: '/ws/socket.io/'});

    socket.on('new user', data => {
        socket.user = data.username
        console.log({ data });
    });

    socket.on('message', text => {
        const el = document.createElement('li');
        el.innerHTML = text + socket.user;
        document.querySelector('ul').appendChild(el);
    });


    document.getElementById('sendBtn').onclick = () => {
        const text = document.getElementById('textInput').value;
        socket.emit('message', text)
    }

    const logoutBtn = document.getElementById('logoutBtn')

    socket.on('logout', userName => {
        if ( currentUser === userName) {
            socket.disconnect();
            logoutBtn.click();
        }
    });

    display_ct();
</script>
</html>
posted on 2022-11-16 14:11  lazycookie  阅读(298)  评论(0编辑  收藏  举报