@gonextgames/utils 0.0.15

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.
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _trackEvent = require("./trackEvent");
7
+ Object.keys(_trackEvent).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _trackEvent[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _trackEvent[key];
14
+ }
15
+ });
16
+ });
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.trackEvent = void 0;
7
+ const trackEvent = function (eventName, eventParams) {
8
+ if (process.env.ENVIRONMENT === 'development') {
9
+ const eventKey = `${eventName}-${JSON.stringify(eventParams)}`;
10
+ if (window._lastTrackedEvent === eventKey) {
11
+ return;
12
+ }
13
+ window._lastTrackedEvent = eventKey;
14
+ }
15
+
16
+ // console.log('trackEvent', eventName, eventParams);
17
+ if (typeof window !== 'undefined' && window.gtag) {
18
+ window.gtag('event', eventName, eventParams);
19
+ }
20
+ };
21
+ exports.trackEvent = trackEvent;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useAuthStore = void 0;
7
+ var _zustand = require("zustand");
8
+ const useAuthStore = exports.useAuthStore = (0, _zustand.create)(set => ({
9
+ user: null,
10
+ isAuthenticated: false,
11
+ isLoading: false,
12
+ refreshing: false,
13
+ lastVerified: null,
14
+ error: null,
15
+ setAuth: user => {
16
+ set({
17
+ user,
18
+ isAuthenticated: true,
19
+ isLoading: false
20
+ });
21
+ },
22
+ clearAuth: () => {
23
+ set({
24
+ user: null,
25
+ isAuthenticated: false,
26
+ isLoading: false
27
+ });
28
+ },
29
+ setLoading: loading => {
30
+ set({
31
+ isLoading: loading
32
+ });
33
+ }
34
+ }));
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ "use client";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.AuthProvider = AuthProvider;
8
+ exports.useAuth = useAuth;
9
+ var _react = require("react");
10
+ var _navigation = require("next/navigation");
11
+ var _authStore = require("./authStore");
12
+ const AuthContext = /*#__PURE__*/(0, _react.createContext)({});
13
+ function AuthProvider({
14
+ children
15
+ }) {
16
+ const {
17
+ setAuth,
18
+ clearAuth,
19
+ setLoading,
20
+ setError
21
+ } = (0, _authStore.useAuthStore)();
22
+ const router = (0, _navigation.useRouter)();
23
+ (0, _react.useEffect)(() => {
24
+ // Only check auth on initial mount
25
+ const initialCheck = async () => {
26
+ // Skip auth check on login/register pages
27
+ if (window.location.pathname === '/login' || window.location.pathname === '/register') {
28
+ setLoading(false);
29
+ return;
30
+ }
31
+ await checkAuth();
32
+ };
33
+ initialCheck();
34
+ }, []);
35
+ const checkAuth = async () => {
36
+ const randomNumber = Math.floor(Math.random() * 100);
37
+ // if (useAuthStore.getState().isLoading) {
38
+ // console.warn('[Auth] Already loading', randomNumber)
39
+ // return // Prevent concurrent checks
40
+ // }
41
+ setLoading(true);
42
+ try {
43
+ const response = await fetch('/api/auth/verify', {
44
+ credentials: 'include'
45
+ });
46
+ if (!response.ok) {
47
+ console.warn('[Auth] Verification failed', randomNumber);
48
+ throw new Error('Verification failed', randomNumber);
49
+ }
50
+ const data = await response.json();
51
+ console.log('[Auth] Verification successful', data);
52
+ if (!data.authenticated) {
53
+ console.warn('[Auth] Not authenticated', randomNumber);
54
+ clearAuth();
55
+ return;
56
+ }
57
+ if (!data.user || !data.user.user_id) {
58
+ console.warn('[Auth] Invalid user data', randomNumber);
59
+ throw new Error('Invalid user data', randomNumber);
60
+ }
61
+ setAuth(data.user);
62
+ } catch (error) {
63
+ console.error('[Auth] Check failed:', error, randomNumber);
64
+ setError(error);
65
+ clearAuth();
66
+ } finally {
67
+ setLoading(false);
68
+ }
69
+ };
70
+ const login = async (email, password) => {
71
+ try {
72
+ const response = await fetch('/api/auth/login', {
73
+ method: 'POST',
74
+ headers: {
75
+ 'Content-Type': 'application/json'
76
+ },
77
+ body: JSON.stringify({
78
+ email,
79
+ password
80
+ })
81
+ });
82
+ if (!response.ok) throw new Error('Login failed');
83
+ const data = await response.json();
84
+ await setAuth(data.user);
85
+
86
+ // Verify auth state is set
87
+ const verifyResponse = await fetch('/api/auth/verify');
88
+ if (!verifyResponse.ok) throw new Error('Auth verification failed');
89
+ } catch (error) {
90
+ clearAuth();
91
+ throw error;
92
+ }
93
+ };
94
+ const logout = async () => {
95
+ try {
96
+ await fetch('/api/auth/logout', {
97
+ method: 'POST'
98
+ });
99
+ } catch (error) {
100
+ // Ignore logout errors
101
+ } finally {
102
+ clearAuth();
103
+ router.push('/');
104
+ }
105
+ };
106
+ const register = async userData => {
107
+ try {
108
+ const res = await fetch('/api/auth/register', {
109
+ method: 'POST',
110
+ headers: {
111
+ 'Content-Type': 'application/json'
112
+ },
113
+ body: JSON.stringify(userData)
114
+ });
115
+ const data = await res.json();
116
+ if (!res.ok) {
117
+ throw new Error(data.error || 'Registration failed');
118
+ }
119
+ setAuth(data.user);
120
+ } catch (error) {
121
+ throw error;
122
+ }
123
+ };
124
+ const value = {
125
+ isAuthenticated: (0, _authStore.useAuthStore)().isAuthenticated,
126
+ user: (0, _authStore.useAuthStore)().user,
127
+ isLoading: (0, _authStore.useAuthStore)().isLoading,
128
+ login,
129
+ logout,
130
+ register
131
+ };
132
+ return /*#__PURE__*/React.createElement(AuthContext.Provider, {
133
+ value: value
134
+ }, children);
135
+ }
136
+
137
+ // Export the hook directly with the function declaration
138
+ function useAuth() {
139
+ const {
140
+ user,
141
+ isAuthenticated,
142
+ isLoading
143
+ } = (0, _authStore.useAuthStore)();
144
+ const {
145
+ login,
146
+ logout,
147
+ register
148
+ } = (0, _react.useContext)(AuthContext);
149
+ return {
150
+ user,
151
+ isAuthenticated,
152
+ isLoading,
153
+ login,
154
+ logout,
155
+ register
156
+ };
157
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ 'use client';
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = AdBar;
8
+ var _react = require("react");
9
+ var _trackEvent = require("../analytics/trackEvent");
10
+ const defaultLinks = [{
11
+ url: 'https://randomgameidea.com',
12
+ title: 'Random Game Idea',
13
+ description: 'Generate unique board game ideas instantly by combining mechanics, themes, and turn orders.'
14
+ }, {
15
+ url: 'https://templative.net',
16
+ title: 'Templative',
17
+ description: 'Batch board game art updates, export to Tabletop Simulator, pdf, and the GameCrafter.'
18
+ }, {
19
+ url: 'https://playtestfeedback.com',
20
+ title: 'Playtest Feedback',
21
+ description: 'Organize playtests, collect feedback, take photos, build your mailing list, and improve your games.'
22
+ }, {
23
+ url: 'https://boardgameprototypes.com',
24
+ title: 'Board Game Prototypes',
25
+ description: 'Share your prototypes and sellsheets with publishers.'
26
+ }];
27
+ function AdBar({
28
+ via = "",
29
+ links = defaultLinks,
30
+ defaultIcon = '/default-favicon.png'
31
+ }) {
32
+ const [activePopover, setActivePopover] = (0, _react.useState)(null);
33
+ return /*#__PURE__*/React.createElement("div", {
34
+ className: "adbar"
35
+ }, links.map((link, index) => /*#__PURE__*/React.createElement("div", {
36
+ key: index,
37
+ className: "adbar-item-wrapper",
38
+ onMouseEnter: () => setActivePopover(index),
39
+ onMouseLeave: () => setActivePopover(null)
40
+ }, /*#__PURE__*/React.createElement("a", {
41
+ href: link.url + (via ? `?via=${via}` : '')
42
+ // target="_blank"
43
+ // rel="noopener noreferrer"
44
+ ,
45
+ onClick: e => {
46
+ e.preventDefault();
47
+ (0, _trackEvent.trackEvent)(`adbar_${link.title}_click`);
48
+ window.location.href = link.url + (via ? `?via=${via}` : '');
49
+ },
50
+ className: "adbar-icon"
51
+ }, /*#__PURE__*/React.createElement("img", {
52
+ src: `${link.url}/icon.ico`,
53
+ alt: link.title || `Visit ${link.url}`,
54
+ onError: e => {
55
+ e.target.src = defaultIcon;
56
+ }
57
+ })), activePopover === index && /*#__PURE__*/React.createElement("div", {
58
+ className: "custom-popover"
59
+ }, link.title && /*#__PURE__*/React.createElement("div", {
60
+ className: "popover-title"
61
+ }, link.title || new URL(link.url || link).hostname), link.description && /*#__PURE__*/React.createElement("div", {
62
+ className: "popover-content"
63
+ }, link.description)))));
64
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "AdBar", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _AdBar.default;
10
+ }
11
+ });
12
+ var _AdBar = _interopRequireDefault(require("./AdBar"));
13
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _auth = require("./auth");
7
+ Object.keys(_auth).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _auth[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _auth[key];
14
+ }
15
+ });
16
+ });
17
+ var _analytics = require("./analytics");
18
+ Object.keys(_analytics).forEach(function (key) {
19
+ if (key === "default" || key === "__esModule") return;
20
+ if (key in exports && exports[key] === _analytics[key]) return;
21
+ Object.defineProperty(exports, key, {
22
+ enumerable: true,
23
+ get: function () {
24
+ return _analytics[key];
25
+ }
26
+ });
27
+ });
28
+ var _components = require("./components");
29
+ Object.keys(_components).forEach(function (key) {
30
+ if (key === "default" || key === "__esModule") return;
31
+ if (key in exports && exports[key] === _components[key]) return;
32
+ Object.defineProperty(exports, key, {
33
+ enumerable: true,
34
+ get: function () {
35
+ return _components[key];
36
+ }
37
+ });
38
+ });
39
+ var _useStripe = require("./useStripe");
40
+ Object.keys(_useStripe).forEach(function (key) {
41
+ if (key === "default" || key === "__esModule") return;
42
+ if (key in exports && exports[key] === _useStripe[key]) return;
43
+ Object.defineProperty(exports, key, {
44
+ enumerable: true,
45
+ get: function () {
46
+ return _useStripe[key];
47
+ }
48
+ });
49
+ });
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useStripe = useStripe;
7
+ function useStripe() {
8
+ const initiateDonation = async (amount = 500) => {
9
+ try {
10
+ const response = await fetch('https://api.templative.net/stripe/create-donation-session', {
11
+ method: 'POST',
12
+ headers: {
13
+ 'Content-Type': 'application/json'
14
+ },
15
+ body: JSON.stringify({
16
+ amount
17
+ })
18
+ });
19
+ const session = await response.json();
20
+ if (session.error) {
21
+ throw new Error('Error creating donation session');
22
+ }
23
+
24
+ // Instead of using Stripe.js, directly redirect to the checkout URL
25
+ window.location.href = session.url;
26
+ } catch (error) {
27
+ console.error('Donation process error:', error);
28
+ alert('Error processing donation');
29
+ }
30
+ };
31
+ return {
32
+ initiateDonation
33
+ };
34
+ }
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ 'use server';
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.getUserFromToken = getUserFromToken;
8
+ exports.loginWithEmailAndPasswordAndSetToken = loginWithEmailAndPasswordAndSetToken;
9
+ exports.registerUserAndSetToken = registerUserAndSetToken;
10
+ var _users = require("../users");
11
+ var _passwordHashing = require("./passwordHashing.js");
12
+ var _uuid = require("uuid");
13
+ async function getUserFromToken(tableName, tokenName) {
14
+ try {
15
+ if (!tableName) throw new Error('Table name is required to getUserFromToken.');
16
+ const verifiedUser = await getUserIdAndEmailFromToken(tokenName);
17
+ if (!verifiedUser || !verifiedUser.user_id || !verifiedUser.email) {
18
+ console.warn('No user ID or email found in token', verifiedUser);
19
+ return null;
20
+ }
21
+ const user = await (0, _users.getUser)(verifiedUser.user_id, tableName);
22
+ if (!user) {
23
+ console.warn('User not found in database');
24
+ cookieStore.delete(tokenName);
25
+ return null;
26
+ }
27
+ if (user.email !== verifiedUser.email) {
28
+ console.warn('User email mismatch');
29
+ cookieStore.delete(tokenName);
30
+ return null;
31
+ }
32
+ const {
33
+ password,
34
+ ...safeUser
35
+ } = user;
36
+ return safeUser;
37
+ } catch (error) {
38
+ console.error('Auth check failed:', error.message);
39
+ return null;
40
+ }
41
+ }
42
+ async function loginWithEmailAndPasswordAndSetToken(email, password, tableName, tokenName) {
43
+ try {
44
+ if (!tableName) throw new Error('Table name is required to loginWithEmailAndPasswordAndSetToken.');
45
+ if (!tokenName) throw new Error('Token name is required to loginWithEmailAndPasswordAndSetToken.');
46
+ const user = await (0, _users.getUserByEmail)(email, tableName);
47
+ if (!user) {
48
+ console.warn('User not found for email:', email);
49
+ throw new Error('User not found');
50
+ }
51
+ const isValidPassword = await (0, _passwordHashing.comparePasswords)(password, user.password);
52
+ if (!isValidPassword) {
53
+ console.warn('Invalid password for user:', email);
54
+ throw new Error('Invalid password');
55
+ }
56
+ await setToken(tokenName, user.user_id, user.email);
57
+ const {
58
+ password: _,
59
+ ...userWithoutPassword
60
+ } = user;
61
+ return userWithoutPassword;
62
+ } catch (error) {
63
+ console.error('Error in loginWithEmailAndPasswordAndSetToken:', error.message);
64
+ throw error;
65
+ }
66
+ }
67
+ async function registerUserAndSetToken(name, email, password, otherData, tableName) {
68
+ try {
69
+ if (!tableName) throw new Error('Table name is required');
70
+
71
+ // Check if user exists
72
+ const existingUser = await (0, _users.getUserByEmail)(email, tableName);
73
+ if (existingUser) {
74
+ console.warn('Email already registered:', email);
75
+ throw new Error('Email already registered');
76
+ }
77
+
78
+ // Hash password
79
+ const hashedPassword = await hashPassword(password);
80
+
81
+ // Create user object with user_id
82
+ const user = {
83
+ user_id: (0, _uuid.v4)(),
84
+ name,
85
+ email: email,
86
+ contact_email: email,
87
+ password: hashedPassword
88
+ };
89
+ await (0, _users.createUser)(user.user_id, user.name, user.email, user.password, otherData, tableName);
90
+ await setToken('token', user.user_id, user.email);
91
+ const {
92
+ password: _,
93
+ ...userWithoutPassword
94
+ } = user;
95
+ return userWithoutPassword;
96
+ } catch (error) {
97
+ console.error('Error in registerUserAndSetToken:', {
98
+ message: error.message,
99
+ stack: error.stack
100
+ });
101
+ throw new Error('An error occurred while registering the user');
102
+ }
103
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ 'use server';
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.comparePasswords = comparePasswords;
8
+ exports.hashPassword = hashPassword;
9
+ var _bcryptjs = _interopRequireDefault(require("bcryptjs"));
10
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ async function hashPassword(password) {
12
+ try {
13
+ return await _bcryptjs.default.hash(password, 10);
14
+ } catch (error) {
15
+ console.error('Error in hashPassword:', error);
16
+ throw error;
17
+ }
18
+ }
19
+ async function comparePasswords(password, hashedPassword) {
20
+ try {
21
+ return await _bcryptjs.default.compare(password, hashedPassword);
22
+ } catch (error) {
23
+ console.error('Error in comparePasswords:', {
24
+ message: error.message,
25
+ stack: error.stack
26
+ });
27
+ throw error;
28
+ }
29
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ 'use server';
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.getUserIdAndEmailFromToken = getUserIdAndEmailFromToken;
8
+ exports.setToken = setToken;
9
+ var _headers = require("next/headers");
10
+ var _jsonwebtoken = _interopRequireDefault(require("jsonwebtoken"));
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ const JWT_SECRET = process.env.JWT_SECRET_KEY;
13
+ async function getUserIdAndEmailFromToken(tokenName) {
14
+ const cookieStore = await (0, _headers.cookies)();
15
+ const token = cookieStore.get(tokenName)?.value;
16
+ if (!token) return null;
17
+ const decodedToken = decodeURIComponent(token);
18
+ const verifiedUser = _jsonwebtoken.default.verify(decodedToken, JWT_SECRET);
19
+ if (!verifiedUser || !verifiedUser.user_id || !verifiedUser.exp || !verifiedUser.email) {
20
+ console.warn('Invalid or expired token', verifiedUser);
21
+ return null;
22
+ }
23
+ const fiveMinutes = 5 * 60 * 1000;
24
+ if (verifiedUser.exp * 1000 - Date.now() < fiveMinutes) {
25
+ cookieStore.delete(tokenName);
26
+ return null;
27
+ }
28
+ return verifiedUser;
29
+ }
30
+ async function setToken(tokenName, userId, email) {
31
+ const payload = {
32
+ user_id: userId,
33
+ email: email
34
+ };
35
+ const token = _jsonwebtoken.default.sign(payload, JWT_SECRET, {
36
+ expiresIn: '7d'
37
+ });
38
+ const cookieStore = (0, _headers.cookies)();
39
+ cookieStore.set(tokenName, token, {
40
+ httpOnly: true,
41
+ secure: process.env.ENVIRONMENT === 'production',
42
+ sameSite: 'lax',
43
+ path: '/',
44
+ maxAge: 7 * 24 * 60 * 60 // 7 days
45
+ });
46
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ "use server";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.dynamoDb = void 0;
8
+ var _clientDynamodb = require("@aws-sdk/client-dynamodb");
9
+ var _libDynamodb = require("@aws-sdk/lib-dynamodb");
10
+ if (!process.env.AWS_ACCESS_KEY || !process.env.AWS_SECRET) {
11
+ throw new Error('AWS credentials are not properly configured');
12
+ }
13
+ const client = new _clientDynamodb.DynamoDBClient({
14
+ region: "us-west-2",
15
+ credentials: {
16
+ accessKeyId: process.env.AWS_ACCESS_KEY,
17
+ secretAccessKey: process.env.AWS_SECRET
18
+ }
19
+ });
20
+ const dynamoDb = exports.dynamoDb = _libDynamodb.DynamoDBDocumentClient.from(client);
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ "use server";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.sendEmail = sendEmail;
8
+ var _nodemailer = _interopRequireDefault(require("nodemailer"));
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ const GONEXTGAMES_EMAIL = process.env.GONEXTGAMES_EMAIL;
11
+ const GONEXTGAMES_EMAIL_PASSWORD = process.env.GONEXTGAMES_EMAIL_PASSWORD;
12
+ const transporter = _nodemailer.default.createTransport({
13
+ service: 'gmail',
14
+ auth: {
15
+ user: GONEXTGAMES_EMAIL,
16
+ pass: GONEXTGAMES_EMAIL_PASSWORD
17
+ }
18
+ });
19
+ async function sendEmail({
20
+ to,
21
+ subject,
22
+ html,
23
+ unsubscribeToken
24
+ }) {
25
+ try {
26
+ var finalHtml = html;
27
+ if (unsubscribeToken) {
28
+ const footer = `<p style="font-size: 12px; color: #666;">
29
+ Our mailing address: 801 W 5th Street Unit 1210, Austin, TX 78703<br>
30
+ To unsubscribe, <a href="https://templative.net/waitlist/unsubscribe?email=${to}&token=${unsubscribeToken}">click here</a>.</p>`;
31
+ finalHtml += footer;
32
+ }
33
+ const plaintext = finalHtml.replace(/<br\/?>/g, '\n').replace(/<\/p>/g, '\n').replace(/<[^>]*>/g, '').trim();
34
+ const mailOptions = {
35
+ from: GONEXTGAMES_EMAIL,
36
+ to,
37
+ subject,
38
+ text: plaintext,
39
+ html: finalHtml || plaintext
40
+ };
41
+ const info = await transporter.sendMail(mailOptions);
42
+ // console.log('Email sent:', info.messageId);
43
+ return info;
44
+ } catch (error) {
45
+ console.error('Error sending email:', error);
46
+ throw error;
47
+ }
48
+ }
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _dynamoDb = require("./dynamoDb");
7
+ Object.keys(_dynamoDb).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _dynamoDb[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _dynamoDb[key];
14
+ }
15
+ });
16
+ });
17
+ var _email = require("./email");
18
+ Object.keys(_email).forEach(function (key) {
19
+ if (key === "default" || key === "__esModule") return;
20
+ if (key in exports && exports[key] === _email[key]) return;
21
+ Object.defineProperty(exports, key, {
22
+ enumerable: true,
23
+ get: function () {
24
+ return _email[key];
25
+ }
26
+ });
27
+ });
28
+ var _openAI = require("./openAI");
29
+ Object.keys(_openAI).forEach(function (key) {
30
+ if (key === "default" || key === "__esModule") return;
31
+ if (key in exports && exports[key] === _openAI[key]) return;
32
+ Object.defineProperty(exports, key, {
33
+ enumerable: true,
34
+ get: function () {
35
+ return _openAI[key];
36
+ }
37
+ });
38
+ });
39
+ var _s = require("./s3");
40
+ Object.keys(_s).forEach(function (key) {
41
+ if (key === "default" || key === "__esModule") return;
42
+ if (key in exports && exports[key] === _s[key]) return;
43
+ Object.defineProperty(exports, key, {
44
+ enumerable: true,
45
+ get: function () {
46
+ return _s[key];
47
+ }
48
+ });
49
+ });
50
+ var _users = require("./users");
51
+ Object.keys(_users).forEach(function (key) {
52
+ if (key === "default" || key === "__esModule") return;
53
+ if (key in exports && exports[key] === _users[key]) return;
54
+ Object.defineProperty(exports, key, {
55
+ enumerable: true,
56
+ get: function () {
57
+ return _users[key];
58
+ }
59
+ });
60
+ });
61
+ var _auth = require("./auth");
62
+ Object.keys(_auth).forEach(function (key) {
63
+ if (key === "default" || key === "__esModule") return;
64
+ if (key in exports && exports[key] === _auth[key]) return;
65
+ Object.defineProperty(exports, key, {
66
+ enumerable: true,
67
+ get: function () {
68
+ return _auth[key];
69
+ }
70
+ });
71
+ });
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ "use server";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.getChatCompletion = getChatCompletion;
8
+ var _openai = _interopRequireDefault(require("openai"));
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ const openai = new _openai.default({
11
+ apiKey: process.env.OPENAI_API_KEY
12
+ });
13
+ async function getChatCompletion(messages) {
14
+ try {
15
+ const completion = await openai.chat.completions.create({
16
+ model: "gpt-4-turbo-preview",
17
+ messages: [{
18
+ role: 'system',
19
+ content: 'When mentioning interesting or important concepts, wrap them in <span> tags. For example: The <span>environmental conditions</span> affect the <span>ecosystem</span>.'
20
+ }, ...messages.map(msg => ({
21
+ role: msg.user_id === 'ai' ? 'assistant' : 'user',
22
+ content: msg.content
23
+ }))],
24
+ temperature: 0.7
25
+ });
26
+ const content = completion.choices[0].message.content;
27
+ return content.split('\n').map(line => line.trim()).filter(line => line !== '');
28
+ } catch (error) {
29
+ console.error('Error in getChatCompletion:', error);
30
+ throw error;
31
+ }
32
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ "use server";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.generateMd5HashFromImage = generateMd5HashFromImage;
8
+ exports.getByGuid = getByGuid;
9
+ exports.uploadImage = uploadImage;
10
+ exports.uploadPdf = uploadPdf;
11
+ var _clientS = require("@aws-sdk/client-s3");
12
+ var _crypto = _interopRequireDefault(require("crypto"));
13
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
+ if (!process.env.AWS_ACCESS_KEY || !process.env.AWS_SECRET) {
15
+ throw new Error('AWS credentials are not properly configured');
16
+ }
17
+ const s3Client = new _clientS.S3Client({
18
+ region: "us-west-2",
19
+ credentials: {
20
+ accessKeyId: process.env.AWS_ACCESS_KEY,
21
+ secretAccessKey: process.env.AWS_SECRET
22
+ }
23
+ });
24
+ async function getByGuid(bucket, guid) {
25
+ try {
26
+ const command = new _clientS.GetObjectCommand({
27
+ Bucket: bucket,
28
+ Key: guid
29
+ });
30
+ const response = await s3Client.send(command);
31
+ const str = await response.Body.transformToString();
32
+ return str;
33
+ } catch (error) {
34
+ console.error('Error getting file from S3:', error);
35
+ throw error;
36
+ }
37
+ }
38
+ async function generateMd5HashFromImage(buffer) {
39
+ return _crypto.default.createHash('md5').update(buffer).digest('hex');
40
+ }
41
+ async function uploadImage(imageBuffer, bucket) {
42
+ try {
43
+ // Generate MD5 hash for the image
44
+ const md5Hash = await generateMd5HashFromImage(imageBuffer);
45
+ const key = `${md5Hash}.png`;
46
+
47
+ // Check if image already exists
48
+ try {
49
+ const checkCommand = new _clientS.GetObjectCommand({
50
+ Bucket: bucket,
51
+ Key: key
52
+ });
53
+ await s3Client.send(checkCommand);
54
+ } catch (error) {
55
+ if (error.name === 'NoSuchKey') {
56
+ // Upload the image if it doesn't exist
57
+ const uploadCommand = new _clientS.PutObjectCommand({
58
+ Bucket: bucket,
59
+ Key: key,
60
+ Body: imageBuffer,
61
+ ContentType: 'image/png'
62
+ });
63
+ await s3Client.send(uploadCommand);
64
+ } else {
65
+ throw error;
66
+ }
67
+ }
68
+
69
+ // Return the URL of the image
70
+ return `https://${bucket}.s3.amazonaws.com/${key}`;
71
+ } catch (error) {
72
+ console.error('Error uploading image:', error);
73
+ return null;
74
+ }
75
+ }
76
+ async function uploadPdf(pdfBuffer, filename, bucket) {
77
+ try {
78
+ const fileExtension = '.pdf';
79
+ const randomId = _crypto.default.randomBytes(8).toString('hex');
80
+ const key = `${randomId}-${filename}`;
81
+ const uploadCommand = new _clientS.PutObjectCommand({
82
+ Bucket: bucket,
83
+ Key: key,
84
+ Body: pdfBuffer,
85
+ ContentType: 'application/pdf'
86
+ });
87
+ await s3Client.send(uploadCommand);
88
+ return `https://${bucket}.s3.amazonaws.com/${key}`;
89
+ } catch (error) {
90
+ console.error('Error uploading PDF:', error);
91
+ throw error;
92
+ }
93
+ }
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ "use server";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.createUser = createUser;
8
+ exports.deleteUser = deleteUser;
9
+ exports.getUser = getUser;
10
+ exports.getUserByEmail = getUserByEmail;
11
+ exports.getUsersByIds = getUsersByIds;
12
+ exports.updateUser = updateUser;
13
+ var _libDynamodb = require("@aws-sdk/lib-dynamodb");
14
+ var _dynamoDb = require("./dynamoDb.js");
15
+ async function createUser(id, name, email, hashedPassword, otherData, tableName) {
16
+ if (!tableName) throw new Error('Table name is required');
17
+ try {
18
+ const item = {
19
+ user_id: id,
20
+ name,
21
+ email: email,
22
+ password: hashedPassword,
23
+ ...otherData,
24
+ createdAt: new Date().toISOString()
25
+ };
26
+ await _dynamoDb.dynamoDb.send(new _libDynamodb.PutCommand({
27
+ TableName: tableName,
28
+ Item: item
29
+ }));
30
+ return item;
31
+ } catch (error) {
32
+ console.error('Error in createUser:', error);
33
+ throw new Error('An error occurred while creating the user');
34
+ }
35
+ }
36
+ async function getUser(userId, tableName) {
37
+ try {
38
+ if (!tableName) throw new Error('Table name is required');
39
+ const result = await _dynamoDb.dynamoDb.send(new _libDynamodb.GetCommand({
40
+ TableName: tableName,
41
+ Key: {
42
+ user_id: userId
43
+ }
44
+ }));
45
+ return result.Item;
46
+ } catch (error) {
47
+ console.error('Error in getUser:', error);
48
+ throw new Error('An error occurred while retrieving the user');
49
+ }
50
+ }
51
+ async function updateUser(id, updates, tableName) {
52
+ if (!tableName) throw new Error('Table name is required');
53
+ try {
54
+ const updateExpressions = [];
55
+ const expressionAttributeValues = {};
56
+ const expressionAttributeNames = {};
57
+ Object.entries(updates).forEach(([key, value]) => {
58
+ updateExpressions.push(`#${key} = :${key}`);
59
+ expressionAttributeValues[`:${key}`] = value;
60
+ expressionAttributeNames[`#${key}`] = key;
61
+ });
62
+ const result = await _dynamoDb.dynamoDb.send(new _libDynamodb.UpdateCommand({
63
+ TableName: tableName,
64
+ Key: {
65
+ user_id: id
66
+ },
67
+ UpdateExpression: `SET ${updateExpressions.join(', ')}`,
68
+ ExpressionAttributeValues: expressionAttributeValues,
69
+ ExpressionAttributeNames: expressionAttributeNames,
70
+ ReturnValues: 'ALL_NEW'
71
+ }));
72
+ return result.Attributes;
73
+ } catch (error) {
74
+ console.error('Error in updateUser:', error);
75
+ throw new Error('An error occurred while updating the user');
76
+ }
77
+ }
78
+ async function deleteUser(id, tableName) {
79
+ try {
80
+ if (!tableName) throw new Error('Table name is required');
81
+ await _dynamoDb.dynamoDb.send(new _libDynamodb.DeleteCommand({
82
+ TableName: tableName,
83
+ Key: {
84
+ user_id: id
85
+ }
86
+ }));
87
+ } catch (error) {
88
+ console.error('Error in deleteUser:', error);
89
+ throw new Error('An error occurred while deleting the user');
90
+ }
91
+ }
92
+ async function getUserByEmail(email, tableName) {
93
+ try {
94
+ if (!tableName) throw new Error('Table name is required');
95
+ const result = await _dynamoDb.dynamoDb.send(new _libDynamodb.QueryCommand({
96
+ TableName: tableName,
97
+ IndexName: 'email-user_id-index',
98
+ KeyConditionExpression: 'email = :email',
99
+ ExpressionAttributeValues: {
100
+ ':email': email
101
+ },
102
+ Limit: 1
103
+ }));
104
+ return result.Items[0];
105
+ } catch (error) {
106
+ console.error('Error in getUserByEmail:', error);
107
+ throw new Error('An error occurred while retrieving the user');
108
+ }
109
+ }
110
+ async function getUsersByIds(userIds, tableName) {
111
+ try {
112
+ if (!tableName) throw new Error('Table name is required');
113
+ // If no userIds provided, return empty array
114
+ if (!userIds || userIds.length === 0) {
115
+ return [];
116
+ }
117
+
118
+ // DynamoDB only allows 25 items per BatchGetItem
119
+ const batchSize = 25;
120
+ let allUsers = [];
121
+
122
+ // Process users in batches
123
+ for (let i = 0; i < userIds.length; i += batchSize) {
124
+ const batch = userIds.slice(i, i + batchSize);
125
+ const result = await _dynamoDb.dynamoDb.send(new _libDynamodb.BatchGetCommand({
126
+ RequestItems: {
127
+ [tableName]: {
128
+ Keys: batch.map(id => ({
129
+ user_id: id
130
+ }))
131
+ }
132
+ }
133
+ }));
134
+ if (result.Responses && result.Responses[tableName]) {
135
+ // Remove sensitive data from users
136
+ const safeUsers = result.Responses[tableName].map(user => {
137
+ const {
138
+ password,
139
+ ...safeUser
140
+ } = user;
141
+ return safeUser;
142
+ });
143
+ allUsers = [...allUsers, ...safeUsers];
144
+ }
145
+ }
146
+ return allUsers;
147
+ } catch (error) {
148
+ console.error('Error in getUsersByIds:', error);
149
+ throw new Error('An error occurred while retrieving the users');
150
+ }
151
+ }
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@gonextgames/utils",
3
+ "exports": {
4
+ "./client": "./dist/client/index.js",
5
+ "./client/*": "./dist/client/*.js",
6
+ "./server": "./dist/server/index.js",
7
+ "./server/*": "./dist/server/*.js",
8
+ "./client/auth": "./dist/client/auth/index.jsx",
9
+ "./server/auth": "./dist/server/auth/index.js"
10
+ },
11
+ "version": "0.0.15",
12
+ "publishConfig": {
13
+ "registry": "https://registry.npmjs.org/",
14
+ "access": "public"
15
+ },
16
+ "main": "dist/index.js",
17
+ "module": "dist/index.js",
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "babel src -d dist --copy-files",
23
+ "prepare": "npm run build",
24
+ "dev": "babel src -d dist --copy-files --watch"
25
+ },
26
+ "dependencies": {
27
+ "@aws-sdk/client-dynamodb": "^3.726.1",
28
+ "@aws-sdk/lib-dynamodb": "^3.726.1",
29
+ "@aws-sdk/client-s3": "^3.726.1",
30
+ "axios": "^1.7.9",
31
+ "bcryptjs": "^2.4.3",
32
+ "jsonwebtoken": "^9.0.0",
33
+ "nodemailer": "^6.9.16",
34
+ "openai": "^4.0.0",
35
+ "next": "^15.1.4",
36
+ "next-sitemap": "^4.2.3",
37
+ "react": "^19.0.0",
38
+ "react-dom": "^19.0.0",
39
+ "zustand": "^5.0.3"
40
+ },
41
+ "devDependencies": {
42
+ "@babel/cli": "^7.23.9",
43
+ "@babel/core": "^7.23.9",
44
+ "@babel/preset-env": "^7.23.9",
45
+ "@babel/preset-react": "^7.23.9",
46
+ "next": "^15.1.4"
47
+ },
48
+ "engines": {
49
+ "node": ">=20.0.0"
50
+ },
51
+ "peerDependencies": {
52
+ "next": "15.1.4"
53
+ }
54
+ }
package/readme.md ADDED
@@ -0,0 +1,129 @@
1
+ # Go Next Games Landing Utils
2
+
3
+ This is a collection of utilities for the Go Next Games Landing Page.
4
+
5
+ ## Development
6
+
7
+ In your package.json, add the following:
8
+ ```json
9
+ "@gonextgames/utils-client": "file:/Users/oliverbarnum/Documents/git/utils/client",
10
+ "@gonextgames/utils-server": "file:/Users/oliverbarnum/Documents/git/utils/server",
11
+ ```
12
+
13
+ Then run `npm install`
14
+
15
+ Create an .env file:
16
+
17
+ ```json
18
+ AWS_ACCESS_KEY=""
19
+ AWS_SECRET=""
20
+ ENVIRONMENT=""
21
+ JWT_SECRET_KEY=""
22
+ STRIPE_API=""
23
+ STRIPE_PUBLIC_KEY=""
24
+ OPENAI_API_KEY=""
25
+ ```
26
+ Then
27
+ ```
28
+ cd ./client
29
+ npm run dev
30
+ ```
31
+ New Terminal:
32
+ ```
33
+ cd ./server
34
+ npm run dev
35
+ ```
36
+
37
+ ## Auth
38
+
39
+ ### Client Side Consumption
40
+
41
+ - Wrap the layout in `<AuthProvider></AuthProvider>`
42
+ - In components that need to know whether you are auth'd:
43
+
44
+ ```js
45
+ import { useAuth, trackEvent } from '@gonextgames/utils-client'
46
+ const { isAuthenticated, logout, user } = useAuth()
47
+ ```
48
+ - To change auth
49
+ ```js
50
+ import { useAuth } from '@gonextgames/utils-client'
51
+ // Call these functions to call the backend
52
+ const { login/register/logout } = useAuth()
53
+ ```
54
+ When implementing auth, you can use the `redirect` function with the `from` parameter to redirect after logging in. After registering, you can redirect to the getting started page or whatever is appropriate for your application.
55
+
56
+ ### Server Side Consumption
57
+
58
+ - Implement /api/auth/verify for tokens
59
+
60
+ ```js
61
+ import { getUserFromToken } from '@gonextgames/utils-server'
62
+
63
+ export async function GET(req) {
64
+ try {
65
+ const user = await getUserFromToken("NAME_OF_USERS_TABLE", "TOKEN_NAME")
66
+ if (user === null) {
67
+ return Response.json({authenticated: false}, { status: 200 })
68
+ }
69
+ return Response.json({authenticated: true, user: user})
70
+ } catch (error) {
71
+ console.error('Verify token error:', error)
72
+ return Response.json({ authenticated: false, error: 'Token verification failed' }, { status: 401 })
73
+ }
74
+ }
75
+ ```
76
+
77
+ - Consume it with
78
+
79
+ ```js
80
+ import { getUserFromToken } from '@gonextgames/utils-server'
81
+ const user = await getUserFromToken("NAME_OF_USERS_TABLE", "TOKEN_NAME")
82
+ if (!user) {
83
+ redirect(`/login?from=${encodeURIComponent('/FROM-ENDPOINT')}`)
84
+ }
85
+ ```
86
+
87
+ - Create /api/auth/login /logout and /register
88
+
89
+ ```js
90
+ // Register
91
+ const user = await registerUser(userData.name, userData.email, userData.password, { link: userData.link }, 'bgp_users')
92
+
93
+ // Login
94
+ const user = await loginWithEmailAndPasswordAndSetToken(email, password, 'bgp_users', "token")
95
+
96
+ //Logout
97
+ const cookieStore = await cookies()
98
+
99
+ cookieStore.set('token', '', {
100
+ httpOnly: true,
101
+ secure: process.env.ENVIRONMENT === 'production',
102
+ sameSite: 'lax',
103
+ path: '/',
104
+ expires: new Date(0)
105
+ })
106
+ ```
107
+
108
+ ### Users Table
109
+
110
+ The user table is standardized. Calling CRUD for users is handled by the package, where you pass the `TABLE_NAME` to the function every time you want to call it.
111
+
112
+ ```js
113
+ await updateUser(user.user_id, {
114
+ contact_email: email,
115
+ }, 'bgp_users')
116
+ ```
117
+
118
+ Nothing is stopping you from creating new ways to get the information, such as by the users /link, like in Board Game Prototypes.
119
+
120
+ The users table must have:
121
+
122
+ - user_id
123
+ - email
124
+ - name
125
+ - password
126
+
127
+ And a `email-user_id-index` secondary index.
128
+
129
+ ## Event Tracking