@kava/kava-api-core 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +40 -0
- package/dist/{auth.util.d.ts → auth/auth.d.ts} +1 -5
- package/dist/{auth.util.js → auth/auth.js} +38 -30
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/{context.util.js → context/context.js} +1 -1
- package/dist/context/context.js.map +1 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +2 -0
- package/dist/context/index.js.map +1 -0
- package/dist/{controller.util.js → controller/controller.js} +1 -1
- package/dist/controller/controller.js.map +1 -0
- package/dist/controller/index.d.ts +1 -0
- package/dist/controller/index.js +2 -0
- package/dist/controller/index.js.map +1 -0
- package/dist/{conversion.util.js → conversion/conversion.js} +1 -1
- package/dist/conversion/conversion.js.map +1 -0
- package/dist/conversion/index.d.ts +1 -0
- package/dist/conversion/index.js +2 -0
- package/dist/conversion/index.js.map +1 -0
- package/dist/{db.util.d.ts → db/db.d.ts} +9 -5
- package/dist/{db.util.js → db/db.js} +40 -29
- package/dist/db/db.js.map +1 -0
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.js +2 -0
- package/dist/db/index.js.map +1 -0
- package/dist/index.d.ts +14 -13
- package/dist/index.js +14 -13
- package/dist/index.js.map +1 -1
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.js +2 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/{logger.util.js → logger/logger.js} +16 -7
- package/dist/logger/logger.js.map +1 -0
- package/dist/mail/index.d.ts +1 -0
- package/dist/mail/index.js +2 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/{mail.util.js → mail/mail.js} +1 -1
- package/dist/mail/mail.js.map +1 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/{middleware.util.js → middleware/middleware.js} +1 -1
- package/dist/middleware/middleware.js.map +1 -0
- package/dist/model/index.d.ts +1 -0
- package/dist/model/index.js +2 -0
- package/dist/model/index.js.map +1 -0
- package/dist/{model.util.js → model/model.js} +1 -1
- package/dist/model/model.js.map +1 -0
- package/dist/permission/index.d.ts +1 -0
- package/dist/permission/index.js +2 -0
- package/dist/permission/index.js.map +1 -0
- package/dist/{permission.util.js → permission/permission.js} +1 -1
- package/dist/permission/permission.js.map +1 -0
- package/dist/registry/index.d.ts +1 -0
- package/dist/registry/index.js +2 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/registry.d.ts +28 -0
- package/dist/registry/registry.js +19 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/route/index.d.ts +1 -0
- package/dist/route/index.js +2 -0
- package/dist/route/index.js.map +1 -0
- package/dist/{route.util.js → route/route.js} +1 -1
- package/dist/route/route.js.map +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/{storage.util.js → storage/storage.js} +2 -2
- package/dist/storage/storage.js.map +1 -0
- package/dist/validation/index.d.ts +1 -0
- package/dist/validation/index.js +2 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/{validation.util.js → validation/validation.js} +1 -1
- package/dist/validation/validation.js.map +1 -0
- package/package.json +2 -2
- package/src/{auth.util.ts → auth/auth.ts} +255 -241
- package/src/auth/index.ts +1 -0
- package/src/{context.util.ts → context/context.ts} +17 -17
- package/src/context/index.ts +1 -0
- package/src/{controller.util.ts → controller/controller.ts} +236 -236
- package/src/controller/index.ts +1 -0
- package/src/{conversion.util.ts → conversion/conversion.ts} +64 -64
- package/src/conversion/index.ts +1 -0
- package/src/{db.util.ts → db/db.ts} +420 -405
- package/src/db/index.ts +1 -0
- package/src/index.ts +14 -13
- package/src/logger/index.ts +1 -0
- package/src/{logger.util.ts → logger/logger.ts} +176 -169
- package/src/mail/index.ts +1 -0
- package/src/{mail.util.ts → mail/mail.ts} +85 -85
- package/src/middleware/index.ts +1 -0
- package/src/{middleware.util.ts → middleware/middleware.ts} +288 -288
- package/src/model/index.ts +1 -0
- package/src/{model.util.ts → model/model.ts} +2210 -2210
- package/src/permission/index.ts +1 -0
- package/src/{permission.util.ts → permission/permission.ts} +136 -136
- package/src/registry/index.ts +1 -0
- package/src/registry/registry.ts +37 -0
- package/src/route/index.ts +1 -0
- package/src/{route.util.ts → route/route.ts} +11 -11
- package/src/storage/index.ts +1 -0
- package/src/{storage.util.ts → storage/storage.ts} +101 -101
- package/src/validation/index.ts +1 -0
- package/src/{validation.util.ts → validation/validation.ts} +338 -338
- package/tsconfig.json +1 -1
- package/bun.lock +0 -160
- package/dist/auth.util.js.map +0 -1
- package/dist/context.util.js.map +0 -1
- package/dist/controller.util.js.map +0 -1
- package/dist/conversion.util.js.map +0 -1
- package/dist/db.util.js.map +0 -1
- package/dist/logger.util.js.map +0 -1
- package/dist/mail.util.js.map +0 -1
- package/dist/middleware.util.js.map +0 -1
- package/dist/model.util.js.map +0 -1
- package/dist/permission.util.js.map +0 -1
- package/dist/route.util.js.map +0 -1
- package/dist/storage.util.js.map +0 -1
- package/dist/validation.util.js.map +0 -1
- /package/dist/{context.util.d.ts → context/context.d.ts} +0 -0
- /package/dist/{controller.util.d.ts → controller/controller.d.ts} +0 -0
- /package/dist/{conversion.util.d.ts → conversion/conversion.d.ts} +0 -0
- /package/dist/{logger.util.d.ts → logger/logger.d.ts} +0 -0
- /package/dist/{mail.util.d.ts → mail/mail.d.ts} +0 -0
- /package/dist/{middleware.util.d.ts → middleware/middleware.d.ts} +0 -0
- /package/dist/{model.util.d.ts → model/model.d.ts} +0 -0
- /package/dist/{permission.util.d.ts → permission/permission.d.ts} +0 -0
- /package/dist/{route.util.d.ts → route/route.d.ts} +0 -0
- /package/dist/{storage.util.d.ts → storage/storage.d.ts} +0 -0
- /package/dist/{validation.util.d.ts → validation/validation.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./permission";
|
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
import { ControllerContext } from "elysia"
|
|
2
|
-
|
|
3
|
-
type KeyDigit = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
|
|
4
|
-
export type KeyFeature = `${"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"}${KeyDigit}${KeyDigit}`
|
|
5
|
-
export type KeyAccess = `${KeyDigit}${KeyDigit}`
|
|
6
|
-
export type KeyPermission = `${KeyFeature}.${KeyAccess}` | KeyAccess
|
|
7
|
-
|
|
8
|
-
const features = new Map<KeyFeature, { key: KeyFeature; name: string }>()
|
|
9
|
-
const accesses = new Map<KeyAccess, any>()
|
|
10
|
-
|
|
11
|
-
type FeatureAccess = Partial<Record<KeyFeature, {
|
|
12
|
-
name: string
|
|
13
|
-
accesses: Partial<Record<KeyAccess, string>>
|
|
14
|
-
}>>
|
|
15
|
-
|
|
16
|
-
export const permission = {
|
|
17
|
-
register: (def: FeatureAccess) => {
|
|
18
|
-
const featureAccessMap = new Map<string, string>()
|
|
19
|
-
let defaultFeature: KeyFeature | null = null
|
|
20
|
-
|
|
21
|
-
for (const [featureKey, feature] of Object.entries(def)) {
|
|
22
|
-
if (!defaultFeature) defaultFeature = featureKey as KeyFeature
|
|
23
|
-
|
|
24
|
-
registerFeature({
|
|
25
|
-
key: featureKey as KeyFeature,
|
|
26
|
-
name: feature.name
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
for (const [accessKey, accessName] of Object.entries(feature.accesses)) {
|
|
30
|
-
const permKey =
|
|
31
|
-
`${featureKey}.${String(accessKey).padStart(2, "0")}`
|
|
32
|
-
|
|
33
|
-
registerAccess({
|
|
34
|
-
featureKey,
|
|
35
|
-
accessKey,
|
|
36
|
-
accessName,
|
|
37
|
-
permKey
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
featureAccessMap.set(
|
|
41
|
-
`${featureKey}.${accessKey}`,
|
|
42
|
-
permKey
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return createScopeApi(defaultFeature!)
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
getFeatures: () => [...features.values()],
|
|
51
|
-
|
|
52
|
-
getAccesses: () => {
|
|
53
|
-
const result: Record<string, {
|
|
54
|
-
key: string
|
|
55
|
-
name: string
|
|
56
|
-
accesses: { key: string; name: string }[]
|
|
57
|
-
}> = {}
|
|
58
|
-
|
|
59
|
-
for (const feature of features.values()) {
|
|
60
|
-
result[String(feature.key)] = {
|
|
61
|
-
key: String(feature.key),
|
|
62
|
-
name: feature.name,
|
|
63
|
-
accesses: []
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
for (const access of accesses.values()) {
|
|
68
|
-
const featureKey = String(access.featureKey)
|
|
69
|
-
|
|
70
|
-
if (!result[featureKey]) continue
|
|
71
|
-
|
|
72
|
-
result[featureKey].accesses.push({
|
|
73
|
-
key: String(access.accessKey).padStart(2, "0"),
|
|
74
|
-
name: access.accessName
|
|
75
|
-
})
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return Object.values(result)
|
|
79
|
-
},
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
function normalize(
|
|
84
|
-
raw: KeyPermission,
|
|
85
|
-
defaultFeature?: KeyFeature
|
|
86
|
-
): KeyPermission {
|
|
87
|
-
if (!raw.includes(".") && defaultFeature) {
|
|
88
|
-
return `${defaultFeature}.${String(raw).padStart(2, "0") as KeyAccess}`
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return raw
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function registerFeature(f: { key: KeyFeature; name: string }) {
|
|
95
|
-
if (!features.has(f.key)) {
|
|
96
|
-
features.set(f.key, f)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function registerAccess(a: any) {
|
|
101
|
-
if (!accesses.has(a.permKey)) {
|
|
102
|
-
accesses.set(a.permKey, a)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function createPermission(keys: KeyPermission[]) {
|
|
107
|
-
return {
|
|
108
|
-
keys,
|
|
109
|
-
|
|
110
|
-
orHave(raw: KeyPermission) {
|
|
111
|
-
return createPermission([
|
|
112
|
-
...this.keys,
|
|
113
|
-
normalize(raw) as KeyPermission
|
|
114
|
-
])
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
guard(c: ControllerContext) {
|
|
118
|
-
const permissions = new Set(c.permissions || [])
|
|
119
|
-
|
|
120
|
-
const ok = this.keys.some(k => permissions?.has(k))
|
|
121
|
-
if (!ok) {
|
|
122
|
-
c.responseForbidden()
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function createScopeApi(defaultFeature: KeyFeature) {
|
|
129
|
-
return {
|
|
130
|
-
have(raw: KeyPermission) {
|
|
131
|
-
const key = normalize(raw, defaultFeature)
|
|
132
|
-
return createPermission([key])
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
1
|
+
import { ControllerContext } from "elysia"
|
|
2
|
+
|
|
3
|
+
type KeyDigit = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
|
|
4
|
+
export type KeyFeature = `${"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"}${KeyDigit}${KeyDigit}`
|
|
5
|
+
export type KeyAccess = `${KeyDigit}${KeyDigit}`
|
|
6
|
+
export type KeyPermission = `${KeyFeature}.${KeyAccess}` | KeyAccess
|
|
7
|
+
|
|
8
|
+
const features = new Map<KeyFeature, { key: KeyFeature; name: string }>()
|
|
9
|
+
const accesses = new Map<KeyAccess, any>()
|
|
10
|
+
|
|
11
|
+
type FeatureAccess = Partial<Record<KeyFeature, {
|
|
12
|
+
name: string
|
|
13
|
+
accesses: Partial<Record<KeyAccess, string>>
|
|
14
|
+
}>>
|
|
15
|
+
|
|
16
|
+
export const permission = {
|
|
17
|
+
register: (def: FeatureAccess) => {
|
|
18
|
+
const featureAccessMap = new Map<string, string>()
|
|
19
|
+
let defaultFeature: KeyFeature | null = null
|
|
20
|
+
|
|
21
|
+
for (const [featureKey, feature] of Object.entries(def)) {
|
|
22
|
+
if (!defaultFeature) defaultFeature = featureKey as KeyFeature
|
|
23
|
+
|
|
24
|
+
registerFeature({
|
|
25
|
+
key: featureKey as KeyFeature,
|
|
26
|
+
name: feature.name
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
for (const [accessKey, accessName] of Object.entries(feature.accesses)) {
|
|
30
|
+
const permKey =
|
|
31
|
+
`${featureKey}.${String(accessKey).padStart(2, "0")}`
|
|
32
|
+
|
|
33
|
+
registerAccess({
|
|
34
|
+
featureKey,
|
|
35
|
+
accessKey,
|
|
36
|
+
accessName,
|
|
37
|
+
permKey
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
featureAccessMap.set(
|
|
41
|
+
`${featureKey}.${accessKey}`,
|
|
42
|
+
permKey
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return createScopeApi(defaultFeature!)
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
getFeatures: () => [...features.values()],
|
|
51
|
+
|
|
52
|
+
getAccesses: () => {
|
|
53
|
+
const result: Record<string, {
|
|
54
|
+
key: string
|
|
55
|
+
name: string
|
|
56
|
+
accesses: { key: string; name: string }[]
|
|
57
|
+
}> = {}
|
|
58
|
+
|
|
59
|
+
for (const feature of features.values()) {
|
|
60
|
+
result[String(feature.key)] = {
|
|
61
|
+
key: String(feature.key),
|
|
62
|
+
name: feature.name,
|
|
63
|
+
accesses: []
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const access of accesses.values()) {
|
|
68
|
+
const featureKey = String(access.featureKey)
|
|
69
|
+
|
|
70
|
+
if (!result[featureKey]) continue
|
|
71
|
+
|
|
72
|
+
result[featureKey].accesses.push({
|
|
73
|
+
key: String(access.accessKey).padStart(2, "0"),
|
|
74
|
+
name: access.accessName
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return Object.values(result)
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
function normalize(
|
|
84
|
+
raw: KeyPermission,
|
|
85
|
+
defaultFeature?: KeyFeature
|
|
86
|
+
): KeyPermission {
|
|
87
|
+
if (!raw.includes(".") && defaultFeature) {
|
|
88
|
+
return `${defaultFeature}.${String(raw).padStart(2, "0") as KeyAccess}`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return raw
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function registerFeature(f: { key: KeyFeature; name: string }) {
|
|
95
|
+
if (!features.has(f.key)) {
|
|
96
|
+
features.set(f.key, f)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function registerAccess(a: any) {
|
|
101
|
+
if (!accesses.has(a.permKey)) {
|
|
102
|
+
accesses.set(a.permKey, a)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function createPermission(keys: KeyPermission[]) {
|
|
107
|
+
return {
|
|
108
|
+
keys,
|
|
109
|
+
|
|
110
|
+
orHave(raw: KeyPermission) {
|
|
111
|
+
return createPermission([
|
|
112
|
+
...this.keys,
|
|
113
|
+
normalize(raw) as KeyPermission
|
|
114
|
+
])
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
guard(c: ControllerContext) {
|
|
118
|
+
const permissions = new Set(c.permissions || [])
|
|
119
|
+
|
|
120
|
+
const ok = this.keys.some(k => permissions?.has(k))
|
|
121
|
+
if (!ok) {
|
|
122
|
+
c.responseForbidden()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function createScopeApi(defaultFeature: KeyFeature) {
|
|
129
|
+
return {
|
|
130
|
+
have(raw: KeyPermission) {
|
|
131
|
+
const key = normalize(raw, defaultFeature)
|
|
132
|
+
return createPermission([key])
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./registry";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface RedisService {
|
|
2
|
+
get(key: string): Promise<string | null>;
|
|
3
|
+
setex(key: string, ttl: number, value: string): Promise<any>;
|
|
4
|
+
del(key: string | string[]): Promise<any>;
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface QueueService {
|
|
9
|
+
add(name: string, data: any, options?: any): Promise<any>;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Registry {
|
|
14
|
+
redis?: RedisService;
|
|
15
|
+
queue?: QueueService;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class ServiceRegistry {
|
|
20
|
+
private services: Registry = {};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Register an optional service instance.
|
|
24
|
+
*/
|
|
25
|
+
register<K extends keyof Registry>(name: K, service: Registry[K]): void {
|
|
26
|
+
this.services[name] = service;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Retrieve a registered service instance.
|
|
31
|
+
*/
|
|
32
|
+
get<K extends keyof Registry>(name: K): Registry[K] {
|
|
33
|
+
return this.services[name];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const registry = new ServiceRegistry();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./route";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// ================================>
|
|
2
|
-
// ## Route: Basic api routers
|
|
3
|
-
// ================================>
|
|
4
|
-
export function api(app: any, basePath: string, controller: any) {
|
|
5
|
-
return app.group(basePath, (group: any) => group
|
|
6
|
-
.get("/", controller.index)
|
|
7
|
-
.post("/", controller.store)
|
|
8
|
-
.get("/:id", controller.show)
|
|
9
|
-
.put("/:id", controller.update)
|
|
10
|
-
.delete("/:id", controller.destroy)
|
|
11
|
-
)
|
|
1
|
+
// ================================>
|
|
2
|
+
// ## Route: Basic api routers
|
|
3
|
+
// ================================>
|
|
4
|
+
export function api(app: any, basePath: string, controller: any) {
|
|
5
|
+
return app.group(basePath, (group: any) => group
|
|
6
|
+
.get("/", controller.index)
|
|
7
|
+
.post("/", controller.store)
|
|
8
|
+
.get("/:id", controller.show)
|
|
9
|
+
.put("/:id", controller.update)
|
|
10
|
+
.delete("/:id", controller.destroy)
|
|
11
|
+
)
|
|
12
12
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./storage";
|
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { Elysia } from "elysia";
|
|
4
|
-
import { db } from "@utils
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// ================================>
|
|
9
|
-
// ## Storage: Middleware storage handler
|
|
10
|
-
// ================================>
|
|
11
|
-
export const storage = (app: Elysia) => app.get("/storage/*", async ({ params, set, user }: Record<string, any>) => {
|
|
12
|
-
const requestedPath = params["*"];
|
|
13
|
-
const baseDir = path.resolve("storage", "public");
|
|
14
|
-
const targetPath = path.resolve(baseDir, requestedPath);
|
|
15
|
-
|
|
16
|
-
if (!targetPath.startsWith(baseDir)) {
|
|
17
|
-
set.status = 400;
|
|
18
|
-
return { error: "Invalid path" };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (fs.existsSync(targetPath)) {
|
|
22
|
-
const ext = path.extname(targetPath).toLowerCase();
|
|
23
|
-
const mimeTypes: Record<string, string> = {
|
|
24
|
-
".jpg" : "image/jpeg",
|
|
25
|
-
".jpeg" : "image/jpeg",
|
|
26
|
-
".png" : "image/png",
|
|
27
|
-
".webp" : "image/webp",
|
|
28
|
-
".gif" : "image/gif",
|
|
29
|
-
".pdf" : "application/pdf",
|
|
30
|
-
".txt" : "text/plain",
|
|
31
|
-
".json" : "application/json",
|
|
32
|
-
".svg" : "image/svg+xml",
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const buffer = fs.readFileSync(targetPath);
|
|
36
|
-
|
|
37
|
-
set.headers["Content-Type"] = mimeTypes[ext] || "application/octet-stream";
|
|
38
|
-
set.headers["Content-Length"] = buffer.length.toString();
|
|
39
|
-
|
|
40
|
-
return new Response(buffer);
|
|
41
|
-
} else {
|
|
42
|
-
const baseDir = path.resolve("storage", "private");
|
|
43
|
-
const targetPath = path.resolve(baseDir, requestedPath);
|
|
44
|
-
|
|
45
|
-
if (fs.existsSync(targetPath)) {
|
|
46
|
-
if (!user) {
|
|
47
|
-
set.status = 404
|
|
48
|
-
return { error: "File not found" };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const file = await db("storages").where({ path: requestedPath, disk: "private" }).first()
|
|
52
|
-
|
|
53
|
-
if (!file) {
|
|
54
|
-
set.status = 404
|
|
55
|
-
return { error: "File not found" }
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let hasAccess = file.user_id === user.id
|
|
59
|
-
|
|
60
|
-
if (!hasAccess) {
|
|
61
|
-
hasAccess = await db("storage_permissions").where("storage_id", file.id)
|
|
62
|
-
.andWhere((q) => {
|
|
63
|
-
q.where("user_id", user.id)
|
|
64
|
-
.orWhere("role_id", user.role_id)
|
|
65
|
-
})
|
|
66
|
-
.first().then(Boolean)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!hasAccess) {
|
|
71
|
-
set.status = 404
|
|
72
|
-
return { error: "File not found" }
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
} else {
|
|
76
|
-
set.status = 404;
|
|
77
|
-
return { error: "File not found" };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const ext = path.extname(targetPath).toLowerCase();
|
|
81
|
-
const mimeTypes: Record<string, string> = {
|
|
82
|
-
".jpg" : "image/jpeg",
|
|
83
|
-
".jpeg" : "image/jpeg",
|
|
84
|
-
".png" : "image/png",
|
|
85
|
-
".webp" : "image/webp",
|
|
86
|
-
".gif" : "image/gif",
|
|
87
|
-
".pdf" : "application/pdf",
|
|
88
|
-
".txt" : "text/plain",
|
|
89
|
-
".json" : "application/json",
|
|
90
|
-
".svg" : "image/svg+xml",
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const buffer = fs.readFileSync(targetPath);
|
|
94
|
-
|
|
95
|
-
set.headers["Content-Type"] = mimeTypes[ext] || "application/octet-stream";
|
|
96
|
-
set.headers["Content-Length"] = buffer.length.toString();
|
|
97
|
-
|
|
98
|
-
return new Response(buffer);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { Elysia } from "elysia";
|
|
4
|
+
import { db } from "@utils";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// ================================>
|
|
9
|
+
// ## Storage: Middleware storage handler
|
|
10
|
+
// ================================>
|
|
11
|
+
export const storage = (app: Elysia) => app.get("/storage/*", async ({ params, set, user }: Record<string, any>) => {
|
|
12
|
+
const requestedPath = params["*"];
|
|
13
|
+
const baseDir = path.resolve("storage", "public");
|
|
14
|
+
const targetPath = path.resolve(baseDir, requestedPath);
|
|
15
|
+
|
|
16
|
+
if (!targetPath.startsWith(baseDir)) {
|
|
17
|
+
set.status = 400;
|
|
18
|
+
return { error: "Invalid path" };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (fs.existsSync(targetPath)) {
|
|
22
|
+
const ext = path.extname(targetPath).toLowerCase();
|
|
23
|
+
const mimeTypes: Record<string, string> = {
|
|
24
|
+
".jpg" : "image/jpeg",
|
|
25
|
+
".jpeg" : "image/jpeg",
|
|
26
|
+
".png" : "image/png",
|
|
27
|
+
".webp" : "image/webp",
|
|
28
|
+
".gif" : "image/gif",
|
|
29
|
+
".pdf" : "application/pdf",
|
|
30
|
+
".txt" : "text/plain",
|
|
31
|
+
".json" : "application/json",
|
|
32
|
+
".svg" : "image/svg+xml",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const buffer = fs.readFileSync(targetPath);
|
|
36
|
+
|
|
37
|
+
set.headers["Content-Type"] = mimeTypes[ext] || "application/octet-stream";
|
|
38
|
+
set.headers["Content-Length"] = buffer.length.toString();
|
|
39
|
+
|
|
40
|
+
return new Response(buffer);
|
|
41
|
+
} else {
|
|
42
|
+
const baseDir = path.resolve("storage", "private");
|
|
43
|
+
const targetPath = path.resolve(baseDir, requestedPath);
|
|
44
|
+
|
|
45
|
+
if (fs.existsSync(targetPath)) {
|
|
46
|
+
if (!user) {
|
|
47
|
+
set.status = 404
|
|
48
|
+
return { error: "File not found" };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const file = await db("storages").where({ path: requestedPath, disk: "private" }).first()
|
|
52
|
+
|
|
53
|
+
if (!file) {
|
|
54
|
+
set.status = 404
|
|
55
|
+
return { error: "File not found" }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let hasAccess = file.user_id === user.id
|
|
59
|
+
|
|
60
|
+
if (!hasAccess) {
|
|
61
|
+
hasAccess = await db("storage_permissions").where("storage_id", file.id)
|
|
62
|
+
.andWhere((q) => {
|
|
63
|
+
q.where("user_id", user.id)
|
|
64
|
+
.orWhere("role_id", user.role_id)
|
|
65
|
+
})
|
|
66
|
+
.first().then(Boolean)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if (!hasAccess) {
|
|
71
|
+
set.status = 404
|
|
72
|
+
return { error: "File not found" }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
} else {
|
|
76
|
+
set.status = 404;
|
|
77
|
+
return { error: "File not found" };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const ext = path.extname(targetPath).toLowerCase();
|
|
81
|
+
const mimeTypes: Record<string, string> = {
|
|
82
|
+
".jpg" : "image/jpeg",
|
|
83
|
+
".jpeg" : "image/jpeg",
|
|
84
|
+
".png" : "image/png",
|
|
85
|
+
".webp" : "image/webp",
|
|
86
|
+
".gif" : "image/gif",
|
|
87
|
+
".pdf" : "application/pdf",
|
|
88
|
+
".txt" : "text/plain",
|
|
89
|
+
".json" : "application/json",
|
|
90
|
+
".svg" : "image/svg+xml",
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const buffer = fs.readFileSync(targetPath);
|
|
94
|
+
|
|
95
|
+
set.headers["Content-Type"] = mimeTypes[ext] || "application/octet-stream";
|
|
96
|
+
set.headers["Content-Length"] = buffer.length.toString();
|
|
97
|
+
|
|
98
|
+
return new Response(buffer);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
102
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./validation";
|