5htp-core 0.2.4 → 0.2.5-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "5htp-core",
3
3
  "description": "Convenient TypeScript framework designed for Performance and Productivity.",
4
- "version": "0.2.4",
4
+ "version": "0.2.5-1",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -92,5 +92,8 @@
92
92
  "@types/ws": "^7.4.7",
93
93
  "@types/yargs-parser": "^21.0.0",
94
94
  "babel-plugin-glob-import": "^0.0.6-2"
95
- }
95
+ },
96
+ "peerDependencies": {
97
+ "5htp": "0.2.3"
98
+ }
96
99
  }
@@ -54,32 +54,6 @@ p, .p {
54
54
  text-overflow: ellipsis;
55
55
  }
56
56
 
57
- /*----------------------------------
58
- - COULEURS
59
- ----------------------------------*/
60
- .txtSuccess {
61
- color: var(--cSuccess) !important;
62
- }
63
- .txtError {
64
- color: var(--cError) !important;
65
- }
66
- .txt-gold {
67
- /*color: #cef57a;
68
- text-shadow: 0 0 0.80rem fade(#cef57a, 80%);*/
69
- color: #c80;
70
- }
71
- .txtWarn {
72
- color: var(--orange) !important;
73
- }
74
-
75
- .txt-blanc { color: #fff !important; }
76
-
77
- .txtPrimary, .txtC1 {
78
- color: var(--cAccent);
79
- }
80
-
81
- .txtC2 { color: var(--cAccent2); }
82
-
83
57
  /*----------------------------------
84
58
  - TAILLE
85
59
  ----------------------------------*/
@@ -24,7 +24,7 @@ export default ({ amount, unit, sign, decimals, ...props }: {
24
24
  sign?: '+' | '-'
25
25
  } & JSX.HTMLAttributes<HTMLDivElement>) => {
26
26
 
27
- const className = 'number row sp-05 ' + (props.class ? props.class + ' ' : '');//sign === undefined ? 'txtPrimary' : (sign === '+' ? 'txtSuccess' : 'txtError');
27
+ const className = 'number row sp-05 ' + (props.class ? props.class + ' ' : '');//sign === undefined ? 'txtPrimary' : (sign === '+' ? 'fg success' : 'fg error');
28
28
  if (unit === 'credits')
29
29
  return <strong {...props} class={className}>{sign} {Format.credits(amount, decimals)} Credits</strong>;
30
30
  else if (unit === 'dollars')
@@ -0,0 +1,6 @@
1
+ #internalLayout {
2
+ min-height: 80vh;
3
+ align-items: center;
4
+ justify-content: center;
5
+ display: flex;
6
+ }
@@ -0,0 +1,43 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import React from 'react';
7
+ import type { ComponentChild } from 'preact';
8
+
9
+ // Core
10
+ import Router from '@client/services/router/components/router';
11
+ import { ClientContext } from '@/client/context';
12
+
13
+ // Core components
14
+
15
+ // Resources
16
+ import "./index.less";
17
+
18
+ /*----------------------------------
19
+ - TYPES
20
+ ----------------------------------*/
21
+
22
+
23
+ /*----------------------------------
24
+ - COMPOSANT
25
+ ----------------------------------*/
26
+ export default function App ({ context, menu }: {
27
+ context: ClientContext,
28
+ menu: ComponentChild
29
+ }) {
30
+
31
+ const { router, page, toast } = context;
32
+
33
+ return (
34
+ <div id="internaLlayout">
35
+
36
+ <div class="center row al-fill">
37
+
38
+ <Router service={router} />
39
+
40
+ </div>
41
+ </div>
42
+ )
43
+ }
@@ -7,7 +7,7 @@ import React from 'react';
7
7
 
8
8
  // Core
9
9
  import { router } from '@app';
10
- import Button from '@client/components/button';
10
+ import { Button } from '@client/components';
11
11
 
12
12
  // App
13
13
  import useHeader from '@client/pages/useHeader';
@@ -15,10 +15,7 @@ import useHeader from '@client/pages/useHeader';
15
15
  /*----------------------------------
16
16
  - CONTROLEUR
17
17
  ----------------------------------*/
18
- router.error(400, {}, ({ message, modal }) => {
19
-
20
- if (!message)
21
- message = "The request you made is incorrect.";
18
+ router.error( 400, ({ message, modal }) => {
22
19
 
23
20
  useHeader({
24
21
  title: 'Bad request',
@@ -26,13 +23,15 @@ router.error(400, {}, ({ message, modal }) => {
26
23
  });
27
24
 
28
25
  return (
29
- <div class="col pd-2">
30
- <i src="times-circle" class="txtPrimary xxl" />
26
+ <div class="card w-3-4 col al-center pd-2">
27
+
28
+ <i src="times-circle" class="fg error xxl" />
31
29
 
32
- <h1>Bad request</h1>
30
+ <h1>Bad Request</h1>
33
31
 
34
32
  <p>{message}</p>
33
+
34
+ <Button type="primary" link="/">Go Home</Button>
35
35
  </div>
36
36
  )
37
-
38
37
  });
@@ -7,9 +7,10 @@ import React from 'react';
7
7
 
8
8
  // Core
9
9
  import { router } from '@app';
10
- import Button from '@client/components/button';
10
+ import { Button } from '@client/components';
11
11
 
12
12
  // App
13
+ import useHeader from '@client/pages/useHeader';
13
14
 
14
15
  /*----------------------------------
15
16
  - RESSOURCES
@@ -18,10 +19,15 @@ import Button from '@client/components/button';
18
19
  /*----------------------------------
19
20
  - CONTROLEUR
20
21
  ----------------------------------*/
21
- router.error(401, { }, ({ api, toast, modal, request, page }) => {
22
+ router.error( 401, ({ message, request, page }) => {
22
23
 
23
24
  request.response?.redirect('/');
24
25
 
26
+ useHeader({
27
+ title: 'Authentication Required',
28
+ subtitle: message
29
+ });
30
+
25
31
  React.useEffect(() => {
26
32
 
27
33
  page?.go('/');
@@ -29,5 +35,16 @@ router.error(401, { }, ({ api, toast, modal, request, page }) => {
29
35
 
30
36
  }, []);
31
37
 
32
- return '';
38
+ return (
39
+ <div class="card w-3-4 col al-center pd-2">
40
+
41
+ <i src="times-circle" class="fg error xxl" />
42
+
43
+ <h1>Authentication Required</h1>
44
+
45
+ <p>{message}</p>
46
+
47
+ <Button type="primary" link="/">Go Home</Button>
48
+ </div>
49
+ )
33
50
  });
@@ -7,7 +7,7 @@ import React from 'react';
7
7
 
8
8
  // Core
9
9
  import { router } from '@app';
10
- import Button from '@client/components/button';
10
+ import { Button } from '@client/components';
11
11
 
12
12
  // App
13
13
  import useHeader from '@client/pages/useHeader';
@@ -15,10 +15,7 @@ import useHeader from '@client/pages/useHeader';
15
15
  /*----------------------------------
16
16
  - CONTROLEUR
17
17
  ----------------------------------*/
18
- router.error( 403, {}, ({ message, modal }) => {
19
-
20
- if (!message)
21
- message = "You do not have sufficient permissions to access this content.";
18
+ router.error( 403, ({ message, modal }) => {
22
19
 
23
20
  useHeader({
24
21
  title: 'Access Denied.',
@@ -27,11 +24,14 @@ router.error( 403, {}, ({ message, modal }) => {
27
24
 
28
25
  return (
29
26
  <div class="col pd-2">
30
- <i src="times-circle" class="txtPrimary xxl" />
27
+
28
+ <i src="times-circle" class="fg error xxl" />
31
29
 
32
30
  <h1>Access Denied.</h1>
33
31
 
34
32
  <p>{message}</p>
33
+
34
+ <Button type="primary" link="/">Go Home</Button>
35
35
  </div>
36
36
  )
37
37
  });
@@ -7,7 +7,7 @@ import React from 'react';
7
7
 
8
8
  // Core
9
9
  import { router } from '@app';
10
- import Button from '@client/components/button';
10
+ import { Button } from '@client/components';
11
11
 
12
12
  // App
13
13
  import useHeader from '@client/pages/useHeader';
@@ -15,10 +15,7 @@ import useHeader from '@client/pages/useHeader';
15
15
  /*----------------------------------
16
16
  - CONTROLEUR
17
17
  ----------------------------------*/
18
- router.error( 404, {}, ({ message, modal }) => {
19
-
20
- if (!message)
21
- message = "The content you asked for was not found.";
18
+ router.error( 404, ({ message, modal }) => {
22
19
 
23
20
  useHeader({
24
21
  title: 'Page Not Found',
@@ -26,12 +23,15 @@ router.error( 404, {}, ({ message, modal }) => {
26
23
  });
27
24
 
28
25
  return (
29
- <div class="col pd-2">
30
- <i src="times-circle" class="txtPrimary xxl" />
26
+ <div class="card w-3-4 col al-center pd-2">
27
+
28
+ <i src="times-circle" class="fg error xxl" />
31
29
 
32
30
  <h1>Page Not Found</h1>
33
31
 
34
32
  <p>{message}</p>
33
+
34
+ <Button type="primary" link="/">Go Home</Button>
35
35
  </div>
36
36
  )
37
37
  });
@@ -7,7 +7,7 @@ import React from 'react';
7
7
 
8
8
  // Core
9
9
  import { router } from '@app';
10
- import Button from '@client/components/button';
10
+ import { Button } from '@client/components';
11
11
 
12
12
  // App
13
13
  import useHeader from '@client/pages/useHeader';
@@ -15,10 +15,7 @@ import useHeader from '@client/pages/useHeader';
15
15
  /*----------------------------------
16
16
  - CONTROLEUR
17
17
  ----------------------------------*/
18
- router.error( 500, {}, ({ message }) => {
19
-
20
- if (!message)
21
- message = "A technical error occurred.";
18
+ router.error( 500, ({ message }) => {
22
19
 
23
20
  useHeader({
24
21
  title: 'Technical Error',
@@ -27,11 +24,14 @@ router.error( 500, {}, ({ message }) => {
27
24
 
28
25
  return (
29
26
  <div class="col pd-2">
30
- <i src="times-circle" class="txtPrimary xxl" />
27
+
28
+ <i src="times-circle" class="fg error xxl" />
31
29
 
32
30
  <h1>Technical Error</h1>
33
31
 
34
32
  <p>{message}</p>
33
+
34
+ <Button type="primary" link="/">Go Home</Button>
35
35
  </div>
36
36
  )
37
37
  });
@@ -17,6 +17,7 @@ import type { TBasicSSrData } from '@server/services/router/response';
17
17
  import BaseRouter, {
18
18
  defaultOptions, TRoute, TErrorRoute, TClientOrServerContext, TRouteModule
19
19
  } from '@common/router'
20
+ import { getLayout } from '@common/router/layouts';
20
21
  import { getRegisterPageArgs, buildRegex } from '@common/router/register';
21
22
  import { TFetcherList } from '@common/router/request/api';
22
23
  import type { TFrontRenderer, TDataProvider } from '@common/router/response/page';
@@ -243,9 +244,12 @@ export default class ClientRouter<
243
244
 
244
245
  public error(code: number, options: TRoute["options"], renderer: TFrontRenderer<{}, { message: string }>) {
245
246
 
247
+ // Automatic layout form the nearest _layout folder
248
+ const layout = getLayout('Error ' + code, options);
249
+
246
250
  const route: TErrorRoute = {
247
251
  code,
248
- controller: (context: TClientOrServerContext) => new ClientPage(null, renderer, context),
252
+ controller: (context: TClientOrServerContext) => new ClientPage(null, renderer, context, layout),
249
253
  options
250
254
  };
251
255
 
@@ -112,7 +112,7 @@ export class AuthRequired extends CoreError {
112
112
  export class Forbidden extends CoreError {
113
113
  public http = 403;
114
114
  public title = "Access Denied";
115
- public static msgDefaut = "You're not allowed to access to this resource.";
115
+ public static msgDefaut = "You do not have sufficient permissions to access this content.";
116
116
  }
117
117
 
118
118
  export class NotFound extends CoreError {
@@ -8,6 +8,7 @@ import type { ComponentChild } from 'preact';
8
8
  import type { ClientContext } from '@/client/context';
9
9
  import type { TRouteOptions } from '.';
10
10
  // App
11
+ import internalLayout from '@client/pages/_layout';
11
12
  import layouts from '@/client/pages/**/_layout/index.tsx';
12
13
 
13
14
  /*----------------------------------
@@ -43,8 +44,6 @@ export const getLayout = (routePath: string, routeOptions?: TRouteOptions): Layo
43
44
  const chunkId = routeOptions["id"];
44
45
  if (chunkId === undefined)
45
46
  throw new Error(`ID has not injected for the following page route: ${routePath}`);
46
-
47
- let layout: Layout | undefined;
48
47
 
49
48
  // Layout via name
50
49
  if (routeOptions.layout !== undefined) {
@@ -53,30 +52,30 @@ export const getLayout = (routePath: string, routeOptions?: TRouteOptions): Layo
53
52
  if (LayoutComponent === undefined)
54
53
  throw new Error(`No layout found with ID: ${routeOptions.layout}. registered layouts: ${Object.keys(layouts)}`);
55
54
 
56
- layout = {
55
+ return {
57
56
  path: routeOptions.layout,
58
57
  Component: layouts[routeOptions.layout]
59
58
  }
60
-
61
- } else {
59
+ }
62
60
 
63
- // Automatic layout via the nearest _layout folder
64
- for (const layoutPath in layouts)
65
- if (
66
- // The layout is nammed index when it's at the root (@/client/pages/_layout)
67
- layoutPath === 'index'
68
- // Exact match
69
- || chunkId === layoutPath
70
- // Parent
71
- || chunkId.startsWith( layoutPath + '_' )
72
- )
73
- layout = {
74
- path: layoutPath,
75
- Component: layouts[layoutPath]
76
- };
77
- }
78
-
79
- //console.log(`[router][layouts] Get layout for "${routePath}". Found:`, layout);
61
+ // Automatic layout via the nearest _layout folder
62
+ for (const layoutPath in layouts)
63
+ if (
64
+ // The layout is nammed index when it's at the root (@/client/pages/_layout)
65
+ layoutPath === 'index'
66
+ // Exact match
67
+ || chunkId === layoutPath
68
+ // Parent
69
+ || chunkId.startsWith( layoutPath + '_' )
70
+ )
71
+ return {
72
+ path: layoutPath,
73
+ Component: layouts[layoutPath]
74
+ };
80
75
 
81
- return layout;
76
+ // Internal layout
77
+ return {
78
+ path: '/',
79
+ Component: internalLayout
80
+ }
82
81
  }
@@ -1,3 +1,4 @@
1
- export { default as Schema, TSchemaFields } from './schema';
1
+ export { default as Schema } from './schema';
2
+ export type { TSchemaFields } from './schema';
2
3
  export { default as Validators } from './validators';
3
4
  export { default as Validator } from './validator';
@@ -65,11 +65,13 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
65
65
 
66
66
  public path = {
67
67
  root: process.cwd(),
68
+ public: process.cwd() + '/public',
69
+
70
+ // TODO: move to disk
68
71
  typings: process.cwd() + '/var/typings',
69
72
  cache: process.cwd() + '/var/cache',
70
73
  data: process.cwd() + '/var/data',
71
74
  log: process.cwd() + '/var/log',
72
- public: process.cwd() + '/public',
73
75
  }
74
76
 
75
77
  public pkg = fs.readJSONSync(this.path.root + '/package.json');
@@ -136,10 +138,15 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
136
138
 
137
139
  public async start() {
138
140
 
139
- console.info(`[boot] Waiting for all services to be ready ...`);
141
+ console.log(`5HTP Core`, process.env.npm_package_version);
142
+
143
+ console.info(`[boot] Connect disk`);
144
+ await this.initDisk();
145
+
146
+ console.info(`[boot] Start services`);
140
147
  await this.startServices()
141
148
 
142
- console.info(`[boot] Launching application ...`);
149
+ console.info(`[boot] App ready`);
143
150
  await this.runHook('ready');
144
151
 
145
152
  console.info(`[boot] Run application-specific boot instructions ...`);
@@ -150,6 +157,15 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
150
157
 
151
158
  }
152
159
 
160
+ private async initDisk() {
161
+ console.info(`[boot] Ensure runtime dirs ...`);
162
+ await Promise.all([
163
+ fs.ensureDir( this.path.cache ),
164
+ fs.ensureDir( this.path.log ),
165
+ fs.ensureDir( this.path.data ),
166
+ ]);
167
+ }
168
+
153
169
  public registerService( service: AnyService ) {
154
170
  console.log(`[app] Register service`, service.constructor?.name);
155
171
  this.servicesList.push(service);
@@ -92,6 +92,8 @@ export default abstract class Service<
92
92
  callbacks.map(
93
93
  cb => cb(...args).catch(e => {
94
94
  console.error(`[hook] Error while executing hook ${name}:`, e);
95
+ if (name !== 'error')
96
+ this.runHook('error', e);
95
97
  })
96
98
  )
97
99
  ).then(() => {
File without changes