aaex-cli 2.0.1 → 2.1.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AaExJS
2
2
 
3
- ## V.2.0.1
3
+ ## V.2.1.0
4
4
 
5
5
  Bug fix for css modules
6
6
 
@@ -66,9 +66,11 @@ async function createPackageJson() {
66
66
  "aaex-file-router": "^2.0.0",
67
67
  jsonwebtoken: "^9.0.3",
68
68
  mongodb: "^7.0.0",
69
+ pg: "^8.16.3",
70
+
69
71
  bcrypt: "^6.0.0",
70
72
  dotenv: "^17.2.3",
71
- "aaexjs": "^2.0.0"
73
+ aaexjs: "^2.1.5",
72
74
  },
73
75
  devDependencies: {
74
76
  typescript: "~5.9.2",
@@ -76,6 +78,8 @@ async function createPackageJson() {
76
78
  "@types/react-dom": "^19.1.9",
77
79
  "@types/express": "^5.0.3",
78
80
  "@vitejs/plugin-react": "^5.0.2",
81
+ "@types/pg": "^8.16.0",
82
+
79
83
  vite: "^7.1.5",
80
84
  "cross-env": "^10.0.0",
81
85
  "@types/bcrypt": "^6.0.0",
@@ -108,8 +112,8 @@ async function main() {
108
112
  message: "Which database do you want to use?",
109
113
  choices: [
110
114
  "MongoDB",
111
- "MySQL - not implemented",
112
- "PostgreSQL - not implemented",
115
+ "MySQL --not implemented",
116
+ "PostgreSQL --not fully implemented",
113
117
  ],
114
118
  default: 0,
115
119
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aaex-cli",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Command line interface for creating aaexjs app",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -16,6 +16,7 @@
16
16
  "@types/express": "^5.0.6",
17
17
  "@types/jsonwebtoken": "^9.0.10",
18
18
  "@types/node": "^24.10.1",
19
+ "@types/pg": "^8.16.0",
19
20
  "@types/react": "^19.2.7",
20
21
  "@types/react-dom": "^19.2.3",
21
22
  "@vitejs/plugin-react": "^5.1.1",
@@ -24,6 +25,7 @@
24
25
  "express": "^5.2.1",
25
26
  "jsonwebtoken": "^9.0.3",
26
27
  "mongodb": "^7.0.0",
28
+ "pg": "^8.16.3",
27
29
  "react": "^19.2.1",
28
30
  "react-dom": "^19.2.1",
29
31
  "react-router": "^7.10.1",
@@ -36,6 +38,6 @@
36
38
  "inquirer": "^13.1.0"
37
39
  },
38
40
  "peerDependencies": {
39
- "aaexjs": "2.0.1"
41
+ "aaexjs": "^2.0.1"
40
42
  }
41
43
  }
@@ -0,0 +1,53 @@
1
+ import type { Request, Response } from "express";
2
+ import jwt from "jsonwebtoken";
3
+ import UserModel, { LoginUser, UserShape } from "../../models/User";
4
+
5
+ export const POST = async (req: Request, res: Response) => {
6
+ const { email, password }: LoginUser = req.body;
7
+
8
+ if (!email || !password) {
9
+ return res.status(400).json({ error: "Missing fields!" });
10
+ }
11
+
12
+ if (!process.env.JWT_SECRET) {
13
+ console.error("Missing: JWT_SECRET from environment variables");
14
+ return res
15
+ .status(500)
16
+ .json({ error: "Internal server error! Try again later" });
17
+ }
18
+
19
+ const normalizedEmail = email.trim().toLowerCase();
20
+ const user = await UserModel.findOne<UserShape>({ email: normalizedEmail });
21
+
22
+ if (!user) {
23
+ return res.status(400).json({ error: "Invalid email or password" });
24
+ }
25
+
26
+ const compared = await UserModel.comparePassword(password, user.password);
27
+
28
+ if (!compared) {
29
+ return res.status(400).json({ error: "Invalid email or password" });
30
+ }
31
+
32
+ const expiration = process.env.JWT_EXP ?? "24h";
33
+
34
+ const token = jwt.sign(
35
+ {
36
+ id: user.id,
37
+ email: user.email,
38
+ username: user.username,
39
+ },
40
+ process.env.JWT_SECRET as string,
41
+ { expiresIn: expiration as any } //fixes stupid thing where it wont accept the sring variable because its not number | ms.stringvlue | undefined
42
+ );
43
+
44
+ return res.status(200).json({
45
+ ok: true,
46
+ user: {
47
+ id: user.id,
48
+ name: user.username,
49
+ email: user.email,
50
+ },
51
+ token,
52
+ });
53
+ };
@@ -0,0 +1,34 @@
1
+ import { Request, Response } from "express";
2
+ import UserModel, { CreateUser, UserShape } from "../../models/User";
3
+ export const POST = async (req: Request, res: Response) => {
4
+ const { email, username, password, confirmPass }: CreateUser = req.body;
5
+
6
+ if (!username || !email || !password || !confirmPass)
7
+ return res.status(400).json({ error: "Missing fields" });
8
+
9
+ if (password !== confirmPass)
10
+ return res.status(400).json({ error: "Passwords do not match" });
11
+
12
+ const exists = await UserModel.findOne<UserShape>({ email });
13
+ if (exists)
14
+ return res
15
+ .status(409)
16
+ .json({ error: "User with that email already exists" });
17
+
18
+ const salt = 10;
19
+ const hashed = await UserModel.hashPassword(password, salt);
20
+
21
+ const created = await UserModel.create<Omit<CreateUser, "confirmPass">>({
22
+ username,
23
+ email,
24
+ password: hashed,
25
+ createdAt: new Date(),
26
+ });
27
+
28
+ if (!created) {
29
+ return res.status(500).json({ error: "User creation failed" });
30
+ }
31
+ return res
32
+ .status(201)
33
+ .json({ ok: true, insertedId: (created as UserShape).id });
34
+ };
@@ -0,0 +1,18 @@
1
+ import jwt from "jsonwebtoken";
2
+ import type { Request, Response } from "express";
3
+
4
+ export async function POST(req: Request, res: Response) {
5
+ const { token } = req.body;
6
+
7
+ if (!process.env.JWT_SECRET) {
8
+ console.error("Missing: JWT_SECRET from environment");
9
+ return res.status(500).json({error: "Internal server error!"});
10
+ }
11
+
12
+ try {
13
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
14
+ return res.status(200).json({ valid: true, user: decoded });
15
+ } catch (err) {
16
+ return res.status(401).json({ valid: false });
17
+ }
18
+ }
@@ -1,13 +1,53 @@
1
- export type User = {
2
- id: string;
1
+ export interface UserShape {
2
+ id: string; // normalized for both mongodb and postgres
3
3
  email: string;
4
4
  username: string;
5
5
  password: string;
6
+ createdAt: Date;
7
+ }
8
+
9
+ //helper types for api and forms
10
+ export type CreateUser = Omit<UserShape, "id"> & {
6
11
  confirmPass: string;
7
12
  };
8
13
 
9
- export type CreateUser = Omit<User, "id">; //should not be edited
14
+ export type LoginUser = Pick<UserShape, "email" | "password">;
15
+
16
+ export type CookieUser = Omit<UserShape, "password" | "confirmPass">;
17
+
18
+ //the model
19
+ import { Model } from "aaexjs/database";
20
+ import bcrypt from "bcrypt";
21
+
22
+ export default class User extends Model<User> {
23
+ static collection = "users"; //defines the collection for the users
10
24
 
11
- export type LoginUser = Pick<User, "email" | "password">; //should not be edited
25
+ // ===================================================
26
+ // ============= Collection specific =================
27
+ // ===================================================
28
+ /**
29
+ * Hash a plain password
30
+ * @param password - The password to hash
31
+ * @param salt - The number of salt rounds default 10
32
+ * @returns Promise resolving to the hashed password
33
+ */
34
+ static async hashPassword(
35
+ password: string,
36
+ salt: number = 10
37
+ ): Promise<string> {
38
+ return bcrypt.hash(password, salt);
39
+ }
12
40
 
13
- export type CookieUser = Omit<User, "password" | "confirmPass">;
41
+ /**
42
+ * Compare a plain password with a hash
43
+ * @param password - Plain password
44
+ * @param hash - Hashed password
45
+ * @returns Promise resolving to true if match
46
+ */
47
+ static async comparePassword(
48
+ password: string,
49
+ hash: string
50
+ ): Promise<boolean> {
51
+ return bcrypt.compare(password, hash);
52
+ }
53
+ }
@@ -1,9 +1,9 @@
1
1
  import { defineConfig } from "vite";
2
2
  import react from "@vitejs/plugin-react";
3
3
  import { aaexServerRouter } from "aaex-file-router/plugin";
4
- import { pluginSsrDevFoucFix } from "aaexjs-test";
4
+ import { SSRCss } from "aaexjs";
5
5
 
6
6
  // https://vite.dev/config/
7
7
  export default defineConfig({
8
- plugins: [react(), aaexServerRouter(), pluginSsrDevFoucFix()],
8
+ plugins: [react(), aaexServerRouter(), SSRCss()],
9
9
  });
@@ -1,7 +0,0 @@
1
- import { useParams } from "react-router";
2
-
3
- export default function Test() {
4
- const { slug } = useParams();
5
-
6
- return <>{slug}</>;
7
- }