Vectrum
Sign In
79 lines · 2.9 KB · JavaScript
// Likes and lists of repositories.
//
// `repo_likes`: a person likes a repository, once. A repository shows how many people like it, and
// a person's profile the repositories they like.
//
// `repo_lists`: named collections of repositories that a person or an organization keeps, public
// (anyone can see them) or private (only that person, or the organization's members). `slug` is the
// list's address under /lists/<owner>/<slug>, made from its name. `repo_list_items` puts a
// repository in a list; a list never shows anyone a repository they couldn't see anyway.

const text = (name, opts = {}) => ({ type: "text", name, max: 1000, ...opts });
const number = (name, opts = {}) => ({ type: "number", name, onlyInt: true, ...opts });
const time = (name, opts = {}) => number(name, { min: 0, ...opts });
const select = (name, values, opts = {}) => ({ type: "select", name, values, maxSelect: 1, required: true, ...opts });
const relation = (name, collection, opts = {}) => ({
  type: "relation",
  name,
  collectionId: collection.id,
  maxSelect: 1,
  cascadeDelete: false,
  ...opts,
});
const parent = (name, collection) => relation(name, collection, { required: true, cascadeDelete: true });

function create(app, name, fields, indexes = []) {
  const collection = new Collection({
    type: "base",
    name,
    listRule: null,
    viewRule: null,
    createRule: null,
    updateRule: null,
    deleteRule: null,
    fields,
    indexes,
  });
  app.save(collection);
  return collection;
}

migrate(
  (app) => {
    const users = app.findCollectionByNameOrId("users");
    const repos = app.findCollectionByNameOrId("repos");

    create(
      app,
      "repo_likes",
      [parent("repo", repos), parent("user", users), time("created_at", { required: true })],
      ["CREATE UNIQUE INDEX idx_repo_likes ON repo_likes (repo, user)", "CREATE INDEX idx_repo_likes_user ON repo_likes (user, created_at)"],
    );

    const lists = create(
      app,
      "repo_lists",
      [
        parent("owner", users),
        text("name", { required: true, max: 60 }),
        text("slug", { required: true, max: 60 }),
        text("description", { max: 300 }),
        select("visibility", ["public", "private"]),
        time("created_at", { required: true }),
        // When a repository was last added, or the list was made; lists are sorted by it.
        time("updated_at", { required: true }),
      ],
      ["CREATE UNIQUE INDEX idx_repo_lists_slug ON repo_lists (owner, slug)"],
    );

    create(
      app,
      "repo_list_items",
      [parent("list", lists), parent("repo", repos), time("created_at", { required: true })],
      ["CREATE UNIQUE INDEX idx_repo_list_items ON repo_list_items (list, repo)", "CREATE INDEX idx_repo_list_items_repo ON repo_list_items (repo)"],
    );
  },
  (app) => {
    for (const name of ["repo_list_items", "repo_lists", "repo_likes"]) app.delete(app.findCollectionByNameOrId(name));
  },
);