Skip to main content

The User Model

The user model is the authentication and profile entity of the whole system. Every person โ€” admins, reporters, editors, managers โ€” is a user, with a level for role-based access control. Source: back/models/user.ts.

export const user_level_emums = enums([
"Ghost", "Manager", "Editor", "Reporter", "Artist", "Diplomat", "Researcher", "Ordinary",
]);

export const user_pure = {
first_name: string(),
last_name: string(),
email: emailPattern,
password: string(),
gender: optional(enums(["Male", "Female"])),
birth_date: optional(coerce(date(), string(), (v) => new Date(v))),
summary: optional(string()),
address: optional(string()),
level: defaulted(coerce(user_level_emums, string(), (v) => v), "Ordinary"),
is_verified: defaulted(boolean(), false),
bio: optional(localizedWarInfo), // { fa, en, ar, zh, pt, es, nl, tr, ru }
expertise: optional(array(string())), // string[]
verified: defaulted(boolean(), false),
verificationBadge: optional(string()),
isPublic: defaulted(boolean(), true),
...createUpdateAt,
};

Pure fieldsโ€‹

FieldTypeNotes
first_name / last_namestring()text-indexed for search
emailpattern(string(), regex)unique index
passwordstring()excluded from every response
levelenums(...)the role used by grantAccess
is_verified / verifiedboolean()account + profile verification
biooptional(localizedWarInfo)9-language object (no selected_language)
expertiseoptional(array(string()))skill tags
isPublicdefaulted(boolean(), true)profile visibility
createdAt / updatedAtspread from createUpdateAt

Relationsโ€‹

export const user_relations = {
avatar: {
schemaName: "file",
type: "single" as RelationDataType,
optional: true,
excludes: file_excludes,
relatedRelations: {},
},
national_card: {
schemaName: "file",
type: "single" as RelationDataType,
optional: true,
excludes: file_excludes,
relatedRelations: {},
},
province: {
schemaName: "province",
type: "single" as RelationDataType,
optional: true,
excludes: location_excludes,
relatedRelations: {
users: { type: "multiple" as RelationDataType, limit: 50, excludes: user_excludes },
},
},
city: {
schemaName: "city",
type: "single" as RelationDataType,
optional: true,
excludes: location_excludes,
relatedRelations: {
users: { type: "multiple" as RelationDataType, limit: 50, excludes: user_excludes },
},
},
};

Three relation patterns worth noticing:

  • avatar / national_card โ€” one-directional single relations to file with no reverse side (relatedRelations: {}). A file belongs to exactly one thing; it doesn't need a back-list.
  • province / city โ€” one-directional single relations whose relatedRelations creates a sorted, limited users array on the Province/City model (newest 50).
  • excludes everywhere โ€” heavy fields (or the whole password) are trimmed from the embedded back-references so payloads stay small.

Registration & indexesโ€‹

export const users = () =>
coreApp.odm.newModel("user", user_pure, user_relations, {
createIndex: {
indexSpec: { first_name: "text", last_name: "text", email: "text" },
},
excludes: ["password"],
});

Two details worth noting:

  • createIndex with a text index enables full-text search on user.getUsers (see the Search & Indexes pattern page).
  • excludes: ["password"] at the model level means the password is stripped from every response โ€” you never have to remember to exclude it per-act.

Actsโ€‹

The user domain has 13 acts, covering auth and profile management:

login, register, tempUser, getMe, getUser, getUsers, addUser, updateUser, updateUserRelations, removeUser, countUsers, dashboardStatistic.

The auth acts (login, register, getMe) are explored in detail on the Authentication Chain pattern page.

Next: The File Model.