diff --git a/Services/client/src/stores/storage.ts b/Services/client/src/stores/storage.ts index bf80bc7..19aac9f 100644 --- a/Services/client/src/stores/storage.ts +++ b/Services/client/src/stores/storage.ts @@ -379,7 +379,7 @@ const useStorage = defineStore('storageStore', () => { const arrayPath: string[] = path.split('/').filter(Boolean) await api.post(`${import.meta.env.VITE_API_ENDPOINT}storage/folder`, { path: arrayPath, - name: name, + name: name.replace(/^./, ''), }) } loader.hide() diff --git a/Services/server/src/controllers/searchController.ts b/Services/server/src/controllers/searchController.ts index 48c2f1e..502b74b 100644 --- a/Services/server/src/controllers/searchController.ts +++ b/Services/server/src/controllers/searchController.ts @@ -27,6 +27,9 @@ export class SearchController extends Controller { should: search.OR?.map((v) => ({ [type[search.exact || v.exact ? 1 : 0]]: { [v.field]: v.value }, })), + must_not: { + match: { hidden: true }, + }, }, }, size: 10000, diff --git a/Services/server/src/controllers/storageController.ts b/Services/server/src/controllers/storageController.ts index 337eda7..d19ff6c 100644 --- a/Services/server/src/controllers/storageController.ts +++ b/Services/server/src/controllers/storageController.ts @@ -35,6 +35,7 @@ const PATH_ALREADY_EXIST = "ตำแหน่งดังกล่าวมี interface ListRequestBody { operation: "folder" | "file"; path: string[]; + hidden?: boolean; } interface FolderBody { @@ -77,6 +78,8 @@ interface FileBody { category?: string[]; /** @example ["การเงิน", "รายรับ", "รายจ่าย"] */ keyword?: string[]; + /** @example false */ + hidden?: boolean; } interface PutFileBody extends Omit { @@ -111,7 +114,7 @@ interface DownloadFileBody { file: string; } -async function listFolder(path: string[]) { +async function listFolder(path: string[], hidden: boolean = false) { const list = await new Promise<{ pathname: string; name: string }[]>((resolve, reject) => { const item: { pathname: string; name: string }[] = []; @@ -132,6 +135,8 @@ async function listFolder(path: string[]) { const folder = await Promise.all( list.map(async (v) => { + if (v.name.startsWith(".") && !hidden) return undefined; + // Get stat from hidden object that used to mark as folder as minio doesn't really have folder const stat = await minioClient .statObject(DEFAULT_BUCKET, `${v.pathname}.keep`) @@ -152,12 +157,17 @@ async function listFolder(path: string[]) { return folder.filter((v: StorageFolder | undefined): v is StorageFolder => !!v); } -async function listFile(path: string[]) { +async function listFile(path: string[], hidden: boolean = false) { const result = await esClient .search }>({ index: DEFAULT_INDEX, sort: [{ pathname: "asc" }], - query: { match: { path: path.join("/") + "/" } }, + query: { + bool: { + must: { match: { path: path.join("/") + "/" } }, + must_not: !hidden ? { match: { hidden: true } } : undefined, + }, + }, size: 10000, }) .then((r) => r.hits.hits); @@ -229,7 +239,7 @@ export class StorageController extends Controller { public async getList(@Body() body: ListRequestBody) { const path = body.path.filter(Boolean); - if (body.operation === "folder") return await listFolder(path); + if (body.operation === "folder") return await listFolder(path, body.hidden); if (body.operation === "file") return await listFile(path); } @@ -428,6 +438,7 @@ export class StorageController extends Controller { category: body.category ?? [], keyword: body.keyword ?? [], upload: false, // flag + hidden: body.hidden ?? false, createdAt: new Date().toISOString(), createdBy: request.user.preferred_username, updatedAt: new Date().toISOString(), diff --git a/Services/server/src/controllers/versionController.ts b/Services/server/src/controllers/versionController.ts new file mode 100644 index 0000000..c732c1f --- /dev/null +++ b/Services/server/src/controllers/versionController.ts @@ -0,0 +1,11 @@ +import { Controller, Get, Route, Tags } from "tsoa"; +import version from "../ver.json"; + +@Route("version") +export class VersionController extends Controller { + @Get() + @Tags("Version") + public async getVersion() { + return version; + } +} diff --git a/Services/server/src/interfaces/storage-fs.ts b/Services/server/src/interfaces/storage-fs.ts index 5e7693f..c370bd9 100644 --- a/Services/server/src/interfaces/storage-fs.ts +++ b/Services/server/src/interfaces/storage-fs.ts @@ -29,6 +29,7 @@ export interface StorageFile { path: string; upload: boolean; + hidden: boolean; updatedAt: string | Date; updatedBy: string; diff --git a/Services/server/src/rabbitmq/handler.ts b/Services/server/src/rabbitmq/handler.ts index 9d8b5c0..55fe0bb 100644 --- a/Services/server/src/rabbitmq/handler.ts +++ b/Services/server/src/rabbitmq/handler.ts @@ -126,6 +126,7 @@ async function handleNotFoundRecord( category: [], keyword: [], upload: true, + hidden: false, createdAt: new Date().toISOString(), createdBy: "n/a", updatedAt: new Date().toISOString(), diff --git a/Services/server/src/routes.ts b/Services/server/src/routes.ts index b64370c..7471773 100644 --- a/Services/server/src/routes.ts +++ b/Services/server/src/routes.ts @@ -18,6 +18,8 @@ import { StorageController } from './controllers/storageController'; import { SubFolderController } from './controllers/subFolderController'; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa import { SubFolderFileController } from './controllers/subFolderFileController'; +// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa +import { VersionController } from './controllers/versionController'; import { expressAuthentication } from './utils/auth'; // @ts-ignore - no great way to install types from subpackage const promiseAny = require('promise.any'); @@ -50,6 +52,7 @@ const models: TsoaRoute.Models = { "keyword": {"dataType":"array","array":{"dataType":"string"},"required":true}, "path": {"dataType":"string","required":true}, "upload": {"dataType":"boolean","required":true}, + "hidden": {"dataType":"boolean","required":true}, "updatedAt": {"dataType":"union","subSchemas":[{"dataType":"string"},{"dataType":"datetime"}],"required":true}, "updatedBy": {"dataType":"string","required":true}, "createdAt": {"dataType":"union","subSchemas":[{"dataType":"string"},{"dataType":"datetime"}],"required":true}, @@ -73,6 +76,7 @@ const models: TsoaRoute.Models = { "properties": { "operation": {"dataType":"union","subSchemas":[{"dataType":"enum","enums":["folder"]},{"dataType":"enum","enums":["file"]}],"required":true}, "path": {"dataType":"array","array":{"dataType":"string"},"required":true}, + "hidden": {"dataType":"boolean"}, }, "additionalProperties": false, }, @@ -112,6 +116,7 @@ const models: TsoaRoute.Models = { "description": {"dataType":"string"}, "category": {"dataType":"array","array":{"dataType":"string"}}, "keyword": {"dataType":"array","array":{"dataType":"string"}}, + "hidden": {"dataType":"boolean"}, }, "additionalProperties": false, }, @@ -123,6 +128,7 @@ const models: TsoaRoute.Models = { "description": {"dataType":"string"}, "category": {"dataType":"array","array":{"dataType":"string"}}, "keyword": {"dataType":"array","array":{"dataType":"string"}}, + "hidden": {"dataType":"boolean"}, "from": {"dataType":"nestedObjectLiteral","nestedProperties":{"file":{"dataType":"string","required":true},"path":{"dataType":"array","array":{"dataType":"string"},"required":true}},"required":true}, "to": {"dataType":"nestedObjectLiteral","nestedProperties":{"file":{"dataType":"string","required":true},"path":{"dataType":"array","array":{"dataType":"string"},"required":true}}}, "upload": {"dataType":"boolean"}, @@ -1248,6 +1254,30 @@ export function RegisterRoutes(app: Router) { } }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.get('/version', + ...(fetchMiddlewares(VersionController)), + ...(fetchMiddlewares(VersionController.prototype.getVersion)), + + function VersionController_getVersion(request: any, response: any, next: any) { + const args = { + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = getValidatedArgs(args, request, response); + + const controller = new VersionController(); + + + const promise = controller.getVersion.apply(controller, validatedArgs as any); + promiseHandler(controller, promise, response, undefined, next); + } catch (err) { + return next(err); + } + }); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa diff --git a/Services/server/src/swagger.json b/Services/server/src/swagger.json index 34a7abb..e077aef 100644 --- a/Services/server/src/swagger.json +++ b/Services/server/src/swagger.json @@ -85,6 +85,9 @@ "upload": { "type": "boolean" }, + "hidden": { + "type": "boolean" + }, "updatedAt": { "anyOf": [ { @@ -125,6 +128,7 @@ "keyword", "path", "upload", + "hidden", "updatedAt", "updatedBy", "createdAt", @@ -198,6 +202,9 @@ "type": "string" }, "type": "array" + }, + "hidden": { + "type": "boolean" } }, "required": [ @@ -352,6 +359,10 @@ "รายรับ", "รายจ่าย" ] + }, + "hidden": { + "type": "boolean", + "example": false } }, "required": [ @@ -392,6 +403,10 @@ "รายจ่าย" ] }, + "hidden": { + "type": "boolean", + "example": false + }, "from": { "properties": { "file": { @@ -1522,6 +1537,9 @@ } ] }, + "hidden": { + "type": "boolean" + }, "upload": { "type": "boolean" }, @@ -1568,6 +1586,7 @@ "createdAt", "updatedBy", "updatedAt", + "hidden", "upload", "path", "keyword", @@ -2216,6 +2235,9 @@ } ] }, + "hidden": { + "type": "boolean" + }, "upload": { "type": "boolean" }, @@ -2262,6 +2284,7 @@ "createdAt", "updatedBy", "updatedAt", + "hidden", "upload", "path", "keyword", @@ -2424,6 +2447,9 @@ } ] }, + "hidden": { + "type": "boolean" + }, "upload": { "type": "boolean" }, @@ -2470,6 +2496,7 @@ "createdAt", "updatedBy", "updatedAt", + "hidden", "upload", "path", "keyword", @@ -3409,6 +3436,9 @@ } ] }, + "hidden": { + "type": "boolean" + }, "upload": { "type": "boolean" }, @@ -3455,6 +3485,7 @@ "createdAt", "updatedBy", "updatedAt", + "hidden", "upload", "path", "keyword", @@ -3535,6 +3566,26 @@ } ] } + }, + "/version": { + "get": { + "operationId": "GetVersion", + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "tags": [ + "Version" + ], + "security": [], + "parameters": [] + } } }, "servers": [