Compare commits
10 commits
478804e414
...
dc1f156025
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dc1f156025 | ||
![]() |
161343909a | ||
![]() |
d4575837d4 | ||
![]() |
071ffab88a | ||
![]() |
7a3db8c48c | ||
![]() |
d5ca0a68da | ||
![]() |
bc6b871b6c | ||
![]() |
36247d7607 | ||
![]() |
dc6dd3fe3f | ||
![]() |
c22936c670 |
5 changed files with 82 additions and 25 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM python:3
|
FROM python:3.13.0
|
||||||
COPY . /app
|
COPY . /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN pip install -r requirements.txt
|
RUN pip install -r requirements.txt
|
||||||
|
|
33
README.md
Normal file
33
README.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# 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/
|
64
app.py
64
app.py
|
@ -1,11 +1,12 @@
|
||||||
from typing import Callable, Awaitable, Optional, List
|
from typing import Callable, Awaitable, Optional, List
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone, timedelta
|
||||||
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
|
||||||
|
@ -13,6 +14,7 @@ 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
|
||||||
|
|
||||||
|
@ -20,7 +22,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
|
||||||
|
@ -39,21 +41,39 @@ 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)
|
||||||
|
|
||||||
# 初期化
|
# 初期化
|
||||||
|
|
||||||
templates = Jinja2Templates("templates")
|
ctx = {}
|
||||||
|
|
||||||
templates.env.filters["ip_to_uid"] = ip_to_uid
|
@asynccontextmanager
|
||||||
templates.env.filters["replace_ng_words"] = replace_ng_words
|
async def lifespan(app: FastAPI):
|
||||||
templates.env.filters["content_to_linksets"] = content_to_linksets
|
ctx["templates"] = Jinja2Templates("templates")
|
||||||
|
|
||||||
mongo_client = MongoClient(
|
ctx["templates"].env.filters["ip_to_uid"] = ip_to_uid
|
||||||
environ.get("MONGO_URI", "mongodb://127.0.0.1:27017/"),
|
ctx["templates"].env.filters["replace_ng_words"] = replace_ng_words
|
||||||
username=environ.get("MONGO_USER"),
|
ctx["templates"].env.filters["content_to_linksets"] = content_to_linksets
|
||||||
password=environ.get("MONGO_PASSWORD")
|
ctx["templates"].env.filters["fromisoformat"] = datetime.fromisoformat
|
||||||
)
|
ctx["templates"].env.filters["is_over_n_hours"] = is_over_n_hours
|
||||||
|
|
||||||
mongo_client.litey.ngs.create_index("word", unique=True)
|
ctx["mongo_client"] = MongoClient(
|
||||||
|
environ.get("MONGO_URI", "mongodb://127.0.0.1:27017/"),
|
||||||
|
username=environ.get("MONGO_USER"),
|
||||||
|
password=environ.get("MONGO_PASSWORD")
|
||||||
|
)
|
||||||
|
|
||||||
|
#uuid重複する?考えすぎ?
|
||||||
|
#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()
|
||||||
|
|
||||||
# スニペット
|
# スニペット
|
||||||
|
|
||||||
|
@ -95,18 +115,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 = mongo_client.litey.notes.find({}, { "_id": False }).sort("date", DESCENDING)
|
cursor = ctx["mongo_client"].litey.notes.find({}, { "_id": False }).sort("date", DESCENDING)
|
||||||
return list(cursor)
|
return list(cursor)
|
||||||
|
|
||||||
return mongo_client.litey.notes.find_one({ "id": id }, { "_id": False })
|
return ctx["mongo_client"].litey.notes.find_one({ "id": id }, { "_id": False })
|
||||||
|
|
||||||
def get_ng_words() -> List[str]:
|
def get_ng_words() -> List[str]:
|
||||||
cursor = mongo_client.litey.ngs.find({}, { "_id": False })
|
cursor = ctx["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()
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
@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]]):
|
||||||
|
@ -132,7 +152,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):
|
||||||
mongo_client.litey.notes.insert_one({
|
ctx["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(),
|
||||||
|
@ -143,7 +163,9 @@ 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):
|
||||||
mongo_client.litey.notes.delete_one({ "id": item.id })
|
ctx["mongo_client"].litey.notes.delete_one({
|
||||||
|
"id": item.id
|
||||||
|
})
|
||||||
|
|
||||||
return PlainTextResponse("OK")
|
return PlainTextResponse("OK")
|
||||||
|
|
||||||
|
@ -170,7 +192,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):
|
||||||
mongo_client.litey.ngs.insert_one({
|
ctx["mongo_client"].litey.ngs.insert_one({
|
||||||
"word": item.word
|
"word": item.word
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -178,7 +200,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):
|
||||||
mongo_client.litey.ngs.delete_one({
|
ctx["mongo_client"].litey.ngs.delete_one({
|
||||||
"word": item.word
|
"word": item.word
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -186,7 +208,7 @@ async def api_ng_delete(item: NGItem):
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def home(req: Request):
|
async def home(req: Request):
|
||||||
res = templates.TemplateResponse(req, "index.html", {
|
res = ctx["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==0.111.0
|
fastapi[standard]==0.115.5
|
||||||
pymongo==4.7.3
|
pymongo==4.10.1
|
||||||
Jinja2==3.1.4
|
Jinja2==3.1.4
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
<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>
|
||||||
<input type="submit" value="削除" onclick="noteDelete(this);" data-id="{{ note.id | urlencode() }}" data-preview="{{ note.content | urlencode() }}">
|
{% 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() }}">
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Add table
Reference in a new issue