From b8b7db1c66c6374d8a81eebc19d6d58bdb5912bd Mon Sep 17 00:00:00 2001 From: Anas Fikhi Date: Wed, 29 Nov 2023 10:29:44 +0100 Subject: [PATCH] [ Add ] --- .env | 3 +- .vscode/settings.json | 3 +- package.json | 6 +-- src/adapters/json.ts | 22 +++++--- src/ai_clients/openAI.ts | 14 ++--- .../auth/validate_api_key_with_user_token.ts | 3 +- src/controllers/cli/process_exceptions.ts | 6 ++- src/controllers/database/client.ts | 3 +- src/controllers/database/delete.ts | 8 +-- src/controllers/database/insert.ts | 8 +-- src/controllers/database/read.ts | 8 +-- src/controllers/database/update.ts | 10 +--- .../translate/process_translations.ts | 6 ++- .../users/file_operation_of_user.ts | 4 +- src/controllers/users/get_user.ts | 7 +-- src/controllers/users/save_file.ts | 25 +++++---- src/controllers/utils/logger.ts | 51 ++++++++----------- src/controllers/utils/utils.ts | 11 +++- src/enum.ts | 4 +- src/index.ts | 4 +- src/routes/router.ts | 4 +- src/type.ts | 2 +- tsconfig.json | 7 +-- uploads/42ac3e18bea8832b88390313fed19de0 | 4 ++ uploads/896f49ff3e57c30a554883d6b1e81d7a | 4 ++ uploads/df6b9479486eb529e20416c1a9906b80 | 4 ++ 26 files changed, 121 insertions(+), 110 deletions(-) create mode 100644 uploads/42ac3e18bea8832b88390313fed19de0 create mode 100644 uploads/896f49ff3e57c30a554883d6b1e81d7a create mode 100644 uploads/df6b9479486eb529e20416c1a9906b80 diff --git a/.env b/.env index ab646be..c154deb 100644 --- a/.env +++ b/.env @@ -1,3 +1,4 @@ PORT=5559 -OPENAI_API_KEY=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 +# OPENAI_API_KEY=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 +OPENAI_API_KEY=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 MANGODBURI="mongodb+srv://Gwhyyy:UfdyZQJwEhjQhp1t@lang-sync.veeha5h.mongodb.net/?retryWrites=true&w=majority" \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 22b51a2..d7457a0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,6 @@ }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - } + }, + "typescript.tsdk": "node_modules\\typescript\\lib" } diff --git a/package.json b/package.json index 94dbc76..55def45 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "main": "index.js", "scripts": { "build": "tsc", - "start": "node ./dist/index.js", - "dev": "echo 'aaa' &&nodemon ./dist/index.js", + "start": "tsc && node ./dist/index.js", + "dev": "echo 'aaa' && nodemon ./dist/index.js", "separator": "node ./src/controllers/utils/json_tiktoken_separator.js", "test": "echo \"Error: no test specified\" && exit 1" }, @@ -33,4 +33,4 @@ "@types/express": "^4.17.18", "typescript": "^5.2.2" } -} +} \ No newline at end of file diff --git a/src/adapters/json.ts b/src/adapters/json.ts index 4491c73..e1da308 100644 --- a/src/adapters/json.ts +++ b/src/adapters/json.ts @@ -1,7 +1,9 @@ import fs from "fs"; import * as utils from "../controllers/utils/utils"; -import uuid from "uuid"; +import { v4 } from "uuid"; import { OpenAIClient } from "../ai_clients/openAI"; +import { LangSyncLogger } from "../controllers/utils/logger"; +import { loggingTypes } from "../enum"; export class JsonAdapter implements BaseAdapter { constructor(private filePath: string) {} @@ -14,12 +16,14 @@ export class JsonAdapter implements BaseAdapter { allowMultipleUniqueIds: boolean = false; deleteFile() { - fs.unlinkSync(this.filePath); + if (fs.existsSync(this.filePath)) { + fs.unlinkSync(this.filePath); + } } readFileAsString(): string { let asString: string = fs.readFileSync(this.filePath, "utf8"); - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: `File named ${this.filePath .split("/") .pop()} has been loaded as a string.`, @@ -32,12 +36,12 @@ export class JsonAdapter implements BaseAdapter { parseString(fileContent: string): any { return JSON.parse(fileContent); } - + // @ts-ignore async isHarming(options: HarmOptions): Promise { let isHarming: boolean = await this.aiClient.isHarming(options.fileContent); if (isHarming) { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: "The provided content violates our policy, and so it is unacceptable to be processed.", }); @@ -49,7 +53,7 @@ export class JsonAdapter implements BaseAdapter { } else { } } else { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: "The provided content is acceptable to be processed.", }); @@ -60,6 +64,7 @@ export class JsonAdapter implements BaseAdapter { async asPartsForOpenAI(): Promise { let asString: string = this.readFileAsString(); + // @ts-ignore let isHarming: boolean = await this.isHarming({ fileContent: asString, throwIfHarming: true, @@ -70,7 +75,7 @@ export class JsonAdapter implements BaseAdapter { let asParts: string[] = await utils.parsedFileContentPartsSeparatorForOpenAI(parsed); - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: `File named ${this.filePath .split("/") .pop()} has been split into ${asParts.length} parts.`, @@ -90,7 +95,8 @@ export class JsonAdapter implements BaseAdapter { ); } - let generatedId = uuid.v4(); + const generatedId = v4(); + this.numberOfGeneratedUniqueIds++; return generatedId; diff --git a/src/ai_clients/openAI.ts b/src/ai_clients/openAI.ts index 02efbe1..8161595 100644 --- a/src/ai_clients/openAI.ts +++ b/src/ai_clients/openAI.ts @@ -1,5 +1,7 @@ import { OpenAI } from "openai"; import configs from "../configs/openai"; +import { LangSyncLogger } from "../controllers/utils/logger"; +import { loggingTypes } from "../enum"; export class OpenAIClient implements ArtificialIntelligenceBase { constructor() { this.init(configs.openAI); @@ -19,7 +21,7 @@ export class OpenAIClient implements ArtificialIntelligenceBase { input: content.toString(), }); - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: "Moderation request has been processed by OpenAI.", type: loggingTypes.info, }); @@ -29,7 +31,7 @@ export class OpenAIClient implements ArtificialIntelligenceBase { return typeof flagged === "boolean" ? flagged : false; } catch (error: Error | any) { if (error.status === 429) { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: "OpenAI API rate limit reached, waiting 20 seconds for next moderation request", type: loggingTypes.warning, @@ -42,7 +44,7 @@ export class OpenAIClient implements ArtificialIntelligenceBase { ) ); - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: "20 seconds passed, continuing request", type: loggingTypes.info, }); @@ -69,14 +71,14 @@ export class OpenAIClient implements ArtificialIntelligenceBase { ], }); - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: res.choices[0].message.content + "\n", }); return res; } catch (error: Error | any) { if (error.status === 429) { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: "OpenAI API rate limit reached, waiting 20 seconds for next request", }); @@ -88,7 +90,7 @@ export class OpenAIClient implements ArtificialIntelligenceBase { ) ); - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: "20 seconds passed, repeating request..", }); return await this.process(messageToOpenAI); diff --git a/src/controllers/auth/validate_api_key_with_user_token.ts b/src/controllers/auth/validate_api_key_with_user_token.ts index 91c679d..20eece3 100644 --- a/src/controllers/auth/validate_api_key_with_user_token.ts +++ b/src/controllers/auth/validate_api_key_with_user_token.ts @@ -1,4 +1,5 @@ import { LangSyncDatabase } from "../database/database"; +import { LangSyncLogger } from "../utils/logger"; export default async function verifyApiKeyWithUserAuthToken( apiKey: ExtractedApiKey, @@ -9,7 +10,7 @@ export default async function verifyApiKeyWithUserAuthToken( if (!document) { throw new Error("No user with this API key found."); } else { - LangSyncLogger.instance.log({ message: "User found with this API key." }); + new LangSyncLogger().log({ message: "User found with this API key." }); onVerified && onVerified(); diff --git a/src/controllers/cli/process_exceptions.ts b/src/controllers/cli/process_exceptions.ts index 19876db..6b267cc 100644 --- a/src/controllers/cli/process_exceptions.ts +++ b/src/controllers/cli/process_exceptions.ts @@ -1,6 +1,8 @@ import Joi from "joi"; import { Request, Response } from "express"; import { LangSyncDatabase } from "../database/database"; +import { LangSyncLogger } from "../utils/logger"; +import { loggingTypes } from "../../enum"; export default async function processCliException(req: Request, res: Response) { let scheme = Joi.object({ @@ -16,7 +18,7 @@ export default async function processCliException(req: Request, res: Response) { let { error, value } = scheme.validate(req.body); if (error) { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: error.toString(), type: loggingTypes.error, }); @@ -28,7 +30,7 @@ export default async function processCliException(req: Request, res: Response) { return res.status(200).json({ message: "success" }); } catch (error: Error | any) { - LangSyncLogger.instance.log({ message: error, type: loggingTypes.error }); + new LangSyncLogger().log({ message: error, type: loggingTypes.error }); return res.status(500).json({ error: error }); } } diff --git a/src/controllers/database/client.ts b/src/controllers/database/client.ts index 366420f..d5692fc 100644 --- a/src/controllers/database/client.ts +++ b/src/controllers/database/client.ts @@ -1,5 +1,6 @@ import { MongoClient, ServerApiVersion } from "mongodb"; import dbConfig from "../../configs/db"; +import { LangSyncLogger } from "../utils/logger"; export class LangSyncDatabaseClient { constructor() {} @@ -24,7 +25,7 @@ export class LangSyncDatabaseClient { await this.client.db("admin").command({ ping: 1 }); - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: "Database connection established successfully.", }); } catch (err) { diff --git a/src/controllers/database/delete.ts b/src/controllers/database/delete.ts index 3f2ea0a..b055019 100644 --- a/src/controllers/database/delete.ts +++ b/src/controllers/database/delete.ts @@ -1,15 +1,9 @@ -import { Filter } from "mongodb"; +import { Document, Filter } from "mongodb"; import { LangSyncDatabaseClient } from "./client"; export class LangSyncDatabaseDelete { constructor() {} - _instance: LangSyncDatabaseDelete = new LangSyncDatabaseDelete(); - - get instance(): LangSyncDatabaseDelete { - return this._instance; - } - delete( databaseName: string, collectionName: string, diff --git a/src/controllers/database/insert.ts b/src/controllers/database/insert.ts index 05fc263..0185e6c 100644 --- a/src/controllers/database/insert.ts +++ b/src/controllers/database/insert.ts @@ -1,22 +1,16 @@ import { LangSyncDatabaseClient } from "./client"; -import { LangSyncDatabase } from "./database"; export class LangSyncDatabaseInsert { async cliException(value: any) { return await this.insert("db", "cli_exceptions", value); } constructor() {} - _instance: LangSyncDatabaseInsert = new LangSyncDatabaseInsert(); - - get instance(): LangSyncDatabaseInsert { - return this._instance; - } async fileOperation(doc: { userId: any; operationId: string; createdAt: string; - jsonAsParts: Promise; + jsonAsParts: string[]; }): Promise { return await this.insert("db", "jsonPartitions", doc); } diff --git a/src/controllers/database/read.ts b/src/controllers/database/read.ts index 37fa74c..ea47fae 100644 --- a/src/controllers/database/read.ts +++ b/src/controllers/database/read.ts @@ -1,4 +1,4 @@ -import { Filter, WithId } from "mongodb"; +import { Document, Filter } from "mongodb"; import { LangSyncDatabaseClient } from "./client"; export class LangSyncDatabaseRead { @@ -24,12 +24,6 @@ export class LangSyncDatabaseRead { constructor() {} - _instance: LangSyncDatabaseRead = new LangSyncDatabaseRead(); - - get instance(): LangSyncDatabaseRead { - return this._instance; - } - async userOperations(operationId: string): Promise { let docFilter = { operationId: operationId, diff --git a/src/controllers/database/update.ts b/src/controllers/database/update.ts index 9c58592..4f01dca 100644 --- a/src/controllers/database/update.ts +++ b/src/controllers/database/update.ts @@ -1,15 +1,9 @@ -import { Filter, UpdateFilter } from "mongodb"; +import { Document, Filter, UpdateFilter } from "mongodb"; import { LangSyncDatabaseClient } from "./client"; export class LangSyncDatabaseUpdate { constructor() {} - static _instance: LangSyncDatabaseUpdate = new LangSyncDatabaseUpdate(); - - static get instance(): LangSyncDatabaseUpdate { - return this._instance; - } - updateOperationDoc( operationId: string, resultTranslations: any[] @@ -17,9 +11,9 @@ export class LangSyncDatabaseUpdate { const filterDoc = { operationId: operationId, }; - return this.update("db", "jsonPartitions", filterDoc, { $addToSet: { + //@ts-ignore output: { $each: resultTranslations, }, diff --git a/src/controllers/translate/process_translations.ts b/src/controllers/translate/process_translations.ts index 248ca30..9f8ebca 100644 --- a/src/controllers/translate/process_translations.ts +++ b/src/controllers/translate/process_translations.ts @@ -3,7 +3,9 @@ import { Request, Response } from "express"; import { extractAndVerifyApiKeyExistence, sseEvent } from "../utils/utils"; import { LangSyncDatabase } from "../database/database"; import { TasksResolver } from "./tasks_resolver"; +import { LangSyncLogger } from "../utils/logger"; +// @ts-ignore export default async function processTranslations(req: Request, res: Response) { res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); @@ -78,7 +80,7 @@ export default async function processTranslations(req: Request, res: Response) { }) ); - const saveFileOperationDoc = + let saveFileOperationDoc = await LangSyncDatabase.instance.read.savedFileByOperationId(operationId); if (!saveFileOperationDoc) { @@ -137,7 +139,7 @@ export default async function processTranslations(req: Request, res: Response) { }; if (includeOutput) { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: "including output in response..", }); response.output = resultTranslations; diff --git a/src/controllers/users/file_operation_of_user.ts b/src/controllers/users/file_operation_of_user.ts index 4671656..dba9497 100644 --- a/src/controllers/users/file_operation_of_user.ts +++ b/src/controllers/users/file_operation_of_user.ts @@ -3,6 +3,8 @@ import { Request, Response } from "express"; import { LangSyncDatabase } from "../database/database"; import { extractAndVerifyApiKeyExistence } from "../utils/utils"; +import { LangSyncLogger } from "../utils/logger"; +import { loggingTypes } from "../../enum"; export default async function fileOperationOfUser(req: Request, res: Response) { await extractAndVerifyApiKeyExistence( @@ -41,7 +43,7 @@ export default async function fileOperationOfUser(req: Request, res: Response) { return res.status(200).json(doc); } } catch (error: Error | any) { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: error, type: loggingTypes.error, }); diff --git a/src/controllers/users/get_user.ts b/src/controllers/users/get_user.ts index 3d59e24..2711b80 100644 --- a/src/controllers/users/get_user.ts +++ b/src/controllers/users/get_user.ts @@ -1,7 +1,8 @@ -import Joi from "joi"; import { Request, Response } from "express"; import { LangSyncDatabase } from "../database/database"; - +import { LangSyncLogger } from "../utils/logger"; +import { loggingTypes } from "../../enum"; +// @ts-ignore export default async function getUser(req: Request, res: Response) { if (!req.headers["authorization"]) { return res.status(400).json({ @@ -103,7 +104,7 @@ export default async function getUser(req: Request, res: Response) { }); } } catch (error: Error | any) { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: error, type: loggingTypes.error, }); diff --git a/src/controllers/users/save_file.ts b/src/controllers/users/save_file.ts index 973d292..1db50f2 100644 --- a/src/controllers/users/save_file.ts +++ b/src/controllers/users/save_file.ts @@ -1,30 +1,33 @@ import { Request, Response } from "express"; -import fs from "fs"; import * as utils from "../utils/utils"; import verifyApiKeyWithUserAuthToken from "../auth/validate_api_key_with_user_token"; import { JsonAdapter } from "../../adapters/json"; import { LangSyncDatabase } from "../database/database"; +import { LangSyncLogger } from "../utils/logger"; +import { loggingTypes } from "../../enum"; export default async function saveFile(req: Request, res: Response) { - try { - let fileType = req.params.fileType; - utils.validateFileTypeSupport(fileType); + let fileType = req.params.fileType; + + let filePath: string = utils.getFilePath(req.file!.path); + + utils.validateFileTypeSupport(fileType); + let adapter = new JsonAdapter(filePath); + + try { let apiKey: ExtractedApiKey = utils.extractApiKeyFromAuthorizationHeader( req.headers.authorization ?? "" ); await verifyApiKeyWithUserAuthToken(apiKey); - let filePath: string = utils.getFilePath(req.file!.path); + let fileAsParts = await adapter.asPartsForOpenAI(); - let adapter = new JsonAdapter(filePath); - let fileAsParts = adapter.asPartsForOpenAI(); + let userDoc = await LangSyncDatabase.instance.read.userDocByApiKey(apiKey); let operationId = adapter.generateUniqueId(); - let userDoc = await LangSyncDatabase.instance.read.userDocByApiKey(apiKey); - await LangSyncDatabase.instance.insert.fileOperation({ userId: userDoc.userId, operationId: operationId, @@ -39,11 +42,13 @@ export default async function saveFile(req: Request, res: Response) { operationId: operationId, }); } catch (error: Error | any) { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: error, type: loggingTypes.error, }); + adapter.deleteFile(); + res.status(500).json({ message: "Internal server error", error: error }); } } diff --git a/src/controllers/utils/logger.ts b/src/controllers/utils/logger.ts index b1c9625..8b547bd 100644 --- a/src/controllers/utils/logger.ts +++ b/src/controllers/utils/logger.ts @@ -1,34 +1,27 @@ +import { loggingTypes } from "../../enum"; + interface LoggerBase { - log(options: LogOptions): void; + log(options: LogOptions): void; } -class LangSyncLogger implements LoggerBase { - private static _instance: LangSyncLogger = new LangSyncLogger(); - - static get instance(): LangSyncLogger { - return LangSyncLogger._instance; - } - - log(options: LogOptions): void { - switch (options.type) { - case loggingTypes.info: - console.log(options.message); - break; - case loggingTypes.error: - console.error(options.message); - break; - case loggingTypes.warning: - console.warn(options.message); - break; - case loggingTypes.debug: - console.debug(options.message); - break; - default: - console.log(options.message); - break; - - - } - +export class LangSyncLogger implements LoggerBase { + log(options: LogOptions): void { + switch (options.type) { + case loggingTypes.info: + console.log(options.message); + break; + case loggingTypes.error: + console.error(options.message); + break; + case loggingTypes.warning: + console.warn(options.message); + break; + case loggingTypes.debug: + console.debug(options.message); + break; + default: + console.log(options.message); + break; } + } } diff --git a/src/controllers/utils/utils.ts b/src/controllers/utils/utils.ts index fd60f7a..b64d140 100644 --- a/src/controllers/utils/utils.ts +++ b/src/controllers/utils/utils.ts @@ -3,15 +3,17 @@ import { getEncoding } from "js-tiktoken"; import configs from "../../configs/openai"; import { OpenAIClient } from "../../ai_clients/openAI"; import verifyApiKeyWithUserAuthToken from "../auth/validate_api_key_with_user_token"; +import { LangSyncLogger } from "./logger"; +import { LangSyncAllowedFileTypes, loggingTypes } from "../../enum"; const enc = getEncoding("gpt2"); export function canBeDecodedToJsonSafely(encapsulatedFieldsString: string[]) { try { - let decoded = jsonFromEncapsulatedFields(encapsulatedFieldsString); + jsonFromEncapsulatedFields(encapsulatedFieldsString); return true; } catch (error: Error | any) { - LangSyncLogger.instance.log({ + new LangSyncLogger().log({ message: error, type: loggingTypes.error, }); @@ -67,6 +69,11 @@ export function validateFileTypeSupport(fileType: string): void { if (!exists) { throw new Error("File type not supported."); + } else { + new LangSyncLogger().log({ + message: `File type ${fileType} is supported.`, + type: loggingTypes.info, + }); } } export async function parsedFileContentPartsSeparatorForOpenAI( diff --git a/src/enum.ts b/src/enum.ts index 95c4600..fc40701 100644 --- a/src/enum.ts +++ b/src/enum.ts @@ -1,4 +1,4 @@ -enum loggingTypes { +export enum loggingTypes { info, warn, error, @@ -6,6 +6,6 @@ enum loggingTypes { warning, } -enum LangSyncAllowedFileTypes { +export enum LangSyncAllowedFileTypes { json, } diff --git a/src/index.ts b/src/index.ts index 6b578f6..bbbf0f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,5 @@ langSyncServerApp.use(express.urlencoded({ extended: true })); langSyncServerApp.use("/", router); langSyncServerApp.listen(configs.port, () => { - LangSyncLogger.instance.log({ - message: `LangSync server is running on localhost:${configs.port}`, - }); + console.log(`LangSync server is running on localhost:${configs.port}`); }); diff --git a/src/routes/router.ts b/src/routes/router.ts index 91ba3e6..5a645b8 100644 --- a/src/routes/router.ts +++ b/src/routes/router.ts @@ -10,9 +10,9 @@ import processCliException from "../controllers/cli/process_exceptions"; import multer from "multer"; let upload = multer({ dest: "uploads/" }); -router.post(":fileType/save-file", upload.single("sourceFile"), saveFile); +router.post("/:fileType/save-file", upload.single("sourceFile"), saveFile); -router.get("file-operation-of-user", fileOperationOfUser); +router.get("/file-operation-of-user", fileOperationOfUser); router.post("/process-translation", processTranslations); diff --git a/src/type.ts b/src/type.ts index 93ded27..6790efd 100644 --- a/src/type.ts +++ b/src/type.ts @@ -4,7 +4,7 @@ type HarmOptions = { fileContent: string; throwIfHarming: boolean }; type LogOptions = { message: string; - type?: loggingTypes; + type?: any; data?: any; }; diff --git a/tsconfig.json b/tsconfig.json index 61f59ee..6ba576d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,11 @@ { "compilerOptions": { + "moduleResolution": "Node", + "preserveConstEnums": true, "module": "CommonJS", "esModuleInterop": true, - "target": "ES2021", // add this line to use ES2021 - "moduleResolution": "node", + "target": "ES2022", + "lib": ["ES2021.String"], "sourceMap": true, "strict": true, "noImplicitAny": true, @@ -21,6 +23,5 @@ "removeComments": true, "allowJs": false }, - "lib": ["es2015"], "exclude": ["node_modules"] } diff --git a/uploads/42ac3e18bea8832b88390313fed19de0 b/uploads/42ac3e18bea8832b88390313fed19de0 new file mode 100644 index 0000000..96d2a49 --- /dev/null +++ b/uploads/42ac3e18bea8832b88390313fed19de0 @@ -0,0 +1,4 @@ +{ + "test": "testing process", + "hello": "Hello my friend" +} \ No newline at end of file diff --git a/uploads/896f49ff3e57c30a554883d6b1e81d7a b/uploads/896f49ff3e57c30a554883d6b1e81d7a new file mode 100644 index 0000000..96d2a49 --- /dev/null +++ b/uploads/896f49ff3e57c30a554883d6b1e81d7a @@ -0,0 +1,4 @@ +{ + "test": "testing process", + "hello": "Hello my friend" +} \ No newline at end of file diff --git a/uploads/df6b9479486eb529e20416c1a9906b80 b/uploads/df6b9479486eb529e20416c1a9906b80 new file mode 100644 index 0000000..96d2a49 --- /dev/null +++ b/uploads/df6b9479486eb529e20416c1a9906b80 @@ -0,0 +1,4 @@ +{ + "test": "testing process", + "hello": "Hello my friend" +} \ No newline at end of file