Activate Institute Portal
The activation functionality requires a Unix-based operating system (e.g., Ubuntu, macOS). It is not supported on Windows as it depends on Nginx site management via /etc/nginx/ and DNS configuration via /etc/hosts.
Overview
The Activate Institute functionality provisions the infrastructure needed to make an institute's fee portal accessible to students and parents.
This feature automates the creation of DNS records and web server configurations, transforming an inactive institute into a live, operational fee collection portal.
Key Features:
- Automatic subdomain creation based on institute's hosted page prefix
- Dynamic Nginx virtual host configuration
- Automated DNS record management (CNAME creation)
- Status-based UI controls (activate/deactivate toggle)
- Audit trail of all activation/deactivation events
- Role-based access control (Institute Admin only)
- Confirmation dialogs to prevent accidental changes
- Support for both production (Route53) and local (hosts file) DNS management
Roles Involved
- Institute Admin: Can activate and deactivate institute portals
You can refer to the User Roles & Responsibilities in the Product Overview for more details on this role.
What "Activating" an Institute Means
Activation is a multi-step infrastructure provisioning process that:
- Creates a Subdomain: Constructs a unique domain name for the institute (e.g.,
delhi.dpsschools.edu.in) - Configures Web Server: Creates and enables an Nginx virtual host to serve the portal
- Sets Up DNS: Creates a DNS CNAME record pointing to the portal infrastructure
- Updates Status: Changes the institute's status from "InActive" to "Active"
- Creates Audit Trail: Logs the activation event with timestamp and live domain link
Important: This is not just a database status change - it actually provisions real infrastructure that makes the portal accessible on the internet.
Data Models Involved
This section describes the data model used for institute activation.
Institute Model
What it represents: An educational institution that uses the fee portal platform to collect payments from students.
Activation-Related Fields
| Field | Required? | Description | Example |
|---|---|---|---|
| Status | Yes (Default: "InActive") | Current activation state of the portal | "InActive", "Active" |
| Hosted Page Prefix | Yes | Unique subdomain prefix for the institute's portal | "delhi", "mumbai" |
| Support Email | Yes | Email address for portal support inquiries | "support@dpsschools.edu.in" |
| Support Mobile | Yes | Phone number for portal support | "9123456789" |
| Payment Gateway Merchant ID | Yes | Merchant identifier for payment processing | "MERCHANT_001" |
| Payment Gateway Access Key | Yes | API key for payment gateway | "ak_live_xxxxx" |
| Payment Gateway Access Secret | Yes | Secret key for payment gateway | "as_live_xxxxx" |
Status Field Details
The status field is the state machine that controls the activation lifecycle:
Possible Values:
"InActive"(default): Institute portal is not accessible, infrastructure not provisioned"Active": Institute portal is live and accessible to students/parents
State Transitions:
New Institute Creation → Status: "InActive"
↓
Institute Admin clicks "Activate"
↓
Infrastructure Provisioning
↓
Status: "Active"
↓
Institute Admin clicks "Deactivate"
↓
Infrastructure Removal
↓
Status: "InActive"
Domain Name Construction
The portal domain is automatically constructed as:
{hostedPagePrefix}.{EDU_BASE_DOMAIN}
Examples:
- Hosted Page Prefix:
"delhi"→ Domain:delhi.dpsschools.edu.in - Hosted Page Prefix:
"navi-mumbai"→ Domain:navi-mumbai.dpsschools.edu.in
Important: The hostedPagePrefix must be:
- Unique across all institutes in the system
- Valid for use in DNS (alphanumeric and hyphens only)
- Set before activation (cannot activate without it)
Prerequisites for Activation
Before activating an institute, ensure the following conditions are met:
Institute Configuration
- Institute Name is set and unique
- Hosted Page Prefix is set and unique (will be used for subdomain)
- Support Email is configured
- Support Mobile is configured
- Payment Gateway Merchant ID is configured
- Payment Gateway Access Key is configured
- Payment Gateway Access Secret is configured
- Status is currently "InActive"
Environment Configuration
The following environment variables must be configured on the server:
| Variable | Required | Description | Example |
|---|---|---|---|
| EDU_BASE_DOMAIN | No (has default) | Base domain for all institute portals | "dpsschools.edu.in" |
| PORTAL_CNAME_DOMAIN | Yes | CNAME target for DNS records | "portal.dpsschools.edu.in" |
| DNS_PROVIDER | No (defaults to "hosts") | DNS management provider | "route53" or "hosts" |
| EDU_FRONTEND_UPSTREAM | No (has default) | Upstream server for Nginx proxy | "http://127.0.0.1:3002" |
For Live/Production Domains:
- AWS credentials configured (IAM role or environment variables)
- Route53 hosted zone ID available
- Route53 hosted zone matches
EDU_BASE_DOMAIN
For Local Testing (No Real Domain):
- Application has sudo/root privileges to modify
/etc/hosts -
/etc/hostsfile is writable
Server Requirements
- Nginx installed and running
- Application has permissions to create files in
/etc/nginx/sites-available/ - Application has permissions to create symlinks in
/etc/nginx/sites-enabled/ - Application can reload Nginx (
systemctl reload nginx) - Port 80 is not blocked by firewall
- Port 443 is not blocked by firewall (only required if HTTPS/SSL is configured; the default activation creates HTTP-only virtual hosts)
Activation Workflow
This section provides a step-by-step guide for Institute Admins to activate an institute portal.
Phase 1: Pre-Activation Verification
Step 1: Navigate to Institute Record
- Login to the Fees Portal admin interface with Institute Admin credentials
- Navigate to "Fees Portal" module in the sidebar
- Select "Institutes" from the menu
- Find the institute you want to activate
- Click on the institute to open the detail/edit view
Step 2: Verify Institute Configuration
Check that all required fields are configured:
Basic Information:
- Institute Name: Filled and descriptive
- Customer User ID: Set (usually auto-generated)
- Status: Currently showing "InActive"
Portal Configuration:
- Hosted Page Prefix: Filled with a unique, valid identifier
- Good examples:
"delhi","mumbai","greenwood-school" - Bad examples:
"delhi.branch"(no dots),"Delhi Branch"(no spaces),"school@1"(no special chars)
- Good examples:
Payment Gateway:
- Payment Gateway Merchant ID: Configured
- Payment Gateway Access Key: Configured
- Payment Gateway Access Secret: Configured
Support Contact:
- Support Email: Valid email address
- Support Mobile: Valid phone number
Step 3: Note the Domain Name
The portal will be accessible at:
{hostedPagePrefix}-{EDU_BASE_DOMAIN}
Write down this domain - you'll verify it's accessible after activation.
Example: If hostedPagePrefix is "delhi" and EDU_BASE_DOMAIN is "dpsschools.edu.in", the portal will be at delhi.dpsschools.edu.in
Phase 2: Activate the Institute
Step 4: Locate the Activate Button
In the institute edit view, look for the "Activate Institute" button:
- Located in the header area of the form
- Icon: Play button (▶)
- Label: "Activate Institute"
- Only visible when status is "InActive"
Step 5: Initiate Activation
- Click the "Activate Institute" button
- A confirmation dialog will appear
Confirmation Dialog:
- Header: "Activate institute ?"
- Message: "Clicking Ok below will activate and host this institute, are you sure you want to continue?"
- Buttons: "Ok" and "Cancel"
Step 6: Confirm Activation
- Review the confirmation message carefully
- If you're sure, click "Ok"
- If you need to double-check configuration, click "Cancel" and return to Step 2
Step 7: Wait for Provisioning
After clicking "Ok", the system will:
-
Construct Domain Name
- Combines
hostedPagePrefixwith base domain - Example:
delhi.dpsschools.edu.in
- Combines
-
Create Nginx Virtual Host
- Creates configuration file:
/etc/nginx/sites-available/{domain}.conf - Creates symlink:
/etc/nginx/sites-enabled/{domain}.conf - Configuration includes:
- Server listening on port 80
- Proxy to frontend upstream
- Security headers
- Validates configuration:
nginx -t - Reloads Nginx:
systemctl reload nginx
- Creates configuration file:
-
Create DNS Record
- Creates CNAME record pointing to
PORTAL_CNAME_DOMAIN - TTL: 300 seconds (5 minutes)
- Provider-specific:
- Route53: Creates record via AWS API
- Hosts File: Adds entry to
/etc/hosts
- Creates CNAME record pointing to
-
Update Database
- Sets
status = 'Active' - Saves institute record
- Sets
-
Create Audit Entry
- Posts message to Chatter system
- Message includes live domain link
- Timestamp and user recorded
Step 8: Verify Success
On successful activation:
- Success toast message appears: "Institute activated successfully."
- Status field updates to "Active"
- "Activate Institute" button disappears
- "Deactivate Institute" button appears (with stop icon ⊗)
- Form refreshes with updated data
On failure:
- Error toast message appears: "Failed to activate institute."
- Check server logs for details
- Common issues:
- Missing
PORTAL_CNAME_DOMAINenvironment variable - Nginx configuration errors
- DNS provider connection issues
- Insufficient permissions
- Missing
Phase 3: Post-Activation Verification
Step 9: Check Audit Trail
- Scroll down to the Chatter/Activity section on the institute record
- Verify the activation event was logged
- Message should read:
### Institute Activated
Institute is now live on domain: <link to domain> - Click the link to verify it's correct
Step 10: Verify DNS Configuration
For Production (Route53):
- Wait 5-10 minutes for DNS propagation
- Use a DNS lookup tool:
nslookup {domain}ordig {domain} - Verify the CNAME record points to
PORTAL_CNAME_DOMAIN
For Local Development (Hosts File):
- Open
/etc/hostsfile - Look for an entry like:
127.0.0.1 delhi.dpsschools.edu.in # solidx-dns - Verify the domain is mapped to the correct IP
Step 11: Verify Nginx Configuration
SSH into the server and run:
# Check configuration file exists
ls -la /etc/nginx/sites-available/{domain}.conf
# Check symlink exists
ls -la /etc/nginx/sites-enabled/{domain}.conf
# Verify Nginx configuration is valid
sudo nginx -t
# Check Nginx is running
sudo systemctl status nginx
All commands should succeed without errors.
Step 12: Test Portal Access
- Open a web browser
- Navigate to
http://{domain}(e.g.,http://delhi.dpsschools.edu.in) - The student portal should load
Expected Result:
- Portal loads successfully
- No certificate warnings (if HTTPS is configured)
- Institute branding appears correct
- No error messages visible
If activation fails or the portal is not accessible, see the Troubleshooting Reference section for common issues and solutions.
Deactivating an Institute
If you need to take a portal offline:
Deactivation Process
Step 1: Navigate to Active Institute
- Open the institute record
- Verify status is currently "Active"
- "Deactivate Institute" button should be visible
Step 2: Initiate Deactivation
- Click "Deactivate Institute" button (⊗ icon)
- Confirmation dialog appears:
- Header: "Deactivate institute?"
- Message: "Clicking Ok below will Deactivate this institute, are you sure you want to continue?"
Step 3: Confirm Deactivation
- Click "Ok" to proceed
- System will:
- Remove Nginx virtual host configuration
- Remove DNS CNAME record
- Set status to "InActive"
- Create audit trail entry
Step 4: Verify Deactivation
- Success message: "institute Deactivated successfully."
- Status changes to "InActive"
- "Activate Institute" button reappears
- Portal becomes inaccessible (404 or connection error)
When to Deactivate
Valid Reasons:
- Institute contract ended
- Switching to different platform
- Temporary suspension due to payment issues
- Maintenance or configuration changes needed
- Institute closure
Important: Deactivation only removes infrastructure - it does not delete data. Student records, payment history, and configuration remain in the database and can be reactivated later.
Technical Implementation Details
This section explains how the activation system works under the hood.
Backend Service Implementation
File: solid-api/src/fees-portal/services/institute.service.ts
Activation Method: activateInstitutePortal(id: number)
async activateInstitutePortal(id: number) {
// 1. Retrieve institute record
const institute = await this.findOne(id, {});
// 2. Construct domain name
const baseDoamin = process.env.EDU_BASE_DOMAIN || 'dpsschools.edu.in';
const domainName = `${institute.hostedPagePrefix}-${baseDoamin}`;
// 3. Create Nginx virtual host
await this.nginxVirtualHostProvider.makeVirtualHost(domainName);
// 4. Create DNS CNAME record
const portalCnameDomain = process.env.PORTAL_CNAME_DOMAIN;
if (!portalCnameDomain) {
throw new NotAcceptableException('PORTAL_CNAME_DOMAIN env var not configured');
}
await this.dns.createCnameRecord(domainName, portalCnameDomain, 300);
// 5. Update status
institute.status = 'Active';
await this.repo.save(institute);
// 6. Create audit entry
const postMessageOnActivate: PostChatterMessageDto = {
coModelEntityId: institute.id,
coModelName: 'institute',
messageBody: `
### Institute Activated
Institute is now live on domain: <a href="${domainName}" target="_blank" rel="noopener noreferrer">${domainName}</a>
`
};
await this.chatterMessageService.postMessage(postMessageOnActivate);
return { message: 'Institute activated successfully' };
}
Key Points:
- Domain name constructed from
hostedPagePrefixandEDU_BASE_DOMAIN - Infrastructure provisioning happens before database update
- If any step fails, exception is thrown (status remains "InActive")
- Audit trail created after successful activation
Deactivation Method: deActivateInstitutePortal(id: number)
async deActivateInstitutePortal(id: number) {
const institute = await this.findOne(id, {});
const baseDoamin = process.env.EDU_BASE_DOMAIN || 'dpsschools.edu.in';
const domainName = `${institute.hostedPagePrefix}-${baseDoamin}`;
// 1. Remove Nginx virtual host
await this.nginxVirtualHostProvider.removeVirtualHost(domainName);
// 2. Remove DNS record
const portalCnameDomain = process.env.PORTAL_CNAME_DOMAIN;
if (!portalCnameDomain) {
throw new NotAcceptableException('PORTAL_CNAME_DOMAIN env var not configured');
}
await this.dns.removeCnameRecord(domainName, portalCnameDomain);
// 3. Update status
institute.status = 'InActive';
await this.repo.save(institute);
// 4. Create audit entry
const postMessageOnDeActivate: PostChatterMessageDto = {
coModelEntityId: institute.id,
coModelName: 'institute',
messageBody: '### Institute De Activated'
};
await this.chatterMessageService.postMessage(postMessageOnDeActivate);
return { message: 'Institute de activated successfully' };
}
API Endpoints
File: solid-api/src/fees-portal/controllers/institute.controller.ts
Activation Endpoint:
@ApiBearerAuth("jwt")
@Post('activate/:id')
async activateFeePortal(@Param('id') id: number) {
return this.service.activateInstitutePortal(id);
}
URL: POST /api/institute/activate/{id}
Authentication: Requires JWT Bearer token
Parameters:
id(path parameter): Institute ID to activate
Response (Success):
{
"message": "Institute activated successfully"
}
Response (Error):
{
"statusCode": 406,
"message": "PORTAL_CNAME_DOMAIN env var not configured",
"error": "Not Acceptable"
}
Deactivation Endpoint:
@ApiBearerAuth("jwt")
@Post('deactivate/:id')
async deactivateFeePortal(@Param('id') id: number) {
return this.service.deActivateInstitutePortal(id);
}
URL: POST /api/institute/deactivate/{id}
Authentication: Requires JWT Bearer token
Parameters:
id(path parameter): Institute ID to deactivate
Response: Same structure as activation endpoint
Nginx Virtual Host Provider
File: solid-api/src/fees-portal/services/ubuntu-nginx-virtual-host-provider.ts
Purpose: Manages Nginx server block configurations for institute domains
Method: makeVirtualHost(domainName: string)
What It Does:
- Validates domain name syntax (conservative regex for ASCII hostnames)
- Renders Nginx server block template with:
- Server name:
{domainName} - Listen: Port 80
- Proxy pass:
{EDU_FRONTEND_UPSTREAM} - Proxy headers: Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto
- Server name:
- Writes configuration to
/etc/nginx/sites-available/{domainName}.conf - Creates symlink in
/etc/nginx/sites-enabled/{domainName}.conf - Tests Nginx configuration:
nginx -t - Reloads Nginx:
systemctl reload nginx
Server Block Template:
server {
listen 80;
server_name {domainName};
location / {
proxy_pass {EDU_FRONTEND_UPSTREAM};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Method: removeVirtualHost(domainName: string)
What It Does:
- Removes symlink from
/etc/nginx/sites-enabled/{domainName}.conf - Removes configuration from
/etc/nginx/sites-available/{domainName}.conf - Tests Nginx configuration:
nginx -t - Reloads Nginx:
systemctl reload nginx
Error Handling:
- Throws exception if domain name is invalid
- Throws exception if Nginx test fails
- Throws exception if Nginx reload fails
- All operations are atomic (rollback on failure)
DNS Management - Route53 Provider
File: solid-api/src/fees-portal/services/route53-website-dns.manager.ts
Purpose: Manages DNS CNAME records using AWS Route53
Method: createCnameRecord(name: string, value: string, ttl: number)
What It Does:
- Constructs Route53 API request:
- Action: UPSERT (create or update)
- Type: CNAME
- Name:
{name}(e.g.,delhi.dpsschools.edu.in) - Value:
{value}(PORTAL_CNAME_DOMAIN) - TTL:
{ttl}seconds (default: 300)
- Calls Route53 API:
changeResourceRecordSets - Waits for change to propagate (async)
Prerequisites:
- AWS credentials configured (IAM role or environment variables)
- Route53 hosted zone ID provided to constructor
- Hosted zone must match
EDU_BASE_DOMAIN
Method: removeCnameRecord(name: string, value: string)
What It Does:
- Constructs Route53 API request with DELETE action
- Calls Route53 API to remove the CNAME record
- Waits for change to propagate
DNS Management - Hosts File Provider
File: solid-api/src/fees-portal/services/hosts-file-website-dns.manager.ts
Purpose: Manages DNS entries in local /etc/hosts file for development
Method: createCnameRecord(name: string, value: string, ttl: number)
What It Does:
- Reads
/etc/hostsfile - Checks if entry already exists
- If not, appends entry:
127.0.0.1 {name} # solidx-dns - Writes updated content back to
/etc/hosts
Method: removeCnameRecord(name: string, value: string)
What It Does:
- Reads
/etc/hostsfile - Filters out lines containing
{name} # solidx-dns - Writes filtered content back to
/etc/hosts
Prerequisites:
- Application runs with sudo/root privileges
/etc/hostsfile is writable- File system allows file modification
Marker System:
- All entries tagged with
# solidx-dnscomment - Allows safe removal without affecting other entries
- Prevents duplicate entries
DNS Provider Selection
File: solid-api/src/fees-portal/fees-portal.module.ts
The DNS provider is selected via a factory provider that should be added to the providers array in the module:
{
provide: WEBSITE_DNS_MANAGER,
useFactory: (): IWebsiteDnsManager => {
// 'route53' | 'hosts'
const which = (process.env.DNS_PROVIDER ?? 'hosts').toLowerCase();
if (which === 'route53') {
const hostedZoneId = process.env.ROUTE53_HOSTED_ZONE_ID;
if (!hostedZoneId) {
throw new Error('ROUTE53_HOSTED_ZONE_ID is required when DNS_PROVIDER=route53');
}
return new Route53WebsiteDnsManager(hostedZoneId);
}
return new HostsFileWebsiteDnsManager();
},
}
Logic:
- Reads
DNS_PROVIDERfromprocess.env(defaults to"hosts"if not set) - If
"route53": Validates thatROUTE53_HOSTED_ZONE_IDis set, then usesRoute53WebsiteDnsManager - If
"hosts"or not set: UsesHostsFileWebsiteDnsManager - Case-insensitive comparison
- Throws an error at startup if Route53 is selected but the hosted zone ID is missing
Recommended Setup:
- Production:
DNS_PROVIDER=route53(ensureROUTE53_HOSTED_ZONE_IDis also set) - Local Development:
DNS_PROVIDER=hosts(or omit)
UI Components
Activate Button Component:
File: solid-ui/app/admin/extensions/headerButtons/activate-portal.tsx
Component: InstituteActivateById
export default function InstituteActivateById({ formData, refetch, onClose }: Props) {
const session = useSession();
const [loading, setLoading] = useState(false);
const handleActivate = async () => {
setLoading(true);
try {
// Call activation API
const response = await fetch(`/api/institute/activate/${formData.id}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${session.data.user.accessToken}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) throw new Error('Activation failed');
// Update status in UI
await fetch(`/api/institute/${formData.id}`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${session.data.user.accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: 'Active' }),
});
toast.success('Institute activated successfully.');
onClose();
refetch();
} catch (error) {
toast.error('Failed to activate institute.');
} finally {
setLoading(false);
}
};
return (
<ConfirmDialog
header="Activate institute ?"
message="Clicking Ok below will activate and host this institute, are you sure you want to continue?"
onConfirm={handleActivate}
onCancel={onClose}
loading={loading}
/>
);
}
Key Features:
- Confirmation dialog prevents accidental activation
- Shows loading state during provisioning
- Success/error toast notifications
- Automatic form refresh after activation
- JWT authentication with session token
Deactivate Button Component:
File: solid-ui/app/admin/extensions/headerButtons/deActivate-portal.tsx
Component: InstituteDeactivateById
Similar structure to activate button, but:
- Different confirmation message
- Calls
/api/institute/deactivate/{id}endpoint - Sets status to "InActive" on success
Conditional UI - Edit Handler
File: solid-ui/app/admin/extensions/instituteEditHandler.ts
const instituteEditHandler = async (event: any) => {
const { viewMetadata, formData, fieldsMetadata } = event;
const { status } = formData || {};
const layoutManager = new SolidViewLayoutManager(viewMetadata.layout);
if(status === "InActive"){
layoutManager.updateNodeAttributes("InstituteActivateById", { visible: true });
layoutManager.updateNodeAttributes("InstituteDeactivateById", { visible: false });
} else if(status === "Active"){
layoutManager.updateNodeAttributes("InstituteActivateById", { visible: false });
layoutManager.updateNodeAttributes("InstituteDeactivateById", { visible: true });
}
return {
layoutChanged: true,
dataChanged: false,
newLayout: layoutManager.getLayout()
};
};
export default instituteEditHandler;
Purpose: Controls button visibility based on current status
Logic:
- When status = "InActive": Show "Activate" button, hide "Deactivate" button
- When status = "Active": Show "Deactivate" button, hide "Activate" button
How It Works:
- Called when institute form loads
- Receives current form data (including status)
- Uses
SolidViewLayoutManagerto update layout - Returns modified layout to UI framework
Benefits:
- Prevents invalid actions (can't activate what's already active)
- Clear visual indication of current state
- Follows state machine pattern
UI Configuration
Button Configuration in Form View:
Both buttons are configured as header buttons in the institute form view layout:
Activate Institute Button:
{
"attrs": {
"label": "Activate Institute",
"icon": "pi pi-caret-right",
"action": "InstituteActivateById",
"customComponentIsSystem": true,
"actionInContextMenu": true,
"openInPopup": true,
"visible": false
}
}
Deactivate Institute Button:
{
"attrs": {
"label": "Deactivate Institute",
"icon": "pi pi-circle-off",
"action": "InstituteDeactivateById",
"customComponentIsSystem": true,
"actionInContextMenu": true,
"openInPopup": true,
"visible": false
}
}
Configuration Properties:
action: References the React component namecustomComponentIsSystem: Indicates this is a custom component (not auto-generated)actionInContextMenu: Shows button in context menu (right-click/three-dot menu)openInPopup: Opens in a modal dialogvisible: Initially hidden (controlled by edit handler)
Security Considerations
Authentication & Authorization
Endpoint Security:
- All activation/deactivation endpoints require JWT authentication
- Bearer token must be included in request headers
Role-Based Access Control:
- Only Institute Admin role can activate/deactivate institutes
- Role check is enforced by assigning the necessary permissions to the activation / deactivation endpoints for the Institute Admin role.
To control access to the activation and deactivation endpoints using SolidX RBAC, you would typically follow these steps:
- Create the endpoints in your controller (as shown in the code snippets) i.e InstituteController with appropriate route and method decorators.
- Run
npx @solidxai/solidctl build && npx @solidxai/solidctl seedfrom the project root directory. This will automatically create permissions for the new endpoints based on the controller and method names. - Go to the SolidX Admin UI and navigate to the Roles section.
- Edit the Institute Admin role and assign the newly created permissions for the activation and deactivation endpoints to this role.
- Save the role configuration. After these steps, only users with the Institute Admin role will be able to access the activation and deactivation endpoints, ensuring that only authorized personnel can perform these actions.
Audit & Compliance
Audit Trail:
- All activation/deactivation events logged to Chatter
- Timestamp, user, and action recorded
- Immutable audit log
Rollback Capability:
- Deactivation removes infrastructure but preserves data
- Can reactivate institute without data loss
- Configuration history maintained
Environment Configuration Reference
This section details all environment variables used by the activation system.
Required Environment Variables
PORTAL_CNAME_DOMAIN
- Description: Target domain for CNAME records
- Required: Yes
- Example:
"portal.dpsschools.edu.in" - Used By: DNS record creation
- Impact if Missing: Activation fails with error
EDU_BASE_DOMAIN
- Description: Base domain for institute subdomains
- Required: No
- Default:
"dpsschools.edu.in" - Example:
"edu.yourcompany.com" - Used By: Domain name construction
EDU_FRONTEND_UPSTREAM
- Description: Upstream server for Nginx proxy i.e (the school portal frontend application)
- Required: No
- Default:
"http://127.0.0.1:3002" - Example:
"http://localhost:3000" - Used By: Nginx virtual host configuration
Optional Environment Variables
DNS_PROVIDER
- Description: DNS management provider
- Required: No
- Default:
"hosts" - Allowed Values:
"route53","hosts" - Example:
"route53" - Used By: DNS provider selection
Route53-Specific Configuration
AWS Credentials:
- Can be provided via IAM role (recommended) or environment variables
- Required environment variables:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_REGION(e.g.,"us-east-1")
ROUTE53_HOSTED_ZONE_ID
- Provided programmatically to Route53WebsiteDnsManager constructor
- Must match the hosted zone for
EDU_BASE_DOMAIN - Example:
"Z1234567890ABC"
Example .env Configuration
Production Setup:
# Base configuration
EDU_BASE_DOMAIN=dpsschools.edu.in
PORTAL_CNAME_DOMAIN=portal.dpsschools.edu.in
DNS_PROVIDER=route53
EDU_FRONTEND_UPSTREAM=http://127.0.0.1:3002
# AWS credentials (or use IAM role)
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=us-east-1
ROUTE53_HOSTED_ZONE_ID=Z1234567890ABC
Local Development Setup:
# Base configuration
EDU_BASE_DOMAIN=edu.local
PORTAL_CNAME_DOMAIN=portal.local
DNS_PROVIDER=hosts
EDU_FRONTEND_UPSTREAM=http://localhost:3000
Troubleshooting Reference
Common Issues and Solutions
Issue: Activation button not visible
- Cause: Status is already "Active" or edit handler not working
- Solution:
- Check current status value
- Verify edit handler is configured in form view
- Check browser console for JavaScript errors
Issue: "PORTAL_CNAME_DOMAIN env var not configured" error
- Cause: Missing required environment variable
- Solution:
- Add
PORTAL_CNAME_DOMAINto.envfile - Restart API server
- Verify value with
echo $PORTAL_CNAME_DOMAIN
- Add
Issue: Nginx reload fails
- Cause: Syntax error in configuration or permission issue
- Solution:
- Run
sudo nginx -tto check syntax - Review generated configuration file
- Check Nginx error logs:
sudo journalctl -u nginx - Verify application has permission to reload Nginx
- Run
Issue: DNS record not created (Route53)
- Cause: Invalid AWS credentials or wrong hosted zone
- Solution:
- Verify AWS credentials are valid
- Check hosted zone ID matches
EDU_BASE_DOMAIN - Confirm IAM permissions include
route53:ChangeResourceRecordSets - Check AWS service status
Issue: Portal returns 502 Bad Gateway
- Cause: Frontend upstream not responding
- Solution:
- Verify frontend service is running
- Check
EDU_FRONTEND_UPSTREAMis correct - Test upstream directly:
curl http://127.0.0.1:3002 - Check frontend logs for errors
Issue: Portal loads but shows wrong content
- Cause: Frontend routing not matching domain to institute
- Solution:
- Verify
hostedPagePrefixis unique - Check frontend routing logic
- Clear browser cache
- Test in incognito/private window
- Verify
Issue: Activation succeeds but portal still inaccessible
- Cause: DNS propagation delay or browser cache
- Solution:
- Wait 10-15 minutes for DNS propagation
- Flush local DNS cache
- Test from different network/location
- Use
nslookupordigto verify DNS
Issue: Cannot deactivate institute
- Cause: Permission issue or configuration locked
- Solution:
- Verify you're logged in as Institute Admin
- Check if there are active payment collections
- Review server logs for specific error
- Try deactivating from API directly (debugging)
Diagnostic Commands
Check DNS Resolution:
# Using nslookup
nslookup delhi.dpsschools.edu.in
# Using dig
dig delhi.dpsschools.edu.in
# Check CNAME specifically
dig CNAME delhi.dpsschools.edu.in
Check Nginx Configuration:
# Test configuration syntax
sudo nginx -t
# View configuration
cat /etc/nginx/sites-available/delhi.dpsschools.edu.in.conf
# Check if symlink exists
ls -la /etc/nginx/sites-enabled/ | grep delhi
# Reload Nginx
sudo systemctl reload nginx
# Check Nginx status
sudo systemctl status nginx
Check Application Logs:
# PM2 logs
pm2 logs solid-api --lines 100
# Direct log files
tail -f /var/log/solid-api/error.log
tail -f /var/log/solid-api/combined.log
Check Hosts File (Local):
# View hosts file
cat /etc/hosts
# Search for specific domain
cat /etc/hosts | grep delhi
# Check file permissions
ls -la /etc/hosts
Test Portal Access:
# Simple GET request
curl -I http://delhi.dpsschools.edu.in
# Full response
curl http://delhi.dpsschools.edu.in
# With verbose output
curl -v http://delhi.dpsschools.edu.in
# Follow redirects
curl -L http://delhi.dpsschools.edu.in
Success Criteria
You've successfully activated an institute when:
- Status field shows "Active" in database and UI
- "Activate Institute" button is hidden
- "Deactivate Institute" button is visible
- Nginx configuration file exists in sites-available
- Symlink exists in sites-enabled
- DNS CNAME record created and resolving
- Portal accessible at
http://{hostedPagePrefix}-{EDU_BASE_DOMAIN} - Portal loads without errors
- Institute branding appears correctly
- Student portal login page accessible
- Audit trail entry created in Chatter
- Institute Admin notified of activation
This completes the Activate Institute guide. For questions or issues not covered here, contact the platform development team.