feat: add god-mode API and auto-permissions - SECURE TOKEN IN COOLIFY ENV ONLY
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -29,3 +29,12 @@ postgres_data/
|
|||||||
redis_data/
|
redis_data/
|
||||||
directus_uploads/
|
directus_uploads/
|
||||||
directus_extensions/
|
directus_extensions/
|
||||||
|
|
||||||
|
# Secrets (God Mode Token, etc.)
|
||||||
|
.secrets/
|
||||||
|
*.secret
|
||||||
|
*.key
|
||||||
|
|
||||||
|
# Directus extension builds
|
||||||
|
directus-extensions/**/node_modules/
|
||||||
|
directus-extensions/**/dist/
|
||||||
|
|||||||
305
GOD_MODE_API.md
Normal file
305
GOD_MODE_API.md
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
# God Mode API - Documentation
|
||||||
|
|
||||||
|
## 🔐 Overview
|
||||||
|
|
||||||
|
The God Mode API provides unrestricted access to the Spark Platform's database and Directus system. It bypasses all authentication and permission checks.
|
||||||
|
|
||||||
|
**Security:** Access requires `X-God-Token` header with secret token.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔑 Your Secure Token
|
||||||
|
|
||||||
|
```
|
||||||
|
GOD_MODE_TOKEN=jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ CRITICAL:**
|
||||||
|
- This token is for YOU and your AI assistant ONLY
|
||||||
|
- NEVER commit to git (already in `.gitignore`)
|
||||||
|
- NEVER share publicly
|
||||||
|
- Store in Coolify environment variables
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Setup in Coolify
|
||||||
|
|
||||||
|
1. Go to Coolify → Your Spark Project
|
||||||
|
2. Click "Directus" service
|
||||||
|
3. Go to "Environment Variables"
|
||||||
|
4. Click "Add Variable":
|
||||||
|
- **Name:** `GOD_MODE_TOKEN`
|
||||||
|
- **Value:** `jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA`
|
||||||
|
5. Save and redeploy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📡 API Endpoints
|
||||||
|
|
||||||
|
### Base URL
|
||||||
|
```
|
||||||
|
https://spark.jumpstartscaling.com/god
|
||||||
|
```
|
||||||
|
|
||||||
|
All endpoints require header:
|
||||||
|
```
|
||||||
|
X-God-Token: jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1. Check God Mode Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X GET https://spark.jumpstartscaling.com/god/status \
|
||||||
|
-H "X-God-Token: jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"god_mode": true,
|
||||||
|
"database": {
|
||||||
|
"tables": 39,
|
||||||
|
"collections": 39,
|
||||||
|
"permissions": 156
|
||||||
|
},
|
||||||
|
"timestamp": "2025-12-14T11:05:00.000Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Initialize Database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Read SQL file
|
||||||
|
SQL_CONTENT=$(cat complete_schema.sql)
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
curl -X POST https://spark.jumpstartscaling.com/god/setup/database \
|
||||||
|
-H "X-God-Token: jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"sql\": $(jq -Rs . < complete_schema.sql)}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"tables_created": 39,
|
||||||
|
"tables": [
|
||||||
|
"sites",
|
||||||
|
"pages",
|
||||||
|
"posts",
|
||||||
|
"avatar_intelligence",
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Grant All Permissions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://spark.jumpstartscaling.com/god/permissions/grant-all \
|
||||||
|
-H "X-God-Token: jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"permissions_granted": 156,
|
||||||
|
"collections": 39
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Execute Raw SQL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://spark.jumpstartscaling.com/god/sql/execute \
|
||||||
|
-H "X-God-Token: jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"sql": "SELECT * FROM sites ORDER BY date_created DESC LIMIT 5;"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"id": "abc123",
|
||||||
|
"name": "My Site",
|
||||||
|
"domain": "example.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rowCount": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Get All Collections (Including System)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X GET https://spark.jumpstartscaling.com/god/collections/all \
|
||||||
|
-H "X-God-Token: jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"count": 75,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"collection": "directus_users",
|
||||||
|
"icon": "people",
|
||||||
|
...
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collection": "sites",
|
||||||
|
"icon": "dns",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Make User Admin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://spark.jumpstartscaling.com/god/user/make-admin \
|
||||||
|
-H "X-God-Token: jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"email": "user@example.com"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"user": {
|
||||||
|
"id": "user123",
|
||||||
|
"email": "user@example.com",
|
||||||
|
"role": "admin-role-id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ Auto-Permissions Hook
|
||||||
|
|
||||||
|
The platform includes an auto-permissions hook that runs on Directus startup:
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Automatically grants all permissions to Administrator policy
|
||||||
|
- Runs after Directus initialization
|
||||||
|
- Checks for existing permissions first
|
||||||
|
- Creates 4 permissions per collection (create, read, update, delete)
|
||||||
|
|
||||||
|
**No manual action needed!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Use Cases
|
||||||
|
|
||||||
|
### Fresh Deployment Setup
|
||||||
|
```bash
|
||||||
|
# 1. Check status
|
||||||
|
curl -X GET .../god/status -H "X-God-Token: ..."
|
||||||
|
|
||||||
|
# 2. Initialize database
|
||||||
|
curl -X POST .../god/setup/database -H "X-God-Token: ..." -d @schema.json
|
||||||
|
|
||||||
|
# 3. Grant permissions
|
||||||
|
curl -X POST .../god/permissions/grant-all -H "X-God-Token: ..."
|
||||||
|
|
||||||
|
# Done! ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fix Permission Issues
|
||||||
|
```bash
|
||||||
|
curl -X POST .../god/permissions/grant-all -H "X-God-Token: ..."
|
||||||
|
```
|
||||||
|
|
||||||
|
### Query Database Directly
|
||||||
|
```bash
|
||||||
|
curl -X POST .../god/sql/execute \
|
||||||
|
-H "X-God-Token: ..." \
|
||||||
|
-d '{"sql": "SELECT COUNT(*) FROM generated_articles WHERE status = '\''published'\'';"}''
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ Security Notes
|
||||||
|
|
||||||
|
### What God Mode Can Do:
|
||||||
|
- ✅ Execute any SQL query
|
||||||
|
- ✅ Modify any Directus collection
|
||||||
|
- ✅ Grant/revoke permissions
|
||||||
|
- ✅ Access system collections
|
||||||
|
- ✅ Bypass all authentication
|
||||||
|
- ✅ Create/delete tables
|
||||||
|
|
||||||
|
### Security Measures:
|
||||||
|
- ✅ 128-character random token
|
||||||
|
- ✅ Token not in git repository
|
||||||
|
- ✅ Only in Coolify environment variables
|
||||||
|
- ✅ Logs all access attempts
|
||||||
|
- ✅ Requires exact token match
|
||||||
|
|
||||||
|
### If Token is Compromised:
|
||||||
|
1. Generate new token:
|
||||||
|
```bash
|
||||||
|
node -e "const crypto = require('crypto'); console.log(crypto.randomBytes(64).toString('base64url'));"
|
||||||
|
```
|
||||||
|
2. Update in Coolify environment variables
|
||||||
|
3. Redeploy Directus service
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
directus-extensions/
|
||||||
|
├── endpoints/
|
||||||
|
│ └── god/
|
||||||
|
│ ├── index.js # God Mode API implementation
|
||||||
|
│ └── package.json # Extension metadata
|
||||||
|
└── hooks/
|
||||||
|
└── auto-permissions/
|
||||||
|
├── index.js # Auto-grant permissions on startup
|
||||||
|
└── package.json # Hook metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Verification
|
||||||
|
|
||||||
|
After deployment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test god mode access
|
||||||
|
curl -X GET https://spark.jumpstartscaling.com/god/status \
|
||||||
|
-H "X-God-Token: jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA"
|
||||||
|
|
||||||
|
# Should return success: true
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**God Mode is your backdoor into everything. Use responsibly!** 🔥
|
||||||
273
directus-extensions/endpoints/god/index.js
Normal file
273
directus-extensions/endpoints/god/index.js
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
/**
|
||||||
|
* Spark Platform - God Mode API Extension
|
||||||
|
*
|
||||||
|
* Provides unrestricted access to Directus and database operations
|
||||||
|
* Bypasses all authentication and permission checks
|
||||||
|
*
|
||||||
|
* SECURITY: Access via X-God-Token header only
|
||||||
|
* DO NOT commit token to git!
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default (router, { services, database, env, logger }) => {
|
||||||
|
const { ItemsService, UsersService, PermissionsService, CollectionsService } = services;
|
||||||
|
|
||||||
|
// God mode authentication middleware
|
||||||
|
const godAuth = (req, res, next) => {
|
||||||
|
const token = req.headers['x-god-token'];
|
||||||
|
|
||||||
|
if (!token || token !== env.GOD_MODE_TOKEN) {
|
||||||
|
logger.warn('Unauthorized god mode access attempt');
|
||||||
|
return res.status(403).json({ error: 'Forbidden' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bypass all Directus auth - set as super admin
|
||||||
|
req.accountability = {
|
||||||
|
user: 'god-mode',
|
||||||
|
role: null,
|
||||||
|
admin: true,
|
||||||
|
app: true,
|
||||||
|
ip: req.ip
|
||||||
|
};
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply god auth to all routes
|
||||||
|
router.use(godAuth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /god/setup/database
|
||||||
|
* Initialize database with complete schema
|
||||||
|
*/
|
||||||
|
router.post('/setup/database', async (req, res) => {
|
||||||
|
try {
|
||||||
|
logger.info('God mode: Running database setup');
|
||||||
|
|
||||||
|
// Execute complete_schema.sql
|
||||||
|
const schemaSQL = req.body.sql || '';
|
||||||
|
|
||||||
|
if (schemaSQL) {
|
||||||
|
await database.raw(schemaSQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all custom tables
|
||||||
|
const tables = await database('pg_tables')
|
||||||
|
.where('schemaname', 'public')
|
||||||
|
.whereNotLike('tablename', 'directus_%')
|
||||||
|
.whereNotLike('tablename', 'spatial_%')
|
||||||
|
.select('tablename');
|
||||||
|
|
||||||
|
logger.info(`Created ${tables.length} custom tables`);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
tables_created: tables.length,
|
||||||
|
tables: tables.map(t => t.tablename)
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('God mode database setup failed:', error);
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /god/permissions/grant-all
|
||||||
|
* Grant all permissions to admin policy
|
||||||
|
*/
|
||||||
|
router.post('/permissions/grant-all', async (req, res) => {
|
||||||
|
try {
|
||||||
|
logger.info('God mode: Granting all permissions');
|
||||||
|
|
||||||
|
// Get or create admin policy
|
||||||
|
let [policy] = await database('directus_policies')
|
||||||
|
.where('name', 'Administrator')
|
||||||
|
.select('id');
|
||||||
|
|
||||||
|
if (!policy) {
|
||||||
|
[policy] = await database('directus_policies')
|
||||||
|
.insert({
|
||||||
|
name: 'Administrator',
|
||||||
|
icon: 'verified_user',
|
||||||
|
description: 'Full access to everything',
|
||||||
|
admin_access: true,
|
||||||
|
app_access: true
|
||||||
|
})
|
||||||
|
.returning('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all collections
|
||||||
|
const collections = await database('directus_collections')
|
||||||
|
.whereNotLike('collection', 'directus_%')
|
||||||
|
.select('collection');
|
||||||
|
|
||||||
|
// Delete existing permissions for this policy
|
||||||
|
await database('directus_permissions')
|
||||||
|
.where('policy', policy.id)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Grant all permissions
|
||||||
|
const permissions = [];
|
||||||
|
const actions = ['create', 'read', 'update', 'delete'];
|
||||||
|
|
||||||
|
for (const { collection } of collections) {
|
||||||
|
for (const action of actions) {
|
||||||
|
permissions.push({
|
||||||
|
policy: policy.id,
|
||||||
|
collection,
|
||||||
|
action,
|
||||||
|
permissions: null, // null = all items
|
||||||
|
validation: null,
|
||||||
|
presets: null,
|
||||||
|
fields: ['*']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await database('directus_permissions').insert(permissions);
|
||||||
|
|
||||||
|
logger.info(`Granted ${permissions.length} permissions`);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
permissions_granted: permissions.length,
|
||||||
|
collections: collections.length
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('God mode permission grant failed:', error);
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /god/sql/execute
|
||||||
|
* Execute arbitrary SQL (DANGEROUS!)
|
||||||
|
*/
|
||||||
|
router.post('/sql/execute', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { sql, params } = req.body;
|
||||||
|
|
||||||
|
logger.warn('God mode: Executing raw SQL', { sql });
|
||||||
|
|
||||||
|
const result = params
|
||||||
|
? await database.raw(sql, params)
|
||||||
|
: await database.raw(sql);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
rows: result.rows || result,
|
||||||
|
rowCount: result.rowCount || (result.rows ? result.rows.length : 0)
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('God mode SQL execution failed:', error);
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /god/collections/all
|
||||||
|
* Get all collections including system
|
||||||
|
*/
|
||||||
|
router.get('/collections/all', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const collections = await database('directus_collections')
|
||||||
|
.select('*')
|
||||||
|
.orderBy('collection');
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
count: collections.length,
|
||||||
|
data: collections
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /god/user/make-admin
|
||||||
|
* Grant admin access to any user
|
||||||
|
*/
|
||||||
|
router.post('/user/make-admin', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { userId, email } = req.body;
|
||||||
|
|
||||||
|
let user;
|
||||||
|
if (userId) {
|
||||||
|
user = await database('directus_users')
|
||||||
|
.where('id', userId)
|
||||||
|
.first();
|
||||||
|
} else if (email) {
|
||||||
|
user = await database('directus_users')
|
||||||
|
.where('email', email)
|
||||||
|
.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(404).json({ error: 'User not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get admin role/policy
|
||||||
|
const [adminRole] = await database('directus_roles')
|
||||||
|
.where('name', 'Administrator')
|
||||||
|
.select('id');
|
||||||
|
|
||||||
|
if (adminRole) {
|
||||||
|
await database('directus_users')
|
||||||
|
.where('id', user.id)
|
||||||
|
.update({ role: adminRole.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('God mode: Made user admin', { userId: user.id, email: user.email });
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
user: {
|
||||||
|
id: user.id,
|
||||||
|
email: user.email,
|
||||||
|
role: adminRole?.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /god/status
|
||||||
|
* Check god mode status and permissions
|
||||||
|
*/
|
||||||
|
router.get('/status', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const tablesCount = await database('pg_tables')
|
||||||
|
.where('schemaname', 'public')
|
||||||
|
.whereNotLike('tablename', 'directus_%')
|
||||||
|
.whereNotLike('tablename', 'spatial_%')
|
||||||
|
.count('* as count')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
const collectionsCount = await database('directus_collections')
|
||||||
|
.whereNotLike('collection', 'directus_%')
|
||||||
|
.count('* as count')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
const permissionsCount = await database('directus_permissions')
|
||||||
|
.count('* as count')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
god_mode: true,
|
||||||
|
database: {
|
||||||
|
tables: parseInt(tablesCount.count),
|
||||||
|
collections: parseInt(collectionsCount.count),
|
||||||
|
permissions: parseInt(permissionsCount.count)
|
||||||
|
},
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info('God Mode API Extension loaded - Use X-God-Token header for access');
|
||||||
|
};
|
||||||
10
directus-extensions/endpoints/god/package.json
Normal file
10
directus-extensions/endpoints/god/package.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"id": "god-mode-api",
|
||||||
|
"name": "God Mode API",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Unrestricted admin API for database and Directus management",
|
||||||
|
"type": "endpoint",
|
||||||
|
"path": "god",
|
||||||
|
"source": "./index.js",
|
||||||
|
"host": "^11.0.0"
|
||||||
|
}
|
||||||
77
directus-extensions/hooks/auto-permissions/index.js
Normal file
77
directus-extensions/hooks/auto-permissions/index.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* Auto-Permissions Hook
|
||||||
|
* Automatically grants all permissions to admin policy on startup
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default ({ init }, { services, database, logger }) => {
|
||||||
|
|
||||||
|
// Run after Directus initialization
|
||||||
|
init('app.after', async () => {
|
||||||
|
try {
|
||||||
|
logger.info('Auto-permissions: Granting all permissions to admin policy');
|
||||||
|
|
||||||
|
// Get admin policy
|
||||||
|
const [policy] = await database('directus_policies')
|
||||||
|
.where('name', 'Administrator')
|
||||||
|
.select('id');
|
||||||
|
|
||||||
|
if (!policy) {
|
||||||
|
logger.warn('Auto-permissions: Admin policy not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all custom collections
|
||||||
|
const collections = await database('directus_collections')
|
||||||
|
.whereNotLike('collection', 'directus_%')
|
||||||
|
.select('collection');
|
||||||
|
|
||||||
|
if (collections.length === 0) {
|
||||||
|
logger.info('Auto-permissions: No custom collections found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if permissions already exist
|
||||||
|
const existing = await database('directus_permissions')
|
||||||
|
.where('policy', policy.id)
|
||||||
|
.count('* as count')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
const expectedCount = collections.length * 4; // 4 actions per collection
|
||||||
|
|
||||||
|
if (parseInt(existing.count) >= expectedCount) {
|
||||||
|
logger.info('Auto-permissions: Permissions already granted');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete old permissions
|
||||||
|
await database('directus_permissions')
|
||||||
|
.where('policy', policy.id)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Grant new permissions
|
||||||
|
const permissions = [];
|
||||||
|
const actions = ['create', 'read', 'update', 'delete'];
|
||||||
|
|
||||||
|
for (const { collection } of collections) {
|
||||||
|
for (const action of actions) {
|
||||||
|
permissions.push({
|
||||||
|
policy: policy.id,
|
||||||
|
collection,
|
||||||
|
action,
|
||||||
|
permissions: null,
|
||||||
|
validation: null,
|
||||||
|
presets: null,
|
||||||
|
fields: ['*']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await database('directus_permissions').insert(permissions);
|
||||||
|
|
||||||
|
logger.info(`Auto-permissions: Granted ${permissions.length} permissions for ${collections.length} collections`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Auto-permissions failed:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
9
directus-extensions/hooks/auto-permissions/package.json
Normal file
9
directus-extensions/hooks/auto-permissions/package.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"id": "auto-permissions",
|
||||||
|
"name": "Auto Permissions",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Automatically grant permissions to admin policy",
|
||||||
|
"type": "hook",
|
||||||
|
"source": "./index.js",
|
||||||
|
"host": "^11.0.0"
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- 'directus-uploads:/directus/uploads'
|
- 'directus-uploads:/directus/uploads'
|
||||||
- 'directus-extensions:/directus/extensions'
|
- 'directus-extensions:/directus/extensions'
|
||||||
|
- ./directus-extensions:/directus/extensions
|
||||||
environment:
|
environment:
|
||||||
KEY: 9i2t1bMAIITWCZ+WrzUEk4EuNmIu3kfyB9Peysk7f/jnUZ7hzQ5HoNC8yOT5vi/rwTmDWX3a1+4j2llgAE2VvA==
|
KEY: 9i2t1bMAIITWCZ+WrzUEk4EuNmIu3kfyB9Peysk7f/jnUZ7hzQ5HoNC8yOT5vi/rwTmDWX3a1+4j2llgAE2VvA==
|
||||||
SECRET: Mr4YSrOAfwToxCDFOPwUa8qtxd7BXOvmqXalk3ReikpfcIwf08Kp+hlNjGcr1NtcLIcIZoraaULnMefD5IukGA==
|
SECRET: Mr4YSrOAfwToxCDFOPwUa8qtxd7BXOvmqXalk3ReikpfcIwf08Kp+hlNjGcr1NtcLIcIZoraaULnMefD5IukGA==
|
||||||
@@ -53,6 +54,13 @@ services:
|
|||||||
CORS_ENABLED: 'true'
|
CORS_ENABLED: 'true'
|
||||||
PUBLIC_URL: 'https://spark.jumpstartscaling.com'
|
PUBLIC_URL: 'https://spark.jumpstartscaling.com'
|
||||||
|
|
||||||
|
# Extensions
|
||||||
|
EXTENSIONS_AUTO_RELOAD: 'true'
|
||||||
|
EXTENSIONS_PATH: '/directus/extensions'
|
||||||
|
|
||||||
|
# God Mode API Token (SET IN COOLIFY SECRETS - DO NOT COMMIT!)
|
||||||
|
GOD_MODE_TOKEN: ${GOD_MODE_TOKEN}
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
postgresql:
|
postgresql:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user