final round of updates. Still need to stabalize first load for the map, having issues for sure; longer load time

This commit is contained in:
admin 2025-07-06 00:43:10 -06:00
parent f4eefa1cae
commit 1fc8b52840
61 changed files with 4767 additions and 758 deletions

View File

@ -66,7 +66,8 @@
<button class="btn btn-secondary btn-sm" id="close-edit-footer-btn">✕ Close</button>
</div>
<form id="edit-location-form">
<input type="hidden" id="edit-location-id" name="id">
<!-- Hidden ID field - don't include in form data -->
<input type="hidden" id="edit-location-id" data-exclude="true">
<div class="form-row">
<div class="form-group">
<label for="edit-first-name">First Name</label>
@ -110,7 +111,7 @@
<div style="display: flex; gap: 10px;">
<input type="text" id="edit-location-address" name="Address" style="flex: 1;">
<button type="button" class="btn btn-secondary btn-sm" id="lookup-address-edit-btn">
🔍 Lookup
📍 Lookup Address
</button>
</div>
</div>
@ -221,7 +222,7 @@
<input type="text" id="location-address" name="Address"
placeholder="Enter address" style="flex: 1;">
<button type="button" class="btn btn-secondary btn-sm" id="lookup-address-add-btn">
🔍 Lookup
📍 Lookup Address
</button>
</div>
</div>

View File

@ -228,9 +228,13 @@ function createLocationMarker(location) {
const popupContent = createPopupContent(location);
marker.bindPopup(popupContent);
// Add click handler for editing
// Add click handler for editing - Store location data on marker
marker._locationData = location;
marker.on('click', () => {
if (currentUser) {
// Debug: Log the location object to see its structure
console.log('Location clicked:', location);
console.log('Available fields:', Object.keys(location));
setTimeout(() => openEditForm(location), 100);
}
});
@ -240,6 +244,9 @@ function createLocationMarker(location) {
// Create popup content
function createPopupContent(location) {
// Try to find the ID field
const locationId = location.Id || location.id || location.ID || location._id;
const name = [location['First Name'], location['Last Name']]
.filter(Boolean).join(' ') || 'Unknown';
const address = location.Address || 'No address';
@ -254,7 +261,7 @@ function createPopupContent(location) {
${location.Sign ? '<p>🏁 Has campaign sign</p>' : ''}
${location.Notes ? `<p><strong>Notes:</strong> ${escapeHtml(location.Notes)}</p>` : ''}
<div class="popup-meta">
<p>ID: ${location.Id}</p>
<p>ID: ${locationId || 'Unknown'}</p>
</div>
</div>
`;
@ -508,7 +515,11 @@ async function handleAddLocation(e) {
// Convert form data to object
for (let [key, value] of formData.entries()) {
if (value.trim() !== '') {
// Map form field names to NocoDB column names
if (key === 'latitude') data.latitude = value.trim();
else if (key === 'longitude') data.longitude = value.trim();
else if (key === 'Geo-Location') data['Geo-Location'] = value.trim();
else if (value.trim() !== '') {
data[key] = value.trim();
}
}
@ -549,8 +560,29 @@ async function handleAddLocation(e) {
function openEditForm(location) {
currentEditingLocation = location;
// Debug: Log all possible ID fields
console.log('Opening edit form for location:', {
'Id': location.Id,
'id': location.id,
'ID': location.ID,
'_id': location._id,
'all_keys': Object.keys(location)
});
// Extract ID - check multiple possible field names
const locationId = location.Id || location.id || location.ID || location._id;
if (!locationId) {
console.error('No ID found in location object. Available fields:', Object.keys(location));
showStatus('Error: Location ID not found. Check console for details.', 'error');
return;
}
// Store the ID in a data attribute for later use
document.getElementById('edit-location-id').value = locationId;
document.getElementById('edit-location-id').setAttribute('data-location-id', locationId);
// Populate form fields
document.getElementById('edit-location-id').value = location.Id || '';
document.getElementById('edit-first-name').value = location['First Name'] || '';
document.getElementById('edit-last-name').value = location['Last Name'] || '';
document.getElementById('edit-location-email').value = location.Email || '';
@ -558,7 +590,7 @@ function openEditForm(location) {
document.getElementById('edit-location-unit').value = location['Unit Number'] || '';
document.getElementById('edit-support-level').value = location['Support Level'] || '';
document.getElementById('edit-location-address').value = location.Address || '';
document.getElementById('edit-sign').checked = location.Sign === true || location.Sign === 'true';
document.getElementById('edit-sign').checked = location.Sign === true || location.Sign === 'true' || location.Sign === 1;
document.getElementById('edit-sign-size').value = location['Sign Size'] || '';
document.getElementById('edit-location-notes').value = location.Notes || '';
document.getElementById('edit-location-lat').value = location.latitude || '';
@ -581,12 +613,25 @@ async function handleEditLocation(e) {
if (!currentEditingLocation) return;
// Get the stored location ID
const locationIdElement = document.getElementById('edit-location-id');
const locationId = locationIdElement.getAttribute('data-location-id') || locationIdElement.value;
if (!locationId || locationId === 'undefined') {
showStatus('Error: Location ID not found', 'error');
return;
}
const formData = new FormData(e.target);
const data = { Id: currentEditingLocation.Id };
const data = {};
// Convert form data to object
for (let [key, value] of formData.entries()) {
if (value.trim() !== '') {
// Skip the ID field
if (key === 'id' || key === 'Id' || key === 'ID') continue;
if (value !== null && value !== undefined) {
// Don't skip empty strings - they may be intentional field clearing
data[key] = value.trim();
}
}
@ -599,8 +644,12 @@ async function handleEditLocation(e) {
// Handle checkbox
data.Sign = document.getElementById('edit-sign').checked;
// Add debugging
console.log('Sending update data for ID:', locationId);
console.log('Update data:', data);
try {
const response = await fetch(`/api/locations/${currentEditingLocation.Id}`, {
const response = await fetch(`/api/locations/${locationId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
@ -608,7 +657,15 @@ async function handleEditLocation(e) {
body: JSON.stringify(data)
});
const result = await response.json();
const responseText = await response.text();
let result;
try {
result = JSON.parse(responseText);
} catch (e) {
console.error('Failed to parse response:', responseText);
throw new Error(`Server response error: ${response.status} ${response.statusText}`);
}
if (result.success) {
showStatus('Location updated successfully!', 'success');
@ -619,7 +676,7 @@ async function handleEditLocation(e) {
}
} catch (error) {
console.error('Error updating location:', error);
showStatus(error.message || 'Failed to update location', 'error');
showStatus(`Update failed: ${error.message}`, 'error');
}
}
@ -627,12 +684,21 @@ async function handleEditLocation(e) {
async function handleDeleteLocation() {
if (!currentEditingLocation) return;
// Get the stored location ID
const locationIdElement = document.getElementById('edit-location-id');
const locationId = locationIdElement.getAttribute('data-location-id') || locationIdElement.value;
if (!locationId || locationId === 'undefined') {
showStatus('Error: Location ID not found', 'error');
return;
}
if (!confirm('Are you sure you want to delete this location?')) {
return;
}
try {
const response = await fetch(`/api/locations/${currentEditingLocation.Id}`, {
const response = await fetch(`/api/locations/${locationId}`, {
method: 'DELETE'
});
@ -651,51 +717,80 @@ async function handleDeleteLocation() {
}
}
// Lookup address
// Lookup address based on current coordinates
async function lookupAddress(mode) {
const addressInput = mode === 'add' ?
document.getElementById('location-address') :
document.getElementById('edit-location-address');
let latInput, lngInput, addressInput;
const address = addressInput.value.trim();
if (!address) {
showStatus('Please enter an address to lookup', 'warning');
if (mode === 'add') {
latInput = document.getElementById('location-lat');
lngInput = document.getElementById('location-lng');
addressInput = document.getElementById('location-address');
} else if (mode === 'edit') {
latInput = document.getElementById('edit-location-lat');
lngInput = document.getElementById('edit-location-lng');
addressInput = document.getElementById('edit-location-address');
} else {
console.error('Invalid lookup mode:', mode);
return;
}
try {
showStatus('Looking up address...', 'info');
const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(address)}&limit=1`);
const results = await response.json();
if (results.length > 0) {
const result = results[0];
const lat = parseFloat(result.lat);
const lng = parseFloat(result.lon);
// Update form fields
if (mode === 'add') {
document.getElementById('location-lat').value = lat.toFixed(8);
document.getElementById('location-lng').value = lng.toFixed(8);
document.getElementById('geo-location').value = `${lat.toFixed(8)};${lng.toFixed(8)}`;
} else {
document.getElementById('edit-location-lat').value = lat.toFixed(8);
document.getElementById('edit-location-lng').value = lng.toFixed(8);
document.getElementById('edit-geo-location').value = `${lat.toFixed(8)};${lng.toFixed(8)}`;
if (!latInput || !lngInput || !addressInput) {
showStatus('Form elements not found', 'error');
return;
}
// Center map on location
map.setView([lat, lng], 16);
const lat = parseFloat(latInput.value);
const lng = parseFloat(lngInput.value);
if (isNaN(lat) || isNaN(lng)) {
showStatus('Please enter valid coordinates first', 'warning');
return;
}
// Show loading state
const button = mode === 'add' ?
document.getElementById('lookup-address-add-btn') :
document.getElementById('lookup-address-edit-btn');
const originalText = button ? button.textContent : '';
if (button) {
button.disabled = true;
button.textContent = 'Looking up...';
}
try {
console.log(`Looking up address for: ${lat}, ${lng}`);
const response = await fetch(`/api/geocode/reverse?lat=${lat}&lng=${lng}`);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Geocoding failed: ${response.status} ${errorText}`);
}
const data = await response.json();
if (data.success && data.data) {
// Use the formatted address or full address
const address = data.data.formattedAddress || data.data.fullAddress;
if (address) {
addressInput.value = address;
showStatus('Address found!', 'success');
} else {
showStatus('Address not found. Please try a different format.', 'warning');
showStatus('No address found for these coordinates', 'warning');
}
} else {
showStatus('Address lookup failed', 'warning');
}
} catch (error) {
console.error('Address lookup error:', error);
showStatus('Failed to lookup address', 'error');
showStatus(`Address lookup failed: ${error.message}`, 'error');
} finally {
// Restore button state
if (button) {
button.disabled = false;
button.textContent = originalText;
}
}
}

View File

@ -952,6 +952,20 @@ app.get('/api/locations', async (req, res) => {
// Process locations to ensure they have required fields
const locations = response.data.list || [];
// Log the structure of the first location to debug ID field name
if (locations.length > 0) {
const sampleLocation = locations[0];
logger.info('Sample location structure:', {
keys: Object.keys(sampleLocation),
idFields: {
'Id': sampleLocation.Id,
'id': sampleLocation.id,
'ID': sampleLocation.ID,
'_id': sampleLocation._id
}
});
}
const validLocations = locations.filter(loc => {
// Apply geo field synchronization to each location
loc = syncGeoFields(loc);
@ -1148,15 +1162,34 @@ app.post('/api/locations', strictLimiter, async (req, res) => {
// Update location
app.put('/api/locations/:id', strictLimiter, async (req, res) => {
try {
const locationId = req.params.id;
// Validate ID
if (!locationId || locationId === 'undefined' || locationId === 'null') {
return res.status(400).json({
success: false,
error: 'Invalid location ID'
});
}
let updateData = { ...req.body };
// Remove ID from update data to avoid conflicts - use the correct field name
delete updateData.ID;
delete updateData.Id;
delete updateData.id;
delete updateData._id;
// Sync geo fields
updateData = syncGeoFields(updateData);
updateData.last_updated_at = new Date().toISOString();
updateData.last_updated_by = req.session.userEmail; // Track who updated
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${process.env.NOCODB_TABLE_ID}/${req.params.id}`;
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${process.env.NOCODB_TABLE_ID}/${locationId}`;
logger.info(`Updating location ${locationId} by ${req.session.userEmail}`);
logger.debug('Update data:', updateData);
const response = await axios.patch(url, updateData, {
headers: {
@ -1172,9 +1205,14 @@ app.put('/api/locations/:id', strictLimiter, async (req, res) => {
} catch (error) {
logger.error(`Error updating location ${req.params.id}:`, error.message);
if (error.response) {
logger.error('Error response:', error.response.data);
}
res.status(error.response?.status || 500).json({
success: false,
error: 'Failed to update location'
error: 'Failed to update location',
details: error.response?.data?.message || error.message
});
}
});
@ -1182,7 +1220,17 @@ app.put('/api/locations/:id', strictLimiter, async (req, res) => {
// Delete location
app.delete('/api/locations/:id', strictLimiter, async (req, res) => {
try {
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${process.env.NOCODB_TABLE_ID}/${req.params.id}`;
const locationId = req.params.id;
// Validate ID
if (!locationId || locationId === 'undefined' || locationId === 'null') {
return res.status(400).json({
success: false,
error: 'Invalid location ID'
});
}
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${process.env.NOCODB_TABLE_ID}/${locationId}`;
await axios.delete(url, {
headers: {
@ -1190,7 +1238,7 @@ app.delete('/api/locations/:id', strictLimiter, async (req, res) => {
}
});
logger.info(`Location ${req.params.id} deleted by ${req.session.userEmail}`);
logger.info(`Location ${locationId} deleted by ${req.session.userEmail}`);
res.json({
success: true,
@ -1206,67 +1254,34 @@ app.delete('/api/locations/:id', strictLimiter, async (req, res) => {
}
});
// Debug endpoint to check settings table structure
app.get('/api/debug/settings-table', requireAdmin, async (req, res) => {
// Add a debug endpoint to check table structure
app.get('/api/debug/table-structure', requireAdmin, async (req, res) => {
try {
logger.info('Debug: SETTINGS_SHEET_ID =', SETTINGS_SHEET_ID);
logger.info('Debug: NOCODB_API_URL =', process.env.NOCODB_API_URL);
logger.info('Debug: NOCODB_PROJECT_ID =', process.env.NOCODB_PROJECT_ID);
const url = `${process.env.NOCODB_API_URL}/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${process.env.NOCODB_TABLE_ID}`;
if (!SETTINGS_SHEET_ID) {
return res.json({
success: false,
error: 'SETTINGS_SHEET_ID not configured',
settingsSheetId: SETTINGS_SHEET_ID,
originalSetting: process.env.NOCODB_SETTINGS_SHEET
});
}
// Try the working endpoint
const workingEndpoint = `/db/data/v1/${process.env.NOCODB_PROJECT_ID}/${SETTINGS_SHEET_ID}`;
try {
const response = await axios.get(
`${process.env.NOCODB_API_URL}${workingEndpoint}`,
{
const response = await axios.get(url, {
headers: {
'xc-token': process.env.NOCODB_API_TOKEN
},
params: {
limit: 5
limit: 1
}
}
);
});
const records = response.data.list || [];
const sampleRecord = records.length > 0 ? records[0] : null;
const sample = response.data.list?.[0] || {};
res.json({
success: true,
settingsSheetId: SETTINGS_SHEET_ID,
workingEndpoint: workingEndpoint,
recordCount: response.data.pageInfo?.totalRows || 0,
sampleRecord: sampleRecord,
availableFields: sampleRecord ? Object.keys(sampleRecord) : [],
allRecords: records
fields: Object.keys(sample),
sampleRecord: sample,
idField: sample.ID ? 'ID' : (sample.Id ? 'Id' : (sample.id ? 'id' : 'unknown'))
});
} catch (error) {
res.json({
success: false,
error: error.message,
responseData: error.response?.data,
status: error.response?.status,
settingsSheetId: SETTINGS_SHEET_ID
});
}
} catch (error) {
logger.error('Debug settings table error:', error);
logger.error('Error checking table structure:', error);
res.status(500).json({
success: false,
error: error.message,
settingsSheetId: SETTINGS_SHEET_ID
error: 'Failed to check table structure'
});
}
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -7,10 +7,10 @@
"stars_count": 0,
"forks_count": 0,
"open_issues_count": 0,
"updated_at": "2025-07-04T14:31:11-06:00",
"updated_at": "2025-07-05T23:14:45-06:00",
"created_at": "2025-05-28T14:54:59-06:00",
"clone_url": "https://gitea.bnkops.com/admin/changemaker.lite.git",
"ssh_url": "git@gitea.bnkops.com:admin/changemaker.lite.git",
"default_branch": "main",
"last_build_update": "2025-07-04T14:31:11-06:00"
"last_build_update": "2025-07-05T23:14:45-06:00"
}

View File

@ -4,10 +4,10 @@
"description": "Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.",
"html_url": "https://github.com/anthropics/claude-code",
"language": "PowerShell",
"stars_count": 17373,
"forks_count": 959,
"open_issues_count": 1578,
"updated_at": "2025-07-04T21:17:51Z",
"stars_count": 17581,
"forks_count": 977,
"open_issues_count": 1616,
"updated_at": "2025-07-06T05:36:02Z",
"created_at": "2025-02-22T17:41:21Z",
"clone_url": "https://github.com/anthropics/claude-code.git",
"ssh_url": "git@github.com:anthropics/claude-code.git",

View File

@ -4,10 +4,10 @@
"description": "VS Code in the browser",
"html_url": "https://github.com/coder/code-server",
"language": "TypeScript",
"stars_count": 72668,
"forks_count": 6074,
"open_issues_count": 143,
"updated_at": "2025-07-04T18:33:06Z",
"stars_count": 72703,
"forks_count": 6078,
"open_issues_count": 144,
"updated_at": "2025-07-06T05:59:54Z",
"created_at": "2019-02-27T16:50:41Z",
"clone_url": "https://github.com/coder/code-server.git",
"ssh_url": "git@github.com:coder/code-server.git",

View File

@ -4,13 +4,13 @@
"description": "A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.",
"html_url": "https://github.com/gethomepage/homepage",
"language": "JavaScript",
"stars_count": 24643,
"forks_count": 1522,
"stars_count": 24660,
"forks_count": 1526,
"open_issues_count": 2,
"updated_at": "2025-07-04T19:49:50Z",
"updated_at": "2025-07-06T05:23:44Z",
"created_at": "2022-08-24T07:29:42Z",
"clone_url": "https://github.com/gethomepage/homepage.git",
"ssh_url": "git@github.com:gethomepage/homepage.git",
"default_branch": "dev",
"last_build_update": "2025-07-04T12:13:48Z"
"last_build_update": "2025-07-06T00:40:33Z"
}

View File

@ -4,13 +4,13 @@
"description": "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD",
"html_url": "https://github.com/go-gitea/gitea",
"language": "Go",
"stars_count": 49370,
"stars_count": 49388,
"forks_count": 5897,
"open_issues_count": 2711,
"updated_at": "2025-07-04T20:42:00Z",
"open_issues_count": 2708,
"updated_at": "2025-07-06T05:36:50Z",
"created_at": "2016-11-01T02:13:26Z",
"clone_url": "https://github.com/go-gitea/gitea.git",
"ssh_url": "git@github.com:go-gitea/gitea.git",
"default_branch": "main",
"last_build_update": "2025-07-04T15:41:19Z"
"last_build_update": "2025-07-06T05:36:45Z"
}

View File

@ -4,13 +4,13 @@
"description": "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.",
"html_url": "https://github.com/knadh/listmonk",
"language": "Go",
"stars_count": 17261,
"forks_count": 1659,
"open_issues_count": 103,
"updated_at": "2025-07-04T18:22:09Z",
"stars_count": 17265,
"forks_count": 1661,
"open_issues_count": 97,
"updated_at": "2025-07-06T04:14:35Z",
"created_at": "2019-06-26T05:08:39Z",
"clone_url": "https://github.com/knadh/listmonk.git",
"ssh_url": "git@github.com:knadh/listmonk.git",
"default_branch": "master",
"last_build_update": "2025-07-02T18:01:43Z"
"last_build_update": "2025-07-05T13:20:25Z"
}

View File

@ -4,13 +4,13 @@
"description": "Create & scan cute qr codes easily \ud83d\udc7e",
"html_url": "https://github.com/lyqht/mini-qr",
"language": "Vue",
"stars_count": 1258,
"stars_count": 1259,
"forks_count": 164,
"open_issues_count": 13,
"updated_at": "2025-07-04T21:12:26Z",
"updated_at": "2025-07-05T15:44:11Z",
"created_at": "2023-04-21T14:20:14Z",
"clone_url": "https://github.com/lyqht/mini-qr.git",
"ssh_url": "git@github.com:lyqht/mini-qr.git",
"default_branch": "main",
"last_build_update": "2025-07-01T14:06:08Z"
"last_build_update": "2025-07-06T03:08:22Z"
}

View File

@ -4,13 +4,13 @@
"description": "Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.",
"html_url": "https://github.com/n8n-io/n8n",
"language": "TypeScript",
"stars_count": 114980,
"forks_count": 33907,
"open_issues_count": 1074,
"updated_at": "2025-07-04T21:26:25Z",
"stars_count": 115275,
"forks_count": 34061,
"open_issues_count": 1080,
"updated_at": "2025-07-06T05:59:18Z",
"created_at": "2019-06-22T09:24:21Z",
"clone_url": "https://github.com/n8n-io/n8n.git",
"ssh_url": "git@github.com:n8n-io/n8n.git",
"default_branch": "master",
"last_build_update": "2025-07-04T18:53:03Z"
"last_build_update": "2025-07-05T21:59:59Z"
}

View File

@ -4,13 +4,13 @@
"description": "\ud83d\udd25 \ud83d\udd25 \ud83d\udd25 Open Source Airtable Alternative",
"html_url": "https://github.com/nocodb/nocodb",
"language": "TypeScript",
"stars_count": 55535,
"stars_count": 55551,
"forks_count": 3997,
"open_issues_count": 718,
"updated_at": "2025-07-04T20:56:30Z",
"open_issues_count": 717,
"updated_at": "2025-07-06T04:11:36Z",
"created_at": "2017-10-29T18:51:48Z",
"clone_url": "https://github.com/nocodb/nocodb.git",
"ssh_url": "git@github.com:nocodb/nocodb.git",
"default_branch": "develop",
"last_build_update": "2025-07-04T18:37:18Z"
"last_build_update": "2025-07-06T04:38:06Z"
}

View File

@ -4,13 +4,13 @@
"description": "Get up and running with Llama 3.3, DeepSeek-R1, Phi-4, Gemma 3, Mistral Small 3.1 and other large language models.",
"html_url": "https://github.com/ollama/ollama",
"language": "Go",
"stars_count": 145579,
"forks_count": 12297,
"open_issues_count": 1881,
"updated_at": "2025-07-04T20:55:45Z",
"stars_count": 145672,
"forks_count": 12305,
"open_issues_count": 1858,
"updated_at": "2025-07-06T05:29:22Z",
"created_at": "2023-06-26T19:39:32Z",
"clone_url": "https://github.com/ollama/ollama.git",
"ssh_url": "git@github.com:ollama/ollama.git",
"default_branch": "main",
"last_build_update": "2025-07-04T05:42:44Z"
"last_build_update": "2025-07-06T00:20:42Z"
}

View File

@ -4,10 +4,10 @@
"description": "Documentation that simply works",
"html_url": "https://github.com/squidfunk/mkdocs-material",
"language": "Python",
"stars_count": 23801,
"forks_count": 3792,
"open_issues_count": 6,
"updated_at": "2025-07-04T20:37:34Z",
"stars_count": 23806,
"forks_count": 3793,
"open_issues_count": 7,
"updated_at": "2025-07-06T00:58:35Z",
"created_at": "2016-01-28T22:09:23Z",
"clone_url": "https://github.com/squidfunk/mkdocs-material.git",
"ssh_url": "git@github.com:squidfunk/mkdocs-material.git",

View File

@ -1,100 +1,138 @@
# Map
# Map Build Guide
Map is BNKops canvassing application. It is built from the ground up to serve our community (Edmonton).
Map is BNKops canvassing application built for community organizing and door-to-door canvassing.
!!! info "Complete Configuration"
For detailed configuration, usage instructions, and troubleshooting, see the [Map Configuration Guide](../config/map.md).
!!! warning "Clean NocoDB"
Currently the way to get a good result is to ensure the target nocodb database is empty. You can do this by deleting all bases. The script should still work with other volumes however may insert tables into odd locations; still debugging. Again, see config if needing to do manually.
## Prerequisites
- Docker and Docker Compose installed
- NocoDB instance with API access
- Domain name (optional but recommended for production)
## NocoDB Table Setup
## Quick Build Process
### Required Columns
### 1. Get NocoDB API Token
!!! warning "Case Sensitive"
When entering in the required columns, make sure that you enter in exact information. Case sensitivity matters for mapping the values to the map itself.
1. Login to your NocoDB instance
2. Click user icon → **Account Settings** → **API Tokens**
3. Create new token with read/write permissions
4. Copy the token for the next step
Create a table in NocoDB with these required columns. The format here is the `Name of the column - column type - details`:
### 2. Configure Environment
1. **Geo-Location** (geo-data) - Format: "latitude;longitude"
2. **latitude** (Decimal) - Precision: 10, Scale: 8
3. **longitude** (Decimal) - Precision: 11, Scale: 8
### Recommended Columns
- First Name (Text)
- Last Name (Text)
- Email (Email)
- Phone (Phone)
- Unit Number (Text)
- Address (LongText)
- Support Level (Single Select) - Values (only enter numbers):
- 1 `Strong Support (Green)`
- 2 `Moderate Support (Yellow)`
- 3 `Low Support (Orange)`
- 4 `No Support (Red)`
- Sign (Checkbox)
- Sign Size (Single Select) - Values: Small, Medium, Large
- Notes (LongText)
## Login Sheet Setup
Create a separate table for authorized users with:
- Email (Email) - Primary column
- Name (Text) - Optional
## API Token Setup
1. In NocoDB, click user icon → Account Settings
2. Go to "API Tokens" tab
3. Create new token with read/write permissions for both tables
## 6. Finding NocoDB IDs
- **Project and Table IDs**: Use the full NocoDB view URL in `NOCODB_VIEW_URL`
- **Login Sheet ID**: Use the full URL to your login sheet in `NOCODB_LOGIN_SHEET`
## Environment Configuration
!!! note "Config"
The `./config.sh` should have created a new `.env` file. If `.env` file is present, and it has properly defined domain, skip to step 2
1. Copy the example env file:
Edit the `.env` file in the `map/` directory:
```bash
cp .env.example .env
cd map
```
2. Edit .env with your NocoDB details:
Update your `.env` file with your NocoDB details, specifically the instance and api token:
```env
# NocoDB API Configuration
NOCODB_API_URL=https://your-nocodb-instance.com/api/v1
NOCODB_API_TOKEN=your-api-token-here
NOCODB_VIEW_URL=https://your-nocodb-instance.com/dashboard/#/nc/project-id/table-id
NOCODB_LOGIN_SHEET=https://your-nocodb-instance.com/dashboard/#/nc/project-id/login-sheet-id
# These will be populated after running build-nocodb.sh
NOCODB_VIEW_URL=
NOCODB_LOGIN_SHEET=
NOCODB_SETTINGS_SHEET=
# Server Configuration
PORT=3000
NODE_ENV=production
# Session Secret (generate with: openssl rand -hex 32)
SESSION_SECRET=your-secure-random-string
# Map Defaults
# Map Defaults (Edmonton, AB)
DEFAULT_LAT=53.5461
DEFAULT_LNG=-113.4938
DEFAULT_ZOOM=11
# Optional: Map Boundaries
# BOUND_NORTH=53.7
# BOUND_SOUTH=53.4
# BOUND_EAST=-113.3
# BOUND_WEST=-113.7
# Domain Settings (for cookies)
# Production Settings
COOKIE_DOMAIN=.yourdomain.com
ALLOWED_ORIGINS=https://map.yourdomain.com,http://localhost:3000
```
## Running the Application
### 3. Auto-Create Database Structure
Run the build script to create required tables:
```bash
chmod +x build-nocodb.sh
./build-nocodb.sh
```
This creates three tables:
- **Locations** - Main map data with geo-location, contact info, support levels
- **Login** - User authentication (email, name, admin flag)
- **Settings** - Admin configuration and QR codes
### 4. Get Table URLs
After the script completes:
1. Login to your NocoDB instance
2. Navigate to your project ("Map Viewer Project")
3. Copy the view URLs for each table from your browser address bar
4. URLs should look like: `https://your-nocodb.com/dashboard/#/nc/project-id/table-id`
### 5. Update Environment with URLs
Edit your `.env` file and add the table URLs:
```env
NOCODB_VIEW_URL=https://your-nocodb.com/dashboard/#/nc/project-id/locations-table-id
NOCODB_LOGIN_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/login-table-id
NOCODB_SETTINGS_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/settings-table-id
```
### 6. Build and Deploy
Build the Docker image and start the application:
```bash
# Build the Docker image
docker-compose build
# Start the application
docker-compose up -d
```
## Verify Installation
1. Check container status:
```bash
docker-compose ps
```
2. View logs:
```bash
docker-compose logs -f map-viewer
```
3. Access the application at `http://localhost:3000`
## Quick Start
1. **Login**: Use an email from your Login table
2. **Add Locations**: Click on the map to add new locations
3. **Admin Panel**: Admin users can access `/admin.html` for configuration
4. **Walk Sheets**: Generate printable canvassing forms with QR codes
## Maintenance Commands
### Update Application
```bash
docker-compose down
git pull origin main
docker-compose build
docker-compose up -d
```
### Development Mode
```bash
@ -103,45 +141,11 @@ npm install
npm run dev
```
### Production with Docker
### Health Check
```bash
docker-compose up -d
curl http://localhost:3000/health
```
## First Run
## Support
1. Access the application at `http://localhost:3000` (or your domain)
2. Login with an email from your authorized users list
3. Verify locations appear on the map
## Maintenance
- To clear the geocoding cache, restart the application
- To update the application:
```bash
docker-compose down
git pull origin main
docker-compose up -d --build
```
## Troubleshooting
- **Locations not showing**: Verify table has required columns and API token has read permissions
- **Cannot add locations**: Check API token has write permissions
- **Authentication issues**: Verify login sheet is properly configured
## Security Recommendations
1. Use HTTPS in production
2. Regularly rotate API tokens
3. Restrict API token permissions to only what's needed
4. Set appropriate CORS and cookie domains
5. Keep dependencies updated
The application will automatically:
- Parse project/table IDs from view URLs
- Sync geo fields between different formats
- Cache geocoding results for performance
- Rate limit API endpoints
- Validate all inputs
For detailed configuration, troubleshooting, and usage instructions, see the [Map Configuration Guide](../config/map.md).

390
mkdocs/docs/config/map.md Normal file
View File

@ -0,0 +1,390 @@
# Map Configuration
The Map system is a containerized web application that visualizes geographic data from NocoDB on an interactive map using Leaflet.js. It's designed for canvassing applications and community organizing.
## Features
- 🗺️ Interactive map visualization with OpenStreetMap
- 📍 Real-time geolocation support for adding locations
- Add new locations directly from the map interface
- 🔄 Auto-refresh every 30 seconds
- 📱 Responsive design for mobile devices
- 🔒 Secure API proxy to protect NocoDB credentials
- 👤 User authentication with login system
- ⚙️ Admin panel for system configuration
- 🎯 Configurable map start location
- 📄 Walk Sheet generator for door-to-door canvassing
- 🔗 QR code integration for digital resources
- 🐳 Docker containerization for easy deployment
- 🆓 100% open source (no proprietary dependencies)
## Setup Process Overview
The setup process involves several steps that must be completed in order:
1. **Get NocoDB API Token** - Create an API token in your NocoDB instance
2. **Configure Environment** - Update the `.env` file with your NocoDB details
3. **Auto-Create Database Structure** - Run the build script to create required tables
4. **Get Table URLs** - Find and copy the URLs for the newly created tables
5. **Update Environment with URLs** - Add the table URLs to your `.env` file
6. **Build and Deploy** - Build the Docker image and start the application
## Prerequisites
- Docker and Docker Compose installed
- NocoDB instance with API access
- Domain name (optional but recommended for production)
## Step 1: Get NocoDB API Token
1. Login to your NocoDB instance
2. Click your user icon → **Account Settings**
3. Go to the **API Tokens** tab
4. Click **Create new token**
5. Set the following permissions:
- **Read**: Yes
- **Write**: Yes
- **Delete**: Yes (optional, for admin functions)
6. Copy the generated token - you'll need it for the next step
!!! warning "Token Security"
Keep your API token secure and never commit it to version control. The token provides full access to your NocoDB data.
## Step 2: Configure Environment
Edit the `.env` file in the `map/` directory:
```env
# NocoDB API Configuration
NOCODB_API_URL=https://your-nocodb-instance.com/api/v1
NOCODB_API_TOKEN=your-api-token-here
# These URLs will be populated after running build-nocodb.sh
NOCODB_VIEW_URL=
NOCODB_LOGIN_SHEET=
NOCODB_SETTINGS_SHEET=
# Server Configuration
PORT=3000
NODE_ENV=production
# Session Secret (generate with: openssl rand -hex 32)
SESSION_SECRET=your-secure-random-string
# Map Defaults (Edmonton, Alberta, Canada)
DEFAULT_LAT=53.5461
DEFAULT_LNG=-113.4938
DEFAULT_ZOOM=11
# Optional: Map Boundaries (prevents users from adding points outside area)
# BOUND_NORTH=53.7
# BOUND_SOUTH=53.4
# BOUND_EAST=-113.3
# BOUND_WEST=-113.7
# Production Settings
TRUST_PROXY=true
COOKIE_DOMAIN=.yourdomain.com
ALLOWED_ORIGINS=https://map.yourdomain.com,http://localhost:3000
```
### Required Configuration
- `NOCODB_API_URL`: Your NocoDB instance API URL (usually ends with `/api/v1`)
- `NOCODB_API_TOKEN`: The token you created in Step 1
- `SESSION_SECRET`: Generate a secure random string for session encryption
### Optional Configuration
- `DEFAULT_LAT/LNG/ZOOM`: Default map center and zoom level
- `BOUND_*`: Map boundaries to restrict where users can add points
- `COOKIE_DOMAIN`: Your domain for cookie security
- `ALLOWED_ORIGINS`: Comma-separated list of allowed origins for CORS
## Step 3: Auto-Create Database Structure
The `build-nocodb.sh` script will automatically create the required tables in your NocoDB instance.
```bash
cd map
chmod +x build-nocodb.sh
./build-nocodb.sh
```
### What the Script Creates
The script creates three tables with the following structure:
#### 1. Locations Table
Main table for storing map data:
- `Geo-Location` (Geo-Data): Format "latitude;longitude"
- `latitude` (Decimal): Precision 10, Scale 8
- `longitude` (Decimal): Precision 11, Scale 8
- `First Name` (Single Line Text): Person's first name
- `Last Name` (Single Line Text): Person's last name
- `Email` (Email): Email address
- `Phone` (Single Line Text): Phone number
- `Unit Number` (Single Line Text): Unit or apartment number
- `Address` (Single Line Text): Street address
- `Support Level` (Single Select): Options: "1", "2", "3", "4"
- 1 = Strong Support (Green)
- 2 = Moderate Support (Yellow)
- 3 = Low Support (Orange)
- 4 = No Support (Red)
- `Sign` (Checkbox): Has campaign sign
- `Sign Size` (Single Select): Options: "Small", "Medium", "Large"
- `Notes` (Long Text): Additional details and comments
#### 2. Login Table
User authentication table:
- `Email` (Email): User email address (Primary)
- `Name` (Single Line Text): User display name
- `Admin` (Checkbox): Admin privileges
#### 3. Settings Table
Admin configuration table:
- `key` (Single Line Text): Setting identifier
- `title` (Single Line Text): Display name
- `value` (Long Text): Setting value
- `Geo-Location` (Text): Format "latitude;longitude"
- `latitude` (Decimal): Precision 10, Scale 8
- `longitude` (Decimal): Precision 11, Scale 8
- `zoom` (Number): Map zoom level
- `category` (Single Select): Setting category
- `updated_by` (Single Line Text): Last updater email
- `updated_at` (DateTime): Last update time
- `qr_code_1_image` (Attachment): QR code 1 image
- `qr_code_2_image` (Attachment): QR code 2 image
- `qr_code_3_image` (Attachment): QR code 3 image
### Default Data
The script also creates:
- A default admin user (admin@example.com)
- A default start location setting
## Step 4: Get Table URLs
After the script completes successfully:
1. Login to your NocoDB instance
2. Navigate to your project (should be named "Map Viewer Project")
3. For each table, get the view URL:
- Click on the table name
- Copy the URL from your browser's address bar
- The URL should look like: `https://your-nocodb.com/dashboard/#/nc/project-id/table-id`
You need URLs for:
- **Locations table**`NOCODB_VIEW_URL`
- **Login table**`NOCODB_LOGIN_SHEET`
- **Settings table**`NOCODB_SETTINGS_SHEET`
## Step 5: Update Environment with URLs
Edit your `.env` file and add the table URLs:
```env
# Update these with the actual URLs from your NocoDB instance
NOCODB_VIEW_URL=https://your-nocodb.com/dashboard/#/nc/project-id/locations-table-id
NOCODB_LOGIN_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/login-table-id
NOCODB_SETTINGS_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/settings-table-id
```
!!! warning "URL Format"
Make sure to use the complete dashboard URLs, not the API URLs. The application will automatically extract the project and table IDs from these URLs.
## Step 6: Build and Deploy
Build the Docker image and start the application:
```bash
# Build the Docker image
docker-compose build
# Start the application
docker-compose up -d
```
### Verify Deployment
1. Check that the container is running:
```bash
docker-compose ps
```
2. Check the logs:
```bash
docker-compose logs -f map-viewer
```
3. Access the application at `http://localhost:3000` (or your configured domain)
## Using the Map System
### User Interface
#### Main Map View
- **Interactive Map**: Click and drag to navigate
- **Add Location**: Click on the map to add a new location
- **Search**: Use the search bar to find addresses
- **Refresh**: Data refreshes automatically every 30 seconds
#### Location Markers
- **Green**: Strong Support (Level 1)
- **Yellow**: Moderate Support (Level 2)
- **Orange**: Low Support (Level 3)
- **Red**: No Support (Level 4)
#### Adding Locations
1. Click on the map where you want to add a location
2. Fill out the form with contact information
3. Select support level and sign information
4. Add any relevant notes
5. Click "Save Location"
### Authentication
#### User Login
- Users must be added to the Login table in NocoDB
- Login with email address (no password required for simplified setup)
- Admin users have additional privileges
#### Admin Access
- Admin users can access `/admin.html`
- Configure map start location
- Set up walk sheet generator
- Manage QR codes and settings
### Admin Panel Features
#### Start Location Configuration
- **Interactive Map**: Visual interface for selecting coordinates
- **Real-time Preview**: See changes immediately
- **Validation**: Built-in coordinate and zoom level validation
#### Walk Sheet Generator
- **Printable Forms**: Generate 8.5x11 walk sheets for door-to-door canvassing
- **QR Code Integration**: Add up to 3 QR codes with custom URLs and labels
- **Form Field Matching**: Automatically matches fields from the main location form
- **Live Preview**: See changes as you type
- **Print Optimization**: Proper formatting for printing or PDF export
## API Endpoints
### Public Endpoints
- `GET /api/locations` - Fetch all locations (requires auth)
- `POST /api/locations` - Create new location (requires auth)
- `GET /api/locations/:id` - Get single location (requires auth)
- `PUT /api/locations/:id` - Update location (requires auth)
- `DELETE /api/locations/:id` - Delete location (requires auth)
- `GET /api/config/start-location` - Get map start location
- `GET /health` - Health check
### Authentication Endpoints
- `POST /api/auth/login` - User login
- `GET /api/auth/check` - Check authentication status
- `POST /api/auth/logout` - User logout
### Admin Endpoints (requires admin privileges)
- `GET /api/admin/start-location` - Get start location with source info
- `POST /api/admin/start-location` - Update map start location
- `GET /api/admin/walk-sheet-config` - Get walk sheet configuration
- `POST /api/admin/walk-sheet-config` - Save walk sheet configuration
## Troubleshooting
### Common Issues
#### Locations not showing
- Verify table has required columns (`Geo-Location`, `latitude`, `longitude`)
- Check that coordinates are valid numbers
- Ensure API token has read permissions
- Verify `NOCODB_VIEW_URL` is correct
#### Cannot add locations
- Verify API token has write permissions
- Check browser console for errors
- Ensure coordinates are within valid ranges
- Verify user is authenticated
#### Authentication issues
- Verify login table is properly configured
- Check that user email exists in Login table
- Ensure `NOCODB_LOGIN_SHEET` URL is correct
#### Build script failures
- Check that `NOCODB_API_URL` and `NOCODB_API_TOKEN` are correct
- Verify NocoDB instance is accessible
- Check network connectivity
- Review script output for specific error messages
### Development Mode
For development and debugging:
```bash
cd map/app
npm install
npm run dev
```
This will start the application with hot reload and detailed logging.
### Logs and Monitoring
View application logs:
```bash
docker-compose logs -f map-viewer
```
Check health status:
```bash
curl http://localhost:3000/health
```
## Security Considerations
1. **API Token Security**: Keep tokens secure and rotate regularly
2. **HTTPS**: Use HTTPS in production
3. **CORS Configuration**: Set appropriate `ALLOWED_ORIGINS`
4. **Cookie Security**: Configure `COOKIE_DOMAIN` properly
5. **Input Validation**: All inputs are validated server-side
6. **Rate Limiting**: API endpoints have rate limiting
7. **Session Security**: Use a strong `SESSION_SECRET`
## Maintenance
### Regular Updates
```bash
# Stop the application
docker-compose down
# Pull updates (if using git)
git pull origin main
# Rebuild and restart
docker-compose build
docker-compose up -d
```
### Backup Considerations
- NocoDB data is stored in your NocoDB instance
- Back up your `.env` file securely
- Consider backing up QR code images from the Settings table
### Performance Tips
- Monitor NocoDB performance and scaling
- Consider enabling caching for high-traffic deployments
- Use CDN for static assets if needed
- Monitor Docker container resource usage
## Support
For issues or questions:
1. Check the troubleshooting section above
2. Review NocoDB documentation
3. Check Docker and Docker Compose documentation
4. Open an issue on GitHub

View File

@ -175,6 +175,7 @@ nav:
- Cloudflare: config/cloudflare-config.md
- MKdocs: config/mkdocs.md
- Code Server: config/coder.md
- Map: config/map.md
- Advanced Configuration:
- adv/index.md
- SSH + Tailscale + Ansible: adv/ansible.md

View File

@ -1017,6 +1017,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1127,6 +1129,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>
@ -2355,43 +2380,55 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</span><span id="__span-3-9"><a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a><span class="c1"># Check SSH status</span>
</span><span id="__span-3-10"><a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a>sudo<span class="w"> </span>systemctl<span class="w"> </span>status<span class="w"> </span>ssh
</span></code></pre></div>
<p><strong>Note</strong>: If you get "Unit ssh.service could not be found", you need to install the SSH server first:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="c1"># Install OpenSSH server</span>
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>openssh-server
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a>
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="c1"># Then start and enable SSH</span>
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a>sudo<span class="w"> </span>systemctl<span class="w"> </span>start<span class="w"> </span>ssh
</span><span id="__span-4-6"><a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a>sudo<span class="w"> </span>systemctl<span class="w"> </span><span class="nb">enable</span><span class="w"> </span>ssh
</span><span id="__span-4-7"><a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a>
</span><span id="__span-4-8"><a id="__codelineno-4-8" name="__codelineno-4-8" href="#__codelineno-4-8"></a><span class="c1"># Verify SSH is running and listening</span>
</span><span id="__span-4-9"><a id="__codelineno-4-9" name="__codelineno-4-9" href="#__codelineno-4-9"></a>sudo<span class="w"> </span>ss<span class="w"> </span>-tlnp<span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span>:22
</span></code></pre></div>
<p>You should see SSH listening on port 22.</p>
<h3 id="2-configure-ssh-key-authentication">2. Configure SSH Key Authentication<a class="headerlink" href="#2-configure-ssh-key-authentication" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="c1"># Create .ssh directory</span>
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>mkdir<span class="w"> </span>-p<span class="w"> </span>~/.ssh
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a>chmod<span class="w"> </span><span class="m">700</span><span class="w"> </span>~/.ssh
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a>
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a><span class="c1"># Create authorized_keys file</span>
</span><span id="__span-4-6"><a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a>nano<span class="w"> </span>~/.ssh/authorized_keys
<div class="language-bash highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="c1"># Create .ssh directory</span>
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>mkdir<span class="w"> </span>-p<span class="w"> </span>~/.ssh
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a>chmod<span class="w"> </span><span class="m">700</span><span class="w"> </span>~/.ssh
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a>
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a><span class="c1"># Create authorized_keys file</span>
</span><span id="__span-5-6"><a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a>nano<span class="w"> </span>~/.ssh/authorized_keys
</span></code></pre></div>
<p>Paste your public key from the master node, then:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="c1"># Set proper permissions</span>
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>chmod<span class="w"> </span><span class="m">600</span><span class="w"> </span>~/.ssh/authorized_keys
<div class="language-bash highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="c1"># Set proper permissions</span>
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a>chmod<span class="w"> </span><span class="m">600</span><span class="w"> </span>~/.ssh/authorized_keys
</span></code></pre></div>
<h3 id="3-configure-ssh-security">3. Configure SSH Security<a class="headerlink" href="#3-configure-ssh-security" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="c1"># Edit SSH config</span>
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a>sudo<span class="w"> </span>nano<span class="w"> </span>/etc/ssh/sshd_config
<div class="language-bash highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="c1"># Edit SSH config</span>
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>sudo<span class="w"> </span>nano<span class="w"> </span>/etc/ssh/sshd_config
</span></code></pre></div>
<p>Ensure these lines are uncommented:</p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>PubkeyAuthentication yes
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
<div class="language-text highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a>PubkeyAuthentication yes
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
</span></code></pre></div>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="c1"># Restart SSH service</span>
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>sudo<span class="w"> </span>systemctl<span class="w"> </span>restart<span class="w"> </span>ssh
<div class="language-bash highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="c1"># Restart SSH service</span>
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a>sudo<span class="w"> </span>systemctl<span class="w"> </span>restart<span class="w"> </span>ssh
</span></code></pre></div>
<h3 id="4-configure-firewall">4. Configure Firewall<a class="headerlink" href="#4-configure-firewall" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="c1"># Check firewall status</span>
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a>sudo<span class="w"> </span>ufw<span class="w"> </span>status
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a>
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="c1"># Allow SSH through firewall</span>
</span><span id="__span-9-5"><a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a>sudo<span class="w"> </span>ufw<span class="w"> </span>allow<span class="w"> </span>ssh
</span><span id="__span-9-6"><a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a>
</span><span id="__span-9-7"><a id="__codelineno-9-7" name="__codelineno-9-7" href="#__codelineno-9-7"></a><span class="c1"># Fix home directory permissions (required for SSH keys)</span>
</span><span id="__span-9-8"><a id="__codelineno-9-8" name="__codelineno-9-8" href="#__codelineno-9-8"></a>chmod<span class="w"> </span><span class="m">755</span><span class="w"> </span>~/
<div class="language-bash highlight"><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="c1"># Check firewall status</span>
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>sudo<span class="w"> </span>ufw<span class="w"> </span>status
</span><span id="__span-10-3"><a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a>
</span><span id="__span-10-4"><a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a><span class="c1"># Allow SSH through firewall</span>
</span><span id="__span-10-5"><a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a>sudo<span class="w"> </span>ufw<span class="w"> </span>allow<span class="w"> </span>ssh
</span><span id="__span-10-6"><a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a>
</span><span id="__span-10-7"><a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a><span class="c1"># Fix home directory permissions (required for SSH keys)</span>
</span><span id="__span-10-8"><a id="__codelineno-10-8" name="__codelineno-10-8" href="#__codelineno-10-8"></a>chmod<span class="w"> </span><span class="m">755</span><span class="w"> </span>~/
</span></code></pre></div>
<h2 id="part-3-test-local-ssh-connection">Part 3: Test Local SSH Connection<a class="headerlink" href="#part-3-test-local-ssh-connection" title="Permanent link">&para;</a></h2>
<p>Before proceeding with remote access, test SSH connectivity locally:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="c1"># From master node, test SSH to target</span>
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>ssh<span class="w"> </span>username@&lt;target-local-ip&gt;
<div class="language-bash highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="c1"># From master node, test SSH to target</span>
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a>ssh<span class="w"> </span>username@&lt;target-local-ip&gt;
</span></code></pre></div>
<p><strong>Common Issues and Solutions:</strong></p>
<ul>
@ -2417,42 +2454,42 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li>Free for personal use</li>
</ul>
<h3 id="1-install-tailscale-on-master-node">1. Install Tailscale on Master Node<a class="headerlink" href="#1-install-tailscale-on-master-node" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="c1"># Install Tailscale</span>
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a>curl<span class="w"> </span>-fsSL<span class="w"> </span>https://tailscale.com/install.sh<span class="w"> </span><span class="p">|</span><span class="w"> </span>sh
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a>
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="c1"># Connect to Tailscale network</span>
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a>sudo<span class="w"> </span>tailscale<span class="w"> </span>up
</span></code></pre></div>
<p>Follow the authentication URL to connect with your Google/Microsoft/GitHub account.</p>
<h3 id="2-install-tailscale-on-target-nodes">2. Install Tailscale on Target Nodes<a class="headerlink" href="#2-install-tailscale-on-target-nodes" title="Permanent link">&para;</a></h3>
<p><strong>On each target node:</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="c1"># Install Tailscale</span>
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a>curl<span class="w"> </span>-fsSL<span class="w"> </span>https://tailscale.com/install.sh<span class="w"> </span><span class="p">|</span><span class="w"> </span>sh
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a>
</span><span id="__span-12-4"><a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="c1"># Connect to Tailscale network</span>
</span><span id="__span-12-5"><a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a>sudo<span class="w"> </span>tailscale<span class="w"> </span>up
</span></code></pre></div>
<p>Follow the authentication URL to connect with your Google/Microsoft/GitHub account.</p>
<h3 id="2-install-tailscale-on-target-nodes">2. Install Tailscale on Target Nodes<a class="headerlink" href="#2-install-tailscale-on-target-nodes" title="Permanent link">&para;</a></h3>
<p><strong>On each target node:</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="c1"># Install Tailscale</span>
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a>curl<span class="w"> </span>-fsSL<span class="w"> </span>https://tailscale.com/install.sh<span class="w"> </span><span class="p">|</span><span class="w"> </span>sh
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a>
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="c1"># Connect to Tailscale network</span>
</span><span id="__span-13-5"><a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a>sudo<span class="w"> </span>tailscale<span class="w"> </span>up
</span></code></pre></div>
<p>Authenticate each device through the provided URL.</p>
<h3 id="3-get-tailscale-ip-addresses">3. Get Tailscale IP Addresses<a class="headerlink" href="#3-get-tailscale-ip-addresses" title="Permanent link">&para;</a></h3>
<p><strong>On each machine:</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="c1"># Get your Tailscale IP</span>
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a>tailscale<span class="w"> </span>ip<span class="w"> </span>-4
<div class="language-bash highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="c1"># Get your Tailscale IP</span>
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a>tailscale<span class="w"> </span>ip<span class="w"> </span>-4
</span></code></pre></div>
<p>Each device receives a persistent IP like <code>100.x.x.x</code>.</p>
<h2 id="part-5-configure-ansible">Part 5: Configure Ansible<a class="headerlink" href="#part-5-configure-ansible" title="Permanent link">&para;</a></h2>
<h3 id="1-create-inventory-file">1. Create Inventory File<a class="headerlink" href="#1-create-inventory-file" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="c1"># Create inventory.ini</span>
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a><span class="nb">cd</span><span class="w"> </span>~/ansible_quickstart
</span><span id="__span-14-3"><a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a>nano<span class="w"> </span>inventory.ini
<div class="language-bash highlight"><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="c1"># Create inventory.ini</span>
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="nb">cd</span><span class="w"> </span>~/ansible_quickstart
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a>nano<span class="w"> </span>inventory.ini
</span></code></pre></div>
<p><strong>Content:</strong></p>
<div class="language-ini highlight"><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="k">[thinkcenter]</span>
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="na">tc-node1 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=your-username</span>
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="na">tc-node2 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=your-username</span>
</span><span id="__span-15-4"><a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a>
</span><span id="__span-15-5"><a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a><span class="k">[all:vars]</span>
</span><span id="__span-15-6"><a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a><span class="na">ansible_ssh_private_key_file</span><span class="o">=</span><span class="s">~/.ssh/id_rsa</span>
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a><span class="na">ansible_host_key_checking</span><span class="o">=</span><span class="s">False</span>
<div class="language-ini highlight"><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="k">[thinkcenter]</span>
</span><span id="__span-16-2"><a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a><span class="na">tc-node1 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=your-username</span>
</span><span id="__span-16-3"><a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a><span class="na">tc-node2 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=your-username</span>
</span><span id="__span-16-4"><a id="__codelineno-16-4" name="__codelineno-16-4" href="#__codelineno-16-4"></a>
</span><span id="__span-16-5"><a id="__codelineno-16-5" name="__codelineno-16-5" href="#__codelineno-16-5"></a><span class="k">[all:vars]</span>
</span><span id="__span-16-6"><a id="__codelineno-16-6" name="__codelineno-16-6" href="#__codelineno-16-6"></a><span class="na">ansible_ssh_private_key_file</span><span class="o">=</span><span class="s">~/.ssh/id_rsa</span>
</span><span id="__span-16-7"><a id="__codelineno-16-7" name="__codelineno-16-7" href="#__codelineno-16-7"></a><span class="na">ansible_host_key_checking</span><span class="o">=</span><span class="s">False</span>
</span></code></pre></div>
<p>Replace:</p>
<ul>
@ -2460,92 +2497,92 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li><code>your-username</code> with your actual username</li>
</ul>
<h3 id="2-test-ansible-connectivity">2. Test Ansible Connectivity<a class="headerlink" href="#2-test-ansible-connectivity" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="c1"># Test connection to all nodes</span>
</span><span id="__span-16-2"><a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a>ansible<span class="w"> </span>all<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-m<span class="w"> </span>ping
<div class="language-bash highlight"><pre><span></span><code><span id="__span-17-1"><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="c1"># Test connection to all nodes</span>
</span><span id="__span-17-2"><a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a>ansible<span class="w"> </span>all<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-m<span class="w"> </span>ping
</span></code></pre></div>
<p><strong>Expected output:</strong></p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-17-1"><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a>tc-node1 | SUCCESS =&gt; {
</span><span id="__span-17-2"><a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a> &quot;changed&quot;: false,
</span><span id="__span-17-3"><a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a> &quot;ping&quot;: &quot;pong&quot;
</span><span id="__span-17-4"><a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a>}
<div class="language-text highlight"><pre><span></span><code><span id="__span-18-1"><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a>tc-node1 | SUCCESS =&gt; {
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a> &quot;changed&quot;: false,
</span><span id="__span-18-3"><a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a> &quot;ping&quot;: &quot;pong&quot;
</span><span id="__span-18-4"><a id="__codelineno-18-4" name="__codelineno-18-4" href="#__codelineno-18-4"></a>}
</span></code></pre></div>
<h2 id="part-6-create-and-run-playbooks">Part 6: Create and Run Playbooks<a class="headerlink" href="#part-6-create-and-run-playbooks" title="Permanent link">&para;</a></h2>
<h3 id="1-simple-information-gathering-playbook">1. Simple Information Gathering Playbook<a class="headerlink" href="#1-simple-information-gathering-playbook" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-18-1"><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a>mkdir<span class="w"> </span>-p<span class="w"> </span>playbooks
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a>nano<span class="w"> </span>playbooks/info-playbook.yml
<div class="language-bash highlight"><pre><span></span><code><span id="__span-19-1"><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a>mkdir<span class="w"> </span>-p<span class="w"> </span>playbooks
</span><span id="__span-19-2"><a id="__codelineno-19-2" name="__codelineno-19-2" href="#__codelineno-19-2"></a>nano<span class="w"> </span>playbooks/info-playbook.yml
</span></code></pre></div>
<p><strong>Content:</strong></p>
<div class="language-yaml highlight"><pre><span></span><code><span id="__span-19-1"><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a><span class="nn">---</span>
</span><span id="__span-19-2"><a id="__codelineno-19-2" name="__codelineno-19-2" href="#__codelineno-19-2"></a><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Gather Node Information</span>
</span><span id="__span-19-3"><a id="__codelineno-19-3" name="__codelineno-19-3" href="#__codelineno-19-3"></a><span class="w"> </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">all</span>
</span><span id="__span-19-4"><a id="__codelineno-19-4" name="__codelineno-19-4" href="#__codelineno-19-4"></a><span class="w"> </span><span class="nt">tasks</span><span class="p">:</span>
</span><span id="__span-19-5"><a id="__codelineno-19-5" name="__codelineno-19-5" href="#__codelineno-19-5"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Get system information</span>
</span><span id="__span-19-6"><a id="__codelineno-19-6" name="__codelineno-19-6" href="#__codelineno-19-6"></a><span class="w"> </span><span class="nt">setup</span><span class="p">:</span>
</span><span id="__span-19-7"><a id="__codelineno-19-7" name="__codelineno-19-7" href="#__codelineno-19-7"></a>
</span><span id="__span-19-8"><a id="__codelineno-19-8" name="__codelineno-19-8" href="#__codelineno-19-8"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Display basic system info</span>
</span><span id="__span-19-9"><a id="__codelineno-19-9" name="__codelineno-19-9" href="#__codelineno-19-9"></a><span class="w"> </span><span class="nt">debug</span><span class="p">:</span>
</span><span id="__span-19-10"><a id="__codelineno-19-10" name="__codelineno-19-10" href="#__codelineno-19-10"></a><span class="w"> </span><span class="nt">msg</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">|</span>
</span><span id="__span-19-11"><a id="__codelineno-19-11" name="__codelineno-19-11" href="#__codelineno-19-11"></a><span class="w"> </span><span class="no">Hostname: {{ ansible_hostname }}</span>
</span><span id="__span-19-12"><a id="__codelineno-19-12" name="__codelineno-19-12" href="#__codelineno-19-12"></a><span class="w"> </span><span class="no">Operating System: {{ ansible_distribution }} {{ ansible_distribution_version }}</span>
</span><span id="__span-19-13"><a id="__codelineno-19-13" name="__codelineno-19-13" href="#__codelineno-19-13"></a><span class="w"> </span><span class="no">Architecture: {{ ansible_architecture }}</span>
</span><span id="__span-19-14"><a id="__codelineno-19-14" name="__codelineno-19-14" href="#__codelineno-19-14"></a><span class="w"> </span><span class="no">Memory: {{ ansible_memtotal_mb }}MB</span>
</span><span id="__span-19-15"><a id="__codelineno-19-15" name="__codelineno-19-15" href="#__codelineno-19-15"></a><span class="w"> </span><span class="no">CPU Cores: {{ ansible_processor_vcpus }}</span>
</span><span id="__span-19-16"><a id="__codelineno-19-16" name="__codelineno-19-16" href="#__codelineno-19-16"></a>
</span><span id="__span-19-17"><a id="__codelineno-19-17" name="__codelineno-19-17" href="#__codelineno-19-17"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Show disk usage</span>
</span><span id="__span-19-18"><a id="__codelineno-19-18" name="__codelineno-19-18" href="#__codelineno-19-18"></a><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">df -h /</span>
</span><span id="__span-19-19"><a id="__codelineno-19-19" name="__codelineno-19-19" href="#__codelineno-19-19"></a><span class="w"> </span><span class="nt">register</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">disk_info</span>
</span><span id="__span-19-20"><a id="__codelineno-19-20" name="__codelineno-19-20" href="#__codelineno-19-20"></a>
</span><span id="__span-19-21"><a id="__codelineno-19-21" name="__codelineno-19-21" href="#__codelineno-19-21"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Display disk usage</span>
</span><span id="__span-19-22"><a id="__codelineno-19-22" name="__codelineno-19-22" href="#__codelineno-19-22"></a><span class="w"> </span><span class="nt">debug</span><span class="p">:</span>
</span><span id="__span-19-23"><a id="__codelineno-19-23" name="__codelineno-19-23" href="#__codelineno-19-23"></a><span class="w"> </span><span class="nt">msg</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;Root</span><span class="nv"> </span><span class="s">filesystem</span><span class="nv"> </span><span class="s">usage:</span><span class="nv"> </span><span class="s">{{</span><span class="nv"> </span><span class="s">disk_info.stdout_lines[1]</span><span class="nv"> </span><span class="s">}}&quot;</span>
</span><span id="__span-19-24"><a id="__codelineno-19-24" name="__codelineno-19-24" href="#__codelineno-19-24"></a>
</span><span id="__span-19-25"><a id="__codelineno-19-25" name="__codelineno-19-25" href="#__codelineno-19-25"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Check uptime</span>
</span><span id="__span-19-26"><a id="__codelineno-19-26" name="__codelineno-19-26" href="#__codelineno-19-26"></a><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">uptime</span>
</span><span id="__span-19-27"><a id="__codelineno-19-27" name="__codelineno-19-27" href="#__codelineno-19-27"></a><span class="w"> </span><span class="nt">register</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">uptime_info</span>
</span><span id="__span-19-28"><a id="__codelineno-19-28" name="__codelineno-19-28" href="#__codelineno-19-28"></a>
</span><span id="__span-19-29"><a id="__codelineno-19-29" name="__codelineno-19-29" href="#__codelineno-19-29"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Display uptime</span>
</span><span id="__span-19-30"><a id="__codelineno-19-30" name="__codelineno-19-30" href="#__codelineno-19-30"></a><span class="w"> </span><span class="nt">debug</span><span class="p">:</span>
</span><span id="__span-19-31"><a id="__codelineno-19-31" name="__codelineno-19-31" href="#__codelineno-19-31"></a><span class="w"> </span><span class="nt">msg</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;System</span><span class="nv"> </span><span class="s">uptime:</span><span class="nv"> </span><span class="s">{{</span><span class="nv"> </span><span class="s">uptime_info.stdout</span><span class="nv"> </span><span class="s">}}&quot;</span>
<div class="language-yaml highlight"><pre><span></span><code><span id="__span-20-1"><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a><span class="nn">---</span>
</span><span id="__span-20-2"><a id="__codelineno-20-2" name="__codelineno-20-2" href="#__codelineno-20-2"></a><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Gather Node Information</span>
</span><span id="__span-20-3"><a id="__codelineno-20-3" name="__codelineno-20-3" href="#__codelineno-20-3"></a><span class="w"> </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">all</span>
</span><span id="__span-20-4"><a id="__codelineno-20-4" name="__codelineno-20-4" href="#__codelineno-20-4"></a><span class="w"> </span><span class="nt">tasks</span><span class="p">:</span>
</span><span id="__span-20-5"><a id="__codelineno-20-5" name="__codelineno-20-5" href="#__codelineno-20-5"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Get system information</span>
</span><span id="__span-20-6"><a id="__codelineno-20-6" name="__codelineno-20-6" href="#__codelineno-20-6"></a><span class="w"> </span><span class="nt">setup</span><span class="p">:</span>
</span><span id="__span-20-7"><a id="__codelineno-20-7" name="__codelineno-20-7" href="#__codelineno-20-7"></a>
</span><span id="__span-20-8"><a id="__codelineno-20-8" name="__codelineno-20-8" href="#__codelineno-20-8"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Display basic system info</span>
</span><span id="__span-20-9"><a id="__codelineno-20-9" name="__codelineno-20-9" href="#__codelineno-20-9"></a><span class="w"> </span><span class="nt">debug</span><span class="p">:</span>
</span><span id="__span-20-10"><a id="__codelineno-20-10" name="__codelineno-20-10" href="#__codelineno-20-10"></a><span class="w"> </span><span class="nt">msg</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">|</span>
</span><span id="__span-20-11"><a id="__codelineno-20-11" name="__codelineno-20-11" href="#__codelineno-20-11"></a><span class="w"> </span><span class="no">Hostname: {{ ansible_hostname }}</span>
</span><span id="__span-20-12"><a id="__codelineno-20-12" name="__codelineno-20-12" href="#__codelineno-20-12"></a><span class="w"> </span><span class="no">Operating System: {{ ansible_distribution }} {{ ansible_distribution_version }}</span>
</span><span id="__span-20-13"><a id="__codelineno-20-13" name="__codelineno-20-13" href="#__codelineno-20-13"></a><span class="w"> </span><span class="no">Architecture: {{ ansible_architecture }}</span>
</span><span id="__span-20-14"><a id="__codelineno-20-14" name="__codelineno-20-14" href="#__codelineno-20-14"></a><span class="w"> </span><span class="no">Memory: {{ ansible_memtotal_mb }}MB</span>
</span><span id="__span-20-15"><a id="__codelineno-20-15" name="__codelineno-20-15" href="#__codelineno-20-15"></a><span class="w"> </span><span class="no">CPU Cores: {{ ansible_processor_vcpus }}</span>
</span><span id="__span-20-16"><a id="__codelineno-20-16" name="__codelineno-20-16" href="#__codelineno-20-16"></a>
</span><span id="__span-20-17"><a id="__codelineno-20-17" name="__codelineno-20-17" href="#__codelineno-20-17"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Show disk usage</span>
</span><span id="__span-20-18"><a id="__codelineno-20-18" name="__codelineno-20-18" href="#__codelineno-20-18"></a><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">df -h /</span>
</span><span id="__span-20-19"><a id="__codelineno-20-19" name="__codelineno-20-19" href="#__codelineno-20-19"></a><span class="w"> </span><span class="nt">register</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">disk_info</span>
</span><span id="__span-20-20"><a id="__codelineno-20-20" name="__codelineno-20-20" href="#__codelineno-20-20"></a>
</span><span id="__span-20-21"><a id="__codelineno-20-21" name="__codelineno-20-21" href="#__codelineno-20-21"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Display disk usage</span>
</span><span id="__span-20-22"><a id="__codelineno-20-22" name="__codelineno-20-22" href="#__codelineno-20-22"></a><span class="w"> </span><span class="nt">debug</span><span class="p">:</span>
</span><span id="__span-20-23"><a id="__codelineno-20-23" name="__codelineno-20-23" href="#__codelineno-20-23"></a><span class="w"> </span><span class="nt">msg</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;Root</span><span class="nv"> </span><span class="s">filesystem</span><span class="nv"> </span><span class="s">usage:</span><span class="nv"> </span><span class="s">{{</span><span class="nv"> </span><span class="s">disk_info.stdout_lines[1]</span><span class="nv"> </span><span class="s">}}&quot;</span>
</span><span id="__span-20-24"><a id="__codelineno-20-24" name="__codelineno-20-24" href="#__codelineno-20-24"></a>
</span><span id="__span-20-25"><a id="__codelineno-20-25" name="__codelineno-20-25" href="#__codelineno-20-25"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Check uptime</span>
</span><span id="__span-20-26"><a id="__codelineno-20-26" name="__codelineno-20-26" href="#__codelineno-20-26"></a><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">uptime</span>
</span><span id="__span-20-27"><a id="__codelineno-20-27" name="__codelineno-20-27" href="#__codelineno-20-27"></a><span class="w"> </span><span class="nt">register</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">uptime_info</span>
</span><span id="__span-20-28"><a id="__codelineno-20-28" name="__codelineno-20-28" href="#__codelineno-20-28"></a>
</span><span id="__span-20-29"><a id="__codelineno-20-29" name="__codelineno-20-29" href="#__codelineno-20-29"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Display uptime</span>
</span><span id="__span-20-30"><a id="__codelineno-20-30" name="__codelineno-20-30" href="#__codelineno-20-30"></a><span class="w"> </span><span class="nt">debug</span><span class="p">:</span>
</span><span id="__span-20-31"><a id="__codelineno-20-31" name="__codelineno-20-31" href="#__codelineno-20-31"></a><span class="w"> </span><span class="nt">msg</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;System</span><span class="nv"> </span><span class="s">uptime:</span><span class="nv"> </span><span class="s">{{</span><span class="nv"> </span><span class="s">uptime_info.stdout</span><span class="nv"> </span><span class="s">}}&quot;</span>
</span></code></pre></div>
<h3 id="2-run-the-playbook">2. Run the Playbook<a class="headerlink" href="#2-run-the-playbook" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-20-1"><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a>ansible-playbook<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>playbooks/info-playbook.yml
<div class="language-bash highlight"><pre><span></span><code><span id="__span-21-1"><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a>ansible-playbook<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>playbooks/info-playbook.yml
</span></code></pre></div>
<h2 id="part-7-advanced-playbook-example">Part 7: Advanced Playbook Example<a class="headerlink" href="#part-7-advanced-playbook-example" title="Permanent link">&para;</a></h2>
<h3 id="system-setup-playbook">System Setup Playbook<a class="headerlink" href="#system-setup-playbook" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-21-1"><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a>nano<span class="w"> </span>playbooks/setup-node.yml
<div class="language-bash highlight"><pre><span></span><code><span id="__span-22-1"><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a>nano<span class="w"> </span>playbooks/setup-node.yml
</span></code></pre></div>
<p><strong>Content:</strong></p>
<div class="language-yaml highlight"><pre><span></span><code><span id="__span-22-1"><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a><span class="nn">---</span>
</span><span id="__span-22-2"><a id="__codelineno-22-2" name="__codelineno-22-2" href="#__codelineno-22-2"></a><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Setup ThinkCentre Node</span>
</span><span id="__span-22-3"><a id="__codelineno-22-3" name="__codelineno-22-3" href="#__codelineno-22-3"></a><span class="w"> </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">all</span>
</span><span id="__span-22-4"><a id="__codelineno-22-4" name="__codelineno-22-4" href="#__codelineno-22-4"></a><span class="w"> </span><span class="nt">become</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-22-5"><a id="__codelineno-22-5" name="__codelineno-22-5" href="#__codelineno-22-5"></a><span class="w"> </span><span class="nt">tasks</span><span class="p">:</span>
</span><span id="__span-22-6"><a id="__codelineno-22-6" name="__codelineno-22-6" href="#__codelineno-22-6"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Update package cache</span>
</span><span id="__span-22-7"><a id="__codelineno-22-7" name="__codelineno-22-7" href="#__codelineno-22-7"></a><span class="w"> </span><span class="nt">apt</span><span class="p">:</span>
</span><span id="__span-22-8"><a id="__codelineno-22-8" name="__codelineno-22-8" href="#__codelineno-22-8"></a><span class="w"> </span><span class="nt">update_cache</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-22-9"><a id="__codelineno-22-9" name="__codelineno-22-9" href="#__codelineno-22-9"></a>
</span><span id="__span-22-10"><a id="__codelineno-22-10" name="__codelineno-22-10" href="#__codelineno-22-10"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Install essential packages</span>
</span><span id="__span-22-11"><a id="__codelineno-22-11" name="__codelineno-22-11" href="#__codelineno-22-11"></a><span class="w"> </span><span class="nt">package</span><span class="p">:</span>
</span><span id="__span-22-12"><a id="__codelineno-22-12" name="__codelineno-22-12" href="#__codelineno-22-12"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span>
</span><span id="__span-22-13"><a id="__codelineno-22-13" name="__codelineno-22-13" href="#__codelineno-22-13"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">htop</span>
</span><span id="__span-22-14"><a id="__codelineno-22-14" name="__codelineno-22-14" href="#__codelineno-22-14"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">vim</span>
</span><span id="__span-22-15"><a id="__codelineno-22-15" name="__codelineno-22-15" href="#__codelineno-22-15"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">curl</span>
</span><span id="__span-22-16"><a id="__codelineno-22-16" name="__codelineno-22-16" href="#__codelineno-22-16"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git</span>
</span><span id="__span-22-17"><a id="__codelineno-22-17" name="__codelineno-22-17" href="#__codelineno-22-17"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">docker.io</span>
</span><span id="__span-22-18"><a id="__codelineno-22-18" name="__codelineno-22-18" href="#__codelineno-22-18"></a><span class="w"> </span><span class="nt">state</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">present</span>
</span><span id="__span-22-19"><a id="__codelineno-22-19" name="__codelineno-22-19" href="#__codelineno-22-19"></a>
</span><span id="__span-22-20"><a id="__codelineno-22-20" name="__codelineno-22-20" href="#__codelineno-22-20"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Add user to docker group</span>
</span><span id="__span-22-21"><a id="__codelineno-22-21" name="__codelineno-22-21" href="#__codelineno-22-21"></a><span class="w"> </span><span class="nt">user</span><span class="p">:</span>
</span><span id="__span-22-22"><a id="__codelineno-22-22" name="__codelineno-22-22" href="#__codelineno-22-22"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;{{</span><span class="nv"> </span><span class="s">ansible_user</span><span class="nv"> </span><span class="s">}}&quot;</span>
</span><span id="__span-22-23"><a id="__codelineno-22-23" name="__codelineno-22-23" href="#__codelineno-22-23"></a><span class="w"> </span><span class="nt">groups</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">docker</span>
</span><span id="__span-22-24"><a id="__codelineno-22-24" name="__codelineno-22-24" href="#__codelineno-22-24"></a><span class="w"> </span><span class="nt">append</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-22-25"><a id="__codelineno-22-25" name="__codelineno-22-25" href="#__codelineno-22-25"></a>
</span><span id="__span-22-26"><a id="__codelineno-22-26" name="__codelineno-22-26" href="#__codelineno-22-26"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Create management directory</span>
</span><span id="__span-22-27"><a id="__codelineno-22-27" name="__codelineno-22-27" href="#__codelineno-22-27"></a><span class="w"> </span><span class="nt">file</span><span class="p">:</span>
</span><span id="__span-22-28"><a id="__codelineno-22-28" name="__codelineno-22-28" href="#__codelineno-22-28"></a><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">/opt/management</span>
</span><span id="__span-22-29"><a id="__codelineno-22-29" name="__codelineno-22-29" href="#__codelineno-22-29"></a><span class="w"> </span><span class="nt">state</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">directory</span>
</span><span id="__span-22-30"><a id="__codelineno-22-30" name="__codelineno-22-30" href="#__codelineno-22-30"></a><span class="w"> </span><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;{{</span><span class="nv"> </span><span class="s">ansible_user</span><span class="nv"> </span><span class="s">}}&quot;</span>
</span><span id="__span-22-31"><a id="__codelineno-22-31" name="__codelineno-22-31" href="#__codelineno-22-31"></a><span class="w"> </span><span class="nt">group</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;{{</span><span class="nv"> </span><span class="s">ansible_user</span><span class="nv"> </span><span class="s">}}&quot;</span>
<div class="language-yaml highlight"><pre><span></span><code><span id="__span-23-1"><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a><span class="nn">---</span>
</span><span id="__span-23-2"><a id="__codelineno-23-2" name="__codelineno-23-2" href="#__codelineno-23-2"></a><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Setup ThinkCentre Node</span>
</span><span id="__span-23-3"><a id="__codelineno-23-3" name="__codelineno-23-3" href="#__codelineno-23-3"></a><span class="w"> </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">all</span>
</span><span id="__span-23-4"><a id="__codelineno-23-4" name="__codelineno-23-4" href="#__codelineno-23-4"></a><span class="w"> </span><span class="nt">become</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-23-5"><a id="__codelineno-23-5" name="__codelineno-23-5" href="#__codelineno-23-5"></a><span class="w"> </span><span class="nt">tasks</span><span class="p">:</span>
</span><span id="__span-23-6"><a id="__codelineno-23-6" name="__codelineno-23-6" href="#__codelineno-23-6"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Update package cache</span>
</span><span id="__span-23-7"><a id="__codelineno-23-7" name="__codelineno-23-7" href="#__codelineno-23-7"></a><span class="w"> </span><span class="nt">apt</span><span class="p">:</span>
</span><span id="__span-23-8"><a id="__codelineno-23-8" name="__codelineno-23-8" href="#__codelineno-23-8"></a><span class="w"> </span><span class="nt">update_cache</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-23-9"><a id="__codelineno-23-9" name="__codelineno-23-9" href="#__codelineno-23-9"></a>
</span><span id="__span-23-10"><a id="__codelineno-23-10" name="__codelineno-23-10" href="#__codelineno-23-10"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Install essential packages</span>
</span><span id="__span-23-11"><a id="__codelineno-23-11" name="__codelineno-23-11" href="#__codelineno-23-11"></a><span class="w"> </span><span class="nt">package</span><span class="p">:</span>
</span><span id="__span-23-12"><a id="__codelineno-23-12" name="__codelineno-23-12" href="#__codelineno-23-12"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span>
</span><span id="__span-23-13"><a id="__codelineno-23-13" name="__codelineno-23-13" href="#__codelineno-23-13"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">htop</span>
</span><span id="__span-23-14"><a id="__codelineno-23-14" name="__codelineno-23-14" href="#__codelineno-23-14"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">vim</span>
</span><span id="__span-23-15"><a id="__codelineno-23-15" name="__codelineno-23-15" href="#__codelineno-23-15"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">curl</span>
</span><span id="__span-23-16"><a id="__codelineno-23-16" name="__codelineno-23-16" href="#__codelineno-23-16"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git</span>
</span><span id="__span-23-17"><a id="__codelineno-23-17" name="__codelineno-23-17" href="#__codelineno-23-17"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">docker.io</span>
</span><span id="__span-23-18"><a id="__codelineno-23-18" name="__codelineno-23-18" href="#__codelineno-23-18"></a><span class="w"> </span><span class="nt">state</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">present</span>
</span><span id="__span-23-19"><a id="__codelineno-23-19" name="__codelineno-23-19" href="#__codelineno-23-19"></a>
</span><span id="__span-23-20"><a id="__codelineno-23-20" name="__codelineno-23-20" href="#__codelineno-23-20"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Add user to docker group</span>
</span><span id="__span-23-21"><a id="__codelineno-23-21" name="__codelineno-23-21" href="#__codelineno-23-21"></a><span class="w"> </span><span class="nt">user</span><span class="p">:</span>
</span><span id="__span-23-22"><a id="__codelineno-23-22" name="__codelineno-23-22" href="#__codelineno-23-22"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;{{</span><span class="nv"> </span><span class="s">ansible_user</span><span class="nv"> </span><span class="s">}}&quot;</span>
</span><span id="__span-23-23"><a id="__codelineno-23-23" name="__codelineno-23-23" href="#__codelineno-23-23"></a><span class="w"> </span><span class="nt">groups</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">docker</span>
</span><span id="__span-23-24"><a id="__codelineno-23-24" name="__codelineno-23-24" href="#__codelineno-23-24"></a><span class="w"> </span><span class="nt">append</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-23-25"><a id="__codelineno-23-25" name="__codelineno-23-25" href="#__codelineno-23-25"></a>
</span><span id="__span-23-26"><a id="__codelineno-23-26" name="__codelineno-23-26" href="#__codelineno-23-26"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Create management directory</span>
</span><span id="__span-23-27"><a id="__codelineno-23-27" name="__codelineno-23-27" href="#__codelineno-23-27"></a><span class="w"> </span><span class="nt">file</span><span class="p">:</span>
</span><span id="__span-23-28"><a id="__codelineno-23-28" name="__codelineno-23-28" href="#__codelineno-23-28"></a><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">/opt/management</span>
</span><span id="__span-23-29"><a id="__codelineno-23-29" name="__codelineno-23-29" href="#__codelineno-23-29"></a><span class="w"> </span><span class="nt">state</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">directory</span>
</span><span id="__span-23-30"><a id="__codelineno-23-30" name="__codelineno-23-30" href="#__codelineno-23-30"></a><span class="w"> </span><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;{{</span><span class="nv"> </span><span class="s">ansible_user</span><span class="nv"> </span><span class="s">}}&quot;</span>
</span><span id="__span-23-31"><a id="__codelineno-23-31" name="__codelineno-23-31" href="#__codelineno-23-31"></a><span class="w"> </span><span class="nt">group</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;{{</span><span class="nv"> </span><span class="s">ansible_user</span><span class="nv"> </span><span class="s">}}&quot;</span>
</span></code></pre></div>
<h2 id="troubleshooting-guide">Troubleshooting Guide<a class="headerlink" href="#troubleshooting-guide" title="Permanent link">&para;</a></h2>
<h3 id="ssh-issues">SSH Issues<a class="headerlink" href="#ssh-issues" title="Permanent link">&para;</a></h3>
@ -2562,7 +2599,7 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li>Ensure SSH config allows key auth: <code>PubkeyAuthentication yes</code></li>
</ul>
<p><strong>Problem: Bad owner or permissions on SSH config</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-23-1"><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a>chmod<span class="w"> </span><span class="m">600</span><span class="w"> </span>~/.ssh/config
<div class="language-bash highlight"><pre><span></span><code><span id="__span-24-1"><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a>chmod<span class="w"> </span><span class="m">600</span><span class="w"> </span>~/.ssh/config
</span></code></pre></div>
<h3 id="ansible-issues">Ansible Issues<a class="headerlink" href="#ansible-issues" title="Permanent link">&para;</a></h3>
<p><strong>Problem: Host key verification failed</strong></p>
@ -2570,7 +2607,7 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li>Add to inventory: <code>ansible_host_key_checking=False</code></li>
</ul>
<p><strong>Problem: Ansible command not found</strong></p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-24-1"><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a>sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>ansible
<div class="language-bash highlight"><pre><span></span><code><span id="__span-25-1"><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a>sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>ansible
</span></code></pre></div>
<p><strong>Problem: Connection timeouts</strong></p>
<ul>
@ -2591,25 +2628,25 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li><strong>Set up SSH access</strong> (repeat Part 2)</li>
<li><strong>Add to inventory.ini:</strong></li>
</ol>
<div class="language-ini highlight"><pre><span></span><code><span id="__span-25-1"><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a><span class="k">[thinkcenter]</span>
</span><span id="__span-25-2"><a id="__codelineno-25-2" name="__codelineno-25-2" href="#__codelineno-25-2"></a><span class="na">tc-node1 ansible_host</span><span class="o">=</span><span class="s">100.125.148.60 ansible_user=bunker-admin</span>
</span><span id="__span-25-3"><a id="__codelineno-25-3" name="__codelineno-25-3" href="#__codelineno-25-3"></a><span class="na">tc-node2 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
</span><span id="__span-25-4"><a id="__codelineno-25-4" name="__codelineno-25-4" href="#__codelineno-25-4"></a><span class="na">tc-node3 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
<div class="language-ini highlight"><pre><span></span><code><span id="__span-26-1"><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a><span class="k">[thinkcenter]</span>
</span><span id="__span-26-2"><a id="__codelineno-26-2" name="__codelineno-26-2" href="#__codelineno-26-2"></a><span class="na">tc-node1 ansible_host</span><span class="o">=</span><span class="s">100.125.148.60 ansible_user=bunker-admin</span>
</span><span id="__span-26-3"><a id="__codelineno-26-3" name="__codelineno-26-3" href="#__codelineno-26-3"></a><span class="na">tc-node2 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
</span><span id="__span-26-4"><a id="__codelineno-26-4" name="__codelineno-26-4" href="#__codelineno-26-4"></a><span class="na">tc-node3 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
</span></code></pre></div>
<h3 id="group-management">Group Management<a class="headerlink" href="#group-management" title="Permanent link">&para;</a></h3>
<div class="language-ini highlight"><pre><span></span><code><span id="__span-26-1"><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a><span class="k">[webservers]</span>
</span><span id="__span-26-2"><a id="__codelineno-26-2" name="__codelineno-26-2" href="#__codelineno-26-2"></a><span class="na">tc-node1 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
</span><span id="__span-26-3"><a id="__codelineno-26-3" name="__codelineno-26-3" href="#__codelineno-26-3"></a><span class="na">tc-node2 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
</span><span id="__span-26-4"><a id="__codelineno-26-4" name="__codelineno-26-4" href="#__codelineno-26-4"></a>
</span><span id="__span-26-5"><a id="__codelineno-26-5" name="__codelineno-26-5" href="#__codelineno-26-5"></a><span class="k">[databases]</span>
</span><span id="__span-26-6"><a id="__codelineno-26-6" name="__codelineno-26-6" href="#__codelineno-26-6"></a><span class="na">tc-node3 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
</span><span id="__span-26-7"><a id="__codelineno-26-7" name="__codelineno-26-7" href="#__codelineno-26-7"></a>
</span><span id="__span-26-8"><a id="__codelineno-26-8" name="__codelineno-26-8" href="#__codelineno-26-8"></a><span class="k">[all:vars]</span>
</span><span id="__span-26-9"><a id="__codelineno-26-9" name="__codelineno-26-9" href="#__codelineno-26-9"></a><span class="na">ansible_ssh_private_key_file</span><span class="o">=</span><span class="s">~/.ssh/id_rsa</span>
</span><span id="__span-26-10"><a id="__codelineno-26-10" name="__codelineno-26-10" href="#__codelineno-26-10"></a><span class="na">ansible_host_key_checking</span><span class="o">=</span><span class="s">False</span>
<div class="language-ini highlight"><pre><span></span><code><span id="__span-27-1"><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a><span class="k">[webservers]</span>
</span><span id="__span-27-2"><a id="__codelineno-27-2" name="__codelineno-27-2" href="#__codelineno-27-2"></a><span class="na">tc-node1 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
</span><span id="__span-27-3"><a id="__codelineno-27-3" name="__codelineno-27-3" href="#__codelineno-27-3"></a><span class="na">tc-node2 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
</span><span id="__span-27-4"><a id="__codelineno-27-4" name="__codelineno-27-4" href="#__codelineno-27-4"></a>
</span><span id="__span-27-5"><a id="__codelineno-27-5" name="__codelineno-27-5" href="#__codelineno-27-5"></a><span class="k">[databases]</span>
</span><span id="__span-27-6"><a id="__codelineno-27-6" name="__codelineno-27-6" href="#__codelineno-27-6"></a><span class="na">tc-node3 ansible_host</span><span class="o">=</span><span class="s">100.x.x.x ansible_user=bunker-admin</span>
</span><span id="__span-27-7"><a id="__codelineno-27-7" name="__codelineno-27-7" href="#__codelineno-27-7"></a>
</span><span id="__span-27-8"><a id="__codelineno-27-8" name="__codelineno-27-8" href="#__codelineno-27-8"></a><span class="k">[all:vars]</span>
</span><span id="__span-27-9"><a id="__codelineno-27-9" name="__codelineno-27-9" href="#__codelineno-27-9"></a><span class="na">ansible_ssh_private_key_file</span><span class="o">=</span><span class="s">~/.ssh/id_rsa</span>
</span><span id="__span-27-10"><a id="__codelineno-27-10" name="__codelineno-27-10" href="#__codelineno-27-10"></a><span class="na">ansible_host_key_checking</span><span class="o">=</span><span class="s">False</span>
</span></code></pre></div>
<p>Run playbooks on specific groups:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-27-1"><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a>ansible-playbook<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-l<span class="w"> </span>webservers<span class="w"> </span>playbook.yml
<div class="language-bash highlight"><pre><span></span><code><span id="__span-28-1"><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a>ansible-playbook<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-l<span class="w"> </span>webservers<span class="w"> </span>playbook.yml
</span></code></pre></div>
<h2 id="best-practices">Best Practices<a class="headerlink" href="#best-practices" title="Permanent link">&para;</a></h2>
<h3 id="security">Security<a class="headerlink" href="#security" title="Permanent link">&para;</a></h3>
@ -2620,31 +2657,31 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li>Use <code>become: yes</code> only when necessary</li>
</ul>
<h3 id="organization">Organization<a class="headerlink" href="#organization" title="Permanent link">&para;</a></h3>
<div class="language-text highlight"><pre><span></span><code><span id="__span-28-1"><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a>ansible_quickstart/
</span><span id="__span-28-2"><a id="__codelineno-28-2" name="__codelineno-28-2" href="#__codelineno-28-2"></a>├── inventory.ini
</span><span id="__span-28-3"><a id="__codelineno-28-3" name="__codelineno-28-3" href="#__codelineno-28-3"></a>├── group_vars/
</span><span id="__span-28-4"><a id="__codelineno-28-4" name="__codelineno-28-4" href="#__codelineno-28-4"></a>├── host_vars/
</span><span id="__span-28-5"><a id="__codelineno-28-5" name="__codelineno-28-5" href="#__codelineno-28-5"></a>├── roles/
</span><span id="__span-28-6"><a id="__codelineno-28-6" name="__codelineno-28-6" href="#__codelineno-28-6"></a>└── playbooks/
</span><span id="__span-28-7"><a id="__codelineno-28-7" name="__codelineno-28-7" href="#__codelineno-28-7"></a> ├── info-playbook.yml
</span><span id="__span-28-8"><a id="__codelineno-28-8" name="__codelineno-28-8" href="#__codelineno-28-8"></a> ├── setup-node.yml
</span><span id="__span-28-9"><a id="__codelineno-28-9" name="__codelineno-28-9" href="#__codelineno-28-9"></a> └── maintenance.yml
<div class="language-text highlight"><pre><span></span><code><span id="__span-29-1"><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a>ansible_quickstart/
</span><span id="__span-29-2"><a id="__codelineno-29-2" name="__codelineno-29-2" href="#__codelineno-29-2"></a>├── inventory.ini
</span><span id="__span-29-3"><a id="__codelineno-29-3" name="__codelineno-29-3" href="#__codelineno-29-3"></a>├── group_vars/
</span><span id="__span-29-4"><a id="__codelineno-29-4" name="__codelineno-29-4" href="#__codelineno-29-4"></a>├── host_vars/
</span><span id="__span-29-5"><a id="__codelineno-29-5" name="__codelineno-29-5" href="#__codelineno-29-5"></a>├── roles/
</span><span id="__span-29-6"><a id="__codelineno-29-6" name="__codelineno-29-6" href="#__codelineno-29-6"></a>└── playbooks/
</span><span id="__span-29-7"><a id="__codelineno-29-7" name="__codelineno-29-7" href="#__codelineno-29-7"></a> ├── info-playbook.yml
</span><span id="__span-29-8"><a id="__codelineno-29-8" name="__codelineno-29-8" href="#__codelineno-29-8"></a> ├── setup-node.yml
</span><span id="__span-29-9"><a id="__codelineno-29-9" name="__codelineno-29-9" href="#__codelineno-29-9"></a> └── maintenance.yml
</span></code></pre></div>
<h3 id="monitoring-and-maintenance">Monitoring and Maintenance<a class="headerlink" href="#monitoring-and-maintenance" title="Permanent link">&para;</a></h3>
<p>Create regular maintenance playbooks:</p>
<div class="language-yaml highlight"><pre><span></span><code><span id="__span-29-1"><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">System maintenance</span>
</span><span id="__span-29-2"><a id="__codelineno-29-2" name="__codelineno-29-2" href="#__codelineno-29-2"></a><span class="w"> </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">all</span>
</span><span id="__span-29-3"><a id="__codelineno-29-3" name="__codelineno-29-3" href="#__codelineno-29-3"></a><span class="w"> </span><span class="nt">become</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-29-4"><a id="__codelineno-29-4" name="__codelineno-29-4" href="#__codelineno-29-4"></a><span class="w"> </span><span class="nt">tasks</span><span class="p">:</span>
</span><span id="__span-29-5"><a id="__codelineno-29-5" name="__codelineno-29-5" href="#__codelineno-29-5"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Update all packages</span>
</span><span id="__span-29-6"><a id="__codelineno-29-6" name="__codelineno-29-6" href="#__codelineno-29-6"></a><span class="w"> </span><span class="nt">apt</span><span class="p">:</span>
</span><span id="__span-29-7"><a id="__codelineno-29-7" name="__codelineno-29-7" href="#__codelineno-29-7"></a><span class="w"> </span><span class="nt">upgrade</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">dist</span>
</span><span id="__span-29-8"><a id="__codelineno-29-8" name="__codelineno-29-8" href="#__codelineno-29-8"></a><span class="w"> </span><span class="nt">update_cache</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-29-9"><a id="__codelineno-29-9" name="__codelineno-29-9" href="#__codelineno-29-9"></a>
</span><span id="__span-29-10"><a id="__codelineno-29-10" name="__codelineno-29-10" href="#__codelineno-29-10"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Clean package cache</span>
</span><span id="__span-29-11"><a id="__codelineno-29-11" name="__codelineno-29-11" href="#__codelineno-29-11"></a><span class="w"> </span><span class="nt">apt</span><span class="p">:</span>
</span><span id="__span-29-12"><a id="__codelineno-29-12" name="__codelineno-29-12" href="#__codelineno-29-12"></a><span class="w"> </span><span class="nt">autoclean</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-29-13"><a id="__codelineno-29-13" name="__codelineno-29-13" href="#__codelineno-29-13"></a><span class="w"> </span><span class="nt">autoremove</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
<div class="language-yaml highlight"><pre><span></span><code><span id="__span-30-1"><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">System maintenance</span>
</span><span id="__span-30-2"><a id="__codelineno-30-2" name="__codelineno-30-2" href="#__codelineno-30-2"></a><span class="w"> </span><span class="nt">hosts</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">all</span>
</span><span id="__span-30-3"><a id="__codelineno-30-3" name="__codelineno-30-3" href="#__codelineno-30-3"></a><span class="w"> </span><span class="nt">become</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-30-4"><a id="__codelineno-30-4" name="__codelineno-30-4" href="#__codelineno-30-4"></a><span class="w"> </span><span class="nt">tasks</span><span class="p">:</span>
</span><span id="__span-30-5"><a id="__codelineno-30-5" name="__codelineno-30-5" href="#__codelineno-30-5"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Update all packages</span>
</span><span id="__span-30-6"><a id="__codelineno-30-6" name="__codelineno-30-6" href="#__codelineno-30-6"></a><span class="w"> </span><span class="nt">apt</span><span class="p">:</span>
</span><span id="__span-30-7"><a id="__codelineno-30-7" name="__codelineno-30-7" href="#__codelineno-30-7"></a><span class="w"> </span><span class="nt">upgrade</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">dist</span>
</span><span id="__span-30-8"><a id="__codelineno-30-8" name="__codelineno-30-8" href="#__codelineno-30-8"></a><span class="w"> </span><span class="nt">update_cache</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-30-9"><a id="__codelineno-30-9" name="__codelineno-30-9" href="#__codelineno-30-9"></a>
</span><span id="__span-30-10"><a id="__codelineno-30-10" name="__codelineno-30-10" href="#__codelineno-30-10"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Clean package cache</span>
</span><span id="__span-30-11"><a id="__codelineno-30-11" name="__codelineno-30-11" href="#__codelineno-30-11"></a><span class="w"> </span><span class="nt">apt</span><span class="p">:</span>
</span><span id="__span-30-12"><a id="__codelineno-30-12" name="__codelineno-30-12" href="#__codelineno-30-12"></a><span class="w"> </span><span class="nt">autoclean</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span><span id="__span-30-13"><a id="__codelineno-30-13" name="__codelineno-30-13" href="#__codelineno-30-13"></a><span class="w"> </span><span class="nt">autoremove</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
</span></code></pre></div>
<h2 id="alternative-approaches-we-considered">Alternative Approaches We Considered<a class="headerlink" href="#alternative-approaches-we-considered" title="Permanent link">&para;</a></h2>
<h3 id="cloudflare-tunnels">Cloudflare Tunnels<a class="headerlink" href="#cloudflare-tunnels" title="Permanent link">&para;</a></h3>
@ -2676,23 +2713,23 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</ul>
<p>The combination of Ansible + Tailscale is ideal for managing distributed infrastructure without the complexity of traditional VPN setups or the limitations of cloud-specific solutions.</p>
<h2 id="quick-reference-commands">Quick Reference Commands<a class="headerlink" href="#quick-reference-commands" title="Permanent link">&para;</a></h2>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-30-1"><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a><span class="c1"># Check Tailscale status</span>
</span><span id="__span-30-2"><a id="__codelineno-30-2" name="__codelineno-30-2" href="#__codelineno-30-2"></a>tailscale<span class="w"> </span>status
</span><span id="__span-30-3"><a id="__codelineno-30-3" name="__codelineno-30-3" href="#__codelineno-30-3"></a>
</span><span id="__span-30-4"><a id="__codelineno-30-4" name="__codelineno-30-4" href="#__codelineno-30-4"></a><span class="c1"># Test Ansible connectivity</span>
</span><span id="__span-30-5"><a id="__codelineno-30-5" name="__codelineno-30-5" href="#__codelineno-30-5"></a>ansible<span class="w"> </span>all<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-m<span class="w"> </span>ping
</span><span id="__span-30-6"><a id="__codelineno-30-6" name="__codelineno-30-6" href="#__codelineno-30-6"></a>
</span><span id="__span-30-7"><a id="__codelineno-30-7" name="__codelineno-30-7" href="#__codelineno-30-7"></a><span class="c1"># Run playbook on all hosts</span>
</span><span id="__span-30-8"><a id="__codelineno-30-8" name="__codelineno-30-8" href="#__codelineno-30-8"></a>ansible-playbook<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>playbook.yml
</span><span id="__span-30-9"><a id="__codelineno-30-9" name="__codelineno-30-9" href="#__codelineno-30-9"></a>
</span><span id="__span-30-10"><a id="__codelineno-30-10" name="__codelineno-30-10" href="#__codelineno-30-10"></a><span class="c1"># Run playbook on specific group</span>
</span><span id="__span-30-11"><a id="__codelineno-30-11" name="__codelineno-30-11" href="#__codelineno-30-11"></a>ansible-playbook<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-l<span class="w"> </span>groupname<span class="w"> </span>playbook.yml
</span><span id="__span-30-12"><a id="__codelineno-30-12" name="__codelineno-30-12" href="#__codelineno-30-12"></a>
</span><span id="__span-30-13"><a id="__codelineno-30-13" name="__codelineno-30-13" href="#__codelineno-30-13"></a><span class="c1"># Run single command on all hosts</span>
</span><span id="__span-30-14"><a id="__codelineno-30-14" name="__codelineno-30-14" href="#__codelineno-30-14"></a>ansible<span class="w"> </span>all<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-m<span class="w"> </span><span class="nb">command</span><span class="w"> </span>-a<span class="w"> </span><span class="s2">&quot;uptime&quot;</span>
</span><span id="__span-30-15"><a id="__codelineno-30-15" name="__codelineno-30-15" href="#__codelineno-30-15"></a>
</span><span id="__span-30-16"><a id="__codelineno-30-16" name="__codelineno-30-16" href="#__codelineno-30-16"></a><span class="c1"># SSH to node via Tailscale</span>
</span><span id="__span-30-17"><a id="__codelineno-30-17" name="__codelineno-30-17" href="#__codelineno-30-17"></a>ssh<span class="w"> </span>username@100.x.x.x
<div class="language-bash highlight"><pre><span></span><code><span id="__span-31-1"><a id="__codelineno-31-1" name="__codelineno-31-1" href="#__codelineno-31-1"></a><span class="c1"># Check Tailscale status</span>
</span><span id="__span-31-2"><a id="__codelineno-31-2" name="__codelineno-31-2" href="#__codelineno-31-2"></a>tailscale<span class="w"> </span>status
</span><span id="__span-31-3"><a id="__codelineno-31-3" name="__codelineno-31-3" href="#__codelineno-31-3"></a>
</span><span id="__span-31-4"><a id="__codelineno-31-4" name="__codelineno-31-4" href="#__codelineno-31-4"></a><span class="c1"># Test Ansible connectivity</span>
</span><span id="__span-31-5"><a id="__codelineno-31-5" name="__codelineno-31-5" href="#__codelineno-31-5"></a>ansible<span class="w"> </span>all<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-m<span class="w"> </span>ping
</span><span id="__span-31-6"><a id="__codelineno-31-6" name="__codelineno-31-6" href="#__codelineno-31-6"></a>
</span><span id="__span-31-7"><a id="__codelineno-31-7" name="__codelineno-31-7" href="#__codelineno-31-7"></a><span class="c1"># Run playbook on all hosts</span>
</span><span id="__span-31-8"><a id="__codelineno-31-8" name="__codelineno-31-8" href="#__codelineno-31-8"></a>ansible-playbook<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>playbook.yml
</span><span id="__span-31-9"><a id="__codelineno-31-9" name="__codelineno-31-9" href="#__codelineno-31-9"></a>
</span><span id="__span-31-10"><a id="__codelineno-31-10" name="__codelineno-31-10" href="#__codelineno-31-10"></a><span class="c1"># Run playbook on specific group</span>
</span><span id="__span-31-11"><a id="__codelineno-31-11" name="__codelineno-31-11" href="#__codelineno-31-11"></a>ansible-playbook<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-l<span class="w"> </span>groupname<span class="w"> </span>playbook.yml
</span><span id="__span-31-12"><a id="__codelineno-31-12" name="__codelineno-31-12" href="#__codelineno-31-12"></a>
</span><span id="__span-31-13"><a id="__codelineno-31-13" name="__codelineno-31-13" href="#__codelineno-31-13"></a><span class="c1"># Run single command on all hosts</span>
</span><span id="__span-31-14"><a id="__codelineno-31-14" name="__codelineno-31-14" href="#__codelineno-31-14"></a>ansible<span class="w"> </span>all<span class="w"> </span>-i<span class="w"> </span>inventory.ini<span class="w"> </span>-m<span class="w"> </span><span class="nb">command</span><span class="w"> </span>-a<span class="w"> </span><span class="s2">&quot;uptime&quot;</span>
</span><span id="__span-31-15"><a id="__codelineno-31-15" name="__codelineno-31-15" href="#__codelineno-31-15"></a>
</span><span id="__span-31-16"><a id="__codelineno-31-16" name="__codelineno-31-16" href="#__codelineno-31-16"></a><span class="c1"># SSH to node via Tailscale</span>
</span><span id="__span-31-17"><a id="__codelineno-31-17" name="__codelineno-31-17" href="#__codelineno-31-17"></a>ssh<span class="w"> </span>username@100.x.x.x
</span></code></pre></div>

View File

@ -15,7 +15,7 @@
<link rel="canonical" href="https://cmlite.org/adv/">
<link rel="prev" href="../config/coder/">
<link rel="prev" href="../config/map/">
<link rel="next" href="ansible/">
@ -1017,6 +1017,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1127,6 +1129,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>
@ -1397,7 +1422,7 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../config/coder/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Code Server">
<a href="../config/map/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Map">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
@ -1407,7 +1432,7 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
Previous
</span>
<div class="md-ellipsis">
Code Server
Map
</div>
</div>
</a>

View File

@ -1017,6 +1017,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1127,6 +1129,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -7,10 +7,10 @@
"stars_count": 0,
"forks_count": 0,
"open_issues_count": 0,
"updated_at": "2025-07-04T14:31:11-06:00",
"updated_at": "2025-07-05T23:14:45-06:00",
"created_at": "2025-05-28T14:54:59-06:00",
"clone_url": "https://gitea.bnkops.com/admin/changemaker.lite.git",
"ssh_url": "git@gitea.bnkops.com:admin/changemaker.lite.git",
"default_branch": "main",
"last_build_update": "2025-07-04T14:31:11-06:00"
"last_build_update": "2025-07-05T23:14:45-06:00"
}

View File

@ -4,10 +4,10 @@
"description": "Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.",
"html_url": "https://github.com/anthropics/claude-code",
"language": "PowerShell",
"stars_count": 17373,
"forks_count": 959,
"open_issues_count": 1578,
"updated_at": "2025-07-04T21:17:51Z",
"stars_count": 17581,
"forks_count": 977,
"open_issues_count": 1616,
"updated_at": "2025-07-06T05:36:02Z",
"created_at": "2025-02-22T17:41:21Z",
"clone_url": "https://github.com/anthropics/claude-code.git",
"ssh_url": "git@github.com:anthropics/claude-code.git",

View File

@ -4,10 +4,10 @@
"description": "VS Code in the browser",
"html_url": "https://github.com/coder/code-server",
"language": "TypeScript",
"stars_count": 72668,
"forks_count": 6074,
"open_issues_count": 143,
"updated_at": "2025-07-04T18:33:06Z",
"stars_count": 72703,
"forks_count": 6078,
"open_issues_count": 144,
"updated_at": "2025-07-06T05:59:54Z",
"created_at": "2019-02-27T16:50:41Z",
"clone_url": "https://github.com/coder/code-server.git",
"ssh_url": "git@github.com:coder/code-server.git",

View File

@ -4,13 +4,13 @@
"description": "A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.",
"html_url": "https://github.com/gethomepage/homepage",
"language": "JavaScript",
"stars_count": 24643,
"forks_count": 1522,
"stars_count": 24660,
"forks_count": 1526,
"open_issues_count": 2,
"updated_at": "2025-07-04T19:49:50Z",
"updated_at": "2025-07-06T05:23:44Z",
"created_at": "2022-08-24T07:29:42Z",
"clone_url": "https://github.com/gethomepage/homepage.git",
"ssh_url": "git@github.com:gethomepage/homepage.git",
"default_branch": "dev",
"last_build_update": "2025-07-04T12:13:48Z"
"last_build_update": "2025-07-06T00:40:33Z"
}

View File

@ -4,13 +4,13 @@
"description": "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD",
"html_url": "https://github.com/go-gitea/gitea",
"language": "Go",
"stars_count": 49370,
"stars_count": 49388,
"forks_count": 5897,
"open_issues_count": 2711,
"updated_at": "2025-07-04T20:42:00Z",
"open_issues_count": 2708,
"updated_at": "2025-07-06T05:36:50Z",
"created_at": "2016-11-01T02:13:26Z",
"clone_url": "https://github.com/go-gitea/gitea.git",
"ssh_url": "git@github.com:go-gitea/gitea.git",
"default_branch": "main",
"last_build_update": "2025-07-04T15:41:19Z"
"last_build_update": "2025-07-06T05:36:45Z"
}

View File

@ -4,13 +4,13 @@
"description": "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.",
"html_url": "https://github.com/knadh/listmonk",
"language": "Go",
"stars_count": 17261,
"forks_count": 1659,
"open_issues_count": 103,
"updated_at": "2025-07-04T18:22:09Z",
"stars_count": 17265,
"forks_count": 1661,
"open_issues_count": 97,
"updated_at": "2025-07-06T04:14:35Z",
"created_at": "2019-06-26T05:08:39Z",
"clone_url": "https://github.com/knadh/listmonk.git",
"ssh_url": "git@github.com:knadh/listmonk.git",
"default_branch": "master",
"last_build_update": "2025-07-02T18:01:43Z"
"last_build_update": "2025-07-05T13:20:25Z"
}

View File

@ -4,13 +4,13 @@
"description": "Create & scan cute qr codes easily \ud83d\udc7e",
"html_url": "https://github.com/lyqht/mini-qr",
"language": "Vue",
"stars_count": 1258,
"stars_count": 1259,
"forks_count": 164,
"open_issues_count": 13,
"updated_at": "2025-07-04T21:12:26Z",
"updated_at": "2025-07-05T15:44:11Z",
"created_at": "2023-04-21T14:20:14Z",
"clone_url": "https://github.com/lyqht/mini-qr.git",
"ssh_url": "git@github.com:lyqht/mini-qr.git",
"default_branch": "main",
"last_build_update": "2025-07-01T14:06:08Z"
"last_build_update": "2025-07-06T03:08:22Z"
}

View File

@ -4,13 +4,13 @@
"description": "Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.",
"html_url": "https://github.com/n8n-io/n8n",
"language": "TypeScript",
"stars_count": 114980,
"forks_count": 33907,
"open_issues_count": 1074,
"updated_at": "2025-07-04T21:26:25Z",
"stars_count": 115275,
"forks_count": 34061,
"open_issues_count": 1080,
"updated_at": "2025-07-06T05:59:18Z",
"created_at": "2019-06-22T09:24:21Z",
"clone_url": "https://github.com/n8n-io/n8n.git",
"ssh_url": "git@github.com:n8n-io/n8n.git",
"default_branch": "master",
"last_build_update": "2025-07-04T18:53:03Z"
"last_build_update": "2025-07-05T21:59:59Z"
}

View File

@ -4,13 +4,13 @@
"description": "\ud83d\udd25 \ud83d\udd25 \ud83d\udd25 Open Source Airtable Alternative",
"html_url": "https://github.com/nocodb/nocodb",
"language": "TypeScript",
"stars_count": 55535,
"stars_count": 55551,
"forks_count": 3997,
"open_issues_count": 718,
"updated_at": "2025-07-04T20:56:30Z",
"open_issues_count": 717,
"updated_at": "2025-07-06T04:11:36Z",
"created_at": "2017-10-29T18:51:48Z",
"clone_url": "https://github.com/nocodb/nocodb.git",
"ssh_url": "git@github.com:nocodb/nocodb.git",
"default_branch": "develop",
"last_build_update": "2025-07-04T18:37:18Z"
"last_build_update": "2025-07-06T04:38:06Z"
}

View File

@ -4,13 +4,13 @@
"description": "Get up and running with Llama 3.3, DeepSeek-R1, Phi-4, Gemma 3, Mistral Small 3.1 and other large language models.",
"html_url": "https://github.com/ollama/ollama",
"language": "Go",
"stars_count": 145579,
"forks_count": 12297,
"open_issues_count": 1881,
"updated_at": "2025-07-04T20:55:45Z",
"stars_count": 145672,
"forks_count": 12305,
"open_issues_count": 1858,
"updated_at": "2025-07-06T05:29:22Z",
"created_at": "2023-06-26T19:39:32Z",
"clone_url": "https://github.com/ollama/ollama.git",
"ssh_url": "git@github.com:ollama/ollama.git",
"default_branch": "main",
"last_build_update": "2025-07-04T05:42:44Z"
"last_build_update": "2025-07-06T00:20:42Z"
}

View File

@ -4,10 +4,10 @@
"description": "Documentation that simply works",
"html_url": "https://github.com/squidfunk/mkdocs-material",
"language": "Python",
"stars_count": 23801,
"forks_count": 3792,
"open_issues_count": 6,
"updated_at": "2025-07-04T20:37:34Z",
"stars_count": 23806,
"forks_count": 3793,
"open_issues_count": 7,
"updated_at": "2025-07-06T00:58:35Z",
"created_at": "2016-01-28T22:09:23Z",
"clone_url": "https://github.com/squidfunk/mkdocs-material.git",
"ssh_url": "git@github.com:squidfunk/mkdocs-material.git",

View File

@ -1017,6 +1017,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1127,6 +1129,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -111,7 +111,7 @@
<div data-md-component="skip">
<a href="#map" class="md-skip">
<a href="#map-build-guide" class="md-skip">
Skip to content
</a>
@ -661,28 +661,64 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</li>
<li class="md-nav__item">
<a href="#nocodb-table-setup" class="md-nav__link">
<a href="#quick-build-process" class="md-nav__link">
<span class="md-ellipsis">
NocoDB Table Setup
Quick Build Process
</span>
</a>
<nav class="md-nav" aria-label="NocoDB Table Setup">
<nav class="md-nav" aria-label="Quick Build Process">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#required-columns" class="md-nav__link">
<a href="#1-get-nocodb-api-token" class="md-nav__link">
<span class="md-ellipsis">
Required Columns
1. Get NocoDB API Token
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#recommended-columns" class="md-nav__link">
<a href="#2-configure-environment" class="md-nav__link">
<span class="md-ellipsis">
Recommended Columns
2. Configure Environment
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3-auto-create-database-structure" class="md-nav__link">
<span class="md-ellipsis">
3. Auto-Create Database Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#4-get-table-urls" class="md-nav__link">
<span class="md-ellipsis">
4. Get Table URLs
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#5-update-environment-with-urls" class="md-nav__link">
<span class="md-ellipsis">
5. Update Environment with URLs
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#6-build-and-deploy" class="md-nav__link">
<span class="md-ellipsis">
6. Build and Deploy
</span>
</a>
@ -694,51 +730,42 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</li>
<li class="md-nav__item">
<a href="#login-sheet-setup" class="md-nav__link">
<a href="#verify-installation" class="md-nav__link">
<span class="md-ellipsis">
Login Sheet Setup
Verify Installation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#api-token-setup" class="md-nav__link">
<a href="#quick-start" class="md-nav__link">
<span class="md-ellipsis">
API Token Setup
Quick Start
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#6-finding-nocodb-ids" class="md-nav__link">
<a href="#maintenance-commands" class="md-nav__link">
<span class="md-ellipsis">
6. Finding NocoDB IDs
Maintenance Commands
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#environment-configuration" class="md-nav__link">
<span class="md-ellipsis">
Environment Configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#running-the-application" class="md-nav__link">
<span class="md-ellipsis">
Running the Application
</span>
</a>
<nav class="md-nav" aria-label="Running the Application">
<nav class="md-nav" aria-label="Maintenance Commands">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#update-application" class="md-nav__link">
<span class="md-ellipsis">
Update Application
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#development-mode" class="md-nav__link">
<span class="md-ellipsis">
@ -749,9 +776,9 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</li>
<li class="md-nav__item">
<a href="#production-with-docker" class="md-nav__link">
<a href="#health-check" class="md-nav__link">
<span class="md-ellipsis">
Production with Docker
Health Check
</span>
</a>
@ -763,36 +790,9 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</li>
<li class="md-nav__item">
<a href="#first-run" class="md-nav__link">
<a href="#support" class="md-nav__link">
<span class="md-ellipsis">
First Run
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#maintenance" class="md-nav__link">
<span class="md-ellipsis">
Maintenance
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#security-recommendations" class="md-nav__link">
<span class="md-ellipsis">
Security Recommendations
Support
</span>
</a>
@ -1208,6 +1208,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1318,6 +1320,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>
@ -1550,28 +1575,64 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</li>
<li class="md-nav__item">
<a href="#nocodb-table-setup" class="md-nav__link">
<a href="#quick-build-process" class="md-nav__link">
<span class="md-ellipsis">
NocoDB Table Setup
Quick Build Process
</span>
</a>
<nav class="md-nav" aria-label="NocoDB Table Setup">
<nav class="md-nav" aria-label="Quick Build Process">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#required-columns" class="md-nav__link">
<a href="#1-get-nocodb-api-token" class="md-nav__link">
<span class="md-ellipsis">
Required Columns
1. Get NocoDB API Token
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#recommended-columns" class="md-nav__link">
<a href="#2-configure-environment" class="md-nav__link">
<span class="md-ellipsis">
Recommended Columns
2. Configure Environment
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3-auto-create-database-structure" class="md-nav__link">
<span class="md-ellipsis">
3. Auto-Create Database Structure
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#4-get-table-urls" class="md-nav__link">
<span class="md-ellipsis">
4. Get Table URLs
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#5-update-environment-with-urls" class="md-nav__link">
<span class="md-ellipsis">
5. Update Environment with URLs
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#6-build-and-deploy" class="md-nav__link">
<span class="md-ellipsis">
6. Build and Deploy
</span>
</a>
@ -1583,51 +1644,42 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</li>
<li class="md-nav__item">
<a href="#login-sheet-setup" class="md-nav__link">
<a href="#verify-installation" class="md-nav__link">
<span class="md-ellipsis">
Login Sheet Setup
Verify Installation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#api-token-setup" class="md-nav__link">
<a href="#quick-start" class="md-nav__link">
<span class="md-ellipsis">
API Token Setup
Quick Start
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#6-finding-nocodb-ids" class="md-nav__link">
<a href="#maintenance-commands" class="md-nav__link">
<span class="md-ellipsis">
6. Finding NocoDB IDs
Maintenance Commands
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#environment-configuration" class="md-nav__link">
<span class="md-ellipsis">
Environment Configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#running-the-application" class="md-nav__link">
<span class="md-ellipsis">
Running the Application
</span>
</a>
<nav class="md-nav" aria-label="Running the Application">
<nav class="md-nav" aria-label="Maintenance Commands">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#update-application" class="md-nav__link">
<span class="md-ellipsis">
Update Application
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#development-mode" class="md-nav__link">
<span class="md-ellipsis">
@ -1638,9 +1690,9 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</li>
<li class="md-nav__item">
<a href="#production-with-docker" class="md-nav__link">
<a href="#health-check" class="md-nav__link">
<span class="md-ellipsis">
Production with Docker
Health Check
</span>
</a>
@ -1652,36 +1704,9 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
</li>
<li class="md-nav__item">
<a href="#first-run" class="md-nav__link">
<a href="#support" class="md-nav__link">
<span class="md-ellipsis">
First Run
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#maintenance" class="md-nav__link">
<span class="md-ellipsis">
Maintenance
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#troubleshooting" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#security-recommendations" class="md-nav__link">
<span class="md-ellipsis">
Security Recommendations
Support
</span>
</a>
@ -1703,144 +1728,129 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<h1 id="map">Map<a class="headerlink" href="#map" title="Permanent link">&para;</a></h1>
<p>Map is BNKops canvassing application. It is built from the ground up to serve our community (Edmonton). </p>
<h1 id="map-build-guide">Map Build Guide<a class="headerlink" href="#map-build-guide" title="Permanent link">&para;</a></h1>
<p>Map is BNKops canvassing application built for community organizing and door-to-door canvassing.</p>
<div class="admonition info">
<p class="admonition-title">Complete Configuration</p>
<p>For detailed configuration, usage instructions, and troubleshooting, see the <a href="../../config/map/">Map Configuration Guide</a>.</p>
</div>
<div class="admonition warning">
<p class="admonition-title">Clean NocoDB</p>
<p>Currently the way to get a good result is to ensure the target nocodb database is empty. You can do this by deleting all bases. The script should still work with other volumes however may insert tables into odd locations; still debugging. Again, see config if needing to do manually. </p>
</div>
<h2 id="prerequisites">Prerequisites<a class="headerlink" href="#prerequisites" title="Permanent link">&para;</a></h2>
<ul>
<li>Docker and Docker Compose installed</li>
<li>NocoDB instance with API access</li>
<li>Domain name (optional but recommended for production)</li>
</ul>
<h2 id="nocodb-table-setup">NocoDB Table Setup<a class="headerlink" href="#nocodb-table-setup" title="Permanent link">&para;</a></h2>
<h3 id="required-columns">Required Columns<a class="headerlink" href="#required-columns" title="Permanent link">&para;</a></h3>
<div class="admonition warning">
<p class="admonition-title">Case Sensitive</p>
<p>When entering in the required columns, make sure that you enter in exact information. Case sensitivity matters for mapping the values to the map itself. </p>
</div>
<p>Create a table in NocoDB with these required columns. The format here is the <code>Name of the column - column type - details</code>:</p>
<h2 id="quick-build-process">Quick Build Process<a class="headerlink" href="#quick-build-process" title="Permanent link">&para;</a></h2>
<h3 id="1-get-nocodb-api-token">1. Get NocoDB API Token<a class="headerlink" href="#1-get-nocodb-api-token" title="Permanent link">&para;</a></h3>
<ol>
<li><strong>Geo-Location</strong> (geo-data) - Format: "latitude;longitude"</li>
<li><strong>latitude</strong> (Decimal) - Precision: 10, Scale: 8</li>
<li><strong>longitude</strong> (Decimal) - Precision: 11, Scale: 8</li>
<li>Login to your NocoDB instance</li>
<li>Click user icon → <strong>Account Settings</strong><strong>API Tokens</strong></li>
<li>Create new token with read/write permissions</li>
<li>Copy the token for the next step</li>
</ol>
<h3 id="recommended-columns">Recommended Columns<a class="headerlink" href="#recommended-columns" title="Permanent link">&para;</a></h3>
<ul>
<li>First Name (Text)</li>
<li>Last Name (Text)</li>
<li>Email (Email)</li>
<li>Phone (Phone)</li>
<li>Unit Number (Text)</li>
<li>Address (LongText)</li>
<li>Support Level (Single Select) - Values (only enter numbers): </li>
<li>1 <code>Strong Support (Green)</code></li>
<li>2 <code>Moderate Support (Yellow)</code></li>
<li>3 <code>Low Support (Orange)</code></li>
<li>4 <code>No Support (Red)</code></li>
<li>Sign (Checkbox)</li>
<li>Sign Size (Single Select) - Values: Small, Medium, Large</li>
<li>Notes (LongText)</li>
</ul>
<h2 id="login-sheet-setup">Login Sheet Setup<a class="headerlink" href="#login-sheet-setup" title="Permanent link">&para;</a></h2>
<p>Create a separate table for authorized users with:
- Email (Email) - Primary column
- Name (Text) - Optional</p>
<h2 id="api-token-setup">API Token Setup<a class="headerlink" href="#api-token-setup" title="Permanent link">&para;</a></h2>
<ol>
<li>In NocoDB, click user icon → Account Settings</li>
<li>Go to "API Tokens" tab</li>
<li>Create new token with read/write permissions for both tables</li>
</ol>
<h2 id="6-finding-nocodb-ids">6. Finding NocoDB IDs<a class="headerlink" href="#6-finding-nocodb-ids" title="Permanent link">&para;</a></h2>
<ul>
<li><strong>Project and Table IDs</strong>: Use the full NocoDB view URL in <code>NOCODB_VIEW_URL</code></li>
<li><strong>Login Sheet ID</strong>: Use the full URL to your login sheet in <code>NOCODB_LOGIN_SHEET</code></li>
</ul>
<h2 id="environment-configuration">Environment Configuration<a class="headerlink" href="#environment-configuration" title="Permanent link">&para;</a></h2>
<div class="admonition note">
<p class="admonition-title">Config</p>
<p>The <code>./config.sh</code> should have created a new <code>.env</code> file. If <code>.env</code> file is present, and it has properly defined domain, skip to step 2</p>
</div>
<ol>
<li>Copy the example env file:</li>
</ol>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>cp<span class="w"> </span>.env.example<span class="w"> </span>.env
<h3 id="2-configure-environment">2. Configure Environment<a class="headerlink" href="#2-configure-environment" title="Permanent link">&para;</a></h3>
<p>Edit the <code>.env</code> file in the <code>map/</code> directory:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nb">cd</span><span class="w"> </span>map
</span></code></pre></div>
<ol>
<li>Edit .env with your NocoDB details:
<div class="language-text highlight"><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>NOCODB_API_URL=https://your-nocodb-instance.com/api/v1
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a>NOCODB_API_TOKEN=your-api-token-here
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a>NOCODB_VIEW_URL=https://your-nocodb-instance.com/dashboard/#/nc/project-id/table-id
</span><span id="__span-1-4"><a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a>NOCODB_LOGIN_SHEET=https://your-nocodb-instance.com/dashboard/#/nc/project-id/login-sheet-id
</span><span id="__span-1-5"><a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a>
</span><span id="__span-1-6"><a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a># Server Configuration
</span><span id="__span-1-7"><a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a>PORT=3000
</span><span id="__span-1-8"><a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a>NODE_ENV=production
<p>Update your <code>.env</code> file with your NocoDB details, specifically the instance and api token:</p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a># NocoDB API Configuration
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a>NOCODB_API_URL=https://your-nocodb-instance.com/api/v1
</span><span id="__span-1-3"><a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a>NOCODB_API_TOKEN=your-api-token-here
</span><span id="__span-1-4"><a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a>
</span><span id="__span-1-5"><a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a># These will be populated after running build-nocodb.sh
</span><span id="__span-1-6"><a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a>NOCODB_VIEW_URL=
</span><span id="__span-1-7"><a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a>NOCODB_LOGIN_SHEET=
</span><span id="__span-1-8"><a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a>NOCODB_SETTINGS_SHEET=
</span><span id="__span-1-9"><a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a>
</span><span id="__span-1-10"><a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a># Session Secret (generate with: openssl rand -hex 32)
</span><span id="__span-1-11"><a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a>SESSION_SECRET=your-secure-random-string
</span><span id="__span-1-12"><a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a>
</span><span id="__span-1-13"><a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a># Map Defaults
</span><span id="__span-1-14"><a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a>DEFAULT_LAT=53.5461
</span><span id="__span-1-15"><a id="__codelineno-1-15" name="__codelineno-1-15" href="#__codelineno-1-15"></a>DEFAULT_LNG=-113.4938
</span><span id="__span-1-16"><a id="__codelineno-1-16" name="__codelineno-1-16" href="#__codelineno-1-16"></a>DEFAULT_ZOOM=11
</span><span id="__span-1-17"><a id="__codelineno-1-17" name="__codelineno-1-17" href="#__codelineno-1-17"></a>
</span><span id="__span-1-18"><a id="__codelineno-1-18" name="__codelineno-1-18" href="#__codelineno-1-18"></a># Optional: Map Boundaries
</span><span id="__span-1-19"><a id="__codelineno-1-19" name="__codelineno-1-19" href="#__codelineno-1-19"></a># BOUND_NORTH=53.7
</span><span id="__span-1-20"><a id="__codelineno-1-20" name="__codelineno-1-20" href="#__codelineno-1-20"></a># BOUND_SOUTH=53.4
</span><span id="__span-1-21"><a id="__codelineno-1-21" name="__codelineno-1-21" href="#__codelineno-1-21"></a># BOUND_EAST=-113.3
</span><span id="__span-1-22"><a id="__codelineno-1-22" name="__codelineno-1-22" href="#__codelineno-1-22"></a># BOUND_WEST=-113.7
</span><span id="__span-1-23"><a id="__codelineno-1-23" name="__codelineno-1-23" href="#__codelineno-1-23"></a>
</span><span id="__span-1-24"><a id="__codelineno-1-24" name="__codelineno-1-24" href="#__codelineno-1-24"></a># Domain Settings (for cookies)
</span><span id="__span-1-25"><a id="__codelineno-1-25" name="__codelineno-1-25" href="#__codelineno-1-25"></a>COOKIE_DOMAIN=.yourdomain.com
</span><span id="__span-1-26"><a id="__codelineno-1-26" name="__codelineno-1-26" href="#__codelineno-1-26"></a>ALLOWED_ORIGINS=https://map.yourdomain.com,http://localhost:3000
</span></code></pre></div></li>
</span><span id="__span-1-10"><a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a># Server Configuration
</span><span id="__span-1-11"><a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a>PORT=3000
</span><span id="__span-1-12"><a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a>NODE_ENV=production
</span><span id="__span-1-13"><a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a>SESSION_SECRET=your-secure-random-string
</span><span id="__span-1-14"><a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a>
</span><span id="__span-1-15"><a id="__codelineno-1-15" name="__codelineno-1-15" href="#__codelineno-1-15"></a># Map Defaults (Edmonton, AB)
</span><span id="__span-1-16"><a id="__codelineno-1-16" name="__codelineno-1-16" href="#__codelineno-1-16"></a>DEFAULT_LAT=53.5461
</span><span id="__span-1-17"><a id="__codelineno-1-17" name="__codelineno-1-17" href="#__codelineno-1-17"></a>DEFAULT_LNG=-113.4938
</span><span id="__span-1-18"><a id="__codelineno-1-18" name="__codelineno-1-18" href="#__codelineno-1-18"></a>DEFAULT_ZOOM=11
</span><span id="__span-1-19"><a id="__codelineno-1-19" name="__codelineno-1-19" href="#__codelineno-1-19"></a>
</span><span id="__span-1-20"><a id="__codelineno-1-20" name="__codelineno-1-20" href="#__codelineno-1-20"></a># Production Settings
</span><span id="__span-1-21"><a id="__codelineno-1-21" name="__codelineno-1-21" href="#__codelineno-1-21"></a>COOKIE_DOMAIN=.yourdomain.com
</span><span id="__span-1-22"><a id="__codelineno-1-22" name="__codelineno-1-22" href="#__codelineno-1-22"></a>ALLOWED_ORIGINS=https://map.yourdomain.com,http://localhost:3000
</span></code></pre></div>
<h3 id="3-auto-create-database-structure">3. Auto-Create Database Structure<a class="headerlink" href="#3-auto-create-database-structure" title="Permanent link">&para;</a></h3>
<p>Run the build script to create required tables:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>chmod<span class="w"> </span>+x<span class="w"> </span>build-nocodb.sh
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a>./build-nocodb.sh
</span></code></pre></div>
<p>This creates three tables:
- <strong>Locations</strong> - Main map data with geo-location, contact info, support levels
- <strong>Login</strong> - User authentication (email, name, admin flag)
- <strong>Settings</strong> - Admin configuration and QR codes</p>
<h3 id="4-get-table-urls">4. Get Table URLs<a class="headerlink" href="#4-get-table-urls" title="Permanent link">&para;</a></h3>
<p>After the script completes:</p>
<ol>
<li>Login to your NocoDB instance</li>
<li>Navigate to your project ("Map Viewer Project")</li>
<li>Copy the view URLs for each table from your browser address bar</li>
<li>URLs should look like: <code>https://your-nocodb.com/dashboard/#/nc/project-id/table-id</code></li>
</ol>
<h2 id="running-the-application">Running the Application<a class="headerlink" href="#running-the-application" title="Permanent link">&para;</a></h2>
<h3 id="5-update-environment-with-urls">5. Update Environment with URLs<a class="headerlink" href="#5-update-environment-with-urls" title="Permanent link">&para;</a></h3>
<p>Edit your <code>.env</code> file and add the table URLs:</p>
<div class="language-text highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a>NOCODB_VIEW_URL=https://your-nocodb.com/dashboard/#/nc/project-id/locations-table-id
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a>NOCODB_LOGIN_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/login-table-id
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a>NOCODB_SETTINGS_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/settings-table-id
</span></code></pre></div>
<h3 id="6-build-and-deploy">6. Build and Deploy<a class="headerlink" href="#6-build-and-deploy" title="Permanent link">&para;</a></h3>
<p>Build the Docker image and start the application:</p>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="c1"># Build the Docker image</span>
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>docker-compose<span class="w"> </span>build
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a>
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="c1"># Start the application</span>
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a>docker-compose<span class="w"> </span>up<span class="w"> </span>-d
</span></code></pre></div>
<h2 id="verify-installation">Verify Installation<a class="headerlink" href="#verify-installation" title="Permanent link">&para;</a></h2>
<ol>
<li>
<p>Check container status:
<div class="language-bash highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a>docker-compose<span class="w"> </span>ps
</span></code></pre></div></p>
</li>
<li>
<p>View logs:
<div class="language-bash highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a>docker-compose<span class="w"> </span>logs<span class="w"> </span>-f<span class="w"> </span>map-viewer
</span></code></pre></div></p>
</li>
<li>
<p>Access the application at <code>http://localhost:3000</code></p>
</li>
</ol>
<h2 id="quick-start">Quick Start<a class="headerlink" href="#quick-start" title="Permanent link">&para;</a></h2>
<ol>
<li><strong>Login</strong>: Use an email from your Login table</li>
<li><strong>Add Locations</strong>: Click on the map to add new locations</li>
<li><strong>Admin Panel</strong>: Admin users can access <code>/admin.html</code> for configuration</li>
<li><strong>Walk Sheets</strong>: Generate printable canvassing forms with QR codes</li>
</ol>
<h2 id="maintenance-commands">Maintenance Commands<a class="headerlink" href="#maintenance-commands" title="Permanent link">&para;</a></h2>
<h3 id="update-application">Update Application<a class="headerlink" href="#update-application" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>docker-compose<span class="w"> </span>down
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a>git<span class="w"> </span>pull<span class="w"> </span>origin<span class="w"> </span>main
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a>docker-compose<span class="w"> </span>build
</span><span id="__span-7-4"><a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a>docker-compose<span class="w"> </span>up<span class="w"> </span>-d
</span></code></pre></div>
<h3 id="development-mode">Development Mode<a class="headerlink" href="#development-mode" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="nb">cd</span><span class="w"> </span>app
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a>npm<span class="w"> </span>install
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a>npm<span class="w"> </span>run<span class="w"> </span>dev
<div class="language-bash highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="nb">cd</span><span class="w"> </span>app
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>npm<span class="w"> </span>install
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a>npm<span class="w"> </span>run<span class="w"> </span>dev
</span></code></pre></div>
<h3 id="production-with-docker">Production with Docker<a class="headerlink" href="#production-with-docker" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a>docker-compose<span class="w"> </span>up<span class="w"> </span>-d
<h3 id="health-check">Health Check<a class="headerlink" href="#health-check" title="Permanent link">&para;</a></h3>
<div class="language-bash highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a>curl<span class="w"> </span>http://localhost:3000/health
</span></code></pre></div>
<h2 id="first-run">First Run<a class="headerlink" href="#first-run" title="Permanent link">&para;</a></h2>
<ol>
<li>Access the application at <code>http://localhost:3000</code> (or your domain)</li>
<li>Login with an email from your authorized users list</li>
<li>Verify locations appear on the map</li>
</ol>
<h2 id="maintenance">Maintenance<a class="headerlink" href="#maintenance" title="Permanent link">&para;</a></h2>
<ul>
<li>To clear the geocoding cache, restart the application</li>
<li>To update the application:
<div class="language-bash highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a>docker-compose<span class="w"> </span>down
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>git<span class="w"> </span>pull<span class="w"> </span>origin<span class="w"> </span>main
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a>docker-compose<span class="w"> </span>up<span class="w"> </span>-d<span class="w"> </span>--build
</span></code></pre></div></li>
</ul>
<h2 id="troubleshooting">Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permanent link">&para;</a></h2>
<ul>
<li><strong>Locations not showing</strong>: Verify table has required columns and API token has read permissions</li>
<li><strong>Cannot add locations</strong>: Check API token has write permissions</li>
<li><strong>Authentication issues</strong>: Verify login sheet is properly configured</li>
</ul>
<h2 id="security-recommendations">Security Recommendations<a class="headerlink" href="#security-recommendations" title="Permanent link">&para;</a></h2>
<ol>
<li>Use HTTPS in production</li>
<li>Regularly rotate API tokens</li>
<li>Restrict API token permissions to only what's needed</li>
<li>Set appropriate CORS and cookie domains</li>
<li>Keep dependencies updated</li>
</ol>
<p>The application will automatically:</p>
<ul>
<li>Parse project/table IDs from view URLs</li>
<li>Sync geo fields between different formats</li>
<li>Cache geocoding results for performance</li>
<li>Rate limit API endpoints</li>
<li>Validate all inputs</li>
</ul>
<h2 id="support">Support<a class="headerlink" href="#support" title="Permanent link">&para;</a></h2>
<p>For detailed configuration, troubleshooting, and usage instructions, see the <a href="../../config/map/">Map Configuration Guide</a>.</p>

View File

@ -1307,6 +1307,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1417,6 +1419,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1106,6 +1106,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1216,6 +1218,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1019,6 +1019,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1240,6 +1242,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -18,7 +18,7 @@
<link rel="prev" href="../mkdocs/">
<link rel="next" href="../../adv/">
<link rel="next" href="../map/">
<link rel="icon" href="../../assets/favicon.png">
@ -1019,6 +1019,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1432,6 +1434,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>
@ -2139,13 +2164,13 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<a href="../../adv/" class="md-footer__link md-footer__link--next" aria-label="Next: Advanced Configurations">
<a href="../map/" class="md-footer__link md-footer__link--next" aria-label="Next: Map">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Advanced Configurations
Map
</div>
</div>
<div class="md-footer__button md-icon">

View File

@ -1019,6 +1019,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1127,6 +1129,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

File diff suppressed because it is too large Load Diff

View File

@ -1019,6 +1019,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1249,6 +1251,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block extrahead %}
{{ super() }}
<!-- Custom meta tags or head content can go here -->
{% endblock %}
{% block announce %}
<a href="https://homepage.cmlite.org" class="login-button">Login</a>
Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block extrahead %}
{{ super() }}
<!-- Custom meta tags or head content can go here -->
{% endblock %}
{% block announce %}
<a href="https://homepage.cmlite.org" class="login-button">Login</a>
Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
{% endblock %}

File diff suppressed because one or more lines are too long

View File

@ -1148,6 +1148,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1258,6 +1260,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1139,6 +1139,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1249,6 +1251,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1316,6 +1316,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1426,6 +1428,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1017,6 +1017,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1127,6 +1129,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1157,6 +1157,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1267,6 +1269,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1181,6 +1181,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1291,6 +1293,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1130,6 +1130,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1240,6 +1242,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1214,6 +1214,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1324,6 +1326,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1292,6 +1292,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1402,6 +1404,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1283,6 +1283,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1393,6 +1395,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1223,6 +1223,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1333,6 +1335,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -1208,6 +1208,8 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
@ -1318,6 +1320,29 @@ Changemaker Archive. <a href="https://docs.bnkops.com">Learn more</a>
<li class="md-nav__item">
<a href="../../config/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
</a>
</li>
</ul>
</nav>

View File

@ -2,122 +2,126 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://cmlite.org/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/test/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/adv/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/adv/ansible/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/adv/vscode-ssh/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/blog/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/blog/2025/07/03/blog-1/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/build/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/build/map/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/build/server/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/build/site/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/config/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/config/cloudflare-config/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/config/coder/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/config/map/</loc>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/config/mkdocs/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/phil/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/phil/cost-comparison/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/code-server/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/gitea/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/homepage/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/listmonk/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/map/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/mini-qr/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/mkdocs/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/n8n/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/nocodb/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/postgresql/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/services/static-server/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
<url>
<loc>https://cmlite.org/blog/archive/2025/</loc>
<lastmod>2025-07-04</lastmod>
<lastmod>2025-07-06</lastmod>
</url>
</urlset>

Binary file not shown.