zet-lib 2.0.1 → 3.0.0

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/lib/csrf.js ADDED
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+
3
+ const crypto = require('crypto');
4
+
5
+ /**
6
+ * CSRF Protection Middleware
7
+ * Compatible with csurf API for easy migration
8
+ *
9
+ * @param {Object} options - Configuration options
10
+ * @param {Boolean} options.cookie - Use cookie-based storage (default: false, uses session)
11
+ * @param {String} options.cookieKey - Cookie name for secret (default: '_csrf')
12
+ * @param {String} options.value - Function to get token from request (default: reads from body/query/header)
13
+ * @param {Boolean} options.ignoreMethods - HTTP methods to ignore (default: ['GET', 'HEAD', 'OPTIONS'])
14
+ * @param {String} options.secretLength - Length of secret (default: 24)
15
+ */
16
+ function csrf(options = {}) {
17
+ const opts = {
18
+ cookie: options.cookie !== undefined ? options.cookie : false,
19
+ cookieKey: options.cookieKey || '_csrf',
20
+ value: options.value || defaultValue,
21
+ ignoreMethods: options.ignoreMethods || ['GET', 'HEAD', 'OPTIONS'],
22
+ secretLength: options.secretLength || 24
23
+ };
24
+
25
+ // Generate a secret for cookie-based storage
26
+ function generateSecret() {
27
+ return crypto.randomBytes(opts.secretLength).toString('base64');
28
+ }
29
+
30
+ // Generate CSRF token from secret
31
+ function generateToken(secret) {
32
+ return crypto
33
+ .createHash('sha1')
34
+ .update(secret + 'csrfSecret')
35
+ .digest('base64');
36
+ }
37
+
38
+ // Get secret from cookie or session
39
+ function getSecret(req) {
40
+ if (opts.cookie) {
41
+ return req.cookies && req.cookies[opts.cookieKey];
42
+ } else {
43
+ return req.session && req.session._csrfSecret;
44
+ }
45
+ }
46
+
47
+ // Set secret in cookie or session
48
+ function setSecret(req, res, secret) {
49
+ if (opts.cookie) {
50
+ // Set cookie if not already set
51
+ if (!req.cookies || !req.cookies[opts.cookieKey]) {
52
+ res.cookie(opts.cookieKey, secret, {
53
+ httpOnly: true,
54
+ secure: process.env.NODE_ENV === 'production',
55
+ sameSite: 'strict',
56
+ maxAge: 86400000 // 24 hours
57
+ });
58
+ // Also set in req.cookies for immediate access
59
+ if (!req.cookies) {
60
+ req.cookies = {};
61
+ }
62
+ req.cookies[opts.cookieKey] = secret;
63
+ }
64
+ } else {
65
+ if (!req.session) {
66
+ throw new Error('Session is required when cookie option is false');
67
+ }
68
+ if (!req.session._csrfSecret) {
69
+ req.session._csrfSecret = secret;
70
+ }
71
+ }
72
+ }
73
+
74
+ // Default function to get token from request
75
+ function defaultValue(req) {
76
+ return (req.body && req.body._csrf) ||
77
+ (req.query && req.query._csrf) ||
78
+ (req.headers['csrf-token']) ||
79
+ (req.headers['xsrf-token']) ||
80
+ (req.headers['x-csrf-token']) ||
81
+ (req.headers['x-xsrf-token']);
82
+ }
83
+
84
+ // Middleware function
85
+ return function csrfMiddleware(req, res, next) {
86
+ // Get or create secret
87
+ let secret = getSecret(req);
88
+ if (!secret) {
89
+ secret = generateSecret();
90
+ setSecret(req, res, secret);
91
+ }
92
+
93
+ // Attach csrfToken method to request
94
+ req.csrfToken = function csrfToken() {
95
+ return generateToken(getSecret(req));
96
+ };
97
+
98
+ // Skip CSRF validation for ignored methods
99
+ if (opts.ignoreMethods.indexOf(req.method) !== -1) {
100
+ return next();
101
+ }
102
+
103
+ // Skip CSRF validation for AJAX calls (X-Requested-With header)
104
+ // This allows AJAX calls without token refresh
105
+ if (req.headers['x-requested-with'] === 'XMLHttpRequest' || req.xhr) {
106
+ return next();
107
+ }
108
+
109
+ // Get token from request
110
+ const token = opts.value(req);
111
+ const secretValue = getSecret(req);
112
+ const expectedToken = generateToken(secretValue);
113
+
114
+ // Validate token
115
+ if (!token || token !== expectedToken) {
116
+ const err = new Error('Invalid CSRF token');
117
+ err.status = 403;
118
+ err.code = 'EBADCSRFTOKEN';
119
+ return next(err);
120
+ }
121
+
122
+ // Token is valid, continue
123
+ next();
124
+ };
125
+ }
126
+
127
+ // Export the csrf function
128
+ module.exports = csrf;
129
+
@@ -3,7 +3,7 @@ const router = express.Router();
3
3
  const generatorApp = require("./generatorApp");
4
4
  const configGenerator = require('./config_generator');
5
5
 
6
- const csrf = require('csurf');
6
+ const csrf = require('./csrf');
7
7
  const Model = require("./Model");
8
8
  const fs = require('fs-extra');
9
9
  //const {connection, Util} = require('zet-lib');
package/lib/index.js CHANGED
@@ -28,7 +28,8 @@ const coreModules = {
28
28
  zPage: require("./zPage"),
29
29
  zTester: require("./zTester"),
30
30
  zCache: require("./zCache"),
31
- zDropbox: require("./zDropbox")
31
+ zDropbox: require("./zDropbox"),
32
+ csrf: require("./csrf")
32
33
  };
33
34
 
34
35
  // Heavy dependencies - lazy loaded (only when accessed)
package/lib/moduleLib.js CHANGED
@@ -147,92 +147,185 @@ m.location = function (req, res, key) {
147
147
  let script = ``;
148
148
  let head = ``;
149
149
  let end = ``;
150
- end += `<script src="https://maps.googleapis.com/maps/api/js?key=${process.env.GOOGLE_KEY}&callback=initAutocompleteZmap&libraries=places&language=ID" defer></script>`;
151
- script += `let searchAddressMap;
152
- let autocompleteMap;
153
- function initAutocompleteZmap() {
154
- if(mapLocationJSON && mapLocationJSON.lat) {
155
- $("#search_map_location").val(mapLocationJSON.address1);
156
- initMap(mapLocationJSON.lat,mapLocationJSON.lon);
157
- } else {
158
- navigator.geolocation.getCurrentPosition(function(location) {
159
- initMap(location.coords.latitude,location.coords.longitude);
160
- $("#search_map_location").val("My Place");
161
- });
150
+ end =+ `<script src="https://maps.googleapis.com/maps/api/js?key=${process.env.GOOGLE_KEY}&callback=initAutocompleteZmap&libraries=places,marker&language=ID&loading=async" defer></script>`
151
+ script += `let searchAddressMap, autocompleteMap, marker, map;
152
+
153
+ function initAutocompleteZmap() {
154
+ if (mapLocationJSON && mapLocationJSON.lat) {
155
+ $("#search_map_location").val(mapLocationJSON.address1);
156
+ initMap(mapLocationJSON.lat, mapLocationJSON.lon);
157
+ } else {
158
+ navigator.geolocation.getCurrentPosition(function(position) {
159
+ initMap(position.coords.latitude, position.coords.longitude);
160
+ $("#search_map_location").val("");
161
+ });
162
+ }
163
+
164
+ searchAddressMap = document.querySelector("#search_map_location");
165
+
166
+ // Use PlaceAutocompleteElement (new API) instead of deprecated Autocomplete
167
+ // Check if PlaceAutocompleteElement is available
168
+ if (google.maps.places && google.maps.places.PlaceAutocompleteElement) {
169
+ try {
170
+ // Create PlaceAutocompleteElement programmatically
171
+ autocompleteMap = new google.maps.places.PlaceAutocompleteElement({
172
+ inputField: searchAddressMap,
173
+ componentRestrictions: {country: ["ID"]},
174
+ requestedResultFields: ["formattedAddress", "geometry", "addressComponents"]
175
+ });
176
+
177
+ // Listen for place selection event
178
+ autocompleteMap.addEventListener("gmp-placeselect", function(event) {
179
+ if (event.place) {
180
+ fillInAddressNew(event.place);
181
+ }
182
+ });
183
+
184
+ searchAddressMap.focus();
185
+ } catch (e) {
186
+ console.warn("Error initializing PlaceAutocompleteElement, falling back to Autocomplete:", e);
187
+ // Fallback to old Autocomplete API
188
+ autocompleteMap = new google.maps.places.Autocomplete(searchAddressMap, {
189
+ componentRestrictions: {country: ["ID"]},
190
+ fields: ["address_component", "geometry"]
191
+ });
192
+ searchAddressMap.focus();
193
+ autocompleteMap.addListener("place_changed", fillInAddress);
162
194
  }
163
- searchAddressMap = document.querySelector("#search_map_location");
195
+ } else {
196
+ // Fallback to old Autocomplete API if PlaceAutocompleteElement is not available
197
+ console.warn("PlaceAutocompleteElement not available, using legacy Autocomplete");
164
198
  autocompleteMap = new google.maps.places.Autocomplete(searchAddressMap, {
165
199
  componentRestrictions: {country: ["ID"]},
166
- fields: ["address_component", "geometry"],
200
+ fields: ["address_component", "geometry"]
167
201
  });
168
202
  searchAddressMap.focus();
169
203
  autocompleteMap.addListener("place_changed", fillInAddress);
170
204
  }
205
+ }
206
+
207
+ function fillInAddress() {
208
+ var place = autocompleteMap.getPlace();
209
+ var lat = place.geometry.location.lat();
210
+ var lng = place.geometry.location.lng();
211
+ initMap(lat, lng);
212
+ mapLocationJSON = {
213
+ lat: lat,
214
+ lon: lng,
215
+ address1: $("#search_map_location").val(),
216
+ address2: place.address_components
217
+ };
218
+ setAddress();
219
+ }
220
+
221
+ function fillInAddressNew(place) {
222
+ // Handle PlaceAutocompleteElement place selection
223
+ var location = place.geometry.location;
224
+ var lat = typeof location.lat === 'function' ? location.lat() : location.lat;
225
+ var lng = typeof location.lng === 'function' ? location.lng() : location.lng;
171
226
 
172
- function fillInAddress() {
173
- const place = autocompleteMap.getPlace();
174
- let lat = place.geometry.location.lat();
175
- let lon = place.geometry.location.lng();
176
- initMap(lat, lon);
177
- mapLocationJSON = {
178
- lat:lat,
179
- lon:lon,
180
- address1: $("#search_map_location").val(),
181
- address2:place.address_components,
182
- }
183
- setAddress();
184
- }
185
- function setAddress() {
186
- $("#${key}").val(JSON.stringify(mapLocationJSON));
187
- }
188
- function initMap(lat, lon) {
189
- let position = {lat: lat, lng: lon}
190
- const map = new google.maps.Map(document.getElementById("map_${key}"), {
191
- zoom: 13,
192
- center: position,
193
- draggable: true
227
+ initMap(lat, lng);
228
+ mapLocationJSON = {
229
+ lat: lat,
230
+ lon: lng,
231
+ address1: place.formattedAddress || $("#search_map_location").val(),
232
+ address2: place.addressComponents || []
233
+ };
234
+ setAddress();
235
+ }
236
+
237
+ function setAddress() {
238
+ $("#${key}").val(JSON.stringify(mapLocationJSON));
239
+ }
240
+
241
+ function initMap(lat, lng) {
242
+ var position = {lat: lat, lng: lng};
243
+
244
+ // Create map with mapId for AdvancedMarkerElement support
245
+ // If you have a custom map ID from Google Cloud Console, replace "DEMO_MAP_ID" with it
246
+ map = new google.maps.Map(document.getElementById("map_${key}"), {
247
+ zoom: 13,
248
+ center: position,
249
+ draggable: true,
250
+ mapId: "DEMO_MAP_ID" // Required for AdvancedMarkerElement - replace with your actual Map ID
251
+ });
252
+
253
+ // Use AdvancedMarkerElement instead of deprecated Marker
254
+ // Note: AdvancedMarkerElement requires mapId on the map
255
+ try {
256
+ marker = new google.maps.marker.AdvancedMarkerElement({
257
+ position: position,
258
+ map: map,
259
+ gmpDraggable: true,
260
+ title: $("#search_map_location").val() || "Location"
194
261
  });
195
- var marker;
196
- placeMarker(position, map);
197
- var infowindow = new google.maps.InfoWindow();
198
- function placeMarker(position, map) {
199
- marker = new google.maps.Marker({
200
- position: position,
201
- map: map,
202
- draggable: true,
203
- animation: google.maps.Animation.DROP,
262
+
263
+ // Handle marker drag end
264
+ marker.addListener("dragend", function(event) {
265
+ var newPosition = event.latLng || marker.position;
266
+ mapLocationJSON.lat = typeof newPosition.lat === 'function' ? newPosition.lat() : newPosition.lat;
267
+ mapLocationJSON.lon = typeof newPosition.lng === 'function' ? newPosition.lng() : newPosition.lng;
268
+
269
+ var geocoder = new google.maps.Geocoder();
270
+ geocoder.geocode({location: newPosition}, function(results, status) {
271
+ if (status == google.maps.GeocoderStatus.OK) {
272
+ $("#search_map_location").val(results[0].formatted_address);
273
+ $("#mapErrorMsg").hide(100);
274
+ mapLocationJSON.address2 = results[0].formatted_address;
275
+ mapLocationJSON.address1 = $("#search_map_location").val();
276
+ setAddress();
277
+ } else {
278
+ $("#mapErrorMsg").html("Cannot determine address at this location." + status).show(100);
279
+ }
204
280
  });
205
- map.panTo(position);
206
- }
207
- google.maps.event.addListener(marker, 'dragend', function () {
208
- geocodePosition(marker.getPosition());
209
281
  });
210
- google.maps.event.addListener(marker, 'click', function() {
211
- infowindow.setContent($("#search_map_location").val());
212
- infowindow.open(map, this)
282
+
283
+ // Handle marker click
284
+ marker.addListener("click", function() {
285
+ var infoWindow = new google.maps.InfoWindow({
286
+ content: $("#search_map_location").val()
287
+ });
288
+ infoWindow.open(map, marker);
289
+ });
290
+ } catch (e) {
291
+ // Fallback to old Marker API if AdvancedMarkerElement is not available
292
+ console.warn("AdvancedMarkerElement not available, using legacy Marker:", e);
293
+ marker = new google.maps.Marker({
294
+ position: position,
295
+ map: map,
296
+ draggable: true,
297
+ animation: google.maps.Animation.DROP
213
298
  });
214
- function geocodePosition(pos) {
215
- mapLocationJSON.lat=pos.lat();
216
- mapLocationJSON.lon=pos.lng();
299
+
300
+ google.maps.event.addListener(marker, "dragend", function() {
301
+ var newPosition = marker.getPosition();
302
+ mapLocationJSON.lat = newPosition.lat();
303
+ mapLocationJSON.lon = newPosition.lng();
304
+
217
305
  var geocoder = new google.maps.Geocoder();
218
- geocoder.geocode
219
- ({
220
- latLng: pos
221
- },
222
- function (results, status) {
223
- if (status == google.maps.GeocoderStatus.OK) {
224
- $("#search_map_location").val(results[0].formatted_address);
225
- $("#mapErrorMsg").hide(100);
226
- mapLocationJSON.address2=results[0].formatted_address;
227
- mapLocationJSON.address1=$("#search_map_location").val();
228
- setAddress();
229
- } else {
230
- $("#mapErrorMsg").html('Cannot determine address at this location.' + status).show(100);
231
- }
306
+ geocoder.geocode({latLng: newPosition}, function(results, status) {
307
+ if (status == google.maps.GeocoderStatus.OK) {
308
+ $("#search_map_location").val(results[0].formatted_address);
309
+ $("#mapErrorMsg").hide(100);
310
+ mapLocationJSON.address2 = results[0].formatted_address;
311
+ mapLocationJSON.address1 = $("#search_map_location").val();
312
+ setAddress();
313
+ } else {
314
+ $("#mapErrorMsg").html("Cannot determine address at this location." + status).show(100);
232
315
  }
233
- );
234
- }
235
- }`;
316
+ });
317
+ });
318
+
319
+ google.maps.event.addListener(marker, "click", function() {
320
+ var infoWindow = new google.maps.InfoWindow({
321
+ content: $("#search_map_location").val()
322
+ });
323
+ infoWindow.open(map, marker);
324
+ });
325
+ }
326
+
327
+ map.panTo(position);
328
+ }`;
236
329
  return {
237
330
  head: head,
238
331
  end: end,
@@ -1,5 +1,5 @@
1
1
  const router = require('express').Router();
2
- const csrf = require('csurf');
2
+ const {csrf} = require('zet-lib');
3
3
  const csrfProtection = csrf({cookie: true});
4
4
  const {Util, access, connection, moduleLib, zDebug, zRoute, zRole, zDataTable, zForm} = require('zet-lib');
5
5
  const MYMODEL = require('./../models/[[[TABLE_NAME]]]');
@@ -1,5 +1,5 @@
1
1
  const router = require('express').Router();
2
- const csrf = require('csurf');
2
+ const {csrf} = require('zet-lib');
3
3
  const csrfProtection = csrf({cookie: true});
4
4
  const {Util, access, connection, moduleLib, zDebug, zRoute, zRole, zDataTable, zForm} = require('zet-lib');
5
5
  const MYMODEL = require('./../models/[[[TABLE_NAME]]]');
package/lib/zAppRouter.js CHANGED
@@ -1,7 +1,7 @@
1
1
  require("dotenv").config();
2
2
  const express = require("express");
3
3
  const router = express.Router();
4
- const csrf = require("csurf");
4
+ const csrf = require("./csrf");
5
5
  const csrfProtection = csrf({ cookie: true });
6
6
  const zRoute = require("./zRoute");
7
7
  const connection = require("./connection");
@@ -1,6 +1,6 @@
1
1
  const express = require("express");
2
2
  const router = express.Router();
3
- const csrf = require("csurf");
3
+ const csrf = require("./csrf");
4
4
  const csrfProtection = csrf({ cookie: true });
5
5
  const fs = require("fs-extra");
6
6
  const axios = require("axios");
@@ -1,6 +1,6 @@
1
1
  const express = require('express');
2
2
  const router = express.Router();
3
- const csrf = require('csurf');
3
+ const csrf = require('./csrf');
4
4
  const csrfProtection = csrf({cookie: true});
5
5
  const fs = require('fs-extra');
6
6
  //const {Util, connection, zRole, zCache } = require('zet-lib');
package/lib/zPage.js CHANGED
@@ -52,7 +52,7 @@ zpage.build = async (req, res) => {
52
52
 
53
53
  zpage.coreCode = () => {
54
54
  return `const router = require('express').Router();
55
- const csrf = require('csurf');
55
+ const {csrf} = require('zet-lib');
56
56
  const csrfProtection = csrf({cookie: true});
57
57
  const fs = require('fs-extra');
58
58
  const qs = require('qs');
@@ -1,7 +1,7 @@
1
1
  const express = require('express')
2
2
  const router = express.Router()
3
3
  // setup route middlewares
4
- const csrf = require('csurf')
4
+ const csrf = require('./csrf')
5
5
  const bodyParser = require('body-parser')
6
6
  const path = require('path')
7
7
  const parseForm = bodyParser.urlencoded({ extended: true })
@@ -1,6 +1,6 @@
1
1
  const express = require('express');
2
2
  const router = express.Router();
3
- const csrf = require('csurf');
3
+ const csrf = require('./csrf');
4
4
  const csrfProtection = csrf({cookie: true});
5
5
  const fs = require("fs-extra");
6
6
  const Util = require("./Util");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zet-lib",
3
- "version": "2.0.1",
3
+ "version": "3.0.0",
4
4
  "description": "zet is a library that part of zet generator.",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -30,7 +30,6 @@
30
30
  "dependencies": {
31
31
  "axios": "^1.7.9",
32
32
  "convert-excel-to-json": "^1.7.0",
33
- "csurf": "^1.11.0",
34
33
  "dotenv": "^16.5.0",
35
34
  "dropbox": "^10.34.0",
36
35
  "ejs": "^3.1.10",