zoukankan      html  css  js  c++  java
  • Django项目:堡垒机(Linux服务器主机管理系统)--02--02堡垒机设计后台交互程序

     

     1 #main.py
     2 #本文件写所有的连接交互动作程序
     3 #————————————————02堡垒机设计后台交互程序 开始————————————————
     4 from django.contrib.auth import authenticate #验证用户的证书
     5 import getpass#输入密码时不显示密码
     6 #用户登陆堡垒机后的交互程序
     7 class HostManager(object):
     8     def __init__(self):
     9         self.user = None #当前账号
    10 
    11     def interactive(self):#交互脚本
    12         # ————————————————登录堡垒机 开始————————————————
    13         print("----开始 运行交互脚本----")
    14         count = 0 #用户名密码试错的次数
    15         while count < 6:#用户名密码试错的次数
    16             username = input("请输入你的用户名:").strip()#用户名
    17             password = getpass.getpass("请输入你的密码:").strip()#密码
    18             user = authenticate(username=username,password=password)#验证用户名密码
    19             if user: #验证成功
    20                 print("欢迎 %s 登陆使用!".center(50,'-') % user.name )
    21                 self.user = user #验证成功,赋值
    22                 break #退出无限循环
    23             else:
    24                 print("用户名或密码错误!")
    25             count += 1#用户名密码试错的次数
    26         else:
    27             print("----结束 运行交互脚本----")
    28             exit("用户名密码试错的次数到了,再见。")
    29         """
    30         #————从外部调用django的方法 开始————
    31         #注意写,从外部调用django的方法 E:堡垒机eyefortess_manage.py
    32         #注意,到admin后台配置主机给账户
    33         # ————————————
    34         #运行 Terminal
    35         #E:堡垒机eye>python fortess_manage.py
    36         #Username:admin@qq.com
    37         #Password:admin123456
    38         #————从外部调用django的方法 结束————
    39         """
    40         # ————————————————登录堡垒机 结束————————————————
    41 #————————————————02堡垒机设计后台交互程序 结束————————————————
    #main.py

     1 #fortess_manage.py
     2 #————————————————02堡垒机设计后台交互程序 开始————————————————
     3 #————从外部调用django的方法 开始————
     4 import os #操作系统
     5 if __name__ == "__main__":
     6     os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eye.settings')#找路径,环境变量
     7     import django #导入django
     8     django.setup()#安装django
     9     from Fortress.backend import main #从backend导入main.py
    10     obj = main.HostManager() #实例化类#main.py的类
    11     obj.interactive() #调用类的函数 #交互脚本
    12 #————从外部调用django的方法 结束————
    13 #————————————————02堡垒机设计后台交互程序 结束————————————————
    #fortess_manage.py


      1 #models.py
      2 #————————————————01堡垒机重写DJANGO账户表 开始————————————————
      3 from django.db import models  #模型
      4 from django.contrib.auth.models import BaseUserManager,AbstractBaseUser,PermissionsMixin#DJANGO账户继承重写
      5 #重写DJAGO账号表
      6 class UserProfileManager(BaseUserManager):
      7     def create_user(self, email, name, password=None):#创建并保存一个普通用户给定的邮箱地址,密码。
      8         if not email:
      9             raise ValueError('用户必须有一个电子邮箱地址')
     10         user = self.model(email=self.normalize_email(email),#验证邮箱格式
     11                           name=name,)#创建一条普通用户记录
     12         user.set_password(password)#对密码加密
     13         user.save(using=self._db)#从临时文件读取出来数据,进行保存。
     14         return user
     15     def create_superuser(self, email,name,password):#创建并保存一个超级用户给定的邮箱地址,密码。
     16         user = self.create_user(email,name=name,password=password)#创建一条超级用户记录
     17         user.is_admin = True#管理员
     18         user.save(using=self._db)#从临时文件读取出来数据,进行保存。
     19         return user
     20 
     21 #重写DJAGO账号表
     22 class UserProfile(AbstractBaseUser,PermissionsMixin):
     23     # ————#DJANGO账号必须要有的字段 开始————
     24     email = models.EmailField( max_length=255,unique=True,verbose_name='邮箱地址')
     25     name = models.CharField(max_length=32,verbose_name='昵称')
     26     is_active = models.BooleanField(default=True,verbose_name='是否启用')
     27     is_admin = models.BooleanField(default=False,verbose_name='管理员',help_text='指定用户是否可以登录管理网站并且拥有全部权限',)
     28     is_staff = models.BooleanField(default=True,verbose_name='员工状态',help_text='指定用户是否可以登录管理网站',)
     29     # ————#DJANGO账号必须要有的字段 结束————
     30 
     31     # ————————————————02堡垒机设计后台交互程序 开始————————————————
     32     bind_hosts = models.ManyToManyField('BindHost',blank=True,verbose_name="关联的主机")
     33     host_groups = models.ManyToManyField('HostGroups',blank=True,verbose_name="关联的分组")
     34     # ————————————————02堡垒机设计后台交互程序 结束————————————————
     35 
     36     #————#创建用户记录 开始————
     37     objects = UserProfileManager()#创建用户记录,固定的变量名(objects)
     38     USERNAME_FIELD = 'email' #用于登录的字段
     39     REQUIRED_FIELDS = ['name']#必须填写
     40     # ————#创建用户记录 结束————
     41     # ————#DJANGO账号必须要有的方法 开始————
     42     def get_full_name(self):#用户确认的电子邮件地址
     43         return self.email
     44     def get_short_name(self):#用户确认的电子邮件地址
     45         return self.email
     46     # ————#DJANGO账号必须要有的方法 结束————
     47     def __str__(self):# __unicode__ on Python 2
     48         return self.email
     49     # ————#DJANGO账号权限 开始————
     50     def has_perm(self, perm, obj=None):#权限#用户有一个特定的许可吗?
     51         return True
     52     def has_module_perms(self, app_label):#用户有权限查看应用名称吗?
     53         return True
     54     # ————#DJANGO账号权限 结束————
     55     class Meta:
     56         verbose_name_plural='00账户'
     57 # ————自定制admin 开始————
     58 #到admin.py里给DJANGO账户注册显示 #E:堡垒机eyeFortressadmin.py
     59 # ————自定制admin 结束————
     60 # ————注册到配置文件 开始————
     61 #到配置文件里给DJANGO账户指定模型 #E:堡垒机eyeeyesettings.py
     62 #AUTH_USER_MODEL = 'web.UserProfile'
     63 # ————注册到配置文件 结束————
     64 #————————————————01堡垒机重写DJANGO账户表 结束————————————————
     65 #————————————————02堡垒机设计后台交互程序 开始————————————————
     66 #01标签
     67 class Tag(models.Model):
     68     name =  models.CharField(max_length=64,unique=True) #标签名#CharField定长文本#最长度=64字节#不可以重复
     69     class Meta:#通过一个内嵌类 "class Meta" 给你的 model 定义元数据
     70         verbose_name_plural =  "01标签" #verbose_name_plural给你的模型类起一个更可读的名字
     71     def __str__(self):  # __str__()是Python的一个“魔幻”方法,这个方法定义了当object调用str()时应该返回的值。
     72         return self.name  # 返回 #标签名
     73 #02机房
     74 class IDC(models.Model):
     75     name = models.CharField(max_length=64, unique=True,verbose_name="机房名")
     76     address=models.CharField(max_length=256,blank=True,null=True,verbose_name="机房地址")
     77     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="机房的备注")
     78     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
     79     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
     80     class Meta:
     81         verbose_name_plural='02机房'
     82     def __str__(self):
     83         return self.name
     84 #03主机
     85 class Host(models.Model):
     86     hostname = models.CharField(max_length=64,unique=True,verbose_name="主机名")
     87     ip_addr = models.GenericIPAddressField(verbose_name="主机IP地址")
     88     port = models.SmallIntegerField(default=22,verbose_name="主机端口")
     89     idc = models.ForeignKey('IDC',blank=True,null=True,on_delete=models.CASCADE,verbose_name="主机所在的机房")
     90     system_type_choices = ((0,'Linux'),(1,'Windows'))
     91     system_type = models.SmallIntegerField(choices=system_type_choices,default=0,verbose_name="操作系统类型")
     92     enabled = models.BooleanField(default=1,verbose_name="是否启用本机")
     93     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="主机的备注")
     94     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
     95     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
     96     class Meta:
     97         verbose_name_plural='03主机'
     98         unique_together = ('ip_addr','port')
     99     def __str__(self):
    100         return "%s(%s)" % (self.hostname, self.ip_addr)
    101 #04账号密码
    102 class RemoteUser(models.Model):
    103     auth_type_choices =((0,'ssh-password'), (1,'ssh-key'))
    104     auth_type = models.SmallIntegerField(choices=auth_type_choices,default=0,verbose_name="连接方式,密码或者密钥")
    105     userlevel=models.CharField(max_length=128,blank=True,null=True,verbose_name="用户等级")
    106     username = models.CharField(max_length=128,verbose_name="用户名")
    107     password = models.CharField(max_length=256,verbose_name="密码或者密钥",help_text="如果连接方式选择为ssh-key,那此处就应该是key的路径")
    108     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="账号密码的备注")
    109     host_prompt=models.ManyToManyField("Host",blank=True,verbose_name="可关联的主机",help_text="关联主机与账号密码提示")
    110     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
    111     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    112     class Meta:
    113         verbose_name_plural='04账号密码'
    114         unique_together = ('auth_type', 'username', 'password')  # 联合唯一
    115     def __str__(self):
    116         return "%s:%s:%s" %(self.id,self.username,self.memo)
    117 #05关联主机与账号密码
    118 class BindHost(models.Model):
    119     host = models.ForeignKey('Host',on_delete=models.CASCADE,verbose_name="远程主机名")
    120     remote_user = models.ForeignKey('RemoteUser',on_delete=models.CASCADE,verbose_name="远程用户名")
    121     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="关联的备注")
    122     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
    123     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    124     class Meta:
    125         verbose_name_plural = '05关联主机与账号密码'
    126         unique_together = ('host', 'remote_user')#联合唯一
    127     def __str__(self):
    128         return "<%s:%s:%s>" %(self.host.id,self.host.hostname,self.remote_user.username)
    129 #06主机分组
    130 class HostGroups(models.Model):
    131     name  = models.CharField(max_length=64,unique=True,verbose_name="分组名称")
    132     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="主机分组的备注")
    133     bind_hosts = models.ManyToManyField('BindHost',blank=True,verbose_name="远程主机")
    134     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
    135     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    136     class Meta:
    137         verbose_name_plural='06主机分组'
    138     def __str__(self):
    139         return self.name
    140 #————————————————02堡垒机设计后台交互程序 结束————————————————
    141 
    142 """
    143 # ————————————————Django中使用中国时区和中文 开始————————————————
    144 到settings.py里修改#E:堡垒机eyeeyesettings.py
    145 LANGUAGE_CODE = 'zh-Hans'
    146 TIME_ZONE = 'Asia/Shanghai'
    147 如果需要全局配置默认时区和语言,可在django下的conf模块下的global_settings.py中进行配置
    148 所有可选的语言选项可通过python输入以下代码查看:
    149 from django.conf.locale import LANG_INFO
    150 {key: LANG_INFO[key]['name_local'] for key in LANG_INFO if 'name_local' in LANG_INFO[key]}
    151 # ————————时间扩展 开始———————— 
    152 USE_TZ = True
    153 当设置了 USE_TZ 为 True 时,Django 与其他系统或服务的交流将强制使用 UTC 时间。
    154 1.保证存储到数据库中的是 UTC 时间;
    155 2.在函数之间传递时间参数时,确保时间已经转换成 UTC 时间;
    156 # ——Django中跨时区 开始——
    157 一般不跨时区的应用,可以不使用时区,即在settings.py设置
    158 USE_TZ = False
    159 这样存储的时间就是无时区的时间。
    160 # ——Django中跨时区 结束——
    161 # ————————时间扩展 结束————————
    162 # ————————————————Django中使用中国时区和中文 结束————————————————
    163 # ————————————————Django数据库生成 开始————————————————
    164 运行 Terminal
    165 生成 数据表
    166 E:堡垒机eye>python manage.py makemigrations
    167 数据表 迁移
    168 E:堡垒机eye>python manage.py migrate
    169 # ————————————————Django数据库生成 结束————————————————
    170 # ————————————————重写Django账号表后的创建方法 开始————————————————
    171 E:堡垒机eye>python manage.py createsuperuser
    172 邮箱地址: admin@qq.com
    173 昵称: admin
    174 Password:           admin123456
    175 Password (again):   admin123456    
    176 Superuser created successfully.
    177 # ————————————————重写Django账号表后的创建方法 结束————————————————
    178 #启动 DJANGO 服务
    179 E:堡垒机eye>python manage.py runserver 127.0.0.1:8000
    180 """
    #models.py


      1 #admin.py
      2 #————————————————01堡垒机重写DJANGO账户表 开始————————————————
      3 #————————同目录下可以用 . 开始————————
      4 #from Fortress import models#数据库 #重写DJAGO账号表
      5 from . import models#数据库 #重写DJAGO账号表
      6 #————————同目录下可以用 . 结束————————
      7 from django import forms #django的字段验证
      8 from django.contrib.auth.admin import UserAdmin as BaseUserAdmin ##表单添加和更改用户实例
      9 from django.contrib.auth.forms import ReadOnlyPasswordHashField#只读密码散列字段
     10 from django.contrib import admin #注册类到admin
     11 #创建新用户
     12 class UserCreationForm(forms.ModelForm):
     13     password1 = forms.CharField(label='设置密码', widget=forms.PasswordInput)#密码
     14     password2 = forms.CharField(label='确认密码', widget=forms.PasswordInput)#确认密码
     15     class Meta:
     16         model = models.UserProfile
     17         fields = ['email', 'name']
     18     def clean_password2(self): #检查两个密码条目是否一样(匹配)
     19         password1 = self.cleaned_data.get("password1")
     20         password2 = self.cleaned_data.get("password2")
     21         if password1 and password2 and password1 != password2:
     22             raise forms.ValidationError("密码不匹配")
     23         return password2
     24     def save(self, commit=True): #保存密码(散列的格式提供)
     25         user = super(UserCreationForm, self).save(commit=False)
     26         user.set_password(self.cleaned_data["password1"])
     27         if commit:
     28             user.save()#保存
     29         return user
     30 # 修改用户密码
     31 class UserChangeForm(forms.ModelForm):
     32     password = ReadOnlyPasswordHashField(label="修改密码",
     33         help_text=("原始密码不存储,所以没有办法看到"
     34                     "这个用户的密码,但是你可以改变密码 "
     35                     "使用 <a href="../password/">修改密码</a>."))#哈值#只读密码散列字段
     36     class Meta:
     37         model = models.UserProfile
     38         fields = ['email', 'password', 'name', 'is_active', 'is_admin']
     39     def clean_password(self):#不管用户提供什么,返回初始值。
     40         return self.initial["password"]
     41 
     42 #表单添加和更改用户实例
     43 class UserProfileAdmin(BaseUserAdmin):
     44     form = UserChangeForm  #修改
     45     add_form = UserCreationForm#创建
     46     # 字段用于显示用户模型。
     47     # 这些覆盖定义UserAdmin固定在底座上
     48     # auth.User参考特定字段。
     49     list_display = ['email', 'name','is_staff', 'is_admin']#后台显示的字段
     50     list_filter = ['is_admin','is_staff']#后台过滤的字段
     51     fieldsets = (('账号密码', {'fields': ('email', 'password')}),
     52                  ('个人信息', {'fields': ('name',)}),
     53                  # ————————————————02堡垒机设计后台交互程序 开始————————————————
     54                  ('堡垒机主机授权', {'fields': ('bind_hosts', 'host_groups')}),
     55                  # ————————————————02堡垒机设计后台交互程序 结束————————————————
     56                  ('权限', {'fields': ('is_admin','is_staff','user_permissions','groups')}),)
     57     #添加账户时显示要填写的字段
     58     add_fieldsets = (('添加账户', {'classes': ('wide',),#wide(宽屏样式)
     59             'fields': ('email', 'name', 'password1', 'password2')}),)
     60     search_fields = ['email']#后台搜索的字段
     61     ordering = ['email']#后台排序的字段
     62     filter_horizontal =['user_permissions','groups',#必须覆盖的变量#后台复选框的字段
     63                         # ————————————————02堡垒机设计后台交互程序 开始————————————————
     64                         'bind_hosts', 'host_groups',
     65                         # ————————————————02堡垒机设计后台交互程序 结束————————————————
     66                         ]
     67 admin.site.register(models.UserProfile, UserProfileAdmin)#现在注册这个新UserProfileAdmin
     68 # ——可选——使用Django的内置权限 开始————
     69 #from django.contrib.auth.models import Group #权限组
     70 #admin.site.unregister(Group)#权限组#因为我们不使用Django的内置权限,
     71 # ——可选——使用Django的内置权限 结束————
     72 #————————————————01堡垒机重写DJANGO账户表 结束————————————————
     73 
     74 
     75 #————————————————02堡垒机设计后台交互程序 开始————————————————
     76 #01标签
     77 class TagAdmin(admin.ModelAdmin):
     78     list_display = ['id','name'] #显示字段表头
     79     list_editable = ['name']  #可编辑
     80     search_fields = ['name'] #搜索
     81 admin.site.register(models.Tag, TagAdmin)
     82 #02机房
     83 class IDCAdmin(admin.ModelAdmin):
     84     list_display = ['id','name','address','memo','date'] #显示字段表头
     85     list_editable = ['name','address', 'memo']  #可编辑
     86     search_fields = ['name','tag'] #搜索
     87     filter_horizontal = ['tag'] #复选框
     88 admin.site.register(models.IDC, IDCAdmin)
     89 #03主机
     90 class HostAdmin(admin.ModelAdmin):
     91     list_display = ['id','hostname','ip_addr','port','memo','enabled','date'] #显示字段表头
     92     list_editable = ['hostname','ip_addr','port','memo','enabled']  #可编辑
     93     search_fields = ['hostname','ip_addr','port','tag'] #搜索
     94     filter_horizontal = ['tag'] #复选框
     95 admin.site.register(models.Host, HostAdmin)
     96 #04账号密码
     97 class RemoteUserAdmin(admin.ModelAdmin):
     98     list_display = ['id','userlevel','username','memo','date'] #显示字段表头
     99     list_editable = ['memo']  #可编辑
    100     search_fields = ['userlevel','username','tag'] #搜索
    101     filter_horizontal = ['host_prompt','tag'] #复选框
    102 admin.site.register(models.RemoteUser,RemoteUserAdmin)
    103 #05关联主机与账号密码
    104 class BindHostAdmin(admin.ModelAdmin):
    105     list_display = ['id','host','remote_user','memo','date']#显示字段表头
    106     list_editable = ['host','remote_user','memo']  #可编辑
    107     search_fields = ['host','remote_user','tag'] #搜索
    108     filter_horizontal = ['tag'] #复选框
    109     ordering = ['host','remote_user'] #自定义排序
    110 admin.site.register(models.BindHost,BindHostAdmin)
    111 #06主机分组
    112 class HostGroupsAdmin(admin.ModelAdmin):
    113     list_display = ['id','name','memo','date']#显示字段表头
    114     list_editable = ['name','memo']  #可编辑
    115     search_fields = ['name','tag'] #搜索
    116     filter_horizontal = ['bind_hosts','tag'] #复选框
    117 admin.site.register(models.HostGroups, HostGroupsAdmin)
    118 #————————————————02堡垒机设计后台交互程序 结束————————————————
    #admin.py


     1 #main.py
     2 #本文件写所有的连接交互动作程序
     3 #————————————————02堡垒机设计后台交互程序 开始————————————————
     4 from django.contrib.auth import authenticate #验证用户的证书
     5 #用户登陆堡垒机后的交互程序
     6 class HostManager(object):
     7     def __init__(self):
     8         self.user = None #当前账号
     9     def interactive(self):#交互脚本
    10         # ————————————————登录堡垒机 开始————————————————
    11         print("----开始 运行交互脚本----")
    12         count = 0 #用户名密码试错的次数
    13         while count < 6:#用户名密码试错的次数
    14             username = input("Username:").strip()#用户名
    15             password = input("Password:").strip()#密码
    16             user = authenticate(username=username,password=password)#验证用户名密码
    17             if user: #验证成功
    18                 print("欢迎 %s 登陆使用!".center(50,'-') % user.name )
    19                 self.user = user #验证成功,赋值
    20                 break #退出无限循环
    21             else:
    22                 print("用户名或密码错误!")
    23             count += 1#用户名密码试错的次数
    24         else:
    25             print("----结束 运行交互脚本----")
    26             exit("用户名密码试错的次数到了,再见。")
    27         """
    28         #————从外部调用django的方法 开始————
    29         #注意写,从外部调用django的方法 E:堡垒机eyefortess_manage.py
    30         #注意,到admin后台配置主机给账户
    31         # ————————————
    32         #运行 Terminal
    33         #E:堡垒机eye>python fortess_manage.py
    34         #Username:admin@qq.com
    35         #Password:admin123456
    36         #————从外部调用django的方法 结束————
    37         """
    38         # ————————————————登录堡垒机 结束————————————————
    39         # ——————————————选择要连接的主机 开始————————————
    40         if self.user: #验证登录成功
    41             while True:
    42                 #————————打印当前账户所有的可以登录主机 开始——————
    43                 for index,host_group  in enumerate(self.user.host_groups.all()): #select_related()
    44                     print("%s.	%s[%s]" %(index,host_group.name, host_group.bind_hosts.count()))
    45                 print("z.	未分组主机[%s]" %(self.user.bind_hosts.count()))
    46                 #————————打印当前账户所有的可以登录主机 结束——————
    47                 # ————————开始选择分组 开始——————
    48                 choice = input("%s>>:"% self.user).strip()#输入选择
    49                 if len(choice) == 0:#长度等于0
    50                     continue#跳过这次循环
    51                 selected_host_group = None #选择分组
    52                 if choice.isdigit():#检测字符串是否只由数字组成
    53                     choice = int(choice)#转成整数
    54                     if choice >=0 and choice <= index: #判断输入的选项是否合法
    55                         selected_host_group = self.user.host_groups.all()[choice]#迭代选择分组
    56                 elif choice == 'z':#未分组
    57                     selected_host_group = self.user
    58                 # ————————开始选择分组 结束——————
    59                 # ————————开始选择主机 开始——————
    60                 if selected_host_group: #选择分组后选择主机
    61                     while True:
    62                         print("当前选择的分组: %s ".center(50,'-') %selected_host_group)
    63                         # ————————打印分组里的所有主机 开始——————
    64                         for index, bind_host in enumerate(selected_host_group.bind_hosts.all()):
    65                             print("%s.	%s" % (index, bind_host))
    66                         print("返回上级菜单,请按  b  ")
    67                         # ————————打印分组里的所有主机 结束——————
    68                         # ————————选择分组里的主机 开始——————
    69                         choice = input("%s>>>:" % self.user).strip()#输入选择
    70                         if choice.isdigit():#检测字符串是否只由数字组成
    71                             choice = int(choice)#转成整数
    72                             if choice >= 0 and choice <= index:#判断输入的选项是否合法
    73                                 bind_host = selected_host_group.bind_hosts.all()[choice]#迭代选择主机
    74                                 print("准备登录 %s 主机".center(50,'-') %bind_host)
    75                         # ————————选择分组里的主机 结束——————
    76                         elif choice == 'b':#退出
    77                             break #终止无限循环
    78                 # ————————开始选择主机 结束——————
    79         # ——————————————选择要连接的主机 结束————————————
    80 #————————————————02堡垒机设计后台交互程序 结束————————————————
    #main.py


  • 相关阅读:
    React Native 架构演进
    React Native 架构一览
    React Native 在 Airbnb 的起起落落
    React Native简史
    图解云服务模型的演进
    伯克利研究员们眼中的Cloud Computing
    彻底理解 IaaS、PaaS、SaaS
    JS更随机的随机数
    JS自动化
    fingerprint2 计算浏览器指纹分析
  • 原文地址:https://www.cnblogs.com/ujq3/p/10108320.html
Copyright © 2011-2022 走看看