chore: rename user role (#108)

* chore: rename user role to `host`

* chore: related frontend changes

* chore: fix migration file

* chore: use tricky sql
This commit is contained in:
boojack 2022-07-08 22:16:18 +08:00 committed by GitHub
parent 6f32643d7c
commit bdc9632b5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 105 additions and 45 deletions

View File

@ -3,6 +3,6 @@ package api
import "github.com/usememos/memos/server/profile"
type SystemStatus struct {
Owner *User `json:"owner"`
Host *User `json:"host"`
Profile *profile.Profile `json:"profile"`
}

View File

@ -4,16 +4,16 @@ package api
type Role string
const (
// Owner is the OWNER role.
Owner Role = "OWNER"
// Host is the HOST role.
Host Role = "HOST"
// NormalUser is the USER role.
NormalUser Role = "USER"
)
func (e Role) String() string {
switch e {
case Owner:
return "OWNER"
case Host:
return "HOST"
case NormalUser:
return "USER"
}

View File

@ -60,17 +60,17 @@ func (s *Server) registerAuthRoutes(g *echo.Group) {
})
g.POST("/auth/signup", func(c echo.Context) error {
// Don't allow to signup by this api if site owner existed.
ownerUserType := api.Owner
ownerUserFind := api.UserFind{
Role: &ownerUserType,
// Don't allow to signup by this api if site host existed.
hostUserType := api.Host
hostUserFind := api.UserFind{
Role: &hostUserType,
}
ownerUser, err := s.Store.FindUser(&ownerUserFind)
hostUser, err := s.Store.FindUser(&hostUserFind)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find owner user").SetInternal(err)
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find host user").SetInternal(err)
}
if ownerUser != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "Site Owner existed, please contact the site owner to signin account firstly.").SetInternal(err)
if hostUser != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "Site Host existed, please contact the site host to signin account firstly.").SetInternal(err)
}
signup := &api.Signup{}

View File

@ -21,22 +21,22 @@ func (s *Server) registerSystemRoutes(g *echo.Group) {
})
g.GET("/status", func(c echo.Context) error {
ownerUserType := api.Owner
ownerUserFind := api.UserFind{
Role: &ownerUserType,
hostUserType := api.Host
hostUserFind := api.UserFind{
Role: &hostUserType,
}
ownerUser, err := s.Store.FindUser(&ownerUserFind)
hostUser, err := s.Store.FindUser(&hostUserFind)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find owner user").SetInternal(err)
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find host user").SetInternal(err)
}
if ownerUser != nil {
if hostUser != nil {
// data desensitize
ownerUser.OpenID = ""
hostUser.OpenID = ""
}
systemStatus := api.SystemStatus{
Owner: ownerUser,
Host: hostUser,
Profile: s.Profile,
}

View File

@ -143,7 +143,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
}
if currentUser == nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Current session user not found with ID: %d", currentUserID)).SetInternal(err)
} else if currentUser.Role != api.Owner {
} else if currentUser.Role != api.Host {
return echo.NewHTTPError(http.StatusForbidden, "Access forbidden for current session user").SetInternal(err)
}

View File

@ -0,0 +1,56 @@
-- change user role field from "OWNER"/"USER" to "HOST"/"USER".
PRAGMA foreign_keys = off;
BEGIN TRANSACTION;
DROP TABLE IF EXISTS _user_old;
ALTER TABLE
user RENAME TO _user_old;
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
email TEXT NOT NULL UNIQUE,
role TEXT NOT NULL CHECK (role IN ('HOST', 'USER')) DEFAULT 'USER',
name TEXT NOT NULL,
password_hash TEXT NOT NULL,
open_id TEXT NOT NULL UNIQUE
);
INSERT INTO user (
id, created_ts, updated_ts, row_status,
email, name, password_hash, open_id
)
SELECT
id,
created_ts,
updated_ts,
row_status,
email,
name,
password_hash,
open_id
FROM
_user_old;
UPDATE
user
SET
role = 'HOST'
WHERE
id IN (
SELECT
id
FROM
_user_old
WHERE
role = 'OWNER'
);
DROP TABLE IF EXISTS _user_old;
COMMIT;
PRAGMA foreign_keys = on;

View File

@ -13,7 +13,7 @@ CREATE TABLE user (
-- allowed row status are 'NORMAL', 'ARCHIVED'.
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
email TEXT NOT NULL UNIQUE,
role TEXT NOT NULL CHECK (role IN ('OWNER', 'USER')) DEFAULT 'USER',
role TEXT NOT NULL CHECK (role IN ('HOST', 'USER')) DEFAULT 'USER',
name TEXT NOT NULL,
password_hash TEXT NOT NULL,
open_id TEXT NOT NULL UNIQUE

View File

@ -11,8 +11,8 @@ VALUES
(
101,
'demo@usememos.com',
'OWNER',
'Demo Owner',
'HOST',
'Demo Host',
'demo_open_id',
-- raw password: secret
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'

View File

@ -106,7 +106,7 @@ func createUser(db *sql.DB, create *api.UserCreate) (*userRaw, error) {
open_id
)
VALUES (?, ?, ?, ?, ?)
RETURNING id, email, role, name, password_hash, open_id, created_ts, updated_ts
RETURNING id, email, role, name, password_hash, open_id, created_ts, updated_ts, row_status
`,
create.Email,
create.Role,
@ -130,6 +130,7 @@ func createUser(db *sql.DB, create *api.UserCreate) (*userRaw, error) {
&userRaw.OpenID,
&userRaw.CreatedTs,
&userRaw.UpdatedTs,
&userRaw.RowStatus,
); err != nil {
return nil, FormatError(err)
}
@ -162,7 +163,7 @@ func patchUser(db *sql.DB, patch *api.UserPatch) (*userRaw, error) {
UPDATE user
SET `+strings.Join(set, ", ")+`
WHERE id = ?
RETURNING id, email, role, name, password_hash, open_id, created_ts, updated_ts
RETURNING id, email, role, name, password_hash, open_id, created_ts, updated_ts, row_status
`, args...)
if err != nil {
return nil, FormatError(err)
@ -180,6 +181,7 @@ func patchUser(db *sql.DB, patch *api.UserPatch) (*userRaw, error) {
&userRaw.OpenID,
&userRaw.CreatedTs,
&userRaw.UpdatedTs,
&userRaw.RowStatus,
); err != nil {
return nil, FormatError(err)
}
@ -218,7 +220,8 @@ func findUserList(db *sql.DB, find *api.UserFind) ([]*userRaw, error) {
password_hash,
open_id,
created_ts,
updated_ts
updated_ts,
row_status
FROM user
WHERE `+strings.Join(where, " AND ")+`
ORDER BY created_ts DESC`,
@ -241,6 +244,7 @@ func findUserList(db *sql.DB, find *api.UserFind) ([]*userRaw, error) {
&userRaw.OpenID,
&userRaw.CreatedTs,
&userRaw.UpdatedTs,
&userRaw.RowStatus,
); err != nil {
fmt.Println(err)
return nil, FormatError(err)

View File

@ -48,7 +48,7 @@ const SettingDialog: React.FC<Props> = (props: Props) => {
<span className="icon-text">🏟</span> Preferences
</span>
</div>
{user?.role === "OWNER" ? (
{user?.role === "HOST" ? (
<>
<span className="section-title">Admin</span>
<div className="section-items-container">

View File

@ -28,7 +28,7 @@ const UserBanner: React.FC<Props> = () => {
if (locationService.getState().pathname === "/") {
api.getSystemStatus().then(({ data }) => {
const { data: status } = data;
setUsername(status.owner.name);
setUsername(status.host.name);
});
} else {
const currentUserId = userService.getCurrentUserId();
@ -51,7 +51,7 @@ const UserBanner: React.FC<Props> = () => {
<div className="user-banner-container">
<div className="username-container" onClick={handleUsernameClick}>
<span className="username-text">{username}</span>
{user?.role === "OWNER" ? <span className="tag">MOD</span> : null}
{user?.role === "HOST" ? <span className="tag">MOD</span> : null}
</div>
<span className="action-btn menu-popup-btn" onClick={handlePopupBtnClick}>
<img src="/icons/more.svg" className="icon-img" />

View File

@ -69,7 +69,7 @@
> .tip-text {
@apply w-auto inline-block float-right text-sm mt-4 text-gray-500 text-right whitespace-pre-wrap;
&.owner-tip {
&.host-tip {
@apply bg-blue-500 text-white px-2 py-1 rounded;
}
}

View File

@ -17,7 +17,7 @@ const validateConfig: ValidatorConfig = {
const Signin: React.FC<Props> = () => {
const pageLoadingState = useLoading(true);
const [siteOwner, setSiteOwner] = useState<User>();
const [siteHost, setSiteHost] = useState<User>();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const actionBtnLoadingState = useLoading(false);
@ -25,7 +25,7 @@ const Signin: React.FC<Props> = () => {
useEffect(() => {
api.getSystemStatus().then(({ data }) => {
const { data: status } = data;
setSiteOwner(status.owner);
setSiteHost(status.host);
if (status.profile.mode === "dev") {
setEmail("demo@usememos.com");
setPassword("secret");
@ -77,7 +77,7 @@ const Signin: React.FC<Props> = () => {
actionBtnLoadingState.setFinish();
};
const handleSignUpAsOwnerBtnsClick = async () => {
const handleSignUpAsHostBtnsClick = async () => {
if (actionBtnLoadingState.isLoading) {
return;
}
@ -96,7 +96,7 @@ const Signin: React.FC<Props> = () => {
try {
actionBtnLoadingState.setLoading();
await api.signup(email, password, "OWNER");
await api.signup(email, password, "HOST");
const user = await userService.doSignIn();
if (user) {
locationService.replaceHistory("/");
@ -132,7 +132,7 @@ const Signin: React.FC<Props> = () => {
</div>
</div>
<div className="action-btns-container">
{siteOwner || pageLoadingState.isLoading ? (
{siteHost || pageLoadingState.isLoading ? (
<button
className={`btn signin-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
onClick={() => handleSigninBtnsClick()}
@ -142,16 +142,16 @@ const Signin: React.FC<Props> = () => {
) : (
<button
className={`btn signin-btn ${actionBtnLoadingState.isLoading ? "requesting" : ""}`}
onClick={() => handleSignUpAsOwnerBtnsClick()}
onClick={() => handleSignUpAsHostBtnsClick()}
>
Sign up as Owner
Sign up as Host
</button>
)}
</div>
<p className={`tip-text ${siteOwner || pageLoadingState.isLoading ? "" : "owner-tip"}`}>
{siteOwner || pageLoadingState.isLoading
? "If you don't have an account, please\ncontact the site owner."
: "You are registering as the Site Owner."}
<p className={`tip-text ${siteHost || pageLoadingState.isLoading ? "" : "host-tip"}`}>
{siteHost || pageLoadingState.isLoading
? "If you don't have an account, please\ncontact the site host."
: "You are registering as the Site Host."}
</p>
</div>
</div>

View File

@ -4,6 +4,6 @@ interface Profile {
}
interface SystemStatus {
owner: User;
host: User;
profile: Profile;
}

View File

@ -1,5 +1,5 @@
type UserId = number;
type UserRole = "OWNER" | "USER";
type UserRole = "HOST" | "USER";
interface User {
id: UserId;