This commit is contained in:
2025-04-13 15:19:59 +02:00
parent 1ea3e77fa8
commit 14f415e228
46 changed files with 14161 additions and 539 deletions

15
.env Normal file
View File

@ -0,0 +1,15 @@
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
# Database Configuration
DATABASE_HOST=localhost
DATABASE_USER=root
DATABASE_PASSWORD=
DATABASE_NAME=deployer
# API Server
PORT=3000
NODE_ENV=development

44
api/auth.js Normal file
View File

@ -0,0 +1,44 @@
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const { authenticateUser } = require('../src/services/authService');
const app = express();
const port = process.env.PORT || 3001;
// Middleware
app.use(cors());
app.use(bodyParser.json());
// Authentication endpoint
app.post('/api/auth/login', async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({
success: false,
error: 'Email and password are required'
});
}
const result = await authenticateUser(email, password);
if (result.success) {
return res.json(result);
} else {
return res.status(401).json(result);
}
} catch (error) {
console.error('Authentication error:', error);
return res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Start server
app.listen(port, () => {
console.log(`Authentication API running on port ${port}`);
});

888
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,17 +10,14 @@
"preview": "vite preview"
},
"dependencies": {
"@fullcalendar/core": "^6.1.17",
"@fullcalendar/daygrid": "^6.1.17",
"@fullcalendar/interaction": "^6.1.17",
"@fullcalendar/list": "^6.1.17",
"@fullcalendar/react": "^6.1.17",
"@fullcalendar/timegrid": "^6.1.17",
"@react-jvectormap/core": "^1.0.4",
"@react-jvectormap/world": "^1.1.2",
"@prisma/client": "^6.6.0",
"@types/bcrypt": "^5.0.2",
"@types/crypto-js": "^4.2.2",
"apexcharts": "^4.5.0",
"axios": "^1.8.4",
"bcrypt": "^5.1.1",
"clsx": "^2.1.1",
"crypto-js": "^4.2.0",
"flatpickr": "^4.6.13",
"react": "^19.0.0",
"react-apexcharts": "^1.7.0",
@ -28,7 +25,6 @@
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^19.0.0",
"react-dropzone": "^14.3.8",
"react-helmet-async": "^2.0.5",
"react-router-dom": "^7.5.0",
"swiper": "^11.2.6",
"tailwind-merge": "^3.2.0",
@ -44,6 +40,7 @@
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
"prisma": "^6.6.0",
"tailwindcss": "^4.1.3",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",

View File

@ -0,0 +1,44 @@
-- CreateTable
CREATE TABLE `Project` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`name` VARCHAR(191) NOT NULL,
`repository` VARCHAR(191) NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Deployment` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`projectId` INTEGER NOT NULL,
`environmentId` INTEGER NOT NULL,
`status` VARCHAR(191) NOT NULL DEFAULT 'pending',
`commitHash` VARCHAR(191) NOT NULL,
`deployedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`logs` TEXT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Environment` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`name` VARCHAR(191) NOT NULL,
`url` VARCHAR(191) NOT NULL,
`projectId` INTEGER NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `Deployment` ADD CONSTRAINT `Deployment_projectId_fkey` FOREIGN KEY (`projectId`) REFERENCES `Project`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Deployment` ADD CONSTRAINT `Deployment_environmentId_fkey` FOREIGN KEY (`environmentId`) REFERENCES `Environment`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Environment` ADD CONSTRAINT `Environment_projectId_fkey` FOREIGN KEY (`projectId`) REFERENCES `Project`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,18 @@
-- CreateTable
CREATE TABLE `User` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`email` VARCHAR(191) NOT NULL,
`password` VARCHAR(191) NOT NULL,
`name` VARCHAR(191) NOT NULL,
`surname` VARCHAR(191) NOT NULL,
`active` BOOLEAN NOT NULL DEFAULT true,
`role` VARCHAR(191) NOT NULL DEFAULT 'user',
`lastLogin` DATETIME(3) NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
`resetToken` VARCHAR(191) NULL,
`resetTokenExpiry` DATETIME(3) NULL,
UNIQUE INDEX `User_email_key`(`email`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

View File

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "mysql"

63
prisma/schema.prisma Normal file
View File

@ -0,0 +1,63 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Project {
id Int @id @default(autoincrement())
name String
repository String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deployments Deployment[]
environments Environment[]
}
model Deployment {
id Int @id @default(autoincrement())
projectId Int
project Project @relation(fields: [projectId], references: [id])
environmentId Int
environment Environment @relation(fields: [environmentId], references: [id])
status String @default("pending") // pending, success, failed
commitHash String
deployedAt DateTime @default(now())
logs String? @db.Text
}
model Environment {
id Int @id @default(autoincrement())
name String
url String
projectId Int
project Project @relation(fields: [projectId], references: [id])
deployments Deployment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model User {
id Int @id @default(autoincrement())
email String @unique
password String
name String
surname String
active Boolean @default(true)
role String @default("user") // user, admin, etc.
lastLogin DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resetToken String?
resetTokenExpiry DateTime?
}

105
public/api/login.js Normal file
View File

@ -0,0 +1,105 @@
const express = require('express');
const mysql = require('mysql2/promise');
const router = express.Router();
const crypto = require('crypto-js');
require('dotenv').config();
// Create connection pool to MySQL database
const pool = mysql.createPool({
host: process.env.DATABASE_HOST || 'localhost',
user: process.env.DATABASE_USER || 'root',
password: process.env.DATABASE_PASSWORD || '',
database: process.env.DATABASE_NAME || 'deployer',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
// Generate a secure token
const generateToken = (length = 64) => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let token = '';
for (let i = 0; i < length; i++) {
token += characters.charAt(Math.floor(Math.random() * characters.length));
}
return token;
};
/**
* Login endpoint to authenticate users against the database
*/
router.post('/', async (req, res) => {
const { email, password } = req.body;
// Basic validation
if (!email || !password) {
return res.status(400).json({
success: false,
error: 'Email and password are required'
});
}
try {
// Query database for user with matching email
const [rows] = await pool.query(
'SELECT * FROM User WHERE email = ?',
[email]
);
// Check if user exists
if (rows.length === 0) {
return res.status(401).json({
success: false,
error: 'User not found'
});
}
const user = rows[0];
// Check if user is active
if (!user.active) {
return res.status(401).json({
success: false,
error: 'Account is inactive'
});
}
// Verify password (compare hashed passwords)
if (user.password !== password) {
return res.status(401).json({
success: false,
error: 'Invalid credentials'
});
}
// Generate token
const token = generateToken();
// Update last login timestamp
await pool.query(
'UPDATE User SET lastLogin = NOW() WHERE id = ?',
[user.id]
);
// Return success with token and user info
return res.json({
success: true,
token,
user: {
id: user.id,
name: `${user.name} ${user.surname}`,
email: user.email,
role: user.role
}
});
}
catch (error) {
console.error('Authentication error:', error);
return res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
module.exports = router;

34
public/api/server.js Normal file
View File

@ -0,0 +1,34 @@
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const loginRoutes = require('./login');
const app = express();
const port = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(bodyParser.json());
// Routes
app.use('/api/login', loginRoutes);
// Health check endpoint
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date() });
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error('API error:', err);
res.status(500).json({
success: false,
error: 'Internal server error',
message: process.env.NODE_ENV === 'development' ? err.message : undefined
});
});
// Start server
app.listen(port, () => {
console.log(`API server running on port ${port}`);
});

29
scripts/add-user.js Normal file
View File

@ -0,0 +1,29 @@
import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function createUser() {
try {
const hashedPassword = await bcrypt.hash('gk1510!', 10);
const user = await prisma.user.create({
data: {
email: 'gregor@klevze.si',
password: hashedPassword,
name: 'Gregor',
surname: 'Klevže',
active: true,
role: 'admin',
},
});
console.log('User created successfully:', user);
} catch (error) {
console.error('Error creating user:', error);
} finally {
await prisma.$disconnect();
}
}
createUser();

23
scripts/add-user.sql Normal file
View File

@ -0,0 +1,23 @@
-- Add user: Gregor Klevže
-- Note: In a real application, you would use proper password hashing,
-- but for direct SQL insertion we're using a placeholder password that you should change immediately
INSERT INTO `User` (
`email`,
`password`,
`name`,
`surname`,
`active`,
`role`,
`createdAt`,
`updatedAt`
)
VALUES (
'gregor@klevze.si',
'gk1510!', -- This should be hashed in a real application
'Gregor',
'Klevže',
true,
'admin',
NOW(),
NOW()
);

29
scripts/add-user.ts Normal file
View File

@ -0,0 +1,29 @@
import { PrismaClient } from '../src/generated/prisma';
import * as bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function createUser() {
try {
const hashedPassword = await bcrypt.hash('gk1510!', 10);
const user = await prisma.user.create({
data: {
email: 'gregor@klevze.si',
password: hashedPassword,
name: 'Gregor',
surname: 'Klevže',
active: true,
role: 'admin',
},
});
console.log('User created successfully:', user);
} catch (error) {
console.error('Error creating user:', error);
} finally {
await prisma.$disconnect();
}
}
createUser();

View File

@ -11,7 +11,6 @@ import Avatars from "./pages/UiElements/Avatars";
import Buttons from "./pages/UiElements/Buttons";
import LineChart from "./pages/Charts/LineChart";
import BarChart from "./pages/Charts/BarChart";
import Calendar from "./pages/Calendar";
import BasicTables from "./pages/Tables/BasicTables";
import FormElements from "./pages/Forms/FormElements";
import Blank from "./pages/Blank";
@ -31,7 +30,6 @@ export default function App() {
{/* Others Page */}
<Route path="/profile" element={<UserProfiles />} />
<Route path="/calendar" element={<Calendar />} />
<Route path="/blank" element={<Blank />} />
{/* Forms */}
@ -46,7 +44,6 @@ export default function App() {
<Route path="/badge" element={<Badges />} />
<Route path="/buttons" element={<Buttons />} />
<Route path="/images" element={<Images />} />
<Route path="/videos" element={<Videos />} />
{/* Charts */}
<Route path="/line-chart" element={<LineChart />} />

View File

@ -1,6 +1,10 @@
import React, { useState } from 'react';
import { useAuth } from '../../context/AuthContext';
import { API_BASE_URL } from '../../config/apiConfig';
import { API_BASE_URL, isLocalMode } from '../../config/apiConfig';
import { localLogin } from '../../services/localApi';
// Import crypto library for hashing
import crypto from 'crypto-js';
const Login: React.FC = () => {
const [email, setEmail] = useState('');
@ -15,16 +19,35 @@ const Login: React.FC = () => {
setIsLoading(true);
try {
// Send credentials as JSON
// Create a SHA-256 hash of the password
const hashedPassword = crypto.SHA256(password).toString();
// Choose between local and remote API based on configuration
if (isLocalMode()) {
// Use local authentication
const result = await localLogin(email, hashedPassword);
if (result.success && result.token) {
login(result.token);
} else {
setError(result.error || 'Authentication failed');
}
} else {
// Use remote authentication (original implementation)
const response = await fetch(`${API_BASE_URL}/api/login.php`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Add security headers
'X-Requested-With': 'XMLHttpRequest', // Help prevent CSRF
},
// Send the plaintext email but hashed password
body: JSON.stringify({
email,
password,
password: hashedPassword,
}),
// Include credentials like cookies if needed
credentials: 'include',
});
const data = await response.json();
@ -40,10 +63,13 @@ const Login: React.FC = () => {
// Handle error responses
setError(data.error || 'Authentication failed');
}
}
} catch (err) {
setError('Failed to connect to the server. Please try again.');
console.error('Login error:', err);
} finally {
// Clear password from memory for security
setPassword('');
setIsLoading(false);
}
};
@ -90,6 +116,13 @@ const Login: React.FC = () => {
>
{isLoading ? 'Logging in...' : 'Login'}
</button>
{isLocalMode() && (
<div className="mt-4 text-sm text-gray-500">
<p>Demo accounts:</p>
<p>Email: admin@example.com / Password: admin123</p>
<p>Email: user@example.com / Password: user123</p>
</div>
)}
</form>
</div>
</div>

View File

@ -0,0 +1,57 @@
import { useEffect, createContext, useContext } from 'react';
type HeadProps = {
title?: string;
description?: string;
children?: React.ReactNode;
};
// Create a context to manage document head
const HeadContext = createContext<{
updateHead: (props: HeadProps) => void;
}>({
updateHead: () => {},
});
// Provider component that will wrap the application
export const HeadProvider = ({ children }: { children: React.ReactNode }) => {
const updateHead = ({ title, description }: HeadProps) => {
if (title) {
document.title = title;
}
if (description) {
// Find existing description meta tag or create a new one
let metaDescription = document.querySelector('meta[name="description"]');
if (!metaDescription) {
metaDescription = document.createElement('meta');
metaDescription.setAttribute('name', 'description');
document.head.appendChild(metaDescription);
}
metaDescription.setAttribute('content', description);
}
};
return (
<HeadContext.Provider value={{ updateHead }}>
{children}
</HeadContext.Provider>
);
};
// Component to use in pages for setting title and description
const PageHead = ({ title, description }: HeadProps) => {
const { updateHead } = useContext(HeadContext);
useEffect(() => {
updateHead({ title, description });
// Cleanup is not typically needed, but could reset title if desired
return () => {};
}, [title, description, updateHead]);
// This component doesn't render anything
return null;
};
export default PageHead;

View File

@ -1,4 +1,4 @@
import { HelmetProvider, Helmet } from "react-helmet-async";
import PageHead from './PageHead';
const PageMeta = ({
title,
@ -7,14 +7,12 @@ const PageMeta = ({
title: string;
description: string;
}) => (
<Helmet>
<title>{title}</title>
<meta name="description" content={description} />
</Helmet>
<PageHead title={title} description={description} />
);
// This is just for backward compatibility
export const AppWrapper = ({ children }: { children: React.ReactNode }) => (
<HelmetProvider>{children}</HelmetProvider>
<>{children}</>
);
export default PageMeta;

View File

@ -1,2 +1,9 @@
// Base URL configuration for API endpoints
export const API_BASE_URL = 'https://deploy.projekti.info';
// Environment configuration - simple string setting
export const API_MODE: string = 'local'; // 'local' or 'remote'
// Helper functions
export const isLocalMode = () => API_MODE === 'local';
export const isRemoteMode = () => API_MODE === 'remote';

1
src/generated/prisma/client.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from "./index"

View File

@ -0,0 +1 @@
module.exports = { ...require('.') }

1
src/generated/prisma/default.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from "./index"

View File

@ -0,0 +1 @@
module.exports = { ...require('.') }

1
src/generated/prisma/edge.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from "./default"

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,234 @@
Object.defineProperty(exports, "__esModule", { value: true });
const {
Decimal,
objectEnumValues,
makeStrictEnum,
Public,
getRuntime,
skip
} = require('./runtime/index-browser.js')
const Prisma = {}
exports.Prisma = Prisma
exports.$Enums = {}
/**
* Prisma Client JS version: 6.6.0
* Query Engine version: f676762280b54cd07c770017ed3711ddde35f37a
*/
Prisma.prismaVersion = {
client: "6.6.0",
engine: "f676762280b54cd07c770017ed3711ddde35f37a"
}
Prisma.PrismaClientKnownRequestError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientKnownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)};
Prisma.PrismaClientUnknownRequestError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientUnknownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.PrismaClientRustPanicError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientRustPanicError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.PrismaClientInitializationError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientInitializationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.PrismaClientValidationError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientValidationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.Decimal = Decimal
/**
* Re-export of sql-template-tag
*/
Prisma.sql = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`sqltag is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.empty = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`empty is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.join = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`join is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.raw = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`raw is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.validator = Public.validator
/**
* Extensions
*/
Prisma.getExtensionContext = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`Extensions.getExtensionContext is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.defineExtension = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`Extensions.defineExtension is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
/**
* Shorthand utilities for JSON filtering
*/
Prisma.DbNull = objectEnumValues.instances.DbNull
Prisma.JsonNull = objectEnumValues.instances.JsonNull
Prisma.AnyNull = objectEnumValues.instances.AnyNull
Prisma.NullTypes = {
DbNull: objectEnumValues.classes.DbNull,
JsonNull: objectEnumValues.classes.JsonNull,
AnyNull: objectEnumValues.classes.AnyNull
}
/**
* Enums
*/
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
ReadUncommitted: 'ReadUncommitted',
ReadCommitted: 'ReadCommitted',
RepeatableRead: 'RepeatableRead',
Serializable: 'Serializable'
});
exports.Prisma.ProjectScalarFieldEnum = {
id: 'id',
name: 'name',
repository: 'repository',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
};
exports.Prisma.DeploymentScalarFieldEnum = {
id: 'id',
projectId: 'projectId',
environmentId: 'environmentId',
status: 'status',
commitHash: 'commitHash',
deployedAt: 'deployedAt',
logs: 'logs'
};
exports.Prisma.EnvironmentScalarFieldEnum = {
id: 'id',
name: 'name',
url: 'url',
projectId: 'projectId',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
};
exports.Prisma.UserScalarFieldEnum = {
id: 'id',
email: 'email',
password: 'password',
name: 'name',
surname: 'surname',
active: 'active',
role: 'role',
lastLogin: 'lastLogin',
createdAt: 'createdAt',
updatedAt: 'updatedAt',
resetToken: 'resetToken',
resetTokenExpiry: 'resetTokenExpiry'
};
exports.Prisma.SortOrder = {
asc: 'asc',
desc: 'desc'
};
exports.Prisma.ProjectOrderByRelevanceFieldEnum = {
name: 'name',
repository: 'repository'
};
exports.Prisma.NullsOrder = {
first: 'first',
last: 'last'
};
exports.Prisma.DeploymentOrderByRelevanceFieldEnum = {
status: 'status',
commitHash: 'commitHash',
logs: 'logs'
};
exports.Prisma.EnvironmentOrderByRelevanceFieldEnum = {
name: 'name',
url: 'url'
};
exports.Prisma.UserOrderByRelevanceFieldEnum = {
email: 'email',
password: 'password',
name: 'name',
surname: 'surname',
role: 'role',
resetToken: 'resetToken'
};
exports.Prisma.ModelName = {
Project: 'Project',
Deployment: 'Deployment',
Environment: 'Environment',
User: 'User'
};
/**
* This is a stub Prisma Client that will error at runtime if called.
*/
class PrismaClient {
constructor() {
return new Proxy(this, {
get(target, prop) {
let message
const runtime = getRuntime()
if (runtime.isEdge) {
message = `PrismaClient is not configured to run in ${runtime.prettyName}. In order to run Prisma Client on edge runtime, either:
- Use Prisma Accelerate: https://pris.ly/d/accelerate
- Use Driver Adapters: https://pris.ly/d/driver-adapters
`;
} else {
message = 'PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in `' + runtime.prettyName + '`).'
}
message += `
If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report`
throw new Error(message)
}
})
}
}
exports.PrismaClient = PrismaClient
Object.assign(exports, Prisma)

7115
src/generated/prisma/index.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,140 @@
{
"name": "prisma-client-17e7e976046b9652083f42b4ee182a45cd98ff19613dd50aeb188ab5b9600242",
"main": "index.js",
"types": "index.d.ts",
"browser": "index-browser.js",
"exports": {
"./client": {
"require": {
"node": "./index.js",
"edge-light": "./wasm.js",
"workerd": "./wasm.js",
"worker": "./wasm.js",
"browser": "./index-browser.js",
"default": "./index.js"
},
"import": {
"node": "./index.js",
"edge-light": "./wasm.js",
"workerd": "./wasm.js",
"worker": "./wasm.js",
"browser": "./index-browser.js",
"default": "./index.js"
},
"default": "./index.js"
},
"./package.json": "./package.json",
".": {
"require": {
"node": "./index.js",
"edge-light": "./wasm.js",
"workerd": "./wasm.js",
"worker": "./wasm.js",
"browser": "./index-browser.js",
"default": "./index.js"
},
"import": {
"node": "./index.js",
"edge-light": "./wasm.js",
"workerd": "./wasm.js",
"worker": "./wasm.js",
"browser": "./index-browser.js",
"default": "./index.js"
},
"default": "./index.js"
},
"./edge": {
"types": "./edge.d.ts",
"require": "./edge.js",
"import": "./edge.js",
"default": "./edge.js"
},
"./react-native": {
"types": "./react-native.d.ts",
"require": "./react-native.js",
"import": "./react-native.js",
"default": "./react-native.js"
},
"./extension": {
"types": "./extension.d.ts",
"require": "./extension.js",
"import": "./extension.js",
"default": "./extension.js"
},
"./index-browser": {
"types": "./index.d.ts",
"require": "./index-browser.js",
"import": "./index-browser.js",
"default": "./index-browser.js"
},
"./index": {
"types": "./index.d.ts",
"require": "./index.js",
"import": "./index.js",
"default": "./index.js"
},
"./wasm": {
"types": "./wasm.d.ts",
"require": "./wasm.js",
"import": "./wasm.mjs",
"default": "./wasm.mjs"
},
"./runtime/client": {
"types": "./runtime/client.d.ts",
"require": "./runtime/client.js",
"import": "./runtime/client.mjs",
"default": "./runtime/client.mjs"
},
"./runtime/library": {
"types": "./runtime/library.d.ts",
"require": "./runtime/library.js",
"import": "./runtime/library.mjs",
"default": "./runtime/library.mjs"
},
"./runtime/binary": {
"types": "./runtime/binary.d.ts",
"require": "./runtime/binary.js",
"import": "./runtime/binary.mjs",
"default": "./runtime/binary.mjs"
},
"./runtime/wasm": {
"types": "./runtime/wasm.d.ts",
"require": "./runtime/wasm.js",
"import": "./runtime/wasm.mjs",
"default": "./runtime/wasm.mjs"
},
"./runtime/edge": {
"types": "./runtime/edge.d.ts",
"require": "./runtime/edge.js",
"import": "./runtime/edge-esm.js",
"default": "./runtime/edge-esm.js"
},
"./runtime/react-native": {
"types": "./runtime/react-native.d.ts",
"require": "./runtime/react-native.js",
"import": "./runtime/react-native.js",
"default": "./runtime/react-native.js"
},
"./generator-build": {
"require": "./generator-build/index.js",
"import": "./generator-build/index.js",
"default": "./generator-build/index.js"
},
"./sql": {
"require": {
"types": "./sql.d.ts",
"node": "./sql.js",
"default": "./sql.js"
},
"import": {
"types": "./sql.d.ts",
"node": "./sql.mjs",
"default": "./sql.mjs"
},
"default": "./sql.js"
},
"./*": "./*"
},
"version": "6.6.0",
"sideEffects": false
}

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,370 @@
declare class AnyNull extends NullTypesEnumValue {
private readonly _brand_AnyNull;
}
declare type Args<T, F extends Operation> = T extends {
[K: symbol]: {
types: {
operations: {
[K in F]: {
args: any;
};
};
};
};
} ? T[symbol]['types']['operations'][F]['args'] : any;
declare class DbNull extends NullTypesEnumValue {
private readonly _brand_DbNull;
}
export declare function Decimal(n: Decimal.Value): Decimal;
export declare namespace Decimal {
export type Constructor = typeof Decimal;
export type Instance = Decimal;
export type Rounding = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
export type Modulo = Rounding | 9;
export type Value = string | number | Decimal;
// http://mikemcl.github.io/decimal.js/#constructor-properties
export interface Config {
precision?: number;
rounding?: Rounding;
toExpNeg?: number;
toExpPos?: number;
minE?: number;
maxE?: number;
crypto?: boolean;
modulo?: Modulo;
defaults?: boolean;
}
}
export declare class Decimal {
readonly d: number[];
readonly e: number;
readonly s: number;
constructor(n: Decimal.Value);
absoluteValue(): Decimal;
abs(): Decimal;
ceil(): Decimal;
clampedTo(min: Decimal.Value, max: Decimal.Value): Decimal;
clamp(min: Decimal.Value, max: Decimal.Value): Decimal;
comparedTo(n: Decimal.Value): number;
cmp(n: Decimal.Value): number;
cosine(): Decimal;
cos(): Decimal;
cubeRoot(): Decimal;
cbrt(): Decimal;
decimalPlaces(): number;
dp(): number;
dividedBy(n: Decimal.Value): Decimal;
div(n: Decimal.Value): Decimal;
dividedToIntegerBy(n: Decimal.Value): Decimal;
divToInt(n: Decimal.Value): Decimal;
equals(n: Decimal.Value): boolean;
eq(n: Decimal.Value): boolean;
floor(): Decimal;
greaterThan(n: Decimal.Value): boolean;
gt(n: Decimal.Value): boolean;
greaterThanOrEqualTo(n: Decimal.Value): boolean;
gte(n: Decimal.Value): boolean;
hyperbolicCosine(): Decimal;
cosh(): Decimal;
hyperbolicSine(): Decimal;
sinh(): Decimal;
hyperbolicTangent(): Decimal;
tanh(): Decimal;
inverseCosine(): Decimal;
acos(): Decimal;
inverseHyperbolicCosine(): Decimal;
acosh(): Decimal;
inverseHyperbolicSine(): Decimal;
asinh(): Decimal;
inverseHyperbolicTangent(): Decimal;
atanh(): Decimal;
inverseSine(): Decimal;
asin(): Decimal;
inverseTangent(): Decimal;
atan(): Decimal;
isFinite(): boolean;
isInteger(): boolean;
isInt(): boolean;
isNaN(): boolean;
isNegative(): boolean;
isNeg(): boolean;
isPositive(): boolean;
isPos(): boolean;
isZero(): boolean;
lessThan(n: Decimal.Value): boolean;
lt(n: Decimal.Value): boolean;
lessThanOrEqualTo(n: Decimal.Value): boolean;
lte(n: Decimal.Value): boolean;
logarithm(n?: Decimal.Value): Decimal;
log(n?: Decimal.Value): Decimal;
minus(n: Decimal.Value): Decimal;
sub(n: Decimal.Value): Decimal;
modulo(n: Decimal.Value): Decimal;
mod(n: Decimal.Value): Decimal;
naturalExponential(): Decimal;
exp(): Decimal;
naturalLogarithm(): Decimal;
ln(): Decimal;
negated(): Decimal;
neg(): Decimal;
plus(n: Decimal.Value): Decimal;
add(n: Decimal.Value): Decimal;
precision(includeZeros?: boolean): number;
sd(includeZeros?: boolean): number;
round(): Decimal;
sine() : Decimal;
sin() : Decimal;
squareRoot(): Decimal;
sqrt(): Decimal;
tangent() : Decimal;
tan() : Decimal;
times(n: Decimal.Value): Decimal;
mul(n: Decimal.Value) : Decimal;
toBinary(significantDigits?: number): string;
toBinary(significantDigits: number, rounding: Decimal.Rounding): string;
toDecimalPlaces(decimalPlaces?: number): Decimal;
toDecimalPlaces(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
toDP(decimalPlaces?: number): Decimal;
toDP(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
toExponential(decimalPlaces?: number): string;
toExponential(decimalPlaces: number, rounding: Decimal.Rounding): string;
toFixed(decimalPlaces?: number): string;
toFixed(decimalPlaces: number, rounding: Decimal.Rounding): string;
toFraction(max_denominator?: Decimal.Value): Decimal[];
toHexadecimal(significantDigits?: number): string;
toHexadecimal(significantDigits: number, rounding: Decimal.Rounding): string;
toHex(significantDigits?: number): string;
toHex(significantDigits: number, rounding?: Decimal.Rounding): string;
toJSON(): string;
toNearest(n: Decimal.Value, rounding?: Decimal.Rounding): Decimal;
toNumber(): number;
toOctal(significantDigits?: number): string;
toOctal(significantDigits: number, rounding: Decimal.Rounding): string;
toPower(n: Decimal.Value): Decimal;
pow(n: Decimal.Value): Decimal;
toPrecision(significantDigits?: number): string;
toPrecision(significantDigits: number, rounding: Decimal.Rounding): string;
toSignificantDigits(significantDigits?: number): Decimal;
toSignificantDigits(significantDigits: number, rounding: Decimal.Rounding): Decimal;
toSD(significantDigits?: number): Decimal;
toSD(significantDigits: number, rounding: Decimal.Rounding): Decimal;
toString(): string;
truncated(): Decimal;
trunc(): Decimal;
valueOf(): string;
static abs(n: Decimal.Value): Decimal;
static acos(n: Decimal.Value): Decimal;
static acosh(n: Decimal.Value): Decimal;
static add(x: Decimal.Value, y: Decimal.Value): Decimal;
static asin(n: Decimal.Value): Decimal;
static asinh(n: Decimal.Value): Decimal;
static atan(n: Decimal.Value): Decimal;
static atanh(n: Decimal.Value): Decimal;
static atan2(y: Decimal.Value, x: Decimal.Value): Decimal;
static cbrt(n: Decimal.Value): Decimal;
static ceil(n: Decimal.Value): Decimal;
static clamp(n: Decimal.Value, min: Decimal.Value, max: Decimal.Value): Decimal;
static clone(object?: Decimal.Config): Decimal.Constructor;
static config(object: Decimal.Config): Decimal.Constructor;
static cos(n: Decimal.Value): Decimal;
static cosh(n: Decimal.Value): Decimal;
static div(x: Decimal.Value, y: Decimal.Value): Decimal;
static exp(n: Decimal.Value): Decimal;
static floor(n: Decimal.Value): Decimal;
static hypot(...n: Decimal.Value[]): Decimal;
static isDecimal(object: any): object is Decimal;
static ln(n: Decimal.Value): Decimal;
static log(n: Decimal.Value, base?: Decimal.Value): Decimal;
static log2(n: Decimal.Value): Decimal;
static log10(n: Decimal.Value): Decimal;
static max(...n: Decimal.Value[]): Decimal;
static min(...n: Decimal.Value[]): Decimal;
static mod(x: Decimal.Value, y: Decimal.Value): Decimal;
static mul(x: Decimal.Value, y: Decimal.Value): Decimal;
static noConflict(): Decimal.Constructor; // Browser only
static pow(base: Decimal.Value, exponent: Decimal.Value): Decimal;
static random(significantDigits?: number): Decimal;
static round(n: Decimal.Value): Decimal;
static set(object: Decimal.Config): Decimal.Constructor;
static sign(n: Decimal.Value): number;
static sin(n: Decimal.Value): Decimal;
static sinh(n: Decimal.Value): Decimal;
static sqrt(n: Decimal.Value): Decimal;
static sub(x: Decimal.Value, y: Decimal.Value): Decimal;
static sum(...n: Decimal.Value[]): Decimal;
static tan(n: Decimal.Value): Decimal;
static tanh(n: Decimal.Value): Decimal;
static trunc(n: Decimal.Value): Decimal;
static readonly default?: Decimal.Constructor;
static readonly Decimal?: Decimal.Constructor;
static readonly precision: number;
static readonly rounding: Decimal.Rounding;
static readonly toExpNeg: number;
static readonly toExpPos: number;
static readonly minE: number;
static readonly maxE: number;
static readonly crypto: boolean;
static readonly modulo: Decimal.Modulo;
static readonly ROUND_UP: 0;
static readonly ROUND_DOWN: 1;
static readonly ROUND_CEIL: 2;
static readonly ROUND_FLOOR: 3;
static readonly ROUND_HALF_UP: 4;
static readonly ROUND_HALF_DOWN: 5;
static readonly ROUND_HALF_EVEN: 6;
static readonly ROUND_HALF_CEIL: 7;
static readonly ROUND_HALF_FLOOR: 8;
static readonly EUCLID: 9;
}
declare type Exact<A, W> = (A extends unknown ? (W extends A ? {
[K in keyof A]: Exact<A[K], W[K]>;
} : W) : never) | (A extends Narrowable ? A : never);
export declare function getRuntime(): GetRuntimeOutput;
declare type GetRuntimeOutput = {
id: RuntimeName;
prettyName: string;
isEdge: boolean;
};
declare class JsonNull extends NullTypesEnumValue {
private readonly _brand_JsonNull;
}
/**
* Generates more strict variant of an enum which, unlike regular enum,
* throws on non-existing property access. This can be useful in following situations:
* - we have an API, that accepts both `undefined` and `SomeEnumType` as an input
* - enum values are generated dynamically from DMMF.
*
* In that case, if using normal enums and no compile-time typechecking, using non-existing property
* will result in `undefined` value being used, which will be accepted. Using strict enum
* in this case will help to have a runtime exception, telling you that you are probably doing something wrong.
*
* Note: if you need to check for existence of a value in the enum you can still use either
* `in` operator or `hasOwnProperty` function.
*
* @param definition
* @returns
*/
export declare function makeStrictEnum<T extends Record<PropertyKey, string | number>>(definition: T): T;
declare type Narrowable = string | number | bigint | boolean | [];
declare class NullTypesEnumValue extends ObjectEnumValue {
_getNamespace(): string;
}
/**
* Base class for unique values of object-valued enums.
*/
declare abstract class ObjectEnumValue {
constructor(arg?: symbol);
abstract _getNamespace(): string;
_getName(): string;
toString(): string;
}
export declare const objectEnumValues: {
classes: {
DbNull: typeof DbNull;
JsonNull: typeof JsonNull;
AnyNull: typeof AnyNull;
};
instances: {
DbNull: DbNull;
JsonNull: JsonNull;
AnyNull: AnyNull;
};
};
declare type Operation = 'findFirst' | 'findFirstOrThrow' | 'findUnique' | 'findUniqueOrThrow' | 'findMany' | 'create' | 'createMany' | 'createManyAndReturn' | 'update' | 'updateMany' | 'updateManyAndReturn' | 'upsert' | 'delete' | 'deleteMany' | 'aggregate' | 'count' | 'groupBy' | '$queryRaw' | '$executeRaw' | '$queryRawUnsafe' | '$executeRawUnsafe' | 'findRaw' | 'aggregateRaw' | '$runCommandRaw';
declare namespace Public {
export {
validator
}
}
export { Public }
declare type RuntimeName = 'workerd' | 'deno' | 'netlify' | 'node' | 'bun' | 'edge-light' | '';
declare function validator<V>(): <S>(select: Exact<S, V>) => S;
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation>(client: C, model: M, operation: O): <S>(select: Exact<S, Args<C[M], O>>) => S;
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation, P extends keyof Args<C[M], O>>(client: C, model: M, operation: O, prop: P): <S>(select: Exact<S, Args<C[M], O>[P]>) => S;
export { }

File diff suppressed because one or more lines are too long

3604
src/generated/prisma/runtime/library.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,63 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Project {
id Int @id @default(autoincrement())
name String
repository String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deployments Deployment[]
environments Environment[]
}
model Deployment {
id Int @id @default(autoincrement())
projectId Int
project Project @relation(fields: [projectId], references: [id])
environmentId Int
environment Environment @relation(fields: [environmentId], references: [id])
status String @default("pending") // pending, success, failed
commitHash String
deployedAt DateTime @default(now())
logs String? @db.Text
}
model Environment {
id Int @id @default(autoincrement())
name String
url String
projectId Int
project Project @relation(fields: [projectId], references: [id])
deployments Deployment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model User {
id Int @id @default(autoincrement())
email String @unique
password String
name String
surname String
active Boolean @default(true)
role String @default("user") // user, admin, etc.
lastLogin DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resetToken String?
resetTokenExpiry DateTime?
}

1
src/generated/prisma/wasm.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from "./index"

View File

@ -0,0 +1,234 @@
Object.defineProperty(exports, "__esModule", { value: true });
const {
Decimal,
objectEnumValues,
makeStrictEnum,
Public,
getRuntime,
skip
} = require('./runtime/index-browser.js')
const Prisma = {}
exports.Prisma = Prisma
exports.$Enums = {}
/**
* Prisma Client JS version: 6.6.0
* Query Engine version: f676762280b54cd07c770017ed3711ddde35f37a
*/
Prisma.prismaVersion = {
client: "6.6.0",
engine: "f676762280b54cd07c770017ed3711ddde35f37a"
}
Prisma.PrismaClientKnownRequestError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientKnownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)};
Prisma.PrismaClientUnknownRequestError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientUnknownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.PrismaClientRustPanicError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientRustPanicError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.PrismaClientInitializationError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientInitializationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.PrismaClientValidationError = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`PrismaClientValidationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.Decimal = Decimal
/**
* Re-export of sql-template-tag
*/
Prisma.sql = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`sqltag is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.empty = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`empty is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.join = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`join is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.raw = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`raw is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.validator = Public.validator
/**
* Extensions
*/
Prisma.getExtensionContext = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`Extensions.getExtensionContext is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
Prisma.defineExtension = () => {
const runtimeName = getRuntime().prettyName;
throw new Error(`Extensions.defineExtension is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
)}
/**
* Shorthand utilities for JSON filtering
*/
Prisma.DbNull = objectEnumValues.instances.DbNull
Prisma.JsonNull = objectEnumValues.instances.JsonNull
Prisma.AnyNull = objectEnumValues.instances.AnyNull
Prisma.NullTypes = {
DbNull: objectEnumValues.classes.DbNull,
JsonNull: objectEnumValues.classes.JsonNull,
AnyNull: objectEnumValues.classes.AnyNull
}
/**
* Enums
*/
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
ReadUncommitted: 'ReadUncommitted',
ReadCommitted: 'ReadCommitted',
RepeatableRead: 'RepeatableRead',
Serializable: 'Serializable'
});
exports.Prisma.ProjectScalarFieldEnum = {
id: 'id',
name: 'name',
repository: 'repository',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
};
exports.Prisma.DeploymentScalarFieldEnum = {
id: 'id',
projectId: 'projectId',
environmentId: 'environmentId',
status: 'status',
commitHash: 'commitHash',
deployedAt: 'deployedAt',
logs: 'logs'
};
exports.Prisma.EnvironmentScalarFieldEnum = {
id: 'id',
name: 'name',
url: 'url',
projectId: 'projectId',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
};
exports.Prisma.UserScalarFieldEnum = {
id: 'id',
email: 'email',
password: 'password',
name: 'name',
surname: 'surname',
active: 'active',
role: 'role',
lastLogin: 'lastLogin',
createdAt: 'createdAt',
updatedAt: 'updatedAt',
resetToken: 'resetToken',
resetTokenExpiry: 'resetTokenExpiry'
};
exports.Prisma.SortOrder = {
asc: 'asc',
desc: 'desc'
};
exports.Prisma.ProjectOrderByRelevanceFieldEnum = {
name: 'name',
repository: 'repository'
};
exports.Prisma.NullsOrder = {
first: 'first',
last: 'last'
};
exports.Prisma.DeploymentOrderByRelevanceFieldEnum = {
status: 'status',
commitHash: 'commitHash',
logs: 'logs'
};
exports.Prisma.EnvironmentOrderByRelevanceFieldEnum = {
name: 'name',
url: 'url'
};
exports.Prisma.UserOrderByRelevanceFieldEnum = {
email: 'email',
password: 'password',
name: 'name',
surname: 'surname',
role: 'role',
resetToken: 'resetToken'
};
exports.Prisma.ModelName = {
Project: 'Project',
Deployment: 'Deployment',
Environment: 'Environment',
User: 'User'
};
/**
* This is a stub Prisma Client that will error at runtime if called.
*/
class PrismaClient {
constructor() {
return new Proxy(this, {
get(target, prop) {
let message
const runtime = getRuntime()
if (runtime.isEdge) {
message = `PrismaClient is not configured to run in ${runtime.prettyName}. In order to run Prisma Client on edge runtime, either:
- Use Prisma Accelerate: https://pris.ly/d/accelerate
- Use Driver Adapters: https://pris.ly/d/driver-adapters
`;
} else {
message = 'PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in `' + runtime.prettyName + '`).'
}
message += `
If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report`
throw new Error(message)
}
})
}
}
exports.PrismaClient = PrismaClient
Object.assign(exports, Prisma)

View File

@ -4,7 +4,6 @@ import { Link, useLocation } from "react-router";
// Assume these icons are imported from an icon library
import {
BoxCubeIcon,
CalenderIcon,
ChevronDownIcon,
GridIcon,
HorizontaLDots,
@ -30,57 +29,14 @@ const navItems: NavItem[] = [
name: "Dashboard",
path: "/",
},
{
icon: <CalenderIcon />,
name: "Calendar",
path: "/calendar",
},
{
icon: <UserCircleIcon />,
name: "User Profile",
path: "/profile",
},
{
name: "Forms",
icon: <ListIcon />,
subItems: [{ name: "Form Elements", path: "/form-elements", pro: false }],
},
{
name: "Tables",
icon: <TableIcon />,
subItems: [{ name: "Basic Tables", path: "/basic-tables", pro: false }],
},
{
name: "Pages",
icon: <PageIcon />,
subItems: [
{ name: "Blank Page", path: "/blank", pro: false },
{ name: "404 Error", path: "/error-404", pro: false },
],
},
];
const othersItems: NavItem[] = [
{
icon: <PieChartIcon />,
name: "Charts",
subItems: [
{ name: "Line Chart", path: "/line-chart", pro: false },
{ name: "Bar Chart", path: "/bar-chart", pro: false },
],
},
{
icon: <BoxCubeIcon />,
name: "UI Elements",
subItems: [
{ name: "Alerts", path: "/alerts", pro: false },
{ name: "Avatar", path: "/avatars", pro: false },
{ name: "Badge", path: "/badge", pro: false },
{ name: "Buttons", path: "/buttons", pro: false },
{ name: "Images", path: "/images", pro: false },
{ name: "Videos", path: "/videos", pro: false },
],
},
];
const AppSidebar: React.FC = () => {

View File

@ -4,7 +4,7 @@ import "./index.css";
import "swiper/swiper-bundle.css";
import "flatpickr/dist/flatpickr.css";
import App from "./App.tsx";
import { AppWrapper } from "./components/common/PageMeta.tsx";
import { HeadProvider } from "./components/common/PageHead.tsx";
import { ThemeProvider } from "./context/ThemeContext.tsx";
import { AuthProvider } from "./context/AuthContext";
@ -12,9 +12,9 @@ createRoot(document.getElementById("root")!).render(
<StrictMode>
<AuthProvider>
<ThemeProvider>
<AppWrapper>
<HeadProvider>
<App />
</AppWrapper>
</HeadProvider>
</ThemeProvider>
</AuthProvider>
</StrictMode>,

View File

@ -1,284 +0,0 @@
import { useState, useRef, useEffect } from "react";
import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import interactionPlugin from "@fullcalendar/interaction";
import { EventInput, DateSelectArg, EventClickArg } from "@fullcalendar/core";
import { Modal } from "../components/ui/modal";
import { useModal } from "../hooks/useModal";
import PageMeta from "../components/common/PageMeta";
interface CalendarEvent extends EventInput {
extendedProps: {
calendar: string;
};
}
const Calendar: React.FC = () => {
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(
null
);
const [eventTitle, setEventTitle] = useState("");
const [eventStartDate, setEventStartDate] = useState("");
const [eventEndDate, setEventEndDate] = useState("");
const [eventLevel, setEventLevel] = useState("");
const [events, setEvents] = useState<CalendarEvent[]>([]);
const calendarRef = useRef<FullCalendar>(null);
const { isOpen, openModal, closeModal } = useModal();
const calendarsEvents = {
Danger: "danger",
Success: "success",
Primary: "primary",
Warning: "warning",
};
useEffect(() => {
// Initialize with some events
setEvents([
{
id: "1",
title: "Event Conf.",
start: new Date().toISOString().split("T")[0],
extendedProps: { calendar: "Danger" },
},
{
id: "2",
title: "Meeting",
start: new Date(Date.now() + 86400000).toISOString().split("T")[0],
extendedProps: { calendar: "Success" },
},
{
id: "3",
title: "Workshop",
start: new Date(Date.now() + 172800000).toISOString().split("T")[0],
end: new Date(Date.now() + 259200000).toISOString().split("T")[0],
extendedProps: { calendar: "Primary" },
},
]);
}, []);
const handleDateSelect = (selectInfo: DateSelectArg) => {
resetModalFields();
setEventStartDate(selectInfo.startStr);
setEventEndDate(selectInfo.endStr || selectInfo.startStr);
openModal();
};
const handleEventClick = (clickInfo: EventClickArg) => {
const event = clickInfo.event;
setSelectedEvent(event as unknown as CalendarEvent);
setEventTitle(event.title);
setEventStartDate(event.start?.toISOString().split("T")[0] || "");
setEventEndDate(event.end?.toISOString().split("T")[0] || "");
setEventLevel(event.extendedProps.calendar);
openModal();
};
const handleAddOrUpdateEvent = () => {
if (selectedEvent) {
// Update existing event
setEvents((prevEvents) =>
prevEvents.map((event) =>
event.id === selectedEvent.id
? {
...event,
title: eventTitle,
start: eventStartDate,
end: eventEndDate,
extendedProps: { calendar: eventLevel },
}
: event
)
);
} else {
// Add new event
const newEvent: CalendarEvent = {
id: Date.now().toString(),
title: eventTitle,
start: eventStartDate,
end: eventEndDate,
allDay: true,
extendedProps: { calendar: eventLevel },
};
setEvents((prevEvents) => [...prevEvents, newEvent]);
}
closeModal();
resetModalFields();
};
const resetModalFields = () => {
setEventTitle("");
setEventStartDate("");
setEventEndDate("");
setEventLevel("");
setSelectedEvent(null);
};
return (
<>
<PageMeta
title="React.js Calendar Dashboard | TailAdmin - Next.js Admin Dashboard Template"
description="This is React.js Calendar Dashboard page for TailAdmin - React.js Tailwind CSS Admin Dashboard Template"
/>
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
<div className="custom-calendar">
<FullCalendar
ref={calendarRef}
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
initialView="dayGridMonth"
headerToolbar={{
left: "prev,next addEventButton",
center: "title",
right: "dayGridMonth,timeGridWeek,timeGridDay",
}}
events={events}
selectable={true}
select={handleDateSelect}
eventClick={handleEventClick}
eventContent={renderEventContent}
customButtons={{
addEventButton: {
text: "Add Event +",
click: openModal,
},
}}
/>
</div>
<Modal
isOpen={isOpen}
onClose={closeModal}
className="max-w-[700px] p-6 lg:p-10"
>
<div className="flex flex-col px-2 overflow-y-auto custom-scrollbar">
<div>
<h5 className="mb-2 font-semibold text-gray-800 modal-title text-theme-xl dark:text-white/90 lg:text-2xl">
{selectedEvent ? "Edit Event" : "Add Event"}
</h5>
<p className="text-sm text-gray-500 dark:text-gray-400">
Plan your next big moment: schedule or edit an event to stay on
track
</p>
</div>
<div className="mt-8">
<div>
<div>
<label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
Event Title
</label>
<input
id="event-title"
type="text"
value={eventTitle}
onChange={(e) => setEventTitle(e.target.value)}
className="dark:bg-dark-900 h-11 w-full rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800"
/>
</div>
</div>
<div className="mt-6">
<label className="block mb-4 text-sm font-medium text-gray-700 dark:text-gray-400">
Event Color
</label>
<div className="flex flex-wrap items-center gap-4 sm:gap-5">
{Object.entries(calendarsEvents).map(([key, value]) => (
<div key={key} className="n-chk">
<div
className={`form-check form-check-${value} form-check-inline`}
>
<label
className="flex items-center text-sm text-gray-700 form-check-label dark:text-gray-400"
htmlFor={`modal${key}`}
>
<span className="relative">
<input
className="sr-only form-check-input"
type="radio"
name="event-level"
value={key}
id={`modal${key}`}
checked={eventLevel === key}
onChange={() => setEventLevel(key)}
/>
<span className="flex items-center justify-center w-5 h-5 mr-2 border border-gray-300 rounded-full box dark:border-gray-700">
<span
className={`h-2 w-2 rounded-full bg-white ${
eventLevel === key ? "block" : "hidden"
}`}
></span>
</span>
</span>
{key}
</label>
</div>
</div>
))}
</div>
</div>
<div className="mt-6">
<label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
Enter Start Date
</label>
<div className="relative">
<input
id="event-start-date"
type="date"
value={eventStartDate}
onChange={(e) => setEventStartDate(e.target.value)}
className="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 pl-4 pr-11 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800"
/>
</div>
</div>
<div className="mt-6">
<label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
Enter End Date
</label>
<div className="relative">
<input
id="event-end-date"
type="date"
value={eventEndDate}
onChange={(e) => setEventEndDate(e.target.value)}
className="dark:bg-dark-900 h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent bg-none px-4 py-2.5 pl-4 pr-11 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-hidden focus:ring-3 focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800"
/>
</div>
</div>
</div>
<div className="flex items-center gap-3 mt-6 modal-footer sm:justify-end">
<button
onClick={closeModal}
type="button"
className="flex w-full justify-center rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] sm:w-auto"
>
Close
</button>
<button
onClick={handleAddOrUpdateEvent}
type="button"
className="btn btn-success btn-update-event flex w-full justify-center rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-600 sm:w-auto"
>
{selectedEvent ? "Update Changes" : "Add Event"}
</button>
</div>
</div>
</Modal>
</div>
</>
);
};
const renderEventContent = (eventInfo: any) => {
const colorClass = `fc-bg-${eventInfo.event.extendedProps.calendar.toLowerCase()}`;
return (
<div
className={`event-fc-color flex fc-event-main ${colorClass} p-1 rounded-sm`}
>
<div className="fc-daygrid-event-dot"></div>
<div className="fc-event-time">{eventInfo.timeText}</div>
<div className="fc-event-title">{eventInfo.event.title}</div>
</div>
);
};
export default Calendar;

104
src/services/authService.ts Normal file
View File

@ -0,0 +1,104 @@
const { PrismaClient } = require('@prisma/client');
const crypto = require('crypto-js');
const prisma = new PrismaClient();
/**
* Authentication service for the backend API
*/
/**
* Response structure for login attempts
*/
interface LoginResponse {
success: boolean;
token?: string;
error?: string;
user?: {
id: number;
name: string;
email: string;
role: string;
};
}
// Generate a secure token
const generateToken = (length = 64) => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let token = '';
for (let i = 0; i < length; i++) {
token += characters.charAt(Math.floor(Math.random() * characters.length));
}
return token;
};
/**
* Authenticate user against the database
*/
async function authenticateUser(email, password) {
try {
// Hash the password using the same algorithm as the client
const hashedPassword = crypto.SHA256(password).toString();
// Find the user with matching email
const user = await prisma.user.findUnique({
where: { email }
});
// User not found
if (!user) {
return {
success: false,
error: 'User not found'
};
}
// Check if user is active
if (!user.active) {
return {
success: false,
error: 'Account is inactive'
};
}
// Verify password
if (user.password !== hashedPassword) {
return {
success: false,
error: 'Invalid credentials'
};
}
// Generate authentication token
const token = generateToken();
// Update last login timestamp
await prisma.user.update({
where: { id: user.id },
data: { lastLogin: new Date() }
});
// Return successful login response
return {
success: true,
token,
user: {
id: user.id,
name: `${user.name} ${user.surname}`,
email: user.email,
role: user.role
}
};
} catch (error) {
console.error('Authentication error:', error);
return {
success: false,
error: 'An error occurred during authentication'
};
}
}
// Export using CommonJS syntax
module.exports = {
authenticateUser
};

68
src/services/dbService.ts Normal file
View File

@ -0,0 +1,68 @@
/**
* Database service for frontend
*
* This service provides an interface for frontend code to make database
* operations via API calls instead of directly using Prisma in the browser
*/
import { API_BASE_URL } from '../config/apiConfig';
// API endpoints
const API_ENDPOINTS = {
LOGIN: '/api/login',
USERS: '/api/users',
};
/**
* User authentication service
*/
export const authApi = {
/**
* Authenticate a user with email and password
*/
login: async (email: string, hashedPassword: string) => {
try {
const response = await fetch(`${API_BASE_URL}${API_ENDPOINTS.LOGIN}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
body: JSON.stringify({ email, password: hashedPassword }),
credentials: 'include',
});
return await response.json();
} catch (error) {
console.error('Login error:', error);
return {
success: false,
error: 'Failed to connect to authentication service'
};
}
},
};
/**
* User management service
*/
export const usersApi = {
/**
* Get user by email
*/
getUserByEmail: async (email: string, token: string) => {
try {
const response = await fetch(`${API_BASE_URL}${API_ENDPOINTS.USERS}?email=${encodeURIComponent(email)}`, {
headers: {
'Authorization': `Bearer ${token}`,
'X-Requested-With': 'XMLHttpRequest',
},
});
return await response.json();
} catch (error) {
console.error('Get user error:', error);
return { success: false, error: 'Failed to fetch user data' };
}
},
};

117
src/services/localApi.ts Normal file
View File

@ -0,0 +1,117 @@
// Local API service to handle authentication
import { API_BASE_URL } from '../config/apiConfig';
// Generate a random token as fallback if API fails
const generateToken = (length = 32) => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let token = '';
for (let i = 0; i < length; i++) {
token += characters.charAt(Math.floor(Math.random() * characters.length));
}
return token;
};
interface LoginResponse {
success: boolean;
token?: string;
error?: string;
user?: {
id?: number;
name: string;
email: string;
role: string;
};
}
// Demo users for fallback if database connection fails
const FALLBACK_USERS = [
{
email: 'admin@example.com',
// This is a SHA-256 hash of 'admin123'
passwordHash: '240be518fabd2724ddb6f04eeb1da5967448d7e831c08c8fa822809f74c720a9',
name: 'Admin User',
role: 'admin',
},
{
email: 'user@example.com',
// This is a SHA-256 hash of 'user123'
passwordHash: 'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3',
name: 'Regular User',
role: 'user',
}
];
// Login function that uses MySQL database via API
export const localLogin = async (email: string, hashedPassword: string): Promise<LoginResponse> => {
try {
console.log('Attempting to login with database:', email);
// Call the login API endpoint
const response = await fetch(`${API_BASE_URL}/api/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
body: JSON.stringify({ email, password: hashedPassword }),
credentials: 'include',
});
// Parse the API response
const result = await response.json();
if (response.ok && result.success) {
console.log('Database login successful for:', email);
return result;
}
console.warn('Database login failed, error:', result.error);
console.warn('Falling back to demo users...');
// If API call fails, fall back to demo users
return fallbackLogin(email, hashedPassword);
} catch (error) {
console.error('API login error, falling back to demo users:', error);
return fallbackLogin(email, hashedPassword);
}
};
// Fallback login function using demo users
const fallbackLogin = async (email: string, hashedPassword: string): Promise<LoginResponse> => {
// Simulate network delay
await new Promise(resolve => setTimeout(resolve, 800));
// Find user with matching email
const user = FALLBACK_USERS.find(u => u.email === email);
if (!user) {
return {
success: false,
error: 'User not found'
};
}
// Check password hash
if (user.passwordHash !== hashedPassword) {
return {
success: false,
error: 'Invalid credentials'
};
}
// Generate authentication token
const token = generateToken();
console.log('Fallback login successful for demo user:', user.email);
// Return successful login response
return {
success: true,
token,
user: {
name: user.name,
email: user.email,
role: user.role
}
};
};