diff --git a/.gitignore b/.gitignore
old mode 100755
new mode 100644
index 3efaa1b0..1e481675
--- a/.gitignore
+++ b/.gitignore
@@ -1,26 +1,28 @@
-# dependencies
-/node_modules
-/npm-debug.log*
-/yarn-error.log
-/yarn.lock
+# Secrets
+/**/**/.env
+/**/**/origin.server
+/**/**/server.manifest
+/**/**/server.registry
-# production
-/build
-/dist
-/out
+# Trash
+/**/**/.crash.log
+/**/**/.tmp
+/**/**/.cache
+/**/**/out
+/**/**/.out
+/**/**/dist
+/**/**/node_modules
+/**/**/corenode_modules
+/**/**/.DS_Store
+/**/**/package-lock.json
+/**/**/yarn.lock
+/**/**/.evite
-# umi
-/packages/**/src/.umi
-/packages/**/src/.umi-production
-/packages/**/src/.umi-test
-/packages/**/.env.local
+# Logs
+/**/**/npm-debug.log*
+/**/**/yarn-error.log
+/**/**/dumps.log
+/**/**/corenode.log
-/packages/*/src/.umi
-/packages/*/src/.umi-production
-/packages/*/src/.umi-test
-/packages/*/.env.local
-
-/.env.local
-
-/packages/*/node_modules
-/packages/**/node_modules
\ No newline at end of file
+# Temporal configurations
+/**/**/.aliaser
\ No newline at end of file
diff --git a/packages/app/src/App.jsx b/packages/app/src/App.jsx
index d58b5517..407f07d8 100644
--- a/packages/app/src/App.jsx
+++ b/packages/app/src/App.jsx
@@ -145,9 +145,6 @@ class App {
RenderError: (props) => {
return
},
- initialization: () => {
- return
Initializing...
- }
}
state = {
diff --git a/packages/app/src/extensions/api/index.js b/packages/app/src/extensions/api/index.js
index 9f9f4dce..9d944142 100644
--- a/packages/app/src/extensions/api/index.js
+++ b/packages/app/src/extensions/api/index.js
@@ -1,7 +1,6 @@
import config from 'config'
import { Bridge } from "linebridge/client"
import { Session } from "models"
-import io from "socket.io-client"
export default {
key: "apiBridge",
@@ -10,15 +9,6 @@ export default {
mutateContext: {
async initializeDefaultBridge() {
this.apiBridge = await this.createBridge()
- this.ws = io("http://localhost:9001/main", { transports: ["websocket"] })
-
- this.ws.on("connect", () => {
- console.log(this.ws.id)
- })
-
- this.ws.on("connect_error", (...context) => {
- console.log(...context)
- })
window.app.apiBridge = this.apiBridge
},
diff --git a/packages/app/src/pages/404.js b/packages/app/src/pages/404.js
deleted file mode 100644
index a0c3ba3c..00000000
--- a/packages/app/src/pages/404.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react'
-import styles from './404.less'
-
-const Error404 = () => (
-
-
-
OBA BLYAT
-
- ERROR 404
-
-
-)
-
-export default Error404
diff --git a/packages/app/src/pages/404.less b/packages/app/src/pages/404.less
deleted file mode 100644
index ac10f8cc..00000000
--- a/packages/app/src/pages/404.less
+++ /dev/null
@@ -1,55 +0,0 @@
-/* devanagari */
-@font-face {
- font-family: 'Poppins';
- font-style: normal;
- font-weight: 400;
- font-display: swap;
- src: local('Poppins Regular'), local('Poppins-Regular'), url(https://fonts.gstatic.com/s/poppins/v6/pxiEyp8kv8JHgFVrJJbecmNE.woff2) format('woff2');
- unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
-}
-
-/* latin-ext */
-@font-face {
- font-family: 'Poppins';
- font-style: normal;
- font-weight: 400;
- font-display: swap;
- src: local('Poppins Regular'), local('Poppins-Regular'), url(https://fonts.gstatic.com/s/poppins/v6/pxiEyp8kv8JHgFVrJJnecmNE.woff2) format('woff2');
- unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
-}
-
-/* latin */
-@font-face {
- font-family: 'Poppins';
- font-style: normal;
- font-weight: 400;
- font-display: swap;
- src: local('Poppins Regular'), local('Poppins-Regular'), url(https://fonts.gstatic.com/s/poppins/v6/pxiEyp8kv8JHgFVrJJfecg.woff2) format('woff2');
- unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
-}
-
-.error {
- background-color: transparent;
- color: black;
- text-align: center;
- position: absolute;
- top: 30%;
- margin-top: -50px;
- left: 50%;
- margin-left: -100px;
- width: 200px;
-
- :global .anticon {
- font-size: 48px;
- margin-bottom: 16px;
- }
-
- h1 {
- font-family: 'Poppins', sans-serif;
-
- }
-
- p {
- font-family: 'Poppins', sans-serif;
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/@/[user].js b/packages/app/src/pages/@/[user].js
deleted file mode 100644
index 5713afdf..00000000
--- a/packages/app/src/pages/@/[user].js
+++ /dev/null
@@ -1,237 +0,0 @@
-import React from 'react'
-import * as antd from 'antd'
-import { verbosity, objectToArrayMap } from '@nodecorejs/utils'
-import * as Icons from 'components/Icons'
-import { connect } from 'umi'
-
-import { pathMatchRegexp, booleanFix } from 'core'
-import HandleError from 'core/libs/errorhandler'
-import GlobalBadges from 'schemas/badges_list.json'
-import { Invalid, PostsFeed} from 'components'
-import FollowButton from './components/follow'
-
-import styles from './index.less'
-
-export class UserLayout extends React.Component {
- state = {
- styleComponent: "UserLayout",
- userString: pathMatchRegexp('/@/:id', location.pathname)[1],
- layoutData: {
- avatar: null,
- cover: null,
- about: null,
- followed: null,
- followers: null
- }
- }
-
- handleClickFollow(user_id) {
- if (typeof (this.props.onFollow) !== "undefined") {
- this.updateFollow(!booleanFix(this.state.layoutData.is_following))
-
- this.props.onFollow(user_id, (callback) => {
- this.updateFollow(callback)
- })
- }
- }
-
- updateFollow(to) {
- let updated = this.state.layoutData
- updated.is_following = to
- this.setState({ layoutData: updated })
- }
-
- componentDidMount() {
- const { layoutData } = this.props
- if (layoutData) {
- this.setState({ layoutData: { ...this.state.layoutData, ...layoutData } })
- }
- }
-
- renderUserBadges() {
- let { layoutData } = this.state
- if (typeof(layoutData.user_tags) == "undefined") {
- return null
- }
- let userTags = objectToArrayMap(layoutData.user_tags)
- let renderTags = []
-
- if (!userTags) {
- return null
- }
-
- try {
- userTags = JSON.parse(userTags[0].value.badges)
- } catch (error) {
- console.log(error)
- }
-
- if (!userTags) {
- return null
- }
-
- objectToArrayMap(GlobalBadges).forEach(e => {
- if(userTags.includes(e.value.id)) {
- renderTags.push(e.value)
- }
- })
-
- try {
- if (Array.isArray(userTags)) {
- return renderTags.map((element) => {
- return(
-
-
- {element.title ?? "maybe"}
-
-
- )
- })
- }
- } catch (error) {
- return null
- }
- return null
- }
-
- render() {
- const { styleComponent } = this.state
- const toStyles = e => styles[`${styleComponent}_${e}`]
- const { followers_count } = this.state.layoutData.details ?? {}
- const isFollowed = booleanFix(this.state.layoutData.is_following)
-
- if (!this.state.layoutData) {
- return null
- }
- return (
-
-
-

-
-
-
-
-
-
- {/* {this.renderUserBadges()} */}
-
-
- {this.state.userString}
-
-
-
-
-
-
-
{ this.handleClickFollow(this.state.layoutData.user_id) }} followed={isFollowed} />
-
-
-
-
-
-
-
-
- )
- }
-}
-
-@connect(({ app }) => ({ app }))
-export default class UserIndexer extends React.Component {
- state = {
- ErrorCode: null,
- loading: true,
- response: null,
- layoutData: null
- }
-
- promiseState = async state => new Promise(resolve => this.setState(state, resolve));
-
- handleClickFollow(user_id, callback) {
- if (this.props.app.session_valid) {
- const requestCallback = (callbackResponse) => {
- if (callbackResponse.code == 200) {
- const response = callbackResponse.response
- const result =( response.follow ?? false) == "followed" ? true : false
- if (typeof(response) !== "undefined") {
- return callback(result)
- }else{
- return false
- }
- }else{
- return callback(null)
- }
- }
-
- this.props.dispatch({
- type: "socket/use",
- scope: "users",
- invoke: "actions",
- query: {
- payload: {
- userToken: this.props.app.session_token,
- action: "follow",
- user_id,
- },
- callback: requestCallback
- }
- })
- }else{
- verbosity.log(`Needs auth to dispatch 'actions' event`)
- }
-
- }
-
- componentDidMount() {
- const matchRegexp = pathMatchRegexp('/@/:id', location.pathname)
-
- if (matchRegexp && this.props.app.session_valid) {
- this.props.dispatch({
- type: "user/get",
- payload: {
- from: "profileData",
- username: matchRegexp[1]
- },
- callback: (callbackResponse) => {
- if (callbackResponse.code == 200) {
- this.setState({ loading: false, layoutData: callbackResponse.response })
- } else {
- this.setState({ ErrorCode: callbackResponse.code })
- return HandleError({ code: callbackResponse.code, msg: "no message provided" })
- }
-
- }
- })
- } else {
- this.setState({ ErrorCode: 140 })
- }
- }
- render() {
- if (this.state.ErrorCode) {
- return
- }
- if (this.state.loading) {
- return
- }
- return (
-
-
{this.handleClickFollow(...context)}} layoutData={this.state.layoutData} />
-
-
-
- )
- }
-}
-
diff --git a/packages/app/src/pages/@/components/badges/index.js b/packages/app/src/pages/@/components/badges/index.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/packages/app/src/pages/@/components/follow/index.js b/packages/app/src/pages/@/components/follow/index.js
deleted file mode 100644
index e1733237..00000000
--- a/packages/app/src/pages/@/components/follow/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from 'react'
-import styles from './index.less'
-import classnames from 'classnames'
-
-const FollowButton = (props) => {
- return (
-
- {props.followed ? 'Following' : 'Follow'}
-
- )
-}
-
-export default FollowButton
diff --git a/packages/app/src/pages/@/components/follow/index.less b/packages/app/src/pages/@/components/follow/index.less
deleted file mode 100644
index 3c3a5d7e..00000000
--- a/packages/app/src/pages/@/components/follow/index.less
+++ /dev/null
@@ -1,32 +0,0 @@
-.button {
- user-select: none;
- width: 100px;
- height: 30px;
- padding: 5px 15px;
- text-decoration: none;
- text-align: center;
- vertical-align: middle;
- letter-spacing: 1px;
-
- &:hover {
- color: #7e7e7e;
- }
-
- &.disabled{
- &:hover {
- border: none;
- content: '';
- color: white;
- border-radius: 8px;
- background: linear-gradient(120deg, #6559ae, #ff7159, #6559ae);
- background-size: 400% 400%;
- animation: gradient 15s ease-in-out infinite, border 1s forwards ease-in-out reverse;
- }
- }
-}
-
-@keyframes gradient {
- 0% { background-position: 14% 0%; }
- 50% { background-position: 87% 100%; }
- 100% { background-position: 14% 0%; }
-}
diff --git a/packages/app/src/pages/@/components/menu/index.js b/packages/app/src/pages/@/components/menu/index.js
deleted file mode 100644
index 51144682..00000000
--- a/packages/app/src/pages/@/components/menu/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from 'react'
-import * as antd from 'antd'
-import { MoreOutlined } from 'components/Icons'
-
-const moreMenu = (
-
- __
- __set2
-
-)
-
-const Menu = (props) => {
- return (
-
-
-
- )
-}
-
-export default Menu
diff --git a/packages/app/src/pages/@/index.less b/packages/app/src/pages/@/index.less
deleted file mode 100644
index 5a64cd09..00000000
--- a/packages/app/src/pages/@/index.less
+++ /dev/null
@@ -1,111 +0,0 @@
-.UserLayout_wrapper{
- display: flex;
- height: 100%;
- width: 100%;
-
- flex-direction: column;
- align-items: center;
-
- margin: auto;
- overflow: overlay !important;
-}
-
-.UserLayout_header{
- font-family: "Poppins", sans-serif;
- color: #242424;
-
- vertical-align: top;
- display: flex;
- padding: 20px;
-
- height: auto;
- width: 100%;
-
- max-width: 600px;
- max-height: 200px;
-
- background-color: #ffffff;
- border-radius: 0 0 20px 20px;
- transform: translate(0, -5px);
-}
-
-.UserLayout_cover{
- display: flex;
- align-content: center;
-
- max-height: 400px;
- max-width: 600px;
-
- overflow: hidden;
- border-radius: 15px 15px 0 0;
- border-radius: 8px;
-
- img {
- width: 100%;
- margin: auto;
- }
-}
-
-.UserLayout_title{
- color: #2d2d2d;
- font-weight: 500;
- font-size: 20px;
- line-height: 28px;
- width: 100%;
- height: 100%;
- word-break: break-all;
-
- h1 {
- margin: 0;
- width: fit-content;
- }
-
- span{
- word-break: break-all;
- }
-}
-
-.UserLayout_avatar {
- transform: translate(-35px, -45px);
-
- &>span {
- width: 100%;
- height: 100%;
- }
-
- &.mobile{
- transform: translate(0,-90px);
- margin: auto;
- }
-
- :global {
- .ant-avatar {
- box-shadow: 13px 13px 17px 4px rgba(69, 69, 69, 0.151);
- border-radius: 7px;
- height: 120px;
- width: 120px;
-
- img {
- height: 100%;
- width: 100%;
- }
- }
- }
-}
-
-.UserLayout_options{
- display: flex;
- flex-direction: column;
- text-align: left;
-
- > div {
- text-align: left;
- margin-bottom: 15px;
- }
-}
-
-
-
-.UserLayout_content{
-
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/[indexer].js b/packages/app/src/pages/[indexer].js
deleted file mode 100644
index 8996c437..00000000
--- a/packages/app/src/pages/[indexer].js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react'
-import Error404 from './404.js'
-
-export default class PageIndexer extends React.Component {
- render() {
- const { location } = this.props
-
- // By default return Error 404
- return (
-
-
-
- )
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/account/components/editor/index.jsx b/packages/app/src/pages/account/components/editor/index.jsx
new file mode 100644
index 00000000..dda0195d
--- /dev/null
+++ b/packages/app/src/pages/account/components/editor/index.jsx
@@ -0,0 +1,181 @@
+import React from "react"
+import debounce from "lodash/debounce"
+import { Icons } from "components/Icons"
+
+import classnames from "classnames"
+import * as antd from "antd"
+
+import "./index.less"
+
+export const EditAccountField = ({ id, component, props, header, handleChange, delay, defaultValue, allowEmpty }) => {
+ const [currentValue, setCurrentValue] = React.useState(defaultValue)
+ const [emittedValue, setEmittedValue] = React.useState(null)
+
+ const debouncedHandleChange = React.useCallback(
+ debounce((value) => handleChange({ id, value }), delay ?? 300),
+ [],
+ )
+
+ const handleDiscard = (event) => {
+ if (typeof event !== "undefined") {
+ event.target.blur()
+ }
+
+ setCurrentValue(defaultValue)
+ handleChange({ id, value: defaultValue })
+ }
+
+ React.useEffect(() => {
+ debouncedHandleChange(currentValue)
+ }, [emittedValue])
+
+ const onChange = (event) => {
+ event.persist()
+ let { value } = event.target
+
+ if (typeof value === "string") {
+ if (value.length === 0) {
+ // if is not allowed to be empty, discard modifications
+ if (!allowEmpty) {
+ return handleDiscard(event)
+ }
+ }
+ }
+
+ setCurrentValue(value)
+ setEmittedValue(value)
+ }
+
+ const handleKeyDown = (event) => {
+ if (event.keyCode === 27) {
+ // "escape" pressed, reset to default value
+ handleDiscard(event)
+ }
+ }
+
+ window.app.eventBus.on("discardAllChanges", () => {
+ setCurrentValue(defaultValue)
+ })
+
+ const RenderComponent = component
+ return (
+
+ {header ? header : null}
+
+
+ )
+}
+
+export default class EditAccount extends React.Component {
+ state = {
+ values: this.props.user ?? {},
+ changes: [],
+ loading: false,
+ }
+
+ toogleLoading = (to) => {
+ this.setState({ loading: to ?? !this.state.loading })
+ }
+
+ onSaveDone = (error, data) => {
+ this.setState({ changes: [] })
+ this.toogleLoading(false)
+ }
+
+ onSave = () => {
+ this.props.onSave(this.state.changes, this.onSaveDone)
+ }
+
+ discardAll = () => {
+ window.app.eventBus.emit("discardAllChanges")
+ this.setState({ changes: [] }) // clean changes after emit, cause controller wont handle changes
+ }
+
+ handleChange = (event) => {
+ const { id, value } = event
+ let changes = [...this.state.changes]
+
+ changes = changes.filter((change) => change.id !== id)
+
+ if (this.state.values[id] !== value) {
+ // changes detected
+ changes.push({ id, value })
+ }
+
+ this.setState({ changes })
+ }
+
+ renderActions = () => {
+ return (
+ 0 })}>
+
+ {this.state.loading && }
+ {this.state.changes.length} Changes
+
+
+
+
+ )
+ }
+
+ render() {
+ const { username, fullName, email } = this.state.values
+
+ return (
+
+ {this.renderActions()}
+
+
+
+ Account information
+
+
+ Username
+
+ }
+ component={antd.Input}
+ props={{ placeholder: "Username", disabled: true }}
+ handleChange={this.handleChange}
+ />
+
+ Name
+
+ }
+ component={antd.Input}
+ props={{ placeholder: "Your full name" }}
+ handleChange={this.handleChange}
+ />
+
+ Email
+
+ }
+ component={antd.Input}
+ props={{ placeholder: "Your email address", type: "email" }}
+ handleChange={this.handleChange}
+ />
+
+
+
+ )
+ }
+}
diff --git a/packages/app/src/pages/account/components/editor/index.less b/packages/app/src/pages/account/components/editor/index.less
new file mode 100644
index 00000000..9f83c10c
--- /dev/null
+++ b/packages/app/src/pages/account/components/editor/index.less
@@ -0,0 +1,60 @@
+.edit_account{
+ overflow: hidden!important;
+ height: 100%;
+}
+
+.edit_account_wrapper{
+ overflow: scroll;
+ > div {
+ margin-bottom: 20px;
+ }
+}
+
+.edit_account_actions {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+
+ width: 100%;
+ padding: 20px 0 20px 0;
+ background-color: rgba(221, 221, 221, 0.5);
+ backdrop-filter: blur(2px);
+ border-radius: 18px 18px 0 0;
+
+ transition: all 0.3s ease-in-out;
+
+ display: none;
+ opacity: 0;
+
+ &.show {
+ display: flex;
+ opacity: 1;
+ }
+
+ > div {
+ margin-left: 20px;
+ }
+}
+
+.edit_account_actions_indicator{
+ position: fixed;
+ left: 0;
+ margin-left: 30px;
+}
+
+.edit_account_category{
+ > div {
+ margin-bottom: 20px;
+ margin-left: 20px;
+ }
+}
+
+.edit_account_field {
+ input:not(:focus){
+ color:rgba(105, 105, 105, 0.5)
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/account/components/index.js b/packages/app/src/pages/account/components/index.js
new file mode 100644
index 00000000..e6d93aef
--- /dev/null
+++ b/packages/app/src/pages/account/components/index.js
@@ -0,0 +1,3 @@
+export { default as AccountEditor } from './editor'
+export { default as SessionsView } from './sessionsView'
+export { default as StatisticsView } from './statisticsView'
\ No newline at end of file
diff --git a/packages/app/src/pages/account/components/sessionsView/index.jsx b/packages/app/src/pages/account/components/sessionsView/index.jsx
new file mode 100644
index 00000000..8f3e6b20
--- /dev/null
+++ b/packages/app/src/pages/account/components/sessionsView/index.jsx
@@ -0,0 +1,37 @@
+import React from "react"
+import * as antd from "antd"
+import { Icons } from "components/Icons"
+import { Sessions } from "components"
+
+export default class SessionsView extends React.Component {
+ signOutAll = () => {
+ antd.Modal.warning({
+ title: "Caution",
+ content: "This action will cause all sessions to be closed, you will have to log in again.",
+ onOk: () => {
+ //this.setState({ sessions: null })
+ window.app.eventBus.emit("destroyAllSessions")
+ },
+ okCancel: true,
+ })
+ }
+
+ render() {
+ const { sessions, decodedToken } = this.props
+
+ if (!sessions) {
+ return
+ }
+
+ return (
+
+
+ {sessions && (
+
+ Destroy all sessions
+
+ )}
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/account/components/statisticsView/index.jsx b/packages/app/src/pages/account/components/statisticsView/index.jsx
new file mode 100644
index 00000000..e8eb1114
--- /dev/null
+++ b/packages/app/src/pages/account/components/statisticsView/index.jsx
@@ -0,0 +1,17 @@
+import React from "react"
+
+export default class StatisticsView extends React.Component {
+ state = {
+ statistics: null
+ }
+
+ componentDidMount = async () => {
+
+ }
+
+ render() {
+ return
+
+
+ }
+}
diff --git a/packages/app/src/pages/account/index.jsx b/packages/app/src/pages/account/index.jsx
new file mode 100644
index 00000000..e55fd687
--- /dev/null
+++ b/packages/app/src/pages/account/index.jsx
@@ -0,0 +1,181 @@
+import React from "react"
+import * as antd from "antd"
+import { Icons } from "components/Icons"
+
+import { Roles } from "components"
+import { AccountEditor, SessionsView, StatisticsView } from "./components"
+
+import { Session } from "models"
+
+import "./index.less"
+
+const api = window.app.apiBridge
+
+const SelfViewComponents = {
+ sessionsView: SessionsView,
+ statisticsView: StatisticsView,
+}
+
+const SelfViewTabDecorators = {
+ sessionsView: (
+
+ Sessions
+
+ ),
+ statisticsView: (
+
+ Statistics
+
+ ),
+}
+
+class SelfView extends React.Component {
+ renderComponents = () => {
+ const renderTagDecorator = (key) => {
+ if (typeof this.props.decorators[key] !== "undefined") {
+ return this.props.decorators[key]
+ }
+ return key
+ }
+
+ return Object.keys(this.props.components).map((key, index) => {
+ const Component = this.props.components[key]
+
+ return (
+
+
+
+
+
+ )
+ })
+ }
+
+ render() {
+ return (
+
+ {this.renderComponents()}
+
+ )
+ }
+}
+
+export default class Account extends React.Component {
+ static bindApp = ["userController", "sessionController"]
+
+ state = {
+ isSelf: false,
+ user: null,
+ sessions: null
+ }
+
+ componentDidMount = async () => {
+ const token = Session.decodedToken
+ const location = window.app.history.location
+ const query = new URLSearchParams(location.search)
+
+ const requestedUser = location.state?.username ?? query.get("username") ?? token?.username
+ let state = this.state
+
+ if (requestedUser != null) {
+ if (token.username === requestedUser) {
+ state.isSelf = true
+ state.sessions = await this.props.contexts.app.sessionController.getAllSessions()
+ }
+
+ state.user = await this.props.contexts.app.userController.getData({ username: requestedUser })
+ }
+
+ this.setState(state)
+ }
+
+ handleUpdateUserData = async (changes, callback) => {
+ const update = {}
+ if (Array.isArray(changes)) {
+ changes.forEach((change) => {
+ update[change.id] = change.value
+ })
+ }
+
+ await api.put
+ .selfUser(update)
+ .then((data) => {
+ callback(false, data)
+ })
+ .catch((err) => {
+ callback(true, err)
+ })
+
+ window.app.eventBus.emit("forceReloadUser")
+ }
+
+ openUserEdit = () => {
+ window.app.DrawerController.open("editAccount", AccountEditor, {
+ props: {
+ keyboard: false,
+ width: "45%",
+ bodyStyle: {
+ overflow: "hidden",
+ },
+ },
+ componentProps: {
+ onSave: this.handleUpdateUserData,
+ user: this.state.user,
+ },
+ })
+ }
+
+ renderSelfActions = () => {
+ if (this.state.isSelf) {
+ return (
+
+ )
+ }
+
+ return null
+ }
+
+ render() {
+ const user = this.state.user
+
+ if (!user) {
+ return
+ }
+
+ return (
+
+
+

+
+ {Boolean(user.fullName) ?
+ <>
+
{user.fullName}
+ @{user.username}#{user._id}
+ > :
+ <>
+ @{user.username}
+ #{user._id}
+ >
+ }
+
+
+ {this.state.isSelf && this.renderSelfActions()}
+
+
+ {this.state.isSelf && (
+
+ )}
+
+ )
+ }
+}
diff --git a/packages/app/src/pages/account/index.less b/packages/app/src/pages/account/index.less
new file mode 100644
index 00000000..a35a0e88
--- /dev/null
+++ b/packages/app/src/pages/account/index.less
@@ -0,0 +1,30 @@
+.account_wrapper{
+ > div {
+ margin-bottom: 10px;
+ }
+}
+
+.account_card{
+ display: flex;
+ font-family: 'Roboto Mono', monospace;
+
+ align-items: center;
+
+ padding: 12px;
+ color: #333;
+ border: 1px solid #33333396;
+ border-radius: 12px;
+
+ img {
+ width: 70px;
+ height: 70px;
+ }
+
+ h1 {
+ margin: 0;
+ font-size: 35px;
+ span {
+ font-size: 12px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/index.js b/packages/app/src/pages/index.js
deleted file mode 100644
index 0377ce38..00000000
--- a/packages/app/src/pages/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default () => {
- return
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/login/guest.js b/packages/app/src/pages/login/guest.js
deleted file mode 100644
index b57ca446..00000000
--- a/packages/app/src/pages/login/guest.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react'
-import * as antd from 'antd'
-import { connect } from 'umi'
-
-@connect(({ app }) => ({ app }))
-export default class GuestSession extends React.PureComponent {
- state = {
- accept: false,
- }
-
- render() {
- return (
-
-
-
-
this.setState({accept: e.target.checked})} /> You have read and accept the TOS
-
{ this.props.dispatch({ type: "guestLogin" }) }} > Continue
-
-
- )
- }
-}
diff --git a/packages/app/src/pages/login/index.js b/packages/app/src/pages/login/index.js
deleted file mode 100644
index 30b170c0..00000000
--- a/packages/app/src/pages/login/index.js
+++ /dev/null
@@ -1,132 +0,0 @@
-import React from 'react'
-import { iatToString } from 'core'
-import { router, ui } from 'core/libs'
-
-import styles from './index.less'
-import classnames from 'classnames'
-
-import * as antd from 'antd'
-import * as Icons from 'components/Icons'
-
-import RegistrationForm from './register.js'
-import NormalLoginForm from './login.js'
-import GuestSession from './guest.js'
-
-import config from 'config'
-import { connect } from 'umi'
-
-const types = [
- {
- id: "login",
- key: 0,
- renderText: `Sign in ${config.app.siteName}`
- },
- {
- id: "register",
- key: 1,
- renderText: "Register"
- },
- {
- id: "guest",
- key: 2,
- renderText: "Use guest session"
- },
- {
- id: "forgot",
- key: 3,
- renderText: "Forgotten password"
- },
-]
-
-const typesRenderMap = {
- 0: ,
- 1: ,
- 2:
-}
-
-@connect(({ app }) => ({ app }))
-class Login extends React.Component {
- state = {
- transition: false,
- key: 0,
- }
-
- renderHelperButtons() {
- return types.map((e) => {
- return (
- this.setState({ key: e.key })}>
- {e.renderText || "Invalid"}
-
- )
- })
- }
-
- componentDidMount() {
- if (this.props.app.session_valid) {
- ui.notify.info('You have already logged into an account, you can change your account by logging in again')
- }
- }
-
- componentWillUnmount() {
- antd.Modal.destroyAll()
- }
-
- renderAccountModal() {
- const dispatchLogout = () => this.props.dispatch({ type: "app/logout" })
- antd.Modal.confirm({
- title: this.props.app.session_data.username,
- icon: ,
- onOk() {
- router.push('/')
- },
- onCancel() {
- dispatchLogout()
- },
- okText: <>Resume>,
- cancelText: <>Logout>
- })
- }
-
- renderAccountCard() {
- if (this.props.app.session_authframe) {
- return (
-
-
this.renderAccountModal()}>
-
@{this.props.app.session_data.username}
-
Last login {iatToString(this.props.app.session_authframe.iat || 0)}
-
-
- )
- }
- return null
- }
-
- renderTitle() {
- return (
-
-
YulioID™
-
{types[this.state.key].renderText || "Auth"}
-
- )
- }
-
- render() {
- return (
-
-
-
- {this.renderTitle()}
-
-
- {typesRenderMap[this.state.key]}
-
- {this.renderHelperButtons()}
-
-
- {this.renderAccountCard()}
-
-
- )
- }
-}
-export default Login
diff --git a/packages/app/src/pages/login/index.jsx b/packages/app/src/pages/login/index.jsx
new file mode 100644
index 00000000..7a0f3587
--- /dev/null
+++ b/packages/app/src/pages/login/index.jsx
@@ -0,0 +1,145 @@
+import React from 'react'
+import config from "config"
+import * as antd from "antd"
+import { FormGenerator } from 'components'
+import { Icons } from 'components/Icons'
+
+import "./index.less"
+
+const formInstance = [
+ {
+ id: "username",
+ element: {
+ component: "Input",
+ icon: "User",
+ placeholder: "Username",
+ props: null
+ },
+ item: {
+ hasFeedback: true,
+ rules: [
+ {
+ required: true,
+ message: 'Please input your Username!',
+ },
+ ],
+ props: null
+ }
+ },
+ {
+ id: "password",
+ element: {
+ component: "Input",
+ icon: "Lock",
+ placeholder: "Password",
+ props: {
+ type: "password"
+ }
+ },
+ item: {
+ hasFeedback: true,
+ rules: [
+ {
+ required: true,
+ message: 'Please input your Password!',
+ },
+ ],
+ }
+ },
+ {
+ id: "login_btn",
+ withValidation: true,
+ element: {
+ component: "Button",
+ props: {
+ icon: "User",
+ children: "Login",
+ type: "primary",
+ htmlType: "submit"
+ }
+ }
+ },
+ {
+ id: "allowRegenerate",
+ withValidation: false,
+ element: {
+ component: "Checkbox",
+ props: {
+ children: "Not expire",
+ defaultChecked: false,
+ }
+ }
+ }
+]
+
+export default class Login extends React.Component {
+ static bindApp = ["sessionController"]
+
+ handleFinish = async (values, ctx) => {
+ ctx.toogleValidation(true)
+
+ const payload = {
+ username: values.username,
+ password: values.password,
+ allowRegenerate: values.allowRegenerate,
+ }
+
+ this.props.contexts.app.sessionController.login(payload, (error, response) => {
+ ctx.toogleValidation(false)
+ ctx.clearErrors()
+
+ if (error) {
+ ctx.shake("all")
+ return ctx.error("result", error)
+ } else {
+ if (response.status === 200) {
+ this.onDone()
+ }
+ }
+ })
+ }
+
+ onDone = () => {
+ if (typeof this.props.onDone === "function") {
+ this.props.onDone()
+ }
+ }
+
+ componentWillUnmount() {
+ window.app.SidebarController.toogleVisible(true)
+ window.app.HeaderController.toogleVisible(true)
+ }
+
+ componentDidMount() {
+ if (window.app.SidebarController.isVisible()) {
+ window.app.SidebarController.toogleVisible(false)
+ }
+
+ if (window.app.HeaderController.isVisible()) {
+ window.app.HeaderController.toogleVisible(false)
+ }
+ }
+
+ render() {
+ return (
+
+ {this.props.session?.valid &&
+
You already have a valid session.
+
+ @{this.props.session.username}
+
+
window.app.setLocation(config.app?.mainPath ?? "/main")} >Go to main
+
}
+
+
+
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/login/index.less b/packages/app/src/pages/login/index.less
index b4c52642..4ae1a815 100644
--- a/packages/app/src/pages/login/index.less
+++ b/packages/app/src/pages/login/index.less
@@ -1,244 +1,46 @@
-@import '~theme/index.less';
+.app_login {
+ height: 100vh;
+ width: 100vw;
-.login_wrapper{
- width: 100%;
- height: 100%;
- font-family: "Poppins", sans-serif!important;
- h1,h2,h3,h4,h5,h6{color: #333;}
- position: absolute;
- top: 0;
- left: 0;
- display: flex;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 20px;
- margin: auto;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
- overflow-y: scroll;
- overflow-x: hidden;
- &.goOut{
- .auth_box{
- -webkit-animation-name: fadeOutLeft;
- animation-name: fadeOutLeft;
- }
- }
- transition: all 300ms ease-in-out;
+ > div {
+ margin-bottom: 20px;
+ }
}
-
-.auth_box{
- display: flex;
- transition: all 300ms ease-in-out;
-
- .yid_logo {
- vertical-align: middle;
- height: 17px;
- }
- :global{
- input:-webkit-autofill,
- input:-webkit-autofill:hover,
- input:-webkit-autofill:focus,
- textarea:-webkit-autofill,
- textarea:-webkit-autofill:hover,
- textarea:-webkit-autofill:focus,
- select:-webkit-autofill,
- select:-webkit-autofill:hover,
- select:-webkit-autofill:focus {
- border: 0;
- -webkit-text-fill-color: #333;
- -webkit-box-shadow: 0 0 0 1000px #ffffff98 inset;
- box-shadow: #fff 0 0 0 1000px inset;
- transition: background-color 5000s ease-in-out 0s;
- }
-
- .ant-input-affix-wrapper {
- width: 100%;
- height: 40px;
- padding: 4px 11px;
- color: #333;
- font-size: 14px;
- line-height: 1.5715;
- background-color: #fff; //rgba(255, 255, 255, 0.596);
- border: 1.5px #2F66DF solid;
- border-radius: 7px;
- }
- }
+.login-form {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
}
-.centering_wrapper{
- width: 100%;
- text-align: center;
-}
-
-.left_body{
- z-index: 50;
- transform: translate(12px,0);
- float: left;
- width: 30%;
- color: #333;
- background-color: #fff;
- padding: 20px;
- border-radius: 12px 0 0 12px;
- box-shadow: 0 10px 20px 0 rgba(51,51,51,0.52);
- transition: all 300ms ease-in-out;
-}
-
-.right_body{
- z-index: 51;
- float: right;
- width: 70%;
- max-height: -webkit-fill-available;
- height: 150px;
- padding: 20px 50px;
- color: #333;
- background-color: #fff;
- border-radius: 12px;
- box-shadow: 0 10px 20px 0 rgba(51,51,51,0.52);
- transition: all 300ms ease-in-out;
-}
-
-.third_body{
- z-index: 50;
- transform: translate(0, -24px);
- float: right;
- width: 100%;
- max-height: -webkit-fill-available;
- height: 120px;
- padding: 45px 50px;
- color: #333;
- background-color: #fff;
- border-radius: 12px;
- box-shadow: 0 10px 20px 0 rgba(51, 51, 51, 0.52);
- transition: all 300ms ease-in-out;
-
- .last_auth{
- font-size: 14px;
-
- }
-}
-
-.third_body:hover{
- background-color: rgba(255, 255, 255, 0.3);
- box-shadow: 0 10px 20px 0 rgba(51, 51, 51, 0.8);
- transform: translate(0, -16px);
-}
-
-.helper_login_btn{
- transform: translate(-20px, -14px);
-}
-
-.login_helper_footer{
- width: 100%;
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- display: flex;
- margin: auto;
- :global{
- .ant-btn{
- margin: auto;
- padding: 0 5px;
- }
- }
-
- transform: translate(0, -10px);
-
-}
-
-
-@keyframes go-out {
- 0% {
- filter: blur(0)
- }
-
- 100% {
- filter: blur(15px)
- }
-}
-
-
-// Medium format max-width: 830px
-@media (min-width: 486px){
- .auth_box {
- padding: 0 16px 40px;
- width: 500px;
- min-height: 500px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- }
- .left_body{
- width: 100%;
- float: none;
- border-radius: 12px 12px 0 0;
- padding: 20px;
- transform: translate(0, 22px);
- }
- .right_body{
- width: 100%;
- height: 100%;
- float: none;
- padding: 20px 60px;
- }
-}
-
-// Mobile format
-@media (max-width: 485px){
- .auth_box {
- position: absolute;
- top: 0;
- left: 0;
- transition: all 150ms ease-in-out;
- padding: 70px 16px 40px;
- width: 100%;
- height: 100%;
- min-height: 500px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- border-radius: 0;
- }
- .left_body{
- box-shadow: none;
- width: 100%;
- float: none;
- position: absolute;
- top: 0;
- margin-top: 30px;
- transform: translate(0, 0);
- }
- .right_body{
- box-shadow: none;
- width: 100%;
- float: none;
- padding: 20px;
- }
-}
-
-@-webkit-keyframes fadeOutLeft {
- from {
- opacity: 1;
- }
-
- to {
- opacity: 0;
- -webkit-transform: translate3d(-100%, 0, 0);
- transform: translate3d(-100%, 0, 0);
- }
-}
-
-@keyframes fadeOutLeft {
- from {
- opacity: 1;
- }
-
- to {
- opacity: 0;
- -webkit-transform: translate3d(-100%, 0, 0);
- transform: translate3d(-100%, 0, 0);
- }
+.session_available {
+ width: fit-content;
+ height: fit-content;
+
+ padding: 20px;
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+
+ border: 1px solid #e5e5e5;
+ border-radius: 8px;
+
+ .session_card {
+ width: fit-content;
+ height: fit-content;
+
+ margin: 10px;
+ padding: 5px 10px;
+
+ border: 1px solid #e5e5e5;
+ border-radius: 8px;
+ }
}
diff --git a/packages/app/src/pages/login/login.js b/packages/app/src/pages/login/login.js
deleted file mode 100644
index 77951711..00000000
--- a/packages/app/src/pages/login/login.js
+++ /dev/null
@@ -1,229 +0,0 @@
-import React from 'react'
-import styles from './index.less'
-
-import Fade from 'react-reveal/Fade'
-import HeadShake from 'react-reveal/HeadShake';
-
-import * as antd from 'antd'
-
-import { Form, Input, Button, Checkbox } from 'antd'
-import {
- UserOutlined,
- LockOutlined,
- BulbOutlined,
- SwapLeftOutlined
-} from 'components/Icons'
-import { connect } from 'umi'
-
-@connect(({ app, socket }) => ({ app, socket }))
-export default class NormalLoginForm extends React.Component {
- state = {
- usernameRaw: null,
- passwordRaw: null,
-
- step: 1,
- validating: false,
- error_count: 0,
- step_error: false,
- step_show: true,
- }
-
- next() {
- let step = this.state.step
- this.setState({ validating: true, step_error: false })
- switch (step) {
- case 1: {
- if (this.state.usernameRaw) {
- const payload = { username: this.state.usernameRaw }
- user.get.basicData(payload, (err, res) => {
- if (res.code == 200) {
- step++
- this.anim_transition(50)
- return this.setState({
- validating: false,
- early_data: res.response,
- step: step,
- })
- }
- if (res.code == 210) {
- return this.anim_error()
- }
- return false
- })
- } else {
- return this.anim_error()
- }
- }
- case 2: {
- return this.auth()
- }
- default:
- return false
- }
- }
-
- back() {
- if (this.state.step > 1) {
- this.state.step--
- this.anim_transition(150)
- }
- this.setState({ step: this.state.step })
- }
-
- anim_transition(duration) {
- this.setState({ step_show: false })
- setTimeout(() => {
- this.setState({ step_show: true })
- }, duration || 150)
- }
-
- anim_error() {
- this.setState({ step_error: true, error_count: (this.state.error_count + 1), validating: false })
- }
-
- onChangeField(event) {
- if(!this.state) {
- return false
- }
- let updated = this.state
- updated[event.target.id] = event.target.value
- this.setState(updated)
- }
-
- auth() {
- const { usernameRaw, passwordRaw } = this.state
- if (!usernameRaw || !passwordRaw) return false
- this.setState({ step_error: false, validating: true })
-
- this.props.dispatch({
- type: 'app/login',
- payload: { username: usernameRaw, password: passwordRaw },
- callback: (callbackResponse) => {
- console.log(callbackResponse)
- this.setState({ validating: false })
- switch (callbackResponse) {
- case 100: {
- return null
- }
- case 400: {
- console.log('Credentials error')
- return this.anim_error()
- }
- case 500: {
- console.log('Server error')
- return this.back()
- }
- default: {
- console.log('Unknown error')
- return this.back()
- }
- }
- }
- })
- }
-
- renderFormItems = {
- username: () => {
- return (
-
- this.next()}
- id="usernameRaw"
- onChange={(e) => this.onChangeField(e)}
- prefix={}
- placeholder="Username or Email"
- />
-
- )
- },
- password: () => {
- return (
- <>
- Welcome Back @{this.state.early_data.username}
-
- this.next()}
- disabled={this.state.validating}
- id="passwordRaw"
- prefix={}
- onChange={(e) => this.onChangeField(e)}
- placeholder="Password"
- />
-
- >
- )
- }
- }
-
- renderButtons() {
- const PrimaryButton = () => {
- return (
-
- )
- }
-
- const SecondaryButton = () => {
- return (
-
- )
- }
- if (this.state.step > 1) {
- return
- }
- return
- }
-
- render() {
- return (
-
-
-
-
- {this.renderButtons()}
-
- )
- }
-}
diff --git a/packages/app/src/pages/login/register.js b/packages/app/src/pages/login/register.js
deleted file mode 100644
index 2c48a4ac..00000000
--- a/packages/app/src/pages/login/register.js
+++ /dev/null
@@ -1,151 +0,0 @@
-import React from 'react'
-import * as Icons from 'components/Icons'
-import styles from './index.less'
-
-import {
- Form,
- Input,
- Tooltip,
- Cascader,
- Select,
- Row,
- Col,
- Checkbox,
- Button,
- AutoComplete,
-} from 'antd'
-
-import ReCAPTCHA from 'react-google-recaptcha'
-import { g_recaptcha_key } from 'config/keys'
-
-export default class RegistrationForm extends React.Component {
- state = {
- usernameRaw: null,
- passwordRaw: null,
- emailRaw: null,
-
- captchaValue: null
- }
-
- handleRegister() {
-
- }
-
- onFinish(values) {
- console.log('Received values of form: ', values)
- }
-
- onCaptcha(values) {
- this.setState({ captchaValue: values })
- }
-
- onChangeField(event) {
- if(!this.state) {
- return false
- }
- let updated = this.state
- updated[event.target.id] = event.target.value
- this.setState(updated)
- }
-
- renderForm() {
- return (
-
- this.onChangeField(e)} placeholder="randomuser" prefix={} />
-
-
- this.onChangeField(e)} placeholder="example@no-real.com" prefix={} />
-
-
-
- this.onChangeField(e)} placeholder="mysupersecretpassword" prefix={} />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- value
- ? Promise.resolve()
- : Promise.reject('Should accept agreement'),
- },
- ]}
- >
-
- I have read the agreement
-
-
-
-
-
-
- )
- }
-
- render() {
- return (
-
- { this.renderForm()}
-
- )
- }
-
-}
diff --git a/packages/app/src/pages/logout.js b/packages/app/src/pages/logout.js
deleted file mode 100644
index 8e29686f..00000000
--- a/packages/app/src/pages/logout.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react'
-import * as antd from 'antd'
-import { connect } from 'umi'
-import { router } from 'core/libs';
-import { Home, Trash } from 'components/Icons'
-@connect(({ app }) => ({ app }))
-export default class Logout extends React.Component{
-
- componentDidMount(){
- if (!this.props.app.session_valid) {
- return false
- }
- const dispatchLogout = () => this.props.dispatch({ type: "app/logout" })
-
- antd.Modal.confirm({
- title: this.props.app.session_data.username,
- icon: ,
- content: 'Are you sure you want to log out',
- onOk() {
- router.push('/')
- },
- onCancel() {
- dispatchLogout()
- },
- okText: <>Resume>,
- cancelText: <>Logout>
- });
- }
-
- componentWillUnmount(){
- antd.Modal.destroyAll()
- }
-
- render(){
- return null
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/main/index.jsx b/packages/app/src/pages/main/index.jsx
new file mode 100644
index 00000000..ac3d2fef
--- /dev/null
+++ b/packages/app/src/pages/main/index.jsx
@@ -0,0 +1,86 @@
+import React from "react"
+import * as antd from "antd"
+import { AppSearcher } from "components"
+import * as charts from "react-chartjs-2"
+
+import "./index.less"
+
+const data = {
+ labels: ["Red", "Orange", "Blue"],
+ datasets: [
+ {
+ label: "Popularity of colours",
+ data: [55, 23, 96],
+ borderWidth: 5,
+ fill: false,
+ },
+ ],
+}
+
+const chartKeys = Object.fromEntries(Object.keys(charts).map((key) => {
+ return [String(key).toLowerCase(), key]
+}))
+
+class ChartGenerator extends React.Component {
+ constructor(payload) {
+ super(payload)
+ this.payload = payload
+
+ this.type = this.payload.type
+ this.Chart = charts[this.type] ?? charts[chartKeys[this.type]]
+
+ this.state = {
+ labels: [],
+ datasets: [],
+ }
+
+ if (!this.Chart) {
+ console.error("Chart type is not valid")
+ }
+ }
+
+ render() {
+ const { Chart } = this
+
+ if (React.isValidElement(Chart)) {
+ return null
+ }
+
+ return
+ }
+}
+
+export default class Main extends React.Component {
+ componentWillUnmount() {
+ if (!window.app?.HeaderController?.isVisible()) {
+ window.app.HeaderController.toogleVisible(true)
+ }
+ }
+
+ componentDidMount() {
+ if (window.app?.HeaderController?.isVisible()) {
+ window.app.HeaderController.toogleVisible(false)
+ }
+ }
+
+ render() {
+ const user = this.props.user ?? {}
+
+ return (
+
+
+
+
Welcome back, {user.fullName ?? user.username ?? "Guest"}
+
+
+
+
+
+ )
+ }
+}
diff --git a/packages/app/src/pages/main/index.less b/packages/app/src/pages/main/index.less
new file mode 100644
index 00000000..5660d036
--- /dev/null
+++ b/packages/app/src/pages/main/index.less
@@ -0,0 +1,30 @@
+.dashboard {
+ padding-top: 20px;
+ width: 100%;
+
+ h1{
+ font-size: 28px;
+ margin: 0;
+ }
+
+ .topΒ {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ > div {
+ margin-right: 20px;
+ }
+ }
+
+ > div {
+ margin-bottom: 20px;
+ }
+
+ .content {
+ > div {
+ margin-left: 20px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/new_streaming/index.js b/packages/app/src/pages/new_streaming/index.js
deleted file mode 100644
index 2e9c56f9..00000000
--- a/packages/app/src/pages/new_streaming/index.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react'
-import * as antd from 'antd'
-import { connect } from 'umi'
-
-@connect(({ app }) => ({ app }))
-export default class NewStreaming extends React.Component{
- render(){
- return(
-
-
-
- )
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/post.js b/packages/app/src/pages/post.js
deleted file mode 100644
index 46c6911f..00000000
--- a/packages/app/src/pages/post.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from 'react'
-import { connect } from 'umi'
-import { PostsFeed } from 'components'
-
-@connect(({ app }) => ({ app }))
-export default class Post extends React.Component{
- state = {
- postID: null
- }
- componentDidMount(){
- this.setState({ postID: new URLSearchParams(location.search).get('key') })
- }
- render(){
- if (!this.state.postID) {
- return null
- }
- return
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/saves.js b/packages/app/src/pages/saves.js
deleted file mode 100644
index fec5f5ac..00000000
--- a/packages/app/src/pages/saves.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import React from 'react'
-import { connect } from 'umi'
-import { PostsFeed } from 'components'
-
-@connect(({ app }) => ({ app }))
-export default class Saves extends React.Component{
- render(){
- return
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/about/index.js b/packages/app/src/pages/settings/components/about/index.js
deleted file mode 100644
index cb09617b..00000000
--- a/packages/app/src/pages/settings/components/about/index.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react'
-import { About } from 'components'
-import * as Icons from 'components/Icons'
-import * as antd from 'antd'
-import l from 'schemas/links'
-
-export default class AppAbout extends React.Component {
- render() {
- const handleClickLinks = (e) => {
- const link = l[e]
- link ? window.openLink(link) : console.log("Link not available")
- }
- return <>
-
-
-
-
-
-
- >
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/earnings/index.js b/packages/app/src/pages/settings/components/earnings/index.js
deleted file mode 100644
index 997e5926..00000000
--- a/packages/app/src/pages/settings/components/earnings/index.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react'
-import * as antd from 'antd'
-import * as Icons from 'components/Icons'
-
-
-export default class Earnings extends React.Component {
- state = {
- loading: true,
- pro_points: 0
- }
- componentDidMount(){
- // app.comty_data.get_user_data((err,res)=>{
- // if (err) return false
- // try {
- // const a = JSON.parse(res)['user_data']
- // this.setState({ loading: false, pro_points: a.points })
- // } catch (error) {
-
- // }
- // })
-
- }
- render() {
- return (
-
-
-
-
Your Pro Points
- {this.state.pro_points}
-
-
- )
- }
-}
diff --git a/packages/app/src/pages/settings/components/electron/index.js b/packages/app/src/pages/settings/components/electron/index.js
deleted file mode 100644
index 8a1dd019..00000000
--- a/packages/app/src/pages/settings/components/electron/index.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import React from 'react'
-import * as antd from 'antd'
-import * as Icons from 'components/Icons'
-import styles from './index.less'
-import { connect } from 'umi';
-import { package_json } from 'core'
-import { objectToArrayMap } from '@nodecorejs/utils'
-
-const AppTech = (info) => {
- if (!info) return null
- return(
-
-
-
react
{info.react_version}
-
{info.process.versions.v8}
-
-
{info.process.versions.electron}
-
Webpack
-
Socket.io
-
JS
-
TS
-
WebAssembly
-
OpenAI
-
- )
-}
-
-
-@connect(({ app }) => ({ app }))
-export default class ElectronSettings extends React.PureComponent{
- state = {
- loading: true,
- info: []
- }
-
- getInfo(){
- this.setState({ loading: true })
- this.setState({
- loading: false,
- info: {
- g_umi: window.g_umi,
- process: window.process,
- react_version: React.version,
- deps: objectToArrayMap(package_json.dependencies)
- }
- })
- }
-
- componentDidMount(){
- this.getInfo()
- }
-
-
- render(){
- const showAppTech = () => {
- antd.Modal.info({
- title: package_json.title,
- content: AppTech(this.state.info),
- width: 550
- })
- }
-
- const showThirdParty = () => {
- const generateList = () => {
- return this.state.info.deps.map((e) => {
- return(
-
- -> {e.key}
{e.value.slice(1,e.value.length)}
-
- )
- })
- }
-
- antd.Modal.info({
- title: package_json.title,
- content: generateList(),
- width: 550
- })
- }
-
- if (this.state.loading){
- return
- }
- return(
-
-
showAppTech()}> App Technologies
-
showThirdParty()}> Third-Party
-
- )
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/electron/index.less b/packages/app/src/pages/settings/components/electron/index.less
deleted file mode 100644
index 808ab280..00000000
--- a/packages/app/src/pages/settings/components/electron/index.less
+++ /dev/null
@@ -1,15 +0,0 @@
-.versions{
- overflow: scroll;
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- grid-template-rows: repeat(4, 1fr);
- grid-column-gap: 15px;
- grid-row-gap: 15px;
-
- > div {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/general/index.js b/packages/app/src/pages/settings/components/general/index.js
deleted file mode 100644
index 41b0cb59..00000000
--- a/packages/app/src/pages/settings/components/general/index.js
+++ /dev/null
@@ -1,83 +0,0 @@
-import React from 'react'
-import { List, Button, Switch, Checkbox, InputNumber, Input } from 'antd'
-import * as Icons from 'components/Icons'
-
-import { verbosity } from '@nodecorejs/utils'
-import { settings, newSetting } from 'core/libs/settings'
-import listSettings from 'schemas/settings_general.json'
-
-const AntdComponents = { Button, Switch, Checkbox, InputNumber, Input }
-export default class GeneralSettings extends React.Component {
- state = {
- list: listSettings,
- }
-
- renderSetting = (item) => {
- if (!item.type || !item.id) {
- verbosity.log("Invalid component >", item)
- return null
- }
- if (typeof(AntdComponents[item.type]) == "undefined") {
- verbosity.log(`Invalid component, '${item.type}' not exists >`, item)
- return null
- }
-
- let itemProps = {
- onChange: (e) => this.onChange(item, e),
- checked: settings.get(item.id)
- }
-
- switch (item.type) {
- case 'Switch': {
- itemProps = { ...itemProps } // checkedChildren: "Enabled", unCheckedChildren: "Disabled"
- break
- }
- default:
- break
- }
-
- return React.createElement(AntdComponents[item.type], itemProps)
- }
-
- onChange(item, event) {
- try {
- let to = event
-
- verbosity.colors({ log: { textColor: "blue" } }).log(`Updating setting (${item.id}) > ${to}`)
- settings.set(item.id, to)
-
- const updatedValues = this.state.list.map(element =>
- element.id === item.id ? Object.assign(element, { value: to }) : element
- )
- this.setState({ list: updatedValues })
- } catch (err) {
- console.log(err)
- }
- }
-
- renderIcon(icon, props) {
- if (!Icons[icon]) {
- verbosity.log(`${icon} not exist!`)
- return null
- }
- return React.createElement(Icons[icon], props ?? null)
- }
-
- render() {
- return (
- (
-
- {this.renderIcon(item.icon)}{item.title}>}
- description={item.description}
- />
- {this.renderSetting(item)}
-
- )}
- />
- )
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/help/index.js b/packages/app/src/pages/settings/components/help/index.js
deleted file mode 100644
index 75efbdc3..00000000
--- a/packages/app/src/pages/settings/components/help/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react'
-
-export default class Help extends React.Component{
- render(){
- return <>
-
- >
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/keybinds/index.js b/packages/app/src/pages/settings/components/keybinds/index.js
deleted file mode 100644
index 2864fd7b..00000000
--- a/packages/app/src/pages/settings/components/keybinds/index.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import React from 'react'
-import { connect } from 'umi'
-
-@connect(({ app }) => ({ app }))
-export default class Keybinds extends React.Component {
- render() {
- return <>>
- }
-}
-
diff --git a/packages/app/src/pages/settings/components/notification/index.js b/packages/app/src/pages/settings/components/notification/index.js
deleted file mode 100644
index 2b712970..00000000
--- a/packages/app/src/pages/settings/components/notification/index.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react'
-import * as Icons from 'components/Icons'
-import styles from './index.less'
-
-export default class NotificationView extends React.Component {
- render() {
- return (
-
-
-
- )
- }
-}
-
diff --git a/packages/app/src/pages/settings/components/notification/index.less b/packages/app/src/pages/settings/components/notification/index.less
deleted file mode 100644
index 4eab7076..00000000
--- a/packages/app/src/pages/settings/components/notification/index.less
+++ /dev/null
@@ -1,17 +0,0 @@
-@import '~theme/index.less';
-
-.main {
-
- :global {
- h2 {
- font-weight: 500;
- }
-
- .anticon {
- color: #2d2d2d;
-
- }
-
-
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/plugins/index.js b/packages/app/src/pages/settings/components/plugins/index.js
deleted file mode 100644
index 4bb9481d..00000000
--- a/packages/app/src/pages/settings/components/plugins/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react'
-import {connect} from 'umi'
-@connect(({ app }) => ({ app }))
-export default class Plugins extends React.Component {
- render() {
- return <>
- >
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/security/index.js b/packages/app/src/pages/settings/components/security/index.js
deleted file mode 100644
index ce0a704b..00000000
--- a/packages/app/src/pages/settings/components/security/index.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from 'react'
-import * as Icons from 'components/Icons'
-import * as antd from 'antd'
-import styles from './index.less'
-
-import Sessions_Manager from './sessions.js'
-
-const { Menu } = antd
-export default class SecurityView extends React.Component {
- state = {
- current: 'privacy',
- }
-
- handleClick = e => {
- this.setState({
- current: e.key,
- })
- }
-
- renderChildren = () => {
- const { current } = this.state
- switch (current) {
- case 'privacy':
- return null
- case 'credentials':
- return null
- case 'sessions':
- return
- default:
- break
- }
- return null
- }
-
- render() {
- return (
-
-
-
{this.renderChildren()}
-
- )
- }
-}
-
diff --git a/packages/app/src/pages/settings/components/security/index.less b/packages/app/src/pages/settings/components/security/index.less
deleted file mode 100644
index 4eab7076..00000000
--- a/packages/app/src/pages/settings/components/security/index.less
+++ /dev/null
@@ -1,17 +0,0 @@
-@import '~theme/index.less';
-
-.main {
-
- :global {
- h2 {
- font-weight: 500;
- }
-
- .anticon {
- color: #2d2d2d;
-
- }
-
-
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/security/sessions.js b/packages/app/src/pages/settings/components/security/sessions.js
deleted file mode 100644
index 5d4bbe42..00000000
--- a/packages/app/src/pages/settings/components/security/sessions.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from 'react'
-import * as antd from 'antd'
-
-export default class Sessions_Manager extends React.Component {
- state = {
- sessions_data: '',
- }
- componentDidMount() {
- // app.comty_data.session_id((err, res) => {
- // this.setState({ sid: res })
- // })
- // app.comty_data.sessions((err, res) => {
- // const a = JSON.parse(res)['data']
- // this.setState({ sessions_data: a })
- // })
- }
- render() {
- return (
-
-
(
-
-
- Session #{item.id}
- {this.state.sid == item.session_id ? 'This Session' : null}
-
- {item.platform}
- {item.ip_address}
- {item.time}
-
-
- )}
- />
-
- )
- }
-}
diff --git a/packages/app/src/pages/settings/components/theme/components/background/index.js b/packages/app/src/pages/settings/components/theme/components/background/index.js
deleted file mode 100644
index 076366b8..00000000
--- a/packages/app/src/pages/settings/components/theme/components/background/index.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import React from 'react'
-import * as Icons from 'components/Icons'
-import * as antd from 'antd'
-import { connect } from 'umi'
-import styles from '../../index.less'
-
-import { theme, getOptimalOpacityFromIMG, get_style_rule_value } from 'core/libs/style'
-import { urlToBase64, imageToBase64, arrayToObject } from 'core'
-import ThemeConfigurator from '../../configurator'
-
-@connect(({ app }) => ({ app }))
-export default class BackgroundImage extends ThemeConfigurator {
- state = {
- configKey: "backgroundImage",
- model: { active: false, opacity: null, src: null },
-
- textColor: this.rgbToScheme(getComputedStyle(document.getElementById("appWrapper")).color),
- overlayColor: this.rgbToScheme(getComputedStyle(document.getElementById("appWrapper")).backgroundColor),
-
- processing: null,
- fileURL: null,
- customURL: '',
- }
-
- handleFileUpload = info => {
- if (info.file.status === 'uploading') {
- return this.setState({ processing: false })
- }
- if (info.file.status === 'done') {
- this.setState({ processing: true })
- imageToBase64(info.file.originFileObj, fileURL => {
- this.setState({ fileURL: fileURL })
- this.proccessBackground(fileURL)
- })
- }
- }
-
- handleCustomURL(url) {
- this.setState({ processing: true, fileURL: url })
- urlToBase64(url, fileURL => {
- this.proccessBackground(fileURL)
- })
- }
-
- proccessBackground(data) {
- getOptimalOpacityFromIMG({ textColor: this.state.textColor, overlayColor: this.state.overlayColor, img: data }, (res) => {
- this.handleUpdate({ active: true, src: this.state.fileURL, opacity: res })
- })
- }
-
- schemeToRGB(values) {
- const scheme = values ? values : { r: '0', g: '0', b: '0' }
- const r = scheme.r || '0'
- const g = scheme.g || '0'
- const b = scheme.b || '0'
- return `rgb(${r}, ${g}, ${b})`
- }
-
- rgbToScheme(rgb) {
- const values = rgb.replace(/[^\d,]/g, '').split(',');
- return { r: values[0], g: values[1], b: values[2] }
- }
-
- renderPreviewModel() {
- return (
-
-
Preview
- { this.state.model.src ?
-
-
Sample text
-
Sample text
-
Sample text
-
Sample text
-
Some text here
-
Some text here
-
-

-
:
No Background
}
-
- )
- }
-
- renderUploader() {
- return (
-
-
Upload
-
-
- Upload from your files
-
- } />
-
-
-
-
Or
-
-
- Upload from URL
-
this.handleCustomURL(this.state.customURL)} onChange={e => this.setState({ customURL: e.target.value })} value={this.state.customURL} placeholder="http://example.com/my_coolest_image.jpg" />
-
-
-
- {this.state.processing ?
Processing image ...
: null}
- {this.state.params ? JSON.stringify(this.state.params) : null}
-
- )
- }
-
- renderControls() {
- return (
-
-
-
Enabled
-
{ this.promiseState(prevState => ({ model: { ...prevState.model, active: e } })).then(() => this.handleUpdate()) }}
- checked={this.state.model.active}
- />
-
-
-
-
Opacity
-
{ this.setState(prevState => ({ model: { ...prevState.model, opacity: e / 100 } })) }} onAfterChange={() => this.handleUpdate()} value={this.state.model.opacity * 100} />
-
-
-
Export Code
-
this.handleExport()}> Export
-
-
-
Import Code
-
null}> Import
-
-
-
Erase
-
this.handleErase()} okText="Yes" cancelText="No">
- Delete
-
-
-
-
- )
- }
-
- render() {
- return (
-
- {this.renderControls()}
-
- {this.renderPreviewModel()}
-
- {this.renderUploader()}
-
-
- )
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/theme/components/color/index.js b/packages/app/src/pages/settings/components/theme/components/color/index.js
deleted file mode 100644
index eeccc90e..00000000
--- a/packages/app/src/pages/settings/components/theme/components/color/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react'
-import * as Icons from 'components/Icons'
-import * as antd from 'antd'
-import styles from '../../index.less'
-import ThemeConfigurator from '../../configurator'
-
-export default class Colors extends ThemeConfigurator {
- state = {
- configKey: "colors",
- model: { active: false }
- }
- render() {
- return (
-
- )
- }
-}
diff --git a/packages/app/src/pages/settings/components/theme/components/darkmode/index.js b/packages/app/src/pages/settings/components/theme/components/darkmode/index.js
deleted file mode 100644
index 07841be4..00000000
--- a/packages/app/src/pages/settings/components/theme/components/darkmode/index.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react'
-import * as Icons from 'components/Icons'
-import * as antd from 'antd'
-import styles from '../../index.less'
-import ThemeConfigurator from '../../configurator'
-
-export default class DarkMode extends ThemeConfigurator {
- state = {
- configKey: "darkmode",
- model: { active: false }
- }
- render() {
- return (
-
-
-
Enabled
-
{ this.promiseState(prevState => ({ model: { ...prevState.model, active: e } })).then(() => this.handleUpdate()) }}
- checked={this.state.model.active}
- />
-
-
- )
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/theme/configurator.js b/packages/app/src/pages/settings/components/theme/configurator.js
deleted file mode 100644
index facf34eb..00000000
--- a/packages/app/src/pages/settings/components/theme/configurator.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from 'react'
-import ErrorHandler from 'core/libs/errorhandler'
-import { theme } from 'core/libs/style'
-import exportDataAsFile from 'core/libs/ui/export_data'
-import { verbosity } from '@nodecorejs/utils'
-
-export default class ThemeConfigurator extends React.Component {
- componentDidMount() {
- this.applyStoraged()
- }
-
- applyStoraged() {
- const storaged = theme.get()
- if (storaged && this.state) {
- if (storaged[this.state.configKey]) {
- return this.setState({ model: storaged[this.state.configKey] })
- } else {
- return verbosity.log(`cannot get storagedSetting for ${this.state.configKey}`)
- }
- }
- }
-
- promiseState = async state => new Promise(resolve => this.setState(state, resolve));
-
- handleUpdate(payload) {
- if (!this.state.configKey) {
- return ErrorHandler({ msg: `cannot update without 'configKey', is missing`, code: 140 })
- }
- if (!payload) {
- payload = this.state.model
- }
- this.setState({ model: payload, processing: false })
- window.dispatcher({
- type: 'app/updateTheme',
- payload: {
- key: this.state.configKey,
- value: payload
- }
- });
- }
-
- handleErase() {
- this.handleUpdate({})
- }
-
- handleExport() {
- exportDataAsFile({ data: JSON.stringify(this.state.model), type: 'text/json' })
- }
-
-}
diff --git a/packages/app/src/pages/settings/components/theme/index.js b/packages/app/src/pages/settings/components/theme/index.js
deleted file mode 100644
index f969f7be..00000000
--- a/packages/app/src/pages/settings/components/theme/index.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import React from 'react'
-import * as Icons from 'components/Icons'
-import * as antd from 'antd'
-import { connect } from 'umi'
-import { arrayToObject } from 'core'
-import ThemeSettingsList from 'schemas/theme_settings.json'
-
-import BackgroundSetting from './components/background'
-import DarkmodeSetting from './components/darkmode'
-import ColorSetting from './components/color'
-
-const componentsMap = {
- backgroundImage: ,
- darkmode: ,
- color: ,
-}
-
-@connect(({ app }) => ({ app }))
-export default class ThemeSettings extends React.Component {
- state = {
- selectedKey: null,
- keys: []
- }
-
- componentDidMount() {
- let mix = []
- ThemeSettingsList.forEach(e => {
- mix[e.id] = e
- })
- this.setState({ keys: mix })
- }
-
- renderSelectedKey() {
- const selectedKeyItem = this.state.keys[this.state.selectedKey] ?? { icon: null, title: null }
- return (
- this.setState({ selectedKey: null })}
- visible={this.state.selectedKey ? true : false}
- >
-
-
-
{selectedKeyItem.icon} {selectedKeyItem.title}
-
-
- {componentsMap[this.state.selectedKey]}
-
-
- )
- }
-
- render() {
- const handleClick = (key) => this.setState({ selectedKey: key })
- const isActive = (key) => { return key ? key.active : false }
- return (
-
-
(
-
-
handleClick(item.id)}>
- {React.createElement(Icons[item.icon])}{item.title} {isActive(arrayToObject(this.props.app.app_theme)[item.id]) ? "Enabled" : "Disabled"}
- {item.description}
-
-
- )}
- />
- {this.renderSelectedKey()}
-
- )
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/components/theme/index.less b/packages/app/src/pages/settings/components/theme/index.less
deleted file mode 100644
index 9c0a5ce2..00000000
--- a/packages/app/src/pages/settings/components/theme/index.less
+++ /dev/null
@@ -1,51 +0,0 @@
-.background_image_controls{
- width: 100%;
- margin: auto;
- display: flex;
- flex-direction: row;
-
- > div {
- width: 100%;
- margin: 0 10px;
- }
-}
-
-.background_image_uploader{
- width: 100%;
- margin: auto;
- display: flex;
- flex-direction: row;
- text-align: center;
-
- > div {
- width: 100%;
- margin: 0 10px;
- }
-}
-
-.background_image_preview{
- position: relative;
- height: 50%;
- border-radius: 8px;
-
- img{
- z-index: 9;
- width: 100%;
- border-radius: 8px;
- }
-
- .text_wrapper{
- position: absolute;
- z-index: 10;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 100%;
- width: 100%;
-
- h1{
- font-size: 68px;
- }
- }
-}
\ No newline at end of file
diff --git a/packages/app/src/pages/settings/index.js b/packages/app/src/pages/settings/index.js
deleted file mode 100644
index 7e21ae50..00000000
--- a/packages/app/src/pages/settings/index.js
+++ /dev/null
@@ -1,81 +0,0 @@
-import React from 'react'
-import * as Icons from 'components/Icons'
-
-import { ListedMenu } from 'components'
-
-import NotificationView from './components/notification'
-import SecurityView from './components/security'
-import Base from './components/general'
-import AppAbout from './components/about'
-import Theme from './components/theme'
-import ElectronApp from './components/electron'
-import Keybinds from './components/keybinds'
-import Plugins from './components/plugins'
-
-const Settings = {
- base: ,
- about: ,
- keybinds: ,
- theme: ,
- plugins: ,
- security: ,
- notification: ,
- app:
-}
-
-const menuList = [
- {
- key: "base",
- title: "General",
- icon: ,
- },
- {
- key: "app",
- title: "Application",
- icon: ,
- require: "embedded"
- },
- {
- key: "keybinds",
- title: "Keybinds",
- icon:
- },
- {
- key: "theme",
- title: "Customization",
- icon: ,
- },
- {
- key: "plugins",
- title: "Plugins",
- icon: ,
- },
- {
- key: "security",
- title: "Security & Privacity",
- icon: ,
- },
- {
- key: "notification",
- title: "Notification",
- icon: ,
- },
- {
- key: "help",
- title: "Help",
- icon: ,
- },
- {
- key: "about",
- title: "About",
- icon: ,
- }
-]
-
-class GeneralSettings extends React.Component {
- render() {
- return } title="Settings" childrens={Settings} menuArray={menuList} />
- }
-}
-
-export default GeneralSettings
diff --git a/packages/app/src/pages/streams/index.js b/packages/app/src/pages/streams/index.js
deleted file mode 100644
index 34bfc768..00000000
--- a/packages/app/src/pages/streams/index.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import React from 'react'
-import * as antd from 'antd'
-import * as Icons from 'components/Icons'
-import { connect } from 'umi'
-import { verbosity } from '@nodecorejs/utils'
-
-import { goLive } from 'core/models/helpers'
-
-@connect(({ app }) => ({ app }))
-export default class Streams extends React.Component {
-
- state = {
- list: []
- }
-
- componentDidMount() {
- try {
-
- } catch (error) {
- verbosity.log(error)
- }
- }
-
- availableList = () => {
- if (!Array.isArray(this.state.list)) {
- return false
- }
- if (this.state.list.length == 0) {
- return false
- }
- return true
- }
-
- render() {
-
- if (!this.availableList()) {
- return
-
- {goLive()}} type="primary" >
- Start Streaming
-
-
-
- }
-
- return (
-
-
-
- )
- }
-}
\ No newline at end of file
diff --git a/packages/server/.gitignore b/packages/server/.gitignore
new file mode 100644
index 00000000..37d7e734
--- /dev/null
+++ b/packages/server/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+.env
diff --git a/packages/server/package.json b/packages/server/package.json
index d08f670b..c8e0af94 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -7,14 +7,15 @@
},
"license": "MIT",
"dependencies": {
- "corenode": "^0.28.26",
- "linebridge": "^0.8.4",
- "mongoose": "^6.0.13",
"axios": "^0.24.0",
"bcrypt": "^5.0.1",
"connect-mongo": "^4.6.0",
+ "corenode": "^0.28.26",
+ "dicebar_lib": "^1.0.1",
"jsonwebtoken": "^8.5.1",
+ "linebridge": "^0.8.4",
"moment": "^2.29.1",
+ "mongoose": "^6.0.13",
"passport": "^0.5.0",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
diff --git a/packages/server/src/controllers/RolesController/index.js b/packages/server/src/controllers/RolesController/index.js
new file mode 100644
index 00000000..6674c103
--- /dev/null
+++ b/packages/server/src/controllers/RolesController/index.js
@@ -0,0 +1,36 @@
+import { Role, User } from '../../models'
+import { selectValues } from "../../lib"
+
+export const RolesController = {
+ get: selectValues(["user_id", "username"], async (req, res) => {
+ const { user_id, username } = req.selectedValues
+
+ if (typeof user_id !== "undefined" || typeof username !== "undefined") {
+ const user = await User.findOne(req.selectedValues)
+ if (!user) {
+ return res.status(404).json({ error: "No user founded" })
+ }
+ return res.json(user.roles)
+ }
+
+ const roles = await Role.find({})
+
+ return res.json(roles)
+ }),
+ set: (req, res, next) => {
+ const { name, description } = req.body
+ Role.findOne({ name }).then((data) => {
+ if (data) {
+ return res.status(409).json("This role is already created")
+ }
+ let document = new Role({
+ name,
+ description
+ })
+ document.save()
+ return res.json(true)
+ })
+ }
+}
+
+export default RolesController
\ No newline at end of file
diff --git a/packages/server/src/controllers/SessionController/index.js b/packages/server/src/controllers/SessionController/index.js
new file mode 100644
index 00000000..297e600b
--- /dev/null
+++ b/packages/server/src/controllers/SessionController/index.js
@@ -0,0 +1,104 @@
+import { Session } from '../../models'
+import jwt from 'jsonwebtoken'
+import { Token } from '../../lib'
+
+export const SessionController = {
+ regenerate: async (req, res) => {
+ jwt.verify(req.jwtToken, req.jwtStrategy.secretOrKey, async (err, decoded) => {
+ if (err && !decoded?.allowRegenerate) {
+ return res.status(403).send("This token is invalid and is not allowed to be regenerated")
+ }
+
+ const sessionToken = await Session.findOneAndDelete({ token: req.jwtToken, user_id: decoded.user_id })
+
+ if (sessionToken) {
+ delete decoded["iat"]
+ delete decoded["exp"]
+ delete decoded["date"]
+
+ const token = await Token.signNew({
+ ...decoded,
+ }, req.jwtStrategy)
+
+ return res.json({ token })
+ }
+ })
+ },
+ deleteAll: async (req, res) => {
+ const { user_id } = req.body
+
+ if (typeof user_id === "undefined") {
+ return res.status(400).send("No user_id provided")
+ }
+
+ const allSessions = await Session.deleteMany({ user_id })
+ if (allSessions) {
+ return res.send("done")
+ }
+
+ return res.status(404).send("not found")
+ },
+ delete: async (req, res) => {
+ const { token, user_id } = req.body
+
+ if (typeof user_id === "undefined") {
+ return res.status(400).send("No user_id provided")
+ }
+ if (typeof token === "undefined") {
+ return res.status(400).send("No token provided")
+ }
+
+ const session = await Session.findOneAndDelete({ user_id, token })
+ if (session) {
+ return res.send("done")
+ }
+
+ return res.status(404).send("not found")
+ },
+ validate: async (req, res) => {
+ const token = req.body.session
+ let result = {
+ expired: false,
+ valid: true
+ }
+
+ await jwt.verify(token, req.jwtStrategy.secretOrKey, async (err, decoded) => {
+ if (err) {
+ result.valid = false
+ result.error = err.message
+
+ if (err.message === "jwt expired") {
+ result.expired = true
+ }
+ return
+ }
+
+ result = { ...result, ...decoded }
+
+ const sessions = await Session.find({ user_id: result.user_id })
+ const sessionsTokens = sessions.map((session) => {
+ if (session.user_id === result.user_id) {
+ return session.token
+ }
+ })
+
+ if (!sessionsTokens.includes(token)) {
+ result.valid = false
+ result.error = "Session token not found"
+ } else {
+ result.valid = true
+ }
+ })
+
+ return res.json(result)
+ },
+ get: async (req, res) => {
+ // get current session _id
+ const { _id } = req.user
+ const sessions = await Session.find({ user_id: _id }, { token: 0 })
+
+ return res.json(sessions)
+ },
+}
+
+export default SessionController
\ No newline at end of file
diff --git a/packages/server/src/controllers/UserController/index.js b/packages/server/src/controllers/UserController/index.js
new file mode 100644
index 00000000..841e76ba
--- /dev/null
+++ b/packages/server/src/controllers/UserController/index.js
@@ -0,0 +1,209 @@
+import passport from 'passport'
+import bcrypt from 'bcrypt'
+
+import { User } from '../../models'
+import SessionController from '../SessionController'
+import { Token, selectValues } from '../../lib'
+import AvatarController from 'dicebar_lib'
+
+export const UserController = {
+ isAuth: (req, res) => {
+ return res.json(`You look nice today π`)
+ },
+ getSelf: (req, res) => {
+ return res.json(req.user)
+ },
+ get: selectValues(["_id", "username"], async (req, res) => {
+ const user = await User.find(req.selectedValues, { username: 1, fullName: 1, _id: 1, roles: 1, avatar: 1 })
+
+ if (!user) {
+ return res.status(404).json({ error: "User not exists" })
+ }
+
+ return res.json(user)
+ }),
+ getOne: selectValues(["_id", "username"], async (req, res) => {
+ const user = await User.findOne(req.selectedValues)
+
+ if (!user) {
+ return res.status(404).json({ error: "User not exists" })
+ }
+
+ return res.json(user)
+ }),
+ register: (req, res, next) => {
+ User.findOne({ username: req.body.username })
+ .then((data) => {
+ if (data) {
+ return res.status(409).json("Username is already exists")
+ }
+
+ const avatar = AvatarController.generate({ seed: req.body.username, type: "initials" })
+ const hash = bcrypt.hashSync(req.body.password, parseInt(process.env.BCRYPT_ROUNDS))
+
+ let document = new User({
+ username: req.body.username,
+ fullName: req.body.fullName,
+ avatar: avatar.uri,
+ email: req.body.email,
+ roles: ["registered"],
+ password: hash
+ })
+
+ return document.save()
+ })
+ .then(data => {
+ return res.send(data)
+ })
+ .catch(err => {
+ return next(err)
+ })
+ },
+ denyRole: async (req, res) => {
+ // check if issuer user is admin
+ if (!req.isAdmin()) {
+ return res.status(403).send("You do not have administrator permission")
+ }
+
+ let { user_id, username, roles } = req.body
+ const userQuery = {
+ username: username,
+ user_id: user_id,
+ }
+
+ // parse requested roles
+ if (typeof roles === "string") {
+ roles = roles.split(",").map(role => role.trim())
+ } else {
+ return res.send("No effect")
+ }
+
+ // get current user roles
+ const user = await User.findOne({ ...userQuery })
+ if (typeof user === "undefined") {
+ return res.status(404).send(`[${username}] User not found`)
+ }
+
+ // query all roles mutation
+ let queryRoles = []
+ if (Array.isArray(roles)) {
+ queryRoles = roles
+ } else if (typeof roles === 'string') {
+ queryRoles.push(roles)
+ }
+
+ // mutate all roles
+ if (queryRoles.length > 0 && Array.isArray(user.roles)) {
+ queryRoles.forEach(role => {
+ user.roles = user.roles.filter(_role => _role !== role)
+ })
+ }
+
+ // update user roles
+ await user.save()
+ return res.send("done")
+ },
+ grantRole: async (req, res) => {
+ // check if issuer user is admin
+ if (!req.isAdmin()) {
+ return res.status(403).send("You do not have administrator permission")
+ }
+
+ let { user_id, username, roles } = req.body
+ const userQuery = {
+ username: username,
+ user_id: user_id,
+ }
+
+ // parse requested roles
+ if (typeof roles === "string") {
+ roles = roles.split(",").map(role => role.trim())
+ } else {
+ return res.send("No effect")
+ }
+
+ // get current user roles
+ const user = await User.findOne({ ...userQuery })
+ if (typeof user === "undefined") {
+ return res.status(404).send(`[${username}] User not found`)
+ }
+
+ // query all roles mutation
+ let queryRoles = []
+ if (Array.isArray(roles)) {
+ queryRoles = roles
+ } else if (typeof roles === 'string') {
+ queryRoles.push(roles)
+ }
+
+
+ // mutate all roles
+ if (queryRoles.length > 0 && Array.isArray(user.roles)) {
+ queryRoles.forEach(role => {
+ if (!user.roles.includes(role)) {
+ user.roles.push(role)
+ }
+ })
+ }
+
+ // update user roles
+ await user.save()
+ return res.send("done")
+ },
+ updatePassword: async (req, res) => {
+ //TODO
+ },
+ updateSelf: async (req, res) => {
+ Object.keys(req.body).forEach(key => {
+ req.user[key] = req.body[key]
+ })
+
+ User.findOneAndUpdate({ _id: req.user._id }, req.user)
+ .then(() => {
+ return res.send(req.user)
+ })
+ .catch((err) => {
+ return res.send(500).send(err)
+ })
+ },
+ update: async (req, res) => {
+ // TODO
+ },
+ login: async (req, res) => {
+ passport.authenticate("local", { session: false }, async (error, user, options) => {
+ if (error) {
+ return res.status(500).json(`Error validating user > ${error.message}`)
+ }
+
+ if (!user) {
+ return res.status(401).json("Invalid credentials")
+ }
+
+ const payload = {
+ user_id: user._id,
+ username: user.username,
+ email: user.email
+ }
+
+ if (req.body.allowRegenerate) {
+ payload.allowRegenerate = true
+ }
+
+ // generate token
+ const token = Token.signNew(payload, options)
+
+ // send result
+ res.json({ token: token })
+ })(req, res)
+ },
+ logout: async (req, res, next) => {
+ req.body = {
+ user_id: req.decodedToken.user_id,
+ token: req.jwtToken
+ }
+
+ return SessionController.delete(req, res, next)
+ },
+}
+
+export default UserController
diff --git a/packages/server/src/controllers/index.js b/packages/server/src/controllers/index.js
new file mode 100644
index 00000000..da7be5c9
--- /dev/null
+++ b/packages/server/src/controllers/index.js
@@ -0,0 +1,3 @@
+export { default as RolesController } from './RolesController'
+export { default as SessionController } from './SessionController'
+export { default as UserController } from './UserController'
\ No newline at end of file
diff --git a/packages/server/src/endpoints/index.js b/packages/server/src/endpoints/index.js
new file mode 100644
index 00000000..88d3b973
--- /dev/null
+++ b/packages/server/src/endpoints/index.js
@@ -0,0 +1,109 @@
+module.exports = [
+ {
+ route: "/regenerate",
+ method: "POST",
+ middleware: ["ensureAuthenticated", "useJwtStrategy"],
+ fn: "SessionController.regenerate"
+ },
+ {
+ route: "/role",
+ method: 'PUT',
+ middleware: ["ensureAuthenticated", "roles"],
+ fn: "UserController.grantRole"
+ },
+ {
+ route: "/role",
+ method: 'DELETE',
+ middleware: ["ensureAuthenticated", "roles"],
+ fn: "UserController.denyRole"
+ },
+ {
+ route: "/roles",
+ method: "GET",
+ fn: "RolesController.get",
+ },
+ {
+ route: "/session",
+ method: 'DELETE',
+ middleware: "ensureAuthenticated",
+ fn: "SessionController.delete",
+ },
+ {
+ route: "/sessions",
+ method: 'DELETE',
+ middleware: "ensureAuthenticated",
+ fn: "SessionController.deleteAll",
+ },
+ {
+ route: "/validate_session",
+ method: "POST",
+ middleware: "useJwtStrategy",
+ fn: "SessionController.validate",
+ },
+ {
+ route: "/sessions",
+ method: "GET",
+ middleware: "ensureAuthenticated",
+ fn: "SessionController.get",
+ },
+ {
+ route: "/has_permissions",
+ method: "POST",
+ middleware: [
+ "ensureAuthenticated",
+ "hasPermissions"
+ ]
+ },
+ {
+ route: "/self",
+ method: "GET",
+ middleware: "ensureAuthenticated",
+ fn: "UserController.getSelf",
+ },
+ {
+ route: "/users",
+ method: "GET",
+ middleware: "ensureAuthenticated",
+ fn: "UserController.get",
+ },
+ {
+ route: "/user",
+ method: "GET",
+ middleware: "ensureAuthenticated",
+ fn: "UserController.getOne",
+ },
+ {
+ route: "/self_user",
+ method: "PUT",
+ middleware: "ensureAuthenticated",
+ fn: "UserController.updateSelf",
+ },
+ {
+ route: "/user",
+ method: "PUT",
+ middleware: ["ensureAuthenticated", "privileged"],
+ fn: "UserController.update",
+ },
+ {
+ route: "/login",
+ method: "POST",
+ fn: "UserController.login",
+ },
+ {
+ route: "/logout",
+ method: "POST",
+ middleware: ["ensureAuthenticated"],
+ fn: "UserController.logout",
+ },
+ {
+ route: "/register",
+ method: "POST",
+ fn: "UserController.register",
+ },
+ {
+ route: "/is_auth",
+ method: "POST",
+ middleware: "ensureAuthenticated",
+ fn: "UserController.isAuth",
+ }
+]
\ No newline at end of file
diff --git a/packages/server/src/index.js b/packages/server/src/index.js
index 592de37c..e3f3535d 100644
--- a/packages/server/src/index.js
+++ b/packages/server/src/index.js
@@ -92,7 +92,7 @@ class Server {
return new Promise((resolve, reject) => {
try {
console.log("π Trying to connect to DB...")
- mongoose.connect(this.getDBConnectionString(), { useNewUrlParser: true, useFindAndModify: false })
+ mongoose.connect(this.getDBConnectionString(), { useNewUrlParser: true, useUnifiedTopology: true })
.then((res) => { return resolve(true) })
.catch((err) => { return reject(err) })
} catch (err) {
diff --git a/packages/server/src/lib/index.js b/packages/server/src/lib/index.js
new file mode 100644
index 00000000..d1e792da
--- /dev/null
+++ b/packages/server/src/lib/index.js
@@ -0,0 +1,3 @@
+export * as Token from './token'
+export { default as Schematized } from './schematized'
+export { default as selectValues } from './selectValues'
\ No newline at end of file
diff --git a/packages/server/src/lib/schematized/index.js b/packages/server/src/lib/schematized/index.js
new file mode 100644
index 00000000..27d70ceb
--- /dev/null
+++ b/packages/server/src/lib/schematized/index.js
@@ -0,0 +1,20 @@
+export default (schema, fn) => {
+ return async (req, res, next) => {
+ const missingKeys = []
+ const requiredKeys = Array.isArray(schema) ? schema : []
+
+ for await (let key of requiredKeys) {
+ if (typeof req.body[key] === "undefined") {
+ missingKeys.push(key)
+ }
+ }
+
+ if (missingKeys.length > 0) {
+ return res.status(400).json({ error: `Missing required keys > ${missingKeys}` })
+ } else {
+ if (typeof fn === "function") {
+ return await fn(req, res, next)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/server/src/lib/selectValues/index.js b/packages/server/src/lib/selectValues/index.js
new file mode 100644
index 00000000..641cf3dc
--- /dev/null
+++ b/packages/server/src/lib/selectValues/index.js
@@ -0,0 +1,27 @@
+export default (query = [], fn) => {
+ return async (req, res, next) => {
+ if (typeof fn === "function") {
+ const obj = {}
+
+ if (!req.body) {
+ req.body = {}
+ }
+ if (!req.query) {
+ req.query = {}
+ }
+
+ if (Array.isArray(query)) {
+ query.forEach(key => {
+ const value = req.body[key] ?? req.query[key]
+ if (typeof value !== "undefined") {
+ obj[key] = value
+ }
+ })
+ }
+
+ req.selectedValues = obj
+
+ return await fn(req, res, next)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/server/src/lib/token/index.js b/packages/server/src/lib/token/index.js
new file mode 100644
index 00000000..ee48fea7
--- /dev/null
+++ b/packages/server/src/lib/token/index.js
@@ -0,0 +1,29 @@
+import jwt from 'jsonwebtoken'
+import { nanoid } from 'nanoid'
+import { Session } from '../../models'
+
+export function signNew(payload, options) {
+ const data = {
+ uuid: nanoid(),
+ allowRegenerate: false,
+ ...payload
+ }
+
+ const token = jwt.sign(data, options.secretOrKey, {
+ expiresIn: options.expiresIn ?? "1h",
+ algorithm: options.algorithm ?? "HS256"
+ })
+
+ let newSession = new Session({
+ uuid: data.uuid,
+ user_id: data.user_id,
+ allowRegenerate: data.allowRegenerate,
+ token: token,
+ date: new Date().getTime(),
+ location: options.sessionLocationSign
+ })
+
+ newSession.save()
+
+ return token
+}
\ No newline at end of file
diff --git a/packages/server/src/middlewares/ensureAuthenticated/index.js b/packages/server/src/middlewares/ensureAuthenticated/index.js
new file mode 100644
index 00000000..7fe37a09
--- /dev/null
+++ b/packages/server/src/middlewares/ensureAuthenticated/index.js
@@ -0,0 +1,40 @@
+import passport from 'passport'
+import { Session } from '../../models'
+
+export default (req, res, next) => {
+ function unauthorized() {
+ console.log("Returning failed session")
+ return res.status(401).send({ error: 'Invalid session', })
+ }
+
+ const authHeader = req.headers?.authorization?.split(' ')
+
+ if (authHeader && authHeader[0] === 'Bearer') {
+ const token = authHeader[1]
+
+ passport.authenticate('jwt', { session: false }, async (err, user, decodedToken) => {
+ if (err) {
+ return res.status(500).send({ error: err.message })
+ }
+
+ if (!user) {
+ return res.status(404).send({ error: "No user data found" })
+ }
+
+ const sessions = await Session.find({ user_id: decodedToken.user_id })
+ const sessionsTokens = sessions.map(session => session.token)
+
+ if (!sessionsTokens.includes(token)) {
+ return unauthorized()
+ }
+
+ req.user = user
+ req.jwtToken = token
+ req.decodedToken = decodedToken
+
+ return next()
+ })(req, res, next)
+ } else {
+ return unauthorized()
+ }
+}
diff --git a/packages/server/src/middlewares/errorHandler/index.js b/packages/server/src/middlewares/errorHandler/index.js
new file mode 100644
index 00000000..d8e53df2
--- /dev/null
+++ b/packages/server/src/middlewares/errorHandler/index.js
@@ -0,0 +1,5 @@
+export const errorHandler = (error, req, res, next) => {
+ res.json({ error: error.message })
+}
+
+export default errorHandler
\ No newline at end of file
diff --git a/packages/server/src/middlewares/hasPermissions/index.js b/packages/server/src/middlewares/hasPermissions/index.js
new file mode 100644
index 00000000..2cc254a8
--- /dev/null
+++ b/packages/server/src/middlewares/hasPermissions/index.js
@@ -0,0 +1,30 @@
+export const hasPermissions = (req, res, next) => {
+ if (typeof (req.userData) == "undefined") {
+ return res.status(403).json(`User data is not available, please ensure if you are authenticated`)
+ }
+
+ const { _id, username, roles } = req.userData
+ const { permissions } = req.body
+
+ req.userPermissions = roles
+
+ let check = []
+
+ if (Array.isArray(permissions)) {
+ check = permissions
+ } else {
+ check.push(permissions)
+ }
+
+ if (check.length > 0) {
+ check.forEach((role) => {
+ if (!roles.includes(role)) {
+ return res.status(403).json(`${username} not have permissions ${permissions}`)
+ }
+ })
+ }
+
+ next()
+}
+
+export default hasPermissions
diff --git a/packages/server/src/middlewares/index.js b/packages/server/src/middlewares/index.js
new file mode 100644
index 00000000..e38789c7
--- /dev/null
+++ b/packages/server/src/middlewares/index.js
@@ -0,0 +1,4 @@
+export { default as ensureAuthenticated } from './ensureAuthenticated'
+export { default as errorHandler } from './errorHandler'
+export { default as hasPermissions } from './hasPermissions'
+export { default as roles } from './roles'
\ No newline at end of file
diff --git a/packages/server/src/middlewares/privileged/index.js b/packages/server/src/middlewares/privileged/index.js
new file mode 100644
index 00000000..1dfc5e62
--- /dev/null
+++ b/packages/server/src/middlewares/privileged/index.js
@@ -0,0 +1,7 @@
+export default (req, res, next) => {
+ if (!req.user.roles.includes("admin")) {
+ return res.status(401).send({ error: "To make this request it is necessary to have administrator permissions" })
+ }
+
+ next()
+}
\ No newline at end of file
diff --git a/packages/server/src/middlewares/roles/index.js b/packages/server/src/middlewares/roles/index.js
new file mode 100644
index 00000000..9e1d58f4
--- /dev/null
+++ b/packages/server/src/middlewares/roles/index.js
@@ -0,0 +1,11 @@
+export default (req, res, next) => {
+ req.isAdmin = () => {
+ if (req.user.roles.includes("admin")) {
+ return true
+ }
+
+ return false
+ }
+
+ next()
+}
\ No newline at end of file
diff --git a/packages/server/src/models/index.js b/packages/server/src/models/index.js
new file mode 100644
index 00000000..832d8162
--- /dev/null
+++ b/packages/server/src/models/index.js
@@ -0,0 +1,28 @@
+import mongoose from 'mongoose'
+import { Schema } from 'mongoose'
+
+function getSchemas() {
+ const obj = Object()
+
+ const _schemas = require("../schemas")
+ Object.keys(_schemas).forEach(key => {
+ obj[key] = Schema(_schemas[key])
+ })
+
+ return obj
+}
+
+const schemas = getSchemas()
+
+export const FabricObject = mongoose.model('FabricObject', schemas.FabricObject, "fabricObjects")
+export const Workload = mongoose.model('Workload', schemas.Workload, "workload")
+export const Workshifts = mongoose.model('Workshifts', schemas.Workshift, "workshifts")
+export const Role = mongoose.model('Role', schemas.Role, 'roles')
+export const Vault = mongoose.model('Vault', schemas.VaultItem, "vault")
+export const GeoRegion = mongoose.model('GeoRegion', schemas.Region, "regions")
+
+export const Contract = mongoose.model('Contract', schemas.Contract, "contracts")
+export const User = mongoose.model('User', schemas.User, "accounts")
+export const Session = mongoose.model('Session', schemas.Session, "sessions")
+export const Workshift = mongoose.model("Workshift", schemas.Workshift, "workshifts")
+export const Report = mongoose.model("Report", schemas.Report, "reports")
\ No newline at end of file
diff --git a/packages/server/src/schemas/index.js b/packages/server/src/schemas/index.js
new file mode 100644
index 00000000..86928a02
--- /dev/null
+++ b/packages/server/src/schemas/index.js
@@ -0,0 +1,3 @@
+export { default as User } from './user'
+export { default as Role } from './role'
+export { default as Session } from './session'
\ No newline at end of file
diff --git a/packages/server/src/schemas/role/index.js b/packages/server/src/schemas/role/index.js
new file mode 100644
index 00000000..6992f3d0
--- /dev/null
+++ b/packages/server/src/schemas/role/index.js
@@ -0,0 +1,5 @@
+export default {
+ name: String,
+ description: String,
+ apply: Object
+}
\ No newline at end of file
diff --git a/packages/server/src/schemas/session/index.js b/packages/server/src/schemas/session/index.js
new file mode 100644
index 00000000..654e1d77
--- /dev/null
+++ b/packages/server/src/schemas/session/index.js
@@ -0,0 +1,9 @@
+export default {
+ allowRegenerate: { type: Boolean, default: false },
+ uuid: { type: String, required: true },
+ token: { type: String, required: true },
+ user_id: { type: String, required: true },
+ date: { type: Number, default: 0 },
+ location: { type: String, default: "Unknown" },
+ geo: { type: String, default: "Unknown" },
+}
\ No newline at end of file
diff --git a/packages/server/src/schemas/user/index.js b/packages/server/src/schemas/user/index.js
new file mode 100644
index 00000000..2ef04b1c
--- /dev/null
+++ b/packages/server/src/schemas/user/index.js
@@ -0,0 +1,10 @@
+export default {
+ username: { type: String, required: true },
+ password: { type: String, required: true, select: false },
+ fullName: String,
+ avatar: { type: String },
+ email: String,
+ roles: [],
+ legal_id: Object,
+ phone: Number,
+}
\ No newline at end of file