gencow 0.1.120 → 0.1.121
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/core/index.js +62 -20
- package/package.json +31 -32
- package/server/index.js +82 -27
- package/server/index.js.map +2 -2
- package/dashboard/apple-touch-icon.png +0 -0
- package/dashboard/assets/index-Dv-kFCfN.js +0 -372
- package/dashboard/assets/index-treFoOsZ.css +0 -1
- package/dashboard/favicon-16.png +0 -0
- package/dashboard/favicon-192.png +0 -0
- package/dashboard/favicon-32.png +0 -0
- package/dashboard/favicon-512.png +0 -0
- package/dashboard/favicon.ico +0 -0
- package/dashboard/favicon.svg +0 -1
- package/dashboard/file.svg +0 -1
- package/dashboard/globe.svg +0 -1
- package/dashboard/index.html +0 -23
- package/dashboard/next.svg +0 -1
- package/dashboard/vercel.svg +0 -1
- package/dashboard/window.svg +0 -1
package/core/index.js
CHANGED
|
@@ -1928,7 +1928,7 @@ function ownerRls(userIdColumn, options) {
|
|
|
1928
1928
|
"[ownerRls] userIdColumn must have a .name property. Ensure you pass a valid Drizzle column reference (e.g. t.userId)."
|
|
1929
1929
|
);
|
|
1930
1930
|
}
|
|
1931
|
-
const isOwner = sql`${userIdColumn} = current_setting('app.current_user_id')`;
|
|
1931
|
+
const isOwner = sql`${userIdColumn} = current_setting('app.current_user_id', true)`;
|
|
1932
1932
|
const meta = {
|
|
1933
1933
|
columnName: colName,
|
|
1934
1934
|
readPublic: options?.read === "public"
|
|
@@ -1966,6 +1966,10 @@ function createRlsDb(db, userId) {
|
|
|
1966
1966
|
// ../core/src/crud.ts
|
|
1967
1967
|
import { eq, ne, gt, gte, lt, lte, desc, asc, like, ilike, inArray, notInArray, or, and, count as drizzleCount, getTableName, getTableColumns } from "drizzle-orm";
|
|
1968
1968
|
import { getTableConfig } from "drizzle-orm/pg-core";
|
|
1969
|
+
var _ownerRlsTables = [];
|
|
1970
|
+
function getOwnerRlsTables() {
|
|
1971
|
+
return _ownerRlsTables;
|
|
1972
|
+
}
|
|
1969
1973
|
function detectIdType(column) {
|
|
1970
1974
|
const colType = column.dataType;
|
|
1971
1975
|
if (colType === "string") return v.string();
|
|
@@ -2001,6 +2005,10 @@ function detectOwnerMeta(table) {
|
|
|
2001
2005
|
registerOwnerRls(table, { columnName: colName, readPublic: false });
|
|
2002
2006
|
return { column: userIdCol, columnName: colName, propertyName: propName, readPublic: false };
|
|
2003
2007
|
}
|
|
2008
|
+
const tblName = getTableName(table);
|
|
2009
|
+
console.warn(
|
|
2010
|
+
`[crud] \u26A0\uFE0F Table "${tblName}" has ${config.policies.length} pgPolicy but no userId/user_id column found. ownerRls auto-isolation will NOT be applied. If you used ownerRls(), ensure the column is named 'userId' (JS) / 'user_id' (DB).`
|
|
2011
|
+
);
|
|
2004
2012
|
}
|
|
2005
2013
|
} catch {
|
|
2006
2014
|
}
|
|
@@ -2088,6 +2096,13 @@ function crud(table, options) {
|
|
|
2088
2096
|
const defaultOrderCol = createdAtCol || pk;
|
|
2089
2097
|
const userIdCol = anyTable["userId"];
|
|
2090
2098
|
const ownerMeta = detectOwnerMeta(table);
|
|
2099
|
+
if (ownerMeta && !_ownerRlsTables.some((t) => t.tableName === tableName)) {
|
|
2100
|
+
_ownerRlsTables.push({
|
|
2101
|
+
tableName,
|
|
2102
|
+
columnName: ownerMeta.columnName,
|
|
2103
|
+
readPublic: ownerMeta.readPublic
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2091
2106
|
if (ownerMeta && isPublic && !ownerMeta.readPublic) {
|
|
2092
2107
|
console.warn(
|
|
2093
2108
|
`[crud] \u26A0\uFE0F Table "${tableName}": ownerRls detected but public=true. CUD operations will still enforce ownerRls (auth required). Consider removing { public: true } or using ownerRls(col, { read: "public" }).`
|
|
@@ -2116,15 +2131,23 @@ function crud(table, options) {
|
|
|
2116
2131
|
}
|
|
2117
2132
|
return conditions.length > 0 ? and(...conditions) : void 0;
|
|
2118
2133
|
}
|
|
2134
|
+
async function inRlsOrPlainTx(db, fn) {
|
|
2135
|
+
if (typeof db?.transaction === "function") {
|
|
2136
|
+
return await db.transaction(fn);
|
|
2137
|
+
}
|
|
2138
|
+
return await fn(db);
|
|
2139
|
+
}
|
|
2119
2140
|
async function fetchListWithTotal(db, whereClause, userId) {
|
|
2120
2141
|
let effectiveWhere = whereClause;
|
|
2121
2142
|
if (ownerMeta && userId && !ownerMeta.readPublic) {
|
|
2122
2143
|
const ownerFilter = eq(ownerMeta.column, userId);
|
|
2123
2144
|
effectiveWhere = effectiveWhere ? and(effectiveWhere, ownerFilter) : ownerFilter;
|
|
2124
2145
|
}
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2146
|
+
return await inRlsOrPlainTx(db, async (tx) => {
|
|
2147
|
+
const data = await tx.select().from(anyTable).where(effectiveWhere).orderBy(desc(defaultOrderCol));
|
|
2148
|
+
const countResult = await tx.select({ count: drizzleCount() }).from(anyTable).where(effectiveWhere);
|
|
2149
|
+
return { data, total: Number(countResult[0]?.count ?? 0) };
|
|
2150
|
+
});
|
|
2128
2151
|
}
|
|
2129
2152
|
const enabledMethods = new Set(options?.methods ?? ["list", "get", "create", "update", "remove"]);
|
|
2130
2153
|
const listDef = !enabledMethods.has("list") ? void 0 : query(`${prefix}.list`, {
|
|
@@ -2155,12 +2178,14 @@ function crud(table, options) {
|
|
|
2155
2178
|
} else {
|
|
2156
2179
|
orderByClause = desc(defaultOrderCol);
|
|
2157
2180
|
}
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2181
|
+
return await inRlsOrPlainTx(ctx.db, async (tx) => {
|
|
2182
|
+
const results = await tx.select().from(anyTable).where(whereClause).orderBy(orderByClause).limit(limit).offset(offset);
|
|
2183
|
+
const countResult = await tx.select({ count: drizzleCount() }).from(anyTable).where(whereClause);
|
|
2184
|
+
return {
|
|
2185
|
+
data: results,
|
|
2186
|
+
total: Number(countResult[0]?.count ?? 0)
|
|
2187
|
+
};
|
|
2188
|
+
});
|
|
2164
2189
|
}
|
|
2165
2190
|
});
|
|
2166
2191
|
const getDef = !enabledMethods.has("get") ? void 0 : query(`${prefix}.get`, {
|
|
@@ -2177,8 +2202,10 @@ function crud(table, options) {
|
|
|
2177
2202
|
const sdField = anyTable[options.softDelete.field];
|
|
2178
2203
|
whereCond = and(whereCond, eq(sdField, null));
|
|
2179
2204
|
}
|
|
2180
|
-
|
|
2181
|
-
|
|
2205
|
+
return await inRlsOrPlainTx(ctx.db, async (tx) => {
|
|
2206
|
+
const [result] = await tx.select().from(anyTable).where(whereCond).limit(1);
|
|
2207
|
+
return result ?? null;
|
|
2208
|
+
});
|
|
2182
2209
|
}
|
|
2183
2210
|
});
|
|
2184
2211
|
const createDef = !enabledMethods.has("create") ? void 0 : mutation(`${prefix}.create`, {
|
|
@@ -2186,6 +2213,12 @@ function crud(table, options) {
|
|
|
2186
2213
|
handler: async (ctx, args) => {
|
|
2187
2214
|
const user = ownerMeta || !isPublic ? ctx.auth.requireAuth() : null;
|
|
2188
2215
|
let insertData = { ...args };
|
|
2216
|
+
if (ownerMeta && user) {
|
|
2217
|
+
const requestedOwner = insertData[ownerMeta.propertyName] ?? insertData[ownerMeta.columnName] ?? insertData.userId;
|
|
2218
|
+
if (requestedOwner != null && requestedOwner !== user.id) {
|
|
2219
|
+
throw new Error("Forbidden: cannot create resource for another user");
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2189
2222
|
if (ownerMeta && user) {
|
|
2190
2223
|
insertData[ownerMeta.propertyName] = user.id;
|
|
2191
2224
|
} else if (userIdCol && user && !insertData.userId) {
|
|
@@ -2194,7 +2227,10 @@ function crud(table, options) {
|
|
|
2194
2227
|
if (options?.hooks?.beforeCreate) {
|
|
2195
2228
|
insertData = await options.hooks.beforeCreate(insertData);
|
|
2196
2229
|
}
|
|
2197
|
-
const [result] = await
|
|
2230
|
+
const [result] = await inRlsOrPlainTx(
|
|
2231
|
+
ctx.db,
|
|
2232
|
+
async (tx) => tx.insert(anyTable).values(insertData).returning()
|
|
2233
|
+
);
|
|
2198
2234
|
if (useRealtime && enabledMethods.has("list")) {
|
|
2199
2235
|
const currentUserId = user?.id;
|
|
2200
2236
|
const listResult = await fetchListWithTotal(ctx.db, void 0, currentUserId);
|
|
@@ -2223,7 +2259,10 @@ function crud(table, options) {
|
|
|
2223
2259
|
if (options?.hooks?.beforeUpdate) {
|
|
2224
2260
|
updateData = await options.hooks.beforeUpdate(updateData);
|
|
2225
2261
|
}
|
|
2226
|
-
const [result] = await
|
|
2262
|
+
const [result] = await inRlsOrPlainTx(
|
|
2263
|
+
ctx.db,
|
|
2264
|
+
async (tx) => tx.update(anyTable).set(updateData).where(updateWhere).returning()
|
|
2265
|
+
);
|
|
2227
2266
|
if (useRealtime) {
|
|
2228
2267
|
const currentUserId = ownerMeta ? user?.id : void 0;
|
|
2229
2268
|
if (enabledMethods.has("list")) {
|
|
@@ -2245,12 +2284,14 @@ function crud(table, options) {
|
|
|
2245
2284
|
if (ownerMeta && user) {
|
|
2246
2285
|
deleteWhere = and(eq(pk, args.id), eq(ownerMeta.column, user.id));
|
|
2247
2286
|
}
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2287
|
+
await inRlsOrPlainTx(ctx.db, async (tx) => {
|
|
2288
|
+
if (options?.softDelete) {
|
|
2289
|
+
const sdField = options.softDelete.field;
|
|
2290
|
+
await tx.update(anyTable).set({ [sdField]: /* @__PURE__ */ new Date() }).where(deleteWhere);
|
|
2291
|
+
} else {
|
|
2292
|
+
await tx.delete(anyTable).where(deleteWhere);
|
|
2293
|
+
}
|
|
2294
|
+
});
|
|
2254
2295
|
if (useRealtime && enabledMethods.has("list")) {
|
|
2255
2296
|
const currentUserId = ownerMeta ? user?.id : void 0;
|
|
2256
2297
|
const listResult = await fetchListWithTotal(ctx.db, void 0, currentUserId);
|
|
@@ -2279,6 +2320,7 @@ export {
|
|
|
2279
2320
|
deregisterClient,
|
|
2280
2321
|
crud as gencowCrud,
|
|
2281
2322
|
getOwnerRlsMeta,
|
|
2323
|
+
getOwnerRlsTables,
|
|
2282
2324
|
getQueryDef,
|
|
2283
2325
|
getQueryHandler,
|
|
2284
2326
|
getRegisteredHttpActions,
|
package/package.json
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
2
|
+
"name": "gencow",
|
|
3
|
+
"version": "0.1.121",
|
|
4
|
+
"description": "Gencow — AI Backend Engine",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"gencow": "./bin/gencow.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"lib/",
|
|
12
|
+
"templates/",
|
|
13
|
+
"server/",
|
|
14
|
+
"core/",
|
|
15
|
+
"scripts/",
|
|
16
|
+
"dashboard/"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
20
|
+
"open": "^10.1.0",
|
|
21
|
+
"tar": "^7",
|
|
22
|
+
"ws": "^8.19.0",
|
|
23
|
+
"zod": "^4.3.6"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^25",
|
|
27
|
+
"esbuild": "^0.27.3"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "node scripts/bundle-server.mjs"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/server/index.js
CHANGED
|
@@ -1968,7 +1968,7 @@ function ownerRls(userIdColumn, options) {
|
|
|
1968
1968
|
"[ownerRls] userIdColumn must have a .name property. Ensure you pass a valid Drizzle column reference (e.g. t.userId)."
|
|
1969
1969
|
);
|
|
1970
1970
|
}
|
|
1971
|
-
const isOwner = sql`${userIdColumn} = current_setting('app.current_user_id')`;
|
|
1971
|
+
const isOwner = sql`${userIdColumn} = current_setting('app.current_user_id', true)`;
|
|
1972
1972
|
const meta3 = {
|
|
1973
1973
|
columnName: colName,
|
|
1974
1974
|
readPublic: options?.read === "public"
|
|
@@ -2018,6 +2018,9 @@ var init_rls_db = __esm({
|
|
|
2018
2018
|
// ../core/src/crud.ts
|
|
2019
2019
|
import { eq, ne, gt, gte, lt, lte, desc, asc, like, ilike, inArray, notInArray, or, and, count as drizzleCount, getTableName, getTableColumns } from "drizzle-orm";
|
|
2020
2020
|
import { getTableConfig } from "drizzle-orm/pg-core";
|
|
2021
|
+
function getOwnerRlsTables() {
|
|
2022
|
+
return _ownerRlsTables;
|
|
2023
|
+
}
|
|
2021
2024
|
function detectIdType(column) {
|
|
2022
2025
|
const colType = column.dataType;
|
|
2023
2026
|
if (colType === "string") return v.string();
|
|
@@ -2053,6 +2056,10 @@ function detectOwnerMeta(table) {
|
|
|
2053
2056
|
registerOwnerRls(table, { columnName: colName, readPublic: false });
|
|
2054
2057
|
return { column: userIdCol, columnName: colName, propertyName: propName, readPublic: false };
|
|
2055
2058
|
}
|
|
2059
|
+
const tblName = getTableName(table);
|
|
2060
|
+
console.warn(
|
|
2061
|
+
`[crud] \u26A0\uFE0F Table "${tblName}" has ${config2.policies.length} pgPolicy but no userId/user_id column found. ownerRls auto-isolation will NOT be applied. If you used ownerRls(), ensure the column is named 'userId' (JS) / 'user_id' (DB).`
|
|
2062
|
+
);
|
|
2056
2063
|
}
|
|
2057
2064
|
} catch {
|
|
2058
2065
|
}
|
|
@@ -2138,6 +2145,13 @@ function crud(table, options) {
|
|
|
2138
2145
|
const defaultOrderCol = createdAtCol || pk;
|
|
2139
2146
|
const userIdCol = anyTable["userId"];
|
|
2140
2147
|
const ownerMeta = detectOwnerMeta(table);
|
|
2148
|
+
if (ownerMeta && !_ownerRlsTables.some((t) => t.tableName === tableName)) {
|
|
2149
|
+
_ownerRlsTables.push({
|
|
2150
|
+
tableName,
|
|
2151
|
+
columnName: ownerMeta.columnName,
|
|
2152
|
+
readPublic: ownerMeta.readPublic
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2141
2155
|
if (ownerMeta && isPublic && !ownerMeta.readPublic) {
|
|
2142
2156
|
console.warn(
|
|
2143
2157
|
`[crud] \u26A0\uFE0F Table "${tableName}": ownerRls detected but public=true. CUD operations will still enforce ownerRls (auth required). Consider removing { public: true } or using ownerRls(col, { read: "public" }).`
|
|
@@ -2166,15 +2180,23 @@ function crud(table, options) {
|
|
|
2166
2180
|
}
|
|
2167
2181
|
return conditions.length > 0 ? and(...conditions) : void 0;
|
|
2168
2182
|
}
|
|
2183
|
+
async function inRlsOrPlainTx(db, fn) {
|
|
2184
|
+
if (typeof db?.transaction === "function") {
|
|
2185
|
+
return await db.transaction(fn);
|
|
2186
|
+
}
|
|
2187
|
+
return await fn(db);
|
|
2188
|
+
}
|
|
2169
2189
|
async function fetchListWithTotal(db, whereClause, userId) {
|
|
2170
2190
|
let effectiveWhere = whereClause;
|
|
2171
2191
|
if (ownerMeta && userId && !ownerMeta.readPublic) {
|
|
2172
2192
|
const ownerFilter = eq(ownerMeta.column, userId);
|
|
2173
2193
|
effectiveWhere = effectiveWhere ? and(effectiveWhere, ownerFilter) : ownerFilter;
|
|
2174
2194
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2195
|
+
return await inRlsOrPlainTx(db, async (tx) => {
|
|
2196
|
+
const data = await tx.select().from(anyTable).where(effectiveWhere).orderBy(desc(defaultOrderCol));
|
|
2197
|
+
const countResult = await tx.select({ count: drizzleCount() }).from(anyTable).where(effectiveWhere);
|
|
2198
|
+
return { data, total: Number(countResult[0]?.count ?? 0) };
|
|
2199
|
+
});
|
|
2178
2200
|
}
|
|
2179
2201
|
const enabledMethods = new Set(options?.methods ?? ["list", "get", "create", "update", "remove"]);
|
|
2180
2202
|
const listDef = !enabledMethods.has("list") ? void 0 : query(`${prefix}.list`, {
|
|
@@ -2205,12 +2227,14 @@ function crud(table, options) {
|
|
|
2205
2227
|
} else {
|
|
2206
2228
|
orderByClause = desc(defaultOrderCol);
|
|
2207
2229
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2230
|
+
return await inRlsOrPlainTx(ctx.db, async (tx) => {
|
|
2231
|
+
const results = await tx.select().from(anyTable).where(whereClause).orderBy(orderByClause).limit(limit).offset(offset);
|
|
2232
|
+
const countResult = await tx.select({ count: drizzleCount() }).from(anyTable).where(whereClause);
|
|
2233
|
+
return {
|
|
2234
|
+
data: results,
|
|
2235
|
+
total: Number(countResult[0]?.count ?? 0)
|
|
2236
|
+
};
|
|
2237
|
+
});
|
|
2214
2238
|
}
|
|
2215
2239
|
});
|
|
2216
2240
|
const getDef = !enabledMethods.has("get") ? void 0 : query(`${prefix}.get`, {
|
|
@@ -2227,8 +2251,10 @@ function crud(table, options) {
|
|
|
2227
2251
|
const sdField = anyTable[options.softDelete.field];
|
|
2228
2252
|
whereCond = and(whereCond, eq(sdField, null));
|
|
2229
2253
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2254
|
+
return await inRlsOrPlainTx(ctx.db, async (tx) => {
|
|
2255
|
+
const [result] = await tx.select().from(anyTable).where(whereCond).limit(1);
|
|
2256
|
+
return result ?? null;
|
|
2257
|
+
});
|
|
2232
2258
|
}
|
|
2233
2259
|
});
|
|
2234
2260
|
const createDef = !enabledMethods.has("create") ? void 0 : mutation(`${prefix}.create`, {
|
|
@@ -2236,6 +2262,12 @@ function crud(table, options) {
|
|
|
2236
2262
|
handler: async (ctx, args) => {
|
|
2237
2263
|
const user2 = ownerMeta || !isPublic ? ctx.auth.requireAuth() : null;
|
|
2238
2264
|
let insertData = { ...args };
|
|
2265
|
+
if (ownerMeta && user2) {
|
|
2266
|
+
const requestedOwner = insertData[ownerMeta.propertyName] ?? insertData[ownerMeta.columnName] ?? insertData.userId;
|
|
2267
|
+
if (requestedOwner != null && requestedOwner !== user2.id) {
|
|
2268
|
+
throw new Error("Forbidden: cannot create resource for another user");
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2239
2271
|
if (ownerMeta && user2) {
|
|
2240
2272
|
insertData[ownerMeta.propertyName] = user2.id;
|
|
2241
2273
|
} else if (userIdCol && user2 && !insertData.userId) {
|
|
@@ -2244,7 +2276,10 @@ function crud(table, options) {
|
|
|
2244
2276
|
if (options?.hooks?.beforeCreate) {
|
|
2245
2277
|
insertData = await options.hooks.beforeCreate(insertData);
|
|
2246
2278
|
}
|
|
2247
|
-
const [result] = await
|
|
2279
|
+
const [result] = await inRlsOrPlainTx(
|
|
2280
|
+
ctx.db,
|
|
2281
|
+
async (tx) => tx.insert(anyTable).values(insertData).returning()
|
|
2282
|
+
);
|
|
2248
2283
|
if (useRealtime && enabledMethods.has("list")) {
|
|
2249
2284
|
const currentUserId = user2?.id;
|
|
2250
2285
|
const listResult = await fetchListWithTotal(ctx.db, void 0, currentUserId);
|
|
@@ -2273,7 +2308,10 @@ function crud(table, options) {
|
|
|
2273
2308
|
if (options?.hooks?.beforeUpdate) {
|
|
2274
2309
|
updateData = await options.hooks.beforeUpdate(updateData);
|
|
2275
2310
|
}
|
|
2276
|
-
const [result] = await
|
|
2311
|
+
const [result] = await inRlsOrPlainTx(
|
|
2312
|
+
ctx.db,
|
|
2313
|
+
async (tx) => tx.update(anyTable).set(updateData).where(updateWhere).returning()
|
|
2314
|
+
);
|
|
2277
2315
|
if (useRealtime) {
|
|
2278
2316
|
const currentUserId = ownerMeta ? user2?.id : void 0;
|
|
2279
2317
|
if (enabledMethods.has("list")) {
|
|
@@ -2295,12 +2333,14 @@ function crud(table, options) {
|
|
|
2295
2333
|
if (ownerMeta && user2) {
|
|
2296
2334
|
deleteWhere = and(eq(pk, args.id), eq(ownerMeta.column, user2.id));
|
|
2297
2335
|
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2336
|
+
await inRlsOrPlainTx(ctx.db, async (tx) => {
|
|
2337
|
+
if (options?.softDelete) {
|
|
2338
|
+
const sdField = options.softDelete.field;
|
|
2339
|
+
await tx.update(anyTable).set({ [sdField]: /* @__PURE__ */ new Date() }).where(deleteWhere);
|
|
2340
|
+
} else {
|
|
2341
|
+
await tx.delete(anyTable).where(deleteWhere);
|
|
2342
|
+
}
|
|
2343
|
+
});
|
|
2304
2344
|
if (useRealtime && enabledMethods.has("list")) {
|
|
2305
2345
|
const currentUserId = ownerMeta ? user2?.id : void 0;
|
|
2306
2346
|
const listResult = await fetchListWithTotal(ctx.db, void 0, currentUserId);
|
|
@@ -2317,13 +2357,14 @@ function crud(table, options) {
|
|
|
2317
2357
|
remove: removeDef
|
|
2318
2358
|
};
|
|
2319
2359
|
}
|
|
2320
|
-
var MAX_FILTER_DEPTH, FILTER_OPS;
|
|
2360
|
+
var _ownerRlsTables, MAX_FILTER_DEPTH, FILTER_OPS;
|
|
2321
2361
|
var init_crud = __esm({
|
|
2322
2362
|
"../core/src/crud.ts"() {
|
|
2323
2363
|
"use strict";
|
|
2324
2364
|
init_reactive();
|
|
2325
2365
|
init_v();
|
|
2326
2366
|
init_rls();
|
|
2367
|
+
_ownerRlsTables = [];
|
|
2327
2368
|
MAX_FILTER_DEPTH = 5;
|
|
2328
2369
|
FILTER_OPS = ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "like", "ilike"];
|
|
2329
2370
|
}
|
|
@@ -2343,6 +2384,7 @@ __export(src_exports, {
|
|
|
2343
2384
|
deregisterClient: () => deregisterClient,
|
|
2344
2385
|
gencowCrud: () => crud,
|
|
2345
2386
|
getOwnerRlsMeta: () => getOwnerRlsMeta,
|
|
2387
|
+
getOwnerRlsTables: () => getOwnerRlsTables,
|
|
2346
2388
|
getQueryDef: () => getQueryDef,
|
|
2347
2389
|
getQueryHandler: () => getQueryHandler,
|
|
2348
2390
|
getRegisteredHttpActions: () => getRegisteredHttpActions,
|
|
@@ -61278,9 +61320,7 @@ var ALLOWED_NODE_MODULES = /* @__PURE__ */ new Set([
|
|
|
61278
61320
|
"node:timers",
|
|
61279
61321
|
"node:timers/promises",
|
|
61280
61322
|
"node:zlib",
|
|
61281
|
-
// 서드파티 패키지 호환을 위해 허용 (
|
|
61282
|
-
"node:fs",
|
|
61283
|
-
"node:fs/promises",
|
|
61323
|
+
// 서드파티 패키지 호환을 위해 허용 (cowbox Landlock TCP가 보안 담당)
|
|
61284
61324
|
"node:http",
|
|
61285
61325
|
"node:https",
|
|
61286
61326
|
"node:http2",
|
|
@@ -61300,8 +61340,6 @@ var ALLOWED_NODE_MODULES = /* @__PURE__ */ new Set([
|
|
|
61300
61340
|
"string_decoder",
|
|
61301
61341
|
"timers",
|
|
61302
61342
|
"zlib",
|
|
61303
|
-
"fs",
|
|
61304
|
-
"fs/promises",
|
|
61305
61343
|
"http",
|
|
61306
61344
|
"https",
|
|
61307
61345
|
"http2",
|
|
@@ -62531,7 +62569,13 @@ async function main() {
|
|
|
62531
62569
|
printFrontendAntiPatternReport(frontendResult);
|
|
62532
62570
|
}
|
|
62533
62571
|
_t("pre-import");
|
|
62534
|
-
|
|
62572
|
+
let importPath = functionsPath;
|
|
62573
|
+
if (existsSync4(resolve6(functionsPath, "index.js"))) {
|
|
62574
|
+
importPath = resolve6(functionsPath, "index.js");
|
|
62575
|
+
} else if (existsSync4(resolve6(functionsPath, "index.ts"))) {
|
|
62576
|
+
importPath = resolve6(functionsPath, "index.ts");
|
|
62577
|
+
}
|
|
62578
|
+
appModule = await import(importPath);
|
|
62535
62579
|
_t("post-import");
|
|
62536
62580
|
const regQ = getRegisteredQueries().length;
|
|
62537
62581
|
const regM = getRegisteredMutations().length;
|
|
@@ -63959,6 +64003,17 @@ async function main() {
|
|
|
63959
64003
|
console.log(`[gencow] \u2192 \u{1F4C4} gencow/SECURITY.md \uCC38\uACE0`);
|
|
63960
64004
|
}
|
|
63961
64005
|
console.log(`[gencow] \u{1F512} Secure by Default: ${protectedQueryCount} queries + ${protectedMutationCount} mutations require auth`);
|
|
64006
|
+
const rlsTables = getOwnerRlsTables();
|
|
64007
|
+
if (rlsTables.length > 0) {
|
|
64008
|
+
console.log(``);
|
|
64009
|
+
console.log(`[gencow] \u{1F6E1}\uFE0F ownerRls \u2014 2-Layer Data Isolation:`);
|
|
64010
|
+
for (const t of rlsTables) {
|
|
64011
|
+
const mode = t.readPublic ? "read: public, CUD: owner" : "full owner isolation";
|
|
64012
|
+
console.log(`[gencow] \u2713 ${t.tableName} (${t.columnName}) \u2014 ${mode}`);
|
|
64013
|
+
}
|
|
64014
|
+
console.log(`[gencow] Layer 1: crud() auto-filters all queries by userId`);
|
|
64015
|
+
console.log(`[gencow] Layer 2: PostgreSQL RLS policies (set_config in transactions)`);
|
|
64016
|
+
}
|
|
63962
64017
|
const _aiKey = process.env.OPENAI_API_KEY;
|
|
63963
64018
|
if (_aiKey && (_aiKey.length < 20 || /^sk-\.{2,}$/.test(_aiKey) || _aiKey.includes("your-key") || _aiKey.includes("YOUR_KEY"))) {
|
|
63964
64019
|
console.warn(`[gencow] \u26A0\uFE0F OPENAI_API_KEY\uAC00 \uD50C\uB808\uC774\uC2A4\uD640\uB354\uC785\uB2C8\uB2E4.`);
|