5htp-core 0.5.9-9 → 0.6.0

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.
@@ -0,0 +1,244 @@
1
+ @import (reference) '@/client/assets/vars.less';
2
+
3
+ /*----------------------------------
4
+ - BASIC THEMES
5
+ ----------------------------------*/
6
+
7
+ @whiteTheme: {
8
+ alpha: false;
9
+ background: #FFF;
10
+ foreground: #555;
11
+ accent1: @c1;
12
+ accent2: @c2;
13
+ }
14
+
15
+ @greyTheme: {
16
+ alpha: false;
17
+ background: #F3F3F3;
18
+ foreground: #4D4D4D;
19
+ accent1: @c1;
20
+ accent2: @c2;
21
+ }
22
+
23
+ @silverTheme: {
24
+ alpha: false;
25
+ background: @cBgPage - #080808;
26
+ foreground: #777;
27
+ accent1: @c1;
28
+ accent2: @c2;
29
+ }
30
+
31
+ @darkTheme: {
32
+ alpha: false;
33
+ background: #000;
34
+ foreground: #DDD;
35
+ accent1: #83B2FF;
36
+ accent2: @c2;
37
+ }
38
+
39
+ @darkerTheme: {
40
+ alpha: false;
41
+ background: #181818;
42
+ foreground: #bbb;
43
+ accent1: #fff;
44
+ accent2: @c2;
45
+ }
46
+
47
+ @darkestTheme: {
48
+ alpha: false;
49
+ background: #242424;
50
+ foreground: #ddd;
51
+ accent1: #fff;
52
+ accent2: @c2;
53
+ }
54
+
55
+ /*----------------------------------
56
+ - COLOR THEMES
57
+ ----------------------------------*/
58
+
59
+ @primaryTheme: {
60
+ alpha: false;
61
+ background: #111;
62
+ foreground: #fff;
63
+ accent1: #FFF;
64
+ accent2: #FFF;
65
+ }
66
+
67
+ /*@accentTheme: {
68
+ alpha: false;
69
+ background: @c1;
70
+ foreground: #fff;
71
+ accent1: #fff;
72
+ accent2: #fff;;
73
+ }*/
74
+
75
+
76
+
77
+ @accentTheme: {
78
+ alpha: false;
79
+ background: #EAE8F9;
80
+ foreground: @c1 - #444;
81
+ accent1: #fff;
82
+ accent2: #fff;;
83
+ }
84
+
85
+ .bg.primary {
86
+
87
+ .apply-theme(@primaryTheme);
88
+ }
89
+
90
+ .bg.accent,
91
+ .btn.bg.col.active {
92
+ .apply-theme(@accentTheme);
93
+ }
94
+
95
+ .bg.grey {
96
+ .apply-theme(@greyTheme, @whiteTheme);
97
+ }
98
+
99
+ .card,
100
+ .bg.white {
101
+ .apply-theme(@whiteTheme, @greyTheme);
102
+ }
103
+
104
+ .bg.silver {
105
+ .apply-theme(@silverTheme, @whiteTheme);
106
+
107
+ box-shadow: inset 0 0 20px fade(#000, 5%)
108
+ }
109
+
110
+ .bg.dark {
111
+ .apply-theme(@darkTheme, @darkerTheme);
112
+ }
113
+
114
+ .bg.darker {
115
+ .apply-theme(@darkerTheme, @darkestTheme);
116
+ }
117
+
118
+ /*----------------------------------
119
+ - GRADIENT & IMG THEMES
120
+ ----------------------------------*/
121
+
122
+ .bg.img {
123
+
124
+ .apply-theme({
125
+ alpha: true;
126
+ background: #000;
127
+ foreground: fade(#fff, 60%);
128
+ accent1: #fff;
129
+ accent2: green;
130
+ }, @whiteTheme);
131
+ }
132
+
133
+ .card:not(.bg), .card.bg.white {
134
+ border: 1px solid var(--cLine);
135
+ }
136
+
137
+ .bg.color1 { .color-theme(#0066ff ); }
138
+ .bg.color2 { .color-theme(#00e6da ); }
139
+ .bg.color3 { .color-theme(#00e417 ); }
140
+ .bg.color4 { .color-theme(#ffb700 ); }
141
+ .bg.color5 { .color-theme(#eb4a00 ); }
142
+ .bg.color6 { .color-theme(#e500ac ); }
143
+ .bg.color7 { .color-theme(#6200ff ); }
144
+
145
+ .bg.light1 { .color-theme(#cce0fe ); }
146
+ .bg.light2 { .color-theme(#c7f0ee ); }
147
+ .bg.light3 { .color-theme(#cef5d2 ); }
148
+ .bg.light4 { .color-theme(#feeab6 ); }
149
+ .bg.light5 { .color-theme(#ffdccc ); }
150
+ .bg.light6 { .color-theme(#fcccf0 ); }
151
+ .bg.light7 { .color-theme(#e2d5f7 ); }
152
+
153
+ .bg.success {
154
+ box-shadow: none;
155
+ border: none;
156
+
157
+ .apply-theme({
158
+ alpha: false;
159
+ background: #D7FEEE;
160
+ foreground: darken( #D7FEEE, 80% );
161
+ accent1: #111;
162
+ accent2: #111;
163
+ });
164
+
165
+ &.light {
166
+ .apply-theme({
167
+ alpha: false;
168
+ background: lighten( @cSuccess, 40% );
169
+ foreground: darken( @cSuccess, 50% );
170
+ accent1: #111;
171
+ accent2: #111;
172
+ });
173
+ }
174
+ }
175
+
176
+ .bg.error {
177
+ box-shadow: none;
178
+ border: none;
179
+
180
+ .apply-theme({
181
+ alpha: false;
182
+ background: lighten( @cError, 20% );
183
+ foreground: darken( @cError, 50% );
184
+ accent1: #111;
185
+ accent2: #111;
186
+ });
187
+ }
188
+
189
+ .bg.warn {
190
+ box-shadow: none;
191
+ border: none;
192
+
193
+ .apply-theme({
194
+ alpha: false;
195
+ background: lighten( @cWarn, 20% );
196
+ foreground: darken( @cWarn, 50% );
197
+ accent1: #111;
198
+ accent2: #111;
199
+ });
200
+
201
+ &.light {
202
+ .apply-theme({
203
+ alpha: false;
204
+ background: lighten( @cWarn, 20% );
205
+ foreground: darken( @cWarn, 50% );
206
+ accent1: #111;
207
+ accent2: #111;
208
+ });
209
+ }
210
+ }
211
+
212
+ .bg.info {
213
+ box-shadow: none;
214
+ border: none;
215
+
216
+ .apply-theme({
217
+ alpha: false;
218
+ background: lighten( @cInfo, 50% );
219
+ foreground: darken( @cInfo, 50% );
220
+ accent1: #111;
221
+ accent2: #111;
222
+ });
223
+
224
+ &.light {
225
+ .apply-theme({
226
+ alpha: false;
227
+ background: lighten( @cInfo, 35% );
228
+ foreground: darken( @cInfo, 50% );
229
+ accent1: #111;
230
+ accent2: #111;
231
+ });
232
+ }
233
+ }
234
+
235
+ .fg {
236
+
237
+ &.primary { .build-theme-fg(@c1, #fff); }
238
+
239
+ &.desc { color: var(--cTxtDesc); }
240
+
241
+ &.success { .build-theme-fg( @cSuccess ); }
242
+ &.error { .build-theme-fg( @cError ); }
243
+ &.warn { .build-theme-fg( @cWarn ); }
244
+ }
@@ -160,19 +160,20 @@
160
160
  color: var(--cTxtAccent);
161
161
  }
162
162
 
163
+ &.secondary {
164
+ border: 1px solid var(--cLine);
165
+
166
+ &:hover {
167
+ border-color: var(--cLine2);
168
+ }
169
+ }
170
+
163
171
  /*----------------------------------
164
172
  - STATE
165
173
  ----------------------------------*/
166
174
 
167
175
  cursor: pointer;
168
176
 
169
- &:not(.primary).active {
170
-
171
- &.col {
172
- box-shadow: 0 0 0 0.2em @c2;
173
- }
174
- }
175
-
176
177
  &[disabled] {
177
178
  cursor: default;
178
179
  opacity: 0.5;
@@ -279,7 +280,7 @@
279
280
  &.menu > .btn,
280
281
  &.menu > li > .btn {
281
282
 
282
- > .label {
283
+ &:not(.col) > .label {
283
284
  // All the list items label must be aligned
284
285
  justify-content: flex-start;
285
286
  // Since they're all horizontally aligned,
@@ -289,6 +290,20 @@
289
290
  }
290
291
  }
291
292
 
293
+ .submenu.card {
294
+ animation: aff-submenu 0.1s ease;
295
+ @keyframes aff-submenu {
296
+ 0% {
297
+ opacity: 0;
298
+ transform: scale(0.8);
299
+ }
300
+ 100% {
301
+ opacity: 1;
302
+ transform: scale(1);
303
+ }
304
+ }
305
+ }
306
+
292
307
  ul.col,
293
308
  ul.row {
294
309
 
@@ -323,7 +338,9 @@ ul.col {
323
338
  &.menu > li > .btn {
324
339
 
325
340
  // Align items ver sla gauche
326
- justify-content: flex-start;
341
+ &:not(.col) {
342
+ justify-content: flex-start;
343
+ }
327
344
 
328
345
  // Align all icons by giveing them the same width
329
346
  > i {
@@ -2,8 +2,8 @@
2
2
  @import '@mantine/core/styles.css';
3
3
 
4
4
  // Utils
5
+ @import './colors.less';
5
6
  @import (reference) "./theme.less";
6
-
7
7
  // Apply the theme class
8
8
  .bg {
9
9
  background-color: var(--cBg);
@@ -118,4 +118,14 @@
118
118
  --cTxtAccent: @cAccent;
119
119
 
120
120
  color: var(--cTxtBase);
121
+ }
122
+
123
+ .color-theme( @color ) {
124
+ .apply-theme({
125
+ alpha: false;
126
+ background: @color;
127
+ foreground: #111;
128
+ accent1: #111;
129
+ accent2: #111;
130
+ });
121
131
  }
@@ -134,10 +134,9 @@
134
134
  display: flex;
135
135
  flex-direction: column;
136
136
 
137
- &.scrollable {
138
- > * {
139
- min-height: fit-content;
140
- }
137
+ // Not too precice as it culd override important heights (ex: btn)
138
+ :where(& > *) {
139
+ min-height: fit-content;
141
140
  }
142
141
 
143
142
  // Put this one at first because high possibilities we need to override
@@ -187,6 +187,12 @@ export class NotFound extends CoreError {
187
187
  public static msgDefaut = "The resource you asked for was not found.";
188
188
  }
189
189
 
190
+ export class Gone extends CoreError {
191
+ public http = 410;
192
+ public title = "Gone";
193
+ public static msgDefaut = "The resource you asked for has been removed.";
194
+ }
195
+
190
196
  export class RateLimit extends CoreError {
191
197
  public http = 429;
192
198
  public title = "You're going too fast";
@@ -95,10 +95,11 @@ export type TRouteOptions = {
95
95
  accept?: string,
96
96
  raw?: boolean, // true to return raw data
97
97
  auth?: TUserRole | boolean,
98
- canonicalParams?: string[],
98
+ redirectLogged?: string, // Redirect to this route if auth: false and user is logged
99
99
 
100
100
  // Rendering
101
101
  static?: boolean,
102
+ canonicalParams?: string[], // For SEO + unique ID for static cache
102
103
  layout?: false | string, // The nale of the layout
103
104
 
104
105
  // To cleanup
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.5.9-9",
4
+ "version": "0.6.0",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -81,7 +81,7 @@ export abstract class Application<
81
81
  // Status
82
82
  public debug: boolean = false;
83
83
  public launched: boolean = false;
84
-
84
+
85
85
  protected abstract registered: {
86
86
  [serviceId: string]: {
87
87
  name: string,
@@ -183,12 +183,14 @@ export abstract class Application<
183
183
  protected async ready() {
184
184
 
185
185
  // Print services
186
- const processService = (propKey: string, service: AnyService, level: number = 0) => {
186
+ const processService = async (propKey: string, service: AnyService, level: number = 0) => {
187
187
 
188
188
  if (service.status !== 'starting')
189
189
  return;
190
190
 
191
- service.ready();
191
+ // Services start shouldn't block app boot
192
+ // use await ServiceName.started to make services depends on each other
193
+ this.starting = service.ready();
192
194
  service.status = 'running';
193
195
  console.log('-' + '-'.repeat(level * 1), propKey + ': ' + service.constructor.name);
194
196
 
@@ -232,6 +234,7 @@ export abstract class Application<
232
234
  if (!isService)
233
235
  continue;
234
236
 
237
+ // Services start shouldn't block app boot
235
238
  processService(propKey, propValue, level + 1);
236
239
  }
237
240
  }
@@ -244,7 +247,7 @@ export abstract class Application<
244
247
  // TODO: move to router
245
248
  // Application.on('service.ready')
246
249
 
247
-
250
+ // Services start shouldn't block app boot
248
251
  processService(serviceId, service);
249
252
  }
250
253
  }
@@ -89,6 +89,8 @@ export default abstract class AuthService<
89
89
 
90
90
  // Get auth session
91
91
  const session = this.getAuthSession(tokenType, token);
92
+ if (session === null)
93
+ return null;
92
94
 
93
95
  // Return email only
94
96
  if (!withData) {
@@ -132,7 +134,7 @@ export default abstract class AuthService<
132
134
  return { tokenType, token };
133
135
  }
134
136
 
135
- private getAuthSession( tokenType: string | undefined, token: string ): TJwtSession {
137
+ private getAuthSession( tokenType: string | undefined, token: string ): TJwtSession | null {
136
138
 
137
139
  let session: TJwtSession;
138
140
 
@@ -153,10 +155,12 @@ export default abstract class AuthService<
153
155
  });
154
156
  } catch (error) {
155
157
  console.warn(LogPrefix, "Failed to decode jwt token:", token);
156
- throw new Forbidden(`The JWT token provided in the Authorization header is invalid`);
158
+ return null;
159
+ //throw new Forbidden(`The JWT token provided in the Authorization header is invalid`);
157
160
  }
158
161
  } else
159
- throw new InputError(`The authorization scheme provided in the Authorization header is unsupported.`);
162
+ return null;
163
+ //throw new InputError(`The authorization scheme provided in the Authorization header is unsupported.`);
160
164
 
161
165
  return session;
162
166
  }
@@ -183,9 +187,9 @@ export default abstract class AuthService<
183
187
  request.res.clearCookie('authorization');
184
188
  }
185
189
 
186
- public check( request: TRequest, entity: string, role: TUserRole, motivation?: string): TUser;
187
- public check( request: TRequest, entity: string, role: false, motivation?: string): null;
188
- public check( request: TRequest, entity: string, role: TUserRole | false = 'USER', motivation?: string): TUser | null {
190
+ public check( request: TRequest, entity: string, role: TUserRole): TUser;
191
+ public check( request: TRequest, entity: string, role: false): null;
192
+ public check( request: TRequest, entity: string, role: TUserRole | false = 'USER'): TUser | null {
189
193
 
190
194
  const user = request.user;
191
195
 
@@ -195,7 +199,7 @@ export default abstract class AuthService<
195
199
 
196
200
  throw new Error(`request.user has not been decoded.`);
197
201
 
198
- // No auth needed
202
+ // Shoudln't be logged
199
203
  } else if (role === false) {
200
204
 
201
205
  return user;
@@ -6,8 +6,8 @@
6
6
 
7
7
  // Core
8
8
  import {
9
- default as Router, Request as ServerRequest, TAnyRoute,
10
- RouterService
9
+ default as Router, Request as ServerRequest, Response as ServerResponse, TAnyRoute,
10
+ RouterService, TRouterServiceArgs
11
11
  } from '@server/services/router';
12
12
 
13
13
  // Specific
@@ -39,7 +39,7 @@ export default class AuthenticationRouterService<
39
39
 
40
40
  public users: UsersService;
41
41
 
42
- public constructor(...args) {
42
+ public constructor(...args: TRouterServiceArgs) {
43
43
  super(...args);
44
44
 
45
45
  this.users = this.config.users;
@@ -57,11 +57,21 @@ export default class AuthenticationRouterService<
57
57
  })
58
58
 
59
59
  // Check route permissions
60
- this.parent.on('resolved', async (route: TAnyRoute, request: TRequest) => {
60
+ this.parent.on('resolved', async (
61
+ route: TAnyRoute,
62
+ request: TRequest,
63
+ response: ServerResponse<Router>
64
+ ) => {
61
65
 
62
- if (route.options.auth !== undefined)
63
- // TODO: How to pas the router type to router config ? Circular rfeerence ?
64
- this.users.check(request, route.options.auth);
66
+ if (route.options.auth !== undefined) {
67
+
68
+ // Basic auth check
69
+ this.users.check(request, 'User', route.options.auth);
70
+
71
+ // Redirect to logged page
72
+ if (route.options.auth === false && request.user && route.options.redirectLogged)
73
+ response.redirect(route.options.redirectLogged);
74
+ }
65
75
  })
66
76
  }
67
77
 
@@ -487,9 +487,6 @@ export default class ServerRouter
487
487
  let route = this.controllers[request.path];
488
488
  if (route !== undefined) {
489
489
 
490
- // Run on resolution hooks. Ex: authentication check
491
- await this.runHook('resolved', route);
492
-
493
490
  // Create response
494
491
  await this.resolvedRoute(route, response, logId, timeStart);
495
492
  if (response.wasProvided)
@@ -542,7 +539,7 @@ export default class ServerRouter
542
539
  ) {
543
540
 
544
541
  // Run on resolution hooks. Ex: authentication check
545
- await this.runHook('resolved', route);
542
+ await this.runHook('resolved', route, response.request, response);
546
543
 
547
544
  // Create response
548
545
  await response.runController(route);
@@ -11,6 +11,8 @@ import type { default as Router } from '.';
11
11
  import type ServerRequest from './request';
12
12
  import type RequestService from './request/service';
13
13
 
14
+ export type TRouterServiceArgs = TServiceArgs<RouterService>;
15
+
14
16
  /*----------------------------------
15
17
  - SERVICE
16
18
  ----------------------------------*/
@@ -18,7 +20,7 @@ export default abstract class RouterService<
18
20
  TConfig extends {} = {}
19
21
  > extends Service<TConfig, {}, Application> {
20
22
 
21
- public constructor( ...args: TServiceArgs<RouterService>) {
23
+ public constructor( ...args: TRouterServiceArgs) {
22
24
  super(...args);
23
25
  }
24
26
 
package/types/icons.d.ts CHANGED
@@ -1 +1 @@
1
- export type TIcones = "times"|"solid/spinner-third"|"long-arrow-right"|"sack-dollar"|"bell"|"bullseye"|"project-diagram"|"user-friends"|"eye"|"lock"|"comments"|"phone"|"chalkboard-teacher"|"rocket"|"planet-ringed"|"brands/linkedin"|"user-circle"|"crosshairs"|"plus-circle"|"comments-alt"|"arrow-right"|"user-shield"|"shield-alt"|"chart-line"|"money-bill-wave"|"star"|"link"|"file-alt"|"long-arrow-left"|"chart-bar"|"at"|"calendar-alt"|"paper-plane"|"search"|"lightbulb"|"magnet"|"angle-up"|"angle-down"|"solid/crown"|"brands/discord"|"pen"|"plus"|"file"|"key"|"user"|"user-plus"|"mouse-pointer"|"thumbs-up"|"dollar-sign"|"envelope"|"times-circle"|"globe"|"industry"|"users"|"briefcase"|"check-circle"|"magic"|"map-marker-alt"|"solid/magic"|"angle-right"|"save"|"binoculars"|"info-circle"|"exclamation-circle"|"check"|"arrow-left"|"trash"|"meh-rolling-eyes"|"bars"|"solid/star"|"solid/star-half-alt"|"regular/star"|"chevron-left"|"cog"|"power-off"|"play"|"minus-circle"|"plane-departure"|"brands/whatsapp"|"wind"|"external-link"|"question-circle"|"clock"|"arrow-to-bottom"|"ellipsis-h"|"usd-circle"|"stream"|"home-alt"|"trophy"|"map-marker"|"language"|"calendar"|"fire"|"redo"|"building"|"unlink"|"bold"|"italic"|"underline"|"strikethrough"|"subscript"|"superscript"|"code"|"font"|"empty-set"|"horizontal-rule"|"page-break"|"image"|"table"|"poll"|"columns"|"sticky-note"|"caret-right"|"list-ul"|"check-square"|"h1"|"h2"|"h3"|"h4"|"list-ol"|"paragraph"|"quote-left"|"align-left"|"align-center"|"align-right"|"align-justify"|"indent"|"outdent"
1
+ export type TIcones = "solid/spinner-third"|"times-circle"|"traffic-light-stop"|"search"|"paper-plane"|"bookmark"|"download"|"long-arrow-right"|"times"|"angle-down"|"th-large"|"table"|"mouse-pointer"|"thumbs-up"|"dollar-sign"|"info-circle"|"check-circle"|"exclamation-circle"|"heart"|"bell"|"chart-bar"|"power-off"|"bars"|"home"|"planet-ringed"|"rocket"|"plus-circle"|"binoculars"|"users"|"angle-up"|"infinity"|"arrow-up"|"plane"|"seedling"|"palette"|"car"|"university"|"briefcase"|"hard-hat"|"graduation-cap"|"bolt"|"cogs"|"film"|"leaf"|"tshirt"|"utensils"|"globe"|"map-marked-alt"|"dumbbell"|"stethoscope"|"concierge-bell"|"book"|"shield-alt"|"gavel"|"industry"|"square-root-alt"|"newspaper"|"pills"|"medal"|"capsules"|"balance-scale"|"praying-hands"|"shopping-cart"|"flask"|"futbol"|"microchip"|"satellite-dish"|"shipping-fast"|"passport"|"tools"|"database"|"brain"|"code"|"money-bill"|"check"|"at"|"brands/google"|"brands/github"|"brands/linkedin"|"image"|"key"|"solid/heart"|"regular/heart"|"eye"|"trash"|"arrow-left"|"arrow-right"|"meh-rolling-eyes"|"unlink"|"pen"|"bold"|"italic"|"underline"|"strikethrough"|"subscript"|"superscript"|"link"|"file"|"plus"|"font"|"empty-set"|"horizontal-rule"|"page-break"|"poll"|"columns"|"sticky-note"|"caret-right"|"align-left"|"align-center"|"align-right"|"align-justify"|"indent"|"outdent"|"list-ul"|"check-square"|"h1"|"h2"|"h3"|"h4"|"list-ol"|"paragraph"|"quote-left"