mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
init
This commit is contained in:
8
cloud_actions/firebase.json
Normal file
8
cloud_actions/firebase.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"functions": {
|
||||
"predeploy": [
|
||||
"npm --prefix \"$RESOURCE_DIR\" run lint",
|
||||
"npm --prefix \"$RESOURCE_DIR\" run build"
|
||||
]
|
||||
}
|
||||
}
|
||||
70
cloud_actions/functions/.eslintrc.js
Normal file
70
cloud_actions/functions/.eslintrc.js
Normal file
@@ -0,0 +1,70 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: "tsconfig.json",
|
||||
sourceType: "module",
|
||||
},
|
||||
plugins: [
|
||||
"@typescript-eslint",
|
||||
"import",
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/adjacent-overload-signatures": "error",
|
||||
"@typescript-eslint/no-empty-function": "error",
|
||||
"@typescript-eslint/no-empty-interface": "warn",
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-namespace": "error",
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||
"@typescript-eslint/prefer-for-of": "warn",
|
||||
"@typescript-eslint/triple-slash-reference": "error",
|
||||
"@typescript-eslint/unified-signatures": "warn",
|
||||
"comma-dangle": "warn",
|
||||
"constructor-super": "error",
|
||||
eqeqeq: ["warn", "always"],
|
||||
"import/no-deprecated": "warn",
|
||||
"import/no-extraneous-dependencies": "error",
|
||||
"import/no-unassigned-import": "warn",
|
||||
"no-cond-assign": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
"no-empty": [
|
||||
"error",
|
||||
{
|
||||
allowEmptyCatch: true,
|
||||
},
|
||||
],
|
||||
"no-invalid-this": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-param-reassign": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-sequences": "error",
|
||||
"no-shadow": [
|
||||
"error",
|
||||
{
|
||||
hoist: "all",
|
||||
},
|
||||
],
|
||||
"no-throw-literal": "error",
|
||||
"no-unsafe-finally": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-var": "warn",
|
||||
"no-void": "error",
|
||||
"prefer-const": "warn",
|
||||
},
|
||||
settings: {
|
||||
jsdoc: {
|
||||
tagNamePreference: {
|
||||
returns: "return",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
12
cloud_actions/functions/.gitignore
vendored
Normal file
12
cloud_actions/functions/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Compiled JavaScript files
|
||||
**/*.js
|
||||
**/*.js.map
|
||||
|
||||
# Except the ESLint config file
|
||||
!.eslintrc.js
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Node.js dependency directory
|
||||
node_modules/
|
||||
31
cloud_actions/functions/package.json
Normal file
31
cloud_actions/functions/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "functions",
|
||||
"scripts": {
|
||||
"lint": "eslint \"src/**/*\"",
|
||||
"build": "tsc",
|
||||
"serve": "npm run build && firebase emulators:start --only functions",
|
||||
"shell": "npm run build && firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log"
|
||||
},
|
||||
"engines": {
|
||||
"node": "10"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"firebase-admin": "^9.2.0",
|
||||
"firebase-functions": "^3.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^3.9.1",
|
||||
"@typescript-eslint/parser": "^3.8.0",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"firebase-functions-test": "^0.2.0",
|
||||
"typescript": "^3.8.0",
|
||||
"husky": "^4.2.5",
|
||||
"prettier": "^2.1.1"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
13
cloud_actions/functions/src/config.ts
Normal file
13
cloud_actions/functions/src/config.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// Initialize Firebase Admin
|
||||
import * as functions from "firebase-functions";
|
||||
import * as admin from "firebase-admin";
|
||||
admin.initializeApp();
|
||||
|
||||
// Initialize Cloud Firestore Database
|
||||
export const db = admin.firestore();
|
||||
// Initialize Auth
|
||||
export const auth = admin.auth();
|
||||
|
||||
const settings = { timestampsInSnapshots: true };
|
||||
db.settings(settings);
|
||||
export const env = functions.config();
|
||||
126
cloud_actions/functions/src/index.ts
Normal file
126
cloud_actions/functions/src/index.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import * as functions from "firebase-functions";
|
||||
import { db, auth } from "./config";
|
||||
import * as admin from "firebase-admin";
|
||||
import utilFns from "./utils";
|
||||
type ActionData = {
|
||||
ref: {
|
||||
id: string;
|
||||
path: string;
|
||||
parentId: string;
|
||||
tablePath: string;
|
||||
};
|
||||
schemaDocPath?: string;
|
||||
row: any;
|
||||
column: any;
|
||||
action: "run" | "redo" | "undo";
|
||||
actionParams: any;
|
||||
};
|
||||
// import {
|
||||
// makeId,
|
||||
// hasGoogleMailServer,
|
||||
// hasMissingFields,
|
||||
// } from "../utils";
|
||||
|
||||
import { hasAnyRole } from "./utils/auth";
|
||||
|
||||
const missingFieldsReducer = (data: any) => (acc: string[], curr: string) => {
|
||||
if (data[curr] === undefined) {
|
||||
return [...acc, curr];
|
||||
} else return acc;
|
||||
};
|
||||
|
||||
const generateSchemaDocPath = (tablePath:string) => {
|
||||
const pathComponents = tablePath.split("/");
|
||||
return `_FIRETABLE_/settings/${
|
||||
pathComponents[1] === "table" ? "schema" : "groupSchema"
|
||||
}/${pathComponents[2]}`;
|
||||
};
|
||||
const serverTimestamp = admin.firestore.FieldValue.serverTimestamp;
|
||||
|
||||
export const actionScript = functions.https.onCall(
|
||||
async (data: ActionData, context: functions.https.CallableContext) => {
|
||||
try {
|
||||
if (!context) {
|
||||
throw Error(`You are unauthenticated`);
|
||||
}
|
||||
|
||||
const { ref, actionParams, row, column, action, schemaDocPath } = data;
|
||||
|
||||
const _schemaDocPath =
|
||||
schemaDocPath ?? generateSchemaDocPath(ref.tablePath);
|
||||
const schemaDoc = await db.doc(_schemaDocPath).get();
|
||||
const schemaDocData = schemaDoc.data();
|
||||
if (!schemaDocData) {
|
||||
return {
|
||||
success: false,
|
||||
message: "no schema found",
|
||||
};
|
||||
}
|
||||
const config = schemaDocData.columns[column.key].config;
|
||||
const { script, requiredRoles, requiredFields, undo, redo } = config;
|
||||
if (!hasAnyRole(requiredRoles, context)) {
|
||||
throw Error(`You don't have the required roles permissions`);
|
||||
}
|
||||
|
||||
const missingRequiredFields = requiredFields
|
||||
? requiredFields.reduce(missingFieldsReducer(row), [])
|
||||
: [];
|
||||
if (missingRequiredFields.length > 0) {
|
||||
throw new Error(
|
||||
`Missing required fields:${missingRequiredFields.join(", ")}`
|
||||
);
|
||||
}
|
||||
//
|
||||
// get auth
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
undo,
|
||||
redo,
|
||||
row,
|
||||
ref,
|
||||
actionParams,
|
||||
column,
|
||||
schemaDocData,
|
||||
script,
|
||||
requiredRoles,
|
||||
requiredFields,
|
||||
})
|
||||
);
|
||||
|
||||
const result: {
|
||||
message: string;
|
||||
status: string;
|
||||
success: boolean;
|
||||
} = await eval(
|
||||
`async({row,db, ref,auth,utilFns,actionParams,context})=>{${
|
||||
action === "undo" ? config["undo.script"] : script
|
||||
}}`
|
||||
)({ row, db, auth, utilFns, ref, actionParams, context });
|
||||
if (result.success)
|
||||
return {
|
||||
success: result.success,
|
||||
message: result.message,
|
||||
cellValue: {
|
||||
redo: config["redo.enabled"],
|
||||
status: result.status,
|
||||
completedAt: serverTimestamp(),
|
||||
meta: { ranBy: context.auth!.token.email },
|
||||
undo: action !== "undo" && config["undo.enabled"],
|
||||
},
|
||||
undo: config["undo.enabled"],
|
||||
redo: config["redo.enabled"],
|
||||
};
|
||||
else
|
||||
return {
|
||||
success: false,
|
||||
message: result.message,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error,
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
17
cloud_actions/functions/src/utils/auth.ts
Normal file
17
cloud_actions/functions/src/utils/auth.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import * as functions from "firebase-functions";
|
||||
|
||||
export const hasAnyRole = (
|
||||
authorizedRoles: string[],
|
||||
context: functions.https.CallableContext
|
||||
) => {
|
||||
if (!context.auth || !context.auth.token.roles) return false;
|
||||
const userRoles = context.auth.token.roles as string[];
|
||||
const authorization = authorizedRoles.reduce(
|
||||
(authorized: boolean, role: string) => {
|
||||
if (userRoles.includes(role)) return true;
|
||||
else return authorized;
|
||||
},
|
||||
false
|
||||
);
|
||||
return authorization;
|
||||
};
|
||||
4
cloud_actions/functions/src/utils/email.ts
Normal file
4
cloud_actions/functions/src/utils/email.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const sendEmail = (msg: any) => {
|
||||
|
||||
// pubsub.push
|
||||
}
|
||||
38
cloud_actions/functions/src/utils/index.ts
Normal file
38
cloud_actions/functions/src/utils/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import * as admin from "firebase-admin";
|
||||
export const serverTimestamp = admin.firestore.FieldValue.serverTimestamp;
|
||||
import { sendEmail } from "./email";
|
||||
import { hasAnyRole } from "./auth";
|
||||
|
||||
var characters =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
export function generateId(length:number):string {
|
||||
var result = "";
|
||||
var charactersLength = characters.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const hasRequiredFields = (requiredFields: string[], data: any) =>
|
||||
requiredFields.reduce((acc: boolean, currField: string) => {
|
||||
if (data[currField] === undefined || data[currField] === null) return false;
|
||||
else return acc;
|
||||
}, true);
|
||||
async function asyncForEach(array: any[], callback: Function) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
const identifyTriggerType = (beforeData:any, afterData:any) =>
|
||||
Boolean(beforeData) && Boolean(afterData)
|
||||
? "update"
|
||||
: Boolean(afterData)
|
||||
? "create"
|
||||
: "delete";
|
||||
|
||||
|
||||
export default { hasRequiredFields,generateId, sendEmail, serverTimestamp, hasAnyRole,asyncForEach,identifyTriggerType };
|
||||
15
cloud_actions/functions/tsconfig.json
Normal file
15
cloud_actions/functions/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"outDir": "lib",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"target": "es2017"
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
2833
cloud_actions/functions/yarn.lock
Normal file
2833
cloud_actions/functions/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user