├── .gitignore ├── celerybeat-schedule ├── instance └── database.sqlite3 ├── __pycache__ └── app.cpython-310.pyc ├── backend ├── __pycache__ │ ├── config.cpython-310.pyc │ ├── models.cpython-310.pyc │ ├── routes.cpython-310.pyc │ ├── resources.cpython-310.pyc │ └── create_initial_data.cpython-310.pyc ├── celery │ ├── __pycache__ │ │ ├── tasks.cpython-310.pyc │ │ └── celery_factory.cpython-310.pyc │ ├── user-downloads │ │ ├── blog.csv │ │ └── blog_data_b7881c24-9ef7-4b25-953b-71cd5f51a471.csv │ ├── mail_service.py │ ├── celery_factory.py │ ├── celery_schedule.py │ └── tasks.py ├── config.py ├── create_initial_data.py ├── models.py ├── resources.py └── routes.py ├── frontend ├── app.js ├── components │ ├── BlogCard.js │ ├── Navbar.js │ └── User.js ├── pages │ ├── DisplayBlogPage.js │ ├── BlogsListPage.js │ ├── RegisterPage.js │ ├── AdminDashboardPage.js │ ├── ExploreUsersPage.js │ └── LoginPage.js ├── utils │ ├── store.js │ ├── router.js │ └── fetchWithAuth.js └── index.html ├── req.txt ├── app.py └── seed.py /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | backend/__pycache__ 3 | user-downloads 4 | **/__pycache__/ -------------------------------------------------------------------------------- /celerybeat-schedule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/celerybeat-schedule -------------------------------------------------------------------------------- /instance/database.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/instance/database.sqlite3 -------------------------------------------------------------------------------- /__pycache__/app.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/__pycache__/app.cpython-310.pyc -------------------------------------------------------------------------------- /backend/__pycache__/config.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/backend/__pycache__/config.cpython-310.pyc -------------------------------------------------------------------------------- /backend/__pycache__/models.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/backend/__pycache__/models.cpython-310.pyc -------------------------------------------------------------------------------- /backend/__pycache__/routes.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/backend/__pycache__/routes.cpython-310.pyc -------------------------------------------------------------------------------- /backend/__pycache__/resources.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/backend/__pycache__/resources.cpython-310.pyc -------------------------------------------------------------------------------- /backend/celery/__pycache__/tasks.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/backend/celery/__pycache__/tasks.cpython-310.pyc -------------------------------------------------------------------------------- /backend/__pycache__/create_initial_data.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/backend/__pycache__/create_initial_data.cpython-310.pyc -------------------------------------------------------------------------------- /backend/celery/__pycache__/celery_factory.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaBaxla/24_Sep_BlogLite/HEAD/backend/celery/__pycache__/celery_factory.cpython-310.pyc -------------------------------------------------------------------------------- /frontend/app.js: -------------------------------------------------------------------------------- 1 | import Navbar from "./components/Navbar.js" 2 | import router from "./utils/router.js" 3 | import store from "./utils/store.js" 4 | 5 | const app = new Vue({ 6 | el : '#app', 7 | template : ` 8 |
9 | 10 | 11 |
12 | `, 13 | components : { 14 | Navbar, 15 | }, 16 | router, 17 | store, 18 | }) -------------------------------------------------------------------------------- /backend/celery/user-downloads/blog.csv: -------------------------------------------------------------------------------- 1 | id,title,caption,image_url,timestamp,user_id 2 | 1,Anuj Jain,Husn,https://static.vecteezy.com/system/resources/previews/021/693/323/non_2x/a-logo-for-a-music-company-that-is-made-by-song-brand-vector.jpg,2024-10-14T20:52:34.541108,2 3 | 2,Talyor,Teardrops,https://static.vecteezy.com/system/resources/previews/021/693/323/non_2x/a-logo-for-a-music-company-that-is-made-by-song-brand-vector.jpg,2024-10-14T20:55:03.320170,2 4 | -------------------------------------------------------------------------------- /backend/celery/user-downloads/blog_data_b7881c24-9ef7-4b25-953b-71cd5f51a471.csv: -------------------------------------------------------------------------------- 1 | id,title,caption,image_url,timestamp,user_id 2 | 1,Anuj Jain,Husn,https://static.vecteezy.com/system/resources/previews/021/693/323/non_2x/a-logo-for-a-music-company-that-is-made-by-song-brand-vector.jpg,2024-10-14T20:52:34.541108,2 3 | 2,Talyor,Teardrops,https://static.vecteezy.com/system/resources/previews/021/693/323/non_2x/a-logo-for-a-music-company-that-is-made-by-song-brand-vector.jpg,2024-10-14T20:55:03.320170,2 4 | -------------------------------------------------------------------------------- /frontend/components/BlogCard.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props : ['title', 'author_email', 'date', 'blog_id'], 3 | template : ` 4 |
5 |

{{title}}

6 |

{{author_email}}

7 |
8 |

Published : {{formattedDate}}

9 |
10 | `, 11 | computed: { 12 | formattedDate(){ 13 | return new Date(this.date).toLocaleString(); 14 | } 15 | } 16 | } 17 | // change for commit -------------------------------------------------------------------------------- /backend/config.py: -------------------------------------------------------------------------------- 1 | class Config(): 2 | DEBUG = False 3 | SQL_ALCHEMY_TRACK_MODIFICATIONS = False 4 | 5 | class LocalDevelopmentConfig(Config): 6 | SQLALCHEMY_DATABASE_URI = "sqlite:///database.sqlite3" 7 | DEBUG = True 8 | SECURITY_PASSWORD_HASH = 'bcrypt' 9 | SECURITY_PASSWORD_SALT = 'thisshouldbekeptsecret' 10 | SECRET_KEY = "shouldbekeyveryhidden" 11 | SECURITY_TOKEN_AUTHENTICATION_HEADER = 'Authentication-Token' 12 | SECURITY_TOKEN_MAX_AGE = 3600 13 | 14 | # cache specific 15 | CACHE_TYPE = "RedisCache" 16 | CACHE_DEFAULT_TIMEOUT = 30 17 | CACHE_REDIS_PORT = 6379 18 | 19 | WTF_CSRF_ENABLED = False -------------------------------------------------------------------------------- /backend/celery/mail_service.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | from email.mime.multipart import MIMEMultipart 3 | from email.mime.text import MIMEText 4 | 5 | 6 | SMTP_SERVER = "localhost" 7 | SMTP_PORT = 1025 8 | SENDER_EMAIL = 'blogAdmin@example' 9 | SENDER_PASSWORD = '' 10 | 11 | def send_email(to, subject, content): 12 | 13 | msg = MIMEMultipart() 14 | msg['To'] = to 15 | msg['Subject'] = subject 16 | msg['From'] = SENDER_EMAIL 17 | 18 | msg.attach(MIMEText(content,'html')) 19 | 20 | with smtplib.SMTP(host=SMTP_SERVER, port=SMTP_PORT) as client: 21 | client.send_message(msg) 22 | client.quit() 23 | 24 | # send_email('aditya@example', 'Test Email', '

Welcome to AppDev

') 25 | -------------------------------------------------------------------------------- /backend/celery/celery_factory.py: -------------------------------------------------------------------------------- 1 | from celery import Celery, Task 2 | from flask import Flask 3 | 4 | class CeleryConfig(): 5 | broker_url = 'redis://localhost:6379/0' 6 | result_backend = 'redis://localhost:6379/1' 7 | timezone = 'Asia/Kolkata' 8 | 9 | def celery_init_app(app: Flask) -> Celery: 10 | class FlaskTask(Task): 11 | def __call__(self, *args: object, **kwargs: object) -> object: 12 | with app.app_context(): 13 | return self.run(*args, **kwargs) 14 | 15 | celery_app = Celery(app.name, task_cls=FlaskTask) 16 | celery_app.config_from_object(CeleryConfig) 17 | celery_app.set_default() 18 | app.extensions["celery"] = celery_app 19 | return celery_app -------------------------------------------------------------------------------- /frontend/components/Navbar.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template : ` 3 |
4 | Home 5 | Login 6 | Register 7 | 8 | Admin Dash 9 | Feed 10 | Explore 11 | 12 | 13 | 14 | 15 |
16 | ` 17 | } -------------------------------------------------------------------------------- /frontend/pages/DisplayBlogPage.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props : ['id'], 3 | template : ` 4 |
5 |

{{blog.title}}

6 |

Published : {{formattedDate}}

7 | 8 |

9 |
10 | `, 11 | data(){ 12 | return { 13 | blog : {}, 14 | } 15 | }, 16 | computed : { 17 | formattedDate(){ 18 | return new Date(this.blog.timestamp).toLocaleString(); 19 | } 20 | }, 21 | async mounted(){ 22 | const res = await fetch(`${location.origin}/api/blogs/${this.id}`, { 23 | headers : { 24 | 'Authentication-Token' : this.$store.state.auth_token 25 | } 26 | }) 27 | if (res.ok){ 28 | this.blog = await res.json() 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /backend/celery/celery_schedule.py: -------------------------------------------------------------------------------- 1 | from celery.schedules import crontab 2 | from flask import current_app as app 3 | from backend.celery.tasks import email_reminder 4 | 5 | celery_app = app.extensions['celery'] 6 | 7 | @celery_app.on_after_configure.connect 8 | def setup_periodic_tasks(sender, **kwargs): 9 | # every 10 seconds 10 | # sender.add_periodic_task(10.0, email_reminder.s('students@gmail', 'reminder to login', '

hello everyone

') ) 11 | 12 | # daily message at 6:55 pm, everyday 13 | sender.add_periodic_task(crontab(hour=18, minute=55), email_reminder.s('students@gmail', 'reminder to login', '

hello everyone

'), name='daily reminder' ) 14 | 15 | # weekly messages 16 | sender.add_periodic_task(crontab(hour=18, minute=55, day_of_week='monday'), email_reminder.s('students@gmail', 'reminder to login', '

hello everyone

'), name = 'weekly reminder' ) 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /backend/create_initial_data.py: -------------------------------------------------------------------------------- 1 | from flask import current_app as app 2 | from backend.models import db 3 | from flask_security import SQLAlchemyUserDatastore, hash_password 4 | 5 | with app.app_context(): 6 | db.create_all() 7 | 8 | userdatastore : SQLAlchemyUserDatastore = app.security.datastore 9 | 10 | userdatastore.find_or_create_role(name = 'admin', description = 'superuser') 11 | userdatastore.find_or_create_role(name = 'user', description = 'general user') 12 | 13 | if (not userdatastore.find_user(email = 'admin@study.iitm.ac.in')): 14 | userdatastore.create_user(email = 'admin@study.iitm.ac.in', password = hash_password('pass'), roles = ['admin'] ) 15 | if (not userdatastore.find_user(email = 'user01@study.iitm.ac.in')): 16 | userdatastore.create_user(email = 'user01@study.iitm.ac.in', password = hash_password('pass'), roles = ['user'] ) # for testing 17 | 18 | db.session.commit() -------------------------------------------------------------------------------- /frontend/pages/BlogsListPage.js: -------------------------------------------------------------------------------- 1 | import BlogCard from "../components/BlogCard.js" 2 | 3 | export default { 4 | template :` 5 |
6 |

Blogs Feed 👌

7 |
8 | 9 |
10 |
No blogs found
11 |
12 | `, 13 | data(){ 14 | return { 15 | blogs : [] 16 | } 17 | }, 18 | methods : { 19 | 20 | }, 21 | async mounted(){ 22 | const res = await fetch(location.origin + '/api/feed', { 23 | headers : { 24 | 'Authentication-Token' : this.$store.state.auth_token 25 | } 26 | }) 27 | 28 | this.blogs = await res.json() 29 | console.log(this.blogs) 30 | }, 31 | components : { 32 | BlogCard, 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /backend/celery/tasks.py: -------------------------------------------------------------------------------- 1 | from celery import shared_task 2 | import time 3 | 4 | import flask_excel 5 | from backend.models import Blog 6 | from backend.celery.mail_service import send_email 7 | 8 | @shared_task(ignore_result = False) 9 | def add(x,y): 10 | time.sleep(10) 11 | return x+y 12 | 13 | 14 | @shared_task(bind = True, ignore_result = False) 15 | def create_csv(self): 16 | resource = Blog.query.all() 17 | 18 | task_id = self.request.id 19 | filename = f'blog_data_{task_id}.csv' 20 | column_names = [column.name for column in Blog.__table__.columns] 21 | print(column_names) 22 | csv_out = flask_excel.make_response_from_query_sets(resource, column_names = column_names, file_type='csv' ) 23 | 24 | with open(f'./backend/celery/user-downloads/{filename}', 'wb') as file: 25 | file.write(csv_out.data) 26 | 27 | return filename 28 | 29 | @shared_task(ignore_result = True) 30 | def email_reminder(to, subject, content): 31 | send_email(to, subject, content) -------------------------------------------------------------------------------- /frontend/pages/RegisterPage.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template : ` 3 |
4 | 5 | 6 | 7 | 8 |
9 | `, 10 | data(){ 11 | return { 12 | email : null, 13 | password : null, 14 | role : null, 15 | } 16 | }, 17 | methods : { 18 | async submitLogin(){ 19 | 20 | const res = await fetch(location.origin+'/register', 21 | {method : 'POST', 22 | headers: {'Content-Type' : 'application/json'}, 23 | body : JSON.stringify({'email': this.email,'password': this.password, 'role' : this.role}) 24 | }) 25 | if (res.ok){ 26 | console.log('we are register') 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /frontend/pages/AdminDashboardPage.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template : ` 3 |
4 |

this is admin dashboard

5 | 6 |
7 | `, 8 | methods : { 9 | async create_csv(){ 10 | const res = await fetch(location.origin + '/create-csv', { 11 | headers : { 12 | 'Authentication-Token' : this.$store.state.auth_token 13 | } 14 | }) 15 | const task_id = (await res.json()).task_id 16 | 17 | const interval = setInterval(async() => { 18 | const res = await fetch(`${location.origin}/get-csv/${task_id}` ) 19 | if (res.ok){ 20 | console.log('data is ready') 21 | window.open(`${location.origin}/get-csv/${task_id}`) 22 | clearInterval(interval) 23 | } 24 | 25 | }, 100) 26 | 27 | }, 28 | }, 29 | } -------------------------------------------------------------------------------- /req.txt: -------------------------------------------------------------------------------- 1 | amqp==5.2.0 2 | aniso8601==9.0.1 3 | async-timeout==4.0.3 4 | billiard==4.2.1 5 | blinker==1.8.2 6 | cachelib==0.9.0 7 | celery==5.4.0 8 | chardet==5.2.0 9 | click==8.1.7 10 | click-didyoumean==0.3.1 11 | click-plugins==1.1.1 12 | click-repl==0.3.0 13 | dnspython==2.7.0 14 | email_validator==2.2.0 15 | Flask==3.0.3 16 | Flask-Caching==2.3.0 17 | Flask-Excel==0.0.7 18 | Flask-Login==0.6.3 19 | Flask-Principal==0.4.0 20 | Flask-RESTful==0.3.10 21 | Flask-Security-Too==5.5.2 22 | Flask-SQLAlchemy==3.1.1 23 | Flask-WTF==1.2.1 24 | greenlet==3.1.1 25 | idna==3.10 26 | importlib_resources==6.4.5 27 | itsdangerous==2.2.0 28 | Jinja2==3.1.4 29 | kombu==5.4.2 30 | lml==0.1.0 31 | MarkupSafe==2.1.5 32 | passlib==1.7.4 33 | prompt_toolkit==3.0.48 34 | pyexcel==0.7.0 35 | pyexcel-io==0.6.6 36 | pyexcel-webio==0.1.4 37 | python-dateutil==2.9.0.post0 38 | pytz==2024.2 39 | redis==5.2.0 40 | six==1.16.0 41 | SQLAlchemy==2.0.36 42 | texttable==1.7.0 43 | typing_extensions==4.12.2 44 | tzdata==2024.2 45 | vine==5.1.0 46 | wcwidth==0.2.13 47 | Werkzeug==3.0.4 48 | WTForms==3.1.2 49 | -------------------------------------------------------------------------------- /frontend/components/User.js: -------------------------------------------------------------------------------- 1 | import { fetchWithAuth } from "../utils/fetchWithAuth.js" 2 | 3 | export default { 4 | props : ['email', 'followers', 'id', 'posts'], 5 | template : ` 6 |
7 |

{{email}}

8 |
9 |

followers {{followers}}

10 |

Posts {{posts}}

11 | 12 | 13 |
14 | `, 15 | methods : { 16 | async sendFollow(){ 17 | const res = await fetchWithAuth('/follow/' + this.id) 18 | 19 | if (res.ok){ 20 | // add popup 21 | console.log('user followed') 22 | } 23 | }, 24 | async sendUnFollow(){ 25 | const res = await fetchWithAuth('/unfollow/' + this.id) 26 | 27 | if (res.ok){ 28 | // add popup 29 | console.log('user un-followed') 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /frontend/pages/ExploreUsersPage.js: -------------------------------------------------------------------------------- 1 | import { fetchWithAuth } from "../utils/fetchWithAuth.js" 2 | import User from "../components/User.js" 3 | 4 | export default { 5 | template : `
6 |

Users

7 | 8 | 9 |
`, 10 | data(){ 11 | return { 12 | users : [], 13 | searchQuery : null, 14 | } 15 | }, 16 | methods : { 17 | async search(){ 18 | const res = await fetchWithAuth('/api/users?query='+this.searchQuery) 19 | if (res.ok){ 20 | this.users = await res.json() 21 | }else { 22 | console.warn('error fetching ') 23 | } 24 | } 25 | }, 26 | 27 | async mounted(){ 28 | console.log('mounted ran') 29 | const res = await fetchWithAuth('/api/users') 30 | this.users = await res.json() 31 | }, 32 | 33 | components : { 34 | User, 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /frontend/pages/LoginPage.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template : ` 3 |
4 | 5 | 6 | 7 |
8 | `, 9 | data(){ 10 | return { 11 | email : null, 12 | password : null, 13 | } 14 | }, 15 | methods : { 16 | async submitLogin(){ 17 | const res = await fetch(location.origin+'/login', 18 | { 19 | method : 'POST', 20 | headers: {'Content-Type' : 'application/json'}, 21 | body : JSON.stringify({'email': this.email,'password': this.password}) 22 | }) 23 | if (res.ok){ 24 | console.log('we are logged in') 25 | const data = await res.json() 26 | 27 | localStorage.setItem('user', JSON.stringify(data)) 28 | 29 | this.$store.commit('setUser') 30 | this.$router.push('/feed') 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /frontend/utils/store.js: -------------------------------------------------------------------------------- 1 | const store = new Vuex.Store({ 2 | state : { 3 | // like data 4 | auth_token : null, 5 | role : null, 6 | loggedIn : false, 7 | user_id : null, 8 | }, 9 | mutations : { 10 | // functions that change state 11 | setUser(state) { 12 | try{ 13 | if (JSON.parse(localStorage.getItem('user'))){ 14 | const user = JSON.parse(localStorage.getItem('user')); 15 | state.auth_token = user.token; 16 | state.role = user.role; 17 | state.loggedIn = true; 18 | state.user_id = user.id; 19 | } 20 | } catch { 21 | console.warn('not logged in') 22 | } 23 | }, 24 | 25 | logout(state){ 26 | state.auth_token = null; 27 | state.role = null; 28 | state.loggedIn = false; 29 | state.user_id = null; 30 | 31 | localStorage.removeItem('user') 32 | } 33 | }, 34 | actions : { 35 | // actions commit mutations can be async 36 | } 37 | }) 38 | 39 | store.commit('setUser') 40 | 41 | export default store; -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_login import login_required 3 | from backend.config import LocalDevelopmentConfig 4 | from backend.models import db, User, Role 5 | from flask_security import Security, SQLAlchemyUserDatastore, auth_required 6 | from flask_caching import Cache 7 | from backend.celery.celery_factory import celery_init_app 8 | import flask_excel as excel 9 | 10 | 11 | def createApp(): 12 | app = Flask(__name__, template_folder='frontend', static_folder='frontend', static_url_path='/static') 13 | 14 | app.config.from_object(LocalDevelopmentConfig) 15 | 16 | 17 | # model init 18 | db.init_app(app) 19 | 20 | # cache init 21 | cache = Cache(app) 22 | 23 | 24 | #flask security 25 | datastore = SQLAlchemyUserDatastore(db, User, Role) 26 | app.cache = cache 27 | 28 | app.security = Security(app, datastore=datastore, register_blueprint=False) 29 | app.app_context().push() 30 | 31 | from backend.resources import api 32 | # flask-restful init 33 | api.init_app(app) 34 | 35 | return app 36 | 37 | app = createApp() 38 | 39 | celery_app = celery_init_app(app) 40 | 41 | import backend.create_initial_data 42 | 43 | import backend.routes 44 | 45 | import backend.celery.celery_schedule 46 | 47 | excel.init_excel(app) 48 | 49 | if (__name__ == '__main__'): 50 | # flask-excel 51 | app.run() 52 | 53 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Blog Lite V2 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /frontend/utils/router.js: -------------------------------------------------------------------------------- 1 | const Home = { 2 | template : `

this is home

` 3 | } 4 | import AdminDashboardPage from "../pages/AdminDashboardPage.js"; 5 | import BlogsListPage from "../pages/BlogsListPage.js"; 6 | import DisplayBlogPage from "../pages/DisplayBlogPage.js"; 7 | import LoginPage from "../pages/LoginPage.js"; 8 | import RegisterPage from "../pages/RegisterPage.js"; 9 | import ExploreUsersPage from "../pages/ExploreUsersPage.js"; 10 | 11 | import store from './store.js' 12 | 13 | 14 | const routes = [ 15 | {path : '/', component : Home}, 16 | {path : '/login', component : LoginPage}, 17 | {path : '/register', component : RegisterPage}, 18 | {path : '/feed', component : BlogsListPage, meta : {requiresLogin : true}}, 19 | {path : '/blogs/:id', component : DisplayBlogPage, props : true, meta : {requiresLogin : true}}, 20 | {path : '/admin-dashboard', component : AdminDashboardPage, meta : {requiresLogin : true, role : "admin"}}, 21 | {path : '/explore', component : ExploreUsersPage } 22 | ] 23 | 24 | const router = new VueRouter({ 25 | routes 26 | }) 27 | 28 | // navigation guards 29 | router.beforeEach((to, from, next) => { 30 | if (to.matched.some((record) => record.meta.requiresLogin)){ 31 | if (!store.state.loggedIn){ 32 | next({path : '/login'}) 33 | } else if (to.meta.role && to.meta.role != store.state.role){ 34 | alert('role not authorized') 35 | next({path : '/'}) 36 | } else { 37 | next(); 38 | } 39 | } else { 40 | next(); 41 | } 42 | }) 43 | 44 | 45 | export default router; -------------------------------------------------------------------------------- /frontend/utils/fetchWithAuth.js: -------------------------------------------------------------------------------- 1 | import router from "./router.js" 2 | 3 | export const fetchWithAuth = async (url = '/', options = {auth : true}) => { 4 | const origin = window.location.origin; 5 | 6 | // Parse the stored user to get the authentication token 7 | const user = JSON.parse(localStorage.getItem('user')); 8 | const authToken = user?.['token']; // Get the auth token, if it exists 9 | 10 | // If authentication is required and no token is found, handle accordingly 11 | if (options.auth && !authToken) { 12 | console.error('Authentication token is missing.'); 13 | // Optionally, redirect to login or return an error 14 | router.push('/login'); 15 | return; 16 | } 17 | 18 | // Build the full request options object 19 | const fetchOptions = { 20 | method: options.method ?? 'GET', // Default to GET method 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | ...(authToken ? { 'Authentication-Token': authToken } : {}), // Include token if available 24 | ...options.headers, // Merge additional headers if provided 25 | }, 26 | ...(options.body ? { body: JSON.stringify(options.body) } : {}), // Stringify the body if provided 27 | ...options, // Merge any other options provided 28 | }; 29 | 30 | try { 31 | // Make the fetch request 32 | const res = await fetch(`${origin}${url}`, fetchOptions); 33 | 34 | // If response is forbidden, redirect to login 35 | if (res.status === 403 || res.status === 405) { 36 | router.push('/login'); 37 | return; 38 | } 39 | 40 | return res; // Return the response object 41 | 42 | } catch (error) { 43 | console.error('Fetch error:', error); 44 | // Handle errors (e.g., network issues) 45 | throw error; 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /backend/models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from flask_sqlalchemy import SQLAlchemy 3 | from flask_security import UserMixin, RoleMixin 4 | 5 | db = SQLAlchemy() 6 | 7 | # many-to-many relationship 8 | # association table 9 | followers = db.Table('followers', 10 | db.Column('follower_id', db.Integer, db.ForeignKey('user.id')), 11 | db.Column('followed_id', db.Integer, db.ForeignKey('user.id')) 12 | ) 13 | 14 | 15 | class User(db.Model, UserMixin): 16 | id = db.Column(db.Integer, primary_key = True) 17 | email = db.Column(db.String, unique = True, nullable = False) 18 | password = db.Column(db.String, nullable = False) 19 | # flask-security specific 20 | fs_uniquifier = db.Column(db.String, unique = True, nullable = False) 21 | active = db.Column(db.Boolean, default = True) 22 | roles = db.Relationship('Role', backref = 'bearers', secondary='user_roles') 23 | 24 | blogs = db.relationship('Blog', backref = 'author', lazy='dynamic') # User.blogs.filter() 25 | # User.blogs (blog object) 26 | # Blog.author (user object), using backref 27 | 28 | 29 | followed = db.relationship( 30 | 'User', secondary = followers, 31 | primaryjoin = (followers.c.follower_id == id), 32 | secondaryjoin = (followers.c.followed_id == id), 33 | backref = db.backref('followers', lazy='dynamic'), lazy = 'dynamic' 34 | ) 35 | 36 | @property 37 | def num_followed(self): 38 | return self.followed.count() 39 | 40 | # User(particual user).num_followed 41 | 42 | @property 43 | def num_following(self): 44 | return self.followers.count() 45 | 46 | @property 47 | def num_post(self): 48 | return self.blogs.count() 49 | 50 | 51 | 52 | class Role(db.Model, RoleMixin): 53 | id = db.Column(db.Integer, primary_key = True) 54 | name = db.Column(db.String, unique = True, nullable = False) 55 | description = db.Column(db.String, nullable = False) 56 | 57 | class UserRoles(db.Model): 58 | id = db.Column(db.Integer, primary_key = True) 59 | user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 60 | role_id = db.Column(db.Integer, db.ForeignKey('role.id')) 61 | 62 | class Blog(db.Model): 63 | id = db.Column(db.Integer, primary_key = True) 64 | title = db.Column(db.String) 65 | caption = db.Column(db.String) 66 | image_url = db.Column(db.String) 67 | timestamp = db.Column(db.DateTime, index = True, default = datetime.now()) 68 | user_id = db.Column(db.Integer, db.ForeignKey('user.id')) -------------------------------------------------------------------------------- /backend/resources.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, current_app as app 2 | from flask_restful import Api, Resource, fields, marshal_with 3 | from flask_security import auth_required, current_user 4 | from backend.models import Blog, Role, User, db 5 | 6 | cache = app.cache 7 | 8 | api = Api(prefix='/api') 9 | 10 | blog_fields = { 11 | 'id' : fields.Integer, 12 | 'title' : fields.String, 13 | 'caption' : fields.String, 14 | 'image_url' : fields.String, 15 | 'user_id' : fields.Integer, 16 | 'timestamp' : fields.DateTime, 17 | 'author.email' : fields.String, 18 | } 19 | 20 | class BlogAPI(Resource): 21 | 22 | @auth_required('token') 23 | @cache.memoize(timeout = 5) 24 | @marshal_with(blog_fields) 25 | def get(self, blog_id): 26 | blog = Blog.query.get(blog_id) 27 | 28 | if not blog: 29 | return {"message" : "not found"}, 404 30 | return blog 31 | 32 | @auth_required('token') 33 | def delete(self, blog_id): 34 | 35 | blog = Blog.query.get(blog_id) 36 | 37 | if not blog: 38 | return {"message" : "not found"}, 404 39 | 40 | if blog.user_id == current_user.id: 41 | 42 | db.session.delete(blog) 43 | db.session.commit() 44 | else: 45 | return {"message" : "not valid user"}, 403 46 | 47 | 48 | class BlogListAPI(Resource): 49 | 50 | @auth_required('token') 51 | @cache.cached(timeout = 5, key_prefix = "blog_list") 52 | @marshal_with(blog_fields) 53 | def get(self ): 54 | blogs = Blog.query.all() 55 | return blogs 56 | 57 | @auth_required('token') 58 | def post(self): 59 | data = request.get_json() 60 | title = data.get('title') 61 | caption = data.get('caption') 62 | image_url = data.get('image_url') 63 | 64 | blog = Blog(title = title, caption = caption, image_url = image_url, user_id = current_user.id) 65 | 66 | db.session.add(blog) 67 | db.session.commit() 68 | return jsonify({'message' : 'blog created'}) 69 | 70 | 71 | # user list api 72 | 73 | user_list_fields = { 74 | 'id' : fields.Integer, 75 | 'email' : fields.String, 76 | 'num_following' : fields.Integer, 77 | 'num_followed' : fields.Integer, 78 | 'num_post' : fields.Integer, 79 | } 80 | 81 | class UserListAPI(Resource): 82 | 83 | # search functionality 84 | # query parameters url....?key=value&key2=value2 85 | @marshal_with(user_list_fields) 86 | def get(self): 87 | 88 | query = request.args.get('query') 89 | 90 | if query: 91 | users = User.query.join(User.roles).filter( 92 | Role.name == 'user', 93 | User.active == True, 94 | User.email.ilike(f'%{query}%') 95 | ).all() 96 | else : 97 | users = User.query.join(User.roles).filter(Role.name == 'user', User.active == True).all() 98 | 99 | return users 100 | 101 | 102 | user_fields = { 103 | 'id' : fields.Integer, 104 | 'email' : fields.String, 105 | 'num_following' : fields.Integer, 106 | 'num_followed' : fields.Integer, 107 | 'num_post' : fields.Integer, 108 | 'blogs' : fields.List(fields.Nested(blog_fields)) 109 | } 110 | 111 | 112 | class UserAPI(Resource): 113 | 114 | @marshal_with(user_fields) 115 | def get(self, user_id): 116 | user = User.query.get(user_id) 117 | return user 118 | 119 | class FeedAPI(Resource): 120 | 121 | @auth_required('token') 122 | @marshal_with(blog_fields) 123 | def get(self): 124 | followed_ids = [ followed.id for followed in current_user.followed] 125 | blogs = Blog.query.filter(Blog.user_id.in_(followed_ids)).all() 126 | if not blogs: 127 | return {'message' : 'no posts from followed users'}, 200 128 | return blogs 129 | 130 | 131 | 132 | api.add_resource(BlogAPI, '/blogs/') 133 | api.add_resource(BlogListAPI,'/blogs') 134 | api.add_resource(UserListAPI, '/users') 135 | api.add_resource(UserAPI, '/users/') 136 | api.add_resource(FeedAPI, '/feed') -------------------------------------------------------------------------------- /backend/routes.py: -------------------------------------------------------------------------------- 1 | from flask import current_app as app, jsonify, render_template, request, send_file 2 | from flask_security import auth_required, verify_password, hash_password, current_user 3 | from backend.models import User, db 4 | from datetime import datetime 5 | from backend.celery.tasks import add, create_csv 6 | from celery.result import AsyncResult 7 | 8 | datastore = app.security.datastore 9 | cache = app.cache 10 | 11 | @app.route('/') 12 | def home(): 13 | return render_template('index.html') 14 | 15 | 16 | @app.get('/celery') 17 | def celery(): 18 | task = add.delay(10, 20) 19 | return {'task_id' : task.id} 20 | 21 | 22 | 23 | @auth_required('token') 24 | @app.get('/get-csv/') 25 | def getCSV(id): 26 | result = AsyncResult(id) 27 | 28 | if result.ready(): 29 | return send_file(f'./backend/celery/user-downloads/{result.result}'), 200 30 | else: 31 | return {'message' : 'task not ready'}, 405 32 | 33 | @auth_required('token') 34 | @app.get('/create-csv') 35 | def createCSV(): 36 | task = create_csv.delay() 37 | return {'task_id' : task.id}, 200 38 | 39 | @app.get('/cache') 40 | @cache.cached(timeout = 5) 41 | def cache(): 42 | return {'time' : str(datetime.now())} 43 | 44 | 45 | @app.get('/protected') 46 | @auth_required('token') 47 | def protected(): 48 | return '

only accessible by auth user

' 49 | 50 | @app.route('/login', methods=['POST']) 51 | def login(): 52 | data = request.get_json() 53 | 54 | email = data.get('email') 55 | password = data.get('password') 56 | 57 | if not email or not password: 58 | return jsonify({"message" : "invalid inputs"}), 404 59 | 60 | user = datastore.find_user(email = email) 61 | 62 | if not user: 63 | return jsonify({"message" : "invalid email"}), 404 64 | 65 | if verify_password(password, user.password): 66 | return jsonify({'token' : user.get_auth_token(), 'email' : user.email, 'role' : user.roles[0].name, 'id' : user.id}) 67 | 68 | return jsonify({'message' : 'password wrong'}), 400 69 | 70 | @app.route('/register', methods=['POST']) 71 | def register(): 72 | data = request.get_json() 73 | 74 | email = data.get('email') 75 | password = data.get('password') 76 | role = data.get('role') 77 | 78 | if not email or not password or role not in ['admin', 'user']: 79 | return jsonify({"message" : "invalid inputs"}), 404 80 | 81 | user = datastore.find_user(email = email) 82 | 83 | if user: 84 | return jsonify({"message" : "user already exists"}), 404 85 | 86 | try : 87 | datastore.create_user(email = email, password = hash_password(password), roles = [role], active = True) 88 | db.session.commit() 89 | return jsonify({"message" : "user created"}), 200 90 | except: 91 | db.session.rollback() 92 | return jsonify({"message" : "error creating user"}), 400 93 | 94 | 95 | 96 | @app.get('/follow/') 97 | def follow(id): 98 | followed_user = User.query.get_or_404(id) 99 | 100 | if not followed_user: 101 | return jsonify({'message' : "user doesn't exist"}), 404 102 | 103 | if followed_user.id == current_user.id: 104 | return jsonify({'message' : "cant follow yourself"}), 400 105 | 106 | if followed_user in current_user.followed: 107 | return jsonify({'message' : "already following"}), 400 108 | 109 | if followed_user.active == False: 110 | return jsonify({'message' : "user is banned"}), 400 111 | 112 | current_user.followed.append(followed_user) 113 | db.session.commit() 114 | 115 | return jsonify({'message' : 'following user '}), 200 116 | 117 | 118 | 119 | @app.get('/unfollow/') 120 | def unfollow(id): 121 | followed_user = User.query.get_or_404(id) 122 | 123 | if not followed_user: 124 | return jsonify({'message' : "user doesn't exist"}), 404 125 | 126 | if followed_user.id == current_user.id: 127 | return jsonify({'message' : "cant un-follow yourself"}), 400 128 | 129 | if followed_user not in current_user.followed: 130 | return jsonify({'message' : "not following"}), 400 131 | 132 | if followed_user.active == False: 133 | return jsonify({'message' : "user is banned"}), 400 134 | 135 | current_user.followed.remove(followed_user) 136 | db.session.commit() 137 | 138 | return jsonify({'message' : 'now un-followed'}), 200 -------------------------------------------------------------------------------- /seed.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import random 3 | 4 | BASE_URL = 'http://127.0.0.1:5000' # Adjust based on your Flask app's URL 5 | 6 | NAMES_LIST = [ 7 | "Aarav", "Vivaan", "Aditya", "Vihaan", "Krishna", "Sai", "Aryan", "Dhruv", 8 | "Karthik", "Shreyas", "Nikhil", "Rohan", "Siddharth", "Yash", "Rudra", "Ishaan", 9 | "Om", "Arjun", "Ananya", "Aditi", "Meera", "Pooja", "Priya", "Sana", "Riya", 10 | "Naina", "Tara", "Aishwarya", "Diya", "Tanvi", "Isha", "Kavya", "Sneha", "Maya", 11 | "Sanya", "Lakshmi", "Radhika", "Sakshi", "Neha", "Anjali", "Ashwin", "Raghav", 12 | "Harsh", "Manish", "Pranav", "Saurabh", "Akash", "Bhavesh", "Kunal", "Aniket", 13 | "Ajay", "Vikas", "Sanjay", "Chirag", "Gaurav", "Rajesh", "Amit", "Rahul", 14 | "Suresh", "Pavan", "Varun", "Ravi", "Bharat", "Mohit", "Jay", "Mahesh", 15 | "Vikram", "Tarun", "Sunil", "Kiran", "Ritu", "Swati", "Rashmi", "Geeta", 16 | "Suman", "Shilpa", "Rekha", "Vidya", "Manisha", "Sunita", "Jaya", "Alka", 17 | "Divya", "Bhavana", "Monika", "Komal", "Pallavi", "Seema", "Meenakshi", 18 | "Sheetal", "Payal", "Sonali", "Rajiv", "Naveen", "Rakesh", "Mohan", "Balaji" 19 | ] 20 | 21 | BLOG_TITLES = [ 22 | "10 Tips to Boost Your Productivity at Work", 23 | "The Ultimate Guide to Mindfulness for Beginners", 24 | "How to Create a Successful Side Hustle", 25 | "Top 5 Coding Languages to Learn in 2024", 26 | "Exploring the World of Sustainable Fashion", 27 | "How to Build a Morning Routine for Success", 28 | "The Benefits of Yoga for Mental Health", 29 | "A Beginner’s Guide to Cryptocurrency", 30 | "How to Travel on a Budget: 20 Expert Tips", 31 | "The Science of Building Healthy Habits", 32 | "Mastering Time Management: Strategies That Work", 33 | "The Future of Artificial Intelligence in Healthcare", 34 | "How to Start a Profitable Blog in 2024", 35 | "Essential Tools for Remote Work Success", 36 | "Exploring the Benefits of a Plant-Based Diet", 37 | "The Psychology Behind Decision Making", 38 | "A Guide to Freelancing: How to Get Started", 39 | "How to Create an Effective Marketing Strategy", 40 | "The Art of Negotiation: Tips for Better Deals", 41 | "How to Stay Motivated When Working From Home", 42 | "The Importance of Sleep for Mental and Physical Health", 43 | "Top 10 Fitness Trends to Watch Out For", 44 | "How to Improve Your Focus and Concentration", 45 | "The Benefits of Learning a New Language", 46 | "How to Overcome Procrastination and Get Things Done", 47 | "The Power of Positive Thinking: A Complete Guide", 48 | "10 Easy Steps to Start Your Own Business", 49 | "How to Improve Your Leadership Skills", 50 | "The Role of Technology in Education Today", 51 | "How to Invest in Real Estate: A Beginner’s Guide", 52 | "Tips for Building a Strong Professional Network", 53 | "The Importance of Emotional Intelligence in the Workplace", 54 | "How to Create a Digital Marketing Plan", 55 | "10 Essential Life Skills You Need to Succeed", 56 | "The Impact of Social Media on Mental Health", 57 | "How to Plan a Solo Travel Adventure", 58 | "The Pros and Cons of Remote Work", 59 | "The Future of Virtual Reality in Entertainment", 60 | "How to Start a Career in Web Development", 61 | "A Complete Guide to Time Blocking for Productivity", 62 | "How to Build a Personal Brand Online", 63 | "The Benefits of Reading for Personal Growth", 64 | "How to Set and Achieve Your Long-Term Goals", 65 | "Top 5 Skills Every Entrepreneur Should Have", 66 | "How to Effectively Manage Stress in Daily Life", 67 | "A Beginner’s Guide to Stock Market Investing", 68 | "How to Stay Creative in a Fast-Paced World", 69 | "The Power of Networking: How to Build Valuable Connections", 70 | "How to Improve Your Public Speaking Skills", 71 | "The Importance of Financial Literacy for Young Adults", 72 | "How to Create a Social Media Content Calendar", 73 | "The Benefits of Volunteering for Personal Development", 74 | "How to Build a Strong Online Community", 75 | "A Guide to Self-Care for Busy Professionals", 76 | "How to Create a Minimalist Lifestyle", 77 | "The Role of Ethics in Modern Technology", 78 | "How to Improve Your Problem-Solving Skills", 79 | "The Benefits of Meditation for Stress Relief", 80 | "How to Achieve Work-Life Balance in a Hectic World", 81 | "The Evolution of E-Commerce: Trends and Innovations", 82 | "How to Create a Successful Online Course", 83 | "The Importance of Soft Skills in the Workplace", 84 | "How to Build Confidence and Overcome Self-Doubt", 85 | "The Future of Renewable Energy: What You Need to Know", 86 | "How to Master the Art of Delegation", 87 | "The Impact of Climate Change on Global Economies", 88 | "How to Write Engaging and Shareable Blog Posts", 89 | "The Science Behind Effective Goal Setting", 90 | "How to Handle Criticism Like a Pro", 91 | "Top 10 Books Every Entrepreneur Should Read", 92 | "The Benefits of Journaling for Personal Growth", 93 | "How to Start a Podcast: A Step-by-Step Guide", 94 | "The Future of Work: Trends Shaping the Job Market", 95 | "How to Develop a Growth Mindset for Success", 96 | "The Role of Innovation in Business Growth", 97 | "How to Create a Personal Development Plan", 98 | "The Importance of Diversity and Inclusion in the Workplace", 99 | "How to Make Money with Affiliate Marketing", 100 | "Top 5 Ways to Improve Your Mental Health", 101 | "The Art of Writing Compelling Product Descriptions", 102 | "How to Master the Gig Economy: Tips for Freelancers", 103 | "The Impact of 5G Technology on Businesses", 104 | "How to Use Data Analytics to Grow Your Business", 105 | "The Benefits of Taking Regular Breaks at Work", 106 | "How to Improve Your Customer Service Skills", 107 | "The Role of AI in Enhancing Customer Experience", 108 | "How to Start a Nonprofit Organization: A Step-by-Step Guide", 109 | "The Importance of Continuous Learning in Your Career", 110 | "How to Manage Your Finances as a Freelancer", 111 | "Top 5 Marketing Strategies for Small Businesses", 112 | "How to Create Engaging Video Content for Social Media", 113 | "The Benefits of Implementing a CRM System", 114 | "How to Build a Strong Email Marketing Campaign", 115 | "The Role of Blockchain in the Future of Finance", 116 | "How to Create a Sustainable Business Model", 117 | "Top 10 Creative Ways to Generate Passive Income", 118 | "How to Build Strong Relationships with Your Clients", 119 | "The Future of Remote Collaboration Tools", 120 | "How to Leverage Influencer Marketing for Your Business", 121 | "A Beginner’s Guide to Dropshipping in 2024", 122 | "How to Plan and Execute a Successful Event", 123 | "The Power of Gratitude in Personal and Professional Life", 124 | "How to Build and Scale a Startup in 2024" 125 | ] 126 | 127 | BLOG_CONTENT = [ 128 | """ 129 |

In today’s fast-paced world, productivity is essential. Here are 10 tips that can help you get more done in less time.

130 |
    131 |
  • Plan your day in advance.
  • 132 |
  • Use time blocking to manage tasks.
  • 133 |
  • Take regular breaks.
  • 134 |
135 |

Implement these tips, and you’ll notice a significant boost in your productivity.

136 | """, 137 | 138 | """ 139 |

Meditation has numerous benefits for mental health. Here's how to start:

140 |
    141 |
  1. Find a quiet place.
  2. 142 |
  3. Focus on your breath.
  4. 143 |
  5. Let go of any distractions.
  6. 144 |
145 |

Try meditating for just 10 minutes a day, and see how it changes your mind and body.

146 | """, 147 | 148 | """ 149 |

Coding can be an enjoyable journey when you have the right resources. Below are some useful tips:

150 |
    151 |
  • Practice daily, even for 30 minutes.
  • 152 |
  • Join coding communities.
  • 153 |
  • Work on real-life projects.
  • 154 |
155 |

By following these steps, you'll become a better programmer and enjoy the process more!

156 | """, 157 | 158 | """ 159 |

Healthy eating is the foundation of a good life. Here are some tips to keep in mind:

160 |
    161 |
  • Include more fruits and vegetables in your meals.
  • 162 |
  • Avoid processed foods as much as possible.
  • 163 |
  • Drink plenty of water throughout the day.
  • 164 |
165 |

Remember, your body is your temple, and nourishing it properly is key to a happy life.

166 | """, 167 | 168 | """ 169 |

Freelancing offers many benefits like flexible working hours, but it can also come with challenges. Here’s how to make it work for you:

170 |
    171 |
  • Create a dedicated workspace.
  • 172 |
  • Set clear boundaries with clients.
  • 173 |
  • Manage your time effectively.
  • 174 |
175 |

By maintaining discipline, you can have a successful freelance career with a balanced life.

176 | """, 177 | 178 | """ 179 |

Traveling on a budget doesn’t mean missing out on great experiences. Here are some strategies to help:

180 |
    181 |
  • Book flights in advance.
  • 182 |
  • Stay in budget accommodations like hostels or Airbnbs.
  • 183 |
  • Use public transportation instead of taxis.
  • 184 |
185 |

With these tips, you can travel the world without breaking the bank!

186 | """, 187 | 188 | """ 189 |

Starting a blog can be overwhelming, but following these steps will help you get started:

190 |
    191 |
  1. Choose a niche you are passionate about.
  2. 192 |
  3. Select a blogging platform (like WordPress or Blogger).
  4. 193 |
  5. Write engaging and informative content.
  6. 194 |
195 |

Once you have these basics in place, you’ll be well on your way to becoming a successful blogger.

196 | """, 197 | 198 | """ 199 |

When managing stress, it’s important to practice self-care regularly. Here’s how:

200 |
    201 |
  • Practice mindfulness or meditation.
  • 202 |
  • Exercise regularly, even if it’s a short walk.
  • 203 |
  • Take time to disconnect from technology.
  • 204 |
205 |

By focusing on self-care, you can reduce stress and improve your overall well-being.

206 | """, 207 | 208 | """ 209 |

Investing in real estate can be a smart financial decision. Consider these factors before getting started:

210 |
    211 |
  • Research the market thoroughly.
  • 212 |
  • Start small with affordable properties.
  • 213 |
  • Work with experienced agents and investors.
  • 214 |
215 |

Real estate is a long-term investment, so be patient and plan for the future.

216 | """, 217 | 218 | """ 219 |

Public speaking is a vital skill that can boost your career. Here are a few techniques to improve:

220 |
    221 |
  • Practice in front of a mirror or record yourself.
  • 222 |
  • Know your audience and tailor your message accordingly.
  • 223 |
  • Focus on your body language and tone.
  • 224 |
225 |

With practice and confidence, you’ll become a powerful and engaging speaker.

226 | """ 227 | ] 228 | 229 | 230 | def get_random_name(): 231 | return NAMES_LIST[random.randint(0,95)] 232 | 233 | def get_random_title(): 234 | return BLOG_TITLES[random.randint(0,99)] 235 | 236 | def get_random_caption(): 237 | return BLOG_CONTENT[random.randint(0,9)] 238 | 239 | 240 | def register_user(email, password): 241 | response = requests.post(f"{BASE_URL}/register", json={"email": email, "password": password, "role" : "user"}) 242 | return response.json() 243 | 244 | def login_user(email, password): 245 | response = requests.post(f"{BASE_URL}/login", json={"email": email, "password": password }) 246 | return response.json() 247 | 248 | def create_blog(auth_token, title, caption, image_url='https://picsum.photos/200'): 249 | headers = { 250 | 'Authentication-Token': f'{auth_token}' # Assuming you're using Bearer token 251 | } 252 | response = requests.post(f"{BASE_URL}/api/blogs", json={ 253 | "title": title, 254 | "caption": caption, 255 | "image_url": image_url 256 | }, headers=headers) 257 | return response.json() 258 | 259 | def follow_user(auth_token, follower_id, followed_id): 260 | headers = { 261 | 'Authentication-Token': f'{auth_token}' # Assuming you're using Bearer token 262 | } 263 | response = requests.get(f"{BASE_URL}/follow/{followed_id}", headers=headers) 264 | return response.json() 265 | 266 | def seed_database(): 267 | users = [] 268 | # Register 10 users and log them in 269 | for i in range(10): 270 | email = f"{get_random_name()}@example.com" 271 | password = "1234" 272 | register_user(email, password) 273 | login_response = login_user(email, password) 274 | if 'token' in login_response: 275 | users.append({"id": i + 1, "email": email, "token": login_response['token']}) 276 | 277 | 278 | # Create blogs for each user 279 | for user in users: 280 | user_id = user['id'] 281 | auth_token = user['token'] 282 | for i in range(3): # Create 3 blogs per user 283 | title = get_random_title() 284 | caption = get_random_caption() 285 | create_blog(auth_token, title, caption) 286 | 287 | # Create followers 288 | for user in users: 289 | followed_users = random.sample([u for u in users if u['id'] != user['id']], k=random.randint(1, len(users) - 1)) 290 | for followed in followed_users: 291 | follow_user(user['token'], user['id'], followed['id']) 292 | 293 | if __name__ == "__main__": 294 | seed_database() 295 | --------------------------------------------------------------------------------