2023-08-02 17:10:20 +09:00
from flask import Flask , render_template , request , redirect , url_for , flash , session
import pymysql
from flask import session , g , jsonify
import bcrypt
from bs4 import BeautifulSoup
from werkzeug . utils import secure_filename
import os
import uuid
2023-09-13 17:04:52 +09:00
import re
2023-08-03 21:09:18 +09:00
from markupsafe import Markup
from jinja2 import filters
2023-08-02 17:10:20 +09:00
UPLOAD_FOLDER = ' static/upload/img ' # 경로를 Flask 앱 루트 기준으로 수정
ALLOWED_EXTENSIONS = { ' png ' , ' jpg ' , ' jpeg ' , ' gif ' }
app = Flask ( __name__ )
app . secret_key = ' your secret key '
app . config [ ' UPLOAD_FOLDER ' ] = UPLOAD_FOLDER
2023-09-13 17:04:52 +09:00
def remove_html_tags ( text ) :
clean = re . compile ( ' <.*?> ' )
return re . sub ( clean , ' ' , text )
2023-08-02 17:10:20 +09:00
@app.before_request
def load_user ( ) :
if ' user_info ' in session :
g . is_login = True
g . user_info = session [ ' user_info ' ]
def allowed_file ( filename ) :
return ' . ' in filename and \
filename . rsplit ( ' . ' , 1 ) [ 1 ] . lower ( ) in ALLOWED_EXTENSIONS
@app.route ( ' /upload_image ' , methods = [ ' POST ' ] )
def upload_image ( ) :
if ' file ' not in request . files :
return jsonify ( success = False , message = ' No file part ' ) , 400
file = request . files [ ' file ' ]
if file . filename == ' ' :
return jsonify ( success = False , message = ' No selected file ' ) , 400
if file and allowed_file ( file . filename ) :
# Secure the filename and keep its extension
filename = secure_filename ( file . filename )
ext = filename . rsplit ( ' . ' , 1 ) [ 1 ] . lower ( )
# Generate a random filename using uuid4
random_filename = f ' { uuid . uuid4 ( ) . hex } . { ext } '
file_path = os . path . join ( app . config [ ' UPLOAD_FOLDER ' ] , random_filename )
# Create directories if not exist
os . makedirs ( os . path . dirname ( file_path ) , exist_ok = True )
file . save ( file_path )
# Generate the URL of the saved image file
file_url = url_for ( ' static ' , filename = ' upload/img/ ' + random_filename )
return jsonify ( success = True , fileUrl = file_url ) , 200
return jsonify ( success = False , message = ' File not allowed ' ) , 400
# MySQL 데이터베이스 연결 설정
def connnect_db ( ) :
return pymysql . connect (
host = ' wxnasso.synology.me ' ,
user = ' wixon5 ' ,
password = ' Wixon2022@! ' ,
2023-08-03 21:09:18 +09:00
database = ' test ' ,
2023-08-02 17:10:20 +09:00
charset = " utf8mb4 " ,
cursorclass = pymysql . cursors . DictCursor , # DictCursor를 사용하여 딕셔너리 형태로 결과를 반환
init_command = ' SET SQL_SAFE_UPDATES = 0; ' ,
)
def sql_execute ( q , d , is_data = False , is_last_id = False ) :
data = None
last_id = None
with connnect_db ( ) . cursor ( pymysql . cursors . DictCursor ) as cursor :
try :
res = cursor . execute ( q , d )
data = cursor . fetchall ( ) if is_data else None
last_id = cursor . lastrowid if cursor . lastrowid != 0 else None
cursor . connection . commit ( )
except Exception as e :
print ( e )
cursor . connection . rollback ( )
res = False
return ( res , last_id if is_last_id else data ) if is_data or is_last_id else res
@app.route ( ' / ' )
def index ( ) :
2023-09-13 17:04:52 +09:00
if ' user_info ' in session : # 로그인된 사용자
query = " SELECT `id`, `title`, `category`, `thumbnail_img`, `contents`, `add_date` FROM `blog` WHERE `use_yn` = ' Y ' ORDER BY `add_date` DESC limit 6; "
r_query = " SELECT `id`, `title`, `category`, `thumbnail_img`, `contents`, `add_date` FROM `blog` WHERE `use_yn` = ' Y ' ORDER BY RAND() DESC limit 1; "
else :
query = " SELECT `id`, `title`, `category`, `thumbnail_img`, `contents`, `add_date` FROM `blog` WHERE `use_yn` = ' Y ' and `public_yn` = ' Y ' ORDER BY `add_date` DESC limit 6; "
r_query = " SELECT `id`, `title`, `category`, `thumbnail_img`, `contents`, `add_date` FROM `blog` WHERE `use_yn` = ' Y ' and `public_yn` = ' Y ' ORDER BY RAND() DESC limit 1; "
2023-08-03 21:09:18 +09:00
2023-08-02 17:10:20 +09:00
r , posts = sql_execute ( query , ( ) , is_data = True )
2023-09-13 17:04:52 +09:00
r , random_post = sql_execute ( r_query , ( ) , is_data = True )
posts . append ( random_post [ 0 ] )
2023-09-13 17:14:36 +09:00
# 태그 제거 후 150자로 제한
2023-09-13 17:04:52 +09:00
for post in posts :
post [ ' contents ' ] = remove_html_tags ( post [ ' contents ' ] ) [ : 150 ]
2023-08-02 17:10:20 +09:00
2023-09-13 17:04:52 +09:00
return render_template ( ' index.html ' , posts = posts )
2023-08-02 17:10:20 +09:00
@app.route ( ' /login ' , methods = [ ' GET ' , ' POST ' ] )
def login ( ) :
if request . method == ' POST ' :
username = request . form . get ( ' username ' )
password = request . form . get ( ' password ' ) . encode ( ' utf-8 ' )
# DB에서 사용자 정보 가져오기
res , user = sql_execute ( " SELECT * FROM member WHERE mb_id= %s " , ( username , ) , is_data = True )
if res and user and bcrypt . checkpw ( password , user [ 0 ] [ ' mb_passwd ' ] . encode ( ' utf-8 ' ) ) :
session [ ' username ' ] = username
session [ ' user_info ' ] = user [ 0 ]
return redirect ( url_for ( ' index ' ) )
else :
flash ( ' Username or password is incorrect ' )
return render_template ( ' login.html ' )
@app.route ( ' /logout ' )
def logout ( ) :
session . clear ( )
g . is_login = False
g . user_info = None
return redirect ( url_for ( ' index ' ) )
2023-08-03 21:09:18 +09:00
@app.route ( ' /post/<int:post_id> ' )
def post ( post_id ) :
# SQL 쿼리와 삽입할 데이터 설정
query = " SELECT blog.*, member.mb_name, member.mb_id FROM blog INNER JOIN member ON blog.user_id = member.mb_idx WHERE blog.id = %s "
data = ( post_id , )
# Post를 데이터베이스에서 검색
result , fetched_data = sql_execute ( query , data , is_data = True )
if result :
# 검색 성공시, 포스트 상세 페이지로 이동
post = fetched_data [ 0 ]
# 이전과 다음 포스트를 찾기
if ' user_info ' in session : # 로그인된 사용자가 있을 경우
# 외부 공개 안된 글도 포함하여 모든 블로그 포스트를 가져옵니다.
prev_query = " SELECT blog.*, member.mb_name, member.mb_id FROM blog INNER JOIN member ON blog.user_id = member.mb_idx WHERE blog.add_date < %s AND blog.use_yn = ' Y ' ORDER BY blog.add_date DESC LIMIT 1 "
next_query = " SELECT blog.*, member.mb_name, member.mb_id FROM blog INNER JOIN member ON blog.user_id = member.mb_idx WHERE blog.add_date > %s AND blog.use_yn = ' Y ' ORDER BY blog.add_date ASC LIMIT 1 "
else : # 로그인된 사용자가 없을 경우
# 외부 공개된 블로그 포스트만 가져옵니다.
prev_query = " SELECT blog.*, member.mb_name, member.mb_id FROM blog INNER JOIN member ON blog.user_id = member.mb_idx WHERE blog.add_date < %s AND blog.public_yn = ' Y ' AND blog.use_yn = ' Y ' ORDER BY blog.add_date DESC LIMIT 1 "
next_query = " SELECT blog.*, member.mb_name, member.mb_id FROM blog INNER JOIN member ON blog.user_id = member.mb_idx WHERE blog.add_date > %s AND blog.public_yn = ' Y ' AND blog.use_yn = ' Y ' ORDER BY blog.add_date ASC LIMIT 1 "
prev_post = None
next_post = None
prev_result , prev_fetched_data = sql_execute ( prev_query , ( post [ ' add_date ' ] , ) , is_data = True )
next_result , next_fetched_data = sql_execute ( next_query , ( post [ ' add_date ' ] , ) , is_data = True )
if prev_result and prev_fetched_data :
prev_post = prev_fetched_data [ 0 ]
if next_result and next_fetched_data :
next_post = next_fetched_data [ 0 ]
return render_template ( ' post.html ' , post = post , prev_post = prev_post , next_post = next_post )
else :
# 검색 실패시, 에러 메시지 반환
return " Failed to fetch the post " , 500
@app.route ( ' /edit_post/<int:post_id> ' , methods = [ ' GET ' , ' POST ' ] )
def edit_post ( post_id ) :
if ' username ' not in session or ( ' username ' in session and session [ ' username ' ] not in [ ' admin ' , ' wixon ' ] ) :
flash ( ' You are not allowed to edit this post. ' )
return redirect ( url_for ( ' index ' ) )
if request . method == ' POST ' :
# 포스트 업데이트 로직
title = request . form . get ( ' title ' )
category = request . form . get ( ' category ' )
public_yn = ' Y ' if request . form . get ( ' public ' ) == ' on ' else ' N '
contents = request . form . get ( ' contents ' )
query = " UPDATE blog SET title = %s , category = %s , public_yn = %s , contents = %s WHERE id = %s "
data = ( title , category , public_yn , contents , post_id )
res = sql_execute ( query , data )
if res :
flash ( ' Post updated successfully. ' )
return redirect ( url_for ( ' post ' , post_id = post_id ) )
else :
flash ( ' Failed to update the post. ' )
return redirect ( url_for ( ' edit_post ' , post_id = post_id ) )
else :
# 포스트 가져오기 로직
query = " SELECT * FROM blog WHERE id = %s "
data = ( post_id , )
res , fetched_data = sql_execute ( query , data , is_data = True )
if res and fetched_data :
return render_template ( ' edit_post.html ' , post = fetched_data [ 0 ] )
else :
flash ( ' Failed to fetch the post. ' )
return redirect ( url_for ( ' index ' ) )
2023-08-02 17:10:20 +09:00
@app.route ( ' /write ' , methods = [ ' GET ' , ' POST ' ] )
def write ( ) :
2023-08-03 21:09:18 +09:00
if ' user_info ' not in session :
flash ( " You need to login first. " )
return redirect ( url_for ( ' login ' ) )
2023-08-02 17:10:20 +09:00
if request . method == ' POST ' :
2023-08-03 21:09:18 +09:00
user_id = session [ ' user_info ' ] [ ' mb_idx ' ]
2023-08-02 17:10:20 +09:00
title = request . form [ ' title ' ]
category = request . form [ ' category ' ]
contents = request . form [ ' contents ' ]
2023-08-03 21:09:18 +09:00
is_public = ' Y ' if request . form . get ( ' public ' ) == ' on ' else ' N '
2023-08-02 17:10:20 +09:00
soup = BeautifulSoup ( contents , ' html.parser ' )
first_image = soup . find ( ' img ' )
thumbnail_img = first_image [ ' src ' ] if first_image else None
2023-08-03 21:09:18 +09:00
query = " INSERT INTO blog (user_id, title, category, contents, thumbnail_img, public_yn) VALUES ( %s , %s , %s , %s , %s , %s ) "
data = ( user_id , title , category , contents , thumbnail_img , is_public )
2023-08-02 17:10:20 +09:00
result , last_id = sql_execute ( query , data , is_last_id = True )
if result :
return redirect ( url_for ( ' index ' ) )
else :
return " Failed to write post " , 500
else :
return render_template ( ' write.html ' )
2023-08-03 21:09:18 +09:00
@app.route ( ' /blog/<int:id> ' , methods = [ ' DELETE ' ] )
def delete_post ( id ) :
query = " UPDATE `blog` SET `use_yn` = ' N ' WHERE `id` = %s ; "
res = sql_execute ( query , ( id , ) )
if res :
return jsonify ( success = True , message = ' Post deleted successfully ' ) , 200
else :
return jsonify ( success = False , message = ' Could not delete the post ' ) , 500
2023-08-02 17:10:20 +09:00
if __name__ == ' __main__ ' :
app . run ( host = ' 0.0.0.0 ' , port = 8899 )