Skip to content

Instantly share code, notes, and snippets.

@duangsuse
Last active July 27, 2018 12:16
Show Gist options
  • Save duangsuse/1d488eec74e6d670ccbb67513a072bac to your computer and use it in GitHub Desktop.
Save duangsuse/1d488eec74e6d670ccbb67513a072bac to your computer and use it in GitHub Desktop.
GeekApk 基本规划/API

GeekApk 后端:基本法 | API | 模型

没有……任何 (内定、钦点) 的意思。还是按照香港的……按照 基本法、按照 选举的法——去产生……

GeekApk 是一个曾经 酷友 们自发组织创立的开源应用社区,它崇尚自由,设计时融合了 酷安Google PlayGitHubApkPureApkMirrorF-Droid 的优秀设计于一体,目标是成为一个能替代酷安的应用市场兼社区

关于模型

GeekApk 里有这些模型:

实体:用户、分类、应用、更新、评论

辅助:时间线、通知

GeekApk 还支持 用户 Star 应用用户 Follow 用户

评论和更新 附加在 应用

评论 可以被添加为 评论子评论

详细信息

  • 用户有简易名称(唯一标识)、用户名和 UID、头像链接、自我介绍、创建时间、管理员修改的小黑屋状态
  • 分类有自己不可修改的 TID 和管理员修改的名称,一般以 / 切分分类路径
  • 应用有 AID(内部使用的唯一标识)、包名(也是唯一标识)、管理员修改的创建用户 ID 引用、管理员修改的所属分类 ID 引用、名称、图标链接、应用描述、预览图像、创建时间、更新时间
  • 更新有不可修改的所属应用的 ID 引用、版本名、reversion、安装链接、更新内容、最低适配 SDK 版本、创建时间
  • 评论有 CID、不可修改的创建用户的 ID 引用、不可修改的所属应用的 ID 引用、不可修改可空的被回复评论的 ID 引用、内容、更新时间、被回复数、创建时间
  • 时间线有所属用户引用、创建时间、类型、数据引用
  • 通知有所属用户、创建时间、类型、数据引用、已读状态

“图” 解模型

models 'GeekApk' do
    perm :admin, 'create, delete'

    model 'user', :uid do
        perm :owner

        field :simple_name, 'unique', :string, 'len < 30, match: /([A-Z]|[a-z]|_)*/'
        field :name, :string, 'len < 50'
        field :avatar, :string, 'len < 500'
        field :bio, :string, 'len < 5000'

        perm :admin
        field :blocked, :bool, 'default false'

        perm { all: '!read', owner_shash: 'write' }
        field :hash, :string

        perm { all: '!read', admin: 'write' }
        field :shash, :string

        counter 'followers', 'when new follower', 'when unfollow'

        perm :restricted
        created_field
    end

    perm :admin, 'create, delete, update'
    model 'category', :tid do
        perm :admin

        field :name, :string
    end

    perm { admin: 'delete', user: 'create', owner: 'delete, update' }
    model 'app', :aid do
        perm owner: 'write'
        field :package, 'unique', :string, 'len < 200, match: /([A-Z]|[a-z]|_)*/'

        perm admin: 'update'
        ref 'user', :author
        ref 'category', :category

        field :name, :string, 'len < 500'
        field :icon, :string, 'len < 500'
        field :readme, :string, 'len < 9000'
        field :previews, :string, 'len < 2000'


        counter 'stars', 'when new star', 'when unstar'
        counter 'comments', 'when new comment', 'when delete comment'

        created_field
        updated_field
    end

    model 'update', 'app' do
        ref 'app', :app
        field :version, :string, 'len < 40'
        field :reversion, :smallint, 'ge 0, index'
        field :install, :string, 'len < 2000'
        field :updates, :string, 'len < 600'
        field :minsdk, :smallint, 'gt 0'

        created_field
    end

    model 'comment', :cid, 'app' do
        ref 'user', :author
        ref 'app', :app

        ref? 'comment', :reply
        field :content, :string, 'len < 600'

        created_field
        updated_field

        counter 'replies', 'when replied'
    end

    model 'timeline', 'user' do
        ref 'user', :user
        field :type, :smallint
        ref ref_line_type(:type), :int
        created_field
    end

    model 'notification', 'user' do
        ref 'user', :user
        field :type, :smallint
        ref ref_notif_type(:type), :int
        created_field
        field :readed, :bool, 'default false'
    end
end

关于权限

GeekApk 使用 基于角色 的权限制度

GeekApk 中有三种角色:访客、用户、权限狗(Doge)

任何人,包括 用户权限狗,同时也都是访客

所有人都 无权修改 创建时间和实体 ID,系统生成的 时间线通知(除了 已读 字段)不能修改和删除

权限狗是 GeekApk 的管理员,管理员可以

  • 管理用户,如创建/删除用户、修改用户分发密码、ban/unban 用户
  • 管理分类,如创建修改删除分类
  • 删除实体,如删除应用、删除更新、删除评论
  • 修改信息,如转移应用、移动应用到新分类

用户是 GeekApk 的普通用户,用户可以

  • 管理自我介绍
  • 创建/删除和修改自己的应用,但不能移动应用到新分类
  • 修改自己是协作者的应用,但不能移动应用到新分类
  • 创建/删除和修改自己的/自己是协作者的应用的更新
  • 创建评论、删除/修改自己的评论
  • 读取和标记已读自己的通知
  • (Un)Follow 用户/(Un)Star 应用

作为 GeekApk 访客,我可以

  • 获得除了 密码、通知 外所有的 只读权限 "Read Access"

权限验证实现

GeekApk HTTP API 使用 Cookie 来验证操作权限

  • 权限狗使用 doge cookie,服务器只有一个命名为 DOGE_TOK 的令牌,测试相等性
  • 用户使用 uidhash 两个 cookie,服务器不会从执行的操作推测你的用户 ID,如果没有权限或验证失败,返回 HTTP 代码 Forbidden 和 Unauthorized
  • 用户可以使用 shash(分发密码)URL 参数来重置用户 hash,只有一个 API 能这么做
  • 计算密码的 Hash 值过程由客户端执行,服务器只把密码作为字符串看待
  • GeekApk 允许应用主添加其他用户作为 Collaborator

关于模型操作

Category "Misc"
    api version : Text
    api server_description : Text
    api api_locations
End

Category "Admin"
    api create_user(initiate_simple_name: Body) perm doge return uid, rhash
    api rhash_user(uid: User, new_shash: Body) perm doge
        return new_shash
    api delete_user(uid: User) perm doge
    api ban_user(uid: User, new_state: Bool) perm doge
        return Old_state
    api create_category(name: Body) perm doge
        return cid
    api update_category_name(tid: Category, name: Body) perm doge
        return tid, name
    api delete_category(tid: Category) perm doge
        return tid
    api delete_app(aid: App) perm doge
        return aid
    api change_app_category(aid: App, new_cate: Category) perm doge
        return old_category
    api change_app_ownership(aid: App, new_owner: User) perm doge
        return old_owner
    api delete_app_update(aid: App, rev: SmallInt) perm doge
        return reversion
    api delete_comment(cid: Comment) perm doge
        return num_deleted_comments
End

Category "Category"
    api get_categories
        return categories
End

Category "User"
    api get_user(uid: User)
        return user

    ; simple_name、name、avatar、bio
    api update_user_att(uid: User, attr: String, new_val: Body) perm owner
        allow_banned_user
        check_size
        return attr, new_var

    api update_user_hash(uid: User, shash: String, new_hash: Body) perm everyone
        check new hash size > 4
        return new_hash

    api check_pass(uid: User, hash: String) perm everyone
        return OK or Unauthorized

    api search_user(type: name/simple_name/bio, sort: created/followers, content: Body)
        return Paged<List<User>>

    api list_user(sort: created/followers)
        return Paged List of User
End

Category "Timeline"
    api get_user_timlines(uid: User, only_type: SmallInt, only_ref: Int)
    api get_all_timelines(only_type: SmallInt, only_ref: Int)
    api bulk_get_user_timline(uids: User[], only_type: SmallInt, only_ref: Int)
End

Category "Notification"
    api get_mine_notifications(uid: User) perm owner
    api get_all_mine_notifications(uid: User) perm owner
    api mark_time_range_notifications(uid: User, start: UnixTime, end: UnixTime, stat: Bool) perm owner
End

Category "App"
    api get_app(aid: App)
        return app

    ; package name icon previews readme
    api update_app_att(aid: App, attr: String, new_val: Body) perm owner or collab
        return attr, new_val

    api create_app(package: String, category: Category) perm user
        return aid

    api search_app(in_category: Category?, content: Body, type: name/package/icon/readme, sort: updated/created/comments/stars)

    api aid_realid(package: Text)
        return aid

    api list_app(in_category: Category?, sort: updated/created/comments/stars)

    api delete_app(aid: App) perm owner

    ; Collaborator

    api add_collaborator(uid: User) perm 'owner'
        return add collaborator to list
    end

    api del_collaborator(uid: User) perm 'owner or who uid == param uid'
        return delete collaborator from list
    end

    api query_app_collaborators(app: App)
        body list(app collaborators)
        return
    end

    api query_user_collaborated_apps(uid: User)
        body list(user collaborated apps)
        return
    end
End

Category "Update"
    api get_app_updates(aid: App)
    api bulk_get_new_rev(aids: App[])
    api get_update(aid: App, rev: SmallInt)
    api create_update(aid: App, rev: SmallInt) perm owner or collab
    api update_update_att(aid: App, rev: SmallInt, attr: version/install/updates/minsdk, new_val: Body) perm owner or collab
    api delete_update(aid: App, rev: SmallInt) perm owner or collab
End

Category "Comment"
    api search(in_app: Option<App>, user: Option<User>, replies_to: Option<Comment>, content: Body) paged

    api list_comments(app: App) paged
    api list_subcomments(cid: Comment)
    api create_comment(app: App, uid: User) perm user
    api update_comment(cid: Comment, text: Body) perm owner
    api delete_comment(cid: Comment) perm owner
End

Category "Follow"
    api follow_user(uid: User) perm user
    api unfollow_user(uid: User) perm owner
    api get_followers(uid: User)
    api get followed(uid: User)
End

Category "Star"
    api stat_app(app: App) perm user
    api unstar_app(app: App) perm owner
    api get_stared_users(app: App)
    api get_user_stars(uid: User)
End

关于数据格式

是 Markdown 文本

  • 用户自我介绍
  • 应用描述
  • 更新内容
  • 评论内容

通知和时间线的内部定义

括号内的是 类型 ID,可以在 JSONAPI 参数里使用

通知

  • 用户的评论被回复(1
  • 用户被 @ 提到(2
  • 用户被用户跟随(3
  • 用户被 Ban(4
  • 用户被 Unban(5
  • 用户删除了应用(6
  • 用户被添加为 Collaborator(7

时间线

  • 用户 跟随 了某个用户(1
  • 用户 创建 了某个评论(2
  • 用户 发布 了某个应用(3
  • 用户 更新 了某个应用(4
  • 用户 删除 了某个应用(5
  • 用户 星标 了某个应用(6
  • 用户 被添加 为 Collaborator(7

关于分页和排序

如何排序

  • 用户 执行 全站排列或搜索时 可以按照 跟随者数目、创建时间 排序
  • 应用 执行 排列或搜索时 可以按照 星标数、评论数、更新时间、创建时间 排序
  • 评论 使用创建时间排序

应用 默认使用更新时间排序,其他列出的默认使用 创建时间,没有列出的查询时的不会添加排序参数

如何分页

所有 需要分页 的请求都可以使用 URL 参数 startlimit,意味着 索引范围 起止(始于 0、包含最小值,不包含最大值

limit 将是 SQL LIMIT 语句 后的参数,它对使用分页的请求是 必须 的,start 参数不是必须的,它默认为 0

需要分页的 API 请求返回 JSON 列表 都在 list 字段里,表本身包含 startlimittotal 指示 请求参数 和服务端实际上的结果列表 总长度

如果 start 大于实际上 SQL 查询返回数据的长度,直接返回空列表,不然,返回裁剪过的列表

删除时

用户、分类、应用、更新、评论、通知 这些都可以删除,删除后将不会再出现使用 同一 ID 的新实体

用户 只能由 管理员删除,删除时所有他名下的 应用 都会 自动删除,管理员不能拒绝用户删除帐号

如果用户不想让应用随帐号被删除,可以提前将所有权移动到其他用户手上,所有权转交行为是由管理员执行的

被删除用户的 Star 保留、评论全部删除,Following 全部删除,时间线和通知全部删除

应用 删除时自动删除其所有 更新和评论

评论 删除时自动删除其所有 子评论

HTTP API by Example

GeekApk HTTP API

杂项

  • 查询软件版本

GET /version

返回服务端软件版本文本信息,不保证是语义化版本格式

  • 查询服务器自述

GET /description

返回服务器维护人员设置的服务器自述信息

  • 根路径

GET /

返回 API 地址 JSON 信息,类似 GitHub

管理(Doge)

  • 创建用户

  • 修改用户分发密码

  • 删除用户

  • 设置用户屏蔽状态

  • 创建分类

  • 修改分类名

  • 删除分类

  • 删除应用

  • 修改应用分类

  • 转移应用所有权

  • 删除更新

  • 删除评论

分类

  • 查询分类列表信息

GET /categories

返回分类 JSON 对象列表

用户

  • 获得用户信息

GET /user/:uid

返回用户 JSON 对象,如果没有用户,返回 Not Found,如果用户 ID 无效,返回 Bad Request

  • 修改用户属性

PUT /user/:uid

如果 UID 无效,返回 Bad Request

如果没有用户,返回 Not Found

URL 参数 attr 可以是:simple_name、name、avatar、bio

body 应该是新值

如果没有这个参数,返回 Bad Request

如果没有 Cookie,返回 Unauthorized

必须验证 Cookie 中的 uid 是否为 UID 参数,如果不是,返回 Unauthorized

必须验证 Cookie 中的 hash 是否真的是 UID 的,如果不是,返回 Unauthorized

如果用户已经被屏蔽,返回 Forbidden

如果长度超出限制,返回 Payload Too Large

如果修改成功,返回 OK,body 是 修改的属性名 : 新值

  • 修改用户密码

PUT /user/:uid/passwd

body 应该是新用户密码

如果 UID 无效,返回 Bad Request

如果没有用户,返回 Not Found

URL 参数 metahash 应该是用户的分发密码

如果没有这个参数,返回 Unauthorized

如果不是用户的分发密码,返回 Forbidden

如果 body 长度小于 4,返回 Bad Request

如果修改成功,返回 OK

  • 检查用户密码

GET /user/:uid/pass-check

返回 Unauthorized 或 OK

  • 搜索用户

POST /user/search

URL 参数 type 可以是 name 或 bio 或 simple_name,默认查找 name

URL 参数 sort 可以是 followers 或 created,默认 created

body 为搜索数据

如果成功,返回 OK,body 为查找 JSON 结果,使用分页

时间线

  • 获取用户时间线

时间线数据对任何角色都是只读的,你只能分页读取它,只能使用创建时间排行

GET /timeline/:uid

返回用户的时间线 JSON,可以使用分页

URL 参数 only_type 控制只返回指定类型的数据

URL 参数 only_ref 控制只返回指定引用的数据

  • 获取所有用户的时间线

GET /timeline/all

返回用户的时间线 JSON,可以使用分页

URL 参数 only_type 控制只返回指定类型的数据

URL 参数 only_ref 控制只返回指定引用的数据

通知

以下 API 全部要求验证 uid、hash cookie,不分页

  • 获得自己的通知

GET /notifications/:uid

返回 JSON 列表,不包括已读通知

  • 获得自己的所有通知,包括已读的

GET /notifications/:uid/all

返回 JSON 列表

  • 删除自己一段时间里的通知

DELETE /notifications/:uid

URL 参数 start 和 end 都是 Psql 能够识别的时间

  • Mark as readed 自己在一段时间里的通知

PUT /notifications/:uid

URL 参数 start 和 end 都是 Psql 能够识别的时间

应用

  • 获得应用信息

GET /app/:real_id

如果 real_id 无效,返回 Bad Request

如果没有应用,返回 Not Found

如果成功,返回 OK,body 为 app json

  • 修改应用属性

参考 user 修改属性 API

  • 创建应用

  • 搜索应用

  • 根据 AID 查找 real_id

  • 列出应用

  • 删除应用

  • 设置 Collaborators

  • 获得 Collaborators 列表

更新

  • 查看应用所有更新

  • 获得一打应用的最新修订号

  • 读取更新信息

  • 创建更新

  • 修改更新属性

  • 删除更新

评论

  • 列出评论
  • 列出子评论
  • 创建评论
  • 修改评论
  • 删除评论

Follow、Star

  • Follow 用户

  • Unfollow 用户

  • 查看用户被谁 Follow 了

  • 查看用户 Follow 了谁

  • Star 应用

  • Unstar 应用

  • 查看应用被谁 Star 了

  • 查看用户 Star 了哪些应用

GeekApk 基本规划

系统通知是什么

  • 用户的评论被回复(1)
  • 用户被 @ 提到(2)
  • 用户被用户跟随(3)

系统 Timeline(时间线) 会记录什么

  • 用户跟随了某个用户(1)
  • 用户发布了某个评论(2)
  • 用户发布了某个应用(3)
  • 用户发布了某个应用的更新(4)
  • 用户删除了某个应用(5)
  • 用户星标了某个应用(6)

怎么排序

用户可按照跟随数目排序,这要在数据库表里增加 followers_num 字段

应用可按照 Star 数、Comment 数、更新时间排序,这要在表里增加 stars_numcomments_num 字段

评论只能按照创建时间排序

所有数据默认使用创建时间正向排序

怎么分页

所有需要分页的请求都可以使用 URL 参数 startend,意味着索引范围起止(始于 0、包含最小值,不包含最大值)

end 将是 SQL LIMIT 语句后的参数,它是必须的,但 start 不是必须的,它默认为 0

需要分页的 API 请求返回列表都在 list 字段里,表本身包含 startendtotal 指示请求参数和表总长度

如果 start 大于实际上 SQL 查询返回数据的长度,直接返回空列表,不然,返回裁剪过的列表

关于权限

权限还是用户可写,全服只有一个管理帐号使用 DOGE_TOK (URL 参数 doge)验证,只行使管理员权限而没有用户权限

用户还是 uidhash 两个 cookie、分发密码称为 metahash,用来重置密码

计算密码的 Hash 值过程由客户端执行,所有官方客户端都自动取 Hash 值作为密码,服务器只把密码作为字符串看待

GeekApk 允许应用主添加其他 Collaborator UID 以允许其他人获得自己应用的 Write Access

哪些东西应该是 Markdown 格式化的

  • 用户自我介绍
  • 应用描述
  • 更新内容
  • 评论内容

简易模型

用户称为 user,复数形式为 users,以 uid 识别,它是 GeekApk 的用户帐号

用户可以跟随其他用户,用户有 metahash 和 hash,用户可以发布评论/应用/更新/星标。

分类称为 category,复数形式为 categories,以 tid 识别,它是 GeekApk 的应用分类

管理员可以创建/删除分类、修改分类名。

应用称为 app,复数形式为 apps,以 aid 识别,是 GeekApk 的应用

应用可以被附加更新和评论,被用户星标。

评论称为 comment,复数形式 comments,以 cid 识别,它是 GeekApk 评论

评论可以被附加子评论。

他们具体应该包含什么内容

用户有简易名称、用户名和 UID、头像链接、自我介绍、创建时间、管理员修改的小黑屋状态

分类有自己不可修改的 TID 和管理员修改的名称,一般以 / 切分分类路径

应用有 AID(内部使用的唯一标识)、包名(也是唯一标识)、管理员修改的创建用户 ID 引用、管理员修改的所属分类 ID 引用、名称、图标链接、应用描述、预览图像、创建时间、更新时间

更新有不可修改的所属应用的 ID 引用、版本名、reversion、安装链接、更新内容、最低适配 SDK 版本、创建时间

评论有 CID、不可修改的创建用户的 ID 引用、不可修改的所属应用的 ID 引用、不可修改可空的被回复评论的 ID 引用、内容、更新时间、被回复数、创建时间

时间线有所属用户引用、创建时间、类型、数据引用

通知有所属用户、创建时间、类型、数据引用、已读状态

为了允许在用户和应用间创建 Star 关系、用户和用户之前创建跟随关系,还额外建立了 followstar

创建时间和 ID 都是只读的、系统生成的数据除了已读状态、都不可修改

模型权限

管理员拥有管理用户、部分用户权限转移和分类的权限 "Write access"

用户拥有管理自己的部分用户信息、评论、应用和它的所有更新、通知、Star、Follow 的权限 “Write access”

合作者拥有管理所属应用的权限 "Write access"

所有人都拥有查看除用户密码/通知外一切平台信息的权限 "Read-only access"

On-delete

用户、分类、应用、更新、评论、通知 都作为可删除的实体存在(Star 和 Follow 则不作为实体看待,但允许 Unstar/Unfollow),删除后将不会再出现使用同一 ID 的新实体

用户只能由管理员删除,删除时所有他名下的应用都会自动删除,管理员不能拒绝用户删除帐号

如果用户不想让应用随帐号被删除,可以将所有权移动到其他用户手上,所有权转交行为是由管理员执行的

被删除用户的 Star 保留、评论全部删除、所有数据持久化层失去引用的数据(如用户密码表上的、用户跟随)全部删除

分类、更新、通知是独立的个体,不被其他个体依赖所以没有特殊规则

应用删除时自动删除其所有更新和评论

评论删除时自动删除其所有子评论

GeekApk API JSON 对象

用户

{
    "uid": 1,
    "simple_name": "duangsuse",
    "name": "duangsuse",
    "avatar": "https://i.jpg.dog/img/9bb1c31af0f51e5b8d2e1ebb9318da3d.jpg",
    "bio": "Salted fish, salty life....",
    "created": 1532180922,
    "banned": false
}

分类

{
    "tid": 0,
    "name": "系统工具/优化清理"
}

应用

{
    "real_id": 0,
    "aid": "kh.android.dir",
    "author": 3,
    "category": 0,
    "name": "Dir",
    "icon": "https://i.loli.net/2018/01/11/5a572dc8d8967.png",
    "readme": "## Dir - 简单美观的 Android 垃圾清理工具\n\n- 阻止创建垃圾文件夹\n> Dir 支持使用文件替换文件夹来尽可能地阻止垃圾文件夹复活\n- 支持更多软件\n> Dir 依靠来自社区的力量获得更多垃圾文件规则,包括那些小众应用\n- Material Design\nDir 遵循 Material Design 设计,小巧美观\n- 良心整洁 绿色应用\n> Dir 无任何后台和推送,已通过绿色应用公约认证\n## 权限清单\n",
    "previews": "http://image.coolapk.com/apk_image/2018/0111/6706e5403da311bd-128297-o_1c3iqijt71lmp19f3igf10l91nd310-uid-543424@1440x2560.png.t.jpg;http://image.coolapk.com/apk_image/2017/0710/screener_1499669411395-for-128297-o_1bklkjfa71bed1qgv5bg4fv62cq-uid-543424.png.t.jpg",
    "created": 1522181505,
    "updated": 1532081641
}

评论

{
    "cid": 1,
    "author": 1,
    "app": 0,
    "reply": null,
    "content": "&em@doge/smile!&",
    "created": 1532082181,
    "updated": null,
    "replies": 1
}

更新

{
    "app": 0,
    "version": "1.62b",
    "rev": 36,
    "install": "https_dl://api.trumeet.top/dir/v1/beta?stable",
    "updates": "fooooooo",
    "min_sdk": 21,
    "created": 1532082513
}

时间线

{
    "uid": 1,
    "type": 1,
    "ref": 0,
    "created": 1532182596
}

通知

{
    "created": 1532182596,
    "type": 2,
    "ref": 0,
    "readed": false
}

Star 某应用的人/某人 Star 的应用返回

按 Star 时间排行的空格分割 AID/UID 列表

12 32 23 4 2

Follow 某人的/被某人 Follow 的返回列表

按开始跟随时间排行的空格切分 UID 列表

12 0 21 4 566
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment