setup for DRF

This commit is contained in:
j3d1 2023-06-11 10:46:11 +02:00
parent 6816ff0865
commit 5f68e12f82
10 changed files with 323 additions and 31 deletions

View file

@ -1,13 +1,53 @@
# toolshed # toolshed
## Installation ## Installation / Development
``` bash ``` bash
git clone https://github.com/gr4yj3d1/toolshed.git git clone https://github.com/gr4yj3d1/toolshed.git
```
or
``` bash
git clone https://git.neulandlabor.de/j3d1/toolshed.git
```
### Backend
``` bash
cd toolshed/backend cd toolshed/backend
python3 -m venv venv python3 -m venv venv
source venv/bin/activate source venv/bin/activate
pip install -r requirements.txt pip install -r requirements.txt
python manage.py migrate python configure.py
python manage.py runserver python manage.py runserver 0.0.0.0:800 --insecure
``` ```
to run this in properly in production, you need to configure a webserver to serve the static files and proxy the requests to the backend, then run the backend with just `python manage.py runserver` without the `--insecure` flag.
### Frontend
``` bash
cd toolshed/frontend
npm install
npm run dev
```
### Docs
``` bash
cd toolshed/docs
mkdocs serve
```
## CLI Client
### Requirements
- python3
- python3-nacl
### Usage Example
``` bash
cli-client/toolshed-client.py --key <hex private key> --user name@example.com --host 1.2.3.4:8000 getinventory
```

2
backend/.env.dist Normal file
View file

@ -0,0 +1,2 @@
ALLOWED_HOSTS="localhost,127.0.0.1"

13
backend/Dockerfile Normal file
View file

@ -0,0 +1,13 @@
FROM python:alpine
WORKDIR /app
RUN apk add --no-cache gcc musl-dev python3-dev
COPY requirements.txt /app
RUN pip install --upgrade pip && pip install -r requirements.txt
COPY . /app
RUN python configure.py
RUN python manage.py collectstatic --noinput
CMD python manage.py runserver 0.0.0.0:8000 --insecure
# TODO serve static files with nginx and remove --insecure
EXPOSE 8000

View file

@ -1,7 +1,7 @@
""" """
Django settings for backend project. Django settings for backend project.
Generated by 'django-admin startproject' using Django 4.1.7. Generated by 'django-admin startproject' using Django 4.2.2.
For more information on this file, see For more information on this file, see
https://docs.djangoproject.com/en/4.1/topics/settings/ https://docs.djangoproject.com/en/4.1/topics/settings/
@ -9,24 +9,28 @@ https://docs.djangoproject.com/en/4.1/topics/settings/
For the full list of settings and their values, see For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.1/ref/settings/ https://docs.djangoproject.com/en/4.1/ref/settings/
""" """
import os
import dotenv
from pathlib import Path from pathlib import Path
from django.contrib import staticfiles
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
dotenv.load_dotenv(BASE_DIR / '.env')
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = os.environ.get('SECRET_KEY', None)
SECRET_KEY = 'django-insecure-np^7ug@ag1r261shqver8i$np9x07w@8ejxkbzw8gghqk_cov=' if SECRET_KEY is None:
raise Exception('environment variable SECRET_KEY not set. try running `configure.py` or setting it manually')
# SECURITY WARNING: don't run with debug turned on in production! DEBUG = os.environ.get('DEBUG', 'False').lower() == 'true'
DEBUG = True
ALLOWED_HOSTS = []
if DEBUG:
print('DEBUG mode is enabled')
# Application definition # Application definition
@ -37,11 +41,33 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django_extensions',
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
'drf_yasg',
] ]
REST_FRAMEWORK = {
'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'api_key': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization'
}
},
'USE_SESSION_AUTH': False,
'JSON_EDITOR': True,
}
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
@ -49,13 +75,16 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')
CORS_ALLOW_ALL_ORIGINS = True
ROOT_URLCONF = 'backend.urls' ROOT_URLCONF = 'backend.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'] 'DIRS': [BASE_DIR / 'templates'],
,
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
@ -70,7 +99,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'backend.wsgi.application' WSGI_APPLICATION = 'backend.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases # https://docs.djangoproject.com/en/4.1/ref/settings/#databases
@ -100,7 +128,6 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
] ]
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/ # https://docs.djangoproject.com/en/4.1/topics/i18n/
@ -112,11 +139,16 @@ USE_I18N = True
USE_TZ = True USE_TZ = True
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.1/howto/static-files/ # https://docs.djangoproject.com/en/4.1/howto/static-files/
STATIC_URL = 'static/' STATIC_ROOT = 'staticfiles'
STATIC_URL = '/static/'
# Extra places for collectstatic to find static files.
STATICFILES_DIRS = (
BASE_DIR / 'static',
)
# Default primary key field type # Default primary key field type
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field

View file

@ -14,9 +14,21 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path, include
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
schema_view = get_schema_view(
openapi.Info(
title="Toolshed API",
default_version='v1',
description="API for all things …",
),
public=True,
permission_classes=[]
)
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('djangoadmin/', admin.site.urls),
path('/', path('docs/', schema_view.with_ui('swagger', cache_timeout=0), name='api-docs'),
] ]

89
backend/configure.py Executable file
View file

@ -0,0 +1,89 @@
#!/usr/bin/env python3
import os
import sys
import dotenv
def yesno(prompt, default=False):
if not sys.stdin.isatty():
return default
yes = {'yes', 'y', 'ye'}
no = {'no', 'n'}
if default:
yes.add('')
else:
no.add('')
hint = ' [Y/n] ' if default else ' [y/N] '
while True:
choice = input(prompt + hint).lower()
if choice in yes:
return True
elif choice in no:
return False
else:
print('Please respond with "yes" or "no"')
def configure():
if not os.path.exists('.env'):
if not yesno("the .env file does not exist, do you want to create it?", default=True):
print('Aborting')
exit(0)
if not os.path.exists('.env.dist'):
print('No .env.dist file found')
exit(1)
else:
from shutil import copyfile
copyfile('.env.dist', '.env')
env = dotenv.load_dotenv('.env')
if not env or not os.getenv('SECRET_KEY'):
from django.core.management.utils import get_random_secret_key
print('No SECRET_KEY found in .env file, generating one...')
with open('.env', 'a') as f:
f.write('\nSECRET_KEY=')
f.write(get_random_secret_key())
f.write('\n')
# TODO rename ALLOWED_HOSTS to something more self-explanatory
current_hosts = os.getenv('ALLOWED_HOSTS')
print('Current ALLOWED_HOSTS: {}'.format(current_hosts))
if yesno("Do you want to add ALLOWED_HOSTS?"):
hosts = input("Enter a comma-separated list of allowed hosts: ")
joined_hosts = current_hosts + ',' + hosts if current_hosts else hosts
dotenv.set_key('.env', 'ALLOWED_HOSTS', joined_hosts)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings")
import django
django.setup()
if not os.path.exists('db.sqlite3'):
if not yesno("No database found, do you want to create one?", default=True):
print('Aborting')
exit(0)
from django.core.management import call_command
call_command('migrate')
if yesno("Do you want to create a superuser?"):
from django.core.management import call_command
call_command('createsuperuser')
if not os.path.exists('static'):
if yesno("No static directory found, do you want to create one?", default=True):
os.mkdir('static')
call_command('collectstatic', '--no-input')
# if yesno("Do you want to load initial data?"):
# call_command('loaddata', 'initial_data.json')
if __name__ == '__main__':
configure()

View file

@ -1,14 +1,37 @@
asgiref==3.6.0 asgiref==3.7.2
certifi==2022.12.7 certifi==2023.5.7
cffi==1.15.1
charset-normalizer==3.1.0 charset-normalizer==3.1.0
Django==4.1.7 coreapi==2.3.3
django-bootstrap-icons==0.8.2 coreschema==0.0.4
django-bootstrap5==22.2 Django==4.2.2
django-filter==22.1 django-annoying==0.10.6
django-cors-headers==4.1.0
django-extensions==3.2.3
django-filter==23.2
django-jsonstore==0.5.1
django-soft-delete==0.9.21
djangorestframework==3.14.0 djangorestframework==3.14.0
drf-yasg==1.21.5
idna==3.4 idna==3.4
Markdown==3.4.1 inflection==0.5.1
pytz==2022.7.1 itypes==1.2.0
requests==2.28.2 Jinja2==3.1.2
sqlparse==0.4.3 Markdown==3.4.3
urllib3==1.26.14 markdown-include==0.8.1
MarkupSafe==2.1.3
openapi-codec==1.3.2
packaging==23.1
pycparser==2.21
PyNaCl==1.5.0
python-dotenv==1.0.0
pytz==2023.3
PyYAML==6.0
requests==2.31.0
ruamel.yaml==0.17.31
ruamel.yaml.clib==0.2.7
simplejson==3.19.1
six==1.16.0
sqlparse==0.4.4
uritemplate==4.1.1
urllib3==2.0.3

52
docs/index.md Normal file
View file

@ -0,0 +1,52 @@
# Toolshed Documentation
## Introduction
This is the documentation for the Toolshed project. It is a work in progress.
`#social` `#network` `#federation` `#decentralized` `#federated` `#socialnetwork` `#fediverse` `#community` `#hashtags`
## Getting Started
## Installation
``` bash
# TODO add installation instructions
# similar to development instructions just with more docker
# TODO add docker-compose.yml
```
## Development
``` bash
git clone https://github.com/gr4yj3d1/toolshed.git
```
or
``` bash
git clone https://git.neulandlabor.de/j3d1/toolshed.git
```
### Frontend
``` bash
cd toolshed/frontend
npm install
npm run dev
```
### Backend
``` bash
cd toolshed/backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py runserver 0.0.0.0:8000
```
### Docs
``` bash
cd toolshed/docs
mkdocs serve -a 0.0.0.0:8080
```

3
docs/toolshed.css Normal file
View file

@ -0,0 +1,3 @@
.wy-nav-content {
max-width: 1100px;
}

26
mkdocs.yml Normal file
View file

@ -0,0 +1,26 @@
site_name: Toolshed Documentation
theme:
name: readthedocs
locale: de
highlightjs: true
hljs_languages:
- cmake
- c
- cpp
- shell
- json
- bash
- yaml
- python
markdown_extensions:
- meta
- toc
- tables
- fenced_code
plugins:
- search
extra_javascript:
- https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.js
extra_css:
- toolshed.css