feat: add cookie management and HTTP client for user and admin authentication

This commit is contained in:
Ardeman 2025-02-25 08:51:08 +08:00
parent 0c38a775e3
commit c1bacacb49
7 changed files with 120 additions and 13 deletions

7
app/configs/storages.ts Normal file
View File

@ -0,0 +1,7 @@
export const USER_COOKIES = {
token: '__lg-usr-tkn',
}
export const ADMIN_COOKIES = {
token: '__lg-adm-tkn',
}

13
app/libs/cookie.server.ts Normal file
View File

@ -0,0 +1,13 @@
import { createCookie } from 'react-router'
import { ADMIN_COOKIES, USER_COOKIES } from '~/configs/storages'
export const userTokenCookie = createCookie(USER_COOKIES.token, {
secure: process.env.NODE_ENV === 'production',
path: '/news',
})
export const adminTokenCookie = createCookie(ADMIN_COOKIES.token, {
secure: process.env.NODE_ENV === 'production',
path: '/admin',
})

31
app/libs/http-client.ts Normal file
View File

@ -0,0 +1,31 @@
import xior, { merge } from 'xior'
const baseURL = import.meta.env.VITE_API_URL
const HttpClient = (token?: string) => {
const instance = xior.create({
baseURL,
})
instance.interceptors.request.use((config) => {
// eslint-disable-next-line no-console
console.info(`🚀requesting ${config.url}`)
return merge(config, {
headers: {
...(token && { Authorization: `Bearer ${token}` }),
},
})
})
instance.interceptors.response.use(
(response) => {
return response
},
(error) => {
return Promise.reject(error)
},
)
return instance
}
export default HttpClient

View File

@ -0,0 +1,11 @@
import { USER_COOKIES } from '~/configs/storages'
export const setUserLogoutHeaders = () => {
const responseHeaders = new Headers()
responseHeaders.append(
'Set-Cookie',
`${USER_COOKIES.token}=; Path=/news; Max-Age=0`,
)
return responseHeaders
}

View File

@ -8,6 +8,7 @@ import tsEslintParser from '@typescript-eslint/parser'
import eslintPluginUnicorn from 'eslint-plugin-unicorn'
import reactPlugin from 'eslint-plugin-react'
import pluginQuery from '@tanstack/eslint-plugin-query'
import unusedImports from 'eslint-plugin-unused-imports'
export default tseslint.config(
{ ignores: ['dist', 'node_modules', '.react-router'] },
@ -28,6 +29,7 @@ export default tseslint.config(
plugins: {
'react-hooks': reactHooks,
react: reactPlugin,
'unused-imports': unusedImports,
},
languageOptions: {
parser: tsEslintParser,
@ -47,18 +49,7 @@ export default tseslint.config(
...reactHooks.configs.recommended.rules,
...tseslint.configs.recommended.rules,
...reactPlugin.configs.recommended.rules,
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'all',
argsIgnorePattern: '^_',
caughtErrors: 'all',
caughtErrorsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
'@typescript-eslint/no-unused-vars': 'off',
'unicorn/filename-case': [
'error',
{
@ -90,7 +81,8 @@ export default tseslint.config(
'~/layouts/*/*/*/*',
'~/schemas/*/*/*',
'~/types/*/*',
'~/utils/*/**',
'~/utils/*/*',
'~/libs/*/*',
],
},
],
@ -125,6 +117,16 @@ export default tseslint.config(
],
},
],
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'error',
{
vars: 'all',
varsIgnorePattern: '^_',
args: 'after-used',
argsIgnorePattern: '^_',
},
],
'react/react-in-jsx-scope': 'off',
'react/jsx-key': [
'error',

View File

@ -33,6 +33,7 @@
"react-hook-form": "^7.54.2",
"react-router": "^7.1.3",
"tailwind-merge": "^3.0.1",
"xior": "^0.6.3",
"zod": "^3.24.2"
},
"devDependencies": {
@ -56,6 +57,7 @@
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-unicorn": "^56.0.1",
"eslint-plugin-unused-imports": "^4.1.4",
"globals": "^15.14.0",
"husky": "^9.1.7",
"knip": "^5.43.6",

41
pnpm-lock.yaml generated
View File

@ -65,6 +65,9 @@ importers:
tailwind-merge:
specifier: ^3.0.1
version: 3.0.1
xior:
specifier: ^0.6.3
version: 0.6.3
zod:
specifier: ^3.24.2
version: 3.24.2
@ -129,6 +132,9 @@ importers:
eslint-plugin-unicorn:
specifier: ^56.0.1
version: 56.0.1(eslint@8.57.1)
eslint-plugin-unused-imports:
specifier: ^4.1.4
version: 4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)
globals:
specifier: ^15.14.0
version: 15.14.0
@ -2229,6 +2235,15 @@ packages:
peerDependencies:
eslint: '>=8.56.0'
eslint-plugin-unused-imports@4.1.4:
resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==}
peerDependencies:
'@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0
eslint: ^9.0.0 || ^8.0.0
peerDependenciesMeta:
'@typescript-eslint/eslint-plugin':
optional: true
eslint-scope@7.2.2:
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -3927,6 +3942,10 @@ packages:
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
tiny-lru@11.2.11:
resolution: {integrity: sha512-27BIW0dIWTYYoWNnqSmoNMKe5WIbkXsc0xaCQHd3/3xT2XMuMJrzHdrO9QBFR14emBz1Bu0dOAs2sCBBrvgPQA==}
engines: {node: '>=12'}
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
@ -3944,6 +3963,10 @@ packages:
peerDependencies:
typescript: '>=4.8.4'
ts-deepmerge@7.0.2:
resolution: {integrity: sha512-akcpDTPuez4xzULo5NwuoKwYRtjQJ9eoNfBACiBMaXwNAx7B1PKfe5wqUFJuW5uKzQ68YjDFwPaWHDG1KnFGsA==}
engines: {node: '>=14.13.1'}
tsconfck@3.1.4:
resolution: {integrity: sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==}
engines: {node: ^18 || >=20}
@ -4191,6 +4214,9 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
xior@0.6.3:
resolution: {integrity: sha512-WxDMGk7W2duFoCS0M59Pll/BIGfWiadZp4MMEY0/56K+3Vz400DUTiEZLpuaVcSnv+pCSz05MJz8kohn8wivhg==}
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
@ -6514,6 +6540,12 @@ snapshots:
semver: 7.7.0
strip-indent: 3.0.0
eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.22.0(@typescript-eslint/parser@8.22.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1):
dependencies:
eslint: 8.57.1
optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.22.0(@typescript-eslint/parser@8.22.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)
eslint-scope@7.2.2:
dependencies:
esrecurse: 4.3.0
@ -8260,6 +8292,8 @@ snapshots:
through@2.3.8: {}
tiny-lru@11.2.11: {}
tinyexec@0.3.2: {}
to-regex-range@5.0.1:
@ -8272,6 +8306,8 @@ snapshots:
dependencies:
typescript: 5.7.3
ts-deepmerge@7.0.2: {}
tsconfck@3.1.4(typescript@5.7.3):
optionalDependencies:
typescript: 5.7.3
@ -8528,6 +8564,11 @@ snapshots:
wrappy@1.0.2: {}
xior@0.6.3:
dependencies:
tiny-lru: 11.2.11
ts-deepmerge: 7.0.2
xtend@4.0.2: {}
y18n@5.0.8: {}