@andrewcaires/api 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2015-2022 Andrew Caires (https://github.com/andrewcaires)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ [![npm](https://img.shields.io/npm/v/@andrewcaires/api?color=blue&logo=npm)](https://www.npmjs.com/package/@andrewcaires/api)
2
+ [![downloads](https://img.shields.io/npm/dt/@andrewcaires/api?color=blue)](https://www.npmjs.com/package/@andrewcaires/api)
3
+ [![size](https://img.shields.io/bundlephobia/min/@andrewcaires/api?color=blue)](https://www.npmjs.com/package/@andrewcaires/api)
4
+ [![license](https://img.shields.io/github/license/andrewcaires/npm?color=blue)](https://github.com/andrewcaires/npm/blob/main/LICENSE)
5
+
6
+ # API
7
+
8
+ Simple api for small applications using express, jsonwebtoken, sequelize and websocket
9
+
10
+ ## Installation
11
+
12
+ The module is now available on npm! `npm i @andrewcaires/api`
13
+
14
+ ## Example usage
15
+
16
+ ```js
17
+ import { initialize } from '@andrewcaires/api';
18
+
19
+ initialize();
20
+ ```
21
+
22
+ ### Links
23
+
24
+ * [Docs](https://github.com/andrewcaires/npm/blob/main/api/README.md)
25
+ * [GitHub](https://github.com/andrewcaires/npm/tree/main/api)
26
+ * [npm](https://www.npmjs.com/package/@andrewcaires/api)
27
+
28
+ ## License
29
+
30
+ * [MIT](https://github.com/andrewcaires/npm/blob/main/LICENSE)
@@ -0,0 +1,182 @@
1
+ /// <reference types="node" />
2
+ import * as https from 'https';
3
+ import * as http from 'http';
4
+ import * as _sequelize from 'sequelize';
5
+ import { Model, Sequelize, ModelStatic, FindOptions, Attributes } from 'sequelize';
6
+ import * as express_serve_static_core from 'express-serve-static-core';
7
+ import WebSocket from 'ws';
8
+ import { Request, Response, NextFunction } from 'express';
9
+ import { WhereOptions } from 'sequelize/types';
10
+ import { ObjectSchema } from 'joi';
11
+
12
+ declare const initialize: () => void;
13
+ declare const server: http.Server | https.Server;
14
+
15
+ declare const API_AUTH_SLEEP: number;
16
+ declare const API_DB_DATABASE: string;
17
+ declare const API_DB_HOST: string;
18
+ declare const API_DB_PASSWORD: string;
19
+ declare const API_DB_PORT: number;
20
+ declare const API_DB_TYPE: string;
21
+ declare const API_DB_USERNAME: string;
22
+ declare const API_DB_LOG: boolean;
23
+ declare const API_HTTP_CRT: string;
24
+ declare const API_HTTP_KEY: string;
25
+ declare const API_HTTP_PORT: number;
26
+ declare const API_HTTP_PUBLIC: string;
27
+ declare const API_HTTP_CROSS: boolean;
28
+ declare const API_HTTP_HYBRID: boolean;
29
+ declare const API_TOKEN_CRT: string;
30
+ declare const API_TOKEN_KEY: string;
31
+ declare const API_TOKEN_HEADER: string;
32
+ declare const API_TOKEN_LIFETIME: number;
33
+ declare const API_UPLOAD_COUNT: number;
34
+ declare const API_UPLOAD_FIELD: string;
35
+ declare const API_UPLOAD_SIZE: number;
36
+ declare const API_UPLOAD_TEMP: string;
37
+ declare const API_WEBSOCKET_START: boolean;
38
+
39
+ declare class Route extends Model {
40
+ id: number;
41
+ name: string;
42
+ description: string;
43
+ state: boolean;
44
+ groups_routes: GroupRoute[];
45
+ }
46
+
47
+ declare class GroupRoute extends Model {
48
+ groupId: number;
49
+ routeId: number;
50
+ group: Group;
51
+ route: Route;
52
+ }
53
+
54
+ declare class Group extends Model {
55
+ id: number;
56
+ name: string;
57
+ description: string;
58
+ state: boolean;
59
+ groups_routes: GroupRoute[];
60
+ users_groups: UserGroup[];
61
+ }
62
+
63
+ declare class UserGroup extends Model {
64
+ userId: number;
65
+ groupId: number;
66
+ user: User;
67
+ group: Group;
68
+ }
69
+
70
+ declare class User extends Model {
71
+ id: number;
72
+ name: string;
73
+ email: string;
74
+ username: string;
75
+ password: string;
76
+ description: string;
77
+ state: boolean;
78
+ users_groups: UserGroup[];
79
+ }
80
+
81
+ declare class Auth extends Model {
82
+ id: number;
83
+ login: number;
84
+ logout: number;
85
+ secret: string;
86
+ timestamp: number;
87
+ useragent: string;
88
+ user: User;
89
+ }
90
+
91
+ declare class Logs extends Model {
92
+ type: string;
93
+ source: string;
94
+ message: string;
95
+ }
96
+
97
+ declare const app: express_serve_static_core.Express;
98
+
99
+ declare const router: express_serve_static_core.Router;
100
+
101
+ declare const uuid: {
102
+ type: _sequelize.AbstractDataTypeConstructor;
103
+ defaultValue: _sequelize.AbstractDataTypeConstructor;
104
+ allowNull: boolean;
105
+ primaryKey: boolean;
106
+ };
107
+ declare const sequelize: Sequelize;
108
+
109
+ declare const crt: {
110
+ http: Buffer | undefined;
111
+ token: Buffer | undefined;
112
+ };
113
+ declare const key: {
114
+ http: Buffer | undefined;
115
+ token: Buffer | undefined;
116
+ };
117
+
118
+ declare const startSocket: (server: http.Server | https.Server) => WebSocket.Server;
119
+
120
+ declare type FnRecord<M> = (records: M) => any;
121
+ declare type FnRecords<M> = (records: Array<M>) => Array<any>;
122
+ declare class Controller<M extends Model<M>> {
123
+ private log;
124
+ private model;
125
+ constructor(log: string, model: ModelStatic<M>);
126
+ add(): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
127
+ all(options?: FindOptions<Attributes<M>>, fn?: FnRecords<M>): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
128
+ del(): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
129
+ get(options?: FindOptions<Attributes<M>>, fn?: FnRecord<M>): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
130
+ set(): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
131
+ }
132
+
133
+ declare class Log {
134
+ static write(message: string, source?: string): void;
135
+ static error(message: string, source?: string): void;
136
+ static info(message: string, source?: string): void;
137
+ static success(message: string, source?: string): void;
138
+ static warning(message: string, source?: string): void;
139
+ private static writeConsole;
140
+ private static writeRaw;
141
+ }
142
+
143
+ declare class Permission {
144
+ static all(user: User, where?: WhereOptions): Promise<void | Route[]>;
145
+ static get(user: User): Promise<string[]>;
146
+ static is(user: User, name: string): Promise<boolean | void>;
147
+ }
148
+
149
+ declare class Responses {
150
+ static success(res: Response, message: string): Response<any, Record<string, any>>;
151
+ static data(res: Response, obj: object): Response<any, Record<string, any>>;
152
+ static error(res: Response, message: string): Response<any, Record<string, any>>;
153
+ static list(res: Response, arr: Array<any>): Response<any, Record<string, any>>;
154
+ static notfound(res: Response, message: string): Response<any, Record<string, any>>;
155
+ static validation(res: Response, message: string, details: any): Response<any, Record<string, any>>;
156
+ static unauthorized(res: Response, message: string): Response<any, Record<string, any>>;
157
+ }
158
+
159
+ declare global {
160
+ namespace Express {
161
+ interface Request {
162
+ auth: Auth | undefined;
163
+ user: User | undefined;
164
+ }
165
+ }
166
+ }
167
+ declare const auth: (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
168
+
169
+ declare const permission: (name: string) => (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
170
+
171
+ interface UploadOptions {
172
+ count?: number;
173
+ field?: string;
174
+ path?: string;
175
+ size?: number;
176
+ types?: string[];
177
+ }
178
+ declare const upload: (options?: UploadOptions | undefined) => (req: Request, res: Response, next: NextFunction) => void;
179
+
180
+ declare const validation: (schema: ObjectSchema) => (req: Request, res: Response, next: NextFunction) => void | Response<any, Record<string, any>>;
181
+
182
+ export { API_AUTH_SLEEP, API_DB_DATABASE, API_DB_HOST, API_DB_LOG, API_DB_PASSWORD, API_DB_PORT, API_DB_TYPE, API_DB_USERNAME, API_HTTP_CROSS, API_HTTP_CRT, API_HTTP_HYBRID, API_HTTP_KEY, API_HTTP_PORT, API_HTTP_PUBLIC, API_TOKEN_CRT, API_TOKEN_HEADER, API_TOKEN_KEY, API_TOKEN_LIFETIME, API_UPLOAD_COUNT, API_UPLOAD_FIELD, API_UPLOAD_SIZE, API_UPLOAD_TEMP, API_WEBSOCKET_START, Auth, Controller, Group, GroupRoute, Log, Logs, Permission, Responses, Route, User, UserGroup, app, auth, crt, initialize, key, permission, router, sequelize, server, startSocket, upload, uuid, validation };
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * @andrewcaires/api v1.0.0
3
+ * (c) 2022 Andrew Caires
4
+ * @license: MIT
5
+ */
6
+ import{toBool as e,forEachSeries as t,dateFormat as r,sleep as s,uniqueID as o,allowedObject as n,uuidv4 as i}from"@andrewcaires/utils.js";import{config as a}from"dotenv";import{Sequelize as d,UUID as u,UUIDV4 as c,DataTypes as l,Model as m}from"sequelize";import p from"cors";import g,{Router as v}from"express";import{existsSync as f,readFileSync as h}from"fs";import{SHA256 as y}from"crypto-js";import w from"jsonwebtoken";import I from"joi";import*as b from"http";import*as T from"https";import{Server as A}from"net";import S from"ws";import _ from"mime-types";import E from"multer";import{join as P}from"path";a();const O=parseInt(process.env.API_AUTH_SLEEP||"1000"),N=process.env.API_DB_DATABASE||"",R=process.env.API_DB_HOST||"127.0.0.1",q=process.env.API_DB_PASSWORD||"",j=parseInt(process.env.API_DB_PORT||"3306"),k=process.env.API_DB_TYPE||"",x=process.env.API_DB_USERNAME||"root",D=e(process.env.API_DB_LOG||"false"),B=process.env.API_HTTP_CRT||"",z=process.env.API_HTTP_KEY||"",G=parseInt(process.env.API_HTTP_PORT||"3000"),U=process.env.API_HTTP_PUBLIC||"",L=e(process.env.API_HTTP_CROSS||"false"),C=e(process.env.API_HTTP_HYBRID||"false"),H=process.env.API_TOKEN_CRT||"",K=process.env.API_TOKEN_KEY||"",M=process.env.API_TOKEN_HEADER||"authorization",F=parseInt(process.env.API_TOKEN_LIFETIME||"15"),W=parseInt(process.env.API_UPLOAD_COUNT||"1"),X=process.env.API_UPLOAD_FIELD||"upload",Y=parseInt(process.env.API_UPLOAD_SIZE||"2"),$=process.env.API_UPLOAD_TEMP||"./temp",J=e(process.env.API_WEBSOCKET_START||"false");function V(e,t,r,s){return new(r||(r=Promise))((function(o,n){function i(e){try{d(s.next(e))}catch(e){n(e)}}function a(e){try{d(s.throw(e))}catch(e){n(e)}}function d(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(i,a)}d((s=s.apply(e,t||[])).next())}))}const Z={type:u,defaultValue:c,allowNull:!1,primaryKey:!0},Q=(()=>{switch(k){case"mariadb":return new d(N,x,q,{host:R,port:j,dialect:"mariadb",logging:D,define:{timestamps:!1}});case"mysql":return new d(N,x,q,{host:R,port:j,dialect:"mysql",logging:D,define:{timestamps:!1}});case"postgres":return new d(N,x,q,{host:R,port:j,dialect:"postgres",logging:D,define:{timestamps:!1}})}throw new Error("No database selected")})();class ee extends m{}ee.init({login:l.BIGINT,logout:l.BIGINT,secret:l.STRING,timestamp:l.BIGINT,useragent:l.STRING},{sequelize:Q,modelName:"auth"});class te extends m{}te.init({name:l.STRING,description:l.TEXT,state:l.BOOLEAN},{sequelize:Q,modelName:"group"});class re extends m{}re.init({type:l.STRING,source:l.STRING,message:l.TEXT},{sequelize:Q,modelName:"logs",timestamps:!0,createdAt:"created",updatedAt:!1});class se extends m{}se.init({name:l.STRING,description:l.TEXT,state:l.BOOLEAN},{sequelize:Q,modelName:"route"});class oe extends m{}oe.init({},{sequelize:Q,modelName:"groups_routes"}),oe.removeAttribute("id");class ne extends m{}ne.init({name:l.STRING,email:l.STRING,username:l.STRING,password:l.STRING,description:l.TEXT,state:l.BOOLEAN},{sequelize:Q,modelName:"user"});class ie extends m{}ie.init({},{sequelize:Q,modelName:"users_groups"}),ie.removeAttribute("id"),ne.hasMany(ee),ee.belongsTo(ne),te.hasMany(oe),oe.belongsTo(te),se.hasMany(oe),oe.belongsTo(se),ne.hasMany(ie),ie.belongsTo(ne),te.hasMany(ie),ie.belongsTo(te);var ae;!function(e){e.ERROR="",e.INFO="",e.NONE="",e.SUCCESS="",e.WARNING=""}(ae||(ae={}));class de{static write(e,t){de.writeRaw(ae.NONE,"",e,t)}static error(e,t){de.writeRaw(ae.ERROR,"error",e,t)}static info(e,t){de.writeRaw(ae.INFO,"info",e,t)}static success(e,t){de.writeRaw(ae.ERROR,"success",e,t)}static warning(e,t){de.writeRaw(ae.WARNING,"warning",e,t)}static writeConsole(e,t,s){const o=r(new Date,"%H:%M:%S");console.log(">",e,t+" ".repeat(10-t.length),"","|",o,"|",s)}static writeRaw(e,t,r,s){de.writeConsole(e,t,r),re.create({type:t,source:s,message:r}).catch((()=>null))}}class ue{static success(e,t){return e.status(200).json({message:t})}static data(e,t){return e.status(200).json(Object.assign({},t))}static error(e,t){return e.status(500).json({message:t})}static list(e,t){return e.status(200).json([...t])}static notfound(e,t){return e.status(404).json({message:t})}static validation(e,t,r){return e.status(400).json({message:t,details:r})}static unauthorized(e,t){return e.status(401).json({message:t})}}const ce=e=>{if(f(e))return h(e)},le={http:ce(B),token:ce(H)},me={http:ce(z),token:ce(K)};class pe{static all(e,t){return V(this,void 0,void 0,(function*(){return yield se.findAll({where:Object.assign(Object.assign({},t),{state:!0}),include:[{model:oe,required:!0,include:[{model:te,required:!0,where:{state:!0},include:[{model:ie,required:!0,where:{userId:e.id}}]}]}]}).catch((e=>{de.error(e.message,"permission")}))}))}static get(e){return V(this,void 0,void 0,(function*(){const t=yield pe.all(e);return t?t.map((e=>e.name)):[]}))}static is(e,t){return V(this,void 0,void 0,(function*(){const r=yield pe.all(e,{name:t});return r&&!!r.length}))}}const ge=["id","name","email","username"],ve=(e,t,r)=>V(void 0,void 0,void 0,(function*(){const s=e.get(M)||"";if(!le.token)return ue.error(t,"Invalid Secret");if(!s||s.split(".").length<3)return ue.error(t,"Invalid token");const o=w.verify(s,le.token);if(o&&o.id&&o.secret){const s=yield ne.findOne({where:{id:o.id}}).catch((e=>{de.error(e.message,"token.check")}));if(s&&s.state){const n=yield ee.findOne({where:{logout:null,secret:o.secret,userId:s.id}}).catch((e=>{de.error(e.message,"token.check")}));if(n){const o=Date.now();if(o>n.timestamp)return ue.error(t,"Expired token");n.timestamp=o+6e4*F;const i=e.get("User-Agent");return i&&(n.useragent=i),yield n.save(),e.auth=n,e.user=s,r()}}return ue.error(t,"Access denied")}return ue.error(t,"Invalid token")})),fe=e=>{const t={abortEarly:!1,allowUnknown:!0,stripUnknown:!0};return(r,s,o)=>{const{error:n,value:i}=e.validate(r.body,t);if(n){const e=n.details.map((e=>e.message)).join(", "),t=[];return n.details.forEach((e=>{var r;t.push({key:(null===(r=e.context)||void 0===r?void 0:r.key)||"",type:e.type})})),ue.validation(s,"Validation error: "+e,t)}return r.body=i,o()}},he={username:I.string().min(5).required(),password:I.string().min(5).required()},ye=fe(I.object(he)),we=v();we.get("/",ve,((e,t)=>V(void 0,void 0,void 0,(function*(){const r=e.user;if(!r)return ue.unauthorized(t,"Access denied");const s=yield pe.all(r);if(s){const e=n(ge,r.toJSON()),o=s.map((e=>e.name));return ue.data(t,Object.assign(Object.assign({},e),{permissions:o}))}return ue.error(t,"Internal Server Error")})))),we.post("/login",ye,((e,t)=>V(void 0,void 0,void 0,(function*(){const{username:r,password:n}=e.body;if(!r||!n)return ue.error(t,"Invalid parameters");yield s(O);const i=yield ne.findOne({where:{username:r}}).catch((e=>{de.error(e.message,"auth.login")}));if(!i)return ue.notfound(t,"User not found");if(!i.state)return ue.error(t,"User is disabled");const a=y(n).toString();if(i.password!=a)return ue.error(t,"Invalid password");if(!me.token)return ue.error(t,"Invalid Secret");const d=i.id,u=o(),c=w.sign({id:d,secret:u},me.token,{algorithm:"RS256"});if(c){const r=Date.now(),s=r+6e4*F,o=e.get("User-Agent");if(yield ee.create({login:r,secret:u,timestamp:s,useragent:o,userId:d}).catch((e=>{de.error(e.message,"auth.login")})))return ue.data(t,{token:c})}return ue.error(t,"Internal Server Error")})))),we.get("/logout",ve,((e,t)=>V(void 0,void 0,void 0,(function*(){const r=e.auth;return r?(r.logout=Date.now(),r.timestamp=r.logout,yield r.save(),ue.success(t,"Disconcerted")):ue.unauthorized(t,"Access denied")}))));class Ie{constructor(e,t){this.log=e,this.model=t}add(){return(e,t)=>V(this,void 0,void 0,(function*(){const r=yield this.model.create(e.body).catch((e=>{de.error(e.message,this.log+".add")}));return r?ue.data(t,{id:r.id}):ue.error(t,"Internal Server Error")}))}all(e,t){return(r,s)=>V(this,void 0,void 0,(function*(){var o,n;e||(e={});const i=null===(o=r.query.limit)||void 0===o?void 0:o.toString();i&&(e.limit=parseInt(i));const a=null===(n=r.query.offset)||void 0===n?void 0:n.toString();a&&(e.offset=parseInt(a));const d=yield this.model.findAll(e).catch((e=>{de.error(e.message,this.log+".all")})),u=yield this.model.count().catch((e=>{de.error(e.message,this.log+".all")}));return d?ue.data(s,{count:u,records:t?t(d):d}):ue.error(s,"Internal Server Error")}))}del(){return(e,t)=>V(this,void 0,void 0,(function*(){const{id:r}=e.params;return(yield this.model.destroy({where:{id:r}}).catch((e=>{de.error(e.message,this.log+".del")})))?ue.success(t,"OK"):ue.notfound(t,"Record not found")}))}get(e,t){return(r,s)=>V(this,void 0,void 0,(function*(){e||(e={});const{id:o}=r.params,n=yield this.model.findByPk(o,e).catch((e=>{de.error(e.message,this.log+".get")}));return n?ue.data(s,t?t(n):n.toJSON()):null===n?ue.notfound(s,"Record not found"):ue.error(s,"Internal Server Error")}))}set(){return(e,t)=>V(this,void 0,void 0,(function*(){const{id:r}=e.params,s=yield this.model.update(e.body,{where:{id:r}}).catch((e=>{de.error(e.message,this.log+".set")}));if(s){const[e]=s;if(e)return ue.success(t,"OK")}return ue.success(t,"Record not changed")}))}}const be=new Ie("groups",te),Te=be.add(),Ae=be.all(),Se=be.del(),_e=be.get(),Ee=be.set(),Pe=e=>(t,r,s)=>V(void 0,void 0,void 0,(function*(){const o=t.user;return o?(yield pe.is(o,e))?s():ue.error(r,"Unauthorized access"):ue.unauthorized(r,"Access denied")})),Oe={name:I.string().min(3).required(),description:I.string().allow(null,""),state:I.boolean().required()},Ne=fe(I.object(Oe)),Re={routes:I.array().items(I.number())},qe=fe(I.object(Re)),je={name:I.string().min(3).empty(""),description:I.string().allow(null,""),state:I.boolean().empty("")},ke=fe(I.object(je)),xe=v();xe.use(ve),xe.get("/",Pe("groups.read"),Ae),xe.post("/",Pe("groups.write"),Ne,Te),xe.route("/:id").get(Pe("groups.read"),_e).put(Pe("groups.write"),ke,Ee).delete(Pe("groups.write"),Se),xe.route("/:id/routes").get(Pe("groups.read"),qe,((e,t)=>V(void 0,void 0,void 0,(function*(){const{id:r}=e.params,s=yield oe.findAll({where:{groupId:r},include:[{model:se,required:!0}]}).catch((e=>{de.error(e.message,"groups.routesAll")}));return s?ue.list(t,s.map((e=>e.route))):ue.error(t,"Internal Server Error")})))).put(Pe("groups.write"),qe,((e,t)=>V(void 0,void 0,void 0,(function*(){const{id:r}=e.params,{routes:s}=e.body,o=yield te.findByPk(r,{attributes:["id"]}).catch((e=>{de.error(e.message,"groups.routesSet")}));if(o){const e=o.id;if(yield oe.destroy({where:{groupId:e}}),s){let t=0;for(let r=0;r<s.length;r++)t=yield se.count({where:{id:s[r]}}),t&&(yield oe.create({groupId:e,routeId:s[r]}))}return ue.success(t,"OK")}return null===o?ue.notfound(t,"Record not found"):ue.error(t,"Internal Server Error")}))));const De=new Ie("route",se),Be=De.add(),ze=De.all(),Ge=De.del(),Ue=De.get(),Le=De.set(),Ce={name:I.string().min(5).required(),permission:I.string().required(),description:I.string().allow(null,""),state:I.boolean().required()},He=fe(I.object(Ce)),Ke={name:I.string().min(5).empty(""),permission:I.string().empty(""),description:I.string().allow(null,""),state:I.boolean().empty("")},Me=fe(I.object(Ke)),Fe=v();Fe.use(ve),Fe.get("/",Pe("routes.read"),ze),Fe.post("/",Pe("routes.write"),He,Be),Fe.route("/:id").get(Pe("routes.read"),Ue).put(Pe("routes.write"),Me,Le).delete(Pe("routes.write"),Ge);const We=new Ie("user",ne),Xe=new Ie("user.groups",ie),Ye=["id","name","email","username","description","state"],$e=We.del(),Je={name:I.string().min(5).required(),email:I.string().email().required(),username:I.string().min(5).required(),password:I.string().min(5).required(),description:I.string().allow(null,""),state:I.boolean().required()},Ve=fe(I.object(Je)),Ze={groups:I.array().items(I.number())},Qe=fe(I.object(Ze)),et={name:I.string().min(5).empty(""),email:I.string().email().empty(""),username:I.string().min(5).empty(""),password:I.string().min(5).empty(""),description:I.string().allow(null,""),state:I.boolean().empty("")},tt=fe(I.object(et)),rt=v();rt.use(ve),rt.get("/",Pe("users.read"),((e,t)=>V(void 0,void 0,void 0,(function*(){return We.all({attributes:Ye})(e,t)})))),rt.post("/",Pe("users.write"),Ve,((e,t)=>V(void 0,void 0,void 0,(function*(){return e.body.password=y(e.body.password).toString(),yield We.add()(e,t)})))),rt.route("/:id").get(Pe("users.read"),((e,t)=>V(void 0,void 0,void 0,(function*(){return We.get({attributes:Ye})(e,t)})))).put(Pe("users.write"),tt,((e,t)=>V(void 0,void 0,void 0,(function*(){return e.body.password&&(e.body.password=y(e.body.password).toString()),We.set()(e,t)})))).delete(Pe("users.write"),$e),rt.route("/:id/groups").get(Pe("users.read"),((e,t)=>V(void 0,void 0,void 0,(function*(){const{id:r}=e.params;return Xe.all({where:{userId:r},include:[{model:te,required:!0}]},(e=>e.map((e=>e.group))))(e,t)})))).put(Pe("users.write"),Qe,((e,t)=>V(void 0,void 0,void 0,(function*(){const{id:r}=e.params,{groups:s}=e.body,o=yield ne.findByPk(r,{attributes:["id"]}).catch((e=>{de.error(e.message,"user.groups.set")}));if(o){if(yield ie.destroy({where:{userId:o.id}}),s){let e=0;for(let t=0;t<s.length;t++)e=yield te.count({where:{id:s[t]}}),e&&(yield ie.create({userId:o.id,groupId:s[t]}))}return ue.success(t,"OK")}return ue.notfound(t,"Record not found")}))));const st=v();st.use("/v1/auth",we),st.use("/v1/groups",xe),st.use("/v1/routes",Fe),st.use("/v1/users",rt);const ot=g();ot.use(g.json()),ot.use(((e,t,r,s)=>e?(de.error(e.message,"express"),ue.error(r,"Internal Server Error")):s())),ot.use(g.urlencoded({extended:!0})),ot.disable("x-powered-by"),L&&ot.use(p()),ot.use("/api",st),ot.all("/api/*",((e,t)=>ue.notfound(t,"Invalid API"))),U&&(f(U)&&ot.use(g.static(U)),ot.get("*",((e,t)=>{if(!f(U))return ue.notfound(t,'File "index.html" not found');t.sendFile(U+"/index.html")})));const nt=e=>{const t=new S.Server({server:e,verifyClient:it});return t.on("connection",(e=>{e.on("message",(t=>{e.close()})),setTimeout((()=>{e.close()}),2e3),e.on("close",(()=>{t.emit("close",e)}))})),t},it=(e,t)=>t(J),at=()=>{Q.sync().then((()=>{V(void 0,void 0,void 0,(function*(){if(0==(yield ne.count())){const e=yield ne.create({name:"Admin",email:"admin@localhost",username:"admin",password:"8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",description:"",state:!0});if(0==(yield te.count())){const r=yield te.create({name:"admin",description:"",state:!0});if(yield ie.create({userId:e.id,groupId:r.id}),0==(yield se.count())){const e=["read","write"];t(["users","groups","permission"],(s=>V(void 0,void 0,void 0,(function*(){t(e,(e=>V(void 0,void 0,void 0,(function*(){const t=s.toLowerCase()+"."+e.toLowerCase(),o=yield se.create({name:t,description:"",state:!0});yield oe.create({groupId:r.id,routeId:o.id})}))))}))))}}}})),J&&nt(dt)}))},dt=(e=>{if(!le.http||!me.http){const t=b.createServer(e);return t.listen(G,(()=>de.info(`Server started on port ${G}`))),t}const t=b.createServer(((e,t)=>{const r=80==G?":80":"";t.writeHead(301,{Location:"https://"+e.headers.host+r+e.url}),t.end()})),r=T.createServer({key:me.http,cert:le.http},e);if(C){return new A((e=>{e.once("data",(s=>{e.pause(),e.unshift(s),(22===s[0]?r:t).emit("connection",e),process.nextTick((()=>e.resume()))}))})).listen(G,(()=>de.info(`Server started on port ${G}`))),r}return 443==G&&t.listen(80,(()=>de.info("Server started on port 80"))),r.listen(G,(()=>de.info(`Server started on port ${G}`))),r})(ot),ut=(e,t)=>{const r=i()+"."+t;return f(P(e,r))?ut(e,t):r},ct=e=>{const t=(null==e?void 0:e.path)||$,r=E.diskStorage({destination:(e,r,s)=>s(null,t),filename:(e,r,s)=>V(void 0,void 0,void 0,(function*(){return s(null,ut(t,_.extension(r.mimetype)||"bin"))}))}),s=1e6*((null==e?void 0:e.size)||Y),o=E({storage:r,fileFilter(t,r,s){var o;return V(this,void 0,void 0,(function*(){const t=null===(o=null==e?void 0:e.types)||void 0===o?void 0:o.indexOf(r.mimetype);return void 0!==t&&t<0?s(new Error("File type not allowed")):s(null,!0)}))},limits:{fileSize:s}}),n=(null==e?void 0:e.count)||W,i=(null==e?void 0:e.field)||X,a=o.array(i,n);return(e,t,r)=>{a(e,t,(function(e){return e?ue.error(t,e.message):r()}))}};export{O as API_AUTH_SLEEP,N as API_DB_DATABASE,R as API_DB_HOST,D as API_DB_LOG,q as API_DB_PASSWORD,j as API_DB_PORT,k as API_DB_TYPE,x as API_DB_USERNAME,L as API_HTTP_CROSS,B as API_HTTP_CRT,C as API_HTTP_HYBRID,z as API_HTTP_KEY,G as API_HTTP_PORT,U as API_HTTP_PUBLIC,H as API_TOKEN_CRT,M as API_TOKEN_HEADER,K as API_TOKEN_KEY,F as API_TOKEN_LIFETIME,W as API_UPLOAD_COUNT,X as API_UPLOAD_FIELD,Y as API_UPLOAD_SIZE,$ as API_UPLOAD_TEMP,J as API_WEBSOCKET_START,ee as Auth,Ie as Controller,te as Group,oe as GroupRoute,de as Log,re as Logs,pe as Permission,ue as Responses,se as Route,ne as User,ie as UserGroup,ot as app,ve as auth,le as crt,at as initialize,me as key,Pe as permission,st as router,Q as sequelize,dt as server,nt as startSocket,ct as upload,Z as uuid,fe as validation};
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * @andrewcaires/api v1.0.0
3
+ * (c) 2022 Andrew Caires
4
+ * @license: MIT
5
+ */
6
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@andrewcaires/utils.js"),t=require("dotenv"),r=require("sequelize"),s=require("cors"),o=require("express"),n=require("fs"),i=require("crypto-js"),a=require("jsonwebtoken"),d=require("joi"),u=require("http"),l=require("https"),c=require("net"),p=require("ws"),f=require("mime-types"),m=require("multer"),g=require("path");function v(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function y(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var s=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,s.get?s:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var I=v(s),h=v(o),T=v(a),_=v(d),w=y(u),A=y(l),P=v(p),S=v(f),x=v(m);t.config();const E=parseInt(process.env.API_AUTH_SLEEP||"1000"),b=process.env.API_DB_DATABASE||"",O=process.env.API_DB_HOST||"127.0.0.1",D=process.env.API_DB_PASSWORD||"",R=parseInt(process.env.API_DB_PORT||"3306"),q=process.env.API_DB_TYPE||"",N=process.env.API_DB_USERNAME||"root",j=e.toBool(process.env.API_DB_LOG||"false"),B=process.env.API_HTTP_CRT||"",U=process.env.API_HTTP_KEY||"",k=parseInt(process.env.API_HTTP_PORT||"3000"),L=process.env.API_HTTP_PUBLIC||"",H=e.toBool(process.env.API_HTTP_CROSS||"false"),z=e.toBool(process.env.API_HTTP_HYBRID||"false"),M=process.env.API_TOKEN_CRT||"",C=process.env.API_TOKEN_KEY||"",G=process.env.API_TOKEN_HEADER||"authorization",K=parseInt(process.env.API_TOKEN_LIFETIME||"15"),F=parseInt(process.env.API_UPLOAD_COUNT||"1"),Y=process.env.API_UPLOAD_FIELD||"upload",W=parseInt(process.env.API_UPLOAD_SIZE||"2"),X=process.env.API_UPLOAD_TEMP||"./temp",V=e.toBool(process.env.API_WEBSOCKET_START||"false");function $(e,t,r,s){return new(r||(r=Promise))((function(o,n){function i(e){try{d(s.next(e))}catch(e){n(e)}}function a(e){try{d(s.throw(e))}catch(e){n(e)}}function d(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(i,a)}d((s=s.apply(e,t||[])).next())}))}const J={type:r.UUID,defaultValue:r.UUIDV4,allowNull:!1,primaryKey:!0},Z=(()=>{switch(q){case"mariadb":return new r.Sequelize(b,N,D,{host:O,port:R,dialect:"mariadb",logging:j,define:{timestamps:!1}});case"mysql":return new r.Sequelize(b,N,D,{host:O,port:R,dialect:"mysql",logging:j,define:{timestamps:!1}});case"postgres":return new r.Sequelize(b,N,D,{host:O,port:R,dialect:"postgres",logging:j,define:{timestamps:!1}})}throw new Error("No database selected")})();class Q extends r.Model{}Q.init({login:r.DataTypes.BIGINT,logout:r.DataTypes.BIGINT,secret:r.DataTypes.STRING,timestamp:r.DataTypes.BIGINT,useragent:r.DataTypes.STRING},{sequelize:Z,modelName:"auth"});class ee extends r.Model{}ee.init({name:r.DataTypes.STRING,description:r.DataTypes.TEXT,state:r.DataTypes.BOOLEAN},{sequelize:Z,modelName:"group"});class te extends r.Model{}te.init({type:r.DataTypes.STRING,source:r.DataTypes.STRING,message:r.DataTypes.TEXT},{sequelize:Z,modelName:"logs",timestamps:!0,createdAt:"created",updatedAt:!1});class re extends r.Model{}re.init({name:r.DataTypes.STRING,description:r.DataTypes.TEXT,state:r.DataTypes.BOOLEAN},{sequelize:Z,modelName:"route"});class se extends r.Model{}se.init({},{sequelize:Z,modelName:"groups_routes"}),se.removeAttribute("id");class oe extends r.Model{}oe.init({name:r.DataTypes.STRING,email:r.DataTypes.STRING,username:r.DataTypes.STRING,password:r.DataTypes.STRING,description:r.DataTypes.TEXT,state:r.DataTypes.BOOLEAN},{sequelize:Z,modelName:"user"});class ne extends r.Model{}ne.init({},{sequelize:Z,modelName:"users_groups"}),ne.removeAttribute("id"),oe.hasMany(Q),Q.belongsTo(oe),ee.hasMany(se),se.belongsTo(ee),re.hasMany(se),se.belongsTo(re),oe.hasMany(ne),ne.belongsTo(oe),ee.hasMany(ne),ne.belongsTo(ee);var ie;!function(e){e.ERROR="",e.INFO="",e.NONE="",e.SUCCESS="",e.WARNING=""}(ie||(ie={}));class ae{static write(e,t){ae.writeRaw(ie.NONE,"",e,t)}static error(e,t){ae.writeRaw(ie.ERROR,"error",e,t)}static info(e,t){ae.writeRaw(ie.INFO,"info",e,t)}static success(e,t){ae.writeRaw(ie.ERROR,"success",e,t)}static warning(e,t){ae.writeRaw(ie.WARNING,"warning",e,t)}static writeConsole(t,r,s){const o=e.dateFormat(new Date,"%H:%M:%S");console.log(">",t,r+" ".repeat(10-r.length),"","|",o,"|",s)}static writeRaw(e,t,r,s){ae.writeConsole(e,t,r),te.create({type:t,source:s,message:r}).catch((()=>null))}}class de{static success(e,t){return e.status(200).json({message:t})}static data(e,t){return e.status(200).json(Object.assign({},t))}static error(e,t){return e.status(500).json({message:t})}static list(e,t){return e.status(200).json([...t])}static notfound(e,t){return e.status(404).json({message:t})}static validation(e,t,r){return e.status(400).json({message:t,details:r})}static unauthorized(e,t){return e.status(401).json({message:t})}}const ue=e=>{if(n.existsSync(e))return n.readFileSync(e)},le={http:ue(B),token:ue(M)},ce={http:ue(U),token:ue(C)};class pe{static all(e,t){return $(this,void 0,void 0,(function*(){return yield re.findAll({where:Object.assign(Object.assign({},t),{state:!0}),include:[{model:se,required:!0,include:[{model:ee,required:!0,where:{state:!0},include:[{model:ne,required:!0,where:{userId:e.id}}]}]}]}).catch((e=>{ae.error(e.message,"permission")}))}))}static get(e){return $(this,void 0,void 0,(function*(){const t=yield pe.all(e);return t?t.map((e=>e.name)):[]}))}static is(e,t){return $(this,void 0,void 0,(function*(){const r=yield pe.all(e,{name:t});return r&&!!r.length}))}}const fe=["id","name","email","username"],me=(e,t,r)=>$(void 0,void 0,void 0,(function*(){const s=e.get(G)||"";if(!le.token)return de.error(t,"Invalid Secret");if(!s||s.split(".").length<3)return de.error(t,"Invalid token");const o=T.default.verify(s,le.token);if(o&&o.id&&o.secret){const s=yield oe.findOne({where:{id:o.id}}).catch((e=>{ae.error(e.message,"token.check")}));if(s&&s.state){const n=yield Q.findOne({where:{logout:null,secret:o.secret,userId:s.id}}).catch((e=>{ae.error(e.message,"token.check")}));if(n){const o=Date.now();if(o>n.timestamp)return de.error(t,"Expired token");n.timestamp=o+6e4*K;const i=e.get("User-Agent");return i&&(n.useragent=i),yield n.save(),e.auth=n,e.user=s,r()}}return de.error(t,"Access denied")}return de.error(t,"Invalid token")})),ge=e=>{const t={abortEarly:!1,allowUnknown:!0,stripUnknown:!0};return(r,s,o)=>{const{error:n,value:i}=e.validate(r.body,t);if(n){const e=n.details.map((e=>e.message)).join(", "),t=[];return n.details.forEach((e=>{var r;t.push({key:(null===(r=e.context)||void 0===r?void 0:r.key)||"",type:e.type})})),de.validation(s,"Validation error: "+e,t)}return r.body=i,o()}},ve={username:_.default.string().min(5).required(),password:_.default.string().min(5).required()},ye=ge(_.default.object(ve)),Ie=o.Router();Ie.get("/",me,((t,r)=>$(void 0,void 0,void 0,(function*(){const s=t.user;if(!s)return de.unauthorized(r,"Access denied");const o=yield pe.all(s);if(o){const t=e.allowedObject(fe,s.toJSON()),n=o.map((e=>e.name));return de.data(r,Object.assign(Object.assign({},t),{permissions:n}))}return de.error(r,"Internal Server Error")})))),Ie.post("/login",ye,((t,r)=>$(void 0,void 0,void 0,(function*(){const{username:s,password:o}=t.body;if(!s||!o)return de.error(r,"Invalid parameters");yield e.sleep(E);const n=yield oe.findOne({where:{username:s}}).catch((e=>{ae.error(e.message,"auth.login")}));if(!n)return de.notfound(r,"User not found");if(!n.state)return de.error(r,"User is disabled");const a=i.SHA256(o).toString();if(n.password!=a)return de.error(r,"Invalid password");if(!ce.token)return de.error(r,"Invalid Secret");const d=n.id,u=e.uniqueID(),l=T.default.sign({id:d,secret:u},ce.token,{algorithm:"RS256"});if(l){const e=Date.now(),s=e+6e4*K,o=t.get("User-Agent");if(yield Q.create({login:e,secret:u,timestamp:s,useragent:o,userId:d}).catch((e=>{ae.error(e.message,"auth.login")})))return de.data(r,{token:l})}return de.error(r,"Internal Server Error")})))),Ie.get("/logout",me,((e,t)=>$(void 0,void 0,void 0,(function*(){const r=e.auth;return r?(r.logout=Date.now(),r.timestamp=r.logout,yield r.save(),de.success(t,"Disconcerted")):de.unauthorized(t,"Access denied")}))));class he{constructor(e,t){this.log=e,this.model=t}add(){return(e,t)=>$(this,void 0,void 0,(function*(){const r=yield this.model.create(e.body).catch((e=>{ae.error(e.message,this.log+".add")}));return r?de.data(t,{id:r.id}):de.error(t,"Internal Server Error")}))}all(e,t){return(r,s)=>$(this,void 0,void 0,(function*(){var o,n;e||(e={});const i=null===(o=r.query.limit)||void 0===o?void 0:o.toString();i&&(e.limit=parseInt(i));const a=null===(n=r.query.offset)||void 0===n?void 0:n.toString();a&&(e.offset=parseInt(a));const d=yield this.model.findAll(e).catch((e=>{ae.error(e.message,this.log+".all")})),u=yield this.model.count().catch((e=>{ae.error(e.message,this.log+".all")}));return d?de.data(s,{count:u,records:t?t(d):d}):de.error(s,"Internal Server Error")}))}del(){return(e,t)=>$(this,void 0,void 0,(function*(){const{id:r}=e.params;return(yield this.model.destroy({where:{id:r}}).catch((e=>{ae.error(e.message,this.log+".del")})))?de.success(t,"OK"):de.notfound(t,"Record not found")}))}get(e,t){return(r,s)=>$(this,void 0,void 0,(function*(){e||(e={});const{id:o}=r.params,n=yield this.model.findByPk(o,e).catch((e=>{ae.error(e.message,this.log+".get")}));return n?de.data(s,t?t(n):n.toJSON()):null===n?de.notfound(s,"Record not found"):de.error(s,"Internal Server Error")}))}set(){return(e,t)=>$(this,void 0,void 0,(function*(){const{id:r}=e.params,s=yield this.model.update(e.body,{where:{id:r}}).catch((e=>{ae.error(e.message,this.log+".set")}));if(s){const[e]=s;if(e)return de.success(t,"OK")}return de.success(t,"Record not changed")}))}}const Te=new he("groups",ee),_e=Te.add(),we=Te.all(),Ae=Te.del(),Pe=Te.get(),Se=Te.set(),xe=e=>(t,r,s)=>$(void 0,void 0,void 0,(function*(){const o=t.user;return o?(yield pe.is(o,e))?s():de.error(r,"Unauthorized access"):de.unauthorized(r,"Access denied")})),Ee={name:_.default.string().min(3).required(),description:_.default.string().allow(null,""),state:_.default.boolean().required()},be=ge(_.default.object(Ee)),Oe={routes:_.default.array().items(_.default.number())},De=ge(_.default.object(Oe)),Re={name:_.default.string().min(3).empty(""),description:_.default.string().allow(null,""),state:_.default.boolean().empty("")},qe=ge(_.default.object(Re)),Ne=o.Router();Ne.use(me),Ne.get("/",xe("groups.read"),we),Ne.post("/",xe("groups.write"),be,_e),Ne.route("/:id").get(xe("groups.read"),Pe).put(xe("groups.write"),qe,Se).delete(xe("groups.write"),Ae),Ne.route("/:id/routes").get(xe("groups.read"),De,((e,t)=>$(void 0,void 0,void 0,(function*(){const{id:r}=e.params,s=yield se.findAll({where:{groupId:r},include:[{model:re,required:!0}]}).catch((e=>{ae.error(e.message,"groups.routesAll")}));return s?de.list(t,s.map((e=>e.route))):de.error(t,"Internal Server Error")})))).put(xe("groups.write"),De,((e,t)=>$(void 0,void 0,void 0,(function*(){const{id:r}=e.params,{routes:s}=e.body,o=yield ee.findByPk(r,{attributes:["id"]}).catch((e=>{ae.error(e.message,"groups.routesSet")}));if(o){const e=o.id;if(yield se.destroy({where:{groupId:e}}),s){let t=0;for(let r=0;r<s.length;r++)t=yield re.count({where:{id:s[r]}}),t&&(yield se.create({groupId:e,routeId:s[r]}))}return de.success(t,"OK")}return null===o?de.notfound(t,"Record not found"):de.error(t,"Internal Server Error")}))));const je=new he("route",re),Be=je.add(),Ue=je.all(),ke=je.del(),Le=je.get(),He=je.set(),ze={name:_.default.string().min(5).required(),permission:_.default.string().required(),description:_.default.string().allow(null,""),state:_.default.boolean().required()},Me=ge(_.default.object(ze)),Ce={name:_.default.string().min(5).empty(""),permission:_.default.string().empty(""),description:_.default.string().allow(null,""),state:_.default.boolean().empty("")},Ge=ge(_.default.object(Ce)),Ke=o.Router();Ke.use(me),Ke.get("/",xe("routes.read"),Ue),Ke.post("/",xe("routes.write"),Me,Be),Ke.route("/:id").get(xe("routes.read"),Le).put(xe("routes.write"),Ge,He).delete(xe("routes.write"),ke);const Fe=new he("user",oe),Ye=new he("user.groups",ne),We=["id","name","email","username","description","state"],Xe=Fe.del(),Ve={name:_.default.string().min(5).required(),email:_.default.string().email().required(),username:_.default.string().min(5).required(),password:_.default.string().min(5).required(),description:_.default.string().allow(null,""),state:_.default.boolean().required()},$e=ge(_.default.object(Ve)),Je={groups:_.default.array().items(_.default.number())},Ze=ge(_.default.object(Je)),Qe={name:_.default.string().min(5).empty(""),email:_.default.string().email().empty(""),username:_.default.string().min(5).empty(""),password:_.default.string().min(5).empty(""),description:_.default.string().allow(null,""),state:_.default.boolean().empty("")},et=ge(_.default.object(Qe)),tt=o.Router();tt.use(me),tt.get("/",xe("users.read"),((e,t)=>$(void 0,void 0,void 0,(function*(){return Fe.all({attributes:We})(e,t)})))),tt.post("/",xe("users.write"),$e,((e,t)=>$(void 0,void 0,void 0,(function*(){return e.body.password=i.SHA256(e.body.password).toString(),yield Fe.add()(e,t)})))),tt.route("/:id").get(xe("users.read"),((e,t)=>$(void 0,void 0,void 0,(function*(){return Fe.get({attributes:We})(e,t)})))).put(xe("users.write"),et,((e,t)=>$(void 0,void 0,void 0,(function*(){return e.body.password&&(e.body.password=i.SHA256(e.body.password).toString()),Fe.set()(e,t)})))).delete(xe("users.write"),Xe),tt.route("/:id/groups").get(xe("users.read"),((e,t)=>$(void 0,void 0,void 0,(function*(){const{id:r}=e.params;return Ye.all({where:{userId:r},include:[{model:ee,required:!0}]},(e=>e.map((e=>e.group))))(e,t)})))).put(xe("users.write"),Ze,((e,t)=>$(void 0,void 0,void 0,(function*(){const{id:r}=e.params,{groups:s}=e.body,o=yield oe.findByPk(r,{attributes:["id"]}).catch((e=>{ae.error(e.message,"user.groups.set")}));if(o){if(yield ne.destroy({where:{userId:o.id}}),s){let e=0;for(let t=0;t<s.length;t++)e=yield ee.count({where:{id:s[t]}}),e&&(yield ne.create({userId:o.id,groupId:s[t]}))}return de.success(t,"OK")}return de.notfound(t,"Record not found")}))));const rt=o.Router();rt.use("/v1/auth",Ie),rt.use("/v1/groups",Ne),rt.use("/v1/routes",Ke),rt.use("/v1/users",tt);const st=h.default();st.use(h.default.json()),st.use(((e,t,r,s)=>e?(ae.error(e.message,"express"),de.error(r,"Internal Server Error")):s())),st.use(h.default.urlencoded({extended:!0})),st.disable("x-powered-by"),H&&st.use(I.default()),st.use("/api",rt),st.all("/api/*",((e,t)=>de.notfound(t,"Invalid API"))),L&&(n.existsSync(L)&&st.use(h.default.static(L)),st.get("*",((e,t)=>{if(!n.existsSync(L))return de.notfound(t,'File "index.html" not found');t.sendFile(L+"/index.html")})));const ot=e=>{const t=new P.default.Server({server:e,verifyClient:nt});return t.on("connection",(e=>{e.on("message",(t=>{e.close()})),setTimeout((()=>{e.close()}),2e3),e.on("close",(()=>{t.emit("close",e)}))})),t},nt=(e,t)=>t(V),it=(e=>{if(!le.http||!ce.http){const t=w.createServer(e);return t.listen(k,(()=>ae.info(`Server started on port ${k}`))),t}const t=w.createServer(((e,t)=>{const r=80==k?":80":"";t.writeHead(301,{Location:"https://"+e.headers.host+r+e.url}),t.end()})),r=A.createServer({key:ce.http,cert:le.http},e);if(z){return new c.Server((e=>{e.once("data",(s=>{e.pause(),e.unshift(s),(22===s[0]?r:t).emit("connection",e),process.nextTick((()=>e.resume()))}))})).listen(k,(()=>ae.info(`Server started on port ${k}`))),r}return 443==k&&t.listen(80,(()=>ae.info("Server started on port 80"))),r.listen(k,(()=>ae.info(`Server started on port ${k}`))),r})(st),at=(t,r)=>{const s=e.uuidv4()+"."+r;return n.existsSync(g.join(t,s))?at(t,r):s};exports.API_AUTH_SLEEP=E,exports.API_DB_DATABASE=b,exports.API_DB_HOST=O,exports.API_DB_LOG=j,exports.API_DB_PASSWORD=D,exports.API_DB_PORT=R,exports.API_DB_TYPE=q,exports.API_DB_USERNAME=N,exports.API_HTTP_CROSS=H,exports.API_HTTP_CRT=B,exports.API_HTTP_HYBRID=z,exports.API_HTTP_KEY=U,exports.API_HTTP_PORT=k,exports.API_HTTP_PUBLIC=L,exports.API_TOKEN_CRT=M,exports.API_TOKEN_HEADER=G,exports.API_TOKEN_KEY=C,exports.API_TOKEN_LIFETIME=K,exports.API_UPLOAD_COUNT=F,exports.API_UPLOAD_FIELD=Y,exports.API_UPLOAD_SIZE=W,exports.API_UPLOAD_TEMP=X,exports.API_WEBSOCKET_START=V,exports.Auth=Q,exports.Controller=he,exports.Group=ee,exports.GroupRoute=se,exports.Log=ae,exports.Logs=te,exports.Permission=pe,exports.Responses=de,exports.Route=re,exports.User=oe,exports.UserGroup=ne,exports.app=st,exports.auth=me,exports.crt=le,exports.initialize=()=>{Z.sync().then((()=>{$(void 0,void 0,void 0,(function*(){if(0==(yield oe.count())){const t=yield oe.create({name:"Admin",email:"admin@localhost",username:"admin",password:"8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",description:"",state:!0});if(0==(yield ee.count())){const r=yield ee.create({name:"admin",description:"",state:!0});if(yield ne.create({userId:t.id,groupId:r.id}),0==(yield re.count())){const t=["read","write"],s=["users","groups","permission"];e.forEachSeries(s,(s=>$(void 0,void 0,void 0,(function*(){e.forEachSeries(t,(e=>$(void 0,void 0,void 0,(function*(){const t=s.toLowerCase()+"."+e.toLowerCase(),o=yield re.create({name:t,description:"",state:!0});yield se.create({groupId:r.id,routeId:o.id})}))))}))))}}}})),V&&ot(it)}))},exports.key=ce,exports.permission=xe,exports.router=rt,exports.sequelize=Z,exports.server=it,exports.startSocket=ot,exports.upload=e=>{const t=(null==e?void 0:e.path)||X,r=x.default.diskStorage({destination:(e,r,s)=>s(null,t),filename:(e,r,s)=>$(void 0,void 0,void 0,(function*(){return s(null,at(t,S.default.extension(r.mimetype)||"bin"))}))}),s=1e6*((null==e?void 0:e.size)||W),o=x.default({storage:r,fileFilter(t,r,s){var o;return $(this,void 0,void 0,(function*(){const t=null===(o=null==e?void 0:e.types)||void 0===o?void 0:o.indexOf(r.mimetype);return void 0!==t&&t<0?s(new Error("File type not allowed")):s(null,!0)}))},limits:{fileSize:s}}),n=(null==e?void 0:e.count)||F,i=(null==e?void 0:e.field)||Y,a=o.array(i,n);return(e,t,r)=>{a(e,t,(function(e){return e?de.error(t,e.message):r()}))}},exports.uuid=J,exports.validation=ge;
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@andrewcaires/api",
3
+ "version": "1.0.0",
4
+ "description": "Simple api for small applications using express, jsonwebtoken, sequelize and websocket",
5
+ "main": "dist/index.umd.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "serve": "ts-node src/test.ts",
10
+ "build": "rollup -c build/rollup.config.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/andrewcaires/npm.git"
15
+ },
16
+ "keywords": [
17
+ "app",
18
+ "api",
19
+ "auth",
20
+ "express",
21
+ "jsonwebtoken",
22
+ "permissions",
23
+ "routes",
24
+ "sequelize",
25
+ "token",
26
+ "validations",
27
+ "websocket",
28
+ "web"
29
+ ],
30
+ "author": "Andrew Caires",
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/andrewcaires/npm/issues"
34
+ },
35
+ "homepage": "https://github.com/andrewcaires/npm/tree/main/api#readme",
36
+ "dependencies": {
37
+ "@andrewcaires/utils.js": "^0.1.0",
38
+ "cors": "^2.8.5",
39
+ "crypto-js": "^4.1.1",
40
+ "dotenv": "^16.0.1",
41
+ "express": "^4.18.1",
42
+ "fs": "^0.0.1-security",
43
+ "joi": "^17.6.0",
44
+ "jsonwebtoken": "^8.5.1",
45
+ "mariadb": "^3.0.0",
46
+ "mime-types": "^2.1.35",
47
+ "multer": "^1.4.4",
48
+ "mysql2": "^2.3.3",
49
+ "pg": "^8.7.3",
50
+ "pg-hstore": "^2.3.4",
51
+ "sequelize": "^6.19.1",
52
+ "ws": "^8.6.0"
53
+ },
54
+ "devDependencies": {
55
+ "@rollup/plugin-typescript": "^8.3.2",
56
+ "@types/cors": "^2.8.12",
57
+ "@types/crypto-js": "^4.1.1",
58
+ "@types/express": "^4.17.13",
59
+ "@types/jsonwebtoken": "^8.5.8",
60
+ "@types/mime-types": "^2.1.1",
61
+ "@types/multer": "^1.4.7",
62
+ "@types/node": "^17.0.34",
63
+ "@types/ws": "^8.5.3",
64
+ "@typescript-eslint/eslint-plugin": "^5.24.0",
65
+ "@typescript-eslint/parser": "^5.24.0",
66
+ "eslint": "^8.15.0",
67
+ "rollup": "^2.73.0",
68
+ "rollup-plugin-dts": "^4.2.1",
69
+ "rollup-plugin-terser": "^7.0.2",
70
+ "ts-node": "^10.7.0",
71
+ "typescript": "^4.6.4"
72
+ }
73
+ }