Compare commits
No commits in common. "dc1f156025246745d80382fe0e5a069bcce6ec85" and "478804e414a5756fbc1582967104f6da827014c8" have entirely different histories.
dc1f156025
...
478804e414
5 changed files with 25 additions and 82 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM python:3.13.0
|
FROM python:3
|
||||||
COPY . /app
|
COPY . /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
|
|
33
README.md
33
README.md
|
@ -1,33 +0,0 @@
|
||||||
# LiteY
|
|
||||||
|
|
||||||
軽量な掲示板です
|
|
||||||
|
|
||||||
## デバッグの開始
|
|
||||||
|
|
||||||
依存関係をインストールします
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
データベースを起動します
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run --detach \
|
|
||||||
--name litey-mongo-debug \
|
|
||||||
--volume litey-mongo-debug:/data/db \
|
|
||||||
--publish 27017:27017 \
|
|
||||||
mongo
|
|
||||||
```
|
|
||||||
|
|
||||||
サーバーを起動します
|
|
||||||
|
|
||||||
```bash
|
|
||||||
fastapi dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## デプロイ
|
|
||||||
|
|
||||||
今すぐデプロイ!
|
|
||||||
|
|
||||||
- https://litey.trade/
|
|
56
app.py
56
app.py
|
@ -1,12 +1,11 @@
|
||||||
from typing import Callable, Awaitable, Optional, List
|
from typing import Callable, Awaitable, Optional, List
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from os import environ
|
from os import environ
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from re import escape, compile, IGNORECASE
|
from re import escape, compile, IGNORECASE
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
from requests import get
|
from requests import get
|
||||||
from fastapi import FastAPI, Request, Response, status
|
from fastapi import FastAPI, Request, Response, status
|
||||||
|
@ -14,7 +13,6 @@ from fastapi.responses import JSONResponse, Response, PlainTextResponse, FileRes
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from pymongo import MongoClient, DESCENDING
|
from pymongo import MongoClient, DESCENDING
|
||||||
from contextlib import asynccontextmanager
|
|
||||||
|
|
||||||
# Jinja2
|
# Jinja2
|
||||||
|
|
||||||
|
@ -22,7 +20,7 @@ def ip_to_uid(ip: Optional[str]) -> str:
|
||||||
if not ip:
|
if not ip:
|
||||||
return "-"
|
return "-"
|
||||||
|
|
||||||
return b64encode(ip.encode("utf-8")).decode("utf-8")[-11:]
|
return b64encode(ip.encode("utf-8")).decode("utf-8")[:11]
|
||||||
|
|
||||||
def replace_ng_words(src: str, ng_words: List[str]) -> str:
|
def replace_ng_words(src: str, ng_words: List[str]) -> str:
|
||||||
result = src
|
result = src
|
||||||
|
@ -41,39 +39,21 @@ def content_to_linksets(content: str) -> str:
|
||||||
groups = pattern.findall(content)
|
groups = pattern.findall(content)
|
||||||
return "\n".join(groups)
|
return "\n".join(groups)
|
||||||
|
|
||||||
def is_over_n_hours(src: datetime, hours: int) -> bool:
|
|
||||||
now = datetime.now()
|
|
||||||
return now - src.replace(tzinfo=None) > timedelta(hours=hours)
|
|
||||||
|
|
||||||
# 初期化
|
# 初期化
|
||||||
|
|
||||||
ctx = {}
|
templates = Jinja2Templates("templates")
|
||||||
|
|
||||||
@asynccontextmanager
|
templates.env.filters["ip_to_uid"] = ip_to_uid
|
||||||
async def lifespan(app: FastAPI):
|
templates.env.filters["replace_ng_words"] = replace_ng_words
|
||||||
ctx["templates"] = Jinja2Templates("templates")
|
templates.env.filters["content_to_linksets"] = content_to_linksets
|
||||||
|
|
||||||
ctx["templates"].env.filters["ip_to_uid"] = ip_to_uid
|
mongo_client = MongoClient(
|
||||||
ctx["templates"].env.filters["replace_ng_words"] = replace_ng_words
|
|
||||||
ctx["templates"].env.filters["content_to_linksets"] = content_to_linksets
|
|
||||||
ctx["templates"].env.filters["fromisoformat"] = datetime.fromisoformat
|
|
||||||
ctx["templates"].env.filters["is_over_n_hours"] = is_over_n_hours
|
|
||||||
|
|
||||||
ctx["mongo_client"] = MongoClient(
|
|
||||||
environ.get("MONGO_URI", "mongodb://127.0.0.1:27017/"),
|
environ.get("MONGO_URI", "mongodb://127.0.0.1:27017/"),
|
||||||
username=environ.get("MONGO_USER"),
|
username=environ.get("MONGO_USER"),
|
||||||
password=environ.get("MONGO_PASSWORD")
|
password=environ.get("MONGO_PASSWORD")
|
||||||
)
|
)
|
||||||
|
|
||||||
#uuid重複する?考えすぎ?
|
mongo_client.litey.ngs.create_index("word", unique=True)
|
||||||
#ctx["mongo_client"].litey.notes.create_index("id", unique=True)
|
|
||||||
ctx["mongo_client"].litey.ngs.create_index("word", unique=True)
|
|
||||||
|
|
||||||
pprint(ctx)
|
|
||||||
yield
|
|
||||||
ctx["mongo_client"].close()
|
|
||||||
|
|
||||||
ctx.clear()
|
|
||||||
|
|
||||||
# スニペット
|
# スニペット
|
||||||
|
|
||||||
|
@ -115,18 +95,18 @@ def get_ip(req: Request) -> str:
|
||||||
|
|
||||||
def get_litey_notes(id: str = None) -> List[dict]:
|
def get_litey_notes(id: str = None) -> List[dict]:
|
||||||
if not id:
|
if not id:
|
||||||
cursor = ctx["mongo_client"].litey.notes.find({}, { "_id": False }).sort("date", DESCENDING)
|
cursor = mongo_client.litey.notes.find({}, { "_id": False }).sort("date", DESCENDING)
|
||||||
return list(cursor)
|
return list(cursor)
|
||||||
|
|
||||||
return ctx["mongo_client"].litey.notes.find_one({ "id": id }, { "_id": False })
|
return mongo_client.litey.notes.find_one({ "id": id }, { "_id": False })
|
||||||
|
|
||||||
def get_ng_words() -> List[str]:
|
def get_ng_words() -> List[str]:
|
||||||
cursor = ctx["mongo_client"].litey.ngs.find({}, { "_id": False })
|
cursor = mongo_client.litey.ngs.find({}, { "_id": False })
|
||||||
return [ng["word"] for ng in list(cursor) if "word" in ng]
|
return [ng["word"] for ng in list(cursor) if "word" in ng]
|
||||||
|
|
||||||
# FastAPI
|
# FastAPI
|
||||||
|
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI()
|
||||||
|
|
||||||
@app.middleware("http")
|
@app.middleware("http")
|
||||||
async def cors_handler(req: Request, call_next: Callable[[Request], Awaitable[Response]]):
|
async def cors_handler(req: Request, call_next: Callable[[Request], Awaitable[Response]]):
|
||||||
|
@ -152,7 +132,7 @@ async def api_get(id: str = None):
|
||||||
|
|
||||||
@app.post("/api/litey/post")
|
@app.post("/api/litey/post")
|
||||||
async def api_post(item: LiteYItem, req: Request):
|
async def api_post(item: LiteYItem, req: Request):
|
||||||
ctx["mongo_client"].litey.notes.insert_one({
|
mongo_client.litey.notes.insert_one({
|
||||||
"id": str(uuid4()),
|
"id": str(uuid4()),
|
||||||
"content": item.content,
|
"content": item.content,
|
||||||
"date": datetime.now().astimezone(timezone.utc).isoformat(),
|
"date": datetime.now().astimezone(timezone.utc).isoformat(),
|
||||||
|
@ -163,9 +143,7 @@ async def api_post(item: LiteYItem, req: Request):
|
||||||
|
|
||||||
@app.post("/api/litey/delete")
|
@app.post("/api/litey/delete")
|
||||||
async def api_delete(item: LiteYDeleteItem):
|
async def api_delete(item: LiteYDeleteItem):
|
||||||
ctx["mongo_client"].litey.notes.delete_one({
|
mongo_client.litey.notes.delete_one({ "id": item.id })
|
||||||
"id": item.id
|
|
||||||
})
|
|
||||||
|
|
||||||
return PlainTextResponse("OK")
|
return PlainTextResponse("OK")
|
||||||
|
|
||||||
|
@ -192,7 +170,7 @@ async def api_ng_get():
|
||||||
|
|
||||||
@app.post("/api/ng/post")
|
@app.post("/api/ng/post")
|
||||||
async def api_ng_post(item: NGItem):
|
async def api_ng_post(item: NGItem):
|
||||||
ctx["mongo_client"].litey.ngs.insert_one({
|
mongo_client.litey.ngs.insert_one({
|
||||||
"word": item.word
|
"word": item.word
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -200,7 +178,7 @@ async def api_ng_post(item: NGItem):
|
||||||
|
|
||||||
@app.post("/api/ng/delete")
|
@app.post("/api/ng/delete")
|
||||||
async def api_ng_delete(item: NGItem):
|
async def api_ng_delete(item: NGItem):
|
||||||
ctx["mongo_client"].litey.ngs.delete_one({
|
mongo_client.litey.ngs.delete_one({
|
||||||
"word": item.word
|
"word": item.word
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -208,7 +186,7 @@ async def api_ng_delete(item: NGItem):
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def home(req: Request):
|
async def home(req: Request):
|
||||||
res = ctx["templates"].TemplateResponse(req, "index.html", {
|
res = templates.TemplateResponse(req, "index.html", {
|
||||||
"notes": get_litey_notes(),
|
"notes": get_litey_notes(),
|
||||||
"ng_words": get_ng_words()
|
"ng_words": get_ng_words()
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
fastapi[standard]==0.115.5
|
fastapi==0.111.0
|
||||||
pymongo==4.10.1
|
pymongo==4.7.3
|
||||||
Jinja2==3.1.4
|
Jinja2==3.1.4
|
||||||
|
|
|
@ -18,9 +18,7 @@
|
||||||
<div>{{ note.content | replace_ng_words(ng_words) }}</div>
|
<div>{{ note.content | replace_ng_words(ng_words) }}</div>
|
||||||
<div id="attachments" data-linksets="{{ note.content | content_to_linksets | urlencode() }}"></div>
|
<div id="attachments" data-linksets="{{ note.content | content_to_linksets | urlencode() }}"></div>
|
||||||
<code id="date" data-date="{{ note.date | urlencode() }}"></code>
|
<code id="date" data-date="{{ note.date | urlencode() }}"></code>
|
||||||
{% if not ("/ninja" in note.content and not note.date | fromisoformat | is_over_n_hours(72)) %}
|
|
||||||
<input type="submit" value="削除" onclick="noteDelete(this);" data-id="{{ note.id | urlencode() }}" data-preview="{{ note.content | urlencode() }}">
|
<input type="submit" value="削除" onclick="noteDelete(this);" data-id="{{ note.id | urlencode() }}" data-preview="{{ note.content | urlencode() }}">
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Add table
Reference in a new issue