Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@devfamily/admiral
Advanced tools
Admiral is a frontend framework for creating back office using React. It provides out-of-the-box components and tools that make developing an admin interface easy and fast.
Admiral is a frontend framework for creating back office in React. It provides out-of-the-box components and tools that make developing an admin interface easy and fast.
Made with :purple_heart: by dev.family
Requirements:
There are several options for installing Admiral:
To use npx, make sure you have Node.js
npx create-admiral-app@latest
When you enter this command into the console, you will see 2 installation options:
If you choose "Install the template with the backend setting on Express.js", you will install the fully customized template with backend on Express.js.
Detailed installation and startup instructions
All environment variables will be set automatically. If you want to configure them manually, go to the project folder and open the .env.
You will have 1 CRUDs from the start - Users. To find them pass the way - admiral/src/crud/users/index.tsx
If you choose "Install the template without backend setting", you get just the frontend shell of Admiral in the admiral folder with CRUD - Users. To find it pass the way - admiral/src/crud/users/index.tsx
. To use your backend, read the Documentation
Admiral is available at http://localhost:3000. If port 3000 is busy, any other free port will be selected.
In the console you will see something like this
Port 3000 is in use, trying another one...
vite v2.9.15 dev server running at:
> Local: http://localhost:3001/
> Network: http://192.168.100.82:3001/
ready in 459ms.
Detailed installation and startup instructions are in each of the examples.
Open your browser and go to http://localhost:3000.
Yes, that's right. You can just clone this repository and enter the following commands:
yarn
yarn dev
Then go to http://localhost:3000. The Admiral with mock data is now available to you.
The App.tsx file is the entry point into the application. It is where the library is initialized and where the components are rendered Admin
.
import React from 'react'
import { Admin, createRoutesFrom, OAuthProvidersEnum } from '../admiral'
import Menu from './config/menu'
import dataProvider from './dataProvider'
import authProvider from './authProvider'
const apiUrl = '/api'
// import all pages from pages folder and create routes
const Routes = createRoutesFrom(import.meta.globEager('../pages/**/*'))
function App() {
return (
<Admin
dataProvider={dataProvider(apiUrl)}
authProvider={authProvider(apiUrl)}
menu={Menu}
oauthProviders={[
OAuthProvidersEnum.Google,
OAuthProvidersEnum.Github,
OAuthProvidersEnum.Jira,
]}
>
<Routes />
</Admin>
)
}
export default App
The main contract for authorization in the system is the interface AuthProvider
.
export interface AuthProvider {
login: (params: any) => Promise<any>
logout: (params: any) => Promise<void | false | string>
checkAuth: (params: any) => Promise<void>
getIdentity: () => Promise<UserIdentity>
oauthLogin?: (provider: OAuthProvidersEnum) => Promise<{ redirect: string }>
oauthCallback?: (provider: OAuthProvidersEnum, data: string) => Promise<any>
[key: string]: any
}
Example of implementation The interface itself can be customized to your liking, but it is important to respect the contract it provides. Detailed contract description
Let's look at the basic methods of implementation:
Method | Name | Description | Parameters | Return value |
---|---|---|---|---|
login | User Authentication | Makes a POST request to /api/login and stores the token field in localStorage, which is used in further requests | params - object with the fields username and password | An object with a token field and a user object with email and name fields |
logout | User Logout | Makes a POST request to /api/logout and removes the token field from localStorage | void | |
checkAuth | User authorization check | Makes a GET request to /api/checkAuth and checks token validity, expects a status code - 200. Used every time the API is used | Bearer token | Status code 200 |
getIdentity | Receiving user data | Makes a GET request to /api/getIdentity and returns an object with user data | Bearer token | Object user with the fields email and name |
oauthLogin | Authorization via OAuth | Makes a GET request to /api/auth/social-login/${provider} and returns an object with the redirect field, which is used for the redirect | provider - OAuth provider | Object with the field redirect |
oauthCallback | Callback authorization via OAuth | Makes a POST request to /api/auth/social-login/${provider}/callback and stores the token field in localStorage, which is used in further requests | provider - OAuth provider, data - data received from OAuth provider where the token field exists | Object with the field token |
The main contract for working with data represents the interface DataProvider
.
export interface DataProvider {
getList: (
resource: string,
params: Partial<GetListParams>,
) => Promise<GetListResult<RecordType>>
reorderList: (resource: string, params: ReorderParams) => Promise<void>
getOne: (resource: string, params: GetOneParams) => Promise<GetOneResult<RecordType>>
getCreateFormData: (resource: string) => Promise<GetFormDataResult<RecordType>>
getFiltersFormData: (
resource: string,
urlState?: Record<string, any>,
) => Promise<GetFiltersFormDataResult>
create: (resource: string, params: CreateParams) => Promise<CreateResult<RecordType>>
getUpdateFormData: (
resource: string,
params: GetOneParams,
) => Promise<GetFormDataResult<RecordType>>
update: (resource: string, params: UpdateParams) => Promise<UpdateResult<RecordType>>
deleteOne: (resource: string, params: DeleteParams) => Promise<DeleteResult<RecordType>>
[key: string]: any
}
Example of implementation Detailed contract description
Let's look at the basic methods of implementation:
Method | Name | Description | Parameters |
---|---|---|---|
getList | Getting a list of entities | Makes a GET request to /api/${resource} and returns an object with data to be used in the List component | resource - resource name, params - object with query parameters |
reorderList | Changing the order of entities | Makes a POST request to /api/${resource}/reorder and returns an object with data to be used in the List component | resource - resource name, params - object with query parameters |
getOne | Obtaining an entity | Makes a GET request to /api/${resource}/${id} and returns an object with data to be used in the Show component | resource - resource name, id - entity identifier |
getCreateFormData | Getting data for an entity creation form (Select, AjaxSelect) | Makes a GET request to /api/${resource}/create and returns an object with data to be used in the Create component | resource - resource name |
getFiltersFormData | Receiving data for filters | Makes a GET request to /api/${resource}/filters and returns an object with data to be used in the Filters component | resource - resource name, urlState - object with parameters from the url to be used in the component Filters |
create | Creating an Entity | Makes a POST request to /api/${resource} and returns an object with data to be used in the component Create | resource - resource name, params - entity data object |
getUpdateFormData | Getting data for an entity edit form (Select, AjaxSelect) | Makes a GET request to /api/${resource}/${id}/update and returns an object with data to be used in the component Edit | resource - resource name, id - entity identifier |
update | Updating an entity | Makes a POST request to /api/${resource}/${id} and returns an object with the data to be used in the component Edit | resource - resource name, id - entity identifier, params - entity data object |
delete | Deleting an Entity | Делает DELETE запрос на /api/${resource}/${id} и возвращает объект с данными, которые будут использоваться в компоненте Delete | resource - resource name, id - entity identifier |
Query:
http://localhost/admin/users?page=1&perPage=10&filter%5Bid%5D=1
Result:
{
"items": [
{
"id": 1,
"name": "Dev family",
"email": "info@dev.family",
"role": "Administrator",
"created_at": "2023-05-05 14:17:51"
}
],
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"per_page": 10,
"to": 1,
"total": 1
}
}
Query:
http://localhost/admin/users/1/update?id=1
Result:
{
"data": {
"id": 1,
"name": "Dev family",
"email": "info@dev.family",
"role_id": 1
},
"values": {
"role_id": [
{
"label": "Administrator",
"value": 1
}
]
}
}
❗Note: We utilize the HTTP status code 422 Unprocessable Entity to handle validation errors.
{
"errors": {
"name": ["Field 'name' is invalid."],
"email": ["Field 'email' is invalid."]
},
"message": "Validation failed"
}
Pagination work with getList
method. You can pass page
and perPage
params to getList
method, and it will return PaginationResult
object with items
and meta
fields.
Filters work with getList
method. You can pass filter[$field]
param to getList
method, and it will return PaginationResult
object with items
and meta
fields.
Sorting work with getList
method. You can pass sort[$field]
param to getList
method, and it will return PaginationResult
object with items
and meta
fields.
Admiral has a file-system based router.
A page is a React Component exported from a .js, .jsx, .ts, or .tsx file in the pages' directory. When a file is added to the pages' directory, it's automatically available as a route. react-router-dom is used under the hood.
The router will automatically route files named index to the root of the directory.
pages/index.ts → /
pages/users/index.ts → /users
The router supports nested files. If you create a nested folder structure, files will automatically be routed in the same way still.
pages/users/create.ts → /users/create
To match a dynamic segment, you can use the bracket syntax. This allows you to match named parameters.
pages/users/[id].ts → /users/:id (/users/42)
A menu is an array of objects that have the following structure:
import { Menu, SubMenu, MenuItemLink } from '../../admiral'
const CustomMenu = () => {
return (
<Menu>
<MenuItemLink icon="FiCircle" name="First Menu Item" to="/first" />
<SubMenu icon="FiCircle" name="Second Menu Item">
<MenuItemLink icon="FiCircle" name="Sub Menu Item" to="/second" />
</SubMenu>
</Menu>
)
}
export default CustomMenu
Our application uses React hooks. You can use them from anywhere in the application inside the React components. These are the hooks you can use:
This hook allows you to receive and manage the status of the navigation bar
import { useNav } from '@devfamily/admiral'
const { collapsed, toggleCollapsed, visible, toggle, open, close } = useNav()
This hook allows you to get form values and manage the state of the form. The hook can be used in components used in "form" inside the configuration of the createCRUD function.
import { useForm } from '@devfamily/admiral'
const {
values,
options,
errors,
setErrors,
setValues,
setOptions,
isSubmitting,
isFetching,
locale,
} = useForm()
This hook allows you to receive and manage the state of theme.
import { useTheme } from '@devfamily/admiral'
const { themeName, setTheme } = useTheme()
A hook that allows you to get the state obtained by calling AuthProvider.getIdentity()
import { useGetIdentty } from '@devfamily/admiral'
const { identity, loading, loaded, error } = useGetIdentty()
Icons used in Admiral are from React Icons.
ThemeProvider uses @consta/uikit Theme component under the hood.
You can pass your presets to Admin
component with themePresets
prop:
import React from 'react'
import { Admin, createRoutesFrom } from '../admiral'
import Menu from './config/menu'
import dataProvider from './dataProvider'
import authProvider from './authProvider'
import themeLight from './theme/presets/themeLight'
import themeDark from './theme/presets/themeDark'
const apiUrl = '/api'
const Routes = createRoutesFrom(import.meta.globEager('../pages/**/*'))
function App() {
return (
<Admin
dataProvider={dataProvider(apiUrl)}
authProvider={authProvider(apiUrl)}
menu={Menu}
themePresets={{ light: themeLight, dark: themeDark }}
>
<Routes />
</Admin>
)
}
Create a directory for presets. Inside make folders for each modifier - the same as in the Theme component.
Create CSS files. In the folders with the modifiers put the CSS files that will be responsible for those modifiers.
You will get something similar:
presets/
_color/
_Theme_color_themeDark.css
_Theme_color_themeLight.css
_control/
_Theme_control_themeLight.css
_font/
_Theme_font_themeLight.css
_size/
_Theme_size_themeLight.css
_space/
_Theme_space_themeLight.css
_shadow/
_Theme_shadow_themeLight.css
themeLight.ts
themeDark.ts
Configure the variables in CSS files.
Create a preset files (themeLight, themeDark).
Import the CSS files you are going to use.
Create a preset object. Specify which values (i.e. CSS files) for which modifiers to use in the preset. You will get something similar:
// in presets/themeLight.ts
import './_color/_Theme_color_themeLight.css'
import './_color/_Theme_color_themeDark.css'
import './_control/_Theme_control_themeLight.css'
import './_font/_Theme_font_themeLight.css'
import './_size/_Theme_size_themeLight.css'
import './_space/_Theme_space_themeLight.css'
import './_shadow/_Theme_shadow_themeLight.css'
export default {
color: {
primary: 'themeLight',
accent: 'themeDark',
invert: 'themeDark',
},
control: 'themeLight',
font: 'themeLight',
size: 'themeLight',
space: 'themeLight',
shadow: 'themeLight',
}
Pass your presets to Admin
component as in the example above.
❗Note: postcss plugins are used for color transformation in admiral presets example. If you want to reproduce, setup postcss and postcss-color-mod-function plugin.
If you want to participate in the development of Admiral, make a Fork of the repository, make the desired changes and send a pull request. We will be glad to consider your suggestions!
This library is distributed under the MIT license.
If you have any questions, please contact us at: admiral@dev.family We're always happy to receive your feedback!
FAQs
Admiral is a frontend framework for creating back office using React. It provides out-of-the-box components and tools that make developing an admin interface easy and fast.
We found that @devfamily/admiral demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.