Skip to content

Commit

Permalink
Merge pull request #73 from supertokens/areeb/error-boundary-and-misc
Browse files Browse the repository at this point in the history
feat: Add email password login to the dashboard
  • Loading branch information
rishabhpoddar authored Feb 22, 2023
2 parents c33bdd2 + 95a9cca commit edf317f
Show file tree
Hide file tree
Showing 50 changed files with 1,358 additions and 432 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [0.4.0] - 2023-02-22

- Adds email password based login to the dashboard that can be used instead of API keys

## [0.3.3] - 2023-02-07

- Added build files to ensure the previous version becomes effective.
Expand Down
87 changes: 87 additions & 0 deletions api_spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,99 @@ info:
description: Interface for dashboard and backend SDK to communicate with each other. All APIs must be prefixed by `{apiDomain}/{apiBasePath}`

tags:
- name: "User Authentication"
description: APIs for validating user authentication & authorization
- name: "User Management"
description: APIs for user management features
- name: "User Details"
description: APIs for fetching and modifying information specific to one user

paths:
/signin:
post:
tags:
- User Authentication
summary: Login via email and password
operationId: signinViaEmailPassword
requestBody:
content:
application/json:
schema:
type: object
properties:
email:
type: string
password:
type: string
responses:
200:
description: Success
content:
application/json:
schema:
type: object
properties:
status:
type: string
default: "OK"
sessionId:
type: string
example: "5ebe4237-308f-47a8-b823-344b5372ee8f"
401:
description: Unauthorized access
content:
application/json:
schema:
oneOf:
- type: object
properties:
status:
type: string
default: "INVALID_CREDENTIALS_ERROR"
message:
type: string
- type: object
properties:
status:
type: string
default: "USER_SUSPENDED_ERROR"
message:
type: string
- type: object
properties:
status:
type: string
default: "USER_LIMIT_REACHED_ERROR"
message:
type: string
/verify:
post:
tags:
- User Authentication
summary: Login via API Key
operationId: signinViaAPIKey
responses:
200:
description: Success
/signout:
post:
tags:
- User Authentication
summary: Log out the user
operationId: signOut
responses:
200:
description: Success
content:
application/json:
schema:
type: object
properties:
status:
type: string
default: "OK"
401:
description: Unauthorized access
/users:
get:
tags:
Expand Down
2 changes: 1 addition & 1 deletion build/asset-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
"static/media/left-arrow-dark.svg": "/static/media/left-arrow-dark.svg",
"static/media/help-icon.png": "/static/media/help-icon.png",
"static/media/people.svg": "/static/media/people.svg",
"static/media/provider-facebook.svg": "/static/media/provider-facebook.svg",
"static/media/chevron-left.svg": "/static/media/chevron-left.svg",
"static/media/provider-facebook.svg": "/static/media/provider-facebook.svg",
"static/media/chevron-right.svg": "/static/media/chevron-right.svg",
"static/media/chevron-down.svg": "/static/media/chevron-down.svg",
"static/media/triangle-down.svg": "/static/media/triangle-down.svg",
Expand Down
4 changes: 2 additions & 2 deletions build/static/css/main.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/static/css/main.css.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/static/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/static/js/bundle.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions build/static/media/chevron-left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dashboard",
"version": "0.3.3",
"version": "0.4.0",
"private": true,
"dependencies": {
"@babel/core": "^7.16.0",
Expand Down
1 change: 0 additions & 1 deletion server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ SuperTokens.init({
},
recipeList: [
Dashboard.init({
apiKey: "someapikey",
override: {
functions: (original) => {
return {
Expand Down
7 changes: 5 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { getDashboardAppBasePath } from "./utils";

// This is to make sure that images are packed in the build folder
import "./images";
import SignOutBtn from "./ui/components/auth/SignOutBtn";
import AuthWrapper from "./ui/components/authWrapper";
import ErrorBoundary from "./ui/components/errorboundary";
import { LayoutModalContainer } from "./ui/components/layout/layoutModal";
import SafeAreaView from "./ui/components/safeAreaView/SafeAreaView";
Expand All @@ -31,8 +33,9 @@ function App() {
<SafeAreaView />
<ErrorBoundary>
<PopupContentContextProvider>
<>
<AuthWrapper>
<Router basename={getDashboardAppBasePath()}>
<SignOutBtn />
<Routes>
<Route
path="/"
Expand All @@ -46,7 +49,7 @@ function App() {
</Router>
<ToastNotificationContainer />
<LayoutModalContainer />
</>
</AuthWrapper>
</PopupContentContextProvider>
</ErrorBoundary>
</>
Expand Down
39 changes: 39 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { StorageKeys } from "../constants";
import { localStorageHandler } from "../services/storage";
import { getApiUrl, useFetchData } from "../utils";

const useAuthService = () => {
const fetchData = useFetchData();

const logout = async () => {
const response = await fetchData({
url: getApiUrl("/api/signout"),
method: "POST",
});
const body = await response.json();
if (body.status === "OK") {
localStorageHandler.removeItem(StorageKeys.AUTH_KEY);
window.location.reload();
}
};

const signIn = async ({ email, password }: { email: string; password: string }) => {
return await fetchData({
url: getApiUrl("/api/signin"),
method: "POST",
config: {
body: JSON.stringify({
email,
password,
}),
},
});
};

return {
logout,
signIn,
};
};

export default useAuthService;
54 changes: 35 additions & 19 deletions src/api/user/delete.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
import { fetchDataAndRedirectIf401, getApiUrl } from "../../utils";

export const deleteUser = async (userId: string): Promise<{ status: "OK" } | undefined> => {
const response = await fetchDataAndRedirectIf401({
url: getApiUrl("/api/user"),
method: "DELETE",
query: {
userId,
},
});

if (response.ok) {
const body = await response.json();

if (body.status !== "OK") {
return undefined;
import { getApiUrl, useFetchData } from "../../utils";

type TDeleteUserResponse = Promise<{ status: "OK" } | undefined>;

interface IUseDeleteUserService {
deleteUser: (userId: string) => TDeleteUserResponse;
}

const useDeleteUserService = (): IUseDeleteUserService => {
const fetchData = useFetchData();

const deleteUser = async (userId: string): Promise<{ status: "OK" } | undefined> => {
const response = await fetchData({
url: getApiUrl("/api/user"),
method: "DELETE",
query: {
userId,
},
});

if (response.ok) {
const body = await response.json();

if (body.status !== "OK") {
return undefined;
}

return body;
}

return body;
}
return undefined;
};

return undefined;
return {
deleteUser,
};
};

export default useDeleteUserService;
54 changes: 35 additions & 19 deletions src/api/user/email/verify/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
import { EmailVerificationStatus } from "../../../../ui/pages/usersList/types";
import { fetchDataAndRedirectIf401, getApiUrl } from "../../../../utils";
import { getApiUrl, useFetchData } from "../../../../utils";

export const getUserEmailVerificationStatus = async (userId: string): Promise<EmailVerificationStatus> => {
const response = await fetchDataAndRedirectIf401({
url: getApiUrl("/api/user/email/verify"),
method: "GET",
query: { userId },
});
interface IUseVerifyUserEmailService {
getUserEmailVerificationStatus: (userId: string) => Promise<EmailVerificationStatus>;
updateUserEmailVerificationStatus: (userId: string, isEmailVerified: boolean) => Promise<boolean>;
}

const body = await response.json();
return body;
};
const useVerifyUserEmail = (): IUseVerifyUserEmailService => {
const fetchData = useFetchData();

const getUserEmailVerificationStatus = async (userId: string): Promise<EmailVerificationStatus> => {
const response = await fetchData({
url: getApiUrl("/api/user/email/verify"),
method: "GET",
query: { userId },
});

const body = await response.json();
return body;
};

export const updateUserEmailVerificationStatus = async (userId: string, isEmailVerified: boolean) => {
const response = await fetchDataAndRedirectIf401({
url: getApiUrl("/api/user/email/verify"),
method: "PUT",
config: {
body: JSON.stringify({ verified: isEmailVerified, userId }),
},
});
return response?.ok;
const updateUserEmailVerificationStatus = async (userId: string, isEmailVerified: boolean) => {
const response = await fetchData({
url: getApiUrl("/api/user/email/verify"),
method: "PUT",
config: {
body: JSON.stringify({ verified: isEmailVerified, userId }),
},
});
return response?.ok;
};

return {
getUserEmailVerificationStatus,
updateUserEmailVerificationStatus,
};
};

export default useVerifyUserEmail;
38 changes: 25 additions & 13 deletions src/api/user/email/verify/token.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { fetchDataAndRedirectIf401, getApiUrl } from "../../../../utils";

export const sendUserEmailVerification = async (userId: string) => {
const response = await fetchDataAndRedirectIf401({
url: getApiUrl("/api/user/email/verify/token"),
method: "POST",
config: {
body: JSON.stringify({
userId,
}),
},
});
return response?.ok;
import { getApiUrl, useFetchData } from "../../../../utils";

interface IUseVerifyUserTokenService {
sendUserEmailVerification: (userId: string) => Promise<boolean>;
}

const useVerifyUserTokenService = (): IUseVerifyUserTokenService => {
const fetchData = useFetchData();

const sendUserEmailVerification = async (userId: string) => {
const response = await fetchData({
url: getApiUrl("/api/user/email/verify/token"),
method: "POST",
config: {
body: JSON.stringify({
userId,
}),
},
});
return response?.ok;
};

return { sendUserEmailVerification };
};

export default useVerifyUserTokenService;
Loading

0 comments on commit edf317f

Please sign in to comment.