Skip to content

Commit

Permalink
Merge pull request #1 from codescalersinternships/development
Browse files Browse the repository at this point in the history
Implementation of Secret Note web application using django
  • Loading branch information
abdahmed22 authored Nov 14, 2024
2 parents 3213dda + bbb0cf1 commit 9dea105
Show file tree
Hide file tree
Showing 87 changed files with 1,238 additions and 1 deletion.
34 changes: 34 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This workflow will test a python project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Test

on:
push:
branches: [ "development" ]
pull_request:
branches: [ "main" ]

env:
PYTHON_VERSION: '3.10.12'

jobs:

test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Python
uses: actions/setup-python@v3
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install requirments
run: make install_requirements

- name: Run tests
run: make run_tests

25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
all: install_requirements

install_requirements:
pip install --no-cache-dir -r requirements.txt

run_sever: install_requirements
python3 secretnote/manage.py runserver

run_tests:
python3 secretnote/manage.py test home
python3 secretnote/manage.py test notes

run_e2etests:
python3 secretnote/manage.py test e2etests

run_lint:
pip install pylint
pylint ./secretnote

build_image:
docker build --tag=buildme-secretnote:v5.0 --target=base .

run_container:
docker compose up

50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,50 @@
# SecretNote-MVC-Abdelrahman-Mahmoud
https://github.com/codescalersinternships/home/issues/225

## Introduction

Secret notes is a web application that allows the users to securely share self-destructing secret notes.

## Features:

### Signup

Create a new user by pressing the signup button on the navigation bar then creatie a username and password.

### Login

Login by pressing the login button on the navigation bar then entering your username and password.

### Logout

Logout by pressing the logout button on the navigation bar then confirm your logout request.

### Create a secret note

Create a secret note by pressing the Create Note button on the home page then enter the title and content of your secret note.

### Delete a secret note

Delete a note by pressing the delete button on the note's detail page then confirm your delete request.

### Edit a secret note

Edit a note by pressing the edit button on the note's detail page then enter the new title and content of your secret note.

### View all notes

View your secret notes by pressing the My Notes button on the home page.

### View note details

View your note's details by pressing on the details button on My Notes page.

### Built-in Features:

- Unique, secure URL for each note.
- View count expiration.
- Rate limiting to prevent abuse of server.
- Database encryption

## Setup

## Tests
5 changes: 5 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
services:
secretnote:
image: buildme-secretnote:v5.0
ports:
- "8000:8000"
16 changes: 16 additions & 0 deletions dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.10-alpine3.20 AS base

WORKDIR /app

COPY . /app/

RUN apk update
RUN apk add libpq

RUN pip install --no-cache-dir -r requirements.txt

ENTRYPOINT ["python3"]
CMD ["secretnote/manage.py", "runserver", "0.0.0.0:8000"]

EXPOSE 8000

3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cryptography==42.0.8
Django==5.0.7
selenium==4.22.0
Empty file added secretnote/__init__.py
Empty file.
Binary file added secretnote/db.sqlite3
Binary file not shown.
Empty file added secretnote/e2etests/__init__.py
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added secretnote/e2etests/geckodriver
Binary file not shown.
53 changes: 53 additions & 0 deletions secretnote/e2etests/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from selenium import webdriver
from selenium.webdriver.common.by import By
from django.contrib.staticfiles.testing import StaticLiveServerTestCase


class TestNavBar(StaticLiveServerTestCase):
def setUp(self):
self.browser = webdriver.Firefox()

def tearDown(self):
self.browser.close()

def test_secretnote(self):
self.browser.get(self.live_server_url)

navbar = self.browser.find_element(By.ID, "navbar")
navbar.find_element(By.ID, "signup_button").click()

signup_form = self.browser.find_element(By.ID, "signup_form")
signup_form.find_element(By.ID, "id_username").send_keys("username")
signup_form.find_element(By.ID, "id_password1").send_keys("strongPassword11")
signup_form.find_element(By.ID, "id_password2").send_keys("strongPassword11")
signup_form.find_element(By.ID, "signup_submit").click()

navbar = self.browser.find_element(By.ID, "navbar")
navbar.find_element(By.ID, "login_button").click()

login_form = self.browser.find_element(By.ID, "login_form")
login_form.find_element(By.ID, "id_username").send_keys("username")
login_form.find_element(By.ID, "id_password").send_keys("strongPassword11")
login_form.find_element(By.ID, "login_submit").click()

home = self.browser.find_element(By.ID, "home")
home.find_element(By.ID, "create_button").click()

note_form = self.browser.find_element(By.ID, "note_form")
note_form.find_element(By.ID, "id_title").send_keys("secret note")
note_form.find_element(By.ID, "id_text").send_keys(
"secret note secret note secret note secret note"
)
note_form.find_element(By.ID, "id_secret").send_keys("secret")
note_form.find_element(By.ID, "id_destruction_date").send_keys("222225666")
note_form.find_element(By.ID, "note_submit").click()

note_list = self.browser.find_element(By.ID, "note_list")
note_list.find_element(By.ID, "view_button").click()

note_detail = self.browser.find_element(By.ID, "note_detail")
self.assertEquals(note_detail.find_element(By.ID, "title").text, "secret note")
self.assertEquals(
note_detail.find_element(By.ID, "text").text,
"secret note secret note secret note secret note",
)
Empty file added secretnote/home/__init__.py
Empty file.
Binary file added secretnote/home/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file added secretnote/home/__pycache__/admin.cpython-310.pyc
Binary file not shown.
Binary file added secretnote/home/__pycache__/apps.cpython-310.pyc
Binary file not shown.
Binary file added secretnote/home/__pycache__/models.cpython-310.pyc
Binary file not shown.
Binary file added secretnote/home/__pycache__/urls.cpython-310.pyc
Binary file not shown.
Binary file added secretnote/home/__pycache__/views.cpython-310.pyc
Binary file not shown.
3 changes: 3 additions & 0 deletions secretnote/home/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions secretnote/home/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class HomeConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'home'
Empty file.
Binary file not shown.
21 changes: 21 additions & 0 deletions secretnote/home/templates/home/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% load static %}

{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<form id="login_form" action="" method="post">
<div class="card mt-5">
<div class="card-header">Enter your username and password</div>
<div class="card-body">
{% csrf_token %}
{{ form.as_p }}
</div>
<div class="card-footer">
<input id="login_submit" type="submit" class="btn btn-sm btn-success" value="Login" />
</div>
</div>
</form>
</div>
</div>
{% endblock %}
19 changes: 19 additions & 0 deletions secretnote/home/templates/home/logout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{% extends 'base.html' %}
{% load static %}

{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<form action="{% url 'postlogout' %}" method="post">
<div class="card mt-5">
<div class="card-header">Thanks for using Secret Notes !!</div>
<div class="card-body">
{% csrf_token %}
<input type="submit" class="btn btn-sm btn-danger" value="Logout" />
<a href="{% url 'home' %}" class="btn btn-success btn-sm">Back</a>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
21 changes: 21 additions & 0 deletions secretnote/home/templates/home/signup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% load static %}

{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<form id="signup_form" action="" method="post">
<div class="card mt-5">
<div class="card-header">Create a new user</div>
<div class="card-body">
{% csrf_token %}
{{ form.as_p }}
</div>
<div class="card-footer">
<input id="signup_submit" type="submit" class="btn btn-sm btn-success" value="Sign Up" />
</div>
</div>
</form>
</div>
</div>
{% endblock %}
16 changes: 16 additions & 0 deletions secretnote/home/templates/home/welcome.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% load static %}

{% block content %}
<div class="row">
<div class="col">
<div id="home" class="home-holder text-center">
<h1 class="fs-3 mb-5">Secret Notes</h1>
<p>Welcome to secret notes!</p>
<p>Create and securely share self-destructing secret notes.</p>
<a id="create_button" href="{% url 'notes.new' %}" class="btn btn-success btn-sm">Create Note</a>
<a href="{% url 'notes.list' %}" class="btn btn-success btn-sm">My Notes</a>
</div>
</div>
</div>
{% endblock %}
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
27 changes: 27 additions & 0 deletions secretnote/home/tests/test_urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.test import SimpleTestCase
from django.urls import resolve, reverse
from home.views import LoginInterfaceView, HomeView, SignUpInterfaceView
from django.contrib.auth.views import LogoutView


class TestUrls(SimpleTestCase):

def test_home_url_resolves(self):
url = reverse("home")
self.assertEquals(resolve(url).func.__name__, HomeView.as_view().__name__)

def test_login_url_resolves(self):
url = reverse("login")
self.assertEquals(
resolve(url).func.__name__, LoginInterfaceView.as_view().__name__
)

def test_logout_url_resolves(self):
url = reverse("logout")
self.assertEquals(resolve(url).func.__name__, LogoutView.as_view().__name__)

def test_signup_url_resolves(self):
url = reverse("signup")
self.assertEquals(
resolve(url).func.__name__, SignUpInterfaceView.as_view().__name__
)
28 changes: 28 additions & 0 deletions secretnote/home/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth.models import User


class TestViews(TestCase):

def setUp(self):
self.client = Client()
self.user = User.objects.create_user(username="testuser", password="password")
self.home_url = reverse("home")
self.login_url = reverse("login")
self.signup_url = reverse("signup")

def test_home_view(self):
response = self.client.get(self.home_url)
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, "home/welcome.html")

def test_login_view(self):
response = self.client.get(self.login_url)
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, "home/login.html")

def test_signup_view(self):
response = self.client.get(self.signup_url)
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, "home/signup.html")
10 changes: 10 additions & 0 deletions secretnote/home/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.urls import path
from . import views
from django.contrib.auth import views as auth_views

urlpatterns = [
path("", views.HomeView.as_view(), name="home"),
path("login/", views.LoginInterfaceView.as_view(), name="login"),
path("logout/", auth_views.LogoutView.as_view(), name="logout"),
path("signup/", views.SignUpInterfaceView.as_view(), name="signup"),
]
26 changes: 26 additions & 0 deletions secretnote/home/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from django.views.generic import TemplateView, CreateView
from django.contrib.auth.views import LoginView
from django.contrib.auth.forms import UserCreationForm
from django.shortcuts import redirect


# @ratelimit(key='ip', rate='100/h')
class SignUpInterfaceView(CreateView):
template_name = "home/signup.html"
form_class = UserCreationForm
success_url = "/"

def get(self, request, *args, **kwargs):
if self.request.user.is_authenticated:
return redirect("/")
return super().get(request, *args, **kwargs)


# @ratelimit(key='ip', rate='100/h')
class LoginInterfaceView(LoginView):
template_name = "home/login.html"


# @ratelimit(key='ip', rate='100/h')
class HomeView(TemplateView):
template_name = "home/welcome.html"
Loading

0 comments on commit 9dea105

Please sign in to comment.