├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── documentation_upgrade.md │ ├── enhancement.md │ ├── feature_request.md │ ├── question_discussion.md │ └── ui_ux.md └── PULL_REQUEST_TEMPLATE │ └── pull_request.md ├── .gitignore ├── Code_of_Conduct.md ├── __pycache__ └── app.cpython-311.pyc ├── app.py ├── contributing.md ├── readme.md ├── requirements.txt ├── static ├── analytics.css ├── images │ └── logo.png └── style.css ├── templates ├── 404_page.html ├── analytics.html ├── api.html ├── documentation.html ├── errorpage.html └── index.html └── vercel.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve & fix 4 | title: "[BUG] Title" 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | ## Description 10 | A clear and concise description of what the bug is. 11 | 12 | ## Steps to Reproduce 13 | 1. Go to '...' 14 | 2. Click on '...' 15 | 3. Scroll down to '...' 16 | 4. See error 17 | 18 | ## Expected Behavior 19 | A description of what you expected to happen. 20 | 21 | ## Actual Behavior 22 | A description of what actually happened. 23 | 24 | ## Screenshots 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | ## Environment 28 | - OS: [e.g., Windows, macOS, Linux] 29 | - Version: [e.g., 1.0.0] 30 | - Browser: [e.g., Chrome, Firefox, etc.] 31 | 32 | ## Additional Context 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation_upgrade.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation Upgrade 3 | about: Suggest an improvement to the project documentation 4 | title: "[DOC] Title" 5 | labels: documentation 6 | assignees: '' 7 | --- 8 | 9 | ## Current Documentation 10 | Describe the section of the documentation that needs improvement. 11 | 12 | ## Suggested Changes 13 | Outline the changes you propose, including any new content or restructuring. 14 | 15 | ## Additional Resources 16 | Provide links to relevant resources or references that could aid in updating the documentation. 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement 3 | about: Suggest improvements or enhancements to existing features 4 | title: "[ENHANCEMENT] Title" 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | ## Description 10 | Please describe the enhancement you would like to propose. 11 | 12 | ## Benefits 13 | Explain how this enhancement improves the project or user experience. 14 | 15 | ## Additional Context 16 | Provide any additional context or examples that may help in understanding the enhancement. 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for a new feature 4 | title: "[FEATURE] Title" 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | ## Description 10 | Please describe the feature you would like to see. 11 | 12 | ## Motivation 13 | Explain why this feature is important and what problem it solves. 14 | 15 | ## Proposed Solution 16 | Outline how you envision the feature working or any specific details that should be included. 17 | 18 | ## Additional Context 19 | Link to any relevant resources or examples of similar features. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question_discussion.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question/Discussion 3 | about: Ask a question or start a discussion 4 | title: "[DISCUSSION] Title" 5 | labels: question 6 | assignees: '' 7 | --- 8 | 9 | ## Question/Discussion Topic 10 | Clearly state your question or the topic you want to discuss. 11 | 12 | ## Context 13 | Provide any relevant context that will help others understand your question. 14 | 15 | ## Suggested Solutions (if applicable) 16 | If you have any ideas or suggestions related to your question, please include them here. 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ui_ux.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: UI/UX Update 3 | about: Suggest improvements related to user interface or user experience 4 | title: "[UI/UX] Title" 5 | labels: enhancement, UI/UX 6 | assignees: '' 7 | --- 8 | 9 | ## Description 10 | Describe the current UI/UX issue and why it needs to be addressed. 11 | 12 | ## Proposed Changes 13 | Outline the specific changes you propose to enhance the user interface or experience. 14 | 15 | ## Examples 16 | If possible, provide mockups or references to similar designs that illustrate your suggestions. 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/pull_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Pull Request 3 | about: Create a pull request to propose changes 4 | title: "[PR] Title" 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | ## Description 10 | Provide a summary of the changes made and the motivation behind them. 11 | 12 | ## Related Issue 13 | If applicable, link to the issue this pull request addresses (e.g., fixes #123). 14 | 15 | ## How to Test 16 | Describe the steps to test the changes made in this pull request. 17 | 18 | ## Checklist 19 | - [ ] My code follows the project's coding style 20 | - [ ] I have tested my changes 21 | - [ ] I have added necessary documentation 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | .env 3 | _pycache_/ 4 | __pycache__/ 5 | .vercel 6 | .venv/ -------------------------------------------------------------------------------- /Code_of_Conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct for OSUS 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at harshithtunuguntla@gmail.com. 63 | All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. 64 | The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 65 | 66 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series 85 | of actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or 92 | permanent ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within 112 | the community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. 118 | 119 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 120 | enforcement ladder](https://github.com/mozilla/diversity). 121 | 122 | [homepage]: https://www.contributor-covenant.org 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | https://www.contributor-covenant.org/faq. Translations are available at 126 | https://www.contributor-covenant.org/translations. 127 | -------------------------------------------------------------------------------- /__pycache__/app.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harshithtunuguntla/project-osus/f4d20c437aba542add4e8f112650705c51547f12/__pycache__/app.cpython-311.pyc -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, jsonify, redirect 2 | from datetime import datetime 3 | import pytz 4 | 5 | from pytz import timezone, utc 6 | import os 7 | from datetime import datetime, timezone 8 | from pymongo import MongoClient 9 | from dotenv import load_dotenv 10 | import random 11 | import string 12 | 13 | load_dotenv() 14 | app = Flask(__name__) 15 | #,int(os.getenv('MONGO_PORT')) 16 | # MongoDB Setup 17 | client = MongoClient(os.getenv('MONGO_PATH')) 18 | db = client.ShortUrlDatabase 19 | url_collection = db.URLData 20 | 21 | # Helper Functions 22 | def generate_random_string(length=5): 23 | """Generate a random string of specified length with both uppercase and lowercase letters.""" 24 | return ''.join(random.choices(string.ascii_letters, k=length)) 25 | 26 | def check_keyword_existence(keyword): 27 | """Check if the given keyword already exists in the database.""" 28 | return url_collection.find_one({'keyword': keyword}) is not None 29 | 30 | def insert_url_data(keyword, longUrl, expiration_datetime_utc ): 31 | """Insert the long URL and keyword into the database.""" 32 | url_collection.insert_one( { 33 | 'keyword': keyword, 34 | 'url': longUrl, 35 | 'clicks': 0, 36 | 'created_at': datetime.now(timezone.utc), 37 | 'expiration': expiration_datetime_utc # This will be None if no expiration is set 38 | }) 39 | 40 | def get_long_url_by_keyword(keyword): 41 | """Retrieve the long URL associated with the given keyword.""" 42 | return url_collection.find_one({'keyword': keyword}) 43 | 44 | def update_click_count(keyword): 45 | """Increment the click count for the given keyword.""" 46 | url_collection.update_one({'keyword': keyword}, {'$inc': {'clicks': 1}}) 47 | from datetime import datetime 48 | 49 | def get_expiration_datetime(expiration_date, expiration_time, expiration_period=None, user_timezone=None): 50 | expiration_datetime_utc = None 51 | print(expiration_period) 52 | 53 | if expiration_date and expiration_time: 54 | # Normalize the expiration_time if it contains "PM" and is in 24-hour format 55 | if expiration_period and expiration_period.upper() == 'PM' and int(expiration_time.split(':')[0]) > 12: 56 | # Convert 24-hour PM time to 12-hour format 57 | hour = int(expiration_time.split(':')[0]) - 12 58 | expiration_time = f"{hour}:{expiration_time.split(':')[1]}" 59 | 60 | # Create the datetime string 61 | expiration_datetime_str = f"{expiration_date} {expiration_time} {expiration_period.upper() if expiration_period else ''}".strip() 62 | print(expiration_datetime_str) 63 | 64 | try: 65 | # Parse the datetime as per the provided format 66 | if expiration_period and expiration_period.upper() in ['AM', 'PM']: 67 | # Parse as 12-hour format with AM/PM 68 | expiration_datetime = datetime.strptime(expiration_datetime_str, '%Y-%m-%d %I:%M %p') 69 | else: 70 | # Parse as 24-hour format (no AM/PM) 71 | expiration_datetime = datetime.strptime(expiration_datetime_str, '%Y-%m-%d %H:%M') 72 | 73 | # If a timezone is provided, localize the datetime to that timezone 74 | if user_timezone: 75 | local_tz = pytz.timezone(user_timezone) 76 | local_expiration_dt = local_tz.localize(expiration_datetime) 77 | # Convert local datetime to UTC 78 | expiration_datetime_utc = local_expiration_dt.astimezone(pytz.utc) 79 | else: 80 | # If no timezone is provided, treat the datetime as UTC 81 | expiration_datetime_utc = expiration_datetime 82 | 83 | return expiration_datetime_utc 84 | except ValueError: 85 | return {'error': 'Invalid expiration time format. Use HH:MM AM/PM for 12-hour or HH:MM for 24-hour format.'}, 400 86 | return None # Return None if no expiration is set 87 | 88 | # Routes 89 | @app.route('/') 90 | def home(): 91 | """Render the index page.""" 92 | return render_template('index.html') 93 | 94 | @app.route('/documentation') 95 | def documentation(): 96 | """Render the documentation page.""" 97 | return render_template('documentation.html') 98 | @app.route('/api-docs') 99 | def api_documentation(): 100 | """Render the api docs page.""" 101 | return render_template('api.html') 102 | 103 | @app.route('/getURL') 104 | def current_url(): 105 | """Return the current working URL of the application.""" 106 | return f'The app is running on {request.host_url}' 107 | 108 | 109 | @app.route('/shorten', methods=['POST']) 110 | def shortenAPI(): 111 | ''' 112 | This method will shorten the link and give the short URL with an optional expiration time. 113 | ''' 114 | print('URL Shorten Called') 115 | print('Mongo Connection: ' + str(os.getenv('MONGO_PATH')[:6])) 116 | print("Called") 117 | 118 | if request.method == 'POST': 119 | # Extracting data from the request 120 | longUrl = request.json.get("longUrl") 121 | keyword = request.json.get("keyword") 122 | expiration_date = request.json.get("expirationDate") # Date in 'YYYY-MM-DD' format (optional) 123 | expiration_time = request.json.get("expirationTime") # Time in 'HH:MM' format (optional) 124 | expiration_period = request.json.get("expirationPeriod") # 'AM' or 'PM' (optional) 125 | user_timezone = request.json.get("timezone") # User's timezone (optional) 126 | 127 | # Logs 128 | print('Long URL Received: ' + str(longUrl)) 129 | print('Keyword Received: ' + str(keyword)) 130 | 131 | # Generate a random keyword if none provided 132 | if not keyword or keyword == '': 133 | keyword = generate_random_string() 134 | 135 | # Combine expiration date and time if provided 136 | expiration_datetime_utc=get_expiration_datetime(expiration_date,expiration_time,expiration_period,user_timezone) 137 | 138 | # Check if the keyword is already present in the database 139 | if check_keyword_existence(keyword): 140 | return jsonify({'error': 'The keyword already exists. Please choose a different one.'}), 400 141 | insert_url_data(keyword, longUrl,expiration_datetime_utc) 142 | short_url = f'{request.host_url}{keyword}' 143 | return jsonify({'shortUrl': short_url}), 200 144 | 145 | @app.route('/analytics', methods=['GET', 'POST']) 146 | def analyticsAPI(): 147 | ''' 148 | This /analytics route consists of both GET and POST requests. 149 | - GET: Returns the analytics.html file. 150 | - POST: Returns the total number of clicks (visits) for the specific shortened URL from the DB along with expiration details if applicable. 151 | ''' 152 | 153 | if request.method == 'GET': 154 | print('Analytics page requested.') 155 | return render_template('analytics.html') 156 | 157 | if request.method == 'POST': 158 | keyword = request.json.get("keyword") 159 | print('Keyword Received: ' + str(keyword)) 160 | 161 | # Check if the keyword exists in the database 162 | url_data = url_collection.find_one({'keyword': keyword}) 163 | 164 | if url_data: 165 | # Return the current clicks count and expiration details 166 | clicks_count = url_data['clicks'] 167 | expiration = url_data.get('expiration') 168 | 169 | response_data = {'clicks': clicks_count} 170 | 171 | if expiration: 172 | remaining_time = handle_expiration(expiration) 173 | response_data['expiration'] = remaining_time 174 | 175 | print(f'Keyword: {keyword}, Clicks Count: {clicks_count}, Expiration: {response_data.get("expiration")}') 176 | return jsonify(response_data), 200 177 | else: 178 | print('Keyword not found in DB') 179 | return jsonify({'error': 'Keyword not found.'}), 404 180 | 181 | def handle_expiration(expiration): 182 | # Return the entire expiration timestamp as a string 183 | return expiration.isoformat() # Returns in ISO 8601 format (YYYY-MM-DDTHH:MM:SS.mmmmmm) 184 | 185 | 186 | 187 | @app.route('/heartbeat') 188 | def heartbeat(): 189 | """Check if the website is running.""" 190 | return 'The website is up' 191 | 192 | @app.route('/') 193 | def redirect_to_long_url(keyword): 194 | """Redirect to the long URL associated with the given keyword.""" 195 | url_data = get_long_url_by_keyword(keyword) 196 | 197 | if not url_data: 198 | return render_template('errorpage.html', 199 | title="Oops! Page Not Found", 200 | message="404 - The URL you are trying to reach doesn't exist!") 201 | 202 | utc = pytz.utc 203 | current_time = datetime.now(utc) # Get current time in UTC 204 | 205 | expiration_time = url_data.get('expiration') # Use .get() to avoid KeyError 206 | if expiration_time is None: 207 | update_click_count(keyword) 208 | return redirect(url_data['url'], code=302) 209 | 210 | # If expiration_time is present, perform the comparison 211 | unix_current_time = current_time.timestamp() 212 | unix_expiration_time = expiration_time.timestamp() 213 | 214 | if unix_current_time > unix_expiration_time: 215 | return render_template('errorpage.html', 216 | title="Oops! URL has expired", 217 | message="404 - The URL you are trying to reach has expired!") 218 | 219 | update_click_count(keyword) 220 | return redirect(url_data['url'], code=302) 221 | 222 | if __name__ == '__main__': 223 | app.run(host='0.0.0.0', port=5000) 224 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing to Project OSUS 3 | 4 | Thank you for showing interest in contributing to **Project OSUS**! We appreciate all contributions, and your help is what makes this project grow. 🎉 5 | 6 | This guide will help you understand how to get involved and contribute in different ways. The project is beginner-friendly, and we encourage everyone to participate, whether it's through code or non-technical contributions. 7 | 8 | 9 | ## Table of Contents 10 | 11 | - [Your First Contribution](#your-first-contribution) 12 | - [Suggesting Enhancements](#suggesting-enhancements) 13 | - [Reporting Bugs](#reporting-bugs) 14 | - [Improving Documentation and Non-Tech Contributions](#improving-documentation-and-non-tech-contributions) 15 | - [Styleguides](#styleguides) 16 | 17 | ## Your First Contribution 18 | 19 | We’re excited to have you contribute! Whether you want to fix bugs, add features, optimize code, or suggest improvements, your contribution is valuable. Here’s how to get started: 20 | 21 | - Pick any existing issue from our [Issues page](https://github.com/harshithtunuguntla/project-osus/issues). Feel free to comment on the issue to let others know you’re working on it. 22 | - If you find something that can be improved (code quality, performance, etc.), you’re free to improve it. 23 | - Want to add a feature? Suggest it and implement it! Your ideas are welcome. 24 | - Don’t be shy if you’re a beginner — even the smallest improvements are helpful. 25 | 26 | You can also improve existing code by submitting optimizations or refactoring parts of the project. We appreciate any contribution, no matter how small! 27 | 28 | ## Suggesting Enhancements 29 | 30 | Have an idea to make **Project OSUS** better? Enhancements and new features are a great way to contribute. 31 | 32 | - Browse through the existing [enhancements](https://github.com/harshithtunuguntla/project-osus/issues?q=label%3Aenhancement) to see if someone has already suggested a similar feature. 33 | - If not, open a new [issue](https://github.com/harshithtunuguntla/project-osus/issues/new) with a clear and descriptive title. 34 | - Explain your idea, the current behavior, and what improvement or feature you'd like to see. 35 | - If possible, include a step-by-step explanation, and feel free to include any examples or code snippets. 36 | 37 | Your ideas might inspire new features that benefit many users! 38 | 39 | ## Reporting Bugs 40 | 41 | If you encounter any bugs or issues while working with **Project OSUS**, please report them to help us improve the project. Here’s how: 42 | 43 | - Make sure you’re using the latest version of the project. 44 | - Look through [existing issues](https://github.com/harshithtunuguntla/project-osus/issues?q=label%3Abug) to see if the bug has already been reported. 45 | - If the bug hasn’t been reported, open a new [issue](https://github.com/harshithtunuguntla/project-osus/issues/new). 46 | - Include as much detail as possible — platform information (e.g., OS, versions), how to reproduce the issue, and any logs or error messages that help. 47 | 48 | Please provide clear steps to reproduce the issue. This makes it easier for the maintainers to address and fix it! 49 | 50 | ## Improving Documentation and Non-Tech Contributions 51 | 52 | Not a developer? No worries — you can still contribute in many other ways: 53 | 54 | - Improving the documentation: If you spot any missing or outdated info in our documentation, feel free to update it. 55 | - Enhancing the README: The README is a key part of helping others understand the project, so your contributions here are valuable. 56 | - Suggesting edits or writing for clarity: Help make our guides clearer for others by improving explanations and instructions. 57 | 58 | If you find any area in need of improvement, feel free to submit a pull request. 59 | 60 | ## Styleguides 61 | 62 | - Follow the guidelines for commit messages. Make them clear and descriptive. 63 | - Ensure your code is clean and readable. Simplicity is key. 64 | - Use proper formatting for documentation updates. 65 | 66 | ## Thank You 67 | 68 | Your contributions make **Project OSUS** better for everyone! We value and appreciate your efforts, whether you're contributing through code, ideas, or other forms of participation. 69 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Project OSUS - Open Source URL Shortener 🎉 2 | 3 | Welcome to **Project OSUS**, an open-source URL shortener initiative built with **Flask**, **Python**, and **MongoDB**! This project is part of **Hacktoberfest 2024**, and we're inviting contributions from everyone, whether you're a techie or not. 4 | 5 | The goal of this project is to create a simple and efficient URL shortener while fostering learning and collaboration. Contributions can be technical (code) or non-technical (documentation, design), and we encourage everyone to get involved! 6 | 7 | --- 8 | 9 | ## Table of Contents 10 | - [What is HacktoberFest](#hacktober-fest) 11 | - [About the Project](#about-the-project) 12 | - [Tech Stack](#tech-stack) 13 | - [Live Demo](#live-demo) 14 | - [Getting Started](#getting-started) 15 | - [Prerequisites](#prerequisites) 16 | - [Installation](#installation) 17 | - [Environment Variables](#environment-variables) 18 | - [Running the Project](#running-the-project) 19 | - [Contributing](#contributing) 20 | - [How to Contribute](#how-to-contribute) 21 | - [Tech Contributions](#tech-contributions) 22 | - [Non-Tech Contributions](#non-tech-contributions) 23 | - [License](#license) 24 | - [Contact](#contact) 25 | 26 | 27 | ## What is HacktoberFest 28 | 29 | **Hacktoberfest** is an annual event celebrated in October, organized by DigitalOcean, GitHub, and other partners, to encourage open-source contributions. During this month, developers of all skill levels are invited to contribute to open-source projects, celebrate their achievements, and connect with the community. 30 | 31 | You get to enhance your coding and collaboration skills, connect with like-minded developers and industry professionals and get some recognition and prizes too :) 32 | 33 | **Important Note:** Make sure you've registered in [Hacktoberfest website](https://hacktoberfest.com/) with your Github account. This will track your contributions for the event and ensure you receive your rewards. 34 | 35 | --- 36 | 37 | ## About the Project 38 | **Project OSUS** is a URL Shortener that allows users to generate shortened URLs that redirect to longer URLs. The project is built with Flask (Python) on the backend and MongoDB for data storage. 39 | 40 | Whether you’re a developer, writer, designer, or tester, there are various ways to contribute and learn through this open-source project. 41 | 42 | ### How it Works: 43 | 1. **Long URL**: The user inputs a long URL, for example: 44 | `https://www.reddit.com/r/ProgrammerHumor/comments/m7xi4j/should_i_contribute_or_work_on_kaggle/` 45 | 46 | 2. **Short URL**: The system generates a short URL, for example: 47 | `https://project-osus.vercel.app/meme` [click here](https://project-osus.vercel.app/meme) 48 | 49 | When users visit the short URL, they are automatically redirected to the original long URL. This makes it easy to share and manage links efficiently. 50 | 51 | --- 52 | 53 | ## Tech Stack 54 | - **Backend:** Flask (Python) 55 | - **Database:** MongoDB 56 | - **Frontend:** HTML, CSS, JS (with flexibility to improve) 57 | - **Version Control:** Git/GitHub 58 | 59 | --- 60 | 61 | ## Live Demo 62 | 63 | 🚀 All the changes you contribute will go live on our official website: 64 | **https://project-osus.vercel.app/** 65 | 66 | This means that every improvement you make, whether it's fixing bugs, adding features, or refining the user interface, will be reflected in the live application for everyone to use. 67 | 68 | Your contributions will help make **Project OSUS** better for the community, and you'll get the satisfaction of seeing your work in action! 69 | 70 | Ready to make an impact? Start contributing today and watch your work go live! 🌟 71 | 72 | --- 73 | 74 | ## Getting Started 75 | 76 | Here’s how to get the project running on your local machine. 77 | 78 | ### Prerequisites 79 | Make sure you have the following installed on your system: 80 | - **Python** 81 | - **MongoDB** (Local installation or Cloud version) 82 | - **Git** (for version control) 83 | 84 | ### Installation 85 | 86 | 1. **Fork the Repository**: First, fork the repository to create a personal copy in your GitHub account. 87 | 88 | 2. **Clone the Repository**: Clone your forked repository to your local machine using the command: 89 | 90 | ```bash 91 | git clone https://github.com/your-username/repo-name.git 92 | ``` 93 | 94 | 2. **Create a virtual environment** (optional but recommended): 95 | ```bash 96 | python -m venv venv 97 | source venv/bin/activate # Linux/macOS 98 | venv\Scripts\activate # Windows 99 | ``` 100 | 101 | 3. **Install dependencies**: 102 | ```bash 103 | pip install -r requirements.txt 104 | ``` 105 | 106 | 4. **Create a `.env` file**: In the root directory of your project, create a file named `.env` and add the following environment variables to the file: 107 | 108 | ```plaintext 109 | MONGO_PATH='localhost' 110 | MONGO_PORT=27017 111 | 112 | **Note:** If you are using a cloud version of MongoDB, make sure to change MONGO_PATH to your cloud connection string. The connection string format typically looks like this: mongodb+srv://myUser:myPassword@ cluster0.mongodb.net/ 113 | 114 | ### Running the Project 115 | 116 | 1. **Start MongoDB** (if running locally): 117 | ```bash 118 | mongod 119 | ``` 120 | 121 | 2. **Run the Flask development server**: 122 | ```bash 123 | flask run 124 | ``` 125 | 126 | 3. The project will be running at `http://localhost:5000`. Visit that in your browser to start using the URL shortener! 127 | 128 | --- 129 | 130 | ## Contributing 131 | 132 | We encourage contributions from everyone! Whether you're looking to contribute code, documentation, design, or testing, here's how you can get involved. 133 | 134 | ### How to Contribute 135 | 136 | 1. **Fork the repository**: 137 | - Go to the main repository page and click on the "Fork" button in the top right corner. 138 | 139 | 2. **Raise an issue**: 140 | - Before starting to work on a new feature or bug fix, check if there’s an existing issue related to it. If not, [open a new issue](https://github.com/harshithtunuguntla/project-osus/issues). 141 | - If you're working on an existing issue, leave a comment on that issue thread saying you're working on it to avoid duplicating efforts. 142 | 143 | 3. **Create a new branch**: 144 | - Create a branch for your contribution. Use a descriptive name for your branch, such as `fix-issue-#number` or `add-new-feature`: 145 | ```bash 146 | git checkout -b branch-name 147 | ``` 148 | 149 | 4. **Make your changes**: 150 | - Make sure your code follows the project structure and **use autopep8 for code formatting** to ensure consistency: 151 | 152 | 153 | 5. **Commit your changes**: 154 | - Write a meaningful commit message describing your changes: 155 | ```bash 156 | git add . 157 | git commit -m "Description of your changes" 158 | ``` 159 | 160 | 6. **Push to your forked repository**: 161 | ```bash 162 | git push origin branch-name 163 | 164 | 7. **Create a pull request (PR)**: 165 | 166 | Once your changes are pushed, come back to this repository on GitHub. You should see a prompt to create a pull request (PR). Click on it and fill in the details about your changes, explaining why you made them. 167 | 168 | **And that's it! You have successfully made your contribution. The maintainers will review your PR and provide feedback or merge it if everything looks good.** 169 | 170 | --- 171 | 172 | ### Tech Contributions 173 | 174 | - **Bug Fixes**: Find and fix bugs to improve the project. 175 | - **Feature Enhancements**: Add new features, such as custom URLs, user authentication, or analytics. 176 | - **Code Quality**: Refactor and improve code quality or performance. 177 | - **UI/UX**: Help improve the front-end design or overall user experience. 178 | 179 | --- 180 | 181 | ### Non-Tech Contributions 182 | 183 | - **Documentation**: Improve this README or create new guides for contributors. 184 | - **Testing**: Write test cases or help test the project for bugs. 185 | - **Design**: Create graphics, logos, or improve the UI design. 186 | 187 | --- 188 | 189 | ### License 190 | 191 | This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more details. 192 | 193 | --- 194 | 195 | ### Contact 196 | 197 | Got questions, or need help or you want to help maintainers?? Feel free to reach out via issues or via: 198 | 199 | Whatsapp Instagram 200 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | blinker==1.8.2 2 | certifi==2024.8.30 3 | charset-normalizer==3.3.2 4 | click==8.1.7 5 | colorama==0.4.6 6 | dnspython==2.7.0 7 | Flask==3.0.3 8 | idna==3.10 9 | itsdangerous==2.2.0 10 | Jinja2==3.1.4 11 | MarkupSafe==2.1.5 12 | pymongo==4.10.1 13 | python-dotenv==1.0.1 14 | requests==2.32.3 15 | urllib3==2.2.3 16 | vercel==0.2.1 17 | Werkzeug==3.0.4 18 | pytz==2024.2 -------------------------------------------------------------------------------- /static/analytics.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Lato', sans-serif; 3 | background: url('https://img.pikbest.com/wp/202347/purple-background-image-rendering-of-a-3d-illustrated-with-black-and-shapes_9770212.jpg!bw700') no-repeat center center fixed; 4 | background-size: cover; 5 | color: #fff; 6 | display: flex; 7 | flex-direction: column; 8 | min-height: 100vh; 9 | } 10 | .btn { 11 | margin-top: 10px; /* Adds a 5px margin on all sides */ 12 | display: flex; /* Allows centering */ 13 | align-items: center; /* Vertically centers content */ 14 | justify-content: center; /* Horizontally centers content */ 15 | } 16 | 17 | .small-icon { 18 | font-size: 0.75em; /* Reduce icon size */ 19 | margin-left: 8px; /* Add space between text and icon */ 20 | } 21 | .overlay { 22 | position: absolute; 23 | top: 0; 24 | left: 0; 25 | right: 0; 26 | bottom: 0; 27 | background: rgba(0, 0, 0, 0.6); 28 | } 29 | 30 | .header, 31 | .footer { 32 | display: flex; 33 | justify-content: space-between; 34 | align-items: center; 35 | padding: 15px; 36 | z-index: 10; 37 | } 38 | 39 | .header { 40 | background-color: #000; 41 | font-family: 'Poppins', sans-serif; 42 | } 43 | 44 | .header .brand { 45 | font-size: 28px; 46 | font-weight: 700; 47 | letter-spacing: 1px; 48 | color: #9B59B6; 49 | } 50 | 51 | .header .btn { 52 | border-radius: 30px; 53 | padding: 10px 20px; 54 | margin-left: 10px; 55 | transition: all 0.3s ease-in-out; 56 | /* background: linear-gradient(to right, #8e44ad, #9b59b6); */ 57 | color: #fff; 58 | } 59 | 60 | .header .btn:hover { 61 | color: #8644ad; 62 | transform: translateY(-2px); /* Raise the button slightly */ 63 | } 64 | /* Add a pseudo-element for the white line */ 65 | .header .btn::after { 66 | content: ''; 67 | position: absolute; 68 | left: 0; 69 | right: 0; 70 | bottom: 0px; /* Adjust to position the line */ 71 | height: 2px; /* Height of the line */ 72 | background-color: white; /* Color of the line */ 73 | opacity: 0; /* Initially invisible */ 74 | transition: opacity 0.3s ease; /* Smooth transition for the line */ 75 | } 76 | .header .btn:hover::after { 77 | opacity: 1; /* Show the line on hover */ 78 | } 79 | .header ul { 80 | display: flex; 81 | list-style: none; 82 | 83 | } 84 | 85 | 86 | 87 | .main-content { 88 | display: flex; 89 | flex-direction: column; 90 | align-items: center; 91 | justify-content: center; 92 | flex-grow: 1; 93 | position: relative; 94 | z-index: 10; 95 | font-family: "Poppins", "Sans-serif"; 96 | 97 | } 98 | 99 | .url-box { 100 | background: rgba(255, 255, 255, 0.1); /* Semi-transparent white for glass effect */ 101 | background: linear-gradient(145deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.2)); /* Gradient effect */ 102 | padding: 30px; 103 | border-radius: 8px; 104 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); 105 | width: 100%; 106 | max-width: 500px; 107 | opacity: 0; /* Initial opacity for animation */ 108 | transform: scale(0.8) rotate(3deg); /* Initial transform for animation */ 109 | transition: opacity 0.5s ease, transform 0.5s ease; 110 | animation: fadeInScale 0.5s forwards; 111 | font-weight: 100 ; 112 | 113 | /* Glassmorphism effect */ 114 | backdrop-filter: blur(20px); /* Frosted glass effect */ 115 | border: 1px solid rgba(255, 255, 255, 0.2); /* Subtle border */ 116 | } 117 | 118 | @keyframes fadeInScale { 119 | 0% { 120 | opacity: 0; 121 | transform: scale(0.8) rotate(3deg); 122 | } 123 | 124 | 50% { 125 | opacity: 0.5; 126 | transform: scale(1.05) rotate(-3deg); 127 | } 128 | 129 | 100% { 130 | opacity: 1; 131 | transform: scale(1) rotate(0deg); 132 | } 133 | } 134 | 135 | .url-box:hover { 136 | transform: scale(1.03); 137 | transition: transform 0.3s ease; 138 | } 139 | 140 | .url-box .btn { 141 | background-color: #9B59B6; 142 | color: #fff; 143 | font-weight: bold; 144 | border-radius: 30px; 145 | padding: 10px 20px; 146 | width: 100%; 147 | transition: background-color 0.3s ease-in-out; 148 | } 149 | 150 | .url-box .btn:hover { 151 | background-color: #8E44AD; 152 | } 153 | 154 | .footer { 155 | background-color: #000; 156 | color: #fff; 157 | text-align: center; 158 | padding: 40px; 159 | flex-shrink: 0; 160 | flex-direction: column; 161 | } 162 | 163 | .footer a { 164 | color: #9B59B6; 165 | text-decoration: none; 166 | font-weight: bold; 167 | } 168 | 169 | .footer .heart { 170 | color: #8E44AD; 171 | } 172 | 173 | .footer a:hover { 174 | color: #ffffff; 175 | } 176 | 177 | .footer .heart:hover { 178 | color: #c39bd3; 179 | transform: scale(1.1); 180 | } 181 | 182 | .footer-details { 183 | margin-top: 20px; 184 | text-align: center; 185 | } 186 | 187 | .copy-icon { 188 | cursor: pointer; 189 | color: #9B59B6; 190 | margin-left: 10px; 191 | transition: color 0.3s; 192 | font-size: 1.2em; 193 | } 194 | 195 | .url-highlight { 196 | background-color: #9B59B6; 197 | color: white; 198 | padding: 2px 4px; 199 | border-radius: 5px; 200 | transition: background-color 0.3s; 201 | } 202 | .short-url{ 203 | word-wrap: break-word; 204 | overflow-wrap: break-word; 205 | } 206 | 207 | .error-message { 208 | color: red; 209 | font-weight: bold; 210 | } 211 | 212 | #keywordLengthIndicator { 213 | float: right; 214 | } 215 | 216 | .checkbtn { 217 | font-size: 30px; 218 | color: white; 219 | float: right; 220 | line-height: 80px; 221 | margin-right: 40px; 222 | cursor: pointer; 223 | display: none; 224 | } 225 | #check { 226 | display: none; 227 | } 228 | 229 | 230 | /***** MEDIA QUERY *****/ 231 | 232 | @media(max-width : 600px) { 233 | .main-content { 234 | height: calc(100vh - 30vh) !important; 235 | width: 100% !important; 236 | z-index: 0; 237 | } 238 | 239 | .header { 240 | padding: 5px 10px; 241 | z-index: 10; 242 | height: 13vh; 243 | width: 100%; 244 | } 245 | 246 | .header ul { 247 | display: flex; 248 | flex-direction: column; 249 | position: fixed; 250 | width: 100%; 251 | height: 100vh; 252 | background-color: black; 253 | top: 80px; 254 | right: -100%; 255 | text-align: center; 256 | transition: all .5s; 257 | gap : 6px; 258 | padding-top: 20px; 259 | padding-right: 0; 260 | padding-left: 0; 261 | } 262 | .header ul li { 263 | margin: 5px 0px; 264 | } 265 | 266 | .header .brand span { 267 | display: none; 268 | } 269 | 270 | .checkbtn { 271 | display: block; 272 | margin-top: -5px; 273 | margin-right: 20px; 274 | } 275 | #check:checked ~ ul { 276 | right: 0; 277 | } 278 | 279 | .url-box { 280 | width: 90%; 281 | } 282 | 283 | 284 | .footer { 285 | padding-top: 10px; 286 | z-index: 10; 287 | height: 17vh; 288 | justify-content: space-around; 289 | } 290 | .footer-details { 291 | margin: 0; 292 | } 293 | 294 | 295 | } 296 | 297 | @media (max-width: 768px) { 298 | .header .brand{ 299 | font-size: 20px; 300 | } 301 | .header ul li a { 302 | font-size: 14px; 303 | } 304 | 305 | 306 | .footer-details { 307 | margin: 8; 308 | } 309 | .footer-details p { 310 | margin-bottom: 0; 311 | } 312 | } -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harshithtunuguntla/project-osus/f4d20c437aba542add4e8f112650705c51547f12/static/images/logo.png -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Lato", sans-serif; 3 | background: url("https://img.pikbest.com/wp/202347/purple-background-image-rendering-of-a-3d-illustrated-with-black-and-shapes_9770212.jpg!bw700") 4 | no-repeat center center fixed; 5 | background-size: cover; 6 | color: #fff; 7 | display: flex; 8 | flex-direction: column; 9 | min-height: 100vh; 10 | overflow: hidden; 11 | } 12 | 13 | .overlay { 14 | position: absolute; 15 | top: 0; 16 | left: 0; 17 | right: 0; 18 | bottom: 0; 19 | background: rgba(0, 0, 0, 0.6); 20 | } 21 | 22 | .header, 23 | .footer { 24 | display: flex; 25 | justify-content: space-between; 26 | align-items: center; 27 | padding: 8px; 28 | z-index: 10; 29 | } 30 | 31 | .header { 32 | background-color: #000; 33 | font-family: "Poppins", sans-serif; 34 | display: flex; 35 | } 36 | 37 | .header .brand { 38 | font-size: 28px; 39 | font-weight: 700; 40 | letter-spacing: 1px; 41 | color: #9b59b6; 42 | padding-left: 10px; 43 | } 44 | .header ul { 45 | display: flex; 46 | list-style: none; 47 | } 48 | 49 | .btn { 50 | margin-top: 10px; /* Adds a 5px margin on all sides */ 51 | display: flex; /* Allows centering */ 52 | align-items: center; /* Vertically centers content */ 53 | justify-content: center; /* Horizontally centers content */ 54 | } 55 | 56 | .small-icon { 57 | font-size: 0.75em; /* Reduce icon size */ 58 | margin-left: 8px; /* Add space between text and icon */ 59 | } 60 | 61 | .header .btn { 62 | border-radius: 30px; 63 | padding: 10px 20px; 64 | margin-left: 10px; 65 | transition: all 0.3s ease-in-out; 66 | /* background: linear-gradient(to right, #8e44ad, #9b59b6); */ 67 | color: #fff; 68 | } 69 | 70 | .header .btn { 71 | 72 | position: relative; /* Required for absolute positioning of the pseudo-element */ 73 | transition: transform 0.3s ease; /* Transition for smooth effect */ 74 | } 75 | 76 | .header .btn:hover { 77 | color: #8644ad; 78 | transform: translateY(-2px); /* Raise the button slightly */ 79 | } 80 | 81 | /* Add a pseudo-element for the white line */ 82 | .header .btn::after { 83 | content: ''; 84 | position: absolute; 85 | left: 0; 86 | right: 0; 87 | bottom: 0px; /* Adjust to position the line */ 88 | height: 2px; /* Height of the line */ 89 | background-color: white; /* Color of the line */ 90 | opacity: 0; /* Initially invisible */ 91 | transition: opacity 0.3s ease; /* Smooth transition for the line */ 92 | } 93 | 94 | .header .btn:hover::after { 95 | opacity: 5; /* Show the line on hover */ 96 | } 97 | 98 | 99 | .main-content { 100 | display: flex; 101 | flex-direction: column; 102 | align-items: center; 103 | justify-content: center; 104 | flex-grow: 1; 105 | position: relative; 106 | z-index: 10; 107 | font-family: "Poppins", "Sans-serif"; 108 | } 109 | 110 | .url-box { 111 | background: rgba(255, 255, 255, 0.1); /* Semi-transparent white for glass effect */ 112 | background: linear-gradient(145deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.2)); /* Gradient effect */ 113 | padding: 30px; 114 | border-radius: 8px; 115 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); 116 | width: 100%; 117 | max-width: 500px; 118 | opacity: 0; /* Initial opacity for animation */ 119 | transform: scale(0.8) rotate(3deg); /* Initial transform for animation */ 120 | transition: opacity 0.5s ease, transform 0.5s ease; 121 | animation: fadeInScale 0.5s forwards; 122 | font-weight: 200 ; 123 | 124 | /* Glassmorphism effect */ 125 | backdrop-filter: blur(20px); /* Frosted glass effect */ 126 | border: 1px solid rgba(255, 255, 255, 0.2); /* Subtle border */ 127 | } 128 | 129 | .form-label{ 130 | padding-bottom: 2px; 131 | } 132 | 133 | .formhead{ 134 | padding-bottom: 10px; 135 | } 136 | 137 | .form-control{ 138 | padding-bottom: 10px; 139 | border-radius: 5px; 140 | } 141 | 142 | /* Optional: Keyframes for the fadeInScale animation */ 143 | 144 | @keyframes fadeInScale { 145 | 0% { 146 | opacity: 0; 147 | transform: scale(0.8) rotate(3deg); 148 | } 149 | 150 | 50% { 151 | opacity: 0.5; 152 | transform: scale(1.05) rotate(-3deg); 153 | } 154 | 155 | 100% { 156 | opacity: 1; 157 | transform: scale(1) rotate(0deg); 158 | } 159 | } 160 | 161 | .url-box:hover { 162 | transform: scale(1.03); 163 | transition: transform 0.3s ease; 164 | } 165 | 166 | .url-box .btn { 167 | background-color: #9b59b6; 168 | color: #fff; 169 | font-weight: medium; 170 | border-radius: 2px; 171 | padding: 10px 20px; 172 | width: 100%; 173 | transition: background-color 0.3s ease-in-out; 174 | } 175 | 176 | .url-box .btn:hover { 177 | background-color: #8e44ad; 178 | } 179 | 180 | .footer { 181 | background-color: #000; 182 | color: #fff; 183 | text-align: center; 184 | padding: 30px; 185 | flex-shrink: 0; 186 | flex-direction: column; 187 | } 188 | 189 | .footer a:hover { 190 | color: #ffffff; 191 | } 192 | 193 | .footer .heart:hover { 194 | color: #c39bd3; 195 | transform: scale(1.1); 196 | } 197 | 198 | .footer a { 199 | color: #9b59b6; 200 | text-decoration: none; 201 | font-weight: bold; 202 | } 203 | 204 | .footer .heart { 205 | color: #8e44ad; 206 | } 207 | 208 | .footer-details { 209 | margin-top: 20px; 210 | text-align: center; 211 | } 212 | 213 | .copy-icon { 214 | cursor: pointer; 215 | color: #9b59b6; 216 | margin-left: 10px; 217 | transition: color 0.3s; 218 | font-size: 1.2em; 219 | } 220 | 221 | .url-highlight { 222 | background-color: #9b59b6; 223 | color: white; 224 | padding: 2px 4px; 225 | border-radius: 5px; 226 | transition: background-color 0.3s; 227 | } 228 | .short-url { 229 | word-wrap: break-word; 230 | overflow-wrap: break-word; 231 | } 232 | 233 | .error-message { 234 | color: red; 235 | font-weight: bold; 236 | } 237 | 238 | #keywordLengthIndicator { 239 | float: right; 240 | } 241 | 242 | .checkbtn { 243 | font-size: 30px; 244 | color: white; 245 | float: right; 246 | line-height: 80px; 247 | margin-right: 40px; 248 | cursor: pointer; 249 | display: none; 250 | } 251 | #check { 252 | display: none; 253 | } 254 | 255 | /***** MEDIA QUERY *****/ 256 | 257 | @media (max-width: 600px) { 258 | .main-content { 259 | height: calc(100vh - 30vh); 260 | width: 100% !important; 261 | z-index: 0; 262 | } 263 | 264 | .header { 265 | padding: 5px 10px; 266 | z-index: 10; 267 | height: 13vh; 268 | width: 100%; 269 | } 270 | 271 | .header ul { 272 | display: flex; 273 | flex-direction: column; 274 | position: fixed; 275 | width: 100%; 276 | height: 100vh; 277 | background-color: black; 278 | top: 80px; 279 | right: -100%; 280 | text-align: center; 281 | transition: all 0.5s; 282 | gap: 6px; 283 | padding-top: 20px; 284 | padding-right: 0; 285 | padding-left: 0; 286 | } 287 | .header ul li { 288 | margin: 5px 0px; 289 | } 290 | 291 | .header .brand span { 292 | display: none; 293 | } 294 | 295 | .checkbtn { 296 | display: block; 297 | margin-top: -5px; 298 | margin-right: 20px; 299 | } 300 | #check:checked ~ ul { 301 | right: 0; 302 | } 303 | 304 | .url-box { 305 | width: 90%; 306 | } 307 | 308 | .footer { 309 | padding-top: 10px; 310 | z-index: 10; 311 | height: 17vh; 312 | justify-content: space-around; 313 | } 314 | .footer-details { 315 | margin: 0; 316 | } 317 | } 318 | 319 | @media (max-width: 768px) { 320 | .header .brand { 321 | font-size: 20px; 322 | } 323 | 324 | .header ul li a { 325 | font-size: 14px; 326 | } 327 | 328 | .footer-details { 329 | margin: 8; 330 | } 331 | .footer-details p { 332 | margin-bottom: 0; 333 | } 334 | } 335 | 336 | /***** End MEDIA QUERY *****/ 337 | -------------------------------------------------------------------------------- /templates/404_page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 - Page Not Found 7 | 36 | 37 | 38 |

404 - Page Not Found

39 |

The link you are looking for with the keyword doesn't exist.

40 |

Would you like to create a new short URL for this keyword?

41 |
42 | 43 | 44 | 45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /templates/analytics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Project OSUS | Analytics 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 |
23 |
24 | OSUS logo 25 | O S U S 26 |
27 | 28 | 29 | 32 |
49 |
50 | 51 | 52 |
53 |
54 |

Track your URL

55 |
56 |
57 | 58 | 59 | 60 |

61 |
62 | 63 |
64 |
65 |
66 |
67 |
68 | 69 | 70 | 75 | 76 | 77 | 85 | 86 | 248 | 249 | 250 | 251 | 252 | -------------------------------------------------------------------------------- /templates/api.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Project OSUS | API Docs 7 | 11 | 16 | 285 | 286 | 287 | 288 |
289 |

Project OSUS - Open Source URL Shortener

290 | Use Now 291 |
292 | 293 | 294 |
295 | 296 | 315 | 316 | 317 |
318 | 319 |
320 |

Welcome, Developers.

321 |

322 | Welcome to Project OSUS, an open-source URL 323 | shortener built using Flask, Python, and MongoDB. This project is 324 | part of Hacktoberfest 2024, encouraging contributions from 325 | developers and non-developers alike. The goal is to provide a 326 | simple, easy-to-use URL shortening service while fostering 327 | collaboration in the open-source community. 328 |

329 |
330 | 331 | 332 |
333 |

Postman Collection

334 |

To get started with Project OSUS, use the following Postman collection to test and understand the API:

335 | 344 |

Note: Forking in Postman creates a new instance of an element that you can modify without changing the original. You can fork collections, environments, and flows to make contributions even without Editor access.

345 |
346 | 347 |
348 | 349 |

Endpoints

350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 |
EndpointDescription
/shorten
Shortens a long URL by providing a custom keyword, expiration date, and time.
/analytics
Retrieves analytics data for shortened URLs, including click statistics and referrer information.
364 | 365 | 366 |
367 | 1) /shorten Endpoint 368 |

This endpoint allows you to shorten a long URL by providing a custom keyword, expiration date, and time.

369 | 370 | 371 |

Endpoint Details

372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 |
Endpoint
/shorten
Method
POST
URL
https://project-osus.vercel.app/shorten
386 | 387 | 388 |

Request Body

389 |
    390 |
  • longUrl (string): The long URL to be shortened.
  • 391 |
  • keyword (string, optional): Custom keyword for the short URL.
  • 392 |
  • expirationDate (string, optional): Date for the short URL expiration.
  • 393 |
  • expirationTime (string, optional): Time for the short URL expiration.
  • 394 |
  • expirationPeriod (string, optional): Period (AM/PM) for the expiration time.
  • 395 |
  • timezone (string, optional): Timezone for the expiration date and time.
  • 396 |
397 | 398 | 399 |

Sample Request

400 |
401 |     {
402 |        "longUrl": "https://www.google.com/",
403 |        "keyword": "your-key-word",
404 |        "expirationDate": "YYYY-MM-DD",
405 |        "expirationTime": "HH:MM",
406 |        "expirationPeriod": "PM",
407 |        "timezone": "UTC"
408 |     }
409 |     
410 | 411 | 412 |

Response

413 |

Status Code: 200

414 |

JSON:

415 |
416 |     {
417 |        "shortUrl": "https://project-osus.vercel.app/your-key-word"
418 |     }
419 |     
420 | 421 |

Status Code: 400

422 |

If the keyword already exists:

423 | 424 |

JSON:

425 |
426 | {
427 |     "error": "The keyword already exists. Please choose a different one."
428 | }
429 | 
430 |
431 |
432 | 2) /analytics Endpoint 433 |

This endpoint allows you to submit analytics data.

434 | 435 | 436 |

Endpoint Details

437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 |
Endpoint
/analytics
Method
POST
URL
https://project-osus.vercel.app/analytics
451 | 452 | 453 |

Request Body

454 |
    455 |
  • keyword (text, required): The keyword for which the analytics data is being submitted.
  • 456 |
457 | 458 | 459 |

Example Request

460 |
461 |     {
462 |         "keyword": "LarryPage"
463 |     }
464 |     
465 | 466 | 467 |

Response

468 |

Status Code: 200

469 |

If the keyword is present:

470 | 471 |

JSON:

472 |
473 |     {
474 |         "clicks": 0,
475 |         "expiration": "2050-07-05T23:59:00"
476 |     }
477 |     
478 | 479 | 480 |

Status Code: 404

481 |

If the keyword is not present:

482 |
483 |     {
484 |         "error": "Keyword not found."
485 |     }
486 |     
487 |
488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 |
496 | 497 | 498 |
499 | 500 | 501 | 512 | 513 | 522 | 523 | -------------------------------------------------------------------------------- /templates/documentation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Project OSUS | Docs 7 | 11 | 16 | 218 | 219 | 220 | 221 |
222 |

Project OSUS - Open Source URL Shortener

223 | Use Now 224 |
225 | 226 | 227 |
228 | 229 | 243 | 244 | 245 |
246 | 247 |
248 |

Overview

249 |

250 | Welcome to Project OSUS, an open-source URL 251 | shortener built using Flask, Python, and MongoDB. This project is 252 | part of Hacktoberfest 2024, encouraging contributions from 253 | developers and non-developers alike. The goal is to provide a 254 | simple, easy-to-use URL shortening service while fostering 255 | collaboration in the open-source community. 256 |

257 |
258 | 259 | 260 |
261 |

Installation

262 |

To get started with Project OSUS, follow these steps:

263 |
    264 |
  1. Fork the repository on GitHub.
  2. 265 |
  3. Clone the repository using the command:
  4. 266 |
    git clone https://github.com/your-username/project-osus.git
    267 |
  5. Set up a virtual environment (optional but recommended):
  6. 268 |
    python -m venv venv
    269 |
  7. Activate the virtual environment:
  8. 270 |
    source venv/bin/activate
    271 |
  9. Install the required dependencies:
  10. 272 |
    pip install -r requirements.txt
    273 |
  11. Set up your MongoDB connection string in a `.env` file:
  12. 274 |
    MONGO_PATH='localhost'
    MONGO_PORT=27017
    275 |
  13. Run the application:
  14. 276 |
    flask run
    277 |
  15. Access the application at:
  16. 278 |
    http://localhost:5000
    279 |
280 |
281 | 282 | 283 |
284 |

Features

285 |
    286 |
  • Shorten long URLs with ease.
  • 287 |
  • Automatic redirection to the original URL.
  • 288 |
  • Simple and intuitive user interface.
  • 289 |
290 |
291 | 292 | 293 |
294 |

Contributing

295 |

We welcome contributions from developers of all skill levels! Whether you're fixing bugs, adding new features, or improving documentation, all help is appreciated. To contribute:

296 |
    297 |
  1. Fork the repository on GitHub.
  2. 298 |
  3. Clone your forked repository:
  4. 299 |
    git clone https://github.com/your-username/project-osus.git
    300 |
  5. Create a new branch for your changes:
  6. 301 |
    git checkout -b feature-branch
    302 |
  7. Make your changes and commit them:
  8. 303 |
    git add .
    git commit -m "Description of your changes"
    304 |
  9. Push your changes:
  10. 305 |
    git push origin feature-branch
    306 |
  11. Create a pull request on GitHub with a description of your changes.
  12. 307 |
308 |
309 | 310 | 311 |
312 |

Frequently Asked Questions (FAQ)

313 |
314 |

1. How do I report a bug?

315 |

To report a bug, open an issue in the GitHub repository, including details and steps to reproduce the problem.

316 |
317 |
318 |

2. Can I suggest new features?

319 |

Absolutely! Open an issue on GitHub to discuss new feature ideas. We're always open to suggestions from the community.

320 |
321 |
322 | 323 | 324 |
325 |

Contact Us

326 |

If you have any questions or need further assistance, feel free to reach out to us via the following platforms:

327 | 330 |
331 | 332 |
333 | 334 | 335 |
336 | 337 | 338 | 347 | 348 | 349 | 350 | -------------------------------------------------------------------------------- /templates/errorpage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 404 - Page Not Found 12 | 98 | 99 | 100 | 101 |
102 |

{{ title }}

103 |

{{ message }}

104 | Create a New Short URL 105 |
106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | URL Shortener 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 |
23 |
24 | OSUS logo 25 | O S U S 26 |
27 | 28 | 29 | 32 | 53 |
54 | 55 | 56 |
57 |
58 |

Shorten Your URL

59 |
60 |
61 | 62 | 63 |
64 |
65 | 66 | 67 | 68 |

69 |
70 |
71 |
72 |
Set Link Expiration (Optional)
73 |
74 |
75 |
76 |
77 | 78 | 79 |
80 |
81 | 82 | 83 |
84 |
85 | 86 | 90 |
91 |
92 | 93 | 94 |
95 |
96 |
97 |
98 | 99 | 100 | 105 | 106 | 107 | 115 | 116 | 117 | 221 | 222 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "app.py", 6 | "use": "@vercel/python" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "app.py" 13 | } 14 | ] 15 | } --------------------------------------------------------------------------------