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
관리자 페이지 추가 및 버그 수정
- 관리자 대시보드 추가 (/admin/)
- 통계: 총 포스트, 공개/비공개, 삭제된 포스트, 회원 수
- 포스트 관리 추가 (/admin/posts)
- 목록, 검색, 필터링, 페이지네이션
- 포스트 수정, 삭제, 복구 기능
- 회원 관리 추가 (/admin/members)
- 회원 목록, 추가, 수정, 삭제
- 비밀번호 재설정
- 버그 수정
- g.is_login, g.user_info 기본값 설정
- index 페이지 빈 포스트 처리
- 관리자 권한: admin, wixon, javamon
- README.md 프로젝트 문서 추가
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 15:30:49 +09:00
from functools import wraps
import math
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 ( ) :
관리자 페이지 추가 및 버그 수정
- 관리자 대시보드 추가 (/admin/)
- 통계: 총 포스트, 공개/비공개, 삭제된 포스트, 회원 수
- 포스트 관리 추가 (/admin/posts)
- 목록, 검색, 필터링, 페이지네이션
- 포스트 수정, 삭제, 복구 기능
- 회원 관리 추가 (/admin/members)
- 회원 목록, 추가, 수정, 삭제
- 비밀번호 재설정
- 버그 수정
- g.is_login, g.user_info 기본값 설정
- index 페이지 빈 포스트 처리
- 관리자 권한: admin, wixon, javamon
- README.md 프로젝트 문서 추가
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 15:30:49 +09:00
g . is_login = False
g . user_info = None
2023-08-02 17:10:20 +09:00
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 )
관리자 페이지 추가 및 버그 수정
- 관리자 대시보드 추가 (/admin/)
- 통계: 총 포스트, 공개/비공개, 삭제된 포스트, 회원 수
- 포스트 관리 추가 (/admin/posts)
- 목록, 검색, 필터링, 페이지네이션
- 포스트 수정, 삭제, 복구 기능
- 회원 관리 추가 (/admin/members)
- 회원 목록, 추가, 수정, 삭제
- 비밀번호 재설정
- 버그 수정
- g.is_login, g.user_info 기본값 설정
- index 페이지 빈 포스트 처리
- 관리자 권한: admin, wixon, javamon
- README.md 프로젝트 문서 추가
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 15:30:49 +09:00
if random_post :
posts . append ( random_post [ 0 ] )
2023-09-13 17:04:52 +09:00
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
2026-01-05 16:59:20 +09:00
@app.route ( ' /list ' )
@app.route ( ' /list/<string:cate> ' )
def list ( cate = None ) :
params = ( )
cate_condition = " "
if cate :
cate_condition = " AND `category` = %s "
params = ( cate , )
if ' user_info ' in session : # 로그인된 사용자
query = f """ SELECT `id`, `title`, `category`, `thumbnail_img`, `contents`, `add_date`
FROM ` blog `
WHERE ` use_yn ` = ' Y ' { cate_condition }
2026-01-16 16:12:48 +09:00
ORDER BY ` add_date ` DESC LIMIT 10 ; """
2026-01-05 16:59:20 +09:00
else :
query = f """ SELECT `id`, `title`, `category`, `thumbnail_img`, `contents`, `add_date`
FROM ` blog `
WHERE ` use_yn ` = ' Y ' AND ` public_yn ` = ' Y ' { cate_condition }
2026-01-16 16:12:48 +09:00
ORDER BY ` add_date ` DESC LIMIT 10 ; """
2026-01-05 16:59:20 +09:00
r , posts = sql_execute ( query , params , is_data = True )
# 태그 제거 후 150자로 제한
for post in posts :
post [ ' contents ' ] = remove_html_tags ( post [ ' contents ' ] ) [ : 150 ]
return render_template ( ' list.html ' , posts = posts )
2023-08-03 21:09:18 +09:00
2026-01-16 16:12:48 +09:00
@app.route ( ' /list_more ' )
def list_more ( ) :
page = int ( request . args . get ( ' page ' , 1 ) )
limit = 10
offset = ( page - 1 ) * limit
cate = request . args . get ( ' cate ' )
cate_condition = " "
params = [ ]
if cate :
cate_condition = " AND `category` = %s "
params . append ( cate )
query = f """
SELECT ` id ` , ` title ` , ` category ` , ` thumbnail_img ` , ` contents ` , ` add_date `
FROM ` blog `
WHERE ` use_yn ` = ' Y '
{ " AND `public_yn` = ' Y ' " if ' user_info ' not in session else " " }
{ cate_condition }
ORDER BY ` add_date ` DESC
LIMIT % s OFFSET % s
"""
params . extend ( [ limit , offset ] )
r , posts = sql_execute ( query , tuple ( params ) , is_data = True )
for post in posts :
post [ ' contents ' ] = remove_html_tags ( post [ ' contents ' ] ) [ : 150 ]
post [ ' add_date_str ' ] = post [ ' add_date ' ] . strftime ( " % b %d / % Y " )
return jsonify ( posts )
관리자 페이지 추가 및 버그 수정
- 관리자 대시보드 추가 (/admin/)
- 통계: 총 포스트, 공개/비공개, 삭제된 포스트, 회원 수
- 포스트 관리 추가 (/admin/posts)
- 목록, 검색, 필터링, 페이지네이션
- 포스트 수정, 삭제, 복구 기능
- 회원 관리 추가 (/admin/members)
- 회원 목록, 추가, 수정, 삭제
- 비밀번호 재설정
- 버그 수정
- g.is_login, g.user_info 기본값 설정
- index 페이지 빈 포스트 처리
- 관리자 권한: admin, wixon, javamon
- README.md 프로젝트 문서 추가
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 15:30:49 +09:00
# ============================================
# 관리자 페이지
# ============================================
def admin_required ( f ) :
@wraps ( f )
def decorated_function ( * args , * * kwargs ) :
if ' username ' not in session or session [ ' username ' ] not in [ ' admin ' , ' wixon ' , ' javamon ' ] :
flash ( ' 관리자 권한이 필요합니다. ' )
return redirect ( url_for ( ' login ' ) )
return f ( * args , * * kwargs )
return decorated_function
@app.route ( ' /admin/ ' )
@admin_required
def admin_dashboard ( ) :
stats = {
' total_posts ' : 0 ,
' public_posts ' : 0 ,
' private_posts ' : 0 ,
' deleted_posts ' : 0 ,
' total_members ' : 0
}
r , data = sql_execute ( " SELECT COUNT(*) as cnt FROM blog WHERE use_yn= ' Y ' " , ( ) , is_data = True )
if r and data :
stats [ ' total_posts ' ] = data [ 0 ] [ ' cnt ' ]
r , data = sql_execute ( " SELECT COUNT(*) as cnt FROM blog WHERE use_yn= ' Y ' AND public_yn= ' Y ' " , ( ) , is_data = True )
if r and data :
stats [ ' public_posts ' ] = data [ 0 ] [ ' cnt ' ]
r , data = sql_execute ( " SELECT COUNT(*) as cnt FROM blog WHERE use_yn= ' Y ' AND public_yn= ' N ' " , ( ) , is_data = True )
if r and data :
stats [ ' private_posts ' ] = data [ 0 ] [ ' cnt ' ]
r , data = sql_execute ( " SELECT COUNT(*) as cnt FROM blog WHERE use_yn= ' N ' " , ( ) , is_data = True )
if r and data :
stats [ ' deleted_posts ' ] = data [ 0 ] [ ' cnt ' ]
r , data = sql_execute ( " SELECT COUNT(*) as cnt FROM member " , ( ) , is_data = True )
if r and data :
stats [ ' total_members ' ] = data [ 0 ] [ ' cnt ' ]
return render_template ( ' admin/dashboard.html ' , stats = stats )
@app.route ( ' /admin/posts ' )
@admin_required
def admin_posts ( ) :
page = request . args . get ( ' page ' , 1 , type = int )
per_page = 20
offset = ( page - 1 ) * per_page
category = request . args . get ( ' category ' , ' ' )
public_yn = request . args . get ( ' public_yn ' , ' ' )
use_yn = request . args . get ( ' use_yn ' , ' Y ' )
search = request . args . get ( ' search ' , ' ' )
where_clauses = [ ]
params = [ ]
if category :
where_clauses . append ( " blog.category = %s " )
params . append ( category )
if public_yn :
where_clauses . append ( " blog.public_yn = %s " )
params . append ( public_yn )
if use_yn :
where_clauses . append ( " blog.use_yn = %s " )
params . append ( use_yn )
if search :
where_clauses . append ( " blog.title LIKE %s " )
params . append ( f " % { search } % " )
where_sql = " AND " . join ( where_clauses ) if where_clauses else " 1=1 "
count_query = f " SELECT COUNT(*) as cnt FROM blog INNER JOIN member ON blog.user_id = member.mb_idx WHERE { where_sql } "
r , count_data = sql_execute ( count_query , tuple ( params ) , is_data = True )
total = count_data [ 0 ] [ ' cnt ' ] if r and count_data else 0
total_pages = math . ceil ( total / per_page ) if total > 0 else 1
query = f """
SELECT blog . * , member . mb_name , member . mb_id
FROM blog
INNER JOIN member ON blog . user_id = member . mb_idx
WHERE { where_sql }
ORDER BY blog . add_date DESC
LIMIT % s OFFSET % s
"""
params . extend ( [ per_page , offset ] )
r , posts = sql_execute ( query , tuple ( params ) , is_data = True )
pagination = {
' page ' : page ,
' per_page ' : per_page ,
' total ' : total ,
' total_pages ' : total_pages ,
' has_prev ' : page > 1 ,
' has_next ' : page < total_pages ,
' prev_num ' : page - 1 ,
' next_num ' : page + 1
}
return render_template ( ' admin/posts.html ' , posts = posts if posts else [ ] , pagination = pagination )
@app.route ( ' /admin/posts/<int:post_id> ' , methods = [ ' GET ' , ' POST ' ] )
@admin_required
def admin_post_detail ( post_id ) :
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 ' )
soup = BeautifulSoup ( contents , ' html.parser ' )
first_image = soup . find ( ' img ' )
thumbnail_img = first_image [ ' src ' ] if first_image else None
query = " UPDATE blog SET title= %s , category= %s , public_yn= %s , contents= %s , thumbnail_img= %s WHERE id= %s "
res = sql_execute ( query , ( title , category , public_yn , contents , thumbnail_img , post_id ) )
if res :
flash ( ' 포스트가 수정되었습니다. ' )
return redirect ( url_for ( ' admin_posts ' ) )
else :
flash ( ' 포스트 수정에 실패했습니다. ' )
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 "
r , data = sql_execute ( query , ( post_id , ) , is_data = True )
if r and data :
return render_template ( ' admin/post_detail.html ' , post = data [ 0 ] )
else :
flash ( ' 포스트를 찾을 수 없습니다. ' )
return redirect ( url_for ( ' admin_posts ' ) )
@app.route ( ' /admin/posts/<int:post_id>/delete ' , methods = [ ' DELETE ' ] )
@admin_required
def admin_post_delete ( post_id ) :
query = " UPDATE blog SET use_yn= ' N ' WHERE id= %s "
res = sql_execute ( query , ( post_id , ) )
if res :
return jsonify ( success = True , message = ' 포스트가 삭제되었습니다. ' )
return jsonify ( success = False , message = ' 삭제 실패 ' ) , 500
@app.route ( ' /admin/posts/<int:post_id>/restore ' , methods = [ ' POST ' ] )
@admin_required
def admin_post_restore ( post_id ) :
query = " UPDATE blog SET use_yn= ' Y ' WHERE id= %s "
res = sql_execute ( query , ( post_id , ) )
if res :
return jsonify ( success = True , message = ' 포스트가 복구되었습니다. ' )
return jsonify ( success = False , message = ' 복구 실패 ' ) , 500
@app.route ( ' /admin/members ' )
@admin_required
def admin_members ( ) :
query = " SELECT mb_idx, mb_id, mb_name FROM member ORDER BY mb_idx DESC "
r , members = sql_execute ( query , ( ) , is_data = True )
return render_template ( ' admin/members.html ' , members = members if members else [ ] )
@app.route ( ' /admin/members/add ' , methods = [ ' GET ' , ' POST ' ] )
@admin_required
def admin_member_add ( ) :
if request . method == ' POST ' :
mb_id = request . form . get ( ' mb_id ' )
mb_name = request . form . get ( ' mb_name ' )
password = request . form . get ( ' password ' ) . encode ( ' utf-8 ' )
hashed_pw = bcrypt . hashpw ( password , bcrypt . gensalt ( ) ) . decode ( ' utf-8 ' )
r , existing = sql_execute ( " SELECT mb_id FROM member WHERE mb_id= %s " , ( mb_id , ) , is_data = True )
if r and existing :
flash ( ' 이미 존재하는 아이디입니다. ' )
return render_template ( ' admin/member_form.html ' , member = None )
query = " INSERT INTO member (mb_id, mb_passwd, mb_name) VALUES ( %s , %s , %s ) "
res = sql_execute ( query , ( mb_id , hashed_pw , mb_name ) )
if res :
flash ( ' 회원이 추가되었습니다. ' )
return redirect ( url_for ( ' admin_members ' ) )
flash ( ' 회원 추가에 실패했습니다. ' )
return render_template ( ' admin/member_form.html ' , member = None )
@app.route ( ' /admin/members/<int:mb_idx> ' , methods = [ ' GET ' , ' POST ' ] )
@admin_required
def admin_member_detail ( mb_idx ) :
if request . method == ' POST ' :
mb_name = request . form . get ( ' mb_name ' )
query = " UPDATE member SET mb_name= %s WHERE mb_idx= %s "
res = sql_execute ( query , ( mb_name , mb_idx ) )
if res :
flash ( ' 회원 정보가 수정되었습니다. ' )
return redirect ( url_for ( ' admin_members ' ) )
flash ( ' 회원 정보 수정에 실패했습니다. ' )
query = " SELECT mb_idx, mb_id, mb_name FROM member WHERE mb_idx= %s "
r , member = sql_execute ( query , ( mb_idx , ) , is_data = True )
if r and member :
return render_template ( ' admin/member_form.html ' , member = member [ 0 ] )
else :
flash ( ' 회원을 찾을 수 없습니다. ' )
return redirect ( url_for ( ' admin_members ' ) )
@app.route ( ' /admin/members/<int:mb_idx>/delete ' , methods = [ ' DELETE ' ] )
@admin_required
def admin_member_delete ( mb_idx ) :
if session . get ( ' user_info ' , { } ) . get ( ' mb_idx ' ) == mb_idx :
return jsonify ( success = False , message = ' 자기 자신은 삭제할 수 없습니다. ' ) , 400
query = " DELETE FROM member WHERE mb_idx= %s "
res = sql_execute ( query , ( mb_idx , ) )
if res :
return jsonify ( success = True , message = ' 회원이 삭제되었습니다. ' )
return jsonify ( success = False , message = ' 삭제 실패 ' ) , 500
@app.route ( ' /admin/members/<int:mb_idx>/reset-password ' , methods = [ ' POST ' ] )
@admin_required
def admin_member_reset_password ( mb_idx ) :
data = request . get_json ( )
new_password = data . get ( ' password ' , ' ' ) . encode ( ' utf-8 ' )
if len ( new_password ) < 4 :
return jsonify ( success = False , message = ' 비밀번호는 4자 이상이어야 합니다. ' ) , 400
hashed_pw = bcrypt . hashpw ( new_password , bcrypt . gensalt ( ) ) . decode ( ' utf-8 ' )
query = " UPDATE member SET mb_passwd= %s WHERE mb_idx= %s "
res = sql_execute ( query , ( hashed_pw , mb_idx ) )
if res :
return jsonify ( success = True , message = ' 비밀번호가 재설정되었습니다. ' )
return jsonify ( success = False , message = ' 비밀번호 재설정 실패 ' ) , 500
2023-08-02 17:10:20 +09:00
if __name__ == ' __main__ ' :
app . run ( host = ' 0.0.0.0 ' , port = 8899 )