commit 69bc5698bd7b9ad45d7fb06a0c4eb77c78a2bec4 Author: yaemiku Date: Mon Dec 16 16:35:12 2024 +0100 First version diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..57ee5b5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +frontend/node_modules \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..be4aee6 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +SECRET_KEY='' +DB_NAME='' +DB_USER='' +DB_PASSWORD='' +EMAIL_BACKEND="django.core.mail.backends.console.EmailBackend" +EMAIL_USE_TLS=... +EMAIL_HOST='' +EMAIL_HOST_USER='' +EMAIL_HOST_PASSWORD='' +EMAIL_PORT=... \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..3275517 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,16 @@ +:8080 { + encode zstd gzip + + handle_path /static/* { + file_server { + root /static + } + } + + # handle_path /api/* { + # reverse_proxy backend:8000 + # } + + reverse_proxy /admin/* backend:8000 + reverse_proxy frontend:3000 +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..729ed5f --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Jak uruchomić + +## Development + +```bash +docker compose -f docker-compose.yaml.prod up -d --build +docker compose exec -it backend python mange.py migrate +docker compose exec -it backend python mange.py createsuperuser +``` + +Po czym +- backend jest dostępny na porcie ```8000``` +- frontend jest dostępny na porcie ```3000``` + +## Produkcja + +```bash +docker compose -f docker-compose.yaml.prod up -d --build +docker compose -f docker-compose.yaml.prod exec -it backend python mange.py migrate +docker compose -f docker-compose.yaml.prod exec -it backend python mange.py createsuperuser +docker compose -f docker-compose.yaml.prod exec -it backend python manage.py collectstatic +``` + +Po czym aplikacja jest dostępna na porcie ```8080``` \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..dfd6b5f --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,29 @@ +FROM python:3.12-slim + +WORKDIR /usr/src/app + +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3-setuptools \ + python3-pip \ + python3-dev \ + python3-venv \ + netcat-openbsd && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN pip install --upgrade pip +COPY ./requirements.txt . +RUN pip install -r requirements.txt + +COPY ./entrypoint.sh . +RUN sed -i 's/\r$//g' /usr/src/app/entrypoint.sh +RUN chmod +x /usr/src/app/entrypoint.sh + +COPY . . + +EXPOSE 8000 + +ENTRYPOINT ["/usr/src/app/entrypoint.sh"] diff --git a/backend/Pipfile b/backend/Pipfile new file mode 100644 index 0000000..ac044b6 --- /dev/null +++ b/backend/Pipfile @@ -0,0 +1,23 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +django = "*" +psycopg2-binary = "*" +django-browser-reload = "*" +six = "*" +channels-redis = "*" +redis = "*" +django-redis = "*" +channels = {extras = ["daphne"], version = "*"} +twisted = {extras = ["tls", "http2"], version = "*"} +djangorestframework = "*" +markdown = "*" +django-filter = "*" + +[dev-packages] + +[requires] +python_version = "3.12" diff --git a/backend/Pipfile.lock b/backend/Pipfile.lock new file mode 100644 index 0000000..645ec86 --- /dev/null +++ b/backend/Pipfile.lock @@ -0,0 +1,587 @@ +{ + "_meta": { + "hash": { + "sha256": "e6a67e78f8300789081bdb71bcddebc4d6823353c42327f8b5267670e4df5b51" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.12" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "asgiref": { + "hashes": [ + "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", + "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590" + ], + "markers": "python_version >= '3.8'", + "version": "==3.8.1" + }, + "attrs": { + "hashes": [ + "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", + "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" + ], + "markers": "python_version >= '3.7'", + "version": "==24.2.0" + }, + "autobahn": { + "hashes": [ + "sha256:a2d71ef1b0cf780b6d11f8b205fd2c7749765e65795f2ea7d823796642ee92c9", + "sha256:c56a2abe7ac78abbfb778c02892d673a4de58fd004d088cd7ab297db25918e81" + ], + "markers": "python_version >= '3.9'", + "version": "==24.4.2" + }, + "automat": { + "hashes": [ + "sha256:b34227cf63f6325b8ad2399ede780675083e439b20c323d376373d8ee6306d88", + "sha256:bf029a7bc3da1e2c24da2343e7598affaa9f10bf0ab63ff808566ce90551e02a" + ], + "markers": "python_version >= '3.8'", + "version": "==24.8.1" + }, + "cffi": { + "hashes": [ + "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", + "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", + "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1", + "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", + "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", + "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", + "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", + "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", + "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", + "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", + "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc", + "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", + "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", + "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", + "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", + "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", + "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", + "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", + "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", + "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b", + "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", + "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", + "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c", + "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", + "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", + "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", + "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8", + "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1", + "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", + "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", + "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", + "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", + "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", + "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", + "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", + "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", + "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", + "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", + "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", + "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", + "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", + "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", + "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", + "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964", + "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", + "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", + "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", + "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", + "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", + "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", + "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", + "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", + "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", + "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", + "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", + "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", + "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", + "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9", + "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", + "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", + "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", + "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", + "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", + "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", + "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", + "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", + "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" + ], + "markers": "platform_python_implementation != 'PyPy'", + "version": "==1.17.1" + }, + "channels": { + "extras": [ + "daphne" + ], + "hashes": [ + "sha256:6b75bc8d6888fb7236e7e7bf1948520b72d296ad08216a242fc56b1db0ffde1a", + "sha256:d9e707487431ba5dbce9af982970dab3b0efd786580fadb99e45dca5e39fdd59" + ], + "markers": "python_version >= '3.8'", + "version": "==4.2.0" + }, + "channels-redis": { + "hashes": [ + "sha256:2ca33105b3a04b5a327a9c47dd762b546f30b76a0cd3f3f593a23d91d346b6f4", + "sha256:8375e81493e684792efe6e6eca60ef3d7782ef76c6664057d2e5c31e80d636dd" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==4.2.1" + }, + "constantly": { + "hashes": [ + "sha256:3fd9b4d1c3dc1ec9757f3c52aef7e53ad9323dbe39f51dfd4c43853b68dfa3f9", + "sha256:aa92b70a33e2ac0bb33cd745eb61776594dc48764b06c35e0efd050b7f1c7cbd" + ], + "markers": "python_version >= '3.8'", + "version": "==23.10.4" + }, + "cryptography": { + "hashes": [ + "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", + "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", + "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", + "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", + "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", + "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", + "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", + "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", + "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", + "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", + "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", + "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", + "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", + "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", + "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", + "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", + "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", + "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", + "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", + "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", + "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", + "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", + "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", + "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", + "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", + "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", + "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" + ], + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.0" + }, + "daphne": { + "hashes": [ + "sha256:618d1322bb4d875342b99dd2a10da2d9aae7ee3645f765965fdc1e658ea5290a", + "sha256:fcbcace38eb86624ae247c7ffdc8ac12f155d7d19eafac4247381896d6f33761" + ], + "version": "==4.1.2" + }, + "django": { + "hashes": [ + "sha256:236e023f021f5ce7dee5779de7b286565fdea5f4ab86bae5338e3f7b69896cf0", + "sha256:de450c09e91879fa5a307f696e57c851955c910a438a35e6b4c895e86bedc82a" + ], + "index": "pypi", + "markers": "python_version >= '3.10'", + "version": "==5.1.4" + }, + "django-browser-reload": { + "hashes": [ + "sha256:3667939cde0eee1a6d698dbe3b78cf10b573dabc4e711fb7933f1ba91fb98da4", + "sha256:d372c12c1c5962c02279a53cac7e8a020c48f104592c637a06d0768b28d2d6be" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==1.17.0" + }, + "django-filter": { + "hashes": [ + "sha256:c4852822928ce17fb699bcfccd644b3574f1a2d80aeb2b4ff4f16b02dd49dc64", + "sha256:d8ccaf6732afd21ca0542f6733b11591030fa98669f8d15599b358e24a2cd9c3" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==24.3" + }, + "django-redis": { + "hashes": [ + "sha256:6a02abaa34b0fea8bf9b707d2c363ab6adc7409950b2db93602e6cb292818c42", + "sha256:ebc88df7da810732e2af9987f7f426c96204bf89319df4c6da6ca9a2942edd5b" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==5.4.0" + }, + "djangorestframework": { + "hashes": [ + "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20", + "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.15.2" + }, + "h2": { + "hashes": [ + "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d", + "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb" + ], + "version": "==4.1.0" + }, + "hpack": { + "hashes": [ + "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c", + "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==4.0.0" + }, + "hyperframe": { + "hashes": [ + "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15", + "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914" + ], + "markers": "python_full_version >= '3.6.1'", + "version": "==6.0.1" + }, + "hyperlink": { + "hashes": [ + "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b", + "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4" + ], + "version": "==21.0.0" + }, + "idna": { + "hashes": [ + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" + ], + "version": "==3.10" + }, + "incremental": { + "hashes": [ + "sha256:8cb2c3431530bec48ad70513931a760f446ad6c25e8333ca5d95e24b0ed7b8fe", + "sha256:fb4f1d47ee60efe87d4f6f0ebb5f70b9760db2b2574c59c8e8912be4ebd464c9" + ], + "markers": "python_version >= '3.8'", + "version": "==24.7.2" + }, + "markdown": { + "hashes": [ + "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", + "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.7" + }, + "msgpack": { + "hashes": [ + "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", + "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", + "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", + "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", + "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", + "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f", + "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", + "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", + "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b", + "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", + "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", + "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044", + "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", + "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b", + "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", + "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", + "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468", + "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7", + "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", + "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", + "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325", + "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1", + "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846", + "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", + "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", + "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", + "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", + "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", + "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb", + "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68", + "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", + "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f", + "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", + "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b", + "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d", + "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", + "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", + "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd", + "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", + "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48", + "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb", + "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74", + "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b", + "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346", + "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", + "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", + "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5", + "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", + "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", + "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", + "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", + "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", + "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec", + "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8", + "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5", + "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", + "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e", + "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", + "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870", + "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f", + "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96", + "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c", + "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd", + "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788" + ], + "markers": "python_version >= '3.8'", + "version": "==1.1.0" + }, + "priority": { + "hashes": [ + "sha256:6bc1961a6d7fcacbfc337769f1a382c8e746566aaa365e78047abe9f66b2ffbe", + "sha256:be4fcb94b5e37cdeb40af5533afe6dd603bd665fe9c8b3052610fc1001d5d1eb" + ], + "version": "==1.3.0" + }, + "psycopg2-binary": { + "hashes": [ + "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", + "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5", + "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", + "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", + "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", + "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", + "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", + "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", + "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", + "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", + "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", + "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", + "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", + "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", + "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", + "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", + "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", + "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", + "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", + "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", + "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", + "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", + "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", + "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", + "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", + "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", + "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", + "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", + "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44", + "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", + "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", + "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", + "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa", + "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", + "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", + "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", + "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", + "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", + "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", + "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", + "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", + "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", + "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", + "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3", + "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", + "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92", + "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", + "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", + "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8", + "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", + "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", + "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864", + "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", + "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", + "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", + "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", + "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", + "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", + "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", + "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4", + "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", + "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", + "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", + "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", + "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", + "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", + "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.9.10" + }, + "pyasn1": { + "hashes": [ + "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", + "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034" + ], + "markers": "python_version >= '3.8'", + "version": "==0.6.1" + }, + "pyasn1-modules": { + "hashes": [ + "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", + "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c" + ], + "markers": "python_version >= '3.8'", + "version": "==0.4.1" + }, + "pycparser": { + "hashes": [ + "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" + ], + "markers": "python_version >= '3.8'", + "version": "==2.22" + }, + "pyopenssl": { + "hashes": [ + "sha256:49f7a019577d834746bc55c5fce6ecbcec0f2b4ec5ce1cf43a9a173b8138bb36", + "sha256:e474f5a473cd7f92221cc04976e48f4d11502804657a08a989fb3be5514c904a" + ], + "version": "==24.3.0" + }, + "redis": { + "hashes": [ + "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f", + "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==5.2.1" + }, + "service-identity": { + "hashes": [ + "sha256:6b047fbd8a84fd0bb0d55ebce4031e400562b9196e1e0d3e0fe2b8a59f6d4a85", + "sha256:b8683ba13f0d39c6cd5d625d2c5f65421d6d707b013b375c355751557cbe8e09" + ], + "version": "==24.2.0" + }, + "setuptools": { + "hashes": [ + "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", + "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d" + ], + "markers": "python_version >= '3.9'", + "version": "==75.6.0" + }, + "six": { + "hashes": [ + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" + ], + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.17.0" + }, + "sqlparse": { + "hashes": [ + "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", + "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca" + ], + "markers": "python_version >= '3.8'", + "version": "==0.5.3" + }, + "twisted": { + "extras": [ + "http2", + "tls" + ], + "hashes": [ + "sha256:695d0556d5ec579dcc464d2856b634880ed1319f45b10d19043f2b57eb0115b5", + "sha256:fe403076c71f04d5d2d789a755b687c5637ec3bcd3b2b8252d76f2ba65f54261" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==24.11.0" + }, + "txaio": { + "hashes": [ + "sha256:aaea42f8aad50e0ecfb976130ada140797e9dcb85fad2cf72b0f37f8cefcb490", + "sha256:f9a9216e976e5e3246dfd112ad7ad55ca915606b60b84a757ac769bd404ff704" + ], + "markers": "python_version >= '3.7'", + "version": "==23.1.1" + }, + "typing-extensions": { + "hashes": [ + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" + ], + "markers": "python_version >= '3.8'", + "version": "==4.12.2" + }, + "zope-interface": { + "hashes": [ + "sha256:033b3923b63474800b04cba480b70f6e6243a62208071fc148354f3f89cc01b7", + "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a", + "sha256:086ee2f51eaef1e4a52bd7d3111a0404081dadae87f84c0ad4ce2649d4f708b7", + "sha256:0ef9e2f865721553c6f22a9ff97da0f0216c074bd02b25cf0d3af60ea4d6931d", + "sha256:1090c60116b3da3bfdd0c03406e2f14a1ff53e5771aebe33fec1edc0a350175d", + "sha256:144964649eba4c5e4410bb0ee290d338e78f179cdbfd15813de1a664e7649b3b", + "sha256:15398c000c094b8855d7d74f4fdc9e73aa02d4d0d5c775acdef98cdb1119768d", + "sha256:1909f52a00c8c3dcab6c4fad5d13de2285a4b3c7be063b239b8dc15ddfb73bd2", + "sha256:21328fcc9d5b80768bf051faa35ab98fb979080c18e6f84ab3f27ce703bce465", + "sha256:224b7b0314f919e751f2bca17d15aad00ddbb1eadf1cb0190fa8175edb7ede62", + "sha256:25e6a61dcb184453bb00eafa733169ab6d903e46f5c2ace4ad275386f9ab327a", + "sha256:27f926f0dcb058211a3bb3e0e501c69759613b17a553788b2caeb991bed3b61d", + "sha256:29caad142a2355ce7cfea48725aa8bcf0067e2b5cc63fcf5cd9f97ad12d6afb5", + "sha256:2ad9913fd858274db8dd867012ebe544ef18d218f6f7d1e3c3e6d98000f14b75", + "sha256:31d06db13a30303c08d61d5fb32154be51dfcbdb8438d2374ae27b4e069aac40", + "sha256:3e0350b51e88658d5ad126c6a57502b19d5f559f6cb0a628e3dc90442b53dd98", + "sha256:3f6771d1647b1fc543d37640b45c06b34832a943c80d1db214a37c31161a93f1", + "sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd", + "sha256:52e446f9955195440e787596dccd1411f543743c359eeb26e9b2c02b077b0519", + "sha256:550f1c6588ecc368c9ce13c44a49b8d6b6f3ca7588873c679bd8fd88a1b557b6", + "sha256:72cd1790b48c16db85d51fbbd12d20949d7339ad84fd971427cf00d990c1f137", + "sha256:7bd449c306ba006c65799ea7912adbbfed071089461a19091a228998b82b1fdb", + "sha256:7dc5016e0133c1a1ec212fc87a4f7e7e562054549a99c73c8896fa3a9e80cbc7", + "sha256:802176a9f99bd8cc276dcd3b8512808716492f6f557c11196d42e26c01a69a4c", + "sha256:80ecf2451596f19fd607bb09953f426588fc1e79e93f5968ecf3367550396b22", + "sha256:8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe", + "sha256:8e7da17f53e25d1a3bde5da4601e026adc9e8071f9f6f936d0fe3fe84ace6d54", + "sha256:a102424e28c6b47c67923a1f337ede4a4c2bba3965b01cf707978a801fc7442c", + "sha256:a19a6cc9c6ce4b1e7e3d319a473cf0ee989cbbe2b39201d7c19e214d2dfb80c7", + "sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b", + "sha256:baf95683cde5bc7d0e12d8e7588a3eb754d7c4fa714548adcd96bdf90169f021", + "sha256:cab15ff4832580aa440dc9790b8a6128abd0b88b7ee4dd56abacbc52f212209d", + "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2", + "sha256:d3a8ffec2a50d8ec470143ea3d15c0c52d73df882eef92de7537e8ce13475e8a", + "sha256:e204937f67b28d2dca73ca936d3039a144a081fc47a07598d44854ea2a106239", + "sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398", + "sha256:f6dd02ec01f4468da0f234da9d9c8545c5412fef80bc590cc51d8dd084138a89" + ], + "markers": "python_version >= '3.8'", + "version": "==7.2" + } + }, + "develop": {} +} diff --git a/backend/app/__init__.py b/backend/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/admin.py b/backend/app/admin.py new file mode 100644 index 0000000..30a66d4 --- /dev/null +++ b/backend/app/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin +from app.models import Order, Waiter, Meal + +# Register your models here. + +admin.site.register([Meal, Order, Waiter]) diff --git a/backend/app/apps.py b/backend/app/apps.py new file mode 100644 index 0000000..ed327d2 --- /dev/null +++ b/backend/app/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AppConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'app' diff --git a/backend/app/consumers.py b/backend/app/consumers.py new file mode 100644 index 0000000..19d4e5c --- /dev/null +++ b/backend/app/consumers.py @@ -0,0 +1,3 @@ +from channels.db import database_sync_to_async +from channels.generic.websocket import AsyncJsonWebsocketConsumer +from django.urls import reverse diff --git a/backend/app/migrations/0001_initial.py b/backend/app/migrations/0001_initial.py new file mode 100644 index 0000000..8d4fbd3 --- /dev/null +++ b/backend/app/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1.4 on 2024-12-12 14:12 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Waiter', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=120, verbose_name='Imię i nazwisko')), + ], + ), + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Utworzono')), + ('updated_on', models.DateTimeField(auto_now=True, verbose_name='Zaktualizowano')), + ('data', models.JSONField(default=dict, verbose_name='Dane zamówienia')), + ('status', models.PositiveSmallIntegerField(choices=[(1, 'Zamówienie złożone'), (2, 'Zamówienie w trakcie przygotowywania'), (3, 'Zamówienie gotowe'), (4, 'Zamówienie skończone')], default=1, verbose_name='Status zamówienia')), + ('waiter', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='orders', to='app.waiter', verbose_name='Kelner')), + ], + ), + ] diff --git a/backend/app/migrations/0002_meal.py b/backend/app/migrations/0002_meal.py new file mode 100644 index 0000000..7eb2c1f --- /dev/null +++ b/backend/app/migrations/0002_meal.py @@ -0,0 +1,20 @@ +# Generated by Django 5.1.4 on 2024-12-16 13:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Meal', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField(verbose_name='Danie')), + ], + ), + ] diff --git a/backend/app/migrations/__init__.py b/backend/app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/models.py b/backend/app/models.py new file mode 100644 index 0000000..fdea81f --- /dev/null +++ b/backend/app/models.py @@ -0,0 +1,54 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +# Create your models here. + + +class Waiter(models.Model): + name = models.CharField(_('Imię i nazwisko'), max_length=120) + + def __str__(self): + return self.name + + class Meta: + verbose_name = _("Kelner") + verbose_name_plural = _("Kelnerzy") + + +class Meal(models.Model): + name = models.TextField(_('Danie')) + + def __str__(self): + return self.name + + class Meta: + verbose_name = _("Danie") + verbose_name_plural = _("Dania") + + +class Order(models.Model): + created_on = models.DateTimeField(_('Utworzono'), auto_now_add=True) + updated_on = models.DateTimeField(_('Zaktualizowano'), auto_now=True) + waiter = models.ForeignKey( + Waiter, models.SET_NULL, related_name='orders', verbose_name=_('Kelner'), null=True + ) + data = models.JSONField(_('Dane zamówienia'), default=dict) + + class StatusChoices(models.IntegerChoices): + ORDERED = (1, _('Zamówienie złożone')) + IN_PROGRESS = (2, ('Zamówienie w trakcie przygotowywania')) + READY = (3, _('Zamówienie gotowe')) + FINALIZED = (4, _('Zamówienie skończone')) + + status = models.PositiveSmallIntegerField( + _('Status zamówienia'), + choices=StatusChoices.choices, + default=StatusChoices.ORDERED, + ) + + def __str__(self): + return self.id + + class Meta: + verbose_name = _("Zamówienie") + verbose_name_plural = _("Zamówienia") diff --git a/backend/app/routing.py b/backend/app/routing.py new file mode 100644 index 0000000..0f6ee26 --- /dev/null +++ b/backend/app/routing.py @@ -0,0 +1,7 @@ +# app/routing.py + +from django.urls import re_path + +from . import consumers + +websocket_urlpatterns = [] diff --git a/backend/app/tests.py b/backend/app/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/app/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/app/views.py b/backend/app/views.py new file mode 100644 index 0000000..00cf670 --- /dev/null +++ b/backend/app/views.py @@ -0,0 +1,5 @@ +from django.shortcuts import render + +# Create your views here. + + diff --git a/backend/backend/__init__.py b/backend/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/backend/asgi.py b/backend/backend/asgi.py new file mode 100644 index 0000000..eb75c4d --- /dev/null +++ b/backend/backend/asgi.py @@ -0,0 +1,34 @@ +""" +ASGI config for backend 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/5.1/howto/deployment/asgi/ +""" + +import os +import django + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') +django.setup() + +from channels.auth import AuthMiddlewareStack +from channels.routing import ProtocolTypeRouter, URLRouter +from channels.security.websocket import AllowedHostsOriginValidator +from django.core.asgi import get_asgi_application + +from app.routing import websocket_urlpatterns + +# Initialize Django ASGI application early to ensure the AppRegistry +# is populated before importing code that may import ORM models. +django_asgi_app = get_asgi_application() + +application = ProtocolTypeRouter( + { + 'http': django_asgi_app, + 'websocket': AllowedHostsOriginValidator( + AuthMiddlewareStack(URLRouter(websocket_urlpatterns)) + ), + } +) diff --git a/backend/backend/settings.py b/backend/backend/settings.py new file mode 100644 index 0000000..7b0a36b --- /dev/null +++ b/backend/backend/settings.py @@ -0,0 +1,148 @@ +""" +Django settings for backend project. + +Generated by 'django-admin startproject' using Django 5.1.4. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.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/5.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = os.environ['SECRET_KEY'] + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = os.environ['DEBUG']=='True' + +ALLOWED_HOSTS = ['*'] +CSRF_TRUSTED_ORIGINS = ['http://127.0.0.1', 'https://localhost'] + +# Application definition + +INSTALLED_APPS = [ + 'django_browser_reload', + 'app', + 'daphne', + 'rest_framework', + 'django_filters', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + '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', + 'django_browser_reload.middleware.BrowserReloadMiddleware', +] + +ROOT_URLCONF = 'backend.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 = 'backend.wsgi.application' +ASGI_APPLICATION = 'backend.asgi.application' + +# Channel layers + +CHANNEL_LAYERS = { + 'default': { + 'BACKEND': 'channels_redis.core.RedisChannelLayer', + 'CONFIG': { + 'hosts': [('redis', 6379)], + }, + }, +} + + +# Database +# https://docs.djangoproject.com/en/5.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'db', + 'USER': os.environ['DB_USER'], + 'PASSWORD': os.environ['DB_PASSWORD'], + 'HOST': 'db', + 'PORT': 5432, + + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.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/5.1/topics/i18n/ + +LANGUAGE_CODE = 'pl-PL' + +TIME_ZONE = 'Europe/Warsaw' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.1/howto/static-files/ + +STATIC_ROOT = BASE_DIR / 'static' +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/backend/backend/urls.py b/backend/backend/urls.py new file mode 100644 index 0000000..8856f5b --- /dev/null +++ b/backend/backend/urls.py @@ -0,0 +1,109 @@ +""" +URL configuration for backend project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.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 rest_framework import routers, serializers, viewsets, filters +from django_filters.rest_framework import DjangoFilterBackend +from django.db.models import Q +from django.utils import timezone +import datetime + +from app.models import Order, Waiter, Meal + + +# Serializers define the API representation. +class WaiterSerializer(serializers.ModelSerializer): + class Meta: + model = Waiter + fields = [ + 'id', + 'name', + ] + + +# ViewSets define the view behavior. +class WaiterViewSet(viewsets.ModelViewSet): + queryset = Waiter.objects.all() + serializer_class = WaiterSerializer + + +class MealSerializer(serializers.ModelSerializer): + class Meta: + model = Meal + fields = [ + 'id', + 'name', + ] + + +# ViewSets define the view behavior. +class MealViewSet(viewsets.ModelViewSet): + queryset = Meal.objects.all() + serializer_class = MealSerializer + + +class OrderSerializer(serializers.ModelSerializer): + waiter_name = serializers.SerializerMethodField() + status_name = serializers.CharField(source='get_status_display', read_only=True) + + class Meta: + model = Order + fields = [ + 'id', + 'created_on', + 'updated_on', + 'waiter', + 'waiter_name', + 'data', + 'status', + 'status_name', + ] + + def get_waiter_name(self, obj): + return obj.waiter.name if obj.waiter else '' + + +# ViewSets define the view behavior. +class OrderViewSet(viewsets.ModelViewSet): + serializer_class = OrderSerializer + filter_backends = [DjangoFilterBackend, filters.OrderingFilter] + filterset_fields = ['waiter'] + ordering = ['-updated_on'] + + def get_queryset(self): + five_minutes_ago = timezone.now() + datetime.timedelta(minutes=-5) + return Order.objects.filter( + ~Q(status=Order.StatusChoices.FINALIZED) + | Q(updated_on__gte=five_minutes_ago) + ) + + +# Routers provide an easy way of automatically determining the URL conf. +router = routers.DefaultRouter() +router.register(r'meals', MealViewSet, 'meals') +router.register(r'waiters', WaiterViewSet, 'waiters') +router.register(r'orders', OrderViewSet, 'orders') + +# Wire up our API using automatic URL routing. +# Additionally, we include login URLs for the browsable API. +urlpatterns = [ + path('api/', include(router.urls)), + path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), + path('admin/', admin.site.urls), + path('__reload__/', include('django_browser_reload.urls')), +] diff --git a/backend/backend/wsgi.py b/backend/backend/wsgi.py new file mode 100644 index 0000000..07bda9d --- /dev/null +++ b/backend/backend/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for backend 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/5.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') + +application = get_wsgi_application() diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh new file mode 100755 index 0000000..8c2e563 --- /dev/null +++ b/backend/entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +echo "Waiting for postgres..." + +while ! nc -z db 5432; do + sleep 0.1 +done + +echo "PostgreSQL started" + +# python manage.py flush --no-input +# python manage.py migrate + +python manage.py collectstatic --no-input --clear > /dev/null 2>&1 & + +exec "$@" diff --git a/backend/manage.py b/backend/manage.py new file mode 100755 index 0000000..eb6431e --- /dev/null +++ b/backend/manage.py @@ -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', 'backend.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() diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..46e0424 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,12 @@ +Django +psycopg2-binary +django-browser-reload +six +channels[daphne] +channels_redis +redis +django-redis +Twisted[tls,http2] +djangorestframework +markdown +django-filter \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..ec94b6f --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,49 @@ +services: + backend: + build: ./backend/ + # command: daphne -b 0.0.0.0 -p 8000 backend.asgi:application + command: python manage.py runserver 0.0.0.0:8000 + restart: unless-stopped + volumes: + - ./backend:/usr/src/app + - django_static:/usr/src/app/static + ports: + - "8000:8000" + environment: + SECRET_KEY: ${SECRET_KEY} + DEBUG: ${DEBUG:-False} + DB_NAME: ${DB_NAME:-db} + DB_USER: ${DB_USER:-user} + DB_PASSWORD: ${DB_PASSWORD:-password} + depends_on: + - redis + - db + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.dev + command: pnpm run dev + restart: unless-stopped + volumes: + - ./frontend:/app + ports: + - "3000:3000" + + redis: + restart: unless-stopped + image: redis + + db: + image: postgres + restart: unless-stopped + volumes: + - postgres_data:/var/lib/postgresql/data/ + environment: + - POSTGRES_DB=${DB_NAME:-db} + - POSTGRES_USER=${DB_USER:-user} + - POSTGRES_PASSWORD=${DB_PASSWORD:-password} + +volumes: + postgres_data: + django_static: diff --git a/docker-compose.yaml.prod b/docker-compose.yaml.prod new file mode 100644 index 0000000..90b2c4f --- /dev/null +++ b/docker-compose.yaml.prod @@ -0,0 +1,60 @@ +services: + backend: + build: ./backend/ + command: daphne -b 0.0.0.0 -p 8000 backend.asgi:application + restart: unless-stopped + volumes: + - django_static_prod:/usr/src/app/static + ports: + - "8000:8000" + environment: + SECRET_KEY: ${SECRET_KEY} + DEBUG: False + DB_NAME: ${DB_NAME:-db} + DB_USER: ${DB_USER:-user} + DB_PASSWORD: ${DB_PASSWORD:-password} + depends_on: + - redis + - db + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.prod + restart: unless-stopped + volumes: + - ./frontend:/app + ports: + - "3000:3000" + + redis: + restart: unless-stopped + image: redis + + db: + image: postgres + restart: unless-stopped + volumes: + - postgres_data_prod:/var/lib/postgresql/data/ + environment: + - POSTGRES_DB=${DB_NAME:-db} + - POSTGRES_USER=${DB_USER:-user} + - POSTGRES_PASSWORD=${DB_PASSWORD:-password} + + caddy: + image: caddy:latest + restart: unless-stopped + cap_add: + - NET_ADMIN + ports: + - "8080:8080" + # - "443:443" + # - "443:443/udp" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile + - django_static_prod:/static + + +volumes: + postgres_data_prod: + django_static_prod: diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev new file mode 100644 index 0000000..c895cfb --- /dev/null +++ b/frontend/Dockerfile.dev @@ -0,0 +1,21 @@ +FROM node:20 AS base +WORKDIR /app +RUN npm i -g pnpm +COPY package.json pnpm-lock.yaml ./ + +RUN pnpm install + +COPY . . + +FROM node:20-alpine3.19 as release +WORKDIR /app +RUN npm i -g pnpm + +COPY --from=base /app/node_modules ./node_modules +COPY --from=base /app/package.json ./package.json +COPY --from=base /app/.next ./.next +COPY --from=base /app . + +EXPOSE 3000 + +CMD ["pnpm", "dev"] \ No newline at end of file diff --git a/frontend/Dockerfile.prod b/frontend/Dockerfile.prod new file mode 100644 index 0000000..dac69be --- /dev/null +++ b/frontend/Dockerfile.prod @@ -0,0 +1,65 @@ +FROM node:alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \ + else echo "Lockfile not found." && exit 1; \ + fi + + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry during the build. +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN yarn build + +# If using npm comment out above and use below instead +# RUN npm run build + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV production +# Uncomment the following line in case you want to disable telemetry during runtime. +ENV NEXT_TELEMETRY_DISABLED 1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown nextjs:nodejs .next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT 3000 +# set hostname to localhost +ENV HOSTNAME "0.0.0.0" + +CMD ["node", "server.js"] diff --git a/frontend/app/api/meals/route.ts b/frontend/app/api/meals/route.ts new file mode 100644 index 0000000..7e74fcf --- /dev/null +++ b/frontend/app/api/meals/route.ts @@ -0,0 +1,10 @@ +export async function GET() { + const res = await fetch('http://backend:8000/api/meals', { + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await res.json(); + + return Response.json(data); +} diff --git a/frontend/app/api/orders/route.ts b/frontend/app/api/orders/route.ts new file mode 100644 index 0000000..83692dc --- /dev/null +++ b/frontend/app/api/orders/route.ts @@ -0,0 +1,42 @@ +import { NextRequest } from "next/server"; + +export async function GET(request: NextRequest) { + const searchParams = request.nextUrl.searchParams + const query = searchParams.get('waiter') + const res = await fetch(!!query ? `http://backend:8000/api/orders/?waiter=${query}` : 'http://backend:8000/api/orders/', { + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await res.json(); + + return Response.json(data); +} + +export async function POST(request: NextRequest) { + const body = await request.json() + const res = await fetch('http://backend:8000/api/orders/', { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + }, + }); + + return Response.json({ status: res.status }); +} + +export async function PUT(request: NextRequest) { + const searchParams = request.nextUrl.searchParams + const query = searchParams.get('id') + const body = await request.json() + const res = await fetch(`http://backend:8000/api/orders/${query}/`, { + method: 'PUT', + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + }, + }); + + return Response.json({ status: res.status }); +} diff --git a/frontend/app/api/tools.ts b/frontend/app/api/tools.ts new file mode 100644 index 0000000..49e8754 --- /dev/null +++ b/frontend/app/api/tools.ts @@ -0,0 +1,16 @@ +"use client" + +import useSWR from 'swr' + +export const fetcher = (...args) => fetch(...args).then(res => res.json()) // @ts-ignore + + +export function useUser (id: number) { + const { data, error, isLoading } = useSWR(`/api/waiters/${id}`, fetcher) + + return { + user: data, + isLoading, + isError: error + } +} \ No newline at end of file diff --git a/frontend/app/api/waiters/route.ts b/frontend/app/api/waiters/route.ts new file mode 100644 index 0000000..347d45c --- /dev/null +++ b/frontend/app/api/waiters/route.ts @@ -0,0 +1,10 @@ +export async function GET() { + const res = await fetch('http://backend:8000/api/waiters', { + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await res.json(); + + return Response.json(data); +} diff --git a/frontend/app/favicon.ico b/frontend/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/frontend/app/favicon.ico differ diff --git a/frontend/app/globals.css b/frontend/app/globals.css new file mode 100644 index 0000000..6a0d541 --- /dev/null +++ b/frontend/app/globals.css @@ -0,0 +1,48 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + + +form#form p { + @apply flex flex-col; +} + +form#form label { + @apply text-gray-700 font-bold text-sm; +} + +form#form input:not([type='checkbox']), +textarea { + @apply p-1 ps-2 transition ease-in-out focus:outline-none border-b-2 border-b-gray-400 mb-2 focus:bg-gray-100 focus:border-gray-600 appearance-none; +} + +form#form select { + @apply p-2 ps-2 rounded-md bg-gray-100; +} + +select.form { + @apply p-2 ps-2 rounded-md bg-gray-200; +} + +.dropdown:focus-within .dropdown-menu { + /* @apply block; */ + display: block; +} + +@layer components { + button { + @apply std-btn + } + + .prose-with-table { + @apply prose-table:max-w-screen-2xl prose-table:text-center max-w-screen-xl w-full prose-td:py-1 prose-td:px-2 prose-th:py-1 prose-th:px-2 prose-td:align-middle prose-th:align-middle prose-thead:mx-auto; + } + + .std-btn { + @apply inline-flex text-center justify-center items-center no-underline px-3 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-700 focus:outline-none focus:border-blue-300 active:bg-gray-50 active:text-gray-800; + } + + .std-menu-item { + @apply text-gray-700 flex justify-between w-full px-4 py-2 text-sm leading-5 text-left + } +} diff --git a/frontend/app/kitchen/page.tsx b/frontend/app/kitchen/page.tsx new file mode 100644 index 0000000..704b475 --- /dev/null +++ b/frontend/app/kitchen/page.tsx @@ -0,0 +1,106 @@ +'use client'; + +import { fetcher } from '@/app/api/tools'; +import useSWR from 'swr'; +import Link from 'next/link'; + +type Order = { + id: number; + created_on: string; + updated_on: string; + waiter: number; + waiter_name: string; + data: [{ item: string; additional_info: string; finished: boolean }]; + status: string; + status_name: string; +}; + +export default function Home() { + const { data, mutate, error, isLoading } = useSWR(`/api/orders`, fetcher, { + refreshInterval: 1000, + }); + + const update_finished = (o: number, i: number, v: boolean) => { + let d = data; + d[o].data[i].finished = v; + console.log(v); + fetch(`/api/orders?id=${d[o].id}`, { + method: 'PUT', + body: JSON.stringify(d[o]), + headers: { + 'Content-Type': 'application/json', + }, + }).then(() => mutate(d)); + }; + + const update_status = (o: number, v: number) => { + let d = data; + d[o].status = v; + console.log(v); + fetch(`/api/orders?id=${d[o].id}`, { + method: 'PUT', + body: JSON.stringify(d[o]), + headers: { + 'Content-Type': 'application/json', + }, + }).then(() => mutate(d)); + }; + + if (error) return
Błąd przy ładowaniu danych
; + if (isLoading) return
Ładowanie
; + + return ( + <> +

Zamówienia:

+ + + ); +} diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx new file mode 100644 index 0000000..dae43f0 --- /dev/null +++ b/frontend/app/layout.tsx @@ -0,0 +1,23 @@ +import type { Metadata } from "next"; +import "./globals.css"; + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx new file mode 100644 index 0000000..4e6078a --- /dev/null +++ b/frontend/app/page.tsx @@ -0,0 +1,18 @@ +import Image from 'next/image'; +import Link from 'next/link'; + +export default function Home() { + return ( + <> +

Witamy

+
+ + + + + + +
+ + ); +} diff --git a/frontend/app/waiter/[id]/new/page.tsx b/frontend/app/waiter/[id]/new/page.tsx new file mode 100644 index 0000000..ff43f56 --- /dev/null +++ b/frontend/app/waiter/[id]/new/page.tsx @@ -0,0 +1,147 @@ +'use client'; + +import Link from 'next/link'; +import { useParams, useRouter } from 'next/navigation'; +import * as React from 'react'; +import { useForm, useFieldArray, useWatch, Control } from 'react-hook-form'; +import { fetcher } from '@/app/api/tools'; +import useSWR from 'swr'; + +type FormValues = { + data: { + item: string; + additional_info: string; + finished: boolean; + }[]; +}; + +export default function App() { + const { data, error, isLoading } = useSWR('/api/meals', fetcher); + + const { id } = useParams(); + const router = useRouter(); + const { + register, + control, + handleSubmit, + formState: { errors }, + } = useForm({ + defaultValues: { + data: [{ item: '', additional_info: '', finished: false }], + }, + mode: 'onBlur', + }); + const { fields, append, remove } = useFieldArray({ + name: 'data', + control, + }); + const onSubmit = (data: FormValues) => { + console.log(data); + fetch('/api/orders', { + method: 'POST', + body: JSON.stringify({ waiter: id, ...data }), + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(({ status }) => console.log(status)) + .then((_) => router.push(`/waiter/${id}`)); + }; + + if (error) return
Błąd przy ładowaniu danych
; + if (isLoading) return
Ładowanie
; + + return ( + <> +
+

Nowe zamówienie

+ {fields.map((field, index) => { + return ( +
+ +

+ + +

+

+ +