diff --git a/README.md b/README.md index 7f91e3e..854b54b 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,16 @@ # toolshed -## Development +## Installation / Development ``` bash git clone https://github.com/gr4yj3d1/toolshed.git ``` - or - ``` bash git clone https://git.neulandlabor.de/j3d1/toolshed.git ``` -all following development mode commands support auto-reloading and hot-reloading where applicable, they do not need to bw -restarted after changes. - -### Backend only +### Backend ``` bash cd toolshed/backend @@ -25,11 +20,9 @@ pip install -r requirements.txt python configure.py python manage.py runserver 0.0.0.0:8000 --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. -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 only +### Frontend ``` bash cd toolshed/frontend @@ -37,44 +30,14 @@ npm install npm run dev ``` -### Docs only +### Docs ``` bash cd toolshed/docs mkdocs serve ``` -### Full stack -``` bash -cd toolshed -docker-compose -f deploy/docker-compose.override.yml up --build -``` - -## Deployment - -### Requirements - -- python3 -- python3-pip -- python3-venv -- wget -- unzip -- nginx -- uwsgi - -### Installation - -* Get the latest release from -`https://git.neulandlabor.de/j3d1/toolshed/releases/download//toolshed.zip` or -`https://github.com/gr4yj3d1/toolshed/archive/refs/tags/.zip`. -* Unpack it to `/var/www` or wherever you want to install toolshed. -* Create a virtual environment and install the requirements. -* Then run the configuration script. -* Configure your webserver to serve the static files and proxy the requests to the backend. -* Configure your webserver to run the backend with uwsgi. - -for detailed instructions see [docs](/docs/deployment.md). ## CLI Client diff --git a/backend/configure.py b/backend/configure.py index e027805..ee26cac 100755 --- a/backend/configure.py +++ b/backend/configure.py @@ -8,41 +8,32 @@ import dotenv from django.db import transaction, IntegrityError -class CmdCtx: +def yesno(prompt, default=False): + if not sys.stdin.isatty(): + return default + yes = {'yes', 'y', 'ye'} + no = {'no', 'n'} - def __init__(self, args): - self.args = args + if default: + yes.add('') + else: + no.add('') - def yesno(self, prompt, default=False): - if not sys.stdin.isatty() or self.args.noninteractive: - return default - elif self.args.yes: + hint = ' [Y/n] ' if default else ' [y/N] ' + + while True: + choice = input(prompt + hint).lower() + if choice in yes: return True - elif self.args.no: + elif choice in no: return False - 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"') + print('Please respond with "yes" or "no"') -def configure(ctx): +def configure(): if not os.path.exists('.env'): - if not ctx.yesno("the .env file does not exist, do you want to create it?", default=True): + 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'): @@ -65,7 +56,7 @@ def configure(ctx): current_hosts = os.getenv('ALLOWED_HOSTS') print('Current ALLOWED_HOSTS: {}'.format(current_hosts)) - if ctx.yesno("Do you want to add ALLOWED_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) @@ -76,21 +67,20 @@ def configure(ctx): django.setup() if not os.path.exists('db.sqlite3'): - if not ctx.yesno("No database found, do you want to create one?", default=True): + 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 ctx.yesno("Do you want to create a superuser?"): + if yesno("Do you want to create a superuser?"): from django.core.management import call_command call_command('createsuperuser') call_command('collectstatic', '--no-input') - if ctx.yesno("Do you want to import all categories, properties and tags contained in this repository?", - default=True): + if yesno("Do you want to import all categories, properties and tags contained in this repository?", default=True): from hostadmin.serializers import CategorySerializer, PropertySerializer, TagSerializer from hostadmin.models import ImportedIdentifierSets from hashlib import sha256 @@ -206,7 +196,6 @@ def main(): parser = ArgumentParser(description='Toolshed Server Configuration') parser.add_argument('--yes', '-y', help='Answer yes to all questions', action='store_true') parser.add_argument('--no', '-n', help='Answer no to all questions', action='store_true') - parser.add_argument('--noninteractive', '-x', help="Run in noninteractive mode", action='store_true') parser.add_argument('cmd', help='Command', default='configure', nargs='?') args = parser.parse_args() @@ -214,10 +203,8 @@ def main(): print('Error: --yes and --no are mutually exclusive') exit(1) - ctx = CmdCtx(args) - if args.cmd == 'configure': - configure(ctx) + configure() elif args.cmd == 'reset': reset() elif args.cmd == 'testdata': diff --git a/backend/shared_data/base.json b/backend/shared_data/base.json new file mode 100644 index 0000000..d961bb8 --- /dev/null +++ b/backend/shared_data/base.json @@ -0,0 +1,59 @@ +{ + "categories": [ + { "name": "hardware"}, + { "name": "material"}, + { "name": "tools"} + ], + "properties": [ + { "name": "angle", "unit_symbol": "°", "unit_name": "degree", "unit_name_plural": "degrees" }, + { "name": "area", "unit_symbol": "m²", "unit_name": "square meter", "unit_name_plural": "square meters" }, + { "name": "current", "unit_symbol": "A", "unit_name": "ampere", "unit_name_plural": "amperes" }, + { "name": "diameter", "unit_symbol": "m", "unit_name": "meter", "unit_name_plural": "meters" }, + { "name": "energy", "unit_symbol": "J", "unit_name": "joule", "unit_name_plural": "joules" }, + { "name": "frequency", "unit_symbol": "Hz", "unit_name": "hertz", "unit_name_plural": "hertz" }, + { "name": "height", "unit_symbol": "m", "unit_name": "meter", "unit_name_plural": "meters" }, + { "name": "length", "unit_symbol": "m", "unit_name": "meter", "unit_name_plural": "meters" }, + { "name": "memory", "unit_symbol": "B", "unit_name": "byte", "unit_name_plural": "bytes", "base2_prefix": true }, + { "name": "power", "unit_symbol": "W", "unit_name": "watt", "unit_name_plural": "watts" }, + { "name": "price", "unit_symbol": "€", "unit_name": "euro", "unit_name_plural": "euros" }, + { "name": "speed", "unit_symbol": "m/s", "unit_name": "meter per second", "unit_name_plural": "meters per second" }, + { "name": "temperature", "unit_symbol": "°C", "unit_name": "degree Celsius", "unit_name_plural": "degrees Celsius" }, + { "name": "time", "unit_symbol": "s", "unit_name": "second", "unit_name_plural": "seconds" }, + { "name": "voltage", "unit_symbol": "V", "unit_name": "volt", "unit_name_plural": "volts" }, + { "name": "volume", "unit_symbol": "l", "unit_name": "liter", "unit_name_plural": "liters" }, + { "name": "weight", "unit_symbol": "g", "unit_name": "gram", "unit_name_plural": "grams" }, + { "name": "width", "unit_symbol": "m", "unit_name": "meter", "unit_name_plural": "meters" } + ], + "tags": [ + {"name": "bolt", "category": "hardware"}, + {"name": "chisel", "category": "tools"}, + {"name": "clamp", "category": "tools"}, + {"name": "drill", "category": "tools"}, + {"name": "ear plugs", "category": "tools"}, + {"name": "extension cord", "category": "tools"}, + {"name": "flashlight", "category": "tools"}, + {"name": "gloves", "category": "tools"}, + {"name": "goggles", "category": "tools"}, + {"name": "hammer", "category": "tools"}, + {"name": "level", "category": "tools"}, + {"name": "mask", "category": "tools"}, + {"name": "nail", "category": "hardware"}, + {"name": "nut", "category": "hardware"}, + {"name": "paint brush", "category": "tools"}, + {"name": "paint roller", "category": "tools"}, + {"name": "paint tray", "category": "tools"}, + {"name": "pliers", "category": "tools"}, + {"name": "power strip", "category": "tools"}, + {"name": "sander", "category": "tools"}, + {"name": "saw", "category": "tools"}, + {"name": "screw", "category": "hardware"}, + {"name": "screwdriver", "category": "tools"}, + {"name": "soldering iron", "category": "tools"}, + {"name": "stapler", "category": "tools"}, + {"name": "tape measure", "category": "tools"}, + {"name": "tool"}, + {"name": "vise", "category": "tools"}, + {"name": "washer", "category": "hardware"}, + {"name": "wrench", "category": "tools"} + ] +} \ No newline at end of file diff --git a/backend/shared_data/ee.json b/backend/shared_data/ee.json new file mode 100644 index 0000000..3b0e73e --- /dev/null +++ b/backend/shared_data/ee.json @@ -0,0 +1,92 @@ +{ + "depends": [ "git:base" ], + "categories": [ + { "name": "electronics", "parent": "hardware"}, + { "name": "electronics", "parent": "tools"}, + { "name": "bus", "parent": "hardware/electronics"}, + { "name": "mcu", "parent": "hardware/electronics"}, + { "name": "wireless", "parent": "hardware/electronics"} + ], + "tags": [ + {"name": "smt", "category": "hardware/electronics"}, + {"name": "tht", "category": "hardware/electronics"}, + {"name": "adapter", "category": "hardware/electronics"}, + {"name": "amperemeter", "category": "tools/electronics"}, + {"name": "cable", "category": "hardware/electronics"}, + {"name": "camera", "category": "tools/electronics"}, + {"name": "connector", "category": "hardware/electronics"}, + {"name": "flux", "category": "hardware/electronics"}, + {"name": "microscope", "category": "tools/electronics"}, + {"name": "multimeter", "category": "tools/electronics"}, + {"name": "oscilloscope", "category": "tools/electronics"}, + {"name": "power supply", "category": "hardware/electronics"}, + {"name": "solder", "category": "hardware/electronics"}, + {"name": "soldering", "category": "tools/electronics"}, + {"name": "voltmeter", "category": "tools/electronics"}, + {"name": "charger", "category": "hardware/electronics"}, + {"name": "actuator", "category": "hardware/electronics"}, + {"name": "battery", "category": "hardware/electronics"}, + {"name": "capacitor", "category": "hardware/electronics"}, + {"name": "diode", "category": "hardware/electronics"}, + {"name": "display", "category": "hardware/electronics"}, + {"name": "encoder", "category": "hardware/electronics"}, + {"name": "fuse", "category": "hardware/electronics"}, + {"name": "inductor", "category": "hardware/electronics"}, + {"name": "inverter", "category": "hardware/electronics"}, + {"name": "lcd", "category": "hardware/electronics"}, + {"name": "led", "category": "hardware/electronics"}, + {"name": "motor", "category": "hardware/electronics"}, + {"name": "oscillator", "category": "hardware/electronics"}, + {"name": "potentiometer", "category": "hardware/electronics"}, + {"name": "relay", "category": "hardware/electronics"}, + {"name": "resistor", "category": "hardware/electronics"}, + {"name": "sensor", "category": "hardware/electronics"}, + {"name": "servo", "category": "hardware/electronics"}, + {"name": "stepper", "category": "hardware/electronics"}, + {"name": "switch", "category": "hardware/electronics"}, + {"name": "thermistor", "category": "hardware/electronics"}, + {"name": "thermocouple", "category": "hardware/electronics"}, + {"name": "transformer", "category": "hardware/electronics"}, + {"name": "transistor", "category": "hardware/electronics"}, + {"name": "bluetooth", "category": "hardware/electronics/wireless"}, + {"name": "gps", "category": "hardware/electronics/wireless"}, + {"name": "gsm", "category": "hardware/electronics/wireless"}, + {"name": "lora", "category": "hardware/electronics/wireless"}, + {"name": "nfc", "category": "hardware/electronics/wireless"}, + {"name": "rfid", "category": "hardware/electronics/wireless"}, + {"name": "thread", "category": "hardware/electronics/wireless"}, + {"name": "wifi", "category": "hardware/electronics/wireless"}, + {"name": "zigbee", "category": "hardware/electronics/wireless"}, + {"name": "zwave", "category": "hardware/electronics/wireless"}, + {"name": "can", "category": "hardware/electronics/bus"}, + {"name": "ethernet", "category": "hardware/electronics/bus"}, + {"name": "i2c", "category": "hardware/electronics/bus"}, + {"name": "lin", "category": "hardware/electronics/bus"}, + {"name": "spi", "category": "hardware/electronics/bus"}, + {"name": "uart", "category": "hardware/electronics/bus"}, + {"name": "usb", "category": "hardware/electronics/bus"}, + {"name": "arduino", "category": "hardware/electronics/mcu"}, + {"name": "atmega", "category": "hardware/electronics/mcu"}, + {"name": "attiny", "category": "hardware/electronics/mcu"}, + {"name": "beaglebone", "category": "hardware/electronics/mcu"}, + {"name": "esp32", "category": "hardware/electronics/mcu"}, + {"name": "esp8266", "category": "hardware/electronics/mcu"}, + {"name": "nucleo", "category": "hardware/electronics/mcu"}, + {"name": "raspberry", "category": "hardware/electronics/mcu"}, + {"name": "stm32", "category": "hardware/electronics/mcu"}, + {"name": "stm8", "category": "hardware/electronics/mcu"}, + {"name": "6502", "category": "hardware/electronics/mcu"}, + {"name": "8051", "category": "hardware/electronics/mcu"}, + {"name": "arm", "category": "hardware/electronics/mcu"}, + {"name": "avr", "category": "hardware/electronics/mcu"}, + {"name": "cortex", "category": "hardware/electronics/mcu"}, + {"name": "m68k", "category": "hardware/electronics/mcu"}, + {"name": "mips", "category": "hardware/electronics/mcu"}, + {"name": "pic", "category": "hardware/electronics/mcu"}, + {"name": "powerpc", "category": "hardware/electronics/mcu"}, + {"name": "risc-v", "category": "hardware/electronics/mcu"}, + {"name": "x86", "category": "hardware/electronics/mcu"}, + {"name": "xtensa", "category": "hardware/electronics/mcu"}, + {"name": "z80", "category": "hardware/electronics/mcu"} + ] +} \ No newline at end of file diff --git a/backend/shared_data/ee_packages.json b/backend/shared_data/ee_packages.json new file mode 100644 index 0000000..5701202 --- /dev/null +++ b/backend/shared_data/ee_packages.json @@ -0,0 +1,99 @@ +{ + "depends": [ "git:ee" ], + "categories": [ + { "name": "packages", "parent": "hardware/electronics", "description": "Component Packages"} + ], + "tags": [ + {"name": "0201", "category": "hardware/electronics/packages"}, + {"name": "0402", "category": "hardware/electronics/packages"}, + {"name": "0603", "category": "hardware/electronics/packages"}, + {"name": "0805", "category": "hardware/electronics/packages"}, + {"name": "1206", "category": "hardware/electronics/packages"}, + {"name": "1210", "category": "hardware/electronics/packages"}, + {"name": "1812", "category": "hardware/electronics/packages"}, + {"name": "MELF", "category": "hardware/electronics/packages"}, + {"name": "MiniMELF", "category": "hardware/electronics/packages"}, + {"name": "MicroMELF", "category": "hardware/electronics/packages"}, + {"name": "SMC", "category": "hardware/electronics/packages"}, + {"name": "SMB", "category": "hardware/electronics/packages"}, + {"name": "SMA", "category": "hardware/electronics/packages"}, + {"name": "GF1", "category": "hardware/electronics/packages"}, + {"name": "DIP-x", "category": "hardware/electronics/packages", + "description": "Dual Inline Package, needs pin count property to be unambiguous"}, + {"name": "SOD", "category": "hardware/electronics/packages"}, + {"name": "SOD-123", "category": "hardware/electronics/packages"}, + {"name": "SOD-323", "category": "hardware/electronics/packages"}, + {"name": "SOD-523", "category": "hardware/electronics/packages"}, + {"name": "SOD-923", "category": "hardware/electronics/packages"}, + {"name": "SOT", "category": "hardware/electronics/packages"}, + {"name": "SOT23", "category": "hardware/electronics/packages"}, + {"name": "SOT23-3", "category": "hardware/electronics/packages"}, + {"name": "SOT323", "category": "hardware/electronics/packages"}, + {"name": "SOT416", "category": "hardware/electronics/packages"}, + {"name": "SOT23-5", "category": "hardware/electronics/packages"}, + {"name": "SOT353", "category": "hardware/electronics/packages"}, + {"name": "SOT553", "category": "hardware/electronics/packages"}, + {"name": "SOT23-6", "category": "hardware/electronics/packages"}, + {"name": "SOT363", "category": "hardware/electronics/packages"}, + {"name": "SOT563", "category": "hardware/electronics/packages"}, + {"name": "SOT23-8", "category": "hardware/electronics/packages"}, + {"name": "SOT54", "category": "hardware/electronics/packages", "alias":"TO-92"}, + {"name": "SOT143", "category": "hardware/electronics/packages"}, + {"name": "SOT343", "category": "hardware/electronics/packages"}, + {"name": "SOT490", "category": "hardware/electronics/packages"}, + {"name": "SOT89-3", "category": "hardware/electronics/packages"}, + {"name": "SOT89-5", "category": "hardware/electronics/packages"}, + {"name": "SOT223-4", "category": "hardware/electronics/packages"}, + {"name": "SOT223-5", "category": "hardware/electronics/packages"}, + {"name": "SOT223-8", "category": "hardware/electronics/packages"}, + {"name": "TO-3", "category": "hardware/electronics/packages"}, + {"name": "TO-5", "category": "hardware/electronics/packages"}, + {"name": "TO-8", "category": "hardware/electronics/packages"}, + {"name": "TO-18", "category": "hardware/electronics/packages"}, + {"name": "TO-39", "category": "hardware/electronics/packages"}, + {"name": "TO-66", "category": "hardware/electronics/packages"}, + {"name": "TO-92", "category": "hardware/electronics/packages"}, + {"name": "TO-220", "category": "hardware/electronics/packages"}, + {"name": "TO-247", "category": "hardware/electronics/packages"}, + {"name": "TO-251", "category": "hardware/electronics/packages"}, + {"name": "TO-252", "category": "hardware/electronics/packages"}, + {"name": "TO-263", "category": "hardware/electronics/packages"}, + {"name": "TO-264", "category": "hardware/electronics/packages"}, + {"name": "TO-268", "category": "hardware/electronics/packages"}, + {"name": "TO-269", "category": "hardware/electronics/packages"}, + {"name": "SOIC-x", "category": "hardware/electronics/packages", "alias": "SO-x", + "description": "Small Outline Integrated Circuit, needs pin count property to be unambiguous"}, + {"name": "SOJ-x", "category": "hardware/electronics/packages", + "description": "Small Outline J-leaded, needs pin count property to be unambiguous"}, + {"name": "MSOP-x", "category": "hardware/electronics/packages", + "description": "Mini Small Outline Package, needs pin count property to be unambiguous"}, + {"name": "SSOP-x", "category": "hardware/electronics/packages", + "description": "Shrink Small Outline Package, needs pin count property to be unambiguous"}, + {"name": "SOP-x", "category": "hardware/electronics/packages", + "description": "Small Outline Package, needs pin count property to be unambiguous"}, + {"name": "TSOP-x", "category": "hardware/electronics/packages", + "description": "Thin Small Outline Package, needs pin count property to be unambiguous"}, + {"name": "TSSOP-x", "category": "hardware/electronics/packages", + "description": "Thin Shrink Small Outline Package, needs pin count property to be unambiguous"}, + {"name": "QFP-x", "category": "hardware/electronics/packages", + "description": "Quad Flat Package, needs pin count property to be unambiguous"}, + {"name": "TQFP-x", "category": "hardware/electronics/packages", + "description": "Thin Quad Flat Package, needs pin count property to be unambiguous"}, + {"name": "LQFP-x", "category": "hardware/electronics/packages", + "description": "Low-profile Quad Flat Package, needs pin count property to be unambiguous"}, + {"name": "DFN-x", "category": "hardware/electronics/packages", + "description": "Dual Flat No-leaded, needs pin count property to be unambiguous"}, + {"name": "QFN-x", "category": "hardware/electronics/packages", + "description": "Quad Flat No-leaded, needs pin count property to be unambiguous"}, + {"name": "TQFN-x", "category": "hardware/electronics/packages", + "description": "Thin Quad Flat No-leaded, needs pin count property to be unambiguous"}, + {"name": "LQFN-x", "category": "hardware/electronics/packages", + "description": "Low-profile Quad Flat No-leaded, needs pin count property to be unambiguous"}, + {"name": "UQFN-x", "category": "hardware/electronics/packages", + "description": "Ultra-thin Quad Flat No-leaded, needs pin count property to be unambiguous"} + ], + "properties": [ + { "name": "pin count", "unit_symbol": "", "unit_name": "", "unit_name_plural": "" } + ], + "url": "https://en.wikipedia.org/wiki/List_of_integrated_circuit_packaging_types" +} \ No newline at end of file diff --git a/backend/shared_data/electrical.json b/backend/shared_data/electrical.json new file mode 100644 index 0000000..c0a6fac --- /dev/null +++ b/backend/shared_data/electrical.json @@ -0,0 +1,62 @@ +{ + "depends": ["git:base"], + "categories": [ + { "name": "electrical"}, + { "name": "connectors"}, + { "name": "power", "parent": "connectors" } + ], + "tags": [ + { "name": "braker", "category": "electrical" }, + { "name": "cable", "category": "electrical" }, + { "name": "connector", "category": "electrical" }, + { "name": "plug", "category": "connectors" }, + { "name": "socket", "category": "connectors" }, + { "name": "power", "category": "connectors" }, + { "name": "C1", "category": "connectors/power" }, + { "name": "C2", "category": "connectors/power" }, + { "name": "C3", "category": "connectors/power" }, + { "name": "C4", "category": "connectors/power" }, + { "name": "C5", "category": "connectors/power" }, + { "name": "C6", "category": "connectors/power" }, + { "name": "C7", "category": "connectors/power" }, + { "name": "C7P", "category": "connectors/power" }, + { "name": "C8", "category": "connectors/power" }, + { "name": "C8P", "category": "connectors/power" }, + { "name": "C9", "category": "connectors/power" }, + { "name": "C10", "category": "connectors/power" }, + { "name": "C11", "category": "connectors/power" }, + { "name": "C12", "category": "connectors/power" }, + { "name": "C13", "category": "connectors/power" }, + { "name": "C14", "category": "connectors/power" }, + { "name": "C15", "category": "connectors/power" }, + { "name": "C15A", "category": "connectors/power" }, + { "name": "C16", "category": "connectors/power" }, + { "name": "C16A", "category": "connectors/power" }, + { "name": "C17", "category": "connectors/power" }, + { "name": "C18", "category": "connectors/power" }, + { "name": "C19", "category": "connectors/power" }, + { "name": "C20", "category": "connectors/power" }, + { "name": "C21", "category": "connectors/power" }, + { "name": "C22", "category": "connectors/power" }, + { "name": "C23", "category": "connectors/power" }, + { "name": "C24", "category": "connectors/power" }, + { "name": "Type A", "category": "connectors/power", "description": "NEMA 1-15, U.S. 2 pin" }, + { "name": "Type B", "category": "connectors/power", "description": "NEMA 5-15, U.S. 3 pin" }, + { "name": "Type C", "category": "connectors/power", "description": "CEE 7/16, Europlug" }, + { "name": "Type D", "category": "connectors/power", "description": "BS 546, India 5A/15A" }, + { "name": "Type E", "category": "connectors/power", "description": "CEE 7/5, French 2 pin" }, + { "name": "Type F", "category": "connectors/power", "description": "CEE 7/4, Schuko" }, + { "name": "Type E/F", "category": "connectors/power", "description": "CEE 7/7, Schuko/French hybrid" }, + { "name": "Type G", "category": "connectors/power", "description": "BS 1363, U.K. 3 pin" }, + { "name": "Type H", "category": "connectors/power", "description": "SI 32 Israel"}, + { "name": "Type I", "category": "connectors/power", "description": "AS/NZS 3112, Australia 3 pin" }, + { "name": "Type J", "category": "connectors/power", "description": "SEV 1011, Swiss 3 pin" }, + { "name": "Type K", "category": "connectors/power", "description": "DS 60884-2-D1, Danish 3 pin" }, + { "name": "Type L", "category": "connectors/power", "description": "CEI 23-16/VII, Italian 3 pin" } + ], + "properties": [ + { "name": "max current", "description": "Current rating"}, + { "name": "max power", "description": "Power rating" }, + { "name": "grid frequency", "description": "Typical frequency" } + ] +} \ No newline at end of file diff --git a/backend/shared_data/it.json b/backend/shared_data/it.json new file mode 100644 index 0000000..56f6189 --- /dev/null +++ b/backend/shared_data/it.json @@ -0,0 +1,82 @@ +{ + "depends": [ "git:electrical" ], + "categories": [ + { "name": "pc", "description": "PC related" }, + { "name": "usb", "parent": "connectors" } + ], + "tags": [ + { "name": "case", "category": "pc" }, + { "name": "cooler", "category": "pc" }, + { "name": "cpu" }, + { "name": "drone" }, + { "name": "fan", "category": "pc" }, + { "name": "gpu" }, + { "name": "hdd" }, + { "name": "headset" }, + { "name": "hub" }, + { "name": "iot" }, + { "name": "keyboard" }, + { "name": "laptop" }, + { "name": "memory" }, + { "name": "microphone" }, + { "name": "monitor" }, + { "name": "motherboard" }, + { "name": "mouse" }, + { "name": "pc"}, + { "name": "power supply" }, + { "name": "printer" }, + { "name": "router" }, + { "name": "scanner" }, + { "name": "server" }, + { "name": "smartphone" }, + { "name": "smartwatch" }, + { "name": "speaker" }, + { "name": "ssd" }, + { "name": "switch" }, + { "name": "tablet" }, + { "name": "watercooling" , "category": "pc"}, + { "name": "webcam" }, + { "name": "workstation" }, + { "name": "USB", "category": "connectors" }, + { "name": "Type A", "category": "connectors/usb" }, + { "name": "Type B", "category": "connectors/usb" }, + { "name": "Type C", "category": "connectors/usb" }, + { "name": "Micro", "category": "connectors/usb" }, + { "name": "Mini", "category": "connectors/usb" }, + { "name": "USB 2.0", "category": "connectors/usb" }, + { "name": "USB 3.0", "category": "connectors/usb" }, + { "name": "USB 3.1", "category": "connectors/usb" }, + { "name": "USB 3.2", "category": "connectors/usb" }, + { "name": "OTG", "category": "connectors/usb" }, + { "name": "thunderbolt", "category": "connectors/usb" }, + { "name": "24pin", "category": "connectors" }, + { "name": "8pin", "category": "connectors" }, + { "name": "atx", "category": "connectors" }, + { "name": "chinch", "category": "connectors" }, + { "name": "displayport", "category": "connectors" }, + { "name": "dvi", "category": "connectors" }, + { "name": "eps", "category": "connectors" }, + { "name": "floppy", "category": "connectors" }, + { "name": "hdmi", "category": "connectors" }, + { "name": "ide", "category": "connectors" }, + { "name": "jack", "category": "connectors" }, + { "name": "m.2", "category": "connectors" }, + { "name": "molex", "category": "connectors" }, + { "name": "p4", "category": "connectors" }, + { "name": "pcie", "category": "connectors" }, + { "name": "qsfp", "category": "connectors" }, + { "name": "qsfp+", "category": "connectors" }, + { "name": "qsfp28", "category": "connectors" }, + { "name": "rj11", "category": "connectors" }, + { "name": "rj45", "category": "connectors" }, + { "name": "sas", "category": "connectors" }, + { "name": "sata", "category": "connectors" }, + { "name": "scsi", "category": "connectors" }, + { "name": "sfp", "category": "connectors" }, + { "name": "sfp+", "category": "connectors" }, + { "name": "sfp28", "category": "connectors" }, + { "name": "toslink", "category": "connectors" }, + { "name": "vga", "category": "connectors" }, + { "name": "xlr", "category": "connectors" } + ] +} \ No newline at end of file diff --git a/backend/shared_data/screws.json b/backend/shared_data/screws.json new file mode 100644 index 0000000..20c8b93 --- /dev/null +++ b/backend/shared_data/screws.json @@ -0,0 +1,30 @@ +{ + "depends": [ "git:base" ], + "categories": [ + { "name": "screws", "parent": "hardware"} + ], + "tags": [ + {"name": "m1", "category": "screws"}, + {"name": "m2", "category": "screws"}, + {"name": "m2.5", "category": "screws"}, + {"name": "m3", "category": "screws"}, + {"name": "m4", "category": "screws"}, + {"name": "m5", "category": "screws"}, + {"name": "m6", "category": "screws"}, + {"name": "m8", "category": "screws"}, + {"name": "m10", "category": "screws"}, + {"name": "m12", "category": "screws"}, + {"name": "m16", "category": "screws"}, + {"name": "torx", "category": "screws"}, + {"name": "hex", "category": "screws"}, + {"name": "phillips", "category": "screws"}, + {"name": "pozidriv", "category": "screws"}, + {"name": "slotted", "category": "screws"}, + {"name": "socket", "category": "screws"}, + {"name": "flat", "category": "screws"}, + {"name": "pan", "category": "screws"}, + {"name": "button", "category": "screws"}, + {"name": "countersunk", "category": "screws"}, + {"name": "round", "category": "screws"} + ] +} \ No newline at end of file diff --git a/backend/shared_data/tools.json b/backend/shared_data/tools.json new file mode 100644 index 0000000..d4c4f88 --- /dev/null +++ b/backend/shared_data/tools.json @@ -0,0 +1,68 @@ +{ + "depends": ["git:base"], + "categories": [ + { "name": "powertools", "parent": "tools" } + ], + "tags": [ + { "name": "3d printer"}, + { "name": "air compressor", "category": "powertools"}, + { "name": "air filter"}, + { "name": "automotive", "category": "tools" }, + { "name": "bandsaw"}, + { "name": "belt sander"}, + { "name": "bench grinder"}, + { "name": "circular saw", "category": "powertools"}, + { "name": "concrete", "category": "tools" }, + { "name": "construction", "category": "tools" }, + { "name": "corded", "category": "powertools" }, + { "name": "cordless", "category": "powertools" }, + { "name": "disc sander"}, + { "name": "drill press"}, + { "name": "drywall", "category": "tools" }, + { "name": "dust collector"}, + { "name": "dust mask"}, + { "name": "ear protection"}, + { "name": "electrical", "category": "tools" }, + { "name": "eye protection"}, + { "name": "fire extinguisher"}, + { "name": "first aid"}, + { "name": "generator", "category": "powertools"}, + { "name": "glue gun", "category": "powertools"}, + { "name": "grinder", "category": "powertools"}, + { "name": "hammer drill", "category": "powertools"}, + { "name": "handheld", "category": "tools" }, + { "name": "heat gun", "category": "powertools"}, + { "name": "impact driver", "category": "powertools"}, + { "name": "jigsaw"}, + { "name": "jointer"}, + { "name": "ladder"}, + { "name": "laser cutter"}, + { "name": "lathe"}, + { "name": "masonry", "category": "tools" }, + { "name": "mechanical", "category": "tools" }, + { "name": "metalworking", "category": "tools" }, + { "name": "mill"}, + { "name": "multitool"}, + { "name": "nailgun", "category": "powertools"}, + { "name": "oscillating tool", "category": "powertools"}, + { "name": "painting", "category": "tools" }, + { "name": "planer"}, + { "name": "plasma cutter"}, + { "name": "plumbing", "category": "tools" }, + { "name": "powerplane", "category": "powertools"}, + { "name": "pressure washer", "category": "powertools"}, + { "name": "roofing", "category": "tools" }, + { "name": "router table"}, + { "name": "router", "category": "powertools"}, + { "name": "sawhorse"}, + { "name": "sawzall", "category": "powertools"}, + { "name": "screwgun", "category": "powertools"}, + { "name": "stationary", "category": "tools" }, + { "name": "tablesaw"}, + { "name": "tile", "category": "tools" }, + { "name": "welder", "category": "powertools"}, + { "name": "woodworking", "category": "tools" }, + { "name": "workbench"}, + { "name": "worklight"} + ] +} diff --git a/docs/deployment.md b/docs/deployment.md deleted file mode 100644 index 9262ebe..0000000 --- a/docs/deployment.md +++ /dev/null @@ -1,102 +0,0 @@ -# Deployment - -## Native - -### Requirements - -- python3 -- python3-pip -- python3-venv -- wget -- unzip -- nginx -- uwsgi -- certbot - -### Installation - -Get the latest release: - -``` bash -cd /var/www # or wherever you want to install toolshed -wget https://git.neulandlabor.de/j3d1/toolshed/releases/download//toolshed.zip -``` -or from github: -``` bash -cd /var/www # or wherever you want to install toolshed -wget https://github.com/gr4yj3d1/toolshed/archive/refs/tags/.zip -O toolshed.zip -``` - -Extract and configure the backend: - -``` bash -unzip toolshed.zip -cd toolshed/backend -python3 -m venv venv -source venv/bin/activate -pip install -r requirements.txt -python configure.py -``` - -Configure uWSGI to serve the backend locally: - -``` bash -cd /var/www/toolshed/backend -cp toolshed.ini /etc/uwsgi/apps-available/ -ln -s /etc/uwsgi/apps-available/toolshed.ini /etc/uwsgi/apps-enabled/ -systemctl restart uwsgi -``` - -Configure nginx to serve the static files and proxy the requests to the backend: - -``` bash -cd /var/www/toolshed/backend -cp toolshed.nginx /etc/nginx/sites-available/toolshed -ln -s /etc/nginx/sites-available/toolsheed /etc/nginx/sites-enabled/ -systemctl restart nginx -``` - -Configure certbot to get a certificate for the domain: - -``` bash -certbot --nginx -d -``` - -### Update - -``` bash -cd /var/www -wget https://git.neulandlabor.de/j3d1/toolshed/releases/download//toolshed.zip -unzip toolshed.zip -cd toolshed/backend -source venv/bin/activate -pip install -r requirements.txt -python configure.py -systemctl restart uwsgi -``` - -## Docker - -### Requirements - -- docker -- docker-compose -- git - -### Installation - -``` bash -git clone https://git.neulandlabor.de/j3d1/toolshed.git -# or -git clone https://github.com/gr4yj3d1/toolshed.git -cd toolshed -docker-compose -f deploy/docker-compose.prod.yml up -d --build -``` - -### Update - -``` bash -toolshed -git pull -docker-compose -f deploy/docker-compose.prod.yml up -d --build -``` \ No newline at end of file diff --git a/docs/development.md b/docs/development.md deleted file mode 100644 index 3fcd229..0000000 --- a/docs/development.md +++ /dev/null @@ -1,105 +0,0 @@ -# Development - -``` bash -git clone https://github.com/gr4yj3d1/toolshed.git -``` - -or - -``` bash -git clone https://git.neulandlabor.de/j3d1/toolshed.git -``` - -## Native - -To a certain extent, the frontend and backend can be developed independently. The frontend is a Vue.js project and the -backend is a DRF (Django-Rest-Framework) project. If you want to develop the frontend, you can do so without the backend -and vice -versa. However, especially for the frontend, it is recommended to use the backend as well, as the frontend does not have -a lot of 'offline' functionality. -If you want to run the fullstack application, it is recommended to use the [docker-compose](#docker) method. - -### Frontend - -install `node.js` and `npm` - -on Debian* for example: `sudo apt install npm` - -``` bash -cd toolshed/frontend -npm install -npm run dev -``` - -### Backend - -Install `python3`, `pip` and `virtualenv` - -on Debian* for example: `sudo apt install python3 python3-pip python3-venv` - -Prepare backend environment - -``` bash -cd toolshed/backend -python -m venv venv -source venv/bin/activate -pip install -r requirements.txt -``` - -Run the test suite: - -``` bash -python manage.py test -``` - -optionally with coverage: - -``` bash -coverage run manage.py test -coverage report -``` - -Start the backend in development mode: - -``` bash -python manage.py migrate -cp .env.dist .env -echo "DEBUG = True" >> .env -python manage.py runserver 0.0.0.0:8000 -``` - -provides the api docs at `http://localhost:8000/docs/` - -### Docs (Wiki) - -Install `mkdocs` - -on Debian* for example: `sudo apt install mkdocs` - -Start the docs server: - -``` bash -cd toolshed/docs -mkdocs serve -a 0.0.0.0:8080 -``` - -## Docker - -### Fullstack - -Install `docker` and `docker-compose` - -on Debian* for example: `sudo apt install docker.io docker-compose` - -Start the fullstack application: - -``` bash -docker-compose -f deploy/docker-compose.override.yml up --build -``` - -This will start an instance of the frontend and wiki, a limited DoH (DNS over HTTPS) server and **two** instances of the backend. -The two backend instances are set up to use the domains `a.localhost` and `b.localhost`, the local DoH -server is used to direct the frontend to the correct backend instance. -The frontend is configured to act as if it was served from the domain `a.localhost`. -Access the frontend at `http://localhost:8080/`, backend at `http://localhost:8080/api/`, api docs -at `http://localhost:8080/docs/` and the wiki at `http://localhost:8080/wiki/`. \ No newline at end of file diff --git a/docs/federation.md b/docs/federation.md deleted file mode 100644 index d242677..0000000 --- a/docs/federation.md +++ /dev/null @@ -1,23 +0,0 @@ -# Federation - -This section will cover how federation works in Toolshed. - -## What is Federation? - -Since user of Toolshed you can search and interact the inventory of all their 'friends' that are potentially on -different servers there is a need for a way to communicate between servers. We don't want to rely on a central server that -stores all the data and we don't want to have a central server that handles all the communication between servers. This -is where federation comes in. Toolshed uses a protocol that can not only exchange data with the server where the user -is registered but also with the servers where their friends are registered. - -## How does it work? - -Any user can register on any server and creates a personal key pair. The public key is stored on the server and the private -key is stored on the client. The private key is used to sign all requests to the server and the public key is used to -verify the signature. Once a user has registered on a server they can send friend requests to other users containing -their public key. If the other user accepts the friend request, the server stores the public key of the friend and -uses it to verify access to the friend's inventory. While accepting a friend request the user also automatically sends -their own public key to the friend's server. This way both users can access each other's inventory. - -The protocol is based on a simple HTTPS API exchanging JSON data that is signed with the user's private key. By default -Toolshed servers provide a documentation of the API at [/docs/api](/docs/api). \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 95588dd..0ba4a97 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,8 +6,47 @@ 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 - - [Deploying Toolshed](deployment.md) - - [Development Setup](development.md) - - [About Federation](federation.md) +## 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 +``` diff --git a/frontend/src/dns.js b/frontend/src/dns.js deleted file mode 100644 index f07ea22..0000000 --- a/frontend/src/dns.js +++ /dev/null @@ -1,50 +0,0 @@ -import {query} from 'dns-query'; - -function get_prefered_server() { - try { - const servers = JSON.parse(localStorage.getItem('dns-servers')); - if (servers && servers.length > 0) { - return servers; - } - } catch (e) { - console.error(e); - } - const request = new XMLHttpRequest(); - request.open('GET', '/local/dns', false); - request.send(null); - if (request.status === 200) { - const servers = JSON.parse(request.responseText); - if (servers && servers.length > 0) { - return servers; - } - } - return ['1.1.1.1', '8.8.8.8']; -} - -class FallBackResolver { - constructor() { - this._servers = get_prefered_server(); - this._cache = JSON.parse(localStorage.getItem('dns-cache')) || {}; - } - - async query(domain, type) { - const key = domain + ':' + type; - if (key in this._cache && this._cache[key].time > Date.now() - 1000 * 60 * 60) { - const age_seconds = Math.ceil(Date.now() / 1000 - this._cache[key].time / 1000); - return [this._cache[key].data]; - } - const result = await query( - {question: {type: type, name: domain}}, - { - endpoints: this._servers, - } - ) - if (result.answers.length === 0) throw new Error('No answer'); - const first = result.answers[0]; - this._cache[key] = {time: Date.now(), ...first}; // TODO hadle multiple answers - localStorage.setItem('dns-cache', JSON.stringify(this._cache)); - return [first.data]; - } -} - -export default FallBackResolver; \ No newline at end of file diff --git a/frontend/src/federation.js b/frontend/src/federation.js deleted file mode 100644 index 9afe33c..0000000 --- a/frontend/src/federation.js +++ /dev/null @@ -1,324 +0,0 @@ -class ServerSet { - constructor(servers, unreachable_neighbors) { - if (!servers || !Array.isArray(servers)) { - throw new Error('no servers') - } - if (!unreachable_neighbors || typeof unreachable_neighbors.queryUnreachable !== 'function' || typeof unreachable_neighbors.unreachable !== 'function') { - throw new Error('no unreachable_neighbors') - } - this.servers = [...new Set(servers)] // deduplicate - this.unreachable_neighbors = unreachable_neighbors; - } - - add(server) { - console.log('adding server', server) - if (!server || typeof server !== 'string') { - throw new Error('server must be a string') - } - if (server in this.servers) { - console.log('server already in set', server) - return - } - this.servers.push(server); - } - - async get(auth, target) { - if (!auth || typeof auth.buildAuthHeader !== 'function') { - throw new Error('no auth') - } - for (const server of this.servers) { - try { - if (this.unreachable_neighbors.queryUnreachable(server)) { - continue - } - const url = "https://" + server + target // TODO https - return await fetch(url, { - method: 'GET', - headers: { - ...auth.buildAuthHeader(url) - }, - credentials: 'omit' - }).catch(err => { - console.error('get from server failed', server, err) - this.unreachable_neighbors.unreachable(server) - } - ).then(response => response.json()) - } catch (e) { - console.error('get from server failed', server, e) - } - } - throw new Error('all servers failed') - } - - async post(auth, target, data) { - if (!auth || typeof auth.buildAuthHeader !== 'function') { - throw new Error('no auth') - } - for (const server of this.servers) { - try { - if (this.unreachable_neighbors.queryUnreachable(server)) { - continue - } - const url = "https://" + server + target // TODO https - return await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...auth.buildAuthHeader(url, data) - }, - credentials: 'omit', - body: JSON.stringify(data) - }).catch(err => { - console.error('post to server failed', server, err) - this.unreachable_neighbors.unreachable(server) - } - ).then(response => response.json()) - } catch (e) { - console.error('post to server failed', server, e) - } - } - throw new Error('all servers failed') - } - - async patch(auth, target, data) { - if (!auth || typeof auth.buildAuthHeader !== 'function') { - throw new Error('no auth') - } - for (const server of this.servers) { - try { - if (this.unreachable_neighbors.queryUnreachable(server)) { - continue - } - const url = "https://" + server + target // TODO https - return await fetch(url, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - ...auth.buildAuthHeader(url, data) - }, - credentials: 'omit', - body: JSON.stringify(data) - }).catch(err => { - console.error('patch to server failed', server, err) - this.unreachable_neighbors.unreachable(server) - } - ).then(response => response.json()) - } catch (e) { - console.error('patch to server failed', server, e) - } - } - throw new Error('all servers failed') - } - - async put(auth, target, data) { - if (!auth || typeof auth.buildAuthHeader !== 'function') { - throw new Error('no auth') - } - for (const server of this.servers) { - try { - if (this.unreachable_neighbors.queryUnreachable(server)) { - continue - } - const url = "https://" + server + target // TODO https - return await fetch(url, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - ...auth.buildAuthHeader(url, data) - }, - credentials: 'omit', - body: JSON.stringify(data) - }).catch(err => { - console.error('put to server failed', server, err) - this.unreachable_neighbors.unreachable(server) - } - ).then(response => response.json()) - } catch (e) { - console.error('put to server failed', server, e) - } - } - throw new Error('all servers failed') - } - - async delete(auth, target) { - if (!auth || typeof auth.buildAuthHeader !== 'function') { - throw new Error('no auth') - } - for (const server of this.servers) { - try { - if (this.unreachable_neighbors.queryUnreachable(server)) { - continue - } - const url = "https://" + server + target // TODO https - return await fetch(url, { - method: 'DELETE', - headers: { - ...auth.buildAuthHeader(url) - }, - credentials: 'omit' - }).catch(err => { - console.error('delete from server failed', server, err) - this.unreachable_neighbors.unreachable(server) - } - ) - } catch (e) { - console.error('delete from server failed', server, e) - } - } - throw new Error('all servers failed') - } - - async getRaw(auth, target) { - if (!auth || typeof auth.buildAuthHeader !== 'function') { - throw new Error('no auth') - } - for (const server of this.servers) { - try { - if (this.unreachable_neighbors.queryUnreachable(server)) { - continue - } - const url = "https://" + server + target // TODO https - return await fetch(url, { - method: 'GET', - headers: { - ...auth.buildAuthHeader(url) - }, - credentials: 'omit' - }).catch(err => { - console.error('get from server failed', server, err) - this.unreachable_neighbors.unreachable(server) - } - ) - } catch (e) { - console.error('get from server failed', server, e) - } - } - throw new Error('all servers failed') - } -} - -class ServerSetUnion { - constructor(serverSets) { - if (!serverSets || !Array.isArray(serverSets)) { - throw new Error('no serverSets') - } - this.serverSets = serverSets; - } - - add(serverset) { - if (!serverset || !(serverset instanceof ServerSet)) { - throw new Error('no serverset') - } - if (this.serverSets.find(s => serverset.servers.every(s2 => s.servers.includes(s2)))) { - console.warn('serverset already in union', serverset) - return - } - this.serverSets.push(serverset) - } - - async get(auth, target) { - try { - return await this.serverSets.reduce(async (acc, serverset) => { - return acc.then(async (acc) => { - return acc.concat(await serverset.get(auth, target)) - }) - }, Promise.resolve([])) - } catch (e) { - throw new Error('all servers failed') - } - } - - async post(auth, target, data) { - try { - return await this.serverSets.reduce(async (acc, serverset) => { - return acc.then(async (acc) => { - return acc.concat(await serverset.post(auth, target, data)) - }) - }, Promise.resolve([])) - } catch (e) { - throw new Error('all servers failed') - } - } - - async patch(auth, target, data) { - try { - return await this.serverSets.reduce(async (acc, serverset) => { - return acc.then(async (acc) => { - return acc.concat(await serverset.patch(auth, target, data)) - }) - }, Promise.resolve([])) - } catch (e) { - throw new Error('all servers failed') - } - } - - async put(auth, target, data) { - try { - return await this.serverSets.reduce(async (acc, serverset) => { - return acc.then(async (acc) => { - return acc.concat(await serverset.put(auth, target, data)) - }) - }, Promise.resolve([])) - } catch (e) { - throw new Error('all servers failed') - } - } - - async delete(auth, target) { - try { - return await this.serverSets.reduce(async (acc, serverset) => { - return acc.then(async (acc) => { - return acc.concat(await serverset.delete(auth, target)) - }) - }, Promise.resolve([])) - } catch (e) { - throw new Error('all servers failed') - } - } -} - - -class authMethod { - constructor(method, auth) { - this.method = method; - this.auth = auth; - } - - buildAuthHeader(url, data) { - return this.method(this.auth, {url, data}) - } - -} - -function createSignAuth(username, signKey) { - const context = {username, signKey} - if (!context.signKey || !context.username || typeof context.username !== 'string' - || !(context.signKey instanceof Uint8Array) || context.signKey.length !== 64) { - throw new Error('no signKey or username') - } - return new authMethod(({signKey, username}, {url, data}) => { - const json = JSON.stringify(data) - const signature = nacl.crypto_sign_detached(nacl.encode_utf8(url + (data ? json : "")), signKey) - return {'Authorization': 'Signature ' + username + ':' + nacl.to_hex(signature)} - }, context) -} - -function createTokenAuth(token) { - const context = {token} - if (!context.token) { - throw new Error('no token') - } - return new authMethod(({token}, {url, data}) => { - return {'Authorization': 'Token ' + token} - }, context) -} - -function createNullAuth() { - return new authMethod(() => { - return {} - }, {}) -} - -export {ServerSet, ServerSetUnion, createSignAuth, createTokenAuth, createNullAuth}; - - diff --git a/frontend/src/main.js b/frontend/src/main.js index 2277ac2..66ab5c7 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -5,11 +5,10 @@ import App from './App.vue' import './scss/toolshed.scss' import router from './router' -import store from './store'; import _nacl from 'js-nacl'; -const app = createApp(App).use(store).use(BootstrapIconsPlugin); +const app = createApp(App).use(BootstrapIconsPlugin); _nacl.instantiate((nacl) => { window.nacl = nacl diff --git a/frontend/src/neigbors.js b/frontend/src/neigbors.js deleted file mode 100644 index bbdfc06..0000000 --- a/frontend/src/neigbors.js +++ /dev/null @@ -1,48 +0,0 @@ -class NeighborsCache { - constructor() { - //this._max_age = 1000 * 60 * 60; // 1 hour - //this._max_age = 1000 * 60 * 5; // 5 minutes - this._max_age = 1000 * 15; // 15 seconds - this._cache = JSON.parse(localStorage.getItem('neighbor-cache')) || {}; - } - - reachable(domain) { - console.log('reachable neighbor ' + domain) - if (domain in this._cache) { - delete this._cache[domain]; - localStorage.setItem('neighbor-cache', JSON.stringify(this._cache)); - } - } - - unreachable(domain) { - console.log('unreachable neighbor ' + domain) - this._cache[domain] = {time: Date.now()}; - localStorage.setItem('neighbor-cache', JSON.stringify(this._cache)); - } - - queryUnreachable(domain) { - //return false if unreachable - if (domain in this._cache) { - if (this._cache[domain].time > Date.now() - this._max_age) { - console.log('skip unreachable neighbor ' + domain + ' ' + Math.ceil( - Date.now()/1000 - this._cache[domain].time/1000) + 's/' + Math.ceil(this._max_age/1000) + 's') - return true - } else { - delete this._cache[domain]; - localStorage.setItem('neighbor-cache', JSON.stringify(this._cache)); - } - } - return false; - } - - list() { - return Object.entries(this._cache).map(([domain, elem]) => { - return { - domain: domain, - time: elem.time - } - }) - } -} - -export default NeighborsCache; \ No newline at end of file diff --git a/frontend/src/store.js b/frontend/src/store.js deleted file mode 100644 index 6d0dd73..0000000 --- a/frontend/src/store.js +++ /dev/null @@ -1,132 +0,0 @@ -import {createStore} from 'vuex'; -import router from '@/router'; -import FallBackResolver from "@/dns"; -import NeighborsCache from "@/neigbors"; -import {createNullAuth, createSignAuth, createTokenAuth, ServerSet, ServerSetUnion} from "@/federation"; - - -export default createStore({ - state: { - local_loaded: false, - last_load: {}, - user: null, - token: null, - keypair: null, - remember: false, - home_servers: null, - resolver: new FallBackResolver(), - unreachable_neighbors: new NeighborsCache(), - }, - mutations: { - setUser(state, user) { - state.user = user; - if (state.remember) - localStorage.setItem('user', user); - }, - setToken(state, token) { - state.token = token; - if (state.remember) - localStorage.setItem('token', token); - }, - setKey(state, keypair) { - state.keypair = nacl.crypto_sign_keypair_from_seed(nacl.from_hex(keypair)) - if (state.remember) - localStorage.setItem('keypair', nacl.to_hex(state.keypair.signSk).slice(0, 64)) - }, - setRemember(state, remember) { - state.remember = remember; - if (!remember) { - localStorage.removeItem('user'); - localStorage.removeItem('token'); - localStorage.removeItem('keypair'); - } - localStorage.setItem('remember', remember); - }, - setHomeServers(state, home_servers) { - state.home_servers = home_servers; - }, - logout(state) { - state.user = null; - state.token = null; - state.keypair = null; - localStorage.removeItem('user'); - localStorage.removeItem('token'); - localStorage.removeItem('keypair'); - router.push('/login'); - }, - load_local(state) { - if (state.local_loaded) - return; - const remember = localStorage.getItem('remember'); - const user = localStorage.getItem('user'); - const token = localStorage.getItem('token'); - const keypair = localStorage.getItem('keypair'); - if (user && token) { - this.commit('setUser', user); - this.commit('setToken', token); - if (keypair) { - this.commit('setKey', keypair) - } - } - state.cache_loaded = true; - } - }, - actions: { - async login({commit, dispatch, state, getters}, {username, password, remember}) { - commit('setRemember', remember); - const data = await dispatch('lookupServer', {username}).then(servers => new ServerSet(servers, state.unreachable_neighbors)) - .then(set => set.post(getters.nullAuth, '/auth/token/', {username, password})) - if (data.token && data.key) { - commit('setToken', data.token); - commit('setUser', username); - commit('setKey', data.key); - const s = await dispatch('lookupServer', {username}).then(servers => new ServerSet(servers, state.unreachable_neighbors)) - commit('setHomeServers', s) - return true; - } else { - return false; - } - }, - async lookupServer({state}, {username}) { - const domain = username.split('@')[1] - const request = '_toolshed-server._tcp.' + domain + '.' - return await state.resolver.query(request, 'SRV').then( - (result) => result.map( - (answer) => answer.target + ':' + answer.port)) - }, - async getHomeServers({state, dispatch, commit}) { - if (state.home_servers) - return state.home_servers - const promise = dispatch('lookupServer', {username: state.user}).then(servers => new ServerSet(servers, state.unreachable_neighbors)) - commit('setHomeServers', promise) - return promise - }, - async getFriendServers({state, dispatch, commit}, {username}) { - return dispatch('lookupServer', {username}).then(servers => new ServerSet(servers, state.unreachable_neighbors)) - }, - }, - getters: { - isLoggedIn(state) { - if (!state.local_loaded) { - state.remember = localStorage.getItem('remember') === 'true' - state.user = localStorage.getItem('user') - state.token = localStorage.getItem('token') - const keypair = localStorage.getItem('keypair') - if (keypair) - state.keypair = nacl.crypto_sign_keypair_from_seed(nacl.from_hex(keypair)) - state.local_loaded = true - } - - return state.user !== null && state.token !== null; - }, - signAuth(state) { - return createSignAuth(state.user, state.keypair.signSk) - }, - tokenAuth(state) { - return createTokenAuth(state.token) - }, - nullAuth(state) { - return createNullAuth({}) - }, - } -}) \ No newline at end of file