@churchapps/apihelper 0.5.2 → 0.5.4
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/dist/auth/CustomAuthProvider.js +1 -1
- package/dist/controllers/CustomBaseController.d.ts.map +1 -1
- package/dist/controllers/CustomBaseController.js.map +1 -1
- package/dist/controllers/ErrorController.d.ts.map +1 -1
- package/dist/controllers/ErrorController.js +5 -5
- package/dist/controllers/ErrorController.js.map +1 -1
- package/dist/helpers/AwsHelper.d.ts.map +1 -1
- package/dist/helpers/AwsHelper.js.map +1 -1
- package/dist/helpers/BasePermissions.d.ts.map +1 -1
- package/dist/helpers/BasePermissions.js +3 -9
- package/dist/helpers/BasePermissions.js.map +1 -1
- package/dist/helpers/DB.d.ts.map +1 -1
- package/dist/helpers/DB.js +0 -1
- package/dist/helpers/DB.js.map +1 -1
- package/dist/helpers/DBCreator.d.ts.map +1 -1
- package/dist/helpers/DBCreator.js.map +1 -1
- package/dist/helpers/EmailHelper.d.ts +1 -1
- package/dist/helpers/EmailHelper.d.ts.map +1 -1
- package/dist/helpers/EmailHelper.js +12 -11
- package/dist/helpers/EmailHelper.js.map +1 -1
- package/dist/helpers/EncryptionHelper.d.ts.map +1 -1
- package/dist/helpers/EncryptionHelper.js +4 -4
- package/dist/helpers/EncryptionHelper.js.map +1 -1
- package/dist/helpers/EnvironmentBase.d.ts.map +1 -1
- package/dist/helpers/EnvironmentBase.js.map +1 -1
- package/dist/helpers/FileStorageHelper.d.ts.map +1 -1
- package/dist/helpers/FileStorageHelper.js.map +1 -1
- package/dist/helpers/LoggingHelper.d.ts.map +1 -1
- package/dist/helpers/LoggingHelper.js +1 -1
- package/dist/helpers/LoggingHelper.js.map +1 -1
- package/dist/helpers/MySqlHelper.d.ts.map +1 -1
- package/dist/helpers/MySqlHelper.js.map +1 -1
- package/dist/helpers/OmitEmpty.d.ts.map +1 -1
- package/dist/helpers/OmitEmpty.js +1 -1
- package/dist/helpers/OmitEmpty.js.map +1 -1
- package/dist/helpers/Pool.d.ts.map +1 -1
- package/dist/helpers/Pool.js +2 -2
- package/dist/helpers/Pool.js.map +1 -1
- package/dist/helpers/SlugHelper.d.ts.map +1 -1
- package/dist/helpers/SlugHelper.js +5 -3
- package/dist/helpers/SlugHelper.js.map +1 -1
- package/package.json +7 -9
- package/.github/FUNDING.yml +0 -1
- package/.prettierrc.json +0 -13
- package/CLAUDE.md +0 -110
- package/eslint.config.mjs +0 -40
- package/scripts/copy-assets.js +0 -35
- package/src/auth/AuthenticatedUser.ts +0 -44
- package/src/auth/CustomAuthProvider.ts +0 -25
- package/src/auth/Principal.ts +0 -38
- package/src/auth/index.ts +0 -3
- package/src/controllers/CustomBaseController.ts +0 -62
- package/src/controllers/ErrorController.ts +0 -33
- package/src/controllers/index.ts +0 -2
- package/src/helpers/AwsHelper.ts +0 -129
- package/src/helpers/BasePermissions.ts +0 -15
- package/src/helpers/DB.ts +0 -41
- package/src/helpers/DBCreator.ts +0 -39
- package/src/helpers/EmailHelper.ts +0 -99
- package/src/helpers/EncryptionHelper.ts +0 -25
- package/src/helpers/EnvironmentBase.ts +0 -36
- package/src/helpers/FileStorageHelper.ts +0 -71
- package/src/helpers/Interfaces.ts +0 -2
- package/src/helpers/LoggingHelper.ts +0 -71
- package/src/helpers/MySqlHelper.ts +0 -5
- package/src/helpers/OmitEmpty.ts +0 -128
- package/src/helpers/Pool.ts +0 -56
- package/src/helpers/SlugHelper.ts +0 -37
- package/src/helpers/index.ts +0 -17
- package/src/index.ts +0 -3
- package/src/models/ErrorLog.ts +0 -7
- package/src/models/index.ts +0 -1
- package/src/tools/templates/ChurchEmailTemplate.html +0 -383
- package/src/tools/templates/EmailTemplate.html +0 -424
- package/tsconfig.json +0 -26
package/CLAUDE.md
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Project Overview
|
|
6
|
-
|
|
7
|
-
This is the `@churchapps/apihelper` package - a Node.js/Express.js server-side utility library that provides common functionality for ChurchApps API projects. It serves as a foundation layer for building REST APIs with authentication, database access, AWS integration, and error handling.
|
|
8
|
-
|
|
9
|
-
## Key Dependencies
|
|
10
|
-
|
|
11
|
-
- **Core**: Express.js with Inversify for dependency injection
|
|
12
|
-
- **AWS SDK v3**: S3, SES, CloudWatch Logs, SSM Parameter Store
|
|
13
|
-
- **Database**: MySQL2 with connection pooling
|
|
14
|
-
- **Authentication**: JSON Web Tokens (JWT)
|
|
15
|
-
- **Depends on**: `@churchapps/helpers` (base utilities package)
|
|
16
|
-
|
|
17
|
-
## Common Development Commands
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
# Build the package
|
|
21
|
-
npm run clean # Remove dist folder
|
|
22
|
-
npm run tsc # TypeScript compilation only
|
|
23
|
-
npm run build # Full build (clean + tsc + copy-assets)
|
|
24
|
-
|
|
25
|
-
# Code quality
|
|
26
|
-
npm run lint # Run ESLint
|
|
27
|
-
npm run lint:fix # Auto-fix lint issues
|
|
28
|
-
npm run format # Format code with Prettier
|
|
29
|
-
npm run format:check # Check formatting
|
|
30
|
-
|
|
31
|
-
# Local development
|
|
32
|
-
npm run build
|
|
33
|
-
npm link
|
|
34
|
-
|
|
35
|
-
# In consuming project
|
|
36
|
-
npm link @churchapps/apihelper
|
|
37
|
-
|
|
38
|
-
# Publishing
|
|
39
|
-
npm run build
|
|
40
|
-
npm publish --access=public
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Architecture Overview
|
|
44
|
-
|
|
45
|
-
### Core Components
|
|
46
|
-
|
|
47
|
-
1. **Authentication System** (`/auth`)
|
|
48
|
-
- `CustomAuthProvider`: Inversify-based authentication provider
|
|
49
|
-
- `AuthenticatedUser`: User session management with JWT
|
|
50
|
-
- `Principal`: Security principal for authorization checks
|
|
51
|
-
|
|
52
|
-
2. **Base Controllers** (`/controllers`)
|
|
53
|
-
- `CustomBaseController`: Extends Inversify's BaseHttpController
|
|
54
|
-
- Provides error handling (`internalServerError`, `notFound`, `conflict`)
|
|
55
|
-
- Authentication helpers (`loadUser`, `checkAccess`)
|
|
56
|
-
- Request utilities (`getNumber`, `getBoolean`, `getDate`)
|
|
57
|
-
- `ErrorController`: Centralized error logging to database
|
|
58
|
-
|
|
59
|
-
3. **AWS Integration** (`/helpers`)
|
|
60
|
-
- `AwsHelper`: S3 operations, SSM parameter reading, presigned URLs
|
|
61
|
-
- `FileStorageHelper`: Abstraction for file storage operations
|
|
62
|
-
- `EmailHelper`: Dual support for SMTP and AWS SES
|
|
63
|
-
- `LoggingHelper`: Winston logging with CloudWatch integration
|
|
64
|
-
|
|
65
|
-
4. **Database Layer** (`/helpers`)
|
|
66
|
-
- `DB`: Promise-based MySQL query wrapper
|
|
67
|
-
- `Pool`: MySQL connection pooling management
|
|
68
|
-
- `MySqlHelper`: MySQL-specific utilities
|
|
69
|
-
- `DBCreator`: Database schema creation utilities
|
|
70
|
-
|
|
71
|
-
### Key Technical Details
|
|
72
|
-
|
|
73
|
-
- **Inversify Decorators**: Controllers use `@controller`, `@httpGet`, etc.
|
|
74
|
-
- **Environment Configuration**: `EnvironmentBase` class handles env vars with SSM fallback
|
|
75
|
-
- **Email Templates**: HTML templates copied to dist/templates during build
|
|
76
|
-
- **TypeScript**: Targets ES2020 with decorators enabled, declaration files generated
|
|
77
|
-
|
|
78
|
-
### Important Notes
|
|
79
|
-
|
|
80
|
-
- **Build Process Issue**: The copy-assets script references `src/apiBase/tools/templates/*` but the actual path is `src/tools/templates/*`
|
|
81
|
-
- **No Test Suite**: Currently no tests configured in package.json
|
|
82
|
-
- **Environment Variables**: Expects `CONNECTION_STRING`, `ENCRYPTION_KEY`, `JWT_SECRET`, `SMTP_*` configs
|
|
83
|
-
|
|
84
|
-
## Usage Pattern
|
|
85
|
-
|
|
86
|
-
This package is designed to be extended by API projects:
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
// Example controller
|
|
90
|
-
import { controller, httpGet } from "inversify-express-utils";
|
|
91
|
-
import { CustomBaseController } from "@churchapps/apihelper";
|
|
92
|
-
|
|
93
|
-
@controller("/api/example")
|
|
94
|
-
export class ExampleController extends CustomBaseController {
|
|
95
|
-
@httpGet("/")
|
|
96
|
-
public async getAll(req: express.Request, res: express.Response): Promise<any> {
|
|
97
|
-
return this.actionWrapper(req, res, async () => {
|
|
98
|
-
// Implementation
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Development Workflow
|
|
105
|
-
|
|
106
|
-
1. Controllers extend `CustomBaseController` for built-in error handling and utilities
|
|
107
|
-
2. Use `EnvironmentBase` for configuration management
|
|
108
|
-
3. Database queries through `DB.query()` or `DB.queryOne()`
|
|
109
|
-
4. AWS operations via `AwsHelper` static methods
|
|
110
|
-
5. Logging through `LoggingHelper.getInstance()` singleton
|
package/eslint.config.mjs
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import eslint from "@eslint/js";
|
|
2
|
-
import tseslint from "@typescript-eslint/eslint-plugin";
|
|
3
|
-
import parser from "@typescript-eslint/parser";
|
|
4
|
-
|
|
5
|
-
export default [
|
|
6
|
-
eslint.configs.recommended,
|
|
7
|
-
{
|
|
8
|
-
files: ["**/*.ts"],
|
|
9
|
-
languageOptions: {
|
|
10
|
-
parser: parser,
|
|
11
|
-
parserOptions: {
|
|
12
|
-
ecmaVersion: 2020,
|
|
13
|
-
sourceType: "module"
|
|
14
|
-
},
|
|
15
|
-
globals: {
|
|
16
|
-
console: "readonly",
|
|
17
|
-
process: "readonly",
|
|
18
|
-
Buffer: "readonly",
|
|
19
|
-
__dirname: "readonly",
|
|
20
|
-
global: "readonly"
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
plugins: {
|
|
24
|
-
"@typescript-eslint": tseslint
|
|
25
|
-
},
|
|
26
|
-
rules: {
|
|
27
|
-
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
|
|
28
|
-
"no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
|
|
29
|
-
"@typescript-eslint/no-explicit-any": "warn",
|
|
30
|
-
"no-console": "off",
|
|
31
|
-
"no-useless-catch": "off",
|
|
32
|
-
"comma-dangle": ["error", "never"],
|
|
33
|
-
"object-curly-spacing": ["error", "always"],
|
|
34
|
-
"array-bracket-spacing": ["error", "never"]
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
ignores: ["dist/**/*", "node_modules/**/*"]
|
|
39
|
-
}
|
|
40
|
-
];
|
package/scripts/copy-assets.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
|
|
5
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
-
const __dirname = path.dirname(__filename);
|
|
7
|
-
|
|
8
|
-
async function copyAssets() {
|
|
9
|
-
try {
|
|
10
|
-
const srcDir = path.join(__dirname, '..', 'src', 'tools', 'templates');
|
|
11
|
-
const destDir = path.join(__dirname, '..', 'dist', 'templates');
|
|
12
|
-
|
|
13
|
-
// Ensure destination directory exists
|
|
14
|
-
await fs.promises.mkdir(destDir, { recursive: true });
|
|
15
|
-
|
|
16
|
-
// Copy all files from src/tools/templates to dist/templates
|
|
17
|
-
const files = await fs.promises.readdir(srcDir);
|
|
18
|
-
for (const file of files) {
|
|
19
|
-
const srcPath = path.join(srcDir, file);
|
|
20
|
-
const destPath = path.join(destDir, file);
|
|
21
|
-
const stat = await fs.promises.stat(srcPath);
|
|
22
|
-
|
|
23
|
-
if (stat.isFile()) {
|
|
24
|
-
await fs.promises.copyFile(srcPath, destPath);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
console.log('Assets copied successfully');
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.error('Error copying assets:', error);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
copyAssets();
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Principal } from "./Principal.js";
|
|
2
|
-
import { IPermission } from "../helpers/Interfaces.js";
|
|
3
|
-
|
|
4
|
-
export class AuthenticatedUser {
|
|
5
|
-
public id: string;
|
|
6
|
-
public churchId: string;
|
|
7
|
-
public email: string;
|
|
8
|
-
public apiName: string;
|
|
9
|
-
public permissions: string[];
|
|
10
|
-
public personId: string;
|
|
11
|
-
public firstName: string;
|
|
12
|
-
public lastName: string;
|
|
13
|
-
public membershipStatus?: string;
|
|
14
|
-
public groupIds?: string[];
|
|
15
|
-
public leaderGroupIds?: string[];
|
|
16
|
-
public jwt: string;
|
|
17
|
-
|
|
18
|
-
public constructor(principal: Principal) {
|
|
19
|
-
this.jwt = principal.details.jwt || "";
|
|
20
|
-
this.id = principal.details.id || "";
|
|
21
|
-
this.churchId = principal.details.churchId || "";
|
|
22
|
-
this.permissions = principal.details.permissions || [];
|
|
23
|
-
this.apiName = principal.details.apiName || "";
|
|
24
|
-
this.email = principal.details.email || "";
|
|
25
|
-
this.personId = principal.details.personId || "";
|
|
26
|
-
this.firstName = principal.details.firstName || "";
|
|
27
|
-
this.lastName = principal.details.lastName || "";
|
|
28
|
-
this.membershipStatus = principal.details.membershipStatus;
|
|
29
|
-
this.groupIds = principal.details.groupIds || [];
|
|
30
|
-
this.leaderGroupIds = principal.details.leaderGroupIds || [];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public checkAccess(permission: IPermission) {
|
|
34
|
-
const key = (permission.apiName)
|
|
35
|
-
? permission.apiName + "_" + permission.contentType + "__" + permission.action
|
|
36
|
-
: permission.contentType + "__" + permission.action;
|
|
37
|
-
|
|
38
|
-
let result = false;
|
|
39
|
-
this.permissions?.forEach((p: string) => {
|
|
40
|
-
if (p === key) result = true;
|
|
41
|
-
});
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { injectable } from "inversify";
|
|
2
|
-
import { interfaces } from "inversify-express-utils";
|
|
3
|
-
import express from "express";
|
|
4
|
-
import jwt from "jsonwebtoken";
|
|
5
|
-
import { Principal } from "./Principal.js";
|
|
6
|
-
import { EnvironmentBase } from "../helpers/EnvironmentBase.js";
|
|
7
|
-
|
|
8
|
-
@injectable()
|
|
9
|
-
export class CustomAuthProvider implements interfaces.AuthProvider {
|
|
10
|
-
// public async getUser(req: express.Request, res: express.Response, next: express.NextFunction): Promise<interfaces.Principal> {
|
|
11
|
-
public async getUser(req: express.Request, _res: express.Response, _next: express.NextFunction): Promise<Principal> {
|
|
12
|
-
const authHeader = req.headers.authorization;
|
|
13
|
-
if (authHeader) {
|
|
14
|
-
const token = authHeader.split(" ")[1];
|
|
15
|
-
if (!token) return null;
|
|
16
|
-
const decoded = jwt.verify(token, EnvironmentBase.jwtSecret);
|
|
17
|
-
|
|
18
|
-
const result = decoded ? new Principal(typeof decoded === 'object' && decoded !== null ? decoded as Record<string, unknown> : {}) : null;
|
|
19
|
-
if (result) result.details.jwt = token;
|
|
20
|
-
return result;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
}
|
package/src/auth/Principal.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { interfaces } from "inversify-express-utils";
|
|
2
|
-
|
|
3
|
-
export interface PrincipalDetails {
|
|
4
|
-
jwt?: string;
|
|
5
|
-
id?: string;
|
|
6
|
-
churchId?: string;
|
|
7
|
-
permissions?: string[];
|
|
8
|
-
apiName?: string;
|
|
9
|
-
email?: string;
|
|
10
|
-
personId?: string;
|
|
11
|
-
firstName?: string;
|
|
12
|
-
lastName?: string;
|
|
13
|
-
membershipStatus?: string;
|
|
14
|
-
groupIds?: string[];
|
|
15
|
-
leaderGroupIds?: string[];
|
|
16
|
-
[key: string]: unknown;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class Principal implements interfaces.Principal {
|
|
20
|
-
public details: PrincipalDetails;
|
|
21
|
-
|
|
22
|
-
public constructor(details: PrincipalDetails) {
|
|
23
|
-
this.details = details;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public isAuthenticated(): Promise<boolean> {
|
|
27
|
-
return Promise.resolve(true);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public isResourceOwner(resourceId: string | number): Promise<boolean> {
|
|
31
|
-
return Promise.resolve(resourceId === 1111);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public isInRole(role: string): Promise<boolean> {
|
|
35
|
-
return Promise.resolve(role === "admin");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
}
|
package/src/auth/index.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { BaseHttpController } from "inversify-express-utils";
|
|
2
|
-
import express from "express";
|
|
3
|
-
import { LoggingHelper } from "../helpers/LoggingHelper.js";
|
|
4
|
-
import { AuthenticatedUser, Principal } from "../auth/index.js";
|
|
5
|
-
|
|
6
|
-
export class CustomBaseController extends BaseHttpController {
|
|
7
|
-
|
|
8
|
-
public logger: LoggingHelper;
|
|
9
|
-
|
|
10
|
-
constructor() {
|
|
11
|
-
super()
|
|
12
|
-
this.logger = LoggingHelper.getCurrent();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
public error(errors: string[]) {
|
|
16
|
-
return this.json({ errors }, 500);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
public denyAccess(errors: string[]) {
|
|
20
|
-
return this.json({ errors }, 401);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
public authUser(): AuthenticatedUser {
|
|
24
|
-
if (this.httpContext.user === null) return new AuthenticatedUser(new Principal({}));
|
|
25
|
-
else return new AuthenticatedUser(this.httpContext.user as Principal);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public include(req: express.Request, item: string) {
|
|
29
|
-
let result = false;
|
|
30
|
-
if (req.query.include !== undefined) {
|
|
31
|
-
const value: string = req.query.include as string;
|
|
32
|
-
const items = value.split(",");
|
|
33
|
-
if (items.indexOf(item) > -1) result = true;
|
|
34
|
-
}
|
|
35
|
-
return result;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public async actionWrapper(_req: express.Request, _res: express.Response, fetchFunction: (_au: AuthenticatedUser) => unknown): Promise<unknown> {
|
|
39
|
-
try {
|
|
40
|
-
const result = await fetchFunction(this.authUser());
|
|
41
|
-
await this.logger.flush();
|
|
42
|
-
return result;
|
|
43
|
-
} catch (e: unknown) {
|
|
44
|
-
// Since logger.error now throws, just re-throw the error
|
|
45
|
-
throw e;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
public async actionWrapperAnon(_req: express.Request, _res: express.Response, fetchFunction: () => unknown): Promise<unknown> {
|
|
50
|
-
try {
|
|
51
|
-
const result = await fetchFunction();
|
|
52
|
-
await this.logger.flush();
|
|
53
|
-
return result;
|
|
54
|
-
} catch (e: unknown) {
|
|
55
|
-
// Since logger.error now throws, just re-throw the error
|
|
56
|
-
throw e;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { controller, httpPost } from "inversify-express-utils";
|
|
2
|
-
import express from "express";
|
|
3
|
-
import { CustomBaseController } from "./CustomBaseController.js";
|
|
4
|
-
import { ErrorLog } from "../models/index.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@controller("/errors")
|
|
8
|
-
export class ErrorController extends CustomBaseController {
|
|
9
|
-
|
|
10
|
-
@httpPost("/")
|
|
11
|
-
public async save(req: express.Request<{}, {}, ErrorLog[]>, _res: express.Response): Promise<ErrorLog[]> {
|
|
12
|
-
// try {
|
|
13
|
-
/*
|
|
14
|
-
try {
|
|
15
|
-
au = this.authUser();
|
|
16
|
-
} catch (e) {
|
|
17
|
-
this.logger.log(req.body[0].application, "info", e);
|
|
18
|
-
}*/
|
|
19
|
-
req.body.forEach(error => {
|
|
20
|
-
let fullMessage = error.message;
|
|
21
|
-
if (error.additionalDetails !== undefined) fullMessage += "\n" + error.additionalDetails;
|
|
22
|
-
// if (au !== null) fullMessage += "\nUser: " + au.id + " Church: " + au.churchId;
|
|
23
|
-
this.logger.log(error.application, error.level, fullMessage);
|
|
24
|
-
});
|
|
25
|
-
await this.logger.flush();
|
|
26
|
-
return req.body;
|
|
27
|
-
// } catch (e) {
|
|
28
|
-
// console.log(e)
|
|
29
|
-
// };
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
}
|
package/src/controllers/index.ts
DELETED
package/src/helpers/AwsHelper.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { S3Client, GetObjectCommand, PutObjectCommand, DeleteObjectCommand, CopyObjectCommand, ListObjectsV2Command, ListObjectsV2Output } from "@aws-sdk/client-s3";
|
|
2
|
-
import { createPresignedPost } from "@aws-sdk/s3-presigned-post";
|
|
3
|
-
import { EnvironmentBase } from "./EnvironmentBase.js";
|
|
4
|
-
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
|
|
5
|
-
|
|
6
|
-
export class AwsHelper {
|
|
7
|
-
|
|
8
|
-
//Pulls from AWS SSM Parameter Store
|
|
9
|
-
static async readParameter(parameterName: string): Promise<string> {
|
|
10
|
-
let result = "";
|
|
11
|
-
try {
|
|
12
|
-
const ssm = new SSMClient({ region: "us-east-2" });
|
|
13
|
-
const params = { Name: parameterName, WithDecryption: true };
|
|
14
|
-
const command = new GetParameterCommand(params);
|
|
15
|
-
const response = await ssm.send(command);
|
|
16
|
-
result = response?.Parameter?.Value || "";
|
|
17
|
-
} catch {
|
|
18
|
-
result = "";
|
|
19
|
-
}
|
|
20
|
-
return result;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
private static _client: S3Client;
|
|
25
|
-
|
|
26
|
-
private static getClient(): S3Client {
|
|
27
|
-
if (!this._client) {
|
|
28
|
-
this._client = new S3Client({});
|
|
29
|
-
}
|
|
30
|
-
return this._client;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static async S3PresignedUrl(key: string): Promise<{url: string, fields: Record<string, string>, key: string}> {
|
|
34
|
-
if (key.startsWith("/")) key = key.substring(1);
|
|
35
|
-
const { url, fields } = await createPresignedPost(this.getClient(), {
|
|
36
|
-
Bucket: EnvironmentBase.s3Bucket,
|
|
37
|
-
Key: key,
|
|
38
|
-
Conditions: [
|
|
39
|
-
["starts-with", "$Content-Type", ""],
|
|
40
|
-
{ acl: "public-read" }
|
|
41
|
-
],
|
|
42
|
-
Expires: 3600 // 1 hour
|
|
43
|
-
});
|
|
44
|
-
return { url, fields, key };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
static async S3Upload(key: string, contentType: string, contents: Buffer): Promise<void> {
|
|
48
|
-
if (key.startsWith("/")) key = key.substring(1);
|
|
49
|
-
const command = new PutObjectCommand({
|
|
50
|
-
Bucket: EnvironmentBase.s3Bucket,
|
|
51
|
-
Key: key,
|
|
52
|
-
Body: contents,
|
|
53
|
-
ACL: "public-read",
|
|
54
|
-
ContentType: contentType
|
|
55
|
-
});
|
|
56
|
-
await this.getClient().send(command);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
static async S3Remove(key: string): Promise<void> {
|
|
60
|
-
if (key.startsWith("/")) key = key.substring(1);
|
|
61
|
-
const command = new DeleteObjectCommand({
|
|
62
|
-
Bucket: EnvironmentBase.s3Bucket,
|
|
63
|
-
Key: key
|
|
64
|
-
});
|
|
65
|
-
await this.getClient().send(command);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
static async S3Rename(oldKey: string, newKey: string): Promise<void> {
|
|
69
|
-
console.log(`Renaming: ${oldKey} to ${newKey}`);
|
|
70
|
-
await this.S3Copy(oldKey, newKey);
|
|
71
|
-
await this.S3Remove(oldKey);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
static S3Move(oldKey: string, newKey: string): Promise<void> {
|
|
75
|
-
return this.S3Rename(oldKey, newKey);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
static async S3Copy(oldKey: string, newKey: string): Promise<void> {
|
|
79
|
-
const command = new CopyObjectCommand({
|
|
80
|
-
Bucket: EnvironmentBase.s3Bucket,
|
|
81
|
-
CopySource: `/${EnvironmentBase.s3Bucket}/${oldKey}`,
|
|
82
|
-
Key: newKey,
|
|
83
|
-
ACL: "public-read"
|
|
84
|
-
});
|
|
85
|
-
await this.getClient().send(command);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
static async S3List(path: string): Promise<string[]> {
|
|
89
|
-
return this.S3ListMultiPage(this.getClient(), EnvironmentBase.s3Bucket, path);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
static async S3ListMultiPage(s3:S3Client, bucket:string, path: string): Promise<string[]> {
|
|
93
|
-
const result: string[] = [];
|
|
94
|
-
let continuationToken: string | undefined;
|
|
95
|
-
|
|
96
|
-
do {
|
|
97
|
-
const { Contents, NextContinuationToken } = await this.S3ListManual(s3, bucket, path, continuationToken);
|
|
98
|
-
result.push(...(Contents?.map((item) => item.Key).filter((key): key is string => key !== undefined) || []));
|
|
99
|
-
continuationToken = NextContinuationToken;
|
|
100
|
-
} while (continuationToken);
|
|
101
|
-
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
private static async S3ListManual(s3:S3Client, bucket:string, path: string, continuationToken?: string): Promise<ListObjectsV2Output> {
|
|
106
|
-
const command = new ListObjectsV2Command({
|
|
107
|
-
Bucket: bucket,
|
|
108
|
-
Prefix: path,
|
|
109
|
-
MaxKeys: 10000,
|
|
110
|
-
ContinuationToken: continuationToken
|
|
111
|
-
});
|
|
112
|
-
return s3.send(command);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
static async S3Read(key: string): Promise<string | null> {
|
|
116
|
-
try {
|
|
117
|
-
const command = new GetObjectCommand({
|
|
118
|
-
Bucket: EnvironmentBase.s3Bucket,
|
|
119
|
-
Key: key
|
|
120
|
-
});
|
|
121
|
-
const response = await this.getClient().send(command);
|
|
122
|
-
return await response.Body?.transformToString();
|
|
123
|
-
} catch (error) {
|
|
124
|
-
console.error("Error reading from S3:", error);
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export class BasePermissions {
|
|
2
|
-
static forms = {
|
|
3
|
-
admin: { contentType: "Forms", action: "Admin" },
|
|
4
|
-
edit: { contentType: "Forms", action: "Edit" }
|
|
5
|
-
};
|
|
6
|
-
static links = {
|
|
7
|
-
edit: { contentType: "Links", action: "Edit" }
|
|
8
|
-
};
|
|
9
|
-
static pages = {
|
|
10
|
-
edit: { contentType: "Pages", action: "Edit" }
|
|
11
|
-
};
|
|
12
|
-
static settings = {
|
|
13
|
-
edit: { contentType: "Settings", action: "Edit" }
|
|
14
|
-
};
|
|
15
|
-
}
|
package/src/helpers/DB.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { Pool } from "./Pool.js";
|
|
2
|
-
import { PoolConnection, QueryError } from "mysql2";
|
|
3
|
-
import { LoggingHelper } from "./LoggingHelper.js";
|
|
4
|
-
|
|
5
|
-
export class DB {
|
|
6
|
-
|
|
7
|
-
// wraps in promise
|
|
8
|
-
static async getConnection() {
|
|
9
|
-
const promise: Promise<PoolConnection> = new Promise((resolve, reject) => {
|
|
10
|
-
Pool.current.getConnection((ex: QueryError | null, conn: PoolConnection) => { if (ex) reject(ex); else resolve(conn); });
|
|
11
|
-
});;
|
|
12
|
-
const connection: PoolConnection = await promise;
|
|
13
|
-
return connection;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// wraps in promise
|
|
17
|
-
static async getQuery(connection: PoolConnection, sql: string, params: unknown[]) {
|
|
18
|
-
const promise: Promise<unknown> = new Promise((resolve, reject) => {
|
|
19
|
-
connection.query(sql, params, async (ex: QueryError | null, rows: unknown) => {
|
|
20
|
-
if (ex) { LoggingHelper.getCurrent().error(ex); reject(ex); }
|
|
21
|
-
else { resolve(rows); }
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
const query: unknown = await promise;
|
|
25
|
-
return query;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public static async query(sql: string, params: unknown[]) {
|
|
29
|
-
let result: unknown = null;
|
|
30
|
-
const connection = await this.getConnection();
|
|
31
|
-
try { result = await this.getQuery(connection, sql, params); }
|
|
32
|
-
catch (ex: unknown) { LoggingHelper.getCurrent().error(ex as Error); }
|
|
33
|
-
finally { connection.release(); }
|
|
34
|
-
return result;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public static async queryOne(sql: string, params: unknown[]) {
|
|
38
|
-
const result = await this.query(sql, params) as unknown[];
|
|
39
|
-
return result?.length > 0 ? result[0] : null;
|
|
40
|
-
}
|
|
41
|
-
}
|
package/src/helpers/DBCreator.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import dotenv from "dotenv";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import { DB } from "./DB.js";
|
|
4
|
-
|
|
5
|
-
export class DBCreator {
|
|
6
|
-
|
|
7
|
-
private static tables: { title: string, file: string }[] = [
|
|
8
|
-
{ title: "Links", file: "links.mysql" },
|
|
9
|
-
{ title: "Pages", file: "pages.mysql" },
|
|
10
|
-
{ title: "Settings", file: "settings.mysql" }
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
public static async init(selectedTables: string[]) {
|
|
14
|
-
dotenv.config();
|
|
15
|
-
|
|
16
|
-
const todo: { title: string, file: string }[] = [];
|
|
17
|
-
selectedTables.forEach(async st => {
|
|
18
|
-
this.tables.forEach(async t => {
|
|
19
|
-
if (t.title === st) todo.push(t);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
for (const td of todo) await this.runScript(td.title, "./src/tools/dbScripts/" + td.file, false);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public static async runScript(title: string, file: string, customDelimeter: boolean) {
|
|
28
|
-
console.log("Creating '" + title + "'");
|
|
29
|
-
const sql = fs.readFileSync(file, "utf-8");
|
|
30
|
-
let del = /;(?=END)\s*$|;(?!\nEND)\s*$/gm;
|
|
31
|
-
if (customDelimeter) {
|
|
32
|
-
del = /\$\$$/gm;
|
|
33
|
-
}
|
|
34
|
-
const statements = sql.split(del);
|
|
35
|
-
for (const statement of statements) if (statement.length > 3) await DB.query(statement, []);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|