Compare commits

...

1 Commits

Author SHA1 Message Date
26b64f8c15 add: auto link feature 2025-01-17 16:33:39 +00:00
5 changed files with 49 additions and 23 deletions

View File

@@ -2,10 +2,10 @@ FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
WORKDIR /app WORKDIR /app
RUN apt-get update &&\ #RUN apt-get update &&\
apt-get install -y default-mysql-client &&\ # apt-get install -y default-mysql-client &&\
apt-get clean &&\ # apt-get clean &&\
rm -rf /var/lib/apt/lists/* # rm -rf /var/lib/apt/lists/*
COPY requirements.txt ./requirements.txt COPY requirements.txt ./requirements.txt
RUN pip install --no-cache-dir -r ./requirements.txt RUN pip install --no-cache-dir -r ./requirements.txt

View File

@@ -1,5 +1,4 @@
import shutil import shutil
import zipfile
from datetime import datetime from datetime import datetime
from flask import Blueprint, send_file, jsonify from flask import Blueprint, send_file, jsonify
@@ -60,6 +59,7 @@ def traverse(path_id, paths):
with open(f"{md.title}.mdmeta", "w") as meta_file: with open(f"{md.title}.mdmeta", "w") as meta_file:
meta_file.write(f"created_at: {md.created_at}\n") meta_file.write(f"created_at: {md.created_at}\n")
meta_file.write(f"order: {md.order}\n") meta_file.write(f"order: {md.order}\n")
meta_file.write(f"shortcut: {md.shortcut}\n")
children = [c for c in paths.values() if c.parent_id == path_id] children = [c for c in paths.values() if c.parent_id == path_id]
for child in children: for child in children:
traverse(child.id, paths) traverse(child.id, paths)

View File

@@ -1,6 +1,5 @@
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from sqlalchemy import or_ from sqlalchemy import or_
import api import api
from api import require_auth, etag_response from api import require_auth, etag_response
from contexts.RequestContext import RequestContext from contexts.RequestContext import RequestContext
@@ -29,6 +28,7 @@ def get_home():
if markdown is None: if markdown is None:
return jsonify({}), 204 return jsonify({}), 204
return jsonify(markdown.to_dict()), 200 return jsonify(markdown.to_dict()), 200
@markdown_bp.route('/by_path/<int:path_id>', methods=['GET']) @markdown_bp.route('/by_path/<int:path_id>', methods=['GET'])
@limiter.limit(api.get_rate_limit) @limiter.limit(api.get_rate_limit)
@etag_response @etag_response
@@ -36,6 +36,7 @@ def get_markdowns_by_path(path_id):
with get_db() as session: with get_db() as session:
markdowns = session.query(Markdown).filter(Markdown.path_id == path_id).all() markdowns = session.query(Markdown).filter(Markdown.path_id == path_id).all()
return jsonify([md.to_dict() for md in markdowns]), 200 return jsonify([md.to_dict() for md in markdowns]), 200
@markdown_bp.route('/get_index/<int:path_id>', methods=['GET']) @markdown_bp.route('/get_index/<int:path_id>', methods=['GET'])
@limiter.limit(api.get_rate_limit) @limiter.limit(api.get_rate_limit)
@etag_response @etag_response
@@ -66,11 +67,16 @@ def create_markdown():
title = data.get('title') title = data.get('title')
content = data.get('content') content = data.get('content')
path_id = data.get('path_id') path_id = data.get('path_id')
shortcut = data.get('shortcut', "")
if not title or not content: if not title or not content:
return jsonify({"error": "missing required fields"}), 400 return jsonify({"error": "missing required fields"}), 400
new_markdown = Markdown(title=title, content=content, path_id=path_id) new_markdown = Markdown(title=title, content=content, path_id=path_id, shortcut=shortcut)
with get_db() as session: with get_db() as session:
try: try:
if shortcut != "":
r = session.query(Markdown).filter(Markdown.shortcut == shortcut).all()
if len(r) > 0:
return jsonify({"error": "duplicate shortcut"}), 400
session.add(new_markdown) session.add(new_markdown)
session.commit() session.commit()
return jsonify(new_markdown.to_dict()), 201 return jsonify(new_markdown.to_dict()), 201
@@ -89,10 +95,19 @@ def update_markdown(markdown_id):
if markdown is None: if markdown is None:
return jsonify({"error": "file not found"}), 404 return jsonify({"error": "file not found"}), 404
data = request.json data = request.json
if data.get('shortcut', "") != "":
r = session.query(Markdown).filter(
Markdown.shortcut == data.get('shortcut')
).filter(
Markdown.id != markdown_id
).all()
if len(r) > 0:
return jsonify({"error": "duplicate shortcut"}), 400
if request.method == "PUT": if request.method == "PUT":
markdown.title = data.get('title') markdown.title = data.get('title')
markdown.content = data.get('content') markdown.content = data.get('content')
markdown.path_id = data.get('path_id') markdown.path_id = data.get('path_id')
markdown.shortcut = data.get('shortcut', '')
elif request.method == "PATCH": elif request.method == "PATCH":
if 'title' in data: if 'title' in data:
markdown.title = data.get('title') markdown.title = data.get('title')
@@ -100,6 +115,8 @@ def update_markdown(markdown_id):
markdown.content = data.get('content') markdown.content = data.get('content')
if 'path_id' in data: if 'path_id' in data:
markdown.path_id = data.get('path_id') markdown.path_id = data.get('path_id')
if 'shortcut' in data:
markdown.shortcut = data.get('shortcut')
session.commit() session.commit()
return jsonify(markdown.to_dict()), 200 return jsonify(markdown.to_dict()), 200
@@ -157,8 +174,16 @@ def move_backward(markdown_id):
@markdown_bp.route('/search/<string:keyword>', methods=['GET']) @markdown_bp.route('/search/<string:keyword>', methods=['GET'])
@limiter.limit(api.get_rate_limit) @limiter.limit(api.get_rate_limit)
def search_markdowns(keyword): def search_markdowns(keyword):
with (get_db() as session): with get_db() as session:
res = session.query(Markdown).filter( res = session.query(Markdown).filter(
or_(Markdown.title.like(keyword), Markdown.content.like(keyword)) or_(Markdown.title.like(keyword), Markdown.content.like(keyword))
).all() ).all()
return jsonify([md.to_dict() for md in res]), 200 return jsonify([md.to_dict() for md in res]), 200
@markdown_bp.route('/links', methods=['GET'])
@limiter.limit(api.get_rate_limit)
def get_links():
with get_db() as session:
mds = [md.to_dict() for md in session.query(Markdown).filter(Markdown.shortcut != "").all()]
links = [f"[{md['shortcut']}]: {md['id']}" for md in mds]
return jsonify(links), 200

View File

@@ -21,14 +21,14 @@ def get_db():
finally: finally:
db.close() db.close()
def dump_db(): # def dump_db():
try: # try:
os.environ['MYSQL_PWD'] = DB_PASSWORD # os.environ['MYSQL_PWD'] = DB_PASSWORD
dump_cmd = f"mysqldump --no-tablespaces -h {DB_HOST} -P {DB_PORT} -u {DB_USER} {DB_NAME} > /app/dump/db_dump.sql" # dump_cmd = f"mysqldump --no-tablespaces -h {DB_HOST} -P {DB_PORT} -u {DB_USER} {DB_NAME} > /app/dump/db_dump.sql"
subprocess.run(dump_cmd, shell=True, check=True) # subprocess.run(dump_cmd, shell=True, check=True)
except subprocess.CalledProcessError as e: # except subprocess.CalledProcessError as e:
print(f"Failed to dump database: {e}") # print(f"Failed to dump database: {e}")
raise e # raise e
def clear_db(): def clear_db():
with engine.connect() as conn: with engine.connect() as conn:
@@ -73,11 +73,11 @@ def init_payload():
def setup_db(): def setup_db():
if DB_SCHEMA_UPDATED: if DB_SCHEMA_UPDATED:
try: # try:
dump_db() # dump_db()
print("[ x ] db dumped") # print("[ x ] db dumped")
except Exception as e: # except Exception as e:
print(f"[ x ] Failed to dump database: {e}") # print(f"[ x ] Failed to dump database: {e}")
clear_db() clear_db()
print("[ x ] db cleared") print("[ x ] db cleared")
create_all() create_all()

View File

@@ -1,6 +1,6 @@
import uuid import uuid
from sqlalchemy import Column, Text, Integer, String, DateTime, ForeignKey, Float, text from sqlalchemy import Column, Text, Integer, String, DateTime, ForeignKey, Float, text, UniqueConstraint
from db.models import Base from db.models import Base
import datetime import datetime
@@ -12,7 +12,7 @@ class Markdown(Base):
created_at = Column(DateTime, default=datetime.datetime.utcnow) created_at = Column(DateTime, default=datetime.datetime.utcnow)
path_id = Column(Integer, ForeignKey('path.id'), nullable=False) path_id = Column(Integer, ForeignKey('path.id'), nullable=False)
order = Column(String(36), default=lambda: str(uuid.uuid4())) order = Column(String(36), default=lambda: str(uuid.uuid4()))
shortcut = Column(String(36), default="")
def to_dict(self): def to_dict(self):
return { return {
'id': self.id, 'id': self.id,
@@ -21,6 +21,7 @@ class Markdown(Base):
'created_at': self.created_at, 'created_at': self.created_at,
'path_id': self.path_id, 'path_id': self.path_id,
'order': self.order, 'order': self.order,
'shortcut': self.shortcut,
} }
__pay_load__ = { __pay_load__ = {
'dev': [ 'dev': [