您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 

324 行
11 KiB

  1. import { NextRequestWithAuth, withAuth } from "next-auth/middleware";
  2. import { ability, authOptions } from "@/config/authConfig";
  3. import { NextFetchEvent, NextResponse } from "next/server";
  4. import { getToken } from "next-auth/jwt";
  5. // user groups
  6. export const [
  7. SUPER_ADMIN,
  8. TOP_MANAGEMENT,
  9. TEAM_LEAD,
  10. NORMAL_STAFF,
  11. SUPPORTING_STAFF
  12. ] = [
  13. "Super Admin",
  14. "Top Management",
  15. "Team Leader",
  16. "Normal Staff",
  17. "Supporting Staff"
  18. ]
  19. // abilities
  20. export const [
  21. MAINTAIN_USER,
  22. MAINTAIN_TIMESHEET,
  23. VIEW_TASK_TEMPLATE,
  24. VIEW_GROUP,
  25. VIEW_CLIENT,
  26. VIEW_SUBSIDIARY,
  27. VIEW_STAFF,
  28. VIEW_COMPANY,
  29. VIEW_SKILL,
  30. VIEW_DEPARTMENT,
  31. VIEW_POSITION,
  32. VIEW_SALARY,
  33. VIEW_TEAM,
  34. VIEW_HOLIDAY,
  35. MAINTAIN_CLIENT,
  36. MAINTAIN_SUBSIDIARY,
  37. MAINTAIN_STAFF,
  38. MAINTAIN_COMPANY,
  39. MAINTAIN_SKILL,
  40. MAINTAIN_DEPARTMENT,
  41. MAINTAIN_POSITION,
  42. MAINTAIN_SALARY,
  43. MAINTAIN_TEAM,
  44. MAINTAIN_GROUP,
  45. MAINTAIN_HOLIDAY,
  46. VIEW_DASHBOARD_SELF,
  47. VIEW_DASHBOARD_ALL,
  48. IMPORT_INVOICE,
  49. VIEW_STAFF_PROFILE,
  50. IMPORT_RECEIPT,
  51. MAINTAIN_TASK_TEMPLATE,
  52. MAINTAIN_TIMESHEET_7DAYS,
  53. VIEW_PROJECT,
  54. MAINTAIN_PROJECT,
  55. DELETE_PROJECT,
  56. MAINTAIN_TIMESHEET_FAST_TIME_ENTRY,
  57. VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING,
  58. MAINTAIN_NORMAL_STAFF_WORKSPACE,
  59. MAINTAIN_MANAGEMENT_STAFF_WORKSPACE,
  60. GENERATE_LATE_START_REPORT,
  61. GENERATE_PROJECT_POTENTIAL_DELAY_REPORT,
  62. GENERATE_RESOURCE_OVERCONSUMPTION_REPORT,
  63. GENERATE_COST_AND_EXPENSE_REPORT,
  64. GENERATE_PROJECT_COMPLETION_REPORT,
  65. GENERATE_PROJECT_PANDL_REPORT,
  66. GENERATE_FINANCIAL_STATUS_REPORT,
  67. GENERATE_PROJECT_CASH_FLOW_REPORT,
  68. GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT,
  69. GENERATE_CROSS_TEAM_CHARGE_REPORT
  70. ] = [
  71. 'MAINTAIN_USER',
  72. 'MAINTAIN_TIMESHEET',
  73. 'VIEW_TASK_TEMPLATE',
  74. 'VIEW_GROUP',
  75. 'VIEW_CLIENT',
  76. 'VIEW_SUBSIDIARY',
  77. 'VIEW_STAFF',
  78. 'VIEW_COMPANY',
  79. 'VIEW_SKILL',
  80. 'VIEW_DEPARTMENT',
  81. 'VIEW_POSITION',
  82. 'VIEW_SALARY',
  83. 'VIEW_TEAM',
  84. 'VIEW_HOLIDAY',
  85. 'MAINTAIN_CLIENT',
  86. 'MAINTAIN_SUBSIDIARY',
  87. 'MAINTAIN_STAFF',
  88. 'MAINTAIN_COMPANY',
  89. 'MAINTAIN_SKILL',
  90. 'MAINTAIN_DEPARTMENT',
  91. 'MAINTAIN_POSITION',
  92. 'MAINTAIN_SALARY',
  93. 'MAINTAIN_TEAM',
  94. 'MAINTAIN_GROUP',
  95. 'MAINTAIN_HOLIDAY',
  96. 'VIEW_DASHBOARD_SELF',
  97. 'VIEW_DASHBOARD_ALL',
  98. 'IMPORT_INVOICE',
  99. 'VIEW_STAFF_PROFILE',
  100. 'IMPORT_RECEIPT',
  101. 'MAINTAIN_TASK_TEMPLATE',
  102. 'MAINTAIN_TIMESHEET_7DAYS',
  103. 'VIEW_PROJECT',
  104. 'MAINTAIN_PROJECT',
  105. 'DELETE_PROJECT',
  106. 'MAINTAIN_TIMESHEET_FAST_TIME_ENTRY',
  107. 'VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING',
  108. 'MAINTAIN_NORMAL_STAFF_WORKSPACE',
  109. 'MAINTAIN_MANAGEMENT_STAFF_WORKSPACE',
  110. 'G_LATE_START_REPORT',
  111. 'G_PROJECT_POTENTIAL_DELAY_REPORT',
  112. 'G_RESOURCE_OVERCONSUMPTION_REPORT',
  113. 'G_COST_AND_EXPENSE_REPORT',
  114. 'G_PROJECT_COMPLETION_REPORT',
  115. 'G_PROJECT_P&L_REPORT',
  116. 'G_FINANCIAL_STATUS_REPORT',
  117. 'G_PROJECT_CASH_FLOW_REPORT',
  118. 'G_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT',
  119. 'G_CROSS_TEAM_CHARGE_REPORT'
  120. ]
  121. const PRIVATE_ROUTES = [
  122. "/analytics",
  123. "/dashboard",
  124. // "/home",
  125. "/invoice",
  126. "/projects",
  127. "/tasks",
  128. "/settings",
  129. "/staffReimbursement",
  130. ];
  131. const LANG_QUERY_PARAM = "lang";
  132. export default async function middleware(
  133. req: NextRequestWithAuth,
  134. event: NextFetchEvent,
  135. ) {
  136. const langPref = req.nextUrl.searchParams.get(LANG_QUERY_PARAM);
  137. // const token = await getToken({ req: req, secret: process.env.SECRET });
  138. if (langPref) {
  139. // Redirect to same url without the lang query param + set cookies
  140. const newUrl = new URL(req.nextUrl);
  141. newUrl.searchParams.delete(LANG_QUERY_PARAM);
  142. const response = NextResponse.redirect(newUrl);
  143. response.cookies.set("i18next", langPref);
  144. return response;
  145. }
  146. const authMiddleware = withAuth({
  147. pages: authOptions.pages,
  148. callbacks: {
  149. authorized: ({ req, token }) => {
  150. let isAuth = Boolean(token);
  151. if (!Boolean(token)) {
  152. return Boolean(token)
  153. }
  154. const abilities = (token!.abilities as ability[]).map((item: ability) => item.actionSubjectCombo);
  155. if (req.nextUrl.pathname.startsWith('/projects')) {
  156. isAuth = [MAINTAIN_PROJECT].some((ability) => abilities.includes(ability));
  157. }
  158. if (req.nextUrl.pathname.startsWith('/tasks')) {
  159. isAuth = [MAINTAIN_TASK_TEMPLATE].some((ability) => abilities.includes(ability));
  160. }
  161. if (req.nextUrl.pathname.startsWith('/settings')) {
  162. isAuth = [
  163. VIEW_CLIENT,
  164. VIEW_SUBSIDIARY,
  165. VIEW_STAFF,
  166. VIEW_COMPANY,
  167. VIEW_SKILL,
  168. VIEW_DEPARTMENT,
  169. VIEW_POSITION,
  170. VIEW_SALARY,
  171. VIEW_TEAM,
  172. VIEW_GROUP,
  173. VIEW_HOLIDAY,
  174. MAINTAIN_CLIENT,
  175. MAINTAIN_SUBSIDIARY,
  176. MAINTAIN_STAFF,
  177. MAINTAIN_COMPANY,
  178. MAINTAIN_SKILL,
  179. MAINTAIN_DEPARTMENT,
  180. MAINTAIN_POSITION,
  181. MAINTAIN_SALARY,
  182. MAINTAIN_TEAM,
  183. MAINTAIN_GROUP,
  184. MAINTAIN_HOLIDAY
  185. ].some((ability) => abilities.includes(ability));
  186. }
  187. if (req.nextUrl.pathname.startsWith('/settings/customer/create') || req.nextUrl.pathname.startsWith('/settings/customer/edit')) {
  188. isAuth = [MAINTAIN_CLIENT].some((ability) => abilities.includes(ability));
  189. }
  190. if (req.nextUrl.pathname.startsWith('/settings/subsidiary/create') || req.nextUrl.pathname.startsWith('/settings/subsidiary/edit')) {
  191. isAuth = [MAINTAIN_SUBSIDIARY].some((ability) => abilities.includes(ability));
  192. }
  193. if (req.nextUrl.pathname.startsWith('/settings/staff/create') || req.nextUrl.pathname.startsWith('/settings/staff/edit')) {
  194. isAuth = [MAINTAIN_STAFF].some((ability) => abilities.includes(ability));
  195. }
  196. if (req.nextUrl.pathname.startsWith('/settings/company/create') || req.nextUrl.pathname.startsWith('/settings/company/edit')) {
  197. isAuth = [MAINTAIN_COMPANY].some((ability) => abilities.includes(ability));
  198. }
  199. if (req.nextUrl.pathname.startsWith('/settings/skill/create') || req.nextUrl.pathname.startsWith('/settings/skill/edit')) {
  200. isAuth = [MAINTAIN_SKILL].some((ability) => abilities.includes(ability));
  201. }
  202. if (req.nextUrl.pathname.startsWith('/settings/department/create') || req.nextUrl.pathname.startsWith('/settings/department/edit')) {
  203. isAuth = [MAINTAIN_DEPARTMENT].some((ability) => abilities.includes(ability));
  204. }
  205. if (req.nextUrl.pathname.startsWith('/settings/position/create') || req.nextUrl.pathname.startsWith('/settings/position/edit')) {
  206. isAuth = [MAINTAIN_POSITION].some((ability) => abilities.includes(ability));
  207. }
  208. if (req.nextUrl.pathname.startsWith('/settings/team/create') || req.nextUrl.pathname.startsWith('/settings/team/edit')) {
  209. isAuth = [MAINTAIN_TEAM].some((ability) => abilities.includes(ability));
  210. }
  211. if (req.nextUrl.pathname.startsWith('/settings/group/create') || req.nextUrl.pathname.startsWith('/settings/group/edit')) {
  212. isAuth = [MAINTAIN_GROUP].some((ability) => abilities.includes(ability));
  213. }
  214. if (req.nextUrl.pathname.startsWith('/settings/holiday/create') || req.nextUrl.pathname.startsWith('/settings/holiday/edit')) {
  215. isAuth = [MAINTAIN_HOLIDAY].some((ability) => abilities.includes(ability));
  216. }
  217. if (req.nextUrl.pathname.startsWith('/settings/user')) {
  218. isAuth = [MAINTAIN_USER].some((ability) => abilities.includes(ability));
  219. }
  220. if (req.nextUrl.pathname.startsWith('/settings/staff/user')) {
  221. isAuth = [MAINTAIN_USER].some((ability) => abilities.includes(ability));
  222. }
  223. if (req.nextUrl.pathname.startsWith('/analytics')) {
  224. isAuth = [
  225. GENERATE_LATE_START_REPORT,
  226. GENERATE_PROJECT_POTENTIAL_DELAY_REPORT,
  227. GENERATE_RESOURCE_OVERCONSUMPTION_REPORT,
  228. GENERATE_COST_AND_EXPENSE_REPORT,
  229. GENERATE_PROJECT_COMPLETION_REPORT,
  230. GENERATE_PROJECT_PANDL_REPORT,
  231. GENERATE_FINANCIAL_STATUS_REPORT,
  232. GENERATE_PROJECT_CASH_FLOW_REPORT,
  233. GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT,
  234. GENERATE_CROSS_TEAM_CHARGE_REPORT
  235. ].some((ability) => abilities.includes(ability));
  236. }
  237. if (req.nextUrl.pathname.startsWith('/analytics/LateStartReport')) {
  238. isAuth = [GENERATE_LATE_START_REPORT].some((ability) => abilities.includes(ability));
  239. }
  240. if (req.nextUrl.pathname.startsWith('/analytics/ProjectPotentialDelayReport')) {
  241. isAuth = [GENERATE_PROJECT_POTENTIAL_DELAY_REPORT].some((ability) => abilities.includes(ability));
  242. }
  243. if (req.nextUrl.pathname.startsWith('/analytics/ResourceOverconsumptionReport')) {
  244. isAuth = [GENERATE_RESOURCE_OVERCONSUMPTION_REPORT].some((ability) => abilities.includes(ability));
  245. }
  246. if (req.nextUrl.pathname.startsWith('/analytics/CostandExpenseReport')) {
  247. isAuth = [GENERATE_COST_AND_EXPENSE_REPORT].some((ability) => abilities.includes(ability));
  248. }
  249. if (req.nextUrl.pathname.startsWith('/analytics/ProjectCompletionReport')) {
  250. isAuth = [GENERATE_PROJECT_COMPLETION_REPORT].some((ability) => abilities.includes(ability));
  251. }
  252. if (req.nextUrl.pathname.startsWith('/analytics/ProjectPandLReport')) {
  253. isAuth = [GENERATE_PROJECT_PANDL_REPORT].some((ability) => abilities.includes(ability));
  254. }
  255. if (req.nextUrl.pathname.startsWith('/analytics/FinancialStatusReport')) {
  256. isAuth = [GENERATE_FINANCIAL_STATUS_REPORT].some((ability) => abilities.includes(ability));
  257. }
  258. if (req.nextUrl.pathname.startsWith('/analytics/ProjectCashFlowReport')) {
  259. isAuth = [GENERATE_PROJECT_CASH_FLOW_REPORT].some((ability) => abilities.includes(ability));
  260. }
  261. if (req.nextUrl.pathname.startsWith('/analytics/StaffMonthlyWorkHoursAnalysisReport')) {
  262. isAuth = [GENERATE_STAFF_MONTHLY_WORK_HOURS_ANALYSIS_REPORT].some((ability) => abilities.includes(ability));
  263. }
  264. if (req.nextUrl.pathname.startsWith('/analytics/CrossTeamChargeReport')) {
  265. isAuth = [GENERATE_CROSS_TEAM_CHARGE_REPORT].some((ability) => abilities.includes(ability));
  266. }
  267. if (req.nextUrl.pathname.startsWith('/settings/staff/edit')) {
  268. isAuth = [VIEW_STAFF_PROFILE].some((ability) => abilities.includes(ability));
  269. }
  270. if (req.nextUrl.pathname.startsWith('/invoice')) {
  271. isAuth = [IMPORT_INVOICE, IMPORT_RECEIPT].some((ability) => abilities.includes(ability));
  272. }
  273. if (req.nextUrl.pathname.startsWith('/dashboard')) {
  274. isAuth = [VIEW_DASHBOARD_ALL, VIEW_DASHBOARD_SELF].some((ability) => abilities.includes(ability));
  275. }
  276. if (req.nextUrl.pathname.startsWith('/dashboard/ProjectResourceConsumptionRanking')) {
  277. isAuth = [VIEW_PROJECT_RESOURCE_CONSUMPTION_RANKING].some((ability) => abilities.includes(ability));
  278. }
  279. return isAuth
  280. }
  281. }
  282. });
  283. // Matcher for using the auth middleware
  284. return PRIVATE_ROUTES.some((route) => req.nextUrl.pathname.startsWith(route))
  285. ? await authMiddleware(req, event) // Let auth middleware handle response
  286. : NextResponse.next(); // Return normal response
  287. }