Stefan Pejcic 1 rok temu
rodzic
commit
8496a83edb
100 zmienionych plików z 7179 dodań i 2 usunięć
  1. 21 0
      LICENSE
  2. 272 2
      README.md
  3. 12 0
      commitlint.config.js
  4. 4 0
      cypress/fixtures/auth0-credentials.json
  5. 302 0
      cypress/fixtures/blog-posts.json
  6. 42 0
      cypress/fixtures/categories.json
  7. 4 0
      cypress/fixtures/demo-auth-credentials.json
  8. 231 0
      cypress/fixtures/hasura-blog-posts.json
  9. 251 0
      cypress/fixtures/hasura-blog-posts.ts
  10. 61 0
      cypress/fixtures/hasura-categories.json
  11. 61 0
      cypress/fixtures/hasura-categories.ts
  12. 4 0
      cypress/fixtures/keycloak-credentials.json
  13. 5 0
      cypress/fixtures/mock-post.json
  14. 167 0
      cypress/fixtures/posts.json
  15. 4 0
      cypress/fixtures/strapi-v4-credentials.json
  16. 4 0
      cypress/fixtures/supabase-credentials.json
  17. 81 0
      cypress/support/commands/antd/index.ts
  18. 44 0
      cypress/support/commands/chakra-ui/index.ts
  19. 47 0
      cypress/support/commands/document-title-handler.ts
  20. 252 0
      cypress/support/commands/intercepts/api-fake-rest.ts
  21. 151 0
      cypress/support/commands/intercepts/hasura.ts
  22. 5 0
      cypress/support/commands/intercepts/index.ts
  23. 158 0
      cypress/support/commands/intercepts/strapi-v4.ts
  24. 138 0
      cypress/support/commands/intercepts/supabase.ts
  25. 30 0
      cypress/support/commands/mantine/index.ts
  26. 37 0
      cypress/support/commands/material-ui/index.ts
  27. 23 0
      cypress/support/commands/refine/index.ts
  28. 265 0
      cypress/support/commands/resource.ts
  29. 133 0
      cypress/support/e2e.ts
  30. 154 0
      cypress/support/index.d.ts
  31. 5 0
      cypress/support/types/index.ts
  32. 4 0
      cypress/utils/getIdFromURL/index.ts
  33. 1 0
      cypress/utils/index.ts
  34. 20 0
      documentation/.gitignore
  35. 3 0
      documentation/11111versions.json
  36. 8 0
      documentation/111vercel.json
  37. 33 0
      documentation/README.md
  38. 3 0
      documentation/babel.config.js
  39. 148 0
      documentation/docs/admin/000_intro.md
  40. 86 0
      documentation/docs/admin/001_dashboard.md
  41. 42 0
      documentation/docs/admin/002_notifications.md
  42. 7 0
      documentation/docs/admin/customize/_category_.json
  43. 116 0
      documentation/docs/admin/customize/api.md
  44. 49 0
      documentation/docs/admin/customize/localization.md
  45. 11 0
      documentation/docs/admin/customize/troubleshooting.md
  46. 7 0
      documentation/docs/admin/plans/_category_.json
  47. 24 0
      documentation/docs/admin/plans/change-plan-for-user.md
  48. 51 0
      documentation/docs/admin/plans/hosting_plans.md
  49. 7 0
      documentation/docs/admin/scripts/_category_.json
  50. 135 0
      documentation/docs/admin/scripts/admin.md
  51. 338 0
      documentation/docs/admin/scripts/backup.md
  52. 64 0
      documentation/docs/admin/scripts/docker.md
  53. 93 0
      documentation/docs/admin/scripts/domains.md
  54. 22 0
      documentation/docs/admin/scripts/files.md
  55. 355 0
      documentation/docs/admin/scripts/openpanel_config.md
  56. 34 0
      documentation/docs/admin/scripts/panel.sh
  57. 165 0
      documentation/docs/admin/scripts/php.md
  58. 88 0
      documentation/docs/admin/scripts/plans.md
  59. 63 0
      documentation/docs/admin/scripts/ssl.md
  60. 173 0
      documentation/docs/admin/scripts/users.md
  61. 60 0
      documentation/docs/admin/scripts/webserver.md
  62. 7 0
      documentation/docs/admin/settings/_category_.json
  63. 26 0
      documentation/docs/admin/settings/adminpanel.md
  64. 6 0
      documentation/docs/admin/settings/backups.md
  65. 11 0
      documentation/docs/admin/settings/docker.md
  66. 41 0
      documentation/docs/admin/settings/firewall.md
  67. 70 0
      documentation/docs/admin/settings/general.md
  68. 71 0
      documentation/docs/admin/settings/openpanel.md
  69. 51 0
      documentation/docs/admin/settings/waf.md
  70. 7 0
      documentation/docs/admin/users/_category_.json
  71. 44 0
      documentation/docs/admin/users/openadmin.md
  72. 199 0
      documentation/docs/admin/users/openpanel.md
  73. 144 0
      documentation/docs/changelog/0.1.md
  74. 32 0
      documentation/docs/changelog/intro.md
  75. 23 0
      documentation/docs/enterprise-edition/okta/index.md
  76. 72 0
      documentation/docs/panel/000_intro.md
  77. 21 0
      documentation/docs/panel/account/2fa.md
  78. 7 0
      documentation/docs/panel/account/_category.json
  79. 13 0
      documentation/docs/panel/account/account.md
  80. 42 0
      documentation/docs/panel/account/login.md
  81. 14 0
      documentation/docs/panel/account/login_history.md
  82. 7 0
      documentation/docs/panel/advanced/_category.json
  83. 19 0
      documentation/docs/panel/advanced/advanced.md
  84. 54 0
      documentation/docs/panel/advanced/cronjobs.md
  85. 19 0
      documentation/docs/panel/advanced/process_manager.md
  86. 248 0
      documentation/docs/panel/advanced/server_settings.md
  87. 21 0
      documentation/docs/panel/advanced/ssh.md
  88. 15 0
      documentation/docs/panel/advanced/terminal.md
  89. 7 0
      documentation/docs/panel/analytics/_category.json
  90. 26 0
      documentation/docs/panel/analytics/account_activity.md
  91. 13 0
      documentation/docs/panel/analytics/analytics.md
  92. 18 0
      documentation/docs/panel/analytics/domain_visitors.md
  93. 79 0
      documentation/docs/panel/analytics/resource_usage.md
  94. 7 0
      documentation/docs/panel/applications/_category.json
  95. 16 0
      documentation/docs/panel/applications/applications.md
  96. 139 0
      documentation/docs/panel/applications/pm2.md
  97. 230 0
      documentation/docs/panel/applications/wordpress.md
  98. 88 0
      documentation/docs/panel/caching/Memcached.md
  99. 87 0
      documentation/docs/panel/caching/Redis.md
  100. 7 0
      documentation/docs/panel/caching/_category.json

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Refine Development Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 272 - 2
README.md

@@ -1,2 +1,272 @@
-# openpanel
-OpenPanel Support and Documentation
+<div align="center">
+<a href="https://refine.dev/">
+    <img alt="refine logo" src="https://refine.ams3.cdn.digitaloceanspaces.com/readme/refine-readme-banner.png">
+</a>
+  
+<br/>
+<br/>
+
+<div align="center">
+    <a href="https://refine.dev">Home Page</a> |
+    <a href="https://discord.gg/refine">Discord</a> |
+    <a href="https://refine.dev/examples/">Examples</a> |
+    <a href="https://refine.dev/blog/">Blog</a> |
+    <a href="https://refine.dev/docs/">Documentation</a>
+</div>
+</div>
+
+<br/>
+<br/>
+
+<div align="center"><strong>Build your <a href="https://reactjs.org/">React</a>-based CRUD applications, without constraints.</strong><br>An open-source, headless web application framework developed with flexibility in mind.
+
+<br />
+<br />
+
+</div>
+
+<div align="center">
+
+[![Awesome](https://github.com/refinedev/awesome-refine/raw/main/images/badge.svg)](https://github.com/refinedev/awesome-refine)
+[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8101/badge)](https://www.bestpractices.dev/projects/8101)
+[![npm version](https://img.shields.io/npm/v/@refinedev/core.svg)](https://www.npmjs.com/package/@refinedev/core)
+[![](https://img.shields.io/github/commit-activity/m/refinedev/refine)](https://github.com/refinedev/refine/commits/master)
+[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](CODE_OF_CONDUCT.md)
+
+[![Discord](https://img.shields.io/discord/837692625737613362.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/refine)
+[![Twitter Follow](https://img.shields.io/twitter/follow/refine_dev?style=social)](https://twitter.com/refine_dev)
+
+<a href="https://www.producthunt.com/posts/refine-3?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-refine&#0045;3" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=362220&theme=light&period=daily" alt="refine - 100&#0037;&#0032;open&#0032;source&#0032;React&#0032;framework&#0032;to&#0032;build&#0032;web&#0032;apps&#0032;3x&#0032;faster | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
+
+</div>
+
+<br/>
+
+[![how-refine-works](https://refine.ams3.cdn.digitaloceanspaces.com/website/static/img/new-diagram.jpg)](https://refine.dev)
+
+## What is Refine?
+
+**Refine** is a meta **React** framework that enables the rapid✨ development of a wide range of web applications.
+
+From internal tools to admin panels, B2B apps, and dashboards, it serves as a comprehensive solution for building any type of **CRUD** application.
+
+Refine's internal hooks and components simplify the development process and eliminate repetitive tasks by providing industry-standard solutions for crucial aspects of a project, including **authentication**, **access control**, **routing**, **networking**, **state management**, and **i18n**.
+
+**Refine** is _headless by design_, thereby offering unlimited styling and customization options.
+
+## What do you mean by "headless" ?
+
+Instead of being limited to a set of pre-styled components, **Refine** provides collections of helper `hooks`, `components`, `providers`, and more. Since business logic and the UI are completely decoupled, you can customize the UI without constraints.
+
+It means that **Refine** just works _seamlessly_ with any _custom designs_ or _UI frameworks_. Thanks to it's headless architecture, you can use popular CSS frameworks like [TailwindCSS](https://tailwindcss.com/) or even create your own styles from scratch.
+
+Refine also provides integrations with [Ant Design](https://ant.design/), [Material UI](https://mui.com/material-ui/getting-started/overview/), [Mantine](https://mantine.dev/), and [Chakra UI](https://chakra-ui.com/) to get you started quickly. These libraries are a set of components that are nicely integrated with the headless `@refinedev/core` package.
+
+### Headless in Routing
+
+For the routing, Refine's headless approach shines too. It doesn't tie you to a single routing method or library. Instead, it offers a simple routing interface with built-in integrations for popular libraries.
+
+This means you can use Refine seamlessly on different platforms like React Native, Electron, Next.js, Remix, etc. without any extra setup steps.
+
+## ⚡ Try Refine
+
+Refine's [browser-based app scaffolder](https://refine.dev/#playground) enables you to build a Refine app through an interactive, step-by-step process in your browser.
+
+You have the freedom to select your preferred libraries and frameworks, and the tool generates a ready-to-download boilerplate code. This feature not only lets you preview and tweak your project on the fly but also accelerates the overall development workflow.
+
+<br/>
+
+<div align="center">
+<a href="https://refine.dev/#playground" target="_blank">
+<img src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-07-25-refine-primereact/create-refine-project.gif"    />
+</a>
+</div>
+
+## Use cases
+
+**Refine** shines on _data-intensive⚡_ enterprise B2B applications like **admin panels**, **dashboards** and **internal tools**. Thanks to the built-in **SSR support**, it can also power _customer-facing_ applications like **storefronts**.
+
+You can take a look at some live examples that can be built using **refine** from scratch:
+
+- [Fully-functional CRM Application](https://example.crm.refine.dev/)
+- [Fully-functional Admin Panel](https://s.refine.dev/readme-admin-panel)
+- [Win95 Style Admin panel 🪟](https://win95.refine.dev/)
+- [Medium Clone - Real World Example](https://s.refine.dev/readme-medium-clone)
+- [Multitenancy Example](https://multi-tenancy-strapi.refine.dev/)
+- [Storefront](https://s.refine.dev/readme-ssr-storefront)
+
+[👉 Refer to most popular real use case examples](https://refine.dev/docs/examples/)
+
+[👉 More **Refine** powered different usage scenarios can be found here](https://refine.dev/docs/examples#other-examples)
+
+## Key Features
+
+⚙️ Zero-config, **one-minute setup** with a **single CLI command**
+
+🔌 Connectors for **15+ backend services** including [REST API](https://github.com/refinedev/refine/tree/master/packages/simple-rest), [GraphQL](https://github.com/refinedev/refine/tree/master/packages/graphql), [NestJs CRUD](https://github.com/refinedev/refine/tree/master/packages/nestjsx-crud), [Airtable](https://github.com/refinedev/refine/tree/master/packages/airtable), [Strapi](https://github.com/refinedev/refine/tree/master/packages/strapi), [Strapi v4](https://github.com/refinedev/refine/tree/master/packages/strapi-v4), [Supabase](https://github.com/refinedev/refine/tree/master/packages/supabase), [Hasura](https://github.com/refinedev/refine/tree/master/packages/hasura), [Appwrite](https://github.com/refinedev/refine/tree/master/packages/appwrite), [Nestjs-Query](https://github.com/refinedev/refine/tree/master/packages/nestjs-query), [Firebase](https://firebase.google.com/), [Sanity](https://www.sanity.io/), and [Directus](https://directus.io/).
+
+🌐 **SSR support** with **Next.js** or **Remix**
+
+🔍 Auto-generated **CRUD** UIs from **your API data structure**
+
+⚛ Perfect **state management** & **mutations** with **React Query**
+
+🔀 **Advanced routing** with any router library of your choice
+
+🔐 Providers for seamless **authentication** and **access control** flows
+
+⚡ Out-of-the-box support for **live / real-time applications**
+
+📄 Easy **audit logs** & **document versioning**
+
+💬 Support for any **i18n** framework
+
+💪 Future-proof, **robust architecture**
+
+⌛️ Built-in CLI with time-saving features
+
+💻 Refine [Devtools](https://github.com/refinedev/refine/blob/master/packages/devtools/README.md) - dive deeper into your app and provide useful insights
+
+✅ Full **test coverage**
+
+## Quick Start
+
+There are two ways to create a Refine app: either by using the `create refine-app` CLI tool or the [browser-based app scaffolder](https://refine.dev/#playground).
+
+To quickly start a Refine project with [Ant Design](https://ant.design/) as the default UI framework, run the following command.
+
+```
+npm create refine-app@latest -- -o refine-antd
+```
+
+Once the setup is complete, navigate to the project folder and start your project with:
+
+```
+npm run dev
+```
+
+<br/>
+
+Your **Refine** application will be accessible at [http://localhost:5173](http://localhost:5173):
+
+<a href="http://localhost:5173">![Welcome on board](https://refine.ams3.cdn.digitaloceanspaces.com/website/static/img/new-welcome.png)</a>
+
+<br/>
+
+> Note: The command above uses pre-set options for ease. For a different tech stack, simply run:
+>
+> ```
+> npm create refine-app@latest
+> ```
+
+Let's consume a public `fake REST API` and add two resources (_blog_posts_ and _categories_) to our project. Replace the contents of `src/App.tsx` with the following code:
+
+```tsx title="src/App.tsx"
+import { Refine } from "@refinedev/core";
+import {
+  notificationProvider,
+  ErrorComponent,
+  ThemedLayout,
+} from "@refinedev/antd";
+import routerProvider, { NavigateToResource } from "@refinedev/react-router-v6";
+import dataProvider from "@refinedev/simple-rest";
+
+import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
+
+import { AntdInferencer } from "@refinedev/inferencer/antd";
+
+import "@refinedev/antd/dist/reset.css";
+
+const App: React.FC = () => {
+  return (
+    <BrowserRouter>
+      <Refine
+        routerProvider={routerProvider}
+        dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
+        notificationProvider={notificationProvider}
+        resources={[
+          {
+            name: "blog_posts",
+            list: "/blog-posts",
+            show: "/blog-posts/show/:id",
+            create: "/blog-posts/create",
+            edit: "/blog-posts/edit/:id",
+            meta: { canDelete: true },
+          },
+          {
+            name: "categories",
+            list: "/categories",
+            show: "/categories/show/:id",
+          },
+        ]}
+      >
+        <Routes>
+          <Route
+            element={
+              <ThemedLayout>
+                <Outlet />
+              </ThemedLayout>
+            }
+          >
+            <Route index element={<NavigateToResource />} />
+            <Route path="blog-posts">
+              <Route index element={<AntdInferencer />} />
+              <Route path="show/:id" element={<AntdInferencer />} />
+              <Route path="create" element={<AntdInferencer />} />
+              <Route path="edit/:id" element={<AntdInferencer />} />
+            </Route>
+            <Route path="categories">
+              <Route index element={<AntdInferencer />} />
+              <Route path="show/:id" element={<AntdInferencer />} />
+            </Route>
+            <Route path="*" element={<ErrorComponent />} />
+          </Route>
+        </Routes>
+      </Refine>
+    </BrowserRouter>
+  );
+};
+
+export default App;
+```
+
+<br/>
+
+🚀 The [**Refine Inferencer package**](https://refine.dev/docs/packages/documentation/inferencer/) automatically generates `list`, `show`, `create`, and `edit` pages by guessing configurations from API data. We've used it here for a quick, clear start, but you can also choose to code your pages from scratch instead of using the Inferencer feature.
+
+Now, you should see the output as a table populated with `blog_posts` & `category` data:
+
+![First example result](https://refine.ams3.cdn.digitaloceanspaces.com/website/static/img/readme-quick-start-2.png)
+
+<br/>
+
+You can get the auto-generated page codes by clicking the `Show Code` button on each page. Afterward, simply pass the pages to the `resources` array by replacing them with the Inferencer components.
+
+## Next Steps
+
+👉 Jump to [Tutorial](https://refine.dev/docs/tutorial/introduction/index/) to continue your work and turn the example into a full-blown CRUD application.
+
+👉 Visit the [Learn the Basics page](https://refine.dev/docs/getting-started/overview/) to get informed about the fundamental concepts.
+
+👉 Read more on [Advanced Tutorials
+](https://refine.dev/docs/advanced-tutorials/) for different usage scenarios.
+
+👉 See the real-life [CRM Application](https://example.crm.refine.dev/) project built using Refine.
+
+👉 Play with interactive [examples](https://refine.dev/docs/examples/).
+
+## Contribution
+
+[👉 Refer to the contribution docs for more information.](https://refine.dev/docs/contributing/#ways-to-contribute)
+
+If you have any doubts related to the project or want to discuss something, then join our [Discord server](https://discord.gg/refine).
+
+## Contributors ♥️
+
+<a href="https://github.com/refinedev/refine/graphs/contributors">
+  <img src="https://contrib.rocks/image?repo=refinedev/refine&max=400&columns=20" />
+</a>
+
+## License
+
+Licensed under the MIT License, Copyright © 2021-present Refinedev

+ 12 - 0
commitlint.config.js

@@ -0,0 +1,12 @@
+module.exports = {
+    extends: ["@commitlint/config-conventional"],
+    rules: {
+        "body-max-line-length": [1, "always", 100],
+    },
+    helpUrl:
+        "https://refine.dev/docs/guides-concepts/contributing/#committing-your-work-and-preparing-a-pull-request",
+    ignores: [
+        (commit) =>
+            commit.includes("Optimised images with calibre/image-actions"),
+    ],
+};

+ 4 - 0
cypress/fixtures/auth0-credentials.json

@@ -0,0 +1,4 @@
+{
+  "email": "johndoe@refine.dev",
+  "password": "JohnDoe-refine-dev-123"
+}

+ 302 - 0
cypress/fixtures/blog-posts.json

@@ -0,0 +1,302 @@
+[
+  {
+    "id": 1,
+    "title": "Ut Voluptatem Est",
+    "content": "Repellendus temporibus provident nobis. Non adipisci quod et est dolorem sed qui. A ut omnis. Et perspiciatis quibusdam maiores aliquid est fugit nam odit. Aut aliquam consectetur deleniti commodi velit. Eum eum aperiam voluptate quos quo. Ut quia doloribus a. Molestiae non est fugit enim fugiat non ea quas accusamus. Consequuntur voluptatem nesciunt dolorum expedita optio deserunt. Illo dolorem et similique.",
+    "category": {
+      "id": 1
+    },
+    "status": "published",
+    "createdAt": "2022-06-12T11:03:09.829Z"
+  },
+  {
+    "id": 2,
+    "title": "Sequi Quod Repellendus",
+    "content": "Odit natus dolor consequatur tempore recusandae exercitationem. Unde dolores aut. Dolor ipsam quam quis modi sint. Dolor itaque voluptatum non qui ratione nisi ullam assumenda. Nisi et aut incidunt fuga aut deserunt. Labore sit ut quia vero vel et sed suscipit. Voluptatum maiores adipisci vel molestiae minima in enim et. Repellendus autem quis nisi vero vel consectetur laborum. Similique quos voluptates officiis velit. Sapiente nihil ut ipsa autem.",
+    "category": {
+      "id": 2
+    },
+    "status": "published",
+    "createdAt": "2021-10-11T08:03:00.155Z"
+  },
+  {
+    "id": 3,
+    "title": "Ad Enim Blanditiis",
+    "content": "Illo et facilis deleniti voluptatem neque et optio. Eum rerum distinctio dolor omnis sequi expedita dicta reprehenderit quaerat. In eaque odio dicta ullam cumque qui neque molestias. Libero vitae quo pariatur tempore. Excepturi fuga pariatur est sint quasi. Expedita reiciendis laboriosam explicabo ratione quia laboriosam. Alias ratione ea. Minus ut nisi ad ex eligendi id vero soluta unde. Aut aut placeat reprehenderit voluptatem ut. Voluptatibus laborum modi ducimus.",
+    "category": {
+      "id": 1
+    },
+    "status": "rejected",
+    "createdAt": "2022-04-16T16:34:03.319Z"
+  },
+  {
+    "id": 4,
+    "title": "Dolores Modi Natus",
+    "content": "Magnam pariatur ut ab accusamus minus labore commodi. Excepturi natus hic aut illum mollitia. Ipsum incidunt adipisci rerum veritatis quae rerum. Ipsum blanditiis quisquam ea quia et in. Exercitationem rerum in. Ut quo quia et esse expedita consequatur sint culpa voluptas. Sed optio voluptas totam id aut odio voluptatum sequi. Velit aut ea esse alias voluptatem omnis numquam aut eligendi. Voluptatem natus suscipit. Praesentium enim reiciendis nihil delectus eum totam fugit accusantium fuga.",
+    "category": {
+      "id": 2
+    },
+    "status": "published",
+    "createdAt": "2021-05-20T02:19:56.513Z"
+  },
+  {
+    "id": 5,
+    "title": "Repellendus Aspernatur Porro",
+    "content": "Blanditiis voluptas suscipit harum. Nobis aspernatur quas. Asperiores optio id iste quis sint in modi. Ea explicabo velit numquam sed natus sed aliquam. Modi sapiente expedita suscipit neque reprehenderit qui et officiis. Quis et iusto quis veniam. Ut quis quidem incidunt adipisci non et ad ut. Quis quo illum nemo. Totam esse ut quisquam dolorum explicabo. Ea mollitia sed eos ducimus saepe ratione et doloremque.",
+    "category": {
+      "id": 3
+    },
+    "status": "rejected",
+    "createdAt": "2022-01-09T07:01:11.195Z"
+  },
+  {
+    "id": 6,
+    "title": "Natus Ut Distinctio",
+    "content": "Sit ut eos nulla. Assumenda ut aut aspernatur placeat rerum numquam quasi quos. Aut nihil ratione assumenda quos voluptatem. Repudiandae excepturi vel qui vel facilis non blanditiis asperiores. Et nesciunt consequatur natus incidunt sit accusantium maxime quo. Qui modi maxime est est aut ipsam et illo nihil. Voluptas enim quod tempora sit eveniet atque. Consectetur aperiam quisquam quis rerum. Maiores repellendus dolor et earum consequuntur sit qui dolor minima. Sint vitae veritatis qui consequuntur nemo impedit et.",
+    "category": {
+      "id": 4
+    },
+    "status": "published",
+    "createdAt": "2023-02-01T15:32:15.719Z"
+  },
+  {
+    "id": 7,
+    "title": "Vel Labore Sint",
+    "content": "Quasi magni asperiores laborum accusantium debitis in voluptas quia. Velit mollitia earum magnam maxime aut. Enim at assumenda quidem vel libero facilis vel. Enim voluptate maiores. Quia ut odio fugit tempore iste qui et. Sit eaque voluptate omnis dolorem perspiciatis deleniti non qui assumenda. Possimus et rerum nam possimus inventore dolorem corrupti earum. Architecto consequuntur et cupiditate. Molestiae nisi facere qui ut saepe quis. Eius adipisci tempore itaque sed odio veniam harum quia.",
+    "category": {
+      "id": 5
+    },
+    "status": "rejected",
+    "createdAt": "2022-02-27T09:01:32.679Z"
+  },
+  {
+    "id": 8,
+    "title": "Minus Nulla Molestias",
+    "content": "Qui velit dolorem fuga ad quas omnis harum. Ipsam sint ipsam quos nam sed adipisci. Aut adipisci asperiores tenetur laborum accusantium sint atque. Optio delectus fugiat iusto ut consequatur non delectus sit. Aut corrupti est sequi rerum asperiores fuga aut velit laborum. Quaerat magnam fuga dolores. Vel tenetur neque sapiente. Soluta quia autem delectus eos eveniet. Qui quis et velit molestiae enim qui explicabo dolor. Sit placeat molestias fugiat optio repellat voluptas.",
+    "category": {
+      "id": 6
+    },
+    "status": "rejected",
+    "createdAt": "2022-11-07T12:30:27.331Z"
+  },
+  {
+    "id": 9,
+    "title": "Quibusdam Ut Natus",
+    "content": "Minus nam fugiat veritatis sequi ut. Iure nisi hic exercitationem rerum tempore earum. Rerum velit nemo est omnis nulla consequatur excepturi aut. Iusto ut autem porro commodi non. Et excepturi et aliquid alias qui minima nostrum deleniti. Ut qui consequatur voluptatem. Recusandae eos et odit neque. Non quaerat voluptatem molestiae necessitatibus. Et omnis dolore aperiam delectus. Rerum et et libero.",
+    "category": {
+      "id": 7
+    },
+    "status": "draft",
+    "createdAt": "2021-12-18T05:22:45.857Z"
+  },
+  {
+    "id": 10,
+    "title": "Nam Voluptas Ipsum",
+    "content": "Eum quaerat nostrum nulla consequatur quia quod autem minus. Quis omnis corrupti qui dolores. Non soluta suscipit. Ut distinctio eius consequuntur unde ab vel nobis sint nostrum. Natus omnis omnis debitis. Porro sit rerum vitae officiis dolorem veniam occaecati amet. Vitae et facilis provident enim et perferendis dolorum voluptatum. Fugiat provident explicabo ut libero aperiam dolore voluptatem. Et reiciendis error cupiditate blanditiis ea velit in incidunt. Doloribus et quis totam.",
+    "category": {
+      "id": 8
+    },
+    "status": "rejected",
+    "createdAt": "2021-11-21T05:29:05.942Z"
+  },
+  {
+    "id": 11,
+    "title": "Nam Placeat Expedita",
+    "content": "Et iusto vel aut animi mollitia. Nesciunt nobis sunt et dolore soluta. Dolore doloremque qui vero ut. Aperiam maiores ea. Consequuntur omnis eos. Ipsam reprehenderit cupiditate maiores est fuga dolorum repellendus quisquam dolorem. Consequatur ea reprehenderit nulla quia occaecati laboriosam. Maiores dicta in tempora. Tempora et adipisci aspernatur est. Ipsum et molestiae illum fugiat.",
+    "category": {
+      "id": 9
+    },
+    "status": "rejected",
+    "createdAt": "2023-05-05T19:52:59.535Z"
+  },
+  {
+    "id": 12,
+    "title": "Rerum Voluptatem Sunt",
+    "content": "Voluptas et cumque numquam. Suscipit eum cumque cupiditate vel possimus voluptatibus est ut. Optio molestiae nisi officiis sint sed vel provident accusantium. Omnis voluptatum officiis voluptatem numquam necessitatibus quisquam. A sit quae numquam numquam est. Aut in placeat. Modi quod et quaerat autem nemo. Amet in laudantium. Quis adipisci facere. Sed suscipit labore.",
+    "category": {
+      "id": 4
+    },
+    "status": "rejected",
+    "createdAt": "2021-06-26T00:07:45.503Z"
+  },
+  {
+    "id": 13,
+    "title": "In Sequi Recusandae",
+    "content": "Quia sit nulla occaecati enim. Totam rerum delectus. Quidem dolorum a rerum odio. Et molestias magnam amet sint soluta nemo. Dolore incidunt voluptatum quo recusandae quis consectetur est rerum nobis. Sit facere dignissimos libero et sint unde nam atque sunt. Tenetur facilis eos animi animi possimus. Qui maxime ut eum. Quas accusantium non rerum accusantium id soluta. Illum fugiat similique cumque.",
+    "category": {
+      "id": 2
+    },
+    "status": "draft",
+    "createdAt": "2021-10-02T13:20:44.138Z"
+  },
+  {
+    "id": 14,
+    "title": "Impedit Maiores Consequatur",
+    "content": "Quibusdam sed incidunt assumenda. Nulla unde optio. Reiciendis placeat provident aut culpa aut nam et quis dolores. Ipsa velit non enim est earum quae velit totam corporis. Quod unde recusandae. Dolores et sed vel illo perferendis vitae sunt et. Cumque voluptatem dolores aut illum. Qui voluptates id ut est maxime adipisci rerum. Enim porro quo repudiandae dolore enim incidunt iste. Perferendis adipisci dicta harum.",
+    "category": {
+      "id": 3
+    },
+    "status": "draft",
+    "createdAt": "2021-06-06T15:12:04.543Z"
+  },
+  {
+    "id": 15,
+    "title": "Deserunt Ut Enim",
+    "content": "Explicabo quaerat officia rerum nobis non sint. Fugit blanditiis ipsum et tempora iure eaque quo. Ratione sed voluptatem rerum ad illum veniam sint. Excepturi aliquam et. Dolor reprehenderit reiciendis quia voluptatem accusamus odio et et. Voluptatem maxime enim reprehenderit veritatis voluptatem maxime. Non quia adipisci molestiae dolorem voluptatum ipsam libero sed. Illo et voluptatem. Atque sit eos et fugit fugiat quia sed aliquam eveniet. Veniam nulla dolorem exercitationem blanditiis consequatur ipsam.",
+    "category": {
+      "id": 9
+    },
+    "status": "draft",
+    "createdAt": "2022-04-24T18:07:20.293Z"
+  },
+  {
+    "id": 16,
+    "title": "Ut Voluptatem Est",
+    "content": "Repellendus temporibus provident nobis. Non adipisci quod et est dolorem sed qui. A ut omnis. Et perspiciatis quibusdam maiores aliquid est fugit nam odit. Aut aliquam consectetur deleniti commodi velit. Eum eum aperiam voluptate quos quo. Ut quia doloribus a. Molestiae non est fugit enim fugiat non ea quas accusamus. Consequuntur voluptatem nesciunt dolorum expedita optio deserunt. Illo dolorem et similique.",
+    "category": {
+      "id": 1
+    },
+    "status": "published",
+    "createdAt": "2022-06-12T11:03:09.829Z"
+  },
+  {
+    "id": 17,
+    "title": "Sequi Quod Repellendus",
+    "content": "Odit natus dolor consequatur tempore recusandae exercitationem. Unde dolores aut. Dolor ipsam quam quis modi sint. Dolor itaque voluptatum non qui ratione nisi ullam assumenda. Nisi et aut incidunt fuga aut deserunt. Labore sit ut quia vero vel et sed suscipit. Voluptatum maiores adipisci vel molestiae minima in enim et. Repellendus autem quis nisi vero vel consectetur laborum. Similique quos voluptates officiis velit. Sapiente nihil ut ipsa autem.",
+    "category": {
+      "id": 2
+    },
+    "status": "published",
+    "createdAt": "2021-10-11T08:03:00.155Z"
+  },
+  {
+    "id": 18,
+    "title": "Ad Enim Blanditiis",
+    "content": "Illo et facilis deleniti voluptatem neque et optio. Eum rerum distinctio dolor omnis sequi expedita dicta reprehenderit quaerat. In eaque odio dicta ullam cumque qui neque molestias. Libero vitae quo pariatur tempore. Excepturi fuga pariatur est sint quasi. Expedita reiciendis laboriosam explicabo ratione quia laboriosam. Alias ratione ea. Minus ut nisi ad ex eligendi id vero soluta unde. Aut aut placeat reprehenderit voluptatem ut. Voluptatibus laborum modi ducimus.",
+    "category": {
+      "id": 1
+    },
+    "status": "rejected",
+    "createdAt": "2022-04-16T16:34:03.319Z"
+  },
+  {
+    "id": 19,
+    "title": "Dolores Modi Natus",
+    "content": "Magnam pariatur ut ab accusamus minus labore commodi. Excepturi natus hic aut illum mollitia. Ipsum incidunt adipisci rerum veritatis quae rerum. Ipsum blanditiis quisquam ea quia et in. Exercitationem rerum in. Ut quo quia et esse expedita consequatur sint culpa voluptas. Sed optio voluptas totam id aut odio voluptatum sequi. Velit aut ea esse alias voluptatem omnis numquam aut eligendi. Voluptatem natus suscipit. Praesentium enim reiciendis nihil delectus eum totam fugit accusantium fuga.",
+    "category": {
+      "id": 2
+    },
+    "status": "published",
+    "createdAt": "2021-05-20T02:19:56.513Z"
+  },
+  {
+    "id": 20,
+    "title": "Repellendus Aspernatur Porro",
+    "content": "Blanditiis voluptas suscipit harum. Nobis aspernatur quas. Asperiores optio id iste quis sint in modi. Ea explicabo velit numquam sed natus sed aliquam. Modi sapiente expedita suscipit neque reprehenderit qui et officiis. Quis et iusto quis veniam. Ut quis quidem incidunt adipisci non et ad ut. Quis quo illum nemo. Totam esse ut quisquam dolorum explicabo. Ea mollitia sed eos ducimus saepe ratione et doloremque.",
+    "category": {
+      "id": 3
+    },
+    "status": "rejected",
+    "createdAt": "2022-01-09T07:01:11.195Z"
+  },
+  {
+    "id": 21,
+    "title": "Natus Ut Distinctio",
+    "content": "Sit ut eos nulla. Assumenda ut aut aspernatur placeat rerum numquam quasi quos. Aut nihil ratione assumenda quos voluptatem. Repudiandae excepturi vel qui vel facilis non blanditiis asperiores. Et nesciunt consequatur natus incidunt sit accusantium maxime quo. Qui modi maxime est est aut ipsam et illo nihil. Voluptas enim quod tempora sit eveniet atque. Consectetur aperiam quisquam quis rerum. Maiores repellendus dolor et earum consequuntur sit qui dolor minima. Sint vitae veritatis qui consequuntur nemo impedit et.",
+    "category": {
+      "id": 4
+    },
+    "status": "published",
+    "createdAt": "2023-02-01T15:32:15.719Z"
+  },
+  {
+    "id": 22,
+    "title": "Vel Labore Sint",
+    "content": "Quasi magni asperiores laborum accusantium debitis in voluptas quia. Velit mollitia earum magnam maxime aut. Enim at assumenda quidem vel libero facilis vel. Enim voluptate maiores. Quia ut odio fugit tempore iste qui et. Sit eaque voluptate omnis dolorem perspiciatis deleniti non qui assumenda. Possimus et rerum nam possimus inventore dolorem corrupti earum. Architecto consequuntur et cupiditate. Molestiae nisi facere qui ut saepe quis. Eius adipisci tempore itaque sed odio veniam harum quia.",
+    "category": {
+      "id": 5
+    },
+    "status": "rejected",
+    "createdAt": "2022-02-27T09:01:32.679Z"
+  },
+  {
+    "id": 23,
+    "title": "Minus Nulla Molestias",
+    "content": "Qui velit dolorem fuga ad quas omnis harum. Ipsam sint ipsam quos nam sed adipisci. Aut adipisci asperiores tenetur laborum accusantium sint atque. Optio delectus fugiat iusto ut consequatur non delectus sit. Aut corrupti est sequi rerum asperiores fuga aut velit laborum. Quaerat magnam fuga dolores. Vel tenetur neque sapiente. Soluta quia autem delectus eos eveniet. Qui quis et velit molestiae enim qui explicabo dolor. Sit placeat molestias fugiat optio repellat voluptas.",
+    "category": {
+      "id": 6
+    },
+    "status": "rejected",
+    "createdAt": "2022-11-07T12:30:27.331Z"
+  },
+  {
+    "id": 24,
+    "title": "Quibusdam Ut Natus",
+    "content": "Minus nam fugiat veritatis sequi ut. Iure nisi hic exercitationem rerum tempore earum. Rerum velit nemo est omnis nulla consequatur excepturi aut. Iusto ut autem porro commodi non. Et excepturi et aliquid alias qui minima nostrum deleniti. Ut qui consequatur voluptatem. Recusandae eos et odit neque. Non quaerat voluptatem molestiae necessitatibus. Et omnis dolore aperiam delectus. Rerum et et libero.",
+    "category": {
+      "id": 7
+    },
+    "status": "draft",
+    "createdAt": "2021-12-18T05:22:45.857Z"
+  },
+  {
+    "id": 25,
+    "title": "Nam Voluptas Ipsum",
+    "content": "Eum quaerat nostrum nulla consequatur quia quod autem minus. Quis omnis corrupti qui dolores. Non soluta suscipit. Ut distinctio eius consequuntur unde ab vel nobis sint nostrum. Natus omnis omnis debitis. Porro sit rerum vitae officiis dolorem veniam occaecati amet. Vitae et facilis provident enim et perferendis dolorum voluptatum. Fugiat provident explicabo ut libero aperiam dolore voluptatem. Et reiciendis error cupiditate blanditiis ea velit in incidunt. Doloribus et quis totam.",
+    "category": {
+      "id": 8
+    },
+    "status": "rejected",
+    "createdAt": "2021-11-21T05:29:05.942Z"
+  },
+  {
+    "id": 26,
+    "title": "Nam Placeat Expedita",
+    "content": "Et iusto vel aut animi mollitia. Nesciunt nobis sunt et dolore soluta. Dolore doloremque qui vero ut. Aperiam maiores ea. Consequuntur omnis eos. Ipsam reprehenderit cupiditate maiores est fuga dolorum repellendus quisquam dolorem. Consequatur ea reprehenderit nulla quia occaecati laboriosam. Maiores dicta in tempora. Tempora et adipisci aspernatur est. Ipsum et molestiae illum fugiat.",
+    "category": {
+      "id": 9
+    },
+    "status": "rejected",
+    "createdAt": "2023-05-05T19:52:59.535Z"
+  },
+  {
+    "id": 27,
+    "title": "Rerum Voluptatem Sunt",
+    "content": "Voluptas et cumque numquam. Suscipit eum cumque cupiditate vel possimus voluptatibus est ut. Optio molestiae nisi officiis sint sed vel provident accusantium. Omnis voluptatum officiis voluptatem numquam necessitatibus quisquam. A sit quae numquam numquam est. Aut in placeat. Modi quod et quaerat autem nemo. Amet in laudantium. Quis adipisci facere. Sed suscipit labore.",
+    "category": {
+      "id": 4
+    },
+    "status": "rejected",
+    "createdAt": "2021-06-26T00:07:45.503Z"
+  },
+  {
+    "id": 28,
+    "title": "In Sequi Recusandae",
+    "content": "Quia sit nulla occaecati enim. Totam rerum delectus. Quidem dolorum a rerum odio. Et molestias magnam amet sint soluta nemo. Dolore incidunt voluptatum quo recusandae quis consectetur est rerum nobis. Sit facere dignissimos libero et sint unde nam atque sunt. Tenetur facilis eos animi animi possimus. Qui maxime ut eum. Quas accusantium non rerum accusantium id soluta. Illum fugiat similique cumque.",
+    "category": {
+      "id": 2
+    },
+    "status": "draft",
+    "createdAt": "2021-10-02T13:20:44.138Z"
+  },
+  {
+    "id": 29,
+    "title": "Impedit Maiores Consequatur",
+    "content": "Quibusdam sed incidunt assumenda. Nulla unde optio. Reiciendis placeat provident aut culpa aut nam et quis dolores. Ipsa velit non enim est earum quae velit totam corporis. Quod unde recusandae. Dolores et sed vel illo perferendis vitae sunt et. Cumque voluptatem dolores aut illum. Qui voluptates id ut est maxime adipisci rerum. Enim porro quo repudiandae dolore enim incidunt iste. Perferendis adipisci dicta harum.",
+    "category": {
+      "id": 3
+    },
+    "status": "draft",
+    "createdAt": "2021-06-06T15:12:04.543Z"
+  },
+  {
+    "id": 30,
+    "title": "Deserunt Ut Enim",
+    "content": "Explicabo quaerat officia rerum nobis non sint. Fugit blanditiis ipsum et tempora iure eaque quo. Ratione sed voluptatem rerum ad illum veniam sint. Excepturi aliquam et. Dolor reprehenderit reiciendis quia voluptatem accusamus odio et et. Voluptatem maxime enim reprehenderit veritatis voluptatem maxime. Non quia adipisci molestiae dolorem voluptatum ipsam libero sed. Illo et voluptatem. Atque sit eos et fugit fugiat quia sed aliquam eveniet. Veniam nulla dolorem exercitationem blanditiis consequatur ipsam.",
+    "category": {
+      "id": 9
+    },
+    "status": "draft",
+    "createdAt": "2022-04-24T18:07:20.293Z"
+  }
+]

+ 42 - 0
cypress/fixtures/categories.json

@@ -0,0 +1,42 @@
+[
+  {
+    "id": 1,
+    "title": "Sint Ipsam Tempora"
+  },
+  {
+    "id": 2,
+    "title": "Neque Consequuntur Dicta"
+  },
+  {
+    "id": 3,
+    "title": "Totam Similique Quidem"
+  },
+  {
+    "id": 4,
+    "title": "Enim Perferendis Praesentium"
+  },
+  {
+    "id": 5,
+    "title": "Neque Quidem Est"
+  },
+  {
+    "id": 6,
+    "title": "Cumque Cumque Fuga"
+  },
+  {
+    "id": 7,
+    "title": "Beatae Voluptatem Voluptas"
+  },
+  {
+    "id": 8,
+    "title": "Dolor Laudantium Quia"
+  },
+  {
+    "id": 9,
+    "title": "Sint Libero Sint"
+  },
+  {
+    "id": 10,
+    "title": "Placeat Excepturi Ad"
+  }
+]

+ 4 - 0
cypress/fixtures/demo-auth-credentials.json

@@ -0,0 +1,4 @@
+{
+  "email": "demo@refine.dev",
+  "password": "demodemo"
+}

+ 231 - 0
cypress/fixtures/hasura-blog-posts.json

@@ -0,0 +1,231 @@
+{
+  "data": {
+    "blog_posts": [
+      {
+        "id": "1",
+        "title": "Sed ante. Vivamus tortor. Duis mattis egestas metus.",
+        "category": {
+          "id": "1",
+          "title": "neque vestibulum"
+        },
+        "category_id": "1",
+        "content": "Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "2",
+        "title": "Phasellus sit amet erat.",
+        "category": {
+          "id": "2",
+          "title": "enim lorem ipsum"
+        },
+        "category_id": "2",
+        "content": "Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "3",
+        "title": "Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus.",
+        "category": {
+          "id": "3",
+          "title": "vivamus vel"
+        },
+        "category_id": "3",
+        "content": "Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\n\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "4",
+        "title": "Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.",
+        "category": {
+          "id": "4",
+          "title": "aaaaaaaaaarhoncus"
+        },
+        "category_id": "4",
+        "content": "Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "5",
+        "title": "In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",
+        "category": {
+          "id": "5",
+          "title": "turpis adipiscing lorem"
+        },
+        "category_id": "5",
+        "content": "Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\n\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "6",
+        "title": "Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.",
+        "category": {
+          "id": "6",
+          "title": "aaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+        },
+        "category_id": "6",
+        "content": "Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\n\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "7",
+        "title": "Etiam pretium iaculis justo.",
+        "category": {
+          "id": "7",
+          "title": "test category"
+        },
+        "category_id": "7",
+        "content": "In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "8",
+        "title": "Pellentesque at nulla. Suspendisse potenti.",
+        "category": {
+          "id": "8",
+          "title": "vulputate elementum nullam 2"
+        },
+        "category_id": "8",
+        "content": "Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "9",
+        "title": "Integer ac neque. Duis bibendum. Morbi non quam nec dui luctus rutrum.",
+        "category": {
+          "id": "9",
+          "title": "lacinia eget tincidunt"
+        },
+        "category_id": "9",
+        "content": "Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "10",
+        "title": "Aliquam non mauris. Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet.",
+        "category": {
+          "id": "10",
+          "title": "aliquam erat volutpat"
+        },
+        "category_id": "10",
+        "content": "In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "11",
+        "title": "Praesent lectus.",
+        "category": {
+          "id": "1",
+          "title": "aaaaaaaaaarhoncus"
+        },
+        "category_id": "1",
+        "content": "Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\n\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "12",
+        "title": "Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis. Fusce posuere felis sed lacus.",
+        "category": {
+          "id": "2",
+          "title": "vulputate elementum nullam 2"
+        },
+        "category_id": "2",
+        "content": "Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\n\nSed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "13",
+        "title": "Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.",
+        "category": {
+          "id": "3",
+          "title": "sed accumsan felis"
+        },
+        "category_id": "3",
+        "content": "Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\n\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "14",
+        "title": "Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.",
+        "category": {
+          "id": "4",
+          "title": "enim lorem ipsum"
+        },
+        "category_id": "4",
+        "content": "Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\n\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "15",
+        "title": "In hac habitasse platea dictumst. Maecenas ut massa quis augue luctus tincidunt.",
+        "category": {
+          "id": "5",
+          "title": "vulputate elementum nullam 2"
+        },
+        "category_id": "5",
+        "content": "Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\n\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "16",
+        "title": "Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo.",
+        "category": {
+          "id": "6",
+          "title": "blandit ultrices enim"
+        },
+        "category_id": "6",
+        "content": "Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "17",
+        "title": "Praesent lectus.",
+        "category": {
+          "id": "7",
+          "title": "amet turpis elementum"
+        },
+        "category_id": "7",
+        "content": "In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "18",
+        "title": "Proin risus. Praesent lectus.",
+        "category": {
+          "id": "8",
+          "title": "ipsum primis in"
+        },
+        "category_id": "8",
+        "content": "Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\n\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "19",
+        "title": "Integer non velit.",
+        "category": {
+          "id": "9",
+          "title": "lorem integer tincidunt"
+        },
+        "category_id": "9",
+        "content": "Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\n\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\n\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      },
+      {
+        "id": "20",
+        "title": "Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci.",
+        "category": {
+          "id": "10",
+          "title": "HK"
+        },
+        "category_id": "10",
+        "content": "Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\n\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\n\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.",
+        "created_at": "2023-03-20T09:57:27.548697+00:00"
+      }
+    ],
+    "blog_posts_aggregate": {
+      "aggregate": {
+        "count": 20
+      }
+    }
+  }
+}

+ 251 - 0
cypress/fixtures/hasura-blog-posts.ts

@@ -0,0 +1,251 @@
+export const hasuraBlogPosts = {
+    data: {
+        blog_posts: [
+            {
+                id: "1",
+                title: "Sed ante. Vivamus tortor. Duis mattis egestas metus.",
+                category: {
+                    id: "1",
+                    title: "neque vestibulum",
+                },
+                category_id: "1",
+                content:
+                    "Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "2",
+                title: "Phasellus sit amet erat.",
+                category: {
+                    id: "2",
+                    title: "enim lorem ipsum",
+                },
+                category_id: "2",
+                content:
+                    "Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "3",
+                title: "Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus.",
+                category: {
+                    id: "3",
+                    title: "vivamus vel",
+                },
+                category_id: "3",
+                content:
+                    "Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\n\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "4",
+                title: "Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.",
+                category: {
+                    id: "4",
+                    title: "aaaaaaaaaarhoncus",
+                },
+                category_id: "4",
+                content:
+                    "Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "5",
+                title: "In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",
+                category: {
+                    id: "5",
+                    title: "turpis adipiscing lorem",
+                },
+                category_id: "5",
+                content:
+                    "Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\n\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "6",
+                title: "Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.",
+                category: {
+                    id: "6",
+                    title: "aaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+                },
+                category_id: "6",
+                content:
+                    "Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\n\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "7",
+                title: "Etiam pretium iaculis justo.",
+                category: {
+                    id: "7",
+                    title: "test category",
+                },
+                category_id: "7",
+                content:
+                    "In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "8",
+                title: "Pellentesque at nulla. Suspendisse potenti.",
+                category: {
+                    id: "8",
+                    title: "vulputate elementum nullam 2",
+                },
+                category_id: "8",
+                content:
+                    "Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "9",
+                title: "Integer ac neque. Duis bibendum. Morbi non quam nec dui luctus rutrum.",
+                category: {
+                    id: "9",
+                    title: "lacinia eget tincidunt",
+                },
+                category_id: "9",
+                content:
+                    "Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "10",
+                title: "Aliquam non mauris. Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet.",
+                category: {
+                    id: "10",
+                    title: "aliquam erat volutpat",
+                },
+                category_id: "10",
+                content:
+                    "In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "11",
+                title: "Praesent lectus.",
+                category: {
+                    id: "1",
+                    title: "aaaaaaaaaarhoncus",
+                },
+                category_id: "1",
+                content:
+                    "Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\n\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "12",
+                title: "Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis. Fusce posuere felis sed lacus.",
+                category: {
+                    id: "2",
+                    title: "vulputate elementum nullam 2",
+                },
+                category_id: "2",
+                content:
+                    "Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\n\nSed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "13",
+                title: "Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.",
+                category: {
+                    id: "3",
+                    title: "sed accumsan felis",
+                },
+                category_id: "3",
+                content:
+                    "Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\n\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "14",
+                title: "Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.",
+                category: {
+                    id: "4",
+                    title: "enim lorem ipsum",
+                },
+                category_id: "4",
+                content:
+                    "Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\n\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "15",
+                title: "In hac habitasse platea dictumst. Maecenas ut massa quis augue luctus tincidunt.",
+                category: {
+                    id: "5",
+                    title: "vulputate elementum nullam 2",
+                },
+                category_id: "5",
+                content:
+                    "Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\n\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "16",
+                title: "Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo.",
+                category: {
+                    id: "6",
+                    title: "blandit ultrices enim",
+                },
+                category_id: "6",
+                content:
+                    "Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "17",
+                title: "Praesent lectus.",
+                category: {
+                    id: "7",
+                    title: "amet turpis elementum",
+                },
+                category_id: "7",
+                content:
+                    "In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "18",
+                title: "Proin risus. Praesent lectus.",
+                category: {
+                    id: "8",
+                    title: "ipsum primis in",
+                },
+                category_id: "8",
+                content:
+                    "Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\n\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "19",
+                title: "Integer non velit.",
+                category: {
+                    id: "9",
+                    title: "lorem integer tincidunt",
+                },
+                category_id: "9",
+                content:
+                    "Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\n\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\n\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+            {
+                id: "20",
+                title: "Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci.",
+                category: {
+                    id: "10",
+                    title: "HK",
+                },
+                category_id: "10",
+                content:
+                    "Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\n\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\n\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.",
+                created_at: "2023-03-20T09:57:27.548697+00:00",
+            },
+        ],
+        blog_posts_aggregate: {
+            aggregate: {
+                count: 20,
+            },
+        },
+    },
+};

+ 61 - 0
cypress/fixtures/hasura-categories.json

@@ -0,0 +1,61 @@
+{
+  "data": {
+    "categories": [
+      {
+        "id": "1",
+        "title": "aliquam erat volutpat",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      },
+      {
+        "id": "2",
+        "title": "volutpat in congue",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      },
+      {
+        "id": "3",
+        "title": "enim lorem ipsum",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      },
+      {
+        "id": "4",
+        "title": "ornare consequat lectus in",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      },
+      {
+        "id": "5",
+        "title": "vestibulum rutrum rutrum",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      },
+      {
+        "id": "6",
+        "title": "blandit ultrices enim",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      },
+      {
+        "id": "7",
+        "title": "lacinia eget tincidunt",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      },
+      {
+        "id": "8",
+        "title": "amet turpis elementum",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      },
+      {
+        "id": "9",
+        "title": "sed accumsan felis",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      },
+      {
+        "id": "10",
+        "title": "ipsum primis in",
+        "created_at": "2022-12-26T08:10:16.962584+00:00"
+      }
+    ],
+    "categories_aggregate": {
+      "aggregate": {
+        "count": 10
+      }
+    }
+  }
+}

+ 61 - 0
cypress/fixtures/hasura-categories.ts

@@ -0,0 +1,61 @@
+export const hasuraCategories = {
+    data: {
+        categories: [
+            {
+                id: "1",
+                title: "aliquam erat volutpat",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+            {
+                id: "2",
+                title: "volutpat in congue",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+            {
+                id: "3",
+                title: "enim lorem ipsum",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+            {
+                id: "4",
+                title: "ornare consequat lectus in",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+            {
+                id: "5",
+                title: "vestibulum rutrum rutrum",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+            {
+                id: "6",
+                title: "blandit ultrices enim",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+            {
+                id: "7",
+                title: "lacinia eget tincidunt",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+            {
+                id: "8",
+                title: "amet turpis elementum",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+            {
+                id: "9",
+                title: "sed accumsan felis",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+            {
+                id: "10",
+                title: "ipsum primis in",
+                created_at: "2022-12-26T08:10:16.962584+00:00",
+            },
+        ],
+        categories_aggregate: {
+            aggregate: {
+                count: 10,
+            },
+        },
+    },
+};

+ 4 - 0
cypress/fixtures/keycloak-credentials.json

@@ -0,0 +1,4 @@
+{
+  "email": "refine",
+  "password": "refine"
+}

+ 5 - 0
cypress/fixtures/mock-post.json

@@ -0,0 +1,5 @@
+{
+  "title": "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
+  "content": "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
+  "status": "Published"
+}

+ 167 - 0
cypress/fixtures/posts.json

@@ -0,0 +1,167 @@
+[
+  {
+    "id": 1,
+    "title": "Ut Voluptatem Est",
+    "content": "Repellendus temporibus provident nobis. Non adipisci quod et est dolorem sed qui. A ut omnis. Et perspiciatis quibusdam maiores aliquid est fugit nam odit. Aut aliquam consectetur deleniti commodi velit. Eum eum aperiam voluptate quos quo. Ut quia doloribus a. Molestiae non est fugit enim fugiat non ea quas accusamus. Consequuntur voluptatem nesciunt dolorum expedita optio deserunt. Illo dolorem et similique.",
+    "category": {
+      "id": 1
+    },
+    "status": "published",
+    "createdAt": "2022-06-12T11:03:09.829Z",
+    "slug": "ut-voluptatem-est"
+  },
+  {
+    "id": 2,
+    "title": "Sequi Quod Repellendus",
+    "content": "Odit natus dolor consequatur tempore recusandae exercitationem. Unde dolores aut. Dolor ipsam quam quis modi sint. Dolor itaque voluptatum non qui ratione nisi ullam assumenda. Nisi et aut incidunt fuga aut deserunt. Labore sit ut quia vero vel et sed suscipit. Voluptatum maiores adipisci vel molestiae minima in enim et. Repellendus autem quis nisi vero vel consectetur laborum. Similique quos voluptates officiis velit. Sapiente nihil ut ipsa autem.",
+    "category": {
+      "id": 2
+    },
+    "status": "published",
+    "createdAt": "2021-10-11T08:03:00.155Z",
+    "slug": "sequi-quod-repellendus"
+  },
+  {
+    "id": 3,
+    "title": "Ad Enim Blanditiis",
+    "content": "Illo et facilis deleniti voluptatem neque et optio. Eum rerum distinctio dolor omnis sequi expedita dicta reprehenderit quaerat. In eaque odio dicta ullam cumque qui neque molestias. Libero vitae quo pariatur tempore. Excepturi fuga pariatur est sint quasi. Expedita reiciendis laboriosam explicabo ratione quia laboriosam. Alias ratione ea. Minus ut nisi ad ex eligendi id vero soluta unde. Aut aut placeat reprehenderit voluptatem ut. Voluptatibus laborum modi ducimus.",
+    "category": {
+      "id": 1
+    },
+    "status": "rejected",
+    "createdAt": "2022-04-16T16:34:03.319Z",
+    "slug": "ad-enim-blanditiis"
+  },
+  {
+    "id": 4,
+    "title": "Dolores Modi Natus",
+    "content": "Magnam pariatur ut ab accusamus minus labore commodi. Excepturi natus hic aut illum mollitia. Ipsum incidunt adipisci rerum veritatis quae rerum. Ipsum blanditiis quisquam ea quia et in. Exercitationem rerum in. Ut quo quia et esse expedita consequatur sint culpa voluptas. Sed optio voluptas totam id aut odio voluptatum sequi. Velit aut ea esse alias voluptatem omnis numquam aut eligendi. Voluptatem natus suscipit. Praesentium enim reiciendis nihil delectus eum totam fugit accusantium fuga.",
+    "category": {
+      "id": 2
+    },
+    "status": "published",
+    "createdAt": "2021-05-20T02:19:56.513Z",
+    "slug": "dolores-modi-natus"
+  },
+  {
+    "id": 5,
+    "title": "Repellendus Aspernatur Porro",
+    "content": "Blanditiis voluptas suscipit harum. Nobis aspernatur quas. Asperiores optio id iste quis sint in modi. Ea explicabo velit numquam sed natus sed aliquam. Modi sapiente expedita suscipit neque reprehenderit qui et officiis. Quis et iusto quis veniam. Ut quis quidem incidunt adipisci non et ad ut. Quis quo illum nemo. Totam esse ut quisquam dolorum explicabo. Ea mollitia sed eos ducimus saepe ratione et doloremque.",
+    "category": {
+      "id": 3
+    },
+    "status": "rejected",
+    "createdAt": "2022-01-09T07:01:11.195Z",
+    "slug": "repellendus-aspernatur-porro"
+  },
+  {
+    "id": 6,
+    "title": "Natus Ut Distinctio",
+    "content": "Sit ut eos nulla. Assumenda ut aut aspernatur placeat rerum numquam quasi quos. Aut nihil ratione assumenda quos voluptatem. Repudiandae excepturi vel qui vel facilis non blanditiis asperiores. Et nesciunt consequatur natus incidunt sit accusantium maxime quo. Qui modi maxime est est aut ipsam et illo nihil. Voluptas enim quod tempora sit eveniet atque. Consectetur aperiam quisquam quis rerum. Maiores repellendus dolor et earum consequuntur sit qui dolor minima. Sint vitae veritatis qui consequuntur nemo impedit et.",
+    "category": {
+      "id": 4
+    },
+    "status": "published",
+    "createdAt": "2023-02-01T15:32:15.719Z",
+    "slug": "natus-ut-distinctio"
+  },
+  {
+    "id": 7,
+    "title": "Vel Labore Sint",
+    "content": "Quasi magni asperiores laborum accusantium debitis in voluptas quia. Velit mollitia earum magnam maxime aut. Enim at assumenda quidem vel libero facilis vel. Enim voluptate maiores. Quia ut odio fugit tempore iste qui et. Sit eaque voluptate omnis dolorem perspiciatis deleniti non qui assumenda. Possimus et rerum nam possimus inventore dolorem corrupti earum. Architecto consequuntur et cupiditate. Molestiae nisi facere qui ut saepe quis. Eius adipisci tempore itaque sed odio veniam harum quia.",
+    "category": {
+      "id": 5
+    },
+    "status": "rejected",
+    "createdAt": "2022-02-27T09:01:32.679Z",
+    "slug": "vel-labore-sint"
+  },
+  {
+    "id": 8,
+    "title": "Minus Nulla Molestias",
+    "content": "Qui velit dolorem fuga ad quas omnis harum. Ipsam sint ipsam quos nam sed adipisci. Aut adipisci asperiores tenetur laborum accusantium sint atque. Optio delectus fugiat iusto ut consequatur non delectus sit. Aut corrupti est sequi rerum asperiores fuga aut velit laborum. Quaerat magnam fuga dolores. Vel tenetur neque sapiente. Soluta quia autem delectus eos eveniet. Qui quis et velit molestiae enim qui explicabo dolor. Sit placeat molestias fugiat optio repellat voluptas.",
+    "category": {
+      "id": 6
+    },
+    "status": "rejected",
+    "createdAt": "2022-11-07T12:30:27.331Z",
+    "slug": "minus-nulla-molestias"
+  },
+  {
+    "id": 9,
+    "title": "Quibusdam Ut Natus",
+    "content": "Minus nam fugiat veritatis sequi ut. Iure nisi hic exercitationem rerum tempore earum. Rerum velit nemo est omnis nulla consequatur excepturi aut. Iusto ut autem porro commodi non. Et excepturi et aliquid alias qui minima nostrum deleniti. Ut qui consequatur voluptatem. Recusandae eos et odit neque. Non quaerat voluptatem molestiae necessitatibus. Et omnis dolore aperiam delectus. Rerum et et libero.",
+    "category": {
+      "id": 7
+    },
+    "status": "draft",
+    "createdAt": "2021-12-18T05:22:45.857Z",
+    "slug": "quibusdam-ut-natus"
+  },
+  {
+    "id": 10,
+    "title": "Nam Voluptas Ipsum",
+    "content": "Eum quaerat nostrum nulla consequatur quia quod autem minus. Quis omnis corrupti qui dolores. Non soluta suscipit. Ut distinctio eius consequuntur unde ab vel nobis sint nostrum. Natus omnis omnis debitis. Porro sit rerum vitae officiis dolorem veniam occaecati amet. Vitae et facilis provident enim et perferendis dolorum voluptatum. Fugiat provident explicabo ut libero aperiam dolore voluptatem. Et reiciendis error cupiditate blanditiis ea velit in incidunt. Doloribus et quis totam.",
+    "category": {
+      "id": 8
+    },
+    "status": "rejected",
+    "createdAt": "2021-11-21T05:29:05.942Z",
+    "slug": "nam-voluptas-ipsum"
+  },
+  {
+    "id": 11,
+    "title": "Nam Placeat Expedita",
+    "content": "Et iusto vel aut animi mollitia. Nesciunt nobis sunt et dolore soluta. Dolore doloremque qui vero ut. Aperiam maiores ea. Consequuntur omnis eos. Ipsam reprehenderit cupiditate maiores est fuga dolorum repellendus quisquam dolorem. Consequatur ea reprehenderit nulla quia occaecati laboriosam. Maiores dicta in tempora. Tempora et adipisci aspernatur est. Ipsum et molestiae illum fugiat.",
+    "category": {
+      "id": 9
+    },
+    "status": "rejected",
+    "createdAt": "2023-05-05T19:52:59.535Z",
+    "slug": "nam-placeat-expedita"
+  },
+  {
+    "id": 12,
+    "title": "Rerum Voluptatem Sunt",
+    "content": "Voluptas et cumque numquam. Suscipit eum cumque cupiditate vel possimus voluptatibus est ut. Optio molestiae nisi officiis sint sed vel provident accusantium. Omnis voluptatum officiis voluptatem numquam necessitatibus quisquam. A sit quae numquam numquam est. Aut in placeat. Modi quod et quaerat autem nemo. Amet in laudantium. Quis adipisci facere. Sed suscipit labore.",
+    "category": {
+      "id": 4
+    },
+    "status": "rejected",
+    "createdAt": "2021-06-26T00:07:45.503Z",
+    "slug": "rerum-voluptatem-sunt"
+  },
+  {
+    "id": 13,
+    "title": "In Sequi Recusandae",
+    "content": "Quia sit nulla occaecati enim. Totam rerum delectus. Quidem dolorum a rerum odio. Et molestias magnam amet sint soluta nemo. Dolore incidunt voluptatum quo recusandae quis consectetur est rerum nobis. Sit facere dignissimos libero et sint unde nam atque sunt. Tenetur facilis eos animi animi possimus. Qui maxime ut eum. Quas accusantium non rerum accusantium id soluta. Illum fugiat similique cumque.",
+    "category": {
+      "id": 2
+    },
+    "status": "draft",
+    "createdAt": "2021-10-02T13:20:44.138Z",
+    "slug": "in-sequi-recusandae"
+  },
+  {
+    "id": 14,
+    "title": "Impedit Maiores Consequatur",
+    "content": "Quibusdam sed incidunt assumenda. Nulla unde optio. Reiciendis placeat provident aut culpa aut nam et quis dolores. Ipsa velit non enim est earum quae velit totam corporis. Quod unde recusandae. Dolores et sed vel illo perferendis vitae sunt et. Cumque voluptatem dolores aut illum. Qui voluptates id ut est maxime adipisci rerum. Enim porro quo repudiandae dolore enim incidunt iste. Perferendis adipisci dicta harum.",
+    "category": {
+      "id": 3
+    },
+    "status": "draft",
+    "createdAt": "2021-06-06T15:12:04.543Z",
+    "slug": "impedit-maiores-consequatur"
+  },
+  {
+    "id": 15,
+    "title": "Deserunt Ut Enim",
+    "content": "Explicabo quaerat officia rerum nobis non sint. Fugit blanditiis ipsum et tempora iure eaque quo. Ratione sed voluptatem rerum ad illum veniam sint. Excepturi aliquam et. Dolor reprehenderit reiciendis quia voluptatem accusamus odio et et. Voluptatem maxime enim reprehenderit veritatis voluptatem maxime. Non quia adipisci molestiae dolorem voluptatum ipsam libero sed. Illo et voluptatem. Atque sit eos et fugit fugiat quia sed aliquam eveniet. Veniam nulla dolorem exercitationem blanditiis consequatur ipsam.",
+    "category": {
+      "id": 9
+    },
+    "status": "draft",
+    "createdAt": "2022-04-24T18:07:20.293Z",
+    "slug": "deserunt-ut-enim"
+  }
+]

+ 4 - 0
cypress/fixtures/strapi-v4-credentials.json

@@ -0,0 +1,4 @@
+{
+  "email": "demo@refine.dev",
+  "password": "demodemo"
+}

+ 4 - 0
cypress/fixtures/supabase-credentials.json

@@ -0,0 +1,4 @@
+{
+  "email": "info@refine.dev",
+  "password": "refine-supabase"
+}

+ 81 - 0
cypress/support/commands/antd/index.ts

@@ -0,0 +1,81 @@
+/// <reference types="cypress" />
+/// <reference types="../../index.d.ts" />
+
+export const getAntdNotification = () => {
+    return cy.get(".ant-notification-notice");
+};
+
+export const setAntdDropdown = ({
+    id,
+    selectIndex,
+}: ISetAntdDropdownParams) => {
+    return cy
+        .get(`#${id}`)
+        .click({ force: true })
+        .get(".ant-select-item-option")
+        .eq(selectIndex || 0)
+        .click({ force: true })
+        .get(`#${id}`)
+        .blur();
+};
+
+export const setAntdSelect = ({ id, value }: ISetAntdSelectParams) => {
+    return cy
+        .get(`#${id}`)
+        .click({ force: true })
+        .get(`.ant-select-item[title="${value}"]`)
+        .click({ force: true })
+        .get(`#${id}`)
+        .blur();
+};
+
+export const setAntdRangeDatePickerToToday = ({
+    id,
+}: ISetAntdRangeDatePickerToTodayParams) => {
+    return cy
+        .get(`#${id}`)
+        .click({ force: true })
+        .get(".ant-picker-cell-today")
+        .eq(0)
+        .click({ force: true })
+        .click({ force: true });
+};
+
+export const getAntdFormItemError = ({ id }: IGetAntdFormItemErrorParams) => {
+    return cy.get(`#${id}_help > .ant-form-item-explain-error`);
+};
+
+export const getAntdLoadingOverlay = () => {
+    return cy.get(`.ant-spin`);
+};
+
+export const getAntdPopoverDeleteButton = () => {
+    return cy.get(".ant-popconfirm-buttons button").contains(/delete/gi);
+};
+
+export const getAntdColumnSorter = (index: number) => {
+    return cy.get(".ant-table-column-sorters").eq(index);
+};
+
+export const getAntdFilterTrigger = (index: number) => {
+    return cy.get(".ant-table-filter-trigger").eq(index);
+};
+
+export const getAntdPaginationItem = (index: number) => {
+    return cy.get(`.ant-pagination-item-${index}`);
+};
+
+export const getTableRowExpandButton = (index: number) => {
+    return cy.get(".ant-table-row-expand-icon").eq(index);
+};
+
+export const fillAntdForm = () => {
+    cy.fixture("mock-post").then((mockPost) => {
+        cy.get("#title").clear();
+        cy.get("#title").type(mockPost.title);
+        cy.get("#content textarea").clear();
+        cy.get("#content textarea").type(mockPost.content);
+        cy.setAntdDropdown({ id: "category_id", selectIndex: 0 });
+        cy.setAntdSelect({ id: "status", value: mockPost.status });
+    });
+};

+ 44 - 0
cypress/support/commands/chakra-ui/index.ts

@@ -0,0 +1,44 @@
+export const getChakraUINotification = () => {
+    return cy.get(".chakra-alert");
+};
+
+export const getChakraUIToast = () => {
+    return cy.get(".chakra-toast");
+};
+
+export const getChakraUIFormItemError = ({
+    id,
+    type = "text",
+}: IGetChakraUIFormItemErrorParams) => {
+    if (type === "text") {
+        return cy.get(`#${id}`).siblings(".chakra-form__error-message");
+    }
+
+    if (type === "select") {
+        return cy
+            .get(`#${id}`)
+            .parent()
+            .parent()
+            .find(".chakra-form__error-message");
+    }
+
+    // type === "text"
+    return cy.get(`#${id}`).siblings(".chakra-form__error-message");
+};
+
+export const getChakraUIPopoverDeleteButton = () => {
+    return cy.get(".chakra-popover__body button").contains(/delete/gi);
+};
+
+export const getChakraUILoadingOverlay = () => {
+    return cy.get(".chakra-spinner");
+};
+
+export const fillChakraUIForm = () => {
+    cy.fixture("mock-post").then((mockPost) => {
+        cy.get("#title").clear().type(mockPost.title);
+        cy.get("#status").select(mockPost.status.toLowerCase());
+        cy.get("#categoryId").select(2);
+        cy.get("#content").clear().type(mockPost.content);
+    });
+};

+ 47 - 0
cypress/support/commands/document-title-handler.ts

@@ -0,0 +1,47 @@
+/// <reference types="cypress" />
+/// <reference types="../index.d.ts" />
+
+export const assertDocumentTitle = (resource: string, action?: IAction) => {
+    switch (action) {
+        case "list":
+            cy.document()
+                .its("title")
+                .should("match", new RegExp(`^${resource} | refine$`, "i"));
+            break;
+        case "edit":
+            cy.document()
+                .its("title")
+                .should(
+                    "match",
+                    new RegExp(`^#\\d+ Edit ${resource} | refine$`, "i"),
+                );
+            break;
+        case "show":
+            cy.document()
+                .its("title")
+                .should(
+                    "match",
+                    new RegExp(`^#\\d+ Show ${resource} | refine$`, "i"),
+                );
+            break;
+        case "create":
+            cy.document()
+                .its("title")
+                .should(
+                    "match",
+                    new RegExp(`^Create new ${resource} | refine$`, "i"),
+                );
+            break;
+        case "clone":
+            cy.document()
+                .its("title")
+                .should(
+                    "match",
+                    new RegExp(`^#\\d+ Clone ${resource} | refine$`, "i"),
+                );
+            break;
+        default:
+            cy.document().its("title").should("eq", `refine`);
+            break;
+    }
+};

+ 252 - 0
cypress/support/commands/intercepts/api-fake-rest.ts

@@ -0,0 +1,252 @@
+/// <reference types="cypress" />
+/// <reference types="../../index.d.ts" />
+
+import { getIdFromURL } from "../../../utils";
+
+const hostname = "api.fake-rest.refine.dev";
+
+Cypress.Commands.add("interceptGETPosts", () => {
+    return cy
+        .intercept(
+            {
+                method: "GET",
+                hostname: hostname,
+                pathname: "/posts",
+            },
+            {
+                fixture: "posts.json",
+            },
+        )
+        .as("getPosts");
+});
+
+Cypress.Commands.add("interceptGETPost", () => {
+    return cy
+        .fixture("posts")
+        .then((posts) => {
+            return cy.intercept(
+                {
+                    method: "GET",
+                    hostname: hostname,
+                    pathname: "/posts/*",
+                },
+
+                (req) => {
+                    const id = getIdFromURL(req.url);
+                    const post = posts.find((post) => post.id === id);
+
+                    if (!post) {
+                        req.reply(404, {});
+                        return;
+                    }
+
+                    req.reply(post);
+                },
+            );
+        })
+        .as("getPost");
+});
+
+Cypress.Commands.add("interceptPOSTPost", () => {
+    return cy.fixture("posts").then((posts) =>
+        cy
+            .intercept(
+                {
+                    method: "POST",
+                    hostname: hostname,
+                    pathname: "/posts",
+                },
+                (req) => {
+                    const merged = Object.assign({}, req.body, {
+                        id: posts.length + 1,
+                    });
+
+                    return req.reply(merged);
+                },
+            )
+            .as("postPost"),
+    );
+});
+
+Cypress.Commands.add("interceptPATCHPost", () => {
+    return cy
+        .fixture("posts")
+        .then((posts) => {
+            return cy.intercept(
+                {
+                    method: "PATCH",
+                    hostname: hostname,
+                    pathname: "/posts/*",
+                },
+
+                (req) => {
+                    const id = getIdFromURL(req.url);
+                    const post = posts.find((post) => post.id === id);
+
+                    if (!post) {
+                        return req.reply(404, {});
+                    }
+                    const merged = Object.assign({}, post, req.body);
+                    return req.reply(merged);
+                },
+            );
+        })
+        .as("patchPost");
+});
+
+Cypress.Commands.add("interceptDELETEPost", () => {
+    return cy
+        .intercept(
+            {
+                method: "DELETE",
+                hostname: hostname,
+                pathname: "/posts/*",
+            },
+            {},
+        )
+        .as("deletePost");
+});
+
+Cypress.Commands.add("interceptGETCategories", () => {
+    return cy
+        .intercept(
+            {
+                method: "GET",
+                hostname: hostname,
+                pathname: "/categories",
+            },
+            { fixture: "categories.json" },
+        )
+        .as("getCategories");
+});
+
+Cypress.Commands.add("interceptGETCategory", () => {
+    return cy
+        .fixture("categories")
+        .then((categories) => {
+            return cy.intercept(
+                {
+                    method: "GET",
+                    hostname: hostname,
+                    pathname: "/categories/*",
+                },
+
+                (req) => {
+                    const id = getIdFromURL(req.url);
+                    const category = categories.find(
+                        (category) => category.id.toString() === id.toString(),
+                    );
+
+                    if (!category) {
+                        req.reply(404, {});
+                        return;
+                    }
+
+                    req.reply(category);
+                },
+            );
+        })
+        .as("getCategory");
+});
+
+Cypress.Commands.add("interceptGETBlogPosts", () => {
+    return cy
+        .intercept(
+            {
+                method: "GET",
+                hostname: hostname,
+                pathname: "/blog_posts",
+            },
+            {
+                fixture: "blog-posts.json",
+            },
+        )
+        .as("getBlogPosts");
+});
+
+Cypress.Commands.add("interceptGETBlogPost", () => {
+    return cy
+        .fixture("blog-posts")
+        .then((posts) => {
+            return cy.intercept(
+                {
+                    method: "GET",
+                    hostname: hostname,
+                    pathname: "/blog_posts/*",
+                },
+
+                (req) => {
+                    const id = getIdFromURL(req.url);
+                    const post = posts.find((post) => post.id === id);
+
+                    if (!post) {
+                        req.reply(404, {});
+                        return;
+                    }
+
+                    req.reply(post);
+                },
+            );
+        })
+        .as("getBlogPost");
+});
+
+Cypress.Commands.add("interceptPOSTBlogPost", () => {
+    return cy.fixture("blog-posts").then((posts) =>
+        cy
+            .intercept(
+                {
+                    method: "POST",
+                    hostname: hostname,
+                    pathname: "/blog_posts",
+                },
+                (req) => {
+                    const merged = Object.assign({}, req.body, {
+                        id: posts.length + 1,
+                    });
+
+                    return req.reply(merged);
+                },
+            )
+            .as("postBlogPost"),
+    );
+});
+
+Cypress.Commands.add("interceptPATCHBlogPost", () => {
+    return cy
+        .fixture("blog-posts")
+        .then((posts) => {
+            return cy.intercept(
+                {
+                    method: "PATCH",
+                    hostname: hostname,
+                    pathname: "/blog_posts/*",
+                },
+
+                (req) => {
+                    const id = getIdFromURL(req.url);
+                    const post = posts.find((post) => post.id === id);
+
+                    if (!post) {
+                        return req.reply(404, {});
+                    }
+                    const merged = Object.assign({}, post, req.body);
+                    return req.reply(merged);
+                },
+            );
+        })
+        .as("patchBlogPost");
+});
+
+Cypress.Commands.add("interceptDELETEBlogPost", () => {
+    return cy
+        .intercept(
+            {
+                method: "DELETE",
+                hostname: hostname,
+                pathname: "/blog_posts/*",
+            },
+            {},
+        )
+        .as("deleteBlogPost");
+});

+ 151 - 0
cypress/support/commands/intercepts/hasura.ts

@@ -0,0 +1,151 @@
+import { CyHttpMessages } from "cypress/types/net-stubbing";
+import hasuraBlogPosts from "../../../fixtures/hasura-blog-posts.json";
+import hasuraCategories from "../../../fixtures/hasura-categories.json";
+
+type Operation = "get" | "getAll" | "create" | "update" | "delete";
+
+export const getOperation = (
+    req: CyHttpMessages.IncomingHttpRequest,
+): Operation | null => {
+    const query = req.body.query as string;
+
+    if (query.startsWith("query")) {
+        if (query.includes("aggregate") || query.includes("$where")) {
+            return "getAll";
+        }
+
+        if (query.includes("by_pk")) {
+            return "get";
+        }
+    }
+
+    if (query.startsWith("mutation")) {
+        if (query.includes("delete")) {
+            return "delete";
+        }
+
+        if (query.includes("$_set")) {
+            return "update";
+        }
+
+        if (query.includes("insert")) {
+            return "create";
+        }
+    }
+
+    return null;
+};
+
+const getResource = (req: CyHttpMessages.IncomingHttpRequest) => {
+    const query = req.body.query as string;
+
+    if (query.includes("blog_posts")) {
+        return "BlogPosts";
+    }
+
+    if (query.includes("categories")) {
+        return "Categories";
+    }
+
+    return null;
+};
+
+Cypress.Commands.add("interceptHasura", () => {
+    return cy.intercept(
+        {
+            method: "POST",
+            hostname: "flowing-mammal-24.hasura.app",
+            pathname: "/v1/graphql",
+        },
+        (req) => {
+            const body = req.body;
+            const operation = getOperation(req);
+            const resource = getResource(req);
+            const alias = `${operation}${resource}`;
+            console.log({ alias, body });
+
+            if (!operation || !resource) {
+                console.log("no operation or resource", {
+                    operation,
+                    resource,
+                });
+                return req.reply(404, {});
+            }
+
+            req.alias = alias;
+
+            if (resource === "BlogPosts") {
+                if (operation === "getAll") {
+                    return req.reply({
+                        data: hasuraBlogPosts.data,
+                    });
+                }
+
+                if (operation === "get") {
+                    const id = body.variables.id;
+                    const { category: _category, ...post } =
+                        hasuraBlogPosts.data.blog_posts.find(
+                            (post) => post.id === id,
+                        ) as any;
+
+                    if (!post) {
+                        return req.reply(404, {});
+                    }
+
+                    return req.reply({
+                        data: {
+                            blog_posts_by_pk: post,
+                        },
+                    });
+                }
+
+                if (operation === "update") {
+                    return req.reply({
+                        data: {
+                            update_blog_posts_by_pk: body.variables._set,
+                        },
+                    });
+                }
+
+                if (operation === "delete") {
+                    return req.reply({
+                        data: {
+                            delete_blog_posts_by_pk: {
+                                id: body.variables.id,
+                            },
+                        },
+                    });
+                }
+            }
+
+            if (resource === "Categories") {
+                if (operation === "getAll") {
+                    return req.reply({
+                        data: hasuraCategories.data,
+                    });
+                }
+
+                if (operation === "get") {
+                    const id = body.variables.id;
+                    const category = hasuraCategories.data.categories.find(
+                        (category) => category.id === id,
+                    );
+
+                    if (!category) {
+                        console.log("no category found", {
+                            id,
+                            category,
+                        });
+                        return req.reply(404, {});
+                    }
+
+                    return req.reply({
+                        data: {
+                            categories_by_pk: category,
+                        },
+                    });
+                }
+            }
+        },
+    );
+});

+ 5 - 0
cypress/support/commands/intercepts/index.ts

@@ -0,0 +1,5 @@
+// add commands to the Cypress chain
+import "./api-fake-rest";
+import "./supabase";
+import "./strapi-v4";
+import "./hasura";

+ 158 - 0
cypress/support/commands/intercepts/strapi-v4.ts

@@ -0,0 +1,158 @@
+/// <reference types="cypress" />
+/// <reference types="../../index.d.ts" />
+
+import { getIdFromURL } from "../../../utils";
+
+const hostname = "api.strapi-v4.refine.dev";
+const BASE_PATH = "/api";
+
+Cypress.Commands.add("interceptStrapiV4GETPosts", () => {
+    return cy
+        .intercept(
+            {
+                method: "GET",
+                hostname: hostname,
+                pathname: `${BASE_PATH}/posts`,
+            },
+            {
+                fixture: "posts.json",
+            },
+        )
+        .as("strapiV4GetPosts");
+});
+
+Cypress.Commands.add("interceptStrapiV4GETPost", () => {
+    return cy
+        .fixture("posts")
+        .then((posts) => {
+            return cy.intercept(
+                {
+                    method: "GET",
+                    hostname: hostname,
+                    pathname: `${BASE_PATH}/posts/*`,
+                },
+
+                (req) => {
+                    const id = getIdFromURL(req.url);
+                    const post = posts.find(
+                        (post) => post.id.toString() === id.toString(),
+                    );
+
+                    if (!post) {
+                        req.reply(404, {});
+                        return;
+                    }
+
+                    req.reply({
+                        data: post,
+                        meta: {},
+                    });
+                },
+            );
+        })
+        .as("strapiV4GetPost");
+});
+
+Cypress.Commands.add("interceptStrapiV4POSTPost", () => {
+    return cy.fixture("posts").then((posts) =>
+        cy
+            .intercept(
+                {
+                    method: "POST",
+                    hostname: hostname,
+                    pathname: `${BASE_PATH}/posts`,
+                },
+                (req) => {
+                    const merged = Object.assign({}, req.body, {
+                        id: posts.length + 1,
+                    });
+
+                    return req.reply(merged);
+                },
+            )
+            .as("strapiV4PostPost"),
+    );
+});
+
+Cypress.Commands.add("interceptStrapiV4PUTPost", () => {
+    return cy
+        .fixture("posts")
+        .then((posts) => {
+            return cy.intercept(
+                {
+                    method: "PUT",
+                    hostname: hostname,
+                    pathname: `${BASE_PATH}/posts/*`,
+                },
+
+                (req) => {
+                    const id = getIdFromURL(req.url);
+                    const post = posts.find((post) => post.id === id);
+
+                    if (!post) {
+                        return req.reply(404, {});
+                    }
+                    const merged = Object.assign({}, post, req.body);
+                    return req.reply(merged);
+                },
+            );
+        })
+        .as("strapiV4PutPost");
+});
+
+Cypress.Commands.add("interceptStrapiV4DELETEPost", () => {
+    return cy
+        .intercept(
+            {
+                method: "DELETE",
+                hostname: hostname,
+                pathname: `${BASE_PATH}/posts/*`,
+            },
+            {},
+        )
+        .as("strapiV4DeletePost");
+});
+
+Cypress.Commands.add("interceptStrapiV4GETCategories", () => {
+    return cy
+        .intercept(
+            {
+                method: "GET",
+                hostname: hostname,
+                pathname: `${BASE_PATH}/categories`,
+            },
+            { fixture: "categories.json" },
+        )
+        .as("strapiV4GetCategories");
+});
+
+Cypress.Commands.add("interceptStrapiV4GETCategory", () => {
+    return cy
+        .fixture("categories")
+        .then((categories) => {
+            return cy.intercept(
+                {
+                    method: "GET",
+                    hostname: hostname,
+                    pathname: `${BASE_PATH}/categories/*`,
+                },
+
+                (req) => {
+                    const id = getIdFromURL(req.url);
+                    const category = categories.find(
+                        (category) => category.id.toString() === id.toString(),
+                    );
+
+                    if (!category) {
+                        req.reply(404, {});
+                        return;
+                    }
+
+                    req.reply({
+                        data: category,
+                    });
+                },
+            );
+        })
+        .as("strapiV4GetCategory");
+});

+ 138 - 0
cypress/support/commands/intercepts/supabase.ts

@@ -0,0 +1,138 @@
+/// <reference types="cypress" />
+/// <reference types="../../index.d.ts" />
+
+import { ICategory, IPost } from "../../types";
+
+const HOSTNAME = "iwdfzvfqbtokqetmbmbp.supabase.co";
+const BASE_PATH = "/rest/v1";
+
+const getSupabaseIdFromQuery = (query?: Record<string, string | number>) => {
+    // supabase uses id in query like this {id: 'eq.1'}
+    return (query?.id as string)?.split(".")?.[1];
+};
+
+Cypress.Commands.add("interceptSupabaseGETPosts", () => {
+    // read posts and categories from fixtures
+    let posts: (IPost & {
+        categories: ICategory;
+    })[] = [];
+    let categories: ICategory[] = [];
+    cy.fixture("categories").then((categoriesFixture) => {
+        categories = categoriesFixture;
+    });
+    //  transform fixtures to match supabase response
+    cy.fixture("posts").then((rawPosts) => {
+        posts = rawPosts.map((post) => {
+            // in supabase, the category is not object, but in fixture it is
+            // because of that, we need to convert it to categoryId
+            return Object.assign({}, post, {
+                categoryId: post.category.id,
+                categories: categories.find(
+                    (category) => category.id === post.category.id,
+                ),
+            });
+        });
+    });
+
+    return cy
+        .intercept(
+            {
+                method: "GET",
+                hostname: HOSTNAME,
+                pathname: `${BASE_PATH}/posts`,
+            },
+
+            (req) => {
+                const id = getSupabaseIdFromQuery(req.query);
+                if (id) {
+                    const post = posts.find(
+                        (post) => post.id.toString() === id.toString(),
+                    );
+
+                    if (!post) {
+                        return req.reply(404, []);
+                    }
+
+                    return req.reply([post]);
+                }
+
+                return req.reply(posts);
+            },
+        )
+        .as("supabaseGetPosts");
+});
+
+Cypress.Commands.add("interceptSupabasePOSTPost", () => {
+    return cy.fixture("posts").then((posts) =>
+        cy
+            .intercept(
+                {
+                    method: "POST",
+                    hostname: HOSTNAME,
+                    pathname: `${BASE_PATH}/posts`,
+                },
+                (req) => {
+                    const merged = Object.assign({}, req.body, {
+                        id: posts.length + 1,
+                    });
+
+                    return req.reply(merged);
+                },
+            )
+            .as("supabasePostPost"),
+    );
+});
+
+Cypress.Commands.add("interceptSupabasePATCHPost", () => {
+    return cy
+        .fixture("posts")
+        .then((posts) => {
+            return cy.intercept(
+                {
+                    method: "PATCH",
+                    hostname: HOSTNAME,
+                    pathname: `${BASE_PATH}/posts`,
+                },
+
+                (req) => {
+                    const id = getSupabaseIdFromQuery(req.query);
+                    const post = posts.find(
+                        (post) => post.id.toString() === id.toString(),
+                    );
+
+                    if (!post) {
+                        return req.reply(404, {});
+                    }
+                    const merged = Object.assign({}, post, req.body);
+                    return req.reply(merged);
+                },
+            );
+        })
+        .as("supabasePatchPost");
+});
+
+Cypress.Commands.add("interceptSupabaseDELETEPost", () => {
+    return cy
+        .intercept(
+            {
+                method: "DELETE",
+                hostname: HOSTNAME,
+                pathname: `${BASE_PATH}/posts`,
+            },
+            {},
+        )
+        .as("supabaseDeletePost");
+});
+
+Cypress.Commands.add("interceptSupabaseGETCategories", () => {
+    return cy
+        .intercept(
+            {
+                method: "GET",
+                hostname: HOSTNAME,
+                pathname: `${BASE_PATH}/categories*`,
+            },
+            { fixture: "categories.json" },
+        )
+        .as("supabaseGetCategories");
+});

+ 30 - 0
cypress/support/commands/mantine/index.ts

@@ -0,0 +1,30 @@
+export const getMantineNotification = () => {
+    return cy.get(".mantine-Notification-description");
+};
+
+export const getMantinePopoverDeleteButton = () => {
+    return cy.get(".mantine-Popover-dropdown").contains(/delete/gi);
+};
+
+export const getMantineFormItemError = ({
+    id,
+}: IGetChakraUIFormItemErrorParams) => {
+    return cy.get(`#${id}-error`);
+};
+
+export const getMantineLoadingOverlay = () => {
+    return cy.get(".mantine-LoadingOverlay-root");
+};
+
+export const fillMantineForm = () => {
+    cy.fixture("mock-post").then((mockPost) => {
+        cy.get("#title").clear().type(mockPost.title);
+        cy.get("#content textarea")
+            .clear({ force: true })
+            .type(mockPost.content, {
+                delay: 32,
+            });
+        cy.get("#status").click().get("#status-0").click();
+        cy.get("#categoryId").clear().get("#categoryId-1").click();
+    });
+};

+ 37 - 0
cypress/support/commands/material-ui/index.ts

@@ -0,0 +1,37 @@
+/// <reference types="cypress" />
+/// <reference types="../../index.d.ts" />
+
+export const getMaterialUINotifications = () => {
+    return cy.get("#notistack-snackbar .MuiBox-root > h6");
+};
+
+export const getMaterialUIDeletePopoverButton = () => {
+    return cy.get(".MuiDialogActions-root > button").contains(/delete/i);
+};
+
+export const getMaterialUIFormItemError = ({
+    id,
+}: IGetMaterialUIFormItemErrorParams) => {
+    return cy.get(`#${id}-helper-text`);
+};
+
+export const getMaterialUILoadingCircular = () => {
+    return cy.get(".MuiCircularProgress-root");
+};
+
+export const getMaterialUIColumnHeader = (index: number) => {
+    return cy.get(`.MuiDataGrid-columnHeader[aria-colindex="${index}"]`);
+};
+
+export const fillMaterialUIForm = () => {
+    cy.fixture("mock-post").then((mockPost) => {
+        cy.get("#title").clear();
+        cy.get("#title").type(mockPost.title);
+        cy.get("#content").clear();
+        cy.get("#content").type(mockPost.content);
+        cy.get("#status").click();
+        cy.get("#status-option-0").click();
+        cy.get("#category").click();
+        cy.get("#category-option-0").click();
+    });
+};

+ 23 - 0
cypress/support/commands/refine/index.ts

@@ -0,0 +1,23 @@
+export const getSaveButton = () => {
+    return cy.get(".refine-save-button");
+};
+
+export const getCreateButton = () => {
+    return cy.get(".refine-create-button");
+};
+
+export const getDeleteButton = () => {
+    return cy.get(".refine-delete-button");
+};
+
+export const getEditButton = () => {
+    return cy.get(".refine-edit-button");
+};
+
+export const getShowButton = () => {
+    return cy.get(".refine-show-button");
+};
+
+export const getPageHeaderTitle = () => {
+    return cy.get(".refine-pageHeader-title");
+};

+ 265 - 0
cypress/support/commands/resource.ts

@@ -0,0 +1,265 @@
+/// <reference types="cypress" />
+/// <reference types="../index.d.ts" />
+
+const assertNotification = (ui: UITypes) => {
+    switch (ui) {
+        case "antd":
+            return cy.getAntdNotification().should("contain", "Success");
+        case "chakra-ui":
+            return cy.getChakraUINotification().should("contain", "Success");
+        case "mantine":
+            return cy.getMantineNotification().should("contain", "Success");
+        case "material-ui":
+            return cy.getMaterialUINotification().should("contain", "Success");
+    }
+};
+
+const waitLoadingOverlay = (ui: UITypes) => {
+    switch (ui) {
+        case "antd":
+            return cy.getAntdLoadingOverlay().should("not.exist");
+        case "chakra-ui":
+            return cy.getChakraUILoadingOverlay().should("not.exist");
+        case "mantine":
+            return cy.getMantineLoadingOverlay().should("not.exist");
+        case "material-ui":
+            return cy.getMaterialUILoadingCircular().should("not.exist");
+    }
+};
+
+const fillForm = (ui: UITypes) => {
+    switch (ui) {
+        case "antd":
+            return cy.fillAntdForm();
+        case "chakra-ui":
+            return cy.fillChakraUIForm();
+        case "mantine":
+            return cy.fillMantineForm();
+        case "material-ui":
+            return cy.fillMaterialUIForm();
+    }
+};
+
+const assertFormShouldHaveResponseValues = (response: any, ui: UITypes) => {
+    const body = response?.body;
+
+    // assert response values are equal to the form values
+    switch (ui) {
+        case "antd":
+            cy.get("#title").should("have.value", body?.title);
+            cy.get("#content textarea").should("have.value", body?.content);
+            cy.get("#status")
+                .parent()
+                .siblings()
+                .last()
+                .should(($status) => {
+                    return (
+                        $status.val()?.toString().toLowerCase() ===
+                        body?.status.toLowerCase()
+                    );
+                });
+            cy.get("#category_id")
+                .parent()
+                .siblings()
+                .last()
+                .should(($category_id) => {
+                    return (
+                        $category_id.val()?.toString().toLowerCase() ===
+                        body?.status.toLowerCase()
+                    );
+                });
+            break;
+        case "chakra-ui":
+            cy.get("#title").should("have.value", body?.title);
+            cy.get("#status").should("have.value", body?.status);
+            cy.get("#content").should("have.value", body?.content);
+            cy.get("#categoryId").should("have.value", body?.category?.id);
+            break;
+
+        case "mantine":
+            cy.get("#title").should("have.value", body?.title);
+            cy.get("#content textarea").should("have.value", body?.content);
+            cy.get("#status").should(($status) => {
+                return (
+                    $status.val()?.toString().toLowerCase() ===
+                    body?.status.toLowerCase()
+                );
+            });
+            cy.fixture("categories").then((categories) => {
+                const category = categories.find(
+                    (category) => category.id === body?.category?.id,
+                );
+                cy.get("#categoryId").should("have.value", category?.title);
+            });
+            break;
+
+        case "material-ui":
+            cy.get("#title").should("have.value", body?.title);
+            cy.get("#content").should("have.value", body?.content);
+            cy.get("#status").should("have.value", body?.status);
+            cy.fixture("categories").then((categories) => {
+                const category = categories.find(
+                    (category) => category.id === body?.category?.id,
+                );
+                cy.get("#category").should("have.value", category?.title);
+            });
+            break;
+    }
+};
+
+const assertSuccessResponse = (response: any, ui: UITypes) => {
+    const body = response?.body;
+
+    expect(response?.statusCode).to.eq(200);
+    expect(body).to.have.property("id");
+    expect(body).to.have.property("category");
+
+    cy.fixture("mock-post").then((mockPost) => {
+        expect(body?.title).to.eq(mockPost.title);
+        expect(body?.content).to.eq(mockPost.content);
+        expect(body?.status?.toLowerCase()).to.eq(
+            mockPost?.status?.toLowerCase(),
+        );
+    });
+
+    assertNotification(ui);
+    cy.location().should((loc) => {
+        expect(loc.pathname).to.eq("/posts");
+    });
+};
+
+export const list = () => {
+    cy.url().should("include", "/posts");
+    cy.getPageHeaderTitle().should("contain", "Posts");
+
+    cy.assertDocumentTitle("Posts", "list");
+};
+
+export const create = ({ ui }: IResourceCreateParams) => {
+    cy.interceptGETCategories();
+
+    cy.getCreateButton().click();
+    cy.wait("@getCategories");
+    cy.location("pathname").should("eq", "/posts/create");
+
+    cy.assertDocumentTitle("Post", "create");
+
+    fillForm(ui);
+
+    cy.interceptPOSTPost();
+    cy.getSaveButton().click();
+
+    cy.wait("@postPost").then((interception) => {
+        const response = interception?.response;
+        assertSuccessResponse(response, ui);
+    });
+};
+
+export const edit = ({ ui }: IResourceEditParams) => {
+    cy.interceptGETCategories();
+    cy.interceptGETPost();
+
+    // wait loading state and render to be finished
+    cy.wait("@getPosts");
+    waitLoadingOverlay(ui);
+
+    cy.getEditButton().first().click();
+    cy.wait("@getCategories");
+    cy.wait("@getPost").then((interception) => {
+        const getResponse = interception?.response;
+
+        // wait loading state and render to be finished
+        waitLoadingOverlay(ui);
+        cy.getSaveButton().should("not.be.disabled");
+        cy.location("pathname").should("include", "/posts/edit");
+
+        assertFormShouldHaveResponseValues(getResponse, ui);
+    });
+
+    cy.assertDocumentTitle("Post", "edit");
+
+    fillForm(ui);
+
+    cy.interceptPATCHPost();
+    cy.getSaveButton().click();
+
+    cy.wait("@patchPost").then((interception) => {
+        const response = interception?.response;
+        assertSuccessResponse(response, ui);
+    });
+};
+
+export const show = () => {
+    cy.interceptGETPost();
+    cy.interceptGETCategory();
+
+    cy.getShowButton().first().click();
+
+    cy.assertDocumentTitle("Post", "show");
+
+    cy.wait("@getPost").then((interception) => {
+        const response = interception?.response;
+
+        const id = response?.body?.id;
+        cy.location("pathname").should("include", `/posts/show/${id}`);
+
+        // should be visible id,title,content
+        ["Id", "Title", "Content"].forEach((field) => {
+            cy.get("body").should("contain", field);
+        });
+        // should be visible id,title,content values
+        const title = response?.body?.title;
+        const content = response?.body?.content;
+        [id, title, content].forEach((value) => {
+            cy.get("body").should("contain", value);
+        });
+    });
+
+    cy.wait("@getCategory").then((interception) => {
+        const response = interception?.response;
+
+        const category = response?.body;
+        cy.get("body").should("contain", category?.title);
+    });
+};
+
+export const resourceDelete = ({ ui }: IResourceDeleteParams) => {
+    cy.interceptGETCategories();
+    cy.wait("@getPosts");
+    waitLoadingOverlay(ui);
+
+    cy.interceptGETPost();
+    cy.getEditButton().first().click();
+
+    // wait loading state and render to be finished
+    cy.wait("@getPost");
+    waitLoadingOverlay(ui);
+    cy.getSaveButton().should("not.be.disabled");
+
+    cy.interceptDELETEPost();
+    cy.getDeleteButton().first().click();
+    switch (ui) {
+        case "antd":
+            cy.getAntdPopoverDeleteButton().click({ force: true });
+            break;
+        case "chakra-ui":
+            cy.getChakraUIPopoverDeleteButton().click({ force: true });
+            break;
+        case "mantine":
+            cy.getMantinePopoverDeleteButton().click({ force: true });
+            break;
+        case "material-ui":
+            cy.getMaterialUIDeletePopoverButton().click({ force: true });
+            break;
+    }
+
+    cy.wait("@deletePost").then((interception) => {
+        const response = interception?.response;
+
+        expect(response?.statusCode).to.eq(200);
+        assertNotification(ui);
+        cy.location().should((loc) => {
+            expect(loc.pathname).to.eq("/posts");
+        });
+    });
+};

+ 133 - 0
cypress/support/e2e.ts

@@ -0,0 +1,133 @@
+/// <reference types="cypress" />
+/// <reference types="./index.d.ts" />
+
+import {
+    getAntdNotification,
+    setAntdSelect,
+    setAntdDropdown,
+    getAntdFormItemError,
+    getAntdLoadingOverlay,
+    getAntdPopoverDeleteButton,
+    getAntdColumnSorter,
+    getAntdFilterTrigger,
+    getAntdPaginationItem,
+    getTableRowExpandButton,
+    setAntdRangeDatePickerToToday,
+    fillAntdForm,
+} from "./commands/antd";
+import {
+    getChakraUIPopoverDeleteButton,
+    getChakraUIFormItemError,
+    getChakraUILoadingOverlay,
+    getChakraUINotification,
+    getChakraUIToast,
+    fillChakraUIForm,
+} from "./commands/chakra-ui";
+import {
+    fillMantineForm,
+    getMantineFormItemError,
+    getMantineLoadingOverlay,
+    getMantineNotification,
+    getMantinePopoverDeleteButton,
+} from "./commands/mantine";
+import {
+    getCreateButton,
+    getDeleteButton,
+    getEditButton,
+    getPageHeaderTitle,
+    getSaveButton,
+    getShowButton,
+} from "./commands/refine";
+import { list, create, edit, show, resourceDelete } from "./commands/resource";
+import { assertDocumentTitle } from "./commands/document-title-handler";
+
+// add commands to the Cypress chain
+import "./commands/intercepts";
+import {
+    fillMaterialUIForm,
+    getMaterialUIColumnHeader,
+    getMaterialUIDeletePopoverButton,
+    getMaterialUIFormItemError,
+    getMaterialUILoadingCircular,
+    getMaterialUINotifications,
+} from "./commands/material-ui";
+
+Cypress.Keyboard.defaults({
+    keystrokeDelay: 0,
+});
+
+Cypress.config("defaultCommandTimeout", 20000);
+Cypress.config("requestTimeout", 20000);
+
+Cypress.Commands.add("assertDocumentTitle", assertDocumentTitle);
+
+Cypress.Commands.add("resourceList", list);
+Cypress.Commands.add("resourceCreate", create);
+Cypress.Commands.add("resourceEdit", edit);
+Cypress.Commands.add("resourceShow", show);
+Cypress.Commands.add("resourceDelete", resourceDelete);
+
+Cypress.Commands.add("getSaveButton", getSaveButton);
+Cypress.Commands.add("getCreateButton", getCreateButton);
+Cypress.Commands.add("getDeleteButton", getDeleteButton);
+Cypress.Commands.add("getEditButton", getEditButton);
+Cypress.Commands.add("getShowButton", getShowButton);
+Cypress.Commands.add("getPageHeaderTitle", getPageHeaderTitle);
+
+Cypress.Commands.add("fillAntdForm", fillAntdForm);
+Cypress.Commands.add("getAntdNotification", getAntdNotification);
+Cypress.Commands.add("setAntdSelect", setAntdSelect);
+Cypress.Commands.add("setAntdDropdown", setAntdDropdown);
+Cypress.Commands.add("getAntdFormItemError", getAntdFormItemError);
+Cypress.Commands.add("getAntdLoadingOverlay", getAntdLoadingOverlay);
+Cypress.Commands.add("getAntdPopoverDeleteButton", getAntdPopoverDeleteButton);
+Cypress.Commands.add("getAntdColumnSorter", getAntdColumnSorter);
+Cypress.Commands.add("getAntdFilterTrigger", getAntdFilterTrigger);
+Cypress.Commands.add("getAntdPaginationItem", getAntdPaginationItem);
+Cypress.Commands.add("getTableRowExpandButton", getTableRowExpandButton);
+Cypress.Commands.add(
+    "setAntdRangeDatePickerToToday",
+    setAntdRangeDatePickerToToday,
+);
+
+Cypress.Commands.add("fillChakraUIForm", fillChakraUIForm);
+Cypress.Commands.add("getChakraUINotification", getChakraUINotification);
+Cypress.Commands.add("getChakraUIToast", getChakraUIToast);
+Cypress.Commands.add("getChakraUIFormItemError", getChakraUIFormItemError);
+Cypress.Commands.add(
+    "getChakraUIPopoverDeleteButton",
+    getChakraUIPopoverDeleteButton,
+);
+Cypress.Commands.add("getChakraUILoadingOverlay", getChakraUILoadingOverlay);
+
+Cypress.Commands.add("getMaterialUINotification", getMaterialUINotifications);
+Cypress.Commands.add(
+    "getMaterialUIDeletePopoverButton",
+    getMaterialUIDeletePopoverButton,
+);
+Cypress.Commands.add("getMaterialUIFormItemError", getMaterialUIFormItemError);
+Cypress.Commands.add(
+    "getMaterialUILoadingCircular",
+    getMaterialUILoadingCircular,
+);
+Cypress.Commands.add("getMaterialUIColumnHeader", getMaterialUIColumnHeader);
+
+Cypress.Commands.add("fillMantineForm", fillMantineForm);
+Cypress.Commands.add("getMantineNotification", getMantineNotification);
+Cypress.Commands.add(
+    "getMantinePopoverDeleteButton",
+    getMantinePopoverDeleteButton,
+);
+Cypress.Commands.add("getMantineFormItemError", getMantineFormItemError);
+Cypress.Commands.add("getMantineLoadingOverlay", getMantineLoadingOverlay);
+Cypress.Commands.add("fillMaterialUIForm", fillMaterialUIForm);
+
+/**
+ * Disable telemetry calls
+ */
+beforeEach(() => {
+    cy.intercept("https://telemetry.refine.dev/**", {
+        body: "Disabled telemetry to avoid unwanted entries in the database",
+        statusCode: 200,
+    }).as("telemetry");
+});

+ 154 - 0
cypress/support/index.d.ts

@@ -0,0 +1,154 @@
+/// <reference types="cypress" />
+
+type UITypes = "antd" | "material-ui" | "chakra-ui" | "mantine";
+
+interface ISetAntdDropdownParams {
+    id: string;
+    selectIndex?: number;
+}
+
+interface ISetAntdSelectParams {
+    id: string;
+    value: string;
+}
+
+interface ISetAntdRangeDatePickerToTodayParams {
+    id: string;
+}
+
+interface IGetAntdFormItemErrorParams {
+    id: string;
+}
+
+interface IGetChakraUIFormItemErrorParams {
+    id: string;
+    type?: "text" | "select";
+}
+
+interface IGetMaterialUIFormItemErrorParams {
+    id: string;
+}
+
+interface IGetMantineFormItemErrorParams {
+    id: string;
+}
+
+interface IResourceCreateParams {
+    ui: UITypes;
+}
+
+interface IResourceEditParams {
+    ui: UITypes;
+}
+
+interface IResourceDeleteParams {
+    ui: UITypes;
+}
+
+type IAction = "list" | "edit" | "show" | "create" | "clone" | "default";
+
+declare namespace Cypress {
+    interface Chainable {
+        resourceList(): Chainable<void>;
+        resourceCreate(
+            params: IResourceCreateParams,
+        ): Chainable<JQuery<HTMLElement>>;
+        resourceEdit(
+            params: IResourceCreateParams,
+        ): Chainable<JQuery<HTMLElement>>;
+        resourceShow(): Chainable<void>;
+        resourceDelete(params: IResourceCreateParams): Chainable<void>;
+
+        assertDocumentTitle(
+            resource: string,
+            action?: IAction,
+        ): Chainable<void>;
+
+        getSaveButton(): Chainable<JQuery<HTMLElement>>;
+        getCreateButton(): Chainable<JQuery<HTMLElement>>;
+        getDeleteButton(): Chainable<JQuery<HTMLElement>>;
+        getEditButton(): Chainable<JQuery<HTMLElement>>;
+        getShowButton(): Chainable<JQuery<HTMLElement>>;
+        getPageHeaderTitle(): Chainable<JQuery<HTMLElement>>;
+
+        getAntdNotification(): Chainable<JQuery<HTMLElement>>;
+        getAntdLoadingOverlay(): Chainable<JQuery<HTMLElement>>;
+        getAntdPopoverDeleteButton(): Chainable<JQuery<HTMLElement>>;
+        getAntdColumnSorter(index: number): Chainable<JQuery<HTMLElement>>;
+        getAntdFilterTrigger(index: number): Chainable<JQuery<HTMLElement>>;
+        getAntdPaginationItem(index: number): Chainable<JQuery<HTMLElement>>;
+        getTableRowExpandButton(index: number): Chainable<JQuery<HTMLElement>>;
+        setAntdDropdown(
+            params: ISetAntdDropdownParams,
+        ): Chainable<JQuery<HTMLElement>>;
+        setAntdSelect(
+            params: ISetAntdSelectParams,
+        ): Chainable<JQuery<HTMLElement>>;
+        setAntdRangeDatePickerToToday(
+            params: ISetAntdRangeDatePickerToTodayParams,
+        ): Chainable<JQuery<HTMLElement>>;
+        getAntdFormItemError(
+            params: IGetAntdFormItemErrorParams,
+        ): Chainable<JQuery<HTMLElement>>;
+        fillAntdForm: () => void;
+
+        getChakraUINotification(): Chainable<JQuery<HTMLElement>>;
+        getChakraUIToast(): Chainable<JQuery<HTMLElement>>;
+        getChakraUIFormItemError(
+            params: IGetChakraUIFormItemErrorParams,
+        ): Chainable<JQuery<HTMLElement>>;
+        getChakraUIDeletePopoverButton(): Chainable<JQuery<HTMLElement>>;
+        getChakraUILoadingOverlay(): Chainable<JQuery<HTMLElement>>;
+        getChakraUIPopoverDeleteButton(): Chainable<JQuery<HTMLElement>>;
+        fillChakraUIForm: () => void;
+
+        getMantineNotification(): Chainable<JQuery<HTMLElement>>;
+        getMantinePopoverDeleteButton(): Chainable<JQuery<HTMLElement>>;
+        getMantineFormItemError(
+            params: IGetMantineFormItemErrorParams,
+        ): Chainable<JQuery<HTMLElement>>;
+        getMantineLoadingOverlay(): Chainable<JQuery<HTMLElement>>;
+        fillMantineForm: () => void;
+
+        getMaterialUINotification(): Chainable<JQuery<HTMLElement>>;
+        getMaterialUIDeletePopoverButton(): Chainable<JQuery<HTMLElement>>;
+        getMaterialUIFormItemError(
+            params: IGetChakraUIFormItemErrorParams,
+        ): Chainable<JQuery<HTMLElement>>;
+        getMaterialUILoadingCircular(): Chainable<JQuery<HTMLElement>>;
+        getMaterialUIColumnHeader(
+            index: number,
+        ): Chainable<JQuery<HTMLElement>>;
+        fillMaterialUIForm: () => void;
+
+        interceptGETBlogPost(): Chainable<null>;
+        interceptGETBlogPosts(): Chainable<null>;
+        interceptPOSTBlogPost(): Chainable<null>;
+        interceptPATCHBlogPost(): Chainable<null>;
+        interceptDELETEBlogPost(): Chainable<null>;
+
+        interceptGETPost(): Chainable<null>;
+        interceptGETPosts(): Chainable<null>;
+        interceptPOSTPost(): Chainable<null>;
+        interceptPATCHPost(): Chainable<null>;
+        interceptDELETEPost(): Chainable<null>;
+        interceptGETCategories(): Chainable<null>;
+        interceptGETCategory(): Chainable<null>;
+
+        interceptSupabaseGETPosts(): Chainable<null>;
+        interceptSupabasePOSTPost(): Chainable<null>;
+        interceptSupabasePATCHPost(): Chainable<null>;
+        interceptSupabaseDELETEPost(): Chainable<null>;
+        interceptSupabaseGETCategories(): Chainable<null>;
+
+        interceptStrapiV4GETPost(): Chainable<null>;
+        interceptStrapiV4GETPosts(): Chainable<null>;
+        interceptStrapiV4POSTPost(): Chainable<null>;
+        interceptStrapiV4PUTPost(): Chainable<null>;
+        interceptStrapiV4DELETEPost(): Chainable<null>;
+        interceptStrapiV4GETCategories(): Chainable<null>;
+        interceptStrapiV4GETCategory(): Chainable<null>;
+
+        interceptHasura(): Chainable<null>;
+    }
+}

+ 5 - 0
cypress/support/types/index.ts

@@ -0,0 +1,5 @@
+import posts from "../../fixtures/posts.json";
+import categories from "../../fixtures/categories.json";
+
+export type IPost = (typeof posts)[number];
+export type ICategory = (typeof categories)[number];

+ 4 - 0
cypress/utils/getIdFromURL/index.ts

@@ -0,0 +1,4 @@
+export const getIdFromURL = (url: string) => {
+    const id = Number(url.split("/")?.pop()?.split("?")?.[0]);
+    return id;
+};

+ 1 - 0
cypress/utils/index.ts

@@ -0,0 +1 @@
+export * from "./getIdFromURL";

+ 20 - 0
documentation/.gitignore

@@ -0,0 +1,20 @@
+# Dependencies
+/node_modules
+
+# Production
+/build
+
+# Generated files
+.docusaurus
+.cache-loader
+
+# Misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 3 - 0
documentation/11111versions.json

@@ -0,0 +1,3 @@
+[
+  "3.xx.xx"
+]

+ 8 - 0
documentation/111vercel.json

@@ -0,0 +1,8 @@
+{
+"git": {
+"deploymentEnabled": {
+"main": false
+}
+}
+}
+

+ 33 - 0
documentation/README.md

@@ -0,0 +1,33 @@
+# Website
+
+This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator.
+
+## Installation
+
+```console
+yarn install
+```
+
+## Local Development
+
+```console
+yarn start
+```
+
+This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server.
+
+## Build
+
+```console
+yarn build
+```
+
+This command generates static content into the `build` directory and can be served using any static contents hosting service.
+
+## Deployment
+
+```console
+GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy
+```
+
+If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

+ 3 - 0
documentation/babel.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
+};

+ 148 - 0
documentation/docs/admin/000_intro.md

@@ -0,0 +1,148 @@
+---
+sidebar_position: 1
+---
+
+# Get Started with OpenAdmin
+
+The OpenAdmin offers an administrator-level interface where you can efficiently handle tasks such as creating and managing users, setting up hosting plans, configuring backups, and editing OpenPanel settings.
+
+![openpanel vs openadmin](/img/admin/openadmin_vs_openpanel_what_is_the_difference.png)
+
+
+
+## Requirements
+
+Hardware Requirements
+
+| Operating system and version | Processor                                 | RAM                                 | Disk Space                             | Architecture |
+| ---------------------------- | -----------------------------------------| ----------------------------------- | -------------------------------------- | ------------ |
+| [Ubuntu 20.04](https://releases.ubuntu.com/22.04/) or newer            | Minimum: 1.1 GHz<br></br>Recommended: 2 GHz    | Minimum: 1 GB<br></br>Recommended: 4 GB   | Minimum: 12 GB<br></br>Recommended: 20 GB   | 64-bit       |
+
+
+
+Docker and any required Docker images will be installed for you.
+
+All users data resides in `/var` and we recommend, if your disk is partitioned, that this partition contains most of your available space.
+
+## Installation
+
+To install openpanel follow these steps: 
+
+1. Log in to your new server;
+- as root via SSH or
+- as a user with sudo privileges and type "sudo -i"
+2. Copy and paste openpanel installation command into the terminal
+```shell
+cd /root && (curl -sSL -o install https://get.openpanel.co || wget -qO install https://get.openpanel.co) && chmod +x install && ./install
+```
+
+:::info
+We recommend that you run the installation command within a Linux screen session. The Linux Screen utility allows you to create a shell session that will stay active through a network disruption.
+:::
+
+OpenPanel installation process will by default perform the following steps:
+- Check if your OS and available hardware resources meet the bare minimum system requirements
+- Check if existing hosting panel or webserver is already installed
+- Detect your OS and the package manager to be used for installation commands
+- Check if another OpenPanel installaiton is in progress or eas interrupted
+- Update and start installing required packages
+- Configure and start all required system services
+- Download required Docker images
+- Set domain, generate SSL, and configure services
+- Create hosting packages
+
+The installation script also supports optional flags that can be used to install additional services, skip certain installation steps or display debugging information:
+
+| Flag                | What it does                                                                                                      |
+|---------------------|-------------------------------------------------------------------------------------------------------------------|
+| `--with_modsec`     | Rebuild Nginx with ModSecurity module and set the OWASP core ruleset.                                                   |
+| `--debug`           | Display on screen each installation step and output debugging information.                                   |
+| `--skip-requirements`| Do not check if the server meets the minimum recommended requirements for running OpenPanel.                          |
+| `--skip-panel-check`| Do not check if other hosting panels or webservers are installed and overwrite.                                               |
+| `--repair`          | Do not check if OpenPanel installation is already running or previously failed and overwrite configuration.                     |
+| `--skip-firewall`   | Do not install UFW and skip setting custom firewall rules.                                                          |
+| `--skip-images`     | Skip building Docker images for Nginx and Apache.                                                                   |
+| `--skip-ssl`        | Do not check if the server hostname is pointed to the IP and set it to be used for OpenPanel; instead, the server IP will be used. |
+| `--skip-plans`      | Skip creating default Nginx and Apache hosting plans.                                                                                   |
+
+Example: Install OpenPanel in debug mode
+
+```shell
+cd /root && (curl -sSL -o install https://get.openpanel.co || wget -qO install https://get.openpanel.co) && chmod +x install && ./install --debug
+```
+
+
+:::info
+openpanel will install Docker, MySQL, Nginx, and several other tools on your server. You should install it on a fresh server, otherwise, you risk facing installation errors.
+:::
+
+:::warning
+Port 53, 80, 443 and 2083 must be available and not blocked by your hosting provider firewall.
+:::
+
+### Troubleshooting failed installation
+
+In a rare case that the OpenPanel installation process fails you shoud be able to determine the root cause from the error message alone.
+
+You can also run the installation process with the `--debug` flag and afterwards check the installation log file for errors:
+
+```bash
+cat /root/openpanel_install.log
+```
+
+:::tip
+In nearly 99.9% of instances, installation failures result from conflicts with residual services from a previous hosting panel or web server. If a web server was previously installed on the server, it is advisable to reinstall the operating system before attempting to install OpenPanel again.
+:::
+
+### Reporting bugs
+
+If you encountered any errors while running the installation script, and **you are able to again reproduce the error** on another server (or same after reinstalling the OS) then please copy & paste the installation log file to [the community forums](https://community.openpanel.co).
+
+
+## Post Install Steps
+
+- [access admin panel](/docs/admin/intro#access-adminpanel)
+- [set custom nameservers](/docs/admin/intro#set-nameservers)
+- [create a package](/docs/admin/plans/hosting_plans#create-a-plan)
+- [create a new user account](/docs/admin/users/openpanel#create-users)
+
+### Access AdminPanel
+
+Run `opencli admin` command to find the address on which AdminPanel is accessible. Example output:
+
+```bash
+root@server:/home# opencli admin
+● AdminPanel is running and is available on: https://server.openpanel.co:2087/
+```
+
+To login to admin panel you need a username and password.
+
+Default username for the main Administrator account is **admin** and password is random generated. To set a new password for the admin account run command: `opencli password NEW_PASSWORD_HERE`
+
+Example:
+```bash
+root@server:/home# opencli admin password ba63vfav7fq36vas
+Password for user 'admin' changed.
+
+===============================================================
+● AdminPanel is running and is available on: https://server.openpanel.co:2087/
+
+- username: admin
+- password: ba63vfav7fq36vas
+
+===============================================================
+```
+
+## Updates
+
+The panel will check for new updates nightly. If available, it will check your local update and patch preferences and update only when enabled.
+
+To enable or disable updates, navigate to OpenAdmin > General Settings and check or uncheck the 'Auto Updates' and 'Auto Patches'.
+
+![openadmin update preferences](/img/admin/openadmin_set_update_preferences.png)
+
+If you want to update manually regardless of the schedule, you can run the following command.
+
+```shell
+opencli update
+```

+ 86 - 0
documentation/docs/admin/001_dashboard.md

@@ -0,0 +1,86 @@
+---
+sidebar_position: 2
+---
+
+# Dashboard
+
+The OpenAdmin offers an administrator-level interface where you can efficiently handle tasks such as creating and managing users, setting up hosting plans, configuring backups, and editing OpenPanel settings.
+
+The dashboard page is the hub of the Admin interface and provides the overview of the current server performance, services and usage.
+
+![openadmin dashboard](/img/admin/openadmin_dashboard.png)
+
+On top of the pages, you can find the header that contains:
+
+- **Server name:** If brand name is set, display brand name; otherwise, show server IP address.
+- **Menu** to access the Users, Plans, Backups, and Settings pages.
+- **Links** to the official Documentation and our Community Forums.
+- **Dark mode toggle:** Allows you to switch between dark and light modes.
+- **Notifications indicator:** Displays important server-wide alerts.
+- **Avatar:** Indicates the admin username with options to edit the profile.
+
+
+The dashboard page contains 10 widgets:
+
+- **User** widget: Displays the total number of user accounts.
+- **Backups** widget: Shows the number of backup jobs and indicates backup errors if any.
+- **Plans** widget: Indicates the number of plans available.
+- **Load Averages** widget: Presents real-time server load with color indicators.
+- **Memory Usage** widget: Provides the current memory usage with color indicators.
+- **Activity** widget: Exhibits the latest activities of users and the Administrator.
+- **Server list** widget: Lists connected remote OpenPanel instances.
+- **CPU** widget: Illustrates real-time usage for each CPU core with color indicators.
+- **Services status** widget: Displays the status of relevant services and actions to control them.
+- **Disk usage** widget: Presents server partitions and disk usage for each partition.
+
+## Activity
+
+The OpenAdmin dashboard's activity widget shows a log of actions taken by OpenPanel users, with a focus on those performed by the Administrator. The log is organized from newest to oldest, and active users, who have taken actions in the last 30 minutes, are marked with a green dot on the right side. To view detailed activity information, click on the user icon (avatar) to open their user account page with the Activity tab.
+
+![openadmin dashboard activity widget](/img/admin/openadmin_dashboard_activity_widget.png)
+
+## Quick start guide
+
+Quick start guide highlights the recommended steps for the Administrator to perform uppon installing OpenPanel.
+
+![openadmin dashboard quick_start_guide](/img/admin/dashboard/quick_start_guide.png)
+
+These steps include:
+
+- Set domain name instead of IP address for accessing OpenPanel
+- Create a hosting plan and create a user account
+- Settings custom nameservers to be used for domains
+- Install and configure ModSecurity WAF for Nginx
+
+## Server List
+
+:::info 
+Note: This feature is still experimental and is not included in release versions, as it is not yet suitable for production.
+:::
+
+The Server List shows the current Docker server and connected remote servers where OpenPanel is installed. Install OpenPanel on multiple servers and connect them to manage all from a single OpenAdmin interface.
+
+## CPU
+
+The CPU usage percentage represents the amount of the CPU's processing power that is currently being utilized. It indicates how much of the CPU's capacity is in use at a specific moment. For example, a CPU usage of 50% means that the CPU is operating at half of its maximum processing capacity, while 100% usage indicates that the CPU is fully utilized, and there may be resource constraints or performance issues.
+
+The data is auto-refreshed every 1 second to provide real-time updates.
+
+![openadmin dashboard cpu widget](/img/admin/openadmin_dashboard_cpu_widget.png)
+
+## Services Status
+
+The Services Status widget displays a list of services managed by OpenPanel and enables you to check their current status, restart them, or start/stop when needed.
+
+![OpenAdmin Dashboard Services Widget](/img/admin/openadmin_dashboard_services_widget.png)
+
+The Admin service is excluded from the list since stopping that service will disable the OpenAdmin interface. To disable the admin panel, use the 'Disable Admin Panel' option from Settings > OpenAdmin.
+
+
+## Disk Usage
+
+The Disk Usage widget provides an overview of your system's disk usage. It displays information about each mounted disk partition, including details such as the device, mount point, filesystem type, and the amount of space used and available in a human-readable format (in gigabytes, GB or terabytes, TB). The 'Usage Percentage' column indicates the percentage of disk space currently in use.
+
+![openadmin dashboard disk widget](/img/admin/openadmin_dashboard_disk_widget.png)
+
+

+ 42 - 0
documentation/docs/admin/002_notifications.md

@@ -0,0 +1,42 @@
+---
+sidebar_position: 6
+---
+
+# Notifications
+
+Notifications are accesible from the notification icon in upper right corner. 
+
+![notifications center](/img/admin/notifications_center.png)
+
+
+OpenPanel records the following actions:
+
+- server reboot
+- service is inactive
+- update is available
+- high memory usage
+- high averageload 
+- high cpu usage
+- high disk usage
+
+To confirm receipt of a notification, select the checkmark icon located in front of it. Once a notification is confirmed, subsequent notifications of the same type will be logged if the issue persists. For instance, if a service is unavailable, the system will generate an initial notification. However, if you acknowledge the notification and the service remains unrecovered, the next time the check is executed, it will log another notification.
+
+Example notifications:
+
+On Server Reboot
+![reboot](/img/admin/dashboard/reboot.png)
+
+If service is inactive:
+![service](/img/admin/dashboard/service.png)
+
+If CPU usage is over a treshold:
+![cpu](/img/admin/dashboard/cpu.png)
+
+If new version of OpenPanel is available:
+![update](/img/admin/dashboard/update.png)
+
+If Memory usage is over a treshold:
+![ram](/img/admin/dashboard/ram.png)
+
+If system is running out of disk space:
+![disk](/img/admin/dashboard/disk.png)

+ 7 - 0
documentation/docs/admin/customize/_category_.json

@@ -0,0 +1,7 @@
+{
+  "label": "Advanced",
+  "position": 99,
+  "link": {
+    "type": "generated-index"
+  }
+}

+ 116 - 0
documentation/docs/admin/customize/api.md

@@ -0,0 +1,116 @@
+---
+sidebar_position: 9
+---
+
+# API
+
+## Basic Authentication
+
+OpenPanel API uses JWT tokens for authentication.
+
+### Generate access token
+
+Send POST request to /api with your username and password:
+
+```bash
+curl -X POST -H "Content-Type: application/json" -d '{"username":"USERNAME_HERE", "password":"PASSWORD_HERE"}' https://OPENPANEL:2087/api
+```
+Example response:
+  
+```json 
+{
+    "name": "string"
+}
+```
+
+### Check if token is valid
+
+```bash
+curl -H "Authorization: Bearer JWT_TOKEN" https://OPENPANEL:2087/api/whoami
+```
+
+Example response:
+```json 
+{
+  "logged_in_as": "user"
+}
+```
+
+## Users
+
+### List All Users
+
+```bash
+curl -H "Authorization: Bearer JWT_TOKEN" https://OPENPANEL:2087/api/users
+```
+
+Example response:
+```json
+{
+{
+  "users": [
+    [
+      12, 
+      "SUSPENDED_20240103122300_jasta", 
+      "pbkdf2:sha256:260000$G26Nw1zBQ6yywbiG$6a0d321490842b58068ad44e6e21b96e89f42cb6daecebe3afc06ad447ef3785", 
+      "jasta", 
+      "1,2,3,4,5,6,7,8,9,10,11,12", 
+      "", 
+      0, 
+      null, 
+      null, 
+      "Tue, 02 Jan 2024 19:44:25 GMT", 
+      2, 
+      "cloud_4_nginx"
+    ], 
+    [
+      13, 
+      "example_user", 
+      "pbkdf2:sha256:260000$EIkp3AkT9OBPDtr1$956594ccc6aaa2c24c528f98eacadee7ac35193578b32dc415b2402fa9a22595", 
+      "user@example.com", 
+      "1,2,3,4,5,6,7,8,9,10,11,12", 
+      "", 
+      0, 
+      null, 
+      null, 
+      "Tue, 09 Jan 2024 14:18:56 GMT", 
+      2, 
+      "cloud_4_apache"
+    ]
+  ]
+}
+}
+```
+
+### List Single User
+
+```bash
+curl -H "Authorization: Bearer JWT_TOKEN" https://OPENPANEL:2087/api/users/stefan
+```
+
+Example response:
+```json
+{
+{
+  "users": [
+    [
+      15, 
+      "stefan", 
+      "pbkdf2:sha256:260000$EIkp3AkT9OBPDtr1$956594ccc6aaa2c24c528f98eacadee7ac35193578b32dc415b2402fa9a22595", 
+      "user@example.com", 
+      "1,2,3,4,5,6,7,8,9,10,11,12", 
+      "", 
+      0, 
+      null, 
+      null, 
+      "Tue, 09 Jan 2024 14:18:56 GMT", 
+      2, 
+      "cloud_4_apache"
+    ]
+  ]
+}
+}
+```
+
+### Create User
+

+ 49 - 0
documentation/docs/admin/customize/localization.md

@@ -0,0 +1,49 @@
+---
+sidebar_position: 9
+---
+
+# Localization
+
+OpenPanel is localization ready and can easily be translated into any language.
+
+## Available Locales
+
+OpenPanel is shipped with the following locales:
+
+- English (EN)
+
+## How to translate
+
+To translate OpenPanel to another language, for example Serbian:
+
+1. Navigate to the OpenPanel directory using the "cd" command:
+```bash
+cd /usr/local/panel
+```
+
+2. Initialize a new translations directory for your locale:
+```bash
+pybabel init -i messages.pot -d translations -l sr
+```
+
+3. Enter the newly created locale directory and edit the messages.po file. For each `msgid` write the translation in the `msgtr` tag, for example:
+
+
+```bash title="/usr/local/panel/translations/sr/LC_MESSAGES/messages.po"
+#: templates/base.html:237
+msgid "Websites"
+msgstr "Sajtovi"
+``` 
+
+
+4. After you are finished translating, you need to compile the edited `.po` file to `.mo` in order to be used by the panel:
+
+```bash
+pybabel compile -d translations
+```
+
+Thats it, your new locale is added and ready to be used.
+
+:::info
+If you would like to share your translation with the OpenPanel community, please email the content of your locale folder `/usr/local/panel/translations/<LOCALE-HERE>/LC_MESSAGES/` to support@openpanel.co
+:::

+ 11 - 0
documentation/docs/admin/customize/troubleshooting.md

@@ -0,0 +1,11 @@
+---
+sidebar_position: 5
+---
+
+# Troubleshooting
+
+
+
+## Logging
+
+multitail /var/log/nginx/access.log -I /var/log/nginx/error.log

+ 7 - 0
documentation/docs/admin/plans/_category_.json

@@ -0,0 +1,7 @@
+{
+  "label": "Plans",
+  "position": 3,
+  "link": {
+    "type": "generated-index"
+  }
+}

+ 24 - 0
documentation/docs/admin/plans/change-plan-for-user.md

@@ -0,0 +1,24 @@
+---
+sidebar_position: 2
+---
+
+# Change plan for a user
+
+You can control the memory, CPUs, and disk space the docker container uses, and you can specify whether or not Kubernetes is supported. You can change the CPUs and memory on an existing docker container on the fly without any downtime.
+
+To change a hosting plan (package) for an account click on 'Edit information' for that user and in the new modal select the new plan name then click on 'Save changes'.
+
+![openadmin change plan for a user](/img/admin/change_plan.png)
+
+
+
+
+
+If you need more disk space, you have to create a new docker container. Unfortunately **you can’t resize the existing docker container’s disk**.
+
+This is a limitation with the Docker service itself, and not with OpenPanel. Support for resizing existing docker images is on the way.
+
+You can [increase the size of the  devicemapper partition for the docker container](https://pcx3.com/linux/how-to-increase-docker-container-disk-size-devicemapper/), but please note that the change is not permanent and will be reverted on server reboot. 
+
+
+When changing a plan for the user, the disk size value from the new plan is simply ignored. The value is used only for new user accounts.

+ 51 - 0
documentation/docs/admin/plans/hosting_plans.md

@@ -0,0 +1,51 @@
+---
+sidebar_position: 1
+---
+
+# Hosting Plans
+
+Hosting plans are used to set services and limits for users.
+
+## List hosting plans
+
+To list existing plans navigate to Plans page:
+
+![openadmin list plans](/img/admin/adminpanel_plans.png)
+
+| Field              | Description                                                               |
+| ------------------ | ------------------------------------------------------------------------- |
+| **ID**             | Unique ID for the plan.                                                    |
+| **Name**           | Display name that users will see in their OpenPanel dashboards.            |
+| **Description**    | Visible only to administrators.                                           |
+| **Domains Limit**  | Total number of domain names allowed per user on the plan.                  |
+| **Websites Limit** | Total number of websites (WordPress, NodeJS, Python) per user on the plan.   |
+| **Disk Limit**     | Disk space allocated for the user's container           |
+| **Inodes Limit**   | *(DEPRECATED)* Limits the total number of files allowed in the container.   |
+| **Database Limit** | Total number of MySQL databases allowed per user on the plan.              |
+| **CPU**            | Number of CPU cores dedicated to the user on this hosting plan.             |
+| **RAM**            | Physical Memory (RAM) in GB allocated to the user on this hosting plan.     |
+| **Docker Image**   | Name of the Docker image used when creating new accounts on the plan.        |
+
+
+## Create a plan
+
+To create a new hosting plan click on the 'Create new plan' nutton and set the desired limits for the plan.
+
+![openadmin create new plan](/img/admin/adminpanel_plans_create_new.png)
+
+## Change user plan
+
+Please visit [this page](/docs/admin/plans/change-plan-for-user)
+
+## Modify plan
+
+To change plan limits click on the edit button for the plan and set the new limits.
+
+The new limits will be applied to new accounts only. To apply the limits to all users on the same plan automatically you can use the `apply limits` script.
+
+When changing the limits for a plan, to apply them to existing users:
+- Domains, Websites, and Databases limits will automatically increase.
+- CPU and RAM limits require the docker container to be rebuilt.
+- Disk space **CAN NOT BE CHANGED**, inodes **CAN NOT LONGER BE SET**.
+- Bandwidth (port speed) limits require a manual intervention.
+

+ 7 - 0
documentation/docs/admin/scripts/_category_.json

@@ -0,0 +1,7 @@
+{
+  "label": "OpenPanel CLI",
+  "position": 99,
+  "link": {
+    "type": "generated-index"
+  }
+}

+ 135 - 0
documentation/docs/admin/scripts/admin.md

@@ -0,0 +1,135 @@
+---
+sidebar_position: 1
+---
+
+# Admin
+
+enable/disable the admin panel, reset password, add admin users, etc.
+
+## Check Status
+
+Check if AdminPanel is enabled or disabled and display link on which the AdminPanel is accessibe:
+
+```bash
+opencli admin
+```
+
+Example:
+```bash
+# opencli admin
+● AdminPanel is running and is available on: https://server.openpanel.co:2087/
+```
+
+## Enable / Disable AdminPanel
+
+To disable access to the AdminPanel:
+
+```bash
+opencli admin off
+```
+
+To enable access to the AdminPanel:
+
+```bash
+opencli admin on
+```
+## List Admin users
+
+To view all admin accounts:
+
+```bash
+opencli admin list
+```
+
+## Create new Admin
+
+To create new admin accounts:
+
+```bash
+opencli admin new <username> <password>
+```
+## Reset Admin Password
+
+To reset the password for an admin user:
+
+```bash
+opencli admin password <new_password> [username | admin]
+```
+
+## Rename Admin User
+
+To rename an existing admin user:
+
+```bash
+opencli admin rename <old_username> <new_username>
+```
+
+## Delete Admin User
+
+To rename an existing admin user:
+
+```bash
+opencli admin delete <username>
+```
+
+:::info
+Note: User with 'admin' role can not be deleted.
+:::
+
+## Notifications
+
+`opencli admin notifications` allows you to change the notification preferences.
+
+Settings are stored in `/usr/local/admin/service/notifications.ini` file. However, it is recommended not to modify this file directly. Instead, it's best to utilize the opencli commands. This way, any changes made are immediately applied, and the admin service is automatically restarted only when necessary.
+
+### Get
+
+The `get` parameter allows you to view current notification settings.
+
+```bash
+opencli admin notifications get <OPTION>
+```
+
+Example:
+
+```bash
+# opencli admin notifications get reboot
+yes
+```
+
+### Update
+
+The `update` parameter allows you to change the notification settings.
+
+
+```bash
+opencli admin notifications update <OPTION> <NEW-VALUE>
+```
+
+Example:
+```bash
+opencli admin notifications update load 10
+Updated load to 10
+```
+
+### Options
+
+Currently available options for notifications are:
+
+- reboot
+- backups
+- attack
+- limit
+- update
+- load
+- cpu
+- ram
+- du
+
+## Check server info
+
+To check current server info you can use the following command:
+
+```bash
+opencli server_info 
+```

+ 338 - 0
documentation/docs/admin/scripts/backup.md

@@ -0,0 +1,338 @@
+---
+sidebar_position: 8
+---
+
+# Backups
+
+Manage backup jobs, destiantions, exclude lists, restore, etc.
+
+## Backup Jobs
+
+To list all current backup jobs run command:
+```bash
+opencli backup-job list
+```
+### Create a job
+```bash
+opencli backup-job create
+```
+### Edit backup job
+```bash
+opencli backup-job edit
+```
+### Run backup job
+```bash
+opencli backup-run <ID> --force-run
+```
+### Delete backup job
+```bash
+opencli backup-job delete <ID>
+```
+
+## Destination
+
+To list all current destinations run command:
+
+```bash
+opencli backup-destination list
+```
+
+Example output:
+```bash
+Available destination IDs:
+2143
+2145
+2142
+2144
+2141
+```
+
+### Add Destination
+
+To create a new destination run the following command:
+
+```bash
+opencli backup-destination create <HOSTNAME> <PASSWORD_FOR_KEY_FILE> <PORT> <USER> <PATH_TO_SSH_KEY_FILE> <STORAGE_PERCENTAGE>
+```
+Example:
+```bash
+opencli backup-destination create 15.19.90.29 strongpass 22 root /root/.ssh/id_rsa 85
+```
+
+### Edit Destination
+
+To edit destination configuration run the following command:
+
+```bash
+opencli backup-destination edit <ID> <HOSTNAME> <PASSWORD_FOR_KEY_FILE> <PORT> <USER> <PATH_TO_SSH_KEY_FILE> <STORAGE_PERCENTAGE>
+```
+Example:
+```bash
+opencli backup-destination edit 241 15.19.90.29 strongpass 22 root /root/.ssh/id_rsa 85
+
+Destination  ID: '241' edited successfully!
+Previous destination configuration:
+{
+  "hostname": "18.19.90.29",
+  "password": "pass222",
+  "ssh_port": 22,
+  "ssh_user": "new_ssh_user",
+  "ssh_key_path": "/root/.ssh/id_rsa",
+  "storage_limit": "100"
+}
+New destination configuration:
+{
+  "hostname": "15.19.90.29",
+  "password": "strongpass",
+  "ssh_port": 22,
+  "ssh_user": "root",
+  "ssh_key_path": "/root/.ssh/id_rsa",
+  "storage_limit": "85"
+}
+```
+
+### Delete Destination
+
+To delete a destination run the following command:
+
+```bash
+opencli backup-destination delete <ID>
+```
+
+:::tip
+Destination can not be deleted if it is used by any backup job.
+:::
+
+
+### Validate Destination
+
+To validate a destination run the following command:
+
+```bash
+opencli backup-destination validate <ID>
+```
+
+Command will test;
+
+- if ssh key exists and its permissions
+- ssh connection to the destination server
+- check if destination folder exists
+- check permissions and owner for destination folder
+- check available disk space on destination and compare it with storage limit 
+
+
+Examples:
+
+```bash
+opencli backup-destination validate 2144
+Validating SSH connection with the destination, running command: 'ssh -i /root/.ssh/id_rsa root@18.19.90.29 -p 22'
+SSH connection successful.
+Destination directory /root/backup on the remote server is owned by root user.
+Disk usage on remote destination is over the storage limit (70%) for /root/backup. Backups jobs will not run!
+
+```
+
+```bash
+opencli backup-destination validate 2145
+Validating SSH connection with the destination, running command: 'ssh -i /root/.ssh/id_rsa root@18.19.90.29 -p 22'
+SSH connection successful.
+Destination directory /root/backup on the remote server is owned by root user.
+Disk space on remote destination is below the storage limit (85%) for /root/backup. Backups can run without a problem.
+```
+
+
+```bash
+opencli backup-destination validate 1
+Validating SSH connection with the destination, running command: 'ssh -i /root/.ssh/id_rsa root@18.19.90.29 -p 202'
+ssh: connect to host 18.19.90.29 port 202: Connection refused
+Validation failed! SSH connection failed or timed out.
+SSH Connection Status: 255
+```
+
+
+## Restore
+
+
+### Full Account restore
+To restore user files from a backup run command:
+
+```bash
+opencli backup-restore <DATE> <USER> --all
+```
+
+### Partial restore
+To restore only specific files from a backup, specify what to restore instead of the `--all` flag:
+
+```bash
+opencli backup-restore <DATE> <USER> [-files --entrypoint --nginx-conf --mysql-conf --mysql-data --php-versions --crontab --user-data --core-users --stats-users --apache-ssl-conf --domain-access-reports --ssh --timezone]
+```
+
+Available options:
+- `--all` - restores all user files.
+- `--files` - restores home directory
+- `--entrypoint` - restore services and their status
+- `--apache-conf` - restore Apache configuration
+- `--nginx-conf` - restore Nginx configuration
+- `--mysql-conf` - restore MySQL configuration
+- `--mysql-data` - restore MySQL databases and users
+- `--php-versions` - restore php versions and their php.ini files
+- `--crontab` - restore cronjobs
+- `--user-data` - restore user password, email address, 2fa settings, preferences..
+- `--core-users` - restore OpenPanel data for the user account
+- `--stats-users` - restore resource usage statistics for the account
+- `--apache-ssl-conf` - restore VirtualHosts and SSL certificates
+- `--domain-access-reports` - restore html reports for domain visitors
+- `--ssh` - restore SSH users and passwords
+- `--timezone` - restore timezone setting
+
+
+## List Backups for user
+
+`opencli backup-list` allows you to view available backups for a single user.
+
+```bash
+opencli backup-list <USERNAME> [--json]
+```
+
+
+## View backup details
+
+`opencli backup-details` allows you to view information about a specific user backup.
+
+```bash
+opencli backup-list <USERNAME> <BACKUP_JOB_ID> <DATE> [--json]
+```
+
+Example:
+```bash
+root@server:/# openpanel backup-details nesto 2 20240131170700
+
+backup_job_id=2
+destination_id=1
+destination_directory=20240131170700
+start_time=Wed Jan 31 17:07:52 UTC 2024
+end_time=Wed Jan 31 17:09:35 UTC 2024
+total_exec_time=103
+contains=Full Backup
+status=Completed
+```
+
+
+
+## Index
+
+`opencli backup-index` allows you to re-index backups from a remote destination and make them available for users.
+
+```bash
+opencli backup-index <ID>
+```
+
+Example:
+```bash
+opencli backup-index 21
+
+Indexing backups for 5 users from destination: 18.19.90.29 and directory: /root/backup
+Processing user stefan (1/5)
+Indexed 6 backups for user stefan.
+Processing user demo (2/5)
+Indexed 6 backups for user demo.
+Processing user demo123 (3/5)
+Indexed 6 backups for user demo123.
+Processing user pera (4/5)
+Indexed 5 backups for user pera.
+Processing user nesto (5/5)
+Indexed 5 backups for user nesto.
+Index complete, found a total of 28 backups for all 5 users.
+```
+
+
+## Check
+
+`opencli backup-check` checks process id for any running jobs and terminates the backupjob if process is not running.
+
+```bash
+opencli backup-check
+```
+
+
+## Logs
+
+### List Logs
+
+### View Log File
+
+### Delete Log File
+
+
+## Scheduler
+
+`opencli backup-scheduler` command is executed daily to schedule new backup jobs and ensure they are carried out on time.
+
+Example:
+```bash
+opencli backup-scheduler --debug
+
+0 1 * * SUN opencli backup-run 2  --entrypoint --files --mysql_conf
+0 1 * * * opencli backup-run 3 --conf
+```
+
+
+## Config
+
+`opencli backup-config` allows you to change general backup settings that affect all backup jobs and destinations.
+
+
+
+### Get
+
+The `get` parameter allows you to view current backup settings.
+
+```bash
+opencli backup-config get <OPTION>
+```
+
+Example:
+```bash
+# opencli backup-config get time_format
+24
+```
+
+### Update
+
+The `update` parameter allows you to change backuo settings.
+
+
+```bash
+opencli backup-config update <OPTION> <NEW-VALUE>
+```
+
+Example:
+```bash
+opencli backup-config update time_format 12
+Updated time_format to 12
+```
+
+### Options
+
+Currently available backup configuration options:
+
+- debug (yes|no)
+- error_report (yes|no)
+- workplace_dir (path)
+- downloads_dir (path)
+- delete_orphan_backups (days)
+- days_to_keep_logs (days)
+- time_format (12|24)
+- avg_load_limit (number)
+- concurent_jobs (number)
+- backup_restore_ttl (number)
+- cpu_limit (number)
+- io_read_limit (number)
+- io_write_limit (number)
+- enable_notification (yes|no)
+- email_notification (yes|no)
+- send_emails_to (email)
+- notify_on_every_job (yes|no)
+- notify_on_failed_backups (yes|no)
+- notify_on_no_backups (yes|no)
+- notify_if_no_backups_after (days)

+ 64 - 0
documentation/docs/admin/scripts/docker.md

@@ -0,0 +1,64 @@
+---
+sidebar_position: 9
+---
+
+# Docker
+
+Manage users docker containers and their information.
+
+## Collect Stats
+
+The `collect_stats` script periodically checks resource usage for all users using the [docker stats](https://docs.docker.com/engine/reference/commandline/stats/) command.
+
+For each user, data is logged within their respective folder in JSON files, located in the `/usr/local/panel/core/stats/` directory.
+
+By default, the script is configured to operate in the background at 60-minute intervals using a cronjob:
+
+
+```bash
+0 * * * * opencli docker-collect_stats
+```
+
+To modify the script's execution time, [edit the crontab](https://www.airplane.dev/blog/how-to-edit-crontab) and adjust the cron schedule as needed to specify the desired execution frequency for the script.
+
+To initiate the manual data collection process, execute the following command:
+
+```bash
+opencli docker-collect_stats
+```
+
+Example:
+```bash
+# opencli docker-collect_stats
+
+Data for stefan written to /usr/local/panel/core/stats/stefan/2023-10-08-09-33-56.json
+{"cpu_percent": 0.81, "mem_percent": 13.38, "net_io": "240.5k", "block_io": "409.2k"}
+Data for radovan written to /usr/local/panel/core/stats/radovan/2023-10-08-09-33-56.json
+{"cpu_percent": 0.01, "mem_percent": 0.02, "net_io": "50.59k", "block_io": "0"}
+```
+
+:::info
+The `docker stats` command is a resource-intensive operation that consumes significant host server resources and should be executed sparingly, adhering to the established schedule.
+:::
+
+
+
+## Is port in use
+
+The `is_port_in_use` script is used by the PM2 (Python / NodeJS Application Manager) when installing a new applicaiton to chekc if specified port is already in use in the docker contianer by another application or process.
+
+
+```bash
+opencli docker-is_port_in_use [username]
+```
+
+
+## usage_stats_cleanup
+
+The `usage_stats_cleanup` script is used by the AdminPanel to rotate the 'Past Resource Usage' data for each user, with [the setting](https://docs.openpanel.co/docs/admin/scripts/openpanel_config#resource_usage_retention) specified by the Administrator.
+
+If you need to manually rotate the data run:
+
+```bash
+opencli docker-usage_stats_cleanup
+```

+ 93 - 0
documentation/docs/admin/scripts/domains.md

@@ -0,0 +1,93 @@
+---
+sidebar_position: 2
+---
+
+# Domains
+
+Manage domains: Add, Delete, detect, etc.
+
+## List user domains
+
+To list all domains owned by a user run the following command:
+
+
+```bash
+opencli domains-user <USERNAME>
+```
+
+Example:
+```bash
+# opencli domains-user stefan
+panel.pejcic.rs
+example.openpanel.co
+```
+
+## List all domains
+
+To list all domains owned by all users run the following command:
+
+
+```bash
+opencli domains-all
+```
+
+Example:
+```bash
+# opencli domains-all
+panel.pejcic.rs
+example.openpanel.co
+example.net
+...
+```
+
+## Check who owns a domain name
+
+To check which user owns a domain name run the following command:
+
+```bash
+opencli domains-whoowns <DOMAIN-NAME>
+```
+
+Example:
+```bash
+opencli domains-whoowns pejcic.rs
+Owner of 'pejcic.rs': stefan
+```
+
+The `whoowns` script searches the database in order to determine which username added a domain.
+
+## Parse domain access logs
+
+To parse domain (Nginx) access logs and generate static reports for users domains accessible from `Domains > Access Logs` run the script:
+
+```bash
+opencli domains-stats [USERNAME]
+```
+
+Example:
+```bash
+opencli domains-stats stefan
+Processing user: stefan
+Processed domain pejcic.rs for user stefan
+Processed domain openpanel.co for user stefan
+```
+
+To parse (Nginx) access logs for all active users and their domains run the script without username:
+
+```bash
+opencli domains-stats
+```
+
+## Enable modsecurity
+
+To enable modsecurity for all domains owned by a specific user:
+
+```bash
+opencli domains-enable_modsec [USERNAME]
+```
+
+To enable modsecurity on all domains for all active users:
+
+```bash
+opencli domains-enable_modsec
+```

+ 22 - 0
documentation/docs/admin/scripts/files.md

@@ -0,0 +1,22 @@
+---
+sidebar_position: 10
+---
+
+# Files
+
+Scripts for managing users files.
+
+## Fix Permissions
+
+The `fix_permissions` script is usd to fix file permissions and owner for users files inside their home directory.
+
+To fix permissions for all active users on the server:
+
+```bash
+opencli files-fix_permissions --all
+```
+For a single user:
+
+```bash
+opencli files-fix_permissions [USERNAME] [PATH]
+```

+ 355 - 0
documentation/docs/admin/scripts/openpanel_config.md

@@ -0,0 +1,355 @@
+---
+sidebar_position: 1
+---
+
+# Config
+
+`opencli config` allows you to change the configuration of the user interface and set defaults for new accounts.
+
+Settings are stored in `/usr/local/panel/conf/panel.config` file. However, it is recommended not to modify this file directly. Instead, it's best to utilize the `config` script. This way, any changes made are immediately applied, and the  panel service is automatically restarted only when necessary.
+
+
+Example:
+```bash
+############################## NOTICE #########################################
+#                                                                             #
+# Manually modifying this file is not recommended!                            #
+#                                                                             #
+# You should use the interface in Admin Panel > Server Configuration          #
+# for applying changes to these settings.                                     #
+#                                                                             #
+# https://openpanel.co/docs/admin/scripts/openpanel_config               #
+#                                                                             #
+###############################################################################
+
+[DEFAULT]
+brand_name=
+logo=
+force_domain=
+port=2083
+ssl=no
+openpanel_proxy=
+ns1=
+ns2=
+ns3=
+ns4=
+logout_url=
+enabled_modules=phpmyadmin,ssh,crons,backups,wordpress,pm2,disk_usage,inodes,usage,terminal,services,webserver,fix_permissions,malware_scan,process_manager,ip_blocker,redis,memcached,elasticsearch,login_history,activity
+
+[USERS]
+password_reset=no
+twofa_nag=yes
+how_to_guides=yes
+sidebar_color=dark
+avatar_type=gravatar
+max_login_records=20
+activity_items_per_page=25
+resource_usage_charts_mode=two
+resource_usage_items_per_page=100
+resource_usage_retention=100
+acccess_logs_per_page=1000
+domains_per_page=100
+
+[PHP]
+default_php_version=8.2
+
+[PANEL]
+autoupdate=on
+autopatch=on
+```
+
+
+## Get
+
+The `get` parameter allows you to view current settings.
+
+```bash
+opencli config get <OPTION>
+```
+
+Example:
+
+```bash
+# opencli config get force_domain
+srv.openpanel.co
+```
+
+## Update
+
+The `update` parameter allows you to change the settings.
+
+
+```bash
+opencli config update <OPTION> <NEW-VALUE>
+```
+
+Example:
+```bash
+opencli config update force_domain nesto.rs
+Updated force_domain to nesto.rs
+```
+
+:::info
+To apply the new settings panel service needs to be restarted.
+:::
+
+## Options
+
+Currently available configuration options:
+
+### logo
+
+`logo` allows you to set a url for image *(suggested dimensions are 80x200px) that will be displayed to users on:
+
+- logo on every page
+- logo displayed on the login page
+- footer logo used in emails
+
+```bash
+logo=https://http.cat/images/100.jpg
+```
+
+### brand_name
+
+`brand_name` allows you to brand the OpenPanel name that users see in their user panel with your custom brand name.
+
+![brand_name](/img/admin/brand_name.png)
+
+This brand name is displayed in the following positions:
+- on top left when logo is not provided
+- in the page title
+- in the footer text on every page
+  
+
+```bash
+brand_name=pcx3.com
+```
+
+### max_login_records
+
+`max_login_records` determines how many records are kept for every users login history page. Default value, if empty is 20.
+
+```bash
+max_login_records=20
+```
+
+### default_php_version
+
+`default_php_version` sets the PHP version used for new accounts. Default value is 8.3
+
+```bash
+default_php_version=8.3
+```
+
+
+### domains_per_page
+
+Set number of domains to show per page for the user, default value is 100.
+
+```bash
+domains_per_page=100
+```
+
+### acccess_logs_per_page
+
+Set number of lines to show per page from the domains activity log.
+
+```bash
+acccess_logs_per_page=1000
+```
+
+### resource_usage_items_per_page
+
+Set number of rows to display per page on the 'Resource Usage History' page.
+
+```bash
+resource_usage_items_per_page=100
+```
+
+### resource_usage_retention
+
+Set number of records to keep for each user, default value is 100.
+
+```bash
+resource_usage_retention=100
+```
+
+### resource_usage_charts_mode
+`resource_usage_charts_mode` allows you to set the number of charts displayed on users _Historical Resource Usage_ page.
+
+![resource_columns](/img/admin/resource_columns.gif)
+
+- _one_ - displays a single chart for both CPU and RAM usage.
+- _two_ (Default) - displays two charts: one for CPU and another for RAM. 
+- _none_ - displays no charts.
+
+```bash
+resource_usage_charts_mode=two
+```
+
+### sidebar_color
+
+`sidebar_color` allows you to set a custom color scheme for the sidebar.
+
+![Sidebar colors](/img/admin/sidebar_colors.gif)
+
+Available options are:
+
+- dark
+- prime
+- light
+
+```bash
+sidebar_color=prime
+```
+
+### password_reset
+
+`password_reset` allows you to enable or disable the password reset functionality for the login page. By default password reset is disabled.
+
+```bash
+password_reset=yes
+```
+
+### avatar_type
+
+`avatar_type` allows you to set the type of images used for users profile pictures in the users panel.
+
+![avatar type](/img/admin/profile_picture.gif)
+
+Available options:
+
+- _gravatar_ - displays the gravatar picture associated with the email address of the user.
+- _letter_ - displays a CSS generated icon using the users first letter of the username.
+- _icon_ - displays a placeholder bootstrap icon representing the user.
+
+```bash
+avatar_type=gravatar
+```
+
+### logout_url
+
+`logout_url` allows administrators to define the destination to which users should be redirected after they log out. The default setting directs them to the `/login` page.
+
+```bash
+logout_url=https://google.com
+```
+
+### activity_items_per_page
+
+`activity_items_per_page` allows administrators to define the default number of displayed items on the `/activity` page, the value defaults to 25.
+
+```bash
+activity_items_per_page=50
+```
+
+### force_domain
+
+`force_domain` enables you to specify a mandatory domain name for accessing the user panel. If a user attempts to visit the panel using any other domain, such as "their-domain.com:2083", they will be automatically redirected to "your-domain.com:2083." This feature utilizes the `port` option to ensure the enforcement of the specified port value as well.
+
+
+```bash
+force_domain=openpanel.co
+```
+
+### port
+
+The `port` setting enables you to define a custom port for the panel's operation. By default, the panel uses port **2083**, but you have the flexibility to set it to any desired port.
+It's crucial to ensure that the chosen port is open in your firewall configuration. 
+
+```bash
+port=2083
+```
+
+:::info
+After adjusting the port, it's necessary to restart the panel service to apply the new port configuration.
+:::
+
+
+### ssl
+
+The `ssl` setting when enabled will force both the OpenPanel and AdminPanel to use SSL for the hostname and redirect traffic to https. This setting is by default disabled but will be automatically enabled if [opencli ssl-hostname](/docs/admin/scripts/ssl#hostname) succeeded in generating an SSL for the hostname during OpenPanel installation.
+
+```bash
+ssl=no
+```
+
+### openpanel_proxy
+
+The `openpanel_proxy` setting allows you to set a custom /something that will allow users to access their openpanel interface using their domain names. For example: 'panel' will make any domain, example: 'example.com/panel' redirect to the OpenPanel.
+
+```bash
+openpanel_proxy=open_sesame
+```
+
+### nameservers
+
+The `ns1` `ns2` `ns3` `ns4` options allow you to set nameservers that will be used in dns zone files for newly added domains, and displayed to users on their panel dashboard.
+Nameservers should be added in pairs, ns1 and ns2, ns3 and ns4.
+
+![ns](/img/admin/ns.png)
+
+```bash
+ns1=ns1.openpanel.co
+ns2=ns2.openpanel.co
+ns3=ns3.openpanel.co
+ns4=ns4.openpanel.co
+```
+
+### enabled_modules
+
+In OpenPanel version 0.1.5, we implemented the Modules feature, which, by default, loads only the essential modules. Administrators also have the option to selectively disable modules they do not require.
+
+Currently, the following modules are optional and can be disabled by the Administrator:
+```bash
+enabled_modules=phpmyadmin,ssh,crons,backups,wordpress,pm2,disk_usage,inodes,usage,terminal,services,webserver,fix_permissions,malware_scan,process_manager,ip_blocker,redis,memcached,elasticsearch,login_history,activity
+```
+
+### autopatch
+
+The `autopatch` option allows Administrator to automatically update OpenPanel to minor versions.
+
+| Autopatch    | Update to minor version  | Update to major version  |
+| -------- | ------- | ------- |
+| yes | 1.0.2 will be updated to 1.0.3 | 1.0.2 will **NOT** be updated to 2.0.0 |
+| no | 1.0.2 will **NOT** be updated to 1.0.3 | 1.0.2 will **NOT** be updated to 2.0.0 |
+
+```bash
+autopatch=yes
+```
+
+### autoupdate
+
+The `autoupdate` option allows Aministrator to enable or disable automatic updates.
+
+| Autoupdate    | Update to minor version  | Update to major version  |
+| -------- | ------- | ------- |
+| yes | 1.0.2 will **NOT** be updated to 1.0.3 | 1.0.2 will be updated to 2.0.0 |
+| no | 1.0.2 will **NOT** be updated to 1.0.3 | 1.0.2 will **NOT** be updated to 2.0.0 |
+
+```bash
+autoupdate=yes
+```
+
+
+### twofa_nag
+
+The `twofa_nag` option allows Administrator to set if 2FA nag should be displayed to users on their dashboard page.
+
+![2fa nag](/img/admin/2fa_nag.png)
+
+
+```bash
+twofa_nag=yes
+```
+
+### how_to_guides
+
+The `how_to_guides` option allows Aministrator to enable or disable the How-to guides section from users dashboards.
+
+![how_to guides](/img/admin/how_to.png)
+
+```bash
+how_to_guides=yes
+```
+If configured as "yes", the system will initially attempt to access a JSON file containing your custom how-to guides. In the event that the file is not available, articles from https://openpanel.co/docs/panel/ will be shown instead.

+ 34 - 0
documentation/docs/admin/scripts/panel.sh

@@ -0,0 +1,34 @@
+---
+sidebar_position: 9
+---
+
+# System
+
+Scripts for checking OpenPanel version, running updates, etc.
+
+## Check version
+
+To check installed openpanel version run the following command:
+
+```bash
+bash /usr/local/admin/scripts/users/version.sh
+```
+
+## update_check
+
+To check if update is available run:
+
+```bash
+bash /usr/local/admin/scripts/users/update_check.sh
+```
+
+
+## ate
+
+This script is used by OpenPanel to check if [autoupdate](/docs/admin/scripts/openpanel_config#autoupdate) or [autopatch](/docs/admin/scripts/openpanel_config#autopatch) options are enabled by user and then run update:
+
+```bash
+bash /usr/local/admin/scripts/users/update.sh
+```
+
+To manually update OpenPanel regardless of the `autoupdate` and `autopatch` settings, use  `--force` flag.

+ 165 - 0
documentation/docs/admin/scripts/php.md

@@ -0,0 +1,165 @@
+---
+sidebar_position: 8
+---
+
+# PHP
+
+Manage users' PHP versions: list enabled, list available, change version, etc.
+
+## Get version for a domain
+
+To view the current PHP version used by a domain, run the following command:
+
+```bash
+opencli php-domain_php <DOMAIN-NAME>
+```
+
+Example:
+```bash
+# opencli php-domain_php pejcic.rs
+Domain 'pejcic.rs' (owned by user: stefan) uses PHP version: php8.1
+```
+
+## Change version for a domain
+
+To change a PHP version for a domain name run the `domain_php` script with `--update` flag::
+
+```bash
+opencli php-domain_php <DOMAIN-NAME> --update <PHP-VERSION>
+```
+
+Example:
+```bash
+# opencli php-domain_php pejcic.rs --update 8.3
+Updating PHP version to: 8.3
+Domain 'pejcic.rs' (owned by user: stefan) uses PHP version: php8.3
+Updating PHP version in the Apache configuration file...
+ * Reloading Apache httpd web server apache2
+ *
+Updated PHP version in the configuration file to 8.3
+```
+
+## List the default version
+
+The default PHP version for a user determines which PHP version will be used for all domains that the user adds in the future. It does not change the PHP version for any existing domains.
+
+To list the currently set default PHP version for a user, run the following command:
+
+```bash
+opencli php-default_php_version <USERNAME>
+```
+
+Example:
+```bash
+# opencli php-default_php_version stefan
+Default PHP version for user 'stefan' is: php8.3
+```
+
+## Change the default version
+
+To update the default PHP version for a user use the php-default_php_version with `--update` flag and provide the new PHP version.
+
+```bash
+opencli php-default_php_version <USERNAME> --update <VERSION>
+```
+
+Example:
+```bash
+# opencli php-default_php_version stefan --update 8.1
+PHP version for user 'stefan' updated to: 8.1
+```
+
+## List installed versions
+
+To list all installed PHP versions for a user, run the following command:
+
+```bash
+opencli php-enabled_php_versions <USERNAME>
+```
+
+Example:
+```bash
+# opencli php-enabled_php_versions stefan
+php7.4
+php8.1
+php8.2
+```
+
+## List available versions
+
+To get available (that can be installed) PHP versions for users' OS, run the following command:
+
+```bash
+opencli php-get_available_php_versions <USERNAME>
+```
+
+Example:
+```bash
+# opencli php-get_available_php_versions stefan
+....
+PHP versions for user stefan have been updated and stored in /home/stefan/etc/.panel/php/php_available_versions.json.
+```
+
+The script will by default update users' available PHP versions setting for the UI, optionally you can add `--show` flag to display the available versions.
+
+```bash
+opencli php-get_available_php_versions <USERNAME> --show
+```
+
+Example:
+```bash
+# opencli php-get_available_php_versions stefan --show
+....
+Available PHP versions for user stefan:
+php8.1-fpm
+php5.6-fpm
+php7.0-fpm
+php7.1-fpm
+php7.2-fpm
+php7.3-fpm
+php7.4-fpm
+php8.0-fpm
+php8.2-fpm
+php8.3-fpm
+```
+
+The `get_available_php_versions` script performs various actions:
+
+- Runs `apt-get update` inside users contianer
+- Lists available PHP versions from remote repositories
+- Saves the list to `/php_available_versions.json` in user home directory
+- optionally display the list
+
+## Install a new version
+
+To install a a new PHP version run the following command:
+
+```bash
+opencli php-install_php_version <USERNAME> <PHP-VERSION>
+```
+
+Example:
+```bash
+# opencli php-install_php_version stefan 8.2
+Hit:1 https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy InRelease
+Hit:2 http://security.ubuntu.com/ubuntu jammy-security InRelease
+Hit:3 http://archive.ubuntu.com/ubuntu jammy InRelease
+Hit:4 http://archive.ubuntu.com/ubuntu jammy-updates InRelease
+Hit:5 http://archive.ubuntu.com/ubuntu jammy-backports InRelease
+Reading package lists... Done
+Reading package lists... Done
+Building dependency tree... Done
+Reading state information... Done
+The following additional packages will be installed:
+  php8.1-cli php8.1-common php8.1-opcache php8.1-readline
+Suggested packages:
+  php-pear
+The following NEW packages will be installed:
+  php8.1-cli php8.1-common php8.1-fpm php8.1-opcache php8.1-readline
+0 upgraded, 5 newly installed, 0 to remove and 5 not upgraded.
+Need to get 4804 kB of archives.
+After this operation, 21.1 MB of additional disk space will be used.
+..
+.
+```
+

+ 88 - 0
documentation/docs/admin/scripts/plans.md

@@ -0,0 +1,88 @@
+---
+sidebar_position: 2
+---
+
+# Plans
+
+Scripts for creating and editing hosting plans (packages).
+
+## List Plans
+
+To list all current hosting packages (plans) run:
+
+```bash
+opencli plan-list
+```
+
+Example output:
+```bash
+opencli plan-list
++----+-----------------+------------------------+---------------+----------------+------------+--------------+----------+------+------+-----------------+-----------+
+| id | name            | description            | domains_limit | websites_limit | disk_limit | inodes_limit | db_limit | cpu  | ram  | docker_image    | bandwidth |
++----+-----------------+------------------------+---------------+----------------+------------+--------------+----------+------+------+-----------------+-----------+
+|  1 | cloud_4_nginx   | 20gb space and Nginx   |             0 |             10 | 20 GB      |      1000000 |        0 | 4    | 4g   | dev_plan_nginx  |       100 |
+|  2 | cloud_4_apache  | 20gb space and Apache  |             0 |             10 | 20 GB      |      1000000 |        0 | 4    | 4g   | dev_plan_apache |       100 |
+|  3 | cloud_8_nginx   | 80gb space and Nginx   |             0 |             50 | 80 GB      |      2000000 |        0 | 8    | 8g   | dev_plan_nginx  |       200 |
+|  4 | cloud_8_apache  | 80gb space and Apache  |             0 |             50 | 80 GB      |      2000000 |        0 | 8    | 8g   | dev_plan_apache |       200 |
++----+-----------------+------------------------+---------------+----------------+------------+--------------+----------+------+------+-----------------+-----------+
+```
+
+You can also format the data as JSON:
+
+```bash
+opencli plan-list --json
+```
+
+## Create Plan
+
+To create a new user run the following command:
+
+```bash
+opencli plan-create <NAME> <DESCRIPTION> <DOMAINS_LIMIT> <WEBSITES_LIMIT> <DISK_LIMIT> <INODES_LIMITS> <DATABASES_LIMIT> <CPU_LIMIT> <RAM_LIMIT> <DOCKER_IMAGE> <PORT_SPEED_LIMIT>
+```
+
+Example:
+```bash
+opencli plan-create cloud_8 "Custom plan with 8GB of RAM&CPU" 0 0 15 500000 0 8 8 nginx 200
+```
+
+## List Users on Plan
+
+List all users that are currently using a plan:
+
+```bash
+opencli plan-usage
+```
+
+Example output:
+```bash
+opencli plan-usage
++----+----------+-------+----------------+---------------------+
+| id | username | email | plan_name      | registered_date     |
++----+----------+-------+----------------+---------------------+
+|  2 | rasa     | rasa  | cloud_4_apache | 2023-11-30 10:33:52 |
+|  3 | aas      | sasa  | cloud_4_apache | 2023-11-30 12:01:49 |
++----+----------+-------+----------------+---------------------+
+```
+
+You can also format the data as JSON:
+
+```bash
+opencli plan-usage --json
+```
+
+## Delete Plan
+
+Delete a plan if no users are currently using it.
+
+```bash
+opencli plan-delete <PLAN_NAME> 
+```
+
+## Edit Plan
+
+Change plan limits.
+
+```bash
+# 
+```

+ 63 - 0
documentation/docs/admin/scripts/ssl.md

@@ -0,0 +1,63 @@
+---
+sidebar_position: 10
+---
+
+# SSL
+
+Generate and manage SSL certificates for user domains and server hostname.
+
+
+## Check SSL for all domains owned by user
+
+This command allows you to check the SSL status of all user domains in Certbot and updates the ssl/.domain_status file of the user. This file is used to display SSL status and expiry date on [SSL Certificates page](/docs/panel/domains/SSL) in OpenPanel.
+
+```bash
+opencli ssl-user <username>
+```
+
+Example:
+```bash
+# opencli ssl-user stefan
+panel.pejcic.rs:  2024-02-20 08:24:00+00:00 (VALID: 89 days)
+example.com: None
+```
+
+
+## Check SSL for server hostname
+
+By default when OpenPanel is installed, it will run the 'opencli ssl-hostname' and try to generate SSL for the server hostname, if succeeds, it will set the OpenPanel and AdminPanel to use the SSL and redirect all domains:2083 to the https://hostname.tld:2083 and https://hostname.tld:2087 
+
+If you change the hostname and want the OpenPanel to use the new domain name, you can manually at any time run the following command to generate SSL for the new hostname and force it for OpenPanel: 
+
+```bash
+opencli ssl-hostname
+```
+
+Example:
+```bash
+opencli ssl-hostname
+No SSL certificate found for server.openpanel.co. Proceeding to generate a new certificate...
+Saving debug log to /var/log/letsencrypt/letsencrypt.log
+Account registered.
+Requesting a certificate for server.openpanel.co
+
+Successfully received certificate.
+Certificate is saved at: /etc/letsencrypt/live/server.openpanel.co/fullchain.pem
+Key is saved at:         /etc/letsencrypt/live/server.openpanel.co/privkey.pem
+This certificate expires on 2024-02-18.
+These files will be updated when the certificate renews.
+Certbot has set up a scheduled task to automatically renew this certificate in the background.
+
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+If you like Certbot, please consider supporting our work by:
+ * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
+ * Donating to EFF:                    https://eff.org/donate-le
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ssl is now enabled and force_domain value in /usr/local/panel/conf/panel.config is set to 'server.openpanel.co'.
+Restarting the panel services to apply the newly generated SSL and force domain server.openpanel.co.
+
+- OpenPanel  is now available on: https://server.openpanel.co:2083
+- AdminPanel is now available on: https://server.openpanel.co:2087
+
+
+```

+ 173 - 0
documentation/docs/admin/scripts/users.md

@@ -0,0 +1,173 @@
+---
+sidebar_position: 1
+---
+
+# Users
+
+Manage users: Add, Delete, Suspend, Unsuspend, etc.
+
+## List Users
+
+To list all users, use the following command:
+
+```bash
+opencli user-list
+```
+
+Example output:
+```bash
+opencli user-list
++----+----------+-----------------+-----------------+---------------------+
+| id | username | email           | plan_name       | registered_date     |
++----+----------+-----------------+-----------------+---------------------+
+| 52 | stefan   | stefan          | cloud_4_nginx_3 | 2023-11-16 19:11:20 |
+| 53 | petar    | petarc@petar.rs | cloud_8_nginx   | 2023-11-17 12:25:44 |
+| 54 | rasa     | rasa@rasa.rs    | cloud_12_nginx  | 2023-11-17 15:09:28 |
++----+----------+-----------------+-----------------+---------------------+
+```
+
+You can also format the data as JSON:
+
+```bash
+opencli user-list --json
+```
+
+## Add User
+
+To create a new user run the following command:
+
+```bash
+opencli user-add <USERNAME> <PASSWORD> <EMAIL> <PLAN_ID>
+```
+
+:::tip
+Provide `random` as password to generate a strong random password.
+:::
+
+## Delete User
+
+To delete a user and all his data run the following command:
+
+```bash
+opencli user-delete <USERNAME>
+```
+
+add `-y` flag to disable prompt.
+
+:::warning
+This action is irreversible and will permanently delete all user data.
+:::
+
+## Suspend User
+
+To suspend (temporary disable access) to user, run the follwowing command:
+
+```bash
+opencli user-suspend <USERNAME>
+```
+
+## Unsuspend User
+
+To unsuspend (enable access) to user, run the follwowing command:
+
+```bash
+opencli user-unsuspend <USERNAME>
+```
+
+## Rename User
+
+To change a username run:
+```bash
+opencli user-rename <USERNAME> <NEW_USERNAME>
+```
+
+## Change Email
+
+To change a email run:
+```bash
+opencli user-email <USERNAME> <NEW_EMAIL>
+```
+
+## Change Password
+
+To reset the password for a OpenPanel user, you can use the `user-password` command:
+
+```bash
+opencli user-password <USERNAME> <NEW_PASSWORD> --ssh
+```
+Use the `--ssh` flag to also change the password for the SSH user in the container.
+
+## Login as User
+
+This command allows you to login as the root user inside any users container.
+```bash
+opencli user-login <USERNAME>
+```
+
+## Check / Disable 2FA
+
+To disable **Two-Factor Authentication** for a user, run the following command:
+
+```bash
+opencli user-2fa <USERNAME> [disable]
+```
+
+
+## Assign / Remove IP to User
+
+To assign free IP address to a user run the following command:
+
+```bash
+opencli user-ip <USERNAME> <IP_ADDRESS>
+```
+
+To assign IP address **that is currently used by another user** to this user, run the following command:
+
+
+```bash
+opencli user-ip <USERNAME> <IP_ADDRESS> --y
+```
+
+
+To remove dedicated IP address from a user run:
+
+```bash
+opencli user-ip <USERNAME> delete
+```
+
+
+## View login log
+View up to last 20 successfull logins for the user.
+
+```bash
+opencli user-loginlog <USERNAME> [--json]
+```
+
+## REDIS
+Check REDIS service status for user and enable/disable REDIS® service.
+
+```bash
+opencli user-redis [check|enable|disable] <USERNAME>
+```
+
+Example:
+```bash
+opencli user-redis check stefan
+
+Memcached is not installed for user stefan.
+```
+
+## Memcached
+Check Memcached service status for user and enable/disable Memcached service.
+
+```bash
+opencli user-memcached [check|enable|disable] <USERNAME>
+```
+
+Example:
+```bash
+opencli user-memcached enable stefan
+
+Memcached enabled successfully for user stefan.
+```
+

+ 60 - 0
documentation/docs/admin/scripts/webserver.md

@@ -0,0 +1,60 @@
+---
+sidebar_position: 7
+---
+
+# Apache / Nginx
+
+Scripts for managing users webserver: Nginx or Apache
+
+
+## Get users webserver
+
+To list users current webserver run the following command:
+
+```bash
+opencli webserver-get_webserver_for_user <USERNAME>
+```
+
+Example:
+```bash
+# opencli webserver-get_webserver_for_user stefan
+Web Server for user stefan: apache
+```
+
+The script will by default display cached information from the users `server_config.yml` file, optionally you can add `--update` flag to check the current webserver in the container and update the file.
+
+Example:
+```bash
+# opencli webserver-get_webserver_for_user stefan --update
+Web Server for user stefan updated to: apache
+```
+
+## Fix Permissions
+
+The `fixperms` script can be used to fix permissions on user files.
+
+It performs:
+- sets the owner of all files inside /home/$username to the user.
+- sets the permissions of .php files to 755.
+- sets the permissions of .cgi and .pl files to 755.
+- sets the permissions of .log files to 640.
+- changes the ownership of all directories to match the user.
+- sets the permissions of all directories to 755.
+
+```bash
+opencli webserver-fixperms <USERNAME>
+```
+
+You can pass the `--all` flag to change permissions for all users:
+
+```bash
+opencli webserver-fixperms --all
+```
+
+## Install ModSecurity
+
+You can install modsecurity by using:
+
+```bash
+opencli nginx-install_modsec
+```

+ 7 - 0
documentation/docs/admin/settings/_category_.json

@@ -0,0 +1,7 @@
+{
+  "label": "Settings",
+  "position": 6,
+  "link": {
+    "type": "generated-index"
+  }
+}

+ 26 - 0
documentation/docs/admin/settings/adminpanel.md

@@ -0,0 +1,26 @@
+---
+sidebar_position: 6
+---
+
+# OpenAdmin
+
+Manage admin users, enable features and disable admin interface.
+
+![openadmin admin panel settings](/img/admin/adminpanel_openadmin_settings.png)
+
+The Settings > OpenAdmin page allows you to manage Admin users, enable/disable server-wide settings such as backups or domain access logs.
+
+##  Admin Users
+
+For options on managing admin users please [visit this page](/docs/admin/users/openadmin).
+
+## Enable Features
+
+To enable a eature check it and click on save.
+
+## Disable Admin Panel
+As an advanced security measure, you can temporarily disable access to the OpenAdmin interface. This will disable the admin panel and you can turn it back on when needed by executing the command `opencli admin on`
+
+## Server Information Report
+Generate a report on server information and service status, and provide it when reaching out for support on [community.openpanel.co](https://community.openpanel.co/)
+

+ 6 - 0
documentation/docs/admin/settings/backups.md

@@ -0,0 +1,6 @@
+---
+sidebar_position: 6
+---
+
+# Backups
+

+ 11 - 0
documentation/docs/admin/settings/docker.md

@@ -0,0 +1,11 @@
+---
+sidebar_position: 2
+---
+
+# Docker
+
+:::info
+This feature is still experimental and not yet released.
+:::
+
+![openadmin docker settings](/img/admin/adminpanel_docker_settings.png)

+ 41 - 0
documentation/docs/admin/settings/firewall.md

@@ -0,0 +1,41 @@
+---
+sidebar_position: 4
+---
+
+# Firewall
+
+View and edit firewall (UFW) rules
+
+![openadmin firewall settings](/img/admin/adminpanel_firewall_settings.png)
+
+The firewall settings page provides three tabs:
+
+- IPv4 - that lists all IPv4 firewall rules
+- IPv6 - that lists all IPv6 firewall rules
+- Logs - displays the UFW service log
+
+## View existing rules
+
+The table shows firewall rules, showcasing information such as rule ID, action, ports, source/destination IP, and the username of the user utilizing the port.
+For IPv6 rules, navigate to the IPv6 tab.
+
+![openadmin firewall ipv6 rules](/img/admin/adminpanel_firewall_ipv6.png)
+
+## Add Rules
+
+To create a new rule click on the 'New Rule' button and in the modal choose 'ALLOW' to allow the IP address or port, and 'DENY' to block access for IP address or port.
+
+![openadmin firewall add rule](/img/admin/adminpanel_firewall_add_rule.png)
+
+## Delete Rules
+
+To delete a rule click on the 'Delete' link next to it, and in the confirmaiton modal click on 'Delete' button.
+
+![openadmin firewall delete rule](/img/admin/adminpanel_firewall_delete_rule.png)
+
+
+## View logs
+
+For logs, navigate to the 'Logs' tab.
+
+![openadmin firewall logs](/img/admin/adminpanel_firewall_logs.png)

+ 70 - 0
documentation/docs/admin/settings/general.md

@@ -0,0 +1,70 @@
+---
+sidebar_position: 1
+---
+
+# General Settings
+
+Change ports, domain, enable ssl, etc.
+
+![openadmin general panel settings](/img/admin/adminpanel_general_settings.png)
+
+The General Settings page enables you to specify the domain name for accessing both the admin and user panels, with an option to switch to an IP address. 
+
+## Set domain for OpenPanel
+
+To enable access to both OpenAdmin and OpenPanel through a domain name, such as srv.your-domain.com:2083, follow these three steps:
+
+1. Set the desired name as the server hostname.
+2. Point the domain name to the public IP of the server.
+3. Configure the domain name in OpenAdmin under OpenAdmin Settings.
+
+Completing these steps will allow users to access both OpenAdmin and OpenPanel using the specified domain name and port.
+
+![openadmin set_domainname](/img/admin/adminpanel_domainname.png)
+
+## Set IP address for OpenPanel
+
+To access OpenPanel and OpenAdmin via server public IP address, choose the "Server IP address" option and click save. The modification is immediate, redirecting you to the designated IP:2087 for the admin panel upon saving.
+
+![openadmin set ip](/img/admin/adminpanel_serverip.png)
+
+## Change OpenPanel port
+
+Port configurations for OpenAdmin and OpenPanel interfaces can be modified from their default settings (2087 for OpenAdmin and 203 for OpenPanel). 
+
+![openadmin set port for openpanel](/img/admin/openpanel_settings.png)
+
+To modify the port for the OpenPanel from the default `2083` to another value, you can easily set the desired port in the "OpenPanel Port" field.
+It's important to note that the port must fall within the range of 1000-33000.
+
+## Change OpenAdmin port
+
+Port configurations for OpenAdmin and OpenPanel interfaces can be modified from their default settings (2087 for OpenAdmin and 203 for OpenPanel). 
+
+![openadmin set port for openadmin](/img/admin/openadmin_settings.png)
+
+To modify the port for the AdminPanel from the default `2087` to another value, you can easily set the desired port in the "OpenAdmin Port" field.
+It's important to note that the port must fall within the range of 1000-33000.
+
+
+# Force HTTPS
+
+Enabling the "Force HTTPS" option ensures that the panels are accessible via HTTPS, recommended for enhanced security features like CORS and header checks in the OpenPanel interface.
+To activate HTTPS, select the "Force HTTPS" option; to deactivate, simply uncheck it.
+
+# Change /openpanel
+
+By default, when users add a domain, the addition of "/openpanel" to the domain URL will redirect them to the OpenPanel interface. However, you have the flexibility to customize this, such as changing it to "/awesome," allowing users to access the OpenPanel via "their-domain.com/awesome".
+
+To change the "/openpanel" to something else, simply set the value for the "OpenPanel is also available on:" field and click on save. Changes take effect instantly without service interruption.
+![openadmin set custom path](/img/admin/openpanel_settings_available_on_openpanel.png)
+
+## Enable Updates
+
+The Update Preferences section provides the option to enable or disable Auto updates and Auto patches. Patches involve minor updates aimed at fixing bugs without introducing new features. On the other hand, Updates encompass major updates that introduce new features, potentially impacting existing functionality.
+
+![openadmin set update preferences](/img/admin/openpanel_settings_updates.png)
+
+Examples:
+- Autoupdate: 1.0.2 will **NOT** be updated to 1.0.3 BUT 1.0.2 will be updated to 2.0.0
+- Autopatch:  1.0.2 will be updated to 1.0.3 BUT 1.0.2 will **NOT** be updated to 2.0.0

+ 71 - 0
documentation/docs/admin/settings/openpanel.md

@@ -0,0 +1,71 @@
+---
+sidebar_position: 3
+---
+
+# OpenPanel
+
+Edit nameservers, disable features and more.
+
+![openadmin openpanel settings](/img/admin/adminpanel_openpanel_settings.png)
+
+The OpenPanel Settings page allows you to edit setitngs and features availabnel to users in their OpenPanel interface.
+
+## Branding
+
+To set a custom name visible in the OpenPanel sidebar and on login pages, enter the desired name in the "Brand name" option. Alternatively, to display a logo instead, provide the URL in the "Logo image" field and save the changes.
+
+## Set nameservers
+
+Before adding any domains its important to first create nameservers so that added domains will have valid dns zone files and be able to propagate.
+
+Configuring nameservers involves two steps:
+
+1. Create private nameservers (glue DNS records) for the domain through your domain registry.
+2. Add the nameservers into the OpenPanel configuration.
+
+Here are tutorials for some popular domain providers:
+- [Cloudflare](https://developers.cloudflare.com/dns/additional-options/custom-nameservers/zone-custom-nameservers/)
+- [GoDaddy](https://uk.godaddy.com/help/add-custom-hostnames-12320)
+- [NameCheap](https://www.namecheap.com/support/knowledgebase/article.aspx/768/10/how-do-i-register-personal-nameservers-for-my-domain/#:~:text=Click%20on%20the%20Manage%20option,5.)
+
+To add nameservers from OpenAdmin navgiate to Settings > OpenPanel and set nameservers in ns1 and ns2 fields and click on save:
+
+![openpanel add nameservers](/img/admin/openadmin_add_ns.png)
+
+Or from terminal run commands:
+```bash
+opencli config update ns1 your_ns1.domain.com
+opencli config update ns2 your_ns2.domain.com
+```
+
+:::info
+After creating nameservers it can take up to 12h for the records to be globally accessible. Use a tool sush as [whatsmydns.net](https://www.whatsmydns.net/) to monitor the status.
+
+If you still experience problems after the propagation process, then please check this guide: [dns server not responding to reqeuests](https://community.openpanel.co/d/5-dns-server-does-not-respond-to-request-for-domain-zone).
+:::
+
+
+## Enable Features
+
+Administrators have the ability to enable or disable each feature (page) in the OpenPanel interface. To activate a feature, select it in the "Enable Features" section and click save. The change is immediate and necessitates the restart of the OpenPanel service to implement the modifications.
+
+Once enabled, the feature becomes instantly available to all users, appearing in the OpenPanel interface sidebar, search results, and dashboard icons.
+
+![openpanel enable modules](/img/admin/openpanel_settings_modules.png)
+
+## Other settings
+
+Additional settings available in the Settings > OpenPanel page include:
+
+- **Logout URL:** Set the URL for redirecting users upon logout from the OpenPanel.
+- **Avatar Type:** Choose to display Gravatar, Letter, or Icon as avatars for users.
+- **Resource Usage Charts:** Opt to display 1, 2, or no charts on the Resource Usage page.
+- **Default PHP Version:** Specify the default PHP version for domains added by users (users can override this setting).
+- **Enable Password Reset:** Activate password reset on login forms (not recommended).
+- **Display 2FA Nag:** Show a message in users' dashboards encouraging them to set up 2FA for added security.
+- **Display How-to Guides:** Display how-to articles for users in their dashboard pages.
+- **Login Records:** Set the number of login records to keep for each user.
+- **Activities per Page:** Specify the number of activity items to display per page.
+- **Usage per Page:** Specify the number of Resource Usage items to display per page.
+- **Usage Retention:** Set the number of Resource Usage items to keep for each user.
+- **Domains per Page:** Specify the number of domains to display per page.

+ 51 - 0
documentation/docs/admin/settings/waf.md

@@ -0,0 +1,51 @@
+---
+sidebar_position: 5
+---
+
+# WAF
+
+Install ModSecurity and enable it for user domains.
+
+The Settings > ModSecurity page allows you to install ModSecurity for Nginx and configures the [OWASP core ruleset](https://owasp.org/www-project-modsecurity-core-rule-set/)
+
+The OWASP ModSecurity Core Rule Set (CRS) is a set of generic attack detection rules for use with ModSecurity that will increase the security of user domains and websites.
+
+## Install ModSecurity
+
+Upon the initial access to the ModSecurity page, you will be prompted to install the ModSecurity plugin.
+
+:::warning
+The installation process may require up to 10 minutes and involves rebuilding the Nginx configuration. It's important to note that any customizations to the service will be permanently removed during this process. It is advisable to perform the installation during off-peak hours to minimize the risk of causing downtime for websites.
+:::
+
+To install ModSecurity click on the 'Install' button.
+
+![openadmin modsec install](/img/admin/adminpanel_modsec_install.png)
+
+Or from terminal run: [opencli nginx-install_modsec](/docs/admin/scripts/webserver#install-modsecurity)
+
+## Activate ModSecurity
+
+Upon ModSecurity installation, all new domains will have ModSecurity enabled by default. However, individual users can choose to disable ModSecurity for their domains at any time through their OpenPanel interface. [More information](/docs/panel/advanced/server_settings#modsecurity-settings)
+
+
+## Customize ModSecurity rules
+
+Adjusting ModSecurity rules means fine-tuning security settings for your specific needs, giving administrators the power to better protect against specific threats and reduce false positives.
+
+You can follow user-friendly guides to easily customize ModSecurity rules, adapting security settings to your specific needs.
+
+- [Nginx Docs: Using the OWASP CRS with the NGINX ModSecurity WAF](https://docs.nginx.com/nginx-waf/admin-guide/nginx-plus-modsecurity-waf-owasp-crs/)
+- [Nginx Docs: Using the ModSecurity Rules from Trustwave SpiderLabs with the NGINX ModSecurity WAF](https://docs.nginx.com/nginx-waf/admin-guide/nginx-plus-modsecurity-waf-trustwave-spiderlabs-rules/)
+- [ModSecurity Documentation](https://github.com/SpiderLabs/ModSecurity/wiki)
+- [ProSec Blog: Modsecurity Core Rule Sets and Custom Rules](https://www.prosec-networks.com/en/blog/modsecurity-core-rule-sets-und-eigene-regeln/)
+
+## Enable ModSecurity for existing domains
+
+After installing ModSecurioty only new domains that users add will by default have ModSecurity activate, and for existing users this process can be performed by the administrator from this page or from each user panel individually. To enable ModSecurity on all domains owneed by a user, select the user anc click on 'Enable' button.
+
+![openadmin modsec settings](/img/admin/adminpanel_modsec_use.png)
+
+Or from terminal run: [opencli domains-enable_modsec](/docs/admin/scripts/domains#enable-modsecurity)
+
+

+ 7 - 0
documentation/docs/admin/users/_category_.json

@@ -0,0 +1,7 @@
+{
+  "label": "Users",
+  "position": 2,
+  "link": {
+    "type": "generated-index"
+  }
+}

+ 44 - 0
documentation/docs/admin/users/openadmin.md

@@ -0,0 +1,44 @@
+---
+sidebar_position: 1
+---
+
+# OpenAdmin Users
+
+The admin panel has two user roles:
+
+- **Super Admin** that gets created when OpenPanel is installed and can add other users, with full control over the server.
+- **Admin** - sub-admin users that are created by the SuperAdmin and have access to everything that the SuperAdmin has but can not edit the ‘Super Admin’ user.
+
+## Manage Admin users
+
+To manage admin users that can access OpenAdmin interface use Settings > OpenAdmin page
+
+![openadmin admin users](/img/admin/openadmin_admin_page.png)
+
+Or from the terminal:
+
+- [opencli admin new](/docs/admin/scripts/admin#create-new-admin)
+- [opencli admin password](/docs/admin/scripts/admin#reset-admin-password)
+- [opencli admin delete](/docs/admin/scripts/admin#delete-admin-user)
+
+## Reset Admin Password
+
+To reset admin password click on the user in Settings > OpenAdmin page, then click on Edit button and set the password.
+
+Or from the terminal: [opencli admin password](/docs/admin/scripts/admin#reset-admin-password)
+
+## Create new Admin
+
+To create new admin user click on the 'New' button in Settings > OpenAdmin page, set the username and password and click on save.
+
+Or from the terminal: [opencli admin new](/docs/admin/scripts/admin#create-new-admin)
+
+## Delete Admin user
+
+Select the user on Settings > OpenAdmin page and click on the delete button then confirm.
+
+Or from the terminal: [opencli admin delete](/docs/admin/scripts/admin#delete-admin-user)
+
+:::info
+The Super Admin user can not be deleted.
+:::

+ 199 - 0
documentation/docs/admin/users/openpanel.md

@@ -0,0 +1,199 @@
+---
+sidebar_position: 1
+---
+
+# OpenPanel Users
+
+OpenPanel currently has only a single user role named **User** that can only manage their docker container and inherits settings specified by the Admin user.
+
+
+## List Users
+
+To access all OpenPanel users, navigate to OpenAdmin > Users.
+
+The Users page displays a table showcasing each user's Gravatar linked to their email address, username, assigned IP Address, hosting plan name, creation date of the account, a login link enabling user impersonation, and an actions dropdown. In the actions dropdown, you can perform actions such as editing, suspending, or deleting the user.
+
+![openadmin users page](/img/admin/openadmin_users_page.png)
+
+Or from the terminal: [opencli user-list](/docs/admin/scripts/users#list-users)
+
+Suspended users are highlighted in red, and no actions can be performed on a suspended user.
+
+## Create Users
+
+To create a new user, click on the 'New User' button on the Users page. A new section will be displayed with a form where you can set the email address, username, generate a strong password, and assign a hosting plan for the user.
+
+![openadmin users add new](/img/admin/openadmin_add_new_user.png)
+
+Or from the terminal: [opencli user-add](/docs/admin/scripts/users#add-user)
+
+## Reset User Password
+
+To reset password for a user click on the Edit dropdown in table for that user in OpenAdmin > Users or from the individual User page click on "Edit information" and set the new password in the Password field then save.
+
+Step 1.             |  Step 2.
+:-------------------------:|:-------------------------:
+![openadmin users reset password step 1](/img/admin/openadmin_users_edit_information.png)  |  ![openadmin users reset password step 2](/img/admin/openadmin_users_edit_information_change_password.png)
+
+Or from the terminal: [opencli user-password](/docs/admin/scripts/users#change-password)
+
+
+## Detailed User Information
+
+To view detailed information about the account click on the Gravatar image or the username in the users table.
+
+This page shows detailed information about the account and provides tools to manage it.
+
+![openadmin users single user view](/img/admin/openadmin_users_single_user_view.png)
+
+The username is displayed at the top, along with the status of the Docker container for the user. Colors indicate whether the user is suspended or if the Docker container has encountered an error. Next to the username, there are buttons that allow you to suspend/unsuspend the user, delete the user, a configure button to edit user settings inside their Docker container, and a 'Login as user' button that automatically logs you into their OpenPanel interface.
+
+![openadmin users single user view 1](/img/admin/user_1.png)
+
+There are 4 widgets on top of the page:
+
+![openadmin users single user view 2](/img/admin/user_2.png)
+
+- **CPU Usage:** Shows the current CPU usage of all processes by the user, represented in percentage with color indicators.
+- **Memory Usage:** Displays the current memory usage of the Docker container, represented in percentage with color indicators.
+- **Disk Usage:** Shows the current disk usage of the user's Docker container, represented in percentage with color indicators.
+- **IP Address:** Displays the public IPv4 address for the user, indicating whether the user has a Dedicated IP address assigned.
+
+----
+
+The next section is divided into two parts: tabs and widgets.
+
+There are 6 tabs that allow you to view relevant information about the user's Docker container:
+
+### Docker
+
+![openadmin users single user view docker_tab](/img/admin/docker_tab.png)
+
+The Docker tab displays information about the Docker container for the user, including:
+
+- **Server:** Server on which the container is running.
+- **Private IP:** Private IP address within the plan network that the container is using.
+- **ID:** Unique container ID for that user container.
+- **Memory Allocated:** RAM allocated to the user container (can be manually extended even beyond the plan limit).
+- **CPU:** Number of CPU cores allocated to the container (can also be increased outside the plan limits).
+- **Docker Image:** The name of the image (Nginx or Apache) that the container is using and the OS in the image (Debian or Ubuntu).
+- **Hostname:** Hostname that the user sees via SSH in their Docker container (same as your server name).
+- **Exposed Ports:** Ports inside the Docker container that accept incoming connections (e.g., ports for SSH, MySQL, REDIS, Apache).
+- **Container Created:** Timestamp when the container was started (may be different from the account creation date).
+
+----
+
+### Websites
+
+The Websites tab will display all domains and websites that the user has inside their Docker container.
+
+- **Domains table** showcases information such as domain name, root directory, and links to view the access log for the domain, edit DNS records, and edit the VirtualHost file for Nginx associated with the domain.
+- **Websites table** displays the website URL, type (WordPress, Node.js, or Python), CMS version, and the time when the user installed or added it to the WP ;anager/PM2 interfaces.
+
+![openadmin users single user view websites tab](/img/admin/websites_tab.png)
+
+----
+
+### Services
+
+The Services tab displays a list of all services installed inside the user's Docker container, along with their current status. You have options to start, stop, or restart a service.
+
+![openadmin users single user view services tab](/img/admin/services_tab.png)
+
+----
+
+### Backups
+
+The Backups tab displays a list of all available backups for the user account, showcasing backup content and sizes.
+
+![openadmin users single user view backups tab](/img/admin/backups_tab.png)
+
+----
+
+### Usage
+
+The Usage tab will display Docker container stats for the user, including CPU usage, memory percentage used at that moment, network I/O, and total block I/O. This information is the same to what users can view from OpenPanel > Resource Usage.
+
+![openadmin users single user view usage tab](/img/admin/usage_tab.png)
+
+----
+
+### Activity
+
+The Activity tab shows the user's account activity log, providing the same information users can view from OpenPanel > Account Activity page.
+
+![openadmin users single user view activity tab](/img/admin/activity_tab.png)
+
+----
+
+### General information
+
+General information widget displays the general information about the user and their container:
+
+![openadmin users single user view general info](/img/admin/general_info_tab.png)
+
+- **User ID:** Unique ID for the user account.
+- **Email Address:** Current email address for the user.
+- **Two Factor:** Indicates whether 2FA is enabled by the user.
+- **Hosting Plan:** Name of the hosting plan assigned to the user.
+- **IP Address:** Public IPv4 address for the user.
+- **Server Location:** Flag and country name indicating the geolocation of the IP address.
+- **Private IP:** Private IP address for the Docker container used in internal networking.
+- **Setup Date:** Date when the user account was created.
+- **Domains:** Number of domains that the user has added.
+- **Websites:** Number of websites that the user has added.
+
+To edit any information for the user, click on the 'Edit Information' link, and a new modal will be displayed where you can change the username, email, plan, IP, or password.
+
+![openadmin users single user edit_info_tab](/img/admin/edit_info_tab.png)
+
+----
+
+### Ports
+
+The Ports widget displays all ports published in the user's Docker container and corresponding randomly generated ports for the user on the host server machine.
+
+![openadmin users single user ports_tab](/img/admin/ports_tab.png)
+
+
+## Suspend User
+
+Suspending an account will immediately disable the user's access to the OpenPanel. This action involves pausing the user's Docker container and revoking access to their email, website, and other associated services. Please be aware of the immediate impact before proceeding.
+
+To suspend a user click on the Suspend button on that user page and click on 'Suspend' on the confirmation modal.
+
+Step 1.             |  Step 2.
+:-------------------------:|:-------------------------:
+![openadmin users suspend step 1](/img/admin/openadmin_users_suspend_1.png)  |  ![openadmin users suspend step 2](/img/admin/openadmin_users_suspend_2.png)
+
+Or from the terminal: [opencli user-suspend](/docs/admin/scripts/users#suspend-user)
+
+## Unsuspend User
+
+To unsuspend a user click on the Unsuspend button for that user.
+
+![openadmin users add new](/img/admin/openadmin_users_unsuspend.png)
+
+## Change IP address for User
+
+To change IP address for a user, click on the 'Edit Information' link for the user, then elect the new IP address and click on 'Save changes'.
+
+Step 1.             |  Step 2.
+:-------------------------:|:-------------------------:
+![openadmin users change ip address step 1](/img/admin/openadmin_users_edit_information.png)  |  ![openadmin users change ip address step 2](/img/admin/openadmin_users_edit_information_change_ip_address.png)
+
+Or from the terminal: [opencli user-ip](/docs/admin/scripts/users#assign--remove-ip-to-user)
+
+## Delete User
+
+To delete a user click on the delete button for that user, then type 'delete' in the confirmation modal and finally click on the 'Terminate' button.
+
+Step 1.             |  Step 2.
+:-------------------------:|:-------------------------:
+![openadmin users delete step 1](/img/admin/openadmin_users_delete_1.png)  |  ![openadmin users deletes step 2](/img/admin/openadmin_users_delete_2.png)
+
+Or from the terminal: [opencli user-delete](/docs/admin/scripts/users#delete-user)
+
+:::warning
+This action is irreversible and will permanently delete all user data.
+:::

+ 144 - 0
documentation/docs/changelog/0.1.md

@@ -0,0 +1,144 @@
+---
+---
+
+## 0.1.5-alpha.1
+
+Not yet released
+
+### 🚀 New features
+- [Notifications Center](https://community.openpanel.co/d/13-introducing-notifications-center)
+- Administrators can now [customize the generated Domain Access Logs reports](https://community.openpanel.co/d/6-issues-with-domain-access-logs)
+- [Custom nameservers can now be added](/docs/admin/scripts/openpanel_config#nameservers) and will automatically be used in the dns zone template for new domains
+- Administrators can now [set custom how-to articles](/docs/admin/scripts/openpanel_config#how_to_guides) to be displayed in user dashboard
+- Docker images now support all PHP verisons from 5.6 to 8.3
+- Administrators can now change the default `/openpanel` proxy to anything they want using the: [openpanel_proxy](/docs/admin/scripts/openpanel_config#openpanel_proxy) setting
+- [opencli admin list](/docs/admin/scripts/admin#list-admin-users), [opencli admin notifications](/docs/admin/scripts/admin#notifications), [opencli user-redis](/docs/admin/scripts/users#redis), [opencli user-memcached](/docs/admin/scripts/users#memcached), [opencli backup-config](/docs/admin/scripts/backup#config), [opencli backup-destination](/docs/admin/scripts/backup#destination), [opencli backup-logs](/docs/admin/scripts/backup#logs), [opencli backup-job](/docs/admin/scripts/backup#job)
+- [OpenAdminAPI](/docs/admin/customize/api)! 🎉
+- Implemented support for previewing .webp and .avif files in File Manager.
+
+### 💥 Breaking changes
+- Storage driver [devicemapper is replaced with overlay2](https://community.openpanel.co/d/8-switching-docker-storage-engine-from-devicemapper-to-overlay2-storage) as the default storage driver for Docker
+- PHP 7.4 is removed as the pre-installed verison in docker containers, PHP 8.3 is now the only version that is pre-installed.
+- New users will have REDIS 7.2 instead of 6.3
+- Usernames must consist of a minimum of 3 characters and a maximum of 20 characters, only numbers and letters are allowed.
+- Remote Backups with SSH key-based authentication
+
+
+### 🐛 Bug fixes
+- Fixed [bug with bind9 configuration](https://community.openpanel.co/d/5-dns-server-does-not-respond-to-request-for-domain-zone) that caused the DNS server not to respond to request for domain zone
+- Fixed bug in [opencli config update](/docs/admin/scripts/openpanel_config#update) not restarting the service for major system changes
+- Fixed bug with bind9 service restart instead of reload when adding new domains
+- Fixed bug with 500 error on dashboard page if invalid JSON data in knowledge_base_articles.json
+- Fixed bug 'create command denied for user' when creating tables in phpMyAdmin
+- Fixed bug 'unexpected 'static' (T_STATIC)' in phpMyAdmin minimum required php version
+- Fixed bug in dashboard page with wrong color indicators for current CPU and RAM usage
+- Fixed 500 error in 'Resource Usage History' page caused by the % symbol in localization strings
+- Fixed 404 error for 'Domain Logs' page link in the search results
+- Fixed bug with free memory usage not being returned from containers back to the host server
+- Fixed bug with container private ip address showing in domain logs
+
+### 💅 Polish
+- The new template is now fully localized and [can be translated to any language](https://github.com/stefanpejcic/openpanel-translations)
+- REDIS, Memcached and Elasticsearch are now not preinstalled, but can be installed by the user with a single click.
+- Account Activity now highlights actions performed by the Administrator user.
+- `panel` and `admin` services now support reload command.
+- KEYPHRASES are now disabled in generated html reports from domain logs.
+- Search results in FileManager now prioritize Files and Folders.
+- Quick start guide added to OpenAdmin dashboard.
+- 
+
+
+
+---
+
+## 0.1.4
+
+Released on December 25, 2023
+
+### 💅 Polish
+
+- [Admini bootstrap template](https://github.com/lekoala/admini) integrated as the new default template for the OpenPanel User Interface
+
+### 🚀 New features
+- Autocomplete for OpenCLI scripts
+- OpenCLI commands: [admin](https://openpanel.co/docs/admin/scripts/admin) [admin off](https://openpanel.co/docs/admin/scripts/admin#enable--disable-adminpanel) [admin on](https://openpanel.co/docs/admin/scripts/admin#enable--disable-adminpanel) [admin new](https://openpanel.co/docs/admin/scripts/admin#create-new-admin) [admin password](https://openpanel.co/docs/admin/scripts/admin#reset-admin-password) [opencli admin rename](https://openpanel.co/docs/admin/scripts/admin#rename-admin-user) [opencli admin delete](https://openpanel.co/docs/admin/scripts/admin#delete-admin-user) [opencli plan delete](https://openpanel.co/docs/admin/scripts/plans#delete-plan) [opencli user email change](https://openpanel.co/docs/admin/scripts/users#change-email) [opencli nginx-install_modsec](https://openpanel.co/docs/admin/scripts/webserver#install-modsecurity) [opencli domains-enable_modsec](https://openpanel.co/docs/admin/scripts/domains#enable-modsecurity) [opencli server_info](https://openpanel.co/docs/admin/scripts/admin#server_info) [opencli files-fix_permissions](https://openpanel.co/docs/admin/scripts/files#fix_permissions) [opencli docker-usage_stats_cleanup](https://openpanel.co/docs/admin/scripts/docker#usage_stats_cleanup) [opencli docker-is_port_in_use](https://openpanel.co/docs/admin/scripts/docker#is-port-in-use)
+- [goaccess](https://github.com/allinurl/goaccess) integrated
+- [ModSecurity](https://github.com/SpiderLabs/ModSecurity-nginx) with [OWASP core ruleset](https://github.com/coreruleset/coreruleset)
+- Edit general options for WPManager
+- Set WordPress update preferences for core, plugins and themes inside the WPManager
+- Set debugging options for a website inside the WPManager
+- Update WordPress core to latest version using the WPManager
+- Refresh data option for WP Manager to re-scan existing installations
+- Process Manager
+- Change TimeZone
+- Nginx / Apache Configuration Editor
+- ModSecurity Settings
+- Elasticsearch
+- ClamAV Scanner (BETA)
+- Fix Permissions
+- IP blocking per domain
+
+### 🐛 Bug fixes
+ - Fixed bug where the menu sidebar would display while in File Editor full screen mode.
+ 
+### 💥 Breaking changes
+- [custom nginx error pages](https://github.com/denysvitali/nginx-error-pages) added to the Nginx configuration
+- HTTP/2 enabled by default in Nginx
+
+---
+
+## 0.1.3
+
+Released on November 30, 2023
+
+### 💥 Breaking changes
+- [All OpenCLI scripts](/docs/category/openpanel-cli) are now converted to binaries
+
+### 🚀 New features
+- OpenCLI commands: [user-list](https://openpanel.co/docs/admin/scripts/users#list-users) [user-rename](https://openpanel.co/docs/admin/scripts/users#rename-user) [user-password](https://openpanel.co/docs/admin/scripts/users#change-password) [user-ip](https://openpanel.co/docs/admin/scripts/users#assign--remove-ip-to-user) [plan-list](https://openpanel.co/docs/admin/scripts/plans#list-plans) [plan-create](https://openpanel.co/docs/admin/scripts/plans#create-plan) [plan-usage](https://openpanel.co/docs/admin/scripts/plans#list-users-on-plan)
+- MySQL Processes
+
+---
+
+## 0.1.2
+
+Released on November 23, 2023
+
+### 🚀 New features
+- OpenCLI commands: [ssl-hostname](https://openpanel.co/docs/admin/scripts/users#list-users) [ssl-user](https://openpanel.co/docs/admin/scripts/users#list-users) [user-list](https://openpanel.co/docs/admin/scripts/users#list-users) [user-login](https://openpanel.co/docs/admin/scripts/users#login-as-user) [user-loginlog](https://openpanel.co/docs/admin/scripts/users#list-users)
+- Set dedicated IP address for user in Nginx
+- HTTP/2 and GZIP support for Nginx
+- Access logs per domain name using GoAccess log analyzer
+- SSL status detection for WP Manager
+- Edit MySQL Configuration
+- Dark mode toogle based on users OS or preference
+- 
+### 🐛 Bug fixes
+- Fixed bug on WP Manager to not show subdirectories backups for main domain
+- Reverse sort of logs on the login history 
+
+---
+
+## 0.1.1
+
+Released on October 26, 2023
+
+### 🔧 Maintenance
+
+- Ubuntu Docker images updated on [hub.openpanel.co](https://hub.openpanel.co/_/ubuntu_22.04/)
+
+### 🐛 Bug fixes
+
+- Fixed bug with removing certificates during nginx reload
+- Fixed incorrect group permissions for wordpress files after ibstalling from WP Manager
+- Fixed style bug with Bootstrap4 modals not being triggered using Bootstrap5 names
+- Restrict PHP-FPM permissions to a new user to prevent permission escalation
+
+---
+
+## 0.1.0
+
+Released on July 18, 2023
+
+🎉🎉🎉 Initial release 🎉🎉🎉
+

+ 32 - 0
documentation/docs/changelog/intro.md

@@ -0,0 +1,32 @@
+---
+sidebar_position: 1
+sidebar_label: RELEASES
+sidebar_class_name: green
+---
+
+# Changelog
+
+### Next version (Unreleased)
+
+|   |
+|---|
+|__[0.1.5-alpha1](/docs/changelog/0.1#015-alpha1)__|
+
+### Current version (Stable)
+
+|   |
+|---|
+|__[0.1.4](/docs/changelog/0.1#014)__|
+
+### Past versions (Not maintained anymore)
+
+| 0.1.X |
+|---|
+| [0.1.3](/docs/changelog/0.1#013) |
+| [0.1.2](/docs/changelog/0.1#012) |
+| [0.1.1](/docs/changelog/0.1#011) |
+| [0.1.0](/docs/changelog/0.1#010) |
+
+:::info
+If version is not in the list, it means that update was done for technical reasons and does not contain any critical changes.
+:::

+ 23 - 0
documentation/docs/enterprise-edition/okta/index.md

@@ -0,0 +1,23 @@
+---
+title: Okta Auth Provider
+---
+
+# Okta Auth Provider
+
+Okta is an enterprise-grade identity management service. Refine's integration of Okta's authentication services allows you to easily add Okta services to your application.
+
+## Installation
+
+This package is included in Refine's Enterprise Edition. To learn more about Refine's Enterprise Edition, please [contact us](https://s.refine.dev/okta-enterprise).
+
+<InstallPackagesCommand args="@refinedev-ee/okta @okta/okta-auth-js">
+
+```yml title=".npmrc"
+# A registry with the auth token should be added for the @refinedev-ee scope
+@refinedev-ee:registry=https://registry.npmjs.org/
+//registry.npmjs.org/:_authToken=$NPM_TOKEN
+```
+
+</InstallPackagesCommand>
+
+## Usage

+ 72 - 0
documentation/docs/panel/000_intro.md

@@ -0,0 +1,72 @@
+---
+sidebar_position: 1
+---
+
+# Get Started with OpenPanel
+
+OpenPanel offers a distinct advantage over other hosting panels by providing each user with an isolated environment and tools to fully manage it. This ensures that you enjoy full control over your environment, simillar to a VPS experience. You can effortlessly install new PHP versions, modify server configurations, view domain logs, restart services, and perform numerous other advanced tasks.
+
+![openpanel scheme](/img/admin/openpanel_scheme.png)
+
+This panel is the culmination of years of experience in the hosting industry, having spent decades working with various hosting panels we  made sure to include all features that simply make sense.
+
+When we designed OpenPanel, we prioritized features that are not only user-friendly for beginners but also advanced enough to alleviate maintenance tasks for system administrators and hosting support teams.
+
+
+Some of the unique features worth mentioning are:
+
+
+- You can [install PHP versions](/docs/panel/advanced/server_settings#install-php-version) you need, [edit php.ini files](/docs/panel/advanced/server_settings#phpini-editor) and set desired limits.
+- Control [MySQL settings](/docs/panel/advanced/server_settings#mysql-settings), set limits, [enable remote mysql access](/docs/panel/databases/remote) and much more.
+- [Update system services](/docs/panel/advanced/server_settings#service-status) and even install new services that you need.
+- Manage WordPress websites easily with [WP Manager](/docs/panel/applications/wordpress).
+- Password-less login to [phpMyAdmin](/docs/panel/databases/phpmyadmin) and [Web Terminal](/docs/panel/advanced/terminal).
+- Built-in [REDIS](/docs/panel/caching/Redis) and [Memcached](/docs/panel/caching/Memcached) object caching.
+
+
+## Compare OpenPanel
+
+| Feature / Panel               | OpenPanel   | cPanel   | Plesk | CyberPanel  |
+|-------------------------------|-------------|----------|---------|---|
+| Upload file size limit in FileManager |      ∞       |     2GB     |     2GB    |  200MB |
+| Upload multiple files at once      |      ✔️        |    ❌      |yes | ❌  |
+| Upload files in background |       ✔️       |     ❌     |    ❌     |  ❌ |
+| Ajax search |      ✔️        |      ❌    |    ❌     | ❌  |
+| Restore file premissions in bulk |      ✔️        |      1 file at a time    |    1 file at a time     | 1 file at a time  |
+| View total folder size in FileManager |       ✔️      |     ❌      |     ❌    |  yes | ❌  |
+| Remote MySQL per account                | ✔️  | ❌ |❌|❌|
+| View MySQL processes |      ✔️        |     ❌     |    ❌     |  ❌ |
+| Change panel port             | ✔️         | ❌       | ❌      | ❌  |
+| Change panel access path                 | ✔️  | ❌ |    ❌     |  ❌ |
+|  Force https   |      ✔️        |    ✔️       |    ❌     |  ❌ |
+| Dark Mode  |     ✔️         |      ❌    |     ✔️     | ❌  |
+| Object Caching  |     REDIS and Memcached        |      ❌    |     ❌    | ❌  |
+| Elasticsearch suport   |      ✔️        |     ❌     |     ❌    | ❌  |
+| SSH Access per account  |      ✔️        |     ❌ *(per server)     |     ❌ *(per server)    | ❌ *(per server)  |
+| Install new PHP versions |      ✔️        |     ❌     |     ❌    | ❌  |
+| Change TimeZone for account  |      ✔️        |     ❌     |     ❌    | ❌  |
+| View services status  |      ✔️        |     ✔️     |     ✔️     | ❌  |
+| Restart server services  |      ✔️        |     ❌     |     ❌    | ❌  |
+| Edit MySQL configuration for account |      ✔️        |     ❌     |     ❌    | ❌  |
+| Edit server configuration for account |      ✔️        |     ❌     |     ❌    | ❌  |
+| Nginx or Apache webserver per account |      ✔️        |     ❌     |     ❌    | ❌  |
+| WAF |      ✔️        |     ❌ *(only with ModSecurity)    |     ❌    | ❌  |
+| Edit PHP.INI file |      ✔️        |     limited    |     limited    | limited  |
+| PHP version per domain name|      ✔️        |     ✔️    |     ✔️    | ❌  |
+| View resource usage  |      ✔️        |     ❌ *(only with Cloudlinux)     |     ❌    | ❌  |
+| Activity log per account  |      ✔️        |     ❌     |     ❌    | ❌  |
+| Detailed login log  |      ✔️        |     ❌     |     ❌    | ❌  |
+| View inodes usage per folders  |      ✔️        |     ❌ *(only total count for account)     |     ❌ *(only total count for account)     | ❌  |
+| Completelly isolated user accounts  |      ✔️        |     ❌ *(only with Cloudlinux)     |     ❌ *(only with Cloudlinux)     | ❌  |
+| Keyboard shortcuts  |      ✔️        |     ❌ *(only in file editor)     |     ❌    | ❌ |
+| Display custom messages to users |      ✔️        |     ❌ *(only for all users)     |     ❌ *(only for all users)    | ❌ |
+| Localization ready and custom templates  |      ✔️        |     ❌ *(limited style editing)     |     ❌ *(limited style editing)    | ❌ *(limited style editing)  |
+
+
+---
+
+## Support
+
+Our [Community](https://community.openpanel.co/) serves as our virtual Headquater, where the community helps each other.
+
+**Learn, share** and **discuss** with other community members your questions.

+ 21 - 0
documentation/docs/panel/account/2fa.md

@@ -0,0 +1,21 @@
+---
+sidebar_position: 2
+---
+
+# Two-Factor Authentication
+
+The Two-Factor Authentication is a recommended security feature that allows you to set up a second factor device. Essentially, during login, it requires not only your password but also a code from an application on your mobile phone.
+
+## Enable 2FA
+
+To enable 2FA for your acocunt click on the 'Enable 2FA' button.
+
+![2fa_1.png](/img/panel/v1/account/2fa_1.png)
+
+A QR code will be displayed that you can scan with your phone using a selected application such as Google Authenticator or DuoMobile. Alternatively, you can click on the 'Display OTP code' link to show a code that you can manually type in or copy into the application.
+
+![2fa_2.png](/img/panel/v1/account/2fa_2.png)
+
+Once you have set the QR code or OTP code in your application, it's essential to click on the 'Confirm' button to permanently hide the OTP and QR codes. Otherwise, anyone who accesses your account can view these codes and set them in their application.
+
+![2fa_3.png](/img/panel/v1/account/2fa_3.png)

+ 7 - 0
documentation/docs/panel/account/_category.json

@@ -0,0 +1,7 @@
+{
+  "label": "Account",
+  "position": 7,
+  "link": {
+    "type": "generated-index"
+  }
+}

+ 13 - 0
documentation/docs/panel/account/account.md

@@ -0,0 +1,13 @@
+---
+sidebar_position: 1
+---
+
+# Account
+
+The Account page allows you to change email and password, toggle [Dark Mode](/docs/panel/dashboard/dark-mode), enable [2FA](/docs/panel/account/2fa) and view account [Login History](docs/panel/account/login_history).
+
+![account_1.png](/img/panel/v1/account/account_1.png)
+
+To enable Dark Mode click on the icon:
+
+![account_2.png](/img/panel/v1/account/account_2.png)

+ 42 - 0
documentation/docs/panel/account/login.md

@@ -0,0 +1,42 @@
+---
+sidebar_position: 9
+---
+
+# Login and Password Reset
+
+By default, the port on which OpenPanel is accessible is 2083, so you should be able to access your account under `IP:2083` or `DOMAIN:2083`
+
+However, the port can be changed by the administrator, if that is the case, you would log in via `IP:custom-port` or `DOMAIN:custom-port`
+
+## Login
+
+To log in to OpenPanel, you need a username and password.
+
+![login.png](/img/panel/v1/account/login.png)
+![login_failed.png](/img/panel/v1/account/login_failed.png)
+
+If 2FA is enabled on your account, you will also be prompted to provide the 2FA code after providing the correct username and password.
+
+![login_2fa.png](/img/panel/v1/account/login_2fa.png)
+
+
+## Password Reset
+
+In case you forgot your OpenPanel password, it can be reset in the following ways:
+
+- From the "Forgot Password" link on the login form. *If enabled by the Administrator.
+- From the "Account > Settings" when you are logged in.
+- Manually by the Administrator. 
+
+If you forgot your password and the password reset option is enabled on the server, you can use your email address to reset the password.
+
+Simply click on the 'Forgot Password?' link and input your email address. Within seconds, you will receive a reset link to your email. Click on the reset link within the next 30 minutes to set a new password.
+
+![reset page](/img/reset_pass.png)
+
+In case you do not see the "Forgot Password" link on the login form, this means that the option is disabled by the Administrator, and you need to contact them to perform the password reset for you.
+
+
+## Logout
+
+To log out of the OpenPanel, simply click on the logout link under your profile.

+ 14 - 0
documentation/docs/panel/account/login_history.md

@@ -0,0 +1,14 @@
+---
+sidebar_position: 4
+---
+
+# Login History
+
+By default, OpenPanel records your IP address when you log in and displays the last login IP address on the Dashboard page.
+
+The Login History page displays a list of up to the last 20 logins in a table format. It includes the IP address, the time of the successful login, a two-letter country code representing the country of the IP address, and a flag icon to visualize the country.
+
+![login_history.png](/img/panel/v1/account/login_history.png)
+
+
+The purpose of this page is to help you determine if your account has been compromised. If you notice an IP address that you do not recognize, check the [Activity logs](/docs/panel/analytics/account_activity) for that IP to review the actions it performed on your account. If you notice any suspicious activity, immediately [change your account password](/docs/panel/account/login#password-reset) and contact your hosting provider.

+ 7 - 0
documentation/docs/panel/advanced/_category.json

@@ -0,0 +1,7 @@
+{
+  "label": "Advanced",
+  "position": 13,
+  "link": {
+    "type": "generated-index"
+  }
+}

+ 19 - 0
documentation/docs/panel/advanced/advanced.md

@@ -0,0 +1,19 @@
+---
+sidebar_position: 1
+---
+
+# Advanced
+
+The Advanced page allows you to view and edit server configuration. 
+
+:::warning
+This section is for advanced users only. Exercise caution while making changes, as improper configurations can impact server performance and functionality."
+:::
+
+Available pages:
+
+- [CronJobs](/docs/panel/advanced/cronjobs)
+- [SSH Access](/docs/panel/advanced/ssh)
+- [Web Terminal](/docs/panel/advanced/terminal)
+- [Process Manager](/docs/panel/advanced/process_manager)
+- [Server Settings](/docs/panel/advanced/server_settings)

+ 54 - 0
documentation/docs/panel/advanced/cronjobs.md

@@ -0,0 +1,54 @@
+---
+sidebar_position: 1
+---
+
+# CronJobs
+
+A cron job is a Linux command used to schedule tasks for future execution. It allows you to automate repetitive tasks, such as sending notifications or running scripts at specific intervals.
+
+![cronjobs_list.png](/img/panel/v1/advanced/cronjobs_list.png)
+
+On the CronJobs page you can view currently scheduled tasks, create new, edit or delete them.
+
+:::info
+The TimeZone setting is handy for running scheduled [cronjobs](/docs/panel/advanced/cronjobs) in your local timezone.
+:::
+
+
+## Add a CronJob
+
+To create a new cronjob click on the 'Create CronJob' button and in the new form set the script to be executed and desired schedule.
+
+![cronjobs_new.png](/img/panel/v1/advanced/cronjobs_new.png)
+
+The first field allows you to set a predefined schedule:
+
+- Once per minute
+- Once per 5 minutes
+- Twice per hour 
+- Once per hour
+- Twice per day
+- Once per day
+- Once per week
+- Twice per month (every 1st and 15th of the month)
+- Once per month
+- Once per year
+
+![cronjobs_new_predefined.png](/img/panel/v1/advanced/cronjobs_new_predefined.png)
+
+
+## Edit a CronJob
+
+To edit an existing cronjob, click on the 'Edit' button next to it. This action will open a modal displaying the current cronjob as saved in the crontab file.
+
+![cronjobs_edit.png](/img/panel/v1/advanced/cronjobs_edit.png)
+
+To modify the schedule for when the script is executed, update the first part of the script. You can configure the schedule using a tool such as https://crontab.guru/
+
+To alter the script that is executed, modify the part following the cron schedule.
+
+## Delete a CronJob
+
+To delete a cronjob, click on the 'Delete' button next to it. This action will open a modal asking you to confirm the deletion.
+
+![cronjobs_delete.png](/img/panel/v1/advanced/cronjobs_delete.png)

+ 19 - 0
documentation/docs/panel/advanced/process_manager.md

@@ -0,0 +1,19 @@
+---
+sidebar_position: 4
+---
+
+# Process Manager
+
+The process manager interface allows you to view all running processes and kill (force stop the process).
+
+![process_manager.png](/img/panel/v1/advanced/process_manager.png)
+
+Processes are sorted by their current CPU % usage.
+
+Due to security considerations, certain processes, such as [phpMyAdmin](/docs/panel/databases/phpmyadmin) and [Web Terminal](/docs/panel/advanced/terminal), which involve sensitive data like passwords in their command line attributes, have been substituted with placeholders (xxxxxxxx) in the CMD column.
+
+To terminate a process click on the 'kill' button next to it.
+
+:::danger
+**NOTE:** Stopping MySQL, PHP-FPM, or Nginx/Apache processes may result in downtime for your websites. In the event that a service fails to restart after terminating the process, you can utilize the [Service Status](/docs/panel/advanced/server_settings#service-status) page to restart the appropriate service.
+:::

+ 248 - 0
documentation/docs/panel/advanced/server_settings.md

@@ -0,0 +1,248 @@
+---
+sidebar_position: 5
+---
+
+# Server Settings
+
+![server_settings.png](/img/panel/v1/advanced/server_settings.png)
+
+## Server Time
+
+The 'Change TimeZone' page allows you to view the current TimeZone and change it.
+
+To change a timezone for your server simply select the new timezone in the select and click on 'Change Timezone' button to save.
+
+![change_timezone.png](/img/panel/v1/advanced/change_timezone.png)
+
+:::info
+The TimeZone setting is handy for running scheduled [cronjobs](/docs/panel/advanced/cronjobs) in your local timezone.
+:::
+
+
+## Service Status
+
+The Services page provides information about the status and version of all your currently installed services. 
+
+![services_status.png](/img/panel/v1/advanced/services_status.png)
+
+It includes details about:
+
+- Apache or Nginx
+- PHP
+- MySQL
+- phpMyAdmin
+- NodeJS
+- Python
+- REDIS
+- Memcached
+- Elasticsearch
+
+From this page, you have the ability to view service versions, monitor their status, and restart MySQL or Nginx/Apache services.
+
+Each service's status is indicated by the "on" or "off" label displayed next to it.
+
+To restart MySQL or Nginx/Apache service click on the 'Restart' link next to it.
+This will force stop of the service process and immediatelly start it.
+
+![services_status_restart.png](/img/panel/v1/advanced/services_status_restart.png)
+
+## Nginx / Apache Settings
+
+The Nginx / Apache Configuration Editor page allows you to view and edit the main Apache/Nginx configuration file inside your docker container.
+
+![webserver_editor.png](/img/panel/v1/advanced/webserver_editor.png)
+
+- For Apache the main configuration file is: `/etc/apache2/httpd.conf`
+- For Nginx the main configuration file is: `/etc/nginx/nginx.conf`
+
+:::danger
+Editing this file is for advanced users. Make sure to create a backup before making any changes, as even a small syntax error can cause server restart failure and downtime for all your websites.
+:::
+
+## MySQL Settings
+
+Edit MySQL configuration
+
+![mysql_editor.png](/img/panel/v1/advanced/mysql_editor.png)
+
+These settings are used to configure various aspects of MySQL's service:
+
+- **max_allowed_packet**: Maximum size of the communication buffer between the client and server.
+- **max_connect_errors**: Maximum number of interrupted connections before the server blocks the host.
+- **max_connections**: Maximum number of simultaneous client connections.
+- **open_files_limit**: Limit on the number of file descriptors that mysqld should be allowed to use.
+- **performance_schema**: Whether to enable the Performance Schema.
+- **sql_mode**: SQL mode to set. Here it's set to ERROR_FOR_DIVISION_BY_ZERO.
+- **thread_cache_size**: Number of threads the server should cache for reuse.
+- **interactive_timeout**: The number of seconds the server waits for activity on an interactive connection before closing it.
+- **wait_timeout**: The number of seconds the server waits for activity on a connection before closing it.
+- **log_output**: Where to write the general query log. It's set to FILE.
+- **log_error**: Location of the error log.
+- **log_error_verbosity**: Verbosity level of the error log.
+- **general_log**: Whether to enable the general query log.
+- **general_log_file**: Location of the general query log file.
+- **long_query_time**: Time in seconds that a query must take to be considered slow.
+- **slow_query_log**: Whether to enable the slow query log.
+- **slow_query_log_file**: Location of the slow query log file.
+- **join_buffer_size**: Size of the buffer used for index scans.
+- **key_buffer_size**: Size of the buffer used for index blocks.
+- **read_buffer_size**: Size of the buffer used for sequential scans.
+- **read_rnd_buffer_size**: Size of the buffer used for random reads.
+- **sort_buffer_size**: Size of the buffer used for sorting.
+- **innodb_log_buffer_size**: Size of the buffer for InnoDB log writes.
+- **innodb_log_file_size**: Size of each InnoDB log file.
+- **innodb_sort_buffer_size**: Size of the buffer used for sorting in InnoDB.
+- **innodb_buffer_pool_chunk_size**: Size of each chunk in the InnoDB buffer pool.
+- **innodb_buffer_pool_instances**: Number of instances that the InnoDB buffer pool is divided into.
+- **innodb_buffer_pool_size**: Total size of the InnoDB buffer pool.
+- **max_heap_table_size**: Maximum size of in-memory temporary tables.
+- **tmp_table_size**: Size of in-memory temporary tables used for ALTER TABLE.
+
+Make sure to adjust these values based on your specific server requirements and workload. Additionally, regularly review the MySQL error log for any issues or warnings.
+
+:::warning
+ Modifying these values without proper understanding may impact MySQL performance and stability. Make backups and consult the [official MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/server-configuration.html) before making changes.
+:::
+
+
+## PHP Settings
+
+### Change PHP version for domain
+
+PHP versions tab allows you to view current PHP version for each domain and change the version per domain.
+
+To change a PHP version for a domain simply select new version and click on the 'Change PHP Version' button to save.
+
+![php_change_version_for_domain.png](/img/panel/v1/advanced/php_change_version_for_domain.png)
+
+:::warning
+Changing the PHP version will stop all the processes on your site. It takes 1-2 seconds to complete. Be sure to check your script and plugin requirements to know which PHP version works best for your website.
+:::
+
+### Set default PHP version
+
+This option allows you to set the default PHP version that will be used for newly added domains.
+
+![php_change_default_version_for_new_domains.png](/img/panel/v1/advanced/php_change_default_version_for_new_domains.png)
+
+You can also view current default version setting.
+
+:::info
+phpMyAdmin runs by default on the version that you set here, but due to phpMyAdmin's minimum requirements, if the PHP version is less than 8.0, then the [default PHP version defined by the administrator](/docs/admin/settings/openpanel) will be used instead.
+:::
+
+### Install PHP version
+
+To install a new PHP version simply select the version from the 'Select PHP Version to Install:' select and click on the 'Install' button to start the process.
+
+![php_install_new_version_1.png](/img/panel/v1/advanced/php_install_new_version_1.png)
+
+The process takes from 2-10 minutes to finish and you can view a real-time output of the installation process:
+
+![php_install_new_version_2.png](/img/panel/v1/advanced/php_install_new_version_2.png)
+
+Once the process is complete the new PHP version will be available for use.
+
+:::info
+For best performance we recommend to only install PHP versions that you will be activelly using.
+:::
+
+### View PHP extensions
+
+The PHP extensions tab lists all installed extensions for PHP versions in a same manner as a [phpinfo function](https://www.php.net/manual/en/function.phpinfo.php).
+
+![php_view_extensions.png](/img/panel/v1/advanced/php_view_extensions.png)
+
+### View PHP options
+
+The PHP options tab lists general information for each PHP verison from the php.ini file.
+
+![php_view_options.png](/img/panel/v1/advanced/php_view_options.png)
+
+Listed values:
+
+- max_execution_time
+- max_input_time
+- memory_limit
+- post_max_size
+- upload_max_filesize
+
+To modify any of these values simply click on the 'Edit PHP.INI' link for that version.
+
+Default values for these settings on all PHP versions installed via OpenPanel interface are:
+
+- max_execution_time = 600
+- max_input_time = 600
+- memory_limit = -1 (unlimited)
+- post_max_size = 1024M
+- upload_max_filesize = 1024M
+
+### PHP.INI Editor
+
+The primary configuration file for PHP is php.ini. A distinct php.ini file exists for each PHP version installed on the server. 
+
+![php_edit_phpini.png](/img/panel/v1/advanced/php_edit_phpini.png)
+
+With the php.ini editor, you can modify the PHP configuration for each version and configure settings such as:
+
+
+- Error Reporting: Control error reporting and handling, including displaying errors, error logging, and verbosity.
+- File Uploads: Configure settings related to file uploads, such as maximum file size, file type restrictions, and temporary directory.
+- Memory Management: Adjust memory limits for PHP scripts using settings like `memory_limit`.
+- Execution Time: Set the maximum script execution time with `max_execution_time`.
+- Display Errors: Choose whether to display errors in the browser using the `display_errors` setting.
+- Date and Time: Set the default time zone with `date.timezone`.
+- Session Handling: Configure session-related settings, including session save path and session cookie parameters.
+- Error Handling: Define custom error handling functions using `set_error_handler` and `set_exception_handler`.
+- Database Connections: Adjust settings related to database connections, like PDO or MySQL.
+- OPcache: Fine-tune PHP's opcode cache with settings like `opcache.enable` and `opcache.memory_consumption`.
+- Security: Enhance security with settings like `disable_functions`, which disables specific PHP functions, and `open_basedir` to restrict file system access.
+- Resource Limits: Control resource usage with settings like `max_input_vars`, which limits the number of input variables.
+- PHP Modules: Enable or disable PHP extensions and modules using settings like `extension`.
+and many more.
+
+[List of php.ini directives](https://www.php.net/manual/en/ini.list.php)
+
+
+
+#### Disabling dangerous functions
+
+To enhance the security of your PHP scripts and server, it's important to be cautious about allowing certain PHP functions that can be potentially dangerous.
+
+You can disable these functions for the PHP versions you're using by editing the php.ini files and setting:
+
+```bash
+disable_functions = system, system_exec, shell, shell_exec, exec, passthru, proc_close, proc_open, ini_alter, dl, popen, show_source, inject_code, mysql_pconnect, openlog, php_uname, phpAds_remoteInfo, phpAds_XmlRpc, phpAds_xmlrpcDecode, phpAds_xmlrpcEncode, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, posix_setuid, posix_uname, proc_get_status, proc_nice, proc_terminate, syslog, xmlrpc_entity_decode, apache_setenv, eval, pfsockopen, leak, apache_child_terminate
+```
+
+same can be achieved from the terminal by running the following command:
+
+
+```bash
+echo "disable_functions = system, system_exec, shell, shell_exec, exec, passthru, proc_close, proc_open, ini_alter, dl, popen, show_source, inject_code, mysql_pconnect, openlog, php_uname, phpAds_remoteInfo, phpAds_XmlRpc, phpAds_xmlrpcDecode, phpAds_xmlrpcEncode, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, posix_setuid, posix_uname, proc_get_status, proc_nice, proc_terminate, syslog, xmlrpc_entity_decode, apache_setenv, eval, pfsockopen, leak, apache_child_terminate" > /opt/alt/php-fpm73/usr/php/php.d/disabled_function.ini && service php-fpm73 restart
+```
+
+
+## ModSecurity Settings
+The ModSecurity settings page allows you to view current status for each domain and enable/disable ModSecurity WAF per domain.
+
+![modsecurity_settings.png](/img/panel/v1/advanced/modsecurity_settings.png)
+
+
+## Server Information
+
+The page offers a section where you can access Server Information.
+
+![server_info.png](/img/panel/v1/advanced/server_info.png)
+
+This includes:
+
+- Hostname
+- Average load
+- Uptime
+- IP Address
+- OpenPanel version
+- Operating System
+- Release and Version numbers
+- Processor architecture
+

+ 21 - 0
documentation/docs/panel/advanced/ssh.md

@@ -0,0 +1,21 @@
+---
+sidebar_position: 2
+---
+
+# SSH
+
+The SSH Access page provides the capability to check the current status of remote SSH access, which is by default disabled. Moreover, it allows you to toggle the enable/disable setting as needed.
+
+![ssh_enabled.png](/img/panel/v1/advanced/ssh_enabled.png)
+
+To enable SSH access click on the 'Enable SSH Access' button.
+
+![ssh_disabled.png](/img/panel/v1/advanced/ssh_disabled.png)
+
+When SSH access is enabled, it permits SSH connections from any location to the random SSH port unique to your account.
+
+To modify the SSH password for your account, click on the 'Change SSH Password' button, and within the modal, set a new password.
+
+This alteration will promptly update the password; however, all existing ssh connections will remain active.
+
+To terminate existing SSH connections, use the [Process Manager](/docs/panel/advanced/process_manager) interface and kill the appropriate processes.

+ 15 - 0
documentation/docs/panel/advanced/terminal.md

@@ -0,0 +1,15 @@
+---
+sidebar_position: 3
+---
+
+# Terminal
+
+The Web Terminal interface uses the [ttyd](https://github.com/tsl0922/ttyd) tool, providing you with command-line  access from your browser.
+
+![web_terminal.png](/img/panel/v1/advanced/web_terminal.png)
+
+Through this interface, you possess identical permissions as those granted by a remote SSH agent.
+
+:::info
+Due to the utilization of ttyd instead of a conventional SSH connection by the Web Terminal, this interface remains accessible even when remote SSH access is disabled for the account from the [SSH Access](/docs/panel/advanced/ssh) page.
+:::

+ 7 - 0
documentation/docs/panel/analytics/_category.json

@@ -0,0 +1,7 @@
+{
+  "label": "Analytics",
+  "position": 4,
+  "link": {
+    "type": "generated-index"
+  }
+}

+ 26 - 0
documentation/docs/panel/analytics/account_activity.md

@@ -0,0 +1,26 @@
+---
+sidebar_position: 3
+---
+
+# Activity Log
+
+The Account Activity page provides a log record of all your actions performed on the OpenPanel, along with the timestamp and IP address from which the action was executed. The primary objective of this page is to offer insights into who carried out specific actions, such as deleting a file, adding domains, resetting WordPress admin passwords, and more.
+
+![account_activity_log.png](/img/panel/v1/analytics/account_activity_log.png)
+
+
+Logged Activities Include:
+
+- Account: Logging in and logging out.
+- Domains: Adding and deleting domains.
+- File Manager: Deleting, moving, copying, and creating files.
+- Services: Enabling or disabling 2FA, Memcached, REDIS, PHP, and more.
+- WordPress: Installing, deleting, changing passwords, auto-login, and more.
+
+You can search for these actions by IP, username, date, or specific action.
+
+By default only 25 actions will be shown per page, you can navigate pages using the pagination links or click on 'Show all activity' to display all actions.
+
+:::info
+The default configuration retains a log of the most recent 100 actions, but [the administrator can change this setting](docs/admin/scripts/openpanel_config#activity_items_per_page).
+:::

+ 13 - 0
documentation/docs/panel/analytics/analytics.md

@@ -0,0 +1,13 @@
+---
+sidebar_position: 1
+---
+
+# Analytics
+
+The Analytics section allows you to view statistics and logs for your account and domains. 
+
+Available pages:
+
+- [Resource Usage](/docs/panel/analytics/resource_usage)
+- [Domain Visitors](/docs/panel/analytics/domain_visitors)
+- [Activity Log](/docs/panel/analytics/account_activity)

+ 18 - 0
documentation/docs/panel/analytics/domain_visitors.md

@@ -0,0 +1,18 @@
+---
+sidebar_position: 2
+---
+
+# Domain Visitors
+
+OpenPanel uses the [goaccess](https://github.com/allinurl/goaccess) web log analyzer tool to generate visual reports for every domain from the Nginx access logs.
+
+To view a report for a domain name simply select it from the select:
+
+![domain_visitors_1.png](/img/panel/v1/analytics/domain_visitors_1.png)
+
+New page will open in the browser showing the generated report.
+
+![domain_visitors_2.png](/img/panel/v1/analytics/domain_visitors_2.png)
+
+
+By default these reports are generated daily. In the top right corner you can view the timestamp when the report was last refreshed.

+ 79 - 0
documentation/docs/panel/analytics/resource_usage.md

@@ -0,0 +1,79 @@
+---
+sidebar_position: 1
+---
+
+# Resource Usage
+
+Resource Usage provides real-time information about your server's CPU, RAM, and other performance metrics. You can monitor your server's resource consumption and access historical data to make informed decisions.
+
+![resource_usage.png](/img/panel/v1/analytics/resource_usage.png)
+
+
+## CPU Usage
+
+The CPU Usage section displays the current CPU usage of your server. It provides essential details about the percentage of CPU utilization and allows you to track historical CPU usage.
+
+### Current CPU Usage
+
+The gauge shows the current CPU usage as a percentage. It helps you quickly assess how much of your CPU resources are in use.
+
+### Top Processes
+
+Clicking on the 'View top processes' will display top 10 active processes sorted by the CPU usage.
+
+![view_top_processes_cpu.png](/img/panel/v1/analytics/view_top_processes_cpu.png)
+
+:::info
+To view more than top 10 processes use the [Process Manager](/docs/panel/advanced/process_manager) interface.
+:::
+
+### Historical Data
+
+To view past CPU usage data, click the 'View Past CPU Usage' button.
+
+This will open the Historical Resource Usage page that allows you to track historical CPU and memory usage, enabling you to make informed decisions and analyze your server's performance over time.
+
+![resource_usage_history.png](/img/panel/v1/analytics/resource_usage_history.png)
+
+## RAM Usage
+
+The RAM Usage section provides insights into your server's memory consumption. It displays the current memory usage, memory limit, and historical data.
+
+### Current RAM Usage
+
+The gauge illustrates the current RAM usage, including the percentage used and the actual memory usage in relation to the memory limit.
+
+- Current Memory usage
+- Memory Usage
+- Memory Limit
+
+### Top Processes
+
+Clicking on the 'View top processes' will display top 10 active processes sorted by the RAM usage.
+
+![view_top_processes_ram.png](/img/panel/v1/analytics/view_top_processes_ram.png)
+
+### Historical Data
+
+Access historical RAM usage data by clicking the 'View Past Usage' button.
+
+This will open the Historical Resource Usage page that allows you to track historical CPU and memory usage, enabling you to make informed decisions and analyze your server's performance over time.
+
+![resource_usage_history.png](/img/panel/v1/analytics/resource_usage_history.png)
+
+
+## Network I/O
+
+The Network I/O section provides insights into network input and output for your server.
+
+## Block I/O
+
+Monitor block input and output operations in the Block I/O section.
+
+## Process IDs (PIDs)
+
+The PIDs section displays the number of running processes on your server.
+
+---
+
+By monitoring and managing your server's resource usage effectively, you can ensure optimal performance and make informed decisions to address any resource-related challenges.

+ 7 - 0
documentation/docs/panel/applications/_category.json

@@ -0,0 +1,7 @@
+{
+  "label": "Applications",
+  "position": 1,
+  "link": {
+    "type": "generated-index"
+  }
+}

+ 16 - 0
documentation/docs/panel/applications/applications.md

@@ -0,0 +1,16 @@
+---
+sidebar_position: 1
+---
+
+# Applications
+
+The Applications section provides access to the PM2 and WP Manager interfaces where you can install and manage WordPress, NodeJS or Python Applications.
+
+By click on the 'New Website' button in the sidebar a new popup will appear allowing you to select 'WordPress' to navigate to [WP Manager](/docs/panel/applications/wordpress) or 'NodeJS / Python' to navigate to the [PM2](/docs/panel/applications/pm2) interface.
+
+![new_site_popup.png](/img/panel/v1/applications/new_site_popup.png)
+
+Available pages:
+
+- [PM2](/docs/panel/applications/pm2)
+- [WP Manager](/docs/panel/applications/wordpress)

+ 139 - 0
documentation/docs/panel/applications/pm2.md

@@ -0,0 +1,139 @@
+---
+sidebar_position: 2
+---
+
+# NodeJS and Python
+
+The PM2 page allows you to run Python or NodeJS applications from within the OpenPanel interface.
+
+![pm2_noapps.png](/img/panel/v1/applications/pm2_noapps.png)
+
+## Create an Application
+
+To create a new application click on the 'New Application' button.
+
+In the form set:
+
+- Application URL - domain on which the application will be visible
+- Application startup file - this is the file that PM2 will start
+- Type - NodeJS or Python Application
+- Port - port on which the application will run and Nginx will proxy the _Application URL_ to
+- Watch - wheather to auto restart the application if fails
+- Enable Logs - wheather to collect logs for the application or not
+
+And click on the 'Create' button.
+
+![pm2_app_new.png](/img/panel/v1/applications/pm2_app_new.png)
+
+### Python Applications
+
+For python applications you need to set the Type to *Python* so that PM2 uses the [--interpreter](https://pm2.keymetrics.io/docs/tutorials/using-transpilers-with-pm2) flag.
+
+#### Example python application
+
+```python
+from flask import Flask
+
+app = Flask(__name__)
+
+@app.route('/')
+def hello():
+    return "Hello world!"
+
+if __name__ == '__main__':
+    app.run(host='0.0.0.0', port=5000)
+
+```
+
+
+### NodeJS Application
+
+#### NPM Install 
+
+1.  Navigate to your project directory:
+Open [Web Terminal](/docs/panel/advanced/terminal) or connect via [SSH](/docs/panel/advanced/ssh) to the server and navigate to the directory where your project is located. You can use the cd command to change directories.
+
+```bash
+cd path/to/your/project
+```
+
+2. Check for package.json file:
+Make sure that your project has a package.json file. This file contains metadata about your project and the list of dependencies. If you don't have one, you can create it by running:
+
+```bash
+npm init
+```
+Follow the prompts to set up your package.json file.
+
+3. Install dependencies:
+Run the following command to install the dependencies listed in your package.json file:
+
+```bash
+npm install
+```
+
+#### Example NodeJS application
+
+```js
+const http = require('http');
+
+const server = http.createServer((req, res) => {
+  res.writeHead(200, { 'Content-Type': 'text/plain' });
+  res.end('Hello, World!\n');
+});
+
+const port = process.env.PORT || 3000;
+
+server.listen(port, () => {
+  console.log(`Server is running on port ${port}`);
+});
+
+```
+
+
+
+
+## View Applications
+
+The PM2 page displays a list of all current NodeJS and Python applications.
+
+- Application name - domain on which the application is visible
+- Uptime - time passed since the application was started or created
+- Restarts - number of times the PM2 restarted the script (only if watch was enabled for the application)
+- CPU - current CPU % usage for the application process
+- Memory - current RAM usage in bytes by the applciaiton process
+- Watching - wheather the application was created with the watch option or not
+- Actions - Start/Stop, Restart and Delete the application
+- Type - NodeJS or Python application
+- Port - port on which the application is running and Nginx is proxying requests to
+- Startup file - main entrypoint for the application
+
+![pm2_app.png](/img/panel/v1/applications/pm2_app.png)
+
+## Manage Application
+
+Currently only the following actions can be performed for existing applications in the PM2 page:
+
+- [Start / Stop](/docs/panel/applications/pm2#stop)
+- [Restart](/docs/panel/applications/pm2#restart)
+- [Delete](/docs/panel/applications/pm2#delete)
+
+### Restart
+
+To restart an application *(force stop and start it immediately) click on the 'Restart' button:
+
+![pm2_app_restart.png](/img/panel/v1/applications/pm2_app_restart.png)
+
+This will immediately stop the pm2 process for the applicaiton and start again the startup script.
+
+### Stop
+
+To stop the application simply click on the 'Stop' button. This will immediately stop the process but leave the Nginx proxy to the port.
+
+![pm2_app_stop.png](/img/panel/v1/applications/pm2_app_stop.png)
+
+### Delete
+
+To completely remove an appliaiton click on the 'Delete' button. This will immediately stop the process and delete the Nginx proxy to the port.
+
+![pm2_app_delete.png](/img/panel/v1/applications/pm2_app_delete.png)

+ 230 - 0
documentation/docs/panel/applications/wordpress.md

@@ -0,0 +1,230 @@
+---
+sidebar_position: 1
+---
+
+# WordPress Manager
+
+![wp_manager_grid.png](/img/panel/v1/applications/wp_manager_grid.png)
+
+WordPress Manager is a powerful tool integrated into OpenPanel, enabling you to easily install and manage WordPress websites directly from your hosting panel.
+
+**Key Features:**
+- [Single-click login to wp-admin](/docs/panel/applications/wordpress#auto-login-to-wp-admin)
+- [Change DEBUG/Update preferences](https://docs.openpanel.co/docs/panel/applications/wordpress#update-preferences)
+- [Backup and Restore](/docs/panel/applications/wordpress#backup-and-restore)
+
+## Manage WordPress sites
+
+Using the manager, you can modify WordPress settings without the need to log in to the actual website. This feature is especially handy for managing multiple websites, as you can efficiently manage options in bulk, create backups, update plugins, enable automatic WordPress core updates, enable debugging, and much more—all from the OpenPanel interface.
+
+### WP Manager overview
+
+On the main WP Manager page you can:
+
+- [view installations](#): domain name, WordPress version, date when site was installed with or added to WP Manager interface and admin email.
+- [refresh the data](/docs/panel/applications/wordpress#refresh-existing-data): in case you have changed the domain name, updated WordPress manually or changed admin email address.
+- [install WordPress](/docs/panel/applications/wordpress#install-wordpress): install WordPress on a new domain.
+- [run a scan](/docs/panel/applications/wordpress#scan-import-installations): scan for WordPress installations that were manually added on the hosting plan and are not yet visible in the WP Manager interface.
+- [change view](/docs/panel/applications/wordpress#grid-vs-tabular-view): change the display from grid (default) to table.
+
+### Install WordPress
+
+You can quickly install any of the latest 10 WordPress versions with just a few clicks. Each WordPress installation is sourced directly from wp.org, and OpenPanel handles all additional steps, including creating a MySQL database and user, connecting it with the files, configuring the domain based on your choice of Nginx or Apache WebServer, and setting up WordPress.
+
+To install WordPress you need to first add a domain name.
+
+After adding a domain name, click on the 'New Website' button in the sidebar and on popup click on WordPress
+
+![new_site_popup.png](/img/panel/v1/applications/new_site_popup.png)
+
+Same page can be accessed by navigating to WordPress Manager and clicking on the Install button. 
+
+In the install form set:
+
+- Website name
+- Site description (optional)
+- Domain name (optionally a subfolder)
+- Admin username
+- Admin password
+- WordPress version
+
+and click on the 'Start Installation' button.
+
+![wp_install_domain.png](/img/panel/v1/applications/wp_install_domain.png)
+
+
+### Scan (Import) Installations
+
+By performing a scan you can import existing WordPress installations into the WP Manager interface.
+The tool will search all your files for wp-config.php (main WordPress configuration file) and import all websites that it finds.
+
+![wp_manager_scan.png](/img/panel/v1/applications/wp_manager_scan.png)
+
+### Refresh existing data
+
+In cases when you manually updated WordPress core or changed the admin email address outside the WP Manager interface, the tool will display out-of-date information. To update the data inside the WP Manager database for your websites simply click on the 'Refresh data' button.
+
+![wp_manager_refresh_data.png](/img/panel/v1/applications/wp_manager_refresh_data.png)
+
+### Grid VS Tabular view
+
+Websites can be viewed in grid mode with screenshots or tabular (table) mode. 
+To change the view to table mode click on the button in bottom right corner of the screen:
+![wp_manager_table.png](/img/panel/v1/applications/wp_manager_table.png)
+
+
+---
+
+## Managing a website
+
+
+![wp_manager_site.png](/img/panel/v1/applications/wp_manager_site.png)
+
+### General Information
+
+For each WordPress installation you can view:
+
+- Website name
+- Screenshot
+- Path to files and total size of the folder
+- Database information and total database size
+- Domain name
+- Date when the installation was added to WP Manager
+- WordPress core version
+- PHP version used on the domain name
+- MySQL version
+- Link to phpMyAdmin to view database tables
+- SSL status for the domain name
+- Link to login to wp-admin
+- Link to edit PHP version for the domain
+- Link to edit Cronjobs
+
+### View database information
+
+To view database login information for a WordPress website from the WP Manager click on the Database link:
+
+![wp_manager_site_database.png](/img/panel/v1/applications/wp_manager_site_database.png)
+
+To reveal the password click on the blured password field:
+![wp_manager_site_database_password.png](/img/panel/v1/applications/wp_manager_site_database_password.png)
+
+### Detach a website
+
+Use the Detach option to remove a WordPress website from the WP Manager interface without actually remove any files or database.
+
+Click on the 'Detach' button:
+
+![wp_manager_site_detach_1.png](/img/panel/v1/applications/wp_manager_site_detach_1.png)
+
+On the popup click on 'Remove' button to confirm:
+
+![wp_manager_site_detach_2.png](/img/panel/v1/applications/wp_manager_site_detach_2.png)
+
+
+
+### Auto Login to wp-admin
+Use the single sign-on option to auto login securely to your wp-admin dashboard as the Administrator user.
+
+Click on the 'Login as Admin' button.
+
+![wp_manager_site_login_admin.png](/img/panel/v1/applications/wp_manager_site_login_admin.png)
+
+
+### Edit WordPress settings
+
+To edit General, Updates and Debugging preferences for a WordPress website click on the pencil icon in top right corner.
+
+#### General Settings
+
+General settigns that can be edited for a website:
+
+- Website name
+- Blog Description
+- Enable/Disable user registrations
+- Admin Email that is used for receiving all information
+- Allow/Block pingbacks from other websites that mention you
+- Block/Allow search engines like Google, Bing, etc.
+
+![wp_manager_site_edit_1.png](/img/panel/v1/applications/wp_manager_site_edit_1.png)
+
+#### Update Preferences
+
+Here you can set the update preferences for WordPress core, plugins and themes.
+
+By default only WordPress core updates to minor versions are enabled.
+
+![wp_manager_site_edit_2.png](/img/panel/v1/applications/wp_manager_site_edit_2.png)
+
+#### Update WordPress core
+
+If a newer WordPress core version is available, you will see 'Click to update' button which when clicked will perform WordPress update to the newest version available.
+
+#### Debug Preferences
+
+These options allow you to manage the native WordPress debugging tools, enabling and disabling these tools in the wp-config.php file. It is not recommended to use these options on production websites since they are meant for development and test installations. Refer to [Debugging in WordPress article](https://wordpress.org/documentation/article/debugging-in-wordpress/) for more information on these options.
+
+
+Here you can enable:
+
+- WP_DEBUG
+- WP_DEBUG_LOG
+- WP_DEBUG_DISPLAY
+- SHOW_DEBUG
+- SAVEQUERIES
+
+![wp_manager_site_edit_3.png](/img/panel/v1/applications/wp_manager_site_edit_3.png)
+
+
+### Refresh website screenshot
+
+Website screenshots are periodically re-generated every 24h, if you need to manually refresh the screenshot click on the icon in top left corner of the screenshot.
+
+![wp_manager_site_refresh_screenshot.png](/img/panel/v1/applications/wp_manager_site_refresh_screenshot.png)
+
+
+### Uninstall WordPress
+
+To uninstall WordPress and permanently delete all website files and database, click on the 'Uninstall' button.
+
+![wp_manager_site_uninstall_1.png](/img/panel/v1/applications/wp_manager_site_uninstall_1.png)
+
+On the modal click on the 'Confirm Uninstall' button to confirm.
+
+![wp_manager_site_uninstall_2.png](/img/panel/v1/applications/wp_manager_site_uninstall_2.png)
+
+
+### Backup and Restore
+You have the options to perform manual backups of WordPress files or databases as needed, and easily restore them when required.
+
+#### Create a backup
+
+To generate a new backup click on the 'Backup' button.
+
+![wp_manager_site_backup_1.png](/img/panel/v1/applications/wp_manager_site_backup_1.png)
+
+On the modal select to backup both files and database, just a database or just files.
+Click on the 'Run Backup' to start the backup process:
+
+![wp_manager_site_backup_2.png](/img/panel/v1/applications/wp_manager_site_backup_2.png)
+
+After backup process is finished you will receive a notification.
+
+![wp_manager_site_backup_done.png](/img/panel/v1/applications/wp_manager_site_backup_done.png)
+
+#### Restore from backup
+
+To restore website from a backup created with OpenPanel WP Manager backup option simply click on the 'Restore' button for that website in WP Manager:
+
+![wp_manager_site_restore_1.png](/img/panel/v1/applications/wp_manager_site_restore_1.png)
+
+In the modal, select the backup date from which to restore the website. In the brackets next to each date you can view if the backup contains only database, only files or both. 
+
+![wp_manager_site_restore_2.png](/img/panel/v1/applications/wp_manager_site_restore_2.png)
+
+After selecting a date, confirm the restore process by clicking on the 'Confirm Restore (Click Again)' button.
+
+![wp_manager_site_restore_confirm.png](/img/panel/v1/applications/wp_manager_site_restore_confirm.png)
+
+When the restore process is complete you will receive a notification:
+
+![wp_manager_site_restore_done.png](/img/panel/v1/applications/wp_manager_site_restore_done.png)

+ 88 - 0
documentation/docs/panel/caching/Memcached.md

@@ -0,0 +1,88 @@
+---
+sidebar_position: 3
+---
+
+# Memcached
+
+![memcached_disabled.png](/img/panel/v1/caching/memcached_disabled.png)
+
+Memcached is a high-performance, distributed memory caching system. It is often used to speed up dynamic database-driven websites and applications by caching data in memory.
+
+It is commonly used to reduce the load on a database server and improve the responsiveness of websites by caching frequently accessed data, such as database query results or API responses.
+
+## Enable / Disable
+
+You have the options to enable or disable the Memcached service as needed. Disabling it will promptly clear all existing Memcached data from memory.
+
+Enabling the Memcached service will start the service using the default Memcached port, which is _11211_.
+
+![memcached_enabled.png](/img/panel/v1/caching/memcached_enabled.png)
+
+## Set Memory Limits
+
+Memcached, by default, does not impose memory limits and can utilize available RAM memory without constraints. To prevent Memcached from consuming all available memory, it is recommended to establish a maximum memory limit.
+
+:::info
+Changing the memory limit will necessitate the service to restart to apply the new restrictions, resulting in the removal of all existing cache data.
+:::
+
+## Connect to Memcached
+
+To establish a connection to your Memcached instance, use the following details:
+
+- Server address: **127.0.0.1**
+- Port: **11211** (the default Memcached port)
+
+For testing the connection to the Memcached server, you can use the following tools or scripts.
+
+### Test Connection with PHP
+
+1. Navigate to your website directory using a File Manager.
+2. Create a new file named _memcached-test.php_.
+3. Add the following PHP code to the newly created file and save it:
+
+```php
+<?php 
+   // Connect to Memcached server on localhost 
+   $memcached = new Memcached(); 
+   $memcached->addServer('127.0.0.1', 11211); 
+   echo "Connection to server successful"; 
+   // Check whether the server is running or not 
+   echo "Server is running: ".$memcached->getVersion(); 
+?>
+```
+
+Access your website in a browser and append /memcached-test.php. For example, if your website is example.com, you should open example.com/memcached-test.php
+
+You should see the "Server is running.." message, indicating that the Memcached service is active, and the connection is established.
+
+### Test Connection Using Telnet
+
+To connect to the Memcached service from the terminal you can run the telnet command and specify the hostname and port of the Memcached service:
+
+```bash
+telnet localhost 11211
+```
+
+The command will display the diagnostic message, showing that it's connecting to the hostname. If the connection is successful, you will see two additional lines, confirming that the connection is established.
+
+```bash
+$ telnet localhost 11211
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+```
+
+
+### WordPress Plugins
+
+To implement Memcached caching for your WordPress website, you'll need a dedicated plugin. Here are some WordPress plugins we've tested for Memcached caching:
+
+- [Memcached Object Cache](https://wordpress.org/plugins/memcached/)
+
+## View Logs
+
+You have the option to access the Memcached service logs. By doing so, you can identify any service errors or check for memory usage and limits.
+
+![memcached_log.png](/img/panel/v1/caching/memcached_log.png)
+

+ 87 - 0
documentation/docs/panel/caching/Redis.md

@@ -0,0 +1,87 @@
+---
+sidebar_position: 2
+---
+
+# REDIS
+
+![redis_disabled.png](/img/panel/v1/caching/redis_disabled.png)
+
+REDIS stands as a robust and persistent object cache solution, purpose-built to efficiently retain frequently accessed website data within the RAM memory.
+
+With its capability to store and serve data from RAM, it minimizes the need for time-consuming database queries, significantly enhancing website performance.
+
+## Enable / Disable
+
+You have the option to enable or disable the REDIS service as necessary. Choosing to disable it will promptly clear (delete) all existing Redis data from memory.
+
+Enabling the REDIS service will initiate the Redis service on the default port, which is _6379_.
+
+![redis_enabled.png](/img/panel/v1/caching/redis_enabled.png)
+
+## Set Memory Limits
+
+By default, Redis has no memory limits set and can utilize all available RAM memory, as indicated by the ∞ symbol. To avoid Redis from consuming all available memory, it is advisable to establish a maximum memory limit.
+You can set this limit to a maximum of 2GB for the Redis service.
+
+:::info
+Modifying the memory limit will require the service to reload to apply the new restrictions, resulting in the removal of all existing data from the cache.
+:::
+
+## Connect to REDIS
+
+To establish a connection to your REDIS instance, use the following details:
+
+- Server address: **127.0.0.1**
+- Port: **6379** (the default REDIS port)
+
+For testing the connection to the REDIS server, you can use the 'telnet' command in your terminal or refer to example scripts bellow.
+
+### Test connection with PHP
+
+1. Using FileManager enter the directory of your website
+2. Create a new file named _redis-test.php_
+3. Add the following code in the newly created file and save:
+```php
+<?php 
+   //Connecting to Redis server on localhost 
+   $redis = new Redis(); 
+   $redis->connect('127.0.0.1', 6379); 
+   echo "Connection to server sucessfully"; 
+   //check whether server is running or not 
+   echo "Server is running: ".$redis->ping(); 
+?>
+```
+
+4. In your browser navigate to your website and add /redis-test.php for example, if your website is example.com you should open example.com/redis-test.php
+
+You should see the _Server is running message.._ indicating that the REDIS service is active and connection is established.
+
+### Test connection using telnet
+
+To connect to the Redis service, you can run the telnet command and specify the hostname and port of the Redis service:
+
+```bash
+telnet localhost 6379
+```
+
+The command always prints the first line of the diagnostic message, stating that it’s connecting to the hostname. Then, if the connection is successful, you will see two additional lines, the first additional line confirms that connection is established:
+
+```bash
+$ telnet localhost 6379
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+```
+
+### WordPress plugins
+
+To incorporate REDIS caching into your WordPress website, a WordPress plugin is required. Below are some of the WordPress plugins we evaluated for REDIS caching:
+
+- [Redis Object Cache](https://wordpress.org/plugins/redis-cache/)
+- [Use Redis](https://plugins.club/premium-wordpress-plugins/use-redis/)
+   
+## View Logs
+
+You have the option to view the REDIS service logs. By doing so, you can identify any service errors or, for instance, determine whether memory limits have been reached.
+
+![redis_log.png](/img/panel/v1/caching/redis_log.png)

+ 7 - 0
documentation/docs/panel/caching/_category.json

@@ -0,0 +1,7 @@
+{
+  "label": "Caching",
+  "position": 11,
+  "link": {
+    "type": "generated-index"
+  }
+}

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików