-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
235 lines (201 loc) · 8.08 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
import os
from dotenv import load_dotenv
from flask import Flask, render_template, request, send_from_directory, redirect, url_for, jsonify, session, send_file
from flask_dance.contrib.google import make_google_blueprint, google
from flask_dance.consumer import oauth_authorized
from oauthlib.oauth2.rfc6749.errors import TokenExpiredError, InvalidScopeError
import config
from werkzeug.utils import secure_filename
import json
import uuid
from datetime import datetime
from functools import wraps
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
load_dotenv()
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
app = Flask(__name__)
app.secret_key = config.YOUR_SECRET_KEY
app.config['UPLOAD_FOLDER'] = config.UPLOAD_FOLDER
app.config['ALLOWED_EXTENSIONS'] = config.ALLOWED_EXTENSIONS
app.config['CAD_DATA_FILE'] = 'cad_data.json'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16 MB max upload size
# Setup for Google OAuth2
blueprint = make_google_blueprint(
client_id=config.CLIENT_ID,
client_secret=config.CLIENT_SECRET,
scope=["https://www.googleapis.com/auth/userinfo.email", "openid", "https://www.googleapis.com/auth/userinfo.profile"],
redirect_url="/"
)
app.register_blueprint(blueprint, url_prefix="/login")
# Setup rate limiting
limiter = Limiter(
get_remote_address,
app=app,
default_limits=["200 per day", "50 per hour"]
)
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not google.authorized:
return redirect(url_for("google.login"))
return f(*args, **kwargs)
return decorated_function
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
def get_cad_data():
if not os.path.exists(app.config['CAD_DATA_FILE']):
return {}
with open(app.config['CAD_DATA_FILE'], 'r') as f:
return json.load(f)
def save_cad_data(data):
with open(app.config['CAD_DATA_FILE'], 'w') as f:
json.dump(data, f, indent=4)
@app.route("/")
@login_required
def index():
try:
resp = google.get("/oauth2/v2/userinfo")
assert resp.ok, resp.text
user_info = resp.json()
session['user_id'] = user_info['id']
session['user_email'] = user_info['email']
except (TokenExpiredError, InvalidScopeError):
del blueprint.token
return redirect(url_for("google.login"))
return render_template("index.html", user_info=user_info)
@app.route('/upload', methods=['POST'])
@login_required
@limiter.limit("10/minute")
def upload_file():
if 'cad_file' not in request.files:
return jsonify({'success': False, 'message': 'No file part'}), 400
file = request.files['cad_file']
description = request.form.get('description', '').strip()
tags = [tag.strip() for tag in request.form.get('tags', '').split(',') if tag.strip()]
if file.filename == '':
return jsonify({'success': False, 'message': 'No selected file'}), 400
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
unique_filename = f"{uuid.uuid4()}_{filename}"
file_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
file.save(file_path)
cad_data = get_cad_data()
cad_data[unique_filename] = {
'original_filename': filename,
'description': description,
'tags': tags,
'likes': 0,
'comments': [],
'upload_date': datetime.now().isoformat(),
'uploader': session.get('user_email', 'anonymous'),
'file_size': os.path.getsize(file_path)
}
save_cad_data(cad_data)
return jsonify({'success': True, 'message': 'File uploaded successfully'})
return jsonify({'success': False, 'message': 'Invalid file extension'}), 400
@app.route('/get_cad_files')
@login_required
def get_cad_files_route():
cad_files = get_cad_files()
return jsonify(cad_files)
@app.route('/download/<filename>', methods=['GET'])
@login_required
@limiter.limit("30/minute")
def download_file(filename):
cad_data = get_cad_data()
if filename in cad_data:
original_filename = cad_data[filename]['original_filename']
return send_from_directory(app.config['UPLOAD_FOLDER'], filename, as_attachment=True, download_name=original_filename)
return jsonify({'success': False, 'message': 'File not found'}), 404
@app.route('/like/<filename>', methods=['POST'])
@login_required
@limiter.limit("30/minute")
def like_file(filename):
cad_data = get_cad_data()
if filename in cad_data:
cad_data[filename]['likes'] += 1
save_cad_data(cad_data)
return jsonify({'success': True, 'likes': cad_data[filename]['likes']})
return jsonify({'success': False, 'message': 'File not found'}), 404
@app.route('/delete/<filename>', methods=['DELETE'])
@login_required
def delete_file(filename):
cad_data = get_cad_data()
if filename in cad_data:
if cad_data[filename]['uploader'] != session.get('user_email'):
return jsonify({'success': False, 'message': 'You are not authorized to delete this file'}), 403
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
if os.path.exists(file_path):
os.remove(file_path)
del cad_data[filename]
save_cad_data(cad_data)
return jsonify({'success': True, 'message': 'File deleted successfully'})
return jsonify({'success': False, 'message': 'File not found'}), 404
@app.route('/comment/<filename>', methods=['POST'])
@login_required
@limiter.limit("10/minute")
def add_comment(filename):
comment = request.json.get('comment', '').strip()
if not comment:
return jsonify({'success': False, 'message': 'Comment cannot be empty'}), 400
user_email = session.get('user_email', 'anonymous')
cad_data = get_cad_data()
if filename in cad_data:
cad_data[filename]['comments'].append({
'user': user_email,
'comment': comment,
'timestamp': datetime.now().isoformat()
})
save_cad_data(cad_data)
return jsonify({'success': True, 'message': 'Comment added successfully'})
return jsonify({'success': False, 'message': 'File not found'}), 404
@app.route('/search', methods=['GET'])
@login_required
def search_files():
query = request.args.get('q', '').lower()
cad_files = get_cad_files()
results = [file for file in cad_files if query in file['original_filename'].lower() or
query in file['description'].lower() or
any(query in tag.lower() for tag in file['tags'])]
return jsonify(results)
def get_cad_files():
cad_data = get_cad_data()
files = [
{
'filename': filename,
'original_filename': file_data['original_filename'],
'description': file_data['description'],
'tags': file_data['tags'],
'likes': file_data['likes'],
'comments': file_data['comments'],
'upload_date': file_data['upload_date'],
'uploader': file_data['uploader'],
'file_size': file_data['file_size']
}
for filename, file_data in cad_data.items()
]
return sorted(files, key=lambda x: x['likes'], reverse=True)
@app.route('/get_file_content/<filename>')
@login_required
def get_file_content(filename):
cad_data = get_cad_data()
if filename in cad_data:
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
return send_file(file_path)
return jsonify({'success': False, 'message': 'File not found'}), 404
@app.route("/logout")
def logout():
if google.authorized:
token = blueprint.token["access_token"]
resp = google.post(
"https://accounts.google.com/o/oauth2/revoke",
params={"token": token},
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
assert resp.ok, resp.text
del blueprint.token
session.clear()
return redirect(url_for("index"))
if __name__ == "__main__":
app.run(ssl_context="adhoc", port=5001, debug=False)