├── .dockerignore ├── .env.example ├── .git-old ├── COMMIT_EDITMSG ├── HEAD ├── config ├── description ├── hooks │ ├── applypatch-msg.sample │ ├── commit-msg.sample │ ├── fsmonitor-watchman.sample │ ├── post-update.sample │ ├── pre-applypatch.sample │ ├── pre-commit.sample │ ├── pre-merge-commit.sample │ ├── pre-push.sample │ ├── pre-rebase.sample │ ├── pre-receive.sample │ ├── prepare-commit-msg.sample │ ├── push-to-checkout.sample │ ├── sendemail-validate.sample │ └── update.sample ├── index ├── info │ └── exclude ├── objects │ ├── 10 │ │ └── 003f5c31e8bed26f59b7fe9326a415ffc2e5a2 │ ├── 11 │ │ ├── a55c9420dbb3ba7e8a74401d21df9e70fa3a51 │ │ ├── ae12ab68bcc622ae3c0a17c66c9ceb8a9b236d │ │ └── f598f10052cebbc27f2ef90f01a870a01077c7 │ ├── 12 │ │ ├── 78debc927fd60bab7e090a31534dd70057b37b │ │ └── b42c0f684464e79ab5f9d612bdecaaf4900436 │ ├── 14 │ │ └── 5f4b467baf644f1c33193182b7b2551e2e8b6f │ ├── 17 │ │ └── 3da64dcf9be36843b38c3763a5b6e455251aec │ ├── 19 │ │ └── feb596e924525f775afb13632a71f3aaf783b4 │ ├── 20 │ │ └── d084ca68de2172e257b05e3e9d276f93f68af0 │ ├── 23 │ │ └── 216d3302517dcda33f2ece120c55bc9d98aa57 │ ├── 24 │ │ ├── 4c6d2d3655caf6f325157525b877e0ee5d0264 │ │ └── 54499bf59464fa31cb7faf1cdc5f1a30d875e1 │ ├── 26 │ │ └── 500c442db5b30913257686879bf8a25586a59b │ ├── 27 │ │ └── f51cef71a06ebc13d428ec5aa450f22bb81e3d │ ├── 29 │ │ ├── a7f0d7b23dc83862d5a0aeb20a6daf6ba5c5a2 │ │ └── be2e90a7e51ea44e8a8bb080a71fc6042ab8dd │ ├── 31 │ │ └── 23ed14d6cb5789f21b70c31f0182bfaa234661 │ ├── 32 │ │ └── 3f03292c2d0a68a5e9e03aeaaafbebcc4cb55e │ ├── 36 │ │ └── 069196631fc29fd9c96e819482d0ac54cb4c0e │ ├── 39 │ │ ├── 138523f0ca6add46cb3e9e0eb66156d7cfd6e3 │ │ └── 7ae8878fa8fdd6475da83d6b1a00640e4fc38a │ ├── 40 │ │ └── b714c0050bc4d9284203ef1ec1309e35a54eab │ ├── 41 │ │ ├── 405aff8f28edfdf66c251ce9f4b557c9580804 │ │ ├── 5fa73d2738b1f381268d6ae88d8b6bfa392856 │ │ └── 8c78fa174789b42f43235b63ab045d2d0123a0 │ ├── 46 │ │ └── 1aebde340af5cb7907335e020ddac212a614ba │ ├── 48 │ │ └── af258320389b89a5c8e5e4656b4bb2f8147a55 │ ├── 49 │ │ └── 6c4c2e8ea7033ffac7bc9192c3865268b1b026 │ ├── 50 │ │ └── 8f41b442ec42c5890f8ea771ace8d3a3127d49 │ ├── 51 │ │ └── c3781e5ccb396a0e6ece38adf478b7b9279095 │ ├── 52 │ │ └── 65c3fda2cfecdcbeb500ba6eabac5ab665d622 │ ├── 56 │ │ └── 8515825e7fd04817d7911634e9f9075441c53c │ ├── 57 │ │ ├── abf907e474f2bcc739fecceb78ae52e9ade479 │ │ └── f4765c35fcc4dd8be95881dabf1e6df6cb36ab │ ├── 58 │ │ ├── 54ab2b7de551488182495193b15d790bfc8a73 │ │ └── d43cdfac380818c18b35622659cf4c79a7b360 │ ├── 60 │ │ ├── 0f8ace02f527f8eb158f73180913cb433cc561 │ │ └── 8db8e485709223f1f21b4c3b25baf6d24687ad │ ├── 61 │ │ └── 1e7982410fb8033a892f6cc0525060c60712b4 │ ├── 62 │ │ └── bed2d79fa62fe34c7b8414a3075b973505fa0b │ ├── 63 │ │ └── 942815d71cabf073ef8771358e0e4c9fe9f461 │ ├── 66 │ │ ├── a63e8758dff2e2a0bee94f958a60ebff19f957 │ │ └── ae7997209d1fc2756365d0346f728fadd83cb5 │ ├── 67 │ │ ├── 22c93fc28b24a9fe632943c6361956c10d5e60 │ │ └── e2b928a89ccf6d608f680b5f085dfff6a68c72 │ ├── 68 │ │ └── 0f646c28039145e256fbd1e7423fcb64b6f1d4 │ ├── 69 │ │ ├── 33d225627ca27d5be2342db73df25656c83f76 │ │ └── c65bb2691aa69bb70d76278a1c8f5bcf1e8da4 │ ├── 74 │ │ └── 3a88e0c239fa209cc90677c937960ea221deb7 │ ├── 75 │ │ └── bfade613d6e89313e92bb0f9dbcfe1f52726c4 │ ├── 76 │ │ ├── 0a8684f45047be77b8dddfc4937e8218af83af │ │ └── 1ede33462ce60eb76f5904efad89160254dacb │ ├── 79 │ │ └── 740d5491fd65c6d6495cec72e2da50fb5683a7 │ ├── 81 │ │ ├── 15e8d1c3990483d0e4d079667d9a60b47d63f5 │ │ └── a60a62eb302d4a07d90ebe3d0d577f51507a75 │ ├── 83 │ │ └── 4b740aea3ba41b44127934554176b0cb0ee292 │ ├── 84 │ │ └── f6e78fcbeea0f407e4d436fdf86e5cf1b01f96 │ ├── 87 │ │ └── dc1818b1c2ceece345c4005ac4dc93934d9558 │ ├── 89 │ │ └── 3464107c08da785804fd868efdfafa20297508 │ ├── 91 │ │ └── 021494f4fd4cc4ae8bb98a42f97bc52eab5bc4 │ ├── 93 │ │ ├── 37a3f7e4ae98843ac49fe66da587fd1fd6b934 │ │ ├── 58a62092f9992ddf43c1e10c690b0823a750a1 │ │ └── 6247bb618aaf86dc8141842ebad52929f476e7 │ ├── 96 │ │ └── 8ad4797727ae3f67e66ab4811f2eda5a959ead │ ├── 97 │ │ └── 47186db34faaa694aa76e0bfeb2024c5212b43 │ ├── 98 │ │ └── efbe7ce867c5b37367383dd379489903bf9bdc │ ├── 99 │ │ ├── 590a689e3612d31913b0ebdec2abf2129a7f85 │ │ └── e0cf35845eaa1d97d51ad74c2d784fd477e96c │ ├── 02 │ │ └── e9ce1b48e6fc035b9a1e892bf2d92dcb333f51 │ ├── 04 │ │ └── 113fd1c20061c5a971cba6d5c92b1b9dbea756 │ ├── 08 │ │ └── d1e4f0a86bb71522c2b7867e215754c98a2ea6 │ ├── 09 │ │ ├── 1a9e41d5ca15e880114d7d484dbbb8bd711b4e │ │ └── b97a6fa22188b62b22236f07366bd7f27ffd42 │ ├── 0b │ │ ├── 9dd54cac4e037eaaf7e84eb26ba02ee5cc489f │ │ └── c6aaeb893447c1317f1993de2cbf1411ddeb89 │ ├── 0c │ │ ├── 5190a66f6a8de5c89782bb7e9bd8614a8f2254 │ │ └── c300404fb4014f59db118441c7122a030bb6b7 │ ├── 1b │ │ ├── a787e6d247041d4ca5dd593357a04add01e5cf │ │ └── ea1c678719ee16e4c858153d238370caefe4fa │ ├── 1d │ │ ├── 399106b712b43a9d3265193a9590d412dafd34 │ │ ├── 8db26ff99178b8e53efd46c0f798360b8db4eb │ │ └── ca6a5583a283d633d42ae79e2978f6608f46d6 │ ├── 1e │ │ └── 212b4fefbf1ffcf3460c2f26b7c7fa3ac32e15 │ ├── 1f │ │ ├── b6aee898d115b0bbd03e051b3fe1eae5923d80 │ │ └── f9452d5b9f24e89df0f8536b067839b4860448 │ ├── 2a │ │ └── 75eeeb596abfdd6e88f48b88183600539df1a4 │ ├── 2e │ │ └── d88e025b8609f51020a8c454dbb5508c24b624 │ ├── 2f │ │ └── e0f25563d5ff10af0f040a1ff9610a2e58cc18 │ ├── 3a │ │ ├── a19c647b07528dd85d921f4d8363f033c1794a │ │ └── a86073b91e2fd27bff096ed138399c15681389 │ ├── 3c │ │ └── 1087abe4350a6595828536d82bfd0282daa36e │ ├── 3f │ │ └── 56f2539226d0d05cc9ff68303f808fcbb63c8f │ ├── 4a │ │ └── 24ea3c81768cbfe99333e77d98fd3258fb1646 │ ├── 4b │ │ ├── 066a9350dd2173c53c5381bf233833cf340880 │ │ └── ed1c3c926bc7e56c8509d6c97ce538486debcc │ ├── 4c │ │ └── 23be44d8f139d6ab2d5d0a8b00998f134793f0 │ ├── 5b │ │ └── 838d9b00e07b3272656dcadb59ca3dfa2a85cd │ ├── 5d │ │ ├── 20e021ccea7ea221b79a773f10f1fd59609454 │ │ ├── 62881c9a9eeb48b5580ae4fcbf337affa0cc8f │ │ ├── 80d5932b8464d41dbe8abe3ded51dee1ed9200 │ │ └── a7b00ed3c5b4eee4a65b3dbf537221e18a18d6 │ ├── 5f │ │ └── d8fca418ee7cfbc062b56b1f96722c4bc4e745 │ ├── 6a │ │ ├── 4fc4416149400fdcb08e3c3495d1a32e82498a │ │ └── d0f5044b4970e2a8b391d21d113eb082951c7e │ ├── 6b │ │ └── 8b2b1d4cfbbdec30d88079bf0a24a505cd57fc │ ├── 6d │ │ └── b941908c461425824528a750b814cf78c5e92e │ ├── 6e │ │ └── d71cf013a97b7c33acc03c279953ad96d2b7d1 │ ├── 7a │ │ └── 79cf980fc4257778138e72197dbf16d8dbebe7 │ ├── 7b │ │ ├── 1cf1880b6153315177e4b37b8d479fa90364fd │ │ └── efe9f111c760cd768ec22408300759706cd272 │ ├── 7c │ │ └── 35a716c260a19da15480a580bda54c37d0128b │ ├── 7e │ │ ├── 05208910ed43e7e20a06f3be88cf60843f3acd │ │ └── 6a3e64a157fd8d2df5a6714b80623cbfbbada2 │ ├── 8b │ │ └── b09d7649382c48513b7afcac75dab568daa384 │ ├── 8d │ │ ├── 57fbd07d7f8bb7dce0098ba71e0dac2a61e2f8 │ │ ├── 7ead74ecfee3dc156c0a832a8b086b58e0fd0f │ │ └── a2414af3aa04a246567732337bb738c51221b0 │ ├── 9e │ │ ├── 3c03594c58bfdfa95c73d4a297307bdfe24301 │ │ └── 755d2f90e43ad316f79b5c46c8d2459f9b1d08 │ ├── a0 │ │ ├── 2a3346e931840a80d27044fa04491c5eddbafe │ │ └── 2ac4e4b810dbef3b4ec1b729d8527ea0249840 │ ├── a1 │ │ └── 48bada0a0ff7ad99a663cfaae0a182a0479810 │ ├── a4 │ │ ├── 097c30a13412aaa38b3ddece23bacdb91ec1e5 │ │ └── e437f7168cd980bb1ededcd7ae354c67304789 │ ├── a5 │ │ ├── 2cd33871e21703d6ec429f12d99e62ed4a68e1 │ │ ├── 44d467cd920b475d9fe29896e40b00750efa52 │ │ ├── c6700c44185f7240f2864b41f3ece5e9220c91 │ │ ├── c88b6f50f5a7faf632ef0dd3c0e492ecbccc6f │ │ ├── e51f8dabb71386204e8fa80436889fd280e899 │ │ └── fd5a6776c768e5f7d808bb903dbc4c6280a655 │ ├── a7 │ │ ├── accd8cf790a4c1df30be240aad7c1822c83639 │ │ └── ccd4b9cf93e7812d84e88d83c84615a177f018 │ ├── a8 │ │ ├── 8d906887180a93117e1161693a4289f3396047 │ │ └── b1814f61cbfeedaeff77d672f08aa4a921b0b5 │ ├── aa │ │ └── d30393fafd2c0cae1e69ec513b7eaf358939ef │ ├── ac │ │ └── de01ca6faf568409e9bfc9c22bd22f94e9b75f │ ├── ad │ │ ├── 224634da54212ca16841917164cfb752dc539d │ │ └── 4c8c36b340e58f17b445fd6da283a5d1a1c51f │ ├── ae │ │ └── 3ccc596def51f401e8a4740bfb8ef07a515fde │ ├── b0 │ │ └── 7148d02893395ece5b13dd839e880d671bd771 │ ├── b2 │ │ └── c427bda167d9c769833f6a18ad9820603fa08e │ ├── b8 │ │ └── 33ddd60b45a1c98b3ae2cbf5727a5d283b5cd9 │ ├── bd │ │ ├── 31362c9c4b1271b607db1fee9cd218de540227 │ │ ├── 7b2d2ba5e3d3961dcb1e330d70f26a73ea1e7a │ │ ├── 7e9451424fc4da7117f432b7f38f67e8cae0db │ │ └── fd6d021923ff67ca5618e016f52654796f962a │ ├── bf │ │ └── 472f3ecbf062e37142889ef94a18bee5705d2f │ ├── c7 │ │ ├── 6325fb7f6ca5663d200d11b0505adf42f9b341 │ │ └── f236df46ca9de243200d5ae762f0564f6f6942 │ ├── c8 │ │ └── 272d8301080e32817022db4fbc7d5245a2b54b │ ├── ca │ │ └── b44255474d9184d8dbface7c4308c2cd08bf29 │ ├── cb │ │ └── 67381b16c67500110629cd983b1041922c6c4f │ ├── d0 │ │ └── 74bd934498806062b472d33998ac24f7046fa6 │ ├── d1 │ │ └── 0d1f8cb7d8e9b4b9ac167e6223615a5427c11d │ ├── d7 │ │ └── db1975765c5a47e47dab4625d8e500f33a00d0 │ ├── d8 │ │ ├── 7606e184811be77b9a74a6ea591a33121cdf84 │ │ └── bc0e62285de01b308578843ab85531fc27db21 │ ├── df │ │ └── 4372e038b7b20bf6ace646770cb4d55c4afb89 │ ├── e1 │ │ ├── acb58360077444a7a73a356aba5f00f7337f13 │ │ └── b627e244cb6dd4a11c6e73c912c67cded0ff64 │ ├── e2 │ │ └── 751c8a323568e46dd93c9838bb3db2976370fd │ ├── e5 │ │ ├── 95d75de522572d42183494b468c543b30023f9 │ │ └── c400bb42cdab54523c7f2453d9aea4f42f380b │ ├── e7 │ │ └── d435380f24ecac5dea5626d5d0a10410c4bef8 │ ├── e9 │ │ └── 437f4b129efe8656f8ce95a3ab1eafc5a2a2fb │ ├── ef │ │ └── 6886236f24d079d370e6172bf913abe76c4d0f │ ├── f1 │ │ └── 007da59971a4f15800bdb347b15fbed3944d20 │ ├── f4 │ │ ├── d9ba1d3637a143f2013a784f3876bff8520fcf │ │ └── f5dd1745f03ed23a33046e79e7ea0dce6168d6 │ ├── f5 │ │ └── 51947734655d8e948a4570687b2d43b4761a97 │ ├── f8 │ │ └── a9a00f7056fdccc381a87fccfc9228a999a013 │ ├── f9 │ │ └── 71dff8757acb34ce000d46a2bbf79c2de78555 │ ├── fb │ │ └── 91840cc695e055d250f96b766b3806f5955ed7 │ ├── ff │ │ ├── 36248efc79d24cc38f826ab15a2a2f8356a450 │ │ └── f7a6ce8c2c271d395e140dd3bcc209bf0ed499 │ └── pack │ │ ├── pack-1486bbf9bc147ffafa3769d62da3e2169f0b61a1.idx │ │ ├── pack-1486bbf9bc147ffafa3769d62da3e2169f0b61a1.pack │ │ └── pack-1486bbf9bc147ffafa3769d62da3e2169f0b61a1.rev ├── packed-refs └── refs │ ├── heads │ └── main │ └── remotes │ └── origin │ ├── HEAD │ └── main ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── apps ├── accounts │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── auth.py │ ├── emails.py │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_user_access_user_refresh_delete_jwt.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── chat │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── consumers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_remove_chat_dm_chat_constraints_and_more.py │ │ ├── 0003_remove_chat_dm_chat_constraints_and_more.py │ │ ├── 0004_remove_chat_dm_chat_constraints_and_more.py │ │ ├── 0005_remove_chat_dm_chat_constraints_and_more.py │ │ ├── 0006_alter_chat_description.py │ │ ├── 0007_remove_chat_dm_chat_constraints_and_more.py │ │ ├── 0008_remove_chat_dm_chat_constraints_and_more.py │ │ ├── 0009_remove_chat_dm_chat_constraints_and_more.py │ │ ├── 0010_remove_chat_dm_chat_constraints_remove_chat_users_and_more.py │ │ ├── 0011_remove_chat_user_ids_chat_users.py │ │ ├── 0012_message.py │ │ ├── 0013_remove_chat_dm_chat_constraints_and_more.py │ │ ├── 0014_alter_chat_users.py │ │ ├── 0015_chat_group_chat_constraints.py │ │ ├── 0016_remove_chat_dm_chat_constraints_and_more.py │ │ ├── 0017_remove_chat_group_chat_constraints_and_more.py │ │ ├── 0018_alter_message_options_alter_message_file_and_more.py │ │ ├── 0019_alter_message_text.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── socket_serializers.py │ ├── tests.py │ ├── urls.py │ ├── utils.py │ ├── validators.py │ └── views.py ├── common │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── consumers.py │ ├── error.py │ ├── exceptions.py │ ├── file_processors.py │ ├── file_types.py │ ├── management │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── data_script.py │ │ │ └── initial_data.py │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── paginators.py │ ├── responses.py │ ├── schema_examples.py │ ├── serializers.py │ ├── socket_auth.py │ ├── utils.py │ └── validators.py ├── feed │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── general │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py └── profiles │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── consumers.py │ ├── migrations │ ├── 0001_initial.py │ ├── 0002_remove_friend_unique_user_combination_and_more.py │ ├── 0003_remove_friend_unique_user_combination_and_more.py │ ├── 0004_remove_friend_unique_user_combination_and_more.py │ ├── 0005_remove_friend_different_users_and_more.py │ ├── 0006_alter_friend_status.py │ ├── 0007_notification.py │ ├── 0008_notification_ntype.py │ ├── 0009_alter_notification_comment_alter_notification_ntype_and_more.py │ ├── 0010_alter_notification_ntype_and_more.py │ ├── 0011_alter_notification_read_by_alter_notification_text.py │ ├── 0012_alter_notification_receivers_and_more.py │ ├── 0013_alter_notification_sender.py │ ├── 0014_remove_notification_selected_object_constraints_and_more.py │ ├── 0015_remove_notification_selected_object_constraints_and_more.py │ ├── 0016_alter_notification_text.py │ ├── 0017_remove_notification_sender_text_type_constraints_and_more.py │ ├── 0018_alter_notification_receivers.py │ ├── 0019_alter_notification_comment_alter_notification_post_and_more.py │ ├── 0020_notification_host_notification_secured.py │ ├── 0021_alter_notification_text.py │ ├── 0022_remove_notification_host_remove_notification_secured.py │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── build_files.sh ├── display ├── display1.png ├── display10.png ├── display2.png ├── display3.png ├── display4.png ├── display5.png ├── display6.png ├── display7.png ├── display8.png ├── display9.png └── drf.png ├── docker-compose.yml ├── docker ├── entrypoint └── start ├── manage.py ├── pytest.ini ├── requirements.txt ├── socialnet ├── __init__.py ├── asgi.py ├── settings │ ├── base.py │ ├── dev.py │ └── prod.py ├── urls.py └── wsgi.py ├── static └── media │ └── logo.png ├── staticfiles └── mt ├── templates ├── email-activation.html ├── password-reset-success.html ├── password-reset.html └── welcome.html └── vercel.json /.dockerignore: -------------------------------------------------------------------------------- 1 | README.md 2 | .gitignore 3 | data 4 | .vscode 5 | venv 6 | .venv 7 | letsencrypt 8 | poetry.lock 9 | pyproject.toml 10 | .git/ 11 | 12 | build 13 | dist 14 | *.egg-info 15 | *.egg/ 16 | *.pyc 17 | *.swp 18 | 19 | .tox 20 | .coverage 21 | .pytest_cache 22 | 23 | html/* 24 | 25 | __pycache__ -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | SITE_NAME= 2 | SECRET_KEY= 3 | DJANGO_SETTINGS_MODULE=dev 4 | ALLOWED_HOSTS= 5 | EMAIL_OTP_EXPIRE_SECONDS= 6 | ACCESS_TOKEN_EXPIRE_MINUTES= 7 | REFRESH_TOKEN_EXPIRE_MINUTES= 8 | FRONTEND_URL= 9 | FIRST_SUPERUSER_EMAIL= 10 | FIRST_SUPERUSER_PASSWORD= 11 | FIRST_CLIENT_EMAIL= 12 | FIRST_CLIENT_PASSWORD= 13 | POSTGRES_USER= 14 | POSTGRES_PASSWORD= 15 | POSTGRES_SERVER= 16 | POSTGRES_PORT= 17 | POSTGRES_DB= 18 | EMAIL_HOST_USER= 19 | EMAIL_HOST_PASSWORD= 20 | EMAIL_HOST= 21 | EMAIL_PORT= 22 | EMAIL_USE_SSL= 23 | DEFAULT_FROM_EMAIL= 24 | CORS_ALLOWED_ORIGINS= 25 | CLOUDINARY_CLOUD_NAME= 26 | CLOUDINARY_API_KEY= 27 | CLOUDINARY_API_SECRET= 28 | REDIS_URL= 29 | PORT= 30 | SOCKET_SECRET= -------------------------------------------------------------------------------- /.git-old/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | serializers: chats 2 | -------------------------------------------------------------------------------- /.git-old/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/main 2 | -------------------------------------------------------------------------------- /.git-old/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | [remote "origin"] 7 | url = git@github.com:kayprogrammer/socialnet-v1.git 8 | fetch = +refs/heads/*:refs/remotes/origin/* 9 | [branch "main"] 10 | remote = origin 11 | merge = refs/heads/main 12 | -------------------------------------------------------------------------------- /.git-old/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /.git-old/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | commitmsg="$(git rev-parse --git-path hooks/commit-msg)" 14 | test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /.git-old/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by "git commit" with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /.git-old/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git update-server-info 9 | -------------------------------------------------------------------------------- /.git-old/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | precommit="$(git rev-parse --git-path hooks/pre-commit)" 13 | test -x "$precommit" && exec "$precommit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /.git-old/hooks/pre-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git commit" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | 10 | if git rev-parse --verify HEAD >/dev/null 2>&1 11 | then 12 | against=HEAD 13 | else 14 | # Initial commit: diff against an empty tree object 15 | against=$(git hash-object -t tree /dev/null) 16 | fi 17 | 18 | # If you want to allow non-ASCII filenames set this variable to true. 19 | allownonascii=$(git config --type=bool hooks.allownonascii) 20 | 21 | # Redirect output to stderr. 22 | exec 1>&2 23 | 24 | # Cross platform projects tend to avoid non-ASCII filenames; prevent 25 | # them from being added to the repository. We exploit the fact that the 26 | # printable range starts at the space character and ends with tilde. 27 | if [ "$allownonascii" != "true" ] && 28 | # Note that the use of brackets around a tr range is ok here, (it's 29 | # even required, for portability to Solaris 10's /usr/bin/tr), since 30 | # the square bracket bytes happen to fall in the designated range. 31 | test $(git diff --cached --name-only --diff-filter=A -z $against | 32 | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 33 | then 34 | cat <<\EOF 35 | Error: Attempt to add a non-ASCII file name. 36 | 37 | This can cause problems if you want to work with people on other platforms. 38 | 39 | To be portable it is advisable to rename the file. 40 | 41 | If you know what you are doing you can disable this check using: 42 | 43 | git config hooks.allownonascii true 44 | EOF 45 | exit 1 46 | fi 47 | 48 | # If there are whitespace errors, print the offending file names and fail. 49 | exec git diff-index --check --cached $against -- 50 | -------------------------------------------------------------------------------- /.git-old/hooks/pre-merge-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git merge" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message to 6 | # stderr if it wants to stop the merge commit. 7 | # 8 | # To enable this hook, rename this file to "pre-merge-commit". 9 | 10 | . git-sh-setup 11 | test -x "$GIT_DIR/hooks/pre-commit" && 12 | exec "$GIT_DIR/hooks/pre-commit" 13 | : 14 | -------------------------------------------------------------------------------- /.git-old/hooks/pre-push.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to verify what is about to be pushed. Called by "git 4 | # push" after it has checked the remote status, but before anything has been 5 | # pushed. If this script exits with a non-zero status nothing will be pushed. 6 | # 7 | # This hook is called with the following parameters: 8 | # 9 | # $1 -- Name of the remote to which the push is being done 10 | # $2 -- URL to which the push is being done 11 | # 12 | # If pushing without using a named remote those arguments will be equal. 13 | # 14 | # Information about the commits which are being pushed is supplied as lines to 15 | # the standard input in the form: 16 | # 17 | # 18 | # 19 | # This sample shows how to prevent push of commits where the log message starts 20 | # with "WIP" (work in progress). 21 | 22 | remote="$1" 23 | url="$2" 24 | 25 | zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing" 48 | exit 1 49 | fi 50 | fi 51 | done 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /.git-old/hooks/pre-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to make use of push options. 4 | # The example simply echoes all push options that start with 'echoback=' 5 | # and rejects all pushes when the "reject" push option is used. 6 | # 7 | # To enable this hook, rename this file to "pre-receive". 8 | 9 | if test -n "$GIT_PUSH_OPTION_COUNT" 10 | then 11 | i=0 12 | while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" 13 | do 14 | eval "value=\$GIT_PUSH_OPTION_$i" 15 | case "$value" in 16 | echoback=*) 17 | echo "echo from the pre-receive-hook: ${value#*=}" >&2 18 | ;; 19 | reject) 20 | exit 1 21 | esac 22 | i=$((i + 1)) 23 | done 24 | fi 25 | -------------------------------------------------------------------------------- /.git-old/hooks/prepare-commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare the commit log message. 4 | # Called by "git commit" with the name of the file that has the 5 | # commit message, followed by the description of the commit 6 | # message's source. The hook's purpose is to edit the commit 7 | # message file. If the hook fails with a non-zero status, 8 | # the commit is aborted. 9 | # 10 | # To enable this hook, rename this file to "prepare-commit-msg". 11 | 12 | # This hook includes three examples. The first one removes the 13 | # "# Please enter the commit message..." help message. 14 | # 15 | # The second includes the output of "git diff --name-status -r" 16 | # into the message, just before the "git status" output. It is 17 | # commented because it doesn't cope with --amend or with squashed 18 | # commits. 19 | # 20 | # The third example adds a Signed-off-by line to the message, that can 21 | # still be edited. This is rarely a good idea. 22 | 23 | COMMIT_MSG_FILE=$1 24 | COMMIT_SOURCE=$2 25 | SHA1=$3 26 | 27 | /usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" 28 | 29 | # case "$COMMIT_SOURCE,$SHA1" in 30 | # ,|template,) 31 | # /usr/bin/perl -i.bak -pe ' 32 | # print "\n" . `git diff --cached --name-status -r` 33 | # if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; 34 | # *) ;; 35 | # esac 36 | 37 | # SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 38 | # git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" 39 | # if test -z "$COMMIT_SOURCE" 40 | # then 41 | # /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" 42 | # fi 43 | -------------------------------------------------------------------------------- /.git-old/hooks/push-to-checkout.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to update a checked-out tree on a git push. 4 | # 5 | # This hook is invoked by git-receive-pack(1) when it reacts to git 6 | # push and updates reference(s) in its repository, and when the push 7 | # tries to update the branch that is currently checked out and the 8 | # receive.denyCurrentBranch configuration variable is set to 9 | # updateInstead. 10 | # 11 | # By default, such a push is refused if the working tree and the index 12 | # of the remote repository has any difference from the currently 13 | # checked out commit; when both the working tree and the index match 14 | # the current commit, they are updated to match the newly pushed tip 15 | # of the branch. This hook is to be used to override the default 16 | # behaviour; however the code below reimplements the default behaviour 17 | # as a starting point for convenient modification. 18 | # 19 | # The hook receives the commit with which the tip of the current 20 | # branch is going to be updated: 21 | commit=$1 22 | 23 | # It can exit with a non-zero status to refuse the push (when it does 24 | # so, it must not modify the index or the working tree). 25 | die () { 26 | echo >&2 "$*" 27 | exit 1 28 | } 29 | 30 | # Or it can make any necessary changes to the working tree and to the 31 | # index to bring them to the desired state when the tip of the current 32 | # branch is updated to the new commit, and exit with a zero status. 33 | # 34 | # For example, the hook can simply run git read-tree -u -m HEAD "$1" 35 | # in order to emulate git fetch that is run in the reverse direction 36 | # with git push, as the two-tree form of git read-tree -u -m is 37 | # essentially the same as git switch or git checkout that switches 38 | # branches while keeping the local changes in the working tree that do 39 | # not interfere with the difference between the branches. 40 | 41 | # The below is a more-or-less exact translation to shell of the C code 42 | # for the default behaviour for git's push-to-checkout hook defined in 43 | # the push_to_deploy() function in builtin/receive-pack.c. 44 | # 45 | # Note that the hook will be executed from the repository directory, 46 | # not from the working tree, so if you want to perform operations on 47 | # the working tree, you will have to adapt your code accordingly, e.g. 48 | # by adding "cd .." or using relative paths. 49 | 50 | if ! git update-index -q --ignore-submodules --refresh 51 | then 52 | die "Up-to-date check failed" 53 | fi 54 | 55 | if ! git diff-files --quiet --ignore-submodules -- 56 | then 57 | die "Working directory has unstaged changes" 58 | fi 59 | 60 | # This is a rough translation of: 61 | # 62 | # head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX 63 | if git cat-file -e HEAD 2>/dev/null 64 | then 65 | head=HEAD 66 | else 67 | head=$(git hash-object -t tree --stdin &2 59 | exit 1 60 | } 61 | 62 | unset GIT_DIR GIT_WORK_TREE 63 | cd "$worktree" && 64 | 65 | if grep -q "^diff --git " "$1" 66 | then 67 | validate_patch "$1" 68 | else 69 | validate_cover_letter "$1" 70 | fi && 71 | 72 | if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL" 73 | then 74 | git config --unset-all sendemail.validateWorktree && 75 | trap 'git worktree remove -ff "$worktree"' EXIT && 76 | validate_series 77 | fi 78 | -------------------------------------------------------------------------------- /.git-old/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/index -------------------------------------------------------------------------------- /.git-old/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /.git-old/objects/02/e9ce1b48e6fc035b9a1e892bf2d92dcb333f51: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/02/e9ce1b48e6fc035b9a1e892bf2d92dcb333f51 -------------------------------------------------------------------------------- /.git-old/objects/04/113fd1c20061c5a971cba6d5c92b1b9dbea756: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/04/113fd1c20061c5a971cba6d5c92b1b9dbea756 -------------------------------------------------------------------------------- /.git-old/objects/08/d1e4f0a86bb71522c2b7867e215754c98a2ea6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/08/d1e4f0a86bb71522c2b7867e215754c98a2ea6 -------------------------------------------------------------------------------- /.git-old/objects/09/1a9e41d5ca15e880114d7d484dbbb8bd711b4e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/09/1a9e41d5ca15e880114d7d484dbbb8bd711b4e -------------------------------------------------------------------------------- /.git-old/objects/09/b97a6fa22188b62b22236f07366bd7f27ffd42: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/09/b97a6fa22188b62b22236f07366bd7f27ffd42 -------------------------------------------------------------------------------- /.git-old/objects/0b/9dd54cac4e037eaaf7e84eb26ba02ee5cc489f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/0b/9dd54cac4e037eaaf7e84eb26ba02ee5cc489f -------------------------------------------------------------------------------- /.git-old/objects/0b/c6aaeb893447c1317f1993de2cbf1411ddeb89: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/0b/c6aaeb893447c1317f1993de2cbf1411ddeb89 -------------------------------------------------------------------------------- /.git-old/objects/0c/5190a66f6a8de5c89782bb7e9bd8614a8f2254: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/0c/5190a66f6a8de5c89782bb7e9bd8614a8f2254 -------------------------------------------------------------------------------- /.git-old/objects/0c/c300404fb4014f59db118441c7122a030bb6b7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/0c/c300404fb4014f59db118441c7122a030bb6b7 -------------------------------------------------------------------------------- /.git-old/objects/10/003f5c31e8bed26f59b7fe9326a415ffc2e5a2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/10/003f5c31e8bed26f59b7fe9326a415ffc2e5a2 -------------------------------------------------------------------------------- /.git-old/objects/11/a55c9420dbb3ba7e8a74401d21df9e70fa3a51: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/11/a55c9420dbb3ba7e8a74401d21df9e70fa3a51 -------------------------------------------------------------------------------- /.git-old/objects/11/ae12ab68bcc622ae3c0a17c66c9ceb8a9b236d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/11/ae12ab68bcc622ae3c0a17c66c9ceb8a9b236d -------------------------------------------------------------------------------- /.git-old/objects/11/f598f10052cebbc27f2ef90f01a870a01077c7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/11/f598f10052cebbc27f2ef90f01a870a01077c7 -------------------------------------------------------------------------------- /.git-old/objects/12/78debc927fd60bab7e090a31534dd70057b37b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/12/78debc927fd60bab7e090a31534dd70057b37b -------------------------------------------------------------------------------- /.git-old/objects/12/b42c0f684464e79ab5f9d612bdecaaf4900436: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/12/b42c0f684464e79ab5f9d612bdecaaf4900436 -------------------------------------------------------------------------------- /.git-old/objects/14/5f4b467baf644f1c33193182b7b2551e2e8b6f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/14/5f4b467baf644f1c33193182b7b2551e2e8b6f -------------------------------------------------------------------------------- /.git-old/objects/17/3da64dcf9be36843b38c3763a5b6e455251aec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/17/3da64dcf9be36843b38c3763a5b6e455251aec -------------------------------------------------------------------------------- /.git-old/objects/19/feb596e924525f775afb13632a71f3aaf783b4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/19/feb596e924525f775afb13632a71f3aaf783b4 -------------------------------------------------------------------------------- /.git-old/objects/1b/a787e6d247041d4ca5dd593357a04add01e5cf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/1b/a787e6d247041d4ca5dd593357a04add01e5cf -------------------------------------------------------------------------------- /.git-old/objects/1b/ea1c678719ee16e4c858153d238370caefe4fa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/1b/ea1c678719ee16e4c858153d238370caefe4fa -------------------------------------------------------------------------------- /.git-old/objects/1d/399106b712b43a9d3265193a9590d412dafd34: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/1d/399106b712b43a9d3265193a9590d412dafd34 -------------------------------------------------------------------------------- /.git-old/objects/1d/8db26ff99178b8e53efd46c0f798360b8db4eb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/1d/8db26ff99178b8e53efd46c0f798360b8db4eb -------------------------------------------------------------------------------- /.git-old/objects/1d/ca6a5583a283d633d42ae79e2978f6608f46d6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/1d/ca6a5583a283d633d42ae79e2978f6608f46d6 -------------------------------------------------------------------------------- /.git-old/objects/1e/212b4fefbf1ffcf3460c2f26b7c7fa3ac32e15: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/1e/212b4fefbf1ffcf3460c2f26b7c7fa3ac32e15 -------------------------------------------------------------------------------- /.git-old/objects/1f/b6aee898d115b0bbd03e051b3fe1eae5923d80: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/1f/b6aee898d115b0bbd03e051b3fe1eae5923d80 -------------------------------------------------------------------------------- /.git-old/objects/1f/f9452d5b9f24e89df0f8536b067839b4860448: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/1f/f9452d5b9f24e89df0f8536b067839b4860448 -------------------------------------------------------------------------------- /.git-old/objects/20/d084ca68de2172e257b05e3e9d276f93f68af0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/20/d084ca68de2172e257b05e3e9d276f93f68af0 -------------------------------------------------------------------------------- /.git-old/objects/23/216d3302517dcda33f2ece120c55bc9d98aa57: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/23/216d3302517dcda33f2ece120c55bc9d98aa57 -------------------------------------------------------------------------------- /.git-old/objects/24/4c6d2d3655caf6f325157525b877e0ee5d0264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/24/4c6d2d3655caf6f325157525b877e0ee5d0264 -------------------------------------------------------------------------------- /.git-old/objects/24/54499bf59464fa31cb7faf1cdc5f1a30d875e1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/24/54499bf59464fa31cb7faf1cdc5f1a30d875e1 -------------------------------------------------------------------------------- /.git-old/objects/26/500c442db5b30913257686879bf8a25586a59b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/26/500c442db5b30913257686879bf8a25586a59b -------------------------------------------------------------------------------- /.git-old/objects/27/f51cef71a06ebc13d428ec5aa450f22bb81e3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/27/f51cef71a06ebc13d428ec5aa450f22bb81e3d -------------------------------------------------------------------------------- /.git-old/objects/29/a7f0d7b23dc83862d5a0aeb20a6daf6ba5c5a2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/29/a7f0d7b23dc83862d5a0aeb20a6daf6ba5c5a2 -------------------------------------------------------------------------------- /.git-old/objects/29/be2e90a7e51ea44e8a8bb080a71fc6042ab8dd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/29/be2e90a7e51ea44e8a8bb080a71fc6042ab8dd -------------------------------------------------------------------------------- /.git-old/objects/2a/75eeeb596abfdd6e88f48b88183600539df1a4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/2a/75eeeb596abfdd6e88f48b88183600539df1a4 -------------------------------------------------------------------------------- /.git-old/objects/2e/d88e025b8609f51020a8c454dbb5508c24b624: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/2e/d88e025b8609f51020a8c454dbb5508c24b624 -------------------------------------------------------------------------------- /.git-old/objects/2f/e0f25563d5ff10af0f040a1ff9610a2e58cc18: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/2f/e0f25563d5ff10af0f040a1ff9610a2e58cc18 -------------------------------------------------------------------------------- /.git-old/objects/31/23ed14d6cb5789f21b70c31f0182bfaa234661: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/31/23ed14d6cb5789f21b70c31f0182bfaa234661 -------------------------------------------------------------------------------- /.git-old/objects/32/3f03292c2d0a68a5e9e03aeaaafbebcc4cb55e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/32/3f03292c2d0a68a5e9e03aeaaafbebcc4cb55e -------------------------------------------------------------------------------- /.git-old/objects/36/069196631fc29fd9c96e819482d0ac54cb4c0e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/36/069196631fc29fd9c96e819482d0ac54cb4c0e -------------------------------------------------------------------------------- /.git-old/objects/39/138523f0ca6add46cb3e9e0eb66156d7cfd6e3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/39/138523f0ca6add46cb3e9e0eb66156d7cfd6e3 -------------------------------------------------------------------------------- /.git-old/objects/39/7ae8878fa8fdd6475da83d6b1a00640e4fc38a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/39/7ae8878fa8fdd6475da83d6b1a00640e4fc38a -------------------------------------------------------------------------------- /.git-old/objects/3a/a19c647b07528dd85d921f4d8363f033c1794a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/3a/a19c647b07528dd85d921f4d8363f033c1794a -------------------------------------------------------------------------------- /.git-old/objects/3a/a86073b91e2fd27bff096ed138399c15681389: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/3a/a86073b91e2fd27bff096ed138399c15681389 -------------------------------------------------------------------------------- /.git-old/objects/3c/1087abe4350a6595828536d82bfd0282daa36e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/3c/1087abe4350a6595828536d82bfd0282daa36e -------------------------------------------------------------------------------- /.git-old/objects/3f/56f2539226d0d05cc9ff68303f808fcbb63c8f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/3f/56f2539226d0d05cc9ff68303f808fcbb63c8f -------------------------------------------------------------------------------- /.git-old/objects/40/b714c0050bc4d9284203ef1ec1309e35a54eab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/40/b714c0050bc4d9284203ef1ec1309e35a54eab -------------------------------------------------------------------------------- /.git-old/objects/41/405aff8f28edfdf66c251ce9f4b557c9580804: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/41/405aff8f28edfdf66c251ce9f4b557c9580804 -------------------------------------------------------------------------------- /.git-old/objects/41/5fa73d2738b1f381268d6ae88d8b6bfa392856: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/41/5fa73d2738b1f381268d6ae88d8b6bfa392856 -------------------------------------------------------------------------------- /.git-old/objects/41/8c78fa174789b42f43235b63ab045d2d0123a0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/41/8c78fa174789b42f43235b63ab045d2d0123a0 -------------------------------------------------------------------------------- /.git-old/objects/46/1aebde340af5cb7907335e020ddac212a614ba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/46/1aebde340af5cb7907335e020ddac212a614ba -------------------------------------------------------------------------------- /.git-old/objects/48/af258320389b89a5c8e5e4656b4bb2f8147a55: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/48/af258320389b89a5c8e5e4656b4bb2f8147a55 -------------------------------------------------------------------------------- /.git-old/objects/49/6c4c2e8ea7033ffac7bc9192c3865268b1b026: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/49/6c4c2e8ea7033ffac7bc9192c3865268b1b026 -------------------------------------------------------------------------------- /.git-old/objects/4a/24ea3c81768cbfe99333e77d98fd3258fb1646: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/4a/24ea3c81768cbfe99333e77d98fd3258fb1646 -------------------------------------------------------------------------------- /.git-old/objects/4b/066a9350dd2173c53c5381bf233833cf340880: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/4b/066a9350dd2173c53c5381bf233833cf340880 -------------------------------------------------------------------------------- /.git-old/objects/4b/ed1c3c926bc7e56c8509d6c97ce538486debcc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/4b/ed1c3c926bc7e56c8509d6c97ce538486debcc -------------------------------------------------------------------------------- /.git-old/objects/4c/23be44d8f139d6ab2d5d0a8b00998f134793f0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/4c/23be44d8f139d6ab2d5d0a8b00998f134793f0 -------------------------------------------------------------------------------- /.git-old/objects/50/8f41b442ec42c5890f8ea771ace8d3a3127d49: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/50/8f41b442ec42c5890f8ea771ace8d3a3127d49 -------------------------------------------------------------------------------- /.git-old/objects/51/c3781e5ccb396a0e6ece38adf478b7b9279095: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/51/c3781e5ccb396a0e6ece38adf478b7b9279095 -------------------------------------------------------------------------------- /.git-old/objects/52/65c3fda2cfecdcbeb500ba6eabac5ab665d622: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/52/65c3fda2cfecdcbeb500ba6eabac5ab665d622 -------------------------------------------------------------------------------- /.git-old/objects/56/8515825e7fd04817d7911634e9f9075441c53c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/56/8515825e7fd04817d7911634e9f9075441c53c -------------------------------------------------------------------------------- /.git-old/objects/57/abf907e474f2bcc739fecceb78ae52e9ade479: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/57/abf907e474f2bcc739fecceb78ae52e9ade479 -------------------------------------------------------------------------------- /.git-old/objects/57/f4765c35fcc4dd8be95881dabf1e6df6cb36ab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/57/f4765c35fcc4dd8be95881dabf1e6df6cb36ab -------------------------------------------------------------------------------- /.git-old/objects/58/54ab2b7de551488182495193b15d790bfc8a73: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/58/54ab2b7de551488182495193b15d790bfc8a73 -------------------------------------------------------------------------------- /.git-old/objects/58/d43cdfac380818c18b35622659cf4c79a7b360: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/58/d43cdfac380818c18b35622659cf4c79a7b360 -------------------------------------------------------------------------------- /.git-old/objects/5b/838d9b00e07b3272656dcadb59ca3dfa2a85cd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/5b/838d9b00e07b3272656dcadb59ca3dfa2a85cd -------------------------------------------------------------------------------- /.git-old/objects/5d/20e021ccea7ea221b79a773f10f1fd59609454: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/5d/20e021ccea7ea221b79a773f10f1fd59609454 -------------------------------------------------------------------------------- /.git-old/objects/5d/62881c9a9eeb48b5580ae4fcbf337affa0cc8f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/5d/62881c9a9eeb48b5580ae4fcbf337affa0cc8f -------------------------------------------------------------------------------- /.git-old/objects/5d/80d5932b8464d41dbe8abe3ded51dee1ed9200: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/5d/80d5932b8464d41dbe8abe3ded51dee1ed9200 -------------------------------------------------------------------------------- /.git-old/objects/5d/a7b00ed3c5b4eee4a65b3dbf537221e18a18d6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/5d/a7b00ed3c5b4eee4a65b3dbf537221e18a18d6 -------------------------------------------------------------------------------- /.git-old/objects/5f/d8fca418ee7cfbc062b56b1f96722c4bc4e745: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/5f/d8fca418ee7cfbc062b56b1f96722c4bc4e745 -------------------------------------------------------------------------------- /.git-old/objects/60/0f8ace02f527f8eb158f73180913cb433cc561: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/60/0f8ace02f527f8eb158f73180913cb433cc561 -------------------------------------------------------------------------------- /.git-old/objects/60/8db8e485709223f1f21b4c3b25baf6d24687ad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/60/8db8e485709223f1f21b4c3b25baf6d24687ad -------------------------------------------------------------------------------- /.git-old/objects/61/1e7982410fb8033a892f6cc0525060c60712b4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/61/1e7982410fb8033a892f6cc0525060c60712b4 -------------------------------------------------------------------------------- /.git-old/objects/62/bed2d79fa62fe34c7b8414a3075b973505fa0b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/62/bed2d79fa62fe34c7b8414a3075b973505fa0b -------------------------------------------------------------------------------- /.git-old/objects/63/942815d71cabf073ef8771358e0e4c9fe9f461: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/63/942815d71cabf073ef8771358e0e4c9fe9f461 -------------------------------------------------------------------------------- /.git-old/objects/66/a63e8758dff2e2a0bee94f958a60ebff19f957: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/66/a63e8758dff2e2a0bee94f958a60ebff19f957 -------------------------------------------------------------------------------- /.git-old/objects/66/ae7997209d1fc2756365d0346f728fadd83cb5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/66/ae7997209d1fc2756365d0346f728fadd83cb5 -------------------------------------------------------------------------------- /.git-old/objects/67/22c93fc28b24a9fe632943c6361956c10d5e60: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/67/22c93fc28b24a9fe632943c6361956c10d5e60 -------------------------------------------------------------------------------- /.git-old/objects/67/e2b928a89ccf6d608f680b5f085dfff6a68c72: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/67/e2b928a89ccf6d608f680b5f085dfff6a68c72 -------------------------------------------------------------------------------- /.git-old/objects/68/0f646c28039145e256fbd1e7423fcb64b6f1d4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/68/0f646c28039145e256fbd1e7423fcb64b6f1d4 -------------------------------------------------------------------------------- /.git-old/objects/69/33d225627ca27d5be2342db73df25656c83f76: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/69/33d225627ca27d5be2342db73df25656c83f76 -------------------------------------------------------------------------------- /.git-old/objects/69/c65bb2691aa69bb70d76278a1c8f5bcf1e8da4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/69/c65bb2691aa69bb70d76278a1c8f5bcf1e8da4 -------------------------------------------------------------------------------- /.git-old/objects/6a/4fc4416149400fdcb08e3c3495d1a32e82498a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/6a/4fc4416149400fdcb08e3c3495d1a32e82498a -------------------------------------------------------------------------------- /.git-old/objects/6a/d0f5044b4970e2a8b391d21d113eb082951c7e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/6a/d0f5044b4970e2a8b391d21d113eb082951c7e -------------------------------------------------------------------------------- /.git-old/objects/6b/8b2b1d4cfbbdec30d88079bf0a24a505cd57fc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/6b/8b2b1d4cfbbdec30d88079bf0a24a505cd57fc -------------------------------------------------------------------------------- /.git-old/objects/6d/b941908c461425824528a750b814cf78c5e92e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/6d/b941908c461425824528a750b814cf78c5e92e -------------------------------------------------------------------------------- /.git-old/objects/6e/d71cf013a97b7c33acc03c279953ad96d2b7d1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/6e/d71cf013a97b7c33acc03c279953ad96d2b7d1 -------------------------------------------------------------------------------- /.git-old/objects/74/3a88e0c239fa209cc90677c937960ea221deb7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/74/3a88e0c239fa209cc90677c937960ea221deb7 -------------------------------------------------------------------------------- /.git-old/objects/75/bfade613d6e89313e92bb0f9dbcfe1f52726c4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/75/bfade613d6e89313e92bb0f9dbcfe1f52726c4 -------------------------------------------------------------------------------- /.git-old/objects/76/0a8684f45047be77b8dddfc4937e8218af83af: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/76/0a8684f45047be77b8dddfc4937e8218af83af -------------------------------------------------------------------------------- /.git-old/objects/76/1ede33462ce60eb76f5904efad89160254dacb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/76/1ede33462ce60eb76f5904efad89160254dacb -------------------------------------------------------------------------------- /.git-old/objects/79/740d5491fd65c6d6495cec72e2da50fb5683a7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/79/740d5491fd65c6d6495cec72e2da50fb5683a7 -------------------------------------------------------------------------------- /.git-old/objects/7a/79cf980fc4257778138e72197dbf16d8dbebe7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/7a/79cf980fc4257778138e72197dbf16d8dbebe7 -------------------------------------------------------------------------------- /.git-old/objects/7b/1cf1880b6153315177e4b37b8d479fa90364fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/7b/1cf1880b6153315177e4b37b8d479fa90364fd -------------------------------------------------------------------------------- /.git-old/objects/7b/efe9f111c760cd768ec22408300759706cd272: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/7b/efe9f111c760cd768ec22408300759706cd272 -------------------------------------------------------------------------------- /.git-old/objects/7c/35a716c260a19da15480a580bda54c37d0128b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/7c/35a716c260a19da15480a580bda54c37d0128b -------------------------------------------------------------------------------- /.git-old/objects/7e/05208910ed43e7e20a06f3be88cf60843f3acd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/7e/05208910ed43e7e20a06f3be88cf60843f3acd -------------------------------------------------------------------------------- /.git-old/objects/7e/6a3e64a157fd8d2df5a6714b80623cbfbbada2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/7e/6a3e64a157fd8d2df5a6714b80623cbfbbada2 -------------------------------------------------------------------------------- /.git-old/objects/81/15e8d1c3990483d0e4d079667d9a60b47d63f5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/81/15e8d1c3990483d0e4d079667d9a60b47d63f5 -------------------------------------------------------------------------------- /.git-old/objects/81/a60a62eb302d4a07d90ebe3d0d577f51507a75: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/81/a60a62eb302d4a07d90ebe3d0d577f51507a75 -------------------------------------------------------------------------------- /.git-old/objects/83/4b740aea3ba41b44127934554176b0cb0ee292: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/83/4b740aea3ba41b44127934554176b0cb0ee292 -------------------------------------------------------------------------------- /.git-old/objects/84/f6e78fcbeea0f407e4d436fdf86e5cf1b01f96: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/84/f6e78fcbeea0f407e4d436fdf86e5cf1b01f96 -------------------------------------------------------------------------------- /.git-old/objects/87/dc1818b1c2ceece345c4005ac4dc93934d9558: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/87/dc1818b1c2ceece345c4005ac4dc93934d9558 -------------------------------------------------------------------------------- /.git-old/objects/89/3464107c08da785804fd868efdfafa20297508: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/89/3464107c08da785804fd868efdfafa20297508 -------------------------------------------------------------------------------- /.git-old/objects/8b/b09d7649382c48513b7afcac75dab568daa384: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/8b/b09d7649382c48513b7afcac75dab568daa384 -------------------------------------------------------------------------------- /.git-old/objects/8d/57fbd07d7f8bb7dce0098ba71e0dac2a61e2f8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/8d/57fbd07d7f8bb7dce0098ba71e0dac2a61e2f8 -------------------------------------------------------------------------------- /.git-old/objects/8d/7ead74ecfee3dc156c0a832a8b086b58e0fd0f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/8d/7ead74ecfee3dc156c0a832a8b086b58e0fd0f -------------------------------------------------------------------------------- /.git-old/objects/8d/a2414af3aa04a246567732337bb738c51221b0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/8d/a2414af3aa04a246567732337bb738c51221b0 -------------------------------------------------------------------------------- /.git-old/objects/91/021494f4fd4cc4ae8bb98a42f97bc52eab5bc4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/91/021494f4fd4cc4ae8bb98a42f97bc52eab5bc4 -------------------------------------------------------------------------------- /.git-old/objects/93/37a3f7e4ae98843ac49fe66da587fd1fd6b934: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/93/37a3f7e4ae98843ac49fe66da587fd1fd6b934 -------------------------------------------------------------------------------- /.git-old/objects/93/58a62092f9992ddf43c1e10c690b0823a750a1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/93/58a62092f9992ddf43c1e10c690b0823a750a1 -------------------------------------------------------------------------------- /.git-old/objects/93/6247bb618aaf86dc8141842ebad52929f476e7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/93/6247bb618aaf86dc8141842ebad52929f476e7 -------------------------------------------------------------------------------- /.git-old/objects/96/8ad4797727ae3f67e66ab4811f2eda5a959ead: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/96/8ad4797727ae3f67e66ab4811f2eda5a959ead -------------------------------------------------------------------------------- /.git-old/objects/97/47186db34faaa694aa76e0bfeb2024c5212b43: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/97/47186db34faaa694aa76e0bfeb2024c5212b43 -------------------------------------------------------------------------------- /.git-old/objects/98/efbe7ce867c5b37367383dd379489903bf9bdc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/98/efbe7ce867c5b37367383dd379489903bf9bdc -------------------------------------------------------------------------------- /.git-old/objects/99/590a689e3612d31913b0ebdec2abf2129a7f85: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/99/590a689e3612d31913b0ebdec2abf2129a7f85 -------------------------------------------------------------------------------- /.git-old/objects/99/e0cf35845eaa1d97d51ad74c2d784fd477e96c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/99/e0cf35845eaa1d97d51ad74c2d784fd477e96c -------------------------------------------------------------------------------- /.git-old/objects/9e/3c03594c58bfdfa95c73d4a297307bdfe24301: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/9e/3c03594c58bfdfa95c73d4a297307bdfe24301 -------------------------------------------------------------------------------- /.git-old/objects/9e/755d2f90e43ad316f79b5c46c8d2459f9b1d08: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/9e/755d2f90e43ad316f79b5c46c8d2459f9b1d08 -------------------------------------------------------------------------------- /.git-old/objects/a0/2a3346e931840a80d27044fa04491c5eddbafe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a0/2a3346e931840a80d27044fa04491c5eddbafe -------------------------------------------------------------------------------- /.git-old/objects/a0/2ac4e4b810dbef3b4ec1b729d8527ea0249840: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a0/2ac4e4b810dbef3b4ec1b729d8527ea0249840 -------------------------------------------------------------------------------- /.git-old/objects/a1/48bada0a0ff7ad99a663cfaae0a182a0479810: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a1/48bada0a0ff7ad99a663cfaae0a182a0479810 -------------------------------------------------------------------------------- /.git-old/objects/a4/097c30a13412aaa38b3ddece23bacdb91ec1e5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a4/097c30a13412aaa38b3ddece23bacdb91ec1e5 -------------------------------------------------------------------------------- /.git-old/objects/a4/e437f7168cd980bb1ededcd7ae354c67304789: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a4/e437f7168cd980bb1ededcd7ae354c67304789 -------------------------------------------------------------------------------- /.git-old/objects/a5/2cd33871e21703d6ec429f12d99e62ed4a68e1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a5/2cd33871e21703d6ec429f12d99e62ed4a68e1 -------------------------------------------------------------------------------- /.git-old/objects/a5/44d467cd920b475d9fe29896e40b00750efa52: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a5/44d467cd920b475d9fe29896e40b00750efa52 -------------------------------------------------------------------------------- /.git-old/objects/a5/c6700c44185f7240f2864b41f3ece5e9220c91: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a5/c6700c44185f7240f2864b41f3ece5e9220c91 -------------------------------------------------------------------------------- /.git-old/objects/a5/c88b6f50f5a7faf632ef0dd3c0e492ecbccc6f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a5/c88b6f50f5a7faf632ef0dd3c0e492ecbccc6f -------------------------------------------------------------------------------- /.git-old/objects/a5/e51f8dabb71386204e8fa80436889fd280e899: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a5/e51f8dabb71386204e8fa80436889fd280e899 -------------------------------------------------------------------------------- /.git-old/objects/a5/fd5a6776c768e5f7d808bb903dbc4c6280a655: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a5/fd5a6776c768e5f7d808bb903dbc4c6280a655 -------------------------------------------------------------------------------- /.git-old/objects/a7/accd8cf790a4c1df30be240aad7c1822c83639: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a7/accd8cf790a4c1df30be240aad7c1822c83639 -------------------------------------------------------------------------------- /.git-old/objects/a7/ccd4b9cf93e7812d84e88d83c84615a177f018: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a7/ccd4b9cf93e7812d84e88d83c84615a177f018 -------------------------------------------------------------------------------- /.git-old/objects/a8/8d906887180a93117e1161693a4289f3396047: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a8/8d906887180a93117e1161693a4289f3396047 -------------------------------------------------------------------------------- /.git-old/objects/a8/b1814f61cbfeedaeff77d672f08aa4a921b0b5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/a8/b1814f61cbfeedaeff77d672f08aa4a921b0b5 -------------------------------------------------------------------------------- /.git-old/objects/aa/d30393fafd2c0cae1e69ec513b7eaf358939ef: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/aa/d30393fafd2c0cae1e69ec513b7eaf358939ef -------------------------------------------------------------------------------- /.git-old/objects/ac/de01ca6faf568409e9bfc9c22bd22f94e9b75f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/ac/de01ca6faf568409e9bfc9c22bd22f94e9b75f -------------------------------------------------------------------------------- /.git-old/objects/ad/224634da54212ca16841917164cfb752dc539d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/ad/224634da54212ca16841917164cfb752dc539d -------------------------------------------------------------------------------- /.git-old/objects/ad/4c8c36b340e58f17b445fd6da283a5d1a1c51f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/ad/4c8c36b340e58f17b445fd6da283a5d1a1c51f -------------------------------------------------------------------------------- /.git-old/objects/ae/3ccc596def51f401e8a4740bfb8ef07a515fde: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/ae/3ccc596def51f401e8a4740bfb8ef07a515fde -------------------------------------------------------------------------------- /.git-old/objects/b0/7148d02893395ece5b13dd839e880d671bd771: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/b0/7148d02893395ece5b13dd839e880d671bd771 -------------------------------------------------------------------------------- /.git-old/objects/b2/c427bda167d9c769833f6a18ad9820603fa08e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/b2/c427bda167d9c769833f6a18ad9820603fa08e -------------------------------------------------------------------------------- /.git-old/objects/b8/33ddd60b45a1c98b3ae2cbf5727a5d283b5cd9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/b8/33ddd60b45a1c98b3ae2cbf5727a5d283b5cd9 -------------------------------------------------------------------------------- /.git-old/objects/bd/31362c9c4b1271b607db1fee9cd218de540227: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/bd/31362c9c4b1271b607db1fee9cd218de540227 -------------------------------------------------------------------------------- /.git-old/objects/bd/7b2d2ba5e3d3961dcb1e330d70f26a73ea1e7a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/bd/7b2d2ba5e3d3961dcb1e330d70f26a73ea1e7a -------------------------------------------------------------------------------- /.git-old/objects/bd/7e9451424fc4da7117f432b7f38f67e8cae0db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/bd/7e9451424fc4da7117f432b7f38f67e8cae0db -------------------------------------------------------------------------------- /.git-old/objects/bd/fd6d021923ff67ca5618e016f52654796f962a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/bd/fd6d021923ff67ca5618e016f52654796f962a -------------------------------------------------------------------------------- /.git-old/objects/bf/472f3ecbf062e37142889ef94a18bee5705d2f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/bf/472f3ecbf062e37142889ef94a18bee5705d2f -------------------------------------------------------------------------------- /.git-old/objects/c7/6325fb7f6ca5663d200d11b0505adf42f9b341: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/c7/6325fb7f6ca5663d200d11b0505adf42f9b341 -------------------------------------------------------------------------------- /.git-old/objects/c7/f236df46ca9de243200d5ae762f0564f6f6942: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/c7/f236df46ca9de243200d5ae762f0564f6f6942 -------------------------------------------------------------------------------- /.git-old/objects/c8/272d8301080e32817022db4fbc7d5245a2b54b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/c8/272d8301080e32817022db4fbc7d5245a2b54b -------------------------------------------------------------------------------- /.git-old/objects/ca/b44255474d9184d8dbface7c4308c2cd08bf29: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/ca/b44255474d9184d8dbface7c4308c2cd08bf29 -------------------------------------------------------------------------------- /.git-old/objects/cb/67381b16c67500110629cd983b1041922c6c4f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/cb/67381b16c67500110629cd983b1041922c6c4f -------------------------------------------------------------------------------- /.git-old/objects/d0/74bd934498806062b472d33998ac24f7046fa6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/d0/74bd934498806062b472d33998ac24f7046fa6 -------------------------------------------------------------------------------- /.git-old/objects/d1/0d1f8cb7d8e9b4b9ac167e6223615a5427c11d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/d1/0d1f8cb7d8e9b4b9ac167e6223615a5427c11d -------------------------------------------------------------------------------- /.git-old/objects/d7/db1975765c5a47e47dab4625d8e500f33a00d0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/d7/db1975765c5a47e47dab4625d8e500f33a00d0 -------------------------------------------------------------------------------- /.git-old/objects/d8/7606e184811be77b9a74a6ea591a33121cdf84: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/d8/7606e184811be77b9a74a6ea591a33121cdf84 -------------------------------------------------------------------------------- /.git-old/objects/d8/bc0e62285de01b308578843ab85531fc27db21: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/d8/bc0e62285de01b308578843ab85531fc27db21 -------------------------------------------------------------------------------- /.git-old/objects/df/4372e038b7b20bf6ace646770cb4d55c4afb89: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/df/4372e038b7b20bf6ace646770cb4d55c4afb89 -------------------------------------------------------------------------------- /.git-old/objects/e1/acb58360077444a7a73a356aba5f00f7337f13: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/e1/acb58360077444a7a73a356aba5f00f7337f13 -------------------------------------------------------------------------------- /.git-old/objects/e1/b627e244cb6dd4a11c6e73c912c67cded0ff64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/e1/b627e244cb6dd4a11c6e73c912c67cded0ff64 -------------------------------------------------------------------------------- /.git-old/objects/e2/751c8a323568e46dd93c9838bb3db2976370fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/e2/751c8a323568e46dd93c9838bb3db2976370fd -------------------------------------------------------------------------------- /.git-old/objects/e5/95d75de522572d42183494b468c543b30023f9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/e5/95d75de522572d42183494b468c543b30023f9 -------------------------------------------------------------------------------- /.git-old/objects/e5/c400bb42cdab54523c7f2453d9aea4f42f380b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/e5/c400bb42cdab54523c7f2453d9aea4f42f380b -------------------------------------------------------------------------------- /.git-old/objects/e7/d435380f24ecac5dea5626d5d0a10410c4bef8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/e7/d435380f24ecac5dea5626d5d0a10410c4bef8 -------------------------------------------------------------------------------- /.git-old/objects/e9/437f4b129efe8656f8ce95a3ab1eafc5a2a2fb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/e9/437f4b129efe8656f8ce95a3ab1eafc5a2a2fb -------------------------------------------------------------------------------- /.git-old/objects/ef/6886236f24d079d370e6172bf913abe76c4d0f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/ef/6886236f24d079d370e6172bf913abe76c4d0f -------------------------------------------------------------------------------- /.git-old/objects/f1/007da59971a4f15800bdb347b15fbed3944d20: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/f1/007da59971a4f15800bdb347b15fbed3944d20 -------------------------------------------------------------------------------- /.git-old/objects/f4/d9ba1d3637a143f2013a784f3876bff8520fcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/f4/d9ba1d3637a143f2013a784f3876bff8520fcf -------------------------------------------------------------------------------- /.git-old/objects/f4/f5dd1745f03ed23a33046e79e7ea0dce6168d6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/f4/f5dd1745f03ed23a33046e79e7ea0dce6168d6 -------------------------------------------------------------------------------- /.git-old/objects/f5/51947734655d8e948a4570687b2d43b4761a97: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/f5/51947734655d8e948a4570687b2d43b4761a97 -------------------------------------------------------------------------------- /.git-old/objects/f8/a9a00f7056fdccc381a87fccfc9228a999a013: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/f8/a9a00f7056fdccc381a87fccfc9228a999a013 -------------------------------------------------------------------------------- /.git-old/objects/f9/71dff8757acb34ce000d46a2bbf79c2de78555: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/f9/71dff8757acb34ce000d46a2bbf79c2de78555 -------------------------------------------------------------------------------- /.git-old/objects/fb/91840cc695e055d250f96b766b3806f5955ed7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/fb/91840cc695e055d250f96b766b3806f5955ed7 -------------------------------------------------------------------------------- /.git-old/objects/ff/36248efc79d24cc38f826ab15a2a2f8356a450: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/ff/36248efc79d24cc38f826ab15a2a2f8356a450 -------------------------------------------------------------------------------- /.git-old/objects/ff/f7a6ce8c2c271d395e140dd3bcc209bf0ed499: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/ff/f7a6ce8c2c271d395e140dd3bcc209bf0ed499 -------------------------------------------------------------------------------- /.git-old/objects/pack/pack-1486bbf9bc147ffafa3769d62da3e2169f0b61a1.idx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/pack/pack-1486bbf9bc147ffafa3769d62da3e2169f0b61a1.idx -------------------------------------------------------------------------------- /.git-old/objects/pack/pack-1486bbf9bc147ffafa3769d62da3e2169f0b61a1.pack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/pack/pack-1486bbf9bc147ffafa3769d62da3e2169f0b61a1.pack -------------------------------------------------------------------------------- /.git-old/objects/pack/pack-1486bbf9bc147ffafa3769d62da3e2169f0b61a1.rev: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/.git-old/objects/pack/pack-1486bbf9bc147ffafa3769d62da3e2169f0b61a1.rev -------------------------------------------------------------------------------- /.git-old/packed-refs: -------------------------------------------------------------------------------- 1 | # pack-refs with: peeled fully-peeled sorted 2 | f93de5062a73c58c441809e038b7a128a51f0f60 refs/remotes/origin/main 3 | -------------------------------------------------------------------------------- /.git-old/refs/heads/main: -------------------------------------------------------------------------------- 1 | e1b627e244cb6dd4a11c6e73c912c67cded0ff64 2 | -------------------------------------------------------------------------------- /.git-old/refs/remotes/origin/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/remotes/origin/main 2 | -------------------------------------------------------------------------------- /.git-old/refs/remotes/origin/main: -------------------------------------------------------------------------------- 1 | e1b627e244cb6dd4a11c6e73c912c67cded0ff64 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim-buster 2 | 3 | ENV PYTHONDONTWRITEBYTECODE 1 4 | ENV PYTHONUNBUFFERED 1 5 | 6 | ENV APP_HOME=/build 7 | RUN mkdir $APP_HOME 8 | RUN mkdir $APP_HOME/staticfiles 9 | 10 | LABEL maintainer='kayprogrammer1@gmail.com' 11 | LABEL description="Development image for Socialnet API V1 Project" 12 | 13 | ENV PYTHONDONTWRITEBYTECODE 1 14 | 15 | ENV PYTHONUNBUFFERED 1 16 | 17 | # We create folder named build for our app. 18 | WORKDIR $APP_HOME 19 | 20 | COPY ./docker ./docker 21 | COPY ./.env . 22 | COPY ./requirements.txt . 23 | 24 | # We copy our app folder to the /build 25 | 26 | RUN pip install -r requirements.txt 27 | 28 | COPY ./docker/entrypoint /entrypoint 29 | RUN sed -i 's/\r$//g' /entrypoint 30 | RUN chmod +x /entrypoint 31 | 32 | COPY ./docker/start /start 33 | RUN sed -i 's/\r$//g' /start 34 | RUN chmod +x /start 35 | 36 | ENTRYPOINT [ "/entrypoint" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kenechi Ifeanyi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifneq (,$(wildcard ./.env)) 2 | include .env 3 | export 4 | ENV_FILE_PARAM = --env-file .env 5 | 6 | endif 7 | 8 | build: 9 | docker-compose up --build -d --remove-orphans 10 | 11 | up: 12 | docker-compose up -d 13 | 14 | down: 15 | docker-compose down 16 | 17 | show-logs: 18 | docker-compose logs 19 | 20 | serv: 21 | uvicorn socialnet.asgi:application --reload 22 | 23 | mmig: # run with "make mmig" or "make mmig app='app'" 24 | if [ -z "$(app)" ]; then \ 25 | python manage.py makemigrations; \ 26 | else \ 27 | python manage.py makemigrations "$(app)"; \ 28 | fi 29 | 30 | mig: # run with "make mig" or "make mig app='app'" 31 | if [ -z "$(app)" ]; then \ 32 | python manage.py migrate; \ 33 | else \ 34 | python manage.py migrate "$(app)"; \ 35 | fi 36 | 37 | cities: 38 | python manage.py cities_light 39 | 40 | init: 41 | python manage.py initial_data 42 | 43 | test: 44 | pytest --disable-warnings -vv -x 45 | 46 | shell: 47 | python manage.py shell 48 | 49 | suser: 50 | python manage.py createsuperuser 51 | 52 | cpass: 53 | python manage.py changepassword 54 | 55 | reqm: # Install requirements 56 | pip install -r requirements.txt 57 | 58 | ureqm: # Update requirements 59 | pip freeze > requirements.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SOCIALNET V1 2 | 3 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/drf.png?raw=true) 4 | 5 | 6 | #### DJANGO DOCS: [Documentation](https://docs.djangoproject.com/en/4.2/) 7 | #### DJANGO REST FRAMEWORK DOCS: [Documentation](https://www.django-rest-framework.org/) 8 | 9 | #### PG ADMIN: [Documentation](https://pgadmin.org) 10 | 11 | 12 | ## How to run locally 13 | 14 | * Download this repo or run: 15 | ```bash 16 | $ git clone git@github.com:kayprogrammer/socialnet-v1.git 17 | ``` 18 | 19 | #### In the root directory: 20 | - Install all dependencies 21 | ```bash 22 | $ pip install -r requirements.txt 23 | ``` 24 | - Create an `.env` file and copy the contents from the `.env.example` to the file and set the respective values. A postgres database can be created with PG ADMIN or psql 25 | 26 | - Run Locally 27 | ```bash 28 | $ python manage.py migrate 29 | ``` 30 | ```bash 31 | $ uvicorn socialnet.asgi:application --reload 32 | ``` 33 | 34 | - Run With Docker 35 | ```bash 36 | $ docker-compose up --build -d --remove-orphans 37 | ``` 38 | OR 39 | ```bash 40 | $ make build 41 | ``` 42 | 43 | - Test Coverage 44 | ```bash 45 | $ pytest --disable-warnings -vv 46 | ``` 47 | OR 48 | ```bash 49 | $ make test 50 | ``` 51 | 52 | ## Docs 53 | 54 | #### Swagger: [Documentation](https://swagger.io/docs/) 55 | 56 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display1.png?raw=true) 57 | 58 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display2.png?raw=true) 59 | 60 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display3.png?raw=true) 61 | 62 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display4.png?raw=true) 63 | 64 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display5.png?raw=true) 65 | 66 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display6.png?raw=true) 67 | 68 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display7.png?raw=true) 69 | 70 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display8.png?raw=true) 71 | 72 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display9.png?raw=true) 73 | 74 | ![alt text](https://github.com/kayprogrammer/socialnet-v1/blob/main/display/display10.png?raw=true) 75 | 76 | -------------------------------------------------------------------------------- /apps/accounts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/accounts/__init__.py -------------------------------------------------------------------------------- /apps/accounts/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.models import Group as DjangoGroup 3 | from django.contrib.auth.admin import UserAdmin as BaseUserAdmin 4 | from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | from .models import User 8 | 9 | 10 | class Group(DjangoGroup): 11 | class Meta: 12 | verbose_name = "group" 13 | verbose_name_plural = "groups" 14 | proxy = True 15 | 16 | 17 | class GroupAdmin(BaseGroupAdmin): 18 | pass 19 | 20 | 21 | class UserAdmin(BaseUserAdmin): 22 | ordering = ["email"] 23 | 24 | list_display = [ 25 | "id", 26 | "first_name", 27 | "last_name", 28 | "email", 29 | "is_staff", 30 | "is_active", 31 | ] 32 | 33 | list_display_links = ["first_name", "last_name", "email"] 34 | list_filter = [ 35 | "first_name", 36 | "last_name", 37 | "username", 38 | "email", 39 | "is_staff", 40 | "is_active", 41 | ] 42 | 43 | fieldsets = ( 44 | ( 45 | _("Login Credentials"), 46 | {"fields": ("email", "password")}, 47 | ), 48 | ( 49 | _("Personal Information"), 50 | { 51 | "fields": ( 52 | "first_name", 53 | "last_name", 54 | "username", 55 | "bio", 56 | "city", 57 | "dob", 58 | "avatar", 59 | ) 60 | }, 61 | ), 62 | ( 63 | _("Permissions and Groups"), 64 | { 65 | "fields": ( 66 | "is_email_verified", 67 | "is_active", 68 | "is_staff", 69 | "is_superuser", 70 | "groups", 71 | "user_permissions", 72 | ) 73 | }, 74 | ), 75 | ( 76 | _("Important Dates"), 77 | {"fields": ("created_at", "updated_at", "last_login")}, 78 | ), 79 | ) 80 | 81 | add_fieldsets = ( 82 | ( 83 | None, 84 | { 85 | "classes": ("wide",), 86 | "fields": ( 87 | "first_name", 88 | "last_name", 89 | "email", 90 | "password1", 91 | "password2", 92 | "is_staff", 93 | "is_active", 94 | ), 95 | }, 96 | ), 97 | ) 98 | readonly_fields = ("username", "created_at", "updated_at") 99 | search_fields = ["first_name", "first_name", "email"] 100 | 101 | 102 | admin.site.register(User, UserAdmin) 103 | admin.site.register(Group, GroupAdmin) 104 | admin.site.unregister(DjangoGroup) 105 | -------------------------------------------------------------------------------- /apps/accounts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "apps.accounts" 7 | -------------------------------------------------------------------------------- /apps/accounts/auth.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from apps.accounts.models import User 3 | from datetime import datetime, timedelta 4 | import jwt, random, string 5 | 6 | ALGORITHM = "HS256" 7 | 8 | 9 | class Authentication: 10 | # generate random string 11 | def get_random(length: int): 12 | return "".join(random.choices(string.ascii_letters + string.digits, k=length)) 13 | 14 | # generate access token based and encode user's id 15 | def create_access_token(payload: dict): 16 | expire = datetime.utcnow() + timedelta( 17 | minutes=int(settings.ACCESS_TOKEN_EXPIRE_MINUTES) 18 | ) 19 | to_encode = {"exp": expire, **payload} 20 | encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM) 21 | return encoded_jwt 22 | 23 | # generate random refresh token 24 | def create_refresh_token(): 25 | expire = datetime.utcnow() + timedelta( 26 | minutes=int(settings.REFRESH_TOKEN_EXPIRE_MINUTES) 27 | ) 28 | return jwt.encode( 29 | {"exp": expire, "data": Authentication.get_random(10)}, 30 | settings.SECRET_KEY, 31 | algorithm=ALGORITHM, 32 | ) 33 | 34 | # deocde access token from header 35 | def decode_jwt(token: str): 36 | try: 37 | decoded = jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM]) 38 | except: 39 | decoded = False 40 | return decoded 41 | 42 | def decodeAuthorization(token: str): 43 | token = token[7:] 44 | decoded = Authentication.decode_jwt(token) 45 | if not decoded: 46 | return None 47 | user = User.objects.select_related( 48 | "city", "city__region", "city__country", "avatar" 49 | ).get_or_none(id=decoded["user_id"], access=token) 50 | return user 51 | -------------------------------------------------------------------------------- /apps/accounts/migrations/0002_user_access_user_refresh_delete_jwt.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2024-01-05 12:33 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("accounts", "0001_initial"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="user", 14 | name="access", 15 | field=models.TextField(editable=False, null=True, unique=True), 16 | ), 17 | migrations.AddField( 18 | model_name="user", 19 | name="refresh", 20 | field=models.TextField(editable=False, null=True, unique=True), 21 | ), 22 | migrations.DeleteModel( 23 | name="Jwt", 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /apps/accounts/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/accounts/migrations/__init__.py -------------------------------------------------------------------------------- /apps/accounts/models.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin 4 | from django.db import models 5 | from django.utils.translation import gettext_lazy as _ 6 | from django.utils import timezone 7 | from apps.common.models import BaseModel, File 8 | from django.conf import settings 9 | from apps.common.file_processors import FileProcessor 10 | from .managers import CustomUserManager 11 | from autoslug import AutoSlugField 12 | 13 | 14 | def slugify_two_fields(self): 15 | return f"{self.first_name}-{self.last_name}" 16 | 17 | 18 | class User(AbstractBaseUser, PermissionsMixin): 19 | id = models.UUIDField( 20 | default=uuid.uuid4, editable=False, unique=True, primary_key=True 21 | ) 22 | first_name = models.CharField(max_length=50) 23 | last_name = models.CharField(max_length=50) 24 | username = AutoSlugField( 25 | _("Username"), populate_from=slugify_two_fields, unique=True, always_update=True 26 | ) 27 | email = models.EmailField(verbose_name=(_("Email address")), unique=True) 28 | avatar = models.ForeignKey(File, on_delete=models.SET_NULL, null=True, blank=True) 29 | 30 | terms_agreement = models.BooleanField(default=False) 31 | is_email_verified = models.BooleanField(default=False) 32 | is_staff = models.BooleanField(default=False) 33 | is_active = models.BooleanField(default=True) 34 | created_at = models.DateTimeField(auto_now_add=True) 35 | updated_at = models.DateTimeField(auto_now=True) 36 | 37 | # Profile Fields 38 | bio = models.CharField(max_length=200, null=True, blank=True) 39 | city = models.ForeignKey( 40 | "cities_light.City", on_delete=models.SET_NULL, null=True, blank=True 41 | ) 42 | dob = models.DateField(verbose_name=(_("Date of Birth")), null=True, blank=True) 43 | 44 | # Tokens 45 | access = models.TextField(editable=False, unique=True, null=True) 46 | refresh = models.TextField(editable=False, unique=True, null=True) 47 | 48 | USERNAME_FIELD = "email" 49 | REQUIRED_FIELDS = ["first_name", "last_name"] 50 | 51 | objects = CustomUserManager() 52 | 53 | class Meta: 54 | verbose_name = _("User") 55 | verbose_name_plural = _("Users") 56 | 57 | def __str__(self): 58 | return self.full_name 59 | 60 | @property 61 | def full_name(self): 62 | return f"{self.first_name} {self.last_name}" 63 | 64 | @property 65 | def get_avatar(self): 66 | avatar = self.avatar 67 | if avatar: 68 | return FileProcessor.generate_file_url( 69 | key=self.avatar_id, 70 | folder="avatars", 71 | content_type=avatar.resource_type, 72 | ) 73 | return None 74 | 75 | 76 | class Otp(BaseModel): 77 | user = models.OneToOneField(User, on_delete=models.CASCADE) 78 | code = models.IntegerField() 79 | 80 | def check_expiration(self): 81 | now = timezone.now() 82 | diff = now - self.updated_at 83 | if diff.total_seconds() > int(settings.EMAIL_OTP_EXPIRE_SECONDS): 84 | return True 85 | return False 86 | -------------------------------------------------------------------------------- /apps/accounts/serializers.py: -------------------------------------------------------------------------------- 1 | from email.policy import default 2 | from django.utils.translation import gettext_lazy as _ 3 | from rest_framework import serializers 4 | from apps.common.serializers import SuccessResponseSerializer 5 | 6 | 7 | # REQUEST SERIALIZERS 8 | class RegisterSerializer(serializers.Serializer): 9 | first_name = serializers.CharField( 10 | max_length=50, error_messages={"max_length": _("{max_length} characters max.")} 11 | ) 12 | last_name = serializers.CharField( 13 | max_length=50, error_messages={"max_length": _("{max_length} characters max.")} 14 | ) 15 | email = serializers.EmailField() 16 | password = serializers.CharField( 17 | min_length=8, error_messages={"min_length": _("{min_length} characters min.")} 18 | ) 19 | terms_agreement = serializers.BooleanField() 20 | 21 | def validate(self, attrs): 22 | first_name = attrs["first_name"] 23 | last_name = attrs["last_name"] 24 | terms_agreement = attrs["terms_agreement"] 25 | 26 | if len(first_name.split(" ")) > 1: 27 | raise serializers.ValidationError({"first_name": "No spacing allowed"}) 28 | 29 | if len(last_name.split(" ")) > 1: 30 | raise serializers.ValidationError({"last_name": "No spacing allowed"}) 31 | 32 | if terms_agreement != True: 33 | raise serializers.ValidationError( 34 | {"terms_agreement": "You must agree to terms and conditions"} 35 | ) 36 | return attrs 37 | 38 | 39 | class ResendOtpSerializer(serializers.Serializer): 40 | email = serializers.EmailField() 41 | 42 | 43 | class VerifyOtpSerializer(ResendOtpSerializer): 44 | otp = serializers.IntegerField() 45 | 46 | 47 | class SetNewPasswordSerializer(VerifyOtpSerializer): 48 | password = serializers.CharField( 49 | min_length=8, error_messages={"min_length": _("{min_length} characters min.")} 50 | ) 51 | 52 | 53 | class LoginSerializer(ResendOtpSerializer): 54 | password = serializers.CharField() 55 | 56 | 57 | class RefreshSerializer(serializers.Serializer): 58 | refresh = serializers.CharField() 59 | 60 | 61 | # RESPONSE SERIALIZERS 62 | class RegisterResponseSerializer(SuccessResponseSerializer): 63 | data = serializers.DictField(default={"email": "johndoe@example.com"}) 64 | 65 | 66 | class LoginResponseSerializer(SuccessResponseSerializer): 67 | data = serializers.DictField( 68 | default={ 69 | "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.", 70 | "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lMSIsInBhc3N3b3JkIjoiYXNkZmFzZGYiLCJpYXQiOjE2MzA2MzA3NTh9.", 71 | } 72 | ) 73 | -------------------------------------------------------------------------------- /apps/accounts/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path("register/", views.RegisterView.as_view()), 7 | path("verify-email/", views.VerifyEmailView.as_view()), 8 | path("resend-verification-email/", views.ResendVerificationEmailView.as_view()), 9 | path("send-password-reset-otp/", views.SendPasswordResetOtpView.as_view()), 10 | path("set-new-password/", views.SetNewPasswordView.as_view()), 11 | path("login/", views.LoginView.as_view()), 12 | path("refresh/", views.RefreshTokensView.as_view()), 13 | path("logout/", views.LogoutView.as_view()), 14 | ] 15 | -------------------------------------------------------------------------------- /apps/chat/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/chat/__init__.py -------------------------------------------------------------------------------- /apps/chat/admin.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib import admin 3 | 4 | from apps.chat.models import Chat, Message 5 | 6 | from apps.chat.validators import validate_chat_users_m2m 7 | 8 | # Register your models here. 9 | 10 | 11 | class ChatForm(forms.ModelForm): 12 | def clean_users(self): 13 | users = self.cleaned_data.get("users") 14 | if users: 15 | validate_chat_users_m2m( 16 | users, self.cleaned_data.get("ctype"), self.cleaned_data.get("owner") 17 | ) 18 | return users 19 | 20 | 21 | class ChatAdmin(admin.ModelAdmin): 22 | list_display = ("name", "owner", "ctype", "created_at", "updated_at") 23 | list_filter = ("name", "owner", "ctype", "created_at", "updated_at") 24 | list_display_links = ("name", "owner") 25 | form = ChatForm 26 | 27 | 28 | class MessageAdmin(admin.ModelAdmin): 29 | list_display = ("chat", "sender", "text", "file", "updated_at") 30 | list_filter = ("chat", "sender", "text", "file", "updated_at") 31 | 32 | 33 | admin.site.register(Chat, ChatAdmin) 34 | admin.site.register(Message, MessageAdmin) 35 | -------------------------------------------------------------------------------- /apps/chat/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ChatConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "apps.chat" 7 | -------------------------------------------------------------------------------- /apps/chat/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 18:00 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import uuid 7 | 8 | 9 | class Migration(migrations.Migration): 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ("common", "0001_initial"), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name="Chat", 20 | fields=[ 21 | ( 22 | "id", 23 | models.UUIDField( 24 | default=uuid.uuid4, 25 | editable=False, 26 | primary_key=True, 27 | serialize=False, 28 | unique=True, 29 | ), 30 | ), 31 | ("created_at", models.DateTimeField(auto_now_add=True)), 32 | ("updated_at", models.DateTimeField(auto_now=True)), 33 | ("name", models.CharField(blank=True, max_length=100, null=True)), 34 | ( 35 | "ctype", 36 | models.CharField( 37 | choices=[("DM", "DM"), ("GROUP", "GROUP")], 38 | default="DM", 39 | max_length=10, 40 | ), 41 | ), 42 | ("description", models.TextField(null=True)), 43 | ( 44 | "image", 45 | models.ForeignKey( 46 | blank=True, 47 | null=True, 48 | on_delete=django.db.models.deletion.SET_NULL, 49 | to="common.file", 50 | ), 51 | ), 52 | ( 53 | "owner", 54 | models.ForeignKey( 55 | on_delete=django.db.models.deletion.CASCADE, 56 | related_name="chats", 57 | to=settings.AUTH_USER_MODEL, 58 | ), 59 | ), 60 | ("users", models.ManyToManyField(to=settings.AUTH_USER_MODEL)), 61 | ], 62 | options={ 63 | "ordering": ["-updated_at"], 64 | }, 65 | ), 66 | migrations.AddConstraint( 67 | model_name="chat", 68 | constraint=models.CheckConstraint( 69 | check=models.Q( 70 | ("ctype", "DM"), 71 | ("description", None), 72 | ("image", None), 73 | ("name", None), 74 | ), 75 | name="dm_chat_constraints", 76 | ), 77 | ), 78 | ] 79 | -------------------------------------------------------------------------------- /apps/chat/migrations/0002_remove_chat_dm_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 18:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0001_initial"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.AlterField( 17 | model_name="chat", 18 | name="description", 19 | field=models.TextField(blank=True, null=True), 20 | ), 21 | migrations.AddConstraint( 22 | model_name="chat", 23 | constraint=models.CheckConstraint( 24 | check=models.Q( 25 | ("ctype", "DM"), 26 | ("description", None), 27 | ("image", None), 28 | ("name", None), 29 | _negated=True, 30 | ), 31 | name="dm_chat_constraints", 32 | ), 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /apps/chat/migrations/0003_remove_chat_dm_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 18:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0002_remove_chat_dm_chat_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="chat", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | ("ctype", "DM"), 21 | ("description", None), 22 | ("image", None), 23 | ("name", None), 24 | ), 25 | name="dm_chat_constraints", 26 | ), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/chat/migrations/0004_remove_chat_dm_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 18:09 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0003_remove_chat_dm_chat_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="chat", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | ("ctype", "DM"), 21 | ("name", None), 22 | ("description", None), 23 | ("image", None), 24 | ), 25 | name="dm_chat_constraints", 26 | ), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/chat/migrations/0005_remove_chat_dm_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 18:19 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0004_remove_chat_dm_chat_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="chat", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | ("ctype", "DM"), 21 | ("description", None), 22 | ("image", None), 23 | ("name", None), 24 | ), 25 | name="dm_chat_constraints", 26 | ), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/chat/migrations/0006_alter_chat_description.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 18:24 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0005_remove_chat_dm_chat_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="chat", 14 | name="description", 15 | field=models.CharField(blank=True, max_length=1000, null=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/chat/migrations/0007_remove_chat_dm_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 18:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0006_alter_chat_description"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="chat", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | models.Q(("ctype", "DM"), ("ctype", "GROUP"), _connector="OR"), 21 | ("name", None), 22 | ("description", None), 23 | ("image", None), 24 | ), 25 | name="dm_chat_constraints", 26 | ), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/chat/migrations/0008_remove_chat_dm_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 18:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0007_remove_chat_dm_chat_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="chat", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | ("ctype__exact", "DM"), 21 | ("name", None), 22 | ("description", None), 23 | ("image", None), 24 | ), 25 | name="dm_chat_constraints", 26 | ), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/chat/migrations/0009_remove_chat_dm_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 18:58 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0008_remove_chat_dm_chat_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="chat", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | models.Q( 21 | ("ctype", "DM"), 22 | ("description", None), 23 | ("image", None), 24 | ("name", None), 25 | ), 26 | ("ctype", "GROUP"), 27 | _connector="OR", 28 | ), 29 | name="dm_chat_constraints", 30 | ), 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /apps/chat/migrations/0010_remove_chat_dm_chat_constraints_remove_chat_users_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 20:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0009_remove_chat_dm_chat_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.RemoveField( 17 | model_name="chat", 18 | name="users", 19 | ), 20 | migrations.AddField( 21 | model_name="chat", 22 | name="user_ids", 23 | field=models.JSONField(blank=True, default=list), 24 | ), 25 | migrations.AddConstraint( 26 | model_name="chat", 27 | constraint=models.CheckConstraint( 28 | check=models.Q( 29 | models.Q( 30 | ("ctype", "DM"), 31 | ("description", None), 32 | ("image", None), 33 | ("name", None), 34 | ), 35 | ("ctype", "GROUP"), 36 | _connector="OR", 37 | ), 38 | name="dm_chat_constraints", 39 | violation_error_message="Chat with type 'DM' must have 'name', 'image' and 'description' as None", 40 | ), 41 | ), 42 | ] 43 | -------------------------------------------------------------------------------- /apps/chat/migrations/0011_remove_chat_user_ids_chat_users.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 20:53 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 10 | ("chat", "0010_remove_chat_dm_chat_constraints_remove_chat_users_and_more"), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name="chat", 16 | name="user_ids", 17 | ), 18 | migrations.AddField( 19 | model_name="chat", 20 | name="users", 21 | field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/chat/migrations/0012_message.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-15 16:06 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import uuid 7 | 8 | 9 | class Migration(migrations.Migration): 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ("common", "0001_initial"), 13 | ("chat", "0011_remove_chat_user_ids_chat_users"), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name="Message", 19 | fields=[ 20 | ( 21 | "id", 22 | models.UUIDField( 23 | default=uuid.uuid4, 24 | editable=False, 25 | primary_key=True, 26 | serialize=False, 27 | unique=True, 28 | ), 29 | ), 30 | ("created_at", models.DateTimeField(auto_now_add=True)), 31 | ("updated_at", models.DateTimeField(auto_now=True)), 32 | ("text", models.TextField()), 33 | ( 34 | "chat", 35 | models.ForeignKey( 36 | on_delete=django.db.models.deletion.CASCADE, 37 | related_name="messages", 38 | to="chat.chat", 39 | ), 40 | ), 41 | ( 42 | "file", 43 | models.ForeignKey( 44 | null=True, 45 | on_delete=django.db.models.deletion.SET_NULL, 46 | to="common.file", 47 | ), 48 | ), 49 | ( 50 | "sender", 51 | models.ForeignKey( 52 | on_delete=django.db.models.deletion.CASCADE, 53 | related_name="messages", 54 | to=settings.AUTH_USER_MODEL, 55 | ), 56 | ), 57 | ], 58 | options={ 59 | "abstract": False, 60 | }, 61 | ), 62 | ] 63 | -------------------------------------------------------------------------------- /apps/chat/migrations/0013_remove_chat_dm_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-16 21:09 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0012_message"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="chat", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | models.Q( 21 | ("ctype", "DM"), 22 | ("description", None), 23 | ("image", None), 24 | ("name", None), 25 | ), 26 | models.Q(("ctype", "GROUP"), ("name__isnull", False)), 27 | _connector="OR", 28 | ), 29 | name="dm_chat_constraints", 30 | violation_error_message="Chat with type 'DM' must have 'name', 'image' and 'description' as None", 31 | ), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /apps/chat/migrations/0014_alter_chat_users.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-16 21:26 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 10 | ("chat", "0013_remove_chat_dm_chat_constraints_and_more"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="chat", 16 | name="users", 17 | field=models.ManyToManyField(to=settings.AUTH_USER_MODEL), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/chat/migrations/0015_chat_group_chat_constraints.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-16 22:05 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0014_alter_chat_users"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddConstraint( 13 | model_name="chat", 14 | constraint=models.CheckConstraint( 15 | check=models.Q( 16 | models.Q(("ctype", "GROUP"), ("name__isnull", False)), 17 | ("ctype", "DM"), 18 | _connector="OR", 19 | ), 20 | name="group_chat_constraints", 21 | violation_error_message="Chat with type 'GROUP' must not have 'name' as None", 22 | ), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/chat/migrations/0016_remove_chat_dm_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-16 22:05 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0015_chat_group_chat_constraints"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="dm_chat_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="chat", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | models.Q( 21 | ("ctype", "DM"), 22 | ("description", None), 23 | ("image", None), 24 | ("name", None), 25 | ), 26 | ("ctype", "GROUP"), 27 | _connector="OR", 28 | ), 29 | name="dm_chat_constraints", 30 | violation_error_message="Chat with type 'DM' must have 'name', 'image' and 'description' as None", 31 | ), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /apps/chat/migrations/0017_remove_chat_group_chat_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-16 22:17 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0016_remove_chat_dm_chat_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="chat", 14 | name="group_chat_constraints", 15 | ), 16 | migrations.RemoveConstraint( 17 | model_name="chat", 18 | name="dm_chat_constraints", 19 | ), 20 | migrations.AddConstraint( 21 | model_name="chat", 22 | constraint=models.CheckConstraint( 23 | check=models.Q( 24 | models.Q( 25 | ("ctype", "DM"), 26 | ("description", None), 27 | ("image", None), 28 | ("name", None), 29 | ), 30 | ("ctype", "GROUP"), 31 | _connector="OR", 32 | ), 33 | name="dm_chat_constraints", 34 | violation_error_message="DMs cannot have name, image and description", 35 | ), 36 | ), 37 | migrations.AddConstraint( 38 | model_name="chat", 39 | constraint=models.CheckConstraint( 40 | check=models.Q( 41 | models.Q(("ctype", "GROUP"), ("name__isnull", False)), 42 | ("ctype", "DM"), 43 | _connector="OR", 44 | ), 45 | name="group_chat_constraints", 46 | violation_error_message="Enter name for group chat", 47 | ), 48 | ), 49 | ] 50 | -------------------------------------------------------------------------------- /apps/chat/migrations/0018_alter_message_options_alter_message_file_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-20 12:20 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ("common", "0001_initial"), 10 | ("chat", "0017_remove_chat_group_chat_constraints_and_more"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name="message", 16 | options={"get_latest_by": "created_at"}, 17 | ), 18 | migrations.AlterField( 19 | model_name="message", 20 | name="file", 21 | field=models.ForeignKey( 22 | blank=True, 23 | null=True, 24 | on_delete=django.db.models.deletion.SET_NULL, 25 | to="common.file", 26 | ), 27 | ), 28 | migrations.AlterField( 29 | model_name="message", 30 | name="text", 31 | field=models.TextField(null=True), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /apps/chat/migrations/0019_alter_message_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-20 13:12 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("chat", "0018_alter_message_options_alter_message_file_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="message", 14 | name="text", 15 | field=models.TextField(blank=True, null=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/chat/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/chat/migrations/__init__.py -------------------------------------------------------------------------------- /apps/chat/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.db.models import CheckConstraint, Q 3 | from django.db.models.signals import m2m_changed 4 | from apps.accounts.models import User 5 | from apps.chat.validators import validate_chat_users_m2m 6 | from apps.common.file_processors import FileProcessor 7 | 8 | from apps.common.models import BaseModel, File 9 | 10 | # Create your models here. 11 | 12 | CHAT_TYPES = (("DM", "DM"), ("GROUP", "GROUP")) 13 | 14 | 15 | class Chat(BaseModel): 16 | name = models.CharField(max_length=100, null=True, blank=True) 17 | owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="chats") 18 | ctype = models.CharField(default="DM", max_length=10, choices=CHAT_TYPES) 19 | users = models.ManyToManyField(User) 20 | description = models.CharField(max_length=1000, null=True, blank=True) 21 | image = models.ForeignKey(File, on_delete=models.SET_NULL, null=True, blank=True) 22 | 23 | def __str__(self): 24 | return str(self.id) 25 | 26 | @property 27 | def get_image(self): 28 | image = self.image 29 | if image: 30 | return FileProcessor.generate_file_url( 31 | key=self.image_id, 32 | folder="chats", 33 | content_type=image.resource_type, 34 | ) 35 | return None 36 | 37 | class Meta: 38 | ordering = ["-updated_at"] 39 | constraints = [ 40 | CheckConstraint( 41 | check=(Q(ctype="DM", name=None, description=None, image=None)) 42 | | Q(ctype="GROUP"), 43 | name="dm_chat_constraints", 44 | violation_error_message="DMs cannot have name, image and description", 45 | ), 46 | CheckConstraint( 47 | check=Q(ctype="GROUP", name__isnull=False) | (Q(ctype="DM")), 48 | name="group_chat_constraints", 49 | violation_error_message="Enter name for group chat", 50 | ), 51 | ] 52 | 53 | 54 | def users_changed(sender, instance, **kwargs): 55 | users = instance.users 56 | validate_chat_users_m2m(users, instance.ctype, instance.owner) 57 | 58 | 59 | m2m_changed.connect(users_changed, sender=Chat.users.through) 60 | 61 | 62 | class Message(BaseModel): 63 | chat = models.ForeignKey(Chat, on_delete=models.CASCADE, related_name="messages") 64 | sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="messages") 65 | text = models.TextField(null=True, blank=True) 66 | file = models.ForeignKey(File, on_delete=models.SET_NULL, null=True, blank=True) 67 | 68 | def save(self, *args, **kwargs): 69 | if self._state.adding: 70 | self.chat.save() 71 | super().save(*args, **kwargs) 72 | 73 | @property 74 | def get_file(self): 75 | file = self.file 76 | if file: 77 | return FileProcessor.generate_file_url( 78 | key=self.file_id, 79 | folder="messages", 80 | content_type=file.resource_type, 81 | ) 82 | return None 83 | 84 | class Meta: 85 | get_latest_by = "created_at" 86 | -------------------------------------------------------------------------------- /apps/chat/socket_serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | MESSAGE_STATUS = ( 4 | ("CREATED", "CREATED"), 5 | ("UPDATED", "UPDATED"), 6 | ("DELETED", "DELETED"), 7 | ) 8 | 9 | 10 | class SocketMessageSerializer(serializers.Serializer): 11 | status = serializers.ChoiceField(choices=MESSAGE_STATUS) 12 | id = serializers.UUIDField() 13 | -------------------------------------------------------------------------------- /apps/chat/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from apps.chat import consumers 4 | 5 | from . import views 6 | 7 | urlpatterns = [ 8 | path("", views.ChatsView.as_view()), 9 | path("/", views.ChatView.as_view()), 10 | path("messages//", views.MessageView.as_view()), 11 | path("groups/group/", views.ChatGroupCreateView.as_view()), 12 | ] 13 | 14 | chatsocket_urlpatterns = [ 15 | path("api/v1/ws/chats//", consumers.ChatConsumer.as_asgi()) 16 | ] 17 | -------------------------------------------------------------------------------- /apps/chat/validators.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ValidationError 2 | 3 | 4 | def validate_chat_users_m2m(users, ctype, owner): 5 | users_count = users.count() 6 | if users_count > 1 and ctype == "DM": 7 | raise ValidationError("You can't assign more than 1 user") 8 | elif owner in users.all(): 9 | raise ValidationError("Owner cannot be in users") 10 | elif users_count > 99: # Group owner is one 11 | raise ValidationError("Cannot have more than 100 users in a group") 12 | -------------------------------------------------------------------------------- /apps/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/common/__init__.py -------------------------------------------------------------------------------- /apps/common/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.utils.safestring import mark_safe 3 | from cities_light.models import SubRegion 4 | 5 | admin.site.site_header = mark_safe( 6 | 'SOCIALNET ADMIN' 7 | ) 8 | 9 | admin.site.unregister(SubRegion) 10 | -------------------------------------------------------------------------------- /apps/common/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CommonConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "apps.common" 7 | -------------------------------------------------------------------------------- /apps/common/consumers.py: -------------------------------------------------------------------------------- 1 | from channels.generic.websocket import AsyncWebsocketConsumer 2 | from rest_framework.exceptions import ValidationError 3 | from apps.common.error import ErrorCode 4 | import json 5 | 6 | 7 | class BaseConsumer(AsyncWebsocketConsumer): 8 | async def validate_entry(self, entry_data, serializer_class): 9 | err = None 10 | try: 11 | data_json = json.loads(entry_data) 12 | serializer = serializer_class(data=data_json) 13 | serializer.is_valid(raise_exception=True) 14 | except Exception as e: 15 | err = await self.err_handler(e) 16 | 17 | if err: 18 | return err, False 19 | return serializer.data, True 20 | 21 | async def err_handler(self, exc): 22 | err = {} 23 | if isinstance(exc, json.decoder.JSONDecodeError) or exc.detail.get( 24 | "non_field_errors" 25 | ): 26 | err["type"] = ErrorCode.INVALID_DATA_TYPE 27 | err["message"] = "Data is not a valid json" 28 | 29 | elif isinstance(exc, ValidationError): 30 | errors = exc.detail 31 | for key in errors: 32 | err_val = str(errors[key][0]).replace('"', "") 33 | errors[key] = err_val 34 | if isinstance(err_val, list): 35 | errors[key] = err_val 36 | 37 | err["type"] = ErrorCode.INVALID_ENTRY 38 | err["message"] = "Invalid entry data" 39 | err["data"] = errors 40 | return err 41 | 42 | async def send_error_message(self, error): 43 | err = {"status": "error"} | error 44 | # Send an error message to the client 45 | await self.send(json.dumps(err)) 46 | -------------------------------------------------------------------------------- /apps/common/error.py: -------------------------------------------------------------------------------- 1 | class ErrorCode: 2 | UNAUTHORIZED_USER = "unauthorized_user" 3 | NETWORK_FAILURE = "network_failure" 4 | SERVER_ERROR = "server_error" 5 | INVALID_ENTRY = "invalid_entry" 6 | INCORRECT_EMAIL = "incorrect_email" 7 | INCORRECT_OTP = "incorrect_otp" 8 | EXPIRED_OTP = "expired_otp" 9 | INVALID_AUTH = "invalid_auth" 10 | INVALID_TOKEN = "invalid_token" 11 | INVALID_CREDENTIALS = "invalid_credentials" 12 | UNVERIFIED_USER = "unverified_user" 13 | NON_EXISTENT = "non_existent" 14 | INVALID_OWNER = "invalid_owner" 15 | INVALID_PAGE = "invalid_page" 16 | INVALID_VALUE = "invalid_value" 17 | NOT_ALLOWED = "not_allowed" 18 | INVALID_DATA_TYPE = "invalid_data_type" 19 | -------------------------------------------------------------------------------- /apps/common/exceptions.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | from rest_framework.views import exception_handler 3 | from rest_framework.exceptions import ( 4 | AuthenticationFailed, 5 | ValidationError, 6 | APIException, 7 | NotFound, 8 | ) 9 | 10 | from .responses import CustomResponse 11 | from .error import ErrorCode 12 | 13 | 14 | class RequestError(APIException): 15 | default_detail = "An error occured" 16 | 17 | def __init__( 18 | self, err_msg: str, err_code: str, status_code: int = 400, data: dict = None 19 | ) -> None: 20 | self.status_code = HTTPStatus(status_code) 21 | self.err_code = err_code 22 | self.err_msg = err_msg 23 | self.data = data 24 | 25 | super().__init__() 26 | 27 | 28 | def custom_exception_handler(exc, context): 29 | try: 30 | response = exception_handler(exc, context) 31 | if isinstance(exc, AuthenticationFailed): 32 | exc_list = str(exc).split("DETAIL: ") 33 | return CustomResponse.error( 34 | message=exc_list[-1], 35 | status_code=401, 36 | err_code=ErrorCode.UNAUTHORIZED_USER, 37 | ) 38 | elif isinstance(exc, RequestError): 39 | return CustomResponse.error( 40 | message=exc.err_msg, 41 | data=exc.data, 42 | status_code=exc.status_code, 43 | err_code=exc.err_code, 44 | ) 45 | elif isinstance(exc, ValidationError): 46 | errors = exc.detail 47 | for key in errors: 48 | err_val = str(errors[key][0]).replace('"', "") 49 | errors[key] = err_val 50 | if isinstance(err_val, list): 51 | errors[key] = err_val 52 | 53 | return CustomResponse.error( 54 | message="Invalid Entry", 55 | data=errors, 56 | status_code=422, 57 | err_code=ErrorCode.INVALID_ENTRY, 58 | ) 59 | else: 60 | print(exc) 61 | return CustomResponse.error( 62 | message="Something went wrong!", 63 | status_code=( 64 | response.status_code if hasattr(response, "status_code") else 500 65 | ), 66 | err_code=ErrorCode.SERVER_ERROR, 67 | ) 68 | except APIException as e: 69 | print("Server Error: ", e) 70 | return CustomResponse.error( 71 | message="Server Error", status_code=500, err_code=ErrorCode.SERVER_ERROR 72 | ) 73 | -------------------------------------------------------------------------------- /apps/common/file_processors.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | import time 3 | import cloudinary 4 | import cloudinary.uploader 5 | import mimetypes 6 | 7 | BASE_FOLDER = "socialnet-v1/" 8 | 9 | # FILES CONFIG WITH CLOUDINARY 10 | cloudinary.config( 11 | cloud_name=settings.CLOUDINARY_CLOUD_NAME, 12 | api_key=settings.CLOUDINARY_API_KEY, 13 | api_secret=settings.CLOUDINARY_API_SECRET, 14 | ) 15 | 16 | 17 | class FileProcessor: 18 | @staticmethod 19 | def generate_file_signature(key, folder): 20 | key = f"{BASE_FOLDER}{folder}/{key}" 21 | timestamp = str(int(time.time())) 22 | params = { 23 | "public_id": key, 24 | "timestamp": timestamp, 25 | } 26 | try: 27 | signature = cloudinary.utils.api_sign_request( 28 | params_to_sign=params, api_secret=settings.CLOUDINARY_API_SECRET 29 | ) 30 | return {"public_id": key, "signature": signature, "timestamp": timestamp} 31 | except Exception as e: 32 | print(e) 33 | pass 34 | 35 | def generate_file_url(key, folder, content_type): 36 | file_extension = mimetypes.guess_extension(content_type) 37 | key = f"{BASE_FOLDER}{folder}/{key}{file_extension}" 38 | 39 | try: 40 | return cloudinary.utils.cloudinary_url(key, secure=True)[0] 41 | except Exception as e: 42 | print(e) 43 | pass 44 | 45 | def upload_file(file, key, folder): 46 | key = f"{BASE_FOLDER}{folder}/{key}" 47 | try: 48 | cloudinary.uploader.upload(file, public_id=key, overwrite=True, faces=True) 49 | except Exception as e: 50 | print(e) 51 | pass 52 | -------------------------------------------------------------------------------- /apps/common/file_types.py: -------------------------------------------------------------------------------- 1 | ALLOWED_IMAGE_TYPES = [ 2 | "image/bmp", 3 | "image/gif", 4 | "image/jpeg", 5 | "image/png", 6 | "image/tiff", 7 | "image/webp", 8 | "image/svg+xml", 9 | ] 10 | 11 | ALLOWED_AUDIO_TYPES = ["audio/mp3", "audio/aac", "audio/wav", "audio/m4a"] 12 | 13 | ALLOWED_DOCUMENT_TYPES = [ 14 | "application/pdf", 15 | "application/msword", 16 | ] 17 | 18 | ALLOWED_FILE_TYPES = ALLOWED_IMAGE_TYPES + ALLOWED_AUDIO_TYPES + ALLOWED_DOCUMENT_TYPES 19 | -------------------------------------------------------------------------------- /apps/common/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/common/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/common/management/commands/data_script.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from apps.accounts.models import User 3 | from apps.general.models import SiteDetail 4 | from django.contrib.auth.hashers import make_password 5 | 6 | 7 | class CreateData(object): 8 | def __init__(self) -> None: 9 | pass 10 | 11 | async def initialize(self) -> None: 12 | await self.create_superuser() 13 | await self.create_clientuser() 14 | await self.create_sitedetail() 15 | 16 | async def create_superuser(self) -> User: 17 | user_dict = { 18 | "first_name": "Test", 19 | "last_name": "Admin", 20 | "password": make_password(settings.FIRST_SUPERUSER_PASSWORD), 21 | "is_superuser": True, 22 | "is_staff": True, 23 | "is_email_verified": True, 24 | } 25 | superuser, created = await User.objects.aget_or_create( 26 | email=settings.FIRST_SUPERUSER_EMAIL, defaults=user_dict 27 | ) 28 | return superuser 29 | 30 | async def create_clientuser(self) -> User: 31 | user_dict = { 32 | "first_name": "Test", 33 | "last_name": "Client", 34 | "password": make_password(settings.FIRST_CLIENT_PASSWORD), 35 | "is_email_verified": True, 36 | } 37 | client, created = await User.objects.aget_or_create( 38 | email=settings.FIRST_CLIENT_EMAIL, defaults=user_dict 39 | ) 40 | return client 41 | 42 | async def create_sitedetail(self) -> SiteDetail: 43 | sitedetail, created = await SiteDetail.objects.aget_or_create() 44 | return sitedetail 45 | -------------------------------------------------------------------------------- /apps/common/management/commands/initial_data.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | from .data_script import CreateData 3 | import logging, asyncio 4 | 5 | logging.basicConfig(level=logging.INFO) 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | async def init() -> None: 10 | create_data = CreateData() 11 | await create_data.initialize() 12 | 13 | 14 | class Command(BaseCommand): 15 | def handle(self, **options) -> None: 16 | logger.info("Creating initial data") 17 | asyncio.run(init()) 18 | logger.info("Initial data created") 19 | -------------------------------------------------------------------------------- /apps/common/managers.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class GetOrNoneQuerySet(models.QuerySet): 5 | """Custom QuerySet that supports get_or_none()""" 6 | 7 | def get_or_none(self, **kwargs): 8 | try: 9 | return self.get(**kwargs) 10 | except self.model.DoesNotExist: 11 | return None 12 | 13 | async def aget_or_none(self, **kwargs): 14 | try: 15 | return await self.aget(**kwargs) 16 | except self.model.DoesNotExist: 17 | return None 18 | 19 | 20 | class GetOrNoneManager(models.Manager): 21 | """Adds get_or_none method to objects""" 22 | 23 | def get_queryset(self): 24 | return GetOrNoneQuerySet(self.model, using=self._db) 25 | 26 | def get_or_none(self, **kwargs): 27 | return self.get_queryset().get_or_none(**kwargs) 28 | 29 | async def aget_or_none(self, **kwargs): 30 | return await self.get_queryset().aget_or_none(**kwargs) 31 | -------------------------------------------------------------------------------- /apps/common/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-11 14:50 2 | 3 | from django.db import migrations, models 4 | import uuid 5 | 6 | 7 | class Migration(migrations.Migration): 8 | initial = True 9 | 10 | dependencies = [] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="File", 15 | fields=[ 16 | ( 17 | "id", 18 | models.UUIDField( 19 | default=uuid.uuid4, 20 | editable=False, 21 | primary_key=True, 22 | serialize=False, 23 | unique=True, 24 | ), 25 | ), 26 | ("created_at", models.DateTimeField(auto_now_add=True)), 27 | ("updated_at", models.DateTimeField(auto_now=True)), 28 | ("resource_type", models.CharField(max_length=200)), 29 | ], 30 | options={ 31 | "abstract": False, 32 | }, 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /apps/common/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/common/migrations/__init__.py -------------------------------------------------------------------------------- /apps/common/models.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from django.db import models 4 | from .managers import GetOrNoneManager 5 | 6 | 7 | class BaseModel(models.Model): 8 | id = models.UUIDField( 9 | default=uuid.uuid4, editable=False, unique=True, primary_key=True 10 | ) 11 | created_at = models.DateTimeField(auto_now_add=True) 12 | updated_at = models.DateTimeField(auto_now=True) 13 | 14 | objects = GetOrNoneManager() 15 | 16 | class Meta: 17 | abstract = True 18 | 19 | 20 | class File(BaseModel): 21 | resource_type = models.CharField(max_length=200) 22 | 23 | def __str__(self): 24 | return str(self.id) 25 | -------------------------------------------------------------------------------- /apps/common/paginators.py: -------------------------------------------------------------------------------- 1 | from django.core.paginator import InvalidPage 2 | from rest_framework.pagination import PageNumberPagination 3 | from apps.common.error import ErrorCode 4 | 5 | from apps.common.exceptions import RequestError 6 | 7 | 8 | class CustomPagination(PageNumberPagination): 9 | page_size_query_param = ( 10 | "page_size" # Optional: allow clients to override the page size 11 | ) 12 | 13 | def paginate_queryset(self, queryset, request, view=None): 14 | """ 15 | Paginate a queryset if required, either returning a 16 | page object, or `None` if pagination is not configured for this view. 17 | """ 18 | page_size = self.get_page_size(request) 19 | if not page_size: 20 | return None 21 | 22 | paginator = self.django_paginator_class(queryset, page_size) 23 | page_number = self.get_page_number(request, paginator) 24 | 25 | try: 26 | self.page = paginator.page(page_number) 27 | except InvalidPage: 28 | raise RequestError( 29 | err_code=ErrorCode.INVALID_PAGE, err_msg="Invalid Page", status_code=404 30 | ) 31 | 32 | self.request = request 33 | return { 34 | "items": list(self.page), 35 | "per_page": page_size, 36 | "current_page": page_number, 37 | "last_page": paginator.num_pages, 38 | } 39 | -------------------------------------------------------------------------------- /apps/common/responses.py: -------------------------------------------------------------------------------- 1 | from rest_framework.response import Response 2 | 3 | 4 | class CustomResponse: 5 | def success(message, data=None, status_code=200): 6 | response = { 7 | "status": "success", 8 | "message": message, 9 | "data": data, 10 | } 11 | response.pop("data", None) if data is None else ... 12 | return Response(data=response, status=status_code) 13 | 14 | def error(message, err_code, data=None, status_code=400): 15 | response = { 16 | "status": "failure", 17 | "message": message, 18 | "code": err_code, 19 | "data": data, 20 | } 21 | response.pop("data", None) if data is None else ... 22 | return Response(data=response, status=status_code) 23 | -------------------------------------------------------------------------------- /apps/common/schema_examples.py: -------------------------------------------------------------------------------- 1 | file_upload_data = { 2 | "public_id": "d23dde64-a242-4ed0-bd75-4c759624b3a6", 3 | "signature": "djsdsjAushsh", 4 | "timestamp": "16272637829", 5 | } 6 | 7 | user_data = {"name": "John Doe", "slug": "john-doe", "avatar": "https://img.url"} 8 | 9 | latest_message_data = { 10 | "sender": user_data, 11 | "text": "Cool text", 12 | "file": "https://img.url", 13 | } 14 | 15 | notification_data = { 16 | "sender": user_data, 17 | "ntype": "REACTION", 18 | "post_slug": "john-doe-d23dde64-a242-4ed0-bd75-4c759624b3a6", 19 | "comment_slug": "john-doe-a23dde64-a242-4ed0-bd75-4c759624b3a9", 20 | "reply_slug": "john-doe-a45dde64-a242-4ed0-bd75-4c759624b3a1", 21 | "message": "John Doe reacted to your post", 22 | "is_read": False, 23 | } 24 | -------------------------------------------------------------------------------- /apps/common/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | 4 | class SuccessResponseSerializer(serializers.Serializer): 5 | status = serializers.CharField(default="success") 6 | message = serializers.CharField() 7 | 8 | 9 | class ErrorResponseSerializer(SuccessResponseSerializer): 10 | status = serializers.CharField(default="failure") 11 | 12 | 13 | class ErrorDataResponseSerializer(ErrorResponseSerializer): 14 | data = serializers.DictField() 15 | 16 | 17 | class PaginatedResponseDataSerializer(serializers.Serializer): 18 | per_page = serializers.IntegerField() 19 | current_page = serializers.IntegerField() 20 | last_page = serializers.IntegerField() 21 | -------------------------------------------------------------------------------- /apps/common/socket_auth.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.conf import settings 4 | from apps.accounts.auth import Authentication 5 | from asgiref.sync import sync_to_async 6 | 7 | 8 | class SocketAuthMiddleware: 9 | """ 10 | Custom middleware that verifies the user. 11 | """ 12 | 13 | def __init__(self, app): 14 | # Store the ASGI application we were passed 15 | self.app = app 16 | 17 | async def __call__(self, scope, receive, send): 18 | headers = {key.decode(): value.decode() for key, value in scope["headers"]} 19 | token = headers.get("authorization") 20 | error = {"type": "auth"} 21 | if not token: 22 | error["message"] = "Auth bearer not set" 23 | else: 24 | if ( 25 | token == settings.SOCKET_SECRET 26 | ): # If the app is making the connection itself 27 | scope["user"] = token 28 | else: 29 | user = await sync_to_async(Authentication.decodeAuthorization)(token) 30 | scope["user"] = user 31 | if not user: 32 | error["message"] = "Auth token is invalid or expired" 33 | 34 | scope["error"] = error 35 | return await self.app(scope, receive, send) 36 | -------------------------------------------------------------------------------- /apps/common/utils.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import BasePermission 2 | from apps.accounts.auth import Authentication 3 | from apps.accounts.models import User 4 | from apps.common.exceptions import RequestError 5 | from apps.common.error import ErrorCode 6 | 7 | from uuid import UUID 8 | 9 | 10 | def get_user(bearer): 11 | user = Authentication.decodeAuthorization(bearer) 12 | if not user: 13 | raise RequestError( 14 | err_code=ErrorCode.INVALID_TOKEN, 15 | err_msg="Auth Token is Invalid or Expired!", 16 | status_code=401, 17 | ) 18 | return user 19 | 20 | 21 | class IsAuthenticatedCustom(BasePermission): 22 | def has_permission(self, request, view): 23 | http_auth = request.META.get("HTTP_AUTHORIZATION") 24 | if not http_auth: 25 | raise RequestError( 26 | err_code=ErrorCode.INVALID_AUTH, 27 | err_msg="Auth Bearer not provided!", 28 | status_code=401, 29 | ) 30 | user = get_user(http_auth) 31 | request.user = user 32 | if request.user and request.user.is_authenticated: 33 | return True 34 | return False 35 | 36 | 37 | class IsAuthenticatedOrGuestCustom(BasePermission): 38 | def has_permission(self, request, view): 39 | http_auth = request.META.get("HTTP_AUTHORIZATION") 40 | request.user = None 41 | if http_auth: 42 | user = get_user(http_auth) 43 | request.user = user 44 | return True 45 | 46 | 47 | def set_dict_attr(obj, data): 48 | for attr, value in data.items(): 49 | setattr(obj, attr, value) 50 | return obj 51 | 52 | 53 | # Test Utils 54 | class TestUtil: 55 | def new_user(): 56 | user_dict = { 57 | "first_name": "Test", 58 | "last_name": "Name", 59 | "email": "test@example.com", 60 | "password": "testpassword", 61 | } 62 | user = User.objects.create_user(**user_dict) 63 | return user 64 | 65 | def verified_user(): 66 | user_dict = { 67 | "first_name": "Test", 68 | "last_name": "Verified", 69 | "email": "testverifieduser@example.com", 70 | "is_email_verified": True, 71 | "password": "testpassword", 72 | } 73 | user = User.objects.create_user(**user_dict) 74 | return user 75 | 76 | def another_verified_user(): 77 | create_user_dict = { 78 | "first_name": "AnotherTest", 79 | "last_name": "UserVerified", 80 | "email": "anothertestverifieduser@example.com", 81 | "is_email_verified": True, 82 | "password": "anothertestverifieduser123", 83 | } 84 | user = User.objects.create_user(**create_user_dict) 85 | return user 86 | 87 | def auth_token(verified_user): 88 | verified_user.access = Authentication.create_access_token( 89 | {"user_id": str(verified_user.id), "username": verified_user.username} 90 | ) 91 | verified_user.refresh = Authentication.create_refresh_token() 92 | verified_user.save() 93 | return verified_user.access 94 | -------------------------------------------------------------------------------- /apps/common/validators.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from apps.common.file_types import ALLOWED_FILE_TYPES, ALLOWED_IMAGE_TYPES 3 | 4 | 5 | def validate_image_type(value): 6 | if value and value not in ALLOWED_IMAGE_TYPES: 7 | raise serializers.ValidationError("Image type not allowed!") 8 | return value 9 | 10 | 11 | def validate_file_type(value): 12 | if value and value not in ALLOWED_FILE_TYPES: 13 | raise serializers.ValidationError("File type not allowed!") 14 | return value 15 | -------------------------------------------------------------------------------- /apps/feed/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/feed/__init__.py -------------------------------------------------------------------------------- /apps/feed/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from apps.feed.models import Comment, Post, Reaction, Reply 3 | 4 | 5 | class ReactionAdmin(admin.ModelAdmin): 6 | list_display = ("user", "rtype") 7 | list_filter = ("user", "rtype") 8 | 9 | 10 | class PostAdmin(admin.ModelAdmin): 11 | list_display = ("author", "slug", "created_at", "updated_at") 12 | list_filter = ("author", "slug", "created_at", "updated_at") 13 | 14 | readonly_fields = ("slug",) 15 | 16 | 17 | class CommentAdmin(admin.ModelAdmin): 18 | list_display = ("author", "created_at", "updated_at") 19 | list_filter = ("author", "created_at", "updated_at") 20 | 21 | readonly_fields = ("slug",) 22 | 23 | 24 | class ReplyAdmin(admin.ModelAdmin): 25 | list_display = ("author", "created_at", "updated_at") 26 | list_filter = ("author", "created_at", "updated_at") 27 | 28 | readonly_fields = ("slug",) 29 | 30 | 31 | admin.site.register(Reaction, ReactionAdmin) 32 | admin.site.register(Post, PostAdmin) 33 | admin.site.register(Comment, CommentAdmin) 34 | admin.site.register(Reply, ReplyAdmin) 35 | -------------------------------------------------------------------------------- /apps/feed/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class FeedConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "apps.feed" 7 | -------------------------------------------------------------------------------- /apps/feed/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/feed/migrations/__init__.py -------------------------------------------------------------------------------- /apps/feed/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path("posts/", views.PostsView.as_view()), 7 | path("posts//", views.PostDetailView.as_view()), 8 | path("posts//comments/", views.CommentsView.as_view()), 9 | path("comments//", views.CommentView.as_view()), 10 | path("replies//", views.ReplyView.as_view()), 11 | path("reactions///", views.ReactionsView.as_view()), 12 | path("reactions//", views.RemoveReaction.as_view()), 13 | ] 14 | -------------------------------------------------------------------------------- /apps/general/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/general/__init__.py -------------------------------------------------------------------------------- /apps/general/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.http import HttpResponseRedirect 3 | from django.urls import reverse 4 | from apps.general.models import SiteDetail 5 | 6 | 7 | class SiteDetailAdmin(admin.ModelAdmin): 8 | fieldsets = ( 9 | ("General", {"fields": ["name", "email", "phone", "address"]}), 10 | ("Social", {"fields": ["fb", "tw", "wh", "ig"]}), 11 | ) 12 | 13 | def has_add_permission(self, request): 14 | return False 15 | 16 | def has_delete_permission(self, request, obj=None): 17 | return False 18 | 19 | def changelist_view(self, request, extra_context=None): 20 | obj, created = self.model.objects.get_or_create() 21 | return HttpResponseRedirect( 22 | reverse( 23 | "admin:%s_%s_change" 24 | % (self.model._meta.app_label, self.model._meta.model_name), 25 | args=(obj.id,), 26 | ) 27 | ) 28 | 29 | 30 | admin.site.register(SiteDetail, SiteDetailAdmin) 31 | -------------------------------------------------------------------------------- /apps/general/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class GeneralConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "apps.general" 7 | -------------------------------------------------------------------------------- /apps/general/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-11 13:01 2 | 3 | from django.db import migrations, models 4 | import uuid 5 | 6 | 7 | class Migration(migrations.Migration): 8 | initial = True 9 | 10 | dependencies = [] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="SiteDetail", 15 | fields=[ 16 | ( 17 | "id", 18 | models.UUIDField( 19 | default=uuid.uuid4, 20 | editable=False, 21 | primary_key=True, 22 | serialize=False, 23 | unique=True, 24 | ), 25 | ), 26 | ("created_at", models.DateTimeField(auto_now_add=True)), 27 | ("updated_at", models.DateTimeField(auto_now=True)), 28 | ("name", models.CharField(default="SocialNet", max_length=300)), 29 | ( 30 | "email", 31 | models.EmailField( 32 | default="kayprogrammer1@gmail.com", max_length=254 33 | ), 34 | ), 35 | ("phone", models.CharField(default="+2348133831036", max_length=300)), 36 | ( 37 | "address", 38 | models.CharField(default="234, Lagos, Nigeria", max_length=300), 39 | ), 40 | ( 41 | "fb", 42 | models.CharField( 43 | default="https://facebook.com", 44 | max_length=300, 45 | verbose_name="Facebook", 46 | ), 47 | ), 48 | ( 49 | "tw", 50 | models.CharField( 51 | default="https://twitter.com", 52 | max_length=300, 53 | verbose_name="Twitter", 54 | ), 55 | ), 56 | ( 57 | "wh", 58 | models.CharField( 59 | default="https://wa.me/2348133831036", 60 | max_length=300, 61 | verbose_name="Whatsapp", 62 | ), 63 | ), 64 | ( 65 | "ig", 66 | models.CharField( 67 | default="https://instagram.com", 68 | max_length=300, 69 | verbose_name="Instagram", 70 | ), 71 | ), 72 | ], 73 | options={ 74 | "verbose_name_plural": "Site details", 75 | }, 76 | ), 77 | ] 78 | -------------------------------------------------------------------------------- /apps/general/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/general/migrations/__init__.py -------------------------------------------------------------------------------- /apps/general/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.core.exceptions import ValidationError 3 | from django.utils.translation import gettext_lazy as _ 4 | 5 | from apps.common.models import BaseModel 6 | 7 | 8 | class SiteDetail(BaseModel): 9 | name = models.CharField(max_length=300, default="SocialNet") 10 | email = models.EmailField(default="kayprogrammer1@gmail.com") 11 | phone = models.CharField(max_length=300, default="+2348133831036") 12 | address = models.CharField(max_length=300, default="234, Lagos, Nigeria") 13 | fb = models.CharField( 14 | max_length=300, verbose_name=(_("Facebook")), default="https://facebook.com" 15 | ) 16 | tw = models.CharField( 17 | max_length=300, verbose_name=(_("Twitter")), default="https://twitter.com" 18 | ) 19 | wh = models.CharField( 20 | max_length=300, 21 | verbose_name=(_("Whatsapp")), 22 | default="https://wa.me/2348133831036", 23 | ) 24 | ig = models.CharField( 25 | max_length=300, verbose_name=(_("Instagram")), default="https://instagram.com" 26 | ) 27 | 28 | def __str__(self): 29 | return self.name 30 | 31 | def save(self, *args, **kwargs): 32 | if self._state.adding and SiteDetail.objects.exists(): 33 | raise ValidationError("There can be only one Site Detail instance") 34 | 35 | return super(SiteDetail, self).save(*args, **kwargs) 36 | 37 | class Meta: 38 | verbose_name_plural = "Site details" 39 | -------------------------------------------------------------------------------- /apps/general/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from apps.common.serializers import SuccessResponseSerializer 3 | 4 | 5 | class SiteDetailSerializer(serializers.Serializer): 6 | name = serializers.CharField(default="SocialNet") 7 | email = serializers.EmailField(default="kayprogrammer1@gmail.com") 8 | phone = serializers.CharField(default="+2348133831036") 9 | address = serializers.CharField(default="234, Lagos, Nigeria") 10 | fb = serializers.CharField(default="https://facebook.com") 11 | tw = serializers.CharField(default="https://twitter.com") 12 | wh = serializers.CharField(default="https://wa.me/2348133831036") 13 | ig = serializers.CharField(default="https://instagram.com") 14 | 15 | 16 | class SiteDetailResponseSerializer(SuccessResponseSerializer): 17 | data = SiteDetailSerializer() 18 | -------------------------------------------------------------------------------- /apps/general/tests.py: -------------------------------------------------------------------------------- 1 | from rest_framework.test import APITestCase 2 | 3 | 4 | class TestGeneral(APITestCase): 5 | sitedetail_url = "/api/v1/general/site-detail/" 6 | 7 | def test_retrieve_sitedetail(self): 8 | response = self.client.get(self.sitedetail_url) 9 | self.assertEqual(response.status_code, 200) 10 | result = response.json() 11 | self.assertEqual(result["status"], "success") 12 | self.assertEqual(result["message"], "Site Details fetched") 13 | keys = ["name", "email", "phone", "address", "fb", "tw", "wh", "ig"] 14 | self.assertTrue(all(item in result["data"] for item in keys)) 15 | -------------------------------------------------------------------------------- /apps/general/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path("site-detail/", views.SiteDetailView.as_view()), 7 | ] 8 | -------------------------------------------------------------------------------- /apps/general/views.py: -------------------------------------------------------------------------------- 1 | from adrf.views import APIView 2 | from drf_spectacular.utils import extend_schema 3 | from apps.common.responses import CustomResponse 4 | 5 | from apps.general.models import SiteDetail 6 | from apps.general.serializers import SiteDetailSerializer, SiteDetailResponseSerializer 7 | 8 | 9 | class SiteDetailView(APIView): 10 | serializer_class = SiteDetailSerializer 11 | 12 | @extend_schema( 13 | summary="Retrieve site details", 14 | description="This endpoint retrieves few details of the site/application", 15 | tags=["General"], 16 | responses=SiteDetailResponseSerializer, 17 | ) 18 | async def get(self, request): 19 | sitedetail, created = await SiteDetail.objects.aget_or_create() 20 | serializer = self.serializer_class(sitedetail) 21 | return CustomResponse.success( 22 | message="Site Details fetched", data=serializer.data 23 | ) 24 | -------------------------------------------------------------------------------- /apps/profiles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/profiles/__init__.py -------------------------------------------------------------------------------- /apps/profiles/admin.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from django.contrib import admin 3 | from django.http.request import HttpRequest 4 | 5 | from apps.profiles.models import Friend, Notification 6 | from apps.profiles.utils import send_notification_in_socket 7 | 8 | 9 | class FriendAdmin(admin.ModelAdmin): 10 | list_display = ("requester", "requestee", "status", "created_at", "updated_at") 11 | list_filter = ("requester", "requestee", "status", "created_at", "updated_at") 12 | 13 | 14 | class NotificationAdmin(admin.ModelAdmin): 15 | list_display = ("id", "sender", "ntype", "created_at", "updated_at") 16 | list_filter = ( 17 | "sender", 18 | "receivers", 19 | "ntype", 20 | "read_by", 21 | "created_at", 22 | "updated_at", 23 | ) 24 | 25 | readonly_fields = ("receivers", "read_by", "ntype", "comment", "reply", "sender") 26 | 27 | def save_model(self, request, obj, form, change): 28 | obj.from_admin_site = True 29 | obj.ntype = "ADMIN" 30 | obj.host = request.get_host() 31 | obj.secured = request.is_secure() 32 | super().save_model(request, obj, form, change) 33 | 34 | def delete_model(self, request: HttpRequest, obj: Notification) -> None: 35 | # Send socket notification 36 | loop = asyncio.new_event_loop() 37 | asyncio.set_event_loop(loop) 38 | 39 | loop.run_until_complete( 40 | send_notification_in_socket( 41 | request.is_secure(), request.get_host(), obj, status="DELETED" 42 | ) 43 | ) 44 | super().delete_model(request, obj) 45 | 46 | 47 | admin.site.register(Friend, FriendAdmin) 48 | admin.site.register(Notification, NotificationAdmin) 49 | -------------------------------------------------------------------------------- /apps/profiles/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ProfilesConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "apps.profiles" 7 | -------------------------------------------------------------------------------- /apps/profiles/consumers.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from apps.accounts.models import User 3 | from apps.common.consumers import BaseConsumer 4 | from apps.common.error import ErrorCode 5 | import json 6 | 7 | 8 | class NotificationConsumer(BaseConsumer): 9 | async def connect(self): 10 | err = self.scope["error"] 11 | await self.accept() 12 | 13 | if err.get("message"): # Check for auth errors 14 | await self.send_error_message(err) 15 | return await self.close(code=4001) 16 | self.room_name = "notifications" 17 | self.room_group_name = "notifications" 18 | await self.channel_layer.group_add(self.room_group_name, self.channel_name) 19 | 20 | async def disconnect(self, close_code): 21 | await self.channel_layer.group_discard(self.room_group_name, self.channel_name) 22 | 23 | async def receive(self, text_data): 24 | user = self.scope["user"] 25 | if user != settings.SOCKET_SECRET: 26 | await self.send_error_message( 27 | { 28 | "type": ErrorCode.NOT_ALLOWED, 29 | "message": "You're not allowed to send data", 30 | } 31 | ) 32 | return await self.close(code=1001) 33 | 34 | data = json.loads(text_data) 35 | 36 | # Send notification data 37 | await self.channel_layer.group_send( 38 | self.room_group_name, 39 | {"type": "notification_message", "notification_data": data}, 40 | ) 41 | 42 | async def notification_message(self, event): 43 | notification_data = event["notification_data"] 44 | user = self.scope["user"] 45 | 46 | if isinstance(user, User) and ( 47 | notification_data["ntype"] == "ADMIN" 48 | or await user.notifications.filter(id=notification_data["id"]).aexists() 49 | ): 50 | # Ensure that only receivers of the notification can read it. 51 | await self.send(text_data=json.dumps(notification_data)) 52 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-11 14:50 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import uuid 7 | 8 | 9 | class Migration(migrations.Migration): 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name="Friend", 19 | fields=[ 20 | ( 21 | "id", 22 | models.UUIDField( 23 | default=uuid.uuid4, 24 | editable=False, 25 | primary_key=True, 26 | serialize=False, 27 | unique=True, 28 | ), 29 | ), 30 | ("created_at", models.DateTimeField(auto_now_add=True)), 31 | ("updated_at", models.DateTimeField(auto_now=True)), 32 | ( 33 | "status", 34 | models.CharField( 35 | choices=[ 36 | ("PENDING", "PENDING"), 37 | ("ACCEPTED", "ACCEPTED"), 38 | ("REJECTED", "REJECTED"), 39 | ], 40 | default="PENDING", 41 | max_length=20, 42 | ), 43 | ), 44 | ( 45 | "requestee", 46 | models.ForeignKey( 47 | on_delete=django.db.models.deletion.CASCADE, 48 | related_name="requestee_friends", 49 | to=settings.AUTH_USER_MODEL, 50 | ), 51 | ), 52 | ( 53 | "requester", 54 | models.ForeignKey( 55 | on_delete=django.db.models.deletion.CASCADE, 56 | related_name="requester_friends", 57 | to=settings.AUTH_USER_MODEL, 58 | ), 59 | ), 60 | ], 61 | ), 62 | migrations.AddConstraint( 63 | model_name="friend", 64 | constraint=models.UniqueConstraint( 65 | models.OrderBy(models.F("requester"), models.F("requestee")), 66 | condition=models.Q(("requester", models.F("requestee")), _negated=True), 67 | name="unique_user_combination", 68 | ), 69 | ), 70 | migrations.AddConstraint( 71 | model_name="friend", 72 | constraint=models.CheckConstraint( 73 | check=models.Q(("requester", models.F("requestee")), _negated=True), 74 | name="different_users", 75 | ), 76 | ), 77 | ] 78 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0002_remove_friend_unique_user_combination_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-11 14:51 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("profiles", "0001_initial"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="friend", 14 | name="unique_user_combination", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="friend", 18 | constraint=models.UniqueConstraint( 19 | models.OrderBy(models.F("requester"), models.F("requestee")), 20 | name="unique_user_combination", 21 | ), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0003_remove_friend_unique_user_combination_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-11 14:53 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("profiles", "0002_remove_friend_unique_user_combination_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="friend", 14 | name="unique_user_combination", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="friend", 18 | constraint=models.UniqueConstraint( 19 | models.OrderBy(models.F("requester"), models.F("requestee")), 20 | models.OrderBy(models.F("requestee"), models.F("requester")), 21 | name="unique_user_combination", 22 | ), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0004_remove_friend_unique_user_combination_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-11 15:07 2 | 3 | from django.db import migrations, models 4 | import django.db.models.functions.comparison 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ("profiles", "0003_remove_friend_unique_user_combination_and_more"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveConstraint( 14 | model_name="friend", 15 | name="unique_user_combination", 16 | ), 17 | migrations.AddConstraint( 18 | model_name="friend", 19 | constraint=models.UniqueConstraint( 20 | django.db.models.functions.comparison.Least("requester", "requestee"), 21 | django.db.models.functions.comparison.Greatest( 22 | "requestee", "requester" 23 | ), 24 | name="unique_user_combination", 25 | ), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0005_remove_friend_different_users_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-11 15:11 2 | 3 | from django.db import migrations, models 4 | import django.db.models.functions.comparison 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ("profiles", "0004_remove_friend_unique_user_combination_and_more"), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveConstraint( 14 | model_name="friend", 15 | name="different_users", 16 | ), 17 | migrations.RemoveConstraint( 18 | model_name="friend", 19 | name="unique_user_combination", 20 | ), 21 | migrations.AddConstraint( 22 | model_name="friend", 23 | constraint=models.UniqueConstraint( 24 | django.db.models.functions.comparison.Least("requester", "requestee"), 25 | django.db.models.functions.comparison.Greatest( 26 | "requester", "requestee" 27 | ), 28 | name="bidirectional_unique_user_combination", 29 | violation_error_message="Friend with similar users already exists", 30 | ), 31 | ), 32 | migrations.AddConstraint( 33 | model_name="friend", 34 | constraint=models.CheckConstraint( 35 | check=models.Q(("requester", models.F("requestee")), _negated=True), 36 | name="different_users", 37 | violation_error_message="Requester and Requestee cannot be the same", 38 | ), 39 | ), 40 | ] 41 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0006_alter_friend_status.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-14 17:56 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("profiles", "0005_remove_friend_different_users_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="friend", 14 | name="status", 15 | field=models.CharField( 16 | choices=[("PENDING", "PENDING"), ("ACCEPTED", "ACCEPTED")], 17 | default="PENDING", 18 | max_length=20, 19 | ), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0007_notification.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-30 01:16 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import uuid 7 | 8 | 9 | class Migration(migrations.Migration): 10 | dependencies = [ 11 | ("feed", "0001_initial"), 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ("profiles", "0006_alter_friend_status"), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name="Notification", 19 | fields=[ 20 | ( 21 | "id", 22 | models.UUIDField( 23 | default=uuid.uuid4, 24 | editable=False, 25 | primary_key=True, 26 | serialize=False, 27 | unique=True, 28 | ), 29 | ), 30 | ("created_at", models.DateTimeField(auto_now_add=True)), 31 | ("updated_at", models.DateTimeField(auto_now=True)), 32 | ("text", models.TextField()), 33 | ( 34 | "comment", 35 | models.ForeignKey( 36 | null=True, 37 | on_delete=django.db.models.deletion.SET_NULL, 38 | to="feed.comment", 39 | ), 40 | ), 41 | ( 42 | "post", 43 | models.ForeignKey( 44 | null=True, 45 | on_delete=django.db.models.deletion.SET_NULL, 46 | to="feed.post", 47 | ), 48 | ), 49 | ( 50 | "read_by", 51 | models.ManyToManyField( 52 | related_name="notifications_read", to=settings.AUTH_USER_MODEL 53 | ), 54 | ), 55 | ("receivers", models.ManyToManyField(to=settings.AUTH_USER_MODEL)), 56 | ( 57 | "reply", 58 | models.ForeignKey( 59 | null=True, 60 | on_delete=django.db.models.deletion.SET_NULL, 61 | to="feed.reply", 62 | ), 63 | ), 64 | ( 65 | "sender", 66 | models.ForeignKey( 67 | null=True, 68 | on_delete=django.db.models.deletion.SET_NULL, 69 | related_name="notifications_from", 70 | to=settings.AUTH_USER_MODEL, 71 | ), 72 | ), 73 | ], 74 | options={ 75 | "abstract": False, 76 | }, 77 | ), 78 | ] 79 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0008_notification_ntype.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-09-30 01:26 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("profiles", "0007_notification"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="notification", 14 | name="ntype", 15 | field=models.CharField( 16 | blank=True, 17 | choices=[ 18 | ("REACTION", "REACTION"), 19 | ("COMMENT", "COMMENT"), 20 | ("REPLY", "REPLY"), 21 | ], 22 | max_length=100, 23 | null=True, 24 | verbose_name="Type", 25 | ), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0009_alter_notification_comment_alter_notification_ntype_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-01 15:46 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | ("feed", "0001_initial"), 10 | ("profiles", "0008_notification_ntype"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="notification", 16 | name="comment", 17 | field=models.ForeignKey( 18 | blank=True, 19 | null=True, 20 | on_delete=django.db.models.deletion.SET_NULL, 21 | to="feed.comment", 22 | ), 23 | ), 24 | migrations.AlterField( 25 | model_name="notification", 26 | name="ntype", 27 | field=models.CharField( 28 | blank=True, 29 | choices=[ 30 | ("REACTION", "REACTION"), 31 | ("COMMENT", "COMMENT"), 32 | ("REPLY", "REPLY"), 33 | ("ADMIN", "ADMIN"), 34 | ], 35 | max_length=100, 36 | null=True, 37 | verbose_name="Type", 38 | ), 39 | ), 40 | migrations.AlterField( 41 | model_name="notification", 42 | name="post", 43 | field=models.ForeignKey( 44 | blank=True, 45 | null=True, 46 | on_delete=django.db.models.deletion.SET_NULL, 47 | to="feed.post", 48 | ), 49 | ), 50 | migrations.AlterField( 51 | model_name="notification", 52 | name="reply", 53 | field=models.ForeignKey( 54 | blank=True, 55 | null=True, 56 | on_delete=django.db.models.deletion.SET_NULL, 57 | to="feed.reply", 58 | ), 59 | ), 60 | ] 61 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0010_alter_notification_ntype_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-03 15:39 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 10 | ( 11 | "profiles", 12 | "0009_alter_notification_comment_alter_notification_ntype_and_more", 13 | ), 14 | ] 15 | 16 | operations = [ 17 | migrations.AlterField( 18 | model_name="notification", 19 | name="ntype", 20 | field=models.CharField( 21 | choices=[ 22 | ("REACTION", "REACTION"), 23 | ("COMMENT", "COMMENT"), 24 | ("REPLY", "REPLY"), 25 | ("ADMIN", "ADMIN"), 26 | ], 27 | default="REACTION", 28 | max_length=100, 29 | verbose_name="Type", 30 | ), 31 | preserve_default=False, 32 | ), 33 | migrations.AlterField( 34 | model_name="notification", 35 | name="receivers", 36 | field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), 37 | ), 38 | ] 39 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0011_alter_notification_read_by_alter_notification_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-03 15:45 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 10 | ("profiles", "0010_alter_notification_ntype_and_more"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="notification", 16 | name="read_by", 17 | field=models.ManyToManyField( 18 | blank=True, 19 | related_name="notifications_read", 20 | to=settings.AUTH_USER_MODEL, 21 | ), 22 | ), 23 | migrations.AlterField( 24 | model_name="notification", 25 | name="text", 26 | field=models.TextField(blank=True, null=True), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0012_alter_notification_receivers_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-04 12:55 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 10 | ("profiles", "0011_alter_notification_read_by_alter_notification_text"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="notification", 16 | name="receivers", 17 | field=models.ManyToManyField(to=settings.AUTH_USER_MODEL), 18 | ), 19 | migrations.AddConstraint( 20 | model_name="notification", 21 | constraint=models.CheckConstraint( 22 | check=models.Q( 23 | models.Q( 24 | ("comment", None), ("post__isnull", False), ("reply", None) 25 | ), 26 | models.Q( 27 | ("comment__isnull", False), ("post", None), ("reply", None) 28 | ), 29 | models.Q( 30 | ("comment", None), ("post", None), ("reply__isnull", False) 31 | ), 32 | models.Q(("comment", None), ("post", None), ("reply", None)), 33 | _connector="OR", 34 | ), 35 | name="selected_object_constraints", 36 | violation_error_message="Cannot have cannot have post, comment, reply or any two of the three simultaneously", 37 | ), 38 | ), 39 | migrations.AddConstraint( 40 | model_name="notification", 41 | constraint=models.CheckConstraint( 42 | check=models.Q( 43 | models.Q( 44 | ("ntype", "ADMIN"), ("sender", None), ("text__isnull", False) 45 | ), 46 | models.Q( 47 | models.Q(("ntype", "ADMIN"), _negated=True), 48 | ("sender__isnull", False), 49 | ("text", None), 50 | ), 51 | _connector="OR", 52 | ), 53 | name="sender_text_type_constraints", 54 | violation_error_message="\n If Sender, type must be ADMIN and text musn't be None and vice versa.\n ", 55 | ), 56 | ), 57 | ] 58 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0013_alter_notification_sender.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-04 12:55 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [ 10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 11 | ("profiles", "0012_alter_notification_receivers_and_more"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name="notification", 17 | name="sender", 18 | field=models.ForeignKey( 19 | blank=True, 20 | null=True, 21 | on_delete=django.db.models.deletion.SET_NULL, 22 | related_name="notifications_from", 23 | to=settings.AUTH_USER_MODEL, 24 | ), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0014_remove_notification_selected_object_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-04 13:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("profiles", "0013_alter_notification_sender"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="notification", 14 | name="selected_object_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="notification", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | models.Q( 21 | ("comment", None), ("post__isnull", False), ("reply", None) 22 | ), 23 | models.Q( 24 | ("comment__isnull", False), ("post", None), ("reply", None) 25 | ), 26 | models.Q( 27 | ("comment", None), ("post", None), ("reply__isnull", False) 28 | ), 29 | models.Q( 30 | ("comment", None), 31 | ("ntype", "ADMIN"), 32 | ("post", None), 33 | ("reply", None), 34 | ), 35 | _connector="OR", 36 | ), 37 | name="selected_object_constraints", 38 | violation_error_message="\n Cannot have cannot have post, comment, reply or any two of the three simultaneously\n If the three are None, then it must be of type 'ADMIN'\n ", 39 | ), 40 | ), 41 | ] 42 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0015_remove_notification_selected_object_constraints_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-04 13:14 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("profiles", "0014_remove_notification_selected_object_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveConstraint( 13 | model_name="notification", 14 | name="selected_object_constraints", 15 | ), 16 | migrations.AddConstraint( 17 | model_name="notification", 18 | constraint=models.CheckConstraint( 19 | check=models.Q( 20 | models.Q( 21 | ("comment", None), ("post__isnull", False), ("reply", None) 22 | ), 23 | models.Q( 24 | ("comment__isnull", False), ("post", None), ("reply", None) 25 | ), 26 | models.Q( 27 | ("comment", None), ("post", None), ("reply__isnull", False) 28 | ), 29 | models.Q( 30 | ("comment", None), 31 | ("ntype", "ADMIN"), 32 | ("post", None), 33 | ("reply", None), 34 | ), 35 | _connector="OR", 36 | ), 37 | name="selected_object_constraints", 38 | violation_error_message="Cannot have cannot have post, comment, reply or any two of the three simultaneously. If the three are None, then it must be of type 'ADMIN'", 39 | ), 40 | ), 41 | ] 42 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0016_alter_notification_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-04 13:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("profiles", "0015_remove_notification_selected_object_constraints_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="notification", 14 | name="text", 15 | field=models.CharField(blank=True, max_length=100, null=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0018_alter_notification_receivers.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-05 08:43 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | dependencies = [ 9 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 10 | ("profiles", "0017_remove_notification_sender_text_type_constraints_and_more"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name="notification", 16 | name="receivers", 17 | field=models.ManyToManyField( 18 | related_name="notifications", to=settings.AUTH_USER_MODEL 19 | ), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0019_alter_notification_comment_alter_notification_post_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-05 08:58 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [ 10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 11 | ("feed", "0001_initial"), 12 | ("profiles", "0018_alter_notification_receivers"), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name="notification", 18 | name="comment", 19 | field=models.ForeignKey( 20 | blank=True, 21 | null=True, 22 | on_delete=django.db.models.deletion.CASCADE, 23 | to="feed.comment", 24 | ), 25 | ), 26 | migrations.AlterField( 27 | model_name="notification", 28 | name="post", 29 | field=models.ForeignKey( 30 | blank=True, 31 | null=True, 32 | on_delete=django.db.models.deletion.CASCADE, 33 | to="feed.post", 34 | ), 35 | ), 36 | migrations.AlterField( 37 | model_name="notification", 38 | name="reply", 39 | field=models.ForeignKey( 40 | blank=True, 41 | null=True, 42 | on_delete=django.db.models.deletion.CASCADE, 43 | to="feed.reply", 44 | ), 45 | ), 46 | migrations.AlterField( 47 | model_name="notification", 48 | name="sender", 49 | field=models.ForeignKey( 50 | blank=True, 51 | null=True, 52 | on_delete=django.db.models.deletion.CASCADE, 53 | related_name="notifications_from", 54 | to=settings.AUTH_USER_MODEL, 55 | ), 56 | ), 57 | ] 58 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0020_notification_host_notification_secured.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-05 18:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ( 9 | "profiles", 10 | "0019_alter_notification_comment_alter_notification_post_and_more", 11 | ), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="notification", 17 | name="host", 18 | field=models.CharField(editable=False, max_length=300, null=True), 19 | ), 20 | migrations.AddField( 21 | model_name="notification", 22 | name="secured", 23 | field=models.BooleanField(default=False, editable=False), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0021_alter_notification_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-06 16:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("profiles", "0020_notification_host_notification_secured"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="notification", 14 | name="text", 15 | field=models.CharField(max_length=100, null=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/profiles/migrations/0022_remove_notification_host_remove_notification_secured.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.3 on 2023-10-09 21:37 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("profiles", "0021_alter_notification_text"), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name="notification", 14 | name="host", 15 | ), 16 | migrations.RemoveField( 17 | model_name="notification", 18 | name="secured", 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/profiles/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/apps/profiles/migrations/__init__.py -------------------------------------------------------------------------------- /apps/profiles/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from apps.profiles import consumers 4 | 5 | from . import views 6 | 7 | urlpatterns = [ 8 | path("", views.ProfilesView.as_view()), 9 | path("cities/", views.CitiesView.as_view()), 10 | path("profile//", views.ProfileView.as_view()), 11 | path("profile/", views.ProfileUpdateDeleteView.as_view()), 12 | path("friends/", views.FriendsView.as_view()), 13 | path("friends/requests/", views.FriendRequestsView.as_view()), 14 | path("notifications/", views.NotificationsView.as_view()), 15 | ] 16 | 17 | notification_socket_urlpatterns = [ 18 | path("api/v1/ws/notifications/", consumers.NotificationConsumer.as_asgi()) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/profiles/utils.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | import json, os, websockets 3 | 4 | 5 | def get_notification_message(obj): 6 | """This function returns a notification message""" 7 | ntype = obj.ntype 8 | message = f"{obj.sender.full_name} reacted to your post" 9 | if ntype == "REACTION": 10 | if obj.comment_id: 11 | message = f"{obj.sender.full_name} reacted to your comment" 12 | elif obj.reply_id: 13 | message = f"{obj.sender.full_name} reacted to your reply" 14 | elif ntype == "COMMENT": 15 | message = f"{obj.sender.full_name} commented on your post" 16 | elif ntype == "REPLY": 17 | message = f"{obj.sender.full_name} replied your comment" 18 | return message 19 | 20 | 21 | async def sort_notification_slugs(notification): 22 | if notification.post_id: 23 | notification.post_slug = notification.post.slug 24 | elif notification.comment_id: 25 | notification.comment_slug = notification.comment.slug 26 | notification.post_slug = notification.comment.post.slug 27 | elif notification.reply_id: 28 | notification.reply_slug = notification.reply.slug 29 | notification.comment_slug = notification.reply.comment.slug 30 | notification.post_slug = notification.reply.comment.post.slug 31 | return notification 32 | 33 | 34 | # Send notification in websocket 35 | async def send_notification_in_socket( 36 | secured: bool, host: str, notification: object, status: str = "CREATED" 37 | ): 38 | if os.environ.get("ENVIRONMENT") == "TESTING": 39 | return 40 | websocket_scheme = "wss://" if secured else "ws://" 41 | uri = f"{websocket_scheme}{host}/api/v1/ws/notifications/" 42 | notification_data = { 43 | "id": str(notification.id), 44 | "status": status, 45 | "ntype": notification.ntype, 46 | } 47 | if status == "CREATED": 48 | notification = await sort_notification_slugs(notification) 49 | 50 | from apps.profiles.serializers import NotificationSerializer 51 | 52 | notification_data = NotificationSerializer(notification).data | { 53 | "status": status 54 | } 55 | 56 | headers = [ 57 | ("Authorization", settings.SOCKET_SECRET), 58 | ] 59 | async with websockets.connect(uri, extra_headers=headers) as websocket: 60 | # Send a notification to the WebSocket server 61 | await websocket.send(json.dumps(notification_data)) 62 | await websocket.close() 63 | -------------------------------------------------------------------------------- /build_files.sh: -------------------------------------------------------------------------------- 1 | echo "BUILD START" 2 | python3.9 -m pip install -r requirements.txt 3 | python3.9 manage.py migrate 4 | python3.9 manage.py initial_data 5 | python3.9 manage.py collectstatic --noinput --clear 6 | echo "BUILD END" -------------------------------------------------------------------------------- /display/display1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display1.png -------------------------------------------------------------------------------- /display/display10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display10.png -------------------------------------------------------------------------------- /display/display2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display2.png -------------------------------------------------------------------------------- /display/display3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display3.png -------------------------------------------------------------------------------- /display/display4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display4.png -------------------------------------------------------------------------------- /display/display5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display5.png -------------------------------------------------------------------------------- /display/display6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display6.png -------------------------------------------------------------------------------- /display/display7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display7.png -------------------------------------------------------------------------------- /display/display8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display8.png -------------------------------------------------------------------------------- /display/display9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/display9.png -------------------------------------------------------------------------------- /display/drf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/display/drf.png -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | 3 | services: 4 | api: 5 | build: 6 | context: ./ 7 | dockerfile: Dockerfile 8 | command: /start 9 | volumes: 10 | - .:/build 11 | - static_volume:/build/staticfiles 12 | - media_volume:/build/mediafiles 13 | ports: 14 | - "8000:8000" 15 | environment: 16 | - POSTGRES_SERVER=db 17 | env_file: 18 | - .env 19 | depends_on: 20 | - db 21 | 22 | db: 23 | restart: always 24 | image: postgres:13-alpine 25 | volumes: 26 | - postgres_data:/var/lib/postgresql/data/ 27 | environment: 28 | - POSTGRES_DB=${POSTGRES_DB} 29 | - POSTGRES_USER=${POSTGRES_USER} 30 | - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} 31 | env_file: 32 | - .env 33 | healthcheck: 34 | test: ["CMD-SHELL", "pg_isready -U postgres"] 35 | interval: 5s 36 | timeout: 5s 37 | retries: 5 38 | 39 | pgadmin: 40 | container_name: pgadmin 41 | image: dpage/pgadmin4 42 | environment: 43 | - PGADMIN_DEFAULT_EMAIL=pgadmin4@pgadmin.org 44 | - PGADMIN_DEFAULT_PASSWORD=admin 45 | ports: 46 | - "5050:80" 47 | depends_on: 48 | - db 49 | 50 | volumes: 51 | postgres_data: 52 | static_volume: 53 | media_volume: -------------------------------------------------------------------------------- /docker/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | 5 | set -o pipefail 6 | 7 | set -o nounset 8 | 9 | postgres_ready() { 10 | python << END 11 | import sys 12 | import psycopg 13 | try: 14 | psycopg.connect("host=${POSTGRES_SERVER} port=${POSTGRES_PORT} dbname=${POSTGRES_DB} user=${POSTGRES_USER} password=${POSTGRES_PASSWORD}") 15 | except Exception as e: 16 | print(e) 17 | sys.exit(-1) 18 | sys.exit(0) 19 | END 20 | } 21 | 22 | until postgres_ready; do 23 | >&2 echo "Waiting for PostgreSQL to become available....:-(" 24 | sleep 1 25 | done 26 | >&2 echo "PostgreSQL is ready!!!!...:-)" 27 | 28 | exec "$@" 29 | -------------------------------------------------------------------------------- /docker/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | 5 | set -o pipefail 6 | 7 | set -o nounset 8 | 9 | python3 manage.py migrate --no-input 10 | python3 manage.py collectstatic --no-input 11 | python3 manage.py initial_data 12 | uvicorn socialnet.asgi:application --host 0.0.0.0 --port 8000 --reload 13 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os, sys 4 | 5 | 6 | def main(): 7 | """Run administrative tasks.""" 8 | 9 | os.environ.setdefault( 10 | "DJANGO_SETTINGS_MODULE", 11 | "socialnet.settings.dev", 12 | ) 13 | 14 | try: 15 | from django.core.management import execute_from_command_line 16 | except ImportError as exc: 17 | raise ImportError( 18 | "Couldn't import Django. Are you sure it's installed and " 19 | "available on your PYTHONPATH environment variable? Did you " 20 | "forget to activate a virtual environment?" 21 | ) from exc 22 | execute_from_command_line(sys.argv) 23 | 24 | 25 | if __name__ == "__main__": 26 | main() 27 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | DJANGO_SETTINGS_MODULE = socialnet.settings.dev 3 | python_files = tests.py 4 | filterwarnings = 5 | error 6 | ignore::UserWarning -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | adrf==0.1.1 2 | anyio==4.0.0 3 | asgiref==3.7.2 4 | attrs==23.1.0 5 | certifi==2023.5.7 6 | cffi==1.15.1 7 | channels==4.0.0 8 | channels-redis==4.1.0 9 | charset-normalizer==3.2.0 10 | click==8.1.5 11 | cloudinary==1.33.0 12 | coreschema==0.0.4 13 | cryptography==41.0.4 14 | Django==4.2.3 15 | django-after-response==0.2.2 16 | django-autoslug==1.9.9 17 | django-cities-light==3.9.2 18 | django-cors-headers==4.2.0 19 | django-debug-toolbar==4.2.0 20 | django-jazzmin==2.6.0 21 | djangorestframework==3.14.0 22 | drf-spectacular==0.26.3 23 | h11==0.14.0 24 | httptools==0.6.0 25 | idna==3.4 26 | inflection==0.5.1 27 | iniconfig==2.0.0 28 | itypes==1.2.0 29 | Jinja2==3.1.2 30 | jsonschema==4.18.3 31 | jsonschema-specifications==2023.6.1 32 | MarkupSafe==2.1.3 33 | msgpack==1.0.5 34 | packaging==23.1 35 | pluggy==1.2.0 36 | progressbar2==4.2.0 37 | psycopg==3.2.1 38 | psycopg-binary==3.2.1 39 | pycparser==2.21 40 | PyJWT==2.7.0 41 | pytest==7.4.0 42 | pytest-django==4.5.2 43 | python-decouple==3.8 44 | python-dotenv==1.0.0 45 | python-utils==3.7.0 46 | pytz==2023.3 47 | PyYAML==6.0.1 48 | redis==4.6.0 49 | referencing==0.29.1 50 | requests==2.31.0 51 | rpds-py==0.8.10 52 | six==1.16.0 53 | sniffio==1.3.0 54 | sqlparse==0.4.4 55 | typing_extensions==4.7.1 56 | Unidecode==1.3.6 57 | uritemplate==4.1.1 58 | urllib3==1.26.16 59 | uvicorn==0.23.0 60 | watchfiles==0.20.0 61 | websockets==11.0.3 62 | whitenoise==6.5.0 63 | -------------------------------------------------------------------------------- /socialnet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/socialnet/__init__.py -------------------------------------------------------------------------------- /socialnet/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for socialnet project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | from channels.routing import ProtocolTypeRouter, URLRouter 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault( 15 | "DJANGO_SETTINGS_MODULE", 16 | "socialnet.settings.dev", 17 | ) 18 | 19 | django_asgi_app = get_asgi_application() 20 | 21 | from apps.chat.urls import chatsocket_urlpatterns 22 | from apps.profiles.urls import notification_socket_urlpatterns 23 | from apps.common.socket_auth import SocketAuthMiddleware 24 | 25 | socket_urlpatterns = chatsocket_urlpatterns + notification_socket_urlpatterns 26 | 27 | # AllowedHostsOriginValidator 28 | application = ProtocolTypeRouter( 29 | { 30 | "http": django_asgi_app, 31 | # Just HTTP for now. (We can add other protocols later.) 32 | "websocket": SocketAuthMiddleware(URLRouter(socket_urlpatterns)), 33 | } 34 | ) 35 | 36 | app = application 37 | -------------------------------------------------------------------------------- /socialnet/settings/dev.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | import logging 3 | import logging.config 4 | 5 | from django.utils.log import DEFAULT_LOGGING 6 | 7 | DEBUG = True 8 | 9 | logger = logging.getLogger(__name__) 10 | LOG_LEVEL = "INFO" 11 | logging.config.dictConfig( 12 | { 13 | "version": 1, 14 | "disable_existing_loggers": False, 15 | "formatters": { 16 | "console": { 17 | "format": "%(asctime)s %(name)-12s %(levelname)-8s %(message)s", 18 | }, 19 | "file": {"format": "%(asctime)s %(name)-12s %(levelname)-8s %(message)s"}, 20 | "django.server": DEFAULT_LOGGING["formatters"]["django.server"], 21 | }, 22 | "handlers": { 23 | "console": { 24 | "class": "logging.StreamHandler", 25 | "formatter": "console", 26 | }, 27 | "file": { 28 | "level": "INFO", 29 | "class": "logging.FileHandler", 30 | "formatter": "file", 31 | "filename": "logs/socialnet.log", 32 | }, 33 | "django.server": DEFAULT_LOGGING["handlers"]["django.server"], 34 | }, 35 | "loggers": { 36 | "": { 37 | "level": "INFO", 38 | "handlers": ["console", "file"], 39 | "propagate": False, 40 | }, 41 | "apps": { 42 | "level": "INFO", 43 | "handlers": ["console"], 44 | "propagate": False, 45 | }, 46 | "django.server": DEFAULT_LOGGING["loggers"]["django.server"], 47 | }, 48 | } 49 | ) 50 | -------------------------------------------------------------------------------- /socialnet/settings/prod.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | DEBUG = False 4 | ASGI_PORT = config("PORT") 5 | SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") 6 | SECURE_SSL_REDIRECT = True 7 | -------------------------------------------------------------------------------- /socialnet/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.http import JsonResponse 3 | from django.urls import include, path 4 | from django.conf.urls.static import static 5 | from django.conf import settings 6 | from drf_spectacular.views import ( 7 | SpectacularAPIView, 8 | SpectacularSwaggerView, 9 | ) 10 | from drf_spectacular.utils import extend_schema 11 | from adrf.views import APIView 12 | 13 | from apps.common.responses import CustomResponse 14 | from apps.common.serializers import SuccessResponseSerializer 15 | import debug_toolbar 16 | 17 | 18 | class HealthCheckView(APIView): 19 | @extend_schema( 20 | "/", 21 | summary="API Health Check", 22 | description="This endpoint checks the health of the API", 23 | responses=SuccessResponseSerializer, 24 | tags=["HealthCheck"], 25 | ) 26 | async def get(self, request): 27 | return CustomResponse.success(message="pong") 28 | 29 | 30 | def handler404(request, exception=None): 31 | response = JsonResponse({"status": "failure", "message": "Not Found"}) 32 | response.status_code = 404 33 | return response 34 | 35 | 36 | def handler500(request, exception=None): 37 | response = JsonResponse({"status": "failure", "message": "Server Error"}) 38 | response.status_code = 500 39 | return response 40 | 41 | 42 | handler404 = handler404 43 | handler500 = handler500 44 | 45 | urlpatterns = [ 46 | path("schema/", SpectacularAPIView.as_view(), name="schema"), 47 | path( 48 | "", 49 | SpectacularSwaggerView.as_view(url_name="schema"), 50 | name="swagger-ui", 51 | ), 52 | path("admin/", admin.site.urls), 53 | path("api/v1/general/", include("apps.general.urls")), 54 | path("api/v1/auth/", include("apps.accounts.urls")), 55 | path("api/v1/profiles/", include("apps.profiles.urls")), 56 | path("api/v1/feed/", include("apps.feed.urls")), 57 | path("api/v1/chats/", include("apps.chat.urls")), 58 | path("api/v1/healthcheck/", HealthCheckView.as_view()), 59 | path("__debug__/", include(debug_toolbar.urls)), 60 | ] 61 | 62 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 63 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 64 | -------------------------------------------------------------------------------- /socialnet/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for socialnet project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault( 15 | "DJANGO_SETTINGS_MODULE", 16 | "socialnet.settings.dev", 17 | ) 18 | 19 | 20 | application = get_wsgi_application() 21 | 22 | app = application 23 | -------------------------------------------------------------------------------- /static/media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/static/media/logo.png -------------------------------------------------------------------------------- /staticfiles/mt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kayprogrammer/socialnet-v1/265dd105ff98fbfa658a4adbd4ce311e7ff711c7/staticfiles/mt -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "socialnet/asgi.py", 6 | "use": "@vercel/python", 7 | "config": { 8 | "runtime": "python3.9", 9 | "maxLambdaSize": "15mb" 10 | } 11 | }, 12 | { 13 | "src": "build_files.sh", 14 | "use": "@vercel/static-build", 15 | "config": { 16 | "distDir": "staticfiles" 17 | } 18 | } 19 | ], 20 | "routes": [ 21 | { 22 | "src": "/(.*)", 23 | "dest": "socialnet/asgi.py" 24 | } 25 | ] 26 | } --------------------------------------------------------------------------------