Shellmiao 3 лет назад
Сommit
0f731bb680
100 измененных файлов с 1461 добавлено и 0 удалено
  1. 8 0
      .idea/.gitignore
  2. 12 0
      .idea/dataSources.xml
  3. 22 0
      .idea/inspectionProfiles/Project_Default.xml
  4. 6 0
      .idea/inspectionProfiles/profiles_settings.xml
  5. 4 0
      .idea/misc.xml
  6. 8 0
      .idea/modules.xml
  7. 30 0
      .idea/st_cloud.iml
  8. 7 0
      .idea/vcs.xml
  9. 0 0
      README.md
  10. 0 0
      account/__init__.py
  11. BIN
      account/__pycache__/__init__.cpython-38.pyc
  12. BIN
      account/__pycache__/admin.cpython-38.pyc
  13. BIN
      account/__pycache__/apps.cpython-38.pyc
  14. BIN
      account/__pycache__/decorators.cpython-38.pyc
  15. BIN
      account/__pycache__/models.cpython-38.pyc
  16. BIN
      account/__pycache__/urls.cpython-38.pyc
  17. BIN
      account/__pycache__/utils.cpython-38.pyc
  18. BIN
      account/__pycache__/validators.cpython-38.pyc
  19. BIN
      account/__pycache__/views.cpython-38.pyc
  20. 16 0
      account/admin.py
  21. 6 0
      account/apps.py
  22. 38 0
      account/decorators.py
  23. 48 0
      account/migrations/0001_initial.py
  24. 16 0
      account/migrations/0002_delete_profile.py
  25. 27 0
      account/migrations/0003_auto_20210911_1439.py
  26. 0 0
      account/migrations/__init__.py
  27. BIN
      account/migrations/__pycache__/0001_initial.cpython-38.pyc
  28. BIN
      account/migrations/__pycache__/0002_delete_profile.cpython-38.pyc
  29. BIN
      account/migrations/__pycache__/0003_auto_20210911_1439.cpython-38.pyc
  30. BIN
      account/migrations/__pycache__/__init__.cpython-38.pyc
  31. 101 0
      account/models.py
  32. 8 0
      account/serializers.py
  33. 19 0
      account/templates/create_group.html
  34. 25 0
      account/templates/login.html
  35. 30 0
      account/templates/register.html
  36. 3 0
      account/tests.py
  37. 13 0
      account/urls.py
  38. 24 0
      account/utils.py
  39. 25 0
      account/validators.py
  40. 152 0
      account/views.py
  41. BIN
      db.sqlite3
  42. 48 0
      docs/account.md
  43. 28 0
      docs/file.md
  44. 35 0
      docs/folder.md
  45. 41 0
      docs/group.md
  46. 0 0
      file/__init__.py
  47. BIN
      file/__pycache__/__init__.cpython-38.pyc
  48. BIN
      file/__pycache__/admin.cpython-38.pyc
  49. BIN
      file/__pycache__/apps.cpython-38.pyc
  50. BIN
      file/__pycache__/judgement_function.cpython-38.pyc
  51. BIN
      file/__pycache__/models.cpython-38.pyc
  52. BIN
      file/__pycache__/urls.cpython-38.pyc
  53. BIN
      file/__pycache__/views.cpython-38.pyc
  54. 10 0
      file/admin.py
  55. 6 0
      file/apps.py
  56. 38 0
      file/interface.md
  57. 31 0
      file/judgement_function.py
  58. 28 0
      file/migrations/0001_initial.py
  59. 20 0
      file/migrations/0002_alter_file_folder.py
  60. 25 0
      file/migrations/0003_auto_20210909_2239.py
  61. 20 0
      file/migrations/0004_file_group.py
  62. 18 0
      file/migrations/0005_rename_folder_file_father_folder.py
  63. 38 0
      file/migrations/0006_auto_20210911_1439.py
  64. 18 0
      file/migrations/0007_alter_file_key.py
  65. 0 0
      file/migrations/__init__.py
  66. BIN
      file/migrations/__pycache__/0001_initial.cpython-38.pyc
  67. BIN
      file/migrations/__pycache__/0002_alter_file_folder.cpython-38.pyc
  68. BIN
      file/migrations/__pycache__/0003_auto_20210909_2239.cpython-38.pyc
  69. BIN
      file/migrations/__pycache__/0004_file_group.cpython-38.pyc
  70. BIN
      file/migrations/__pycache__/0005_rename_folder_file_father_folder.cpython-38.pyc
  71. BIN
      file/migrations/__pycache__/0006_auto_20210911_1439.cpython-38.pyc
  72. BIN
      file/migrations/__pycache__/0007_alter_file_key.cpython-38.pyc
  73. BIN
      file/migrations/__pycache__/__init__.cpython-38.pyc
  74. 58 0
      file/models.py
  75. 3 0
      file/tests.py
  76. 9 0
      file/urls.py
  77. 128 0
      file/views.py
  78. 0 0
      folder/__init__.py
  79. BIN
      folder/__pycache__/__init__.cpython-38.pyc
  80. BIN
      folder/__pycache__/admin.cpython-38.pyc
  81. BIN
      folder/__pycache__/apps.cpython-38.pyc
  82. BIN
      folder/__pycache__/models.cpython-38.pyc
  83. BIN
      folder/__pycache__/urls.cpython-38.pyc
  84. BIN
      folder/__pycache__/views.cpython-38.pyc
  85. 10 0
      folder/admin.py
  86. 6 0
      folder/apps.py
  87. 50 0
      folder/interface.md
  88. 23 0
      folder/migrations/0001_initial.py
  89. 18 0
      folder/migrations/0002_alter_folder_folder_name.py
  90. 19 0
      folder/migrations/0003_alter_folder_father_folder.py
  91. 19 0
      folder/migrations/0004_alter_folder_father_folder.py
  92. 21 0
      folder/migrations/0005_folder_owner.py
  93. 20 0
      folder/migrations/0006_folder_group.py
  94. 25 0
      folder/migrations/0007_auto_20210910_1615.py
  95. 0 0
      folder/migrations/__init__.py
  96. BIN
      folder/migrations/__pycache__/0001_initial.cpython-38.pyc
  97. BIN
      folder/migrations/__pycache__/0002_alter_folder_folder_name.cpython-38.pyc
  98. BIN
      folder/migrations/__pycache__/0003_alter_folder_father_folder.cpython-38.pyc
  99. BIN
      folder/migrations/__pycache__/0004_alter_folder_father_folder.cpython-38.pyc
  100. BIN
      folder/migrations/__pycache__/0005_folder_owner.cpython-38.pyc

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 12 - 0
.idea/dataSources.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
+    <data-source source="LOCAL" name="db" uuid="1407b81f-6eb8-4a13-bf4d-8e18ebbdb340">
+      <driver-ref>sqlite.xerial</driver-ref>
+      <synchronize>true</synchronize>
+      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
+      <jdbc-url>jdbc:sqlite:C:\Users\k\PycharmProjects\st-cloud\db.sqlite3</jdbc-url>
+      <working-dir>$ProjectFileDir$</working-dir>
+    </data-source>
+  </component>
+</project>

+ 22 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,22 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
+      <option name="ignoredErrors">
+        <list>
+          <option value="N801" />
+          <option value="N806" />
+          <option value="N802" />
+        </list>
+      </option>
+    </inspection_tool>
+    <inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="ignoredIdentifiers">
+        <list>
+          <option value="chat.models.message.objects" />
+          <option value="chat.models.message.DoesNotExist" />
+        </list>
+      </option>
+    </inspection_tool>
+  </profile>
+</component>

+ 6 - 0
.idea/inspectionProfiles/profiles_settings.xml

@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="USE_PROJECT_PROFILE" value="false" />
+    <version value="1.0" />
+  </settings>
+</component>

+ 4 - 0
.idea/misc.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (st-cloud)" project-jdk-type="Python SDK" />
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/st_cloud.iml" filepath="$PROJECT_DIR$/.idea/st_cloud.iml" />
+    </modules>
+  </component>
+</project>

+ 30 - 0
.idea/st_cloud.iml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="django" name="Django">
+      <configuration>
+        <option name="rootFolder" value="$MODULE_DIR$" />
+        <option name="settingsModule" value="st_cloud/settings.py" />
+        <option name="manageScript" value="$MODULE_DIR$/manage.py" />
+        <option name="environment" value="&lt;map/&gt;" />
+        <option name="doNotUseTestRunner" value="false" />
+        <option name="trackFilePattern" value="migrations" />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/venv" />
+    </content>
+    <orderEntry type="jdk" jdkName="Python 3.9 (st-cloud)" jdkType="Python SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+  <component name="TemplatesService">
+    <option name="TEMPLATE_CONFIGURATION" value="Django" />
+    <option name="TEMPLATE_FOLDERS">
+      <list>
+        <option value="$MODULE_DIR$/../st_cloud\templates" />
+      </list>
+    </option>
+  </component>
+</module>

+ 7 - 0
.idea/vcs.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/libgit2" vcs="Git" />
+  </component>
+</project>


+ 0 - 0
account/__init__.py


BIN
account/__pycache__/__init__.cpython-38.pyc


BIN
account/__pycache__/admin.cpython-38.pyc


BIN
account/__pycache__/apps.cpython-38.pyc


BIN
account/__pycache__/decorators.cpython-38.pyc


BIN
account/__pycache__/models.cpython-38.pyc


BIN
account/__pycache__/urls.cpython-38.pyc


BIN
account/__pycache__/utils.cpython-38.pyc


BIN
account/__pycache__/validators.cpython-38.pyc


BIN
account/__pycache__/views.cpython-38.pyc


+ 16 - 0
account/admin.py

@@ -0,0 +1,16 @@
+from django.contrib import admin
+from .models import User, LoginToken
+
+
+# Register your models here.
+
+class UserAdmin(admin.ModelAdmin):
+    list_display = ["username", "password", "email"]
+
+
+class LoginTokenAdmin(admin.ModelAdmin):
+    list_display = ["user", "token"]
+
+
+admin.site.register(User, UserAdmin)
+admin.site.register(LoginToken, LoginTokenAdmin)

+ 6 - 0
account/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class AccountConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'account'

+ 38 - 0
account/decorators.py

@@ -0,0 +1,38 @@
+from functools import wraps
+
+from .models import User
+from utils.http import make_json_response
+
+
+def _login_required(error='未登录'):
+    # 判断是否登录的decorator
+    def is_login(request):
+        if request.method != 'POST':
+            return False
+        username = request.POST.get('username', '')
+        token = request.POST.get('token', '')
+        print(f'username={username} token={token}')
+        try:
+            user = User.objects.get(username=username)
+        except:
+            print('用户不存在')
+            return False
+        if not user.check_token(token) or not user.tokens.filter(token=token):
+            print('token无效')
+            return False
+        print('已登录')
+        if hasattr(request, 'user'):
+            request.user = user
+        return True
+
+    def decorator(view_func):
+        @wraps(view_func)
+        def _wrapped_view(request, *args, **kwargs):
+            if is_login(request):
+                return view_func(request, *args, **kwargs)
+            return make_json_response(code=401, error=error)
+        return _wrapped_view
+
+    return decorator
+
+login_required = _login_required()

+ 48 - 0
account/migrations/0001_initial.py

@@ -0,0 +1,48 @@
+# Generated by Django 3.2.5 on 2021-09-09 07:30
+
+import account.validators
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('folder', '0002_alter_folder_folder_name'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='User',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('username', models.CharField(error_messages={'unique': 'A _user with that username already exists.'}, help_text='Required. 25 characters or fewer. Letters, digits and _ only.', max_length=25, unique=True, validators=[account.validators.ASCIIUsernameValidator()], verbose_name='username')),
+                ('password', models.CharField(max_length=128, verbose_name='password')),
+                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+                ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')),
+            ],
+            options={
+                'verbose_name': '用户信息表',
+                'verbose_name_plural': '用户信息表',
+                'db_table': '_user',
+            },
+        ),
+        migrations.CreateModel(
+            name='Profile',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('root_folder', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='profile', to='folder.folder')),
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to='account.user')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='LoginToken',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('token', models.CharField(max_length=256)),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tokens', to='account.user')),
+            ],
+        ),
+    ]

+ 16 - 0
account/migrations/0002_delete_profile.py

@@ -0,0 +1,16 @@
+# Generated by Django 3.2.5 on 2021-09-09 16:10
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('account', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.DeleteModel(
+            name='Profile',
+        ),
+    ]

+ 27 - 0
account/migrations/0003_auto_20210911_1439.py

@@ -0,0 +1,27 @@
+# Generated by Django 3.2.7 on 2021-09-11 06:39
+
+import account.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('account', '0002_delete_profile'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='user',
+            options={},
+        ),
+        migrations.AlterField(
+            model_name='user',
+            name='username',
+            field=models.CharField(max_length=25, unique=True, validators=[account.validators.ASCIIUsernameValidator()]),
+        ),
+        migrations.AlterModelTable(
+            name='user',
+            table=None,
+        ),
+    ]

+ 0 - 0
account/migrations/__init__.py


BIN
account/migrations/__pycache__/0001_initial.cpython-38.pyc


BIN
account/migrations/__pycache__/0002_delete_profile.cpython-38.pyc


BIN
account/migrations/__pycache__/0003_auto_20210911_1439.cpython-38.pyc


BIN
account/migrations/__pycache__/__init__.cpython-38.pyc


+ 101 - 0
account/models.py

@@ -0,0 +1,101 @@
+from datetime import datetime
+from django.core.mail import send_mail
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+from django.utils.http import base36_to_int, int_to_base36
+from django.conf import settings
+from django.utils.crypto import salted_hmac
+
+from .utils import encode_password
+from .validators import ASCIIUsernameValidator
+
+
+class User(models.Model):
+    username = models.CharField(
+        max_length=25,
+        unique=True,
+        validators=[ASCIIUsernameValidator()]
+    )
+    password = models.CharField(_('password'), max_length=128)
+    last_login = models.DateTimeField(_('last login'), blank=True, null=True)
+    email = models.EmailField(_('email address'), unique=True)
+
+    def save(self, *args, **kwargs):
+        self.password = encode_password(self.username, self.password)
+        super(User, self).save(*args, **kwargs)
+
+    def get_root_folder(self):
+        return self.folders.get(father_folder=None, group=None)
+
+    def send_email(self, subject, message, from_email=None, **kwargs):
+        send_mail(subject, message, from_email, [self.email], **kwargs)
+
+    def make_token(self):
+        return self._make_token(_timestamp())
+
+    def check_token(self, token):
+        if not token:
+            return False
+        try:
+            ts_b36, hash_str = token.split('-')
+        except ValueError:
+            return False
+
+        try:
+            ts = base36_to_int(ts_b36)
+        except ValueError:
+            return False
+
+        if self._make_token(ts) != token:
+            return False
+
+        timestamp = _timestamp()
+        if (timestamp - ts) > settings.PASSWORD_RESET_TIMEOUT:
+            return False
+
+        return True
+
+    def _make_token(self, timestamp):
+        ts_b36 = int_to_base36(timestamp)
+        salt = settings.SALT
+        value = self._make_hash_value(timestamp)
+        secret = settings.SECRET_KEY
+        hash_str = salted_hmac(
+            salt, value, secret=secret, algorithm='sha256'
+        ).hexdigest()[::2]
+        token = "%s-%s" % (ts_b36, hash_str)
+        return token
+
+    def _make_hash_value(self, timestamp):
+        return f'{self.pk}{self.password}{timestamp}{self.email}'
+
+    def __str__(self):
+        return self.username
+
+
+def get_user(request):
+    if hasattr(request, 'user') and isinstance(request.user, User):
+        print(f'get user from request.user, username={request.user.username}')
+        return request.user
+    username = request.POST.get('username', '')
+    token = request.POST.get('token', '')
+    try:
+        user = User.objects.get(username=username)
+        if user.check_token(token):
+            user.tokens.get(token=token)
+            return user
+    except:
+        return None
+    return None
+
+
+def _timestamp():
+    dt = datetime.now()
+    return int((dt - datetime(2001, 1, 1)).total_seconds())
+
+
+class LoginToken(models.Model):
+    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tokens')
+    token = models.CharField(max_length=256)
+
+

+ 8 - 0
account/serializers.py

@@ -0,0 +1,8 @@
+from rest_framework import serializers
+from .models import User, LoginToken
+
+
+class UserSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = User
+        fields = ('username', 'password', 'email')

+ 19 - 0
account/templates/create_group.html

@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+    <div>
+        <form method="post" action=".">
+            {% csrf_token %}
+            <!-- 昵称 -->
+            <div>
+                <label for="username">昵称</label>
+                <input type="text" id="username" name="username">
+            </div>
+            <!-- 群名 -->
+            <div>
+                <label for="group_name">昵称</label>
+                <input type="text" id="group_name" name="group_name">
+            </div>
+            <button type="submit">提交</button>
+        </form>
+    </div>
+</html>

+ 25 - 0
account/templates/login.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+    <div>
+        <form method="post" action=".">
+            {% csrf_token %}
+            <!-- 账号 -->
+            <div>
+                <label for="username">账号</label>
+                <input type="text" id="username" name="username">
+            </div>
+            <!-- 密码 -->
+            <div>
+                <label for="password">密码</label>
+                <input type="password" id="password" name="password">
+            </div>
+            <!-- new_token -->
+            <div>
+                <label for="token">新token</label>
+                <input type="text" id="token" name="token">
+            </div>
+            <!-- 提交按钮 -->
+            <button type="submit">提交</button>
+        </form>
+    </div>
+</html>

+ 30 - 0
account/templates/register.html

@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+    <div>
+        <form method="post" action=".">
+            {% csrf_token %}
+            <!-- 昵称 -->
+            <div>
+                <label for="username">昵称</label>
+                <input type="text" id="username" name="username">
+            </div>
+            <!-- 邮箱 -->
+            <div>
+                <label for="email">邮箱</label>
+                <input type="text" id="email" name="email">
+            </div>
+            <!-- 密码 -->
+            <div>
+                <label for="password">设置密码</label>
+                <input type="password" id="password" name="password" required>
+            </div>
+            <!-- 确认密码 -->
+            <div>
+                <label for="password2">确认密码</label>
+                <input type="password" id="password2" name="password2" required>
+            </div>
+            <!-- 提交按钮 -->
+            <button type="submit">提交</button>
+        </form>
+    </div>
+</html>

+ 3 - 0
account/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 13 - 0
account/urls.py

@@ -0,0 +1,13 @@
+from django.shortcuts import render
+from django.urls import path
+from . import views
+# Create your views here.
+urlpatterns = [
+    # path('register/', views.user_register, name='register'),
+    path('login/', views.login, name='login'),
+    path('logout/', views.logout, name='logout'),
+    path('register/', views.register, name='register'),
+    path('reset_password/', views.reset_password, name='reset_password'),
+    path('send_email_verification_code/', views.send_email_verification_code, name='send_email_verification_code'),
+    path('check_token/', views.check_token, name='check_token')
+]

+ 24 - 0
account/utils.py

@@ -0,0 +1,24 @@
+from string import ascii_letters, digits, printable
+
+
+def auth_with_username_or_email(username, password):
+    password = encode_password(username, password)
+    from account.models import User
+    if '@' in username:
+        user = User.objects.get(email=username, password=password)
+    else:
+        user = User.objects.get(username=username, password=password)
+    return user
+
+
+def check_password(password):
+    if set(password).isdisjoint(ascii_letters) and set(password).isdisjoint(digits):
+        return False
+    return set(password).issubset(printable) and len(password) >= 8
+
+
+def encode_password(username, password):
+    import hashlib
+    md5 = hashlib.md5()
+    md5.update((username + password).encode())
+    return md5.hexdigest()

+ 25 - 0
account/validators.py

@@ -0,0 +1,25 @@
+import re
+
+from django.core import validators
+from django.utils.deconstruct import deconstructible
+from django.utils.translation import gettext_lazy as _
+
+
+@deconstructible
+class ASCIIUsernameValidator(validators.RegexValidator):
+    regex = r'^[\w]+\Z'
+    message = _(
+        'Enter a valid username. This value may contain only English letters, '
+        'numbers, and _ characters.'
+    )
+    flags = re.ASCII
+
+
+@deconstructible
+class UnicodeUsernameValidator(validators.RegexValidator):
+    regex = r'^[\w]+\Z'
+    message = _(
+        'Enter a valid username. This value may contain only letters, '
+        'numbers, and _ characters.'
+    )
+    flags = 0

+ 152 - 0
account/views.py

@@ -0,0 +1,152 @@
+# Create your views here.
+
+from datetime import datetime
+from .models import User, LoginToken
+from utils.crypto import secure_transport
+from utils.http import make_json_response
+from .models import get_user
+from django.views.decorators.http import require_POST
+from .decorators import login_required
+from .utils import auth_with_username_or_email, check_password
+
+
+@secure_transport
+@require_POST
+def register(request):
+    username = request.POST.get('username', '')
+    password = request.POST.get('password', '')
+    email = request.POST.get('email', '')
+    if not username or not password or not email:
+        return make_json_response(code=303, error='用户名/密码/邮箱不能为空')
+
+    if User.objects.filter(username=username):
+        return make_json_response(code=301, error='用户名已存在')
+
+    if User.objects.filter(email=email):
+        return make_json_response(code=302, error='邮箱已存在')
+
+    if not check_password(password):
+        return make_json_response(code=304, error='密码长度不能小于8, 必须包含数字和英文字符')
+
+    try:
+        User.objects.create(username=username, password=password, email=email)
+        print('注册成功')
+        return make_json_response()
+    except Exception as e:
+        return make_json_response(code=500, error=str(e))
+
+
+@secure_transport
+@require_POST
+def login(request):
+    username = request.POST.get('username', '')
+    password = request.POST.get('password', '')
+    old_token = request.POST.get('token', '')
+
+    try:
+        user = auth_with_username_or_email(username, password)
+        print(user)
+    except:
+        return make_json_response(code=303, error='用户名或密码错误')
+
+    print(f'token = {old_token}')
+    if user.check_token(old_token):
+        try:
+            user_token = user.tokens.get(token=old_token)
+            print('已登录')
+            user_token.delete()
+            # return JsonResponse({'code': 303, 'msg': '已登录'}, status=303)
+        except Exception as e:
+            print('token无效')
+    else:
+        print('token已过期')
+
+    user.last_login = datetime.now()
+
+    token = user.make_token()
+    user_token = LoginToken()
+    user_token.user = user
+    user_token.token = token
+    user_token.save()
+
+    print('登录成功')
+    print(f'token = {token}')
+    return make_json_response(token=token)
+
+
+@secure_transport
+@login_required
+def logout(request):
+    user = get_user(request)
+    data = request.POST
+    token = data.get('token')
+    user_token = user.tokens.get(token=token)
+    user_token.delete()
+    return make_json_response()
+
+
+@secure_transport
+@require_POST
+def send_email_verification_code(request):
+    data = request.POST
+    username = data.get('username')
+    email = data.get('email')
+    try:
+        user = User.objects.get(username=username)
+    except:
+        return make_json_response(code=302, error='用户不存在')
+    if user.email != email:
+        return make_json_response(code=301, error='邮箱错误')
+    try:
+        # 发送验证码
+        token = user.make_token()
+        print(f'发送验证码 email = {user.email} token = {token}')
+        user.send_email('ST网盘重置密码验证码', token)
+        return make_json_response()
+    except Exception as e:
+        print(e)
+        return make_json_response(code=500, error='验证码发送失败')
+
+
+@secure_transport
+@require_POST
+def check_token(request):
+    data = request.POST
+    username = data.get('username')
+    token = data.get('token')
+    print(username)
+    try:
+        user = User.objects.get(username=username)
+    except:
+        return make_json_response(code=302, error='用户不存在')
+    print(f'token={token}')
+    if token and user.check_token(token):
+        print('验证码有效')
+        return make_json_response()
+    else:
+        return make_json_response(code=303, error='验证码无效')
+
+
+@secure_transport
+@require_POST
+def reset_password(request):
+    data = request.POST
+    username = data.get('username')
+    password = data.get('password')
+    token = data.get('token')
+
+    if not check_password(password):
+        return make_json_response(code=304, error='密码长度不能小于8, 必须包含数字和英文字符')
+    try:
+        user = User.objects.get(username=username)
+    except:
+        return make_json_response(code=302, error='用户不存在')
+    print(f'token={token}')
+    if token and user.check_token(token):
+        # 重置密码
+        print("验证码有效")
+        user.password = password
+        user.save()
+        return make_json_response()
+    else:
+        return make_json_response(code=303, error='验证码无效')


+ 48 - 0
docs/account.md

@@ -0,0 +1,48 @@
+# account
+
+## register
+### params
+'username', 'password', 'email'
+### return
+- code=303, error='用户名/密码/邮箱不能为空'
+- code=301, error='用户名已存在'
+- code=302, error='邮箱已存在'
+- code=500, error=str(e)
+- code=200
+
+## login
+### params
+'username', 'password'
+### return
+- code=303, error='用户名或密码错误'
+- code=200, token=token
+
+## logout
+### params
+'username', 'token'
+### return
+- code=401, error='未登录'
+- code=200
+
+## send_email_verification_code
+### params
+'username', 'email'
+### return
+- code=302, error='用户不存在'
+- code=301, error='邮箱错误'
+- code=500, error='验证码发送失败'
+
+## check_token
+### params
+'username', 'token'
+### return
+- code=302, error='用户不存在'
+- code=303, error='验证码无效'
+- code=200
+
+## reset_password
+### params
+'username', 'password', 'token'
+### return
+- code=302, error='用户不存在'
+- code=303, error='验证码无效'

+ 28 - 0
docs/file.md

@@ -0,0 +1,28 @@
+# file
+## upload_file
+### params
+'username', 'token', 'father'
+### return
+- code=401, error='未登录'
+- code=400, error='文件不存在'
+- code=402, error='文件夹不存在'
+- code=404, error='没有上传文件的权限'
+- code=500, error='文件保存失败'
+
+## download_file
+### params
+'username', 'token', 'file_id'
+### return
+- code=401, error='未登录'
+- code=402, error='文件不存在'
+- code=404, error='没有下载文件的权限'
+- 文件
+
+## delete_file
+### params
+- 'username', 'token', 'file_id'
+### return
+- code=401, error='未登录'
+- code=402, error='文件不存在'
+- code=404, error='没有删除文件的权限'
+- code=200

+ 35 - 0
docs/folder.md

@@ -0,0 +1,35 @@
+# folder
+
+## get_root_folder
+### params
+'username', 'token'
+### return
+- code=200, root_folder_id
+- code=401, error='未登录'
+
+## folder_list
+### params
+'username', 'token', 'folder_id'
+### return
+- code=401, error='未登录'
+- code=400, error='文件夹不存在'
+- code=404, error='没有权限'
+- code=200, children=[] (json列表)
+
+## add_folder
+### params
+'username', 'token', 'father_folder_id', 'folder_name'
+### return
+- code=401, error='未登录'
+- code=400, error='上级文件夹不存在'
+- code=404, error='没有权限'
+- code=200
+
+## delete_folder
+### params
+'username', 'token', 'folder_id'
+### return
+- code=401, error='未登录'
+- code=400, error='无此文件夹'
+- code=404, error='没有删除文件的权限'
+- code=200

+ 41 - 0
docs/group.md

@@ -0,0 +1,41 @@
+# group
+
+## get_group_root_folder
+### params
+'username', 'token', 'group_id'
+### return
+- code=401, error='未登录'
+- code=403, error='群不存在'
+- code=200, root_folder_id
+
+## join_group
+### params
+'username', 'token', 'group_id'
+### return
+- code=401, error='未登录'
+- code=402, error='已在群内'
+- code=403, error='群不存在'
+- code=200
+
+## create_group
+### params
+'username', 'token', 'group_name'
+### return
+- code=401, error='未登录'
+- code=500, error='新建群失败'
+- code=200
+
+## quit_group
+### params
+'username', 'token', 'group_id'
+### return
+- code=401, error='未登录'
+- code=403, error='群不存在'
+- code=421, error='群主不可退群'
+- code=200
+
+## group_list
+### params
+'username', 'token'
+### return
+- code=200, group_list

+ 0 - 0
file/__init__.py


BIN
file/__pycache__/__init__.cpython-38.pyc


BIN
file/__pycache__/admin.cpython-38.pyc


BIN
file/__pycache__/apps.cpython-38.pyc


BIN
file/__pycache__/judgement_function.cpython-38.pyc


BIN
file/__pycache__/models.cpython-38.pyc


BIN
file/__pycache__/urls.cpython-38.pyc


BIN
file/__pycache__/views.cpython-38.pyc


+ 10 - 0
file/admin.py

@@ -0,0 +1,10 @@
+from django.contrib import admin
+from .models import File
+
+
+# Register your models here.
+class FileAdmin(admin.ModelAdmin):
+    list_display = ["file_id", "file_name", "father_folder", "update_time", "file_type", "owner", "group"]
+
+
+admin.site.register(File, FileAdmin)

+ 6 - 0
file/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class FileConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'file'

+ 38 - 0
file/interface.md

@@ -0,0 +1,38 @@
+# 上传文件
+******
+## 请求(POST)
+请求链接:file/upload_file
+
+file_path(文件路径)
+folder_id(文件夹id)
+文件
+## 返回值(http状态码+(列表))
+200:成功
+
+400:请求不合法
+
+# 下载文件
+******
+## 请求(POST)
+请求链接:file/download_file
+
+file_path(文件路径)
+## 返回值(文件)
+200:返回文件
+
+400:请求不合法
+
+# 删除文件
+******
+## 请求(POST)
+请求链接:file/delete_file
+
+file_path(文件路径)
+file_id(文件id)
+## 返回值(http状态码+列表)
+200:创建成功
+
+421:此文件不存在
+
+400:请求不合法
+

+ 31 - 0
file/judgement_function.py

@@ -0,0 +1,31 @@
+
+def judge_filepath(file_type):
+    img_list = ['bmp', 'jpg', 'png', 'tif', 'gif', 'pcx', 'tga', 'exif', 'fpx', 'svg', 'psd', 'cdr', 'pcd', 'dxf',
+                'ufo', 'eps', 'ai', 'raw', 'WMF', 'webp']
+    doc_list = ['txt', 'doc', 'xls', 'ppt', 'docx', 'xlsx', 'pptx', 'lrc', 'wps', 'zip', 'rar', '7z', 'torrent', 'pdf']
+    video_list = ['cd', 'ogg', 'mp3', 'asf', 'wma', 'wav', 'mp3pro', 'rm', 'mp4', 'real', 'ape', 'module', 'midi',
+                  'vqf']
+    procedure_list = ['exe', 'py', 'java', 'class', 'pyc', 'app', 'apk', 'bat']
+    if file_type in img_list:
+        file_path = 'img'
+    elif file_type in doc_list:
+        file_path = 'doc'
+    elif file_type in video_list:
+        file_path = 'video'
+    elif file_type in procedure_list:
+        file_path = 'procedure'
+    else:
+        file_path = 'others'
+    return file_path
+
+
+def format_size(old_size):
+    if 1024 < old_size < 1024 * 1024:
+        new_size = round(old_size / 1024, 2)
+        return str(new_size) + 'KB'
+    elif 1024 * 1024 < old_size < 1024 * 1024 * 1024:
+        new_size = round(old_size / (1024 * 1024), 2)
+        return str(new_size) + 'MB'
+    elif old_size > 1024 * 1024 * 1024:
+        new_size = round(old_size / (1024 * 1024 * 1024), 2)
+        return str(new_size) + 'GB'

+ 28 - 0
file/migrations/0001_initial.py

@@ -0,0 +1,28 @@
+# Generated by Django 3.2.7 on 2021-09-08 15:01
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('folder', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='File',
+            fields=[
+                ('file_id', models.AutoField(primary_key=True, serialize=False)),
+                ('file_name', models.CharField(max_length=50)),
+                ('file_path', models.CharField(max_length=128)),
+                ('update_time', models.DateTimeField()),
+                ('file_type', models.CharField(max_length=32)),
+                ('file_size', models.CharField(max_length=16)),
+                ('folder', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='file_folder', to='folder.folder')),
+            ],
+        ),
+    ]

+ 20 - 0
file/migrations/0002_alter_file_folder.py

@@ -0,0 +1,20 @@
+# Generated by Django 3.2.5 on 2021-09-09 12:18
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('folder', '0004_alter_folder_father_folder'),
+        ('file', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='file',
+            name='folder',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='children_files', to='folder.folder'),
+        ),
+    ]

+ 25 - 0
file/migrations/0003_auto_20210909_2239.py

@@ -0,0 +1,25 @@
+# Generated by Django 3.2.5 on 2021-09-09 14:39
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('account', '0001_initial'),
+        ('file', '0002_alter_file_folder'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='file',
+            name='file_path',
+        ),
+        migrations.AddField(
+            model_name='file',
+            name='owner',
+            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.DO_NOTHING, related_name='files', to='account.user'),
+            preserve_default=False,
+        ),
+    ]

+ 20 - 0
file/migrations/0004_file_group.py

@@ -0,0 +1,20 @@
+# Generated by Django 3.2.5 on 2021-09-09 16:10
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('group', '0003_auto_20210910_0010'),
+        ('file', '0003_auto_20210909_2239'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='file',
+            name='group',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='files', to='group.group'),
+        ),
+    ]

+ 18 - 0
file/migrations/0005_rename_folder_file_father_folder.py

@@ -0,0 +1,18 @@
+# Generated by Django 3.2.7 on 2021-09-10 08:15
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('file', '0004_file_group'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='file',
+            old_name='folder',
+            new_name='father_folder',
+        ),
+    ]

+ 38 - 0
file/migrations/0006_auto_20210911_1439.py

@@ -0,0 +1,38 @@
+# Generated by Django 3.2.7 on 2021-09-11 06:39
+
+from django.db import migrations, models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('file', '0005_rename_folder_file_father_folder'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='file',
+            name='file_size',
+        ),
+        migrations.RemoveField(
+            model_name='file',
+            name='update_time',
+        ),
+        migrations.AddField(
+            model_name='file',
+            name='key',
+            field=models.CharField(default=123456, max_length=1024),
+            preserve_default=False,
+        ),
+        migrations.AddField(
+            model_name='file',
+            name='upload_time',
+            field=models.DateTimeField(auto_now=True),
+        ),
+        migrations.AlterField(
+            model_name='file',
+            name='file_id',
+            field=models.UUIDField(auto_created=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
+        ),
+    ]

+ 18 - 0
file/migrations/0007_alter_file_key.py

@@ -0,0 +1,18 @@
+# Generated by Django 3.2.7 on 2021-09-11 08:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('file', '0006_auto_20210911_1439'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='file',
+            name='key',
+            field=models.CharField(blank=True, max_length=1024, null=True),
+        ),
+    ]

+ 0 - 0
file/migrations/__init__.py


BIN
file/migrations/__pycache__/0001_initial.cpython-38.pyc


BIN
file/migrations/__pycache__/0002_alter_file_folder.cpython-38.pyc


BIN
file/migrations/__pycache__/0003_auto_20210909_2239.cpython-38.pyc


BIN
file/migrations/__pycache__/0004_file_group.cpython-38.pyc


BIN
file/migrations/__pycache__/0005_rename_folder_file_father_folder.cpython-38.pyc


BIN
file/migrations/__pycache__/0006_auto_20210911_1439.cpython-38.pyc


BIN
file/migrations/__pycache__/0007_alter_file_key.cpython-38.pyc


BIN
file/migrations/__pycache__/__init__.cpython-38.pyc


+ 58 - 0
file/models.py

@@ -0,0 +1,58 @@
+import uuid
+
+from django.db import models
+from folder.models import Folder
+from account.models import User
+from group.models import Group
+from django.conf import settings
+# 引入内置信号
+from django.db.models.signals import post_delete
+# 引入信号接收器的装饰器
+from django.dispatch import receiver
+
+import os
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# 文件表
+class File(models.Model):
+    # 文件id
+    file_id = models.UUIDField(primary_key=True, default=uuid.uuid4, auto_created=True, editable=False)
+    # 文件名
+    file_name = models.CharField(max_length=50, blank=False)
+    # 从属的文件夹
+    father_folder = models.ForeignKey(Folder, on_delete=models.CASCADE, related_name='children_files')
+    # 更新时间
+    update_time = models.DateTimeField(auto_now=True, editable=False)
+    # 文件类型
+    file_type = models.CharField(max_length=32)
+    # 文件大小
+    # file_size = models.CharField(max_length=16)
+
+    key = models.CharField(max_length=1024, null=True, blank=True)
+
+    owner = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name='files')
+
+    group = models.ForeignKey(Group, on_delete=models.DO_NOTHING, related_name='files', null=True)
+
+    def get_path(self):
+        return settings.MEDIA_ROOT / f'{self.file_name}_{self.file_id}'
+
+    def to_json(self):
+        return {'file_id': self.file_id,
+                'file_name': self.file_name,
+                'father_folder_id': self.father_folder_id,
+                'update_time': self.update_time,
+                'file_type': self.file_type,
+                # 'file_size': self.file_size,
+                'key': self.key,
+                'file': 'file'}
+
+
+# 信号接收函数,每当删除file时自动删除文件
+@receiver(post_delete, sender=File)
+def delete_file(sender, instance, **kwargs):
+    try:
+        os.remove(instance.get_path())
+    except:
+        print('找不到文件')

+ 3 - 0
file/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 9 - 0
file/urls.py

@@ -0,0 +1,9 @@
+from django.shortcuts import render
+from django.urls import path
+from . import views
+# Create your views here.
+urlpatterns = [
+    path('upload_file/', views.upload_file, name='upload_file'),
+    path('download_file/', views.download_file, name='download_file'),
+    path('delete_file/', views.delete_file, name='delete_file'),
+]

+ 128 - 0
file/views.py

@@ -0,0 +1,128 @@
+from account.decorators import login_required
+from file.models import File
+
+from django.http import FileResponse
+from django.utils.http import urlquote
+from folder.models import Folder
+from .judgement_function import judge_filepath
+from account.models import get_user
+from utils.debug import debug_view
+from utils.http import make_json_response
+from utils.permission import can_delete
+from utils.crypto import secure_transport
+from utils.crypto import get_file_encrypt_cipher, get_padding
+import base64
+
+# Create your views here.
+
+
+@secure_transport
+@debug_view(template_name='upload_file.html')
+@login_required
+def upload_file(request):
+    data = request.POST
+    user = get_user(request)
+    key = data.get('key', '')
+    if key:
+        file_b64 = data.get('file_b64')
+        if not file_b64:
+            return make_json_response(code=400, error='文件不存在')
+        file_name = data.get('file_name')
+    else:
+        try:
+            file_obj = request.FILES.get('file')
+        except:
+            return make_json_response(code=400, error='文件不存在')
+        file_name = file_obj.name
+    file_type = judge_filepath(file_name.split('.')[-1].lower()) if '.' in file_name else ''
+    father_folder_id = data.get('father_folder_id')
+    try:
+        folder = Folder.objects.get(folder_id=father_folder_id)
+    except:
+        return make_json_response(code=402, error='文件夹不存在')
+    if not folder.check_permission(user=user):
+        return make_json_response(code=404, error='没有上传文件的权限')
+    file = File.objects.create(file_name=file_name,
+                               father_folder=folder,
+                               file_type=file_type,
+                               owner=user,
+                               group=folder.group,
+                               key=key)
+    try:
+        file_path = file.get_path()
+        with open(file_path, 'wb+') as f:
+            if key:
+                print(key)
+                file.file_type, content_b64 = file_b64.split(',')
+                file.save()
+                file_bytes = base64.b64decode(content_b64)
+                file_bytes += get_padding(file_bytes)
+                enc_file_bytes = get_file_encrypt_cipher(key).encrypt(file_bytes)
+                f.write(enc_file_bytes)
+            else:
+                for chunk in file_obj.chunks():
+                    f.write(chunk)
+    except Exception as e:
+        print(e)
+        file.delete()
+        return make_json_response(code=500, error='文件保存失败')
+
+    return make_json_response()
+
+
+@secure_transport
+# @debug_view('file_id')
+@login_required
+def download_file(request):
+    user = get_user(request)
+    file_id = request.POST.get('file_id')
+    try:
+        file = File.objects.get(file_id=file_id)
+    except:
+        return make_json_response(code=402, error='文件不存在')
+    if not file.father_folder.check_permission(user=user):
+        return make_json_response(code=404, error='没有下载文件的权限')
+    try:
+        file_path = file.get_path()
+        f = open(file_path, 'rb')
+    except:
+        return make_json_response(code=500, error='文件读取失败')
+    if file.key:
+        try:
+            enc_file_bytes = f.read()
+            file_bytes = get_file_encrypt_cipher(file.key).decrypt(enc_file_bytes)
+            content_b64 = base64.b64encode(file_bytes).decode()
+            file_b64 = f'{file.file_type},{content_b64}'
+            f.close()
+        except Exception as e:
+            print(e)
+            return make_json_response(code=500, error='文件读取失败')
+        return make_json_response(file_b64=file_b64, **file.to_json())
+    else:
+        file_bytes = f.read()
+        content_b64 = base64.b64encode(file_bytes).decode()
+        file_b64 = f'{file.file_type},{content_b64}'
+        f.close()
+        return make_json_response(file_b64=file_b64, **file.to_json())
+        # file_name = file.file_name
+        # response = FileResponse(f)
+        # response['Content-Type'] = 'application/octet-stream'
+        # response['Content-Disposition'] = 'attachment;filename={}'.format(urlquote(file_name))
+        # return response
+
+
+@secure_transport
+# @debug_view('file_id')
+@login_required
+def delete_file(request):
+    data = request.POST
+    user = get_user(request)
+    file_id = data.get('file_id')
+    try:
+        file = File.objects.get(file_id=file_id)
+    except:
+        return make_json_response(code=402, error='文件不存在')
+    if not can_delete(user=user, f=file):
+        return make_json_response(code=404, error='没有删除文件的权限')
+    file.delete()
+    return make_json_response()

+ 0 - 0
folder/__init__.py


BIN
folder/__pycache__/__init__.cpython-38.pyc


BIN
folder/__pycache__/admin.cpython-38.pyc


BIN
folder/__pycache__/apps.cpython-38.pyc


BIN
folder/__pycache__/models.cpython-38.pyc


BIN
folder/__pycache__/urls.cpython-38.pyc


BIN
folder/__pycache__/views.cpython-38.pyc


+ 10 - 0
folder/admin.py

@@ -0,0 +1,10 @@
+from django.contrib import admin
+from .models import Folder
+
+
+# Register your models here.
+class FolderAdmin(admin.ModelAdmin):
+    list_display = ["folder_id", "folder_name", "father_folder", "owner", "group"]
+
+
+admin.site.register(Folder, FolderAdmin)

+ 6 - 0
folder/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class FolderConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'folder'

+ 50 - 0
folder/interface.md

@@ -0,0 +1,50 @@
+# 请求根目录id与名称
+******
+## 请求(POST)
+请求链接:folder/get_first_folder
+
+无参数
+## 返回值(http状态码+(列表))
+200+列表:成功+根目录名称与id列表
+
+400:请求不合法
+
+# 请求根目录下所有文件
+******
+## 请求(POST)
+请求链接:folder/folder_list
+
+folder_id(群号)
+## 返回值(http状态码+(列表))
+200:返回目录下所有文件与id
+
+400:请求不合法
+
+# 请求新建文件夹
+******
+## 请求(POST)
+请求链接:folder/add_folder/
+
+father_folder_id(父节点id)
+folder_name(节点名称)
+## 返回值(http状态码+列表)
+200:创建成功
+
+422:此文件夹不存在
+
+400:请求不合法
+
+# 删除文件夹
+******
+## 请求(POST)
+请求链接:folder/delete_folder
+
+folder_id(文件夹id)
+## 返回值(http状态码)
+200:成功
+
+400:请求不合法
+
+421:无此文件夹
+
+

+ 23 - 0
folder/migrations/0001_initial.py

@@ -0,0 +1,23 @@
+# Generated by Django 3.2.7 on 2021-09-08 15:01
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Folder',
+            fields=[
+                ('folder_id', models.AutoField(primary_key=True, serialize=False)),
+                ('folder_name', models.CharField(max_length=50)),
+                ('father_folder', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='the_father_folder', to='folder.folder')),
+            ],
+        ),
+    ]

+ 18 - 0
folder/migrations/0002_alter_folder_folder_name.py

@@ -0,0 +1,18 @@
+# Generated by Django 3.2.5 on 2021-09-09 07:30
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('folder', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='folder',
+            name='folder_name',
+            field=models.CharField(default='root', max_length=50),
+        ),
+    ]

+ 19 - 0
folder/migrations/0003_alter_folder_father_folder.py

@@ -0,0 +1,19 @@
+# Generated by Django 3.2.5 on 2021-09-09 12:16
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('folder', '0002_alter_folder_folder_name'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='folder',
+            name='father_folder',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children_folder', to='folder.folder'),
+        ),
+    ]

+ 19 - 0
folder/migrations/0004_alter_folder_father_folder.py

@@ -0,0 +1,19 @@
+# Generated by Django 3.2.5 on 2021-09-09 12:18
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('folder', '0003_alter_folder_father_folder'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='folder',
+            name='father_folder',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children_folders', to='folder.folder'),
+        ),
+    ]

+ 21 - 0
folder/migrations/0005_folder_owner.py

@@ -0,0 +1,21 @@
+# Generated by Django 3.2.5 on 2021-09-09 12:38
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('account', '0001_initial'),
+        ('folder', '0004_alter_folder_father_folder'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='folder',
+            name='owner',
+            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='folders', to='account.user'),
+            preserve_default=False,
+        ),
+    ]

+ 20 - 0
folder/migrations/0006_folder_group.py

@@ -0,0 +1,20 @@
+# Generated by Django 3.2.5 on 2021-09-09 16:10
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('group', '0003_auto_20210910_0010'),
+        ('folder', '0005_folder_owner'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='folder',
+            name='group',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='folders', to='group.group'),
+        ),
+    ]

+ 25 - 0
folder/migrations/0007_auto_20210910_1615.py

@@ -0,0 +1,25 @@
+# Generated by Django 3.2.7 on 2021-09-10 08:15
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('account', '0002_delete_profile'),
+        ('folder', '0006_folder_group'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='folder',
+            name='father_folder',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children_folders', to='folder.folder'),
+        ),
+        migrations.AlterField(
+            model_name='folder',
+            name='owner',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='folders', to='account.user'),
+        ),
+    ]

+ 0 - 0
folder/migrations/__init__.py


BIN
folder/migrations/__pycache__/0001_initial.cpython-38.pyc


BIN
folder/migrations/__pycache__/0002_alter_folder_folder_name.cpython-38.pyc


BIN
folder/migrations/__pycache__/0003_alter_folder_father_folder.cpython-38.pyc


BIN
folder/migrations/__pycache__/0004_alter_folder_father_folder.cpython-38.pyc


BIN
folder/migrations/__pycache__/0005_folder_owner.cpython-38.pyc


Некоторые файлы не были показаны из-за большого количества измененных файлов