feat: Initial commit
This commit is contained in:
		
							
								
								
									
										141
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| ### Python template | ||||
| # Byte-compiled / optimized / DLL files | ||||
| __pycache__/ | ||||
| *.py[cod] | ||||
| *$py.class | ||||
|  | ||||
| # C extensions | ||||
| *.so | ||||
|  | ||||
| # Distribution / packaging | ||||
| .Python | ||||
| build/ | ||||
| develop-eggs/ | ||||
| dist/ | ||||
| downloads/ | ||||
| eggs/ | ||||
| .eggs/ | ||||
| lib/ | ||||
| lib64/ | ||||
| parts/ | ||||
| sdist/ | ||||
| var/ | ||||
| wheels/ | ||||
| share/python-wheels/ | ||||
| *.egg-info/ | ||||
| .installed.cfg | ||||
| *.egg | ||||
| MANIFEST | ||||
|  | ||||
| # PyInstaller | ||||
| #  Usually these files are written by a python script from a template | ||||
| #  before PyInstaller builds the exe, so as to inject date/other infos into it. | ||||
| *.manifest | ||||
| *.spec | ||||
|  | ||||
| # Installer logs | ||||
| pip-log.txt | ||||
| pip-delete-this-directory.txt | ||||
|  | ||||
| # Unit test / coverage reports | ||||
| htmlcov/ | ||||
| .tox/ | ||||
| .nox/ | ||||
| .coverage | ||||
| .coverage.* | ||||
| .cache | ||||
| nosetests.xml | ||||
| coverage.xml | ||||
| *.cover | ||||
| *.py,cover | ||||
| .hypothesis/ | ||||
| .pytest_cache/ | ||||
| cover/ | ||||
|  | ||||
| # Translations | ||||
| *.mo | ||||
| *.pot | ||||
|  | ||||
| # Django stuff: | ||||
| *.log | ||||
| local_settings.py | ||||
| db.sqlite3 | ||||
| db.sqlite3-journal | ||||
|  | ||||
| # Flask stuff: | ||||
| instance/ | ||||
| .webassets-cache | ||||
|  | ||||
| # Scrapy stuff: | ||||
| .scrapy | ||||
|  | ||||
| # Sphinx documentation | ||||
| docs/_build/ | ||||
|  | ||||
| # PyBuilder | ||||
| .pybuilder/ | ||||
| target/ | ||||
|  | ||||
| # Jupyter Notebook | ||||
| .ipynb_checkpoints | ||||
|  | ||||
| # IPython | ||||
| profile_default/ | ||||
| ipython_config.py | ||||
|  | ||||
| # pyenv | ||||
| #   For a library or package, you might want to ignore these files since the code is | ||||
| #   intended to run in multiple environments; otherwise, check them in: | ||||
| # .python-version | ||||
|  | ||||
| # pipenv | ||||
| #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||||
| #   However, in case of collaboration, if having platform-specific dependencies or dependencies | ||||
| #   having no cross-platform support, pipenv may install dependencies that don't work, or not | ||||
| #   install all needed dependencies. | ||||
| #Pipfile.lock | ||||
|  | ||||
| # PEP 582; used by e.g. github.com/David-OConnor/pyflow | ||||
| __pypackages__/ | ||||
|  | ||||
| # Celery stuff | ||||
| celerybeat-schedule | ||||
| celerybeat.pid | ||||
|  | ||||
| # SageMath parsed files | ||||
| *.sage.py | ||||
|  | ||||
| # Environments | ||||
| .env | ||||
| .venv | ||||
| env/ | ||||
| venv/ | ||||
| ENV/ | ||||
| env.bak/ | ||||
| venv.bak/ | ||||
|  | ||||
| # Spyder project settings | ||||
| .spyderproject | ||||
| .spyproject | ||||
|  | ||||
| # Rope project settings | ||||
| .ropeproject | ||||
|  | ||||
| # mkdocs documentation | ||||
| /site | ||||
|  | ||||
| # mypy | ||||
| .mypy_cache/ | ||||
| .dmypy.json | ||||
| dmypy.json | ||||
|  | ||||
| # Pyre type checker | ||||
| .pyre/ | ||||
|  | ||||
| # pytype static type analyzer | ||||
| .pytype/ | ||||
|  | ||||
| # Cython debug symbols | ||||
| cython_debug/ | ||||
|  | ||||
| db/*.sqlite3 | ||||
							
								
								
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| MIT License | ||||
|  | ||||
| Copyright (c) 2022 DNTSK | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
							
								
								
									
										0
									
								
								later42/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								later42/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										5
									
								
								later42/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								later42/admin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| from django.contrib import admin | ||||
|  | ||||
| from .models.urls import URL | ||||
|  | ||||
| admin.site.register(URL) | ||||
							
								
								
									
										16
									
								
								later42/asgi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								later42/asgi.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| """ | ||||
| ASGI config for bookmarks project. | ||||
|  | ||||
| It exposes the ASGI callable as a module-level variable named ``application``. | ||||
|  | ||||
| For more information on this file, see | ||||
| https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ | ||||
| """ | ||||
|  | ||||
| import os | ||||
|  | ||||
| from django.core.asgi import get_asgi_application | ||||
|  | ||||
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'later42.settings') | ||||
|  | ||||
| application = get_asgi_application() | ||||
							
								
								
									
										35
									
								
								later42/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								later42/forms.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| from django.contrib.auth.forms import AuthenticationForm | ||||
|  | ||||
| from django.contrib.auth.models import User | ||||
| from django.contrib.auth.forms import UserCreationForm | ||||
|  | ||||
|  | ||||
| class SignUpForm(UserCreationForm): | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super().__init__(*args, **kwargs) | ||||
|         self.fields['username'].widget.attrs.update( | ||||
|             {'class': 'form-control'} | ||||
|         ) | ||||
|         self.fields['password1'].widget.attrs.update( | ||||
|             {'class': 'form-control'} | ||||
|         ) | ||||
|         self.fields['password2'].widget.attrs.update( | ||||
|             {'class': 'form-control'} | ||||
|         ) | ||||
|         self.fields['username'].label = 'Логин' | ||||
|  | ||||
|     class Meta: | ||||
|         model = User | ||||
|         fields = ('username', 'email', 'password1', 'password2',) | ||||
|  | ||||
|  | ||||
| class CustomLoginForm(AuthenticationForm): | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super().__init__(*args, **kwargs) | ||||
|         self.fields['username'].widget.attrs.update( | ||||
|             {'class': 'form-control'} | ||||
|         ) | ||||
|         self.fields['password'].widget.attrs.update( | ||||
|             {'class': 'form-control'} | ||||
|         ) | ||||
|         self.fields['username'].label = 'Логин' | ||||
							
								
								
									
										0
									
								
								later42/logging/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								later42/logging/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										18
									
								
								later42/logging/debug.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								later42/logging/debug.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| class ExceptionLoggingMiddleware(object): | ||||
|  | ||||
|     def __init__(self, get_response): | ||||
|         self.get_response = get_response | ||||
|  | ||||
|     def __call__(self, request): | ||||
|  | ||||
|         # Code to be executed for each request before | ||||
|         # the view (and later middleware) are called. | ||||
|         print(request.body) | ||||
|         print(request.scheme) | ||||
|         print(request.method) | ||||
|         print(request.META) | ||||
|         print(request.headers) | ||||
|  | ||||
|         response = self.get_response(request) | ||||
|  | ||||
|         return response | ||||
							
								
								
									
										26
									
								
								later42/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								later42/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| # Generated by Django 4.1.2 on 2022-10-12 10:05 | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     initial = True | ||||
|  | ||||
|     dependencies = [ | ||||
|         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name='URL', | ||||
|             fields=[ | ||||
|                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), | ||||
|                 ('url', models.CharField(max_length=2000)), | ||||
|                 ('title', models.CharField(max_length=2000)), | ||||
|                 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||||
|             ], | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										0
									
								
								later42/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								later42/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								later42/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								later42/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										9
									
								
								later42/models/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								later42/models/urls.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| from django.contrib.auth.models import User | ||||
| from django.db import models | ||||
|  | ||||
|  | ||||
| class URL(models.Model): | ||||
|     id = models.AutoField(auto_created=True, primary_key=True) | ||||
|     user = models.ForeignKey(User, on_delete=models.CASCADE) | ||||
|     url = models.CharField(max_length=2000) | ||||
|     title = models.CharField(max_length=2000) | ||||
							
								
								
									
										14
									
								
								later42/serializers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								later42/serializers.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| from django.contrib.auth.models import User, Group | ||||
| from rest_framework import serializers | ||||
|  | ||||
|  | ||||
| class UserSerializer(serializers.HyperlinkedModelSerializer): | ||||
|     class Meta: | ||||
|         model = User | ||||
|         fields = ['url', 'username', 'email', 'groups'] | ||||
|  | ||||
|  | ||||
| class GroupSerializer(serializers.HyperlinkedModelSerializer): | ||||
|     class Meta: | ||||
|         model = Group | ||||
|         fields = ['url', 'name'] | ||||
							
								
								
									
										142
									
								
								later42/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								later42/settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| """ | ||||
| Django settings for bookmarks project. | ||||
|  | ||||
| Generated by 'django-admin startproject' using Django 4.1.2. | ||||
|  | ||||
| For more information on this file, see | ||||
| https://docs.djangoproject.com/en/4.1/topics/settings/ | ||||
|  | ||||
| For the full list of settings and their values, see | ||||
| https://docs.djangoproject.com/en/4.1/ref/settings/ | ||||
| """ | ||||
| import os | ||||
| from pathlib import Path | ||||
|  | ||||
| # Build paths inside the project like this: BASE_DIR / 'subdir'. | ||||
| BASE_DIR = Path(__file__).resolve().parent.parent | ||||
|  | ||||
|  | ||||
| # Quick-start development settings - unsuitable for production | ||||
| # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ | ||||
|  | ||||
| # SECURITY WARNING: keep the secret key used in production secret! | ||||
| SECRET_KEY = os.getenv('SECRET', 'django-insecure-c%g@wujt4dco#e%k-!25o3)0%t+wm5=k%l(m!kk^p_g%kknod!') | ||||
|  | ||||
| # SECURITY WARNING: don't run with debug turned on in production! | ||||
| DEBUG = True | ||||
|  | ||||
| ALLOWED_HOSTS = ['*'] | ||||
|  | ||||
|  | ||||
| # Application definition | ||||
|  | ||||
| INSTALLED_APPS = [ | ||||
|     'django.contrib.admin', | ||||
|     'django.contrib.auth', | ||||
|     'django.contrib.contenttypes', | ||||
|     'django.contrib.sessions', | ||||
|     'django.contrib.messages', | ||||
|     'django.contrib.staticfiles', | ||||
|     'later42', | ||||
|     'rest_framework', | ||||
|     'rest_framework.authtoken', | ||||
| ] | ||||
|  | ||||
| MIDDLEWARE = [ | ||||
|     # 'later42.logging.debug.ExceptionLoggingMiddleware', | ||||
|     'django.middleware.security.SecurityMiddleware', | ||||
|     'django.contrib.sessions.middleware.SessionMiddleware', | ||||
|     'django.middleware.common.CommonMiddleware', | ||||
|     'django.middleware.csrf.CsrfViewMiddleware', | ||||
|     'django.contrib.auth.middleware.AuthenticationMiddleware', | ||||
|     'django.contrib.messages.middleware.MessageMiddleware', | ||||
|     'django.middleware.clickjacking.XFrameOptionsMiddleware', | ||||
| ] | ||||
|  | ||||
| ROOT_URLCONF = 'later42.urls' | ||||
|  | ||||
| TEMPLATES = [ | ||||
|     { | ||||
|         'BACKEND': 'django.template.backends.django.DjangoTemplates', | ||||
|         'DIRS': [], | ||||
|         'APP_DIRS': True, | ||||
|         'OPTIONS': { | ||||
|             'context_processors': [ | ||||
|                 'django.template.context_processors.debug', | ||||
|                 'django.template.context_processors.request', | ||||
|                 'django.contrib.auth.context_processors.auth', | ||||
|                 'django.contrib.messages.context_processors.messages', | ||||
|             ], | ||||
|         }, | ||||
|     }, | ||||
| ] | ||||
|  | ||||
| WSGI_APPLICATION = 'later42.wsgi.application' | ||||
|  | ||||
|  | ||||
| # Database | ||||
| # https://docs.djangoproject.com/en/4.1/ref/settings/#databases | ||||
|  | ||||
| DATABASES = { | ||||
|     'default': { | ||||
|         'ENGINE': 'django.db.backends.sqlite3', | ||||
|         'NAME': BASE_DIR / 'db/db.sqlite3', | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| # Password validation | ||||
| # https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators | ||||
|  | ||||
| AUTH_PASSWORD_VALIDATORS = [ | ||||
|     { | ||||
|         'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', | ||||
|     }, | ||||
|     { | ||||
|         'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', | ||||
|     }, | ||||
|     { | ||||
|         'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', | ||||
|     }, | ||||
|     { | ||||
|         'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', | ||||
|     }, | ||||
| ] | ||||
|  | ||||
|  | ||||
| # Internationalization | ||||
| # https://docs.djangoproject.com/en/4.1/topics/i18n/ | ||||
|  | ||||
| LANGUAGE_CODE = 'ru-ru' | ||||
|  | ||||
| TIME_ZONE = 'UTC' | ||||
|  | ||||
| USE_I18N = True | ||||
|  | ||||
| USE_TZ = True | ||||
|  | ||||
|  | ||||
| # Static files (CSS, JavaScript, Images) | ||||
| # https://docs.djangoproject.com/en/4.1/howto/static-files/ | ||||
|  | ||||
| STATIC_URL = 'static/' | ||||
| STATICFILES_DIRS = [ | ||||
|     BASE_DIR / "later42/static", | ||||
| ] | ||||
| # Default primary key field type | ||||
| # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field | ||||
|  | ||||
| DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' | ||||
| LOGIN_REDIRECT_URL = "index" | ||||
| LOGOUT_REDIRECT_URL = "index" | ||||
|  | ||||
| REST_FRAMEWORK = { | ||||
|     'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', | ||||
|     'PAGE_SIZE': 10, | ||||
|     'DEFAULT_AUTHENTICATION_CLASSES': ( | ||||
|         'rest_framework.authentication.TokenAuthentication', | ||||
|     ), | ||||
|     'DEFAULT_PERMISSION_CLASSES': ( | ||||
|         'rest_framework.permissions.IsAuthenticated', | ||||
|     ), | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								later42/static/assets/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								later42/static/assets/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 23 KiB | 
							
								
								
									
										
											BIN
										
									
								
								later42/static/assets/img/about-bg.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								later42/static/assets/img/about-bg.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.4 MiB | 
							
								
								
									
										
											BIN
										
									
								
								later42/static/assets/img/contact-bg.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								later42/static/assets/img/contact-bg.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 489 KiB | 
							
								
								
									
										
											BIN
										
									
								
								later42/static/assets/img/home-bg.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								later42/static/assets/img/home-bg.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 984 KiB | 
							
								
								
									
										
											BIN
										
									
								
								later42/static/assets/img/post-bg.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								later42/static/assets/img/post-bg.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.7 MiB | 
							
								
								
									
										
											BIN
										
									
								
								later42/static/assets/img/post-sample-image.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								later42/static/assets/img/post-sample-image.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 112 KiB | 
							
								
								
									
										11248
									
								
								later42/static/css/styles.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11248
									
								
								later42/static/css/styles.css
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										29
									
								
								later42/static/js/scripts.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								later42/static/js/scripts.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| /*! | ||||
| * Start Bootstrap - Clean Blog v6.0.8 (https://startbootstrap.com/theme/clean-blog) | ||||
| * Copyright 2013-2022 Start Bootstrap | ||||
| * Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-clean-blog/blob/master/LICENSE) | ||||
| */ | ||||
| window.addEventListener('DOMContentLoaded', () => { | ||||
|     let scrollPos = 0; | ||||
|     const mainNav = document.getElementById('mainNav'); | ||||
|     const headerHeight = mainNav.clientHeight; | ||||
|     window.addEventListener('scroll', function() { | ||||
|         const currentTop = document.body.getBoundingClientRect().top * -1; | ||||
|         if ( currentTop < scrollPos) { | ||||
|             // Scrolling Up | ||||
|             if (currentTop > 0 && mainNav.classList.contains('is-fixed')) { | ||||
|                 mainNav.classList.add('is-visible'); | ||||
|             } else { | ||||
|                 console.log(123); | ||||
|                 mainNav.classList.remove('is-visible', 'is-fixed'); | ||||
|             } | ||||
|         } else { | ||||
|             // Scrolling Down | ||||
|             mainNav.classList.remove(['is-visible']); | ||||
|             if (currentTop > headerHeight && !mainNav.classList.contains('is-fixed')) { | ||||
|                 mainNav.classList.add('is-fixed'); | ||||
|             } | ||||
|         } | ||||
|         scrollPos = currentTop; | ||||
|     }); | ||||
| }) | ||||
							
								
								
									
										98
									
								
								later42/templates/base.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								later42/templates/base.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| {% load static %} | ||||
|  | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|     <head> | ||||
|         <meta charset="utf-8" /> | ||||
|         <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> | ||||
|         <meta name="description" content="Ссылки для отложенного чтения. Сохрани ссылки для отложенного чтения, если нет времени прочитать сейчас." /> | ||||
|         <meta name="author" content="Silver Ghost" /> | ||||
|         <title>Later 42: {% block title %}Сохрани ссылки, чтобы прочитать позже{% endblock %}</title> | ||||
|         <link rel="icon" type="image/x-icon" href="assets/favicon.ico" /> | ||||
|         <!-- Font Awesome icons (free version)--> | ||||
|         <script src="https://use.fontawesome.com/releases/v6.1.0/js/all.js" crossorigin="anonymous"></script> | ||||
|         <!-- Google fonts--> | ||||
|         <link href="https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic" rel="stylesheet" type="text/css" /> | ||||
|         <link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css" /> | ||||
|         <!-- Core theme CSS (includes Bootstrap)--> | ||||
|         <link href="{% static 'css/styles.css' %}" rel="stylesheet" /> | ||||
|     </head> | ||||
|     <body> | ||||
|         <!-- Navigation--> | ||||
|         <nav class="navbar navbar-expand-lg navbar-light" id="mainNav"> | ||||
|             <div class="container px-4 px-lg-5"> | ||||
|                 <a class="navbar-brand" href="{% url 'index' %}">Later 42</a> | ||||
|                 <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|                     Menu | ||||
|                     <i class="fas fa-bars"></i> | ||||
|                 </button> | ||||
|                 <div class="collapse navbar-collapse" id="navbarResponsive"> | ||||
|                     <ul class="navbar-nav ms-auto py-4 py-lg-0"> | ||||
| {% if user.is_authenticated %} | ||||
|                         <li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="{% url 'profile' %}">Профиль</a></li> | ||||
|                         <li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="{% url 'logout' %}">Выйти</a></li> | ||||
| {% else %} | ||||
|                         <li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="{% url 'signup' %}">Регистрация</a></li> | ||||
|                         <li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="{% url 'login' %}">Войти</a></li> | ||||
| {% endif %} | ||||
|                     </ul> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </nav> | ||||
|         <!-- Page Header--> | ||||
|         <header class="masthead" style="background-image: url('{% static 'assets/img/home-bg.jpg' %}')"> | ||||
|             <div class="container position-relative px-4 px-lg-5"> | ||||
|                 <div class="row gx-4 gx-lg-5 justify-content-center"> | ||||
|                     <div class="col-md-10 col-lg-8 col-xl-7"> | ||||
|                         <div class="site-heading"> | ||||
|                             <h1>Later 42</h1> | ||||
|                             <span class="subheading">Сохрани ссылки, чтобы прочитать позже</span> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </header> | ||||
|         <!-- Main Content--> | ||||
|         <div class="container px-4 px-lg-5"> | ||||
|             <div class="row gx-4 gx-lg-5 justify-content-center"> | ||||
|                 <div class="col-md-10 col-lg-8 col-xl-7"> | ||||
|  | ||||
| {% block content %}{% endblock %} | ||||
|  | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <!-- Footer--> | ||||
|         <footer class="border-top"> | ||||
|             <div class="container px-4 px-lg-5"> | ||||
|                 <div class="row gx-4 gx-lg-5 justify-content-center"> | ||||
|                     <div class="col-md-10 col-lg-8 col-xl-7"> | ||||
|                         <ul class="list-inline text-center"> | ||||
|                             <li class="list-inline-item"> | ||||
|                                 <a href="https://dntsk.dev"> | ||||
|                                     <span class="fa-stack fa-lg"> | ||||
|                                         <i class="fas fa-circle fa-stack-2x"></i> | ||||
|                                         <i class="fa-solid fa-location-dot fa-stack-1x fa-inverse"></i> | ||||
|                                     </span> | ||||
|                                 </a> | ||||
|                             </li> | ||||
|                             <li class="list-inline-item"> | ||||
|                                 <a href="#!"> | ||||
|                                     <span class="fa-stack fa-lg"> | ||||
|                                         <i class="fas fa-circle fa-stack-2x"></i> | ||||
|                                         <i class="fab fa-github fa-stack-1x fa-inverse"></i> | ||||
|                                     </span> | ||||
|                                 </a> | ||||
|                             </li> | ||||
|                         </ul> | ||||
|                         <div class="small text-center text-muted fst-italic">© dntsk.dev, 2022</div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </footer> | ||||
|         <!-- Bootstrap core JS--> | ||||
|         <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> | ||||
|         <!-- Core theme JS--> | ||||
|         <script src="{% static 'js/scripts.js' %}"></script> | ||||
|     </body> | ||||
| </html> | ||||
							
								
								
									
										2
									
								
								later42/templates/docs.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								later42/templates/docs.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| Test content block | ||||
| <p></p> | ||||
							
								
								
									
										20
									
								
								later42/templates/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								later42/templates/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| {% extends 'base.html' %} | ||||
|  | ||||
| {% block content %} | ||||
|     {% if not user.is_authenticated %} | ||||
|     {% include 'docs.html' %} | ||||
|     {% endif %} | ||||
|     {% for url in urls %} | ||||
|                     <!-- Post preview--> | ||||
|                     <div class="post-preview"> | ||||
|                         <a href="{{ url.url }}"> | ||||
|                             <h2 class="post-title">{{ url.title }}</h2> | ||||
|                         </a> | ||||
|                         <p class="post-meta"> | ||||
|                             {{ url.url }} | ||||
|                         </p> | ||||
|                     </div> | ||||
|                     <!-- Divider--> | ||||
|                     <hr class="my-4" /> | ||||
|     {% endfor %} | ||||
| {% endblock %} | ||||
							
								
								
									
										51
									
								
								later42/templates/profile.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								later42/templates/profile.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| {% extends 'base.html' %} | ||||
|  | ||||
| {% block title %}Настройки{% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
|  | ||||
|     <h2 class="center-block">Профиль</h2> | ||||
|  | ||||
|     <p> </p> | ||||
|     <div class="container"> | ||||
|       <div class="row align-items-start"> | ||||
|         <div class="col"> | ||||
|           Логин: | ||||
|         </div> | ||||
|         <div class="col"> | ||||
|           {{ user.username }} | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="row align-items-start"> | ||||
|         <div class="col"> | ||||
|           Email: | ||||
|         </div> | ||||
|         <div class="col"> | ||||
|           {{ user.email }} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <p> </p> | ||||
|     <div class="container"> | ||||
|       <div class="row align-items-start"> | ||||
|         <div class="col"> | ||||
|           API ключ: | ||||
|         </div> | ||||
|         <div class="col"> | ||||
| {% if token is not None %} | ||||
|             {{ token }} | ||||
|             <form action="{% url 'api_token' %}" method="post"> | ||||
|                 {% csrf_token %} | ||||
|                 <input type="submit" value="Сбросить на новый"> | ||||
|             </form> | ||||
| {% else %} | ||||
|             <form action="{% url 'api_token' %}" method="post"> | ||||
|                 {% csrf_token %} | ||||
|                 <input type="submit" value="Создать"> | ||||
|             </form> | ||||
| {% endif %} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <p></p> | ||||
| {% endblock %} | ||||
							
								
								
									
										12
									
								
								later42/templates/registration/login.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								later42/templates/registration/login.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| {% extends 'base.html' %} | ||||
|  | ||||
| {% block content %} | ||||
| <h2>Вход</h2> | ||||
| <form method="post"> | ||||
|   {% csrf_token %} | ||||
|   {{ form.as_p }} | ||||
|   <button class="btn btn-primary text-uppercase" type="submit">Войти</button> | ||||
| </form> | ||||
| <br /> | ||||
| {% endblock %} | ||||
|  | ||||
							
								
								
									
										24
									
								
								later42/templates/signup.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								later42/templates/signup.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| {% extends 'base.html' %} | ||||
|  | ||||
| {% block content %} | ||||
|  | ||||
|     <h2>Регистрация</h2> | ||||
|      <form method="post"> | ||||
|         {% csrf_token %} | ||||
|         {% for field in form %} | ||||
|           <p> | ||||
|             {{ field.label_tag }}<br> | ||||
|             {{ field }} | ||||
|             {% if field.help_text %} | ||||
|               <small class="text-muted">{{ field.help_text }}</small> | ||||
|             {% endif %} | ||||
|             {% for error in field.errors %} | ||||
|               <p style="color: red">{{ error }}</p> | ||||
|             {% endfor %} | ||||
|           </p> | ||||
|         {% endfor %} | ||||
|         <button class="btn btn-primary text-uppercase" type="submit">Зарегистрироваться</button> | ||||
|       </form> | ||||
|     <p></p> | ||||
|  | ||||
| {% endblock %} | ||||
							
								
								
									
										53
									
								
								later42/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								later42/urls.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| """later42 URL Configuration | ||||
|  | ||||
| The `urlpatterns` list routes URLs to views. For more information please see: | ||||
|     https://docs.djangoproject.com/en/4.1/topics/http/urls/ | ||||
| Examples: | ||||
| Function views | ||||
|     1. Add an import:  from my_app import views | ||||
|     2. Add a URL to urlpatterns:  path('', views.home, name='home') | ||||
| Class-based views | ||||
|     1. Add an import:  from other_app.views import Home | ||||
|     2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home') | ||||
| Including another URLconf | ||||
|     1. Import the include() function: from django.urls import include, path | ||||
|     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls')) | ||||
| """ | ||||
| from django.contrib import admin | ||||
|  | ||||
| from django.urls import path, include | ||||
| from django.contrib.auth.models import User | ||||
| from django.contrib.auth.views import LoginView | ||||
|  | ||||
| from rest_framework import routers, serializers, viewsets | ||||
|  | ||||
| from later42.forms import CustomLoginForm | ||||
| from later42.views import index, profile, api, api_token, signup | ||||
|  | ||||
|  | ||||
| class UserSerializer(serializers.HyperlinkedModelSerializer): | ||||
|     class Meta: | ||||
|         model = User | ||||
|         fields = ['url', 'username', 'email', 'is_staff'] | ||||
|  | ||||
|  | ||||
| class UserViewSet(viewsets.ModelViewSet): | ||||
|     queryset = User.objects.all() | ||||
|     serializer_class = UserSerializer | ||||
|  | ||||
|  | ||||
| router = routers.DefaultRouter() | ||||
| router.register(r'users', UserViewSet) | ||||
|  | ||||
| urlpatterns = [ | ||||
|     path('admin/', admin.site.urls), | ||||
|     path('signup/', signup.register, name='signup'), | ||||
|     path("accounts/login/", LoginView.as_view(authentication_form=CustomLoginForm), name="login"), | ||||
|     path('accounts/', include('django_registration.backends.activation.urls')), | ||||
|     path("accounts/", include("django.contrib.auth.urls")), | ||||
|     path('api/url/', api.URL.as_view(), name='urls'), | ||||
|     path('', index.get, name='index'), | ||||
|     path('delete/<int:url_id>', index.delete, name='delete'), | ||||
|     path('profile/', profile.get, name='profile'), | ||||
|     path('api_token/', api_token.create, name='api_token'), | ||||
| ] | ||||
							
								
								
									
										0
									
								
								later42/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								later42/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										37
									
								
								later42/views/api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								later42/views/api.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| import requests | ||||
|  | ||||
| from rest_framework.response import Response | ||||
| from rest_framework.views import APIView | ||||
| from bs4 import BeautifulSoup | ||||
| from later42.models.urls import URL as URLModel | ||||
|  | ||||
|  | ||||
| class URL(APIView): | ||||
|     def get_title(self, url): | ||||
|         try: | ||||
|             response = requests.get(url) | ||||
|             soup = BeautifulSoup(response.text, 'html.parser') | ||||
|             title = soup.find('title').text | ||||
|             return title | ||||
|         except AttributeError: | ||||
|             return None | ||||
|  | ||||
|     def post(self, request, format=None): | ||||
|         url = request.GET.get('url') | ||||
|         if url: | ||||
|             title = self.get_title(url) | ||||
|             if title is None: | ||||
|                 title = url | ||||
|             url = URLModel(url=url, title=title, user=request.user) | ||||
|             url.save() | ||||
|             return Response({'status': 'success'}) | ||||
|         else: | ||||
|             return Response({'status': 'error'}) | ||||
|  | ||||
|     def delete(self, request, format=None): | ||||
|         id = request.GET.get('id') | ||||
|         if id: | ||||
|             URLModel.objects.filter(id=id).delete() | ||||
|             return Response({'status': 'success'}) | ||||
|         else: | ||||
|             return Response({'status': 'error'}) | ||||
							
								
								
									
										13
									
								
								later42/views/api_token.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								later42/views/api_token.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| from django.shortcuts import redirect | ||||
| from rest_framework.authtoken.models import Token | ||||
|  | ||||
|  | ||||
| def create(request): | ||||
|     token = Token.objects.filter(user=request.user) | ||||
|     try: | ||||
|         token.delete() | ||||
|     except: | ||||
|         pass | ||||
|     token = Token.objects.create(user=request.user) | ||||
|     token.save() | ||||
|     return redirect('profile') | ||||
							
								
								
									
										18
									
								
								later42/views/index.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								later42/views/index.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| from django.contrib.auth.decorators import login_required | ||||
| from django.shortcuts import render, redirect | ||||
| from later42.models.urls import URL | ||||
|  | ||||
|  | ||||
| def get(request): | ||||
|     try: | ||||
|         urls = URL.objects.filter(user=request.user).order_by('-id') | ||||
|         context = {'urls': urls} | ||||
|     except: | ||||
|         context = {'urls': []} | ||||
|     return render(request, 'index.html', context) | ||||
|  | ||||
|  | ||||
| @login_required | ||||
| def delete(request, url_id): | ||||
|     URL.objects.filter(id=url_id, user=request.user).delete() | ||||
|     return redirect('index') | ||||
							
								
								
									
										18
									
								
								later42/views/profile.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								later42/views/profile.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| from django.contrib.auth.decorators import login_required | ||||
| from django.contrib.auth.models import User | ||||
| from django.shortcuts import render | ||||
| from rest_framework.authtoken.models import Token | ||||
|  | ||||
|  | ||||
| @login_required | ||||
| def get(request): | ||||
|     token = Token.objects.filter(user=request.user) | ||||
|     user = User.objects.get(username=request.user) | ||||
|  | ||||
|     if token: | ||||
|         context = {'token': token[0], 'user': user} | ||||
|     else: | ||||
|         context = {'token': None, 'user': user} | ||||
|  | ||||
|     return render(request, 'profile.html', context) | ||||
|  | ||||
							
								
								
									
										21
									
								
								later42/views/signup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								later42/views/signup.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| from django.contrib.auth import authenticate, login | ||||
| from django.shortcuts import redirect, render | ||||
| from later42.forms import SignUpForm | ||||
|  | ||||
|  | ||||
| def register(request): | ||||
|     if request.method == 'POST': | ||||
|         form = SignUpForm(request.POST) | ||||
|         if form.is_valid(): | ||||
|             user = form.save() | ||||
|             user.refresh_from_db() | ||||
|             user.save() | ||||
|             raw_password = form.cleaned_data.get('password1') | ||||
|  | ||||
|             user = authenticate(username=user.username, password=raw_password) | ||||
|             login(request, user) | ||||
|  | ||||
|             return redirect('index') | ||||
|     else: | ||||
|         form = SignUpForm() | ||||
|     return render(request, 'signup.html', {'form': form}) | ||||
							
								
								
									
										16
									
								
								later42/wsgi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								later42/wsgi.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| """ | ||||
| WSGI config for bookmarks project. | ||||
|  | ||||
| It exposes the WSGI callable as a module-level variable named ``application``. | ||||
|  | ||||
| For more information on this file, see | ||||
| https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ | ||||
| """ | ||||
|  | ||||
| import os | ||||
|  | ||||
| from django.core.wsgi import get_wsgi_application | ||||
|  | ||||
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'later42.settings') | ||||
|  | ||||
| application = get_wsgi_application() | ||||
							
								
								
									
										22
									
								
								manage.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										22
									
								
								manage.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| #!/usr/bin/env python | ||||
| """Django's command-line utility for administrative tasks.""" | ||||
| import os | ||||
| import sys | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     """Run administrative tasks.""" | ||||
|     os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'later42.settings') | ||||
|     try: | ||||
|         from django.core.management import execute_from_command_line | ||||
|     except ImportError as exc: | ||||
|         raise ImportError( | ||||
|             "Couldn't import Django. Are you sure it's installed and " | ||||
|             "available on your PYTHONPATH environment variable? Did you " | ||||
|             "forget to activate a virtual environment?" | ||||
|         ) from exc | ||||
|     execute_from_command_line(sys.argv) | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     main() | ||||
							
								
								
									
										6
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| Django==4.1.2 | ||||
| gunicorn==20.1.0 | ||||
| loguru==0.6.0 | ||||
| bs4==0.0.1 | ||||
| requests==2.28.1 | ||||
| djangorestframework==3.14.0 | ||||
		Reference in New Issue
	
	Block a user